Tear down mapped DATA granules and the RTT hierarchy before returning
the backing pages to Xen.

Signed-off-by: Koichiro Den <[email protected]>
---
 xen/arch/arm/cca/state.c       | 151 +++++++++++++++++++++++++++++++++
 xen/arch/arm/include/asm/cca.h |   1 +
 2 files changed, 152 insertions(+)

diff --git a/xen/arch/arm/cca/state.c b/xen/arch/arm/cca/state.c
index e58e261265f3..c85ef56a1297 100644
--- a/xen/arch/arm/cca/state.c
+++ b/xen/arch/arm/cca/state.c
@@ -1,15 +1,19 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 
+#include <xen/errno.h>
 #include <xen/lib.h>
 #include <xen/mm.h>
 #include <xen/sched.h>
 #include <xen/xmalloc.h>
 
 #include <asm/cca.h>
+#include <asm/event.h>
 
 #include "rmi.h"
 #include "sro.h"
 
+#define ARM_CCA_RTT_DATA_UNMAP_BATCH 1024UL
+
 static void arm_cca_reset_domain_state(struct domain *d)
 {
     unsigned int i;
@@ -26,6 +30,7 @@ static void arm_cca_reset_domain_state(struct domain *d)
     d->arch.cca.nr_rtts = 0;
     d->arch.cca.data_pages = NULL;
     d->arch.cca.nr_data_pages = 0;
+    d->arch.cca.relinquish_data_idx = 0;
     d->arch.cca.realm_terminate_done = false;
 
     for ( i = 0; i < ARRAY_SIZE(d->arch.cca.realm_sro_pages); ++i )
@@ -51,6 +56,37 @@ void arm_cca_domain_init(struct domain *d)
     arm_cca_reset_domain_state(d);
 }
 
+static int
+arm_cca_rtt_data_unmap_4k(paddr_t rd,
+                          const struct arm_cca_data_page_record *rec)
+{
+    struct arm_smccc_res res;
+    paddr_t top = rec->ipa + PAGE_SIZE;
+    uint64_t desc;
+    int rc;
+
+    /*
+     * DEN0137 2.0-bet1 marks SINGLE oaddr as SBZ, and TF-RMM does not use it,
+     * so keep X5 zero here.
+     */
+    rc = arm_cca_rmi_rtt_data_unmap(rd, rec->ipa, top,
+                                    ARM_CCA_RMI_ADDR_TYPE_SINGLE, 0, &res);
+    if ( rc != 0 )
+        return rc;
+
+    desc = res.a2;
+
+    if ( res.a1 != top ||
+         arm_cca_rmi_addr_desc_4k_pa(desc) != rec->pa ||
+         arm_cca_rmi_addr_desc_4k_size(desc) != ARM_CCA_RMI_PAGE_L3 ||
+         arm_cca_rmi_addr_desc_4k_count(desc) != 1 ||
+         arm_cca_rmi_addr_desc_4k_state(desc) !=
+         ARM_CCA_RMI_OP_MEM_DELEGATED )
+        return -EIO;
+
+    return 0;
+}
+
 static void arm_cca_domain_free_metadata(struct domain *d)
 {
     xfree(d->arch.cca.data_pages);
@@ -104,6 +140,68 @@ static int arm_cca_rmi_realm_destroy_complete(struct 
domain *d)
     return rc;
 }
 
+static int arm_cca_teardown_data_pages(struct domain *d)
+{
+    unsigned long i;
+    int rc;
+
+    if ( !d->arch.cca.data_pages )
+        return 0;
+
+    for ( i = d->arch.cca.relinquish_data_idx;
+          i < d->arch.cca.nr_data_pages; ++i )
+    {
+        struct arm_cca_data_page_record *rec = &d->arch.cca.data_pages[i];
+
+        if ( rec->pa == INVALID_PADDR )
+            continue;
+
+        if ( rec->ipa != INVALID_PADDR )
+        {
+            if ( d->arch.cca.rd == INVALID_PADDR )
+                return -EIO;
+
+            rc = arm_cca_rtt_data_unmap_4k(d->arch.cca.rd, rec);
+            if ( rc != 0 )
+            {
+                d->arch.cca.relinquish_data_idx = i;
+                return rc;
+            }
+
+            /*
+             * The RTTE no longer owns this DATA granule.  Keep rec->pa so a
+             * later retry can finish the host-side undelegation.
+             */
+            rec->ipa = INVALID_PADDR;
+        }
+
+        rc = arm_cca_undelegate_granule(rec->pa);
+        if ( rc != 0 )
+        {
+            d->arch.cca.relinquish_data_idx = i;
+            return rc;
+        }
+
+        /*
+         * The page remains owned by the domain and on d->page_list so the
+         * generic relinquish path can free it later.
+         */
+        rec->pa = INVALID_PADDR;
+
+        if ( ((i + 1) % ARM_CCA_RTT_DATA_UNMAP_BATCH) == 0 &&
+             hypercall_preempt_check() )
+        {
+            d->arch.cca.relinquish_data_idx = i + 1;
+            return -ERESTART;
+        }
+    }
+
+    d->arch.cca.nr_data_pages = 0;
+    d->arch.cca.relinquish_data_idx = 0;
+
+    return 0;
+}
+
 static int arm_cca_terminate_realm(struct domain *d)
 {
     struct arm_smccc_res res;
@@ -126,6 +224,51 @@ static int arm_cca_terminate_realm(struct domain *d)
     return 0;
 }
 
+static int arm_cca_teardown_rtts(struct domain *d)
+{
+    struct arm_smccc_res res;
+    int rc;
+
+    while ( d->arch.cca.nr_rtts != 0 )
+    {
+        struct arm_cca_rtt_record *rec =
+            &d->arch.cca.rtts[d->arch.cca.nr_rtts - 1];
+
+        if ( rec->pa == INVALID_PADDR )
+        {
+            d->arch.cca.nr_rtts--;
+            continue;
+        }
+
+        if ( rec->ipa != INVALID_PADDR )
+        {
+            if ( d->arch.cca.rd == INVALID_PADDR )
+                return -EIO;
+
+            rc = arm_cca_rmi_rtt_destroy(d->arch.cca.rd, rec->ipa,
+                                         rec->level, &res);
+            if ( rc != 0 )
+                return rc;
+
+            /*
+             * The RMM object is gone.  Keep rec->pa so a later retry can
+             * finish the host-side undelegation.
+             */
+            rec->ipa = INVALID_PADDR;
+        }
+
+        rc = arm_cca_undelegate_granule(rec->pa);
+        if ( rc != 0 )
+            return rc;
+
+        free_domheap_page(maddr_to_page(rec->pa));
+        rec->pa = INVALID_PADDR;
+        d->arch.cca.nr_rtts--;
+    }
+
+    return 0;
+}
+
 static int arm_cca_destroy_realm(struct domain *d)
 {
     int rc;
@@ -179,6 +322,14 @@ int arm_cca_domain_relinquish_resources(struct domain *d)
     if ( rc != 0 )
         return rc;
 
+    rc = arm_cca_teardown_data_pages(d);
+    if ( rc != 0 )
+        return rc;
+
+    rc = arm_cca_teardown_rtts(d);
+    if ( rc != 0 )
+        return rc;
+
     rc = arm_cca_destroy_realm(d);
     if ( rc != 0 )
         return rc;
diff --git a/xen/arch/arm/include/asm/cca.h b/xen/arch/arm/include/asm/cca.h
index b135dd176751..d69e95a10010 100644
--- a/xen/arch/arm/include/asm/cca.h
+++ b/xen/arch/arm/include/asm/cca.h
@@ -68,6 +68,7 @@ struct arm_cca_domain_state {
     unsigned long nr_data_pages;
 
     /* Realm destruction state for domain_relinquish_resources(). */
+    unsigned long relinquish_data_idx;
     bool realm_terminate_done;
 };
 
-- 
2.51.0


Reply via email to