All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] Driver for Open Profile for DICE
@ 2021-12-07 12:36 David Brazdil
  2021-12-07 12:36 ` [PATCH 1/2] dt-bindings: firmware: Add " David Brazdil
  2021-12-07 12:36 ` [PATCH 2/2] misc: dice: Add driver to forward secrets to userspace David Brazdil
  0 siblings, 2 replies; 7+ messages in thread
From: David Brazdil @ 2021-12-07 12:36 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Rob Herring, Jonathan Corbet, Derek Kiernan, Dragan Cvetic,
	Arnd Bergmann, David Brazdil, Hans de Goede, devicetree,
	linux-kernel, linux-doc, Andrew Scull, Will Deacon

Open Profile for DICE is a secret derivation protocol used by some
Android devices. The firmware/bootloader generates the secrets and hands
them over to Linux in a reserved memory region.

This patchset adds the corresponding DeviceTree bindings and a driver
that takes ownership of the memory region and exposes it to userspace
via a character device. It is currently under drivers/misc but perhaps
a better location would be drivers/firmware. Let me know what you think.

The patches are based on top of v5.16-rc4 and can also be found here:
  https://android-kvm.googlesource.com/linux topic/dice_v1

David Brazdil (2):
  dt-bindings: firmware: Add Open Profile for DICE
  misc: dice: Add driver to forward secrets to userspace

 .../devicetree/bindings/firmware/dice.yaml    |  51 ++++
 .../userspace-api/ioctl/ioctl-number.rst      |   1 +
 drivers/misc/Kconfig                          |   8 +
 drivers/misc/Makefile                         |   1 +
 drivers/misc/dice.c                           | 254 ++++++++++++++++++
 include/uapi/linux/dice.h                     |  14 +
 6 files changed, 329 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/firmware/dice.yaml
 create mode 100644 drivers/misc/dice.c
 create mode 100644 include/uapi/linux/dice.h

-- 
2.34.1.400.ga245620fadb-goog


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/2] dt-bindings: firmware: Add Open Profile for DICE
  2021-12-07 12:36 [PATCH 0/2] Driver for Open Profile for DICE David Brazdil
@ 2021-12-07 12:36 ` David Brazdil
  2021-12-07 12:36 ` [PATCH 2/2] misc: dice: Add driver to forward secrets to userspace David Brazdil
  1 sibling, 0 replies; 7+ messages in thread
From: David Brazdil @ 2021-12-07 12:36 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Rob Herring, Jonathan Corbet, Derek Kiernan, Dragan Cvetic,
	Arnd Bergmann, David Brazdil, Hans de Goede, devicetree,
	linux-kernel, linux-doc, Andrew Scull, Will Deacon

Add DeviceTree bindings for Open Profile for DICE. DICE is a protocol
for deriving Compound Device Identifier (CDI) certificates. These are
generated by the firmware/bootloader and stored in memory. Location of
the buffer is described as a reserved memory region referenced by
a compatible DICE device node.

See https://pigweed.googlesource.com/open-dice

Signed-off-by: David Brazdil <dbrazdil@google.com>
---
 .../devicetree/bindings/firmware/dice.yaml    | 51 +++++++++++++++++++
 1 file changed, 51 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/firmware/dice.yaml

diff --git a/Documentation/devicetree/bindings/firmware/dice.yaml b/Documentation/devicetree/bindings/firmware/dice.yaml
new file mode 100644
index 000000000000..c0726109e73d
--- /dev/null
+++ b/Documentation/devicetree/bindings/firmware/dice.yaml
@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/firmware/dice.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Open Profile for DICE Device Tree Bindings
+
+description: |
+  This binding represents a reserved memory region containing secrets derived
+  derived following the Open Profile for DICE.
+
+  See https://pigweed.googlesource.com/open-dice/
+
+maintainers:
+  - David Brazdil <dbrazdil@google.com>
+
+properties:
+  compatible:
+    enum:
+      - google,dice
+
+  memory-region:
+    maxItems: 1
+    description: |
+      phandle to the reserved memory node to be associated with the device
+      The reserved memory node should be defined as per the bindings,
+      Documentation/devicetree/bindings/reserved-memory/reserved-memory.yaml
+
+required:
+  - compatible
+  - memory-region
+
+additionalProperties: false
+
+examples:
+  - |
+    reserved-memory {
+        #address-cells = <2>;
+        #size-cells = <1>;
+
+        dice_reserved: dice@12340000 {
+            reg = <0x00 0x12340000 0x2000>;
+            no-map;
+        };
+    };
+
+    dice {
+        compatible = "google,dice";
+        memory-region = <&dice_reserved>;
+    };
-- 
2.34.1.400.ga245620fadb-goog


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/2] misc: dice: Add driver to forward secrets to userspace
  2021-12-07 12:36 [PATCH 0/2] Driver for Open Profile for DICE David Brazdil
  2021-12-07 12:36 ` [PATCH 1/2] dt-bindings: firmware: Add " David Brazdil
@ 2021-12-07 12:36 ` David Brazdil
  2021-12-07 13:08   ` Greg Kroah-Hartman
  1 sibling, 1 reply; 7+ messages in thread
From: David Brazdil @ 2021-12-07 12:36 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Rob Herring, Jonathan Corbet, Derek Kiernan, Dragan Cvetic,
	Arnd Bergmann, David Brazdil, Hans de Goede, devicetree,
	linux-kernel, linux-doc, Andrew Scull, Will Deacon

Open Profile for DICE is a protocol for deriving unique secrets at boot,
used by some Android devices. The firmware/bootloader hands over secrets
in a reserved memory region, this driver takes ownership of the memory
region and exposes it to userspace via a character device that
lets userspace mmap the memory region into its process.

The character device can only be opened once at any given time.

Userspace can issue an ioctl requesting that the memory be wiped after
the current FD is released. In that case, the driver will clear
the buffer and refuse to open any new FDs.

Cc: Andrew Scull <ascull@google.com>
Cc: Will Deacon <will@kernel.org>
Signed-off-by: David Brazdil <dbrazdil@google.com>
---
 .../userspace-api/ioctl/ioctl-number.rst      |   1 +
 drivers/misc/Kconfig                          |   8 +
 drivers/misc/Makefile                         |   1 +
 drivers/misc/dice.c                           | 254 ++++++++++++++++++
 include/uapi/linux/dice.h                     |  14 +
 5 files changed, 278 insertions(+)
 create mode 100644 drivers/misc/dice.c
 create mode 100644 include/uapi/linux/dice.h

diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
index cfe6cccf0f44..4b8bee2ffd1e 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
@@ -341,6 +341,7 @@ Code  Seq#    Include File                                           Comments
 0xAE  40-FF  linux/kvm.h                                             Kernel-based Virtual Machine
                                                                      <mailto:kvm@vger.kernel.org>
 0xAE  20-3F  linux/nitro_enclaves.h                                  Nitro Enclaves
+0xAE  40-5F  uapi/linux/dice.h                                       Open Profile for DICE driver
 0xAF  00-1F  linux/fsl_hypervisor.h                                  Freescale hypervisor
 0xB0  all                                                            RATIO devices in development:
                                                                      <mailto:vgo@ratio.de>
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 0f5a49fc7c9e..7165f4b6c41b 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -470,6 +470,14 @@ config HISI_HIKEY_USB
 	  switching between the dual-role USB-C port and the USB-A host ports
 	  using only one USB controller.
 
+config DICE
+	tristate "Open Profile for DICE driver"
+	depends on OF_RESERVED_MEM
+	help
+	  This driver allows to ownership of a reserved memory region
+	  containing DICE secrets and expose them to userspace via
+	  a character device.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index a086197af544..f73c6bb23ccd 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -59,3 +59,4 @@ obj-$(CONFIG_UACCE)		+= uacce/
 obj-$(CONFIG_XILINX_SDFEC)	+= xilinx_sdfec.o
 obj-$(CONFIG_HISI_HIKEY_USB)	+= hisi_hikey_usb.o
 obj-$(CONFIG_HI6421V600_IRQ)	+= hi6421v600-irq.o
+obj-$(CONFIG_DICE)		+= dice.o
diff --git a/drivers/misc/dice.c b/drivers/misc/dice.c
new file mode 100644
index 000000000000..c5217793328e
--- /dev/null
+++ b/drivers/misc/dice.c
@@ -0,0 +1,254 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2021 - Google LLC
+ * Author: David Brazdil <dbrazdil@google.com>
+ *
+ * Driver for Open Profile for DICE.
+ *
+ * This driver takes ownership of a reserved memory region containing secrets
+ * derived following the Open Profile for DICE. The contents of the memory
+ * region are not interpreted by the kernel but can be mapped into a userspace
+ * process via a character device. The memory region can also be wiped, removing
+ * the secrets from memory.
+ *
+ * Userspace can access the data by (w/o error handling):
+ *
+ *     int fd = open("/dev/dice", O_RDONLY | O_CLOEXEC);
+ *     size_t size = ioctl(fd, DICE_GET_SIZE);
+ *     ioctl(fd, DICE_SET_WIPE_ON_CLOSE);
+ *     void *data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
+ *     close(fd);
+ */
+
+#include <linux/cdev.h>
+#include <linux/dice.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+
+#define DICE_MKDEV		MKDEV(MAJOR(dice_devt), 0)
+#define DICE_MINOR_COUNT	1
+
+enum dice_state {
+	DICE_STATE_READY = 0,
+	DICE_STATE_BUSY,
+	DICE_STATE_BUSY_WIPE_ON_CLOSE,
+	DICE_STATE_WIPED,
+};
+
+struct dice_data {
+	struct device *dev;
+	struct cdev cdev;
+	atomic_t state;
+	phys_addr_t base;
+	size_t size;
+};
+
+static dev_t dice_devt;
+static struct class *dice_class;
+
+static int dice_open(struct inode *inode, struct file *filp)
+{
+	struct dice_data *data;
+
+	data = container_of(inode->i_cdev, struct dice_data, cdev);
+
+	/* Never allow write access. */
+	if (filp->f_mode & FMODE_WRITE)
+		return -EROFS;
+
+	switch (atomic_cmpxchg(&data->state, DICE_STATE_READY, DICE_STATE_BUSY)) {
+	case DICE_STATE_READY:
+		break;
+	case DICE_STATE_WIPED:
+		/* Return error to inform caller memory has been wiped. */
+		return -EACCES;
+	default:
+		return -EBUSY;
+	}
+
+	filp->private_data = data;
+	nonseekable_open(inode, filp);
+	return 0;
+}
+
+static int dice_release(struct inode *inode, struct file *filp)
+{
+	struct dice_data *data = filp->private_data;
+	void *base;
+
+	if (atomic_read(&data->state) == DICE_STATE_BUSY_WIPE_ON_CLOSE) {
+		base = devm_memremap(data->dev, data->base, data->size, MEMREMAP_WT);
+		if (!WARN_ON(!base)) {
+			memzero_explicit(base, data->size);
+			devm_memunmap(data->dev, base);
+		}
+		atomic_set(&data->state, DICE_STATE_WIPED);
+		return 0;
+	}
+
+	atomic_set(&data->state, DICE_STATE_READY);
+	return 0;
+}
+
+static int dice_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	struct dice_data *data = filp->private_data;
+
+	vma->vm_flags |= VM_DONTCOPY;
+	return vm_iomap_memory(vma, data->base, data->size);
+}
+
+static long dice_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	struct dice_data *data = filp->private_data;
+
+	switch (cmd) {
+	case DICE_GET_SIZE:
+		/* Checked against INT_MAX in dice_probe(). */
+		return data->size;
+	case DICE_SET_WIPE_ON_CLOSE:
+		atomic_set(&data->state, DICE_STATE_BUSY_WIPE_ON_CLOSE);
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static const struct file_operations dice_fops = {
+	.open = dice_open,
+	.release = dice_release,
+	.mmap = dice_mmap,
+	.unlocked_ioctl = dice_ioctl,
+	.llseek = no_llseek,
+};
+
+static int __init dice_probe(struct platform_device *pdev)
+{
+	struct device *chr_dev, *dev = &pdev->dev;
+	struct device_node *rmem_np;
+	struct reserved_mem *rmem;
+	struct dice_data *data;
+	int ret;
+
+	rmem_np = of_parse_phandle(dev->of_node, "memory-region", 0);
+	if (!rmem_np) {
+		dev_err(dev, "missing 'memory-region' property\n");
+		return -EINVAL;
+	}
+
+	rmem = of_reserved_mem_lookup(rmem_np);
+	of_node_put(rmem_np);
+	if (!rmem) {
+		dev_err(dev, "failed to lookup reserved memory\n");
+		return -EINVAL;
+	}
+
+	if (!PAGE_ALIGNED(rmem->base) || !PAGE_ALIGNED(rmem->size)) {
+		dev_err(dev, "memory region must be page-aligned\n");
+		return -EINVAL;
+	}
+
+	if (rmem->size > INT_MAX) {
+		dev_err(dev, "memory region too large\n");
+		return -EINVAL;
+	}
+
+	data = devm_kmalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	*data = (struct dice_data){
+		.dev = dev,
+		.base = rmem->base,
+		.size = rmem->size,
+		.state = ATOMIC_INIT(DICE_STATE_READY),
+	};
+
+	cdev_init(&data->cdev, &dice_fops);
+	data->cdev.owner = THIS_MODULE;
+	ret = cdev_add(&data->cdev, DICE_MKDEV, 1);
+	if (ret)
+		return ret;
+
+	chr_dev = device_create(dice_class, dev, DICE_MKDEV, NULL, "dice");
+	if (IS_ERR(chr_dev)) {
+		cdev_del(&data->cdev);
+		return PTR_ERR(chr_dev);
+	}
+
+	platform_set_drvdata(pdev, data);
+	return 0;
+}
+
+static int dice_remove(struct platform_device *pdev)
+{
+	struct dice_data *data = platform_get_drvdata(pdev);
+
+	cdev_del(&data->cdev);
+	device_destroy(dice_class, DICE_MKDEV);
+	return 0;
+}
+
+static char *dice_devnode(struct device *dev, umode_t *mode)
+{
+	/* Initial permissions: read-only by owner */
+	if (mode)
+		*mode = 0400;
+	return NULL;
+}
+
+static const struct of_device_id dice_of_match[] = {
+	{ .compatible = "google,dice" },
+	{},
+};
+
+static struct platform_driver dice_driver = {
+	.remove = dice_remove,
+	.driver = {
+		.name = "dice",
+		.of_match_table = dice_of_match,
+	},
+};
+
+static int __init dice_init(void)
+{
+	int ret;
+
+	ret = alloc_chrdev_region(&dice_devt, 0, DICE_MINOR_COUNT, "dice");
+	if (ret)
+		return ret;
+
+	dice_class = class_create(THIS_MODULE, "dice");
+	if (IS_ERR(dice_class)) {
+		ret = PTR_ERR(dice_class);
+		goto fail;
+	}
+	dice_class->devnode = dice_devnode;
+
+	ret = platform_driver_probe(&dice_driver, dice_probe);
+	if (ret)
+		goto fail;
+
+	return 0;
+
+fail:
+	class_destroy(dice_class);
+	unregister_chrdev_region(dice_devt, DICE_MINOR_COUNT);
+	return ret;
+}
+
+static void __exit dice_exit(void)
+{
+	platform_driver_unregister(&dice_driver);
+	class_destroy(dice_class);
+	unregister_chrdev_region(dice_devt, DICE_MINOR_COUNT);
+}
+
+module_init(dice_init);
+module_exit(dice_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("David Brazdil <dbrazdil@google.com>");
diff --git a/include/uapi/linux/dice.h b/include/uapi/linux/dice.h
new file mode 100644
index 000000000000..19d679c56d06
--- /dev/null
+++ b/include/uapi/linux/dice.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * IOCTLs for Open Profile for DICE character device
+ */
+
+#ifndef _UAPI_DICE_H_
+#define _UAPI_DICE_H_
+
+#include <linux/ioctl.h>
+
+#define DICE_GET_SIZE		_IO(0xAE, 0x40)
+#define DICE_SET_WIPE_ON_CLOSE	_IO(0xAE, 0x41)
+
+#endif
-- 
2.34.1.400.ga245620fadb-goog


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 2/2] misc: dice: Add driver to forward secrets to userspace
  2021-12-07 12:36 ` [PATCH 2/2] misc: dice: Add driver to forward secrets to userspace David Brazdil
@ 2021-12-07 13:08   ` Greg Kroah-Hartman
  2021-12-07 16:44     ` David Brazdil
  0 siblings, 1 reply; 7+ messages in thread
From: Greg Kroah-Hartman @ 2021-12-07 13:08 UTC (permalink / raw)
  To: David Brazdil
  Cc: Rob Herring, Jonathan Corbet, Derek Kiernan, Dragan Cvetic,
	Arnd Bergmann, Hans de Goede, devicetree, linux-kernel,
	linux-doc, Andrew Scull, Will Deacon

On Tue, Dec 07, 2021 at 12:36:17PM +0000, David Brazdil wrote:
> Open Profile for DICE is a protocol for deriving unique secrets at boot,
> used by some Android devices. The firmware/bootloader hands over secrets
> in a reserved memory region, this driver takes ownership of the memory
> region and exposes it to userspace via a character device that
> lets userspace mmap the memory region into its process.
> 
> The character device can only be opened once at any given time.

Why?  That should not matter.  And your code (correctly), does not check
for that.  So why say that here?

> Userspace can issue an ioctl requesting that the memory be wiped after
> the current FD is released. In that case, the driver will clear
> the buffer and refuse to open any new FDs.
> 
> Cc: Andrew Scull <ascull@google.com>
> Cc: Will Deacon <will@kernel.org>
> Signed-off-by: David Brazdil <dbrazdil@google.com>

Some minor comments on the code:

> +#include <linux/cdev.h>
> +#include <linux/dice.h>
> +#include <linux/io.h>
> +#include <linux/mm.h>
> +#include <linux/module.h>
> +#include <linux/of_reserved_mem.h>
> +#include <linux/platform_device.h>
> +
> +#define DICE_MKDEV		MKDEV(MAJOR(dice_devt), 0)
> +#define DICE_MINOR_COUNT	1

Please just use the misc_device api, no need to try to claim a major
number for just one device node.  That will simplify your code a lot as
well.

> +enum dice_state {
> +	DICE_STATE_READY = 0,
> +	DICE_STATE_BUSY,
> +	DICE_STATE_BUSY_WIPE_ON_CLOSE,
> +	DICE_STATE_WIPED,
> +};
> +
> +struct dice_data {
> +	struct device *dev;

What is this for?  The parent?  If so, say parent :)

> +	struct cdev cdev;
> +	atomic_t state;
> +	phys_addr_t base;
> +	size_t size;
> +};
> +
> +static dev_t dice_devt;
> +static struct class *dice_class;
> +
> +static int dice_open(struct inode *inode, struct file *filp)
> +{
> +	struct dice_data *data;
> +
> +	data = container_of(inode->i_cdev, struct dice_data, cdev);
> +
> +	/* Never allow write access. */
> +	if (filp->f_mode & FMODE_WRITE)
> +		return -EROFS;

Why do you care?  Writes just will not work anyway, right?

> +
> +	switch (atomic_cmpxchg(&data->state, DICE_STATE_READY, DICE_STATE_BUSY)) {
> +	case DICE_STATE_READY:
> +		break;
> +	case DICE_STATE_WIPED:
> +		/* Return error to inform caller memory has been wiped. */
> +		return -EACCES;
> +	default:
> +		return -EBUSY;
> +	}
> +
> +	filp->private_data = data;
> +	nonseekable_open(inode, filp);
> +	return 0;
> +}
> +
> +static int dice_release(struct inode *inode, struct file *filp)
> +{
> +	struct dice_data *data = filp->private_data;
> +	void *base;
> +
> +	if (atomic_read(&data->state) == DICE_STATE_BUSY_WIPE_ON_CLOSE) {
> +		base = devm_memremap(data->dev, data->base, data->size, MEMREMAP_WT);
> +		if (!WARN_ON(!base)) {
> +			memzero_explicit(base, data->size);
> +			devm_memunmap(data->dev, base);
> +		}
> +		atomic_set(&data->state, DICE_STATE_WIPED);
> +		return 0;
> +	}
> +
> +	atomic_set(&data->state, DICE_STATE_READY);
> +	return 0;
> +}
> +
> +static int dice_mmap(struct file *filp, struct vm_area_struct *vma)
> +{
> +	struct dice_data *data = filp->private_data;
> +
> +	vma->vm_flags |= VM_DONTCOPY;
> +	return vm_iomap_memory(vma, data->base, data->size);
> +}
> +
> +static long dice_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> +{
> +	struct dice_data *data = filp->private_data;
> +
> +	switch (cmd) {
> +	case DICE_GET_SIZE:
> +		/* Checked against INT_MAX in dice_probe(). */
> +		return data->size;
> +	case DICE_SET_WIPE_ON_CLOSE:
> +		atomic_set(&data->state, DICE_STATE_BUSY_WIPE_ON_CLOSE);
> +		return 0;
> +	}
> +
> +	return -EINVAL;

Wrong error value for invalid ioctl.

And why do these have to be ioctls at all?  I guess sysfs could be used,
but if you are comfortable with ioctls, that's fine.

> +}
> +
> +static const struct file_operations dice_fops = {
> +	.open = dice_open,
> +	.release = dice_release,
> +	.mmap = dice_mmap,
> +	.unlocked_ioctl = dice_ioctl,
> +	.llseek = no_llseek,
> +};
> +
> +static int __init dice_probe(struct platform_device *pdev)
> +{
> +	struct device *chr_dev, *dev = &pdev->dev;
> +	struct device_node *rmem_np;
> +	struct reserved_mem *rmem;
> +	struct dice_data *data;
> +	int ret;
> +
> +	rmem_np = of_parse_phandle(dev->of_node, "memory-region", 0);
> +	if (!rmem_np) {
> +		dev_err(dev, "missing 'memory-region' property\n");
> +		return -EINVAL;
> +	}
> +
> +	rmem = of_reserved_mem_lookup(rmem_np);
> +	of_node_put(rmem_np);
> +	if (!rmem) {
> +		dev_err(dev, "failed to lookup reserved memory\n");
> +		return -EINVAL;
> +	}
> +
> +	if (!PAGE_ALIGNED(rmem->base) || !PAGE_ALIGNED(rmem->size)) {
> +		dev_err(dev, "memory region must be page-aligned\n");
> +		return -EINVAL;
> +	}
> +
> +	if (rmem->size > INT_MAX) {
> +		dev_err(dev, "memory region too large\n");
> +		return -EINVAL;
> +	}
> +
> +	data = devm_kmalloc(dev, sizeof(*data), GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	*data = (struct dice_data){
> +		.dev = dev,
> +		.base = rmem->base,
> +		.size = rmem->size,
> +		.state = ATOMIC_INIT(DICE_STATE_READY),
> +	};
> +
> +	cdev_init(&data->cdev, &dice_fops);

Again a misc device will make this much simpler.

> +	data->cdev.owner = THIS_MODULE;
> +	ret = cdev_add(&data->cdev, DICE_MKDEV, 1);
> +	if (ret)
> +		return ret;
> +
> +	chr_dev = device_create(dice_class, dev, DICE_MKDEV, NULL, "dice");
> +	if (IS_ERR(chr_dev)) {
> +		cdev_del(&data->cdev);
> +		return PTR_ERR(chr_dev);
> +	}
> +
> +	platform_set_drvdata(pdev, data);
> +	return 0;
> +}
> +
> +static int dice_remove(struct platform_device *pdev)
> +{
> +	struct dice_data *data = platform_get_drvdata(pdev);
> +
> +	cdev_del(&data->cdev);
> +	device_destroy(dice_class, DICE_MKDEV);
> +	return 0;
> +}
> +
> +static char *dice_devnode(struct device *dev, umode_t *mode)
> +{
> +	/* Initial permissions: read-only by owner */
> +	if (mode)
> +		*mode = 0400;
> +	return NULL;

Put the mode in the misc device structure please.

> +}
> +
> +static const struct of_device_id dice_of_match[] = {
> +	{ .compatible = "google,dice" },
> +	{},
> +};
> +
> +static struct platform_driver dice_driver = {
> +	.remove = dice_remove,
> +	.driver = {
> +		.name = "dice",
> +		.of_match_table = dice_of_match,
> +	},
> +};
> +
> +static int __init dice_init(void)
> +{
> +	int ret;
> +
> +	ret = alloc_chrdev_region(&dice_devt, 0, DICE_MINOR_COUNT, "dice");
> +	if (ret)
> +		return ret;
> +
> +	dice_class = class_create(THIS_MODULE, "dice");
> +	if (IS_ERR(dice_class)) {
> +		ret = PTR_ERR(dice_class);
> +		goto fail;
> +	}
> +	dice_class->devnode = dice_devnode;

Never create a class and reserve things like this, if you do not even
know if your hardware is present or not.  That just wastes resources.
Only allocate it if your device probes properly.

And again, moving to a misc_device makes this all much simpler and your
whole init function can go away.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 2/2] misc: dice: Add driver to forward secrets to userspace
  2021-12-07 13:08   ` Greg Kroah-Hartman
@ 2021-12-07 16:44     ` David Brazdil
  2021-12-07 17:16       ` Greg Kroah-Hartman
  0 siblings, 1 reply; 7+ messages in thread
From: David Brazdil @ 2021-12-07 16:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Rob Herring, Jonathan Corbet, Derek Kiernan, Dragan Cvetic,
	Arnd Bergmann, Hans de Goede, devicetree, linux-kernel,
	linux-doc, Andrew Scull, Will Deacon

Hi Greg,

On Tue, Dec 07, 2021 at 02:08:17PM +0100, Greg Kroah-Hartman wrote:
> On Tue, Dec 07, 2021 at 12:36:17PM +0000, David Brazdil wrote:
> > Open Profile for DICE is a protocol for deriving unique secrets at boot,
> > used by some Android devices. The firmware/bootloader hands over secrets
> > in a reserved memory region, this driver takes ownership of the memory
> > region and exposes it to userspace via a character device that
> > lets userspace mmap the memory region into its process.
> > 
> > The character device can only be opened once at any given time.
> 
> Why?  That should not matter.  And your code (correctly), does not check
> for that.  So why say that here?

It does check - open() returns -EBUSY if cmpxchg of the state from READY
to BUSY fails. I agree this is a bit unconventional but it makes things
easier to reason about. With multiple open FDs the driver would have to
wait for all of them to get released before wiping, so one user could
block the wiping requested by others by holding the FD indefinitely.
And wiping despite other open FDs seems wrong, too. Is there a better
way of doing this?

> > +#include <linux/cdev.h>
> > +#include <linux/dice.h>
> > +#include <linux/io.h>
> > +#include <linux/mm.h>
> > +#include <linux/module.h>
> > +#include <linux/of_reserved_mem.h>
> > +#include <linux/platform_device.h>
> > +
> > +#define DICE_MKDEV		MKDEV(MAJOR(dice_devt), 0)
> > +#define DICE_MINOR_COUNT	1
> 
> Please just use the misc_device api, no need to try to claim a major
> number for just one device node.  That will simplify your code a lot as
> well.

Ok, I'll look into it.

> > +static int dice_open(struct inode *inode, struct file *filp)
> > +{
> > +	struct dice_data *data;
> > +
> > +	data = container_of(inode->i_cdev, struct dice_data, cdev);
> > +
> > +	/* Never allow write access. */
> > +	if (filp->f_mode & FMODE_WRITE)
> > +		return -EROFS;
> 
> Why do you care?  Writes just will not work anyway, right?

There is nothing else preventing writes, the reserved memory is just plain
old RAM.

Thanks,
David

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 2/2] misc: dice: Add driver to forward secrets to userspace
  2021-12-07 16:44     ` David Brazdil
@ 2021-12-07 17:16       ` Greg Kroah-Hartman
  2021-12-07 18:09         ` David Brazdil
  0 siblings, 1 reply; 7+ messages in thread
From: Greg Kroah-Hartman @ 2021-12-07 17:16 UTC (permalink / raw)
  To: David Brazdil
  Cc: Rob Herring, Jonathan Corbet, Derek Kiernan, Dragan Cvetic,
	Arnd Bergmann, Hans de Goede, devicetree, linux-kernel,
	linux-doc, Andrew Scull, Will Deacon

On Tue, Dec 07, 2021 at 04:44:18PM +0000, David Brazdil wrote:
> Hi Greg,
> 
> On Tue, Dec 07, 2021 at 02:08:17PM +0100, Greg Kroah-Hartman wrote:
> > On Tue, Dec 07, 2021 at 12:36:17PM +0000, David Brazdil wrote:
> > > Open Profile for DICE is a protocol for deriving unique secrets at boot,
> > > used by some Android devices. The firmware/bootloader hands over secrets
> > > in a reserved memory region, this driver takes ownership of the memory
> > > region and exposes it to userspace via a character device that
> > > lets userspace mmap the memory region into its process.
> > > 
> > > The character device can only be opened once at any given time.
> > 
> > Why?  That should not matter.  And your code (correctly), does not check
> > for that.  So why say that here?
> 
> It does check - open() returns -EBUSY if cmpxchg of the state from READY
> to BUSY fails. I agree this is a bit unconventional but it makes things
> easier to reason about. With multiple open FDs the driver would have to
> wait for all of them to get released before wiping, so one user could
> block the wiping requested by others by holding the FD indefinitely.
> And wiping despite other open FDs seems wrong, too. Is there a better
> way of doing this?

Yes, totally ignore it from the kernel point of view.  You don't know
what userspace just did with that FD the kernel gave it, it could have
sent it across a pipe, run dup() on it, or any sort of other things.
Just rely on open/release to know when the device is opened, and then
when that instance is released.  If userspace wants to do looney things,
and oddities happen, that's userspace's problem, not yours :)


> > > +#include <linux/cdev.h>
> > > +#include <linux/dice.h>
> > > +#include <linux/io.h>
> > > +#include <linux/mm.h>
> > > +#include <linux/module.h>
> > > +#include <linux/of_reserved_mem.h>
> > > +#include <linux/platform_device.h>
> > > +
> > > +#define DICE_MKDEV		MKDEV(MAJOR(dice_devt), 0)
> > > +#define DICE_MINOR_COUNT	1
> > 
> > Please just use the misc_device api, no need to try to claim a major
> > number for just one device node.  That will simplify your code a lot as
> > well.
> 
> Ok, I'll look into it.
> 
> > > +static int dice_open(struct inode *inode, struct file *filp)
> > > +{
> > > +	struct dice_data *data;
> > > +
> > > +	data = container_of(inode->i_cdev, struct dice_data, cdev);
> > > +
> > > +	/* Never allow write access. */
> > > +	if (filp->f_mode & FMODE_WRITE)
> > > +		return -EROFS;
> > 
> > Why do you care?  Writes just will not work anyway, right?
> 
> There is nothing else preventing writes, the reserved memory is just plain
> old RAM.

And you can rely on this check only?  Nothing else needed with mmap?
And why can't userspace write to this?  What's wrong with that
happening?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 2/2] misc: dice: Add driver to forward secrets to userspace
  2021-12-07 17:16       ` Greg Kroah-Hartman
@ 2021-12-07 18:09         ` David Brazdil
  0 siblings, 0 replies; 7+ messages in thread
From: David Brazdil @ 2021-12-07 18:09 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Rob Herring, Jonathan Corbet, Derek Kiernan, Dragan Cvetic,
	Arnd Bergmann, Hans de Goede, devicetree, linux-kernel,
	linux-doc, Andrew Scull, Will Deacon

On Tue, Dec 07, 2021 at 06:16:17PM +0100, Greg Kroah-Hartman wrote:
> On Tue, Dec 07, 2021 at 04:44:18PM +0000, David Brazdil wrote:
> > Hi Greg,
> > 
> > On Tue, Dec 07, 2021 at 02:08:17PM +0100, Greg Kroah-Hartman wrote:
> > > On Tue, Dec 07, 2021 at 12:36:17PM +0000, David Brazdil wrote:
> > > > Open Profile for DICE is a protocol for deriving unique secrets at boot,
> > > > used by some Android devices. The firmware/bootloader hands over secrets
> > > > in a reserved memory region, this driver takes ownership of the memory
> > > > region and exposes it to userspace via a character device that
> > > > lets userspace mmap the memory region into its process.
> > > > 
> > > > The character device can only be opened once at any given time.
> > > 
> > > Why?  That should not matter.  And your code (correctly), does not check
> > > for that.  So why say that here?
> > 
> > It does check - open() returns -EBUSY if cmpxchg of the state from READY
> > to BUSY fails. I agree this is a bit unconventional but it makes things
> > easier to reason about. With multiple open FDs the driver would have to
> > wait for all of them to get released before wiping, so one user could
> > block the wiping requested by others by holding the FD indefinitely.
> > And wiping despite other open FDs seems wrong, too. Is there a better
> > way of doing this?
> 
> Yes, totally ignore it from the kernel point of view.  You don't know
> what userspace just did with that FD the kernel gave it, it could have
> sent it across a pipe, run dup() on it, or any sort of other things.
> Just rely on open/release to know when the device is opened, and then
> when that instance is released.  If userspace wants to do looney things,
> and oddities happen, that's userspace's problem, not yours :)
> 
Fair point.

> > > > +#include <linux/cdev.h>
> > > > +#include <linux/dice.h>
> > > > +#include <linux/io.h>
> > > > +#include <linux/mm.h>
> > > > +#include <linux/module.h>
> > > > +#include <linux/of_reserved_mem.h>
> > > > +#include <linux/platform_device.h>
> > > > +
> > > > +#define DICE_MKDEV		MKDEV(MAJOR(dice_devt), 0)
> > > > +#define DICE_MINOR_COUNT	1
> > > 
> > > Please just use the misc_device api, no need to try to claim a major
> > > number for just one device node.  That will simplify your code a lot as
> > > well.
> > 
> > Ok, I'll look into it.
> > 
> > > > +static int dice_open(struct inode *inode, struct file *filp)
> > > > +{
> > > > +	struct dice_data *data;
> > > > +
> > > > +	data = container_of(inode->i_cdev, struct dice_data, cdev);
> > > > +
> > > > +	/* Never allow write access. */
> > > > +	if (filp->f_mode & FMODE_WRITE)
> > > > +		return -EROFS;
> > > 
> > > Why do you care?  Writes just will not work anyway, right?
> > 
> > There is nothing else preventing writes, the reserved memory is just plain
> > old RAM.
> 
> And you can rely on this check only?  Nothing else needed with mmap?
> And why can't userspace write to this?  What's wrong with that
> happening?

AFAICT vm_iomap_memory takes care of it. It will allow a MAP_PRIVATE
mapping of a read-only FD but not a MAP_SHARED one. I think that gives
nice guarantees to userspace that if a process opens the char device itself,
it's getting the original data, not something another process wrote there.

-David

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2021-12-07 18:09 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-07 12:36 [PATCH 0/2] Driver for Open Profile for DICE David Brazdil
2021-12-07 12:36 ` [PATCH 1/2] dt-bindings: firmware: Add " David Brazdil
2021-12-07 12:36 ` [PATCH 2/2] misc: dice: Add driver to forward secrets to userspace David Brazdil
2021-12-07 13:08   ` Greg Kroah-Hartman
2021-12-07 16:44     ` David Brazdil
2021-12-07 17:16       ` Greg Kroah-Hartman
2021-12-07 18:09         ` David Brazdil

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.