1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * (C) Copyright 2000
3*4882a593Smuzhiyun * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * SPDX-License-Identifier: GPL-2.0+
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include <common.h>
9*4882a593Smuzhiyun #include <autoboot.h>
10*4882a593Smuzhiyun #include <bootretry.h>
11*4882a593Smuzhiyun #include <cli.h>
12*4882a593Smuzhiyun #include <console.h>
13*4882a593Smuzhiyun #include <fdtdec.h>
14*4882a593Smuzhiyun #include <menu.h>
15*4882a593Smuzhiyun #include <post.h>
16*4882a593Smuzhiyun #include <u-boot/sha256.h>
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun DECLARE_GLOBAL_DATA_PTR;
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun #define MAX_DELAY_STOP_STR 32
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun #ifndef DEBUG_BOOTKEYS
23*4882a593Smuzhiyun #define DEBUG_BOOTKEYS 0
24*4882a593Smuzhiyun #endif
25*4882a593Smuzhiyun #define debug_bootkeys(fmt, args...) \
26*4882a593Smuzhiyun debug_cond(DEBUG_BOOTKEYS, fmt, ##args)
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun /* Stored value of bootdelay, used by autoboot_command() */
29*4882a593Smuzhiyun static int stored_bootdelay;
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun #if defined(CONFIG_AUTOBOOT_KEYED)
32*4882a593Smuzhiyun #if defined(CONFIG_AUTOBOOT_STOP_STR_SHA256)
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun /*
35*4882a593Smuzhiyun * Use a "constant-length" time compare function for this
36*4882a593Smuzhiyun * hash compare:
37*4882a593Smuzhiyun *
38*4882a593Smuzhiyun * https://crackstation.net/hashing-security.htm
39*4882a593Smuzhiyun */
slow_equals(u8 * a,u8 * b,int len)40*4882a593Smuzhiyun static int slow_equals(u8 *a, u8 *b, int len)
41*4882a593Smuzhiyun {
42*4882a593Smuzhiyun int diff = 0;
43*4882a593Smuzhiyun int i;
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun for (i = 0; i < len; i++)
46*4882a593Smuzhiyun diff |= a[i] ^ b[i];
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun return diff == 0;
49*4882a593Smuzhiyun }
50*4882a593Smuzhiyun
passwd_abort(uint64_t etime)51*4882a593Smuzhiyun static int passwd_abort(uint64_t etime)
52*4882a593Smuzhiyun {
53*4882a593Smuzhiyun const char *sha_env_str = env_get("bootstopkeysha256");
54*4882a593Smuzhiyun u8 sha_env[SHA256_SUM_LEN];
55*4882a593Smuzhiyun u8 sha[SHA256_SUM_LEN];
56*4882a593Smuzhiyun char presskey[MAX_DELAY_STOP_STR];
57*4882a593Smuzhiyun const char *algo_name = "sha256";
58*4882a593Smuzhiyun u_int presskey_len = 0;
59*4882a593Smuzhiyun int abort = 0;
60*4882a593Smuzhiyun int size;
61*4882a593Smuzhiyun int ret;
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun if (sha_env_str == NULL)
64*4882a593Smuzhiyun sha_env_str = CONFIG_AUTOBOOT_STOP_STR_SHA256;
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun /*
67*4882a593Smuzhiyun * Generate the binary value from the environment hash value
68*4882a593Smuzhiyun * so that we can compare this value with the computed hash
69*4882a593Smuzhiyun * from the user input
70*4882a593Smuzhiyun */
71*4882a593Smuzhiyun ret = hash_parse_string(algo_name, sha_env_str, sha_env);
72*4882a593Smuzhiyun if (ret) {
73*4882a593Smuzhiyun printf("Hash %s not supported!\n", algo_name);
74*4882a593Smuzhiyun return 0;
75*4882a593Smuzhiyun }
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun /*
78*4882a593Smuzhiyun * We don't know how long the stop-string is, so we need to
79*4882a593Smuzhiyun * generate the sha256 hash upon each input character and
80*4882a593Smuzhiyun * compare the value with the one saved in the environment
81*4882a593Smuzhiyun */
82*4882a593Smuzhiyun do {
83*4882a593Smuzhiyun if (tstc()) {
84*4882a593Smuzhiyun /* Check for input string overflow */
85*4882a593Smuzhiyun if (presskey_len >= MAX_DELAY_STOP_STR)
86*4882a593Smuzhiyun return 0;
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun presskey[presskey_len++] = getc();
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun /* Calculate sha256 upon each new char */
91*4882a593Smuzhiyun hash_block(algo_name, (const void *)presskey,
92*4882a593Smuzhiyun presskey_len, sha, &size);
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun /* And check if sha matches saved value in env */
95*4882a593Smuzhiyun if (slow_equals(sha, sha_env, SHA256_SUM_LEN))
96*4882a593Smuzhiyun abort = 1;
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun } while (!abort && get_ticks() <= etime);
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun return abort;
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun #else
passwd_abort(uint64_t etime)103*4882a593Smuzhiyun static int passwd_abort(uint64_t etime)
104*4882a593Smuzhiyun {
105*4882a593Smuzhiyun int abort = 0;
106*4882a593Smuzhiyun struct {
107*4882a593Smuzhiyun char *str;
108*4882a593Smuzhiyun u_int len;
109*4882a593Smuzhiyun int retry;
110*4882a593Smuzhiyun }
111*4882a593Smuzhiyun delaykey[] = {
112*4882a593Smuzhiyun { .str = env_get("bootdelaykey"), .retry = 1 },
113*4882a593Smuzhiyun { .str = env_get("bootstopkey"), .retry = 0 },
114*4882a593Smuzhiyun };
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun char presskey[MAX_DELAY_STOP_STR];
117*4882a593Smuzhiyun u_int presskey_len = 0;
118*4882a593Smuzhiyun u_int presskey_max = 0;
119*4882a593Smuzhiyun u_int i;
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun # ifdef CONFIG_AUTOBOOT_DELAY_STR
122*4882a593Smuzhiyun if (delaykey[0].str == NULL)
123*4882a593Smuzhiyun delaykey[0].str = CONFIG_AUTOBOOT_DELAY_STR;
124*4882a593Smuzhiyun # endif
125*4882a593Smuzhiyun # ifdef CONFIG_AUTOBOOT_STOP_STR
126*4882a593Smuzhiyun if (delaykey[1].str == NULL)
127*4882a593Smuzhiyun delaykey[1].str = CONFIG_AUTOBOOT_STOP_STR;
128*4882a593Smuzhiyun # endif
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i++) {
131*4882a593Smuzhiyun delaykey[i].len = delaykey[i].str == NULL ?
132*4882a593Smuzhiyun 0 : strlen(delaykey[i].str);
133*4882a593Smuzhiyun delaykey[i].len = delaykey[i].len > MAX_DELAY_STOP_STR ?
134*4882a593Smuzhiyun MAX_DELAY_STOP_STR : delaykey[i].len;
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun presskey_max = presskey_max > delaykey[i].len ?
137*4882a593Smuzhiyun presskey_max : delaykey[i].len;
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun debug_bootkeys("%s key:<%s>\n",
140*4882a593Smuzhiyun delaykey[i].retry ? "delay" : "stop",
141*4882a593Smuzhiyun delaykey[i].str ? delaykey[i].str : "NULL");
142*4882a593Smuzhiyun }
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun /* In order to keep up with incoming data, check timeout only
145*4882a593Smuzhiyun * when catch up.
146*4882a593Smuzhiyun */
147*4882a593Smuzhiyun do {
148*4882a593Smuzhiyun if (tstc()) {
149*4882a593Smuzhiyun if (presskey_len < presskey_max) {
150*4882a593Smuzhiyun presskey[presskey_len++] = getc();
151*4882a593Smuzhiyun } else {
152*4882a593Smuzhiyun for (i = 0; i < presskey_max - 1; i++)
153*4882a593Smuzhiyun presskey[i] = presskey[i + 1];
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun presskey[i] = getc();
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i++) {
160*4882a593Smuzhiyun if (delaykey[i].len > 0 &&
161*4882a593Smuzhiyun presskey_len >= delaykey[i].len &&
162*4882a593Smuzhiyun memcmp(presskey + presskey_len -
163*4882a593Smuzhiyun delaykey[i].len, delaykey[i].str,
164*4882a593Smuzhiyun delaykey[i].len) == 0) {
165*4882a593Smuzhiyun debug_bootkeys("got %skey\n",
166*4882a593Smuzhiyun delaykey[i].retry ? "delay" :
167*4882a593Smuzhiyun "stop");
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun /* don't retry auto boot */
170*4882a593Smuzhiyun if (!delaykey[i].retry)
171*4882a593Smuzhiyun bootretry_dont_retry();
172*4882a593Smuzhiyun abort = 1;
173*4882a593Smuzhiyun }
174*4882a593Smuzhiyun }
175*4882a593Smuzhiyun } while (!abort && get_ticks() <= etime);
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun return abort;
178*4882a593Smuzhiyun }
179*4882a593Smuzhiyun #endif
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun /***************************************************************************
182*4882a593Smuzhiyun * Watch for 'delay' seconds for autoboot stop or autoboot delay string.
183*4882a593Smuzhiyun * returns: 0 - no key string, allow autoboot 1 - got key string, abort
184*4882a593Smuzhiyun */
__abortboot(int bootdelay)185*4882a593Smuzhiyun static int __abortboot(int bootdelay)
186*4882a593Smuzhiyun {
187*4882a593Smuzhiyun int abort;
188*4882a593Smuzhiyun uint64_t etime = endtick(bootdelay);
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun # ifdef CONFIG_AUTOBOOT_PROMPT
191*4882a593Smuzhiyun /*
192*4882a593Smuzhiyun * CONFIG_AUTOBOOT_PROMPT includes the %d for all boards.
193*4882a593Smuzhiyun * To print the bootdelay value upon bootup.
194*4882a593Smuzhiyun */
195*4882a593Smuzhiyun printf(CONFIG_AUTOBOOT_PROMPT, bootdelay);
196*4882a593Smuzhiyun # endif
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun abort = passwd_abort(etime);
199*4882a593Smuzhiyun if (!abort)
200*4882a593Smuzhiyun debug_bootkeys("key timeout\n");
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun return abort;
203*4882a593Smuzhiyun }
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun # else /* !defined(CONFIG_AUTOBOOT_KEYED) */
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun #ifdef CONFIG_MENUKEY
208*4882a593Smuzhiyun static int menukey;
209*4882a593Smuzhiyun #endif
210*4882a593Smuzhiyun
__abortboot(int bootdelay)211*4882a593Smuzhiyun static int __abortboot(int bootdelay)
212*4882a593Smuzhiyun {
213*4882a593Smuzhiyun int abort = 0;
214*4882a593Smuzhiyun unsigned long ts;
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun #ifdef CONFIG_MENUPROMPT
217*4882a593Smuzhiyun printf(CONFIG_MENUPROMPT);
218*4882a593Smuzhiyun #else
219*4882a593Smuzhiyun printf("Hit key to stop autoboot('CTRL+C'): %2d ", bootdelay);
220*4882a593Smuzhiyun #endif
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun #ifdef CONFIG_ARCH_ROCKCHIP
223*4882a593Smuzhiyun if (!IS_ENABLED(CONFIG_CONSOLE_DISABLE_CLI) && ctrlc()) { /* we press ctrl+c ? */
224*4882a593Smuzhiyun #else
225*4882a593Smuzhiyun /*
226*4882a593Smuzhiyun * Check if key already pressed
227*4882a593Smuzhiyun */
228*4882a593Smuzhiyun if (tstc()) { /* we got a key press */
229*4882a593Smuzhiyun #endif
230*4882a593Smuzhiyun (void) getc(); /* consume input */
231*4882a593Smuzhiyun puts("\b\b\b 0");
232*4882a593Smuzhiyun abort = 1; /* don't auto boot */
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun while ((bootdelay > 0) && (!abort)) {
236*4882a593Smuzhiyun --bootdelay;
237*4882a593Smuzhiyun /* delay 1000 ms */
238*4882a593Smuzhiyun ts = get_timer(0);
239*4882a593Smuzhiyun do {
240*4882a593Smuzhiyun if (ctrlc()) { /* we got a ctrl+c key press */
241*4882a593Smuzhiyun abort = 1; /* don't auto boot */
242*4882a593Smuzhiyun bootdelay = 0; /* no more delay */
243*4882a593Smuzhiyun # ifdef CONFIG_MENUKEY
244*4882a593Smuzhiyun menukey = 0x03; /* ctrl+c key code */
245*4882a593Smuzhiyun # endif
246*4882a593Smuzhiyun break;
247*4882a593Smuzhiyun }
248*4882a593Smuzhiyun udelay(10000);
249*4882a593Smuzhiyun } while (!abort && get_timer(ts) < 1000);
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun printf("\b\b\b%2d ", bootdelay);
252*4882a593Smuzhiyun }
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun putc('\n');
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun return abort;
257*4882a593Smuzhiyun }
258*4882a593Smuzhiyun # endif /* CONFIG_AUTOBOOT_KEYED */
259*4882a593Smuzhiyun
260*4882a593Smuzhiyun static int abortboot(int bootdelay)
261*4882a593Smuzhiyun {
262*4882a593Smuzhiyun int abort = 0;
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun if (bootdelay >= 0)
265*4882a593Smuzhiyun abort = __abortboot(bootdelay);
266*4882a593Smuzhiyun
267*4882a593Smuzhiyun #ifdef CONFIG_SILENT_CONSOLE
268*4882a593Smuzhiyun if (abort)
269*4882a593Smuzhiyun gd->flags &= ~GD_FLG_SILENT;
270*4882a593Smuzhiyun #endif
271*4882a593Smuzhiyun
272*4882a593Smuzhiyun return abort;
273*4882a593Smuzhiyun }
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun static void process_fdt_options(const void *blob)
276*4882a593Smuzhiyun {
277*4882a593Smuzhiyun #if defined(CONFIG_OF_CONTROL) && defined(CONFIG_SYS_TEXT_BASE)
278*4882a593Smuzhiyun ulong addr;
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun /* Add an env variable to point to a kernel payload, if available */
281*4882a593Smuzhiyun addr = fdtdec_get_config_int(gd->fdt_blob, "kernel-offset", 0);
282*4882a593Smuzhiyun if (addr)
283*4882a593Smuzhiyun env_set_addr("kernaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr));
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun /* Add an env variable to point to a root disk, if available */
286*4882a593Smuzhiyun addr = fdtdec_get_config_int(gd->fdt_blob, "rootdisk-offset", 0);
287*4882a593Smuzhiyun if (addr)
288*4882a593Smuzhiyun env_set_addr("rootaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr));
289*4882a593Smuzhiyun #endif /* CONFIG_OF_CONTROL && CONFIG_SYS_TEXT_BASE */
290*4882a593Smuzhiyun }
291*4882a593Smuzhiyun
292*4882a593Smuzhiyun const char *bootdelay_process(void)
293*4882a593Smuzhiyun {
294*4882a593Smuzhiyun char *s;
295*4882a593Smuzhiyun int bootdelay;
296*4882a593Smuzhiyun #ifdef CONFIG_BOOTCOUNT_LIMIT
297*4882a593Smuzhiyun unsigned long bootcount = 0;
298*4882a593Smuzhiyun unsigned long bootlimit = 0;
299*4882a593Smuzhiyun #endif /* CONFIG_BOOTCOUNT_LIMIT */
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun #ifdef CONFIG_BOOTCOUNT_LIMIT
302*4882a593Smuzhiyun bootcount = bootcount_load();
303*4882a593Smuzhiyun bootcount++;
304*4882a593Smuzhiyun bootcount_store(bootcount);
305*4882a593Smuzhiyun env_set_ulong("bootcount", bootcount);
306*4882a593Smuzhiyun bootlimit = env_get_ulong("bootlimit", 10, 0);
307*4882a593Smuzhiyun #endif /* CONFIG_BOOTCOUNT_LIMIT */
308*4882a593Smuzhiyun
309*4882a593Smuzhiyun s = env_get("bootdelay");
310*4882a593Smuzhiyun bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
311*4882a593Smuzhiyun
312*4882a593Smuzhiyun #ifdef CONFIG_OF_CONTROL
313*4882a593Smuzhiyun bootdelay = fdtdec_get_config_int(gd->fdt_blob, "bootdelay",
314*4882a593Smuzhiyun bootdelay);
315*4882a593Smuzhiyun #endif
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun debug("### main_loop entered: bootdelay=%d\n\n", bootdelay);
318*4882a593Smuzhiyun
319*4882a593Smuzhiyun #if defined(CONFIG_MENU_SHOW)
320*4882a593Smuzhiyun bootdelay = menu_show(bootdelay);
321*4882a593Smuzhiyun #endif
322*4882a593Smuzhiyun bootretry_init_cmd_timeout();
323*4882a593Smuzhiyun
324*4882a593Smuzhiyun #ifdef CONFIG_POST
325*4882a593Smuzhiyun if (gd->flags & GD_FLG_POSTFAIL) {
326*4882a593Smuzhiyun s = env_get("failbootcmd");
327*4882a593Smuzhiyun } else
328*4882a593Smuzhiyun #endif /* CONFIG_POST */
329*4882a593Smuzhiyun #ifdef CONFIG_BOOTCOUNT_LIMIT
330*4882a593Smuzhiyun if (bootlimit && (bootcount > bootlimit)) {
331*4882a593Smuzhiyun printf("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",
332*4882a593Smuzhiyun (unsigned)bootlimit);
333*4882a593Smuzhiyun s = env_get("altbootcmd");
334*4882a593Smuzhiyun } else
335*4882a593Smuzhiyun #endif /* CONFIG_BOOTCOUNT_LIMIT */
336*4882a593Smuzhiyun s = env_get("bootcmd");
337*4882a593Smuzhiyun
338*4882a593Smuzhiyun process_fdt_options(gd->fdt_blob);
339*4882a593Smuzhiyun stored_bootdelay = bootdelay;
340*4882a593Smuzhiyun
341*4882a593Smuzhiyun return s;
342*4882a593Smuzhiyun }
343*4882a593Smuzhiyun
344*4882a593Smuzhiyun /*
345*4882a593Smuzhiyun * Board-specific Platform code can reimplement autoboot_command_fail_handle ()
346*4882a593Smuzhiyun * if needed
347*4882a593Smuzhiyun */
348*4882a593Smuzhiyun __weak void autoboot_command_fail_handle(void) {}
349*4882a593Smuzhiyun
350*4882a593Smuzhiyun void autoboot_command(const char *s)
351*4882a593Smuzhiyun {
352*4882a593Smuzhiyun debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
353*4882a593Smuzhiyun
354*4882a593Smuzhiyun if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {
355*4882a593Smuzhiyun #if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
356*4882a593Smuzhiyun int prev = disable_ctrlc(1); /* disable Control C checking */
357*4882a593Smuzhiyun #endif
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun run_command_list(s, -1, 0);
360*4882a593Smuzhiyun autoboot_command_fail_handle();
361*4882a593Smuzhiyun
362*4882a593Smuzhiyun #if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
363*4882a593Smuzhiyun disable_ctrlc(prev); /* restore Control C checking */
364*4882a593Smuzhiyun #endif
365*4882a593Smuzhiyun }
366*4882a593Smuzhiyun
367*4882a593Smuzhiyun #ifdef CONFIG_MENUKEY
368*4882a593Smuzhiyun if (menukey == CONFIG_MENUKEY) {
369*4882a593Smuzhiyun s = env_get("menucmd");
370*4882a593Smuzhiyun if (s)
371*4882a593Smuzhiyun run_command_list(s, -1, 0);
372*4882a593Smuzhiyun }
373*4882a593Smuzhiyun #endif /* CONFIG_MENUKEY */
374*4882a593Smuzhiyun }
375