sylveos

Toy Operating System
Log | Files | Refs

commit b9eb38b45f64c796ffa4b68070c8f59bb26c3ea7
parent 675082592540bbadd50b6ba796c19110e299b9c6
Author: Sylvia Ivory <git@sivory.net>
Date:   Tue,  3 Mar 2026 15:35:55 -0800

Add pinned lookup

Diffstat:
Mpi/mmu.zig | 38++++++++++++++++++++++++++++++++++----
Mpi/pinned.zig | 24+++++++++++++++++-------
Api/procmap.zig | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpi/root.zig | 1+
Aprograms/pinned-lookup.zig | 36++++++++++++++++++++++++++++++++++++
5 files changed, 205 insertions(+), 11 deletions(-)

diff --git a/pi/mmu.zig b/pi/mmu.zig @@ -241,6 +241,14 @@ pub const OuterCacheableAttr = enum(u2) { WriteBackNoWriteAlloc = 0b11, }; +pub const InnerCacheableAttr = enum(u3) { + Noncacheable = 0b000, + StronglyOrdered = 0b001, + Device = 0b011, + InnerWriteThrough = 0b110, + InnerWriteBack = 0b111, +}; + pub const TranslationTableBaseRegister0 = packed struct(u32) { // // This is pain // pub const TranslationTableBase = packed union { @@ -387,7 +395,29 @@ pub const TranslationMode = enum { UserWrite, }; -pub inline fn va_translation_cw(va: u32, comptime mode: TranslationMode) u32 { +pub const TranslationResult = packed struct(u32) { + pub const TranslationSuccess = packed struct(u31) { + _reserved_1: u1, + outer: OuterCacheableAttr, + inner: InnerCacheableAttr, + shared: bool, + _unused_8: u1, + not_secure: bool, + address: u22, + }; + pub const TranslationAborted = packed struct(u31) { + fsr_bits: u6, + _reserved_7_31: u25, + }; + + aborted: bool, + inner: packed union { + success: TranslationSuccess, + aborted: TranslationAborted, + }, +}; + +pub inline fn va_translation_cw(va: u32, comptime mode: TranslationMode) TranslationResult { const mode_asm = switch (mode) { .PrivilegedRead => "0", .PrivilegedWrite => "1", @@ -395,13 +425,13 @@ pub inline fn va_translation_cw(va: u32, comptime mode: TranslationMode) u32 { .UserWrite => "3", }; - asm volatile ("MRC p15, 0, %[value], c7, c8, " ++ mode_asm + asm volatile ("MCR p15, 0, %[value], c7, c8, " ++ mode_asm : : [value] "r" (va), ); - return asm volatile ("MCR p15, 0, %[result], c7, c4, 0" - : [result] "=r" (-> u32), + return asm volatile ("MRC p15, 0, %[result], c7, c4, 0" + : [result] "=r" (-> TranslationResult), ); } diff --git a/pi/pinned.zig b/pi/pinned.zig @@ -2,7 +2,7 @@ const system = @import("./system.zig"); const mmu = @import("./mmu.zig"); const mem = @import("./mem.zig"); -const Error = error{ +pub const Error = error{ MMUEnabled, MMUDisabled, InvalidMVA, @@ -10,15 +10,16 @@ const Error = error{ BadSetVA, BadSetAttr, BadSetPA, + ExpectedPinned, }; // Invalidate page table const null_pages: [4096 * 4]u8 align(1 << 14) = .{0} ** (4096 * 4); -pub fn init(domain: mmu.DomainAccessControlRegister) Error!void { +pub fn init() Error!void { if (mmu.is_enabled()) return Error.MMUEnabled; - // TODO - _ = domain; + mmu.init(); + mmu.DomainAccessControlRegister.set_all(.Client); } // Map VA -> PA at IDX with attributes E @@ -92,14 +93,23 @@ pub fn get(va: u32) Error!u32 { if (va & 0b11 != 0) return Error.InvalidMVA; const pa = mmu.va_translation_cw(va, .PrivilegedRead); - if (pa & 0b1 == 1) return Error.TranslationAborted; + if (pa.aborted) return Error.TranslationAborted; - return pa; + const physical_base = @as(u32, pa.inner.success.address) << 10; + const offset = va & 0xFFF; + + return physical_base | offset; } // Check if VA == PA pub fn is_pinned(va: u32) Error!bool { - return (try get(va)) == va; + const pa = get(va) catch |err| { + // A translation abort explicitly means the VA is not pinned + if (err == Error.TranslationAborted) return false; + return err; + }; + + return va == pa; } pub const PinnedAttribute = struct { diff --git a/pi/procmap.zig b/pi/procmap.zig @@ -0,0 +1,117 @@ +const std = @import("std"); + +const pinned = @import("./pinned.zig"); +const mmu = @import("./mmu.zig"); +const pi = @import("./root.zig"); + +pub const MAX_ENTRIES = 8; + +pub const DEVICE_ATTR: pinned.PinnedAttribute = .{ + .asid = 0, + .domain = 1, + .scope = .Global, + .permission = .UserNoAccess, + .permission_x = .SupervisorRW, + .mem_attributes = .StronglyOrdered, +}; + +const READ_WRITE_ATTR: pinned.PinnedAttribute = .{ + .asid = 0, + .domain = 1, + .scope = .Global, + .permission = .UserNoAccess, + .permission_x = .SupervisorRW, + .mem_attributes = .OuterInnerNoncacheable, +}; + +const READ_ONLY_ATTR: pinned.PinnedAttribute = .{ + .asid = 0, + .domain = 1, + .scope = .Global, + .permission = .UserNoAccess, + .permission_x = .SupervisorRO, + .mem_attributes = .OuterInnerNoncacheable, +}; + +pub const ProcEntry = struct { + pub const ProcEntryType = enum { + Device, + ReadWrite, + ReadOnly, + }; + + addr: usize, + // TODO; handle other byte sizes + n_bytes: usize, + type: ProcEntryType, + domain: u4, +}; + +pub const ProcMap = struct { + entries: [MAX_ENTRIES]ProcEntry, + current_entry: usize, + + const Self = @This(); + + pub fn init(dom: u4) Self { + var map = std.mem.zeroes(ProcMap); + + const MB = 1024 * 1024; + + // BCM2835 + map.push(.{ .addr = 0x20000000, .n_bytes = MB, .type = .Device, .domain = dom }); + map.push(.{ .addr = 0x20100000, .n_bytes = MB, .type = .Device, .domain = dom }); + map.push(.{ .addr = 0x20200000, .n_bytes = MB, .type = .Device, .domain = dom }); + + // Program Code + map.push(.{ .addr = 0x00000000, .n_bytes = MB, .type = .ReadWrite, .domain = dom }); + + // Heap would live here if we used one + // map.push(.{ .addr = 0x00100000, .n_bytes = MB, .type = .ReadWrite, .dom = dom }); + + // Stacks + map.push(.{ .addr = pi.STACK_ADDRESS - MB, .n_bytes = MB, .type = .ReadWrite, .domain = dom }); + map.push(.{ .addr = pi.STACK_INTERRUPT_ADDRESS - MB, .n_bytes = MB, .type = .ReadWrite, .domain = dom }); + + return map; + } + + pub fn push(self: *Self, entry: ProcEntry) void { + self.entries[self.current_entry] = entry; + self.current_entry += 1; + } + + fn pin(self: *const Self) !void { + for (0..self.current_entry) |n| { + const entry = self.entries[n]; + + var attr = switch (entry.type) { + .Device => DEVICE_ATTR, + .ReadWrite => READ_WRITE_ATTR, + .ReadOnly => READ_ONLY_ATTR, + }; + attr.domain = entry.domain; + + try pinned.set(@truncate(n), entry.addr, entry.addr, attr); + } + } + + var null_ptr: [4096 * 4]u8 align(1 << 14) = .{0} ** (4096 * 4); + pub fn enable(self: *const Self) !void { + mmu.init(); + + try self.pin(); + mmu.DomainAccessControlRegister.set_all(.Client); + + mmu.set_context_ttbr0(.{ .asid = 1, .pid = 128 << 8 }, @bitCast(@intFromPtr(&null_ptr))); + mmu.enable(); + + pinned.lockdown_print_entries(); + + for (0..self.current_entry) |n| { + const entry = self.entries[n]; + + if (!try pinned.is_pinned(entry.addr)) return pinned.Error.ExpectedPinned; + } + } +}; diff --git a/pi/root.zig b/pi/root.zig @@ -7,6 +7,7 @@ pub const interrupts = @import("./interrupts.zig"); pub const switching = @import("./switching.zig"); pub const register = @import("./register.zig"); pub const journal = @import("./journal.zig"); +pub const procmap = @import("./procmap.zig"); pub const faults = @import("./faults.zig"); pub const thread = @import("./thread.zig"); pub const pinned = @import("./pinned.zig"); diff --git a/programs/pinned-lookup.zig b/programs/pinned-lookup.zig @@ -0,0 +1,36 @@ +const std = @import("std"); +const pi = @import("pi"); + +const uart = pi.devices.mini_uart; + +pub fn main() !void { + const map: pi.procmap.ProcMap = .init(1); + try map.enable(); + + pi.pinned.lockdown_print_entries(); + + for (0..map.current_entry) |n| { + const va = map.entries[n].addr + 0xFAA0; + + uart.print("checking entry n={d} addr=0x{X}\n", .{ n, va }); + uart.print(" translation result=0x{X}\n", .{try pi.pinned.get(va)}); + + if (!try pi.pinned.is_pinned(va)) { + uart.print("expected 0x{X} to be pinned\n", .{va}); + + return; + } + + const pa = try pi.pinned.get(va); + + if (pa != va) { + uart.print("VA address did not match, expected 0x{X}, got 0x{X}", .{ va, pa }); + } + } + + uart.print("checking invalid\n", .{}); + + if (try pi.pinned.is_pinned(0xDEADFAA0)) { + uart.print("pinned invalid VA\n", .{}); + } +}