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:
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user