feat(lib/rmm_el3_ifc): support for attestation via EL3
Add support functions to be able to push attestation signing
requests to EL3. The patch also adds some additional comments
to the rmm_el3_ifc.h file
Change-Id: I801ac4ab9cb232e8e8a5a54ec1238048f4285f51
Signed-off-by: Raghu Krishnamurthy <raghupathyk@nvidia.com>
diff --git a/lib/rmm_el3_ifc/include/rmm_el3_ifc.h b/lib/rmm_el3_ifc/include/rmm_el3_ifc.h
index 2ad4d5e..2555b9b 100644
--- a/lib/rmm_el3_ifc/include/rmm_el3_ifc.h
+++ b/lib/rmm_el3_ifc/include/rmm_el3_ifc.h
@@ -10,12 +10,13 @@
#include <smc.h>
#ifndef __ASSEMBLER__
+#include <stdbool.h>
#include <stddef.h>
#endif
-/*************************************
- * SMC codes for the EL3-RMM interface
- *************************************/
+/***************************************
+ * Error codes for the EL3-RMM interface
+ ***************************************/
#define E_RMM_OK (0)
#define E_RMM_UNK (-1)
@@ -25,6 +26,20 @@
#define E_RMM_INVAL (-5)
#define E_RMM_AGAIN (-6)
+/****************************************
+ * Generic defines for RMM-EL3 interface
+ ****************************************/
+
+/* Signature Algorithm ID */
+#define ATTEST_KEY_CURVE_ECC_SECP384R1 (0U)
+
+/* Hash Algorithm ID */
+#define EL3_TOKEN_SIGN_HASH_ALG_SHA384 (1U)
+
+/********************************************
+ * SMC Function IDs for the EL3-RMM interface
+ ********************************************/
+
/* 0x1B0 - 0x1B1 */
#define SMC_RMM_GTSI_DELEGATE SMC64_STD_FID(RMM_EL3, U(0))
#define SMC_RMM_GTSI_UNDELEGATE SMC64_STD_FID(RMM_EL3, U(1))
@@ -46,12 +61,20 @@
#define E_RMM_BOOT_MANIFEST_VERSION_NOT_SUPPORTED (-6)
#define E_RMM_BOOT_MANIFEST_DATA_ERROR (-7)
+/* Starting RMM-EL3 interface version 0.4 */
+ /* 0x1B4 */
+#define SMC_RMM_EL3_FEATURES SMC64_STD_FID(RMM_EL3, U(4))
+
+ /* 0x1B5 */
+#define SMC_RMM_EL3_TOKEN_SIGN SMC64_STD_FID(RMM_EL3, U(5))
+
+
/************************
* Version related macros
************************/
/*
- * Boot Interface and Manifest versions encoding:
+ * RMM-EL3 Interface and Boot Manifest versions encoding:
* - Bit[31] RES0
* - Bits [30:16] Major version
* - Bits [15:0] Minor version
@@ -65,13 +88,13 @@
(((((_major) & 0x7FFFU) << 16) | ((_minor) & 0xFFFFU)))
/*
- * The Major version value for the Boot Interface supported by this
+ * The Major version value for the RMM-EL3 Interface supported by this
* implementation of RMM.
*/
#define RMM_EL3_IFC_VERS_MAJOR (U(0))
/*
- * The Minor version value for the Boot interface supported by this
+ * The Minor version value for the RMM-EL3 Interface supported by this
* implementation of RMM.
*/
#define RMM_EL3_IFC_VERS_MINOR (U(3))
@@ -113,12 +136,61 @@
(RMM_EL3_MANIFEST_GET_VERS_MINOR(_version) >= RMM_EL3_MANIFEST_VERS_MINOR))
#ifndef __ASSEMBLER__
-/****************************************************************************
- * Boot interface related functions
+
+/***************************************************************************
+ * SMC_RMM_EL3_FEATURES related definitions
+ ***************************************************************************/
+#define RMM_EL3_IFC_FEAT_REG_0_IDX U(0)
+#define RMM_EL3_IFC_FEAT_REG_0_EL3_TOKEN_SIGN_SHIFT U(0)
+#define RMM_EL3_IFC_FEAT_REG_0_EL3_TOKEN_SIGN_WIDTH U(1)
+
+/***************************************************************************
+ * SMC_RMM_EL3_TOKEN_SIGN related definitions and structures
+ ***************************************************************************/
+
+/* Command code for SMC_RMM_EL3_TOKEN_SIGN SMC */
+#define SMC_RMM_EL3_TOKEN_SIGN_PUSH_REQ_OP U(1)
+#define SMC_RMM_EL3_TOKEN_SIGN_PULL_RESP_OP U(2)
+#define SMC_RMM_EL3_TOKEN_SIGN_GET_RAK_PUB_OP U(3)
+
+/* The Max hash size is set to be SHA-512 Digest size */
+#define EL3_TOKEN_REQUEST_MAX_HASH_LEN 64
+/* The Max Signature is set to be a buffer of 512 Bytes (4096 bits) */
+#define EL3_TOKEN_RESPONSE_MAX_SIG_LEN 512
+
+/* Structure format in which EL3 expects a request */
+struct el3_token_sign_request {
+ SET_MEMBER(uint32_t sign_alg_id, 0x0, 0x8);
+ SET_MEMBER(uint64_t cookie, 0x8, 0x10); /* Passthrough to response */
+ SET_MEMBER(uint64_t req_ticket, 0x10, 0x18); /* Passthrough to response */
+ SET_MEMBER(uint32_t hash_alg_id, 0x18, 0x20);
+ SET_MEMBER(uint8_t hash_buf[EL3_TOKEN_REQUEST_MAX_HASH_LEN], 0x20, 0x60);
+};
+COMPILER_ASSERT(U(offsetof(struct el3_token_sign_request, sign_alg_id)) == 0x0U);
+COMPILER_ASSERT(U(offsetof(struct el3_token_sign_request, cookie)) == 0x8U);
+COMPILER_ASSERT(U(offsetof(struct el3_token_sign_request, req_ticket)) == 0x10U);
+COMPILER_ASSERT(U(offsetof(struct el3_token_sign_request, hash_alg_id)) == 0x18U);
+COMPILER_ASSERT(U(offsetof(struct el3_token_sign_request, hash_buf)) == 0x20U);
+
+/* Structure format in which EL3 is expected to return data */
+struct el3_token_sign_response {
+ SET_MEMBER(uint64_t cookie, 0x0, 0x8); /* Passthrough from request */
+ SET_MEMBER(uint64_t req_ticket, 0x8, 0x10); /* Passthrough from request */
+ SET_MEMBER(uint16_t sig_len, 0x10, 0x12);
+ SET_MEMBER(uint8_t signature_buf[EL3_TOKEN_RESPONSE_MAX_SIG_LEN], 0x12, 0x212);
+};
+
+COMPILER_ASSERT(U(offsetof(struct el3_token_sign_response, cookie)) == 0x0U);
+COMPILER_ASSERT(U(offsetof(struct el3_token_sign_response, req_ticket)) == 0x8U);
+COMPILER_ASSERT(U(offsetof(struct el3_token_sign_response, sig_len)) == 0x10U);
+COMPILER_ASSERT(U(offsetof(struct el3_token_sign_response, signature_buf)) == 0x12U);
+
+/***************************************************************************
+ * RMM-EL3 Interface related functions
***************************************************************************/
/*
- * Accessors to the parameters obtained through the boot interface arguments.
+ * Accessors to the parameters obtained through the RMM-EL3 Interface arguments.
*/
unsigned int rmm_el3_ifc_get_version(void);
uintptr_t rmm_el3_ifc_get_shared_buf_pa(void);
@@ -129,8 +201,9 @@
}
/*
- * Validate the boot arguments and Initialize the rmm_el3_ifc library.
- * This function must be called only once during cold boot.
+ * Validate the RMM-EL3 Interface boot arguments and initialize the
+ * rmm_el3_ifc library. This function must be called only once during cold
+ * boot.
*
* This function must be called prior to enable the MMU and data cache for
* RMM execution.
@@ -277,7 +350,7 @@
int rmm_el3_ifc_get_console_list_pa(struct console_list **plat_console_list);
/****************************************************************************
- * RMM-EL3 Runtime APIs
+ * RMM-EL3 Runtime interface APIs
***************************************************************************/
/*
@@ -294,7 +367,11 @@
* - crv: ECC Crve type for querying attestation key from monitor.
*
* Return:
- * - 0 On success or a negative error code otherwise.
+ * - E_RMM_OK On Success.
+ * - E_RMM_INVAL If the arguments are invalid.
+ * - E_RMM_AGAIN EL3 is busy and the key cannot be retrieved at this
+ * time. At least one further call will be needed in
+ * order to retrieve the key.
*/
int rmm_el3_ifc_get_realm_attest_key(uintptr_t buf, size_t buflen,
size_t *len, unsigned int crv);
@@ -363,5 +440,85 @@
*/
__dead2 void rmm_el3_ifc_report_fail_to_el3(int ec);
+/*
+ * Query if EL3_TOKEN_SIGN feature is supported by EL3 firmware.
+ * Return true or false depending on the support.
+ */
+bool rmm_el3_ifc_el3_token_sign_supported(void);
+
+/*
+ * Push the attestation signing request to EL3 firmware.
+ * Optional interface from the RMM-EL3 interface v0.4 onwards.
+ *
+ * Args:
+ * - req: Pointer to the token sign request to be pushed to EL3.
+ * The structure must be located in the RMM-EL3 shared
+ * memory buffer and must be locked before use.
+ *
+ * Return:
+ * - E_RMM_OK On Success.
+ * - E_RMM_INVAL If the arguments are invalid.
+ * - E_RMM_AGAIN Indicates that the request was not queued since the
+ * queue in EL3 is full.
+ * - E_RMM_UNK If the SMC is not implemented or if interface
+ * version is < 0.4.
+ */
+int rmm_el3_ifc_push_el3_token_sign_request(const struct el3_token_sign_request *req);
+
+/*
+ * Pull the attestation signing response from the EL3 firmware.
+ * Optional interface from the RMM-EL3 interface v0.4 onwards.
+ * Args:
+ * - resp: Pointer to the token sign response to get from EL3.
+ * The structure must be located in the RMM-EL3 shared
+ * memory buffer and must be locked before use.
+ *
+ * Return:
+ * - E_RMM_OK On Success.
+ * - E_RMM_INVAL If the arguments are invalid.
+ * - E_RMM_AGAIN Indicates that a response is not ready yet.
+ * - E_RMM_UNK If the SMC is not implemented or if interface
+ * version is < 0.4.
+ */
+int rmm_el3_ifc_pull_el3_token_sign_response(const struct el3_token_sign_response *resp);
+
+/*
+ * Get the realm attestation public key when EL3 is used to sign attestaion
+ * tokens.
+ * Optional interface from the RMM-EL3 interface v0.4 onwards.
+ * Args:
+ * - buf Pointer to the buffer used to get the attestation public key
+ * This must belong to the RMM-EL3 shared memory and must be locked
+ * before use.
+ * - buflen The size of the buffer `buf`.
+ * - len Pointer to a size_t variable to store the size of the
+ * received response.
+ * - crv The ECC curve for which the public key is requested.
+ *
+ * Return:
+ * - E_RMM_OK On Success.
+ * - E_RMM_INVAL If the arguments are invalid.
+ * - E_RMM_AGAIN Indicates that a response is not ready yet.
+ * - E_RMM_UNK If the SMC is not implemented or if interface
+ * version is < 0.4.
+ */
+int rmm_el3_ifc_get_realm_attest_pub_key_from_el3(uintptr_t buf, size_t buflen,
+ size_t *len, unsigned int crv);
+
+/*
+ * Access the feature register. This is supported for interface version 0.4 and
+ * later.
+ *
+ * Args:
+ * - feat_reg_idx The feature register index.
+ * - feat_reg Pointer to store the value of the register.
+ * Return:
+ * - E_RMM_OK On Success.
+ * - E_RMM_INVAL If the arguments are invalid.
+ * - E_RMM_UNK If the SMC is not present if interface
+ * version is < 0.4.
+ */
+int rmm_el3_ifc_get_feat_register(unsigned int feat_reg_idx, uint64_t *feat_reg);
+
#endif /* __ASSEMBLER__ */
#endif /* RMM_EL3_IFC_H */
diff --git a/lib/rmm_el3_ifc/src/rmm_el3_runtime.c b/lib/rmm_el3_ifc/src/rmm_el3_runtime.c
index 93f60c0..9f493ad 100644
--- a/lib/rmm_el3_ifc/src/rmm_el3_runtime.c
+++ b/lib/rmm_el3_ifc/src/rmm_el3_runtime.c
@@ -5,6 +5,7 @@
#include <assert.h>
#include <debug.h>
+#include <errno.h>
#include <rmm_el3_ifc.h>
#include <rmm_el3_ifc_priv.h>
#include <spinlock.h>
@@ -16,6 +17,27 @@
/* Spinlock used to protect the EL3<->RMM shared area */
static spinlock_t shared_area_lock = {0U};
+/* Helper to detect whether EL3_TOKEN_SIGN is supported by EL3 */
+bool rmm_el3_ifc_el3_token_sign_supported(void)
+{
+ static uint64_t feat_reg;
+ static bool feat_reg_read;
+
+ if (!feat_reg_read) {
+ int ret;
+ ret = rmm_el3_ifc_get_feat_register(RMM_EL3_IFC_FEAT_REG_0_IDX,
+ &feat_reg);
+ if (ret != 0) {
+ ERROR("Failed to get feature register\n");
+ return false;
+ }
+
+ feat_reg_read = true;
+ }
+
+ return (feat_reg & MASK(RMM_EL3_IFC_FEAT_REG_0_EL3_TOKEN_SIGN)) != 0UL;
+}
+
/*
* Get and lock a pointer to the start of the RMM<->EL3 shared buffer.
*/
@@ -34,14 +56,8 @@
spinlock_release(&shared_area_lock);
}
-/*
- * Get the realm attestation key to sign the realm attestation token. It is
- * expected that only the private key is retrieved in raw format.
- */
-int rmm_el3_ifc_get_realm_attest_key(uintptr_t buf, size_t buflen,
- size_t *len, unsigned int crv)
+static unsigned long get_buffer_pa(uintptr_t buf, size_t buflen)
{
- struct smc_result smc_res;
unsigned long buffer_pa;
unsigned long offset = buf - rmm_shared_buffer_start_va;
@@ -50,15 +66,25 @@
buffer_pa = (unsigned long)rmm_el3_ifc_get_shared_buf_pa() + offset;
- monitor_call_with_res(SMC_RMM_GET_REALM_ATTEST_KEY,
- buffer_pa,
+ return buffer_pa;
+}
+
+static int rmm_el3_ifc_get_realm_attest_key_internal(uintptr_t buf,
+ size_t buflen, size_t *len,
+ unsigned int crv,
+ unsigned long id)
+{
+ struct smc_result smc_res;
+
+ monitor_call_with_res(id,
+ get_buffer_pa(buf, buflen),
buflen,
crv, 0UL, 0UL, 0UL, &smc_res);
/* coverity[uninit_use:SUPPRESS] */
if (smc_res.x[0] != 0UL) {
ERROR("Failed to get realm attestation key x0 = 0x%lx\n",
- smc_res.x[0]);
+ smc_res.x[0]);
return (int)smc_res.x[0];
}
@@ -68,6 +94,17 @@
}
/*
+ * Get the realm attestation key to sign the realm attestation token. It is
+ * expected that only the private key is retrieved in raw format.
+ */
+int rmm_el3_ifc_get_realm_attest_key(uintptr_t buf, size_t buflen, size_t *len,
+ unsigned int crv)
+{
+ return rmm_el3_ifc_get_realm_attest_key_internal(
+ buf, buflen, len, crv, SMC_RMM_GET_REALM_ATTEST_KEY);
+}
+
+/*
* Get the platform token from the EL3 firmware.
* The caller must have already populated the public hash in `buf` which is an
* input for platform token computation.
@@ -78,17 +115,10 @@
size_t *remaining_len)
{
struct smc_result smc_res;
- unsigned long buffer_pa;
- unsigned long offset = buf - rmm_shared_buffer_start_va;
-
- assert((offset + buflen) <= rmm_el3_ifc_get_shared_buf_size());
- assert((buf & ~PAGE_SIZE_MASK) == rmm_shared_buffer_start_va);
-
- buffer_pa = (unsigned long)rmm_el3_ifc_get_shared_buf_pa() + offset;
/* Get the available space on the buffer after the offset */
monitor_call_with_res(SMC_RMM_GET_PLAT_TOKEN,
- buffer_pa,
+ get_buffer_pa(buf, buflen),
buflen,
hash_size,
0UL, 0UL, 0UL, &smc_res);
@@ -105,3 +135,132 @@
return (int)smc_res.x[0];
}
+
+/*
+ * Push an attestation request to EL3.
+ * The caller must have already populated the request in the shared buffer.
+ * The push operation may fail if EL3 does not have enough queue space or if
+ * the EL3 is not ready to accept the request.
+ */
+/* coverity[misra_c_2012_rule_8_7_violation:SUPPRESS] */
+int rmm_el3_ifc_push_el3_token_sign_request(
+ const struct el3_token_sign_request *req)
+{
+ struct smc_result smc_res;
+
+ if (!rmm_el3_ifc_el3_token_sign_supported()) {
+ ERROR("EL3 does not support token signing\n");
+ return E_RMM_UNK;
+ }
+
+ monitor_call_with_res(SMC_RMM_EL3_TOKEN_SIGN,
+ SMC_RMM_EL3_TOKEN_SIGN_PUSH_REQ_OP,
+ get_buffer_pa((uintptr_t)req, sizeof(*req)),
+ sizeof(*req),
+ 0UL, 0UL, 0UL, &smc_res);
+
+ /* coverity[uninit_use:SUPPRESS] */
+ if (smc_res.x[0] != 0UL) {
+ VERBOSE("Failed to push token sign req to EL3 x0 = 0x%lx\n",
+ smc_res.x[0]);
+ return (int)smc_res.x[0];
+ }
+
+ return 0;
+}
+
+/*
+ * Pull an attestation response from EL3. The pull operation may fail if
+ * the EL3 is not yet ready to provide a response.
+ */
+/* coverity[misra_c_2012_rule_8_7_violation:SUPPRESS] */
+int rmm_el3_ifc_pull_el3_token_sign_response(
+ const struct el3_token_sign_response *resp)
+{
+ struct smc_result smc_res;
+
+ if (!rmm_el3_ifc_el3_token_sign_supported()) {
+ ERROR("EL3 does not support token signing\n");
+ return E_RMM_UNK;
+ }
+
+ monitor_call_with_res(SMC_RMM_EL3_TOKEN_SIGN,
+ SMC_RMM_EL3_TOKEN_SIGN_PULL_RESP_OP,
+ get_buffer_pa((uintptr_t)resp, sizeof(*resp)),
+ sizeof(*resp),
+ 0UL, 0UL, 0UL, &smc_res);
+
+ /* coverity[uninit_use:SUPPRESS] */
+ if (smc_res.x[0] != 0UL) {
+ VERBOSE("Failed to get token sign response x0 = 0x%lx\n",
+ smc_res.x[0]);
+ return (int)smc_res.x[0];
+ }
+
+ return 0;
+}
+
+/*
+ * Get the realm attestation public key from EL3. This is required when
+ * token signing is done in EL3.
+ */
+/* coverity[misra_c_2012_rule_8_7_violation:SUPPRESS] */
+int rmm_el3_ifc_get_realm_attest_pub_key_from_el3(uintptr_t buf, size_t buflen,
+ size_t *len, unsigned int crv)
+{
+ struct smc_result smc_res;
+
+ if (!rmm_el3_ifc_el3_token_sign_supported()) {
+ ERROR("EL3 does not support token signing\n");
+ return E_RMM_UNK;
+ }
+
+ monitor_call_with_res(SMC_RMM_EL3_TOKEN_SIGN,
+ SMC_RMM_EL3_TOKEN_SIGN_GET_RAK_PUB_OP,
+ get_buffer_pa(buf, buflen),
+ buflen,
+ crv, 0UL, 0UL, &smc_res);
+
+ /* coverity[uninit_use:SUPPRESS] */
+ if (smc_res.x[0] != 0UL) {
+ ERROR("Failed to get realm attestation key x0 = 0x%lx\n",
+ smc_res.x[0]);
+ return (int)smc_res.x[0];
+ }
+
+ *len = smc_res.x[1];
+
+ return 0;
+}
+
+/*
+ * Access the feature register. This is supported for interface version 0.4 and
+ * later.
+ */
+int rmm_el3_ifc_get_feat_register(unsigned int feat_reg_idx, uint64_t *feat_reg)
+{
+ struct smc_result smc_res;
+ unsigned long rmm_el3_ifc_version = rmm_el3_ifc_get_version();
+
+ if ((RMM_EL3_IFC_GET_VERS_MAJOR(rmm_el3_ifc_version) != 0U) ||
+ (RMM_EL3_IFC_GET_VERS_MINOR(rmm_el3_ifc_version) < 4U)) {
+ ERROR("Feature register access not supported by this version 0x%lx\n",
+ rmm_el3_ifc_version);
+ return E_RMM_UNK;
+ }
+
+ monitor_call_with_res(SMC_RMM_EL3_FEATURES,
+ feat_reg_idx,
+ 0UL, 0UL, 0UL, 0UL, 0UL, &smc_res);
+
+ /* coverity[uninit_use:SUPPRESS] */
+ if (smc_res.x[0] != 0UL) {
+ ERROR("Failed to get feature register x0 = 0x%lx\n",
+ smc_res.x[0]);
+ return (int)smc_res.x[0];
+ }
+
+ *feat_reg = smc_res.x[1];
+
+ return 0;
+}