diff options
| author | Tom Rini <[email protected]> | 2025-01-22 09:48:24 -0600 |
|---|---|---|
| committer | Tom Rini <[email protected]> | 2025-01-22 11:21:58 -0600 |
| commit | 5fe39e5df4265622a35ad36d0ce13e6b1a4f158e (patch) | |
| tree | a01fe788c157820fdbc6a22a7700d23f8260114e /common/spl | |
| parent | 72ff74dc09b7a9c62c4b3ccbb6c7bd6c9874ef47 (diff) | |
| parent | 68727fac696537b368324fbff61094f8b0d82048 (diff) | |
Merge patch series "vbe: Series part F"
Simon Glass <[email protected]> says:
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
Link: https://lore.kernel.org/r/[email protected]
Diffstat (limited to 'common/spl')
| -rw-r--r-- | common/spl/Kconfig | 8 | ||||
| -rw-r--r-- | common/spl/Kconfig.tpl | 8 | ||||
| -rw-r--r-- | common/spl/Kconfig.vpl | 8 | ||||
| -rw-r--r-- | common/spl/Makefile | 1 | ||||
| -rw-r--r-- | common/spl/spl.c | 15 | ||||
| -rw-r--r-- | common/spl/spl_reloc.c | 183 |
6 files changed, 221 insertions, 2 deletions
diff --git a/common/spl/Kconfig b/common/spl/Kconfig index 4e56d9909c8..94e118f8465 100644 --- a/common/spl/Kconfig +++ b/common/spl/Kconfig @@ -983,6 +983,14 @@ config SPL_NAND_IDENT help SPL uses the chip ID list to identify the NAND flash. +config SPL_RELOC_LOADER + bool "Allow relocating the next phase" + help + In some cases multiple U-Boot phases need to run in SRAM, typically + at the same address. Enable this to support loading the next phase + to temporary memory, then copying it into place afterwards, then + jumping to it. + config SPL_UBI bool "Support UBI" help diff --git a/common/spl/Kconfig.tpl b/common/spl/Kconfig.tpl index 92d4d43ec87..22ca7016453 100644 --- a/common/spl/Kconfig.tpl +++ b/common/spl/Kconfig.tpl @@ -268,6 +268,14 @@ config TPL_RAM_DEVICE be already in memory when TPL takes over, e.g. loaded by the boot ROM. +config TPL_RELOC_LOADER + bool "Allow relocating the next phase" + help + In some cases multiple U-Boot phases need to run in SRAM, typically + at the same address. Enable this to support loading the next phase + to temporary memory, then copying it into place afterwards, then + jumping to it. + config TPL_RTC bool "Support RTC drivers" help diff --git a/common/spl/Kconfig.vpl b/common/spl/Kconfig.vpl index eb57dfabea5..97dfc630152 100644 --- a/common/spl/Kconfig.vpl +++ b/common/spl/Kconfig.vpl @@ -181,6 +181,14 @@ config VPL_PCI necessary driver support. This enables the drivers in drivers/pci as part of a VPL build. +config VPL_RELOC_LOADER + bool "Allow relocating the next phase" + help + In some cases multiple U-Boot phases need to run in SRAM, typically + at the same address. Enable this to support loading the next phase + to temporary memory, then copying it into place afterwards, then + jumping to it. + config VPL_RTC bool "Support RTC drivers" help diff --git a/common/spl/Makefile b/common/spl/Makefile index 75123eb666b..4c9482bd309 100644 --- a/common/spl/Makefile +++ b/common/spl/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_$(PHASE_)BOOTROM_SUPPORT) += spl_bootrom.o obj-$(CONFIG_$(PHASE_)LOAD_FIT) += spl_fit.o obj-$(CONFIG_$(PHASE_)BLK_FS) += spl_blk_fs.o obj-$(CONFIG_$(PHASE_)LEGACY_IMAGE_FORMAT) += spl_legacy.o +obj-$(CONFIG_$(PHASE_)RELOC_LOADER) += spl_reloc.o obj-$(CONFIG_$(PHASE_)NOR_SUPPORT) += spl_nor.o obj-$(CONFIG_$(PHASE_)XIP_SUPPORT) += spl_xip.o obj-$(CONFIG_$(PHASE_)YMODEM_SUPPORT) += spl_ymodem.o diff --git a/common/spl/spl.c b/common/spl/spl.c index 02269fff93c..9af0a4954d4 100644 --- a/common/spl/spl.c +++ b/common/spl/spl.c @@ -675,8 +675,7 @@ void board_init_r(gd_t *dummy1, ulong dummy2) BOOT_DEVICE_NONE, BOOT_DEVICE_NONE, }; - typedef void __noreturn (*jump_to_image_t)(struct spl_image_info *); - jump_to_image_t jump_to_image = &jump_to_image_no_args; + spl_jump_to_image_t jump_to_image = &jump_to_image_no_args; struct spl_image_info spl_image; int ret, os; @@ -831,6 +830,18 @@ void board_init_r(gd_t *dummy1, ulong dummy2) } spl_board_prepare_for_boot(); + + if (CONFIG_IS_ENABLED(RELOC_LOADER)) { + int ret; + + ret = spl_reloc_jump(&spl_image, jump_to_image); + if (ret) { + if (xpl_phase() == PHASE_VPL) + printf("jump failed %d\n", ret); + hang(); + } + } + jump_to_image(&spl_image); } diff --git a/common/spl/spl_reloc.c b/common/spl/spl_reloc.c new file mode 100644 index 00000000000..be8349b535b --- /dev/null +++ b/common/spl/spl_reloc.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 Google LLC + * Written by Simon Glass <[email protected]> + */ + +#include <gzip.h> +#include <image.h> +#include <log.h> +#include <mapmem.h> +#include <spl.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <asm/sections.h> +#include <asm/unaligned.h> +#include <linux/types.h> +#include <lzma/LzmaTypes.h> +#include <lzma/LzmaDec.h> +#include <lzma/LzmaTools.h> +#include <u-boot/crc.h> +#include <u-boot/lz4.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* provide a way to jump straight into the relocation code, for debugging */ +#define DEBUG_JUMP 0 + +enum { + /* margin to allow for stack growth */ + RELOC_STACK_MARGIN = 0x800, + + /* align base address for DMA controllers which require it */ + BASE_ALIGN = 0x200, + + STACK_PROT_VALUE = 0x51ce4697, +}; + +typedef int (*rcode_func)(struct spl_image_info *image); + +static int setup_layout(struct spl_image_info *image, ulong *addrp) +{ + ulong base, fdt_size; + ulong limit, rcode_base; + uint rcode_size; + int buf_size, margin; + char *rcode_buf; + + limit = ALIGN(map_to_sysmem(&limit) - RELOC_STACK_MARGIN, 8); + image->stack_prot = map_sysmem(limit, sizeof(uint)); + *image->stack_prot = STACK_PROT_VALUE; + + fdt_size = fdt_totalsize(gd->fdt_blob); + base = ALIGN(map_to_sysmem(gd->fdt_blob) + fdt_size + BASE_ALIGN - 1, + BASE_ALIGN); + + rcode_size = _rcode_end - _rcode_start; + rcode_base = limit - rcode_size; + buf_size = rcode_base - base; + uint need_size = image->size + image->fdt_size; + margin = buf_size - need_size; + log_debug("spl_reloc %s->%s: margin%s%lx limit %lx fdt_size %lx base %lx avail %x image %x fdt %lx need %x\n", + spl_phase_name(spl_phase()), spl_phase_name(spl_phase() + 1), + margin >= 0 ? " " : " -", abs(margin), limit, fdt_size, base, + buf_size, image->size, image->fdt_size, need_size); + if (margin < 0) { + log_err("Image size %x but buffer is only %x\n", need_size, + buf_size); + return -ENOSPC; + } + + rcode_buf = map_sysmem(rcode_base, rcode_size); + log_debug("_rcode_start %p: %x -- func %p %x\n", _rcode_start, + *(uint *)_rcode_start, setup_layout, *(uint *)setup_layout); + + image->reloc_offset = rcode_buf - _rcode_start; + log_debug("_rcode start %lx base %lx size %x offset %lx\n", + (ulong)map_to_sysmem(_rcode_start), rcode_base, rcode_size, + image->reloc_offset); + + memcpy(rcode_buf, _rcode_start, rcode_size); + + image->buf = map_sysmem(base, need_size); + image->fdt_buf = image->buf + image->size; + image->rcode_buf = rcode_buf; + *addrp = base; + + return 0; +} + +int spl_reloc_prepare(struct spl_image_info *image, ulong *addrp) +{ + int ret; + + ret = setup_layout(image, addrp); + if (ret) + return ret; + + return 0; +} + +typedef void __noreturn (*image_entry_noargs_t)(uint crc, uint unc_len); + +/* this is the relocation + jump code that is copied to the top of memory */ +__rcode int rcode_reloc_and_jump(struct spl_image_info *image) +{ + image_entry_noargs_t entry = (image_entry_noargs_t)image->entry_point; + u32 *dst; + ulong image_len; + size_t unc_len; + int ret, crc; + uint magic; + + dst = map_sysmem(image->load_addr, image->size); + unc_len = (void *)image->rcode_buf - (void *)dst; + image_len = image->size; + if (*image->stack_prot != STACK_PROT_VALUE) + return -EFAULT; + magic = get_unaligned_le32(image->buf); + if (CONFIG_IS_ENABLED(LZMA)) { + SizeT lzma_len = unc_len; + + ret = lzmaBuffToBuffDecompress((u8 *)dst, &lzma_len, + image->buf, image_len); + unc_len = lzma_len; + } else if (CONFIG_IS_ENABLED(GZIP)) { + ret = gunzip(dst, unc_len, image->buf, &image_len); + } else if (CONFIG_IS_ENABLED(LZ4) && magic == LZ4F_MAGIC) { + ret = ulz4fn(image->buf, image_len, dst, &unc_len); + if (ret) + return ret; + } else { + u32 *src, *end, *ptr; + + unc_len = image->size; + for (src = image->buf, end = (void *)src + image->size, + ptr = dst; src < end;) + *ptr++ = *src++; + } + if (*image->stack_prot != STACK_PROT_VALUE) + return -EFAULT; + + /* copy in the FDT if needed */ + if (image->fdt_size) + memcpy(image->fdt_start, image->fdt_buf, image->fdt_size); + + crc = crc8(0, (u8 *)dst, unc_len); + + /* jump to the entry point */ + entry(crc, unc_len); +} + +int spl_reloc_jump(struct spl_image_info *image, spl_jump_to_image_t jump) +{ + rcode_func loader; + int ret; + + log_debug("malloc usage %lx bytes (%ld KB of %d KB)\n", gd->malloc_ptr, + gd->malloc_ptr / 1024, CONFIG_VAL(SYS_MALLOC_F_LEN) / 1024); + + if (*image->stack_prot != STACK_PROT_VALUE) { + log_err("stack busted, cannot continue\n"); + return -EFAULT; + } + loader = (rcode_func)(void *)rcode_reloc_and_jump + image->reloc_offset; + log_debug("Jumping via %p to %lx - image %p size %x load %lx\n", loader, + image->entry_point, image, image->size, image->load_addr); + + log_debug("unc_len %lx\n", + image->rcode_buf - map_sysmem(image->load_addr, image->size)); + if (DEBUG_JUMP) { + rcode_reloc_and_jump(image); + } else { + /* + * Must disable LOG_DEBUG since the decompressor cannot call + * log functions, printf(), etc. + */ + _Static_assert(DEBUG_JUMP || !_DEBUG, + "Cannot have debug output from decompressor"); + ret = loader(image); + } + + return -EFAULT; +} |
