commit 519bb1f2f4b188235b976c68c8576d4402d77bd6
parent 4710c97f45222dbe59c29f380461ce882093ea8e
Author: Sylvia Ivory <git@sivory.net>
Date: Fri, 13 Mar 2026 09:39:53 -0700
Relocate above IO
Diffstat:
9 files changed, 218 insertions(+), 49 deletions(-)
diff --git a/boot/root.zig b/boot/root.zig
@@ -14,16 +14,16 @@ fn initialize_interrupts() void {
pi.interrupts.clear_interrupt_flags();
pi.mem.barrier(.Write);
- uart.write_slice(" Setting exception vector\n");
- pi.interrupts.setup_exception_vector();
- pi.mem.barrier(.Write);
+ // uart.write_slice(" Setting exception vector\n");
+ // pi.interrupts.setup_exception_vector();
+ // pi.mem.barrier(.Write);
}
export const _start = make(kmain, abort)._start;
export fn kmain() void {
// We don't return so we can modify SP
- pi.set_sp(@ptrFromInt(pi.get_stack_address()));
+ // pi.set_sp(@ptrFromInt(pi.get_stack_address()));
uart.initialize(921600, .Gpio14, .Gpio15) catch {};
@@ -42,10 +42,10 @@ export fn kmain() void {
uart.print(" 0x{X} = 0x{X}\n", .{ @intFromPtr(dst), dst.* });
}
- uart.write_slice("Enabling Interrupts\n");
+ // uart.write_slice("Enabling Interrupts\n");
// uart.set_tx_interrupts(true);
// uart.set_rx_interrupts(true) catch {};
- pi.interrupts.enable_interrupts();
+ // pi.interrupts.enable_interrupts();
uart.write_slice("Entering program\n");
uart.flush();
diff --git a/build.zig b/build.zig
@@ -87,6 +87,7 @@ fn build_pi(
exe.link_data_sections = true;
exe.link_gc_sections = true;
exe.lto = .full;
+ exe.pie = true;
exe.setLinkerScript(b.path("pi/linker.ld"));
b.installArtifact(exe);
diff --git a/pi/interrupts.zig b/pi/interrupts.zig
@@ -209,7 +209,7 @@ fn create_trampoline(name: []const u8, offset: []const u8, ret: []const u8) []co
".global " ++ name ++ "\n" ++
".type " ++ name ++ ", %function\n" ++
name ++ ":\n" ++
- " ldr sp, =__int_stack_end__\n" ++
+ " mov sp, 0x9000000\n" ++
" push {r0-r12, lr}\n" ++
" sub lr, lr, #" ++ offset ++ "\n" ++
" mov r0, lr\n" ++
diff --git a/pi/pinned.zig b/pi/pinned.zig
@@ -119,6 +119,17 @@ pub const PinnedAttribute = struct {
permission: mmu.AccessPermissions,
permission_x: mmu.AccessPermissionsExtended,
mem_attributes: mmu.LockdownAttributesRegister.PageTableEncodingPreset,
+
+ pub fn dupe_asid_domain(self: *const PinnedAttribute, asid: u8, domain: u4) PinnedAttribute {
+ return .{
+ .scope = self.scope,
+ .permission = self.permission,
+ .permission_x = self.permission_x,
+ .mem_attributes = self.mem_attributes,
+ .asid = asid,
+ .domain = domain,
+ };
+ }
};
const uart = @import("./devices/mini-uart.zig");
diff --git a/pi/procmap.zig b/pi/procmap.zig
@@ -18,7 +18,7 @@ pub const DEVICE_ATTR: pinned.PinnedAttribute = .{
.mem_attributes = .StronglyOrdered,
};
-const READ_WRITE_ATTR: pinned.PinnedAttribute = .{
+pub const READ_WRITE_ATTR: pinned.PinnedAttribute = .{
.asid = 0,
.domain = 1,
.scope = .Global,
@@ -27,7 +27,7 @@ const READ_WRITE_ATTR: pinned.PinnedAttribute = .{
.mem_attributes = .OuterInnerNoncacheable,
};
-const READ_ONLY_ATTR: pinned.PinnedAttribute = .{
+pub const READ_ONLY_ATTR: pinned.PinnedAttribute = .{
.asid = 0,
.domain = 1,
.scope = .Global,
diff --git a/pi/pt.zig b/pi/pt.zig
@@ -11,11 +11,11 @@ pub const Error = error{
MmuDisabled,
} || std.mem.Allocator.Error;
+pub const fault_page: mmu.FirstLevelDescriptor = .{ .ty = .Fault, .descriptor = .{ .fault = .{} } };
+
pub fn init(alloc: std.mem.Allocator, count: u16) Error![]mmu.FirstLevelDescriptor {
if (count != 4096) return Error.InvalidPageCount;
- const fault_page: mmu.FirstLevelDescriptor = .{ .ty = .Fault, .descriptor = .{ .fault = .{} } };
-
const page_table = try alloc.alignedAlloc(
mmu.FirstLevelDescriptor,
std.mem.Alignment.fromByteUnits(1 << 14),
diff --git a/pi/switching.zig b/pi/switching.zig
@@ -65,11 +65,21 @@ comptime {
\\ mcr p15, 0, r2, c7, c5, 4
\\ ldm r0, {r0-r15}
));
+
+ // Jump to lr and put sp
+ // r0 - lr
+ // r1 - sp
+ asm (mk_fn("jump",
+ \\ mov sp, r1
+ \\ mov lr, r0
+ \\ bx lr
+ ));
}
extern fn switch_state_inner(old: *interrupts.Registers, new: *const interrupts.Registers, *const fn (*interrupts.Registers) callconv(.c) void) void;
pub extern fn restore_state_user(state: *const interrupts.Registers) noreturn;
pub extern fn restore_state_privileged(state: *const interrupts.Registers) noreturn;
+pub extern fn jump(lr: u32, sp: u32) noreturn;
pub inline fn switch_state(old: *interrupts.Registers, new: *const interrupts.Registers) void {
if (new.psr.mode == .User) {
diff --git a/sylveos/memory.zig b/sylveos/memory.zig
@@ -3,38 +3,105 @@ const pi = @import("pi");
const mailbox = pi.devices.mailbox;
const uart = pi.devices.mini_uart;
+const pinned = pi.pinned;
+const page_table = pi.pt;
+const mmu = pi.mmu;
-const MB: u32 = 1024 * 1024;
+pub const MB: u32 = 1024 * 1024;
extern const __program_start__: u32;
extern const __program_end__: u32;
extern const __heap_start__: u32;
-pub fn print_regions() void {
- const program_start = std.mem.alignBackward(u32, @intFromPtr(&__program_start__), MB);
- const program_end = std.mem.alignForward(u32, @intFromPtr(&__program_end__), MB);
- const program_size = program_end - program_start;
+pub const Region = struct {
+ start: u32,
+ end: u32,
+ size: u32,
+ raw: []allowzero u8,
+
+ pub fn init(start: u32, end: u32) Region {
+ const size = end - start;
+ var raw: [*]allowzero u8 = @ptrFromInt(start);
+
+ return .{
+ .start = start,
+ .end = end,
+ .size = size,
+ .raw = raw[0..size],
+ };
+ }
+
+ pub fn program() Region {
+ const program_start = std.mem.alignBackward(u32, @intFromPtr(&__program_start__), MB);
+ const program_end = std.mem.alignForward(u32, @intFromPtr(&__program_end__), MB);
+
+ return Region.init(program_start, program_end);
+ }
+
+ pub fn stack() Region {
+ const stack_end = pi.get_stack_address();
+ const stack_start = stack_end - MB;
+
+ return Region.init(stack_start, stack_end);
+ }
+
+ pub fn stack_boot() Region {
+ const stack_end: u32 = 0x8000000;
+ const stack_start = stack_end - MB;
+
+ return Region.init(stack_start, stack_end);
+ }
+
+ pub fn interrupt_stack() Region {
+ const int_stack_end = pi.get_interrupt_stack_address();
+ const int_stack_start = int_stack_end - MB;
+
+ return Region.init(int_stack_start, int_stack_end);
+ }
+
+ pub fn io() Region {
+ const io_start = std.mem.alignBackward(u32, 0x2000_0000, MB);
+ const io_end = std.mem.alignForward(u32, 0x20FF_FFFF, MB);
- const stack_end = pi.get_stack_address();
- const stack_start = stack_end - MB;
- const stack_size = stack_end - stack_start;
+ return Region.init(io_start, io_end);
+ }
- const int_stack_end = pi.get_interrupt_stack_address();
- const int_stack_start = int_stack_end - MB;
- const int_stack_size = int_stack_end - int_stack_start;
+ pub fn memory() Region {
+ const memory_info = mailbox.get_arm_memory_info() catch unreachable;
+ const memory_start = memory_info.base_address;
+ const memory_end = memory_info.base_address + memory_info.memory_size;
- const io_start = std.mem.alignBackward(u32, 0x2000_0000, MB);
- const io_end = std.mem.alignForward(u32, 0x20FF_FFFF, MB);
- const io_size = io_end - io_start;
+ return Region.init(memory_start, memory_end);
+ }
- const memory_info = mailbox.get_arm_memory_info() catch unreachable;
- const memory_start = memory_info.base_address;
- const memory_end = memory_info.base_address + memory_info.memory_size;
- const memory_size = memory_end - memory_start;
+ pub fn heap() Region {
+ const memory_end = memory().end;
+ const heap_start = std.mem.alignBackward(u32, @intFromPtr(&__heap_start__), MB);
+ const heap_end = std.mem.alignBackward(u32, memory_end, MB);
- const heap_start = std.mem.alignBackward(u32, @intFromPtr(&__heap_start__), MB);
- const heap_end = std.mem.alignBackward(u32, memory_end, MB);
- const heap_size = heap_end - heap_start;
+ return Region.init(heap_start, heap_end);
+ }
+
+ pub fn map_to(self: *const Region, pt: []mmu.FirstLevelDescriptor, to: u32, attr: pinned.PinnedAttribute) !void {
+ const size = (self.end / MB) - (self.start / MB);
+
+ for (0..size) |offset| {
+ _ = try page_table.set(pt, to + offset * MB, self.start + offset * MB, attr);
+ }
+ }
+
+ pub fn map_identity(self: *const Region, pt: []mmu.FirstLevelDescriptor, attr: pinned.PinnedAttribute) !void {
+ try self.map_to(pt, self.start, attr);
+ }
+};
+
+pub fn print_regions() void {
+ const memory = Region.memory();
+ const program = Region.program();
+ const stack = Region.stack();
+ const int_stack = Region.interrupt_stack();
+ const heap = Region.heap();
+ const io = Region.io();
uart.print(
\\Physical Memory Layout: 0x{X:0>8} - 0x{X:0>8} ({Bi})
@@ -45,27 +112,35 @@ pub fn print_regions() void {
\\ BCM2835 IO: 0x{X:0>8} - 0x{X:0>8} ({Bi})
\\
, .{
- memory_start, memory_end, memory_size,
- program_start, program_end, program_size,
- stack_start, stack_end, stack_size,
- int_stack_start, int_stack_end, int_stack_size,
- heap_start, heap_end, heap_size,
- io_start, io_end, io_size,
+ memory.start, memory.end, memory.size,
+ program.start, program.end, program.size,
+ stack.start, stack.end, stack.size,
+ int_stack.start, int_stack.end, int_stack.size,
+ heap.start, heap.end, heap.size,
+ io.start, io.end, io.size,
});
}
pub fn get_allocator() std.mem.Allocator {
- const memory_info = mailbox.get_arm_memory_info() catch unreachable;
- const memory_start = memory_info.base_address;
- const memory_end = memory_start + memory_info.memory_size;
+ const heap = Region.heap();
+
+ var fba: std.heap.FixedBufferAllocator = .init(heap.raw[0..heap.size]);
- const heap_start = std.mem.alignBackward(u32, @intFromPtr(&__heap_start__), MB);
- const heap_end = std.mem.alignBackward(u32, memory_end, MB);
- const heap_size = heap_end - heap_start;
+ return fba.allocator();
+}
+
+pub fn copy_kernel(to: [*]u8) void {
+ const program = Region.program();
- var buffer_raw: [*]u8 = @ptrFromInt(heap_start);
+ @memcpy(to, program.raw[0..program.size]);
+}
- var fba: std.heap.FixedBufferAllocator = .init(buffer_raw[0..heap_size]);
+pub fn translate(va: u32) !u32 {
+ const res = mmu.va_translation_cw(va, .PrivilegedRead);
+ if (res.aborted) return error.FailedTranslation;
- return fba.allocator();
+ const physical_base = @as(u32, res.inner.success.address) << 10;
+ const offset = va & 0xFFF;
+
+ return physical_base | offset;
}
diff --git a/sylveos/root.zig b/sylveos/root.zig
@@ -1,8 +1,80 @@
+const std = @import("std");
const pi = @import("pi");
+
const memory = @import("./memory.zig");
+const uart = pi.devices.mini_uart;
+const procmap = pi.procmap;
+const page_table = pi.pt;
+const mmu = pi.mmu;
+
+const Region = memory.Region;
+
+// The whole point of this code is to relocate the kernel into the high addresses
pub fn main() !void {
- const alloc = memory.get_allocator();
memory.print_regions();
- _ = alloc;
+
+ // Now it's time for the fun
+ // We want to copy the kernel's code to high address space
+ // But we need to copy it somewhere in physical memory
+ // So we pick memory_end - kernel_size
+ const mem_region = Region.memory();
+ const program_region = Region.program();
+ const io_region = Region.io();
+
+ const kernel_relocation = mem_region.end - program_region.size;
+ memory.copy_kernel(@ptrFromInt(kernel_relocation));
+
+ // Need it to be 1mb aligned, we'll just let stack eventually merge into it and deal with that bug
+ var page_table_raw: [*]mmu.FirstLevelDescriptor = @ptrFromInt(kernel_relocation - memory.MB);
+ const pt = page_table_raw[0..4096];
+ @memset(pt, page_table.fault_page);
+
+ // Setup ASID
+ const READ_WRITE_ATTR = procmap.READ_WRITE_ATTR.dupe_asid_domain(0, 1);
+ // const READ_ONLY_ATTR = procmap.READ_ONLY_ATTR.dupe_asid_domain(1, 1);
+ const DEVICE_ATTR = procmap.DEVICE_ATTR.dupe_asid_domain(0, 1);
+
+ // We want to preserve program code and stack so we can continue running
+ try program_region.map_identity(pt, READ_WRITE_ATTR);
+ try Region.stack().map_identity(pt, READ_WRITE_ATTR);
+ try Region.stack_boot().map_identity(pt, READ_WRITE_ATTR);
+
+ // And also the new program code and stack
+ const relocation: Region = .init(@intFromPtr(page_table_raw), mem_region.end);
+ // Right on top of IO
+ const mapped_kernel = io_region.end;
+
+ try relocation.map_identity(pt, READ_WRITE_ATTR);
+ try relocation.map_to(pt, mapped_kernel, READ_WRITE_ATTR);
+
+ try io_region.map_identity(pt, DEVICE_ATTR);
+
+ const new_main_offset = @intFromPtr(&new_main);
+
+ // We need to be careful with the page table
+ // So we squeeze a space just under the kernel for it
+ // Then we have the "root" page table
+ // It'll live in the same page as the main stack
+ // I doubt the main stack would need 1MB of ram
+
+ // Now do MMU things
+ mmu.init();
+ mmu.DomainAccessControlRegister.set_all(.Manager);
+
+ try page_table.pt_switch(pt, 0x140E, 1);
+
+ mmu.sync_pte();
+ mmu.enable();
+
+ pi.switching.jump(mapped_kernel + memory.MB + new_main_offset, kernel_relocation);
+}
+
+fn new_main() noreturn {
+ uart.print("Relocated successfully!\n", .{});
+
+ const foo: u32 = 3;
+ uart.print("foo lives at 0x{X}\n", .{@intFromPtr(&foo)});
+
+ pi.reboot();
}