diff options
| -rw-r--r-- | arch/x86/kvm/emulate.c | 14 | ||||
| -rw-r--r-- | arch/x86/kvm/paging_tmpl.h | 20 | ||||
| -rw-r--r-- | arch/x86/kvm/vmx.c | 13 | ||||
| -rw-r--r-- | include/linux/kvm_host.h | 1 | ||||
| -rw-r--r-- | virt/kvm/async_pf.c | 5 | ||||
| -rw-r--r-- | virt/kvm/kvm_main.c | 14 |
6 files changed, 59 insertions, 8 deletions
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 2bc1e81045b0..ddc3f3d2afdb 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c | |||
| @@ -2025,6 +2025,17 @@ static int em_ret_far(struct x86_emulate_ctxt *ctxt) | |||
| 2025 | return rc; | 2025 | return rc; |
| 2026 | } | 2026 | } |
| 2027 | 2027 | ||
| 2028 | static int em_ret_far_imm(struct x86_emulate_ctxt *ctxt) | ||
| 2029 | { | ||
| 2030 | int rc; | ||
| 2031 | |||
| 2032 | rc = em_ret_far(ctxt); | ||
| 2033 | if (rc != X86EMUL_CONTINUE) | ||
| 2034 | return rc; | ||
| 2035 | rsp_increment(ctxt, ctxt->src.val); | ||
| 2036 | return X86EMUL_CONTINUE; | ||
| 2037 | } | ||
| 2038 | |||
| 2028 | static int em_cmpxchg(struct x86_emulate_ctxt *ctxt) | 2039 | static int em_cmpxchg(struct x86_emulate_ctxt *ctxt) |
| 2029 | { | 2040 | { |
| 2030 | /* Save real source value, then compare EAX against destination. */ | 2041 | /* Save real source value, then compare EAX against destination. */ |
| @@ -3763,7 +3774,8 @@ static const struct opcode opcode_table[256] = { | |||
| 3763 | G(ByteOp, group11), G(0, group11), | 3774 | G(ByteOp, group11), G(0, group11), |
| 3764 | /* 0xC8 - 0xCF */ | 3775 | /* 0xC8 - 0xCF */ |
| 3765 | I(Stack | SrcImmU16 | Src2ImmByte, em_enter), I(Stack, em_leave), | 3776 | I(Stack | SrcImmU16 | Src2ImmByte, em_enter), I(Stack, em_leave), |
| 3766 | N, I(ImplicitOps | Stack, em_ret_far), | 3777 | I(ImplicitOps | Stack | SrcImmU16, em_ret_far_imm), |
| 3778 | I(ImplicitOps | Stack, em_ret_far), | ||
| 3767 | D(ImplicitOps), DI(SrcImmByte, intn), | 3779 | D(ImplicitOps), DI(SrcImmByte, intn), |
| 3768 | D(ImplicitOps | No64), II(ImplicitOps, em_iret, iret), | 3780 | D(ImplicitOps | No64), II(ImplicitOps, em_iret, iret), |
| 3769 | /* 0xD0 - 0xD7 */ | 3781 | /* 0xD0 - 0xD7 */ |
diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h index 043330159179..ad75d77999d0 100644 --- a/arch/x86/kvm/paging_tmpl.h +++ b/arch/x86/kvm/paging_tmpl.h | |||
| @@ -99,6 +99,7 @@ struct guest_walker { | |||
| 99 | pt_element_t prefetch_ptes[PTE_PREFETCH_NUM]; | 99 | pt_element_t prefetch_ptes[PTE_PREFETCH_NUM]; |
| 100 | gpa_t pte_gpa[PT_MAX_FULL_LEVELS]; | 100 | gpa_t pte_gpa[PT_MAX_FULL_LEVELS]; |
| 101 | pt_element_t __user *ptep_user[PT_MAX_FULL_LEVELS]; | 101 | pt_element_t __user *ptep_user[PT_MAX_FULL_LEVELS]; |
| 102 | bool pte_writable[PT_MAX_FULL_LEVELS]; | ||
| 102 | unsigned pt_access; | 103 | unsigned pt_access; |
| 103 | unsigned pte_access; | 104 | unsigned pte_access; |
| 104 | gfn_t gfn; | 105 | gfn_t gfn; |
| @@ -235,6 +236,22 @@ static int FNAME(update_accessed_dirty_bits)(struct kvm_vcpu *vcpu, | |||
| 235 | if (pte == orig_pte) | 236 | if (pte == orig_pte) |
| 236 | continue; | 237 | continue; |
| 237 | 238 | ||
| 239 | /* | ||
| 240 | * If the slot is read-only, simply do not process the accessed | ||
| 241 | * and dirty bits. This is the correct thing to do if the slot | ||
| 242 | * is ROM, and page tables in read-as-ROM/write-as-MMIO slots | ||
| 243 | * are only supported if the accessed and dirty bits are already | ||
| 244 | * set in the ROM (so that MMIO writes are never needed). | ||
| 245 | * | ||
| 246 | * Note that NPT does not allow this at all and faults, since | ||
| 247 | * it always wants nested page table entries for the guest | ||
| 248 | * page tables to be writable. And EPT works but will simply | ||
| 249 | * overwrite the read-only memory to set the accessed and dirty | ||
| 250 | * bits. | ||
| 251 | */ | ||
| 252 | if (unlikely(!walker->pte_writable[level - 1])) | ||
| 253 | continue; | ||
| 254 | |||
| 238 | ret = FNAME(cmpxchg_gpte)(vcpu, mmu, ptep_user, index, orig_pte, pte); | 255 | ret = FNAME(cmpxchg_gpte)(vcpu, mmu, ptep_user, index, orig_pte, pte); |
| 239 | if (ret) | 256 | if (ret) |
| 240 | return ret; | 257 | return ret; |
| @@ -309,7 +326,8 @@ retry_walk: | |||
| 309 | goto error; | 326 | goto error; |
| 310 | real_gfn = gpa_to_gfn(real_gfn); | 327 | real_gfn = gpa_to_gfn(real_gfn); |
| 311 | 328 | ||
| 312 | host_addr = gfn_to_hva(vcpu->kvm, real_gfn); | 329 | host_addr = gfn_to_hva_prot(vcpu->kvm, real_gfn, |
| 330 | &walker->pte_writable[walker->level - 1]); | ||
| 313 | if (unlikely(kvm_is_error_hva(host_addr))) | 331 | if (unlikely(kvm_is_error_hva(host_addr))) |
| 314 | goto error; | 332 | goto error; |
| 315 | 333 | ||
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 1f1da43ff2a2..a1216de9ffda 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c | |||
| @@ -5339,6 +5339,15 @@ static int handle_ept_violation(struct kvm_vcpu *vcpu) | |||
| 5339 | return 0; | 5339 | return 0; |
| 5340 | } | 5340 | } |
| 5341 | 5341 | ||
| 5342 | /* | ||
| 5343 | * EPT violation happened while executing iret from NMI, | ||
| 5344 | * "blocked by NMI" bit has to be set before next VM entry. | ||
| 5345 | * There are errata that may cause this bit to not be set: | ||
| 5346 | * AAK134, BY25. | ||
| 5347 | */ | ||
| 5348 | if (exit_qualification & INTR_INFO_UNBLOCK_NMI) | ||
| 5349 | vmcs_set_bits(GUEST_INTERRUPTIBILITY_INFO, GUEST_INTR_STATE_NMI); | ||
| 5350 | |||
| 5342 | gpa = vmcs_read64(GUEST_PHYSICAL_ADDRESS); | 5351 | gpa = vmcs_read64(GUEST_PHYSICAL_ADDRESS); |
| 5343 | trace_kvm_page_fault(gpa, exit_qualification); | 5352 | trace_kvm_page_fault(gpa, exit_qualification); |
| 5344 | 5353 | ||
| @@ -7766,6 +7775,10 @@ static void prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12) | |||
| 7766 | vmcs_write64(GUEST_PDPTR1, vmcs12->guest_pdptr1); | 7775 | vmcs_write64(GUEST_PDPTR1, vmcs12->guest_pdptr1); |
| 7767 | vmcs_write64(GUEST_PDPTR2, vmcs12->guest_pdptr2); | 7776 | vmcs_write64(GUEST_PDPTR2, vmcs12->guest_pdptr2); |
| 7768 | vmcs_write64(GUEST_PDPTR3, vmcs12->guest_pdptr3); | 7777 | vmcs_write64(GUEST_PDPTR3, vmcs12->guest_pdptr3); |
| 7778 | __clear_bit(VCPU_EXREG_PDPTR, | ||
| 7779 | (unsigned long *)&vcpu->arch.regs_avail); | ||
| 7780 | __clear_bit(VCPU_EXREG_PDPTR, | ||
| 7781 | (unsigned long *)&vcpu->arch.regs_dirty); | ||
| 7769 | } | 7782 | } |
| 7770 | 7783 | ||
| 7771 | kvm_register_write(vcpu, VCPU_REGS_RSP, vmcs12->guest_rsp); | 7784 | kvm_register_write(vcpu, VCPU_REGS_RSP, vmcs12->guest_rsp); |
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index ca645a01d37a..0fbbc7aa02cb 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h | |||
| @@ -533,6 +533,7 @@ int gfn_to_page_many_atomic(struct kvm *kvm, gfn_t gfn, struct page **pages, | |||
| 533 | 533 | ||
| 534 | struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn); | 534 | struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn); |
| 535 | unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn); | 535 | unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn); |
| 536 | unsigned long gfn_to_hva_prot(struct kvm *kvm, gfn_t gfn, bool *writable); | ||
| 536 | unsigned long gfn_to_hva_memslot(struct kvm_memory_slot *slot, gfn_t gfn); | 537 | unsigned long gfn_to_hva_memslot(struct kvm_memory_slot *slot, gfn_t gfn); |
| 537 | void kvm_release_page_clean(struct page *page); | 538 | void kvm_release_page_clean(struct page *page); |
| 538 | void kvm_release_page_dirty(struct page *page); | 539 | void kvm_release_page_dirty(struct page *page); |
diff --git a/virt/kvm/async_pf.c b/virt/kvm/async_pf.c index ea475cd03511..8a39dda7a325 100644 --- a/virt/kvm/async_pf.c +++ b/virt/kvm/async_pf.c | |||
| @@ -101,8 +101,11 @@ void kvm_clear_async_pf_completion_queue(struct kvm_vcpu *vcpu) | |||
| 101 | typeof(*work), queue); | 101 | typeof(*work), queue); |
| 102 | cancel_work_sync(&work->work); | 102 | cancel_work_sync(&work->work); |
| 103 | list_del(&work->queue); | 103 | list_del(&work->queue); |
| 104 | if (!work->done) /* work was canceled */ | 104 | if (!work->done) { /* work was canceled */ |
| 105 | mmdrop(work->mm); | ||
| 106 | kvm_put_kvm(vcpu->kvm); /* == work->vcpu->kvm */ | ||
| 105 | kmem_cache_free(async_pf_cache, work); | 107 | kmem_cache_free(async_pf_cache, work); |
| 108 | } | ||
| 106 | } | 109 | } |
| 107 | 110 | ||
| 108 | spin_lock(&vcpu->async_pf.lock); | 111 | spin_lock(&vcpu->async_pf.lock); |
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index bf040c4e02b3..979bff485fb0 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c | |||
| @@ -1058,11 +1058,15 @@ unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn) | |||
| 1058 | EXPORT_SYMBOL_GPL(gfn_to_hva); | 1058 | EXPORT_SYMBOL_GPL(gfn_to_hva); |
| 1059 | 1059 | ||
| 1060 | /* | 1060 | /* |
| 1061 | * The hva returned by this function is only allowed to be read. | 1061 | * If writable is set to false, the hva returned by this function is only |
| 1062 | * It should pair with kvm_read_hva() or kvm_read_hva_atomic(). | 1062 | * allowed to be read. |
| 1063 | */ | 1063 | */ |
| 1064 | static unsigned long gfn_to_hva_read(struct kvm *kvm, gfn_t gfn) | 1064 | unsigned long gfn_to_hva_prot(struct kvm *kvm, gfn_t gfn, bool *writable) |
| 1065 | { | 1065 | { |
| 1066 | struct kvm_memory_slot *slot = gfn_to_memslot(kvm, gfn); | ||
| 1067 | if (writable) | ||
| 1068 | *writable = !memslot_is_readonly(slot); | ||
| 1069 | |||
| 1066 | return __gfn_to_hva_many(gfn_to_memslot(kvm, gfn), gfn, NULL, false); | 1070 | return __gfn_to_hva_many(gfn_to_memslot(kvm, gfn), gfn, NULL, false); |
| 1067 | } | 1071 | } |
| 1068 | 1072 | ||
| @@ -1430,7 +1434,7 @@ int kvm_read_guest_page(struct kvm *kvm, gfn_t gfn, void *data, int offset, | |||
| 1430 | int r; | 1434 | int r; |
| 1431 | unsigned long addr; | 1435 | unsigned long addr; |
| 1432 | 1436 | ||
| 1433 | addr = gfn_to_hva_read(kvm, gfn); | 1437 | addr = gfn_to_hva_prot(kvm, gfn, NULL); |
| 1434 | if (kvm_is_error_hva(addr)) | 1438 | if (kvm_is_error_hva(addr)) |
| 1435 | return -EFAULT; | 1439 | return -EFAULT; |
| 1436 | r = kvm_read_hva(data, (void __user *)addr + offset, len); | 1440 | r = kvm_read_hva(data, (void __user *)addr + offset, len); |
| @@ -1468,7 +1472,7 @@ int kvm_read_guest_atomic(struct kvm *kvm, gpa_t gpa, void *data, | |||
| 1468 | gfn_t gfn = gpa >> PAGE_SHIFT; | 1472 | gfn_t gfn = gpa >> PAGE_SHIFT; |
| 1469 | int offset = offset_in_page(gpa); | 1473 | int offset = offset_in_page(gpa); |
| 1470 | 1474 | ||
| 1471 | addr = gfn_to_hva_read(kvm, gfn); | 1475 | addr = gfn_to_hva_prot(kvm, gfn, NULL); |
| 1472 | if (kvm_is_error_hva(addr)) | 1476 | if (kvm_is_error_hva(addr)) |
| 1473 | return -EFAULT; | 1477 | return -EFAULT; |
| 1474 | pagefault_disable(); | 1478 | pagefault_disable(); |
