uboot/lib/efi_loader/efi_bootmgr.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 *  EFI boot manager
   4 *
   5 *  Copyright (c) 2017 Rob Clark
   6 */
   7
   8#define LOG_CATEGORY LOGC_EFI
   9
  10#include <common.h>
  11#include <charset.h>
  12#include <log.h>
  13#include <malloc.h>
  14#include <efi_loader.h>
  15#include <efi_variable.h>
  16#include <asm/unaligned.h>
  17
  18static const struct efi_boot_services *bs;
  19static const struct efi_runtime_services *rs;
  20
  21/*
  22 * bootmgr implements the logic of trying to find a payload to boot
  23 * based on the BootOrder + BootXXXX variables, and then loading it.
  24 *
  25 * TODO detecting a special key held (f9?) and displaying a boot menu
  26 * like you would get on a PC would be clever.
  27 *
  28 * TODO if we had a way to write and persist variables after the OS
  29 * has started, we'd also want to check OsIndications to see if we
  30 * should do normal or recovery boot.
  31 */
  32
  33/**
  34 * try_load_entry() - try to load image for boot option
  35 *
  36 * Attempt to load load-option number 'n', returning device_path and file_path
  37 * if successful. This checks that the EFI_LOAD_OPTION is active (enabled)
  38 * and that the specified file to boot exists.
  39 *
  40 * @n:                  number of the boot option, e.g. 0x0a13 for Boot0A13
  41 * @handle:             on return handle for the newly installed image
  42 * @load_options:       load options set on the loaded image protocol
  43 * Return:              status code
  44 */
  45static efi_status_t try_load_entry(u16 n, efi_handle_t *handle,
  46                                   void **load_options)
  47{
  48        struct efi_load_option lo;
  49        u16 varname[] = L"Boot0000";
  50        u16 hexmap[] = L"0123456789ABCDEF";
  51        void *load_option;
  52        efi_uintn_t size;
  53        efi_status_t ret;
  54
  55        varname[4] = hexmap[(n & 0xf000) >> 12];
  56        varname[5] = hexmap[(n & 0x0f00) >> 8];
  57        varname[6] = hexmap[(n & 0x00f0) >> 4];
  58        varname[7] = hexmap[(n & 0x000f) >> 0];
  59
  60        load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
  61        if (!load_option)
  62                return EFI_LOAD_ERROR;
  63
  64        ret = efi_deserialize_load_option(&lo, load_option, &size);
  65        if (ret != EFI_SUCCESS) {
  66                log_warning("Invalid load option for %ls\n", varname);
  67                goto error;
  68        }
  69
  70        if (lo.attributes & LOAD_OPTION_ACTIVE) {
  71                u32 attributes;
  72
  73                log_debug("%s: trying to load \"%ls\" from %pD\n",
  74                          __func__, lo.label, lo.file_path);
  75
  76                ret = EFI_CALL(efi_load_image(true, efi_root, lo.file_path,
  77                                              NULL, 0, handle));
  78                if (ret != EFI_SUCCESS) {
  79                        log_warning("Loading %ls '%ls' failed\n",
  80                                    varname, lo.label);
  81                        goto error;
  82                }
  83
  84                attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS |
  85                             EFI_VARIABLE_RUNTIME_ACCESS;
  86                ret = efi_set_variable_int(L"BootCurrent",
  87                                           &efi_global_variable_guid,
  88                                           attributes, sizeof(n), &n, false);
  89                if (ret != EFI_SUCCESS)
  90                        goto unload;
  91                /* try to register load file2 for initrd's */
  92                if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD)) {
  93                        ret = efi_initrd_register();
  94                        if (ret != EFI_SUCCESS)
  95                                goto unload;
  96                }
  97
  98                log_info("Booting: %ls\n", lo.label);
  99        } else {
 100                ret = EFI_LOAD_ERROR;
 101        }
 102
 103        /* Set load options */
 104        if (size) {
 105                *load_options = malloc(size);
 106                if (!*load_options) {
 107                        ret = EFI_OUT_OF_RESOURCES;
 108                        goto error;
 109                }
 110                memcpy(*load_options, lo.optional_data, size);
 111                ret = efi_set_load_options(*handle, size, *load_options);
 112        } else {
 113                *load_options = NULL;
 114        }
 115
 116error:
 117        free(load_option);
 118
 119        return ret;
 120
 121unload:
 122        if (EFI_CALL(efi_unload_image(*handle)) != EFI_SUCCESS)
 123                log_err("Unloading image failed\n");
 124        free(load_option);
 125
 126        return ret;
 127}
 128
 129/**
 130 * efi_bootmgr_load() - try to load from BootNext or BootOrder
 131 *
 132 * Attempt to load from BootNext or in the order specified by BootOrder
 133 * EFI variable, the available load-options, finding and returning
 134 * the first one that can be loaded successfully.
 135 *
 136 * @handle:             on return handle for the newly installed image
 137 * @load_options:       load options set on the loaded image protocol
 138 * Return:              status code
 139 */
 140efi_status_t efi_bootmgr_load(efi_handle_t *handle, void **load_options)
 141{
 142        u16 bootnext, *bootorder;
 143        efi_uintn_t size;
 144        int i, num;
 145        efi_status_t ret;
 146
 147        bs = systab.boottime;
 148        rs = systab.runtime;
 149
 150        /* BootNext */
 151        size = sizeof(bootnext);
 152        ret = efi_get_variable_int(L"BootNext",
 153                                   &efi_global_variable_guid,
 154                                   NULL, &size, &bootnext, NULL);
 155        if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
 156                /* BootNext does exist here */
 157                if (ret == EFI_BUFFER_TOO_SMALL || size != sizeof(u16))
 158                        log_err("BootNext must be 16-bit integer\n");
 159
 160                /* delete BootNext */
 161                ret = efi_set_variable_int(L"BootNext",
 162                                           &efi_global_variable_guid,
 163                                           0, 0, NULL, false);
 164
 165                /* load BootNext */
 166                if (ret == EFI_SUCCESS) {
 167                        if (size == sizeof(u16)) {
 168                                ret = try_load_entry(bootnext, handle,
 169                                                     load_options);
 170                                if (ret == EFI_SUCCESS)
 171                                        return ret;
 172                                log_warning(
 173                                        "Loading from BootNext failed, falling back to BootOrder\n");
 174                        }
 175                } else {
 176                        log_err("Deleting BootNext failed\n");
 177                }
 178        }
 179
 180        /* BootOrder */
 181        bootorder = efi_get_var(L"BootOrder", &efi_global_variable_guid, &size);
 182        if (!bootorder) {
 183                log_info("BootOrder not defined\n");
 184                ret = EFI_NOT_FOUND;
 185                goto error;
 186        }
 187
 188        num = size / sizeof(uint16_t);
 189        for (i = 0; i < num; i++) {
 190                log_debug("%s trying to load Boot%04X\n", __func__,
 191                          bootorder[i]);
 192                ret = try_load_entry(bootorder[i], handle, load_options);
 193                if (ret == EFI_SUCCESS)
 194                        break;
 195        }
 196
 197        free(bootorder);
 198
 199error:
 200        return ret;
 201}
 202