feat(smc): support SMC calls with no ret vals in x8

The current implementation of the SMC library assumes that x8
contains the address of an smc_ret_values structure. Although
this can be convenient, it prevents from using registers x8-x17
as arguments, since it assumes there is an address in x8.

This patch implements an alternative API for SMC calls, allowing
the use of registers x1-x17 as input arguments and the use of
registers x0-x17 as output arguments without assuming a pointer
in x8.

Signed-off-by: Juan Pablo Conde <juanpablo.conde@arm.com>
Change-Id: I0016116b8d2ee4ef5aac9473f31e38434cda4943
diff --git a/include/lib/tftf_lib.h b/include/lib/tftf_lib.h
index 36e2e0f..c65b061 100644
--- a/include/lib/tftf_lib.h
+++ b/include/lib/tftf_lib.h
@@ -140,48 +140,109 @@
 void waitms(uint64_t ms);
 void waitus(uint64_t us);
 
+/* Define fields in common for smc_args and smc_args_ext */
+#define FID_COMMON_ARGS()						\
+	/*								\
+	 * Function identifier. Identifies which function is being	\
+	 * invoked.							\
+	 */								\
+	uint32_t	fid;						\
+									\
+	u_register_t	arg1;						\
+	u_register_t	arg2;						\
+	u_register_t	arg3;						\
+	u_register_t	arg4;						\
+	u_register_t	arg5;						\
+	u_register_t	arg6;						\
+	u_register_t	arg7;
+
+/* Define fields in common for smc_ret_values and smc_ret_values_ext */
+#define COMMON_RETVALS()						\
+	u_register_t	ret0;						\
+	u_register_t	ret1;						\
+	u_register_t	ret2;						\
+	u_register_t	ret3;						\
+	u_register_t	ret4;						\
+	u_register_t	ret5;						\
+	u_register_t	ret6;						\
+	u_register_t	ret7;
+
+
 /*
- * SMC calls take a function identifier and up to 7 arguments.
+ * SMC calls take a function identifier and up to 7 arguments if using x8
+ * as an address pointing to a structure where return values are stored.
  * Additionally, few SMC calls that originate from EL2 leverage the seventh
  * argument explicitly. Given that TFTF runs in EL2, we need to be able to
  * specify it.
  */
 typedef struct {
-	/* Function identifier. Identifies which function is being invoked. */
-	uint32_t	fid;
-
-	u_register_t	arg1;
-	u_register_t	arg2;
-	u_register_t	arg3;
-	u_register_t	arg4;
-	u_register_t	arg5;
-	u_register_t	arg6;
-	u_register_t	arg7;
+	FID_COMMON_ARGS()
 } smc_args;
 
-/* SMC calls can return up to 8 register values */
+/*
+ * If x8 is not used as an address pointing to a structure where the return
+ * values are stored, SMC calls take up to 17 arguments.
+ */
 typedef struct {
-	u_register_t	ret0;
-	u_register_t	ret1;
-	u_register_t	ret2;
-	u_register_t	ret3;
-	u_register_t	ret4;
-	u_register_t	ret5;
-	u_register_t	ret6;
-	u_register_t	ret7;
+	FID_COMMON_ARGS()
+	u_register_t	arg8;
+	u_register_t	arg9;
+	u_register_t	arg10;
+	u_register_t	arg11;
+	u_register_t	arg12;
+	u_register_t	arg13;
+	u_register_t	arg14;
+	u_register_t	arg15;
+	u_register_t	arg16;
+	u_register_t	arg17;
+} smc_args_ext;
+
+/*
+ * SMC calls can return up to 8 register values if x8 is used as an address
+ * pointing to a structure where the return values are stored.
+ */
+typedef struct {
+	COMMON_RETVALS()
 } smc_ret_values;
 
 /*
- * Trigger an SMC call.
+ * If x8 is not used as an address pointing to a structure where the return
+ * values are stored, SMC calls return up to 18 register values.
+ */
+typedef struct {
+	COMMON_RETVALS()
+	u_register_t	ret8;
+	u_register_t	ret9;
+	u_register_t	ret10;
+	u_register_t	ret11;
+	u_register_t	ret12;
+	u_register_t	ret13;
+	u_register_t	ret14;
+	u_register_t	ret15;
+	u_register_t	ret16;
+	u_register_t	ret17;
+} smc_ret_values_ext;
+
+/*
+ * Trigger an SMC call. Return values are stored in structure pointed by address
+ * stored in x8.
  */
 smc_ret_values tftf_smc(const smc_args *args);
 
+/*
+ * Trigger an SMC call. Return values are stored in structure pointed by 'ret'
+ */
+void tftf_smc_no_retval_x8(const smc_args_ext *args, smc_ret_values_ext *ret);
+
 /* Assembler routine to trigger a SMC call. */
 smc_ret_values asm_tftf_smc64(uint32_t fid, u_register_t arg1, u_register_t arg2,
 			      u_register_t arg3, u_register_t arg4,
 			      u_register_t arg5, u_register_t arg6,
 			      u_register_t arg7);
 
+/* Assembler routine to trigger a SMC call without smc_ret_values in x8. */
+u_register_t asm_tftf_smc64_no_retval_x8(const smc_args_ext *args,
+					  smc_ret_values_ext *ret);
 /*
  * Update the SVE hint for the current CPU. Any SMC call made through tftf_smc
  * will update the SVE hint bit in the SMC Function ID.
diff --git a/lib/smc/aarch64/asm_smc.S b/lib/smc/aarch64/asm_smc.S
index b11baa8..d4cede9 100644
--- a/lib/smc/aarch64/asm_smc.S
+++ b/lib/smc/aarch64/asm_smc.S
@@ -38,6 +38,59 @@
 
 	.endm
 
+	.macro smccc_conduit_nox8 _conduit
+
+	/*
+	 * Store address pointing at the smc_ret_values structure in the stack
+	 */
+	str	x1, [sp, #-16]!
+
+	/*
+	 * Store arguments in x0..x17. Start from highest registers so address
+	 * pointed by x0 is preserved until it is no longer needed.
+	 */
+	ldp	x16, x17, [x0, #128]
+	ldp	x14, x15, [x0, #112]
+	ldp	x12, x13, [x0, #96]
+	ldp	x10, x11, [x0, #80]
+	ldp	x8, x9, [x0, #64]
+	ldp	x6, x7, [x0, #48]
+	ldp	x4, x5, [x0, #32]
+	ldp	x2, x3, [x0, #16]
+	ldp	x0, x1, [x0, #0]
+
+	/* "Conduit" arguments are already stored in x0..x17 */
+	\_conduit	#0
+
+	/*
+	 * Store value received in x0 as x0 will be used to compute addresses
+	 * to store the results.
+	 */
+	str	x0, [sp, #-16]!
+
+	/* Load address of smc_ret_values structure into x0 */
+	ldr	x0, [sp, #16]
+
+	/* Store values x1..x17 in the smc_ret_values structure */
+	stp	x16, x17, [x0, #128]
+	stp	x14, x15, [x0, #112]
+	stp	x12, x13, [x0, #96]
+	stp	x10, x11, [x0, #80]
+	stp	x8, x9, [x0, #64]
+	stp	x6, x7, [x0, #48]
+	stp	x4, x5, [x0, #32]
+	stp	x2, x3, [x0, #16]
+	str	x1, [x0, #8]
+
+	/*
+	 * Load previously stored value of x0 into x1 and store it in the
+	 * smc_ret_values structure. Return sp to its original position.
+	 */
+	ldr	x1, [sp], #32
+	str	x1, [x0, #0]
+
+	.endm
+
 /* ---------------------------------------------------------------------------
  * smc_ret_values asm_tftf_smc64(uint32_t fid,
  *				 u_register_t arg1,
@@ -56,6 +109,17 @@
 	ret
 endfunc asm_tftf_smc64
 
+	.globl asm_tftf_smc64_no_retval_x8
+
+/* ---------------------------------------------------------------------------
+ * void asm_tftf_smc64_no_retval_x8(smc_args *args, smc_ret_values *ret);
+ * ---------------------------------------------------------------------------
+ */
+func asm_tftf_smc64_no_retval_x8
+	smccc_conduit_nox8 smc
+	ret
+endfunc asm_tftf_smc64_no_retval_x8
+
 /* ---------------------------------------------------------------------------
  * hvc_ret_values asm_tftf_hvcc64(uint32_t fid,
  *				 u_register_t arg1,
diff --git a/lib/smc/aarch64/smc.c b/lib/smc/aarch64/smc.c
index 9912e72..b0fea4b 100644
--- a/lib/smc/aarch64/smc.c
+++ b/lib/smc/aarch64/smc.c
@@ -102,3 +102,19 @@
 			      args->arg6,
 			      args->arg7);
 }
+
+void tftf_smc_no_retval_x8(const smc_args_ext *args, smc_ret_values_ext *ret)
+{
+	uint32_t fid = args->fid;
+	/* Copy args into new structure so the fid field can be modified */
+	smc_args_ext args_copy = *args;
+
+	if (tftf_smc_get_sve_hint()) {
+		fid |= MASK(FUNCID_SVE_HINT);
+	} else {
+		fid &= ~MASK(FUNCID_SVE_HINT);
+	}
+	args_copy.fid = fid;
+
+	asm_tftf_smc64_no_retval_x8(&args_copy, ret);
+}