Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Alexander Graf and committed by
Avi Kivity
4ab96919 73196cd3

+72
+2
arch/powerpc/include/asm/dbell.h
··· 19 19 20 20 #define PPC_DBELL_MSG_BRDCAST (0x04000000) 21 21 #define PPC_DBELL_TYPE(x) (((x) & 0xf) << (63-36)) 22 + #define PPC_DBELL_TYPE_MASK PPC_DBELL_TYPE(0xf) 22 23 #define PPC_DBELL_LPID(x) ((x) << (63 - 49)) 24 + #define PPC_DBELL_PIR_MASK 0x3fff 23 25 enum ppc_dbell { 24 26 PPC_DBELL = 0, /* doorbell */ 25 27 PPC_DBELL_CRIT = 1, /* critical doorbell */
+2
arch/powerpc/kvm/booke.c
··· 326 326 int_class = INT_CLASS_NONCRIT; 327 327 break; 328 328 case BOOKE_IRQPRIO_CRITICAL: 329 + case BOOKE_IRQPRIO_DBELL_CRIT: 329 330 allowed = vcpu->arch.shared->msr & MSR_CE; 330 331 allowed = allowed && !crit; 331 332 msr_mask = MSR_GS | MSR_ME; ··· 343 342 keep_irq = true; 344 343 /* fall through */ 345 344 case BOOKE_IRQPRIO_EXTERNAL: 345 + case BOOKE_IRQPRIO_DBELL: 346 346 allowed = vcpu->arch.shared->msr & MSR_EE; 347 347 allowed = allowed && !crit; 348 348 msr_mask = MSR_GS | MSR_CE | MSR_ME | MSR_DE;
+68
arch/powerpc/kvm/e500_emulate.c
··· 14 14 15 15 #include <asm/kvm_ppc.h> 16 16 #include <asm/disassemble.h> 17 + #include <asm/dbell.h> 17 18 18 19 #include "booke.h" 19 20 #include "e500.h" 20 21 22 + #define XOP_MSGSND 206 23 + #define XOP_MSGCLR 238 21 24 #define XOP_TLBIVAX 786 22 25 #define XOP_TLBSX 914 23 26 #define XOP_TLBRE 946 24 27 #define XOP_TLBWE 978 25 28 #define XOP_TLBILX 18 29 + 30 + #ifdef CONFIG_KVM_E500MC 31 + static int dbell2prio(ulong param) 32 + { 33 + int msg = param & PPC_DBELL_TYPE_MASK; 34 + int prio = -1; 35 + 36 + switch (msg) { 37 + case PPC_DBELL_TYPE(PPC_DBELL): 38 + prio = BOOKE_IRQPRIO_DBELL; 39 + break; 40 + case PPC_DBELL_TYPE(PPC_DBELL_CRIT): 41 + prio = BOOKE_IRQPRIO_DBELL_CRIT; 42 + break; 43 + default: 44 + break; 45 + } 46 + 47 + return prio; 48 + } 49 + 50 + static int kvmppc_e500_emul_msgclr(struct kvm_vcpu *vcpu, int rb) 51 + { 52 + ulong param = vcpu->arch.gpr[rb]; 53 + int prio = dbell2prio(param); 54 + 55 + if (prio < 0) 56 + return EMULATE_FAIL; 57 + 58 + clear_bit(prio, &vcpu->arch.pending_exceptions); 59 + return EMULATE_DONE; 60 + } 61 + 62 + static int kvmppc_e500_emul_msgsnd(struct kvm_vcpu *vcpu, int rb) 63 + { 64 + ulong param = vcpu->arch.gpr[rb]; 65 + int prio = dbell2prio(rb); 66 + int pir = param & PPC_DBELL_PIR_MASK; 67 + int i; 68 + struct kvm_vcpu *cvcpu; 69 + 70 + if (prio < 0) 71 + return EMULATE_FAIL; 72 + 73 + kvm_for_each_vcpu(i, cvcpu, vcpu->kvm) { 74 + int cpir = cvcpu->arch.shared->pir; 75 + if ((param & PPC_DBELL_MSG_BRDCAST) || (cpir == pir)) { 76 + set_bit(prio, &cvcpu->arch.pending_exceptions); 77 + kvm_vcpu_kick(cvcpu); 78 + } 79 + } 80 + 81 + return EMULATE_DONE; 82 + } 83 + #endif 26 84 27 85 int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu, 28 86 unsigned int inst, int *advance) ··· 93 35 switch (get_op(inst)) { 94 36 case 31: 95 37 switch (get_xop(inst)) { 38 + 39 + #ifdef CONFIG_KVM_E500MC 40 + case XOP_MSGSND: 41 + emulated = kvmppc_e500_emul_msgsnd(vcpu, get_rb(inst)); 42 + break; 43 + 44 + case XOP_MSGCLR: 45 + emulated = kvmppc_e500_emul_msgclr(vcpu, get_rb(inst)); 46 + break; 47 + #endif 96 48 97 49 case XOP_TLBRE: 98 50 emulated = kvmppc_e500_emul_tlbre(vcpu);