diff options
| author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2013-04-17 16:31:15 -0400 |
|---|---|---|
| committer | Alexander Graf <agraf@suse.de> | 2013-04-26 14:27:32 -0400 |
| commit | e7d26f285b4be9466c9e393139e1c9cffe4cedfc (patch) | |
| tree | 0afc30678671f87be82992d306cdec3b984bc6dd | |
| parent | 54695c3088a74e25474db8eb6b490b45d1aeb0ca (diff) | |
KVM: PPC: Book3S HV: Add support for real mode ICP in XICS emulation
This adds an implementation of the XICS hypercalls in real mode for HV
KVM, which allows us to avoid exiting the guest MMU context on all
threads for a variety of operations such as fetching a pending
interrupt, EOI of messages, IPIs, etc.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: Alexander Graf <agraf@suse.de>
| -rw-r--r-- | arch/powerpc/kvm/Makefile | 5 | ||||
| -rw-r--r-- | arch/powerpc/kvm/book3s_hv_rm_xics.c | 406 | ||||
| -rw-r--r-- | arch/powerpc/kvm/book3s_hv_rmhandlers.S | 18 | ||||
| -rw-r--r-- | arch/powerpc/kvm/book3s_xics.c | 64 | ||||
| -rw-r--r-- | arch/powerpc/kvm/book3s_xics.h | 16 |
5 files changed, 490 insertions, 19 deletions
diff --git a/arch/powerpc/kvm/Makefile b/arch/powerpc/kvm/Makefile index f9b87b540450..422de3f4d46c 100644 --- a/arch/powerpc/kvm/Makefile +++ b/arch/powerpc/kvm/Makefile | |||
| @@ -72,12 +72,15 @@ kvm-book3s_64-objs-$(CONFIG_KVM_BOOK3S_64_HV) := \ | |||
| 72 | book3s_hv.o \ | 72 | book3s_hv.o \ |
| 73 | book3s_hv_interrupts.o \ | 73 | book3s_hv_interrupts.o \ |
| 74 | book3s_64_mmu_hv.o | 74 | book3s_64_mmu_hv.o |
| 75 | kvm-book3s_64-builtin-xics-objs-$(CONFIG_KVM_XICS) := \ | ||
| 76 | book3s_hv_rm_xics.o | ||
| 75 | kvm-book3s_64-builtin-objs-$(CONFIG_KVM_BOOK3S_64_HV) := \ | 77 | kvm-book3s_64-builtin-objs-$(CONFIG_KVM_BOOK3S_64_HV) := \ |
| 76 | book3s_hv_rmhandlers.o \ | 78 | book3s_hv_rmhandlers.o \ |
| 77 | book3s_hv_rm_mmu.o \ | 79 | book3s_hv_rm_mmu.o \ |
| 78 | book3s_64_vio_hv.o \ | 80 | book3s_64_vio_hv.o \ |
| 79 | book3s_hv_ras.o \ | 81 | book3s_hv_ras.o \ |
| 80 | book3s_hv_builtin.o | 82 | book3s_hv_builtin.o \ |
| 83 | $(kvm-book3s_64-builtin-xics-objs-y) | ||
| 81 | 84 | ||
| 82 | kvm-book3s_64-objs-$(CONFIG_KVM_XICS) += \ | 85 | kvm-book3s_64-objs-$(CONFIG_KVM_XICS) += \ |
| 83 | book3s_xics.o | 86 | book3s_xics.o |
diff --git a/arch/powerpc/kvm/book3s_hv_rm_xics.c b/arch/powerpc/kvm/book3s_hv_rm_xics.c new file mode 100644 index 000000000000..b4b0082f761c --- /dev/null +++ b/arch/powerpc/kvm/book3s_hv_rm_xics.c | |||
| @@ -0,0 +1,406 @@ | |||
| 1 | /* | ||
| 2 | * Copyright 2012 Michael Ellerman, IBM Corporation. | ||
| 3 | * Copyright 2012 Benjamin Herrenschmidt, IBM Corporation | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License, version 2, as | ||
| 7 | * published by the Free Software Foundation. | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include <linux/kernel.h> | ||
| 11 | #include <linux/kvm_host.h> | ||
| 12 | #include <linux/err.h> | ||
| 13 | |||
| 14 | #include <asm/kvm_book3s.h> | ||
| 15 | #include <asm/kvm_ppc.h> | ||
| 16 | #include <asm/hvcall.h> | ||
| 17 | #include <asm/xics.h> | ||
| 18 | #include <asm/debug.h> | ||
| 19 | #include <asm/synch.h> | ||
| 20 | #include <asm/ppc-opcode.h> | ||
| 21 | |||
| 22 | #include "book3s_xics.h" | ||
| 23 | |||
| 24 | #define DEBUG_PASSUP | ||
| 25 | |||
| 26 | static inline void rm_writeb(unsigned long paddr, u8 val) | ||
| 27 | { | ||
| 28 | __asm__ __volatile__("sync; stbcix %0,0,%1" | ||
| 29 | : : "r" (val), "r" (paddr) : "memory"); | ||
| 30 | } | ||
| 31 | |||
| 32 | static void icp_rm_set_vcpu_irq(struct kvm_vcpu *vcpu, | ||
| 33 | struct kvm_vcpu *this_vcpu) | ||
| 34 | { | ||
| 35 | struct kvmppc_icp *this_icp = this_vcpu->arch.icp; | ||
| 36 | unsigned long xics_phys; | ||
| 37 | int cpu; | ||
| 38 | |||
| 39 | /* Mark the target VCPU as having an interrupt pending */ | ||
| 40 | vcpu->stat.queue_intr++; | ||
| 41 | set_bit(BOOK3S_IRQPRIO_EXTERNAL_LEVEL, &vcpu->arch.pending_exceptions); | ||
| 42 | |||
| 43 | /* Kick self ? Just set MER and return */ | ||
| 44 | if (vcpu == this_vcpu) { | ||
| 45 | mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) | LPCR_MER); | ||
| 46 | return; | ||
| 47 | } | ||
| 48 | |||
| 49 | /* Check if the core is loaded, if not, too hard */ | ||
| 50 | cpu = vcpu->cpu; | ||
| 51 | if (cpu < 0 || cpu >= nr_cpu_ids) { | ||
| 52 | this_icp->rm_action |= XICS_RM_KICK_VCPU; | ||
| 53 | this_icp->rm_kick_target = vcpu; | ||
| 54 | return; | ||
| 55 | } | ||
| 56 | /* In SMT cpu will always point to thread 0, we adjust it */ | ||
| 57 | cpu += vcpu->arch.ptid; | ||
| 58 | |||
| 59 | /* Not too hard, then poke the target */ | ||
| 60 | xics_phys = paca[cpu].kvm_hstate.xics_phys; | ||
| 61 | rm_writeb(xics_phys + XICS_MFRR, IPI_PRIORITY); | ||
| 62 | } | ||
| 63 | |||
| 64 | static void icp_rm_clr_vcpu_irq(struct kvm_vcpu *vcpu) | ||
| 65 | { | ||
| 66 | /* Note: Only called on self ! */ | ||
| 67 | clear_bit(BOOK3S_IRQPRIO_EXTERNAL_LEVEL, | ||
| 68 | &vcpu->arch.pending_exceptions); | ||
| 69 | mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) & ~LPCR_MER); | ||
| 70 | } | ||
| 71 | |||
| 72 | static inline bool icp_rm_try_update(struct kvmppc_icp *icp, | ||
| 73 | union kvmppc_icp_state old, | ||
| 74 | union kvmppc_icp_state new) | ||
| 75 | { | ||
| 76 | struct kvm_vcpu *this_vcpu = local_paca->kvm_hstate.kvm_vcpu; | ||
| 77 | bool success; | ||
| 78 | |||
| 79 | /* Calculate new output value */ | ||
| 80 | new.out_ee = (new.xisr && (new.pending_pri < new.cppr)); | ||
| 81 | |||
| 82 | /* Attempt atomic update */ | ||
| 83 | success = cmpxchg64(&icp->state.raw, old.raw, new.raw) == old.raw; | ||
| 84 | if (!success) | ||
| 85 | goto bail; | ||
| 86 | |||
| 87 | /* | ||
| 88 | * Check for output state update | ||
| 89 | * | ||
| 90 | * Note that this is racy since another processor could be updating | ||
| 91 | * the state already. This is why we never clear the interrupt output | ||
| 92 | * here, we only ever set it. The clear only happens prior to doing | ||
| 93 | * an update and only by the processor itself. Currently we do it | ||
| 94 | * in Accept (H_XIRR) and Up_Cppr (H_XPPR). | ||
| 95 | * | ||
| 96 | * We also do not try to figure out whether the EE state has changed, | ||
| 97 | * we unconditionally set it if the new state calls for it. The reason | ||
| 98 | * for that is that we opportunistically remove the pending interrupt | ||
| 99 | * flag when raising CPPR, so we need to set it back here if an | ||
| 100 | * interrupt is still pending. | ||
| 101 | */ | ||
| 102 | if (new.out_ee) | ||
| 103 | icp_rm_set_vcpu_irq(icp->vcpu, this_vcpu); | ||
| 104 | |||
| 105 | /* Expose the state change for debug purposes */ | ||
| 106 | this_vcpu->arch.icp->rm_dbgstate = new; | ||
| 107 | this_vcpu->arch.icp->rm_dbgtgt = icp->vcpu; | ||
| 108 | |||
| 109 | bail: | ||
| 110 | return success; | ||
| 111 | } | ||
| 112 | |||
| 113 | static inline int check_too_hard(struct kvmppc_xics *xics, | ||
| 114 | struct kvmppc_icp *icp) | ||
| 115 | { | ||
| 116 | return (xics->real_mode_dbg || icp->rm_action) ? H_TOO_HARD : H_SUCCESS; | ||
| 117 | } | ||
| 118 | |||
| 119 | static void icp_rm_down_cppr(struct kvmppc_xics *xics, struct kvmppc_icp *icp, | ||
| 120 | u8 new_cppr) | ||
| 121 | { | ||
| 122 | union kvmppc_icp_state old_state, new_state; | ||
| 123 | bool resend; | ||
| 124 | |||
| 125 | /* | ||
| 126 | * This handles several related states in one operation: | ||
| 127 | * | ||
| 128 | * ICP State: Down_CPPR | ||
| 129 | * | ||
| 130 | * Load CPPR with new value and if the XISR is 0 | ||
| 131 | * then check for resends: | ||
| 132 | * | ||
| 133 | * ICP State: Resend | ||
| 134 | * | ||
| 135 | * If MFRR is more favored than CPPR, check for IPIs | ||
| 136 | * and notify ICS of a potential resend. This is done | ||
| 137 | * asynchronously (when used in real mode, we will have | ||
| 138 | * to exit here). | ||
| 139 | * | ||
| 140 | * We do not handle the complete Check_IPI as documented | ||
| 141 | * here. In the PAPR, this state will be used for both | ||
| 142 | * Set_MFRR and Down_CPPR. However, we know that we aren't | ||
| 143 | * changing the MFRR state here so we don't need to handle | ||
| 144 | * the case of an MFRR causing a reject of a pending irq, | ||
| 145 | * this will have been handled when the MFRR was set in the | ||
| 146 | * first place. | ||
| 147 | * | ||
| 148 | * Thus we don't have to handle rejects, only resends. | ||
| 149 | * | ||
| 150 | * When implementing real mode for HV KVM, resend will lead to | ||
| 151 | * a H_TOO_HARD return and the whole transaction will be handled | ||
| 152 | * in virtual mode. | ||
| 153 | */ | ||
| 154 | do { | ||
| 155 | old_state = new_state = ACCESS_ONCE(icp->state); | ||
| 156 | |||
| 157 | /* Down_CPPR */ | ||
| 158 | new_state.cppr = new_cppr; | ||
| 159 | |||
| 160 | /* | ||
