1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * (C) Copyright 2000-2009 4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 5 */ 6 7/* 8 * Command Processor Table 9 */ 10 11#include <common.h> 12#include <compiler.h> 13#include <command.h> 14#include <console.h> 15#include <env.h> 16#include <image.h> 17#include <log.h> 18#include <mapmem.h> 19#include <asm/global_data.h> 20#include <linux/ctype.h> 21 22DECLARE_GLOBAL_DATA_PTR; 23 24/* 25 * Use puts() instead of printf() to avoid printf buffer overflow 26 * for long help messages 27 */ 28 29int _do_help(struct cmd_tbl *cmd_start, int cmd_items, struct cmd_tbl *cmdtp, 30 int flag, int argc, char *const argv[]) 31{ 32 int i; 33 int rcode = 0; 34 35 if (argc == 1) { /* show list of commands */ 36 struct cmd_tbl *cmd_array[cmd_items]; 37 int i, j, swaps; 38 39 /* Make array of commands from .uboot_cmd section */ 40 cmdtp = cmd_start; 41 for (i = 0; i < cmd_items; i++) { 42 cmd_array[i] = cmdtp++; 43 } 44 45 /* Sort command list (trivial bubble sort) */ 46 for (i = cmd_items - 1; i > 0; --i) { 47 swaps = 0; 48 for (j = 0; j < i; ++j) { 49 if (strcmp(cmd_array[j]->name, 50 cmd_array[j + 1]->name) > 0) { 51 struct cmd_tbl *tmp; 52 tmp = cmd_array[j]; 53 cmd_array[j] = cmd_array[j + 1]; 54 cmd_array[j + 1] = tmp; 55 ++swaps; 56 } 57 } 58 if (!swaps) 59 break; 60 } 61 62 /* print short help (usage) */ 63 for (i = 0; i < cmd_items; i++) { 64 const char *usage = cmd_array[i]->usage; 65 66 /* allow user abort */ 67 if (ctrlc()) 68 return 1; 69 if (usage == NULL) 70 continue; 71 printf("%-*s- %s\n", CFG_SYS_HELP_CMD_WIDTH, 72 cmd_array[i]->name, usage); 73 } 74 return 0; 75 } 76 /* 77 * command help (long version) 78 */ 79 for (i = 1; i < argc; ++i) { 80 cmdtp = find_cmd_tbl(argv[i], cmd_start, cmd_items); 81 if (cmdtp != NULL) { 82 rcode |= cmd_usage(cmdtp); 83 } else { 84 printf("Unknown command '%s' - try 'help' without arguments for list of all known commands\n\n", 85 argv[i]); 86 rcode = 1; 87 } 88 } 89 return rcode; 90} 91 92/* find command table entry for a command */ 93struct cmd_tbl *find_cmd_tbl(const char *cmd, struct cmd_tbl *table, 94 int table_len) 95{ 96#ifdef CONFIG_CMDLINE 97 struct cmd_tbl *cmdtp; 98 struct cmd_tbl *cmdtp_temp = table; /* Init value */ 99 const char *p; 100 int len; 101 int n_found = 0; 102 103 if (!cmd) 104 return NULL; 105 /* 106 * Some commands allow length modifiers (like "cp.b"); 107 * compare command name only until first dot. 108 */ 109 len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd); 110 111 for (cmdtp = table; cmdtp != table + table_len; cmdtp++) { 112 if (strncmp(cmd, cmdtp->name, len) == 0) { 113 if (len == strlen(cmdtp->name)) 114 return cmdtp; /* full match */ 115 116 cmdtp_temp = cmdtp; /* abbreviated command ? */ 117 n_found++; 118 } 119 } 120 if (n_found == 1) { /* exactly one match */ 121 return cmdtp_temp; 122 } 123#endif /* CONFIG_CMDLINE */ 124 125 return NULL; /* not found or ambiguous command */ 126} 127 128struct cmd_tbl *find_cmd(const char *cmd) 129{ 130 struct cmd_tbl *start = ll_entry_start(struct cmd_tbl, cmd); 131 const int len = ll_entry_count(struct cmd_tbl, cmd); 132 return find_cmd_tbl(cmd, start, len); 133} 134 135int cmd_usage(const struct cmd_tbl *cmdtp) 136{ 137 printf("%s - %s\n\n", cmdtp->name, cmdtp->usage); 138 139#ifdef CONFIG_SYS_LONGHELP 140 printf("Usage:\n%s ", cmdtp->name); 141 142 if (!cmdtp->help) { 143 puts ("- No additional help available.\n"); 144 return 1; 145 } 146 147 puts(cmdtp->help); 148 putc('\n'); 149#endif /* CONFIG_SYS_LONGHELP */ 150 return 1; 151} 152 153#ifdef CONFIG_AUTO_COMPLETE 154static char env_complete_buf[512]; 155 156int var_complete(int argc, char *const argv[], char last_char, int maxv, 157 char *cmdv[]) 158{ 159 int space; 160 161 space = last_char == '\0' || isblank(last_char); 162 163 if (space && argc == 1) 164 return env_complete("", maxv, cmdv, sizeof(env_complete_buf), 165 env_complete_buf, false); 166 167 if (!space && argc == 2) 168 return env_complete(argv[1], maxv, cmdv, 169 sizeof(env_complete_buf), 170 env_complete_buf, false); 171 172 return 0; 173} 174 175static int dollar_complete(int argc, char *const argv[], char last_char, 176 int maxv, char *cmdv[]) 177{ 178 /* Make sure the last argument starts with a $. */ 179 if (argc < 1 || argv[argc - 1][0] != '$' || 180 last_char == '\0' || isblank(last_char)) 181 return 0; 182 183 return env_complete(argv[argc - 1], maxv, cmdv, sizeof(env_complete_buf), 184 env_complete_buf, true); 185} 186 187/*************************************************************************************/ 188 189int complete_subcmdv(struct cmd_tbl *cmdtp, int count, int argc, 190 char *const argv[], char last_char, 191 int maxv, char *cmdv[]) 192{ 193#ifdef CONFIG_CMDLINE 194 const struct cmd_tbl *cmdend = cmdtp + count; 195 const char *p; 196 int len, clen; 197 int n_found = 0; 198 const char *cmd; 199 200 /* sanity? */ 201 if (maxv < 2) 202 return -2; 203 204 cmdv[0] = NULL; 205 206 if (argc == 0) { 207 /* output full list of commands */ 208 for (; cmdtp != cmdend; cmdtp++) { 209 if (n_found >= maxv - 2) { 210 cmdv[n_found++] = "..."; 211 break; 212 } 213 cmdv[n_found++] = cmdtp->name; 214 } 215 cmdv[n_found] = NULL; 216 return n_found; 217 } 218 219 /* more than one arg or one but the start of the next */ 220 if (argc > 1 || last_char == '\0' || isblank(last_char)) { 221 cmdtp = find_cmd_tbl(argv[0], cmdtp, count); 222 if (cmdtp == NULL || cmdtp->complete == NULL) { 223 cmdv[0] = NULL; 224 return 0; 225 } 226 return (*cmdtp->complete)(argc, argv, last_char, maxv, cmdv); 227 } 228 229 cmd = argv[0]; 230 /* 231 * Some commands allow length modifiers (like "cp.b"); 232 * compare command name only until first dot. 233 */ 234 p = strchr(cmd, '.'); 235 if (p == NULL) 236 len = strlen(cmd); 237 else 238 len = p - cmd; 239 240 /* return the partial matches */ 241 for (; cmdtp != cmdend; cmdtp++) { 242 243 clen = strlen(cmdtp->name); 244 if (clen < len) 245 continue; 246 247 if (memcmp(cmd, cmdtp->name, len) != 0) 248 continue; 249 250 /* too many! */ 251 if (n_found >= maxv - 2) { 252 cmdv[n_found++] = "..."; 253 break; 254 } 255 256 cmdv[n_found++] = cmdtp->name; 257 } 258 259 cmdv[n_found] = NULL; 260 return n_found; 261#else 262 return 0; 263#endif 264} 265 266static int complete_cmdv(int argc, char *const argv[], char last_char, 267 int maxv, char *cmdv[]) 268{ 269#ifdef CONFIG_CMDLINE 270 return complete_subcmdv(ll_entry_start(struct cmd_tbl, cmd), 271 ll_entry_count(struct cmd_tbl, cmd), argc, argv, 272 last_char, maxv, cmdv); 273#else 274 return 0; 275#endif 276} 277 278static int make_argv(char *s, int argvsz, char *argv[]) 279{ 280 int argc = 0; 281 282 /* split into argv */ 283 while (argc < argvsz - 1) { 284 285 /* skip any white space */ 286 while (isblank(*s)) 287 ++s; 288 289 if (*s == '\0') /* end of s, no more args */ 290 break; 291 292 argv[argc++] = s; /* begin of argument string */ 293 294 /* find end of string */ 295 while (*s && !isblank(*s)) 296 ++s; 297 298 if (*s == '\0') /* end of s, no more args */ 299 break; 300 301 *s++ = '\0'; /* terminate current arg */ 302 } 303 argv[argc] = NULL; 304 305 return argc; 306} 307 308static void print_argv(const char *banner, const char *leader, const char *sep, 309 int linemax, char *const argv[]) 310{ 311 int ll = leader != NULL ? strlen(leader) : 0; 312 int sl = sep != NULL ? strlen(sep) : 0; 313 int len, i; 314 315 if (banner) { 316 puts("\n"); 317 puts(banner); 318 } 319 320 i = linemax; /* force leader and newline */ 321 while (*argv != NULL) { 322 len = strlen(*argv) + sl; 323 if (i + len >= linemax) { 324 puts("\n"); 325 if (leader) 326 puts(leader); 327 i = ll - sl; 328 } else if (sep) 329 puts(sep); 330 puts(*argv++); 331 i += len; 332 } 333 printf("\n"); 334} 335 336static int find_common_prefix(char *const argv[]) 337{ 338 int i, len; 339 char *anchor, *s, *t; 340 341 if (*argv == NULL) 342 return 0; 343 344 /* begin with max */ 345 anchor = *argv++; 346 len = strlen(anchor); 347 while ((t = *argv++) != NULL) { 348 s = anchor; 349 for (i = 0; i < len; i++, t++, s++) { 350 if (*t != *s) 351 break; 352 } 353 len = s - anchor; 354 } 355 return len; 356} 357 358int cmd_auto_complete(const char *const prompt, char *buf, int *np, int *colp) 359{ 360 char tmp_buf[CONFIG_SYS_CBSIZE + 1]; /* copy of console I/O buffer */ 361 int n = *np, col = *colp; 362 char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */ 363 char *cmdv[20]; 364 char *s, *t; 365 const char *sep; 366 int i, j, k, len, seplen, argc; 367 int cnt; 368 char last_char; 369#ifdef CONFIG_CMDLINE_PS_SUPPORT 370 const char *ps_prompt = env_get("PS1"); 371#else 372 const char *ps_prompt = CONFIG_SYS_PROMPT; 373#endif 374 375 if (strcmp(prompt, ps_prompt) != 0) 376 return 0; /* not in normal console */ 377 378 cnt = strlen(buf); 379 if (cnt >= 1) 380 last_char = buf[cnt - 1]; 381 else 382 last_char = '\0'; 383 384 /* copy to secondary buffer which will be affected */ 385 strcpy(tmp_buf, buf); 386 387 /* separate into argv */ 388 argc = make_argv(tmp_buf, sizeof(argv)/sizeof(argv[0]), argv); 389 390 /* first try a $ completion */ 391 i = dollar_complete(argc, argv, last_char, 392 sizeof(cmdv) / sizeof(cmdv[0]), cmdv); 393 if (!i) { 394 /* do the completion and return the possible completions */ 395 i = complete_cmdv(argc, argv, last_char, 396 sizeof(cmdv) / sizeof(cmdv[0]), cmdv); 397 } 398 399 /* no match; bell and out */ 400 if (i == 0) { 401 if (argc > 1) /* allow tab for non command */ 402 return 0; 403 putc('\a'); 404 return 1; 405 } 406 407 s = NULL; 408 len = 0; 409 sep = NULL; 410 seplen = 0; 411 if (i == 1) { /* one match; perfect */ 412 if (last_char != '\0' && !isblank(last_char)) 413 k = strlen(argv[argc - 1]); 414 else 415 k = 0; 416 417 s = cmdv[0] + k; 418 len = strlen(s); 419 sep = " "; 420 seplen = 1; 421 } else if (i > 1 && (j = find_common_prefix(cmdv)) != 0) { /* more */ 422 if (last_char != '\0' && !isblank(last_char)) 423 k = strlen(argv[argc - 1]); 424 else 425 k = 0; 426 427 j -= k; 428 if (j > 0) { 429 s = cmdv[0] + k; 430 len = j; 431 } 432 } 433 434 if (s != NULL) { 435 k = len + seplen; 436 /* make sure it fits */ 437 if (n + k >= CONFIG_SYS_CBSIZE - 2) { 438 putc('\a'); 439 return 1; 440 } 441 442 t = buf + cnt; 443 for (i = 0; i < len; i++) 444 *t++ = *s++; 445 if (sep != NULL) 446 for (i = 0; i < seplen; i++) 447 *t++ = sep[i]; 448 *t = '\0'; 449 n += k; 450 col += k; 451 puts(t - k); 452 if (sep == NULL) 453 putc('\a'); 454 *np = n; 455 *colp = col; 456 } else { 457 print_argv(NULL, " ", " ", 78, cmdv); 458 459 puts(prompt); 460 puts(buf); 461 } 462 return 1; 463} 464 465#endif 466 467#ifdef CMD_DATA_SIZE 468int cmd_get_data_size(const char *arg, int default_size) 469{ 470 /* Check for a size specification .b, .w or .l. 471 */ 472 int len = strlen(arg); 473 if (len >= 2 && arg[len-2] == '.') { 474 switch (arg[len-1]) { 475 case 'b': 476 return 1; 477 case 'w': 478 return 2; 479 case 'l': 480 return 4; 481 case 's': 482 return CMD_DATA_SIZE_STR; 483 case 'q': 484 if (MEM_SUPPORT_64BIT_DATA) 485 return 8; 486 /* no break */ 487 default: 488 return CMD_DATA_SIZE_ERR; 489 } 490 } 491 return default_size; 492} 493#endif 494 495void fixup_cmdtable(struct cmd_tbl *cmdtp, int size) 496{ 497 int i; 498 499 if (gd->reloc_off == 0) 500 return; 501 502 for (i = 0; i < size; i++) { 503 ulong addr; 504 505 addr = (ulong)(cmdtp->cmd_rep) + gd->reloc_off; 506 cmdtp->cmd_rep = 507 (int (*)(struct cmd_tbl *, int, int, 508 char * const [], int *))addr; 509 510 addr = (ulong)(cmdtp->cmd) + gd->reloc_off; 511#ifdef DEBUG_COMMANDS 512 printf("Command \"%s\": 0x%08lx => 0x%08lx\n", 513 cmdtp->name, (ulong)(cmdtp->cmd), addr); 514#endif 515 cmdtp->cmd = (int (*)(struct cmd_tbl *, int, int, 516 char *const []))addr; 517 addr = (ulong)(cmdtp->name) + gd->reloc_off; 518 cmdtp->name = (char *)addr; 519 if (cmdtp->usage) { 520 addr = (ulong)(cmdtp->usage) + gd->reloc_off; 521 cmdtp->usage = (char *)addr; 522 } 523#ifdef CONFIG_SYS_LONGHELP 524 if (cmdtp->help) { 525 addr = (ulong)(cmdtp->help) + gd->reloc_off; 526 cmdtp->help = (char *)addr; 527 } 528#endif 529#ifdef CONFIG_AUTO_COMPLETE 530 if (cmdtp->complete) { 531 addr = (ulong)(cmdtp->complete) + gd->reloc_off; 532 cmdtp->complete = 533 (int (*)(int, char * const [], char, int, char * []))addr; 534 } 535#endif 536 cmdtp++; 537 } 538} 539 540int cmd_always_repeatable(struct cmd_tbl *cmdtp, int flag, int argc, 541 char *const argv[], int *repeatable) 542{ 543 *repeatable = 1; 544 545 return cmdtp->cmd(cmdtp, flag, argc, argv); 546} 547 548int cmd_never_repeatable(struct cmd_tbl *cmdtp, int flag, int argc, 549 char *const argv[], int *repeatable) 550{ 551 *repeatable = 0; 552 553 return cmdtp->cmd(cmdtp, flag, argc, argv); 554} 555 556int cmd_discard_repeatable(struct cmd_tbl *cmdtp, int flag, int argc, 557 char *const argv[]) 558{ 559 int repeatable; 560 561 return cmdtp->cmd_rep(cmdtp, flag, argc, argv, &repeatable); 562} 563 564/** 565 * Call a command function. This should be the only route in U-Boot to call 566 * a command, so that we can track whether we are waiting for input or 567 * executing a command. 568 * 569 * @param cmdtp Pointer to the command to execute 570 * @param flag Some flags normally 0 (see CMD_FLAG_.. above) 571 * @param argc Number of arguments (arg 0 must be the command text) 572 * @param argv Arguments 573 * @param repeatable Can the command be repeated 574 * Return: 0 if command succeeded, else non-zero (CMD_RET_...) 575 */ 576static int cmd_call(struct cmd_tbl *cmdtp, int flag, int argc, 577 char *const argv[], int *repeatable) 578{ 579 int result; 580 581 result = cmdtp->cmd_rep(cmdtp, flag, argc, argv, repeatable); 582 if (result) 583 debug("Command failed, result=%d\n", result); 584 return result; 585} 586 587enum command_ret_t cmd_process(int flag, int argc, char *const argv[], 588 int *repeatable, ulong *ticks) 589{ 590 enum command_ret_t rc = CMD_RET_SUCCESS; 591 struct cmd_tbl *cmdtp; 592 593#if defined(CONFIG_SYS_XTRACE) 594 char *xtrace; 595 596 xtrace = env_get("xtrace"); 597 if (xtrace) { 598 puts("+"); 599 for (int i = 0; i < argc; i++) { 600 puts(" "); 601 puts(argv[i]); 602 } 603 puts("\n"); 604 } 605#endif 606 607 /* Look up command in command table */ 608 cmdtp = find_cmd(argv[0]); 609 if (cmdtp == NULL) { 610 printf("Unknown command '%s' - try 'help'\n", argv[0]); 611 return 1; 612 } 613 614 /* found - check max args */ 615 if (argc > cmdtp->maxargs) 616 rc = CMD_RET_USAGE; 617 618#if defined(CONFIG_CMD_BOOTD) 619 /* avoid "bootd" recursion */ 620 else if (cmdtp->cmd == do_bootd) { 621 if (flag & CMD_FLAG_BOOTD) { 622 puts("'bootd' recursion detected\n"); 623 rc = CMD_RET_FAILURE; 624 } else { 625 flag |= CMD_FLAG_BOOTD; 626 } 627 } 628#endif 629 630 /* If OK so far, then do the command */ 631 if (!rc) { 632 int newrep; 633 634 if (ticks) 635 *ticks = get_timer(0); 636 rc = cmd_call(cmdtp, flag, argc, argv, &newrep); 637 if (ticks) 638 *ticks = get_timer(*ticks); 639 *repeatable &= newrep; 640 } 641 if (rc == CMD_RET_USAGE) 642 rc = cmd_usage(cmdtp); 643 return rc; 644} 645 646int cmd_process_error(struct cmd_tbl *cmdtp, int err) 647{ 648 if (err == CMD_RET_USAGE) 649 return CMD_RET_USAGE; 650 651 if (err) { 652 printf("Command '%s' failed: Error %d\n", cmdtp->name, err); 653 return CMD_RET_FAILURE; 654 } 655 656 return CMD_RET_SUCCESS; 657} 658 659int cmd_source_script(ulong addr, const char *fit_uname, const char *confname) 660{ 661 char *data; 662 void *buf; 663 uint len; 664 int ret; 665 666 buf = map_sysmem(addr, 0); 667 ret = image_locate_script(buf, 0, fit_uname, confname, &data, &len); 668 unmap_sysmem(buf); 669 if (ret) 670 return CMD_RET_FAILURE; 671 672 debug("** Script length: %d\n", len); 673 return run_command_list(data, len, 0); 674} 675