150149ea3SAlexander Graf /* 250149ea3SAlexander Graf * EFI application runtime services 350149ea3SAlexander Graf * 450149ea3SAlexander Graf * Copyright (c) 2016 Alexander Graf 550149ea3SAlexander Graf * 650149ea3SAlexander Graf * SPDX-License-Identifier: GPL-2.0+ 750149ea3SAlexander Graf */ 850149ea3SAlexander Graf 950149ea3SAlexander Graf #include <common.h> 1050149ea3SAlexander Graf #include <command.h> 1150149ea3SAlexander Graf #include <dm.h> 1250149ea3SAlexander Graf #include <efi_loader.h> 1350149ea3SAlexander Graf #include <rtc.h> 1450149ea3SAlexander Graf #include <asm/global_data.h> 1550149ea3SAlexander Graf 1650149ea3SAlexander Graf /* For manual relocation support */ 1750149ea3SAlexander Graf DECLARE_GLOBAL_DATA_PTR; 1850149ea3SAlexander Graf 1950149ea3SAlexander Graf static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_unimplemented(void); 2050149ea3SAlexander Graf static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_device_error(void); 2150149ea3SAlexander Graf static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_invalid_parameter(void); 2250149ea3SAlexander Graf 23*36c37a84SAlexander Graf #ifdef CONFIG_SYS_CACHELINE_SIZE 24*36c37a84SAlexander Graf #define EFI_CACHELINE_SIZE CONFIG_SYS_CACHELINE_SIZE 25*36c37a84SAlexander Graf #else 26*36c37a84SAlexander Graf /* Just use the greatest cache flush alignment requirement I'm aware of */ 27*36c37a84SAlexander Graf #define EFI_CACHELINE_SIZE 128 28*36c37a84SAlexander Graf #endif 29*36c37a84SAlexander Graf 3050149ea3SAlexander Graf #if defined(CONFIG_ARM64) 3150149ea3SAlexander Graf #define R_RELATIVE 1027 3250149ea3SAlexander Graf #define R_MASK 0xffffffffULL 3350149ea3SAlexander Graf #define IS_RELA 1 3450149ea3SAlexander Graf #elif defined(CONFIG_ARM) 3550149ea3SAlexander Graf #define R_RELATIVE 23 3650149ea3SAlexander Graf #define R_MASK 0xffULL 3750149ea3SAlexander Graf #else 3850149ea3SAlexander Graf #error Need to add relocation awareness 3950149ea3SAlexander Graf #endif 4050149ea3SAlexander Graf 4150149ea3SAlexander Graf struct elf_rel { 4250149ea3SAlexander Graf ulong *offset; 4350149ea3SAlexander Graf ulong info; 4450149ea3SAlexander Graf }; 4550149ea3SAlexander Graf 4650149ea3SAlexander Graf struct elf_rela { 4750149ea3SAlexander Graf ulong *offset; 4850149ea3SAlexander Graf ulong info; 4950149ea3SAlexander Graf long addend; 5050149ea3SAlexander Graf }; 5150149ea3SAlexander Graf 5250149ea3SAlexander Graf /* 5350149ea3SAlexander Graf * EFI Runtime code lives in 2 stages. In the first stage, U-Boot and an EFI 5450149ea3SAlexander Graf * payload are running concurrently at the same time. In this mode, we can 5550149ea3SAlexander Graf * handle a good number of runtime callbacks 5650149ea3SAlexander Graf */ 5750149ea3SAlexander Graf 5850149ea3SAlexander Graf static void EFIAPI efi_reset_system(enum efi_reset_type reset_type, 5950149ea3SAlexander Graf efi_status_t reset_status, 6050149ea3SAlexander Graf unsigned long data_size, void *reset_data) 6150149ea3SAlexander Graf { 6250149ea3SAlexander Graf EFI_ENTRY("%d %lx %lx %p", reset_type, reset_status, data_size, 6350149ea3SAlexander Graf reset_data); 6450149ea3SAlexander Graf 6550149ea3SAlexander Graf switch (reset_type) { 6650149ea3SAlexander Graf case EFI_RESET_COLD: 6750149ea3SAlexander Graf case EFI_RESET_WARM: 6850149ea3SAlexander Graf do_reset(NULL, 0, 0, NULL); 6950149ea3SAlexander Graf break; 7050149ea3SAlexander Graf case EFI_RESET_SHUTDOWN: 7150149ea3SAlexander Graf /* We don't have anything to map this to */ 7250149ea3SAlexander Graf break; 7350149ea3SAlexander Graf } 7450149ea3SAlexander Graf 7550149ea3SAlexander Graf EFI_EXIT(EFI_SUCCESS); 7650149ea3SAlexander Graf } 7750149ea3SAlexander Graf 7850149ea3SAlexander Graf static efi_status_t EFIAPI efi_get_time(struct efi_time *time, 7950149ea3SAlexander Graf struct efi_time_cap *capabilities) 8050149ea3SAlexander Graf { 8150149ea3SAlexander Graf #if defined(CONFIG_CMD_DATE) && defined(CONFIG_DM_RTC) 8250149ea3SAlexander Graf struct rtc_time tm; 8350149ea3SAlexander Graf int r; 8450149ea3SAlexander Graf struct udevice *dev; 8550149ea3SAlexander Graf 8650149ea3SAlexander Graf EFI_ENTRY("%p %p", time, capabilities); 8750149ea3SAlexander Graf 8850149ea3SAlexander Graf r = uclass_get_device(UCLASS_RTC, 0, &dev); 8950149ea3SAlexander Graf if (r) 9050149ea3SAlexander Graf return EFI_EXIT(EFI_DEVICE_ERROR); 9150149ea3SAlexander Graf 9250149ea3SAlexander Graf r = dm_rtc_get(dev, &tm); 9350149ea3SAlexander Graf if (r) 9450149ea3SAlexander Graf return EFI_EXIT(EFI_DEVICE_ERROR); 9550149ea3SAlexander Graf 9650149ea3SAlexander Graf memset(time, 0, sizeof(*time)); 9750149ea3SAlexander Graf time->year = tm.tm_year; 9850149ea3SAlexander Graf time->month = tm.tm_mon; 9950149ea3SAlexander Graf time->day = tm.tm_mday; 10050149ea3SAlexander Graf time->hour = tm.tm_hour; 10150149ea3SAlexander Graf time->minute = tm.tm_min; 10250149ea3SAlexander Graf time->daylight = tm.tm_isdst; 10350149ea3SAlexander Graf 10450149ea3SAlexander Graf return EFI_EXIT(EFI_SUCCESS); 10550149ea3SAlexander Graf #else 10650149ea3SAlexander Graf return EFI_DEVICE_ERROR; 10750149ea3SAlexander Graf #endif 10850149ea3SAlexander Graf } 10950149ea3SAlexander Graf 11050149ea3SAlexander Graf struct efi_runtime_detach_list_struct { 11150149ea3SAlexander Graf void *ptr; 11250149ea3SAlexander Graf void *patchto; 11350149ea3SAlexander Graf }; 11450149ea3SAlexander Graf 11550149ea3SAlexander Graf static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = { 11650149ea3SAlexander Graf { 11750149ea3SAlexander Graf /* do_reset is gone */ 11850149ea3SAlexander Graf .ptr = &efi_runtime_services.reset_system, 11950149ea3SAlexander Graf .patchto = NULL, 12050149ea3SAlexander Graf }, { 12150149ea3SAlexander Graf /* invalidate_*cache_all are gone */ 12250149ea3SAlexander Graf .ptr = &efi_runtime_services.set_virtual_address_map, 12350149ea3SAlexander Graf .patchto = &efi_invalid_parameter, 12450149ea3SAlexander Graf }, { 12550149ea3SAlexander Graf /* RTC accessors are gone */ 12650149ea3SAlexander Graf .ptr = &efi_runtime_services.get_time, 12750149ea3SAlexander Graf .patchto = &efi_device_error, 12850149ea3SAlexander Graf }, 12950149ea3SAlexander Graf }; 13050149ea3SAlexander Graf 13150149ea3SAlexander Graf static bool efi_runtime_tobedetached(void *p) 13250149ea3SAlexander Graf { 13350149ea3SAlexander Graf int i; 13450149ea3SAlexander Graf 13550149ea3SAlexander Graf for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++) 13650149ea3SAlexander Graf if (efi_runtime_detach_list[i].ptr == p) 13750149ea3SAlexander Graf return true; 13850149ea3SAlexander Graf 13950149ea3SAlexander Graf return false; 14050149ea3SAlexander Graf } 14150149ea3SAlexander Graf 14250149ea3SAlexander Graf static void efi_runtime_detach(ulong offset) 14350149ea3SAlexander Graf { 14450149ea3SAlexander Graf int i; 14550149ea3SAlexander Graf ulong patchoff = offset - (ulong)gd->relocaddr; 14650149ea3SAlexander Graf 14750149ea3SAlexander Graf for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++) { 14850149ea3SAlexander Graf ulong patchto = (ulong)efi_runtime_detach_list[i].patchto; 14950149ea3SAlexander Graf ulong *p = efi_runtime_detach_list[i].ptr; 15050149ea3SAlexander Graf ulong newaddr = patchto ? (patchto + patchoff) : 0; 15150149ea3SAlexander Graf 15250149ea3SAlexander Graf #ifdef DEBUG_EFI 15350149ea3SAlexander Graf printf("%s: Setting %p to %lx\n", __func__, p, newaddr); 15450149ea3SAlexander Graf #endif 15550149ea3SAlexander Graf *p = newaddr; 15650149ea3SAlexander Graf } 15750149ea3SAlexander Graf } 15850149ea3SAlexander Graf 15950149ea3SAlexander Graf /* Relocate EFI runtime to uboot_reloc_base = offset */ 16050149ea3SAlexander Graf void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map) 16150149ea3SAlexander Graf { 16250149ea3SAlexander Graf #ifdef IS_RELA 16350149ea3SAlexander Graf struct elf_rela *rel = (void*)&__efi_runtime_rel_start; 16450149ea3SAlexander Graf #else 16550149ea3SAlexander Graf struct elf_rel *rel = (void*)&__efi_runtime_rel_start; 16650149ea3SAlexander Graf static ulong lastoff = CONFIG_SYS_TEXT_BASE; 16750149ea3SAlexander Graf #endif 16850149ea3SAlexander Graf 16950149ea3SAlexander Graf #ifdef DEBUG_EFI 17050149ea3SAlexander Graf printf("%s: Relocating to offset=%lx\n", __func__, offset); 17150149ea3SAlexander Graf #endif 17250149ea3SAlexander Graf 17350149ea3SAlexander Graf for (; (ulong)rel < (ulong)&__efi_runtime_rel_stop; rel++) { 17450149ea3SAlexander Graf ulong base = CONFIG_SYS_TEXT_BASE; 17550149ea3SAlexander Graf ulong *p; 17650149ea3SAlexander Graf ulong newaddr; 17750149ea3SAlexander Graf 17850149ea3SAlexander Graf p = (void*)((ulong)rel->offset - base) + gd->relocaddr; 17950149ea3SAlexander Graf 18050149ea3SAlexander Graf if ((rel->info & R_MASK) != R_RELATIVE) { 18150149ea3SAlexander Graf continue; 18250149ea3SAlexander Graf } 18350149ea3SAlexander Graf 18450149ea3SAlexander Graf #ifdef IS_RELA 18550149ea3SAlexander Graf newaddr = rel->addend + offset - CONFIG_SYS_TEXT_BASE; 18650149ea3SAlexander Graf #else 18750149ea3SAlexander Graf newaddr = *p - lastoff + offset; 18850149ea3SAlexander Graf #endif 18950149ea3SAlexander Graf 19050149ea3SAlexander Graf /* Check if the relocation is inside bounds */ 19150149ea3SAlexander Graf if (map && ((newaddr < map->virtual_start) || 19250149ea3SAlexander Graf newaddr > (map->virtual_start + (map->num_pages << 12)))) { 19350149ea3SAlexander Graf if (!efi_runtime_tobedetached(p)) 19450149ea3SAlexander Graf printf("U-Boot EFI: Relocation at %p is out of " 19550149ea3SAlexander Graf "range (%lx)\n", p, newaddr); 19650149ea3SAlexander Graf continue; 19750149ea3SAlexander Graf } 19850149ea3SAlexander Graf 19950149ea3SAlexander Graf #ifdef DEBUG_EFI 20050149ea3SAlexander Graf printf("%s: Setting %p to %lx\n", __func__, p, newaddr); 20150149ea3SAlexander Graf #endif 20250149ea3SAlexander Graf 20350149ea3SAlexander Graf *p = newaddr; 204*36c37a84SAlexander Graf flush_dcache_range((ulong)p & ~(EFI_CACHELINE_SIZE - 1), 205*36c37a84SAlexander Graf ALIGN((ulong)&p[1], EFI_CACHELINE_SIZE)); 20650149ea3SAlexander Graf } 20750149ea3SAlexander Graf 20850149ea3SAlexander Graf #ifndef IS_RELA 20950149ea3SAlexander Graf lastoff = offset; 21050149ea3SAlexander Graf #endif 21150149ea3SAlexander Graf 21250149ea3SAlexander Graf invalidate_icache_all(); 21350149ea3SAlexander Graf } 21450149ea3SAlexander Graf 21550149ea3SAlexander Graf static efi_status_t EFIAPI efi_set_virtual_address_map( 21650149ea3SAlexander Graf unsigned long memory_map_size, 21750149ea3SAlexander Graf unsigned long descriptor_size, 21850149ea3SAlexander Graf uint32_t descriptor_version, 21950149ea3SAlexander Graf struct efi_mem_desc *virtmap) 22050149ea3SAlexander Graf { 22150149ea3SAlexander Graf ulong runtime_start = (ulong)&__efi_runtime_start & ~0xfffULL; 22250149ea3SAlexander Graf int n = memory_map_size / descriptor_size; 22350149ea3SAlexander Graf int i; 22450149ea3SAlexander Graf 22550149ea3SAlexander Graf EFI_ENTRY("%lx %lx %x %p", memory_map_size, descriptor_size, 22650149ea3SAlexander Graf descriptor_version, virtmap); 22750149ea3SAlexander Graf 22850149ea3SAlexander Graf for (i = 0; i < n; i++) { 22950149ea3SAlexander Graf struct efi_mem_desc *map; 23050149ea3SAlexander Graf 23150149ea3SAlexander Graf map = (void*)virtmap + (descriptor_size * i); 23250149ea3SAlexander Graf if (map->type == EFI_RUNTIME_SERVICES_CODE) { 23350149ea3SAlexander Graf ulong new_offset = map->virtual_start - (runtime_start - gd->relocaddr); 23450149ea3SAlexander Graf 23550149ea3SAlexander Graf efi_runtime_relocate(new_offset, map); 23650149ea3SAlexander Graf /* Once we're virtual, we can no longer handle 23750149ea3SAlexander Graf complex callbacks */ 23850149ea3SAlexander Graf efi_runtime_detach(new_offset); 23950149ea3SAlexander Graf return EFI_EXIT(EFI_SUCCESS); 24050149ea3SAlexander Graf } 24150149ea3SAlexander Graf } 24250149ea3SAlexander Graf 24350149ea3SAlexander Graf return EFI_EXIT(EFI_INVALID_PARAMETER); 24450149ea3SAlexander Graf } 24550149ea3SAlexander Graf 24650149ea3SAlexander Graf /* 24750149ea3SAlexander Graf * In the second stage, U-Boot has disappeared. To isolate our runtime code 24850149ea3SAlexander Graf * that at this point still exists from the rest, we put it into a special 24950149ea3SAlexander Graf * section. 25050149ea3SAlexander Graf * 25150149ea3SAlexander Graf * !!WARNING!! 25250149ea3SAlexander Graf * 25350149ea3SAlexander Graf * This means that we can not rely on any code outside of this file in any 25450149ea3SAlexander Graf * function or variable below this line. 25550149ea3SAlexander Graf * 25650149ea3SAlexander Graf * Please keep everything fully self-contained and annotated with 25750149ea3SAlexander Graf * EFI_RUNTIME_TEXT and EFI_RUNTIME_DATA markers. 25850149ea3SAlexander Graf */ 25950149ea3SAlexander Graf 26050149ea3SAlexander Graf /* 26150149ea3SAlexander Graf * Relocate the EFI runtime stub to a different place. We need to call this 26250149ea3SAlexander Graf * the first time we expose the runtime interface to a user and on set virtual 26350149ea3SAlexander Graf * address map calls. 26450149ea3SAlexander Graf */ 26550149ea3SAlexander Graf 26650149ea3SAlexander Graf static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_unimplemented(void) 26750149ea3SAlexander Graf { 26850149ea3SAlexander Graf return EFI_UNSUPPORTED; 26950149ea3SAlexander Graf } 27050149ea3SAlexander Graf 27150149ea3SAlexander Graf static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_device_error(void) 27250149ea3SAlexander Graf { 27350149ea3SAlexander Graf return EFI_DEVICE_ERROR; 27450149ea3SAlexander Graf } 27550149ea3SAlexander Graf 27650149ea3SAlexander Graf static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_invalid_parameter(void) 27750149ea3SAlexander Graf { 27850149ea3SAlexander Graf return EFI_INVALID_PARAMETER; 27950149ea3SAlexander Graf } 28050149ea3SAlexander Graf 28150149ea3SAlexander Graf struct efi_runtime_services EFI_RUNTIME_DATA efi_runtime_services = { 28250149ea3SAlexander Graf .hdr = { 28350149ea3SAlexander Graf .signature = EFI_RUNTIME_SERVICES_SIGNATURE, 28450149ea3SAlexander Graf .revision = EFI_RUNTIME_SERVICES_REVISION, 28550149ea3SAlexander Graf .headersize = sizeof(struct efi_table_hdr), 28650149ea3SAlexander Graf }, 28750149ea3SAlexander Graf .get_time = &efi_get_time, 28850149ea3SAlexander Graf .set_time = (void *)&efi_device_error, 28950149ea3SAlexander Graf .get_wakeup_time = (void *)&efi_unimplemented, 29050149ea3SAlexander Graf .set_wakeup_time = (void *)&efi_unimplemented, 29150149ea3SAlexander Graf .set_virtual_address_map = &efi_set_virtual_address_map, 29250149ea3SAlexander Graf .convert_pointer = (void *)&efi_invalid_parameter, 29350149ea3SAlexander Graf .get_variable = (void *)&efi_device_error, 29450149ea3SAlexander Graf .get_next_variable = (void *)&efi_device_error, 29550149ea3SAlexander Graf .set_variable = (void *)&efi_device_error, 29650149ea3SAlexander Graf .get_next_high_mono_count = (void *)&efi_device_error, 29750149ea3SAlexander Graf .reset_system = &efi_reset_system, 29850149ea3SAlexander Graf }; 299