diff mbox series

[4/4] drivers: rng: add smccc trng driver

Message ID 20220601082734.301474-4-etienne.carriere@linaro.org
State Accepted
Commit 53355bb86c25d9cced1493df9fc95140acece556
Headers show
Series [1/4] smccc: define generic IDs for feature discovery | expand

Commit Message

Etienne Carriere June 1, 2022, 8:27 a.m. UTC
Adds random number generator driver using Arm SMCCC TRNG interface to
get entropy bytes from secure monitor. The driver registers as an
Arm SMCCC feature driver to allow PSCI driver to bind a device for
when secure monitor exposes RNG support from Arm SMCCC TRNG interface.

Cc: Sughosh Ganu <sughosh.ganu@linaro.org>
Cc: Heinrich Schuchardt <xypron.glpk@gmx.de>
Signed-off-by: Etienne Carriere <etienne.carriere@linaro.org>
---
 MAINTAINERS              |   5 +
 drivers/rng/Kconfig      |   9 ++
 drivers/rng/Makefile     |   1 +
 drivers/rng/smccc_trng.c | 207 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 222 insertions(+)
 create mode 100644 drivers/rng/smccc_trng.c

Comments

Tom Rini June 23, 2022, 6:32 p.m. UTC | #1
On Wed, Jun 01, 2022 at 10:27:34AM +0200, Etienne Carriere wrote:

> Adds random number generator driver using Arm SMCCC TRNG interface to
> get entropy bytes from secure monitor. The driver registers as an
> Arm SMCCC feature driver to allow PSCI driver to bind a device for
> when secure monitor exposes RNG support from Arm SMCCC TRNG interface.
> 
> Cc: Sughosh Ganu <sughosh.ganu@linaro.org>
> Cc: Heinrich Schuchardt <xypron.glpk@gmx.de>
> Signed-off-by: Etienne Carriere <etienne.carriere@linaro.org>

Applied to u-boot/next, thanks!
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 56be0bfad0..5a92b8bfcb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1231,6 +1231,11 @@  F:	drivers/gpio/sl28cpld-gpio.c
 F:	drivers/misc/sl28cpld.c
 F:	drivers/watchdog/sl28cpld-wdt.c
 
+SMCCC TRNG
+M:	Etienne Carriere <etienne.carriere@linaro.org>
+S:	Maintained
+F:	drivers/rng/smccc_trng.c
+
 SPI
 M:	Jagan Teki <jagan@amarulasolutions.com>
 S:	Maintained
diff --git a/drivers/rng/Kconfig b/drivers/rng/Kconfig
index c10f7d345b..6f73be8f9b 100644
--- a/drivers/rng/Kconfig
+++ b/drivers/rng/Kconfig
@@ -58,4 +58,13 @@  config RNG_IPROC200
 	depends on DM_RNG
 	help
 	  Enable random number generator for RPI4.
+
+config RNG_SMCCC_TRNG
+	bool "Arm SMCCC TRNG interface"
+	depends on DM_RNG && ARM_PSCI_FW
+	default y if ARM_SMCCC_FEATURES
+	help
+	  Enable random number generator for platforms that support Arm
+	  SMCCC TRNG interface.
+
 endif
diff --git a/drivers/rng/Makefile b/drivers/rng/Makefile
index 435b3b965a..20c40a964c 100644
--- a/drivers/rng/Makefile
+++ b/drivers/rng/Makefile
@@ -11,3 +11,4 @@  obj-$(CONFIG_RNG_OPTEE) += optee_rng.o
 obj-$(CONFIG_RNG_STM32MP1) += stm32mp1_rng.o
 obj-$(CONFIG_RNG_ROCKCHIP) += rockchip_rng.o
 obj-$(CONFIG_RNG_IPROC200) += iproc_rng200.o
+obj-$(CONFIG_RNG_SMCCC_TRNG) += smccc_trng.o
diff --git a/drivers/rng/smccc_trng.c b/drivers/rng/smccc_trng.c
new file mode 100644
index 0000000000..3a4bb33941
--- /dev/null
+++ b/drivers/rng/smccc_trng.c
@@ -0,0 +1,207 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022, Linaro Limited
+ */
+
+#define LOG_CATEGORY UCLASS_RNG
+
+#include <common.h>
+#include <dm.h>
+#include <linker_lists.h>
+#include <log.h>
+#include <rng.h>
+#include <dm/device_compat.h>
+#include <linux/arm-smccc.h>
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/psci.h>
+
+#define DRIVER_NAME	"smccc-trng"
+
+/**
+ * Arm SMCCC TRNG firmware interface specification:
+ * https://developer.arm.com/documentation/den0098/latest/
+ */
+#define ARM_SMCCC_TRNG_VERSION		0x84000050
+#define ARM_SMCCC_TRNG_FEATURES		0x84000051
+#define ARM_SMCCC_TRNG_GET_UUID		0x84000052
+#define ARM_SMCCC_TRNG_RND_32		0x84000053
+#define ARM_SMCCC_TRNG_RND_64		0xC4000053
+
+#define ARM_SMCCC_RET_TRNG_SUCCESS		((ulong)0)
+#define ARM_SMCCC_RET_TRNG_NOT_SUPPORTED	((ulong)-1)
+#define ARM_SMCCC_RET_TRNG_INVALID_PARAMETER	((ulong)-2)
+#define ARM_SMCCC_RET_TRNG_NO_ENTROPY		((ulong)-3)
+
+#define TRNG_MAJOR_MASK		GENMASK(30, 16)
+#define TRNG_MAJOR_SHIFT	16
+#define TRNG_MINOR_MASK		GENMASK(15, 0)
+#define TRNG_MINOR_SHIFT	0
+
+#define TRNG_MAX_RND_64		(192 / 8)
+#define TRNG_MAX_RND_32		(96 / 8)
+
+/**
+ * struct smccc_trng_priv - Private data for SMCCC TRNG support
+ *
+ * @smc64 - True if TRNG_RND_64 is supported, false if TRNG_RND_32 is supported
+ */
+struct smccc_trng_priv {
+	bool smc64;
+};
+
+/*
+ * Copy random bytes from ulong SMCCC output register to target buffer
+ * Defines 2 function flavors for whether ARM_SMCCC_TRNG_RND_32 or
+ * ARM_SMCCC_TRNG_RND_64 was used to invoke the service.
+ */
+static size_t smc32_copy_sample(u8 **ptr, size_t size, ulong *rnd)
+{
+	size_t len = min(size, sizeof(u32));
+	u32 sample = *rnd;
+
+	memcpy(*ptr, &sample, len);
+	*ptr += len;
+
+	return size - len;
+}
+
+static size_t smc64_copy_sample(u8 **ptr, size_t size, ulong *rnd)
+{
+	size_t len = min(size, sizeof(u64));
+	u64 sample = *rnd;
+
+	memcpy(*ptr, &sample, len);
+	*ptr += len;
+
+	return size - len;
+}
+
+static int smccc_trng_read(struct udevice *dev, void *data, size_t len)
+{
+	struct psci_plat_data *smccc = dev_get_parent_plat(dev);
+	struct smccc_trng_priv *priv = dev_get_priv(dev);
+	struct arm_smccc_res res;
+	u32 func_id;
+	u8 *ptr = data;
+	size_t rem = len;
+	size_t max_sz;
+	size_t (*copy_sample)(u8 **ptr, size_t size, ulong *rnd);
+
+	if (priv->smc64) {
+		copy_sample = smc64_copy_sample;
+		func_id = ARM_SMCCC_TRNG_RND_64;
+		max_sz = TRNG_MAX_RND_64;
+	} else {
+		copy_sample = smc32_copy_sample;
+		func_id = ARM_SMCCC_TRNG_RND_32;
+		max_sz = TRNG_MAX_RND_32;
+	}
+
+	while (rem) {
+		size_t sz = min(rem, max_sz);
+
+		smccc->invoke_fn(func_id, sz * 8, 0, 0, 0, 0, 0, 0, &res);
+
+		switch (res.a0) {
+		case ARM_SMCCC_RET_TRNG_SUCCESS:
+			break;
+		case ARM_SMCCC_RET_TRNG_NO_ENTROPY:
+			continue;
+		default:
+			return -EIO;
+		}
+
+		rem -= sz;
+
+		sz = copy_sample(&ptr, sz, &res.a3);
+		if (sz)
+			sz = copy_sample(&ptr, sz, &res.a2);
+		if (sz)
+			sz = copy_sample(&ptr, sz, &res.a1);
+	}
+
+	return 0;
+}
+
+static const struct dm_rng_ops smccc_trng_ops = {
+	.read = smccc_trng_read,
+};
+
+static bool smccc_trng_is_supported(void (*invoke_fn)(unsigned long a0, unsigned long a1,
+						      unsigned long a2, unsigned long a3,
+						      unsigned long a4, unsigned long a5,
+						      unsigned long a6, unsigned long a7,
+						      struct arm_smccc_res *res))
+{
+	struct arm_smccc_res res;
+
+	(*invoke_fn)(ARM_SMCCC_ARCH_FEATURES, ARM_SMCCC_TRNG_VERSION, 0, 0, 0, 0, 0, 0, &res);
+	if (res.a0 == ARM_SMCCC_RET_NOT_SUPPORTED)
+		return false;
+
+	(*invoke_fn)(ARM_SMCCC_TRNG_VERSION, 0, 0, 0, 0, 0, 0, 0, &res);
+	if (res.a0 & BIT(31))
+		return false;
+
+	/* Test 64bit interface and fallback to 32bit interface */
+	invoke_fn(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_TRNG_RND_64,
+		  0, 0, 0, 0, 0, 0, &res);
+
+	if (res.a0 == ARM_SMCCC_RET_TRNG_NOT_SUPPORTED)
+		invoke_fn(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_TRNG_RND_32,
+			  0, 0, 0, 0, 0, 0, &res);
+
+	return res.a0 == ARM_SMCCC_RET_TRNG_SUCCESS;
+}
+
+ARM_SMCCC_FEATURE_DRIVER(smccc_trng) = {
+	.driver_name = DRIVER_NAME,
+	.is_supported = smccc_trng_is_supported,
+};
+
+static int smccc_trng_probe(struct udevice *dev)
+{
+	struct psci_plat_data *smccc = dev_get_parent_plat(dev);
+	struct smccc_trng_priv *priv = dev_get_priv(dev);
+	struct arm_smccc_res res;
+
+	if (!(smccc_trng_is_supported(smccc->invoke_fn)))
+		return -ENODEV;
+
+	/* At least one of 64bit and 32bit interfaces is available */
+	smccc->invoke_fn(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_TRNG_RND_64,
+			 0, 0, 0, 0, 0, 0, &res);
+	priv->smc64 = (res.a0 == ARM_SMCCC_RET_TRNG_SUCCESS);
+
+#ifdef DEBUG
+	smccc->invoke_fn(ARM_SMCCC_TRNG_GET_UUID, 0, 0, 0, 0, 0, 0, 0, &res);
+	if (res.a0 != ARM_SMCCC_RET_TRNG_NOT_SUPPORTED) {
+		unsigned long uuid_a0 = res.a0;
+		unsigned long uuid_a1 = res.a1;
+		unsigned long uuid_a2 = res.a2;
+		unsigned long uuid_a3 = res.a3;
+		unsigned long major, minor;
+
+		smccc->invoke_fn(ARM_SMCCC_TRNG_VERSION, 0, 0, 0, 0, 0, 0, 0, &res);
+		major = (res.a0 & TRNG_MAJOR_MASK) >> TRNG_MAJOR_SHIFT;
+		minor = (res.a0 & TRNG_MINOR_MASK) >> TRNG_MINOR_SHIFT;
+
+		dev_dbg(dev, "Version %lu.%lu, UUID %08lx-%04lx-%04lx-%04lx-%04lx%08lx\n",
+			major, minor, uuid_a0, uuid_a1 >> 16, uuid_a1 & GENMASK(16, 0),
+			uuid_a2 >> 16, uuid_a2 & GENMASK(16, 0), uuid_a3);
+	} else {
+		dev_warn(dev, "Can't get TRNG UUID\n");
+	}
+#endif
+
+	return 0;
+}
+
+U_BOOT_DRIVER(smccc_trng) = {
+	.name = DRIVER_NAME,
+	.id = UCLASS_RNG,
+	.ops = &smccc_trng_ops,
+	.probe = smccc_trng_probe,
+	.priv_auto = sizeof(struct smccc_trng_priv),
+};