From 58ce7d9a6ad03a870835b34ed88bce84b4f4611a Mon Sep 17 00:00:00 2001 From: markfirmware Date: Wed, 8 Jul 2020 19:43:08 +0000 Subject: [PATCH] Better exception handling --- src/kernel/arch/aarch64/boot.zig | 4 +- src/kernel/arch/aarch64/interrupts.zig | 182 +++++++++++++++++++------ src/kernel/tty.zig | 1 + 3 files changed, 147 insertions(+), 40 deletions(-) diff --git a/src/kernel/arch/aarch64/boot.zig b/src/kernel/arch/aarch64/boot.zig index 56b8f1af..a3f1f9d0 100644 --- a/src/kernel/arch/aarch64/boot.zig +++ b/src/kernel/arch/aarch64/boot.zig @@ -25,9 +25,9 @@ export fn _start() linksection(".text.boot") callconv(.Naked) noreturn { // Setup the exception table asm volatile ( - \\msr vbar_el3, %[table_addr] - \\msr vbar_el2, %[table_addr] \\msr vbar_el1, %[table_addr] + \\msr vbar_el2, %[table_addr] + \\msr vbar_el3, %[table_addr] : : [table_addr] "r" (@ptrToInt(&interrupts.exception_table)) ); diff --git a/src/kernel/arch/aarch64/interrupts.zig b/src/kernel/arch/aarch64/interrupts.zig index dae011cb..52207f96 100644 --- a/src/kernel/arch/aarch64/interrupts.zig +++ b/src/kernel/arch/aarch64/interrupts.zig @@ -6,70 +6,176 @@ pub extern var exception_table: *usize; comptime { asm ( \\.globl exception_table - \\exception_table: \\.balign 0x800 + \\exception_table: + \\.balign 0x80 + \\ bl exceptionHandler + \\.balign 0x80 + \\ bl exceptionHandler + \\.balign 0x80 + \\ bl exceptionHandler + \\.balign 0x80 + \\ bl exceptionHandler + \\.balign 0x80 + \\ bl exceptionHandler \\.balign 0x80 - \\ b resetHandler + \\ bl exceptionHandler \\.balign 0x80 - \\ b undefinedInstructionHandler + \\ bl exceptionHandler \\.balign 0x80 - \\ b softwareInterruptHandler + \\ bl exceptionHandler \\.balign 0x80 - \\ b prefetchAbortHandler + \\ bl exceptionHandler \\.balign 0x80 - \\ b dataAbortHandler + \\ bl exceptionHandler \\.balign 0x80 - \\ // Reserved - \\ b reservedHandler + \\ bl exceptionHandler \\.balign 0x80 - \\ b irqHandler + \\ bl exceptionHandler \\.balign 0x80 - \\ b fastIrqHandler + \\ bl exceptionHandler + \\.balign 0x80 + \\ bl exceptionHandler + \\.balign 0x80 + \\ bl exceptionHandler + \\.balign 0x80 + \\ bl exceptionHandler ); } -inline fn interruptedInstruction() usize { - return asm ("mrs %[res], ELR_EL3" - : [res] "=r" (-> usize) - ); -} +const ExceptionTakenFrom = enum(u2) { + same_level_while_using_sp_el0, + same_level_while_using_sp_elx, + lower_level_aarch64, + lower_level_aarch32, +}; + +const ExceptionCategory = enum(u2) { + synchronous, + irq_or_virq, + fiq_or_vfiq, + serror_or_vserror, +}; + +const ExceptionClass = enum(u6) { + instruction_abort = 0x21, + data_abort = 0x25, + sp_alignment = 0x26, + _, +}; -export fn resetHandler() callconv(.Naked) noreturn { - log.logError("rest at instruction 0x{X}\n", .{interruptedInstruction()}); +export fn exceptionHandler() noreturn { + const exception_entry_offset = @truncate(u32, lr() & 0x780); + var elr_elx: usize = undefined; + var esr_elx: usize = undefined; + var far_elx: usize = undefined; + var sctlr_elx: usize = undefined; + var spsr_elx: usize = undefined; + var vbar_elx: usize = undefined; + inline for ([_]u32{ 1, 2, 3 }) |exception_level| { + if (exception_level == currentExceptionLevel()) { + elr_elx = mrsEl("elr_el", exception_level); + esr_elx = mrsEl("esr_el", exception_level); + far_elx = mrsEl("far_el", exception_level); + sctlr_elx = mrsEl("sctlr_el", exception_level); + spsr_elx = mrsEl("spsr_el", exception_level); + vbar_elx = mrsEl("vbar_el", exception_level); + } + } + const esr_elx_class = @intToEnum(ExceptionClass, @truncate(u6, esr_elx >> 26)); + const esr_elx_instruction_is_32_bits = esr_elx & 0x2000000 != 0; + const esr_elx_iss = esr_elx & 0x1ffffff; + log.logError("\n", .{}); + log.logError("arm exception taken to level {}\n", .{currentExceptionLevel()}); + var seen_previously = false; + if (currentExceptionLevel() == 3 and exception_entry_offset == 0x200 and esr_elx_instruction_is_32_bits) { + switch (esr_elx_class) { + .data_abort => { + switch (esr_elx_iss) { + 0x0 => { + seen_previously = true; + log.logError("this exception has been seen previously in development\n", .{}); + log.logError(" data abort in level {} (while using sp_el{} and not sp_el0)\n", .{ currentExceptionLevel(), currentExceptionLevel() }); + log.logError(" 32 bit instruction at 0x{x} accessing 0x{x}\n", .{ elr_elx, far_elx }); + }, + else => {}, + } + }, + .instruction_abort => { + switch (esr_elx_iss) { + 0x0, 0x10 => { + seen_previously = true; + log.logError("this exception has been seen previously in development\n", .{}); + log.logError(" instruction abort (variant: esr_el{}.iss = 0x{x}) in level {} (while using sp_el{} and not sp_el0)\n", .{ currentExceptionLevel(), esr_elx_iss, currentExceptionLevel(), currentExceptionLevel() }); + log.logError(" 32 bit instruction at 0x{x} accessing 0x{x}\n", .{ elr_elx, far_elx }); + }, + else => {}, + } + }, + else => {}, + } + } + if (!seen_previously) { + log.logError("this exception has not been seen previously in development - please update aarch64/interrupts.zig\n", .{}); + } + log.logError("details\n", .{}); + log.logError(" elr_el{} 0x{x}\n", .{ currentExceptionLevel(), elr_elx }); + log.logError(" esr_el{} 0x{x}: {}, 32 bit instruction {}, iss 0x{x}\n", .{ currentExceptionLevel(), esr_elx, esr_elx_class, esr_elx_instruction_is_32_bits, esr_elx_iss }); + log.logError(" exception entry offset 0x{x} {} {}\n", .{ exception_entry_offset, @intToEnum(ExceptionTakenFrom, @truncate(u2, exception_entry_offset >> 9)), @intToEnum(ExceptionCategory, @truncate(u2, exception_entry_offset >> 7)) }); + log.logError(" far_el{} 0x{x}\n", .{ currentExceptionLevel(), far_elx }); + log.logError(" sctlr_el{} 0x{x}\n", .{ currentExceptionLevel(), sctlr_elx }); + log.logError(" spsr_el{} 0x{x}\n", .{ currentExceptionLevel(), spsr_elx }); + log.logError(" vbar_el{} 0x{x}\n", .{ currentExceptionLevel(), vbar_elx }); while (true) {} } -export fn undefinedInstructionHandler() callconv(.Naked) noreturn { - log.logError("undefined instruction at instruction 0x{X}\n", .{interruptedInstruction()}); - while (true) {} +inline fn lr() usize { + return register("lr"); } -export fn softwareInterruptHandler() callconv(.Naked) noreturn { - log.logError("software interrupt at instruction 0x{X}\n", .{interruptedInstruction()}); - while (true) {} +inline fn sp() usize { + return register("sp"); } -export fn prefetchAbortHandler() callconv(.Naked) noreturn { - log.logError("prefetch abort at instruction 0x{X}\n", .{interruptedInstruction()}); - while (true) {} +fn cpsr() usize { + return mrs("cpsr"); } -export fn dataAbortHandler() callconv(.Naked) noreturn { - log.logError("data abort at instruction 0x{X}\n", .{interruptedInstruction()}); - while (true) {} +fn spsr() usize { + return mrs("spsr"); } -export fn reservedHandler() callconv(.Naked) noreturn { - log.logError("reserved exception at instruction 0x{X}\n", .{interruptedInstruction()}); - while (true) {} +fn sctlr() usize { + var word = asm ("mrc p15, 0, %[word], c1, c0, 0" + : [word] "=r" (-> usize) + ); + return word; } -export fn irqHandler() callconv(.Naked) noreturn { - log.logError("irq at instruction 0x{X}\n", .{interruptedInstruction()}); - while (true) {} +inline fn mrs(comptime register_name: []const u8) usize { + const word = asm ("mrs %[word], " ++ register_name + : [word] "=r" (-> usize) + ); + return word; } -export fn fastIrqHandler() callconv(.Naked) noreturn { - log.logError("fast irq at instruction 0x{X}\n", .{interruptedInstruction()}); - while (true) {} +inline fn register(comptime register_name: []const u8) usize { + const word = asm ("mov %[word], " ++ register_name + : [word] "=r" (-> usize) + ); + return word; +} + +inline fn mrsEl(comptime register_name: []const u8, comptime exception_level: u32) usize { + const exception_level_string = switch (exception_level) { + 1 => "1", + 2 => "2", + 3 => "3", + else => unreachable, + }; + return mrs(register_name ++ exception_level_string); +} + +pub inline fn currentExceptionLevel() u2 { + return @truncate(u2, mrs("CurrentEL") >> 2); } diff --git a/src/kernel/tty.zig b/src/kernel/tty.zig index 0d54f03f..6c2ec33b 100644 --- a/src/kernel/tty.zig +++ b/src/kernel/tty.zig @@ -60,6 +60,7 @@ pub fn print(comptime format: []const u8, args: var) void { /// Clear the screen by printing a space at each cursor position. Sets the cursor to the top left (0, 0) /// pub fn clear() void { + log.logInfo("tty.clear() aarch64.tty.clear = {}\n", .{tty.clear}); // testing: note undefined value if (tty.clear) |clr| { clr(); } else {