blob: d52bd9ddff013280d4dbbe6ef5d0e14d355b9d49 [file] [log] [blame]
Simon Glassc2213082025-01-26 11:43:29 -07001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Verified Boot for Embedded (VBE) loading firmware phases
4 *
5 * Copyright 2022 Google LLC
6 * Written by Simon Glass <sjg@chromium.org>
7 */
8
9#define LOG_CATEGORY LOGC_BOOT
10
11#include <binman_sym.h>
12#include <bloblist.h>
13#include <bootdev.h>
14#include <bootflow.h>
15#include <bootmeth.h>
16#include <bootstage.h>
17#include <display_options.h>
18#include <dm.h>
19#include <image.h>
20#include <log.h>
21#include <mapmem.h>
22#include <memalign.h>
23#include <mmc.h>
24#include <spl.h>
25#include <vbe.h>
26#include <dm/device-internal.h>
27#include "vbe_abrec.h"
28#include "vbe_common.h"
29
30binman_sym_declare(ulong, spl_a, image_pos);
31binman_sym_declare(ulong, spl_b, image_pos);
32binman_sym_declare(ulong, spl_recovery, image_pos);
33
34binman_sym_declare(ulong, spl_a, size);
35binman_sym_declare(ulong, spl_b, size);
36binman_sym_declare(ulong, spl_recovery, size);
37
38binman_sym_declare(ulong, u_boot_a, image_pos);
39binman_sym_declare(ulong, u_boot_b, image_pos);
40binman_sym_declare(ulong, u_boot_recovery, image_pos);
41
42binman_sym_declare(ulong, u_boot_a, size);
43binman_sym_declare(ulong, u_boot_b, size);
44binman_sym_declare(ulong, u_boot_recovery, size);
45
46binman_sym_declare(ulong, vpl, image_pos);
47binman_sym_declare(ulong, vpl, size);
48
49static const char *const pick_names[] = {"A", "B", "Recovery"};
50
51/**
52 * abrec_read_bootflow_fw() - Create a bootflow for firmware
53 *
54 * Locates and loads the firmware image (FIT) needed for the next phase. The FIT
55 * should ideally use external data, to reduce the amount of it that needs to be
56 * read.
57 *
58 * @bdev: bootdev device containing the firmwre
59 * @meth: VBE abrec bootmeth
60 * @blow: Place to put the created bootflow, on success
61 * @return 0 if OK, -ve on error
62 */
63int abrec_read_bootflow_fw(struct udevice *dev, struct bootflow *bflow)
64{
65 struct udevice *media = dev_get_parent(bflow->dev);
66 struct udevice *meth = bflow->method;
67 struct abrec_priv *priv = dev_get_priv(meth);
68 ulong len, load_addr;
69 struct udevice *blk;
70 int ret;
71
72 log_debug("media=%s\n", media->name);
73 ret = blk_get_from_parent(media, &blk);
74 if (ret)
75 return log_msg_ret("med", ret);
76
77 ret = vbe_read_fit(blk, priv->area_start + priv->skip_offset,
78 priv->area_size, NULL, &load_addr, &len, &bflow->name);
79 if (ret)
80 return log_msg_ret("vbe", ret);
81
82 /* set up the bootflow with the info we obtained */
83 bflow->blk = blk;
84 bflow->buf = map_sysmem(load_addr, len);
85 bflow->size = len;
86
87 return 0;
88}
89
90static int abrec_run_vpl(struct udevice *blk, struct spl_image_info *image,
91 struct vbe_handoff *handoff)
92{
93 uint flags, tries, prev_result;
94 struct abrec_priv priv;
95 struct abrec_state state;
96 enum vbe_pick_t pick;
97 uint try_count;
98 ulong offset, size;
99 ulong ub_offset, ub_size;
100 ofnode node;
101 int ret;
102
103 node = vbe_get_node();
104 if (!ofnode_valid(node))
105 return log_msg_ret("nod", -EINVAL);
106
107 ret = abrec_read_priv(node, &priv);
108 if (ret)
109 return log_msg_ret("pri", ret);
110
111 ret = abrec_read_nvdata(&priv, blk, &state);
112 if (ret)
113 return log_msg_ret("sta", ret);
114
115 prev_result = state.try_result;
116 try_count = state.try_count;
117
118 if (state.recovery) {
119 pick = VBEP_RECOVERY;
120
121 /* if we are trying B but ran out of tries, use A */
122 } else if ((prev_result == VBETR_TRYING) && !tries) {
123 pick = VBEP_A;
124 state.try_result = VBETR_BAD;
125
126 /* if requested, try B */
127 } else if (flags & VBEF_TRY_B) {
128 pick = VBEP_B;
129
130 /* decrement the try count if not already zero */
131 if (try_count)
132 try_count--;
133 state.try_result = VBETR_TRYING;
134 } else {
135 pick = VBEP_A;
136 }
137 state.try_count = try_count;
138
139 switch (pick) {
140 case VBEP_A:
141 offset = binman_sym(ulong, spl_a, image_pos);
142 size = binman_sym(ulong, spl_a, size);
143 ub_offset = binman_sym(ulong, u_boot_a, image_pos);
144 ub_size = binman_sym(ulong, u_boot_a, size);
145 break;
146 case VBEP_B:
147 offset = binman_sym(ulong, spl_b, image_pos);
148 size = binman_sym(ulong, spl_b, size);
149 ub_offset = binman_sym(ulong, u_boot_b, image_pos);
150 ub_size = binman_sym(ulong, u_boot_b, size);
151 break;
152 case VBEP_RECOVERY:
153 offset = binman_sym(ulong, spl_recovery, image_pos);
154 size = binman_sym(ulong, spl_recovery, size);
155 ub_offset = binman_sym(ulong, u_boot_recovery, image_pos);
156 ub_size = binman_sym(ulong, u_boot_recovery, size);
157 break;
158 }
159 log_debug("pick=%d, offset=%lx size=%lx\n", pick, offset, size);
160 log_info("VBE: Firmware pick %s at %lx\n", pick_names[pick], offset);
161
162 ret = vbe_read_fit(blk, offset, size, image, NULL, NULL, NULL);
163 if (ret)
164 return log_msg_ret("vbe", ret);
165 handoff->offset = ub_offset;
166 handoff->size = ub_size;
167 handoff->pick = pick;
168 image->load_addr = spl_get_image_text_base();
169 image->entry_point = image->load_addr;
170
171 return 0;
172}
173
174static int abrec_run_spl(struct udevice *blk, struct spl_image_info *image,
175 struct vbe_handoff *handoff)
176{
177 int ret;
178
179 log_info("VBE: Firmware pick %s at %lx\n", pick_names[handoff->pick],
180 handoff->offset);
181 ret = vbe_read_fit(blk, handoff->offset, handoff->size, image, NULL,
182 NULL, NULL);
183 if (ret)
184 return log_msg_ret("vbe", ret);
185 image->load_addr = spl_get_image_text_base();
186 image->entry_point = image->load_addr;
187
188 return 0;
189}
190
191static int abrec_load_from_image(struct spl_image_info *image,
192 struct spl_boot_device *bootdev)
193{
194 struct vbe_handoff *handoff;
195 int ret;
196
197 printf("load: %s\n", ofnode_read_string(ofnode_root(), "model"));
198 if (xpl_phase() != PHASE_VPL && xpl_phase() != PHASE_SPL &&
199 xpl_phase() != PHASE_TPL)
200 return -ENOENT;
201
202 ret = bloblist_ensure_size(BLOBLISTT_VBE, sizeof(struct vbe_handoff),
203 0, (void **)&handoff);
204 if (ret)
205 return log_msg_ret("ro", ret);
206
207 if (USE_BOOTMETH) {
208 struct udevice *meth, *bdev;
209 struct abrec_priv *priv;
210 struct bootflow bflow;
211
212 vbe_find_first_device(&meth);
213 if (!meth)
214 return log_msg_ret("vd", -ENODEV);
215 log_debug("vbe dev %s\n", meth->name);
216 ret = device_probe(meth);
217 if (ret)
218 return log_msg_ret("probe", ret);
219
220 priv = dev_get_priv(meth);
221 log_debug("abrec %s\n", priv->storage);
222 ret = bootdev_find_by_label(priv->storage, &bdev, NULL);
223 if (ret)
224 return log_msg_ret("bd", ret);
225 log_debug("bootdev %s\n", bdev->name);
226
227 bootflow_init(&bflow, bdev, meth);
228 ret = bootmeth_read_bootflow(meth, &bflow);
229 log_debug("\nfw ret=%d\n", ret);
230 if (ret)
231 return log_msg_ret("rd", ret);
232
233 /* jump to the image */
234 image->flags = SPL_SANDBOXF_ARG_IS_BUF;
235 image->arg = bflow.buf;
236 image->size = bflow.size;
237 log_debug("Image: %s at %p size %x\n", bflow.name, bflow.buf,
238 bflow.size);
239
240 /* this is not used from now on, so free it */
241 bootflow_free(&bflow);
242 } else {
243 struct udevice *media;
244 struct udevice *blk;
245
246 ret = uclass_get_device_by_seq(UCLASS_MMC, 1, &media);
247 if (ret)
248 return log_msg_ret("vdv", ret);
249 ret = blk_get_from_parent(media, &blk);
250 if (ret)
251 return log_msg_ret("med", ret);
252
253 if (xpl_phase() == PHASE_TPL) {
254 ulong offset, size;
255
256 offset = binman_sym(ulong, vpl, image_pos);
257 size = binman_sym(ulong, vpl, size);
258 log_debug("VPL at offset %lx size %lx\n", offset, size);
259 ret = vbe_read_fit(blk, offset, size, image, NULL,
260 NULL, NULL);
261 if (ret)
262 return log_msg_ret("vbe", ret);
263 } else if (xpl_phase() == PHASE_VPL) {
264 ret = abrec_run_vpl(blk, image, handoff);
265 } else {
266 ret = abrec_run_spl(blk, image, handoff);
267 }
268 }
269
270 /* Record that VBE was used in this phase */
271 handoff->phases |= 1 << xpl_phase();
272
273 return 0;
274}
275SPL_LOAD_IMAGE_METHOD("vbe_abrec", 5, BOOT_DEVICE_VBE,
276 abrec_load_from_image);