Enable network on VF2 board
[platform/kernel/u-boot.git] / boot / bootflow.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2021 Google LLC
4  * Written by Simon Glass <sjg@chromium.org>
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 <serial.h>
18 #include <dm/device-internal.h>
19 #include <dm/uclass-internal.h>
20
21 /* error codes used to signal running out of things */
22 enum {
23         BF_NO_MORE_PARTS        = -ESHUTDOWN,
24         BF_NO_MORE_DEVICES      = -ENODEV,
25 };
26
27 /**
28  * bootflow_state - name for each state
29  *
30  * See enum bootflow_state_t for what each of these means
31  */
32 static const char *const bootflow_state[BOOTFLOWST_COUNT] = {
33         "base",
34         "media",
35         "part",
36         "fs",
37         "file",
38         "ready",
39 };
40
41 const char *bootflow_state_get_name(enum bootflow_state_t state)
42 {
43         /* This doesn't need to be a useful name, since it will never occur */
44         if (state < 0 || state >= BOOTFLOWST_COUNT)
45                 return "?";
46
47         return bootflow_state[state];
48 }
49
50 int bootflow_first_glob(struct bootflow **bflowp)
51 {
52         struct bootstd_priv *std;
53         int ret;
54
55         ret = bootstd_get_priv(&std);
56         if (ret)
57                 return ret;
58
59         if (list_empty(&std->glob_head))
60                 return -ENOENT;
61
62         *bflowp = list_first_entry(&std->glob_head, struct bootflow,
63                                    glob_node);
64
65         return 0;
66 }
67
68 int bootflow_next_glob(struct bootflow **bflowp)
69 {
70         struct bootstd_priv *std;
71         struct bootflow *bflow = *bflowp;
72         int ret;
73
74         ret = bootstd_get_priv(&std);
75         if (ret)
76                 return ret;
77
78         *bflowp = NULL;
79
80         if (list_is_last(&bflow->glob_node, &std->glob_head))
81                 return -ENOENT;
82
83         *bflowp = list_entry(bflow->glob_node.next, struct bootflow, glob_node);
84
85         return 0;
86 }
87
88 void bootflow_iter_init(struct bootflow_iter *iter, int flags)
89 {
90         memset(iter, '\0', sizeof(*iter));
91         iter->first_glob_method = -1;
92         iter->flags = flags;
93
94         /* remember the first bootdevs we see */
95         iter->max_devs = BOOTFLOW_MAX_USED_DEVS;
96 }
97
98 void bootflow_iter_uninit(struct bootflow_iter *iter)
99 {
100         free(iter->method_order);
101 }
102
103 int bootflow_iter_drop_bootmeth(struct bootflow_iter *iter,
104                                 const struct udevice *bmeth)
105 {
106         /* We only support disabling the current bootmeth */
107         if (bmeth != iter->method || iter->cur_method >= iter->num_methods ||
108             iter->method_order[iter->cur_method] != bmeth)
109                 return -EINVAL;
110
111         memmove(&iter->method_order[iter->cur_method],
112                 &iter->method_order[iter->cur_method + 1],
113                 (iter->num_methods - iter->cur_method - 1) * sizeof(void *));
114
115         iter->num_methods--;
116
117         return 0;
118 }
119
120 /**
121  * bootflow_iter_set_dev() - switch to the next bootdev when iterating
122  *
123  * This sets iter->dev, records the device in the dev_used[] list and shows a
124  * message if required
125  *
126  * @iter: Iterator to update
127  * @dev: Bootdev to use, or NULL if there are no more
128  */
129 static void bootflow_iter_set_dev(struct bootflow_iter *iter,
130                                   struct udevice *dev, int method_flags)
131 {
132         struct bootmeth_uc_plat *ucp = dev_get_uclass_plat(iter->method);
133
134         log_debug("iter: Setting dev to %s, flags %x\n",
135                   dev ? dev->name : "(none)", method_flags);
136         iter->dev = dev;
137         iter->method_flags = method_flags;
138
139         if (IS_ENABLED(CONFIG_BOOTSTD_FULL)) {
140                 /* record the device for later */
141                 if (dev && iter->num_devs < iter->max_devs)
142                         iter->dev_used[iter->num_devs++] = dev;
143
144                 if ((iter->flags & (BOOTFLOWIF_SHOW | BOOTFLOWIF_SINGLE_DEV)) ==
145                     BOOTFLOWIF_SHOW) {
146                         if (dev)
147                                 printf("Scanning bootdev '%s':\n", dev->name);
148                         else if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) &&
149                                 ucp->flags & BOOTMETHF_GLOBAL)
150                                 printf("Scanning global bootmeth '%s':\n",
151                                 iter->method->name);
152                         else
153                                 printf("No more bootdevs\n");
154                 }
155         }
156 }
157
158 /**
159  * iter_incr() - Move to the next item (method, part, bootdev)
160  *
161  * Return: 0 if OK, BF_NO_MORE_DEVICES if there are no more bootdevs
162  */
163 static int iter_incr(struct bootflow_iter *iter)
164 {
165         struct udevice *dev;
166         bool inc_dev = true;
167         bool global;
168         int ret;
169
170         log_debug("entry: err=%d\n", iter->err);
171         global = iter->doing_global;
172
173         if (iter->err == BF_NO_MORE_DEVICES)
174                 return BF_NO_MORE_DEVICES;
175
176         if (iter->err != BF_NO_MORE_PARTS) {
177                 /* Get the next boothmethod */
178                 if (++iter->cur_method < iter->num_methods) {
179                         iter->method = iter->method_order[iter->cur_method];
180                         return 0;
181                 }
182
183                 /*
184                  * If we have finished scanning the global bootmeths, start the
185                  * normal bootdev scan
186                  */
187                 if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && global) {
188                         iter->num_methods = iter->first_glob_method;
189                         iter->doing_global = false;
190
191                         /*
192                          * Don't move to the next dev as we haven't tried this
193                          * one yet!
194                          */
195                         inc_dev = false;
196                 }
197         }
198
199         /* No more bootmeths; start at the first one, and... */
200         iter->cur_method = 0;
201         iter->method = iter->method_order[iter->cur_method];
202
203         if (iter->err != BF_NO_MORE_PARTS) {
204                 /* ...select next partition  */
205                 if (++iter->part <= iter->max_part)
206                         return 0;
207         }
208
209         /* No more partitions; start at the first one and... */
210         iter->part = 0;
211
212         /*
213          * Note: as far as we know, there is no partition table on the next
214          * bootdev, so set max_part to 0 until we discover otherwise. See
215          * bootdev_find_in_blk() for where this is set.
216          */
217         iter->max_part = 0;
218
219         /* ...select next bootdev */
220         if (iter->flags & BOOTFLOWIF_SINGLE_DEV) {
221                 ret = -ENOENT;
222         } else {
223                 int method_flags;
224
225                 ret = 0;
226                 dev = iter->dev;
227                 log_debug("inc_dev=%d\n", inc_dev);
228                 if (!inc_dev) {
229                         ret = bootdev_setup_iter(iter, NULL, &dev,
230                                                  &method_flags);
231                 } else if (IS_ENABLED(CONFIG_BOOTSTD_FULL) &&
232                            (iter->flags & BOOTFLOWIF_SINGLE_UCLASS)) {
233                         /* Move to the next bootdev in this uclass */
234                         uclass_find_next_device(&dev);
235                         if (!dev) {
236                                 log_debug("finished uclass %s\n",
237                                           dev_get_uclass_name(dev));
238                                 ret = -ENODEV;
239                         }
240                 } else if (IS_ENABLED(CONFIG_BOOTSTD_FULL) &&
241                            iter->flags & BOOTFLOWIF_SINGLE_MEDIA) {
242                         log_debug("next in single\n");
243                         method_flags = 0;
244                         do {
245                                 /*
246                                  * Move to the next bootdev child of this media
247                                  * device. This ensures that we cover all the
248                                  * available SCSI IDs and LUNs.
249                                  */
250                                 device_find_next_child(&dev);
251                                 log_debug("- next %s\n",
252                                         dev ? dev->name : "(none)");
253                         } while (dev && device_get_uclass_id(dev) !=
254                                 UCLASS_BOOTDEV);
255                         if (!dev) {
256                                 log_debug("finished uclass %s\n",
257                                           dev_get_uclass_name(dev));
258                                 ret = -ENODEV;
259                         }
260                 } else {
261                         log_debug("labels %p\n", iter->labels);
262                         if (iter->labels) {
263                                 ret = bootdev_next_label(iter, &dev,
264                                                          &method_flags);
265                         } else {
266                                 ret = bootdev_next_prio(iter, &dev);
267                                 method_flags = 0;
268                         }
269                 }
270                 log_debug("ret=%d, dev=%p %s\n", ret, dev,
271                           dev ? dev->name : "none");
272                 if (ret) {
273                         bootflow_iter_set_dev(iter, NULL, 0);
274                 } else {
275                         /*
276                          * Probe the bootdev. This does not probe any attached
277                          * block device, since they are siblings
278                          */
279                         ret = device_probe(dev);
280                         log_debug("probe %s %d\n", dev->name, ret);
281                         if (!log_msg_ret("probe", ret))
282                                 bootflow_iter_set_dev(iter, dev, method_flags);
283                 }
284         }
285
286         /* if there are no more bootdevs, give up */
287         if (ret)
288                 return log_msg_ret("incr", BF_NO_MORE_DEVICES);
289
290         return 0;
291 }
292
293 /**
294  * bootflow_check() - Check if a bootflow can be obtained
295  *
296  * @iter: Provides part, bootmeth to use
297  * @bflow: Bootflow to update on success
298  * Return: 0 if OK, -ENOSYS if there is no bootflow support on this device,
299  *      BF_NO_MORE_PARTS if there are no more partitions on bootdev
300  */
301 static int bootflow_check(struct bootflow_iter *iter, struct bootflow *bflow)
302 {
303         struct udevice *dev;
304         int ret;
305
306         if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && iter->doing_global) {
307                 bootflow_iter_set_dev(iter, NULL, 0);
308                 ret = bootmeth_get_bootflow(iter->method, bflow);
309                 if (ret)
310                         return log_msg_ret("glob", ret);
311
312                 return 0;
313         }
314
315         dev = iter->dev;
316         ret = bootdev_get_bootflow(dev, iter, bflow);
317
318         /* If we got a valid bootflow, return it */
319         if (!ret) {
320                 log_debug("Bootdev '%s' part %d method '%s': Found bootflow\n",
321                           dev->name, iter->part, iter->method->name);
322                 return 0;
323         }
324
325         /* Unless there is nothing more to try, move to the next device */
326         else if (ret != BF_NO_MORE_PARTS && ret != -ENOSYS) {
327                 log_debug("Bootdev '%s' part %d method '%s': Error %d\n",
328                           dev->name, iter->part, iter->method->name, ret);
329                 /*
330                  * For 'all' we return all bootflows, even
331                  * those with errors
332                  */
333                 if (iter->flags & BOOTFLOWIF_ALL)
334                         return log_msg_ret("all", ret);
335         }
336         if (ret)
337                 return log_msg_ret("check", ret);
338
339         return 0;
340 }
341
342 int bootflow_scan_first(struct udevice *dev, const char *label,
343                         struct bootflow_iter *iter, int flags,
344                         struct bootflow *bflow)
345 {
346         int ret;
347
348         if (dev || label)
349                 flags |= BOOTFLOWIF_SKIP_GLOBAL;
350         bootflow_iter_init(iter, flags);
351
352         /*
353          * Set up the ordering of bootmeths. This sets iter->doing_global and
354          * iter->first_glob_method if we are starting with the global bootmeths
355          */
356         ret = bootmeth_setup_iter_order(iter, !(flags & BOOTFLOWIF_SKIP_GLOBAL));
357         if (ret)
358                 return log_msg_ret("obmeth", -ENODEV);
359
360         /* Find the first bootmeth (there must be at least one!) */
361         iter->method = iter->method_order[iter->cur_method];
362
363         if (!IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) || !iter->doing_global) {
364                 struct udevice *dev = NULL;
365                 int method_flags;
366
367                 ret = bootdev_setup_iter(iter, label, &dev, &method_flags);
368                 if (ret)
369                         return log_msg_ret("obdev", -ENODEV);
370
371                 bootflow_iter_set_dev(iter, dev, method_flags);
372         }
373
374         ret = bootflow_check(iter, bflow);
375         if (ret) {
376                 log_debug("check - ret=%d\n", ret);
377                 if (ret != BF_NO_MORE_PARTS && ret != -ENOSYS) {
378                         if (iter->flags & BOOTFLOWIF_ALL)
379                                 return log_msg_ret("all", ret);
380                 }
381                 iter->err = ret;
382                 ret = bootflow_scan_next(iter, bflow);
383                 if (ret)
384                         return log_msg_ret("get", ret);
385         }
386
387         return 0;
388 }
389
390 int bootflow_scan_next(struct bootflow_iter *iter, struct bootflow *bflow)
391 {
392         int ret;
393
394         do {
395                 ret = iter_incr(iter);
396                 log_debug("iter_incr: ret=%d\n", ret);
397                 if (ret == BF_NO_MORE_DEVICES)
398                         return log_msg_ret("done", ret);
399
400                 if (!ret) {
401                         ret = bootflow_check(iter, bflow);
402                         log_debug("check - ret=%d\n", ret);
403                         if (!ret)
404                                 return 0;
405                         iter->err = ret;
406                         if (ret != BF_NO_MORE_PARTS && ret != -ENOSYS) {
407                                 if (iter->flags & BOOTFLOWIF_ALL)
408                                         return log_msg_ret("all", ret);
409                         }
410                 } else {
411                         log_debug("incr failed, err=%d\n", ret);
412                         iter->err = ret;
413                 }
414
415         } while (1);
416 }
417
418 void bootflow_init(struct bootflow *bflow, struct udevice *bootdev,
419                    struct udevice *meth)
420 {
421         memset(bflow, '\0', sizeof(*bflow));
422         bflow->dev = bootdev;
423         bflow->method = meth;
424         bflow->state = BOOTFLOWST_BASE;
425 }
426
427 void bootflow_free(struct bootflow *bflow)
428 {
429         free(bflow->name);
430         free(bflow->subdir);
431         free(bflow->fname);
432         free(bflow->buf);
433         free(bflow->os_name);
434         free(bflow->fdt_fname);
435 }
436
437 void bootflow_remove(struct bootflow *bflow)
438 {
439         if (bflow->dev)
440                 list_del(&bflow->bm_node);
441         list_del(&bflow->glob_node);
442
443         bootflow_free(bflow);
444         free(bflow);
445 }
446
447 int bootflow_boot(struct bootflow *bflow)
448 {
449         int ret;
450
451         if (bflow->state != BOOTFLOWST_READY)
452                 return log_msg_ret("load", -EPROTO);
453
454         ret = bootmeth_boot(bflow->method, bflow);
455         if (ret)
456                 return log_msg_ret("boot", ret);
457
458         /*
459          * internal error, should not get here since we should have booted
460          * something or returned an error
461          */
462
463         return log_msg_ret("end", -EFAULT);
464 }
465
466 int bootflow_run_boot(struct bootflow_iter *iter, struct bootflow *bflow)
467 {
468         int ret;
469
470         printf("** Booting bootflow '%s' with %s\n", bflow->name,
471                bflow->method->name);
472         if (IS_ENABLED(CONFIG_OF_HAS_PRIOR_STAGE) &&
473             (bflow->flags & BOOTFLOWF_USE_PRIOR_FDT))
474                 printf("Using prior-stage device tree\n");
475         ret = bootflow_boot(bflow);
476         if (!IS_ENABLED(CONFIG_BOOTSTD_FULL)) {
477                 printf("Boot failed (err=%d)\n", ret);
478                 return ret;
479         }
480
481         switch (ret) {
482         case -EPROTO:
483                 printf("Bootflow not loaded (state '%s')\n",
484                        bootflow_state_get_name(bflow->state));
485                 break;
486         case -ENOSYS:
487                 printf("Boot method '%s' not supported\n", bflow->method->name);
488                 break;
489         case -ENOTSUPP:
490                 /* Disable this bootflow for this iteration */
491                 if (iter) {
492                         int ret2;
493
494                         ret2 = bootflow_iter_drop_bootmeth(iter, bflow->method);
495                         if (!ret2) {
496                                 printf("Boot method '%s' failed and will not be retried\n",
497                                        bflow->method->name);
498                         }
499                 }
500
501                 break;
502         default:
503                 printf("Boot failed (err=%d)\n", ret);
504                 break;
505         }
506
507         return ret;
508 }
509
510 int bootflow_iter_check_blk(const struct bootflow_iter *iter)
511 {
512         const struct udevice *media = dev_get_parent(iter->dev);
513         enum uclass_id id = device_get_uclass_id(media);
514
515         log_debug("uclass %d: %s\n", id, uclass_get_name(id));
516         if (id != UCLASS_ETH && id != UCLASS_BOOTSTD && id != UCLASS_QFW)
517                 return 0;
518
519         return -ENOTSUPP;
520 }
521
522 int bootflow_iter_check_sf(const struct bootflow_iter *iter)
523 {
524         const struct udevice *media = dev_get_parent(iter->dev);
525         enum uclass_id id = device_get_uclass_id(media);
526
527         log_debug("uclass %d: %s\n", id, uclass_get_name(id));
528         if (id == UCLASS_SPI_FLASH)
529                 return 0;
530
531         return -ENOTSUPP;
532 }
533
534 int bootflow_iter_check_net(const struct bootflow_iter *iter)
535 {
536         const struct udevice *media = dev_get_parent(iter->dev);
537         enum uclass_id id = device_get_uclass_id(media);
538
539         log_debug("uclass %d: %s\n", id, uclass_get_name(id));
540         if (id == UCLASS_ETH)
541                 return 0;
542
543         return -ENOTSUPP;
544 }
545
546 int bootflow_iter_check_system(const struct bootflow_iter *iter)
547 {
548         const struct udevice *media = dev_get_parent(iter->dev);
549         enum uclass_id id = device_get_uclass_id(media);
550
551         log_debug("uclass %d: %s\n", id, uclass_get_name(id));
552         if (id == UCLASS_BOOTSTD)
553                 return 0;
554
555         return -ENOTSUPP;
556 }
557
558 /**
559  * bootflow_cmdline_set() - Set the command line for a bootflow
560  *
561  * @value: New command-line string
562  * Returns 0 if OK, -ENOENT if no current bootflow, -ENOMEM if out of memory
563  */
564 int bootflow_cmdline_set(struct bootflow *bflow, const char *value)
565 {
566         char *cmdline = NULL;
567
568         if (value) {
569                 cmdline = strdup(value);
570                 if (!cmdline)
571                         return -ENOMEM;
572         }
573
574         free(bflow->cmdline);
575         bflow->cmdline = cmdline;
576
577         return 0;
578 }
579
580 #ifdef CONFIG_BOOTSTD_FULL
581 /**
582  * on_bootargs() - Update the cmdline of a bootflow
583  */
584 static int on_bootargs(const char *name, const char *value, enum env_op op,
585                        int flags)
586 {
587         struct bootstd_priv *std;
588         struct bootflow *bflow;
589         int ret;
590
591         ret = bootstd_get_priv(&std);
592         if (ret)
593                 return 0;
594         bflow = std->cur_bootflow;
595         if (!bflow)
596                 return 0;
597
598         switch (op) {
599         case env_op_create:
600         case env_op_overwrite:
601                 ret = bootflow_cmdline_set(bflow, value);
602                 if (ret && ret != ENOENT)
603                         return 1;
604                 return 0;
605         case env_op_delete:
606                 bootflow_cmdline_set(bflow, NULL);
607                 fallthrough;
608         default:
609                 return 0;
610         }
611 }
612 U_BOOT_ENV_CALLBACK(bootargs, on_bootargs);
613 #endif
614
615 /**
616  * copy_in() - Copy a string into a cmdline buffer
617  *
618  * @buf: Buffer to copy into
619  * @end: End of buffer (pointer to char after the end)
620  * @arg: String to copy from
621  * @len: Number of chars to copy from @arg (note that this is not usually the
622  * sane as strlen(arg) since the string may contain following arguments)
623  * @new_val: Value to put after arg, or BOOTFLOWCL_EMPTY to use an empty value
624  * with no '=' sign
625  * Returns: Number of chars written to @buf
626  */
627 static int copy_in(char *buf, char *end, const char *arg, int len,
628                    const char *new_val)
629 {
630         char *to = buf;
631
632         /* copy the arg name */
633         if (to + len >= end)
634                 return -E2BIG;
635         memcpy(to, arg, len);
636         to += len;
637
638         if (new_val == BOOTFLOWCL_EMPTY) {
639                 /* no value */
640         } else {
641                 bool need_quote = strchr(new_val, ' ');
642                 len = strlen(new_val);
643
644                 /* need space for value, equals sign and maybe two quotes */
645                 if (to + 1 + (need_quote ? 2 : 0) + len >= end)
646                         return -E2BIG;
647                 *to++ = '=';
648                 if (need_quote)
649                         *to++ = '"';
650                 memcpy(to, new_val, len);
651                 to += len;
652                 if (need_quote)
653                         *to++ = '"';
654         }
655
656         return to - buf;
657 }
658
659 int cmdline_set_arg(char *buf, int maxlen, const char *cmdline,
660                     const char *set_arg, const char *new_val, int *posp)
661 {
662         bool found_arg = false;
663         const char *from;
664         char *to, *end;
665         int set_arg_len;
666         char empty = '\0';
667         int ret;
668
669         from = cmdline ?: &empty;
670
671         /* check if the value has quotes inside */
672         if (new_val && new_val != BOOTFLOWCL_EMPTY && strchr(new_val, '"'))
673                 return -EBADF;
674
675         set_arg_len = strlen(set_arg);
676         for (to = buf, end = buf + maxlen; *from;) {
677                 const char *val, *arg_end, *val_end, *p;
678                 bool in_quote;
679
680                 if (to >= end)
681                         return -E2BIG;
682                 while (*from == ' ')
683                         from++;
684                 if (!*from)
685                         break;
686
687                 /* find the end of this arg */
688                 val = NULL;
689                 arg_end = NULL;
690                 val_end = NULL;
691                 in_quote = false;
692                 for (p = from;; p++) {
693                         if (in_quote) {
694                                 if (!*p)
695                                         return -EINVAL;
696                                 if (*p == '"')
697                                         in_quote = false;
698                                 continue;
699                         }
700                         if (*p == '=') {
701                                 arg_end = p;
702                                 val = p + 1;
703                         } else if (*p == '"') {
704                                 in_quote = true;
705                         } else if (!*p || *p == ' ') {
706                                 val_end = p;
707                                 if (!arg_end)
708                                         arg_end = p;
709                                 break;
710                         }
711                 }
712                 /*
713                  * At this point val_end points to the end of the value, or the
714                  * last char after the arg name, if there is no label.
715                  * arg_end is the char after the arg name
716                  * val points to the value, or NULL if there is none
717                  * char after the value.
718                  *
719                  *        fred=1234
720                  *        ^   ^^   ^
721                  *      from  ||   |
722                  *           / \    \
723                  *    arg_end  val   val_end
724                  */
725                 log_debug("from %s arg_end %ld val %ld val_end %ld\n", from,
726                           (long)(arg_end - from), (long)(val - from),
727                           (long)(val_end - from));
728
729                 if (to != buf) {
730                         if (to >= end)
731                                 return -E2BIG;
732                         *to++ = ' ';
733                 }
734
735                 /* if this is the target arg, update it */
736                 if (!strncmp(from, set_arg, arg_end - from)) {
737                         if (!buf) {
738                                 bool has_quote = val_end[-1] == '"';
739
740                                 /*
741                                  * exclude any start/end quotes from
742                                  * calculations
743                                  */
744                                 if (!val)
745                                         val = val_end;
746                                 *posp = val - cmdline + has_quote;
747                                 return val_end - val - 2 * has_quote;
748                         }
749                         found_arg = true;
750                         if (!new_val) {
751                                 /* delete this arg */
752                                 from = val_end + (*val_end == ' ');
753                                 log_debug("delete from: %s\n", from);
754                                 if (to != buf)
755                                         to--; /* drop the space we added */
756                                 continue;
757                         }
758
759                         ret = copy_in(to, end, from, arg_end - from, new_val);
760                         if (ret < 0)
761                                 return ret;
762                         to += ret;
763
764                 /* if not the target arg, copy it unchanged */
765                 } else if (to) {
766                         int len;
767
768                         len = val_end - from;
769                         if (to + len >= end)
770                                 return -E2BIG;
771                         memcpy(to, from, len);
772                         to += len;
773                 }
774                 from = val_end;
775         }
776
777         /* If we didn't find the arg, add it */
778         if (!found_arg) {
779                 /* trying to delete something that is not there */
780                 if (!new_val || !buf)
781                         return -ENOENT;
782                 if (to >= end)
783                         return -E2BIG;
784
785                 /* add a space to separate it from the previous arg */
786                 if (to != buf && to[-1] != ' ')
787                         *to++ = ' ';
788                 ret = copy_in(to, end, set_arg, set_arg_len, new_val);
789                 log_debug("ret=%d, to: %s buf: %s\n", ret, to, buf);
790                 if (ret < 0)
791                         return ret;
792                 to += ret;
793         }
794
795         /* delete any trailing space */
796         if (to > buf && to[-1] == ' ')
797                 to--;
798
799         if (to >= end)
800                 return -E2BIG;
801         *to++ = '\0';
802
803         return to - buf;
804 }
805
806 int bootflow_cmdline_set_arg(struct bootflow *bflow, const char *set_arg,
807                              const char *new_val, bool set_env)
808 {
809         char buf[2048];
810         char *cmd = NULL;
811         int ret;
812
813         ret = cmdline_set_arg(buf, sizeof(buf), bflow->cmdline, set_arg,
814                               new_val, NULL);
815         if (ret < 0)
816                 return ret;
817
818         ret = bootflow_cmdline_set(bflow, buf);
819         if (*buf) {
820                 cmd = strdup(buf);
821                 if (!cmd)
822                         return -ENOMEM;
823         }
824         free(bflow->cmdline);
825         bflow->cmdline = cmd;
826
827         if (set_env) {
828                 ret = env_set("bootargs", bflow->cmdline);
829                 if (ret)
830                         return ret;
831         }
832
833         return 0;
834 }
835
836 int cmdline_get_arg(const char *cmdline, const char *arg, int *posp)
837 {
838         int ret;
839
840         ret = cmdline_set_arg(NULL, 1, cmdline, arg, NULL, posp);
841
842         return ret;
843 }
844
845 int bootflow_cmdline_get_arg(struct bootflow *bflow, const char *arg,
846                              const char **val)
847 {
848         int ret;
849         int pos;
850
851         ret = cmdline_get_arg(bflow->cmdline, arg, &pos);
852         if (ret < 0)
853                 return ret;
854         *val = bflow->cmdline + pos;
855
856         return ret;
857 }
858
859 int bootflow_cmdline_auto(struct bootflow *bflow, const char *arg)
860 {
861         struct serial_device_info info;
862         char buf[50];
863         int ret;
864
865         ret = serial_getinfo(gd->cur_serial_dev, &info);
866         if (ret)
867                 return ret;
868
869         *buf = '\0';
870         if (!strcmp("earlycon", arg)) {
871                 snprintf(buf, sizeof(buf),
872                          "uart8250,mmio32,%#lx,%dn8", info.addr,
873                          info.baudrate);
874         } else if (!strcmp("console", arg)) {
875                 snprintf(buf, sizeof(buf),
876                          "ttyS0,%dn8", info.baudrate);
877         }
878
879         if (!*buf) {
880                 printf("Unknown param '%s\n", arg);
881                 return -ENOENT;
882         }
883
884         ret = bootflow_cmdline_set_arg(bflow, arg, buf, true);
885         if (ret)
886                 return ret;
887
888         return 0;
889 }