diff options
author | Christoffer Dall <christoffer.dall@linaro.org> | 2014-08-19 06:18:04 -0400 |
---|---|---|
committer | Christoffer Dall <christoffer.dall@linaro.org> | 2014-08-27 16:46:09 -0400 |
commit | 98047888bb9fd57734028c44ec17413ddd623958 (patch) | |
tree | 976048e835e2f88df23b73aad4e0591102ab8c6e | |
parent | 64d831269ccbca1fc6d739a0f3c8aa24afb43a5e (diff) |
arm/arm64: KVM: Support KVM_CAP_READONLY_MEM
When userspace loads code and data in a read-only memory regions, KVM
needs to be able to handle this on arm and arm64. Specifically this is
used when running code directly from a read-only flash device; the
common scenario is a UEFI blob loaded with the -bios option in QEMU.
Note that the MMIO exit on writes to a read-only memory is ABI and can
be used to emulate block-erase style flash devices.
Acked-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
-rw-r--r-- | arch/arm/include/uapi/asm/kvm.h | 1 | ||||
-rw-r--r-- | arch/arm/kvm/arm.c | 1 | ||||
-rw-r--r-- | arch/arm/kvm/mmu.c | 22 | ||||
-rw-r--r-- | arch/arm64/include/uapi/asm/kvm.h | 1 |
4 files changed, 11 insertions, 14 deletions
diff --git a/arch/arm/include/uapi/asm/kvm.h b/arch/arm/include/uapi/asm/kvm.h index e6ebdd3471e5..51257fda254b 100644 --- a/arch/arm/include/uapi/asm/kvm.h +++ b/arch/arm/include/uapi/asm/kvm.h | |||
@@ -25,6 +25,7 @@ | |||
25 | 25 | ||
26 | #define __KVM_HAVE_GUEST_DEBUG | 26 | #define __KVM_HAVE_GUEST_DEBUG |
27 | #define __KVM_HAVE_IRQ_LINE | 27 | #define __KVM_HAVE_IRQ_LINE |
28 | #define __KVM_HAVE_READONLY_MEM | ||
28 | 29 | ||
29 | #define KVM_REG_SIZE(id) \ | 30 | #define KVM_REG_SIZE(id) \ |
30 | (1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT)) | 31 | (1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT)) |
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index 9f788ebac55b..ac306b4dbe1d 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c | |||
@@ -188,6 +188,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) | |||
188 | case KVM_CAP_ONE_REG: | 188 | case KVM_CAP_ONE_REG: |
189 | case KVM_CAP_ARM_PSCI: | 189 | case KVM_CAP_ARM_PSCI: |
190 | case KVM_CAP_ARM_PSCI_0_2: | 190 | case KVM_CAP_ARM_PSCI_0_2: |
191 | case KVM_CAP_READONLY_MEM: | ||
191 | r = 1; | 192 | r = 1; |
192 | break; | 193 | break; |
193 | case KVM_CAP_COALESCED_MMIO: | 194 | case KVM_CAP_COALESCED_MMIO: |
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c index 16e7994bf347..62f5642153f9 100644 --- a/arch/arm/kvm/mmu.c +++ b/arch/arm/kvm/mmu.c | |||
@@ -747,14 +747,13 @@ static bool transparent_hugepage_adjust(pfn_t *pfnp, phys_addr_t *ipap) | |||
747 | } | 747 | } |
748 | 748 | ||
749 | static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, | 749 | static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, |
750 | struct kvm_memory_slot *memslot, | 750 | struct kvm_memory_slot *memslot, unsigned long hva, |
751 | unsigned long fault_status) | 751 | unsigned long fault_status) |
752 | { | 752 | { |
753 | int ret; | 753 | int ret; |
754 | bool write_fault, writable, hugetlb = false, force_pte = false; | 754 | bool write_fault, writable, hugetlb = false, force_pte = false; |
755 | unsigned long mmu_seq; | 755 | unsigned long mmu_seq; |
756 | gfn_t gfn = fault_ipa >> PAGE_SHIFT; | 756 | gfn_t gfn = fault_ipa >> PAGE_SHIFT; |
757 | unsigned long hva = gfn_to_hva(vcpu->kvm, gfn); | ||
758 | struct kvm *kvm = vcpu->kvm; | 757 | struct kvm *kvm = vcpu->kvm; |
759 | struct kvm_mmu_memory_cache *memcache = &vcpu->arch.mmu_page_cache; | 758 | struct kvm_mmu_memory_cache *memcache = &vcpu->arch.mmu_page_cache; |
760 | struct vm_area_struct *vma; | 759 | struct vm_area_struct *vma; |
@@ -863,7 +862,8 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run) | |||
863 | unsigned long fault_status; | 862 | unsigned long fault_status; |
864 | phys_addr_t fault_ipa; | 863 | phys_addr_t fault_ipa; |
865 | struct kvm_memory_slot *memslot; | 864 | struct kvm_memory_slot *memslot; |
866 | bool is_iabt; | 865 | unsigned long hva; |
866 | bool is_iabt, write_fault, writable; | ||
867 | gfn_t gfn; | 867 | gfn_t gfn; |
868 | int ret, idx; | 868 | int ret, idx; |
869 | 869 | ||
@@ -884,7 +884,10 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run) | |||
884 | idx = srcu_read_lock(&vcpu->kvm->srcu); | 884 | idx = srcu_read_lock(&vcpu->kvm->srcu); |
885 | 885 | ||
886 | gfn = fault_ipa >> PAGE_SHIFT; | 886 | gfn = fault_ipa >> PAGE_SHIFT; |
887 | if (!kvm_is_visible_gfn(vcpu->kvm, gfn)) { | 887 | memslot = gfn_to_memslot(vcpu->kvm, gfn); |
888 | hva = gfn_to_hva_memslot_prot(memslot, gfn, &writable); | ||
889 | write_fault = kvm_is_write_fault(kvm_vcpu_get_hsr(vcpu)); | ||
890 | if (kvm_is_error_hva(hva) || (write_fault && !writable)) { | ||
888 | if (is_iabt) { | 891 | if (is_iabt) { |
889 | /* Prefetch Abort on I/O address */ | 892 | /* Prefetch Abort on I/O address */ |
890 | kvm_inject_pabt(vcpu, kvm_vcpu_get_hfar(vcpu)); | 893 | kvm_inject_pabt(vcpu, kvm_vcpu_get_hfar(vcpu)); |
@@ -892,13 +895,6 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run) | |||
892 | goto out_unlock; | 895 | goto out_unlock; |
893 | } | 896 | } |
894 | 897 | ||
895 | if (fault_status != FSC_FAULT) { | ||
896 | kvm_err("Unsupported fault status on io memory: %#lx\n", | ||
897 | fault_status); | ||
898 | ret = -EFAULT; | ||
899 | goto out_unlock; | ||
900 | } | ||
901 | |||
902 | /* | 898 | /* |
903 | * The IPA is reported as [MAX:12], so we need to | 899 | * The IPA is reported as [MAX:12], so we need to |
904 | * complement it with the bottom 12 bits from the | 900 | * complement it with the bottom 12 bits from the |
@@ -910,9 +906,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run) | |||
910 | goto out_unlock; | 906 | goto out_unlock; |
911 | } | 907 | } |
912 | 908 | ||
913 | memslot = gfn_to_memslot(vcpu->kvm, gfn); | 909 | ret = user_mem_abort(vcpu, fault_ipa, memslot, hva, fault_status); |
914 | |||
915 | ret = user_mem_abort(vcpu, fault_ipa, memslot, fault_status); | ||
916 | if (ret == 0) | 910 | if (ret == 0) |
917 | ret = 1; | 911 | ret = 1; |
918 | out_unlock: | 912 | out_unlock: |
diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h index e633ff8cdec8..f4ec5a674d05 100644 --- a/arch/arm64/include/uapi/asm/kvm.h +++ b/arch/arm64/include/uapi/asm/kvm.h | |||
@@ -37,6 +37,7 @@ | |||
37 | 37 | ||
38 | #define __KVM_HAVE_GUEST_DEBUG | 38 | #define __KVM_HAVE_GUEST_DEBUG |
39 | #define __KVM_HAVE_IRQ_LINE | 39 | #define __KVM_HAVE_IRQ_LINE |
40 | #define __KVM_HAVE_READONLY_MEM | ||
40 | 41 | ||
41 | #define KVM_REG_SIZE(id) \ | 42 | #define KVM_REG_SIZE(id) \ |
42 | (1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT)) | 43 | (1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT)) |