diff --git a/README.md b/README.md index d80a3f3..3b70f6a 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,33 @@ -## hobby kernel in zig - -slowly porting from rust. +# hobby kernel in zig ### features - - vga frame buffer + - 80x25 frame buffer - ps2 keyboard driver - - interrupts - terminal console - lspci + - x86 + - MMU + - interrupts ### dependencies -`zig` compiler + - [ziglang](https://github.com/ziglang/zig) 0.5.0 -### compile +# How to -`zig build` compiles and links the multiboot kernel, without a bootloader. +## compile -### test +`zig build` compiles and links the multiboot kernel (without a bootloader) -`./qemu.sh start` -`./qemu.sh monitor` -`./qemu.sh gdb` +## test + + - `./qemu.sh start` + - `./qemu.sh monitor` + - `./qemu.sh gdb` # Notes -## interrupts +## interrupt call chain `interrupt` -> `idt[n]` -> `isrN` -> `isrDispatch` -> `handlers[n]` (default `unhandled()`) - -## layout - -`0->4Mib` kernel reserved -`1Mib` interrupt stack diff --git a/qemu.sh b/qemu.sh index 950dbb8..0abadd2 100755 --- a/qemu.sh +++ b/qemu.sh @@ -18,7 +18,7 @@ start() { # -device virtio-net,netdev=network0 -netdev tap,id=network0,ifname=tap0,script=no,downscript=no \ # build/kernel.iso - # this allows this switch to monitor with ^a-c, but doesn't + # this allows to monitor with ^a-c, but doesn't # play nice with irqs apparently... # -serial mon:stdio \ } diff --git a/src/arch/x86/idt.zig b/src/arch/x86/idt.zig index 0b9d9f8..33ec4a8 100644 --- a/src/arch/x86/idt.zig +++ b/src/arch/x86/idt.zig @@ -58,6 +58,7 @@ pub fn initialize() void { isr.install_irqs(); isr.install_syscalls(); interrupt.registerIRQ(0, interrupt.pit_handler); + interrupt.registerIRQ(1, ps2.keyboard_handler); // load IDT lidt(@ptrToInt(&idtr)); diff --git a/src/arch/x86/index.zig b/src/arch/x86/index.zig index 8cf9cea..052e97b 100644 --- a/src/arch/x86/index.zig +++ b/src/arch/x86/index.zig @@ -1,16 +1,20 @@ +// std +pub const assert = @import("std").debug.assert; + +// from core kernel pub usingnamespace @import("../../vga.zig"); pub const multiboot = @import("../../multiboot.zig"); pub const time = @import("../../time.zig"); +pub const ps2 = @import("../../ps2.zig"); +// x86 namespace pub usingnamespace @import("lib/io.zig"); pub usingnamespace @import("lib/instructions.zig"); pub usingnamespace @import("main.zig"); - +pub const layout = @import("layout.zig"); pub const memory = @import("memory.zig"); pub const paging = @import("paging.zig"); pub const idt = @import("idt.zig"); pub const isr = @import("isr.zig"); pub const gdt = @import("gdt.zig"); pub const interrupt = @import("interrupt.zig"); - -pub const assert = @import("std").debug.assert; diff --git a/src/arch/x86/interrupt.zig b/src/arch/x86/interrupt.zig index 4a3b001..5900a2f 100644 --- a/src/arch/x86/interrupt.zig +++ b/src/arch/x86/interrupt.zig @@ -175,11 +175,8 @@ pub fn maskIRQ(irq: u8, mask: bool) void { // Mask or unmask the interrupt. const shift = @intCast(u3, irq % 8); - if (mask) { - outb(port, old | (u8(1) << shift)); - } else { - outb(port, old & ~(u8(1) << shift)); - } + if (mask) outb(port, old | (u8(1) << shift)); + if (!mask) outb(port, old & ~(u8(1) << shift)); const new = inb(port); // Retrieve the current mask. } diff --git a/src/arch/x86/layout.zig b/src/arch/x86/layout.zig index 0f6f79b..cc23c5f 100644 --- a/src/arch/x86/layout.zig +++ b/src/arch/x86/layout.zig @@ -1,6 +1,15 @@ //https://wiki.osdev.org/Memory_Map_(x86) -pub const KSTACK = 0x80000; // todo: move to .bss -pub const KERNEL = 0x100000; -pub const IDENTITY = 0x400000; // 0->4MiB -pub const HEAP = 0x800000; +const kiB = 1024; +const MiB = 1024 * kiB; // 0x100000 +const GiB = 1024 * MiB; + +// zig fmt: off +pub const KSTACK = 0x80000; // todo: move to .bss +pub const KERNEL = 1 * MiB; +pub const IDENTITY = 4 * MiB; // 0->4MiB + +pub const HEAP = 8 * MiB; +pub const USER_STACKS = 0x1000000; +pub const USER_STACKS_END = 0x10000000; +// zig fmt: on diff --git a/src/arch/x86/memory.zig b/src/arch/x86/memory.zig index 3c7335c..89a2518 100644 --- a/src/arch/x86/memory.zig +++ b/src/arch/x86/memory.zig @@ -8,27 +8,31 @@ pub var stack_size: usize = undefined; pub var stack_end: usize = undefined; pub const PAGE_SIZE: usize = 4096; -// 4095 -> 4096 -// 4096 -> 4096 -// 4097 -> 8192 -pub fn pageAlign(address: u32) u32 { +pub inline fn pageAlign(address: u32) u32 { + // 4095 -> 4096 + // 4096 -> 4096 + // 4097 -> 8192 return (address + PAGE_SIZE - 1) & (~PAGE_SIZE +% 1); } //// // Return the amount of variable elements (in bytes). // -pub fn available() usize { +pub inline fn available() usize { return stack_index * PAGE_SIZE; } +pub inline fn available_MiB() usize { + return available() / (1024 * 1024); +} + //// // Request a free physical page and return its address. // -pub fn allocate() ?usize { +pub fn allocate() !usize { if (available() == 0) { println("out of memory"); - return null; + return error.OutOfMemory; } stack_index -= 1; return stack[stack_index]; @@ -56,9 +60,14 @@ pub fn initialize(info: *const multiboot.MultibootInfo) void { assert((info.flags & multiboot.MULTIBOOT_INFO_MEMORY) != 0); assert((info.flags & multiboot.MULTIBOOT_INFO_MEM_MAP) != 0); + // TODO: WHAT WHY WHAAAAT, must check back here later + // Place stack at 0x200000 so that in the future I trigger a + // random bug and I won't ever know where it came from, great. + stack = @intToPtr([*]usize, 0x200000); // 2 MiB + // Place the stack of free pages after the last Multiboot module. - stack = @intToPtr([*]usize, 0x200000); // stack = @intToPtr([*]usize, pageAlign(info.mods_addr)); + // Calculate the approximate size of the stack based on the amount of total upper memory. stack_size = ((info.mem_upper * 1024) / PAGE_SIZE) * @sizeOf(usize); stack_end = pageAlign(@ptrToInt(stack) + stack_size); @@ -83,3 +92,7 @@ pub fn initialize(info: *const multiboot.MultibootInfo) void { println("available memory: {d} MiB ", available() / 1024 / 1024); } + +pub fn introspect() void { + println("physframes left: {d} ({d} MiB)", stack_index, available_MiB()); +} diff --git a/src/arch/x86/paging.zig b/src/arch/x86/paging.zig index 6a92188..3446a4f 100644 --- a/src/arch/x86/paging.zig +++ b/src/arch/x86/paging.zig @@ -22,13 +22,13 @@ fn pageFault() void { while (true) asm volatile ("hlt"); } -fn pageBase(addr: usize) usize { +inline fn pageBase(addr: usize) usize { return addr & (~PAGE_SIZE +% 1); } -fn pde(addr: usize) *PageEntry { +inline fn pde(addr: usize) *PageEntry { return &PD[addr >> 22]; } -fn pte(addr: usize) *PageEntry { +inline fn pte(addr: usize) *PageEntry { return &PT[addr >> 12]; } @@ -46,14 +46,28 @@ pub fn unmap(virt: usize) void { } } -pub fn mmap(virt: usize, phys: ?usize) void { +pub fn mmap(virt: usize, phys: ?usize) !void { var pde: *PageEntry = pde(virt); - if (pde.* == 0) pde.* = memory.allocate() | WRITE | PRESENT; + if (pde.* == 0) pde.* = try memory.allocate() | WRITE | PRESENT; var pte: *PageEntry = pte(virt); pte.* = if (phys) |p| p else allocate() | PRESENT; } -pub fn addrspace() void { +pub fn initialize() void { + var p2 = pageDirectory[0..]; + + // identity map 0 -> 4MB + p2[0] = 0x000000 | PRESENT | WRITE | HUGE; + // recursive mapping + p2[1023] = @ptrToInt(&p2[0]) | PRESENT | WRITE; + + assert(memory.stack_end < layout.IDENTITY); + + interrupt.register(14, pageFault); + setupPaging(@ptrToInt(&pageDirectory[0])); +} + +pub fn introspect() void { var i: usize = 1; i = 0; while (i < 1024) : (i += 1) { @@ -67,17 +81,3 @@ pub fn addrspace() void { } } } - -pub fn initialize() void { - var p2 = pageDirectory[0..]; - - // identity map 0 -> 4MB - p2[0] = 0x000000 | PRESENT | WRITE | HUGE; - // recursive mapping - p2[1023] = @ptrToInt(&p2[0]) | PRESENT | WRITE; - - assert(memory.stack_end < 0x400000); - - interrupt.register(14, pageFault); - setupPaging(@ptrToInt(&pageDirectory[0])); -} diff --git a/src/console.zig b/src/console.zig index cbc47b5..a7c0b9d 100644 --- a/src/console.zig +++ b/src/console.zig @@ -4,10 +4,13 @@ var command: [10]u8 = undefined; var command_len: usize = 0; fn execute(com: []u8) void { - if (@import("std").mem.eql(u8, com, "lspci")) pci.lspci(); - if (@import("std").mem.eql(u8, com, "paging")) x86.paging.addrspace(); - if (@import("std").mem.eql(u8, com, "uptime")) time.uptime(); - if (@import("std").mem.eql(u8, com, "topbar")) topbar(); + const eql = std.mem.eql; + if (eql(u8, com, "x86paging")) return x86.paging.introspect(); + if (eql(u8, com, "x86memory")) return x86.memory.introspect(); + if (eql(u8, com, "lspci")) return pci.lspci(); + if (eql(u8, com, "uptime")) return time.uptime(); + if (eql(u8, com, "topbar")) return topbar(); + println("{}: command not found", com); } pub fn keypress(char: u8) void { @@ -15,7 +18,7 @@ pub fn keypress(char: u8) void { switch (char) { '\n' => { print("\n"); - execute(command[0..command_len]); + if (command_len > 0) execute(command[0..command_len]); print("> "); command_len = 0; }, @@ -38,6 +41,6 @@ pub fn keypress(char: u8) void { } pub fn initialize() void { - x86.interrupt.registerIRQ(1, ps2.keyboard_handler); + ps2.keyboard_callback = keypress; print("> "); } diff --git a/src/index.zig b/src/index.zig index f748201..dc067e2 100644 --- a/src/index.zig +++ b/src/index.zig @@ -1,10 +1,14 @@ -pub const assert = @import("std").debug.assert; +// std pub const std = @import("std"); +pub const assert = std.debug.assert; +// main namespace pub usingnamespace @import("vga.zig"); +pub const x86 = @import("arch/x86/index.zig"); pub const multiboot = @import("multiboot.zig"); +pub const mem = @import("memory.zig"); +pub const task = @import("task.zig"); pub const console = @import("console.zig"); pub const pci = @import("pci/pci.zig"); pub const ps2 = @import("ps2.zig"); -pub const x86 = @import("arch/x86/index.zig"); pub const time = @import("time.zig"); diff --git a/src/main.zig b/src/main.zig index ff6a638..19fba39 100644 --- a/src/main.zig +++ b/src/main.zig @@ -26,5 +26,7 @@ export fn kmain(magic: u32, info: *const multiboot.MultibootInfo) noreturn { pci.scan(); console.initialize(); + // const t = task.Task.new(@ptrToInt(topbar)); + while (true) asm volatile ("hlt"); } diff --git a/src/memory.zig b/src/memory.zig index e992f6d..31b728e 100644 --- a/src/memory.zig +++ b/src/memory.zig @@ -1,8 +1,7 @@ -const paging = @import("arch/x86/paging.zig"); +pub usingnamespace @import("index.zig"); -pub fn alloc(size: usize) usize { - -} +pub fn allocate(comptime T: type) !*type {} -pub fn free() { -} +pub fn free(address: usize) void {} + +pub fn initialize() void {} diff --git a/src/pci/pci.zig b/src/pci/pci.zig index 34751e2..e212420 100644 --- a/src/pci/pci.zig +++ b/src/pci/pci.zig @@ -54,10 +54,12 @@ pub const PciDevice = struct { var drv = Drivers[i]; if (self.class() != drv.class or self.subclass() != drv.subclass) continue; - if (drv.vendor) |v| if (self.vendor != v) - continue; - if (drv.subsystem) |ss| if (self.subsystem() != ss) - continue; + if (drv.vendor) |v| + if (self.vendor != v) + continue; + if (drv.subsystem) |ss| + if (self.subsystem() != ss) + continue; return drv; } return null; @@ -139,7 +141,7 @@ pub fn scan() void { pub fn lspci() void { var slot: u5 = 0; - println("b:s.f c,s v d drv"); + println("b:s.f c, s v d drv"); while (slot < 31) : (slot += 1) { if (PciDevice.init(0, slot, 0)) |dev| { var function: u3 = 0; diff --git a/src/ps2.zig b/src/ps2.zig index d815698..e04f7b6 100644 --- a/src/ps2.zig +++ b/src/ps2.zig @@ -78,9 +78,13 @@ fn key_isrelease(scancode: u8) bool { return scancode & (1 << 7) != 0; } +pub var keyboard_callback: fn (u8) void = undefined; + pub fn keyboard_handler() void { const scancode = ps2_scancode(); + if (scancode > KEYMAP_US.len) return; if (key_isrelease(scancode)) return; // don't process releases const shift = false; // don't know about modifiers yet - console.keypress(KEYMAP_US[scancode][if (shift) 1 else 0]); + const character = KEYMAP_US[scancode][if (shift) 1 else 0]; + if (keyboard_callback != undefined) keyboard_callback(character); } diff --git a/src/time.zig b/src/time.zig index d33ad5e..834205c 100644 --- a/src/time.zig +++ b/src/time.zig @@ -10,5 +10,5 @@ pub fn increment(value: u32) void { pub fn uptime() void { const offset_ms = offset_us / 1000; - println("uptime: {}.{:.3}", offset_s, offset_ms); + println("{}.{:.3}", offset_s, offset_ms); } diff --git a/src/vga.zig b/src/vga.zig index 3c72299..02d5847 100644 --- a/src/vga.zig +++ b/src/vga.zig @@ -2,7 +2,6 @@ const time = @import("time.zig"); const x86 = @import("arch/x86/index.zig"); const std = @import("std"); - // Screen size. pub const VGA_WIDTH = 80; pub const VGA_HEIGHT = 25; @@ -70,8 +69,7 @@ pub fn topbar() void { vga.cursor = 0; vga.background = Color.Red; - const offset_ms = time.offset_us / 1000; - println("{}.{}", time.offset_s, offset_ms / 10); + time.uptime(); vga.cursor = cursor; vga.background = bg; @@ -154,10 +152,12 @@ const VGA = struct { // fn scrollDown(self: *VGA) void { const first = VGA_WIDTH; // Index of first line. + const second = 2 * VGA_WIDTH; // Index of first line. const last = VGA_SIZE - VGA_WIDTH; // Index of last line. // Copy all the screen (apart from the first line) up one line. - std.mem.copy(VGAEntry, self.vram[0..last], self.vram[first..VGA_SIZE]); + // std.mem.copy(VGAEntry, self.vram[0..last], self.vram[first .. VGA_SIZE]); // whole screen + std.mem.copy(VGAEntry, self.vram[first..last], self.vram[second..VGA_SIZE]); // skip topbar // Clean the last line. std.mem.set(VGAEntry, self.vram[last..VGA_SIZE], self.entry(' '));