Commit 0869b6fd authored by Mahesh Salgaonkar's avatar Mahesh Salgaonkar Committed by Benjamin Herrenschmidt

powerpc/book3s: Add basic infrastructure to handle HMI in Linux.

Handle Hypervisor Maintenance Interrupt (HMI) in Linux. This patch implements
basic infrastructure to handle HMI in Linux host. The design is to invoke
opal handle hmi in real mode for recovery and set irq_pending when we hit HMI.
During check_irq_replay pull opal hmi event and print hmi info on console.
Signed-off-by: default avatarMahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent 84f1966e
...@@ -425,6 +425,8 @@ label##_relon_hv: \ ...@@ -425,6 +425,8 @@ label##_relon_hv: \
#define SOFTEN_VALUE_0xa00 PACA_IRQ_DBELL #define SOFTEN_VALUE_0xa00 PACA_IRQ_DBELL
#define SOFTEN_VALUE_0xe80 PACA_IRQ_DBELL #define SOFTEN_VALUE_0xe80 PACA_IRQ_DBELL
#define SOFTEN_VALUE_0xe82 PACA_IRQ_DBELL #define SOFTEN_VALUE_0xe82 PACA_IRQ_DBELL
#define SOFTEN_VALUE_0xe60 PACA_IRQ_HMI
#define SOFTEN_VALUE_0xe62 PACA_IRQ_HMI
#define __SOFTEN_TEST(h, vec) \ #define __SOFTEN_TEST(h, vec) \
lbz r10,PACASOFTIRQEN(r13); \ lbz r10,PACASOFTIRQEN(r13); \
......
...@@ -11,6 +11,7 @@ typedef struct { ...@@ -11,6 +11,7 @@ typedef struct {
unsigned int pmu_irqs; unsigned int pmu_irqs;
unsigned int mce_exceptions; unsigned int mce_exceptions;
unsigned int spurious_irqs; unsigned int spurious_irqs;
unsigned int hmi_exceptions;
#ifdef CONFIG_PPC_DOORBELL #ifdef CONFIG_PPC_DOORBELL
unsigned int doorbell_irqs; unsigned int doorbell_irqs;
#endif #endif
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#define PACA_IRQ_EE 0x04 #define PACA_IRQ_EE 0x04
#define PACA_IRQ_DEC 0x08 /* Or FIT */ #define PACA_IRQ_DEC 0x08 /* Or FIT */
#define PACA_IRQ_EE_EDGE 0x10 /* BookE only */ #define PACA_IRQ_EE_EDGE 0x10 /* BookE only */
#define PACA_IRQ_HMI 0x20
#endif /* CONFIG_PPC64 */ #endif /* CONFIG_PPC64 */
......
...@@ -98,6 +98,7 @@ ...@@ -98,6 +98,7 @@
#define BOOK3S_INTERRUPT_H_DATA_STORAGE 0xe00 #define BOOK3S_INTERRUPT_H_DATA_STORAGE 0xe00
#define BOOK3S_INTERRUPT_H_INST_STORAGE 0xe20 #define BOOK3S_INTERRUPT_H_INST_STORAGE 0xe20
#define BOOK3S_INTERRUPT_H_EMUL_ASSIST 0xe40 #define BOOK3S_INTERRUPT_H_EMUL_ASSIST 0xe40
#define BOOK3S_INTERRUPT_HMI 0xe60
#define BOOK3S_INTERRUPT_H_DOORBELL 0xe80 #define BOOK3S_INTERRUPT_H_DOORBELL 0xe80
#define BOOK3S_INTERRUPT_PERFMON 0xf00 #define BOOK3S_INTERRUPT_PERFMON 0xf00
#define BOOK3S_INTERRUPT_ALTIVEC 0xf20 #define BOOK3S_INTERRUPT_ALTIVEC 0xf20
......
...@@ -174,6 +174,10 @@ struct machdep_calls { ...@@ -174,6 +174,10 @@ struct machdep_calls {
/* Exception handlers */ /* Exception handlers */
int (*system_reset_exception)(struct pt_regs *regs); int (*system_reset_exception)(struct pt_regs *regs);
int (*machine_check_exception)(struct pt_regs *regs); int (*machine_check_exception)(struct pt_regs *regs);
int (*handle_hmi_exception)(struct pt_regs *regs);
/* Early exception handlers called in realmode */
int (*hmi_exception_early)(struct pt_regs *regs);
/* Called during machine check exception to retrive fixup address. */ /* Called during machine check exception to retrive fixup address. */
bool (*mce_check_early_recovery)(struct pt_regs *regs); bool (*mce_check_early_recovery)(struct pt_regs *regs);
......
...@@ -915,6 +915,8 @@ extern void opal_msglog_init(void); ...@@ -915,6 +915,8 @@ extern void opal_msglog_init(void);
extern int opal_machine_check(struct pt_regs *regs); extern int opal_machine_check(struct pt_regs *regs);
extern bool opal_mce_check_early_recovery(struct pt_regs *regs); extern bool opal_mce_check_early_recovery(struct pt_regs *regs);
extern int opal_hmi_exception_early(struct pt_regs *regs);
extern int opal_handle_hmi_exception(struct pt_regs *regs);
extern void opal_shutdown(void); extern void opal_shutdown(void);
extern int opal_resync_timebase(void); extern int opal_resync_timebase(void);
......
...@@ -915,6 +915,11 @@ restore_check_irq_replay: ...@@ -915,6 +915,11 @@ restore_check_irq_replay:
addi r3,r1,STACK_FRAME_OVERHEAD; addi r3,r1,STACK_FRAME_OVERHEAD;
bl do_IRQ bl do_IRQ
b ret_from_except b ret_from_except
1: cmpwi cr0,r3,0xe60
bne 1f
addi r3,r1,STACK_FRAME_OVERHEAD;
bl handle_hmi_exception
b ret_from_except
1: cmpwi cr0,r3,0x900 1: cmpwi cr0,r3,0x900
bne 1f bne 1f
addi r3,r1,STACK_FRAME_OVERHEAD; addi r3,r1,STACK_FRAME_OVERHEAD;
......
...@@ -335,7 +335,7 @@ emulation_assist_trampoline: ...@@ -335,7 +335,7 @@ emulation_assist_trampoline:
hv_exception_trampoline: hv_exception_trampoline:
SET_SCRATCH0(r13) SET_SCRATCH0(r13)
EXCEPTION_PROLOG_0(PACA_EXGEN) EXCEPTION_PROLOG_0(PACA_EXGEN)
b hmi_exception_hv b hmi_exception_early
. = 0xe80 . = 0xe80
hv_doorbell_trampoline: hv_doorbell_trampoline:
...@@ -589,8 +589,64 @@ END_FTR_SECTION_IFSET(CPU_FTR_CFAR) ...@@ -589,8 +589,64 @@ END_FTR_SECTION_IFSET(CPU_FTR_CFAR)
KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe22) KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe22)
STD_EXCEPTION_HV_OOL(0xe42, emulation_assist) STD_EXCEPTION_HV_OOL(0xe42, emulation_assist)
KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe42) KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe42)
STD_EXCEPTION_HV_OOL(0xe62, hmi_exception) /* need to flush cache ? */ MASKABLE_EXCEPTION_HV_OOL(0xe62, hmi_exception)
KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe62) KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe62)
.globl hmi_exception_early
hmi_exception_early:
EXCEPTION_PROLOG_1(PACA_EXGEN, NOTEST, 0xe60)
mr r10,r1 /* Save r1 */
ld r1,PACAEMERGSP(r13) /* Use emergency stack */
subi r1,r1,INT_FRAME_SIZE /* alloc stack frame */
std r9,_CCR(r1) /* save CR in stackframe */
mfspr r11,SPRN_HSRR0 /* Save HSRR0 */
std r11,_NIP(r1) /* save HSRR0 in stackframe */
mfspr r12,SPRN_HSRR1 /* Save SRR1 */
std r12,_MSR(r1) /* save SRR1 in stackframe */
std r10,0(r1) /* make stack chain pointer */
std r0,GPR0(r1) /* save r0 in stackframe */
std r10,GPR1(r1) /* save r1 in stackframe */
EXCEPTION_PROLOG_COMMON_2(PACA_EXGEN)
EXCEPTION_PROLOG_COMMON_3(0xe60)
addi r3,r1,STACK_FRAME_OVERHEAD
bl hmi_exception_realmode
/* Windup the stack. */
/* Clear MSR_RI before setting SRR0 and SRR1. */
li r0,MSR_RI
mfmsr r9 /* get MSR value */
andc r9,r9,r0
mtmsrd r9,1 /* Clear MSR_RI */
/* Move original HSRR0 and HSRR1 into the respective regs */
ld r9,_MSR(r1)
mtspr SPRN_HSRR1,r9
ld r3,_NIP(r1)
mtspr SPRN_HSRR0,r3
ld r9,_CTR(r1)
mtctr r9
ld r9,_XER(r1)
mtxer r9
ld r9,_LINK(r1)
mtlr r9
REST_GPR(0, r1)
REST_8GPRS(2, r1)
REST_GPR(10, r1)
ld r11,_CCR(r1)
mtcr r11
REST_GPR(11, r1)
REST_2GPRS(12, r1)
/* restore original r1. */
ld r1,GPR1(r1)
/*
* Go to virtual mode and pull the HMI event information from
* firmware.
*/
.globl hmi_exception_after_realmode
hmi_exception_after_realmode:
SET_SCRATCH0(r13)
EXCEPTION_PROLOG_0(PACA_EXGEN)
b hmi_exception_hv
MASKABLE_EXCEPTION_HV_OOL(0xe82, h_doorbell) MASKABLE_EXCEPTION_HV_OOL(0xe82, h_doorbell)
KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe82) KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe82)
...@@ -611,6 +667,8 @@ END_FTR_SECTION_IFSET(CPU_FTR_CFAR) ...@@ -611,6 +667,8 @@ END_FTR_SECTION_IFSET(CPU_FTR_CFAR)
* - If it was a decrementer interrupt, we bump the dec to max and and return. * - If it was a decrementer interrupt, we bump the dec to max and and return.
* - If it was a doorbell we return immediately since doorbells are edge * - If it was a doorbell we return immediately since doorbells are edge
* triggered and won't automatically refire. * triggered and won't automatically refire.
* - If it was a HMI we return immediately since we handled it in realmode
* and it won't refire.
* - else we hard disable and return. * - else we hard disable and return.
* This is called with r10 containing the value to OR to the paca field. * This is called with r10 containing the value to OR to the paca field.
*/ */
...@@ -627,6 +685,8 @@ masked_##_H##interrupt: \ ...@@ -627,6 +685,8 @@ masked_##_H##interrupt: \
mtspr SPRN_DEC,r10; \ mtspr SPRN_DEC,r10; \
b 2f; \ b 2f; \
1: cmpwi r10,PACA_IRQ_DBELL; \ 1: cmpwi r10,PACA_IRQ_DBELL; \
beq 2f; \
cmpwi r10,PACA_IRQ_HMI; \
beq 2f; \ beq 2f; \
mfspr r10,SPRN_##_H##SRR1; \ mfspr r10,SPRN_##_H##SRR1; \
rldicl r10,r10,48,1; /* clear MSR_EE */ \ rldicl r10,r10,48,1; /* clear MSR_EE */ \
...@@ -767,7 +827,7 @@ kvmppc_skip_Hinterrupt: ...@@ -767,7 +827,7 @@ kvmppc_skip_Hinterrupt:
STD_EXCEPTION_COMMON(0xd00, single_step, single_step_exception) STD_EXCEPTION_COMMON(0xd00, single_step, single_step_exception)
STD_EXCEPTION_COMMON(0xe00, trap_0e, unknown_exception) STD_EXCEPTION_COMMON(0xe00, trap_0e, unknown_exception)
STD_EXCEPTION_COMMON(0xe40, emulation_assist, emulation_assist_interrupt) STD_EXCEPTION_COMMON(0xe40, emulation_assist, emulation_assist_interrupt)
STD_EXCEPTION_COMMON(0xe60, hmi_exception, unknown_exception) STD_EXCEPTION_COMMON_ASYNC(0xe60, hmi_exception, handle_hmi_exception)
#ifdef CONFIG_PPC_DOORBELL #ifdef CONFIG_PPC_DOORBELL
STD_EXCEPTION_COMMON_ASYNC(0xe80, h_doorbell, doorbell_exception) STD_EXCEPTION_COMMON_ASYNC(0xe80, h_doorbell, doorbell_exception)
#else #else
......
...@@ -189,6 +189,11 @@ notrace unsigned int __check_irq_replay(void) ...@@ -189,6 +189,11 @@ notrace unsigned int __check_irq_replay(void)
} }
#endif /* CONFIG_PPC_BOOK3E */ #endif /* CONFIG_PPC_BOOK3E */
/* Check if an hypervisor Maintenance interrupt happened */
local_paca->irq_happened &= ~PACA_IRQ_HMI;
if (happened & PACA_IRQ_HMI)
return 0xe60;
/* There should be nothing left ! */ /* There should be nothing left ! */
BUG_ON(local_paca->irq_happened != 0); BUG_ON(local_paca->irq_happened != 0);
...@@ -377,6 +382,14 @@ int arch_show_interrupts(struct seq_file *p, int prec) ...@@ -377,6 +382,14 @@ int arch_show_interrupts(struct seq_file *p, int prec)
seq_printf(p, "%10u ", per_cpu(irq_stat, j).mce_exceptions); seq_printf(p, "%10u ", per_cpu(irq_stat, j).mce_exceptions);
seq_printf(p, " Machine check exceptions\n"); seq_printf(p, " Machine check exceptions\n");
if (cpu_has_feature(CPU_FTR_HVMODE)) {
seq_printf(p, "%*s: ", prec, "HMI");
for_each_online_cpu(j)
seq_printf(p, "%10u ",
per_cpu(irq_stat, j).hmi_exceptions);
seq_printf(p, " Hypervisor Maintenance Interrupts\n");
}
#ifdef CONFIG_PPC_DOORBELL #ifdef CONFIG_PPC_DOORBELL
if (cpu_has_feature(CPU_FTR_DBELL)) { if (cpu_has_feature(CPU_FTR_DBELL)) {
seq_printf(p, "%*s: ", prec, "DBL"); seq_printf(p, "%*s: ", prec, "DBL");
...@@ -400,6 +413,7 @@ u64 arch_irq_stat_cpu(unsigned int cpu) ...@@ -400,6 +413,7 @@ u64 arch_irq_stat_cpu(unsigned int cpu)
sum += per_cpu(irq_stat, cpu).mce_exceptions; sum += per_cpu(irq_stat, cpu).mce_exceptions;
sum += per_cpu(irq_stat, cpu).spurious_irqs; sum += per_cpu(irq_stat, cpu).spurious_irqs;
sum += per_cpu(irq_stat, cpu).timer_irqs_others; sum += per_cpu(irq_stat, cpu).timer_irqs_others;
sum += per_cpu(irq_stat, cpu).hmi_exceptions;
#ifdef CONFIG_PPC_DOORBELL #ifdef CONFIG_PPC_DOORBELL
sum += per_cpu(irq_stat, cpu).doorbell_irqs; sum += per_cpu(irq_stat, cpu).doorbell_irqs;
#endif #endif
......
...@@ -302,6 +302,16 @@ long machine_check_early(struct pt_regs *regs) ...@@ -302,6 +302,16 @@ long machine_check_early(struct pt_regs *regs)
return handled; return handled;
} }
long hmi_exception_realmode(struct pt_regs *regs)
{
__get_cpu_var(irq_stat).hmi_exceptions++;
if (ppc_md.hmi_exception_early)
ppc_md.hmi_exception_early(regs);
return 0;
}
#endif #endif
/* /*
...@@ -738,6 +748,20 @@ void SMIException(struct pt_regs *regs) ...@@ -738,6 +748,20 @@ void SMIException(struct pt_regs *regs)
die("System Management Interrupt", regs, SIGABRT); die("System Management Interrupt", regs, SIGABRT);
} }
void handle_hmi_exception(struct pt_regs *regs)
{
struct pt_regs *old_regs;
old_regs = set_irq_regs(regs);
irq_enter();
if (ppc_md.handle_hmi_exception)
ppc_md.handle_hmi_exception(regs);
irq_exit();
set_irq_regs(old_regs);
}
void unknown_exception(struct pt_regs *regs) void unknown_exception(struct pt_regs *regs)
{ {
enum ctx_state prev_state = exception_enter(); enum ctx_state prev_state = exception_enter();
......
...@@ -159,6 +159,8 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) ...@@ -159,6 +159,8 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
cmpwi r12, BOOK3S_INTERRUPT_EXTERNAL cmpwi r12, BOOK3S_INTERRUPT_EXTERNAL
BEGIN_FTR_SECTION BEGIN_FTR_SECTION
beq 11f beq 11f
cmpwi cr2, r12, BOOK3S_INTERRUPT_HMI
beq cr2, 14f /* HMI check */
END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206) END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
/* RFI into the highmem handler, or branch to interrupt handler */ /* RFI into the highmem handler, or branch to interrupt handler */
...@@ -179,6 +181,10 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206) ...@@ -179,6 +181,10 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
13: b machine_check_fwnmi 13: b machine_check_fwnmi
14: mtspr SPRN_HSRR0, r8
mtspr SPRN_HSRR1, r7
b hmi_exception_after_realmode
kvmppc_primary_no_guest: kvmppc_primary_no_guest:
/* We handle this much like a ceded vcpu */ /* We handle this much like a ceded vcpu */
/* set our bit in napping_threads */ /* set our bit in napping_threads */
......
...@@ -514,6 +514,20 @@ int opal_machine_check(struct pt_regs *regs) ...@@ -514,6 +514,20 @@ int opal_machine_check(struct pt_regs *regs)
return 0; return 0;
} }
/* Early hmi handler called in real mode. */
int opal_hmi_exception_early(struct pt_regs *regs)
{
/* TODO: Call opal hmi handler. */
return 0;
}
/* HMI exception handler called in virtual mode during check_irq_replay. */
int opal_handle_hmi_exception(struct pt_regs *regs)
{
/* TODO: Retrive and print HMI event from OPAL. */
return 0;
}
static uint64_t find_recovery_address(uint64_t nip) static uint64_t find_recovery_address(uint64_t nip)
{ {
int i; int i;
......
...@@ -264,6 +264,8 @@ static void __init pnv_setup_machdep_opal(void) ...@@ -264,6 +264,8 @@ static void __init pnv_setup_machdep_opal(void)
ppc_md.halt = pnv_halt; ppc_md.halt = pnv_halt;
ppc_md.machine_check_exception = opal_machine_check; ppc_md.machine_check_exception = opal_machine_check;
ppc_md.mce_check_early_recovery = opal_mce_check_early_recovery; ppc_md.mce_check_early_recovery = opal_mce_check_early_recovery;
ppc_md.hmi_exception_early = opal_hmi_exception_early;
ppc_md.handle_hmi_exception = opal_handle_hmi_exception;
} }
#ifdef CONFIG_PPC_POWERNV_RTAS #ifdef CONFIG_PPC_POWERNV_RTAS
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment