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 }