uboot/lib/efi_loader/efi_runtime.c
<<
>>
Prefs
   1/*
   2 *  EFI application runtime services
   3 *
   4 *  Copyright (c) 2016 Alexander Graf
   5 *
   6 *  SPDX-License-Identifier:     GPL-2.0+
   7 */
   8
   9#include <common.h>
  10#include <command.h>
  11#include <dm.h>
  12#include <efi_loader.h>
  13#include <rtc.h>
  14#include <asm/global_data.h>
  15
  16/* For manual relocation support */
  17DECLARE_GLOBAL_DATA_PTR;
  18
  19struct efi_runtime_mmio_list {
  20        struct list_head link;
  21        void **ptr;
  22        u64 paddr;
  23        u64 len;
  24};
  25
  26/* This list contains all runtime available mmio regions */
  27LIST_HEAD(efi_runtime_mmio);
  28
  29static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void);
  30static efi_status_t __efi_runtime EFIAPI efi_device_error(void);
  31static efi_status_t __efi_runtime EFIAPI efi_invalid_parameter(void);
  32
  33#ifdef CONFIG_SYS_CACHELINE_SIZE
  34#define EFI_CACHELINE_SIZE CONFIG_SYS_CACHELINE_SIZE
  35#else
  36/* Just use the greatest cache flush alignment requirement I'm aware of */
  37#define EFI_CACHELINE_SIZE 128
  38#endif
  39
  40#if defined(CONFIG_ARM64)
  41#define R_RELATIVE      1027
  42#define R_MASK          0xffffffffULL
  43#define IS_RELA         1
  44#elif defined(CONFIG_ARM)
  45#define R_RELATIVE      23
  46#define R_MASK          0xffULL
  47#elif defined(CONFIG_X86)
  48#include <asm/elf.h>
  49#define R_RELATIVE      R_386_RELATIVE
  50#define R_MASK          0xffULL
  51#else
  52#error Need to add relocation awareness
  53#endif
  54
  55struct elf_rel {
  56        ulong *offset;
  57        ulong info;
  58};
  59
  60struct elf_rela {
  61        ulong *offset;
  62        ulong info;
  63        long addend;
  64};
  65
  66/*
  67 * EFI Runtime code lives in 2 stages. In the first stage, U-Boot and an EFI
  68 * payload are running concurrently at the same time. In this mode, we can
  69 * handle a good number of runtime callbacks
  70 */
  71
  72static void EFIAPI efi_reset_system_boottime(
  73                        enum efi_reset_type reset_type,
  74                        efi_status_t reset_status,
  75                        unsigned long data_size, void *reset_data)
  76{
  77        EFI_ENTRY("%d %lx %lx %p", reset_type, reset_status, data_size,
  78                  reset_data);
  79
  80        switch (reset_type) {
  81        case EFI_RESET_COLD:
  82        case EFI_RESET_WARM:
  83                do_reset(NULL, 0, 0, NULL);
  84                break;
  85        case EFI_RESET_SHUTDOWN:
  86                /* We don't have anything to map this to */
  87                break;
  88        }
  89
  90        while (1) { }
  91}
  92
  93static efi_status_t EFIAPI efi_get_time_boottime(
  94                        struct efi_time *time,
  95                        struct efi_time_cap *capabilities)
  96{
  97#if defined(CONFIG_CMD_DATE) && defined(CONFIG_DM_RTC)
  98        struct rtc_time tm;
  99        int r;
 100        struct udevice *dev;
 101
 102        EFI_ENTRY("%p %p", time, capabilities);
 103
 104        r = uclass_get_device(UCLASS_RTC, 0, &dev);
 105        if (r)
 106                return EFI_EXIT(EFI_DEVICE_ERROR);
 107
 108        r = dm_rtc_get(dev, &tm);
 109        if (r)
 110                return EFI_EXIT(EFI_DEVICE_ERROR);
 111
 112        memset(time, 0, sizeof(*time));
 113        time->year = tm.tm_year;
 114        time->month = tm.tm_mon;
 115        time->day = tm.tm_mday;
 116        time->hour = tm.tm_hour;
 117        time->minute = tm.tm_min;
 118        time->daylight = tm.tm_isdst;
 119
 120        return EFI_EXIT(EFI_SUCCESS);
 121#else
 122        return EFI_DEVICE_ERROR;
 123#endif
 124}
 125
 126/* Boards may override the helpers below to implement RTS functionality */
 127
 128void __weak __efi_runtime EFIAPI efi_reset_system(
 129                        enum efi_reset_type reset_type,
 130                        efi_status_t reset_status,
 131                        unsigned long data_size, void *reset_data)
 132{
 133        /* Nothing we can do */
 134        while (1) { }
 135}
 136
 137void __weak efi_reset_system_init(void)
 138{
 139}
 140
 141efi_status_t __weak __efi_runtime EFIAPI efi_get_time(
 142                        struct efi_time *time,
 143                        struct efi_time_cap *capabilities)
 144{
 145        /* Nothing we can do */
 146        return EFI_DEVICE_ERROR;
 147}
 148
 149void __weak efi_get_time_init(void)
 150{
 151}
 152
 153struct efi_runtime_detach_list_struct {
 154        void *ptr;
 155        void *patchto;
 156};
 157
 158static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
 159        {
 160                /* do_reset is gone */
 161                .ptr = &efi_runtime_services.reset_system,
 162                .patchto = efi_reset_system,
 163        }, {
 164                /* invalidate_*cache_all are gone */
 165                .ptr = &efi_runtime_services.set_virtual_address_map,
 166                .patchto = &efi_invalid_parameter,
 167        }, {
 168                /* RTC accessors are gone */
 169                .ptr = &efi_runtime_services.get_time,
 170                .patchto = &efi_get_time,
 171        }, {
 172                /* Clean up system table */
 173                .ptr = &systab.con_in,
 174                .patchto = NULL,
 175        }, {
 176                /* Clean up system table */
 177                .ptr = &systab.con_out,
 178                .patchto = NULL,
 179        }, {
 180                /* Clean up system table */
 181                .ptr = &systab.std_err,
 182                .patchto = NULL,
 183        }, {
 184                /* Clean up system table */
 185                .ptr = &systab.boottime,
 186                .patchto = NULL,
 187        },
 188};
 189
 190static bool efi_runtime_tobedetached(void *p)
 191{
 192        int i;
 193
 194        for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++)
 195                if (efi_runtime_detach_list[i].ptr == p)
 196                        return true;
 197
 198        return false;
 199}
 200
 201static void efi_runtime_detach(ulong offset)
 202{
 203        int i;
 204        ulong patchoff = offset - (ulong)gd->relocaddr;
 205
 206        for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++) {
 207                ulong patchto = (ulong)efi_runtime_detach_list[i].patchto;
 208                ulong *p = efi_runtime_detach_list[i].ptr;
 209                ulong newaddr = patchto ? (patchto + patchoff) : 0;
 210
 211                debug("%s: Setting %p to %lx\n", __func__, p, newaddr);
 212                *p = newaddr;
 213        }
 214}
 215
 216/* Relocate EFI runtime to uboot_reloc_base = offset */
 217void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map)
 218{
 219#ifdef IS_RELA
 220        struct elf_rela *rel = (void*)&__efi_runtime_rel_start;
 221#else
 222        struct elf_rel *rel = (void*)&__efi_runtime_rel_start;
 223        static ulong lastoff = CONFIG_SYS_TEXT_BASE;
 224#endif
 225
 226        debug("%s: Relocating to offset=%lx\n", __func__, offset);
 227        for (; (ulong)rel < (ulong)&__efi_runtime_rel_stop; rel++) {
 228                ulong base = CONFIG_SYS_TEXT_BASE;
 229                ulong *p;
 230                ulong newaddr;
 231
 232                p = (void*)((ulong)rel->offset - base) + gd->relocaddr;
 233
 234                if ((rel->info & R_MASK) != R_RELATIVE) {
 235                        continue;
 236                }
 237
 238#ifdef IS_RELA
 239                newaddr = rel->addend + offset - CONFIG_SYS_TEXT_BASE;
 240#else
 241                newaddr = *p - lastoff + offset;
 242#endif
 243
 244                /* Check if the relocation is inside bounds */
 245                if (map && ((newaddr < map->virtual_start) ||
 246                    newaddr > (map->virtual_start + (map->num_pages << 12)))) {
 247                        if (!efi_runtime_tobedetached(p))
 248                                printf("U-Boot EFI: Relocation at %p is out of "
 249                                       "range (%lx)\n", p, newaddr);
 250                        continue;
 251                }
 252
 253                debug("%s: Setting %p to %lx\n", __func__, p, newaddr);
 254                *p = newaddr;
 255                flush_dcache_range((ulong)p & ~(EFI_CACHELINE_SIZE - 1),
 256                        ALIGN((ulong)&p[1], EFI_CACHELINE_SIZE));
 257        }
 258
 259#ifndef IS_RELA
 260        lastoff = offset;
 261#endif
 262
 263        invalidate_icache_all();
 264}
 265
 266static efi_status_t EFIAPI efi_set_virtual_address_map(
 267                        unsigned long memory_map_size,
 268                        unsigned long descriptor_size,
 269                        uint32_t descriptor_version,
 270                        struct efi_mem_desc *virtmap)
 271{
 272        ulong runtime_start = (ulong)&__efi_runtime_start & ~0xfffULL;
 273        int n = memory_map_size / descriptor_size;
 274        int i;
 275
 276        EFI_ENTRY("%lx %lx %x %p", memory_map_size, descriptor_size,
 277                  descriptor_version, virtmap);
 278
 279        /* Rebind mmio pointers */
 280        for (i = 0; i < n; i++) {
 281                struct efi_mem_desc *map = (void*)virtmap +
 282                                           (descriptor_size * i);
 283                struct list_head *lhandle;
 284                efi_physical_addr_t map_start = map->physical_start;
 285                efi_physical_addr_t map_len = map->num_pages << EFI_PAGE_SHIFT;
 286                efi_physical_addr_t map_end = map_start + map_len;
 287
 288                /* Adjust all mmio pointers in this region */
 289                list_for_each(lhandle, &efi_runtime_mmio) {
 290                        struct efi_runtime_mmio_list *lmmio;
 291
 292                        lmmio = list_entry(lhandle,
 293                                           struct efi_runtime_mmio_list,
 294                                           link);
 295                        if ((map_start <= lmmio->paddr) &&
 296                            (map_end >= lmmio->paddr)) {
 297                                u64 off = map->virtual_start - map_start;
 298                                uintptr_t new_addr = lmmio->paddr + off;
 299                                *lmmio->ptr = (void *)new_addr;
 300                        }
 301                }
 302        }
 303
 304        /* Move the actual runtime code over */
 305        for (i = 0; i < n; i++) {
 306                struct efi_mem_desc *map;
 307
 308                map = (void*)virtmap + (descriptor_size * i);
 309                if (map->type == EFI_RUNTIME_SERVICES_CODE) {
 310                        ulong new_offset = map->virtual_start -
 311                                           (runtime_start - gd->relocaddr);
 312
 313                        efi_runtime_relocate(new_offset, map);
 314                        /* Once we're virtual, we can no longer handle
 315                           complex callbacks */
 316                        efi_runtime_detach(new_offset);
 317                        return EFI_EXIT(EFI_SUCCESS);
 318                }
 319        }
 320
 321        return EFI_EXIT(EFI_INVALID_PARAMETER);
 322}
 323
 324void efi_add_runtime_mmio(void *mmio_ptr, u64 len)
 325{
 326        struct efi_runtime_mmio_list *newmmio;
 327
 328        u64 pages = (len + EFI_PAGE_SIZE - 1) >> EFI_PAGE_SHIFT;
 329        efi_add_memory_map(*(uintptr_t *)mmio_ptr, pages, EFI_MMAP_IO, false);
 330
 331        newmmio = calloc(1, sizeof(*newmmio));
 332        newmmio->ptr = mmio_ptr;
 333        newmmio->paddr = *(uintptr_t *)mmio_ptr;
 334        newmmio->len = len;
 335        list_add_tail(&newmmio->link, &efi_runtime_mmio);
 336}
 337
 338/*
 339 * In the second stage, U-Boot has disappeared. To isolate our runtime code
 340 * that at this point still exists from the rest, we put it into a special
 341 * section.
 342 *
 343 *        !!WARNING!!
 344 *
 345 * This means that we can not rely on any code outside of this file in any
 346 * function or variable below this line.
 347 *
 348 * Please keep everything fully self-contained and annotated with
 349 * __efi_runtime and __efi_runtime_data markers.
 350 */
 351
 352/*
 353 * Relocate the EFI runtime stub to a different place. We need to call this
 354 * the first time we expose the runtime interface to a user and on set virtual
 355 * address map calls.
 356 */
 357
 358static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void)
 359{
 360        return EFI_UNSUPPORTED;
 361}
 362
 363static efi_status_t __efi_runtime EFIAPI efi_device_error(void)
 364{
 365        return EFI_DEVICE_ERROR;
 366}
 367
 368static efi_status_t __efi_runtime EFIAPI efi_invalid_parameter(void)
 369{
 370        return EFI_INVALID_PARAMETER;
 371}
 372
 373struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
 374        .hdr = {
 375                .signature = EFI_RUNTIME_SERVICES_SIGNATURE,
 376                .revision = EFI_RUNTIME_SERVICES_REVISION,
 377                .headersize = sizeof(struct efi_table_hdr),
 378        },
 379        .get_time = &efi_get_time_boottime,
 380        .set_time = (void *)&efi_device_error,
 381        .get_wakeup_time = (void *)&efi_unimplemented,
 382        .set_wakeup_time = (void *)&efi_unimplemented,
 383        .set_virtual_address_map = &efi_set_virtual_address_map,
 384        .convert_pointer = (void *)&efi_invalid_parameter,
 385        .get_variable = (void *)&efi_device_error,
 386        .get_next_variable = (void *)&efi_device_error,
 387        .set_variable = (void *)&efi_device_error,
 388        .get_next_high_mono_count = (void *)&efi_device_error,
 389        .reset_system = &efi_reset_system_boottime,
 390};
 391