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.rey...@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, &reg, 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
-- 
2.17.1

Reply via email to