vbe: Handle loading from an unaligned offset

There is no guarantee that an FIT image starts on a block boundary. When
it doesn't, the image starts part-way through the first block.

Add logic to detect this and copy the image down into place.

Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Simon Glass 2025-01-15 18:27:14 -07:00 committed by Tom Rini
parent d337037e1a
commit 36d6c89950

View file

@ -192,32 +192,41 @@ int vbe_read_fit(struct udevice *blk, ulong area_offset, ulong area_size,
log_debug("load_addr %lx len %lx addr %lx aligned_size %lx\n", log_debug("load_addr %lx len %lx addr %lx aligned_size %lx\n",
load_addr, len, addr, aligned_size); load_addr, len, addr, aligned_size);
if (load_addr + len > addr + aligned_size) { if (load_addr + len > addr + aligned_size) {
ulong base, full_size; ulong base, full_size, offset, extra;
void *base_buf; void *base_buf;
/* Find the start address to load from */ /* Find the start address to load from */
base = ALIGN_DOWN(load_addr, desc->blksz); 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 * Get the total number of bytes to load, taking care of
* block alignment * block alignment
*/ */
full_size = load_addr + len - base; full_size = len + extra;
/* /*
* Get the start block number, number of blocks and the address * Get the start block number, number of blocks and the address
* to load to, then load the blocks * to load to, then load the blocks
*/ */
blknum = (area_offset + base - addr) / desc->blksz;
num_blks = DIV_ROUND_UP(full_size, desc->blksz); num_blks = DIV_ROUND_UP(full_size, desc->blksz);
base_buf = map_sysmem(base, full_size); base_buf = map_sysmem(base, full_size);
ret = blk_read(blk, blknum, num_blks, base_buf); ret = blk_read(blk, blknum, num_blks, base_buf);
log_debug("read %lx %lx, %lx blocks to %lx / %p: ret=%d\n", log_debug("read foffset %lx blknum %lx full_size %lx num_blks %lx to %lx / %p: ret=%d\n",
blknum, full_size, num_blks, base, base_buf, ret); offset - 0x8000, blknum, full_size, num_blks, base, base_buf,
ret);
if (ret < 0) if (ret < 0)
return log_msg_ret("rd", ret); return log_msg_ret("rd", ret);
if (ret != num_blks) if (ret != num_blks)
return log_msg_ret("rd", -EIO); 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 (load_addrp) if (load_addrp)
*load_addrp = load_addr; *load_addrp = load_addr;