From mboxrd@z Thu Jan 1 00:00:00 1970 Return-path: Received: from metis.ext.pengutronix.de ([2001:67c:670:201:290:27ff:fe1d:cc33]) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1crIxF-0007L8-LC for barebox@lists.infradead.org; Fri, 24 Mar 2017 06:44:12 +0000 Date: Fri, 24 Mar 2017 07:43:47 +0100 From: Sascha Hauer Message-ID: <20170324064347.krrhwsfsltcn5g7c@pengutronix.de> References: <20170323101938.10425-1-o.rempel@pengutronix.de> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <20170323101938.10425-1-o.rempel@pengutronix.de> List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "barebox" Errors-To: barebox-bounces+u.kleine-koenig=pengutronix.de@lists.infradead.org Subject: Re: [PATCH v1] i.MX: hab: Add HAB fusebox related convenience functions / command To: Oleksij Rempel Cc: barebox@lists.infradead.org On Thu, Mar 23, 2017 at 11:19:38AM +0100, Oleksij Rempel wrote: > From: Sascha Hauer > > 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 > Signed-off-by: Oleksij Rempel > --- > commands/Kconfig | 5 + > commands/Makefile | 1 + > commands/hab.c | 120 +++++++++++++++++ > drivers/hab/Makefile | 1 + > drivers/hab/hab.c | 358 +++++++++++++++++++++++++++++++++++++++++++++++++++ > include/hab.h | 21 +++ > 6 files changed, 506 insertions(+) > create mode 100644 commands/hab.c > create mode 100644 drivers/hab/hab.c Applied, thanks Sascha > > diff --git a/commands/Kconfig b/commands/Kconfig > index 21d921268..d39db9546 100644 > --- a/commands/Kconfig > +++ b/commands/Kconfig > @@ -1930,6 +1930,11 @@ config CMD_WD_DEFAULT_TIMOUT > 'wd' is done without a timeout value (which means the watchdog gets > enabled and re-triggered with the default timeout value). > > +config CMD_HAB > + bool > + depends on HAB > + prompt "High Assurance boot (hab)" > + > # end Hardware manipulation commands > endmenu > > diff --git a/commands/Makefile b/commands/Makefile > index 601f15fc3..a15d36269 100644 > --- a/commands/Makefile > +++ b/commands/Makefile > @@ -87,6 +87,7 @@ obj-$(CONFIG_CMD_AUTOMOUNT) += automount.o > obj-$(CONFIG_CMD_GLOBAL) += global.o > obj-$(CONFIG_CMD_DMESG) += dmesg.o > obj-$(CONFIG_CMD_BASENAME) += basename.o > +obj-$(CONFIG_CMD_HAB) += hab.o > obj-$(CONFIG_CMD_DIRNAME) += dirname.o > obj-$(CONFIG_CMD_READLINK) += readlink.o > obj-$(CONFIG_CMD_LET) += let.o > diff --git a/commands/hab.c b/commands/hab.c > new file mode 100644 > index 000000000..0d7ee8e76 > --- /dev/null > +++ b/commands/hab.c > @@ -0,0 +1,120 @@ > +/* > + * 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. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +static int do_hab(int argc, char *argv[]) > +{ > + int opt, ret, i; > + char *srkhashfile = NULL, *srkhash = NULL; > + unsigned flags = 0; > + u8 srk[SRK_HASH_SIZE]; > + int lockdown = 0, info = 0; > + > + while ((opt = getopt(argc, argv, "s:fpx:li")) > 0) { > + switch (opt) { > + case 's': > + srkhashfile = optarg; > + break; > + case 'f': > + flags |= IMX_SRK_HASH_FORCE; > + break; > + case 'p': > + flags |= IMX_SRK_HASH_WRITE_PERMANENT; > + break; > + case 'x': > + srkhash = optarg; > + break; > + case 'l': > + lockdown = 1; > + break; > + case 'i': > + info = 1; > + break; > + default: > + return COMMAND_ERROR_USAGE; > + } > + } > + > + if (!info && !lockdown && !srkhashfile && !srkhash) { > + printf("Nothing to do\n"); > + return COMMAND_ERROR_USAGE; > + } > + > + if (info) { > + ret = imx_hab_read_srk_hash(srk); > + if (ret) > + return ret; > + > + printf("Current SRK hash: "); > + for (i = 0; i < SRK_HASH_SIZE; i++) > + printf("%02x", srk[i]); > + printf("\n"); > + > + if (imx_hab_device_locked_down()) > + printf("secure mode\n"); > + else > + printf("devel mode\n"); > + > + return 0; > + } > + > + if (srkhashfile && srkhash) { > + printf("-s and -x options may not be given together\n"); > + return COMMAND_ERROR_USAGE; > + } > + > + if (srkhashfile) { > + ret = imx_hab_write_srk_hash_file(srkhashfile, flags); > + if (ret) > + return ret; > + } else if (srkhash) { > + ret = imx_hab_write_srk_hash_hex(srkhash, flags); > + if (ret) > + return ret; > + } > + > + if (lockdown) { > + ret = imx_hab_lockdown_device(flags); > + if (ret) > + return ret; > + printf("Device successfully locked down\n"); > + } > + > + return 0; > +} > + > +BAREBOX_CMD_HELP_START(hab) > +BAREBOX_CMD_HELP_TEXT("Handle i.MX HAB (High Assurance Boot)") > +BAREBOX_CMD_HELP_TEXT("") > +BAREBOX_CMD_HELP_OPT ("-s ", "Burn Super Root Key hash from ") > +BAREBOX_CMD_HELP_OPT ("-x ", "Burn Super Root Key hash from hex string") > +BAREBOX_CMD_HELP_OPT ("-i", "Print HAB info") > +BAREBOX_CMD_HELP_OPT ("-f", "Force. Write even when a key is already written") > +BAREBOX_CMD_HELP_OPT ("-l", "Lockdown device. Dangerous! After executing only signed images can be booted") > +BAREBOX_CMD_HELP_OPT ("-p", "Permanent. Really burn fuses. Be careful!") > +BAREBOX_CMD_HELP_END > + > +BAREBOX_CMD_START(hab) > + .cmd = do_hab, > + BAREBOX_CMD_DESC("Handle i.MX HAB") > + BAREBOX_CMD_OPTS("sxfp") > + BAREBOX_CMD_GROUP(CMD_GRP_HWMANIP) > + BAREBOX_CMD_HELP(cmd_hab_help) > +BAREBOX_CMD_END > diff --git a/drivers/hab/Makefile b/drivers/hab/Makefile > index 8528ef954..b169a1346 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 000000000..56d49e8e3 > --- /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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +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(IMX6_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(IMX6_OCOTP_SRK_HASH(i), newsrk[i]); > + if (ret < 0) > + return ret; > + } > + > + if (flags & IMX_SRK_HASH_WRITE_LOCK) { > + ret = imx_ocotp_write_field(IMX6_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(IMX6_OCOTP_SEC_CONFIG, 1); > +} > + > +static int imx_hab_device_locked_down_ocotp(void) > +{ > + int ret; > + unsigned int v; > + > + ret = imx_ocotp_read_field(IMX6_OCOTP_SEC_CONFIG, &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(); > +} > diff --git a/include/hab.h b/include/hab.h > index 818d7ca1c..fb7149ef5 100644 > --- a/include/hab.h > +++ b/include/hab.h > @@ -41,4 +41,25 @@ static inline int imx25_hab_get_status(void) > } > #endif > > +#define SRK_HASH_SIZE 32 > + > +/* Force writing of key, even when a key is already written */ > +#define IMX_SRK_HASH_FORCE (1 << 0) > +/* Permanently write fuses, without this flag only the shadow registers > + * are written. > + */ > +#define IMX_SRK_HASH_WRITE_PERMANENT (1 << 1) > +/* When writing the super root key hash, also burn the write protection > + * fuses so that the key hash can not be modified. > + */ > +#define IMX_SRK_HASH_WRITE_LOCK (1 << 2) > + > +bool imx_hab_srk_hash_valid(const void *buf); > +int imx_hab_write_srk_hash(const void *buf, unsigned flags); > +int imx_hab_write_srk_hash_hex(const char *srkhash, unsigned flags); > +int imx_hab_write_srk_hash_file(const char *filename, unsigned flags); > +int imx_hab_read_srk_hash(void *buf); > +int imx_hab_lockdown_device(unsigned flags); > +int imx_hab_device_locked_down(void); > + > #endif /* __HABV4_H */ > -- > 2.11.0 > > -- Pengutronix e.K. | | Industrial Linux Solutions | http://www.pengutronix.de/ | Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox