From ab2c31fe1d020392c3211aeca274602b0a2b8840 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Tue, 26 Oct 2021 14:40:24 +0100 Subject: [PATCH] ANDROID: KVM: arm64: Pin host structs for pVMs In nVHE protected mode the hypervisor sometime needs to read or write to host-provided data-structures, such as vcpu structs or the kvm struct. To ensure that the hypervisor can't be tricked by the host into writing to pages it doesn't own, let's pin the host pages containing those data structures during the shadow vm creation. This will ensure those pages remain in a host-shared state for the lifetime of the VM. Signed-off-by: Quentin Perret Bug: 209580772 Change-Id: Id11bd6a86754b6a3e0c504b06940df310641357d Signed-off-by: Will Deacon --- arch/arm64/kvm/hyp/nvhe/pkvm.c | 35 ++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c index 34fc9039110f..1f4d38d67dec 100644 --- a/arch/arm64/kvm/hyp/nvhe/pkvm.c +++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c @@ -312,10 +312,21 @@ static void copy_features(struct kvm_vcpu *shadow_vcpu, struct kvm_vcpu *host_vc allowed_features, KVM_VCPU_MAX_FEATURES); } -static void init_shadow_structs(struct kvm *kvm, struct kvm_shadow_vm *vm, int nr_vcpus) +static void unpin_host_vcpus(struct kvm_shadow_vm *vm) { int i; + for (i = 0; i < vm->created_vcpus; i++) { + struct kvm_vcpu *vcpu = vm->vcpus[i]->arch.pkvm.host_vcpu; + hyp_unpin_shared_mem(vcpu, vcpu + 1); + } +} + +static int init_shadow_structs(struct kvm *kvm, struct kvm_shadow_vm *vm, int nr_vcpus) +{ + int i; + int ret; + /* TODO: initialize the protected MMU. For now, use the host's. */ vm->mmu = &kvm->arch.mmu; vm->host_kvm = kvm; @@ -326,6 +337,12 @@ static void init_shadow_structs(struct kvm *kvm, struct kvm_shadow_vm *vm, int n struct shadow_vcpu_state *shadow_state = &vm->shadow_vcpus[i]; struct kvm_vcpu *shadow_vcpu = &shadow_state->vcpu; + ret = hyp_pin_shared_mem(host_vcpu, host_vcpu + 1); + if (ret) + return -EBUSY; + + vm->created_vcpus++; + shadow_vcpu->kvm = kvm; shadow_vcpu->vcpu_id = host_vcpu->vcpu_id; shadow_vcpu->vcpu_idx = i; @@ -347,9 +364,9 @@ static void init_shadow_structs(struct kvm *kvm, struct kvm_shadow_vm *vm, int n shadow_vcpu->arch.pkvm.shadow_handle = vm->shadow_handle; shadow_vcpu->arch.pkvm.host_vcpu = host_vcpu; shadow_vcpu->arch.pkvm.shadow_vm = vm; - - vm->created_vcpus++; } + + return 0; } static bool exists_shadow(struct kvm *host_kvm) @@ -486,6 +503,10 @@ int __pkvm_init_shadow(struct kvm *kvm, kvm = kern_hyp_va(kvm); + ret = hyp_pin_shared_mem(kvm, kvm + 1); + if (ret) + return ret; + /* Ensure the host has donated enough memory for the shadow structs. */ nr_vcpus = kvm->created_vcpus; ret = check_shadow_size(nr_vcpus, shadow_size); @@ -504,16 +525,20 @@ int __pkvm_init_shadow(struct kvm *kvm, if (ret < 0) goto err_clear_shadow; - init_shadow_structs(kvm, vm, nr_vcpus); + ret = init_shadow_structs(kvm, vm, nr_vcpus); + if (ret < 0) + goto err_clear_shadow; return vm->shadow_handle; err_clear_shadow: + unpin_host_vcpus(vm); /* Clear the donated shadow memory on failure to avoid data leaks. */ memset(vm, 0, shadow_size); WARN_ON(__pkvm_hyp_donate_host(pfn, nr_pages)); err: + hyp_unpin_shared_mem(kvm, kvm + 1); return ret; } @@ -536,6 +561,8 @@ int __pkvm_teardown_shadow(struct kvm *kvm) shadow_size = vm->shadow_area_size; + unpin_host_vcpus(vm); + hyp_unpin_shared_mem(vm->host_kvm, vm->host_kvm + 1); remove_shadow_table(shadow_handle); /* Clear the shadow memory since hyp is releasing it back to host. */