KVM: PPC: e500mc: Add doorbell emulation support
authorAlexander Graf <agraf@suse.de>
Wed, 15 Feb 2012 13:28:48 +0000 (13:28 +0000)
committerAvi Kivity <avi@redhat.com>
Sun, 8 Apr 2012 09:54:50 +0000 (12:54 +0300)
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>
arch/powerpc/include/asm/dbell.h
arch/powerpc/kvm/booke.c
arch/powerpc/kvm/e500_emulate.c

index d7365b01f0c4b18c33024bb6c3f41cfdd3087604..154c067761b15bbf50af6bf099bed0c997ff542e 100644 (file)
@@ -19,7 +19,9 @@
 
 #define PPC_DBELL_MSG_BRDCAST  (0x04000000)
 #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_PIR_MASK     0x3fff
 enum ppc_dbell {
        PPC_DBELL = 0,          /* doorbell */
        PPC_DBELL_CRIT = 1,     /* critical doorbell */
index 0b77be187cf7740cc1cdff20f57bd1eee2cca5fc..85bd5b8f3fd5d237729ee4c7400aa5e240794cc7 100644 (file)
@@ -326,6 +326,7 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu,
                int_class = INT_CLASS_NONCRIT;
                break;
        case BOOKE_IRQPRIO_CRITICAL:
+       case BOOKE_IRQPRIO_DBELL_CRIT:
                allowed = vcpu->arch.shared->msr & MSR_CE;
                allowed = allowed && !crit;
                msr_mask = MSR_GS | MSR_ME;
@@ -342,6 +343,7 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu,
                keep_irq = true;
                /* fall through */
        case BOOKE_IRQPRIO_EXTERNAL:
+       case BOOKE_IRQPRIO_DBELL:
                allowed = vcpu->arch.shared->msr & MSR_EE;
                allowed = allowed && !crit;
                msr_mask = MSR_GS | MSR_CE | MSR_ME | MSR_DE;
index 98b6c1cd6b82439b099d841fb25c3b71130a6626..99155f847a6a749da074e37bfa8fcd9b236a748d 100644 (file)
 
 #include <asm/kvm_ppc.h>
 #include <asm/disassemble.h>
+#include <asm/dbell.h>
 
 #include "booke.h"
 #include "e500.h"
 
+#define XOP_MSGSND  206
+#define XOP_MSGCLR  238
 #define XOP_TLBIVAX 786
 #define XOP_TLBSX   914
 #define XOP_TLBRE   946
 #define XOP_TLBWE   978
 #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,
                            unsigned int inst, int *advance)
 {
@@ -36,6 +94,16 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
        case 31:
                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:
                        emulated = kvmppc_e500_emul_tlbre(vcpu);
                        break;