lib/machine/src/instance/layout.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const os = @import("os");
3 const memory = @import("memory.zig");
4 const protection = @import("protection.zig");
5
6 const manifest = os.boot.kernel.manifest;
7
8 pub const page_bytes: usize = os.abi.boot.page_bytes;
9 pub const ram_bytes: usize = @intCast(os.abi.channel.ram_bytes);
10 pub const pml4_address: u64 = manifest.k0_v1_pml4_address;
11 pub const pdpt_address: u64 = 0x2000;
12 pub const page_directory_address: u64 = 0x3000;
13 pub const boot_frame_address: u64 = os.abi.channel.boot_frame_address;
14 pub const low_page_table_address: u64 = 0x5000;
15 pub const kernel_page_table_base: u64 = 0x6000;
16 pub const request_ring_address: u64 = os.abi.channel.request_ring_address;
17 pub const event_ring_address: u64 = os.abi.channel.event_ring_address;
18 pub const stack_base: u64 = os.boot.profile.stack_base;
19 pub const stack_top: u64 =
20 os.boot.profile.stack_top - manifest.k0_v1_entry_stack_bytes;
21 pub const stack_end: u64 = os.boot.profile.stack_top;
22 pub const code_selector: u16 = manifest.k0_v1_code_selector;
23 pub const data_selector: u16 = manifest.k0_v1_data_selector;
24 pub const control_cr0: u64 = manifest.k0_v1_cr0;
25 pub const control_cr4: u64 = manifest.k0_v1_cr4;
26 pub const control_efer: u64 = manifest.k0_v1_efer;
27 pub const large_page_bytes: usize = 2 * 1024 * 1024;
28 pub const identity_large_pages: usize = ram_bytes / large_page_bytes;
29 pub const kernel_page_table_count: usize =
30 os.boot.kernel.kernel_memory_bytes_max / large_page_bytes;
31 pub const page_table_count: usize = 4 + kernel_page_table_count;
32 pub const page_table_addresses: [page_table_count]u64 = pageTableAddresses();
33 pub const reactivation_page_count: usize =
34 3 + @as(usize, @intCast((stack_end - stack_base) / page_bytes));
35
36 const page_present: u64 = 1 << 0;
37 const page_writable: u64 = 1 << 1;
38 const page_accessed: u64 = 1 << 5;
39 const page_dirty: u64 = 1 << 6;
40 const page_large: u64 = 1 << 7;
41 const page_no_execute: u64 = 1 << 63;
42 const table_entries: usize = page_bytes / @sizeOf(u64);
43 const kernel_large_page_index: usize =
44 os.boot.kernel.physical_base / large_page_bytes;
45
46 pub const InitialState = manifest.InitialState;
47
48 pub const Error = error{RamBytesMismatch} || os.abi.ring.Error;
49
50 pub fn validateRamBytes(bytes: usize) error{RamBytesMismatch}!void {
51 if (bytes != ram_bytes) return error.RamBytesMismatch;
52 }
53
54 pub fn populate(
55 ram: []align(page_bytes) u8,
56 image: []const u8,
57 execution: manifest.View,
58 plan: *const protection.Plan,
59 frame: os.abi.BootFrame,
60 encoded_frame: *const os.abi.BootWire,
61 ) Error!void {
62 try validateRamBytes(ram.len);
63 @memset(ram, 0);
64 writePageTables(ram, plan);
65 loadImage(ram, image, execution);
66 const frame_offset: usize = @intCast(boot_frame_address);
67 @memcpy(ram[frame_offset..][0..encoded_frame.len], encoded_frame);
68 try os.abi.RequestRing.initialize(requestRing(ram), frame.fence);
69 try os.abi.EventRing.initialize(eventRing(ram), frame.fence);
70 }
71
72 pub fn reactivate(
73 ram: []align(page_bytes) u8,
74 frame: os.abi.BootFrame,
75 encoded_frame: *const os.abi.BootWire,
76 ) Error!void {
77 try validateRamBytes(ram.len);
78 var requests: os.abi.RequestRing.Storage = undefined;
79 var events: os.abi.EventRing.Storage = undefined;
80 try os.abi.RequestRing.initialize(&requests, frame.fence);
81 try os.abi.EventRing.initialize(&events, frame.fence);
82 const boot_start: usize = @intCast(boot_frame_address);
83 const stack_start: usize = @intCast(stack_base);
84 const stack_bytes: usize = @intCast(stack_end - stack_base);
85 @memset(ram[boot_start..][0..page_bytes], 0);
86 @memset(ram[stack_start..][0..stack_bytes], 0);
87 requestRing(ram).* = requests;
88 eventRing(ram).* = events;
89 @memcpy(ram[boot_start..][0..encoded_frame.len], encoded_frame);
90 }
91
92 pub fn reactivateAccess(
93 access: memory.Access,
94 frame: os.abi.BootFrame,
95 encoded_frame: *const os.abi.BootWire,
96 ) (Error || memory.Error)!void {
97 const pages = reactivationPages();
98 try access.preparePages(&pages);
99 var requests: os.abi.RequestRing.Storage = undefined;
100 var events: os.abi.EventRing.Storage = undefined;
101 try os.abi.RequestRing.initialize(&requests, frame.fence);
102 try os.abi.EventRing.initialize(&events, frame.fence);
103 const boot_start: usize = @intCast(boot_frame_address);
104 const stack_start: usize = @intCast(stack_base);
105 const stack_bytes: usize = @intCast(stack_end - stack_base);
106 try access.fill(boot_start, page_bytes, 0);
107 try access.fill(stack_start, stack_bytes, 0);
108 (try requestRingAccess(access)).* = requests;
109 (try eventRingAccess(access)).* = events;
110 try access.write(boot_start, encoded_frame);
111 }
112
113 pub fn canonicalizePageTables(
114 ram: []u8,
115 plan: *const protection.Plan,
116 ) error{PageTableMismatch}!void {
117 try validatePageTables(ram, plan, true);
118 for (page_table_addresses) |address| zeroPage(ram, address);
119 writePageTables(ram, plan);
120 }
121
122 pub fn validateCanonicalPageTables(
123 ram: []const u8,
124 plan: *const protection.Plan,
125 ) error{PageTableMismatch}!void {
126 try validatePageTables(ram, plan, false);
127 }
128
129 pub fn validateRestorablePageTables(
130 ram: []const u8,
131 plan: *const protection.Plan,
132 ) error{PageTableMismatch}!void {
133 try validatePageTables(ram, plan, true);
134 }
135
136 pub fn requestRing(ram: []align(page_bytes) u8) *os.abi.RequestRing.Storage {
137 std.debug.assert(ram.len == ram_bytes);
138 return @ptrCast(@alignCast(ram.ptr + request_ring_address));
139 }
140
141 pub fn eventRing(ram: []align(page_bytes) u8) *os.abi.EventRing.Storage {
142 std.debug.assert(ram.len == ram_bytes);
143 return @ptrCast(@alignCast(ram.ptr + event_ring_address));
144 }
145
146 pub fn requestRingAccess(
147 access: memory.Access,
148 ) memory.Error!*os.abi.RequestRing.Storage {
149 const page_index: u16 = @intCast(request_ring_address / page_bytes);
150 return @ptrCast(try access.writablePage(page_index));
151 }
152
153 pub fn eventRingAccess(
154 access: memory.Access,
155 ) memory.Error!*os.abi.EventRing.Storage {
156 const page_index: u16 = @intCast(event_ring_address / page_bytes);
157 return @ptrCast(try access.writablePage(page_index));
158 }
159
160 pub fn firstUnmappedPagingAddress(start: u64, end: u64) ?u64 {
161 std.debug.assert(start < end);
162 std.debug.assert(end <= ram_bytes);
163 for (page_table_addresses) |address| {
164 const table_end = address + page_bytes;
165 if (start < table_end and address < end) return @max(start, address);
166 }
167 return null;
168 }
169
170 fn writePageTables(ram: []u8, plan: *const protection.Plan) void {
171 assertProtectionGeometry(plan);
172 writeEntry(ram, pml4_address, 0, pdpt_address | page_present | page_writable);
173 writeEntry(
174 ram,
175 pdpt_address,
176 0,
177 page_directory_address | page_present | page_writable,
178 );
179 for (0..identity_large_pages) |index| writeEntry(
180 ram,
181 page_directory_address,
182 index,
183 pageDirectoryEntry(index),
184 );
185 writeLowPageTable(ram);
186 writeKernelPageTables(ram, plan);
187 }
188
189 fn reactivationPages() [reactivation_page_count]u16 {
190 var pages: [reactivation_page_count]u16 = undefined;
191 pages[0] = @intCast(boot_frame_address / page_bytes);
192 pages[1] = @intCast(request_ring_address / page_bytes);
193 pages[2] = @intCast(event_ring_address / page_bytes);
194 var address = stack_base;
195 var index: usize = 3;
196 while (address < stack_end) : (address += page_bytes) {
197 pages[index] = @intCast(address / page_bytes);
198 index += 1;
199 }
200 std.debug.assert(index == pages.len);
201 return pages;
202 }
203
204 fn validatePageTables(
205 ram: []const u8,
206 plan: *const protection.Plan,
207 allow_activity: bool,
208 ) error{PageTableMismatch}!void {
209 assertProtectionGeometry(plan);
210 for (0..table_entries) |index| {
211 try validateEntry(
212 ram,
213 pml4_address,
214 index,
215 if (index == 0)
216 pdpt_address | page_present | page_writable
217 else
218 0,
219 if (allow_activity and index == 0) page_accessed else 0,
220 );
221 try validateEntry(
222 ram,
223 pdpt_address,
224 index,
225 if (index == 0)
226 page_directory_address | page_present | page_writable
227 else
228 0,
229 if (allow_activity and index == 0) page_accessed else 0,
230 );
231 try validateEntry(
232 ram,
233 page_directory_address,
234 index,
235 if (index < identity_large_pages)
236 pageDirectoryEntry(index)
237 else
238 0,
239 if (allow_activity) pageDirectoryActivity(index) else 0,
240 );
241 }
242 try validateLowPageTable(ram, allow_activity);
243 try validateKernelPageTables(ram, plan, allow_activity);
244 }
245
246 fn pageTableAddresses() [page_table_count]u64 {
247 var result: [page_table_count]u64 = undefined;
248 result[0] = pml4_address;
249 result[1] = pdpt_address;
250 result[2] = page_directory_address;
251 result[3] = low_page_table_address;
252 for (0..kernel_page_table_count) |index| {
253 result[4 + index] = kernelPageTableAddress(index);
254 }
255 return result;
256 }
257
258 fn pageDirectoryEntry(index: usize) u64 {
259 std.debug.assert(index < identity_large_pages);
260 if (index == 0) {
261 return low_page_table_address |
262 page_present | page_writable | page_no_execute;
263 }
264 if (index >= kernel_large_page_index and
265 index < kernel_large_page_index + kernel_page_table_count)
266 {
267 return kernelPageTableAddress(index - kernel_large_page_index) |
268 page_present | page_writable;
269 }
270 const physical = @as(u64, index) * large_page_bytes;
271 return physical |
272 page_present | page_writable | page_large | page_no_execute;
273 }
274
275 fn pageDirectoryActivity(index: usize) u64 {
276 if (index >= identity_large_pages) return 0;
277 if (index == 0 or
278 index >= kernel_large_page_index and
279 index < kernel_large_page_index + kernel_page_table_count)
280 {
281 return page_accessed;
282 }
283 return page_accessed | page_dirty;
284 }
285
286 fn writeLowPageTable(ram: []u8) void {
287 for (0..table_entries) |index| writeEntry(
288 ram,
289 low_page_table_address,
290 index,
291 lowPageTableEntry(index),
292 );
293 }
294
295 fn lowPageTableEntry(index: usize) u64 {
296 std.debug.assert(index < table_entries);
297 const physical = @as(u64, index) * page_bytes;
298 if (isPageTableAddress(physical)) return 0;
299 return physical | page_present | page_writable | page_no_execute;
300 }
301
302 fn writeKernelPageTables(ram: []u8, plan: *const protection.Plan) void {
303 for (0..kernel_page_table_count) |table_index| {
304 for (0..table_entries) |entry_index| writeEntry(
305 ram,
306 kernelPageTableAddress(table_index),
307 entry_index,
308 kernelPageTableEntry(plan, table_index, entry_index),
309 );
310 }
311 }
312
313 fn kernelPageTableEntry(
314 plan: *const protection.Plan,
315 table_index: usize,
316 entry_index: usize,
317 ) u64 {
318 std.debug.assert(table_index < kernel_page_table_count);
319 std.debug.assert(entry_index < table_entries);
320 const physical = os.boot.kernel.physical_base +
321 @as(u64, table_index) * large_page_bytes +
322 @as(u64, entry_index) * page_bytes;
323 if (plan.protectsPage(physical)) return physical | page_present;
324 return physical | page_present | page_writable | page_no_execute;
325 }
326
327 fn validateLowPageTable(
328 ram: []const u8,
329 allow_activity: bool,
330 ) error{PageTableMismatch}!void {
331 for (0..table_entries) |index| {
332 const expected = lowPageTableEntry(index);
333 try validateEntry(
334 ram,
335 low_page_table_address,
336 index,
337 expected,
338 if (allow_activity and expected != 0)
339 page_accessed | page_dirty
340 else
341 0,
342 );
343 }
344 }
345
346 fn validateKernelPageTables(
347 ram: []const u8,
348 plan: *const protection.Plan,
349 allow_activity: bool,
350 ) error{PageTableMismatch}!void {
351 for (0..kernel_page_table_count) |table_index| {
352 for (0..table_entries) |entry_index| try validateEntry(
353 ram,
354 kernelPageTableAddress(table_index),
355 entry_index,
356 kernelPageTableEntry(plan, table_index, entry_index),
357 if (allow_activity)
358 kernelPageActivity(plan, table_index, entry_index)
359 else
360 0,
361 );
362 }
363 }
364
365 fn kernelPageActivity(
366 plan: *const protection.Plan,
367 table_index: usize,
368 entry_index: usize,
369 ) u64 {
370 const entry = kernelPageTableEntry(plan, table_index, entry_index);
371 return page_accessed |
372 if (entry & page_writable != 0) page_dirty else 0;
373 }
374
375 fn kernelPageTableAddress(index: usize) u64 {
376 std.debug.assert(index < kernel_page_table_count);
377 return kernel_page_table_base + @as(u64, index) * page_bytes;
378 }
379
380 fn isPageTableAddress(address: u64) bool {
381 for (page_table_addresses) |candidate| {
382 if (address == candidate) return true;
383 }
384 return false;
385 }
386
387 fn assertProtectionGeometry(plan: *const protection.Plan) void {
388 std.debug.assert(plan.ram_base == manifest.k0_v1_ram_base);
389 std.debug.assert(plan.ram_bytes == ram_bytes);
390 for (plan.sealedSpans()) |span| {
391 std.debug.assert(span.ram_offset >= os.boot.kernel.physical_base);
392 std.debug.assert(
393 span.ram_offset + span.bytes <=
394 os.boot.kernel.physical_base + os.boot.kernel.kernel_memory_bytes_max,
395 );
396 }
397 }
398
399 fn validateEntry(
400 ram: []const u8,
401 table_address: u64,
402 index: usize,
403 expected: u64,
404 ignored: u64,
405 ) error{PageTableMismatch}!void {
406 const table: usize = @intCast(table_address);
407 const offset = table + index * @sizeOf(u64);
408 const actual = std.mem.readInt(
409 u64,
410 ram[offset..][0..@sizeOf(u64)],
411 .little,
412 );
413 if (actual & ~ignored != expected) return error.PageTableMismatch;
414 }
415
416 fn zeroPage(ram: []u8, address: u64) void {
417 const start: usize = @intCast(address);
418 @memset(ram[start..][0..page_bytes], 0);
419 }
420
421 fn writeEntry(ram: []u8, table_address: u64, index: usize, value: u64) void {
422 std.debug.assert(index < page_bytes / @sizeOf(u64));
423 const table: usize = @intCast(table_address);
424 const offset = table + index * @sizeOf(u64);
425 std.mem.writeInt(u64, ram[offset..][0..@sizeOf(u64)], value, .little);
426 }
427
428 fn loadImage(
429 ram: []u8,
430 image: []const u8,
431 execution: manifest.View,
432 ) void {
433 var index: u16 = 0;
434 while (index < execution.header.load_count) : (index += 1) {
435 const source = execution.load(index);
436 const destination_start = execution.header.facts.physical_base +
437 source.physical_offset;
438 const destination_end = destination_start + source.file_bytes;
439 const source_end = source.input_offset + source.file_bytes;
440 std.debug.assert(destination_end <= ram.len);
441 std.debug.assert(source_end <= image.len);
442 @memcpy(
443 ram[@intCast(destination_start)..@intCast(destination_end)],
444 image[source.input_offset..source_end],
445 );
446 }
447 }
448
449 comptime {
450 std.debug.assert(page_bytes == 4096);
451 std.debug.assert(ram_bytes % large_page_bytes == 0);
452 std.debug.assert(identity_large_pages == 32);
453 std.debug.assert(os.boot.kernel.physical_base % large_page_bytes == 0);
454 std.debug.assert(os.boot.kernel.kernel_memory_bytes_max % large_page_bytes == 0);
455 std.debug.assert(kernel_page_table_count == 2);
456 std.debug.assert(page_table_count == 6);
457 std.debug.assert(table_entries == 512);
458 std.debug.assert(pml4_address + page_bytes <= pdpt_address);
459 std.debug.assert(pdpt_address + page_bytes <= page_directory_address);
460 std.debug.assert(page_directory_address + page_bytes <= boot_frame_address);
461 std.debug.assert(boot_frame_address + page_bytes <= low_page_table_address);
462 std.debug.assert(low_page_table_address + page_bytes <= kernel_page_table_base);
463 std.debug.assert(
464 kernel_page_table_base + kernel_page_table_count * page_bytes <=
465 request_ring_address,
466 );
467 std.debug.assert(request_ring_address + page_bytes <= event_ring_address);
468 std.debug.assert(event_ring_address + page_bytes <= os.boot.kernel.physical_base);
469 std.debug.assert(
470 os.boot.kernel.physical_base + os.boot.kernel.kernel_memory_bytes_max <= stack_base,
471 );
472 std.debug.assert(stack_top <= ram_bytes);
473 std.debug.assert(stack_end <= ram_bytes);
474 }