uboot/lib/efi_loader/efi_var_mem.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * File interface for UEFI variables
   4 *
   5 * Copyright (c) 2020, Heinrich Schuchardt
   6 */
   7
   8#include <common.h>
   9#include <efi_loader.h>
  10#include <efi_variable.h>
  11#include <u-boot/crc.h>
  12
  13/*
  14 * The variables efi_var_file and efi_var_entry must be static to avoid
  15 * referencing them via the global offset table (section .got). The GOT
  16 * is neither mapped as EfiRuntimeServicesData nor do we support its
  17 * relocation during SetVirtualAddressMap().
  18 */
  19static struct efi_var_file __efi_runtime_data *efi_var_buf;
  20static struct efi_var_entry __efi_runtime_data *efi_current_var;
  21
  22/**
  23 * efi_var_mem_compare() - compare GUID and name with a variable
  24 *
  25 * @var:        variable to compare
  26 * @guid:       GUID to compare
  27 * @name:       variable name to compare
  28 * @next:       pointer to next variable
  29 * Return:      true if match
  30 */
  31static bool __efi_runtime
  32efi_var_mem_compare(struct efi_var_entry *var, const efi_guid_t *guid,
  33                    const u16 *name, struct efi_var_entry **next)
  34{
  35        int i;
  36        u8 *guid1, *guid2;
  37        const u16 *data, *var_name;
  38        bool match = true;
  39
  40        for (guid1 = (u8 *)&var->guid, guid2 = (u8 *)guid, i = 0;
  41             i < sizeof(efi_guid_t) && match; ++i)
  42                match = (guid1[i] == guid2[i]);
  43
  44        for (data = var->name, var_name = name;; ++data, ++var_name) {
  45                if (match)
  46                        match = (*data == *var_name);
  47                if (!*data)
  48                        break;
  49        }
  50
  51        ++data;
  52
  53        if (next)
  54                *next = (struct efi_var_entry *)
  55                        ALIGN((uintptr_t)data + var->length, 8);
  56
  57        if (match)
  58                efi_current_var = var;
  59
  60        return match;
  61}
  62
  63struct efi_var_entry __efi_runtime
  64*efi_var_mem_find(const efi_guid_t *guid, const u16 *name,
  65                  struct efi_var_entry **next)
  66{
  67        struct efi_var_entry *var, *last;
  68
  69        last = (struct efi_var_entry *)
  70               ((uintptr_t)efi_var_buf + efi_var_buf->length);
  71
  72        if (!*name) {
  73                if (next) {
  74                        *next = efi_var_buf->var;
  75                        if (*next >= last)
  76                                *next = NULL;
  77                }
  78                return NULL;
  79        }
  80        if (efi_current_var &&
  81            efi_var_mem_compare(efi_current_var, guid, name, next)) {
  82                if (next && *next >= last)
  83                        *next = NULL;
  84                return efi_current_var;
  85        }
  86
  87        var = efi_var_buf->var;
  88        if (var < last) {
  89                for (; var;) {
  90                        struct efi_var_entry *pos;
  91                        bool match;
  92
  93                        match = efi_var_mem_compare(var, guid, name, &pos);
  94                        if (pos >= last)
  95                                pos = NULL;
  96                        if (match) {
  97                                if (next)
  98                                        *next = pos;
  99                                return var;
 100                        }
 101                        var = pos;
 102                }
 103        }
 104        if (next)
 105                *next = NULL;
 106        return NULL;
 107}
 108
 109void __efi_runtime efi_var_mem_del(struct efi_var_entry *var)
 110{
 111        u16 *data;
 112        struct efi_var_entry *next, *last;
 113
 114        if (!var)
 115                return;
 116
 117        last = (struct efi_var_entry *)
 118               ((uintptr_t)efi_var_buf + efi_var_buf->length);
 119        if (var <= efi_current_var)
 120                efi_current_var = NULL;
 121
 122        for (data = var->name; *data; ++data)
 123                ;
 124        ++data;
 125        next = (struct efi_var_entry *)
 126               ALIGN((uintptr_t)data + var->length, 8);
 127        efi_var_buf->length -= (uintptr_t)next - (uintptr_t)var;
 128
 129        /* efi_memcpy_runtime() can be used because next >= var. */
 130        efi_memcpy_runtime(var, next, (uintptr_t)last - (uintptr_t)next);
 131        efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
 132                                   efi_var_buf->length -
 133                                   sizeof(struct efi_var_file));
 134}
 135
 136efi_status_t __efi_runtime efi_var_mem_ins(
 137                                const u16 *variable_name,
 138                                const efi_guid_t *vendor, u32 attributes,
 139                                const efi_uintn_t size1, const void *data1,
 140                                const efi_uintn_t size2, const void *data2,
 141                                const u64 time)
 142{
 143        u16 *data;
 144        struct efi_var_entry *var;
 145        u32 var_name_len;
 146
 147        var = (struct efi_var_entry *)
 148              ((uintptr_t)efi_var_buf + efi_var_buf->length);
 149        for (var_name_len = 0; variable_name[var_name_len]; ++var_name_len)
 150                ;
 151        ++var_name_len;
 152        data = var->name + var_name_len;
 153
 154        if ((uintptr_t)data - (uintptr_t)efi_var_buf + size1 + size2 >
 155            EFI_VAR_BUF_SIZE)
 156                return EFI_OUT_OF_RESOURCES;
 157
 158        var->attr = attributes;
 159        var->length = size1 + size2;
 160        var->time = time;
 161
 162        efi_memcpy_runtime(&var->guid, vendor, sizeof(efi_guid_t));
 163        efi_memcpy_runtime(var->name, variable_name,
 164                           sizeof(u16) * var_name_len);
 165        efi_memcpy_runtime(data, data1, size1);
 166        efi_memcpy_runtime((u8 *)data + size1, data2, size2);
 167
 168        var = (struct efi_var_entry *)
 169              ALIGN((uintptr_t)data + var->length, 8);
 170        efi_var_buf->length = (uintptr_t)var - (uintptr_t)efi_var_buf;
 171        efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
 172                                   efi_var_buf->length -
 173                                   sizeof(struct efi_var_file));
 174
 175        return EFI_SUCCESS;
 176}
 177
 178u64 __efi_runtime efi_var_mem_free(void)
 179{
 180        return EFI_VAR_BUF_SIZE - efi_var_buf->length -
 181               sizeof(struct efi_var_entry);
 182}
 183
 184/**
 185 * efi_var_mem_bs_del() - delete boot service only variables
 186 */
 187static void efi_var_mem_bs_del(void)
 188{
 189        struct efi_var_entry *var = efi_var_buf->var;
 190
 191        for (;;) {
 192                struct efi_var_entry *last;
 193
 194                last = (struct efi_var_entry *)
 195                       ((uintptr_t)efi_var_buf + efi_var_buf->length);
 196                if (var >= last)
 197                        break;
 198                if (var->attr & EFI_VARIABLE_RUNTIME_ACCESS) {
 199                        u16 *data;
 200
 201                        /* skip variable */
 202                        for (data = var->name; *data; ++data)
 203                                ;
 204                        ++data;
 205                        var = (struct efi_var_entry *)
 206                              ALIGN((uintptr_t)data + var->length, 8);
 207                } else {
 208                        /* delete variable */
 209                        efi_var_mem_del(var);
 210                }
 211        }
 212}
 213
 214/**
 215 * efi_var_mem_notify_exit_boot_services() - ExitBootService callback
 216 *
 217 * @event:      callback event
 218 * @context:    callback context
 219 */
 220static void EFIAPI
 221efi_var_mem_notify_exit_boot_services(struct efi_event *event, void *context)
 222{
 223        EFI_ENTRY("%p, %p", event, context);
 224
 225        /* Delete boot service only variables */
 226        efi_var_mem_bs_del();
 227
 228        EFI_EXIT(EFI_SUCCESS);
 229}
 230
 231/**
 232 * efi_var_mem_notify_exit_boot_services() - SetVirtualMemoryMap callback
 233 *
 234 * @event:      callback event
 235 * @context:    callback context
 236 */
 237static void EFIAPI __efi_runtime
 238efi_var_mem_notify_virtual_address_map(struct efi_event *event, void *context)
 239{
 240        efi_convert_pointer(0, (void **)&efi_var_buf);
 241        efi_current_var = NULL;
 242}
 243
 244efi_status_t efi_var_mem_init(void)
 245{
 246        u64 memory;
 247        efi_status_t ret;
 248        struct efi_event *event;
 249
 250        ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
 251                                 EFI_RUNTIME_SERVICES_DATA,
 252                                 efi_size_in_pages(EFI_VAR_BUF_SIZE),
 253                                 &memory);
 254        if (ret != EFI_SUCCESS)
 255                return ret;
 256        efi_var_buf = (struct efi_var_file *)(uintptr_t)memory;
 257        memset(efi_var_buf, 0, EFI_VAR_BUF_SIZE);
 258        efi_var_buf->magic = EFI_VAR_FILE_MAGIC;
 259        efi_var_buf->length = (uintptr_t)efi_var_buf->var -
 260                              (uintptr_t)efi_var_buf;
 261        /* crc32 for 0 bytes = 0 */
 262
 263        ret = efi_create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
 264                               efi_var_mem_notify_exit_boot_services, NULL,
 265                               NULL, &event);
 266        if (ret != EFI_SUCCESS)
 267                return ret;
 268        ret = efi_create_event(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE, TPL_CALLBACK,
 269                               efi_var_mem_notify_virtual_address_map, NULL,
 270                               NULL, &event);
 271        if (ret != EFI_SUCCESS)
 272                return ret;
 273        return ret;
 274}
 275
 276efi_status_t __efi_runtime
 277efi_get_variable_mem(const u16 *variable_name, const efi_guid_t *vendor,
 278                     u32 *attributes, efi_uintn_t *data_size, void *data,
 279                     u64 *timep)
 280{
 281        efi_uintn_t old_size;
 282        struct efi_var_entry *var;
 283        u16 *pdata;
 284
 285        if (!variable_name || !vendor || !data_size)
 286                return EFI_INVALID_PARAMETER;
 287        var = efi_var_mem_find(vendor, variable_name, NULL);
 288        if (!var)
 289                return EFI_NOT_FOUND;
 290
 291        if (attributes)
 292                *attributes = var->attr;
 293        if (timep)
 294                *timep = var->time;
 295
 296        old_size = *data_size;
 297        *data_size = var->length;
 298        if (old_size < var->length)
 299                return EFI_BUFFER_TOO_SMALL;
 300
 301        if (!data)
 302                return EFI_INVALID_PARAMETER;
 303
 304        for (pdata = var->name; *pdata; ++pdata)
 305                ;
 306        ++pdata;
 307
 308        efi_memcpy_runtime(data, pdata, var->length);
 309
 310        return EFI_SUCCESS;
 311}
 312
 313efi_status_t __efi_runtime
 314efi_get_next_variable_name_mem(efi_uintn_t *variable_name_size,
 315                               u16 *variable_name, efi_guid_t *vendor)
 316{
 317        struct efi_var_entry *var;
 318        efi_uintn_t old_size;
 319        u16 *pdata;
 320
 321        if (!variable_name_size || !variable_name || !vendor)
 322                return EFI_INVALID_PARAMETER;
 323
 324        if (u16_strnlen(variable_name, *variable_name_size) ==
 325            *variable_name_size)
 326                return EFI_INVALID_PARAMETER;
 327
 328        if (!efi_var_mem_find(vendor, variable_name, &var) && *variable_name)
 329                return EFI_INVALID_PARAMETER;
 330
 331        if (!var)
 332                return EFI_NOT_FOUND;
 333
 334        for (pdata = var->name; *pdata; ++pdata)
 335                ;
 336        ++pdata;
 337
 338        old_size = *variable_name_size;
 339        *variable_name_size = (uintptr_t)pdata - (uintptr_t)var->name;
 340
 341        if (old_size < *variable_name_size)
 342                return EFI_BUFFER_TOO_SMALL;
 343
 344        efi_memcpy_runtime(variable_name, var->name, *variable_name_size);
 345        efi_memcpy_runtime(vendor, &var->guid, sizeof(efi_guid_t));
 346
 347        return EFI_SUCCESS;
 348}
 349
 350void efi_var_buf_update(struct efi_var_file *var_buf)
 351{
 352        memcpy(efi_var_buf, var_buf, EFI_VAR_BUF_SIZE);
 353}
 354