Microkernel thing OS experiment (Zig ⚡)
1const common = @import("common"); 2const arch = @import("../root.zig"); 3const std = @import("std"); 4const physToVirt = common.mm.physToHHDM; 5const Perms = common.mm.paging.Perms; 6 7pub const page_sizes = [_]usize{ 8 0x1000, // 4K 9 0x200000, // 2M 10 0x40000000, // 1G 11 0x8000000000, // 512G 12 0x1000000000000, // 256T 13}; 14 15pub const PageTable = extern struct { 16 entries: [512]Entry, 17 18 pub const Entry = packed struct { 19 present: bool, 20 writable: bool, 21 user_accessible: bool, 22 write_through: bool, 23 disable_cache: bool, 24 accessed: bool, 25 dirty: bool, 26 huge: bool, 27 global: bool, 28 idk: u3, 29 phys_addr: u40, 30 idk2: u11, 31 nx: bool, 32 33 const Self = @This(); 34 35 pub fn getAddr(self: *const Self) u64 { 36 return self.phys_addr << 12; 37 } 38 39 pub fn setAddr(self: *Self, phys_addr: u64) void { 40 const addr = phys_addr >> 12; 41 self.phys_addr = @truncate(addr); 42 } 43 }; 44}; 45 46fn extract_index_from_vaddr(vaddr: u64, level: u6) u9 { 47 const shamt = 12 + level * 9; 48 return @truncate(vaddr >> shamt); 49} 50 51pub const TypedPTE = union(common.mm.paging.PTEType) { 52 Mapping: MappingHandle, 53 Table: TableHandle, 54 Empty, 55 56 const Self = @This(); 57 58 pub fn decode(pte: *PageTable.Entry, level: u3) Self { 59 if (!pte.present) { 60 return .Empty; 61 } 62 if (!pte.huge and level != 0) { 63 return .{ .Table = decode_table(pte, level) }; 64 } 65 return .{ .Mapping = decode_mapping(pte, level) }; 66 } 67 68 pub fn decode_table(pte: *PageTable.Entry, level: u3) TableHandle { 69 return .{ 70 .phys_addr = pte.getAddr(), 71 .level = level, 72 .underlying = pte, 73 .perms = .{ 74 .writable = pte.writable, 75 .executable = !pte.nx, 76 .userspace_accessible = pte.user_accessible, 77 }, 78 }; 79 } 80 81 pub fn decode_mapping(pte: *PageTable.Entry, level: u3) MappingHandle { 82 return .{ 83 .phys_addr = pte.getAddr(), 84 .level = level, 85 // TODO: memory types 86 .memory_type = null, 87 .underlying = pte, 88 .perms = .{ 89 .writable = pte.writable, 90 .executable = !pte.nx, 91 .userspace_accessible = pte.user_accessible, 92 }, 93 }; 94 } 95}; 96 97pub const MappingHandle = struct { 98 phys_addr: usize, 99 level: u3, 100 memory_type: ?MemoryType, 101 perms: Perms, 102 underlying: *PageTable.Entry, 103}; 104 105pub const TableHandle = struct { 106 phys_addr: usize, 107 level: u3, 108 perms: Perms, 109 underlying: ?*PageTable.Entry, 110 111 const Self = @This(); 112 113 // Get the child entries of this page table 114 pub fn get_children(self: *const Self) []PageTable.Entry { 115 const page_table = physToVirt(*PageTable, self.phys_addr); 116 return page_table.entries[0..]; 117 } 118 119 // Get children from the position holding the table and on 120 pub fn skip_to(self: *const Self, vaddr: usize) []PageTable.Entry { 121 return self.get_children()[extract_index_from_vaddr(vaddr, self.level - 1)..]; 122 } 123 124 // Decode child table given an entry 125 pub fn decode_child(self: *const Self, pte: *PageTable.Entry) TypedPTE { 126 return TypedPTE.decode(pte, self.level - 1); 127 } 128 129 pub fn addPerms(self: *const Self, perms: Perms) void { 130 if (perms.executable) { 131 self.underlying.?.nx = false; 132 } 133 if (perms.writable) { 134 self.underlying.?.writable = true; 135 } 136 if (perms.userspace_accessible) { 137 self.underlying.?.user_accessible = true; 138 } 139 } 140 141 pub fn child_domain(self: *const Self, vaddr: usize) UntypedSlice { 142 return domain(vaddr, self.level - 1); 143 } 144 145 pub fn make_child_table(self: *const Self, pte: *PageTable.Entry, perms: Perms) !TableHandle { 146 const pmem = try make_page_table(); 147 148 const result: TableHandle = .{ 149 .phys_addr = pmem, 150 .level = self.level - 1, 151 .perms = perms, 152 .underlying = pte, 153 }; 154 pte.* = encode_table(result); 155 156 return result; 157 } 158 159 pub fn make_child_mapping( 160 self: *const Self, 161 pte: *PageTable.Entry, 162 paddr: ?usize, 163 perms: Perms, 164 memory_type: MemoryType, 165 ) !MappingHandle { 166 const page_size = page_sizes[self.level - 1]; 167 const pmem = paddr orelse try common.init_data.bootmem.allocPhys(page_size); 168 169 const result: MappingHandle = .{ 170 .level = self.level - 1, 171 .memory_type = memory_type, 172 .perms = perms, 173 .underlying = pte, 174 .phys_addr = pmem, 175 }; 176 177 pte.* = encode_mapping(result); 178 179 return result; 180 } 181}; 182 183pub fn root_table(vaddr: usize) TableHandle { 184 _ = vaddr; 185 const cr3_val = arch.registers.ControlRegisters.Cr3.read() & 0xFFFF_FFFF_FFFF_F000; 186 return .{ 187 .phys_addr = cr3_val, 188 // TODO: detect and support 5 level paging! 189 .level = 4, 190 .perms = .{ 191 .executable = true, 192 .writable = true, 193 }, 194 .underlying = null, 195 }; 196} 197 198fn encode_table(pte_handle: TableHandle) PageTable.Entry { 199 var pte = std.mem.zeroes(PageTable.Entry); 200 201 pte.setAddr(pte_handle.phys_addr); 202 pte.writable = pte_handle.perms.writable; 203 pte.user_accessible = pte_handle.perms.userspace_accessible; 204 pte.nx = !pte_handle.perms.executable; 205 pte.present = true; 206 pte.huge = false; 207 208 return pte; 209} 210 211fn encode_mapping(pte_handle: MappingHandle) PageTable.Entry { 212 var pte = std.mem.zeroes(PageTable.Entry); 213 214 pte.setAddr(pte_handle.phys_addr); 215 pte.present = true; 216 217 if (pte_handle.level != 0) { 218 pte.huge = true; 219 } 220 221 pte.writable = pte_handle.perms.writable; 222 pte.user_accessible = pte_handle.perms.userspace_accessible; 223 pte.nx = !pte_handle.perms.executable; 224 225 encode_memory_type(&pte, pte_handle); 226 227 return pte; 228} 229 230fn encode_memory_type(pte: *PageTable.Entry, pte_handle: MappingHandle) void { 231 const mt = pte_handle.memory_type orelse @panic("Unknown memory type"); 232 233 // TODO: Page Attribute Table 234 switch (mt) { 235 .MemoryWritethrough => pte.write_through = true, 236 .DeviceUncacheable => pte.disable_cache = true, 237 .MemoryWriteBack => {}, 238 else => @panic("Cannot set memory type"), 239 } 240} 241 242/// Returns physical address 243fn make_page_table() !usize { 244 const pt_phys = try common.init_data.bootmem.allocPhys(std.heap.pageSize()); 245 const pt = physToVirt([*]u8, pt_phys); 246 @memset(pt[0..std.heap.pageSize()], 0x00); 247 return pt_phys; 248} 249 250pub fn invalidate(vaddr: u64) void { 251 asm volatile ( 252 \\ invlpg (%[vaddr]) 253 : 254 : [vaddr] "r" (vaddr), 255 : .{ .memory = true }); 256} 257 258const UntypedSlice = struct { 259 len: usize, 260 ptr: usize, 261}; 262 263pub fn domain(vaddr: usize, level: u3) UntypedSlice { 264 return .{ 265 .len = page_sizes[level], 266 .ptr = vaddr & ~(page_sizes[level] - 1), 267 }; 268} 269 270pub const MemoryType = enum { 271 DeviceUncacheable, 272 DeviceWriteCombining, 273 MemoryWritethrough, 274 MemoryWriteBack, 275}; 276 277pub fn can_map_at(level: u3) bool { 278 return level < 2; 279}