Microkernel thing OS experiment (Zig ⚡)
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}