blob: a86986d86e9472033fec25a3b0f0955b6fb20af4 [file] [log] [blame]
Simon Glass15d320d2025-01-15 18:27:07 -07001// 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
Simon Glass988f16a2025-01-15 18:27:10 -07009#include <bootstage.h>
10#include <dm.h>
Simon Glass9846e5f2025-01-15 18:27:08 -070011#include <blk.h>
Simon Glass988f16a2025-01-15 18:27:10 -070012#include <image.h>
13#include <mapmem.h>
Simon Glass9846e5f2025-01-15 18:27:08 -070014#include <memalign.h>
15#include <spl.h>
Simon Glass72424b82025-01-15 18:27:09 -070016#include <u-boot/crc.h>
Simon Glass15d320d2025-01-15 18:27:07 -070017#include "vbe_common.h"
18
Simon Glassedd22362025-01-15 18:27:22 -070019binman_sym_declare(ulong, u_boot_vpl_nodtb, size);
20binman_sym_declare(ulong, u_boot_vpl_bss_pad, size);
21binman_sym_declare(ulong, u_boot_spl_nodtb, size);
22binman_sym_declare(ulong, u_boot_spl_bss_pad, size);
23
Simon Glass15d320d2025-01-15 18:27:07 -070024int 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}
Simon Glass9846e5f2025-01-15 18:27:08 -070048
49int 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}
Simon Glass72424b82025-01-15 18:27:09 -070072
73int 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}
Simon Glass988f16a2025-01-15 18:27:10 -0700112
Simon Glassedd22362025-01-15 18:27:22 -0700113/**
114 * h_vbe_load_read() - Handler for reading an SPL image from a FIT
115 *
116 * See spl_load_reader for the definition
117 */
118ulong 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
Simon Glass988f16a2025-01-15 18:27:10 -0700137int vbe_read_fit(struct udevice *blk, ulong area_offset, ulong area_size,
Simon Glassedd22362025-01-15 18:27:22 -0700138 struct spl_image_info *image, ulong *load_addrp, ulong *lenp,
139 char **namep)
Simon Glass988f16a2025-01-15 18:27:10 -0700140{
141 ALLOC_CACHE_ALIGN_BUFFER(u8, sbuf, MMC_MAX_BLOCK_LEN);
Simon Glassedd22362025-01-15 18:27:22 -0700142 ulong size, blknum, addr, len, load_addr, num_blks, spl_load_addr;
Simon Glass14894b92025-01-15 18:27:16 -0700143 ulong aligned_size, fdt_load_addr, fdt_size;
Simon Glass988f16a2025-01-15 18:27:10 -0700144 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;
Simon Glassedd22362025-01-15 18:27:22 -0700149 bool for_xpl;
Simon Glass988f16a2025-01-15 18:27:10 -0700150 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);
Simon Glass4588f952025-01-15 18:27:13 -0700160 else if (ret != 1)
161 return log_msg_ret("rd2", -EIO);
Simon Glass988f16a2025-01-15 18:27:10 -0700162
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);
Simon Glassb6bb70d2025-01-15 18:27:11 -0700170 aligned_size = ALIGN(size, desc->blksz);
Simon Glass988f16a2025-01-15 18:27:10 -0700171
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 */
Simon Glassb6bb70d2025-01-15 18:27:11 -0700176 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);
Simon Glass988f16a2025-01-15 18:27:10 -0700188 ret = blk_read(blk, blknum, num_blks, buf);
189 if (ret < 0)
Simon Glassb6bb70d2025-01-15 18:27:11 -0700190 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));
Simon Glass988f16a2025-01-15 18:27:10 -0700195
Simon Glassb6bb70d2025-01-15 18:27:11 -0700196#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
Simon Glass988f16a2025-01-15 18:27:10 -0700201 /* figure out the phase to load */
Simon Glassedd22362025-01-15 18:27:22 -0700202 phase = IS_ENABLED(CONFIG_TPL_BUILD) ? IH_PHASE_NONE :
203 IS_ENABLED(CONFIG_VPL_BUILD) ? IH_PHASE_SPL : IH_PHASE_U_BOOT;
Simon Glass988f16a2025-01-15 18:27:10 -0700204
Simon Glass988f16a2025-01-15 18:27:10 -0700205 log_debug("loading FIT\n");
Simon Glassedd22362025-01-15 18:27:22 -0700206
207 if (xpl_phase() == PHASE_SPL && !IS_ENABLED(CONFIG_SANDBOX)) {
208 struct spl_load_info info;
209
210 spl_load_init(&info, h_vbe_load_read, desc, desc->blksz);
Simon Glassd4315522025-01-26 11:43:28 -0700211 xpl_set_fdt_update(&info, false);
Simon Glassedd22362025-01-15 18:27:22 -0700212 xpl_set_phase(&info, IH_PHASE_U_BOOT);
213 log_debug("doing SPL from %s blksz %lx log2blksz %x area_offset %lx + fdt_size %lx\n",
214 blk->name, desc->blksz, desc->log2blksz, area_offset, ALIGN(size, 4));
215 ret = spl_load_simple_fit(image, &info, area_offset, buf);
Simon Glassd1ed5842025-01-26 11:43:27 -0700216 log_debug("spl_load_simple_fit() ret=%d\n", ret);
Simon Glassedd22362025-01-15 18:27:22 -0700217
218 return ret;
219 }
220
Simon Glassd1ed5842025-01-26 11:43:27 -0700221 /*
222 * Load the image from the FIT. We ignore any load-address information
223 * so in practice this simply locates the image in the external-data
224 * region and returns its address and size. Since we only loaded the FIT
225 * itself, only a part of the image will be present, at best.
226 */
227 fit_uname = NULL;
228 fit_uname_config = NULL;
Simon Glass988f16a2025-01-15 18:27:10 -0700229 ret = fit_image_load(&images, addr, &fit_uname, &fit_uname_config,
Simon Glass3993bd42025-01-15 18:27:12 -0700230 IH_ARCH_DEFAULT, image_ph(phase, IH_TYPE_FIRMWARE),
Simon Glass988f16a2025-01-15 18:27:10 -0700231 BOOTSTAGE_ID_FIT_SPL_START, FIT_LOAD_IGNORED,
232 &load_addr, &len);
Simon Glass51c91222025-01-15 18:27:15 -0700233 if (ret == -ENOENT) {
234 ret = fit_image_load(&images, addr, &fit_uname,
235 &fit_uname_config, IH_ARCH_DEFAULT,
236 image_ph(phase, IH_TYPE_LOADABLE),
237 BOOTSTAGE_ID_FIT_SPL_START,
238 FIT_LOAD_IGNORED, &load_addr, &len);
239 }
Simon Glass988f16a2025-01-15 18:27:10 -0700240 if (ret < 0)
241 return log_msg_ret("ld", ret);
242 node = ret;
Simon Glass0fcc6c42025-01-15 18:27:23 -0700243 log_debug("load %lx size %lx\n", load_addr, len);
Simon Glass988f16a2025-01-15 18:27:10 -0700244
Simon Glass14894b92025-01-15 18:27:16 -0700245 fdt_load_addr = 0;
246 fdt_size = 0;
Simon Glassedd22362025-01-15 18:27:22 -0700247 if ((xpl_phase() == PHASE_TPL || xpl_phase() == PHASE_VPL) &&
248 !IS_ENABLED(CONFIG_SANDBOX)) {
249 /* allow use of a different image from the configuration node */
250 fit_uname = NULL;
251 ret = fit_image_load(&images, addr, &fit_uname,
252 &fit_uname_config, IH_ARCH_DEFAULT,
253 image_ph(phase, IH_TYPE_FLATDT),
254 BOOTSTAGE_ID_FIT_SPL_START,
255 FIT_LOAD_IGNORED, &fdt_load_addr,
256 &fdt_size);
257 fdt_size = ALIGN(fdt_size, desc->blksz);
258 log_debug("FDT noload to %lx size %lx\n", fdt_load_addr,
259 fdt_size);
260 }
261
262 for_xpl = !USE_BOOTMETH && CONFIG_IS_ENABLED(RELOC_LOADER);
263 if (for_xpl) {
264 image->size = len;
265 image->fdt_size = fdt_size;
266 ret = spl_reloc_prepare(image, &spl_load_addr);
267 if (ret)
268 return log_msg_ret("spl", ret);
269 }
270 if (!IS_ENABLED(CONFIG_SANDBOX))
271 image->os = IH_OS_U_BOOT;
Simon Glass14894b92025-01-15 18:27:16 -0700272
Simon Glass988f16a2025-01-15 18:27:10 -0700273 /* For FIT external data, read in the external data */
Simon Glassb6bb70d2025-01-15 18:27:11 -0700274 log_debug("load_addr %lx len %lx addr %lx aligned_size %lx\n",
275 load_addr, len, addr, aligned_size);
276 if (load_addr + len > addr + aligned_size) {
Simon Glass14894b92025-01-15 18:27:16 -0700277 ulong base, full_size, offset, extra, fdt_base, fdt_full_size;
278 ulong fdt_offset;
279 void *base_buf, *fdt_base_buf;
Simon Glass988f16a2025-01-15 18:27:10 -0700280
281 /* Find the start address to load from */
282 base = ALIGN_DOWN(load_addr, desc->blksz);
283
Simon Glasse36d01a2025-01-15 18:27:14 -0700284 offset = area_offset + load_addr - addr;
285 blknum = offset / desc->blksz;
286 extra = offset % desc->blksz;
287
Simon Glass988f16a2025-01-15 18:27:10 -0700288 /*
289 * Get the total number of bytes to load, taking care of
290 * block alignment
291 */
Simon Glasse36d01a2025-01-15 18:27:14 -0700292 full_size = len + extra;
Simon Glass988f16a2025-01-15 18:27:10 -0700293
294 /*
295 * Get the start block number, number of blocks and the address
296 * to load to, then load the blocks
297 */
Simon Glass988f16a2025-01-15 18:27:10 -0700298 num_blks = DIV_ROUND_UP(full_size, desc->blksz);
Simon Glassedd22362025-01-15 18:27:22 -0700299 if (for_xpl)
300 base = spl_load_addr;
Simon Glass988f16a2025-01-15 18:27:10 -0700301 base_buf = map_sysmem(base, full_size);
302 ret = blk_read(blk, blknum, num_blks, base_buf);
Simon Glasse36d01a2025-01-15 18:27:14 -0700303 log_debug("read foffset %lx blknum %lx full_size %lx num_blks %lx to %lx / %p: ret=%d\n",
304 offset - 0x8000, blknum, full_size, num_blks, base, base_buf,
305 ret);
Simon Glass988f16a2025-01-15 18:27:10 -0700306 if (ret < 0)
307 return log_msg_ret("rd", ret);
Simon Glass4588f952025-01-15 18:27:13 -0700308 if (ret != num_blks)
309 return log_msg_ret("rd", -EIO);
Simon Glasse36d01a2025-01-15 18:27:14 -0700310 if (extra && !IS_ENABLED(CONFIG_SANDBOX)) {
311 log_debug("move %p %p %lx\n", base_buf,
312 base_buf + extra, len);
313 memmove(base_buf, base_buf + extra, len);
314 }
Simon Glass14894b92025-01-15 18:27:16 -0700315
Simon Glassedd22362025-01-15 18:27:22 -0700316 if ((xpl_phase() == PHASE_VPL || xpl_phase() == PHASE_TPL) &&
317 !IS_ENABLED(CONFIG_SANDBOX)) {
318 image->load_addr = spl_get_image_text_base();
319 image->entry_point = image->load_addr;
320 }
321
Simon Glass14894b92025-01-15 18:27:16 -0700322 /* now the FDT */
323 if (fdt_size) {
324 fdt_offset = area_offset + fdt_load_addr - addr;
325 blknum = fdt_offset / desc->blksz;
326 extra = fdt_offset % desc->blksz;
327 fdt_full_size = fdt_size + extra;
328 num_blks = DIV_ROUND_UP(fdt_full_size, desc->blksz);
329 fdt_base = ALIGN(base + len, 4);
330 fdt_base_buf = map_sysmem(fdt_base, fdt_size);
331 ret = blk_read(blk, blknum, num_blks, fdt_base_buf);
332 log_debug("fdt read foffset %lx blknum %lx full_size %lx num_blks %lx to %lx / %p: ret=%d\n",
333 fdt_offset - 0x8000, blknum, fdt_full_size, num_blks,
334 fdt_base, fdt_base_buf, ret);
335 if (ret != num_blks)
336 return log_msg_ret("rdf", -EIO);
337 if (extra) {
338 log_debug("move %p %p %lx\n", fdt_base_buf,
339 fdt_base_buf + extra, fdt_size);
340 memmove(fdt_base_buf, fdt_base_buf + extra,
341 fdt_size);
342 }
Simon Glass7d243eb2025-01-15 18:27:21 -0700343#if CONFIG_IS_ENABLED(RELOC_LOADER)
344 image->fdt_buf = fdt_base_buf;
345
346 ulong xpl_size;
347 ulong xpl_pad;
348 ulong fdt_start;
349
350 if (xpl_phase() == PHASE_TPL) {
351 xpl_size = binman_sym(ulong, u_boot_vpl_nodtb, size);
352 xpl_pad = binman_sym(ulong, u_boot_vpl_bss_pad, size);
353 } else {
354 xpl_size = binman_sym(ulong, u_boot_spl_nodtb, size);
355 xpl_pad = binman_sym(ulong, u_boot_spl_bss_pad, size);
356 }
357 fdt_start = image->load_addr + xpl_size + xpl_pad;
358 log_debug("load_addr %lx xpl_size %lx copy-to %lx\n",
359 image->load_addr, xpl_size + xpl_pad,
360 fdt_start);
361 image->fdt_start = map_sysmem(fdt_start, fdt_size);
362#endif
Simon Glass14894b92025-01-15 18:27:16 -0700363 }
Simon Glass988f16a2025-01-15 18:27:10 -0700364 }
365 if (load_addrp)
366 *load_addrp = load_addr;
367 if (lenp)
368 *lenp = len;
369 if (namep) {
370 *namep = strdup(fdt_get_name(buf, node, NULL));
371 if (!namep)
372 return log_msg_ret("nam", -ENOMEM);
373 }
374
375 return 0;
376}
Simon Glassc2213082025-01-26 11:43:29 -0700377
378ofnode vbe_get_node(void)
379{
380 return ofnode_path("/bootstd/firmware0");
381}