diff options
author | Rusty Russell <rusty@rustcorp.com.au> | 2007-07-24 23:29:51 -0400 |
---|---|---|
committer | Avi Kivity <avi@qumranet.com> | 2007-10-13 04:18:20 -0400 |
commit | c820c2aa27bb5b6069aa708b0a0b44b59a16bfa7 (patch) | |
tree | 4fbf8adca97cafb1f52968fed36963fb36c4254d /drivers/kvm/kvm_main.c | |
parent | 3ccb8827fb3bd389ed15320da83543d016a94822 (diff) |
KVM: load_pdptrs() cleanups
load_pdptrs can be handed an invalid cr3, and it should not oops.
This can happen because we injected #gp in set_cr3() after we set
vcpu->cr3 to the invalid value, or from kvm_vcpu_ioctl_set_sregs(), or
memory configuration changes after the guest did set_cr3().
We should also copy the pdpte array once, before checking and
assigning, otherwise an SMP guest can potentially alter the values
between the check and the set.
Finally one nitpick: ret = 1 should be done as late as possible: this
allows GCC to check for unset "ret" should the function change in
future.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Avi Kivity <avi@qumranet.com>
Diffstat (limited to 'drivers/kvm/kvm_main.c')
-rw-r--r-- | drivers/kvm/kvm_main.c | 22 |
1 files changed, 12 insertions, 10 deletions
diff --git a/drivers/kvm/kvm_main.c b/drivers/kvm/kvm_main.c index 80ee427754d2..65c9a31f1d91 100644 --- a/drivers/kvm/kvm_main.c +++ b/drivers/kvm/kvm_main.c | |||
@@ -442,30 +442,32 @@ static int load_pdptrs(struct kvm_vcpu *vcpu, unsigned long cr3) | |||
442 | gfn_t pdpt_gfn = cr3 >> PAGE_SHIFT; | 442 | gfn_t pdpt_gfn = cr3 >> PAGE_SHIFT; |
443 | unsigned offset = ((cr3 & (PAGE_SIZE-1)) >> 5) << 2; | 443 | unsigned offset = ((cr3 & (PAGE_SIZE-1)) >> 5) << 2; |
444 | int i; | 444 | int i; |
445 | u64 pdpte; | ||
446 | u64 *pdpt; | 445 | u64 *pdpt; |
447 | int ret; | 446 | int ret; |
448 | struct page *page; | 447 | struct page *page; |
448 | u64 pdpte[ARRAY_SIZE(vcpu->pdptrs)]; | ||
449 | 449 | ||
450 | spin_lock(&vcpu->kvm->lock); | 450 | spin_lock(&vcpu->kvm->lock); |
451 | page = gfn_to_page(vcpu->kvm, pdpt_gfn); | 451 | page = gfn_to_page(vcpu->kvm, pdpt_gfn); |
452 | /* FIXME: !page - emulate? 0xff? */ | 452 | if (!page) { |
453 | ret = 0; | ||
454 | goto out; | ||
455 | } | ||
456 | |||
453 | pdpt = kmap_atomic(page, KM_USER0); | 457 | pdpt = kmap_atomic(page, KM_USER0); |
458 | memcpy(pdpte, pdpt+offset, sizeof(pdpte)); | ||
459 | kunmap_atomic(pdpt, KM_USER0); | ||
454 | 460 | ||
455 | ret = 1; | 461 | for (i = 0; i < ARRAY_SIZE(pdpte); ++i) { |
456 | for (i = 0; i < 4; ++i) { | 462 | if ((pdpte[i] & 1) && (pdpte[i] & 0xfffffff0000001e6ull)) { |
457 | pdpte = pdpt[offset + i]; | ||
458 | if ((pdpte & 1) && (pdpte & 0xfffffff0000001e6ull)) { | ||
459 | ret = 0; | 463 | ret = 0; |
460 | goto out; | 464 | goto out; |
461 | } | 465 | } |
462 | } | 466 | } |
467 | ret = 1; | ||
463 | 468 | ||
464 | for (i = 0; i < 4; ++i) | 469 | memcpy(vcpu->pdptrs, pdpte, sizeof(vcpu->pdptrs)); |
465 | vcpu->pdptrs[i] = pdpt[offset + i]; | ||
466 | |||
467 | out: | 470 | out: |
468 | kunmap_atomic(pdpt, KM_USER0); | ||
469 | spin_unlock(&vcpu->kvm->lock); | 471 | spin_unlock(&vcpu->kvm->lock); |
470 | 472 | ||
471 | return ret; | 473 | return ret; |