From d2b22ae23196604fda88e1ad9ec9f0e8fd285d07 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 20 Oct 2022 18:23:10 -0600 Subject: [PATCH] vbe: Support reading the next SPL phase via VBE Add an SPL loader to obtain the next-phase binary from a FIT provided by the VBE driver. Signed-off-by: Simon Glass --- arch/sandbox/cpu/spl.c | 3 +- arch/sandbox/include/asm/spl.h | 1 + boot/Makefile | 1 + boot/vbe_request.c | 2 +- boot/vbe_simple.c | 46 ++++++---- boot/vbe_simple.h | 37 ++++++++ boot/vbe_simple_fw.c | 196 +++++++++++++++++++++++++++++++++++++++++ include/bootstage.h | 2 + 8 files changed, 269 insertions(+), 19 deletions(-) create mode 100644 boot/vbe_simple.h create mode 100644 boot/vbe_simple_fw.c diff --git a/arch/sandbox/cpu/spl.c b/arch/sandbox/cpu/spl.c index 75f4601..0faf34c 100644 --- a/arch/sandbox/cpu/spl.c +++ b/arch/sandbox/cpu/spl.c @@ -51,7 +51,8 @@ void board_init_f(ulong flag) void board_boot_order(u32 *spl_boot_list) { - spl_boot_list[0] = BOOT_DEVICE_BOARD; + spl_boot_list[0] = BOOT_DEVICE_VBE; + spl_boot_list[1] = BOOT_DEVICE_BOARD; } static int spl_board_load_file(struct spl_image_info *spl_image, diff --git a/arch/sandbox/include/asm/spl.h b/arch/sandbox/include/asm/spl.h index 312aef7..2f8b5fc 100644 --- a/arch/sandbox/include/asm/spl.h +++ b/arch/sandbox/include/asm/spl.h @@ -11,6 +11,7 @@ enum { BOOT_DEVICE_MMC2, BOOT_DEVICE_MMC2_2, BOOT_DEVICE_BOARD, + BOOT_DEVICE_VBE, }; /** diff --git a/boot/Makefile b/boot/Makefile index 19d628d..e5c2790 100644 --- a/boot/Makefile +++ b/boot/Makefile @@ -49,3 +49,4 @@ endif 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 diff --git a/boot/vbe_request.c b/boot/vbe_request.c index 312edfa..45f1d2b 100644 --- a/boot/vbe_request.c +++ b/boot/vbe_request.c @@ -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)) { diff --git a/boot/vbe_simple.c b/boot/vbe_simple.c index 076b650..1ccd416 100644 --- a/boot/vbe_simple.c +++ b/boot/vbe_simple.c @@ -9,18 +9,19 @@ #define LOG_CATEGORY LOGC_BOOT #include -#include -#include -#include +#include #include #include #include +#include +#include #include #include #include #include #include #include +#include "vbe_simple.h" enum { MAX_VERSION_LEN = 256, @@ -37,18 +38,6 @@ enum { 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 @@ -183,15 +172,38 @@ 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 */ + int 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 -EINVAL; } +static int vbe_simple_read_file(struct udevice *dev, struct bootflow *bflow, + const char *file_path, ulong addr, ulong *sizep) +{ + int ret; + + if (vbe_phase() == VBE_PHASE_OS) { + ret = bootmeth_common_read_file(dev, bflow, file_path, addr, + sizep); + if (ret) + return log_msg_ret("os", ret); + } + + /* 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, + .read_file = vbe_simple_read_file, }; int vbe_simple_fixup_node(ofnode node, struct simple_state *state) diff --git a/boot/vbe_simple.h b/boot/vbe_simple.h new file mode 100644 index 0000000..e37a9fa --- /dev/null +++ b/boot/vbe_simple.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Verified Boot for Embedded (VBE) vbe-simple common file + * + * Copyright 2022 Google LLC + * Written by Simon Glass + */ + +#ifndef __VBE_SIMPLE_H +#define __VBE_SIMPLE_H + +/** 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; +}; + +/** + * 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); + +#endif /* __VBE_SIMPLE_H */ diff --git a/boot/vbe_simple_fw.c b/boot/vbe_simple_fw.c new file mode 100644 index 0000000..fc05e9e --- /dev/null +++ b/boot/vbe_simple_fw.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Verified Boot for Embedded (VBE) loading firmware phases + * + * Copyright 2022 Google LLC + * Written by Simon Glass + */ + +#define LOG_CATEGORY LOGC_BOOT + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; + int ret; + + if (spl_phase() != PHASE_VPL && spl_phase() != PHASE_SPL) + return -ENOENT; + + 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); + + return 0; +} +SPL_LOAD_IMAGE_METHOD("vbe_simple", 5, BOOT_DEVICE_VBE, + simple_load_from_image); diff --git a/include/bootstage.h b/include/bootstage.h index 7088d0b..685939c 100644 --- a/include/bootstage.h +++ b/include/bootstage.h @@ -166,6 +166,8 @@ enum bootstage_id { BOOTSTAGE_ID_NAND_FIT_READ_OK, BOOTSTAGE_ID_FIT_LOADABLE_START = 160, /* for Loadable Images */ + + BOOTSTAGE_ID_FIT_SPL_START = 170, /* for SPL Images */ /* * These boot stages are new, higher level, and not directly related * to the old boot progress numbers. They are useful for recording -- 2.7.4