From a0c8f15f33de3b339067c9924a6ab126913b08e8 Mon Sep 17 00:00:00 2001 From: David Karoly Date: Tue, 20 Sep 2022 10:33:04 +0200 Subject: [PATCH] kernel/arm64: implement iframe stack and unwinding Change-Id: I1587c1f57bd73777a188bb8f1bc58263de82fcb9 Reviewed-on: https://review.haiku-os.org/c/haiku/+/5684 Reviewed-by: Adrien Destugues Reviewed-by: David Karoly Tested-by: Commit checker robot --- headers/private/kernel/arch/arm64/arch_cpu.h | 1 + .../private/kernel/arch/arm64/arch_thread.h | 3 + .../kernel/arch/arm64/arch_thread_types.h | 11 +++ src/system/kernel/arch/arm64/arch_asm.S | 5 +- src/system/kernel/arch/arm64/arch_debug.cpp | 51 +++++++++++--- src/system/kernel/arch/arm64/arch_int.cpp | 67 +++++++++++++++++++ src/system/kernel/arch/arm64/arch_thread.cpp | 16 +++++ src/system/kernel/arch/arm64/asm_offsets.cpp | 2 + 8 files changed, 146 insertions(+), 10 deletions(-) diff --git a/headers/private/kernel/arch/arm64/arch_cpu.h b/headers/private/kernel/arch/arm64/arch_cpu.h index 9072060e1e..53a77cddcd 100644 --- a/headers/private/kernel/arch/arm64/arch_cpu.h +++ b/headers/private/kernel/arch/arm64/arch_cpu.h @@ -133,6 +133,7 @@ struct iframe { uint64 x[20]; uint64 lr; uint64 sp; + uint64 fp; // exception info uint64 esr; diff --git a/headers/private/kernel/arch/arm64/arch_thread.h b/headers/private/kernel/arch/arm64/arch_thread.h index 9022e82599..e8fe673b9c 100644 --- a/headers/private/kernel/arch/arm64/arch_thread.h +++ b/headers/private/kernel/arch/arm64/arch_thread.h @@ -1,4 +1,5 @@ /* + * Copyright 2022, Haiku Inc. All rights reserved. * Copyright 2018, Jaroslaw Pelczar * Distributed under the terms of the MIT License. */ @@ -13,6 +14,8 @@ extern "C" { #endif +void arm64_push_iframe(struct iframe_stack *stack, struct iframe *frame); +void arm64_pop_iframe(struct iframe_stack *stack); static inline Thread * arch_thread_get_current_thread(void) { diff --git a/headers/private/kernel/arch/arm64/arch_thread_types.h b/headers/private/kernel/arch/arm64/arch_thread_types.h index 899c89399a..fe1835ae2d 100644 --- a/headers/private/kernel/arch/arm64/arch_thread_types.h +++ b/headers/private/kernel/arch/arm64/arch_thread_types.h @@ -1,4 +1,5 @@ /* + * Copyright 2022, Haiku Inc. All rights reserved. * Copyright 2018, Jaroslaw Pelczar * Distributed under the terms of the MIT License. */ @@ -8,10 +9,20 @@ #include +#define IFRAME_TRACE_DEPTH 4 + +struct iframe_stack { + struct iframe *frames[IFRAME_TRACE_DEPTH]; + int32 index; +}; + struct arch_thread { uint64 regs[13]; // x19-x30, sp uint64 fp_regs[8]; // d8-d15 + + // used to track interrupts on this thread + struct iframe_stack iframes; }; struct arch_team { diff --git a/src/system/kernel/arch/arm64/arch_asm.S b/src/system/kernel/arch/arm64/arch_asm.S index e1c884266d..0f25ccf7e8 100644 --- a/src/system/kernel/arch/arm64/arch_asm.S +++ b/src/system/kernel/arch/arm64/arch_asm.S @@ -41,9 +41,10 @@ sub sp, sp, \xt mov x0, sp // original x19 that we swapped with sp stp x18, x0, [x19, #(IFRAME_x + 18 * 8)] - // x20-x29 won't be clobbered + // x20-x28 won't be clobbered // thus we don't really need to store these + str x29, [x19, #(IFRAME_fp)] str x30, [x19, #(IFRAME_lr)] .if \el == 0 @@ -83,6 +84,7 @@ sub sp, sp, \xt ldp x14, x15, [x19, #(IFRAME_x + 14 * 8)] ldp x16, x17, [x19, #(IFRAME_x + 16 * 8)] // x18 and x19 will be restored later + ldr x29, [x19, #(IFRAME_fp)] ldr x30, [x19, #(IFRAME_lr)] // disable interrupts before restoring ELR/SPSR/sp @@ -119,6 +121,7 @@ sub sp, sp, \xt // call C handler, passing IFRAME in x0 // handler can enable interrupts if it wants to mov x0, x19 + mov x29, x0 bl \func EXCEPTION_RETURN \el diff --git a/src/system/kernel/arch/arm64/arch_debug.cpp b/src/system/kernel/arch/arm64/arch_debug.cpp index 15218cab7d..355dd3f0ea 100644 --- a/src/system/kernel/arch/arm64/arch_debug.cpp +++ b/src/system/kernel/arch/arm64/arch_debug.cpp @@ -19,6 +19,8 @@ #define NUM_PREVIOUS_LOCATIONS 32 +extern struct iframe_stack gBootFrameStack; + static bool already_visited(addr_t* visited, int32* _last, int32* _num, addr_t fp) @@ -348,6 +350,19 @@ stack_trace(int argc, char **argv) Thread* thread = thread_get_current_thread(); addr_t fp = arm64_get_fp(); int32 num = 0, last = 0; + struct iframe_stack *frameStack; + + // We don't have a thread pointer early in the boot process + if (thread != NULL) + frameStack = &thread->arch_info.iframes; + else + frameStack = &gBootFrameStack; + + int32 i; + for (i = 0; i < frameStack->index; i++) { + kprintf("iframe %p (end = %p)\n", + frameStack->frames[i], frameStack->frames[i] + 1); + } if (thread != NULL) { kprintf("stack trace for thread 0x%" B_PRIx32 " \"%s\"\n", thread->id, @@ -366,19 +381,37 @@ stack_trace(int argc, char **argv) kprintf("frame caller :function + offset\n"); for (int32 callIndex = 0;; callIndex++) { - addr_t ip, next; - - if (get_next_frame(fp, &next, &ip) != B_OK) { - kprintf("%08lx -- read fault\n", fp); - break; + // see if the frame pointer matches the iframe + struct iframe *frame = NULL; + for (i = 0; i < frameStack->index; i++) { + if (fp == (addr_t)frameStack->frames[i]) { + // it's an iframe + frame = frameStack->frames[i]; + break; + } } - if (ip == 0 || fp == 0) - break; + if (frame) { + kprintf("iframe at %p\n", frame); + dprintf("ELR=%016lx SPSR=%016lx\n", frame->elr, frame->spsr); + dprintf("LR =%016lx SP =%016lx FP =%016lx\n", frame->lr, frame->sp, frame->fp); + dprintf("ESR=%016lx FAR =%016lx\n", frame->esr, frame->far); + print_stack_frame(thread, frame->elr, fp, frame->fp, callIndex, demangle); + fp = frame->fp; + } else { + addr_t ip, next; - print_stack_frame(thread, ip, fp, next, callIndex, demangle); - fp = next; + if (get_next_frame(fp, &next, &ip) != B_OK) { + kprintf("%08lx -- read fault\n", fp); + break; + } + if (ip == 0 || fp == 0) + break; + + print_stack_frame(thread, ip, fp, next, callIndex, demangle); + fp = next; + } if (already_visited(previousLocations, &last, &num, fp)) { kprintf("circular stack frame: %p!\n", (void *)fp); diff --git a/src/system/kernel/arch/arm64/arch_int.cpp b/src/system/kernel/arch/arm64/arch_int.cpp index a3197c13c3..07f4ab9c97 100644 --- a/src/system/kernel/arch/arm64/arch_int.cpp +++ b/src/system/kernel/arch/arm64/arch_int.cpp @@ -33,6 +33,12 @@ # define TRACE(x) ; #endif +//#define TRACE_ARCH_INT_IFRAMES + +// An iframe stack used in the early boot process when we don't have +// threads yet. +struct iframe_stack gBootFrameStack; + void arch_int_enable_io_interrupt(int irq) @@ -60,6 +66,19 @@ arch_int_assign_to_cpu(int32 irq, int32 cpu) } +static void +print_iframe(const char *event, struct iframe *frame) +{ + if (event) + dprintf("Exception: %s\n", event); + + dprintf("ELR=%016lx SPSR=%016lx\n", + frame->elr, frame->spsr); + dprintf("LR=%016lx SP =%016lx\n", + frame->lr, frame->sp); +} + + status_t arch_int_init(kernel_args *args) { @@ -164,9 +183,39 @@ after_exception() } +// Little helper class for handling the +// iframe stack as used by KDL. +class IFrameScope { +public: + IFrameScope(struct iframe *iframe) { + fThread = thread_get_current_thread(); + if (fThread) + arm64_push_iframe(&fThread->arch_info.iframes, iframe); + else + arm64_push_iframe(&gBootFrameStack, iframe); + } + + virtual ~IFrameScope() { + // pop iframe + if (fThread) + arm64_pop_iframe(&fThread->arch_info.iframes); + else + arm64_pop_iframe(&gBootFrameStack); + } +private: + Thread* fThread; +}; + + extern "C" void do_sync_handler(iframe * frame) { +#ifdef TRACE_ARCH_INT_IFRAMES + print_iframe("Sync abort", frame); +#endif + + IFrameScope scope(frame); + bool isExec = false; switch (ESR_ELx_EXCEPTION(frame->esr)) { case EXCP_INSN_ABORT_L: @@ -313,6 +362,12 @@ do_sync_handler(iframe * frame) extern "C" void do_error_handler(iframe * frame) { +#ifdef TRACE_ARCH_INT_IFRAMES + print_iframe("Error", frame); +#endif + + IFrameScope scope(frame); + panic("unhandled error! FAR=%lx ELR=%lx ESR=%lx", frame->far, frame->elr, frame->esr); } @@ -320,6 +375,12 @@ do_error_handler(iframe * frame) extern "C" void do_irq_handler(iframe * frame) { +#ifdef TRACE_ARCH_INT_IFRAMES + print_iframe("IRQ", frame); +#endif + + IFrameScope scope(frame); + InterruptController *ic = InterruptController::Get(); if (ic != NULL) ic->HandleInterrupt(); @@ -331,5 +392,11 @@ do_irq_handler(iframe * frame) extern "C" void do_fiq_handler(iframe * frame) { +#ifdef TRACE_ARCH_INT_IFRAMES + print_iframe("FIQ", frame); +#endif + + IFrameScope scope(frame); + panic("do_fiq_handler"); } diff --git a/src/system/kernel/arch/arm64/arch_thread.cpp b/src/system/kernel/arch/arm64/arch_thread.cpp index 4d9108ca9a..2e210e20d5 100644 --- a/src/system/kernel/arch/arm64/arch_thread.cpp +++ b/src/system/kernel/arch/arm64/arch_thread.cpp @@ -27,6 +27,22 @@ #endif +void +arm64_push_iframe(struct iframe_stack *stack, struct iframe *frame) +{ + ASSERT(stack->index < IFRAME_TRACE_DEPTH); + stack->frames[stack->index++] = frame; +} + + +void +arm64_pop_iframe(struct iframe_stack *stack) +{ + ASSERT(stack->index > 0); + stack->index--; +} + + status_t arch_thread_init(struct kernel_args *args) { diff --git a/src/system/kernel/arch/arm64/asm_offsets.cpp b/src/system/kernel/arch/arm64/asm_offsets.cpp index 2dfe84f84e..9493980390 100644 --- a/src/system/kernel/arch/arm64/asm_offsets.cpp +++ b/src/system/kernel/arch/arm64/asm_offsets.cpp @@ -1,4 +1,5 @@ /* + * Copyright 2022 Haiku, Inc. All Rights Reserved. * Copyright 2007-2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ @@ -36,6 +37,7 @@ dummy() DEFINE_OFFSET_MACRO(IFRAME, iframe, x); DEFINE_OFFSET_MACRO(IFRAME, iframe, lr); DEFINE_OFFSET_MACRO(IFRAME, iframe, sp); + DEFINE_OFFSET_MACRO(IFRAME, iframe, fp); DEFINE_OFFSET_MACRO(IFRAME, iframe, esr); DEFINE_OFFSET_MACRO(IFRAME, iframe, far);