From: Quentin Perret <qper...@google.com>

Transferring ownership information of a memory region from one component
to another can be achieved using a "donate" operation, which results
in the previous owner losing access to the underlying pages entirely.

Implement a do_donate() helper, along the same lines as do_{un,}share,
and provide this functionality for the host-{to,from}-hyp cases as this
will later be used to donate/reclaim memory pages to store VM metadata
at EL2.

Signed-off-by: Quentin Perret <qper...@google.com>
---
 arch/arm64/kvm/hyp/include/nvhe/mem_protect.h |   2 +
 arch/arm64/kvm/hyp/nvhe/mem_protect.c         | 239 ++++++++++++++++++
 2 files changed, 241 insertions(+)

diff --git a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h 
b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
index f5705a1e972f..c87b19b2d468 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h
@@ -60,6 +60,8 @@ enum pkvm_component_id {
 int __pkvm_prot_finalize(void);
 int __pkvm_host_share_hyp(u64 pfn);
 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);
 
 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 ff86f5bd230f..c30402737548 100644
--- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c
+++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
@@ -391,6 +391,9 @@ struct pkvm_mem_transition {
                                /* Address in the completer's address space */
                                u64     completer_addr;
                        } host;
+                       struct {
+                               u64     completer_addr;
+                       } hyp;
                };
        } initiator;
 
@@ -404,6 +407,10 @@ struct pkvm_mem_share {
        const enum kvm_pgtable_prot             completer_prot;
 };
 
+struct pkvm_mem_donation {
+       const struct pkvm_mem_transition        tx;
+};
+
 struct check_walk_data {
        enum pkvm_page_state    desired;
        enum pkvm_page_state    (*get_page_state)(kvm_pte_t pte);
@@ -503,6 +510,46 @@ static int host_initiate_unshare(u64 *completer_addr,
        return __host_set_page_state_range(addr, size, PKVM_PAGE_OWNED);
 }
 
+static int host_initiate_donation(u64 *completer_addr,
+                                 const struct pkvm_mem_transition *tx)
+{
+       u8 owner_id = tx->completer.id;
+       u64 size = tx->nr_pages * PAGE_SIZE;
+
+       *completer_addr = tx->initiator.host.completer_addr;
+       return host_stage2_set_owner_locked(tx->initiator.addr, size, owner_id);
+}
+
+static bool __host_ack_skip_pgtable_check(const struct pkvm_mem_transition *tx)
+{
+       return !(IS_ENABLED(CONFIG_NVHE_EL2_DEBUG) ||
+                tx->initiator.id != PKVM_ID_HYP);
+}
+
+static int __host_ack_transition(u64 addr, const struct pkvm_mem_transition 
*tx,
+                                enum pkvm_page_state state)
+{
+       u64 size = tx->nr_pages * PAGE_SIZE;
+
+       if (__host_ack_skip_pgtable_check(tx))
+               return 0;
+
+       return __host_check_page_state_range(addr, size, state);
+}
+
+static int host_ack_donation(u64 addr, const struct pkvm_mem_transition *tx)
+{
+       return __host_ack_transition(addr, tx, PKVM_NOPAGE);
+}
+
+static int host_complete_donation(u64 addr, const struct pkvm_mem_transition 
*tx)
+{
+       u64 size = tx->nr_pages * PAGE_SIZE;
+       u8 host_id = tx->completer.id;
+
+       return host_stage2_set_owner_locked(addr, size, host_id);
+}
+
 static enum pkvm_page_state hyp_get_page_state(kvm_pte_t pte)
 {
        if (!kvm_pte_valid(pte))
@@ -523,6 +570,27 @@ static int __hyp_check_page_state_range(u64 addr, u64 size,
        return check_page_state_range(&pkvm_pgtable, addr, size, &d);
 }
 
+static int hyp_request_donation(u64 *completer_addr,
+                               const struct pkvm_mem_transition *tx)
+{
+       u64 size = tx->nr_pages * PAGE_SIZE;
+       u64 addr = tx->initiator.addr;
+
+       *completer_addr = tx->initiator.hyp.completer_addr;
+       return __hyp_check_page_state_range(addr, size, PKVM_PAGE_OWNED);
+}
+
+static int hyp_initiate_donation(u64 *completer_addr,
+                                const struct pkvm_mem_transition *tx)
+{
+       u64 size = tx->nr_pages * PAGE_SIZE;
+       int ret;
+
+       *completer_addr = tx->initiator.hyp.completer_addr;
+       ret = kvm_pgtable_hyp_unmap(&pkvm_pgtable, tx->initiator.addr, size);
+       return (ret != size) ? -EFAULT : 0;
+}
+
 static bool __hyp_ack_skip_pgtable_check(const struct pkvm_mem_transition *tx)
 {
        return !(IS_ENABLED(CONFIG_NVHE_EL2_DEBUG) ||
@@ -554,6 +622,16 @@ static int hyp_ack_unshare(u64 addr, const struct 
pkvm_mem_transition *tx)
                                            PKVM_PAGE_SHARED_BORROWED);
 }
 
+static int hyp_ack_donation(u64 addr, const struct pkvm_mem_transition *tx)
+{
+       u64 size = tx->nr_pages * PAGE_SIZE;
+
+       if (__hyp_ack_skip_pgtable_check(tx))
+               return 0;
+
+       return __hyp_check_page_state_range(addr, size, PKVM_NOPAGE);
+}
+
 static int hyp_complete_share(u64 addr, const struct pkvm_mem_transition *tx,
                              enum kvm_pgtable_prot perms)
 {
@@ -572,6 +650,15 @@ static int hyp_complete_unshare(u64 addr, const struct 
pkvm_mem_transition *tx)
        return (ret != size) ? -EFAULT : 0;
 }
 
+static int hyp_complete_donation(u64 addr,
+                                const struct pkvm_mem_transition *tx)
+{
+       void *start = (void *)addr, *end = start + (tx->nr_pages * PAGE_SIZE);
+       enum kvm_pgtable_prot prot = pkvm_mkstate(PAGE_HYP, PKVM_PAGE_OWNED);
+
+       return pkvm_create_mappings_locked(start, end, prot);
+}
+
 static int check_share(struct pkvm_mem_share *share)
 {
        const struct pkvm_mem_transition *tx = &share->tx;
@@ -724,6 +811,94 @@ static int do_unshare(struct pkvm_mem_share *share)
        return WARN_ON(__do_unshare(share));
 }
 
+static int check_donation(struct pkvm_mem_donation *donation)
+{
+       const struct pkvm_mem_transition *tx = &donation->tx;
+       u64 completer_addr;
+       int ret;
+
+       switch (tx->initiator.id) {
+       case PKVM_ID_HOST:
+               ret = host_request_owned_transition(&completer_addr, tx);
+               break;
+       case PKVM_ID_HYP:
+               ret = hyp_request_donation(&completer_addr, tx);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       if (ret)
+               return ret;
+
+       switch (tx->completer.id){
+       case PKVM_ID_HOST:
+               ret = host_ack_donation(completer_addr, tx);
+               break;
+       case PKVM_ID_HYP:
+               ret = hyp_ack_donation(completer_addr, tx);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static int __do_donate(struct pkvm_mem_donation *donation)
+{
+       const struct pkvm_mem_transition *tx = &donation->tx;
+       u64 completer_addr;
+       int ret;
+
+       switch (tx->initiator.id) {
+       case PKVM_ID_HOST:
+               ret = host_initiate_donation(&completer_addr, tx);
+               break;
+       case PKVM_ID_HYP:
+               ret = hyp_initiate_donation(&completer_addr, tx);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       if (ret)
+               return ret;
+
+       switch (tx->completer.id){
+       case PKVM_ID_HOST:
+               ret = host_complete_donation(completer_addr, tx);
+               break;
+       case PKVM_ID_HYP:
+               ret = hyp_complete_donation(completer_addr, tx);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+/*
+ * do_donate():
+ *
+ * The page owner transfers ownership to another component, losing access
+ * as a consequence.
+ *
+ * Initiator: OWNED    => NOPAGE
+ * Completer: NOPAGE   => OWNED
+ */
+static int do_donate(struct pkvm_mem_donation *donation)
+{
+       int ret;
+
+       ret = check_donation(donation);
+       if (ret)
+               return ret;
+
+       return WARN_ON(__do_donate(donation));
+}
+
 int __pkvm_host_share_hyp(u64 pfn)
 {
        int ret;
@@ -789,3 +964,67 @@ int __pkvm_host_unshare_hyp(u64 pfn)
 
        return ret;
 }
+
+int __pkvm_host_donate_hyp(u64 pfn, u64 nr_pages)
+{
+       int ret;
+       u64 host_addr = hyp_pfn_to_phys(pfn);
+       u64 hyp_addr = (u64)__hyp_va(host_addr);
+       struct pkvm_mem_donation donation = {
+               .tx     = {
+                       .nr_pages       = nr_pages,
+                       .initiator      = {
+                               .id     = PKVM_ID_HOST,
+                               .addr   = host_addr,
+                               .host   = {
+                                       .completer_addr = hyp_addr,
+                               },
+                       },
+                       .completer      = {
+                               .id     = PKVM_ID_HYP,
+                       },
+               },
+       };
+
+       host_lock_component();
+       hyp_lock_component();
+
+       ret = do_donate(&donation);
+
+       hyp_unlock_component();
+       host_unlock_component();
+
+       return ret;
+}
+
+int __pkvm_hyp_donate_host(u64 pfn, u64 nr_pages)
+{
+       int ret;
+       u64 host_addr = hyp_pfn_to_phys(pfn);
+       u64 hyp_addr = (u64)__hyp_va(host_addr);
+       struct pkvm_mem_donation donation = {
+               .tx     = {
+                       .nr_pages       = nr_pages,
+                       .initiator      = {
+                               .id     = PKVM_ID_HYP,
+                               .addr   = hyp_addr,
+                               .hyp    = {
+                                       .completer_addr = host_addr,
+                               },
+                       },
+                       .completer      = {
+                               .id     = PKVM_ID_HOST,
+                       },
+               },
+       };
+
+       host_lock_component();
+       hyp_lock_component();
+
+       ret = do_donate(&donation);
+
+       hyp_unlock_component();
+       host_unlock_component();
+
+       return ret;
+}
-- 
2.36.1.124.g0e6072fb45-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

Reply via email to