diff options
Diffstat (limited to 'boot')
-rw-r--r-- | boot/Kconfig | 146 | ||||
-rw-r--r-- | boot/Makefile | 4 | ||||
-rw-r--r-- | boot/bootdev-uclass.c | 5 | ||||
-rw-r--r-- | boot/bootflow.c | 9 | ||||
-rw-r--r-- | boot/bootmeth-uclass.c | 5 | ||||
-rw-r--r-- | boot/image-fit.c | 126 | ||||
-rw-r--r-- | boot/image.c | 18 | ||||
-rw-r--r-- | boot/vbe_request.c (renamed from boot/vbe_fixup.c) | 8 | ||||
-rw-r--r-- | boot/vbe_simple.c | 142 | ||||
-rw-r--r-- | boot/vbe_simple.h | 71 | ||||
-rw-r--r-- | boot/vbe_simple_fw.c | 206 | ||||
-rw-r--r-- | boot/vbe_simple_os.c | 104 |
12 files changed, 659 insertions, 185 deletions
diff --git a/boot/Kconfig b/boot/Kconfig index 45f86e946cd..d5c582ebe8c 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -292,6 +292,56 @@ config SPL_FIT_GENERATOR endif # SPL +if VPL + +config VPL_FIT + bool "Support Flattened Image Tree within VPL" + depends on VPL + default y + select VPL_HASH + select VPL_OF_LIBFDT + +config VPL_FIT_PRINT + bool "Support FIT printing within VPL" + depends on VPL_FIT + default y + help + Support printing the content of the fitImage in a verbose manner in VPL. + +config VPL_FIT_FULL_CHECK + bool "Do a full check of the FIT before using it" + default y + help + Enable this do a full check of the FIT to make sure it is valid. This + helps to protect against carefully crafted FITs which take advantage + of bugs or omissions in the code. This includes a bad structure, + multiple root nodes and the like. + +config VPL_FIT_SIGNATURE + bool "Enable signature verification of FIT firmware within VPL" + depends on VPL_DM + default y + select FIT_SIGNATURE + select VPL_FIT + select VPL_CRYPTO + select VPL_HASH + imply VPL_RSA + imply VPL_RSA_VERIFY + select VPL_IMAGE_SIGN_INFO + select VPL_FIT_FULL_CHECK + +config VPL_FIT_SIGNATURE_MAX_SIZE + hex "Max size of signed FIT structures in VPL" + depends on VPL_FIT_SIGNATURE + default 0x10000000 + help + This option sets a max size in bytes for verified FIT uImages. + A sane value of 256MB protects corrupted DTB structures from overlapping + device memory. Assure this size does not extend past expected storage + space. + +endif # VPL + endif # FIT config PXE_UTILS @@ -334,6 +384,26 @@ config BOOTSTD_FULL - support for selecting the ordering of bootdevs using the devicetree as well as the "boot_targets" environment variable +config SPL_BOOTSTD + bool "Standard boot support in VPL" + depends on SPL && SPL_DM && SPL_OF_CONTROL && SPL_BLK + default y if VPL + help + This enables standard boot in SPL. This is neeeded so that VBE + (Verified Boot for Embedded) can be used, since it depends on standard + boot. It is enabled by default since the main purpose of VPL is to + handle the firmware part of VBE. + +config VPL_BOOTSTD + bool "Standard boot support in VPL" + depends on VPL && VPL_DM && VPL_OF_CONTROL && VPL_BLK + default y + help + This enables standard boot in SPL. This is neeeded so that VBE + (Verified Boot for Embedded) can be used, since it depends on standard + boot. It is enabled by default since the main purpose of VPL is to + handle the firmware part of VBE. + if BOOTSTD config BOOTSTD_BOOTCOMMAND @@ -408,6 +478,24 @@ config BOOTMETH_VBE supports selection of various firmware components, seleciton of an OS to boot as well as updating these using fwupd. +config SPL_BOOTMETH_VBE + bool "Bootdev support for Verified Boot for Embedded (SPL)" + depends on SPL && FIT + default y if VPL + help + Enables support for VBE boot. This is a standard boot method which + supports selection of various firmware components, seleciton of an OS to + boot as well as updating these using fwupd. + +config VPL_BOOTMETH_VBE + bool "Bootdev support for Verified Boot for Embedded (VPL)" + depends on VPL && FIT + default y + help + Enables support for VBE boot. This is a standard boot method which + supports selection of various firmware components, seleciton of an OS to + boot as well as updating these using fwupd. + if BOOTMETH_VBE config BOOTMETH_VBE_SIMPLE @@ -418,6 +506,54 @@ 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_SIMPLE_OS + bool "Bootdev support for VBE 'simple' method OS phase" + default y + help + Enables support for the OS parts of VBE 'simple' boot. This includes + fixing up the device tree with the required VBE information, ready + for booting into the OS. This option is only enabled for U-Boot + proper, since it is the phase where device tree fixups happen. + +config SPL_BOOTMETH_VBE_SIMPLE + bool "Bootdev support for VBE 'simple' method (SPL)" + depends on SPL + default y if VPL + help + Enables support for VBE 'simple' boot. This allows updating a single + firmware image in boot media such as MMC. It does not support any sort + of rollback, recovery or A/B boot. + +config VPL_BOOTMETH_VBE_SIMPLE + bool "Bootdev support for VBE 'simple' method (VPL)" + depends on VPL + default y + help + Enables support for VBE 'simple' boot. This allows updating a single + firmware image in boot media such as MMC. It does not support any sort + of rollback, recovery or A/B boot. + +config SPL_BOOTMETH_VBE_SIMPLE_FW + bool "Bootdev support for VBE 'simple' method firmware phase (SPL)" + depends on VPL + default y + help + Enables support for the firmware parts of VBE 'simple' boot. This + includes an SPL loader which locates the correct U-Boot to boot into. + This option should really only be enabled for VPL, since it is the + phase where the SPL + U-Boot decision should be made. But for now, + SPL does its own FIT-configuration selection. + +config VPL_BOOTMETH_VBE_SIMPLE_FW + bool "Bootdev support for VBE 'simple' method firmware phase (VPL)" + depends on VPL + default y + help + Enables support for the firmware parts of VBE 'simple' boot. This + includes an SPL loader which locates the correct SPL to boot into. + This option enabled for VPL, since it is the phase where the SPL + decision is made. + endif # BOOTMETH_VBE config BOOTMETH_SANDBOX @@ -487,14 +623,14 @@ config OF_STDOUT_VIA_ALIAS incorrect when used with device tree as this option does not exist / should not be used. -config HAVE_SYS_TEXT_BASE +config HAVE_TEXT_BASE bool depends on !NIOS2 && !XTENSA depends on !EFI_APP default y -config SYS_TEXT_BASE - depends on HAVE_SYS_TEXT_BASE +config TEXT_BASE + depends on HAVE_TEXT_BASE default 0x0 if POSITION_INDEPENDENT default 0x80800000 if ARCH_OMAP2PLUS || ARCH_K3 default 0x81700000 if MACH_SUNIV @@ -515,10 +651,10 @@ config HAVE_SYS_MONITOR_BASE config SYS_MONITOR_BASE depends on HAVE_SYS_MONITOR_BASE hex "Physical start address of boot monitor code" - default SYS_TEXT_BASE + default TEXT_BASE help The physical start address of boot monitor code (which is the same as - CONFIG_SYS_TEXT_BASE when linking) and the same as CONFIG_SYS_FLASH_BASE + CONFIG_TEXT_BASE when linking) and the same as CONFIG_SYS_FLASH_BASE when booting from flash. config SPL_SYS_MONITOR_BASE diff --git a/boot/Makefile b/boot/Makefile index dd45d786f8c..f0c31549213 100644 --- a/boot/Makefile +++ b/boot/Makefile @@ -47,5 +47,7 @@ ifdef CONFIG_SPL_BUILD obj-$(CONFIG_SPL_LOAD_FIT) += common_fit.o endif -obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE) += vbe.o vbe_fixup.o +obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE) += vbe.o vbe_request.o obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE_SIMPLE) += vbe_simple.o +obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE_SIMPLE_FW) += vbe_simple_fw.o +obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE_SIMPLE_OS) += vbe_simple_os.o diff --git a/boot/bootdev-uclass.c b/boot/bootdev-uclass.c index 9d98bee4549..affe0d3e04e 100644 --- a/boot/bootdev-uclass.c +++ b/boot/bootdev-uclass.c @@ -440,10 +440,7 @@ int bootdev_get_bootflow(struct udevice *dev, struct bootflow_iter *iter, if (!ops->get_bootflow) return -ENOSYS; - memset(bflow, '\0', sizeof(*bflow)); - bflow->dev = dev; - bflow->method = iter->method; - bflow->state = BOOTFLOWST_BASE; + bootflow_init(bflow, dev, iter->method); return ops->get_bootflow(dev, iter, bflow); } diff --git a/boot/bootflow.c b/boot/bootflow.c index 5d94a27ff84..f9ad4099244 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -339,6 +339,15 @@ int bootflow_scan_next(struct bootflow_iter *iter, struct bootflow *bflow) } while (1); } +void bootflow_init(struct bootflow *bflow, struct udevice *bootdev, + struct udevice *meth) +{ + memset(bflow, '\0', sizeof(*bflow)); + bflow->dev = bootdev; + bflow->method = meth; + bflow->state = BOOTFLOWST_BASE; +} + void bootflow_free(struct bootflow *bflow) { free(bflow->name); diff --git a/boot/bootmeth-uclass.c b/boot/bootmeth-uclass.c index 2d7652edeab..25552dd96f6 100644 --- a/boot/bootmeth-uclass.c +++ b/boot/bootmeth-uclass.c @@ -77,10 +77,7 @@ int bootmeth_get_bootflow(struct udevice *dev, struct bootflow *bflow) if (!ops->read_bootflow) return -ENOSYS; - memset(bflow, '\0', sizeof(*bflow)); - bflow->dev = NULL; - bflow->method = dev; - bflow->state = BOOTFLOWST_BASE; + bootflow_init(bflow, NULL, dev); return ops->read_bootflow(dev, bflow); } diff --git a/boot/image-fit.c b/boot/image-fit.c index 6e503f827dd..3cc556b727f 100644 --- a/boot/image-fit.c +++ b/boot/image-fit.c @@ -802,6 +802,40 @@ int fit_image_get_comp(const void *fit, int noffset, uint8_t *comp) return 0; } +/** + * fit_image_get_phase() - get the phase for a configuration node + * @fit: pointer to the FIT format image header + * @offset: configuration-node offset + * @phasep: returns the phase + * + * Finds the phase property in a given configuration node. If the property is + * found, its (string) value is translated to the numeric id which is returned + * to the caller. + * + * Returns: 0 on success, -ENOENT if missing, -EINVAL for invalid value + */ +int fit_image_get_phase(const void *fit, int offset, enum image_phase_t *phasep) +{ + const void *data; + int len, ret; + + /* Get phase name from property data */ + data = fdt_getprop(fit, offset, FIT_PHASE_PROP, &len); + if (!data) { + fit_get_debug(fit, offset, FIT_PHASE_PROP, len); + *phasep = 0; + return -ENOENT; + } + + /* Translate phase name to id */ + ret = genimg_get_phase_id(data); + if (ret < 0) + return ret; + *phasep = ret; + + return 0; +} + static int fit_image_get_address(const void *fit, int noffset, char *name, ulong *load) { @@ -1686,49 +1720,6 @@ int fit_check_format(const void *fit, ulong size) return 0; } -/** - * fit_conf_find_compat - * @fit: pointer to the FIT format image header - * @fdt: pointer to the device tree to compare against - * - * fit_conf_find_compat() attempts to find the configuration whose fdt is the - * most compatible with the passed in device tree. - * - * Example: - * - * / o image-tree - * |-o images - * | |-o fdt-1 - * | |-o fdt-2 - * | - * |-o configurations - * |-o config-1 - * | |-fdt = fdt-1 - * | - * |-o config-2 - * |-fdt = fdt-2 - * - * / o U-Boot fdt - * |-compatible = "foo,bar", "bim,bam" - * - * / o kernel fdt1 - * |-compatible = "foo,bar", - * - * / o kernel fdt2 - * |-compatible = "bim,bam", "baz,biz" - * - * Configuration 1 would be picked because the first string in U-Boot's - * compatible list, "foo,bar", matches a compatible string in the root of fdt1. - * "bim,bam" in fdt2 matches the second string which isn't as good as fdt1. - * - * As an optimization, the compatible property from the FDT's root node can be - * copied into the configuration node in the FIT image. This is required to - * match configurations with compressed FDTs. - * - * returns: - * offset to the configuration to use if one was found - * -1 otherwise - */ int fit_conf_find_compat(const void *fit, const void *fdt) { int ndepth = 0; @@ -1910,10 +1901,37 @@ int fit_conf_get_prop_node_index(const void *fit, int noffset, return fit_image_get_node(fit, uname); } -int fit_conf_get_prop_node(const void *fit, int noffset, - const char *prop_name) +int fit_conf_get_prop_node(const void *fit, int noffset, const char *prop_name, + enum image_phase_t sel_phase) { - return fit_conf_get_prop_node_index(fit, noffset, prop_name, 0); + int i, count; + + if (sel_phase == IH_PHASE_NONE) + return fit_conf_get_prop_node_index(fit, noffset, prop_name, 0); + + count = fit_conf_get_prop_node_count(fit, noffset, prop_name); + if (count < 0) + return count; + + /* check each image in the list */ + for (i = 0; i < count; i++) { + enum image_phase_t phase; + int ret, node; + + node = fit_conf_get_prop_node_index(fit, noffset, prop_name, i); + ret = fit_image_get_phase(fit, node, &phase); + + /* if the image is for any phase, let's use it */ + if (ret == -ENOENT) + return node; + else if (ret < 0) + return ret; + + if (phase == sel_phase) + return node; + } + + return -ENOENT; } static int fit_get_data_tail(const void *fit, int noffset, @@ -1949,7 +1967,8 @@ int fit_get_data_conf_prop(const void *fit, const char *prop_name, { int noffset = fit_conf_get_node(fit, NULL); - noffset = fit_conf_get_prop_node(fit, noffset, prop_name); + noffset = fit_conf_get_prop_node(fit, noffset, prop_name, + IH_PHASE_NONE); return fit_get_data_tail(fit, noffset, data, size); } @@ -1987,7 +2006,8 @@ int fit_get_node_from_config(struct bootm_headers *images, return -EINVAL; } - noffset = fit_conf_get_prop_node(fit_hdr, cfg_noffset, prop_name); + noffset = fit_conf_get_prop_node(fit_hdr, cfg_noffset, prop_name, + IH_PHASE_NONE); if (noffset < 0) { debug("* %s: no '%s' in config\n", prop_name, prop_name); return -ENOENT; @@ -2033,9 +2053,10 @@ static const char *fit_get_image_type_property(int type) int fit_image_load(struct bootm_headers *images, ulong addr, const char **fit_unamep, const char **fit_uname_configp, - int arch, int image_type, int bootstage_id, + int arch, int ph_type, int bootstage_id, enum fit_load_op load_op, ulong *datap, ulong *lenp) { + int image_type = image_ph_type(ph_type); int cfg_noffset, noffset; const char *fit_uname; const char *fit_uname_config; @@ -2081,8 +2102,7 @@ int fit_image_load(struct bootm_headers *images, ulong addr, if (IS_ENABLED(CONFIG_FIT_BEST_MATCH) && !fit_uname_config) { cfg_noffset = fit_conf_find_compat(fit, gd_fdt_blob()); } else { - cfg_noffset = fit_conf_get_node(fit, - fit_uname_config); + cfg_noffset = fit_conf_get_node(fit, fit_uname_config); } if (cfg_noffset < 0) { puts("Could not find configuration node\n"); @@ -2110,8 +2130,8 @@ int fit_image_load(struct bootm_headers *images, ulong addr, bootstage_mark(BOOTSTAGE_ID_FIT_CONFIG); - noffset = fit_conf_get_prop_node(fit, cfg_noffset, - prop_name); + noffset = fit_conf_get_prop_node(fit, cfg_noffset, prop_name, + image_ph_phase(ph_type)); fit_uname = fit_get_name(fit, noffset, NULL); } if (noffset < 0) { diff --git a/boot/image.c b/boot/image.c index 9f95b3260a8..b33d1dfc6b3 100644 --- a/boot/image.c +++ b/boot/image.c @@ -194,6 +194,13 @@ static const table_entry_t uimage_comp[] = { { -1, "", "", }, }; +static const table_entry_t uimage_phase[] = { + { IH_PHASE_NONE, "none", "any", }, + { IH_PHASE_U_BOOT, "u-boot", "U-Boot phase", }, + { IH_PHASE_SPL, "spl", "SPL Phase", }, + { -1, "", "", }, +}; + struct table_info { const char *desc; int count; @@ -215,6 +222,7 @@ static const struct table_info table_info[IH_COUNT] = { { "compression", IH_COMP_COUNT, uimage_comp }, { "operating system", IH_OS_COUNT, uimage_os }, { "image type", IH_TYPE_COUNT, uimage_type }, + { "phase", IH_PHASE_COUNT, uimage_phase }, }; /*****************************************************************************/ @@ -656,6 +664,11 @@ const char *genimg_get_comp_name(uint8_t comp) comp)); } +const char *genimg_get_phase_name(enum image_phase_t phase) +{ + return get_table_entry_name(uimage_phase, "Unknown Phase", phase); +} + static const char *genimg_get_short_name(const table_entry_t *table, int val) { table = get_table_entry(table, val); @@ -731,3 +744,8 @@ int genimg_get_comp_id(const char *name) { return (get_table_entry_id(uimage_comp, "Compression", name)); } + +int genimg_get_phase_id(const char *name) +{ + return get_table_entry_id(uimage_phase, "Phase", name); +} diff --git a/boot/vbe_fixup.c b/boot/vbe_request.c index 53d88678c92..45f1d2b7e17 100644 --- a/boot/vbe_fixup.c +++ b/boot/vbe_request.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Verified Boot for Embedded (VBE) device tree fixup functions + * Verified Boot for Embedded (VBE) OS request (device tree fixup) functions * * Copyright 2022 Google LLC * Written by Simon Glass <sjg@chromium.org> @@ -36,7 +36,7 @@ static int handle_random_req(ofnode node, int default_size, u32 size; int ret; - if (!IS_ENABLED(CONFIG_DM_RNG)) + if (!CONFIG_IS_ENABLED(DM_RNG)) return -ENOTSUPP; if (ofnode_read_u32(node, "vbe,size", &size)) { @@ -195,8 +195,8 @@ static int bootmeth_vbe_ft_fixup(void *ctx, struct event *event) ret = vbe_process_request(dest, &result); if (ret) { result.errnum = ret; - log_err("Failed to process VBE request %s (err=%d)\n", - ofnode_get_name(dest), ret); + log_warning("Failed to process VBE request %s (err=%d)\n", + ofnode_get_name(dest), ret); if (*result.err_str) { char *msg = strdup(result.err_str); diff --git a/boot/vbe_simple.c b/boot/vbe_simple.c index 076b650c25a..59676d8613f 100644 --- a/boot/vbe_simple.c +++ b/boot/vbe_simple.c @@ -9,55 +9,18 @@ #define LOG_CATEGORY LOGC_BOOT #include <common.h> -#include <log.h> -#include <memalign.h> -#include <part.h> +#include <bootdev.h> #include <bootflow.h> #include <bootmeth.h> #include <dm.h> +#include <log.h> +#include <memalign.h> #include <mmc.h> #include <vbe.h> -#include <version_string.h> #include <dm/device-internal.h> #include <dm/ofnode.h> #include <u-boot/crc.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 */ -}; - -/** struct simple_priv - information read from the device tree */ -struct simple_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 simple_state - state information read from media - * - * @fw_version: Firmware version string - * @fw_vernum: Firmware version number - */ -struct simple_state { - char fw_version[MAX_VERSION_LEN]; - u32 fw_vernum; -}; +#include "vbe_simple.h" /** struct simple_nvdata - storage format for non-volatile data */ struct simple_nvdata { @@ -127,7 +90,7 @@ static int simple_read_nvdata(struct udevice *dev, struct blk_desc *desc, return 0; } -static int simple_read_state(struct udevice *dev, struct simple_state *state) +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); @@ -168,7 +131,7 @@ static int vbe_simple_get_state_desc(struct udevice *dev, char *buf, struct simple_state state; int ret; - ret = simple_read_state(dev, &state); + ret = vbe_simple_read_state(dev, &state); if (ret) return log_msg_ret("read", ret); @@ -183,88 +146,39 @@ static int vbe_simple_get_state_desc(struct udevice *dev, char *buf, static int vbe_simple_read_bootflow(struct udevice *dev, struct bootflow *bflow) { - /* To be implemented */ - - return -EINVAL; -} - -static struct bootmeth_ops bootmeth_vbe_simple_ops = { - .get_state_desc = vbe_simple_get_state_desc, - .read_bootflow = vbe_simple_read_bootflow, - .read_file = bootmeth_common_read_file, -}; - -int vbe_simple_fixup_node(ofnode node, struct simple_state *state) -{ - char *version; int ret; - version = strdup(state->fw_version); - if (!version) - return log_msg_ret("dup", -ENOMEM); - - ret = ofnode_write_string(node, "cur-version", version); - if (ret) - return log_msg_ret("ver", ret); - ret = ofnode_write_u32(node, "cur-vernum", state->fw_vernum); - if (ret) - return log_msg_ret("num", ret); - ret = ofnode_write_string(node, "bootloader-version", version_string); - if (ret) - return log_msg_ret("bl", ret); + if (vbe_phase() == VBE_PHASE_FIRMWARE) { + ret = vbe_simple_read_bootflow_fw(dev, bflow); + if (ret) + return log_msg_ret("fw", ret); + return 0; + } - return 0; + return -EINVAL; } -/** - * bootmeth_vbe_simple_ft_fixup() - Write out all VBE simple data to the DT - * - * @ctx: Context for event - * @event: Event to process - * @return 0 if OK, -ve on error - */ -static int bootmeth_vbe_simple_ft_fixup(void *ctx, struct event *event) +static int vbe_simple_read_file(struct udevice *dev, struct bootflow *bflow, + const char *file_path, ulong addr, ulong *sizep) { - oftree tree = event->data.ft_fixup.tree; - struct udevice *dev; - - /* - * Ideally we would have driver model support for fixups, but that does - * not exist yet. It is a step too far to try to do this before VBE is - * in place. - */ - for (vbe_find_first_device(&dev); dev; vbe_find_next_device(&dev)) { - struct simple_state state; - ofnode node, subnode; - int ret; - - if (strcmp("vbe_simple", dev->driver->name)) - continue; - - /* Check if there is a node to fix up */ - node = oftree_path(tree, "/chosen/fwupd"); - if (!ofnode_valid(node)) - continue; - subnode = ofnode_find_subnode(node, dev->name); - if (!ofnode_valid(subnode)) - continue; - - log_debug("Fixing up: %s\n", dev->name); - ret = device_probe(dev); - if (ret) - return log_msg_ret("probe", ret); - ret = simple_read_state(dev, &state); - if (ret) - return log_msg_ret("read", ret); + int ret; - ret = vbe_simple_fixup_node(subnode, &state); + if (vbe_phase() == VBE_PHASE_OS) { + ret = bootmeth_common_read_file(dev, bflow, file_path, addr, + sizep); if (ret) - return log_msg_ret("fix", ret); + return log_msg_ret("os", ret); } - return 0; + /* To be implemented */ + return -EINVAL; } -EVENT_SPY(EVT_FT_FIXUP, bootmeth_vbe_simple_ft_fixup); + +static struct bootmeth_ops bootmeth_vbe_simple_ops = { + .get_state_desc = vbe_simple_get_state_desc, + .read_bootflow = vbe_simple_read_bootflow, + .read_file = vbe_simple_read_file, +}; static int bootmeth_vbe_simple_probe(struct udevice *dev) { diff --git a/boot/vbe_simple.h b/boot/vbe_simple.h new file mode 100644 index 00000000000..56d319206f2 --- /dev/null +++ b/boot/vbe_simple.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Verified Boot for Embedded (VBE) vbe-simple common file + * + * Copyright 2022 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#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 */ +}; + +/** struct simple_priv - information read from the device tree */ +struct simple_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 simple_state - state information read from media + * + * @fw_version: Firmware version string + * @fw_vernum: Firmware version number + */ +struct simple_state { + char fw_version[MAX_VERSION_LEN]; + u32 fw_vernum; +}; + +/** + * vbe_simple_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 + * @blow: Place to put the created bootflow, on success + * @return 0 if OK, -ve on error + */ +int vbe_simple_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 vbe_simple_read_state(struct udevice *dev, struct simple_state *state); + +#endif /* __VBE_SIMPLE_H */ diff --git a/boot/vbe_simple_fw.c b/boot/vbe_simple_fw.c new file mode 100644 index 00000000000..0a49d286703 --- /dev/null +++ b/boot/vbe_simple_fw.c @@ -0,0 +1,206 @@ +// 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 <common.h> +#include <bloblist.h> +#include <bootdev.h> +#include <bootflow.h> +#include <bootmeth.h> +#include <bootstage.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_simple.h" + +/** + * vbe_simple_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 simple bootmeth + * @blow: Place to put the created bootflow, on success + * @return 0 if OK, -ve on error + */ +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; + struct udevice *blk; + int node, ret; + void *buf; + + 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); + } + + /* 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; + + return 0; +} + +static int simple_load_from_image(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + struct udevice *meth, *bdev; + struct simple_priv *priv; + struct bootflow bflow; + struct vbe_handoff *handoff; + int ret; + + if (spl_phase() != PHASE_VPL && spl_phase() != PHASE_SPL) + return -ENOENT; + + ret = bloblist_ensure_size(BLOBLISTT_VBE, sizeof(struct vbe_handoff), + 0, (void **)&handoff); + 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); + 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); + + /* this is not used from now on, so free it */ + bootflow_free(&bflow); + + /* Record that VBE was used in this phase */ + handoff->phases |= 1 << spl_phase(); + + return 0; +} +SPL_LOAD_IMAGE_METHOD("vbe_simple", 5, BOOT_DEVICE_VBE, + simple_load_from_image); diff --git a/boot/vbe_simple_os.c b/boot/vbe_simple_os.c new file mode 100644 index 00000000000..b2041a95a30 --- /dev/null +++ b/boot/vbe_simple_os.c @@ -0,0 +1,104 @@ +// 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 <common.h> +#include <dm.h> +#include <bootflow.h> +#include <vbe.h> +#include <version_string.h> +#include <dm/device-internal.h> +#include "vbe_simple.h" + +int vbe_simple_fixup_node(ofnode node, struct simple_state *state) +{ + const char *version, *str; + int ret; + + version = strdup(state->fw_version); + if (!version) + return log_msg_ret("dup", -ENOMEM); + + ret = ofnode_write_string(node, "cur-version", version); + if (ret) + return log_msg_ret("ver", ret); + ret = ofnode_write_u32(node, "cur-vernum", state->fw_vernum); + if (ret) + return log_msg_ret("num", ret); + + /* Drop the 'U-Boot ' at the start */ + str = version_string; + if (!strncmp("U-Boot ", str, 7)) + str += 7; + ret = ofnode_write_string(node, "bootloader-version", str); + if (ret) + return log_msg_ret("bl", ret); + + return 0; +} + +/** + * bootmeth_vbe_simple_ft_fixup() - Write out all VBE simple data to the DT + * + * @ctx: Context for event + * @event: Event to process + * @return 0 if OK, -ve on error + */ +static int bootmeth_vbe_simple_ft_fixup(void *ctx, struct event *event) +{ + oftree tree = event->data.ft_fixup.tree; + struct udevice *dev; + + /* + * Ideally we would have driver model support for fixups, but that does + * not exist yet. It is a step too far to try to do this before VBE is + * in place. + */ + for (vbe_find_first_device(&dev); dev; vbe_find_next_device(&dev)) { + struct simple_state state; + ofnode node, subnode, chosen; + int ret; + + if (strcmp("vbe_simple", dev->driver->name)) + continue; + + /* Check if there is a node to fix up, adding if not */ + chosen = oftree_path(tree, "/chosen"); + if (!ofnode_valid(chosen)) + continue; + ret = ofnode_add_subnode(chosen, "fwupd", &node); + if (ret && ret != -EEXIST) + return log_msg_ret("fwu", ret); + + ret = ofnode_add_subnode(node, dev->name, &subnode); + if (ret && ret != -EEXIST) + return log_msg_ret("dev", ret); + + ret = device_probe(dev); + if (ret) + return log_msg_ret("probe", ret); + + /* Copy over the vbe properties for fwupd */ + log_debug("Fixing up: %s\n", dev->name); + ret = ofnode_copy_props(dev_ofnode(dev), subnode); + if (ret) + return log_msg_ret("cp", ret); + + ret = vbe_simple_read_state(dev, &state); + if (ret) + return log_msg_ret("read", ret); + + ret = vbe_simple_fixup_node(subnode, &state); + if (ret) + return log_msg_ret("fix", ret); + } + + return 0; +} +EVENT_SPY(EVT_FT_FIXUP, bootmeth_vbe_simple_ft_fixup); |