Coverage Report

Created: 2026-03-11 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/u-boot/boot/vbe_common.c
Line
Count
Source
1
// 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
9
#include <bootstage.h>
10
#include <dm.h>
11
#include <blk.h>
12
#include <image.h>
13
#include <mapmem.h>
14
#include <memalign.h>
15
#include <spl.h>
16
#include <u-boot/crc.h>
17
#include "vbe_common.h"
18
19
binman_sym_declare(ulong, u_boot_vpl_nodtb, size);
20
binman_sym_declare(ulong, u_boot_vpl_bss_pad, size);
21
binman_sym_declare(ulong, u_boot_spl_nodtb, size);
22
binman_sym_declare(ulong, u_boot_spl_bss_pad, size);
23
24
int vbe_get_blk(const char *storage, struct udevice **blkp)
25
0
{
26
0
  struct blk_desc *desc;
27
0
  char devname[16];
28
0
  const char *end;
29
0
  int devnum;
30
31
  /* First figure out the block device */
32
0
  log_debug("storage=%s\n", storage);
33
0
  devnum = trailing_strtoln_end(storage, NULL, &end);
34
0
  if (devnum == -1)
35
0
    return log_msg_ret("num", -ENODEV);
36
0
  if (end - storage >= sizeof(devname))
37
0
    return log_msg_ret("end", -E2BIG);
38
0
  strlcpy(devname, storage, end - storage + 1);
39
0
  log_debug("dev=%s, %x\n", devname, devnum);
40
41
0
  desc = blk_get_dev(devname, devnum);
42
0
  if (!desc)
43
0
    return log_msg_ret("get", -ENXIO);
44
0
  *blkp = desc->bdev;
45
46
0
  return 0;
47
0
}
48
49
int vbe_read_version(struct udevice *blk, ulong offset, char *version,
50
         int max_size)
51
0
{
52
0
  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
0
  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
0
  assert(!(offset & (MMC_MAX_BLOCK_LEN - 1)));
62
63
0
  offset /= MMC_MAX_BLOCK_LEN;
64
65
0
  if (blk_read(blk, offset, 1, buf) != 1)
66
0
    return log_msg_ret("read", -EIO);
67
0
  strlcpy(version, buf, max_size);
68
0
  log_debug("version=%s\n", version);
69
70
0
  return 0;
71
0
}
72
73
int vbe_read_nvdata(struct udevice *blk, ulong offset, ulong size, u8 *buf)
74
0
{
75
0
  uint hdr_ver, hdr_size, data_size, crc;
76
0
  const struct vbe_nvdata *nvd;
77
78
  /* we can use an assert() here since we already read only one block */
79
0
  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
0
  assert(!(offset & (MMC_MAX_BLOCK_LEN - 1)));
90
91
0
  if (offset & (MMC_MAX_BLOCK_LEN - 1))
92
0
    return log_msg_ret("get", -EBADF);
93
0
  offset /= MMC_MAX_BLOCK_LEN;
94
95
0
  if (blk_read(blk, offset, 1, buf) != 1)
96
0
    return log_msg_ret("read", -EIO);
97
0
  nvd = (struct vbe_nvdata *)buf;
98
0
  hdr_ver = (nvd->hdr & NVD_HDR_VER_MASK) >> NVD_HDR_VER_SHIFT;
99
0
  hdr_size = (nvd->hdr & NVD_HDR_SIZE_MASK) >> NVD_HDR_SIZE_SHIFT;
100
0
  if (hdr_ver != NVD_HDR_VER_CUR)
101
0
    return log_msg_ret("hdr", -EPERM);
102
0
  data_size = 1 << hdr_size;
103
0
  if (!data_size || data_size > sizeof(*nvd))
104
0
    return log_msg_ret("sz", -EPERM);
105
106
0
  crc = crc8(0, buf + 1, data_size - 1);
107
0
  if (crc != nvd->crc8)
108
0
    return log_msg_ret("crc", -EPERM);
109
110
0
  return 0;
111
0
}
112
113
/**
114
 * h_vbe_load_read() - Handler for reading an SPL image from a FIT
115
 *
116
 * See spl_load_reader for the definition
117
 */
118
ulong h_vbe_load_read(struct spl_load_info *load, ulong off, ulong size,
119
          void *buf)
120
0
{
121
0
  struct blk_desc *desc = load->priv;
122
0
  lbaint_t sector = off >> desc->log2blksz;
123
0
  lbaint_t count = size >> desc->log2blksz;
124
0
  int ret;
125
126
0
  log_debug("vbe read log2blksz %x offset %lx sector %lx count %lx\n",
127
0
      desc->log2blksz, (ulong)off, (long)sector, (ulong)count);
128
129
0
  ret = blk_dread(desc, sector, count, buf);
130
0
  log_debug("ret=%x\n", ret);
131
0
  if (ret < 0)
132
0
    return ret;
133
134
0
  return ret << desc->log2blksz;
135
0
}
136
137
int vbe_read_fit(struct udevice *blk, ulong area_offset, ulong area_size,
138
     struct spl_image_info *image, ulong *load_addrp, ulong *lenp,
139
     char **namep)
140
0
{
141
0
  ALLOC_CACHE_ALIGN_BUFFER(u8, sbuf, MMC_MAX_BLOCK_LEN);
142
0
  ulong size, blknum, addr, len, load_addr, num_blks, spl_load_addr;
143
0
  ulong aligned_size, fdt_load_addr, fdt_size;
144
0
  const char *fit_uname, *fit_uname_config;
145
0
  struct bootm_headers images = {};
146
0
  enum image_phase_t phase;
147
0
  struct blk_desc *desc;
148
0
  int node, ret;
149
0
  bool for_xpl;
150
0
  void *buf;
151
152
0
  desc = dev_get_uclass_plat(blk);
153
154
  /* read in one block to find the FIT size */
155
0
  blknum =  area_offset / desc->blksz;
156
0
  log_debug("read at %lx, blknum %lx\n", area_offset, blknum);
157
0
  ret = blk_read(blk, blknum, 1, sbuf);
158
0
  if (ret < 0)
159
0
    return log_msg_ret("rd", ret);
160
0
  else if (ret != 1)
161
0
    return log_msg_ret("rd2", -EIO);
162
163
0
  ret = fdt_check_header(sbuf);
164
0
  if (ret < 0)
165
0
    return log_msg_ret("fdt", -EINVAL);
166
0
  size = fdt_totalsize(sbuf);
167
0
  if (size > area_size)
168
0
    return log_msg_ret("fdt", -E2BIG);
169
0
  log_debug("FIT size %lx\n", size);
170
0
  aligned_size = ALIGN(size, desc->blksz);
171
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
   */
176
0
  if (IS_ENABLED(CONFIG_SANDBOX)) {
177
0
    addr = CONFIG_VAL(TEXT_BASE);
178
0
    buf = map_sysmem(addr, size);
179
0
  } else {
180
0
    buf = malloc(aligned_size);
181
0
    if (!buf)
182
0
      return log_msg_ret("fit", -ENOMEM);
183
0
    addr = map_to_sysmem(buf);
184
0
  }
185
0
  num_blks = aligned_size / desc->blksz;
186
0
  log_debug("read %lx, %lx blocks to %lx / %p\n", aligned_size, num_blks,
187
0
      addr, buf);
188
0
  ret = blk_read(blk, blknum, num_blks, buf);
189
0
  if (ret < 0)
190
0
    return log_msg_ret("rd3", ret);
191
0
  else if (ret != num_blks)
192
0
    return log_msg_ret("rd4", -EIO);
193
0
  log_debug("check total size %x off_dt_strings %x\n", fdt_totalsize(buf),
194
0
      fdt_off_dt_strings(buf));
195
196
0
#if CONFIG_IS_ENABLED(SYS_MALLOC_F)
197
0
  log_debug("malloc base %lx ptr %x limit %x top %lx\n",
198
0
      gd->malloc_base, gd->malloc_ptr, gd->malloc_limit,
199
0
      gd->malloc_base + gd->malloc_limit);
200
0
#endif
201
  /* figure out the phase to load */
202
0
  phase = IS_ENABLED(CONFIG_TPL_BUILD) ? IH_PHASE_NONE :
203
0
    IS_ENABLED(CONFIG_VPL_BUILD) ? IH_PHASE_SPL : IH_PHASE_U_BOOT;
204
205
0
  log_debug("loading FIT\n");
206
207
0
  if (xpl_phase() == PHASE_SPL && !IS_ENABLED(CONFIG_SANDBOX)) {
208
0
    struct spl_load_info info;
209
210
0
    spl_load_init(&info, h_vbe_load_read, desc, desc->blksz);
211
0
    xpl_set_fdt_update(&info, false);
212
0
    xpl_set_phase(&info, IH_PHASE_U_BOOT);
213
0
    log_debug("doing SPL from %s blksz %lx log2blksz %x area_offset %lx + fdt_size %lx\n",
214
0
        blk->name, desc->blksz, desc->log2blksz, area_offset, ALIGN(size, 4));
215
0
    ret = spl_load_simple_fit(image, &info, area_offset, buf);
216
0
    log_debug("spl_load_simple_fit() ret=%d\n", ret);
217
218
0
    return ret;
219
0
  }
220
221
  /*
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
0
  fit_uname = NULL;
228
0
  fit_uname_config = NULL;
229
0
  ret = fit_image_load(&images, addr, &fit_uname, &fit_uname_config,
230
0
           IH_ARCH_DEFAULT, image_ph(phase, IH_TYPE_FIRMWARE),
231
0
           BOOTSTAGE_ID_FIT_SPL_START, FIT_LOAD_IGNORED,
232
0
           &load_addr, &len);
233
0
  if (ret == -ENOENT) {
234
0
    ret = fit_image_load(&images, addr, &fit_uname,
235
0
             &fit_uname_config, IH_ARCH_DEFAULT,
236
0
             image_ph(phase, IH_TYPE_LOADABLE),
237
0
             BOOTSTAGE_ID_FIT_SPL_START,
238
0
             FIT_LOAD_IGNORED, &load_addr, &len);
239
0
  }
240
0
  if (ret < 0)
241
0
    return log_msg_ret("ld", ret);
242
0
  node = ret;
243
0
  log_debug("load %lx size %lx\n", load_addr, len);
244
245
0
  fdt_load_addr = 0;
246
0
  fdt_size = 0;
247
0
  if ((xpl_phase() == PHASE_TPL || xpl_phase() == PHASE_VPL) &&
248
0
      !IS_ENABLED(CONFIG_SANDBOX)) {
249
    /* allow use of a different image from the configuration node */
250
0
    fit_uname = NULL;
251
0
    ret = fit_image_load(&images, addr, &fit_uname,
252
0
             &fit_uname_config, IH_ARCH_DEFAULT,
253
0
             image_ph(phase, IH_TYPE_FLATDT),
254
0
             BOOTSTAGE_ID_FIT_SPL_START,
255
0
             FIT_LOAD_IGNORED, &fdt_load_addr,
256
0
             &fdt_size);
257
0
    fdt_size = ALIGN(fdt_size, desc->blksz);
258
0
    log_debug("FDT noload to %lx size %lx\n", fdt_load_addr,
259
0
        fdt_size);
260
0
  }
261
262
0
  for_xpl = !USE_BOOTMETH && CONFIG_IS_ENABLED(RELOC_LOADER);
263
0
  if (for_xpl) {
264
0
    image->size = len;
265
0
    image->fdt_size = fdt_size;
266
0
    ret = spl_reloc_prepare(image, &spl_load_addr);
267
0
    if (ret)
268
0
      return log_msg_ret("spl", ret);
269
0
  }
270
0
  if (!IS_ENABLED(CONFIG_SANDBOX))
271
0
    image->os = IH_OS_U_BOOT;
272
273
  /* For FIT external data, read in the external data */
274
0
  log_debug("load_addr %lx len %lx addr %lx aligned_size %lx\n",
275
0
      load_addr, len, addr, aligned_size);
276
0
  if (load_addr + len > addr + aligned_size) {
277
0
    ulong base, full_size, offset, extra, fdt_base, fdt_full_size;
278
0
    ulong fdt_offset;
279
0
    void *base_buf, *fdt_base_buf;
280
281
    /* Find the start address to load from */
282
0
    base = ALIGN_DOWN(load_addr, desc->blksz);
283
284
0
    offset = area_offset + load_addr - addr;
285
0
    blknum = offset / desc->blksz;
286
0
    extra = offset % desc->blksz;
287
288
    /*
289
     * Get the total number of bytes to load, taking care of
290
     * block alignment
291
     */
292
0
    full_size = len + extra;
293
294
    /*
295
     * Get the start block number, number of blocks and the address
296
     * to load to, then load the blocks
297
     */
298
0
    num_blks = DIV_ROUND_UP(full_size, desc->blksz);
299
0
    if (for_xpl)
300
0
      base = spl_load_addr;
301
0
    base_buf = map_sysmem(base, full_size);
302
0
    ret = blk_read(blk, blknum, num_blks, base_buf);
303
0
    log_debug("read foffset %lx blknum %lx full_size %lx num_blks %lx to %lx / %p: ret=%d\n",
304
0
        offset - 0x8000, blknum, full_size, num_blks, base, base_buf,
305
0
        ret);
306
0
    if (ret < 0)
307
0
      return log_msg_ret("rd", ret);
308
0
    if (ret != num_blks)
309
0
      return log_msg_ret("rd", -EIO);
310
0
    if (extra && !IS_ENABLED(CONFIG_SANDBOX)) {
311
0
      log_debug("move %p %p %lx\n", base_buf,
312
0
          base_buf + extra, len);
313
0
      memmove(base_buf, base_buf + extra, len);
314
0
    }
315
316
0
    if ((xpl_phase() == PHASE_VPL || xpl_phase() == PHASE_TPL) &&
317
0
        !IS_ENABLED(CONFIG_SANDBOX)) {
318
0
      image->load_addr = spl_get_image_text_base();
319
0
      image->entry_point = image->load_addr;
320
0
    }
321
322
    /* now the FDT */
323
0
    if (fdt_size) {
324
0
      fdt_offset = area_offset + fdt_load_addr - addr;
325
0
      blknum = fdt_offset / desc->blksz;
326
0
      extra = fdt_offset % desc->blksz;
327
0
      fdt_full_size = fdt_size + extra;
328
0
      num_blks = DIV_ROUND_UP(fdt_full_size, desc->blksz);
329
0
      fdt_base = ALIGN(base + len, 4);
330
0
      fdt_base_buf = map_sysmem(fdt_base, fdt_size);
331
0
      ret = blk_read(blk, blknum, num_blks, fdt_base_buf);
332
0
      log_debug("fdt read foffset %lx blknum %lx full_size %lx num_blks %lx to %lx / %p: ret=%d\n",
333
0
          fdt_offset - 0x8000, blknum, fdt_full_size, num_blks,
334
0
          fdt_base, fdt_base_buf, ret);
335
0
      if (ret != num_blks)
336
0
        return log_msg_ret("rdf", -EIO);
337
0
      if (extra) {
338
0
        log_debug("move %p %p %lx\n", fdt_base_buf,
339
0
            fdt_base_buf + extra, fdt_size);
340
0
        memmove(fdt_base_buf, fdt_base_buf + extra,
341
0
          fdt_size);
342
0
      }
343
#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
363
0
    }
364
0
  }
365
0
  if (load_addrp)
366
0
    *load_addrp = load_addr;
367
0
  if (lenp)
368
0
    *lenp = len;
369
0
  if (namep) {
370
0
    *namep = strdup(fdt_get_name(buf, node, NULL));
371
0
    if (!namep)
372
0
      return log_msg_ret("nam", -ENOMEM);
373
0
  }
374
375
0
  return 0;
376
0
}
377
378
ofnode vbe_get_node(void)
379
0
{
380
0
  return ofnode_path("/bootstd/firmware0");
381
0
}