2024-04-18 12:11:34

by Marek Behún

[permalink] [raw]
Subject: [PATCH v6 00/11] Turris Omnia MCU driver

Hello Andy, Dan, Linus, Arnd, Gregory, and others,

I am sending v6 of the series adding Turris Omnia MCU driver.

This series depends on the immutable branch between LEDs and locking,
introducing devm_mutex_init(), see the PR
https://lore.kernel.org/linux-leds/[email protected]/

See the cover letters for v1, v2, v3, v4 and v5:
https://patchwork.kernel.org/project/linux-soc/cover/[email protected]/
https://patchwork.kernel.org/project/linux-soc/cover/[email protected]/
https://patchwork.kernel.org/project/linux-soc/cover/[email protected]/
https://patchwork.kernel.org/project/linux-soc/cover/[email protected]/
https://patchwork.kernel.org/project/linux-soc/cover/[email protected]/

Changes since v5:
- use dev_get_drvdata(dev) instead of
i2c_get_clientdata(to_i2c_client(dev))
- uses devm_mutex_init() from the above mentioned branch
- addressed Andy's suggestions for patch 2 (excluding the one suggesting
100 column lines, I still prefer 80 columns)
- addressed Dan's suggestion for devm_irq_create_mapping() (return
-ENXIO if mapping could not be created, not 0 as irq_create_mapping()
does)
- I threw away the changes for other drivers that came with the new
devm_ helpers, notably conversion to devm_debugfs_create_dir(), as
there were some problems

Marek Behún (11):
dt-bindings: arm: add cznic,turris-omnia-mcu binding
platform: cznic: Add preliminary support for Turris Omnia MCU
platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs
platform: cznic: turris-omnia-mcu: Add support for poweroff and wakeup
platform: cznic: turris-omnia-mcu: Add support for MCU watchdog
devm-helpers: Add resource managed version of irq_create_mapping()
platform: cznic: turris-omnia-mcu: Add support for MCU provided TRNG
devm-helpers: Add resource managed version of debugfs directory create
function
platform: cznic: turris-omnia-mcu: Add support for digital message
signing via debugfs
ARM: dts: turris-omnia: Add MCU system-controller node
ARM: dts: turris-omnia: Add GPIO key node for front button

.../ABI/testing/debugfs-turris-omnia-mcu | 13 +
.../sysfs-bus-i2c-devices-turris-omnia-mcu | 126 ++
.../bindings/arm/cznic,turris-omnia-mcu.yaml | 86 ++
MAINTAINERS | 5 +
.../dts/marvell/armada-385-turris-omnia.dts | 35 +-
drivers/platform/Kconfig | 2 +
drivers/platform/Makefile | 1 +
drivers/platform/cznic/Kconfig | 51 +
drivers/platform/cznic/Makefile | 9 +
.../platform/cznic/turris-omnia-mcu-base.c | 439 +++++++
.../platform/cznic/turris-omnia-mcu-debugfs.c | 209 ++++
.../platform/cznic/turris-omnia-mcu-gpio.c | 1047 +++++++++++++++++
.../cznic/turris-omnia-mcu-sys-off-wakeup.c | 258 ++++
.../platform/cznic/turris-omnia-mcu-trng.c | 89 ++
.../cznic/turris-omnia-mcu-watchdog.c | 123 ++
drivers/platform/cznic/turris-omnia-mcu.h | 188 +++
include/linux/devm-helpers.h | 94 ++
include/linux/turris-omnia-mcu-interface.h | 249 ++++
18 files changed, 3023 insertions(+), 1 deletion(-)
create mode 100644 Documentation/ABI/testing/debugfs-turris-omnia-mcu
create mode 100644 Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu
create mode 100644 Documentation/devicetree/bindings/arm/cznic,turris-omnia-mcu.yaml
create mode 100644 drivers/platform/cznic/Kconfig
create mode 100644 drivers/platform/cznic/Makefile
create mode 100644 drivers/platform/cznic/turris-omnia-mcu-base.c
create mode 100644 drivers/platform/cznic/turris-omnia-mcu-debugfs.c
create mode 100644 drivers/platform/cznic/turris-omnia-mcu-gpio.c
create mode 100644 drivers/platform/cznic/turris-omnia-mcu-sys-off-wakeup.c
create mode 100644 drivers/platform/cznic/turris-omnia-mcu-trng.c
create mode 100644 drivers/platform/cznic/turris-omnia-mcu-watchdog.c
create mode 100644 drivers/platform/cznic/turris-omnia-mcu.h
create mode 100644 include/linux/turris-omnia-mcu-interface.h

--
2.43.2



2024-04-18 12:12:01

by Marek Behún

[permalink] [raw]
Subject: [PATCH v6 07/11] platform: cznic: turris-omnia-mcu: Add support for MCU provided TRNG

Add support for true random number generator provided by the MCU.
New Omnia boards come without the Atmel SHA204-A chip. Instead the
crypto functionality is provided by new microcontroller, which has
a TRNG peripheral.

Signed-off-by: Marek Behún <[email protected]>
---
drivers/platform/cznic/Kconfig | 2 +
drivers/platform/cznic/Makefile | 1 +
.../platform/cznic/turris-omnia-mcu-base.c | 6 +-
.../platform/cznic/turris-omnia-mcu-gpio.c | 2 +-
.../platform/cznic/turris-omnia-mcu-trng.c | 89 +++++++++++++++++++
drivers/platform/cznic/turris-omnia-mcu.h | 8 ++
6 files changed, 106 insertions(+), 2 deletions(-)
create mode 100644 drivers/platform/cznic/turris-omnia-mcu-trng.c

diff --git a/drivers/platform/cznic/Kconfig b/drivers/platform/cznic/Kconfig
index e2649cdecc38..750d5f47dba8 100644
--- a/drivers/platform/cznic/Kconfig
+++ b/drivers/platform/cznic/Kconfig
@@ -19,6 +19,7 @@ config TURRIS_OMNIA_MCU
depends on I2C
select GPIOLIB
select GPIOLIB_IRQCHIP
+ select HW_RANDOM
select RTC_CLASS
select WATCHDOG_CORE
help
@@ -28,6 +29,7 @@ config TURRIS_OMNIA_MCU
- board poweroff into true low power mode (with voltage regulators
disabled) and the ability to configure wake up from this mode (via
rtcwake)
+ - true random number generator (if available on the MCU)
- MCU watchdog
- GPIO pins
- to get front button press events (the front button can be
diff --git a/drivers/platform/cznic/Makefile b/drivers/platform/cznic/Makefile
index 687f7718c0a1..eae4c6b341ff 100644
--- a/drivers/platform/cznic/Makefile
+++ b/drivers/platform/cznic/Makefile
@@ -4,4 +4,5 @@ obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris-omnia-mcu.o
turris-omnia-mcu-y := turris-omnia-mcu-base.o
turris-omnia-mcu-y += turris-omnia-mcu-gpio.o
turris-omnia-mcu-y += turris-omnia-mcu-sys-off-wakeup.o
+turris-omnia-mcu-y += turris-omnia-mcu-trng.o
turris-omnia-mcu-y += turris-omnia-mcu-watchdog.o
diff --git a/drivers/platform/cznic/turris-omnia-mcu-base.c b/drivers/platform/cznic/turris-omnia-mcu-base.c
index 5f88119d825c..7fe4a3df93a6 100644
--- a/drivers/platform/cznic/turris-omnia-mcu-base.c
+++ b/drivers/platform/cznic/turris-omnia-mcu-base.c
@@ -369,7 +369,11 @@ static int omnia_mcu_probe(struct i2c_client *client)
if (err)
return err;

- return omnia_mcu_register_gpiochip(mcu);
+ err = omnia_mcu_register_gpiochip(mcu);
+ if (err)
+ return err;
+
+ return omnia_mcu_register_trng(mcu);
}

static const struct of_device_id of_omnia_mcu_match[] = {
diff --git a/drivers/platform/cznic/turris-omnia-mcu-gpio.c b/drivers/platform/cznic/turris-omnia-mcu-gpio.c
index b3b203f0d2b9..625018ca82cc 100644
--- a/drivers/platform/cznic/turris-omnia-mcu-gpio.c
+++ b/drivers/platform/cznic/turris-omnia-mcu-gpio.c
@@ -162,7 +162,7 @@ static const struct omnia_gpio {
};

/* mapping from interrupts to indexes of GPIOs in the omnia_gpios array */
-static const u8 omnia_int_to_gpio_idx[32] = {
+const u8 omnia_int_to_gpio_idx[32] = {
[__bf_shf(INT_CARD_DET)] = 4,
[__bf_shf(INT_MSATA_IND)] = 5,
[__bf_shf(INT_USB30_OVC)] = 6,
diff --git a/drivers/platform/cznic/turris-omnia-mcu-trng.c b/drivers/platform/cznic/turris-omnia-mcu-trng.c
new file mode 100644
index 000000000000..24fa8cb5d522
--- /dev/null
+++ b/drivers/platform/cznic/turris-omnia-mcu-trng.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * CZ.NIC's Turris Omnia MCU TRNG driver
+ *
+ * 2024 by Marek Behún <[email protected]>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/completion.h>
+#include <linux/devm-helpers.h>
+#include <linux/i2c.h>
+#include <linux/irqdomain.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/turris-omnia-mcu-interface.h>
+#include <linux/types.h>
+
+#include "turris-omnia-mcu.h"
+
+#define CMD_TRNG_MAX_ENTROPY_LEN 64
+
+static irqreturn_t omnia_trng_irq_handler(int irq, void *dev_id)
+{
+ struct omnia_mcu *mcu = dev_id;
+
+ complete(&mcu->trng_completion);
+
+ return IRQ_HANDLED;
+}
+
+static int omnia_trng_read(struct hwrng *rng, void *data, size_t max, bool wait)
+{
+ struct omnia_mcu *mcu = (struct omnia_mcu *)rng->priv;
+ u8 reply[1 + CMD_TRNG_MAX_ENTROPY_LEN];
+ int err, bytes;
+
+ if (!wait && !completion_done(&mcu->trng_completion))
+ return 0;
+
+ do {
+ if (wait_for_completion_interruptible(&mcu->trng_completion))
+ return -EINTR;
+
+ err = omnia_cmd_read(mcu->client, CMD_TRNG_COLLECT_ENTROPY,
+ reply, sizeof(reply));
+ if (err)
+ return err;
+
+ bytes = min3(reply[0], max, CMD_TRNG_MAX_ENTROPY_LEN);
+ } while (wait && !bytes);
+
+ memcpy(data, &reply[1], bytes);
+
+ return bytes;
+}
+
+int omnia_mcu_register_trng(struct omnia_mcu *mcu)
+{
+ struct device *dev = &mcu->client->dev;
+ int irq, err;
+ u8 irq_idx;
+
+ if (!(mcu->features & FEAT_TRNG))
+ return 0;
+
+ irq_idx = omnia_int_to_gpio_idx[__bf_shf(INT_TRNG)];
+ irq = devm_irq_create_mapping(dev, mcu->gc.irq.domain, irq_idx);
+ if (irq < 0)
+ return dev_err_probe(dev, irq, "Cannot map TRNG IRQ\n");
+
+ init_completion(&mcu->trng_completion);
+
+ err = devm_request_threaded_irq(dev, irq, NULL, omnia_trng_irq_handler,
+ IRQF_ONESHOT, "turris-omnia-mcu-trng",
+ mcu);
+ if (err)
+ return dev_err_probe(dev, err, "Cannot request TRNG IRQ\n");
+
+ mcu->trng.name = "turris-omnia-mcu-trng";
+ mcu->trng.read = omnia_trng_read;
+ mcu->trng.priv = (unsigned long)mcu;
+
+ err = devm_hwrng_register(dev, &mcu->trng);
+ if (err)
+ return dev_err_probe(dev, err, "Cannot register TRNG\n");
+
+ return 0;
+}
diff --git a/drivers/platform/cznic/turris-omnia-mcu.h b/drivers/platform/cznic/turris-omnia-mcu.h
index 1838cb3d636e..e0cf10f8c32e 100644
--- a/drivers/platform/cznic/turris-omnia-mcu.h
+++ b/drivers/platform/cznic/turris-omnia-mcu.h
@@ -9,7 +9,9 @@
#define __TURRIS_OMNIA_MCU_H

#include <linux/bitops.h>
+#include <linux/completion.h>
#include <linux/gpio/driver.h>
+#include <linux/hw_random.h>
#include <linux/if_ether.h>
#include <linux/mutex.h>
#include <linux/rtc.h>
@@ -45,6 +47,10 @@ struct omnia_mcu {

/* MCU watchdog */
struct watchdog_device wdt;
+
+ /* true random number generator */
+ struct hwrng trng;
+ struct completion trng_completion;
};

int omnia_cmd_write_read(const struct i2c_client *client,
@@ -147,11 +153,13 @@ static inline int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd)
return err ?: reply;
}

+extern const u8 omnia_int_to_gpio_idx[32];
extern const struct attribute_group omnia_mcu_gpio_group;
extern const struct attribute_group omnia_mcu_poweroff_group;

int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu);
int omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu *mcu);
+int omnia_mcu_register_trng(struct omnia_mcu *mcu);
int omnia_mcu_register_watchdog(struct omnia_mcu *mcu);

#endif /* __TURRIS_OMNIA_MCU_H */
--
2.43.2


2024-04-18 12:12:02

by Marek Behún

[permalink] [raw]
Subject: [PATCH v6 09/11] platform: cznic: turris-omnia-mcu: Add support for digital message signing via debugfs

Add support for digital message signing with private key stored in the
MCU. Boards with MKL MCUs have a NIST256p ECDSA private key created
when manufactured. The private key is not readable from the MCU, but
MCU allows for signing messages with it and retrieving the public key.

As described in a similar commit 50524d787de3 ("firmware:
turris-mox-rwtm: support ECDSA signatures via debugfs"):
The optimal solution would be to register an akcipher provider via
kernel's crypto API, but crypto API does not yet support accessing
akcipher API from userspace (and probably won't for some time, see
https://www.spinics.net/lists/linux-crypto/msg38388.html).

Therefore we add support for accessing this signature generation
mechanism via debugfs for now, so that userspace can access it.

Signed-off-by: Marek Behún <[email protected]>
---
.../ABI/testing/debugfs-turris-omnia-mcu | 13 ++
.../sysfs-bus-i2c-devices-turris-omnia-mcu | 13 ++
MAINTAINERS | 1 +
drivers/platform/cznic/Kconfig | 2 +
drivers/platform/cznic/Makefile | 1 +
.../platform/cznic/turris-omnia-mcu-base.c | 45 +++-
.../platform/cznic/turris-omnia-mcu-debugfs.c | 209 ++++++++++++++++++
drivers/platform/cznic/turris-omnia-mcu.h | 23 ++
8 files changed, 306 insertions(+), 1 deletion(-)
create mode 100644 Documentation/ABI/testing/debugfs-turris-omnia-mcu
create mode 100644 drivers/platform/cznic/turris-omnia-mcu-debugfs.c

diff --git a/Documentation/ABI/testing/debugfs-turris-omnia-mcu b/Documentation/ABI/testing/debugfs-turris-omnia-mcu
new file mode 100644
index 000000000000..1665005c2dcd
--- /dev/null
+++ b/Documentation/ABI/testing/debugfs-turris-omnia-mcu
@@ -0,0 +1,13 @@
+What: /sys/kernel/debug/turris-omnia-mcu/do_sign
+Date: July 2024
+KernelVersion: 6.10
+Contact: Marek Behún <[email protected]>
+Description:
+
+ ======= ===========================================================
+ (Write) Message to sign with the ECDSA private key stored in MCU.
+ The message must be exactly 32 bytes long (since this is
+ intended to be a SHA-256 hash).
+ (Read) The resulting signature, 64 bytes. This contains the R and
+ S values of the ECDSA signature, both in big-endian format.
+ ======= ===========================================================
diff --git a/Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu b/Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu
index 564119849388..b239224cf9bc 100644
--- a/Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu
+++ b/Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu
@@ -90,6 +90,19 @@ Description: (RO) Contains the microcontroller type (STM32, GD32, MKL).

Format: %s.

+What: /sys/bus/i2c/devices/<mcu_device>/public_key
+Date: July 2024
+KernelVersion: 6.10
+Contact: Marek Behún <[email protected]>
+Description: (RO) Contains board ECDSA public key.
+
+ Only available if MCU supports signing messages with the ECDSA
+ algorithm. If so, the board has a private key stored in the MCU
+ that was generated during manufacture and cannot be retrieved
+ from the MCU.
+
+ Format: %s.
+
What: /sys/bus/i2c/devices/<mcu_device>/reset_selector
Date: July 2024
KernelVersion: 6.10
diff --git a/MAINTAINERS b/MAINTAINERS
index b37f7ca81785..d21084ce32ba 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2139,6 +2139,7 @@ M: Marek Behún <[email protected]>
S: Maintained
W: https://www.turris.cz/
F: Documentation/ABI/testing/debugfs-moxtet
+F: Documentation/ABI/testing/debugfs-turris-omnia-mcu
F: Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu
F: Documentation/ABI/testing/sysfs-bus-moxtet-devices
F: Documentation/ABI/testing/sysfs-firmware-turris-mox-rwtm
diff --git a/drivers/platform/cznic/Kconfig b/drivers/platform/cznic/Kconfig
index 750d5f47dba8..2d45495c0a2d 100644
--- a/drivers/platform/cznic/Kconfig
+++ b/drivers/platform/cznic/Kconfig
@@ -30,6 +30,8 @@ config TURRIS_OMNIA_MCU
disabled) and the ability to configure wake up from this mode (via
rtcwake)
- true random number generator (if available on the MCU)
+ - ECDSA message signing with board private key (if available on the
+ MCU)
- MCU watchdog
- GPIO pins
- to get front button press events (the front button can be
diff --git a/drivers/platform/cznic/Makefile b/drivers/platform/cznic/Makefile
index eae4c6b341ff..af9213928404 100644
--- a/drivers/platform/cznic/Makefile
+++ b/drivers/platform/cznic/Makefile
@@ -6,3 +6,4 @@ turris-omnia-mcu-y += turris-omnia-mcu-gpio.o
turris-omnia-mcu-y += turris-omnia-mcu-sys-off-wakeup.o
turris-omnia-mcu-y += turris-omnia-mcu-trng.o
turris-omnia-mcu-y += turris-omnia-mcu-watchdog.o
+turris-omnia-mcu-$(CONFIG_DEBUG_FS) += turris-omnia-mcu-debugfs.o
diff --git a/drivers/platform/cznic/turris-omnia-mcu-base.c b/drivers/platform/cznic/turris-omnia-mcu-base.c
index 7fe4a3df93a6..ec08443551a7 100644
--- a/drivers/platform/cznic/turris-omnia-mcu-base.c
+++ b/drivers/platform/cznic/turris-omnia-mcu-base.c
@@ -159,6 +159,16 @@ static ssize_t board_revision_show(struct device *dev,
}
static DEVICE_ATTR_RO(board_revision);

+static ssize_t public_key_show(struct device *dev, struct device_attribute *a,
+ char *buf)
+{
+ struct omnia_mcu *mcu = i2c_get_clientdata(to_i2c_client(dev));
+
+ return sysfs_emit(buf, "%*phN\n", OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN,
+ mcu->board_public_key);
+}
+static DEVICE_ATTR_RO(public_key);
+
static struct attribute *omnia_mcu_base_attrs[] = {
&dev_attr_fw_version_hash_application.attr,
&dev_attr_fw_version_hash_bootloader.attr,
@@ -168,6 +178,7 @@ static struct attribute *omnia_mcu_base_attrs[] = {
&dev_attr_serial_number.attr,
&dev_attr_first_mac_address.attr,
&dev_attr_board_revision.attr,
+ &dev_attr_public_key.attr,
NULL
};

@@ -183,6 +194,9 @@ static umode_t omnia_mcu_base_attrs_visible(struct kobject *kobj,
a == &dev_attr_board_revision.attr) &&
!(mcu->features & FEAT_BOARD_INFO))
mode = 0;
+ else if (a == &dev_attr_public_key.attr &&
+ !(mcu->features & FEAT_CRYPTO))
+ mode = 0;

return mode;
}
@@ -333,6 +347,24 @@ static int omnia_mcu_read_board_info(struct omnia_mcu *mcu)
return 0;
}

+static int omnia_mcu_read_public_key(struct omnia_mcu *mcu)
+{
+ u8 reply[1 + OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN];
+ int err;
+
+ err = omnia_cmd_read(mcu->client, CMD_CRYPTO_GET_PUBLIC_KEY, reply,
+ sizeof(reply));
+ if (err)
+ return err;
+
+ if (reply[0] != OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN)
+ return -EIO;
+
+ memcpy(mcu->board_public_key, &reply[1], sizeof(mcu->board_public_key));
+
+ return 0;
+}
+
static int omnia_mcu_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
@@ -361,6 +393,13 @@ static int omnia_mcu_probe(struct i2c_client *client)
"Cannot read board info\n");
}

+ if (mcu->features & FEAT_CRYPTO) {
+ err = omnia_mcu_read_public_key(mcu);
+ if (err)
+ return dev_err_probe(dev, err,
+ "Cannot read board public key\n");
+ }
+
err = omnia_mcu_register_sys_off_and_wakeup(mcu);
if (err)
return err;
@@ -373,7 +412,11 @@ static int omnia_mcu_probe(struct i2c_client *client)
if (err)
return err;

- return omnia_mcu_register_trng(mcu);
+ err = omnia_mcu_register_trng(mcu);
+ if (err)
+ return err;
+
+ return omnia_mcu_register_debugfs(mcu);
}

static const struct of_device_id of_omnia_mcu_match[] = {
diff --git a/drivers/platform/cznic/turris-omnia-mcu-debugfs.c b/drivers/platform/cznic/turris-omnia-mcu-debugfs.c
new file mode 100644
index 000000000000..484664f8d75c
--- /dev/null
+++ b/drivers/platform/cznic/turris-omnia-mcu-debugfs.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * CZ.NIC's Turris Omnia MCU ECDSA message signing via debugfs
+ *
+ * 2024 by Marek Behún <[email protected]>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/completion.h>
+#include <linux/debugfs.h>
+#include <linux/devm-helpers.h>
+#include <linux/i2c.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/turris-omnia-mcu-interface.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+
+#include "turris-omnia-mcu.h"
+
+#define CMD_CRYPTO_SIGN_MESSAGE_LEN 32
+
+enum {
+ SIGN_STATE_CLOSED = 0,
+ SIGN_STATE_OPEN = 1,
+ SIGN_STATE_REQUESTED = 2,
+ SIGN_STATE_COLLECTED = 3,
+};
+
+static irqreturn_t omnia_msg_signed_irq_handler(int irq, void *dev_id)
+{
+ u8 reply[1 + OMNIA_MCU_CRYPTO_SIGNATURE_LEN];
+ struct omnia_mcu *mcu = dev_id;
+ int err;
+
+ err = omnia_cmd_read(mcu->client, CMD_CRYPTO_COLLECT_SIGNATURE, reply,
+ sizeof(reply));
+ if (!err && reply[0] != OMNIA_MCU_CRYPTO_SIGNATURE_LEN)
+ err = -EIO;
+
+ guard(mutex)(&mcu->sign_lock);
+
+ if (mcu->sign_state == SIGN_STATE_REQUESTED) {
+ mcu->sign_err = err;
+ if (!err)
+ memcpy(mcu->signature, &reply[1],
+ OMNIA_MCU_CRYPTO_SIGNATURE_LEN);
+ mcu->sign_state = SIGN_STATE_COLLECTED;
+ complete(&mcu->msg_signed_completion);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int do_sign_open(struct inode *inode, struct file *file)
+{
+ struct omnia_mcu *mcu = inode->i_private;
+
+ guard(mutex)(&mcu->sign_lock);
+
+ /* do_sign is allowed to be opened only once */
+ if (mcu->sign_state != SIGN_STATE_CLOSED)
+ return -EBUSY;
+
+ mcu->sign_state = SIGN_STATE_OPEN;
+
+ file->private_data = mcu;
+
+ return nonseekable_open(inode, file);
+}
+
+static int do_sign_release(struct inode *inode, struct file *file)
+{
+ struct omnia_mcu *mcu = file->private_data;
+
+ guard(mutex)(&mcu->sign_lock);
+
+ mcu->sign_state = SIGN_STATE_CLOSED;
+
+ /* forget signature on release even if it was not read, for security */
+ memzero_explicit(mcu->signature, sizeof(mcu->signature));
+
+ return 0;
+}
+
+static ssize_t do_sign_read(struct file *file, char __user *buf, size_t len,
+ loff_t *ppos)
+{
+ struct omnia_mcu *mcu = file->private_data;
+
+ /* only allow read of one whole signature */
+ if (len != sizeof(mcu->signature))
+ return -EINVAL;
+
+ scoped_guard(mutex, &mcu->sign_lock)
+ if (mcu->sign_state != SIGN_STATE_REQUESTED &&
+ mcu->sign_state != SIGN_STATE_COLLECTED)
+ return -ENODATA;
+
+ if (wait_for_completion_interruptible(&mcu->msg_signed_completion))
+ return -EINTR;
+
+ guard(mutex)(&mcu->sign_lock);
+
+ mcu->sign_state = SIGN_STATE_OPEN;
+
+ if (mcu->sign_err)
+ return mcu->sign_err;
+
+ if (copy_to_user(buf, mcu->signature, len))
+ return -EFAULT;
+
+ /* on read forget the signature, for security */
+ memzero_explicit(mcu->signature, sizeof(mcu->signature));
+
+ return len;
+}
+
+static ssize_t do_sign_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ u8 cmd[1 + CMD_CRYPTO_SIGN_MESSAGE_LEN], reply;
+ struct omnia_mcu *mcu = file->private_data;
+ int err;
+
+ /*
+ * the input is a SHA-256 hash of a message to sign, so exactly
+ * 32 bytes have to be read
+ */
+ if (len != CMD_CRYPTO_SIGN_MESSAGE_LEN)
+ return -EINVAL;
+
+ cmd[0] = CMD_CRYPTO_SIGN_MESSAGE;
+
+ if (copy_from_user(&cmd[1], buf, len))
+ return -EFAULT;
+
+ guard(mutex)(&mcu->sign_lock);
+
+ if (mcu->sign_state != SIGN_STATE_OPEN)
+ return -EBUSY;
+
+ err = omnia_cmd_write_read(mcu->client, cmd, sizeof(cmd), &reply, 1);
+ if (err)
+ return err;
+
+ if (reply)
+ mcu->sign_state = SIGN_STATE_REQUESTED;
+
+ return reply ? len : -EBUSY;
+}
+
+static const struct file_operations do_sign_fops = {
+ .owner = THIS_MODULE,
+ .open = do_sign_open,
+ .read = do_sign_read,
+ .write = do_sign_write,
+ .release = do_sign_release,
+ .llseek = no_llseek,
+};
+
+int omnia_mcu_register_debugfs(struct omnia_mcu *mcu)
+{
+ struct device *dev = &mcu->client->dev;
+ struct dentry *root, *entry;
+ int irq, err;
+ u8 irq_idx;
+
+ if (!(mcu->features & FEAT_CRYPTO))
+ return 0;
+
+ irq_idx = omnia_int_to_gpio_idx[__bf_shf(INT_MESSAGE_SIGNED)];
+ irq = devm_irq_create_mapping(dev, mcu->gc.irq.domain, irq_idx);
+ if (irq < 0)
+ return dev_err_probe(dev, irq,
+ "Cannot map MESSAGE_SIGNED IRQ\n");
+
+ err = devm_mutex_init(dev, &mcu->sign_lock);
+ if (err)
+ return err;
+
+ mcu->sign_state = 0;
+
+ init_completion(&mcu->msg_signed_completion);
+
+ err = devm_request_threaded_irq(dev, irq, NULL,
+ omnia_msg_signed_irq_handler,
+ IRQF_ONESHOT,
+ "turris-omnia-mcu-debugfs", mcu);
+ if (err)
+ return dev_err_probe(dev, err,
+ "Cannot request MESSAGE_SIGNED IRQ\n");
+
+ root = devm_debugfs_create_dir(dev, "turris-omnia-mcu", NULL);
+ if (IS_ERR(root))
+ return dev_err_probe(dev, PTR_ERR(root),
+ "Cannot create debugfs dir\n");
+
+
+ entry = debugfs_create_file_unsafe("do_sign", 0600, root, mcu,
+ &do_sign_fops);
+ if (IS_ERR(entry))
+ return dev_err_probe(dev, PTR_ERR(entry),
+ "Cannot create debugfs entry\n");
+
+ return 0;
+}
diff --git a/drivers/platform/cznic/turris-omnia-mcu.h b/drivers/platform/cznic/turris-omnia-mcu.h
index e0cf10f8c32e..5ce639d5ec67 100644
--- a/drivers/platform/cznic/turris-omnia-mcu.h
+++ b/drivers/platform/cznic/turris-omnia-mcu.h
@@ -21,6 +21,9 @@
#include <asm/byteorder.h>
#include <asm/unaligned.h>

+#define OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN 33
+#define OMNIA_MCU_CRYPTO_SIGNATURE_LEN 64
+
struct omnia_mcu {
struct i2c_client *client;
const char *type;
@@ -30,6 +33,7 @@ struct omnia_mcu {
u64 board_serial_number;
u8 board_first_mac[ETH_ALEN];
u8 board_revision;
+ u8 board_public_key[OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN];

/* GPIO chip */
struct gpio_chip gc;
@@ -51,6 +55,16 @@ struct omnia_mcu {
/* true random number generator */
struct hwrng trng;
struct completion trng_completion;
+
+#ifdef CONFIG_DEBUG_FS
+ /* MCU ECDSA message signing via debugfs */
+ struct dentry *debugfs_root;
+ struct completion msg_signed_completion;
+ struct mutex sign_lock;
+ unsigned int sign_state;
+ u8 signature[OMNIA_MCU_CRYPTO_SIGNATURE_LEN];
+ int sign_err;
+#endif
};

int omnia_cmd_write_read(const struct i2c_client *client,
@@ -162,4 +176,13 @@ int omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu *mcu);
int omnia_mcu_register_trng(struct omnia_mcu *mcu);
int omnia_mcu_register_watchdog(struct omnia_mcu *mcu);

+#ifdef CONFIG_DEBUG_FS
+int omnia_mcu_register_debugfs(struct omnia_mcu *mcu);
+#else
+static inline int omnia_mcu_register_debugfs(struct omnia_mcu *mcu)
+{
+ return 0;
+}
+#endif
+
#endif /* __TURRIS_OMNIA_MCU_H */
--
2.43.2


2024-04-23 16:06:12

by Andy Shevchenko

[permalink] [raw]
Subject: Re: [PATCH v6 07/11] platform: cznic: turris-omnia-mcu: Add support for MCU provided TRNG

On Thu, Apr 18, 2024 at 02:11:12PM +0200, Marek Beh?n wrote:
> Add support for true random number generator provided by the MCU.
> New Omnia boards come without the Atmel SHA204-A chip. Instead the
> crypto functionality is provided by new microcontroller, which has
> a TRNG peripheral.

...

> +int omnia_mcu_register_trng(struct omnia_mcu *mcu)
> +{
> + struct device *dev = &mcu->client->dev;
> + int irq, err;
> + u8 irq_idx;
> +
> + if (!(mcu->features & FEAT_TRNG))
> + return 0;

> + irq_idx = omnia_int_to_gpio_idx[__bf_shf(INT_TRNG)];
> + irq = devm_irq_create_mapping(dev, mcu->gc.irq.domain, irq_idx);
> + if (irq < 0)
> + return dev_err_probe(dev, irq, "Cannot map TRNG IRQ\n");

This looks like some workaround against existing gpiod_to_irq(). Why do you
need this?

> + init_completion(&mcu->trng_completion);
> +
> + err = devm_request_threaded_irq(dev, irq, NULL, omnia_trng_irq_handler,
> + IRQF_ONESHOT, "turris-omnia-mcu-trng",
> + mcu);
> + if (err)
> + return dev_err_probe(dev, err, "Cannot request TRNG IRQ\n");
> +
> + mcu->trng.name = "turris-omnia-mcu-trng";
> + mcu->trng.read = omnia_trng_read;
> + mcu->trng.priv = (unsigned long)mcu;
> +
> + err = devm_hwrng_register(dev, &mcu->trng);
> + if (err)
> + return dev_err_probe(dev, err, "Cannot register TRNG\n");
> +
> + return 0;
> +}

--
With Best Regards,
Andy Shevchenko



2024-04-23 16:06:13

by Andy Shevchenko

[permalink] [raw]
Subject: Re: [PATCH v6 00/11] Turris Omnia MCU driver

On Thu, Apr 18, 2024 at 02:11:05PM +0200, Marek Beh?n wrote:
> Hello Andy, Dan, Linus, Arnd, Gregory, and others,
>
> I am sending v6 of the series adding Turris Omnia MCU driver.
>
> This series depends on the immutable branch between LEDs and locking,
> introducing devm_mutex_init(), see the PR
> https://lore.kernel.org/linux-leds/[email protected]/

...

> devm-helpers: Add resource managed version of irq_create_mapping()
> devm-helpers: Add resource managed version of debugfs directory create
> function

IIUC you created them as static inline, the header will become yet another
cumbersome and messy "kernel.h". Can we prevent that?

--
With Best Regards,
Andy Shevchenko



2024-04-23 16:43:02

by Marek Behún

[permalink] [raw]
Subject: Re: [PATCH v6 07/11] platform: cznic: turris-omnia-mcu: Add support for MCU provided TRNG

On Tue, 23 Apr 2024 18:58:19 +0300
Andy Shevchenko <[email protected]> wrote:

> On Thu, Apr 18, 2024 at 02:11:12PM +0200, Marek Behún wrote:
> > Add support for true random number generator provided by the MCU.
> > New Omnia boards come without the Atmel SHA204-A chip. Instead the
> > crypto functionality is provided by new microcontroller, which has
> > a TRNG peripheral.
>
> ...
>
> > +int omnia_mcu_register_trng(struct omnia_mcu *mcu)
> > +{
> > + struct device *dev = &mcu->client->dev;
> > + int irq, err;
> > + u8 irq_idx;
> > +
> > + if (!(mcu->features & FEAT_TRNG))
> > + return 0;
>
> > + irq_idx = omnia_int_to_gpio_idx[__bf_shf(INT_TRNG)];
> > + irq = devm_irq_create_mapping(dev, mcu->gc.irq.domain, irq_idx);
> > + if (irq < 0)
> > + return dev_err_probe(dev, irq, "Cannot map TRNG IRQ\n");
>
> This looks like some workaround against existing gpiod_to_irq(). Why do you
> need this?

Hmmm, I thought that would not work because that line is only valid
as an IRQ, not as a GPIO (this is enforced via the valid_mask member of
gpio_chip and gpio_irq_chip).

But looking at the code of gpiolib, if I do
irq = gpiod_to_irq(gpiochip_get_desc(gc, irq_idx));
the valid_mask is not enforced anywhere.

Is this semantically right to do even in spite of the fact that the
line is not a valid GPIO line?

Marek

2024-04-23 16:44:32

by Andy Shevchenko

[permalink] [raw]
Subject: Re: [PATCH v6 07/11] platform: cznic: turris-omnia-mcu: Add support for MCU provided TRNG

On Tue, Apr 23, 2024 at 7:32 PM Marek Behún <[email protected]> wrote:
> On Tue, 23 Apr 2024 18:58:19 +0300
> Andy Shevchenko <[email protected]> wrote:
> > On Thu, Apr 18, 2024 at 02:11:12PM +0200, Marek Behún wrote:

...

> > > + irq_idx = omnia_int_to_gpio_idx[__bf_shf(INT_TRNG)];
> > > + irq = devm_irq_create_mapping(dev, mcu->gc.irq.domain, irq_idx);
> > > + if (irq < 0)
> > > + return dev_err_probe(dev, irq, "Cannot map TRNG IRQ\n");
> >
> > This looks like some workaround against existing gpiod_to_irq(). Why do you
> > need this?
>
> Hmmm, I thought that would not work because that line is only valid
> as an IRQ, not as a GPIO (this is enforced via the valid_mask member of
> gpio_chip and gpio_irq_chip).
>
> But looking at the code of gpiolib, if I do
> irq = gpiod_to_irq(gpiochip_get_desc(gc, irq_idx));
> the valid_mask is not enforced anywhere.

Which one? GPIO has two: one per GPIO realm and one for IRQ domain.

> Is this semantically right to do even in spite of the fact that the
> line is not a valid GPIO line?

Yes. It's orthogonal to that.

--
With Best Regards,
Andy Shevchenko

2024-04-23 16:59:50

by Marek Behún

[permalink] [raw]
Subject: Re: [PATCH v6 07/11] platform: cznic: turris-omnia-mcu: Add support for MCU provided TRNG

On Tue, 23 Apr 2024 19:43:41 +0300
Andy Shevchenko <[email protected]> wrote:

> On Tue, Apr 23, 2024 at 7:32 PM Marek Behún <[email protected]> wrote:
> > On Tue, 23 Apr 2024 18:58:19 +0300
> > Andy Shevchenko <[email protected]> wrote:
> > > On Thu, Apr 18, 2024 at 02:11:12PM +0200, Marek Behún wrote:
>
> ...
>
> > > > + irq_idx = omnia_int_to_gpio_idx[__bf_shf(INT_TRNG)];
> > > > + irq = devm_irq_create_mapping(dev, mcu->gc.irq.domain, irq_idx);
> > > > + if (irq < 0)
> > > > + return dev_err_probe(dev, irq, "Cannot map TRNG IRQ\n");
> > >
> > > This looks like some workaround against existing gpiod_to_irq(). Why do you
> > > need this?
> >
> > Hmmm, I thought that would not work because that line is only valid
> > as an IRQ, not as a GPIO (this is enforced via the valid_mask member of
> > gpio_chip and gpio_irq_chip).
> >
> > But looking at the code of gpiolib, if I do
> > irq = gpiod_to_irq(gpiochip_get_desc(gc, irq_idx));
> > the valid_mask is not enforced anywhere.
>
> Which one? GPIO has two: one per GPIO realm and one for IRQ domain.

The GPIO line validity is not enforced. The IRQ line validity is
enforced in the gpiochip_to_irq() method.

> > Is this semantically right to do even in spite of the fact that the
> > line is not a valid GPIO line?
>
> Yes. It's orthogonal to that.
>


2024-04-23 17:29:40

by Andy Shevchenko

[permalink] [raw]
Subject: Re: [PATCH v6 07/11] platform: cznic: turris-omnia-mcu: Add support for MCU provided TRNG

On Tue, Apr 23, 2024 at 06:57:04PM +0200, Marek Behún wrote:
> On Tue, 23 Apr 2024 19:43:41 +0300
> Andy Shevchenko <[email protected]> wrote:
> > On Tue, Apr 23, 2024 at 7:32 PM Marek Behún <[email protected]> wrote:
> > > On Tue, 23 Apr 2024 18:58:19 +0300
> > > Andy Shevchenko <[email protected]> wrote:
> > > > On Thu, Apr 18, 2024 at 02:11:12PM +0200, Marek Behún wrote:

...

> > > > > + irq_idx = omnia_int_to_gpio_idx[__bf_shf(INT_TRNG)];
> > > > > + irq = devm_irq_create_mapping(dev, mcu->gc.irq.domain, irq_idx);
> > > > > + if (irq < 0)
> > > > > + return dev_err_probe(dev, irq, "Cannot map TRNG IRQ\n");
> > > >
> > > > This looks like some workaround against existing gpiod_to_irq(). Why do you
> > > > need this?
> > >
> > > Hmmm, I thought that would not work because that line is only valid
> > > as an IRQ, not as a GPIO (this is enforced via the valid_mask member of
> > > gpio_chip and gpio_irq_chip).
> > >
> > > But looking at the code of gpiolib, if I do
> > > irq = gpiod_to_irq(gpiochip_get_desc(gc, irq_idx));
> > > the valid_mask is not enforced anywhere.
> >
> > Which one? GPIO has two: one per GPIO realm and one for IRQ domain.
>
> The GPIO line validity is not enforced. The IRQ line validity is
> enforced in the gpiochip_to_irq() method.

Okay, but does it work for you as expected then?

If not, we should fix GPIO library to have gpiod_to_irq() to work as expected.

> > > Is this semantically right to do even in spite of the fact that the
> > > line is not a valid GPIO line?
> >
> > Yes. It's orthogonal to that.

--
With Best Regards,
Andy Shevchenko



2024-04-24 16:56:12

by Marek Behún

[permalink] [raw]
Subject: Re: [PATCH v6 07/11] platform: cznic: turris-omnia-mcu: Add support for MCU provided TRNG

On Tue, 23 Apr 2024 20:25:14 +0300
Andy Shevchenko <[email protected]> wrote:

> On Tue, Apr 23, 2024 at 06:57:04PM +0200, Marek Behún wrote:
> > On Tue, 23 Apr 2024 19:43:41 +0300
> > Andy Shevchenko <[email protected]> wrote:
> > > On Tue, Apr 23, 2024 at 7:32 PM Marek Behún <[email protected]> wrote:
> > > > On Tue, 23 Apr 2024 18:58:19 +0300
> > > > Andy Shevchenko <[email protected]> wrote:
> > > > > On Thu, Apr 18, 2024 at 02:11:12PM +0200, Marek Behún wrote:
>
> ...
>
> > > > > > + irq_idx = omnia_int_to_gpio_idx[__bf_shf(INT_TRNG)];
> > > > > > + irq = devm_irq_create_mapping(dev, mcu->gc.irq.domain, irq_idx);
> > > > > > + if (irq < 0)
> > > > > > + return dev_err_probe(dev, irq, "Cannot map TRNG IRQ\n");
> > > > >
> > > > > This looks like some workaround against existing gpiod_to_irq(). Why do you
> > > > > need this?
> > > >
> > > > Hmmm, I thought that would not work because that line is only valid
> > > > as an IRQ, not as a GPIO (this is enforced via the valid_mask member of
> > > > gpio_chip and gpio_irq_chip).
> > > >
> > > > But looking at the code of gpiolib, if I do
> > > > irq = gpiod_to_irq(gpiochip_get_desc(gc, irq_idx));
> > > > the valid_mask is not enforced anywhere.
> > >
> > > Which one? GPIO has two: one per GPIO realm and one for IRQ domain.
> >
> > The GPIO line validity is not enforced. The IRQ line validity is
> > enforced in the gpiochip_to_irq() method.
>
> Okay, but does it work for you as expected then?
>
> If not, we should fix GPIO library to have gpiod_to_irq() to work as expected.

Yes, it does. I am going to send a new version in a few minutes.

Marek