summaryrefslogtreecommitdiffstats
path: root/drivers/hab
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2017-03-23 11:19:38 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2017-04-04 08:44:29 +0200
commit9dc622d5622ceeedfc5e793a201cad029ff0f5ab (patch)
treeab76b409c306a42a62fb1a85db09d8122e9ac8f9 /drivers/hab
parentf98666122e3456115cbb0cb8bd730a87183deb98 (diff)
downloadbarebox-9dc622d5622ceeedfc5e793a201cad029ff0f5ab.tar.gz
barebox-9dc622d5622ceeedfc5e793a201cad029ff0f5ab.tar.xz
i.MX: hab: Add HAB fusebox related convenience functions / command
Secure boot with HAB requires handling of the super root key hash and actually locking down the device. The related information is stored in the i.MX fusebox device (IIM on older SoCs, OCOTP on newer SoCs). This patch adds several convenience functions to store and read the super root key hash and to lock down a SoC. Also we add a command to do this from the command line. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/hab')
-rw-r--r--drivers/hab/Makefile1
-rw-r--r--drivers/hab/hab.c358
2 files changed, 359 insertions, 0 deletions
diff --git a/drivers/hab/Makefile b/drivers/hab/Makefile
index 8528ef954f..b169a1346d 100644
--- a/drivers/hab/Makefile
+++ b/drivers/hab/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_HABV4) += habv4.o
obj-$(CONFIG_HABV3) += habv3.o
+obj-$(CONFIG_HAB) += hab.o
diff --git a/drivers/hab/hab.c b/drivers/hab/hab.c
new file mode 100644
index 0000000000..512ff7ecf2
--- /dev/null
+++ b/drivers/hab/hab.c
@@ -0,0 +1,358 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+#define pr_fmt(fmt) "HAB: " fmt
+
+#include <common.h>
+#include <fcntl.h>
+#include <environment.h>
+#include <libfile.h>
+#include <mach/generic.h>
+#include <hab.h>
+#include <regmap.h>
+#include <fs.h>
+#include <mach/iim.h>
+#include <mach/imx25-fusemap.h>
+#include <mach/ocotp.h>
+#include <mach/imx6-fusemap.h>
+
+bool imx_hab_srk_hash_valid(const void *buf)
+{
+ const u8 *srk = buf;
+ int all_zero = 1, all_ff = 1;
+ int i;
+
+ for (i = 0; i < SRK_HASH_SIZE; i++) {
+ if (srk[i] != 0x0)
+ all_zero = 0;
+ if (srk[i] != 0xff)
+ all_ff = 0;
+ }
+
+ return !all_zero && !all_ff;
+}
+
+static int imx_hab_read_srk_hash_iim(u8 *srk)
+{
+ int ret, i;
+ unsigned int val;
+
+ ret = imx_iim_read_field(IMX25_IIM_SRK0_HASH_0, &val);
+ if (ret < 0)
+ return ret;
+ srk[0] = val;
+
+ for (i = 1; i < SRK_HASH_SIZE; i++) {
+ ret = imx_iim_read_field(IMX25_IIM_SRK0_HASH_1_31(i), &val);
+ if (ret < 0)
+ return ret;
+ srk[i] = val;
+ }
+
+ return 0;
+}
+
+static int imx_hab_write_srk_hash_iim(const u8 *srk, unsigned flags)
+{
+ int ret, i;
+
+ ret = imx_iim_write_field(IMX25_IIM_SRK0_HASH_0, srk[0]);
+ if (ret < 0)
+ return ret;
+
+ for (i = 1; i < SRK_HASH_SIZE; i++) {
+ ret = imx_iim_write_field(IMX25_IIM_SRK0_HASH_1_31(i), srk[i]);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (flags & IMX_SRK_HASH_WRITE_PERMANENT) {
+ u8 verify[SRK_HASH_SIZE];
+
+ setenv("iim.explicit_sense_enable", "1");
+ ret = imx_hab_read_srk_hash_iim(verify);
+ if (ret)
+ return ret;
+ setenv("iim.explicit_sense_enable", "0");
+
+ if (memcmp(srk, verify, SRK_HASH_SIZE))
+ return -EIO;
+ }
+
+ if (flags & IMX_SRK_HASH_WRITE_LOCK) {
+ ret = imx_iim_write_field(IMX25_IIM_SRK0_LOCK96, 1);
+ if (ret < 0)
+ return ret;
+ ret = imx_iim_write_field(IMX25_IIM_SRK0_LOCK160, 1);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int imx_hab_permanent_write_enable_iim(int enable)
+{
+ return imx_iim_permanent_write(enable);
+}
+
+static int imx_hab_lockdown_device_iim(void)
+{
+ return imx_iim_write_field(IMX25_IIM_HAB_TYPE, 3);
+}
+
+static int imx_hab_device_locked_down_iim(void)
+{
+ int ret;
+ unsigned int v;
+
+ ret = imx_iim_read_field(IMX25_IIM_HAB_TYPE, &v);
+ if (ret < 0)
+ return ret;
+
+ return v == 1 ? false : true;
+}
+
+static int imx_hab_read_srk_hash_ocotp(u8 *__srk)
+{
+ u32 *srk = (u32 *)__srk;
+ int ret, i;
+
+ for (i = 0; i < SRK_HASH_SIZE / sizeof(uint32_t); i++) {
+ ret = imx_ocotp_read_field(OCOTP_SRK_HASH(i), &srk[i]);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int imx_hab_write_srk_hash_ocotp(const u8 *__newsrk, unsigned flags)
+{
+ u32 *newsrk = (u32 *)__newsrk;
+ int ret, i;
+
+ for (i = 0; i < SRK_HASH_SIZE / sizeof(uint32_t); i++) {
+ ret = imx_ocotp_write_field(OCOTP_SRK_HASH(i), newsrk[i]);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (flags & IMX_SRK_HASH_WRITE_LOCK) {
+ ret = imx_ocotp_write_field(OCOTP_SRK_LOCK, 1);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int imx_hab_permanent_write_enable_ocotp(int enable)
+{
+ return imx_ocotp_permanent_write(enable);
+}
+
+static int imx_hab_lockdown_device_ocotp(void)
+{
+ return imx_ocotp_write_field(OCOTP_SEC_CONFIG_1, 1);
+}
+
+static int imx_hab_device_locked_down_ocotp(void)
+{
+ int ret;
+ unsigned int v;
+
+ ret = imx_ocotp_read_field(OCOTP_SEC_CONFIG_1, &v);
+ if (ret < 0)
+ return ret;
+
+ return v;
+}
+
+struct imx_hab_ops {
+ int (*init)(void);
+ int (*write_srk_hash)(const u8 *srk, unsigned flags);
+ int (*read_srk_hash)(u8 *srk);
+ int (*permanent_write_enable)(int enable);
+ int (*lockdown_device)(void);
+ int (*device_locked_down)(void);
+};
+
+static struct imx_hab_ops imx_hab_ops_iim = {
+ .write_srk_hash = imx_hab_write_srk_hash_iim,
+ .read_srk_hash = imx_hab_read_srk_hash_iim,
+ .lockdown_device = imx_hab_lockdown_device_iim,
+ .device_locked_down = imx_hab_device_locked_down_iim,
+ .permanent_write_enable = imx_hab_permanent_write_enable_iim,
+};
+
+static struct imx_hab_ops imx_hab_ops_ocotp = {
+ .write_srk_hash = imx_hab_write_srk_hash_ocotp,
+ .read_srk_hash = imx_hab_read_srk_hash_ocotp,
+ .lockdown_device = imx_hab_lockdown_device_ocotp,
+ .device_locked_down = imx_hab_device_locked_down_ocotp,
+ .permanent_write_enable = imx_hab_permanent_write_enable_ocotp,
+};
+
+static struct imx_hab_ops *imx_get_hab_ops(void)
+{
+ static struct imx_hab_ops *ops, *tmp;
+ int ret;
+
+ if (ops)
+ return ops;
+
+ if (cpu_is_mx25() || cpu_is_mx35())
+ tmp = &imx_hab_ops_iim;
+ else if (cpu_is_mx6())
+ tmp = &imx_hab_ops_ocotp;
+ else
+ return NULL;
+
+ if (tmp->init) {
+ ret = tmp->init();
+ if (ret)
+ return NULL;
+ }
+
+ ops = tmp;
+
+ return ops;
+}
+
+int imx_hab_read_srk_hash(void *buf)
+{
+ struct imx_hab_ops *ops = imx_get_hab_ops();
+
+ if (!ops)
+ return -ENOSYS;
+
+ return ops->read_srk_hash(buf);
+}
+
+int imx_hab_write_srk_hash(const void *buf, unsigned flags)
+{
+ u8 cursrk[SRK_HASH_SIZE];
+ int ret;
+ struct imx_hab_ops *ops = imx_get_hab_ops();
+
+ if (!ops)
+ return -ENOSYS;
+
+ ret = ops->read_srk_hash(cursrk);
+ if (ret) {
+ pr_err("Cannot read current SRK hash: %s\n", strerror(-ret));
+ return ret;
+ }
+
+ if (imx_hab_srk_hash_valid(cursrk)) {
+ char *str = "Current SRK hash is valid";
+
+ if (flags & IMX_SRK_HASH_FORCE) {
+ pr_warn("%s, ignoring\n", str);
+ } else {
+ pr_err("%s, refusing to burn again\n", str);
+ return -EEXIST;
+ }
+ }
+
+ if (flags & IMX_SRK_HASH_WRITE_PERMANENT) {
+ ret = ops->permanent_write_enable(1);
+ if (ret)
+ return ret;
+ }
+
+ ret = ops->write_srk_hash(buf, flags);
+
+ if (flags & IMX_SRK_HASH_WRITE_PERMANENT)
+ ops->permanent_write_enable(0);
+
+ return ret;
+}
+
+int imx_hab_write_srk_hash_file(const char *filename, unsigned flags)
+{
+ int ret;
+ size_t size;
+ void *srk;
+
+ ret = read_file_2(filename, &size, &srk, FILESIZE_MAX);
+ if (ret)
+ return ret;
+
+ if (size != SRK_HASH_SIZE) {
+ pr_err("File has wrong size, must be %d bytes\n", SRK_HASH_SIZE);
+ return -EINVAL;
+ }
+
+ ret = imx_hab_write_srk_hash(srk, flags);
+
+ free(srk);
+
+ return ret;
+}
+
+int imx_hab_write_srk_hash_hex(const char *srkhash, unsigned flags)
+{
+ int ret;
+ u8 srk[SRK_HASH_SIZE];
+
+ if (strlen(srkhash) != SRK_HASH_SIZE * 2) {
+ pr_err("Invalid srk hash %s\n", srkhash);
+ return -EINVAL;
+ }
+
+ ret = hex2bin(srk, srkhash, SRK_HASH_SIZE);
+ if (ret < 0) {
+ pr_err("Invalid srk hash %s\n", srkhash);
+ return -EINVAL;
+ }
+
+ return imx_hab_write_srk_hash(srk, flags);
+}
+
+int imx_hab_lockdown_device(unsigned flags)
+{
+ struct imx_hab_ops *ops = imx_get_hab_ops();
+ u8 srk[SRK_HASH_SIZE];
+ int ret;
+
+ ret = imx_hab_read_srk_hash(srk);
+ if (ret)
+ return ret;
+
+ if (!imx_hab_srk_hash_valid(srk)) {
+ pr_err("No SRK hash burnt into fuses. Refusing to lock device\n");
+ return -EINVAL;
+ }
+
+ if (!ops)
+ return -ENOSYS;
+
+ if (flags & IMX_SRK_HASH_WRITE_PERMANENT) {
+ ret = ops->permanent_write_enable(1);
+ if (ret)
+ return ret;
+ }
+
+ ret = ops->lockdown_device();
+
+ if (flags & IMX_SRK_HASH_WRITE_PERMANENT)
+ ops->permanent_write_enable(0);
+
+ return ret;
+}
+
+int imx_hab_device_locked_down(void)
+{
+ struct imx_hab_ops *ops = imx_get_hab_ops();
+
+ return ops->device_locked_down();
+}