From 0d40de553ecade142fcaa740a865f5e4fcadf008 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 7 Apr 2022 14:00:10 +0100 Subject: [PATCH] ANDROID: KVM: arm64: Extend memory donation to allow host-to-guest transitions In preparation for supporting protected guests, where guest memory defaults to being inaccessible to the host, extend our memory protection mechanisms to support donation of pages from the host to a specific guest. Signed-off-by: Will Deacon Signed-off-by: Will Deacon Bug: 233587962 Change-Id: Ic397b6fd0f7b5f0911ddd8f457e40c6e6689673c Signed-off-by: Quentin Perret --- arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 1 + arch/arm64/kvm/hyp/nvhe/mem_protect.c | 62 +++++++++++++++++++ arch/arm64/kvm/hyp/pgtable.c | 2 +- 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h index a91c3ad6db0b..4d14b4cfb3b7 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h +++ b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h @@ -69,6 +69,7 @@ int __pkvm_host_reclaim_page(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 pkvm_hyp_vcpu *vcpu); +int __pkvm_host_donate_guest(u64 pfn, u64 gfn, struct pkvm_hyp_vcpu *vcpu); bool addr_is_memory(phys_addr_t phys); int host_stage2_idmap_locked(phys_addr_t addr, u64 size, enum kvm_pgtable_prot prot); diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c index 5736f700b451..d73b88c7062e 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -889,6 +889,14 @@ static int guest_ack_share(u64 addr, const struct pkvm_mem_transition *tx, addr, 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.hyp_vcpu, + addr, size, PKVM_NOPAGE); +} + static int guest_complete_share(u64 addr, const struct pkvm_mem_transition *tx, enum kvm_pgtable_prot perms) { @@ -902,6 +910,17 @@ static int guest_complete_share(u64 addr, const struct pkvm_mem_transition *tx, prot, &vcpu->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 pkvm_hyp_vcpu *vcpu = tx->completer.guest.hyp_vcpu; + struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu); + u64 size = tx->nr_pages * PAGE_SIZE; + + return kvm_pgtable_stage2_map(&vm->pgt, addr, size, tx->completer.guest.phys, + prot, &vcpu->vcpu.arch.pkvm_memcache); +} + static int check_share(struct pkvm_mem_share *share) { const struct pkvm_mem_transition *tx = &share->tx; @@ -1087,6 +1106,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; } @@ -1121,6 +1143,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; } @@ -1361,6 +1386,43 @@ int __pkvm_host_share_guest(u64 pfn, u64 gfn, struct pkvm_hyp_vcpu *vcpu) return ret; } +int __pkvm_host_donate_guest(u64 pfn, u64 gfn, struct pkvm_hyp_vcpu *vcpu) +{ + int ret; + u64 host_addr = hyp_pfn_to_phys(pfn); + u64 guest_addr = hyp_pfn_to_phys(gfn); + struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu); + 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 = { + .hyp_vcpu = vcpu, + .phys = host_addr, + }, + }, + }, + }; + + host_lock_component(); + guest_lock_component(vm); + + ret = do_donate(&donation); + + guest_unlock_component(vm); + host_unlock_component(); + + return ret; +} + static int hyp_zero_page(phys_addr_t phys) { void *addr; diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c index 362a3fdf651f..1597b388d12e 100644 --- a/arch/arm64/kvm/hyp/pgtable.c +++ b/arch/arm64/kvm/hyp/pgtable.c @@ -47,7 +47,7 @@ KVM_PTE_LEAF_ATTR_HI_S2_XN) #define KVM_INVALID_PTE_OWNER_MASK GENMASK(9, 2) -#define KVM_MAX_OWNER_ID 1 +#define KVM_MAX_OWNER_ID FIELD_MAX(KVM_INVALID_PTE_OWNER_MASK) struct kvm_pgtable_walk_data { struct kvm_pgtable *pgt;