commit cc2c284a52816cafdc8ee58e774643faa4c1d028
parent 997b21c4f4c97481bc190ce3108c9ea7acd02886
Author: Sylvia Ivory <git@sivory.net>
Date: Thu, 12 Mar 2026 18:14:50 -0700
Allow switching between procmaps
Diffstat:
4 files changed, 133 insertions(+), 37 deletions(-)
diff --git a/pi/procmap.zig b/pi/procmap.zig
@@ -1,10 +1,11 @@
+// TODO; handle different page sizes
const std = @import("std");
+const uart = @import("./devices/mini-uart.zig");
const pinned = @import("./pinned.zig");
const mmu = @import("./mmu.zig");
const mem = @import("./mem.zig");
const pt = @import("./pt.zig");
-const uart = @import("./devices/mini-uart.zig");
const pi = @import("./root.zig");
@@ -35,6 +36,12 @@ const READ_ONLY_ATTR: pinned.PinnedAttribute = .{
.mem_attributes = .OuterInnerNoncacheable,
};
+const MB = 1024 * 1024;
+
+pub const Error = error{
+ FailedTranslation,
+};
+
pub const ProcEntry = struct {
pub const ProcEntryType = enum {
Device,
@@ -42,11 +49,22 @@ pub const ProcEntry = struct {
ReadOnly,
};
- addr: u32,
- // TODO; handle other byte sizes
+ virt_addr: u32,
+ phys_addr: u32,
n_bytes: u32,
type: ProcEntryType,
- domain: u4,
+ domain: u4 = 1,
+ asid: u8 = 0,
+
+ pub fn identity(addr: u32, n_bytes: u32, ty: ProcEntryType, dom: u4) ProcEntry {
+ return .{
+ .virt_addr = addr,
+ .phys_addr = addr,
+ .n_bytes = n_bytes,
+ .domain = dom,
+ .type = ty,
+ };
+ }
};
pub const ProcMap = struct {
@@ -63,23 +81,19 @@ pub const ProcMap = struct {
.alloc = alloc,
};
- const MB = 1024 * 1024;
-
// BCM2835
- try map.push(.{ .addr = 0x20000000, .n_bytes = MB, .type = .Device, .domain = dom });
- try map.push(.{ .addr = 0x20100000, .n_bytes = MB, .type = .Device, .domain = dom });
- try map.push(.{ .addr = 0x20200000, .n_bytes = MB, .type = .Device, .domain = dom });
+ try map.push(ProcEntry.identity(0x20000000, MB * 3, .Device, dom));
// Program Code
- try map.push(.{ .addr = 0x00000000, .n_bytes = MB, .type = .ReadWrite, .domain = dom });
+ try map.push(ProcEntry.identity(0x00000000, MB, .ReadWrite, dom));
// Page table
- try map.push(.{ .addr = 0x7E00000, .n_bytes = MB, .type = .ReadWrite, .domain = dom });
- try map.push(.{ .addr = @intFromPtr(map.pt.ptr), .n_bytes = MB, .type = .ReadWrite, .domain = dom });
+ try map.push(ProcEntry.identity(@intFromPtr(map.pt.ptr), MB, .ReadWrite, dom));
+ try map.push(ProcEntry.identity(0x7E00000, MB, .ReadWrite, dom));
// Stacks
- try map.push(.{ .addr = pi.STACK_ADDRESS - MB, .n_bytes = MB, .type = .ReadWrite, .domain = dom });
- try map.push(.{ .addr = pi.STACK_INTERRUPT_ADDRESS - MB, .n_bytes = MB, .type = .ReadWrite, .domain = dom });
+ try map.push(ProcEntry.identity(pi.STACK_ADDRESS - MB, MB, .ReadWrite, dom));
+ try map.push(ProcEntry.identity(pi.STACK_INTERRUPT_ADDRESS - MB, MB, .ReadWrite, dom));
return map;
}
@@ -96,40 +110,47 @@ pub const ProcMap = struct {
.ReadOnly => READ_ONLY_ATTR,
};
attr.domain = entry.domain;
+ attr.asid = entry.asid;
- uart.print("pinning 0x{X} -> 0x{X}\n", .{ entry.addr, entry.addr });
- _ = try pt.set(self.pt, entry.addr, entry.addr, attr);
- uart.print(" check 0x{X} -> 0x{X}\n", .{ entry.addr, pt.translate(self.pt, entry.addr) orelse 0 });
+ for (0..(entry.n_bytes / MB)) |offset| {
+ const va = entry.virt_addr + offset * MB;
+ const pa = entry.phys_addr + offset * MB;
+ _ = try pt.set(self.pt, va, pa, attr);
+ }
}
}
- pub fn enable(self: *const Self) !void {
+ pub fn enable(self: *const Self, asid: u8) !void {
mmu.init();
mmu.DomainAccessControlRegister.set_all(.Manager);
try self.pin();
- try pt.pt_switch(self.pt, 0x140E, 1);
-
- const ttbc = mmu.TranslationTableBaseControl.get();
- uart.print("N={}\n", .{@intFromEnum(ttbc.boundary_size)});
+ try pt.pt_switch(self.pt, 0x140E, asid);
+ mmu.sync_pte();
+ mmu.enable();
for (self.entries.items) |entry| {
- pt.check_entry(entry.addr);
- }
-
- const ctx = mmu.ContextId.get();
- uart.print("asid={} pid={X}\n", .{ ctx.asid, ctx.pid });
+ const res = mmu.va_translation_cw(entry.virt_addr, .PrivilegedRead);
+ if (res.aborted) return Error.FailedTranslation;
- mmu.sync_pte();
+ const physical_base = @as(u32, res.inner.success.address) << 10;
+ const offset = entry.virt_addr & 0xFFF;
- uart.print(" check 0x{X} -> 0x{X}\n", .{ 0x7EFFE24, pt.translate(self.pt, 0x7EFFE24) orelse 0 });
+ if ((physical_base | offset) != entry.phys_addr) return Error.FailedTranslation;
+ }
+ }
- mmu.enable();
+ pub fn disable(self: *const Self) !void {
+ _ = self;
+ mmu.disable();
+ }
- uart.print("checking entries\n", .{});
- for (self.entries.items) |entry| {
- if (!try pinned.is_pinned(entry.addr)) return pinned.Error.ExpectedPinned;
- }
+ pub fn dupe(self: *const Self) !Self {
+ return .{
+ .alloc = self.alloc,
+ .entries = try self.entries.clone(self.alloc),
+ .pt = try pt.dupe(self.alloc, self.pt),
+ };
}
};
diff --git a/pi/pt.zig b/pi/pt.zig
@@ -29,7 +29,16 @@ pub fn init(alloc: std.mem.Allocator, count: u16) Error![]mmu.FirstLevelDescript
}
pub fn dupe(alloc: std.mem.Allocator, pt: []mmu.FirstLevelDescriptor) Error![]mmu.FirstLevelDescriptor {
- return try alloc.dupe(mmu.FirstLevelDescriptor, pt);
+ const page_table = try alloc.alignedAlloc(
+ mmu.FirstLevelDescriptor,
+ std.mem.Alignment.fromByteUnits(1 << 14),
+ pt.len,
+ );
+
+ @memcpy(page_table, pt);
+ mmu.sync_pte();
+
+ return page_table;
}
pub fn pt_switch(pt: []mmu.FirstLevelDescriptor, pid: u24, asid: u8) Error!void {
diff --git a/programs/pinned-lookup.zig b/programs/pinned-lookup.zig
@@ -20,10 +20,10 @@ pub fn main() !void {
interrupts.set_exception_handler(.DataAbort, data_abort_handler);
const map: pi.procmap.ProcMap = try .init(alloc, 1);
- try map.enable();
+ try map.enable(1);
for (map.entries.items, 0..) |entry, n| {
- const va = entry.addr;
+ const va = entry.virt_addr;
uart.print("checking entry n={d} addr=0x{X}\n", .{ n, va });
uart.print(" translation result=0x{X}\n", .{try pi.pinned.get(va)});
diff --git a/programs/pt-asid.zig b/programs/pt-asid.zig
@@ -0,0 +1,66 @@
+const std = @import("std");
+const pi = @import("pi");
+
+const interrupts = pi.interrupts;
+const faults = pi.faults;
+const uart = pi.devices.mini_uart;
+const mem = pi.mem;
+
+fn data_abort_handler(regs: interrupts.Registers) void {
+ pi.mmu.disable();
+ const far = faults.FAR.get();
+
+ uart.print("got fault on 0x{X} from 0x{X}\n", .{ far, regs.pc });
+ pi.reboot();
+}
+
+fn mb(n: u32) u32 {
+ return n * 1024 * 1024;
+}
+
+pub fn main() !void {
+ var buffer: [1024 * 1024]u8 = undefined;
+ var fba: std.heap.FixedBufferAllocator = .init(&buffer);
+ const alloc = fba.allocator();
+
+ interrupts.set_exception_handler(.DataAbort, data_abort_handler);
+
+ const user_addr: *u32 = @ptrFromInt(mb(16));
+ const phys_1: *u32 = @ptrFromInt(@intFromPtr(user_addr) + mb(1));
+ const phys_2: *u32 = @ptrFromInt(@intFromPtr(user_addr) + mb(2));
+
+ var map_1: pi.procmap.ProcMap = try .init(alloc, 1);
+ var map_2 = try map_1.dupe();
+
+ try map_1.push(.{
+ .asid = 1,
+ .n_bytes = mb(1),
+ .type = .ReadWrite,
+ .phys_addr = @intFromPtr(phys_1),
+ .virt_addr = @intFromPtr(user_addr),
+ });
+ try map_2.push(.{
+ .asid = 2,
+ .n_bytes = mb(1),
+ .type = .ReadWrite,
+ .phys_addr = @intFromPtr(phys_2),
+ .virt_addr = @intFromPtr(user_addr),
+ });
+
+ mem.put_u32(phys_1, 0x1111_1111);
+ mem.put_u32(phys_2, 0x2222_2222);
+
+ uart.print("enabling with ASID=1\n", .{});
+ try map_1.enable(1);
+ uart.print("ASID 1 got 0x{X}\n", .{mem.get_u32(user_addr)});
+ mem.put_u32(user_addr, 1);
+ try map_1.disable();
+ uart.print("checking write: 0x{X}\n", .{mem.get_u32(phys_1)});
+
+ uart.print("enabling with ASID=2\n", .{});
+ try map_2.enable(2);
+ uart.print("ASID 2 got 0x{X}\n", .{mem.get_u32(user_addr)});
+ mem.put_u32(user_addr, 2);
+ try map_2.disable();
+ uart.print("checking write: 0x{X}\n", .{mem.get_u32(phys_2)});
+}