test(realm): add support for RSI Planes ABI

Add Planes RSI support.
Add helpers to setup initialize and enter planes.

Signed-off-by: Shruti Gupta <shruti.gupta@arm.com>
Change-Id: I62946a185c47fe77a04f42751d0b0a467d41ceee
diff --git a/realm/include/realm_helpers.h b/realm/include/realm_helpers.h
index 11c4423..a9b2f7c 100644
--- a/realm/include/realm_helpers.h
+++ b/realm/include/realm_helpers.h
@@ -10,6 +10,19 @@
 
 /* Generate 64-bit random number */
 unsigned long long realm_rand64(void);
+/*
+ * Function to enter Aux Plane from Primary Plane
+ * arg1 == plane index
+ * arg2 == permission index to be used by plane
+ * arg3 == base entrypoint
+ * arg4 == entry flags
+ * aarg5 == run object, needs to be PAGE aligned
+ */
+bool realm_plane_enter(u_register_t plane_index, u_register_t perm_index,
+		u_register_t base, u_register_t flags, rsi_plane_run *run);
+
+/* Function for initializing planes, called at Boot */
+void realm_plane_init(void);
 
 #endif /* REALM_HELPERS_H */
 
diff --git a/realm/include/realm_rsi.h b/realm/include/realm_rsi.h
index 72ee80e..33e8fcf 100644
--- a/realm/include/realm_rsi.h
+++ b/realm/include/realm_rsi.h
@@ -8,6 +8,7 @@
 #ifndef REALM_RSI_H
 #define REALM_RSI_H
 
+#include <arch.h>
 #include <stdint.h>
 #include <host_shared_data.h>
 #include <tftf_lib.h>
@@ -71,7 +72,9 @@
 	/* IPA width in bits */
 	SET_MEMBER(unsigned long ipa_width, 0, 8);	/* Offset 0 */
 	/* Hash algorithm */
-	SET_MEMBER(unsigned long algorithm, 8, 0x200);	/* Offset 8 */
+	SET_MEMBER(unsigned long algorithm, 8, 0x10);	/* Offset 8 */
+	/* Number of auxiliary Planes */
+	SET_MEMBER(unsigned long num_aux_planes, 0x10, 0x200); /* Offset 0x10 */
 	/* Realm Personalization Value */
 	SET_MEMBER(unsigned char rpv[RSI_RPV_SIZE], 0x200, 0x1000); /* Offset 0x200 */
 };
@@ -157,7 +160,151 @@
 #define RSI_NO_CHANGE_DESTROYED	0UL
 #define RSI_CHANGE_DESTROYED	1UL
 
-/* Request RIPAS of a target IPA range to be changed to a specified value */
+/*
+ * arg1 == plane index
+ * arg2 == perm index
+ *
+ * ret0 == status
+ * ret1 == perm value
+ */
+#define RSI_MEM_GET_PERM_VALUE	SMC_RSI_FID(0x10U)
+
+/*
+ * arg1 == base adr
+ * arg2 == top adr
+ * arg3 == perm index
+ * arg4 == cookie
+ *
+ * ret0 == status
+ * ret1 == new_base
+ * ret2 == response
+ * ret3 == new_cookie
+ */
+#define RSI_MEM_SET_PERM_INDEX	SMC_RSI_FID(0x11U)
+
+/*
+ * arg1 == plane index
+ * arg2 == perm index
+ *
+ * ret0 == status
+ */
+#define RSI_MEM_SET_PERM_VALUE	SMC_RSI_FID(0x12U)
+
+#define RSI_PLANE_NR_GPRS	31U
+#define RSI_PLANE_GIC_NUM_LRS	16U
+
+/*
+ * Flags provided by the Primary Plane to the secondary ones upon
+ * plane entry.
+ */
+#define RSI_PLANE_ENTRY_FLAG_TRAP_WFI	U(1UL << 0)
+#define RSI_PLANE_ENTRY_FLAG_TRAP_WFE	U(1UL << 1)
+#define RSI_PLANE_ENTRY_FLAG_TRAP_HC	U(1UL << 2)
+
+/* Data structure used to pass values from P0 to the RMM on Plane entry */
+struct rsi_plane_entry {
+	/* Flags */
+	SET_MEMBER(u_register_t flags, 0, 0x8);	/* Offset 0 */
+	/* PC */
+	SET_MEMBER(u_register_t pc, 0x8, 0x100);	/* Offset 0x8 */
+	/* General-purpose registers */
+	SET_MEMBER(u_register_t gprs[RSI_PLANE_NR_GPRS], 0x100, 0x200);	/* 0x100 */
+	/* EL1 system registers */
+	SET_MEMBER(struct {
+		/* GICv3 Hypervisor Control Register */
+		u_register_t gicv3_hcr;                         /* 0x200 */
+		/* GICv3 List Registers */
+		u_register_t gicv3_lrs[RSI_PLANE_GIC_NUM_LRS];        /* 0x208 */
+	}, 0x200, 0x800);
+};
+
+/* Data structure used to pass values from the RMM to P0 on Plane exit */
+struct rsi_plane_exit {
+	/* Exit reason */
+	SET_MEMBER(u_register_t exit_reason, 0, 0x100);/* Offset 0 */
+	SET_MEMBER(struct {
+		/* Exception Link Register */
+		u_register_t elr;				/* 0x100 */
+		/* Exception Syndrome Register */
+		u_register_t esr;				/* 0x108 */
+		/* Fault Address Register */
+		u_register_t far;				/* 0x108 */
+		/* Hypervisor IPA Fault Address register */
+		u_register_t hpfar;				/* 0x110 */
+	}, 0x100, 0x200);
+	/* General-purpose registers */
+	SET_MEMBER(u_register_t gprs[RSI_PLANE_NR_GPRS], 0x200, 0x300); /* 0x200 */
+	SET_MEMBER(struct {
+		/* GICv3 Hypervisor Control Register */
+		u_register_t gicv3_hcr;				/* 0x300 */
+		/* GICv3 List Registers */
+		u_register_t gicv3_lrs[RSI_PLANE_GIC_NUM_LRS];	/* 0x308 */
+		/* GICv3 Maintenance Interrupt State Register */
+		u_register_t gicv3_misr;			/* 0x388 */
+		/* GICv3 Virtual Machine Control Register */
+		u_register_t gicv3_vmcr;			/* 0x390 */
+	}, 0x300, 0x600);
+};
+
+typedef struct {
+	/* Entry information */
+	SET_MEMBER(struct rsi_plane_entry enter, 0, 0x800); /* Offset 0 */
+	/* Exit information */
+	SET_MEMBER(struct rsi_plane_exit exit, 0x800, 0x1000);/* 0x800 */
+} rsi_plane_run;
+
+/*
+ * arg1 == plane index
+ * arg2 == run pointer
+ *
+ * ret0 == status
+ */
+#define RSI_PLANE_ENTER		SMC_RSI_FID(0x13U)
+
+/*
+ * arg1 == plane index
+ * arg2 == register encoding
+ *
+ * ret0 == status
+ * ret1 = register value
+ */
+#define RSI_PLANE_REG_READ	SMC_RSI_FID(0x1EU)
+
+/*
+ * arg1 == plane index
+ * arg2 == register encoding
+ * arg3 == register value
+ *
+ * ret0 == status
+ */
+#define RSI_PLANE_REG_WRITE	SMC_RSI_FID(0x1FU)
+
+/*
+ * Function to set overlay permission value for a specified
+ * (plane index, overlay permission index) tuple
+ */
+u_register_t rsi_mem_set_perm_value(u_register_t plane_index,
+	u_register_t perm_index,
+	u_register_t perm);
+
+/*
+ * Function to Get overlay permission value for a specified
+ * (plane index, overlay permission index) tuple
+ */
+u_register_t rsi_mem_get_perm_value(u_register_t plane_index,
+	u_register_t perm_index,
+	u_register_t *perm);
+
+/* Function to Set overlay permission index for a specified IPA range See RSI_MEM_SET_PERM_INDEX */
+u_register_t rsi_mem_set_perm_index(u_register_t base,
+	u_register_t top,
+	u_register_t perm_index,
+	u_register_t cookie,
+	u_register_t *new_base,
+	u_register_t *response,
+	u_register_t *new_cookie);
+
+/* Request RIPAS of a target IPA range to be changed to a specified value. */
 u_register_t rsi_ipa_state_set(u_register_t base,
 				u_register_t top,
 				rsi_ripas_type ripas,
@@ -197,4 +344,10 @@
 /* This function call Host and request to exit Realm with proper exit code */
 void rsi_exit_to_host(enum host_call_cmd exit_code);
 
+/* Function to get Realm configuration. See RSI_REALM_CONFIG */
+u_register_t rsi_realm_config(struct rsi_realm_config *s);
+
+/* Function to enter aux plane. See RSI_PLANE_ENTER */
+u_register_t rsi_plane_enter(u_register_t plane_index, u_register_t run);
+
 #endif /* REALM_RSI_H */
diff --git a/realm/realm.mk b/realm/realm.mk
index 3e66adc..f59387c 100644
--- a/realm/realm.mk
+++ b/realm/realm.mk
@@ -34,6 +34,7 @@
 	realm_multiple_rec.c						\
 	realm_pauth.c							\
 	realm_payload_main.c						\
+	realm_plane.c							\
 	realm_pmuv3.c							\
 	realm_psci.c							\
 	realm_rsi.c							\
diff --git a/realm/realm_plane.c b/realm/realm_plane.c
new file mode 100644
index 0000000..ef1e0ab
--- /dev/null
+++ b/realm/realm_plane.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2022-2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#include <stdio.h>
+#include <arch.h>
+#include <arch_features.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <host_realm_helper.h>
+#include <realm_rsi.h>
+#include <sync.h>
+
+bool is_plane0;
+
+void realm_plane_init(void)
+{
+	u_register_t ret;
+
+	ret = rsi_get_version(RSI_ABI_VERSION_VAL);
+	if (ret == RSI_ERROR_STATE) {
+		is_plane0 = false;
+	} else {
+		is_plane0 = true;
+	}
+}
+
+static void restore_plane_context(rsi_plane_run *run)
+{
+	for (unsigned int i = 0U; i < RSI_PLANE_NR_GPRS; i++) {
+		run->enter.gprs[i] = run->exit.gprs[i];
+	}
+
+	run->enter.pc = run->exit.elr;
+}
+
+/* return true to re-enter PlaneN, false to exit to P0 */
+static bool handle_plane_exit(u_register_t plane_index,
+		u_register_t perm_index,
+		rsi_plane_run *run)
+{
+	u_register_t ec = EC_BITS(run->exit.esr);
+
+	/* Disallow SMC from Plane N */
+	if (ec == EC_AARCH64_SMC) {
+		restore_plane_context(run);
+		run->enter.gprs[0] = RSI_ERROR_STATE;
+		return true;
+	}
+	return false;
+}
+
+static bool plane_common_init(u_register_t plane_index,
+		u_register_t perm_index,
+		u_register_t base,
+		rsi_plane_run *run)
+{
+	u_register_t ret;
+
+	memset(run, 0, sizeof(rsi_plane_run));
+	run->enter.pc = base;
+
+	/* Perm init */
+	ret = rsi_mem_set_perm_value(plane_index, perm_index, PERM_LABEL_RW_upX);
+	if (ret != RSI_SUCCESS) {
+		ERROR("rsi_mem_set_perm_value failed %u\n", plane_index);
+		return false;
+	}
+	return true;
+}
+
+bool realm_plane_enter(u_register_t plane_index,
+		u_register_t perm_index,
+		u_register_t base,
+		u_register_t flags,
+		rsi_plane_run *run)
+{
+	u_register_t ret;
+	bool ret1;
+
+	ret1 = plane_common_init(plane_index, perm_index, base, run);
+	if (!ret1) {
+		return ret1;
+	}
+
+	run->enter.flags = flags;
+
+	while (ret1) {
+		ret = rsi_plane_enter(plane_index, (u_register_t)run);
+		if (ret != RSI_SUCCESS) {
+			ERROR("Plane %u enter failed ret= 0x%lx\n", plane_index, ret);
+			return false;
+		}
+
+		VERBOSE("plane exit_reason=0x%lx esr=0x%lx hpfar=0x%lx far=0x%lx\n",
+				run->exit.exit_reason,
+				run->exit.esr,
+				run->exit.hpfar,
+				run->exit.far);
+
+		ret1 = handle_plane_exit(plane_index, perm_index, run);
+	}
+	return true;
+}
+
diff --git a/realm/realm_rsi.c b/realm/realm_rsi.c
index db2394c..1c6fc8e 100644
--- a/realm/realm_rsi.c
+++ b/realm/realm_rsi.c
@@ -22,6 +22,11 @@
 	if (res.ret0 == SMC_UNKNOWN) {
 		return SMC_UNKNOWN;
 	}
+
+	if (res.ret0 == RSI_ERROR_STATE) {
+		return RSI_ERROR_STATE;
+	}
+
 	/* Return lower version */
 	return res.ret1;
 }
@@ -141,3 +146,67 @@
 	}
 	return res.ret0;
 }
+
+u_register_t rsi_realm_config(struct rsi_realm_config *s)
+{
+	smc_ret_values res = {};
+
+	res = tftf_smc(&(smc_args)
+			{RSI_REALM_CONFIG, (u_register_t)s});
+	return res.ret0;
+}
+
+u_register_t rsi_mem_get_perm_value(u_register_t plane_index,
+				    u_register_t perm_index,
+				    u_register_t *perm)
+{
+	smc_ret_values res = {};
+
+	res = tftf_smc(&(smc_args)
+			{RSI_MEM_GET_PERM_VALUE, plane_index, perm_index});
+	if (res.ret0 == RSI_SUCCESS) {
+		*perm = res.ret1;
+	}
+	return res.ret0;
+}
+
+u_register_t rsi_mem_set_perm_value(u_register_t plane_index,
+				    u_register_t perm_index,
+				    u_register_t perm)
+{
+	smc_ret_values res = {};
+
+	res = tftf_smc(&(smc_args)
+			{RSI_MEM_SET_PERM_VALUE, plane_index, perm_index, perm});
+	return res.ret0;
+}
+
+u_register_t rsi_mem_set_perm_index(u_register_t base,
+				    u_register_t top,
+				    u_register_t perm_index,
+				    u_register_t cookie,
+				    u_register_t *new_base,
+				    u_register_t *response,
+				    u_register_t *new_cookie)
+{
+	smc_ret_values res = {};
+
+	res = tftf_smc(&(smc_args)
+			{RSI_MEM_SET_PERM_INDEX, base, top, perm_index, cookie});
+	if (res.ret0 == RSI_SUCCESS) {
+		*new_base = res.ret1;
+		*response = res.ret2;
+		*new_cookie = res.ret3;
+	}
+	return res.ret0;
+}
+
+u_register_t rsi_plane_enter(u_register_t plane_index,
+			     u_register_t plane_run)
+{
+	smc_ret_values res = {};
+
+	res = tftf_smc(&(smc_args)
+			{RSI_PLANE_ENTER, plane_index, plane_run});
+	return res.ret0;
+}