LF-13910-5: drivers: firmware: imx: add support for imx93 soc

Add support for imx93 soc and its platforms.

Signed-off-by: Pankaj Gupta <pankaj.gupta@nxp.com>
Acked-by: Rahul Kumar Yadav <rahulkumar.yadav@nxp.com>
Acked-by: Vabhav Sharma <vabhav.sharma@nxp.com>
Acked-by: Meenakshi Aggarwal <meenakshi.aggarwal@nxp.com>
Acked-by: Jason Liu <jason.hui.liu@nxp.com>
This commit is contained in:
Pankaj Gupta 2024-11-12 15:32:20 +05:30 committed by Jason Liu
parent 2c2034c656
commit c44540c6a8
10 changed files with 568 additions and 3 deletions

View File

@ -52,3 +52,16 @@ config IMX_SEC_ENCLAVE
like base, HSM, V2X & SHE using the SAB protocol via the shared Messaging
Unit. This driver exposes these interfaces via a set of file descriptors
allowing to configure shared memory, send and receive messages.
config IMX_ELE_TRNG
bool "i.MX ELE True Random Number Generator"
depends on IMX_SEC_ENCLAVE
default y
select CRYPTO_RNG
select HW_RANDOM
help
With platforms having secure enclave as the only true source for
generating true random number, the config CONFIG_ELE_TRNG needs to be enabled.
This config helps provides kernel-side support for the Random Number generation,
through NXP hardware IP for secure-enclave(s), called EdgeLock Enclave.

View File

@ -4,5 +4,6 @@ obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o rm.o imx-scu-soc.o seco
obj-${CONFIG_IMX_SCMI_MISC_CONTROL} += sm-misc.o
obj-$(CONFIG_IMX_SCMI_BBM_CONTROL) += sm-bbm.o
obj-$(CONFIG_IMX_ISPMU) += isp-mu.o
sec_enclave-objs = se_ctrl.o ele_common.o ele_base_msg.o
sec_enclave-objs = se_ctrl.o ele_common.o ele_base_msg.o ele_fw_api.o
obj-${CONFIG_IMX_SEC_ENCLAVE} += sec_enclave.o
sec_enclave-${CONFIG_IMX_ELE_TRNG} += ele_trng.o

View File

@ -404,3 +404,252 @@ int ele_debug_dump(struct se_if_priv *priv)
exit:
return ret;
}
/*
* ele_start_rng() - prepare and send the command to start
* initialization of the ELE RNG context
*
* returns: 0 on success.
*/
int ele_start_rng(struct se_if_priv *priv)
{
struct se_api_msg *tx_msg __free(kfree) = NULL;
struct se_api_msg *rx_msg __free(kfree) = NULL;
int ret = 0;
if (!priv) {
ret = -EINVAL;
goto exit;
}
tx_msg = kzalloc(ELE_START_RNG_REQ_MSG_SZ, GFP_KERNEL);
if (!tx_msg) {
ret = -ENOMEM;
goto exit;
}
rx_msg = kzalloc(ELE_START_RNG_RSP_MSG_SZ, GFP_KERNEL);
if (!rx_msg) {
ret = -ENOMEM;
goto exit;
}
ret = se_fill_cmd_msg_hdr(priv,
(struct se_msg_hdr *)&tx_msg->header,
ELE_START_RNG_REQ,
ELE_START_RNG_REQ_MSG_SZ,
true);
if (ret)
goto exit;
ret = ele_msg_send_rcv(priv->priv_dev_ctx,
tx_msg,
ELE_START_RNG_REQ_MSG_SZ,
rx_msg,
ELE_START_RNG_RSP_MSG_SZ);
if (ret < 0)
goto exit;
ret = se_val_rsp_hdr_n_status(priv,
rx_msg,
ELE_START_RNG_REQ,
ELE_START_RNG_RSP_MSG_SZ,
true);
exit:
return ret;
}
int ele_write_fuse(struct se_if_priv *priv, u16 fuse_index,
u32 value, bool block)
{
struct se_api_msg *tx_msg __free(kfree) = NULL;
struct se_api_msg *rx_msg __free(kfree) = NULL;
int ret = 0;
if (!priv) {
ret = -EINVAL;
goto exit;
}
tx_msg = kzalloc(ELE_WRITE_FUSE_REQ_MSG_SZ, GFP_KERNEL);
if (!tx_msg) {
ret = -ENOMEM;
goto exit;
}
rx_msg = kzalloc(ELE_WRITE_FUSE_RSP_MSG_SZ, GFP_KERNEL);
if (!rx_msg) {
ret = -ENOMEM;
goto exit;
}
ret = se_fill_cmd_msg_hdr(priv,
(struct se_msg_hdr *)&tx_msg->header,
ELE_WRITE_FUSE,
ELE_WRITE_FUSE_REQ_MSG_SZ,
true);
if (ret)
goto exit;
tx_msg->data[0] = (32 << 16) | (fuse_index << 5);
if (block)
tx_msg->data[0] |= BIT(31);
tx_msg->data[1] = value;
ret = ele_msg_send_rcv(priv->priv_dev_ctx,
tx_msg,
ELE_WRITE_FUSE_REQ_MSG_SZ,
rx_msg,
ELE_WRITE_FUSE_RSP_MSG_SZ);
if (ret < 0)
goto exit;
ret = se_val_rsp_hdr_n_status(priv,
rx_msg,
ELE_WRITE_FUSE,
ELE_WRITE_FUSE_RSP_MSG_SZ,
true);
exit:
return ret;
}
/**
* read_common_fuse() - Brief description of function.
* @struct device *dev: Device to send the request to read fuses.
* @u16 fuse_id: Fuse identifier to read.
* @u32 *value: unsigned integer array to store the fused-values.
*
* Secure-enclave like EdgeLock Enclave, manages the fuse. This API
* requests FW to read the common fuses. FW sends the read value as
* response.
*
* Context: This function takes two mutex locks: one on the command
* and second on the message unit.
* such that multiple commands cannot be sent.
* for the device Describes whether the function can sleep, what locks it takes,
* releases, or expects to be held. It can extend over multiple
* lines.
* Return: Describe the return value of function_name.
*
* The return value description can also have multiple paragraphs, and should
* be placed at the end of the comment block.
*/
int read_common_fuse(struct se_if_priv *priv,
u16 fuse_id, u32 *value)
{
struct se_api_msg *tx_msg __free(kfree) = NULL;
struct se_api_msg *rx_msg __free(kfree) = NULL;
int rx_msg_sz = ELE_READ_FUSE_RSP_MSG_SZ;
int ret = 0;
if (!priv) {
ret = -EINVAL;
goto exit;
}
tx_msg = kzalloc(ELE_READ_FUSE_REQ_MSG_SZ, GFP_KERNEL);
if (!tx_msg) {
ret = -ENOMEM;
goto exit;
}
if (fuse_id == OTP_UNIQ_ID)
rx_msg_sz = ELE_READ_FUSE_OTP_UNQ_ID_RSP_MSG_SZ;
rx_msg = kzalloc(rx_msg_sz, GFP_KERNEL);
if (!rx_msg) {
ret = -ENOMEM;
goto exit;
}
ret = se_fill_cmd_msg_hdr(priv, (struct se_msg_hdr *)&tx_msg->header,
ELE_READ_FUSE_REQ, ELE_READ_FUSE_REQ_MSG_SZ,
true);
if (ret) {
dev_err(priv->dev, "Error: se_fill_cmd_msg_hdr failed.\n");
goto exit;
}
tx_msg->data[0] = fuse_id;
ret = ele_msg_send_rcv(priv->priv_dev_ctx,
tx_msg,
ELE_READ_FUSE_REQ_MSG_SZ,
rx_msg,
rx_msg_sz);
if (ret < 0)
goto exit;
ret = se_val_rsp_hdr_n_status(priv,
rx_msg,
ELE_READ_FUSE_REQ,
rx_msg_sz,
true);
if (ret)
goto exit;
switch (fuse_id) {
case OTP_UNIQ_ID:
value[0] = rx_msg->data[1];
value[1] = rx_msg->data[2];
value[2] = rx_msg->data[3];
value[3] = rx_msg->data[4];
break;
default:
value[0] = rx_msg->data[1];
break;
}
exit:
return ret;
}
int ele_voltage_change_req(struct se_if_priv *priv, bool start)
{
struct se_api_msg *tx_msg __free(kfree) = NULL;
struct se_api_msg *rx_msg __free(kfree) = NULL;
u8 cmd = start ? ELE_VOLT_CHANGE_START_REQ : ELE_VOLT_CHANGE_FINISH_REQ;
int ret = 0;
if (!priv) {
ret = -EINVAL;
goto exit;
}
tx_msg = kzalloc(ELE_VOLT_CHANGE_REQ_MSG_SZ, GFP_KERNEL);
if (!tx_msg) {
ret = -ENOMEM;
goto exit;
}
rx_msg = kzalloc(ELE_VOLT_CHANGE_RSP_MSG_SZ, GFP_KERNEL);
if (!rx_msg) {
ret = -ENOMEM;
goto exit;
}
ret = se_fill_cmd_msg_hdr(priv,
(struct se_msg_hdr *)&tx_msg->header,
cmd,
ELE_VOLT_CHANGE_REQ_MSG_SZ,
true);
if (ret)
goto exit;
ret = ele_msg_send_rcv(priv->priv_dev_ctx,
tx_msg,
ELE_VOLT_CHANGE_REQ_MSG_SZ,
rx_msg,
ELE_VOLT_CHANGE_RSP_MSG_SZ);
if (ret < 0)
goto exit;
ret = se_val_rsp_hdr_n_status(priv,
rx_msg,
cmd,
ELE_VOLT_CHANGE_RSP_MSG_SZ,
true);
exit:
return ret;
}

View File

@ -87,6 +87,27 @@ struct ele_dev_info {
#define ELE_FW_AUTH_REQ_SZ 0x10
#define ELE_FW_AUTH_RSP_MSG_SZ 0x08
#define ELE_START_RNG_REQ 0xA3
#define ELE_START_RNG_REQ_MSG_SZ 0x04
#define ELE_START_RNG_RSP_MSG_SZ 0x08
#define ELE_WRITE_FUSE 0xD6
#define ELE_WRITE_FUSE_REQ_MSG_SZ 12
#define ELE_WRITE_FUSE_RSP_MSG_SZ 12
#define ELE_READ_FUSE_REQ 0x97
#define ELE_READ_FUSE_REQ_MSG_SZ 0x08
#define ELE_READ_FUSE_RSP_MSG_SZ 0x0C
#define ELE_READ_FUSE_OTP_UNQ_ID_RSP_MSG_SZ \
0x1C
#define OTP_UNIQ_ID 0x01
#define OTFAD_CONFIG 0x2
#define ELE_VOLT_CHANGE_START_REQ 0x12
#define ELE_VOLT_CHANGE_FINISH_REQ 0x13
#define ELE_VOLT_CHANGE_REQ_MSG_SZ 0x4
#define ELE_VOLT_CHANGE_RSP_MSG_SZ 0x8
int ele_get_info(struct se_if_priv *priv, struct ele_dev_info *s_info);
int ele_fetch_soc_info(struct se_if_priv *priv, void *data);
int ele_ping(struct se_if_priv *priv);
@ -95,4 +116,10 @@ int ele_service_swap(struct se_if_priv *priv,
u32 addr_size, u16 flag);
int ele_fw_authenticate(struct se_if_priv *priv, phys_addr_t addr);
int ele_debug_dump(struct se_if_priv *priv);
int ele_start_rng(struct se_if_priv *priv);
int ele_write_fuse(struct se_if_priv *priv, u16 fuse_index,
u32 value, bool block);
int ele_voltage_change_req(struct se_if_priv *priv, bool start);
int read_common_fuse(struct se_if_priv *priv,
u16 fuse_id, u32 *value);
#endif

View File

@ -0,0 +1,152 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2023-2024 NXP
*/
#include <linux/dma-mapping.h>
#include <linux/firmware/imx/se_api.h>
#include "ele_common.h"
#include "ele_fw_api.h"
struct ele_rng_msg_data {
u16 rsv;
u16 flags;
u32 data[2];
};
int ele_init_fw(struct se_if_priv *priv)
{
struct se_api_msg *tx_msg __free(kfree) = NULL;
struct se_api_msg *rx_msg __free(kfree) = NULL;
int ret = 0;
if (!priv) {
ret = -EINVAL;
goto exit;
}
tx_msg = kzalloc(ELE_INIT_FW_REQ_SZ, GFP_KERNEL);
if (!tx_msg) {
ret = -ENOMEM;
goto exit;
}
rx_msg = kzalloc(ELE_INIT_FW_RSP_SZ, GFP_KERNEL);
if (!rx_msg) {
ret = -ENOMEM;
goto exit;
}
ret = se_fill_cmd_msg_hdr(priv,
(struct se_msg_hdr *)&tx_msg->header,
ELE_INIT_FW_REQ,
ELE_INIT_FW_REQ_SZ,
false);
if (ret)
goto exit;
ret = ele_msg_send_rcv(priv->priv_dev_ctx,
tx_msg,
ELE_INIT_FW_REQ_SZ,
rx_msg,
ELE_INIT_FW_RSP_SZ);
if (ret < 0)
goto exit;
ret = se_val_rsp_hdr_n_status(priv,
rx_msg,
ELE_INIT_FW_REQ,
ELE_INIT_FW_RSP_SZ,
false);
exit:
return ret;
}
/*
* ele_get_random() - prepare and send the command to proceed
* with a random number generation operation
*
* returns: size of the rondom number generated
*/
int ele_get_random(struct se_if_priv *priv,
void *data, size_t len)
{
struct se_api_msg *tx_msg __free(kfree) = NULL;
struct se_api_msg *rx_msg __free(kfree) = NULL;
struct ele_rng_msg_data *rng_msg_data;
dma_addr_t dst_dma;
u8 *buf = NULL;
int ret;
if (!priv) {
ret = -EINVAL;
goto exit;
}
tx_msg = kzalloc(ELE_GET_RANDOM_REQ_SZ, GFP_KERNEL);
if (!tx_msg) {
ret = -ENOMEM;
goto exit;
}
rx_msg = kzalloc(ELE_GET_RANDOM_RSP_SZ, GFP_KERNEL);
if (!rx_msg) {
ret = -ENOMEM;
goto exit;
}
/* As per RBG3(RS) construction mentioned in NIST SP800-90C,
* CTR_DRBG generates 128(full entropy) bits after reseeding
* the CTR_DRBG with 256 bits of entropy. so splitting the
* user rng request in multiple of 128 bits & enforce reseed
* for every iteration.
*/
len = ELE_RNG_MAX_SIZE;
buf = dma_alloc_coherent(priv->dev, len, &dst_dma, GFP_KERNEL);
if (!buf) {
dev_err(priv->dev, "Failed to map destination buffer memory.\n");
ret = -ENOMEM;
goto exit;
}
ret = se_fill_cmd_msg_hdr(priv,
(struct se_msg_hdr *)&tx_msg->header,
ELE_GET_RANDOM_REQ,
ELE_GET_RANDOM_REQ_SZ,
false);
if (ret)
goto exit;
rng_msg_data = (struct ele_rng_msg_data *)tx_msg->data;
/* bit 1(blocking reseed): wait for trng entropy,
* then reseed rng context.
*/
if (get_se_soc_id(priv) != SOC_ID_OF_IMX95)
rng_msg_data->flags = BIT(1);
rng_msg_data->data[0] = dst_dma;
rng_msg_data->data[1] = len;
ret = ele_msg_send_rcv(priv->priv_dev_ctx,
tx_msg,
ELE_GET_RANDOM_REQ_SZ,
rx_msg,
ELE_GET_RANDOM_RSP_SZ);
if (ret < 0)
goto exit;
ret = se_val_rsp_hdr_n_status(priv,
rx_msg,
ELE_GET_RANDOM_REQ,
ELE_GET_RANDOM_RSP_SZ,
false);
if (!ret) {
memcpy(data, buf, len);
ret = len;
}
exit:
if (buf)
dma_free_coherent(priv->dev, len, buf, dst_dma);
return ret;
}

View File

@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright 2024 NXP
*/
#ifndef ELE_FW_API_H
#define ELE_FW_API_H
#include <linux/hw_random.h>
#define MESSAGING_VERSION_7 0x7
#define ELE_INIT_FW_REQ 0x17
#define ELE_INIT_FW_REQ_SZ 0x04
#define ELE_INIT_FW_RSP_SZ 0x08
#define ELE_GET_RANDOM_REQ 0xCD
#define ELE_GET_RANDOM_REQ_SZ 0x10
#define ELE_GET_RANDOM_RSP_SZ 0x08
#define ELE_RNG_MAX_SIZE 16
int ele_init_fw(struct se_if_priv *priv);
int ele_get_random(struct se_if_priv *priv, void *data, size_t len);
int ele_get_hwrng(struct hwrng *rng, void *data, size_t len, bool wait);
#endif /* ELE_FW_API_H */

View File

@ -0,0 +1,47 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ELE Random Number Generator Driver NXP's Platforms
*
* Copyright 2024 NXP
*/
#include "ele_trng.h"
#include "ele_fw_api.h"
struct ele_trng {
struct hwrng rng;
struct se_if_priv *priv;
};
int ele_trng_init(struct se_if_priv *priv)
{
struct ele_trng *trng;
int ret;
trng = devm_kzalloc(priv->dev, sizeof(*trng), GFP_KERNEL);
if (!trng)
return -ENOMEM;
trng->priv = priv;
trng->rng.name = "ele-trng";
trng->rng.read = ele_get_hwrng;
trng->rng.priv = (unsigned long)trng;
trng->rng.quality = 1024;
dev_dbg(priv->dev, "registering ele-trng\n");
ret = devm_hwrng_register(priv->dev, &trng->rng);
if (ret)
return ret;
dev_info(priv->dev, "Successfully registered ele-trng\n");
return 0;
}
int ele_get_hwrng(struct hwrng *rng,
void *data, size_t len, bool wait)
{
struct ele_trng *trng = (struct ele_trng *)rng->priv;
return ele_get_random(trng->priv, data, len);
}

View File

@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright 2024 NXP
*/
#ifndef __ELE_TRNG_H__
#define __ELE_TRNG_H__
#include "se_ctrl.h"
#if IS_ENABLED(CONFIG_IMX_ELE_TRNG)
int ele_trng_init(struct se_if_priv *priv);
#else
#define ele_trng_init NULL
#endif
#endif /*__ELE_TRNG_H__ */

View File

@ -28,6 +28,8 @@
#include "ele_base_msg.h"
#include "ele_common.h"
#include "ele_fw_api.h"
#include "ele_trng.h"
#include "se_ctrl.h"
#define MAX_SOC_INFO_DATA_SZ 256
@ -60,6 +62,10 @@ struct se_if_node_info {
struct se_if_defines if_defs;
u8 *pool_name;
bool reserved_dma_ranges;
int (*start_rng)(struct se_if_priv *priv);
int (*init_trng)(struct se_if_priv *priv);
int (*se_if_early_init)(struct se_if_priv *priv);
int (*se_if_late_init)(struct se_if_priv *priv);
};
/* contains fixed information */
@ -130,8 +136,7 @@ static struct se_if_node_info_list imx93_info = {
},
.info = {
{
.se_if_id = 2,
.se_if_did = 3,
.se_if_id = 0,
.if_defs = {
.se_if_type = SE_TYPE_ID_HSM,
.se_instance_id = 0,
@ -142,6 +147,10 @@ static struct se_if_node_info_list imx93_info = {
.fw_api_ver = MESSAGING_VERSION_7,
},
.reserved_dma_ranges = true,
.start_rng = ele_start_rng,
.init_trng = ele_trng_init,
.se_if_early_init = NULL,
.se_if_late_init = ele_init_fw,
},
},
};
@ -1501,6 +1510,25 @@ static int se_if_probe(struct platform_device *pdev)
}
}
if (info->se_if_late_init) {
ret = info->se_if_late_init(priv);
if (ret)
goto exit;
}
/* start ele rng */
if (info->start_rng) {
ret = info->start_rng(priv);
if (ret)
dev_err(dev, "Failed[0x%x] to start rng.\n", ret);
}
if (info->init_trng) {
ret = info->init_trng(priv);
if (ret)
dev_err(dev, "Failed[0x%x] to init trng.\n", ret);
}
/* By default, there is no pending FW to be loaded.*/
if (info_list->se_fw_img_nm.prim_fw_nm_in_rfs ||
info_list->se_fw_img_nm.seco_fw_nm_in_rfs) {

View File

@ -144,6 +144,7 @@ struct se_if_priv {
#define SE_DUMP_MU_RCV_BUFS 2
#define SE_DUMP_KDEBUG_BUFS 3
uint32_t get_se_soc_id(struct se_if_priv *priv);
int se_dump_to_logfl(struct se_if_device_ctx *dev_ctx,
u8 caller_type, int buf_size,
const char *buf, ...);