in kvm/book3s_emulate.c [234:631]
int kvmppc_core_emulate_op_pr(struct kvm_vcpu *vcpu,
unsigned int inst, int *advance)
{
int emulated = EMULATE_DONE;
int rt = get_rt(inst);
int rs = get_rs(inst);
int ra = get_ra(inst);
int rb = get_rb(inst);
u32 inst_sc = 0x44000002;
switch (get_op(inst)) {
case 0:
emulated = EMULATE_FAIL;
if ((kvmppc_get_msr(vcpu) & MSR_LE) &&
(inst == swab32(inst_sc))) {
/*
* This is the byte reversed syscall instruction of our
* hypercall handler. Early versions of LE Linux didn't
* swap the instructions correctly and ended up in
* illegal instructions.
* Just always fail hypercalls on these broken systems.
*/
kvmppc_set_gpr(vcpu, 3, EV_UNIMPLEMENTED);
kvmppc_set_pc(vcpu, kvmppc_get_pc(vcpu) + 4);
emulated = EMULATE_DONE;
}
break;
case 19:
switch (get_xop(inst)) {
case OP_19_XOP_RFID:
case OP_19_XOP_RFI: {
unsigned long srr1 = kvmppc_get_srr1(vcpu);
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
unsigned long cur_msr = kvmppc_get_msr(vcpu);
/*
* add rules to fit in ISA specification regarding TM
* state transistion in TM disable/Suspended state,
* and target TM state is TM inactive(00) state. (the
* change should be suppressed).
*/
if (((cur_msr & MSR_TM) == 0) &&
((srr1 & MSR_TM) == 0) &&
MSR_TM_SUSPENDED(cur_msr) &&
!MSR_TM_ACTIVE(srr1))
srr1 |= MSR_TS_S;
#endif
kvmppc_set_pc(vcpu, kvmppc_get_srr0(vcpu));
kvmppc_set_msr(vcpu, srr1);
*advance = 0;
break;
}
default:
emulated = EMULATE_FAIL;
break;
}
break;
case 31:
switch (get_xop(inst)) {
case OP_31_XOP_MFMSR:
kvmppc_set_gpr(vcpu, rt, kvmppc_get_msr(vcpu));
break;
case OP_31_XOP_MTMSRD:
{
ulong rs_val = kvmppc_get_gpr(vcpu, rs);
if (inst & 0x10000) {
ulong new_msr = kvmppc_get_msr(vcpu);
new_msr &= ~(MSR_RI | MSR_EE);
new_msr |= rs_val & (MSR_RI | MSR_EE);
kvmppc_set_msr_fast(vcpu, new_msr);
} else
kvmppc_set_msr(vcpu, rs_val);
break;
}
case OP_31_XOP_MTMSR:
kvmppc_set_msr(vcpu, kvmppc_get_gpr(vcpu, rs));
break;
case OP_31_XOP_MFSR:
{
int srnum;
srnum = kvmppc_get_field(inst, 12 + 32, 15 + 32);
if (vcpu->arch.mmu.mfsrin) {
u32 sr;
sr = vcpu->arch.mmu.mfsrin(vcpu, srnum);
kvmppc_set_gpr(vcpu, rt, sr);
}
break;
}
case OP_31_XOP_MFSRIN:
{
int srnum;
srnum = (kvmppc_get_gpr(vcpu, rb) >> 28) & 0xf;
if (vcpu->arch.mmu.mfsrin) {
u32 sr;
sr = vcpu->arch.mmu.mfsrin(vcpu, srnum);
kvmppc_set_gpr(vcpu, rt, sr);
}
break;
}
case OP_31_XOP_MTSR:
vcpu->arch.mmu.mtsrin(vcpu,
(inst >> 16) & 0xf,
kvmppc_get_gpr(vcpu, rs));
break;
case OP_31_XOP_MTSRIN:
vcpu->arch.mmu.mtsrin(vcpu,
(kvmppc_get_gpr(vcpu, rb) >> 28) & 0xf,
kvmppc_get_gpr(vcpu, rs));
break;
case OP_31_XOP_TLBIE:
case OP_31_XOP_TLBIEL:
{
bool large = (inst & 0x00200000) ? true : false;
ulong addr = kvmppc_get_gpr(vcpu, rb);
vcpu->arch.mmu.tlbie(vcpu, addr, large);
break;
}
#ifdef CONFIG_PPC_BOOK3S_64
case OP_31_XOP_FAKE_SC1:
{
/* SC 1 papr hypercalls */
ulong cmd = kvmppc_get_gpr(vcpu, 3);
int i;
if ((kvmppc_get_msr(vcpu) & MSR_PR) ||
!vcpu->arch.papr_enabled) {
emulated = EMULATE_FAIL;
break;
}
if (kvmppc_h_pr(vcpu, cmd) == EMULATE_DONE)
break;
vcpu->run->papr_hcall.nr = cmd;
for (i = 0; i < 9; ++i) {
ulong gpr = kvmppc_get_gpr(vcpu, 4 + i);
vcpu->run->papr_hcall.args[i] = gpr;
}
vcpu->run->exit_reason = KVM_EXIT_PAPR_HCALL;
vcpu->arch.hcall_needed = 1;
emulated = EMULATE_EXIT_USER;
break;
}
#endif
case OP_31_XOP_EIOIO:
break;
case OP_31_XOP_SLBMTE:
if (!vcpu->arch.mmu.slbmte)
return EMULATE_FAIL;
vcpu->arch.mmu.slbmte(vcpu,
kvmppc_get_gpr(vcpu, rs),
kvmppc_get_gpr(vcpu, rb));
break;
case OP_31_XOP_SLBIE:
if (!vcpu->arch.mmu.slbie)
return EMULATE_FAIL;
vcpu->arch.mmu.slbie(vcpu,
kvmppc_get_gpr(vcpu, rb));
break;
case OP_31_XOP_SLBIA:
if (!vcpu->arch.mmu.slbia)
return EMULATE_FAIL;
vcpu->arch.mmu.slbia(vcpu);
break;
case OP_31_XOP_SLBFEE:
if (!(inst & 1) || !vcpu->arch.mmu.slbfee) {
return EMULATE_FAIL;
} else {
ulong b, t;
ulong cr = kvmppc_get_cr(vcpu) & ~CR0_MASK;
b = kvmppc_get_gpr(vcpu, rb);
if (!vcpu->arch.mmu.slbfee(vcpu, b, &t))
cr |= 2 << CR0_SHIFT;
kvmppc_set_gpr(vcpu, rt, t);
/* copy XER[SO] bit to CR0[SO] */
cr |= (vcpu->arch.regs.xer & 0x80000000) >>
(31 - CR0_SHIFT);
kvmppc_set_cr(vcpu, cr);
}
break;
case OP_31_XOP_SLBMFEE:
if (!vcpu->arch.mmu.slbmfee) {
emulated = EMULATE_FAIL;
} else {
ulong t, rb_val;
rb_val = kvmppc_get_gpr(vcpu, rb);
t = vcpu->arch.mmu.slbmfee(vcpu, rb_val);
kvmppc_set_gpr(vcpu, rt, t);
}
break;
case OP_31_XOP_SLBMFEV:
if (!vcpu->arch.mmu.slbmfev) {
emulated = EMULATE_FAIL;
} else {
ulong t, rb_val;
rb_val = kvmppc_get_gpr(vcpu, rb);
t = vcpu->arch.mmu.slbmfev(vcpu, rb_val);
kvmppc_set_gpr(vcpu, rt, t);
}
break;
case OP_31_XOP_DCBA:
/* Gets treated as NOP */
break;
case OP_31_XOP_DCBZ:
{
ulong rb_val = kvmppc_get_gpr(vcpu, rb);
ulong ra_val = 0;
ulong addr, vaddr;
u32 zeros[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
u32 dsisr;
int r;
if (ra)
ra_val = kvmppc_get_gpr(vcpu, ra);
addr = (ra_val + rb_val) & ~31ULL;
if (!(kvmppc_get_msr(vcpu) & MSR_SF))
addr &= 0xffffffff;
vaddr = addr;
r = kvmppc_st(vcpu, &addr, 32, zeros, true);
if ((r == -ENOENT) || (r == -EPERM)) {
*advance = 0;
kvmppc_set_dar(vcpu, vaddr);
vcpu->arch.fault_dar = vaddr;
dsisr = DSISR_ISSTORE;
if (r == -ENOENT)
dsisr |= DSISR_NOHPTE;
else if (r == -EPERM)
dsisr |= DSISR_PROTFAULT;
kvmppc_set_dsisr(vcpu, dsisr);
vcpu->arch.fault_dsisr = dsisr;
kvmppc_book3s_queue_irqprio(vcpu,
BOOK3S_INTERRUPT_DATA_STORAGE);
}
break;
}
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
case OP_31_XOP_TBEGIN:
{
if (!cpu_has_feature(CPU_FTR_TM))
break;
if (!(kvmppc_get_msr(vcpu) & MSR_TM)) {
kvmppc_trigger_fac_interrupt(vcpu, FSCR_TM_LG);
emulated = EMULATE_AGAIN;
break;
}
if (!(kvmppc_get_msr(vcpu) & MSR_PR)) {
preempt_disable();
vcpu->arch.regs.ccr = (CR0_TBEGIN_FAILURE |
(vcpu->arch.regs.ccr & ~(CR0_MASK << CR0_SHIFT)));
vcpu->arch.texasr = (TEXASR_FS | TEXASR_EXACT |
(((u64)(TM_CAUSE_EMULATE | TM_CAUSE_PERSISTENT))
<< TEXASR_FC_LG));
if ((inst >> 21) & 0x1)
vcpu->arch.texasr |= TEXASR_ROT;
if (kvmppc_get_msr(vcpu) & MSR_HV)
vcpu->arch.texasr |= TEXASR_HV;
vcpu->arch.tfhar = kvmppc_get_pc(vcpu) + 4;
vcpu->arch.tfiar = kvmppc_get_pc(vcpu);
kvmppc_restore_tm_sprs(vcpu);
preempt_enable();
} else
emulated = EMULATE_FAIL;
break;
}
case OP_31_XOP_TABORT:
{
ulong guest_msr = kvmppc_get_msr(vcpu);
unsigned long ra_val = 0;
if (!cpu_has_feature(CPU_FTR_TM))
break;
if (!(kvmppc_get_msr(vcpu) & MSR_TM)) {
kvmppc_trigger_fac_interrupt(vcpu, FSCR_TM_LG);
emulated = EMULATE_AGAIN;
break;
}
/* only emulate for privilege guest, since problem state
* guest can run with TM enabled and we don't expect to
* trap at here for that case.
*/
WARN_ON(guest_msr & MSR_PR);
if (ra)
ra_val = kvmppc_get_gpr(vcpu, ra);
kvmppc_emulate_tabort(vcpu, ra_val);
break;
}
case OP_31_XOP_TRECLAIM:
{
ulong guest_msr = kvmppc_get_msr(vcpu);
unsigned long ra_val = 0;
if (!cpu_has_feature(CPU_FTR_TM))
break;
if (!(kvmppc_get_msr(vcpu) & MSR_TM)) {
kvmppc_trigger_fac_interrupt(vcpu, FSCR_TM_LG);
emulated = EMULATE_AGAIN;
break;
}
/* generate interrupts based on priorities */
if (guest_msr & MSR_PR) {
/* Privileged Instruction type Program Interrupt */
kvmppc_core_queue_program(vcpu, SRR1_PROGPRIV);
emulated = EMULATE_AGAIN;
break;
}
if (!MSR_TM_ACTIVE(guest_msr)) {
/* TM bad thing interrupt */
kvmppc_core_queue_program(vcpu, SRR1_PROGTM);
emulated = EMULATE_AGAIN;
break;
}
if (ra)
ra_val = kvmppc_get_gpr(vcpu, ra);
kvmppc_emulate_treclaim(vcpu, ra_val);
break;
}
case OP_31_XOP_TRCHKPT:
{
ulong guest_msr = kvmppc_get_msr(vcpu);
unsigned long texasr;
if (!cpu_has_feature(CPU_FTR_TM))
break;
if (!(kvmppc_get_msr(vcpu) & MSR_TM)) {
kvmppc_trigger_fac_interrupt(vcpu, FSCR_TM_LG);
emulated = EMULATE_AGAIN;
break;
}
/* generate interrupt based on priorities */
if (guest_msr & MSR_PR) {
/* Privileged Instruction type Program Intr */
kvmppc_core_queue_program(vcpu, SRR1_PROGPRIV);
emulated = EMULATE_AGAIN;
break;
}
tm_enable();
texasr = mfspr(SPRN_TEXASR);
tm_disable();
if (MSR_TM_ACTIVE(guest_msr) ||
!(texasr & (TEXASR_FS))) {
/* TM bad thing interrupt */
kvmppc_core_queue_program(vcpu, SRR1_PROGTM);
emulated = EMULATE_AGAIN;
break;
}
kvmppc_emulate_trchkpt(vcpu);
break;
}
#endif
default:
emulated = EMULATE_FAIL;
}
break;
default:
emulated = EMULATE_FAIL;
}
if (emulated == EMULATE_FAIL)
emulated = kvmppc_emulate_paired_single(vcpu);
return emulated;
}