[PATCH] misc: atmel-secumod: Driver for Atmel "security module".

Alexandre Belloni
2016-01-25T11:09:21Z

Hi,

On 20/01/2016 at 20:57:35 -0700, David Mosberger-Tang wrote :
> From: David Mosberger <davidm@egauge.net>
>
> SAMA5D2 (and perhaps other SOCs) implements a secure module which
> hosts a battery-backed SRAM with is sectioned into three different
> areas with different properties: a 4KiB auto-erasable secure RAM, a
> 1KiB non-auto-eraseable secure RAM, and 32 bytes of (non-secure) RAM.
>
> This driver provides (minimal) access to these RAM areas through
> sysfs. For example, adding this to the DTS:
>
> secumod at fc040000 {
> compatible = "atmel,sama5d2-secumod";
> reg = <0xfc040000 0x4000>;
> status = "okay";
>
> #address-cells = <1>;
> #size-cells = <1>;
>
> secram_auto_erasable at f8044000 {
> reg = <0xf8044000 0x1000>;
> };
> secram at f8045000 {
> reg = <0xf8045000 0x400>;
> };
> ram at f8045400 {
> reg = <0xf8045400 0x20>;
> };
> };
>
> would provide binary files "ram", "secram", and "secram_auto_erasable"
> in directory /sys/bus/platform/devices/fc040000.secumod.
> This files can then be read or written with any user-level tool.
>
> The driver is minimal in the sense that it doesn't provide (yet)
> a way to manage scrambling keys etc.
>

I know this does more than that but I think those thre sections should
be registered using the nvmem framework. The sysfs file creation and
accesses then comes for free.

Another idea is also to expose it using a genpool so it can be accessed
as sram from inside the kernel.

> Signed-off-by: David Mosberger <davidm@egauge.net>
> ---
> arch/arm/boot/dts/sama5d2.dtsi | 19 ++++
> drivers/misc/Kconfig | 7 ++
> drivers/misc/Makefile | 1 +
> drivers/misc/atmel-secumod.c | 224 +++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 251 insertions(+)
> create mode 100644 drivers/misc/atmel-secumod.c
>
> diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
> index 89d4de8..d4bd3b6 100644
> --- a/arch/arm/boot/dts/sama5d2.dtsi
> +++ b/arch/arm/boot/dts/sama5d2.dtsi
> @@ -1239,6 +1239,25 @@
> clocks = <&pioA_clk>;
> };
>
> + secumod at fc040000 {
> + compatible = "atmel,sama5d2-secumod";
> + reg = <0xfc040000 0x4000>;
> + status = "okay";
> +
> + #address-cells = <1>;
> + #size-cells = <1>;
> +
> + secram_auto_erasable at f8044000 {
> + reg = <0xf8044000 0x1000>;
> + };
> + secram at f8045000 {
> + reg = <0xf8045000 0x400>;
> + };
> + ram at f8045400 {
> + reg = <0xf8045400 0x20>;
> + };
> + };
> +
> tdes at fc044000 {
> compatible = "atmel,at91sam9g46-tdes";
> reg = <0xfc044000 0x100>;
> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> index 006242c..74a8ee5 100644
> --- a/drivers/misc/Kconfig
> +++ b/drivers/misc/Kconfig
> @@ -84,6 +84,13 @@ config ATMEL_TCB_CLKSRC_BLOCK
> TC can be used for other purposes, such as PWM generation and
> interval timing.
>
> +config ATMEL_SECUMOD
> + tristate "Atmel Secure Module driver"
> + depends on ARCH_AT91
> + help
> + Select this to get support for the secure module (SECUMOD) built
> + into the SAMA5D2 chips.
> +
> config DUMMY_IRQ
> tristate "Dummy IRQ handler"
> default n
> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> index 7d5c4cd..e1f661d 100644
> --- a/drivers/misc/Makefile
> +++ b/drivers/misc/Makefile
> @@ -9,6 +9,7 @@ obj-$(CONFIG_AD525X_DPOT_SPI) += ad525x_dpot-spi.o
> obj-$(CONFIG_INTEL_MID_PTI) += pti.o
> obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o
> obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o
> +obj-$(CONFIG_ATMEL_SECUMOD) += atmel-secumod.o
> obj-$(CONFIG_BMP085) += bmp085.o
> obj-$(CONFIG_BMP085_I2C) += bmp085-i2c.o
> obj-$(CONFIG_BMP085_SPI) += bmp085-spi.o
> diff --git a/drivers/misc/atmel-secumod.c b/drivers/misc/atmel-secumod.c
> new file mode 100644
> index 0000000..5ac1a18
> --- /dev/null
> +++ b/drivers/misc/atmel-secumod.c
> @@ -0,0 +1,224 @@
> +/*
> + * Driver for SAMA5D2 secure module (SECUMOD).
> + *
> + * Copyright (C) 2016 eGauge Systems LLC
> + *
> + * David Mosberger <davidm@egauge.net>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/delay.h>
> +
> +#include <asm/io.h>
> +
> +/*
> + * Security-module register definitions:
> + */
> +#define SECUMOD_RAMRDY 0x0014
> +
> +struct ram_area {
> + struct list_head next;
> + void *regs; /* ioremapped RAM area */
> + size_t size; /* size in bytes */
> + struct bin_attribute attr;
> +};
> +
> +struct secumod_device {
> + struct spinlock lock;
> + void __iomem *regs; /* ioremapped SECUMOD registers */
> + struct platform_device *pdev;
> + struct list_head ram_areas;
> +};
> +
> +static ssize_t
> +secumod_ram_write(struct file *filp, struct kobject *kobj,
> + struct bin_attribute *attr,
> + char *buf, loff_t off, size_t count)
> +{
> + struct ram_area *ram = attr->private;
> + struct device *dev = kobj_to_dev(kobj);
> + struct platform_device *pdev = to_platform_device(dev);
> + struct secumod_device *secumod = platform_get_drvdata(pdev);
> +
> + if (off < 0 || off >= ram->size)
> + return -EINVAL;
> + if (off + count > ram->size)
> + count = ram->size = off;
> +
> + if (count > 0) {
> + spin_lock(&secumod->lock);
> + memcpy_toio(ram->regs + off, buf, count);
> + spin_unlock(&secumod->lock);
> + }
> + return count;
> +}
> +
> +static ssize_t
> +secumod_ram_read(struct file *filp, struct kobject *kobj,
> + struct bin_attribute *attr,
> + char *buf, loff_t off, size_t count)
> +{
> + struct ram_area *ram = attr->private;
> + struct device *dev = kobj_to_dev(kobj);
> + struct platform_device *pdev = to_platform_device(dev);
> + struct secumod_device *secumod = platform_get_drvdata(pdev);
> +
> + if (off < 0 || off >= ram->size)
> + return -EINVAL;
> + if (off + count > ram->size)
> + count = ram->size = off;
> +
> + if (count > 0) {
> + spin_lock(&secumod->lock);
> + memcpy_fromio(buf, ram->regs + off, count);
> + spin_unlock(&secumod->lock);
> + }
> + return count;
> +}
> +
> +static void
> +secumod_register_ram(struct secumod_device *secumod,
> + const char *name, unsigned long addr, size_t size)
> +{
> + struct device *dev = &secumod->pdev->dev;
> + struct ram_area *ram;
> + int ret;
> +
> + ram = devm_kzalloc(dev, sizeof(*ram), GFP_KERNEL);
> + if (!ram) {
> + dev_err(dev, "Out of memory for RAM area %s\n", name);
> + return;
> + }
> + INIT_LIST_HEAD(&ram->next);
> + ram->size = size;
> + ram->regs = ioremap_nocache(addr, size);
> + ram->attr.attr.name = name;
> + ram->attr.attr.mode = S_IRUGO | S_IWUSR;
> + ram->attr.private = ram;
> + ram->attr.size = size;
> + ram->attr.read = secumod_ram_read;
> + ram->attr.write = secumod_ram_write;
> + ret = device_create_bin_file(dev, &ram->attr);
> + if (ret) {
> + dev_err(dev, "Failed to register RAM area %s (%d)", name, ret);
> + return;
> + }
> + spin_lock(&secumod->lock);
> + list_add_tail(&ram->next, &secumod->ram_areas);
> + spin_unlock(&secumod->lock);
> + pr_info("atmel-secumod: RAM 0x%08lx-0x%08lx (%s)\n",
> + addr, addr + size - 1, name);
> +}
> +
> +/*
> + * Since the secure module may need to automatically erase some of the
> + * RAM, it may take a while for it to be ready. As far as I know,
> + * it's not documented how long this might take in the worst-case.
> + */
> +static void
> +secumod_wait_ready (struct secumod_device *secumod)
> +{
> + unsigned long start, stop;
> +
> + start = jiffies;
> + while (!(readl(secumod->regs + SECUMOD_RAMRDY) & 1))
> + msleep_interruptible(1);
> + stop = jiffies;
> + if (stop != start)
> + pr_info("atmel-secumod: it took %u msec for SECUMOD "
> + "to become ready...\n", jiffies_to_msecs(stop - start));
> +}
> +
> +static int
> +secumod_probe(struct platform_device *pdev)
> +{
> + struct secumod_device *secumod;
> + struct device_node *child;
> + struct resource *regs;
> + u32 addr, size;
> + const void *pv;
> + int len;
> +
> + secumod = devm_kzalloc(&pdev->dev, sizeof(*secumod), GFP_KERNEL);
> + if (!secumod) {
> + dev_err(&pdev->dev, "out of memory\n");
> + return -ENOMEM;
> + }
> + spin_lock_init(&secumod->lock);
> + INIT_LIST_HEAD(&secumod->ram_areas);
> +
> + if (!pdev->dev.of_node) {
> + dev_err(&pdev->dev, "Missing of_node attribute\n");
> + return -ENODEV;
> + }
> +
> + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!regs) {
> + dev_err(&pdev->dev, "Missing secumod resources.\n");
> + return -ENODEV;
> + }
> + secumod->pdev = pdev;
> + secumod->regs = devm_ioremap_resource(&pdev->dev, regs);
> +
> + platform_set_drvdata(pdev, secumod);
> +
> + for_each_child_of_node(pdev->dev.of_node, child) {
> + pv = of_get_property(child, "reg", &len);
> + if (!pv ||
> + of_n_addr_cells(child) != 1 ||
> + of_n_size_cells(child) != 1) {
> + dev_err(&pdev->dev, "Node %s missing \"reg\" property "
> + "or incorrect address/size count.",
> + child->name);
> + return -ENODEV;
> + }
> + addr = be32_to_cpup(pv);
> + size = be32_to_cpup(pv + 4);
> + secumod_register_ram(secumod, child->name, addr, size);
> + }
> + secumod_wait_ready(secumod);
> + return 0;
> +}
> +
> +static int
> +secumod_remove(struct platform_device *pdev)
> +{
> + struct secumod_device *secumod = platform_get_drvdata(pdev);
> + struct ram_area *ram;
> +
> + spin_lock(&secumod->lock);
> + list_for_each_entry(ram, &secumod->ram_areas, next)
> + device_remove_bin_file(&pdev->dev, &ram->attr);
> + spin_unlock(&secumod->lock);
> + return 0;
> +}
> +
> +static const struct of_device_id secumod_dt_ids[] = {
> + {
> + .compatible = "atmel,sama5d2-secumod"
> + },
> + {
> + /* sentinel */
> + }
> +};
> +MODULE_DEVICE_TABLE(of, secumod_dt_ids);
> +
> +static struct platform_driver secumod_driver = {
> + .driver = {
> + .name = "atmel-secumod",
> + .of_match_table = of_match_ptr(secumod_dt_ids),
> + },
> + .probe = secumod_probe,
> + .remove = secumod_remove
> +};
> +module_platform_driver(secumod_driver);
> +
> +MODULE_AUTHOR("David Mosberger <davidm@egauge.net>");
> +MODULE_DESCRIPTION("Atmel SAMA5D2 secure module driver");
> +MODULE_LICENSE("GPL v2");
> --
> 1.9.1
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

--
Alexandre Belloni, Free Electrons
Embedded Linux, Kernel and Android engineering