From eca86fad3d823c3c1e7e78b07752aa6a10e35283 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 10 Apr 2014 20:01:24 -0600 Subject: Rename hush to cli_hush Hush is a command-line interpreter, so rename it to make that clearer. Signed-off-by: Simon Glass --- common/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'common/Makefile') diff --git a/common/Makefile b/common/Makefile index 219cb51b2d7..da184a8dc15 100644 --- a/common/Makefile +++ b/common/Makefile @@ -11,7 +11,7 @@ obj-y += main.o obj-y += command.o obj-y += exports.o obj-y += hash.o -obj-$(CONFIG_SYS_HUSH_PARSER) += hush.o +obj-$(CONFIG_SYS_HUSH_PARSER) += cli_hush.o obj-y += s_record.o obj-y += xyzModem.o obj-y += cmd_disk.o -- cgit v1.3.1 From 6493ccc7cf2357081267effffa7d345e50d68d00 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 10 Apr 2014 20:01:26 -0600 Subject: Split out simple parser and readline into separate files It doesn't make sense to have the simple parser and the readline code all in main. Split them out into separate files. Signed-off-by: Simon Glass --- common/Makefile | 9 +- common/cli_readline.c | 670 ++++++++++++++++++++++++++++++++ common/cli_simple.c | 338 ++++++++++++++++ common/main.c | 1017 +------------------------------------------------ include/cli.h | 5 + 5 files changed, 1026 insertions(+), 1013 deletions(-) create mode 100644 common/cli_readline.c create mode 100644 common/cli_simple.c (limited to 'common/Makefile') diff --git a/common/Makefile b/common/Makefile index da184a8dc15..79983254521 100644 --- a/common/Makefile +++ b/common/Makefile @@ -11,7 +11,14 @@ obj-y += main.o obj-y += command.o obj-y += exports.o obj-y += hash.o -obj-$(CONFIG_SYS_HUSH_PARSER) += cli_hush.o +ifdef CONFIG_SYS_HUSH_PARSER +obj-y += cli_hush.o +endif + +# We always have this since drivers/ddr/fs/interactive.c needs it +obj-y += cli_simple.o + +obj-y += cli_readline.o obj-y += s_record.o obj-y += xyzModem.o obj-y += cmd_disk.o diff --git a/common/cli_readline.c b/common/cli_readline.c new file mode 100644 index 00000000000..cea0b7c81b8 --- /dev/null +++ b/common/cli_readline.c @@ -0,0 +1,670 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Add to readline cmdline-editing by + * (C) Copyright 2005 + * JinHua Luo, GuangDong Linux Center, + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +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; /* must be set, default is instant timeout */ +static int retry_time = -1; /* -1 so can call readline before main_loop */ +#endif + +char console_buffer[CONFIG_SYS_CBSIZE + 1]; /* console I/O buffer */ + +#ifndef CONFIG_BOOT_RETRY_MIN +#define CONFIG_BOOT_RETRY_MIN CONFIG_BOOT_RETRY_TIME +#endif + +static char *delete_char (char *buffer, char *p, int *colp, int *np, int plen) +{ + char *s; + + if (*np == 0) + return p; + + if (*(--p) == '\t') { /* will retype the whole line */ + while (*colp > plen) { + puts(erase_seq); + (*colp)--; + } + for (s = buffer; s < p; ++s) { + if (*s == '\t') { + puts(tab_seq + ((*colp) & 07)); + *colp += 8 - ((*colp) & 07); + } else { + ++(*colp); + putc(*s); + } + } + } else { + puts(erase_seq); + (*colp)--; + } + (*np)--; + + return p; +} + +#ifdef CONFIG_CMDLINE_EDITING + +/* + * cmdline-editing related codes from vivi. + * Author: Janghoon Lyu + */ + +#define putnstr(str, n) printf("%.*s", (int)n, str) + +#define CTL_CH(c) ((c) - 'a' + 1) +#define CTL_BACKSPACE ('\b') +#define DEL ((char)255) +#define DEL7 ((char)127) +#define CREAD_HIST_CHAR ('!') + +#define getcmd_putch(ch) putc(ch) +#define getcmd_getch() getc() +#define getcmd_cbeep() getcmd_putch('\a') + +#define HIST_MAX 20 +#define HIST_SIZE CONFIG_SYS_CBSIZE + +static int hist_max; +static int hist_add_idx; +static int hist_cur = -1; +static unsigned hist_num; + +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) + +static void hist_init(void) +{ + int i; + + hist_max = 0; + hist_add_idx = 0; + hist_cur = -1; + hist_num = 0; + + for (i = 0; i < HIST_MAX; i++) { + hist_list[i] = hist_lines[i]; + hist_list[i][0] = '\0'; + } +} + +static void cread_add_to_hist(char *line) +{ + strcpy(hist_list[hist_add_idx], line); + + if (++hist_add_idx >= HIST_MAX) + hist_add_idx = 0; + + if (hist_add_idx > hist_max) + hist_max = hist_add_idx; + + hist_num++; +} + +static char *hist_prev(void) +{ + char *ret; + int old_cur; + + if (hist_cur < 0) + return NULL; + + old_cur = hist_cur; + if (--hist_cur < 0) + hist_cur = hist_max; + + if (hist_cur == hist_add_idx) { + hist_cur = old_cur; + ret = NULL; + } else { + ret = hist_list[hist_cur]; + } + + return ret; +} + +static char *hist_next(void) +{ + char *ret; + + if (hist_cur < 0) + return NULL; + + if (hist_cur == hist_add_idx) + return NULL; + + if (++hist_cur > hist_max) + hist_cur = 0; + + if (hist_cur == hist_add_idx) + ret = ""; + else + ret = hist_list[hist_cur]; + + return ret; +} + +#ifndef CONFIG_CMDLINE_EDITING +static void cread_print_hist_list(void) +{ + int i; + unsigned long n; + + n = hist_num - hist_max; + + i = hist_add_idx + 1; + while (1) { + if (i > hist_max) + i = 0; + if (i == hist_add_idx) + break; + printf("%s\n", hist_list[i]); + n++; + i++; + } +} +#endif /* CONFIG_CMDLINE_EDITING */ + +#define BEGINNING_OF_LINE() { \ + while (num) { \ + getcmd_putch(CTL_BACKSPACE); \ + num--; \ + } \ +} + +#define ERASE_TO_EOL() { \ + if (num < eol_num) { \ + printf("%*s", (int)(eol_num - num), ""); \ + do { \ + getcmd_putch(CTL_BACKSPACE); \ + } while (--eol_num > num); \ + } \ +} + +#define REFRESH_TO_EOL() { \ + if (num < eol_num) { \ + wlen = eol_num - num; \ + putnstr(buf + num, wlen); \ + num = eol_num; \ + } \ +} + +static void cread_add_char(char ichar, int insert, unsigned long *num, + unsigned long *eol_num, char *buf, unsigned long len) +{ + unsigned long wlen; + + /* room ??? */ + if (insert || *num == *eol_num) { + if (*eol_num > len - 1) { + getcmd_cbeep(); + return; + } + (*eol_num)++; + } + + if (insert) { + wlen = *eol_num - *num; + if (wlen > 1) + memmove(&buf[*num+1], &buf[*num], wlen-1); + + buf[*num] = ichar; + putnstr(buf + *num, wlen); + (*num)++; + while (--wlen) + getcmd_putch(CTL_BACKSPACE); + } else { + /* echo the character */ + wlen = 1; + buf[*num] = ichar; + putnstr(buf + *num, wlen); + (*num)++; + } +} + +static void cread_add_str(char *str, int strsize, int insert, + unsigned long *num, unsigned long *eol_num, + char *buf, unsigned long len) +{ + while (strsize--) { + cread_add_char(*str, insert, num, eol_num, buf, len); + str++; + } +} + +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 wlen; + char ichar; + int insert = 1; + int esc_len = 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) { +#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(); + + if ((ichar == '\n') || (ichar == '\r')) { + putc('\n'); + break; + } + + /* + * handle standard linux xterm esc sequences for arrow key, etc. + */ + if (esc_len != 0) { + if (esc_len == 1) { + if (ichar == '[') { + esc_save[esc_len] = ichar; + esc_len = 2; + } else { + cread_add_str(esc_save, esc_len, + insert, &num, &eol_num, + buf, *len); + esc_len = 0; + } + continue; + } + + switch (ichar) { + case 'D': /* <- key */ + ichar = CTL_CH('b'); + esc_len = 0; + break; + case 'C': /* -> key */ + ichar = CTL_CH('f'); + esc_len = 0; + break; /* pass off to ^F handler */ + case 'H': /* Home key */ + ichar = CTL_CH('a'); + esc_len = 0; + break; /* pass off to ^A handler */ + case 'A': /* up arrow */ + ichar = CTL_CH('p'); + esc_len = 0; + break; /* pass off to ^P handler */ + case 'B': /* down arrow */ + ichar = CTL_CH('n'); + esc_len = 0; + break; /* pass off to ^N handler */ + default: + esc_save[esc_len++] = ichar; + cread_add_str(esc_save, esc_len, insert, + &num, &eol_num, buf, *len); + esc_len = 0; + continue; + } + } + + switch (ichar) { + case 0x1b: + if (esc_len == 0) { + esc_save[esc_len] = ichar; + esc_len = 1; + } else { + puts("impossible condition #876\n"); + esc_len = 0; + } + break; + + case CTL_CH('a'): + BEGINNING_OF_LINE(); + break; + case CTL_CH('c'): /* ^C - break */ + *buf = '\0'; /* discard input */ + return -1; + case CTL_CH('f'): + if (num < eol_num) { + getcmd_putch(buf[num]); + num++; + } + break; + case CTL_CH('b'): + if (num) { + getcmd_putch(CTL_BACKSPACE); + num--; + } + break; + case CTL_CH('d'): + if (num < eol_num) { + wlen = eol_num - num - 1; + if (wlen) { + memmove(&buf[num], &buf[num+1], wlen); + putnstr(buf + num, wlen); + } + + getcmd_putch(' '); + do { + getcmd_putch(CTL_BACKSPACE); + } while (wlen--); + eol_num--; + } + break; + case CTL_CH('k'): + ERASE_TO_EOL(); + break; + case CTL_CH('e'): + REFRESH_TO_EOL(); + break; + case CTL_CH('o'): + insert = !insert; + break; + case CTL_CH('x'): + case CTL_CH('u'): + BEGINNING_OF_LINE(); + ERASE_TO_EOL(); + break; + case DEL: + case DEL7: + case 8: + if (num) { + wlen = eol_num - num; + num--; + memmove(&buf[num], &buf[num+1], wlen); + getcmd_putch(CTL_BACKSPACE); + putnstr(buf + num, wlen); + getcmd_putch(' '); + do { + getcmd_putch(CTL_BACKSPACE); + } while (wlen--); + eol_num--; + } + break; + case CTL_CH('p'): + case CTL_CH('n'): + { + char *hline; + + esc_len = 0; + + if (ichar == CTL_CH('p')) + hline = hist_prev(); + else + hline = hist_next(); + + if (!hline) { + getcmd_cbeep(); + continue; + } + + /* nuke the current line */ + /* first, go home */ + BEGINNING_OF_LINE(); + + /* erase to end of line */ + ERASE_TO_EOL(); + + /* copy new line into place and display */ + strcpy(buf, hline); + eol_num = strlen(buf); + REFRESH_TO_EOL(); + continue; + } +#ifdef CONFIG_AUTO_COMPLETE + case '\t': { + int num2, col; + + /* do not autocomplete when in the middle */ + if (num < eol_num) { + getcmd_cbeep(); + break; + } + + buf[num] = '\0'; + col = strlen(prompt) + eol_num; + num2 = num; + if (cmd_auto_complete(prompt, buf, &num2, &col)) { + col = num2 - num; + num += col; + eol_num += col; + } + break; + } +#endif + default: + cread_add_char(ichar, insert, &num, &eol_num, buf, + *len); + break; + } + } + *len = eol_num; + buf[eol_num] = '\0'; /* lose the newline */ + + if (buf[0] && buf[0] != CREAD_HIST_CHAR) + cread_add_to_hist(buf); + hist_cur = hist_add_idx; + + return 0; +} + +#endif /* CONFIG_CMDLINE_EDITING */ + +/****************************************************************************/ + +int readline(const char *const prompt) +{ + /* + * 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 timeout) +{ + char *p = buffer; +#ifdef CONFIG_CMDLINE_EDITING + unsigned int len = CONFIG_SYS_CBSIZE; + int rc; + static int initted; + + /* + * History uses a global array which is not + * writable until after relocation to RAM. + * Revert to non-history version if still + * running from flash. + */ + if (gd->flags & GD_FLG_RELOC) { + if (!initted) { + hist_init(); + initted = 1; + } + + if (prompt) + puts(prompt); + + rc = cread_line(prompt, p, &len, timeout); + return rc < 0 ? rc : len; + + } else { +#endif /* CONFIG_CMDLINE_EDITING */ + char *p_buf = p; + int n = 0; /* buffer index */ + int plen = 0; /* prompt length */ + int col; /* output column cnt */ + char c; + + /* print prompt */ + if (prompt) { + plen = strlen(prompt); + puts(prompt); + } + col = plen; + + for (;;) { +#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 + WATCHDOG_RESET(); /* Trigger watchdog, if needed */ + +#ifdef CONFIG_SHOW_ACTIVITY + while (!tstc()) { + show_activity(0); + WATCHDOG_RESET(); + } +#endif + c = getc(); + + /* + * Special character handling + */ + switch (c) { + case '\r': /* Enter */ + case '\n': + *p = '\0'; + puts("\r\n"); + return p - p_buf; + + case '\0': /* nul */ + continue; + + case 0x03: /* ^C - break */ + p_buf[0] = '\0'; /* discard input */ + return -1; + + case 0x15: /* ^U - erase line */ + while (col > plen) { + puts(erase_seq); + --col; + } + p = p_buf; + n = 0; + continue; + + 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 */ + p = delete_char(p_buf, p, &col, &n, plen); + continue; + + default: + /* + * Must be a normal character then + */ + if (n < CONFIG_SYS_CBSIZE-2) { + if (c == '\t') { /* expand TABs */ +#ifdef CONFIG_AUTO_COMPLETE + /* + * if auto completion triggered just + * continue + */ + *p = '\0'; + if (cmd_auto_complete(prompt, + console_buffer, + &n, &col)) { + p = p_buf + n; /* reset */ + continue; + } +#endif + puts(tab_seq + (col & 07)); + col += 8 - (col & 07); + } else { + 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; + } else { /* Buffer full */ + putc('\a'); + } + } + } +#ifdef CONFIG_CMDLINE_EDITING + } +#endif +} + +#ifdef CONFIG_BOOT_RETRY_TIME +/*************************************************************************** + * initialize command line timeout + */ +void init_cmd_timeout(void) +{ + char *s = getenv("bootretry"); + + if (s != NULL) + retry_time = (int)simple_strtol(s, NULL, 10); + else + retry_time = CONFIG_BOOT_RETRY_TIME; + + if (retry_time >= 0 && retry_time < CONFIG_BOOT_RETRY_MIN) + retry_time = CONFIG_BOOT_RETRY_MIN; +} + +/*************************************************************************** + * reset command line timeout to retry_time seconds + */ +void reset_cmd_timeout(void) +{ + endtime = endtick(retry_time); +} + +void bootretry_dont_retry(void) +{ + retry_time = -1; +} + +#endif diff --git a/common/cli_simple.c b/common/cli_simple.c new file mode 100644 index 00000000000..0610615ea5a --- /dev/null +++ b/common/cli_simple.c @@ -0,0 +1,338 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Add to readline cmdline-editing by + * (C) Copyright 2005 + * JinHua Luo, GuangDong Linux Center, + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#define DEBUG_PARSER 0 /* set to 1 to debug */ + +#define debug_parser(fmt, args...) \ + debug_cond(DEBUG_PARSER, fmt, ##args) + + +int parse_line(char *line, char *argv[]) +{ + int nargs = 0; + + debug_parser("%s: \"%s\"\n", __func__, line); + while (nargs < CONFIG_SYS_MAXARGS) { + /* skip any white space */ + while (isblank(*line)) + ++line; + + if (*line == '\0') { /* end of line, no more args */ + argv[nargs] = NULL; + debug_parser("%s: nargs=%d\n", __func__, nargs); + return nargs; + } + + argv[nargs++] = line; /* begin of argument string */ + + /* find end of string */ + while (*line && !isblank(*line)) + ++line; + + if (*line == '\0') { /* end of line, no more args */ + argv[nargs] = NULL; + debug_parser("parse_line: nargs=%d\n", nargs); + return nargs; + } + + *line++ = '\0'; /* terminate current arg */ + } + + printf("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS); + + debug_parser("%s: nargs=%d\n", __func__, nargs); + return nargs; +} + +static void process_macros(const char *input, char *output) +{ + char c, prev; + const char *varname_start = NULL; + int inputcnt = strlen(input); + int outputcnt = CONFIG_SYS_CBSIZE; + int state = 0; /* 0 = waiting for '$' */ + + /* 1 = waiting for '(' or '{' */ + /* 2 = waiting for ')' or '}' */ + /* 3 = waiting for ''' */ + char *output_start = output; + + debug_parser("[PROCESS_MACROS] INPUT len %zd: \"%s\"\n", strlen(input), + input); + + prev = '\0'; /* previous character */ + + while (inputcnt && outputcnt) { + c = *input++; + inputcnt--; + + if (state != 3) { + /* remove one level of escape characters */ + if ((c == '\\') && (prev != '\\')) { + if (inputcnt-- == 0) + break; + prev = c; + c = *input++; + } + } + + switch (state) { + case 0: /* Waiting for (unescaped) $ */ + if ((c == '\'') && (prev != '\\')) { + state = 3; + break; + } + if ((c == '$') && (prev != '\\')) { + state++; + } else { + *(output++) = c; + outputcnt--; + } + break; + case 1: /* Waiting for ( */ + if (c == '(' || c == '{') { + state++; + varname_start = input; + } else { + state = 0; + *(output++) = '$'; + outputcnt--; + + if (outputcnt) { + *(output++) = c; + outputcnt--; + } + } + break; + case 2: /* Waiting for ) */ + if (c == ')' || c == '}') { + int i; + char envname[CONFIG_SYS_CBSIZE], *envval; + /* Varname # of chars */ + int envcnt = input - varname_start - 1; + + /* Get the varname */ + for (i = 0; i < envcnt; i++) + envname[i] = varname_start[i]; + envname[i] = 0; + + /* Get its value */ + envval = getenv(envname); + + /* Copy into the line if it exists */ + if (envval != NULL) + while ((*envval) && outputcnt) { + *(output++) = *(envval++); + outputcnt--; + } + /* Look for another '$' */ + state = 0; + } + break; + case 3: /* Waiting for ' */ + if ((c == '\'') && (prev != '\\')) { + state = 0; + } else { + *(output++) = c; + outputcnt--; + } + break; + } + prev = c; + } + + if (outputcnt) + *output = 0; + else + *(output - 1) = 0; + + debug_parser("[PROCESS_MACROS] OUTPUT len %zd: \"%s\"\n", + strlen(output_start), output_start); +} + + /* + * WARNING: + * + * We must create a temporary copy of the command since the command we get + * may be the result from getenv(), which returns a pointer directly to + * the environment data, which may change magicly when the command we run + * creates or modifies environment variables (like "bootp" does). + */ +int cli_simple_run_command(const char *cmd, int flag) +{ + 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[CONFIG_SYS_CBSIZE]; + char *str = cmdbuf; + char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */ + int argc, inquotes; + int repeatable = 1; + int rc = 0; + + 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) >= CONFIG_SYS_CBSIZE) { + puts("## Command too long!\n"); + return -1; + } + + strcpy(cmdbuf, cmd); + + /* Process separators and check for invalid + * repeatable commands + */ + + debug_parser("[PROCESS_SEPARATORS] %s\n", cmd); + while (*str) { + /* + * Find separator, or string end + * Allow simple escape of ';' by writing "\;" + */ + for (inquotes = 0, sep = str; *sep; sep++) { + if ((*sep == '\'') && + (*(sep - 1) != '\\')) + inquotes = !inquotes; + + if (!inquotes && + (*sep == ';') && /* separator */ + (sep != str) && /* past string start */ + (*(sep - 1) != '\\')) /* and NOT escaped */ + break; + } + + /* + * Limit the token to data between separators + */ + token = str; + if (*sep) { + str = sep + 1; /* start of command for next pass */ + *sep = '\0'; + } else { + str = sep; /* no more commands for next pass */ + } + debug_parser("token: \"%s\"\n", token); + + /* find macros in this token and replace them */ + process_macros(token, finaltoken); + + /* Extract arguments */ + argc = parse_line(finaltoken, argv); + if (argc == 0) { + rc = -1; /* no command at all */ + continue; + } + + if (cmd_process(flag, argc, argv, &repeatable, NULL)) + rc = -1; + + /* Did the user stop this? */ + if (had_ctrlc()) + return -1; /* if stopped then not repeatable */ + } + + return rc ? rc : repeatable; +} + +void cli_loop(void) +{ + static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, }; + + int len; + int flag; + int rc = 1; + + for (;;) { +#ifdef CONFIG_BOOT_RETRY_TIME + if (rc >= 0) { + /* Saw enough of a valid command to + * restart the timeout. + */ + reset_cmd_timeout(); + } +#endif + len = readline(CONFIG_SYS_PROMPT); + + flag = 0; /* assume no special flags for now */ + if (len > 0) + strcpy(lastcommand, console_buffer); + else if (len == 0) + flag |= CMD_FLAG_REPEAT; +#ifdef CONFIG_BOOT_RETRY_TIME + else if (len == -2) { + /* -2 means timed out, retry autoboot + */ + puts("\nTimed out waiting for command\n"); +# ifdef CONFIG_RESET_TO_RETRY + /* Reinit board to run initialization code again */ + do_reset(NULL, 0, 0, NULL); +# else + return; /* retry autoboot */ +# endif + } +#endif + + if (len == -1) + puts("\n"); + else + rc = run_command(lastcommand, flag); + + if (rc <= 0) { + /* invalid command or not repeatable, forget it */ + lastcommand[0] = 0; + } + } +} + +int cli_simple_run_command_list(char *cmd, int flag) +{ + char *line, *next; + int rcode = 0; + + /* + * Break into individual lines, and execute each line; terminate on + * error. + */ + next = cmd; + line = cmd; + while (*next) { + if (*next == '\n') { + *next = '\0'; + /* run only non-empty commands */ + if (*line) { + debug("** exec: \"%s\"\n", line); + if (cli_simple_run_command(line, 0) < 0) { + rcode = 1; + break; + } + } + line = next + 1; + } + ++next; + } + if (rcode == 0 && *line) + rcode = (cli_simple_run_command(line, 0) >= 0); + + return rcode; +} diff --git a/common/main.c b/common/main.c index 6d1c3a9fadb..88b0adb6b70 100644 --- a/common/main.c +++ b/common/main.c @@ -2,10 +2,6 @@ * (C) Copyright 2000 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * - * Add to readline cmdline-editing by - * (C) Copyright 2005 - * JinHua Luo, GuangDong Linux Center, - * * SPDX-License-Identifier: GPL-2.0+ */ @@ -20,8 +16,6 @@ #include #include #include -#include -#include DECLARE_GLOBAL_DATA_PTR; @@ -33,34 +27,12 @@ void show_boot_progress (int val) __attribute__((weak, alias("__show_boot_progre #define MAX_DELAY_STOP_STR 32 -#define DEBUG_PARSER 0 /* set to 1 to debug */ - -#define debug_parser(fmt, args...) \ - debug_cond(DEBUG_PARSER, fmt, ##args) - #ifndef DEBUG_BOOTKEYS #define DEBUG_BOOTKEYS 0 #endif #define debug_bootkeys(fmt, args...) \ debug_cond(DEBUG_BOOTKEYS, fmt, ##args) -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 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 */ -static int retry_time = -1; /* -1 so can call readline before main_loop */ -#endif - -#define endtick(seconds) (get_ticks() + (uint64_t)(seconds) * get_tbclk()) - -#ifndef CONFIG_BOOT_RETRY_MIN -#define CONFIG_BOOT_RETRY_MIN CONFIG_BOOT_RETRY_TIME -#endif - #ifdef CONFIG_MODEM_SUPPORT int do_mdm_init = 0; extern void mdm_init(void); /* defined in board.c */ @@ -162,7 +134,7 @@ static int abortboot_keyed(int bootdelay) # ifdef CONFIG_BOOT_RETRY_TIME /* don't retry auto boot */ if (! delaykey[i].retry) - retry_time = -1; + bootretry_dont_retry(); # endif abort = 1; } @@ -416,12 +388,6 @@ static void process_boot_delay(void) 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 @@ -489,942 +455,12 @@ void main_loop(void) /* This point is never reached */ for (;;); #else - for (;;) { -#ifdef CONFIG_BOOT_RETRY_TIME - if (rc >= 0) { - /* Saw enough of a valid command to - * restart the timeout. - */ - reset_cmd_timeout(); - } -#endif - len = readline (CONFIG_SYS_PROMPT); - - flag = 0; /* assume no special flags for now */ - if (len > 0) - strcpy (lastcommand, console_buffer); - else if (len == 0) - flag |= CMD_FLAG_REPEAT; -#ifdef CONFIG_BOOT_RETRY_TIME - else if (len == -2) { - /* -2 means timed out, retry autoboot - */ - puts ("\nTimed out waiting for command\n"); -# ifdef CONFIG_RESET_TO_RETRY - /* Reinit board to run initialization code again */ - do_reset (NULL, 0, 0, NULL); -# else - return; /* retry autoboot */ -# endif - } -#endif - - if (len == -1) - puts ("\n"); - else - rc = run_command(lastcommand, flag); - - if (rc <= 0) { - /* invalid command or not repeatable, forget it */ - lastcommand[0] = 0; - } - } + cli_loop(); #endif /*CONFIG_SYS_HUSH_PARSER*/ } -#ifdef CONFIG_BOOT_RETRY_TIME -/*************************************************************************** - * initialize command line timeout - */ -void init_cmd_timeout(void) -{ - char *s = getenv ("bootretry"); - - if (s != NULL) - retry_time = (int)simple_strtol(s, NULL, 10); - else - retry_time = CONFIG_BOOT_RETRY_TIME; - - if (retry_time >= 0 && retry_time < CONFIG_BOOT_RETRY_MIN) - retry_time = CONFIG_BOOT_RETRY_MIN; -} - -/*************************************************************************** - * reset command line timeout to retry_time seconds - */ -void reset_cmd_timeout(void) -{ - endtime = endtick(retry_time); -} -#endif - -#ifdef CONFIG_CMDLINE_EDITING - -/* - * cmdline-editing related codes from vivi. - * Author: Janghoon Lyu - */ - -#define putnstr(str,n) do { \ - printf ("%.*s", (int)n, str); \ - } while (0) - -#define CTL_CH(c) ((c) - 'a' + 1) -#define CTL_BACKSPACE ('\b') -#define DEL ((char)255) -#define DEL7 ((char)127) -#define CREAD_HIST_CHAR ('!') - -#define getcmd_putch(ch) putc(ch) -#define getcmd_getch() getc() -#define getcmd_cbeep() getcmd_putch('\a') - -#define HIST_MAX 20 -#define HIST_SIZE CONFIG_SYS_CBSIZE - -static int hist_max; -static int hist_add_idx; -static int hist_cur = -1; -static unsigned hist_num; - -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) - -static void hist_init(void) -{ - int i; - - hist_max = 0; - hist_add_idx = 0; - hist_cur = -1; - hist_num = 0; - - for (i = 0; i < HIST_MAX; i++) { - hist_list[i] = hist_lines[i]; - hist_list[i][0] = '\0'; - } -} - -static void cread_add_to_hist(char *line) -{ - strcpy(hist_list[hist_add_idx], line); - - if (++hist_add_idx >= HIST_MAX) - hist_add_idx = 0; - - if (hist_add_idx > hist_max) - hist_max = hist_add_idx; - - hist_num++; -} - -static char* hist_prev(void) -{ - char *ret; - int old_cur; - - if (hist_cur < 0) - return NULL; - - old_cur = hist_cur; - if (--hist_cur < 0) - hist_cur = hist_max; - - if (hist_cur == hist_add_idx) { - hist_cur = old_cur; - ret = NULL; - } else - ret = hist_list[hist_cur]; - - return (ret); -} - -static char* hist_next(void) -{ - char *ret; - - if (hist_cur < 0) - return NULL; - - if (hist_cur == hist_add_idx) - return NULL; - - if (++hist_cur > hist_max) - hist_cur = 0; - - if (hist_cur == hist_add_idx) { - ret = ""; - } else - ret = hist_list[hist_cur]; - - return (ret); -} - -#ifndef CONFIG_CMDLINE_EDITING -static void cread_print_hist_list(void) -{ - int i; - unsigned long n; - - n = hist_num - hist_max; - - i = hist_add_idx + 1; - while (1) { - if (i > hist_max) - i = 0; - if (i == hist_add_idx) - break; - printf("%s\n", hist_list[i]); - n++; - i++; - } -} -#endif /* CONFIG_CMDLINE_EDITING */ - -#define BEGINNING_OF_LINE() { \ - while (num) { \ - getcmd_putch(CTL_BACKSPACE); \ - num--; \ - } \ -} - -#define ERASE_TO_EOL() { \ - if (num < eol_num) { \ - printf("%*s", (int)(eol_num - num), ""); \ - do { \ - getcmd_putch(CTL_BACKSPACE); \ - } while (--eol_num > num); \ - } \ -} - -#define REFRESH_TO_EOL() { \ - if (num < eol_num) { \ - wlen = eol_num - num; \ - putnstr(buf + num, wlen); \ - num = eol_num; \ - } \ -} - -static void cread_add_char(char ichar, int insert, unsigned long *num, - unsigned long *eol_num, char *buf, unsigned long len) -{ - unsigned long wlen; - - /* room ??? */ - if (insert || *num == *eol_num) { - if (*eol_num > len - 1) { - getcmd_cbeep(); - return; - } - (*eol_num)++; - } - - if (insert) { - wlen = *eol_num - *num; - if (wlen > 1) { - memmove(&buf[*num+1], &buf[*num], wlen-1); - } - - buf[*num] = ichar; - putnstr(buf + *num, wlen); - (*num)++; - while (--wlen) { - getcmd_putch(CTL_BACKSPACE); - } - } else { - /* echo the character */ - wlen = 1; - buf[*num] = ichar; - putnstr(buf + *num, wlen); - (*num)++; - } -} - -static void cread_add_str(char *str, int strsize, int insert, unsigned long *num, - unsigned long *eol_num, char *buf, unsigned long len) -{ - while (strsize--) { - cread_add_char(*str, insert, num, eol_num, buf, len); - str++; - } -} - -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 wlen; - char ichar; - int insert = 1; - int esc_len = 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) { -#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(); - - if ((ichar == '\n') || (ichar == '\r')) { - putc('\n'); - break; - } - - /* - * handle standard linux xterm esc sequences for arrow key, etc. - */ - if (esc_len != 0) { - if (esc_len == 1) { - if (ichar == '[') { - esc_save[esc_len] = ichar; - esc_len = 2; - } else { - cread_add_str(esc_save, esc_len, insert, - &num, &eol_num, buf, *len); - esc_len = 0; - } - continue; - } - - switch (ichar) { - - case 'D': /* <- key */ - ichar = CTL_CH('b'); - esc_len = 0; - break; - case 'C': /* -> key */ - ichar = CTL_CH('f'); - esc_len = 0; - break; /* pass off to ^F handler */ - case 'H': /* Home key */ - ichar = CTL_CH('a'); - esc_len = 0; - break; /* pass off to ^A handler */ - case 'A': /* up arrow */ - ichar = CTL_CH('p'); - esc_len = 0; - break; /* pass off to ^P handler */ - case 'B': /* down arrow */ - ichar = CTL_CH('n'); - esc_len = 0; - break; /* pass off to ^N handler */ - default: - esc_save[esc_len++] = ichar; - cread_add_str(esc_save, esc_len, insert, - &num, &eol_num, buf, *len); - esc_len = 0; - continue; - } - } - - switch (ichar) { - case 0x1b: - if (esc_len == 0) { - esc_save[esc_len] = ichar; - esc_len = 1; - } else { - puts("impossible condition #876\n"); - esc_len = 0; - } - break; - - case CTL_CH('a'): - BEGINNING_OF_LINE(); - break; - case CTL_CH('c'): /* ^C - break */ - *buf = '\0'; /* discard input */ - return (-1); - case CTL_CH('f'): - if (num < eol_num) { - getcmd_putch(buf[num]); - num++; - } - break; - case CTL_CH('b'): - if (num) { - getcmd_putch(CTL_BACKSPACE); - num--; - } - break; - case CTL_CH('d'): - if (num < eol_num) { - wlen = eol_num - num - 1; - if (wlen) { - memmove(&buf[num], &buf[num+1], wlen); - putnstr(buf + num, wlen); - } - - getcmd_putch(' '); - do { - getcmd_putch(CTL_BACKSPACE); - } while (wlen--); - eol_num--; - } - break; - case CTL_CH('k'): - ERASE_TO_EOL(); - break; - case CTL_CH('e'): - REFRESH_TO_EOL(); - break; - case CTL_CH('o'): - insert = !insert; - break; - case CTL_CH('x'): - case CTL_CH('u'): - BEGINNING_OF_LINE(); - ERASE_TO_EOL(); - break; - case DEL: - case DEL7: - case 8: - if (num) { - wlen = eol_num - num; - num--; - memmove(&buf[num], &buf[num+1], wlen); - getcmd_putch(CTL_BACKSPACE); - putnstr(buf + num, wlen); - getcmd_putch(' '); - do { - getcmd_putch(CTL_BACKSPACE); - } while (wlen--); - eol_num--; - } - break; - case CTL_CH('p'): - case CTL_CH('n'): - { - char * hline; - - esc_len = 0; - - if (ichar == CTL_CH('p')) - hline = hist_prev(); - else - hline = hist_next(); - - if (!hline) { - getcmd_cbeep(); - continue; - } - - /* nuke the current line */ - /* first, go home */ - BEGINNING_OF_LINE(); - - /* erase to end of line */ - ERASE_TO_EOL(); - - /* copy new line into place and display */ - strcpy(buf, hline); - eol_num = strlen(buf); - REFRESH_TO_EOL(); - continue; - } -#ifdef CONFIG_AUTO_COMPLETE - case '\t': { - int num2, col; - - /* do not autocomplete when in the middle */ - if (num < eol_num) { - getcmd_cbeep(); - break; - } - - buf[num] = '\0'; - col = strlen(prompt) + eol_num; - num2 = num; - if (cmd_auto_complete(prompt, buf, &num2, &col)) { - col = num2 - num; - num += col; - eol_num += col; - } - break; - } -#endif - default: - cread_add_char(ichar, insert, &num, &eol_num, buf, *len); - break; - } - } - *len = eol_num; - buf[eol_num] = '\0'; /* lose the newline */ - - if (buf[0] && buf[0] != CREAD_HIST_CHAR) - cread_add_to_hist(buf); - hist_cur = hist_add_idx; - - return 0; -} - -#endif /* CONFIG_CMDLINE_EDITING */ - /****************************************************************************/ -/* - * Prompt for input and read a line. - * If CONFIG_BOOT_RETRY_TIME is defined and retry_time >= 0, - * time out when time goes past endtime (timebase time in ticks). - * Return: number of read characters - * -1 if break - * -2 if timed out - */ -int readline (const char *const prompt) -{ - /* - * 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 timeout) -{ - char *p = buffer; -#ifdef CONFIG_CMDLINE_EDITING - unsigned int len = CONFIG_SYS_CBSIZE; - int rc; - static int initted = 0; - - /* - * History uses a global array which is not - * writable until after relocation to RAM. - * Revert to non-history version if still - * running from flash. - */ - if (gd->flags & GD_FLG_RELOC) { - if (!initted) { - hist_init(); - initted = 1; - } - - if (prompt) - puts (prompt); - - rc = cread_line(prompt, p, &len, timeout); - return rc < 0 ? rc : len; - - } else { -#endif /* CONFIG_CMDLINE_EDITING */ - char * p_buf = p; - int n = 0; /* buffer index */ - int plen = 0; /* prompt length */ - int col; /* output column cnt */ - char c; - - /* print prompt */ - if (prompt) { - plen = strlen (prompt); - puts (prompt); - } - col = plen; - - for (;;) { -#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 - WATCHDOG_RESET(); /* Trigger watchdog, if needed */ - -#ifdef CONFIG_SHOW_ACTIVITY - while (!tstc()) { - show_activity(0); - WATCHDOG_RESET(); - } -#endif - c = getc(); - - /* - * Special character handling - */ - switch (c) { - case '\r': /* Enter */ - case '\n': - *p = '\0'; - puts ("\r\n"); - return p - p_buf; - - case '\0': /* nul */ - continue; - - case 0x03: /* ^C - break */ - p_buf[0] = '\0'; /* discard input */ - return -1; - - case 0x15: /* ^U - erase line */ - while (col > plen) { - puts (erase_seq); - --col; - } - p = p_buf; - n = 0; - continue; - - 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 */ - p=delete_char(p_buf, p, &col, &n, plen); - continue; - - default: - /* - * Must be a normal character then - */ - if (n < CONFIG_SYS_CBSIZE-2) { - if (c == '\t') { /* expand TABs */ -#ifdef CONFIG_AUTO_COMPLETE - /* if auto completion triggered just continue */ - *p = '\0'; - if (cmd_auto_complete(prompt, console_buffer, &n, &col)) { - p = p_buf + n; /* reset */ - continue; - } -#endif - puts (tab_seq+(col&07)); - col += 8 - (col&07); - } else { - 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; - } else { /* Buffer full */ - putc ('\a'); - } - } - } -#ifdef CONFIG_CMDLINE_EDITING - } -#endif -} - -/****************************************************************************/ - -static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen) -{ - char *s; - - if (*np == 0) { - return (p); - } - - if (*(--p) == '\t') { /* will retype the whole line */ - while (*colp > plen) { - puts (erase_seq); - (*colp)--; - } - for (s=buffer; s= CONFIG_SYS_CBSIZE) { - puts ("## Command too long!\n"); - return -1; - } - - strcpy (cmdbuf, cmd); - - /* Process separators and check for invalid - * repeatable commands - */ - - debug_parser("[PROCESS_SEPARATORS] %s\n", cmd); - while (*str) { - - /* - * Find separator, or string end - * Allow simple escape of ';' by writing "\;" - */ - for (inquotes = 0, sep = str; *sep; sep++) { - if ((*sep=='\'') && - (*(sep-1) != '\\')) - inquotes=!inquotes; - - if (!inquotes && - (*sep == ';') && /* separator */ - ( sep != str) && /* past string start */ - (*(sep-1) != '\\')) /* and NOT escaped */ - break; - } - - /* - * Limit the token to data between separators - */ - token = str; - if (*sep) { - str = sep + 1; /* start of command for next pass */ - *sep = '\0'; - } - else - str = sep; /* no more commands for next pass */ - debug_parser("token: \"%s\"\n", token); - - /* find macros in this token and replace them */ - process_macros (token, finaltoken); - - /* Extract arguments */ - if ((argc = parse_line (finaltoken, argv)) == 0) { - rc = -1; /* no command at all */ - continue; - } - - if (cmd_process(flag, argc, argv, &repeatable, NULL)) - rc = -1; - - /* Did the user stop this? */ - if (had_ctrlc ()) - return -1; /* if stopped then not repeatable */ - } - - return rc ? rc : repeatable; -} -#endif - /* * Run a command using the selected parser. * @@ -1436,10 +472,10 @@ 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 + * cli_run_command can return 0 or 1 for success, so clean up * its result. */ - if (builtin_run_command(cmd, flag) == -1) + if (cli_simple_run_command(cmd, flag) == -1) return 1; return 0; @@ -1449,49 +485,6 @@ int run_command(const char *cmd, int flag) #endif } -#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); - - return rcode; -} -#endif - int run_command_list(const char *cmd, int len, int flag) { int need_buff = 1; @@ -1525,7 +518,7 @@ int run_command_list(const char *cmd, int len, int flag) * doing a malloc() which is actually required only in a case that * is pretty rare. */ - rcode = builtin_run_command_list(buff, flag); + rcode = cli_simple_run_command_list(buff, flag); if (need_buff) free(buff); #endif diff --git a/include/cli.h b/include/cli.h index 0075bd44c94..b04539f3202 100644 --- a/include/cli.h +++ b/include/cli.h @@ -99,4 +99,9 @@ int readline_into_buffer(const char *const prompt, char *buffer, int timeout); */ int parse_line(char *line, char *argv[]); +/** bootretry_dont_retry() - Indicate that we should not retry the boot */ +void bootretry_dont_retry(void); + +#define endtick(seconds) (get_ticks() + (uint64_t)(seconds) * get_tbclk()) + #endif -- cgit v1.3.1 From 66ded17dfc8110f0d9aa9d50fe140a320bfa4e53 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 10 Apr 2014 20:01:28 -0600 Subject: Move autoboot code to autoboot.c The autoboot code is complex and long. It deserves its own file with a simple interface from main.c. Signed-off-by: Simon Glass --- common/Makefile | 5 + common/autoboot.c | 363 ++++++++++++++++++++++++++++++++++++++++++++++++++++ common/main.c | 366 +---------------------------------------------------- include/autoboot.h | 23 ++++ 4 files changed, 393 insertions(+), 364 deletions(-) create mode 100644 common/autoboot.c create mode 100644 include/autoboot.h (limited to 'common/Makefile') diff --git a/common/Makefile b/common/Makefile index 79983254521..ae798654ca3 100644 --- a/common/Makefile +++ b/common/Makefile @@ -23,6 +23,11 @@ obj-y += s_record.o obj-y += xyzModem.o obj-y += cmd_disk.o +# This option is not just y/n - it can have a numeric value +ifdef CONFIG_BOOTDELAY +obj-y += autoboot.o +endif + # boards obj-$(CONFIG_SYS_GENERIC_BOARD) += board_f.o obj-$(CONFIG_SYS_GENERIC_BOARD) += board_r.o diff --git a/common/autoboot.c b/common/autoboot.c new file mode 100644 index 00000000000..6933e3fc469 --- /dev/null +++ b/common/autoboot.c @@ -0,0 +1,363 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +#define MAX_DELAY_STOP_STR 32 + +#ifndef DEBUG_BOOTKEYS +#define DEBUG_BOOTKEYS 0 +#endif +#define debug_bootkeys(fmt, args...) \ + debug_cond(DEBUG_BOOTKEYS, fmt, ##args) + +/*************************************************************************** + * Watch for 'delay' seconds for autoboot stop or autoboot delay string. + * returns: 0 - no key string, allow autoboot 1 - got key string, abort + */ +# if defined(CONFIG_AUTOBOOT_KEYED) +static int abortboot_keyed(int bootdelay) +{ + int abort = 0; + uint64_t etime = endtick(bootdelay); + struct { + char *str; + u_int len; + int retry; + } + delaykey[] = { + { str: getenv("bootdelaykey"), retry: 1 }, + { str: getenv("bootdelaykey2"), retry: 1 }, + { str: getenv("bootstopkey"), retry: 0 }, + { str: getenv("bootstopkey2"), retry: 0 }, + }; + + char presskey[MAX_DELAY_STOP_STR]; + u_int presskey_len = 0; + 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); +# endif + +# ifdef CONFIG_AUTOBOOT_DELAY_STR + if (delaykey[0].str == NULL) + delaykey[0].str = CONFIG_AUTOBOOT_DELAY_STR; +# endif +# ifdef CONFIG_AUTOBOOT_DELAY_STR2 + if (delaykey[1].str == NULL) + delaykey[1].str = CONFIG_AUTOBOOT_DELAY_STR2; +# endif +# ifdef CONFIG_AUTOBOOT_STOP_STR + if (delaykey[2].str == NULL) + delaykey[2].str = CONFIG_AUTOBOOT_STOP_STR; +# endif +# ifdef CONFIG_AUTOBOOT_STOP_STR2 + if (delaykey[3].str == NULL) + delaykey[3].str = CONFIG_AUTOBOOT_STOP_STR2; +# endif + + for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i++) { + delaykey[i].len = delaykey[i].str == NULL ? + 0 : strlen(delaykey[i].str); + delaykey[i].len = delaykey[i].len > MAX_DELAY_STOP_STR ? + MAX_DELAY_STOP_STR : delaykey[i].len; + + presskey_max = presskey_max > delaykey[i].len ? + presskey_max : delaykey[i].len; + + 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. + */ + 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) { + debug_bootkeys("got %skey\n", + delaykey[i].retry ? "delay" : + "stop"); + +# ifdef CONFIG_BOOT_RETRY_TIME + /* don't retry auto boot */ + if (!delaykey[i].retry) + bootretry_dont_retry(); +# endif + abort = 1; + } + } + } while (!abort && get_ticks() <= etime); + + if (!abort) + debug_bootkeys("key timeout\n"); + +#ifdef CONFIG_SILENT_CONSOLE + if (abort) + gd->flags &= ~GD_FLG_SILENT; +#endif + + return abort; +} + +# else /* !defined(CONFIG_AUTOBOOT_KEYED) */ + +#ifdef CONFIG_MENUKEY +static int menukey; +#endif + +static int abortboot_normal(int bootdelay) +{ + int abort = 0; + unsigned long ts; + +#ifdef CONFIG_MENUPROMPT + printf(CONFIG_MENUPROMPT); +#else + if (bootdelay >= 0) + printf("Hit any key to stop autoboot: %2d ", bootdelay); +#endif + +#if defined CONFIG_ZERO_BOOTDELAY_CHECK + /* + * Check if key already pressed + * Don't check if bootdelay < 0 + */ + if (bootdelay >= 0) { + if (tstc()) { /* we got a key press */ + (void) getc(); /* consume input */ + puts("\b\b\b 0"); + abort = 1; /* don't auto boot */ + } + } +#endif + + while ((bootdelay > 0) && (!abort)) { + --bootdelay; + /* 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 */ +# ifdef CONFIG_MENUKEY + menukey = getc(); +# else + (void) getc(); /* consume input */ +# endif + break; + } + udelay(10000); + } while (!abort && get_timer(ts) < 1000); + + printf("\b\b\b%2d ", bootdelay); + } + + putc('\n'); + +#ifdef CONFIG_SILENT_CONSOLE + if (abort) + gd->flags &= ~GD_FLG_SILENT; +#endif + + return abort; +} +# endif /* CONFIG_AUTOBOOT_KEYED */ + +static int abortboot(int bootdelay) +{ +#ifdef CONFIG_AUTOBOOT_KEYED + return abortboot_keyed(bootdelay); +#else + return abortboot_normal(bootdelay); +#endif +} + +/* + * 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_OF_CONTROL) +static void secure_boot_cmd(char *cmd) +{ + cmd_tbl_t *cmdtp; + int rc; + + if (!cmd) { + printf("## Error: Secure boot command not specified\n"); + goto err; + } + + /* Disable Ctrl-C just in case some command is used that checks it. */ + disable_ctrlc(1); + + /* Find the command directly. */ + cmdtp = find_cmd(cmd); + if (!cmdtp) { + printf("## Error: \"%s\" not defined\n", cmd); + goto err; + } + + /* Run the command, forcing no flags and faking argc and argv. */ + rc = (cmdtp->cmd)(cmdtp, 0, 1, &cmd); + + /* Shouldn't ever return from boot command. */ + printf("## Error: \"%s\" returned (code %d)\n", cmd, rc); + +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(); +} + +static void process_fdt_options(const void *blob) +{ + ulong addr; + + /* 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)); + + /* 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 */ + +void bootdelay_process(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 */ + + 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 */ + +#ifdef CONFIG_POST + if (gd->flags & GD_FLG_POSTFAIL) { + s = getenv("failbootcmd"); + } else +#endif /* CONFIG_POST */ +#ifdef CONFIG_BOOTCOUNT_LIMIT + if (bootlimit && (bootcount > bootlimit)) { + printf("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n", + (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 != -1 && s && !abortboot(bootdelay)) { +#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC) + int prev = disable_ctrlc(1); /* disable Control C checking */ +#endif + + run_command_list(s, -1, 0); + +#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC) + disable_ctrlc(prev); /* restore Control C checking */ +#endif + } + +#ifdef CONFIG_MENUKEY + if (menukey == CONFIG_MENUKEY) { + s = getenv("menucmd"); + if (s) + run_command_list(s, -1, 0); + } +#endif /* CONFIG_MENUKEY */ +} diff --git a/common/main.c b/common/main.c index 88b0adb6b70..19d6f2caffd 100644 --- a/common/main.c +++ b/common/main.c @@ -8,384 +8,24 @@ /* #define DEBUG */ #include +#include #include #include -#include #include #include -#include -#include #include -DECLARE_GLOBAL_DATA_PTR; - /* * Board-specific Platform code can reimplement show_boot_progress () if needed */ void inline __show_boot_progress (int val) {} void show_boot_progress (int val) __attribute__((weak, alias("__show_boot_progress"))); -#define MAX_DELAY_STOP_STR 32 - -#ifndef DEBUG_BOOTKEYS -#define DEBUG_BOOTKEYS 0 -#endif -#define debug_bootkeys(fmt, args...) \ - debug_cond(DEBUG_BOOTKEYS, fmt, ##args) - #ifdef CONFIG_MODEM_SUPPORT int do_mdm_init = 0; extern void mdm_init(void); /* defined in board.c */ #endif -/*************************************************************************** - * Watch for 'delay' seconds for autoboot stop or autoboot delay string. - * returns: 0 - no key string, allow autoboot 1 - got key string, abort - */ -#if defined(CONFIG_BOOTDELAY) -# if defined(CONFIG_AUTOBOOT_KEYED) -static int abortboot_keyed(int bootdelay) -{ - int abort = 0; - uint64_t etime = endtick(bootdelay); - struct { - char* str; - u_int len; - int retry; - } - delaykey [] = { - { str: getenv ("bootdelaykey"), retry: 1 }, - { str: getenv ("bootdelaykey2"), retry: 1 }, - { str: getenv ("bootstopkey"), retry: 0 }, - { str: getenv ("bootstopkey2"), retry: 0 }, - }; - - char presskey [MAX_DELAY_STOP_STR]; - u_int presskey_len = 0; - 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); -# endif - -# ifdef CONFIG_AUTOBOOT_DELAY_STR - if (delaykey[0].str == NULL) - delaykey[0].str = CONFIG_AUTOBOOT_DELAY_STR; -# endif -# ifdef CONFIG_AUTOBOOT_DELAY_STR2 - if (delaykey[1].str == NULL) - delaykey[1].str = CONFIG_AUTOBOOT_DELAY_STR2; -# endif -# ifdef CONFIG_AUTOBOOT_STOP_STR - if (delaykey[2].str == NULL) - delaykey[2].str = CONFIG_AUTOBOOT_STOP_STR; -# endif -# ifdef CONFIG_AUTOBOOT_STOP_STR2 - if (delaykey[3].str == NULL) - delaykey[3].str = CONFIG_AUTOBOOT_STOP_STR2; -# endif - - for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i ++) { - delaykey[i].len = delaykey[i].str == NULL ? - 0 : strlen (delaykey[i].str); - delaykey[i].len = delaykey[i].len > MAX_DELAY_STOP_STR ? - MAX_DELAY_STOP_STR : delaykey[i].len; - - presskey_max = presskey_max > delaykey[i].len ? - presskey_max : delaykey[i].len; - - 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. - */ - 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) { - debug_bootkeys("got %skey\n", - delaykey[i].retry ? "delay" : - "stop"); - -# ifdef CONFIG_BOOT_RETRY_TIME - /* don't retry auto boot */ - if (! delaykey[i].retry) - bootretry_dont_retry(); -# endif - abort = 1; - } - } - } while (!abort && get_ticks() <= etime); - - if (!abort) - debug_bootkeys("key timeout\n"); - -#ifdef CONFIG_SILENT_CONSOLE - if (abort) - gd->flags &= ~GD_FLG_SILENT; -#endif - - return abort; -} - -# else /* !defined(CONFIG_AUTOBOOT_KEYED) */ - -#ifdef CONFIG_MENUKEY -static int menukey = 0; -#endif - -static int abortboot_normal(int bootdelay) -{ - int abort = 0; - unsigned long ts; - -#ifdef CONFIG_MENUPROMPT - printf(CONFIG_MENUPROMPT); -#else - if (bootdelay >= 0) - printf("Hit any key to stop autoboot: %2d ", bootdelay); -#endif - -#if defined CONFIG_ZERO_BOOTDELAY_CHECK - /* - * Check if key already pressed - * Don't check if bootdelay < 0 - */ - if (bootdelay >= 0) { - if (tstc()) { /* we got a key press */ - (void) getc(); /* consume input */ - puts ("\b\b\b 0"); - abort = 1; /* don't auto boot */ - } - } -#endif - - while ((bootdelay > 0) && (!abort)) { - --bootdelay; - /* 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 */ -# ifdef CONFIG_MENUKEY - menukey = getc(); -# else - (void) getc(); /* consume input */ -# endif - break; - } - udelay(10000); - } while (!abort && get_timer(ts) < 1000); - - printf("\b\b\b%2d ", bootdelay); - } - - putc('\n'); - -#ifdef CONFIG_SILENT_CONSOLE - if (abort) - gd->flags &= ~GD_FLG_SILENT; -#endif - - return abort; -} -# endif /* CONFIG_AUTOBOOT_KEYED */ - -static int abortboot(int bootdelay) -{ -#ifdef CONFIG_AUTOBOOT_KEYED - return abortboot_keyed(bootdelay); -#else - return abortboot_normal(bootdelay); -#endif -} -#endif /* CONFIG_BOOTDELAY */ - -/* - * 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_BOOTDELAY) && defined(CONFIG_OF_CONTROL) -static void secure_boot_cmd(char *cmd) -{ - cmd_tbl_t *cmdtp; - int rc; - - if (!cmd) { - printf("## Error: Secure boot command not specified\n"); - goto err; - } - - /* Disable Ctrl-C just in case some command is used that checks it. */ - disable_ctrlc(1); - - /* Find the command directly. */ - cmdtp = find_cmd(cmd); - if (!cmdtp) { - printf("## Error: \"%s\" not defined\n", cmd); - goto err; - } - - /* Run the command, forcing no flags and faking argc and argv. */ - rc = (cmdtp->cmd)(cmdtp, 0, 1, &cmd); - - /* Shouldn't ever return from boot command. */ - printf("## Error: \"%s\" returned (code %d)\n", cmd, rc); - -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(); -} - -static void process_fdt_options(const void *blob) -{ - ulong addr; - - /* 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)); - - /* 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_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 */ - - 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 */ - -#ifdef CONFIG_POST - if (gd->flags & GD_FLG_POSTFAIL) { - s = getenv("failbootcmd"); - } - else -#endif /* CONFIG_POST */ -#ifdef CONFIG_BOOTCOUNT_LIMIT - if (bootlimit && (bootcount > bootlimit)) { - printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n", - (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 != -1 && s && !abortboot(bootdelay)) { -#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC) - int prev = disable_ctrlc(1); /* disable Control C checking */ -#endif - - run_command_list(s, -1, 0); - -#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC) - disable_ctrlc(prev); /* restore Control C checking */ -#endif - } - -#ifdef CONFIG_MENUKEY - if (menukey == CONFIG_MENUKEY) { - s = getenv("menucmd"); - if (s) - run_command_list(s, -1, 0); - } -#endif /* CONFIG_MENUKEY */ -} -#endif /* CONFIG_BOOTDELAY */ - void main_loop(void) { #ifdef CONFIG_PREBOOT @@ -444,9 +84,7 @@ void main_loop(void) update_tftp(0UL); #endif /* CONFIG_UPDATE_TFTP */ -#ifdef CONFIG_BOOTDELAY - process_boot_delay(); -#endif + bootdelay_process(); /* * Main Loop for Monitor Command Processing */ diff --git a/include/autoboot.h b/include/autoboot.h new file mode 100644 index 00000000000..aaae4afff4b --- /dev/null +++ b/include/autoboot.h @@ -0,0 +1,23 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Add to readline cmdline-editing by + * (C) Copyright 2005 + * JinHua Luo, GuangDong Linux Center, + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __AUTOBOOT_H +#define __AUTOBOOT_H + +#ifdef CONFIG_BOOTDELAY +void bootdelay_process(void); +#else +static inline void bootdelay_process(void) +{ +} +#endif + +#endif -- cgit v1.3.1 From 30354978ff470470c15caea2566b61b5792ad277 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 10 Apr 2014 20:01:29 -0600 Subject: Move command line API into cli.c We now have a single entry point to the CLI, whether simple or hush. Put this in its own file. Signed-off-by: Simon Glass --- common/Makefile | 1 + common/cli.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ common/main.c | 93 ------------------------------------------------- 3 files changed, 107 insertions(+), 93 deletions(-) create mode 100644 common/cli.c (limited to 'common/Makefile') diff --git a/common/Makefile b/common/Makefile index ae798654ca3..81721f9b389 100644 --- a/common/Makefile +++ b/common/Makefile @@ -18,6 +18,7 @@ endif # We always have this since drivers/ddr/fs/interactive.c needs it obj-y += cli_simple.o +obj-y += cli.o obj-y += cli_readline.o obj-y += s_record.o obj-y += xyzModem.o diff --git a/common/cli.c b/common/cli.c new file mode 100644 index 00000000000..9cf7ba1bd79 --- /dev/null +++ b/common/cli.c @@ -0,0 +1,106 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Add to readline cmdline-editing by + * (C) Copyright 2005 + * JinHua Luo, GuangDong Linux Center, + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +/* + * 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 + /* + * cli_run_command can return 0 or 1 for success, so clean up + * its result. + */ + if (cli_simple_run_command(cmd, flag) == -1) + return 1; + + return 0; +#else + return parse_string_outer(cmd, + FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP); +#endif +} + +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 = cli_simple_run_command_list(buff, flag); + if (need_buff) + free(buff); +#endif + + return rcode; +} + +/****************************************************************************/ + +#if defined(CONFIG_CMD_RUN) +int do_run(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int i; + + if (argc < 2) + return CMD_RET_USAGE; + + for (i = 1; i < argc; ++i) { + char *arg; + + arg = getenv(argv[i]); + if (arg == NULL) { + printf("## Error: \"%s\" not defined\n", argv[i]); + return 1; + } + + if (run_command(arg, flag) != 0) + return 1; + } + return 0; +} +#endif diff --git a/common/main.c b/common/main.c index 19d6f2caffd..c4ed846c3b2 100644 --- a/common/main.c +++ b/common/main.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -96,95 +95,3 @@ void main_loop(void) cli_loop(); #endif /*CONFIG_SYS_HUSH_PARSER*/ } - -/****************************************************************************/ - -/* - * 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 - /* - * cli_run_command can return 0 or 1 for success, so clean up - * its result. - */ - if (cli_simple_run_command(cmd, flag) == -1) - return 1; - - return 0; -#else - return parse_string_outer(cmd, - FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP); -#endif -} - -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 = cli_simple_run_command_list(buff, flag); - if (need_buff) - free(buff); -#endif - - return rcode; -} - -/****************************************************************************/ - -#if defined(CONFIG_CMD_RUN) -int do_run (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) -{ - int i; - - if (argc < 2) - return CMD_RET_USAGE; - - for (i=1; i Date: Thu, 10 Apr 2014 20:01:30 -0600 Subject: Move bootretry code into bootretry.c and clean up This code is only used by one board, so it seems a shame to clutter up the readline code with it. Move it into its own file. Signed-off-by: Simon Glass --- board/hymod/hymod.c | 1 + board/hymod/input.c | 1 + common/Makefile | 5 +++++ common/autoboot.c | 1 + common/bootretry.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++ common/cli_hush.c | 1 + common/cli_readline.c | 60 +++++---------------------------------------------- common/cli_simple.c | 1 + common/cmd_i2c.c | 1 + common/cmd_mem.c | 1 + common/cmd_pci.c | 1 + include/bootretry.h | 31 ++++++++++++++++++++++++++ include/common.h | 2 -- 13 files changed, 108 insertions(+), 57 deletions(-) create mode 100644 common/bootretry.c create mode 100644 include/bootretry.h (limited to 'common/Makefile') diff --git a/board/hymod/hymod.c b/board/hymod/hymod.c index 55ffd676cf5..f6990e929f9 100644 --- a/board/hymod/hymod.c +++ b/board/hymod/hymod.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include diff --git a/board/hymod/input.c b/board/hymod/input.c index 59ad6aa3ed9..2f857c06ae9 100644 --- a/board/hymod/input.c +++ b/board/hymod/input.c @@ -6,6 +6,7 @@ */ #include +#include #include int diff --git a/common/Makefile b/common/Makefile index 81721f9b389..391a8d6230a 100644 --- a/common/Makefile +++ b/common/Makefile @@ -29,6 +29,11 @@ ifdef CONFIG_BOOTDELAY obj-y += autoboot.o endif +# This option is not just y/n - it can have a numeric value +ifdef CONFIG_BOOT_RETRY_TIME +obj-y += bootretry.o +endif + # boards obj-$(CONFIG_SYS_GENERIC_BOARD) += board_f.o obj-$(CONFIG_SYS_GENERIC_BOARD) += board_r.o diff --git a/common/autoboot.c b/common/autoboot.c index 6933e3fc469..5f8d9c3445c 100644 --- a/common/autoboot.c +++ b/common/autoboot.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include diff --git a/common/bootretry.c b/common/bootretry.c new file mode 100644 index 00000000000..12653c00d0f --- /dev/null +++ b/common/bootretry.c @@ -0,0 +1,59 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#ifndef CONFIG_BOOT_RETRY_MIN +#define CONFIG_BOOT_RETRY_MIN CONFIG_BOOT_RETRY_TIME +#endif + +static uint64_t endtime; /* must be set, default is instant timeout */ +static int retry_time = -1; /* -1 so can call readline before main_loop */ + +/*************************************************************************** + * initialize command line timeout + */ +void init_cmd_timeout(void) +{ + char *s = getenv("bootretry"); + + if (s != NULL) + retry_time = (int)simple_strtol(s, NULL, 10); + else + retry_time = CONFIG_BOOT_RETRY_TIME; + + if (retry_time >= 0 && retry_time < CONFIG_BOOT_RETRY_MIN) + retry_time = CONFIG_BOOT_RETRY_MIN; +} + +/*************************************************************************** + * reset command line timeout to retry_time seconds + */ +void reset_cmd_timeout(void) +{ + endtime = endtick(retry_time); +} + +int bootretry_tstc_timeout(void) +{ + while (!tstc()) { /* while no incoming data */ + if (retry_time >= 0 && get_ticks() > endtime) + return -ETIMEDOUT; + WATCHDOG_RESET(); + } + + return 0; +} + +void bootretry_dont_retry(void) +{ + retry_time = -1; +} diff --git a/common/cli_hush.c b/common/cli_hush.c index a612bfbef51..d6544ae6311 100644 --- a/common/cli_hush.c +++ b/common/cli_hush.c @@ -79,6 +79,7 @@ #include /* malloc, free, realloc*/ #include /* isalpha, isdigit */ #include /* readline */ +#include #include #include #include /* find_cmd */ diff --git a/common/cli_readline.c b/common/cli_readline.c index df446b8c836..9a9fb35b712 100644 --- a/common/cli_readline.c +++ b/common/cli_readline.c @@ -10,6 +10,7 @@ */ #include +#include #include #include @@ -18,17 +19,8 @@ DECLARE_GLOBAL_DATA_PTR; 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; /* must be set, default is instant timeout */ -static int retry_time = -1; /* -1 so can call readline before main_loop */ -#endif - char console_buffer[CONFIG_SYS_CBSIZE + 1]; /* console I/O buffer */ -#ifndef CONFIG_BOOT_RETRY_MIN -#define CONFIG_BOOT_RETRY_MIN CONFIG_BOOT_RETRY_TIME -#endif - static char *delete_char (char *buffer, char *p, int *colp, int *np, int plen) { char *s; @@ -267,13 +259,8 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len, cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len); while (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 (bootretry_tstc_timeout()) + return -2; /* timed out */ if (first && timeout) { uint64_t etime = endtick(timeout); @@ -539,13 +526,8 @@ int cli_readline_into_buffer(const char *const prompt, char *buffer, col = plen; for (;;) { -#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 (bootretry_tstc_timeout()) + return -2; /* timed out */ WATCHDOG_RESET(); /* Trigger watchdog, if needed */ #ifdef CONFIG_SHOW_ACTIVITY @@ -637,35 +619,3 @@ int cli_readline_into_buffer(const char *const prompt, char *buffer, } #endif } - -#ifdef CONFIG_BOOT_RETRY_TIME -/*************************************************************************** - * initialize command line timeout - */ -void init_cmd_timeout(void) -{ - char *s = getenv("bootretry"); - - if (s != NULL) - retry_time = (int)simple_strtol(s, NULL, 10); - else - retry_time = CONFIG_BOOT_RETRY_TIME; - - if (retry_time >= 0 && retry_time < CONFIG_BOOT_RETRY_MIN) - retry_time = CONFIG_BOOT_RETRY_MIN; -} - -/*************************************************************************** - * reset command line timeout to retry_time seconds - */ -void reset_cmd_timeout(void) -{ - endtime = endtick(retry_time); -} - -void bootretry_dont_retry(void) -{ - retry_time = -1; -} - -#endif diff --git a/common/cli_simple.c b/common/cli_simple.c index 3039cfcca1f..5b7e2ce7d53 100644 --- a/common/cli_simple.c +++ b/common/cli_simple.c @@ -10,6 +10,7 @@ */ #include +#include #include #include diff --git a/common/cmd_i2c.c b/common/cmd_i2c.c index 7158b5a2db1..4fa12138c35 100644 --- a/common/cmd_i2c.c +++ b/common/cmd_i2c.c @@ -66,6 +66,7 @@ */ #include +#include #include #include #include diff --git a/common/cmd_mem.c b/common/cmd_mem.c index 15a4b84efdc..9bd7c0eedb9 100644 --- a/common/cmd_mem.c +++ b/common/cmd_mem.c @@ -12,6 +12,7 @@ */ #include +#include #include #include #ifdef CONFIG_HAS_DATAFLASH diff --git a/common/cmd_pci.c b/common/cmd_pci.c index 1ed55cedb16..180b35ab0f7 100644 --- a/common/cmd_pci.c +++ b/common/cmd_pci.c @@ -14,6 +14,7 @@ */ #include +#include #include #include #include diff --git a/include/bootretry.h b/include/bootretry.h new file mode 100644 index 00000000000..025c29d526a --- /dev/null +++ b/include/bootretry.h @@ -0,0 +1,31 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __bootretry_h +#define __bootretry_h + +#ifdef CONFIG_BOOT_RETRY_TIME +/** + * bootretry_tstc_timeout() - ensure we get a keypress before timeout + * + * Check for a keypress repeatedly, resetting the watchdog each time. If a + * keypress is not received within the command timeout, return an error. + * + * @return 0 if a key is received in time, -ETIMEDOUT if not + */ +int bootretry_tstc_timeout(void); +#else +static inline int bootretry_tstc_timeout(void) +{ + return 0; +} +#endif + +void init_cmd_timeout(void); +void reset_cmd_timeout(void); + +#endif diff --git a/include/common.h b/include/common.h index 75cb525f3ec..91745cf790f 100644 --- a/include/common.h +++ b/include/common.h @@ -286,8 +286,6 @@ int run_command(const char *cmd, int flag); * @return 0 on success, or != 0 on error. */ int run_command_list(const char *cmd, int len, int flag); -void init_cmd_timeout(void); -void reset_cmd_timeout(void); extern char console_buffer[]; /* arch/$(ARCH)/lib/board.c */ -- cgit v1.3.1 From b639640371ed38c76602387af865b814967473ba Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 12 Jun 2014 07:24:46 -0600 Subject: bootm: Split out code from cmd_bootm.c This file has code in three different categories: - Command processing - OS-specific boot code - Locating images and setting up to boot Only the first category really belongs in a file called cmd_bootm.c. Leave the command processing code where it is. Split out the OS-specific boot code into bootm_os.c. Split out the other code into bootm.c Header files and extern declarations are tidied but otherwise no code changes are made, to make it easier to review. Signed-off-by: Simon Glass --- common/Makefile | 2 +- common/bootm.c | 812 +++++++++++++++++++++++++++++++ common/bootm_os.c | 480 +++++++++++++++++++ common/cmd_bootm.c | 1345 +--------------------------------------------------- include/bootm.h | 55 +++ 5 files changed, 1356 insertions(+), 1338 deletions(-) create mode 100644 common/bootm.c create mode 100644 common/bootm_os.c create mode 100644 include/bootm.h (limited to 'common/Makefile') diff --git a/common/Makefile b/common/Makefile index 391a8d6230a..f6cd98012a1 100644 --- a/common/Makefile +++ b/common/Makefile @@ -40,7 +40,7 @@ obj-$(CONFIG_SYS_GENERIC_BOARD) += board_r.o # core command obj-y += cmd_boot.o -obj-$(CONFIG_CMD_BOOTM) += cmd_bootm.o +obj-$(CONFIG_CMD_BOOTM) += cmd_bootm.o bootm.o bootm_os.o obj-y += cmd_help.o obj-y += cmd_version.o diff --git a/common/bootm.c b/common/bootm.c new file mode 100644 index 00000000000..27a7f028a6f --- /dev/null +++ b/common/bootm.c @@ -0,0 +1,812 @@ +/* + * (C) Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_CMD_USB) +#include +#endif + +DECLARE_GLOBAL_DATA_PTR; + +#ifndef CONFIG_SYS_BOOTM_LEN +/* use 8MByte as default max gunzip size */ +#define CONFIG_SYS_BOOTM_LEN 0x800000 +#endif + +#define IH_INITRD_ARCH IH_ARCH_DEFAULT + +static const void *boot_get_kernel(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[], bootm_headers_t *images, + ulong *os_data, ulong *os_len); + +#ifdef CONFIG_LMB +static void boot_start_lmb(bootm_headers_t *images) +{ + ulong mem_start; + phys_size_t mem_size; + + lmb_init(&images->lmb); + + mem_start = getenv_bootm_low(); + mem_size = getenv_bootm_size(); + + lmb_add(&images->lmb, (phys_addr_t)mem_start, mem_size); + + arch_lmb_reserve(&images->lmb); + board_lmb_reserve(&images->lmb); +} +#else +#define lmb_reserve(lmb, base, size) +static inline void boot_start_lmb(bootm_headers_t *images) { } +#endif + +static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + memset((void *)&images, 0, sizeof(images)); + images.verify = getenv_yesno("verify"); + + boot_start_lmb(&images); + + bootstage_mark_name(BOOTSTAGE_ID_BOOTM_START, "bootm_start"); + images.state = BOOTM_STATE_START; + + return 0; +} + +static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + const void *os_hdr; + bool ep_found = false; + + /* get kernel image header, start address and length */ + os_hdr = boot_get_kernel(cmdtp, flag, argc, argv, + &images, &images.os.image_start, &images.os.image_len); + if (images.os.image_len == 0) { + puts("ERROR: can't get kernel image!\n"); + return 1; + } + + /* get image parameters */ + switch (genimg_get_format(os_hdr)) { +#if defined(CONFIG_IMAGE_FORMAT_LEGACY) + case IMAGE_FORMAT_LEGACY: + images.os.type = image_get_type(os_hdr); + images.os.comp = image_get_comp(os_hdr); + images.os.os = image_get_os(os_hdr); + + images.os.end = image_get_image_end(os_hdr); + images.os.load = image_get_load(os_hdr); + break; +#endif +#if defined(CONFIG_FIT) + case IMAGE_FORMAT_FIT: + if (fit_image_get_type(images.fit_hdr_os, + images.fit_noffset_os, + &images.os.type)) { + puts("Can't get image type!\n"); + bootstage_error(BOOTSTAGE_ID_FIT_TYPE); + return 1; + } + + if (fit_image_get_comp(images.fit_hdr_os, + images.fit_noffset_os, + &images.os.comp)) { + puts("Can't get image compression!\n"); + bootstage_error(BOOTSTAGE_ID_FIT_COMPRESSION); + return 1; + } + + if (fit_image_get_os(images.fit_hdr_os, images.fit_noffset_os, + &images.os.os)) { + puts("Can't get image OS!\n"); + bootstage_error(BOOTSTAGE_ID_FIT_OS); + return 1; + } + + images.os.end = fit_get_end(images.fit_hdr_os); + + if (fit_image_get_load(images.fit_hdr_os, images.fit_noffset_os, + &images.os.load)) { + puts("Can't get image load address!\n"); + bootstage_error(BOOTSTAGE_ID_FIT_LOADADDR); + return 1; + } + break; +#endif +#ifdef CONFIG_ANDROID_BOOT_IMAGE + case IMAGE_FORMAT_ANDROID: + images.os.type = IH_TYPE_KERNEL; + images.os.comp = IH_COMP_NONE; + images.os.os = IH_OS_LINUX; + images.ep = images.os.load; + ep_found = true; + + images.os.end = android_image_get_end(os_hdr); + images.os.load = android_image_get_kload(os_hdr); + break; +#endif + default: + puts("ERROR: unknown image format type!\n"); + return 1; + } + + /* find kernel entry point */ + if (images.legacy_hdr_valid) { + images.ep = image_get_ep(&images.legacy_hdr_os_copy); +#if defined(CONFIG_FIT) + } else if (images.fit_uname_os) { + int ret; + + ret = fit_image_get_entry(images.fit_hdr_os, + images.fit_noffset_os, &images.ep); + if (ret) { + puts("Can't get entry point property!\n"); + return 1; + } +#endif + } else if (!ep_found) { + puts("Could not find kernel entry point!\n"); + return 1; + } + + if (images.os.type == IH_TYPE_KERNEL_NOLOAD) { + images.os.load = images.os.image_start; + images.ep += images.os.load; + } + + images.os.start = (ulong)os_hdr; + + return 0; +} + +static int bootm_find_ramdisk(int flag, int argc, char * const argv[]) +{ + int ret; + + /* find ramdisk */ + ret = boot_get_ramdisk(argc, argv, &images, IH_INITRD_ARCH, + &images.rd_start, &images.rd_end); + if (ret) { + puts("Ramdisk image is corrupt or invalid\n"); + return 1; + } + + return 0; +} + +#if defined(CONFIG_OF_LIBFDT) +static int bootm_find_fdt(int flag, int argc, char * const argv[]) +{ + int ret; + + /* find flattened device tree */ + ret = boot_get_fdt(flag, argc, argv, IH_ARCH_DEFAULT, &images, + &images.ft_addr, &images.ft_len); + if (ret) { + puts("Could not find a valid device tree\n"); + return 1; + } + + set_working_fdt_addr(images.ft_addr); + + return 0; +} +#endif + +int bootm_find_ramdisk_fdt(int flag, int argc, char * const argv[]) +{ + if (bootm_find_ramdisk(flag, argc, argv)) + return 1; + +#if defined(CONFIG_OF_LIBFDT) + if (bootm_find_fdt(flag, argc, argv)) + return 1; +#endif + + return 0; +} + +static int bootm_find_other(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + if (((images.os.type == IH_TYPE_KERNEL) || + (images.os.type == IH_TYPE_KERNEL_NOLOAD) || + (images.os.type == IH_TYPE_MULTI)) && + (images.os.os == IH_OS_LINUX || + images.os.os == IH_OS_VXWORKS)) + return bootm_find_ramdisk_fdt(flag, argc, argv); + + return 0; +} + +static int bootm_load_os(bootm_headers_t *images, unsigned long *load_end, + int boot_progress) +{ + image_info_t os = images->os; + uint8_t comp = os.comp; + ulong load = os.load; + ulong blob_start = os.start; + ulong blob_end = os.end; + ulong image_start = os.image_start; + ulong image_len = os.image_len; + __maybe_unused uint unc_len = CONFIG_SYS_BOOTM_LEN; + int no_overlap = 0; + void *load_buf, *image_buf; +#if defined(CONFIG_LZMA) || defined(CONFIG_LZO) + int ret; +#endif /* defined(CONFIG_LZMA) || defined(CONFIG_LZO) */ + + const char *type_name = genimg_get_type_name(os.type); + + load_buf = map_sysmem(load, unc_len); + image_buf = map_sysmem(image_start, image_len); + switch (comp) { + case IH_COMP_NONE: + if (load == image_start) { + printf(" XIP %s ... ", type_name); + no_overlap = 1; + } else { + printf(" Loading %s ... ", type_name); + memmove_wd(load_buf, image_buf, image_len, CHUNKSZ); + } + *load_end = load + image_len; + break; +#ifdef CONFIG_GZIP + case IH_COMP_GZIP: + printf(" Uncompressing %s ... ", type_name); + if (gunzip(load_buf, unc_len, image_buf, &image_len) != 0) { + puts("GUNZIP: uncompress, out-of-mem or overwrite error - must RESET board to recover\n"); + if (boot_progress) + bootstage_error(BOOTSTAGE_ID_DECOMP_IMAGE); + return BOOTM_ERR_RESET; + } + + *load_end = load + image_len; + break; +#endif /* CONFIG_GZIP */ +#ifdef CONFIG_BZIP2 + case IH_COMP_BZIP2: + printf(" Uncompressing %s ... ", type_name); + /* + * If we've got less than 4 MB of malloc() space, + * use slower decompression algorithm which requires + * at most 2300 KB of memory. + */ + int i = BZ2_bzBuffToBuffDecompress(load_buf, &unc_len, + image_buf, image_len, + CONFIG_SYS_MALLOC_LEN < (4096 * 1024), 0); + if (i != BZ_OK) { + printf("BUNZIP2: uncompress or overwrite error %d - must RESET board to recover\n", + i); + if (boot_progress) + bootstage_error(BOOTSTAGE_ID_DECOMP_IMAGE); + return BOOTM_ERR_RESET; + } + + *load_end = load + unc_len; + break; +#endif /* CONFIG_BZIP2 */ +#ifdef CONFIG_LZMA + case IH_COMP_LZMA: { + SizeT lzma_len = unc_len; + printf(" Uncompressing %s ... ", type_name); + + ret = lzmaBuffToBuffDecompress(load_buf, &lzma_len, + image_buf, image_len); + unc_len = lzma_len; + if (ret != SZ_OK) { + printf("LZMA: uncompress or overwrite error %d - must RESET board to recover\n", + ret); + bootstage_error(BOOTSTAGE_ID_DECOMP_IMAGE); + return BOOTM_ERR_RESET; + } + *load_end = load + unc_len; + break; + } +#endif /* CONFIG_LZMA */ +#ifdef CONFIG_LZO + case IH_COMP_LZO: { + size_t size = unc_len; + + printf(" Uncompressing %s ... ", type_name); + + ret = lzop_decompress(image_buf, image_len, load_buf, &size); + if (ret != LZO_E_OK) { + printf("LZO: uncompress or overwrite error %d - must RESET board to recover\n", + ret); + if (boot_progress) + bootstage_error(BOOTSTAGE_ID_DECOMP_IMAGE); + return BOOTM_ERR_RESET; + } + + *load_end = load + size; + break; + } +#endif /* CONFIG_LZO */ + default: + printf("Unimplemented compression type %d\n", comp); + return BOOTM_ERR_UNIMPLEMENTED; + } + + flush_cache(load, (*load_end - load) * sizeof(ulong)); + + puts("OK\n"); + debug(" kernel loaded at 0x%08lx, end = 0x%08lx\n", load, *load_end); + bootstage_mark(BOOTSTAGE_ID_KERNEL_LOADED); + + if (!no_overlap && (load < blob_end) && (*load_end > blob_start)) { + debug("images.os.start = 0x%lX, images.os.end = 0x%lx\n", + blob_start, blob_end); + debug("images.os.load = 0x%lx, load_end = 0x%lx\n", load, + *load_end); + + /* Check what type of image this is. */ + if (images->legacy_hdr_valid) { + if (image_get_type(&images->legacy_hdr_os_copy) + == IH_TYPE_MULTI) + puts("WARNING: legacy format multi component image overwritten\n"); + return BOOTM_ERR_OVERLAP; + } else { + puts("ERROR: new format image overwritten - must RESET the board to recover\n"); + bootstage_error(BOOTSTAGE_ID_OVERWRITTEN); + return BOOTM_ERR_RESET; + } + } + + return 0; +} + +/** + * bootm_disable_interrupts() - Disable interrupts in preparation for load/boot + * + * @return interrupt flag (0 if interrupts were disabled, non-zero if they were + * enabled) + */ +ulong bootm_disable_interrupts(void) +{ + ulong iflag; + + /* + * We have reached the point of no return: we are going to + * overwrite all exception vector code, so we cannot easily + * recover from any failures any more... + */ + iflag = disable_interrupts(); +#ifdef CONFIG_NETCONSOLE + /* Stop the ethernet stack if NetConsole could have left it up */ + eth_halt(); + eth_unregister(eth_get_dev()); +#endif + +#if defined(CONFIG_CMD_USB) + /* + * turn off USB to prevent the host controller from writing to the + * SDRAM while Linux is booting. This could happen (at least for OHCI + * controller), because the HCCA (Host Controller Communication Area) + * lies within the SDRAM and the host controller writes continously to + * this area (as busmaster!). The HccaFrameNumber is for example + * updated every 1 ms within the HCCA structure in SDRAM! For more + * details see the OpenHCI specification. + */ + usb_stop(); +#endif + return iflag; +} + +#if defined(CONFIG_SILENT_CONSOLE) && !defined(CONFIG_SILENT_U_BOOT_ONLY) + +#define CONSOLE_ARG "console=" +#define CONSOLE_ARG_LEN (sizeof(CONSOLE_ARG) - 1) + +static void fixup_silent_linux(void) +{ + char *buf; + const char *env_val; + char *cmdline = getenv("bootargs"); + int want_silent; + + /* + * Only fix cmdline when requested. The environment variable can be: + * + * no - we never fixup + * yes - we always fixup + * unset - we rely on the console silent flag + */ + want_silent = getenv_yesno("silent_linux"); + if (want_silent == 0) + return; + else if (want_silent == -1 && !(gd->flags & GD_FLG_SILENT)) + return; + + debug("before silent fix-up: %s\n", cmdline); + if (cmdline && (cmdline[0] != '\0')) { + char *start = strstr(cmdline, CONSOLE_ARG); + + /* Allocate space for maximum possible new command line */ + buf = malloc(strlen(cmdline) + 1 + CONSOLE_ARG_LEN + 1); + if (!buf) { + debug("%s: out of memory\n", __func__); + return; + } + + if (start) { + char *end = strchr(start, ' '); + int num_start_bytes = start - cmdline + CONSOLE_ARG_LEN; + + strncpy(buf, cmdline, num_start_bytes); + if (end) + strcpy(buf + num_start_bytes, end); + else + buf[num_start_bytes] = '\0'; + } else { + sprintf(buf, "%s %s", cmdline, CONSOLE_ARG); + } + env_val = buf; + } else { + buf = NULL; + env_val = CONSOLE_ARG; + } + + setenv("bootargs", env_val); + debug("after silent fix-up: %s\n", env_val); + free(buf); +} +#endif /* CONFIG_SILENT_CONSOLE */ + +/** + * Execute selected states of the bootm command. + * + * Note the arguments to this state must be the first argument, Any 'bootm' + * or sub-command arguments must have already been taken. + * + * Note that if states contains more than one flag it MUST contain + * BOOTM_STATE_START, since this handles and consumes the command line args. + * + * Also note that aside from boot_os_fn functions and bootm_load_os no other + * functions we store the return value of in 'ret' may use a negative return + * value, without special handling. + * + * @param cmdtp Pointer to bootm command table entry + * @param flag Command flags (CMD_FLAG_...) + * @param argc Number of subcommand arguments (0 = no arguments) + * @param argv Arguments + * @param states Mask containing states to run (BOOTM_STATE_...) + * @param images Image header information + * @param boot_progress 1 to show boot progress, 0 to not do this + * @return 0 if ok, something else on error. Some errors will cause this + * function to perform a reboot! If states contains BOOTM_STATE_OS_GO + * then the intent is to boot an OS, so this function will not return + * unless the image type is standalone. + */ +int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], + int states, bootm_headers_t *images, int boot_progress) +{ + boot_os_fn *boot_fn; + ulong iflag = 0; + int ret = 0, need_boot_fn; + + images->state |= states; + + /* + * Work through the states and see how far we get. We stop on + * any error. + */ + if (states & BOOTM_STATE_START) + ret = bootm_start(cmdtp, flag, argc, argv); + + if (!ret && (states & BOOTM_STATE_FINDOS)) + ret = bootm_find_os(cmdtp, flag, argc, argv); + + if (!ret && (states & BOOTM_STATE_FINDOTHER)) { + ret = bootm_find_other(cmdtp, flag, argc, argv); + argc = 0; /* consume the args */ + } + + /* Load the OS */ + if (!ret && (states & BOOTM_STATE_LOADOS)) { + ulong load_end; + + iflag = bootm_disable_interrupts(); + ret = bootm_load_os(images, &load_end, 0); + if (ret == 0) + lmb_reserve(&images->lmb, images->os.load, + (load_end - images->os.load)); + else if (ret && ret != BOOTM_ERR_OVERLAP) + goto err; + else if (ret == BOOTM_ERR_OVERLAP) + ret = 0; +#if defined(CONFIG_SILENT_CONSOLE) && !defined(CONFIG_SILENT_U_BOOT_ONLY) + if (images->os.os == IH_OS_LINUX) + fixup_silent_linux(); +#endif + } + + /* Relocate the ramdisk */ +#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH + if (!ret && (states & BOOTM_STATE_RAMDISK)) { + ulong rd_len = images->rd_end - images->rd_start; + + ret = boot_ramdisk_high(&images->lmb, images->rd_start, + rd_len, &images->initrd_start, &images->initrd_end); + if (!ret) { + setenv_hex("initrd_start", images->initrd_start); + setenv_hex("initrd_end", images->initrd_end); + } + } +#endif +#if defined(CONFIG_OF_LIBFDT) && defined(CONFIG_LMB) + if (!ret && (states & BOOTM_STATE_FDT)) { + boot_fdt_add_mem_rsv_regions(&images->lmb, images->ft_addr); + ret = boot_relocate_fdt(&images->lmb, &images->ft_addr, + &images->ft_len); + } +#endif + + /* From now on, we need the OS boot function */ + if (ret) + return ret; + boot_fn = bootm_os_get_boot_func(images->os.os); + need_boot_fn = states & (BOOTM_STATE_OS_CMDLINE | + BOOTM_STATE_OS_BD_T | BOOTM_STATE_OS_PREP | + BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO); + if (boot_fn == NULL && need_boot_fn) { + if (iflag) + enable_interrupts(); + printf("ERROR: booting os '%s' (%d) is not supported\n", + genimg_get_os_name(images->os.os), images->os.os); + bootstage_error(BOOTSTAGE_ID_CHECK_BOOT_OS); + return 1; + } + + /* Call various other states that are not generally used */ + if (!ret && (states & BOOTM_STATE_OS_CMDLINE)) + ret = boot_fn(BOOTM_STATE_OS_CMDLINE, argc, argv, images); + if (!ret && (states & BOOTM_STATE_OS_BD_T)) + ret = boot_fn(BOOTM_STATE_OS_BD_T, argc, argv, images); + if (!ret && (states & BOOTM_STATE_OS_PREP)) + ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images); + +#ifdef CONFIG_TRACE + /* Pretend to run the OS, then run a user command */ + if (!ret && (states & BOOTM_STATE_OS_FAKE_GO)) { + char *cmd_list = getenv("fakegocmd"); + + ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_FAKE_GO, + images, boot_fn); + if (!ret && cmd_list) + ret = run_command_list(cmd_list, -1, flag); + } +#endif + + /* Check for unsupported subcommand. */ + if (ret) { + puts("subcommand not supported\n"); + return ret; + } + + /* Now run the OS! We hope this doesn't return */ + if (!ret && (states & BOOTM_STATE_OS_GO)) + ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO, + images, boot_fn); + + /* Deal with any fallout */ +err: + if (iflag) + enable_interrupts(); + + if (ret == BOOTM_ERR_UNIMPLEMENTED) + bootstage_error(BOOTSTAGE_ID_DECOMP_UNIMPL); + else if (ret == BOOTM_ERR_RESET) + do_reset(cmdtp, flag, argc, argv); + + return ret; +} + +#if defined(CONFIG_IMAGE_FORMAT_LEGACY) +/** + * image_get_kernel - verify legacy format kernel image + * @img_addr: in RAM address of the legacy format image to be verified + * @verify: data CRC verification flag + * + * image_get_kernel() verifies legacy image integrity and returns pointer to + * legacy image header if image verification was completed successfully. + * + * returns: + * pointer to a legacy image header if valid image was found + * otherwise return NULL + */ +static image_header_t *image_get_kernel(ulong img_addr, int verify) +{ + image_header_t *hdr = (image_header_t *)img_addr; + + if (!image_check_magic(hdr)) { + puts("Bad Magic Number\n"); + bootstage_error(BOOTSTAGE_ID_CHECK_MAGIC); + return NULL; + } + bootstage_mark(BOOTSTAGE_ID_CHECK_HEADER); + + if (!image_check_hcrc(hdr)) { + puts("Bad Header Checksum\n"); + bootstage_error(BOOTSTAGE_ID_CHECK_HEADER); + return NULL; + } + + bootstage_mark(BOOTSTAGE_ID_CHECK_CHECKSUM); + image_print_contents(hdr); + + if (verify) { + puts(" Verifying Checksum ... "); + if (!image_check_dcrc(hdr)) { + printf("Bad Data CRC\n"); + bootstage_error(BOOTSTAGE_ID_CHECK_CHECKSUM); + return NULL; + } + puts("OK\n"); + } + bootstage_mark(BOOTSTAGE_ID_CHECK_ARCH); + + if (!image_check_target_arch(hdr)) { + printf("Unsupported Architecture 0x%x\n", image_get_arch(hdr)); + bootstage_error(BOOTSTAGE_ID_CHECK_ARCH); + return NULL; + } + return hdr; +} +#endif + +/** + * boot_get_kernel - find kernel image + * @os_data: pointer to a ulong variable, will hold os data start address + * @os_len: pointer to a ulong variable, will hold os data length + * + * boot_get_kernel() tries to find a kernel image, verifies its integrity + * and locates kernel data. + * + * returns: + * pointer to image header if valid image was found, plus kernel start + * address and length, otherwise NULL + */ +static const void *boot_get_kernel(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[], bootm_headers_t *images, + ulong *os_data, ulong *os_len) +{ +#if defined(CONFIG_IMAGE_FORMAT_LEGACY) + image_header_t *hdr; +#endif + ulong img_addr; + const void *buf; +#if defined(CONFIG_FIT) + const char *fit_uname_config = NULL; + const char *fit_uname_kernel = NULL; + int os_noffset; +#endif + + /* find out kernel image address */ + if (argc < 1) { + img_addr = load_addr; + debug("* kernel: default image load address = 0x%08lx\n", + load_addr); +#if defined(CONFIG_FIT) + } else if (fit_parse_conf(argv[0], load_addr, &img_addr, + &fit_uname_config)) { + debug("* kernel: config '%s' from image at 0x%08lx\n", + fit_uname_config, img_addr); + } else if (fit_parse_subimage(argv[0], load_addr, &img_addr, + &fit_uname_kernel)) { + debug("* kernel: subimage '%s' from image at 0x%08lx\n", + fit_uname_kernel, img_addr); +#endif + } else { + img_addr = simple_strtoul(argv[0], NULL, 16); + debug("* kernel: cmdline image address = 0x%08lx\n", + img_addr); + } + + bootstage_mark(BOOTSTAGE_ID_CHECK_MAGIC); + + /* copy from dataflash if needed */ + img_addr = genimg_get_image(img_addr); + + /* check image type, for FIT images get FIT kernel node */ + *os_data = *os_len = 0; + buf = map_sysmem(img_addr, 0); + switch (genimg_get_format(buf)) { +#if defined(CONFIG_IMAGE_FORMAT_LEGACY) + case IMAGE_FORMAT_LEGACY: + printf("## Booting kernel from Legacy Image at %08lx ...\n", + img_addr); + hdr = image_get_kernel(img_addr, images->verify); + if (!hdr) + return NULL; + bootstage_mark(BOOTSTAGE_ID_CHECK_IMAGETYPE); + + /* get os_data and os_len */ + switch (image_get_type(hdr)) { + case IH_TYPE_KERNEL: + case IH_TYPE_KERNEL_NOLOAD: + *os_data = image_get_data(hdr); + *os_len = image_get_data_size(hdr); + break; + case IH_TYPE_MULTI: + image_multi_getimg(hdr, 0, os_data, os_len); + break; + case IH_TYPE_STANDALONE: + *os_data = image_get_data(hdr); + *os_len = image_get_data_size(hdr); + break; + default: + printf("Wrong Image Type for %s command\n", + cmdtp->name); + bootstage_error(BOOTSTAGE_ID_CHECK_IMAGETYPE); + return NULL; + } + + /* + * copy image header to allow for image overwrites during + * kernel decompression. + */ + memmove(&images->legacy_hdr_os_copy, hdr, + sizeof(image_header_t)); + + /* save pointer to image header */ + images->legacy_hdr_os = hdr; + + images->legacy_hdr_valid = 1; + bootstage_mark(BOOTSTAGE_ID_DECOMP_IMAGE); + break; +#endif +#if defined(CONFIG_FIT) + case IMAGE_FORMAT_FIT: + os_noffset = fit_image_load(images, FIT_KERNEL_PROP, + img_addr, + &fit_uname_kernel, &fit_uname_config, + IH_ARCH_DEFAULT, IH_TYPE_KERNEL, + BOOTSTAGE_ID_FIT_KERNEL_START, + FIT_LOAD_IGNORED, os_data, os_len); + if (os_noffset < 0) + return NULL; + + images->fit_hdr_os = map_sysmem(img_addr, 0); + images->fit_uname_os = fit_uname_kernel; + images->fit_uname_cfg = fit_uname_config; + images->fit_noffset_os = os_noffset; + break; +#endif +#ifdef CONFIG_ANDROID_BOOT_IMAGE + case IMAGE_FORMAT_ANDROID: + printf("## Booting Android Image at 0x%08lx ...\n", img_addr); + if (android_image_get_kernel((void *)img_addr, images->verify, + os_data, os_len)) + return NULL; + break; +#endif + default: + printf("Wrong Image Format for %s command\n", cmdtp->name); + bootstage_error(BOOTSTAGE_ID_FIT_KERNEL_INFO); + return NULL; + } + + debug(" kernel data at 0x%08lx, len = 0x%08lx (%ld)\n", + *os_data, *os_len, *os_len); + + return buf; +} diff --git a/common/bootm_os.c b/common/bootm_os.c new file mode 100644 index 00000000000..f7769ac853b --- /dev/null +++ b/common/bootm_os.c @@ -0,0 +1,480 @@ +/* + * (C) Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +static int do_bootm_standalone(int flag, int argc, char * const argv[], + bootm_headers_t *images) +{ + char *s; + int (*appl)(int, char *const[]); + + /* Don't start if "autostart" is set to "no" */ + s = getenv("autostart"); + if ((s != NULL) && !strcmp(s, "no")) { + setenv_hex("filesize", images->os.image_len); + return 0; + } + appl = (int (*)(int, char * const []))images->ep; + appl(argc, argv); + return 0; +} + +/*******************************************************************/ +/* OS booting routines */ +/*******************************************************************/ + +#if defined(CONFIG_BOOTM_NETBSD) || defined(CONFIG_BOOTM_PLAN9) +static void copy_args(char *dest, int argc, char * const argv[], char delim) +{ + int i; + + for (i = 0; i < argc; i++) { + if (i > 0) + *dest++ = delim; + strcpy(dest, argv[i]); + dest += strlen(argv[i]); + } +} +#endif + +#ifdef CONFIG_BOOTM_NETBSD +static int do_bootm_netbsd(int flag, int argc, char * const argv[], + bootm_headers_t *images) +{ + void (*loader)(bd_t *, image_header_t *, char *, char *); + image_header_t *os_hdr, *hdr; + ulong kernel_data, kernel_len; + char *consdev; + char *cmdline; + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("NetBSD"); + return 1; + } +#endif + hdr = images->legacy_hdr_os; + + /* + * Booting a (NetBSD) kernel image + * + * This process is pretty similar to a standalone application: + * The (first part of an multi-) image must be a stage-2 loader, + * which in turn is responsible for loading & invoking the actual + * kernel. The only differences are the parameters being passed: + * besides the board info strucure, the loader expects a command + * line, the name of the console device, and (optionally) the + * address of the original image header. + */ + os_hdr = NULL; + if (image_check_type(&images->legacy_hdr_os_copy, IH_TYPE_MULTI)) { + image_multi_getimg(hdr, 1, &kernel_data, &kernel_len); + if (kernel_len) + os_hdr = hdr; + } + + consdev = ""; +#if defined(CONFIG_8xx_CONS_SMC1) + consdev = "smc1"; +#elif defined(CONFIG_8xx_CONS_SMC2) + consdev = "smc2"; +#elif defined(CONFIG_8xx_CONS_SCC2) + consdev = "scc2"; +#elif defined(CONFIG_8xx_CONS_SCC3) + consdev = "scc3"; +#endif + + if (argc > 0) { + ulong len; + int i; + + for (i = 0, len = 0; i < argc; i += 1) + len += strlen(argv[i]) + 1; + cmdline = malloc(len); + copy_args(cmdline, argc, argv, ' '); + } else { + cmdline = getenv("bootargs"); + if (cmdline == NULL) + cmdline = ""; + } + + loader = (void (*)(bd_t *, image_header_t *, char *, char *))images->ep; + + printf("## Transferring control to NetBSD stage-2 loader (at address %08lx) ...\n", + (ulong)loader); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + /* + * NetBSD Stage-2 Loader Parameters: + * arg[0]: pointer to board info data + * arg[1]: image load address + * arg[2]: char pointer to the console device to use + * arg[3]: char pointer to the boot arguments + */ + (*loader)(gd->bd, os_hdr, consdev, cmdline); + + return 1; +} +#endif /* CONFIG_BOOTM_NETBSD*/ + +#ifdef CONFIG_LYNXKDI +static int do_bootm_lynxkdi(int flag, int argc, char * const argv[], + bootm_headers_t *images) +{ + image_header_t *hdr = &images->legacy_hdr_os_copy; + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("Lynx"); + return 1; + } +#endif + + lynxkdi_boot((image_header_t *)hdr); + + return 1; +} +#endif /* CONFIG_LYNXKDI */ + +#ifdef CONFIG_BOOTM_RTEMS +static int do_bootm_rtems(int flag, int argc, char * const argv[], + bootm_headers_t *images) +{ + void (*entry_point)(bd_t *); + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("RTEMS"); + return 1; + } +#endif + + entry_point = (void (*)(bd_t *))images->ep; + + printf("## Transferring control to RTEMS (at address %08lx) ...\n", + (ulong)entry_point); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + /* + * RTEMS Parameters: + * r3: ptr to board info data + */ + (*entry_point)(gd->bd); + + return 1; +} +#endif /* CONFIG_BOOTM_RTEMS */ + +#if defined(CONFIG_BOOTM_OSE) +static int do_bootm_ose(int flag, int argc, char * const argv[], + bootm_headers_t *images) +{ + void (*entry_point)(void); + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("OSE"); + return 1; + } +#endif + + entry_point = (void (*)(void))images->ep; + + printf("## Transferring control to OSE (at address %08lx) ...\n", + (ulong)entry_point); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + /* + * OSE Parameters: + * None + */ + (*entry_point)(); + + return 1; +} +#endif /* CONFIG_BOOTM_OSE */ + +#if defined(CONFIG_BOOTM_PLAN9) +static int do_bootm_plan9(int flag, int argc, char * const argv[], + bootm_headers_t *images) +{ + void (*entry_point)(void); + char *s; + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("Plan 9"); + return 1; + } +#endif + + /* See README.plan9 */ + s = getenv("confaddr"); + if (s != NULL) { + char *confaddr = (char *)simple_strtoul(s, NULL, 16); + + if (argc > 0) { + copy_args(confaddr, argc, argv, '\n'); + } else { + s = getenv("bootargs"); + if (s != NULL) + strcpy(confaddr, s); + } + } + + entry_point = (void (*)(void))images->ep; + + printf("## Transferring control to Plan 9 (at address %08lx) ...\n", + (ulong)entry_point); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + /* + * Plan 9 Parameters: + * None + */ + (*entry_point)(); + + return 1; +} +#endif /* CONFIG_BOOTM_PLAN9 */ + +#if defined(CONFIG_BOOTM_VXWORKS) && \ + (defined(CONFIG_PPC) || defined(CONFIG_ARM)) + +void do_bootvx_fdt(bootm_headers_t *images) +{ +#if defined(CONFIG_OF_LIBFDT) + int ret; + char *bootline; + ulong of_size = images->ft_len; + char **of_flat_tree = &images->ft_addr; + struct lmb *lmb = &images->lmb; + + if (*of_flat_tree) { + boot_fdt_add_mem_rsv_regions(lmb, *of_flat_tree); + + ret = boot_relocate_fdt(lmb, of_flat_tree, &of_size); + if (ret) + return; + + ret = fdt_add_subnode(*of_flat_tree, 0, "chosen"); + if ((ret >= 0 || ret == -FDT_ERR_EXISTS)) { + bootline = getenv("bootargs"); + if (bootline) { + ret = fdt_find_and_setprop(*of_flat_tree, + "/chosen", "bootargs", + bootline, + strlen(bootline) + 1, 1); + if (ret < 0) { + printf("## ERROR: %s : %s\n", __func__, + fdt_strerror(ret)); + return; + } + } + } else { + printf("## ERROR: %s : %s\n", __func__, + fdt_strerror(ret)); + return; + } + } +#endif + + boot_prep_vxworks(images); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + +#if defined(CONFIG_OF_LIBFDT) + printf("## Starting vxWorks at 0x%08lx, device tree at 0x%08lx ...\n", + (ulong)images->ep, (ulong)*of_flat_tree); +#else + printf("## Starting vxWorks at 0x%08lx\n", (ulong)images->ep); +#endif + + boot_jump_vxworks(images); + + puts("## vxWorks terminated\n"); +} + +static int do_bootm_vxworks(int flag, int argc, char * const argv[], + bootm_headers_t *images) +{ + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("VxWorks"); + return 1; + } +#endif + + do_bootvx_fdt(images); + + return 1; +} +#endif + +#if defined(CONFIG_CMD_ELF) +static int do_bootm_qnxelf(int flag, int argc, char * const argv[], + bootm_headers_t *images) +{ + char *local_args[2]; + char str[16]; + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("QNX"); + return 1; + } +#endif + + sprintf(str, "%lx", images->ep); /* write entry-point into string */ + local_args[0] = argv[0]; + local_args[1] = str; /* and provide it via the arguments */ + do_bootelf(NULL, 0, 2, local_args); + + return 1; +} +#endif + +#ifdef CONFIG_INTEGRITY +static int do_bootm_integrity(int flag, int argc, char * const argv[], + bootm_headers_t *images) +{ + void (*entry_point)(void); + + if (flag != BOOTM_STATE_OS_GO) + return 0; + +#if defined(CONFIG_FIT) + if (!images->legacy_hdr_valid) { + fit_unsupported_reset("INTEGRITY"); + return 1; + } +#endif + + entry_point = (void (*)(void))images->ep; + + printf("## Transferring control to INTEGRITY (at address %08lx) ...\n", + (ulong)entry_point); + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + /* + * INTEGRITY Parameters: + * None + */ + (*entry_point)(); + + return 1; +} +#endif + +static boot_os_fn *boot_os[] = { + [IH_OS_U_BOOT] = do_bootm_standalone, +#ifdef CONFIG_BOOTM_LINUX + [IH_OS_LINUX] = do_bootm_linux, +#endif +#ifdef CONFIG_BOOTM_NETBSD + [IH_OS_NETBSD] = do_bootm_netbsd, +#endif +#ifdef CONFIG_LYNXKDI + [IH_OS_LYNXOS] = do_bootm_lynxkdi, +#endif +#ifdef CONFIG_BOOTM_RTEMS + [IH_OS_RTEMS] = do_bootm_rtems, +#endif +#if defined(CONFIG_BOOTM_OSE) + [IH_OS_OSE] = do_bootm_ose, +#endif +#if defined(CONFIG_BOOTM_PLAN9) + [IH_OS_PLAN9] = do_bootm_plan9, +#endif +#if defined(CONFIG_BOOTM_VXWORKS) && \ + (defined(CONFIG_PPC) || defined(CONFIG_ARM)) + [IH_OS_VXWORKS] = do_bootm_vxworks, +#endif +#if defined(CONFIG_CMD_ELF) + [IH_OS_QNX] = do_bootm_qnxelf, +#endif +#ifdef CONFIG_INTEGRITY + [IH_OS_INTEGRITY] = do_bootm_integrity, +#endif +}; + +/* Allow for arch specific config before we boot */ +static void __arch_preboot_os(void) +{ + /* please define platform specific arch_preboot_os() */ +} +void arch_preboot_os(void) __attribute__((weak, alias("__arch_preboot_os"))); + +int boot_selected_os(int argc, char * const argv[], int state, + bootm_headers_t *images, boot_os_fn *boot_fn) +{ + arch_preboot_os(); + boot_fn(state, argc, argv, images); + + /* Stand-alone may return when 'autostart' is 'no' */ + if (images->os.type == IH_TYPE_STANDALONE || + state == BOOTM_STATE_OS_FAKE_GO) /* We expect to return */ + return 0; + bootstage_error(BOOTSTAGE_ID_BOOT_OS_RETURNED); +#ifdef DEBUG + puts("\n## Control returned to monitor - resetting...\n"); +#endif + return BOOTM_ERR_RESET; +} + +boot_os_fn *bootm_os_get_boot_func(int os) +{ +#ifdef CONFIG_NEEDS_MANUAL_RELOC + static bool relocated; + + if (!relocated) { + int i; + + /* relocate boot function table */ + for (i = 0; i < ARRAY_SIZE(boot_os); i++) + if (boot_os[i] != NULL) + boot_os[i] += gd->reloc_off; + + relocated = true; + } +#endif + return boot_os[os]; +} diff --git a/common/cmd_bootm.c b/common/cmd_bootm.c index c06f4b730f3..8b897c8d664 100644 --- a/common/cmd_bootm.c +++ b/common/cmd_bootm.c @@ -5,58 +5,25 @@ * SPDX-License-Identifier: GPL-2.0+ */ - /* * Boot support */ #include -#include +#include #include -#include -#include -#include -#include #include +#include #include -#include +#include +#include #include -#include #include - -#if defined(CONFIG_BOOTM_VXWORKS) && \ - (defined(CONFIG_PPC) || defined(CONFIG_ARM)) -#include -#endif - -#if defined(CONFIG_CMD_USB) -#include -#endif - -#if defined(CONFIG_OF_LIBFDT) -#include -#include -#endif - -#ifdef CONFIG_LZMA -#include -#include -#include -#endif /* CONFIG_LZMA */ - -#ifdef CONFIG_LZO -#include -#endif /* CONFIG_LZO */ +#include +#include +#include DECLARE_GLOBAL_DATA_PTR; -#ifndef CONFIG_SYS_BOOTM_LEN -#define CONFIG_SYS_BOOTM_LEN 0x800000 /* use 8MByte as default max gunzip size */ -#endif - -#ifdef CONFIG_BZIP2 -extern void bz_internal_error(int); -#endif - #if defined(CONFIG_CMD_IMI) static int image_info(unsigned long addr); #endif @@ -71,465 +38,8 @@ extern flash_info_t flash_info[]; /* info for FLASH chips */ static int do_imls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); #endif -#include -#include - -#if defined(CONFIG_SILENT_CONSOLE) && !defined(CONFIG_SILENT_U_BOOT_ONLY) -static void fixup_silent_linux(void); -#endif - -static int do_bootm_standalone(int flag, int argc, char * const argv[], - bootm_headers_t *images); - -static const void *boot_get_kernel(cmd_tbl_t *cmdtp, int flag, int argc, - char * const argv[], bootm_headers_t *images, - ulong *os_data, ulong *os_len); - -/* - * Continue booting an OS image; caller already has: - * - copied image header to global variable `header' - * - checked header magic number, checksums (both header & image), - * - verified image architecture (PPC) and type (KERNEL or MULTI), - * - loaded (first part of) image to header load address, - * - disabled interrupts. - * - * @flag: Flags indicating what to do (BOOTM_STATE_...) - * @argc: Number of arguments. Note that the arguments are shifted down - * so that 0 is the first argument not processed by U-Boot, and - * argc is adjusted accordingly. This avoids confusion as to how - * many arguments are available for the OS. - * @images: Pointers to os/initrd/fdt - * @return 1 on error. On success the OS boots so this function does - * not return. - */ -typedef int boot_os_fn(int flag, int argc, char * const argv[], - bootm_headers_t *images); - -#ifdef CONFIG_BOOTM_LINUX -extern boot_os_fn do_bootm_linux; -#endif -#ifdef CONFIG_BOOTM_NETBSD -static boot_os_fn do_bootm_netbsd; -#endif -#if defined(CONFIG_LYNXKDI) -static boot_os_fn do_bootm_lynxkdi; -extern void lynxkdi_boot(image_header_t *); -#endif -#ifdef CONFIG_BOOTM_RTEMS -static boot_os_fn do_bootm_rtems; -#endif -#if defined(CONFIG_BOOTM_OSE) -static boot_os_fn do_bootm_ose; -#endif -#if defined(CONFIG_BOOTM_PLAN9) -static boot_os_fn do_bootm_plan9; -#endif -#if defined(CONFIG_BOOTM_VXWORKS) && \ - (defined(CONFIG_PPC) || defined(CONFIG_ARM)) -static boot_os_fn do_bootm_vxworks; -#endif -#if defined(CONFIG_CMD_ELF) -static boot_os_fn do_bootm_qnxelf; -int do_bootvx(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); -int do_bootelf(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); -#endif -#if defined(CONFIG_INTEGRITY) -static boot_os_fn do_bootm_integrity; -#endif - -static boot_os_fn *boot_os[] = { - [IH_OS_U_BOOT] = do_bootm_standalone, -#ifdef CONFIG_BOOTM_LINUX - [IH_OS_LINUX] = do_bootm_linux, -#endif -#ifdef CONFIG_BOOTM_NETBSD - [IH_OS_NETBSD] = do_bootm_netbsd, -#endif -#ifdef CONFIG_LYNXKDI - [IH_OS_LYNXOS] = do_bootm_lynxkdi, -#endif -#ifdef CONFIG_BOOTM_RTEMS - [IH_OS_RTEMS] = do_bootm_rtems, -#endif -#if defined(CONFIG_BOOTM_OSE) - [IH_OS_OSE] = do_bootm_ose, -#endif -#if defined(CONFIG_BOOTM_PLAN9) - [IH_OS_PLAN9] = do_bootm_plan9, -#endif -#if defined(CONFIG_BOOTM_VXWORKS) && \ - (defined(CONFIG_PPC) || defined(CONFIG_ARM)) - [IH_OS_VXWORKS] = do_bootm_vxworks, -#endif -#if defined(CONFIG_CMD_ELF) - [IH_OS_QNX] = do_bootm_qnxelf, -#endif -#ifdef CONFIG_INTEGRITY - [IH_OS_INTEGRITY] = do_bootm_integrity, -#endif -}; - bootm_headers_t images; /* pointers to os/initrd/fdt images */ -/* Allow for arch specific config before we boot */ -static void __arch_preboot_os(void) -{ - /* please define platform specific arch_preboot_os() */ -} -void arch_preboot_os(void) __attribute__((weak, alias("__arch_preboot_os"))); - -#define IH_INITRD_ARCH IH_ARCH_DEFAULT - -#ifdef CONFIG_LMB -static void boot_start_lmb(bootm_headers_t *images) -{ - ulong mem_start; - phys_size_t mem_size; - - lmb_init(&images->lmb); - - mem_start = getenv_bootm_low(); - mem_size = getenv_bootm_size(); - - lmb_add(&images->lmb, (phys_addr_t)mem_start, mem_size); - - arch_lmb_reserve(&images->lmb); - board_lmb_reserve(&images->lmb); -} -#else -#define lmb_reserve(lmb, base, size) -static inline void boot_start_lmb(bootm_headers_t *images) { } -#endif - -static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) -{ - memset((void *)&images, 0, sizeof(images)); - images.verify = getenv_yesno("verify"); - - boot_start_lmb(&images); - - bootstage_mark_name(BOOTSTAGE_ID_BOOTM_START, "bootm_start"); - images.state = BOOTM_STATE_START; - - return 0; -} - -static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc, - char * const argv[]) -{ - const void *os_hdr; - bool ep_found = false; - - /* get kernel image header, start address and length */ - os_hdr = boot_get_kernel(cmdtp, flag, argc, argv, - &images, &images.os.image_start, &images.os.image_len); - if (images.os.image_len == 0) { - puts("ERROR: can't get kernel image!\n"); - return 1; - } - - /* get image parameters */ - switch (genimg_get_format(os_hdr)) { -#if defined(CONFIG_IMAGE_FORMAT_LEGACY) - case IMAGE_FORMAT_LEGACY: - images.os.type = image_get_type(os_hdr); - images.os.comp = image_get_comp(os_hdr); - images.os.os = image_get_os(os_hdr); - - images.os.end = image_get_image_end(os_hdr); - images.os.load = image_get_load(os_hdr); - break; -#endif -#if defined(CONFIG_FIT) - case IMAGE_FORMAT_FIT: - if (fit_image_get_type(images.fit_hdr_os, - images.fit_noffset_os, &images.os.type)) { - puts("Can't get image type!\n"); - bootstage_error(BOOTSTAGE_ID_FIT_TYPE); - return 1; - } - - if (fit_image_get_comp(images.fit_hdr_os, - images.fit_noffset_os, &images.os.comp)) { - puts("Can't get image compression!\n"); - bootstage_error(BOOTSTAGE_ID_FIT_COMPRESSION); - return 1; - } - - if (fit_image_get_os(images.fit_hdr_os, - images.fit_noffset_os, &images.os.os)) { - puts("Can't get image OS!\n"); - bootstage_error(BOOTSTAGE_ID_FIT_OS); - return 1; - } - - images.os.end = fit_get_end(images.fit_hdr_os); - - if (fit_image_get_load(images.fit_hdr_os, images.fit_noffset_os, - &images.os.load)) { - puts("Can't get image load address!\n"); - bootstage_error(BOOTSTAGE_ID_FIT_LOADADDR); - return 1; - } - break; -#endif -#ifdef CONFIG_ANDROID_BOOT_IMAGE - case IMAGE_FORMAT_ANDROID: - images.os.type = IH_TYPE_KERNEL; - images.os.comp = IH_COMP_NONE; - images.os.os = IH_OS_LINUX; - images.ep = images.os.load; - ep_found = true; - - images.os.end = android_image_get_end(os_hdr); - images.os.load = android_image_get_kload(os_hdr); - break; -#endif - default: - puts("ERROR: unknown image format type!\n"); - return 1; - } - - /* find kernel entry point */ - if (images.legacy_hdr_valid) { - images.ep = image_get_ep(&images.legacy_hdr_os_copy); -#if defined(CONFIG_FIT) - } else if (images.fit_uname_os) { - int ret; - - ret = fit_image_get_entry(images.fit_hdr_os, - images.fit_noffset_os, &images.ep); - if (ret) { - puts("Can't get entry point property!\n"); - return 1; - } -#endif - } else if (!ep_found) { - puts("Could not find kernel entry point!\n"); - return 1; - } - - if (images.os.type == IH_TYPE_KERNEL_NOLOAD) { - images.os.load = images.os.image_start; - images.ep += images.os.load; - } - - images.os.start = (ulong)os_hdr; - - return 0; -} - -static int bootm_find_ramdisk(int flag, int argc, char * const argv[]) -{ - int ret; - - /* find ramdisk */ - ret = boot_get_ramdisk(argc, argv, &images, IH_INITRD_ARCH, - &images.rd_start, &images.rd_end); - if (ret) { - puts("Ramdisk image is corrupt or invalid\n"); - return 1; - } - - return 0; -} - -#if defined(CONFIG_OF_LIBFDT) -static int bootm_find_fdt(int flag, int argc, char * const argv[]) -{ - int ret; - - /* find flattened device tree */ - ret = boot_get_fdt(flag, argc, argv, IH_ARCH_DEFAULT, &images, - &images.ft_addr, &images.ft_len); - if (ret) { - puts("Could not find a valid device tree\n"); - return 1; - } - - set_working_fdt_addr(images.ft_addr); - - return 0; -} -#endif - -static int bootm_find_other(cmd_tbl_t *cmdtp, int flag, int argc, - char * const argv[]) -{ - if (((images.os.type == IH_TYPE_KERNEL) || - (images.os.type == IH_TYPE_KERNEL_NOLOAD) || - (images.os.type == IH_TYPE_MULTI)) && - (images.os.os == IH_OS_LINUX || - images.os.os == IH_OS_VXWORKS)) { - if (bootm_find_ramdisk(flag, argc, argv)) - return 1; - -#if defined(CONFIG_OF_LIBFDT) - if (bootm_find_fdt(flag, argc, argv)) - return 1; -#endif - } - - return 0; -} - -#define BOOTM_ERR_RESET -1 -#define BOOTM_ERR_OVERLAP -2 -#define BOOTM_ERR_UNIMPLEMENTED -3 -static int bootm_load_os(bootm_headers_t *images, unsigned long *load_end, - int boot_progress) -{ - image_info_t os = images->os; - uint8_t comp = os.comp; - ulong load = os.load; - ulong blob_start = os.start; - ulong blob_end = os.end; - ulong image_start = os.image_start; - ulong image_len = os.image_len; - __maybe_unused uint unc_len = CONFIG_SYS_BOOTM_LEN; - int no_overlap = 0; - void *load_buf, *image_buf; -#if defined(CONFIG_LZMA) || defined(CONFIG_LZO) - int ret; -#endif /* defined(CONFIG_LZMA) || defined(CONFIG_LZO) */ - - const char *type_name = genimg_get_type_name(os.type); - - load_buf = map_sysmem(load, unc_len); - image_buf = map_sysmem(image_start, image_len); - switch (comp) { - case IH_COMP_NONE: - if (load == image_start) { - printf(" XIP %s ... ", type_name); - no_overlap = 1; - } else { - printf(" Loading %s ... ", type_name); - memmove_wd(load_buf, image_buf, image_len, CHUNKSZ); - } - *load_end = load + image_len; - break; -#ifdef CONFIG_GZIP - case IH_COMP_GZIP: - printf(" Uncompressing %s ... ", type_name); - if (gunzip(load_buf, unc_len, image_buf, &image_len) != 0) { - puts("GUNZIP: uncompress, out-of-mem or overwrite " - "error - must RESET board to recover\n"); - if (boot_progress) - bootstage_error(BOOTSTAGE_ID_DECOMP_IMAGE); - return BOOTM_ERR_RESET; - } - - *load_end = load + image_len; - break; -#endif /* CONFIG_GZIP */ -#ifdef CONFIG_BZIP2 - case IH_COMP_BZIP2: - printf(" Uncompressing %s ... ", type_name); - /* - * If we've got less than 4 MB of malloc() space, - * use slower decompression algorithm which requires - * at most 2300 KB of memory. - */ - int i = BZ2_bzBuffToBuffDecompress(load_buf, &unc_len, - image_buf, image_len, - CONFIG_SYS_MALLOC_LEN < (4096 * 1024), 0); - if (i != BZ_OK) { - printf("BUNZIP2: uncompress or overwrite error %d " - "- must RESET board to recover\n", i); - if (boot_progress) - bootstage_error(BOOTSTAGE_ID_DECOMP_IMAGE); - return BOOTM_ERR_RESET; - } - - *load_end = load + unc_len; - break; -#endif /* CONFIG_BZIP2 */ -#ifdef CONFIG_LZMA - case IH_COMP_LZMA: { - SizeT lzma_len = unc_len; - printf(" Uncompressing %s ... ", type_name); - - ret = lzmaBuffToBuffDecompress(load_buf, &lzma_len, - image_buf, image_len); - unc_len = lzma_len; - if (ret != SZ_OK) { - printf("LZMA: uncompress or overwrite error %d " - "- must RESET board to recover\n", ret); - bootstage_error(BOOTSTAGE_ID_DECOMP_IMAGE); - return BOOTM_ERR_RESET; - } - *load_end = load + unc_len; - break; - } -#endif /* CONFIG_LZMA */ -#ifdef CONFIG_LZO - case IH_COMP_LZO: { - size_t size = unc_len; - - printf(" Uncompressing %s ... ", type_name); - - ret = lzop_decompress(image_buf, image_len, load_buf, &size); - if (ret != LZO_E_OK) { - printf("LZO: uncompress or overwrite error %d " - "- must RESET board to recover\n", ret); - if (boot_progress) - bootstage_error(BOOTSTAGE_ID_DECOMP_IMAGE); - return BOOTM_ERR_RESET; - } - - *load_end = load + size; - break; - } -#endif /* CONFIG_LZO */ - default: - printf("Unimplemented compression type %d\n", comp); - return BOOTM_ERR_UNIMPLEMENTED; - } - - flush_cache(load, (*load_end - load) * sizeof(ulong)); - - puts("OK\n"); - debug(" kernel loaded at 0x%08lx, end = 0x%08lx\n", load, *load_end); - bootstage_mark(BOOTSTAGE_ID_KERNEL_LOADED); - - if (!no_overlap && (load < blob_end) && (*load_end > blob_start)) { - debug("images.os.start = 0x%lX, images.os.end = 0x%lx\n", - blob_start, blob_end); - debug("images.os.load = 0x%lx, load_end = 0x%lx\n", load, - *load_end); - - /* Check what type of image this is. */ - if (images->legacy_hdr_valid) { - if (image_get_type(&images->legacy_hdr_os_copy) - == IH_TYPE_MULTI) - puts("WARNING: legacy format multi component image overwritten\n"); - return BOOTM_ERR_OVERLAP; - } else { - puts("ERROR: new format image overwritten - must RESET the board to recover\n"); - bootstage_error(BOOTSTAGE_ID_OVERWRITTEN); - return BOOTM_ERR_RESET; - } - } - - return 0; -} - -static int do_bootm_standalone(int flag, int argc, char * const argv[], - bootm_headers_t *images) -{ - char *s; - int (*appl)(int, char * const []); - - /* Don't start if "autostart" is set to "no" */ - if (((s = getenv("autostart")) != NULL) && (strcmp(s, "no") == 0)) { - setenv_hex("filesize", images->os.image_len); - return 0; - } - appl = (int (*)(int, char * const []))images->ep; - appl(argc, argv); - return 0; -} - /* we overload the cmd field with our state machine info instead of a * function pointer */ static cmd_tbl_t cmd_bootm_sub[] = { @@ -548,210 +58,6 @@ static cmd_tbl_t cmd_bootm_sub[] = { U_BOOT_CMD_MKENT(go, 0, 1, (void *)BOOTM_STATE_OS_GO, "", ""), }; -static int boot_selected_os(int argc, char * const argv[], int state, - bootm_headers_t *images, boot_os_fn *boot_fn) -{ - arch_preboot_os(); - boot_fn(state, argc, argv, images); - - /* Stand-alone may return when 'autostart' is 'no' */ - if (images->os.type == IH_TYPE_STANDALONE || - state == BOOTM_STATE_OS_FAKE_GO) /* We expect to return */ - return 0; - bootstage_error(BOOTSTAGE_ID_BOOT_OS_RETURNED); -#ifdef DEBUG - puts("\n## Control returned to monitor - resetting...\n"); -#endif - return BOOTM_ERR_RESET; -} - -/** - * bootm_disable_interrupts() - Disable interrupts in preparation for load/boot - * - * @return interrupt flag (0 if interrupts were disabled, non-zero if they were - * enabled) - */ -static ulong bootm_disable_interrupts(void) -{ - ulong iflag; - - /* - * We have reached the point of no return: we are going to - * overwrite all exception vector code, so we cannot easily - * recover from any failures any more... - */ - iflag = disable_interrupts(); -#ifdef CONFIG_NETCONSOLE - /* Stop the ethernet stack if NetConsole could have left it up */ - eth_halt(); - eth_unregister(eth_get_dev()); -#endif - -#if defined(CONFIG_CMD_USB) - /* - * turn off USB to prevent the host controller from writing to the - * SDRAM while Linux is booting. This could happen (at least for OHCI - * controller), because the HCCA (Host Controller Communication Area) - * lies within the SDRAM and the host controller writes continously to - * this area (as busmaster!). The HccaFrameNumber is for example - * updated every 1 ms within the HCCA structure in SDRAM! For more - * details see the OpenHCI specification. - */ - usb_stop(); -#endif - return iflag; -} - -/** - * Execute selected states of the bootm command. - * - * Note the arguments to this state must be the first argument, Any 'bootm' - * or sub-command arguments must have already been taken. - * - * Note that if states contains more than one flag it MUST contain - * BOOTM_STATE_START, since this handles and consumes the command line args. - * - * Also note that aside from boot_os_fn functions and bootm_load_os no other - * functions we store the return value of in 'ret' may use a negative return - * value, without special handling. - * - * @param cmdtp Pointer to bootm command table entry - * @param flag Command flags (CMD_FLAG_...) - * @param argc Number of subcommand arguments (0 = no arguments) - * @param argv Arguments - * @param states Mask containing states to run (BOOTM_STATE_...) - * @param images Image header information - * @param boot_progress 1 to show boot progress, 0 to not do this - * @return 0 if ok, something else on error. Some errors will cause this - * function to perform a reboot! If states contains BOOTM_STATE_OS_GO - * then the intent is to boot an OS, so this function will not return - * unless the image type is standalone. - */ -static int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, - char * const argv[], int states, bootm_headers_t *images, - int boot_progress) -{ - boot_os_fn *boot_fn; - ulong iflag = 0; - int ret = 0, need_boot_fn; - - images->state |= states; - - /* - * Work through the states and see how far we get. We stop on - * any error. - */ - if (states & BOOTM_STATE_START) - ret = bootm_start(cmdtp, flag, argc, argv); - - if (!ret && (states & BOOTM_STATE_FINDOS)) - ret = bootm_find_os(cmdtp, flag, argc, argv); - - if (!ret && (states & BOOTM_STATE_FINDOTHER)) { - ret = bootm_find_other(cmdtp, flag, argc, argv); - argc = 0; /* consume the args */ - } - - /* Load the OS */ - if (!ret && (states & BOOTM_STATE_LOADOS)) { - ulong load_end; - - iflag = bootm_disable_interrupts(); - ret = bootm_load_os(images, &load_end, 0); - if (ret == 0) - lmb_reserve(&images->lmb, images->os.load, - (load_end - images->os.load)); - else if (ret && ret != BOOTM_ERR_OVERLAP) - goto err; - else if (ret == BOOTM_ERR_OVERLAP) - ret = 0; -#if defined(CONFIG_SILENT_CONSOLE) && !defined(CONFIG_SILENT_U_BOOT_ONLY) - if (images->os.os == IH_OS_LINUX) - fixup_silent_linux(); -#endif - } - - /* Relocate the ramdisk */ -#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH - if (!ret && (states & BOOTM_STATE_RAMDISK)) { - ulong rd_len = images->rd_end - images->rd_start; - - ret = boot_ramdisk_high(&images->lmb, images->rd_start, - rd_len, &images->initrd_start, &images->initrd_end); - if (!ret) { - setenv_hex("initrd_start", images->initrd_start); - setenv_hex("initrd_end", images->initrd_end); - } - } -#endif -#if defined(CONFIG_OF_LIBFDT) && defined(CONFIG_LMB) - if (!ret && (states & BOOTM_STATE_FDT)) { - boot_fdt_add_mem_rsv_regions(&images->lmb, images->ft_addr); - ret = boot_relocate_fdt(&images->lmb, &images->ft_addr, - &images->ft_len); - } -#endif - - /* From now on, we need the OS boot function */ - if (ret) - return ret; - boot_fn = boot_os[images->os.os]; - need_boot_fn = states & (BOOTM_STATE_OS_CMDLINE | - BOOTM_STATE_OS_BD_T | BOOTM_STATE_OS_PREP | - BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO); - if (boot_fn == NULL && need_boot_fn) { - if (iflag) - enable_interrupts(); - printf("ERROR: booting os '%s' (%d) is not supported\n", - genimg_get_os_name(images->os.os), images->os.os); - bootstage_error(BOOTSTAGE_ID_CHECK_BOOT_OS); - return 1; - } - - /* Call various other states that are not generally used */ - if (!ret && (states & BOOTM_STATE_OS_CMDLINE)) - ret = boot_fn(BOOTM_STATE_OS_CMDLINE, argc, argv, images); - if (!ret && (states & BOOTM_STATE_OS_BD_T)) - ret = boot_fn(BOOTM_STATE_OS_BD_T, argc, argv, images); - if (!ret && (states & BOOTM_STATE_OS_PREP)) - ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images); - -#ifdef CONFIG_TRACE - /* Pretend to run the OS, then run a user command */ - if (!ret && (states & BOOTM_STATE_OS_FAKE_GO)) { - char *cmd_list = getenv("fakegocmd"); - - ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_FAKE_GO, - images, boot_fn); - if (!ret && cmd_list) - ret = run_command_list(cmd_list, -1, flag); - } -#endif - - /* Check for unsupported subcommand. */ - if (ret) { - puts("subcommand not supported\n"); - return ret; - } - - /* Now run the OS! We hope this doesn't return */ - if (!ret && (states & BOOTM_STATE_OS_GO)) - ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO, - images, boot_fn); - - /* Deal with any fallout */ -err: - if (iflag) - enable_interrupts(); - - if (ret == BOOTM_ERR_UNIMPLEMENTED) - bootstage_error(BOOTSTAGE_ID_DECOMP_UNIMPL); - else if (ret == BOOTM_ERR_RESET) - do_reset(cmdtp, flag, argc, argv); - - return ret; -} - static int do_bootm_subcommand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { @@ -793,11 +99,6 @@ int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if (!relocated) { int i; - /* relocate boot function table */ - for (i = 0; i < ARRAY_SIZE(boot_os); i++) - if (boot_os[i] != NULL) - boot_os[i] += gd->reloc_off; - /* relocate names of sub-command table */ for (i = 0; i < ARRAY_SIZE(cmd_bootm_sub); i++) cmd_bootm_sub[i].name += gd->reloc_off; @@ -849,196 +150,6 @@ int bootm_maybe_autostart(cmd_tbl_t *cmdtp, const char *cmd) return 0; } -#if defined(CONFIG_IMAGE_FORMAT_LEGACY) -/** - * image_get_kernel - verify legacy format kernel image - * @img_addr: in RAM address of the legacy format image to be verified - * @verify: data CRC verification flag - * - * image_get_kernel() verifies legacy image integrity and returns pointer to - * legacy image header if image verification was completed successfully. - * - * returns: - * pointer to a legacy image header if valid image was found - * otherwise return NULL - */ -static image_header_t *image_get_kernel(ulong img_addr, int verify) -{ - image_header_t *hdr = (image_header_t *)img_addr; - - if (!image_check_magic(hdr)) { - puts("Bad Magic Number\n"); - bootstage_error(BOOTSTAGE_ID_CHECK_MAGIC); - return NULL; - } - bootstage_mark(BOOTSTAGE_ID_CHECK_HEADER); - - if (!image_check_hcrc(hdr)) { - puts("Bad Header Checksum\n"); - bootstage_error(BOOTSTAGE_ID_CHECK_HEADER); - return NULL; - } - - bootstage_mark(BOOTSTAGE_ID_CHECK_CHECKSUM); - image_print_contents(hdr); - - if (verify) { - puts(" Verifying Checksum ... "); - if (!image_check_dcrc(hdr)) { - printf("Bad Data CRC\n"); - bootstage_error(BOOTSTAGE_ID_CHECK_CHECKSUM); - return NULL; - } - puts("OK\n"); - } - bootstage_mark(BOOTSTAGE_ID_CHECK_ARCH); - - if (!image_check_target_arch(hdr)) { - printf("Unsupported Architecture 0x%x\n", image_get_arch(hdr)); - bootstage_error(BOOTSTAGE_ID_CHECK_ARCH); - return NULL; - } - return hdr; -} -#endif - -/** - * boot_get_kernel - find kernel image - * @os_data: pointer to a ulong variable, will hold os data start address - * @os_len: pointer to a ulong variable, will hold os data length - * - * boot_get_kernel() tries to find a kernel image, verifies its integrity - * and locates kernel data. - * - * returns: - * pointer to image header if valid image was found, plus kernel start - * address and length, otherwise NULL - */ -static const void *boot_get_kernel(cmd_tbl_t *cmdtp, int flag, int argc, - char * const argv[], bootm_headers_t *images, ulong *os_data, - ulong *os_len) -{ -#if defined(CONFIG_IMAGE_FORMAT_LEGACY) - image_header_t *hdr; -#endif - ulong img_addr; - const void *buf; -#if defined(CONFIG_FIT) - const char *fit_uname_config = NULL; - const char *fit_uname_kernel = NULL; - int os_noffset; -#endif - - /* find out kernel image address */ - if (argc < 1) { - img_addr = load_addr; - debug("* kernel: default image load address = 0x%08lx\n", - load_addr); -#if defined(CONFIG_FIT) - } else if (fit_parse_conf(argv[0], load_addr, &img_addr, - &fit_uname_config)) { - debug("* kernel: config '%s' from image at 0x%08lx\n", - fit_uname_config, img_addr); - } else if (fit_parse_subimage(argv[0], load_addr, &img_addr, - &fit_uname_kernel)) { - debug("* kernel: subimage '%s' from image at 0x%08lx\n", - fit_uname_kernel, img_addr); -#endif - } else { - img_addr = simple_strtoul(argv[0], NULL, 16); - debug("* kernel: cmdline image address = 0x%08lx\n", img_addr); - } - - bootstage_mark(BOOTSTAGE_ID_CHECK_MAGIC); - - /* copy from dataflash if needed */ - img_addr = genimg_get_image(img_addr); - - /* check image type, for FIT images get FIT kernel node */ - *os_data = *os_len = 0; - buf = map_sysmem(img_addr, 0); - switch (genimg_get_format(buf)) { -#if defined(CONFIG_IMAGE_FORMAT_LEGACY) - case IMAGE_FORMAT_LEGACY: - printf("## Booting kernel from Legacy Image at %08lx ...\n", - img_addr); - hdr = image_get_kernel(img_addr, images->verify); - if (!hdr) - return NULL; - bootstage_mark(BOOTSTAGE_ID_CHECK_IMAGETYPE); - - /* get os_data and os_len */ - switch (image_get_type(hdr)) { - case IH_TYPE_KERNEL: - case IH_TYPE_KERNEL_NOLOAD: - *os_data = image_get_data(hdr); - *os_len = image_get_data_size(hdr); - break; - case IH_TYPE_MULTI: - image_multi_getimg(hdr, 0, os_data, os_len); - break; - case IH_TYPE_STANDALONE: - *os_data = image_get_data(hdr); - *os_len = image_get_data_size(hdr); - break; - default: - printf("Wrong Image Type for %s command\n", - cmdtp->name); - bootstage_error(BOOTSTAGE_ID_CHECK_IMAGETYPE); - return NULL; - } - - /* - * copy image header to allow for image overwrites during - * kernel decompression. - */ - memmove(&images->legacy_hdr_os_copy, hdr, - sizeof(image_header_t)); - - /* save pointer to image header */ - images->legacy_hdr_os = hdr; - - images->legacy_hdr_valid = 1; - bootstage_mark(BOOTSTAGE_ID_DECOMP_IMAGE); - break; -#endif -#if defined(CONFIG_FIT) - case IMAGE_FORMAT_FIT: - os_noffset = fit_image_load(images, FIT_KERNEL_PROP, - img_addr, - &fit_uname_kernel, &fit_uname_config, - IH_ARCH_DEFAULT, IH_TYPE_KERNEL, - BOOTSTAGE_ID_FIT_KERNEL_START, - FIT_LOAD_IGNORED, os_data, os_len); - if (os_noffset < 0) - return NULL; - - images->fit_hdr_os = map_sysmem(img_addr, 0); - images->fit_uname_os = fit_uname_kernel; - images->fit_uname_cfg = fit_uname_config; - images->fit_noffset_os = os_noffset; - break; -#endif -#ifdef CONFIG_ANDROID_BOOT_IMAGE - case IMAGE_FORMAT_ANDROID: - printf("## Booting Android Image at 0x%08lx ...\n", img_addr); - if (android_image_get_kernel((void *)img_addr, images->verify, - os_data, os_len)) - return NULL; - break; -#endif - default: - printf("Wrong Image Format for %s command\n", cmdtp->name); - bootstage_error(BOOTSTAGE_ID_FIT_KERNEL_INFO); - return NULL; - } - - debug(" kernel data at 0x%08lx, len = 0x%08lx (%ld)\n", - *os_data, *os_len, *os_len); - - return buf; -} - #ifdef CONFIG_SYS_LONGHELP static char bootm_help_text[] = "[addr [arg ...]]\n - boot application image stored in memory\n" @@ -1420,441 +531,6 @@ U_BOOT_CMD( ); #endif -/*******************************************************************/ -/* helper routines */ -/*******************************************************************/ -#if defined(CONFIG_SILENT_CONSOLE) && !defined(CONFIG_SILENT_U_BOOT_ONLY) - -#define CONSOLE_ARG "console=" -#define CONSOLE_ARG_LEN (sizeof(CONSOLE_ARG) - 1) - -static void fixup_silent_linux(void) -{ - char *buf; - const char *env_val; - char *cmdline = getenv("bootargs"); - int want_silent; - - /* - * Only fix cmdline when requested. The environment variable can be: - * - * no - we never fixup - * yes - we always fixup - * unset - we rely on the console silent flag - */ - want_silent = getenv_yesno("silent_linux"); - if (want_silent == 0) - return; - else if (want_silent == -1 && !(gd->flags & GD_FLG_SILENT)) - return; - - debug("before silent fix-up: %s\n", cmdline); - if (cmdline && (cmdline[0] != '\0')) { - char *start = strstr(cmdline, CONSOLE_ARG); - - /* Allocate space for maximum possible new command line */ - buf = malloc(strlen(cmdline) + 1 + CONSOLE_ARG_LEN + 1); - if (!buf) { - debug("%s: out of memory\n", __func__); - return; - } - - if (start) { - char *end = strchr(start, ' '); - int num_start_bytes = start - cmdline + CONSOLE_ARG_LEN; - - strncpy(buf, cmdline, num_start_bytes); - if (end) - strcpy(buf + num_start_bytes, end); - else - buf[num_start_bytes] = '\0'; - } else { - sprintf(buf, "%s %s", cmdline, CONSOLE_ARG); - } - env_val = buf; - } else { - buf = NULL; - env_val = CONSOLE_ARG; - } - - setenv("bootargs", env_val); - debug("after silent fix-up: %s\n", env_val); - free(buf); -} -#endif /* CONFIG_SILENT_CONSOLE */ - -#if defined(CONFIG_BOOTM_NETBSD) || defined(CONFIG_BOOTM_PLAN9) -static void copy_args(char *dest, int argc, char * const argv[], char delim) -{ - int i; - - for (i = 0; i < argc; i++) { - if (i > 0) - *dest++ = delim; - strcpy(dest, argv[i]); - dest += strlen(argv[i]); - } -} -#endif - -/*******************************************************************/ -/* OS booting routines */ -/*******************************************************************/ - -#ifdef CONFIG_BOOTM_NETBSD -static int do_bootm_netbsd(int flag, int argc, char * const argv[], - bootm_headers_t *images) -{ - void (*loader)(bd_t *, image_header_t *, char *, char *); - image_header_t *os_hdr, *hdr; - ulong kernel_data, kernel_len; - char *consdev; - char *cmdline; - - if (flag != BOOTM_STATE_OS_GO) - return 0; - -#if defined(CONFIG_FIT) - if (!images->legacy_hdr_valid) { - fit_unsupported_reset("NetBSD"); - return 1; - } -#endif - hdr = images->legacy_hdr_os; - - /* - * Booting a (NetBSD) kernel image - * - * This process is pretty similar to a standalone application: - * The (first part of an multi-) image must be a stage-2 loader, - * which in turn is responsible for loading & invoking the actual - * kernel. The only differences are the parameters being passed: - * besides the board info strucure, the loader expects a command - * line, the name of the console device, and (optionally) the - * address of the original image header. - */ - os_hdr = NULL; - if (image_check_type(&images->legacy_hdr_os_copy, IH_TYPE_MULTI)) { - image_multi_getimg(hdr, 1, &kernel_data, &kernel_len); - if (kernel_len) - os_hdr = hdr; - } - - consdev = ""; -#if defined(CONFIG_8xx_CONS_SMC1) - consdev = "smc1"; -#elif defined(CONFIG_8xx_CONS_SMC2) - consdev = "smc2"; -#elif defined(CONFIG_8xx_CONS_SCC2) - consdev = "scc2"; -#elif defined(CONFIG_8xx_CONS_SCC3) - consdev = "scc3"; -#endif - - if (argc > 0) { - ulong len; - int i; - - for (i = 0, len = 0; i < argc; i += 1) - len += strlen(argv[i]) + 1; - cmdline = malloc(len); - copy_args(cmdline, argc, argv, ' '); - } else if ((cmdline = getenv("bootargs")) == NULL) { - cmdline = ""; - } - - loader = (void (*)(bd_t *, image_header_t *, char *, char *))images->ep; - - printf("## Transferring control to NetBSD stage-2 loader " - "(at address %08lx) ...\n", - (ulong)loader); - - bootstage_mark(BOOTSTAGE_ID_RUN_OS); - - /* - * NetBSD Stage-2 Loader Parameters: - * arg[0]: pointer to board info data - * arg[1]: image load address - * arg[2]: char pointer to the console device to use - * arg[3]: char pointer to the boot arguments - */ - (*loader)(gd->bd, os_hdr, consdev, cmdline); - - return 1; -} -#endif /* CONFIG_BOOTM_NETBSD*/ - -#ifdef CONFIG_LYNXKDI -static int do_bootm_lynxkdi(int flag, int argc, char * const argv[], - bootm_headers_t *images) -{ - image_header_t *hdr = &images->legacy_hdr_os_copy; - - if (flag != BOOTM_STATE_OS_GO) - return 0; - -#if defined(CONFIG_FIT) - if (!images->legacy_hdr_valid) { - fit_unsupported_reset("Lynx"); - return 1; - } -#endif - - lynxkdi_boot((image_header_t *)hdr); - - return 1; -} -#endif /* CONFIG_LYNXKDI */ - -#ifdef CONFIG_BOOTM_RTEMS -static int do_bootm_rtems(int flag, int argc, char * const argv[], - bootm_headers_t *images) -{ - void (*entry_point)(bd_t *); - - if (flag != BOOTM_STATE_OS_GO) - return 0; - -#if defined(CONFIG_FIT) - if (!images->legacy_hdr_valid) { - fit_unsupported_reset("RTEMS"); - return 1; - } -#endif - - entry_point = (void (*)(bd_t *))images->ep; - - printf("## Transferring control to RTEMS (at address %08lx) ...\n", - (ulong)entry_point); - - bootstage_mark(BOOTSTAGE_ID_RUN_OS); - - /* - * RTEMS Parameters: - * r3: ptr to board info data - */ - (*entry_point)(gd->bd); - - return 1; -} -#endif /* CONFIG_BOOTM_RTEMS */ - -#if defined(CONFIG_BOOTM_OSE) -static int do_bootm_ose(int flag, int argc, char * const argv[], - bootm_headers_t *images) -{ - void (*entry_point)(void); - - if (flag != BOOTM_STATE_OS_GO) - return 0; - -#if defined(CONFIG_FIT) - if (!images->legacy_hdr_valid) { - fit_unsupported_reset("OSE"); - return 1; - } -#endif - - entry_point = (void (*)(void))images->ep; - - printf("## Transferring control to OSE (at address %08lx) ...\n", - (ulong)entry_point); - - bootstage_mark(BOOTSTAGE_ID_RUN_OS); - - /* - * OSE Parameters: - * None - */ - (*entry_point)(); - - return 1; -} -#endif /* CONFIG_BOOTM_OSE */ - -#if defined(CONFIG_BOOTM_PLAN9) -static int do_bootm_plan9(int flag, int argc, char * const argv[], - bootm_headers_t *images) -{ - void (*entry_point)(void); - char *s; - - if (flag != BOOTM_STATE_OS_GO) - return 0; - -#if defined(CONFIG_FIT) - if (!images->legacy_hdr_valid) { - fit_unsupported_reset("Plan 9"); - return 1; - } -#endif - - /* See README.plan9 */ - s = getenv("confaddr"); - if (s != NULL) { - char *confaddr = (char *)simple_strtoul(s, NULL, 16); - - if (argc > 0) { - copy_args(confaddr, argc, argv, '\n'); - } else { - s = getenv("bootargs"); - if (s != NULL) - strcpy(confaddr, s); - } - } - - entry_point = (void (*)(void))images->ep; - - printf("## Transferring control to Plan 9 (at address %08lx) ...\n", - (ulong)entry_point); - - bootstage_mark(BOOTSTAGE_ID_RUN_OS); - - /* - * Plan 9 Parameters: - * None - */ - (*entry_point)(); - - return 1; -} -#endif /* CONFIG_BOOTM_PLAN9 */ - -#if defined(CONFIG_BOOTM_VXWORKS) && \ - (defined(CONFIG_PPC) || defined(CONFIG_ARM)) - -void do_bootvx_fdt(bootm_headers_t *images) -{ -#if defined(CONFIG_OF_LIBFDT) - int ret; - char *bootline; - ulong of_size = images->ft_len; - char **of_flat_tree = &images->ft_addr; - struct lmb *lmb = &images->lmb; - - if (*of_flat_tree) { - boot_fdt_add_mem_rsv_regions(lmb, *of_flat_tree); - - ret = boot_relocate_fdt(lmb, of_flat_tree, &of_size); - if (ret) - return; - - ret = fdt_add_subnode(*of_flat_tree, 0, "chosen"); - if ((ret >= 0 || ret == -FDT_ERR_EXISTS)) { - bootline = getenv("bootargs"); - if (bootline) { - ret = fdt_find_and_setprop(*of_flat_tree, - "/chosen", "bootargs", - bootline, - strlen(bootline) + 1, 1); - if (ret < 0) { - printf("## ERROR: %s : %s\n", __func__, - fdt_strerror(ret)); - return; - } - } - } else { - printf("## ERROR: %s : %s\n", __func__, - fdt_strerror(ret)); - return; - } - } -#endif - - boot_prep_vxworks(images); - - bootstage_mark(BOOTSTAGE_ID_RUN_OS); - -#if defined(CONFIG_OF_LIBFDT) - printf("## Starting vxWorks at 0x%08lx, device tree at 0x%08lx ...\n", - (ulong)images->ep, (ulong)*of_flat_tree); -#else - printf("## Starting vxWorks at 0x%08lx\n", (ulong)images->ep); -#endif - - boot_jump_vxworks(images); - - puts("## vxWorks terminated\n"); -} - -static int do_bootm_vxworks(int flag, int argc, char * const argv[], - bootm_headers_t *images) -{ - if (flag != BOOTM_STATE_OS_GO) - return 0; - -#if defined(CONFIG_FIT) - if (!images->legacy_hdr_valid) { - fit_unsupported_reset("VxWorks"); - return 1; - } -#endif - - do_bootvx_fdt(images); - - return 1; -} -#endif - -#if defined(CONFIG_CMD_ELF) -static int do_bootm_qnxelf(int flag, int argc, char * const argv[], - bootm_headers_t *images) -{ - char *local_args[2]; - char str[16]; - - if (flag != BOOTM_STATE_OS_GO) - return 0; - -#if defined(CONFIG_FIT) - if (!images->legacy_hdr_valid) { - fit_unsupported_reset("QNX"); - return 1; - } -#endif - - sprintf(str, "%lx", images->ep); /* write entry-point into string */ - local_args[0] = argv[0]; - local_args[1] = str; /* and provide it via the arguments */ - do_bootelf(NULL, 0, 2, local_args); - - return 1; -} -#endif - -#ifdef CONFIG_INTEGRITY -static int do_bootm_integrity(int flag, int argc, char * const argv[], - bootm_headers_t *images) -{ - void (*entry_point)(void); - - if (flag != BOOTM_STATE_OS_GO) - return 0; - -#if defined(CONFIG_FIT) - if (!images->legacy_hdr_valid) { - fit_unsupported_reset("INTEGRITY"); - return 1; - } -#endif - - entry_point = (void (*)(void))images->ep; - - printf("## Transferring control to INTEGRITY (at address %08lx) ...\n", - (ulong)entry_point); - - bootstage_mark(BOOTSTAGE_ID_RUN_OS); - - /* - * INTEGRITY Parameters: - * None - */ - (*entry_point)(); - - return 1; -} -#endif - #ifdef CONFIG_CMD_BOOTZ int __weak bootz_setup(ulong image, ulong *start, ulong *end) @@ -1898,14 +574,9 @@ static int bootz_start(cmd_tbl_t *cmdtp, int flag, int argc, * Handle the BOOTM_STATE_FINDOTHER state ourselves as we do not * have a header that provide this informaiton. */ - if (bootm_find_ramdisk(flag, argc, argv)) + if (bootm_find_ramdisk_fdt(flag, argc, argv)) return 1; -#if defined(CONFIG_OF_LIBFDT) - if (bootm_find_fdt(flag, argc, argv)) - return 1; -#endif - return 0; } diff --git a/include/bootm.h b/include/bootm.h new file mode 100644 index 00000000000..0a3ec566c9c --- /dev/null +++ b/include/bootm.h @@ -0,0 +1,55 @@ +/* + * (C) Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _BOOTM_H +#define _BOOTM_H + +#include +#include + +#define BOOTM_ERR_RESET (-1) +#define BOOTM_ERR_OVERLAP (-2) +#define BOOTM_ERR_UNIMPLEMENTED (-3) + +/* + * Continue booting an OS image; caller already has: + * - copied image header to global variable `header' + * - checked header magic number, checksums (both header & image), + * - verified image architecture (PPC) and type (KERNEL or MULTI), + * - loaded (first part of) image to header load address, + * - disabled interrupts. + * + * @flag: Flags indicating what to do (BOOTM_STATE_...) + * @argc: Number of arguments. Note that the arguments are shifted down + * so that 0 is the first argument not processed by U-Boot, and + * argc is adjusted accordingly. This avoids confusion as to how + * many arguments are available for the OS. + * @images: Pointers to os/initrd/fdt + * @return 1 on error. On success the OS boots so this function does + * not return. + */ +typedef int boot_os_fn(int flag, int argc, char * const argv[], + bootm_headers_t *images); + +extern boot_os_fn do_bootm_linux; +int do_bootelf(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); +void lynxkdi_boot(image_header_t *hdr); + +boot_os_fn *bootm_os_get_boot_func(int os); + +int boot_selected_os(int argc, char * const argv[], int state, + bootm_headers_t *images, boot_os_fn *boot_fn); + +ulong bootm_disable_interrupts(void); + +/* This is a special function used by bootz */ +int bootm_find_ramdisk_fdt(int flag, int argc, char * const argv[]); + +int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], + int states, bootm_headers_t *images, int boot_progress); + +#endif -- cgit v1.3.1 From aa53233a15e22ae207436e4015a69d24f06c2703 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 11 Jun 2014 23:29:41 -0600 Subject: Add an I/O tracing feature When debugging drivers it is useful to see what I/O accesses were done and in what order. Even if the individual accesses are of little interest it can be useful to verify that the access pattern is consistent each time an operation is performed. In this case a checksum can be used to characterise the operation of a driver. The checksum can be compared across different runs of the operation to verify that the driver is working properly. In particular, when performing major refactoring of the driver, where the access pattern should not change, the checksum provides assurance that the refactoring work has not broken the driver. Add an I/O tracing feature and associated commands to provide this facility. It works by sneaking into the io.h heder for an architecture and redirecting I/O accesses through its tracing mechanism. For now no commands are provided to examine the trace buffer. The format is fairly simple, so 'md' is a reasonable substitute. Note: The checksum feature is only useful for I/O regions where the contents do not change outside of software control. Where this is not suitable you can fall back to manually comparing the addresses. It might be useful to enhance tracing to only checksum the accesses and not the data read/written. Signed-off-by: Simon Glass --- README | 23 +++++++ common/Makefile | 2 + common/cmd_iotrace.c | 73 ++++++++++++++++++++++ common/iotrace.c | 169 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/iotrace.h | 104 +++++++++++++++++++++++++++++++ 5 files changed, 371 insertions(+) create mode 100644 common/cmd_iotrace.c create mode 100644 common/iotrace.c create mode 100644 include/iotrace.h (limited to 'common/Makefile') diff --git a/README b/README index 7129df822c1..d0723701c8c 100644 --- a/README +++ b/README @@ -1000,6 +1000,7 @@ The following options need to be configured: CONFIG_CMD_IMLS List all images found in NOR flash CONFIG_CMD_IMLS_NAND * List all images found in NAND flash CONFIG_CMD_IMMAP * IMMR dump support + CONFIG_CMD_IOTRACE * I/O tracing for debugging CONFIG_CMD_IMPORTENV * import an environment CONFIG_CMD_INI * import data from an ini file into the env CONFIG_CMD_IRQ * irqinfo @@ -1171,6 +1172,28 @@ The following options need to be configured: Note that if the GPIO device uses I2C, then the I2C interface must also be configured. See I2C Support, below. +- I/O tracing: + When CONFIG_IO_TRACE is selected, U-Boot intercepts all I/O + accesses and can checksum them or write a list of them out + to memory. See the 'iotrace' command for details. This is + useful for testing device drivers since it can confirm that + the driver behaves the same way before and after a code + change. Currently this is supported on sandbox and arm. To + add support for your architecture, add '#include ' + to the bottom of arch//include/asm/io.h and test. + + Example output from the 'iotrace stats' command is below. + Note that if the trace buffer is exhausted, the checksum will + still continue to operate. + + iotrace is enabled + Start: 10000000 (buffer start address) + Size: 00010000 (buffer size) + Offset: 00000120 (current buffer offset) + Output: 10000120 (start + offset) + Count: 00000018 (number of trace records) + CRC32: 9526fb66 (CRC32 of all trace records) + - Timestamp Support: When CONFIG_TIMESTAMP is selected, the timestamp diff --git a/common/Makefile b/common/Makefile index f6cd98012a1..de5cce86cc6 100644 --- a/common/Makefile +++ b/common/Makefile @@ -114,6 +114,7 @@ obj-$(CONFIG_CMD_FUSE) += cmd_fuse.o obj-$(CONFIG_CMD_GETTIME) += cmd_gettime.o obj-$(CONFIG_CMD_GPIO) += cmd_gpio.o obj-$(CONFIG_CMD_I2C) += cmd_i2c.o +obj-$(CONFIG_CMD_IOTRACE) += cmd_iotrace.o obj-$(CONFIG_CMD_HASH) += cmd_hash.o obj-$(CONFIG_CMD_IDE) += cmd_ide.o obj-$(CONFIG_CMD_IMMAP) += cmd_immap.o @@ -261,6 +262,7 @@ obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o obj-$(CONFIG_OF_LIBFDT) += image-fdt.o obj-$(CONFIG_FIT) += image-fit.o obj-$(CONFIG_FIT_SIGNATURE) += image-sig.o +obj-$(CONFIG_IO_TRACE) += iotrace.o obj-y += memsize.o obj-y += stdio.o diff --git a/common/cmd_iotrace.c b/common/cmd_iotrace.c new file mode 100644 index 00000000000..f54276d2f57 --- /dev/null +++ b/common/cmd_iotrace.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2014 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +static void do_print_stats(void) +{ + ulong start, size, offset, count; + + printf("iotrace is %sabled\n", iotrace_get_enabled() ? "en" : "dis"); + iotrace_get_buffer(&start, &size, &offset, &count); + printf("Start: %08lx\n", start); + printf("Size: %08lx\n", size); + printf("Offset: %08lx\n", offset); + printf("Output: %08lx\n", start + offset); + printf("Count: %08lx\n", count); + printf("CRC32: %08lx\n", (ulong)iotrace_get_checksum()); +} + +static int do_set_buffer(int argc, char * const argv[]) +{ + ulong addr = 0, size = 0; + + if (argc == 2) { + addr = simple_strtoul(*argv++, NULL, 16); + size = simple_strtoul(*argv++, NULL, 16); + } else if (argc != 0) { + return CMD_RET_USAGE; + } + + iotrace_set_buffer(addr, size); + + return 0; +} + +int do_iotrace(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + const char *cmd = argc < 2 ? NULL : argv[1]; + + if (!cmd) + return cmd_usage(cmdtp); + switch (*cmd) { + case 'b': + return do_set_buffer(argc - 2, argv + 2); + case 'p': + iotrace_set_enabled(0); + break; + case 'r': + iotrace_set_enabled(1); + break; + case 's': + do_print_stats(); + break; + default: + return CMD_RET_USAGE; + } + + return 0; +} + +U_BOOT_CMD( + iotrace, 4, 1, do_iotrace, + "iotrace utility commands", + "stats - display iotrace stats\n" + "iotrace buffer
- set iotrace buffer\n" + "iotrace pause - pause tracing\n" + "iotrace resume - resume tracing" +); diff --git a/common/iotrace.c b/common/iotrace.c new file mode 100644 index 00000000000..ced426ea5c6 --- /dev/null +++ b/common/iotrace.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2014 Google, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#define IOTRACE_IMPL + +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* Support up to the machine word length for now */ +typedef ulong iovalue_t; + +enum iotrace_flags { + IOT_8 = 0, + IOT_16, + IOT_32, + + IOT_READ = 0 << 3, + IOT_WRITE = 1 << 3, +}; + +/** + * struct iotrace_record - Holds a single I/O trace record + * + * @flags: I/O access type + * @addr: Address of access + * @value: Value written or read + */ +struct iotrace_record { + enum iotrace_flags flags; + phys_addr_t addr; + iovalue_t value; +}; + +/** + * struct iotrace - current trace status and checksum + * + * @start: Start address of iotrace buffer + * @size: Size of iotrace buffer in bytes + * @offset: Current write offset into iotrace buffer + * @crc32: Current value of CRC chceksum of trace records + * @enabled: true if enabled, false if disabled + */ +static struct iotrace { + ulong start; + ulong size; + ulong offset; + u32 crc32; + bool enabled; +} iotrace; + +static void add_record(int flags, const void *ptr, ulong value) +{ + struct iotrace_record srec, *rec = &srec; + + /* + * We don't support iotrace before relocation. Since the trace buffer + * is set up by a command, it can't be enabled at present. To change + * this we would need to set the iotrace buffer at build-time. See + * lib/trace.c for how this might be done if you are interested. + */ + if (!(gd->flags & GD_FLG_RELOC) || !iotrace.enabled) + return; + + /* Store it if there is room */ + if (iotrace.offset + sizeof(*rec) < iotrace.size) { + rec = (struct iotrace_record *)map_sysmem( + iotrace.start + iotrace.offset, + sizeof(value)); + } + + rec->flags = flags; + rec->addr = map_to_sysmem(ptr); + rec->value = value; + + /* Update our checksum */ + iotrace.crc32 = crc32(iotrace.crc32, (unsigned char *)rec, + sizeof(*rec)); + + iotrace.offset += sizeof(struct iotrace_record); +} + +u32 iotrace_readl(const void *ptr) +{ + u32 v; + + v = readl(ptr); + add_record(IOT_32 | IOT_READ, ptr, v); + + return v; +} + +void iotrace_writel(ulong value, const void *ptr) +{ + add_record(IOT_32 | IOT_WRITE, ptr, value); + writel(value, ptr); +} + +u16 iotrace_readw(const void *ptr) +{ + u32 v; + + v = readw(ptr); + add_record(IOT_16 | IOT_READ, ptr, v); + + return v; +} + +void iotrace_writew(ulong value, const void *ptr) +{ + add_record(IOT_16 | IOT_WRITE, ptr, value); + writew(value, ptr); +} + +u8 iotrace_readb(const void *ptr) +{ + u32 v; + + v = readb(ptr); + add_record(IOT_8 | IOT_READ, ptr, v); + + return v; +} + +void iotrace_writeb(ulong value, const void *ptr) +{ + add_record(IOT_8 | IOT_WRITE, ptr, value); + writeb(value, ptr); +} + +void iotrace_reset_checksum(void) +{ + iotrace.crc32 = 0; +} + +u32 iotrace_get_checksum(void) +{ + return iotrace.crc32; +} + +void iotrace_set_enabled(int enable) +{ + iotrace.enabled = enable; +} + +int iotrace_get_enabled(void) +{ + return iotrace.enabled; +} + +void iotrace_set_buffer(ulong start, ulong size) +{ + iotrace.start = start; + iotrace.size = size; + iotrace.offset = 0; + iotrace.crc32 = 0; +} + +void iotrace_get_buffer(ulong *start, ulong *size, ulong *offset, ulong *count) +{ + *start = iotrace.start; + *size = iotrace.size; + *offset = iotrace.offset; + *count = iotrace.offset / sizeof(struct iotrace_record); +} diff --git a/include/iotrace.h b/include/iotrace.h new file mode 100644 index 00000000000..9bd1f167abe --- /dev/null +++ b/include/iotrace.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2014 Google, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __IOTRACE_H +#define __IOTRACE_H + +#include + +/* + * This file is designed to be included in arch//include/asm/io.h. + * It redirects all IO access through a tracing/checksumming feature for + * testing purposes. + */ + +#if defined(CONFIG_IO_TRACE) && !defined(IOTRACE_IMPL) && \ + !defined(CONFIG_SPL_BUILD) + +#undef readl +#define readl(addr) iotrace_readl((const void *)(addr)) + +#undef writel +#define writel(val, addr) iotrace_writel(val, (const void *)(addr)) + +#undef readw +#define readw(addr) iotrace_readw((const void *)(addr)) + +#undef writew +#define writew(val, addr) iotrace_writew(val, (const void *)(addr)) + +#undef readb +#define readb(addr) iotrace_readb((const void *)(addr)) + +#undef writeb +#define writeb(val, addr) iotrace_writeb(val, (const void *)(addr)) + +#endif + +/* Tracing functions which mirror their io.h counterparts */ +u32 iotrace_readl(const void *ptr); +void iotrace_writel(ulong value, const void *ptr); +u16 iotrace_readw(const void *ptr); +void iotrace_writew(ulong value, const void *ptr); +u8 iotrace_readb(const void *ptr); +void iotrace_writeb(ulong value, const void *ptr); + +/** + * iotrace_reset_checksum() - Reset the iotrace checksum + */ +void iotrace_reset_checksum(void); + +/** + * iotrace_get_checksum() - Get the current checksum value + * + * @return currect checksum value + */ +u32 iotrace_get_checksum(void); + +/** + * iotrace_set_enabled() - Set whether iotracing is enabled or not + * + * This controls whether the checksum is updated and a trace record added + * for each I/O access. + * + * @enable: true to enable iotracing, false to disable + */ +void iotrace_set_enabled(int enable); + +/** + * iotrace_get_enabled() - Get whether iotracing is enabled or not + * + * @return true if enabled, false if disabled + */ +int iotrace_get_enabled(void); + +/** + * iotrace_set_buffer() - Set position and size of iotrace buffer + * + * Defines where the iotrace buffer goes, and resets the output pointer to + * the start of the buffer. + * + * The buffer can be 0 size in which case the checksum is updated but no + * trace records are writen. If the buffer is exhausted, the offset will + * continue to increase but not new data will be written. + * + * @start: Start address of buffer + * @size: Size of buffer in bytes + */ +void iotrace_set_buffer(ulong start, ulong size); + +/** + * iotrace_get_buffer() - Get buffer information + * + * @start: Returns start address of buffer + * @size: Returns size of buffer in bytes + * @offset: Returns the byte offset where the next output trace record will + * @count: Returns the number of trace records recorded + * be written (or would be if the buffer was large enough) + */ +void iotrace_get_buffer(ulong *start, ulong *size, ulong *offset, ulong *count); + +#endif /* __IOTRACE_H */ -- cgit v1.3.1