xref: /rk3399_rockchip-uboot/lib/efi_loader/efi_runtime.c (revision 36c37a8481551a6958fd91ccafc6936bf81e00f3)
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