diff options
23 files changed, 3453 insertions, 57 deletions
diff --git a/Documentation/devicetree/bindings/arm/freescale/fsl,s400-api.yaml b/Documentation/devicetree/bindings/arm/freescale/fsl,s400-api.yaml new file mode 100644 index 000000000000..fe2c2b69b63c --- /dev/null +++ b/Documentation/devicetree/bindings/arm/freescale/fsl,s400-api.yaml @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/arm/freescale/fsl,s400-api.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP S400 Baseline API module + +maintainers: + - Alice Guo <alice.guo@nxp.com> + +description: | + In the Sentinel application, the security subsystem uses S4 MU-AP to + communicate and coordinate with the SoC host processor. The s400-api firmware + driver provides the services to transmit data to and receive data from the + S4 MU-AP. + +properties: + compatible: + items: + - const: fsl,imx8ulp-s400 + + mboxes: + description: | + Use the mailbox provided by S4 MU-AP device to communicate with the S400. + It should contain 2 mailboxes, one for transmitting messages and another + for receiving. + maxItems: 1 + + mbox-names: + items: + - const: tx + - const: rx + +required: + - compatible + - mboxes + - mbox-names + +additionalProperties: false + +examples: + - | + s400-api { + compatible = "fsl,imx8ulp-s400"; + mboxes = <&s4muap 0 0 &s4muap 1 0>; + mbox-names = "tx", "rx"; + }; diff --git a/Documentation/devicetree/bindings/arm/freescale/fsl,seco_mu.yaml b/Documentation/devicetree/bindings/arm/freescale/fsl,seco_mu.yaml new file mode 100644 index 000000000000..c36bea2079c2 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/freescale/fsl,seco_mu.yaml @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/arm/freescale/fsl,seco_mu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP i.MX8 SECO MU driver + +maintainers: + - Aisheng Dong <aisheng.dong@nxp.com> + +description: | + Create char devices in /dev as channels of the form /dev/seco_muXchY with X + the id of the driver and Y for each users. It allows to send and receive + messages to the SECO. + +properties: + compatible: + enum: + - fsl,imx-seco-mu + + mboxes: + description: + List of <&phandle type channel> - 4 channels for TX, 4 channels for RX, + 1 channel for TXDB (see mailbox/fsl,mu.txt) + maxItems: 9 + + mbox-names: + items: + - const: txdb + - const: rxdb + + fsl,seco_mu_id: + description: + Identify the driver instance, used to create the channels, default to 1 + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - enum: [0,1,2,3] + + fsl,seco_max_users: + description: + Number of channels to create, default to 4 + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - enum: [0,1,2,3,4,5,6,7,8,9] + + fsl,cmd_tag: + description: + Tag in message header for commands on this MU, default to 0x17 + allOf: + - $ref: /schemas/types.yaml#/definitions/uint8 + - enum: [0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e] + + fsl,rsp_tag: + description: + Tag in message header for responses on this MU, default to 0xe1 + allOf: + - $ref: /schemas/types.yaml#/definitions/uint8 + - enum: [0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8] + +required: + - compatible + - mboxes + - mbox-names + +examples: + - | + seco_mu: seco_mu { + compatible = "fsl,imx-seco-mu"; + mbox-names = "txdb", "rxdb"; + mboxes = <&mu 2 0 + &mu 3 0>; + + fsl,seco_mu_id = <1>; + fsl,seco_max_users = <4>; + fsl,cmd_tag = /bits/ 8 <0x17>; + fsl,rsp_tag = /bits/ 8 <0xe1>; + }; diff --git a/drivers/firmware/imx/Kconfig b/drivers/firmware/imx/Kconfig index c027d99f2a59..f7a2ff0b4373 100644 --- a/drivers/firmware/imx/Kconfig +++ b/drivers/firmware/imx/Kconfig @@ -28,3 +28,25 @@ config IMX_SCU_PD depends on IMX_SCU help The System Controller Firmware (SCFW) based power domain driver. + +config IMX_SECO_MU + tristate "i.MX Security Controller (SECO) support" + depends on IMX_MBOX + default y if IMX_SCU + + help + It is possible to use APIs exposed by the SECO like HSM and 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_SENCLAVE_MU + tristate "i.MX Embedded Secure Enclave support." + depends on IMX_MBOX + default y if ARM64 + + help + It is possible to use APIs exposed by the iMX Secure Enclave like base, HSM & + 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. diff --git a/drivers/firmware/imx/Makefile b/drivers/firmware/imx/Makefile index b76acbade2a0..87ef85208350 100644 --- a/drivers/firmware/imx/Makefile +++ b/drivers/firmware/imx/Makefile @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_IMX_DSP) += imx-dsp.o -obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o rm.o imx-scu-soc.o +obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o rm.o imx-scu-soc.o seco.o obj-$(CONFIG_IMX_SCU_PD) += scu-pd.o +obj-${CONFIG_IMX_SECO_MU} += seco_mu.o +obj-${CONFIG_IMX_SENCLAVE_MU} += sentnl_mu.o senclave_base_msg.o diff --git a/drivers/firmware/imx/imx-scu-irq.c b/drivers/firmware/imx/imx-scu-irq.c index d9dcc20945c6..afa1f94f3600 100644 --- a/drivers/firmware/imx/imx-scu-irq.c +++ b/drivers/firmware/imx/imx-scu-irq.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright 2019 NXP + * Copyright 2019-2020 NXP * * Implementation of the SCU IRQ functions using MU. * @@ -11,10 +11,11 @@ #include <linux/firmware/imx/sci.h> #include <linux/mailbox_client.h> #include <linux/suspend.h> +#include <linux/sysfs.h> +#include <linux/kobject.h> #define IMX_SC_IRQ_FUNC_ENABLE 1 #define IMX_SC_IRQ_FUNC_STATUS 2 -#define IMX_SC_IRQ_NUM_GROUP 4 static u32 mu_resource_id; @@ -40,63 +41,102 @@ struct imx_sc_msg_irq_enable { u8 enable; } __packed; +struct scu_wakeup { + u32 mask; + u32 wakeup_src; + bool valid; +}; + +/* Sysfs functions */ +struct kobject *wakeup_obj; +static ssize_t wakeup_source_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); +static ssize_t num_wakeup_groups_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); +static struct kobj_attribute wakeup_source_attr = __ATTR(wakeup_src, 0660, wakeup_source_show, NULL); +static struct kobj_attribute num_wakeup_groups = __ATTR(num_wakeup_grps, 0660, num_wakeup_groups_show, NULL); + +static struct scu_wakeup scu_irq_wakeup[IMX_SC_IRQ_NUM_GROUP]; + + static struct imx_sc_ipc *imx_sc_irq_ipc_handle; static struct work_struct imx_sc_irq_work; -static ATOMIC_NOTIFIER_HEAD(imx_scu_irq_notifier_chain); +static BLOCKING_NOTIFIER_HEAD(imx_scu_irq_notifier_chain); int imx_scu_irq_register_notifier(struct notifier_block *nb) { - return atomic_notifier_chain_register( + return blocking_notifier_chain_register( &imx_scu_irq_notifier_chain, nb); } EXPORT_SYMBOL(imx_scu_irq_register_notifier); int imx_scu_irq_unregister_notifier(struct notifier_block *nb) { - return atomic_notifier_chain_unregister( + return blocking_notifier_chain_unregister( &imx_scu_irq_notifier_chain, nb); } EXPORT_SYMBOL(imx_scu_irq_unregister_notifier); static int imx_scu_irq_notifier_call_chain(unsigned long status, u8 *group) { - return atomic_notifier_call_chain(&imx_scu_irq_notifier_chain, + return blocking_notifier_call_chain(&imx_scu_irq_notifier_chain, status, (void *)group); } static void imx_scu_irq_work_handler(struct work_struct *work) { - struct imx_sc_msg_irq_get_status msg; - struct imx_sc_rpc_msg *hdr = &msg.hdr; u32 irq_status; int ret; u8 i; for (i = 0; i < IMX_SC_IRQ_NUM_GROUP; i++) { - hdr->ver = IMX_SC_RPC_VERSION; - hdr->svc = IMX_SC_RPC_SVC_IRQ; - hdr->func = IMX_SC_IRQ_FUNC_STATUS; - hdr->size = 2; - - msg.data.req.resource = mu_resource_id; - msg.data.req.group = i; - - ret = imx_scu_call_rpc(imx_sc_irq_ipc_handle, &msg, true); + if (scu_irq_wakeup[i].mask) { + scu_irq_wakeup[i].valid = false; + scu_irq_wakeup[i].wakeup_src = 0; + } + ret = imx_scu_irq_get_status(i, &irq_status); if (ret) { pr_err("get irq group %d status failed, ret %d\n", i, ret); return; } - irq_status = msg.data.resp.status; if (!irq_status) continue; - + if (scu_irq_wakeup[i].mask & irq_status) { + scu_irq_wakeup[i].valid = true; + scu_irq_wakeup[i].wakeup_src = irq_status & scu_irq_wakeup[i].mask; + } else { + scu_irq_wakeup[i].wakeup_src = irq_status; + } pm_system_wakeup(); imx_scu_irq_notifier_call_chain(irq_status, &i); } } +int imx_scu_irq_get_status(u8 group, u32 *irq_status) +{ + struct imx_sc_msg_irq_get_status msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + int ret; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_IRQ; + hdr->func = IMX_SC_IRQ_FUNC_STATUS; + hdr->size = 2; + + msg.data.req.resource = mu_resource_id; + msg.data.req.group = group; + + ret = imx_scu_call_rpc(imx_sc_irq_ipc_handle, &msg, true); + if (ret) + return ret; + + if (irq_status) + *irq_status = msg.data.resp.status; + + return 0; +} +EXPORT_SYMBOL(imx_scu_irq_get_status); + int imx_scu_irq_group_enable(u8 group, u32 mask, u8 enable) { struct imx_sc_msg_irq_enable msg; @@ -121,6 +161,11 @@ int imx_scu_irq_group_enable(u8 group, u32 mask, u8 enable) pr_err("enable irq failed, group %d, mask %d, ret %d\n", group, mask, ret); + if (enable) + scu_irq_wakeup[group].mask |= mask; + else + scu_irq_wakeup[group].mask &= ~mask; + return ret; } EXPORT_SYMBOL(imx_scu_irq_group_enable); @@ -130,6 +175,30 @@ static void imx_scu_irq_callback(struct mbox_client *c, void *msg) schedule_work(&imx_sc_irq_work); } +static ssize_t wakeup_source_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + u8 i = 0, size = 0; + + for (i = 0; i < IMX_SC_IRQ_NUM_GROUP; i++) { + if (scu_irq_wakeup[i].wakeup_src != 0) { + if (scu_irq_wakeup[i].valid) + size += sprintf(buf + size, "Wakeup source group = %d, irq = 0x%x\n", + i, scu_irq_wakeup[i].wakeup_src); + else + size += sprintf(buf + size, "Spurious SCU wakeup, group = %d, irq = 0x%x\n", + i, scu_irq_wakeup[i].wakeup_src); + } + } + return strlen(buf); +} + +static ssize_t num_wakeup_groups_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", IMX_SC_IRQ_NUM_GROUP); +} + int imx_scu_enable_general_irq_channel(struct device *dev) { struct of_phandle_args spec; @@ -169,6 +238,15 @@ int imx_scu_enable_general_irq_channel(struct device *dev) mu_resource_id = IMX_SC_R_MU_0A + i; + /* Create directory under /sysfs/firmware */ + wakeup_obj = kobject_create_and_add("scu_wakeup_source", firmware_kobj); + + if (sysfs_create_file(wakeup_obj, &wakeup_source_attr.attr)) { + pr_err("Cannot create sysfs file......\n"); + kobject_put(wakeup_obj); + sysfs_remove_file(firmware_kobj, &wakeup_source_attr.attr); + } + return ret; } EXPORT_SYMBOL(imx_scu_enable_general_irq_channel); diff --git a/drivers/firmware/imx/imx-scu-soc.c b/drivers/firmware/imx/imx-scu-soc.c index 418a2ac29937..c8d14315d463 100644 --- a/drivers/firmware/imx/imx-scu-soc.c +++ b/drivers/firmware/imx/imx-scu-soc.c @@ -37,18 +37,15 @@ static int imx_scu_soc_uid(u64 *soc_uid) { struct imx_sc_msg_misc_get_soc_uid msg; struct imx_sc_rpc_msg *hdr = &msg.hdr; - int ret; + + memset(&msg, 0, sizeof(msg)); hdr->ver = IMX_SC_RPC_VERSION; hdr->svc = IMX_SC_RPC_SVC_MISC; hdr->func = IMX_SC_MISC_FUNC_UNIQUE_ID; hdr->size = 1; - ret = imx_scu_call_rpc(imx_sc_soc_ipc_handle, &msg, true); - if (ret) { - pr_err("%s: get soc uid failed, ret %d\n", __func__, ret); - return ret; - } + imx_scu_call_rpc(imx_sc_soc_ipc_handle, &msg, true); *soc_uid = msg.uid_high; *soc_uid <<= 32; diff --git a/drivers/firmware/imx/imx-scu.c b/drivers/firmware/imx/imx-scu.c index dca79caccd01..fd6de5771841 100644 --- a/drivers/firmware/imx/imx-scu.c +++ b/drivers/firmware/imx/imx-scu.c @@ -7,6 +7,7 @@ * */ +#include <linux/arm-smccc.h> #include <linux/err.h> #include <linux/firmware/imx/ipc.h> #include <linux/firmware/imx/sci.h> @@ -19,8 +20,11 @@ #include <linux/of_platform.h> #include <linux/platform_device.h> +#include <xen/xen.h> + +#define FSL_HVC_SC 0xC6000000 #define SCU_MU_CHAN_NUM 8 -#define MAX_RX_TIMEOUT (msecs_to_jiffies(30)) +#define MAX_RX_TIMEOUT (msecs_to_jiffies(3000)) struct imx_sc_chan { struct imx_sc_ipc *sc_ipc; @@ -204,6 +208,7 @@ int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp) { uint8_t saved_svc, saved_func; struct imx_sc_rpc_msg *hdr; + struct arm_smccc_res res; int ret; if (WARN_ON(!sc_ipc || !msg)) @@ -218,33 +223,45 @@ int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp) saved_func = ((struct imx_sc_rpc_msg *)msg)->func; } sc_ipc->count = 0; - ret = imx_scu_ipc_write(sc_ipc, msg); - if (ret < 0) { - dev_err(sc_ipc->dev, "RPC send msg failed: %d\n", ret); - goto out; - } - - if (have_resp) { - if (!wait_for_completion_timeout(&sc_ipc->done, - MAX_RX_TIMEOUT)) { - dev_err(sc_ipc->dev, "RPC send msg timeout\n"); - mutex_unlock(&sc_ipc->lock); - return -ETIMEDOUT; + sc_ipc->rx_size = 0; + if (xen_initial_domain()) { + arm_smccc_hvc(FSL_HVC_SC, (uint64_t)msg, !have_resp, 0, 0, 0, + 0, 0, &res); + if (res.a0) + printk("Error FSL_HVC_SC %ld\n", res.a0); + + ret = res.a0; + + } else { + ret = imx_scu_ipc_write(sc_ipc, msg); + if (ret < 0) { + dev_err(sc_ipc->dev, "RPC send msg failed: %d\n", ret); + goto out; } - /* response status is stored in hdr->func field */ - hdr = msg; - ret = hdr->func; - /* - * Some special SCU firmware APIs do NOT have return value - * in hdr->func, but they do have response data, those special - * APIs are defined as void function in SCU firmware, so they - * should be treated as return success always. - */ - if ((saved_svc == IMX_SC_RPC_SVC_MISC) && - (saved_func == IMX_SC_MISC_FUNC_UNIQUE_ID || - saved_func == IMX_SC_MISC_FUNC_GET_BUTTON_STATUS)) - ret = 0; + if (have_resp) { + if (!wait_for_completion_timeout(&sc_ipc->done, + MAX_RX_TIMEOUT)) { + dev_err(sc_ipc->dev, "RPC send msg timeout\n"); + mutex_unlock(&sc_ipc->lock); + return -ETIMEDOUT; + } + + /* response status is stored in hdr->func field */ + hdr = msg; + ret = hdr->func; + + /* + * Some special SCU firmware APIs do NOT have return value + * in hdr->func, but they do have response data, those special + * APIs are defined as void function in SCU firmware, so they + * should be treated as return success always. + */ + if ((saved_svc == IMX_SC_RPC_SVC_MISC) && + (saved_func == IMX_SC_MISC_FUNC_UNIQUE_ID || + saved_func == IMX_SC_MISC_FUNC_GET_BUTTON_STATUS)) + ret = 0; + } } out: @@ -354,7 +371,12 @@ static struct platform_driver imx_scu_driver = { }, .probe = imx_scu_probe, }; -builtin_platform_driver(imx_scu_driver); + +static int __init imx_scu_driver_init(void) +{ + return platform_driver_register(&imx_scu_driver); +} +subsys_initcall_sync(imx_scu_driver_init); MODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>"); MODULE_DESCRIPTION("IMX SCU firmware protocol driver"); diff --git a/drivers/firmware/imx/misc.c b/drivers/firmware/imx/misc.c index d073cb3ce699..01878451d4ed 100644 --- a/drivers/firmware/imx/misc.c +++ b/drivers/firmware/imx/misc.c @@ -18,6 +18,13 @@ struct imx_sc_msg_req_misc_set_ctrl { u16 resource; } __packed __aligned(4); + +struct imx_sc_msg_req_misc_set_dma_group { + struct imx_sc_rpc_msg hdr; + u16 resource; + u8 val; +} __packed __aligned(4); + struct imx_sc_msg_req_cpu_start { struct imx_sc_rpc_msg hdr; u32 address_hi; @@ -67,6 +74,24 @@ int imx_sc_misc_set_control(struct imx_sc_ipc *ipc, u32 resource, } EXPORT_SYMBOL(imx_sc_misc_set_control); +int imx_sc_misc_set_dma_group(struct imx_sc_ipc *ipc, u32 resource, + u32 val) +{ + struct imx_sc_msg_req_misc_set_dma_group msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = (uint8_t)IMX_SC_RPC_SVC_MISC; + hdr->func = (uint8_t)IMX_SC_MISC_FUNC_SET_DMA_GROUP; + hdr->size = 2; + + msg.val = val; + msg.resource = resource; + + return imx_scu_call_rpc(ipc, &msg, true); +} +EXPORT_SYMBOL(imx_sc_misc_set_dma_group); + /* * This function gets a miscellaneous control value. * diff --git a/drivers/firmware/imx/rm.c b/drivers/firmware/imx/rm.c index a12db6ff323b..6dd4db3861d7 100644 --- a/drivers/firmware/imx/rm.c +++ b/drivers/firmware/imx/rm.c @@ -13,6 +13,11 @@ struct imx_sc_msg_rm_rsrc_owned { u16 resource; } __packed __aligned(4); +struct imx_sc_msg_rm_pt { + struct imx_sc_rpc_msg hdr; + u8 val; +} __packed __aligned(4); + /* * This function check @resource is owned by current partition or not * @@ -43,3 +48,160 @@ bool imx_sc_rm_is_resource_owned(struct imx_sc_ipc *ipc, u16 resource) return hdr->func; } EXPORT_SYMBOL(imx_sc_rm_is_resource_owned); + +/* + * This function returns the current partition number + * + * @param[in] ipc IPC handle + * @param[out] pt holding the partition number + * + * @return Returns 0 for success and < 0 for errors. + */ +int imx_sc_rm_get_partition(struct imx_sc_ipc *ipc, u8 *pt) +{ + struct imx_sc_msg_rm_pt msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + int ret; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_RM; + hdr->func = IMX_SC_RM_FUNC_GET_PARTITION; + hdr->size = 1; + + ret = imx_scu_call_rpc(ipc, &msg, true); + if (ret) + return ret; + + if (pt != NULL) + *pt = msg.val; + + return 0; +} +EXPORT_SYMBOL(imx_sc_rm_get_partition); + +struct imx_sc_msg_rm_find_memreg { + struct imx_sc_rpc_msg hdr; + union { + struct { + u32 add_start_hi; + u32 add_start_lo; + u32 add_end_hi; + u32 add_end_lo; + } req; + struct { + u8 val; + } resp; + } data; +} __packed __aligned(4); + +int imx_sc_rm_find_memreg(struct imx_sc_ipc *ipc, u8 *mr, u64 addr_start, + u64 addr_end) +{ + struct imx_sc_msg_rm_find_memreg msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + int ret; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_RM; + hdr->func = IMX_SC_RM_FUNC_FIND_MEMREG; + hdr->size = 5; + + msg.data.req.add_start_hi = addr_start >> 32; + msg.data.req.add_start_lo = addr_start; + msg.data.req.add_end_hi = addr_end >> 32; + msg.data.req.add_end_lo = addr_end; + + ret = imx_scu_call_rpc(ipc, &msg, true); + if (ret) + return ret; + + if (mr) + *mr = msg.data.resp.val; + + return 0; +} +EXPORT_SYMBOL(imx_sc_rm_find_memreg); + +struct imx_sc_msg_rm_get_resource_owner { + struct imx_sc_rpc_msg hdr; + union { + struct { + u16 resource; + } req; + struct { + u8 val; + } resp; + } data; +} __packed __aligned(4); + +int imx_sc_rm_get_resource_owner(struct imx_sc_ipc *ipc, u16 resource, u8 *pt) +{ + struct imx_sc_msg_rm_get_resource_owner msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + int ret; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_RM; + hdr->func = IMX_SC_RM_FUNC_GET_RESOURCE_OWNER; + hdr->size = 2; + + msg.data.req.resource = resource; + + ret = imx_scu_call_rpc(ipc, &msg, true); + if (ret) + return ret; + + if (pt) + *pt = msg.data.resp.val; + + return 0; +} +EXPORT_SYMBOL(imx_sc_rm_get_resource_owner); + +struct imx_sc_msg_set_memreg_permissions { + struct imx_sc_rpc_msg hdr; + u8 mr; + u8 pt; + u8 perm; +} __packed __aligned(4); + +int imx_sc_rm_set_memreg_permissions(struct imx_sc_ipc *ipc, u8 mr, + u8 pt, u8 perm) +{ + struct imx_sc_msg_set_memreg_permissions msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_RM; + hdr->func = IMX_SC_RM_FUNC_SET_MEMREG_PERMISSIONS; + hdr->size = 2; + + msg.mr = mr; + msg.pt = pt; + msg.perm = perm; + + return imx_scu_call_rpc(ipc, &msg, true); +} +EXPORT_SYMBOL(imx_sc_rm_set_memreg_permissions); + +int imx_sc_rm_get_did(struct imx_sc_ipc *ipc, u8 *did) +{ + struct imx_sc_rpc_msg msg; + struct imx_sc_rpc_msg *hdr = &msg; + int ret; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_RM; + hdr->func = IMX_SC_RM_FUNC_GET_DID; + hdr->size = 1; + + ret = imx_scu_call_rpc(ipc, &msg, true); + if (ret < 0) + return ret; + + if (did) + *did = msg.func; + + return 0; +} +EXPORT_SYMBOL(imx_sc_rm_get_did); diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c index 9dfec68219e3..75778c6eb9bf 100644 --- a/drivers/firmware/imx/scu-pd.c +++ b/drivers/firmware/imx/scu-pd.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2016 Freescale Semiconductor, Inc. - * Copyright 2017-2018 NXP + * Copyright 2017-2018,2020 NXP * Dong Aisheng <aisheng.dong@nxp.com> * * Implementation of the SCU based Power Domains @@ -108,6 +108,7 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { /* CONN SS */ { "usb", IMX_SC_R_USB_0, 2, true, 0 }, { "usb0phy", IMX_SC_R_USB_0_PHY, 1, false, 0 }, + { "usb1phy", IMX_SC_R_USB_1_PHY, 1, false, 0}, { "usb2", IMX_SC_R_USB_2, 1, false, 0 }, { "usb2phy", IMX_SC_R_USB_2_PHY, 1, false, 0 }, { "sdhc", IMX_SC_R_SDHC_0, 3, true, 0 }, @@ -147,7 +148,7 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { { "lcd", IMX_SC_R_LCD_0, 1, true, 0 }, { "lcd-pll", IMX_SC_R_ELCDIF_PLL, 1, true, 0 }, { "lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 }, - { "lpuart", IMX_SC_R_UART_0, 4, true, 0 }, + { "lpuart", IMX_SC_R_UART_0, 5, true, 0 }, { "lpspi", IMX_SC_R_SPI_0, 4, true, 0 }, { "irqstr_dsp", IMX_SC_R_IRQSTR_DSP, 1, false, 0 }, @@ -202,6 +203,14 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { { "img-jpegdec-s0", IMX_SC_R_MJPEG_DEC_S0, 4, true, 0 }, { "img-jpegenc-mp", IMX_SC_R_MJPEG_ENC_MP, 1, false, 0 }, { "img-jpegenc-s0", IMX_SC_R_MJPEG_ENC_S0, 4, true, 0 }, + + /* SECO SS */ + { "seco_mu", IMX_SC_R_SECO_MU_2, 3, true, 2}, + + /* V2X SS */ + { "v2x_mu", IMX_SC_R_V2X_MU_0, 2, true, 0}, + { "v2x_mu", IMX_SC_R_V2X_MU_2, 1, true, 2}, + { "v2x_mu", IMX_SC_R_V2X_MU_3, 2, true, 3}, }; static const struct imx_sc_pd_soc imx8qxp_scu_pd = { @@ -252,6 +261,12 @@ static int imx_sc_pd_power(struct generic_pm_domain *domain, bool power_on) msg.mode = power_on ? IMX_SC_PM_PW_MODE_ON : IMX_SC_PM_PW_MODE_LP; ret = imx_scu_call_rpc(pm_ipc_handle, &msg, true); + if (ret == -EACCES) + { + pr_warn("Resource %d not owned by partition, power state unchanged\n", + pd->rsrc); + return 0; + } if (ret) dev_err(&domain->dev, "failed to power %s resource %d ret %d\n", power_on ? "up" : "off", pd->rsrc, ret); diff --git a/drivers/firmware/imx/seco.c b/drivers/firmware/imx/seco.c new file mode 100644 index 000000000000..18232c70053b --- /dev/null +++ b/drivers/firmware/imx/seco.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020 NXP + * + * File containing client-side RPC functions for the SECO service. These + * function are ported to clients that communicate to the SC. + */ + +#include <linux/firmware/imx/sci.h> + +struct imx_sc_msg_seco_get_build_id { + struct imx_sc_rpc_msg hdr; + u32 version; + u32 commit; +} __packed __aligned(4); + +int imx_sc_seco_build_info(struct imx_sc_ipc *ipc, uint32_t *version, + uint32_t *commit) +{ + struct imx_sc_msg_seco_get_build_id msg = {0}; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_SECO; + hdr->func = IMX_SC_SECO_FUNC_BUILD_INFO; + hdr->size = 1; + + imx_scu_call_rpc(ipc, &msg, true); + + if (version) + *version = msg.version; + if (commit) + *commit = msg.commit; + + return 0; +} +EXPORT_SYMBOL(imx_sc_seco_build_info); + +struct imx_sc_msg_seco_sab_msg { + struct imx_sc_rpc_msg hdr; + u32 smsg_addr_hi; + u32 smsg_addr_lo; +} __packed __aligned(4); + +int imx_sc_seco_sab_msg(struct imx_sc_ipc *ipc, u64 smsg_addr) +{ + struct imx_sc_msg_seco_sab_msg msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + int ret; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_SECO; + hdr->func = IMX_SC_SECO_FUNC_SAB_MSG; + hdr->size = 3; + + msg.smsg_addr_hi = smsg_addr >> 32; + msg.smsg_addr_lo = smsg_addr; + + ret = imx_scu_call_rpc(ipc, &msg, true); + return ret; +} +EXPORT_SYMBOL(imx_sc_seco_sab_msg); + +int imx_sc_seco_secvio_enable(struct imx_sc_ipc *ipc) +{ + struct imx_sc_rpc_msg msg; + struct imx_sc_rpc_msg *hdr = &msg; + int ret; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = (uint8_t)IMX_SC_RPC_SVC_SECO; + hdr->func = (uint8_t)IMX_SC_SECO_FUNC_SECVIO_ENABLE; + hdr->size = 1; + + ret = imx_scu_call_rpc(ipc, &msg, true); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(imx_sc_seco_secvio_enable); + +struct imx_sc_msg_req_seco_config { + struct imx_sc_rpc_msg hdr; + u32 data0; + u32 data1; + u32 data2; + u32 data3; + u32 data4; + u8 id; + u8 access; + u8 size; +} __packed __aligned(4); + +struct imx_sc_msg_resp_seco_config { + struct imx_sc_rpc_msg hdr; + u32 data0; + u32 data1; + u32 data2; + u32 data3; + u32 data4; +} __packed __aligned(4); + +int imx_sc_seco_secvio_config(struct imx_sc_ipc *ipc, u8 id, u8 access, + u32 *data0, u32 *data1, u32 *data2, u32 *data3, + u32 *data4, u8 size) +{ + struct imx_sc_msg_req_seco_config msg; + struct imx_sc_msg_resp_seco_config *resp; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + int ret; + + if (!ipc) + return -EINVAL; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = (uint8_t)IMX_SC_RPC_SVC_SECO; + hdr->func = (uint8_t)IMX_SC_SECO_FUNC_SECVIO_CONFIG; + hdr->size = 7; + + /* Check the pointers on data are valid and set it if doing a write */ + switch (size) { + case 5: + if (data4) { + if (access) + msg.data4 = *data4; + } else { + return -EINVAL; + } + fallthrough; + case 4: + if (data3) { + if (access) + msg.data3 = *data3; + } else { + return -EINVAL; + } + fallthrough; + case 3: + if (data2) { + if (access) + msg.data2 = *data2; + } else { + return -EINVAL; + } + fallthrough; + case 2: + if (data1) { + if (access) + msg.data1 = *data1; + } else { + return -EINVAL; + } + fallthrough; + case 1: + if (data0) { + if (access) + msg.data0 = *data0; + } else { + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + msg.id = id; + msg.access = access; + msg.size = size; + + ret = imx_scu_call_rpc(ipc, &msg, true); + if (ret) + return ret; + + resp = (struct imx_sc_msg_resp_seco_config *)&msg; + + /* Pointers already checked so we just copy the data if reading */ + if (!access) + switch (size) { + case 5: + *data4 = resp->data4; + fallthrough; + case 4: + *data3 = resp->data3; + fallthrough; + case 3: + *data2 = resp->data2; + fallthrough; + case 2: + *data1 = resp->data1; + fallthrough; + case 1: + *data0 = resp->data0; + } + + return 0; +} +EXPORT_SYMBOL(imx_sc_seco_secvio_config); + +struct imx_sc_msg_req_seco_dgo_config { + struct imx_sc_rpc_msg hdr; + u32 data; + u8 id; + u8 access; +} __packed __aligned(4); + +struct imx_sc_msg_resp_seco_dgo_config { + struct imx_sc_rpc_msg hdr; + u32 data; +} __packed __aligned(4); + +int imx_sc_seco_secvio_dgo_config(struct imx_sc_ipc *ipc, u8 id, u8 access, + u32 *data) +{ + struct imx_sc_msg_req_seco_dgo_config msg; + struct imx_sc_msg_resp_seco_dgo_config *resp; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + int ret; + + if (!ipc) + return -EINVAL; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = (uint8_t)IMX_SC_RPC_SVC_SECO; + hdr->func = (uint8_t)IMX_SC_SECO_FUNC_SECVIO_DGO_CONFIG; + hdr->size = 3; + + if (access) { + if (data) + msg.data = *data; + else + return -EINVAL; + } + + msg.access = access; + msg.id = id; + + ret = imx_scu_call_rpc(ipc, &msg, true); + if (ret) + return ret; + + resp = (struct imx_sc_msg_resp_seco_dgo_config *)&msg; + + if (!access && data) + *data = resp->data; + + return 0; +} +EXPORT_SYMBOL(imx_sc_seco_secvio_dgo_config); diff --git a/drivers/firmware/imx/seco_mu.c b/drivers/firmware/imx/seco_mu.c new file mode 100644 index 000000000000..02f4295f8099 --- /dev/null +++ b/drivers/firmware/imx/seco_mu.c @@ -0,0 +1,1210 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright 2019-2020 NXP + */ + +/* + * This driver allows to send messages to the SECO using a shared mailbox. The + * messages must follow the protocol defined. + */ + +/* + * Architecture of the driver: + * + * Non-Secure + Secure + * | + * | + * +---------+ +-------------+ | + * |seco_mu.c+<---->+imx-mailbox.c| | + * | | | mailbox.c +<-->+------+ +------+ + * +---+-----+ +-------------+ | MU X +<-->+ SECO | + * | +------+ +------+ + * +----------------+ | + * | | | + * v v | + * logical logical | + * receiver waiter | + * + + | + * | | | + * | | | + * | +----+------+ | + * | | | | + * | | | | + * device_ctx device_ctx device_ctx | + * | + * User 0 User 1 User Y | + * +------+ +------+ +------+ | + * |misc.c| |misc.c| |misc.c| | + * kernel space +------+ +------+ +------+ | + * | + * +------------------------------------------------------ | + * | | | | + * userspace /dev/seco_muXch0 | | | + * /dev/seco_muXch1 | | + * /dev/seco_muXchY | + * | + * + * When a user sends a command to the seco, it registers its device_ctx as + * waiter of a response from SECO + * + * A user can be registered as receiver of command by the SECO. + * + * When a message is received, the driver select the device_ctx receiving the + * message depending on the tag in the message. It selects the device_ctx + * accordingly. + */ + +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/miscdevice.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of_irq.h> +#include <linux/uaccess.h> +#include <linux/firmware/imx/sci.h> +#include <dt-bindings/firmware/imx/rsrc.h> +#include <linux/firmware/imx/seco_mu_ioctl.h> +#include <linux/mailbox_client.h> + +#define MAX_RECV_SIZE 31 +#define MAX_RECV_SIZE_BYTES (MAX_RECV_SIZE * sizeof(u32)) +#define MAX_MESSAGE_SIZE 31 +#define MAX_MESSAGE_SIZE_BYTES (MAX_MESSAGE_SIZE * sizeof(u32)) +#define MESSAGE_SIZE(hdr) (((struct she_mu_hdr *)(&(hdr)))->size) +#define MESSAGE_TAG(hdr) (((struct she_mu_hdr *)(&(hdr)))->tag) + +#define DEFAULT_MESSAGING_TAG_COMMAND (0x17u) +#define DEFAULT_MESSAGING_TAG_RESPONSE (0xe1u) + +#define SECURE_RAM_BASE_ADDRESS (0x31800000ULL) +#define SECURE_RAM_BASE_ADDRESS_SCU (0x20800000u) +#define SECURE_RAM_SIZE (0x10000ULL) + +#define SECO_MU_DEFAULT_MAX_USERS 4 + +#define SECO_MU_INTERRUPT_INDEX (0u) +#define SECO_DEFAULT_MU_INDEX (1u) +#define SECO_DEFAULT_TZ (0u) +#define DEFAULT_DID (0u) + +#define MAX_DATA_SIZE_PER_USER (65 * 1024) + +/* Header of the messages exchange with the SECO */ +struct she_mu_hdr { + u8 ver; + u8 size; + u8 command; + u8 tag; +} __packed; + +/* Status of a char device */ +enum mu_device_status_t { + MU_FREE, + MU_OPENED +}; + +struct seco_shared_mem { + dma_addr_t dma_addr; + u32 size; + u32 pos; + u8 *ptr; +}; + +struct seco_out_buffer_desc { + u8 *out_ptr; + u8 *out_usr_ptr; + u32 out_size; + struct list_head link; +}; + +/* Private struct for each char device instance. */ +struct seco_mu_device_ctx { + struct device *dev; + struct seco_mu_priv *mu_priv; + struct miscdevice miscdev; + + enum mu_device_status_t status; + wait_queue_head_t wq; + struct semaphore fops_lock; + + u32 pending_hdr; + struct list_head pending_out; + + struct seco_shared_mem secure_mem; + struct seco_shared_mem non_secure_mem; + + u32 temp_cmd[MAX_MESSAGE_SIZE]; + u32 temp_resp[MAX_RECV_SIZE]; + u32 temp_resp_size; + struct notifier_block scu_notify; + bool v2x_reset; +}; + +/* Private struct for seco MU driver. */ +struct seco_mu_priv { + struct seco_mu_device_ctx *cmd_receiver_dev; + struct seco_mu_device_ctx *waiting_rsp_dev; + /* + * prevent parallel access to the MU registers + * e.g. a user trying to send a command while the other one is + * sending a response. + */ + struct mutex mu_lock; + /* + * prevent a command to be sent on the MU while another one is still + * processing. (response to a command is allowed) + */ + struct mutex mu_cmd_lock; + struct device *dev; + u32 seco_mu_id; + u8 cmd_tag; + u8 rsp_tag; + + struct mbox_client cl; + struct mbox_chan *tx_chan; + struct mbox_chan *rx_chan; + + struct imx_sc_ipc *ipc_scu; + u8 seco_part_owner; +}; + +/* macro to log operation of a misc device */ +#define miscdev_dbg(p_miscdev, fmt, va_args...) \ + ({ \ + struct miscdevice *_p_miscdev = p_miscdev; \ + dev_dbg((_p_miscdev)->parent, "%s: " fmt, (_p_miscdev)->name, \ + ##va_args); \ + }) + +#define miscdev_info(p_miscdev, fmt, va_args...) \ + ({ \ + struct miscdevice *_p_miscdev = p_miscdev; \ + dev_info((_p_miscdev)->parent, "%s: " fmt, (_p_miscdev)->name, \ + ##va_args); \ + }) + +#define miscdev_err(p_miscdev, fmt, va_args...) \ + ({ \ + struct miscdevice *_p_miscdev = p_miscdev; \ + dev_err((_p_miscdev)->parent, "%s: " fmt, (_p_miscdev)->name, \ + ##va_args); \ + }) + +/* macro to log operation of a device context */ +#define devctx_dbg(p_devctx, fmt, va_args...) \ + miscdev_dbg(&((p_devctx)->miscdev), fmt, ##va_args) +#define devctx_info(p_devctx, fmt, va_args...) \ + miscdev_info(&((p_devctx)->miscdev), fmt, ##va_args) +#define devctx_err(p_devctx, fmt, va_args...) \ + miscdev_err((&(p_devctx)->miscdev), fmt, ##va_args) + +#define IMX_SC_RM_PERM_FULL 7U /* Full access */ + +/* Give access to SECU to the memory we want to share */ +static int seco_mu_setup_seco_memory_access(struct seco_mu_device_ctx *dev_ctx, + u64 addr, u32 len) +{ + struct seco_mu_priv *priv = dev_get_drvdata(dev_ctx->dev); + int ret; + u8 mr; + + ret = imx_sc_rm_find_memreg(priv->ipc_scu, &mr, addr, addr + len); + if (ret) { + devctx_err(dev_ctx, "Fail find memreg\n"); + goto exit; + } + + ret = imx_sc_rm_set_memreg_permissions(priv->ipc_scu, mr, + priv->seco_part_owner, + IMX_SC_RM_PERM_FULL); + if (ret) { + devctx_err(dev_ctx, "Fail set permission for resource\n"); + goto exit; + } + +exit: + return ret; +} + +/* + * File operations for user-space + */ +/* Open a char device. */ +static int seco_mu_fops_open(struct inode *nd, struct file *fp) +{ + struct seco_mu_device_ctx *dev_ctx = container_of(fp->private_data, + struct seco_mu_device_ctx, miscdev); + int err; + + /* Avoid race if opened at the same time */ + if (down_trylock(&dev_ctx->fops_lock)) + return -EBUSY; + + /* Authorize only 1 instance. */ + if (dev_ctx->status != MU_FREE) { + err = -EBUSY; + goto exit; + } + + /* + * Allocate some memory for data exchanges with SECO. + * This will be used for data not requiring secure memory. + */ + dev_ctx->non_secure_mem.ptr = dmam_alloc_coherent(dev_ctx->dev, + MAX_DATA_SIZE_PER_USER, + &dev_ctx->non_secure_mem.dma_addr, + GFP_KERNEL); + if (!dev_ctx->non_secure_mem.ptr) { + err = -ENOMEM; + devctx_err(dev_ctx, "Failed to map shared memory with SECO\n"); + goto exit; + } + + err = seco_mu_setup_seco_memory_access(dev_ctx, + dev_ctx->non_secure_mem.dma_addr, + MAX_DATA_SIZE_PER_USER); + if (err) { + err = -EPERM; + devctx_err(dev_ctx, + "Failed to share access to shared memory\n"); + goto free_coherent; + } + + dev_ctx->non_secure_mem.size = MAX_DATA_SIZE_PER_USER; + dev_ctx->non_secure_mem.pos = 0; + dev_ctx->status = MU_OPENED; + + dev_ctx->pending_hdr = 0; + dev_ctx->v2x_reset = 0; + + goto exit; + +free_coherent: + dmam_free_coherent(dev_ctx->mu_priv->dev, MAX_DATA_SIZE_PER_USER, + dev_ctx->non_secure_mem.ptr, + dev_ctx->non_secure_mem.dma_addr); + +exit: + up(&dev_ctx->fops_lock); + return err; +} + +/* Close a char device. */ +static int seco_mu_fops_close(struct inode *nd, struct file *fp) +{ + struct seco_mu_device_ctx *dev_ctx = container_of(fp->private_data, + struct seco_mu_device_ctx, miscdev); + struct seco_mu_priv *mu_priv = dev_ctx->mu_priv; + struct seco_out_buffer_desc *out_buf_desc; + + /* Avoid race if closed at the same time */ + if (down_trylock(&dev_ctx->fops_lock)) + return -EBUSY; + + /* The device context has not been opened */ + if (dev_ctx->status != MU_OPENED) + goto exit; + + /* check if this device was registered as command receiver. */ + if (mu_priv->cmd_receiver_dev == dev_ctx) + mu_priv->cmd_receiver_dev = NULL; + + /* check if this device was registered as waiting response. */ + if (mu_priv->waiting_rsp_dev == dev_ctx) { + mu_priv->waiting_rsp_dev = NULL; + mutex_unlock(&mu_priv->mu_cmd_lock); + } + + /* Unmap secure memory shared buffer. */ + if (dev_ctx->secure_mem.ptr) + devm_iounmap(dev_ctx->dev, dev_ctx->secure_mem.ptr); + + dev_ctx->secure_mem.ptr = NULL; + dev_ctx->secure_mem.dma_addr = 0; + dev_ctx->secure_mem.size = 0; + dev_ctx->secure_mem.pos = 0; + + /* Free non-secure shared buffer. */ + dmam_free_coherent(dev_ctx->mu_priv->dev, MAX_DATA_SIZE_PER_USER, + dev_ctx->non_secure_mem.ptr, + dev_ctx->non_secure_mem.dma_addr); + + dev_ctx->non_secure_mem.ptr = NULL; + dev_ctx->non_secure_mem.dma_addr = 0; + dev_ctx->non_secure_mem.size = 0; + dev_ctx->non_secure_mem.pos = 0; + + while (!list_empty(&dev_ctx->pending_out)) { + out_buf_desc = list_first_entry_or_null(&dev_ctx->pending_out, + struct seco_out_buffer_desc, + link); + __list_del_entry(&out_buf_desc->link); + devm_kfree(dev_ctx->dev, out_buf_desc); + } + + dev_ctx->status = MU_FREE; + +exit: + up(&dev_ctx->fops_lock); + return 0; +} + +/* Write a message to the MU. */ +static ssize_t seco_mu_fops_write(struct file *fp, const char __user *buf, + size_t size, loff_t *ppos) +{ + struct seco_mu_device_ctx *dev_ctx = container_of(fp->private_data, + struct seco_mu_device_ctx, miscdev); + struct seco_mu_priv *mu_priv = dev_ctx->mu_priv; + u32 nb_words = 0, header; + int err; + + devctx_dbg(dev_ctx, "write from buf (%p)%ld, ppos=%lld\n", buf, size, + ((ppos) ? *ppos : 0)); + + if (down_interruptible(&dev_ctx->fops_lock)) + return -EBUSY; + + if (dev_ctx->status != MU_OPENED) { + err = -EINVAL; + goto exit; + } + + if (size < sizeof(struct she_mu_hdr)) { + devctx_err(dev_ctx, "User buffer too small(%ld < %lu)\n", size, + sizeof(struct she_mu_hdr)); + err = -ENOSPC; + goto exit; + } + + if (size > MAX_MESSAGE_SIZE_BYTES) { + devctx_err(dev_ctx, "User buffer too big(%ld > %lu)\n", size, + MAX_MESSAGE_SIZE_BYTES); + err = -ENOSPC; + goto exit; + } + + /* Copy data to buffer */ + err = (int)copy_from_user(dev_ctx->temp_cmd, buf, size); + if (err) { + err = -EFAULT; + devctx_err(dev_ctx, "Fail copy message from user\n"); + goto exit; + } + + print_hex_dump_debug("from user ", DUMP_PREFIX_OFFSET, 4, 4, + dev_ctx->temp_cmd, size, false); + + header = dev_ctx->temp_cmd[0]; + + /* Check the message is valid according to tags */ + if (MESSAGE_TAG(header) == mu_priv->cmd_tag) { + /* + * unlocked in seco_mu_receive_work_handler when the + * response to this command is received. + */ + mutex_lock(&mu_priv->mu_cmd_lock); + mu_priv->waiting_rsp_dev = dev_ctx; + } else if (MESSAGE_TAG(header) == mu_priv->rsp_tag) { + /* Check the device context can send the command */ + if (dev_ctx != mu_priv->cmd_receiver_dev) { + devctx_err(dev_ctx, + "This channel is not configured to send response to SECO\n"); + err = -EPERM; + goto exit; + } + } else { + devctx_err(dev_ctx, "The message does not have a valid TAG\n"); + err = -EINVAL; + goto exit; + } + + /* + * Check that the size passed as argument matches the size + * carried in the message. + */ + nb_words = MESSAGE_SIZE(header); + if (nb_words * sizeof(u32) != size) { + devctx_err(dev_ctx, "User buffer too small\n"); + goto exit; + } + + mutex_lock(&mu_priv->mu_lock); + + /* Send message */ + devctx_dbg(dev_ctx, "sending message\n"); + err = mbox_send_message(mu_priv->tx_chan, dev_ctx->temp_cmd); + if (err < 0) { + devctx_err(dev_ctx, "Failed to send message\n"); + goto unlock; + } + + err = nb_words * (u32)sizeof(u32); + +unlock: + mutex_unlock(&mu_priv->mu_lock); + +exit: + up(&dev_ctx->fops_lock); + return err; +} + +/* + * Read a message from the MU. + * Blocking until a message is available. + */ +static ssize_t seco_mu_fops_read(struct file *fp, char __user *buf, + size_t size, loff_t *ppos) +{ + struct seco_mu_device_ctx *dev_ctx = container_of(fp->private_data, + struct seco_mu_device_ctx, miscdev); + u32 data_size = 0, size_to_copy = 0; + struct seco_out_buffer_desc *b_desc; + int err; + + devctx_dbg(dev_ctx, "read to buf %p(%ld), ppos=%lld\n", buf, size, + ((ppos) ? *ppos : 0)); + + if (down_interruptible(&dev_ctx->fops_lock)) + return -EBUSY; + + if (dev_ctx->status != MU_OPENED) { + err = -EINVAL; + goto exit; + } + + if (dev_ctx->v2x_reset) { + err = -EINVAL; + goto exit; + } + + /* Wait until the complete message is received on the MU. */ + err = wait_event_interruptible(dev_ctx->wq, dev_ctx->pending_hdr != 0); + if (err) { + devctx_err(dev_ctx, "Interrupted by signal\n"); + goto exit; + } + + if (dev_ctx->v2x_reset) { + err = -EINVAL; + dev_ctx->v2x_reset = 0; + goto exit; + } + + devctx_dbg(dev_ctx, "%s %s\n", __func__, + "message received, start transmit to user"); + + /* Check that the size passed as argument is larger than + * the one carried in the message. + */ + data_size = dev_ctx->temp_resp_size * sizeof(u32); + size_to_copy = data_size; + if (size_to_copy > size) { + devctx_dbg(dev_ctx, "User buffer too small (%ld < %d)\n", + size, size_to_copy); + size_to_copy = size; + } + + /* We may need to copy the output data to user before + * delivering the completion message. + */ + while (!list_empty(&dev_ctx->pending_out)) { + b_desc = list_first_entry_or_null(&dev_ctx->pending_out, + struct seco_out_buffer_desc, + link); + if (b_desc->out_usr_ptr && b_desc->out_ptr) { + devctx_dbg(dev_ctx, "Copy output data to user\n"); + err = (int)copy_to_user(b_desc->out_usr_ptr, + b_desc->out_ptr, + b_desc->out_size); + if (err) { + devctx_err(dev_ctx, + "Failed to copy output data to user\n"); + err = -EFAULT; + goto exit; + } + } + __list_del_entry(&b_desc->link); + devm_kfree(dev_ctx->dev, b_desc); + } + + /* Copy data from the buffer */ + print_hex_dump_debug("to user ", DUMP_PREFIX_OFFSET, 4, 4, + dev_ctx->temp_resp, size_to_copy, false); + err = (int)copy_to_user(buf, dev_ctx->temp_resp, size_to_copy); + if (err) { + devctx_err(dev_ctx, "Failed to copy to user\n"); + err = -EFAULT; + goto exit; + } + + err = size_to_copy; + + /* free memory allocated on the shared buffers. */ + dev_ctx->secure_mem.pos = 0; + dev_ctx->non_secure_mem.pos = 0; + + dev_ctx->pending_hdr = 0; + +exit: + up(&dev_ctx->fops_lock); + return err; +} + +/* Configure the shared memory according to user config */ +static int +seco_mu_ioctl_shared_mem_cfg_handler(struct seco_mu_device_ctx *dev_ctx, + unsigned long arg) +{ + struct seco_mu_ioctl_shared_mem_cfg cfg; + int err = -EINVAL; + u64 high_boundary; + + /* Check if not already configured. */ + if (dev_ctx->secure_mem.dma_addr != 0u) { + devctx_err(dev_ctx, "Shared memory not configured\n"); + goto exit; + } + + err = (int)copy_from_user(&cfg, (u8 *)arg, + sizeof(cfg)); + if (err) { + devctx_err(dev_ctx, "Fail copy shared memory config to user\n"); + err = -EFAULT; + goto exit; + } + + devctx_dbg(dev_ctx, "cfg offset: %u(%d)\n", cfg.base_offset, cfg.size); + + high_boundary = cfg.base_offset; + if (high_boundary > SECURE_RAM_SIZE) { + devctx_err(dev_ctx, "base offset is over secure memory\n"); + err = -ENOMEM; + goto exit; + } + + high_boundary += cfg.size; + if (high_boundary > SECURE_RAM_SIZE) { + devctx_err(dev_ctx, "total memory is over secure memory\n"); + err = -ENOMEM; + goto exit; + } + + dev_ctx->secure_mem.dma_addr = (dma_addr_t)cfg.base_offset; + dev_ctx->secure_mem.size = cfg.size; + dev_ctx->secure_mem.pos = 0; + dev_ctx->secure_mem.ptr = devm_ioremap(dev_ctx->dev, + (phys_addr_t)(SECURE_RAM_BASE_ADDRESS + + (u64)dev_ctx->secure_mem.dma_addr), + dev_ctx->secure_mem.size); + if (!dev_ctx->secure_mem.ptr) { + devctx_err(dev_ctx, "Failed to map secure memory\n"); + err = -ENOMEM; + goto exit; + } + +exit: + return err; +} + +/* + * Copy a buffer of daa to/from the user and return the address to use in + * messages + */ +static int seco_mu_ioctl_setup_iobuf_handler(struct seco_mu_device_ctx *dev_ctx, + unsigned long arg) +{ + struct seco_out_buffer_desc *out_buf_desc; + struct seco_mu_ioctl_setup_iobuf io; + struct seco_shared_mem *shared_mem; + int err = -EINVAL; + u32 pos; + + err = (int)copy_from_user(&io, + (u8 *)arg, + sizeof(io)); + if (err) { + devctx_err(dev_ctx, "Failed copy iobuf config from user\n"); + err = -EFAULT; + goto exit; + } + + devctx_dbg(dev_ctx, "io [buf: %p(%d) flag: %x]\n", + io.user_buf, io.length, io.flags); + + if (io.length == 0 || !io.user_buf) { + /* + * Accept NULL pointers since some buffers are optional + * in SECO commands. In this case we should return 0 as + * pointer to be embedded into the message. + * Skip all data copy part of code below. + */ + io.seco_addr = 0; + goto copy; + } + + /* Select the shared memory to be used for this buffer. */ + if (io.flags & SECO_MU_IO_FLAGS_USE_SEC_MEM) { + /* App requires to use secure memory for this buffer.*/ + shared_mem = &dev_ctx->secure_mem; + } else { + /* No specific requirement for this buffer. */ + shared_mem = &dev_ctx->non_secure_mem; + } + + /* Check there is enough space in the shared memory. */ + if (io.length >= shared_mem->size - shared_mem->pos) { + devctx_err(dev_ctx, "Not enough space in shared memory\n"); + err = -ENOMEM; + goto exit; + } + + /* Allocate space in shared memory. 8 bytes aligned. */ + pos = shared_mem->pos; + shared_mem->pos += round_up(io.length, 8u); + io.seco_addr = (u64)shared_mem->dma_addr + pos; + + if ((io.flags & SECO_MU_IO_FLAGS_USE_SEC_MEM) && + !(io.flags & SECO_MU_IO_FLAGS_USE_SHORT_ADDR)) + /*Add base address to get full address.*/ + io.seco_addr += SECURE_RAM_BASE_ADDRESS_SCU; + + if (io.flags & SECO_MU_IO_FLAGS_IS_INPUT) { + /* + * buffer is input: + * copy data from user space to this allocated buffer. + */ + err = (int)copy_from_user(shared_mem->ptr + pos, io.user_buf, + io.length); + if (err) { + devctx_err(dev_ctx, + "Failed copy data to shared memory\n"); + err = -EFAULT; + goto exit; + } + } else { + /* + * buffer is output: + * add an entry in the "pending buffers" list so data + * can be copied to user space when receiving SECO + * response. + */ + out_buf_desc = devm_kmalloc(dev_ctx->dev, sizeof(*out_buf_desc), + GFP_KERNEL); + if (!out_buf_desc) { + err = -ENOMEM; + devctx_err(dev_ctx, + "Failed allocating mem for pending buffer\n" + ); + goto exit; + } + + out_buf_desc->out_ptr = shared_mem->ptr + pos; + out_buf_desc->out_usr_ptr = io.user_buf; + out_buf_desc->out_size = io.length; + list_add_tail(&out_buf_desc->link, &dev_ctx->pending_out); + } + +copy: + /* Provide the seco address to user space only if success. */ + err = (int)copy_to_user((u8 *)arg, &io, + sizeof(io)); + if (err) { + devctx_err(dev_ctx, "Failed to copy iobuff setup to user\n"); + err = -EFAULT; + goto exit; + } + +exit: + return err; +} + +/* Retrieve info about the MU */ +static int seco_mu_ioctl_get_mu_info_handler(struct seco_mu_device_ctx *dev_ctx, + unsigned long arg) +{ + struct seco_mu_priv *priv = dev_get_drvdata(dev_ctx->dev); + struct seco_mu_ioctl_get_mu_info info; + int err = -EINVAL; + + info.seco_mu_idx = (u8)priv->seco_mu_id; + info.interrupt_idx = SECO_MU_INTERRUPT_INDEX; + info.tz = SECO_DEFAULT_TZ; + + err = imx_sc_rm_get_did(priv->ipc_scu, &info.did); + if (err) { + devctx_err(dev_ctx, "Get did failed\n"); + goto exit; + } + + devctx_dbg(dev_ctx, + "info [mu_idx: %d, irq_idx: %d, tz: 0x%x, did: 0x%x]\n", + info.seco_mu_idx, info.interrupt_idx, info.tz, info.did); + + err = (int)copy_to_user((u8 *)arg, &info, + sizeof(info)); + if (err) { + devctx_err(dev_ctx, "Failed to copy mu info to user\n"); + err = -EFAULT; + goto exit; + } + +exit: + return err; +} + +static int seco_mu_ioctl_signed_msg_handler(struct seco_mu_device_ctx *dev_ctx, + unsigned long arg) +{ + struct seco_shared_mem *shared_mem = &dev_ctx->non_secure_mem; + struct seco_mu_priv *priv = dev_get_drvdata(dev_ctx->dev); + struct seco_mu_ioctl_signed_message msg; + int err = -EINVAL; + u64 addr; + u32 pos; + + err = (int)copy_from_user(&msg, + (u8 *)arg, + sizeof(msg)); + if (err) { + devctx_err(dev_ctx, "Failed to copy from user: %d\n", err); + err = -EFAULT; + goto exit; + } + + /* Check there is enough space in the shared memory. */ + if (msg.msg_size >= shared_mem->size - shared_mem->pos) { + devctx_err(dev_ctx, "Not enough mem: %d left, %d required\n", + shared_mem->size - shared_mem->pos, msg.msg_size); + err = -ENOMEM; + goto exit; + } + + /* Allocate space in shared memory. 8 bytes aligned. */ + pos = shared_mem->pos; + + /* get physical address from the pos */ + addr = (u64)shared_mem->dma_addr + pos; + + /* copy signed message from user space to this allocated buffer */ + err = (int)copy_from_user(shared_mem->ptr + pos, msg.message, + msg.msg_size); + if (err) { + devctx_err(dev_ctx, "Failed to signed message from user: %d\n", + err); + err = -EFAULT; + goto exit; + } + + /* Send the message to SECO through SCU */ + msg.error_code = imx_sc_seco_sab_msg(priv->ipc_scu, addr); + + err = (int)copy_to_user((u8 *)arg, &msg, + sizeof(msg)); + if (err) { + devctx_err(dev_ctx, "Failed to copy to user: %d\n", err); + err = -EFAULT; + goto exit; + } + +exit: + return err; +} + +/* IOCTL entry point of a char device */ +static long seco_mu_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + struct seco_mu_device_ctx *dev_ctx = container_of(fp->private_data, + struct seco_mu_device_ctx, miscdev); + struct seco_mu_priv *mu_priv = dev_ctx->mu_priv; + int err = -EINVAL; + + /* Prevent race during change of device context */ + if (down_interruptible(&dev_ctx->fops_lock)) + return -EBUSY; + + switch (cmd) { + case SECO_MU_IOCTL_ENABLE_CMD_RCV: + if (!mu_priv->cmd_receiver_dev) { + devctx_dbg(dev_ctx, "setting as receiver\n"); + mu_priv->cmd_receiver_dev = dev_ctx; + err = 0; + }; + break; + case SECO_MU_IOCTL_SHARED_BUF_CFG: + err = seco_mu_ioctl_shared_mem_cfg_handler(dev_ctx, arg); + break; + case SECO_MU_IOCTL_SETUP_IOBUF: + err = seco_mu_ioctl_setup_iobuf_handler(dev_ctx, arg); + break; + case SECO_MU_IOCTL_GET_MU_INFO: + err = seco_mu_ioctl_get_mu_info_handler(dev_ctx, arg); + break; + case SECO_MU_IOCTL_SIGNED_MESSAGE: + err = seco_mu_ioctl_signed_msg_handler(dev_ctx, arg); + break; + default: + err = -EINVAL; + devctx_dbg(dev_ctx, "IOCTL %.8x not supported\n", cmd); + } + + up(&dev_ctx->fops_lock); + return (long)err; +} + +/* + * Callback called by mailbox FW when data are received + */ +static void seco_mu_rx_callback(struct mbox_client *c, void *msg) +{ + struct device *dev = c->dev; + struct seco_mu_priv *priv = dev_get_drvdata(dev); + struct seco_mu_device_ctx *dev_ctx; + bool is_response = false; + int msg_size; + u32 header; + + dev_dbg(dev, "Message received on mailbox\n"); + + /* The function can be called with NULL msg */ + if (!msg) { + dev_err(dev, "Message is invalid\n"); + return; + } + + if (IS_ERR(msg)) { + dev_err(dev, "Error during reception of message: %ld\n", + PTR_ERR(msg)); + return; + } + + header = *(u32 *)msg; + + dev_dbg(dev, "Selecting device\n"); + + /* Incoming command: wake up the receiver if any. */ + if (MESSAGE_TAG(header) == priv->cmd_tag) { + dev_dbg(dev, "Selecting cmd receiver\n"); + dev_ctx = priv->cmd_receiver_dev; + } else if (MESSAGE_TAG(header) == priv->rsp_tag) { + dev_dbg(dev, "Selecting rsp waiter\n"); + dev_ctx = priv->waiting_rsp_dev; + is_response = true; + } else { + dev_err(dev, "Failed to select a device for message: %.8x\n", + header); + return; + } + + if (!dev_ctx) { + dev_err(dev, "No device context selected for message: %.8x\n", + header); + return; + } + + /* Init reception */ + msg_size = MESSAGE_SIZE(header); + if (msg_size > MAX_RECV_SIZE) { + devctx_err(dev_ctx, "Message is too big (%d > %d)", msg_size, + MAX_RECV_SIZE); + return; + } + + memcpy(dev_ctx->temp_resp, msg, msg_size * sizeof(u32)); + dev_ctx->temp_resp_size = msg_size; + + /* Allow user to read */ + dev_ctx->pending_hdr = dev_ctx->temp_resp[0]; + wake_up_interruptible(&dev_ctx->wq); + + if (is_response) { + /* Allow user to send new command */ + mutex_unlock(&priv->mu_cmd_lock); + } +} + +#define SECO_FW_VER_FEAT_MASK (0x0000FFF0u) +#define SECO_FW_VER_FEAT_SHIFT (0x04u) +#define SECO_FW_VER_FEAT_MIN_ALL_MU (0x04u) + +/* + * Get SECO FW version and check if it supports receiving commands on all MUs + * The version is retrieved through SCU since this is the only communication + * channel to SECO always present. + */ +static int seco_mu_check_all_mu_supported(struct device *dev) +{ + struct seco_mu_priv *priv = dev_get_drvdata(dev); + u32 seco_ver; + int ret; + + ret = imx_sc_seco_build_info(priv->ipc_scu, &seco_ver, NULL); + if (ret) { + dev_err(dev, "failed to retrieve SECO build info\n"); + goto exit; + } + + if (((seco_ver & SECO_FW_VER_FEAT_MASK) >> SECO_FW_VER_FEAT_SHIFT) + < SECO_FW_VER_FEAT_MIN_ALL_MU) { + dev_err(dev, "current SECO FW do not support MU with Linux\n"); + ret = -ENOTSUPP; + goto exit; + } + +exit: + return ret; +} + +/* Char driver setup */ +static const struct file_operations seco_mu_fops = { + .open = seco_mu_fops_open, + .owner = THIS_MODULE, + .read = seco_mu_fops_read, + .release = seco_mu_fops_close, + .write = seco_mu_fops_write, + .unlocked_ioctl = seco_mu_ioctl, +}; + +/* interface for managed res to free a mailbox channel */ +static void if_mbox_free_channel(void *mbox_chan) +{ + mbox_free_channel(mbox_chan); +} + +/* interface for managed res to unregister a char device */ +static void if_misc_deregister(void *miscdevice) +{ + misc_deregister(miscdevice); +} + +static int seco_mu_request_channel(struct device *dev, + struct mbox_chan **chan, + const char *name) +{ + struct seco_mu_priv *priv = dev_get_drvdata(dev); + struct mbox_chan *t_chan; + int ret = 0; + + t_chan = mbox_request_channel_byname(&priv->cl, name); + if (IS_ERR(t_chan)) { + ret = PTR_ERR(t_chan); + if (ret != -EPROBE_DEFER) + dev_err(dev, + "Failed to request chan %s ret %d\n", name, + ret); + goto exit; + } + + ret = devm_add_action(dev, if_mbox_free_channel, t_chan); + if (ret) { + dev_err(dev, "failed to add devm removal of mbox %s\n", name); + goto exit; + } + + *chan = t_chan; + +exit: + return ret; +} + +static int imx_sc_v2x_reset_notify(struct notifier_block *nb, + unsigned long event, void *group) +{ + struct seco_mu_device_ctx *dev_ctx = container_of(nb, + struct seco_mu_device_ctx, scu_notify); + + if (!(event & IMX_SC_IRQ_V2X_RESET)) + return 0; + + dev_ctx->v2x_reset = true; + + wake_up_interruptible(&dev_ctx->wq); + return 0; +} +/* Driver probe.*/ +static int seco_mu_probe(struct platform_device *pdev) +{ + struct seco_mu_device_ctx *dev_ctx; + struct device *dev = &pdev->dev; + struct seco_mu_priv *priv; + struct device_node *np; + int max_nb_users = 0; + char *devname; + int ret; + int i; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + dev_err(dev, "Fail allocate mem for private data\n"); + goto exit; + } + priv->dev = dev; + dev_set_drvdata(dev, priv); + + /* + * Get the address of MU to be used for communication with the SCU + */ + np = pdev->dev.of_node; + if (!np) { + dev_err(dev, "Cannot find MU User entry in device tree\n"); + ret = -ENOTSUPP; + goto exit; + } + + ret = imx_scu_get_handle(&priv->ipc_scu); + if (ret) { + dev_err(dev, "Fail to retrieve IPC handle\n"); + goto exit; + } + + ret = imx_sc_rm_get_resource_owner(priv->ipc_scu, IMX_SC_R_SECO, + &priv->seco_part_owner); + if (ret) { + dev_err(dev, "Fail get owner of SECO resource\n"); + goto exit; + } + + ret = seco_mu_check_all_mu_supported(dev); + if (ret) { + dev_err(dev, "Fail seco_mu_check_all_mu_supported\n"); + goto exit; + } + + /* Initialize the mutex. */ + mutex_init(&priv->mu_cmd_lock); + mutex_init(&priv->mu_lock); + + priv->cmd_receiver_dev = NULL; + priv->waiting_rsp_dev = NULL; + + ret = of_property_read_u32(np, "fsl,seco_mu_id", &priv->seco_mu_id); + if (ret) { + dev_warn(dev, "%s: Not able to read mu_id", __func__); + priv->seco_mu_id = SECO_DEFAULT_MU_INDEX; + } + + ret = of_property_read_u32(np, "fsl,seco_max_users", &max_nb_users); + if (ret) { + dev_warn(dev, "%s: Not able to read mu_max_user", __func__); + max_nb_users = SECO_MU_DEFAULT_MAX_USERS; + } + + ret = of_property_read_u8(np, "fsl,cmd_tag", &priv->cmd_tag); + if (ret) + priv->cmd_tag = DEFAULT_MESSAGING_TAG_COMMAND; + + ret = of_property_read_u8(np, "fsl,rsp_tag", &priv->rsp_tag); + if (ret) + priv->rsp_tag = DEFAULT_MESSAGING_TAG_RESPONSE; + + /* Mailbox client configuration */ + priv->cl.dev = dev; + priv->cl.knows_txdone = true; + priv->cl.rx_callback = seco_mu_rx_callback; + + ret = seco_mu_request_channel(dev, &priv->tx_chan, "txdb"); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to request txdb channel\n"); + + goto exit; + } + + ret = seco_mu_request_channel(dev, &priv->rx_chan, "rxdb"); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to request rxdb channel\n"); + + goto exit; + } + + /* Create users */ + for (i = 0; i < max_nb_users; i++) { + dev_ctx = devm_kzalloc(dev, sizeof(*dev_ctx), GFP_KERNEL); + if (!dev_ctx) { + ret = -ENOMEM; + dev_err(dev, + "Fail to allocate memory for device context\n"); + goto exit; + } + + dev_ctx->dev = dev; + dev_ctx->status = MU_FREE; + dev_ctx->mu_priv = priv; + /* Default value invalid for an header. */ + init_waitqueue_head(&dev_ctx->wq); + + INIT_LIST_HEAD(&dev_ctx->pending_out); + sema_init(&dev_ctx->fops_lock, 1); + + devname = devm_kasprintf(dev, GFP_KERNEL, "seco_mu%d_ch%d", + priv->seco_mu_id, i); + if (!devname) { + ret = -ENOMEM; + dev_err(dev, + "Fail to allocate memory for misc dev name\n"); + goto exit; + } + + dev_ctx->miscdev.name = devname; + dev_ctx->miscdev.minor = MISC_DYNAMIC_MINOR; + dev_ctx->miscdev.fops = &seco_mu_fops; + dev_ctx->miscdev.parent = dev; + ret = misc_register(&dev_ctx->miscdev); + if (ret) { + dev_err(dev, "failed to register misc device %d\n", + ret); + goto exit; + } + + ret = devm_add_action(dev, if_misc_deregister, + &dev_ctx->miscdev); + + dev_ctx->scu_notify.notifier_call = imx_sc_v2x_reset_notify; + + ret = imx_scu_irq_register_notifier(&dev_ctx->scu_notify); + if (ret) { + dev_err(&pdev->dev, "v2x reqister scu notifier failed.\n"); + return ret; + } + + if (ret) + dev_warn(dev, + "failed to add managed removal of miscdev\n"); + } + + ret = imx_scu_irq_group_enable(IMX_SC_IRQ_GROUP_WAKE, + IMX_SC_IRQ_V2X_RESET, true); + if (ret) { + dev_warn(&pdev->dev, "v2x Enable irq failed.\n"); + return ret; + } + +exit: + return ret; +} + +static const struct of_device_id seco_mu_match[] = { + { + .compatible = "fsl,imx-seco-mu", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, seco_mu_match); + +static struct platform_driver seco_mu_driver = { + .driver = { + .name = "seco_mu", + .of_match_table = seco_mu_match, + }, + .probe = seco_mu_probe, +}; + +module_platform_driver(seco_mu_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("IMX Seco MU"); +MODULE_AUTHOR("NXP"); diff --git a/drivers/firmware/imx/senclave_base_msg.c b/drivers/firmware/imx/senclave_base_msg.c new file mode 100644 index 000000000000..ad8b88648968 --- /dev/null +++ b/drivers/firmware/imx/senclave_base_msg.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 NXP + * Author: Pankaj <pankaj.gupta@nxp.com> + Alice Guo <alice.guo@nxp.com> + */ + +#include <linux/types.h> +#include <linux/completion.h> +#include <linux/mailbox_client.h> + +#include <linux/firmware/imx/senclave_base_msg.h> +#include <linux/firmware/imx/sentnl_mu_ioctl.h> + +#include "sentnl_mu.h" + +/* Fill a command message header with a given command ID and length in bytes. */ +static int plat_fill_cmd_msg_hdr(struct mu_hdr *hdr, uint8_t cmd, uint32_t len) +{ + struct sentnl_mu_priv *priv = NULL; + int err = 0; + + err = get_sentnl_mu_priv(&priv); + if (err) { + pr_err("Error: iMX Sentinel MU is not probed successfully.\n"); + return err; + } + hdr->tag = priv->cmd_tag; + hdr->ver = MESSAGING_VERSION_6; + hdr->command = cmd; + hdr->size = (uint8_t)(len / sizeof(uint32_t)); + + return err; +} + +static int imx_sentnl_msg_send_rcv(struct sentnl_mu_priv *priv) +{ + unsigned int wait; + int err = 0; + + mutex_lock(&priv->mu_cmd_lock); + mutex_lock(&priv->mu_lock); + + err = mbox_send_message(priv->tx_chan, &priv->tx_msg); + if (err < 0) { + pr_err("Error: mbox_send_message failure.\n"); + mutex_unlock(&priv->mu_lock); + return err; + } + mutex_unlock(&priv->mu_lock); + + wait = msecs_to_jiffies(1000); + if (!wait_for_completion_timeout(&priv->done, wait)) { + mutex_unlock(&priv->mu_cmd_lock); + pr_err("Error: wait_for_completion timed out.\n"); + return -ETIMEDOUT; + } + + /* As part of func sentnl_mu_rx_callback() execution, + * response will copied to sentnl_msg->rsp_msg. + * + * Lock: (mutex_unlock(&sentnl_mu_priv->mu_cmd_lock), + * will be unlocked if it is a response. + */ + return err; +} + +static int read_otp_uniq_id(struct sentnl_mu_priv *priv, u32 *value) +{ + unsigned int tag, command, size, ver, status; + + 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 == 0xe1 && command == SENTNL_READ_FUSE_REQ && + size == 0x07 && ver == SENTNL_VERSION && status == SENTNL_SUCCESS_IND) { + value[0] = priv->rx_msg.data[1]; + value[1] = priv->rx_msg.data[2]; + value[2] = priv->rx_msg.data[3]; + value[3] = priv->rx_msg.data[4]; + return 0; + } + + return -EINVAL; +} + +static int read_fuse_word(struct sentnl_mu_priv *priv, u32 *value) +{ + unsigned int tag, command, size, ver, status; + + 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 == 0xe1 && command == SENTNL_READ_FUSE_REQ && + size == 0x03 && ver == 0x06 && status == SENTNL_SUCCESS_IND) { + value[0] = priv->rx_msg.data[1]; + return 0; + } + + return -EINVAL; +} + +int read_common_fuse(uint16_t fuse_id, u32 *value) +{ + struct sentnl_mu_priv *priv = NULL; + int err = 0; + + err = get_sentnl_mu_priv(&priv); + if (err) { + pr_err("Error: iMX Sentinel MU is not probed successfully.\n"); + return err; + } + err = plat_fill_cmd_msg_hdr((struct mu_hdr *)&priv->tx_msg.header, SENTNL_READ_FUSE_REQ, 8); + if (err) { + pr_err("Error: plat_fill_cmd_msg_hdr failed.\n"); + return err; + } + + priv->tx_msg.data[0] = fuse_id; + err = imx_sentnl_msg_send_rcv(priv); + if (err < 0) + return err; + + switch (fuse_id) { + case OTP_UNIQ_ID: + err = read_otp_uniq_id(priv, value); + break; + default: + err = read_fuse_word(priv, value); + break; + } + + return err; +} +EXPORT_SYMBOL_GPL(read_common_fuse); diff --git a/drivers/firmware/imx/sentnl_mu.c b/drivers/firmware/imx/sentnl_mu.c new file mode 100644 index 000000000000..2913c72f296a --- /dev/null +++ b/drivers/firmware/imx/sentnl_mu.c @@ -0,0 +1,918 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 NXP + * Author: Alice Guo <alice.guo@nxp.com> + * Author: Pankaj Gupta <pankaj.gupta@nxp.com> + */ + +#include <linux/dma-mapping.h> +#include <linux/completion.h> +#include <linux/dev_printk.h> +#include <linux/errno.h> +#include <linux/export.h> +#include <linux/firmware/imx/senclave_base_msg.h> +#include <linux/firmware/imx/sentnl_mu_ioctl.h> +#include <linux/io.h> +#include <linux/init.h> +#include <linux/mailbox_client.h> +#include <linux/miscdevice.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/sys_soc.h> + +#include "sentnl_mu.h" + +struct sentnl_mu_priv *sentnl_priv_export; + +int get_sentnl_mu_priv(struct sentnl_mu_priv **export) +{ + if (!sentnl_priv_export) + return -EPROBE_DEFER; + + *export = sentnl_priv_export; + return 0; +} +EXPORT_SYMBOL_GPL(get_sentnl_mu_priv); + + +/* + * Callback called by mailbox FW when data are received + */ +static void sentnl_mu_rx_callback(struct mbox_client *c, void *msg) +{ + struct device *dev = c->dev; + struct sentnl_mu_priv *priv = dev_get_drvdata(dev); + struct sentnl_mu_device_ctx *dev_ctx; + bool is_response = false; + int msg_size; + struct mu_hdr header; + + dev_dbg(dev, "Message received on mailbox\n"); + + /* The function can be called with NULL msg */ + if (!msg) { + dev_err(dev, "Message is invalid\n"); + return; + } + + if (IS_ERR(msg)) { + dev_err(dev, "Error during reception of message: %ld\n", + PTR_ERR(msg)); + return; + } + + header.tag = ((u8 *)msg)[3]; + header.command = ((u8 *)msg)[2]; + header.size = ((u8 *)msg)[1]; + header.ver = ((u8 *)msg)[0]; + + dev_dbg(dev, "Selecting device\n"); + + /* Incoming command: wake up the receiver if any. */ + if (header.tag == priv->cmd_tag) { + dev_dbg(dev, "Selecting cmd receiver\n"); + dev_ctx = priv->cmd_receiver_dev; + } else if (header.tag == priv->rsp_tag) { + if (priv->waiting_rsp_dev) { + dev_dbg(dev, "Selecting rsp waiter\n"); + dev_ctx = priv->waiting_rsp_dev; + is_response = true; + } else { + /* Reading the Sentinel response + * to the command sent by other + * linux kernel services. + */ + spin_lock(&priv->lock); + priv->rx_msg = *(struct sentnl_api_msg *)msg; + complete(&priv->done); + spin_unlock(&priv->lock); + mutex_unlock(&priv->mu_cmd_lock); + return; + } + } else { + dev_err(dev, "Failed to select a device for message: %.8x\n", + *((u32 *) &header)); + return; + } + + if (!dev_ctx) { + dev_err(dev, "No device context selected for message: %.8x\n", + *((u32 *)&header)); + return; + } + /* Init reception */ + msg_size = header.size; + if (msg_size > MAX_RECV_SIZE) { + devctx_err(dev_ctx, "Message is too big (%d > %d)", msg_size, + MAX_RECV_SIZE); + return; + } + + memcpy(dev_ctx->temp_resp, msg, msg_size * sizeof(u32)); + dev_ctx->temp_resp_size = msg_size; + + /* Allow user to read */ + dev_ctx->pending_hdr = dev_ctx->temp_resp[0]; + wake_up_interruptible(&dev_ctx->wq); + + if (is_response) { + /* Allow user to send new command */ + mutex_unlock(&priv->mu_cmd_lock); + } +} + +struct device *imx_soc_device_register(void) +{ + struct soc_device_attribute *attr; + struct soc_device *dev; + u32 v[4]; + int err; + + err = read_common_fuse(OTP_UNIQ_ID, v); + if (err) + return NULL; + + attr = kzalloc(sizeof(*attr), GFP_KERNEL); + if (!attr) + return NULL; + + err = of_property_read_string(of_root, "model", &attr->machine); + if (err) { + kfree(attr); + return NULL; + } + attr->family = kasprintf(GFP_KERNEL, "Freescale i.MX"); + attr->revision = kasprintf(GFP_KERNEL, "1.0"); + attr->serial_number = kasprintf(GFP_KERNEL, "%016llX", (u64)v[3] << 32 | v[0]); + attr->soc_id = kasprintf(GFP_KERNEL, "i.MX8ULP"); + + dev = soc_device_register(attr); + if (IS_ERR(dev)) { + kfree(attr->soc_id); + kfree(attr->serial_number); + kfree(attr->revision); + kfree(attr->family); + kfree(attr->machine); + kfree(attr); + return ERR_CAST(dev); + } + + return soc_device_to_device(dev); +} + +/* + * File operations for user-space + */ + +/* Write a message to the MU. */ +static ssize_t sentnl_mu_fops_write(struct file *fp, const char __user *buf, + size_t size, loff_t *ppos) +{ + struct sentnl_mu_device_ctx *dev_ctx = container_of(fp->private_data, + struct sentnl_mu_device_ctx, miscdev); + struct sentnl_mu_priv *sentnl_mu_priv = dev_ctx->priv; + u32 nb_words = 0; + struct mu_hdr header; + int err; + + devctx_dbg(dev_ctx, "write from buf (%p)%ld, ppos=%lld\n", buf, size, + ((ppos) ? *ppos : 0)); + + if (down_interruptible(&dev_ctx->fops_lock)) + return -EBUSY; + + if (dev_ctx->status != MU_OPENED) { + err = -EINVAL; + goto exit; + } + + if (size < 4) {//sizeof(struct she_mu_hdr)) { + devctx_err(dev_ctx, "User buffer too small(%ld < %x)\n", size, 0x4); + //devctx_err(dev_ctx, "User buffer too small(%ld < %lu)\n", size, ()0x4); + // sizeof(struct she_mu_hdr)); + err = -ENOSPC; + goto exit; + } + + if (size > MAX_MESSAGE_SIZE_BYTES) { + devctx_err(dev_ctx, "User buffer too big(%ld > %lu)\n", size, + MAX_MESSAGE_SIZE_BYTES); + err = -ENOSPC; + goto exit; + } + + /* Copy data to buffer */ + err = (int)copy_from_user(dev_ctx->temp_cmd, buf, size); + if (err) { + err = -EFAULT; + devctx_err(dev_ctx, "Fail copy message from user\n"); + goto exit; + } + + print_hex_dump_debug("from user ", DUMP_PREFIX_OFFSET, 4, 4, + dev_ctx->temp_cmd, size, false); + + header = *((struct mu_hdr *) (&dev_ctx->temp_cmd[0])); + + /* Check the message is valid according to tags */ + if (header.tag == sentnl_mu_priv->cmd_tag) { + /* + * unlocked in sentnl_mu_receive_work_handler when the + * response to this command is received. + */ + mutex_lock(&sentnl_mu_priv->mu_cmd_lock); + sentnl_mu_priv->waiting_rsp_dev = dev_ctx; + } else if (header.tag == sentnl_mu_priv->rsp_tag) { + /* Check the device context can send the command */ + if (dev_ctx != sentnl_mu_priv->cmd_receiver_dev) { + devctx_err(dev_ctx, + "This channel is not configured to send response to SECO\n"); + err = -EPERM; + goto exit; + } + } else { + devctx_err(dev_ctx, "The message does not have a valid TAG\n"); + err = -EINVAL; + goto exit; + } + + /* + * Check that the size passed as argument matches the size + * carried in the message. + */ + nb_words = header.size; + if (nb_words * sizeof(u32) != size) { + devctx_err(dev_ctx, "User buffer too small\n"); + goto exit; + } + + mutex_lock(&sentnl_mu_priv->mu_lock); + + /* Send message */ + devctx_dbg(dev_ctx, "sending message\n"); + err = mbox_send_message(sentnl_mu_priv->tx_chan, dev_ctx->temp_cmd); + if (err < 0) { + devctx_err(dev_ctx, "Failed to send message\n"); + goto unlock; + } + + err = nb_words * (u32)sizeof(u32); + +unlock: + mutex_unlock(&sentnl_mu_priv->mu_lock); + +exit: + up(&dev_ctx->fops_lock); + return err; +} + +/* + * Read a message from the MU. + * Blocking until a message is available. + */ +static ssize_t sentnl_mu_fops_read(struct file *fp, char __user *buf, + size_t size, loff_t *ppos) +{ + struct sentnl_mu_device_ctx *dev_ctx = container_of(fp->private_data, + struct sentnl_mu_device_ctx, miscdev); + u32 data_size = 0, size_to_copy = 0; + struct sentnl_obuf_desc *b_desc; + int err; + + devctx_dbg(dev_ctx, "read to buf %p(%ld), ppos=%lld\n", buf, size, + ((ppos) ? *ppos : 0)); + + if (down_interruptible(&dev_ctx->fops_lock)) + return -EBUSY; + + if (dev_ctx->status != MU_OPENED) { + err = -EINVAL; + goto exit; + } + + /* Wait until the complete message is received on the MU. */ + err = wait_event_interruptible(dev_ctx->wq, dev_ctx->pending_hdr != 0); + if (err) { + devctx_err(dev_ctx, "Interrupted by signal\n"); + goto exit; + } + + devctx_dbg(dev_ctx, "%s %s\n", __func__, + "message received, start transmit to user"); + + /* Check that the size passed as argument is larger than + * the one carried in the message. + */ + data_size = dev_ctx->temp_resp_size * sizeof(u32); + size_to_copy = data_size; + if (size_to_copy > size) { + devctx_dbg(dev_ctx, "User buffer too small (%ld < %d)\n", + size, size_to_copy); + size_to_copy = size; + } + + /* We may need to copy the output data to user before + * delivering the completion message. + */ + while (!list_empty(&dev_ctx->pending_out)) { + b_desc = list_first_entry_or_null(&dev_ctx->pending_out, + struct sentnl_obuf_desc, + link); + if (b_desc->out_usr_ptr && b_desc->out_ptr) { + devctx_dbg(dev_ctx, "Copy output data to user\n"); + err = (int)copy_to_user(b_desc->out_usr_ptr, + b_desc->out_ptr, + b_desc->out_size); + if (err) { + devctx_err(dev_ctx, + "Failed to copy output data to user\n"); + err = -EFAULT; + goto exit; + } + } + __list_del_entry(&b_desc->link); + devm_kfree(dev_ctx->dev, b_desc); + } + + /* Copy data from the buffer */ + print_hex_dump_debug("to user ", DUMP_PREFIX_OFFSET, 4, 4, + dev_ctx->temp_resp, size_to_copy, false); + err = (int)copy_to_user(buf, dev_ctx->temp_resp, size_to_copy); + if (err) { + devctx_err(dev_ctx, "Failed to copy to user\n"); + err = -EFAULT; + goto exit; + } + + err = size_to_copy; + + /* free memory allocated on the shared buffers. */ + dev_ctx->secure_mem.pos = 0; + dev_ctx->non_secure_mem.pos = 0; + + dev_ctx->pending_hdr = 0; + +exit: + up(&dev_ctx->fops_lock); + return err; +} + +/* Give access to Sentinel, to the memory we want to share */ +static int sentnl_mu_setup_sentnl_mem_access(struct sentnl_mu_device_ctx *dev_ctx, + u64 addr, u32 len) +{ + /* Assuming Sentinel has access to all the memory regions */ + int ret = 0; + + if (ret) { + devctx_err(dev_ctx, "Fail find memreg\n"); + goto exit; + } + + if (ret) { + devctx_err(dev_ctx, "Fail set permission for resource\n"); + goto exit; + } + +exit: + return ret; +} + +static int sentnl_mu_ioctl_get_mu_info(struct sentnl_mu_device_ctx *dev_ctx, + unsigned long arg) +{ + struct sentnl_mu_priv *priv = dev_get_drvdata(dev_ctx->dev); + struct sentnl_mu_ioctl_get_mu_info info; + int err = -EINVAL; + + info.sentnl_mu_id = (u8)priv->sentnl_mu_id; + info.interrupt_idx = 0; + info.tz = 0; + info.did = 0x7; + + devctx_dbg(dev_ctx, + "info [mu_idx: %d, irq_idx: %d, tz: 0x%x, did: 0x%x]\n", + info.sentnl_mu_id, info.interrupt_idx, info.tz, info.did); + + err = (int)copy_to_user((u8 *)arg, &info, + sizeof(info)); + if (err) { + devctx_err(dev_ctx, "Failed to copy mu info to user\n"); + err = -EFAULT; + goto exit; + } + +exit: + return err; +} + +/* + * Copy a buffer of daa to/from the user and return the address to use in + * messages + */ +static int sentnl_mu_ioctl_setup_iobuf_handler(struct sentnl_mu_device_ctx *dev_ctx, + unsigned long arg) +{ + struct sentnl_obuf_desc *out_buf_desc; + struct sentnl_mu_ioctl_setup_iobuf io = {0}; + struct sentnl_shared_mem *shared_mem; + int err = -EINVAL; + u32 pos; + + err = (int)copy_from_user(&io, + (u8 *)arg, + sizeof(io)); + if (err) { + devctx_err(dev_ctx, "Failed copy iobuf config from user\n"); + err = -EFAULT; + goto exit; + } + + devctx_dbg(dev_ctx, "io [buf: %p(%d) flag: %x]\n", + io.user_buf, io.length, io.flags); + + if (io.length == 0 || !io.user_buf) { + /* + * Accept NULL pointers since some buffers are optional + * in SECO commands. In this case we should return 0 as + * pointer to be embedded into the message. + * Skip all data copy part of code below. + */ + io.sentnl_addr = 0; + goto copy; + } + + /* Select the shared memory to be used for this buffer. */ + if (io.flags & SECO_MU_IO_FLAGS_USE_SEC_MEM) { + /* App requires to use secure memory for this buffer.*/ + devctx_err(dev_ctx, "Failed allocate SEC MEM memory\n"); + err = -EFAULT; + goto exit; + } else { + /* No specific requirement for this buffer. */ + shared_mem = &dev_ctx->non_secure_mem; + } + + /* Check there is enough space in the shared memory. */ + if (io.length >= shared_mem->size - shared_mem->pos) { + devctx_err(dev_ctx, "Not enough space in shared memory\n"); + err = -ENOMEM; + goto exit; + } + + /* Allocate space in shared memory. 8 bytes aligned. */ + pos = shared_mem->pos; + shared_mem->pos += round_up(io.length, 8u); + io.sentnl_addr = (u64)shared_mem->dma_addr + pos; + + if ((io.flags & SECO_MU_IO_FLAGS_USE_SEC_MEM) && + !(io.flags & SECO_MU_IO_FLAGS_USE_SHORT_ADDR)) { + /*Add base address to get full address.*/ + devctx_err(dev_ctx, "Failed allocate SEC MEM memory\n"); + err = -EFAULT; + goto exit; + } + + if (io.flags & SECO_MU_IO_FLAGS_IS_INPUT) { + /* + * buffer is input: + * copy data from user space to this allocated buffer. + */ + err = (int)copy_from_user(shared_mem->ptr + pos, io.user_buf, + io.length); + if (err) { + devctx_err(dev_ctx, + "Failed copy data to shared memory\n"); + err = -EFAULT; + goto exit; + } + } else { + /* + * buffer is output: + * add an entry in the "pending buffers" list so data + * can be copied to user space when receiving SECO + * response. + */ + out_buf_desc = devm_kmalloc(dev_ctx->dev, sizeof(*out_buf_desc), + GFP_KERNEL); + if (!out_buf_desc) { + err = -ENOMEM; + devctx_err(dev_ctx, + "Failed allocating mem for pending buffer\n" + ); + goto exit; + } + + out_buf_desc->out_ptr = shared_mem->ptr + pos; + out_buf_desc->out_usr_ptr = io.user_buf; + out_buf_desc->out_size = io.length; + list_add_tail(&out_buf_desc->link, &dev_ctx->pending_out); + } + +copy: + /* Provide the sentinel address to user space only if success. */ + err = (int)copy_to_user((u8 *)arg, &io, + sizeof(io)); + if (err) { + devctx_err(dev_ctx, "Failed to copy iobuff setup to user\n"); + err = -EFAULT; + goto exit; + } +exit: + return err; +} + + + +/* Open a char device. */ +static int sentnl_mu_fops_open(struct inode *nd, struct file *fp) +{ + struct sentnl_mu_device_ctx *dev_ctx = container_of(fp->private_data, + struct sentnl_mu_device_ctx, + miscdev); + int err; + + /* Avoid race if opened at the same time */ + if (down_trylock(&dev_ctx->fops_lock)) + return -EBUSY; + + /* Authorize only 1 instance. */ + if (dev_ctx->status != MU_FREE) { + err = -EBUSY; + goto exit; + } + + /* + * Allocate some memory for data exchanges with S40x. + * This will be used for data not requiring secure memory. + */ + dev_ctx->non_secure_mem.ptr = dmam_alloc_coherent(dev_ctx->dev, + MAX_DATA_SIZE_PER_USER, + &dev_ctx->non_secure_mem.dma_addr, + GFP_KERNEL); + if (!dev_ctx->non_secure_mem.ptr) { + err = -ENOMEM; + devctx_err(dev_ctx, "Failed to map shared memory with S40x\n"); + goto exit; + } + + err = sentnl_mu_setup_sentnl_mem_access(dev_ctx, + dev_ctx->non_secure_mem.dma_addr, + MAX_DATA_SIZE_PER_USER); + if (err) { + err = -EPERM; + devctx_err(dev_ctx, + "Failed to share access to shared memory\n"); + goto free_coherent; + } + + dev_ctx->non_secure_mem.size = MAX_DATA_SIZE_PER_USER; + dev_ctx->non_secure_mem.pos = 0; + dev_ctx->status = MU_OPENED; + + dev_ctx->pending_hdr = 0; + + goto exit; + +free_coherent: + dmam_free_coherent(dev_ctx->priv->dev, MAX_DATA_SIZE_PER_USER, + dev_ctx->non_secure_mem.ptr, + dev_ctx->non_secure_mem.dma_addr); + +exit: + up(&dev_ctx->fops_lock); + return err; +} + +/* Close a char device. */ +static int sentnl_mu_fops_close(struct inode *nd, struct file *fp) +{ + struct sentnl_mu_device_ctx *dev_ctx = container_of(fp->private_data, + struct sentnl_mu_device_ctx, miscdev); + struct sentnl_mu_priv *priv = dev_ctx->priv; + struct sentnl_obuf_desc *out_buf_desc; + + /* Avoid race if closed at the same time */ + if (down_trylock(&dev_ctx->fops_lock)) + return -EBUSY; + + /* The device context has not been opened */ + if (dev_ctx->status != MU_OPENED) + goto exit; + + /* check if this device was registered as command receiver. */ + if (priv->cmd_receiver_dev == dev_ctx) + priv->cmd_receiver_dev = NULL; + + /* check if this device was registered as waiting response. */ + if (priv->waiting_rsp_dev == dev_ctx) { + priv->waiting_rsp_dev = NULL; + mutex_unlock(&priv->mu_cmd_lock); + } + + /* Unmap secure memory shared buffer. */ + if (dev_ctx->secure_mem.ptr) + devm_iounmap(dev_ctx->dev, dev_ctx->secure_mem.ptr); + + dev_ctx->secure_mem.ptr = NULL; + dev_ctx->secure_mem.dma_addr = 0; + dev_ctx->secure_mem.size = 0; + dev_ctx->secure_mem.pos = 0; + + /* Free non-secure shared buffer. */ + dmam_free_coherent(dev_ctx->priv->dev, MAX_DATA_SIZE_PER_USER, + dev_ctx->non_secure_mem.ptr, + dev_ctx->non_secure_mem.dma_addr); + + dev_ctx->non_secure_mem.ptr = NULL; + dev_ctx->non_secure_mem.dma_addr = 0; + dev_ctx->non_secure_mem.size = 0; + dev_ctx->non_secure_mem.pos = 0; + + while (!list_empty(&dev_ctx->pending_out)) { + out_buf_desc = list_first_entry_or_null(&dev_ctx->pending_out, + struct sentnl_obuf_desc, + link); + __list_del_entry(&out_buf_desc->link); + devm_kfree(dev_ctx->dev, out_buf_desc); + } + + dev_ctx->status = MU_FREE; + +exit: + up(&dev_ctx->fops_lock); + return 0; +} + +/* IOCTL entry point of a char device */ +static long sentnl_mu_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + struct sentnl_mu_device_ctx *dev_ctx = container_of(fp->private_data, + struct sentnl_mu_device_ctx, + miscdev); + struct sentnl_mu_priv *sentnl_mu_priv = dev_ctx->priv; + int err = -EINVAL; + + /* Prevent race during change of device context */ + if (down_interruptible(&dev_ctx->fops_lock)) + return -EBUSY; + + switch (cmd) { + case SENTNL_MU_IOCTL_ENABLE_CMD_RCV: + if (!sentnl_mu_priv->cmd_receiver_dev) { + sentnl_mu_priv->cmd_receiver_dev = dev_ctx; + err = 0; + }; + break; + case SENTNL_MU_IOCTL_GET_MU_INFO: + err = sentnl_mu_ioctl_get_mu_info(dev_ctx, arg); + break; + case SENTNL_MU_IOCTL_SHARED_BUF_CFG: + devctx_err(dev_ctx, "SENTNL_MU_IOCTL_SHARED_BUF_CFG not supported [0x%x].\n", err); + break; + case SENTNL_MU_IOCTL_SETUP_IOBUF: + err = sentnl_mu_ioctl_setup_iobuf_handler(dev_ctx, arg); + break; + case SENTNL_MU_IOCTL_SIGNED_MESSAGE: + devctx_err(dev_ctx, "SENTNL_MU_IOCTL_SIGNED_MESSAGE not supported [0x%x].\n", err); + break; + default: + err = -EINVAL; + devctx_dbg(dev_ctx, "IOCTL %.8x not supported\n", cmd); + } + + up(&dev_ctx->fops_lock); + return (long)err; +} + +/* Char driver setup */ +static const struct file_operations sentnl_mu_fops = { + .open = sentnl_mu_fops_open, + .owner = THIS_MODULE, + .release = sentnl_mu_fops_close, + .unlocked_ioctl = sentnl_mu_ioctl, + .read = sentnl_mu_fops_read, + .write = sentnl_mu_fops_write, +}; + +/* interface for managed res to free a mailbox channel */ +static void if_mbox_free_channel(void *mbox_chan) +{ + mbox_free_channel(mbox_chan); +} + +/* interface for managed res to unregister a char device */ +static void if_misc_deregister(void *miscdevice) +{ + misc_deregister(miscdevice); +} + +static int sentnl_mu_request_channel(struct device *dev, + struct mbox_chan **chan, + struct mbox_client *cl, + const char *name) +{ + struct mbox_chan *t_chan; + int ret = 0; + + t_chan = mbox_request_channel_byname(cl, name); + if (IS_ERR(t_chan)) { + ret = PTR_ERR(t_chan); + if (ret != -EPROBE_DEFER) + dev_err(dev, + "Failed to request chan %s ret %d\n", name, + ret); + goto exit; + } + + ret = devm_add_action(dev, if_mbox_free_channel, t_chan); + if (ret) { + dev_err(dev, "failed to add devm removal of mbox %s\n", name); + goto exit; + } + + *chan = t_chan; + +exit: + return ret; +} + +static int sentnl_mu_probe(struct platform_device *pdev) +{ + struct sentnl_mu_device_ctx *dev_ctx; + struct device *dev = &pdev->dev; + struct sentnl_mu_priv *priv; + struct device_node *np; + int max_nb_users = 0; + char *devname; + struct device *soc; + int ret; + int i; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + dev_err(dev, "Fail allocate mem for private data\n"); + goto exit; + } + priv->dev = dev; + dev_set_drvdata(dev, priv); + + /* + * Get the address of MU to be used for communication with the SCU + */ + np = pdev->dev.of_node; + if (!np) { + dev_err(dev, "Cannot find MU User entry in device tree\n"); + ret = -ENOTSUPP; + goto exit; + } + + /* Initialize the mutex. */ + mutex_init(&priv->mu_cmd_lock); + mutex_init(&priv->mu_lock); + + /* TBD */ + priv->cmd_receiver_dev = NULL; + priv->waiting_rsp_dev = NULL; + + ret = of_property_read_u32(np, "fsl,sentnl_mu_id", &priv->sentnl_mu_id); + if (ret) { + dev_warn(dev, "%s: Not able to read mu_id", __func__); + priv->sentnl_mu_id = S4_DEFAULT_MUAP_INDEX; + } + + ret = of_property_read_u32(np, "fsl,sentnl_mu_max_users", &max_nb_users); + if (ret) { + dev_warn(dev, "%s: Not able to read mu_max_user", __func__); + max_nb_users = S4_MUAP_DEFAULT_MAX_USERS; + } + + ret = of_property_read_u8(np, "fsl,cmd_tag", &priv->cmd_tag); + if (ret) { + dev_warn(dev, "%s: Not able to read cmd_tag", __func__); + priv->cmd_tag = DEFAULT_MESSAGING_TAG_COMMAND; + } + + ret = of_property_read_u8(np, "fsl,rsp_tag", &priv->rsp_tag); + if (ret) { + dev_warn(dev, "%s: Not able to read rsp_tag", __func__); + priv->rsp_tag = DEFAULT_MESSAGING_TAG_RESPONSE; + } + + /* Mailbox client configuration */ + priv->sentnl_mb_cl.dev = dev; + priv->sentnl_mb_cl.tx_block = false; + priv->sentnl_mb_cl.knows_txdone = true; + priv->sentnl_mb_cl.rx_callback = sentnl_mu_rx_callback; + + ret = sentnl_mu_request_channel(dev, &priv->tx_chan, &priv->sentnl_mb_cl, "tx"); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to request tx channel\n"); + + goto exit; + } + + ret = sentnl_mu_request_channel(dev, &priv->rx_chan, &priv->sentnl_mb_cl, "rx"); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to request rx channel\n"); + + goto exit; + } + + /* Create users */ + for (i = 0; i < max_nb_users; i++) { + dev_ctx = devm_kzalloc(dev, sizeof(*dev_ctx), GFP_KERNEL); + if (!dev_ctx) { + ret = -ENOMEM; + dev_err(dev, + "Fail to allocate memory for device context\n"); + goto exit; + } + + dev_ctx->dev = dev; + dev_ctx->status = MU_FREE; + dev_ctx->priv = priv; + /* Default value invalid for an header. */ + init_waitqueue_head(&dev_ctx->wq); + + INIT_LIST_HEAD(&dev_ctx->pending_out); + sema_init(&dev_ctx->fops_lock, 1); + + devname = devm_kasprintf(dev, GFP_KERNEL, "sentnl_mu%d_ch%d", + priv->sentnl_mu_id, i); + if (!devname) { + ret = -ENOMEM; + dev_err(dev, + "Fail to allocate memory for misc dev name\n"); + goto exit; + } + + dev_ctx->miscdev.name = devname; + dev_ctx->miscdev.minor = MISC_DYNAMIC_MINOR; + dev_ctx->miscdev.fops = &sentnl_mu_fops; + dev_ctx->miscdev.parent = dev; + ret = misc_register(&dev_ctx->miscdev); + if (ret) { + dev_err(dev, "failed to register misc device %d\n", + ret); + goto exit; + } + + ret = devm_add_action(dev, if_misc_deregister, + &dev_ctx->miscdev); + + } + + init_completion(&priv->done); + spin_lock_init(&priv->lock); + + sentnl_priv_export = priv; + + soc = imx_soc_device_register(); + if (IS_ERR(soc)) { + pr_err("failed to register SoC device: %ld\n", PTR_ERR(soc)); + return PTR_ERR(soc); + } + + dev_set_drvdata(dev, priv); + return devm_of_platform_populate(dev); + +exit: + return ret; +} + +static int sentnl_mu_remove(struct platform_device *pdev) +{ + struct sentnl_mu_priv *priv; + + priv = dev_get_drvdata(&pdev->dev); + mbox_free_channel(priv->tx_chan); + mbox_free_channel(priv->rx_chan); + + return 0; +} + +static const struct of_device_id sentnl_mu_match[] = { + { .compatible = "fsl,imx-sentnl", }, + {}, +}; + +static struct platform_driver sentnl_mu_driver = { + .driver = { + .name = "fsl-sentnl-mu", + .of_match_table = sentnl_mu_match, + }, + .probe = sentnl_mu_probe, + .remove = sentnl_mu_remove, +}; +module_platform_driver(sentnl_mu_driver); + +MODULE_AUTHOR("Pankaj Gupta <pankaj.gupta@nxp.com>"); +MODULE_DESCRIPTION("iMX Secure Enclave MU Driver."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/firmware/imx/sentnl_mu.h b/drivers/firmware/imx/sentnl_mu.h new file mode 100644 index 000000000000..3f5a4488effa --- /dev/null +++ b/drivers/firmware/imx/sentnl_mu.h @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021 NXP + */ + +#ifndef SENTNL_MU_H +#define SENTNL_MU_H + +#include <linux/miscdevice.h> +#include <linux/semaphore.h> + +/* macro to log operation of a misc device */ +#define miscdev_dbg(p_miscdev, fmt, va_args...) \ + ({ \ + struct miscdevice *_p_miscdev = p_miscdev; \ + dev_dbg((_p_miscdev)->parent, "%s: " fmt, (_p_miscdev)->name, \ + ##va_args); \ + }) + +#define miscdev_info(p_miscdev, fmt, va_args...) \ + ({ \ + struct miscdevice *_p_miscdev = p_miscdev; \ + dev_info((_p_miscdev)->parent, "%s: " fmt, (_p_miscdev)->name, \ + ##va_args); \ + }) + +#define miscdev_err(p_miscdev, fmt, va_args...) \ + ({ \ + struct miscdevice *_p_miscdev = p_miscdev; \ + dev_err((_p_miscdev)->parent, "%s: " fmt, (_p_miscdev)->name, \ + ##va_args); \ + }) +/* macro to log operation of a device context */ +#define devctx_dbg(p_devctx, fmt, va_args...) \ + miscdev_dbg(&((p_devctx)->miscdev), fmt, ##va_args) +#define devctx_info(p_devctx, fmt, va_args...) \ + miscdev_info(&((p_devctx)->miscdev), fmt, ##va_args) +#define devctx_err(p_devctx, fmt, va_args...) \ + miscdev_err((&(p_devctx)->miscdev), fmt, ##va_args) + +#define MSG_TAG(x) (((x) & 0xff000000) >> 24) +#define MSG_COMMAND(x) (((x) & 0x00ff0000) >> 16) +#define MSG_SIZE(x) (((x) & 0x0000ff00) >> 8) +#define MSG_VER(x) ((x) & 0x000000ff) +#define RES_STATUS(x) ((x) & 0x000000ff) +#define MAX_DATA_SIZE_PER_USER (65 * 1024) +#define S4_DEFAULT_MUAP_INDEX (2) +#define S4_MUAP_DEFAULT_MAX_USERS (4) + +#define DEFAULT_MESSAGING_TAG_COMMAND (0x17u) +#define DEFAULT_MESSAGING_TAG_RESPONSE (0xe1u) + +#define SECO_MU_IO_FLAGS_IS_INPUT (0x01u) +#define SECO_MU_IO_FLAGS_USE_SEC_MEM (0x02u) +#define SECO_MU_IO_FLAGS_USE_SHORT_ADDR (0x04u) + +struct sentnl_obuf_desc { + u8 *out_ptr; + u8 *out_usr_ptr; + u32 out_size; + struct list_head link; +}; + +/* Status of a char device */ +enum mu_device_status_t { + MU_FREE, + MU_OPENED +}; + +struct sentnl_shared_mem { + dma_addr_t dma_addr; + u32 size; + u32 pos; + u8 *ptr; +}; + +/* Private struct for each char device instance. */ +struct sentnl_mu_device_ctx { + struct device *dev; + struct sentnl_mu_priv *priv; + struct miscdevice miscdev; + + enum mu_device_status_t status; + wait_queue_head_t wq; + struct semaphore fops_lock; + + u32 pending_hdr; + struct list_head pending_out; + + struct sentnl_shared_mem secure_mem; + struct sentnl_shared_mem non_secure_mem; + + u32 temp_cmd[MAX_MESSAGE_SIZE]; + u32 temp_resp[MAX_RECV_SIZE]; + u32 temp_resp_size; + struct notifier_block sentnl_notify; +}; + +/* Header of the messages exchange with the SENTINEL */ +struct mu_hdr { + u8 ver; + u8 size; + u8 command; + u8 tag; +} __packed; + +struct sentnl_api_msg { + u32 header; /* u8 Tag; u8 Command; u8 Size; u8 Ver; */ + u32 data[SENTNL_MSG_DATA_NUM]; +}; + +struct sentnl_mu_priv { + struct sentnl_mu_device_ctx *cmd_receiver_dev; + struct sentnl_mu_device_ctx *waiting_rsp_dev; + /* + * prevent parallel access to the MU registers + * e.g. a user trying to send a command while the other one is + * sending a response. + */ + struct mutex mu_lock; + /* + * prevent a command to be sent on the MU while another one is still + * processing. (response to a command is allowed) + */ + struct mutex mu_cmd_lock; + struct device *dev; + u32 sentnl_mu_id; + u8 cmd_tag; + u8 rsp_tag; + + struct mbox_client sentnl_mb_cl; + struct mbox_chan *tx_chan, *rx_chan; + struct sentnl_api_msg tx_msg, rx_msg; + struct completion done; + spinlock_t lock; +}; + +int get_sentnl_mu_priv(struct sentnl_mu_priv **export); +#endif diff --git a/include/linux/firmware/imx/ipc.h b/include/linux/firmware/imx/ipc.h index 0b4643571625..6e60322bcef9 100644 --- a/include/linux/firmware/imx/ipc.h +++ b/include/linux/firmware/imx/ipc.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* - * Copyright 2018 NXP + * Copyright 2018,2020 NXP * * Header file for the IPC implementation. */ @@ -25,6 +25,8 @@ enum imx_sc_rpc_svc { IMX_SC_RPC_SVC_PAD = 6, IMX_SC_RPC_SVC_MISC = 7, IMX_SC_RPC_SVC_IRQ = 8, + IMX_SC_RPC_SVC_SECO = 9, + IMX_SC_RPC_SVC_ABORT = 10, }; struct imx_sc_rpc_msg { diff --git a/include/linux/firmware/imx/sci.h b/include/linux/firmware/imx/sci.h index 5cc63fe7e84d..d45dbbe8f67d 100644 --- a/include/linux/firmware/imx/sci.h +++ b/include/linux/firmware/imx/sci.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* * Copyright (C) 2016 Freescale Semiconductor, Inc. - * Copyright 2017~2018 NXP + * Copyright 2017~2018,2020 NXP * * Header file containing the public System Controller Interface (SCI) * definitions. @@ -15,12 +15,38 @@ #include <linux/firmware/imx/svc/misc.h> #include <linux/firmware/imx/svc/pm.h> #include <linux/firmware/imx/svc/rm.h> +#include <linux/firmware/imx/svc/seco.h> + +#define IMX_SC_IRQ_NUM_GROUP 9 + +#define IMX_SC_IRQ_GROUP_TEMP 0 /* Temp interrupts */ +#define IMX_SC_IRQ_GROUP_WDOG 1 /* Watchdog interrupts */ +#define IMX_SC_IRQ_GROUP_RTC 2 /* RTC interrupts */ +#define IMX_SC_IRQ_GROUP_WAKE 3 /* Wakeup interrupts */ +#define IMX_SC_IRQ_GROUP_SYSCTR 4 /* System counter interrupts */ +#define IMX_SC_IRQ_GROUP_REBOOTED 5 /* Partition reboot complete */ +#define IMX_SC_IRQ_GROUP_REBOOT 6 /* Partition reboot starting */ +#define IMX_SC_IRQ_GROUP_OFFED 7 /* Partition off complete */ +#define IMX_SC_IRQ_GROUP_OFF 8 /* Partition off starting */ + +#define IMX_SC_IRQ_RTC BIT(0) /* RTC interrupt */ +#define IMX_SC_IRQ_WDOG BIT(0) /* Watch Dog interrupt */ +#define IMX_SC_IRQ_SYSCTR BIT(0) /* System Counter interrupt */ +#define IMX_SC_IRQ_BUTTON BIT(0) /* Button interrupt */ +#define IMX_SC_IRQ_PAD BIT(1) /* Pad wakeup */ +#define IMX_SC_IRQ_USR1 BIT(2) /* User defined 1 */ +#define IMX_SC_IRQ_USR2 BIT(3) /* User defined 2 */ +#define IMX_SC_IRQ_BC_PAD BIT(4) /* Pad wakeup (broadcast to all partitions) */ +#define IMX_SC_IRQ_SW_WAKE BIT(5) /* Software requested wake */ +#define IMX_SC_IRQ_SECVIO BIT(6) /* Security violation */ +#define IMX_SC_IRQ_V2X_RESET BIT(7) /* V2X reset */ #if IS_ENABLED(CONFIG_IMX_SCU) int imx_scu_enable_general_irq_channel(struct device *dev); int imx_scu_irq_register_notifier(struct notifier_block *nb); int imx_scu_irq_unregister_notifier(struct notifier_block *nb); int imx_scu_irq_group_enable(u8 group, u32 mask, u8 enable); +int imx_scu_irq_get_status(u8 group, u32 *irq_status); int imx_scu_soc_init(struct device *dev); #else static inline int imx_scu_soc_init(struct device *dev) diff --git a/include/linux/firmware/imx/seco_mu_ioctl.h b/include/linux/firmware/imx/seco_mu_ioctl.h new file mode 100644 index 000000000000..bd8402b473a4 --- /dev/null +++ b/include/linux/firmware/imx/seco_mu_ioctl.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause*/ +/* + * Copyright 2019-2020 NXP + */ + +#ifndef SECO_MU_IOCTL_H +#define SECO_MU_IOCTL_H + +/* IOCTL definitions. */ +struct seco_mu_ioctl_setup_iobuf { + u8 *user_buf; + u32 length; + u32 flags; + u64 seco_addr; +}; + +struct seco_mu_ioctl_shared_mem_cfg { + u32 base_offset; + u32 size; +}; + +struct seco_mu_ioctl_get_mu_info { + u8 seco_mu_idx; + u8 interrupt_idx; + u8 tz; + u8 did; +}; + +struct seco_mu_ioctl_signed_message { + u8 *message; + u32 msg_size; + u32 error_code; +}; + +#define SECO_MU_IO_FLAGS_IS_INPUT (0x01u) +#define SECO_MU_IO_FLAGS_USE_SEC_MEM (0x02u) +#define SECO_MU_IO_FLAGS_USE_SHORT_ADDR (0x04u) + +#define SECO_MU_IOCTL 0x0A /* like MISC_MAJOR. */ +#define SECO_MU_IOCTL_ENABLE_CMD_RCV _IO(SECO_MU_IOCTL, 0x01) +#define SECO_MU_IOCTL_SHARED_BUF_CFG _IOW(SECO_MU_IOCTL, 0x02, \ + struct seco_mu_ioctl_shared_mem_cfg) +#define SECO_MU_IOCTL_SETUP_IOBUF _IOWR(SECO_MU_IOCTL, 0x03, \ + struct seco_mu_ioctl_setup_iobuf) +#define SECO_MU_IOCTL_GET_MU_INFO _IOR(SECO_MU_IOCTL, 0x04, \ + struct seco_mu_ioctl_get_mu_info) +#define SECO_MU_IOCTL_SIGNED_MESSAGE _IOWR(SECO_MU_IOCTL, 0x05, \ + struct seco_mu_ioctl_signed_message) + +#endif diff --git a/include/linux/firmware/imx/senclave_base_msg.h b/include/linux/firmware/imx/senclave_base_msg.h new file mode 100644 index 000000000000..0592fbcf8fd3 --- /dev/null +++ b/include/linux/firmware/imx/senclave_base_msg.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021 NXP + */ + +#ifndef SENTNL_BASE_MSG_H +#define SENTNL_BASE_MSG_H + +#define MAX_RECV_SIZE 31 +#define MAX_RECV_SIZE_BYTES (MAX_RECV_SIZE * sizeof(u32)) +#define MAX_MESSAGE_SIZE 31 +#define MAX_MESSAGE_SIZE_BYTES (MAX_MESSAGE_SIZE * sizeof(u32)) + +#define MESSAGING_VERSION_6 0x6 + +#define SENTNL_OEM_CNTN_AUTH_REQ 0x87 +#define SENTNL_VERIFY_IMAGE_REQ 0x88 +#define SENTNL_RELEASE_CONTAINER_REQ 0x89 +#define SENTNL_READ_FUSE_REQ 0x97 +#define OTP_UNIQ_ID 0x01 +#define OTFAD_CONFIG 0x2 + +#define SENTNL_VERSION 0x6 +#define SENTNL_SUCCESS_IND 0xD6 +#define SENTNL_FAILURE_IND 0x29 + +#define SENTNL_MSG_DATA_NUM 10 + +#define SENTNL_OEM_CNTN_AUTH_REQ_SIZE 3 +#define SENTNL_VERIFY_IMAGE_REQ_SIZE 2 +#define SENTNL_REL_CONTAINER_REQ_SIZE 1 + + +int read_common_fuse(uint16_t fuse_index, u32 *value); + +#endif diff --git a/include/linux/firmware/imx/sentnl_mu_ioctl.h b/include/linux/firmware/imx/sentnl_mu_ioctl.h new file mode 100644 index 000000000000..eda727aa81fc --- /dev/null +++ b/include/linux/firmware/imx/sentnl_mu_ioctl.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause*/ +/* + * Copyright 2019-2021 NXP + */ + +#ifndef SENTNL_MU_IOCTL_H +#define SENTNL_MU_IOCTL_H + +/* IOCTL definitions. */ + +struct sentnl_mu_ioctl_setup_iobuf { + u8 *user_buf; + u32 length; + u32 flags; + u64 sentnl_addr; +}; + +struct sentnl_mu_ioctl_shared_mem_cfg { + u32 base_offset; + u32 size; +}; + +struct sentnl_mu_ioctl_get_mu_info { + u8 sentnl_mu_id; + u8 interrupt_idx; + u8 tz; + u8 did; +}; + +struct sentnl_mu_ioctl_signed_message { + u8 *message; + u32 msg_size; + u32 error_code; +}; + +#define SENTNL_MU_IO_FLAGS_IS_INTPUT (0x01u) +#define SENTNL_MU_IO_FLAGS_USE_SEC_MEM (0x02u) +#define SENTNL_MU_IO_FLAGS_USE_SHORT_ADDR (0x04u) + +#define SENTNL_MU_IOCTL 0x0A /* like MISC_MAJOR. */ +#define SENTNL_MU_IOCTL_ENABLE_CMD_RCV _IO(SENTNL_MU_IOCTL, 0x01) +#define SENTNL_MU_IOCTL_SHARED_BUF_CFG _IOW(SENTNL_MU_IOCTL, 0x02, \ + struct sentnl_mu_ioctl_shared_mem_cfg) +#define SENTNL_MU_IOCTL_SETUP_IOBUF _IOWR(SENTNL_MU_IOCTL, 0x03, \ + struct sentnl_mu_ioctl_setup_iobuf) +#define SENTNL_MU_IOCTL_GET_MU_INFO _IOR(SENTNL_MU_IOCTL, 0x04, \ + struct sentnl_mu_ioctl_get_mu_info) +#define SENTNL_MU_IOCTL_SIGNED_MESSAGE _IOWR(SENTNL_MU_IOCTL, 0x05, \ + struct sentnl_mu_ioctl_signed_message) + +#endif diff --git a/include/linux/firmware/imx/svc/misc.h b/include/linux/firmware/imx/svc/misc.h index 760db08a67fc..8fb0fe2dfc92 100644 --- a/include/linux/firmware/imx/svc/misc.h +++ b/include/linux/firmware/imx/svc/misc.h @@ -50,6 +50,9 @@ enum imx_misc_func { int imx_sc_misc_set_control(struct imx_sc_ipc *ipc, u32 resource, u8 ctrl, u32 val); +int imx_sc_misc_set_dma_group(struct imx_sc_ipc *ipc, u32 resource, + u32 val); + int imx_sc_misc_get_control(struct imx_sc_ipc *ipc, u32 resource, u8 ctrl, u32 *val); @@ -67,6 +70,12 @@ static inline int imx_sc_misc_get_control(struct imx_sc_ipc *ipc, { return -ENOTSUPP; } +static inline int +imx_sc_misc_set_dma_group(struct imx_sc_ipc *ipc, u32 resource, + u32 val) +{ + return -EIO; +} static inline int imx_sc_pm_cpu_start(struct imx_sc_ipc *ipc, u32 resource, bool enable, u64 phys_addr) diff --git a/include/linux/firmware/imx/svc/rm.h b/include/linux/firmware/imx/svc/rm.h index 456b6a59d29b..64aabcba8768 100644 --- a/include/linux/firmware/imx/svc/rm.h +++ b/include/linux/firmware/imx/svc/rm.h @@ -59,11 +59,50 @@ enum imx_sc_rm_func { #if IS_ENABLED(CONFIG_IMX_SCU) bool imx_sc_rm_is_resource_owned(struct imx_sc_ipc *ipc, u16 resource); +int imx_sc_rm_get_partition(struct imx_sc_ipc *ipc, u8 *pt); +int imx_sc_rm_find_memreg(struct imx_sc_ipc *ipc, u8 *mr, u64 addr_start, + u64 addr_end); +int imx_sc_rm_get_resource_owner(struct imx_sc_ipc *ipc, u16 resource, u8 *pt); +int imx_sc_rm_set_memreg_permissions(struct imx_sc_ipc *ipc, u8 mr, + u8 pt, u8 perm); +int imx_sc_rm_get_did(struct imx_sc_ipc *ipc, u8 *did); #else static inline bool imx_sc_rm_is_resource_owned(struct imx_sc_ipc *ipc, u16 resource) { return true; } + +static inline int imx_sc_rm_get_partition(struct imx_sc_ipc *ipc, u8 *pt) +{ + return -ENOENT; +} + +static inline +int imx_sc_rm_find_memreg(struct imx_sc_ipc *ipc, u8 *mr, u64 addr_start, + u64 addr_end) +{ + return -EOPNOTSUPP; +} + +static inline +int imx_sc_rm_get_resource_owner(struct imx_sc_ipc *ipc, u16 resource, u8 *pt) +{ + return -EOPNOTSUPP; +} + +static inline +int imx_sc_rm_set_memreg_permissions(struct imx_sc_ipc *ipc, u8 mr, + u8 pt, u8 perm) +{ + return -EOPNOTSUPP; +} + +static inline +int imx_sc_rm_get_did(struct imx_sc_ipc *ipc, u8 *did) +{ + return -EOPNOTSUPP; +} #endif + #endif diff --git a/include/linux/firmware/imx/svc/seco.h b/include/linux/firmware/imx/svc/seco.h new file mode 100644 index 000000000000..d0dd803a1a52 --- /dev/null +++ b/include/linux/firmware/imx/svc/seco.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2020 NXP + * + * Header file containing the public API for the System Controller (SC) + * Security Controller (SECO) function. + * + * SECO_SVC (SVC) Security Controller Service + * + * Module for the Security Controller (SECO) service. + */ + +#ifndef _SC_SECO_API_H +#define _SC_SECO_API_H + +#include <linux/errno.h> +#include <linux/firmware/imx/sci.h> + +/* + * This type is used to indicate RPC RM function calls. + */ +enum imx_sc_seco_func { + IMX_SC_SECO_FUNC_UNKNOWN = 0, + IMX_SC_SECO_FUNC_BUILD_INFO = 16, + IMX_SC_SECO_FUNC_SAB_MSG = 23, + IMX_SC_SECO_FUNC_SECVIO_ENABLE = 25, + IMX_SC_SECO_FUNC_SECVIO_CONFIG = 26, + IMX_SC_SECO_FUNC_SECVIO_DGO_CONFIG = 27, +}; + +#if IS_ENABLED(CONFIG_IMX_SCU) +int imx_sc_seco_build_info(struct imx_sc_ipc *ipc, uint32_t *version, + uint32_t *commit); +int imx_sc_seco_sab_msg(struct imx_sc_ipc *ipc, u64 smsg_addr); +int imx_sc_seco_secvio_enable(struct imx_sc_ipc *ipc); +int imx_sc_seco_secvio_config(struct imx_sc_ipc *ipc, u8 id, u8 access, + u32 *data0, u32 *data1, u32 *data2, u32 *data3, + u32 *data4, u8 size); +int imx_sc_seco_secvio_dgo_config(struct imx_sc_ipc *ipc, u8 id, u8 access, + u32 *data); +#else /* IS_ENABLED(CONFIG_IMX_SCU) */ +static inline +int imx_sc_seco_build_info(struct imx_sc_ipc *ipc, uint32_t *version, + uint32_t *commit) +{ + return -EOPNOTSUPP; +} + +static inline +int imx_sc_seco_sab_msg(struct imx_sc_ipc *ipc, u64 smsg_addr) +{ + return -EOPNOTSUPP; +} + +static inline +int imx_sc_seco_secvio_enable(struct imx_sc_ipc *ipc) +{ + return -EOPNOTSUPP; +} + +static inline +int imx_sc_seco_secvio_config(struct imx_sc_ipc *ipc, u8 id, u8 access, + u32 *data0, u32 *data1, u32 *data2, u32 *data3, + u32 *data4, u8 size) +{ + return -EOPNOTSUPP; +} + +static inline +int imx_sc_seco_secvio_dgo_config(struct imx_sc_ipc *ipc, u8 id, u8 access, + u32 *data) +{ + return -EOPNOTSUPP; +} +#endif /* IS_ENABLED(CONFIG_IMX_SCU) */ + +#endif /* _SC_SECO_API_H */ |