aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/eeprom
diff options
context:
space:
mode:
authorOliver Schinagl <oliver@schinagl.nl>2013-09-03 06:33:27 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-09-26 16:47:36 -0400
commit9fd379e929a2808208b1d2d4cd9697067e83a242 (patch)
treee3e7c5c4f7e2b0a3fb82d70f29b43287196cacac /drivers/misc/eeprom
parent269ce62bbc00c4e80bf3ca2aa21823f20625bcf6 (diff)
ARM: sunxi: Initial support for Allwinner's Security ID fuses
Allwinner has electric fuses (efuse) on their line of chips. This driver reads those fuses, seeds the kernel entropy and exports them as a sysfs node. These fuses are most likely to be programmed at the factory, encoding things like Chip ID, some sort of serial number, etc. and appear to be reasonably unique. While in theory, these should be writeable by the user, it will probably be inconvenient to do so. Allwinner recommends that a certain input pin, labeled 'efuse_vddq', be connected to GND. To write these fuses however, a 2.5 V programming voltage needs to be applied to this pin. Even so, they can still be used to generate a board-unique mac from, board unique RSA key and seed the kernel RNG. On sun7i additional storage is available, this is initially used for an UEFI BOOT key, Secure JTAG key, HDMI-HDCP key and vendor specific keys. Currently supported are the following known chips: Allwinner sun4i (A10) Allwinner sun5i (A10s, A13) Allwinner sun7i (A20) Signed-off-by: Oliver Schinagl <oliver@schinagl.nl> Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/misc/eeprom')
-rw-r--r--drivers/misc/eeprom/Kconfig13
-rw-r--r--drivers/misc/eeprom/Makefile1
-rw-r--r--drivers/misc/eeprom/sunxi_sid.c158
3 files changed, 172 insertions, 0 deletions
diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig
index 04f2e1fa9dd1..9536852fd4c6 100644
--- a/drivers/misc/eeprom/Kconfig
+++ b/drivers/misc/eeprom/Kconfig
@@ -96,4 +96,17 @@ config EEPROM_DIGSY_MTC_CFG
96 96
97 If unsure, say N. 97 If unsure, say N.
98 98
99config EEPROM_SUNXI_SID
100 tristate "Allwinner sunxi security ID support"
101 depends on ARCH_SUNXI && SYSFS
102 help
103 This is a driver for the 'security ID' available on various Allwinner
104 devices.
105
106 Due to the potential risks involved with changing e-fuses,
107 this driver is read-only.
108
109 This driver can also be built as a module. If so, the module
110 will be called sunxi_sid.
111
99endmenu 112endmenu
diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile
index fc1e81d29267..9507aec95e94 100644
--- a/drivers/misc/eeprom/Makefile
+++ b/drivers/misc/eeprom/Makefile
@@ -4,4 +4,5 @@ obj-$(CONFIG_EEPROM_LEGACY) += eeprom.o
4obj-$(CONFIG_EEPROM_MAX6875) += max6875.o 4obj-$(CONFIG_EEPROM_MAX6875) += max6875.o
5obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o 5obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o
6obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o 6obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o
7obj-$(CONFIG_EEPROM_SUNXI_SID) += sunxi_sid.o
7obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o 8obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o
diff --git a/drivers/misc/eeprom/sunxi_sid.c b/drivers/misc/eeprom/sunxi_sid.c
new file mode 100644
index 000000000000..9c34e5704304
--- /dev/null
+++ b/drivers/misc/eeprom/sunxi_sid.c
@@ -0,0 +1,158 @@
1/*
2 * Copyright (c) 2013 Oliver Schinagl <oliver@schinagl.nl>
3 * http://www.linux-sunxi.org
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * This driver exposes the Allwinner security ID, efuses exported in byte-
16 * sized chunks.
17 */
18
19#include <linux/compiler.h>
20#include <linux/device.h>
21#include <linux/err.h>
22#include <linux/export.h>
23#include <linux/fs.h>
24#include <linux/init.h>
25#include <linux/io.h>
26#include <linux/kernel.h>
27#include <linux/kobject.h>
28#include <linux/module.h>
29#include <linux/of_device.h>
30#include <linux/platform_device.h>
31#include <linux/random.h>
32#include <linux/slab.h>
33#include <linux/stat.h>
34#include <linux/sysfs.h>
35#include <linux/types.h>
36
37#define DRV_NAME "sunxi-sid"
38
39struct sunxi_sid_data {
40 void __iomem *reg_base;
41 unsigned int keysize;
42};
43
44/* We read the entire key, due to a 32 bit read alignment requirement. Since we
45 * want to return the requested byte, this results in somewhat slower code and
46 * uses 4 times more reads as needed but keeps code simpler. Since the SID is
47 * only very rarely probed, this is not really an issue.
48 */
49static u8 sunxi_sid_read_byte(const struct sunxi_sid_data *sid_data,
50 const unsigned int offset)
51{
52 u32 sid_key;
53
54 if (offset >= sid_data->keysize)
55 return 0;
56
57 sid_key = ioread32be(sid_data->reg_base + round_down(offset, 4));
58 sid_key >>= (offset % 4) * 8;
59
60 return sid_key; /* Only return the last byte */
61}
62
63static ssize_t sid_read(struct file *fd, struct kobject *kobj,
64 struct bin_attribute *attr, char *buf,
65 loff_t pos, size_t size)
66{
67 struct platform_device *pdev;
68 struct sunxi_sid_data *sid_data;
69 int i;
70
71 pdev = to_platform_device(kobj_to_dev(kobj));
72 sid_data = platform_get_drvdata(pdev);
73
74 if (pos < 0 || pos >= sid_data->keysize)
75 return 0;
76 if (size > sid_data->keysize - pos)
77 size = sid_data->keysize - pos;
78
79 for (i = 0; i < size; i++)
80 buf[i] = sunxi_sid_read_byte(sid_data, pos + i);
81
82 return i;
83}
84
85static struct bin_attribute sid_bin_attr = {
86 .attr = { .name = "eeprom", .mode = S_IRUGO, },
87 .read = sid_read,
88};
89
90static int sunxi_sid_remove(struct platform_device *pdev)
91{
92 device_remove_bin_file(&pdev->dev, &sid_bin_attr);
93 dev_dbg(&pdev->dev, "driver unloaded\n");
94
95 return 0;
96}
97
98static const struct of_device_id sunxi_sid_of_match[] = {
99 { .compatible = "allwinner,sun4i-sid", .data = (void *)16},
100 { .compatible = "allwinner,sun7i-a20-sid", .data = (void *)512},
101 {/* sentinel */},
102};
103MODULE_DEVICE_TABLE(of, sunxi_sid_of_match);
104
105static int sunxi_sid_probe(struct platform_device *pdev)
106{
107 struct sunxi_sid_data *sid_data;
108 struct resource *res;
109 const struct of_device_id *of_dev_id;
110 u8 *entropy;
111 unsigned int i;
112
113 sid_data = devm_kzalloc(&pdev->dev, sizeof(struct sunxi_sid_data),
114 GFP_KERNEL);
115 if (!sid_data)
116 return -ENOMEM;
117
118 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
119 sid_data->reg_base = devm_ioremap_resource(&pdev->dev, res);
120 if (IS_ERR(sid_data->reg_base))
121 return PTR_ERR(sid_data->reg_base);
122
123 of_dev_id = of_match_device(sunxi_sid_of_match, &pdev->dev);
124 if (!of_dev_id)
125 return -ENODEV;
126 sid_data->keysize = (int)of_dev_id->data;
127
128 platform_set_drvdata(pdev, sid_data);
129
130 sid_bin_attr.size = sid_data->keysize;
131 if (device_create_bin_file(&pdev->dev, &sid_bin_attr))
132 return -ENODEV;
133
134 entropy = kzalloc(sizeof(u8) * sid_data->keysize, GFP_KERNEL);
135 for (i = 0; i < sid_data->keysize; i++)
136 entropy[i] = sunxi_sid_read_byte(sid_data, i);
137 add_device_randomness(entropy, sid_data->keysize);
138 kfree(entropy);
139
140 dev_dbg(&pdev->dev, "loaded\n");
141
142 return 0;
143}
144
145static struct platform_driver sunxi_sid_driver = {
146 .probe = sunxi_sid_probe,
147 .remove = sunxi_sid_remove,
148 .driver = {
149 .name = DRV_NAME,
150 .owner = THIS_MODULE,
151 .of_match_table = sunxi_sid_of_match,
152 },
153};
154module_platform_driver(sunxi_sid_driver);
155
156MODULE_AUTHOR("Oliver Schinagl <oliver@schinagl.nl>");
157MODULE_DESCRIPTION("Allwinner sunxi security id driver");
158MODULE_LICENSE("GPL");