1,4 → 1,4 |
/* |
/* |
* Copyright (c) 2007 Petr Stepan |
* All rights reserved. |
* |
36,61 → 36,203 |
#include <arch/exception.h> |
#include "aux_print/printf.h" |
#include <arch/memstr.h> |
#include <arch/regutils.h> |
#include <interrupt.h> |
|
|
#define PREFETCH_OFFSET 0x8 |
#define BRANCH_OPCODE 0xea000000 |
#define LDR_OPCODE 0xe59ff000 |
#define VALID_BRANCH_MASK 0xff000000 |
#define EXC_VECTORS_SIZE 0x20 |
#define EXC_VECTORS 0x8 |
|
/** Updates specified exception vector to jump to given handler. |
/* GXEmul interrupt controller macros |
TODO might go to drivers/ together with servicing functions |
*/ |
static void install_handler (unsigned* handler, unsigned* vector) { |
volatile unsigned vec; |
/* IRQ Controller device is added in a special premium gxemul |
* edition at www.ms.mff.cuni.cz/~stepp3am/mygxemul-0.4.4.1.tar.gz |
* :) |
*/ |
#define IRQ_CONTROLLER_CAUSE 0x0000000016000000 |
#define IRQ_CONTROLLER_MAX_IRQ 8 |
|
/* IRQs */ |
#define CONSOLE_IRQ 2 |
#define TIMER_IRQ 4 |
|
|
#define SAVE_REGS_TO_STACK \ |
asm("stmfd sp!, {r0-r12, sp, lr}"); \ |
asm("mrs r14, spsr"); \ |
asm("stmfd sp!, {r14}"); |
|
#define CALL_EXC_DISPATCH(exception) \ |
asm("mov r0, %0" : : "i" (exception)); \ |
asm("mov r1, sp"); \ |
asm("bl exc_dispatch"); |
|
/**Loads registers from the stack and resets SPSR before exitting exception |
* handler. |
*/ |
#define LOAD_REGS_FROM_STACK \ |
asm("ldmfd sp!, {r14}"); \ |
asm("msr spsr, r14"); \ |
asm("ldmfd sp!, {r0-r12, sp, pc}^"); |
|
/* calculate relative distance */ |
vec = ((unsigned)handler - (unsigned)vector - PREFETCH_OFFSET); |
/** General exception handler. |
* Stores registers, dispatches the exception, |
* and finally restores registers and returns from exception processing. |
*/ |
#define PROCESS_EXCEPTION(exception) \ |
SAVE_REGS_TO_STACK \ |
CALL_EXC_DISPATCH(exception) \ |
LOAD_REGS_FROM_STACK |
|
/** Updates specified exception vector to jump to given handler. |
* Addresses of handlers are stored in memory following exception vectors. |
*/ |
static void install_handler (unsigned handler_addr, unsigned* vector) |
{ |
/* relative address (related to exc. vector) of the word |
* where handler's address is stored |
*/ |
volatile uint32_t handler_address_ptr = EXC_VECTORS_SIZE - PREFETCH_OFFSET; |
|
/* word offset */ |
vec >>= 2; |
/* make it LDR instruction and store at exception vector */ |
*vector = handler_address_ptr | LDR_OPCODE; |
|
/* check if haven't jumped beyond 32 MB */ |
if ((vec & VALID_BRANCH_MASK) != 0) { |
/*panic("exception handler beyond 32MB.");*/ |
return; |
} |
|
/* make it branch instruction */ |
vec |= BRANCH_OPCODE; |
*vector = vec; |
|
aux_printf("installed handler to %p, to vector %p\n.", handler, vector); |
/* store handler's address */ |
*(vector + EXC_VECTORS) = handler_addr; |
} |
|
/* TODO add other exception handlers */ |
static void dummy_exception(){ |
asm("bkpt"); |
static void reset_exception_entry() |
{ |
PROCESS_EXCEPTION(EXC_RESET); |
} |
|
/** IRQ Exception handler */ |
static void irq_exception(){ |
asm("bkpt"); |
/** Low-level Software Interrupt Exception handler */ |
static void swi_exception_entry() |
{ |
PROCESS_EXCEPTION(EXC_SWI); |
} |
|
/** Low-level Undefined Instruction Exception handler */ |
static void undef_instr_exception_entry() |
{ |
PROCESS_EXCEPTION(EXC_UNDEF_INSTR); |
} |
|
/** Low-level Fast Interrupt Exception handler */ |
static void fiq_exception_entry() |
{ |
PROCESS_EXCEPTION(EXC_FIQ); |
} |
|
/** Low-level Prefetch Abort Exception handler */ |
static void prefetch_abort_exception_entry() |
{ |
asm("sub lr, lr, #4"); |
PROCESS_EXCEPTION(EXC_PREFETCH_ABORT); |
} |
|
/** Low-level Data Abort Exception handler */ |
static void data_abort_exception_entry() |
{ |
asm("sub lr, lr, #8"); |
PROCESS_EXCEPTION(EXC_DATA_ABORT); |
} |
|
|
/** Low-level Interrupt Exception handler */ |
static void irq_exception_entry() |
{ |
asm("sub lr, lr, #4"); |
PROCESS_EXCEPTION(EXC_IRQ); |
} |
|
|
/** Interrupt Exception handler. |
* Determines the sources of interrupt, and calls their handlers. |
*/ |
static void irq_exception(int exc_no, istate_t* istate) |
{ |
/* TODO this will call interrupt dispatching routine |
* |
*/ |
|
uint32_t sources = *(uint32_t*) IRQ_CONTROLLER_CAUSE; |
int i = 0; |
int noirq = 1; |
for (; i < IRQ_CONTROLLER_MAX_IRQ; i++) { |
if (sources & (1 << i)) { |
noirq = 0; |
if (i == CONSOLE_IRQ) { |
char readchar = *(char*)0x10000000; |
if (readchar == 0) { |
aux_puts("?"); |
} |
else { |
aux_printf("%c", readchar); |
} |
|
} |
else if (i == TIMER_IRQ) { |
aux_printf("\n.\n"); |
/* acknowledge */ |
*(uint32_t*)0x15000110 = 0; |
} |
} |
} |
|
if (noirq) |
aux_puts("IRQ exception without source\n"); |
} |
|
/** Fills exception vectors with appropriate exception handlers. |
*/ |
void install_exception_handlers(void) { |
install_handler((unsigned*)dummy_exception, (unsigned*)EXC_RESET_VEC); |
install_handler((unsigned*)dummy_exception, (unsigned*)EXC_UNDEF_INSTR_VEC); |
install_handler((unsigned*)dummy_exception, (unsigned*)EXC_SWI_VEC); |
install_handler((unsigned*)dummy_exception, (unsigned*)EXC_PREFETCH_ABORT_VEC); |
install_handler((unsigned*)dummy_exception, (unsigned*)EXC_DATA_ABORT_VEC); |
install_handler((unsigned*)irq_exception, (unsigned*)EXC_IRQ_VEC); |
install_handler((unsigned*)dummy_exception, (unsigned*)EXC_FIQ_VEC); |
void install_exception_handlers(void) |
{ |
install_handler((unsigned)reset_exception_entry, |
(unsigned*)EXC_RESET_VEC); |
|
install_handler((unsigned)undef_instr_exception_entry, |
(unsigned*)EXC_UNDEF_INSTR_VEC); |
|
install_handler((unsigned)swi_exception_entry, |
(unsigned*)EXC_SWI_VEC); |
|
install_handler((unsigned)prefetch_abort_exception_entry, |
(unsigned*)EXC_PREFETCH_ABORT_VEC); |
|
install_handler((unsigned)data_abort_exception_entry, |
(unsigned*)EXC_DATA_ABORT_VEC); |
|
install_handler((unsigned)irq_exception_entry, |
(unsigned*)EXC_IRQ_VEC); |
|
install_handler((unsigned)fiq_exception_entry, |
(unsigned*)EXC_FIQ_VEC); |
} |
|
/** Registers exceptions and their handlers to kernel exception dispatcher. */ |
void exception_init(void){ |
/* TODO */ |
void exception_init(void) |
{ |
exc_register(EXC_IRQ, "interrupt", (iroutine) irq_exception); |
/* TODO add next */ |
} |
|
/* TODO change soon, temporary hack. */ |
void setup_exception_stacks() |
{ |
/* switch to particular mode and set "sp" there */ |
uint32_t cspr = current_status_reg_read(); |
current_status_reg_control_write( |
(cspr & ~STATUS_REG_MODE_MASK) | IRQ_MODE |
); |
asm("ldr sp, =irq_stack"); |
current_status_reg_control_write( cspr); |
} |
|
/** @} |
*/ |