Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10599d661c | ||
|
|
18852972b2 | ||
|
|
3da5b8c67b | ||
|
|
96b19f562e | ||
|
|
ccc0ed0e90 | ||
|
|
5811cfc1b7 | ||
|
|
50b481e14e | ||
|
|
26298acf7a | ||
|
|
f9cd7ed6e9 | ||
|
|
979827f115 | ||
|
|
8f940af49d | ||
|
|
2eadd2f821 | ||
|
|
c00826edc9 | ||
|
|
9591813ca5 | ||
|
|
a94df18146 | ||
|
|
0388cd7ed4 | ||
|
|
2e259b8474 | ||
|
|
99ab046fd4 | ||
|
|
9faf50492a | ||
|
|
a5cbd3a547 | ||
|
|
c788422d5c | ||
|
|
687299f0c1 |
@@ -66,7 +66,7 @@ endif
|
|||||||
|
|
||||||
obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE) += vbe.o
|
obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE) += vbe.o
|
||||||
obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_REQUEST) += vbe_request.o
|
obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_REQUEST) += vbe_request.o
|
||||||
obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE) += vbe_simple.o
|
obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE) += vbe_simple.o vbe_common.o
|
||||||
obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_FW) += vbe_simple_fw.o
|
obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_FW) += vbe_simple_fw.o
|
||||||
obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_OS) += vbe_simple_os.o
|
obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_OS) += vbe_simple_os.o
|
||||||
|
|
||||||
|
|||||||
375
boot/vbe_common.c
Normal file
375
boot/vbe_common.c
Normal file
@@ -0,0 +1,375 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Verified Boot for Embedded (VBE) common functions
|
||||||
|
*
|
||||||
|
* Copyright 2024 Google LLC
|
||||||
|
* Written by Simon Glass <sjg@chromium.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <bootstage.h>
|
||||||
|
#include <dm.h>
|
||||||
|
#include <blk.h>
|
||||||
|
#include <image.h>
|
||||||
|
#include <mapmem.h>
|
||||||
|
#include <memalign.h>
|
||||||
|
#include <spl.h>
|
||||||
|
#include <u-boot/crc.h>
|
||||||
|
#include "vbe_common.h"
|
||||||
|
|
||||||
|
binman_sym_declare(ulong, u_boot_vpl_nodtb, size);
|
||||||
|
binman_sym_declare(ulong, u_boot_vpl_bss_pad, size);
|
||||||
|
binman_sym_declare(ulong, u_boot_spl_nodtb, size);
|
||||||
|
binman_sym_declare(ulong, u_boot_spl_bss_pad, size);
|
||||||
|
|
||||||
|
int vbe_get_blk(const char *storage, struct udevice **blkp)
|
||||||
|
{
|
||||||
|
struct blk_desc *desc;
|
||||||
|
char devname[16];
|
||||||
|
const char *end;
|
||||||
|
int devnum;
|
||||||
|
|
||||||
|
/* First figure out the block device */
|
||||||
|
log_debug("storage=%s\n", storage);
|
||||||
|
devnum = trailing_strtoln_end(storage, NULL, &end);
|
||||||
|
if (devnum == -1)
|
||||||
|
return log_msg_ret("num", -ENODEV);
|
||||||
|
if (end - storage >= sizeof(devname))
|
||||||
|
return log_msg_ret("end", -E2BIG);
|
||||||
|
strlcpy(devname, storage, end - storage + 1);
|
||||||
|
log_debug("dev=%s, %x\n", devname, devnum);
|
||||||
|
|
||||||
|
desc = blk_get_dev(devname, devnum);
|
||||||
|
if (!desc)
|
||||||
|
return log_msg_ret("get", -ENXIO);
|
||||||
|
*blkp = desc->bdev;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int vbe_read_version(struct udevice *blk, ulong offset, char *version,
|
||||||
|
int max_size)
|
||||||
|
{
|
||||||
|
ALLOC_CACHE_ALIGN_BUFFER(u8, buf, MMC_MAX_BLOCK_LEN);
|
||||||
|
|
||||||
|
/* we can use an assert() here since we already read only one block */
|
||||||
|
assert(max_size <= MMC_MAX_BLOCK_LEN);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* we can use an assert() here since reading the wrong block will just
|
||||||
|
* cause an invalid version-string to be (safely) read
|
||||||
|
*/
|
||||||
|
assert(!(offset & (MMC_MAX_BLOCK_LEN - 1)));
|
||||||
|
|
||||||
|
offset /= MMC_MAX_BLOCK_LEN;
|
||||||
|
|
||||||
|
if (blk_read(blk, offset, 1, buf) != 1)
|
||||||
|
return log_msg_ret("read", -EIO);
|
||||||
|
strlcpy(version, buf, max_size);
|
||||||
|
log_debug("version=%s\n", version);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int vbe_read_nvdata(struct udevice *blk, ulong offset, ulong size, u8 *buf)
|
||||||
|
{
|
||||||
|
uint hdr_ver, hdr_size, data_size, crc;
|
||||||
|
const struct vbe_nvdata *nvd;
|
||||||
|
|
||||||
|
/* we can use an assert() here since we already read only one block */
|
||||||
|
assert(size <= MMC_MAX_BLOCK_LEN);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We can use an assert() here since reading the wrong block will just
|
||||||
|
* cause invalid state to be (safely) read. If the crc passes, then we
|
||||||
|
* obtain invalid state and it will likely cause booting to fail.
|
||||||
|
*
|
||||||
|
* VBE relies on valid values being in U-Boot's devicetree, so this
|
||||||
|
* should not every be wrong on a production device.
|
||||||
|
*/
|
||||||
|
assert(!(offset & (MMC_MAX_BLOCK_LEN - 1)));
|
||||||
|
|
||||||
|
if (offset & (MMC_MAX_BLOCK_LEN - 1))
|
||||||
|
return log_msg_ret("get", -EBADF);
|
||||||
|
offset /= MMC_MAX_BLOCK_LEN;
|
||||||
|
|
||||||
|
if (blk_read(blk, offset, 1, buf) != 1)
|
||||||
|
return log_msg_ret("read", -EIO);
|
||||||
|
nvd = (struct vbe_nvdata *)buf;
|
||||||
|
hdr_ver = (nvd->hdr & NVD_HDR_VER_MASK) >> NVD_HDR_VER_SHIFT;
|
||||||
|
hdr_size = (nvd->hdr & NVD_HDR_SIZE_MASK) >> NVD_HDR_SIZE_SHIFT;
|
||||||
|
if (hdr_ver != NVD_HDR_VER_CUR)
|
||||||
|
return log_msg_ret("hdr", -EPERM);
|
||||||
|
data_size = 1 << hdr_size;
|
||||||
|
if (!data_size || data_size > sizeof(*nvd))
|
||||||
|
return log_msg_ret("sz", -EPERM);
|
||||||
|
|
||||||
|
crc = crc8(0, buf + 1, data_size - 1);
|
||||||
|
if (crc != nvd->crc8)
|
||||||
|
return log_msg_ret("crc", -EPERM);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* h_vbe_load_read() - Handler for reading an SPL image from a FIT
|
||||||
|
*
|
||||||
|
* See spl_load_reader for the definition
|
||||||
|
*/
|
||||||
|
ulong h_vbe_load_read(struct spl_load_info *load, ulong off, ulong size,
|
||||||
|
void *buf)
|
||||||
|
{
|
||||||
|
struct blk_desc *desc = load->priv;
|
||||||
|
lbaint_t sector = off >> desc->log2blksz;
|
||||||
|
lbaint_t count = size >> desc->log2blksz;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
log_debug("vbe read log2blksz %x offset %lx sector %lx count %lx\n",
|
||||||
|
desc->log2blksz, (ulong)off, (long)sector, (ulong)count);
|
||||||
|
|
||||||
|
ret = blk_dread(desc, sector, count, buf);
|
||||||
|
log_debug("ret=%x\n", ret);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return ret << desc->log2blksz;
|
||||||
|
}
|
||||||
|
|
||||||
|
int vbe_read_fit(struct udevice *blk, ulong area_offset, ulong area_size,
|
||||||
|
struct spl_image_info *image, ulong *load_addrp, ulong *lenp,
|
||||||
|
char **namep)
|
||||||
|
{
|
||||||
|
ALLOC_CACHE_ALIGN_BUFFER(u8, sbuf, MMC_MAX_BLOCK_LEN);
|
||||||
|
ulong size, blknum, addr, len, load_addr, num_blks, spl_load_addr;
|
||||||
|
ulong aligned_size, fdt_load_addr, fdt_size;
|
||||||
|
const char *fit_uname, *fit_uname_config;
|
||||||
|
struct bootm_headers images = {};
|
||||||
|
enum image_phase_t phase;
|
||||||
|
struct blk_desc *desc;
|
||||||
|
int node, ret;
|
||||||
|
bool for_xpl;
|
||||||
|
void *buf;
|
||||||
|
|
||||||
|
desc = dev_get_uclass_plat(blk);
|
||||||
|
|
||||||
|
/* read in one block to find the FIT size */
|
||||||
|
blknum = area_offset / desc->blksz;
|
||||||
|
log_debug("read at %lx, blknum %lx\n", area_offset, blknum);
|
||||||
|
ret = blk_read(blk, blknum, 1, sbuf);
|
||||||
|
if (ret < 0)
|
||||||
|
return log_msg_ret("rd", ret);
|
||||||
|
else if (ret != 1)
|
||||||
|
return log_msg_ret("rd2", -EIO);
|
||||||
|
|
||||||
|
ret = fdt_check_header(sbuf);
|
||||||
|
if (ret < 0)
|
||||||
|
return log_msg_ret("fdt", -EINVAL);
|
||||||
|
size = fdt_totalsize(sbuf);
|
||||||
|
if (size > area_size)
|
||||||
|
return log_msg_ret("fdt", -E2BIG);
|
||||||
|
log_debug("FIT size %lx\n", size);
|
||||||
|
aligned_size = ALIGN(size, desc->blksz);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Load the FIT into the SPL memory. This is typically a FIT with
|
||||||
|
* external data, so this is quite small, perhaps a few KB.
|
||||||
|
*/
|
||||||
|
if (IS_ENABLED(CONFIG_SANDBOX)) {
|
||||||
|
addr = CONFIG_VAL(TEXT_BASE);
|
||||||
|
buf = map_sysmem(addr, size);
|
||||||
|
} else {
|
||||||
|
buf = malloc(aligned_size);
|
||||||
|
if (!buf)
|
||||||
|
return log_msg_ret("fit", -ENOMEM);
|
||||||
|
addr = map_to_sysmem(buf);
|
||||||
|
}
|
||||||
|
num_blks = aligned_size / desc->blksz;
|
||||||
|
log_debug("read %lx, %lx blocks to %lx / %p\n", aligned_size, num_blks,
|
||||||
|
addr, buf);
|
||||||
|
ret = blk_read(blk, blknum, num_blks, buf);
|
||||||
|
if (ret < 0)
|
||||||
|
return log_msg_ret("rd3", ret);
|
||||||
|
else if (ret != num_blks)
|
||||||
|
return log_msg_ret("rd4", -EIO);
|
||||||
|
log_debug("check total size %x off_dt_strings %x\n", fdt_totalsize(buf),
|
||||||
|
fdt_off_dt_strings(buf));
|
||||||
|
|
||||||
|
#if CONFIG_IS_ENABLED(SYS_MALLOC_F)
|
||||||
|
log_debug("malloc base %lx ptr %x limit %x top %lx\n",
|
||||||
|
gd->malloc_base, gd->malloc_ptr, gd->malloc_limit,
|
||||||
|
gd->malloc_base + gd->malloc_limit);
|
||||||
|
#endif
|
||||||
|
/* figure out the phase to load */
|
||||||
|
phase = IS_ENABLED(CONFIG_TPL_BUILD) ? IH_PHASE_NONE :
|
||||||
|
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");
|
||||||
|
|
||||||
|
if (xpl_phase() == PHASE_SPL && !IS_ENABLED(CONFIG_SANDBOX)) {
|
||||||
|
struct spl_load_info info;
|
||||||
|
|
||||||
|
spl_load_init(&info, h_vbe_load_read, desc, desc->blksz);
|
||||||
|
xpl_set_phase(&info, IH_PHASE_U_BOOT);
|
||||||
|
log_debug("doing SPL from %s blksz %lx log2blksz %x area_offset %lx + fdt_size %lx\n",
|
||||||
|
blk->name, desc->blksz, desc->log2blksz, area_offset, ALIGN(size, 4));
|
||||||
|
ret = spl_load_simple_fit(image, &info, area_offset, buf);
|
||||||
|
log_debug("spl_load_abrec_fit() ret=%d\n", ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = fit_image_load(&images, addr, &fit_uname, &fit_uname_config,
|
||||||
|
IH_ARCH_DEFAULT, image_ph(phase, IH_TYPE_FIRMWARE),
|
||||||
|
BOOTSTAGE_ID_FIT_SPL_START, FIT_LOAD_IGNORED,
|
||||||
|
&load_addr, &len);
|
||||||
|
if (ret == -ENOENT) {
|
||||||
|
ret = fit_image_load(&images, addr, &fit_uname,
|
||||||
|
&fit_uname_config, IH_ARCH_DEFAULT,
|
||||||
|
image_ph(phase, IH_TYPE_LOADABLE),
|
||||||
|
BOOTSTAGE_ID_FIT_SPL_START,
|
||||||
|
FIT_LOAD_IGNORED, &load_addr, &len);
|
||||||
|
}
|
||||||
|
if (ret < 0)
|
||||||
|
return log_msg_ret("ld", ret);
|
||||||
|
node = ret;
|
||||||
|
log_debug("load %lx size %lx\n", load_addr, len);
|
||||||
|
|
||||||
|
fdt_load_addr = 0;
|
||||||
|
fdt_size = 0;
|
||||||
|
if ((xpl_phase() == PHASE_TPL || xpl_phase() == PHASE_VPL) &&
|
||||||
|
!IS_ENABLED(CONFIG_SANDBOX)) {
|
||||||
|
/* allow use of a different image from the configuration node */
|
||||||
|
fit_uname = NULL;
|
||||||
|
ret = fit_image_load(&images, addr, &fit_uname,
|
||||||
|
&fit_uname_config, IH_ARCH_DEFAULT,
|
||||||
|
image_ph(phase, IH_TYPE_FLATDT),
|
||||||
|
BOOTSTAGE_ID_FIT_SPL_START,
|
||||||
|
FIT_LOAD_IGNORED, &fdt_load_addr,
|
||||||
|
&fdt_size);
|
||||||
|
fdt_size = ALIGN(fdt_size, desc->blksz);
|
||||||
|
log_debug("FDT noload to %lx size %lx\n", fdt_load_addr,
|
||||||
|
fdt_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
for_xpl = !USE_BOOTMETH && CONFIG_IS_ENABLED(RELOC_LOADER);
|
||||||
|
if (for_xpl) {
|
||||||
|
image->size = len;
|
||||||
|
image->fdt_size = fdt_size;
|
||||||
|
ret = spl_reloc_prepare(image, &spl_load_addr);
|
||||||
|
if (ret)
|
||||||
|
return log_msg_ret("spl", ret);
|
||||||
|
}
|
||||||
|
if (!IS_ENABLED(CONFIG_SANDBOX))
|
||||||
|
image->os = IH_OS_U_BOOT;
|
||||||
|
|
||||||
|
/* For FIT external data, read in the external data */
|
||||||
|
log_debug("load_addr %lx len %lx addr %lx aligned_size %lx\n",
|
||||||
|
load_addr, len, addr, aligned_size);
|
||||||
|
if (load_addr + len > addr + aligned_size) {
|
||||||
|
ulong base, full_size, offset, extra, fdt_base, fdt_full_size;
|
||||||
|
ulong fdt_offset;
|
||||||
|
void *base_buf, *fdt_base_buf;
|
||||||
|
|
||||||
|
/* Find the start address to load from */
|
||||||
|
base = ALIGN_DOWN(load_addr, desc->blksz);
|
||||||
|
|
||||||
|
offset = area_offset + load_addr - addr;
|
||||||
|
blknum = offset / desc->blksz;
|
||||||
|
extra = offset % desc->blksz;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the total number of bytes to load, taking care of
|
||||||
|
* block alignment
|
||||||
|
*/
|
||||||
|
full_size = len + extra;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the start block number, number of blocks and the address
|
||||||
|
* to load to, then load the blocks
|
||||||
|
*/
|
||||||
|
num_blks = DIV_ROUND_UP(full_size, desc->blksz);
|
||||||
|
if (for_xpl)
|
||||||
|
base = spl_load_addr;
|
||||||
|
base_buf = map_sysmem(base, full_size);
|
||||||
|
ret = blk_read(blk, blknum, num_blks, base_buf);
|
||||||
|
log_debug("read foffset %lx blknum %lx full_size %lx num_blks %lx to %lx / %p: ret=%d\n",
|
||||||
|
offset - 0x8000, blknum, full_size, num_blks, base, base_buf,
|
||||||
|
ret);
|
||||||
|
if (ret < 0)
|
||||||
|
return log_msg_ret("rd", ret);
|
||||||
|
if (ret != num_blks)
|
||||||
|
return log_msg_ret("rd", -EIO);
|
||||||
|
if (extra && !IS_ENABLED(CONFIG_SANDBOX)) {
|
||||||
|
log_debug("move %p %p %lx\n", base_buf,
|
||||||
|
base_buf + extra, len);
|
||||||
|
memmove(base_buf, base_buf + extra, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((xpl_phase() == PHASE_VPL || xpl_phase() == PHASE_TPL) &&
|
||||||
|
!IS_ENABLED(CONFIG_SANDBOX)) {
|
||||||
|
image->load_addr = spl_get_image_text_base();
|
||||||
|
image->entry_point = image->load_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now the FDT */
|
||||||
|
if (fdt_size) {
|
||||||
|
fdt_offset = area_offset + fdt_load_addr - addr;
|
||||||
|
blknum = fdt_offset / desc->blksz;
|
||||||
|
extra = fdt_offset % desc->blksz;
|
||||||
|
fdt_full_size = fdt_size + extra;
|
||||||
|
num_blks = DIV_ROUND_UP(fdt_full_size, desc->blksz);
|
||||||
|
fdt_base = ALIGN(base + len, 4);
|
||||||
|
fdt_base_buf = map_sysmem(fdt_base, fdt_size);
|
||||||
|
ret = blk_read(blk, blknum, num_blks, fdt_base_buf);
|
||||||
|
log_debug("fdt read foffset %lx blknum %lx full_size %lx num_blks %lx to %lx / %p: ret=%d\n",
|
||||||
|
fdt_offset - 0x8000, blknum, fdt_full_size, num_blks,
|
||||||
|
fdt_base, fdt_base_buf, ret);
|
||||||
|
if (ret != num_blks)
|
||||||
|
return log_msg_ret("rdf", -EIO);
|
||||||
|
if (extra) {
|
||||||
|
log_debug("move %p %p %lx\n", fdt_base_buf,
|
||||||
|
fdt_base_buf + extra, fdt_size);
|
||||||
|
memmove(fdt_base_buf, fdt_base_buf + extra,
|
||||||
|
fdt_size);
|
||||||
|
}
|
||||||
|
#if CONFIG_IS_ENABLED(RELOC_LOADER)
|
||||||
|
image->fdt_buf = fdt_base_buf;
|
||||||
|
|
||||||
|
ulong xpl_size;
|
||||||
|
ulong xpl_pad;
|
||||||
|
ulong fdt_start;
|
||||||
|
|
||||||
|
if (xpl_phase() == PHASE_TPL) {
|
||||||
|
xpl_size = binman_sym(ulong, u_boot_vpl_nodtb, size);
|
||||||
|
xpl_pad = binman_sym(ulong, u_boot_vpl_bss_pad, size);
|
||||||
|
} else {
|
||||||
|
xpl_size = binman_sym(ulong, u_boot_spl_nodtb, size);
|
||||||
|
xpl_pad = binman_sym(ulong, u_boot_spl_bss_pad, size);
|
||||||
|
}
|
||||||
|
fdt_start = image->load_addr + xpl_size + xpl_pad;
|
||||||
|
log_debug("load_addr %lx xpl_size %lx copy-to %lx\n",
|
||||||
|
image->load_addr, xpl_size + xpl_pad,
|
||||||
|
fdt_start);
|
||||||
|
image->fdt_start = map_sysmem(fdt_start, fdt_size);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (load_addrp)
|
||||||
|
*load_addrp = load_addr;
|
||||||
|
if (lenp)
|
||||||
|
*lenp = len;
|
||||||
|
if (namep) {
|
||||||
|
*namep = strdup(fdt_get_name(buf, node, NULL));
|
||||||
|
if (!namep)
|
||||||
|
return log_msg_ret("nam", -ENOMEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
137
boot/vbe_common.h
Normal file
137
boot/vbe_common.h
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
/*
|
||||||
|
* Verified Boot for Embedded (VBE) common functions
|
||||||
|
*
|
||||||
|
* Copyright 2024 Google LLC
|
||||||
|
* Written by Simon Glass <sjg@chromium.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __VBE_COMMON_H
|
||||||
|
#define __VBE_COMMON_H
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
struct spl_image_info;
|
||||||
|
struct udevice;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Controls whether we use a full bootmeth driver with VBE in this phase, or
|
||||||
|
* just access the information directly.
|
||||||
|
*
|
||||||
|
* For now VBE-simple uses the full bootmeth, but VBE-abrec does not, to reduce
|
||||||
|
* code size
|
||||||
|
*/
|
||||||
|
#define USE_BOOTMETH CONFIG_IS_ENABLED(BOOTMETH_VBE_SIMPLE)
|
||||||
|
|
||||||
|
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 vbe_nvdata - basic storage format for non-volatile data
|
||||||
|
*
|
||||||
|
* This is used for all VBE methods
|
||||||
|
*
|
||||||
|
* @crc8: crc8 for the entire record except @crc8 field itself
|
||||||
|
* @hdr: header size and version (NVD_HDR_...)
|
||||||
|
* @spare1: unused, must be 0
|
||||||
|
* @fw_vernum: version and key version (FWVER_...)
|
||||||
|
* @flags: Flags controlling operation (enum vbe_flags)
|
||||||
|
*/
|
||||||
|
struct vbe_nvdata {
|
||||||
|
u8 crc8;
|
||||||
|
u8 hdr;
|
||||||
|
u16 spare1;
|
||||||
|
u32 fw_vernum;
|
||||||
|
u32 flags;
|
||||||
|
u8 spare2[0x34];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vbe_get_blk() - Obtain the block device to use for VBE
|
||||||
|
*
|
||||||
|
* Decodes the string to produce a block device
|
||||||
|
*
|
||||||
|
* @storage: String indicating the device to use, e.g. "mmc1"
|
||||||
|
* @blkp: Returns associated block device, on success
|
||||||
|
* Return 0 if OK, -ENODEV if @storage does not end with a number, -E2BIG if
|
||||||
|
* the device name is more than 15 characters, -ENXIO if the block device could
|
||||||
|
* not be found
|
||||||
|
*/
|
||||||
|
int vbe_get_blk(const char *storage, struct udevice **blkp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vbe_read_version() - Read version-string from a block device
|
||||||
|
*
|
||||||
|
* Reads the VBE version-string from a device. This function reads a single
|
||||||
|
* block from the device, so the string cannot be larger than that. It uses a
|
||||||
|
* temporary buffer for the read, then copies in up to @size bytes
|
||||||
|
*
|
||||||
|
* @blk: Device to read from
|
||||||
|
* @offset: Offset to read, in bytes
|
||||||
|
* @version: Place to put the string
|
||||||
|
* @max_size: Maximum size of @version
|
||||||
|
* Return: 0 if OK, -E2BIG if @max_size > block size, -EBADF if the offset is
|
||||||
|
* not block-aligned, -EIO if an I/O error occurred
|
||||||
|
*/
|
||||||
|
int vbe_read_version(struct udevice *blk, ulong offset, char *version,
|
||||||
|
int max_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vbe_read_nvdata() - Read non-volatile data from a block device
|
||||||
|
*
|
||||||
|
* Reads the VBE nvdata from a device. This function reads a single block from
|
||||||
|
* the device, so the nvdata cannot be larger than that.
|
||||||
|
*
|
||||||
|
* @blk: Device to read from
|
||||||
|
* @offset: Offset to read, in bytes
|
||||||
|
* @size: Number of bytes to read
|
||||||
|
* @buf: Buffer to hold the data
|
||||||
|
* Return: 0 if OK, -E2BIG if @size > block size, -EBADF if the offset is not
|
||||||
|
* block-aligned, -EIO if an I/O error occurred, -EPERM if the header version is
|
||||||
|
* incorrect, the header size is invalid or the data fails its CRC check
|
||||||
|
*/
|
||||||
|
int vbe_read_nvdata(struct udevice *blk, ulong offset, ulong size, u8 *buf);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vbe_read_fit() - Read an image from a FIT
|
||||||
|
*
|
||||||
|
* This handles most of the VBE logic for reading from a FIT. It reads the FIT
|
||||||
|
* metadata, decides which image to load and loads it to a suitable address,
|
||||||
|
* ready for jumping to the next phase of VBE.
|
||||||
|
*
|
||||||
|
* This supports transition from VPL to SPL as well as SPL to U-Boot proper. For
|
||||||
|
* now, TPL->VPL is not supported.
|
||||||
|
*
|
||||||
|
* Both embedded and external data are supported for the FIT
|
||||||
|
*
|
||||||
|
* @blk: Block device containing FIT
|
||||||
|
* @area_offset: Byte offset of the VBE area in @blk containing the FIT
|
||||||
|
* @area_size: Size of the VBE area
|
||||||
|
* @image: SPL image to fill in with details of the loaded image, or NULL
|
||||||
|
* @load_addrp: If non-null, returns the address where the image was loaded
|
||||||
|
* @lenp: If non-null, returns the size of the image loaded, in bytes
|
||||||
|
* @namep: If non-null, returns the name of the FIT-image node that was loaded
|
||||||
|
* (allocated by this function)
|
||||||
|
* Return: 0 if OK, -EINVAL if the area does not contain an FDT (the underlying
|
||||||
|
* format for FIT), -E2BIG if the FIT extends past @area_size, -ENOMEM if there
|
||||||
|
* was not space to allocate the image-node name, other error if a read error
|
||||||
|
* occurred (see blk_read()), or something went wrong with the actually
|
||||||
|
* FIT-parsing (see fit_image_load()).
|
||||||
|
*/
|
||||||
|
int vbe_read_fit(struct udevice *blk, ulong area_offset, ulong area_size,
|
||||||
|
struct spl_image_info *image, ulong *load_addrp, ulong *lenp,
|
||||||
|
char **namep);
|
||||||
|
|
||||||
|
#endif /* __VBE_ABREC_H */
|
||||||
@@ -18,70 +18,21 @@
|
|||||||
#include <vbe.h>
|
#include <vbe.h>
|
||||||
#include <dm/device-internal.h>
|
#include <dm/device-internal.h>
|
||||||
#include <dm/ofnode.h>
|
#include <dm/ofnode.h>
|
||||||
#include <u-boot/crc.h>
|
|
||||||
#include "vbe_simple.h"
|
#include "vbe_simple.h"
|
||||||
|
|
||||||
/** struct simple_nvdata - storage format for non-volatile data */
|
static int simple_read_nvdata(const struct simple_priv *priv,
|
||||||
struct simple_nvdata {
|
struct udevice *blk, struct simple_state *state)
|
||||||
u8 crc8;
|
|
||||||
u8 hdr;
|
|
||||||
u16 spare1;
|
|
||||||
u32 fw_vernum;
|
|
||||||
u8 spare2[0x38];
|
|
||||||
};
|
|
||||||
|
|
||||||
static int simple_read_version(struct udevice *dev, struct blk_desc *desc,
|
|
||||||
u8 *buf, struct simple_state *state)
|
|
||||||
{
|
{
|
||||||
struct simple_priv *priv = dev_get_priv(dev);
|
ALLOC_CACHE_ALIGN_BUFFER(u8, buf, MMC_MAX_BLOCK_LEN);
|
||||||
int start;
|
const struct vbe_nvdata *nvd;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (priv->version_size > MMC_MAX_BLOCK_LEN)
|
ret = vbe_read_nvdata(blk, priv->area_start + priv->state_offset,
|
||||||
return log_msg_ret("ver", -E2BIG);
|
priv->state_size, buf);
|
||||||
|
if (ret)
|
||||||
|
return log_msg_ret("nv", ret);
|
||||||
|
|
||||||
start = priv->area_start + priv->version_offset;
|
nvd = (struct vbe_nvdata *)buf;
|
||||||
if (start & (MMC_MAX_BLOCK_LEN - 1))
|
|
||||||
return log_msg_ret("get", -EBADF);
|
|
||||||
start /= MMC_MAX_BLOCK_LEN;
|
|
||||||
|
|
||||||
if (blk_dread(desc, start, 1, buf) != 1)
|
|
||||||
return log_msg_ret("read", -EIO);
|
|
||||||
strlcpy(state->fw_version, buf, MAX_VERSION_LEN);
|
|
||||||
log_debug("version=%s\n", state->fw_version);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int simple_read_nvdata(struct udevice *dev, struct blk_desc *desc,
|
|
||||||
u8 *buf, struct simple_state *state)
|
|
||||||
{
|
|
||||||
struct simple_priv *priv = dev_get_priv(dev);
|
|
||||||
uint hdr_ver, hdr_size, size, crc;
|
|
||||||
const struct simple_nvdata *nvd;
|
|
||||||
int start;
|
|
||||||
|
|
||||||
if (priv->state_size > MMC_MAX_BLOCK_LEN)
|
|
||||||
return log_msg_ret("state", -E2BIG);
|
|
||||||
|
|
||||||
start = priv->area_start + priv->state_offset;
|
|
||||||
if (start & (MMC_MAX_BLOCK_LEN - 1))
|
|
||||||
return log_msg_ret("get", -EBADF);
|
|
||||||
start /= MMC_MAX_BLOCK_LEN;
|
|
||||||
|
|
||||||
if (blk_dread(desc, start, 1, buf) != 1)
|
|
||||||
return log_msg_ret("read", -EIO);
|
|
||||||
nvd = (struct simple_nvdata *)buf;
|
|
||||||
hdr_ver = (nvd->hdr & NVD_HDR_VER_MASK) >> NVD_HDR_VER_SHIFT;
|
|
||||||
hdr_size = (nvd->hdr & NVD_HDR_SIZE_MASK) >> NVD_HDR_SIZE_SHIFT;
|
|
||||||
if (hdr_ver != NVD_HDR_VER_CUR)
|
|
||||||
return log_msg_ret("hdr", -EPERM);
|
|
||||||
size = 1 << hdr_size;
|
|
||||||
if (size > sizeof(*nvd))
|
|
||||||
return log_msg_ret("sz", -ENOEXEC);
|
|
||||||
|
|
||||||
crc = crc8(0, buf + 1, size - 1);
|
|
||||||
if (crc != nvd->crc8)
|
|
||||||
return log_msg_ret("crc", -EPERM);
|
|
||||||
state->fw_vernum = nvd->fw_vernum;
|
state->fw_vernum = nvd->fw_vernum;
|
||||||
|
|
||||||
log_debug("version=%s\n", state->fw_version);
|
log_debug("version=%s\n", state->fw_version);
|
||||||
@@ -91,33 +42,20 @@ static int simple_read_nvdata(struct udevice *dev, struct blk_desc *desc,
|
|||||||
|
|
||||||
int vbe_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);
|
struct simple_priv *priv = dev_get_priv(dev);
|
||||||
struct blk_desc *desc;
|
struct udevice *blk;
|
||||||
char devname[16];
|
|
||||||
const char *end;
|
|
||||||
int devnum;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* First figure out the block device */
|
ret = vbe_get_blk(priv->storage, &blk);
|
||||||
log_debug("storage=%s\n", priv->storage);
|
if (ret)
|
||||||
devnum = trailing_strtoln_end(priv->storage, NULL, &end);
|
return log_msg_ret("blk", ret);
|
||||||
if (devnum == -1)
|
|
||||||
return log_msg_ret("num", -ENODEV);
|
|
||||||
if (end - priv->storage >= sizeof(devname))
|
|
||||||
return log_msg_ret("end", -E2BIG);
|
|
||||||
strlcpy(devname, priv->storage, end - priv->storage + 1);
|
|
||||||
log_debug("dev=%s, %x\n", devname, devnum);
|
|
||||||
|
|
||||||
desc = blk_get_dev(devname, devnum);
|
ret = vbe_read_version(blk, priv->area_start + priv->version_offset,
|
||||||
if (!desc)
|
state->fw_version, MAX_VERSION_LEN);
|
||||||
return log_msg_ret("get", -ENXIO);
|
|
||||||
|
|
||||||
ret = simple_read_version(dev, desc, buf, state);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return log_msg_ret("ver", ret);
|
return log_msg_ret("ver", ret);
|
||||||
|
|
||||||
ret = simple_read_nvdata(dev, desc, buf, state);
|
ret = simple_read_nvdata(priv, blk, state);
|
||||||
if (ret)
|
if (ret)
|
||||||
return log_msg_ret("nvd", ret);
|
return log_msg_ret("nvd", ret);
|
||||||
|
|
||||||
|
|||||||
@@ -9,20 +9,8 @@
|
|||||||
#ifndef __VBE_SIMPLE_H
|
#ifndef __VBE_SIMPLE_H
|
||||||
#define __VBE_SIMPLE_H
|
#define __VBE_SIMPLE_H
|
||||||
|
|
||||||
enum {
|
#include <linux/types.h>
|
||||||
MAX_VERSION_LEN = 256,
|
#include "vbe_common.h"
|
||||||
|
|
||||||
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 - information read from the device tree */
|
||||||
struct simple_priv {
|
struct simple_priv {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#define LOG_CATEGORY LOGC_BOOT
|
#define LOG_CATEGORY LOGC_BOOT
|
||||||
|
|
||||||
|
#include <binman_sym.h>
|
||||||
#include <bloblist.h>
|
#include <bloblist.h>
|
||||||
#include <bootdev.h>
|
#include <bootdev.h>
|
||||||
#include <bootflow.h>
|
#include <bootflow.h>
|
||||||
@@ -17,13 +18,24 @@
|
|||||||
#include <image.h>
|
#include <image.h>
|
||||||
#include <log.h>
|
#include <log.h>
|
||||||
#include <mapmem.h>
|
#include <mapmem.h>
|
||||||
#include <memalign.h>
|
|
||||||
#include <mmc.h>
|
#include <mmc.h>
|
||||||
#include <spl.h>
|
#include <spl.h>
|
||||||
#include <vbe.h>
|
#include <vbe.h>
|
||||||
#include <dm/device-internal.h>
|
#include <dm/device-internal.h>
|
||||||
|
#include "vbe_common.h"
|
||||||
#include "vbe_simple.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
|
* vbe_simple_read_bootflow_fw() - Create a bootflow for firmware
|
||||||
*
|
*
|
||||||
@@ -38,109 +50,26 @@
|
|||||||
*/
|
*/
|
||||||
int vbe_simple_read_bootflow_fw(struct udevice *dev, struct bootflow *bflow)
|
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 *media = dev_get_parent(bflow->dev);
|
||||||
struct udevice *meth = bflow->method;
|
struct udevice *meth = bflow->method;
|
||||||
struct simple_priv *priv = dev_get_priv(meth);
|
struct simple_priv *priv = dev_get_priv(meth);
|
||||||
const char *fit_uname, *fit_uname_config;
|
ulong len, load_addr;
|
||||||
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;
|
struct udevice *blk;
|
||||||
int node, ret;
|
int ret;
|
||||||
void *buf;
|
|
||||||
|
|
||||||
log_debug("media=%s\n", media->name);
|
log_debug("media=%s\n", media->name);
|
||||||
ret = blk_get_from_parent(media, &blk);
|
ret = blk_get_from_parent(media, &blk);
|
||||||
if (ret)
|
if (ret)
|
||||||
return log_msg_ret("med", ret);
|
return log_msg_ret("med", ret);
|
||||||
log_debug("blk=%s\n", blk->name);
|
log_debug("blk=%s\n", blk->name);
|
||||||
desc = dev_get_uclass_plat(blk);
|
|
||||||
|
|
||||||
offset = priv->area_start + priv->skip_offset;
|
ret = vbe_read_fit(blk, priv->area_start + priv->skip_offset,
|
||||||
|
priv->area_size, NULL, &load_addr, &len,
|
||||||
/* read in one block to find the FIT size */
|
&bflow->name);
|
||||||
blknum = offset / desc->blksz;
|
if (ret)
|
||||||
log_debug("read at %lx, blknum %lx\n", offset, blknum);
|
return log_msg_ret("vbe", ret);
|
||||||
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 */
|
/* 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->blk = blk;
|
||||||
bflow->buf = map_sysmem(load_addr, len);
|
bflow->buf = map_sysmem(load_addr, len);
|
||||||
bflow->size = len;
|
bflow->size = len;
|
||||||
@@ -148,16 +77,14 @@ int vbe_simple_read_bootflow_fw(struct udevice *dev, struct bootflow *bflow)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int simple_load_from_image(struct spl_image_info *spl_image,
|
static int simple_load_from_image(struct spl_image_info *image,
|
||||||
struct spl_boot_device *bootdev)
|
struct spl_boot_device *bootdev)
|
||||||
{
|
{
|
||||||
struct udevice *meth, *bdev;
|
|
||||||
struct simple_priv *priv;
|
|
||||||
struct bootflow bflow;
|
|
||||||
struct vbe_handoff *handoff;
|
struct vbe_handoff *handoff;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (xpl_phase() != PHASE_VPL && xpl_phase() != PHASE_SPL)
|
if (xpl_phase() != PHASE_VPL && xpl_phase() != PHASE_SPL &&
|
||||||
|
xpl_phase() != PHASE_TPL)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
ret = bloblist_ensure_size(BLOBLISTT_VBE, sizeof(struct vbe_handoff),
|
ret = bloblist_ensure_size(BLOBLISTT_VBE, sizeof(struct vbe_handoff),
|
||||||
@@ -165,36 +92,64 @@ static int simple_load_from_image(struct spl_image_info *spl_image,
|
|||||||
if (ret)
|
if (ret)
|
||||||
return log_msg_ret("ro", ret);
|
return log_msg_ret("ro", ret);
|
||||||
|
|
||||||
vbe_find_first_device(&meth);
|
if (USE_BOOTMETH) {
|
||||||
if (!meth)
|
struct udevice *meth, *bdev;
|
||||||
return log_msg_ret("vd", -ENODEV);
|
struct simple_priv *priv;
|
||||||
log_debug("vbe dev %s\n", meth->name);
|
struct bootflow bflow;
|
||||||
ret = device_probe(meth);
|
|
||||||
if (ret)
|
|
||||||
return log_msg_ret("probe", ret);
|
|
||||||
|
|
||||||
priv = dev_get_priv(meth);
|
vbe_find_first_device(&meth);
|
||||||
log_debug("simple %s\n", priv->storage);
|
if (!meth)
|
||||||
ret = bootdev_find_by_label(priv->storage, &bdev, NULL);
|
return log_msg_ret("vd", -ENODEV);
|
||||||
if (ret)
|
log_debug("vbe dev %s\n", meth->name);
|
||||||
return log_msg_ret("bd", ret);
|
ret = device_probe(meth);
|
||||||
log_debug("bootdev %s\n", bdev->name);
|
if (ret)
|
||||||
|
return log_msg_ret("probe", ret);
|
||||||
|
|
||||||
bootflow_init(&bflow, bdev, meth);
|
priv = dev_get_priv(meth);
|
||||||
ret = bootmeth_read_bootflow(meth, &bflow);
|
log_debug("simple %s\n", priv->storage);
|
||||||
log_debug("\nfw ret=%d\n", ret);
|
ret = bootdev_find_by_label(priv->storage, &bdev, NULL);
|
||||||
if (ret)
|
if (ret)
|
||||||
return log_msg_ret("rd", ret);
|
return log_msg_ret("bd", ret);
|
||||||
|
log_debug("bootdev %s\n", bdev->name);
|
||||||
|
|
||||||
/* jump to the image */
|
bootflow_init(&bflow, bdev, meth);
|
||||||
spl_image->flags = SPL_SANDBOXF_ARG_IS_BUF;
|
ret = bootmeth_read_bootflow(meth, &bflow);
|
||||||
spl_image->arg = bflow.buf;
|
log_debug("\nfw ret=%d\n", ret);
|
||||||
spl_image->size = bflow.size;
|
if (ret)
|
||||||
log_debug("Image: %s at %p size %x\n", bflow.name, bflow.buf,
|
return log_msg_ret("rd", ret);
|
||||||
bflow.size);
|
|
||||||
|
|
||||||
/* this is not used from now on, so free it */
|
/* jump to the image */
|
||||||
bootflow_free(&bflow);
|
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 */
|
/* Record that VBE was used in this phase */
|
||||||
handoff->phases |= 1 << xpl_phase();
|
handoff->phases |= 1 << xpl_phase();
|
||||||
|
|||||||
@@ -981,6 +981,14 @@ config SPL_NAND_IDENT
|
|||||||
help
|
help
|
||||||
SPL uses the chip ID list to identify the NAND flash.
|
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
|
config SPL_UBI
|
||||||
bool "Support UBI"
|
bool "Support UBI"
|
||||||
help
|
help
|
||||||
|
|||||||
@@ -268,6 +268,14 @@ config TPL_RAM_DEVICE
|
|||||||
be already in memory when TPL takes over, e.g. loaded by the boot
|
be already in memory when TPL takes over, e.g. loaded by the boot
|
||||||
ROM.
|
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
|
config TPL_RTC
|
||||||
bool "Support RTC drivers"
|
bool "Support RTC drivers"
|
||||||
help
|
help
|
||||||
|
|||||||
@@ -181,6 +181,14 @@ config VPL_PCI
|
|||||||
necessary driver support. This enables the drivers in drivers/pci
|
necessary driver support. This enables the drivers in drivers/pci
|
||||||
as part of a VPL build.
|
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
|
config VPL_RTC
|
||||||
bool "Support RTC drivers"
|
bool "Support RTC drivers"
|
||||||
help
|
help
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ obj-$(CONFIG_$(PHASE_)BOOTROM_SUPPORT) += spl_bootrom.o
|
|||||||
obj-$(CONFIG_$(PHASE_)LOAD_FIT) += spl_fit.o
|
obj-$(CONFIG_$(PHASE_)LOAD_FIT) += spl_fit.o
|
||||||
obj-$(CONFIG_$(PHASE_)BLK_FS) += spl_blk_fs.o
|
obj-$(CONFIG_$(PHASE_)BLK_FS) += spl_blk_fs.o
|
||||||
obj-$(CONFIG_$(PHASE_)LEGACY_IMAGE_FORMAT) += spl_legacy.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_)NOR_SUPPORT) += spl_nor.o
|
||||||
obj-$(CONFIG_$(PHASE_)XIP_SUPPORT) += spl_xip.o
|
obj-$(CONFIG_$(PHASE_)XIP_SUPPORT) += spl_xip.o
|
||||||
obj-$(CONFIG_$(PHASE_)YMODEM_SUPPORT) += spl_ymodem.o
|
obj-$(CONFIG_$(PHASE_)YMODEM_SUPPORT) += spl_ymodem.o
|
||||||
|
|||||||
@@ -671,8 +671,7 @@ void board_init_r(gd_t *dummy1, ulong dummy2)
|
|||||||
BOOT_DEVICE_NONE,
|
BOOT_DEVICE_NONE,
|
||||||
BOOT_DEVICE_NONE,
|
BOOT_DEVICE_NONE,
|
||||||
};
|
};
|
||||||
typedef void __noreturn (*jump_to_image_t)(struct spl_image_info *);
|
spl_jump_to_image_t jump_to_image = &jump_to_image_no_args;
|
||||||
jump_to_image_t jump_to_image = &jump_to_image_no_args;
|
|
||||||
struct spl_image_info spl_image;
|
struct spl_image_info spl_image;
|
||||||
int ret, os;
|
int ret, os;
|
||||||
|
|
||||||
@@ -827,6 +826,18 @@ void board_init_r(gd_t *dummy1, ulong dummy2)
|
|||||||
}
|
}
|
||||||
|
|
||||||
spl_board_prepare_for_boot();
|
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);
|
jump_to_image(&spl_image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
183
common/spl/spl_reloc.c
Normal file
183
common/spl/spl_reloc.c
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Google LLC
|
||||||
|
* Written by Simon Glass <sjg@chromium.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
#include <asm/global_data.h>
|
#include <asm/global_data.h>
|
||||||
#include <asm/spl.h>
|
#include <asm/spl.h>
|
||||||
#include <handoff.h>
|
#include <handoff.h>
|
||||||
|
#include <image.h>
|
||||||
#include <mmc.h>
|
#include <mmc.h>
|
||||||
|
|
||||||
struct blk_desc;
|
struct blk_desc;
|
||||||
@@ -265,6 +266,21 @@ enum spl_sandbox_flags {
|
|||||||
SPL_SANDBOXF_ARG_IS_BUF,
|
SPL_SANDBOXF_ARG_IS_BUF,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct spl_image_info - Information about the SPL image being loaded
|
||||||
|
*
|
||||||
|
* @fdt_size: Size of the FDT for the image (0 if none)
|
||||||
|
* @buf: Buffer where the image should be loaded
|
||||||
|
* @fdt_buf: Buffer where the FDT will be copied by spl_reloc_jump(), only used
|
||||||
|
* if @fdt_size is non-zero
|
||||||
|
* @fdt_start: Pointer to the FDT to be copied (must be set up before calling
|
||||||
|
* spl_reloc_jump()
|
||||||
|
* @rcode_buf: Buffer to hold the relocating-jump code
|
||||||
|
* @stack_prot: Pointer to the stack-protection value, used to ensure the stack
|
||||||
|
* does not overflow
|
||||||
|
* @reloc_offset: offset between the relocating-jump code and its place in the
|
||||||
|
* currently running image
|
||||||
|
*/
|
||||||
struct spl_image_info {
|
struct spl_image_info {
|
||||||
const char *name;
|
const char *name;
|
||||||
u8 os;
|
u8 os;
|
||||||
@@ -276,6 +292,7 @@ struct spl_image_info {
|
|||||||
u32 boot_device;
|
u32 boot_device;
|
||||||
u32 offset;
|
u32 offset;
|
||||||
u32 size;
|
u32 size;
|
||||||
|
ulong fdt_size;
|
||||||
u32 flags;
|
u32 flags;
|
||||||
void *arg;
|
void *arg;
|
||||||
#ifdef CONFIG_SPL_LEGACY_IMAGE_CRC_CHECK
|
#ifdef CONFIG_SPL_LEGACY_IMAGE_CRC_CHECK
|
||||||
@@ -283,8 +300,19 @@ struct spl_image_info {
|
|||||||
ulong dcrc_length;
|
ulong dcrc_length;
|
||||||
ulong dcrc;
|
ulong dcrc;
|
||||||
#endif
|
#endif
|
||||||
|
#if CONFIG_IS_ENABLED(RELOC_LOADER)
|
||||||
|
void *buf;
|
||||||
|
void *fdt_buf;
|
||||||
|
void *fdt_start;
|
||||||
|
void *rcode_buf;
|
||||||
|
uint *stack_prot;
|
||||||
|
ulong reloc_offset;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* function to jump to an image from SPL */
|
||||||
|
typedef void __noreturn (*spl_jump_to_image_t)(struct spl_image_info *);
|
||||||
|
|
||||||
static inline void *spl_image_fdt_addr(struct spl_image_info *info)
|
static inline void *spl_image_fdt_addr(struct spl_image_info *info)
|
||||||
{
|
{
|
||||||
#if CONFIG_IS_ENABLED(LOAD_FIT) || CONFIG_IS_ENABLED(LOAD_FIT_FULL)
|
#if CONFIG_IS_ENABLED(LOAD_FIT) || CONFIG_IS_ENABLED(LOAD_FIT_FULL)
|
||||||
@@ -316,12 +344,18 @@ typedef ulong (*spl_load_reader)(struct spl_load_info *load, ulong sector,
|
|||||||
* @read: Function to call to read from the device
|
* @read: Function to call to read from the device
|
||||||
* @priv: Private data for the device
|
* @priv: Private data for the device
|
||||||
* @bl_len: Block length for reading in bytes
|
* @bl_len: Block length for reading in bytes
|
||||||
|
* @phase: Image phase to load
|
||||||
|
* @fit_loaded: true if the FIT has been loaded, except for external data
|
||||||
*/
|
*/
|
||||||
struct spl_load_info {
|
struct spl_load_info {
|
||||||
spl_load_reader read;
|
spl_load_reader read;
|
||||||
void *priv;
|
void *priv;
|
||||||
#if IS_ENABLED(CONFIG_SPL_LOAD_BLOCK)
|
#if IS_ENABLED(CONFIG_SPL_LOAD_BLOCK)
|
||||||
int bl_len;
|
u16 bl_len;
|
||||||
|
#endif
|
||||||
|
#if CONFIG_IS_ENABLED(BOOTMETH_VBE)
|
||||||
|
u8 phase;
|
||||||
|
u8 fit_loaded;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -344,6 +378,32 @@ static inline void spl_set_bl_len(struct spl_load_info *info, int bl_len)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void xpl_set_phase(struct spl_load_info *info,
|
||||||
|
enum image_phase_t phase)
|
||||||
|
{
|
||||||
|
#if CONFIG_IS_ENABLED(BOOTMETH_VBE)
|
||||||
|
info->phase = phase;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline enum image_phase_t xpl_get_phase(struct spl_load_info *info)
|
||||||
|
{
|
||||||
|
#if CONFIG_IS_ENABLED(BOOTMETH_VBE)
|
||||||
|
return info->phase;
|
||||||
|
#else
|
||||||
|
return IH_PHASE_NONE;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool xpl_get_fit_loaded(struct spl_load_info *info)
|
||||||
|
{
|
||||||
|
#if CONFIG_IS_ENABLED(BOOTMETH_VBE)
|
||||||
|
return info->fit_loaded;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* spl_load_init() - Set up a new spl_load_info structure
|
* spl_load_init() - Set up a new spl_load_info structure
|
||||||
*/
|
*/
|
||||||
@@ -354,6 +414,7 @@ static inline void spl_load_init(struct spl_load_info *load,
|
|||||||
load->read = h_read;
|
load->read = h_read;
|
||||||
load->priv = priv;
|
load->priv = priv;
|
||||||
spl_set_bl_len(load, bl_len);
|
spl_set_bl_len(load, bl_len);
|
||||||
|
xpl_set_phase(load, IH_PHASE_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1113,4 +1174,31 @@ int spl_write_upl_handoff(struct spl_image_info *spl_image);
|
|||||||
*/
|
*/
|
||||||
void spl_upl_init(void);
|
void spl_upl_init(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* spl_reloc_prepare() - Prepare the relocating loader ready for use
|
||||||
|
*
|
||||||
|
* Sets up the relocating loader ready for use. This must be called before
|
||||||
|
* spl_reloc_jump() can be used.
|
||||||
|
*
|
||||||
|
* The memory layout is figured out, making use of the space between the top of
|
||||||
|
* the current image and the top of memory.
|
||||||
|
*
|
||||||
|
* Once this is done, the relocating-jump code is copied into place at
|
||||||
|
* image->rcode_buf
|
||||||
|
*
|
||||||
|
* @image: SPL image containing information. This is updated with various
|
||||||
|
* necessary values. On entry, the size and fdt_size fields must be valid
|
||||||
|
* @addrp: Returns the address to which the image should be loaded into memory
|
||||||
|
* Return 0 if OK, -ENOSPC if there is not enough memory available
|
||||||
|
*/
|
||||||
|
int spl_reloc_prepare(struct spl_image_info *image, ulong *addrp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* spl_reloc_jump() - Jump to an image, via a 'relocating-jump' region
|
||||||
|
*
|
||||||
|
* @image: SPL image to jump to
|
||||||
|
* @func: Function to call in the final image
|
||||||
|
*/
|
||||||
|
int spl_reloc_jump(struct spl_image_info *image, spl_jump_to_image_t func);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user