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);
+}