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 2336c37a84SAlexander Graf #ifdef CONFIG_SYS_CACHELINE_SIZE 2436c37a84SAlexander Graf #define EFI_CACHELINE_SIZE CONFIG_SYS_CACHELINE_SIZE 2536c37a84SAlexander Graf #else 2636c37a84SAlexander Graf /* Just use the greatest cache flush alignment requirement I'm aware of */ 2736c37a84SAlexander Graf #define EFI_CACHELINE_SIZE 128 2836c37a84SAlexander Graf #endif 2936c37a84SAlexander 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, 128ae874405SAlexander Graf }, { 129ae874405SAlexander Graf /* Clean up system table */ 130ae874405SAlexander Graf .ptr = &systab.con_in, 131ae874405SAlexander Graf .patchto = NULL, 132ae874405SAlexander Graf }, { 133ae874405SAlexander Graf /* Clean up system table */ 134ae874405SAlexander Graf .ptr = &systab.con_out, 135ae874405SAlexander Graf .patchto = NULL, 136ae874405SAlexander Graf }, { 137ae874405SAlexander Graf /* Clean up system table */ 138ae874405SAlexander Graf .ptr = &systab.std_err, 139ae874405SAlexander Graf .patchto = NULL, 140ae874405SAlexander Graf }, { 141ae874405SAlexander Graf /* Clean up system table */ 142ae874405SAlexander Graf .ptr = &systab.boottime, 143ae874405SAlexander Graf .patchto = NULL, 14450149ea3SAlexander Graf }, 14550149ea3SAlexander Graf }; 14650149ea3SAlexander Graf 14750149ea3SAlexander Graf static bool efi_runtime_tobedetached(void *p) 14850149ea3SAlexander Graf { 14950149ea3SAlexander Graf int i; 15050149ea3SAlexander Graf 15150149ea3SAlexander Graf for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++) 15250149ea3SAlexander Graf if (efi_runtime_detach_list[i].ptr == p) 15350149ea3SAlexander Graf return true; 15450149ea3SAlexander Graf 15550149ea3SAlexander Graf return false; 15650149ea3SAlexander Graf } 15750149ea3SAlexander Graf 15850149ea3SAlexander Graf static void efi_runtime_detach(ulong offset) 15950149ea3SAlexander Graf { 16050149ea3SAlexander Graf int i; 16150149ea3SAlexander Graf ulong patchoff = offset - (ulong)gd->relocaddr; 16250149ea3SAlexander Graf 16350149ea3SAlexander Graf for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++) { 16450149ea3SAlexander Graf ulong patchto = (ulong)efi_runtime_detach_list[i].patchto; 16550149ea3SAlexander Graf ulong *p = efi_runtime_detach_list[i].ptr; 16650149ea3SAlexander Graf ulong newaddr = patchto ? (patchto + patchoff) : 0; 16750149ea3SAlexander Graf 168*edcef3baSAlexander Graf debug("%s: Setting %p to %lx\n", __func__, p, newaddr); 16950149ea3SAlexander Graf *p = newaddr; 17050149ea3SAlexander Graf } 17150149ea3SAlexander Graf } 17250149ea3SAlexander Graf 17350149ea3SAlexander Graf /* Relocate EFI runtime to uboot_reloc_base = offset */ 17450149ea3SAlexander Graf void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map) 17550149ea3SAlexander Graf { 17650149ea3SAlexander Graf #ifdef IS_RELA 17750149ea3SAlexander Graf struct elf_rela *rel = (void*)&__efi_runtime_rel_start; 17850149ea3SAlexander Graf #else 17950149ea3SAlexander Graf struct elf_rel *rel = (void*)&__efi_runtime_rel_start; 18050149ea3SAlexander Graf static ulong lastoff = CONFIG_SYS_TEXT_BASE; 18150149ea3SAlexander Graf #endif 18250149ea3SAlexander Graf 183*edcef3baSAlexander Graf debug("%s: Relocating to offset=%lx\n", __func__, offset); 18450149ea3SAlexander Graf for (; (ulong)rel < (ulong)&__efi_runtime_rel_stop; rel++) { 18550149ea3SAlexander Graf ulong base = CONFIG_SYS_TEXT_BASE; 18650149ea3SAlexander Graf ulong *p; 18750149ea3SAlexander Graf ulong newaddr; 18850149ea3SAlexander Graf 18950149ea3SAlexander Graf p = (void*)((ulong)rel->offset - base) + gd->relocaddr; 19050149ea3SAlexander Graf 19150149ea3SAlexander Graf if ((rel->info & R_MASK) != R_RELATIVE) { 19250149ea3SAlexander Graf continue; 19350149ea3SAlexander Graf } 19450149ea3SAlexander Graf 19550149ea3SAlexander Graf #ifdef IS_RELA 19650149ea3SAlexander Graf newaddr = rel->addend + offset - CONFIG_SYS_TEXT_BASE; 19750149ea3SAlexander Graf #else 19850149ea3SAlexander Graf newaddr = *p - lastoff + offset; 19950149ea3SAlexander Graf #endif 20050149ea3SAlexander Graf 20150149ea3SAlexander Graf /* Check if the relocation is inside bounds */ 20250149ea3SAlexander Graf if (map && ((newaddr < map->virtual_start) || 20350149ea3SAlexander Graf newaddr > (map->virtual_start + (map->num_pages << 12)))) { 20450149ea3SAlexander Graf if (!efi_runtime_tobedetached(p)) 20550149ea3SAlexander Graf printf("U-Boot EFI: Relocation at %p is out of " 20650149ea3SAlexander Graf "range (%lx)\n", p, newaddr); 20750149ea3SAlexander Graf continue; 20850149ea3SAlexander Graf } 20950149ea3SAlexander Graf 210*edcef3baSAlexander Graf debug("%s: Setting %p to %lx\n", __func__, p, newaddr); 21150149ea3SAlexander Graf *p = newaddr; 21236c37a84SAlexander Graf flush_dcache_range((ulong)p & ~(EFI_CACHELINE_SIZE - 1), 21336c37a84SAlexander Graf ALIGN((ulong)&p[1], EFI_CACHELINE_SIZE)); 21450149ea3SAlexander Graf } 21550149ea3SAlexander Graf 21650149ea3SAlexander Graf #ifndef IS_RELA 21750149ea3SAlexander Graf lastoff = offset; 21850149ea3SAlexander Graf #endif 21950149ea3SAlexander Graf 22050149ea3SAlexander Graf invalidate_icache_all(); 22150149ea3SAlexander Graf } 22250149ea3SAlexander Graf 22350149ea3SAlexander Graf static efi_status_t EFIAPI efi_set_virtual_address_map( 22450149ea3SAlexander Graf unsigned long memory_map_size, 22550149ea3SAlexander Graf unsigned long descriptor_size, 22650149ea3SAlexander Graf uint32_t descriptor_version, 22750149ea3SAlexander Graf struct efi_mem_desc *virtmap) 22850149ea3SAlexander Graf { 22950149ea3SAlexander Graf ulong runtime_start = (ulong)&__efi_runtime_start & ~0xfffULL; 23050149ea3SAlexander Graf int n = memory_map_size / descriptor_size; 23150149ea3SAlexander Graf int i; 23250149ea3SAlexander Graf 23350149ea3SAlexander Graf EFI_ENTRY("%lx %lx %x %p", memory_map_size, descriptor_size, 23450149ea3SAlexander Graf descriptor_version, virtmap); 23550149ea3SAlexander Graf 23650149ea3SAlexander Graf for (i = 0; i < n; i++) { 23750149ea3SAlexander Graf struct efi_mem_desc *map; 23850149ea3SAlexander Graf 23950149ea3SAlexander Graf map = (void*)virtmap + (descriptor_size * i); 24050149ea3SAlexander Graf if (map->type == EFI_RUNTIME_SERVICES_CODE) { 24150149ea3SAlexander Graf ulong new_offset = map->virtual_start - (runtime_start - gd->relocaddr); 24250149ea3SAlexander Graf 24350149ea3SAlexander Graf efi_runtime_relocate(new_offset, map); 24450149ea3SAlexander Graf /* Once we're virtual, we can no longer handle 24550149ea3SAlexander Graf complex callbacks */ 24650149ea3SAlexander Graf efi_runtime_detach(new_offset); 24750149ea3SAlexander Graf return EFI_EXIT(EFI_SUCCESS); 24850149ea3SAlexander Graf } 24950149ea3SAlexander Graf } 25050149ea3SAlexander Graf 25150149ea3SAlexander Graf return EFI_EXIT(EFI_INVALID_PARAMETER); 25250149ea3SAlexander Graf } 25350149ea3SAlexander Graf 25450149ea3SAlexander Graf /* 25550149ea3SAlexander Graf * In the second stage, U-Boot has disappeared. To isolate our runtime code 25650149ea3SAlexander Graf * that at this point still exists from the rest, we put it into a special 25750149ea3SAlexander Graf * section. 25850149ea3SAlexander Graf * 25950149ea3SAlexander Graf * !!WARNING!! 26050149ea3SAlexander Graf * 26150149ea3SAlexander Graf * This means that we can not rely on any code outside of this file in any 26250149ea3SAlexander Graf * function or variable below this line. 26350149ea3SAlexander Graf * 26450149ea3SAlexander Graf * Please keep everything fully self-contained and annotated with 26550149ea3SAlexander Graf * EFI_RUNTIME_TEXT and EFI_RUNTIME_DATA markers. 26650149ea3SAlexander Graf */ 26750149ea3SAlexander Graf 26850149ea3SAlexander Graf /* 26950149ea3SAlexander Graf * Relocate the EFI runtime stub to a different place. We need to call this 27050149ea3SAlexander Graf * the first time we expose the runtime interface to a user and on set virtual 27150149ea3SAlexander Graf * address map calls. 27250149ea3SAlexander Graf */ 27350149ea3SAlexander Graf 27450149ea3SAlexander Graf static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_unimplemented(void) 27550149ea3SAlexander Graf { 27650149ea3SAlexander Graf return EFI_UNSUPPORTED; 27750149ea3SAlexander Graf } 27850149ea3SAlexander Graf 27950149ea3SAlexander Graf static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_device_error(void) 28050149ea3SAlexander Graf { 28150149ea3SAlexander Graf return EFI_DEVICE_ERROR; 28250149ea3SAlexander Graf } 28350149ea3SAlexander Graf 28450149ea3SAlexander Graf static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_invalid_parameter(void) 28550149ea3SAlexander Graf { 28650149ea3SAlexander Graf return EFI_INVALID_PARAMETER; 28750149ea3SAlexander Graf } 28850149ea3SAlexander Graf 28950149ea3SAlexander Graf struct efi_runtime_services EFI_RUNTIME_DATA efi_runtime_services = { 29050149ea3SAlexander Graf .hdr = { 29150149ea3SAlexander Graf .signature = EFI_RUNTIME_SERVICES_SIGNATURE, 29250149ea3SAlexander Graf .revision = EFI_RUNTIME_SERVICES_REVISION, 29350149ea3SAlexander Graf .headersize = sizeof(struct efi_table_hdr), 29450149ea3SAlexander Graf }, 29550149ea3SAlexander Graf .get_time = &efi_get_time, 29650149ea3SAlexander Graf .set_time = (void *)&efi_device_error, 29750149ea3SAlexander Graf .get_wakeup_time = (void *)&efi_unimplemented, 29850149ea3SAlexander Graf .set_wakeup_time = (void *)&efi_unimplemented, 29950149ea3SAlexander Graf .set_virtual_address_map = &efi_set_virtual_address_map, 30050149ea3SAlexander Graf .convert_pointer = (void *)&efi_invalid_parameter, 30150149ea3SAlexander Graf .get_variable = (void *)&efi_device_error, 30250149ea3SAlexander Graf .get_next_variable = (void *)&efi_device_error, 30350149ea3SAlexander Graf .set_variable = (void *)&efi_device_error, 30450149ea3SAlexander Graf .get_next_high_mono_count = (void *)&efi_device_error, 30550149ea3SAlexander Graf .reset_system = &efi_reset_system, 30650149ea3SAlexander Graf }; 307