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/autoboot.c | 363 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 363 insertions(+) create mode 100644 common/autoboot.c (limited to 'common/autoboot.c') diff --git a/common/autoboot.c b/common/autoboot.c new file mode 100644 index 0000000..6933e3f --- /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 */ +} -- cgit v1.1 From 0098e179e1afacb3cf595c67a98b8739dc7edcde Mon Sep 17 00:00:00 2001 From: Simon Glass 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 --- common/autoboot.c | 1 + 1 file changed, 1 insertion(+) (limited to 'common/autoboot.c') diff --git a/common/autoboot.c b/common/autoboot.c index 6933e3f..5f8d9c3 100644 --- a/common/autoboot.c +++ b/common/autoboot.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include -- cgit v1.1 From b26440f1fa243396000536028ea00e5e185b6b6a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 10 Apr 2014 20:01:31 -0600 Subject: Rename bootretry functions and remove #ifdefs Add a bootretry_ prefix to these two functions, and remove the need for the #ifdef around everything (it moves to the Makefile). Signed-off-by: Simon Glass --- common/autoboot.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'common/autoboot.c') diff --git a/common/autoboot.c b/common/autoboot.c index 5f8d9c3..9843898 100644 --- a/common/autoboot.c +++ b/common/autoboot.c @@ -113,11 +113,9 @@ static int abortboot_keyed(int bootdelay) 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; } } @@ -305,9 +303,7 @@ void bootdelay_process(void) #if defined(CONFIG_MENU_SHOW) bootdelay = menu_show(bootdelay); #endif -# ifdef CONFIG_BOOT_RETRY_TIME - init_cmd_timeout(); -# endif /* CONFIG_BOOT_RETRY_TIME */ + bootretry_init_cmd_timeout(); #ifdef CONFIG_POST if (gd->flags & GD_FLG_POSTFAIL) { -- cgit v1.1 From affb215626f91e717088a27081d24c473895d47d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 10 Apr 2014 20:01:35 -0600 Subject: main: Make the execution path a little clearer in main.c bootdelay_process() never returns in some circumstances, whichs makes the control flow confusing. Change it so that the decision about how to execute the boot command is made in the main_loop() code, so it is easier to follow. Move CLI stuff to cli.c. Signed-off-by: Simon Glass --- common/autoboot.c | 81 +++++++++---------------------------------------------- 1 file changed, 12 insertions(+), 69 deletions(-) (limited to 'common/autoboot.c') diff --git a/common/autoboot.c b/common/autoboot.c index 9843898..dc24cae 100644 --- a/common/autoboot.c +++ b/common/autoboot.c @@ -22,6 +22,9 @@ DECLARE_GLOBAL_DATA_PTR; #define debug_bootkeys(fmt, args...) \ debug_cond(DEBUG_BOOTKEYS, fmt, ##args) +/* Stored value of bootdelay, used by autoboot_command() */ +static int stored_bootdelay; + /*************************************************************************** * Watch for 'delay' seconds for autoboot stop or autoboot delay string. * returns: 0 - no key string, allow autoboot 1 - got key string, abort @@ -205,57 +208,9 @@ static int abortboot(int 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) { +#if defined(CONFIG_OF_CONTROL) ulong addr; /* Add an env variable to point to a kernel payload, if available */ @@ -267,14 +222,11 @@ static void process_fdt_options(const void *blob) 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) +const char *bootdelay_process(void) { -#ifdef CONFIG_OF_CONTROL - char *env; -#endif char *s; int bootdelay; #ifdef CONFIG_BOOTCOUNT_LIMIT @@ -318,27 +270,18 @@ void bootdelay_process(void) } 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); + stored_bootdelay = bootdelay; - /* - * 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 */ + return s; +} +void autoboot_command(const char *s) +{ debug("### main_loop: bootcmd=\"%s\"\n", s ? s : ""); - if (bootdelay != -1 && s && !abortboot(bootdelay)) { + if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) { #if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC) int prev = disable_ctrlc(1); /* disable Control C checking */ #endif -- cgit v1.1 From 9e546ee9c90fc0a888423fa3269020fe736df7a3 Mon Sep 17 00:00:00 2001 From: Jeroen Hofstee Date: Mon, 16 Jun 2014 00:17:33 +0200 Subject: cosmetic: autoboot: update old style GNU struct init Signed-off-by: Jeroen Hofstee --- common/autoboot.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'common/autoboot.c') diff --git a/common/autoboot.c b/common/autoboot.c index dc24cae..30102a4 100644 --- a/common/autoboot.c +++ b/common/autoboot.c @@ -40,10 +40,10 @@ static int abortboot_keyed(int bootdelay) int retry; } delaykey[] = { - { str: getenv("bootdelaykey"), retry: 1 }, - { str: getenv("bootdelaykey2"), retry: 1 }, - { str: getenv("bootstopkey"), retry: 0 }, - { str: getenv("bootstopkey2"), retry: 0 }, + { .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]; -- cgit v1.1