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