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
