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