For a sandbox implementation, where code size is no object, it makes sense
to use the full bootstd drivers to load images.
For real boards, running from SRAM, this adds quite a bit of overhead.
Add a way to load the next phase using just the underlying storage
driver, to reduce code size. For now, only MMC is supported.
Change the log_debug() to show the load address and size in a more
neutral way, rather than suggesting that the load has already happened.
Series-to: u-boot
Series-cc: trini
Series-version: 2
Series-changes: 2
- Split up the first patch into 5 separate patches, to highlight code size
Cover-letter:
vbe: Series part F
This includes various patches towards implementing the VBE abrec
bootmeth in U-Boot. It mostly focuses on introducing a relocating
SPL-loader so that VBE can run in the limited amount of SRAM available
on many devices.
Another minor new feature is support in VBE for specifying the image
phase when loading from a FIT. This allows a single FIT to include
images for several boot phases, thus simplifying image-creation.
One lingering niggle in this series is that it has a different code path
for sandbox, since it does not support the relocating jump. It should be
possible to resolve this with additional work, but I have not attempted
this so far.
For v2, I have split the first patch into 5 pieces, to make it easier to
see the code-size impact, plus added a few tweaks to reduce code size.
Again, only MMC is supported so far.
Looking ahead, series G will have some more plumbing and H some rk3399
pieces. That should be enough to complete these feature.
Here is a run in my lab, with the VBE ABrec bootmeth. You can see that
VPL runs before memory is set up. SPL sets up memory and can be upgraded
in the field reliably.
$ ub-int vbe
Building U-Boot in sourcedir for rk3399-generic
Bootstrapping U-Boot from dir /tmp/b/rk3399-generic
Writing U-Boot using method rockchip
U-Boot TPL 2025.01-rc3-00345-gdfbdbf1eb56c-dirty (Jan 08 2025 - 10:47:58)
Trying to boot from vbe_abrec
load: Firefly-RK3399 Board
Using 'config-3' configuration
Trying 'image-vpl' firmware subimage
Using 'config-3' configuration
Trying 'fdt-3' fdt subimage
U-Boot VPL 2025.01-rc3-00345-gdfbdbf1eb56c-dirty (Jan 08 2025 - 10:47:58)
Trying to boot from vbe_abrec
load: Firefly-RK3399 Board
Starting with empty state
VBE: Firmware pick A at 800000
Using 'config-3' configuration
Trying 'spl' firmware subimage
Using 'config-3' configuration
Trying 'fdt-3' fdt subimage
Channel 0: DDR3, 800MHz
BW=32 Col=10 Bk=8 CS0 Row=15 CS1 Row=15 CS=2 Die BW=16 Size=2048MB
Channel 1: DDR3, 800MHz
BW=32 Col=10 Bk=8 CS0 Row=15 CS1 Row=15 CS=2 Die BW=16 Size=2048MB
256B stride
U-Boot SPL 2025.01-rc3-00345-gdfbdbf1eb56c-dirty (Jan 08 2025 - 10:47:58 -0700)
Trying to boot from vbe_abrec
load: Firefly-RK3399 Board
VBE: Firmware pick A at 900000
load_simple_fit: Skip load 'atf-5': image size is 0!
Relocating bloblist ff8eff00 to 100000: done
ns16550_serial serial@ff1a0000: pinctrl_select_state_full: uclass_get_device_by_phandle_id: err=-19
U-Boot 2025.01-rc3-00345-gdfbdbf1eb56c-dirty (Jan 08 2025 - 10:47:58 -0700)
SoC: Rockchip rk3399
Reset cause: POR
Model: Firefly-RK3399 Board
DRAM: 4 GiB (effective 3.9 GiB)
Core: 314 devices, 33 uclasses, devicetree: separate
MMC: mmc@fe310000: 3, mmc@fe320000: 1, mmc@fe330000: 0
Loading Environment from SPIFlash... Invalid bus 0 (err=-19)
*** Warning - spi_flash_probe_bus_cs() failed, using default environment
In: serial,usbkbd
Out: serial,vidconsole
Err: serial,vidconsole
Model: Firefly-RK3399 Board
Net: PMIC: RK808
eth0: ethernet@fe300000
starting USB...
Bus usb@fe380000: USB EHCI 1.00
Bus usb@fe3a0000: USB OHCI 1.0
Bus usb@fe3c0000: USB EHCI 1.00
Bus usb@fe3e0000: USB OHCI 1.0
Bus usb@fe900000: Register 2000140 NbrPorts 2
Starting the controller
USB XHCI 1.10
scanning bus usb@fe380000 for devices... 1 USB Device(s) found
scanning bus usb@fe3a0000 for devices... 1 USB Device(s) found
scanning bus usb@fe3c0000 for devices... 2 USB Device(s) found
scanning bus usb@fe3e0000 for devices... 1 USB Device(s) found
scanning bus usb@fe900000 for devices... 1 USB Device(s) found
scanning usb for storage devices... 0 Storage Device(s) found
Hit any key to stop autoboot: 0
END
Signed-off-by: Simon Glass <sjg@chromium.org>
161 lines
4.1 KiB
C
161 lines
4.1 KiB
C
// 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 <dm.h>
|
|
#include <image.h>
|
|
#include <log.h>
|
|
#include <mapmem.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
|
|
*
|
|
* 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)
|
|
{
|
|
struct udevice *media = dev_get_parent(bflow->dev);
|
|
struct udevice *meth = bflow->method;
|
|
struct simple_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);
|
|
log_debug("blk=%s\n", blk->name);
|
|
|
|
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 simple_load_from_image(struct spl_image_info *image,
|
|
struct spl_boot_device *bootdev)
|
|
{
|
|
struct vbe_handoff *handoff;
|
|
int ret;
|
|
|
|
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 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);
|
|
|
|
/* 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();
|
|
|
|
return 0;
|
|
}
|
|
SPL_LOAD_IMAGE_METHOD("vbe_simple", 5, BOOT_DEVICE_VBE,
|
|
simple_load_from_image);
|