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