interrupts.zig (10599B)
1 const uart = @import("devices/mini-uart.zig"); 2 const switching = @import("./switching.zig"); 3 const system = @import("./system.zig"); 4 const PSR = @import("./psr.zig").PSR; 5 const mem = @import("./mem.zig"); 6 const pi = @import("./root.zig"); 7 8 const BASE_ADDRESS: usize = mem.BASE_ADDRESS + 0xB200; 9 10 const IRQ_BASIC_PENDING: usize = BASE_ADDRESS + 0x00; 11 const IRQ_PENDING_1: usize = BASE_ADDRESS + 0x04; 12 const IRQ_PENDING_2: usize = BASE_ADDRESS + 0x08; 13 14 const IRQ_FIQ_CONTROL: usize = BASE_ADDRESS + 0x0C; 15 16 const IRQ_ENABLE_1: usize = BASE_ADDRESS + 0x10; 17 const IRQ_ENABLE_2: usize = BASE_ADDRESS + 0x14; 18 const IRQ_ENABLE_BASIC: usize = BASE_ADDRESS + 0x18; 19 20 const IRQ_DISABLE_1: usize = BASE_ADDRESS + 0x1C; 21 const IRQ_DISABLE_2: usize = BASE_ADDRESS + 0x20; 22 const IRQ_DISABLE_BASIC: usize = BASE_ADDRESS + 0x24; 23 24 pub inline fn enable_interrupts() void { 25 asm volatile ("cpsie i\n"); 26 } 27 28 pub inline fn disable_interrupts() void { 29 asm volatile ("cpsid i\n"); 30 } 31 32 pub inline fn clear_interrupt_flags() void { 33 mem.put_u32(@ptrFromInt(IRQ_DISABLE_1), ~@as(u32, 0)); 34 mem.put_u32(@ptrFromInt(IRQ_DISABLE_2), ~@as(u32, 0)); 35 mem.barrier(.Write); 36 } 37 38 const BasicInterrupt = enum(u8) { 39 Timer = 1 << 0, 40 Mailbox = 1 << 1, 41 Doorbell0 = 1 << 2, 42 Doorbell1 = 1 << 3, 43 GPU0Halted = 1 << 4, 44 GPU1Halted = 1 << 5, 45 AccessError1 = 1 << 6, 46 AccessError0 = 1 << 7, 47 }; 48 49 pub inline fn enable_basic_interrupt(i: BasicInterrupt) void { 50 mem.put_u32_barrier(@ptrFromInt(IRQ_ENABLE_BASIC), @intFromEnum(i)); 51 } 52 pub inline fn disable_basic_interrupt(i: BasicInterrupt) void { 53 mem.put_u32_barrier(@ptrFromInt(IRQ_DISABLE_BASIC), @intFromEnum(i)); 54 } 55 pub inline fn pending_basic(i: BasicInterrupt) bool { 56 return (mem.get_u32_barrier(@ptrFromInt(IRQ_BASIC_PENDING)) & @intFromEnum(i)) != 0; 57 } 58 59 const PeripheralsInterrupt = enum(u6) { 60 AuxInterrupt = 29, 61 I2CSPISLVInterrupt = 43, 62 PWA0 = 45, 63 PWA1 = 46, 64 SMI = 48, 65 GPIO0 = 49, 66 GPIO1 = 50, 67 GPIO2 = 51, 68 GPIO3 = 52, 69 I2CInt = 53, 70 SPIInt = 54, 71 PCMInt = 55, 72 UARTInt = 57, 73 }; 74 75 // TODO; abstract out GPIO's get_address 76 pub inline fn enable_peripheral_interrupt(i: PeripheralsInterrupt) void { 77 const v = @intFromEnum(i); 78 if (v < 32) { 79 mem.put_u32_barrier(@ptrFromInt(IRQ_ENABLE_1), 1 << v); 80 } else { 81 mem.put_u32_barrier(@ptrFromInt(IRQ_ENABLE_2), 1 << (v - 32)); 82 } 83 } 84 pub inline fn disable_peripheral_interrupt(i: PeripheralsInterrupt) void { 85 const v = @intFromEnum(i); 86 if (v < 32) { 87 mem.put_u32_barrier(@ptrFromInt(IRQ_DISABLE_1), 1 << v); 88 } else { 89 mem.put_u32_barrier(@ptrFromInt(IRQ_DISABLE_2), 1 << (v - 32)); 90 } 91 } 92 pub inline fn pending_peripheral_interrupt(i: PeripheralsInterrupt) bool { 93 const v = @intFromEnum(i); 94 if (v < 32) { 95 return mem.get_u32_barrier(@ptrFromInt(IRQ_PENDING_1)) & (1 << v) != 0; 96 } else { 97 return mem.get_u32_barrier(@ptrFromInt(IRQ_PENDING_2)) & (1 << (v - 32)) != 0; 98 } 99 } 100 101 fn empty(regs: Registers, vector: ExceptionVector) void { 102 uart.print("unexpected {s}\n", .{@tagName(vector)}); 103 dump_registers(®s); 104 } 105 106 var reset_handler: *const fn (Registers, ExceptionVector) void = empty; 107 var undefined_instruction_handler: *const fn (Registers, ExceptionVector) void = empty; 108 var software_interrupt_handler: *const fn (Registers, ExceptionVector) void = empty; 109 var prefetch_abort_handler: *const fn (Registers, ExceptionVector) void = empty; 110 var data_abort_handler: *const fn (Registers, ExceptionVector) void = empty; 111 var interrupt_handler: *const fn (Registers, ExceptionVector) void = empty; 112 var fast_interrupt_handler: *const fn (Registers, ExceptionVector) void = empty; 113 114 pub const Registers = struct { 115 gp: [13]usize, 116 sp: usize, 117 lr: usize, 118 pc: usize, 119 psr: PSR, 120 }; 121 122 // TODO; remove 123 pub fn dump_registers(regs: *const Registers) void { 124 uart.print("Registers\n", .{}); 125 for (regs.gp, 0..) |r, i| { 126 uart.print(" r{d} = 0x{X}\n", .{ i, r }); 127 } 128 uart.print(" sp = 0x{X}\n lr = 0x{X}\n pc = 0x{X}\n", .{ regs.sp, regs.lr, regs.pc }); 129 uart.print(" psr = {s}\n", .{@tagName(regs.psr.mode)}); 130 uart.flush(); 131 } 132 133 // For whatever godforsaken reason, inlining this function causes bad things to happen 134 noinline fn user_get_sp_lr(sp: *u32, lr: *u32) void { 135 asm volatile ( 136 \\ stm %[sp], {sp}^ 137 \\ stm %[lr], {lr}^ 138 : 139 : [sp] "r" (sp), 140 [lr] "r" (lr), 141 : .{ .memory = true }); 142 } 143 144 noinline fn privileged_get_sp_lr(sp: *u32, lr: *u32) void { 145 const cpsr = PSR.switch_current_mode(PSR.get_s().mode); 146 mem.barrier(.Instruction); 147 148 asm volatile ( 149 \\ str sp, [%[sp]] 150 \\ str lr, [%[lr]] 151 : 152 : [sp] "r" (sp), 153 [lr] "r" (lr), 154 : .{ .memory = true }); 155 156 cpsr.set_c(); 157 mem.barrier(.Instruction); 158 } 159 160 inline fn setup_reg(pc: u32, gp: *const [13]u32) Registers { 161 // We use a copy as if editing the registers was 162 // desired, using a restore_state would be better 163 var view: Registers = .{ .gp = undefined, .sp = undefined, .lr = undefined, .pc = pc, .psr = PSR.get_s() }; 164 165 @memcpy(&view.gp, gp); 166 167 if (view.psr.mode == .User or view.psr.mode == .System) { 168 user_get_sp_lr(&view.sp, &view.lr); 169 } else { 170 privileged_get_sp_lr(&view.sp, &view.lr); 171 } 172 173 return view; 174 } 175 176 // I still hate this 177 export fn reset_stub(pc: u32, gp: *const [13]u32) void { 178 reset_handler(setup_reg(pc, gp), .Reset); 179 } 180 181 export fn undefined_instruction_stub(pc: u32, gp: *const [13]u32) void { 182 undefined_instruction_handler(setup_reg(pc, gp), .UndefinedInstruction); 183 } 184 185 export fn software_interrupt_stub(pc: u32, gp: *const [13]u32) void { 186 software_interrupt_handler(setup_reg(pc, gp), .SoftwareInterrupt); 187 } 188 189 export fn prefetch_abort_stub(pc: u32, gp: *const [13]u32) void { 190 prefetch_abort_handler(setup_reg(pc, gp), .PrefetchAbort); 191 } 192 193 export fn data_abort_stub(pc: u32, gp: *const [13]u32) void { 194 data_abort_handler(setup_reg(pc, gp), .DataAbort); 195 } 196 197 export fn interrupt_stub(pc: u32, gp: *const [13]u32) void { 198 interrupt_handler(setup_reg(pc, gp), .IRQ); 199 } 200 201 // TODO; 202 export fn fast_interrupt_stub(pc: u32, reg: *Registers) void { 203 _ = pc; 204 _ = reg; 205 } 206 207 fn create_trampoline(name: []const u8, offset: []const u8, ret: []const u8) []const u8 { 208 return "" ++ 209 ".global " ++ name ++ "\n" ++ 210 ".type " ++ name ++ ", %function\n" ++ 211 name ++ ":\n" ++ 212 " ldr sp, " ++ name ++ "_data\n" ++ 213 " b " ++ name ++ "_skip\n" ++ 214 " " ++ name ++ "_data: .word 0\n" ++ 215 " " ++ name ++ "_skip:\n" ++ 216 " push {r0-r12, lr}\n" ++ 217 " sub lr, lr, #" ++ offset ++ "\n" ++ 218 " mov r0, lr\n" ++ 219 " mov r1, sp\n" ++ 220 " blx " ++ name ++ "_stub\n" ++ 221 " pop {r0-r12, lr}\n" ++ ret ++ "\n"; 222 } 223 224 comptime { 225 asm (create_trampoline("reset", "4", "")); // doesn't matter 226 asm (create_trampoline("undefined_instruction", "4", "movs pc, lr")); 227 asm (create_trampoline("software_interrupt", "4", "movs pc, lr")); 228 asm (create_trampoline("prefetch_abort", "4", "subs pc, lr, #4")); 229 asm (create_trampoline("data_abort", "8", "subs pc, lr, #8")); 230 asm (create_trampoline("interrupt", "4", "subs pc, lr, #4")); 231 asm (create_trampoline("fast_interrupt", "4", "subs pc, lr, #4")); 232 } 233 extern fn reset() void; 234 extern fn undefined_instruction() void; 235 extern fn software_interrupt() void; 236 extern fn prefetch_abort() void; 237 extern fn data_abort() void; 238 extern fn interrupt() void; 239 extern fn fast_interrupt() void; 240 241 // https://leiradel.github.io/2019/02/09/Initialization.html 242 pub fn setup_exception_vector(base_address: u32) void { 243 var exception_vector = base_address; 244 245 for (0..8) |_| { 246 mem.put_u32(@ptrFromInt(exception_vector), 0xE59FF018); // ldr pc, [pc, #24] 247 exception_vector += @sizeOf(u32); 248 } 249 250 // These need to be absolute addresses 251 mem.put_u32(@ptrFromInt(exception_vector), @intFromPtr(&reset)); 252 exception_vector += @sizeOf(u32); 253 254 mem.put_u32(@ptrFromInt(exception_vector), @intFromPtr(&undefined_instruction)); 255 exception_vector += @sizeOf(u32); 256 257 mem.put_u32(@ptrFromInt(exception_vector), @intFromPtr(&software_interrupt)); 258 exception_vector += @sizeOf(u32); 259 260 mem.put_u32(@ptrFromInt(exception_vector), @intFromPtr(&prefetch_abort)); 261 exception_vector += @sizeOf(u32); 262 263 mem.put_u32(@ptrFromInt(exception_vector), @intFromPtr(&data_abort)); 264 exception_vector += @sizeOf(u32); 265 266 mem.put_u32(@ptrFromInt(exception_vector), 0); 267 exception_vector += @sizeOf(u32); 268 269 mem.put_u32(@ptrFromInt(exception_vector), @intFromPtr(&interrupt)); 270 exception_vector += @sizeOf(u32); 271 272 mem.put_u32(@ptrFromInt(exception_vector), @intFromPtr(&fast_interrupt)); 273 274 rewrite_stacks(pi.get_interrupt_stack_address()); 275 276 // We've just inserted _new_ code 277 system.flush_self_modifying_code(); 278 } 279 280 pub inline fn relocate_exception_vector(location: u32) void { 281 asm volatile ("MCR p15, 0, %[value], c12, c0, 0" 282 : 283 : [value] "r" (location), 284 ); 285 } 286 287 pub inline fn rewrite_stacks(stack: u32) void { 288 // <exception>_data is at an 8 byte offset 289 const offset = 8; 290 291 // The first instruction of each function is the mov 292 mem.put_u32(@ptrFromInt(offset + @intFromPtr(&reset)), stack); 293 mem.put_u32(@ptrFromInt(offset + @intFromPtr(&undefined_instruction)), stack); 294 mem.put_u32(@ptrFromInt(offset + @intFromPtr(&software_interrupt)), stack); 295 mem.put_u32(@ptrFromInt(offset + @intFromPtr(&prefetch_abort)), stack); 296 mem.put_u32(@ptrFromInt(offset + @intFromPtr(&data_abort)), stack); 297 mem.put_u32(@ptrFromInt(offset + @intFromPtr(&interrupt)), stack); 298 mem.put_u32(@ptrFromInt(offset + @intFromPtr(&fast_interrupt)), stack); 299 300 // We've just _modified_ existing code 301 system.flush_self_modifying_code(); 302 } 303 304 pub const ExceptionVector = enum { 305 Reset, 306 UndefinedInstruction, 307 SoftwareInterrupt, 308 PrefetchAbort, 309 DataAbort, 310 IRQ, 311 FIQ, 312 }; 313 314 pub fn set_exception_handler(vector: ExceptionVector, handler: *const fn (Registers, ExceptionVector) void) void { 315 switch (vector) { 316 .Reset => reset_handler = handler, 317 .UndefinedInstruction => undefined_instruction_handler = handler, 318 .SoftwareInterrupt => software_interrupt_handler = handler, 319 .PrefetchAbort => prefetch_abort_handler = handler, 320 .DataAbort => data_abort_handler = handler, 321 .IRQ => interrupt_handler = handler, 322 .FIQ => fast_interrupt_handler = handler, 323 } 324 }