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#include <common.h>
   9#include <charset.h>
  10#include <malloc.h>
  11#include <efi_loader.h>
  12#include <asm/unaligned.h>
  13
  14static const struct efi_boot_services *bs;
  15static const struct efi_runtime_services *rs;
  16
  17/*
  18 * bootmgr implements the logic of trying to find a payload to boot
  19 * based on the BootOrder + BootXXXX variables, and then loading it.
  20 *
  21 * TODO detecting a special key held (f9?) and displaying a boot menu
  22 * like you would get on a PC would be clever.
  23 *
  24 * TODO if we had a way to write and persist variables after the OS
  25 * has started, we'd also want to check OsIndications to see if we
  26 * should do normal or recovery boot.
  27 */
  28
  29
  30/**
  31 * efi_deserialize_load_option() - parse serialized data
  32 *
  33 * Parse serialized data describing a load option and transform it to the
  34 * efi_load_option structure.
  35 *
  36 * @lo:         pointer to target
  37 * @data:       serialized data
  38 */
  39void efi_deserialize_load_option(struct efi_load_option *lo, u8 *data)
  40{
  41        lo->attributes = get_unaligned_le32(data);
  42        data += sizeof(u32);
  43
  44        lo->file_path_length = get_unaligned_le16(data);
  45        data += sizeof(u16);
  46
  47        /* FIXME */
  48        lo->label = (u16 *)data;
  49        data += (u16_strlen(lo->label) + 1) * sizeof(u16);
  50
  51        /* FIXME */
  52        lo->file_path = (struct efi_device_path *)data;
  53        data += lo->file_path_length;
  54
  55        lo->optional_data = data;
  56}
  57
  58/**
  59 * efi_serialize_load_option() - serialize load option
  60 *
  61 * Serialize efi_load_option structure into byte stream for BootXXXX.
  62 *
  63 * @data:       buffer for serialized data
  64 * @lo:         load option
  65 * Return:      size of allocated buffer
  66 */
  67unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data)
  68{
  69        unsigned long label_len;
  70        unsigned long size;
  71        u8 *p;
  72
  73        label_len = (u16_strlen(lo->label) + 1) * sizeof(u16);
  74
  75        /* total size */
  76        size = sizeof(lo->attributes);
  77        size += sizeof(lo->file_path_length);
  78        size += label_len;
  79        size += lo->file_path_length;
  80        if (lo->optional_data)
  81                size += (utf8_utf16_strlen((const char *)lo->optional_data)
  82                                           + 1) * sizeof(u16);
  83        p = malloc(size);
  84        if (!p)
  85                return 0;
  86
  87        /* copy data */
  88        *data = p;
  89        memcpy(p, &lo->attributes, sizeof(lo->attributes));
  90        p += sizeof(lo->attributes);
  91
  92        memcpy(p, &lo->file_path_length, sizeof(lo->file_path_length));
  93        p += sizeof(lo->file_path_length);
  94
  95        memcpy(p, lo->label, label_len);
  96        p += label_len;
  97
  98        memcpy(p, lo->file_path, lo->file_path_length);
  99        p += lo->file_path_length;
 100
 101        if (lo->optional_data) {
 102                utf8_utf16_strcpy((u16 **)&p, (const char *)lo->optional_data);
 103                p += sizeof(u16); /* size of trailing \0 */
 104        }
 105        return size;
 106}
 107
 108/**
 109 * get_var() - get UEFI variable
 110 *
 111 * It is the caller's duty to free the returned buffer.
 112 *
 113 * @name:       name of variable
 114 * @vendor:     vendor GUID of variable
 115 * @size:       size of allocated buffer
 116 * Return:      buffer with variable data or NULL
 117 */
 118static void *get_var(u16 *name, const efi_guid_t *vendor,
 119                     efi_uintn_t *size)
 120{
 121        efi_guid_t *v = (efi_guid_t *)vendor;
 122        efi_status_t ret;
 123        void *buf = NULL;
 124
 125        *size = 0;
 126        EFI_CALL(ret = rs->get_variable(name, v, NULL, size, buf));
 127        if (ret == EFI_BUFFER_TOO_SMALL) {
 128                buf = malloc(*size);
 129                EFI_CALL(ret = rs->get_variable(name, v, NULL, size, buf));
 130        }
 131
 132        if (ret != EFI_SUCCESS) {
 133                free(buf);
 134                *size = 0;
 135                return NULL;
 136        }
 137
 138        return buf;
 139}
 140
 141/**
 142 * try_load_entry() - try to load image for boot option
 143 *
 144 * Attempt to load load-option number 'n', returning device_path and file_path
 145 * if successful. This checks that the EFI_LOAD_OPTION is active (enabled)
 146 * and that the specified file to boot exists.
 147 *
 148 * @n:          number of the boot option, e.g. 0x0a13 for Boot0A13
 149 * @handle:     on return handle for the newly installed image
 150 * Return:      status code
 151 */
 152static efi_status_t try_load_entry(u16 n, efi_handle_t *handle)
 153{
 154        struct efi_load_option lo;
 155        u16 varname[] = L"Boot0000";
 156        u16 hexmap[] = L"0123456789ABCDEF";
 157        void *load_option;
 158        efi_uintn_t size;
 159        efi_status_t ret;
 160
 161        varname[4] = hexmap[(n & 0xf000) >> 12];
 162        varname[5] = hexmap[(n & 0x0f00) >> 8];
 163        varname[6] = hexmap[(n & 0x00f0) >> 4];
 164        varname[7] = hexmap[(n & 0x000f) >> 0];
 165
 166        load_option = get_var(varname, &efi_global_variable_guid, &size);
 167        if (!load_option)
 168                return EFI_LOAD_ERROR;
 169
 170        efi_deserialize_load_option(&lo, load_option);
 171
 172        if (lo.attributes & LOAD_OPTION_ACTIVE) {
 173                u32 attributes;
 174
 175                debug("%s: trying to load \"%ls\" from %pD\n",
 176                      __func__, lo.label, lo.file_path);
 177
 178                ret = EFI_CALL(efi_load_image(true, efi_root, lo.file_path,
 179                                              NULL, 0, handle));
 180                if (ret != EFI_SUCCESS) {
 181                        printf("Loading from Boot%04X '%ls' failed\n", n,
 182                               lo.label);
 183                        goto error;
 184                }
 185
 186                attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS |
 187                             EFI_VARIABLE_RUNTIME_ACCESS;
 188                size = sizeof(n);
 189                ret = EFI_CALL(efi_set_variable(
 190                                L"BootCurrent",
 191                                (efi_guid_t *)&efi_global_variable_guid,
 192                                attributes, size, &n));
 193                if (ret != EFI_SUCCESS) {
 194                        if (EFI_CALL(efi_unload_image(*handle))
 195                            != EFI_SUCCESS)
 196                                printf("Unloading image failed\n");
 197                        goto error;
 198                }
 199
 200                printf("Booting: %ls\n", lo.label);
 201        } else {
 202                ret = EFI_LOAD_ERROR;
 203        }
 204
 205error:
 206        free(load_option);
 207
 208        return ret;
 209}
 210
 211/**
 212 * efi_bootmgr_load() - try to load from BootNext or BootOrder
 213 *
 214 * Attempt to load from BootNext or in the order specified by BootOrder
 215 * EFI variable, the available load-options, finding and returning
 216 * the first one that can be loaded successfully.
 217 *
 218 * @handle:     on return handle for the newly installed image
 219 * Return:      status code
 220 */
 221efi_status_t efi_bootmgr_load(efi_handle_t *handle)
 222{
 223        u16 bootnext, *bootorder;
 224        efi_uintn_t size;
 225        int i, num;
 226        efi_status_t ret;
 227
 228        bs = systab.boottime;
 229        rs = systab.runtime;
 230
 231        /* BootNext */
 232        bootnext = 0;
 233        size = sizeof(bootnext);
 234        ret = EFI_CALL(efi_get_variable(L"BootNext",
 235                                        (efi_guid_t *)&efi_global_variable_guid,
 236                                        NULL, &size, &bootnext));
 237        if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
 238                /* BootNext does exist here */
 239                if (ret == EFI_BUFFER_TOO_SMALL || size != sizeof(u16))
 240                        printf("BootNext must be 16-bit integer\n");
 241
 242                /* delete BootNext */
 243                ret = EFI_CALL(efi_set_variable(
 244                                        L"BootNext",
 245                                        (efi_guid_t *)&efi_global_variable_guid,
 246                                        EFI_VARIABLE_NON_VOLATILE, 0,
 247                                        &bootnext));
 248
 249                /* load BootNext */
 250                if (ret == EFI_SUCCESS) {
 251                        if (size == sizeof(u16)) {
 252                                ret = try_load_entry(bootnext, handle);
 253                                if (ret == EFI_SUCCESS)
 254                                        return ret;
 255                                printf("Loading from BootNext failed, falling back to BootOrder\n");
 256                        }
 257                } else {
 258                        printf("Deleting BootNext failed\n");
 259                }
 260        }
 261
 262        /* BootOrder */
 263        bootorder = get_var(L"BootOrder", &efi_global_variable_guid, &size);
 264        if (!bootorder) {
 265                printf("BootOrder not defined\n");
 266                ret = EFI_NOT_FOUND;
 267                goto error;
 268        }
 269
 270        num = size / sizeof(uint16_t);
 271        for (i = 0; i < num; i++) {
 272                debug("%s: trying to load Boot%04X\n", __func__, bootorder[i]);
 273                ret = try_load_entry(bootorder[i], handle);
 274                if (ret == EFI_SUCCESS)
 275                        break;
 276        }
 277
 278        free(bootorder);
 279
 280error:
 281        return ret;
 282}
 283