diff --git a/src/kernel/arch/x86/paging.zig b/src/kernel/arch/x86/paging.zig index 030c287..574220b 100644 --- a/src/kernel/arch/x86/paging.zig +++ b/src/kernel/arch/x86/paging.zig @@ -8,6 +8,7 @@ const isr = @import("isr.zig"); const MemProfile = @import("../../mem.zig").MemProfile; const tty = @import("../../tty.zig"); const log = @import("../../log.zig"); +const mem = @import("../../mem.zig"); const options = @import("build_options"); const testing = std.testing; @@ -119,31 +120,6 @@ const TENTRY_GLOBAL: u32 = 0x100; const TENTRY_AVAILABLE: u32 = 0xE00; const TENTRY_PAGE_ADDR: u32 = 0xFFFFF000; -/// The kernel's virtual address offset. It's assigned in the init function and the virtToPhys test. -/// We can't just use KERNEL_ADDR_OFFSET since using externs in the virtToPhys test is broken in -/// release-safe. This is a workaround until that is fixed. -var ADDR_OFFSET: usize = undefined; -extern var KERNEL_ADDR_OFFSET: *u32; - -/// -/// Convert a virtual address to its physical counterpart by subtracting the kernel virtual offset from the virtual address. -/// -/// Arguments: -/// IN virt: var - The virtual address to covert. Either an integer or pointer. -/// -/// Return: @typeOf(virt) -/// The physical address. -/// -inline fn virtToPhys(virt: var) @typeOf(virt) { - const offset = ADDR_OFFSET; - const T = @typeOf(virt); - return switch (@typeId(T)) { - .Pointer => @intToPtr(T, @ptrToInt(virt) - offset), - .Int => virt - offset, - else => @compileError("Only pointers and integers are supported"), - }; -} - /// /// Convert a virtual address to an index within an array of directory entries. /// @@ -227,7 +203,7 @@ fn mapDirEntry(dir: *Directory, virt_start: usize, virt_end: usize, phys_start: // Create a table and put the physical address in the dir entry table = &(try allocator.alignedAlloc(Table, @truncate(u29, PAGE_SIZE_4KB), 1))[0]; @memset(@ptrCast([*]u8, table), 0, @sizeOf(Table)); - const table_phys_addr = @ptrToInt(virtToPhys(table)); + const table_phys_addr = @ptrToInt(mem.virtToPhys(table)); dir_entry.* |= @intCast(u32, DENTRY_PAGE_ADDR & table_phys_addr); dir.tables[entry] = table; } @@ -316,7 +292,6 @@ fn pageFault(state: *arch.InterruptContext) void { /// pub fn init(mem_profile: *const MemProfile, allocator: *std.mem.Allocator) void { log.logInfo("Init paging\n"); - ADDR_OFFSET = @ptrToInt(&KERNEL_ADDR_OFFSET); // Calculate start and end of mapping const v_start = std.mem.alignBackward(@ptrToInt(mem_profile.vaddr_start), PAGE_SIZE_4KB); const v_end = std.mem.alignForward(@ptrToInt(mem_profile.vaddr_end) + mem_profile.fixed_alloc_size, PAGE_SIZE_4KB); @@ -336,14 +311,14 @@ pub fn init(mem_profile: *const MemProfile, allocator: *std.mem.Allocator) void const tty_addr = tty.getVideoBufferAddress(); // If the previous mapping space didn't cover the tty buffer, do so now if (v_start > tty_addr or v_end <= tty_addr) { - const tty_phys = virtToPhys(tty_addr); + const tty_phys = mem.virtToPhys(tty_addr); const tty_buff_size = 32 * 1024; mapDir(kernel_directory, tty_addr, tty_addr + tty_buff_size, tty_phys, tty_phys + tty_buff_size, allocator) catch |e| { panic(@errorReturnTrace(), "Failed to map vga buffer in kernel directory: {}\n", e); }; } - const dir_physaddr = @ptrToInt(virtToPhys(kernel_directory)); + const dir_physaddr = @ptrToInt(mem.virtToPhys(kernel_directory)); asm volatile ("mov %[addr], %%cr3" : : [addr] "{eax}" (dir_physaddr) @@ -388,14 +363,6 @@ fn checkTableEntry(entry: TableEntry, page_phys: usize) void { expectEqual(entry & TENTRY_PAGE_ADDR, @intCast(u32, page_phys)); } -test "virtToPhys" { - ADDR_OFFSET = 0xC0000000; - const offset: usize = ADDR_OFFSET; - expectEqual(virtToPhys(offset + 0), 0); - expectEqual(virtToPhys(offset + 123), 123); - expectEqual(virtToPhys(@intToPtr(*usize, offset + 123)), @intToPtr(*usize, 123)); -} - test "virtToDirEntryIdx" { expectEqual(virtToDirEntryIdx(0), 0); expectEqual(virtToDirEntryIdx(123), 0); diff --git a/src/kernel/mem.zig b/src/kernel/mem.zig index ccc1504..a749b45 100644 --- a/src/kernel/mem.zig +++ b/src/kernel/mem.zig @@ -1,4 +1,6 @@ const multiboot = @import("multiboot.zig"); +const std = @import("std"); +const expectEqual = std.testing.expectEqual; pub const MemProfile = struct { vaddr_end: [*]u8, @@ -9,16 +11,40 @@ pub const MemProfile = struct { fixed_alloc_size: u32, }; -// The virtual/physical start/end of the kernel code +/// The virtual end of the kernel code extern var KERNEL_VADDR_END: *u32; + +/// The virtual start of the kernel code extern var KERNEL_VADDR_START: *u32; + +/// The physical end of the kernel code extern var KERNEL_PHYSADDR_END: *u32; + +/// The physical start of the kernel code extern var KERNEL_PHYSADDR_START: *u32; -// The size of the fixed allocator used before the heap is set up. Set to 1MiB. +/// The boot-time offset that the virtual addresses are from the physical addresses +extern var KERNEL_ADDR_OFFSET: *u32; + +/// The size of the fixed allocator used before the heap is set up. Set to 1MiB. const FIXED_ALLOC_SIZE = 1024 * 1024; +/// The kernel's virtual address offset. It's assigned in the init function and this file's tests. +/// We can't just use KERNEL_ADDR_OFFSET since using externs in the virtToPhys test is broken in +/// release-safe. This is a workaround until that is fixed. +var ADDR_OFFSET: usize = undefined; + +/// +/// Initialise the system's memory profile based on linker symbols and the multiboot info struct. +/// +/// Arguments: +/// IN mb_info: *multiboot.multiboot_info_t - The multiboot info passed by the bootloader. +/// +/// Return: MemProfile +/// The memory profile constructed from the exported linker symbols and the relevant multiboot info. +/// pub fn init(mb_info: *multiboot.multiboot_info_t) MemProfile { + ADDR_OFFSET = @ptrToInt(&KERNEL_ADDR_OFFSET); return MemProfile{ .vaddr_end = @ptrCast([*]u8, &KERNEL_VADDR_END), .vaddr_start = @ptrCast([*]u8, &KERNEL_VADDR_START), @@ -29,3 +55,55 @@ pub fn init(mb_info: *multiboot.multiboot_info_t) MemProfile { .fixed_alloc_size = FIXED_ALLOC_SIZE, }; } + +/// +/// Convert a virtual address to its physical counterpart by subtracting the kernel virtual offset from the virtual address. +/// +/// Arguments: +/// IN virt: var - The virtual address to covert. Either an integer or pointer. +/// +/// Return: @typeOf(virt) +/// The physical address. +/// +pub fn virtToPhys(virt: var) @typeOf(virt) { + const T = @typeOf(virt); + return switch (@typeId(T)) { + .Pointer => @intToPtr(T, @ptrToInt(virt) - ADDR_OFFSET), + .Int => virt - ADDR_OFFSET, + else => @compileError("Only pointers and integers are supported"), + }; +} + +/// +/// Convert a physical address to its virtual counterpart by adding the kernel virtual offset to the physical address. +/// +/// Arguments: +/// IN phys: var - The physical address to covert. Either an integer or pointer. +/// +/// Return: @typeOf(virt) +/// The virtual address. +/// +pub fn physToVirt(phys: var) @typeOf(phys) { + const T = @typeOf(phys); + return switch (@typeId(T)) { + .Pointer => @intToPtr(T, @ptrToInt(phys) + ADDR_OFFSET), + .Int => phys + ADDR_OFFSET, + else => @compileError("Only pointers and integers are supported"), + }; +} + +test "physToVirt" { + ADDR_OFFSET = 0xC0000000; + const offset: usize = ADDR_OFFSET; + expectEqual(physToVirt(usize(0)), offset + 0); + expectEqual(physToVirt(usize(123)), offset + 123); + expectEqual(@ptrToInt(physToVirt(@intToPtr(*usize, 123))), offset + 123); +} + +test "virtToPhys" { + ADDR_OFFSET = 0xC0000000; + const offset: usize = ADDR_OFFSET; + expectEqual(virtToPhys(offset + 0), 0); + expectEqual(virtToPhys(offset + 123), 123); + expectEqual(@ptrToInt(virtToPhys(@intToPtr(*usize, offset + 123))), 123); +}