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