735 lines
17 KiB
ArmAsm
735 lines
17 KiB
ArmAsm
#include <asm/asm-offsets.h>
|
|
#include <asm/bug.h>
|
|
#ifdef CONFIG_PPC_BOOK3S
|
|
#include <asm/exception-64s.h>
|
|
#else
|
|
#include <asm/exception-64e.h>
|
|
#endif
|
|
#include <asm/feature-fixups.h>
|
|
#include <asm/head-64.h>
|
|
#include <asm/hw_irq.h>
|
|
#include <asm/kup.h>
|
|
#include <asm/mmu.h>
|
|
#include <asm/ppc_asm.h>
|
|
#include <asm/ptrace.h>
|
|
|
|
.align 7
|
|
|
|
.macro DEBUG_SRR_VALID srr
|
|
#ifdef CONFIG_PPC_RFI_SRR_DEBUG
|
|
.ifc \srr,srr
|
|
mfspr r11,SPRN_SRR0
|
|
ld r12,_NIP(r1)
|
|
clrrdi r11,r11,2
|
|
clrrdi r12,r12,2
|
|
100: tdne r11,r12
|
|
EMIT_WARN_ENTRY 100b,__FILE__,__LINE__,(BUGFLAG_WARNING | BUGFLAG_ONCE)
|
|
mfspr r11,SPRN_SRR1
|
|
ld r12,_MSR(r1)
|
|
100: tdne r11,r12
|
|
EMIT_WARN_ENTRY 100b,__FILE__,__LINE__,(BUGFLAG_WARNING | BUGFLAG_ONCE)
|
|
.else
|
|
mfspr r11,SPRN_HSRR0
|
|
ld r12,_NIP(r1)
|
|
clrrdi r11,r11,2
|
|
clrrdi r12,r12,2
|
|
100: tdne r11,r12
|
|
EMIT_WARN_ENTRY 100b,__FILE__,__LINE__,(BUGFLAG_WARNING | BUGFLAG_ONCE)
|
|
mfspr r11,SPRN_HSRR1
|
|
ld r12,_MSR(r1)
|
|
100: tdne r11,r12
|
|
EMIT_WARN_ENTRY 100b,__FILE__,__LINE__,(BUGFLAG_WARNING | BUGFLAG_ONCE)
|
|
.endif
|
|
#endif
|
|
.endm
|
|
|
|
#ifdef CONFIG_PPC_BOOK3S
|
|
.macro system_call_vectored name trapnr
|
|
.globl system_call_vectored_\name
|
|
system_call_vectored_\name:
|
|
_ASM_NOKPROBE_SYMBOL(system_call_vectored_\name)
|
|
SCV_INTERRUPT_TO_KERNEL
|
|
mr r10,r1
|
|
ld r1,PACAKSAVE(r13)
|
|
std r10,0(r1)
|
|
std r11,_NIP(r1)
|
|
std r12,_MSR(r1)
|
|
std r0,GPR0(r1)
|
|
std r10,GPR1(r1)
|
|
std r2,GPR2(r1)
|
|
LOAD_PACA_TOC()
|
|
mfcr r12
|
|
li r11,0
|
|
/* Save syscall parameters in r3-r8 */
|
|
SAVE_GPRS(3, 8, r1)
|
|
/* Zero r9-r12, this should only be required when restoring all GPRs */
|
|
std r11,GPR9(r1)
|
|
std r11,GPR10(r1)
|
|
std r11,GPR11(r1)
|
|
std r11,GPR12(r1)
|
|
std r9,GPR13(r1)
|
|
SAVE_NVGPRS(r1)
|
|
std r11,_XER(r1)
|
|
std r11,_LINK(r1)
|
|
std r11,_CTR(r1)
|
|
|
|
li r11,\trapnr
|
|
std r11,_TRAP(r1)
|
|
std r12,_CCR(r1)
|
|
std r3,ORIG_GPR3(r1)
|
|
/* Calling convention has r3 = regs, r4 = orig r0 */
|
|
addi r3,r1,STACK_FRAME_OVERHEAD
|
|
mr r4,r0
|
|
LOAD_REG_IMMEDIATE(r11, STACK_FRAME_REGS_MARKER)
|
|
std r11,-16(r3) /* "regshere" marker */
|
|
|
|
BEGIN_FTR_SECTION
|
|
HMT_MEDIUM
|
|
END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
|
|
|
|
/*
|
|
* scv enters with MSR[EE]=1 and is immediately considered soft-masked.
|
|
* The entry vector already sets PACAIRQSOFTMASK to IRQS_ALL_DISABLED,
|
|
* and interrupts may be masked and pending already.
|
|
* system_call_exception() will call trace_hardirqs_off() which means
|
|
* interrupts could already have been blocked before trace_hardirqs_off,
|
|
* but this is the best we can do.
|
|
*/
|
|
|
|
bl system_call_exception
|
|
|
|
.Lsyscall_vectored_\name\()_exit:
|
|
addi r4,r1,STACK_FRAME_OVERHEAD
|
|
li r5,1 /* scv */
|
|
bl syscall_exit_prepare
|
|
std r1,PACA_EXIT_SAVE_R1(r13) /* save r1 for restart */
|
|
.Lsyscall_vectored_\name\()_rst_start:
|
|
lbz r11,PACAIRQHAPPENED(r13)
|
|
andi. r11,r11,(~PACA_IRQ_HARD_DIS)@l
|
|
bne- syscall_vectored_\name\()_restart
|
|
li r11,IRQS_ENABLED
|
|
stb r11,PACAIRQSOFTMASK(r13)
|
|
li r11,0
|
|
stb r11,PACAIRQHAPPENED(r13) # clear out possible HARD_DIS
|
|
|
|
ld r2,_CCR(r1)
|
|
ld r4,_NIP(r1)
|
|
ld r5,_MSR(r1)
|
|
|
|
BEGIN_FTR_SECTION
|
|
stdcx. r0,0,r1 /* to clear the reservation */
|
|
END_FTR_SECTION_IFCLR(CPU_FTR_STCX_CHECKS_ADDRESS)
|
|
|
|
BEGIN_FTR_SECTION
|
|
HMT_MEDIUM_LOW
|
|
END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
|
|
|
|
cmpdi r3,0
|
|
bne .Lsyscall_vectored_\name\()_restore_regs
|
|
|
|
/* rfscv returns with LR->NIA and CTR->MSR */
|
|
mtlr r4
|
|
mtctr r5
|
|
|
|
/* Could zero these as per ABI, but we may consider a stricter ABI
|
|
* which preserves these if libc implementations can benefit, so
|
|
* restore them for now until further measurement is done. */
|
|
REST_GPR(0, r1)
|
|
REST_GPRS(4, 8, r1)
|
|
/* Zero volatile regs that may contain sensitive kernel data */
|
|
ZEROIZE_GPRS(9, 12)
|
|
mtspr SPRN_XER,r0
|
|
|
|
/*
|
|
* We don't need to restore AMR on the way back to userspace for KUAP.
|
|
* The value of AMR only matters while we're in the kernel.
|
|
*/
|
|
mtcr r2
|
|
REST_GPRS(2, 3, r1)
|
|
REST_GPR(13, r1)
|
|
REST_GPR(1, r1)
|
|
RFSCV_TO_USER
|
|
b . /* prevent speculative execution */
|
|
|
|
.Lsyscall_vectored_\name\()_restore_regs:
|
|
mtspr SPRN_SRR0,r4
|
|
mtspr SPRN_SRR1,r5
|
|
|
|
ld r3,_CTR(r1)
|
|
ld r4,_LINK(r1)
|
|
ld r5,_XER(r1)
|
|
|
|
REST_NVGPRS(r1)
|
|
REST_GPR(0, r1)
|
|
mtcr r2
|
|
mtctr r3
|
|
mtlr r4
|
|
mtspr SPRN_XER,r5
|
|
REST_GPRS(2, 13, r1)
|
|
REST_GPR(1, r1)
|
|
RFI_TO_USER
|
|
.Lsyscall_vectored_\name\()_rst_end:
|
|
|
|
syscall_vectored_\name\()_restart:
|
|
_ASM_NOKPROBE_SYMBOL(syscall_vectored_\name\()_restart)
|
|
GET_PACA(r13)
|
|
ld r1,PACA_EXIT_SAVE_R1(r13)
|
|
LOAD_PACA_TOC()
|
|
ld r3,RESULT(r1)
|
|
addi r4,r1,STACK_FRAME_OVERHEAD
|
|
li r11,IRQS_ALL_DISABLED
|
|
stb r11,PACAIRQSOFTMASK(r13)
|
|
bl syscall_exit_restart
|
|
std r1,PACA_EXIT_SAVE_R1(r13) /* save r1 for restart */
|
|
b .Lsyscall_vectored_\name\()_rst_start
|
|
1:
|
|
|
|
SOFT_MASK_TABLE(.Lsyscall_vectored_\name\()_rst_start, 1b)
|
|
RESTART_TABLE(.Lsyscall_vectored_\name\()_rst_start, .Lsyscall_vectored_\name\()_rst_end, syscall_vectored_\name\()_restart)
|
|
|
|
.endm
|
|
|
|
system_call_vectored common 0x3000
|
|
|
|
/*
|
|
* We instantiate another entry copy for the SIGILL variant, with TRAP=0x7ff0
|
|
* which is tested by system_call_exception when r0 is -1 (as set by vector
|
|
* entry code).
|
|
*/
|
|
system_call_vectored sigill 0x7ff0
|
|
|
|
#endif /* CONFIG_PPC_BOOK3S */
|
|
|
|
.balign IFETCH_ALIGN_BYTES
|
|
.globl system_call_common_real
|
|
system_call_common_real:
|
|
_ASM_NOKPROBE_SYMBOL(system_call_common_real)
|
|
ld r10,PACAKMSR(r13) /* get MSR value for kernel */
|
|
mtmsrd r10
|
|
|
|
.balign IFETCH_ALIGN_BYTES
|
|
.globl system_call_common
|
|
system_call_common:
|
|
_ASM_NOKPROBE_SYMBOL(system_call_common)
|
|
mr r10,r1
|
|
ld r1,PACAKSAVE(r13)
|
|
std r10,0(r1)
|
|
std r11,_NIP(r1)
|
|
std r12,_MSR(r1)
|
|
std r0,GPR0(r1)
|
|
std r10,GPR1(r1)
|
|
std r2,GPR2(r1)
|
|
#ifdef CONFIG_PPC_E500
|
|
START_BTB_FLUSH_SECTION
|
|
BTB_FLUSH(r10)
|
|
END_BTB_FLUSH_SECTION
|
|
#endif
|
|
LOAD_PACA_TOC()
|
|
mfcr r12
|
|
li r11,0
|
|
/* Save syscall parameters in r3-r8 */
|
|
SAVE_GPRS(3, 8, r1)
|
|
/* Zero r9-r12, this should only be required when restoring all GPRs */
|
|
std r11,GPR9(r1)
|
|
std r11,GPR10(r1)
|
|
std r11,GPR11(r1)
|
|
std r11,GPR12(r1)
|
|
std r9,GPR13(r1)
|
|
SAVE_NVGPRS(r1)
|
|
std r11,_XER(r1)
|
|
std r11,_CTR(r1)
|
|
mflr r10
|
|
|
|
/*
|
|
* This clears CR0.SO (bit 28), which is the error indication on
|
|
* return from this system call.
|
|
*/
|
|
rldimi r12,r11,28,(63-28)
|
|
li r11,0xc00
|
|
std r10,_LINK(r1)
|
|
std r11,_TRAP(r1)
|
|
std r12,_CCR(r1)
|
|
std r3,ORIG_GPR3(r1)
|
|
/* Calling convention has r3 = regs, r4 = orig r0 */
|
|
addi r3,r1,STACK_FRAME_OVERHEAD
|
|
mr r4,r0
|
|
LOAD_REG_IMMEDIATE(r11, STACK_FRAME_REGS_MARKER)
|
|
std r11,-16(r3) /* "regshere" marker */
|
|
|
|
#ifdef CONFIG_PPC_BOOK3S
|
|
li r11,1
|
|
stb r11,PACASRR_VALID(r13)
|
|
#endif
|
|
|
|
/*
|
|
* We always enter kernel from userspace with irq soft-mask enabled and
|
|
* nothing pending. system_call_exception() will call
|
|
* trace_hardirqs_off().
|
|
*/
|
|
li r11,IRQS_ALL_DISABLED
|
|
stb r11,PACAIRQSOFTMASK(r13)
|
|
#ifdef CONFIG_PPC_BOOK3S
|
|
li r12,-1 /* Set MSR_EE and MSR_RI */
|
|
mtmsrd r12,1
|
|
#else
|
|
wrteei 1
|
|
#endif
|
|
|
|
bl system_call_exception
|
|
|
|
.Lsyscall_exit:
|
|
addi r4,r1,STACK_FRAME_OVERHEAD
|
|
li r5,0 /* !scv */
|
|
bl syscall_exit_prepare
|
|
std r1,PACA_EXIT_SAVE_R1(r13) /* save r1 for restart */
|
|
#ifdef CONFIG_PPC_BOOK3S
|
|
.Lsyscall_rst_start:
|
|
lbz r11,PACAIRQHAPPENED(r13)
|
|
andi. r11,r11,(~PACA_IRQ_HARD_DIS)@l
|
|
bne- syscall_restart
|
|
#endif
|
|
li r11,IRQS_ENABLED
|
|
stb r11,PACAIRQSOFTMASK(r13)
|
|
li r11,0
|
|
stb r11,PACAIRQHAPPENED(r13) # clear out possible HARD_DIS
|
|
|
|
ld r2,_CCR(r1)
|
|
ld r6,_LINK(r1)
|
|
mtlr r6
|
|
|
|
#ifdef CONFIG_PPC_BOOK3S
|
|
lbz r4,PACASRR_VALID(r13)
|
|
cmpdi r4,0
|
|
bne 1f
|
|
li r4,0
|
|
stb r4,PACASRR_VALID(r13)
|
|
#endif
|
|
ld r4,_NIP(r1)
|
|
ld r5,_MSR(r1)
|
|
mtspr SPRN_SRR0,r4
|
|
mtspr SPRN_SRR1,r5
|
|
1:
|
|
DEBUG_SRR_VALID srr
|
|
|
|
BEGIN_FTR_SECTION
|
|
stdcx. r0,0,r1 /* to clear the reservation */
|
|
END_FTR_SECTION_IFCLR(CPU_FTR_STCX_CHECKS_ADDRESS)
|
|
|
|
cmpdi r3,0
|
|
bne .Lsyscall_restore_regs
|
|
/* Zero volatile regs that may contain sensitive kernel data */
|
|
ZEROIZE_GPR(0)
|
|
ZEROIZE_GPRS(4, 12)
|
|
mtctr r0
|
|
mtspr SPRN_XER,r0
|
|
.Lsyscall_restore_regs_cont:
|
|
|
|
BEGIN_FTR_SECTION
|
|
HMT_MEDIUM_LOW
|
|
END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
|
|
|
|
/*
|
|
* We don't need to restore AMR on the way back to userspace for KUAP.
|
|
* The value of AMR only matters while we're in the kernel.
|
|
*/
|
|
mtcr r2
|
|
REST_GPRS(2, 3, r1)
|
|
REST_GPR(13, r1)
|
|
REST_GPR(1, r1)
|
|
RFI_TO_USER
|
|
b . /* prevent speculative execution */
|
|
|
|
.Lsyscall_restore_regs:
|
|
ld r3,_CTR(r1)
|
|
ld r4,_XER(r1)
|
|
REST_NVGPRS(r1)
|
|
mtctr r3
|
|
mtspr SPRN_XER,r4
|
|
REST_GPR(0, r1)
|
|
REST_GPRS(4, 12, r1)
|
|
b .Lsyscall_restore_regs_cont
|
|
.Lsyscall_rst_end:
|
|
|
|
#ifdef CONFIG_PPC_BOOK3S
|
|
syscall_restart:
|
|
_ASM_NOKPROBE_SYMBOL(syscall_restart)
|
|
GET_PACA(r13)
|
|
ld r1,PACA_EXIT_SAVE_R1(r13)
|
|
LOAD_PACA_TOC()
|
|
ld r3,RESULT(r1)
|
|
addi r4,r1,STACK_FRAME_OVERHEAD
|
|
li r11,IRQS_ALL_DISABLED
|
|
stb r11,PACAIRQSOFTMASK(r13)
|
|
bl syscall_exit_restart
|
|
std r1,PACA_EXIT_SAVE_R1(r13) /* save r1 for restart */
|
|
b .Lsyscall_rst_start
|
|
1:
|
|
|
|
SOFT_MASK_TABLE(.Lsyscall_rst_start, 1b)
|
|
RESTART_TABLE(.Lsyscall_rst_start, .Lsyscall_rst_end, syscall_restart)
|
|
#endif
|
|
|
|
/*
|
|
* If MSR EE/RI was never enabled, IRQs not reconciled, NVGPRs not
|
|
* touched, no exit work created, then this can be used.
|
|
*/
|
|
.balign IFETCH_ALIGN_BYTES
|
|
.globl fast_interrupt_return_srr
|
|
fast_interrupt_return_srr:
|
|
_ASM_NOKPROBE_SYMBOL(fast_interrupt_return_srr)
|
|
kuap_check_amr r3, r4
|
|
ld r5,_MSR(r1)
|
|
andi. r0,r5,MSR_PR
|
|
#ifdef CONFIG_PPC_BOOK3S
|
|
beq 1f
|
|
kuap_user_restore r3, r4
|
|
b .Lfast_user_interrupt_return_srr
|
|
1: kuap_kernel_restore r3, r4
|
|
andi. r0,r5,MSR_RI
|
|
li r3,0 /* 0 return value, no EMULATE_STACK_STORE */
|
|
bne+ .Lfast_kernel_interrupt_return_srr
|
|
addi r3,r1,STACK_FRAME_OVERHEAD
|
|
bl unrecoverable_exception
|
|
b . /* should not get here */
|
|
#else
|
|
bne .Lfast_user_interrupt_return_srr
|
|
b .Lfast_kernel_interrupt_return_srr
|
|
#endif
|
|
|
|
.macro interrupt_return_macro srr
|
|
.balign IFETCH_ALIGN_BYTES
|
|
.globl interrupt_return_\srr
|
|
interrupt_return_\srr\():
|
|
_ASM_NOKPROBE_SYMBOL(interrupt_return_\srr\())
|
|
ld r4,_MSR(r1)
|
|
andi. r0,r4,MSR_PR
|
|
beq interrupt_return_\srr\()_kernel
|
|
interrupt_return_\srr\()_user: /* make backtraces match the _kernel variant */
|
|
_ASM_NOKPROBE_SYMBOL(interrupt_return_\srr\()_user)
|
|
addi r3,r1,STACK_FRAME_OVERHEAD
|
|
bl interrupt_exit_user_prepare
|
|
cmpdi r3,0
|
|
bne- .Lrestore_nvgprs_\srr
|
|
.Lrestore_nvgprs_\srr\()_cont:
|
|
std r1,PACA_EXIT_SAVE_R1(r13) /* save r1 for restart */
|
|
#ifdef CONFIG_PPC_BOOK3S
|
|
.Linterrupt_return_\srr\()_user_rst_start:
|
|
lbz r11,PACAIRQHAPPENED(r13)
|
|
andi. r11,r11,(~PACA_IRQ_HARD_DIS)@l
|
|
bne- interrupt_return_\srr\()_user_restart
|
|
#endif
|
|
li r11,IRQS_ENABLED
|
|
stb r11,PACAIRQSOFTMASK(r13)
|
|
li r11,0
|
|
stb r11,PACAIRQHAPPENED(r13) # clear out possible HARD_DIS
|
|
|
|
.Lfast_user_interrupt_return_\srr\():
|
|
#ifdef CONFIG_PPC_BOOK3S
|
|
.ifc \srr,srr
|
|
lbz r4,PACASRR_VALID(r13)
|
|
.else
|
|
lbz r4,PACAHSRR_VALID(r13)
|
|
.endif
|
|
cmpdi r4,0
|
|
li r4,0
|
|
bne 1f
|
|
#endif
|
|
ld r11,_NIP(r1)
|
|
ld r12,_MSR(r1)
|
|
.ifc \srr,srr
|
|
mtspr SPRN_SRR0,r11
|
|
mtspr SPRN_SRR1,r12
|
|
1:
|
|
#ifdef CONFIG_PPC_BOOK3S
|
|
stb r4,PACASRR_VALID(r13)
|
|
#endif
|
|
.else
|
|
mtspr SPRN_HSRR0,r11
|
|
mtspr SPRN_HSRR1,r12
|
|
1:
|
|
#ifdef CONFIG_PPC_BOOK3S
|
|
stb r4,PACAHSRR_VALID(r13)
|
|
#endif
|
|
.endif
|
|
DEBUG_SRR_VALID \srr
|
|
|
|
#ifdef CONFIG_PPC_IRQ_SOFT_MASK_DEBUG
|
|
lbz r4,PACAIRQSOFTMASK(r13)
|
|
tdnei r4,IRQS_ENABLED
|
|
#endif
|
|
|
|
BEGIN_FTR_SECTION
|
|
ld r10,_PPR(r1)
|
|
mtspr SPRN_PPR,r10
|
|
END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
|
|
|
|
BEGIN_FTR_SECTION
|
|
stdcx. r0,0,r1 /* to clear the reservation */
|
|
FTR_SECTION_ELSE
|
|
ldarx r0,0,r1
|
|
ALT_FTR_SECTION_END_IFCLR(CPU_FTR_STCX_CHECKS_ADDRESS)
|
|
|
|
ld r3,_CCR(r1)
|
|
ld r4,_LINK(r1)
|
|
ld r5,_CTR(r1)
|
|
ld r6,_XER(r1)
|
|
li r0,0
|
|
|
|
REST_GPRS(7, 13, r1)
|
|
|
|
mtcr r3
|
|
mtlr r4
|
|
mtctr r5
|
|
mtspr SPRN_XER,r6
|
|
|
|
REST_GPRS(2, 6, r1)
|
|
REST_GPR(0, r1)
|
|
REST_GPR(1, r1)
|
|
.ifc \srr,srr
|
|
RFI_TO_USER
|
|
.else
|
|
HRFI_TO_USER
|
|
.endif
|
|
b . /* prevent speculative execution */
|
|
.Linterrupt_return_\srr\()_user_rst_end:
|
|
|
|
.Lrestore_nvgprs_\srr\():
|
|
REST_NVGPRS(r1)
|
|
b .Lrestore_nvgprs_\srr\()_cont
|
|
|
|
#ifdef CONFIG_PPC_BOOK3S
|
|
interrupt_return_\srr\()_user_restart:
|
|
_ASM_NOKPROBE_SYMBOL(interrupt_return_\srr\()_user_restart)
|
|
GET_PACA(r13)
|
|
ld r1,PACA_EXIT_SAVE_R1(r13)
|
|
LOAD_PACA_TOC()
|
|
addi r3,r1,STACK_FRAME_OVERHEAD
|
|
li r11,IRQS_ALL_DISABLED
|
|
stb r11,PACAIRQSOFTMASK(r13)
|
|
bl interrupt_exit_user_restart
|
|
std r1,PACA_EXIT_SAVE_R1(r13) /* save r1 for restart */
|
|
b .Linterrupt_return_\srr\()_user_rst_start
|
|
1:
|
|
|
|
SOFT_MASK_TABLE(.Linterrupt_return_\srr\()_user_rst_start, 1b)
|
|
RESTART_TABLE(.Linterrupt_return_\srr\()_user_rst_start, .Linterrupt_return_\srr\()_user_rst_end, interrupt_return_\srr\()_user_restart)
|
|
#endif
|
|
|
|
.balign IFETCH_ALIGN_BYTES
|
|
interrupt_return_\srr\()_kernel:
|
|
_ASM_NOKPROBE_SYMBOL(interrupt_return_\srr\()_kernel)
|
|
addi r3,r1,STACK_FRAME_OVERHEAD
|
|
bl interrupt_exit_kernel_prepare
|
|
|
|
std r1,PACA_EXIT_SAVE_R1(r13) /* save r1 for restart */
|
|
.Linterrupt_return_\srr\()_kernel_rst_start:
|
|
ld r11,SOFTE(r1)
|
|
cmpwi r11,IRQS_ENABLED
|
|
stb r11,PACAIRQSOFTMASK(r13)
|
|
beq .Linterrupt_return_\srr\()_soft_enabled
|
|
|
|
/*
|
|
* Returning to soft-disabled context.
|
|
* Check if a MUST_HARD_MASK interrupt has become pending, in which
|
|
* case we need to disable MSR[EE] in the return context.
|
|
*
|
|
* The MSR[EE] check catches among other things the short incoherency
|
|
* in hard_irq_disable() between clearing MSR[EE] and setting
|
|
* PACA_IRQ_HARD_DIS.
|
|
*/
|
|
ld r12,_MSR(r1)
|
|
andi. r10,r12,MSR_EE
|
|
beq .Lfast_kernel_interrupt_return_\srr\() // EE already disabled
|
|
lbz r11,PACAIRQHAPPENED(r13)
|
|
andi. r10,r11,PACA_IRQ_MUST_HARD_MASK
|
|
bne 1f // HARD_MASK is pending
|
|
// No HARD_MASK pending, clear possible HARD_DIS set by interrupt
|
|
andi. r11,r11,(~PACA_IRQ_HARD_DIS)@l
|
|
stb r11,PACAIRQHAPPENED(r13)
|
|
b .Lfast_kernel_interrupt_return_\srr\()
|
|
|
|
|
|
1: /* Must clear MSR_EE from _MSR */
|
|
#ifdef CONFIG_PPC_BOOK3S
|
|
li r10,0
|
|
/* Clear valid before changing _MSR */
|
|
.ifc \srr,srr
|
|
stb r10,PACASRR_VALID(r13)
|
|
.else
|
|
stb r10,PACAHSRR_VALID(r13)
|
|
.endif
|
|
#endif
|
|
xori r12,r12,MSR_EE
|
|
std r12,_MSR(r1)
|
|
b .Lfast_kernel_interrupt_return_\srr\()
|
|
|
|
.Linterrupt_return_\srr\()_soft_enabled:
|
|
/*
|
|
* In the soft-enabled case, need to double-check that we have no
|
|
* pending interrupts that might have come in before we reached the
|
|
* restart section of code, and restart the exit so those can be
|
|
* handled.
|
|
*
|
|
* If there are none, it is be possible that the interrupt still
|
|
* has PACA_IRQ_HARD_DIS set, which needs to be cleared for the
|
|
* interrupted context. This clear will not clobber a new pending
|
|
* interrupt coming in, because we're in the restart section, so
|
|
* such would return to the restart location.
|
|
*/
|
|
#ifdef CONFIG_PPC_BOOK3S
|
|
lbz r11,PACAIRQHAPPENED(r13)
|
|
andi. r11,r11,(~PACA_IRQ_HARD_DIS)@l
|
|
bne- interrupt_return_\srr\()_kernel_restart
|
|
#endif
|
|
li r11,0
|
|
stb r11,PACAIRQHAPPENED(r13) // clear the possible HARD_DIS
|
|
|
|
.Lfast_kernel_interrupt_return_\srr\():
|
|
cmpdi cr1,r3,0
|
|
#ifdef CONFIG_PPC_BOOK3S
|
|
.ifc \srr,srr
|
|
lbz r4,PACASRR_VALID(r13)
|
|
.else
|
|
lbz r4,PACAHSRR_VALID(r13)
|
|
.endif
|
|
cmpdi r4,0
|
|
li r4,0
|
|
bne 1f
|
|
#endif
|
|
ld r11,_NIP(r1)
|
|
ld r12,_MSR(r1)
|
|
.ifc \srr,srr
|
|
mtspr SPRN_SRR0,r11
|
|
mtspr SPRN_SRR1,r12
|
|
1:
|
|
#ifdef CONFIG_PPC_BOOK3S
|
|
stb r4,PACASRR_VALID(r13)
|
|
#endif
|
|
.else
|
|
mtspr SPRN_HSRR0,r11
|
|
mtspr SPRN_HSRR1,r12
|
|
1:
|
|
#ifdef CONFIG_PPC_BOOK3S
|
|
stb r4,PACAHSRR_VALID(r13)
|
|
#endif
|
|
.endif
|
|
DEBUG_SRR_VALID \srr
|
|
|
|
BEGIN_FTR_SECTION
|
|
stdcx. r0,0,r1 /* to clear the reservation */
|
|
FTR_SECTION_ELSE
|
|
ldarx r0,0,r1
|
|
ALT_FTR_SECTION_END_IFCLR(CPU_FTR_STCX_CHECKS_ADDRESS)
|
|
|
|
ld r3,_LINK(r1)
|
|
ld r4,_CTR(r1)
|
|
ld r5,_XER(r1)
|
|
ld r6,_CCR(r1)
|
|
li r0,0
|
|
|
|
REST_GPRS(7, 12, r1)
|
|
|
|
mtlr r3
|
|
mtctr r4
|
|
mtspr SPRN_XER,r5
|
|
|
|
/*
|
|
* Leaving a stale STACK_FRAME_REGS_MARKER on the stack can confuse
|
|
* the reliable stack unwinder later on. Clear it.
|
|
*/
|
|
std r0,STACK_FRAME_OVERHEAD-16(r1)
|
|
|
|
REST_GPRS(2, 5, r1)
|
|
|
|
bne- cr1,1f /* emulate stack store */
|
|
mtcr r6
|
|
REST_GPR(6, r1)
|
|
REST_GPR(0, r1)
|
|
REST_GPR(1, r1)
|
|
.ifc \srr,srr
|
|
RFI_TO_KERNEL
|
|
.else
|
|
HRFI_TO_KERNEL
|
|
.endif
|
|
b . /* prevent speculative execution */
|
|
|
|
1: /*
|
|
* Emulate stack store with update. New r1 value was already calculated
|
|
* and updated in our interrupt regs by emulate_loadstore, but we can't
|
|
* store the previous value of r1 to the stack before re-loading our
|
|
* registers from it, otherwise they could be clobbered. Use
|
|
* PACA_EXGEN as temporary storage to hold the store data, as
|
|
* interrupts are disabled here so it won't be clobbered.
|
|
*/
|
|
mtcr r6
|
|
std r9,PACA_EXGEN+0(r13)
|
|
addi r9,r1,INT_FRAME_SIZE /* get original r1 */
|
|
REST_GPR(6, r1)
|
|
REST_GPR(0, r1)
|
|
REST_GPR(1, r1)
|
|
std r9,0(r1) /* perform store component of stdu */
|
|
ld r9,PACA_EXGEN+0(r13)
|
|
|
|
.ifc \srr,srr
|
|
RFI_TO_KERNEL
|
|
.else
|
|
HRFI_TO_KERNEL
|
|
.endif
|
|
b . /* prevent speculative execution */
|
|
.Linterrupt_return_\srr\()_kernel_rst_end:
|
|
|
|
#ifdef CONFIG_PPC_BOOK3S
|
|
interrupt_return_\srr\()_kernel_restart:
|
|
_ASM_NOKPROBE_SYMBOL(interrupt_return_\srr\()_kernel_restart)
|
|
GET_PACA(r13)
|
|
ld r1,PACA_EXIT_SAVE_R1(r13)
|
|
LOAD_PACA_TOC()
|
|
addi r3,r1,STACK_FRAME_OVERHEAD
|
|
li r11,IRQS_ALL_DISABLED
|
|
stb r11,PACAIRQSOFTMASK(r13)
|
|
bl interrupt_exit_kernel_restart
|
|
std r1,PACA_EXIT_SAVE_R1(r13) /* save r1 for restart */
|
|
b .Linterrupt_return_\srr\()_kernel_rst_start
|
|
1:
|
|
|
|
SOFT_MASK_TABLE(.Linterrupt_return_\srr\()_kernel_rst_start, 1b)
|
|
RESTART_TABLE(.Linterrupt_return_\srr\()_kernel_rst_start, .Linterrupt_return_\srr\()_kernel_rst_end, interrupt_return_\srr\()_kernel_restart)
|
|
#endif
|
|
|
|
.endm
|
|
|
|
interrupt_return_macro srr
|
|
#ifdef CONFIG_PPC_BOOK3S
|
|
interrupt_return_macro hsrr
|
|
|
|
.globl __end_soft_masked
|
|
__end_soft_masked:
|
|
DEFINE_FIXED_SYMBOL(__end_soft_masked, text)
|
|
#endif /* CONFIG_PPC_BOOK3S */
|
|
|
|
#ifdef CONFIG_PPC_BOOK3S
|
|
_GLOBAL(ret_from_fork_scv)
|
|
bl schedule_tail
|
|
REST_NVGPRS(r1)
|
|
li r3,0 /* fork() return value */
|
|
b .Lsyscall_vectored_common_exit
|
|
#endif
|
|
|
|
_GLOBAL(ret_from_fork)
|
|
bl schedule_tail
|
|
REST_NVGPRS(r1)
|
|
li r3,0 /* fork() return value */
|
|
b .Lsyscall_exit
|
|
|
|
_GLOBAL(ret_from_kernel_thread)
|
|
bl schedule_tail
|
|
REST_NVGPRS(r1)
|
|
mtctr r14
|
|
mr r3,r15
|
|
#ifdef CONFIG_PPC64_ELF_ABI_V2
|
|
mr r12,r14
|
|
#endif
|
|
bctrl
|
|
li r3,0
|
|
b .Lsyscall_exit
|