| From d4b9e0790aa764c0b01e18d4e8d33e93ba36d51f Mon Sep 17 00:00:00 2001 |
| From: Marc Zyngier <marc.zyngier@arm.com> |
| Date: Thu, 28 Apr 2016 16:16:31 +0100 |
| Subject: arm/arm64: KVM: Enforce Break-Before-Make on Stage-2 page tables |
| |
| From: Marc Zyngier <marc.zyngier@arm.com> |
| |
| commit d4b9e0790aa764c0b01e18d4e8d33e93ba36d51f upstream. |
| |
| The ARM architecture mandates that when changing a page table entry |
| from a valid entry to another valid entry, an invalid entry is first |
| written, TLB invalidated, and only then the new entry being written. |
| |
| The current code doesn't respect this, directly writing the new |
| entry and only then invalidating TLBs. Let's fix it up. |
| |
| Reported-by: Christoffer Dall <christoffer.dall@linaro.org> |
| Signed-off-by: Marc Zyngier <marc.zyngier@arm.com> |
| Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| arch/arm/kvm/mmu.c | 17 +++++++++++------ |
| 1 file changed, 11 insertions(+), 6 deletions(-) |
| |
| --- a/arch/arm/kvm/mmu.c |
| +++ b/arch/arm/kvm/mmu.c |
| @@ -886,11 +886,14 @@ static int stage2_set_pmd_huge(struct kv |
| VM_BUG_ON(pmd_present(*pmd) && pmd_pfn(*pmd) != pmd_pfn(*new_pmd)); |
| |
| old_pmd = *pmd; |
| - kvm_set_pmd(pmd, *new_pmd); |
| - if (pmd_present(old_pmd)) |
| + if (pmd_present(old_pmd)) { |
| + pmd_clear(pmd); |
| kvm_tlb_flush_vmid_ipa(kvm, addr); |
| - else |
| + } else { |
| get_page(virt_to_page(pmd)); |
| + } |
| + |
| + kvm_set_pmd(pmd, *new_pmd); |
| return 0; |
| } |
| |
| @@ -939,12 +942,14 @@ static int stage2_set_pte(struct kvm *kv |
| |
| /* Create 2nd stage page table mapping - Level 3 */ |
| old_pte = *pte; |
| - kvm_set_pte(pte, *new_pte); |
| - if (pte_present(old_pte)) |
| + if (pte_present(old_pte)) { |
| + kvm_set_pte(pte, __pte(0)); |
| kvm_tlb_flush_vmid_ipa(kvm, addr); |
| - else |
| + } else { |
| get_page(virt_to_page(pte)); |
| + } |
| |
| + kvm_set_pte(pte, *new_pte); |
| return 0; |
| } |
| |