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