[RFC][PATCH 1/2] cmd: bootm: add a stage pre-load

This commit adds a stage pre-load that could check or modify the image provided to the bootm command.
For the moment, only a header with a signature is supported. This header has this format: - magic : 4 bytes - image size : 4 bytes - sig size : 4 bytes - signature : n bytes - padding : up to header size
The stage use a node /bootm/pre-load/sig to get some information: - header-size (mandatory) : size of the header - algo-name (mandatory) : name of the algo used to sign - padding-name : name of padding used to sign - mandatory : set to yes if this sig is mandatory - public-key : value of the public key
Before running the image, the stage pre-load check the signature provided in the header.
This is an initial support, later we could add the support of: - ciphering - uncompressing - ...
Signed-off-by: Philippe Reynes philippe.reynes@softathome.com --- cmd/Kconfig | 9 ++ cmd/bootm.c | 2 +- common/bootm.c | 258 ++++++++++++++++++++++++++++++++++++++++++++++++ include/image.h | 1 + 4 files changed, 269 insertions(+), 1 deletion(-)
diff --git a/cmd/Kconfig b/cmd/Kconfig index eff238cb38..086d2b7b74 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -194,6 +194,15 @@ config CMD_BOOTM help Boot an application image from the memory.
+config CMD_BOOTM_PRE_LOAD + bool "enable pre-load on bootm" + depends on CMD_BOOTM + default n + help + Enable support of stage pre-load for the bootm command. + This stage allow to check of modifty the image provided + to the bootm command. + config BOOTM_EFI bool "Support booting UEFI FIT images" depends on CMD_BOOTEFI && CMD_BOOTM && FIT diff --git a/cmd/bootm.c b/cmd/bootm.c index 81c6b93978..7a6299d8d8 100644 --- a/cmd/bootm.c +++ b/cmd/bootm.c @@ -126,7 +126,7 @@ int do_bootm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) }
return do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START | - BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER | + BOOTM_STATE_FINDOS | BOOTM_STATE_PRE_LOAD | BOOTM_STATE_FINDOTHER | BOOTM_STATE_LOADOS | #ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH BOOTM_STATE_RAMDISK | diff --git a/common/bootm.c b/common/bootm.c index defaed8426..37b1340023 100644 --- a/common/bootm.c +++ b/common/bootm.c @@ -42,6 +42,12 @@
#define IH_INITRD_ARCH IH_ARCH_DEFAULT
+#define BOOTM_PRE_LOAD_SIG_MAGIC 0x55425348 +#define BOOTM_PRE_LOAD_SIG_OFFSET_MAGIC 0 +#define BOOTM_PRE_LOAD_SIG_OFFSET_IMG_LEN 4 +#define BOOTM_PRE_LOAD_SIG_OFFSET_SIG_LEN 8 +#define BOOTM_PRE_LOAD_SIG_OFFSET_SIG 12 + #ifndef USE_HOSTCC
DECLARE_GLOBAL_DATA_PTR; @@ -87,6 +93,255 @@ static int bootm_start(struct cmd_tbl *cmdtp, int flag, int argc, return 0; }
+static ulong bootm_data_addr(int argc, char *const argv[]) +{ + ulong addr; + + if (argc > 0) + addr = simple_strtoul(argv[0], NULL, 16); + else + addr = image_load_addr; + + return addr; +} + +struct bootm_sig_header { + u32 magic; + u32 size; + u8 *sig; +}; + +struct bootm_sig_info { + ulong header_size; + char *algo_name; + char *padding_name; + u8 *key; + int key_len; + int mandatory; +}; + +/* + * This function gather information about the signature check + * that could be done in the pre-load stage of bootm. + * + * return: + * -1 => an error has occurred + * 0 => OK + * 1 => no setup + */ +static int bootm_pre_load_sig_setup(struct bootm_sig_info *info) +{ + const void *algo_name, *padding_name, *key, *mandatory; + const u32 *header_size; + int key_len; + int node, ret = 0; + + if (!info) { + printf("ERROR: info is NULL for bootm pre-load sig check\n"); + ret = -1; + goto out; + } + + memset(info, 0, sizeof(*info)); + + node = fdt_path_offset(gd->fdt_blob, "/bootm/pre-load/sig"); + if (node < 0) { + printf("INFO: no info for bootm pre-load sig check\n"); + ret = 1; + goto out; + } + + header_size = fdt_getprop(gd->fdt_blob, node, "header-size", NULL); + if (!header_size) { + printf("ERROR: no header-size for bootm pre-load sig check\n"); + ret = -1; + goto out; + } + + algo_name = fdt_getprop(gd->fdt_blob, node, "algo-name", NULL); + if (!algo_name) { + printf("ERROR: no algo_name for bootm pre-load sig check\n"); + ret = -1; + goto out; + } + + padding_name = fdt_getprop(gd->fdt_blob, node, "padding-name", NULL); + if (!padding_name) { + printf("INFO: no padding_name provided, so using pkcs-1.5\n"); + padding_name = "pkcs-1.5"; + } + + key = fdt_getprop(gd->fdt_blob, node, "public-key", &key_len); + if (!key) { + printf("ERROR: no key for bootm pre-load sig check\n"); + ret = -1; + goto out; + } + + info->header_size = fdt32_to_cpu(*header_size); + info->algo_name = (char *)algo_name; + info->padding_name = (char *)padding_name; + info->key = (uint8_t *)key; + info->key_len = key_len; + + mandatory = fdt_getprop(gd->fdt_blob, node, "mandatory", NULL); + if (mandatory && !strcmp((char *)mandatory, "yes")) + info->mandatory = 1; + + out: + return ret; +} + +static int bootm_pre_load_sig_get_header_u32(struct bootm_sig_info *info, + ulong addr, u32 offset, + u32 *value) +{ + void *header; + u32 *tmp; + int ret = 0; + + header = map_sysmem(addr, info->header_size); + if (!header) { + printf("ERROR: can't map header bootm pre-load sig\n"); + ret = -1; + goto out; + } + + tmp = header + offset; + *value = be32_to_cpu(*tmp); + + unmap_sysmem(header); + + out: + return ret; +} + +static int bootm_pre_load_sig_get_magic(struct bootm_sig_info *info, + ulong addr, u32 *magic) +{ + int ret; + + ret = bootm_pre_load_sig_get_header_u32(info, addr, + BOOTM_PRE_LOAD_SIG_OFFSET_MAGIC, magic); + + return ret; +} + +static int bootm_pre_load_sig_get_img_len(struct bootm_sig_info *info, + ulong addr, u32 *len) +{ + int ret; + + ret = bootm_pre_load_sig_get_header_u32(info, addr, + BOOTM_PRE_LOAD_SIG_OFFSET_IMG_LEN, len); + if (ret < 0) + goto out; + + if (*len > CONFIG_SYS_BOOTM_LEN) { + printf("ERROR: size of image (%u) bigger than CONFIG_SYS_BOOTM_LEN (%u)\n", + *len, CONFIG_SYS_BOOTM_LEN); + ret = -1; + goto out; + } + + out: + return ret; +} + +static int bootm_pre_load_sig_check(struct bootm_sig_info *info, ulong addr, int img_len) +{ + void *image; + struct image_sign_info sig_info; + struct image_region reg; + u32 sig_len, *psig_len; + u8 *sig; + int ret = 0; + + image = (void *)map_sysmem(addr, info->header_size + img_len); + if (!image) { + printf("ERROR: can't map full image\n"); + ret = -1; + goto out; + } + + memset(&sig_info, '\0', sizeof(sig_info)); + sig_info.name = info->algo_name; + sig_info.padding = image_get_padding_algo(info->padding_name); + sig_info.checksum = image_get_checksum_algo(sig_info.name); + sig_info.crypto = image_get_crypto_algo(sig_info.name); + sig_info.key = info->key; + sig_info.keylen = info->key_len; + + reg.data = image + info->header_size; + reg.size = img_len; + + psig_len = (uint32_t *)((uint8_t *)image + BOOTM_PRE_LOAD_SIG_OFFSET_SIG_LEN); + sig_len = be32_to_cpu(*psig_len); + sig = (uint8_t *)image + BOOTM_PRE_LOAD_SIG_OFFSET_SIG; + + ret = sig_info.crypto->verify(&sig_info, ®, 1, sig, sig_len); + if (ret < 0) + printf("ERROR: signature check has failed (err=%d)\n", ret); + + memmove(image, image + info->header_size, img_len); + + unmap_sysmem(image); + + out: + return ret; +} + +static int bootm_pre_load_sig(ulong addr) +{ + struct bootm_sig_info info; + u32 magic, img_len; + int ret; + + ret = bootm_pre_load_sig_setup(&info); + if (ret < 0) + goto out; + if (ret > 0) { + ret = 0; + goto out; + } + + ret = bootm_pre_load_sig_get_magic(&info, addr, &magic); + if (ret < 0) + goto out; + + if (magic != BOOTM_PRE_LOAD_SIG_MAGIC) { + if (info.mandatory) { + printf("ERROR: signature is mandatory\n"); + ret = -1; + } + goto out; + } + + ret = bootm_pre_load_sig_get_img_len(&info, addr, &img_len); + if (ret < 0) + goto out; + + ret = bootm_pre_load_sig_check(&info, addr, img_len); + + out: + return ret; +} + +static int bootm_pre_load(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong data_addr = bootm_data_addr(argc, argv); + int ret = 0; + + if (CONFIG_IS_ENABLED(CMD_BOOTM_PRE_LOAD)) + ret = bootm_pre_load_sig(data_addr); + + if (ret) + ret = CMD_RET_FAILURE; + + return ret; +} + static int bootm_find_os(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -676,6 +931,9 @@ int do_bootm_states(struct cmd_tbl *cmdtp, int flag, int argc, if (states & BOOTM_STATE_START) ret = bootm_start(cmdtp, flag, argc, argv);
+ if (!ret && (states & BOOTM_STATE_PRE_LOAD)) + ret = bootm_pre_load(cmdtp, flag, argc, argv); + if (!ret && (states & BOOTM_STATE_FINDOS)) ret = bootm_find_os(cmdtp, flag, argc, argv);
diff --git a/include/image.h b/include/image.h index b4b284d52b..bd72b49913 100644 --- a/include/image.h +++ b/include/image.h @@ -432,6 +432,7 @@ typedef struct bootm_headers { #define BOOTM_STATE_OS_PREP (0x00000100) #define BOOTM_STATE_OS_FAKE_GO (0x00000200) /* 'Almost' run the OS */ #define BOOTM_STATE_OS_GO (0x00000400) +#define BOOTM_STATE_PRE_LOAD (0x00000800) int state;
#ifdef CONFIG_LMB

This commit enables the stage pre-load in the bootm command.
Signed-off-by: Philippe Reynes philippe.reynes@softathome.com --- configs/sandbox_defconfig | 1 + 1 file changed, 1 insertion(+)
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 5bc90d09a8..ab0e9213f8 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -27,6 +27,7 @@ CONFIG_DISPLAY_BOARDINFO_LATE=y CONFIG_ANDROID_AB=y CONFIG_CMD_CPU=y CONFIG_CMD_LICENSE=y +CONFIG_CMD_BOOTM_PRE_LOAD=y CONFIG_CMD_BOOTZ=y CONFIG_CMD_BOOTEFI_HELLO=y CONFIG_CMD_ABOOTIMG=y

On Wed, 31 Mar 2021 at 05:26, Philippe Reynes philippe.reynes@softathome.com wrote:
This commit enables the stage pre-load in the bootm command.
Signed-off-by: Philippe Reynes philippe.reynes@softathome.com
configs/sandbox_defconfig | 1 + 1 file changed, 1 insertion(+)
Reviewed-by: Simon Glass sjg@chromium.org

Hi Phillipe,
On 3/30/21 11:26 AM, Philippe Reynes wrote:
This commit adds a stage pre-load that could check or modify the image provided to the bootm command.
For the moment, only a header with a signature is supported. This header has this format:
- magic : 4 bytes
- image size : 4 bytes
- sig size : 4 bytes
- signature : n bytes
- padding : up to header size
The stage use a node /bootm/pre-load/sig to get some information:
- header-size (mandatory) : size of the header
- algo-name (mandatory) : name of the algo used to sign
- padding-name : name of padding used to sign
- mandatory : set to yes if this sig is mandatory
- public-key : value of the public key
Before running the image, the stage pre-load check the signature provided in the header.
This is an initial support, later we could add the support of:
- ciphering
- uncompressing
- ...
You're on the right path of what I had in mind.
One thing that we could improve is dropping the dependency on bootm. A FIT image could also be loaded with CONFIG_SPL_LOAD_FIT or CONFIG_SPL_LOAD_FIT_FULL. It would be nice to have the signature verification code shared with image-fit-sig.c
The decision to verify the "header signature" is done at kconfig time. For distinguishing between image or config node signing, the "required" property in the u-boot FDT is used. So it seems odd to introduce another mechanism instead of leveraging "required".
A nice to have: how does mkimage insert this header signature into the FIT?
Alex
Signed-off-by: Philippe Reynes philippe.reynes@softathome.com
cmd/Kconfig | 9 ++ cmd/bootm.c | 2 +- common/bootm.c | 258 ++++++++++++++++++++++++++++++++++++++++++++++++ include/image.h | 1 + 4 files changed, 269 insertions(+), 1 deletion(-)
diff --git a/cmd/Kconfig b/cmd/Kconfig index eff238cb38..086d2b7b74 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -194,6 +194,15 @@ config CMD_BOOTM help Boot an application image from the memory.
+config CMD_BOOTM_PRE_LOAD
bool "enable pre-load on bootm"
depends on CMD_BOOTM
default n
help
Enable support of stage pre-load for the bootm command.
This stage allow to check of modifty the image provided
to the bootm command.
- config BOOTM_EFI bool "Support booting UEFI FIT images" depends on CMD_BOOTEFI && CMD_BOOTM && FIT
diff --git a/cmd/bootm.c b/cmd/bootm.c index 81c6b93978..7a6299d8d8 100644 --- a/cmd/bootm.c +++ b/cmd/bootm.c @@ -126,7 +126,7 @@ int do_bootm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) }
return do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START |
BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER |
BOOTM_STATE_LOADOS | #ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH BOOTM_STATE_RAMDISK |BOOTM_STATE_FINDOS | BOOTM_STATE_PRE_LOAD | BOOTM_STATE_FINDOTHER |
diff --git a/common/bootm.c b/common/bootm.c index defaed8426..37b1340023 100644 --- a/common/bootm.c +++ b/common/bootm.c @@ -42,6 +42,12 @@
#define IH_INITRD_ARCH IH_ARCH_DEFAULT
+#define BOOTM_PRE_LOAD_SIG_MAGIC 0x55425348 +#define BOOTM_PRE_LOAD_SIG_OFFSET_MAGIC 0 +#define BOOTM_PRE_LOAD_SIG_OFFSET_IMG_LEN 4 +#define BOOTM_PRE_LOAD_SIG_OFFSET_SIG_LEN 8 +#define BOOTM_PRE_LOAD_SIG_OFFSET_SIG 12
#ifndef USE_HOSTCC
DECLARE_GLOBAL_DATA_PTR;
@@ -87,6 +93,255 @@ static int bootm_start(struct cmd_tbl *cmdtp, int flag, int argc, return 0; }
+static ulong bootm_data_addr(int argc, char *const argv[]) +{
- ulong addr;
- if (argc > 0)
addr = simple_strtoul(argv[0], NULL, 16);
- else
addr = image_load_addr;
- return addr;
+}
+struct bootm_sig_header {
- u32 magic;
- u32 size;
- u8 *sig;
+};
+struct bootm_sig_info {
- ulong header_size;
- char *algo_name;
- char *padding_name;
- u8 *key;
- int key_len;
- int mandatory;
+};
+/*
- This function gather information about the signature check
- that could be done in the pre-load stage of bootm.
- return:
- -1 => an error has occurred
- 0 => OK
- 1 => no setup
- */
+static int bootm_pre_load_sig_setup(struct bootm_sig_info *info) +{
- const void *algo_name, *padding_name, *key, *mandatory;
- const u32 *header_size;
- int key_len;
- int node, ret = 0;
- if (!info) {
printf("ERROR: info is NULL for bootm pre-load sig check\n");
ret = -1;
goto out;
- }
- memset(info, 0, sizeof(*info));
- node = fdt_path_offset(gd->fdt_blob, "/bootm/pre-load/sig");
- if (node < 0) {
printf("INFO: no info for bootm pre-load sig check\n");
ret = 1;
goto out;
- }
- header_size = fdt_getprop(gd->fdt_blob, node, "header-size", NULL);
- if (!header_size) {
printf("ERROR: no header-size for bootm pre-load sig check\n");
ret = -1;
goto out;
- }
- algo_name = fdt_getprop(gd->fdt_blob, node, "algo-name", NULL);
- if (!algo_name) {
printf("ERROR: no algo_name for bootm pre-load sig check\n");
ret = -1;
goto out;
- }
- padding_name = fdt_getprop(gd->fdt_blob, node, "padding-name", NULL);
- if (!padding_name) {
printf("INFO: no padding_name provided, so using pkcs-1.5\n");
padding_name = "pkcs-1.5";
- }
- key = fdt_getprop(gd->fdt_blob, node, "public-key", &key_len);
- if (!key) {
printf("ERROR: no key for bootm pre-load sig check\n");
ret = -1;
goto out;
- }
- info->header_size = fdt32_to_cpu(*header_size);
- info->algo_name = (char *)algo_name;
- info->padding_name = (char *)padding_name;
- info->key = (uint8_t *)key;
- info->key_len = key_len;
- mandatory = fdt_getprop(gd->fdt_blob, node, "mandatory", NULL);
- if (mandatory && !strcmp((char *)mandatory, "yes"))
info->mandatory = 1;
- out:
- return ret;
+}
+static int bootm_pre_load_sig_get_header_u32(struct bootm_sig_info *info,
ulong addr, u32 offset,
u32 *value)
+{
- void *header;
- u32 *tmp;
- int ret = 0;
- header = map_sysmem(addr, info->header_size);
- if (!header) {
printf("ERROR: can't map header bootm pre-load sig\n");
ret = -1;
goto out;
- }
- tmp = header + offset;
- *value = be32_to_cpu(*tmp);
- unmap_sysmem(header);
- out:
- return ret;
+}
+static int bootm_pre_load_sig_get_magic(struct bootm_sig_info *info,
ulong addr, u32 *magic)
+{
- int ret;
- ret = bootm_pre_load_sig_get_header_u32(info, addr,
BOOTM_PRE_LOAD_SIG_OFFSET_MAGIC, magic);
- return ret;
+}
+static int bootm_pre_load_sig_get_img_len(struct bootm_sig_info *info,
ulong addr, u32 *len)
+{
- int ret;
- ret = bootm_pre_load_sig_get_header_u32(info, addr,
BOOTM_PRE_LOAD_SIG_OFFSET_IMG_LEN, len);
- if (ret < 0)
goto out;
- if (*len > CONFIG_SYS_BOOTM_LEN) {
printf("ERROR: size of image (%u) bigger than CONFIG_SYS_BOOTM_LEN (%u)\n",
*len, CONFIG_SYS_BOOTM_LEN);
ret = -1;
goto out;
- }
- out:
- return ret;
+}
+static int bootm_pre_load_sig_check(struct bootm_sig_info *info, ulong addr, int img_len) +{
- void *image;
- struct image_sign_info sig_info;
- struct image_region reg;
- u32 sig_len, *psig_len;
- u8 *sig;
- int ret = 0;
- image = (void *)map_sysmem(addr, info->header_size + img_len);
- if (!image) {
printf("ERROR: can't map full image\n");
ret = -1;
goto out;
- }
- memset(&sig_info, '\0', sizeof(sig_info));
- sig_info.name = info->algo_name;
- sig_info.padding = image_get_padding_algo(info->padding_name);
- sig_info.checksum = image_get_checksum_algo(sig_info.name);
- sig_info.crypto = image_get_crypto_algo(sig_info.name);
- sig_info.key = info->key;
- sig_info.keylen = info->key_len;
- reg.data = image + info->header_size;
- reg.size = img_len;
- psig_len = (uint32_t *)((uint8_t *)image + BOOTM_PRE_LOAD_SIG_OFFSET_SIG_LEN);
- sig_len = be32_to_cpu(*psig_len);
- sig = (uint8_t *)image + BOOTM_PRE_LOAD_SIG_OFFSET_SIG;
- ret = sig_info.crypto->verify(&sig_info, ®, 1, sig, sig_len);
- if (ret < 0)
printf("ERROR: signature check has failed (err=%d)\n", ret);
- memmove(image, image + info->header_size, img_len);
- unmap_sysmem(image);
- out:
- return ret;
+}
+static int bootm_pre_load_sig(ulong addr) +{
- struct bootm_sig_info info;
- u32 magic, img_len;
- int ret;
- ret = bootm_pre_load_sig_setup(&info);
- if (ret < 0)
goto out;
- if (ret > 0) {
ret = 0;
goto out;
- }
- ret = bootm_pre_load_sig_get_magic(&info, addr, &magic);
- if (ret < 0)
goto out;
- if (magic != BOOTM_PRE_LOAD_SIG_MAGIC) {
if (info.mandatory) {
printf("ERROR: signature is mandatory\n");
ret = -1;
}
goto out;
- }
- ret = bootm_pre_load_sig_get_img_len(&info, addr, &img_len);
- if (ret < 0)
goto out;
- ret = bootm_pre_load_sig_check(&info, addr, img_len);
- out:
- return ret;
+}
+static int bootm_pre_load(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
+{
- ulong data_addr = bootm_data_addr(argc, argv);
- int ret = 0;
- if (CONFIG_IS_ENABLED(CMD_BOOTM_PRE_LOAD))
ret = bootm_pre_load_sig(data_addr);
- if (ret)
ret = CMD_RET_FAILURE;
- return ret;
+}
- static int bootm_find_os(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) {
@@ -676,6 +931,9 @@ int do_bootm_states(struct cmd_tbl *cmdtp, int flag, int argc, if (states & BOOTM_STATE_START) ret = bootm_start(cmdtp, flag, argc, argv);
- if (!ret && (states & BOOTM_STATE_PRE_LOAD))
ret = bootm_pre_load(cmdtp, flag, argc, argv);
- if (!ret && (states & BOOTM_STATE_FINDOS)) ret = bootm_find_os(cmdtp, flag, argc, argv);
diff --git a/include/image.h b/include/image.h index b4b284d52b..bd72b49913 100644 --- a/include/image.h +++ b/include/image.h @@ -432,6 +432,7 @@ typedef struct bootm_headers { #define BOOTM_STATE_OS_PREP (0x00000100) #define BOOTM_STATE_OS_FAKE_GO (0x00000200) /* 'Almost' run the OS */ #define BOOTM_STATE_OS_GO (0x00000400) +#define BOOTM_STATE_PRE_LOAD (0x00000800) int state;
#ifdef CONFIG_LMB
participants (3)
-
Alex G.
-
Philippe Reynes
-
Simon Glass