diff options
Diffstat (limited to 'boot')
-rw-r--r-- | boot/Kconfig | 73 | ||||
-rw-r--r-- | boot/Makefile | 6 | ||||
-rw-r--r-- | boot/bootdev-uclass.c | 78 | ||||
-rw-r--r-- | boot/bootflow.c | 77 | ||||
-rw-r--r-- | boot/bootmeth-uclass.c | 29 | ||||
-rw-r--r-- | boot/bootmeth_android.c | 42 | ||||
-rw-r--r-- | boot/bootmeth_cros.c | 17 | ||||
-rw-r--r-- | boot/bootmeth_efi.c | 80 | ||||
-rw-r--r-- | boot/bootmeth_efi_mgr.c | 3 | ||||
-rw-r--r-- | boot/bootmeth_extlinux.c | 8 | ||||
-rw-r--r-- | boot/bootmeth_pxe.c | 10 | ||||
-rw-r--r-- | boot/bootmeth_qfw.c | 3 | ||||
-rw-r--r-- | boot/bootmeth_sandbox.c | 3 | ||||
-rw-r--r-- | boot/bootmeth_script.c | 7 | ||||
-rw-r--r-- | boot/bootstd-uclass.c | 60 | ||||
-rw-r--r-- | boot/fdt_support.c | 7 | ||||
-rw-r--r-- | boot/image-android.c | 8 | ||||
-rw-r--r-- | boot/image-board.c | 4 | ||||
-rw-r--r-- | boot/image-fit.c | 83 | ||||
-rw-r--r-- | boot/pxe_utils.c | 36 | ||||
-rw-r--r-- | boot/vbe_abrec.c | 83 | ||||
-rw-r--r-- | boot/vbe_abrec.h | 115 | ||||
-rw-r--r-- | boot/vbe_abrec_fw.c | 276 | ||||
-rw-r--r-- | boot/vbe_common.c | 381 | ||||
-rw-r--r-- | boot/vbe_common.h | 180 | ||||
-rw-r--r-- | boot/vbe_simple.c | 103 | ||||
-rw-r--r-- | boot/vbe_simple.h | 16 | ||||
-rw-r--r-- | boot/vbe_simple_fw.c | 207 |
28 files changed, 1533 insertions, 462 deletions
diff --git a/boot/Kconfig b/boot/Kconfig index 20935a269c6..c09a98c3233 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -711,6 +711,20 @@ config BOOTMETH_VBE_SIMPLE firmware image in boot media such as MMC. It does not support any sort of rollback, recovery or A/B boot. +config BOOTMETH_VBE_ABREC + bool "Bootdev support for VBE 'a/b/recovery' method" + imply SPL_CRC8 + imply VPL_CRC8 + help + Enables support for VBE 'abrec' boot. This allows updating one of an + A or B firmware image in boot media such as MMC. The new firmware is + tried and if it boots, it is copied to the other image, so that both + A and B have the same version. If neither firmware image passes the + verification step, a recovery image is booted. This method will + eventually provide rollback protection as well. + +if BOOTMETH_VBE_SIMPLE + config BOOTMETH_VBE_SIMPLE_OS bool "Bootdev support for VBE 'simple' method OS phase" default y @@ -769,6 +783,65 @@ config TPL_BOOTMETH_VBE_SIMPLE_FW endif # BOOTMETH_VBE_SIMPLE +if BOOTMETH_VBE_ABREC + +config SPL_BOOTMETH_VBE_ABREC + bool "Bootdev support for VBE 'abrec' method (SPL)" + depends on SPL + default y if VPL + help + Enables support for VBE 'abrec' boot. The SPL part of this + implementation simply loads U-Boot from the image selected by the + VPL phase. + +config TPL_BOOTMETH_VBE_ABREC + bool "Bootdev support for VBE 'abrec' method (TPL)" + depends on TPL + select TPL_FIT + default y + help + Enables support for VBE 'abrec' boot. The TPL part of this + implementation simply jumps to VPL after device init is completed. + +config VPL_BOOTMETH_VBE_ABREC + bool "Bootdev support for VBE 'abrec' method (VPL)" + depends on VPL + default y + help + Enables support for VBE 'abrec' boot. The VPL part of this + implementation selects which SPL to use (A, B or recovery) and then + boots into SPL. + +config SPL_BOOTMETH_VBE_ABREC_FW + bool "Bootdev support for VBE 'abrec' method firmware phase (SPL)" + depends on SPL + default y if VPL + help + Enables support for VBE 'abrec' boot. The SPL part of this + implementation simply loads U-Boot from the image selected by the + VPL phase. + +config TPL_BOOTMETH_VBE_ABREC_FW + bool "Bootdev support for VBE 'abrec' method firmware phase (TPL)" + depends on TPL + default y if VPL + help + Enables support for VBE 'abrec' boot. The TPL part of this + implementation simply jumps to VPL after device init is completed. + +config VPL_BOOTMETH_VBE_ABREC_FW + bool "Bootdev support for VBE 'abrec' method firmware phase (VPL)" + depends on VPL + default y + help + Enables support for VBE 'abrec' boot. The VPL part of this + implementation selects which SPL to use (A, B or recovery) and then + boots into SPL. + +endif # BOOTMETH_VBE_ABREC + +endif # BOOTMETH_VBE + config EXPO bool "Support for expos - groups of scenes displaying a UI" depends on VIDEO diff --git a/boot/Makefile b/boot/Makefile index a24fd90c510..34bac26c4e2 100644 --- a/boot/Makefile +++ b/boot/Makefile @@ -65,8 +65,12 @@ endif obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE) += vbe.o obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_REQUEST) += vbe_request.o -obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE) += vbe_simple.o +obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE) += vbe_simple.o vbe_common.o obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_FW) += vbe_simple_fw.o obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_OS) += vbe_simple_os.o obj-$(CONFIG_$(PHASE_)BOOTMETH_ANDROID) += bootmeth_android.o + +obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC) += vbe_abrec.o vbe_common.o +obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC_FW) += vbe_abrec_fw.o +obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC_OS) += vbe_abrec_os.o diff --git a/boot/bootdev-uclass.c b/boot/bootdev-uclass.c index 2e61c853142..c39147940b6 100644 --- a/boot/bootdev-uclass.c +++ b/boot/bootdev-uclass.c @@ -33,55 +33,38 @@ enum { BOOT_TARGETS_MAX_LEN = 100, }; -int bootdev_add_bootflow(struct bootflow *bflow) +int bootdev_first_bootflow(struct udevice *dev, struct bootflow **bflowp) { struct bootstd_priv *std; - struct bootflow *new; + struct bootflow *bflow; int ret; ret = bootstd_get_priv(&std); if (ret) - return ret; - - new = malloc(sizeof(*bflow)); - if (!new) - return log_msg_ret("bflow", -ENOMEM); - memcpy(new, bflow, sizeof(*bflow)); - - list_add_tail(&new->glob_node, &std->glob_head); - if (bflow->dev) { - struct bootdev_uc_plat *ucp = dev_get_uclass_plat(bflow->dev); - - list_add_tail(&new->bm_node, &ucp->bootflow_head); - } - - return 0; -} + return log_msg_ret("bff", ret); -int bootdev_first_bootflow(struct udevice *dev, struct bootflow **bflowp) -{ - struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev); - - if (list_empty(&ucp->bootflow_head)) + bflow = alist_getw(&std->bootflows, 0, struct bootflow); + if (!bflow) return -ENOENT; - - *bflowp = list_first_entry(&ucp->bootflow_head, struct bootflow, - bm_node); + *bflowp = bflow; return 0; } int bootdev_next_bootflow(struct bootflow **bflowp) { - struct bootflow *bflow = *bflowp; - struct bootdev_uc_plat *ucp = dev_get_uclass_plat(bflow->dev); + struct bootstd_priv *std; + struct bootflow *bflow; + int ret; - *bflowp = NULL; + ret = bootstd_get_priv(&std); + if (ret) + return log_msg_ret("bff", ret); - if (list_is_last(&bflow->bm_node, &ucp->bootflow_head)) + bflow = alist_nextw(&std->bootflows, *bflowp); + if (!bflow) return -ENOENT; - - *bflowp = list_entry(bflow->bm_node.next, struct bootflow, bm_node); + *bflowp = bflow; return 0; } @@ -342,7 +325,7 @@ int bootdev_get_sibling_blk(struct udevice *dev, struct udevice **blkp) return 0; } -static int bootdev_get_from_blk(struct udevice *blk, struct udevice **bootdevp) +int bootdev_get_from_blk(struct udevice *blk, struct udevice **bootdevp) { struct udevice *parent = dev_get_parent(blk); struct udevice *bootdev; @@ -588,19 +571,6 @@ int bootdev_get_bootflow(struct udevice *dev, struct bootflow_iter *iter, return ops->get_bootflow(dev, iter, bflow); } -void bootdev_clear_bootflows(struct udevice *dev) -{ - struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev); - - while (!list_empty(&ucp->bootflow_head)) { - struct bootflow *bflow; - - bflow = list_first_entry(&ucp->bootflow_head, struct bootflow, - bm_node); - bootflow_remove(bflow); - } -} - int bootdev_next_label(struct bootflow_iter *iter, struct udevice **devp, int *method_flagsp) { @@ -955,18 +925,13 @@ void bootdev_list_hunters(struct bootstd_priv *std) printf("(total hunters: %d)\n", n_ent); } -static int bootdev_post_bind(struct udevice *dev) -{ - struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev); - - INIT_LIST_HEAD(&ucp->bootflow_head); - - return 0; -} - static int bootdev_pre_unbind(struct udevice *dev) { - bootdev_clear_bootflows(dev); + int ret; + + ret = bootstd_clear_bootflows_for_bootdev(dev); + if (ret) + return log_msg_ret("bun", ret); return 0; } @@ -976,6 +941,5 @@ UCLASS_DRIVER(bootdev) = { .name = "bootdev", .flags = DM_UC_FLAG_SEQ_ALIAS, .per_device_plat_auto = sizeof(struct bootdev_uc_plat), - .post_bind = bootdev_post_bind, .pre_unbind = bootdev_pre_unbind, }; diff --git a/boot/bootflow.c b/boot/bootflow.c index d8807eb109d..58a1afa7a75 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -23,6 +23,13 @@ enum { BF_NO_MORE_DEVICES = -ENODEV, }; +static const char *const bootflow_img[BFI_COUNT - BFI_FIRST] = { + "extlinux_cfg", + "logo", + "efi", + "cmdline", +}; + /** * bootflow_state - name for each state * @@ -55,11 +62,10 @@ int bootflow_first_glob(struct bootflow **bflowp) if (ret) return ret; - if (list_empty(&std->glob_head)) + if (!std->bootflows.count) return -ENOENT; - *bflowp = list_first_entry(&std->glob_head, struct bootflow, - glob_node); + *bflowp = alist_getw(&std->bootflows, 0, struct bootflow); return 0; } @@ -67,20 +73,16 @@ int bootflow_first_glob(struct bootflow **bflowp) int bootflow_next_glob(struct bootflow **bflowp) { struct bootstd_priv *std; - struct bootflow *bflow = *bflowp; int ret; ret = bootstd_get_priv(&std); if (ret) return ret; - *bflowp = NULL; - - if (list_is_last(&bflow->glob_node, &std->glob_head)) + *bflowp = alist_nextw(&std->bootflows, *bflowp); + if (!*bflowp) return -ENOENT; - *bflowp = list_entry(bflow->glob_node.next, struct bootflow, glob_node); - return 0; } @@ -460,10 +462,13 @@ void bootflow_init(struct bootflow *bflow, struct udevice *bootdev, bflow->dev = bootdev; bflow->method = meth; bflow->state = BOOTFLOWST_BASE; + alist_init_struct(&bflow->images, struct bootflow_img); } void bootflow_free(struct bootflow *bflow) { + struct bootflow_img *img; + free(bflow->name); free(bflow->subdir); free(bflow->fname); @@ -472,16 +477,15 @@ void bootflow_free(struct bootflow *bflow) free(bflow->os_name); free(bflow->fdt_fname); free(bflow->bootmeth_priv); + + alist_for_each(img, &bflow->images) + free(img->fname); + alist_empty(&bflow->images); } void bootflow_remove(struct bootflow *bflow) { - if (bflow->dev) - list_del(&bflow->bm_node); - list_del(&bflow->glob_node); - bootflow_free(bflow); - free(bflow); } #if CONFIG_IS_ENABLED(BOOTSTD_FULL) @@ -960,3 +964,48 @@ int bootflow_cmdline_auto(struct bootflow *bflow, const char *arg) return 0; } + +const char *bootflow_img_type_name(enum bootflow_img_t type) +{ + const char *name; + + if (type >= BFI_FIRST && type < BFI_COUNT) + name = bootflow_img[type - BFI_FIRST]; + else + name = genimg_get_type_short_name(type); + + return name; +} + +struct bootflow_img *bootflow_img_add(struct bootflow *bflow, const char *fname, + enum bootflow_img_t type, ulong addr, + ulong size) +{ + struct bootflow_img img, *ptr; + + memset(&img, '\0', sizeof(struct bootflow_img)); + img.fname = strdup(fname); + if (!img.fname) + return NULL; + + img.type = type; + img.addr = addr; + img.size = size; + ptr = alist_add(&bflow->images, img); + if (!ptr) + return NULL; + + return ptr; +} + +int bootflow_get_seq(const struct bootflow *bflow) +{ + struct bootstd_priv *std; + int ret; + + ret = bootstd_get_priv(&std); + if (ret) + return ret; + + return alist_calc_index(&std->bootflows, bflow); +} diff --git a/boot/bootmeth-uclass.c b/boot/bootmeth-uclass.c index 5b5fea39b3b..014b7588e8d 100644 --- a/boot/bootmeth-uclass.c +++ b/boot/bootmeth-uclass.c @@ -6,6 +6,7 @@ #define LOG_CATEGORY UCLASS_BOOTSTD +#include <alist.h> #include <blk.h> #include <bootflow.h> #include <bootmeth.h> @@ -83,14 +84,15 @@ int bootmeth_boot(struct udevice *dev, struct bootflow *bflow) } int bootmeth_read_file(struct udevice *dev, struct bootflow *bflow, - const char *file_path, ulong addr, ulong *sizep) + const char *file_path, ulong addr, + enum bootflow_img_t type, ulong *sizep) { const struct bootmeth_ops *ops = bootmeth_get_ops(dev); if (!ops->read_file) return -ENOSYS; - return ops->read_file(dev, bflow, file_path, addr, sizep); + return ops->read_file(dev, bflow, file_path, addr, type, sizep); } int bootmeth_get_bootflow(struct udevice *dev, struct bootflow *bflow) @@ -326,8 +328,10 @@ int bootmeth_try_file(struct bootflow *bflow, struct blk_desc *desc, return 0; } -int bootmeth_alloc_file(struct bootflow *bflow, uint size_limit, uint align) +int bootmeth_alloc_file(struct bootflow *bflow, uint size_limit, uint align, + enum bootflow_img_t type) { + struct blk_desc *desc = NULL; void *buf; uint size; int ret; @@ -344,11 +348,18 @@ int bootmeth_alloc_file(struct bootflow *bflow, uint size_limit, uint align) bflow->state = BOOTFLOWST_READY; bflow->buf = buf; + if (bflow->blk) + desc = dev_get_uclass_plat(bflow->blk); + + if (!bootflow_img_add(bflow, bflow->fname, type, map_to_sysmem(buf), + size)) + return log_msg_ret("bai", -ENOMEM); + return 0; } int bootmeth_alloc_other(struct bootflow *bflow, const char *fname, - void **bufp, uint *sizep) + enum bootflow_img_t type, void **bufp, uint *sizep) { struct blk_desc *desc = NULL; char path[200]; @@ -377,6 +388,10 @@ int bootmeth_alloc_other(struct bootflow *bflow, const char *fname, if (ret) return log_msg_ret("all", ret); + if (!bootflow_img_add(bflow, bflow->fname, type, map_to_sysmem(buf), + size)) + return log_msg_ret("boi", -ENOMEM); + *bufp = buf; *sizep = size; @@ -384,7 +399,8 @@ int bootmeth_alloc_other(struct bootflow *bflow, const char *fname, } int bootmeth_common_read_file(struct udevice *dev, struct bootflow *bflow, - const char *file_path, ulong addr, ulong *sizep) + const char *file_path, ulong addr, + enum bootflow_img_t type, ulong *sizep) { struct blk_desc *desc = NULL; loff_t len_read; @@ -413,6 +429,9 @@ int bootmeth_common_read_file(struct udevice *dev, struct bootflow *bflow, return ret; *sizep = len_read; + if (!bootflow_img_add(bflow, bflow->fname, type, addr, size)) + return log_msg_ret("bci", -ENOMEM); + return 0; } diff --git a/boot/bootmeth_android.c b/boot/bootmeth_android.c index 3a5144aaa3b..a5a86b29d7f 100644 --- a/boot/bootmeth_android.c +++ b/boot/bootmeth_android.c @@ -323,7 +323,8 @@ static int android_read_bootflow(struct udevice *dev, struct bootflow *bflow) } static int android_read_file(struct udevice *dev, struct bootflow *bflow, - const char *file_path, ulong addr, ulong *sizep) + const char *file_path, ulong addr, + enum bootflow_img_t type, ulong *sizep) { /* * Reading individual files is not supported since we only @@ -421,7 +422,7 @@ static int run_avb_verification(struct bootflow *bflow) { struct blk_desc *desc = dev_get_uclass_plat(bflow->blk); struct android_priv *priv = bflow->bootmeth_priv; - const char * const requested_partitions[] = {"boot", "vendor_boot"}; + const char * const requested_partitions[] = {"boot", "vendor_boot", NULL}; struct AvbOps *avb_ops; AvbSlotVerifyResult result; AvbSlotVerifyData *out_data; @@ -449,17 +450,26 @@ static int run_avb_verification(struct bootflow *bflow) AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &out_data); - if (result != AVB_SLOT_VERIFY_RESULT_OK) { - printf("Verification failed, reason: %s\n", - str_avb_slot_error(result)); - avb_slot_verify_data_free(out_data); - return log_msg_ret("avb verify", -EIO); - } - - if (unlocked) - boot_state = AVB_ORANGE; - else + if (!unlocked) { + /* When device is locked, we only accept AVB_SLOT_VERIFY_RESULT_OK */ + if (result != AVB_SLOT_VERIFY_RESULT_OK) { + printf("Verification failed, reason: %s\n", + str_avb_slot_error(result)); + avb_slot_verify_data_free(out_data); + return log_msg_ret("avb verify", -EIO); + } boot_state = AVB_GREEN; + } else { + /* When device is unlocked, we also accept verification errors */ + if (result != AVB_SLOT_VERIFY_RESULT_OK && + result != AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION) { + printf("Unlocked verification failed, reason: %s\n", + str_avb_slot_error(result)); + avb_slot_verify_data_free(out_data); + return log_msg_ret("avb verify unlocked", -EIO); + } + boot_state = AVB_ORANGE; + } extra_args = avb_set_state(avb_ops, boot_state); if (extra_args) { @@ -469,9 +479,11 @@ static int run_avb_verification(struct bootflow *bflow) goto free_out_data; } - ret = avb_append_commandline(bflow, out_data->cmdline); - if (ret < 0) - goto free_out_data; + if (result == AVB_SLOT_VERIFY_RESULT_OK) { + ret = avb_append_commandline(bflow, out_data->cmdline); + if (ret < 0) + goto free_out_data; + } return 0; diff --git a/boot/bootmeth_cros.c b/boot/bootmeth_cros.c index 676f550ca25..c7b862e512a 100644 --- a/boot/bootmeth_cros.c +++ b/boot/bootmeth_cros.c @@ -243,8 +243,17 @@ static int cros_read_buf(struct bootflow *bflow, void *buf, ulong size, ret = copy_cmdline(map_sysmem(cmdline, 0), uuid, &bflow->cmdline); if (ret) return log_msg_ret("cmd", ret); + + if (!bootflow_img_add(bflow, "setup", + (enum bootflow_img_t)IH_TYPE_X86_SETUP, + setup, 0x3000)) + return log_msg_ret("cri", -ENOMEM); + bflow->x86_setup = map_sysmem(setup, 0); + if (!bootflow_img_add(bflow, "cmdline", BFI_CMDLINE, cmdline, 0x1000)) + return log_msg_ret("crc", -ENOMEM); + return 0; } @@ -306,6 +315,11 @@ static int cros_read_info(struct bootflow *bflow, const char *uuid, } priv->info_buf = buf; + if (!bootflow_img_add(bflow, "kernel", + (enum bootflow_img_t)IH_TYPE_KERNEL, 0, + priv->body_size)) + return log_msg_ret("crk", -ENOMEM); + return 0; } @@ -400,7 +414,8 @@ static int cros_read_bootflow(struct udevice *dev, struct bootflow *bflow) } static int cros_read_file(struct udevice *dev, struct bootflow *bflow, - const char *file_path, ulong addr, ulong *sizep) + const char *file_path, ulong addr, + enum bootflow_img_t type, ulong *sizep) { return -ENOSYS; } diff --git a/boot/bootmeth_efi.c b/boot/bootmeth_efi.c index f836aa655f5..0c9b4c3d59d 100644 --- a/boot/bootmeth_efi.c +++ b/boot/bootmeth_efi.c @@ -52,59 +52,22 @@ static bool bootmeth_uses_network(struct bootflow *bflow) device_get_uclass_id(media) == UCLASS_ETH; } -static void set_efi_bootdev(struct blk_desc *desc, struct bootflow *bflow) -{ - const struct udevice *media_dev; - int size = bflow->size; - const char *dev_name; - char devnum_str[9]; - char dirname[200]; - char *last_slash; - - /* - * This is a horrible hack to tell EFI about this boot device. Once we - * unify EFI with the rest of U-Boot we can clean this up. The same hack - * exists in multiple places, e.g. in the fs, tftp and load commands. - * - * Once we can clean up the EFI code to make proper use of driver model, - * this can go away. - */ - media_dev = dev_get_parent(bflow->dev); - snprintf(devnum_str, sizeof(devnum_str), "%x:%x", - desc ? desc->devnum : dev_seq(media_dev), - bflow->part); - - strlcpy(dirname, bflow->fname, sizeof(dirname)); - last_slash = strrchr(dirname, '/'); - if (last_slash) - *last_slash = '\0'; - - dev_name = device_get_uclass_id(media_dev) == UCLASS_MASS_STORAGE ? - "usb" : blk_get_uclass_name(device_get_uclass_id(media_dev)); - log_debug("setting bootdev %s, %s, %s, %p, %x\n", - dev_name, devnum_str, bflow->fname, bflow->buf, size); - efi_set_bootdev(dev_name, devnum_str, bflow->fname, bflow->buf, size); -} - static int efiload_read_file(struct bootflow *bflow, ulong addr) { struct blk_desc *desc = NULL; - loff_t bytes_read; + ulong size; int ret; if (bflow->blk) desc = dev_get_uclass_plat(bflow->blk); - ret = bootmeth_setup_fs(bflow, desc); - if (ret) - return log_msg_ret("set", ret); - ret = fs_read(bflow->fname, addr, 0, bflow->size, &bytes_read); + size = SZ_1G; + ret = bootmeth_common_read_file(bflow->method, bflow, bflow->fname, + addr, BFI_EFI, &size); if (ret) - return log_msg_ret("read", ret); + return log_msg_ret("rdf", ret); bflow->buf = map_sysmem(addr, bflow->size); - set_efi_bootdev(desc, bflow); - return 0; } @@ -173,7 +136,8 @@ static int distro_efi_try_bootflow_files(struct udevice *dev, /* Limit FDT files to 4MB */ size = SZ_4M; ret = bootmeth_common_read_file(dev, bflow, fname, - fdt_addr, &size); + fdt_addr, (enum bootflow_img_t)IH_TYPE_FLATDT, + &size); } } @@ -246,16 +210,15 @@ static int distro_efi_read_bootflow_net(struct bootflow *bflow) if (size <= 0) return log_msg_ret("sz", -EINVAL); bflow->size = size; + bflow->buf = map_sysmem(addr, size); /* bootfile should be setup by dhcp */ bootfile_name = env_get("bootfile"); if (!bootfile_name) return log_msg_ret("bootfile_name", ret); bflow->fname = strdup(bootfile_name); - - /* do the hideous EFI hack */ - efi_set_bootdev("Net", "", bflow->fname, map_sysmem(addr, 0), - bflow->size); + if (!bflow->fname) + return log_msg_ret("fi0", -ENOMEM); /* read the DT file also */ fdt_addr_str = env_get("fdt_addr_r"); @@ -330,29 +293,10 @@ static int distro_efi_boot(struct udevice *dev, struct bootflow *bflow) if (bflow->flags & ~BOOTFLOWF_USE_BUILTIN_FDT) fdt = bflow->fdt_addr; - } else { - /* - * This doesn't actually work for network devices: - * - * do_bootefi_image() No UEFI binary known at 0x02080000 - * - * But this is the same behaviour for distro boot, so it can be - * fixed here. - */ - fdt = env_get_hex("fdt_addr_r", 0); } - if (bflow->flags & BOOTFLOWF_USE_BUILTIN_FDT) { - log_debug("Booting with built-in fdt\n"); - if (efi_binary_run(map_sysmem(kernel, 0), bflow->size, - EFI_FDT_USE_INTERNAL)) - return log_msg_ret("run", -EINVAL); - } else { - log_debug("Booting with external fdt\n"); - if (efi_binary_run(map_sysmem(kernel, 0), bflow->size, - map_sysmem(fdt, 0))) - return log_msg_ret("run", -EINVAL); - } + if (efi_bootflow_run(bflow)) + return log_msg_ret("run", -EINVAL); return 0; } diff --git a/boot/bootmeth_efi_mgr.c b/boot/bootmeth_efi_mgr.c index 23ae1e610ac..42b8863815e 100644 --- a/boot/bootmeth_efi_mgr.c +++ b/boot/bootmeth_efi_mgr.c @@ -74,7 +74,8 @@ static int efi_mgr_read_bootflow(struct udevice *dev, struct bootflow *bflow) } static int efi_mgr_read_file(struct udevice *dev, struct bootflow *bflow, - const char *file_path, ulong addr, ulong *sizep) + const char *file_path, ulong addr, + enum bootflow_img_t type, ulong *sizep) { /* Files are loaded by the 'bootefi bootmgr' command */ diff --git a/boot/bootmeth_extlinux.c b/boot/bootmeth_extlinux.c index c6ae6dffcb7..17c6cebd2f4 100644 --- a/boot/bootmeth_extlinux.c +++ b/boot/bootmeth_extlinux.c @@ -69,7 +69,8 @@ static int extlinux_get_state_desc(struct udevice *dev, char *buf, int maxsize) } static int extlinux_getfile(struct pxe_context *ctx, const char *file_path, - char *file_addr, ulong *sizep) + char *file_addr, enum bootflow_img_t type, + ulong *sizep) { struct extlinux_info *info = ctx->userdata; ulong addr; @@ -80,7 +81,7 @@ static int extlinux_getfile(struct pxe_context *ctx, const char *file_path, /* Allow up to 1GB */ *sizep = 1 << 30; ret = bootmeth_read_file(info->dev, info->bflow, file_path, addr, - sizep); + type, sizep); if (ret) return log_msg_ret("read", ret); @@ -160,7 +161,8 @@ static int extlinux_read_bootflow(struct udevice *dev, struct bootflow *bflow) return log_msg_ret("try", ret); size = bflow->size; - ret = bootmeth_alloc_file(bflow, 0x10000, ARCH_DMA_MINALIGN); + ret = bootmeth_alloc_file(bflow, 0x10000, ARCH_DMA_MINALIGN, + BFI_EXTLINUX_CFG); if (ret) return log_msg_ret("read", ret); diff --git a/boot/bootmeth_pxe.c b/boot/bootmeth_pxe.c index 05c6bece2c1..b91e61bcbc4 100644 --- a/boot/bootmeth_pxe.c +++ b/boot/bootmeth_pxe.c @@ -23,7 +23,8 @@ #include <pxe_utils.h> static int extlinux_pxe_getfile(struct pxe_context *ctx, const char *file_path, - char *file_addr, ulong *sizep) + char *file_addr, enum bootflow_img_t type, + ulong *sizep) { struct extlinux_info *info = ctx->userdata; ulong addr; @@ -34,7 +35,7 @@ static int extlinux_pxe_getfile(struct pxe_context *ctx, const char *file_path, /* Allow up to 1GB */ *sizep = 1 << 30; ret = bootmeth_read_file(info->dev, info->bflow, file_path, addr, - sizep); + type, sizep); if (ret) return log_msg_ret("read", ret); @@ -113,7 +114,7 @@ static int extlinux_pxe_read_bootflow(struct udevice *dev, static int extlinux_pxe_read_file(struct udevice *dev, struct bootflow *bflow, const char *file_path, ulong addr, - ulong *sizep) + enum bootflow_img_t type, ulong *sizep) { char *tftp_argv[] = {"tftp", NULL, NULL, NULL}; struct pxe_context *ctx = dev_get_priv(dev); @@ -134,6 +135,9 @@ static int extlinux_pxe_read_file(struct udevice *dev, struct bootflow *bflow, return log_msg_ret("spc", -ENOSPC); *sizep = size; + if (!bootflow_img_add(bflow, file_path, type, addr, size)) + return log_msg_ret("pxi", -ENOMEM); + return 0; } diff --git a/boot/bootmeth_qfw.c b/boot/bootmeth_qfw.c index 2f8e00cf350..028c2481583 100644 --- a/boot/bootmeth_qfw.c +++ b/boot/bootmeth_qfw.c @@ -52,7 +52,8 @@ static int qfw_read_bootflow(struct udevice *dev, struct bootflow *bflow) } static int qfw_read_file(struct udevice *dev, struct bootflow *bflow, - const char *file_path, ulong addr, ulong *sizep) + const char *file_path, ulong addr, + enum bootflow_img_t type, ulong *sizep) { return -ENOSYS; } diff --git a/boot/bootmeth_sandbox.c b/boot/bootmeth_sandbox.c index 26c713bb5f3..92ba2e3f050 100644 --- a/boot/bootmeth_sandbox.c +++ b/boot/bootmeth_sandbox.c @@ -27,7 +27,8 @@ static int sandbox_read_bootflow(struct udevice *dev, struct bootflow *bflow) } static int sandbox_read_file(struct udevice *dev, struct bootflow *bflow, - const char *file_path, ulong addr, ulong *sizep) + const char *file_path, ulong addr, + enum bootflow_img_t type, ulong *sizep) { return -ENOSYS; } diff --git a/boot/bootmeth_script.c b/boot/bootmeth_script.c index c5cbf18c2e6..020cb8a7aec 100644 --- a/boot/bootmeth_script.c +++ b/boot/bootmeth_script.c @@ -98,7 +98,8 @@ static int script_read_bootflow_file(struct udevice *bootstd, if (!bflow->subdir) return log_msg_ret("prefix", -ENOMEM); - ret = bootmeth_alloc_file(bflow, 0x10000, ARCH_DMA_MINALIGN); + ret = bootmeth_alloc_file(bflow, 0x10000, ARCH_DMA_MINALIGN, + (enum bootflow_img_t)IH_TYPE_SCRIPT); if (ret) return log_msg_ret("read", ret); @@ -106,8 +107,8 @@ static int script_read_bootflow_file(struct udevice *bootstd, if (ret) return log_msg_ret("inf", ret); - ret = bootmeth_alloc_other(bflow, "boot.bmp", &bflow->logo, - &bflow->logo_size); + ret = bootmeth_alloc_other(bflow, "boot.bmp", BFI_LOGO, + &bflow->logo, &bflow->logo_size); /* ignore error */ return 0; diff --git a/boot/bootstd-uclass.c b/boot/bootstd-uclass.c index fdb8d69e320..8c0fd4e63c3 100644 --- a/boot/bootstd-uclass.c +++ b/boot/bootstd-uclass.c @@ -6,6 +6,7 @@ * Written by Simon Glass <sjg@chromium.org> */ +#include <alist.h> #include <bootflow.h> #include <bootstd.h> #include <dm.h> @@ -42,13 +43,11 @@ static int bootstd_of_to_plat(struct udevice *dev) static void bootstd_clear_glob_(struct bootstd_priv *priv) { - while (!list_empty(&priv->glob_head)) { - struct bootflow *bflow; + struct bootflow *bflow; - bflow = list_first_entry(&priv->glob_head, struct bootflow, - glob_node); + alist_for_each(bflow, &priv->bootflows) bootflow_remove(bflow); - } + alist_empty(&priv->bootflows); } void bootstd_clear_glob(void) @@ -61,6 +60,44 @@ void bootstd_clear_glob(void) bootstd_clear_glob_(std); } +int bootstd_add_bootflow(struct bootflow *bflow) +{ + struct bootstd_priv *std; + int ret; + + ret = bootstd_get_priv(&std); + if (ret) + return ret; + + ret = std->bootflows.count; + bflow = alist_add(&std->bootflows, *bflow); + if (!bflow) + return log_msg_ret("bf2", -ENOMEM); + + return ret; +} + +int bootstd_clear_bootflows_for_bootdev(struct udevice *dev) +{ + struct bootstd_priv *std = bootstd_try_priv(); + struct bootflow *from, *to; + + /* if bootstd does not exist we cannot have any bootflows */ + if (!std) + return 0; + + /* Drop any bootflows that mention this dev */ + alist_for_each_filter(from, to, &std->bootflows) { + if (from->dev == dev) + bootflow_remove(from); + else + *to++ = *from; + } + alist_update_end(&std->bootflows, to); + + return 0; +} + static int bootstd_remove(struct udevice *dev) { struct bootstd_priv *priv = dev_get_priv(dev); @@ -100,6 +137,17 @@ const char *const *const bootstd_get_prefixes(struct udevice *dev) return std->prefixes ? std->prefixes : default_prefixes; } +struct bootstd_priv *bootstd_try_priv(void) +{ + struct udevice *dev; + + dev = uclass_try_first_device(UCLASS_BOOTSTD); + if (!dev || !device_active(dev)) + return NULL; + + return dev_get_priv(dev); +} + int bootstd_get_priv(struct bootstd_priv **stdp) { struct udevice *dev; @@ -117,7 +165,7 @@ static int bootstd_probe(struct udevice *dev) { struct bootstd_priv *std = dev_get_priv(dev); - INIT_LIST_HEAD(&std->glob_head); + alist_init_struct(&std->bootflows, struct bootflow); return 0; } diff --git a/boot/fdt_support.c b/boot/fdt_support.c index 2392027d40b..49efeec3681 100644 --- a/boot/fdt_support.c +++ b/boot/fdt_support.c @@ -321,7 +321,7 @@ int fdt_kaslrseed(void *fdt, bool overwrite) * board_fdt_chosen_bootargs - boards may override this function to use * alternative kernel command line arguments */ -__weak char *board_fdt_chosen_bootargs(void) +__weak const char *board_fdt_chosen_bootargs(const struct fdt_property *fdt_ba) { return env_get("bootargs"); } @@ -331,7 +331,7 @@ int fdt_chosen(void *fdt) struct abuf buf = {}; int nodeoffset; int err; - char *str; /* used to set string properties */ + const char *str; /* used to set string properties */ err = fdt_check_header(fdt); if (err < 0) { @@ -364,7 +364,8 @@ int fdt_chosen(void *fdt) } } - str = board_fdt_chosen_bootargs(); + str = board_fdt_chosen_bootargs(fdt_get_property(fdt, nodeoffset, + "bootargs", NULL)); if (str) { err = fdt_setprop(fdt, nodeoffset, "bootargs", str, diff --git a/boot/image-android.c b/boot/image-android.c index 60a422dfb74..fa4e14ca469 100644 --- a/boot/image-android.c +++ b/boot/image-android.c @@ -337,12 +337,12 @@ int android_image_get_kernel(const void *hdr, if (bootargs) len += strlen(bootargs); - if (*img_data.kcmdline) { + if (img_data.kcmdline && *img_data.kcmdline) { printf("Kernel command line: %s\n", img_data.kcmdline); len += strlen(img_data.kcmdline) + (len ? 1 : 0); /* +1 for extra space */ } - if (*img_data.kcmdline_extra) { + if (img_data.kcmdline_extra && *img_data.kcmdline_extra) { printf("Kernel extra command line: %s\n", img_data.kcmdline_extra); len += strlen(img_data.kcmdline_extra) + (len ? 1 : 0); /* +1 for extra space */ } @@ -357,13 +357,13 @@ int android_image_get_kernel(const void *hdr, if (bootargs) strcpy(newbootargs, bootargs); - if (*img_data.kcmdline) { + if (img_data.kcmdline && *img_data.kcmdline) { if (*newbootargs) /* If there is something in newbootargs, a space is needed */ strcat(newbootargs, " "); strcat(newbootargs, img_data.kcmdline); } - if (*img_data.kcmdline_extra) { + if (img_data.kcmdline_extra && *img_data.kcmdline_extra) { if (*newbootargs) /* If there is something in newbootargs, a space is needed */ strcat(newbootargs, " "); strcat(newbootargs, img_data.kcmdline_extra); diff --git a/boot/image-board.c b/boot/image-board.c index 4e86a9a2271..514f8e63f9c 100644 --- a/boot/image-board.c +++ b/boot/image-board.c @@ -1087,8 +1087,8 @@ fallback: } /* get script subimage data address and length */ - if (fit_image_get_data_and_size(fit_hdr, noffset, - &fit_data, &fit_len)) { + if (fit_image_get_data(fit_hdr, noffset, &fit_data, + &fit_len)) { puts("Could not find script subimage data\n"); return 1; } diff --git a/boot/image-fit.c b/boot/image-fit.c index db7fb61bca9..aa139da628c 100644 --- a/boot/image-fit.c +++ b/boot/image-fit.c @@ -509,7 +509,7 @@ void fit_image_print(const void *fit, int image_noffset, const char *p) fit_image_get_comp(fit, image_noffset, &comp); printf("%s Compression: %s\n", p, genimg_get_comp_name(comp)); - ret = fit_image_get_data_and_size(fit, image_noffset, &data, &size); + ret = fit_image_get_data(fit, image_noffset, &data, &size); if (!tools_build()) { printf("%s Data Start: ", p); @@ -902,13 +902,13 @@ int fit_image_get_entry(const void *fit, int noffset, ulong *entry) } /** - * fit_image_get_data - get data property and its size for a given component image node + * fit_image_get_emb_data - get data property and its size for a given component image node * @fit: pointer to the FIT format image header * @noffset: component image node offset * @data: double pointer to void, will hold data property's data address * @size: pointer to size_t, will hold data property's data size * - * fit_image_get_data() finds data property in a given component image node. + * fit_image_get_emb_data() finds data property in a given component image node. * If the property is found its data start address and size are returned to * the caller. * @@ -916,8 +916,8 @@ int fit_image_get_entry(const void *fit, int noffset, ulong *entry) * 0, on success * -1, on failure */ -int fit_image_get_data(const void *fit, int noffset, - const void **data, size_t *size) +int fit_image_get_emb_data(const void *fit, int noffset, const void **data, + size_t *size) { int len; @@ -1031,14 +1031,14 @@ int fit_image_get_data_size_unciphered(const void *fit, int noffset, } /** - * fit_image_get_data_and_size - get data and its size including + * fit_image_get_data - get data and its size including * both embedded and external data * @fit: pointer to the FIT format image header * @noffset: component image node offset * @data: double pointer to void, will hold data property's data address * @size: pointer to size_t, will hold data property's data size * - * fit_image_get_data_and_size() finds data and its size including + * fit_image_get_data() finds data and its size including * both embedded and external data. If the property is found * its data start address and size are returned to the caller. * @@ -1046,8 +1046,8 @@ int fit_image_get_data_size_unciphered(const void *fit, int noffset, * 0, on success * otherwise, on failure */ -int fit_image_get_data_and_size(const void *fit, int noffset, - const void **data, size_t *size) +int fit_image_get_data(const void *fit, int noffset, const void **data, + size_t *size) { bool external_data = false; int offset; @@ -1074,7 +1074,7 @@ int fit_image_get_data_and_size(const void *fit, int noffset, *size = len; } } else { - ret = fit_image_get_data(fit, noffset, data, size); + ret = fit_image_get_emb_data(fit, noffset, data, size); } return ret; @@ -1432,7 +1432,7 @@ int fit_image_verify(const void *fit, int image_noffset) goto err; } /* Get image data and data length */ - if (fit_image_get_data_and_size(fit, image_noffset, &data, &size)) { + if (fit_image_get_data(fit, image_noffset, &data, &size)) { err_msg = "Can't get image data/size"; goto err; } @@ -1781,8 +1781,7 @@ int fit_conf_find_compat(const void *fit, const void *fdt) } /* search in this config's kernel FDT */ - if (fit_image_get_data_and_size(fit, kfdt_noffset, - &fdt, &sz)) { + if (fit_image_get_data(fit, kfdt_noffset, &fdt, &sz)) { debug("Failed to get fdt \"%s\".\n", kfdt_name); continue; } @@ -1908,24 +1907,30 @@ int fit_conf_get_prop_node(const void *fit, int noffset, const char *prop_name, count = fit_conf_get_prop_node_count(fit, noffset, prop_name); if (count < 0) return count; + log_debug("looking for %s (%s, image-count %d):\n", prop_name, + genimg_get_phase_name(image_ph_phase(sel_phase)), count); /* check each image in the list */ for (i = 0; i < count; i++) { - enum image_phase_t phase; + enum image_phase_t phase = IH_PHASE_NONE; int ret, node; node = fit_conf_get_prop_node_index(fit, noffset, prop_name, i); ret = fit_image_get_phase(fit, node, &phase); + log_debug("- %s (%s): ", fdt_get_name(fit, node, NULL), + genimg_get_phase_name(phase)); /* if the image is for any phase, let's use it */ - if (ret == -ENOENT) + if (ret == -ENOENT || phase == sel_phase) { + log_debug("found\n"); return node; - else if (ret < 0) + } else if (ret < 0) { + log_debug("err=%d\n", ret); return ret; - - if (phase == sel_phase) - return node; + } + log_debug("no match\n"); } + log_debug("- not found\n"); return -ENOENT; } @@ -1941,7 +1946,7 @@ static int fit_get_data_tail(const void *fit, int noffset, if (!fit_image_verify(fit, noffset)) return -EINVAL; - if (fit_image_get_data_and_size(fit, noffset, data, size)) + if (fit_image_get_data(fit, noffset, data, size)) return -ENOENT; if (!fit_get_desc(fit, noffset, &desc)) @@ -2013,13 +2018,15 @@ int fit_get_node_from_config(struct bootm_headers *images, } /** - * fit_get_image_type_property() - get property name for IH_TYPE_... + * fit_get_image_type_property() - get property name for sel_phase * * Return: the properly name where we expect to find the image in the * config node */ -static const char *fit_get_image_type_property(int type) +static const char *fit_get_image_type_property(int ph_type) { + int type = image_ph_type(ph_type); + /* * This is sort-of available in the uimage_type[] table in image.c * but we don't have access to the short name, and "fdt" is different @@ -2071,8 +2078,9 @@ int fit_image_load(struct bootm_headers *images, ulong addr, fit_uname = fit_unamep ? *fit_unamep : NULL; fit_uname_config = fit_uname_configp ? *fit_uname_configp : NULL; fit_base_uname_config = NULL; - prop_name = fit_get_image_type_property(image_type); - printf("## Loading %s from FIT Image at %08lx ...\n", prop_name, addr); + prop_name = fit_get_image_type_property(ph_type); + printf("## Loading %s (%s) from FIT Image at %08lx ...\n", + prop_name, genimg_get_phase_name(image_ph_phase(ph_type)), addr); bootstage_mark(bootstage_id + BOOTSTAGE_SUB_FORMAT); ret = fit_check_format(fit, IMAGE_SIZE_INVAL); @@ -2198,8 +2206,7 @@ int fit_image_load(struct bootm_headers *images, ulong addr, bootstage_mark(bootstage_id + BOOTSTAGE_SUB_CHECK_ALL_OK); /* get image data address and length */ - if (fit_image_get_data_and_size(fit, noffset, - (const void **)&buf, &size)) { + if (fit_image_get_data(fit, noffset, (const void **)&buf, &size)) { printf("Could not find %s subimage data!\n", prop_name); bootstage_error(bootstage_id + BOOTSTAGE_SUB_GET_DATA); return -ENOENT; @@ -2348,10 +2355,17 @@ int boot_get_fdt_fit(struct bootm_headers *images, ulong addr, char *next_config = NULL; ulong load, len; #ifdef CONFIG_OF_LIBFDT_OVERLAY - ulong image_start, image_end; ulong ovload, ovlen, ovcopylen; const char *uconfig; const char *uname; + /* + * of_flat_tree is storing the void * returned by map_sysmem, then its + * address is passed to boot_relocate_fdt which expects a char ** and it + * is then cast into a ulong. Setting its type to void * would require + * to cast its address to char ** when passing it to boot_relocate_fdt. + * Instead, let's be lazy and use void *. + */ + char *of_flat_tree; void *base, *ov, *ovcopy = NULL; int i, err, noffset, ov_noffset; #endif @@ -2395,17 +2409,18 @@ int boot_get_fdt_fit(struct bootm_headers *images, ulong addr, /* we need to apply overlays */ #ifdef CONFIG_OF_LIBFDT_OVERLAY - image_start = addr; - image_end = addr + fit_get_size(fit); - /* verify that relocation took place by load address not being in fit */ - if (load >= image_start && load < image_end) { - /* check is simplified; fit load checks for overlaps */ - printf("Overlayed FDT requires relocation\n"); + /* Relocate FDT so resizing does not overwrite other data in FIT. */ + of_flat_tree = map_sysmem(load, len); + len = ALIGN(fdt_totalsize(load), SZ_4K); + err = boot_relocate_fdt(&of_flat_tree, &len); + if (err) { + printf("Required FDT relocation for applying DTOs failed: %d\n", + err); fdt_noffset = -EBADF; goto out; } - base = map_sysmem(load, len); + load = (ulong)of_flat_tree; /* apply extra configs in FIT first, followed by args */ for (i = 1; ; i++) { diff --git a/boot/pxe_utils.c b/boot/pxe_utils.c index 3ae17553c6d..82f217aaf86 100644 --- a/boot/pxe_utils.c +++ b/boot/pxe_utils.c @@ -6,6 +6,7 @@ #define LOG_CATEGORY LOGC_BOOT +#include <bootflow.h> #include <command.h> #include <dm.h> #include <env.h> @@ -97,7 +98,8 @@ int format_mac_pxe(char *outbuf, size_t outbuf_len) * Returns 1 for success, or < 0 on error */ static int get_relfile(struct pxe_context *ctx, const char *file_path, - unsigned long file_addr, ulong *filesizep) + unsigned long file_addr, enum bootflow_img_t type, + ulong *filesizep) { size_t path_len; char relfile[MAX_TFTP_PATH_LEN + 1]; @@ -124,7 +126,7 @@ static int get_relfile(struct pxe_context *ctx, const char *file_path, sprintf(addr_buf, "%lx", file_addr); - ret = ctx->getfile(ctx, relfile, addr_buf, &size); + ret = ctx->getfile(ctx, relfile, addr_buf, type, &size); if (ret < 0) return log_msg_ret("get", ret); if (filesizep) @@ -133,16 +135,6 @@ static int get_relfile(struct pxe_context *ctx, const char *file_path, return 1; } -/** - * get_pxe_file() - read a file - * - * The file is read and nul-terminated - * - * @ctx: PXE context - * @file_path: File path to read (relative to the PXE file) - * @file_addr: Address to load file to - * Returns 1 for success, or < 0 on error - */ int get_pxe_file(struct pxe_context *ctx, const char *file_path, ulong file_addr) { @@ -150,7 +142,8 @@ int get_pxe_file(struct pxe_context *ctx, const char *file_path, int err; char *buf; - err = get_relfile(ctx, file_path, file_addr, &size); + err = get_relfile(ctx, file_path, file_addr, BFI_EXTLINUX_CFG, + &size); if (err < 0) return err; @@ -199,13 +192,15 @@ int get_pxelinux_path(struct pxe_context *ctx, const char *file, * @file_path: File path to read (relative to the PXE file) * @envaddr_name: Name of environment variable which contains the address to * load to + * @type: File type * @filesizep: Returns the file size in bytes * Returns 1 on success, -ENOENT if @envaddr_name does not exist as an * environment variable, -EINVAL if its format is not valid hex, or other * value < 0 on other error */ static int get_relfile_envaddr(struct pxe_context *ctx, const char *file_path, - const char *envaddr_name, ulong *filesizep) + const char *envaddr_name, + enum bootflow_img_t type, ulong *filesizep) { unsigned long file_addr; char *envaddr; @@ -217,7 +212,7 @@ static int get_relfile_envaddr(struct pxe_context *ctx, const char *file_path, if (strict_strtoul(envaddr, 16, &file_addr) < 0) return -EINVAL; - return get_relfile(ctx, file_path, file_addr, filesizep); + return get_relfile(ctx, file_path, file_addr, type, filesizep); } /** @@ -405,6 +400,7 @@ static void label_boot_fdtoverlay(struct pxe_context *ctx, /* Load overlay file */ err = get_relfile_envaddr(ctx, overlayfile, "fdtoverlay_addr_r", + (enum bootflow_img_t)IH_TYPE_FLATDT, NULL); if (err < 0) { printf("Failed loading overlay %s\n", overlayfile); @@ -490,7 +486,8 @@ static int label_boot(struct pxe_context *ctx, struct pxe_label *label) } if (get_relfile_envaddr(ctx, label->kernel, "kernel_addr_r", - NULL) < 0) { + (enum bootflow_img_t)IH_TYPE_KERNEL, NULL) + < 0) { printf("Skipping %s for failure retrieving kernel\n", label->name); return 1; @@ -516,6 +513,7 @@ static int label_boot(struct pxe_context *ctx, struct pxe_label *label) } else if (label->initrd) { ulong size; if (get_relfile_envaddr(ctx, label->initrd, "ramdisk_addr_r", + (enum bootflow_img_t)IH_TYPE_RAMDISK, &size) < 0) { printf("Skipping %s for failure retrieving initrd\n", label->name); @@ -661,7 +659,8 @@ static int label_boot(struct pxe_context *ctx, struct pxe_label *label) if (fdtfile) { int err = get_relfile_envaddr(ctx, fdtfile, - "fdt_addr_r", NULL); + "fdt_addr_r", + (enum bootflow_img_t)IH_TYPE_FLATDT, NULL); free(fdtfilefree); if (err < 0) { @@ -1548,7 +1547,8 @@ void handle_pxe_menu(struct pxe_context *ctx, struct pxe_menu *cfg) if (IS_ENABLED(CONFIG_CMD_BMP)) { /* display BMP if available */ if (cfg->bmp) { - if (get_relfile(ctx, cfg->bmp, image_load_addr, NULL)) { + if (get_relfile(ctx, cfg->bmp, image_load_addr, + BFI_LOGO, NULL)) { #if defined(CONFIG_VIDEO) struct udevice *dev; diff --git a/boot/vbe_abrec.c b/boot/vbe_abrec.c new file mode 100644 index 00000000000..6d0f622262d --- /dev/null +++ b/boot/vbe_abrec.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Verified Boot for Embedded (VBE) 'simple' method + * + * Copyright 2024 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#define LOG_CATEGORY LOGC_BOOT + +#include <dm.h> +#include <memalign.h> +#include <mmc.h> +#include <dm/ofnode.h> +#include "vbe_abrec.h" + +int abrec_read_priv(ofnode node, struct abrec_priv *priv) +{ + memset(priv, '\0', sizeof(*priv)); + if (ofnode_read_u32(node, "area-start", &priv->area_start) || + ofnode_read_u32(node, "area-size", &priv->area_size) || + ofnode_read_u32(node, "version-offset", &priv->version_offset) || + ofnode_read_u32(node, "version-size", &priv->version_size) || + ofnode_read_u32(node, "state-offset", &priv->state_offset) || + ofnode_read_u32(node, "state-size", &priv->state_size)) + return log_msg_ret("read", -EINVAL); + ofnode_read_u32(node, "skip-offset", &priv->skip_offset); + priv->storage = strdup(ofnode_read_string(node, "storage")); + if (!priv->storage) + return log_msg_ret("str", -EINVAL); + + return 0; +} + +int abrec_read_nvdata(struct abrec_priv *priv, struct udevice *blk, + struct abrec_state *state) +{ + ALLOC_CACHE_ALIGN_BUFFER(u8, buf, MMC_MAX_BLOCK_LEN); + const struct vbe_nvdata *nvd = (struct vbe_nvdata *)buf; + uint flags; + int ret; + + ret = vbe_read_nvdata(blk, priv->area_start + priv->state_offset, + priv->state_size, buf); + if (ret == -EPERM) { + memset(buf, '\0', MMC_MAX_BLOCK_LEN); + log_warning("Starting with empty state\n"); + } else if (ret) { + return log_msg_ret("nv", ret); + } + + state->fw_vernum = nvd->fw_vernum; + flags = nvd->flags; + state->try_count = flags & VBEF_TRY_COUNT_MASK; + state->try_b = flags & VBEF_TRY_B; + state->recovery = flags & VBEF_RECOVERY; + state->pick = (flags & VBEF_PICK_MASK) >> VBEF_PICK_SHIFT; + + return 0; +} + +int abrec_read_state(struct udevice *dev, struct abrec_state *state) +{ + struct abrec_priv *priv = dev_get_priv(dev); + struct udevice *blk; + int ret; + + ret = vbe_get_blk(priv->storage, &blk); + if (ret) + return log_msg_ret("blk", ret); + + ret = vbe_read_version(blk, priv->area_start + priv->version_offset, + state->fw_version, MAX_VERSION_LEN); + if (ret) + return log_msg_ret("ver", ret); + log_debug("version=%s\n", state->fw_version); + + ret = abrec_read_nvdata(priv, blk, state); + if (ret) + return log_msg_ret("nvd", ret); + + return 0; +} diff --git a/boot/vbe_abrec.h b/boot/vbe_abrec.h new file mode 100644 index 00000000000..63c73297351 --- /dev/null +++ b/boot/vbe_abrec.h @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Verified Boot for Embedded (VBE) vbe-abrec common file + * + * Copyright 2024 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#ifndef __VBE_ABREC_H +#define __VBE_ABREC_H + +#include <vbe.h> +#include <dm/ofnode_decl.h> + +#include "vbe_common.h" + +struct bootflow; +struct udevice; + +/** + * struct abrec_priv - information read from the device tree + * + * @area_start: Start offset of the VBE area in the device, in bytes + * @area_size: Total size of the VBE area + * @skip_offset: Size of an initial part of the device to skip, when using + * area_start. This is effectively added to area_start to calculate the + * actual start position on the device + * @state_offset: Offset from area_start of the VBE state, in bytes + * @state_size: Size of the state information + * @version_offset: Offset from from area_start of the VBE version info + * @version_size: Size of the version info + * @storage: Storage device to use, in the form <uclass><devnum>, e.g. "mmc1" + */ +struct abrec_priv { + u32 area_start; + u32 area_size; + u32 skip_offset; + u32 state_offset; + u32 state_size; + u32 version_offset; + u32 version_size; + const char *storage; +}; + +/** struct abrec_state - state information read from media + * + * The state on the media is converted into this more code-friendly structure. + * + * @fw_version: Firmware version string + * @fw_vernum: Firmware version number + * @try_count: Number of times the B firmware has been tried + * @try_b: true to try B firmware on the next boot + * @recovery: true to enter recovery firmware on the next boot + * @try_result: Result of trying to boot with the last firmware + * @pick: Firmware which was chosen in this boot + */ +struct abrec_state { + char fw_version[MAX_VERSION_LEN]; + u32 fw_vernum; + u8 try_count; + bool try_b; + bool recovery; + enum vbe_try_result try_result; + enum vbe_pick_t pick; +}; + +/** + * abrec_read_fw_bootflow() - Read a bootflow for firmware + * + * Locates and loads the firmware image (FIT) needed for the next phase. The FIT + * should ideally use external data, to reduce the amount of it that needs to be + * read. + * + * @bdev: bootdev device containing the firmwre + * @bflow: Place to put the created bootflow, on success + * @return 0 if OK, -ve on error + */ +int abrec_read_bootflow_fw(struct udevice *dev, struct bootflow *bflow); + +/** + * vbe_simple_read_state() - Read the VBE simple state information + * + * @dev: VBE bootmeth + * @state: Place to put the state + * @return 0 if OK, -ve on error + */ +int abrec_read_state(struct udevice *dev, struct abrec_state *state); + +/** + * abrec_read_nvdata() - Read non-volatile data from a block device + * + * Reads the ABrec VBE nvdata from a device. This function reads a single block + * from the device, so the nvdata cannot be larger than that. + * + * @blk: Device to read from + * @offset: Offset to read, in bytes + * @size: Number of bytes to read + * @buf: Buffer to hold the data + * Return: 0 if OK, -E2BIG if @size > block size, -EBADF if the offset is not + * block-aligned, -EIO if an I/O error occurred, -EPERM if the header version is + * incorrect, the header size is invalid or the data fails its CRC check + */ +int abrec_read_nvdata(struct abrec_priv *priv, struct udevice *blk, + struct abrec_state *state); + +/** + * abrec_read_priv() - Read info from the devicetree + * + * @node: Node to read from + * @priv: Information to fill in + * Return 0 if OK, -EINVAL if something is wrong with the devicetree node + */ +int abrec_read_priv(ofnode node, struct abrec_priv *priv); + +#endif /* __VBE_ABREC_H */ diff --git a/boot/vbe_abrec_fw.c b/boot/vbe_abrec_fw.c new file mode 100644 index 00000000000..d52bd9ddff0 --- /dev/null +++ b/boot/vbe_abrec_fw.c @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Verified Boot for Embedded (VBE) loading firmware phases + * + * Copyright 2022 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#define LOG_CATEGORY LOGC_BOOT + +#include <binman_sym.h> +#include <bloblist.h> +#include <bootdev.h> +#include <bootflow.h> +#include <bootmeth.h> +#include <bootstage.h> +#include <display_options.h> +#include <dm.h> +#include <image.h> +#include <log.h> +#include <mapmem.h> +#include <memalign.h> +#include <mmc.h> +#include <spl.h> +#include <vbe.h> +#include <dm/device-internal.h> +#include "vbe_abrec.h" +#include "vbe_common.h" + +binman_sym_declare(ulong, spl_a, image_pos); +binman_sym_declare(ulong, spl_b, image_pos); +binman_sym_declare(ulong, spl_recovery, image_pos); + +binman_sym_declare(ulong, spl_a, size); +binman_sym_declare(ulong, spl_b, size); +binman_sym_declare(ulong, spl_recovery, size); + +binman_sym_declare(ulong, u_boot_a, image_pos); +binman_sym_declare(ulong, u_boot_b, image_pos); +binman_sym_declare(ulong, u_boot_recovery, image_pos); + +binman_sym_declare(ulong, u_boot_a, size); +binman_sym_declare(ulong, u_boot_b, size); +binman_sym_declare(ulong, u_boot_recovery, size); + +binman_sym_declare(ulong, vpl, image_pos); +binman_sym_declare(ulong, vpl, size); + +static const char *const pick_names[] = {"A", "B", "Recovery"}; + +/** + * abrec_read_bootflow_fw() - Create a bootflow for firmware + * + * Locates and loads the firmware image (FIT) needed for the next phase. The FIT + * should ideally use external data, to reduce the amount of it that needs to be + * read. + * + * @bdev: bootdev device containing the firmwre + * @meth: VBE abrec bootmeth + * @blow: Place to put the created bootflow, on success + * @return 0 if OK, -ve on error + */ +int abrec_read_bootflow_fw(struct udevice *dev, struct bootflow *bflow) +{ + struct udevice *media = dev_get_parent(bflow->dev); + struct udevice *meth = bflow->method; + struct abrec_priv *priv = dev_get_priv(meth); + ulong len, load_addr; + struct udevice *blk; + int ret; + + log_debug("media=%s\n", media->name); + ret = blk_get_from_parent(media, &blk); + if (ret) + return log_msg_ret("med", ret); + + ret = vbe_read_fit(blk, priv->area_start + priv->skip_offset, + priv->area_size, NULL, &load_addr, &len, &bflow->name); + if (ret) + return log_msg_ret("vbe", ret); + + /* set up the bootflow with the info we obtained */ + bflow->blk = blk; + bflow->buf = map_sysmem(load_addr, len); + bflow->size = len; + + return 0; +} + +static int abrec_run_vpl(struct udevice *blk, struct spl_image_info *image, + struct vbe_handoff *handoff) +{ + uint flags, tries, prev_result; + struct abrec_priv priv; + struct abrec_state state; + enum vbe_pick_t pick; + uint try_count; + ulong offset, size; + ulong ub_offset, ub_size; + ofnode node; + int ret; + + node = vbe_get_node(); + if (!ofnode_valid(node)) + return log_msg_ret("nod", -EINVAL); + + ret = abrec_read_priv(node, &priv); + if (ret) + return log_msg_ret("pri", ret); + + ret = abrec_read_nvdata(&priv, blk, &state); + if (ret) + return log_msg_ret("sta", ret); + + prev_result = state.try_result; + try_count = state.try_count; + + if (state.recovery) { + pick = VBEP_RECOVERY; + + /* if we are trying B but ran out of tries, use A */ + } else if ((prev_result == VBETR_TRYING) && !tries) { + pick = VBEP_A; + state.try_result = VBETR_BAD; + + /* if requested, try B */ + } else if (flags & VBEF_TRY_B) { + pick = VBEP_B; + + /* decrement the try count if not already zero */ + if (try_count) + try_count--; + state.try_result = VBETR_TRYING; + } else { + pick = VBEP_A; + } + state.try_count = try_count; + + switch (pick) { + case VBEP_A: + offset = binman_sym(ulong, spl_a, image_pos); + size = binman_sym(ulong, spl_a, size); + ub_offset = binman_sym(ulong, u_boot_a, image_pos); + ub_size = binman_sym(ulong, u_boot_a, size); + break; + case VBEP_B: + offset = binman_sym(ulong, spl_b, image_pos); + size = binman_sym(ulong, spl_b, size); + ub_offset = binman_sym(ulong, u_boot_b, image_pos); + ub_size = binman_sym(ulong, u_boot_b, size); + break; + case VBEP_RECOVERY: + offset = binman_sym(ulong, spl_recovery, image_pos); + size = binman_sym(ulong, spl_recovery, size); + ub_offset = binman_sym(ulong, u_boot_recovery, image_pos); + ub_size = binman_sym(ulong, u_boot_recovery, size); + break; + } + log_debug("pick=%d, offset=%lx size=%lx\n", pick, offset, size); + log_info("VBE: Firmware pick %s at %lx\n", pick_names[pick], offset); + + ret = vbe_read_fit(blk, offset, size, image, NULL, NULL, NULL); + if (ret) + return log_msg_ret("vbe", ret); + handoff->offset = ub_offset; + handoff->size = ub_size; + handoff->pick = pick; + image->load_addr = spl_get_image_text_base(); + image->entry_point = image->load_addr; + + return 0; +} + +static int abrec_run_spl(struct udevice *blk, struct spl_image_info *image, + struct vbe_handoff *handoff) +{ + int ret; + + log_info("VBE: Firmware pick %s at %lx\n", pick_names[handoff->pick], + handoff->offset); + ret = vbe_read_fit(blk, handoff->offset, handoff->size, image, NULL, + NULL, NULL); + if (ret) + return log_msg_ret("vbe", ret); + image->load_addr = spl_get_image_text_base(); + image->entry_point = image->load_addr; + + return 0; +} + +static int abrec_load_from_image(struct spl_image_info *image, + struct spl_boot_device *bootdev) +{ + struct vbe_handoff *handoff; + int ret; + + printf("load: %s\n", ofnode_read_string(ofnode_root(), "model")); + if (xpl_phase() != PHASE_VPL && xpl_phase() != PHASE_SPL && + xpl_phase() != PHASE_TPL) + return -ENOENT; + + ret = bloblist_ensure_size(BLOBLISTT_VBE, sizeof(struct vbe_handoff), + 0, (void **)&handoff); + if (ret) + return log_msg_ret("ro", ret); + + if (USE_BOOTMETH) { + struct udevice *meth, *bdev; + struct abrec_priv *priv; + struct bootflow bflow; + + vbe_find_first_device(&meth); + if (!meth) + return log_msg_ret("vd", -ENODEV); + log_debug("vbe dev %s\n", meth->name); + ret = device_probe(meth); + if (ret) + return log_msg_ret("probe", ret); + + priv = dev_get_priv(meth); + log_debug("abrec %s\n", priv->storage); + ret = bootdev_find_by_label(priv->storage, &bdev, NULL); + if (ret) + return log_msg_ret("bd", ret); + log_debug("bootdev %s\n", bdev->name); + + bootflow_init(&bflow, bdev, meth); + ret = bootmeth_read_bootflow(meth, &bflow); + log_debug("\nfw ret=%d\n", ret); + if (ret) + return log_msg_ret("rd", ret); + + /* jump to the image */ + image->flags = SPL_SANDBOXF_ARG_IS_BUF; + image->arg = bflow.buf; + image->size = bflow.size; + log_debug("Image: %s at %p size %x\n", bflow.name, bflow.buf, + bflow.size); + + /* this is not used from now on, so free it */ + bootflow_free(&bflow); + } else { + struct udevice *media; + struct udevice *blk; + + ret = uclass_get_device_by_seq(UCLASS_MMC, 1, &media); + if (ret) + return log_msg_ret("vdv", ret); + ret = blk_get_from_parent(media, &blk); + if (ret) + return log_msg_ret("med", ret); + + if (xpl_phase() == PHASE_TPL) { + ulong offset, size; + + offset = binman_sym(ulong, vpl, image_pos); + size = binman_sym(ulong, vpl, size); + log_debug("VPL at offset %lx size %lx\n", offset, size); + ret = vbe_read_fit(blk, offset, size, image, NULL, + NULL, NULL); + if (ret) + return log_msg_ret("vbe", ret); + } else if (xpl_phase() == PHASE_VPL) { + ret = abrec_run_vpl(blk, image, handoff); + } else { + ret = abrec_run_spl(blk, image, handoff); + } + } + + /* Record that VBE was used in this phase */ + handoff->phases |= 1 << xpl_phase(); + + return 0; +} +SPL_LOAD_IMAGE_METHOD("vbe_abrec", 5, BOOT_DEVICE_VBE, + abrec_load_from_image); diff --git a/boot/vbe_common.c b/boot/vbe_common.c new file mode 100644 index 00000000000..a86986d86e9 --- /dev/null +++ b/boot/vbe_common.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Verified Boot for Embedded (VBE) common functions + * + * Copyright 2024 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <bootstage.h> +#include <dm.h> +#include <blk.h> +#include <image.h> +#include <mapmem.h> +#include <memalign.h> +#include <spl.h> +#include <u-boot/crc.h> +#include "vbe_common.h" + +binman_sym_declare(ulong, u_boot_vpl_nodtb, size); +binman_sym_declare(ulong, u_boot_vpl_bss_pad, size); +binman_sym_declare(ulong, u_boot_spl_nodtb, size); +binman_sym_declare(ulong, u_boot_spl_bss_pad, size); + +int vbe_get_blk(const char *storage, struct udevice **blkp) +{ + struct blk_desc *desc; + char devname[16]; + const char *end; + int devnum; + + /* First figure out the block device */ + log_debug("storage=%s\n", storage); + devnum = trailing_strtoln_end(storage, NULL, &end); + if (devnum == -1) + return log_msg_ret("num", -ENODEV); + if (end - storage >= sizeof(devname)) + return log_msg_ret("end", -E2BIG); + strlcpy(devname, storage, end - storage + 1); + log_debug("dev=%s, %x\n", devname, devnum); + + desc = blk_get_dev(devname, devnum); + if (!desc) + return log_msg_ret("get", -ENXIO); + *blkp = desc->bdev; + + return 0; +} + +int vbe_read_version(struct udevice *blk, ulong offset, char *version, + int max_size) +{ + ALLOC_CACHE_ALIGN_BUFFER(u8, buf, MMC_MAX_BLOCK_LEN); + + /* we can use an assert() here since we already read only one block */ + assert(max_size <= MMC_MAX_BLOCK_LEN); + + /* + * we can use an assert() here since reading the wrong block will just + * cause an invalid version-string to be (safely) read + */ + assert(!(offset & (MMC_MAX_BLOCK_LEN - 1))); + + offset /= MMC_MAX_BLOCK_LEN; + + if (blk_read(blk, offset, 1, buf) != 1) + return log_msg_ret("read", -EIO); + strlcpy(version, buf, max_size); + log_debug("version=%s\n", version); + + return 0; +} + +int vbe_read_nvdata(struct udevice *blk, ulong offset, ulong size, u8 *buf) +{ + uint hdr_ver, hdr_size, data_size, crc; + const struct vbe_nvdata *nvd; + + /* we can use an assert() here since we already read only one block */ + assert(size <= MMC_MAX_BLOCK_LEN); + + /* + * We can use an assert() here since reading the wrong block will just + * cause invalid state to be (safely) read. If the crc passes, then we + * obtain invalid state and it will likely cause booting to fail. + * + * VBE relies on valid values being in U-Boot's devicetree, so this + * should not every be wrong on a production device. + */ + assert(!(offset & (MMC_MAX_BLOCK_LEN - 1))); + + if (offset & (MMC_MAX_BLOCK_LEN - 1)) + return log_msg_ret("get", -EBADF); + offset /= MMC_MAX_BLOCK_LEN; + + if (blk_read(blk, offset, 1, buf) != 1) + return log_msg_ret("read", -EIO); + nvd = (struct vbe_nvdata *)buf; + hdr_ver = (nvd->hdr & NVD_HDR_VER_MASK) >> NVD_HDR_VER_SHIFT; + hdr_size = (nvd->hdr & NVD_HDR_SIZE_MASK) >> NVD_HDR_SIZE_SHIFT; + if (hdr_ver != NVD_HDR_VER_CUR) + return log_msg_ret("hdr", -EPERM); + data_size = 1 << hdr_size; + if (!data_size || data_size > sizeof(*nvd)) + return log_msg_ret("sz", -EPERM); + + crc = crc8(0, buf + 1, data_size - 1); + if (crc != nvd->crc8) + return log_msg_ret("crc", -EPERM); + + return 0; +} + +/** + * h_vbe_load_read() - Handler for reading an SPL image from a FIT + * + * See spl_load_reader for the definition + */ +ulong h_vbe_load_read(struct spl_load_info *load, ulong off, ulong size, + void *buf) +{ + struct blk_desc *desc = load->priv; + lbaint_t sector = off >> desc->log2blksz; + lbaint_t count = size >> desc->log2blksz; + int ret; + + log_debug("vbe read log2blksz %x offset %lx sector %lx count %lx\n", + desc->log2blksz, (ulong)off, (long)sector, (ulong)count); + + ret = blk_dread(desc, sector, count, buf); + log_debug("ret=%x\n", ret); + if (ret < 0) + return ret; + + return ret << desc->log2blksz; +} + +int vbe_read_fit(struct udevice *blk, ulong area_offset, ulong area_size, + struct spl_image_info *image, ulong *load_addrp, ulong *lenp, + char **namep) +{ + ALLOC_CACHE_ALIGN_BUFFER(u8, sbuf, MMC_MAX_BLOCK_LEN); + ulong size, blknum, addr, len, load_addr, num_blks, spl_load_addr; + ulong aligned_size, fdt_load_addr, fdt_size; + const char *fit_uname, *fit_uname_config; + struct bootm_headers images = {}; + enum image_phase_t phase; + struct blk_desc *desc; + int node, ret; + bool for_xpl; + void *buf; + + desc = dev_get_uclass_plat(blk); + + /* read in one block to find the FIT size */ + blknum = area_offset / desc->blksz; + log_debug("read at %lx, blknum %lx\n", area_offset, blknum); + ret = blk_read(blk, blknum, 1, sbuf); + if (ret < 0) + return log_msg_ret("rd", ret); + else if (ret != 1) + return log_msg_ret("rd2", -EIO); + + ret = fdt_check_header(sbuf); + if (ret < 0) + return log_msg_ret("fdt", -EINVAL); + size = fdt_totalsize(sbuf); + if (size > area_size) + return log_msg_ret("fdt", -E2BIG); + log_debug("FIT size %lx\n", size); + aligned_size = ALIGN(size, desc->blksz); + + /* + * Load the FIT into the SPL memory. This is typically a FIT with + * external data, so this is quite small, perhaps a few KB. + */ + if (IS_ENABLED(CONFIG_SANDBOX)) { + addr = CONFIG_VAL(TEXT_BASE); + buf = map_sysmem(addr, size); + } else { + buf = malloc(aligned_size); + if (!buf) + return log_msg_ret("fit", -ENOMEM); + addr = map_to_sysmem(buf); + } + num_blks = aligned_size / desc->blksz; + log_debug("read %lx, %lx blocks to %lx / %p\n", aligned_size, num_blks, + addr, buf); + ret = blk_read(blk, blknum, num_blks, buf); + if (ret < 0) + return log_msg_ret("rd3", ret); + else if (ret != num_blks) + return log_msg_ret("rd4", -EIO); + log_debug("check total size %x off_dt_strings %x\n", fdt_totalsize(buf), + fdt_off_dt_strings(buf)); + +#if CONFIG_IS_ENABLED(SYS_MALLOC_F) + log_debug("malloc base %lx ptr %x limit %x top %lx\n", + gd->malloc_base, gd->malloc_ptr, gd->malloc_limit, + gd->malloc_base + gd->malloc_limit); +#endif + /* figure out the phase to load */ + phase = IS_ENABLED(CONFIG_TPL_BUILD) ? IH_PHASE_NONE : + IS_ENABLED(CONFIG_VPL_BUILD) ? IH_PHASE_SPL : IH_PHASE_U_BOOT; + + log_debug("loading FIT\n"); + + if (xpl_phase() == PHASE_SPL && !IS_ENABLED(CONFIG_SANDBOX)) { + struct spl_load_info info; + + spl_load_init(&info, h_vbe_load_read, desc, desc->blksz); + xpl_set_fdt_update(&info, false); + xpl_set_phase(&info, IH_PHASE_U_BOOT); + log_debug("doing SPL from %s blksz %lx log2blksz %x area_offset %lx + fdt_size %lx\n", + blk->name, desc->blksz, desc->log2blksz, area_offset, ALIGN(size, 4)); + ret = spl_load_simple_fit(image, &info, area_offset, buf); + log_debug("spl_load_simple_fit() ret=%d\n", ret); + + return ret; + } + + /* + * Load the image from the FIT. We ignore any load-address information + * so in practice this simply locates the image in the external-data + * region and returns its address and size. Since we only loaded the FIT + * itself, only a part of the image will be present, at best. + */ + fit_uname = NULL; + fit_uname_config = NULL; + ret = fit_image_load(&images, addr, &fit_uname, &fit_uname_config, + IH_ARCH_DEFAULT, image_ph(phase, IH_TYPE_FIRMWARE), + BOOTSTAGE_ID_FIT_SPL_START, FIT_LOAD_IGNORED, + &load_addr, &len); + if (ret == -ENOENT) { + ret = fit_image_load(&images, addr, &fit_uname, + &fit_uname_config, IH_ARCH_DEFAULT, + image_ph(phase, IH_TYPE_LOADABLE), + BOOTSTAGE_ID_FIT_SPL_START, + FIT_LOAD_IGNORED, &load_addr, &len); + } + if (ret < 0) + return log_msg_ret("ld", ret); + node = ret; + log_debug("load %lx size %lx\n", load_addr, len); + + fdt_load_addr = 0; + fdt_size = 0; + if ((xpl_phase() == PHASE_TPL || xpl_phase() == PHASE_VPL) && + !IS_ENABLED(CONFIG_SANDBOX)) { + /* allow use of a different image from the configuration node */ + fit_uname = NULL; + ret = fit_image_load(&images, addr, &fit_uname, + &fit_uname_config, IH_ARCH_DEFAULT, + image_ph(phase, IH_TYPE_FLATDT), + BOOTSTAGE_ID_FIT_SPL_START, + FIT_LOAD_IGNORED, &fdt_load_addr, + &fdt_size); + fdt_size = ALIGN(fdt_size, desc->blksz); + log_debug("FDT noload to %lx size %lx\n", fdt_load_addr, + fdt_size); + } + + for_xpl = !USE_BOOTMETH && CONFIG_IS_ENABLED(RELOC_LOADER); + if (for_xpl) { + image->size = len; + image->fdt_size = fdt_size; + ret = spl_reloc_prepare(image, &spl_load_addr); + if (ret) + return log_msg_ret("spl", ret); + } + if (!IS_ENABLED(CONFIG_SANDBOX)) + image->os = IH_OS_U_BOOT; + + /* For FIT external data, read in the external data */ + log_debug("load_addr %lx len %lx addr %lx aligned_size %lx\n", + load_addr, len, addr, aligned_size); + if (load_addr + len > addr + aligned_size) { + ulong base, full_size, offset, extra, fdt_base, fdt_full_size; + ulong fdt_offset; + void *base_buf, *fdt_base_buf; + + /* Find the start address to load from */ + base = ALIGN_DOWN(load_addr, desc->blksz); + + offset = area_offset + load_addr - addr; + blknum = offset / desc->blksz; + extra = offset % desc->blksz; + + /* + * Get the total number of bytes to load, taking care of + * block alignment + */ + full_size = len + extra; + + /* + * Get the start block number, number of blocks and the address + * to load to, then load the blocks + */ + num_blks = DIV_ROUND_UP(full_size, desc->blksz); + if (for_xpl) + base = spl_load_addr; + base_buf = map_sysmem(base, full_size); + ret = blk_read(blk, blknum, num_blks, base_buf); + log_debug("read foffset %lx blknum %lx full_size %lx num_blks %lx to %lx / %p: ret=%d\n", + offset - 0x8000, blknum, full_size, num_blks, base, base_buf, + ret); + if (ret < 0) + return log_msg_ret("rd", ret); + if (ret != num_blks) + return log_msg_ret("rd", -EIO); + if (extra && !IS_ENABLED(CONFIG_SANDBOX)) { + log_debug("move %p %p %lx\n", base_buf, + base_buf + extra, len); + memmove(base_buf, base_buf + extra, len); + } + + if ((xpl_phase() == PHASE_VPL || xpl_phase() == PHASE_TPL) && + !IS_ENABLED(CONFIG_SANDBOX)) { + image->load_addr = spl_get_image_text_base(); + image->entry_point = image->load_addr; + } + + /* now the FDT */ + if (fdt_size) { + fdt_offset = area_offset + fdt_load_addr - addr; + blknum = fdt_offset / desc->blksz; + extra = fdt_offset % desc->blksz; + fdt_full_size = fdt_size + extra; + num_blks = DIV_ROUND_UP(fdt_full_size, desc->blksz); + fdt_base = ALIGN(base + len, 4); + fdt_base_buf = map_sysmem(fdt_base, fdt_size); + ret = blk_read(blk, blknum, num_blks, fdt_base_buf); + log_debug("fdt read foffset %lx blknum %lx full_size %lx num_blks %lx to %lx / %p: ret=%d\n", + fdt_offset - 0x8000, blknum, fdt_full_size, num_blks, + fdt_base, fdt_base_buf, ret); + if (ret != num_blks) + return log_msg_ret("rdf", -EIO); + if (extra) { + log_debug("move %p %p %lx\n", fdt_base_buf, + fdt_base_buf + extra, fdt_size); + memmove(fdt_base_buf, fdt_base_buf + extra, + fdt_size); + } +#if CONFIG_IS_ENABLED(RELOC_LOADER) + image->fdt_buf = fdt_base_buf; + + ulong xpl_size; + ulong xpl_pad; + ulong fdt_start; + + if (xpl_phase() == PHASE_TPL) { + xpl_size = binman_sym(ulong, u_boot_vpl_nodtb, size); + xpl_pad = binman_sym(ulong, u_boot_vpl_bss_pad, size); + } else { + xpl_size = binman_sym(ulong, u_boot_spl_nodtb, size); + xpl_pad = binman_sym(ulong, u_boot_spl_bss_pad, size); + } + fdt_start = image->load_addr + xpl_size + xpl_pad; + log_debug("load_addr %lx xpl_size %lx copy-to %lx\n", + image->load_addr, xpl_size + xpl_pad, + fdt_start); + image->fdt_start = map_sysmem(fdt_start, fdt_size); +#endif + } + } + if (load_addrp) + *load_addrp = load_addr; + if (lenp) + *lenp = len; + if (namep) { + *namep = strdup(fdt_get_name(buf, node, NULL)); + if (!namep) + return log_msg_ret("nam", -ENOMEM); + } + + return 0; +} + +ofnode vbe_get_node(void) +{ + return ofnode_path("/bootstd/firmware0"); +} diff --git a/boot/vbe_common.h b/boot/vbe_common.h new file mode 100644 index 00000000000..493cbdc3694 --- /dev/null +++ b/boot/vbe_common.h @@ -0,0 +1,180 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Verified Boot for Embedded (VBE) common functions + * + * Copyright 2024 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#ifndef __VBE_COMMON_H +#define __VBE_COMMON_H + +#include <dm/ofnode_decl.h> +#include <linux/bitops.h> +#include <linux/types.h> + +struct spl_image_info; +struct udevice; + +/* + * Controls whether we use a full bootmeth driver with VBE in this phase, or + * just access the information directly. + * + * For now VBE-simple uses the full bootmeth, but VBE-abrec does not, to reduce + * code size + */ +#define USE_BOOTMETH CONFIG_IS_ENABLED(BOOTMETH_VBE_SIMPLE) + +enum { + MAX_VERSION_LEN = 256, + + NVD_HDR_VER_SHIFT = 0, + NVD_HDR_VER_MASK = 0xf, + NVD_HDR_SIZE_SHIFT = 4, + NVD_HDR_SIZE_MASK = 0xf << NVD_HDR_SIZE_SHIFT, + + /* Firmware key-version is in the top 16 bits of fw_ver */ + FWVER_KEY_SHIFT = 16, + FWVER_FW_MASK = 0xffff, + + NVD_HDR_VER_CUR = 1, /* current version */ +}; + +/** + * enum vbe_try_result - result of trying a firmware pick + * + * @VBETR_UNKNOWN: Unknown / invalid result + * @VBETR_TRYING: Firmware pick is being tried + * @VBETR_OK: Firmware pick is OK and can be used from now on + * @VBETR_BAD: Firmware pick is bad and should be removed + */ +enum vbe_try_result { + VBETR_UNKNOWN, + VBETR_TRYING, + VBETR_OK, + VBETR_BAD, +}; + +/** + * enum vbe_flags - flags controlling operation + * + * @VBEF_TRY_COUNT_MASK: mask for the 'try count' value + * @VBEF_TRY_B: Try the B slot + * @VBEF_RECOVERY: Use recovery slot + */ +enum vbe_flags { + VBEF_TRY_COUNT_MASK = 0x3, + VBEF_TRY_B = BIT(2), + VBEF_RECOVERY = BIT(3), + + VBEF_RESULT_SHIFT = 4, + VBEF_RESULT_MASK = 3 << VBEF_RESULT_SHIFT, + + VBEF_PICK_SHIFT = 6, + VBEF_PICK_MASK = 3 << VBEF_PICK_SHIFT, +}; + +/** + * struct vbe_nvdata - basic storage format for non-volatile data + * + * This is used for all VBE methods + * + * @crc8: crc8 for the entire record except @crc8 field itself + * @hdr: header size and version (NVD_HDR_...) + * @spare1: unused, must be 0 + * @fw_vernum: version and key version (FWVER_...) + * @flags: Flags controlling operation (enum vbe_flags) + */ +struct vbe_nvdata { + u8 crc8; + u8 hdr; + u16 spare1; + u32 fw_vernum; + u32 flags; + u8 spare2[0x34]; +}; + +/** + * vbe_get_blk() - Obtain the block device to use for VBE + * + * Decodes the string to produce a block device + * + * @storage: String indicating the device to use, e.g. "mmc1" + * @blkp: Returns associated block device, on success + * Return 0 if OK, -ENODEV if @storage does not end with a number, -E2BIG if + * the device name is more than 15 characters, -ENXIO if the block device could + * not be found + */ +int vbe_get_blk(const char *storage, struct udevice **blkp); + +/** + * vbe_read_version() - Read version-string from a block device + * + * Reads the VBE version-string from a device. This function reads a single + * block from the device, so the string cannot be larger than that. It uses a + * temporary buffer for the read, then copies in up to @size bytes + * + * @blk: Device to read from + * @offset: Offset to read, in bytes + * @version: Place to put the string + * @max_size: Maximum size of @version + * Return: 0 if OK, -E2BIG if @max_size > block size, -EBADF if the offset is + * not block-aligned, -EIO if an I/O error occurred + */ +int vbe_read_version(struct udevice *blk, ulong offset, char *version, + int max_size); + +/** + * vbe_read_nvdata() - Read non-volatile data from a block device + * + * Reads the VBE nvdata from a device. This function reads a single block from + * the device, so the nvdata cannot be larger than that. + * + * @blk: Device to read from + * @offset: Offset to read, in bytes + * @size: Number of bytes to read + * @buf: Buffer to hold the data + * Return: 0 if OK, -E2BIG if @size > block size, -EBADF if the offset is not + * block-aligned, -EIO if an I/O error occurred, -EPERM if the header version is + * incorrect, the header size is invalid or the data fails its CRC check + */ +int vbe_read_nvdata(struct udevice *blk, ulong offset, ulong size, u8 *buf); + +/** + * vbe_read_fit() - Read an image from a FIT + * + * This handles most of the VBE logic for reading from a FIT. It reads the FIT + * metadata, decides which image to load and loads it to a suitable address, + * ready for jumping to the next phase of VBE. + * + * This supports transition from VPL to SPL as well as SPL to U-Boot proper. For + * now, TPL->VPL is not supported. + * + * Both embedded and external data are supported for the FIT + * + * @blk: Block device containing FIT + * @area_offset: Byte offset of the VBE area in @blk containing the FIT + * @area_size: Size of the VBE area + * @image: SPL image to fill in with details of the loaded image, or NULL + * @load_addrp: If non-null, returns the address where the image was loaded + * @lenp: If non-null, returns the size of the image loaded, in bytes + * @namep: If non-null, returns the name of the FIT-image node that was loaded + * (allocated by this function) + * Return: 0 if OK, -EINVAL if the area does not contain an FDT (the underlying + * format for FIT), -E2BIG if the FIT extends past @area_size, -ENOMEM if there + * was not space to allocate the image-node name, other error if a read error + * occurred (see blk_read()), or something went wrong with the actually + * FIT-parsing (see fit_image_load()). + */ +int vbe_read_fit(struct udevice *blk, ulong area_offset, ulong area_size, + struct spl_image_info *image, ulong *load_addrp, ulong *lenp, + char **namep); + +/** + * vbe_get_node() - Get the node containing the VBE settings + * + * Return: VBE node (typically "/bootstd/firmware0") + */ +ofnode vbe_get_node(void); + +#endif /* __VBE_ABREC_H */ diff --git a/boot/vbe_simple.c b/boot/vbe_simple.c index 189e86d2a22..c6766c532f2 100644 --- a/boot/vbe_simple.c +++ b/boot/vbe_simple.c @@ -18,70 +18,21 @@ #include <vbe.h> #include <dm/device-internal.h> #include <dm/ofnode.h> -#include <u-boot/crc.h> #include "vbe_simple.h" -/** struct simple_nvdata - storage format for non-volatile data */ -struct simple_nvdata { - u8 crc8; - u8 hdr; - u16 spare1; - u32 fw_vernum; - u8 spare2[0x38]; -}; - -static int simple_read_version(struct udevice *dev, struct blk_desc *desc, - u8 *buf, struct simple_state *state) +static int simple_read_nvdata(const struct simple_priv *priv, + struct udevice *blk, struct simple_state *state) { - struct simple_priv *priv = dev_get_priv(dev); - int start; - - if (priv->version_size > MMC_MAX_BLOCK_LEN) - return log_msg_ret("ver", -E2BIG); - - start = priv->area_start + priv->version_offset; - if (start & (MMC_MAX_BLOCK_LEN - 1)) - return log_msg_ret("get", -EBADF); - start /= MMC_MAX_BLOCK_LEN; - - if (blk_dread(desc, start, 1, buf) != 1) - return log_msg_ret("read", -EIO); - strlcpy(state->fw_version, buf, MAX_VERSION_LEN); - log_debug("version=%s\n", state->fw_version); + ALLOC_CACHE_ALIGN_BUFFER(u8, buf, MMC_MAX_BLOCK_LEN); + const struct vbe_nvdata *nvd; + int ret; - return 0; -} + ret = vbe_read_nvdata(blk, priv->area_start + priv->state_offset, + priv->state_size, buf); + if (ret) + return log_msg_ret("nv", ret); -static int simple_read_nvdata(struct udevice *dev, struct blk_desc *desc, - u8 *buf, struct simple_state *state) -{ - struct simple_priv *priv = dev_get_priv(dev); - uint hdr_ver, hdr_size, size, crc; - const struct simple_nvdata *nvd; - int start; - - if (priv->state_size > MMC_MAX_BLOCK_LEN) - return log_msg_ret("state", -E2BIG); - - start = priv->area_start + priv->state_offset; - if (start & (MMC_MAX_BLOCK_LEN - 1)) - return log_msg_ret("get", -EBADF); - start /= MMC_MAX_BLOCK_LEN; - - if (blk_dread(desc, start, 1, buf) != 1) - return log_msg_ret("read", -EIO); - nvd = (struct simple_nvdata *)buf; - hdr_ver = (nvd->hdr & NVD_HDR_VER_MASK) >> NVD_HDR_VER_SHIFT; - hdr_size = (nvd->hdr & NVD_HDR_SIZE_MASK) >> NVD_HDR_SIZE_SHIFT; - if (hdr_ver != NVD_HDR_VER_CUR) - return log_msg_ret("hdr", -EPERM); - size = 1 << hdr_size; - if (size > sizeof(*nvd)) - return log_msg_ret("sz", -ENOEXEC); - - crc = crc8(0, buf + 1, size - 1); - if (crc != nvd->crc8) - return log_msg_ret("crc", -EPERM); + nvd = (struct vbe_nvdata *)buf; state->fw_vernum = nvd->fw_vernum; log_debug("version=%s\n", state->fw_version); @@ -91,33 +42,20 @@ static int simple_read_nvdata(struct udevice *dev, struct blk_desc *desc, int vbe_simple_read_state(struct udevice *dev, struct simple_state *state) { - ALLOC_CACHE_ALIGN_BUFFER(u8, buf, MMC_MAX_BLOCK_LEN); struct simple_priv *priv = dev_get_priv(dev); - struct blk_desc *desc; - char devname[16]; - const char *end; - int devnum; + struct udevice *blk; int ret; - /* First figure out the block device */ - log_debug("storage=%s\n", priv->storage); - devnum = trailing_strtoln_end(priv->storage, NULL, &end); - if (devnum == -1) - return log_msg_ret("num", -ENODEV); - if (end - priv->storage >= sizeof(devname)) - return log_msg_ret("end", -E2BIG); - strlcpy(devname, priv->storage, end - priv->storage + 1); - log_debug("dev=%s, %x\n", devname, devnum); - - desc = blk_get_dev(devname, devnum); - if (!desc) - return log_msg_ret("get", -ENXIO); - - ret = simple_read_version(dev, desc, buf, state); + ret = vbe_get_blk(priv->storage, &blk); + if (ret) + return log_msg_ret("blk", ret); + + ret = vbe_read_version(blk, priv->area_start + priv->version_offset, + state->fw_version, MAX_VERSION_LEN); if (ret) return log_msg_ret("ver", ret); - ret = simple_read_nvdata(dev, desc, buf, state); + ret = simple_read_nvdata(priv, blk, state); if (ret) return log_msg_ret("nvd", ret); @@ -160,13 +98,14 @@ static int vbe_simple_read_bootflow(struct udevice *dev, struct bootflow *bflow) } static int vbe_simple_read_file(struct udevice *dev, struct bootflow *bflow, - const char *file_path, ulong addr, ulong *sizep) + const char *file_path, ulong addr, + enum bootflow_img_t type, ulong *sizep) { int ret; if (vbe_phase() == VBE_PHASE_OS) { ret = bootmeth_common_read_file(dev, bflow, file_path, addr, - sizep); + type, sizep); if (ret) return log_msg_ret("os", ret); } diff --git a/boot/vbe_simple.h b/boot/vbe_simple.h index 56d319206f2..dc3f70052b0 100644 --- a/boot/vbe_simple.h +++ b/boot/vbe_simple.h @@ -9,20 +9,8 @@ #ifndef __VBE_SIMPLE_H #define __VBE_SIMPLE_H -enum { - MAX_VERSION_LEN = 256, - - NVD_HDR_VER_SHIFT = 0, - NVD_HDR_VER_MASK = 0xf, - NVD_HDR_SIZE_SHIFT = 4, - NVD_HDR_SIZE_MASK = 0xf << NVD_HDR_SIZE_SHIFT, - - /* Firmware key-version is in the top 16 bits of fw_ver */ - FWVER_KEY_SHIFT = 16, - FWVER_FW_MASK = 0xffff, - - NVD_HDR_VER_CUR = 1, /* current version */ -}; +#include <linux/types.h> +#include "vbe_common.h" /** struct simple_priv - information read from the device tree */ struct simple_priv { diff --git a/boot/vbe_simple_fw.c b/boot/vbe_simple_fw.c index da9701f9eb9..cb5534fc731 100644 --- a/boot/vbe_simple_fw.c +++ b/boot/vbe_simple_fw.c @@ -8,6 +8,7 @@ #define LOG_CATEGORY LOGC_BOOT +#include <binman_sym.h> #include <bloblist.h> #include <bootdev.h> #include <bootflow.h> @@ -17,13 +18,24 @@ #include <image.h> #include <log.h> #include <mapmem.h> -#include <memalign.h> #include <mmc.h> #include <spl.h> #include <vbe.h> #include <dm/device-internal.h> +#include "vbe_common.h" #include "vbe_simple.h" +#ifdef CONFIG_BOOTMETH_VBE_SIMPLE +binman_sym_extern(ulong, vbe_a, image_pos); +binman_sym_extern(ulong, vbe_a, size); +#else +binman_sym_declare(ulong, vbe_a, image_pos); +binman_sym_declare(ulong, vbe_a, size); +#endif + +binman_sym_declare(ulong, vpl, image_pos); +binman_sym_declare(ulong, vpl, size); + /** * vbe_simple_read_bootflow_fw() - Create a bootflow for firmware * @@ -38,109 +50,26 @@ */ int vbe_simple_read_bootflow_fw(struct udevice *dev, struct bootflow *bflow) { - ALLOC_CACHE_ALIGN_BUFFER(u8, sbuf, MMC_MAX_BLOCK_LEN); struct udevice *media = dev_get_parent(bflow->dev); struct udevice *meth = bflow->method; struct simple_priv *priv = dev_get_priv(meth); - const char *fit_uname, *fit_uname_config; - struct bootm_headers images = {}; - ulong offset, size, blknum, addr, len, load_addr, num_blks; - enum image_phase_t phase; - struct blk_desc *desc; + ulong len, load_addr; struct udevice *blk; - int node, ret; - void *buf; + int ret; log_debug("media=%s\n", media->name); ret = blk_get_from_parent(media, &blk); if (ret) return log_msg_ret("med", ret); log_debug("blk=%s\n", blk->name); - desc = dev_get_uclass_plat(blk); - - offset = priv->area_start + priv->skip_offset; - - /* read in one block to find the FIT size */ - blknum = offset / desc->blksz; - log_debug("read at %lx, blknum %lx\n", offset, blknum); - ret = blk_read(blk, blknum, 1, sbuf); - if (ret < 0) - return log_msg_ret("rd", ret); - - ret = fdt_check_header(sbuf); - if (ret < 0) - return log_msg_ret("fdt", -EINVAL); - size = fdt_totalsize(sbuf); - if (size > priv->area_size) - return log_msg_ret("fdt", -E2BIG); - log_debug("FIT size %lx\n", size); - - /* - * Load the FIT into the SPL memory. This is typically a FIT with - * external data, so this is quite small, perhaps a few KB. - */ - addr = CONFIG_VAL(TEXT_BASE); - buf = map_sysmem(addr, size); - num_blks = DIV_ROUND_UP(size, desc->blksz); - log_debug("read %lx, %lx blocks to %lx / %p\n", size, num_blks, addr, - buf); - ret = blk_read(blk, blknum, num_blks, buf); - if (ret < 0) - return log_msg_ret("rd", ret); - - /* figure out the phase to load */ - phase = IS_ENABLED(CONFIG_VPL_BUILD) ? IH_PHASE_SPL : IH_PHASE_U_BOOT; - - /* - * Load the image from the FIT. We ignore any load-address information - * so in practice this simply locates the image in the external-data - * region and returns its address and size. Since we only loaded the FIT - * itself, only a part of the image will be present, at best. - */ - fit_uname = NULL; - fit_uname_config = NULL; - log_debug("loading FIT\n"); - ret = fit_image_load(&images, addr, &fit_uname, &fit_uname_config, - IH_ARCH_SANDBOX, image_ph(phase, IH_TYPE_FIRMWARE), - BOOTSTAGE_ID_FIT_SPL_START, FIT_LOAD_IGNORED, - &load_addr, &len); - if (ret < 0) - return log_msg_ret("ld", ret); - node = ret; - log_debug("loaded to %lx\n", load_addr); - - /* For FIT external data, read in the external data */ - if (load_addr + len > addr + size) { - ulong base, full_size; - void *base_buf; - - /* Find the start address to load from */ - base = ALIGN_DOWN(load_addr, desc->blksz); - - /* - * Get the total number of bytes to load, taking care of - * block alignment - */ - full_size = load_addr + len - base; - - /* - * Get the start block number, number of blocks and the address - * to load to, then load the blocks - */ - blknum = (offset + base - addr) / desc->blksz; - num_blks = DIV_ROUND_UP(full_size, desc->blksz); - base_buf = map_sysmem(base, full_size); - ret = blk_read(blk, blknum, num_blks, base_buf); - log_debug("read %lx %lx, %lx blocks to %lx / %p: ret=%d\n", - blknum, full_size, num_blks, base, base_buf, ret); - if (ret < 0) - return log_msg_ret("rd", ret); - } + + ret = vbe_read_fit(blk, priv->area_start + priv->skip_offset, + priv->area_size, NULL, &load_addr, &len, + &bflow->name); + if (ret) + return log_msg_ret("vbe", ret); /* set up the bootflow with the info we obtained */ - bflow->name = strdup(fdt_get_name(buf, node, NULL)); - if (!bflow->name) - return log_msg_ret("name", -ENOMEM); bflow->blk = blk; bflow->buf = map_sysmem(load_addr, len); bflow->size = len; @@ -148,16 +77,14 @@ int vbe_simple_read_bootflow_fw(struct udevice *dev, struct bootflow *bflow) return 0; } -static int simple_load_from_image(struct spl_image_info *spl_image, +static int simple_load_from_image(struct spl_image_info *image, struct spl_boot_device *bootdev) { - struct udevice *meth, *bdev; - struct simple_priv *priv; - struct bootflow bflow; struct vbe_handoff *handoff; int ret; - if (xpl_phase() != PHASE_VPL && xpl_phase() != PHASE_SPL) + if (xpl_phase() != PHASE_VPL && xpl_phase() != PHASE_SPL && + xpl_phase() != PHASE_TPL) return -ENOENT; ret = bloblist_ensure_size(BLOBLISTT_VBE, sizeof(struct vbe_handoff), @@ -165,36 +92,64 @@ static int simple_load_from_image(struct spl_image_info *spl_image, if (ret) return log_msg_ret("ro", ret); - vbe_find_first_device(&meth); - if (!meth) - return log_msg_ret("vd", -ENODEV); - log_debug("vbe dev %s\n", meth->name); - ret = device_probe(meth); - if (ret) - return log_msg_ret("probe", ret); - - priv = dev_get_priv(meth); - log_debug("simple %s\n", priv->storage); - ret = bootdev_find_by_label(priv->storage, &bdev, NULL); - if (ret) - return log_msg_ret("bd", ret); - log_debug("bootdev %s\n", bdev->name); - - bootflow_init(&bflow, bdev, meth); - ret = bootmeth_read_bootflow(meth, &bflow); - log_debug("\nfw ret=%d\n", ret); - if (ret) - return log_msg_ret("rd", ret); - - /* jump to the image */ - spl_image->flags = SPL_SANDBOXF_ARG_IS_BUF; - spl_image->arg = bflow.buf; - spl_image->size = bflow.size; - log_debug("Image: %s at %p size %x\n", bflow.name, bflow.buf, - bflow.size); + if (USE_BOOTMETH) { + struct udevice *meth, *bdev; + struct simple_priv *priv; + struct bootflow bflow; + + vbe_find_first_device(&meth); + if (!meth) + return log_msg_ret("vd", -ENODEV); + log_debug("vbe dev %s\n", meth->name); + ret = device_probe(meth); + if (ret) + return log_msg_ret("probe", ret); + + priv = dev_get_priv(meth); + log_debug("simple %s\n", priv->storage); + ret = bootdev_find_by_label(priv->storage, &bdev, NULL); + if (ret) + return log_msg_ret("bd", ret); + log_debug("bootdev %s\n", bdev->name); + + bootflow_init(&bflow, bdev, meth); + ret = bootmeth_read_bootflow(meth, &bflow); + log_debug("\nfw ret=%d\n", ret); + if (ret) + return log_msg_ret("rd", ret); - /* this is not used from now on, so free it */ - bootflow_free(&bflow); + /* jump to the image */ + image->flags = SPL_SANDBOXF_ARG_IS_BUF; + image->arg = bflow.buf; + image->size = bflow.size; + log_debug("Image: %s at %p size %x\n", bflow.name, bflow.buf, + bflow.size); + + /* this is not used from now on, so free it */ + bootflow_free(&bflow); + } else { + struct udevice *media, *blk; + ulong offset, size; + + ret = uclass_get_device_by_seq(UCLASS_MMC, 1, &media); + if (ret) + return log_msg_ret("vdv", ret); + ret = blk_get_from_parent(media, &blk); + if (ret) + return log_msg_ret("med", ret); + if (xpl_phase() == PHASE_TPL) { + offset = binman_sym(ulong, vpl, image_pos); + size = binman_sym(ulong, vpl, size); + } else { + offset = binman_sym(ulong, vbe_a, image_pos); + size = binman_sym(ulong, vbe_a, size); + printf("offset=%lx\n", offset); + } + + ret = vbe_read_fit(blk, offset, size, image, NULL, NULL, NULL); + if (ret) + return log_msg_ret("vbe", ret); + } /* Record that VBE was used in this phase */ handoff->phases |= 1 << xpl_phase(); |