diff --git a/system/init/arch/vm.zig b/system/init/arch/vm.zig new file mode 100644 index 0000000..977478e --- /dev/null +++ b/system/init/arch/vm.zig @@ -0,0 +1,9 @@ +const std = @import("std"); +const target = @import("builtin").target; + +pub const arch = switch (target.cpu.arch) { + .x86_64 => @import("x86_64/vm.zig"), + else => { + @compileError("unsupported architecture"); + }, +}; diff --git a/system/init/arch/x86_64/vm.zig b/system/init/arch/x86_64/vm.zig new file mode 100644 index 0000000..4fe9740 --- /dev/null +++ b/system/init/arch/x86_64/vm.zig @@ -0,0 +1,182 @@ +const std = @import("std"); +const kernel = @import("../../kernel.zig"); + +const MapError = error{ + MemoryAlreadyInUse, + MemoryNotInUse, + OutOfMemory, +}; + +const PhysFrame = struct { + address: u64, + + pub fn virtualAddress(self: *const PhysFrame, base: usize) usize { + return base + self.address; + } + + pub fn virtualPointer(self: *const PhysFrame, comptime T: type, base: usize) *T { + return @ptrFromInt(self.virtualAddress(base)); + } +}; + +const PageTableEntry = packed struct { + present: u1, + read_write: u1, + user: u1, + write_through: u1, + cache_disabled: u1, + accessed: u1, + reserved: u1, + larger_pages: u1, + global: u1, + available: u3, + address: u48, + available2: u3, + no_execute: u1, + + pub fn setAddress(self: *PageTableEntry, address: u64) void { + self.address = @intCast(address >> 12); + } + + pub fn getAddress(self: *PageTableEntry) u64 { + return self.address << 12; + } + + pub fn clear(self: *PageTableEntry) void { + self.* = std.mem.zeroes(PageTableEntry); + } +}; + +const PageDirectory = struct { + entries: [512]PageTableEntry, +}; + +pub const Flags = enum(u32) { + None = 0, + ReadWrite = 1, + User = 2, + NoExecute = 4, + WriteThrough = 8, + CacheDisable = 16, + Global = 32, +}; + +const PageTableIndexes = struct { + level4: u24, + level3: u24, + level2: u24, + level1: u24, +}; + +pub const MemoryMapper = struct { + cr3: PhysFrame, + directory: *PageDirectory, + base: u64, + + pub fn create(frame: PhysFrame, base: usize) MemoryMapper { + return .{ .cr3 = frame, .directory = frame.virtualPointer(PageDirectory, base), .base = base }; + } +}; + +fn calculatePageTableIndexes(address: usize) PageTableIndexes { + return .{ .level4 = @intCast((address >> 39) & 0o777), .level3 = @intCast((address >> 30) & 0o777), .level2 = @intCast((address >> 21) & 0o777), .level1 = @intCast((address >> 12) & 0o777) }; +} + +fn hasFlag(flags: u32, flag: Flags) u1 { + return switch ((flags & @intFromEnum(flag)) > 0) { + true => 1, + false => 0, + }; +} + +fn updatePageTableEntry(entry: *PageTableEntry, phys: PhysFrame, flags: u32) void { + entry.clear(); + entry.present = 1; + entry.read_write = hasFlag(flags, Flags.ReadWrite); + entry.user = hasFlag(flags, Flags.User); + entry.write_through = hasFlag(flags, Flags.WriteThrough); + entry.cache_disabled = hasFlag(flags, Flags.CacheDisable); + entry.no_execute = hasFlag(flags, Flags.NoExecute); + entry.global = hasFlag(flags, Flags.Global); + entry.setAddress(phys.address); +} + +fn setUpParentPageTableEntry(mapper: *const MemoryMapper, pte: *PageTableEntry, flags: u32) !void { + if (pte.present == 0) { + pte.clear(); + const frame = PhysFrame{ .address = try kernel.allocFrame() }; + pte.present = 1; + pte.setAddress(frame.address); + getTable(pte, mapper.base).* = std.mem.zeroes(PageDirectory); + } + if (hasFlag(flags, Flags.ReadWrite) == 1) pte.read_write = 1; + if (hasFlag(flags, Flags.User) == 1) pte.user = 1; +} + +fn getTable(mapper: *const MemoryMapper, pte: *PageTableEntry) *PageDirectory { + const frame = PhysFrame{ .address = pte.getAddress() }; + return frame.virtualPointer(PageDirectory, mapper.base); +} + +pub fn map(mapper: *const MemoryMapper, virt_address: u64, phys: PhysFrame, flags: u32) !void { + const indexes = calculatePageTableIndexes(virt_address); + const l4 = &mapper.directory.entries[indexes.level4]; + try setUpParentPageTableEntry(mapper, l4, flags); + + const l3 = &getTable(mapper, l4).entries[indexes.level3]; + if (l3.larger_pages == 1) return error.MemoryAlreadyInUse; + try setUpParentPageTableEntry(mapper, l3, flags); + + const l2 = &getTable(mapper, l3).entries[indexes.level2]; + if (l2.larger_pages == 1) return error.MemoryAlreadyInUse; + try setUpParentPageTableEntry(mapper, l2, flags); + + const l1 = &getTable(mapper, l2).entries[indexes.level1]; + if (l1.present == 1) return error.MemoryAlreadyInUse; + updatePageTableEntry(l1, phys, flags); +} + +pub fn remap(mapper: *const MemoryMapper, virt_address: u64, phys: ?PhysFrame, flags: u32) !PhysFrame { + const entry = getEntry(mapper, virt_address) orelse return error.MemoryNotInUse; + const old_frame = PhysFrame{ .address = entry.getAddress() }; + const frame = phys orelse old_frame; + + updatePageTableEntry(entry, frame, flags); + + return old_frame; +} + +pub fn unmap(mapper: *const MemoryMapper, virt_address: u64) !PhysFrame { + const entry = getEntry(mapper, virt_address) orelse return error.MemoryNotInUse; + + const frame = PhysFrame{ .address = entry.getAddress() }; + + entry.clear(); + + return frame; +} + +pub fn getEntry(mapper: MemoryMapper, virt_address: u64) ?*PageTableEntry { + const indexes = calculatePageTableIndexes(virt_address); + const l4 = &mapper.directory.entries[indexes.level4]; + if (l4.present == 0) return null; + + const l3 = &getTable(mapper, l4).entries[indexes.level3]; + if (l3.present == 0) return null; + if (l3.larger_pages == 1) return l3; + + const l2 = &getTable(mapper, l3).entries[indexes.level2]; + if (l2.present == 0) return null; + if (l2.larger_pages == 1) return l2; + + const l1 = &getTable(mapper, l2).entries[indexes.level1]; + if (l1.present == 0) return null; + + return l1; +} + +pub fn getPhysical(mapper: MemoryMapper, virt_address: u64) ?PhysFrame { + const entry = getEntry(mapper, virt_address) orelse return null; + + return PhysFrame{ .address = entry.getAddress() }; +} diff --git a/system/init/main.zig b/system/init/main.zig index 449e450..350a776 100644 --- a/system/init/main.zig +++ b/system/init/main.zig @@ -1,8 +1,14 @@ const kernel = @import("kernel.zig"); +const vm = @import("arch/vm.zig").arch; + +export fn _start(base: u64, address: u64) callconv(.C) noreturn { + const mapper = vm.MemoryMapper.create(.{ .address = address }, base); -export fn _start(base: u64) callconv(.C) noreturn { kernel.print(base); - kernel.print(kernel.getPriority()); + kernel.print(address); + kernel.print(@intFromPtr(mapper.directory)); + + vm.map(&mapper, 0x6000000, kernel.allocFrame(), @intFromEnum(vm.Flags.ReadWrite) | @intFromEnum(vm.Flags.User)); var counter: u64 = 0;