Changeset View
Changeset View
Standalone View
Standalone View
sys/arm/arm/exception.S
Show First 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | |||||
#include "assym.s" | #include "assym.s" | ||||
#include <machine/acle-compat.h> | #include <machine/acle-compat.h> | ||||
#include <machine/asm.h> | #include <machine/asm.h> | ||||
#include <machine/armreg.h> | #include <machine/armreg.h> | ||||
#include <machine/asmacros.h> | #include <machine/asmacros.h> | ||||
#include <machine/trap.h> | #include <machine/trap.h> | ||||
#include <machine/param.h> | |||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#ifdef KDTRACE_HOOKS | #ifdef KDTRACE_HOOKS | ||||
.bss | .bss | ||||
.align 4 | .align 4 | ||||
.global _C_LABEL(dtrace_invop_jump_addr) | .global _C_LABEL(dtrace_invop_jump_addr) | ||||
_C_LABEL(dtrace_invop_jump_addr): | _C_LABEL(dtrace_invop_jump_addr): | ||||
.word 0 | .word 0 | ||||
.word 0 | .word 0 | ||||
#endif | #endif | ||||
.text | .text | ||||
.align 2 | .align 2 | ||||
#define DATA_ABORT 1 | |||||
/* | /* | ||||
* ASM macros for pushing and pulling trapframes from the stack | * ASM macros for pushing and pulling trapframes from the stack | ||||
* | * | ||||
* These macros are used to handle the irqframe and trapframe structures | * These macros are used to handle the irqframe and trapframe structures | ||||
* defined above. | * defined above. | ||||
*/ | */ | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | |||||
* This should only be used if the processor is not currently in SVC32 | * This should only be used if the processor is not currently in SVC32 | ||||
* mode. The processor mode is switched to SVC mode and the trap frame is | * mode. The processor mode is switched to SVC mode and the trap frame is | ||||
* stored. The SVC lr field is used to store the previous value of | * stored. The SVC lr field is used to store the previous value of | ||||
* lr in SVC mode. | * lr in SVC mode. | ||||
* | * | ||||
* NOTE: r13 and r14 are stored separately as a work around for the | * NOTE: r13 and r14 are stored separately as a work around for the | ||||
* SA110 rev 2 STM^ bug | * SA110 rev 2 STM^ bug | ||||
*/ | */ | ||||
#if __ARM_ARCH < 6 | #if __ARM_ARCH < 6 | ||||
#define PUSHFRAMEINSVC \ | .macro PUSHFRAMEINSVC gp | ||||
stmdb sp, {r0-r3}; /* Save 4 registers */ \ | stmdb sp, {r0-r5}; /* Save 6 registers */ | ||||
mov r0, lr; /* Save xxx32 r14 */ \ | .if \gp == DATA_ABORT | ||||
mov r1, sp; /* Save xxx32 sp */ \ | mrc p15, 0, r4, c6, c0, 0; /* Read DFAR */ | ||||
mrs r3, spsr; /* Save xxx32 spsr */ \ | lsr r4, r4, #(PAGE_SHIFT); | ||||
mrs r2, cpsr; /* Get the CPSR */ \ | .endif | ||||
bic r2, r2, #(PSR_MODE); /* Fix for SVC mode */ \ | mov r0, lr; /* Save xxx32 r14 */ | ||||
orr r2, r2, #(PSR_SVC32_MODE); \ | mov r1, sp; /* Save xxx32 sp */ | ||||
msr cpsr_c, r2; /* Punch into SVC mode */ \ | mrs r3, spsr; /* Save xxx32 spsr */ | ||||
mov r2, sp; /* Save SVC sp */ \ | mrs r2, cpsr; /* Get the CPSR */ | ||||
bic sp, sp, #7; /* Align sp to an 8-byte addrress */ \ | bic r2, r2, #(PSR_MODE); /* Fix for SVC mode */ | ||||
sub sp, sp, #(4 * 17); /* Pad trapframe to keep alignment */ \ | orr r2, r2, #(PSR_SVC32_MODE); | ||||
/* and for dtrace to emulate push/pop */ \ | msr cpsr_c, r2; /* Punch into SVC mode */ | ||||
str r0, [sp, #-4]!; /* Push return address */ \ | mov r2, sp; /* Save SVC sp */ | ||||
str lr, [sp, #-4]!; /* Push SVC lr */ \ | .if \gp == DATA_ABORT | ||||
str r2, [sp, #-4]!; /* Push SVC sp */ \ | mov r5, r3; /* If we were in usermode, skip guard page checking */ | ||||
msr spsr_fsxc, r3; /* Restore correct spsr */ \ | and r5, r5, #(PSR_MODE); | ||||
ldmdb r1, {r0-r3}; /* Restore 4 regs from xxx mode */ \ | teq r5, #(PSR_USR32_MODE); | ||||
sub sp, sp, #(4*15); /* Adjust the stack pointer */ \ | beq 1f; | ||||
stmia sp, {r0-r12}; /* Push the user mode registers */ \ | lsr r5, sp, #(PAGE_SHIFT); /* Check if SVC SP is in page that faulted */ | ||||
add r0, sp, #(4*13); /* Adjust the stack pointer */ \ | cmp r4, r5; | ||||
stmia r0, {r13-r14}^; /* Push the user mode registers */ \ | bne 1f; | ||||
mov r0, r0; /* NOP for previous instruction */ \ | sub r5, sp, #(20*4); /* Check if trapframe falls into faulted page */ | ||||
ldr r5, =ARM_RAS_START; /* Check if there's any RAS */ \ | lsr r5, r5, #(PAGE_SHIFT); | ||||
ldr r4, [r5, #4]; /* reset it to point at the */ \ | cmp r4, r5; | ||||
cmp r4, #0xffffffff; /* end of memory if necessary; */ \ | bne 1f; | ||||
movne r1, #0xffffffff; /* leave value in r4 for later */ \ | ldr sp, .Laux_rescue_stack; /* | ||||
strne r1, [r5, #4]; /* comparision against PC. */ \ | Kernel SP-trapframe is inside faulted page. | ||||
ldr r3, [r5]; /* Retrieve global RAS_START */ \ | Use special stack instead | ||||
cmp r3, #0; /* and reset it if non-zero. */ \ | */ | ||||
movne r1, #0; /* If non-zero RAS_START and */ \ | add sp, sp, #(AUX_RESCUE_STACK_SIZE); | ||||
strne r1, [r5]; /* PC was lower than RAS_END, */ \ | 1: | ||||
ldrne r1, [r0, #16]; /* adjust the saved PC so that */ \ | .endif | ||||
cmpne r4, r1; /* execution later resumes at */ \ | bic sp, sp, #7; /* Align sp to an 8-byte addrress */ | ||||
strhi r3, [r0, #16]; /* the RAS_START location. */ \ | sub sp, sp, #(4 * 17); /* Pad trapframe to keep alignment */ | ||||
mrs r0, spsr; \ | /* and for dtrace to emulate push/pop */ | ||||
str r0, [sp, #-4]!; /* Push return address */ | |||||
str lr, [sp, #-4]!; /* Push SVC lr */ | |||||
str r2, [sp, #-4]!; /* Push SVC sp */ | |||||
msr spsr_fsxc, r3; /* Restore correct spsr */ | |||||
ldmdb r1, {r0-r5}; /* Restore 6 regs from xxx mode */ | |||||
sub sp, sp, #(4*15); /* Adjust the stack pointer */ | |||||
stmia sp, {r0-r12}; /* Push the user mode registers */ | |||||
add r0, sp, #(4*13); /* Adjust the stack pointer */ | |||||
stmia r0, {r13-r14}^; /* Push the user mode registers */ | |||||
mov r0, r0; /* NOP for previous instruction */ | |||||
ldr r5, =ARM_RAS_START; /* Check if there's any RAS */ | |||||
ldr r4, [r5, #4]; /* reset it to point at the */ | |||||
cmp r4, #0xffffffff; /* end of memory if necessary; */ | |||||
movne r1, #0xffffffff; /* leave value in r4 for later */ | |||||
strne r1, [r5, #4]; /* comparision against PC. */ | |||||
ldr r3, [r5]; /* Retrieve global RAS_START */ | |||||
cmp r3, #0; /* and reset it if non-zero. */ | |||||
movne r1, #0; /* If non-zero RAS_START and */ | |||||
strne r1, [r5]; /* PC was lower than RAS_END, */ | |||||
ldrne r1, [r0, #16]; /* adjust the saved PC so that */ | |||||
cmpne r4, r1; /* execution later resumes at */ | |||||
strhi r3, [r0, #16]; /* the RAS_START location. */ | |||||
mrs r0, spsr; | |||||
str r0, [sp, #-4]! | str r0, [sp, #-4]! | ||||
.endm | |||||
#else | #else | ||||
#define PUSHFRAMEINSVC \ | .macro PUSHFRAMEINSVC gp | ||||
stmdb sp, {r0-r3}; /* Save 4 registers */ \ | stmdb sp, {r0-r5}; /* Save 6 registers */ | ||||
mov r0, lr; /* Save xxx32 r14 */ \ | .if \gp == DATA_ABORT | ||||
mov r1, sp; /* Save xxx32 sp */ \ | mrc p15, 0, r4, c6, c0, 0; /* Read DFAR */ | ||||
mrs r3, spsr; /* Save xxx32 spsr */ \ | lsr r4, r4, #(PAGE_SHIFT); | ||||
mrs r2, cpsr; /* Get the CPSR */ \ | .endif | ||||
bic r2, r2, #(PSR_MODE); /* Fix for SVC mode */ \ | mov r0, lr; /* Save xxx32 r14 */ | ||||
orr r2, r2, #(PSR_SVC32_MODE); \ | mov r1, sp; /* Save xxx32 sp */ | ||||
msr cpsr_c, r2; /* Punch into SVC mode */ \ | mrs r3, spsr; /* Save xxx32 spsr */ | ||||
mov r2, sp; /* Save SVC sp */ \ | mrs r2, cpsr; /* Get the CPSR */ | ||||
bic sp, sp, #7; /* Align sp to an 8-byte addrress */ \ | bic r2, r2, #(PSR_MODE); /* Fix for SVC mode */ | ||||
sub sp, sp, #(4 * 17); /* Pad trapframe to keep alignment */ \ | orr r2, r2, #(PSR_SVC32_MODE); | ||||
/* and for dtrace to emulate push/pop */ \ | msr cpsr_c, r2; /* Punch into SVC mode */ | ||||
str r0, [sp, #-4]!; /* Push return address */ \ | mov r2, sp; /* Save SVC sp */ | ||||
str lr, [sp, #-4]!; /* Push SVC lr */ \ | .if \gp == DATA_ABORT | ||||
str r2, [sp, #-4]!; /* Push SVC sp */ \ | mov r5, r3; /* If we were in usermode, skip guard page checking */ | ||||
msr spsr_fsxc, r3; /* Restore correct spsr */ \ | and r5, r5, #(PSR_MODE); | ||||
ldmdb r1, {r0-r3}; /* Restore 4 regs from xxx mode */ \ | teq r5, #(PSR_USR32_MODE); | ||||
sub sp, sp, #(4*15); /* Adjust the stack pointer */ \ | beq 1f; | ||||
stmia sp, {r0-r12}; /* Push the user mode registers */ \ | lsr r5, sp, #(PAGE_SHIFT); /* Check if SVC SP is in page that faulted */ | ||||
add r0, sp, #(4*13); /* Adjust the stack pointer */ \ | cmp r4, r5; | ||||
stmia r0, {r13-r14}^; /* Push the user mode registers */ \ | bne 1f; | ||||
mov r0, r0; /* NOP for previous instruction */ \ | sub r5, sp, #(20*4); /* Check if trapframe falls into faulted page */ | ||||
mrs r0, spsr; /* Put the SPSR on the stack */ \ | lsr r5, r5, #(PAGE_SHIFT); | ||||
cmp r4, r5; | |||||
bne 1f; | |||||
ldr sp, .Laux_rescue_stack; /* | |||||
Kernel SP-trapframe is inside faulted page. | |||||
Use special stack instead | |||||
*/ | |||||
add sp, sp, #(AUX_RESCUE_STACK_SIZE); | |||||
1: | |||||
.endif | |||||
bic sp, sp, #7; /* Align sp to an 8-byte addrress */ | |||||
sub sp, sp, #(4 * 17); /* Pad trapframe to keep alignment */ | |||||
/* and for dtrace to emulate push/pop */ | |||||
str r0, [sp, #-4]!; /* Push return address */ | |||||
str lr, [sp, #-4]!; /* Push SVC lr */ | |||||
str r2, [sp, #-4]!; /* Push SVC sp */ | |||||
msr spsr_fsxc, r3; /* Restore correct spsr */ | |||||
ldmdb r1, {r0-r5}; /* Restore 6 regs from xxx mode */ | |||||
sub sp, sp, #(4*15); /* Adjust the stack pointer */ | |||||
stmia sp, {r0-r12}; /* Push the user mode registers */ | |||||
add r0, sp, #(4*13); /* Adjust the stack pointer */ | |||||
stmia r0, {r13-r14}^; /* Push the user mode registers */ | |||||
mov r0, r0; /* NOP for previous instruction */ | |||||
mrs r0, spsr; /* Put the SPSR on the stack */ | |||||
str r0, [sp, #-4]! | str r0, [sp, #-4]! | ||||
.endm | |||||
#endif | #endif | ||||
/* | /* | ||||
* PULLFRAMEFROMSVCANDEXIT - macro to pull a trap frame from the stack | * PULLFRAMEFROMSVCANDEXIT - macro to pull a trap frame from the stack | ||||
* in SVC32 mode and restore the saved processor mode and PC. | * in SVC32 mode and restore the saved processor mode and PC. | ||||
* This should be used when the SVC lr register needs to be restored on | * This should be used when the SVC lr register needs to be restored on | ||||
* exit. | * exit. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 100 Lines • ▼ Show 20 Lines | |||||
* on exit (without transitioning back through the abort mode stack). | * on exit (without transitioning back through the abort mode stack). | ||||
*/ | */ | ||||
ASENTRY_NP(prefetch_abort_entry) | ASENTRY_NP(prefetch_abort_entry) | ||||
#ifdef __XSCALE__ | #ifdef __XSCALE__ | ||||
nop /* Make absolutely sure any pending */ | nop /* Make absolutely sure any pending */ | ||||
nop /* imprecise aborts have occurred. */ | nop /* imprecise aborts have occurred. */ | ||||
#endif | #endif | ||||
sub lr, lr, #4 /* Adjust the lr. Transition to scv32 */ | sub lr, lr, #4 /* Adjust the lr. Transition to scv32 */ | ||||
PUSHFRAMEINSVC /* mode stack, build trapframe there. */ | PUSHFRAMEINSVC 0 /* mode stack, build trapframe there. */ | ||||
adr lr, exception_exit /* Return from handler via standard */ | adr lr, exception_exit /* Return from handler via standard */ | ||||
mov r0, sp /* exception exit routine. Pass the */ | mov r0, sp /* exception exit routine. Pass the */ | ||||
mov r1, #1 /* Type flag */ | mov r1, #1 /* Type flag */ | ||||
b _C_LABEL(abort_handler) | b _C_LABEL(abort_handler) | ||||
END(prefetch_abort_entry) | END(prefetch_abort_entry) | ||||
/* | /* | ||||
* Entry point for a Data Abort exception. | * Entry point for a Data Abort exception. | ||||
* | * | ||||
* The hardware switches to the abort mode stack; we switch to svc32 before | * The hardware switches to the abort mode stack; we switch to svc32 before | ||||
* calling the handler, then return directly to the original mode/stack | * calling the handler, then return directly to the original mode/stack | ||||
* on exit (without transitioning back through the abort mode stack). | * on exit (without transitioning back through the abort mode stack). | ||||
*/ | */ | ||||
ASENTRY_NP(data_abort_entry) | ASENTRY_NP(data_abort_entry) | ||||
#ifdef __XSCALE__ | #ifdef __XSCALE__ | ||||
nop /* Make absolutely sure any pending */ | nop /* Make absolutely sure any pending */ | ||||
nop /* imprecise aborts have occurred. */ | nop /* imprecise aborts have occurred. */ | ||||
#endif | #endif | ||||
sub lr, lr, #8 /* Adjust the lr. Transition to scv32 */ | sub lr, lr, #8 /* Adjust the lr. Transition to scv32 */ | ||||
PUSHFRAMEINSVC /* mode stack, build trapframe there. */ | PUSHFRAMEINSVC DATA_ABORT /* mode stack, build trapframe there. */ | ||||
adr lr, exception_exit /* Exception exit routine */ | adr lr, exception_exit /* Exception exit routine */ | ||||
mov r0, sp /* Trapframe to the handler */ | mov r0, sp /* Trapframe to the handler */ | ||||
mov r1, #0 /* Type flag */ | mov r1, #0 /* Type flag */ | ||||
b _C_LABEL(abort_handler) | b _C_LABEL(abort_handler) | ||||
END(data_abort_entry) | END(data_abort_entry) | ||||
/* | /* | ||||
* Entry point for an Undefined Instruction exception. | * Entry point for an Undefined Instruction exception. | ||||
* | * | ||||
* The hardware switches to the undefined mode stack; we switch to svc32 before | * The hardware switches to the undefined mode stack; we switch to svc32 before | ||||
* calling the handler, then return directly to the original mode/stack | * calling the handler, then return directly to the original mode/stack | ||||
* on exit (without transitioning back through the undefined mode stack). | * on exit (without transitioning back through the undefined mode stack). | ||||
*/ | */ | ||||
ASENTRY_NP(undefined_entry) | ASENTRY_NP(undefined_entry) | ||||
PUSHFRAMEINSVC /* mode stack, build trapframe there. */ | PUSHFRAMEINSVC 0 /* mode stack, build trapframe there. */ | ||||
mov r4, r0 /* R0 contains SPSR */ | mov r4, r0 /* R0 contains SPSR */ | ||||
adr lr, exception_exit /* Return from handler via standard */ | adr lr, exception_exit /* Return from handler via standard */ | ||||
mov r0, sp /* exception exit routine. pass frame */ | mov r0, sp /* exception exit routine. pass frame */ | ||||
ldr r2, [sp, #(TF_PC)] /* load pc */ | ldr r2, [sp, #(TF_PC)] /* load pc */ | ||||
#if __ARM_ARCH >= 7 | #if __ARM_ARCH >= 7 | ||||
tst r4, #(PSR_T) /* test if PSR_T */ | tst r4, #(PSR_T) /* test if PSR_T */ | ||||
subne r2, r2, #(THUMB_INSN_SIZE) | subne r2, r2, #(THUMB_INSN_SIZE) | ||||
Show All 28 Lines | |||||
* Entry point for a normal IRQ. | * Entry point for a normal IRQ. | ||||
* | * | ||||
* The hardware switches to the IRQ mode stack; we switch to svc32 before | * The hardware switches to the IRQ mode stack; we switch to svc32 before | ||||
* calling the handler, then return directly to the original mode/stack | * calling the handler, then return directly to the original mode/stack | ||||
* on exit (without transitioning back through the IRQ mode stack). | * on exit (without transitioning back through the IRQ mode stack). | ||||
*/ | */ | ||||
ASENTRY_NP(irq_entry) | ASENTRY_NP(irq_entry) | ||||
sub lr, lr, #4 /* Adjust the lr. Transition to scv32 */ | sub lr, lr, #4 /* Adjust the lr. Transition to scv32 */ | ||||
PUSHFRAMEINSVC /* mode stack, build trapframe there. */ | PUSHFRAMEINSVC 0 /* mode stack, build trapframe there. */ | ||||
adr lr, exception_exit /* Return from handler via standard */ | adr lr, exception_exit /* Return from handler via standard */ | ||||
mov r0, sp /* exception exit routine. Pass the */ | mov r0, sp /* exception exit routine. Pass the */ | ||||
b _C_LABEL(intr_irq_handler)/* trapframe to the handler. */ | b _C_LABEL(intr_irq_handler)/* trapframe to the handler. */ | ||||
END(irq_entry) | END(irq_entry) | ||||
/* | /* | ||||
* Entry point for an FIQ interrupt. | * Entry point for an FIQ interrupt. | ||||
* | * | ||||
▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
.global _C_LABEL(fiq_nullhandler_code), _C_LABEL(fiq_nullhandler_size) | .global _C_LABEL(fiq_nullhandler_code), _C_LABEL(fiq_nullhandler_size) | ||||
_C_LABEL(fiq_nullhandler_code): | _C_LABEL(fiq_nullhandler_code): | ||||
.word .fiqv | .word .fiqv | ||||
_C_LABEL(fiq_nullhandler_size): | _C_LABEL(fiq_nullhandler_size): | ||||
.word 4 | .word 4 | ||||
.Laux_rescue_stack: | |||||
.word _C_LABEL(__aux_rescue_stack) |