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 <qperret@google.com>
Bug: 209580772
Change-Id: Id11bd6a86754b6a3e0c504b06940df310641357d
Signed-off-by: Will Deacon <willdeacon@google.com>
This commit is contained in:
Quentin Perret
2021-10-26 14:40:24 +01:00
committed by Will Deacon
parent 7b2e541a63
commit ab2c31fe1d

View File

@@ -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. */