From 07089c2ea1a72677a0c28b0139fe41f18e13dbbc Mon Sep 17 00:00:00 2001 From: Jack Halford Date: Thu, 15 Aug 2019 21:15:10 +0200 Subject: [PATCH] paging done --- README.md | 5 ++ qemu.sh | 3 +- src/arch/x86/_start.s | 1 + src/arch/x86/gdt.zig | 6 +- src/arch/x86/isr.s | 2 +- src/arch/x86/layout.zig | 6 ++ src/arch/x86/linker.ld | 22 +---- src/arch/x86/main.zig | 19 +---- src/arch/x86/memory.zig | 182 ++++++++++++++++------------------------ src/arch/x86/paging.s | 6 +- src/arch/x86/paging.zig | 165 +++++++++++++++++++----------------- src/console.zig | 1 - src/main.zig | 2 + src/memory.zig | 8 ++ src/multiboot.zig | 8 +- src/pci.zig | 61 +++++++++----- src/vga.zig | 3 + 17 files changed, 247 insertions(+), 253 deletions(-) create mode 100644 src/arch/x86/layout.zig create mode 100644 src/memory.zig diff --git a/README.md b/README.md index c46298b..d80a3f3 100644 --- a/README.md +++ b/README.md @@ -29,3 +29,8 @@ slowly porting from rust. ## interrupts `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 23b8d6a..950dbb8 100755 --- a/qemu.sh +++ b/qemu.sh @@ -12,10 +12,11 @@ start() { -m 1337M \ -curses \ -append "Hello" \ + -drive file=disk.img,if=virtio\ -kernel ${KERNEL} + # -no-reboot \ # -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 # play nice with irqs apparently... diff --git a/src/arch/x86/_start.s b/src/arch/x86/_start.s index 7dbe55e..db987e0 100644 --- a/src/arch/x86/_start.s +++ b/src/arch/x86/_start.s @@ -8,6 +8,7 @@ __start: push %ebx // Pass multiboot info structure. push %eax // Pass multiboot magic code. + call kmain // Call the kernel. // Halt the CPU. diff --git a/src/arch/x86/gdt.zig b/src/arch/x86/gdt.zig index 5974038..77e3c9d 100644 --- a/src/arch/x86/gdt.zig +++ b/src/arch/x86/gdt.zig @@ -125,9 +125,9 @@ pub fn initialize() void { loadGDT(&gdtr); // Initialize TSS. - const tss_entry = makeEntry(@ptrToInt(&tss), @sizeOf(TSS) - 1, TSS_ACCESS, PROTECTED); - gdt[TSS_DESC / @sizeOf(GDTEntry)] = tss_entry; - x86.ltr(TSS_DESC); + // const tss_entry = makeEntry(@ptrToInt(&tss), @sizeOf(TSS) - 1, TSS_ACCESS, PROTECTED); + // gdt[TSS_DESC / @sizeOf(GDTEntry)] = tss_entry; + // x86.ltr(TSS_DESC); // tty.stepOK(); } diff --git a/src/arch/x86/isr.s b/src/arch/x86/isr.s index 46ef4dd..118d4c2 100644 --- a/src/arch/x86/isr.s +++ b/src/arch/x86/isr.s @@ -1,5 +1,5 @@ // Kernel stack for interrupt handling. -KERNEL_STACK = 0x800000 +KERNEL_STACK = 0x10000 // GDT selectors. KERNEL_DS = 0x10 diff --git a/src/arch/x86/layout.zig b/src/arch/x86/layout.zig new file mode 100644 index 0000000..0f6f79b --- /dev/null +++ b/src/arch/x86/layout.zig @@ -0,0 +1,6 @@ +//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; diff --git a/src/arch/x86/linker.ld b/src/arch/x86/linker.ld index dabb1b9..f754327 100644 --- a/src/arch/x86/linker.ld +++ b/src/arch/x86/linker.ld @@ -8,7 +8,7 @@ SECTIONS { . = 0xb8000; . += 80 * 25 * 2; - /* lower half kernel */ + /* lower half kernel <1MiB */ . = 0x10000; /* ensure that the multiboot header is at the beginning */ @@ -37,32 +37,12 @@ SECTIONS { . = ALIGN(4K); } - /* NOT A GOOD IDEA TO GROUP debug_* SYMBOLS ! */ - /* .debug : */ - /* { */ - /* /1* KEEP(*(.debug_*)) *1/ */ - /* *(.debug_*) */ - /* . = ALIGN(4K); */ - /* } */ - .gdt : { *(.gdt) . = ALIGN(4K); } - .got : - { - *(.got) - . = ALIGN(4K); - } - - .got.plt : - { - *(.got.plt) - . = ALIGN(4K); - } - .bss : { *(.bss .bss.*) diff --git a/src/arch/x86/main.zig b/src/arch/x86/main.zig index 68b2c56..31ff80c 100644 --- a/src/arch/x86/main.zig +++ b/src/arch/x86/main.zig @@ -12,26 +12,11 @@ const x86 = @import("lib/index.zig"); /// x86 specific intialization /// first entry point (see linker.ld) pub fn x86_main(info: *const MultibootInfo) void { - // set up the physical frame allocator - memory.initialize(info); - - println("{}", memory.allocator.allocate(1)); - // println("{}", memory.allocator.allocate(1)); - // println("{}", memory.allocator.allocate(1)); - // println("{}", memory.allocator.allocate(1)); - - // setup memory segmentation gdt.initialize(); - - // setup interrupts idt.initialize(); + memory.initialize(info); + paging.initialize(); // enable interrupts x86.sti(); - - // set up the virtual page mapper - paging.initialize(); - - // test breakpoint - // x86.int3(); } diff --git a/src/arch/x86/memory.zig b/src/arch/x86/memory.zig index d2a5f41..0f91948 100644 --- a/src/arch/x86/memory.zig +++ b/src/arch/x86/memory.zig @@ -1,124 +1,88 @@ usingnamespace @import("kernel").multiboot; -usingnamespace @import("../../vga.zig"); +usingnamespace @import("kernel").vga; const assert = @import("std").debug.assert; -const std = @import("std"); -pub var allocator: bumpAllocator = undefined; +var stack: [*]usize = undefined; // Stack of free physical page. +var stack_index: usize = 0; // Index into the stack. +// Boundaries of the frame stack. +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 { + return (address + PAGE_SIZE - 1) & (~PAGE_SIZE +% 1); +} + +//// +// Return the amount of variable elements (in bytes). +// +pub fn available() usize { + return stack_index * PAGE_SIZE; +} + +//// +// Request a free physical page and return its address. +// +pub fn allocate() ?usize { + if (available() == 0) { + println("out of memory"); + return null; + } + + stack_index -= 1; + return stack[stack_index]; +} + +//// +// Free a previously allocated physical page. +// +// Arguments: +// address: Address of the page to be freed. +// +pub fn free(address: usize) void { + stack[stack_index] = address; + stack_index += 1; +} + +//// +// Scan the memory map to index all available memory. +// +// Arguments: +// info: Information structure from bootloader. +// pub fn initialize(info: *const MultibootInfo) void { + // Ensure the bootloader has given us the memory map. assert((info.flags & MULTIBOOT_INFO_MEMORY) != 0); assert((info.flags & MULTIBOOT_INFO_MEM_MAP) != 0); - format_multibootinfo(info); - allocator = bumpAllocator.new(info); -} - -fn format_multibootentry(entry: *MultibootMMapEntry) void { - if (entry.type == MULTIBOOT_MEMORY_AVAILABLE) { - print(" AVAILABLE: "); - } else { - print("NOT AVAILABLE: "); - } - print("{x} ", entry.addr); - if (entry.len / (1024 * 1024) > 0) { - println("({} MB)", entry.len / (1024 * 1024)); - } else { - println("({} kB)", entry.len / (1024)); - } -} - -fn format_multibootinfo(info: *const MultibootInfo) void { - var cmdline_ptr = @intToPtr([*c]const u8, info.cmdline); - var cmdline = @ptrCast([*c]const u8, cmdline_ptr); - // var cmdline = std.cstr.toSliceConst(info.cmdline); - println("flags: {b}", info.flags); - println("lower: {x}", info.mem_lower); - println("upper: {x}", info.mem_upper); - println("mmap_l: {}", info.mmap_length); - println("mmap_a: {x}", info.mmap_addr); - println("cmdline: {x}", cmdline_ptr); - println("cmdline: {}", cmdline); + // 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); var map: usize = info.mmap_addr; while (map < info.mmap_addr + info.mmap_length) { var entry = @intToPtr(*MultibootMMapEntry, map); - format_multibootentry(entry); + + // Calculate the start and end of this memory area. + var start = @truncate(usize, entry.addr); + var end = @truncate(usize, start + entry.len); + // Anything that comes before the end of the stack of free pages is reserved. + start = if (start >= stack_end) start else stack_end; + + // Flag all the pages in this memory area as free. + if (entry.type == 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); } - -// returns each available physical frame one by one in order -pub const bumpAllocator = struct { - next_free_frame: PhysFrame, - current_area: ?*MultibootMMapEntry, - info: *const MultibootInfo, - - pub fn new(info: *const MultibootInfo) bumpAllocator { - const first_area = @intToPtr(*MultibootMMapEntry, info.mmap_addr); - var allocato = bumpAllocator{ - .current_area = first_area, - .next_free_frame = PhysFrame.from_addr(@intCast(u32, first_area.addr)), - .info = info, - }; - return allocato; - } - - pub fn allocate(self: var, count: u32) ?PhysFrame { - if (count == 0) { - return null; - } - if (self.current_area == null) { - return null; - } - if (self.current_area.?.type != MULTIBOOT_MEMORY_AVAILABLE) { - self.choose_next_area(); - return self.allocate(count); - } - // <4MB identity mapped kernel, lazy trick - if (PhysFrame.start_addr(self.next_free_frame) < 0x400000) { - self.next_free_frame.number += 1; - return self.allocate(count); - } - - const start_frame = self.next_free_frame; - const end_frame = self.next_free_frame.add(count - 1); - const current_area_last_frame = PhysFrame.from_addr(@intCast(u32, self.current_area.?.addr + self.current_area.?.len)); - if (end_frame.number > current_area_last_frame.number) { - self.choose_next_area(); - return self.allocate(count); - } - self.next_free_frame.number += count; - return start_frame; - } - - pub fn choose_next_area(self: var) void { - println("choosing next area"); - const current = self.current_area.?; - var next_area = @ptrToInt(current) + current.size + @sizeOf(@typeOf(current)); - if (next_area >= self.info.mmap_addr + self.info.mmap_length) { - self.current_area = null; - } else { - self.current_area = @intToPtr(*MultibootMMapEntry, next_area); - format_multibootentry(self.current_area.?); - self.next_free_frame = PhysFrame.from_addr(@intCast(u32, self.current_area.?.addr)); - } - } -}; - -pub const PAGE_SIZE = 4096; - -pub const PhysFrame = struct { - number: u32, - - pub fn from_addr(addr: u32) PhysFrame { - return PhysFrame{ .number = @divTrunc(addr, PAGE_SIZE) }; - } - - pub fn add(self: PhysFrame, count: u32) PhysFrame { - return PhysFrame{ .number = self.number + count }; - } - - pub fn start_addr(self: PhysFrame) u32 { - return (self.number * PAGE_SIZE); - } -}; diff --git a/src/arch/x86/paging.s b/src/arch/x86/paging.s index c164a28..47c94a9 100644 --- a/src/arch/x86/paging.s +++ b/src/arch/x86/paging.s @@ -11,13 +11,15 @@ setupPaging: mov +4(%esp), %eax // Fetch the phys_pd parameter. mov %eax, %cr3 // Point CR3 to the page directory. - // Enable Page Size Extension and Page Global. + // Enable Page Size Extension mov %cr4, %eax - or $0b00010000, %eax + or $0x10, %eax mov %eax, %cr4 // Enable Paging. mov %cr0, %eax + mov $1, %eax + or $(1 << 0), %eax or $(1 << 31), %eax mov %eax, %cr0 diff --git a/src/arch/x86/paging.zig b/src/arch/x86/paging.zig index 545456f..1c89696 100644 --- a/src/arch/x86/paging.zig +++ b/src/arch/x86/paging.zig @@ -1,89 +1,102 @@ const x86 = @import("lib/index.zig"); -const allocator = @import("memory.zig").allocator; +const memory = @import("memory.zig"); +const interrupt = @import("interrupt.zig"); const assert = @import("std").debug.assert; const println = @import("../../vga.zig").println; extern fn setupPaging(phys_pd: usize) void; -pub var mapper: Mapper = undefined; +const PageEntry = usize; pub const PAGE_SIZE = 4096; +pub const PT = @intToPtr([*]PageEntry, 0xFFC00000); +pub const PD = @intToPtr([*]PageEntry, 0xFFFFF000); +const PRESENT = 0x1; +const WRITE = 0x2; +const USER = 0x4; +const WRITE_THRU = 0x8; +const NOCACHE = 0x10; +const ACCESSED = 0x20; +const HUGE = 0x80; + +pub var pageDirectory: [1024]PageEntry align(4096) linksection(".bss") = [_]PageEntry{0} ** 1024; + +fn pageFault() void { + println("pagefault"); + while (true) { + asm volatile ("hlt"); + } +} + +fn pageBase(addr: usize) usize { + return addr & (~PAGE_SIZE +% 1); +} +fn pde(addr: usize) *PageEntry { + return &PD[addr >> 22]; +} +fn pte(addr: usize) *PageEntry { + return &PT[addr >> 12]; +} + +// virtual to physical +pub fn translate(virt: usize) ?usize { + if (pde(virt).* == 0) return null; + return pageBase(pte(virt).*); +} + +pub fn unmap(virt: usize) void { + var pte = pte(virt); + if (pte.* == 0) { + println("can't unmap 0x{x}, map is empty.", addr); + return; + } + pte.* = 0; + memory.free(translate(virt)); +} + +pub fn mmap(virt: usize, phys: ?usize) void { + var pde: *PageEntry = &PD[virt >> 22]; + if (pde.* == 0) { + pde.* = memory.allocate() | WRITE | PRESENT; + } + var pte: *PageEntry = &PT[virt >> 12]; + pte.* = if (phys) |p| p else allocate() | PRESENT; +} + +pub fn addrspace() void { + var i: usize = 1; + i = 0; + while (i < 1024) : (i += 1) { + if (PD[i] == 0) { + continue; + } + 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); + } + } + } +} pub fn initialize() void { - const empty_page = PageDirectoryEntry{}; - // var p2 = allocator.allocate(1); - var p2 = [_]PageDirectoryEntry{empty_page} ** 1024; - // var p2 = [_]u32{0} ** 1024; - + var p2 = pageDirectory[0..]; // identity map 0 -> 4MB - p2[0].pageTable = 0x0; - p2[0].present = true; - p2[0].read_write = true; - p2[0].huge = true; - // p2[0] = @bitReverse(u32, 0b10000011); - println("p2[0] {b}", p2[0]); - println("p2[0] {b}", @bitCast(u32, p2[0])); - // x86.hang(); - // paging.s - // setupPaging(@ptrToInt(&p2)); + p2[0] = 0x000000 | PRESENT | WRITE | HUGE; + p2[1023] = @ptrToInt(&p2[0]) | PRESENT | WRITE; - // mapper = Mapper{ - // .p2 = p2, - // }; - const addr = mapper.translate(0xfffff000); -} - -const builtin = @import("builtin"); -const Mapper = struct { - p2: PageDirectory, - - // virt to phys - pub fn translate(self: Mapper, virt: u32) ?u32 { - const map = @bitCast(VirtAddr, virt); - println("{}", builtin.endian); - println("virt {x} -> {}-{}-{x}", virt, map.page_directory, map.page_table, map.offset); - return null; - } -}; - -pub const VirtAddr = packed struct { - page_directory: u10, - page_table: u10, - offset: u12, -}; - -pub const PageDirectoryEntry = packed struct { - pageTable: u20 = 0, - available: u3 = 0, - ignored: bool = false, - huge: bool = false, - zero: bool = false, - accessed: bool = false, - cache_disabled: bool = false, - write_thru: bool = false, - supervisor: bool = false, - read_write: bool = false, - present: bool = false, -}; - -pub const PageTableEntry = packed struct { - addr: u20 = 0, - available: u3 = 0, - global: bool = false, - zero: bool = false, - dirty: bool = false, - accessed: bool = false, - cache_disabled: bool = false, - write_thru: bool = false, - supervisor: bool = false, - read_write: bool = false, - present: bool = false, -}; - -pub const PageTable = [1024]PageTableEntry; -pub const PageDirectory = [1024]PageDirectoryEntry; - -comptime { - assert(@sizeOf(PageDirectoryEntry) == 4); //32 bits - assert(@sizeOf(PageTableEntry) == 4); //32 bits + assert(memory.stack_end < 0x400000); + // const first: *u32 = @ptrCast(*u32, p2); + // println("p2[0] {b}", first.*); + + interrupt.register(14, pageFault); + setupPaging(@ptrToInt(&pageDirectory[0])); + + addrspace(); + // const addr = mapper.translate(0xfffff000); } diff --git a/src/console.zig b/src/console.zig index 94a7827..21d24aa 100644 --- a/src/console.zig +++ b/src/console.zig @@ -40,7 +40,6 @@ pub fn keypress(char: u8) void { } pub fn initialize() void { - // vga.clear(); interrupt.registerIRQ(1, ps2.keyboard_handler); vga.writeString("> "); } diff --git a/src/main.zig b/src/main.zig index 5f02270..61f9b16 100644 --- a/src/main.zig +++ b/src/main.zig @@ -8,9 +8,11 @@ const assert = @import("std").debug.assert; // arch independant initialization export fn kmain(magic: u32, info: *const MultibootInfo) noreturn { + clear(); assert(magic == MULTIBOOT_BOOTLOADER_MAGIC); println("--- x86 initialization ---"); + x86.x86_main(info); // pagefault_test(0xfeffc000); diff --git a/src/memory.zig b/src/memory.zig new file mode 100644 index 0000000..e992f6d --- /dev/null +++ b/src/memory.zig @@ -0,0 +1,8 @@ +const paging = @import("arch/x86/paging.zig"); + +pub fn alloc(size: usize) usize { + +} + +pub fn free() { +} diff --git a/src/multiboot.zig b/src/multiboot.zig index 2f217a3..e049ed5 100644 --- a/src/multiboot.zig +++ b/src/multiboot.zig @@ -81,8 +81,12 @@ pub const MultibootInfo = packed struct { // Return the ending address of the last module. // pub fn lastModuleEnd(self: *const MultibootInfo) usize { - const mods = @intToPtr([*]MultibootModule, self.mods_addr); - return mods[self.mods_count - 1].mod_end; + if (self.mods_count > 0) { + const mods = @intToPtr([*]MultibootModule, self.mods_addr); + return mods[self.mods_count - 1].mod_end; + } else { + return self.mods_addr; + } } //// diff --git a/src/pci.zig b/src/pci.zig index 624e0f0..e77c689 100644 --- a/src/pci.zig +++ b/src/pci.zig @@ -17,31 +17,28 @@ pub const PciDevice = struct { bus: u8, slot: u5, function: u3, - device: u16, - vendor: u16, - class: u8, - subclass: u8, - header_type: u8, + device: u16 = undefined, + vendor: u16 = undefined, + class: u8 = undefined, + subclass: u8 = undefined, + header_type: u8 = undefined, + driver: ?Driver = null, pub fn init(bus: u8, slot: u5, function: u3) ?PciDevice { - var pcidevice = PciDevice{ + var dev = PciDevice{ .bus = bus, .slot = slot, .function = function, - .device = undefined, - .class = undefined, - .subclass = undefined, - .header_type = undefined, - .vendor = undefined, }; - pcidevice.vendor = pci_config_read_word(pcidevice, 0); - if (pcidevice.vendor == 0xffff) + dev.vendor = dev.pci_config_read_word(0); + if (dev.vendor == 0xffff) return null; - pcidevice.device = pci_config_read_word(pcidevice, 2); - pcidevice.subclass = pci_config_read_byte(pcidevice, 10); - pcidevice.class = pci_config_read_byte(pcidevice, 11); - pcidevice.header_type = pci_config_read_byte(pcidevice, 14); - return (pcidevice); + dev.device = dev.pci_config_read_word(2); + dev.subclass = dev.pci_config_read_byte(10); + dev.class = dev.pci_config_read_byte(11); + dev.header_type = dev.pci_config_read_byte(14); + dev.driver = dev.get_driver(); + return (dev); } pub fn address(self: PciDevice, offset: u8) u32 { @@ -57,7 +54,11 @@ pub const PciDevice = struct { } pub fn format(self: PciDevice) void { - println("{}:{}.{} {x},{x}: {x} {x}", self.bus, self.slot, self.function, self.class, self.subclass, self.vendor, self.device); + print("{}:{}.{}", self.bus, self.slot, self.function); + print(" {x},{x}: {x} {x}", self.class, self.subclass, self.vendor, self.device); + if (self.driver) |d| + print(" {}", d.name); + println(""); } pub fn access(self: PciDevice, offset: u8) void { @@ -78,13 +79,33 @@ pub const PciDevice = struct { self.access(offset); return (arch.inl(PCI_CONFIG_DATA)); } + + pub fn get_driver(self: PciDevice) ?Driver { + var i: usize = 0; + while (i < Drivers.len) : (i += 1) { + var driver = Drivers[i]; + if (self.class == driver.class and self.subclass == driver.subclass and (driver.vendor == null or self.vendor == driver.vendor.?)) { + return driver; + } + } + return null; + } }; +const Driver = struct { + name: [*]u8, + class: u8, + subclass: u8, + vendor: ?u16 = null, +}; + +const name = "virtio-blk"; +pub var Drivers: [1]Driver = [_]Driver{Driver{ .name = &name, .class = 0x1, .subclass = 0x0, .vendor = 0x1af4 }}; + pub fn lspci() void { var slot: u5 = 0; while (true) { if (PciDevice.init(0, slot, 0)) |device| { - device.format(); var function: u3 = 0; while (true) { if (PciDevice.init(0, slot, function)) |vf| diff --git a/src/vga.zig b/src/vga.zig index 7b90ede..c516b53 100644 --- a/src/vga.zig +++ b/src/vga.zig @@ -63,6 +63,9 @@ pub fn print(comptime format: []const u8, args: ...) void { pub fn println(comptime format: []const u8, args: ...) void { var a = std.fmt.format({}, Errors, printCallback, format ++ "\n", args); } +pub fn clear() void { + vga.clear(); +} fn printCallback(context: void, string: []const u8) Errors!void { vga.writeString(string);