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