Microkernel thing OS experiment (Zig ⚡)
1const arch = @import("arch"); 2const std = @import("std"); 3const TableHandle = arch.mm.paging.TableHandle; 4const MemoryType = arch.mm.paging.MemoryType; 5 6pub const Perms = struct { 7 writable: bool, 8 executable: bool, 9 userspace_accessible: bool = false, 10 11 const Self = @This(); 12 13 /// Verify that the current permissions are a superset of the provided ones 14 pub fn allows(self: Self, other: Self) bool { 15 if (!self.writable and other.writable) { 16 return false; 17 } 18 if (!self.executable and other.executable) { 19 return false; 20 } 21 if (!self.userspace_accessible and other.userspace_accessible) { 22 return false; 23 } 24 return true; 25 } 26 27 /// OR two permissions 28 pub fn addPerms(self: Self, other: Self) Self { 29 return .{ 30 .writable = self.writable or other.writable, 31 .executable = self.executable or other.executable, 32 .userspace = self.userspace_accessible or other.userspace_accessible, 33 }; 34 } 35}; 36 37pub const PTEType = enum { Mapping, Table, Empty }; 38 39pub fn mapPhys(args: struct { 40 vaddr: usize, 41 paddr: usize, 42 size: usize, 43 perms: Perms, 44 memory_type: MemoryType, 45}) !void { 46 const root = arch.mm.paging.root_table(args.vaddr); 47 var vaddr = args.vaddr; 48 var paddr = args.paddr; 49 var size = args.size; 50 try mapPageImpl(&vaddr, &paddr, &size, root, args.perms, args.memory_type); 51} 52 53pub fn map(args: struct { 54 vaddr: usize, 55 size: usize, 56 perms: Perms, 57 memory_type: MemoryType, 58}) !void { 59 const root = arch.mm.paging.root_table(args.vaddr); 60 var vaddr = args.vaddr; 61 var size = args.size; 62 try mapPageImpl(&vaddr, null, &size, root, args.perms, args.memory_type); 63} 64 65fn mapPageImpl( 66 vaddr: *usize, 67 paddr: ?*usize, 68 size: *usize, 69 table: TableHandle, 70 perms: Perms, 71 memory_type: MemoryType, 72) !void { 73 // 1. Get slice of every child from the target forwards 74 const children = table.skip_to(vaddr.*); 75 76 // 2. For each PTE, decode to the type (Mapping, Table, Empty) 77 // If there's already a mapping, we're fucked 78 // If it's a table, keep going forward till we reach Mapping or Empty, 79 // while of course ensuring permissions 80 // If it's empty, check if we reached our target level. If we didn't, 81 // then make a new child table and keep going. If it's not empty, then 82 // make the child mapping and reduce the amount of size we're targetting 83 for (children) |*child| { 84 switch (table.decode_child(child)) { 85 .Mapping => return error.AlreadyPresent, 86 .Table => |*tbl| { 87 try mapPageImpl(vaddr, paddr, size, tbl.*, perms, memory_type); 88 if (!tbl.perms.allows(perms)) { 89 tbl.addPerms(perms); 90 arch.mm.paging.invalidate(vaddr.*); 91 } 92 }, 93 .Empty => { 94 const domain = table.child_domain(vaddr.*); 95 if (domain.ptr == vaddr.* and domain.len <= size.* and arch.mm.paging.can_map_at(table.level - 1) and is_aligned(vaddr.*, paddr, table.level - 1)) { 96 // Make child mapping etc 97 _ = try table.make_child_mapping(child, if (paddr) |p| p.* else null, perms, memory_type); 98 const step = domain.len; 99 if (step >= size.*) { 100 size.* = 0; 101 return; 102 } else { 103 size.* -= step; 104 vaddr.* += step; 105 if (paddr) |p| { 106 p.* += step; 107 } 108 } 109 } else { 110 const tbl = try table.make_child_table(child, perms); 111 try mapPageImpl(vaddr, paddr, size, tbl, perms, memory_type); 112 } 113 }, 114 } 115 if (size.* == 0) return; 116 } 117} 118 119fn is_aligned(vaddr: usize, paddr: ?*usize, level: u3) bool { 120 if (!std.mem.isAligned(vaddr, arch.mm.paging.page_sizes[level])) { 121 return false; 122 } 123 124 if (paddr) |p| { 125 return std.mem.isAligned(p.*, arch.mm.paging.page_sizes[level]); 126 } 127 128 return true; 129}