kernel/arm64: implement iframe stack and unwinding

Change-Id: I1587c1f57bd73777a188bb8f1bc58263de82fcb9
Reviewed-on: https://review.haiku-os.org/c/haiku/+/5684
Reviewed-by: Adrien Destugues <[email protected]>
Reviewed-by: David Karoly <[email protected]>
Tested-by: Commit checker robot <[email protected]>
This commit is contained in:
David Karoly
2022-09-23 13:56:16 +00:00
parent 139831c54c
commit a0c8f15f33
8 changed files with 146 additions and 10 deletions
@@ -133,6 +133,7 @@ struct iframe {
uint64 x[20]; uint64 x[20];
uint64 lr; uint64 lr;
uint64 sp; uint64 sp;
uint64 fp;
// exception info // exception info
uint64 esr; uint64 esr;
@@ -1,4 +1,5 @@
/* /*
* Copyright 2022, Haiku Inc. All rights reserved.
* Copyright 2018, Jaroslaw Pelczar <[email protected]> * Copyright 2018, Jaroslaw Pelczar <[email protected]>
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
@@ -13,6 +14,8 @@
extern "C" { extern "C" {
#endif #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) static inline Thread * arch_thread_get_current_thread(void)
{ {
@@ -1,4 +1,5 @@
/* /*
* Copyright 2022, Haiku Inc. All rights reserved.
* Copyright 2018, Jaroslaw Pelczar <[email protected]> * Copyright 2018, Jaroslaw Pelczar <[email protected]>
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
@@ -8,10 +9,20 @@
#include <kernel.h> #include <kernel.h>
#define IFRAME_TRACE_DEPTH 4
struct iframe_stack {
struct iframe *frames[IFRAME_TRACE_DEPTH];
int32 index;
};
struct arch_thread { struct arch_thread {
uint64 regs[13]; // x19-x30, sp uint64 regs[13]; // x19-x30, sp
uint64 fp_regs[8]; // d8-d15 uint64 fp_regs[8]; // d8-d15
// used to track interrupts on this thread
struct iframe_stack iframes;
}; };
struct arch_team { struct arch_team {
+4 -1
View File
@@ -41,9 +41,10 @@ sub sp, sp, \xt
mov x0, sp // original x19 that we swapped with sp mov x0, sp // original x19 that we swapped with sp
stp x18, x0, [x19, #(IFRAME_x + 18 * 8)] 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 // thus we don't really need to store these
str x29, [x19, #(IFRAME_fp)]
str x30, [x19, #(IFRAME_lr)] str x30, [x19, #(IFRAME_lr)]
.if \el == 0 .if \el == 0
@@ -83,6 +84,7 @@ sub sp, sp, \xt
ldp x14, x15, [x19, #(IFRAME_x + 14 * 8)] ldp x14, x15, [x19, #(IFRAME_x + 14 * 8)]
ldp x16, x17, [x19, #(IFRAME_x + 16 * 8)] ldp x16, x17, [x19, #(IFRAME_x + 16 * 8)]
// x18 and x19 will be restored later // x18 and x19 will be restored later
ldr x29, [x19, #(IFRAME_fp)]
ldr x30, [x19, #(IFRAME_lr)] ldr x30, [x19, #(IFRAME_lr)]
// disable interrupts before restoring ELR/SPSR/sp // disable interrupts before restoring ELR/SPSR/sp
@@ -119,6 +121,7 @@ sub sp, sp, \xt
// call C handler, passing IFRAME in x0 // call C handler, passing IFRAME in x0
// handler can enable interrupts if it wants to // handler can enable interrupts if it wants to
mov x0, x19 mov x0, x19
mov x29, x0
bl \func bl \func
EXCEPTION_RETURN \el EXCEPTION_RETURN \el
+42 -9
View File
@@ -19,6 +19,8 @@
#define NUM_PREVIOUS_LOCATIONS 32 #define NUM_PREVIOUS_LOCATIONS 32
extern struct iframe_stack gBootFrameStack;
static bool static bool
already_visited(addr_t* visited, int32* _last, int32* _num, addr_t fp) 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(); Thread* thread = thread_get_current_thread();
addr_t fp = arm64_get_fp(); addr_t fp = arm64_get_fp();
int32 num = 0, last = 0; 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) { if (thread != NULL) {
kprintf("stack trace for thread 0x%" B_PRIx32 " \"%s\"\n", thread->id, 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 <image>:function + offset\n"); kprintf("frame caller <image>:function + offset\n");
for (int32 callIndex = 0;; callIndex++) { for (int32 callIndex = 0;; callIndex++) {
addr_t ip, next; // see if the frame pointer matches the iframe
struct iframe *frame = NULL;
if (get_next_frame(fp, &next, &ip) != B_OK) { for (i = 0; i < frameStack->index; i++) {
kprintf("%08lx -- read fault\n", fp); if (fp == (addr_t)frameStack->frames[i]) {
break; // it's an iframe
frame = frameStack->frames[i];
break;
}
} }
if (ip == 0 || fp == 0) if (frame) {
break; 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); if (get_next_frame(fp, &next, &ip) != B_OK) {
fp = next; 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)) { if (already_visited(previousLocations, &last, &num, fp)) {
kprintf("circular stack frame: %p!\n", (void *)fp); kprintf("circular stack frame: %p!\n", (void *)fp);
+67
View File
@@ -33,6 +33,12 @@
# define TRACE(x) ; # define TRACE(x) ;
#endif #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 void
arch_int_enable_io_interrupt(int irq) 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 status_t
arch_int_init(kernel_args *args) 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 extern "C" void
do_sync_handler(iframe * frame) do_sync_handler(iframe * frame)
{ {
#ifdef TRACE_ARCH_INT_IFRAMES
print_iframe("Sync abort", frame);
#endif
IFrameScope scope(frame);
bool isExec = false; bool isExec = false;
switch (ESR_ELx_EXCEPTION(frame->esr)) { switch (ESR_ELx_EXCEPTION(frame->esr)) {
case EXCP_INSN_ABORT_L: case EXCP_INSN_ABORT_L:
@@ -313,6 +362,12 @@ do_sync_handler(iframe * frame)
extern "C" void extern "C" void
do_error_handler(iframe * frame) 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); 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 extern "C" void
do_irq_handler(iframe * frame) do_irq_handler(iframe * frame)
{ {
#ifdef TRACE_ARCH_INT_IFRAMES
print_iframe("IRQ", frame);
#endif
IFrameScope scope(frame);
InterruptController *ic = InterruptController::Get(); InterruptController *ic = InterruptController::Get();
if (ic != NULL) if (ic != NULL)
ic->HandleInterrupt(); ic->HandleInterrupt();
@@ -331,5 +392,11 @@ do_irq_handler(iframe * frame)
extern "C" void extern "C" void
do_fiq_handler(iframe * frame) do_fiq_handler(iframe * frame)
{ {
#ifdef TRACE_ARCH_INT_IFRAMES
print_iframe("FIQ", frame);
#endif
IFrameScope scope(frame);
panic("do_fiq_handler"); panic("do_fiq_handler");
} }
@@ -27,6 +27,22 @@
#endif #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 status_t
arch_thread_init(struct kernel_args *args) arch_thread_init(struct kernel_args *args)
{ {
@@ -1,4 +1,5 @@
/* /*
* Copyright 2022 Haiku, Inc. All Rights Reserved.
* Copyright 2007-2011, Ingo Weinhold, [email protected]. * Copyright 2007-2011, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
@@ -36,6 +37,7 @@ dummy()
DEFINE_OFFSET_MACRO(IFRAME, iframe, x); DEFINE_OFFSET_MACRO(IFRAME, iframe, x);
DEFINE_OFFSET_MACRO(IFRAME, iframe, lr); DEFINE_OFFSET_MACRO(IFRAME, iframe, lr);
DEFINE_OFFSET_MACRO(IFRAME, iframe, sp); DEFINE_OFFSET_MACRO(IFRAME, iframe, sp);
DEFINE_OFFSET_MACRO(IFRAME, iframe, fp);
DEFINE_OFFSET_MACRO(IFRAME, iframe, esr); DEFINE_OFFSET_MACRO(IFRAME, iframe, esr);
DEFINE_OFFSET_MACRO(IFRAME, iframe, far); DEFINE_OFFSET_MACRO(IFRAME, iframe, far);