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