Merge branch 'master' of git://www.denx.de/git/u-boot-imx
[platform/kernel/u-boot.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 <bootretry.h>
10 #include <cli.h>
11 #include <fdtdec.h>
12 #include <menu.h>
13 #include <post.h>
14
15 DECLARE_GLOBAL_DATA_PTR;
16
17 #define MAX_DELAY_STOP_STR 32
18
19 #ifndef DEBUG_BOOTKEYS
20 #define DEBUG_BOOTKEYS 0
21 #endif
22 #define debug_bootkeys(fmt, args...)            \
23         debug_cond(DEBUG_BOOTKEYS, fmt, ##args)
24
25 /* Stored value of bootdelay, used by autoboot_command() */
26 static int stored_bootdelay;
27
28 /***************************************************************************
29  * Watch for 'delay' seconds for autoboot stop or autoboot delay string.
30  * returns: 0 -  no key string, allow autoboot 1 - got key string, abort
31  */
32 # if defined(CONFIG_AUTOBOOT_KEYED)
33 static int abortboot_keyed(int bootdelay)
34 {
35         int abort = 0;
36         uint64_t etime = endtick(bootdelay);
37         struct {
38                 char *str;
39                 u_int len;
40                 int retry;
41         }
42         delaykey[] = {
43                 { .str = getenv("bootdelaykey"),  .retry = 1 },
44                 { .str = getenv("bootdelaykey2"), .retry = 1 },
45                 { .str = getenv("bootstopkey"),   .retry = 0 },
46                 { .str = getenv("bootstopkey2"),  .retry = 0 },
47         };
48
49         char presskey[MAX_DELAY_STOP_STR];
50         u_int presskey_len = 0;
51         u_int presskey_max = 0;
52         u_int i;
53
54 #ifndef CONFIG_ZERO_BOOTDELAY_CHECK
55         if (bootdelay == 0)
56                 return 0;
57 #endif
58
59 #  ifdef CONFIG_AUTOBOOT_PROMPT
60         printf(CONFIG_AUTOBOOT_PROMPT);
61 #  endif
62
63 #  ifdef CONFIG_AUTOBOOT_DELAY_STR
64         if (delaykey[0].str == NULL)
65                 delaykey[0].str = CONFIG_AUTOBOOT_DELAY_STR;
66 #  endif
67 #  ifdef CONFIG_AUTOBOOT_DELAY_STR2
68         if (delaykey[1].str == NULL)
69                 delaykey[1].str = CONFIG_AUTOBOOT_DELAY_STR2;
70 #  endif
71 #  ifdef CONFIG_AUTOBOOT_STOP_STR
72         if (delaykey[2].str == NULL)
73                 delaykey[2].str = CONFIG_AUTOBOOT_STOP_STR;
74 #  endif
75 #  ifdef CONFIG_AUTOBOOT_STOP_STR2
76         if (delaykey[3].str == NULL)
77                 delaykey[3].str = CONFIG_AUTOBOOT_STOP_STR2;
78 #  endif
79
80         for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i++) {
81                 delaykey[i].len = delaykey[i].str == NULL ?
82                                     0 : strlen(delaykey[i].str);
83                 delaykey[i].len = delaykey[i].len > MAX_DELAY_STOP_STR ?
84                                     MAX_DELAY_STOP_STR : delaykey[i].len;
85
86                 presskey_max = presskey_max > delaykey[i].len ?
87                                     presskey_max : delaykey[i].len;
88
89                 debug_bootkeys("%s key:<%s>\n",
90                                delaykey[i].retry ? "delay" : "stop",
91                                delaykey[i].str ? delaykey[i].str : "NULL");
92         }
93
94         /* In order to keep up with incoming data, check timeout only
95          * when catch up.
96          */
97         do {
98                 if (tstc()) {
99                         if (presskey_len < presskey_max) {
100                                 presskey[presskey_len++] = getc();
101                         } else {
102                                 for (i = 0; i < presskey_max - 1; i++)
103                                         presskey[i] = presskey[i + 1];
104
105                                 presskey[i] = getc();
106                         }
107                 }
108
109                 for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i++) {
110                         if (delaykey[i].len > 0 &&
111                             presskey_len >= delaykey[i].len &&
112                                 memcmp(presskey + presskey_len -
113                                         delaykey[i].len, delaykey[i].str,
114                                         delaykey[i].len) == 0) {
115                                         debug_bootkeys("got %skey\n",
116                                                 delaykey[i].retry ? "delay" :
117                                                 "stop");
118
119                                 /* don't retry auto boot */
120                                 if (!delaykey[i].retry)
121                                         bootretry_dont_retry();
122                                 abort = 1;
123                         }
124                 }
125         } while (!abort && get_ticks() <= etime);
126
127         if (!abort)
128                 debug_bootkeys("key timeout\n");
129
130 #ifdef CONFIG_SILENT_CONSOLE
131         if (abort)
132                 gd->flags &= ~GD_FLG_SILENT;
133 #endif
134
135         return abort;
136 }
137
138 # else  /* !defined(CONFIG_AUTOBOOT_KEYED) */
139
140 #ifdef CONFIG_MENUKEY
141 static int menukey;
142 #endif
143
144 static int abortboot_normal(int bootdelay)
145 {
146         int abort = 0;
147         unsigned long ts;
148
149 #ifdef CONFIG_MENUPROMPT
150         printf(CONFIG_MENUPROMPT);
151 #else
152         if (bootdelay >= 0)
153                 printf("Hit any key to stop autoboot: %2d ", bootdelay);
154 #endif
155
156 #if defined CONFIG_ZERO_BOOTDELAY_CHECK
157         /*
158          * Check if key already pressed
159          * Don't check if bootdelay < 0
160          */
161         if (bootdelay >= 0) {
162                 if (tstc()) {   /* we got a key press   */
163                         (void) getc();  /* consume input        */
164                         puts("\b\b\b 0");
165                         abort = 1;      /* don't auto boot      */
166                 }
167         }
168 #endif
169
170         while ((bootdelay > 0) && (!abort)) {
171                 --bootdelay;
172                 /* delay 1000 ms */
173                 ts = get_timer(0);
174                 do {
175                         if (tstc()) {   /* we got a key press   */
176                                 abort  = 1;     /* don't auto boot      */
177                                 bootdelay = 0;  /* no more delay        */
178 # ifdef CONFIG_MENUKEY
179                                 menukey = getc();
180 # else
181                                 (void) getc();  /* consume input        */
182 # endif
183                                 break;
184                         }
185                         udelay(10000);
186                 } while (!abort && get_timer(ts) < 1000);
187
188                 printf("\b\b\b%2d ", bootdelay);
189         }
190
191         putc('\n');
192
193 #ifdef CONFIG_SILENT_CONSOLE
194         if (abort)
195                 gd->flags &= ~GD_FLG_SILENT;
196 #endif
197
198         return abort;
199 }
200 # endif /* CONFIG_AUTOBOOT_KEYED */
201
202 static int abortboot(int bootdelay)
203 {
204 #ifdef CONFIG_AUTOBOOT_KEYED
205         return abortboot_keyed(bootdelay);
206 #else
207         return abortboot_normal(bootdelay);
208 #endif
209 }
210
211 static void process_fdt_options(const void *blob)
212 {
213 #if defined(CONFIG_OF_CONTROL)
214         ulong addr;
215
216         /* Add an env variable to point to a kernel payload, if available */
217         addr = fdtdec_get_config_int(gd->fdt_blob, "kernel-offset", 0);
218         if (addr)
219                 setenv_addr("kernaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr));
220
221         /* Add an env variable to point to a root disk, if available */
222         addr = fdtdec_get_config_int(gd->fdt_blob, "rootdisk-offset", 0);
223         if (addr)
224                 setenv_addr("rootaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr));
225 #endif /* CONFIG_OF_CONTROL */
226 }
227
228 const char *bootdelay_process(void)
229 {
230         char *s;
231         int bootdelay;
232 #ifdef CONFIG_BOOTCOUNT_LIMIT
233         unsigned long bootcount = 0;
234         unsigned long bootlimit = 0;
235 #endif /* CONFIG_BOOTCOUNT_LIMIT */
236
237 #ifdef CONFIG_BOOTCOUNT_LIMIT
238         bootcount = bootcount_load();
239         bootcount++;
240         bootcount_store(bootcount);
241         setenv_ulong("bootcount", bootcount);
242         bootlimit = getenv_ulong("bootlimit", 10, 0);
243 #endif /* CONFIG_BOOTCOUNT_LIMIT */
244
245         s = getenv("bootdelay");
246         bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
247
248 #ifdef CONFIG_OF_CONTROL
249         bootdelay = fdtdec_get_config_int(gd->fdt_blob, "bootdelay",
250                         bootdelay);
251 #endif
252
253         debug("### main_loop entered: bootdelay=%d\n\n", bootdelay);
254
255 #if defined(CONFIG_MENU_SHOW)
256         bootdelay = menu_show(bootdelay);
257 #endif
258         bootretry_init_cmd_timeout();
259
260 #ifdef CONFIG_POST
261         if (gd->flags & GD_FLG_POSTFAIL) {
262                 s = getenv("failbootcmd");
263         } else
264 #endif /* CONFIG_POST */
265 #ifdef CONFIG_BOOTCOUNT_LIMIT
266         if (bootlimit && (bootcount > bootlimit)) {
267                 printf("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",
268                        (unsigned)bootlimit);
269                 s = getenv("altbootcmd");
270         } else
271 #endif /* CONFIG_BOOTCOUNT_LIMIT */
272                 s = getenv("bootcmd");
273
274         process_fdt_options(gd->fdt_blob);
275         stored_bootdelay = bootdelay;
276
277         return s;
278 }
279
280 void autoboot_command(const char *s)
281 {
282         debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
283
284         if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {
285 #if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
286                 int prev = disable_ctrlc(1);    /* disable Control C checking */
287 #endif
288
289                 run_command_list(s, -1, 0);
290
291 #if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
292                 disable_ctrlc(prev);    /* restore Control C checking */
293 #endif
294         }
295
296 #ifdef CONFIG_MENUKEY
297         if (menukey == CONFIG_MENUKEY) {
298                 s = getenv("menucmd");
299                 if (s)
300                         run_command_list(s, -1, 0);
301         }
302 #endif /* CONFIG_MENUKEY */
303 }