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}