58,6 → 58,159 |
/** Size of memory block occupied by exception vectors. */ |
#define EXC_VECTORS_SIZE (EXC_VECTORS * 4) |
|
/** Switches to kernel stack and saves all registers there. |
* |
* Temporary exception stack is used to save a few registers |
* before stack switch takes place. |
* |
* The stack fram created by the function looks like: |
* |
* |_________________| |
* | | |
* | SPSR | |
* | | |
* |_________________| |
* | Stack Pointer | |
* | of | |
* | Previous Mode | |
* |_________________| |
* | Return address | |
* | of | |
* | Previous Mode | |
* |_________________| |
* | R0 - R12 | |
* | of | |
* | Previous Mode | |
* |_________________| |
* | Return address | |
* | from | |
* |Exception Handler| |
* |_________________| |
* | | |
* |
*/ |
inline static void setup_stack_and_save_regs() |
{ |
asm volatile ( |
"ldr r13, =exc_stack\n" |
"stmfd r13!, {r0-r3}\n" |
"mrs r1, cpsr\n" |
"bic r1, r1, #0x1f\n" |
"mrs r2, spsr\n" |
"and r0, r2, #0x1f\n" |
"cmp r0, #0x10\n" |
"bne 1f\n" |
|
/* prev mode was usermode */ |
"mov r0, sp\n" |
"mov r3, lr\n" |
|
/* Switch to supervisor mode */ |
"orr r1, r1, #0x13\n" |
"msr cpsr_c, r1\n" |
|
/* Load sp with [supervisor_sp] */ |
"ldr r13, =supervisor_sp\n" |
"ldr r13, [r13]\n" |
|
/* Populate the stack frame */ |
"msr spsr, r2\n" |
"mov lr, r3\n" |
"stmfd r13!, {lr}\n" |
"stmfd r13!, {r4-r12}\n" |
"ldmfd r0!, {r4-r7}\n" |
"stmfd r13!, {r4-r7}\n" |
"stmfd r13!, {r13, lr}^\n" |
"stmfd r13!, {r2}\n" |
"b 2f\n" |
|
|
/* mode was not usermode */ |
"1:\n" |
/* Switch to previous mode which is undoubtedly the supervisor mode */ |
"orr r1, r1, r0\n" |
"mov r0, lr\n" |
"mov r3, sp\n" |
"msr cpsr_c, r1\n" |
|
/* Populate the stack frame */ |
"mov r1, sp\n" |
"stmfd r13!, {r0}\n" |
"stmfd r13!, {r4-r12}\n" |
|
/* Store r0-r3 in r4-r7 and then push it on to stack */ |
"ldmfd r3!, {r4-r7}\n" |
"stmfd r13!, {r4-r7}\n" |
|
/* Push return address and stack pointer on to stack */ |
"stmfd r13!, {lr}\n" |
"stmfd r13!, {r1}\n" |
"mov lr, r0\n" |
"msr spsr, r2\n" |
"stmfd r13!, {r2}\n" |
|
"2:\n" |
); |
} |
|
/** Returns from exception mode. |
* |
* Previously saved state of registers (including control register) |
* is restored from the stack. |
*/ |
inline static void load_regs() |
{ |
asm volatile( |
"ldmfd r13!, {r0} \n" |
"msr spsr, r0 \n" |
"and r0, r0, #0x1f \n" |
"cmp r0, #0x10 \n" |
"bne 1f \n" |
|
/* return to user mode */ |
"ldmfd r13!, {r13, lr}^ \n" |
"b 2f \n" |
|
/* return to non-user mode */ |
"1:\n" |
"ldmfd r13!, {r1, r2} \n" |
"mrs r3, cpsr \n" |
"bic r3, r3, #0x1f \n" |
"orr r3, r3, r0 \n" |
"mrs r0, cpsr \n" |
"msr cpsr_c, r3 \n" |
|
"mov lr, r2 \n" |
"msr cpsr_c, r0 \n" |
|
/* actual return */ |
"2:\n" |
"ldmfd r13!, {r0-r12, pc}^\n" |
); |
} |
|
|
/** Calls exception dispatch routine. */ |
#define CALL_EXC_DISPATCH(exception) \ |
asm volatile ( \ |
"mov r0, %[exc]\n" \ |
"mov r1, r13\n" \ |
"bl exc_dispatch\n" \ |
:: [exc] "i" (exception) \ |
);\ |
|
/** General exception handler. |
* |
* Stores registers, dispatches the exception, |
* and finally restores registers and returns from exception processing. |
* |
* @param exception Exception number. |
*/ |
#define PROCESS_EXCEPTION(exception) \ |
setup_stack_and_save_regs(); \ |
CALL_EXC_DISPATCH(exception) \ |
load_regs(); |
|
/** Updates specified exception vector to jump to given handler. |
* |
* Addresses of handlers are stored in memory following exception vectors. |
79,6 → 232,65 |
|
} |
|
/** Low-level Reset Exception handler. */ |
static void reset_exception_entry(void) |
{ |
PROCESS_EXCEPTION(EXC_RESET); |
} |
|
/** Low-level Software Interrupt Exception handler. */ |
static void swi_exception_entry(void) |
{ |
PROCESS_EXCEPTION(EXC_SWI); |
} |
|
/** Low-level Undefined Instruction Exception handler. */ |
static void undef_instr_exception_entry(void) |
{ |
PROCESS_EXCEPTION(EXC_UNDEF_INSTR); |
} |
|
/** Low-level Fast Interrupt Exception handler. */ |
static void fiq_exception_entry(void) |
{ |
PROCESS_EXCEPTION(EXC_FIQ); |
} |
|
/** Low-level Prefetch Abort Exception handler. */ |
static void prefetch_abort_exception_entry(void) |
{ |
asm volatile ( |
"sub lr, lr, #4" |
); |
|
PROCESS_EXCEPTION(EXC_PREFETCH_ABORT); |
} |
|
/** Low-level Data Abort Exception handler. */ |
static void data_abort_exception_entry(void) |
{ |
asm volatile ( |
"sub lr, lr, #8" |
); |
|
PROCESS_EXCEPTION(EXC_DATA_ABORT); |
} |
|
/** Low-level Interrupt Exception handler. |
* |
* CPU is switched to Undefined mode before further interrupt processing |
* because of possible occurence of nested interrupt exception, which |
* would overwrite (and thus spoil) stack pointer. |
*/ |
static void irq_exception_entry(void) |
{ |
asm volatile ( |
"sub lr, lr, #4" |
); |
|
PROCESS_EXCEPTION(EXC_IRQ) |
} |
|
/** Software Interrupt handler. |
* |
* Dispatches the syscall. |