blob: 0105d550bdf7b4645aec7ce2c37bb37a2346d21c [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;
117 int node, ret;
118 void *buf;
119
120 desc = dev_get_uclass_plat(blk);
121
122 /* read in one block to find the FIT size */
123 blknum = area_offset / desc->blksz;
124 log_debug("read at %lx, blknum %lx\n", area_offset, blknum);
125 ret = blk_read(blk, blknum, 1, sbuf);
126 if (ret < 0)
127 return log_msg_ret("rd", ret);
128
129 ret = fdt_check_header(sbuf);
130 if (ret < 0)
131 return log_msg_ret("fdt", -EINVAL);
132 size = fdt_totalsize(sbuf);
133 if (size > area_size)
134 return log_msg_ret("fdt", -E2BIG);
135 log_debug("FIT size %lx\n", size);
136
137 /*
138 * Load the FIT into the SPL memory. This is typically a FIT with
139 * external data, so this is quite small, perhaps a few KB.
140 */
141 addr = CONFIG_VAL(TEXT_BASE);
142 buf = map_sysmem(addr, size);
143 num_blks = DIV_ROUND_UP(size, desc->blksz);
144 log_debug("read %lx, %lx blocks to %lx / %p\n", size, num_blks, addr,
145 buf);
146 ret = blk_read(blk, blknum, num_blks, buf);
147 if (ret < 0)
148 return log_msg_ret("rd", ret);
149
150 /* figure out the phase to load */
151 phase = IS_ENABLED(CONFIG_VPL_BUILD) ? IH_PHASE_SPL : IH_PHASE_U_BOOT;
152
153 /*
154 * Load the image from the FIT. We ignore any load-address information
155 * so in practice this simply locates the image in the external-data
156 * region and returns its address and size. Since we only loaded the FIT
157 * itself, only a part of the image will be present, at best.
158 */
159 fit_uname = NULL;
160 fit_uname_config = NULL;
161 log_debug("loading FIT\n");
162 ret = fit_image_load(&images, addr, &fit_uname, &fit_uname_config,
163 IH_ARCH_SANDBOX, image_ph(phase, IH_TYPE_FIRMWARE),
164 BOOTSTAGE_ID_FIT_SPL_START, FIT_LOAD_IGNORED,
165 &load_addr, &len);
166 if (ret < 0)
167 return log_msg_ret("ld", ret);
168 node = ret;
169 log_debug("loaded to %lx\n", load_addr);
170
171 /* For FIT external data, read in the external data */
172 if (load_addr + len > addr + size) {
173 ulong base, full_size;
174 void *base_buf;
175
176 /* Find the start address to load from */
177 base = ALIGN_DOWN(load_addr, desc->blksz);
178
179 /*
180 * Get the total number of bytes to load, taking care of
181 * block alignment
182 */
183 full_size = load_addr + len - base;
184
185 /*
186 * Get the start block number, number of blocks and the address
187 * to load to, then load the blocks
188 */
189 blknum = (area_offset + base - addr) / desc->blksz;
190 num_blks = DIV_ROUND_UP(full_size, desc->blksz);
191 base_buf = map_sysmem(base, full_size);
192 ret = blk_read(blk, blknum, num_blks, base_buf);
193 log_debug("read %lx %lx, %lx blocks to %lx / %p: ret=%d\n",
194 blknum, full_size, num_blks, base, base_buf, ret);
195 if (ret < 0)
196 return log_msg_ret("rd", ret);
197 }
198 if (load_addrp)
199 *load_addrp = load_addr;
200 if (lenp)
201 *lenp = len;
202 if (namep) {
203 *namep = strdup(fdt_get_name(buf, node, NULL));
204 if (!namep)
205 return log_msg_ret("nam", -ENOMEM);
206 }
207
208 return 0;
209}