1555 lines · 42.5 KB · Assembly
RawDownload
/* SPDX-License-Identifier: GPL-2.0-or-later *//* *  Boot code and exception vectors for Book3E processors * *  Copyright (C) 2007 Ben. Herrenschmidt (benh@kernel.crashing.org), IBM Corp. */ #include <linux/linkage.h>#include <linux/threads.h>#include <asm/reg.h>#include <asm/page.h>#include <asm/ppc_asm.h>#include <asm/asm-offsets.h>#include <asm/cputable.h>#include <asm/setup.h>#include <asm/thread_info.h>#include <asm/exception-64e.h>#include <asm/bug.h>#include <asm/irqflags.h>#include <asm/ptrace.h>#include <asm/ppc-opcode.h>#include <asm/mmu.h>#include <asm/hw_irq.h>#include <asm/kvm_asm.h>#include <asm/kvm_booke_hv_asm.h>#include <asm/feature-fixups.h>#include <asm/context_tracking.h> /* 64e interrupt returns always use SRR registers */#define fast_interrupt_return fast_interrupt_return_srr#define interrupt_return interrupt_return_srr /* XXX This will ultimately add space for a special exception save *     structure used to save things like SRR0/SRR1, SPRGs, MAS, etc... *     when taking special interrupts. For now we don't support that, *     special interrupts from within a non-standard level will probably *     blow you up */#define SPECIAL_EXC_SRR0	0#define SPECIAL_EXC_SRR1	1#define SPECIAL_EXC_SPRG_GEN	2#define SPECIAL_EXC_SPRG_TLB	3#define SPECIAL_EXC_MAS0	4#define SPECIAL_EXC_MAS1	5#define SPECIAL_EXC_MAS2	6#define SPECIAL_EXC_MAS3	7#define SPECIAL_EXC_MAS6	8#define SPECIAL_EXC_MAS7	9#define SPECIAL_EXC_MAS5	10	/* E.HV only */#define SPECIAL_EXC_MAS8	11	/* E.HV only */#define SPECIAL_EXC_IRQHAPPENED	12#define SPECIAL_EXC_DEAR	13#define SPECIAL_EXC_ESR		14#define SPECIAL_EXC_SOFTE	15#define SPECIAL_EXC_CSRR0	16#define SPECIAL_EXC_CSRR1	17/* must be even to keep 16-byte stack alignment */#define SPECIAL_EXC_END		18 #define SPECIAL_EXC_FRAME_SIZE	(INT_FRAME_SIZE + SPECIAL_EXC_END * 8)#define SPECIAL_EXC_FRAME_OFFS  (INT_FRAME_SIZE - 288) #define SPECIAL_EXC_STORE(reg, name) \	std	reg, (SPECIAL_EXC_##name * 8 + SPECIAL_EXC_FRAME_OFFS)(r1) #define SPECIAL_EXC_LOAD(reg, name) \	ld	reg, (SPECIAL_EXC_##name * 8 + SPECIAL_EXC_FRAME_OFFS)(r1) SYM_CODE_START_LOCAL(special_reg_save)	/*	 * We only need (or have stack space) to save this stuff if	 * we interrupted the kernel.	 */	ld	r3,_MSR(r1)	andi.	r3,r3,MSR_PR	bnelr 	/*	 * Advance to the next TLB exception frame for handler	 * types that don't do it automatically.	 */	LOAD_REG_ADDR(r11,extlb_level_exc)	lwz	r12,0(r11)	mfspr	r10,SPRN_SPRG_TLB_EXFRAME	add	r10,r10,r12	mtspr	SPRN_SPRG_TLB_EXFRAME,r10 	/*	 * Save registers needed to allow nesting of certain exceptions	 * (such as TLB misses) inside special exception levels	 */	mfspr	r10,SPRN_SRR0	SPECIAL_EXC_STORE(r10,SRR0)	mfspr	r10,SPRN_SRR1	SPECIAL_EXC_STORE(r10,SRR1)	mfspr	r10,SPRN_SPRG_GEN_SCRATCH	SPECIAL_EXC_STORE(r10,SPRG_GEN)	mfspr	r10,SPRN_SPRG_TLB_SCRATCH	SPECIAL_EXC_STORE(r10,SPRG_TLB)	mfspr	r10,SPRN_MAS0	SPECIAL_EXC_STORE(r10,MAS0)	mfspr	r10,SPRN_MAS1	SPECIAL_EXC_STORE(r10,MAS1)	mfspr	r10,SPRN_MAS2	SPECIAL_EXC_STORE(r10,MAS2)	mfspr	r10,SPRN_MAS3	SPECIAL_EXC_STORE(r10,MAS3)	mfspr	r10,SPRN_MAS6	SPECIAL_EXC_STORE(r10,MAS6)	mfspr	r10,SPRN_MAS7	SPECIAL_EXC_STORE(r10,MAS7)BEGIN_FTR_SECTION	mfspr	r10,SPRN_MAS5	SPECIAL_EXC_STORE(r10,MAS5)	mfspr	r10,SPRN_MAS8	SPECIAL_EXC_STORE(r10,MAS8) 	/* MAS5/8 could have inappropriate values if we interrupted KVM code */	li	r10,0	mtspr	SPRN_MAS5,r10	mtspr	SPRN_MAS8,r10END_FTR_SECTION_IFSET(CPU_FTR_EMB_HV)	mfspr	r10,SPRN_DEAR	SPECIAL_EXC_STORE(r10,DEAR)	mfspr	r10,SPRN_ESR	SPECIAL_EXC_STORE(r10,ESR) 	ld	r10,_NIP(r1)	SPECIAL_EXC_STORE(r10,CSRR0)	ld	r10,_MSR(r1)	SPECIAL_EXC_STORE(r10,CSRR1) 	blrSYM_CODE_END(special_reg_save) SYM_CODE_START_LOCAL(ret_from_level_except)	ld	r3,_MSR(r1)	andi.	r3,r3,MSR_PR	beq	1f	REST_NVGPRS(r1)	b	interrupt_return1: 	LOAD_REG_ADDR(r11,extlb_level_exc)	lwz	r12,0(r11)	mfspr	r10,SPRN_SPRG_TLB_EXFRAME	sub	r10,r10,r12	mtspr	SPRN_SPRG_TLB_EXFRAME,r10 	/*	 * It's possible that the special level exception interrupted a	 * TLB miss handler, and inserted the same entry that the	 * interrupted handler was about to insert.  On CPUs without TLB	 * write conditional, this can result in a duplicate TLB entry.	 * Wipe all non-bolted entries to be safe.	 *	 * Note that this doesn't protect against any TLB misses	 * we may take accessing the stack from here to the end of	 * the special level exception.  It's not clear how we can	 * reasonably protect against that, but only CPUs with	 * neither TLB write conditional nor bolted kernel memory	 * are affected.  Do any such CPUs even exist?	 */	PPC_TLBILX_ALL(0,R0) 	REST_NVGPRS(r1) 	SPECIAL_EXC_LOAD(r10,SRR0)	mtspr	SPRN_SRR0,r10	SPECIAL_EXC_LOAD(r10,SRR1)	mtspr	SPRN_SRR1,r10	SPECIAL_EXC_LOAD(r10,SPRG_GEN)	mtspr	SPRN_SPRG_GEN_SCRATCH,r10	SPECIAL_EXC_LOAD(r10,SPRG_TLB)	mtspr	SPRN_SPRG_TLB_SCRATCH,r10	SPECIAL_EXC_LOAD(r10,MAS0)	mtspr	SPRN_MAS0,r10	SPECIAL_EXC_LOAD(r10,MAS1)	mtspr	SPRN_MAS1,r10	SPECIAL_EXC_LOAD(r10,MAS2)	mtspr	SPRN_MAS2,r10	SPECIAL_EXC_LOAD(r10,MAS3)	mtspr	SPRN_MAS3,r10	SPECIAL_EXC_LOAD(r10,MAS6)	mtspr	SPRN_MAS6,r10	SPECIAL_EXC_LOAD(r10,MAS7)	mtspr	SPRN_MAS7,r10BEGIN_FTR_SECTION	SPECIAL_EXC_LOAD(r10,MAS5)	mtspr	SPRN_MAS5,r10	SPECIAL_EXC_LOAD(r10,MAS8)	mtspr	SPRN_MAS8,r10END_FTR_SECTION_IFSET(CPU_FTR_EMB_HV) 	SPECIAL_EXC_LOAD(r10,DEAR)	mtspr	SPRN_DEAR,r10	SPECIAL_EXC_LOAD(r10,ESR)	mtspr	SPRN_ESR,r10 	stdcx.	r0,0,r1		/* to clear the reservation */ 	REST_GPRS(2, 9, r1) 	ld	r10,_CTR(r1)	ld	r11,_XER(r1)	mtctr	r10	mtxer	r11 	blrSYM_CODE_END(ret_from_level_except) .macro ret_from_level srr0 srr1 paca_ex scratch	bl	ret_from_level_except 	ld	r10,_LINK(r1)	ld	r11,_CCR(r1)	ld	r0,GPR13(r1)	mtlr	r10	mtcr	r11 	REST_GPRS(10, 12, r1)	mtspr	\scratch,r0 	std	r10,\paca_ex+EX_R10(r13);	std	r11,\paca_ex+EX_R11(r13);	ld	r10,_NIP(r1)	ld	r11,_MSR(r1)	REST_GPR(0, r1)	REST_GPR(1, r1)	mtspr	\srr0,r10	mtspr	\srr1,r11	ld	r10,\paca_ex+EX_R10(r13)	ld	r11,\paca_ex+EX_R11(r13)	mfspr	r13,\scratch.endm SYM_CODE_START_LOCAL(ret_from_crit_except)	ret_from_level SPRN_CSRR0 SPRN_CSRR1 PACA_EXCRIT SPRN_SPRG_CRIT_SCRATCH	rfciSYM_CODE_END(ret_from_crit_except) SYM_CODE_START_LOCAL(ret_from_mc_except)	ret_from_level SPRN_MCSRR0 SPRN_MCSRR1 PACA_EXMC SPRN_SPRG_MC_SCRATCH	rfmciSYM_CODE_END(ret_from_mc_except) /* Exception prolog code for all exceptions */#define EXCEPTION_PROLOG(n, intnum, type, addition)	    		    \	mtspr	SPRN_SPRG_##type##_SCRATCH,r13;	/* get spare registers */   \	mfspr	r13,SPRN_SPRG_PACA;	/* get PACA */			    \	std	r10,PACA_EX##type+EX_R10(r13);				    \	std	r11,PACA_EX##type+EX_R11(r13);				    \	mfcr	r10;			/* save CR */			    \	mfspr	r11,SPRN_##type##_SRR1;/* what are we coming from */	    \	DO_KVM	intnum,SPRN_##type##_SRR1;    /* KVM hook */		    \	stw	r10,PACA_EX##type+EX_CR(r13); /* save old CR in the PACA */ \	addition;			/* additional code for that exc. */ \	std	r1,PACA_EX##type+EX_R1(r13); /* save old r1 in the PACA */  \	type##_SET_KSTACK;		/* get special stack if necessary */\	andi.	r10,r11,MSR_PR;		/* save stack pointer */	    \	beq	1f;			/* branch around if supervisor */   \	ld	r1,PACAKSAVE(r13);	/* get kernel stack coming from usr */\1:	type##_BTB_FLUSH		\	cmpdi	cr1,r1,0;		/* check if SP makes sense */	    \	bge-	cr1,exc_##n##_bad_stack;/* bad stack (TODO: out of line) */ \	mfspr	r10,SPRN_##type##_SRR0;	/* read SRR0 before touching stack */ /* Exception type-specific macros */#define	GEN_SET_KSTACK							    \	subi	r1,r1,INT_FRAME_SIZE;	/* alloc frame on kernel stack */#define SPRN_GEN_SRR0	SPRN_SRR0#define SPRN_GEN_SRR1	SPRN_SRR1 #define	GDBELL_SET_KSTACK	GEN_SET_KSTACK#define SPRN_GDBELL_SRR0	SPRN_GSRR0#define SPRN_GDBELL_SRR1	SPRN_GSRR1 #define CRIT_SET_KSTACK						            \	ld	r1,PACA_CRIT_STACK(r13);				    \	subi	r1,r1,SPECIAL_EXC_FRAME_SIZE#define SPRN_CRIT_SRR0	SPRN_CSRR0#define SPRN_CRIT_SRR1	SPRN_CSRR1 #define DBG_SET_KSTACK						            \	ld	r1,PACA_DBG_STACK(r13);					    \	subi	r1,r1,SPECIAL_EXC_FRAME_SIZE#define SPRN_DBG_SRR0	SPRN_DSRR0#define SPRN_DBG_SRR1	SPRN_DSRR1 #define MC_SET_KSTACK						            \	ld	r1,PACA_MC_STACK(r13);					    \	subi	r1,r1,SPECIAL_EXC_FRAME_SIZE#define SPRN_MC_SRR0	SPRN_MCSRR0#define SPRN_MC_SRR1	SPRN_MCSRR1 #define GEN_BTB_FLUSH			\	START_BTB_FLUSH_SECTION		\		beq 1f;			\		BTB_FLUSH(r10)			\		1:		\	END_BTB_FLUSH_SECTION #define CRIT_BTB_FLUSH			\	START_BTB_FLUSH_SECTION		\		BTB_FLUSH(r10)		\	END_BTB_FLUSH_SECTION #define DBG_BTB_FLUSH CRIT_BTB_FLUSH#define MC_BTB_FLUSH CRIT_BTB_FLUSH#define GDBELL_BTB_FLUSH GEN_BTB_FLUSH #define NORMAL_EXCEPTION_PROLOG(n, intnum, addition)			    \	EXCEPTION_PROLOG(n, intnum, GEN, addition##_GEN(n)) #define CRIT_EXCEPTION_PROLOG(n, intnum, addition)			    \	EXCEPTION_PROLOG(n, intnum, CRIT, addition##_CRIT(n)) #define DBG_EXCEPTION_PROLOG(n, intnum, addition)			    \	EXCEPTION_PROLOG(n, intnum, DBG, addition##_DBG(n)) #define MC_EXCEPTION_PROLOG(n, intnum, addition)			    \	EXCEPTION_PROLOG(n, intnum, MC, addition##_MC(n)) #define GDBELL_EXCEPTION_PROLOG(n, intnum, addition)			    \	EXCEPTION_PROLOG(n, intnum, GDBELL, addition##_GDBELL(n)) /* Variants of the "addition" argument for the prolog */#define PROLOG_ADDITION_NONE_GEN(n)#define PROLOG_ADDITION_NONE_GDBELL(n)#define PROLOG_ADDITION_NONE_CRIT(n)#define PROLOG_ADDITION_NONE_DBG(n)#define PROLOG_ADDITION_NONE_MC(n) #define PROLOG_ADDITION_MASKABLE_GEN(n)					    \	lbz	r10,PACAIRQSOFTMASK(r13);	/* are irqs soft-masked? */ \	andi.	r10,r10,IRQS_DISABLED;	/* yes -> go out of line */ \	bne	masked_interrupt_book3e_##n /* * Additional regs must be re-loaded from paca before EXCEPTION_COMMON* is * called, because that does SAVE_NVGPRS which must see the original register * values, otherwise the scratch values might be restored when exiting the * interrupt. */#define PROLOG_ADDITION_2REGS_GEN(n)					    \	std	r14,PACA_EXGEN+EX_R14(r13);				    \	std	r15,PACA_EXGEN+EX_R15(r13) #define PROLOG_ADDITION_1REG_GEN(n)					    \	std	r14,PACA_EXGEN+EX_R14(r13); #define PROLOG_ADDITION_2REGS_CRIT(n)					    \	std	r14,PACA_EXCRIT+EX_R14(r13);				    \	std	r15,PACA_EXCRIT+EX_R15(r13) #define PROLOG_ADDITION_2REGS_DBG(n)					    \	std	r14,PACA_EXDBG+EX_R14(r13);				    \	std	r15,PACA_EXDBG+EX_R15(r13) #define PROLOG_ADDITION_2REGS_MC(n)					    \	std	r14,PACA_EXMC+EX_R14(r13);				    \	std	r15,PACA_EXMC+EX_R15(r13) /* Core exception code for all exceptions except TLB misses. */#define EXCEPTION_COMMON_LVL(n, scratch, excf)				    \exc_##n##_common:							    \	SAVE_GPR(0, r1);		/* save r0 in stackframe */	    \	SAVE_GPRS(2, 9, r1);		/* save r2 - r9 in stackframe */    \	std	r10,_NIP(r1);		/* save SRR0 to stackframe */	    \	std	r11,_MSR(r1);		/* save SRR1 to stackframe */	    \	beq	2f;			/* if from kernel mode */	    \2:	ld	r3,excf+EX_R10(r13);	/* get back r10 */		    \	ld	r4,excf+EX_R11(r13);	/* get back r11 */		    \	mfspr	r5,scratch;		/* get back r13 */		    \	SAVE_GPR(12, r1);		/* save r12 in stackframe */	    \	LOAD_PACA_TOC();		/* get kernel TOC into r2 */	    \	mflr	r6;			/* save LR in stackframe */	    \	mfctr	r7;			/* save CTR in stackframe */	    \	mfspr	r8,SPRN_XER;		/* save XER in stackframe */	    \	ld	r9,excf+EX_R1(r13);	/* load orig r1 back from PACA */   \	lwz	r10,excf+EX_CR(r13);	/* load orig CR back from PACA	*/  \	lbz	r11,PACAIRQSOFTMASK(r13); /* get current IRQ softe */	    \	LOAD_REG_IMMEDIATE(r12, STACK_FRAME_REGS_MARKER);		    \	ZEROIZE_GPR(0);							    \	std	r3,GPR10(r1);		/* save r10 to stackframe */	    \	std	r4,GPR11(r1);		/* save r11 to stackframe */	    \	std	r5,GPR13(r1);		/* save it to stackframe */	    \	std	r6,_LINK(r1);						    \	std	r7,_CTR(r1);						    \	std	r8,_XER(r1);						    \	li	r3,(n);			/* regs.trap vector */		    \	std	r9,0(r1);		/* store stack frame back link */   \	std	r10,_CCR(r1);		/* store orig CR in stackframe */   \	std	r9,GPR1(r1);		/* store stack frame back link */   \	std	r11,SOFTE(r1);		/* and save it to stackframe */     \	std	r12,STACK_INT_FRAME_MARKER(r1); /* mark the frame */	    \	std	r3,_TRAP(r1);		/* set trap number		*/  \	std	r0,RESULT(r1);		/* clear regs->result */	    \	SAVE_NVGPRS(r1);						    \	SANITIZE_NVGPRS();		/* minimise speculation influence */ #define EXCEPTION_COMMON(n) \	EXCEPTION_COMMON_LVL(n, SPRN_SPRG_GEN_SCRATCH, PACA_EXGEN)#define EXCEPTION_COMMON_CRIT(n) \	EXCEPTION_COMMON_LVL(n, SPRN_SPRG_CRIT_SCRATCH, PACA_EXCRIT)#define EXCEPTION_COMMON_MC(n) \	EXCEPTION_COMMON_LVL(n, SPRN_SPRG_MC_SCRATCH, PACA_EXMC)#define EXCEPTION_COMMON_DBG(n) \	EXCEPTION_COMMON_LVL(n, SPRN_SPRG_DBG_SCRATCH, PACA_EXDBG) /* XXX FIXME: Restore r14/r15 when necessary */#define BAD_STACK_TRAMPOLINE(n)						    \exc_##n##_bad_stack:							    \	li	r1,(n);			/* get exception number */	    \	sth	r1,PACA_TRAP_SAVE(r13);	/* store trap */		    \	b	bad_stack_book3e;	/* bad stack error */ /* WARNING: If you change the layout of this stub, make sure you check	*   the debug exception handler which handles single stepping	*   into exceptions from userspace, and the MM code in	*   arch/powerpc/mm/tlb_nohash.c which patches the branch here	*   and would need to be updated if that branch is moved	*/#define	EXCEPTION_STUB(loc, label)					\	. = interrupt_base_book3e + loc;				\	nop;	/* To make debug interrupts happy */			\	b	exc_##label##_book3e; #define ACK_NONE(r)#define ACK_DEC(r)							\	lis	r,TSR_DIS@h;						\	mtspr	SPRN_TSR,r#define ACK_FIT(r)							\	lis	r,TSR_FIS@h;						\	mtspr	SPRN_TSR,r /* Used by asynchronous interrupt that may happen in the idle loop. * * This check if the thread was in the idle loop, and if yes, returns * to the caller rather than the PC. This is to avoid a race if * interrupts happen before the wait instruction. */#define CHECK_NAPPING()							\	ld	r11, PACA_THREAD_INFO(r13);				\	ld	r10,TI_LOCAL_FLAGS(r11);				\	andi.	r9,r10,_TLF_NAPPING;					\	beq+	1f;							\	ld	r8,_LINK(r1);						\	rlwinm	r7,r10,0,~_TLF_NAPPING;					\	std	r8,_NIP(r1);						\	std	r7,TI_LOCAL_FLAGS(r11);					\1:  #define MASKABLE_EXCEPTION(trapnum, intnum, label, hdlr, ack)		\	START_EXCEPTION(label);						\	NORMAL_EXCEPTION_PROLOG(trapnum, intnum, PROLOG_ADDITION_MASKABLE)\	EXCEPTION_COMMON(trapnum)					\	ack(r8);							\	CHECK_NAPPING();						\	addi	r3,r1,STACK_INT_FRAME_REGS;				\	bl	hdlr;							\	b	interrupt_return /* * And here we have the exception vectors ! */ 	.text	.balign	0x1000	.globl interrupt_base_book3einterrupt_base_book3e:					/* fake trap */	EXCEPTION_STUB(0x000, machine_check)	EXCEPTION_STUB(0x020, critical_input)		/* 0x0100 */	EXCEPTION_STUB(0x040, debug_crit)		/* 0x0d00 */	EXCEPTION_STUB(0x060, data_storage)		/* 0x0300 */	EXCEPTION_STUB(0x080, instruction_storage)	/* 0x0400 */	EXCEPTION_STUB(0x0a0, external_input)		/* 0x0500 */	EXCEPTION_STUB(0x0c0, alignment)		/* 0x0600 */	EXCEPTION_STUB(0x0e0, program)			/* 0x0700 */	EXCEPTION_STUB(0x100, fp_unavailable)		/* 0x0800 */	EXCEPTION_STUB(0x120, system_call)		/* 0x0c00 */	EXCEPTION_STUB(0x140, ap_unavailable)		/* 0x0f20 */	EXCEPTION_STUB(0x160, decrementer)		/* 0x0900 */	EXCEPTION_STUB(0x180, fixed_interval)		/* 0x0980 */	EXCEPTION_STUB(0x1a0, watchdog)			/* 0x09f0 */	EXCEPTION_STUB(0x1c0, data_tlb_miss_bolted)	EXCEPTION_STUB(0x1e0, instruction_tlb_miss_bolted)	EXCEPTION_STUB(0x200, altivec_unavailable)	EXCEPTION_STUB(0x220, altivec_assist)	EXCEPTION_STUB(0x260, perfmon)	EXCEPTION_STUB(0x280, doorbell)	EXCEPTION_STUB(0x2a0, doorbell_crit)	EXCEPTION_STUB(0x2c0, guest_doorbell)	EXCEPTION_STUB(0x2e0, guest_doorbell_crit)	EXCEPTION_STUB(0x300, hypercall)	EXCEPTION_STUB(0x320, ehpriv)	EXCEPTION_STUB(0x340, lrat_error) 

Lines 1–500 of 1555.