sylveos

Toy Operating System
Log | Files | Refs

commit 519bb1f2f4b188235b976c68c8576d4402d77bd6
parent 4710c97f45222dbe59c29f380461ce882093ea8e
Author: Sylvia Ivory <git@sivory.net>
Date:   Fri, 13 Mar 2026 09:39:53 -0700

Relocate above IO

Diffstat:
Mboot/root.zig | 12++++++------
Mbuild.zig | 1+
Mpi/interrupts.zig | 2+-
Mpi/pinned.zig | 11+++++++++++
Mpi/procmap.zig | 4++--
Mpi/pt.zig | 4++--
Mpi/switching.zig | 10++++++++++
Msylveos/memory.zig | 147+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msylveos/root.zig | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
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(); }