2024-05-01 05:33:24

by Vabhav Sharma

[permalink] [raw]
Subject: [PATCH 0/4] soc: imx: secvio: Add secvio support

Add secvio driver to handle security violation interrupt.

Vabhav Sharma (4):
dt-bindings: firmware: secvio: Add documentation
firmware: imx: Add SC APIs required for secvio module
soc: imx: secvio: Add support for SNVS secvio and tamper via SCFW
arm64: dts: imx8q: Add node for Security Violation

.../arm/freescale/fsl,scu-secvio.yaml | 35 ++
.../devicetree/bindings/firmware/fsl,scu.yaml | 10 +
arch/arm64/boot/dts/freescale/imx8qxp.dtsi | 5 +
drivers/firmware/imx/Makefile | 2 +-
drivers/firmware/imx/imx-scu.c | 4 +-
drivers/firmware/imx/seco.c | 216 +++++++
drivers/soc/imx/Kconfig | 11 +
drivers/soc/imx/Makefile | 1 +
drivers/soc/imx/secvio/Makefile | 2 +
drivers/soc/imx/secvio/imx-secvio-debugfs.c | 283 +++++++++
drivers/soc/imx/secvio/imx-secvio-sc.c | 595 ++++++++++++++++++
include/linux/firmware/imx/ipc.h | 1 +
include/linux/firmware/imx/sci.h | 4 +
include/linux/firmware/imx/svc/seco.h | 69 ++
include/soc/imx/imx-secvio-sc.h | 216 +++++++
15 files changed, 1452 insertions(+), 2 deletions(-)
create mode 100644 Documentation/devicetree/bindings/arm/freescale/fsl,scu-secvio.yaml
create mode 100644 drivers/firmware/imx/seco.c
create mode 100644 drivers/soc/imx/secvio/Makefile
create mode 100644 drivers/soc/imx/secvio/imx-secvio-debugfs.c
create mode 100644 drivers/soc/imx/secvio/imx-secvio-sc.c
create mode 100644 include/linux/firmware/imx/svc/seco.h
create mode 100644 include/soc/imx/imx-secvio-sc.h

--
2.25.1



2024-05-01 05:33:34

by Vabhav Sharma

[permalink] [raw]
Subject: [PATCH 1/4] dt-bindings: firmware: secvio: Add documentation

This patch adds the documentation for the SECVIO driver.

Signed-off-by: Franck LENORMAND <[email protected]>
Signed-off-by: Vabhav Sharma <[email protected]>
---
.../arm/freescale/fsl,scu-secvio.yaml | 35 +++++++++++++++++++
.../devicetree/bindings/firmware/fsl,scu.yaml | 10 ++++++
2 files changed, 45 insertions(+)
create mode 100644 Documentation/devicetree/bindings/arm/freescale/fsl,scu-secvio.yaml

diff --git a/Documentation/devicetree/bindings/arm/freescale/fsl,scu-secvio.yaml b/Documentation/devicetree/bindings/arm/freescale/fsl,scu-secvio.yaml
new file mode 100644
index 000000000000..30dc1e21f903
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/freescale/fsl,scu-secvio.yaml
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/arm/freescale/fsl,scu-secvio.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP i.MX Security Violation driver
+
+maintainers:
+ - Franck LENORMAND <[email protected]>
+
+description: |
+ Receive security violation from the SNVS via the SCU firmware. Allow to
+ register notifier for additional processing
+
+properties:
+ compatible:
+ enum:
+ - fsl,imx-sc-secvio
+
+ nvmem:
+ maxItems: 1
+
+required:
+ - compatible
+ - nvmem
+
+additionalProperties: false
+
+examples:
+ - |
+ secvio {
+ compatible = "fsl,imx-sc-secvio";
+ nvmem = <&ocotp>;
+ };
diff --git a/Documentation/devicetree/bindings/firmware/fsl,scu.yaml b/Documentation/devicetree/bindings/firmware/fsl,scu.yaml
index 557e524786c2..b40e127fdc88 100644
--- a/Documentation/devicetree/bindings/firmware/fsl,scu.yaml
+++ b/Documentation/devicetree/bindings/firmware/fsl,scu.yaml
@@ -129,6 +129,11 @@ properties:
RTC controller provided by the SCU
$ref: /schemas/rtc/fsl,scu-rtc.yaml

+ secvio:
+ description:
+ Receive security violation from the SNVS via the SCU firmware
+ $ref: /schemas/arm/freescale/fsl,scu-secvio.yaml
+
thermal-sensor:
description:
Thermal sensor provided by the SCU
@@ -197,6 +202,11 @@ examples:
compatible = "fsl,imx8qxp-sc-rtc";
};

+ secvio {
+ compatible = "fsl,imx-sc-secvio";
+ nvmem = <&ocotp>;
+ };
+
keys {
compatible = "fsl,imx8qxp-sc-key", "fsl,imx-sc-key";
linux,keycodes = <KEY_POWER>;
--
2.25.1


2024-05-01 05:34:24

by Vabhav Sharma

[permalink] [raw]
Subject: [PATCH 3/4] soc: imx: secvio: Add support for SNVS secvio and tamper via SCFW

The driver register an IRQ handle to SCU for security
violation interrupt.

When an interruption is fired, the driver inform the user.

Signed-off-by: Franck LENORMAND <[email protected]>
Signed-off-by: Vabhav Sharma <[email protected]>
---
drivers/soc/imx/Kconfig | 11 +
drivers/soc/imx/Makefile | 1 +
drivers/soc/imx/secvio/Makefile | 2 +
drivers/soc/imx/secvio/imx-secvio-debugfs.c | 283 ++++++++++
drivers/soc/imx/secvio/imx-secvio-sc.c | 595 ++++++++++++++++++++
include/soc/imx/imx-secvio-sc.h | 216 +++++++
6 files changed, 1108 insertions(+)
create mode 100644 drivers/soc/imx/secvio/Makefile
create mode 100644 drivers/soc/imx/secvio/imx-secvio-debugfs.c
create mode 100644 drivers/soc/imx/secvio/imx-secvio-sc.c
create mode 100644 include/soc/imx/imx-secvio-sc.h

diff --git a/drivers/soc/imx/Kconfig b/drivers/soc/imx/Kconfig
index 2a90ddd20104..3d2715a8b798 100644
--- a/drivers/soc/imx/Kconfig
+++ b/drivers/soc/imx/Kconfig
@@ -20,4 +20,15 @@ config SOC_IMX9
help
If you say yes here, you get support for the NXP i.MX9 family

+config SECVIO_SC
+ tristate "NXP SC secvio support"
+ depends on IMX_SCU
+ default y
+ help
+ If you say yes here you get support for the NXP SNVS security
+ violation module. It includes the possibility to read information
+ related to security violations and tampers. It also gives the
+ possibility to register user callbacks when a security violation
+ occurs.
+
endmenu
diff --git a/drivers/soc/imx/Makefile b/drivers/soc/imx/Makefile
index 3ad321ca608a..bda0259077be 100644
--- a/drivers/soc/imx/Makefile
+++ b/drivers/soc/imx/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_ARCH_MXC) += soc-imx.o
endif
obj-$(CONFIG_SOC_IMX8M) += soc-imx8m.o
obj-$(CONFIG_SOC_IMX9) += imx93-src.o
+obj-${CONFIG_SECVIO_SC} += secvio/
diff --git a/drivers/soc/imx/secvio/Makefile b/drivers/soc/imx/secvio/Makefile
new file mode 100644
index 000000000000..55ef1c044009
--- /dev/null
+++ b/drivers/soc/imx/secvio/Makefile
@@ -0,0 +1,2 @@
+obj-y += imx-secvio-sc.o
+obj-$(CONFIG_DEBUG_FS) += imx-secvio-debugfs.o
diff --git a/drivers/soc/imx/secvio/imx-secvio-debugfs.c b/drivers/soc/imx/secvio/imx-secvio-debugfs.c
new file mode 100644
index 000000000000..6f0af138ee29
--- /dev/null
+++ b/drivers/soc/imx/secvio/imx-secvio-debugfs.c
@@ -0,0 +1,283 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019, 2024 NXP
+ */
+
+/*
+ * The module exposes 3 files in debugfs:
+ * - secvio/info:
+ * * Read: It returns the value of the fuses and SNVS registers which are
+ * readable and related to secvio and tampers
+ * * Write: A write of the format "<hex id> [<hex value 0> <hex value 1>
+ * <hex value 2> <hex value 3> <hex value 4>](<nb values>)"
+ * will write the SNVS register having the provided id with the
+ * values provided (cf SECO ducumentation)
+ * - secvio/enable: State of the IRQ
+ * - secvio/check: Check the state of the security violation and tampers
+ * and calls notifier
+ * - secvio/clear: Clear the state of all secvio and tampers
+ */
+
+/* Includes */
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/nvmem-consumer.h>
+
+#include <linux/firmware/imx/svc/misc.h>
+#include <linux/firmware/imx/svc/seco.h>
+
+#include <soc/imx/imx-secvio-sc.h>
+
+static int fuse_reader(struct device *dev, u32 id, u32 *value, u8 mul)
+{
+ struct imx_secvio_sc_data *data = dev_get_drvdata(dev);
+ u32 size_to_read = mul * sizeof(u32);
+ int ret;
+
+ ret = nvmem_device_read(data->nvmem, id, size_to_read, value);
+ if (ret < 0) {
+ dev_err(data->dev, "Failed to read fuse %d: %d\n", id, ret);
+ return ret;
+ }
+
+ if (ret != size_to_read) {
+ dev_err(data->dev, "Read only %d instead of %d\n", ret,
+ size_to_read);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int snvs_reader(struct device *dev, u32 id, u32 *value, u8 mul)
+{
+ int ret;
+ u32 *v1, *v2, *v3, *v4, *v5;
+ struct imx_secvio_sc_data *data;
+
+ data = dev_get_drvdata(dev);
+ v1 = NULL;
+ v2 = NULL;
+ v3 = NULL;
+ v4 = NULL;
+ v5 = NULL;
+
+ switch (mul) {
+ case 5:
+ v5 = &value[4];
+ fallthrough;
+ case 4:
+ v4 = &value[3];
+ fallthrough;
+ case 3:
+ v3 = &value[2];
+ fallthrough;
+ case 2:
+ v2 = &value[1];
+ fallthrough;
+ case 1:
+ v1 = &value[0];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = imx_sc_seco_secvio_config(data->ipc_handle, id, SECVIO_CONFIG_READ,
+ v1, v2, v3, v4, v5, mul);
+ if (ret < 0)
+ dev_err(dev, "Failed to read snvs reg %d: %d\n", id, ret);
+
+ return ret;
+}
+
+static int snvs_dgo_reader(struct device *dev, u32 id, u32 *value, u8 mul)
+{
+ struct imx_secvio_sc_data *data = dev_get_drvdata(dev);
+ int ret;
+
+ if (mul != 1)
+ return -EINVAL;
+
+ ret = imx_sc_seco_secvio_dgo_config(data->ipc_handle, id,
+ SECVIO_CONFIG_READ, value);
+ if (ret)
+ dev_err(dev, "Failed to read snvs dgo reg %d: %d\n", id, ret);
+
+ return ret;
+}
+
+static const struct imx_secvio_info_entry {
+ int (*reader)(struct device *dev, u32 id, u32 *value, u8 mul);
+ const char *type;
+ const char *name;
+ u32 id;
+ u8 mul;
+} gs_imx_secvio_info_list[] = {
+ {fuse_reader, "fuse", "trim", 30, 1},
+ {fuse_reader, "fuse", "trim2", 31, 1},
+ {fuse_reader, "fuse", "ctrim1", 260, 1},
+ {fuse_reader, "fuse", "ctrim2", 261, 1},
+ {fuse_reader, "fuse", "ctrim3", 262, 1},
+ {fuse_reader, "fuse", "ctrim4", 263, 1},
+ {fuse_reader, "fuse", "OSC_CAP", 768, 1},
+
+ {snvs_reader, "snvs", "HPLR", 0x0, 1},
+ {snvs_reader, "snvs", "LPLR", 0x34, 1},
+ {snvs_reader, "snvs", "HPSICR", 0xc, 1},
+ {snvs_reader, "snvs", "HPSVCR", 0x10, 1},
+ {snvs_reader, "snvs", "HPSVS", 0x18, 1},
+ {snvs_reader, "snvs", "LPSVC", 0x40, 1},
+ {snvs_reader, "snvs", "LPTDC", 0x48, 2},
+ {snvs_reader, "snvs", "LPSR", 0x4c, 1},
+ {snvs_reader, "snvs", "LPTDS", 0xa4, 1},
+ {snvs_reader, "snvs", "LPTGFC", 0x44, 3},
+ {snvs_reader, "snvs", "LPATCTL", 0xe0, 1},
+ {snvs_reader, "snvs", "LPATCLK", 0xe4, 1},
+ {snvs_reader, "snvs", "LPATRC1", 0xe8, 2},
+ {snvs_reader, "snvs", "LPMKC", 0x3c, 1},
+ {snvs_reader, "snvs", "LPSMC", 0x5c, 2},
+ {snvs_reader, "snvs", "LPPGD", 0x64, 1},
+ {snvs_reader, "snvs", "HPVID", 0xf8, 2},
+
+ {snvs_dgo_reader, "dgo", "Offset", 0x0, 1},
+ {snvs_dgo_reader, "dgo", "PUP/PD", 0x10, 1},
+ {snvs_dgo_reader, "dgo", "Anatest", 0x20, 1},
+ {snvs_dgo_reader, "dgo", "T trim", 0x30, 1},
+ {snvs_dgo_reader, "dgo", "Misc", 0x40, 1},
+ {snvs_dgo_reader, "dgo", "Vmon", 0x50, 1},
+};
+
+struct imx_secvio_sc_info_seq_data {
+ struct device *dev;
+ const struct imx_secvio_info_entry *list;
+ int size;
+};
+
+static void *imx_secvio_sc_info_seq_start(struct seq_file *m, loff_t *pos)
+{
+ struct imx_secvio_sc_info_seq_data *data = m->private;
+
+ /* Check we are not out of bound */
+ if (*pos >= data->size)
+ return NULL;
+
+ return (void *)pos;
+}
+
+static void *imx_secvio_sc_info_seq_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ /* Increment the counter */
+ ++*pos;
+
+ /* call the start function which will check the index */
+ return imx_secvio_sc_info_seq_start(m, pos);
+}
+
+static void imx_secvio_sc_info_seq_stop(struct seq_file *m, void *v)
+{
+}
+
+static int imx_secvio_sc_info_seq_show(struct seq_file *m, void *v)
+{
+ struct imx_secvio_sc_info_seq_data *data = m->private;
+ const struct imx_secvio_info_entry *e;
+ int ret;
+ u32 vals[5];
+ int idx;
+
+ idx = *(loff_t *)v;
+ e = &data->list[idx];
+
+ /* Read the values */
+ ret = e->reader(data->dev, e->id, (u32 *)&vals, e->mul);
+ if (ret) {
+ dev_err(data->dev, "Fail to read %s %s (idx %d)\n", e->type,
+ e->name, e->id);
+ return 0;
+ }
+
+ seq_printf(m, "%5s/%-10s(%.3d):", e->type, e->name, e->id);
+
+ /* Loop over the values */
+ for (idx = 0; idx < e->mul; idx++)
+ seq_printf(m, " %.8x", vals[idx]);
+
+ seq_puts(m, "\n");
+
+ return 0;
+}
+
+static const struct seq_operations imx_secvio_sc_info_seq_ops = {
+ .start = imx_secvio_sc_info_seq_start,
+ .next = imx_secvio_sc_info_seq_next,
+ .stop = imx_secvio_sc_info_seq_stop,
+ .show = imx_secvio_sc_info_seq_show,
+};
+
+static int imx_secvio_sc_info_open(struct inode *inode, struct file *file)
+{
+ struct imx_secvio_sc_info_seq_data *data;
+
+ data = __seq_open_private(file, &imx_secvio_sc_info_seq_ops, sizeof(*data));
+ if (!data)
+ return -ENOMEM;
+
+ data->dev = inode->i_private;
+ data->list = gs_imx_secvio_info_list;
+ data->size = ARRAY_SIZE(gs_imx_secvio_info_list);
+
+ return 0;
+}
+
+static const struct file_operations imx_secvio_sc_info_ops = {
+ .owner = THIS_MODULE,
+ .open = imx_secvio_sc_info_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release_private,
+};
+
+static void if_debugfs_remove_recursive(void *dentry)
+{
+ debugfs_remove_recursive(dentry);
+}
+
+int imx_secvio_sc_debugfs(struct device *dev)
+{
+ struct imx_secvio_sc_data *data = dev_get_drvdata(dev);
+ struct dentry *dir;
+ int ret = 0;
+
+ /* Create a folder */
+ dir = debugfs_create_dir(dev_name(dev), NULL);
+ if (IS_ERR(dir)) {
+ dev_err(dev, "Failed to create dfs dir\n");
+ ret = PTR_ERR(dir);
+ goto exit;
+ }
+ data->dfs = dir;
+
+ ret = devm_add_action(dev, if_debugfs_remove_recursive, data->dfs);
+ if (ret) {
+ dev_err(dev, "Failed to add managed action to disable IRQ\n");
+ goto remove_fs;
+ }
+
+ /* Create the file to read info and write to reg */
+ dir = debugfs_create_file("info", 0x666, data->dfs, dev,
+ &imx_secvio_sc_info_ops);
+ if (IS_ERR(dir)) {
+ dev_err(dev, "Failed to add info to debugfs\n");
+ ret = PTR_ERR(dir);
+ goto exit;
+ }
+
+exit:
+ return ret;
+
+remove_fs:
+ debugfs_remove_recursive(data->dfs);
+ goto exit;
+}
diff --git a/drivers/soc/imx/secvio/imx-secvio-sc.c b/drivers/soc/imx/secvio/imx-secvio-sc.c
new file mode 100644
index 000000000000..a4e96c730a23
--- /dev/null
+++ b/drivers/soc/imx/secvio/imx-secvio-sc.c
@@ -0,0 +1,595 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019, 2024 NXP
+ *
+ */
+
+/*
+ * The i.MX8QXP SoC contains the Secure Non-Volatile Storage (SNVS) block. This
+ * block can detect specific hardware attacks.This block can only be accessible
+ * using the SCFW API.
+ *
+ * This module interact with the SCU which relay request to/from the SNVS block
+ * to detect if security violation occurred.
+ *
+ * The module exports an API to add processing when a SV is detected:
+ * - register_imx_secvio_sc_notifier
+ * - unregister_imx_secvio_sc_notifier
+ * - imx_secvio_sc_check_state
+ * - imx_secvio_sc_clear_state
+ * - imx_secvio_sc_enable_irq
+ * - imx_secvio_sc_disable_irq
+ */
+
+#include <dt-bindings/firmware/imx/rsrc.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+
+#include <linux/firmware/imx/ipc.h>
+#include <linux/firmware/imx/sci.h>
+#include <linux/firmware/imx/svc/seco.h>
+#include <linux/firmware/imx/svc/rm.h>
+#include <soc/imx/imx-secvio-sc.h>
+
+/* Reference on the driver_device */
+static struct device *imx_secvio_sc_dev;
+
+/* Register IDs for sc_seco_secvio_config API */
+#define HPSVS_ID 0x18
+#define LPS_ID 0x4c
+#define LPTDS_ID 0xa4
+#define HPVIDR_ID 0xf8
+
+#define SECO_MINOR_VERSION_SUPPORT_SECVIO_TAMPER 0x53
+#define SECO_VERSION_MINOR_MASK GENMASK(15, 0)
+
+/* Notifier list for new CB */
+static BLOCKING_NOTIFIER_HEAD(imx_secvio_sc_notifier_chain);
+
+int register_imx_secvio_sc_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&imx_secvio_sc_notifier_chain,
+ nb);
+}
+EXPORT_SYMBOL(register_imx_secvio_sc_notifier);
+
+int unregister_imx_secvio_sc_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&imx_secvio_sc_notifier_chain,
+ nb);
+}
+EXPORT_SYMBOL(unregister_imx_secvio_sc_notifier);
+
+static void if_imx_scu_irq_register_notifier(void *nb)
+{
+ imx_scu_irq_register_notifier(nb);
+}
+
+static void if_unregister_imx_secvio_sc_notifier(void *nb)
+{
+ unregister_imx_secvio_sc_notifier(nb);
+}
+
+static
+int imx_secvio_sc_notifier_call_chain(struct secvio_sc_notifier_info *info)
+{
+ return blocking_notifier_call_chain(&imx_secvio_sc_notifier_chain, 0,
+ (void *)info);
+}
+
+int imx_secvio_sc_get_state(struct device *dev,
+ struct secvio_sc_notifier_info *info)
+{
+ int ret, err = 0;
+ struct imx_secvio_sc_data *data;
+
+ dev = imx_secvio_sc_dev;
+ if (!dev)
+ return -EINVAL;
+
+ data = dev_get_drvdata(dev);
+
+ /* Read secvio status */
+ ret = imx_sc_seco_secvio_config(data->ipc_handle, HPSVS_ID, SECVIO_CONFIG_READ,
+ &info->hpsvs, NULL, NULL, NULL, NULL, 1);
+ if (ret) {
+ err = ret;
+ dev_err(dev, "Fail read secvio config status %d\n", ret);
+ }
+ info->hpsvs &= HPSVS_ALL_SV_MASK;
+
+ /* Read tampers status */
+ ret = imx_sc_seco_secvio_config(data->ipc_handle, LPS_ID, SECVIO_CONFIG_READ,
+ &info->lps, NULL, NULL, NULL, NULL, 1);
+ if (ret) {
+ err = ret;
+ dev_err(dev, "Fail read tamper 1 status: %d\n", ret);
+ }
+ info->lps &= LPS_ALL_TP_MASK;
+
+ ret = imx_sc_seco_secvio_config(data->ipc_handle, LPTDS_ID, SECVIO_CONFIG_READ,
+ &info->lptds, NULL, NULL, NULL, NULL, 1);
+ if (ret) {
+ err = ret;
+ dev_err(dev, "Fail read tamper 2 status: %d\n", ret);
+ }
+ info->lptds &= LPTDS_ALL_TP_MASK;
+
+ dev_dbg(dev, "Status: %.8x, %.8x, %.8x\n", info->hpsvs,
+ info->lps, info->lptds);
+
+ return err;
+}
+EXPORT_SYMBOL(imx_secvio_sc_get_state);
+
+int imx_secvio_sc_check_state(struct device *dev)
+{
+ struct secvio_sc_notifier_info info;
+ int ret;
+
+ dev = imx_secvio_sc_dev;
+
+ ret = imx_secvio_sc_get_state(dev, &info);
+ if (ret) {
+ dev_err(dev, "Failed to get secvio state\n");
+ return ret;
+ }
+
+ /* Call chain of CB registered to this module if status detected */
+ if (info.hpsvs || info.lps || info.lptds)
+ if (imx_secvio_sc_notifier_call_chain(&info))
+ dev_warn(dev,
+ "Issues when calling the notifier chain\n");
+
+ return ret;
+}
+EXPORT_SYMBOL(imx_secvio_sc_check_state);
+
+static int imx_secvio_sc_disable_irq(struct device *dev)
+{
+ int ret;
+
+ if (!dev)
+ return -EINVAL;
+
+ /* Disable the IRQ */
+ ret = imx_scu_irq_group_enable(IMX_SC_IRQ_GROUP_WAKE, IMX_SC_IRQ_SECVIO,
+ false);
+ if (ret) {
+ dev_err(dev, "Cannot disable SCU IRQ: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int imx_secvio_sc_enable_irq(struct device *dev)
+{
+ int ret = 0, err;
+ u32 irq_status;
+ struct imx_secvio_sc_data *data;
+
+ if (!dev)
+ return -EINVAL;
+
+ data = dev_get_drvdata(dev);
+
+ /* Enable the IRQ */
+ ret = imx_scu_irq_group_enable(IMX_SC_IRQ_GROUP_WAKE, IMX_SC_IRQ_SECVIO,
+ true);
+ if (ret) {
+ dev_err(dev, "Cannot enable SCU IRQ: %d\n", ret);
+ goto exit;
+ }
+
+ /* Enable interrupt */
+ ret = imx_sc_seco_secvio_enable(data->ipc_handle);
+ if (ret) {
+ dev_err(dev, "Cannot enable SNVS irq: %d\n", ret);
+ goto exit;
+ }
+
+ /* Unmask interrupt */
+ ret = imx_scu_irq_get_status(IMX_SC_IRQ_GROUP_WAKE, &irq_status);
+ if (ret) {
+ dev_err(dev, "Cannot unmask irq: %d\n", ret);
+ goto exit;
+ }
+
+exit:
+ if (ret) {
+ err = imx_secvio_sc_disable_irq(dev);
+ if (err)
+ dev_warn(dev, "Failed to disable the IRQ\n");
+ }
+
+ return ret;
+}
+
+static int imx_secvio_sc_notify(struct notifier_block *nb,
+ unsigned long event, void *group)
+{
+ struct imx_secvio_sc_data *data =
+ container_of(nb, struct imx_secvio_sc_data,
+ irq_nb);
+ struct device *dev = data->dev;
+ int ret;
+
+ /* Filter event for us */
+ if (!((event & IMX_SC_IRQ_SECVIO) &&
+ (*(u8 *)group == IMX_SC_IRQ_GROUP_WAKE)))
+ return 0;
+
+ dev_warn(dev, "secvio security violation detected\n");
+
+ ret = imx_secvio_sc_check_state(dev);
+
+ /* Re-enable interrupt */
+ ret = imx_secvio_sc_enable_irq(dev);
+ if (ret)
+ dev_err(dev, "Failed to enable IRQ\n");
+
+ return ret;
+}
+
+int imx_secvio_sc_clear_state(struct device *dev, u32 hpsvs, u32 lps, u32 lptds)
+{
+ int ret;
+ struct imx_secvio_sc_data *data;
+
+ dev = imx_secvio_sc_dev;
+ if (!dev)
+ return -EINVAL;
+
+ data = dev_get_drvdata(dev);
+
+ ret = imx_sc_seco_secvio_config(data->ipc_handle, HPSVS_ID, SECVIO_CONFIG_WRITE,
+ &hpsvs, NULL, NULL, NULL, NULL, 1);
+ if (ret) {
+ dev_err(dev, "Fail to clear secvio status: %d\n", ret);
+ return ret;
+ }
+
+ ret = imx_sc_seco_secvio_config(data->ipc_handle, LPS_ID, SECVIO_CONFIG_WRITE,
+ &lps, NULL, NULL, NULL, NULL, 1);
+ if (ret) {
+ dev_err(dev, "Fail to clear tamper 1 status: %d\n", ret);
+ return ret;
+ }
+
+ ret = imx_sc_seco_secvio_config(data->ipc_handle, LPTDS_ID, SECVIO_CONFIG_WRITE,
+ &lptds, NULL, NULL, NULL, NULL, 1);
+ if (ret) {
+ dev_err(dev, "Fail to clear tamper 2 status: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(imx_secvio_sc_clear_state);
+
+static int report_to_user_notify(struct notifier_block *nb,
+ unsigned long status, void *notif_info)
+{
+ struct secvio_sc_notifier_info *info = notif_info;
+ struct imx_secvio_sc_data *data =
+ container_of(nb, struct imx_secvio_sc_data,
+ report_nb);
+ struct device *dev = data->dev;
+
+ /* Information about the security violation */
+ if (info->hpsvs & HPSVS_LP_SEC_VIO_MASK)
+ dev_info(dev, "SNVS secvio: LPSV\n");
+ if (info->hpsvs & HPSVS_SW_LPSV_MASK)
+ dev_info(dev, "SNVS secvio: SW LPSV\n");
+ if (info->hpsvs & HPSVS_SW_FSV_MASK)
+ dev_info(dev, "SNVS secvio: SW FSV\n");
+ if (info->hpsvs & HPSVS_SW_SV_MASK)
+ dev_info(dev, "SNVS secvio: SW SV\n");
+ if (info->hpsvs & HPSVS_SV5_MASK)
+ dev_info(dev, "SNVS secvio: SV 5\n");
+ if (info->hpsvs & HPSVS_SV4_MASK)
+ dev_info(dev, "SNVS secvio: SV 4\n");
+ if (info->hpsvs & HPSVS_SV3_MASK)
+ dev_info(dev, "SNVS secvio: SV 3\n");
+ if (info->hpsvs & HPSVS_SV2_MASK)
+ dev_info(dev, "SNVS secvio: SV 2\n");
+ if (info->hpsvs & HPSVS_SV1_MASK)
+ dev_info(dev, "SNVS secvio: SV 1\n");
+ if (info->hpsvs & HPSVS_SV0_MASK)
+ dev_info(dev, "SNVS secvio: SV 0\n");
+
+ /* Information about the tampers */
+ if (info->lps & LPS_ESVD_MASK)
+ dev_info(dev, "SNVS tamper: External SV\n");
+ if (info->lps & LPS_ET2D_MASK)
+ dev_info(dev, "SNVS tamper: Tamper 2\n");
+ if (info->lps & LPS_ET1D_MASK)
+ dev_info(dev, "SNVS tamper: Tamper 1\n");
+ if (info->lps & LPS_WMT2D_MASK)
+ dev_info(dev, "SNVS tamper: Wire Mesh 2\n");
+ if (info->lps & LPS_WMT1D_MASK)
+ dev_info(dev, "SNVS tamper: Wire Mesh 1\n");
+ if (info->lps & LPS_VTD_MASK)
+ dev_info(dev, "SNVS tamper: Voltage\n");
+ if (info->lps & LPS_TTD_MASK)
+ dev_info(dev, "SNVS tamper: Temperature\n");
+ if (info->lps & LPS_CTD_MASK)
+ dev_info(dev, "SNVS tamper: Clock\n");
+ if (info->lps & LPS_PGD_MASK)
+ dev_info(dev, "SNVS tamper: Power Glitch\n");
+ if (info->lps & LPS_MCR_MASK)
+ dev_info(dev, "SNVS tamper: Monotonic Counter rollover\n");
+ if (info->lps & LPS_SRTCR_MASK)
+ dev_info(dev, "SNVS tamper: Secure RTC rollover\n");
+ if (info->lps & LPS_LPTA_MASK)
+ dev_info(dev, "SNVS tamper: Time alarm\n");
+
+ if (info->lptds & LPTDS_ET10D_MASK)
+ dev_info(dev, "SNVS tamper: Tamper 10\n");
+ if (info->lptds & LPTDS_ET9D_MASK)
+ dev_info(dev, "SNVS tamper: Tamper 9\n");
+ if (info->lptds & LPTDS_ET8D_MASK)
+ dev_info(dev, "SNVS tamper: Tamper 8\n");
+ if (info->lptds & LPTDS_ET7D_MASK)
+ dev_info(dev, "SNVS tamper: Tamper 7\n");
+ if (info->lptds & LPTDS_ET6D_MASK)
+ dev_info(dev, "SNVS tamper: Tamper 6\n");
+ if (info->lptds & LPTDS_ET5D_MASK)
+ dev_info(dev, "SNVS tamper: Tamper 5\n");
+ if (info->lptds & LPTDS_ET4D_MASK)
+ dev_info(dev, "SNVS tamper: Tamper 4\n");
+ if (info->lptds & LPTDS_ET3D_MASK)
+ dev_info(dev, "SNVS tamper: Tamper 3\n");
+
+ return 0;
+}
+
+static void if_imx_secvio_sc_disable_irq(void *dev)
+{
+ imx_secvio_sc_disable_irq(dev);
+}
+
+static int imx_secvio_sc_open(struct inode *node, struct file *filp)
+{
+ filp->private_data = node->i_private;
+
+ return 0;
+}
+
+static long imx_secvio_sc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct device *dev = file->private_data;
+ struct secvio_sc_notifier_info info;
+ int ret;
+
+ switch (cmd) {
+ case IMX_SECVIO_SC_GET_STATE:
+ ret = imx_secvio_sc_get_state(dev, &info);
+ if (ret)
+ return ret;
+
+ ret = copy_to_user((void *)arg, &info, sizeof(info));
+ if (ret) {
+ dev_err(dev, "Fail to copy info to user\n");
+ return -EFAULT;
+ }
+ break;
+ case IMX_SECVIO_SC_CHECK_STATE:
+ ret = imx_secvio_sc_check_state(dev);
+ if (ret)
+ return ret;
+ break;
+ case IMX_SECVIO_SC_CLEAR_STATE:
+ ret = copy_from_user(&info, (void *)arg, sizeof(info));
+ if (ret) {
+ dev_err(dev, "Fail to copy info from user\n");
+ return -EFAULT;
+ }
+
+ ret = imx_secvio_sc_clear_state(dev, info.hpsvs, info.lps,
+ info.lptds);
+ if (ret)
+ return ret;
+ break;
+ default:
+ ret = -ENOIOCTLCMD;
+ }
+
+ return ret;
+}
+
+static const struct file_operations imx_secvio_sc_fops = {
+ .owner = THIS_MODULE,
+ .open = imx_secvio_sc_open,
+ .unlocked_ioctl = imx_secvio_sc_ioctl,
+};
+
+static void if_misc_deregister(void *miscdevice)
+{
+ misc_deregister(miscdevice);
+}
+
+static int imx_secvio_sc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct imx_secvio_sc_data *data;
+ u32 seco_version = 0;
+ bool own_secvio;
+ u32 irq_status;
+ int ret;
+
+ /* Allocate private data */
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (!devres_open_group(dev, NULL, GFP_KERNEL))
+ return -ENOMEM;
+
+ data->dev = dev;
+
+ dev_set_drvdata(dev, data);
+
+ data->nvmem = devm_nvmem_device_get(dev, NULL);
+ if (IS_ERR(data->nvmem)) {
+ ret = PTR_ERR(data->nvmem);
+
+ if (ret != -EPROBE_DEFER)
+ dev_err_probe(dev, ret, "Failed to retrieve nvmem\n");
+
+ goto clean;
+ }
+
+ /* Get a handle */
+ ret = imx_scu_get_handle(&data->ipc_handle);
+ if (ret) {
+ dev_err(dev, "cannot get handle to scu: %d\n", ret);
+ goto clean;
+ }
+
+ /* Check the version of the SECO */
+ ret = imx_sc_seco_build_info(data->ipc_handle, &seco_version, NULL);
+ if (ret) {
+ dev_err(dev, "Failed to get seco version\n");
+ goto clean;
+ }
+
+ if ((seco_version & SECO_VERSION_MINOR_MASK) <
+ SECO_MINOR_VERSION_SUPPORT_SECVIO_TAMPER) {
+ dev_err(dev, "SECO version %.8x doesn't support all secvio\n",
+ seco_version);
+ ret = -EOPNOTSUPP;
+ goto clean;
+ }
+
+ /* Init debug FS */
+ ret = imx_secvio_sc_debugfs(dev);
+ if (ret) {
+ dev_err(dev, "Failed to set debugfs\n");
+ goto clean;
+ }
+
+ /* Check we own the SECVIO */
+ ret = imx_sc_rm_is_resource_owned(data->ipc_handle, IMX_SC_R_SECVIO);
+ if (ret < 0) {
+ dev_err(dev, "Failed to retrieve secvio ownership\n");
+ goto clean;
+ }
+
+ own_secvio = ret > 0;
+ if (!own_secvio) {
+ dev_err(dev, "Secvio resource is not owned\n");
+ ret = -EPERM;
+ goto clean;
+ }
+
+ /* Check IRQ exists and enable it */
+ ret = imx_scu_irq_get_status(IMX_SC_IRQ_GROUP_WAKE, &irq_status);
+ if (ret) {
+ dev_err(dev, "Cannot get IRQ state: %d\n", ret);
+ goto clean;
+ }
+
+ ret = imx_secvio_sc_enable_irq(dev);
+ if (ret) {
+ dev_err(dev, "Failed to enable IRQ\n");
+ goto clean;
+ }
+
+ ret = devm_add_action_or_reset(dev, if_imx_secvio_sc_disable_irq, dev);
+ if (ret) {
+ dev_err(dev, "Failed to add managed action to disable IRQ\n");
+ goto clean;
+ }
+
+ /* Register the notifier for IRQ from SNVS */
+ data->irq_nb.notifier_call = imx_secvio_sc_notify;
+ ret = imx_scu_irq_register_notifier(&data->irq_nb);
+ if (ret) {
+ dev_err(dev, "Failed to register IRQ notification handler\n");
+ goto clean;
+ }
+
+ ret = devm_add_action_or_reset(dev, if_imx_scu_irq_register_notifier,
+ &data->irq_nb);
+ if (ret) {
+ dev_err(dev, "Failed to add action to remove irq notif\n");
+ goto clean;
+ }
+
+ /* Register the notification for reporting to user */
+ data->report_nb.notifier_call = report_to_user_notify;
+ ret = register_imx_secvio_sc_notifier(&data->report_nb);
+ if (ret) {
+ dev_err(dev, "Failed to register report notif handler\n");
+ goto clean;
+ }
+
+ ret = devm_add_action_or_reset(dev, if_unregister_imx_secvio_sc_notifier,
+ &data->report_nb);
+ if (ret) {
+ dev_err(dev, "Failed to add action to remove report notif\n");
+ goto clean;
+ }
+
+ /* Register misc device for IOCTL */
+ data->miscdev.name = devm_kstrdup(dev, "secvio-sc", GFP_KERNEL);
+ data->miscdev.minor = MISC_DYNAMIC_MINOR;
+ data->miscdev.fops = &imx_secvio_sc_fops;
+ data->miscdev.parent = dev;
+ ret = misc_register(&data->miscdev);
+ if (ret) {
+ dev_err(dev, "failed to register misc device\n");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(dev, if_misc_deregister, &data->miscdev);
+ if (ret) {
+ dev_err(dev, "Failed to add action to unregister miscdev\n");
+ goto clean;
+ }
+
+ imx_secvio_sc_dev = dev;
+
+ /* Process current state of the secvio and tampers */
+ imx_secvio_sc_check_state(dev);
+
+ devres_remove_group(dev, NULL);
+
+ return ret;
+
+clean:
+ devres_release_group(dev, NULL);
+
+ return ret;
+}
+
+static const struct of_device_id imx_secvio_sc_dt_ids[] = {
+ { .compatible = "fsl,imx-sc-secvio", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_secvio_sc_dt_ids);
+
+static struct platform_driver imx_secvio_sc_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "imx-secvio-sc",
+ .of_match_table = imx_secvio_sc_dt_ids,
+ },
+ .probe = imx_secvio_sc_probe,
+};
+module_platform_driver(imx_secvio_sc_driver);
+
+MODULE_AUTHOR("Franck LENORMAND <[email protected]>");
+MODULE_DESCRIPTION("NXP i.MX driver to handle SNVS secvio irq sent by SCFW");
+MODULE_LICENSE("GPL");
diff --git a/include/soc/imx/imx-secvio-sc.h b/include/soc/imx/imx-secvio-sc.h
new file mode 100644
index 000000000000..d8c9208217fe
--- /dev/null
+++ b/include/soc/imx/imx-secvio-sc.h
@@ -0,0 +1,216 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2019, 2024 NXP
+ */
+
+#ifndef _MISC_IMX_SECVIO_SC_H_
+#define _MISC_IMX_SECVIO_SC_H_
+
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/notifier.h>
+
+/* Bitmask of the security violation status bit in the HPSVS register */
+#define HPSVS_LP_SEC_VIO_MASK BIT(31)
+#define HPSVS_SW_LPSV_MASK BIT(15)
+#define HPSVS_SW_FSV_MASK BIT(14)
+#define HPSVS_SW_SV_MASK BIT(13)
+#define HPSVS_SV5_MASK BIT(5)
+#define HPSVS_SV4_MASK BIT(4)
+#define HPSVS_SV3_MASK BIT(3)
+#define HPSVS_SV2_MASK BIT(2)
+#define HPSVS_SV1_MASK BIT(1)
+#define HPSVS_SV0_MASK BIT(0)
+
+/* Bitmask of all security violation status bit in the HPSVS register */
+#define HPSVS_ALL_SV_MASK (HPSVS_LP_SEC_VIO_MASK | \
+ HPSVS_SW_LPSV_MASK | \
+ HPSVS_SW_FSV_MASK | \
+ HPSVS_SW_SV_MASK | \
+ HPSVS_SV5_MASK | \
+ HPSVS_SV4_MASK | \
+ HPSVS_SV3_MASK | \
+ HPSVS_SV2_MASK | \
+ HPSVS_SV1_MASK | \
+ HPSVS_SV0_MASK)
+
+/*
+ * Bitmask of the security violation and tampers status bit in the LPS register
+ */
+#define LPS_ESVD_MASK BIT(16)
+#define LPS_ET2D_MASK BIT(10)
+#define LPS_ET1D_MASK BIT(9)
+#define LPS_WMT2D_MASK BIT(8)
+#define LPS_WMT1D_MASK BIT(7)
+#define LPS_VTD_MASK BIT(6)
+#define LPS_TTD_MASK BIT(5)
+#define LPS_CTD_MASK BIT(4)
+#define LPS_PGD_MASK BIT(3)
+#define LPS_MCR_MASK BIT(2)
+#define LPS_SRTCR_MASK BIT(1)
+#define LPS_LPTA_MASK BIT(0)
+
+/*
+ * Bitmask of all security violation and tampers status bit in the LPS register
+ */
+#define LPS_ALL_TP_MASK (LPS_ESVD_MASK | \
+ LPS_ET2D_MASK | \
+ LPS_ET1D_MASK | \
+ LPS_WMT2D_MASK | \
+ LPS_WMT1D_MASK | \
+ LPS_VTD_MASK | \
+ LPS_TTD_MASK | \
+ LPS_CTD_MASK | \
+ LPS_PGD_MASK | \
+ LPS_MCR_MASK | \
+ LPS_SRTCR_MASK | \
+ LPS_LPTA_MASK)
+
+/*
+ * Bitmask of the security violation and tampers status bit in the LPTDS
+ * register
+ */
+#define LPTDS_ET10D_MASK BIT(7)
+#define LPTDS_ET9D_MASK BIT(6)
+#define LPTDS_ET8D_MASK BIT(5)
+#define LPTDS_ET7D_MASK BIT(4)
+#define LPTDS_ET6D_MASK BIT(3)
+#define LPTDS_ET5D_MASK BIT(2)
+#define LPTDS_ET4D_MASK BIT(1)
+#define LPTDS_ET3D_MASK BIT(0)
+
+/*
+ * Bitmask of all security violation and tampers status bit in the LPTDS
+ * register
+ */
+#define LPTDS_ALL_TP_MASK (LPTDS_ET10D_MASK | \
+ LPTDS_ET9D_MASK | \
+ LPTDS_ET8D_MASK | \
+ LPTDS_ET7D_MASK | \
+ LPTDS_ET6D_MASK | \
+ LPTDS_ET5D_MASK | \
+ LPTDS_ET4D_MASK | \
+ LPTDS_ET3D_MASK)
+
+/* Access for sc_seco_secvio_config API */
+#define SECVIO_CONFIG_READ 0
+#define SECVIO_CONFIG_WRITE 1
+
+/* Internal Structure */
+struct imx_secvio_sc_data {
+ struct device *dev;
+
+ struct imx_sc_ipc *ipc_handle;
+
+ struct notifier_block irq_nb;
+ struct notifier_block report_nb;
+
+ struct nvmem_device *nvmem;
+
+ struct miscdevice miscdev;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dfs;
+#endif
+
+ u32 version;
+};
+
+/* Struct for notification */
+/**
+ * struct secvio_sc_notifier_info - Information about the status of the SNVS
+ * @hpsvs: status from register HPSVS
+ * @lps: status from register LPS
+ * @lptds: status from register LPTDS
+ */
+struct secvio_sc_notifier_info {
+ u32 hpsvs;
+ u32 lps;
+ u32 lptds;
+};
+
+/**
+ * register_imx_secvio_sc_notifier() - Register a notifier
+ *
+ * @nb: The notifier block structure
+ *
+ * Register a function to notify to the imx-secvio-sc module. The function
+ * will be notified when a check of the state of the SNVS happens: called by
+ * a user or triggered by an interruption form the SNVS.
+ *
+ * The struct secvio_sc_notifier_info is passed as data to the notifier.
+ *
+ * Return: 0 in case of success
+ */
+int register_imx_secvio_sc_notifier(struct notifier_block *nb);
+
+/**
+ * unregister_imx_secvio_sc_notifier() - Unregister a notifier
+ *
+ * @nb: The notifier block structure
+ *
+ * Return: 0 in case of success
+ */
+int unregister_imx_secvio_sc_notifier(struct notifier_block *nb);
+
+/**
+ * imx_secvio_sc_get_state() - Get the state of the SNVS
+ *
+ * @dev: Pointer to the struct device of secvio
+ * @info: The structure containing the state of the SNVS
+ *
+ * Return: 0 in case of success
+ */
+int imx_secvio_sc_get_state(struct device *dev, struct secvio_sc_notifier_info *info);
+
+/**
+ * imx_secvio_sc_check_state() - Check the state of the SNVS
+ *
+ * If a security violation or a tamper is detected, the list of notifier
+ * (registered using register_imx_secvio_sc_notifier() ) will be called
+ *
+ * @dev: Pointer to the struct device of secvio
+ *
+ * Return: 0 in case of success
+ */
+int imx_secvio_sc_check_state(struct device *dev);
+
+/**
+ * imx_secvio_sc_clear_state() - Clear the state of the SNVS
+ *
+ * @dev: Pointer to the struct device of secvio
+ * @hpsvs: Value to write to HPSVS register
+ * @lps: Value to write to LPS register
+ * @lptds: Value to write to LPTDSregister
+ *
+ * The function will write the value provided to the corresponding register
+ * which will clear the status of the bits set.
+ *
+ * Return: 0 in case of success
+ */
+int imx_secvio_sc_clear_state(struct device *dev, u32 hpsvs, u32 lps, u32 lptds);
+
+/* Commands of the ioctl interface */
+enum ioctl_cmd_t {
+ GET_STATE,
+ CHECK_STATE,
+ CLEAR_STATE,
+};
+
+/* Definition for the ioctl interface */
+#define IMX_SECVIO_SC_GET_STATE _IOR('S', GET_STATE, \
+ struct secvio_sc_notifier_info)
+#define IMX_SECVIO_SC_CHECK_STATE _IO('S', CHECK_STATE)
+#define IMX_SECVIO_SC_CLEAR_STATE _IOW('S', CLEAR_STATE, \
+ struct secvio_sc_notifier_info)
+
+#ifdef CONFIG_DEBUG_FS
+int imx_secvio_sc_debugfs(struct device *dev);
+#else
+static inline
+int imx_secvio_sc_debugfs(struct device *dev)
+{
+ return 0;
+}
+#endif /* CONFIG_DEBUG_FS */
+#endif /* _MISC_IMX_SECVIO_SC_H_ */
--
2.25.1


2024-05-01 05:36:06

by Vabhav Sharma

[permalink] [raw]
Subject: [PATCH 2/4] firmware: imx: Add SC APIs required for secvio module

The Security Violation module requires SC API.
Added below API:
- imx_sc_seco_build_info
- imx_sc_seco_secvio_enable
- imx_sc_seco_secvio_config
- imx_sc_seco_secvio_dgo_config

Signed-off-by: Franck LENORMAND <[email protected]>
Reviewed-by: Iuliana Prodan <[email protected]>
Reviewed-by: Horia Geanta<[email protected]>
Signed-off-by: Dong Aisheng <[email protected]>
Signed-off-by: Vabhav Sharma <[email protected]>
---
drivers/firmware/imx/Makefile | 2 +-
drivers/firmware/imx/imx-scu.c | 4 +-
drivers/firmware/imx/seco.c | 216 ++++++++++++++++++++++++++
include/linux/firmware/imx/ipc.h | 1 +
include/linux/firmware/imx/sci.h | 4 +
include/linux/firmware/imx/svc/seco.h | 69 ++++++++
6 files changed, 294 insertions(+), 2 deletions(-)
create mode 100644 drivers/firmware/imx/seco.c
create mode 100644 include/linux/firmware/imx/svc/seco.h

diff --git a/drivers/firmware/imx/Makefile b/drivers/firmware/imx/Makefile
index 8f9f04a513a8..b53d2dee8ff3 100644
--- a/drivers/firmware/imx/Makefile
+++ b/drivers/firmware/imx/Makefile
@@ -1,3 +1,3 @@
# 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
diff --git a/drivers/firmware/imx/imx-scu.c b/drivers/firmware/imx/imx-scu.c
index 1dd4362ef9a3..c96dc73689a8 100644
--- a/drivers/firmware/imx/imx-scu.c
+++ b/drivers/firmware/imx/imx-scu.c
@@ -242,9 +242,11 @@ int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp)
* 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) &&
+ 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))
+ || (saved_svc == IMX_SC_RPC_SVC_SECO &&
+ saved_func == IMX_SC_SECO_FUNC_BUILD_INFO))
ret = 0;
}

diff --git a/drivers/firmware/imx/seco.c b/drivers/firmware/imx/seco.c
new file mode 100644
index 000000000000..2d6bf301ac87
--- /dev/null
+++ b/drivers/firmware/imx/seco.c
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2020, 2024 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;
+ 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_BUILD_INFO;
+ hdr->size = 1;
+
+ ret = imx_scu_call_rpc(ipc, &msg, true);
+ if (ret)
+ return ret;
+
+ if (version)
+ *version = msg.version;
+ if (commit)
+ *commit = msg.commit;
+
+ return 0;
+}
+EXPORT_SYMBOL(imx_sc_seco_build_info);
+
+int imx_sc_seco_secvio_enable(struct imx_sc_ipc *ipc)
+{
+ struct imx_sc_rpc_msg msg;
+ struct imx_sc_rpc_msg *hdr = &msg;
+
+ hdr->ver = IMX_SC_RPC_VERSION;
+ hdr->svc = IMX_SC_RPC_SVC_SECO;
+ hdr->func = IMX_SC_SECO_FUNC_SECVIO_ENABLE;
+ hdr->size = 1;
+
+ return imx_scu_call_rpc(ipc, &msg, true);
+}
+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;
+
+ hdr->ver = IMX_SC_RPC_VERSION;
+ hdr->svc = IMX_SC_RPC_SVC_SECO;
+ hdr->func = 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;
+
+ hdr->ver = IMX_SC_RPC_VERSION;
+ hdr->svc = IMX_SC_RPC_SVC_SECO;
+ hdr->func = 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/include/linux/firmware/imx/ipc.h b/include/linux/firmware/imx/ipc.h
index 0b4643571625..df38ab8e7e2e 100644
--- a/include/linux/firmware/imx/ipc.h
+++ b/include/linux/firmware/imx/ipc.h
@@ -25,6 +25,7 @@ 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,
};

struct imx_sc_rpc_msg {
diff --git a/include/linux/firmware/imx/sci.h b/include/linux/firmware/imx/sci.h
index df17196df5ff..947e49d8bebc 100644
--- a/include/linux/firmware/imx/sci.h
+++ b/include/linux/firmware/imx/sci.h
@@ -15,6 +15,10 @@
#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_SECVIO BIT(6) /* Security violation */
+#define IMX_SC_IRQ_GROUP_WAKE 3 /* Wakeup interrupts */

#if IS_ENABLED(CONFIG_IMX_SCU)
int imx_scu_enable_general_irq_channel(struct device *dev);
diff --git a/include/linux/firmware/imx/svc/seco.h b/include/linux/firmware/imx/svc/seco.h
new file mode 100644
index 000000000000..508444c02d39
--- /dev/null
+++ b/include/linux/firmware/imx/svc/seco.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2020, 2024 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 RPCs/RM/SECO function calls.
+ */
+enum imx_sc_seco_func {
+ IMX_SC_SECO_FUNC_UNKNOWN = 0,
+ IMX_SC_SECO_FUNC_BUILD_INFO = 16,
+ 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_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_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 */
--
2.25.1


2024-05-01 05:43:26

by Vabhav Sharma

[permalink] [raw]
Subject: [PATCH 4/4] arm64: dts: imx8q: Add node for Security Violation

Add secvio node definition for imx8qxp.

Signed-off-by: Franck LENORMAND <[email protected]>
Signed-off-by: Vabhav Sharma <[email protected]>
---
arch/arm64/boot/dts/freescale/imx8qxp.dtsi | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/arch/arm64/boot/dts/freescale/imx8qxp.dtsi b/arch/arm64/boot/dts/freescale/imx8qxp.dtsi
index 958267b33340..5cd9701d47b9 100644
--- a/arch/arm64/boot/dts/freescale/imx8qxp.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8qxp.dtsi
@@ -241,6 +241,11 @@ rtc: rtc {
compatible = "fsl,imx8qxp-sc-rtc";
};

+ secvio: secvio {
+ compatible = "fsl,imx-sc-secvio";
+ nvmem = <&ocotp>;
+ };
+
watchdog {
compatible = "fsl,imx8qxp-sc-wdt", "fsl,imx-sc-wdt";
timeout-sec = <60>;
--
2.25.1


2024-05-01 09:46:33

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH 1/4] dt-bindings: firmware: secvio: Add documentation

On 01/05/2024 07:32, Vabhav Sharma wrote:
> This patch adds the documentation for the SECVIO driver.

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

Describe hardware, not driver.


>
> Signed-off-by: Franck LENORMAND <[email protected]>
> Signed-off-by: Vabhav Sharma <[email protected]>


Please use scripts/get_maintainers.pl to get a list of necessary people
and lists to CC. It might happen, that command when run on an older
kernel, gives you outdated entries. Therefore please be sure you base
your patches on recent Linux kernel.

Tools like b4 or scripts/get_maintainer.pl provide you proper list of
people, so fix your workflow. Tools might also fail if you work on some
ancient tree (don't, instead use mainline), work on fork of kernel
(don't, instead use mainline) or you ignore some maintainers (really
don't). Just use b4 and everything should be fine, although remember
about `b4 prep --auto-to-cc` if you added new patches to the patchset.

You missed at least devicetree list (maybe more), so this won't be
tested by automated tooling. Performing review on untested code might be
a waste of time, thus I will skip this patch entirely till you follow
the process allowing the patch to be tested.

Please kindly resend and include all necessary To/Cc entries.


Best regards,
Krzysztof


2024-05-02 00:57:55

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH 3/4] soc: imx: secvio: Add support for SNVS secvio and tamper via SCFW

Hi Vabhav,

kernel test robot noticed the following build warnings:

[auto build test WARNING on shawnguo/for-next]
[also build test WARNING on robh/for-next krzk/for-next krzk-dt/for-next linus/master v6.9-rc6 next-20240501]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url: https://github.com/intel-lab-lkp/linux/commits/Vabhav-Sharma/dt-bindings-firmware-secvio-Add-documentation/20240501-133607
base: https://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux.git for-next
patch link: https://lore.kernel.org/r/20240501053205.1737248-4-vabhav.sharma%40nxp.com
patch subject: [PATCH 3/4] soc: imx: secvio: Add support for SNVS secvio and tamper via SCFW
config: hexagon-randconfig-r131-20240502 (https://download.01.org/0day-ci/archive/20240502/[email protected]/config)
compiler: clang version 19.0.0git (https://github.com/llvm/llvm-project 37ae4ad0eef338776c7e2cffb3896153d43dcd90)
reproduce: (https://download.01.org/0day-ci/archive/20240502/[email protected]/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

sparse warnings: (new ones prefixed by >>)
>> drivers/soc/imx/secvio/imx-secvio-sc.c:381:37: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void [noderef] __user *to @@ got void * @@
drivers/soc/imx/secvio/imx-secvio-sc.c:381:37: sparse: expected void [noderef] __user *to
drivers/soc/imx/secvio/imx-secvio-sc.c:381:37: sparse: got void *
>> drivers/soc/imx/secvio/imx-secvio-sc.c:393:46: sparse: sparse: incorrect type in argument 2 (different address spaces) @@ expected void const [noderef] __user *from @@ got void * @@
drivers/soc/imx/secvio/imx-secvio-sc.c:393:46: sparse: expected void const [noderef] __user *from
drivers/soc/imx/secvio/imx-secvio-sc.c:393:46: sparse: got void *

vim +381 drivers/soc/imx/secvio/imx-secvio-sc.c

368
369 static long imx_secvio_sc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
370 {
371 struct device *dev = file->private_data;
372 struct secvio_sc_notifier_info info;
373 int ret;
374
375 switch (cmd) {
376 case IMX_SECVIO_SC_GET_STATE:
377 ret = imx_secvio_sc_get_state(dev, &info);
378 if (ret)
379 return ret;
380
> 381 ret = copy_to_user((void *)arg, &info, sizeof(info));
382 if (ret) {
383 dev_err(dev, "Fail to copy info to user\n");
384 return -EFAULT;
385 }
386 break;
387 case IMX_SECVIO_SC_CHECK_STATE:
388 ret = imx_secvio_sc_check_state(dev);
389 if (ret)
390 return ret;
391 break;
392 case IMX_SECVIO_SC_CLEAR_STATE:
> 393 ret = copy_from_user(&info, (void *)arg, sizeof(info));
394 if (ret) {
395 dev_err(dev, "Fail to copy info from user\n");
396 return -EFAULT;
397 }
398
399 ret = imx_secvio_sc_clear_state(dev, info.hpsvs, info.lps,
400 info.lptds);
401 if (ret)
402 return ret;
403 break;
404 default:
405 ret = -ENOIOCTLCMD;
406 }
407
408 return ret;
409 }
410

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

2024-05-09 04:04:40

by Vabhav Sharma

[permalink] [raw]
Subject: RE: [EXT] Re: [PATCH 1/4] dt-bindings: firmware: secvio: Add documentation



> -----Original Message-----
> From: Krzysztof Kozlowski <[email protected]>
> Sent: Wednesday, May 1, 2024 3:16 PM
> To: Vabhav Sharma <[email protected]>; Rob Herring
> <[email protected]>; Krzysztof Kozlowski
> <[email protected]>; Conor Dooley <[email protected]>;
> Pengutronix Kernel Team <[email protected]>; Fabio Estevam
> <[email protected]>; dl-linux-imx <[email protected]>; Ulf Hansson
> <[email protected]>; [email protected]; linux-arm-
> [email protected]; Shawn Guo <[email protected]>; Sascha
> Hauer <[email protected]>; Iuliana Prodan
> <[email protected]>
> Cc: Silvano Di Ninno <[email protected]>; Varun Sethi
> <[email protected]>; Pankaj Gupta <[email protected]>; Peng Fan
> <[email protected]>; Aisheng Dong <[email protected]>; Frank Li
> <[email protected]>; Daniel Baluta <[email protected]>; Franck
> Lenormand <[email protected]>
> Subject: [EXT] Re: [PATCH 1/4] dt-bindings: firmware: secvio: Add
> documentation
>
> Caution: This is an external email. Please take care when clicking links or
> opening attachments. When in doubt, report the message using the 'Report
> this email' button
>
>
> On 01/05/2024 07:32, Vabhav Sharma wrote:
> > This patch adds the documentation for the SECVIO driver.
>
> Please do not use "This commit/patch/change", but imperative mood. See
> longer explanation here:
> https://elixir.b/
> ootlin.com%2Flinux%2Fv5.17.1%2Fsource%2FDocumentation%2Fprocess%2F
> submitting-
> patches.rst%23L95&data=05%7C02%7Cvabhav.sharma%40nxp.com%7C4e698
> f57f2be4e4b419c08dc69c39540%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C
> 0%7C0%7C638501535958415388%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiM
> C4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0%7C
> %7C%7C&sdata=7NB%2FbjnqJStL2QBVTwGflgSSViD9n8TL78X85OtQX54%3D&
> reserved=0
>
> Describe hardware, not driver.
Ok
>
>
> >
> > Signed-off-by: Franck LENORMAND <[email protected]>
> > Signed-off-by: Vabhav Sharma <[email protected]>
>
>
> Please use scripts/get_maintainers.pl to get a list of necessary people and lists
> to CC. It might happen, that command when run on an older kernel, gives you
> outdated entries. Therefore please be sure you base your patches on recent
> Linux kernel.
>
> Tools like b4 or scripts/get_maintainer.pl provide you proper list of people, so
> fix your workflow. Tools might also fail if you work on some ancient tree
> (don't, instead use mainline), work on fork of kernel (don't, instead use
> mainline) or you ignore some maintainers (really don't). Just use b4 and
> everything should be fine, although remember about `b4 prep --auto-to-cc` if
> you added new patches to the patchset.
Thank you for feedback and used b4 as recommended
>
> You missed at least devicetree list (maybe more), so this won't be tested by
> automated tooling. Performing review on untested code might be a waste of
> time, thus I will skip this patch entirely till you follow the process allowing the
> patch to be tested.
Added the required list
>
> Please kindly resend and include all necessary To/Cc entries.
Ok, done
>
>
> Best regards,
> Krzysztof