blob: ecf4ad916e04fbbb93c905770313fbfa608af687 [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
19int vbe_get_blk(const char *storage, struct udevice **blkp)
20{
21 struct blk_desc *desc;
22 char devname[16];
23 const char *end;
24 int devnum;
25
26 /* First figure out the block device */
27 log_debug("storage=%s\n", storage);
28 devnum = trailing_strtoln_end(storage, NULL, &end);
29 if (devnum == -1)
30 return log_msg_ret("num", -ENODEV);
31 if (end - storage >= sizeof(devname))
32 return log_msg_ret("end", -E2BIG);
33 strlcpy(devname, storage, end - storage + 1);
34 log_debug("dev=%s, %x\n", devname, devnum);
35
36 desc = blk_get_dev(devname, devnum);
37 if (!desc)
38 return log_msg_ret("get", -ENXIO);
39 *blkp = desc->bdev;
40
41 return 0;
42}
Simon Glass9846e5f2025-01-15 18:27:08 -070043
44int vbe_read_version(struct udevice *blk, ulong offset, char *version,
45 int max_size)
46{
47 ALLOC_CACHE_ALIGN_BUFFER(u8, buf, MMC_MAX_BLOCK_LEN);
48
49 /* we can use an assert() here since we already read only one block */
50 assert(max_size <= MMC_MAX_BLOCK_LEN);
51
52 /*
53 * we can use an assert() here since reading the wrong block will just
54 * cause an invalid version-string to be (safely) read
55 */
56 assert(!(offset & (MMC_MAX_BLOCK_LEN - 1)));
57
58 offset /= MMC_MAX_BLOCK_LEN;
59
60 if (blk_read(blk, offset, 1, buf) != 1)
61 return log_msg_ret("read", -EIO);
62 strlcpy(version, buf, max_size);
63 log_debug("version=%s\n", version);
64
65 return 0;
66}
Simon Glass72424b82025-01-15 18:27:09 -070067
68int vbe_read_nvdata(struct udevice *blk, ulong offset, ulong size, u8 *buf)
69{
70 uint hdr_ver, hdr_size, data_size, crc;
71 const struct vbe_nvdata *nvd;
72
73 /* we can use an assert() here since we already read only one block */
74 assert(size <= MMC_MAX_BLOCK_LEN);
75
76 /*
77 * We can use an assert() here since reading the wrong block will just
78 * cause invalid state to be (safely) read. If the crc passes, then we
79 * obtain invalid state and it will likely cause booting to fail.
80 *
81 * VBE relies on valid values being in U-Boot's devicetree, so this
82 * should not every be wrong on a production device.
83 */
84 assert(!(offset & (MMC_MAX_BLOCK_LEN - 1)));
85
86 if (offset & (MMC_MAX_BLOCK_LEN - 1))
87 return log_msg_ret("get", -EBADF);
88 offset /= MMC_MAX_BLOCK_LEN;
89
90 if (blk_read(blk, offset, 1, buf) != 1)
91 return log_msg_ret("read", -EIO);
92 nvd = (struct vbe_nvdata *)buf;
93 hdr_ver = (nvd->hdr & NVD_HDR_VER_MASK) >> NVD_HDR_VER_SHIFT;
94 hdr_size = (nvd->hdr & NVD_HDR_SIZE_MASK) >> NVD_HDR_SIZE_SHIFT;
95 if (hdr_ver != NVD_HDR_VER_CUR)
96 return log_msg_ret("hdr", -EPERM);
97 data_size = 1 << hdr_size;
98 if (!data_size || data_size > sizeof(*nvd))
99 return log_msg_ret("sz", -EPERM);
100
101 crc = crc8(0, buf + 1, data_size - 1);
102 if (crc != nvd->crc8)
103 return log_msg_ret("crc", -EPERM);
104
105 return 0;
106}
Simon Glass988f16a2025-01-15 18:27:10 -0700107
108int vbe_read_fit(struct udevice *blk, ulong area_offset, ulong area_size,
109 ulong *load_addrp, ulong *lenp, char **namep)
110{
111 ALLOC_CACHE_ALIGN_BUFFER(u8, sbuf, MMC_MAX_BLOCK_LEN);
112 ulong size, blknum, addr, len, load_addr, num_blks;
113 const char *fit_uname, *fit_uname_config;
114 struct bootm_headers images = {};
115 enum image_phase_t phase;
116 struct blk_desc *desc;
Simon Glassb6bb70d2025-01-15 18:27:11 -0700117 ulong aligned_size;
Simon Glass988f16a2025-01-15 18:27:10 -0700118 int node, ret;
119 void *buf;
120
121 desc = dev_get_uclass_plat(blk);
122
123 /* read in one block to find the FIT size */
124 blknum = area_offset / desc->blksz;
125 log_debug("read at %lx, blknum %lx\n", area_offset, blknum);
126 ret = blk_read(blk, blknum, 1, sbuf);
127 if (ret < 0)
128 return log_msg_ret("rd", ret);
129
130 ret = fdt_check_header(sbuf);
131 if (ret < 0)
132 return log_msg_ret("fdt", -EINVAL);
133 size = fdt_totalsize(sbuf);
134 if (size > area_size)
135 return log_msg_ret("fdt", -E2BIG);
136 log_debug("FIT size %lx\n", size);
Simon Glassb6bb70d2025-01-15 18:27:11 -0700137 aligned_size = ALIGN(size, desc->blksz);
Simon Glass988f16a2025-01-15 18:27:10 -0700138
139 /*
140 * Load the FIT into the SPL memory. This is typically a FIT with
141 * external data, so this is quite small, perhaps a few KB.
142 */
Simon Glassb6bb70d2025-01-15 18:27:11 -0700143 if (IS_ENABLED(CONFIG_SANDBOX)) {
144 addr = CONFIG_VAL(TEXT_BASE);
145 buf = map_sysmem(addr, size);
146 } else {
147 buf = malloc(aligned_size);
148 if (!buf)
149 return log_msg_ret("fit", -ENOMEM);
150 addr = map_to_sysmem(buf);
151 }
152 num_blks = aligned_size / desc->blksz;
153 log_debug("read %lx, %lx blocks to %lx / %p\n", aligned_size, num_blks,
154 addr, buf);
Simon Glass988f16a2025-01-15 18:27:10 -0700155 ret = blk_read(blk, blknum, num_blks, buf);
156 if (ret < 0)
Simon Glassb6bb70d2025-01-15 18:27:11 -0700157 return log_msg_ret("rd3", ret);
158 else if (ret != num_blks)
159 return log_msg_ret("rd4", -EIO);
160 log_debug("check total size %x off_dt_strings %x\n", fdt_totalsize(buf),
161 fdt_off_dt_strings(buf));
Simon Glass988f16a2025-01-15 18:27:10 -0700162
Simon Glassb6bb70d2025-01-15 18:27:11 -0700163#if CONFIG_IS_ENABLED(SYS_MALLOC_F)
164 log_debug("malloc base %lx ptr %x limit %x top %lx\n",
165 gd->malloc_base, gd->malloc_ptr, gd->malloc_limit,
166 gd->malloc_base + gd->malloc_limit);
167#endif
Simon Glass988f16a2025-01-15 18:27:10 -0700168 /* figure out the phase to load */
169 phase = IS_ENABLED(CONFIG_VPL_BUILD) ? IH_PHASE_SPL : IH_PHASE_U_BOOT;
170
171 /*
172 * Load the image from the FIT. We ignore any load-address information
173 * so in practice this simply locates the image in the external-data
174 * region and returns its address and size. Since we only loaded the FIT
175 * itself, only a part of the image will be present, at best.
176 */
177 fit_uname = NULL;
178 fit_uname_config = NULL;
179 log_debug("loading FIT\n");
180 ret = fit_image_load(&images, addr, &fit_uname, &fit_uname_config,
Simon Glass3993bd42025-01-15 18:27:12 -0700181 IH_ARCH_DEFAULT, image_ph(phase, IH_TYPE_FIRMWARE),
Simon Glass988f16a2025-01-15 18:27:10 -0700182 BOOTSTAGE_ID_FIT_SPL_START, FIT_LOAD_IGNORED,
183 &load_addr, &len);
184 if (ret < 0)
185 return log_msg_ret("ld", ret);
186 node = ret;
187 log_debug("loaded to %lx\n", load_addr);
188
189 /* For FIT external data, read in the external data */
Simon Glassb6bb70d2025-01-15 18:27:11 -0700190 log_debug("load_addr %lx len %lx addr %lx aligned_size %lx\n",
191 load_addr, len, addr, aligned_size);
192 if (load_addr + len > addr + aligned_size) {
Simon Glass988f16a2025-01-15 18:27:10 -0700193 ulong base, full_size;
194 void *base_buf;
195
196 /* Find the start address to load from */
197 base = ALIGN_DOWN(load_addr, desc->blksz);
198
199 /*
200 * Get the total number of bytes to load, taking care of
201 * block alignment
202 */
203 full_size = load_addr + len - base;
204
205 /*
206 * Get the start block number, number of blocks and the address
207 * to load to, then load the blocks
208 */
209 blknum = (area_offset + base - addr) / desc->blksz;
210 num_blks = DIV_ROUND_UP(full_size, desc->blksz);
211 base_buf = map_sysmem(base, full_size);
212 ret = blk_read(blk, blknum, num_blks, base_buf);
213 log_debug("read %lx %lx, %lx blocks to %lx / %p: ret=%d\n",
214 blknum, full_size, num_blks, base, base_buf, ret);
215 if (ret < 0)
216 return log_msg_ret("rd", ret);
217 }
218 if (load_addrp)
219 *load_addrp = load_addr;
220 if (lenp)
221 *lenp = len;
222 if (namep) {
223 *namep = strdup(fdt_get_name(buf, node, NULL));
224 if (!namep)
225 return log_msg_ret("nam", -ENOMEM);
226 }
227
228 return 0;
229}