]> Git Repo - J-u-boot.git/blob - boot/bootflow.c
Merge tag 'v2024.07-rc4' into next
[J-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 <bootdev.h>
10 #include <bootflow.h>
11 #include <bootmeth.h>
12 #include <bootstd.h>
13 #include <dm.h>
14 #include <env_internal.h>
15 #include <malloc.h>
16 #include <serial.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  * scan_next_in_uclass() - Scan for the next bootdev in the same media uclass
159  *
160  * Move through the following bootdevs until we find another in this media
161  * uclass, or run out
162  *
163  * @devp: On entry, the device to check, on exit the new device, or NULL if
164  * there is none
165  */
166 static void scan_next_in_uclass(struct udevice **devp)
167 {
168         struct udevice *dev = *devp;
169         enum uclass_id cur_id = device_get_uclass_id(dev->parent);
170
171         do {
172                 uclass_find_next_device(&dev);
173         } while (dev && cur_id != device_get_uclass_id(dev->parent));
174
175         *devp = dev;
176 }
177
178 /**
179  * iter_incr() - Move to the next item (method, part, bootdev)
180  *
181  * Return: 0 if OK, BF_NO_MORE_DEVICES if there are no more bootdevs
182  */
183 static int iter_incr(struct bootflow_iter *iter)
184 {
185         struct udevice *dev;
186         bool inc_dev = true;
187         bool global;
188         int ret;
189
190         log_debug("entry: err=%d\n", iter->err);
191         global = iter->doing_global;
192
193         if (iter->err == BF_NO_MORE_DEVICES)
194                 return BF_NO_MORE_DEVICES;
195
196         if (iter->err != BF_NO_MORE_PARTS) {
197                 /* Get the next boothmethod */
198                 if (++iter->cur_method < iter->num_methods) {
199                         iter->method = iter->method_order[iter->cur_method];
200                         return 0;
201                 }
202
203                 /*
204                  * If we have finished scanning the global bootmeths, start the
205                  * normal bootdev scan
206                  */
207                 if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && global) {
208                         iter->num_methods = iter->first_glob_method;
209                         iter->doing_global = false;
210
211                         /*
212                          * Don't move to the next dev as we haven't tried this
213                          * one yet!
214                          */
215                         inc_dev = false;
216                 }
217         }
218
219         if (iter->flags & BOOTFLOWIF_SINGLE_PARTITION)
220                 return BF_NO_MORE_DEVICES;
221
222         /* No more bootmeths; start at the first one, and... */
223         iter->cur_method = 0;
224         iter->method = iter->method_order[iter->cur_method];
225
226         if (iter->err != BF_NO_MORE_PARTS) {
227                 /* ...select next partition  */
228                 if (++iter->part <= iter->max_part)
229                         return 0;
230         }
231
232         /* No more partitions; start at the first one and... */
233         iter->part = 0;
234
235         /*
236          * Note: as far as we know, there is no partition table on the next
237          * bootdev, so set max_part to 0 until we discover otherwise. See
238          * bootdev_find_in_blk() for where this is set.
239          */
240         iter->max_part = 0;
241
242         /* ...select next bootdev */
243         if (iter->flags & BOOTFLOWIF_SINGLE_DEV) {
244                 ret = -ENOENT;
245         } else {
246                 int method_flags;
247
248                 ret = 0;
249                 dev = iter->dev;
250                 log_debug("inc_dev=%d\n", inc_dev);
251                 if (!inc_dev) {
252                         ret = bootdev_setup_iter(iter, NULL, &dev,
253                                                  &method_flags);
254                 } else if (IS_ENABLED(CONFIG_BOOTSTD_FULL) &&
255                            (iter->flags & BOOTFLOWIF_SINGLE_UCLASS)) {
256                         scan_next_in_uclass(&dev);
257                         if (!dev) {
258                                 log_debug("finished uclass %s\n",
259                                           dev_get_uclass_name(dev));
260                                 ret = -ENODEV;
261                         }
262                 } else if (IS_ENABLED(CONFIG_BOOTSTD_FULL) &&
263                            iter->flags & BOOTFLOWIF_SINGLE_MEDIA) {
264                         log_debug("next in single\n");
265                         method_flags = 0;
266                         do {
267                                 /*
268                                  * Move to the next bootdev child of this media
269                                  * device. This ensures that we cover all the
270                                  * available SCSI IDs and LUNs.
271                                  */
272                                 device_find_next_child(&dev);
273                                 log_debug("- next %s\n",
274                                         dev ? dev->name : "(none)");
275                         } while (dev && device_get_uclass_id(dev) !=
276                                 UCLASS_BOOTDEV);
277                         if (!dev) {
278                                 log_debug("finished uclass %s\n",
279                                           dev_get_uclass_name(dev));
280                                 ret = -ENODEV;
281                         }
282                 } else {
283                         log_debug("labels %p\n", iter->labels);
284                         if (iter->labels) {
285                                 /*
286                                  * when the label is "mmc" we want to scan all
287                                  * mmc bootdevs, not just the first. See
288                                  * bootdev_find_by_label() where this flag is
289                                  * set up
290                                  */
291                                 if (iter->method_flags &
292                                     BOOTFLOW_METHF_SINGLE_UCLASS) {
293                                         scan_next_in_uclass(&dev);
294                                         log_debug("looking for next device %s: %s\n",
295                                                   iter->dev->name,
296                                                   dev ? dev->name : "<none>");
297                                 } else {
298                                         dev = NULL;
299                                 }
300                                 if (!dev) {
301                                         log_debug("looking at next label\n");
302                                         ret = bootdev_next_label(iter, &dev,
303                                                                  &method_flags);
304                                 }
305                         } else {
306                                 ret = bootdev_next_prio(iter, &dev);
307                                 method_flags = 0;
308                         }
309                 }
310                 log_debug("ret=%d, dev=%p %s\n", ret, dev,
311                           dev ? dev->name : "none");
312                 if (ret) {
313                         bootflow_iter_set_dev(iter, NULL, 0);
314                 } else {
315                         /*
316                          * Probe the bootdev. This does not probe any attached
317                          * block device, since they are siblings
318                          */
319                         ret = device_probe(dev);
320                         log_debug("probe %s %d\n", dev->name, ret);
321                         if (!log_msg_ret("probe", ret))
322                                 bootflow_iter_set_dev(iter, dev, method_flags);
323                 }
324         }
325
326         /* if there are no more bootdevs, give up */
327         if (ret)
328                 return log_msg_ret("incr", BF_NO_MORE_DEVICES);
329
330         return 0;
331 }
332
333 /**
334  * bootflow_check() - Check if a bootflow can be obtained
335  *
336  * @iter: Provides part, bootmeth to use
337  * @bflow: Bootflow to update on success
338  * Return: 0 if OK, -ENOSYS if there is no bootflow support on this device,
339  *      BF_NO_MORE_PARTS if there are no more partitions on bootdev
340  */
341 static int bootflow_check(struct bootflow_iter *iter, struct bootflow *bflow)
342 {
343         struct udevice *dev;
344         int ret;
345
346         if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && iter->doing_global) {
347                 bootflow_iter_set_dev(iter, NULL, 0);
348                 ret = bootmeth_get_bootflow(iter->method, bflow);
349                 if (ret)
350                         return log_msg_ret("glob", ret);
351
352                 return 0;
353         }
354
355         dev = iter->dev;
356         ret = bootdev_get_bootflow(dev, iter, bflow);
357
358         /* If we got a valid bootflow, return it */
359         if (!ret) {
360                 log_debug("Bootdev '%s' part %d method '%s': Found bootflow\n",
361                           dev->name, iter->part, iter->method->name);
362                 return 0;
363         }
364
365         /* Unless there is nothing more to try, move to the next device */
366         if (ret != BF_NO_MORE_PARTS && ret != -ENOSYS) {
367                 log_debug("Bootdev '%s' part %d method '%s': Error %d\n",
368                           dev->name, iter->part, iter->method->name, ret);
369                 /*
370                  * For 'all' we return all bootflows, even
371                  * those with errors
372                  */
373                 if (iter->flags & BOOTFLOWIF_ALL)
374                         return log_msg_ret("all", ret);
375         }
376
377         return log_msg_ret("check", ret);
378 }
379
380 int bootflow_scan_first(struct udevice *dev, const char *label,
381                         struct bootflow_iter *iter, int flags,
382                         struct bootflow *bflow)
383 {
384         int ret;
385
386         if (dev || label)
387                 flags |= BOOTFLOWIF_SKIP_GLOBAL;
388         bootflow_iter_init(iter, flags);
389
390         /*
391          * Set up the ordering of bootmeths. This sets iter->doing_global and
392          * iter->first_glob_method if we are starting with the global bootmeths
393          */
394         ret = bootmeth_setup_iter_order(iter, !(flags & BOOTFLOWIF_SKIP_GLOBAL));
395         if (ret)
396                 return log_msg_ret("obmeth", -ENODEV);
397
398         /* Find the first bootmeth (there must be at least one!) */
399         iter->method = iter->method_order[iter->cur_method];
400
401         if (!IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) || !iter->doing_global) {
402                 struct udevice *dev = NULL;
403                 int method_flags;
404
405                 ret = bootdev_setup_iter(iter, label, &dev, &method_flags);
406                 if (ret)
407                         return log_msg_ret("obdev", -ENODEV);
408
409                 bootflow_iter_set_dev(iter, dev, method_flags);
410         }
411
412         ret = bootflow_check(iter, bflow);
413         if (ret) {
414                 log_debug("check - ret=%d\n", ret);
415                 if (ret != BF_NO_MORE_PARTS && ret != -ENOSYS) {
416                         if (iter->flags & BOOTFLOWIF_ALL)
417                                 return log_msg_ret("all", ret);
418                 }
419                 iter->err = ret;
420                 ret = bootflow_scan_next(iter, bflow);
421                 if (ret)
422                         return log_msg_ret("get", ret);
423         }
424
425         return 0;
426 }
427
428 int bootflow_scan_next(struct bootflow_iter *iter, struct bootflow *bflow)
429 {
430         int ret;
431
432         do {
433                 ret = iter_incr(iter);
434                 log_debug("iter_incr: ret=%d\n", ret);
435                 if (ret == BF_NO_MORE_DEVICES)
436                         return log_msg_ret("done", ret);
437
438                 if (!ret) {
439                         ret = bootflow_check(iter, bflow);
440                         log_debug("check - ret=%d\n", ret);
441                         if (!ret)
442                                 return 0;
443                         iter->err = ret;
444                         if (ret != BF_NO_MORE_PARTS && ret != -ENOSYS) {
445                                 if (iter->flags & BOOTFLOWIF_ALL)
446                                         return log_msg_ret("all", ret);
447                         }
448                 } else {
449                         log_debug("incr failed, err=%d\n", ret);
450                         iter->err = ret;
451                 }
452
453         } while (1);
454 }
455
456 void bootflow_init(struct bootflow *bflow, struct udevice *bootdev,
457                    struct udevice *meth)
458 {
459         memset(bflow, '\0', sizeof(*bflow));
460         bflow->dev = bootdev;
461         bflow->method = meth;
462         bflow->state = BOOTFLOWST_BASE;
463 }
464
465 void bootflow_free(struct bootflow *bflow)
466 {
467         free(bflow->name);
468         free(bflow->subdir);
469         free(bflow->fname);
470         if (!(bflow->flags & BOOTFLOWF_STATIC_BUF))
471                 free(bflow->buf);
472         free(bflow->os_name);
473         free(bflow->fdt_fname);
474         free(bflow->bootmeth_priv);
475 }
476
477 void bootflow_remove(struct bootflow *bflow)
478 {
479         if (bflow->dev)
480                 list_del(&bflow->bm_node);
481         list_del(&bflow->glob_node);
482
483         bootflow_free(bflow);
484         free(bflow);
485 }
486
487 #if CONFIG_IS_ENABLED(BOOTSTD_FULL)
488 int bootflow_read_all(struct bootflow *bflow)
489 {
490         int ret;
491
492         if (bflow->state != BOOTFLOWST_READY)
493                 return log_msg_ret("rd", -EPROTO);
494
495         ret = bootmeth_read_all(bflow->method, bflow);
496         if (ret)
497                 return log_msg_ret("rd2", ret);
498
499         return 0;
500 }
501 #endif /* BOOTSTD_FULL */
502
503 int bootflow_boot(struct bootflow *bflow)
504 {
505         int ret;
506
507         if (bflow->state != BOOTFLOWST_READY)
508                 return log_msg_ret("load", -EPROTO);
509
510         ret = bootmeth_boot(bflow->method, bflow);
511         if (ret)
512                 return log_msg_ret("boot", ret);
513
514         /*
515          * internal error, should not get here since we should have booted
516          * something or returned an error
517          */
518
519         return log_msg_ret("end", -EFAULT);
520 }
521
522 int bootflow_run_boot(struct bootflow_iter *iter, struct bootflow *bflow)
523 {
524         int ret;
525
526         printf("** Booting bootflow '%s' with %s\n", bflow->name,
527                bflow->method->name);
528         if (IS_ENABLED(CONFIG_OF_HAS_PRIOR_STAGE) &&
529             (bflow->flags & BOOTFLOWF_USE_PRIOR_FDT))
530                 printf("Using prior-stage device tree\n");
531         ret = bootflow_boot(bflow);
532         if (!IS_ENABLED(CONFIG_BOOTSTD_FULL)) {
533                 printf("Boot failed (err=%d)\n", ret);
534                 return ret;
535         }
536
537         switch (ret) {
538         case -EPROTO:
539                 printf("Bootflow not loaded (state '%s')\n",
540                        bootflow_state_get_name(bflow->state));
541                 break;
542         case -ENOSYS:
543                 printf("Boot method '%s' not supported\n", bflow->method->name);
544                 break;
545         case -ENOTSUPP:
546                 /* Disable this bootflow for this iteration */
547                 if (iter) {
548                         int ret2;
549
550                         ret2 = bootflow_iter_drop_bootmeth(iter, bflow->method);
551                         if (!ret2) {
552                                 printf("Boot method '%s' failed and will not be retried\n",
553                                        bflow->method->name);
554                         }
555                 }
556
557                 break;
558         default:
559                 printf("Boot failed (err=%d)\n", ret);
560                 break;
561         }
562
563         return ret;
564 }
565
566 int bootflow_iter_check_blk(const struct bootflow_iter *iter)
567 {
568         const struct udevice *media = dev_get_parent(iter->dev);
569         enum uclass_id id = device_get_uclass_id(media);
570
571         log_debug("uclass %d: %s\n", id, uclass_get_name(id));
572         if (id != UCLASS_ETH && id != UCLASS_BOOTSTD && id != UCLASS_QFW)
573                 return 0;
574
575         return -ENOTSUPP;
576 }
577
578 int bootflow_iter_check_sf(const struct bootflow_iter *iter)
579 {
580         const struct udevice *media = dev_get_parent(iter->dev);
581         enum uclass_id id = device_get_uclass_id(media);
582
583         log_debug("uclass %d: %s\n", id, uclass_get_name(id));
584         if (id == UCLASS_SPI_FLASH)
585                 return 0;
586
587         return -ENOTSUPP;
588 }
589
590 int bootflow_iter_check_net(const struct bootflow_iter *iter)
591 {
592         const struct udevice *media = dev_get_parent(iter->dev);
593         enum uclass_id id = device_get_uclass_id(media);
594
595         log_debug("uclass %d: %s\n", id, uclass_get_name(id));
596         if (id == UCLASS_ETH)
597                 return 0;
598
599         return -ENOTSUPP;
600 }
601
602 int bootflow_iter_check_system(const struct bootflow_iter *iter)
603 {
604         const struct udevice *media = dev_get_parent(iter->dev);
605         enum uclass_id id = device_get_uclass_id(media);
606
607         log_debug("uclass %d: %s\n", id, uclass_get_name(id));
608         if (id == UCLASS_BOOTSTD)
609                 return 0;
610
611         return -ENOTSUPP;
612 }
613
614 /**
615  * bootflow_cmdline_set() - Set the command line for a bootflow
616  *
617  * @value: New command-line string
618  * Returns 0 if OK, -ENOENT if no current bootflow, -ENOMEM if out of memory
619  */
620 int bootflow_cmdline_set(struct bootflow *bflow, const char *value)
621 {
622         char *cmdline = NULL;
623
624         if (value) {
625                 cmdline = strdup(value);
626                 if (!cmdline)
627                         return -ENOMEM;
628         }
629
630         free(bflow->cmdline);
631         bflow->cmdline = cmdline;
632
633         return 0;
634 }
635
636 #ifdef CONFIG_BOOTSTD_FULL
637 /**
638  * on_bootargs() - Update the cmdline of a bootflow
639  */
640 static int on_bootargs(const char *name, const char *value, enum env_op op,
641                        int flags)
642 {
643         struct bootstd_priv *std;
644         struct bootflow *bflow;
645         int ret;
646
647         ret = bootstd_get_priv(&std);
648         if (ret)
649                 return 0;
650         bflow = std->cur_bootflow;
651         if (!bflow)
652                 return 0;
653
654         switch (op) {
655         case env_op_create:
656         case env_op_overwrite:
657                 ret = bootflow_cmdline_set(bflow, value);
658                 if (ret && ret != ENOENT)
659                         return 1;
660                 return 0;
661         case env_op_delete:
662                 bootflow_cmdline_set(bflow, NULL);
663                 fallthrough;
664         default:
665                 return 0;
666         }
667 }
668 U_BOOT_ENV_CALLBACK(bootargs, on_bootargs);
669 #endif
670
671 /**
672  * copy_in() - Copy a string into a cmdline buffer
673  *
674  * @buf: Buffer to copy into
675  * @end: End of buffer (pointer to char after the end)
676  * @arg: String to copy from
677  * @len: Number of chars to copy from @arg (note that this is not usually the
678  * sane as strlen(arg) since the string may contain following arguments)
679  * @new_val: Value to put after arg, or BOOTFLOWCL_EMPTY to use an empty value
680  * with no '=' sign
681  * Returns: Number of chars written to @buf
682  */
683 static int copy_in(char *buf, char *end, const char *arg, int len,
684                    const char *new_val)
685 {
686         char *to = buf;
687
688         /* copy the arg name */
689         if (to + len >= end)
690                 return -E2BIG;
691         memcpy(to, arg, len);
692         to += len;
693
694         if (new_val == BOOTFLOWCL_EMPTY) {
695                 /* no value */
696         } else {
697                 bool need_quote = strchr(new_val, ' ');
698                 len = strlen(new_val);
699
700                 /* need space for value, equals sign and maybe two quotes */
701                 if (to + 1 + (need_quote ? 2 : 0) + len >= end)
702                         return -E2BIG;
703                 *to++ = '=';
704                 if (need_quote)
705                         *to++ = '"';
706                 memcpy(to, new_val, len);
707                 to += len;
708                 if (need_quote)
709                         *to++ = '"';
710         }
711
712         return to - buf;
713 }
714
715 int cmdline_set_arg(char *buf, int maxlen, const char *cmdline,
716                     const char *set_arg, const char *new_val, int *posp)
717 {
718         bool found_arg = false;
719         const char *from;
720         char *to, *end;
721         int set_arg_len;
722         char empty = '\0';
723         int ret;
724
725         from = cmdline ?: &empty;
726
727         /* check if the value has quotes inside */
728         if (new_val && new_val != BOOTFLOWCL_EMPTY && strchr(new_val, '"'))
729                 return -EBADF;
730
731         set_arg_len = strlen(set_arg);
732         for (to = buf, end = buf + maxlen; *from;) {
733                 const char *val, *arg_end, *val_end, *p;
734                 bool in_quote;
735
736                 if (to >= end)
737                         return -E2BIG;
738                 while (*from == ' ')
739                         from++;
740                 if (!*from)
741                         break;
742
743                 /* find the end of this arg */
744                 val = NULL;
745                 arg_end = NULL;
746                 val_end = NULL;
747                 in_quote = false;
748                 for (p = from;; p++) {
749                         if (in_quote) {
750                                 if (!*p)
751                                         return -EINVAL;
752                                 if (*p == '"')
753                                         in_quote = false;
754                                 continue;
755                         }
756                         if (*p == '=' && !arg_end) {
757                                 arg_end = p;
758                                 val = p + 1;
759                         } else if (*p == '"') {
760                                 in_quote = true;
761                         } else if (!*p || *p == ' ') {
762                                 val_end = p;
763                                 if (!arg_end)
764                                         arg_end = p;
765                                 break;
766                         }
767                 }
768                 /*
769                  * At this point val_end points to the end of the value, or the
770                  * last char after the arg name, if there is no label.
771                  * arg_end is the char after the arg name
772                  * val points to the value, or NULL if there is none
773                  * char after the value.
774                  *
775                  *        fred=1234
776                  *        ^   ^^   ^
777                  *      from  ||   |
778                  *           / \    \
779                  *    arg_end  val   val_end
780                  */
781                 log_debug("from %s arg_end %ld val %ld val_end %ld\n", from,
782                           (long)(arg_end - from), (long)(val - from),
783                           (long)(val_end - from));
784
785                 if (to != buf) {
786                         if (to >= end)
787                                 return -E2BIG;
788                         *to++ = ' ';
789                 }
790
791                 /* if this is the target arg, update it */
792                 if (arg_end - from == set_arg_len &&
793                     !strncmp(from, set_arg, set_arg_len)) {
794                         if (!buf) {
795                                 bool has_quote = val_end[-1] == '"';
796
797                                 /*
798                                  * exclude any start/end quotes from
799                                  * calculations
800                                  */
801                                 if (!val)
802                                         val = val_end;
803                                 *posp = val - cmdline + has_quote;
804                                 return val_end - val - 2 * has_quote;
805                         }
806                         found_arg = true;
807                         if (!new_val) {
808                                 /* delete this arg */
809                                 from = val_end + (*val_end == ' ');
810                                 log_debug("delete from: %s\n", from);
811                                 if (to != buf)
812                                         to--; /* drop the space we added */
813                                 continue;
814                         }
815
816                         ret = copy_in(to, end, from, arg_end - from, new_val);
817                         if (ret < 0)
818                                 return ret;
819                         to += ret;
820
821                 /* if not the target arg, copy it unchanged */
822                 } else if (to) {
823                         int len;
824
825                         len = val_end - from;
826                         if (to + len >= end)
827                                 return -E2BIG;
828                         memcpy(to, from, len);
829                         to += len;
830                 }
831                 from = val_end;
832         }
833
834         /* If we didn't find the arg, add it */
835         if (!found_arg) {
836                 /* trying to delete something that is not there */
837                 if (!new_val || !buf)
838                         return -ENOENT;
839                 if (to >= end)
840                         return -E2BIG;
841
842                 /* add a space to separate it from the previous arg */
843                 if (to != buf && to[-1] != ' ')
844                         *to++ = ' ';
845                 ret = copy_in(to, end, set_arg, set_arg_len, new_val);
846                 log_debug("ret=%d, to: %s buf: %s\n", ret, to, buf);
847                 if (ret < 0)
848                         return ret;
849                 to += ret;
850         }
851
852         /* delete any trailing space */
853         if (to > buf && to[-1] == ' ')
854                 to--;
855
856         if (to >= end)
857                 return -E2BIG;
858         *to++ = '\0';
859
860         return to - buf;
861 }
862
863 int bootflow_cmdline_set_arg(struct bootflow *bflow, const char *set_arg,
864                              const char *new_val, bool set_env)
865 {
866         char buf[2048];
867         char *cmd = NULL;
868         int ret;
869
870         ret = cmdline_set_arg(buf, sizeof(buf), bflow->cmdline, set_arg,
871                               new_val, NULL);
872         if (ret < 0)
873                 return ret;
874
875         ret = bootflow_cmdline_set(bflow, buf);
876         if (*buf) {
877                 cmd = strdup(buf);
878                 if (!cmd)
879                         return -ENOMEM;
880         }
881         free(bflow->cmdline);
882         bflow->cmdline = cmd;
883
884         if (set_env) {
885                 ret = env_set("bootargs", bflow->cmdline);
886                 if (ret)
887                         return ret;
888         }
889
890         return 0;
891 }
892
893 int cmdline_get_arg(const char *cmdline, const char *arg, int *posp)
894 {
895         int ret;
896
897         ret = cmdline_set_arg(NULL, 1, cmdline, arg, NULL, posp);
898
899         return ret;
900 }
901
902 int bootflow_cmdline_get_arg(struct bootflow *bflow, const char *arg,
903                              const char **val)
904 {
905         int ret;
906         int pos;
907
908         ret = cmdline_get_arg(bflow->cmdline, arg, &pos);
909         if (ret < 0)
910                 return ret;
911         *val = bflow->cmdline + pos;
912
913         return ret;
914 }
915
916 int bootflow_cmdline_auto(struct bootflow *bflow, const char *arg)
917 {
918         struct serial_device_info info;
919         char buf[50];
920         int ret;
921
922         ret = serial_getinfo(gd->cur_serial_dev, &info);
923         if (ret)
924                 return ret;
925
926         *buf = '\0';
927         if (!strcmp("earlycon", arg)) {
928                 snprintf(buf, sizeof(buf),
929                          "uart8250,mmio32,%#lx,%dn8", info.addr,
930                          info.baudrate);
931         } else if (!strcmp("console", arg)) {
932                 snprintf(buf, sizeof(buf),
933                          "ttyS0,%dn8", info.baudrate);
934         }
935
936         if (!*buf) {
937                 printf("Unknown param '%s\n", arg);
938                 return -ENOENT;
939         }
940
941         ret = bootflow_cmdline_set_arg(bflow, arg, buf, true);
942         if (ret)
943                 return ret;
944
945         return 0;
946 }
This page took 0.079722 seconds and 4 git commands to generate.