commit b9eb38b45f64c796ffa4b68070c8f59bb26c3ea7
parent 675082592540bbadd50b6ba796c19110e299b9c6
Author: Sylvia Ivory <git@sivory.net>
Date: Tue, 3 Mar 2026 15:35:55 -0800
Add pinned lookup
Diffstat:
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", .{});
+ }
+}