xref: /openbmc/u-boot/common/autoboot.c (revision 018f530323b2cc41be05be5b12375d3648f06554)
166ded17dSSimon Glass /*
266ded17dSSimon Glass  * (C) Copyright 2000
366ded17dSSimon Glass  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
466ded17dSSimon Glass  *
566ded17dSSimon Glass  * SPDX-License-Identifier:	GPL-2.0+
666ded17dSSimon Glass  */
766ded17dSSimon Glass 
866ded17dSSimon Glass #include <common.h>
939e1230eSJeroen Hofstee #include <autoboot.h>
100098e179SSimon Glass #include <bootretry.h>
1166ded17dSSimon Glass #include <cli.h>
1224b852a7SSimon Glass #include <console.h>
1366ded17dSSimon Glass #include <fdtdec.h>
1466ded17dSSimon Glass #include <menu.h>
1566ded17dSSimon Glass #include <post.h>
168f0b1e24SStefan Roese #include <u-boot/sha256.h>
1766ded17dSSimon Glass 
1866ded17dSSimon Glass DECLARE_GLOBAL_DATA_PTR;
1966ded17dSSimon Glass 
2066ded17dSSimon Glass #define MAX_DELAY_STOP_STR 32
2166ded17dSSimon Glass 
2266ded17dSSimon Glass #ifndef DEBUG_BOOTKEYS
2366ded17dSSimon Glass #define DEBUG_BOOTKEYS 0
2466ded17dSSimon Glass #endif
2566ded17dSSimon Glass #define debug_bootkeys(fmt, args...)		\
2666ded17dSSimon Glass 	debug_cond(DEBUG_BOOTKEYS, fmt, ##args)
2766ded17dSSimon Glass 
28affb2156SSimon Glass /* Stored value of bootdelay, used by autoboot_command() */
29affb2156SSimon Glass static int stored_bootdelay;
30affb2156SSimon Glass 
3166ded17dSSimon Glass #if defined(CONFIG_AUTOBOOT_KEYED)
328f0b1e24SStefan Roese #if defined(CONFIG_AUTOBOOT_STOP_STR_SHA256)
338f0b1e24SStefan Roese 
348f0b1e24SStefan Roese /*
358f0b1e24SStefan Roese  * Use a "constant-length" time compare function for this
368f0b1e24SStefan Roese  * hash compare:
378f0b1e24SStefan Roese  *
388f0b1e24SStefan Roese  * https://crackstation.net/hashing-security.htm
398f0b1e24SStefan Roese  */
408f0b1e24SStefan Roese static int slow_equals(u8 *a, u8 *b, int len)
418f0b1e24SStefan Roese {
428f0b1e24SStefan Roese 	int diff = 0;
438f0b1e24SStefan Roese 	int i;
448f0b1e24SStefan Roese 
458f0b1e24SStefan Roese 	for (i = 0; i < len; i++)
468f0b1e24SStefan Roese 		diff |= a[i] ^ b[i];
478f0b1e24SStefan Roese 
488f0b1e24SStefan Roese 	return diff == 0;
498f0b1e24SStefan Roese }
508f0b1e24SStefan Roese 
518f0b1e24SStefan Roese static int passwd_abort(uint64_t etime)
528f0b1e24SStefan Roese {
538f0b1e24SStefan Roese 	const char *sha_env_str = getenv("bootstopkeysha256");
548f0b1e24SStefan Roese 	u8 sha_env[SHA256_SUM_LEN];
558f0b1e24SStefan Roese 	u8 sha[SHA256_SUM_LEN];
568f0b1e24SStefan Roese 	char presskey[MAX_DELAY_STOP_STR];
578f0b1e24SStefan Roese 	const char *algo_name = "sha256";
588f0b1e24SStefan Roese 	u_int presskey_len = 0;
598f0b1e24SStefan Roese 	int abort = 0;
608f0b1e24SStefan Roese 	int size;
618f0b1e24SStefan Roese 	int ret;
628f0b1e24SStefan Roese 
638f0b1e24SStefan Roese 	if (sha_env_str == NULL)
648f0b1e24SStefan Roese 		sha_env_str = CONFIG_AUTOBOOT_STOP_STR_SHA256;
658f0b1e24SStefan Roese 
668f0b1e24SStefan Roese 	/*
678f0b1e24SStefan Roese 	 * Generate the binary value from the environment hash value
688f0b1e24SStefan Roese 	 * so that we can compare this value with the computed hash
698f0b1e24SStefan Roese 	 * from the user input
708f0b1e24SStefan Roese 	 */
718f0b1e24SStefan Roese 	ret = hash_parse_string(algo_name, sha_env_str, sha_env);
728f0b1e24SStefan Roese 	if (ret) {
738f0b1e24SStefan Roese 		printf("Hash %s not supported!\n", algo_name);
748f0b1e24SStefan Roese 		return 0;
758f0b1e24SStefan Roese 	}
768f0b1e24SStefan Roese 
778f0b1e24SStefan Roese 	/*
788f0b1e24SStefan Roese 	 * We don't know how long the stop-string is, so we need to
798f0b1e24SStefan Roese 	 * generate the sha256 hash upon each input character and
808f0b1e24SStefan Roese 	 * compare the value with the one saved in the environment
818f0b1e24SStefan Roese 	 */
828f0b1e24SStefan Roese 	do {
838f0b1e24SStefan Roese 		if (tstc()) {
848f0b1e24SStefan Roese 			/* Check for input string overflow */
858f0b1e24SStefan Roese 			if (presskey_len >= MAX_DELAY_STOP_STR)
868f0b1e24SStefan Roese 				return 0;
878f0b1e24SStefan Roese 
888f0b1e24SStefan Roese 			presskey[presskey_len++] = getc();
898f0b1e24SStefan Roese 
908f0b1e24SStefan Roese 			/* Calculate sha256 upon each new char */
918f0b1e24SStefan Roese 			hash_block(algo_name, (const void *)presskey,
928f0b1e24SStefan Roese 				   presskey_len, sha, &size);
938f0b1e24SStefan Roese 
948f0b1e24SStefan Roese 			/* And check if sha matches saved value in env */
958f0b1e24SStefan Roese 			if (slow_equals(sha, sha_env, SHA256_SUM_LEN))
968f0b1e24SStefan Roese 				abort = 1;
978f0b1e24SStefan Roese 		}
988f0b1e24SStefan Roese 	} while (!abort && get_ticks() <= etime);
998f0b1e24SStefan Roese 
1008f0b1e24SStefan Roese 	return abort;
1018f0b1e24SStefan Roese }
1028f0b1e24SStefan Roese #else
1038f0b1e24SStefan Roese static int passwd_abort(uint64_t etime)
10466ded17dSSimon Glass {
10566ded17dSSimon Glass 	int abort = 0;
10666ded17dSSimon Glass 	struct {
10766ded17dSSimon Glass 		char *str;
10866ded17dSSimon Glass 		u_int len;
10966ded17dSSimon Glass 		int retry;
11066ded17dSSimon Glass 	}
11166ded17dSSimon Glass 	delaykey[] = {
1129e546ee9SJeroen Hofstee 		{ .str = getenv("bootdelaykey"),  .retry = 1 },
1139e546ee9SJeroen Hofstee 		{ .str = getenv("bootstopkey"),   .retry = 0 },
11466ded17dSSimon Glass 	};
11566ded17dSSimon Glass 
11666ded17dSSimon Glass 	char presskey[MAX_DELAY_STOP_STR];
11766ded17dSSimon Glass 	u_int presskey_len = 0;
11866ded17dSSimon Glass 	u_int presskey_max = 0;
11966ded17dSSimon Glass 	u_int i;
12066ded17dSSimon Glass 
12166ded17dSSimon Glass #  ifdef CONFIG_AUTOBOOT_DELAY_STR
12266ded17dSSimon Glass 	if (delaykey[0].str == NULL)
12366ded17dSSimon Glass 		delaykey[0].str = CONFIG_AUTOBOOT_DELAY_STR;
12466ded17dSSimon Glass #  endif
12566ded17dSSimon Glass #  ifdef CONFIG_AUTOBOOT_STOP_STR
1262d908fa0SStefan Roese 	if (delaykey[1].str == NULL)
1272d908fa0SStefan Roese 		delaykey[1].str = CONFIG_AUTOBOOT_STOP_STR;
12866ded17dSSimon Glass #  endif
12966ded17dSSimon Glass 
13066ded17dSSimon Glass 	for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i++) {
13166ded17dSSimon Glass 		delaykey[i].len = delaykey[i].str == NULL ?
13266ded17dSSimon Glass 				    0 : strlen(delaykey[i].str);
13366ded17dSSimon Glass 		delaykey[i].len = delaykey[i].len > MAX_DELAY_STOP_STR ?
13466ded17dSSimon Glass 				    MAX_DELAY_STOP_STR : delaykey[i].len;
13566ded17dSSimon Glass 
13666ded17dSSimon Glass 		presskey_max = presskey_max > delaykey[i].len ?
13766ded17dSSimon Glass 				    presskey_max : delaykey[i].len;
13866ded17dSSimon Glass 
13966ded17dSSimon Glass 		debug_bootkeys("%s key:<%s>\n",
14066ded17dSSimon Glass 			       delaykey[i].retry ? "delay" : "stop",
14166ded17dSSimon Glass 			       delaykey[i].str ? delaykey[i].str : "NULL");
14266ded17dSSimon Glass 	}
14366ded17dSSimon Glass 
14466ded17dSSimon Glass 	/* In order to keep up with incoming data, check timeout only
14566ded17dSSimon Glass 	 * when catch up.
14666ded17dSSimon Glass 	 */
14766ded17dSSimon Glass 	do {
14866ded17dSSimon Glass 		if (tstc()) {
14966ded17dSSimon Glass 			if (presskey_len < presskey_max) {
15066ded17dSSimon Glass 				presskey[presskey_len++] = getc();
15166ded17dSSimon Glass 			} else {
15266ded17dSSimon Glass 				for (i = 0; i < presskey_max - 1; i++)
15366ded17dSSimon Glass 					presskey[i] = presskey[i + 1];
15466ded17dSSimon Glass 
15566ded17dSSimon Glass 				presskey[i] = getc();
15666ded17dSSimon Glass 			}
15766ded17dSSimon Glass 		}
15866ded17dSSimon Glass 
15966ded17dSSimon Glass 		for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i++) {
16066ded17dSSimon Glass 			if (delaykey[i].len > 0 &&
16166ded17dSSimon Glass 			    presskey_len >= delaykey[i].len &&
16266ded17dSSimon Glass 				memcmp(presskey + presskey_len -
16366ded17dSSimon Glass 					delaykey[i].len, delaykey[i].str,
16466ded17dSSimon Glass 					delaykey[i].len) == 0) {
16566ded17dSSimon Glass 					debug_bootkeys("got %skey\n",
16666ded17dSSimon Glass 						delaykey[i].retry ? "delay" :
16766ded17dSSimon Glass 						"stop");
16866ded17dSSimon Glass 
16966ded17dSSimon Glass 				/* don't retry auto boot */
17066ded17dSSimon Glass 				if (!delaykey[i].retry)
17166ded17dSSimon Glass 					bootretry_dont_retry();
17266ded17dSSimon Glass 				abort = 1;
17366ded17dSSimon Glass 			}
17466ded17dSSimon Glass 		}
17566ded17dSSimon Glass 	} while (!abort && get_ticks() <= etime);
17666ded17dSSimon Glass 
1778f0b1e24SStefan Roese 	return abort;
1788f0b1e24SStefan Roese }
1798f0b1e24SStefan Roese #endif
1808f0b1e24SStefan Roese 
1818f0b1e24SStefan Roese /***************************************************************************
1828f0b1e24SStefan Roese  * Watch for 'delay' seconds for autoboot stop or autoboot delay string.
1838f0b1e24SStefan Roese  * returns: 0 -  no key string, allow autoboot 1 - got key string, abort
1848f0b1e24SStefan Roese  */
185d8da8298SMasahiro Yamada static int __abortboot(int bootdelay)
1868f0b1e24SStefan Roese {
1878f0b1e24SStefan Roese 	int abort;
1888f0b1e24SStefan Roese 	uint64_t etime = endtick(bootdelay);
1898f0b1e24SStefan Roese 
1908f0b1e24SStefan Roese #  ifdef CONFIG_AUTOBOOT_PROMPT
1918f0b1e24SStefan Roese 	/*
1928f0b1e24SStefan Roese 	 * CONFIG_AUTOBOOT_PROMPT includes the %d for all boards.
1938f0b1e24SStefan Roese 	 * To print the bootdelay value upon bootup.
1948f0b1e24SStefan Roese 	 */
1958f0b1e24SStefan Roese 	printf(CONFIG_AUTOBOOT_PROMPT, bootdelay);
1968f0b1e24SStefan Roese #  endif
1978f0b1e24SStefan Roese 
1988f0b1e24SStefan Roese 	abort = passwd_abort(etime);
19966ded17dSSimon Glass 	if (!abort)
20066ded17dSSimon Glass 		debug_bootkeys("key timeout\n");
20166ded17dSSimon Glass 
20266ded17dSSimon Glass 	return abort;
20366ded17dSSimon Glass }
20466ded17dSSimon Glass 
20566ded17dSSimon Glass # else	/* !defined(CONFIG_AUTOBOOT_KEYED) */
20666ded17dSSimon Glass 
20766ded17dSSimon Glass #ifdef CONFIG_MENUKEY
20866ded17dSSimon Glass static int menukey;
20966ded17dSSimon Glass #endif
21066ded17dSSimon Glass 
211d8da8298SMasahiro Yamada static int __abortboot(int bootdelay)
21266ded17dSSimon Glass {
21366ded17dSSimon Glass 	int abort = 0;
21466ded17dSSimon Glass 	unsigned long ts;
21566ded17dSSimon Glass 
21666ded17dSSimon Glass #ifdef CONFIG_MENUPROMPT
21766ded17dSSimon Glass 	printf(CONFIG_MENUPROMPT);
21866ded17dSSimon Glass #else
21966ded17dSSimon Glass 	printf("Hit any key to stop autoboot: %2d ", bootdelay);
22066ded17dSSimon Glass #endif
22166ded17dSSimon Glass 
22266ded17dSSimon Glass 	/*
22366ded17dSSimon Glass 	 * Check if key already pressed
22466ded17dSSimon Glass 	 */
22566ded17dSSimon Glass 	if (tstc()) {	/* we got a key press	*/
22666ded17dSSimon Glass 		(void) getc();  /* consume input	*/
22766ded17dSSimon Glass 		puts("\b\b\b 0");
22866ded17dSSimon Glass 		abort = 1;	/* don't auto boot	*/
22966ded17dSSimon Glass 	}
23066ded17dSSimon Glass 
23166ded17dSSimon Glass 	while ((bootdelay > 0) && (!abort)) {
23266ded17dSSimon Glass 		--bootdelay;
23366ded17dSSimon Glass 		/* delay 1000 ms */
23466ded17dSSimon Glass 		ts = get_timer(0);
23566ded17dSSimon Glass 		do {
23666ded17dSSimon Glass 			if (tstc()) {	/* we got a key press	*/
23766ded17dSSimon Glass 				abort  = 1;	/* don't auto boot	*/
23866ded17dSSimon Glass 				bootdelay = 0;	/* no more delay	*/
23966ded17dSSimon Glass # ifdef CONFIG_MENUKEY
24066ded17dSSimon Glass 				menukey = getc();
24166ded17dSSimon Glass # else
24266ded17dSSimon Glass 				(void) getc();  /* consume input	*/
24366ded17dSSimon Glass # endif
24466ded17dSSimon Glass 				break;
24566ded17dSSimon Glass 			}
24666ded17dSSimon Glass 			udelay(10000);
24766ded17dSSimon Glass 		} while (!abort && get_timer(ts) < 1000);
24866ded17dSSimon Glass 
24966ded17dSSimon Glass 		printf("\b\b\b%2d ", bootdelay);
25066ded17dSSimon Glass 	}
25166ded17dSSimon Glass 
25266ded17dSSimon Glass 	putc('\n');
25366ded17dSSimon Glass 
25466ded17dSSimon Glass 	return abort;
25566ded17dSSimon Glass }
25666ded17dSSimon Glass # endif	/* CONFIG_AUTOBOOT_KEYED */
25766ded17dSSimon Glass 
25866ded17dSSimon Glass static int abortboot(int bootdelay)
25966ded17dSSimon Glass {
26046327392SMasahiro Yamada 	int abort = 0;
26109b9d9e5SMasahiro Yamada 
26246327392SMasahiro Yamada 	if (bootdelay >= 0)
26309b9d9e5SMasahiro Yamada 		abort = __abortboot(bootdelay);
26409b9d9e5SMasahiro Yamada 
26509b9d9e5SMasahiro Yamada #ifdef CONFIG_SILENT_CONSOLE
26609b9d9e5SMasahiro Yamada 	if (abort)
26709b9d9e5SMasahiro Yamada 		gd->flags &= ~GD_FLG_SILENT;
26809b9d9e5SMasahiro Yamada #endif
26909b9d9e5SMasahiro Yamada 
27009b9d9e5SMasahiro Yamada 	return abort;
27166ded17dSSimon Glass }
27266ded17dSSimon Glass 
27366ded17dSSimon Glass static void process_fdt_options(const void *blob)
27466ded17dSSimon Glass {
2759f73690cSStefan Roese #if defined(CONFIG_OF_CONTROL) && defined(CONFIG_SYS_TEXT_BASE)
27666ded17dSSimon Glass 	ulong addr;
27766ded17dSSimon Glass 
27866ded17dSSimon Glass 	/* Add an env variable to point to a kernel payload, if available */
27966ded17dSSimon Glass 	addr = fdtdec_get_config_int(gd->fdt_blob, "kernel-offset", 0);
28066ded17dSSimon Glass 	if (addr)
281*018f5303SSimon Glass 		env_set_addr("kernaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr));
28266ded17dSSimon Glass 
28366ded17dSSimon Glass 	/* Add an env variable to point to a root disk, if available */
28466ded17dSSimon Glass 	addr = fdtdec_get_config_int(gd->fdt_blob, "rootdisk-offset", 0);
28566ded17dSSimon Glass 	if (addr)
286*018f5303SSimon Glass 		env_set_addr("rootaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr));
2879f73690cSStefan Roese #endif /* CONFIG_OF_CONTROL && CONFIG_SYS_TEXT_BASE */
288affb2156SSimon Glass }
28966ded17dSSimon Glass 
290affb2156SSimon Glass const char *bootdelay_process(void)
29166ded17dSSimon Glass {
29266ded17dSSimon Glass 	char *s;
29366ded17dSSimon Glass 	int bootdelay;
29466ded17dSSimon Glass #ifdef CONFIG_BOOTCOUNT_LIMIT
29566ded17dSSimon Glass 	unsigned long bootcount = 0;
29666ded17dSSimon Glass 	unsigned long bootlimit = 0;
29766ded17dSSimon Glass #endif /* CONFIG_BOOTCOUNT_LIMIT */
29866ded17dSSimon Glass 
29966ded17dSSimon Glass #ifdef CONFIG_BOOTCOUNT_LIMIT
30066ded17dSSimon Glass 	bootcount = bootcount_load();
30166ded17dSSimon Glass 	bootcount++;
30266ded17dSSimon Glass 	bootcount_store(bootcount);
303*018f5303SSimon Glass 	env_set_ulong("bootcount", bootcount);
30466ded17dSSimon Glass 	bootlimit = getenv_ulong("bootlimit", 10, 0);
30566ded17dSSimon Glass #endif /* CONFIG_BOOTCOUNT_LIMIT */
30666ded17dSSimon Glass 
30766ded17dSSimon Glass 	s = getenv("bootdelay");
30866ded17dSSimon Glass 	bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
30966ded17dSSimon Glass 
31066ded17dSSimon Glass #ifdef CONFIG_OF_CONTROL
31166ded17dSSimon Glass 	bootdelay = fdtdec_get_config_int(gd->fdt_blob, "bootdelay",
31266ded17dSSimon Glass 			bootdelay);
31366ded17dSSimon Glass #endif
31466ded17dSSimon Glass 
31566ded17dSSimon Glass 	debug("### main_loop entered: bootdelay=%d\n\n", bootdelay);
31666ded17dSSimon Glass 
31766ded17dSSimon Glass #if defined(CONFIG_MENU_SHOW)
31866ded17dSSimon Glass 	bootdelay = menu_show(bootdelay);
31966ded17dSSimon Glass #endif
320b26440f1SSimon Glass 	bootretry_init_cmd_timeout();
32166ded17dSSimon Glass 
32266ded17dSSimon Glass #ifdef CONFIG_POST
32366ded17dSSimon Glass 	if (gd->flags & GD_FLG_POSTFAIL) {
32466ded17dSSimon Glass 		s = getenv("failbootcmd");
32566ded17dSSimon Glass 	} else
32666ded17dSSimon Glass #endif /* CONFIG_POST */
32766ded17dSSimon Glass #ifdef CONFIG_BOOTCOUNT_LIMIT
32866ded17dSSimon Glass 	if (bootlimit && (bootcount > bootlimit)) {
32966ded17dSSimon Glass 		printf("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",
33066ded17dSSimon Glass 		       (unsigned)bootlimit);
33166ded17dSSimon Glass 		s = getenv("altbootcmd");
33266ded17dSSimon Glass 	} else
33366ded17dSSimon Glass #endif /* CONFIG_BOOTCOUNT_LIMIT */
33466ded17dSSimon Glass 		s = getenv("bootcmd");
33566ded17dSSimon Glass 
33666ded17dSSimon Glass 	process_fdt_options(gd->fdt_blob);
337affb2156SSimon Glass 	stored_bootdelay = bootdelay;
33866ded17dSSimon Glass 
339affb2156SSimon Glass 	return s;
340affb2156SSimon Glass }
34166ded17dSSimon Glass 
342affb2156SSimon Glass void autoboot_command(const char *s)
343affb2156SSimon Glass {
34466ded17dSSimon Glass 	debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
34566ded17dSSimon Glass 
346affb2156SSimon Glass 	if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {
34766ded17dSSimon Glass #if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
34866ded17dSSimon Glass 		int prev = disable_ctrlc(1);	/* disable Control C checking */
34966ded17dSSimon Glass #endif
35066ded17dSSimon Glass 
35166ded17dSSimon Glass 		run_command_list(s, -1, 0);
35266ded17dSSimon Glass 
35366ded17dSSimon Glass #if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
35466ded17dSSimon Glass 		disable_ctrlc(prev);	/* restore Control C checking */
35566ded17dSSimon Glass #endif
35666ded17dSSimon Glass 	}
35766ded17dSSimon Glass 
35866ded17dSSimon Glass #ifdef CONFIG_MENUKEY
35966ded17dSSimon Glass 	if (menukey == CONFIG_MENUKEY) {
36066ded17dSSimon Glass 		s = getenv("menucmd");
36166ded17dSSimon Glass 		if (s)
36266ded17dSSimon Glass 			run_command_list(s, -1, 0);
36366ded17dSSimon Glass 	}
36466ded17dSSimon Glass #endif /* CONFIG_MENUKEY */
36566ded17dSSimon Glass }
366