X-Git-Url: https://git.kernelconcepts.de/?a=blobdiff_plain;f=common%2Fmain.c;h=8b6f274fa25f723fd2553c1c61a2a32606a90f1a;hb=41364f0fbe1e550a3326dd4310e41adec5ce30d7;hp=046da6f23ca488d02f3fb900c48fc2b96caafb9e;hpb=1730edf76c54381475e2da11f75b1ce563c4e62c;p=karo-tx-uboot.git diff --git a/common/main.c b/common/main.c index 046da6f23c..8b6f274fa2 100644 --- a/common/main.c +++ b/common/main.c @@ -6,70 +6,48 @@ * (C) Copyright 2005 * JinHua Luo, GuangDong Linux Center, * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0+ */ /* #define DEBUG */ #include -#include #include -#ifdef CONFIG_MODEM_SUPPORT -#include /* for free() prototype */ -#endif - -#ifdef CFG_HUSH_PARSER +#include #include -#endif - +#include +#include #include +#include +#include +#include -#if defined(CONFIG_SILENT_CONSOLE) || defined(CONFIG_POST) || defined(CONFIG_CMDLINE_EDITING) DECLARE_GLOBAL_DATA_PTR; -#endif /* * Board-specific Platform code can reimplement show_boot_progress () if needed */ void inline __show_boot_progress (int val) {} -void inline show_boot_progress (int val) __attribute__((weak, alias("__show_boot_progress"))); - -#if defined(CONFIG_BOOT_RETRY_TIME) && defined(CONFIG_RESET_TO_RETRY) -extern int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]); /* for do_reset() prototype */ -#endif +void show_boot_progress (int val) __attribute__((weak, alias("__show_boot_progress"))); -extern int do_bootd (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]); +#define MAX_DELAY_STOP_STR 32 +#define DEBUG_PARSER 0 /* set to 1 to debug */ -#define MAX_DELAY_STOP_STR 32 +#define debug_parser(fmt, args...) \ + debug_cond(DEBUG_PARSER, fmt, ##args) -#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) -static int abortboot(int); +#ifndef DEBUG_BOOTKEYS +#define DEBUG_BOOTKEYS 0 #endif +#define debug_bootkeys(fmt, args...) \ + debug_cond(DEBUG_BOOTKEYS, fmt, ##args) -#undef DEBUG_PARSER - -char console_buffer[CFG_CBSIZE]; /* console I/O buffer */ +char console_buffer[CONFIG_SYS_CBSIZE + 1]; /* console I/O buffer */ static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen); -static char erase_seq[] = "\b \b"; /* erase sequence */ -static char tab_seq[] = " "; /* used to expand TABs */ +static const char erase_seq[] = "\b \b"; /* erase sequence */ +static const char tab_seq[] = " "; /* used to expand TABs */ #ifdef CONFIG_BOOT_RETRY_TIME static uint64_t endtime = 0; /* must be set, default is instant timeout */ @@ -89,12 +67,11 @@ extern void mdm_init(void); /* defined in board.c */ /*************************************************************************** * Watch for 'delay' seconds for autoboot stop or autoboot delay string. - * returns: 0 - no key string, allow autoboot - * 1 - got key string, abort + * returns: 0 - no key string, allow autoboot 1 - got key string, abort */ -#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) +#if defined(CONFIG_BOOTDELAY) # if defined(CONFIG_AUTOBOOT_KEYED) -static __inline__ int abortboot(int bootdelay) +static int abortboot_keyed(int bootdelay) { int abort = 0; uint64_t etime = endtick(bootdelay); @@ -115,8 +92,13 @@ static __inline__ int abortboot(int bootdelay) u_int presskey_max = 0; u_int i; +#ifndef CONFIG_ZERO_BOOTDELAY_CHECK + if (bootdelay == 0) + return 0; +#endif + # ifdef CONFIG_AUTOBOOT_PROMPT - printf(CONFIG_AUTOBOOT_PROMPT, bootdelay); + printf(CONFIG_AUTOBOOT_PROMPT); # endif # ifdef CONFIG_AUTOBOOT_DELAY_STR @@ -145,27 +127,36 @@ static __inline__ int abortboot(int bootdelay) presskey_max = presskey_max > delaykey[i].len ? presskey_max : delaykey[i].len; -# if DEBUG_BOOTKEYS - printf("%s key:<%s>\n", - delaykey[i].retry ? "delay" : "stop", - delaykey[i].str ? delaykey[i].str : "NULL"); -# endif + debug_bootkeys("%s key:<%s>\n", + delaykey[i].retry ? "delay" : "stop", + delaykey[i].str ? delaykey[i].str : "NULL"); } /* In order to keep up with incoming data, check timeout only * when catch up. */ - while (!abort && get_ticks() <= etime) { + do { + if (tstc()) { + if (presskey_len < presskey_max) { + presskey [presskey_len ++] = getc(); + } + else { + for (i = 0; i < presskey_max - 1; i ++) + presskey [i] = presskey [i + 1]; + + presskey [i] = getc(); + } + } + for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i ++) { if (delaykey[i].len > 0 && presskey_len >= delaykey[i].len && memcmp (presskey + presskey_len - delaykey[i].len, delaykey[i].str, delaykey[i].len) == 0) { -# if DEBUG_BOOTKEYS - printf("got %skey\n", - delaykey[i].retry ? "delay" : "stop"); -# endif + debug_bootkeys("got %skey\n", + delaykey[i].retry ? "delay" : + "stop"); # ifdef CONFIG_BOOT_RETRY_TIME /* don't retry auto boot */ @@ -175,23 +166,10 @@ static __inline__ int abortboot(int bootdelay) abort = 1; } } + } while (!abort && get_ticks() <= etime); - if (tstc()) { - if (presskey_len < presskey_max) { - presskey [presskey_len ++] = getc(); - } - else { - for (i = 0; i < presskey_max - 1; i ++) - presskey [i] = presskey [i + 1]; - - presskey [i] = getc(); - } - } - } -# if DEBUG_BOOTKEYS if (!abort) - puts("key timeout\n"); -# endif + debug_bootkeys("key timeout\n"); #ifdef CONFIG_SILENT_CONSOLE if (abort) @@ -207,14 +185,16 @@ static __inline__ int abortboot(int bootdelay) static int menukey = 0; #endif -static __inline__ int abortboot(int bootdelay) +static int abortboot_normal(int bootdelay) { int abort = 0; + unsigned long ts; #ifdef CONFIG_MENUPROMPT - printf(CONFIG_MENUPROMPT, bootdelay); + printf(CONFIG_MENUPROMPT); #else - printf("Hit any key to stop autoboot: %2d ", bootdelay); + if (bootdelay >= 0) + printf("Hit any key to stop autoboot: %2d ", bootdelay); #endif #if defined CONFIG_ZERO_BOOTDELAY_CHECK @@ -232,11 +212,10 @@ static __inline__ int abortboot(int bootdelay) #endif while ((bootdelay > 0) && (!abort)) { - int i; - --bootdelay; - /* delay 100 * 10ms */ - for (i=0; !abort && i<100; ++i) { + /* delay 1000 ms */ + ts = get_timer(0); + do { if (tstc()) { /* we got a key press */ abort = 1; /* don't auto boot */ bootdelay = 0; /* no more delay */ @@ -248,7 +227,7 @@ static __inline__ int abortboot(int bootdelay) break; } udelay(10000); - } + } while (!abort && get_timer(ts) < 1000); printf("\b\b\b%2d ", bootdelay); } @@ -263,106 +242,116 @@ static __inline__ int abortboot(int bootdelay) return abort; } # endif /* CONFIG_AUTOBOOT_KEYED */ -#endif /* CONFIG_BOOTDELAY >= 0 */ -/****************************************************************************/ - -void main_loop (void) +static int abortboot(int bootdelay) { -#ifndef CFG_HUSH_PARSER - static char lastcommand[CFG_CBSIZE] = { 0, }; - int len; - int rc = 1; - int flag; +#ifdef CONFIG_AUTOBOOT_KEYED + return abortboot_keyed(bootdelay); +#else + return abortboot_normal(bootdelay); #endif +} +#endif /* CONFIG_BOOTDELAY */ -#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) - char *s; - int bootdelay; -#endif -#ifdef CONFIG_PREBOOT - char *p; -#endif -#ifdef CONFIG_BOOTCOUNT_LIMIT - unsigned long bootcount = 0; - unsigned long bootlimit = 0; - char *bcs; - char bcs_set[16]; -#endif /* CONFIG_BOOTCOUNT_LIMIT */ +/* + * Runs the given boot command securely. Specifically: + * - Doesn't run the command with the shell (run_command or parse_string_outer), + * since that's a lot of code surface that an attacker might exploit. + * Because of this, we don't do any argument parsing--the secure boot command + * has to be a full-fledged u-boot command. + * - Doesn't check for keypresses before booting, since that could be a + * security hole; also disables Ctrl-C. + * - Doesn't allow the command to return. + * + * Upon any failures, this function will drop into an infinite loop after + * printing the error message to console. + */ -#if defined(CONFIG_VFD) && defined(VFD_TEST_LOGO) - ulong bmp = 0; /* default bitmap */ - extern int trab_vfd (ulong bitmap); +#if defined(CONFIG_BOOTDELAY) && defined(CONFIG_OF_CONTROL) +static void secure_boot_cmd(char *cmd) +{ + cmd_tbl_t *cmdtp; + int rc; -#ifdef CONFIG_MODEM_SUPPORT - if (do_mdm_init) - bmp = 1; /* alternate bitmap */ -#endif - trab_vfd (bmp); -#endif /* CONFIG_VFD && VFD_TEST_LOGO */ + if (!cmd) { + printf("## Error: Secure boot command not specified\n"); + goto err; + } -#ifdef CONFIG_BOOTCOUNT_LIMIT - bootcount = bootcount_load(); - bootcount++; - bootcount_store (bootcount); - sprintf (bcs_set, "%lu", bootcount); - setenv ("bootcount", bcs_set); - bcs = getenv ("bootlimit"); - bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0; -#endif /* CONFIG_BOOTCOUNT_LIMIT */ + /* Disable Ctrl-C just in case some command is used that checks it. */ + disable_ctrlc(1); -#ifdef CONFIG_MODEM_SUPPORT - debug ("DEBUG: main_loop: do_mdm_init=%d\n", do_mdm_init); - if (do_mdm_init) { - char *str = strdup(getenv("mdm_cmd")); - setenv ("preboot", str); /* set or delete definition */ - if (str != NULL) - free (str); - mdm_init(); /* wait for modem connection */ + /* Find the command directly. */ + cmdtp = find_cmd(cmd); + if (!cmdtp) { + printf("## Error: \"%s\" not defined\n", cmd); + goto err; } -#endif /* CONFIG_MODEM_SUPPORT */ -#ifdef CONFIG_VERSION_VARIABLE - { - extern char version_string[]; + /* Run the command, forcing no flags and faking argc and argv. */ + rc = (cmdtp->cmd)(cmdtp, 0, 1, &cmd); - setenv ("ver", version_string); /* set version variable */ - } -#endif /* CONFIG_VERSION_VARIABLE */ + /* Shouldn't ever return from boot command. */ + printf("## Error: \"%s\" returned (code %d)\n", cmd, rc); -#ifdef CFG_HUSH_PARSER - u_boot_hush_start (); -#endif +err: + /* + * Not a whole lot to do here. Rebooting won't help much, since we'll + * just end up right back here. Just loop. + */ + hang(); +} -#ifdef CONFIG_AUTO_COMPLETE - install_auto_complete(); -#endif +static void process_fdt_options(const void *blob) +{ + ulong addr; -#ifdef CONFIG_PREBOOT - if ((p = getenv ("preboot")) != NULL) { -# ifdef CONFIG_AUTOBOOT_KEYED - int prev = disable_ctrlc(1); /* disable Control C checking */ -# endif + /* Add an env variable to point to a kernel payload, if available */ + addr = fdtdec_get_config_int(gd->fdt_blob, "kernel-offset", 0); + if (addr) + setenv_addr("kernaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr)); -# ifndef CFG_HUSH_PARSER - run_command (p, 0); -# else - parse_string_outer(p, FLAG_PARSE_SEMICOLON | - FLAG_EXIT_FROM_LOOP); -# endif + /* Add an env variable to point to a root disk, if available */ + addr = fdtdec_get_config_int(gd->fdt_blob, "rootdisk-offset", 0); + if (addr) + setenv_addr("rootaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr)); +} +#endif /* CONFIG_OF_CONTROL */ -# ifdef CONFIG_AUTOBOOT_KEYED - disable_ctrlc(prev); /* restore Control C checking */ -# endif - } -#endif /* CONFIG_PREBOOT */ +#ifdef CONFIG_BOOTDELAY +static void process_boot_delay(void) +{ +#ifdef CONFIG_OF_CONTROL + char *env; +#endif + char *s; + int bootdelay; +#ifdef CONFIG_BOOTCOUNT_LIMIT + unsigned long bootcount = 0; + unsigned long bootlimit = 0; +#endif /* CONFIG_BOOTCOUNT_LIMIT */ + +#ifdef CONFIG_BOOTCOUNT_LIMIT + bootcount = bootcount_load(); + bootcount++; + bootcount_store (bootcount); + setenv_ulong("bootcount", bootcount); + bootlimit = getenv_ulong("bootlimit", 10, 0); +#endif /* CONFIG_BOOTCOUNT_LIMIT */ -#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) s = getenv ("bootdelay"); bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; +#ifdef CONFIG_OF_CONTROL + bootdelay = fdtdec_get_config_int(gd->fdt_blob, "bootdelay", + bootdelay); +#endif + debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay); +#if defined(CONFIG_MENU_SHOW) + bootdelay = menu_show(bootdelay); +#endif # ifdef CONFIG_BOOT_RETRY_TIME init_cmd_timeout (); # endif /* CONFIG_BOOT_RETRY_TIME */ @@ -376,58 +365,119 @@ void main_loop (void) #ifdef CONFIG_BOOTCOUNT_LIMIT if (bootlimit && (bootcount > bootlimit)) { printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n", - (unsigned)bootlimit); + (unsigned)bootlimit); s = getenv ("altbootcmd"); } else #endif /* CONFIG_BOOTCOUNT_LIMIT */ s = getenv ("bootcmd"); +#ifdef CONFIG_OF_CONTROL + /* Allow the fdt to override the boot command */ + env = fdtdec_get_config_string(gd->fdt_blob, "bootcmd"); + if (env) + s = env; + + process_fdt_options(gd->fdt_blob); + + /* + * If the bootsecure option was chosen, use secure_boot_cmd(). + * Always use 'env' in this case, since bootsecure requres that the + * bootcmd was specified in the FDT too. + */ + if (fdtdec_get_config_int(gd->fdt_blob, "bootsecure", 0)) + secure_boot_cmd(env); + +#endif /* CONFIG_OF_CONTROL */ debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : ""); - if (bootdelay >= 0 && s && !abortboot (bootdelay)) { -# ifdef CONFIG_AUTOBOOT_KEYED + if (bootdelay != -1 && s && !abortboot(bootdelay)) { +#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC) int prev = disable_ctrlc(1); /* disable Control C checking */ -# endif +#endif -# ifndef CFG_HUSH_PARSER - run_command (s, 0); -# else - parse_string_outer(s, FLAG_PARSE_SEMICOLON | - FLAG_EXIT_FROM_LOOP); -# endif + run_command_list(s, -1, 0); -# ifdef CONFIG_AUTOBOOT_KEYED +#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC) disable_ctrlc(prev); /* restore Control C checking */ -# endif +#endif } -# ifdef CONFIG_MENUKEY +#ifdef CONFIG_MENUKEY if (menukey == CONFIG_MENUKEY) { - s = getenv("menucmd"); - if (s) { -# ifndef CFG_HUSH_PARSER - run_command (s, 0); -# else - parse_string_outer(s, FLAG_PARSE_SEMICOLON | - FLAG_EXIT_FROM_LOOP); -# endif - } + s = getenv("menucmd"); + if (s) + run_command_list(s, -1, 0); } #endif /* CONFIG_MENUKEY */ -#endif /* CONFIG_BOOTDELAY */ +} +#endif /* CONFIG_BOOTDELAY */ + +void main_loop(void) +{ +#ifndef CONFIG_SYS_HUSH_PARSER + static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, }; + int len; + int rc = 1; + int flag; +#endif +#ifdef CONFIG_PREBOOT + char *p; +#endif + + bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop"); -#ifdef CONFIG_AMIGAONEG3SE +#ifdef CONFIG_MODEM_SUPPORT + debug("DEBUG: main_loop: do_mdm_init=%d\n", do_mdm_init); + if (do_mdm_init) { + char *str = strdup(getenv("mdm_cmd")); + setenv("preboot", str); /* set or delete definition */ + if (str != NULL) + free(str); + mdm_init(); /* wait for modem connection */ + } +#endif /* CONFIG_MODEM_SUPPORT */ + +#ifdef CONFIG_VERSION_VARIABLE { - extern void video_banner(void); - video_banner(); + setenv("ver", version_string); /* set version variable */ } +#endif /* CONFIG_VERSION_VARIABLE */ + +#ifdef CONFIG_SYS_HUSH_PARSER + u_boot_hush_start(); #endif +#if defined(CONFIG_HUSH_INIT_VAR) + hush_init_var(); +#endif + +#ifdef CONFIG_PREBOOT + p = getenv("preboot"); + if (p != NULL) { +# ifdef CONFIG_AUTOBOOT_KEYED + int prev = disable_ctrlc(1); /* disable Control C checking */ +# endif + + run_command_list(p, -1, 0); + +# ifdef CONFIG_AUTOBOOT_KEYED + disable_ctrlc(prev); /* restore Control C checking */ +# endif + } +#endif /* CONFIG_PREBOOT */ + +#if defined(CONFIG_UPDATE_TFTP) + update_tftp(0UL); +#endif /* CONFIG_UPDATE_TFTP */ + +#ifdef CONFIG_BOOTDELAY + process_boot_delay(); +#endif /* * Main Loop for Monitor Command Processing */ -#ifdef CFG_HUSH_PARSER +#ifdef CONFIG_SYS_HUSH_PARSER parse_file_outer(); /* This point is never reached */ for (;;); @@ -441,7 +491,7 @@ void main_loop (void) reset_cmd_timeout(); } #endif - len = readline (CFG_PROMPT); + len = readline (CONFIG_SYS_PROMPT); flag = 0; /* assume no special flags for now */ if (len > 0) @@ -465,14 +515,14 @@ void main_loop (void) if (len == -1) puts ("\n"); else - rc = run_command (lastcommand, flag); + rc = run_command(lastcommand, flag); if (rc <= 0) { /* invalid command or not repeatable, forget it */ lastcommand[0] = 0; } } -#endif /*CFG_HUSH_PARSER*/ +#endif /*CONFIG_SYS_HUSH_PARSER*/ } #ifdef CONFIG_BOOT_RETRY_TIME @@ -509,13 +559,10 @@ void reset_cmd_timeout(void) */ #define putnstr(str,n) do { \ - printf ("%.*s", n, str); \ + printf ("%.*s", (int)n, str); \ } while (0) #define CTL_CH(c) ((c) - 'a' + 1) - -#define MAX_CMDBUF_SIZE 256 - #define CTL_BACKSPACE ('\b') #define DEL ((char)255) #define DEL7 ((char)127) @@ -526,15 +573,15 @@ void reset_cmd_timeout(void) #define getcmd_cbeep() getcmd_putch('\a') #define HIST_MAX 20 -#define HIST_SIZE MAX_CMDBUF_SIZE +#define HIST_SIZE CONFIG_SYS_CBSIZE -static int hist_max = 0; -static int hist_add_idx = 0; +static int hist_max; +static int hist_add_idx; static int hist_cur = -1; -unsigned hist_num = 0; +static unsigned hist_num; -char* hist_list[HIST_MAX]; -char hist_lines[HIST_MAX][HIST_SIZE]; +static char *hist_list[HIST_MAX]; +static char hist_lines[HIST_MAX][HIST_SIZE + 1]; /* Save room for NULL */ #define add_idx_minus_one() ((hist_add_idx == 0) ? hist_max : hist_add_idx-1) @@ -638,12 +685,10 @@ static void cread_print_hist_list(void) #define ERASE_TO_EOL() { \ if (num < eol_num) { \ - int tmp; \ - for (tmp = num; tmp < eol_num; tmp++) \ - getcmd_putch(' '); \ - while (tmp-- > num) \ + printf("%*s", (int)(eol_num - num), ""); \ + do { \ getcmd_putch(CTL_BACKSPACE); \ - eol_num = num; \ + } while (--eol_num > num); \ } \ } @@ -699,26 +744,40 @@ static void cread_add_str(char *str, int strsize, int insert, unsigned long *num } } -static int cread_line(const char *const prompt, char *buf, unsigned int *len) +static int cread_line(const char *const prompt, char *buf, unsigned int *len, + int timeout) { unsigned long num = 0; unsigned long eol_num = 0; - unsigned long rlen; unsigned long wlen; char ichar; int insert = 1; int esc_len = 0; - int rc = 0; char esc_save[8]; + int init_len = strlen(buf); + int first = 1; + + if (init_len) + cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len); while (1) { - rlen = 1; #ifdef CONFIG_BOOT_RETRY_TIME while (!tstc()) { /* while no incoming data */ if (retry_time >= 0 && get_ticks() > endtime) return (-2); /* timed out */ + WATCHDOG_RESET(); } #endif + if (first && timeout) { + uint64_t etime = endtick(timeout); + + while (!tstc()) { /* while no incoming data */ + if (get_ticks() >= etime) + return -2; /* timed out */ + WATCHDOG_RESET(); + } + first = 0; + } ichar = getcmd_getch(); @@ -911,7 +970,7 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len) cread_add_to_hist(buf); hist_cur = hist_add_idx; - return (rc); + return 0; } #endif /* CONFIG_CMDLINE_EDITING */ @@ -928,15 +987,21 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len) */ int readline (const char *const prompt) { - return readline_into_buffer(prompt, console_buffer); + /* + * If console_buffer isn't 0-length the user will be prompted to modify + * it instead of entering it from scratch as desired. + */ + console_buffer[0] = '\0'; + + return readline_into_buffer(prompt, console_buffer, 0); } -int readline_into_buffer (const char *const prompt, char * buffer) +int readline_into_buffer(const char *const prompt, char *buffer, int timeout) { char *p = buffer; #ifdef CONFIG_CMDLINE_EDITING - unsigned int len=MAX_CMDBUF_SIZE; + unsigned int len = CONFIG_SYS_CBSIZE; int rc; static int initted = 0; @@ -952,9 +1017,10 @@ int readline_into_buffer (const char *const prompt, char * buffer) initted = 1; } - puts (prompt); + if (prompt) + puts (prompt); - rc = cread_line(prompt, p, &len); + rc = cread_line(prompt, p, &len, timeout); return rc < 0 ? rc : len; } else { @@ -977,14 +1043,15 @@ int readline_into_buffer (const char *const prompt, char * buffer) while (!tstc()) { /* while no incoming data */ if (retry_time >= 0 && get_ticks() > endtime) return (-2); /* timed out */ + WATCHDOG_RESET(); } #endif WATCHDOG_RESET(); /* Trigger watchdog, if needed */ #ifdef CONFIG_SHOW_ACTIVITY while (!tstc()) { - extern void show_activity(int arg); show_activity(0); + WATCHDOG_RESET(); } #endif c = getc(); @@ -993,20 +1060,20 @@ int readline_into_buffer (const char *const prompt, char * buffer) * Special character handling */ switch (c) { - case '\r': /* Enter */ + case '\r': /* Enter */ case '\n': *p = '\0'; puts ("\r\n"); - return (p - p_buf); + return p - p_buf; - case '\0': /* nul */ + case '\0': /* nul */ continue; - case 0x03: /* ^C - break */ + case 0x03: /* ^C - break */ p_buf[0] = '\0'; /* discard input */ - return (-1); + return -1; - case 0x15: /* ^U - erase line */ + case 0x15: /* ^U - erase line */ while (col > plen) { puts (erase_seq); --col; @@ -1015,15 +1082,15 @@ int readline_into_buffer (const char *const prompt, char * buffer) n = 0; continue; - case 0x17: /* ^W - erase word */ + case 0x17: /* ^W - erase word */ p=delete_char(p_buf, p, &col, &n, plen); while ((n > 0) && (*p != ' ')) { p=delete_char(p_buf, p, &col, &n, plen); } continue; - case 0x08: /* ^H - backspace */ - case 0x7F: /* DEL - backspace */ + case 0x08: /* ^H - backspace */ + case 0x7F: /* DEL - backspace */ p=delete_char(p_buf, p, &col, &n, plen); continue; @@ -1031,8 +1098,8 @@ int readline_into_buffer (const char *const prompt, char * buffer) /* * Must be a normal character then */ - if (n < CFG_CBSIZE-2) { - if (c == '\t') { /* expand TABs */ + if (n < CONFIG_SYS_CBSIZE-2) { + if (c == '\t') { /* expand TABs */ #ifdef CONFIG_AUTO_COMPLETE /* if auto completion triggered just continue */ *p = '\0'; @@ -1044,8 +1111,16 @@ int readline_into_buffer (const char *const prompt, char * buffer) puts (tab_seq+(col&07)); col += 8 - (col&07); } else { - ++col; /* echo input */ - putc (c); + char buf[2]; + + /* + * Echo input using puts() to force an + * LCD flush if we are using an LCD + */ + ++col; + buf[0] = c; + buf[1] = '\0'; + puts(buf); } *p++ = c; ++n; @@ -1097,69 +1172,58 @@ int parse_line (char *line, char *argv[]) { int nargs = 0; -#ifdef DEBUG_PARSER - printf ("parse_line: \"%s\"\n", line); -#endif - while (nargs < CFG_MAXARGS) { + debug_parser("parse_line: \"%s\"\n", line); + while (nargs < CONFIG_SYS_MAXARGS) { /* skip any white space */ - while ((*line == ' ') || (*line == '\t')) { + while (isblank(*line)) ++line; - } if (*line == '\0') { /* end of line, no more args */ argv[nargs] = NULL; -#ifdef DEBUG_PARSER - printf ("parse_line: nargs=%d\n", nargs); -#endif - return (nargs); + debug_parser("parse_line: nargs=%d\n", nargs); + return nargs; } argv[nargs++] = line; /* begin of argument string */ /* find end of string */ - while (*line && (*line != ' ') && (*line != '\t')) { + while (*line && !isblank(*line)) ++line; - } if (*line == '\0') { /* end of line, no more args */ argv[nargs] = NULL; -#ifdef DEBUG_PARSER - printf ("parse_line: nargs=%d\n", nargs); -#endif - return (nargs); + debug_parser("parse_line: nargs=%d\n", nargs); + return nargs; } *line++ = '\0'; /* terminate current arg */ } - printf ("** Too many args (max. %d) **\n", CFG_MAXARGS); + printf ("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS); -#ifdef DEBUG_PARSER - printf ("parse_line: nargs=%d\n", nargs); -#endif + debug_parser("parse_line: nargs=%d\n", nargs); return (nargs); } /****************************************************************************/ +#ifndef CONFIG_SYS_HUSH_PARSER static void process_macros (const char *input, char *output) { char c, prev; const char *varname_start = NULL; int inputcnt = strlen (input); - int outputcnt = CFG_CBSIZE; + int outputcnt = CONFIG_SYS_CBSIZE; int state = 0; /* 0 = waiting for '$' */ /* 1 = waiting for '(' or '{' */ /* 2 = waiting for ')' or '}' */ /* 3 = waiting for ''' */ -#ifdef DEBUG_PARSER char *output_start = output; - printf ("[PROCESS_MACROS] INPUT len %d: \"%s\"\n", strlen (input), - input); -#endif + debug_parser("[PROCESS_MACROS] INPUT len %zd: \"%s\"\n", strlen(input), + input); prev = '\0'; /* previous character */ @@ -1208,7 +1272,7 @@ static void process_macros (const char *input, char *output) case 2: /* Waiting for ) */ if (c == ')' || c == '}') { int i; - char envname[CFG_CBSIZE], *envval; + char envname[CONFIG_SYS_CBSIZE], *envval; int envcnt = input - varname_start - 1; /* Varname # of chars */ /* Get the varname */ @@ -1247,10 +1311,8 @@ static void process_macros (const char *input, char *output) else *(output - 1) = 0; -#ifdef DEBUG_PARSER - printf ("[PROCESS_MACROS] OUTPUT len %d: \"%s\"\n", - strlen (output_start), output_start); -#endif + debug_parser("[PROCESS_MACROS] OUTPUT len %zd: \"%s\"\n", + strlen(output_start), output_start); } /**************************************************************************** @@ -1259,7 +1321,7 @@ static void process_macros (const char *input, char *output) * 0 - command executed but not repeatable, interrupted commands are * always considered not repeatable * -1 - not executed (unrecognized, bootd recursion or too many args) - * (If cmd is NULL or "" or longer than CFG_CBSIZE-1 it is + * (If cmd is NULL or "" or longer than CONFIG_SYS_CBSIZE-1 it is * considered unrecognized) * * WARNING: @@ -1269,33 +1331,31 @@ static void process_macros (const char *input, char *output) * the environment data, which may change magicly when the command we run * creates or modifies environment variables (like "bootp" does). */ - -int run_command (const char *cmd, int flag) +static int builtin_run_command(const char *cmd, int flag) { - cmd_tbl_t *cmdtp; - char cmdbuf[CFG_CBSIZE]; /* working copy of cmd */ + char cmdbuf[CONFIG_SYS_CBSIZE]; /* working copy of cmd */ char *token; /* start of token in cmdbuf */ char *sep; /* end of token (separator) in cmdbuf */ - char finaltoken[CFG_CBSIZE]; + char finaltoken[CONFIG_SYS_CBSIZE]; char *str = cmdbuf; - char *argv[CFG_MAXARGS + 1]; /* NULL terminated */ + char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */ int argc, inquotes; int repeatable = 1; int rc = 0; -#ifdef DEBUG_PARSER - printf ("[RUN_COMMAND] cmd[%p]=\"", cmd); - puts (cmd ? cmd : "NULL"); /* use puts - string may be loooong */ - puts ("\"\n"); -#endif - + debug_parser("[RUN_COMMAND] cmd[%p]=\"", cmd); + if (DEBUG_PARSER) { + /* use puts - string may be loooong */ + puts(cmd ? cmd : "NULL"); + puts("\"\n"); + } clear_ctrlc(); /* forget any previous Control C */ if (!cmd || !*cmd) { return -1; /* empty command */ } - if (strlen(cmd) >= CFG_CBSIZE) { + if (strlen(cmd) >= CONFIG_SYS_CBSIZE) { puts ("## Command too long!\n"); return -1; } @@ -1306,9 +1366,7 @@ int run_command (const char *cmd, int flag) * repeatable commands */ -#ifdef DEBUG_PARSER - printf ("[PROCESS_SEPARATORS] %s\n", cmd); -#endif + debug_parser("[PROCESS_SEPARATORS] %s\n", cmd); while (*str) { /* @@ -1337,9 +1395,7 @@ int run_command (const char *cmd, int flag) } else str = sep; /* no more commands for next pass */ -#ifdef DEBUG_PARSER - printf ("token: \"%s\"\n", token); -#endif + debug_parser("token: \"%s\"\n", token); /* find macros in this token and replace them */ process_macros (token, finaltoken); @@ -1350,62 +1406,135 @@ int run_command (const char *cmd, int flag) continue; } - /* Look up command in command table */ - if ((cmdtp = find_cmd(argv[0])) == NULL) { - printf ("Unknown command '%s' - try 'help'\n", argv[0]); - rc = -1; /* give up after bad command */ - continue; - } - - /* found - check max args */ - if (argc > cmdtp->maxargs) { - printf ("Usage:\n%s\n", cmdtp->usage); + if (cmd_process(flag, argc, argv, &repeatable, NULL)) rc = -1; - continue; - } -#if defined(CONFIG_CMD_BOOTD) - /* avoid "bootd" recursion */ - if (cmdtp->cmd == do_bootd) { -#ifdef DEBUG_PARSER - printf ("[%s]\n", finaltoken); + /* Did the user stop this? */ + if (had_ctrlc ()) + return -1; /* if stopped then not repeatable */ + } + + return rc ? rc : repeatable; +} #endif - if (flag & CMD_FLAG_BOOTD) { - puts ("'bootd' recursion detected\n"); - rc = -1; - continue; - } else { - flag |= CMD_FLAG_BOOTD; - } - } + +/* + * Run a command using the selected parser. + * + * @param cmd Command to run + * @param flag Execution flags (CMD_FLAG_...) + * @return 0 on success, or != 0 on error. + */ +int run_command(const char *cmd, int flag) +{ +#ifndef CONFIG_SYS_HUSH_PARSER + /* + * builtin_run_command can return 0 or 1 for success, so clean up + * its result. + */ + if (builtin_run_command(cmd, flag) == -1) + return 1; + + return 0; +#else + return parse_string_outer(cmd, + FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP); #endif +} - /* OK - call function to do the command */ - if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) { - rc = -1; +#ifndef CONFIG_SYS_HUSH_PARSER +/** + * Execute a list of command separated by ; or \n using the built-in parser. + * + * This function cannot take a const char * for the command, since if it + * finds newlines in the string, it replaces them with \0. + * + * @param cmd String containing list of commands + * @param flag Execution flags (CMD_FLAG_...) + * @return 0 on success, or != 0 on error. + */ +static int builtin_run_command_list(char *cmd, int flag) +{ + char *line, *next; + int rcode = 0; + + /* + * Break into individual lines, and execute each line; terminate on + * error. + */ + line = next = cmd; + while (*next) { + if (*next == '\n') { + *next = '\0'; + /* run only non-empty commands */ + if (*line) { + debug("** exec: \"%s\"\n", line); + if (builtin_run_command(line, 0) < 0) { + rcode = 1; + break; + } + } + line = next + 1; } + ++next; + } + if (rcode == 0 && *line) + rcode = (builtin_run_command(line, 0) >= 0); - repeatable &= cmdtp->repeatable; + return rcode; +} +#endif - /* Did the user stop this? */ - if (had_ctrlc ()) - return -1; /* if stopped then not repeatable */ +int run_command_list(const char *cmd, int len, int flag) +{ + int need_buff = 1; + char *buff = (char *)cmd; /* cast away const */ + int rcode = 0; + + if (len == -1) { + len = strlen(cmd); +#ifdef CONFIG_SYS_HUSH_PARSER + /* hush will never change our string */ + need_buff = 0; +#else + /* the built-in parser will change our string if it sees \n */ + need_buff = strchr(cmd, '\n') != NULL; +#endif + } + if (need_buff) { + buff = malloc(len + 1); + if (!buff) + return 1; + memcpy(buff, cmd, len); + buff[len] = '\0'; } +#ifdef CONFIG_SYS_HUSH_PARSER + rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON); +#else + /* + * This function will overwrite any \n it sees with a \0, which + * is why it can't work with a const char *. Here we are making + * using of internal knowledge of this function, to avoid always + * doing a malloc() which is actually required only in a case that + * is pretty rare. + */ + rcode = builtin_run_command_list(buff, flag); + if (need_buff) + free(buff); +#endif - return rc ? rc : repeatable; + return rcode; } /****************************************************************************/ #if defined(CONFIG_CMD_RUN) -int do_run (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +int do_run (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) { int i; - if (argc < 2) { - printf ("Usage:\n%s\n", cmdtp->usage); - return 1; - } + if (argc < 2) + return CMD_RET_USAGE; for (i=1; i