]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - common/autoboot.c
Move autoboot code to autoboot.c
[karo-tx-uboot.git] / common / autoboot.c
1 /*
2  * (C) Copyright 2000
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * SPDX-License-Identifier:     GPL-2.0+
6  */
7
8 #include <common.h>
9 #include <cli.h>
10 #include <fdtdec.h>
11 #include <menu.h>
12 #include <post.h>
13
14 DECLARE_GLOBAL_DATA_PTR;
15
16 #define MAX_DELAY_STOP_STR 32
17
18 #ifndef DEBUG_BOOTKEYS
19 #define DEBUG_BOOTKEYS 0
20 #endif
21 #define debug_bootkeys(fmt, args...)            \
22         debug_cond(DEBUG_BOOTKEYS, fmt, ##args)
23
24 /***************************************************************************
25  * Watch for 'delay' seconds for autoboot stop or autoboot delay string.
26  * returns: 0 -  no key string, allow autoboot 1 - got key string, abort
27  */
28 # if defined(CONFIG_AUTOBOOT_KEYED)
29 static int abortboot_keyed(int bootdelay)
30 {
31         int abort = 0;
32         uint64_t etime = endtick(bootdelay);
33         struct {
34                 char *str;
35                 u_int len;
36                 int retry;
37         }
38         delaykey[] = {
39                 { str: getenv("bootdelaykey"),  retry: 1 },
40                 { str: getenv("bootdelaykey2"), retry: 1 },
41                 { str: getenv("bootstopkey"),   retry: 0 },
42                 { str: getenv("bootstopkey2"),  retry: 0 },
43         };
44
45         char presskey[MAX_DELAY_STOP_STR];
46         u_int presskey_len = 0;
47         u_int presskey_max = 0;
48         u_int i;
49
50 #ifndef CONFIG_ZERO_BOOTDELAY_CHECK
51         if (bootdelay == 0)
52                 return 0;
53 #endif
54
55 #  ifdef CONFIG_AUTOBOOT_PROMPT
56         printf(CONFIG_AUTOBOOT_PROMPT);
57 #  endif
58
59 #  ifdef CONFIG_AUTOBOOT_DELAY_STR
60         if (delaykey[0].str == NULL)
61                 delaykey[0].str = CONFIG_AUTOBOOT_DELAY_STR;
62 #  endif
63 #  ifdef CONFIG_AUTOBOOT_DELAY_STR2
64         if (delaykey[1].str == NULL)
65                 delaykey[1].str = CONFIG_AUTOBOOT_DELAY_STR2;
66 #  endif
67 #  ifdef CONFIG_AUTOBOOT_STOP_STR
68         if (delaykey[2].str == NULL)
69                 delaykey[2].str = CONFIG_AUTOBOOT_STOP_STR;
70 #  endif
71 #  ifdef CONFIG_AUTOBOOT_STOP_STR2
72         if (delaykey[3].str == NULL)
73                 delaykey[3].str = CONFIG_AUTOBOOT_STOP_STR2;
74 #  endif
75
76         for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i++) {
77                 delaykey[i].len = delaykey[i].str == NULL ?
78                                     0 : strlen(delaykey[i].str);
79                 delaykey[i].len = delaykey[i].len > MAX_DELAY_STOP_STR ?
80                                     MAX_DELAY_STOP_STR : delaykey[i].len;
81
82                 presskey_max = presskey_max > delaykey[i].len ?
83                                     presskey_max : delaykey[i].len;
84
85                 debug_bootkeys("%s key:<%s>\n",
86                                delaykey[i].retry ? "delay" : "stop",
87                                delaykey[i].str ? delaykey[i].str : "NULL");
88         }
89
90         /* In order to keep up with incoming data, check timeout only
91          * when catch up.
92          */
93         do {
94                 if (tstc()) {
95                         if (presskey_len < presskey_max) {
96                                 presskey[presskey_len++] = getc();
97                         } else {
98                                 for (i = 0; i < presskey_max - 1; i++)
99                                         presskey[i] = presskey[i + 1];
100
101                                 presskey[i] = getc();
102                         }
103                 }
104
105                 for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i++) {
106                         if (delaykey[i].len > 0 &&
107                             presskey_len >= delaykey[i].len &&
108                                 memcmp(presskey + presskey_len -
109                                         delaykey[i].len, delaykey[i].str,
110                                         delaykey[i].len) == 0) {
111                                         debug_bootkeys("got %skey\n",
112                                                 delaykey[i].retry ? "delay" :
113                                                 "stop");
114
115 #  ifdef CONFIG_BOOT_RETRY_TIME
116                                 /* don't retry auto boot */
117                                 if (!delaykey[i].retry)
118                                         bootretry_dont_retry();
119 #  endif
120                                 abort = 1;
121                         }
122                 }
123         } while (!abort && get_ticks() <= etime);
124
125         if (!abort)
126                 debug_bootkeys("key timeout\n");
127
128 #ifdef CONFIG_SILENT_CONSOLE
129         if (abort)
130                 gd->flags &= ~GD_FLG_SILENT;
131 #endif
132
133         return abort;
134 }
135
136 # else  /* !defined(CONFIG_AUTOBOOT_KEYED) */
137
138 #ifdef CONFIG_MENUKEY
139 static int menukey;
140 #endif
141
142 static int abortboot_normal(int bootdelay)
143 {
144         int abort = 0;
145         unsigned long ts;
146
147 #ifdef CONFIG_MENUPROMPT
148         printf(CONFIG_MENUPROMPT);
149 #else
150         if (bootdelay >= 0)
151                 printf("Hit any key to stop autoboot: %2d ", bootdelay);
152 #endif
153
154 #if defined CONFIG_ZERO_BOOTDELAY_CHECK
155         /*
156          * Check if key already pressed
157          * Don't check if bootdelay < 0
158          */
159         if (bootdelay >= 0) {
160                 if (tstc()) {   /* we got a key press   */
161                         (void) getc();  /* consume input        */
162                         puts("\b\b\b 0");
163                         abort = 1;      /* don't auto boot      */
164                 }
165         }
166 #endif
167
168         while ((bootdelay > 0) && (!abort)) {
169                 --bootdelay;
170                 /* delay 1000 ms */
171                 ts = get_timer(0);
172                 do {
173                         if (tstc()) {   /* we got a key press   */
174                                 abort  = 1;     /* don't auto boot      */
175                                 bootdelay = 0;  /* no more delay        */
176 # ifdef CONFIG_MENUKEY
177                                 menukey = getc();
178 # else
179                                 (void) getc();  /* consume input        */
180 # endif
181                                 break;
182                         }
183                         udelay(10000);
184                 } while (!abort && get_timer(ts) < 1000);
185
186                 printf("\b\b\b%2d ", bootdelay);
187         }
188
189         putc('\n');
190
191 #ifdef CONFIG_SILENT_CONSOLE
192         if (abort)
193                 gd->flags &= ~GD_FLG_SILENT;
194 #endif
195
196         return abort;
197 }
198 # endif /* CONFIG_AUTOBOOT_KEYED */
199
200 static int abortboot(int bootdelay)
201 {
202 #ifdef CONFIG_AUTOBOOT_KEYED
203         return abortboot_keyed(bootdelay);
204 #else
205         return abortboot_normal(bootdelay);
206 #endif
207 }
208
209 /*
210  * Runs the given boot command securely.  Specifically:
211  * - Doesn't run the command with the shell (run_command or parse_string_outer),
212  *   since that's a lot of code surface that an attacker might exploit.
213  *   Because of this, we don't do any argument parsing--the secure boot command
214  *   has to be a full-fledged u-boot command.
215  * - Doesn't check for keypresses before booting, since that could be a
216  *   security hole; also disables Ctrl-C.
217  * - Doesn't allow the command to return.
218  *
219  * Upon any failures, this function will drop into an infinite loop after
220  * printing the error message to console.
221  */
222
223 #if defined(CONFIG_OF_CONTROL)
224 static void secure_boot_cmd(char *cmd)
225 {
226         cmd_tbl_t *cmdtp;
227         int rc;
228
229         if (!cmd) {
230                 printf("## Error: Secure boot command not specified\n");
231                 goto err;
232         }
233
234         /* Disable Ctrl-C just in case some command is used that checks it. */
235         disable_ctrlc(1);
236
237         /* Find the command directly. */
238         cmdtp = find_cmd(cmd);
239         if (!cmdtp) {
240                 printf("## Error: \"%s\" not defined\n", cmd);
241                 goto err;
242         }
243
244         /* Run the command, forcing no flags and faking argc and argv. */
245         rc = (cmdtp->cmd)(cmdtp, 0, 1, &cmd);
246
247         /* Shouldn't ever return from boot command. */
248         printf("## Error: \"%s\" returned (code %d)\n", cmd, rc);
249
250 err:
251         /*
252          * Not a whole lot to do here.  Rebooting won't help much, since we'll
253          * just end up right back here.  Just loop.
254          */
255         hang();
256 }
257
258 static void process_fdt_options(const void *blob)
259 {
260         ulong addr;
261
262         /* Add an env variable to point to a kernel payload, if available */
263         addr = fdtdec_get_config_int(gd->fdt_blob, "kernel-offset", 0);
264         if (addr)
265                 setenv_addr("kernaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr));
266
267         /* Add an env variable to point to a root disk, if available */
268         addr = fdtdec_get_config_int(gd->fdt_blob, "rootdisk-offset", 0);
269         if (addr)
270                 setenv_addr("rootaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr));
271 }
272 #endif /* CONFIG_OF_CONTROL */
273
274 void bootdelay_process(void)
275 {
276 #ifdef CONFIG_OF_CONTROL
277         char *env;
278 #endif
279         char *s;
280         int bootdelay;
281 #ifdef CONFIG_BOOTCOUNT_LIMIT
282         unsigned long bootcount = 0;
283         unsigned long bootlimit = 0;
284 #endif /* CONFIG_BOOTCOUNT_LIMIT */
285
286 #ifdef CONFIG_BOOTCOUNT_LIMIT
287         bootcount = bootcount_load();
288         bootcount++;
289         bootcount_store(bootcount);
290         setenv_ulong("bootcount", bootcount);
291         bootlimit = getenv_ulong("bootlimit", 10, 0);
292 #endif /* CONFIG_BOOTCOUNT_LIMIT */
293
294         s = getenv("bootdelay");
295         bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
296
297 #ifdef CONFIG_OF_CONTROL
298         bootdelay = fdtdec_get_config_int(gd->fdt_blob, "bootdelay",
299                         bootdelay);
300 #endif
301
302         debug("### main_loop entered: bootdelay=%d\n\n", bootdelay);
303
304 #if defined(CONFIG_MENU_SHOW)
305         bootdelay = menu_show(bootdelay);
306 #endif
307 # ifdef CONFIG_BOOT_RETRY_TIME
308         init_cmd_timeout();
309 # endif /* CONFIG_BOOT_RETRY_TIME */
310
311 #ifdef CONFIG_POST
312         if (gd->flags & GD_FLG_POSTFAIL) {
313                 s = getenv("failbootcmd");
314         } else
315 #endif /* CONFIG_POST */
316 #ifdef CONFIG_BOOTCOUNT_LIMIT
317         if (bootlimit && (bootcount > bootlimit)) {
318                 printf("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",
319                        (unsigned)bootlimit);
320                 s = getenv("altbootcmd");
321         } else
322 #endif /* CONFIG_BOOTCOUNT_LIMIT */
323                 s = getenv("bootcmd");
324 #ifdef CONFIG_OF_CONTROL
325         /* Allow the fdt to override the boot command */
326         env = fdtdec_get_config_string(gd->fdt_blob, "bootcmd");
327         if (env)
328                 s = env;
329
330         process_fdt_options(gd->fdt_blob);
331
332         /*
333          * If the bootsecure option was chosen, use secure_boot_cmd().
334          * Always use 'env' in this case, since bootsecure requres that the
335          * bootcmd was specified in the FDT too.
336          */
337         if (fdtdec_get_config_int(gd->fdt_blob, "bootsecure", 0))
338                 secure_boot_cmd(env);
339
340 #endif /* CONFIG_OF_CONTROL */
341
342         debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
343
344         if (bootdelay != -1 && s && !abortboot(bootdelay)) {
345 #if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
346                 int prev = disable_ctrlc(1);    /* disable Control C checking */
347 #endif
348
349                 run_command_list(s, -1, 0);
350
351 #if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
352                 disable_ctrlc(prev);    /* restore Control C checking */
353 #endif
354         }
355
356 #ifdef CONFIG_MENUKEY
357         if (menukey == CONFIG_MENUKEY) {
358                 s = getenv("menucmd");
359                 if (s)
360                         run_command_list(s, -1, 0);
361         }
362 #endif /* CONFIG_MENUKEY */
363 }