ANDROID: KVM: arm64: Donate pages to protected guests

Now that we have all the infrastructure in place to allow guest-to-host
sharing of pages in protected mode, let's stop sharing the pages from
the host on guest memory aborts and switch to a proper donation instead.

Signed-off-by Quentin Perret <qperret@google.com>

Bug: 209580772
Change-Id: Ib37625172e24950cd74913a20bff8ce29a72f45b
Signed-off-by: Will Deacon <willdeacon@google.com>
This commit is contained in:
Quentin Perret
2021-10-26 16:52:07 +01:00
committed by Will Deacon
parent 0b7e337baf
commit f85c26161b
5 changed files with 70 additions and 8 deletions

View File

@@ -64,7 +64,7 @@ enum __kvm_host_smccc_func {
/* Hypercalls available after pKVM finalisation */
__KVM_HOST_SMCCC_FUNC___pkvm_host_share_hyp,
__KVM_HOST_SMCCC_FUNC___pkvm_host_unshare_hyp,
__KVM_HOST_SMCCC_FUNC___pkvm_host_share_guest,
__KVM_HOST_SMCCC_FUNC___pkvm_host_donate_guest,
__KVM_HOST_SMCCC_FUNC___kvm_adjust_pc,
__KVM_HOST_SMCCC_FUNC___kvm_vcpu_run,
__KVM_HOST_SMCCC_FUNC___kvm_flush_vm_context,

View File

@@ -63,6 +63,7 @@ int __pkvm_host_unshare_hyp(u64 pfn);
int __pkvm_host_donate_hyp(u64 pfn, u64 nr_pages);
int __pkvm_hyp_donate_host(u64 pfn, u64 nr_pages);
int __pkvm_host_share_guest(u64 pfn, u64 gfn, struct kvm_vcpu *vcpu);
int __pkvm_host_donate_guest(u64 pfn, u64 gfn, struct kvm_vcpu *vcpu);
int __pkvm_guest_share_host(struct kvm_vcpu *vcpu, u64 ipa);
int __pkvm_guest_unshare_host(struct kvm_vcpu *vcpu, u64 ipa);

View File

@@ -594,7 +594,7 @@ static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt)
cpu_reg(host_ctxt, 1) = ret;
}
static void handle___pkvm_host_share_guest(struct kvm_cpu_context *host_ctxt)
static void handle___pkvm_host_donate_guest(struct kvm_cpu_context *host_ctxt)
{
DECLARE_REG(u64, pfn, host_ctxt, 1);
DECLARE_REG(u64, gfn, host_ctxt, 2);
@@ -616,7 +616,7 @@ static void handle___pkvm_host_share_guest(struct kvm_cpu_context *host_ctxt)
ret = refill_memcache(&state->vcpu->arch.pkvm_memcache, nr_pages,
&vcpu->arch.pkvm_memcache);
if (!ret)
ret = __pkvm_host_share_guest(pfn, gfn, state->vcpu);
ret = __pkvm_host_donate_guest(pfn, gfn, state->vcpu);
out:
cpu_reg(host_ctxt, 1) = ret;
}
@@ -860,7 +860,7 @@ static const hcall_t host_hcall[] = {
HANDLE_FUNC(__pkvm_host_share_hyp),
HANDLE_FUNC(__pkvm_host_unshare_hyp),
HANDLE_FUNC(__pkvm_host_share_guest),
HANDLE_FUNC(__pkvm_host_donate_guest),
HANDLE_FUNC(__kvm_adjust_pc),
HANDLE_FUNC(__kvm_vcpu_run),
HANDLE_FUNC(__kvm_flush_vm_context),

View File

@@ -1034,6 +1034,14 @@ static int guest_ack_share(u64 addr, const struct pkvm_mem_transition *tx,
size, PKVM_NOPAGE);
}
static int guest_ack_donation(u64 addr, const struct pkvm_mem_transition *tx)
{
u64 size = tx->nr_pages * PAGE_SIZE;
return __guest_check_page_state_range(tx->completer.guest.vcpu, addr,
size, PKVM_NOPAGE);
}
static int guest_complete_share(u64 addr, const struct pkvm_mem_transition *tx,
enum kvm_pgtable_prot perms)
{
@@ -1047,6 +1055,17 @@ static int guest_complete_share(u64 addr, const struct pkvm_mem_transition *tx,
prot, &vcpu->arch.pkvm_memcache);
}
static int guest_complete_donation(u64 addr, const struct pkvm_mem_transition *tx)
{
enum kvm_pgtable_prot prot = pkvm_mkstate(KVM_PGTABLE_PROT_RWX, PKVM_PAGE_OWNED);
struct kvm_vcpu *vcpu = tx->completer.guest.vcpu;
struct kvm_shadow_vm *vm = vcpu->arch.pkvm.shadow_vm;
u64 size = tx->nr_pages * PAGE_SIZE;
return kvm_pgtable_stage2_map(&vm->pgt, addr, size, tx->completer.guest.phys,
prot, &vcpu->arch.pkvm_memcache);
}
static int __guest_get_completer_addr(u64 *completer_addr, phys_addr_t phys,
const struct pkvm_mem_transition *tx)
{
@@ -1372,6 +1391,9 @@ static int check_donation(struct pkvm_mem_donation *donation)
case PKVM_ID_HYP:
ret = hyp_ack_donation(completer_addr, tx);
break;
case PKVM_ID_GUEST:
ret = guest_ack_donation(completer_addr, tx);
break;
default:
ret = -EINVAL;
}
@@ -1406,6 +1428,9 @@ static int __do_donate(struct pkvm_mem_donation *donation)
case PKVM_ID_HYP:
ret = hyp_complete_donation(completer_addr, tx);
break;
case PKVM_ID_GUEST:
ret = guest_complete_donation(completer_addr, tx);
break;
default:
ret = -EINVAL;
}
@@ -1706,3 +1731,39 @@ int __pkvm_host_share_guest(u64 pfn, u64 gfn, struct kvm_vcpu *vcpu)
return ret;
}
int __pkvm_host_donate_guest(u64 pfn, u64 gfn, struct kvm_vcpu *vcpu)
{
int ret;
u64 host_addr = hyp_pfn_to_phys(pfn);
u64 guest_addr = hyp_pfn_to_phys(gfn);
struct pkvm_mem_donation donation = {
.tx = {
.nr_pages = 1,
.initiator = {
.id = PKVM_ID_HOST,
.addr = host_addr,
.host = {
.completer_addr = guest_addr,
},
},
.completer = {
.id = PKVM_ID_GUEST,
.guest = {
.vcpu = vcpu,
.phys = host_addr,
},
},
},
};
host_lock_component();
guest_lock_component(vcpu);
ret = do_donate(&donation);
guest_unlock_component(vcpu);
host_unlock_component();
return ret;
}

View File

@@ -1125,17 +1125,17 @@ static int sanitise_mte_tags(struct kvm *kvm, kvm_pfn_t pfn,
return 0;
}
static int pkvm_host_share_guest(u64 pfn, u64 gfn, struct kvm_vcpu *vcpu)
static int pkvm_host_donate_guest(u64 pfn, u64 gfn, struct kvm_vcpu *vcpu)
{
struct arm_smccc_res res;
arm_smccc_1_1_hvc(KVM_HOST_SMCCC_FUNC(__pkvm_host_share_guest),
arm_smccc_1_1_hvc(KVM_HOST_SMCCC_FUNC(__pkvm_host_donate_guest),
pfn, gfn, vcpu, &res);
WARN_ON(res.a0 != SMCCC_RET_SUCCESS);
/*
* Getting -EPERM at this point implies that the pfn has already been
* shared. This should only ever happen when two vCPUs faulted on the
* donated. This should only ever happen when two vCPUs faulted on the
* same page, and the current one lost the race to do the donation.
*/
return (res.a1 == -EPERM) ? -EAGAIN : res.a1;
@@ -1182,7 +1182,7 @@ static int pkvm_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
}
pfn = page_to_pfn(page);
ret = pkvm_host_share_guest(pfn, fault_ipa >> PAGE_SHIFT, vcpu);
ret = pkvm_host_donate_guest(pfn, fault_ipa >> PAGE_SHIFT, vcpu);
if (ret) {
if (ret == -EAGAIN)
ret = 0;