Commit 616dff86 authored by Alexander Graf's avatar Alexander Graf

KVM: PPC: Book3S PR: Handle Facility interrupt and FSCR

POWER8 introduced a new interrupt type called "Facility unavailable interrupt"
which contains its status message in a new register called FSCR.

Handle these exits and try to emulate instructions for unhandled facilities.
Follow-on patches enable KVM to expose specific facilities into the guest.
Signed-off-by: default avatarAlexander Graf <agraf@suse.de>
parent a5948fa0
...@@ -102,6 +102,7 @@ ...@@ -102,6 +102,7 @@
#define BOOK3S_INTERRUPT_PERFMON 0xf00 #define BOOK3S_INTERRUPT_PERFMON 0xf00
#define BOOK3S_INTERRUPT_ALTIVEC 0xf20 #define BOOK3S_INTERRUPT_ALTIVEC 0xf20
#define BOOK3S_INTERRUPT_VSX 0xf40 #define BOOK3S_INTERRUPT_VSX 0xf40
#define BOOK3S_INTERRUPT_FAC_UNAVAIL 0xf60
#define BOOK3S_INTERRUPT_H_FAC_UNAVAIL 0xf80 #define BOOK3S_INTERRUPT_H_FAC_UNAVAIL 0xf80
#define BOOK3S_IRQPRIO_SYSTEM_RESET 0 #define BOOK3S_IRQPRIO_SYSTEM_RESET 0
...@@ -114,14 +115,15 @@ ...@@ -114,14 +115,15 @@
#define BOOK3S_IRQPRIO_FP_UNAVAIL 7 #define BOOK3S_IRQPRIO_FP_UNAVAIL 7
#define BOOK3S_IRQPRIO_ALTIVEC 8 #define BOOK3S_IRQPRIO_ALTIVEC 8
#define BOOK3S_IRQPRIO_VSX 9 #define BOOK3S_IRQPRIO_VSX 9
#define BOOK3S_IRQPRIO_SYSCALL 10 #define BOOK3S_IRQPRIO_FAC_UNAVAIL 10
#define BOOK3S_IRQPRIO_MACHINE_CHECK 11 #define BOOK3S_IRQPRIO_SYSCALL 11
#define BOOK3S_IRQPRIO_DEBUG 12 #define BOOK3S_IRQPRIO_MACHINE_CHECK 12
#define BOOK3S_IRQPRIO_EXTERNAL 13 #define BOOK3S_IRQPRIO_DEBUG 13
#define BOOK3S_IRQPRIO_DECREMENTER 14 #define BOOK3S_IRQPRIO_EXTERNAL 14
#define BOOK3S_IRQPRIO_PERFORMANCE_MONITOR 15 #define BOOK3S_IRQPRIO_DECREMENTER 15
#define BOOK3S_IRQPRIO_EXTERNAL_LEVEL 16 #define BOOK3S_IRQPRIO_PERFORMANCE_MONITOR 16
#define BOOK3S_IRQPRIO_MAX 17 #define BOOK3S_IRQPRIO_EXTERNAL_LEVEL 17
#define BOOK3S_IRQPRIO_MAX 18
#define BOOK3S_HFLAG_DCBZ32 0x1 #define BOOK3S_HFLAG_DCBZ32 0x1
#define BOOK3S_HFLAG_SLB 0x2 #define BOOK3S_HFLAG_SLB 0x2
......
...@@ -104,6 +104,7 @@ struct kvmppc_host_state { ...@@ -104,6 +104,7 @@ struct kvmppc_host_state {
#ifdef CONFIG_PPC_BOOK3S_64 #ifdef CONFIG_PPC_BOOK3S_64
u64 cfar; u64 cfar;
u64 ppr; u64 ppr;
u64 host_fscr;
#endif #endif
}; };
...@@ -133,6 +134,7 @@ struct kvmppc_book3s_shadow_vcpu { ...@@ -133,6 +134,7 @@ struct kvmppc_book3s_shadow_vcpu {
u64 esid; u64 esid;
u64 vsid; u64 vsid;
} slb[64]; /* guest SLB */ } slb[64]; /* guest SLB */
u64 shadow_fscr;
#endif #endif
}; };
......
...@@ -475,6 +475,7 @@ struct kvm_vcpu_arch { ...@@ -475,6 +475,7 @@ struct kvm_vcpu_arch {
ulong ppr; ulong ppr;
ulong pspb; ulong pspb;
ulong fscr; ulong fscr;
ulong shadow_fscr;
ulong ebbhr; ulong ebbhr;
ulong ebbrr; ulong ebbrr;
ulong bescr; ulong bescr;
......
...@@ -537,6 +537,7 @@ int main(void) ...@@ -537,6 +537,7 @@ int main(void)
DEFINE(VCPU_CFAR, offsetof(struct kvm_vcpu, arch.cfar)); DEFINE(VCPU_CFAR, offsetof(struct kvm_vcpu, arch.cfar));
DEFINE(VCPU_PPR, offsetof(struct kvm_vcpu, arch.ppr)); DEFINE(VCPU_PPR, offsetof(struct kvm_vcpu, arch.ppr));
DEFINE(VCPU_FSCR, offsetof(struct kvm_vcpu, arch.fscr)); DEFINE(VCPU_FSCR, offsetof(struct kvm_vcpu, arch.fscr));
DEFINE(VCPU_SHADOW_FSCR, offsetof(struct kvm_vcpu, arch.shadow_fscr));
DEFINE(VCPU_PSPB, offsetof(struct kvm_vcpu, arch.pspb)); DEFINE(VCPU_PSPB, offsetof(struct kvm_vcpu, arch.pspb));
DEFINE(VCPU_EBBHR, offsetof(struct kvm_vcpu, arch.ebbhr)); DEFINE(VCPU_EBBHR, offsetof(struct kvm_vcpu, arch.ebbhr));
DEFINE(VCPU_EBBRR, offsetof(struct kvm_vcpu, arch.ebbrr)); DEFINE(VCPU_EBBRR, offsetof(struct kvm_vcpu, arch.ebbrr));
...@@ -618,6 +619,7 @@ int main(void) ...@@ -618,6 +619,7 @@ int main(void)
#ifdef CONFIG_PPC64 #ifdef CONFIG_PPC64
SVCPU_FIELD(SVCPU_SLB, slb); SVCPU_FIELD(SVCPU_SLB, slb);
SVCPU_FIELD(SVCPU_SLB_MAX, slb_max); SVCPU_FIELD(SVCPU_SLB_MAX, slb_max);
SVCPU_FIELD(SVCPU_SHADOW_FSCR, shadow_fscr);
#endif #endif
HSTATE_FIELD(HSTATE_HOST_R1, host_r1); HSTATE_FIELD(HSTATE_HOST_R1, host_r1);
...@@ -653,6 +655,7 @@ int main(void) ...@@ -653,6 +655,7 @@ int main(void)
#ifdef CONFIG_PPC_BOOK3S_64 #ifdef CONFIG_PPC_BOOK3S_64
HSTATE_FIELD(HSTATE_CFAR, cfar); HSTATE_FIELD(HSTATE_CFAR, cfar);
HSTATE_FIELD(HSTATE_PPR, ppr); HSTATE_FIELD(HSTATE_PPR, ppr);
HSTATE_FIELD(HSTATE_HOST_FSCR, host_fscr);
#endif /* CONFIG_PPC_BOOK3S_64 */ #endif /* CONFIG_PPC_BOOK3S_64 */
#else /* CONFIG_PPC_BOOK3S */ #else /* CONFIG_PPC_BOOK3S */
......
...@@ -145,6 +145,7 @@ static int kvmppc_book3s_vec2irqprio(unsigned int vec) ...@@ -145,6 +145,7 @@ static int kvmppc_book3s_vec2irqprio(unsigned int vec)
case 0xd00: prio = BOOK3S_IRQPRIO_DEBUG; break; case 0xd00: prio = BOOK3S_IRQPRIO_DEBUG; break;
case 0xf20: prio = BOOK3S_IRQPRIO_ALTIVEC; break; case 0xf20: prio = BOOK3S_IRQPRIO_ALTIVEC; break;
case 0xf40: prio = BOOK3S_IRQPRIO_VSX; break; case 0xf40: prio = BOOK3S_IRQPRIO_VSX; break;
case 0xf60: prio = BOOK3S_IRQPRIO_FAC_UNAVAIL; break;
default: prio = BOOK3S_IRQPRIO_MAX; break; default: prio = BOOK3S_IRQPRIO_MAX; break;
} }
...@@ -275,6 +276,9 @@ int kvmppc_book3s_irqprio_deliver(struct kvm_vcpu *vcpu, unsigned int priority) ...@@ -275,6 +276,9 @@ int kvmppc_book3s_irqprio_deliver(struct kvm_vcpu *vcpu, unsigned int priority)
case BOOK3S_IRQPRIO_PERFORMANCE_MONITOR: case BOOK3S_IRQPRIO_PERFORMANCE_MONITOR:
vec = BOOK3S_INTERRUPT_PERFMON; vec = BOOK3S_INTERRUPT_PERFMON;
break; break;
case BOOK3S_IRQPRIO_FAC_UNAVAIL:
vec = BOOK3S_INTERRUPT_FAC_UNAVAIL;
break;
default: default:
deliver = 0; deliver = 0;
printk(KERN_ERR "KVM: Unknown interrupt: 0x%x\n", priority); printk(KERN_ERR "KVM: Unknown interrupt: 0x%x\n", priority);
...@@ -627,6 +631,9 @@ int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg) ...@@ -627,6 +631,9 @@ int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
val = get_reg_val(reg->id, kvmppc_xics_get_icp(vcpu)); val = get_reg_val(reg->id, kvmppc_xics_get_icp(vcpu));
break; break;
#endif /* CONFIG_KVM_XICS */ #endif /* CONFIG_KVM_XICS */
case KVM_REG_PPC_FSCR:
val = get_reg_val(reg->id, vcpu->arch.fscr);
break;
default: default:
r = -EINVAL; r = -EINVAL;
break; break;
...@@ -716,6 +723,9 @@ int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg) ...@@ -716,6 +723,9 @@ int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
set_reg_val(reg->id, val)); set_reg_val(reg->id, val));
break; break;
#endif /* CONFIG_KVM_XICS */ #endif /* CONFIG_KVM_XICS */
case KVM_REG_PPC_FSCR:
vcpu->arch.fscr = set_reg_val(reg->id, val);
break;
default: default:
r = -EINVAL; r = -EINVAL;
break; break;
......
...@@ -438,6 +438,9 @@ int kvmppc_core_emulate_mtspr_pr(struct kvm_vcpu *vcpu, int sprn, ulong spr_val) ...@@ -438,6 +438,9 @@ int kvmppc_core_emulate_mtspr_pr(struct kvm_vcpu *vcpu, int sprn, ulong spr_val)
case SPRN_GQR7: case SPRN_GQR7:
to_book3s(vcpu)->gqr[sprn - SPRN_GQR0] = spr_val; to_book3s(vcpu)->gqr[sprn - SPRN_GQR0] = spr_val;
break; break;
case SPRN_FSCR:
vcpu->arch.fscr = spr_val;
break;
case SPRN_ICTC: case SPRN_ICTC:
case SPRN_THRM1: case SPRN_THRM1:
case SPRN_THRM2: case SPRN_THRM2:
...@@ -545,6 +548,9 @@ int kvmppc_core_emulate_mfspr_pr(struct kvm_vcpu *vcpu, int sprn, ulong *spr_val ...@@ -545,6 +548,9 @@ int kvmppc_core_emulate_mfspr_pr(struct kvm_vcpu *vcpu, int sprn, ulong *spr_val
case SPRN_GQR7: case SPRN_GQR7:
*spr_val = to_book3s(vcpu)->gqr[sprn - SPRN_GQR0]; *spr_val = to_book3s(vcpu)->gqr[sprn - SPRN_GQR0];
break; break;
case SPRN_FSCR:
*spr_val = vcpu->arch.fscr;
break;
case SPRN_THRM1: case SPRN_THRM1:
case SPRN_THRM2: case SPRN_THRM2:
case SPRN_THRM3: case SPRN_THRM3:
......
...@@ -879,9 +879,6 @@ static int kvmppc_get_one_reg_hv(struct kvm_vcpu *vcpu, u64 id, ...@@ -879,9 +879,6 @@ static int kvmppc_get_one_reg_hv(struct kvm_vcpu *vcpu, u64 id,
case KVM_REG_PPC_IAMR: case KVM_REG_PPC_IAMR:
*val = get_reg_val(id, vcpu->arch.iamr); *val = get_reg_val(id, vcpu->arch.iamr);
break; break;
case KVM_REG_PPC_FSCR:
*val = get_reg_val(id, vcpu->arch.fscr);
break;
case KVM_REG_PPC_PSPB: case KVM_REG_PPC_PSPB:
*val = get_reg_val(id, vcpu->arch.pspb); *val = get_reg_val(id, vcpu->arch.pspb);
break; break;
...@@ -1091,9 +1088,6 @@ static int kvmppc_set_one_reg_hv(struct kvm_vcpu *vcpu, u64 id, ...@@ -1091,9 +1088,6 @@ static int kvmppc_set_one_reg_hv(struct kvm_vcpu *vcpu, u64 id,
case KVM_REG_PPC_IAMR: case KVM_REG_PPC_IAMR:
vcpu->arch.iamr = set_reg_val(id, *val); vcpu->arch.iamr = set_reg_val(id, *val);
break; break;
case KVM_REG_PPC_FSCR:
vcpu->arch.fscr = set_reg_val(id, *val);
break;
case KVM_REG_PPC_PSPB: case KVM_REG_PPC_PSPB:
vcpu->arch.pspb = set_reg_val(id, *val); vcpu->arch.pspb = set_reg_val(id, *val);
break; break;
......
...@@ -53,6 +53,7 @@ ...@@ -53,6 +53,7 @@
static int kvmppc_handle_ext(struct kvm_vcpu *vcpu, unsigned int exit_nr, static int kvmppc_handle_ext(struct kvm_vcpu *vcpu, unsigned int exit_nr,
ulong msr); ulong msr);
static void kvmppc_giveup_fac(struct kvm_vcpu *vcpu, ulong fac);
/* Some compatibility defines */ /* Some compatibility defines */
#ifdef CONFIG_PPC_BOOK3S_32 #ifdef CONFIG_PPC_BOOK3S_32
...@@ -115,6 +116,9 @@ void kvmppc_copy_to_svcpu(struct kvmppc_book3s_shadow_vcpu *svcpu, ...@@ -115,6 +116,9 @@ void kvmppc_copy_to_svcpu(struct kvmppc_book3s_shadow_vcpu *svcpu,
svcpu->ctr = vcpu->arch.ctr; svcpu->ctr = vcpu->arch.ctr;
svcpu->lr = vcpu->arch.lr; svcpu->lr = vcpu->arch.lr;
svcpu->pc = vcpu->arch.pc; svcpu->pc = vcpu->arch.pc;
#ifdef CONFIG_PPC_BOOK3S_64
svcpu->shadow_fscr = vcpu->arch.shadow_fscr;
#endif
svcpu->in_use = true; svcpu->in_use = true;
} }
...@@ -158,6 +162,9 @@ void kvmppc_copy_from_svcpu(struct kvm_vcpu *vcpu, ...@@ -158,6 +162,9 @@ void kvmppc_copy_from_svcpu(struct kvm_vcpu *vcpu,
vcpu->arch.fault_dar = svcpu->fault_dar; vcpu->arch.fault_dar = svcpu->fault_dar;
vcpu->arch.fault_dsisr = svcpu->fault_dsisr; vcpu->arch.fault_dsisr = svcpu->fault_dsisr;
vcpu->arch.last_inst = svcpu->last_inst; vcpu->arch.last_inst = svcpu->last_inst;
#ifdef CONFIG_PPC_BOOK3S_64
vcpu->arch.shadow_fscr = svcpu->shadow_fscr;
#endif
svcpu->in_use = false; svcpu->in_use = false;
out: out:
...@@ -610,6 +617,17 @@ void kvmppc_giveup_ext(struct kvm_vcpu *vcpu, ulong msr) ...@@ -610,6 +617,17 @@ void kvmppc_giveup_ext(struct kvm_vcpu *vcpu, ulong msr)
kvmppc_recalc_shadow_msr(vcpu); kvmppc_recalc_shadow_msr(vcpu);
} }
/* Give up facility (TAR / EBB / DSCR) */
static void kvmppc_giveup_fac(struct kvm_vcpu *vcpu, ulong fac)
{
#ifdef CONFIG_PPC_BOOK3S_64
if (!(vcpu->arch.shadow_fscr & (1ULL << fac))) {
/* Facility not available to the guest, ignore giveup request*/
return;
}
#endif
}
static int kvmppc_read_inst(struct kvm_vcpu *vcpu) static int kvmppc_read_inst(struct kvm_vcpu *vcpu)
{ {
ulong srr0 = kvmppc_get_pc(vcpu); ulong srr0 = kvmppc_get_pc(vcpu);
...@@ -741,6 +759,50 @@ static void kvmppc_handle_lost_ext(struct kvm_vcpu *vcpu) ...@@ -741,6 +759,50 @@ static void kvmppc_handle_lost_ext(struct kvm_vcpu *vcpu)
current->thread.regs->msr |= lost_ext; current->thread.regs->msr |= lost_ext;
} }
#ifdef CONFIG_PPC_BOOK3S_64
static void kvmppc_trigger_fac_interrupt(struct kvm_vcpu *vcpu, ulong fac)
{
/* Inject the Interrupt Cause field and trigger a guest interrupt */
vcpu->arch.fscr &= ~(0xffULL << 56);
vcpu->arch.fscr |= (fac << 56);
kvmppc_book3s_queue_irqprio(vcpu, BOOK3S_INTERRUPT_FAC_UNAVAIL);
}
static void kvmppc_emulate_fac(struct kvm_vcpu *vcpu, ulong fac)
{
enum emulation_result er = EMULATE_FAIL;
if (!(kvmppc_get_msr(vcpu) & MSR_PR))
er = kvmppc_emulate_instruction(vcpu->run, vcpu);
if ((er != EMULATE_DONE) && (er != EMULATE_AGAIN)) {
/* Couldn't emulate, trigger interrupt in guest */
kvmppc_trigger_fac_interrupt(vcpu, fac);
}
}
/* Enable facilities (TAR, EBB, DSCR) for the guest */
static int kvmppc_handle_fac(struct kvm_vcpu *vcpu, ulong fac)
{
BUG_ON(!cpu_has_feature(CPU_FTR_ARCH_207S));
if (!(vcpu->arch.fscr & (1ULL << fac))) {
/* Facility not enabled by the guest */
kvmppc_trigger_fac_interrupt(vcpu, fac);
return RESUME_GUEST;
}
switch (fac) {
default:
kvmppc_emulate_fac(vcpu, fac);
break;
}
return RESUME_GUEST;
}
#endif
int kvmppc_handle_exit_pr(struct kvm_run *run, struct kvm_vcpu *vcpu, int kvmppc_handle_exit_pr(struct kvm_run *run, struct kvm_vcpu *vcpu,
unsigned int exit_nr) unsigned int exit_nr)
{ {
...@@ -1015,6 +1077,12 @@ int kvmppc_handle_exit_pr(struct kvm_run *run, struct kvm_vcpu *vcpu, ...@@ -1015,6 +1077,12 @@ int kvmppc_handle_exit_pr(struct kvm_run *run, struct kvm_vcpu *vcpu,
} }
r = RESUME_GUEST; r = RESUME_GUEST;
break; break;
#ifdef CONFIG_PPC_BOOK3S_64
case BOOK3S_INTERRUPT_FAC_UNAVAIL:
kvmppc_handle_fac(vcpu, vcpu->arch.shadow_fscr >> 56);
r = RESUME_GUEST;
break;
#endif
case BOOK3S_INTERRUPT_MACHINE_CHECK: case BOOK3S_INTERRUPT_MACHINE_CHECK:
case BOOK3S_INTERRUPT_TRACE: case BOOK3S_INTERRUPT_TRACE:
kvmppc_book3s_queue_irqprio(vcpu, exit_nr); kvmppc_book3s_queue_irqprio(vcpu, exit_nr);
......
...@@ -90,6 +90,15 @@ kvmppc_handler_trampoline_enter: ...@@ -90,6 +90,15 @@ kvmppc_handler_trampoline_enter:
LOAD_GUEST_SEGMENTS LOAD_GUEST_SEGMENTS
#ifdef CONFIG_PPC_BOOK3S_64 #ifdef CONFIG_PPC_BOOK3S_64
BEGIN_FTR_SECTION
/* Save host FSCR */
mfspr r8, SPRN_FSCR
std r8, HSTATE_HOST_FSCR(r13)
/* Set FSCR during guest execution */
ld r9, SVCPU_SHADOW_FSCR(r13)
mtspr SPRN_FSCR, r9
END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
/* Some guests may need to have dcbz set to 32 byte length. /* Some guests may need to have dcbz set to 32 byte length.
* *
* Usually we ensure that by patching the guest's instructions * Usually we ensure that by patching the guest's instructions
...@@ -255,6 +264,10 @@ BEGIN_FTR_SECTION ...@@ -255,6 +264,10 @@ BEGIN_FTR_SECTION
cmpwi r12, BOOK3S_INTERRUPT_H_EMUL_ASSIST cmpwi r12, BOOK3S_INTERRUPT_H_EMUL_ASSIST
beq- ld_last_inst beq- ld_last_inst
END_FTR_SECTION_IFSET(CPU_FTR_HVMODE) END_FTR_SECTION_IFSET(CPU_FTR_HVMODE)
BEGIN_FTR_SECTION
cmpwi r12, BOOK3S_INTERRUPT_FAC_UNAVAIL
beq- ld_last_inst
END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
#endif #endif
b no_ld_last_inst b no_ld_last_inst
...@@ -311,6 +324,18 @@ no_ld_last_inst: ...@@ -311,6 +324,18 @@ no_ld_last_inst:
no_dcbz32_off: no_dcbz32_off:
BEGIN_FTR_SECTION
/* Save guest FSCR on a FAC_UNAVAIL interrupt */
cmpwi r12, BOOK3S_INTERRUPT_FAC_UNAVAIL
bne+ no_fscr_save
mfspr r7, SPRN_FSCR
std r7, SVCPU_SHADOW_FSCR(r13)
no_fscr_save:
/* Restore host FSCR */
ld r8, HSTATE_HOST_FSCR(r13)
mtspr SPRN_FSCR, r8
END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
#endif /* CONFIG_PPC_BOOK3S_64 */ #endif /* CONFIG_PPC_BOOK3S_64 */
/* /*
......
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