test(realm): add AUX RTT support for planes

- Add support for new RMI_RTT_AUX ABIs and helpers.
- Create and destroy aux mappings for realm creation
  with multiple planes and multiple RTTs.

Signed-off-by: Shruti Gupta <shruti.gupta@arm.com>
Change-Id: Ida808b9d311e8a662587257e015b0f62c110ac3b
diff --git a/include/runtime_services/host_realm_managment/host_realm_rmi.h b/include/runtime_services/host_realm_managment/host_realm_rmi.h
index 0534ca2..00435b0 100644
--- a/include/runtime_services/host_realm_managment/host_realm_rmi.h
+++ b/include/runtime_services/host_realm_managment/host_realm_rmi.h
@@ -146,6 +146,30 @@
 
 /*
  * arg0 == RD address
+ * arg1 == RTT address
+ * arg2 == map address
+ * arg3 == level
+ * arg4 == RTT tree index
+ *
+ * ret0 == status/index
+ */
+#define RMI_RTT_AUX_CREATE		SMC64_RMI_FID(U(0x2D))
+
+/*
+ * arg0 == RD address
+ * arg1 == map address
+ * arg2 == level
+ * arg3 == RTT tree index
+ *
+ * ret1 == Address (PA) of the RTT, if ret0 == RMI_SUCCESS
+ *         Otherwise, undefined.
+ * ret2 == Top of the non-live address region. Only valid
+ *         if ret0 == RMI_SUCCESS or ret0 == (RMI_ERROR_RTT_WALK, x)
+ */
+#define RMI_RTT_AUX_DESTROY		SMC64_RMI_FID(U(0x2E))
+
+/*
+ * arg0 == RD address
  * arg1 == map address
  * arg2 == level
  *
@@ -167,6 +191,31 @@
 /*
  * arg0 == RD address
  * arg1 == map address
+ * arg2 == RTT tree index
+ *
+ * ret0 == status/index
+ * ret1 == fail_index
+ * ret2 == primary level
+ * ret3 == state
+ * ret4 == ripas
+ */
+#define RMI_RTT_AUX_MAP_PROTECTED	SMC64_RMI_FID(U(0x30))
+
+/*
+ * arg0 == RD address
+ * arg1 == map address
+ * arg2 == RTT tree index
+ *
+ * ret0 == status/index
+ * ret1 == fail_index
+ * ret2 == primary level
+ * ret3 == state
+ */
+#define RMI_RTT_AUX_MAP_UNPROTECTED	SMC64_RMI_FID(U(0x31))
+
+/*
+ * arg0 == RD address
+ * arg1 == map address
  * arg2 == level
  *
  * ret1 == level
@@ -185,6 +234,40 @@
 #define RMI_RTT_UNMAP_UNPROTECTED	SMC64_RMI_FID(U(0x12))
 
 /*
+ * arg0 == RD address
+ * arg1 == map address
+ * arg2 == RTT tree index
+ *
+ * ret0 == status/index
+ * ret1 == top
+ * ret2 == level
+ */
+#define RMI_RTT_AUX_UNMAP_PROTECTED	SMC64_RMI_FID(U(0x33))
+
+/*
+ * arg0 == RD address
+ * arg1 == map address
+ * arg2 == RTT tree index
+ *
+ * ret0 == status/index
+ * ret1 == top
+ * ret2 == level
+ */
+#define RMI_RTT_AUX_UNMAP_UNPROTECTED	SMC64_RMI_FID(U(0x34))
+
+/*
+ * arg0 == RD address
+ * arg1 == target rec
+ * arg2 == base adr
+ * arg3 == top adr
+ *
+ * ret0 == return code
+ * ret1 == Top IPA of range whose S2AP was modified
+ * ret2 == Index of RTT tree in which base alignment check failed
+ */
+#define RMI_RTT_SET_S2AP		SMC64_RMI_FID(U(0x3B))
+
+/*
  * arg0 == calling rec address
  * arg1 == target rec address
  */
@@ -206,6 +289,16 @@
 
 /*
  * arg0 == RD address
+ * arg1 == map address
+ * arg2 == level
+ * arg3 == RTT tree index
+ *
+ * ret1 == Address(PA) of the RTT folded, if ret0 == RMI_SUCCESS
+ */
+#define RMI_RTT_AUX_FOLD		SMC64_RMI_FID(U(0x2F))
+
+/*
+ * arg0 == RD address
  */
 #define RMI_REC_AUX_COUNT		SMC64_RMI_FID(U(0x17))
 
@@ -430,7 +523,11 @@
 #define RMI_EXIT_RIPAS_CHANGE		4U
 #define RMI_EXIT_HOST_CALL		5U
 #define RMI_EXIT_SERROR			6U
-#define RMI_EXIT_INVALID		(RMI_EXIT_SERROR + 1U)
+#define RMI_EXIT_IO			7U
+#define RMI_EXIT_RTT_REQUEST		8U
+#define RMI_EXIT_S2AP_CHANGE		9U
+#define RMI_EXIT_VDEV_REQUEST		10U
+#define RMI_EXIT_INVALID		(RMI_EXIT_VDEV_REQUEST + 1U)
 
 /* RmiRecRunnable types */
 #define RMI_NOT_RUNNABLE		0U
@@ -537,6 +634,18 @@
 	 * index: RTT level at which the walk terminated
 	 */
 	RMI_ERROR_RTT = 4,
+	/*
+	 * An attribute of a device does not match the expected value
+	 */
+	RMI_ERROR_DEVICE = 5,
+	/*
+	 * The command is not supported
+	 */
+	RMI_ERROR_NOT_SUPPORTED = 6,
+	/*
+	 * RTTE in an auxiliary RTT contained an unexpected value
+	 */
+	RMI_ERROR_RTT_AUX = 7,
 	RMI_ERROR_COUNT
 } status_t;
 
@@ -1051,6 +1160,12 @@
 				    u_register_t start,
 				    u_register_t end,
 				    u_register_t *top);
+u_register_t host_rmi_rtt_set_s2ap(u_register_t rd,
+				   u_register_t rec,
+				   u_register_t start,
+				   u_register_t end,
+				   u_register_t *top,
+				   u_register_t *rtt_tree);
 u_register_t host_rmi_psci_complete(u_register_t calling_rec, u_register_t target_rec,
 				    unsigned long status);
 void host_rmi_init_cmp_result(void);
diff --git a/include/runtime_services/host_realm_managment/realm_def.h b/include/runtime_services/host_realm_managment/realm_def.h
index 3bd166a..5d3a196 100644
--- a/include/runtime_services/host_realm_managment/realm_def.h
+++ b/include/runtime_services/host_realm_managment/realm_def.h
@@ -21,6 +21,9 @@
 #define MAX_REALM_COUNT			U(2)
 #define MAX_AUX_PLANE_COUNT		U(3)
 #define MAX_PLANE_COUNT			MAX_AUX_PLANE_COUNT + U(1)
+#define PRIMARY_RTT_INDEX		0U
+#define PRIMARY_PLANE_ID		0U
+
 /* Only support 4KB at the moment */
 
 #if (PAGE_SIZE == PAGE_SIZE_4KB)
diff --git a/tftf/tests/runtime_services/host_realm_managment/host_realm_helper.c b/tftf/tests/runtime_services/host_realm_managment/host_realm_helper.c
index 6149512..3a4121b 100644
--- a/tftf/tests/runtime_services/host_realm_managment/host_realm_helper.c
+++ b/tftf/tests/runtime_services/host_realm_managment/host_realm_helper.c
@@ -34,7 +34,11 @@
 	RMI_EXIT(PSCI),
 	RMI_EXIT(RIPAS_CHANGE),
 	RMI_EXIT(HOST_CALL),
-	RMI_EXIT(SERROR)
+	RMI_EXIT(SERROR),
+	RMI_EXIT(IO),
+	RMI_EXIT(RTT_REQUEST),
+	RMI_EXIT(S2AP_CHANGE),
+	RMI_EXIT(VDEV_REQUEST)
 };
 
 /*
diff --git a/tftf/tests/runtime_services/host_realm_managment/host_realm_rmi.c b/tftf/tests/runtime_services/host_realm_managment/host_realm_rmi.c
index 1255fc6..821a99e 100644
--- a/tftf/tests/runtime_services/host_realm_managment/host_realm_rmi.c
+++ b/tftf/tests/runtime_services/host_realm_managment/host_realm_rmi.c
@@ -76,7 +76,7 @@
 	 * must be preserved unless they contain result,
 	 * as specified in the function definition.
 	 */
-	if (regs[0] != RMI_RTT_READ_ENTRY) {
+	if ((regs[0] != RMI_RTT_READ_ENTRY) && (regs[0] != RMI_RTT_AUX_MAP_PROTECTED)) {
 		CHECK_RET(4);
 	}
 
@@ -184,6 +184,16 @@
 				rd, rtt, map_addr, (u_register_t)level}, 5U).ret0;
 }
 
+static inline u_register_t host_rmi_rtt_aux_create(u_register_t rd,
+						u_register_t rtt,
+						u_register_t map_addr,
+						long  level,
+						u_register_t tree_index)
+{
+	return host_rmi_handler(&(smc_args) {RMI_RTT_AUX_CREATE,
+				rd, rtt, map_addr, (u_register_t)level, tree_index}, 6U).ret0;
+}
+
 u_register_t host_rmi_rtt_destroy(u_register_t rd,
 				  u_register_t map_addr,
 				  long level,
@@ -200,6 +210,24 @@
 	return rets.ret0;
 }
 
+u_register_t host_rmi_rtt_aux_destroy(u_register_t rd,
+				  u_register_t map_addr,
+				  long level,
+				  u_register_t tree_index,
+				  u_register_t *rtt,
+				  u_register_t *top)
+{
+	smc_ret_values rets;
+
+	rets = host_rmi_handler(&(smc_args) {RMI_RTT_AUX_DESTROY,
+				rd, map_addr, (u_register_t)level,
+				tree_index,
+				(u_register_t)&rets}, 6U);
+	*rtt = rets.ret1;
+	*top = rets.ret2;
+	return rets.ret0;
+}
+
 u_register_t host_rmi_features(u_register_t index, u_register_t *features)
 {
 	smc_ret_values rets;
@@ -243,6 +271,22 @@
 	return rets.ret0;
 }
 
+static inline u_register_t host_rmi_rtt_aux_fold(u_register_t rd,
+						 u_register_t map_addr,
+						 long level,
+						 u_register_t tree_index,
+						 u_register_t *pa)
+{
+	smc_ret_values rets;
+
+	rets = host_rmi_handler(&(smc_args) {RMI_RTT_AUX_FOLD,
+				rd, map_addr, (u_register_t)level,
+				tree_index,
+				(u_register_t)&rets}, 6U);
+	*pa = rets.ret1;
+	return rets.ret0;
+}
+
 static inline u_register_t host_rmi_rec_aux_count(u_register_t rd,
 						  u_register_t *aux_count)
 {
@@ -276,6 +320,42 @@
 				rd, map_addr, (u_register_t)level, ns_pa}, 5U).ret0;
 }
 
+u_register_t host_rmi_rtt_aux_map_unprotected(u_register_t rd,
+					      u_register_t map_addr,
+					      u_register_t tree_index,
+					      u_register_t *fail_index,
+					      u_register_t *level_pri,
+					      u_register_t *state)
+{
+	smc_ret_values rets;
+
+	rets = host_rmi_handler(&(smc_args) {RMI_RTT_AUX_MAP_UNPROTECTED,
+				rd, map_addr, tree_index}, 4U);
+	*fail_index = rets.ret1;
+	*level_pri = rets.ret2;
+	*state = rets.ret3;
+	return rets.ret0;
+}
+
+u_register_t host_rmi_rtt_aux_map_protected(u_register_t rd,
+					 u_register_t map_addr,
+					 u_register_t tree_index,
+					 u_register_t *fail_index,
+					 u_register_t *level_pri,
+					 u_register_t *state,
+					 u_register_t *ripas)
+{
+	smc_ret_values rets;
+
+	rets = host_rmi_handler(&(smc_args) {RMI_RTT_AUX_MAP_PROTECTED,
+				rd, map_addr, tree_index}, 4U);
+	*fail_index = rets.ret1;
+	*level_pri = rets.ret2;
+	*state = rets.ret3;
+	*ripas = rets.ret4;
+	return rets.ret0;
+}
+
 u_register_t host_rmi_rtt_readentry(u_register_t rd,
 				   u_register_t map_addr,
 				   long level,
@@ -305,6 +385,36 @@
 	return rets.ret0;
 }
 
+u_register_t host_rmi_rtt_aux_unmap_unprotected(u_register_t rd,
+					  u_register_t map_addr,
+					  u_register_t tree_index,
+					  u_register_t *top,
+					  u_register_t *level)
+{
+	smc_ret_values rets;
+
+	rets = host_rmi_handler(&(smc_args) {RMI_RTT_AUX_UNMAP_UNPROTECTED,
+					rd, map_addr, tree_index}, 4U);
+	*top = rets.ret1;
+	*level = rets.ret2;
+	return rets.ret0;
+}
+
+u_register_t host_rmi_rtt_aux_unmap_protected(u_register_t rd,
+					  u_register_t map_addr,
+					  u_register_t tree_index,
+					  u_register_t *top,
+					  u_register_t *level)
+{
+	smc_ret_values rets;
+
+	rets = host_rmi_handler(&(smc_args) {RMI_RTT_AUX_UNMAP_PROTECTED,
+					rd, map_addr, tree_index}, 4U);
+	*top = rets.ret1;
+	*level = rets.ret2;
+	return rets.ret0;
+}
+
 bool host_ipa_is_ns(u_register_t addr, u_register_t rmm_feat_reg0)
 {
 	return (addr >> (EXTRACT(RMI_FEATURE_REGISTER_0_S2SZ, rmm_feat_reg0) - 1UL) == 1UL);
@@ -351,6 +461,42 @@
 	return REALM_SUCCESS;
 }
 
+static u_register_t host_realm_create_rtt_aux_levels(struct realm *realm,
+					    u_register_t map_addr,
+					    long level, long max_level,
+					    u_register_t tree_index)
+{
+	u_register_t rtt, ret, ipa_align;
+
+	assert(tree_index != PRIMARY_RTT_INDEX);
+	while (level++ < max_level) {
+		rtt = (u_register_t)page_alloc(PAGE_SIZE);
+		if (rtt == HEAP_NULL_PTR) {
+			ERROR("Failed to allocate memory for rtt\n");
+			return REALM_ERROR;
+		} else {
+			ret = host_rmi_granule_delegate(rtt);
+			if (ret != RMI_SUCCESS) {
+				ERROR("%s() failed, rtt=0x%lx ret=0x%lx\n",
+					"host_rmi_granule_delegate", rtt, ret);
+				return REALM_ERROR;
+			}
+		}
+		ipa_align = ALIGN_DOWN(map_addr, RTT_MAP_SIZE(level - 1));
+		ret = host_rmi_rtt_aux_create(realm->rd, rtt, ipa_align, (u_register_t)level,
+				tree_index);
+		if (ret != RMI_SUCCESS) {
+			ERROR("%s() failed, rtt=0x%lx ret=0x%lx\n",
+				"host_realm_rtt_aux_create", rtt, ret);
+			host_rmi_granule_undelegate(rtt);
+			page_free(rtt);
+			return REALM_ERROR;
+		}
+	}
+
+	return REALM_SUCCESS;
+}
+
 u_register_t host_realm_fold_rtt(u_register_t rd, u_register_t addr,
 				 long level)
 {
@@ -379,6 +525,93 @@
 
 }
 
+u_register_t host_realm_aux_map_protected_data(struct realm *realm,
+						u_register_t target_pa,
+						u_register_t map_size,
+						u_register_t tree_index,
+						u_register_t *fail_index,
+						u_register_t *level_pri,
+						u_register_t *state,
+						u_register_t *ripas)
+{
+	u_register_t ret, top;
+	long level;
+	int8_t ulevel;
+	u_register_t size = 0UL;
+	u_register_t map_addr = target_pa;
+
+	assert(tree_index != PRIMARY_RTT_INDEX);
+	for (size = 0UL; size < map_size; size += PAGE_SIZE) {
+		ret =  host_rmi_rtt_aux_map_protected(realm->rd, map_addr, tree_index,
+			fail_index, level_pri, state, ripas);
+
+		if (RMI_RETURN_STATUS(ret) == RMI_ERROR_RTT_AUX) {
+			/* Create missing RTTs till L3 and retry */
+			ulevel = RMI_RETURN_INDEX(ret);
+			level = (long)ulevel;
+
+			ret = host_realm_create_rtt_aux_levels(realm, map_addr,
+							 (u_register_t)level,
+							 3U, tree_index);
+			if (ret != RMI_SUCCESS) {
+				ERROR("%s() failed, ret=0x%lx line=%u\n",
+					"host_realm_create_rtt_aux_levels",
+					ret, __LINE__);
+				goto err;
+			}
+			ret =  host_rmi_rtt_aux_map_protected(realm->rd, target_pa, tree_index,
+				fail_index, level_pri, state, ripas);
+			if (ret != RMI_SUCCESS) {
+				ERROR("%s() failed, ret=0x%lx\n",
+				"host_rmi_data_create", ret);
+				goto err;
+			}
+		} else if (ret != RMI_SUCCESS) {
+			ERROR("host_rmi_rtt_aux_map_protected failed ret = 0x%lx", ret);
+			goto err;
+		}
+		map_addr += PAGE_SIZE;
+	}
+	return REALM_SUCCESS;
+
+err:
+	while (size >= PAGE_SIZE) {
+		ret = host_rmi_rtt_aux_unmap_protected(realm->rd, target_pa, tree_index,
+				&top, (u_register_t *)&level);
+		if (ret != RMI_SUCCESS) {
+			ERROR("%s() failed, addr=0x%lx ret=0x%lx\n",
+				"host_rmi_rtt_aux_unmap_protected", map_addr, ret);
+		}
+		size -= PAGE_SIZE;
+		map_addr -= PAGE_SIZE;
+	}
+	return REALM_ERROR;
+}
+
+u_register_t host_realm_aux_unmap_protected_data(struct realm *realm,
+						u_register_t target_ipa,
+						u_register_t map_size,
+						u_register_t tree_index,
+						u_register_t *top,
+						u_register_t *level)
+{
+	u_register_t ret, size = 0UL, map_addr = target_ipa;
+
+	assert(tree_index != PRIMARY_RTT_INDEX);
+	for (size = 0UL; size < map_size; size += PAGE_SIZE) {
+		ret = host_rmi_rtt_aux_unmap_protected(realm->rd, map_addr, tree_index,
+				top, level);
+		if (ret != RMI_SUCCESS) {
+			WARN("%s() failed, ret=0x%lx line=%u\n",
+					"host_rmi_rtt_aux_unmap_protected",
+					ret, __LINE__);
+			return REALM_ERROR;
+		}
+		map_addr += PAGE_SIZE;
+	}
+	return REALM_SUCCESS;
+}
+
 u_register_t host_realm_delegate_map_protected_data(bool unknown,
 						    struct realm *realm,
 						    u_register_t target_pa,
@@ -562,6 +795,50 @@
 	return REALM_SUCCESS;
 }
 
+static u_register_t host_realm_destroy_free_aux_rtt(struct realm *realm,
+						    u_register_t addr,
+						    long level)
+{
+	u_register_t rtt2, top, ret;
+
+	/* Destroy and undelegate RTT entry for all trees */
+	for (unsigned int tree_index = 1U;
+			tree_index <= realm->num_aux_planes;
+			tree_index++) {
+
+		ret = host_rmi_rtt_aux_destroy(realm->rd, ALIGN_DOWN(addr,
+					RTT_MAP_SIZE(level)),
+					level + 1L,
+					tree_index, &rtt2, &top);
+
+		if (ret != RMI_SUCCESS) {
+			/*
+			 * IPA might not be mapped on all AUX RTTs
+			 * ignore error
+			 */
+			VERBOSE("%s() failed, map_addr=0x%lx ret=0x%lx \
+					rtt2=0x%lx \
+					top=0x%lx level=0x%lx\n",
+					"host_rmi_rtt_aux_destroy",
+					map_addr, ret, rtt2,
+					top, level + 1L);
+		}
+
+		if (rtt2 != 0UL) {
+			ret = host_rmi_granule_undelegate(rtt2);
+			if (ret != RMI_SUCCESS) {
+				ERROR("%s() failed, rtt=0x%lx ret=0x%lx\n",
+					"host_rmi_granule_undelegate",
+					rtt2, ret);
+				return REALM_ERROR;
+			}
+
+			page_free(rtt2);
+		}
+	}
+	return REALM_SUCCESS;
+}
+
 static u_register_t host_realm_destroy_undelegate_range(struct realm *realm,
 							u_register_t ipa,
 							u_register_t addr,
@@ -573,6 +850,29 @@
 
 	while (size >= PAGE_SIZE) {
 		ret = host_rmi_data_destroy(rd, ipa, &data, &top);
+
+		if (ret == RMI_ERROR_RTT_AUX) {
+			/* Unmap from all Aux RTTs */
+			for (unsigned int tree_index = 1U; tree_index <= realm->num_aux_planes;
+					tree_index++) {
+				u_register_t top1, level1;
+
+				/* IPA might not be mapped in all Aux RTTs ignore error */
+				ret = host_rmi_rtt_aux_unmap_protected(
+								rd,
+								ipa,
+								tree_index,
+								&top1, &level1);
+				if (ret != RMI_SUCCESS) {
+					VERBOSE("%s() failed, addr=0x%lx ret=0x%lx tree=0x%lx\n",
+					"host_rmi_rtt_aux_unmap_protected",
+					ipa, ret, tree_index);
+				}
+			}
+			/* Retry DATA_DESTROY */
+			continue;
+		}
+
 		if (ret != RMI_SUCCESS) {
 			ERROR("%s() failed, addr=0x%lx ret=0x%lx\n",
 				"host_rmi_data_destroy", ipa, ret);
@@ -622,6 +922,28 @@
 		case RMI_ASSIGNED:
 			if (host_ipa_is_ns(map_addr, realm->rmm_feat_reg0)) {
 
+				u_register_t level1;
+
+				/* Unmap from all Aux RTT */
+				if (!realm->rtt_tree_single) {
+					for (unsigned int tree_index = 1U;
+						tree_index <= realm->num_aux_planes;
+						tree_index++) {
+
+						ret = host_rmi_rtt_aux_unmap_unprotected(
+								rd,
+								map_addr,
+								tree_index,
+								&top, &level1);
+
+						if (ret != RMI_SUCCESS) {
+							ERROR("%s() failed, addr=0x%lx ret=0x%lx\n",
+							"host_rmi_rtt_unmap_unprotected",
+							map_addr, ret);
+						}
+					}
+				}
+
 				ret = host_rmi_rtt_unmap_unprotected(
 								rd,
 								map_addr,
@@ -669,6 +991,19 @@
 					map_addr, ret);
 				return REALM_ERROR;
 			}
+
+			/* RTT_AUX_DESTROY */
+			if (!realm->rtt_tree_single) {
+				ret = host_realm_destroy_free_aux_rtt(realm, map_addr,
+						level);
+
+				if (ret != RMI_SUCCESS) {
+					ERROR("%s() failed, map_addr=0x%lx ret=0x%lx\n",
+						"host_realm_destroy_free_aux_rtt",
+						map_addr, ret);
+					return REALM_ERROR;
+				}
+			}
 			break;
 		default:
 			return REALM_ERROR;
@@ -700,6 +1035,23 @@
 	return ret.ret1;
 }
 
+u_register_t host_rmi_rtt_set_s2ap(u_register_t rd,
+				u_register_t rec,
+				u_register_t base,
+				u_register_t top,
+				u_register_t *out_top,
+				u_register_t *rtt_tree)
+{
+	smc_ret_values rets;
+
+	rets = host_rmi_handler(&(smc_args) {RMI_RTT_SET_S2AP,
+				rd, rec, base, top,
+				(u_register_t)&rets}, 6U);
+	*out_top = rets.ret1;
+	*rtt_tree = rets.ret2;
+	return rets.ret0;
+}
+
 u_register_t host_realm_create(struct realm *realm)
 {
 	struct rmi_realm_params *params;