From 05662bc39e0a8bce8cfcd7616e683a256ebfb985 Mon Sep 17 00:00:00 2001 From: fef Date: Tue, 3 Aug 2021 14:41:36 +0200 Subject: [PATCH] arch: add fault handlers, rename exceptions --- arch/at91sam3x8e/CMakeLists.txt | 6 +- arch/at91sam3x8e/handle_fault.S | 53 +++++ arch/at91sam3x8e/handle_fault.c | 168 ++++++++++++++ .../{irq_pend_sv.S => handle_pend_sv.S} | 6 +- arch/at91sam3x8e/{irq_svc.S => handle_svc.S} | 6 +- arch/at91sam3x8e/include/arch/interrupt.h | 43 ++-- arch/at91sam3x8e/sched.c | 6 +- arch/at91sam3x8e/startup.c | 210 +++++++++--------- include/toolchain.h | 11 +- 9 files changed, 368 insertions(+), 141 deletions(-) create mode 100644 arch/at91sam3x8e/handle_fault.S create mode 100644 arch/at91sam3x8e/handle_fault.c rename arch/at91sam3x8e/{irq_pend_sv.S => handle_pend_sv.S} (94%) rename arch/at91sam3x8e/{irq_svc.S => handle_svc.S} (93%) diff --git a/arch/at91sam3x8e/CMakeLists.txt b/arch/at91sam3x8e/CMakeLists.txt index 83cf961..387554a 100644 --- a/arch/at91sam3x8e/CMakeLists.txt +++ b/arch/at91sam3x8e/CMakeLists.txt @@ -16,9 +16,11 @@ target_sources(ardix_arch PRIVATE atom.c atomic.c entry.c + handle_fault.c + handle_fault.S + handle_pend_sv.S + handle_svc.S interrupt.c - irq_pend_sv.S - irq_svc.S mutex.S sched.c serial.c diff --git a/arch/at91sam3x8e/handle_fault.S b/arch/at91sam3x8e/handle_fault.S new file mode 100644 index 0000000..f07d709 --- /dev/null +++ b/arch/at91sam3x8e/handle_fault.S @@ -0,0 +1,53 @@ +/* See the end of this file for copyright, license, and warranty information. */ + +/* + * See arch_handle_fault() + */ + +.include "asm.S" + +.text + +/* __naked __noreturn void arch_handle_fault(struct reg_snapshot *regs, int irqnum); */ +.extern arch_handle_fault + +func_begin handle_hard_fault + push {r4-r11,lr} + mov r0, sp + mov r1, #0xfffffff3 /* -13 */ + b arch_handle_fault +func_end handle_hard_fault + +func_begin handle_mm_fault + push {r4-r11,lr} + mov r0, sp + mov r1, #0xfffffff4 /* -12 */ + b arch_handle_fault +func_end handle_mm_fault + +func_begin handle_bus_fault + push {r4-r11,lr} + mov r0, sp + mov r1, #0xfffffff5 /* -11 */ + b arch_handle_fault +func_end handle_bus_fault + +func_begin handle_usage_fault + push {r4-r11,lr} + mov r0, sp + mov r1, #0xfffffff6 /* -10 */ + b arch_handle_fault +func_end handle_usage_fault + +/* + * This file is part of Ardix. + * Copyright (c) 2020, 2021 Felix Kopp . + * + * Ardix is non-violent software: you may only use, redistribute, + * and/or modify it under the terms of the CNPLv6+ as found in + * the LICENSE file in the source code root directory or at + * . + * + * Ardix comes with ABSOLUTELY NO WARRANTY, to the extent + * permitted by applicable law. See the CNPLv6+ for details. + */ diff --git a/arch/at91sam3x8e/handle_fault.c b/arch/at91sam3x8e/handle_fault.c new file mode 100644 index 0000000..f6f9c7f --- /dev/null +++ b/arch/at91sam3x8e/handle_fault.c @@ -0,0 +1,168 @@ +/* See the end of this file for copyright, license, and warranty information. */ + +#include + +#include +#include + +#include + +/** Setup UART to manual byte-by-byte control */ +static inline void uart_emergency_setup(void) +{ + REG_UART_PDC_PTCR = REG_UART_PDC_PTCR_RXTDIS_MASK | REG_UART_PDC_PTCR_TXTDIS_MASK; + + REG_UART_CR = REG_UART_CR_RXDIS_MASK | REG_UART_CR_RSTRX_MASK + | REG_UART_CR_TXDIS_MASK | REG_UART_CR_RSTTX_MASK; + + REG_UART_IDR = 0xffffffff; + + REG_UART_CR = REG_UART_CR_RXEN_MASK | REG_UART_CR_TXEN_MASK; +} + +static void uart_write_sync(const char *s) +{ + char c; + while ((c = *s++) != '\0') { + mom_are_we_there_yet(REG_UART_SR & REG_UART_SR_TXRDY_MASK); + REG_UART_THR = c; + } +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wswitch" +static inline void print_err_msg(enum irqno irqno) +{ + switch (irqno) { + case IRQNO_HARD_FAULT: + uart_write_sync("Hard"); + break; + case IRQNO_MM_FAULT: + uart_write_sync("Memory Management"); + break; + case IRQNO_BUS_FAULT: + uart_write_sync("Bus"); + break; + case IRQNO_USAGE_FAULT: + uart_write_sync("Usage"); + break; + } + + uart_write_sync(" Fault encountered, system halted.\n\n"); +} +#pragma GCC diagnostic pop /* -Wswitch */ + +static void reg_to_str(char *dest, uint32_t val) +{ + for (int i = 28; i >= 0; i -= 4) { + uint8_t digit = (val >> i) & 0x0f; + + if (digit < 0x0a) + digit += '0'; + else + digit += 'a'; + + *dest++ = digit; + } +} + +static void print_regs(struct reg_snapshot *regs) +{ + static char reg_line[] = "r0 = 0x????????\n"; + char *reg_val = ®_line[8]; /* first question mark */ + + reg_to_str(reg_val, regs->hw.r0); + uart_write_sync(reg_line); + + reg_line[1] = '1'; + reg_to_str(reg_val, regs->hw.r1); + uart_write_sync(reg_line); + + reg_line[1] = '2'; + reg_to_str(reg_val, regs->hw.r2); + uart_write_sync(reg_line); + + reg_line[1] = '3'; + reg_to_str(reg_val, regs->hw.r3); + uart_write_sync(reg_line); + + reg_line[1] = '4'; + reg_to_str(reg_val, regs->sw.r4); + uart_write_sync(reg_line); + + reg_line[1] = '5'; + reg_to_str(reg_val, regs->sw.r5); + uart_write_sync(reg_line); + + reg_line[1] = '6'; + reg_to_str(reg_val, regs->sw.r6); + uart_write_sync(reg_line); + + reg_line[1] = '7'; + reg_to_str(reg_val, regs->sw.r7); + uart_write_sync(reg_line); + + reg_line[1] = '8'; + reg_to_str(reg_val, regs->sw.r8); + uart_write_sync(reg_line); + + reg_line[1] = '9'; + reg_to_str(reg_val, regs->sw.r9); + uart_write_sync(reg_line); + + reg_line[1] = '1'; + reg_line[2] = '0'; + reg_to_str(reg_val, regs->sw.r10); + uart_write_sync(reg_line); + + reg_line[2] = '1'; + reg_to_str(reg_val, regs->sw.r11); + uart_write_sync(reg_line); + + reg_line[2] = '2'; + reg_to_str(reg_val, regs->hw.r12); + uart_write_sync(reg_line); + + reg_line[0] = 's'; + reg_line[1] = 'p'; + reg_line[2] = ' '; + reg_to_str(reg_val, (uint32_t)(regs + 1)); /* where SP was before reg save */ + uart_write_sync(reg_line); + + reg_line[0] = 'l'; + reg_line[1] = 'r'; + reg_to_str(reg_val, (uint32_t)regs->hw.lr); + uart_write_sync(reg_line); + + reg_line[0] = 'p'; + reg_line[1] = 'c'; + reg_to_str(reg_val, (uint32_t)regs->hw.pc); + uart_write_sync(reg_line); +} + +#include + +__naked __noreturn void arch_handle_fault(struct reg_snapshot *regs, enum irqno irqno) +{ + uart_emergency_setup(); + print_err_msg(irqno); + print_regs(regs); + + /* give developers a chance to inspect the system */ + __breakpoint; + /* but never leave this function */ + while (1); +} + +/* + * This file is part of Ardix. + * Copyright (c) 2020, 2021 Felix Kopp . + * + * Ardix is non-violent software: you may only use, redistribute, + * and/or modify it under the terms of the CNPLv6+ as found in + * the LICENSE file in the source code root directory or at + * . + * + * Ardix comes with ABSOLUTELY NO WARRANTY, to the extent + * permitted by applicable law. See the CNPLv6+ for details. + */ diff --git a/arch/at91sam3x8e/irq_pend_sv.S b/arch/at91sam3x8e/handle_pend_sv.S similarity index 94% rename from arch/at91sam3x8e/irq_pend_sv.S rename to arch/at91sam3x8e/handle_pend_sv.S index ea618ab..59647c9 100644 --- a/arch/at91sam3x8e/irq_pend_sv.S +++ b/arch/at91sam3x8e/handle_pend_sv.S @@ -6,8 +6,8 @@ .extern sched_process_switch -/* void irq_pend_sv(void); */ -func_begin irq_pend_sv +/* void handle_pend_sv(void); */ +func_begin handle_pend_sv /* * Some registers have already been saved by hardware at this point, * we only need to take care of r4-r11 and lr (the latter of which is @@ -40,7 +40,7 @@ func_begin irq_pend_sv bx lr -func_end irq_pend_sv +func_end handle_pend_sv /* * This file is part of Ardix. diff --git a/arch/at91sam3x8e/irq_svc.S b/arch/at91sam3x8e/handle_svc.S similarity index 93% rename from arch/at91sam3x8e/irq_svc.S rename to arch/at91sam3x8e/handle_svc.S index 88b772c..4bbcd97 100644 --- a/arch/at91sam3x8e/irq_svc.S +++ b/arch/at91sam3x8e/handle_svc.S @@ -6,8 +6,8 @@ .extern arch_enter -/* void irq_svc(void); */ -func_begin irq_svc +/* void handle_svc(void); */ +func_begin handle_svc /* * Syscalls on Cortex-M use the following parameter calling convention: * @@ -34,7 +34,7 @@ func_begin irq_svc bx lr -func_end irq_svc +func_end handle_svc /* * This file is part of Ardix. diff --git a/arch/at91sam3x8e/include/arch/interrupt.h b/arch/at91sam3x8e/include/arch/interrupt.h index 3b2b990..5fa0e22 100644 --- a/arch/at91sam3x8e/include/arch/interrupt.h +++ b/arch/at91sam3x8e/include/arch/interrupt.h @@ -2,26 +2,26 @@ #pragma once -/** Reset interrupt handler */ -void irq_reset(void); +/** Reset exception handler */ +void handle_reset(void); /** Non-maskable interrupt handler */ -void irq_nmi(void); -/** Hard fault inerrupt handler */ -void irq_hard_fault(void); -/** Memory management fault interrupt handler */ -void irq_mem_fault(void); -/** Bus fault interrupt handler */ -void irq_bus_fault(void); -/** Usage fault (illegal instruction) interrupt handler */ -void irq_usage_fault(void); -/** SVC interrupt handler */ -void irq_svc(void); +void handle_nmi(void); +/** Hard fault exception handler */ +void handle_hard_fault(void); +/** Memory management fault exception handler */ +void handle_mem_fault(void); +/** Bus fault exception handler */ +void handle_bus_fault(void); +/** Usage fault exception handler */ +void handle_usage_fault(void); +/** SVC exception handler */ +void handle_svc(void); /** Debug handler (reserved) */ -void irq_debug_mon(void); -/** Pending SV interrupt handler */ -void irq_pend_sv(void); -/** SysTick interrupt handler */ -void irq_sys_tick(void); +void handle_debug_mon(void); +/** PendSV interrupt handler */ +void handle_pend_sv(void); +/** SysTick exception handler */ +void handle_sys_tick(void); /** Supply Controller (0) interrupt handler */ void irq_supc(void); @@ -107,11 +107,14 @@ void irq_can0(void); void irq_can1(void); /** - * Interrupt numbers for sam3x8e + * @brief IRQ numbers for sam3x8e. + * + * Like CMSIS, we treat other exceptions basically the same as IRQs. */ enum irqno { IRQNO_NMI = -14, - IRQNO_MEM_FAULT = -12, + IRQNO_HARD_FAULT = -13, + IRQNO_MM_FAULT = -12, IRQNO_BUS_FAULT = -11, IRQNO_USAGE_FAULT = -10, IRQNO_SVC_CALL = -5, diff --git a/arch/at91sam3x8e/sched.c b/arch/at91sam3x8e/sched.c index e7dd08b..757b308 100644 --- a/arch/at91sam3x8e/sched.c +++ b/arch/at91sam3x8e/sched.c @@ -9,10 +9,10 @@ #include -void irq_sys_tick(void) +void handle_sys_tick(void) { /* - * fire a PendSV interrupt and do the actual context switching there + * fire a PendSV exception and do the actual context switching there * because it is faster that way (according to the docs, at least) */ if (!is_atomic_context()) @@ -68,7 +68,7 @@ void arch_sched_task_init(struct task *task, void (*entry)(void)) void yield(enum task_state state) { - REG_SYSTICK_VAL = 0U; /* Reset timer */ + REG_SYSTICK_VAL = 0U; /* Reset timer (TODO: don't do this lmao) */ current->state = state; arch_irq_invoke(IRQNO_PEND_SV); } diff --git a/arch/at91sam3x8e/startup.c b/arch/at91sam3x8e/startup.c index 98153f0..01542f1 100644 --- a/arch/at91sam3x8e/startup.c +++ b/arch/at91sam3x8e/startup.c @@ -27,7 +27,7 @@ extern uint32_t _eheap; /* heap end */ /* implementation in init/main.c */ extern int main(void); -__naked void irq_reset(void) +__naked __noreturn void handle_reset(void) { sys_init(); @@ -45,133 +45,129 @@ __naked void irq_reset(void) } /** - * Default IRQ for unimplemented interrupts. + * Default handler for unimplemented interrupts. * This will halt the system. */ -void irq_stub(void) +void _stub_handler(void) { while (1); } -__weak __alias(irq_stub) void irq_nmi(void); -__weak __alias(irq_stub) void irq_hard_fault(void); -__weak __alias(irq_stub) void irq_mem_fault(void); -__weak __alias(irq_stub) void irq_bus_fault(void); -__weak __alias(irq_stub) void irq_usage_fault(void); -__weak __alias(irq_stub) void irq_svc(void); -__weak __alias(irq_stub) void irq_debug_mon(void); -__weak __alias(irq_stub) void irq_pend_sv(void); -__weak __alias(irq_stub) void irq_sys_tick(void); +void handle_nmi(void) __weak __alias(_stub_handler); +void handle_hard_fault(void) __weak __alias(_stub_handler); +void handle_mm_fault(void) __weak __alias(_stub_handler); +void handle_bus_fault(void) __weak __alias(_stub_handler); +void handle_usage_fault(void) __weak __alias(_stub_handler); +void handle_svc(void) __weak __alias(_stub_handler); +void handle_debug_mon(void) __weak __alias(_stub_handler); +void handle_pend_sv(void) __weak __alias(_stub_handler); +void handle_sys_tick(void) __weak __alias(_stub_handler); -__weak __alias(irq_stub) void irq_supc(void); -__weak __alias(irq_stub) void irq_rstc(void); -__weak __alias(irq_stub) void irq_rtc(void); -__weak __alias(irq_stub) void irq_rtt(void); -__weak __alias(irq_stub) void irq_wdt(void); -__weak __alias(irq_stub) void irq_pmc(void); -__weak __alias(irq_stub) void irq_efc0(void); -__weak __alias(irq_stub) void irq_efc1(void); -__weak __alias(irq_stub) void irq_uart(void); -__weak __alias(irq_stub) void irq_smc(void); -__weak __alias(irq_stub) void irq_pioa(void); -__weak __alias(irq_stub) void irq_piob(void); -__weak __alias(irq_stub) void irq_pioc(void); -__weak __alias(irq_stub) void irq_piod(void); -__weak __alias(irq_stub) void irq_usart0(void); -__weak __alias(irq_stub) void irq_usart1(void); -__weak __alias(irq_stub) void irq_usart2(void); -__weak __alias(irq_stub) void irq_usart3(void); -__weak __alias(irq_stub) void irq_hsmci(void); -__weak __alias(irq_stub) void irq_twi0(void); -__weak __alias(irq_stub) void irq_twi1(void); -__weak __alias(irq_stub) void irq_spi0(void); -__weak __alias(irq_stub) void irq_ssc(void); -__weak __alias(irq_stub) void irq_tc0(void); -__weak __alias(irq_stub) void irq_tc1(void); -__weak __alias(irq_stub) void irq_tc2(void); -__weak __alias(irq_stub) void irq_tc3(void); -__weak __alias(irq_stub) void irq_tc4(void); -__weak __alias(irq_stub) void irq_tc5(void); -__weak __alias(irq_stub) void irq_tc6(void); -__weak __alias(irq_stub) void irq_tc7(void); -__weak __alias(irq_stub) void irq_tc8(void); -__weak __alias(irq_stub) void irq_pwm(void); -__weak __alias(irq_stub) void irq_adc(void); -__weak __alias(irq_stub) void irq_dacc(void); -__weak __alias(irq_stub) void irq_dmac(void); -__weak __alias(irq_stub) void irq_uotghs(void); -__weak __alias(irq_stub) void irq_trng(void); -__weak __alias(irq_stub) void irq_emac(void); -__weak __alias(irq_stub) void irq_can0(void); -__weak __alias(irq_stub) void irq_can1(void); +void irq_supc(void) __weak __alias(_stub_handler); +void irq_rstc(void) __weak __alias(_stub_handler); +void irq_rtc(void) __weak __alias(_stub_handler); +void irq_rtt(void) __weak __alias(_stub_handler); +void irq_wdt(void) __weak __alias(_stub_handler); +void irq_pmc(void) __weak __alias(_stub_handler); +void irq_efc0(void) __weak __alias(_stub_handler); +void irq_efc1(void) __weak __alias(_stub_handler); +void irq_uart(void) __weak __alias(_stub_handler); +void irq_smc(void) __weak __alias(_stub_handler); +void irq_pioa(void) __weak __alias(_stub_handler); +void irq_piob(void) __weak __alias(_stub_handler); +void irq_pioc(void) __weak __alias(_stub_handler); +void irq_piod(void) __weak __alias(_stub_handler); +void irq_usart0(void) __weak __alias(_stub_handler); +void irq_usart1(void) __weak __alias(_stub_handler); +void irq_usart2(void) __weak __alias(_stub_handler); +void irq_usart3(void) __weak __alias(_stub_handler); +void irq_hsmci(void) __weak __alias(_stub_handler); +void irq_twi0(void) __weak __alias(_stub_handler); +void irq_twi1(void) __weak __alias(_stub_handler); +void irq_spi0(void) __weak __alias(_stub_handler); +void irq_ssc(void) __weak __alias(_stub_handler); +void irq_tc0(void) __weak __alias(_stub_handler); +void irq_tc1(void) __weak __alias(_stub_handler); +void irq_tc2(void) __weak __alias(_stub_handler); +void irq_tc3(void) __weak __alias(_stub_handler); +void irq_tc4(void) __weak __alias(_stub_handler); +void irq_tc5(void) __weak __alias(_stub_handler); +void irq_tc6(void) __weak __alias(_stub_handler); +void irq_tc7(void) __weak __alias(_stub_handler); +void irq_tc8(void) __weak __alias(_stub_handler); +void irq_pwm(void) __weak __alias(_stub_handler); +void irq_adc(void) __weak __alias(_stub_handler); +void irq_dacc(void) __weak __alias(_stub_handler); +void irq_dmac(void) __weak __alias(_stub_handler); +void irq_uotghs(void) __weak __alias(_stub_handler); +void irq_trng(void) __weak __alias(_stub_handler); +void irq_emac(void) __weak __alias(_stub_handler); +void irq_can0(void) __weak __alias(_stub_handler); +void irq_can1(void) __weak __alias(_stub_handler); __section(.vectors) const void *exception_table[] = { &_estack, /* initial SP value (stack grows down) */ - &irq_reset, /* reset vector */ + handle_reset, /* reset vector */ NULL, /* reserved */ - &irq_hard_fault, /* hard fault */ - &irq_mem_fault, /* memory management fault */ - &irq_bus_fault, /* bus fault */ - &irq_usage_fault, /* usage fault */ + handle_hard_fault, /* hard fault */ + handle_mm_fault, /* memory management fault */ + handle_bus_fault, /* bus fault */ + handle_usage_fault, /* usage fault */ NULL, /* reserved */ NULL, /* reserved */ NULL, /* reserved */ NULL, /* reserved */ - &irq_svc, /* SVC call (used for syscalls) */ - &irq_debug_mon, /* reserved for debug */ + handle_svc, /* SVC call (used for syscalls) */ + handle_debug_mon, /* reserved for debug */ NULL, /* reserved */ - &irq_pend_sv, /* PendSV (used by the scheduler) */ - &irq_sys_tick, /* SysTick */ + handle_pend_sv, /* PendSV (used by the scheduler) */ + handle_sys_tick, /* SysTick */ - /* - * Ok I am REALLY tired of writing out mnemonics. - * Just have a look at include/arch/at91sam3x8e.h for details. - */ - &irq_supc, - &irq_rstc, - &irq_rtc, - &irq_rtt, - &irq_wdt, - &irq_pmc, - &irq_efc0, - &irq_efc1, - &irq_uart, - &irq_smc, + irq_supc, + irq_rstc, + irq_rtc, + irq_rtt, + irq_wdt, + irq_pmc, + irq_efc0, + irq_efc1, + irq_uart, + irq_smc, NULL, /* reserved */ - &irq_pioa, - &irq_piob, - &irq_pioc, - &irq_piod, + irq_pioa, + irq_piob, + irq_pioc, + irq_piod, NULL, /* reserved */ NULL, /* reserved */ - &irq_usart0, - &irq_usart1, - &irq_usart2, - &irq_usart3, - &irq_hsmci, - &irq_twi0, - &irq_twi1, - &irq_spi0, + irq_usart0, + irq_usart1, + irq_usart2, + irq_usart3, + irq_hsmci, + irq_twi0, + irq_twi1, + irq_spi0, NULL, /* reserved */ - &irq_ssc, - &irq_tc0, - &irq_tc1, - &irq_tc2, - &irq_tc3, - &irq_tc4, - &irq_tc5, - &irq_tc6, - &irq_tc7, - &irq_tc8, - &irq_pwm, - &irq_adc, - &irq_dacc, - &irq_dmac, - &irq_uotghs, - &irq_trng, - &irq_emac, - &irq_can0, - &irq_can1, + irq_ssc, + irq_tc0, + irq_tc1, + irq_tc2, + irq_tc3, + irq_tc4, + irq_tc5, + irq_tc6, + irq_tc7, + irq_tc8, + irq_pwm, + irq_adc, + irq_dacc, + irq_dmac, + irq_uotghs, + irq_trng, + irq_emac, + irq_can0, + irq_can1, }; /* diff --git a/include/toolchain.h b/include/toolchain.h index 642ced2..9775ff2 100644 --- a/include/toolchain.h +++ b/include/toolchain.h @@ -11,20 +11,25 @@ * Force a method to always be inlined by the compiler. * Do not use this for functions exceeding one or two lines. */ -#define __always_inline inline __attribute__((always_inline)) +#define __always_inline inline __attribute__(( always_inline )) #endif /* __always_inline */ #ifndef __naked /** Function attribute for disabling register saving. */ -#define __naked __attribute__((naked)) +#define __naked __attribute__(( naked )) #endif +#ifndef __noreturn +/** Function attribute denoting the call will never return. */ +#define __noreturn __attribute__(( noreturn )) +#endif /* __noreturn */ + #ifndef __weak /** * Add the `weak` attribute to a symbol. * This allows that identifier to be re-declared without any warnings. */ -#define __weak __attribute__((__weak__)) +#define __weak __attribute__(( weak )) #endif /* __weak */ #ifndef __alias