arch: add fault handlers, rename exceptions

This commit is contained in:
anna 2021-08-03 14:41:36 +02:00
parent 7fa55c8dab
commit 05662bc39e
Signed by: fef
GPG key ID: EC22E476DC2D3D84
9 changed files with 368 additions and 141 deletions

View file

@ -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

View file

@ -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 <owo@fef.moe>.
*
* 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
* <https://git.pixie.town/thufie/CNPL>.
*
* Ardix comes with ABSOLUTELY NO WARRANTY, to the extent
* permitted by applicable law. See the CNPLv6+ for details.
*/

View file

@ -0,0 +1,168 @@
/* See the end of this file for copyright, license, and warranty information. */
#include <arch-generic/hardware.h>
#include <arch/hardware.h>
#include <arch/interrupt.h>
#include <toolchain.h>
/** 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 = &reg_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 <arch/debug.h>
__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 <owo@fef.moe>.
*
* 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
* <https://git.pixie.town/thufie/CNPL>.
*
* Ardix comes with ABSOLUTELY NO WARRANTY, to the extent
* permitted by applicable law. See the CNPLv6+ for details.
*/

View file

@ -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.

View file

@ -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.

View file

@ -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,

View file

@ -9,10 +9,10 @@
#include <string.h>
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);
}

View file

@ -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,
};
/*

View file

@ -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