aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Mackerras <paulus@samba.org>2012-02-19 12:46:32 -0500
committerAvi Kivity <avi@redhat.com>2012-04-08 07:01:27 -0400
commit2e25aa5f64b18a97f35266e51c71ff4dc644db0c (patch)
tree7b26cf15534d54bc2c370f1e5393cd9e58eb7141
parentf0888f70151c7f53de2b45ee20ff1905837943e8 (diff)
KVM: PPC: Book3S HV: Make virtual processor area registration more robust
The PAPR API allows three sorts of per-virtual-processor areas to be registered (VPA, SLB shadow buffer, and dispatch trace log), and furthermore, these can be registered and unregistered for another virtual CPU. Currently we just update the vcpu fields pointing to these areas at the time of registration or unregistration. If this is done on another vcpu, there is the possibility that the target vcpu is using those fields at the time and could end up using a bogus pointer and corrupting memory. This fixes the race by making the target cpu itself do the update, so we can be sure that the update happens at a time when the fields aren't being used. Each area now has a struct kvmppc_vpa which is used to manage these updates. There is also a spinlock which protects access to all of the kvmppc_vpa structs, other than to the pinned_addr fields. (We could have just taken the spinlock when using the vpa, slb_shadow or dtl fields, but that would mean taking the spinlock on every guest entry and exit.) This also changes 'struct dtl' (which was undefined) to 'struct dtl_entry', which is what the rest of the kernel uses. Thanks to Michael Ellerman <michael@ellerman.id.au> for pointing out the need to initialize vcpu->arch.vpa_update_lock. Signed-off-by: Paul Mackerras <paulus@samba.org> Signed-off-by: Alexander Graf <agraf@suse.de> Signed-off-by: Avi Kivity <avi@redhat.com>
-rw-r--r--arch/powerpc/include/asm/hvcall.h10
-rw-r--r--arch/powerpc/include/asm/kvm_host.h27
-rw-r--r--arch/powerpc/kernel/asm-offsets.c2
-rw-r--r--arch/powerpc/kvm/book3s_hv.c227
4 files changed, 190 insertions, 76 deletions
diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h
index 1c324ff55ea8..318bac9f8752 100644
--- a/arch/powerpc/include/asm/hvcall.h
+++ b/arch/powerpc/include/asm/hvcall.h
@@ -114,6 +114,16 @@
114#define H_PP1 (1UL<<(63-62)) 114#define H_PP1 (1UL<<(63-62))
115#define H_PP2 (1UL<<(63-63)) 115#define H_PP2 (1UL<<(63-63))
116 116
117/* Flags for H_REGISTER_VPA subfunction field */
118#define H_VPA_FUNC_SHIFT (63-18) /* Bit posn of subfunction code */
119#define H_VPA_FUNC_MASK 7UL
120#define H_VPA_REG_VPA 1UL /* Register Virtual Processor Area */
121#define H_VPA_REG_DTL 2UL /* Register Dispatch Trace Log */
122#define H_VPA_REG_SLB 3UL /* Register SLB shadow buffer */
123#define H_VPA_DEREG_VPA 5UL /* Deregister Virtual Processor Area */
124#define H_VPA_DEREG_DTL 6UL /* Deregister Dispatch Trace Log */
125#define H_VPA_DEREG_SLB 7UL /* Deregister SLB shadow buffer */
126
117/* VASI States */ 127/* VASI States */
118#define H_VASI_INVALID 0 128#define H_VASI_INVALID 0
119#define H_VASI_ENABLED 1 129#define H_VASI_ENABLED 1
diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
index 97ecdaf82956..93ffd8dd8554 100644
--- a/arch/powerpc/include/asm/kvm_host.h
+++ b/arch/powerpc/include/asm/kvm_host.h
@@ -82,7 +82,7 @@ struct kvm_vcpu;
82 82
83struct lppaca; 83struct lppaca;
84struct slb_shadow; 84struct slb_shadow;
85struct dtl; 85struct dtl_entry;
86 86
87struct kvm_vm_stat { 87struct kvm_vm_stat {
88 u32 remote_tlb_flush; 88 u32 remote_tlb_flush;
@@ -279,6 +279,19 @@ struct kvmppc_vcore {
279#define VCORE_EXITING 2 279#define VCORE_EXITING 2
280#define VCORE_SLEEPING 3 280#define VCORE_SLEEPING 3
281 281
282/*
283 * Struct used to manage memory for a virtual processor area
284 * registered by a PAPR guest. There are three types of area
285 * that a guest can register.
286 */
287struct kvmppc_vpa {
288 void *pinned_addr; /* Address in kernel linear mapping */
289 void *pinned_end; /* End of region */
290 unsigned long next_gpa; /* Guest phys addr for update */
291 unsigned long len; /* Number of bytes required */
292 u8 update_pending; /* 1 => update pinned_addr from next_gpa */
293};
294
282struct kvmppc_pte { 295struct kvmppc_pte {
283 ulong eaddr; 296 ulong eaddr;
284 u64 vpage; 297 u64 vpage;
@@ -473,11 +486,6 @@ struct kvm_vcpu_arch {
473 u8 prodded; 486 u8 prodded;
474 u32 last_inst; 487 u32 last_inst;
475 488
476 struct lppaca *vpa;
477 struct slb_shadow *slb_shadow;
478 struct dtl *dtl;
479 struct dtl *dtl_end;
480
481 wait_queue_head_t *wqp; 489 wait_queue_head_t *wqp;
482 struct kvmppc_vcore *vcore; 490 struct kvmppc_vcore *vcore;
483 int ret; 491 int ret;
@@ -502,6 +510,13 @@ struct kvm_vcpu_arch {
502 struct task_struct *run_task; 510 struct task_struct *run_task;
503 struct kvm_run *kvm_run; 511 struct kvm_run *kvm_run;
504 pgd_t *pgdir; 512 pgd_t *pgdir;
513
514 spinlock_t vpa_update_lock;
515 struct kvmppc_vpa vpa;
516 struct kvmppc_vpa dtl;
517 struct dtl_entry *dtl_ptr;
518 unsigned long dtl_index;
519 struct kvmppc_vpa slb_shadow;
505#endif 520#endif
506}; 521};
507 522
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index 2abcf7d4b29c..502e038f8c8a 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -466,7 +466,7 @@ int main(void)
466 DEFINE(VCPU_PENDING_EXC, offsetof(struct kvm_vcpu, arch.pending_exceptions)); 466 DEFINE(VCPU_PENDING_EXC, offsetof(struct kvm_vcpu, arch.pending_exceptions));
467 DEFINE(VCPU_CEDED, offsetof(struct kvm_vcpu, arch.ceded)); 467 DEFINE(VCPU_CEDED, offsetof(struct kvm_vcpu, arch.ceded));
468 DEFINE(VCPU_PRODDED, offsetof(struct kvm_vcpu, arch.prodded)); 468 DEFINE(VCPU_PRODDED, offsetof(struct kvm_vcpu, arch.prodded));
469 DEFINE(VCPU_VPA, offsetof(struct kvm_vcpu, arch.vpa)); 469 DEFINE(VCPU_VPA, offsetof(struct kvm_vcpu, arch.vpa.pinned_addr));
470 DEFINE(VCPU_MMCR, offsetof(struct kvm_vcpu, arch.mmcr)); 470 DEFINE(VCPU_MMCR, offsetof(struct kvm_vcpu, arch.mmcr));
471 DEFINE(VCPU_PMC, offsetof(struct kvm_vcpu, arch.pmc)); 471 DEFINE(VCPU_PMC, offsetof(struct kvm_vcpu, arch.pmc));
472 DEFINE(VCPU_SLB, offsetof(struct kvm_vcpu, arch.slb)); 472 DEFINE(VCPU_SLB, offsetof(struct kvm_vcpu, arch.slb));
diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
index e87f6196d222..2444a9ce781f 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -134,6 +134,22 @@ static void init_vpa(struct kvm_vcpu *vcpu, struct lppaca *vpa)
134 vpa->yield_count = 1; 134 vpa->yield_count = 1;
135} 135}
136 136
137/* Length for a per-processor buffer is passed in at offset 4 in the buffer */
138struct reg_vpa {
139 u32 dummy;
140 union {
141 u16 hword;
142 u32 word;
143 } length;
144};
145
146static int vpa_is_registered(struct kvmppc_vpa *vpap)
147{
148 if (vpap->update_pending)
149 return vpap->next_gpa != 0;
150 return vpap->pinned_addr != NULL;
151}
152
137static unsigned long do_h_register_vpa(struct kvm_vcpu *vcpu, 153static unsigned long do_h_register_vpa(struct kvm_vcpu *vcpu,
138 unsigned long flags, 154 unsigned long flags,
139 unsigned long vcpuid, unsigned long vpa) 155 unsigned long vcpuid, unsigned long vpa)
@@ -142,88 +158,153 @@ static unsigned long do_h_register_vpa(struct kvm_vcpu *vcpu,
142 unsigned long len, nb; 158 unsigned long len, nb;
143 void *va; 159 void *va;
144 struct kvm_vcpu *tvcpu; 160 struct kvm_vcpu *tvcpu;
145 int err = H_PARAMETER; 161 int err;
162 int subfunc;
163 struct kvmppc_vpa *vpap;
146 164
147 tvcpu = kvmppc_find_vcpu(kvm, vcpuid); 165 tvcpu = kvmppc_find_vcpu(kvm, vcpuid);
148 if (!tvcpu) 166 if (!tvcpu)
149 return H_PARAMETER; 167 return H_PARAMETER;
150 168
151 flags >>= 63 - 18; 169 subfunc = (flags >> H_VPA_FUNC_SHIFT) & H_VPA_FUNC_MASK;
152 flags &= 7; 170 if (subfunc == H_VPA_REG_VPA || subfunc == H_VPA_REG_DTL ||
153 if (flags == 0 || flags == 4) 171 subfunc == H_VPA_REG_SLB) {
154 return H_PARAMETER; 172 /* Registering new area - address must be cache-line aligned */
155 if (flags < 4) { 173 if ((vpa & (L1_CACHE_BYTES - 1)) || !vpa)
156 if (vpa & 0x7f)
157 return H_PARAMETER; 174 return H_PARAMETER;
158 if (flags >= 2 && !tvcpu->arch.vpa) 175
159 return H_RESOURCE; 176 /* convert logical addr to kernel addr and read length */
160 /* registering new area; convert logical addr to real */
161 va = kvmppc_pin_guest_page(kvm, vpa, &nb); 177 va = kvmppc_pin_guest_page(kvm, vpa, &nb);
162 if (va == NULL) 178 if (va == NULL)
163 return H_PARAMETER; 179 return H_PARAMETER;
164 if (flags <= 1) 180 if (subfunc == H_VPA_REG_VPA)
165 len = *(unsigned short *)(va + 4); 181 len = ((struct reg_vpa *)va)->length.hword;
166 else 182 else
167 len = *(unsigned int *)(va + 4); 183 len = ((struct reg_vpa *)va)->length.word;
168 if (len > nb) 184 kvmppc_unpin_guest_page(kvm, va);
169 goto out_unpin; 185
170 switch (flags) { 186 /* Check length */
171 case 1: /* register VPA */ 187 if (len > nb || len < sizeof(struct reg_vpa))
172 if (len < 640) 188 return H_PARAMETER;
173