]>
Commit | Line | Data |
---|---|---|
a8f5be17 SG |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Copyright 2021 Google LLC | |
4 | * Written by Simon Glass <[email protected]> | |
5 | */ | |
6 | ||
7 | #define LOG_CATEGORY UCLASS_BOOTSTD | |
8 | ||
9 | #include <common.h> | |
10 | #include <bootdev.h> | |
11 | #include <bootflow.h> | |
12 | #include <bootmeth.h> | |
13 | #include <bootstd.h> | |
14 | #include <dm.h> | |
d42243fe | 15 | #include <env_internal.h> |
a8f5be17 SG |
16 | #include <malloc.h> |
17 | #include <dm/device-internal.h> | |
18 | #include <dm/uclass-internal.h> | |
19 | ||
20 | /* error codes used to signal running out of things */ | |
21 | enum { | |
22 | BF_NO_MORE_PARTS = -ESHUTDOWN, | |
23 | BF_NO_MORE_DEVICES = -ENODEV, | |
24 | }; | |
25 | ||
26 | /** | |
27 | * bootflow_state - name for each state | |
28 | * | |
29 | * See enum bootflow_state_t for what each of these means | |
30 | */ | |
31 | static const char *const bootflow_state[BOOTFLOWST_COUNT] = { | |
32 | "base", | |
33 | "media", | |
34 | "part", | |
35 | "fs", | |
36 | "file", | |
37 | "ready", | |
38 | }; | |
39 | ||
40 | const char *bootflow_state_get_name(enum bootflow_state_t state) | |
41 | { | |
42 | /* This doesn't need to be a useful name, since it will never occur */ | |
43 | if (state < 0 || state >= BOOTFLOWST_COUNT) | |
44 | return "?"; | |
45 | ||
46 | return bootflow_state[state]; | |
47 | } | |
48 | ||
49 | int bootflow_first_glob(struct bootflow **bflowp) | |
50 | { | |
51 | struct bootstd_priv *std; | |
52 | int ret; | |
53 | ||
54 | ret = bootstd_get_priv(&std); | |
55 | if (ret) | |
56 | return ret; | |
57 | ||
58 | if (list_empty(&std->glob_head)) | |
59 | return -ENOENT; | |
60 | ||
61 | *bflowp = list_first_entry(&std->glob_head, struct bootflow, | |
62 | glob_node); | |
63 | ||
64 | return 0; | |
65 | } | |
66 | ||
67 | int bootflow_next_glob(struct bootflow **bflowp) | |
68 | { | |
69 | struct bootstd_priv *std; | |
70 | struct bootflow *bflow = *bflowp; | |
71 | int ret; | |
72 | ||
73 | ret = bootstd_get_priv(&std); | |
74 | if (ret) | |
75 | return ret; | |
76 | ||
77 | *bflowp = NULL; | |
78 | ||
79 | if (list_is_last(&bflow->glob_node, &std->glob_head)) | |
80 | return -ENOENT; | |
81 | ||
82 | *bflowp = list_entry(bflow->glob_node.next, struct bootflow, glob_node); | |
83 | ||
84 | return 0; | |
85 | } | |
86 | ||
87 | void bootflow_iter_init(struct bootflow_iter *iter, int flags) | |
88 | { | |
89 | memset(iter, '\0', sizeof(*iter)); | |
2b80bc1e | 90 | iter->first_glob_method = -1; |
a8f5be17 | 91 | iter->flags = flags; |
a950f285 SG |
92 | |
93 | /* remember the first bootdevs we see */ | |
94 | iter->max_devs = BOOTFLOW_MAX_USED_DEVS; | |
a8f5be17 SG |
95 | } |
96 | ||
97 | void bootflow_iter_uninit(struct bootflow_iter *iter) | |
98 | { | |
a8f5be17 SG |
99 | free(iter->method_order); |
100 | } | |
101 | ||
102 | int bootflow_iter_drop_bootmeth(struct bootflow_iter *iter, | |
103 | const struct udevice *bmeth) | |
104 | { | |
105 | /* We only support disabling the current bootmeth */ | |
106 | if (bmeth != iter->method || iter->cur_method >= iter->num_methods || | |
107 | iter->method_order[iter->cur_method] != bmeth) | |
108 | return -EINVAL; | |
109 | ||
110 | memmove(&iter->method_order[iter->cur_method], | |
111 | &iter->method_order[iter->cur_method + 1], | |
112 | (iter->num_methods - iter->cur_method - 1) * sizeof(void *)); | |
113 | ||
114 | iter->num_methods--; | |
115 | ||
116 | return 0; | |
117 | } | |
118 | ||
47aedc29 SG |
119 | /** |
120 | * bootflow_iter_set_dev() - switch to the next bootdev when iterating | |
121 | * | |
122 | * This sets iter->dev, records the device in the dev_used[] list and shows a | |
123 | * message if required | |
124 | * | |
125 | * @iter: Iterator to update | |
126 | * @dev: Bootdev to use, or NULL if there are no more | |
127 | */ | |
a8f5be17 | 128 | static void bootflow_iter_set_dev(struct bootflow_iter *iter, |
47aedc29 | 129 | struct udevice *dev, int method_flags) |
a8f5be17 | 130 | { |
2b80bc1e SG |
131 | struct bootmeth_uc_plat *ucp = dev_get_uclass_plat(iter->method); |
132 | ||
47aedc29 SG |
133 | log_debug("iter: Setting dev to %s, flags %x\n", |
134 | dev ? dev->name : "(none)", method_flags); | |
a8f5be17 | 135 | iter->dev = dev; |
47aedc29 SG |
136 | iter->method_flags = method_flags; |
137 | ||
a950f285 SG |
138 | if (IS_ENABLED(CONFIG_BOOTSTD_FULL)) { |
139 | /* record the device for later */ | |
140 | if (dev && iter->num_devs < iter->max_devs) | |
141 | iter->dev_used[iter->num_devs++] = dev; | |
142 | ||
4f806f31 SG |
143 | if ((iter->flags & (BOOTFLOWIF_SHOW | BOOTFLOWIF_SINGLE_DEV)) == |
144 | BOOTFLOWIF_SHOW) { | |
a950f285 SG |
145 | if (dev) |
146 | printf("Scanning bootdev '%s':\n", dev->name); | |
147 | else if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && | |
148 | ucp->flags & BOOTMETHF_GLOBAL) | |
149 | printf("Scanning global bootmeth '%s':\n", | |
150 | iter->method->name); | |
151 | else | |
152 | printf("No more bootdevs\n"); | |
153 | } | |
a8f5be17 SG |
154 | } |
155 | } | |
156 | ||
157 | /** | |
158 | * iter_incr() - Move to the next item (method, part, bootdev) | |
159 | * | |
160 | * Return: 0 if OK, BF_NO_MORE_DEVICES if there are no more bootdevs | |
161 | */ | |
162 | static int iter_incr(struct bootflow_iter *iter) | |
163 | { | |
164 | struct udevice *dev; | |
2b80bc1e SG |
165 | bool inc_dev = true; |
166 | bool global; | |
a8f5be17 SG |
167 | int ret; |
168 | ||
47aedc29 | 169 | log_debug("entry: err=%d\n", iter->err); |
2b80bc1e SG |
170 | global = iter->doing_global; |
171 | ||
a8f5be17 SG |
172 | if (iter->err == BF_NO_MORE_DEVICES) |
173 | return BF_NO_MORE_DEVICES; | |
174 | ||
175 | if (iter->err != BF_NO_MORE_PARTS) { | |
176 | /* Get the next boothmethod */ | |
177 | if (++iter->cur_method < iter->num_methods) { | |
178 | iter->method = iter->method_order[iter->cur_method]; | |
179 | return 0; | |
180 | } | |
2b80bc1e SG |
181 | |
182 | /* | |
183 | * If we have finished scanning the global bootmeths, start the | |
184 | * normal bootdev scan | |
185 | */ | |
186 | if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && global) { | |
187 | iter->num_methods = iter->first_glob_method; | |
188 | iter->doing_global = false; | |
189 | ||
190 | /* | |
191 | * Don't move to the next dev as we haven't tried this | |
192 | * one yet! | |
193 | */ | |
194 | inc_dev = false; | |
195 | } | |
a8f5be17 SG |
196 | } |
197 | ||
198 | /* No more bootmeths; start at the first one, and... */ | |
199 | iter->cur_method = 0; | |
200 | iter->method = iter->method_order[iter->cur_method]; | |
201 | ||
202 | if (iter->err != BF_NO_MORE_PARTS) { | |
203 | /* ...select next partition */ | |
204 | if (++iter->part <= iter->max_part) | |
205 | return 0; | |
206 | } | |
207 | ||
47aedc29 | 208 | /* No more partitions; start at the first one and... */ |
a8f5be17 SG |
209 | iter->part = 0; |
210 | ||
211 | /* | |
212 | * Note: as far as we know, there is no partition table on the next | |
213 | * bootdev, so set max_part to 0 until we discover otherwise. See | |
214 | * bootdev_find_in_blk() for where this is set. | |
215 | */ | |
216 | iter->max_part = 0; | |
217 | ||
218 | /* ...select next bootdev */ | |
4f806f31 | 219 | if (iter->flags & BOOTFLOWIF_SINGLE_DEV) { |
a8f5be17 | 220 | ret = -ENOENT; |
a8f5be17 | 221 | } else { |
47aedc29 SG |
222 | int method_flags; |
223 | ||
224 | ret = 0; | |
225 | dev = iter->dev; | |
226 | log_debug("inc_dev=%d\n", inc_dev); | |
227 | if (!inc_dev) { | |
91943ff7 SG |
228 | ret = bootdev_setup_iter(iter, NULL, &dev, |
229 | &method_flags); | |
230 | } else if (IS_ENABLED(CONFIG_BOOTSTD_FULL) && | |
4f806f31 | 231 | (iter->flags & BOOTFLOWIF_SINGLE_UCLASS)) { |
91943ff7 SG |
232 | /* Move to the next bootdev in this uclass */ |
233 | uclass_find_next_device(&dev); | |
234 | if (!dev) { | |
235 | log_debug("finished uclass %s\n", | |
236 | dev_get_uclass_name(dev)); | |
237 | ret = -ENODEV; | |
238 | } | |
239 | } else if (IS_ENABLED(CONFIG_BOOTSTD_FULL) && | |
4f806f31 | 240 | iter->flags & BOOTFLOWIF_SINGLE_MEDIA) { |
91943ff7 SG |
241 | log_debug("next in single\n"); |
242 | method_flags = 0; | |
243 | do { | |
244 | /* | |
245 | * Move to the next bootdev child of this media | |
246 | * device. This ensures that we cover all the | |
247 | * available SCSI IDs and LUNs. | |
248 | */ | |
249 | device_find_next_child(&dev); | |
250 | log_debug("- next %s\n", | |
251 | dev ? dev->name : "(none)"); | |
252 | } while (dev && device_get_uclass_id(dev) != | |
253 | UCLASS_BOOTDEV); | |
254 | if (!dev) { | |
255 | log_debug("finished uclass %s\n", | |
256 | dev_get_uclass_name(dev)); | |
257 | ret = -ENODEV; | |
258 | } | |
47aedc29 SG |
259 | } else { |
260 | log_debug("labels %p\n", iter->labels); | |
261 | if (iter->labels) { | |
262 | ret = bootdev_next_label(iter, &dev, | |
263 | &method_flags); | |
264 | } else { | |
265 | ret = bootdev_next_prio(iter, &dev); | |
266 | method_flags = 0; | |
267 | } | |
268 | } | |
269 | log_debug("ret=%d, dev=%p %s\n", ret, dev, | |
270 | dev ? dev->name : "none"); | |
271 | if (ret) { | |
272 | bootflow_iter_set_dev(iter, NULL, 0); | |
2b80bc1e | 273 | } else { |
965020c3 SG |
274 | /* |
275 | * Probe the bootdev. This does not probe any attached | |
276 | * block device, since they are siblings | |
277 | */ | |
2b80bc1e | 278 | ret = device_probe(dev); |
47aedc29 | 279 | log_debug("probe %s %d\n", dev->name, ret); |
2b80bc1e | 280 | if (!log_msg_ret("probe", ret)) |
47aedc29 | 281 | bootflow_iter_set_dev(iter, dev, method_flags); |
2b80bc1e | 282 | } |
a8f5be17 SG |
283 | } |
284 | ||
285 | /* if there are no more bootdevs, give up */ | |
286 | if (ret) | |
287 | return log_msg_ret("incr", BF_NO_MORE_DEVICES); | |
288 | ||
289 | return 0; | |
290 | } | |
291 | ||
292 | /** | |
293 | * bootflow_check() - Check if a bootflow can be obtained | |
294 | * | |
295 | * @iter: Provides part, bootmeth to use | |
296 | * @bflow: Bootflow to update on success | |
297 | * Return: 0 if OK, -ENOSYS if there is no bootflow support on this device, | |
298 | * BF_NO_MORE_PARTS if there are no more partitions on bootdev | |
299 | */ | |
300 | static int bootflow_check(struct bootflow_iter *iter, struct bootflow *bflow) | |
301 | { | |
302 | struct udevice *dev; | |
303 | int ret; | |
304 | ||
2b80bc1e | 305 | if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && iter->doing_global) { |
47aedc29 | 306 | bootflow_iter_set_dev(iter, NULL, 0); |
2b80bc1e SG |
307 | ret = bootmeth_get_bootflow(iter->method, bflow); |
308 | if (ret) | |
309 | return log_msg_ret("glob", ret); | |
310 | ||
311 | return 0; | |
312 | } | |
313 | ||
a8f5be17 SG |
314 | dev = iter->dev; |
315 | ret = bootdev_get_bootflow(dev, iter, bflow); | |
316 | ||
317 | /* If we got a valid bootflow, return it */ | |
318 | if (!ret) { | |
4de979f6 | 319 | log_debug("Bootdev '%s' part %d method '%s': Found bootflow\n", |
a8f5be17 SG |
320 | dev->name, iter->part, iter->method->name); |
321 | return 0; | |
322 | } | |
323 | ||
324 | /* Unless there is nothing more to try, move to the next device */ | |
325 | else if (ret != BF_NO_MORE_PARTS && ret != -ENOSYS) { | |
4de979f6 | 326 | log_debug("Bootdev '%s' part %d method '%s': Error %d\n", |
a8f5be17 SG |
327 | dev->name, iter->part, iter->method->name, ret); |
328 | /* | |
329 | * For 'all' we return all bootflows, even | |
330 | * those with errors | |
331 | */ | |
4f806f31 | 332 | if (iter->flags & BOOTFLOWIF_ALL) |
a8f5be17 SG |
333 | return log_msg_ret("all", ret); |
334 | } | |
335 | if (ret) | |
336 | return log_msg_ret("check", ret); | |
337 | ||
338 | return 0; | |
339 | } | |
340 | ||
4b7cb058 SG |
341 | int bootflow_scan_first(struct udevice *dev, const char *label, |
342 | struct bootflow_iter *iter, int flags, | |
343 | struct bootflow *bflow) | |
a8f5be17 SG |
344 | { |
345 | int ret; | |
346 | ||
91943ff7 | 347 | if (dev || label) |
4f806f31 | 348 | flags |= BOOTFLOWIF_SKIP_GLOBAL; |
a8f5be17 SG |
349 | bootflow_iter_init(iter, flags); |
350 | ||
47aedc29 SG |
351 | /* |
352 | * Set up the ordering of bootmeths. This sets iter->doing_global and | |
353 | * iter->first_glob_method if we are starting with the global bootmeths | |
354 | */ | |
4f806f31 | 355 | ret = bootmeth_setup_iter_order(iter, !(flags & BOOTFLOWIF_SKIP_GLOBAL)); |
a8f5be17 SG |
356 | if (ret) |
357 | return log_msg_ret("obmeth", -ENODEV); | |
358 | ||
359 | /* Find the first bootmeth (there must be at least one!) */ | |
360 | iter->method = iter->method_order[iter->cur_method]; | |
47aedc29 SG |
361 | |
362 | if (!IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) || !iter->doing_global) { | |
363 | struct udevice *dev = NULL; | |
364 | int method_flags; | |
365 | ||
91943ff7 | 366 | ret = bootdev_setup_iter(iter, label, &dev, &method_flags); |
47aedc29 SG |
367 | if (ret) |
368 | return log_msg_ret("obdev", -ENODEV); | |
369 | ||
370 | bootflow_iter_set_dev(iter, dev, method_flags); | |
371 | } | |
a8f5be17 SG |
372 | |
373 | ret = bootflow_check(iter, bflow); | |
374 | if (ret) { | |
f738c73a | 375 | log_debug("check - ret=%d\n", ret); |
a8f5be17 | 376 | if (ret != BF_NO_MORE_PARTS && ret != -ENOSYS) { |
4f806f31 | 377 | if (iter->flags & BOOTFLOWIF_ALL) |
a8f5be17 SG |
378 | return log_msg_ret("all", ret); |
379 | } | |
380 | iter->err = ret; | |
381 | ret = bootflow_scan_next(iter, bflow); | |
382 | if (ret) | |
383 | return log_msg_ret("get", ret); | |
384 | } | |
385 | ||
386 | return 0; | |
387 | } | |
388 | ||
a8f5be17 SG |
389 | int bootflow_scan_next(struct bootflow_iter *iter, struct bootflow *bflow) |
390 | { | |
391 | int ret; | |
392 | ||
393 | do { | |
394 | ret = iter_incr(iter); | |
f738c73a | 395 | log_debug("iter_incr: ret=%d\n", ret); |
a8f5be17 SG |
396 | if (ret == BF_NO_MORE_DEVICES) |
397 | return log_msg_ret("done", ret); | |
398 | ||
399 | if (!ret) { | |
400 | ret = bootflow_check(iter, bflow); | |
f738c73a | 401 | log_debug("check - ret=%d\n", ret); |
a8f5be17 SG |
402 | if (!ret) |
403 | return 0; | |
404 | iter->err = ret; | |
405 | if (ret != BF_NO_MORE_PARTS && ret != -ENOSYS) { | |
4f806f31 | 406 | if (iter->flags & BOOTFLOWIF_ALL) |
a8f5be17 SG |
407 | return log_msg_ret("all", ret); |
408 | } | |
409 | } else { | |
f738c73a | 410 | log_debug("incr failed, err=%d\n", ret); |
a8f5be17 SG |
411 | iter->err = ret; |
412 | } | |
413 | ||
414 | } while (1); | |
415 | } | |
416 | ||
b190deb8 SG |
417 | void bootflow_init(struct bootflow *bflow, struct udevice *bootdev, |
418 | struct udevice *meth) | |
419 | { | |
420 | memset(bflow, '\0', sizeof(*bflow)); | |
421 | bflow->dev = bootdev; | |
422 | bflow->method = meth; | |
423 | bflow->state = BOOTFLOWST_BASE; | |
424 | } | |
425 | ||
a8f5be17 SG |
426 | void bootflow_free(struct bootflow *bflow) |
427 | { | |
428 | free(bflow->name); | |
429 | free(bflow->subdir); | |
430 | free(bflow->fname); | |
431 | free(bflow->buf); | |
2175e76a | 432 | free(bflow->os_name); |
7638c851 | 433 | free(bflow->fdt_fname); |
a8f5be17 SG |
434 | } |
435 | ||
436 | void bootflow_remove(struct bootflow *bflow) | |
437 | { | |
eccb25cd SG |
438 | if (bflow->dev) |
439 | list_del(&bflow->bm_node); | |
a8f5be17 SG |
440 | list_del(&bflow->glob_node); |
441 | ||
442 | bootflow_free(bflow); | |
443 | free(bflow); | |
444 | } | |
445 | ||
446 | int bootflow_boot(struct bootflow *bflow) | |
447 | { | |
448 | int ret; | |
449 | ||
450 | if (bflow->state != BOOTFLOWST_READY) | |
451 | return log_msg_ret("load", -EPROTO); | |
452 | ||
453 | ret = bootmeth_boot(bflow->method, bflow); | |
454 | if (ret) | |
455 | return log_msg_ret("boot", ret); | |
456 | ||
457 | /* | |
458 | * internal error, should not get here since we should have booted | |
459 | * something or returned an error | |
460 | */ | |
461 | ||
462 | return log_msg_ret("end", -EFAULT); | |
463 | } | |
464 | ||
465 | int bootflow_run_boot(struct bootflow_iter *iter, struct bootflow *bflow) | |
466 | { | |
467 | int ret; | |
468 | ||
469 | printf("** Booting bootflow '%s' with %s\n", bflow->name, | |
470 | bflow->method->name); | |
47dd6b4d SG |
471 | if (IS_ENABLED(CONFIG_OF_HAS_PRIOR_STAGE) && |
472 | (bflow->flags & BOOTFLOWF_USE_PRIOR_FDT)) | |
473 | printf("Using prior-stage device tree\n"); | |
a8f5be17 SG |
474 | ret = bootflow_boot(bflow); |
475 | if (!IS_ENABLED(CONFIG_BOOTSTD_FULL)) { | |
476 | printf("Boot failed (err=%d)\n", ret); | |
477 | return ret; | |
478 | } | |
479 | ||
480 | switch (ret) { | |
481 | case -EPROTO: | |
482 | printf("Bootflow not loaded (state '%s')\n", | |
483 | bootflow_state_get_name(bflow->state)); | |
484 | break; | |
485 | case -ENOSYS: | |
486 | printf("Boot method '%s' not supported\n", bflow->method->name); | |
487 | break; | |
488 | case -ENOTSUPP: | |
489 | /* Disable this bootflow for this iteration */ | |
490 | if (iter) { | |
491 | int ret2; | |
492 | ||
493 | ret2 = bootflow_iter_drop_bootmeth(iter, bflow->method); | |
494 | if (!ret2) { | |
495 | printf("Boot method '%s' failed and will not be retried\n", | |
496 | bflow->method->name); | |
497 | } | |
498 | } | |
499 | ||
500 | break; | |
501 | default: | |
502 | printf("Boot failed (err=%d)\n", ret); | |
503 | break; | |
504 | } | |
505 | ||
506 | return ret; | |
507 | } | |
508 | ||
865328c3 | 509 | int bootflow_iter_check_blk(const struct bootflow_iter *iter) |
a8f5be17 SG |
510 | { |
511 | const struct udevice *media = dev_get_parent(iter->dev); | |
512 | enum uclass_id id = device_get_uclass_id(media); | |
513 | ||
514 | log_debug("uclass %d: %s\n", id, uclass_get_name(id)); | |
9c6d57dc | 515 | if (id != UCLASS_ETH && id != UCLASS_BOOTSTD && id != UCLASS_QFW) |
a8f5be17 SG |
516 | return 0; |
517 | ||
518 | return -ENOTSUPP; | |
519 | } | |
520 | ||
0c1f4a9f SG |
521 | int bootflow_iter_check_sf(const struct bootflow_iter *iter) |
522 | { | |
523 | const struct udevice *media = dev_get_parent(iter->dev); | |
524 | enum uclass_id id = device_get_uclass_id(media); | |
525 | ||
526 | log_debug("uclass %d: %s\n", id, uclass_get_name(id)); | |
527 | if (id == UCLASS_SPI_FLASH) | |
528 | return 0; | |
529 | ||
530 | return -ENOTSUPP; | |
531 | } | |
532 | ||
865328c3 | 533 | int bootflow_iter_check_net(const struct bootflow_iter *iter) |
a8f5be17 SG |
534 | { |
535 | const struct udevice *media = dev_get_parent(iter->dev); | |
536 | enum uclass_id id = device_get_uclass_id(media); | |
537 | ||
538 | log_debug("uclass %d: %s\n", id, uclass_get_name(id)); | |
539 | if (id == UCLASS_ETH) | |
540 | return 0; | |
541 | ||
542 | return -ENOTSUPP; | |
543 | } | |
544 | ||
865328c3 | 545 | int bootflow_iter_check_system(const struct bootflow_iter *iter) |
a8f5be17 SG |
546 | { |
547 | const struct udevice *media = dev_get_parent(iter->dev); | |
548 | enum uclass_id id = device_get_uclass_id(media); | |
549 | ||
550 | log_debug("uclass %d: %s\n", id, uclass_get_name(id)); | |
551 | if (id == UCLASS_BOOTSTD) | |
552 | return 0; | |
553 | ||
554 | return -ENOTSUPP; | |
555 | } | |
d42243fe SG |
556 | |
557 | /** | |
558 | * bootflow_cmdline_set() - Set the command line for a bootflow | |
559 | * | |
560 | * @value: New command-line string | |
561 | * Returns 0 if OK, -ENOENT if no current bootflow, -ENOMEM if out of memory | |
562 | */ | |
563 | int bootflow_cmdline_set(struct bootflow *bflow, const char *value) | |
564 | { | |
565 | char *cmdline = NULL; | |
566 | ||
567 | if (value) { | |
568 | cmdline = strdup(value); | |
569 | if (!cmdline) | |
570 | return -ENOMEM; | |
571 | } | |
572 | ||
573 | free(bflow->cmdline); | |
574 | bflow->cmdline = cmdline; | |
575 | ||
576 | return 0; | |
577 | } | |
578 | ||
579 | #ifdef CONFIG_BOOTSTD_FULL | |
580 | /** | |
581 | * on_bootargs() - Update the cmdline of a bootflow | |
582 | */ | |
583 | static int on_bootargs(const char *name, const char *value, enum env_op op, | |
584 | int flags) | |
585 | { | |
586 | struct bootstd_priv *std; | |
587 | struct bootflow *bflow; | |
588 | int ret; | |
589 | ||
590 | ret = bootstd_get_priv(&std); | |
591 | if (ret) | |
592 | return 0; | |
593 | bflow = std->cur_bootflow; | |
594 | if (!bflow) | |
595 | return 0; | |
596 | ||
597 | switch (op) { | |
598 | case env_op_create: | |
599 | case env_op_overwrite: | |
600 | ret = bootflow_cmdline_set(bflow, value); | |
601 | if (ret && ret != ENOENT) | |
602 | return 1; | |
603 | return 0; | |
604 | case env_op_delete: | |
605 | bootflow_cmdline_set(bflow, NULL); | |
606 | fallthrough; | |
607 | default: | |
608 | return 0; | |
609 | } | |
610 | } | |
611 | U_BOOT_ENV_CALLBACK(bootargs, on_bootargs); | |
612 | #endif |