15d9de2d3SDavid du Colombier/* 25d9de2d3SDavid du Colombier * arm exception handlers 35d9de2d3SDavid du Colombier */ 45d9de2d3SDavid du Colombier#include "arm.s" 55d9de2d3SDavid du Colombier 65d9de2d3SDavid du Colombier/* 75d9de2d3SDavid du Colombier * exception vectors, copied by trapinit() to somewhere useful 85d9de2d3SDavid du Colombier */ 95d9de2d3SDavid du ColombierTEXT vectors(SB), 1, $-4 105d9de2d3SDavid du Colombier MOVW 0x18(R15), R15 /* reset */ 115d9de2d3SDavid du Colombier MOVW 0x18(R15), R15 /* undefined instr. */ 125d9de2d3SDavid du Colombier MOVW 0x18(R15), R15 /* SWI & SMC */ 135d9de2d3SDavid du Colombier MOVW 0x18(R15), R15 /* prefetch abort */ 145d9de2d3SDavid du Colombier MOVW 0x18(R15), R15 /* data abort */ 155d9de2d3SDavid du Colombier MOVW 0x18(R15), R15 /* reserved */ 165d9de2d3SDavid du Colombier MOVW 0x18(R15), R15 /* IRQ */ 175d9de2d3SDavid du Colombier MOVW 0x18(R15), R15 /* FIQ */ 185d9de2d3SDavid du Colombier 195d9de2d3SDavid du ColombierTEXT vtable(SB), 1, $-4 205d9de2d3SDavid du Colombier WORD $_vsvc(SB) /* reset, in svc mode already */ 215d9de2d3SDavid du Colombier WORD $_vund(SB) /* undefined, switch to svc mode */ 225d9de2d3SDavid du Colombier WORD $_vsvc(SB) /* swi, in svc mode already */ 235d9de2d3SDavid du Colombier WORD $_vpabt(SB) /* prefetch abort, switch to svc mode */ 245d9de2d3SDavid du Colombier WORD $_vdabt(SB) /* data abort, switch to svc mode */ 255d9de2d3SDavid du Colombier WORD $_vsvc(SB) /* reserved */ 265d9de2d3SDavid du Colombier WORD $_virq(SB) /* IRQ, switch to svc mode */ 275d9de2d3SDavid du Colombier WORD $_vfiq(SB) /* FIQ, switch to svc mode */ 285d9de2d3SDavid du Colombier 295d9de2d3SDavid du ColombierTEXT _vsvc(SB), 1, $-4 /* SWI */ 30*5c47fe09SDavid du Colombier CLREX 315d9de2d3SDavid du Colombier MOVW.W R14, -4(R13) /* ureg->pc = interrupted PC */ 325d9de2d3SDavid du Colombier MOVW SPSR, R14 /* ureg->psr = SPSR */ 335d9de2d3SDavid du Colombier MOVW.W R14, -4(R13) /* ... */ 345d9de2d3SDavid du Colombier MOVW $PsrMsvc, R14 /* ureg->type = PsrMsvc */ 355d9de2d3SDavid du Colombier MOVW.W R14, -4(R13) /* ... */ 365d9de2d3SDavid du Colombier 375d9de2d3SDavid du Colombier /* avoid the ambiguity described in notes/movm.w. */ 385d9de2d3SDavid du Colombier MOVM.DB.S [R0-R14], (R13) /* save user level registers */ 395d9de2d3SDavid du Colombier SUB $(15*4), R13 /* r13 now points to ureg */ 405d9de2d3SDavid du Colombier 415d9de2d3SDavid du Colombier MOVW $setR12(SB), R12 /* Make sure we've got the kernel's SB loaded */ 425d9de2d3SDavid du Colombier 43*5c47fe09SDavid du Colombier /* get R(MACH) for this cpu */ 44*5c47fe09SDavid du Colombier CPUID(R1) 45*5c47fe09SDavid du Colombier SLL $2, R1 /* convert to word index */ 46*5c47fe09SDavid du Colombier MOVW $machaddr(SB), R2 47*5c47fe09SDavid du Colombier ADD R1, R2 48*5c47fe09SDavid du Colombier MOVW (R2), R(MACH) /* m = machaddr[cpuid] */ 49*5c47fe09SDavid du Colombier CMP $0, R(MACH) 50*5c47fe09SDavid du Colombier MOVW.EQ $MACHADDR, R0 /* paranoia: use MACHADDR if 0 */ 51*5c47fe09SDavid du Colombier 52*5c47fe09SDavid du Colombier MOVW 8(R(MACH)), R(USER) /* up */ 535d9de2d3SDavid du Colombier 545d9de2d3SDavid du Colombier MOVW R13, R0 /* first arg is pointer to ureg */ 555d9de2d3SDavid du Colombier SUB $8, R13 /* space for argument+link */ 565d9de2d3SDavid du Colombier 575d9de2d3SDavid du Colombier BL syscall(SB) 585d9de2d3SDavid du Colombier 595d9de2d3SDavid du Colombier ADD $(8+4*15), R13 /* make r13 point to ureg->type */ 605d9de2d3SDavid du Colombier MOVW 8(R13), R14 /* restore link */ 615d9de2d3SDavid du Colombier MOVW 4(R13), R0 /* restore SPSR */ 625d9de2d3SDavid du Colombier MOVW R0, SPSR /* ... */ 635d9de2d3SDavid du Colombier MOVM.DB.S (R13), [R0-R14] /* restore registers */ 645d9de2d3SDavid du Colombier ADD $8, R13 /* pop past ureg->{type+psr} */ 655d9de2d3SDavid du Colombier RFE /* MOVM.IA.S.W (R13), [R15] */ 665d9de2d3SDavid du Colombier 675d9de2d3SDavid du ColombierTEXT _vund(SB), 1, $-4 /* undefined */ 685d9de2d3SDavid du Colombier MOVM.IA [R0-R4], (R13) /* free some working space */ 695d9de2d3SDavid du Colombier MOVW $PsrMund, R0 705d9de2d3SDavid du Colombier B _vswitch 715d9de2d3SDavid du Colombier 725d9de2d3SDavid du ColombierTEXT _vpabt(SB), 1, $-4 /* prefetch abort */ 735d9de2d3SDavid du Colombier MOVM.IA [R0-R4], (R13) /* free some working space */ 745d9de2d3SDavid du Colombier MOVW $PsrMabt, R0 /* r0 = type */ 755d9de2d3SDavid du Colombier B _vswitch 765d9de2d3SDavid du Colombier 775d9de2d3SDavid du ColombierTEXT _vdabt(SB), 1, $-4 /* data abort */ 785d9de2d3SDavid du Colombier MOVM.IA [R0-R4], (R13) /* free some working space */ 795d9de2d3SDavid du Colombier MOVW $(PsrMabt+1), R0 /* r0 = type */ 805d9de2d3SDavid du Colombier B _vswitch 815d9de2d3SDavid du Colombier 825d9de2d3SDavid du ColombierTEXT _virq(SB), 1, $-4 /* IRQ */ 835d9de2d3SDavid du Colombier MOVM.IA [R0-R4], (R13) /* free some working space */ 845d9de2d3SDavid du Colombier MOVW $PsrMirq, R0 /* r0 = type */ 855d9de2d3SDavid du Colombier B _vswitch 865d9de2d3SDavid du Colombier 875d9de2d3SDavid du Colombier /* 885d9de2d3SDavid du Colombier * come here with type in R0 and R13 pointing above saved [r0-r4]. 895d9de2d3SDavid du Colombier * we'll switch to SVC mode and then call trap. 905d9de2d3SDavid du Colombier */ 915d9de2d3SDavid du Colombier_vswitch: 92*5c47fe09SDavid du Colombier CLREX 935d9de2d3SDavid du Colombier MOVW SPSR, R1 /* save SPSR for ureg */ 945d9de2d3SDavid du Colombier MOVW R14, R2 /* save interrupted pc for ureg */ 955d9de2d3SDavid du Colombier MOVW R13, R3 /* save pointer to where the original [R0-R4] are */ 965d9de2d3SDavid du Colombier 975d9de2d3SDavid du Colombier /* 985d9de2d3SDavid du Colombier * switch processor to svc mode. this switches the banked registers 995d9de2d3SDavid du Colombier * (r13 [sp] and r14 [link]) to those of svc mode. 1005d9de2d3SDavid du Colombier */ 1015d9de2d3SDavid du Colombier MOVW CPSR, R14 1025d9de2d3SDavid du Colombier BIC $PsrMask, R14 1035d9de2d3SDavid du Colombier ORR $(PsrDirq|PsrMsvc), R14 1045d9de2d3SDavid du Colombier MOVW R14, CPSR /* switch! */ 1055d9de2d3SDavid du Colombier 1065d9de2d3SDavid du Colombier AND.S $0xf, R1, R4 /* interrupted code kernel or user? */ 1075d9de2d3SDavid du Colombier BEQ _userexcep 1085d9de2d3SDavid du Colombier 1095d9de2d3SDavid du Colombier /* here for trap from SVC mode */ 1105d9de2d3SDavid du Colombier MOVM.DB.W [R0-R2], (R13) /* set ureg->{type, psr, pc}; r13 points to ureg->type */ 1115d9de2d3SDavid du Colombier MOVM.IA (R3), [R0-R4] /* restore [R0-R4] from previous mode's stack */ 1125d9de2d3SDavid du Colombier 1135d9de2d3SDavid du Colombier /* 1145d9de2d3SDavid du Colombier * avoid the ambiguity described in notes/movm.w. 1155d9de2d3SDavid du Colombier * In order to get a predictable value in R13 after the stores, 1165d9de2d3SDavid du Colombier * separate the store-multiple from the stack-pointer adjustment. 1175d9de2d3SDavid du Colombier * We'll assume that the old value of R13 should be stored on the stack. 1185d9de2d3SDavid du Colombier */ 1195d9de2d3SDavid du Colombier /* save kernel level registers, at end r13 points to ureg */ 1205d9de2d3SDavid du Colombier MOVM.DB [R0-R14], (R13) 1215d9de2d3SDavid du Colombier SUB $(15*4), R13 /* SP now points to saved R0 */ 1225d9de2d3SDavid du Colombier 1235d9de2d3SDavid du Colombier MOVW $setR12(SB), R12 /* Make sure we've got the kernel's SB loaded */ 1245d9de2d3SDavid du Colombier 1255d9de2d3SDavid du Colombier MOVW R13, R0 /* first arg is pointer to ureg */ 1265d9de2d3SDavid du Colombier SUB $(4*2), R13 /* space for argument+link (for debugger) */ 1275d9de2d3SDavid du Colombier MOVW $0xdeaddead, R11 /* marker */ 1285d9de2d3SDavid du Colombier 1295d9de2d3SDavid du Colombier BL trap(SB) 1305d9de2d3SDavid du Colombier 131*5c47fe09SDavid du Colombier MOVW $setR12(SB), R12 /* reload kernel's SB (ORLY?) */ 1325d9de2d3SDavid du Colombier ADD $(4*2+4*15), R13 /* make r13 point to ureg->type */ 133*5c47fe09SDavid du Colombier /* 134*5c47fe09SDavid du Colombier * if we interrupted a previous trap's handler and are now 135*5c47fe09SDavid du Colombier * returning to it, we need to propagate the current R(MACH) (R10) 136*5c47fe09SDavid du Colombier * by overriding the saved one on the stack, since we may have 137*5c47fe09SDavid du Colombier * been rescheduled and be on a different processor now than 138*5c47fe09SDavid du Colombier * at entry. 139*5c47fe09SDavid du Colombier */ 140*5c47fe09SDavid du Colombier MOVW R(MACH), (-(15-MACH)*4)(R13) /* restore current cpu's MACH */ 1415d9de2d3SDavid du Colombier MOVW 8(R13), R14 /* restore link */ 1425d9de2d3SDavid du Colombier MOVW 4(R13), R0 /* restore SPSR */ 1435d9de2d3SDavid du Colombier MOVW R0, SPSR /* ... */ 1445d9de2d3SDavid du Colombier 1455d9de2d3SDavid du Colombier MOVM.DB (R13), [R0-R14] /* restore registers */ 1465d9de2d3SDavid du Colombier 1475d9de2d3SDavid du Colombier ADD $(4*2), R13 /* pop past ureg->{type+psr} to pc */ 1485d9de2d3SDavid du Colombier RFE /* MOVM.IA.S.W (R13), [R15] */ 1495d9de2d3SDavid du Colombier 1505d9de2d3SDavid du Colombier /* here for trap from USER mode */ 1515d9de2d3SDavid du Colombier_userexcep: 1525d9de2d3SDavid du Colombier MOVM.DB.W [R0-R2], (R13) /* set ureg->{type, psr, pc}; r13 points to ureg->type */ 1535d9de2d3SDavid du Colombier MOVM.IA (R3), [R0-R4] /* restore [R0-R4] from previous mode's stack */ 1545d9de2d3SDavid du Colombier 1555d9de2d3SDavid du Colombier /* avoid the ambiguity described in notes/movm.w. */ 1565d9de2d3SDavid du Colombier MOVM.DB.S [R0-R14], (R13) /* save kernel level registers */ 1575d9de2d3SDavid du Colombier SUB $(15*4), R13 /* r13 now points to ureg */ 1585d9de2d3SDavid du Colombier 1595d9de2d3SDavid du Colombier MOVW $setR12(SB), R12 /* Make sure we've got the kernel's SB loaded */ 1605d9de2d3SDavid du Colombier 161*5c47fe09SDavid du Colombier /* get R(MACH) for this cpu */ 162*5c47fe09SDavid du Colombier CPUID(R1) 163*5c47fe09SDavid du Colombier SLL $2, R1 /* convert to word index */ 164*5c47fe09SDavid du Colombier MOVW $machaddr(SB), R2 165*5c47fe09SDavid du Colombier ADD R1, R2 166*5c47fe09SDavid du Colombier MOVW (R2), R(MACH) /* m = machaddr[cpuid] */ 167*5c47fe09SDavid du Colombier CMP $0, R(MACH) 168*5c47fe09SDavid du Colombier MOVW.EQ $MACHADDR, R(MACH) /* paranoia: use MACHADDR if 0 */ 169*5c47fe09SDavid du Colombier 170*5c47fe09SDavid du Colombier MOVW 8(R(MACH)), R(USER) /* up */ 1715d9de2d3SDavid du Colombier 1725d9de2d3SDavid du Colombier MOVW R13, R0 /* first arg is pointer to ureg */ 1735d9de2d3SDavid du Colombier SUB $(4*2), R13 /* space for argument+link (for debugger) */ 1745d9de2d3SDavid du Colombier 1755d9de2d3SDavid du Colombier BL trap(SB) 1765d9de2d3SDavid du Colombier 1775d9de2d3SDavid du Colombier ADD $(4*2+4*15), R13 /* make r13 point to ureg->type */ 1785d9de2d3SDavid du Colombier MOVW 8(R13), R14 /* restore link */ 1795d9de2d3SDavid du Colombier MOVW 4(R13), R0 /* restore SPSR */ 1805d9de2d3SDavid du Colombier MOVW R0, SPSR /* ... */ 1815d9de2d3SDavid du Colombier MOVM.DB.S (R13), [R0-R14] /* restore registers */ 1825d9de2d3SDavid du Colombier ADD $(4*2), R13 /* pop past ureg->{type+psr} */ 1835d9de2d3SDavid du Colombier RFE /* MOVM.IA.S.W (R13), [R15] */ 1845d9de2d3SDavid du Colombier 1855d9de2d3SDavid du ColombierTEXT _vfiq(SB), 1, $-4 /* FIQ */ 186*5c47fe09SDavid du Colombier CLREX 1875d9de2d3SDavid du Colombier MOVW $PsrMfiq, R8 /* trap type */ 1885d9de2d3SDavid du Colombier MOVW SPSR, R9 /* interrupted psr */ 1895d9de2d3SDavid du Colombier MOVW R14, R10 /* interrupted pc */ 1905d9de2d3SDavid du Colombier MOVM.DB.W [R8-R10], (R13) /* save in ureg */ 191*5c47fe09SDavid du Colombier MOVM.DB.S [R0-R14], (R13) /* save interrupted regs */ 192*5c47fe09SDavid du Colombier SUB $(15*4), R13 1935d9de2d3SDavid du Colombier MOVW $setR12(SB), R12 /* Make sure we've got the kernel's SB loaded */ 194*5c47fe09SDavid du Colombier /* get R(MACH) for this cpu */ 195*5c47fe09SDavid du Colombier CPUID(R1) 196*5c47fe09SDavid du Colombier SLL $2, R1 /* convert to word index */ 197*5c47fe09SDavid du Colombier MOVW $machaddr(SB), R2 198*5c47fe09SDavid du Colombier ADD R1, R2 199*5c47fe09SDavid du Colombier MOVW (R2), R(MACH) /* m = machaddr[cpuid] */ 200*5c47fe09SDavid du Colombier CMP $0, R(MACH) 201*5c47fe09SDavid du Colombier MOVW.EQ $MACHADDR, R(MACH) /* paranoia: use MACHADDR if 0 */ 202*5c47fe09SDavid du Colombier 203*5c47fe09SDavid du Colombier MOVW 8(R(MACH)), R(USER) /* up */ 2045d9de2d3SDavid du Colombier MOVW R13, R0 /* first arg is pointer to ureg */ 2055d9de2d3SDavid du Colombier SUB $(4*2), R13 /* space for argument+link (for debugger) */ 2065d9de2d3SDavid du Colombier 2075d9de2d3SDavid du Colombier BL fiq(SB) 2085d9de2d3SDavid du Colombier 2095d9de2d3SDavid du Colombier ADD $(8+4*15), R13 /* make r13 point to ureg->type */ 2105d9de2d3SDavid du Colombier MOVW 8(R13), R14 /* restore link */ 2115d9de2d3SDavid du Colombier MOVW 4(R13), R0 /* restore SPSR */ 2125d9de2d3SDavid du Colombier MOVW R0, SPSR /* ... */ 2135d9de2d3SDavid du Colombier MOVM.DB.S (R13), [R0-R14] /* restore registers */ 2145d9de2d3SDavid du Colombier ADD $8, R13 /* pop past ureg->{type+psr} */ 2155d9de2d3SDavid du Colombier RFE /* MOVM.IA.S.W (R13), [R15] */ 2165d9de2d3SDavid du Colombier 2175d9de2d3SDavid du Colombier/* 2185d9de2d3SDavid du Colombier * set the stack value for the mode passed in R0 2195d9de2d3SDavid du Colombier */ 2205d9de2d3SDavid du ColombierTEXT setr13(SB), 1, $-4 2215d9de2d3SDavid du Colombier MOVW 4(FP), R1 2225d9de2d3SDavid du Colombier 2235d9de2d3SDavid du Colombier MOVW CPSR, R2 2245d9de2d3SDavid du Colombier BIC $PsrMask, R2, R3 225*5c47fe09SDavid du Colombier ORR $(PsrDirq|PsrDfiq), R3 2265d9de2d3SDavid du Colombier ORR R0, R3 2275d9de2d3SDavid du Colombier MOVW R3, CPSR /* switch to new mode */ 2285d9de2d3SDavid du Colombier 2295d9de2d3SDavid du Colombier MOVW R13, R0 /* return old sp */ 2305d9de2d3SDavid du Colombier MOVW R1, R13 /* install new one */ 2315d9de2d3SDavid du Colombier 2325d9de2d3SDavid du Colombier MOVW R2, CPSR /* switch back to old mode */ 2335d9de2d3SDavid du Colombier RET 234