/* $NetBSD: trap_subr.S,v 1.27.4.1 2020/03/03 18:54:59 martin Exp $ */
/*
* Copyright 2001 Wasabi Systems, Inc.
* All rights reserved.
*
* Written by Eduardo Horvath and Simon Burge for Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed for the NetBSD Project by
* Wasabi Systems, Inc.
* 4. The name of Wasabi Systems, Inc. may not be used to endorse
* or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (C) 1995, 1996 Wolfgang Solfrank.
* Copyright (C) 1995, 1996 TooLs GmbH.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by TooLs GmbH.
* 4. The name of TooLs GmbH may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* NOTICE: This is not a standalone file. to use it, #include it in
* your port's locore.S, like so:
*
* #include <powerpc/ibm4xx/trap_subr.S>
*/
/*
* XXX Interrupt and spill stacks need to be per-CPU.
*/
#define GET_PCB(rX) \
GET_CPUINFO(rX); \
lwz rX,CI_CURPCB(rX)
#define STANDARD_PROLOG(savearea) \
mtsprg1 %r1; /* save SP */ \
GET_CPUINFO(%r1); \
stmw %r28,(savearea+CPUSAVE_R28)(%r1); /* free r28-r31 */ \
mflr %r28; /* save LR */ \
mfcr %r29; /* save CR */ \
mfsrr0 %r30; \
mfsrr1 %r31; /* Test whether we already had PR set */ \
stmw %r30,(savearea+CPUSAVE_SRR0)(%r1); /* save srr0/srr1 */ \
mfsprg1 %r1; /* restore SP */ \
mtcr %r31; \
bf MSR_PR,1f; /* branch if MSR[PR] is clear */ \
GET_PCB(%r1); \
addi %r1,%r1,USPACE-CALLFRAMELEN; /* stack is top of user struct */ \
1:
#define ACCESS_PROLOG(savearea) \
mtsprg1 %r1; /* save SP temporalily */ \
GET_CPUINFO(%r1); \
stmw %r28,(savearea+CPUSAVE_R28)(%r1); /* free r28-r31 */ \
mflr %r28; /* save LR */ \
mfcr %r29; /* save CR */ \
mfdear %r30; \
mfesr %r31; \
stmw %r30,(savearea+CPUSAVE_DEAR)(%r1); /* save esr/dear */ \
mfsrr0 %r30; \
mfsrr1 %r31; /* Test whether we already had PR set */ \
stmw %r30,(savearea+CPUSAVE_SRR0)(%r1); /* save srr0/srr1 */ \
mfsprg1 %r1; /* restore SP */ \
mtcr %r31; \
bf MSR_PR,1f; /* branch if MSR[PR] is clear */ \
GET_PCB(%r1); \
addi %r1,%r1,USPACE-CALLFRAMELEN; /* stack is top of user struct */ \
1:
#define CRITICAL_PROLOG(savearea) \
mtsprg1 %r1; /* save SP */ \
GET_CPUINFO(%r1); \
stmw %r28,(savearea+CPUSAVE_R28)(%r1); /* free r28-r31 */ \
mflr %r28; /* save LR */ \
mfcr %r29; /* save CR */ \
mfsrr2 %r30; /* Fake a standard trap */ \
mfsrr3 %r31; /* Test whether we already had PR set */ \
stmw %r30,(savearea+CPUSAVE_SRR0)(%r1); /* save srr0/srr1 */ \
mfsprg1 %r1; /* restore SP */ \
mtcr %r31; \
bf MSR_PR,1f; /* branch if MSR[PR] is clear */ \
GET_PCB(%r1); \
addi %r1,%r1,USPACE-CALLFRAMELEN; /* stack is top of user struct */ \
1:
/* Standard handler saves r1,r28-31,LR,CR, sets up the stack and calls s_trap */
#define STANDARD_EXC_HANDLER(name)\
.globl _C_LABEL(name ## trap),_C_LABEL(name ## size) ; \
_C_LABEL(name ## trap): \
STANDARD_PROLOG(CI_TEMPSAVE); \
bla s_trap; \
_C_LABEL(name ## size) = .-_C_LABEL(name ## trap)
/* Access exceptions also need DEAR and ESR saved */
#define ACCESS_EXC_HANDLER(name)\
.globl _C_LABEL(name ## trap),_C_LABEL(name ## size) ; \
_C_LABEL(name ## trap): \
ACCESS_PROLOG(CI_TEMPSAVE); \
bla s_trap; \
_C_LABEL(name ## size) = .-_C_LABEL(name ## trap)
/* Maybe this should call ddb.... */
#define CRITICAL_EXC_HANDLER(name)\
.globl _C_LABEL(name ## trap),_C_LABEL(name ## size) ; \
_C_LABEL(name ## trap): \
CRITICAL_PROLOG(CI_TEMPSAVE); \
bla s_trap; \
_C_LABEL(name ## size) = .-_C_LABEL(name ## trap)
#define INTR_PROLOG(tempsave) \
mtsprg1 %r1; /* save SP */ \
GET_CPUINFO(%r1); \
stmw %r28,(tempsave+CPUSAVE_R28)(%r1); /* free r28-r31 */ \
mflr %r28; /* save LR */ \
mfcr %r29; /* save CR */ \
mfxer %r30; /* save XER */ \
mfsrr1 %r31; \
mtcr %r31; \
mfsprg1 %r1; /* restore SP */ \
bf MSR_PR,1f; /* branch if PSL_PR is false */ \
GET_PCB(%r1); \
addi %r1,%r1,USPACE-CALLFRAMELEN; /* stack is top of user struct */ \
1:
.text
STANDARD_EXC_HANDLER(default)
ACCESS_EXC_HANDLER(access)
CRITICAL_EXC_HANDLER(critical)
/*
* This one for the external interrupt handler.
*/
.globl _C_LABEL(extint),_C_LABEL(extsize)
_C_LABEL(extint):
INTR_PROLOG(CI_TEMPSAVE)
ba extintr
_C_LABEL(extsize) = .-_C_LABEL(extint)
#if defined(DDB) || defined(KGDB)
/*
* In case of DDB we want a separate trap catcher for it
*/
.globl _C_LABEL(ddblow),_C_LABEL(ddbsize)
_C_LABEL(ddblow):
ACCESS_PROLOG(CI_DDBSAVE)
bla ddbtrap
_C_LABEL(ddbsize) = .-_C_LABEL(ddblow)
#endif /* DDB || KGDB */
#ifdef DEBUG
#define TRAP_IF_ZERO(r) tweqi r,0
#else
#define TRAP_IF_ZERO(r)
#endif
#define ENABLE_TRANSLATION(pidreg,tmpreg) \
mfpid pidreg; \
li tmpreg,KERNEL_PID; \
mtpid tmpreg; \
mfmsr tmpreg; \
ori tmpreg,tmpreg,(PSL_DR|PSL_IR)@l; \
mtmsr tmpreg; \
isync
/*
* FRAME_SETUP assumes:
* SPRG1 SP (r1)
* savearea r28-r31,DEAR,ESR,SRR0,SRR1
* (DEAR & ESR only for access traps)
* %r28 LR
* %r29 CR
* %r1 kernel stack
* LR trap type
*/
#define FRAME_SETUP(savearea) \
/* Have to enable translation to allow access of kernel stack: */ \
ENABLE_TRANSLATION(%r30,%r31); \
mfsprg1 %r31; \
stwu %r31,-FRAMELEN(%r1); \
stw %r30,FRAME_PID(%r1); \
stw %r0,FRAME_R0(%r1); \
stw %r31,FRAME_R1(%r1); \
stw %r2,FRAME_R2(%r1); \
GET_CPUINFO(%r2); \
stw %r28,FRAME_LR(%r1); \
stw %r29,FRAME_CR(%r1); \
lmw %r28,(savearea+CPUSAVE_R28)(%r2); \
stmw %r3,FRAME_R3(%r1); \
lmw %r28,(savearea+CPUSAVE_DEAR)(%r2); \
lwz %r13,CI_CURLWP(%r2); \
mfxer %r3; \
mfctr %r4; \
mflr %r5; \
andi. %r5,%r5,0xff00; \
stw %r3,FRAME_XER(%r1); \
stw %r4,FRAME_CTR(%r1); \
stw %r5,FRAME_EXC(%r1); \
stw %r28,FRAME_DEAR(%r1); \
stw %r29,FRAME_ESR(%r1); \
stw %r30,FRAME_SRR0(%r1); \
stw %r31,FRAME_SRR1(%r1)
#define FRAME_SAVE_CALLEE \
stmw %r14,FRAME_R14(%r1)
#define FRAME_RESTORE \
lwz %r6,FRAME_LR(%r1); \
lwz %r7,FRAME_CR(%r1); \
lwz %r8,FRAME_XER(%r1); \
lwz %r9,FRAME_CTR(%r1); \
lwz %r10,FRAME_SRR0(%r1); \
lwz %r11,FRAME_SRR1(%r1); \
mtlr %r6; \
mtcr %r7; \
mtxer %r8; \
mtctr %r9; \
mtsrr0 %r10; \
mtsrr1 %r11; \
lwz %r13,FRAME_R13(%r1); \
lwz %r12,FRAME_R12(%r1); \
lwz %r11,FRAME_R11(%r1); \
lwz %r10,FRAME_R10(%r1); \
lwz %r9,FRAME_R9(%r1); \
lwz %r8,FRAME_R8(%r1); \
lwz %r7,FRAME_R7(%r1); \
lwz %r6,FRAME_R6(%r1); \
lwz %r5,FRAME_R5(%r1); \
lwz %r4,FRAME_R4(%r1); \
lwz %r3,FRAME_R3(%r1); \
lwz %r2,FRAME_R2(%r1); \
lwz %r0,FRAME_R1(%r1); \
mtsprg1 %r0; \
lwz %r0,FRAME_R0(%r1)
/*
* Now the common trap catching code.
*/
s_trap:
FRAME_SETUP(CI_TEMPSAVE)
/* R31 = SRR1 */
/* Now we can recover interrupts again: */
trapagain:
wrtee %r31 /* reenable interrupts */
/* Call C trap code: */
addi %r3,%r1,FRAME_TF
bl _C_LABEL(trap)
.globl _C_LABEL(trapexit)
_C_LABEL(trapexit):
/* Disable interrupts: */
wrteei 0
/* Test AST pending: */
mtcr %r31
bf MSR_PR,trapleave_to_kernel /* branch if MSR[PR] is false */
lwz %r4,L_MD_ASTPENDING(%r13)
andi. %r4,%r4,1
beq trapleave_to_user
li %r6,EXC_AST
stw %r6,FRAME_EXC(%r1)
b trapagain
trapleave_to_kernel:
lmw %r14, FRAME_R14(%r1) /* restore callee registers */
intrleave_to_kernel:
FRAME_RESTORE /* old SP is now in sprg1 */
mtsprg2 %r30
mtsprg3 %r31
mfmsr %r30
li %r31,(PSL_DR|PSL_IR)@l
andc %r30,%r30,%r31
lwz %r31,FRAME_PID(%r1)
TRAP_IF_ZERO(%r31)
/*
* Now that we are done with the trapframe, we can load the original SP
*/
mfsprg1 %r1
mtmsr %r30 /* disable translation */
isync
mtpid %r31
mfsprg3 %r31
mfsprg2 %r30
IBM405_ERRATA77_SYNC
rfi
ba . /* Protect against prefetch */
trapleave_to_user:
lmw %r14, FRAME_R14(%r1) /* restore callee registers */
intrleave_to_user:
/* Now restore regs: */
lwz %r3,FRAME_PID(%r1)
lwz %r4,FRAME_SRR1(%r1)
bl _C_LABEL(ctx_setup)
TRAP_IF_ZERO(%r3)
stw %r3,FRAME_PID(%r1)
FRAME_RESTORE /* old SP is now in sprg1 */
/*
* We are returning to userspace so we need to switch PIDs.
* Since the kernel executes out of what would be userspace,
* we need to turn off translation before we set the PID.
*
* Alterantively, we could map a kernel page at 0xfffff000
* that had the mtpid code in it and branch to it and avoid
* all this. (ba foo; foo: mtpid %r31; mfsprg3 %r31; rfi;)
*/
mtsprg2 %r30
mtsprg3 %r31
mfmsr %r30
li %r31,(PSL_DR|PSL_IR)@l
andc %r30,%r30,%r31
lwz %r31,FRAME_PID(%r1)
TRAP_IF_ZERO(%r31)
/*
* Now that we are done with the trapframe, we can load the original SP
*/
mfsprg1 %r1
mtmsr %r30 /* disable translation */
isync
mtpid %r31
mfsprg3 %r31
mfsprg2 %r30
IBM405_ERRATA77_SYNC
rfi
ba . /* Protect against prefetch */
.globl _C_LABEL(sctrap),_C_LABEL(scsize),_C_LABEL(sctrapexit)
_C_LABEL(sctrap):
STANDARD_PROLOG(CI_TEMPSAVE)
bla s_sctrap
_C_LABEL(scsize) = .-_C_LABEL(sctrap)
s_sctrap:
FRAME_SETUP(CI_TEMPSAVE)
/* Now we can recover interrupts again: */
wrteei 1 /* Enable interrupts */
/* Call the appropriate syscall handler: */
addi %r3,%r1,FRAME_TF
lwz %r4,L_PROC(%r13)
lwz %r4,P_MD_SYSCALL(%r4)
mtctr %r4
bctrl
_C_LABEL(sctrapexit):
b trapexit
/*
* External interrupt second level handler
*/
#define INTR_SAVE(tempsave) \
/* Save non-volatile registers: */ \
stwu %r1,-FRAMELEN(%r1); /* temporarily */ \
stw %r0,FRAME_R0(%r1); \
mfsprg1 %r0; /* get original SP */ \
stw %r0,FRAME_R1(%r1); /* and store it */ \
stw %r2,FRAME_R2(%r1); \
stw %r3,FRAME_R3(%r1); \
stw %r4,FRAME_R4(%r1); \
stw %r5,FRAME_R5(%r1); \
stw %r6,FRAME_R6(%r1); \
stw %r7,FRAME_R7(%r1); \
stw %r8,FRAME_R8(%r1); \
stw %r9,FRAME_R9(%r1); \
stw %r10,FRAME_R10(%r1); \
stw %r11,FRAME_R11(%r1); \
stw %r12,FRAME_R12(%r1); \
stw %r13,FRAME_R13(%r1); \
mfctr %r31; \
stmw %r28,FRAME_LR(%r1); /* save LR, CR, XER, CTR */ \
GET_CPUINFO(%r5); \
lmw %r28,(tempsave+CPUSAVE_R28)(%r5); /* restore r28-r31 */ \
lwz %r13,CI_CURLWP(%r5); \
lwz %r5,CI_IDEPTH(%r5); \
mfsrr0 %r4; \
mfsrr1 %r3; \
stw %r5,FRAME_IDEPTH(%r1); \
stw %r4,FRAME_SRR0(%r1); \
stw %r3,FRAME_SRR1(%r1); \
/* interrupts are recoverable here, and enable translation */ \
ENABLE_TRANSLATION(%r0,%r5); \
stw %r0,FRAME_PID(%r1);
.globl _C_LABEL(extint_call)
extintr:
INTR_SAVE(CI_TEMPSAVE)
_C_LABEL(extint_call):
bl _C_LABEL(extint_call) /* to be filled in later */
intr_exit:
/* Disable interrupts */
wrteei 0
isync
lwz %r4,FRAME_SRR1(%r1)
/* Returning to user mode? */
mtcr %r4 /* saved SRR1 */
bf MSR_PR,intrleave_to_kernel /* branch if MSR[PR] is false */
lwz %r4,L_MD_ASTPENDING(%r13)/* Test AST pending */
andi. %r4,%r4,1
beq intrleave_to_user
FRAME_SAVE_CALLEE /* save rest of callee registers */
li %r6,EXC_AST
stw %r6,FRAME_EXC(%r1)
lwz %r31,FRAME_SRR1(%r1) /* move SRR1 to R31 */
b trapagain
/*
* PIT interrupt handler.
*/
.align 5
_C_LABEL(pitint):
INTR_PROLOG(CI_TEMPSAVE)
INTR_SAVE(CI_TEMPSAVE)
addi %r3,%r1,FRAME_CF /* clock frame */
bl _C_LABEL(decr_intr)
b intr_exit
/*
* FIT interrupt handler.
*/
.align 5
_C_LABEL(fitint):
INTR_PROLOG(CI_TEMPSAVE)
INTR_SAVE(CI_TEMPSAVE)
addi %r3,%r1,FRAME_CF /* clock frame */
bl _C_LABEL(stat_intr)
b intr_exit
#if defined(DDB) || defined(KGDB)
/*
* Deliberate entry to ddbtrap
*/
.globl _C_LABEL(ddb_trap)
_C_LABEL(ddb_trap):
mtsprg1 %r1
GET_CPUINFO(%r4)
mfmsr %r3
stw %r3,(CI_DDBSAVE+CPUSAVE_SRR1)(%r4)
wrteei 0 /* disable interrupts */
isync
stmw %r28,CI_DDBSAVE(%r4)
mflr %r28
stw %r28,(CI_DDBSAVE+CPUSAVE_SRR0)(%r4)
li %r29,EXC_BPT
mtlr %r29
mfcr %r29
/*
* Now the ddb/kgdb trap catching code.
*/
ddbtrap:
FRAME_SETUP(CI_DDBSAVE)
/* Call C trap code: */
addi %r3,%r1,FRAME_TF
bl _C_LABEL(ddb_trap_glue)
or. %r3,%r3,%r3
beq trapagain
b trapexit
#endif /* DDB || KGDB */