commit 6d2fe46133dbd3d4b2ffbac18a8fac5c4ad03314
parent 519bb1f2f4b188235b976c68c8576d4402d77bd6
Author: Sylvia Ivory <git@sivory.net>
Date: Fri, 13 Mar 2026 12:21:22 -0700
Relocate interrupts
Diffstat:
7 files changed, 117 insertions(+), 43 deletions(-)
diff --git a/Justfile b/Justfile
@@ -1,14 +1,19 @@
build program mode="Bootable":
if [ "{{ program }}" = "bootloader" ]; then \
zig build -Dmode=Bootloader -Dprogram=bootloader; \
- elif [ "{{ program }}" = "sylveos" ]; then \
- zig build; \
else \
zig build -Dmode={{ mode }} -Dprogram={{ program }}; \
fi
arm-none-eabi-objdump -m armv6 -D zig-out/bin/{{ program }}.elf > zig-out/bin/{{ program }}.s
du -h zig-out/bin/{{ program }}.elf
+check program="all":
+ if [ "{{ program }}" = "all" ]; then \
+ zig build -Drelease=false; \
+ else \
+ zig build -Dmode=Bootable -Drelease=false -Dprogram={{ program }}; \
+ fi \
+
install program sd-card="/dev/mmcblk0p1":
just build {{ program }} Standalone
udisksctl mount -b {{ sd-card }}
diff --git a/boot/root.zig b/boot/root.zig
@@ -14,9 +14,8 @@ 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(0);
}
export const _start = make(kmain, abort)._start;
@@ -45,7 +44,7 @@ export fn kmain() void {
// 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
@@ -26,6 +26,7 @@ fn pi_module_opts(target: std.Build.ResolvedTarget, optimize: std.builtin.Optimi
.omit_frame_pointer = true,
.single_threaded = true,
.no_builtin = true,
+ .pic = true,
};
}
@@ -104,42 +105,44 @@ fn build_pi(
b.getInstallStep().dependOn(&install_bin.step);
}
-fn build_program_inner(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, program: []const u8, mode: ?BinaryMode) !void {
+fn build_program(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, program: []const u8, mode: ?BinaryMode) !void {
const program_name = try std.fmt.allocPrint(b.allocator, "programs/{s}.zig", .{program});
defer b.allocator.free(program_name);
try build_pi(b, target, optimize, program, b.path(program_name), mode);
}
-fn build_program(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) !void {
- const program = b.option([]const u8, "program", "specify pi program") orelse {
- // Build all programs
-
- const path = try b.path("programs").getPath3(b, null).toString(b.allocator);
- defer b.allocator.free(path);
-
- var dir = try std.fs.openDirAbsolute(path, .{ .access_sub_paths = false, .iterate = true });
- defer dir.close();
+fn build_all_programs(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) !void {
+ const path = try b.path("programs").getPath3(b, null).toString(b.allocator);
+ defer b.allocator.free(path);
- var iter = dir.iterate();
- while (try iter.next()) |entry| {
- if (entry.kind != .file) continue;
- if (!std.mem.endsWith(u8, entry.name, ".zig")) continue;
- const name = entry.name[0..(entry.name.len - 4)];
+ var dir = try std.fs.openDirAbsolute(path, .{ .access_sub_paths = false, .iterate = true });
+ defer dir.close();
- try build_program_inner(b, target, .Debug, name, .Bootable);
- }
-
- return;
- };
+ var iter = dir.iterate();
+ while (try iter.next()) |entry| {
+ if (entry.kind != .file) continue;
+ if (!std.mem.endsWith(u8, entry.name, ".zig")) continue;
+ const name = entry.name[0..(entry.name.len - 4)];
- try build_program_inner(b, target, optimize, program, null);
+ try build_program(b, target, optimize, name, .Bootable);
+ }
}
pub fn build(b: *std.Build) !void {
const pi_target = pi_zero_target(b);
const optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseFast });
- try build_program(b, pi_target, optimize);
- try build_pi(b, pi_target, optimize, "sylveos", b.path("sylveos/root.zig"), .Bootable);
+ const program = b.option([]const u8, "program", "specify pi program") orelse {
+ try build_all_programs(b, pi_target, optimize);
+ try build_pi(b, pi_target, optimize, "sylveos", b.path("sylveos/root.zig"), .Bootable);
+
+ return;
+ };
+
+ if (std.mem.eql(u8, program, "sylveos")) {
+ try build_pi(b, pi_target, optimize, "sylveos", b.path("sylveos/root.zig"), null);
+ } else {
+ try build_program(b, pi_target, optimize, program, null);
+ }
}
diff --git a/pi/interrupts.zig b/pi/interrupts.zig
@@ -1,6 +1,8 @@
-const mem = @import("./mem.zig");
-const PSR = @import("./psr.zig").PSR;
+const uart = @import("devices/mini-uart.zig");
const switching = @import("./switching.zig");
+const system = @import("./system.zig");
+const PSR = @import("./psr.zig").PSR;
+const mem = @import("./mem.zig");
const BASE_ADDRESS: usize = mem.BASE_ADDRESS + 0xB200;
@@ -96,8 +98,6 @@ pub inline fn pending_peripheral_interrupt(i: PeripheralsInterrupt) bool {
}
fn empty(regs: Registers) void {
- const uart = @import("./devices/mini-uart.zig");
-
uart.print("unexpected interrupt\n", .{});
dump_registers(®s);
}
@@ -120,7 +120,6 @@ pub const Registers = struct {
// TODO; remove
pub fn dump_registers(regs: *const Registers) void {
- const uart = @import("devices/mini-uart.zig");
uart.print("Registers\n", .{});
for (regs.gp, 0..) |r, i| {
uart.print(" r{d} = 0x{X}\n", .{ i, r });
@@ -209,7 +208,10 @@ fn create_trampoline(name: []const u8, offset: []const u8, ret: []const u8) []co
".global " ++ name ++ "\n" ++
".type " ++ name ++ ", %function\n" ++
name ++ ":\n" ++
- " mov sp, 0x9000000\n" ++
+ " ldr sp, " ++ name ++ "_data\n" ++
+ " b " ++ name ++ "_skip\n" ++
+ " " ++ name ++ "_data: .word 0x09000000\n" ++
+ " " ++ name ++ "_skip:\n" ++
" push {r0-r12, lr}\n" ++
" sub lr, lr, #" ++ offset ++ "\n" ++
" mov r0, lr\n" ++
@@ -236,14 +238,15 @@ extern fn interrupt() void;
extern fn fast_interrupt() void;
// https://leiradel.github.io/2019/02/09/Initialization.html
-pub fn setup_exception_vector() void {
- var exception_vector: usize = 0;
+pub fn setup_exception_vector(base_address: u32) void {
+ var exception_vector = base_address;
for (0..8) |_| {
mem.put_u32(@ptrFromInt(exception_vector), 0xE59FF018); // ldr pc, [pc, #24]
exception_vector += @sizeOf(u32);
}
+ // These need to be absolute addresses
mem.put_u32(@ptrFromInt(exception_vector), @intFromPtr(&reset));
exception_vector += @sizeOf(u32);
@@ -267,7 +270,32 @@ pub fn setup_exception_vector() void {
mem.put_u32(@ptrFromInt(exception_vector), @intFromPtr(&fast_interrupt));
- mem.barrier(.Write);
+ // We've just inserted _new_ code
+ system.flush_self_modifying_code();
+}
+
+pub inline fn relocate_exception_vector(location: u32) void {
+ asm volatile ("MCR p15, 0, %[value], c12, c0, 0"
+ :
+ : [value] "r" (location),
+ );
+}
+
+pub inline fn rewrite_stacks(stack: u32) void {
+ // <exception>_data is at an 8 byte offset
+ const offset = 8;
+
+ // The first instruction of each function is the mov
+ mem.put_u32(@ptrFromInt(offset + @intFromPtr(&reset)), stack);
+ mem.put_u32(@ptrFromInt(offset + @intFromPtr(&undefined_instruction)), stack);
+ mem.put_u32(@ptrFromInt(offset + @intFromPtr(&software_interrupt)), stack);
+ mem.put_u32(@ptrFromInt(offset + @intFromPtr(&prefetch_abort)), stack);
+ mem.put_u32(@ptrFromInt(offset + @intFromPtr(&data_abort)), stack);
+ mem.put_u32(@ptrFromInt(offset + @intFromPtr(&interrupt)), stack);
+ mem.put_u32(@ptrFromInt(offset + @intFromPtr(&fast_interrupt)), stack);
+
+ // We've just _modified_ existing code
+ system.flush_self_modifying_code();
}
const ExceptionVector = enum {
diff --git a/pi/system.zig b/pi/system.zig
@@ -150,3 +150,13 @@ pub inline fn flush_btb() void {
: [value] "r" (0),
);
}
+
+// 2.7.2 (B2-22)
+pub inline fn flush_self_modifying_code() void {
+ clear_entire_data_cache();
+ mem.barrier(.Write);
+ invalidate_icache();
+ flush_btb();
+ mem.barrier(.Write);
+ mem.barrier(.Instruction);
+}
diff --git a/sylveos/memory.zig b/sylveos/memory.zig
@@ -111,6 +111,7 @@ pub fn print_regions() void {
\\ HEAP: 0x{X:0>8} - 0x{X:0>8} ({Bi})
\\ BCM2835 IO: 0x{X:0>8} - 0x{X:0>8} ({Bi})
\\
+ \\
, .{
memory.start, memory.end, memory.size,
program.start, program.end, program.size,
diff --git a/sylveos/root.zig b/sylveos/root.zig
@@ -4,6 +4,7 @@ const pi = @import("pi");
const memory = @import("./memory.zig");
const uart = pi.devices.mini_uart;
+const interrupts = pi.interrupts;
const procmap = pi.procmap;
const page_table = pi.pt;
const mmu = pi.mmu;
@@ -67,14 +68,41 @@ pub fn main() !void {
mmu.sync_pte();
mmu.enable();
- pi.switching.jump(mapped_kernel + memory.MB + new_main_offset, kernel_relocation);
+ pi.interrupts.disable_interrupts();
+ pi.switching.jump(kernel_relocation + 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)});
+ const base_address = Region.io().end + memory.MB;
+ const stack = base_address;
+ const interrupt_stack = base_address - (memory.MB / 2);
+
+ uart.print(
+ \\new_main: 0x{X:8>0}
+ \\ stack: 0x{X:8>0}
+ \\
+ \\
+ , .{ @intFromPtr(&new_main), stack });
+
+ // Now we need to:
+ // 1. Load the new exception handler
+ // We use p15/c12 to relocate it
+ // 2. Unload previous sections
+ // Old stack, old code, old interrupt stack
+ // 3. Reenable exceptions
+ // We do waste 2mb for kernel overhead which is unfortunate
+ // 1mb for code
+ // 1mb for stacks + root page table
+ interrupts.setup_exception_vector(base_address);
+ interrupts.relocate_exception_vector(base_address);
+ interrupts.rewrite_stacks(interrupt_stack);
+ interrupts.enable_interrupts();
+
+ uart.print(
+ \\interrupts: 0x{X:8>0}
+ \\ stack: 0x{X:8>0}
+ \\
+ , .{ base_address, interrupt_stack });
pi.reboot();
}