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