From ed5b97a87b9bb67f03554e5f82756f7a8ec7a825 Mon Sep 17 00:00:00 2001 From: Jack Halford Date: Fri, 29 Nov 2019 21:24:32 +0100 Subject: [PATCH] commit before starting Brendan's multitasking tutorial --- build.zig | 1 + grub.sh => mkiso.sh | 5 +-- src/arch/x86/constants.zig | 1 + src/arch/x86/idt.zig | 2 +- src/arch/x86/index.zig | 12 +++--- src/arch/x86/interrupt.zig | 8 ++-- src/arch/x86/main.zig | 5 +-- src/arch/x86/paging.zig | 34 ++++++++-------- src/arch/x86/{memory.zig => pmem.zig} | 21 ++++------ src/arch/x86/switch_tasks.s | 47 ++++++++++++++++++++++ src/console.zig | 2 +- src/index.zig | 16 +++++--- src/{arch/x86 => }/layout.zig | 4 +- src/main.zig | 15 +++++-- src/memory.zig | 7 ---- src/task.zig | 56 +++++++++++++++++++++++++++ src/vmem.zig | 40 +++++++++++++++++++ 17 files changed, 211 insertions(+), 65 deletions(-) rename grub.sh => mkiso.sh (73%) create mode 100644 src/arch/x86/constants.zig rename src/arch/x86/{memory.zig => pmem.zig} (80%) create mode 100644 src/arch/x86/switch_tasks.s rename src/{arch/x86 => }/layout.zig (79%) delete mode 100644 src/memory.zig create mode 100644 src/task.zig create mode 100644 src/vmem.zig diff --git a/build.zig b/build.zig index 39c2928..47237cd 100644 --- a/build.zig +++ b/build.zig @@ -11,6 +11,7 @@ pub fn build(b: *Builder) void { kernel.addAssemblyFile("src/arch/x86/gdt.s"); kernel.addAssemblyFile("src/arch/x86/isr.s"); kernel.addAssemblyFile("src/arch/x86/paging.s"); + // kernel.addAssemblyFile("src/arch/x86/switch_tasks.s"); kernel.setBuildMode(b.standardReleaseOptions()); kernel.setTarget(builtin.Arch.i386, builtin.Os.freestanding, builtin.Abi.none); diff --git a/grub.sh b/mkiso.sh similarity index 73% rename from grub.sh rename to mkiso.sh index 2bdd1a5..3c8ae74 100755 --- a/grub.sh +++ b/mkiso.sh @@ -1,10 +1,9 @@ #!/bin/bash -exit_missing() { - printf "$_ must be installed\n" ; exit 1; -} +exit_missing() { printf "$_ must be installed\n" ; exit 1; } which xorriso || exit_missing which grub-mkrescue || exit_missing + mkdir -p build/iso/boot cp build/bzImage build/iso/boot grub-mkrescue -o build/kernel.iso build/iso diff --git a/src/arch/x86/constants.zig b/src/arch/x86/constants.zig new file mode 100644 index 0000000..326ed83 --- /dev/null +++ b/src/arch/x86/constants.zig @@ -0,0 +1 @@ +pub const PAGE_SIZE: usize = 4096; diff --git a/src/arch/x86/idt.zig b/src/arch/x86/idt.zig index 33ec4a8..64d9381 100644 --- a/src/arch/x86/idt.zig +++ b/src/arch/x86/idt.zig @@ -58,7 +58,7 @@ pub fn initialize() void { isr.install_irqs(); isr.install_syscalls(); interrupt.registerIRQ(0, interrupt.pit_handler); - interrupt.registerIRQ(1, ps2.keyboard_handler); + interrupt.registerIRQ(1, kernel.ps2.keyboard_handler); // load IDT lidt(@ptrToInt(&idtr)); diff --git a/src/arch/x86/index.zig b/src/arch/x86/index.zig index 052e97b..8cd1c39 100644 --- a/src/arch/x86/index.zig +++ b/src/arch/x86/index.zig @@ -1,18 +1,16 @@ // std -pub const assert = @import("std").debug.assert; +pub const std = @import("std"); +pub const assert = 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"); +pub const kernel = @import("../../index.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 usingnamespace @import("constants.zig"); +pub const pmem = @import("pmem.zig"); pub const paging = @import("paging.zig"); pub const idt = @import("idt.zig"); pub const isr = @import("isr.zig"); diff --git a/src/arch/x86/interrupt.zig b/src/arch/x86/interrupt.zig index 5900a2f..da581e8 100644 --- a/src/arch/x86/interrupt.zig +++ b/src/arch/x86/interrupt.zig @@ -35,11 +35,11 @@ var handlers = [_]fn () void{unhandled} ** 48; fn unhandled() noreturn { const n = isr.context.interrupt_n; - print("unhandled interrupt number {d}", n); + kernel.print("unhandled interrupt number {d}", n); if (n < IRQ_0) { - println(" (exception)"); + kernel.println(" (exception)"); } else { - println(" (IRQ number {d})", n - IRQ_0); + kernel.println(" (IRQ number {d})", n - IRQ_0); } hang(); } @@ -196,5 +196,5 @@ pub fn pit_handler() void { // pit freq = 1.193182 MHz // chan0 divisor = 2685 // PIT_RATE in us - time.increment(2251); + kernel.time.increment(2251); } diff --git a/src/arch/x86/main.zig b/src/arch/x86/main.zig index 8b5eb77..b5d8ec4 100644 --- a/src/arch/x86/main.zig +++ b/src/arch/x86/main.zig @@ -1,12 +1,11 @@ // usingnamespace @import("kernel"); usingnamespace @import("index.zig"); -// const multiboot = @import("../../multiboot.zig"); /// x86 specific intialization -pub fn x86_main(info: *const multiboot.MultibootInfo) void { +pub fn x86_main(info: *const kernel.multiboot.MultibootInfo) void { gdt.initialize(); idt.initialize(); - memory.initialize(info); + pmem.initialize(info); paging.initialize(); // enable interrupts diff --git a/src/arch/x86/paging.zig b/src/arch/x86/paging.zig index 3446a4f..1f9ad99 100644 --- a/src/arch/x86/paging.zig +++ b/src/arch/x86/paging.zig @@ -1,10 +1,8 @@ usingnamespace @import("index.zig"); -// usingnamespace @import("x86"); extern fn setupPaging(phys_pd: usize) void; const PageEntry = usize; -pub const PAGE_SIZE = 4096; pub const PT = @intToPtr([*]PageEntry, 0xFFC00000); pub const PD = @intToPtr([*]PageEntry, 0xFFFFF000); const PRESENT = 0x1; @@ -18,18 +16,18 @@ const HUGE = 0x80; pub var pageDirectory: [1024]PageEntry align(4096) linksection(".bss") = [_]PageEntry{0} ** 1024; fn pageFault() void { - println("pagefault"); + kernel.println("pagefault"); while (true) asm volatile ("hlt"); } -inline fn pageBase(addr: usize) usize { +inline fn pageBase(virt: usize) usize { return addr & (~PAGE_SIZE +% 1); } -inline fn pde(addr: usize) *PageEntry { - return &PD[addr >> 22]; +inline fn pde(virt: usize) *PageEntry { + return &PD[virt >> 22]; //relies on recursive mapping } -inline fn pte(addr: usize) *PageEntry { - return &PT[addr >> 12]; +inline fn pte(virt: usize) *PageEntry { + return &PT[virt >> 12]; //relies on recursive mapping } // virtual to physical @@ -40,17 +38,18 @@ pub fn translate(virt: usize) ?usize { pub fn unmap(virt: usize) void { if (translate(virt)) |phys| { - memory.free(translate(virt)); + mem.free(phys); } else { - println("can't unmap 0x{x}, map is empty.", addr); + kernel.println("can't unmap 0x{x} because it is not mapped.", virt); } } pub fn mmap(virt: usize, phys: ?usize) !void { - var pde: *PageEntry = pde(virt); - if (pde.* == 0) pde.* = try memory.allocate() | WRITE | PRESENT; - var pte: *PageEntry = pte(virt); - pte.* = if (phys) |p| p else allocate() | PRESENT; + //TODO: support hugepages + // allocate a page directory if there is none + if (pde(virt).* == 0) pde(virt).* = (try pmem.allocate()) | WRITE | PRESENT; + // allocate a frame if phys isn't specified + pte(virt).* = (if (phys) |p| p else try pmem.allocate()) | PRESENT; } pub fn initialize() void { @@ -61,7 +60,8 @@ pub fn initialize() void { // recursive mapping p2[1023] = @ptrToInt(&p2[0]) | PRESENT | WRITE; - assert(memory.stack_end < layout.IDENTITY); + // TODO: verify is this a hack? + assert(pmem.stack_end < kernel.layout.IDENTITY); interrupt.register(14, pageFault); setupPaging(@ptrToInt(&pageDirectory[0])); @@ -72,12 +72,12 @@ pub fn introspect() void { i = 0; while (i < 1024) : (i += 1) { if (PD[i] == 0) continue; - println("p2[{}] -> 0x{x}", i, PD[i]); + kernel.println("p2[{}] -> 0x{x}", i, PD[i]); if (PD[i] & HUGE != 0) continue; var j: usize = 0; while (j < 1024) : (j += 1) { var entry: PageEntry = PT[i * 1024 + j]; - if (entry != 0) println("p2[{}]p1[{}] -> 0x{x}", i, j, entry); + if (entry != 0) kernel.println("p2[{}]p1[{}] -> 0x{x}", i, j, entry); } } } diff --git a/src/arch/x86/memory.zig b/src/arch/x86/pmem.zig similarity index 80% rename from src/arch/x86/memory.zig rename to src/arch/x86/pmem.zig index 89a2518..05046f4 100644 --- a/src/arch/x86/memory.zig +++ b/src/arch/x86/pmem.zig @@ -7,7 +7,6 @@ var stack_index: usize = 0; // Index into the stack. pub var stack_size: usize = undefined; pub var stack_end: usize = undefined; -pub const PAGE_SIZE: usize = 4096; pub inline fn pageAlign(address: u32) u32 { // 4095 -> 4096 // 4096 -> 4096 @@ -15,7 +14,6 @@ pub inline fn pageAlign(address: u32) u32 { return (address + PAGE_SIZE - 1) & (~PAGE_SIZE +% 1); } -//// // Return the amount of variable elements (in bytes). // pub inline fn available() usize { @@ -26,19 +24,17 @@ pub inline fn available_MiB() usize { return available() / (1024 * 1024); } -//// // Request a free physical page and return its address. // pub fn allocate() !usize { if (available() == 0) { - println("out of memory"); + kernel.println("out of memory"); return error.OutOfMemory; } stack_index -= 1; return stack[stack_index]; } -//// // Free a previously allocated physical page. // // Arguments: @@ -49,16 +45,15 @@ pub fn free(address: usize) void { stack_index += 1; } -//// // Scan the memory map to index all available memory. // // Arguments: // info: Information structure from bootloader. // -pub fn initialize(info: *const multiboot.MultibootInfo) void { +pub fn initialize(info: *const kernel.multiboot.MultibootInfo) void { // Ensure the bootloader has given us the memory map. - assert((info.flags & multiboot.MULTIBOOT_INFO_MEMORY) != 0); - assert((info.flags & multiboot.MULTIBOOT_INFO_MEM_MAP) != 0); + assert((info.flags & kernel.multiboot.MULTIBOOT_INFO_MEMORY) != 0); + assert((info.flags & kernel.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 @@ -74,7 +69,7 @@ pub fn initialize(info: *const multiboot.MultibootInfo) void { var map: usize = info.mmap_addr; while (map < info.mmap_addr + info.mmap_length) { - var entry = @intToPtr(*multiboot.MultibootMMapEntry, map); + var entry = @intToPtr(*kernel.multiboot.MultibootMMapEntry, map); // Calculate the start and end of this memory area. var start = @truncate(usize, entry.addr); @@ -83,16 +78,16 @@ pub fn initialize(info: *const multiboot.MultibootInfo) void { start = if (start >= stack_end) start else stack_end; // Flag all the pages in this memory area as free. - if (entry.type == multiboot.MULTIBOOT_MEMORY_AVAILABLE) while (start < end) : (start += PAGE_SIZE) + if (entry.type == kernel.multiboot.MULTIBOOT_MEMORY_AVAILABLE) while (start < end) : (start += PAGE_SIZE) free(start); // Go to the next entry in the memory map. map += entry.size + @sizeOf(@typeOf(entry.size)); } - println("available memory: {d} MiB ", available() / 1024 / 1024); + kernel.println("available memory: {d} MiB ", available() / 1024 / 1024); } pub fn introspect() void { - println("physframes left: {d} ({d} MiB)", stack_index, available_MiB()); + kernel.println("physframes left: {d} ({d} MiB)", stack_index, available_MiB()); } diff --git a/src/arch/x86/switch_tasks.s b/src/arch/x86/switch_tasks.s new file mode 100644 index 0000000..12ff95e --- /dev/null +++ b/src/arch/x86/switch_tasks.s @@ -0,0 +1,47 @@ +;https://wiki.osdev.org/Multitasking_Systems + +;C declaration: +; void switch_tasks(thread_control_block *next_thread); +; +;WARNING: Caller is expected to disable IRQs before calling, and enable IRQs again after function returns + +switch_tasks: + + ;Save previous task's state + + ;Notes: + ; For cdecl; EAX, ECX, and EDX are already saved by the caller and don't need to be saved again + ; EIP is already saved on the stack by the caller's "CALL" instruction + ; The task isn't able to change CR3 so it doesn't need to be saved + ; Segment registers are constants (while running kernel code) so they don't need to be saved + + push ebx + push esi + push edi + push ebp + + mov edi,[current_task_TCB] ;edi = address of the previous task's "thread control block" + mov [edi+TCB.ESP],esp ;Save ESP for previous task's kernel stack in the thread's TCB + + ;Load next task's state + + mov esi,[esp+(4+1)*4] ;esi = address of the next task's "thread control block" (parameter passed on stack) + mov [current_task_TCB],esi ;Current task's TCB is the next task TCB + + mov esp,[esi+TCB.ESP] ;Load ESP for next task's kernel stack from the thread's TCB + mov eax,[esi+TCB.CR3] ;eax = address of page directory for next task + mov ebx,[esi+TCB.ESP0] ;ebx = address for the top of the next task's kernel stack + mov [TSS.ESP0],ebx ;Adjust the ESP0 field in the TSS (used by CPU for for CPL=3 -> CPL=0 privilege level changes) + mov ecx,cr3 ;ecx = previous task's virtual address space + + cmp eax,ecx ;Does the virtual address space need to being changed? + je .doneVAS ; no, virtual address space is the same, so don't reload it and cause TLB flushes + mov cr3,eax ; yes, load the next task's virtual address space +.doneVAS: + + pop ebp + pop edi + pop esi + pop ebx + + ret ;Load next task's EIP from its kernel stack) diff --git a/src/console.zig b/src/console.zig index a7c0b9d..8c5830a 100644 --- a/src/console.zig +++ b/src/console.zig @@ -6,7 +6,7 @@ var command_len: usize = 0; fn execute(com: []u8) void { 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, "x86memory")) return x86.pmem.introspect(); if (eql(u8, com, "lspci")) return pci.lspci(); if (eql(u8, com, "uptime")) return time.uptime(); if (eql(u8, com, "topbar")) return topbar(); diff --git a/src/index.zig b/src/index.zig index dc067e2..5ef7a58 100644 --- a/src/index.zig +++ b/src/index.zig @@ -1,14 +1,20 @@ -// std +/// std pub const std = @import("std"); pub const assert = std.debug.assert; -// main namespace pub usingnamespace @import("vga.zig"); + +///arch pub const x86 = @import("arch/x86/index.zig"); + +///core +pub const layout = @import("layout.zig"); pub const multiboot = @import("multiboot.zig"); -pub const mem = @import("memory.zig"); +pub const vmem = @import("vmem.zig"); pub const task = @import("task.zig"); +pub const time = @import("time.zig"); + +///extra pub const console = @import("console.zig"); pub const pci = @import("pci/pci.zig"); -pub const ps2 = @import("ps2.zig"); -pub const time = @import("time.zig"); +pub const ps2 = @import("ps2.zig"); // i don't know whether this is x86 specific or not diff --git a/src/arch/x86/layout.zig b/src/layout.zig similarity index 79% rename from src/arch/x86/layout.zig rename to src/layout.zig index cc23c5f..5e649de 100644 --- a/src/arch/x86/layout.zig +++ b/src/layout.zig @@ -1,6 +1,7 @@ //https://wiki.osdev.org/Memory_Map_(x86) +// virtual memory layout of the kernel -const kiB = 1024; +const kiB = 1024; // bytes const MiB = 1024 * kiB; // 0x100000 const GiB = 1024 * MiB; @@ -10,6 +11,7 @@ pub const KERNEL = 1 * MiB; pub const IDENTITY = 4 * MiB; // 0->4MiB pub const HEAP = 8 * MiB; +pub const HEAP_END = 0x1000000; pub const USER_STACKS = 0x1000000; pub const USER_STACKS_END = 0x10000000; // zig fmt: on diff --git a/src/main.zig b/src/main.zig index 19fba39..3f9224e 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,5 +1,4 @@ usingnamespace @import("kernel"); -// const x86 = @import("x86"); // Place the header at the very beginning of the binary. export const multiboot_header align(4) linksection(".multiboot") = multiboot: { @@ -16,6 +15,8 @@ export const multiboot_header align(4) linksection(".multiboot") = multiboot: { }; }; +extern fn switch_tasks(stack: u32) void; + // arch independant initialization export fn kmain(magic: u32, info: *const multiboot.MultibootInfo) noreturn { assert(magic == multiboot.MULTIBOOT_BOOTLOADER_MAGIC); @@ -24,9 +25,17 @@ export fn kmain(magic: u32, info: *const multiboot.MultibootInfo) noreturn { x86.x86_main(info); println("--- core initialization ---"); pci.scan(); - console.initialize(); + vmem.initialize(); - // const t = task.Task.new(@ptrToInt(topbar)); + // var a = vmem.allocate(u32) catch unreachable; + // println("a={}", &a); + // const b = vmem.allocate(VGAEntry) catch unreachable; + // println("b={x}", &b); + + const t = task.Task.new(@ptrToInt(topbar)) catch unreachable; + println("task={x}", &t); + + console.initialize(); while (true) asm volatile ("hlt"); } diff --git a/src/memory.zig b/src/memory.zig deleted file mode 100644 index 31b728e..0000000 --- a/src/memory.zig +++ /dev/null @@ -1,7 +0,0 @@ -pub usingnamespace @import("index.zig"); - -pub fn allocate(comptime T: type) !*type {} - -pub fn free(address: usize) void {} - -pub fn initialize() void {} diff --git a/src/task.zig b/src/task.zig new file mode 100644 index 0000000..e291fe1 --- /dev/null +++ b/src/task.zig @@ -0,0 +1,56 @@ +pub usingnamespace @import("index.zig"); +// var tasks = Array(?*Task).init(&mem.allocator); + +const STACK_SIZE = x86.PAGE_SIZE; // Size of thread stacks. + +pub const Task = struct { + // context: isr.Context, + + //// + // Create a new thread inside the current process. + // NOTE: Do not call this function directly. Use Process.createThread instead. + // + // Arguments: + // entry_point: The entry point of the new thread. + // + // Returns: + // Pointer to the new thread structure. + // + tid: u16, + + pub fn stack(tid: u16) usize { + const stack = layout.USER_STACKS + (2 * (tid - 1) * STACK_SIZE); + assert(stack < layout.USER_STACKS_END); + return stack; + } + + pub fn new(entry_point: usize) !*Task { + // assert(scheduler.current_process == process); + + // map the stack + + // Allocate and initialize the thread structure. + var this = try vmem.allocate(Task); + this.tid = 4; + + return this; + } +}; + +// fn initContext(entry_point: usize, stack: usize) isr.Context { +// // Insert a trap return address to destroy the thread on return. +// var stack_top = @intToPtr(*usize, stack + STACK_SIZE - @sizeOf(usize)); +// stack_top.* = layout.THREAD_DESTROY; + +// return isr.Context{ +// .cs = gdt.USER_CODE | gdt.USER_RPL, +// .ss = gdt.USER_DATA | gdt.USER_RPL, +// .eip = entry_point, +// .esp = @ptrToInt(stack_top), +// .eflags = 0x202, + +// .registers = isr.Registers.init(), +// .interrupt_n = 0, +// .error_code = 0, +// }; +// } diff --git a/src/vmem.zig b/src/vmem.zig new file mode 100644 index 0000000..bb33325 --- /dev/null +++ b/src/vmem.zig @@ -0,0 +1,40 @@ +pub usingnamespace @import("index.zig"); + +// TODO: make a better memory allocator +// stupid simple virtual memory allocator +// - does 1:1 virtual/physical mapping +// - no defragmentation +// - no allocation bigger than a page + +const stack_size: usize = (layout.HEAP_END - layout.HEAP) / x86.PAGE_SIZE; +var stack: [stack_size]usize = undefined; // Stack of free virtual addresses +var stack_index: usize = 0; // Index into the stack. + +pub fn available() usize { + return stack_index * x86.PAGE_SIZE; +} + +pub fn allocate(comptime T: type) !*T { + assert(@sizeOf(T) < x86.PAGE_SIZE); // this allocator only support 1:1 mapping + if (available() == 0) return error.OutOfMemory; + stack_index -= 1; + var vaddr: usize = stack[stack_index]; + try x86.paging.mmap(vaddr, null); + return @intToPtr(*T, vaddr); +} + +pub fn free(address: usize) void { + x86.paging.unmap(address); + stack[stack_index] = address; + stack_index += 1; +} + +pub fn initialize() void { + var addr: usize = layout.HEAP; + while (addr < layout.HEAP_END) : (addr += x86.PAGE_SIZE) { + // println("addr {x}", addr); + stack[stack_index] = addr; + stack_index += 1; + // return; + } +}