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