@@ -148,3 +148,45 @@ int ele_start_rng(struct device *dev)
return -EINVAL;
}
+
+int ele_service_swap(struct device *dev,
+ phys_addr_t addr,
+ u32 addr_size, u16 flag)
+{
+ struct ele_mu_priv *priv = dev_get_drvdata(dev);
+ int ret;
+ unsigned int tag, command, size, ver, status;
+
+ ret = plat_fill_cmd_msg_hdr(priv,
+ (struct mu_hdr *)&priv->tx_msg.header,
+ ELE_SERVICE_SWAP_REQ, 24);
+ if (ret)
+ return ret;
+
+ priv->tx_msg.data[0] = flag;
+ priv->tx_msg.data[1] = addr_size;
+ priv->tx_msg.data[2] = ELE_NONE_VAL;
+ priv->tx_msg.data[3] = lower_32_bits(addr);
+ priv->tx_msg.data[4] = plat_add_msg_crc((uint32_t *)&priv->tx_msg, 24);
+ ret = imx_ele_msg_send_rcv(priv);
+ if (ret < 0)
+ return ret;
+
+ tag = MSG_TAG(priv->rx_msg.header);
+ command = MSG_COMMAND(priv->rx_msg.header);
+ size = MSG_SIZE(priv->rx_msg.header);
+ ver = MSG_VER(priv->rx_msg.header);
+ status = RES_STATUS(priv->rx_msg.data[0]);
+ if (tag == priv->rsp_tag &&
+ command == ELE_SERVICE_SWAP_REQ &&
+ size == ELE_SERVICE_SWAP_REQ_MSG_SZ &&
+ ver == ELE_BASE_API_VERSION &&
+ status == priv->success_tag) {
+ if (flag == ELE_IMEM_EXPORT)
+ return priv->rx_msg.data[1];
+ else
+ return 0;
+ }
+
+ return -EINVAL;
+}
@@ -9,6 +9,18 @@
#include "ele_common.h"
+uint32_t plat_add_msg_crc(uint32_t *msg, uint32_t msg_len)
+{
+ uint32_t i;
+ uint32_t crc = 0;
+ uint32_t nb_words = msg_len / (uint32_t)sizeof(uint32_t);
+
+ for (i = 0; i < nb_words - 1; i++)
+ crc ^= *(msg + i);
+
+ return crc;
+}
+
int imx_ele_msg_send_rcv(struct ele_mu_priv *priv)
{
unsigned int wait;
@@ -72,3 +84,105 @@ int ele_do_start_rng(struct device *dev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+int save_imem(struct device *dev)
+{
+ int ret;
+ struct ele_mu_priv *priv = dev_get_drvdata(dev);
+
+ /* EXPORT command will save encrypted IMEM to given address,
+ * so later in resume, IMEM can be restored from the given
+ * address.
+ *
+ * Size must be at least 64 kB.
+ */
+ ret = ele_service_swap(dev,
+ priv->imem.phyaddr,
+ ELE_IMEM_SIZE,
+ ELE_IMEM_EXPORT);
+ if (ret < 0)
+ dev_err(dev, "Failed to export IMEM\n");
+ else
+ dev_info(dev,
+ "Exported %d bytes of encrypted IMEM\n",
+ ret);
+
+ return ret;
+}
+
+int restore_imem(struct device *dev,
+ uint8_t *pool_name)
+{
+ int ret;
+ u32 imem_state;
+ u32 *get_info_buf = NULL;
+ phys_addr_t get_info_phyaddr = 0;
+ struct ele_mu_priv *priv = dev_get_drvdata(dev);
+
+ get_info_phyaddr
+ = pool_name ? get_phy_buf_mem_pool(dev,
+ pool_name,
+ &get_info_buf,
+ DEVICE_GET_INFO_SZ)
+ : 0x0;
+
+ if (!get_info_buf) {
+ dev_err(dev, "Unable to alloc sram from sram pool\n");
+ return -ENOMEM;
+ }
+
+ ret = ele_do_start_rng(dev);
+ if (ret)
+ goto exit;
+
+ /* get info from ELE */
+ ret = ele_get_info(dev, get_info_phyaddr, ELE_GET_INFO_READ_SZ);
+ if (ret) {
+ dev_err(dev, "Failed to get info from ELE.\n");
+ goto exit;
+ }
+
+ /* Get IMEM state, if 0xFE then import IMEM */
+ imem_state = (get_info_buf[ELE_IMEM_STATE_WORD]
+ & ELE_IMEM_STATE_MASK) >> 16;
+ if (imem_state == ELE_IMEM_STATE_BAD) {
+ /* IMPORT command will restore IMEM from the given
+ * address, here size is the actual size returned by ELE
+ * during the export operation
+ */
+ ret = ele_service_swap(dev,
+ priv->imem.phyaddr,
+ priv->imem.size,
+ ELE_IMEM_IMPORT);
+ if (ret) {
+ dev_err(dev, "Failed to import IMEM\n");
+ goto exit;
+ }
+ } else
+ goto exit;
+
+ /* After importing IMEM, check if IMEM state is equal to 0xCA
+ * to ensure IMEM is fully loaded and
+ * ELE functionality can be used.
+ */
+ ret = ele_get_info(dev, get_info_phyaddr, ELE_GET_INFO_READ_SZ);
+ if (ret) {
+ dev_err(dev, "Failed to get info from ELE.\n");
+ goto exit;
+ }
+
+ imem_state = (get_info_buf[ELE_IMEM_STATE_WORD]
+ & ELE_IMEM_STATE_MASK) >> 16;
+ if (imem_state == ELE_IMEM_STATE_OK)
+ dev_info(dev, "Successfully restored IMEM\n");
+ else
+ dev_err(dev, "Failed to restore IMEM\n");
+
+exit:
+ if (pool_name && get_info_buf)
+ free_phybuf_mem_pool(dev, pool_name,
+ get_info_buf, DEVICE_GET_INFO_SZ);
+
+ return ret;
+}
+#endif
@@ -9,6 +9,7 @@
#include "se_fw.h"
+uint32_t plat_add_msg_crc(uint32_t *msg, uint32_t msg_len);
int imx_ele_msg_send_rcv(struct ele_mu_priv *priv);
#ifdef CONFIG_IMX_ELE_TRNG
int ele_trng_init(struct device *dev);
@@ -20,4 +21,11 @@ static inline int ele_trng_init(struct device *dev)
#endif
int ele_do_start_rng(struct device *dev);
+
+#ifdef CONFIG_PM_SLEEP
+int save_imem(struct device *dev);
+int restore_imem(struct device *dev,
+ uint8_t *pool_name);
#endif
+
+#endif /*__ELE_COMMON_H__ */
@@ -46,6 +46,7 @@ struct imx_info {
/* platform specific flag to enable/disable the ELE True RNG */
bool start_rng;
bool enable_ele_trng;
+ bool imem_mgmt;
};
static LIST_HEAD(priv_data_list);
@@ -63,6 +64,7 @@ static const struct imx_info imx8ulp_info = {
.init_fw = false,
.start_rng = true,
.enable_ele_trng = false,
+ .imem_mgmt = true,
};
static const struct imx_info imx93_info = {
@@ -78,6 +80,7 @@ static const struct imx_info imx93_info = {
.init_fw = true,
.start_rng = true,
.enable_ele_trng = true,
+ .imem_mgmt = false,
};
static const struct of_device_id se_fw_match[] = {
@@ -160,7 +163,7 @@ static void ele_mu_rx_callback(struct mbox_client *c, void *msg)
}
-static phys_addr_t get_phy_buf_mem_pool(struct device *dev,
+phys_addr_t get_phy_buf_mem_pool(struct device *dev,
char *mem_pool_name,
u32 **buf,
uint32_t size)
@@ -182,7 +185,7 @@ static phys_addr_t get_phy_buf_mem_pool(struct device *dev,
return gen_pool_virt_to_phys(mem_pool, (ulong)*buf);
}
-static void free_phybuf_mem_pool(struct device *dev,
+void free_phybuf_mem_pool(struct device *dev,
char *mem_pool_name,
u32 *buf,
uint32_t size)
@@ -959,6 +962,17 @@ static int se_probe_cleanup(struct platform_device *pdev)
priv->flags &= (~RESERVED_DMA_POOL);
}
+ /* free the buffer in ele-mu remove, previously allocated
+ * in ele-mu probe to store encrypted IMEM
+ */
+ if (priv->imem.buf) {
+ dmam_free_coherent(&pdev->dev,
+ ELE_IMEM_SIZE,
+ priv->imem.buf,
+ priv->imem.phyaddr);
+ priv->imem.buf = NULL;
+ }
+
if (priv->ctxs) {
for (i = 0; i < priv->max_dev_ctx; i++) {
if (priv->ctxs[i])
@@ -1160,6 +1174,19 @@ static int se_fw_probe(struct platform_device *pdev)
dev_err(dev, "Failed to init ele-trng\n");
}
+ if (info->imem_mgmt) {
+ /* allocate buffer where ELE store encrypted IMEM */
+ priv->imem.buf = dmam_alloc_coherent(dev, ELE_IMEM_SIZE,
+ &priv->imem.phyaddr,
+ GFP_KERNEL);
+ if (!priv->imem.buf) {
+ dev_err(dev,
+ "dmam-alloc-failed: To store encr-IMEM.\n");
+ ret = -ENOMEM;
+ goto exit;
+ }
+ }
+
pr_info("i.MX secure-enclave: %s's mu#%d interface to firmware, configured.\n",
info->se_name,
priv->ele_mu_id);
@@ -1196,17 +1223,31 @@ static int se_fw_remove(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP
static int se_fw_suspend(struct device *dev)
{
+ struct ele_mu_priv *priv = dev_get_drvdata(dev);
+ const struct of_device_id *of_id = of_match_device(se_fw_match, dev);
+ struct imx_info *info = (of_id != NULL) ? (struct imx_info *)of_id->data
+ : NULL;
+
+ if (info && info->imem_mgmt)
+ priv->imem.size = save_imem(dev);
+
return 0;
}
static int se_fw_resume(struct device *dev)
{
struct ele_mu_priv *priv = dev_get_drvdata(dev);
+ const struct of_device_id *of_id = of_match_device(se_fw_match, dev);
+ struct imx_info *info = (of_id != NULL) ? (struct imx_info *)of_id->data
+ : NULL;
int i;
for (i = 0; i < priv->max_dev_ctx; i++)
wake_up_interruptible(&priv->ctxs[i]->wq);
+ if (info && info->imem_mgmt)
+ restore_imem(dev, info->pool_name);
+
return 0;
}
#endif
@@ -165,4 +165,12 @@ struct ele_mu_priv {
struct ele_imem_buf imem;
};
+phys_addr_t get_phy_buf_mem_pool(struct device *dev,
+ char *mem_pool_name,
+ u32 **buf,
+ uint32_t size);
+void free_phybuf_mem_pool(struct device *dev,
+ char *mem_pool_name,
+ u32 *buf,
+ uint32_t size);
#endif
@@ -37,12 +37,23 @@
#define ELE_GET_TRNG_STATE_RETRY_COUNT 0x5
#define CSAL_TRNG_STATE_MASK 0x0000ffff
+#define ELE_SERVICE_SWAP_REQ 0xDF
+#define ELE_SERVICE_SWAP_REQ_MSG_SZ 0x03
+#define ELE_IMEM_SIZE 0x10000
+#define ELE_IMEM_STATE_OK 0xCA
+#define ELE_IMEM_STATE_BAD 0xFE
+#define ELE_IMEM_STATE_WORD 0x27
+#define ELE_IMEM_STATE_MASK 0x00ff0000
+#define ELE_IMEM_EXPORT 0x1
+#define ELE_IMEM_IMPORT 0x2
+
#define ELE_BASE_API_VERSION 0x6
-#define ELE_SUCCESS_IND 0xD6
-#define ELE_FAILURE_IND 0x29
int ele_get_info(struct device *dev, phys_addr_t addr, u32 data_size);
int ele_start_rng(struct device *dev);
int ele_get_trng_state(struct device *dev);
+int ele_service_swap(struct device *dev,
+ phys_addr_t addr,
+ u32 addr_size, u16 flag);
#endif