├── .gitignore ├── .gitmodules ├── bin └── limine.conf ├── bochsrc ├── build.sh ├── build.zig ├── build.zig.zon ├── ideas.md ├── meta ├── bin │ └── ovmf-code-x86_64.fd ├── linker.ld └── scripts │ └── makeiso.sh ├── readme.md ├── src ├── acpi │ └── acpi.zig ├── arch │ └── x86 │ │ ├── cpu.zig │ │ ├── gdt.zig │ │ ├── gdt │ │ └── gdt.s │ │ ├── idt.zig │ │ ├── idt │ │ ├── idt.s │ │ ├── interrupt.s │ │ └── interrupt.zig │ │ ├── regs.zig │ │ ├── syscall.s │ │ ├── syscall.zig │ │ └── tss.zig ├── asm.zig ├── drivers │ ├── ahci.zig │ ├── audio.zig │ ├── drivers.zig │ ├── fbscreen.zig │ ├── hpet.zig │ ├── ioapic.zig │ ├── keyboard.zig │ ├── lapic.zig │ ├── pci.zig │ ├── pic.zig │ ├── ps2.zig │ └── serial.zig ├── ds.zig ├── font │ └── lucida-10x16.psf ├── idiot ├── idiot2 ├── iter.zig ├── limine_rq.zig ├── main.zig ├── mem │ ├── heap.zig │ ├── mem.zig │ ├── pmm.zig │ └── vmm.zig ├── psf.zig ├── sched │ ├── ctx.zig │ └── scheduler.zig ├── smp.zig ├── sync.zig ├── test.zig └── utils.zig └── utils ├── idiot └── idiot.s /.gitignore: -------------------------------------------------------------------------------- 1 | zig-out 2 | zig-cache 3 | .zig-cache 4 | iso_root 5 | bx_enh_dbg.ini 6 | .vscode 7 | zros.hdd 8 | limine 9 | limine-zig 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "limine"] 2 | path = limine 3 | url = https://github.com/limine-bootloader/limine.git 4 | branch = v8.0.12-binary 5 | -------------------------------------------------------------------------------- /bin/limine.conf: -------------------------------------------------------------------------------- 1 | timeout: 0 2 | 3 | /ZROS - Kernel 4 | protocol: limine 5 | kaslr: off 6 | kernel_path: boot():/zros 7 | -------------------------------------------------------------------------------- /bochsrc: -------------------------------------------------------------------------------- 1 | # configuration file generated by Bochs 2 | plugin_ctrl: unmapped=true, biosdev=true, speaker=true, extfpuirq=true, parallel=true, serial=true, iodebug=true, pcidev=false, usb_uhci=false 3 | config_interface: textconfig 4 | display_library: x, options="gui_debug" 5 | memory: host=32, guest=32 6 | romimage: file="/usr/share/bochs/BIOS-bochs-latest", address=0x00000000, options=none 7 | vgaromimage: file="/usr/share/bochs/VGABIOS-lgpl-latest" 8 | boot: cdrom 9 | floppy_bootsig_check: disabled=0 10 | floppya: type=1_44 11 | # no floppyb 12 | ata0: enabled=true, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 13 | ata0-master: type=none 14 | ata0-slave: type=cdrom, path=zros.hdd, status=inserted 15 | ata1: enabled=true, ioaddr1=0x170, ioaddr2=0x370, irq=15 16 | ata1-master: type=none 17 | ata1-slave: type=none 18 | ata2: enabled=false 19 | ata3: enabled=false 20 | optromimage1: file=none 21 | optromimage2: file=none 22 | optromimage3: file=none 23 | optromimage4: file=none 24 | optramimage1: file=none 25 | optramimage2: file=none 26 | optramimage3: file=none 27 | optramimage4: file=none 28 | pci: enabled=1, chipset=i440fx, slot1=none, slot2=none, slot3=none, slot4=none, slot5=none 29 | vga: extension=vbe, update_freq=5, realtime=1, ddc=builtin 30 | cpu: count=1:1:1, ips=4000000, quantum=16, model=bx_generic, reset_on_triple_fault=0, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0 31 | cpuid: level=6, stepping=3, model=3, family=6, vendor_string="AuthenticAMD", brand_string="AMD Athlon(tm) processor" 32 | cpuid: mmx=true, apic=xapic, simd=sse2, sse4a=false, misaligned_sse=false, sep=true 33 | cpuid: movbe=false, adx=false, aes=false, sha=false, xsave=false, xsaveopt=false, avx_f16c=false 34 | cpuid: avx_fma=false, bmi=0, xop=false, fma4=false, tbm=false, x86_64=true, 1g_pages=false 35 | cpuid: pcid=false, fsgsbase=false, smep=false, smap=false, mwait=true 36 | print_timestamps: enabled=0 37 | debugger_log: - 38 | magic_break: enabled=0 39 | port_e9_hack: enabled=0 40 | private_colormap: enabled=0 41 | clock: sync=none, time0=local, rtc_sync=0 42 | # no cmosimage 43 | log: - 44 | logprefix: %t%e%d 45 | debug: action=ignore 46 | info: action=report 47 | error: action=report 48 | panic: action=ask 49 | keyboard: type=mf, serial_delay=250, paste_delay=100000, user_shortcut=none 50 | mouse: type=ps2, enabled=false, toggle=ctrl+mbutton 51 | speaker: enabled=true, mode=system 52 | parport1: enabled=true, file=none 53 | parport2: enabled=false 54 | com1: enabled=true, mode=null 55 | com2: enabled=false 56 | com3: enabled=false 57 | com4: enabled=false 58 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/env bash 2 | 3 | export DEBUG=0 4 | export KVM=1 5 | export GDB=0 6 | 7 | zig build $1 8 | 9 | test $? -eq 0 || exit; 10 | 11 | bash ./meta/scripts/makeiso.sh 12 | 13 | 14 | file="meta/bin/ovmf-code-x86_64.fd" 15 | if [ ! -f "meta/bin/ovmf-code-x86_64.fd" ] 16 | then 17 | wget "https://github.com/osdev0/edk2-ovmf-nightly/releases/latest/download/ovmf-code-x86_64.fd" -O "$file" 18 | fi 19 | 20 | export ARGS="-serial mon:stdio \ 21 | -drive format=raw,file=zros.hdd \ 22 | -device ich9-intel-hda,id=sound0,bus=pcie.0,addr=0x1b -device hda-duplex,id=sound0-codec0,bus=sound0.0,cad=0 \ 23 | -global ICH9-LPC.disable_s3=1 -global ICH9-LPC.disable_s4=1 \ 24 | -no-reboot \ 25 | -no-shutdown \ 26 | -m 1024M \ 27 | -M q35 \ 28 | -smp 4 \ 29 | -bios $file" 30 | 31 | if [[ $DEBUG -eq 1 ]] 32 | then 33 | export ARGS="$ARGS -d int,cpu_reset,in_asm" 34 | fi 35 | 36 | 37 | if [[ $KVM -eq 1 ]] 38 | then 39 | export ARGS="$ARGS -enable-kvm" 40 | fi 41 | 42 | 43 | if [[ $GDB -eq 1 ]] 44 | then 45 | export ARGS="$ARGS -s -S" 46 | fi 47 | 48 | echo $ARGS 49 | 50 | qemu-system-x86_64 $ARGS 51 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn get_git_version(b: *std.Build) []const u8 { 4 | const cmd = b.run(&[_][]const u8{ "git", "rev-parse", "HEAD" }); 5 | return cmd[0..6]; 6 | } 7 | 8 | const AsmPath = struct { 9 | path_file: []const u8, 10 | file_name: []const u8, 11 | }; 12 | 13 | pub fn nasm_to(b: *std.Build, comptime file: AsmPath, exe: *std.Build.Step.Compile) !void { 14 | const output = "./zig-cache/nasm/" ++ file.file_name ++ ".o"; 15 | 16 | _ = b.run(&[_][]const u8{ "nasm", file.path_file, "-f", "elf64", "-o", output }); 17 | 18 | exe.addObjectFile(std.Build.LazyPath{ .cwd_relative = output }); 19 | } 20 | 21 | pub fn build(b: *std.Build) !void { 22 | const optimize = b.standardOptimizeOption(.{}); 23 | 24 | const build_options = b.addOptions(); 25 | 26 | build_options.addOption(bool, "release_mode", optimize != .Debug); 27 | build_options.addOption([]const u8, "git_version", get_git_version(b)); 28 | 29 | var cross = 30 | std.Target.Query{ 31 | .cpu_arch = std.Target.Cpu.Arch.x86_64, 32 | .os_tag = .freestanding, 33 | .abi = std.Target.Abi.none, 34 | }; 35 | 36 | const x86features = std.Target.x86.Feature; 37 | cross.cpu_features_add.addFeature(@intFromEnum(x86features.soft_float)); 38 | cross.cpu_features_sub.addFeature(@intFromEnum(x86features.mmx)); 39 | cross.cpu_features_sub.addFeature(@intFromEnum(x86features.sse)); 40 | cross.cpu_features_sub.addFeature(@intFromEnum(x86features.sse2)); 41 | cross.cpu_features_sub.addFeature(@intFromEnum(x86features.avx)); 42 | cross.cpu_features_sub.addFeature(@intFromEnum(x86features.avx2)); 43 | 44 | const target = b.resolveTargetQuery(cross); 45 | 46 | const limine = b.dependency("limine_zig", .{}); 47 | 48 | const exe = b.addExecutable(.{ 49 | .name = "zros", 50 | .root_source_file = b.path("src/main.zig"), 51 | .target = target, 52 | .optimize = optimize, 53 | .code_model = std.builtin.CodeModel.kernel, 54 | .linkage = .static, 55 | }); 56 | 57 | exe.pie = false; 58 | exe.want_lto = false; 59 | 60 | exe.linker_script = b.path("meta/linker.ld"); 61 | exe.root_module.addImport("limine", limine.module("limine")); 62 | exe.root_module.addOptions("build_options", build_options); 63 | 64 | std.fs.cwd().makePath("./zig-cache/nasm") catch {}; 65 | 66 | try nasm_to(b, .{ .path_file = "./src/arch/x86/gdt/gdt.s", .file_name = "gdt" }, exe); 67 | 68 | try nasm_to(b, .{ .path_file = "./src/arch/x86/idt/idt.s", .file_name = "idt" }, exe); 69 | 70 | try nasm_to(b, .{ .path_file = "./src/arch/x86/idt/interrupt.s", .file_name = "interrupt" }, exe); 71 | 72 | try nasm_to(b, .{ .path_file = "./src/arch/x86/syscall.s", .file_name = "syscall" }, exe); 73 | 74 | b.installArtifact(exe); 75 | 76 | const exe_unit_tests = b.addTest(.{ 77 | .root_source_file = b.path("src/test.zig"), 78 | .target = b.standardTargetOptions(.{}), 79 | .optimize = optimize, 80 | }); 81 | 82 | exe_unit_tests.root_module.addImport("limine", limine.module("limine")); 83 | exe_unit_tests.root_module.addOptions("build_options", build_options); 84 | 85 | const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); 86 | // Similar to creating the run step earlier, this exposes a `test` step to 87 | // the `zig build --help` menu, providing a way for the user to request 88 | // running the unit tests. 89 | const test_step = b.step("test", "Run unit tests"); 90 | test_step.dependOn(&run_exe_unit_tests.step); 91 | } 92 | -------------------------------------------------------------------------------- /build.zig.zon: -------------------------------------------------------------------------------- 1 | .{ 2 | .name = .zros, 3 | .version = "0.0.1", 4 | .fingerprint = 0x59da7434a2e90f5a, 5 | .paths = .{ "./src", "build.zig", "build.zig.zon" }, 6 | .minimum_zig_version = "0.15.0-dev.337+4e700fdf8", 7 | .dependencies = .{ 8 | .limine_zig = .{ 9 | .url = "git+https://github.com/48cf/limine-zig.git#7b29b6e6f6d35052f01ed3831085a39aae131705", 10 | .hash = "1220f946f839eab2ec49dca1c805ce72ac3e3ef9c47b3afcdecd1c05a7b35f66d277", 11 | }, 12 | }, 13 | } 14 | -------------------------------------------------------------------------------- /ideas.md: -------------------------------------------------------------------------------- 1 | Ideas 2 | - Document each structures with a link to a source(osdev.wiki, AMD manuals, ...) 3 | - Remove all magic values 4 | - Rename function with Zig coding style 5 | - Rewrite hackish code 6 | -------------------------------------------------------------------------------- /meta/bin/ovmf-code-x86_64.fd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rheydskey/zros/7055f9c757e234b12a6f7255b2a277561a42108c/meta/bin/ovmf-code-x86_64.fd -------------------------------------------------------------------------------- /meta/linker.ld: -------------------------------------------------------------------------------- 1 | /* FROM: https://raw.githubusercontent.com/limine-bootloader/limine-zig-barebones/trunk/kernel/linker.ld */ 2 | /* Tell the linker that we want an x86_64 ELF64 output file */ 3 | OUTPUT_FORMAT(elf64-x86-64) 4 | OUTPUT_ARCH(i386:x86-64) 5 | 6 | /* We want the symbol _start to be our entry point */ 7 | ENTRY(_start) 8 | 9 | /* Define the program headers we want so the bootloader gives us the right */ 10 | /* MMU permissions */ 11 | PHDRS 12 | { 13 | text PT_LOAD FLAGS((1 << 0) | (1 << 2)) ; /* Execute + Read */ 14 | rodata PT_LOAD FLAGS((1 << 2)) ; /* Read only */ 15 | data PT_LOAD FLAGS((1 << 1) | (1 << 2)) ; /* Write + Read */ 16 | dynamic PT_DYNAMIC FLAGS((1 << 1) | (1 << 2)) ; /* Dynamic PHDR for relocations */ 17 | } 18 | 19 | SECTIONS 20 | { 21 | /* We wanna be placed in the topmost 2GiB of the address space, for optimisations */ 22 | /* and because that is what the Limine spec mandates. */ 23 | /* Any address in this region will do, but often 0xffffffff80000000 is chosen as */ 24 | /* that is the beginning of the region. */ 25 | . = 0xffffffff80000000; 26 | 27 | text_start_addr = .; 28 | 29 | .text : { 30 | *(.text .text.*) 31 | } :text 32 | 33 | text_end_addr = .; 34 | 35 | /* Move to the next memory page for .rodata */ 36 | . += CONSTANT(MAXPAGESIZE); 37 | 38 | rodata_start_addr = .; 39 | 40 | PROVIDE(symbol_table = 1); 41 | 42 | .rodata : { 43 | *(.rodata .rodata.*) 44 | } :rodata 45 | 46 | rodata_end_addr = .; 47 | 48 | /* Move to the next memory page for .data */ 49 | . += CONSTANT(MAXPAGESIZE); 50 | 51 | data_start_addr = .; 52 | 53 | .data : { 54 | *(.data .data.*) 55 | } :data 56 | 57 | /* Dynamic section for relocations, both in its own PHDR and inside data PHDR */ 58 | .dynamic : { 59 | *(.dynamic) 60 | } :data :dynamic 61 | 62 | /* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */ 63 | /* unnecessary zeros will be written to the binary. */ 64 | /* If you need, for example, .init_array and .fini_array, those should be placed */ 65 | /* above this. */ 66 | .bss : { 67 | *(.bss .bss.*) 68 | *(COMMON) 69 | } :data 70 | 71 | data_end_addr = .; 72 | 73 | /* Discard .note.* and .eh_frame since they may cause issues on some hosts. */ 74 | /DISCARD/ : { 75 | *(.eh_frame) 76 | *(.note .note.*) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /meta/scripts/makeiso.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PWD=$(pwd) 4 | 5 | KERNEL_HDD="$PWD/zros.hdd" 6 | 7 | rm -rvf "$PWD/iso_root" 8 | 9 | mkdir -p "$PWD/iso_root" 10 | 11 | cp -v "$PWD/zig-out/bin/zros" "$PWD/bin/limine.conf" "$PWD/limine/limine-bios.sys" "$PWD/limine/limine-bios-cd.bin" "$PWD/limine/limine-uefi-cd.bin" "$PWD/iso_root/" 12 | 13 | xorriso -as mkisofs -b limine-bios-cd.bin \ 14 | -no-emul-boot -boot-load-size 4 -boot-info-table \ 15 | --efi-boot limine-uefi-cd.bin \ 16 | -efi-boot-part --efi-boot-image --protective-msdos-label \ 17 | iso_root -o ${KERNEL_HDD} 18 | 19 | $PWD/limine/limine bios-install ${KERNEL_HDD} 20 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## zros 2 | zros (Zig and Rust os) is an kernel to learn how it's working. 3 | Crappy and dirty code is expected :) 4 | 5 | Why no rust ? Because rust will come when userspace will work 6 | 7 | Big thanks to devse admins to make me do this thing (inspired of them) 8 | 9 | Roadmap (FROM the devse meme generator): 10 | - [x] GDT 11 | - [x] IDT 12 | - [x] PMM 13 | - [x] VMM 14 | - [x] Paging 15 | - [x] ACPI 16 | - [x] APIC 17 | - [x] IOAPIC 18 | - [x] LAPIC 19 | - [x] HPET 20 | - [x] APIC-TIMER 21 | - [x] Userspace 22 | - [ ] SMP 23 | - [ ] The fun begin 24 | -------------------------------------------------------------------------------- /src/acpi/acpi.zig: -------------------------------------------------------------------------------- 1 | const lapic = @import("../drivers/lapic.zig"); 2 | const ioapic = @import("../drivers/ioapic.zig"); 3 | const serial = @import("../drivers/serial.zig"); 4 | const pci = @import("../drivers/pci.zig"); 5 | const limine_rq = @import("../limine_rq.zig"); 6 | const std = @import("std"); 7 | const hpet = @import("../drivers/hpet.zig"); 8 | const disable_pic = @import("../drivers/pic.zig").disable_pic; 9 | 10 | pub var rspt: ?*align(1) Rspt = null; 11 | pub var rsdp: ?*align(1) Rsdp = null; 12 | pub var madt: ?*align(1) Madt = null; 13 | pub var xspt: ?*align(1) Xspt = null; 14 | pub var mcfg: ?*align(1) Mcfg = null; 15 | 16 | pub var cpu_count: u32 = 0; 17 | 18 | pub const AcpiHeader = packed struct(u288) { 19 | signature: u32, 20 | length: u32, 21 | revision: u8, 22 | checksum: u8, 23 | oem_id: u48, 24 | oem_table_id: u64, 25 | oem_revision: u32, 26 | creator_id: u32, 27 | creator_revision: u32, 28 | 29 | fn is_signature_eq(self: *align(1) @This(), name: []const u8) bool { 30 | const signature_as_slice: [*]u8 = @ptrCast(&self.signature); 31 | return std.mem.eql(u8, signature_as_slice[0..4], name); 32 | } 33 | }; 34 | 35 | const Xspt = packed struct { 36 | header: AcpiHeader, 37 | end: void, 38 | 39 | pub inline fn length(self: *align(1) @This()) u64 { 40 | return (self.header.length - @sizeOf(AcpiHeader)) / @sizeOf(u64); 41 | } 42 | 43 | pub fn get(self: *align(1) @This(), name: []const u8) !*align(1) Xspt { 44 | const entries = @as([*]align(1) u64, @ptrCast(@alignCast(&self.end)))[0..self.length()]; 45 | for (entries) |entry| { 46 | const ptr: *align(1) Xspt = @ptrFromInt(entry + limine_rq.hhdm.response.?.offset); 47 | 48 | if (ptr.header.is_signature_eq(name)) { 49 | return ptr; 50 | } 51 | } 52 | 53 | return error.NotFound; 54 | } 55 | 56 | pub fn get_mcfg(self: *align(1) @This()) !*align(1) Mcfg { 57 | return @ptrCast(try self.get("MCFG")); 58 | } 59 | 60 | pub fn get_apic(self: *align(1) @This()) !*align(1) Madt { 61 | return @ptrCast(try self.get("APIC")); 62 | } 63 | }; 64 | 65 | const Rspt = packed struct(u288) { 66 | header: AcpiHeader, 67 | end: void, 68 | 69 | pub inline fn length(self: *align(1) @This()) u32 { 70 | return @divExact(self.header.length - @sizeOf(AcpiHeader), @sizeOf(u32)); 71 | } 72 | 73 | pub fn get(self: *align(1) @This(), name: []const u8) !*Rspt { 74 | const entries = @as([*]u32, @alignCast(@ptrCast(&self.end)))[0..self.length()]; 75 | for (entries) |entry| { 76 | const ptr: *Rspt = @ptrFromInt(entry + limine_rq.hhdm.response.?.offset); 77 | 78 | if (ptr.header.is_signature_eq(name)) { 79 | return ptr; 80 | } 81 | } 82 | 83 | return error.NotFound; 84 | } 85 | 86 | pub fn get_apic(self: *align(1) @This()) !*align(1) Madt { 87 | return @ptrCast(try self.get("APIC")); 88 | } 89 | 90 | pub fn get_mcfg(self: *align(1) @This()) !*align(1) Mcfg { 91 | return @ptrCast(try self.get("MCFG")); 92 | } 93 | }; 94 | 95 | pub const Madt = packed struct { 96 | header: AcpiHeader, 97 | lapic_addr: u32, 98 | flags: u32, 99 | entries: void, 100 | 101 | pub const MadtEntryHeader = packed struct { 102 | entry_type: u8, 103 | length: u8, 104 | entry: void, 105 | }; 106 | 107 | pub const Iso = packed struct { 108 | header: Madt.MadtEntryHeader, 109 | bus_src: u8, 110 | irq_src: u8, 111 | gsi: u32, 112 | flags: u16, 113 | }; 114 | 115 | pub const IoApic = packed struct { 116 | header: Madt.MadtEntryHeader, 117 | ioapic: ioapic.IoApic, 118 | }; 119 | 120 | pub const ProcessorLocalApic = packed struct { 121 | processor_id: u8, 122 | apic_id: u8, 123 | flags: packed struct(u32) { is_enabled: bool, is_online_capable: bool, other: u30 }, 124 | }; 125 | 126 | pub fn read_entries(self: *align(1) @This()) void { 127 | var entry: ?*Madt.MadtEntryHeader = undefined; 128 | var i: usize = 0; 129 | while (i < self.header.length - @sizeOf(@This())) { 130 | entry = @ptrFromInt(@intFromPtr(&self.entries) + i); 131 | serial.println("{any}", .{entry}); 132 | 133 | switch (entry.?.entry_type) { 134 | 0 => { 135 | const lapic_entry: *align(1) Madt.ProcessorLocalApic = @ptrCast(&entry.?.entry); 136 | serial.println("{any}", .{lapic_entry}); 137 | cpu_count += 1; 138 | }, 139 | 1 => { 140 | const ioapic_struct: *align(1) Madt.IoApic = @ptrCast(&entry.?.entry); 141 | serial.println("{any}", .{ioapic_struct}); 142 | }, 143 | 144 | 2 => { 145 | const iso: *align(1) Madt.Iso = @ptrCast(entry); 146 | serial.println("{any}", .{iso}); 147 | }, 148 | 149 | else => { 150 | serial.println("UNSUPPORTED ({})", .{entry.?.entry_type}); 151 | }, 152 | } 153 | 154 | i += @max(@sizeOf(MadtEntryHeader), entry.?.length); 155 | } 156 | 157 | serial.println("CPU COUNT: {}", .{cpu_count}); 158 | } 159 | 160 | pub fn get_iso(self: *align(1) @This(), irq: u8) ?*align(1) Iso { 161 | var entry: ?*Madt.MadtEntryHeader = null; 162 | var i: usize = 0; 163 | while (i < self.header.length - @sizeOf(@This())) { 164 | entry = @ptrFromInt(@intFromPtr(&self.entries) + i); 165 | if (entry.?.entry_type == 2) { 166 | const iso: *align(1) Madt.Iso = @ptrCast(entry); 167 | if (iso.irq_src == irq) { 168 | return iso; 169 | } 170 | } 171 | 172 | i += @max(@sizeOf(MadtEntryHeader), entry.?.length); 173 | } 174 | 175 | return null; 176 | } 177 | 178 | pub fn get_ioapic(self: *align(1) @This()) !*align(1) IoApic { 179 | var entry: ?*Madt.MadtEntryHeader = null; 180 | var i: usize = 0; 181 | while (i < self.header.length - @sizeOf(@This())) { 182 | entry = @ptrFromInt(@intFromPtr(&self.entries) + i); 183 | 184 | if (entry.?.entry_type == 1) { 185 | return @ptrCast(entry.?); 186 | } 187 | 188 | i += @max(@sizeOf(MadtEntryHeader), entry.?.length); 189 | } 190 | 191 | return error.NotFound; 192 | } 193 | }; 194 | 195 | const Rsdp = packed struct(u288) { 196 | signature: u64, 197 | checksum: u8, 198 | oem_id: u48, 199 | revision: u8, 200 | rsdt: u32, 201 | length: u32, 202 | xsdt: u64, 203 | extend_checksum: u8, 204 | reserved: u24, 205 | 206 | pub inline fn get_xsdt(self: *align(1) @This()) !*align(1) Xspt { 207 | if (self.revision <= 1) { 208 | return error.RevisionTooLow; 209 | } 210 | 211 | return @ptrFromInt(self.xsdt + limine_rq.hhdm.response.?.offset); 212 | } 213 | 214 | pub inline fn get_rsdt(self: *align(1) @This()) !*align(1) Rspt { 215 | if (self.revision > 1) { 216 | serial.println("DEPRECATED with revision 2.\n You should use xsdt", .{}); 217 | } 218 | 219 | return @ptrFromInt(@as(u64, self.rsdt) + limine_rq.hhdm.response.?.offset); 220 | } 221 | }; 222 | 223 | pub const Mcfg = packed struct(u352) { 224 | header: AcpiHeader, 225 | reserved: u64, 226 | configuration: void, 227 | 228 | pub const Configuration = packed struct(u128) { 229 | base_addr: u64, 230 | pci_group: u16, 231 | start: u8, 232 | end: u8, 233 | reserved: u32, 234 | 235 | fn get_base_addr(self: @This(), addr: pci.PciAddr) u64 { 236 | return limine_rq.hhdm.response.?.offset + self.base_addr + (@as(u64, addr.bus_no - self.start) << 20) | (@as(u64, addr.device_no) << 15) | @as(u64, addr.fn_no) << 12 | (@as(u64, addr.offset)); 237 | } 238 | 239 | pub fn read( 240 | self: @This(), 241 | addr: pci.PciAddr, 242 | comptime size: type, 243 | ) size { 244 | const base = self.get_base_addr(addr); 245 | switch (size) { 246 | u8, u16, u32 => { 247 | return @as(*size, @ptrFromInt(base)).*; 248 | }, 249 | else => { 250 | @compileError("Should use u8, u16 or u32 as type"); 251 | }, 252 | } 253 | } 254 | 255 | pub fn write(self: @This(), addr: @import("../drivers/pci.zig").PciAddr, comptime size: type, value: size) void { 256 | const base = self.get_base_addr(addr); 257 | 258 | switch (size) { 259 | u8, u16, u32 => { 260 | @as(*size, @ptrFromInt(base)).* = value; 261 | }, 262 | else => { 263 | @compileError("Should use u8, u16 or u32 as type"); 264 | }, 265 | } 266 | } 267 | }; 268 | 269 | pub fn get_configuration(self: *align(1) @This()) *align(1) Configuration { 270 | return @ptrCast(&self.configuration); 271 | } 272 | 273 | pub fn countEntry(self: *align(1) @This()) usize { 274 | return (self.header.length * 8 - @bitSizeOf(Mcfg)) / @bitSizeOf(Configuration); 275 | } 276 | 277 | pub fn get_entry_of_bus(self: *align(1) @This(), bus: u8) ?*align(1) Configuration { 278 | const cfg: [*]align(1) Configuration = @ptrCast(&self.configuration); 279 | var i: usize = 0; 280 | while (i < self.countEntry()) : (i += 1) { 281 | const entry = cfg[i]; 282 | if (entry.start <= bus and bus <= entry.end) { 283 | return &cfg[i]; 284 | } 285 | } 286 | 287 | return null; 288 | } 289 | }; 290 | 291 | pub fn init() !void { 292 | disable_pic(); 293 | 294 | const response = limine_rq.rspd.response orelse return error.NoRspd; 295 | 296 | rsdp = @alignCast(@ptrCast(response.address)); 297 | 298 | serial.println("{any}", .{rsdp}); 299 | if (rsdp.?.revision > 1) { 300 | xspt = try rsdp.?.get_xsdt(); 301 | serial.println("{any}", .{xspt}); 302 | 303 | madt = try xspt.?.get_apic(); 304 | mcfg = try xspt.?.get_mcfg(); 305 | } else { 306 | rspt = try rsdp.?.get_rsdt(); 307 | serial.println("{any}", .{rspt}); 308 | 309 | madt = try rspt.?.get_apic(); 310 | mcfg = try rspt.?.get_mcfg(); 311 | } 312 | 313 | madt.?.read_entries(); 314 | 315 | try lapic.init(); 316 | try hpet.init(); 317 | 318 | var io_apic = (try madt.?.get_ioapic()).ioapic; 319 | io_apic.init(); 320 | 321 | lapic.init_timer(); 322 | } 323 | -------------------------------------------------------------------------------- /src/arch/x86/cpu.zig: -------------------------------------------------------------------------------- 1 | pub const Cpuid = struct { 2 | eax: u32 = 0, 3 | ebx: u32 = 0, 4 | ecx: u32 = 0, 5 | edx: u32 = 0, 6 | 7 | pub fn fromRegister(self: *const Cpuid, comptime t: type) t { 8 | return @bitCast([_]u32{ self.eax, self.ebx, self.edx, self.ecx }); 9 | } 10 | 11 | const Vendor = packed struct(u128) { 12 | hightest_call: u32, 13 | vendor_string: u96, 14 | 15 | pub fn getVendorString(self: *const Vendor) []const u8 { 16 | return @import("std").mem.asBytes(&self.vendor_string); 17 | } 18 | }; 19 | 20 | pub fn readVendor() Vendor { 21 | var self: Cpuid = .{}; 22 | 23 | self.readCpuId(0); 24 | 25 | return self.fromRegister(Vendor); 26 | } 27 | 28 | const CpuInfo = packed struct(u128) { 29 | processor_version: packed struct(u32) { 30 | stepping_id: u4, 31 | model: u4, 32 | family_id: u4, 33 | processor_type: u2, 34 | reserved: u2, 35 | extended_model: u4, 36 | extended_family: u8, 37 | _reserved: u4, 38 | }, 39 | ebx: packed struct(u32) { 40 | brand_index: u8, 41 | clflush: u8, 42 | max_id: u8, 43 | local_apic_id: u8, 44 | }, 45 | other: u64, 46 | }; 47 | 48 | pub fn readCpuInfo() CpuInfo { 49 | var self: Cpuid = .{}; 50 | 51 | self.readCpuId(1); 52 | 53 | return self.fromRegister(CpuInfo); 54 | } 55 | 56 | pub fn readCpuId(self: *Cpuid, cpuid_value: u32) void { 57 | var eax_value: u32 = 0; 58 | var ebx_value: u32 = 0; 59 | var ecx_value: u32 = 0; 60 | var edx_value: u32 = 0; 61 | 62 | asm ( 63 | \\ cpuid 64 | : [_] "={eax}" (eax_value), 65 | [_] "={ebx}" (ebx_value), 66 | [_] "={ecx}" (ecx_value), 67 | [_] "={edx}" (edx_value), 68 | : [cpuid_offset] "{eax}" (cpuid_value), 69 | ); 70 | 71 | self.eax = eax_value; 72 | self.ebx = ebx_value; 73 | self.ecx = ecx_value; 74 | self.edx = edx_value; 75 | } 76 | }; 77 | 78 | pub fn get_id() u8 { 79 | return Cpuid.readCpuInfo().ebx.local_apic_id; 80 | } 81 | -------------------------------------------------------------------------------- /src/arch/x86/gdt.zig: -------------------------------------------------------------------------------- 1 | const serial = @import("root").drivers.serial; 2 | const tss = @import("./tss.zig"); 3 | const zeroes = @import("std").mem.zeroes; 4 | 5 | extern fn load_gdt(gdt_descriptor: *const GDTPtr) void; 6 | extern fn load_tss() void; 7 | 8 | var TSS: tss.TaskSegment = zeroes(tss.TaskSegment); 9 | 10 | const TssEntry = packed struct(u128) { 11 | length: u16, 12 | base_low: u16, 13 | base_mid_low: u8, 14 | flags: u16, 15 | base_mid_high: u8, 16 | base_high: u32, 17 | reserved: u32 = 0, 18 | 19 | const TssFlags = struct { 20 | const PRESENT = (1 << 7); 21 | const LONGMODE = (0x9); 22 | }; 23 | 24 | pub fn fromAddr(ptr: usize) TssEntry { 25 | return .{ 26 | .length = @sizeOf(tss.TaskSegment), 27 | .base_low = @intCast(ptr & 0xFFFF), 28 | .base_mid_low = @intCast((ptr >> 16) & 0xff), 29 | .flags = TssFlags.PRESENT | TssFlags.LONGMODE, 30 | .base_mid_high = @intCast((ptr >> 24) & 0xff), 31 | .base_high = @intCast(ptr >> 32), 32 | }; 33 | } 34 | }; 35 | 36 | const Gdt = extern struct { 37 | entries: [5]GDTEntry align(1), 38 | tss: TssEntry align(1) = zeroes(TssEntry), 39 | }; 40 | 41 | var GDT: Gdt = Gdt{ .entries = [_]GDTEntry{ 42 | GDTEntry{}, 43 | GDTEntry{ 44 | .access_byte = .{ 45 | .present = true, 46 | .executable = true, 47 | .read_write = true, 48 | .typebit = TypeBit.CODE_DATA, 49 | }, 50 | .flags = .{ 51 | .granularity = true, 52 | .long = true, 53 | }, 54 | }, 55 | GDTEntry{ 56 | .access_byte = .{ 57 | .present = true, 58 | .read_write = true, 59 | .typebit = TypeBit.CODE_DATA, 60 | }, 61 | .flags = .{ 62 | .granularity = true, 63 | .descriptor = 1, 64 | }, 65 | }, 66 | GDTEntry{ 67 | .access_byte = .{ 68 | .present = true, 69 | .read_write = true, 70 | .executable = true, 71 | .typebit = TypeBit.CODE_DATA, 72 | .privilege = 3, 73 | }, 74 | .flags = .{ 75 | .granularity = true, 76 | .long = true, 77 | }, 78 | }, 79 | GDTEntry{ 80 | .access_byte = .{ 81 | .present = true, 82 | .read_write = true, 83 | .typebit = TypeBit.CODE_DATA, 84 | .privilege = 3, 85 | }, 86 | .flags = .{ 87 | .granularity = true, 88 | .descriptor = 1, 89 | }, 90 | }, 91 | } }; 92 | 93 | const GDTPtr = packed struct { 94 | size: u16, 95 | address: u64, 96 | }; 97 | 98 | const TypeBit = enum(u1) { 99 | SYSTEM = 0, 100 | CODE_DATA = 1, 101 | }; 102 | 103 | const AccessByte = packed struct(u8) { 104 | accessed_bit: bool = false, 105 | read_write: bool = false, 106 | dc: bool = false, 107 | executable: bool = false, 108 | typebit: TypeBit = TypeBit.SYSTEM, 109 | privilege: u2 = 0, 110 | present: bool = false, 111 | }; 112 | 113 | // https://osdev.wiki/wiki/Global_Descriptor_Table#Segment_Descriptor 114 | pub const Flags = packed struct(u4) { 115 | const Descriptor = struct { 116 | const protected_16bit = 0; 117 | const protected_32bit = 1; 118 | }; 119 | reserved: u1 = 0, 120 | long: bool = false, 121 | descriptor: u1 = Descriptor.protected_16bit, 122 | granularity: bool = false, 123 | }; 124 | 125 | pub const GDTEntry = packed struct(u64) { 126 | limit_low: u16 = 0, 127 | base_low: u16 = 0, 128 | base_middle: u8 = 0, 129 | access_byte: AccessByte = .{}, 130 | limit_high: u4 = 0, 131 | flags: Flags = .{}, 132 | base: u8 = 0, 133 | }; 134 | 135 | pub fn tss_init(kernel_stack: u64) void { 136 | TSS.rsp.rsp0 = kernel_stack; 137 | } 138 | 139 | pub fn init() void { 140 | serial.print("Start GDT Init\n", .{}); 141 | 142 | GDT.tss = TssEntry.fromAddr(@intFromPtr(&TSS)); 143 | load_gdt(&GDTPtr{ .size = @sizeOf(Gdt), .address = @intFromPtr(&GDT) }); 144 | load_tss(); 145 | 146 | serial.print_ok("GDT", .{}); 147 | } 148 | -------------------------------------------------------------------------------- /src/arch/x86/gdt/gdt.s: -------------------------------------------------------------------------------- 1 | [bits 64] 2 | 3 | global load_gdt 4 | load_gdt: 5 | lgdt [rdi] 6 | 7 | mov ax, 0x10 8 | 9 | mov ds, ax 10 | mov es, ax 11 | mov fs, ax 12 | mov gs, ax 13 | mov ss, ax 14 | 15 | pop rdi 16 | 17 | mov rax, 0x08 18 | 19 | push rax 20 | push rdi 21 | 22 | retfq 23 | 24 | global load_tss 25 | load_tss: 26 | mov ax, 0x28 27 | ltr ax 28 | ret 29 | -------------------------------------------------------------------------------- /src/arch/x86/idt.zig: -------------------------------------------------------------------------------- 1 | const serial = @import("root").drivers.serial; 2 | const interrupt = @import("idt/interrupt.zig"); 3 | 4 | const IDT_SIZE = 256; 5 | 6 | extern fn idt_load(usize) void; 7 | 8 | var idt: Idt = undefined; 9 | 10 | const Idt = struct { 11 | entries: [IDT_SIZE]IdtEntry, 12 | 13 | fn empty() @This() { 14 | return @This(){ .entries = [_]IdtEntry{undefined} ** IDT_SIZE }; 15 | } 16 | }; 17 | 18 | const IdtPtr = packed struct(u80) { 19 | size: u16, 20 | base_address: u64, 21 | }; 22 | 23 | pub const IdtEntry = packed struct(u128) { 24 | offset_l: u16 = 0, 25 | code_segment: u16 = 8, // KERNEL_CODE_SEGMENT Should be refactor to delete the magic value 26 | ist: u8 = 0, 27 | type_attr: u8, 28 | offset_m: u16 = 0, 29 | offset_h: u32 = 0, 30 | zero: u32 = 0, 31 | 32 | pub fn set_offset(self: *@This(), base: u64) void { 33 | self.*.offset_l = @intCast(base & 0xFFFF); 34 | self.*.offset_m = @intCast((base >> 16) & 0xFFFF); 35 | self.*.offset_h = @intCast(base >> 32); 36 | } 37 | 38 | pub const InterruptHandler = *const fn (interrupt: *const interrupt.Regs) callconv(.C) void; 39 | 40 | pub fn new(handler: u64, flags: u8) @This() { 41 | var self = IdtEntry{ .type_attr = flags }; 42 | 43 | self.set_function(handler); 44 | 45 | return self; 46 | } 47 | 48 | pub fn set_function(self: *@This(), handler: u64) void { 49 | self.set_offset(handler); 50 | 51 | self.*.code_segment = 8; 52 | } 53 | 54 | const GateType = enum(u4) { Interrupt = 0xE, Trap = 0xF }; 55 | 56 | const EntryAttributes = packed struct(u8) { 57 | gate_type: GateType = .Interrupt, 58 | _reserved: u1 = 0, 59 | privilege: u2 = 0, 60 | present: bool = false, 61 | }; 62 | }; 63 | 64 | pub fn init() void { 65 | asm volatile ("cli"); 66 | serial.print("Start IDT init\n", .{}); 67 | 68 | inline for (0..31) |i| { 69 | idt.entries[i] = IdtEntry.new(interrupt.interrupt_vector[i], 0x8F); // 0x8E = Interrupt Gate 70 | } 71 | 72 | inline for (31..256) |i| { 73 | idt.entries[i] = IdtEntry.new(interrupt.interrupt_vector[i], 0x8E); // 0x8E = Interrupt Gate 74 | } 75 | 76 | var idtptr = IdtPtr{ .size = @sizeOf(Idt) - 1, .base_address = @intFromPtr(&idt) }; 77 | 78 | idt_load(@intFromPtr(&idtptr)); 79 | 80 | asm volatile ("sti"); 81 | 82 | serial.print_ok("IDT", .{}); 83 | } 84 | -------------------------------------------------------------------------------- /src/arch/x86/idt/idt.s: -------------------------------------------------------------------------------- 1 | [bits 64] 2 | 3 | idt_load: 4 | lidt [rdi] 5 | ret 6 | 7 | global idt_load 8 | -------------------------------------------------------------------------------- /src/arch/x86/idt/interrupt.s: -------------------------------------------------------------------------------- 1 | [bits 64] 2 | %macro push_all 0 3 | push rax 4 | push rbx 5 | push rcx 6 | push rdx 7 | push rbp 8 | push rsi 9 | push rdi 10 | push r8 11 | push r9 12 | push r10 13 | push r11 14 | push r12 15 | push r13 16 | push r14 17 | push r15 18 | 19 | %endmacro 20 | 21 | %macro pop_all 0 22 | pop r15 23 | pop r14 24 | pop r13 25 | pop r12 26 | pop r11 27 | pop r10 28 | pop r9 29 | pop r8 30 | pop rdi 31 | pop rsi 32 | pop rbp 33 | pop rdx 34 | pop rcx 35 | pop rbx 36 | pop rax 37 | %endmacro 38 | 39 | 40 | %macro INTERRUPT_ERR 1 41 | interrupt_%1: 42 | push qword %1 43 | jmp interrupt_common 44 | 45 | %endmacro 46 | 47 | %macro INTERRUPT_NOERR 1 48 | interrupt_%1: 49 | push qword 0 50 | push qword %1 51 | jmp interrupt_common 52 | 53 | %endmacro 54 | 55 | 56 | extern interrupt_handler 57 | 58 | interrupt_common: 59 | cld 60 | push_all 61 | 62 | mov rdi, rsp 63 | call interrupt_handler 64 | mov rsp, rax 65 | 66 | pop_all 67 | 68 | add rsp, 16 ; clear error code and interrupt number 69 | 70 | iretq 71 | 72 | INTERRUPT_NOERR 0 73 | INTERRUPT_NOERR 1 74 | INTERRUPT_NOERR 2 75 | INTERRUPT_NOERR 3 76 | INTERRUPT_NOERR 4 77 | INTERRUPT_NOERR 5 78 | INTERRUPT_NOERR 6 79 | INTERRUPT_NOERR 7 80 | INTERRUPT_ERR 8 81 | INTERRUPT_NOERR 9 82 | INTERRUPT_ERR 10 83 | INTERRUPT_ERR 11 84 | INTERRUPT_ERR 12 85 | INTERRUPT_ERR 13 86 | INTERRUPT_ERR 14 87 | INTERRUPT_NOERR 15 88 | INTERRUPT_NOERR 16 89 | INTERRUPT_ERR 17 90 | INTERRUPT_NOERR 18 91 | INTERRUPT_NOERR 19 92 | INTERRUPT_NOERR 20 93 | INTERRUPT_NOERR 21 94 | INTERRUPT_NOERR 22 95 | INTERRUPT_NOERR 23 96 | INTERRUPT_NOERR 24 97 | INTERRUPT_NOERR 25 98 | INTERRUPT_NOERR 26 99 | INTERRUPT_NOERR 27 100 | INTERRUPT_NOERR 28 101 | INTERRUPT_NOERR 29 102 | INTERRUPT_ERR 30 103 | INTERRUPT_NOERR 31 104 | 105 | %assign i 32 106 | %rep 224 107 | INTERRUPT_NOERR i 108 | %assign i i+1 109 | %endrep 110 | 111 | 112 | %macro INTERRUPT_NAME 1 113 | 114 | dq interrupt_%1 115 | 116 | %endmacro 117 | 118 | section .data 119 | global interrupt_vector 120 | %define idt_size 256 121 | 122 | interrupt_vector: 123 | %assign i 0 124 | %rep idt_size 125 | INTERRUPT_NAME i 126 | %assign i i+1 127 | %endrep 128 | 129 | -------------------------------------------------------------------------------- /src/arch/x86/idt/interrupt.zig: -------------------------------------------------------------------------------- 1 | const serial = @import("root").drivers.serial; 2 | const keyboard_handle = @import("root").drivers.keyboard.handle; 3 | const lapic = @import("root").drivers.lapic; 4 | const scheduler = @import("root").scheduler; 5 | 6 | pub extern var interrupt_vector: [256]usize; 7 | 8 | pub var max: u8 = 0; 9 | 10 | pub const Regs = packed struct(u960) { 11 | r15: u64, 12 | r14: u64, 13 | r13: u64, 14 | r12: u64, 15 | r11: u64, 16 | r10: u64, 17 | r9: u64, 18 | r8: u64, 19 | rdi: u64, 20 | rsi: u64, 21 | rbp: u64, 22 | rdx: u64, 23 | rcx: u64, 24 | rbx: u64, 25 | rax: u64, 26 | }; 27 | 28 | pub const Iret = packed struct(u320) { 29 | rip: u64, 30 | cs: u64, 31 | flags: u64, 32 | rsp: u64, 33 | ss: u64, 34 | 35 | pub inline fn debug(self: @This()) void { 36 | serial.println_nolock( 37 | \\ rip: {x} 38 | \\ cs: {x} 39 | \\ flags: {x} 40 | \\ rsp: {x} 41 | \\ ss: {x} 42 | , .{ self.rip, self.cs, self.flags, self.rsp, self.ss }); 43 | } 44 | }; 45 | 46 | pub const Context = packed struct(u1408) { 47 | regs: Regs, 48 | interrupt_no: u64, 49 | error_code: u64, 50 | iret: Iret, 51 | 52 | pub inline fn takeValueOf(self: *@This(), from: *const @This()) void { 53 | self.* = from.*; 54 | } 55 | }; 56 | 57 | pub const Log = packed struct { 58 | const Stacktrace = packed struct { 59 | next: *Stacktrace, 60 | addr: u64, 61 | }; 62 | 63 | const expections_name = [_][]const u8{ 64 | "Division by zero", 65 | "Debug", 66 | "Non-maskable Interrupt", 67 | "Breakpoint", 68 | "Overflow", 69 | "Bound range Exceeded", 70 | "Invalid Opcode", 71 | "Device not available", 72 | "Double fault", 73 | "0x9", 74 | "Invalid TSS", 75 | "Segment not present", 76 | "Stack-Segment Fault", 77 | "General Protection fault", 78 | "Page Fault", 79 | "Reversed", 80 | "x87 Floating-Point exception", 81 | "Alignment check", 82 | "Machine check", 83 | "SIMD Floating-Point Excepction", 84 | "Virtualization Exception", 85 | "Control Protection Exception", 86 | "Reserved", 87 | "Reserved", 88 | "Reserved", 89 | "Reserved", 90 | "Reserved", 91 | "Hypervisor injection exception", 92 | "VMM communcation exception", 93 | "Security exception", 94 | "Reserved", 95 | }; 96 | 97 | pub fn log(ctx: *const Context) void { 98 | // Prevent loop 99 | if (max > 3) { 100 | while (true) 101 | asm volatile ("hlt"); 102 | } 103 | 104 | max += 1; 105 | 106 | serial.println_nolock("===== GOT AN INTERRUPT =====", .{}); 107 | // 0xE == PAGE_FAULT 108 | if (ctx.interrupt_no == 0xE) { 109 | var cr2: u64 = 0; 110 | 111 | cr2 = asm volatile ("mov %%cr2, %[value]" 112 | : [value] "=r" (-> u64), 113 | ); 114 | 115 | serial.println_nolock("FAULTY ADDR: 0x{X}", .{cr2}); 116 | } 117 | 118 | serial.println_nolock("Interrupt no: {x} name: {s}\nError code : 0b{b}\n{any}", .{ ctx.interrupt_no, expections_name[ctx.interrupt_no], ctx.error_code, ctx.regs }); 119 | write_stacktrace(ctx.regs.rbp); 120 | } 121 | 122 | pub fn write_stacktrace(rsp: u64) void { 123 | serial.println_nolock("Stacktrace:", .{}); 124 | 125 | var rbp: *align(1) Stacktrace = @ptrFromInt(rsp); 126 | 127 | var i: u32 = 0; 128 | while (@intFromPtr(rbp) != 0x0) : (i += 1) { 129 | serial.println_nolock("{}: 0x{X}", .{ i, rbp.addr }); 130 | rbp = rbp.next; 131 | } 132 | } 133 | }; 134 | 135 | pub fn irq_handler(ctx: *Context) void { 136 | if (ctx.interrupt_no == 32) { 137 | scheduler.schedule(ctx) catch { 138 | @panic("Cannot schedule"); 139 | }; 140 | return; 141 | } 142 | 143 | if (ctx.interrupt_no == 33) { 144 | const value = @import("root").assembly.inb(0x60); 145 | 146 | keyboard_handle(value); 147 | return; 148 | } 149 | } 150 | 151 | pub export fn interrupt_handler(ctx: *Context) callconv(.C) u64 { 152 | if (ctx.interrupt_no < 32) { 153 | Log.log(ctx); 154 | } else if (ctx.interrupt_no <= 32 + 15) { 155 | irq_handler(ctx); 156 | } else { 157 | serial.println_nolock("UNHANDLED INTERRUPT: {x}", .{ctx.interrupt_no}); 158 | } 159 | 160 | if (lapic.lapic) |_lapic| { 161 | _lapic.end_of_interrupt(); 162 | } 163 | 164 | return @intFromPtr(ctx); 165 | } 166 | -------------------------------------------------------------------------------- /src/arch/x86/regs.zig: -------------------------------------------------------------------------------- 1 | pub const Cr3 = packed struct(u64) { 2 | page_base_addr: u52 = 0, 3 | _reserved: u12 = 0, 4 | 5 | pub fn write_page_base(self: *Cr3, pagemap_base: u64) void { 6 | self.page_base_addr = @truncate(pagemap_base); 7 | } 8 | 9 | pub fn apply(self: *Cr3) void { 10 | const cr3_value: u64 = @bitCast(self.*); 11 | asm volatile ("mov %[value], %%cr3" 12 | : 13 | : [value] "r" (cr3_value), 14 | : "memory" 15 | ); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /src/arch/x86/syscall.s: -------------------------------------------------------------------------------- 1 | ; https://github.com/brutal-org/brutal/blob/main/sources/kernel/x86_64/syscall.s 2 | ; https://cyp.sh/blog/syscallsysret 3 | 4 | [BITS 64] 5 | 6 | %macro push_all 0 7 | 8 | push rax 9 | push rbx 10 | push rcx 11 | push rdx 12 | push rbp 13 | push rsi 14 | push rdi 15 | push r8 16 | push r9 17 | push r10 18 | push r11 19 | push r12 20 | push r13 21 | push r14 22 | push r15 23 | 24 | %endmacro 25 | 26 | %macro pop_all 0 27 | 28 | pop r15 29 | pop r14 30 | pop r13 31 | pop r12 32 | pop r11 33 | pop r10 34 | pop r9 35 | pop r8 36 | pop rdi 37 | pop rsi 38 | pop rbp 39 | pop rdx 40 | pop rcx 41 | pop rbx 42 | 43 | %endmacro 44 | 45 | global prepare_syscall_handler 46 | prepare_syscall_handler: 47 | swapgs 48 | mov [gs:0x8], rsp ; gs.saved_stack = rsp 49 | mov rsp, [gs:0x0] ; rsp = gs.syscall_stack 50 | 51 | sti 52 | 53 | ; push information (gs, cs, rip, rflags, rip...) 54 | push qword 0x23 ; user data segment 55 | push qword [gs:0x8] ; saved stack 56 | push r11 ; saved rflags 57 | push qword 0x1B ; user code segment 58 | push rcx ; current RIP 59 | 60 | push_all ; push every register 61 | 62 | mov rdi, rsp ; put the stackframe as the syscall argument 63 | mov rbp, 0 64 | 65 | extern syscall_handler 66 | call syscall_handler ; jump to beautiful higher level code 67 | 68 | pop_all ; pop every register except RAX as we use it for the return value 69 | 70 | cli 71 | 72 | mov rsp, [gs:0x8] 73 | swapgs 74 | o64 sysret 75 | -------------------------------------------------------------------------------- /src/arch/x86/syscall.zig: -------------------------------------------------------------------------------- 1 | const utils = @import("root").utils; 2 | const Regs = @import("idt/interrupt.zig").Regs; 3 | const serial = @import("root").drivers.serial; 4 | const screen = @import("../../drivers/fbscreen.zig").screen; 5 | const Msr = utils.Msr; 6 | 7 | extern fn prepare_syscall_handler() void; 8 | 9 | pub fn load_ring_3(stack: u64, code: u64) noreturn { 10 | asm volatile ( 11 | \\ push $0x23 // user ss 12 | \\ push %[stack] 13 | \\ push $0x202 // rflags 14 | \\ push $0x1B // user cs 15 | \\ push %[code] 16 | \\ iretq 17 | : 18 | : [stack] "r" (stack), 19 | [code] "r" (code), 20 | ); 21 | 22 | unreachable; 23 | } 24 | 25 | // https://github.com/brutal-org/brutal/blob/d458fa9ca9d7b88dd62dbbc715bf02feaca21d99/sources/kernel/x86_64/syscall.c 26 | pub fn init() void { 27 | Msr.write(Msr.Regs.EFER, Msr.read(Msr.Regs.EFER) | Msr.EFER_ENABLE_SYSCALL); 28 | 29 | // KERNEL_CODE * 8 << 32 | (((USER_DATA - 1) * 8 | RING_3) << 48) 30 | const star_value: u64 = 8 << 32 | ((16 * 8 | 3) << 48); 31 | 32 | Msr.write(Msr.Regs.STAR, star_value); 33 | Msr.write(Msr.Regs.LSTAR, @intFromPtr(&prepare_syscall_handler)); 34 | Msr.write(Msr.Regs.SYSCALL_FLAGS, 0xFFFF_FFFE); 35 | } 36 | 37 | pub fn set_gs(addr: usize) void { 38 | Msr.write(Msr.Regs.GS_BASE, addr); 39 | Msr.write(Msr.Regs.KERN_GS_BASE, addr); 40 | } 41 | 42 | const SyscallId = enum(u8) { 43 | Baka = 0x0, 44 | Uwu = 0x1, 45 | _, 46 | }; 47 | 48 | export fn syscall_handler(registers: *Regs) callconv(.C) void { 49 | const syscall_id: SyscallId = @enumFromInt(registers.rax); 50 | switch (syscall_id) { 51 | .Uwu => serial.println_nolock("UWU !!", .{}), 52 | else => serial.println_nolock("BAKA !!", .{}), 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/arch/x86/tss.zig: -------------------------------------------------------------------------------- 1 | pub const TaskSegment = packed struct { 2 | reserved1: u32 = 0, 3 | rsp: packed struct(u192) { 4 | rsp0: u64, 5 | rsp1: u64, 6 | rsp2: u64, 7 | }, 8 | reserved2: u64 = 0, 9 | reserved3: u64 = 0, 10 | ist: packed struct { 11 | ist1: u64, 12 | ist2: u64, 13 | ist3: u64, 14 | ist4: u64, 15 | ist5: u64, 16 | ist6: u64, 17 | ist7: u64, 18 | }, 19 | reserved4: u64 = 0, 20 | iopb: packed struct(u32) { 21 | reserved: u16 = 0, 22 | iopb: u16, 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /src/asm.zig: -------------------------------------------------------------------------------- 1 | pub inline fn inb(port: u16) u8 { 2 | return asm volatile ("inb %[port], %[value]" 3 | : [value] "={al}" (-> u8), 4 | : [port] "N{dx}" (port), 5 | ); 6 | } 7 | 8 | pub inline fn outb(port: u16, value: u8) void { 9 | asm volatile ("outb %[value], %[port]" 10 | : 11 | : [value] "{al}" (value), 12 | [port] "N{dx}" (port), 13 | ); 14 | } 15 | 16 | pub inline fn inw(port: u16) u16 { 17 | return asm volatile ("inw %[port], %[value]" 18 | : [value] "={ax}" (-> u16), 19 | : [port] "N{dx}" (port), 20 | ); 21 | } 22 | 23 | pub inline fn outw(port: u16, value: u16) void { 24 | asm volatile ("outl %[value], %[port]" 25 | : 26 | : [value] "{ax}" (value), 27 | [port] "N{dx}" (port), 28 | ); 29 | } 30 | 31 | pub inline fn inl(port: u16) u32 { 32 | return asm volatile ("inl %[port], %[value]" 33 | : [value] "={eax}" (-> u32), 34 | : [port] "N{dx}" (port), 35 | ); 36 | } 37 | 38 | pub inline fn outl(port: u16, value: u32) void { 39 | asm volatile ("outl %[value], %[port]" 40 | : 41 | : [value] "{eax}" (value), 42 | [port] "N{edx}" (port), 43 | ); 44 | } 45 | 46 | pub inline fn hlt() void { 47 | asm volatile ("hlt"); 48 | } 49 | -------------------------------------------------------------------------------- /src/drivers/ahci.zig: -------------------------------------------------------------------------------- 1 | // https://osdev.wiki/wiki/AHCI 2 | 3 | const FisType = struct { 4 | /// Register FIS - host to device 5 | const REG_H2D = 0x27; 6 | 7 | /// Register FIS - device to host 8 | const REG_D2H = 0x34; 9 | 10 | /// DMA activate FIS - device to host 11 | const DMA_ACT = 0x39; 12 | 13 | /// DMA setup FIS - bidirectional 14 | const DMA_SETUP = 0x41; 15 | 16 | /// Data FIS - bidirectional 17 | const DATA = 0x46; 18 | 19 | /// BIST activate FIS - bidirectional 20 | const BIST = 0x58; 21 | 22 | /// PIO setup FIS - device to host 23 | const PIO_SETUP = 0x5F; 24 | 25 | /// Set device bits FIS - device to host 26 | const DEV_BITS = 0xA1; 27 | }; 28 | 29 | const Tag = struct { 30 | const Reg_H2D = packed struct { 31 | const FeaturesRegister = struct {}; 32 | 33 | // Should be FisType.REG_H2D 34 | fis_type: u8, 35 | 36 | /// Port multiplier 37 | pmport: u4, 38 | rsv0: u3, 39 | c: u1, 40 | 41 | status_reg: u8, 42 | error_reg: u8, 43 | 44 | lba0: u8, 45 | lba1: u8, 46 | lba2: u8, 47 | /// Device register 48 | device: u8, 49 | 50 | lba3: u8, 51 | lba4: u8, 52 | lba5: u8, 53 | feature: FeaturesRegister, 54 | 55 | /// Count low 56 | countl: u8, 57 | counth: u8, 58 | /// Isochronous command completion 59 | icc: u8, 60 | control: u8, 61 | rsv1: u32, 62 | }; 63 | 64 | const Reg_D2H = packed struct { 65 | // Should be FisType.REG_H2D 66 | fis_type: u8, 67 | 68 | /// Port multiplier 69 | pmport: u4, 70 | rsv0: u3, 71 | /// Interrupt bit 72 | i: u1, 73 | rsv1: u1, 74 | 75 | status_reg: u8, 76 | error_reg: u8, 77 | 78 | lba0: u8, 79 | lba1: u8, 80 | lba2: u8, 81 | /// Device register 82 | device: u8, 83 | 84 | lba3: u8, 85 | lba4: u8, 86 | lba5: u8, 87 | rsv2: u8, 88 | 89 | /// Count low 90 | countl: u8, 91 | counth: u8, 92 | 93 | rsv4: u48, 94 | }; 95 | 96 | const FisData = packed struct(u64) { 97 | fis_type: u8, 98 | 99 | /// Port multiplier 100 | pmport: u4, 101 | rsv1: u20, 102 | 103 | /// Payload 104 | data: u32, 105 | }; 106 | 107 | const FisPioSetup = packed struct { 108 | // Should be FisType.REG_H2D 109 | fis_type: u8, 110 | 111 | /// Port multiplier 112 | pmport: u4, 113 | rsv0: u1, 114 | d: u1, 115 | /// Interrupt bit 116 | i: u1, 117 | rsv1: u1, 118 | 119 | status_reg: u8, 120 | error_reg: u8, 121 | 122 | lba0: u8, 123 | lba1: u8, 124 | lba2: u8, 125 | /// Device register 126 | device: u8, 127 | 128 | lba3: u8, 129 | lba4: u8, 130 | lba5: u8, 131 | rsv2: u8, 132 | 133 | /// Count low 134 | countl: u8, 135 | counth: u8, 136 | rsv3: u8, 137 | e_status: u8, 138 | 139 | transfer_count: u16, 140 | rsv4: u16, 141 | }; 142 | 143 | const FisDmaSetup = struct { 144 | pub const Direction = enum(u1) { 145 | HostToDevice, 146 | DeviceToHost, 147 | }; 148 | 149 | fis_type: u8, 150 | pmport: u4, 151 | rsv0: u1, 152 | 153 | d: Direction, 154 | i: u1, 155 | 156 | /// Auto-activate. Specifies if DMA Activate FIS is neeeded 157 | a: u1, 158 | 159 | rsv0: u16, 160 | 161 | dma_buffer_id: u64, 162 | rsv1: u32, 163 | dma_buffer_offset: u32, 164 | transfer_count: u32, 165 | rsv2: u32, 166 | }; 167 | }; 168 | -------------------------------------------------------------------------------- /src/drivers/audio.zig: -------------------------------------------------------------------------------- 1 | const pci = @import("./pci.zig"); 2 | const rq = @import("../limine_rq.zig"); 3 | const serial = @import("./serial.zig"); 4 | 5 | // https://osdev.wiki/wiki/Intel_High_Definition_Audio#Device_Registers 6 | // https://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/high-definition-audio-specification.pdf 7 | const HdaRegister = packed struct { 8 | const Corb = packed struct(u128) { 9 | corb_lower_base: u32, 10 | corb_upper_base: u32, 11 | corb_writer_ptr: u16, 12 | corb_read_ptr: u16, 13 | corb_ctrl: u8, 14 | corb_status: u8, 15 | corb_size: u8, 16 | reserved: u8, 17 | }; 18 | 19 | const Rirb = packed struct { 20 | rirb_lower_base: u32, 21 | rirb_upper_base: u32, 22 | rirb_writer_ptr: u16, 23 | response_interrupt_count: u16, 24 | rirb_ctrl: u8, 25 | rirb_status: u8, 26 | rirb_size: u8, 27 | reserved: u8, 28 | }; 29 | 30 | const GlobalCapabilities = packed struct(u16) { 31 | is_64bits: bool, 32 | // sdo = serial data out signals 33 | sdo_count: u2, 34 | bidirection_stream_count: u5, 35 | input_stream_count: u4, 36 | output_stream_count: u4, 37 | }; 38 | 39 | global_caps: u16, 40 | min_ver: u8, 41 | maj_ver: u8, 42 | output_payload_caps: u16, 43 | input_payload_caps: u16, 44 | global_ctrl: packed struct(u32) { 45 | controller_reset: u1, 46 | flush_control: u1, 47 | reserved0: u6, 48 | accept_unsollicited_response: bool, 49 | reserved1: u23, 50 | 51 | pub fn reset(self: *@This()) void { 52 | self.controller_reset = 0; 53 | } 54 | 55 | pub fn is_in_reset_state(self: *@This()) bool { 56 | return self.controller_reset == 0; 57 | } 58 | }, 59 | wake_enable: u16, 60 | wake_status: u16, 61 | global_status: u16, 62 | reserved0: u48, 63 | output_stream_payload_caps: u16, 64 | input_stream_payload_caps: u16, 65 | reserved1: u32, 66 | interrupt_ctrl: u32, 67 | interrupt_status: u32, 68 | reserved2: u64, 69 | wall_clock_counter: u32, 70 | reserved3: u32, 71 | stream_sync: u32, 72 | reserved4: u32, 73 | corb: Corb, 74 | rirb: Rirb, 75 | 76 | immediate_command_output: u32, 77 | immediate_command_input: u32, 78 | immediate_command_status: u16, 79 | reserved5: u48, 80 | 81 | dma_position_lower_base: u32, 82 | dma_position_upper_base: u32, 83 | }; 84 | 85 | const IntelHda = struct { 86 | pci: *pci.Pci, 87 | 88 | register: *HdaRegister, 89 | }; 90 | 91 | pub fn init(device: *const pci.Pci, _: u16, _: u16) !void { 92 | const bar = (try device.bar(0)) orelse { 93 | return error.NoBar; 94 | }; 95 | serial.println("{X}, PTR: {X}", .{ bar.base, bar.base + rq.hhdm.response.?.offset }); 96 | 97 | const hda_register: *HdaRegister = @ptrFromInt(bar.base + rq.hhdm.response.?.offset); 98 | 99 | const phys_hda_register: *HdaRegister = @ptrFromInt(bar.base); 100 | 101 | serial.println("{any}", .{phys_hda_register}); 102 | 103 | serial.println("{any}", .{hda_register}); 104 | 105 | hda_register.global_ctrl.reset(); 106 | while (hda_register.global_ctrl.is_in_reset_state()) {} 107 | 108 | serial.println("Reseted", .{}); 109 | 110 | // hda_register.global_ctrl = hda_register.global_ctrl | 1; 111 | // while ((hda_register.global_ctrl & (1 << 0)) == 0) { 112 | // asm volatile ("pause"); 113 | // } 114 | 115 | @import("../drivers/hpet.zig").hpet.?.sleep(1); 116 | 117 | serial.println("{any}", .{hda_register}); 118 | 119 | serial.println("{any}", .{phys_hda_register}); 120 | } 121 | -------------------------------------------------------------------------------- /src/drivers/drivers.zig: -------------------------------------------------------------------------------- 1 | pub const acpi = @import("../acpi//acpi.zig"); 2 | pub const ps2 = @import("./ps2.zig"); 3 | pub const serial = @import("serial.zig"); 4 | pub const fb = @import("fbscreen.zig"); 5 | pub const keyboard = @import("keyboard.zig"); 6 | pub const lapic = @import("lapic.zig"); 7 | 8 | pub fn init() !void { 9 | try acpi.init(); 10 | 11 | try ps2.init(); 12 | } 13 | -------------------------------------------------------------------------------- /src/drivers/fbscreen.zig: -------------------------------------------------------------------------------- 1 | const psf2 = @import("../psf.zig"); 2 | const std = @import("std"); 3 | 4 | pub var fb_ptr: ?Framebuffer = null; 5 | 6 | const Glyph = struct { 7 | character: u8, 8 | font: *align(1) const psf2.Psf2, 9 | color_fg: Color, 10 | color_bg: Color, 11 | 12 | pub inline fn getColor(self: *const @This(), is_foreground: bool) Color { 13 | if (is_foreground) { 14 | return self.color_fg; 15 | } 16 | 17 | return self.color_bg; 18 | } 19 | 20 | pub fn writeGlyph(self: *const @This(), x: usize, y: usize, fb: *const Framebuffer) void { 21 | var lines = self.font.readGlyph(self.character); 22 | var offset: u64 = y; 23 | while (lines.iter()) |line| { 24 | var bit = line; 25 | 26 | for (0..10) |i| { 27 | fb.writePixel(10 - i + x, offset, self.getColor((bit & 1) == 1)); 28 | 29 | bit >>= 1; 30 | } 31 | 32 | offset += 1; 33 | } 34 | } 35 | }; 36 | 37 | pub const Color = packed struct(u32) { 38 | blue: u8, 39 | green: u8, 40 | red: u8, 41 | alpha: u8 = 255, 42 | 43 | const BLACK: Color = .{ .blue = 0, .green = 0, .red = 0 }; 44 | const WHITE: Color = .{ .blue = 255, .green = 255, .red = 255 }; 45 | }; 46 | 47 | pub const Screen = struct { 48 | max_x: usize, 49 | max_y: usize, 50 | 51 | x: usize = 0, 52 | y: usize = 0, 53 | 54 | pub fn resetAll(self: *@This()) void { 55 | var x: usize = 0; 56 | 57 | while (x <= self.max_x) : (x += 10) { 58 | var y: usize = 0; 59 | while (y <= self.max_y) : (y += 16) { 60 | if (fb_ptr) |fb| { 61 | fb.print(' ', x, y); 62 | } 63 | } 64 | } 65 | 66 | self.reset(); 67 | } 68 | 69 | pub fn reset(self: *@This()) void { 70 | self.x = 0; 71 | self.y = 0; 72 | } 73 | 74 | pub fn add(self: *@This(), to_add: usize) void { 75 | if (self.x + to_add > self.max_x) { 76 | self.y += 16 * ((self.x + to_add) / self.max_x); 77 | self.x = 0; 78 | return; 79 | } 80 | 81 | self.x += to_add; 82 | } 83 | 84 | pub fn remove(self: *@This(), to_remove: usize) void { 85 | self.x -|= to_remove; 86 | } 87 | 88 | pub fn nextLine(self: *@This()) void { 89 | self.x = 0; 90 | self.y += 16; 91 | } 92 | 93 | pub fn print(self: *@This(), to_write: u8) void { 94 | if (to_write == '\n') { 95 | self.nextLine(); 96 | return; 97 | } 98 | 99 | if (fb_ptr) |fb| { 100 | fb.print(to_write, self.x, self.y); 101 | self.add(10); 102 | } 103 | } 104 | 105 | pub fn println(self: *@This(), to_write: []const u8) void { 106 | for (to_write) |c| { 107 | self.print(c); 108 | } 109 | self.nextLine(); 110 | } 111 | 112 | pub fn writeWithContext(self: *Screen, values: []const u8) WriteError!usize { 113 | for (values) |value| { 114 | self.print(value); 115 | } 116 | 117 | return values.len; 118 | } 119 | 120 | const WriteError = error{CannotWrite}; 121 | 122 | pub const ScreenWriter = std.io.Writer(*Screen, WriteError, writeWithContext); 123 | 124 | pub fn writer(self: *@This()) ScreenWriter { 125 | return .{ .context = self }; 126 | } 127 | }; 128 | 129 | pub var screen: ?Screen = null; 130 | 131 | pub const Framebuffer = struct { 132 | height: u64, 133 | width: u64, 134 | ptr: [*]u32, 135 | pitch: u64, 136 | bpp: u16, 137 | cursor: u64, 138 | font: *align(1) const psf2.Psf2, 139 | 140 | pub fn init(ptr: u64, height: u64, width: u64, pitch: u64, bpp: u16, font: *align(1) const psf2.Psf2) @This() { 141 | screen = .{ .max_y = width, .max_x = height }; 142 | return .{ .ptr = @ptrFromInt(ptr), .height = height, .width = width, .cursor = 0, .pitch = pitch, .bpp = bpp, .font = font }; 143 | } 144 | 145 | pub fn write(self: *@This(), color: Color) void { 146 | self.writeAt(color, self.cursor); 147 | 148 | self.cursor += 1; 149 | } 150 | 151 | pub fn print(self: *const @This(), to_write: u8, x: usize, y: usize) void { 152 | const glyph: Glyph = .{ 153 | .character = to_write, 154 | .font = self.font, 155 | .color_fg = Color.BLACK, 156 | .color_bg = Color.WHITE, 157 | }; 158 | glyph.writeGlyph(x, y, self); 159 | } 160 | 161 | pub fn print_str( 162 | self: *@This(), 163 | to_write: []const u8, 164 | x: usize, 165 | y: usize, 166 | ) void { 167 | var offset_line: usize = 0; 168 | var column = x; 169 | for (to_write) |c| { 170 | if (c == '\n') { 171 | offset_line += self.font.header.height; 172 | column = x; 173 | continue; 174 | } 175 | self.print(c, column, y + offset_line); 176 | column += self.font.header.width; 177 | } 178 | } 179 | 180 | pub fn writePixel(self: *const @This(), x: usize, y: usize, color: Color) void { 181 | const pos = x + (self.pitch / @sizeOf(u32)) * y; 182 | self.ptr[pos] = @bitCast(color); 183 | } 184 | 185 | pub fn writeAt( 186 | self: *const @This(), 187 | color: Color, 188 | offset: u64, 189 | ) void { 190 | self.ptr[offset] = @bitCast(color); 191 | } 192 | 193 | pub fn fillWith(self: *@This(), fill: fn (x: u64, y: u64) Color) !void { 194 | for (0..self.height) |y| { 195 | for (0..self.width) |x| { 196 | self.write(fill(x, y)); 197 | } 198 | } 199 | } 200 | }; 201 | -------------------------------------------------------------------------------- /src/drivers/hpet.zig: -------------------------------------------------------------------------------- 1 | const acpi = @import("../acpi/acpi.zig"); 2 | const limine = @import("../limine_rq.zig"); 3 | 4 | pub var hpet: ?Hpet = null; 5 | var clock: u64 = 0; 6 | 7 | const AcpiHpet = packed struct(u448) { 8 | header: acpi.AcpiHeader, 9 | rev_id: u8, 10 | info: u8, 11 | vendor_id: u16, 12 | address_space: u8, 13 | register_bit_width: u8, 14 | register_bit_offset: u8, 15 | reserved: u8, 16 | address: u64, 17 | hpet_num: u8, 18 | minimum_tick: u16, 19 | page_protection: u8, 20 | }; 21 | 22 | const Hpet = packed struct { 23 | addr: u64, 24 | 25 | pub fn write(self: *align(1) @This(), reg: u64, value: u64) void { 26 | @as(*align(1) volatile u64, @ptrFromInt(self.addr + reg)).* = value; 27 | } 28 | 29 | pub fn read(self: *@This(), reg: u64) u64 { 30 | return @as(*align(1) volatile u64, @ptrFromInt(self.addr + reg)).*; 31 | } 32 | 33 | const Regs = struct { 34 | const GENERAL_CAPABILITIES = 0x0; 35 | const GENERAL_CONFIGURATION = 0x10; 36 | const MAIN_COUNTER_VALUE = 0xF0; 37 | }; 38 | 39 | const CONF_OFF = 0; 40 | const CONF_ON = 1; 41 | 42 | pub fn init(self: *@This()) void { 43 | clock = self.read(Regs.GENERAL_CAPABILITIES) >> 32; 44 | 45 | self.write(Regs.GENERAL_CONFIGURATION, Hpet.CONF_OFF); 46 | self.write(Regs.MAIN_COUNTER_VALUE, 0); 47 | self.write(Regs.GENERAL_CONFIGURATION, Hpet.CONF_ON); 48 | } 49 | 50 | pub fn sleep(self: *@This(), ms: u64) void { 51 | const max: u64 = self.read(Regs.MAIN_COUNTER_VALUE) + (ms * 1_000_000_000_000) / clock; 52 | 53 | while (self.read(Regs.MAIN_COUNTER_VALUE) < max) {} 54 | } 55 | }; 56 | 57 | pub fn init() !void { 58 | const hpet_acpi: *align(1) AcpiHpet = @ptrCast(try acpi.xspt.?.get("HPET")); 59 | 60 | if (hpet_acpi.address_space == 1) { 61 | return error.UnsupportedSystemIO; 62 | } 63 | 64 | if (hpet_acpi.address == 0) { 65 | return error.WeirdAddr; 66 | } 67 | 68 | hpet = .{ .addr = hpet_acpi.address + limine.hhdm.response.?.offset }; 69 | 70 | hpet.?.init(); 71 | } 72 | -------------------------------------------------------------------------------- /src/drivers/ioapic.zig: -------------------------------------------------------------------------------- 1 | const serial = @import("./serial.zig"); 2 | const limine_rq = @import("../limine_rq.zig"); 3 | const acpi = @import("../acpi/acpi.zig"); 4 | 5 | /// https://osdev.wiki/wiki/IOAPIC#IOREDTBL 6 | pub const IoApicRedirect = packed struct(u64) { 7 | int_vec: u8 = 0, 8 | delivery_mode: DeliveryMode = .Fixed, 9 | destination_mode: DestinationMode = .Physical, 10 | delivery_status: bool = false, 11 | interrupt_pin: InterruptPinPolarity = .High, 12 | remote_irr: bool = false, 13 | trigger_mode: bool = false, 14 | interrupt_mask: bool = false, 15 | reserved: u39 = 0, 16 | destination: u8 = 0, 17 | 18 | const InterruptPinPolarity = enum(u1) { 19 | High = 0, 20 | Low = 1, 21 | }; 22 | 23 | const DestinationMode = enum(u1) { 24 | Physical = 0, 25 | Logical = 1, 26 | }; 27 | 28 | const DeliveryMode = enum(u3) { 29 | Fixed = 0b000, 30 | LowestPriority = 0b001, 31 | SystemManagementInterrupt = 0b010, 32 | NonMaskableInterrupt = 0b100, 33 | Init = 0b101, 34 | ExtInt = 0b111, 35 | }; 36 | 37 | const IOAPIC_ACTIVE_HIGH_WHEN_LOW = (1 << 1); 38 | const IOAPIC_LEVEL_TRIGGER = (1 << 3); 39 | }; 40 | 41 | pub const IoApic = packed struct { 42 | ioapic_id: u8, 43 | reserved: u8, 44 | ioapic_addr: u32, 45 | gsib: u32, 46 | 47 | pub fn write(self: *align(1) @This(), reg: u32, value: u32) void { 48 | const base: u64 = self.ioapic_addr + limine_rq.hhdm.response.?.offset; 49 | 50 | serial.println("Will write at {x}", .{base}); 51 | @as(*volatile u32, @ptrFromInt(base)).* = reg; 52 | @as(*volatile u32, @ptrFromInt(base + 16)).* = value; 53 | } 54 | 55 | pub fn redirect_gsi(self: *align(1) @This(), lapic_id: u32, intno: u8, gsi: u32, flags: u16) void { 56 | var acpi_redirect: IoApicRedirect = .{ 57 | .int_vec = intno, 58 | }; 59 | 60 | if ((flags & IoApicRedirect.IOAPIC_ACTIVE_HIGH_WHEN_LOW) == IoApicRedirect.IOAPIC_ACTIVE_HIGH_WHEN_LOW) { 61 | acpi_redirect.interrupt_pin = .Low; 62 | } 63 | 64 | if ((flags & IoApicRedirect.IOAPIC_LEVEL_TRIGGER) == IoApicRedirect.IOAPIC_LEVEL_TRIGGER) { 65 | acpi_redirect.trigger_mode = true; 66 | } 67 | 68 | acpi_redirect.destination = @intCast(lapic_id); 69 | 70 | const redirect_table: u32 = (gsi - self.gsib) * 2 + 16; 71 | const struct_as_u64: u64 = @bitCast(acpi_redirect); 72 | serial.println("{}", .{redirect_table}); 73 | 74 | self.write(redirect_table, @intCast(struct_as_u64)); 75 | self.write(redirect_table + 1, @intCast(struct_as_u64 >> 32)); 76 | } 77 | 78 | pub fn redirect(self: *align(1) @This(), lapic_id: u32, intno: u8, irq: u8) void { 79 | if (acpi.madt.?.get_iso(irq)) |iso| { 80 | serial.println("Will redirect {} to {} with GSI flags", .{ intno, irq }); 81 | self.redirect_gsi(lapic_id, intno, iso.gsi, iso.flags); 82 | } else { 83 | serial.println("Will redirect {} to {} with 0 flags", .{ intno, irq }); 84 | self.redirect_gsi(lapic_id, intno, irq, 0); 85 | } 86 | } 87 | 88 | pub fn init(self: *align(1) @This()) void { 89 | for (0..16) |i| { 90 | // i + 32 because there is 32 exceptions 91 | self.redirect( 92 | 0, 93 | @as(u8, @intCast(i)) + 32, 94 | @as(u8, @intCast(i)), 95 | ); 96 | } 97 | } 98 | }; 99 | -------------------------------------------------------------------------------- /src/drivers/keyboard.zig: -------------------------------------------------------------------------------- 1 | const serial = @import("serial.zig"); 2 | 3 | const KEYBOARDMAP: [56]u8 = [_]u8{ 4 | '\x00', '\x00', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\x00', '\t', 'q', 'w', 5 | 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\\', '\x00', 'a', 's', 'd', 'f', 'g', 'h', 6 | 'j', 'k', 'l', ';', '\'', '`', ' ', '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 7 | ' ', '*', 8 | }; 9 | 10 | const MAJKEYBOARDMAP: [56]u8 = [_]u8{ 11 | '\x00', '\x00', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\x00', '\t', 'Q', 'W', 12 | 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '|', '\x00', 'A', 'S', 'D', 'F', 'G', 'H', 13 | 'J', 'K', 'L', ':', '"', '\n', ' ', '\\', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 14 | ' ', '*', 15 | }; 16 | 17 | const KeyboardEvent = enum { 18 | Key, 19 | Esc, 20 | Space, 21 | Shift, 22 | Ctrl, 23 | Enter, 24 | BackSpace, 25 | Other, 26 | }; 27 | 28 | pub fn event2enum(scancode: u8) KeyboardEvent { 29 | const event = switch (scancode) { 30 | 0x01 => KeyboardEvent.Esc, 31 | 0x0E => KeyboardEvent.BackSpace, 32 | 0x1C => KeyboardEvent.Enter, 33 | 0x36, 0x2A => KeyboardEvent.Shift, 34 | 0x39 => KeyboardEvent.Space, 35 | 0x1D => KeyboardEvent.Ctrl, 36 | else => { 37 | if (scancode >= comptime @as(u8, KEYBOARDMAP.len)) { 38 | return KeyboardEvent.Other; 39 | } 40 | 41 | return KeyboardEvent.Key; 42 | }, 43 | }; 44 | 45 | return event; 46 | } 47 | 48 | var shift = false; 49 | var ctrl = false; 50 | 51 | pub fn handle(scancode: u8) void { 52 | const event = event2enum(scancode); 53 | var screen = &@import("./fbscreen.zig").screen.?; 54 | 55 | switch (event) { 56 | KeyboardEvent.Key => { 57 | if (shift) { 58 | serial.Serial.write(MAJKEYBOARDMAP[scancode]); 59 | screen.print(MAJKEYBOARDMAP[scancode]); 60 | return; 61 | } 62 | serial.Serial.write(KEYBOARDMAP[scancode]); 63 | screen.print(KEYBOARDMAP[scancode]); 64 | }, 65 | KeyboardEvent.Space => { 66 | serial.Serial.write(' '); 67 | screen.print(' '); 68 | }, 69 | KeyboardEvent.BackSpace => { 70 | screen.remove(10); 71 | screen.print(' '); 72 | screen.remove(10); 73 | }, 74 | KeyboardEvent.Shift => shift = !shift, 75 | KeyboardEvent.Enter => { 76 | screen.nextLine(); 77 | }, 78 | else => { 79 | serial.println("Unsupported: {any} {x}", .{ event, scancode }); 80 | }, 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/drivers/lapic.zig: -------------------------------------------------------------------------------- 1 | const serial = @import("./serial.zig"); 2 | const acpi = @import("../acpi/acpi.zig"); 3 | const limine = @import("../limine_rq.zig"); 4 | const asm_utils = @import("../utils.zig"); 5 | const hpet = @import("./hpet.zig"); 6 | 7 | pub var lapic: ?Lapic = null; 8 | 9 | pub const Lapic = struct { 10 | addr: u64, 11 | 12 | pub fn read(self: *const @This(), reg: u32) u32 { 13 | return @as(*align(1) volatile u32, @ptrFromInt(self.addr + reg)).*; 14 | } 15 | 16 | pub fn write(self: *const @This(), reg: u32, value: u32) void { 17 | @as(*align(1) volatile u32, @ptrFromInt(self.addr + reg)).* = value; 18 | } 19 | 20 | pub fn end_of_interrupt(self: @This()) void { 21 | self.write(Regs.EOI, 0); 22 | } 23 | 24 | pub const Regs = struct { 25 | pub const ERROR_STATUS = 0x280; 26 | pub const CPU_ID = 0x20; 27 | pub const EOI = 0x0b0; 28 | pub const SPURIOUS = 0x0f0; 29 | pub const ICR0 = 0x300; 30 | pub const ICR1 = 0x310; 31 | pub const LVT_TIMER = 0x320; 32 | pub const TIMER_INITCNT = 0x380; 33 | pub const TIMER_CURRCNT = 0x390; 34 | pub const TIMER_DIV = 0x3e0; 35 | }; 36 | 37 | pub const ApicTimer = struct { 38 | pub const MASKED = 0x1000; 39 | 40 | pub const LAPIC_TIMER_IRQ = 32; 41 | pub const LAPIC_TIMER_PERIODIC = 0x20000; 42 | 43 | pub const Divisor = struct { 44 | const APIC_TIMER_DIVIDE_BY_2 = 0; 45 | const APIC_TIMER_DIVIDE_BY_4 = 1; 46 | const APIC_TIMER_DIVIDE_BY_8 = 2; 47 | const APIC_TIMER_DIVIDE_BY_16 = 3; 48 | const APIC_TIMER_DIVIDE_BY_32 = 4; 49 | const APIC_TIMER_DIVIDE_BY_64 = 5; 50 | const APIC_TIMER_DIVIDE_BY_128 = 6; 51 | const APIC_TIMER_DIVIDE_BY_1 = 7; 52 | }; 53 | }; 54 | 55 | pub const LAPIC_ENABLED = 0x800; 56 | pub const MSR_APIC_BASE = 0x1B; 57 | pub const SPURIOUS_ALL = 0xFF; 58 | pub const SPURIOUS_ENABLE_APIC = 0x100; 59 | }; 60 | 61 | pub fn init() !void { 62 | lapic = .{ .addr = acpi.madt.?.lapic_addr + limine.hhdm.response.?.offset }; 63 | serial.println("lapic: {any}", .{lapic}); 64 | 65 | lapic.?.write(Lapic.MSR_APIC_BASE, @truncate((asm_utils.read_msr(Lapic.MSR_APIC_BASE) | Lapic.LAPIC_ENABLED) & ~@as(u64, 1 << 10))); 66 | lapic.?.write(Lapic.Regs.SPURIOUS, lapic.?.read(Lapic.Regs.SPURIOUS) | Lapic.SPURIOUS_ALL | Lapic.SPURIOUS_ENABLE_APIC); 67 | } 68 | 69 | pub fn init_timer() void { 70 | const _lapic = lapic.?; 71 | 72 | _lapic.write(Lapic.Regs.TIMER_DIV, Lapic.ApicTimer.Divisor.APIC_TIMER_DIVIDE_BY_16); 73 | _lapic.write(Lapic.Regs.TIMER_INITCNT, 0xFFFF_FFFF); 74 | 75 | hpet.hpet.?.sleep(10); 76 | 77 | const tick_in_10ms = 0xFFFF_FFFF - _lapic.read(Lapic.Regs.TIMER_CURRCNT); 78 | 79 | _lapic.write(Lapic.Regs.LVT_TIMER, Lapic.ApicTimer.MASKED); 80 | _lapic.write(Lapic.Regs.LVT_TIMER, Lapic.ApicTimer.LAPIC_TIMER_IRQ | Lapic.ApicTimer.LAPIC_TIMER_PERIODIC); 81 | _lapic.write(Lapic.Regs.TIMER_DIV, Lapic.ApicTimer.Divisor.APIC_TIMER_DIVIDE_BY_16); 82 | _lapic.write(Lapic.Regs.TIMER_INITCNT, tick_in_10ms / 10); 83 | } 84 | 85 | pub fn send_ipi(lapic_id: u32, vec: u32) void { 86 | var l = lapic.?; 87 | 88 | l.write(Lapic.Regs.ICR1, lapic_id); 89 | l.write(Lapic.Regs.ICR0, vec); 90 | } 91 | -------------------------------------------------------------------------------- /src/drivers/pci.zig: -------------------------------------------------------------------------------- 1 | const assembly = @import("../asm.zig"); 2 | const acpi = @import("../acpi/acpi.zig"); 3 | const limine_rq = @import("../limine_rq.zig"); 4 | const serial = @import("serial.zig"); 5 | 6 | pub const PciBar = struct { 7 | base: u64, 8 | length: u64, 9 | mmio: bool, 10 | is_64bits: bool, 11 | }; 12 | 13 | pub const PciAddr = packed struct(u32) { 14 | offset: u8 = 0, 15 | fn_no: u3, 16 | device_no: u5, 17 | bus_no: u8, 18 | reserved: u7 = 0, 19 | is_enable: bool = true, 20 | 21 | pub fn read(self: @This(), comptime size: type) size { 22 | const mcfg = acpi.mcfg.?; 23 | 24 | const entry: *align(1) acpi.Mcfg.Configuration = mcfg.get_entry_of_bus(self.bus_no) orelse @panic("PCI CANNOT READ"); 25 | 26 | return entry.read(self, size); 27 | } 28 | 29 | pub fn write(self: @This(), comptime size: type, value: size) void { 30 | const mcfg = acpi.mcfg.?; 31 | 32 | const entry = mcfg.get_entry_of_bus(self.bus_no) orelse @panic("PCI CANNOT WRITE"); 33 | 34 | return entry.write(self, size, value); 35 | } 36 | 37 | pub fn get_addr(self: @This(), cfg: acpi.Mcfg.Configuration) u64 { 38 | const addr: u64 = (@as(u64, self.bus_no - cfg.start) << 20 | @as(u64, self.device_no) << 15 | @as(u64, self.fn_no) << 12); 39 | return addr + limine_rq.hhdm.response.?.offset + cfg.base_addr + self.offset; 40 | } 41 | }; 42 | 43 | pub const HeaderType = packed struct(u8) { 44 | header_type: u7, 45 | multi_func: bool, 46 | 47 | pub fn is_standard_header(self: @This()) bool { 48 | return self.header_type == 0x0; 49 | } 50 | 51 | pub fn is_pci_to_pci_header(self: @This()) bool { 52 | return self.header_type == 0x1; 53 | } 54 | 55 | pub fn is_cardbus_header(self: @This()) bool { 56 | return self.header_type == 0x2; 57 | } 58 | }; 59 | 60 | pub const Pci = struct { 61 | bus: u8, 62 | slot: u5, 63 | function: u3, 64 | mcfg: acpi.Mcfg.Configuration, 65 | 66 | // FROM: https://admin.pci-ids.ucw.cz/read/PD/ 67 | const Class = enum { 68 | UnclassifiedDevice, 69 | MassStorage, 70 | Network, 71 | Display, 72 | Multimedia, 73 | Memory, 74 | Bridge, 75 | Communication, 76 | GenericSystemPeripheral, 77 | InputDevice, 78 | DockingStation, 79 | Processor, 80 | SerialBus, 81 | Wireless, 82 | Intelligent, 83 | SatelliteCommunications, 84 | Encryption, 85 | SignalProcessing, 86 | ProcessingAccelerator, 87 | NonEssentialInstrumentation, 88 | Coprocessor, 89 | Unassigned, 90 | Unknown, 91 | 92 | pub fn from(x: u8) Class { 93 | return switch (x) { 94 | 0 => Class.UnclassifiedDevice, 95 | 1 => Class.MassStorage, 96 | 2 => Class.Network, 97 | 3 => Class.Display, 98 | 4 => Class.Multimedia, 99 | 5 => Class.Memory, 100 | 6 => Class.Bridge, 101 | 7 => Class.Communication, 102 | 8 => Class.GenericSystemPeripheral, 103 | 9 => Class.InputDevice, 104 | 0xA => Class.DockingStation, 105 | 0xB => Class.Processor, 106 | 0xC => Class.SerialBus, 107 | 0xD => Class.Wireless, 108 | 0xE => Class.Intelligent, 109 | 0xF => Class.SatelliteCommunications, 110 | 0x10 => Class.Encryption, 111 | 0x11 => Class.SignalProcessing, 112 | 0x12 => Class.ProcessingAccelerator, 113 | 0x13 => Class.NonEssentialInstrumentation, 114 | 0x40 => Class.Coprocessor, 115 | 0xFF => Class.Unassigned, 116 | else => Class.Unknown, 117 | }; 118 | } 119 | }; 120 | 121 | const Regs = struct { 122 | const CONFIG_ADDRESS = 0xCF8; 123 | const CONFIG_DATA = 0xCFC; 124 | }; 125 | 126 | /// https://wiki.osdev.org/PCI#Command_Register 127 | const CommandRegister = packed struct(u16) { 128 | io_space: bool, 129 | memory_space: bool, 130 | bus_master: bool, 131 | special_cycles: bool, 132 | memory_write_invalidate_enable: bool, 133 | vga_palette_snoop: bool, 134 | parity_error_response: bool, 135 | _reserved0: bool, 136 | serr_enable: bool, 137 | fast_back_to_back_enable: bool, 138 | interrupt_disable: bool, 139 | _reserved1: u5, 140 | }; 141 | 142 | pub fn new(bus: u8, slot: u5, function: u3, mcfg: *align(1) const acpi.Mcfg.Configuration) Pci { 143 | return .{ .bus = bus, .slot = slot, .function = function, .mcfg = mcfg.* }; 144 | } 145 | 146 | pub fn addr(self: @This(), offset: u8) PciAddr { 147 | const pci_addr: PciAddr = .{ 148 | .fn_no = self.function, 149 | .device_no = self.slot, 150 | .bus_no = self.bus, 151 | .offset = offset, 152 | }; 153 | 154 | return pci_addr; 155 | } 156 | 157 | pub fn vendor_id(self: @This()) u16 { 158 | return self.addr(0x0).read(u16); 159 | } 160 | 161 | pub fn device_id(self: @This()) u16 { 162 | return self.addr(0x2).read(u16); 163 | } 164 | 165 | pub fn command(self: @This()) CommandRegister { 166 | return @bitCast(self.addr(0x4).read(u16)); 167 | } 168 | 169 | pub fn status(self: @This()) u16 { 170 | return self.addr(0x6).read(u16); 171 | } 172 | 173 | pub fn class(self: @This()) u8 { 174 | return self.addr(0xb).read(u8); 175 | } 176 | 177 | pub fn subclass(self: @This()) u8 { 178 | return self.addr(0xa).read(u8); 179 | } 180 | 181 | pub fn header_type(self: @This()) HeaderType { 182 | return @bitCast(self.addr(0xe).read(u8)); 183 | } 184 | 185 | pub fn set_command(self: @This(), commandreg: CommandRegister) void { 186 | const paddr = self.addr(0x4); 187 | paddr.write(u16, @bitCast(commandreg)); 188 | } 189 | 190 | pub fn set_master_flag(self: @This()) void { 191 | var command_reg = self.command(); 192 | serial.println("{b}", .{@as(u16, @bitCast(command_reg))}); 193 | command_reg.bus_master = true; 194 | serial.println("{b}", .{@as(u16, @bitCast(command_reg))}); 195 | self.set_command(command_reg); 196 | } 197 | 198 | pub fn set_mmio_flag(self: @This()) void { 199 | var command_reg = self.command(); 200 | command_reg.memory_space = true; 201 | self.set_command(command_reg); 202 | } 203 | 204 | const IS_IO: u32 = 1; 205 | const BAR_TYPE_MASK: u32 = 0x6; 206 | const BAR_TYPE_64BIT: u32 = 0x4; 207 | const BAR_PORT_MASK: u32 = 0xFFFF_FFFC; 208 | const BAR_MMIO_ADDR_MASK: u32 = 0xFFFF_FFF0; 209 | 210 | pub fn bar(self: @This(), n: u8) !?PciBar { 211 | if (n >= 6) { 212 | return error.BarOverflow; 213 | } 214 | 215 | const offset: u8 = 0x10 + 4 * n; 216 | 217 | var pcibar: PciBar = undefined; 218 | const pci_addr = self.addr(offset); 219 | const bar_lower = pci_addr.read(u32); 220 | 221 | const is_io: bool = (bar_lower & IS_IO) == 1; 222 | 223 | if (is_io) { 224 | return .{ 225 | .base = bar_lower & BAR_PORT_MASK, 226 | .mmio = false, 227 | .length = 0, 228 | .is_64bits = false, 229 | }; 230 | } 231 | 232 | const address = bar_lower & BAR_MMIO_ADDR_MASK; 233 | 234 | pci_addr.write(u32, BAR_MMIO_ADDR_MASK); 235 | const bar_size_low = pci_addr.read(u32); 236 | pci_addr.write(u32, address); 237 | 238 | if (bar_size_low == 0) { 239 | return null; 240 | } 241 | 242 | pcibar.mmio = true; 243 | pcibar.base = address; 244 | pcibar.length = ~(bar_size_low & 0xFFFF_FFF0) + 1; 245 | pcibar.is_64bits = (bar_lower & BAR_TYPE_MASK) == BAR_TYPE_64BIT; 246 | 247 | if (pcibar.is_64bits) { 248 | const pci_addr_upper = self.addr(offset + 4); 249 | 250 | const bar_upper = @as(u64, pci_addr_upper.read(u32)); 251 | pcibar.base += (bar_upper << 32); 252 | } 253 | 254 | return pcibar; 255 | } 256 | 257 | pub fn print(self: @This()) void { 258 | serial.println("At slot {}, Vendor {X}:{X} Class: {any}:{x} HeaderType: {any}", .{ self.slot, self.vendor_id(), self.device_id(), Class.from(self.class()), self.subclass(), self.header_type() }); 259 | } 260 | }; 261 | 262 | pub fn scan(mcfg: *align(1) acpi.Mcfg.Configuration) void { 263 | for (0..32) |slot_usize| { 264 | const slot: u5 = @intCast(slot_usize); 265 | const device = Pci.new(0, slot, 0, mcfg); 266 | 267 | if (device.vendor_id() == 0xFFFF) { 268 | continue; 269 | } 270 | 271 | device.print(); 272 | var function: u3 = 1; 273 | while (function < 7) : (function += 1) { 274 | const vf = Pci.new(0, slot, function, mcfg); 275 | 276 | if (vf.vendor_id() == 0xFFFF) { 277 | continue; 278 | } 279 | 280 | vf.print(); 281 | } 282 | 283 | if (device.header_type().is_standard_header()) { 284 | for (0..6) |n| { 285 | serial.println("BAR {} = {any}", .{ n, device.bar(@intCast(n)) }); 286 | } 287 | 288 | const class = Pci.Class.from(device.class()); 289 | if (class == .Multimedia and device.subclass() == 3) { 290 | device.set_master_flag(); 291 | device.set_mmio_flag(); 292 | @import("audio.zig").init(&device, function, slot) catch |err| { 293 | serial.println("{any}", .{err}); 294 | }; 295 | } 296 | } 297 | } 298 | } 299 | -------------------------------------------------------------------------------- /src/drivers/pic.zig: -------------------------------------------------------------------------------- 1 | const outb = @import("../asm.zig").outb; 2 | 3 | const PIC = struct { 4 | cmd: u16, 5 | data: u16, 6 | 7 | const Command = struct { 8 | const DISABLE = 0xFF; 9 | }; 10 | 11 | pub fn disable(self: *const @This()) void { 12 | outb(self.data, @This().Command.DISABLE); 13 | } 14 | }; 15 | 16 | pub const PIC1: PIC = .{ .cmd = 0x20, .data = 0x21 }; 17 | 18 | pub const PIC2: PIC = .{ .cmd = 0xA0, .data = 0xA1 }; 19 | 20 | pub fn disable_pic() void { 21 | PIC1.disable(); 22 | PIC2.disable(); 23 | } 24 | -------------------------------------------------------------------------------- /src/drivers/ps2.zig: -------------------------------------------------------------------------------- 1 | const acpi = @import("../acpi/acpi.zig"); 2 | const inb = @import("../asm.zig").inb; 3 | const outb = @import("../asm.zig").outb; 4 | 5 | pub const PS2 = struct { 6 | const Regs = struct { 7 | const DATA = 0x60; 8 | const STATUS_COMMAND = 0x64; 9 | }; 10 | 11 | const Command = struct { 12 | const DISABLE_SECOND_PORT = 0xA7; 13 | const ENABLE_SECOND_PORT = 0xA8; 14 | const DISABLE_FIRST_PORT = 0xAD; 15 | const ENABLE_FIRST_PORT = 0xAE; 16 | const READ_CONFIG = 0x20; 17 | const WRITE_CONFIG = 0x60; 18 | }; 19 | 20 | const Config = struct { 21 | const ENABLE_FIRST_PORT_INTERRUPT = (1 << 0); 22 | const FIRST_PORT_TRANSLATION = (1 << 6); 23 | }; 24 | 25 | pub fn write( 26 | reg: u16, 27 | value: u8, 28 | ) void { 29 | while ((inb(Regs.STATUS_COMMAND) & 2) != 0) {} 30 | outb(reg, value); 31 | } 32 | 33 | pub fn read( 34 | reg: u16, 35 | ) u8 { 36 | while ((inb(Regs.STATUS_COMMAND) & 1) == 0) {} 37 | return inb(reg); 38 | } 39 | 40 | pub fn read_config() u8 { 41 | write(Regs.STATUS_COMMAND, Command.READ_CONFIG); 42 | return read(Regs.DATA); 43 | } 44 | 45 | pub fn write_config(config: u8) void { 46 | write(Regs.STATUS_COMMAND, Command.WRITE_CONFIG); 47 | return write(Regs.DATA, config); 48 | } 49 | }; 50 | 51 | pub fn init() !void { 52 | PS2.write(PS2.Regs.STATUS_COMMAND, PS2.Command.DISABLE_FIRST_PORT); 53 | PS2.write(PS2.Regs.STATUS_COMMAND, PS2.Command.DISABLE_SECOND_PORT); 54 | 55 | var ps2_config = PS2.read_config(); 56 | 57 | ps2_config |= PS2.Config.ENABLE_FIRST_PORT_INTERRUPT | PS2.Config.FIRST_PORT_TRANSLATION; 58 | 59 | PS2.write_config(ps2_config); 60 | 61 | PS2.write(PS2.Regs.STATUS_COMMAND, PS2.Command.ENABLE_FIRST_PORT); 62 | 63 | var io_apic = (try acpi.madt.?.get_ioapic()).ioapic; 64 | 65 | // Redirect keyboard 66 | io_apic.redirect(0, 33, 1); 67 | } 68 | -------------------------------------------------------------------------------- /src/drivers/serial.zig: -------------------------------------------------------------------------------- 1 | const outb = @import("../asm.zig").outb; 2 | const inb = @import("../asm.zig").inb; 3 | const std = @import("std"); 4 | const sync = @import("../sync.zig"); 5 | 6 | var serial_writer = sync.TicketLock(Serial.SerialWriter).init(Serial.writer()); 7 | 8 | pub fn print_err(comptime format: []const u8, args: anytype) void { 9 | print("[ERR] " ++ format ++ "\n", args); 10 | } 11 | 12 | pub fn print_ok(comptime format: []const u8, args: anytype) void { 13 | print("[OK] " ++ format ++ "\n", args); 14 | } 15 | 16 | pub fn print(comptime format: []const u8, args: anytype) void { 17 | if (@import("builtin").is_test) { 18 | @import("std").debug.print(format, args); 19 | return; 20 | } 21 | 22 | const writer = serial_writer.lock(); 23 | defer serial_writer.unlock(); 24 | writer.print(format, args) catch {}; 25 | } 26 | 27 | pub fn println_nolock(comptime format: []const u8, args: anytype) void { 28 | serial_writer.value.print(format ++ "\n", args) catch {}; 29 | } 30 | 31 | pub fn println(comptime format: []const u8, args: anytype) void { 32 | print(format ++ "\n", args); 33 | } 34 | 35 | pub const WriteOption = struct { linenumber: bool = false }; 36 | 37 | pub const Com = struct { 38 | const COM1 = 0x3F8; 39 | const COM2 = 0x2F8; 40 | const COM3 = 0x3E8; 41 | const COM4 = 0x2E8; 42 | const COM5 = 0x5F8; 43 | const COM6 = 0x4F8; 44 | const COM7 = 0x5E8; 45 | const COM8 = 0x4E8; 46 | }; 47 | 48 | pub const Serial = struct { 49 | pub fn init() !Serial { 50 | const com: u16 = Com.COM1; 51 | 52 | outb(com + 1, 0x00); 53 | outb(com + 3, 0x80); 54 | outb(com, 0x03); 55 | outb(com + 1, 0); 56 | outb(com + 3, 0x03); 57 | outb(com + 2, 0xC7); 58 | outb(com + 4, 0x0B); 59 | outb(com + 4, 0x1E); 60 | outb(com, 0xAE); 61 | 62 | if (inb(com) != 0xAE) { 63 | return error.SerialFault; 64 | } 65 | 66 | outb(com + 4, 0x0F); 67 | 68 | return Serial{}; 69 | } 70 | 71 | pub fn is_transmit_empty() u8 { 72 | return inb(Com.COM1 + 5) & 0x20; 73 | } 74 | 75 | pub fn is_serial_receiving() u8 { 76 | return inb(Com.COM1 + 5) & 1; 77 | } 78 | 79 | pub fn read() !u8 { 80 | while (Serial.is_serial_receiving() == 0) {} 81 | return inb(Com.COM1); 82 | } 83 | 84 | pub fn write(value: u8) void { 85 | while (Serial.is_transmit_empty() == 0) {} 86 | outb(Com.COM1, value); 87 | } 88 | 89 | pub fn write_array(values: []const u8) usize { 90 | var written: usize = 0; 91 | for (values) |value| { 92 | written += 1; 93 | Serial.write(value); 94 | } 95 | 96 | return written; 97 | } 98 | 99 | pub fn writeWithContext(self: Serial, values: []const u8) WriteError!usize { 100 | _ = self; 101 | return Serial.write_array(values); 102 | } 103 | 104 | const WriteError = error{CannotWrite}; 105 | 106 | const SerialWriter = std.io.Writer(Serial, WriteError, writeWithContext); 107 | pub fn writer() SerialWriter { 108 | return .{ .context = Serial{} }; 109 | } 110 | }; 111 | -------------------------------------------------------------------------------- /src/ds.zig: -------------------------------------------------------------------------------- 1 | const serial = @import("./drivers/serial.zig"); 2 | const Range = @import("./iter.zig").Range; 3 | 4 | pub const State = enum(u8) { 5 | Unused = 0, 6 | Used = 1, 7 | 8 | pub fn flip(self: *@This()) @This() { 9 | if (self.* == State.Unused) { 10 | return State.Used; 11 | } 12 | 13 | return State.Unused; 14 | } 15 | }; 16 | 17 | pub const BitMapU8 = struct { 18 | entries: [*]u8, 19 | size: usize, 20 | next_free_block: usize = 0, 21 | 22 | const bit_size: usize = @bitSizeOf(u8); 23 | const Self = @This(); 24 | 25 | pub fn new(base: *void, s: usize) @This() { 26 | serial.println("Create a bitmap of {}kb", .{s / 1024}); 27 | serial.println("Bitsize of {s} is {}", .{ @typeName(u8), @bitSizeOf(u8) }); 28 | 29 | return .{ .entries = @ptrCast(base), .size = s }; 30 | } 31 | 32 | pub fn init(s: *@This()) void { 33 | serial.println("Initializing bitmap", .{}); 34 | for (0..s.size) |i| { 35 | s.set(i); 36 | } 37 | } 38 | 39 | pub fn set(self: *Self, nth: usize) void { 40 | if (nth > self.size) { 41 | return; 42 | } 43 | 44 | self.entries[nth / bit_size] |= @as(u8, 1) << @truncate(nth % bit_size); 45 | } 46 | 47 | pub fn unset(self: *Self, nth: usize) void { 48 | if (nth > self.size) { 49 | return; 50 | } 51 | 52 | self.entries[nth / bit_size] &= ~(@as(u8, 1) << @truncate(nth % bit_size)); 53 | } 54 | 55 | pub fn get(self: *@This(), nth: usize) State { 56 | return @enumFromInt((self.entries[nth / bit_size] >> @truncate(nth % bit_size)) & 1); 57 | } 58 | 59 | pub fn unset_range(self: *Self, range: Range) !void { 60 | var iterator = range.iter(); 61 | while (iterator.next()) |i| { 62 | serial.println("will set {}", .{i}); 63 | if (i > self.size * 8) { 64 | return error.NotEnoughtMemory; 65 | } 66 | 67 | self.unset(i); 68 | } 69 | } 70 | 71 | pub fn set_range(self: *Self, range: Range) !void { 72 | var iterator = range.iter(); 73 | while (iterator.next()) |i| { 74 | if (i > self.size) { 75 | return error.NotEnoughtMemory; 76 | } 77 | 78 | self.set(i); 79 | } 80 | } 81 | 82 | pub fn alloc(self: *Self, range: Range) !void { 83 | while (range.iter()) |i| { 84 | if (i > self.entries.len) { 85 | return error.NotEnoughtMemory; 86 | } 87 | 88 | self.set(i); 89 | } 90 | } 91 | 92 | pub fn debug(self: *Self) void { 93 | serial.println("base: {x} size: {}", .{ &self.entries[0], self.size }); 94 | 95 | var start: u64 = 0; 96 | var state = self.get(start); 97 | for (0..self.size) |i| { 98 | if (state != self.get(i)) { 99 | serial.println("0x{x} - 0x{x} : {s}", .{ start, i, @tagName(state) }); 100 | state = state.flip(); 101 | start = i; 102 | } 103 | } 104 | 105 | serial.println("0x{x} - 0x{x} : {s}", .{ start, self.size, @tagName(state) }); 106 | } 107 | }; 108 | 109 | test "init_bitmap" { 110 | var gpa = @import("std").heap.GeneralPurposeAllocator(.{}){}; 111 | const alloc = gpa.allocator(); 112 | const mem = try alloc.alloc(u8, 20); 113 | var bm = BitMapU8.new(@ptrCast(mem), 20 * 8); 114 | 115 | bm.init(); 116 | bm.set(0); 117 | bm.set(bm.size); 118 | 119 | const expect = @import("std").testing.expectEqual; 120 | 121 | try expect(.Used, bm.get(0)); 122 | try expect(.Used, bm.get(bm.size)); 123 | } 124 | 125 | test "set_unset_bitmap" { 126 | var gpa = @import("std").heap.GeneralPurposeAllocator(.{}){}; 127 | const alloc = gpa.allocator(); 128 | const mem = try alloc.alloc(u8, 20); 129 | var bm = BitMapU8.new(@ptrCast(mem), 20 * 8); 130 | 131 | bm.init(); 132 | bm.set(0); 133 | 134 | const expect = @import("std").testing.expectEqual; 135 | 136 | try expect(.Used, bm.get(0)); 137 | 138 | bm.unset(0); 139 | 140 | try expect(.Unused, bm.get(0)); 141 | } 142 | -------------------------------------------------------------------------------- /src/font/lucida-10x16.psf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rheydskey/zros/7055f9c757e234b12a6f7255b2a277561a42108c/src/font/lucida-10x16.psf -------------------------------------------------------------------------------- /src/idiot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rheydskey/zros/7055f9c757e234b12a6f7255b2a277561a42108c/src/idiot -------------------------------------------------------------------------------- /src/idiot2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rheydskey/zros/7055f9c757e234b12a6f7255b2a277561a42108c/src/idiot2 -------------------------------------------------------------------------------- /src/iter.zig: -------------------------------------------------------------------------------- 1 | pub const Iterator = struct { 2 | range: Range, 3 | 4 | const Self = @This(); 5 | 6 | pub fn next(self: *Self) ?usize { 7 | if (self.range.inclusive and self.range.start > self.range.end) { 8 | return null; 9 | } 10 | 11 | if (!self.range.inclusive and self.range.start >= self.range.end) { 12 | return null; 13 | } 14 | const old = self.range.start; 15 | 16 | self.range.start += 1; 17 | 18 | return old; 19 | } 20 | }; 21 | 22 | pub const Range = struct { 23 | start: u64, 24 | end: u64, 25 | inclusive: bool, 26 | const Self = @This(); 27 | 28 | /// Range like [start; start+end] 29 | pub fn new_inclusive(base: usize, size: usize) Self { 30 | return .{ 31 | .start = base, 32 | .end = size, 33 | .inclusive = true, 34 | }; 35 | } 36 | 37 | /// Range like [start; start+end[ 38 | pub fn new_exclusive(base: usize, size: usize) Self { 39 | return .{ 40 | .start = base, 41 | .end = size, 42 | .inclusive = false, 43 | }; 44 | } 45 | 46 | pub fn iter(self: Self) Iterator { 47 | return Iterator{ 48 | .range = self, 49 | }; 50 | } 51 | 52 | pub fn len(self: *@This()) u64 { 53 | return self.end - self.start; 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /src/limine_rq.zig: -------------------------------------------------------------------------------- 1 | const limine = @import("limine"); 2 | 3 | pub export var kaddr_req: limine.KernelAddressRequest = .{}; 4 | pub export var base_revision: limine.BaseRevision = .{ .revision = 1 }; 5 | pub export var framebuffer: limine.FramebufferRequest = .{}; 6 | pub export var memory_map: limine.MemoryMapRequest = .{}; 7 | pub export var hhdm: limine.HhdmRequest = .{}; 8 | pub export var rspd: limine.RsdpRequest = .{}; 9 | pub export var smp: limine.SmpRequest = .{}; 10 | -------------------------------------------------------------------------------- /src/main.zig: -------------------------------------------------------------------------------- 1 | pub const drivers = @import("./drivers/drivers.zig"); 2 | const pci = @import("./drivers/pci.zig"); 3 | const serial = drivers.serial; 4 | pub const cpu = @import("arch/x86/cpu.zig"); 5 | const gdt = @import("./arch/x86/gdt.zig"); 6 | const idt = @import("./arch/x86/idt.zig"); 7 | pub const syscall = @import("arch/x86/syscall.zig"); 8 | const std = @import("std"); 9 | const builtin = std.builtin; 10 | const build_options = @import("build_options"); 11 | const pmm = @import("./mem/pmm.zig"); 12 | const heap = @import("./mem/heap.zig"); 13 | const vmm = @import("./mem/vmm.zig"); 14 | const fb = drivers.fb; 15 | pub const scheduler = @import("./sched/scheduler.zig"); 16 | const limine_rq = @import("limine_rq.zig"); 17 | const psf = @import("./psf.zig"); 18 | const smp = @import("./smp.zig"); 19 | pub const assembly = @import("asm.zig"); 20 | pub const utils = @import("utils.zig"); 21 | 22 | const idiot = @embedFile("./idiot"); 23 | const idiot2 = @embedFile("./idiot2"); 24 | 25 | var kheap: ?heap.Heap = null; 26 | 27 | pub fn panic(msg: []const u8, _: ?*builtin.StackTrace, ret_addr: ?usize) noreturn { 28 | serial.println_nolock( 29 | \\====== This is a panic message ====== 30 | \\{s} 31 | , .{msg}); 32 | 33 | if (fb.screen != null) { 34 | var screen = &fb.screen.?; 35 | 36 | screen.resetAll(); 37 | screen.println("===== PANIC ====="); 38 | screen.println(msg); 39 | } 40 | 41 | const addr = ret_addr orelse @returnAddress(); 42 | 43 | var si = std.debug.StackIterator.init(addr, null); 44 | defer si.deinit(); 45 | 46 | if (fb.screen != null) { 47 | var screen = &fb.screen.?; 48 | 49 | screen.println("Stacktrace:"); 50 | } 51 | 52 | serial.println_nolock("Stacktrace:", .{}); 53 | 54 | while (si.next()) |trace| { 55 | if (fb.screen) |_| { 56 | var screen = &fb.screen.?; 57 | _ = screen.writer().print("0x{X}\n", .{trace}) catch {}; 58 | } 59 | 60 | serial.println_nolock("0x{X}", .{trace}); 61 | } 62 | 63 | serial.println_nolock("End of stacktrace", .{}); 64 | 65 | while (true) { 66 | assembly.hlt(); 67 | } 68 | } 69 | 70 | fn load_tasks(kernel_stack: []u8) !void { 71 | const code: [*]u8 = @ptrCast(try pmm.alloc(4096)); 72 | 73 | @import("std").mem.copyForwards(u8, code[0..4096], idiot[0..idiot.len]); 74 | 75 | const stack = try pmm.alloc(4096); 76 | 77 | try vmm.remap_page(vmm.kernel_pml4.?, 0x50000000, @intFromPtr(stack), vmm.PmlEntryFlag.USER | vmm.PmlEntryFlag.READ_WRITE | vmm.PmlEntryFlag.PRESENT); 78 | 79 | try vmm.remap_page(vmm.kernel_pml4.?, 0x50005000, @intFromPtr(code), vmm.PmlEntryFlag.USER | vmm.PmlEntryFlag.READ_WRITE | vmm.PmlEntryFlag.PRESENT); 80 | 81 | var task = try scheduler.Task.create(&kheap.?); 82 | 83 | task.init(0x50005000, 0x50000000 + 4096, @intFromPtr(kernel_stack.ptr) + 16000, true); 84 | 85 | try scheduler.add_process(task); 86 | 87 | const code2: [*]u8 = @ptrCast(try pmm.alloc(4096)); 88 | 89 | @import("std").mem.copyForwards(u8, code2[0..4096], idiot2[0..idiot2.len]); 90 | 91 | const stack2 = try pmm.alloc(4096); 92 | 93 | try vmm.remap_page(vmm.kernel_pml4.?, 0x40000000, @intFromPtr(stack2), vmm.PmlEntryFlag.USER | vmm.PmlEntryFlag.READ_WRITE | vmm.PmlEntryFlag.PRESENT); 94 | 95 | try vmm.remap_page(vmm.kernel_pml4.?, 0x40005000, @intFromPtr(code2), vmm.PmlEntryFlag.USER | vmm.PmlEntryFlag.READ_WRITE | vmm.PmlEntryFlag.PRESENT); 96 | 97 | var task2 = try scheduler.Task.create(&kheap.?); 98 | 99 | task2.init(0x40005000, 0x40000000 + 4096, @intFromPtr(kernel_stack.ptr) + 16000, true); 100 | 101 | try scheduler.add_process(task2); 102 | 103 | scheduler.is_running = true; 104 | } 105 | 106 | pub fn main() !noreturn { 107 | _ = serial.Serial.init() catch { 108 | asm volatile ("hlt"); 109 | return error.CannotWrite; 110 | }; 111 | 112 | serial.println("id: {}", .{cpu.get_id()}); 113 | 114 | gdt.init(); 115 | idt.init(); 116 | 117 | if (limine_rq.memory_map.response) |response| { 118 | try pmm.pmm_init(response, limine_rq.hhdm.response.?); 119 | } 120 | 121 | serial.println("HHDM: 0x{x} KADDR: 0x{x}", .{ limine_rq.hhdm.response.?.offset, limine_rq.kaddr_req.response.?.virtual_base }); 122 | try vmm.init(limine_rq.memory_map.response.?); 123 | 124 | const heap_base = try pmm.alloc(0x10000); 125 | kheap = heap.init(@ptrCast(heap_base), 0x10000); 126 | 127 | if (limine_rq.framebuffer.response) |response| { 128 | if (response.framebuffer_count > 0) { 129 | const framebuf = response.framebuffers_ptr[0]; 130 | serial.println("{any}", .{framebuf}); 131 | 132 | const a: *align(1) const psf.Psf2 = @ptrCast(psf.lucida); 133 | fb.fb_ptr = fb.Framebuffer.init(@intFromPtr(framebuf.address), framebuf.height, framebuf.width, framebuf.pitch, framebuf.bpp, a); 134 | } 135 | } 136 | 137 | const kernel_stack = try kheap.?.alloc(16000); 138 | 139 | gdt.tss_init(@intFromPtr(kernel_stack.ptr) + 16000); 140 | 141 | fb.screen.?.println("ZROS - 0.0.1+" ++ build_options.git_version); 142 | fb.screen.?.println("Hewwo worwd"); 143 | fb.screen.?.print(0xA0); 144 | 145 | try drivers.init(); 146 | 147 | const str = try kheap.?.alloc(256); 148 | 149 | const printed = try @import("std").fmt.bufPrint(str, "HHDM: 0x{x} KADDR: 0x{x}", .{ limine_rq.hhdm.response.?.offset, limine_rq.kaddr_req.response.?.virtual_base }); 150 | fb.screen.?.println(printed); 151 | 152 | // try smp.init(); 153 | syscall.init(); 154 | 155 | // const acpi = @import("./acpi/acpi.zig"); 156 | 157 | // pci.scan(acpi.mcfg.?.get_configuration()); 158 | 159 | try load_tasks(kernel_stack); 160 | 161 | while (true) { 162 | asm volatile ("hlt"); 163 | } 164 | } 165 | 166 | export fn _start() noreturn { 167 | main() catch |i| { 168 | serial.println("{}", .{i}); 169 | }; 170 | 171 | while (true) { 172 | asm volatile ("hlt"); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/mem/heap.zig: -------------------------------------------------------------------------------- 1 | const serial = @import("../drivers/serial.zig"); 2 | 3 | pub const Heap = @This(); 4 | 5 | hbase: ?[*]u8 = null, 6 | hsize: ?usize = null, 7 | 8 | const AllocHeader = packed struct { 9 | size: usize, 10 | // Put void here cause zig compiler crash when try align(1) ptr of AllocHeader 11 | prev_block: ?*void, 12 | next_block: ?*void, 13 | is_free: bool, 14 | 15 | fn setHeader(base: *void, size: usize) *align(1) AllocHeader { 16 | const header: *align(1) AllocHeader = @ptrCast(base); 17 | header.* = AllocHeader{ 18 | .size = size, 19 | .prev_block = null, 20 | .next_block = null, 21 | .is_free = true, 22 | }; 23 | 24 | return header; 25 | } 26 | 27 | fn getHeader(base: *void) *align(1) AllocHeader { 28 | return @ptrCast(base); 29 | } 30 | 31 | fn getHeaderFromData(base: *void) *align(1) AllocHeader { 32 | return @ptrFromInt(@intFromPtr(base) - @sizeOf(AllocHeader)); 33 | } 34 | 35 | fn getData(self: *align(1) const AllocHeader) [*]u8 { 36 | const dataptr: [*]u8 = @ptrFromInt(@intFromPtr(self) + @sizeOf(AllocHeader)); 37 | 38 | return dataptr; 39 | } 40 | 41 | fn getNext(self: *align(1) const AllocHeader) ?*align(1) AllocHeader { 42 | if (self.next_block == null) { 43 | return null; 44 | } 45 | 46 | return @ptrCast(self.next_block.?); 47 | } 48 | 49 | fn getPrev(self: *align(1) const AllocHeader) ?*align(1) AllocHeader { 50 | if (self.prev_block == null) { 51 | return null; 52 | } 53 | 54 | return @ptrCast(self.prev_block.?); 55 | } 56 | 57 | fn mergePrevious(self: *align(1) const AllocHeader) !void { 58 | if (self.getPrev()) |prev| { 59 | prev.size += self.size + @sizeOf(AllocHeader); 60 | prev.next_block = self.next_block; 61 | 62 | return; 63 | } 64 | 65 | return error.NoPrevious; 66 | } 67 | 68 | fn mergeNext(self: *align(1) AllocHeader) !void { 69 | if (self.getNext()) |next| { 70 | self.size += next.size + @sizeOf(AllocHeader); 71 | self.next_block = next.next_block; 72 | return; 73 | } 74 | 75 | return error.NoNext; 76 | } 77 | 78 | fn mergeNextWithPrevious(self: *align(1) const AllocHeader) !void { 79 | if (self.getNext()) |next| { 80 | if (self.getPrev()) |prev| { 81 | prev.size += next.size + @sizeOf(AllocHeader); 82 | prev.next_block = next.next_block; 83 | return; 84 | } 85 | 86 | return error.NoPrevious; 87 | } 88 | 89 | return error.NoNext; 90 | } 91 | 92 | pub fn debug(self: *align(1) AllocHeader) void { 93 | const Debug = struct { 94 | pub fn drawArrow() void { 95 | serial.println("{s: >12}|", .{""}); 96 | serial.println("{s: >11}/", .{""}); 97 | serial.println("{s: >10}v", .{""}); 98 | } 99 | 100 | pub fn drawBox(is_free: bool, size: usize) void { 101 | serial.println("{0s: >6}+{0s:->14}+", .{""}); 102 | if (is_free) { 103 | serial.println("{s: >6}|{s: ^14}|", .{ "", "free" }); 104 | } else { 105 | serial.println("{s: >6}|{s: ^14}|", .{ "", "used" }); 106 | } 107 | 108 | serial.println("{s: >6}|{: ^14}|", .{ "", size }); 109 | 110 | serial.println("{0s: >6}+{0s:->14}+", .{""}); 111 | } 112 | }; 113 | serial.println("0x{x} 0x{x}", .{ @intFromPtr(self), @intFromPtr(self) + self.size }); 114 | Debug.drawBox(self.is_free, self.size); 115 | 116 | if (self.next_block == null) { 117 | Debug.drawArrow(); 118 | serial.println("{s: ^20}", .{"null"}); 119 | } else { 120 | Debug.drawArrow(); 121 | AllocHeader.getHeader(self.next_block.?).debug(); 122 | } 123 | } 124 | }; 125 | 126 | pub fn init(base: [*]u8, size: usize) Heap { 127 | _ = AllocHeader.setHeader(@ptrCast(base), size); 128 | 129 | return .{ 130 | .hbase = base, 131 | .hsize = size, 132 | }; 133 | } 134 | 135 | pub fn alloc(self: *align(1) Heap, size: usize) ![]u8 { 136 | if (self.hbase == null) { 137 | return error.HeapNotInitialize; 138 | } 139 | 140 | var header = AllocHeader.getHeader(@ptrCast(self.hbase)); 141 | 142 | while (header.next_block != null) { 143 | header = @ptrCast(header.next_block.?); 144 | } 145 | 146 | if (size + @sizeOf(AllocHeader) > header.size and header.next_block == null) { 147 | return error.NotEnougthMem; 148 | } 149 | 150 | const data: [*]u8 = header.getData(); 151 | 152 | const next_header = AllocHeader.setHeader(@ptrCast(&data[size]), header.size - size - @sizeOf(AllocHeader)); 153 | next_header.prev_block = @ptrCast(header); 154 | 155 | header.next_block = @ptrCast(next_header); 156 | header.size = size; 157 | header.is_free = false; 158 | 159 | return data[0..size]; 160 | } 161 | 162 | pub fn create(self: *align(1) Heap, comptime T: type) !*T { 163 | return @ptrCast(@alignCast(try self.alloc(@sizeOf(T)))); 164 | } 165 | 166 | pub fn free(base: anytype) !void { 167 | // From allocator code: https://ziglang.org/documentation/master/std/#std.mem.Allocator.destroy 168 | const info = @typeInfo(@TypeOf(base)).pointer; 169 | const T = info.child; 170 | if (@sizeOf(T) == 0) return; 171 | const non_const_ptr = @as([*]u8, @ptrCast(@constCast(base))); 172 | 173 | const header = AllocHeader.getHeaderFromData(@ptrCast(non_const_ptr)); 174 | 175 | if (header.getPrev()) |prev| { 176 | if (prev.is_free) { 177 | try header.mergePrevious(); 178 | 179 | if (header.getNext()) |next| { 180 | if (next.is_free) { 181 | try header.mergeNextWithPrevious(); 182 | } 183 | } 184 | } 185 | 186 | return; 187 | } 188 | 189 | if (header.getNext()) |next| { 190 | if (next.is_free) { 191 | try header.mergeNext(); 192 | } 193 | } 194 | 195 | header.is_free = true; 196 | } 197 | 198 | fn test_heap() Heap { 199 | var gpa = @import("std").heap.GeneralPurposeAllocator(.{}){}; 200 | const allocator = gpa.allocator(); 201 | const mem = allocator.alloc(u8, 4096) catch { 202 | @panic("Cannot run test"); 203 | }; 204 | return init(@ptrCast(mem), 4096); 205 | } 206 | 207 | test "alloc" { 208 | var heap = test_heap(); 209 | const allocated = try heap.alloc(200); 210 | try @import("std").testing.expectEqual(200, allocated.len); 211 | } 212 | 213 | test "alloc_and_free" { 214 | var heap = test_heap(); 215 | const allocated = try heap.alloc(200); 216 | 217 | try @import("std").testing.expectEqual(200, allocated.len); 218 | 219 | try free(allocated); 220 | 221 | try @import("std").testing.expectEqual(4096, AllocHeader.getHeader(@ptrCast(heap.hbase.?)).size); 222 | } 223 | 224 | test "alloc_twice" { 225 | var heap = test_heap(); 226 | const allocated = try heap.alloc(200); 227 | const allocated_twice = try heap.alloc(200); 228 | 229 | try @import("std").testing.expectEqual(200, allocated_twice.len); 230 | try @import("std").testing.expectEqual(@intFromPtr(allocated.ptr) + @sizeOf(AllocHeader) + allocated.len, @intFromPtr(allocated_twice.ptr)); 231 | 232 | try free(allocated); 233 | try free(allocated_twice); 234 | 235 | try @import("std").testing.expectEqual(4096, AllocHeader.getHeader(@ptrCast(heap.hbase.?)).size); 236 | } 237 | 238 | test "alloc_too_much" { 239 | var heap = test_heap(); 240 | 241 | try @import("std").testing.expectError(error.NotEnougthMem, heap.alloc(4096)); 242 | } 243 | 244 | test "alloc_max" { 245 | var heap = test_heap(); 246 | 247 | const allocated = try heap.alloc(4096 - @sizeOf(AllocHeader)); 248 | try @import("std").testing.expectEqual(4096 - @sizeOf(AllocHeader), allocated.len); 249 | } 250 | -------------------------------------------------------------------------------- /src/mem/mem.zig: -------------------------------------------------------------------------------- 1 | const limine_rq = @import("../limine_rq.zig"); 2 | pub const utils = @import("../utils.zig"); 3 | 4 | pub fn mmap_virt_to_phys(virt: usize) usize { 5 | return virt - limine_rq.hhdm.response.?.offset; 6 | } 7 | 8 | pub fn mmap_phys_to_virt_ptr(phys: *anyopaque) *anyopaque { 9 | return @ptrFromInt(mmap_phys_to_virt(@intFromPtr(phys))); 10 | } 11 | 12 | pub fn mmap_phys_to_virt(phys: usize) usize { 13 | return phys + limine_rq.hhdm.response.?.offset; 14 | } 15 | 16 | pub fn mmap_phys_to_kernel(phys: anytype) usize { 17 | return limine_rq.kaddr_req.response.?.virtual_base + @intFromPtr(phys); 18 | } 19 | 20 | // Inspiration of rust's x86_64 crate 21 | 22 | pub const VirtAddr = struct { 23 | addr: u64, 24 | 25 | pub fn new(addr: u64) !VirtAddr { 26 | const virtaddr: VirtAddr = new_truncate(addr); 27 | 28 | if (virtaddr.addr != addr) return error.BadVirtAddr; 29 | 30 | return virtaddr; 31 | } 32 | 33 | pub fn new_truncate(addr: u64) VirtAddr { 34 | return .{ .addr = @bitCast(@as(i64, @bitCast(addr << 16)) >> 16) }; 35 | } 36 | 37 | pub fn as_u48(self: *const VirtAddr) u48 { 38 | return @truncate(self.addr); 39 | } 40 | 41 | inline fn get_index_of_pml(addr: u64, comptime level: u8) u64 { 42 | const shift: u64 = 12 + level * 9; 43 | return (addr & (0x1ff << shift)) >> shift; 44 | } 45 | 46 | pub fn get_pml4_index(self: *const VirtAddr) u64 { 47 | return get_index_of_pml(self.addr, 3); 48 | } 49 | 50 | pub fn get_pml3_index(self: *const VirtAddr) u64 { 51 | return get_index_of_pml(self.addr, 2); 52 | } 53 | 54 | pub fn get_pml2_index(self: *const VirtAddr) u64 { 55 | return get_index_of_pml(self.addr, 1); 56 | } 57 | 58 | pub fn get_pml1_index(self: *const VirtAddr) u64 { 59 | return get_index_of_pml(self.addr, 0); 60 | } 61 | 62 | pub fn align_up(self: *const VirtAddr, alignment: u64) !VirtAddr { 63 | return VirtAddr.new(utils.align_up(self.addr, alignment)); 64 | } 65 | 66 | pub fn align_down(self: *const VirtAddr, alignment: u64) !VirtAddr { 67 | return VirtAddr.new(utils.align_down(self.addr, alignment)); 68 | } 69 | }; 70 | 71 | test "pml_index" { 72 | const assert = @import("std").debug.assert; 73 | const virt = VirtAddr.new_truncate(0xff7f80005000); 74 | 75 | assert(virt.get_pml4_index() == 510); 76 | assert(virt.get_pml3_index() == 510); 77 | assert(virt.get_pml2_index() == 0); 78 | assert(virt.get_pml1_index() == 5); 79 | } 80 | 81 | pub const PhysAddr = struct { 82 | addr: u64, 83 | 84 | pub fn new(addr: u64) !PhysAddr { 85 | const phys: PhysAddr = .{ .addr = addr % (1 << 52) }; 86 | 87 | if (phys.addr != addr) return error.BadPhysAddr; 88 | 89 | return phys; 90 | } 91 | 92 | pub fn as_u52(self: *const PhysAddr) u52 { 93 | return @truncate(self.addr); 94 | } 95 | 96 | pub fn align_up(self: *const PhysAddr, alignment: u64) !PhysAddr { 97 | return PhysAddr.new(utils.align_up(self.addr, alignment)); 98 | } 99 | 100 | pub fn align_down(self: *const PhysAddr, alignment: u64) !PhysAddr { 101 | return PhysAddr.new(utils.align_down(self.addr, alignment)); 102 | } 103 | 104 | pub fn to_virt(self: *const PhysAddr) VirtAddr { 105 | return self.try_to_virt() catch |err| { 106 | @import("std").debug.panic("Cannot convert to virt. Addr: {} Error {}", .{ self.addr, err }); 107 | }; 108 | } 109 | 110 | pub fn to_kernel(self: *const PhysAddr) VirtAddr { 111 | return self.try_to_kernel() catch |err| { 112 | @import("std").debug.panic("Cannot convert to kernel. Addr: {} Error {}", .{ self.addr, err }); 113 | }; 114 | } 115 | 116 | pub fn try_to_virt(self: *const PhysAddr) !VirtAddr { 117 | return VirtAddr.new(self.addr + limine_rq.hhdm.response.?.offset); 118 | } 119 | 120 | pub fn try_to_kernel(self: *const PhysAddr) !VirtAddr { 121 | return VirtAddr.new(self.addr + limine_rq.kaddr_req.response.?.virtual_base); 122 | } 123 | }; 124 | -------------------------------------------------------------------------------- /src/mem/pmm.zig: -------------------------------------------------------------------------------- 1 | const ds = @import("../ds.zig"); 2 | const utils = @import("../utils.zig"); 3 | const serial = @import("../drivers/serial.zig"); 4 | const limine = @import("limine"); 5 | 6 | pub const PAGE_SIZE = 0x1000; // 0x1000 = 4Kib 7 | 8 | var bitmap: ?ds.BitMapU8 = null; 9 | var base: ?u64 = null; 10 | var usable_size: usize = 0; 11 | 12 | fn find_block_for_bitmap(mmap: *limine.MemoryMapResponse, bitmap_size: u64) !*limine.MemoryMapEntry { 13 | for (mmap.entries()) |entry| { 14 | if (entry.kind != limine.MemoryMapEntryType.usable) { 15 | continue; 16 | } 17 | 18 | if (entry.length >= bitmap_size) { 19 | return entry; 20 | } 21 | } 22 | serial.print_err("Try to allocate {} but memory size is {}", .{ bitmap_size, usable_size }); 23 | return error.NotEnoughtMem; 24 | } 25 | 26 | pub fn pmm_init(mmap: *limine.MemoryMapResponse, hhdm: *limine.HhdmResponse) !void { 27 | base = hhdm.offset; 28 | var highest_addr: u64 = 0; 29 | 30 | for (mmap.entries()) |entry| { 31 | if (entry.kind == .usable) { 32 | const end_addr = entry.base + entry.length; 33 | 34 | if (end_addr > highest_addr) { 35 | highest_addr = end_addr; 36 | } 37 | 38 | usable_size += entry.length; 39 | } 40 | 41 | serial.println("MMAP - base: 0x{X}-0x{X} kind: {}", .{ entry.base, entry.base + entry.length, entry.kind }); 42 | } 43 | 44 | if (highest_addr == 0) { 45 | @panic("No usable block"); 46 | } 47 | 48 | serial.println("Usable memory size is {}", .{usable_size}); 49 | 50 | const bitmap_size = utils.align_up(highest_addr / PAGE_SIZE / 8 + 1, PAGE_SIZE); 51 | 52 | var bitmap_block = try find_block_for_bitmap(mmap, bitmap_size); 53 | 54 | bitmap = ds.BitMapU8.new(@ptrFromInt(bitmap_block.base + hhdm.offset), bitmap_size); 55 | bitmap.?.init(); 56 | 57 | bitmap_block.base += bitmap_size; 58 | bitmap_block.length -= bitmap_size; 59 | 60 | for (mmap.entries()) |entry| { 61 | if (entry.kind == .usable) { 62 | var i: u64 = entry.base; 63 | while (i < entry.base + entry.length) : (i += PAGE_SIZE) { 64 | bitmap.?.unset(i / PAGE_SIZE); 65 | } 66 | } 67 | } 68 | 69 | bitmap.?.debug(); 70 | 71 | serial.print_ok("PMM", .{}); 72 | } 73 | 74 | pub fn alloc(size: usize) !*void { 75 | // TODO: OPTIMIZATION 76 | const size_needed: u64 = size / PAGE_SIZE; 77 | var length_free_block: u64 = 0; 78 | 79 | for (0..bitmap.?.size) |i| { 80 | if (bitmap.?.get(i) == ds.State.Used) { 81 | length_free_block = 0; 82 | continue; 83 | } 84 | 85 | if (size_needed <= length_free_block) { 86 | bitmap.?.set_range(.{ .start = i - length_free_block, .end = i, .inclusive = true }) catch {}; 87 | return @ptrFromInt((i - length_free_block) * PAGE_SIZE); 88 | } 89 | 90 | length_free_block += 1; 91 | } 92 | 93 | return error.NotEnoughtMem; 94 | } 95 | 96 | pub fn free(ptr: *void, size: usize) !void { 97 | const from: usize = @intFromPtr(ptr) - base.?; 98 | for (from..from + size) |i| { 99 | bitmap.?.unset(i / 4096); 100 | } 101 | } 102 | 103 | pub fn debug() void { 104 | bitmap.?.debug(); 105 | } 106 | 107 | // ===== TEST ===== 108 | 109 | fn inittest() !u64 { 110 | var gpa = @import("std").heap.GeneralPurposeAllocator(.{}){}; 111 | const allocator = gpa.allocator(); 112 | const fakememorysize = PAGE_SIZE * 3; 113 | 114 | var entry = 115 | limine.MemoryMapEntry{ 116 | .base = 0, 117 | .length = fakememorysize, 118 | .kind = .usable, 119 | }; 120 | var entries = [_]*limine.MemoryMapEntry{&entry}; 121 | var mmap = limine.MemoryMapResponse{ 122 | .revision = 0, 123 | .entry_count = 1, 124 | .entries_ptr = &entries, 125 | }; 126 | var hhdm = limine.HhdmResponse{ 127 | .revision = 0, 128 | .offset = @intFromPtr(try allocator.create([fakememorysize]u8)), 129 | }; 130 | 131 | try pmm_init( 132 | &mmap, 133 | &hhdm, 134 | ); 135 | 136 | return hhdm.offset; 137 | } 138 | 139 | test "try_alloc" { 140 | _ = try inittest(); 141 | 142 | try @import("std").testing.expectEqual(.Used, bitmap.?.get(0)); 143 | try @import("std").testing.expectEqual(.Unused, bitmap.?.get(1)); 144 | try @import("std").testing.expectEqual(.Unused, bitmap.?.get(2)); 145 | } 146 | 147 | test "free_alloc" { 148 | const addr = try inittest(); 149 | 150 | try @import("std").testing.expectEqual(.Used, bitmap.?.get(0)); 151 | try @import("std").testing.expectEqual(.Unused, bitmap.?.get(1)); 152 | try @import("std").testing.expectEqual(.Unused, bitmap.?.get(2)); 153 | 154 | debug(); 155 | 156 | try free(@ptrFromInt(addr), 1); 157 | 158 | try @import("std").testing.expectEqual(.Unused, bitmap.?.get(0)); 159 | debug(); 160 | } 161 | -------------------------------------------------------------------------------- /src/mem/vmm.zig: -------------------------------------------------------------------------------- 1 | const serial = @import("../drivers/serial.zig"); 2 | const limine = @import("limine"); 3 | const limine_rq = @import("../limine_rq.zig"); 4 | const pmm = @import("pmm.zig"); 5 | const utils = @import("../utils.zig"); 6 | const mem = @import("mem.zig"); 7 | 8 | const Section = struct { 9 | extern const text_start_addr: [*]u8; 10 | extern const text_end_addr: u64; 11 | 12 | extern const rodata_start_addr: [*]u8; 13 | extern const rodata_end_addr: u64; 14 | 15 | extern const data_start_addr: [*]u8; 16 | extern const data_end_addr: u64; 17 | }; 18 | 19 | pub var kernel_pml4: ?*Pml = null; 20 | 21 | pub const PmlEntryFlag = packed struct(u9) { 22 | present: bool, 23 | read_write: bool, 24 | user: bool, 25 | page_write_through: bool, 26 | caching_disable: bool, 27 | accessed: bool, 28 | dirty: bool, 29 | huge_page: bool, 30 | global_page: bool, 31 | 32 | pub fn from(flags: u9) PmlEntryFlag { 33 | return @bitCast(flags); 34 | } 35 | 36 | pub fn from_u64(flags: u64) PmlEntryFlag { 37 | return from(@truncate(flags)); 38 | } 39 | 40 | pub fn to_int(self: *const PmlEntryFlag) u9 { 41 | return @bitCast(self.*); 42 | } 43 | 44 | pub const PRESENT = 1; 45 | pub const READ_WRITE = (1 << 1); 46 | pub const USER = (1 << 2); 47 | pub const PAGE_WRITE_THROUGH = (1 << 3); 48 | pub const CACHING_DISABLE = (1 << 4); 49 | pub const ACCESSED = (1 << 5); 50 | pub const DIRTY = (1 << 6); 51 | pub const HUGE_PAGE = (1 << 7); 52 | pub const GLOBAL_PAGE = (1 << 8); 53 | pub const NOX = (1 << 63); 54 | }; 55 | 56 | const PmlEntry = packed struct(u64) { 57 | flags: PmlEntryFlag = PmlEntryFlag.from(PmlEntryFlag.PRESENT | PmlEntryFlag.READ_WRITE), 58 | available: u3 = 0, 59 | physical: u52 = 0, 60 | 61 | pub fn new_with_addr(addr: u64) !@This() { 62 | if (!mem.utils.is_align(addr, pmm.PAGE_SIZE)) return error.AddrNotAligned; 63 | return .{ .physical = @truncate(addr >> 12) }; 64 | } 65 | 66 | pub fn set_flags(self: *align(1) PmlEntry, flags: PmlEntryFlag) void { 67 | self.flags = flags; 68 | } 69 | 70 | pub fn as_u64(self: *const PmlEntry) u64 { 71 | return @bitCast(self.*); 72 | } 73 | 74 | pub fn as_phys_addr(self: *align(1) const PmlEntry) !mem.PhysAddr { 75 | return mem.PhysAddr.new(self.physical << 12); 76 | } 77 | }; 78 | 79 | const Pml = struct { 80 | entries: [512]PmlEntry = undefined, 81 | 82 | comptime { 83 | @import("../utils.zig").checkSize(Pml, 4096); 84 | } 85 | }; 86 | 87 | pub fn init(memmap: *limine.MemoryMapResponse) !void { 88 | serial.println("VMM init", .{}); 89 | 90 | const alloc_page = try pmm.alloc(pmm.PAGE_SIZE); 91 | 92 | kernel_pml4 = @alignCast(@ptrCast(mem.mmap_phys_to_virt_ptr(alloc_page))); 93 | @memset(@as(*[pmm.PAGE_SIZE]u8, @ptrCast(kernel_pml4)), 0); 94 | 95 | for (256..512) |i| { 96 | _ = get_next_level(kernel_pml4.?, i) catch |err| { 97 | @import("std").debug.panic("ouppps: {}", .{err}); 98 | }; 99 | } 100 | 101 | try map_kernel(); 102 | 103 | var addr: u64 = 0; 104 | // Map the first 4gb 105 | while (addr < 0x100000000) : (addr += pmm.PAGE_SIZE) { 106 | try map_page(kernel_pml4.?, addr, addr, PmlEntryFlag.PRESENT | PmlEntryFlag.READ_WRITE | PmlEntryFlag.USER); 107 | try map_page(kernel_pml4.?, mem.mmap_phys_to_virt(addr), addr, PmlEntryFlag.PRESENT | PmlEntryFlag.READ_WRITE); 108 | } 109 | 110 | for (0..memmap.entry_count) |i| { 111 | const entry = memmap.entries()[i]; 112 | if (entry.kind == .kernel_and_modules) { 113 | const phys = try mem.PhysAddr.new(entry.base); 114 | 115 | try map_page(kernel_pml4.?, phys.to_kernel().addr, phys.addr, PmlEntryFlag.PRESENT | PmlEntryFlag.READ_WRITE); 116 | 117 | continue; 118 | } 119 | 120 | const base = try (try mem.PhysAddr.new(entry.base)).align_down(pmm.PAGE_SIZE); 121 | const top = try (try mem.PhysAddr.new(entry.base + entry.length)).align_up(pmm.PAGE_SIZE); 122 | 123 | // 4GiB = 0x100_000_000 124 | // map only over 4GiB cause 0..4Gib is already mapped 125 | if (base.addr >= 0x100_000_000) { 126 | var j: u64 = base.addr; 127 | while (j < top.addr) : (j += pmm.PAGE_SIZE) { 128 | const phys = try mem.PhysAddr.new(j); 129 | try map_page(kernel_pml4.?, phys.to_virt().addr, phys.addr, PmlEntryFlag.PRESENT | PmlEntryFlag.READ_WRITE); 130 | } 131 | } 132 | } 133 | 134 | serial.println("Switch pagemap", .{}); 135 | switch_to_pagemap(mem.mmap_virt_to_phys(@intFromPtr(kernel_pml4.?))); 136 | } 137 | 138 | fn map_kernel() !void { 139 | if (@import("builtin").is_test) { 140 | return; 141 | } 142 | 143 | try map_section_range(@intFromPtr(&Section.text_start_addr), @intFromPtr(&Section.text_end_addr), PmlEntryFlag.PRESENT); 144 | try map_section_range(@intFromPtr(&Section.data_start_addr), @intFromPtr(&Section.data_end_addr), PmlEntryFlag.PRESENT | PmlEntryFlag.NOX | PmlEntryFlag.READ_WRITE); 145 | try map_section_range(@intFromPtr(&Section.rodata_start_addr), @intFromPtr(&Section.rodata_end_addr), PmlEntryFlag.PRESENT | PmlEntryFlag.NOX); 146 | } 147 | 148 | fn get_next_level(pml: *align(1) Pml, index: u64) !*align(1) Pml { 149 | const page: PmlEntry = pml.*.entries[index]; 150 | 151 | if (page.flags.present) { 152 | const phys = try page.as_phys_addr(); 153 | 154 | return @ptrFromInt(phys.to_virt().addr); 155 | } 156 | 157 | const alloc_page = try pmm.alloc(1); 158 | 159 | const pml_entry = 160 | try PmlEntry.new_with_addr(@intFromPtr(alloc_page)); 161 | @memset(@as(*[4096]u8, @ptrCast(mem.mmap_phys_to_virt_ptr(alloc_page))), 0); 162 | pml.entries[index] = pml_entry; 163 | 164 | return @ptrCast(mem.mmap_phys_to_virt_ptr(alloc_page)); 165 | } 166 | 167 | pub fn map_page(pml: *Pml, virt: u64, phys: u64, flags: u64) !void { 168 | const virt_addr = try mem.VirtAddr.new(virt); 169 | 170 | const pml4_index = virt_addr.get_pml4_index(); 171 | const pml3_index = virt_addr.get_pml3_index(); 172 | const pml2_index = virt_addr.get_pml2_index(); 173 | const pml1_index = virt_addr.get_pml1_index(); 174 | 175 | const pml3 = try get_next_level(pml, pml4_index); 176 | const pml2 = try get_next_level(pml3, pml3_index); 177 | const pml1 = try get_next_level(pml2, pml2_index); 178 | 179 | if (pml1.entries[pml1_index].flags.present) { 180 | serial.println("{X} is already map to {X}", .{ virt, (try pml1.entries[pml1_index].as_phys_addr()).addr }); 181 | return error.AlreadyMap; 182 | } 183 | 184 | var entry = try PmlEntry.new_with_addr(phys); 185 | entry.set_flags(PmlEntryFlag.from_u64(flags)); 186 | 187 | pml1.entries[pml1_index] = entry; 188 | } 189 | 190 | pub fn remap_page(pml: *Pml, virt: u64, phys: u64, flags: u64) !void { 191 | const virt_addr = try mem.VirtAddr.new(virt); 192 | 193 | const pml4_index = virt_addr.get_pml4_index(); 194 | const pml3_index = virt_addr.get_pml3_index(); 195 | const pml2_index = virt_addr.get_pml2_index(); 196 | const pml1_index = virt_addr.get_pml1_index(); 197 | 198 | const pml3 = try get_next_level(pml, pml4_index); 199 | const pml2 = try get_next_level(pml3, pml3_index); 200 | const pml1 = try get_next_level(pml2, pml2_index); 201 | 202 | pml.entries[pml4_index].set_flags(PmlEntryFlag.from_u64(flags)); 203 | 204 | pml3.entries[pml3_index].set_flags(PmlEntryFlag.from_u64(flags)); 205 | pml2.entries[pml2_index].set_flags(PmlEntryFlag.from_u64(flags)); 206 | 207 | var entry = try PmlEntry.new_with_addr(phys); 208 | entry.set_flags(PmlEntryFlag.from_u64(flags)); 209 | 210 | pml1.entries[pml1_index] = entry; 211 | 212 | asm volatile ("invlpg (%[addr])" 213 | : 214 | : [addr] "r" (virt), 215 | : "memory" 216 | ); 217 | } 218 | 219 | fn map_section_range(start_addr: u64, end_addr: u64, flags: u64) !void { 220 | var addr = utils.align_down(start_addr, pmm.PAGE_SIZE); 221 | 222 | while (addr < utils.align_up(end_addr, pmm.PAGE_SIZE)) : (addr += pmm.PAGE_SIZE) { 223 | const kaddr = limine_rq.kaddr_req.response.?; 224 | const physical: usize = addr - kaddr.virtual_base + kaddr.physical_base; 225 | 226 | try map_page(kernel_pml4.?, addr, physical, flags); 227 | } 228 | } 229 | 230 | fn unmap_page(pml: *Pml, virt: u64) !void { 231 | const virt_addr = try mem.VirtAddr.new(virt); 232 | 233 | const pml4_index = virt_addr.get_pml4_index(); 234 | const pml3_index = virt_addr.get_pml3_index(); 235 | const pml2_index = virt_addr.get_pml2_index(); 236 | const pml1_index = virt_addr.get_pml1_index(); 237 | 238 | const pml3 = try get_next_level(pml, pml4_index); 239 | const pml2 = try get_next_level(pml3, pml3_index); 240 | const pml1 = try get_next_level(pml2, pml2_index); 241 | 242 | pml1.entries[pml1_index].entry = .{ .flags = @bitCast(0) }; 243 | } 244 | 245 | fn switch_to_pagemap(pagemap: u64) void { 246 | if (@import("builtin").is_test) { 247 | return; 248 | } 249 | 250 | const registers = @import("../arch/x86/regs.zig"); 251 | 252 | var cr3: registers.Cr3 = .{}; 253 | cr3.write_page_base(pagemap); 254 | cr3.apply(); 255 | } 256 | -------------------------------------------------------------------------------- /src/psf.zig: -------------------------------------------------------------------------------- 1 | const serial = @import("./drivers/serial.zig"); 2 | 3 | // http://www.zap.org.au/projects/console-fonts-lucida/ 4 | pub const lucida = @embedFile("./font/lucida-10x16.psf"); 5 | 6 | pub const GlyphIter = struct { 7 | base: [*]const u8, 8 | cur: u32 = 0, 9 | max: u32, 10 | step: u32 = 2, 11 | 12 | pub fn iter(self: *@This()) ?u16 { 13 | if (self.cur + self.step > self.max) { 14 | return null; 15 | } 16 | 17 | const high_part = self.base[self.cur]; 18 | const low_part = self.base[self.cur + 1]; 19 | self.cur += self.step; 20 | 21 | return (@as(u16, high_part) << 8 | @as(u16, low_part)) >> 6; 22 | } 23 | }; 24 | 25 | pub const Psf2Header = packed struct(u256) { 26 | magic_bytes: u32, 27 | version: u32, 28 | header_size: u32, 29 | flags: u32, 30 | length: u32, 31 | glyph_size: u32, 32 | height: u32, 33 | width: u32, 34 | 35 | const Flags = struct {}; 36 | }; 37 | 38 | pub const Psf2 = packed struct { 39 | header: Psf2Header, 40 | glyphs: void, 41 | 42 | pub fn printHeader(self: *align(1) const @This()) void { 43 | serial.println("{any}", .{self.header}); 44 | } 45 | 46 | pub fn readall(self: *align(1) const @This()) void { 47 | for (0..self.header.length) |nb| { 48 | serial.println("", .{}); 49 | var iter = self.readGlyph(@intCast(nb)); 50 | while (iter.iter()) |l| { 51 | serial.print("\n{b:0>8}", .{l}); 52 | } 53 | } 54 | } 55 | 56 | pub fn readGlyph(self: *align(1) const @This(), nb: u8) GlyphIter { 57 | const glyphs: [*]const u16 = @alignCast(@ptrCast(&self.glyphs)); 58 | const bytesperlines: u32 = (self.header.width + 7) / 16; 59 | const glyph: u32 = (self.header.glyph_size * nb) / 2; 60 | 61 | return GlyphIter{ 62 | .base = @ptrCast(glyphs[glyph..]), 63 | .max = self.header.height * bytesperlines * 2, 64 | }; 65 | } 66 | }; 67 | 68 | test "read" { 69 | const std = @import("std"); 70 | 71 | const a: *align(1) const Psf2 = @ptrCast(lucida); 72 | 73 | var glyph = a.readGlyph(0xDB); 74 | 75 | while (glyph.iter()) |i| { 76 | try std.testing.expectFmt("1111111111", "{b}", .{i}); 77 | } 78 | } 79 | 80 | test "read_a" { 81 | const std = @import("std"); 82 | 83 | const a: *align(1) const Psf2 = @ptrCast(lucida); 84 | 85 | var glyph = a.readGlyph(0x61); 86 | 87 | for (0..6) |_| { 88 | const value = glyph.iter(); 89 | try std.testing.expectFmt("0", "{b}", .{value.?}); 90 | } 91 | 92 | if (glyph.iter()) |v| { 93 | try std.testing.expectFmt("11110000", "{b}", .{v}); 94 | } 95 | } 96 | 97 | test "read_a0" { 98 | const std = @import("std"); 99 | 100 | const a: *align(1) const Psf2 = @ptrCast(lucida); 101 | 102 | var glyph = a.readGlyph(0xA0); 103 | 104 | for (0..8) |i| { 105 | const value = glyph.iter(); 106 | if (i % 2 == 0) { 107 | try std.testing.expectFmt("101010101", "{b}", .{value.?}); 108 | } else { 109 | try std.testing.expectFmt("1010101010", "{b}", .{value.?}); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/sched/ctx.zig: -------------------------------------------------------------------------------- 1 | pub const RegsContext = @import("../arch/x86/idt/interrupt.zig").Context; 2 | 3 | pub const Status = enum { READY, RUNNING, DEAD, IN_SYSCALL }; 4 | 5 | pub const Context = packed struct { 6 | stack: u64, 7 | kernel_stack: u64, 8 | 9 | regs: RegsContext, 10 | 11 | pub fn init(self: *@This(), ip: u64, stackptr: u64, kernel_stackptr: u64, is_user: bool) void { 12 | self.regs.iret.rip = ip; 13 | self.regs.iret.flags = 202; 14 | 15 | if (is_user) { 16 | self.regs.iret.cs = 0x1B; 17 | self.regs.iret.ss = 0x23; 18 | } else { 19 | self.regs.iret.cs = 0x08; 20 | self.regs.iret.ss = 0x10; 21 | } 22 | 23 | self.regs.regs.rbp = 0; 24 | 25 | self.regs.iret.rsp = stackptr; 26 | 27 | self.stack = stackptr; 28 | self.kernel_stack = kernel_stackptr; 29 | @import("../drivers/serial.zig").println("New task: {any}", .{self}); 30 | } 31 | 32 | pub fn store_regs(self: *@This(), regs: *const RegsContext) void { 33 | self.regs = regs.*; 34 | } 35 | 36 | pub fn load_to(self: *const @This(), regs: *RegsContext) void { 37 | @import("root").syscall.set_gs(@intFromPtr(self)); 38 | 39 | regs.* = self.regs; 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /src/sched/scheduler.zig: -------------------------------------------------------------------------------- 1 | pub const context = @import("ctx.zig"); 2 | pub const serial = @import("../drivers/serial.zig"); 3 | pub const Heap = @import("../mem/heap.zig").Heap; 4 | 5 | pub var is_running = false; 6 | pub var base: ?*Task = undefined; 7 | pub var current_process: ?*Task = null; 8 | 9 | pub const Task = struct { 10 | id: ?u16 = null, 11 | ctx: context.Context, 12 | status: context.Status = .READY, 13 | next_task: ?*Task = null, 14 | 15 | pub fn create(alloc: *align(1) Heap) !*@This() { 16 | return try alloc.create(@This()); 17 | } 18 | 19 | pub fn destroy(self: *@This(), alloc: *align(1) Heap) !void { 20 | alloc.free(self); 21 | } 22 | 23 | pub fn init(self: *@This(), ip: u64, stackptr: u64, kernel_stackptr: u64, is_user: bool) void { 24 | self.ctx.init(ip, stackptr, kernel_stackptr, is_user); 25 | } 26 | }; 27 | 28 | pub fn add_process(new_task: *Task) !void { 29 | if (base == null) { 30 | base = new_task; 31 | new_task.id = 0; 32 | current_process = base; 33 | return; 34 | } 35 | 36 | var id: u16 = 1; 37 | var task = base; 38 | 39 | while (task.?.next_task != null) { 40 | task = task.?.next_task; 41 | id += 1; 42 | } 43 | 44 | new_task.id = id; 45 | task.?.next_task = new_task; 46 | } 47 | 48 | pub fn current() ?*Task { 49 | return current_process; 50 | } 51 | 52 | pub fn next() ?*Task { 53 | if (current_process == null) { 54 | return null; 55 | } 56 | 57 | if (current_process.?.next_task) |next_task| { 58 | current_process = next_task; 59 | return current_process; 60 | } 61 | 62 | current_process = base; 63 | 64 | return current_process; 65 | } 66 | 67 | pub fn schedule(ctx: *context.RegsContext) !void { 68 | if (!is_running) return; 69 | 70 | var current_task = current() orelse return; 71 | 72 | if (current_task.status == .RUNNING) { 73 | current_task.ctx.store_regs(ctx); 74 | current_task.status = .READY; 75 | } 76 | 77 | var next_task = next() orelse return error.NoProcess; 78 | 79 | serial.println_nolock("new task {?}", .{next_task.id}); 80 | next_task.status = .RUNNING; 81 | 82 | current().?.ctx.load_to(ctx); 83 | } 84 | -------------------------------------------------------------------------------- /src/smp.zig: -------------------------------------------------------------------------------- 1 | const println = @import("root").drivers.serial.println; 2 | const cpu = @import("root").cpu; 3 | const limine_rq = @import("./limine_rq.zig"); 4 | const limine = @import("limine"); 5 | 6 | pub fn ap_handler(a: *limine.SmpInfo) callconv(.C) noreturn { 7 | println("Welcome from {}", .{a.processor_id}); 8 | while (true) {} 9 | } 10 | 11 | pub fn init() !void { 12 | const running_cpu = cpu.get_id(); 13 | const limine_smp = limine_rq.smp.response.?; 14 | 15 | for (limine_smp.cpus()) |entry| { 16 | if (entry.processor_id == running_cpu) { 17 | continue; 18 | } 19 | 20 | // println("CPU: {}", .{i}); 21 | entry.goto_address = &ap_handler; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/sync.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn TicketLock(comptime value_type: type) type { 4 | return struct { 5 | last_ticket: std.atomic.Value(u32), 6 | current_ticket: std.atomic.Value(u32), 7 | value: value_type, 8 | 9 | pub fn init(value: value_type) @This() { 10 | return .{ 11 | .last_ticket = std.atomic.Value(u32).init(0), 12 | .current_ticket = std.atomic.Value(u32).init(0), 13 | .value = value, 14 | }; 15 | } 16 | 17 | pub fn lock(self: *@This()) *value_type { 18 | const a = self.last_ticket.fetchAdd(1, .acquire); 19 | 20 | while (self.current_ticket.load(.acquire) != a) {} 21 | 22 | return &self.value; 23 | } 24 | 25 | pub fn unlock(self: *@This()) void { 26 | _ = self.current_ticket.fetchAdd(1, .acquire); 27 | } 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /src/test.zig: -------------------------------------------------------------------------------- 1 | comptime { 2 | _ = @import("./psf.zig"); 3 | _ = @import("./mem/pmm.zig"); 4 | _ = @import("./mem/mem.zig"); 5 | _ = @import("./ds.zig"); 6 | _ = @import("./mem/heap.zig"); 7 | } 8 | 9 | pub fn main() void {} 10 | -------------------------------------------------------------------------------- /src/utils.zig: -------------------------------------------------------------------------------- 1 | pub inline fn align_up(addr: usize, alignment: usize) usize { 2 | return (addr + alignment - 1) & ~(alignment - 1); 3 | } 4 | 5 | pub inline fn align_down(addr: usize, alignment: usize) usize { 6 | return (addr) & ~(alignment - 1); 7 | } 8 | 9 | pub inline fn is_align(addr: usize, alignment: usize) bool { 10 | return align_down(addr, alignment) == addr; 11 | } 12 | 13 | pub fn read_cr0() u64 { 14 | return asm volatile ("mov %%cr0, %[cr0]" 15 | : [cr0] "=r" (-> u64), 16 | ); 17 | } 18 | 19 | pub fn write_cr0(value: usize) void { 20 | asm volatile ("mov %[value], %%cr0" 21 | : 22 | : [value] "{rax}" (value), 23 | ); 24 | } 25 | 26 | pub const Msr = struct { 27 | pub const EFER_ENABLE_SYSCALL = 1; 28 | 29 | pub const Regs = struct { 30 | pub const APIC = 0x1B; 31 | pub const EFER = 0xC000_0080; 32 | pub const STAR = 0xC000_0081; 33 | pub const LSTAR = 0xC000_0082; 34 | pub const COMPAT_STAR = 0xC000_0083; 35 | pub const SYSCALL_FLAGS = 0xC000_0084; 36 | pub const FS_BASE = 0xC000_0100; 37 | pub const GS_BASE = 0xC000_0101; 38 | pub const KERN_GS_BASE = 0xC000_0102; 39 | }; 40 | 41 | pub fn read(msr: u64) u64 { 42 | var low: u32 = 0; 43 | var high: u32 = 0; 44 | 45 | asm volatile ("rdmsr" 46 | : [low] "=rax" (low), 47 | [high] "=rdx" (high), 48 | : [msr] "{rcx}" (msr), 49 | : "=r" 50 | ); 51 | 52 | return @as(u64, @intCast(high)) << 32 | low; 53 | } 54 | 55 | pub fn write(msr: u64, value: u64) void { 56 | const low: u32 = @truncate(value); 57 | const high: u32 = @truncate(value >> 32); 58 | asm volatile ("wrmsr" 59 | : 60 | : [_] "{rcx}" (msr), 61 | [_] "{eax}" (low), 62 | [_] "{edx}" (high), 63 | ); 64 | } 65 | }; 66 | 67 | pub fn read_msr(msr: u64) u64 { 68 | var low: u32 = 0; 69 | var high: u32 = 0; 70 | 71 | asm volatile ("rdmsr" 72 | : [low] "=rax" (low), 73 | [high] "=rdx" (high), 74 | : [msr] "{rcx}" (msr), 75 | : "=r" 76 | ); 77 | 78 | return @as(u64, @intCast(high)) << 32 | low; 79 | } 80 | 81 | pub fn checkSize(cmp: type, expected_size: u64) void { 82 | comptime { 83 | const comptimePrint = @import("std").fmt.comptimePrint; 84 | if (@sizeOf(cmp) != expected_size) { 85 | @compileError(comptimePrint("Bad size({} instead of {}) for " ++ @typeName(cmp), .{ @sizeOf(cmp), expected_size })); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /utils/idiot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rheydskey/zros/7055f9c757e234b12a6f7255b2a277561a42108c/utils/idiot -------------------------------------------------------------------------------- /utils/idiot.s: -------------------------------------------------------------------------------- 1 | [BITS 64] 2 | 3 | push r10 4 | a: 5 | mov r10, 0xCAFE 6 | mov rax, 1 7 | syscall 8 | jmp a 9 | pop r10 10 | --------------------------------------------------------------------------------