]> Git Repo - u-boot.git/blob - boot/bootflow.c
bootstd: Use the bootargs env var for changing the cmdline
[u-boot.git] / boot / bootflow.c
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>
15 #include <env_internal.h>
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));
90         iter->first_glob_method = -1;
91         iter->flags = flags;
92
93         /* remember the first bootdevs we see */
94         iter->max_devs = BOOTFLOW_MAX_USED_DEVS;
95 }
96
97 void bootflow_iter_uninit(struct bootflow_iter *iter)
98 {
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
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  */
128 static void bootflow_iter_set_dev(struct bootflow_iter *iter,
129                                   struct udevice *dev, int method_flags)
130 {
131         struct bootmeth_uc_plat *ucp = dev_get_uclass_plat(iter->method);
132
133         log_debug("iter: Setting dev to %s, flags %x\n",
134                   dev ? dev->name : "(none)", method_flags);
135         iter->dev = dev;
136         iter->method_flags = method_flags;
137
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
143                 if ((iter->flags & (BOOTFLOWIF_SHOW | BOOTFLOWIF_SINGLE_DEV)) ==
144                     BOOTFLOWIF_SHOW) {
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                 }
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;
165         bool inc_dev = true;
166         bool global;
167         int ret;
168
169         log_debug("entry: err=%d\n", iter->err);
170         global = iter->doing_global;
171
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                 }
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                 }
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
208         /* No more partitions; start at the first one and... */
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 */
219         if (iter->flags & BOOTFLOWIF_SINGLE_DEV) {
220                 ret = -ENOENT;
221         } else {
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) {
228                         ret = bootdev_setup_iter(iter, NULL, &dev,
229                                                  &method_flags);
230                 } else if (IS_ENABLED(CONFIG_BOOTSTD_FULL) &&
231                            (iter->flags & BOOTFLOWIF_SINGLE_UCLASS)) {
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) &&
240                            iter->flags & BOOTFLOWIF_SINGLE_MEDIA) {
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                         }
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);
273                 } else {
274                         /*
275                          * Probe the bootdev. This does not probe any attached
276                          * block device, since they are siblings
277                          */
278                         ret = device_probe(dev);
279                         log_debug("probe %s %d\n", dev->name, ret);
280                         if (!log_msg_ret("probe", ret))
281                                 bootflow_iter_set_dev(iter, dev, method_flags);
282                 }
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
305         if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && iter->doing_global) {
306                 bootflow_iter_set_dev(iter, NULL, 0);
307                 ret = bootmeth_get_bootflow(iter->method, bflow);
308                 if (ret)
309                         return log_msg_ret("glob", ret);
310
311                 return 0;
312         }
313
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) {
319                 log_debug("Bootdev '%s' part %d method '%s': Found bootflow\n",
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) {
326                 log_debug("Bootdev '%s' part %d method '%s': Error %d\n",
327                           dev->name, iter->part, iter->method->name, ret);
328                 /*
329                  * For 'all' we return all bootflows, even
330                  * those with errors
331                  */
332                 if (iter->flags & BOOTFLOWIF_ALL)
333                         return log_msg_ret("all", ret);
334         }
335         if (ret)
336                 return log_msg_ret("check", ret);
337
338         return 0;
339 }
340
341 int bootflow_scan_first(struct udevice *dev, const char *label,
342                         struct bootflow_iter *iter, int flags,
343                         struct bootflow *bflow)
344 {
345         int ret;
346
347         if (dev || label)
348                 flags |= BOOTFLOWIF_SKIP_GLOBAL;
349         bootflow_iter_init(iter, flags);
350
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          */
355         ret = bootmeth_setup_iter_order(iter, !(flags & BOOTFLOWIF_SKIP_GLOBAL));
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];
361
362         if (!IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) || !iter->doing_global) {
363                 struct udevice *dev = NULL;
364                 int method_flags;
365
366                 ret = bootdev_setup_iter(iter, label, &dev, &method_flags);
367                 if (ret)
368                         return log_msg_ret("obdev", -ENODEV);
369
370                 bootflow_iter_set_dev(iter, dev, method_flags);
371         }
372
373         ret = bootflow_check(iter, bflow);
374         if (ret) {
375                 log_debug("check - ret=%d\n", ret);
376                 if (ret != BF_NO_MORE_PARTS && ret != -ENOSYS) {
377                         if (iter->flags & BOOTFLOWIF_ALL)
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
389 int bootflow_scan_next(struct bootflow_iter *iter, struct bootflow *bflow)
390 {
391         int ret;
392
393         do {
394                 ret = iter_incr(iter);
395                 log_debug("iter_incr: ret=%d\n", ret);
396                 if (ret == BF_NO_MORE_DEVICES)
397                         return log_msg_ret("done", ret);
398
399                 if (!ret) {
400                         ret = bootflow_check(iter, bflow);
401                         log_debug("check - ret=%d\n", ret);
402                         if (!ret)
403                                 return 0;
404                         iter->err = ret;
405                         if (ret != BF_NO_MORE_PARTS && ret != -ENOSYS) {
406                                 if (iter->flags & BOOTFLOWIF_ALL)
407                                         return log_msg_ret("all", ret);
408                         }
409                 } else {
410                         log_debug("incr failed, err=%d\n", ret);
411                         iter->err = ret;
412                 }
413
414         } while (1);
415 }
416
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
426 void bootflow_free(struct bootflow *bflow)
427 {
428         free(bflow->name);
429         free(bflow->subdir);
430         free(bflow->fname);
431         free(bflow->buf);
432         free(bflow->os_name);
433         free(bflow->fdt_fname);
434 }
435
436 void bootflow_remove(struct bootflow *bflow)
437 {
438         if (bflow->dev)
439                 list_del(&bflow->bm_node);
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);
471         if (IS_ENABLED(CONFIG_OF_HAS_PRIOR_STAGE) &&
472             (bflow->flags & BOOTFLOWF_USE_PRIOR_FDT))
473                 printf("Using prior-stage device tree\n");
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
509 int bootflow_iter_check_blk(const struct bootflow_iter *iter)
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));
515         if (id != UCLASS_ETH && id != UCLASS_BOOTSTD && id != UCLASS_QFW)
516                 return 0;
517
518         return -ENOTSUPP;
519 }
520
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
533 int bootflow_iter_check_net(const struct bootflow_iter *iter)
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
545 int bootflow_iter_check_system(const struct bootflow_iter *iter)
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 }
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
This page took 0.06448 seconds and 4 git commands to generate.