xref: /openbmc/u-boot/common/autoboot.c (revision 9a66328a)
183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
266ded17dSSimon Glass /*
366ded17dSSimon Glass  * (C) Copyright 2000
466ded17dSSimon Glass  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
566ded17dSSimon Glass  */
666ded17dSSimon Glass 
766ded17dSSimon Glass #include <common.h>
839e1230eSJeroen Hofstee #include <autoboot.h>
90098e179SSimon Glass #include <bootretry.h>
1066ded17dSSimon Glass #include <cli.h>
1124b852a7SSimon Glass #include <console.h>
1266ded17dSSimon Glass #include <fdtdec.h>
1366ded17dSSimon Glass #include <menu.h>
1466ded17dSSimon Glass #include <post.h>
158f0b1e24SStefan Roese #include <u-boot/sha256.h>
16*bc8c440fSLukasz Majewski #include <bootcount.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  */
slow_equals(u8 * a,u8 * b,int len)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 
passwd_abort(uint64_t etime)518f0b1e24SStefan Roese static int passwd_abort(uint64_t etime)
528f0b1e24SStefan Roese {
5300caae6dSSimon Glass 	const char *sha_env_str = env_get("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;
602d06fd83SMartin Etnestad 	int size = sizeof(sha);
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
passwd_abort(uint64_t etime)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[] = {
11200caae6dSSimon Glass 		{ .str = env_get("bootdelaykey"),  .retry = 1 },
11300caae6dSSimon Glass 		{ .str = env_get("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  */
__abortboot(int bootdelay)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 
__abortboot(int bootdelay)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 
abortboot(int bootdelay)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 
process_fdt_options(const void * blob)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)
281018f5303SSimon 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)
286018f5303SSimon 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 
bootdelay_process(void)290affb2156SSimon Glass const char *bootdelay_process(void)
29166ded17dSSimon Glass {
29266ded17dSSimon Glass 	char *s;
29366ded17dSSimon Glass 	int bootdelay;
29466ded17dSSimon Glass 
295*bc8c440fSLukasz Majewski 	bootcount_inc();
29666ded17dSSimon Glass 
29700caae6dSSimon Glass 	s = env_get("bootdelay");
29866ded17dSSimon Glass 	bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
29966ded17dSSimon Glass 
30066ded17dSSimon Glass #ifdef CONFIG_OF_CONTROL
30166ded17dSSimon Glass 	bootdelay = fdtdec_get_config_int(gd->fdt_blob, "bootdelay",
30266ded17dSSimon Glass 			bootdelay);
30366ded17dSSimon Glass #endif
30466ded17dSSimon Glass 
30566ded17dSSimon Glass 	debug("### main_loop entered: bootdelay=%d\n\n", bootdelay);
30666ded17dSSimon Glass 
30766ded17dSSimon Glass #if defined(CONFIG_MENU_SHOW)
30866ded17dSSimon Glass 	bootdelay = menu_show(bootdelay);
30966ded17dSSimon Glass #endif
310b26440f1SSimon Glass 	bootretry_init_cmd_timeout();
31166ded17dSSimon Glass 
31266ded17dSSimon Glass #ifdef CONFIG_POST
31366ded17dSSimon Glass 	if (gd->flags & GD_FLG_POSTFAIL) {
31400caae6dSSimon Glass 		s = env_get("failbootcmd");
31566ded17dSSimon Glass 	} else
31666ded17dSSimon Glass #endif /* CONFIG_POST */
317*bc8c440fSLukasz Majewski 	if (bootcount_error())
31800caae6dSSimon Glass 		s = env_get("altbootcmd");
319*bc8c440fSLukasz Majewski 	else
32000caae6dSSimon Glass 		s = env_get("bootcmd");
32166ded17dSSimon Glass 
32266ded17dSSimon Glass 	process_fdt_options(gd->fdt_blob);
323affb2156SSimon Glass 	stored_bootdelay = bootdelay;
32466ded17dSSimon Glass 
325affb2156SSimon Glass 	return s;
326affb2156SSimon Glass }
32766ded17dSSimon Glass 
autoboot_command(const char * s)328affb2156SSimon Glass void autoboot_command(const char *s)
329affb2156SSimon Glass {
33066ded17dSSimon Glass 	debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
33166ded17dSSimon Glass 
332affb2156SSimon Glass 	if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {
33366ded17dSSimon Glass #if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
33466ded17dSSimon Glass 		int prev = disable_ctrlc(1);	/* disable Control C checking */
33566ded17dSSimon Glass #endif
33666ded17dSSimon Glass 
33766ded17dSSimon Glass 		run_command_list(s, -1, 0);
33866ded17dSSimon Glass 
33966ded17dSSimon Glass #if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
34066ded17dSSimon Glass 		disable_ctrlc(prev);	/* restore Control C checking */
34166ded17dSSimon Glass #endif
34266ded17dSSimon Glass 	}
34366ded17dSSimon Glass 
34466ded17dSSimon Glass #ifdef CONFIG_MENUKEY
34566ded17dSSimon Glass 	if (menukey == CONFIG_MENUKEY) {
34600caae6dSSimon Glass 		s = env_get("menucmd");
34766ded17dSSimon Glass 		if (s)
34866ded17dSSimon Glass 			run_command_list(s, -1, 0);
34966ded17dSSimon Glass 	}
35066ded17dSSimon Glass #endif /* CONFIG_MENUKEY */
35166ded17dSSimon Glass }
352