KVM: PPC: e500mc: Add doorbell emulation support
When one vcpu wants to kick another, it can issue a special IPI instruction called msgsnd. This patch emulates this instruction, its clearing counterpart and the infrastructure required to actually trigger that interrupt inside a guest vcpu. With this patch, SMP guests on e500mc work. Signed-off-by: Alexander Graf <agraf@suse.de> Signed-off-by: Avi Kivity <avi@redhat.com>
This commit is contained in:
parent
73196cd364
commit
4ab969199e
|
@ -19,7 +19,9 @@
|
||||||
|
|
||||||
#define PPC_DBELL_MSG_BRDCAST (0x04000000)
|
#define PPC_DBELL_MSG_BRDCAST (0x04000000)
|
||||||
#define PPC_DBELL_TYPE(x) (((x) & 0xf) << (63-36))
|
#define PPC_DBELL_TYPE(x) (((x) & 0xf) << (63-36))
|
||||||
|
#define PPC_DBELL_TYPE_MASK PPC_DBELL_TYPE(0xf)
|
||||||
#define PPC_DBELL_LPID(x) ((x) << (63 - 49))
|
#define PPC_DBELL_LPID(x) ((x) << (63 - 49))
|
||||||
|
#define PPC_DBELL_PIR_MASK 0x3fff
|
||||||
enum ppc_dbell {
|
enum ppc_dbell {
|
||||||
PPC_DBELL = 0, /* doorbell */
|
PPC_DBELL = 0, /* doorbell */
|
||||||
PPC_DBELL_CRIT = 1, /* critical doorbell */
|
PPC_DBELL_CRIT = 1, /* critical doorbell */
|
||||||
|
|
|
@ -326,6 +326,7 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu,
|
||||||
int_class = INT_CLASS_NONCRIT;
|
int_class = INT_CLASS_NONCRIT;
|
||||||
break;
|
break;
|
||||||
case BOOKE_IRQPRIO_CRITICAL:
|
case BOOKE_IRQPRIO_CRITICAL:
|
||||||
|
case BOOKE_IRQPRIO_DBELL_CRIT:
|
||||||
allowed = vcpu->arch.shared->msr & MSR_CE;
|
allowed = vcpu->arch.shared->msr & MSR_CE;
|
||||||
allowed = allowed && !crit;
|
allowed = allowed && !crit;
|
||||||
msr_mask = MSR_GS | MSR_ME;
|
msr_mask = MSR_GS | MSR_ME;
|
||||||
|
@ -342,6 +343,7 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu,
|
||||||
keep_irq = true;
|
keep_irq = true;
|
||||||
/* fall through */
|
/* fall through */
|
||||||
case BOOKE_IRQPRIO_EXTERNAL:
|
case BOOKE_IRQPRIO_EXTERNAL:
|
||||||
|
case BOOKE_IRQPRIO_DBELL:
|
||||||
allowed = vcpu->arch.shared->msr & MSR_EE;
|
allowed = vcpu->arch.shared->msr & MSR_EE;
|
||||||
allowed = allowed && !crit;
|
allowed = allowed && !crit;
|
||||||
msr_mask = MSR_GS | MSR_CE | MSR_ME | MSR_DE;
|
msr_mask = MSR_GS | MSR_CE | MSR_ME | MSR_DE;
|
||||||
|
|
|
@ -14,16 +14,74 @@
|
||||||
|
|
||||||
#include <asm/kvm_ppc.h>
|
#include <asm/kvm_ppc.h>
|
||||||
#include <asm/disassemble.h>
|
#include <asm/disassemble.h>
|
||||||
|
#include <asm/dbell.h>
|
||||||
|
|
||||||
#include "booke.h"
|
#include "booke.h"
|
||||||
#include "e500.h"
|
#include "e500.h"
|
||||||
|
|
||||||
|
#define XOP_MSGSND 206
|
||||||
|
#define XOP_MSGCLR 238
|
||||||
#define XOP_TLBIVAX 786
|
#define XOP_TLBIVAX 786
|
||||||
#define XOP_TLBSX 914
|
#define XOP_TLBSX 914
|
||||||
#define XOP_TLBRE 946
|
#define XOP_TLBRE 946
|
||||||
#define XOP_TLBWE 978
|
#define XOP_TLBWE 978
|
||||||
#define XOP_TLBILX 18
|
#define XOP_TLBILX 18
|
||||||
|
|
||||||
|
#ifdef CONFIG_KVM_E500MC
|
||||||
|
static int dbell2prio(ulong param)
|
||||||
|
{
|
||||||
|
int msg = param & PPC_DBELL_TYPE_MASK;
|
||||||
|
int prio = -1;
|
||||||
|
|
||||||
|
switch (msg) {
|
||||||
|
case PPC_DBELL_TYPE(PPC_DBELL):
|
||||||
|
prio = BOOKE_IRQPRIO_DBELL;
|
||||||
|
break;
|
||||||
|
case PPC_DBELL_TYPE(PPC_DBELL_CRIT):
|
||||||
|
prio = BOOKE_IRQPRIO_DBELL_CRIT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return prio;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvmppc_e500_emul_msgclr(struct kvm_vcpu *vcpu, int rb)
|
||||||
|
{
|
||||||
|
ulong param = vcpu->arch.gpr[rb];
|
||||||
|
int prio = dbell2prio(param);
|
||||||
|
|
||||||
|
if (prio < 0)
|
||||||
|
return EMULATE_FAIL;
|
||||||
|
|
||||||
|
clear_bit(prio, &vcpu->arch.pending_exceptions);
|
||||||
|
return EMULATE_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvmppc_e500_emul_msgsnd(struct kvm_vcpu *vcpu, int rb)
|
||||||
|
{
|
||||||
|
ulong param = vcpu->arch.gpr[rb];
|
||||||
|
int prio = dbell2prio(rb);
|
||||||
|
int pir = param & PPC_DBELL_PIR_MASK;
|
||||||
|
int i;
|
||||||
|
struct kvm_vcpu *cvcpu;
|
||||||
|
|
||||||
|
if (prio < 0)
|
||||||
|
return EMULATE_FAIL;
|
||||||
|
|
||||||
|
kvm_for_each_vcpu(i, cvcpu, vcpu->kvm) {
|
||||||
|
int cpir = cvcpu->arch.shared->pir;
|
||||||
|
if ((param & PPC_DBELL_MSG_BRDCAST) || (cpir == pir)) {
|
||||||
|
set_bit(prio, &cvcpu->arch.pending_exceptions);
|
||||||
|
kvm_vcpu_kick(cvcpu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EMULATE_DONE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
|
int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
|
||||||
unsigned int inst, int *advance)
|
unsigned int inst, int *advance)
|
||||||
{
|
{
|
||||||
|
@ -36,6 +94,16 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
|
||||||
case 31:
|
case 31:
|
||||||
switch (get_xop(inst)) {
|
switch (get_xop(inst)) {
|
||||||
|
|
||||||
|
#ifdef CONFIG_KVM_E500MC
|
||||||
|
case XOP_MSGSND:
|
||||||
|
emulated = kvmppc_e500_emul_msgsnd(vcpu, get_rb(inst));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case XOP_MSGCLR:
|
||||||
|
emulated = kvmppc_e500_emul_msgclr(vcpu, get_rb(inst));
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
case XOP_TLBRE:
|
case XOP_TLBRE:
|
||||||
emulated = kvmppc_e500_emul_tlbre(vcpu);
|
emulated = kvmppc_e500_emul_tlbre(vcpu);
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in New Issue