blob: 5c427b41a2f549ce1ed986858839e72b67f2edea [file] [log] [blame]
Paul Mackerrasb4072df2012-11-23 22:37:50 +00001/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2, as
4 * published by the Free Software Foundation.
5 *
6 * Copyright 2012 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com>
7 */
8
9#include <linux/types.h>
10#include <linux/string.h>
11#include <linux/kvm.h>
12#include <linux/kvm_host.h>
13#include <linux/kernel.h>
14#include <asm/opal.h>
15
16/* SRR1 bits for machine check on POWER7 */
17#define SRR1_MC_LDSTERR (1ul << (63-42))
18#define SRR1_MC_IFETCH_SH (63-45)
19#define SRR1_MC_IFETCH_MASK 0x7
20#define SRR1_MC_IFETCH_SLBPAR 2 /* SLB parity error */
21#define SRR1_MC_IFETCH_SLBMULTI 3 /* SLB multi-hit */
22#define SRR1_MC_IFETCH_SLBPARMULTI 4 /* SLB parity + multi-hit */
23#define SRR1_MC_IFETCH_TLBMULTI 5 /* I-TLB multi-hit */
24
25/* DSISR bits for machine check on POWER7 */
26#define DSISR_MC_DERAT_MULTI 0x800 /* D-ERAT multi-hit */
27#define DSISR_MC_TLB_MULTI 0x400 /* D-TLB multi-hit */
28#define DSISR_MC_SLB_PARITY 0x100 /* SLB parity error */
29#define DSISR_MC_SLB_MULTI 0x080 /* SLB multi-hit */
30#define DSISR_MC_SLB_PARMULTI 0x040 /* SLB parity + multi-hit */
31
32/* POWER7 SLB flush and reload */
33static void reload_slb(struct kvm_vcpu *vcpu)
34{
35 struct slb_shadow *slb;
36 unsigned long i, n;
37
38 /* First clear out SLB */
39 asm volatile("slbmte %0,%0; slbia" : : "r" (0));
40
41 /* Do they have an SLB shadow buffer registered? */
42 slb = vcpu->arch.slb_shadow.pinned_addr;
43 if (!slb)
44 return;
45
46 /* Sanity check */
47 n = min_t(u32, slb->persistent, SLB_MIN_SIZE);
48 if ((void *) &slb->save_area[n] > vcpu->arch.slb_shadow.pinned_end)
49 return;
50
51 /* Load up the SLB from that */
52 for (i = 0; i < n; ++i) {
53 unsigned long rb = slb->save_area[i].esid;
54 unsigned long rs = slb->save_area[i].vsid;
55
56 rb = (rb & ~0xFFFul) | i; /* insert entry number */
57 asm volatile("slbmte %0,%1" : : "r" (rs), "r" (rb));
58 }
59}
60
Paul Mackerrasb4072df2012-11-23 22:37:50 +000061/*
62 * On POWER7, see if we can handle a machine check that occurred inside
63 * the guest in real mode, without switching to the host partition.
64 *
65 * Returns: 0 => exit guest, 1 => deliver machine check to guest
66 */
67static long kvmppc_realmode_mc_power7(struct kvm_vcpu *vcpu)
68{
69 unsigned long srr1 = vcpu->arch.shregs.msr;
Andreas Schwabd5913902012-12-22 04:09:17 +000070#ifdef CONFIG_PPC_POWERNV
Paul Mackerrasb4072df2012-11-23 22:37:50 +000071 struct opal_machine_check_event *opal_evt;
Andreas Schwabd5913902012-12-22 04:09:17 +000072#endif
Paul Mackerrasb4072df2012-11-23 22:37:50 +000073 long handled = 1;
74
75 if (srr1 & SRR1_MC_LDSTERR) {
76 /* error on load/store */
77 unsigned long dsisr = vcpu->arch.shregs.dsisr;
78
79 if (dsisr & (DSISR_MC_SLB_PARMULTI | DSISR_MC_SLB_MULTI |
80 DSISR_MC_SLB_PARITY | DSISR_MC_DERAT_MULTI)) {
81 /* flush and reload SLB; flushes D-ERAT too */
82 reload_slb(vcpu);
83 dsisr &= ~(DSISR_MC_SLB_PARMULTI | DSISR_MC_SLB_MULTI |
84 DSISR_MC_SLB_PARITY | DSISR_MC_DERAT_MULTI);
85 }
86 if (dsisr & DSISR_MC_TLB_MULTI) {
Mahesh Salgaonkar04407052013-10-30 20:04:56 +053087 if (cur_cpu_spec && cur_cpu_spec->flush_tlb)
88 cur_cpu_spec->flush_tlb(TLBIEL_INVAL_SET_LPID);
Paul Mackerrasb4072df2012-11-23 22:37:50 +000089 dsisr &= ~DSISR_MC_TLB_MULTI;
90 }
91 /* Any other errors we don't understand? */
92 if (dsisr & 0xffffffffUL)
93 handled = 0;
94 }
95
96 switch ((srr1 >> SRR1_MC_IFETCH_SH) & SRR1_MC_IFETCH_MASK) {
97 case 0:
98 break;
99 case SRR1_MC_IFETCH_SLBPAR:
100 case SRR1_MC_IFETCH_SLBMULTI:
101 case SRR1_MC_IFETCH_SLBPARMULTI:
102 reload_slb(vcpu);
103 break;
104 case SRR1_MC_IFETCH_TLBMULTI:
Mahesh Salgaonkar04407052013-10-30 20:04:56 +0530105 if (cur_cpu_spec && cur_cpu_spec->flush_tlb)
106 cur_cpu_spec->flush_tlb(TLBIEL_INVAL_SET_LPID);
Paul Mackerrasb4072df2012-11-23 22:37:50 +0000107 break;
108 default:
109 handled = 0;
110 }
111
Andreas Schwabd5913902012-12-22 04:09:17 +0000112#ifdef CONFIG_PPC_POWERNV
Paul Mackerrasb4072df2012-11-23 22:37:50 +0000113 /*
114 * See if OPAL has already handled the condition.
115 * We assume that if the condition is recovered then OPAL
116 * will have generated an error log event that we will pick
117 * up and log later.
118 */
119 opal_evt = local_paca->opal_mc_evt;
120 if (opal_evt->version == OpalMCE_V1 &&
121 (opal_evt->severity == OpalMCE_SEV_NO_ERROR ||
122 opal_evt->disposition == OpalMCE_DISPOSITION_RECOVERED))
123 handled = 1;
124
125 if (handled)
126 opal_evt->in_use = 0;
Andreas Schwabd5913902012-12-22 04:09:17 +0000127#endif
Paul Mackerrasb4072df2012-11-23 22:37:50 +0000128
129 return handled;
130}
131
132long kvmppc_realmode_machine_check(struct kvm_vcpu *vcpu)
133{
134 if (cpu_has_feature(CPU_FTR_ARCH_206))
135 return kvmppc_realmode_mc_power7(vcpu);
136
137 return 0;
138}