aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel/exceptions-64s.S
diff options
context:
space:
mode:
authorMahesh Salgaonkar <mahesh@linux.vnet.ibm.com>2013-10-30 10:34:08 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2013-12-05 00:02:06 -0500
commit1e9b4507ed98457edb8a892934282b8f63e17246 (patch)
treed74d3849a3e29c4831efe5213464ad2074481191 /arch/powerpc/kernel/exceptions-64s.S
parent729b0f715371ce1e7636b4958fc45d6882442456 (diff)
powerpc/book3s: handle machine check in Linux host.
Move machine check entry point into Linux. So far we were dependent on firmware to decode MCE error details and handover the high level info to OS. This patch introduces early machine check routine that saves the MCE information (srr1, srr0, dar and dsisr) to the emergency stack. We allocate stack frame on emergency stack and set the r1 accordingly. This allows us to be prepared to take another exception without loosing context. One thing to note here that, if we get another machine check while ME bit is off then we risk a checkstop. Hence we restrict ourselves to save only MCE information and register saved on PACA_EXMC save are before we turn the ME bit on. We use paca->in_mce flag to differentiate between first entry and nested machine check entry which helps proper use of emergency stack. We increment paca->in_mce every time we enter in early machine check handler and decrement it while leaving. When we enter machine check early handler first time (paca->in_mce == 0), we are sure nobody is using MC emergency stack and allocate a stack frame at the start of the emergency stack. During subsequent entry (paca->in_mce > 0), we know that r1 points inside emergency stack and we allocate separate stack frame accordingly. This prevents us from clobbering MCE information during nested machine checks. The early machine check handler changes are placed under CPU_FTR_HVMODE section. This makes sure that the early machine check handler will get executed only in hypervisor kernel. This is the code flow: Machine Check Interrupt | V 0x200 vector ME=0, IR=0, DR=0 | V +-----------------------------------------------+ |machine_check_pSeries_early: | ME=0, IR=0, DR=0 | Alloc frame on emergency stack | | Save srr1, srr0, dar and dsisr on stack | +-----------------------------------------------+ | (ME=1, IR=0, DR=0, RFID) | V machine_check_handle_early ME=1, IR=0, DR=0 | V +-----------------------------------------------+ | machine_check_early (r3=pt_regs) | ME=1, IR=0, DR=0 | Things to do: (in next patches) | | Flush SLB for SLB errors | | Flush TLB for TLB errors | | Decode and save MCE info | +-----------------------------------------------+ | (Fall through existing exception handler routine.) | V machine_check_pSerie ME=1, IR=0, DR=0 | (ME=1, IR=1, DR=1, RFID) | V machine_check_common ME=1, IR=1, DR=1 . . . Signed-off-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/kernel/exceptions-64s.S')
-rw-r--r--arch/powerpc/kernel/exceptions-64s.S111
1 files changed, 111 insertions, 0 deletions
diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
index 9f905e40922e..4034dfb2a530 100644
--- a/arch/powerpc/kernel/exceptions-64s.S
+++ b/arch/powerpc/kernel/exceptions-64s.S
@@ -156,7 +156,11 @@ machine_check_pSeries_1:
156 HMT_MEDIUM_PPR_DISCARD 156 HMT_MEDIUM_PPR_DISCARD
157 SET_SCRATCH0(r13) /* save r13 */ 157 SET_SCRATCH0(r13) /* save r13 */
158 EXCEPTION_PROLOG_0(PACA_EXMC) 158 EXCEPTION_PROLOG_0(PACA_EXMC)
159BEGIN_FTR_SECTION
160 b machine_check_pSeries_early
161FTR_SECTION_ELSE
159 b machine_check_pSeries_0 162 b machine_check_pSeries_0
163ALT_FTR_SECTION_END_IFSET(CPU_FTR_HVMODE)
160 164
161 . = 0x300 165 . = 0x300
162 .globl data_access_pSeries 166 .globl data_access_pSeries
@@ -405,6 +409,64 @@ denorm_exception_hv:
405 409
406 .align 7 410 .align 7
407 /* moved from 0x200 */ 411 /* moved from 0x200 */
412machine_check_pSeries_early:
413BEGIN_FTR_SECTION
414 EXCEPTION_PROLOG_1(PACA_EXMC, NOTEST, 0x200)
415 /*
416 * Register contents:
417 * R13 = PACA
418 * R9 = CR
419 * Original R9 to R13 is saved on PACA_EXMC
420 *
421 * Switch to mc_emergency stack and handle re-entrancy (though we
422 * currently don't test for overflow). Save MCE registers srr1,
423 * srr0, dar and dsisr and then set ME=1
424 *
425 * We use paca->in_mce to check whether this is the first entry or
426 * nested machine check. We increment paca->in_mce to track nested
427 * machine checks.
428 *
429 * If this is the first entry then set stack pointer to
430 * paca->mc_emergency_sp, otherwise r1 is already pointing to
431 * stack frame on mc_emergency stack.
432 *
433 * NOTE: We are here with MSR_ME=0 (off), which means we risk a
434 * checkstop if we get another machine check exception before we do
435 * rfid with MSR_ME=1.
436 */
437 mr r11,r1 /* Save r1 */
438 lhz r10,PACA_IN_MCE(r13)
439 cmpwi r10,0 /* Are we in nested machine check */
440 bne 0f /* Yes, we are. */
441 /* First machine check entry */
442 ld r1,PACAMCEMERGSP(r13) /* Use MC emergency stack */
4430: subi r1,r1,INT_FRAME_SIZE /* alloc stack frame */
444 addi r10,r10,1 /* increment paca->in_mce */
445 sth r10,PACA_IN_MCE(r13)
446 std r11,GPR1(r1) /* Save r1 on the stack. */
447 std r11,0(r1) /* make stack chain pointer */
448 mfspr r11,SPRN_SRR0 /* Save SRR0 */
449 std r11,_NIP(r1)
450 mfspr r11,SPRN_SRR1 /* Save SRR1 */
451 std r11,_MSR(r1)
452 mfspr r11,SPRN_DAR /* Save DAR */
453 std r11,_DAR(r1)
454 mfspr r11,SPRN_DSISR /* Save DSISR */
455 std r11,_DSISR(r1)
456 std r9,_CCR(r1) /* Save CR in stackframe */
457 /* Save r9 through r13 from EXMC save area to stack frame. */
458 EXCEPTION_PROLOG_COMMON_2(PACA_EXMC)
459 mfmsr r11 /* get MSR value */
460 ori r11,r11,MSR_ME /* turn on ME bit */
461 ori r11,r11,MSR_RI /* turn on RI bit */
462 ld r12,PACAKBASE(r13) /* get high part of &label */
463 LOAD_HANDLER(r12, machine_check_handle_early)
464 mtspr SPRN_SRR0,r12
465 mtspr SPRN_SRR1,r11
466 rfid
467 b . /* prevent speculative execution */
468END_FTR_SECTION_IFSET(CPU_FTR_HVMODE)
469
408machine_check_pSeries: 470machine_check_pSeries:
409 .globl machine_check_fwnmi 471 .globl machine_check_fwnmi
410machine_check_fwnmi: 472machine_check_fwnmi:
@@ -712,6 +774,55 @@ machine_check_common:
712 bl .machine_check_exception 774 bl .machine_check_exception
713 b .ret_from_except 775 b .ret_from_except
714 776
777#define MACHINE_CHECK_HANDLER_WINDUP \
778 /* Clear MSR_RI before setting SRR0 and SRR1. */\
779 li r0,MSR_RI; \
780 mfmsr r9; /* get MSR value */ \
781 andc r9,r9,r0; \
782 mtmsrd r9,1; /* Clear MSR_RI */ \
783 /* Move original SRR0 and SRR1 into the respective regs */ \
784 ld r9,_MSR(r1); \
785 mtspr SPRN_SRR1,r9; \
786 ld r3,_NIP(r1); \
787 mtspr SPRN_SRR0,r3; \
788 ld r9,_CTR(r1); \
789 mtctr r9; \
790 ld r9,_XER(r1); \
791 mtxer r9; \
792 ld r9,_LINK(r1); \
793 mtlr r9; \
794 REST_GPR(0, r1); \
795 REST_8GPRS(2, r1); \
796 REST_GPR(10, r1); \
797 ld r11,_CCR(r1); \
798 mtcr r11; \
799 /* Decrement paca->in_mce. */ \
800 lhz r12,PACA_IN_MCE(r13); \
801 subi r12,r12,1; \
802 sth r12,PACA_IN_MCE(r13); \
803 REST_GPR(11, r1); \
804 REST_2GPRS(12, r1); \
805 /* restore original r1. */ \
806 ld r1,GPR1(r1)
807
808 /*
809 * Handle machine check early in real mode. We come here with
810 * ME=1, MMU (IR=0 and DR=0) off and using MC emergency stack.
811 */
812 .align 7
813 .globl machine_check_handle_early
814machine_check_handle_early:
815BEGIN_FTR_SECTION
816 std r0,GPR0(r1) /* Save r0 */
817 EXCEPTION_PROLOG_COMMON_3(0x200)
818 bl .save_nvgprs
819 addi r3,r1,STACK_FRAME_OVERHEAD
820 bl .machine_check_early
821 /* Deliver the machine check to host kernel in V mode. */
822 MACHINE_CHECK_HANDLER_WINDUP
823 b machine_check_pSeries
824END_FTR_SECTION_IFSET(CPU_FTR_HVMODE)
825
715 STD_EXCEPTION_COMMON_ASYNC(0x500, hardware_interrupt, do_IRQ) 826 STD_EXCEPTION_COMMON_ASYNC(0x500, hardware_interrupt, do_IRQ)
716 STD_EXCEPTION_COMMON_ASYNC(0x900, decrementer, .timer_interrupt) 827 STD_EXCEPTION_COMMON_ASYNC(0x900, decrementer, .timer_interrupt)
717 STD_EXCEPTION_COMMON(0x980, hdecrementer, .hdec_interrupt) 828 STD_EXCEPTION_COMMON(0x980, hdecrementer, .hdec_interrupt)