diff --git a/core/src/arch/x86_64/vmm.zig b/core/src/arch/x86_64/vmm.zig index 37ee1da..fddd282 100644 --- a/core/src/arch/x86_64/vmm.zig +++ b/core/src/arch/x86_64/vmm.zig @@ -2,9 +2,10 @@ const std = @import("std"); const easyboot = @cImport(@cInclude("easyboot.h")); const mmap = @import("../../mmap.zig"); const pmm = @import("../../pmm.zig"); +const platform = @import("platform.zig"); const USER_ADDRESS_RANGE_END = 0x0000_7fff_ffff_ffff; -const PHYSICAL_MAPPING_BASE = 0xffff_8000_0000_0000; +pub const PHYSICAL_MAPPING_BASE = 0xffff_8000_0000_0000; const HUGE_PAGE_SIZE = 0x200000; // 2 MiB pub const PageTableEntry = packed struct { @@ -14,7 +15,7 @@ pub const PageTableEntry = packed struct { write_through: u1, cache_disabled: u1, accessed: u1, - ignore0: u1, + reserved: u1, larger_pages: u1, global: u1, available: u3, @@ -31,7 +32,7 @@ pub const PageTableEntry = packed struct { } pub fn clear(self: *PageTableEntry) void { - self = std.mem.zeroes(@TypeOf(self)); + self.* = std.mem.zeroes(PageTableEntry); } }; @@ -39,7 +40,7 @@ pub const PageDirectory = struct { entries: [512]PageTableEntry, }; -const Flags = enum(u32) { +pub const Flags = enum(u32) { None = 0, ReadWrite = 1, User = 2, @@ -68,6 +69,7 @@ fn hasFlag(flags: u32, flag: Flags) u1 { } fn updatePageTableEntry(entry: *PageTableEntry, phys: pmm.PhysFrame, flags: u32) void { + entry.clear(); entry.present = 1; entry.read_write = hasFlag(flags, Flags.ReadWrite); entry.user = hasFlag(flags, Flags.User); @@ -80,6 +82,7 @@ fn updatePageTableEntry(entry: *PageTableEntry, phys: pmm.PhysFrame, flags: u32) fn setUpParentPageTableEntry(allocator: *pmm.FrameAllocator, pte: *PageTableEntry, flags: u32, base: usize) !void { if (pte.present == 0) { + pte.clear(); const frame = try pmm.allocFrame(allocator); pte.present = 1; pte.set_address(frame.address); @@ -107,8 +110,8 @@ pub fn map(allocator: *pmm.FrameAllocator, directory: *PageDirectory, base: usiz if (l2.larger_pages == 1) return error.MemoryAlreadyInUse; if (use_huge_pages) { - l2.larger_pages = 1; updatePageTableEntry(l2, phys, flags); + l2.larger_pages = 1; return; } @@ -119,6 +122,108 @@ pub fn map(allocator: *pmm.FrameAllocator, directory: *PageDirectory, base: usiz updatePageTableEntry(l1, phys, flags); } +pub fn getEntry(directory: *PageDirectory, base: usize, virt_address: u64) ?*PageTableEntry { + const indexes = calculatePageTableIndexes(virt_address); + const l4 = &directory.entries[indexes.level4]; + if (l4.present == 0) return null; + + const l3 = &getTable(l4, base).entries[indexes.level3]; + if (l3.present == 0) return null; + if (l3.larger_pages == 1) return l3; + + const l2 = &getTable(l3, base).entries[indexes.level2]; + if (l2.present == 0) return null; + if (l2.larger_pages == 1) return l2; + + const l1 = &getTable(l2, base).entries[indexes.level1]; + if (l1.present == 0) return null; + + return l1; +} + +pub fn copyToUser(directory: *PageDirectory, base: usize, user: usize, kernel: [*]const u8, size: usize) !void { + const remainder: usize = @rem(user, platform.PAGE_SIZE); + const user_page = user - remainder; + + var user_address = user; + var kernel_ptr = kernel; + var count = size; + + if (user_address != user_page) { + const pte = getEntry(directory, base, user_page) orelse return error.MemoryNotInUse; + const frame = pmm.PhysFrame{ .address = pte.get_address() }; + const amount: usize = @min((platform.PAGE_SIZE - remainder), count); + const virt = frame.virtualAddress(base) + remainder; + + @memcpy(@as([*]u8, @ptrFromInt(virt))[0..amount], kernel_ptr[0..amount]); + + kernel_ptr += amount; + user_address += amount; + count -= amount; + } + + while (count > 0) { + const pte = getEntry(directory, base, user_address) orelse return error.MemoryNotInUse; + const frame = pmm.PhysFrame{ .address = pte.get_address() }; + const amount: usize = @min(platform.PAGE_SIZE, count); + const virt = frame.virtualAddress(base); + + @memcpy(@as([*]u8, @ptrFromInt(virt))[0..amount], kernel_ptr[0..amount]); + + kernel_ptr += amount; + user_address += amount; + count -= amount; + } + + return; +} + +pub fn memsetUser(directory: *PageDirectory, base: usize, user: usize, elem: u8, size: usize) !void { + const remainder: usize = @rem(user, platform.PAGE_SIZE); + const user_page = user - remainder; + + var user_address = user; + var count = size; + + if (user_address != user_page) { + const pte = getEntry(directory, base, user_page) orelse return error.MemoryNotInUse; + const frame = pmm.PhysFrame{ .address = pte.get_address() }; + const amount: usize = @min((platform.PAGE_SIZE - remainder), count); + const virt = frame.virtualAddress(base) + remainder; + + @memset(@as([*]u8, @ptrFromInt(virt))[0..amount], elem); + + user_address += amount; + count -= amount; + } + + while (count > 0) { + const pte = getEntry(directory, base, user_address) orelse return error.MemoryNotInUse; + const frame = pmm.PhysFrame{ .address = pte.get_address() }; + const amount: usize = @min(platform.PAGE_SIZE, count); + const virt = frame.virtualAddress(base); + + @memset(@as([*]u8, @ptrFromInt(virt))[0..amount], elem); + + user_address += amount; + count -= amount; + } + + return; +} + +pub fn allocAndMap(allocator: *pmm.FrameAllocator, directory: *PageDirectory, base: u64, pages: usize, flags: u32) !void { + var virt = base; + var i: usize = 0; + + while (i < pages) { + const frame = try pmm.allocFrame(allocator); + try map(allocator, directory, PHYSICAL_MAPPING_BASE, virt, frame, flags, false); + virt += platform.PAGE_SIZE; + i += 1; + } +} + fn mapPhysicalMemory(allocator: *pmm.FrameAllocator, tag: *easyboot.multiboot_tag_mmap_t, directory: *PageDirectory, base: usize, flags: u32) !void { const address_space_size = mmap.getAddressSpaceSize(tag) orelse return error.InvalidMemoryMap; const address_space_pages = address_space_size / HUGE_PAGE_SIZE; @@ -195,6 +300,10 @@ pub fn readPageDirectory() *PageDirectory { return directory; } +pub fn getPhysicalPageDirectory(directory: *PageDirectory) *PageDirectory { + return @ptrFromInt(@intFromPtr(directory) - @as(usize, PHYSICAL_MAPPING_BASE)); +} + pub fn setPageDirectory(directory: *PageDirectory) void { asm volatile ("mov %[dir], %%cr3" :