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> 9*0098e179SSimon Glass #include <bootretry.h> 1066ded17dSSimon Glass #include <cli.h> 1166ded17dSSimon Glass #include <fdtdec.h> 1266ded17dSSimon Glass #include <menu.h> 1366ded17dSSimon Glass #include <post.h> 1466ded17dSSimon Glass 1566ded17dSSimon Glass DECLARE_GLOBAL_DATA_PTR; 1666ded17dSSimon Glass 1766ded17dSSimon Glass #define MAX_DELAY_STOP_STR 32 1866ded17dSSimon Glass 1966ded17dSSimon Glass #ifndef DEBUG_BOOTKEYS 2066ded17dSSimon Glass #define DEBUG_BOOTKEYS 0 2166ded17dSSimon Glass #endif 2266ded17dSSimon Glass #define debug_bootkeys(fmt, args...) \ 2366ded17dSSimon Glass debug_cond(DEBUG_BOOTKEYS, fmt, ##args) 2466ded17dSSimon Glass 2566ded17dSSimon Glass /*************************************************************************** 2666ded17dSSimon Glass * Watch for 'delay' seconds for autoboot stop or autoboot delay string. 2766ded17dSSimon Glass * returns: 0 - no key string, allow autoboot 1 - got key string, abort 2866ded17dSSimon Glass */ 2966ded17dSSimon Glass # if defined(CONFIG_AUTOBOOT_KEYED) 3066ded17dSSimon Glass static int abortboot_keyed(int bootdelay) 3166ded17dSSimon Glass { 3266ded17dSSimon Glass int abort = 0; 3366ded17dSSimon Glass uint64_t etime = endtick(bootdelay); 3466ded17dSSimon Glass struct { 3566ded17dSSimon Glass char *str; 3666ded17dSSimon Glass u_int len; 3766ded17dSSimon Glass int retry; 3866ded17dSSimon Glass } 3966ded17dSSimon Glass delaykey[] = { 4066ded17dSSimon Glass { str: getenv("bootdelaykey"), retry: 1 }, 4166ded17dSSimon Glass { str: getenv("bootdelaykey2"), retry: 1 }, 4266ded17dSSimon Glass { str: getenv("bootstopkey"), retry: 0 }, 4366ded17dSSimon Glass { str: getenv("bootstopkey2"), retry: 0 }, 4466ded17dSSimon Glass }; 4566ded17dSSimon Glass 4666ded17dSSimon Glass char presskey[MAX_DELAY_STOP_STR]; 4766ded17dSSimon Glass u_int presskey_len = 0; 4866ded17dSSimon Glass u_int presskey_max = 0; 4966ded17dSSimon Glass u_int i; 5066ded17dSSimon Glass 5166ded17dSSimon Glass #ifndef CONFIG_ZERO_BOOTDELAY_CHECK 5266ded17dSSimon Glass if (bootdelay == 0) 5366ded17dSSimon Glass return 0; 5466ded17dSSimon Glass #endif 5566ded17dSSimon Glass 5666ded17dSSimon Glass # ifdef CONFIG_AUTOBOOT_PROMPT 5766ded17dSSimon Glass printf(CONFIG_AUTOBOOT_PROMPT); 5866ded17dSSimon Glass # endif 5966ded17dSSimon Glass 6066ded17dSSimon Glass # ifdef CONFIG_AUTOBOOT_DELAY_STR 6166ded17dSSimon Glass if (delaykey[0].str == NULL) 6266ded17dSSimon Glass delaykey[0].str = CONFIG_AUTOBOOT_DELAY_STR; 6366ded17dSSimon Glass # endif 6466ded17dSSimon Glass # ifdef CONFIG_AUTOBOOT_DELAY_STR2 6566ded17dSSimon Glass if (delaykey[1].str == NULL) 6666ded17dSSimon Glass delaykey[1].str = CONFIG_AUTOBOOT_DELAY_STR2; 6766ded17dSSimon Glass # endif 6866ded17dSSimon Glass # ifdef CONFIG_AUTOBOOT_STOP_STR 6966ded17dSSimon Glass if (delaykey[2].str == NULL) 7066ded17dSSimon Glass delaykey[2].str = CONFIG_AUTOBOOT_STOP_STR; 7166ded17dSSimon Glass # endif 7266ded17dSSimon Glass # ifdef CONFIG_AUTOBOOT_STOP_STR2 7366ded17dSSimon Glass if (delaykey[3].str == NULL) 7466ded17dSSimon Glass delaykey[3].str = CONFIG_AUTOBOOT_STOP_STR2; 7566ded17dSSimon Glass # endif 7666ded17dSSimon Glass 7766ded17dSSimon Glass for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i++) { 7866ded17dSSimon Glass delaykey[i].len = delaykey[i].str == NULL ? 7966ded17dSSimon Glass 0 : strlen(delaykey[i].str); 8066ded17dSSimon Glass delaykey[i].len = delaykey[i].len > MAX_DELAY_STOP_STR ? 8166ded17dSSimon Glass MAX_DELAY_STOP_STR : delaykey[i].len; 8266ded17dSSimon Glass 8366ded17dSSimon Glass presskey_max = presskey_max > delaykey[i].len ? 8466ded17dSSimon Glass presskey_max : delaykey[i].len; 8566ded17dSSimon Glass 8666ded17dSSimon Glass debug_bootkeys("%s key:<%s>\n", 8766ded17dSSimon Glass delaykey[i].retry ? "delay" : "stop", 8866ded17dSSimon Glass delaykey[i].str ? delaykey[i].str : "NULL"); 8966ded17dSSimon Glass } 9066ded17dSSimon Glass 9166ded17dSSimon Glass /* In order to keep up with incoming data, check timeout only 9266ded17dSSimon Glass * when catch up. 9366ded17dSSimon Glass */ 9466ded17dSSimon Glass do { 9566ded17dSSimon Glass if (tstc()) { 9666ded17dSSimon Glass if (presskey_len < presskey_max) { 9766ded17dSSimon Glass presskey[presskey_len++] = getc(); 9866ded17dSSimon Glass } else { 9966ded17dSSimon Glass for (i = 0; i < presskey_max - 1; i++) 10066ded17dSSimon Glass presskey[i] = presskey[i + 1]; 10166ded17dSSimon Glass 10266ded17dSSimon Glass presskey[i] = getc(); 10366ded17dSSimon Glass } 10466ded17dSSimon Glass } 10566ded17dSSimon Glass 10666ded17dSSimon Glass for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i++) { 10766ded17dSSimon Glass if (delaykey[i].len > 0 && 10866ded17dSSimon Glass presskey_len >= delaykey[i].len && 10966ded17dSSimon Glass memcmp(presskey + presskey_len - 11066ded17dSSimon Glass delaykey[i].len, delaykey[i].str, 11166ded17dSSimon Glass delaykey[i].len) == 0) { 11266ded17dSSimon Glass debug_bootkeys("got %skey\n", 11366ded17dSSimon Glass delaykey[i].retry ? "delay" : 11466ded17dSSimon Glass "stop"); 11566ded17dSSimon Glass 11666ded17dSSimon Glass # ifdef CONFIG_BOOT_RETRY_TIME 11766ded17dSSimon Glass /* don't retry auto boot */ 11866ded17dSSimon Glass if (!delaykey[i].retry) 11966ded17dSSimon Glass bootretry_dont_retry(); 12066ded17dSSimon Glass # endif 12166ded17dSSimon Glass abort = 1; 12266ded17dSSimon Glass } 12366ded17dSSimon Glass } 12466ded17dSSimon Glass } while (!abort && get_ticks() <= etime); 12566ded17dSSimon Glass 12666ded17dSSimon Glass if (!abort) 12766ded17dSSimon Glass debug_bootkeys("key timeout\n"); 12866ded17dSSimon Glass 12966ded17dSSimon Glass #ifdef CONFIG_SILENT_CONSOLE 13066ded17dSSimon Glass if (abort) 13166ded17dSSimon Glass gd->flags &= ~GD_FLG_SILENT; 13266ded17dSSimon Glass #endif 13366ded17dSSimon Glass 13466ded17dSSimon Glass return abort; 13566ded17dSSimon Glass } 13666ded17dSSimon Glass 13766ded17dSSimon Glass # else /* !defined(CONFIG_AUTOBOOT_KEYED) */ 13866ded17dSSimon Glass 13966ded17dSSimon Glass #ifdef CONFIG_MENUKEY 14066ded17dSSimon Glass static int menukey; 14166ded17dSSimon Glass #endif 14266ded17dSSimon Glass 14366ded17dSSimon Glass static int abortboot_normal(int bootdelay) 14466ded17dSSimon Glass { 14566ded17dSSimon Glass int abort = 0; 14666ded17dSSimon Glass unsigned long ts; 14766ded17dSSimon Glass 14866ded17dSSimon Glass #ifdef CONFIG_MENUPROMPT 14966ded17dSSimon Glass printf(CONFIG_MENUPROMPT); 15066ded17dSSimon Glass #else 15166ded17dSSimon Glass if (bootdelay >= 0) 15266ded17dSSimon Glass printf("Hit any key to stop autoboot: %2d ", bootdelay); 15366ded17dSSimon Glass #endif 15466ded17dSSimon Glass 15566ded17dSSimon Glass #if defined CONFIG_ZERO_BOOTDELAY_CHECK 15666ded17dSSimon Glass /* 15766ded17dSSimon Glass * Check if key already pressed 15866ded17dSSimon Glass * Don't check if bootdelay < 0 15966ded17dSSimon Glass */ 16066ded17dSSimon Glass if (bootdelay >= 0) { 16166ded17dSSimon Glass if (tstc()) { /* we got a key press */ 16266ded17dSSimon Glass (void) getc(); /* consume input */ 16366ded17dSSimon Glass puts("\b\b\b 0"); 16466ded17dSSimon Glass abort = 1; /* don't auto boot */ 16566ded17dSSimon Glass } 16666ded17dSSimon Glass } 16766ded17dSSimon Glass #endif 16866ded17dSSimon Glass 16966ded17dSSimon Glass while ((bootdelay > 0) && (!abort)) { 17066ded17dSSimon Glass --bootdelay; 17166ded17dSSimon Glass /* delay 1000 ms */ 17266ded17dSSimon Glass ts = get_timer(0); 17366ded17dSSimon Glass do { 17466ded17dSSimon Glass if (tstc()) { /* we got a key press */ 17566ded17dSSimon Glass abort = 1; /* don't auto boot */ 17666ded17dSSimon Glass bootdelay = 0; /* no more delay */ 17766ded17dSSimon Glass # ifdef CONFIG_MENUKEY 17866ded17dSSimon Glass menukey = getc(); 17966ded17dSSimon Glass # else 18066ded17dSSimon Glass (void) getc(); /* consume input */ 18166ded17dSSimon Glass # endif 18266ded17dSSimon Glass break; 18366ded17dSSimon Glass } 18466ded17dSSimon Glass udelay(10000); 18566ded17dSSimon Glass } while (!abort && get_timer(ts) < 1000); 18666ded17dSSimon Glass 18766ded17dSSimon Glass printf("\b\b\b%2d ", bootdelay); 18866ded17dSSimon Glass } 18966ded17dSSimon Glass 19066ded17dSSimon Glass putc('\n'); 19166ded17dSSimon Glass 19266ded17dSSimon Glass #ifdef CONFIG_SILENT_CONSOLE 19366ded17dSSimon Glass if (abort) 19466ded17dSSimon Glass gd->flags &= ~GD_FLG_SILENT; 19566ded17dSSimon Glass #endif 19666ded17dSSimon Glass 19766ded17dSSimon Glass return abort; 19866ded17dSSimon Glass } 19966ded17dSSimon Glass # endif /* CONFIG_AUTOBOOT_KEYED */ 20066ded17dSSimon Glass 20166ded17dSSimon Glass static int abortboot(int bootdelay) 20266ded17dSSimon Glass { 20366ded17dSSimon Glass #ifdef CONFIG_AUTOBOOT_KEYED 20466ded17dSSimon Glass return abortboot_keyed(bootdelay); 20566ded17dSSimon Glass #else 20666ded17dSSimon Glass return abortboot_normal(bootdelay); 20766ded17dSSimon Glass #endif 20866ded17dSSimon Glass } 20966ded17dSSimon Glass 21066ded17dSSimon Glass /* 21166ded17dSSimon Glass * Runs the given boot command securely. Specifically: 21266ded17dSSimon Glass * - Doesn't run the command with the shell (run_command or parse_string_outer), 21366ded17dSSimon Glass * since that's a lot of code surface that an attacker might exploit. 21466ded17dSSimon Glass * Because of this, we don't do any argument parsing--the secure boot command 21566ded17dSSimon Glass * has to be a full-fledged u-boot command. 21666ded17dSSimon Glass * - Doesn't check for keypresses before booting, since that could be a 21766ded17dSSimon Glass * security hole; also disables Ctrl-C. 21866ded17dSSimon Glass * - Doesn't allow the command to return. 21966ded17dSSimon Glass * 22066ded17dSSimon Glass * Upon any failures, this function will drop into an infinite loop after 22166ded17dSSimon Glass * printing the error message to console. 22266ded17dSSimon Glass */ 22366ded17dSSimon Glass 22466ded17dSSimon Glass #if defined(CONFIG_OF_CONTROL) 22566ded17dSSimon Glass static void secure_boot_cmd(char *cmd) 22666ded17dSSimon Glass { 22766ded17dSSimon Glass cmd_tbl_t *cmdtp; 22866ded17dSSimon Glass int rc; 22966ded17dSSimon Glass 23066ded17dSSimon Glass if (!cmd) { 23166ded17dSSimon Glass printf("## Error: Secure boot command not specified\n"); 23266ded17dSSimon Glass goto err; 23366ded17dSSimon Glass } 23466ded17dSSimon Glass 23566ded17dSSimon Glass /* Disable Ctrl-C just in case some command is used that checks it. */ 23666ded17dSSimon Glass disable_ctrlc(1); 23766ded17dSSimon Glass 23866ded17dSSimon Glass /* Find the command directly. */ 23966ded17dSSimon Glass cmdtp = find_cmd(cmd); 24066ded17dSSimon Glass if (!cmdtp) { 24166ded17dSSimon Glass printf("## Error: \"%s\" not defined\n", cmd); 24266ded17dSSimon Glass goto err; 24366ded17dSSimon Glass } 24466ded17dSSimon Glass 24566ded17dSSimon Glass /* Run the command, forcing no flags and faking argc and argv. */ 24666ded17dSSimon Glass rc = (cmdtp->cmd)(cmdtp, 0, 1, &cmd); 24766ded17dSSimon Glass 24866ded17dSSimon Glass /* Shouldn't ever return from boot command. */ 24966ded17dSSimon Glass printf("## Error: \"%s\" returned (code %d)\n", cmd, rc); 25066ded17dSSimon Glass 25166ded17dSSimon Glass err: 25266ded17dSSimon Glass /* 25366ded17dSSimon Glass * Not a whole lot to do here. Rebooting won't help much, since we'll 25466ded17dSSimon Glass * just end up right back here. Just loop. 25566ded17dSSimon Glass */ 25666ded17dSSimon Glass hang(); 25766ded17dSSimon Glass } 25866ded17dSSimon Glass 25966ded17dSSimon Glass static void process_fdt_options(const void *blob) 26066ded17dSSimon Glass { 26166ded17dSSimon Glass ulong addr; 26266ded17dSSimon Glass 26366ded17dSSimon Glass /* Add an env variable to point to a kernel payload, if available */ 26466ded17dSSimon Glass addr = fdtdec_get_config_int(gd->fdt_blob, "kernel-offset", 0); 26566ded17dSSimon Glass if (addr) 26666ded17dSSimon Glass setenv_addr("kernaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr)); 26766ded17dSSimon Glass 26866ded17dSSimon Glass /* Add an env variable to point to a root disk, if available */ 26966ded17dSSimon Glass addr = fdtdec_get_config_int(gd->fdt_blob, "rootdisk-offset", 0); 27066ded17dSSimon Glass if (addr) 27166ded17dSSimon Glass setenv_addr("rootaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr)); 27266ded17dSSimon Glass } 27366ded17dSSimon Glass #endif /* CONFIG_OF_CONTROL */ 27466ded17dSSimon Glass 27566ded17dSSimon Glass void bootdelay_process(void) 27666ded17dSSimon Glass { 27766ded17dSSimon Glass #ifdef CONFIG_OF_CONTROL 27866ded17dSSimon Glass char *env; 27966ded17dSSimon Glass #endif 28066ded17dSSimon Glass char *s; 28166ded17dSSimon Glass int bootdelay; 28266ded17dSSimon Glass #ifdef CONFIG_BOOTCOUNT_LIMIT 28366ded17dSSimon Glass unsigned long bootcount = 0; 28466ded17dSSimon Glass unsigned long bootlimit = 0; 28566ded17dSSimon Glass #endif /* CONFIG_BOOTCOUNT_LIMIT */ 28666ded17dSSimon Glass 28766ded17dSSimon Glass #ifdef CONFIG_BOOTCOUNT_LIMIT 28866ded17dSSimon Glass bootcount = bootcount_load(); 28966ded17dSSimon Glass bootcount++; 29066ded17dSSimon Glass bootcount_store(bootcount); 29166ded17dSSimon Glass setenv_ulong("bootcount", bootcount); 29266ded17dSSimon Glass bootlimit = getenv_ulong("bootlimit", 10, 0); 29366ded17dSSimon Glass #endif /* CONFIG_BOOTCOUNT_LIMIT */ 29466ded17dSSimon Glass 29566ded17dSSimon Glass s = getenv("bootdelay"); 29666ded17dSSimon Glass bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; 29766ded17dSSimon Glass 29866ded17dSSimon Glass #ifdef CONFIG_OF_CONTROL 29966ded17dSSimon Glass bootdelay = fdtdec_get_config_int(gd->fdt_blob, "bootdelay", 30066ded17dSSimon Glass bootdelay); 30166ded17dSSimon Glass #endif 30266ded17dSSimon Glass 30366ded17dSSimon Glass debug("### main_loop entered: bootdelay=%d\n\n", bootdelay); 30466ded17dSSimon Glass 30566ded17dSSimon Glass #if defined(CONFIG_MENU_SHOW) 30666ded17dSSimon Glass bootdelay = menu_show(bootdelay); 30766ded17dSSimon Glass #endif 30866ded17dSSimon Glass # ifdef CONFIG_BOOT_RETRY_TIME 30966ded17dSSimon Glass init_cmd_timeout(); 31066ded17dSSimon Glass # endif /* CONFIG_BOOT_RETRY_TIME */ 31166ded17dSSimon Glass 31266ded17dSSimon Glass #ifdef CONFIG_POST 31366ded17dSSimon Glass if (gd->flags & GD_FLG_POSTFAIL) { 31466ded17dSSimon Glass s = getenv("failbootcmd"); 31566ded17dSSimon Glass } else 31666ded17dSSimon Glass #endif /* CONFIG_POST */ 31766ded17dSSimon Glass #ifdef CONFIG_BOOTCOUNT_LIMIT 31866ded17dSSimon Glass if (bootlimit && (bootcount > bootlimit)) { 31966ded17dSSimon Glass printf("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n", 32066ded17dSSimon Glass (unsigned)bootlimit); 32166ded17dSSimon Glass s = getenv("altbootcmd"); 32266ded17dSSimon Glass } else 32366ded17dSSimon Glass #endif /* CONFIG_BOOTCOUNT_LIMIT */ 32466ded17dSSimon Glass s = getenv("bootcmd"); 32566ded17dSSimon Glass #ifdef CONFIG_OF_CONTROL 32666ded17dSSimon Glass /* Allow the fdt to override the boot command */ 32766ded17dSSimon Glass env = fdtdec_get_config_string(gd->fdt_blob, "bootcmd"); 32866ded17dSSimon Glass if (env) 32966ded17dSSimon Glass s = env; 33066ded17dSSimon Glass 33166ded17dSSimon Glass process_fdt_options(gd->fdt_blob); 33266ded17dSSimon Glass 33366ded17dSSimon Glass /* 33466ded17dSSimon Glass * If the bootsecure option was chosen, use secure_boot_cmd(). 33566ded17dSSimon Glass * Always use 'env' in this case, since bootsecure requres that the 33666ded17dSSimon Glass * bootcmd was specified in the FDT too. 33766ded17dSSimon Glass */ 33866ded17dSSimon Glass if (fdtdec_get_config_int(gd->fdt_blob, "bootsecure", 0)) 33966ded17dSSimon Glass secure_boot_cmd(env); 34066ded17dSSimon Glass 34166ded17dSSimon Glass #endif /* CONFIG_OF_CONTROL */ 34266ded17dSSimon Glass 34366ded17dSSimon Glass debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>"); 34466ded17dSSimon Glass 34566ded17dSSimon Glass if (bootdelay != -1 && s && !abortboot(bootdelay)) { 34666ded17dSSimon Glass #if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC) 34766ded17dSSimon Glass int prev = disable_ctrlc(1); /* disable Control C checking */ 34866ded17dSSimon Glass #endif 34966ded17dSSimon Glass 35066ded17dSSimon Glass run_command_list(s, -1, 0); 35166ded17dSSimon Glass 35266ded17dSSimon Glass #if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC) 35366ded17dSSimon Glass disable_ctrlc(prev); /* restore Control C checking */ 35466ded17dSSimon Glass #endif 35566ded17dSSimon Glass } 35666ded17dSSimon Glass 35766ded17dSSimon Glass #ifdef CONFIG_MENUKEY 35866ded17dSSimon Glass if (menukey == CONFIG_MENUKEY) { 35966ded17dSSimon Glass s = getenv("menucmd"); 36066ded17dSSimon Glass if (s) 36166ded17dSSimon Glass run_command_list(s, -1, 0); 36266ded17dSSimon Glass } 36366ded17dSSimon Glass #endif /* CONFIG_MENUKEY */ 36466ded17dSSimon Glass } 365