diff options
Diffstat (limited to 'roms/u-boot/common/autoboot.c')
-rw-r--r-- | roms/u-boot/common/autoboot.c | 397 |
1 files changed, 397 insertions, 0 deletions
diff --git a/roms/u-boot/common/autoboot.c b/roms/u-boot/common/autoboot.c new file mode 100644 index 000000000..b42148c72 --- /dev/null +++ b/roms/u-boot/common/autoboot.c @@ -0,0 +1,397 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + */ + +#include <common.h> +#include <autoboot.h> +#include <bootretry.h> +#include <cli.h> +#include <command.h> +#include <console.h> +#include <env.h> +#include <fdtdec.h> +#include <hash.h> +#include <log.h> +#include <malloc.h> +#include <memalign.h> +#include <menu.h> +#include <post.h> +#include <time.h> +#include <asm/global_data.h> +#include <linux/delay.h> +#include <u-boot/sha256.h> +#include <bootcount.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define MAX_DELAY_STOP_STR 64 + +#ifndef DEBUG_BOOTKEYS +#define DEBUG_BOOTKEYS 0 +#endif +#define debug_bootkeys(fmt, args...) \ + debug_cond(DEBUG_BOOTKEYS, fmt, ##args) + +/* Stored value of bootdelay, used by autoboot_command() */ +static int stored_bootdelay; +static int menukey; + +#ifdef CONFIG_AUTOBOOT_ENCRYPTION +#define AUTOBOOT_STOP_STR_SHA256 CONFIG_AUTOBOOT_STOP_STR_SHA256 +#else +#define AUTOBOOT_STOP_STR_SHA256 "" +#endif + +#ifdef CONFIG_AUTOBOOT_USE_MENUKEY +#define AUTOBOOT_MENUKEY CONFIG_AUTOBOOT_MENUKEY +#else +#define AUTOBOOT_MENUKEY 0 +#endif + +/* + * Use a "constant-length" time compare function for this + * hash compare: + * + * https://crackstation.net/hashing-security.htm + */ +static int slow_equals(u8 *a, u8 *b, int len) +{ + int diff = 0; + int i; + + for (i = 0; i < len; i++) + diff |= a[i] ^ b[i]; + + return diff == 0; +} + +/** + * passwd_abort_sha256() - check for a hashed key sequence to abort booting + * + * This checks for the user entering a SHA256 hash within a given time. + * + * @etime: Timeout value ticks (stop when get_ticks() reachs this) + * @return 0 if autoboot should continue, 1 if it should stop + */ +static int passwd_abort_sha256(uint64_t etime) +{ + const char *sha_env_str = env_get("bootstopkeysha256"); + u8 sha_env[SHA256_SUM_LEN]; + u8 *sha; + char *presskey; + char *c; + const char *algo_name = "sha256"; + u_int presskey_len = 0; + int abort = 0; + int size = sizeof(sha); + int ret; + + if (sha_env_str == NULL) + sha_env_str = AUTOBOOT_STOP_STR_SHA256; + + presskey = malloc_cache_aligned(MAX_DELAY_STOP_STR); + c = strstr(sha_env_str, ":"); + if (c && (c - sha_env_str < MAX_DELAY_STOP_STR)) { + /* preload presskey with salt */ + memcpy(presskey, sha_env_str, c - sha_env_str); + presskey_len = c - sha_env_str; + sha_env_str = c + 1; + } + /* + * Generate the binary value from the environment hash value + * so that we can compare this value with the computed hash + * from the user input + */ + ret = hash_parse_string(algo_name, sha_env_str, sha_env); + if (ret) { + printf("Hash %s not supported!\n", algo_name); + return 0; + } + + sha = malloc_cache_aligned(SHA256_SUM_LEN); + size = SHA256_SUM_LEN; + /* + * We don't know how long the stop-string is, so we need to + * generate the sha256 hash upon each input character and + * compare the value with the one saved in the environment + */ + do { + if (tstc()) { + /* Check for input string overflow */ + if (presskey_len >= MAX_DELAY_STOP_STR) { + free(presskey); + free(sha); + return 0; + } + + presskey[presskey_len++] = getchar(); + + /* Calculate sha256 upon each new char */ + hash_block(algo_name, (const void *)presskey, + presskey_len, sha, &size); + + /* And check if sha matches saved value in env */ + if (slow_equals(sha, sha_env, SHA256_SUM_LEN)) + abort = 1; + } + } while (!abort && get_ticks() <= etime); + + free(presskey); + free(sha); + return abort; +} + +/** + * passwd_abort_key() - check for a key sequence to aborted booting + * + * This checks for the user entering a string within a given time. + * + * @etime: Timeout value ticks (stop when get_ticks() reachs this) + * @return 0 if autoboot should continue, 1 if it should stop + */ +static int passwd_abort_key(uint64_t etime) +{ + int abort = 0; + struct { + char *str; + u_int len; + int retry; + } + delaykey[] = { + { .str = env_get("bootdelaykey"), .retry = 1 }, + { .str = env_get("bootstopkey"), .retry = 0 }, + }; + + char presskey[MAX_DELAY_STOP_STR]; + int presskey_len = 0; + int presskey_max = 0; + int i; + +# ifdef CONFIG_AUTOBOOT_DELAY_STR + if (delaykey[0].str == NULL) + delaykey[0].str = CONFIG_AUTOBOOT_DELAY_STR; +# endif +# ifdef CONFIG_AUTOBOOT_STOP_STR + if (delaykey[1].str == NULL) + delaykey[1].str = CONFIG_AUTOBOOT_STOP_STR; +# 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++] = getchar(); + } else { + for (i = 0; i < presskey_max - 1; i++) + presskey[i] = presskey[i + 1]; + + presskey[i] = getchar(); + } + } + + 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"); + + /* don't retry auto boot */ + if (!delaykey[i].retry) + bootretry_dont_retry(); + abort = 1; + } + } + } while (!abort && get_ticks() <= etime); + + return abort; +} + +/*************************************************************************** + * Watch for 'delay' seconds for autoboot stop or autoboot delay string. + * returns: 0 - no key string, allow autoboot 1 - got key string, abort + */ +static int abortboot_key_sequence(int bootdelay) +{ + int abort; + uint64_t etime = endtick(bootdelay); + +# ifdef CONFIG_AUTOBOOT_PROMPT + /* + * CONFIG_AUTOBOOT_PROMPT includes the %d for all boards. + * To print the bootdelay value upon bootup. + */ + printf(CONFIG_AUTOBOOT_PROMPT, bootdelay); +# endif + + if (IS_ENABLED(CONFIG_AUTOBOOT_ENCRYPTION)) + abort = passwd_abort_sha256(etime); + else + abort = passwd_abort_key(etime); + if (!abort) + debug_bootkeys("key timeout\n"); + + return abort; +} + +static int abortboot_single_key(int bootdelay) +{ + int abort = 0; + unsigned long ts; + + printf("Hit any key to stop autoboot: %2d ", bootdelay); + + /* + * Check if key already pressed + */ + if (tstc()) { /* we got a key press */ + getchar(); /* consume input */ + puts("\b\b\b 0"); + abort = 1; /* don't auto boot */ + } + + while ((bootdelay > 0) && (!abort)) { + --bootdelay; + /* delay 1000 ms */ + ts = get_timer(0); + do { + if (tstc()) { /* we got a key press */ + int key; + + abort = 1; /* don't auto boot */ + bootdelay = 0; /* no more delay */ + key = getchar();/* consume input */ + if (IS_ENABLED(CONFIG_AUTOBOOT_USE_MENUKEY)) + menukey = key; + break; + } + udelay(10000); + } while (!abort && get_timer(ts) < 1000); + + printf("\b\b\b%2d ", bootdelay); + } + + putc('\n'); + + return abort; +} + +static int abortboot(int bootdelay) +{ + int abort = 0; + + if (bootdelay >= 0) { + if (IS_ENABLED(CONFIG_AUTOBOOT_KEYED)) + abort = abortboot_key_sequence(bootdelay); + else + abort = abortboot_single_key(bootdelay); + } + + if (IS_ENABLED(CONFIG_SILENT_CONSOLE) && abort) + gd->flags &= ~GD_FLG_SILENT; + + return abort; +} + +static void process_fdt_options(const void *blob) +{ +#ifdef CONFIG_SYS_TEXT_BASE + 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) + env_set_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) + env_set_addr("rootaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr)); +#endif /* CONFIG_SYS_TEXT_BASE */ +} + +const char *bootdelay_process(void) +{ + char *s; + int bootdelay; + + bootcount_inc(); + + s = env_get("bootdelay"); + bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; + + if (IS_ENABLED(CONFIG_OF_CONTROL)) + bootdelay = fdtdec_get_config_int(gd->fdt_blob, "bootdelay", + bootdelay); + + debug("### main_loop entered: bootdelay=%d\n\n", bootdelay); + + if (IS_ENABLED(CONFIG_AUTOBOOT_MENU_SHOW)) + bootdelay = menu_show(bootdelay); + bootretry_init_cmd_timeout(); + +#ifdef CONFIG_POST + if (gd->flags & GD_FLG_POSTFAIL) { + s = env_get("failbootcmd"); + } else +#endif /* CONFIG_POST */ + if (bootcount_error()) + s = env_get("altbootcmd"); + else + s = env_get("bootcmd"); + + if (IS_ENABLED(CONFIG_OF_CONTROL)) + process_fdt_options(gd->fdt_blob); + stored_bootdelay = bootdelay; + + return s; +} + +void autoboot_command(const char *s) +{ + debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>"); + + if (s && (stored_bootdelay == -2 || + (stored_bootdelay != -1 && !abortboot(stored_bootdelay)))) { + bool lock; + int prev; + + lock = IS_ENABLED(CONFIG_AUTOBOOT_KEYED) && + !IS_ENABLED(CONFIG_AUTOBOOT_KEYED_CTRLC); + if (lock) + prev = disable_ctrlc(1); /* disable Ctrl-C checking */ + + run_command_list(s, -1, 0); + + if (lock) + disable_ctrlc(prev); /* restore Ctrl-C checking */ + } + + if (IS_ENABLED(CONFIG_AUTOBOOT_USE_MENUKEY) && + menukey == AUTOBOOT_MENUKEY) { + s = env_get("menucmd"); + if (s) + run_command_list(s, -1, 0); + } +} |