commit 8e1e9d0353d992a7f7cb738e999e59c4d20585f0
parent 2be6a19907a081b3489323b6bc3cb6cee478f169
Author: Sylvia Ivory <git@sivory.net>
Date: Tue, 10 Mar 2026 19:56:43 -0700
Add page table
Diffstat:
| M | pi/mmu.zig | | | 84 | +++++++++++++++++++++++++++++++++++++++++++++---------------------------------- |
| A | pi/pt.zig | | | 112 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
2 files changed, 160 insertions(+), 36 deletions(-)
diff --git a/pi/mmu.zig b/pi/mmu.zig
@@ -250,42 +250,6 @@ pub const InnerCacheableAttr = enum(u3) {
};
pub const TranslationTableBaseRegister0 = packed struct(u32) {
- // // This is pain
- // pub const TranslationTableBase = packed union {
- // @"16KB": packed struct {
- // translation_table_base: u15,
- // _reserved_13_5: u9,
- // },
- // @"8KB": packed struct {
- // translation_table_base: u16,
- // _reserved_12_5: u8,
- // },
- // @"4KB": packed struct {
- // translation_table_base: u17,
- // _reserved_11_5: u7,
- // },
- // @"2KB": packed struct {
- // translation_table_base: u18,
- // _reserved_10_5: u6,
- // },
- // @"1KB": packed struct {
- // translation_table_base: u19,
- // _reserved_9_5: u5,
- // },
- // @"512B": packed struct {
- // translation_table_base: u20,
- // _reserved_8_5: u4,
- // },
- // @"256B": packed struct {
- // translation_table_base: u21,
- // _reserved_7_5: u3,
- // },
- // @"128B": packed struct {
- // translation_table_base: u22,
- // _reserved_6_5: u2,
- // },
- // };
-
inner_cacheable: bool,
shared: bool,
ecc: bool,
@@ -390,6 +354,54 @@ pub const ContextId = packed struct(u32) {
}
};
+pub const FirstLevelDescriptor = packed struct(u32) {
+ pub const Fault = packed struct(u30) {
+ _ignore_31_2: u30,
+ };
+ pub const Section = packed struct(u30) {
+ b: u1,
+ c: u1,
+ never_execute: bool,
+ domain: u4,
+ _implementation_9: u1 = 0,
+ ap: AccessPermissions,
+ tex: u3,
+ apx: AccessPermissions,
+ shared: bool,
+ not_global: bool,
+ is_supersection: bool = false,
+ section_base_address: u22,
+ };
+ pub const SuperSection = packed struct(u30) {
+ b: u1,
+ c: u1,
+ never_execute: bool,
+ base_address_39_36: u4,
+ _implementation_9: u1,
+ ap: AccessPermissions,
+ tex: u3,
+ apx: AccessPermissions,
+ shared: bool,
+ not_global: bool,
+ is_supersection: bool = true,
+ base_address_35_32: u4,
+ section_base_address: u18,
+ };
+
+ pub const Type = enum(u2) {
+ Fault = 0b00,
+ CoarsePageTable = 0b01,
+ Section = 0b10,
+ };
+
+ ty: Type,
+ descriptor: packed union {
+ fault: Fault,
+ section: Section,
+ supersection: SuperSection,
+ },
+};
+
pub const TranslationMode = enum {
PrivilegedRead,
PrivilegedWrite,
diff --git a/pi/pt.zig b/pi/pt.zig
@@ -0,0 +1,112 @@
+const std = @import("std");
+
+const procmap = @import("./procmap.zig");
+const pinned = @import("./pinned.zig");
+const mmu = @import("./mmu.zig");
+const mem = @import("./mem.zig");
+
+pub const Error = error{
+ InvalidPageCount,
+ MmuEnabled,
+ MmuDisabled,
+} || std.mem.Allocator;
+
+pub fn init(alloc: std.mem.Allocator, count: u16) Error![]mmu.FirstLevelDescriptor {
+ if (count != 4096) return Error.InvalidPageCount;
+
+ const invalid: mmu.FirstLevelDescriptor = .{
+ .ty = .Fault,
+ .descriptor = .{ .fault = .{ ._ignore_31_2 = 0 } },
+ };
+
+ var page_table = try alloc.allocWithOptions(
+ mmu.FirstLevelDescriptor,
+ count,
+ std.mem.Alignment.fromByteUnits(14),
+ null,
+ );
+
+ @memset(&page_table, invalid);
+
+ return page_table;
+}
+
+pub fn dupe(alloc: std.mem.Allocator, pt: []mmu.FirstLevelDescriptor) Error![]mmu.FirstLevelDescriptor {
+ return try alloc.dupe(mmu.FirstLevelDescriptor, pt);
+}
+
+pub fn enable() Error!void {
+ if (mmu.is_enabled()) return Error.MmuEnabled;
+ mmu.enable();
+ if (!mmu.is_enabled()) return Error.MmuDisabled;
+}
+
+pub fn disable() Error!void {
+ if (!mmu.is_enabled()) return Error.MmuDisabled;
+ mmu.disable();
+ if (mmu.is_enabled()) return Error.MmuEnabled;
+}
+
+pub fn pt_switch(pt: []mmu.FirstLevelDescriptor, pid: u32, asid: u8) Error!void {
+ mmu.set_context_ttbr0(.{ .asid = asid, .pid = pid }, .{
+ // just guess wcgw
+ .ecc = false,
+ .shared = true,
+ .inner_cacheable = false,
+ .outer_cacheable_attr = .OuterNoncacheable,
+ .translation_table_base = @truncate(@intFromPtr(pt) >> 5),
+ });
+}
+
+pub fn set(pt: []mmu.FirstLevelDescriptor, va: u32, pa: u32, attr: pinned.PinnedAttribute) Error!*mmu.FirstLevelDescriptor {
+ const cs = mem.enter_critical_section();
+ defer cs.exit();
+
+ const index = va >> 20;
+ const page = pt[index];
+
+ const mem_attr: mmu.LockdownAttributesRegister.PageTableEncodingManual = @bitCast(attr.mem_attributes);
+
+ page.ty = .Section;
+ page.descriptor = .{ .section = .{
+ .not_global = if (attr.scope == .Global) false else true,
+ .ap = attr.permission,
+ .apx = attr.permission_x,
+ .domain = attr.domain,
+ .never_execute = false,
+ .b = mem_attr.b,
+ .c = mem_attr.c,
+ .tex = mem_attr.tex,
+ .shared = true,
+ .section_base_address = @truncate(pa >> 10),
+ } };
+
+ mmu.sync_pte();
+
+ return &page;
+}
+
+pub fn get(pt: []mmu.FirstLevelDescriptor, va: u32) !?*mmu.FirstLevelDescriptor {
+ const index = va >> 20;
+ const page = pt[index];
+
+ if (page.ty == .Fault) return null;
+
+ return page;
+}
+
+pub fn translate(pt: []mmu.FirstLevelDescriptor, va: u32) ?u32 {
+ const index = va >> 20;
+ const page = pt[index];
+
+ // Not dealing with with coarse/fault
+ if (page.ty != .Section) return null;
+ const section = page.descriptor.section;
+ // Not dealing with super sections
+ if (section.is_supersection) return null;
+
+ const base = @as(u32, section.section_base_address) << 10;
+
+ // Probably correct
+ return base | (index << 22);
+}