"Das U-Boot" Source Tree
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge patch series "vbe: Series part F"

Simon Glass <sjg@chromium.org> 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/20250116012723.2820301-1-sjg@chromium.org

+919 -219
+1 -1
boot/Makefile
··· 65 65 66 66 obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE) += vbe.o 67 67 obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_REQUEST) += vbe_request.o 68 - obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE) += vbe_simple.o 68 + obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE) += vbe_simple.o vbe_common.o 69 69 obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_FW) += vbe_simple_fw.o 70 70 obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_OS) += vbe_simple_os.o 71 71
+375
boot/vbe_common.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Verified Boot for Embedded (VBE) common functions 4 + * 5 + * Copyright 2024 Google LLC 6 + * Written by Simon Glass <sjg@chromium.org> 7 + */ 8 + 9 + #include <bootstage.h> 10 + #include <dm.h> 11 + #include <blk.h> 12 + #include <image.h> 13 + #include <mapmem.h> 14 + #include <memalign.h> 15 + #include <spl.h> 16 + #include <u-boot/crc.h> 17 + #include "vbe_common.h" 18 + 19 + binman_sym_declare(ulong, u_boot_vpl_nodtb, size); 20 + binman_sym_declare(ulong, u_boot_vpl_bss_pad, size); 21 + binman_sym_declare(ulong, u_boot_spl_nodtb, size); 22 + binman_sym_declare(ulong, u_boot_spl_bss_pad, size); 23 + 24 + int vbe_get_blk(const char *storage, struct udevice **blkp) 25 + { 26 + struct blk_desc *desc; 27 + char devname[16]; 28 + const char *end; 29 + int devnum; 30 + 31 + /* First figure out the block device */ 32 + log_debug("storage=%s\n", storage); 33 + devnum = trailing_strtoln_end(storage, NULL, &end); 34 + if (devnum == -1) 35 + return log_msg_ret("num", -ENODEV); 36 + if (end - storage >= sizeof(devname)) 37 + return log_msg_ret("end", -E2BIG); 38 + strlcpy(devname, storage, end - storage + 1); 39 + log_debug("dev=%s, %x\n", devname, devnum); 40 + 41 + desc = blk_get_dev(devname, devnum); 42 + if (!desc) 43 + return log_msg_ret("get", -ENXIO); 44 + *blkp = desc->bdev; 45 + 46 + return 0; 47 + } 48 + 49 + int vbe_read_version(struct udevice *blk, ulong offset, char *version, 50 + int max_size) 51 + { 52 + ALLOC_CACHE_ALIGN_BUFFER(u8, buf, MMC_MAX_BLOCK_LEN); 53 + 54 + /* we can use an assert() here since we already read only one block */ 55 + assert(max_size <= MMC_MAX_BLOCK_LEN); 56 + 57 + /* 58 + * we can use an assert() here since reading the wrong block will just 59 + * cause an invalid version-string to be (safely) read 60 + */ 61 + assert(!(offset & (MMC_MAX_BLOCK_LEN - 1))); 62 + 63 + offset /= MMC_MAX_BLOCK_LEN; 64 + 65 + if (blk_read(blk, offset, 1, buf) != 1) 66 + return log_msg_ret("read", -EIO); 67 + strlcpy(version, buf, max_size); 68 + log_debug("version=%s\n", version); 69 + 70 + return 0; 71 + } 72 + 73 + int vbe_read_nvdata(struct udevice *blk, ulong offset, ulong size, u8 *buf) 74 + { 75 + uint hdr_ver, hdr_size, data_size, crc; 76 + const struct vbe_nvdata *nvd; 77 + 78 + /* we can use an assert() here since we already read only one block */ 79 + assert(size <= MMC_MAX_BLOCK_LEN); 80 + 81 + /* 82 + * We can use an assert() here since reading the wrong block will just 83 + * cause invalid state to be (safely) read. If the crc passes, then we 84 + * obtain invalid state and it will likely cause booting to fail. 85 + * 86 + * VBE relies on valid values being in U-Boot's devicetree, so this 87 + * should not every be wrong on a production device. 88 + */ 89 + assert(!(offset & (MMC_MAX_BLOCK_LEN - 1))); 90 + 91 + if (offset & (MMC_MAX_BLOCK_LEN - 1)) 92 + return log_msg_ret("get", -EBADF); 93 + offset /= MMC_MAX_BLOCK_LEN; 94 + 95 + if (blk_read(blk, offset, 1, buf) != 1) 96 + return log_msg_ret("read", -EIO); 97 + nvd = (struct vbe_nvdata *)buf; 98 + hdr_ver = (nvd->hdr & NVD_HDR_VER_MASK) >> NVD_HDR_VER_SHIFT; 99 + hdr_size = (nvd->hdr & NVD_HDR_SIZE_MASK) >> NVD_HDR_SIZE_SHIFT; 100 + if (hdr_ver != NVD_HDR_VER_CUR) 101 + return log_msg_ret("hdr", -EPERM); 102 + data_size = 1 << hdr_size; 103 + if (!data_size || data_size > sizeof(*nvd)) 104 + return log_msg_ret("sz", -EPERM); 105 + 106 + crc = crc8(0, buf + 1, data_size - 1); 107 + if (crc != nvd->crc8) 108 + return log_msg_ret("crc", -EPERM); 109 + 110 + return 0; 111 + } 112 + 113 + /** 114 + * h_vbe_load_read() - Handler for reading an SPL image from a FIT 115 + * 116 + * See spl_load_reader for the definition 117 + */ 118 + ulong h_vbe_load_read(struct spl_load_info *load, ulong off, ulong size, 119 + void *buf) 120 + { 121 + struct blk_desc *desc = load->priv; 122 + lbaint_t sector = off >> desc->log2blksz; 123 + lbaint_t count = size >> desc->log2blksz; 124 + int ret; 125 + 126 + log_debug("vbe read log2blksz %x offset %lx sector %lx count %lx\n", 127 + desc->log2blksz, (ulong)off, (long)sector, (ulong)count); 128 + 129 + ret = blk_dread(desc, sector, count, buf); 130 + log_debug("ret=%x\n", ret); 131 + if (ret < 0) 132 + return ret; 133 + 134 + return ret << desc->log2blksz; 135 + } 136 + 137 + int vbe_read_fit(struct udevice *blk, ulong area_offset, ulong area_size, 138 + struct spl_image_info *image, ulong *load_addrp, ulong *lenp, 139 + char **namep) 140 + { 141 + ALLOC_CACHE_ALIGN_BUFFER(u8, sbuf, MMC_MAX_BLOCK_LEN); 142 + ulong size, blknum, addr, len, load_addr, num_blks, spl_load_addr; 143 + ulong aligned_size, fdt_load_addr, fdt_size; 144 + const char *fit_uname, *fit_uname_config; 145 + struct bootm_headers images = {}; 146 + enum image_phase_t phase; 147 + struct blk_desc *desc; 148 + int node, ret; 149 + bool for_xpl; 150 + void *buf; 151 + 152 + desc = dev_get_uclass_plat(blk); 153 + 154 + /* read in one block to find the FIT size */ 155 + blknum = area_offset / desc->blksz; 156 + log_debug("read at %lx, blknum %lx\n", area_offset, blknum); 157 + ret = blk_read(blk, blknum, 1, sbuf); 158 + if (ret < 0) 159 + return log_msg_ret("rd", ret); 160 + else if (ret != 1) 161 + return log_msg_ret("rd2", -EIO); 162 + 163 + ret = fdt_check_header(sbuf); 164 + if (ret < 0) 165 + return log_msg_ret("fdt", -EINVAL); 166 + size = fdt_totalsize(sbuf); 167 + if (size > area_size) 168 + return log_msg_ret("fdt", -E2BIG); 169 + log_debug("FIT size %lx\n", size); 170 + aligned_size = ALIGN(size, desc->blksz); 171 + 172 + /* 173 + * Load the FIT into the SPL memory. This is typically a FIT with 174 + * external data, so this is quite small, perhaps a few KB. 175 + */ 176 + if (IS_ENABLED(CONFIG_SANDBOX)) { 177 + addr = CONFIG_VAL(TEXT_BASE); 178 + buf = map_sysmem(addr, size); 179 + } else { 180 + buf = malloc(aligned_size); 181 + if (!buf) 182 + return log_msg_ret("fit", -ENOMEM); 183 + addr = map_to_sysmem(buf); 184 + } 185 + num_blks = aligned_size / desc->blksz; 186 + log_debug("read %lx, %lx blocks to %lx / %p\n", aligned_size, num_blks, 187 + addr, buf); 188 + ret = blk_read(blk, blknum, num_blks, buf); 189 + if (ret < 0) 190 + return log_msg_ret("rd3", ret); 191 + else if (ret != num_blks) 192 + return log_msg_ret("rd4", -EIO); 193 + log_debug("check total size %x off_dt_strings %x\n", fdt_totalsize(buf), 194 + fdt_off_dt_strings(buf)); 195 + 196 + #if CONFIG_IS_ENABLED(SYS_MALLOC_F) 197 + log_debug("malloc base %lx ptr %x limit %x top %lx\n", 198 + gd->malloc_base, gd->malloc_ptr, gd->malloc_limit, 199 + gd->malloc_base + gd->malloc_limit); 200 + #endif 201 + /* figure out the phase to load */ 202 + phase = IS_ENABLED(CONFIG_TPL_BUILD) ? IH_PHASE_NONE : 203 + IS_ENABLED(CONFIG_VPL_BUILD) ? IH_PHASE_SPL : IH_PHASE_U_BOOT; 204 + 205 + /* 206 + * Load the image from the FIT. We ignore any load-address information 207 + * so in practice this simply locates the image in the external-data 208 + * region and returns its address and size. Since we only loaded the FIT 209 + * itself, only a part of the image will be present, at best. 210 + */ 211 + fit_uname = NULL; 212 + fit_uname_config = NULL; 213 + log_debug("loading FIT\n"); 214 + 215 + if (xpl_phase() == PHASE_SPL && !IS_ENABLED(CONFIG_SANDBOX)) { 216 + struct spl_load_info info; 217 + 218 + spl_load_init(&info, h_vbe_load_read, desc, desc->blksz); 219 + xpl_set_phase(&info, IH_PHASE_U_BOOT); 220 + log_debug("doing SPL from %s blksz %lx log2blksz %x area_offset %lx + fdt_size %lx\n", 221 + blk->name, desc->blksz, desc->log2blksz, area_offset, ALIGN(size, 4)); 222 + ret = spl_load_simple_fit(image, &info, area_offset, buf); 223 + log_debug("spl_load_abrec_fit() ret=%d\n", ret); 224 + 225 + return ret; 226 + } 227 + 228 + ret = fit_image_load(&images, addr, &fit_uname, &fit_uname_config, 229 + IH_ARCH_DEFAULT, image_ph(phase, IH_TYPE_FIRMWARE), 230 + BOOTSTAGE_ID_FIT_SPL_START, FIT_LOAD_IGNORED, 231 + &load_addr, &len); 232 + if (ret == -ENOENT) { 233 + ret = fit_image_load(&images, addr, &fit_uname, 234 + &fit_uname_config, IH_ARCH_DEFAULT, 235 + image_ph(phase, IH_TYPE_LOADABLE), 236 + BOOTSTAGE_ID_FIT_SPL_START, 237 + FIT_LOAD_IGNORED, &load_addr, &len); 238 + } 239 + if (ret < 0) 240 + return log_msg_ret("ld", ret); 241 + node = ret; 242 + log_debug("load %lx size %lx\n", load_addr, len); 243 + 244 + fdt_load_addr = 0; 245 + fdt_size = 0; 246 + if ((xpl_phase() == PHASE_TPL || xpl_phase() == PHASE_VPL) && 247 + !IS_ENABLED(CONFIG_SANDBOX)) { 248 + /* allow use of a different image from the configuration node */ 249 + fit_uname = NULL; 250 + ret = fit_image_load(&images, addr, &fit_uname, 251 + &fit_uname_config, IH_ARCH_DEFAULT, 252 + image_ph(phase, IH_TYPE_FLATDT), 253 + BOOTSTAGE_ID_FIT_SPL_START, 254 + FIT_LOAD_IGNORED, &fdt_load_addr, 255 + &fdt_size); 256 + fdt_size = ALIGN(fdt_size, desc->blksz); 257 + log_debug("FDT noload to %lx size %lx\n", fdt_load_addr, 258 + fdt_size); 259 + } 260 + 261 + for_xpl = !USE_BOOTMETH && CONFIG_IS_ENABLED(RELOC_LOADER); 262 + if (for_xpl) { 263 + image->size = len; 264 + image->fdt_size = fdt_size; 265 + ret = spl_reloc_prepare(image, &spl_load_addr); 266 + if (ret) 267 + return log_msg_ret("spl", ret); 268 + } 269 + if (!IS_ENABLED(CONFIG_SANDBOX)) 270 + image->os = IH_OS_U_BOOT; 271 + 272 + /* For FIT external data, read in the external data */ 273 + log_debug("load_addr %lx len %lx addr %lx aligned_size %lx\n", 274 + load_addr, len, addr, aligned_size); 275 + if (load_addr + len > addr + aligned_size) { 276 + ulong base, full_size, offset, extra, fdt_base, fdt_full_size; 277 + ulong fdt_offset; 278 + void *base_buf, *fdt_base_buf; 279 + 280 + /* Find the start address to load from */ 281 + base = ALIGN_DOWN(load_addr, desc->blksz); 282 + 283 + offset = area_offset + load_addr - addr; 284 + blknum = offset / desc->blksz; 285 + extra = offset % desc->blksz; 286 + 287 + /* 288 + * Get the total number of bytes to load, taking care of 289 + * block alignment 290 + */ 291 + full_size = len + extra; 292 + 293 + /* 294 + * Get the start block number, number of blocks and the address 295 + * to load to, then load the blocks 296 + */ 297 + num_blks = DIV_ROUND_UP(full_size, desc->blksz); 298 + if (for_xpl) 299 + base = spl_load_addr; 300 + base_buf = map_sysmem(base, full_size); 301 + ret = blk_read(blk, blknum, num_blks, base_buf); 302 + log_debug("read foffset %lx blknum %lx full_size %lx num_blks %lx to %lx / %p: ret=%d\n", 303 + offset - 0x8000, blknum, full_size, num_blks, base, base_buf, 304 + ret); 305 + if (ret < 0) 306 + return log_msg_ret("rd", ret); 307 + if (ret != num_blks) 308 + return log_msg_ret("rd", -EIO); 309 + if (extra && !IS_ENABLED(CONFIG_SANDBOX)) { 310 + log_debug("move %p %p %lx\n", base_buf, 311 + base_buf + extra, len); 312 + memmove(base_buf, base_buf + extra, len); 313 + } 314 + 315 + if ((xpl_phase() == PHASE_VPL || xpl_phase() == PHASE_TPL) && 316 + !IS_ENABLED(CONFIG_SANDBOX)) { 317 + image->load_addr = spl_get_image_text_base(); 318 + image->entry_point = image->load_addr; 319 + } 320 + 321 + /* now the FDT */ 322 + if (fdt_size) { 323 + fdt_offset = area_offset + fdt_load_addr - addr; 324 + blknum = fdt_offset / desc->blksz; 325 + extra = fdt_offset % desc->blksz; 326 + fdt_full_size = fdt_size + extra; 327 + num_blks = DIV_ROUND_UP(fdt_full_size, desc->blksz); 328 + fdt_base = ALIGN(base + len, 4); 329 + fdt_base_buf = map_sysmem(fdt_base, fdt_size); 330 + ret = blk_read(blk, blknum, num_blks, fdt_base_buf); 331 + log_debug("fdt read foffset %lx blknum %lx full_size %lx num_blks %lx to %lx / %p: ret=%d\n", 332 + fdt_offset - 0x8000, blknum, fdt_full_size, num_blks, 333 + fdt_base, fdt_base_buf, ret); 334 + if (ret != num_blks) 335 + return log_msg_ret("rdf", -EIO); 336 + if (extra) { 337 + log_debug("move %p %p %lx\n", fdt_base_buf, 338 + fdt_base_buf + extra, fdt_size); 339 + memmove(fdt_base_buf, fdt_base_buf + extra, 340 + fdt_size); 341 + } 342 + #if CONFIG_IS_ENABLED(RELOC_LOADER) 343 + image->fdt_buf = fdt_base_buf; 344 + 345 + ulong xpl_size; 346 + ulong xpl_pad; 347 + ulong fdt_start; 348 + 349 + if (xpl_phase() == PHASE_TPL) { 350 + xpl_size = binman_sym(ulong, u_boot_vpl_nodtb, size); 351 + xpl_pad = binman_sym(ulong, u_boot_vpl_bss_pad, size); 352 + } else { 353 + xpl_size = binman_sym(ulong, u_boot_spl_nodtb, size); 354 + xpl_pad = binman_sym(ulong, u_boot_spl_bss_pad, size); 355 + } 356 + fdt_start = image->load_addr + xpl_size + xpl_pad; 357 + log_debug("load_addr %lx xpl_size %lx copy-to %lx\n", 358 + image->load_addr, xpl_size + xpl_pad, 359 + fdt_start); 360 + image->fdt_start = map_sysmem(fdt_start, fdt_size); 361 + #endif 362 + } 363 + } 364 + if (load_addrp) 365 + *load_addrp = load_addr; 366 + if (lenp) 367 + *lenp = len; 368 + if (namep) { 369 + *namep = strdup(fdt_get_name(buf, node, NULL)); 370 + if (!namep) 371 + return log_msg_ret("nam", -ENOMEM); 372 + } 373 + 374 + return 0; 375 + }
+137
boot/vbe_common.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Verified Boot for Embedded (VBE) common functions 4 + * 5 + * Copyright 2024 Google LLC 6 + * Written by Simon Glass <sjg@chromium.org> 7 + */ 8 + 9 + #ifndef __VBE_COMMON_H 10 + #define __VBE_COMMON_H 11 + 12 + #include <linux/types.h> 13 + 14 + struct spl_image_info; 15 + struct udevice; 16 + 17 + /* 18 + * Controls whether we use a full bootmeth driver with VBE in this phase, or 19 + * just access the information directly. 20 + * 21 + * For now VBE-simple uses the full bootmeth, but VBE-abrec does not, to reduce 22 + * code size 23 + */ 24 + #define USE_BOOTMETH CONFIG_IS_ENABLED(BOOTMETH_VBE_SIMPLE) 25 + 26 + enum { 27 + MAX_VERSION_LEN = 256, 28 + 29 + NVD_HDR_VER_SHIFT = 0, 30 + NVD_HDR_VER_MASK = 0xf, 31 + NVD_HDR_SIZE_SHIFT = 4, 32 + NVD_HDR_SIZE_MASK = 0xf << NVD_HDR_SIZE_SHIFT, 33 + 34 + /* Firmware key-version is in the top 16 bits of fw_ver */ 35 + FWVER_KEY_SHIFT = 16, 36 + FWVER_FW_MASK = 0xffff, 37 + 38 + NVD_HDR_VER_CUR = 1, /* current version */ 39 + }; 40 + 41 + /** 42 + * struct vbe_nvdata - basic storage format for non-volatile data 43 + * 44 + * This is used for all VBE methods 45 + * 46 + * @crc8: crc8 for the entire record except @crc8 field itself 47 + * @hdr: header size and version (NVD_HDR_...) 48 + * @spare1: unused, must be 0 49 + * @fw_vernum: version and key version (FWVER_...) 50 + * @flags: Flags controlling operation (enum vbe_flags) 51 + */ 52 + struct vbe_nvdata { 53 + u8 crc8; 54 + u8 hdr; 55 + u16 spare1; 56 + u32 fw_vernum; 57 + u32 flags; 58 + u8 spare2[0x34]; 59 + }; 60 + 61 + /** 62 + * vbe_get_blk() - Obtain the block device to use for VBE 63 + * 64 + * Decodes the string to produce a block device 65 + * 66 + * @storage: String indicating the device to use, e.g. "mmc1" 67 + * @blkp: Returns associated block device, on success 68 + * Return 0 if OK, -ENODEV if @storage does not end with a number, -E2BIG if 69 + * the device name is more than 15 characters, -ENXIO if the block device could 70 + * not be found 71 + */ 72 + int vbe_get_blk(const char *storage, struct udevice **blkp); 73 + 74 + /** 75 + * vbe_read_version() - Read version-string from a block device 76 + * 77 + * Reads the VBE version-string from a device. This function reads a single 78 + * block from the device, so the string cannot be larger than that. It uses a 79 + * temporary buffer for the read, then copies in up to @size bytes 80 + * 81 + * @blk: Device to read from 82 + * @offset: Offset to read, in bytes 83 + * @version: Place to put the string 84 + * @max_size: Maximum size of @version 85 + * Return: 0 if OK, -E2BIG if @max_size > block size, -EBADF if the offset is 86 + * not block-aligned, -EIO if an I/O error occurred 87 + */ 88 + int vbe_read_version(struct udevice *blk, ulong offset, char *version, 89 + int max_size); 90 + 91 + /** 92 + * vbe_read_nvdata() - Read non-volatile data from a block device 93 + * 94 + * Reads the VBE nvdata from a device. This function reads a single block from 95 + * the device, so the nvdata cannot be larger than that. 96 + * 97 + * @blk: Device to read from 98 + * @offset: Offset to read, in bytes 99 + * @size: Number of bytes to read 100 + * @buf: Buffer to hold the data 101 + * Return: 0 if OK, -E2BIG if @size > block size, -EBADF if the offset is not 102 + * block-aligned, -EIO if an I/O error occurred, -EPERM if the header version is 103 + * incorrect, the header size is invalid or the data fails its CRC check 104 + */ 105 + int vbe_read_nvdata(struct udevice *blk, ulong offset, ulong size, u8 *buf); 106 + 107 + /** 108 + * vbe_read_fit() - Read an image from a FIT 109 + * 110 + * This handles most of the VBE logic for reading from a FIT. It reads the FIT 111 + * metadata, decides which image to load and loads it to a suitable address, 112 + * ready for jumping to the next phase of VBE. 113 + * 114 + * This supports transition from VPL to SPL as well as SPL to U-Boot proper. For 115 + * now, TPL->VPL is not supported. 116 + * 117 + * Both embedded and external data are supported for the FIT 118 + * 119 + * @blk: Block device containing FIT 120 + * @area_offset: Byte offset of the VBE area in @blk containing the FIT 121 + * @area_size: Size of the VBE area 122 + * @image: SPL image to fill in with details of the loaded image, or NULL 123 + * @load_addrp: If non-null, returns the address where the image was loaded 124 + * @lenp: If non-null, returns the size of the image loaded, in bytes 125 + * @namep: If non-null, returns the name of the FIT-image node that was loaded 126 + * (allocated by this function) 127 + * Return: 0 if OK, -EINVAL if the area does not contain an FDT (the underlying 128 + * format for FIT), -E2BIG if the FIT extends past @area_size, -ENOMEM if there 129 + * was not space to allocate the image-node name, other error if a read error 130 + * occurred (see blk_read()), or something went wrong with the actually 131 + * FIT-parsing (see fit_image_load()). 132 + */ 133 + int vbe_read_fit(struct udevice *blk, ulong area_offset, ulong area_size, 134 + struct spl_image_info *image, ulong *load_addrp, ulong *lenp, 135 + char **namep); 136 + 137 + #endif /* __VBE_ABREC_H */
+17 -79
boot/vbe_simple.c
··· 18 18 #include <vbe.h> 19 19 #include <dm/device-internal.h> 20 20 #include <dm/ofnode.h> 21 - #include <u-boot/crc.h> 22 21 #include "vbe_simple.h" 23 22 24 - /** struct simple_nvdata - storage format for non-volatile data */ 25 - struct simple_nvdata { 26 - u8 crc8; 27 - u8 hdr; 28 - u16 spare1; 29 - u32 fw_vernum; 30 - u8 spare2[0x38]; 31 - }; 32 - 33 - static int simple_read_version(struct udevice *dev, struct blk_desc *desc, 34 - u8 *buf, struct simple_state *state) 35 - { 36 - struct simple_priv *priv = dev_get_priv(dev); 37 - int start; 38 - 39 - if (priv->version_size > MMC_MAX_BLOCK_LEN) 40 - return log_msg_ret("ver", -E2BIG); 41 - 42 - start = priv->area_start + priv->version_offset; 43 - if (start & (MMC_MAX_BLOCK_LEN - 1)) 44 - return log_msg_ret("get", -EBADF); 45 - start /= MMC_MAX_BLOCK_LEN; 46 - 47 - if (blk_dread(desc, start, 1, buf) != 1) 48 - return log_msg_ret("read", -EIO); 49 - strlcpy(state->fw_version, buf, MAX_VERSION_LEN); 50 - log_debug("version=%s\n", state->fw_version); 51 - 52 - return 0; 53 - } 54 - 55 - static int simple_read_nvdata(struct udevice *dev, struct blk_desc *desc, 56 - u8 *buf, struct simple_state *state) 23 + static int simple_read_nvdata(const struct simple_priv *priv, 24 + struct udevice *blk, struct simple_state *state) 57 25 { 58 - struct simple_priv *priv = dev_get_priv(dev); 59 - uint hdr_ver, hdr_size, size, crc; 60 - const struct simple_nvdata *nvd; 61 - int start; 62 - 63 - if (priv->state_size > MMC_MAX_BLOCK_LEN) 64 - return log_msg_ret("state", -E2BIG); 65 - 66 - start = priv->area_start + priv->state_offset; 67 - if (start & (MMC_MAX_BLOCK_LEN - 1)) 68 - return log_msg_ret("get", -EBADF); 69 - start /= MMC_MAX_BLOCK_LEN; 26 + ALLOC_CACHE_ALIGN_BUFFER(u8, buf, MMC_MAX_BLOCK_LEN); 27 + const struct vbe_nvdata *nvd; 28 + int ret; 70 29 71 - if (blk_dread(desc, start, 1, buf) != 1) 72 - return log_msg_ret("read", -EIO); 73 - nvd = (struct simple_nvdata *)buf; 74 - hdr_ver = (nvd->hdr & NVD_HDR_VER_MASK) >> NVD_HDR_VER_SHIFT; 75 - hdr_size = (nvd->hdr & NVD_HDR_SIZE_MASK) >> NVD_HDR_SIZE_SHIFT; 76 - if (hdr_ver != NVD_HDR_VER_CUR) 77 - return log_msg_ret("hdr", -EPERM); 78 - size = 1 << hdr_size; 79 - if (size > sizeof(*nvd)) 80 - return log_msg_ret("sz", -ENOEXEC); 30 + ret = vbe_read_nvdata(blk, priv->area_start + priv->state_offset, 31 + priv->state_size, buf); 32 + if (ret) 33 + return log_msg_ret("nv", ret); 81 34 82 - crc = crc8(0, buf + 1, size - 1); 83 - if (crc != nvd->crc8) 84 - return log_msg_ret("crc", -EPERM); 35 + nvd = (struct vbe_nvdata *)buf; 85 36 state->fw_vernum = nvd->fw_vernum; 86 37 87 38 log_debug("version=%s\n", state->fw_version); ··· 91 42 92 43 int vbe_simple_read_state(struct udevice *dev, struct simple_state *state) 93 44 { 94 - ALLOC_CACHE_ALIGN_BUFFER(u8, buf, MMC_MAX_BLOCK_LEN); 95 45 struct simple_priv *priv = dev_get_priv(dev); 96 - struct blk_desc *desc; 97 - char devname[16]; 98 - const char *end; 99 - int devnum; 46 + struct udevice *blk; 100 47 int ret; 101 48 102 - /* First figure out the block device */ 103 - log_debug("storage=%s\n", priv->storage); 104 - devnum = trailing_strtoln_end(priv->storage, NULL, &end); 105 - if (devnum == -1) 106 - return log_msg_ret("num", -ENODEV); 107 - if (end - priv->storage >= sizeof(devname)) 108 - return log_msg_ret("end", -E2BIG); 109 - strlcpy(devname, priv->storage, end - priv->storage + 1); 110 - log_debug("dev=%s, %x\n", devname, devnum); 111 - 112 - desc = blk_get_dev(devname, devnum); 113 - if (!desc) 114 - return log_msg_ret("get", -ENXIO); 49 + ret = vbe_get_blk(priv->storage, &blk); 50 + if (ret) 51 + return log_msg_ret("blk", ret); 115 52 116 - ret = simple_read_version(dev, desc, buf, state); 53 + ret = vbe_read_version(blk, priv->area_start + priv->version_offset, 54 + state->fw_version, MAX_VERSION_LEN); 117 55 if (ret) 118 56 return log_msg_ret("ver", ret); 119 57 120 - ret = simple_read_nvdata(dev, desc, buf, state); 58 + ret = simple_read_nvdata(priv, blk, state); 121 59 if (ret) 122 60 return log_msg_ret("nvd", ret); 123 61
+2 -14
boot/vbe_simple.h
··· 9 9 #ifndef __VBE_SIMPLE_H 10 10 #define __VBE_SIMPLE_H 11 11 12 - enum { 13 - MAX_VERSION_LEN = 256, 14 - 15 - NVD_HDR_VER_SHIFT = 0, 16 - NVD_HDR_VER_MASK = 0xf, 17 - NVD_HDR_SIZE_SHIFT = 4, 18 - NVD_HDR_SIZE_MASK = 0xf << NVD_HDR_SIZE_SHIFT, 19 - 20 - /* Firmware key-version is in the top 16 bits of fw_ver */ 21 - FWVER_KEY_SHIFT = 16, 22 - FWVER_FW_MASK = 0xffff, 23 - 24 - NVD_HDR_VER_CUR = 1, /* current version */ 25 - }; 12 + #include <linux/types.h> 13 + #include "vbe_common.h" 26 14 27 15 /** struct simple_priv - information read from the device tree */ 28 16 struct simple_priv {
+77 -122
boot/vbe_simple_fw.c
··· 8 8 9 9 #define LOG_CATEGORY LOGC_BOOT 10 10 11 + #include <binman_sym.h> 11 12 #include <bloblist.h> 12 13 #include <bootdev.h> 13 14 #include <bootflow.h> ··· 17 18 #include <image.h> 18 19 #include <log.h> 19 20 #include <mapmem.h> 20 - #include <memalign.h> 21 21 #include <mmc.h> 22 22 #include <spl.h> 23 23 #include <vbe.h> 24 24 #include <dm/device-internal.h> 25 + #include "vbe_common.h" 25 26 #include "vbe_simple.h" 26 27 28 + #ifdef CONFIG_BOOTMETH_VBE_SIMPLE 29 + binman_sym_extern(ulong, vbe_a, image_pos); 30 + binman_sym_extern(ulong, vbe_a, size); 31 + #else 32 + binman_sym_declare(ulong, vbe_a, image_pos); 33 + binman_sym_declare(ulong, vbe_a, size); 34 + #endif 35 + 36 + binman_sym_declare(ulong, vpl, image_pos); 37 + binman_sym_declare(ulong, vpl, size); 38 + 27 39 /** 28 40 * vbe_simple_read_bootflow_fw() - Create a bootflow for firmware 29 41 * ··· 38 50 */ 39 51 int vbe_simple_read_bootflow_fw(struct udevice *dev, struct bootflow *bflow) 40 52 { 41 - ALLOC_CACHE_ALIGN_BUFFER(u8, sbuf, MMC_MAX_BLOCK_LEN); 42 53 struct udevice *media = dev_get_parent(bflow->dev); 43 54 struct udevice *meth = bflow->method; 44 55 struct simple_priv *priv = dev_get_priv(meth); 45 - const char *fit_uname, *fit_uname_config; 46 - struct bootm_headers images = {}; 47 - ulong offset, size, blknum, addr, len, load_addr, num_blks; 48 - enum image_phase_t phase; 49 - struct blk_desc *desc; 56 + ulong len, load_addr; 50 57 struct udevice *blk; 51 - int node, ret; 52 - void *buf; 58 + int ret; 53 59 54 60 log_debug("media=%s\n", media->name); 55 61 ret = blk_get_from_parent(media, &blk); 56 62 if (ret) 57 63 return log_msg_ret("med", ret); 58 64 log_debug("blk=%s\n", blk->name); 59 - desc = dev_get_uclass_plat(blk); 60 65 61 - offset = priv->area_start + priv->skip_offset; 62 - 63 - /* read in one block to find the FIT size */ 64 - blknum = offset / desc->blksz; 65 - log_debug("read at %lx, blknum %lx\n", offset, blknum); 66 - ret = blk_read(blk, blknum, 1, sbuf); 67 - if (ret < 0) 68 - return log_msg_ret("rd", ret); 69 - 70 - ret = fdt_check_header(sbuf); 71 - if (ret < 0) 72 - return log_msg_ret("fdt", -EINVAL); 73 - size = fdt_totalsize(sbuf); 74 - if (size > priv->area_size) 75 - return log_msg_ret("fdt", -E2BIG); 76 - log_debug("FIT size %lx\n", size); 77 - 78 - /* 79 - * Load the FIT into the SPL memory. This is typically a FIT with 80 - * external data, so this is quite small, perhaps a few KB. 81 - */ 82 - addr = CONFIG_VAL(TEXT_BASE); 83 - buf = map_sysmem(addr, size); 84 - num_blks = DIV_ROUND_UP(size, desc->blksz); 85 - log_debug("read %lx, %lx blocks to %lx / %p\n", size, num_blks, addr, 86 - buf); 87 - ret = blk_read(blk, blknum, num_blks, buf); 88 - if (ret < 0) 89 - return log_msg_ret("rd", ret); 90 - 91 - /* figure out the phase to load */ 92 - phase = IS_ENABLED(CONFIG_VPL_BUILD) ? IH_PHASE_SPL : IH_PHASE_U_BOOT; 93 - 94 - /* 95 - * Load the image from the FIT. We ignore any load-address information 96 - * so in practice this simply locates the image in the external-data 97 - * region and returns its address and size. Since we only loaded the FIT 98 - * itself, only a part of the image will be present, at best. 99 - */ 100 - fit_uname = NULL; 101 - fit_uname_config = NULL; 102 - log_debug("loading FIT\n"); 103 - ret = fit_image_load(&images, addr, &fit_uname, &fit_uname_config, 104 - IH_ARCH_SANDBOX, image_ph(phase, IH_TYPE_FIRMWARE), 105 - BOOTSTAGE_ID_FIT_SPL_START, FIT_LOAD_IGNORED, 106 - &load_addr, &len); 107 - if (ret < 0) 108 - return log_msg_ret("ld", ret); 109 - node = ret; 110 - log_debug("loaded to %lx\n", load_addr); 111 - 112 - /* For FIT external data, read in the external data */ 113 - if (load_addr + len > addr + size) { 114 - ulong base, full_size; 115 - void *base_buf; 116 - 117 - /* Find the start address to load from */ 118 - base = ALIGN_DOWN(load_addr, desc->blksz); 119 - 120 - /* 121 - * Get the total number of bytes to load, taking care of 122 - * block alignment 123 - */ 124 - full_size = load_addr + len - base; 125 - 126 - /* 127 - * Get the start block number, number of blocks and the address 128 - * to load to, then load the blocks 129 - */ 130 - blknum = (offset + base - addr) / desc->blksz; 131 - num_blks = DIV_ROUND_UP(full_size, desc->blksz); 132 - base_buf = map_sysmem(base, full_size); 133 - ret = blk_read(blk, blknum, num_blks, base_buf); 134 - log_debug("read %lx %lx, %lx blocks to %lx / %p: ret=%d\n", 135 - blknum, full_size, num_blks, base, base_buf, ret); 136 - if (ret < 0) 137 - return log_msg_ret("rd", ret); 138 - } 66 + ret = vbe_read_fit(blk, priv->area_start + priv->skip_offset, 67 + priv->area_size, NULL, &load_addr, &len, 68 + &bflow->name); 69 + if (ret) 70 + return log_msg_ret("vbe", ret); 139 71 140 72 /* set up the bootflow with the info we obtained */ 141 - bflow->name = strdup(fdt_get_name(buf, node, NULL)); 142 - if (!bflow->name) 143 - return log_msg_ret("name", -ENOMEM); 144 73 bflow->blk = blk; 145 74 bflow->buf = map_sysmem(load_addr, len); 146 75 bflow->size = len; ··· 148 77 return 0; 149 78 } 150 79 151 - static int simple_load_from_image(struct spl_image_info *spl_image, 80 + static int simple_load_from_image(struct spl_image_info *image, 152 81 struct spl_boot_device *bootdev) 153 82 { 154 - struct udevice *meth, *bdev; 155 - struct simple_priv *priv; 156 - struct bootflow bflow; 157 83 struct vbe_handoff *handoff; 158 84 int ret; 159 85 160 - if (xpl_phase() != PHASE_VPL && xpl_phase() != PHASE_SPL) 86 + if (xpl_phase() != PHASE_VPL && xpl_phase() != PHASE_SPL && 87 + xpl_phase() != PHASE_TPL) 161 88 return -ENOENT; 162 89 163 90 ret = bloblist_ensure_size(BLOBLISTT_VBE, sizeof(struct vbe_handoff), ··· 165 92 if (ret) 166 93 return log_msg_ret("ro", ret); 167 94 168 - vbe_find_first_device(&meth); 169 - if (!meth) 170 - return log_msg_ret("vd", -ENODEV); 171 - log_debug("vbe dev %s\n", meth->name); 172 - ret = device_probe(meth); 173 - if (ret) 174 - return log_msg_ret("probe", ret); 95 + if (USE_BOOTMETH) { 96 + struct udevice *meth, *bdev; 97 + struct simple_priv *priv; 98 + struct bootflow bflow; 99 + 100 + vbe_find_first_device(&meth); 101 + if (!meth) 102 + return log_msg_ret("vd", -ENODEV); 103 + log_debug("vbe dev %s\n", meth->name); 104 + ret = device_probe(meth); 105 + if (ret) 106 + return log_msg_ret("probe", ret); 107 + 108 + priv = dev_get_priv(meth); 109 + log_debug("simple %s\n", priv->storage); 110 + ret = bootdev_find_by_label(priv->storage, &bdev, NULL); 111 + if (ret) 112 + return log_msg_ret("bd", ret); 113 + log_debug("bootdev %s\n", bdev->name); 114 + 115 + bootflow_init(&bflow, bdev, meth); 116 + ret = bootmeth_read_bootflow(meth, &bflow); 117 + log_debug("\nfw ret=%d\n", ret); 118 + if (ret) 119 + return log_msg_ret("rd", ret); 175 120 176 - priv = dev_get_priv(meth); 177 - log_debug("simple %s\n", priv->storage); 178 - ret = bootdev_find_by_label(priv->storage, &bdev, NULL); 179 - if (ret) 180 - return log_msg_ret("bd", ret); 181 - log_debug("bootdev %s\n", bdev->name); 121 + /* jump to the image */ 122 + image->flags = SPL_SANDBOXF_ARG_IS_BUF; 123 + image->arg = bflow.buf; 124 + image->size = bflow.size; 125 + log_debug("Image: %s at %p size %x\n", bflow.name, bflow.buf, 126 + bflow.size); 182 127 183 - bootflow_init(&bflow, bdev, meth); 184 - ret = bootmeth_read_bootflow(meth, &bflow); 185 - log_debug("\nfw ret=%d\n", ret); 186 - if (ret) 187 - return log_msg_ret("rd", ret); 128 + /* this is not used from now on, so free it */ 129 + bootflow_free(&bflow); 130 + } else { 131 + struct udevice *media, *blk; 132 + ulong offset, size; 188 133 189 - /* jump to the image */ 190 - spl_image->flags = SPL_SANDBOXF_ARG_IS_BUF; 191 - spl_image->arg = bflow.buf; 192 - spl_image->size = bflow.size; 193 - log_debug("Image: %s at %p size %x\n", bflow.name, bflow.buf, 194 - bflow.size); 134 + ret = uclass_get_device_by_seq(UCLASS_MMC, 1, &media); 135 + if (ret) 136 + return log_msg_ret("vdv", ret); 137 + ret = blk_get_from_parent(media, &blk); 138 + if (ret) 139 + return log_msg_ret("med", ret); 140 + if (xpl_phase() == PHASE_TPL) { 141 + offset = binman_sym(ulong, vpl, image_pos); 142 + size = binman_sym(ulong, vpl, size); 143 + } else { 144 + offset = binman_sym(ulong, vbe_a, image_pos); 145 + size = binman_sym(ulong, vbe_a, size); 146 + printf("offset=%lx\n", offset); 147 + } 195 148 196 - /* this is not used from now on, so free it */ 197 - bootflow_free(&bflow); 149 + ret = vbe_read_fit(blk, offset, size, image, NULL, NULL, NULL); 150 + if (ret) 151 + return log_msg_ret("vbe", ret); 152 + } 198 153 199 154 /* Record that VBE was used in this phase */ 200 155 handoff->phases |= 1 << xpl_phase();
+8
common/spl/Kconfig
··· 983 983 help 984 984 SPL uses the chip ID list to identify the NAND flash. 985 985 986 + config SPL_RELOC_LOADER 987 + bool "Allow relocating the next phase" 988 + help 989 + In some cases multiple U-Boot phases need to run in SRAM, typically 990 + at the same address. Enable this to support loading the next phase 991 + to temporary memory, then copying it into place afterwards, then 992 + jumping to it. 993 + 986 994 config SPL_UBI 987 995 bool "Support UBI" 988 996 help
+8
common/spl/Kconfig.tpl
··· 268 268 be already in memory when TPL takes over, e.g. loaded by the boot 269 269 ROM. 270 270 271 + config TPL_RELOC_LOADER 272 + bool "Allow relocating the next phase" 273 + help 274 + In some cases multiple U-Boot phases need to run in SRAM, typically 275 + at the same address. Enable this to support loading the next phase 276 + to temporary memory, then copying it into place afterwards, then 277 + jumping to it. 278 + 271 279 config TPL_RTC 272 280 bool "Support RTC drivers" 273 281 help
+8
common/spl/Kconfig.vpl
··· 181 181 necessary driver support. This enables the drivers in drivers/pci 182 182 as part of a VPL build. 183 183 184 + config VPL_RELOC_LOADER 185 + bool "Allow relocating the next phase" 186 + help 187 + In some cases multiple U-Boot phases need to run in SRAM, typically 188 + at the same address. Enable this to support loading the next phase 189 + to temporary memory, then copying it into place afterwards, then 190 + jumping to it. 191 + 184 192 config VPL_RTC 185 193 bool "Support RTC drivers" 186 194 help
+1
common/spl/Makefile
··· 12 12 obj-$(CONFIG_$(PHASE_)LOAD_FIT) += spl_fit.o 13 13 obj-$(CONFIG_$(PHASE_)BLK_FS) += spl_blk_fs.o 14 14 obj-$(CONFIG_$(PHASE_)LEGACY_IMAGE_FORMAT) += spl_legacy.o 15 + obj-$(CONFIG_$(PHASE_)RELOC_LOADER) += spl_reloc.o 15 16 obj-$(CONFIG_$(PHASE_)NOR_SUPPORT) += spl_nor.o 16 17 obj-$(CONFIG_$(PHASE_)XIP_SUPPORT) += spl_xip.o 17 18 obj-$(CONFIG_$(PHASE_)YMODEM_SUPPORT) += spl_ymodem.o
+13 -2
common/spl/spl.c
··· 675 675 BOOT_DEVICE_NONE, 676 676 BOOT_DEVICE_NONE, 677 677 }; 678 - typedef void __noreturn (*jump_to_image_t)(struct spl_image_info *); 679 - jump_to_image_t jump_to_image = &jump_to_image_no_args; 678 + spl_jump_to_image_t jump_to_image = &jump_to_image_no_args; 680 679 struct spl_image_info spl_image; 681 680 int ret, os; 682 681 ··· 831 830 } 832 831 833 832 spl_board_prepare_for_boot(); 833 + 834 + if (CONFIG_IS_ENABLED(RELOC_LOADER)) { 835 + int ret; 836 + 837 + ret = spl_reloc_jump(&spl_image, jump_to_image); 838 + if (ret) { 839 + if (xpl_phase() == PHASE_VPL) 840 + printf("jump failed %d\n", ret); 841 + hang(); 842 + } 843 + } 844 + 834 845 jump_to_image(&spl_image); 835 846 } 836 847
+183
common/spl/spl_reloc.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * Copyright 2024 Google LLC 4 + * Written by Simon Glass <sjg@chromium.org> 5 + */ 6 + 7 + #include <gzip.h> 8 + #include <image.h> 9 + #include <log.h> 10 + #include <mapmem.h> 11 + #include <spl.h> 12 + #include <asm/global_data.h> 13 + #include <asm/io.h> 14 + #include <asm/sections.h> 15 + #include <asm/unaligned.h> 16 + #include <linux/types.h> 17 + #include <lzma/LzmaTypes.h> 18 + #include <lzma/LzmaDec.h> 19 + #include <lzma/LzmaTools.h> 20 + #include <u-boot/crc.h> 21 + #include <u-boot/lz4.h> 22 + 23 + DECLARE_GLOBAL_DATA_PTR; 24 + 25 + /* provide a way to jump straight into the relocation code, for debugging */ 26 + #define DEBUG_JUMP 0 27 + 28 + enum { 29 + /* margin to allow for stack growth */ 30 + RELOC_STACK_MARGIN = 0x800, 31 + 32 + /* align base address for DMA controllers which require it */ 33 + BASE_ALIGN = 0x200, 34 + 35 + STACK_PROT_VALUE = 0x51ce4697, 36 + }; 37 + 38 + typedef int (*rcode_func)(struct spl_image_info *image); 39 + 40 + static int setup_layout(struct spl_image_info *image, ulong *addrp) 41 + { 42 + ulong base, fdt_size; 43 + ulong limit, rcode_base; 44 + uint rcode_size; 45 + int buf_size, margin; 46 + char *rcode_buf; 47 + 48 + limit = ALIGN(map_to_sysmem(&limit) - RELOC_STACK_MARGIN, 8); 49 + image->stack_prot = map_sysmem(limit, sizeof(uint)); 50 + *image->stack_prot = STACK_PROT_VALUE; 51 + 52 + fdt_size = fdt_totalsize(gd->fdt_blob); 53 + base = ALIGN(map_to_sysmem(gd->fdt_blob) + fdt_size + BASE_ALIGN - 1, 54 + BASE_ALIGN); 55 + 56 + rcode_size = _rcode_end - _rcode_start; 57 + rcode_base = limit - rcode_size; 58 + buf_size = rcode_base - base; 59 + uint need_size = image->size + image->fdt_size; 60 + margin = buf_size - need_size; 61 + 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", 62 + spl_phase_name(spl_phase()), spl_phase_name(spl_phase() + 1), 63 + margin >= 0 ? " " : " -", abs(margin), limit, fdt_size, base, 64 + buf_size, image->size, image->fdt_size, need_size); 65 + if (margin < 0) { 66 + log_err("Image size %x but buffer is only %x\n", need_size, 67 + buf_size); 68 + return -ENOSPC; 69 + } 70 + 71 + rcode_buf = map_sysmem(rcode_base, rcode_size); 72 + log_debug("_rcode_start %p: %x -- func %p %x\n", _rcode_start, 73 + *(uint *)_rcode_start, setup_layout, *(uint *)setup_layout); 74 + 75 + image->reloc_offset = rcode_buf - _rcode_start; 76 + log_debug("_rcode start %lx base %lx size %x offset %lx\n", 77 + (ulong)map_to_sysmem(_rcode_start), rcode_base, rcode_size, 78 + image->reloc_offset); 79 + 80 + memcpy(rcode_buf, _rcode_start, rcode_size); 81 + 82 + image->buf = map_sysmem(base, need_size); 83 + image->fdt_buf = image->buf + image->size; 84 + image->rcode_buf = rcode_buf; 85 + *addrp = base; 86 + 87 + return 0; 88 + } 89 + 90 + int spl_reloc_prepare(struct spl_image_info *image, ulong *addrp) 91 + { 92 + int ret; 93 + 94 + ret = setup_layout(image, addrp); 95 + if (ret) 96 + return ret; 97 + 98 + return 0; 99 + } 100 + 101 + typedef void __noreturn (*image_entry_noargs_t)(uint crc, uint unc_len); 102 + 103 + /* this is the relocation + jump code that is copied to the top of memory */ 104 + __rcode int rcode_reloc_and_jump(struct spl_image_info *image) 105 + { 106 + image_entry_noargs_t entry = (image_entry_noargs_t)image->entry_point; 107 + u32 *dst; 108 + ulong image_len; 109 + size_t unc_len; 110 + int ret, crc; 111 + uint magic; 112 + 113 + dst = map_sysmem(image->load_addr, image->size); 114 + unc_len = (void *)image->rcode_buf - (void *)dst; 115 + image_len = image->size; 116 + if (*image->stack_prot != STACK_PROT_VALUE) 117 + return -EFAULT; 118 + magic = get_unaligned_le32(image->buf); 119 + if (CONFIG_IS_ENABLED(LZMA)) { 120 + SizeT lzma_len = unc_len; 121 + 122 + ret = lzmaBuffToBuffDecompress((u8 *)dst, &lzma_len, 123 + image->buf, image_len); 124 + unc_len = lzma_len; 125 + } else if (CONFIG_IS_ENABLED(GZIP)) { 126 + ret = gunzip(dst, unc_len, image->buf, &image_len); 127 + } else if (CONFIG_IS_ENABLED(LZ4) && magic == LZ4F_MAGIC) { 128 + ret = ulz4fn(image->buf, image_len, dst, &unc_len); 129 + if (ret) 130 + return ret; 131 + } else { 132 + u32 *src, *end, *ptr; 133 + 134 + unc_len = image->size; 135 + for (src = image->buf, end = (void *)src + image->size, 136 + ptr = dst; src < end;) 137 + *ptr++ = *src++; 138 + } 139 + if (*image->stack_prot != STACK_PROT_VALUE) 140 + return -EFAULT; 141 + 142 + /* copy in the FDT if needed */ 143 + if (image->fdt_size) 144 + memcpy(image->fdt_start, image->fdt_buf, image->fdt_size); 145 + 146 + crc = crc8(0, (u8 *)dst, unc_len); 147 + 148 + /* jump to the entry point */ 149 + entry(crc, unc_len); 150 + } 151 + 152 + int spl_reloc_jump(struct spl_image_info *image, spl_jump_to_image_t jump) 153 + { 154 + rcode_func loader; 155 + int ret; 156 + 157 + log_debug("malloc usage %lx bytes (%ld KB of %d KB)\n", gd->malloc_ptr, 158 + gd->malloc_ptr / 1024, CONFIG_VAL(SYS_MALLOC_F_LEN) / 1024); 159 + 160 + if (*image->stack_prot != STACK_PROT_VALUE) { 161 + log_err("stack busted, cannot continue\n"); 162 + return -EFAULT; 163 + } 164 + loader = (rcode_func)(void *)rcode_reloc_and_jump + image->reloc_offset; 165 + log_debug("Jumping via %p to %lx - image %p size %x load %lx\n", loader, 166 + image->entry_point, image, image->size, image->load_addr); 167 + 168 + log_debug("unc_len %lx\n", 169 + image->rcode_buf - map_sysmem(image->load_addr, image->size)); 170 + if (DEBUG_JUMP) { 171 + rcode_reloc_and_jump(image); 172 + } else { 173 + /* 174 + * Must disable LOG_DEBUG since the decompressor cannot call 175 + * log functions, printf(), etc. 176 + */ 177 + _Static_assert(DEBUG_JUMP || !_DEBUG, 178 + "Cannot have debug output from decompressor"); 179 + ret = loader(image); 180 + } 181 + 182 + return -EFAULT; 183 + }
+89 -1
include/spl.h
··· 14 14 #include <asm/global_data.h> 15 15 #include <asm/spl.h> 16 16 #include <handoff.h> 17 + #include <image.h> 17 18 #include <mmc.h> 18 19 19 20 struct blk_desc; ··· 265 266 SPL_SANDBOXF_ARG_IS_BUF, 266 267 }; 267 268 269 + /** 270 + * struct spl_image_info - Information about the SPL image being loaded 271 + * 272 + * @fdt_size: Size of the FDT for the image (0 if none) 273 + * @buf: Buffer where the image should be loaded 274 + * @fdt_buf: Buffer where the FDT will be copied by spl_reloc_jump(), only used 275 + * if @fdt_size is non-zero 276 + * @fdt_start: Pointer to the FDT to be copied (must be set up before calling 277 + * spl_reloc_jump() 278 + * @rcode_buf: Buffer to hold the relocating-jump code 279 + * @stack_prot: Pointer to the stack-protection value, used to ensure the stack 280 + * does not overflow 281 + * @reloc_offset: offset between the relocating-jump code and its place in the 282 + * currently running image 283 + */ 268 284 struct spl_image_info { 269 285 const char *name; 270 286 u8 os; ··· 276 292 u32 boot_device; 277 293 u32 offset; 278 294 u32 size; 295 + ulong fdt_size; 279 296 u32 flags; 280 297 void *arg; 281 298 #ifdef CONFIG_SPL_LEGACY_IMAGE_CRC_CHECK ··· 283 300 ulong dcrc_length; 284 301 ulong dcrc; 285 302 #endif 303 + #if CONFIG_IS_ENABLED(RELOC_LOADER) 304 + void *buf; 305 + void *fdt_buf; 306 + void *fdt_start; 307 + void *rcode_buf; 308 + uint *stack_prot; 309 + ulong reloc_offset; 310 + #endif 286 311 }; 312 + 313 + /* function to jump to an image from SPL */ 314 + typedef void __noreturn (*spl_jump_to_image_t)(struct spl_image_info *); 287 315 288 316 static inline void *spl_image_fdt_addr(struct spl_image_info *info) 289 317 { ··· 316 344 * @read: Function to call to read from the device 317 345 * @priv: Private data for the device 318 346 * @bl_len: Block length for reading in bytes 347 + * @phase: Image phase to load 348 + * @fit_loaded: true if the FIT has been loaded, except for external data 319 349 */ 320 350 struct spl_load_info { 321 351 spl_load_reader read; 322 352 void *priv; 323 353 #if IS_ENABLED(CONFIG_SPL_LOAD_BLOCK) 324 - int bl_len; 354 + u16 bl_len; 355 + #endif 356 + #if CONFIG_IS_ENABLED(BOOTMETH_VBE) 357 + u8 phase; 358 + u8 fit_loaded; 325 359 #endif 326 360 }; 327 361 ··· 344 378 #endif 345 379 } 346 380 381 + static inline void xpl_set_phase(struct spl_load_info *info, 382 + enum image_phase_t phase) 383 + { 384 + #if CONFIG_IS_ENABLED(BOOTMETH_VBE) 385 + info->phase = phase; 386 + #endif 387 + } 388 + 389 + static inline enum image_phase_t xpl_get_phase(struct spl_load_info *info) 390 + { 391 + #if CONFIG_IS_ENABLED(BOOTMETH_VBE) 392 + return info->phase; 393 + #else 394 + return IH_PHASE_NONE; 395 + #endif 396 + } 397 + 398 + static inline bool xpl_get_fit_loaded(struct spl_load_info *info) 399 + { 400 + #if CONFIG_IS_ENABLED(BOOTMETH_VBE) 401 + return info->fit_loaded; 402 + #else 403 + return false; 404 + #endif 405 + } 406 + 347 407 /** 348 408 * spl_load_init() - Set up a new spl_load_info structure 349 409 */ ··· 354 414 load->read = h_read; 355 415 load->priv = priv; 356 416 spl_set_bl_len(load, bl_len); 417 + xpl_set_phase(load, IH_PHASE_NONE); 357 418 } 358 419 359 420 /* ··· 1112 1173 * This must be called before upl_add_image(), etc. 1113 1174 */ 1114 1175 void spl_upl_init(void); 1176 + 1177 + /** 1178 + * spl_reloc_prepare() - Prepare the relocating loader ready for use 1179 + * 1180 + * Sets up the relocating loader ready for use. This must be called before 1181 + * spl_reloc_jump() can be used. 1182 + * 1183 + * The memory layout is figured out, making use of the space between the top of 1184 + * the current image and the top of memory. 1185 + * 1186 + * Once this is done, the relocating-jump code is copied into place at 1187 + * image->rcode_buf 1188 + * 1189 + * @image: SPL image containing information. This is updated with various 1190 + * necessary values. On entry, the size and fdt_size fields must be valid 1191 + * @addrp: Returns the address to which the image should be loaded into memory 1192 + * Return 0 if OK, -ENOSPC if there is not enough memory available 1193 + */ 1194 + int spl_reloc_prepare(struct spl_image_info *image, ulong *addrp); 1195 + 1196 + /** 1197 + * spl_reloc_jump() - Jump to an image, via a 'relocating-jump' region 1198 + * 1199 + * @image: SPL image to jump to 1200 + * @func: Function to call in the final image 1201 + */ 1202 + int spl_reloc_jump(struct spl_image_info *image, spl_jump_to_image_t func); 1115 1203 1116 1204 #endif