Re: [PATCH v12 4/5] firmware: imx: add driver for NXP EdgeLock Enclave

Krzysztof Kozlowski
2025-01-20T12:23:40Z

On 20/01/2025 17:52, Pankaj Gupta wrote:
> NXP hardware IP(s) for secure-enclaves like Edgelock Enclave(ELE),
> are embedded in the SoC to support the features like HSM, SHE & V2X,
> using message based communication interface.

Fix your machine so this is not a "future" work.

>
> The secure enclave FW communicates on a dedicated messaging unit(MU)
> based interface(s) with application core, where kernel is running.
> It exists on specific i.MX processors. e.g. i.MX8ULP, i.MX93.
>
> This patch adds the driver for communication interface to secure-enclave,

Please do not use "This commit/patch/change", but imperative mood. See
longer explanation here:
https://elixir.bootlin.com/linux/v5.17.1/source/Documentation/process/submitting-patches.rst#L95

> for exchanging messages with NXP secure enclave HW IP(s) like EdgeLock
> Enclave (ELE) from Kernel-space, used by kernel management layers like
> - DM-Crypt.
>
> Signed-off-by: Pankaj Gupta <pankaj.gupta@nxp.com>
> ---

> +int ele_fetch_soc_info(struct se_if_priv *priv, void *data)
> +{
> + int err;
> +
> + err = ele_get_info(priv, data);
> + if (err < 0)
> + return err;
> +
> + return err;
> +}
> +
> +int ele_ping(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;

This does not make sense. return.... but is this even possible?

> + }
> +
> + tx_msg = kzalloc(ELE_PING_REQ_SZ, GFP_KERNEL);
> + if (!tx_msg) {
> + ret = -ENOMEM;

return -ENOMEM.

> + goto exit;

Please read in coding style how gotos are supposed to be used.

> + }
> +
> + rx_msg = kzalloc(ELE_PING_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_PING_REQ, ELE_PING_REQ_SZ, true);

Fix your coding style - run checkpatch strict on this.

> + if (ret) {
> + dev_err(priv->dev, "Error: se_fill_cmd_msg_hdr failed.\n");
> + goto exit;
> + }
> +

...

> +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);
> +int ele_service_swap(struct se_if_priv *priv,
> + phys_addr_t addr,
> + u32 addr_size, u16 flag);
> +int ele_fw_authenticate(struct se_if_priv *priv, phys_addr_t addr);
> +#endif
> diff --git a/drivers/firmware/imx/ele_common.c b/drivers/firmware/imx/ele_common.c
> new file mode 100644
> index 000000000000..67d1fa761172
> --- /dev/null
> +++ b/drivers/firmware/imx/ele_common.c
> @@ -0,0 +1,324 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright 2024 NXP
> + */
> +
> +#include "ele_base_msg.h"
> +#include "ele_common.h"
> +
> +u32 se_add_msg_crc(u32 *msg, u32 msg_len)
> +{
> + u32 nb_words = msg_len / (u32)sizeof(u32);
> + u32 crc = 0;
> + u32 i;
> +
> + for (i = 0; i < nb_words - 1; i++)
> + crc ^= *(msg + i);
> +
> + return crc;
> +}
> +
> +int ele_msg_rcv(struct se_if_priv *priv,
> + struct se_clbk_handle *se_clbk_hdl)
> +{
> + int err = 0;
> +
> + do {
> + /* If callback is executed before entrying to wait state,

It is not a networking device. Use Linux coding style.

You already got such comment long time ago and not much improved.

> +
> +static int se_if_probe(struct platform_device *pdev)
> +{
> + const struct se_if_node_info_list *info_list;
> + const struct se_if_node_info *info;
> + struct device *dev = &pdev->dev;
> + struct se_fw_load_info *load_fw;
> + struct se_if_priv *priv;
> + u32 idx;
> + int ret;
> +
> + idx = GET_IDX_FROM_DEV_NODE_NAME(dev->of_node);

NAK. Node can be called firmware and your entire driver collapes.

> + info_list = device_get_match_data(dev);
> + if (idx >= info_list->num_mu) {
> + dev_err(dev,
> + "Incorrect node name :%s\n",
> + dev->of_node->full_name);

Nope. "firmware" or "secure" are correct node names. Where did you
document this ABI?

> + dev_err(dev,
> + "%s-<index>, acceptable index range is 0..%d\n",
> + dev->of_node->name,
> + info_list->num_mu - 1);
> + ret = -EINVAL;
> + return ret;
> + }
> +
> + info = &info_list->info[idx];
> + if (!info) {
> + ret = -EINVAL;
> + goto exit;
> + }
> +
> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv) {
> + ret = -ENOMEM;
> + goto exit;

Nope, You don't get how common exit works. You are supposed to clean up
in comon exit paths, not print error paths, especially ones which are
not welcomed - like here.

> + }
> +
> + priv->dev = dev;
> + priv->if_defs = &info->if_defs;
> + dev_set_drvdata(dev, priv);
> +
> + ret = devm_add_action(dev, se_if_probe_cleanup, pdev);
> + if (ret)
> + goto exit;
> +
> +
> + /* Mailbox client configuration */
> + priv->se_mb_cl.dev = dev;
> + priv->se_mb_cl.tx_block = false;
> + priv->se_mb_cl.knows_txdone = true;
> + priv->se_mb_cl.rx_callback = se_if_rx_callback;
> +
> + ret = se_if_request_channel(dev, &priv->tx_chan,
> + &priv->se_mb_cl, MBOX_TX_NAME);
> + if (ret)
> + goto exit;
> +
> + ret = se_if_request_channel(dev, &priv->rx_chan,
> + &priv->se_mb_cl, MBOX_RX_NAME);
> + if (ret)
> + goto exit;
> +
> + mutex_init(&priv->se_if_cmd_lock);
> +
> + init_completion(&priv->waiting_rsp_clbk_hdl.done);
> + init_completion(&priv->cmd_receiver_clbk_hdl.done);
> +
> + if (info->pool_name) {
> + priv->mem_pool = of_gen_pool_get(dev->of_node,
> + info->pool_name, 0);
> + if (!priv->mem_pool) {
> + dev_err(dev,
> + "Unable to get sram pool = %s\n",
> + info->pool_name);
> + goto exit;

Why do you print erros twice?

> + }
> + }
> +
> + if (info->reserved_dma_ranges) {
> + ret = of_reserved_mem_device_init(dev);
> + if (ret) {
> + dev_err(dev,
> + "failed to init reserved memory region %d\n",
> + ret);
> + goto exit;
> + }
> + }
> +
> + if (info->if_defs.se_if_type == SE_TYPE_ID_HSM) {
> + ret = se_soc_info(priv);
> + if (ret) {
> + dev_err(dev,
> + "failed[%pe] to fetch SoC Info\n", ERR_PTR(ret));
> + goto exit;
> + }
> + }
> +
> + /* 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) {
> + load_fw = get_load_fw_instance(priv);
> + load_fw->se_fw_img_nm = &info_list->se_fw_img_nm;
> + load_fw->is_fw_loaded = false;
> +
> + if (info_list->se_fw_img_nm.prim_fw_nm_in_rfs) {
> + /* allocate buffer where SE store encrypted IMEM */
> + load_fw->imem.buf = dmam_alloc_coherent(priv->dev, ELE_IMEM_SIZE,
> + &load_fw->imem.phyaddr,
> + GFP_KERNEL);
> + if (!load_fw->imem.buf) {
> + dev_err(priv->dev,
> + "dmam-alloc-failed: To store encr-IMEM.\n");
> + ret = -ENOMEM;
> + goto exit;
> + }
> + load_fw->imem_mgmt = true;
> + }
> + }
> + dev_info(dev, "i.MX secure-enclave: %s%d interface to firmware, configured.\n",
> + SE_TYPE_STR_HSM,
> + priv->if_defs->se_instance_id);

Drop probe success. Useless.

> + return ret;
> +
> +exit:
> + /* if execution control reaches here, if probe fails.
> + */

Obvious comment.

> + return dev_err_probe(dev, ret, "%s: Probe failed.", __func__);

Drop. I think I asked already long time - like 10 revisiosn ago - to
drop simple function debug messages. Look at other drivers how exit
paths are handled.

Best regards,
Krzysztof