Skip to content

Commit

Permalink
aarch64: improve unexpected exception handling
Browse files Browse the repository at this point in the history
Debugging scenarios when OSv crashes due to an unexpected exception
can be quite tedious given they are handled by entry_invalid which
simply makes kernel "hang" waiting for an interrupt. One needs to connect
with gdb and introspect registers to make sense of what happenned.

This patch improves the unexpected exception handling by defining
proper handlers for each exception level and exception type. When exception
is triggered corresponding handler prints exception type, exception level and
all registers and aborts potentially printing a backtrace.

Signed-off-by: Waldemar Kozaczuk <[email protected]>
Message-Id: <[email protected]>
  • Loading branch information
wkozaczuk authored and nyh committed Mar 14, 2022
1 parent cfdd501 commit 06ad9a2
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 52 deletions.
125 changes: 77 additions & 48 deletions arch/aarch64/entry.S
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
Lower Exception level, from AArch32 0x600 0x680 0x700 0x780
*/

.macro vector_entry label idx
.macro vector_entry level, type
/* every entry is at 2^7 bits distance */
.align 7
b \label
b entry_\level\()_\type
.endm

.global exception_vectors
Expand All @@ -34,28 +34,28 @@
.align 12
exception_vectors:
/* Current Exception level with SP_EL0 : unused */
vector_entry entry_invalid 0 // Synchronous
vector_entry entry_invalid 1 // IRQ or vIRQ
vector_entry entry_invalid 2 // FIQ or vFIQ
vector_entry entry_invalid 3 // SError or vSError
vector_entry curr_el_sp0 sync // Synchronous
vector_entry curr_el_sp0 irq // IRQ or vIRQ
vector_entry curr_el_sp0 fiq // FIQ or vFIQ
vector_entry curr_el_sp0 serror // SError or vSError

/* Current Exception level with SP_ELx : only actually used */
vector_entry entry_sync 4
vector_entry entry_irq 5
vector_entry entry_fiq 6
vector_entry entry_serror 7
vector_entry curr_el_spx sync
vector_entry curr_el_spx irq
vector_entry curr_el_spx fiq
vector_entry curr_el_spx serror

/* Lower Exception level in AArch64 : unused since we don't go to EL0 */
vector_entry entry_invalid 8
vector_entry entry_invalid 9
vector_entry entry_invalid 10
vector_entry entry_invalid 11
vector_entry lower_el_aarch64 sync
vector_entry lower_el_aarch64 irq
vector_entry lower_el_aarch64 fiq
vector_entry lower_el_aarch64 serror

/* Lower Exception level in AArch32 : no El0, no AArch32 */
vector_entry entry_invalid 12
vector_entry entry_invalid 13
vector_entry entry_invalid 14
vector_entry entry_invalid 15
vector_entry lower_el_aarch32 sync
vector_entry lower_el_aarch32 irq
vector_entry lower_el_aarch32 fiq
vector_entry lower_el_aarch32 serror

/* keep in sync with the struct in exceptions.hh */
.macro push_state_to_exception_frame
Expand Down Expand Up @@ -131,24 +131,61 @@ thread_main:
.equ ESR_FLT_BEG,2 // we strip LL
.equ ESR_FLT_END,5

.global entry_invalid
.hidden entry_invalid
.type entry_invalid, @function
entry_invalid:
mrs x20, elr_el1 // Exception Link Register -> X20
mrs x21, spsr_el1 // Saved PSTATE -> X21
mrs x22, esr_el1 // Exception Syndrome Register -> X22
.macro entry_unexpected_exception level, type, level_id, type_id
.global entry_\level\()_\type
.hidden entry_\level\()_\type
.type entry_\level\()_\type, @function
entry_\level\()_\type:
.cfi_startproc simple
.cfi_signal_frame
.cfi_def_cfa sp, 0
.cfi_offset x30, -32 // Point to the elr register located at the -32 offset
// of the exception frame to help gdb link to the
// address when interrupt was raised
push_state_to_exception_frame
mrs x1, esr_el1
str w1, [sp, #272] // Store Exception Syndrom Register in the frame
mov x0, sp // Save exception_frame to x0
mov x1, \level_id
mov x2, \type_id
bl handle_unexpected_exception
pop_state_from_exception_frame
bl abort
.cfi_endproc
.endm

.equ CURR_EL_SP0, 0x0
.equ CURR_EL_SPX, 0x1
.equ LOWER_EL_AARCH64, 0x2
.equ LOWER_EL_AARCH32, 0x3

ubfm x23, x22, #ESR_EC_BEG, #ESR_EC_END // Exception Class -> X23
ubfm x24, x22, #ESR_ISS_BEG, #ESR_ISS_END // Instruction-Specific Syndrome -> X24
.equ EX_TYPE_SYNC, 0x0
.equ EX_TYPE_IRQ, 0x1
.equ EX_TYPE_FIQ, 0x2
.equ EX_TYPE_SERROR, 0x3

1: wfi
b 1b
entry_unexpected_exception curr_el_sp0, sync, #CURR_EL_SP0, #EX_TYPE_SYNC
entry_unexpected_exception curr_el_sp0, irq, #CURR_EL_SP0, #EX_TYPE_IRQ
entry_unexpected_exception curr_el_sp0, fiq, #CURR_EL_SP0, #EX_TYPE_FIQ
entry_unexpected_exception curr_el_sp0, serror, #CURR_EL_SP0, #EX_TYPE_SERROR

.global entry_sync
.hidden entry_sync
.type entry_sync, @function
entry_sync:
entry_unexpected_exception curr_el_spx, fiq, #CURR_EL_SPX, #EX_TYPE_FIQ
entry_unexpected_exception curr_el_spx, serror, #CURR_EL_SPX, #EX_TYPE_SERROR

entry_unexpected_exception lower_el_aarch64, sync, #LOWER_EL_AARCH64, #EX_TYPE_SYNC
entry_unexpected_exception lower_el_aarch64, irq, #LOWER_EL_AARCH64, #EX_TYPE_IRQ
entry_unexpected_exception lower_el_aarch64, fiq, #LOWER_EL_AARCH64, #EX_TYPE_FIQ
entry_unexpected_exception lower_el_aarch64, serror, #LOWER_EL_AARCH64, #EX_TYPE_SERROR

entry_unexpected_exception lower_el_aarch32, sync, #LOWER_EL_AARCH32, #EX_TYPE_SYNC
entry_unexpected_exception lower_el_aarch32, irq, #LOWER_EL_AARCH32, #EX_TYPE_IRQ
entry_unexpected_exception lower_el_aarch32, fiq, #LOWER_EL_AARCH32, #EX_TYPE_FIQ
entry_unexpected_exception lower_el_aarch32, serror, #LOWER_EL_AARCH32, #EX_TYPE_SERROR

.global entry_curr_el_spx_sync
.hidden entry_curr_el_spx_sync
.type entry_curr_el_spx_sync, @function
entry_curr_el_spx_sync:
.cfi_startproc simple
.cfi_signal_frame
.cfi_def_cfa sp, 0
Expand Down Expand Up @@ -177,15 +214,17 @@ handle_mem_abort:
unexpected_sync_exception:
.cfi_startproc
mov x0, sp // save exception_frame to x0
bl handle_unexpected_sync_exception
mov x1, #CURR_EL_SPX
mov x2, #EX_TYPE_SYNC
bl handle_unexpected_exception
pop_state_from_exception_frame
bl abort
.cfi_endproc

.global entry_irq
.hidden entry_irq
.type entry_irq, @function
entry_irq:
.global entry_curr_el_spx_irq
.hidden entry_curr_el_spx_irq
.type entry_curr_el_spx_irq, @function
entry_curr_el_spx_irq:
.cfi_startproc simple
.cfi_signal_frame
.cfi_def_cfa sp, 0
Expand All @@ -199,16 +238,6 @@ entry_irq:
eret
.cfi_endproc

.global entry_fiq
.hidden entry_fiq
.type entry_fiq, @function
entry_fiq:
.global entry_serror
.hidden entry_serror
.type entry_serror, @function
entry_serror:
b entry_invalid

.global call_signal_handler_thunk
.hidden call_signal_handler_thunk
call_signal_handler_thunk:
Expand Down
30 changes: 26 additions & 4 deletions arch/aarch64/exceptions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -173,16 +173,38 @@ void interrupt(exception_frame* frame)
sched::preempt();
}

extern "C" { void handle_unexpected_sync_exception(exception_frame* frame); }
extern "C" { void handle_unexpected_exception(exception_frame* frame, u64 level, u64 type); }

#define EX_TYPE_SYNC 0x0
#define EX_TYPE_IRQ 0x1
#define EX_TYPE_FIQ 0x2
#define EX_TYPE_SERROR 0x3

#define ESR_EC_BEG 26 // Exception Class field begins in ESR at the bit 26th
#define ESR_EC_END 31 // and ends at 31st
#define ESR_EC_MASK 0b111111UL

void handle_unexpected_sync_exception(exception_frame* frame)
void handle_unexpected_exception(exception_frame* frame, u64 level, u64 type)
{
u64 exception_class = (frame->esr >> ESR_EC_BEG) & ESR_EC_MASK;
debug_ll("unexpected synchronous exception, EC: 0x%04x\n", exception_class);
switch (type) {
case EX_TYPE_SYNC:
{
u64 exception_class = (frame->esr >> ESR_EC_BEG) & ESR_EC_MASK;
debug_ll("unexpected synchronous exception at level:%ld, EC: 0x%04x\n", level, exception_class);
}
break;
case EX_TYPE_IRQ:
debug_ll("unexpected IRQ exception at level:%ld\n", level);
break;
case EX_TYPE_FIQ:
debug_ll("unexpected FIQ exception at level:%ld\n", level);
break;
case EX_TYPE_SERROR:
debug_ll("unexpected system error at level:%ld\n", level);
break;
default:
debug_ll("unexpected exception type:%ld at level:%ld\n", type, level);
}
dump_registers(frame);
}

Expand Down

0 comments on commit 06ad9a2

Please sign in to comment.