xref: /rk3399_rockchip-uboot/lib/efi_loader/efi_runtime.c (revision 50149ea37a21dcbed675297f1536c31a7db39c19)
1*50149ea3SAlexander Graf /*
2*50149ea3SAlexander Graf  *  EFI application runtime services
3*50149ea3SAlexander Graf  *
4*50149ea3SAlexander Graf  *  Copyright (c) 2016 Alexander Graf
5*50149ea3SAlexander Graf  *
6*50149ea3SAlexander Graf  *  SPDX-License-Identifier:     GPL-2.0+
7*50149ea3SAlexander Graf  */
8*50149ea3SAlexander Graf 
9*50149ea3SAlexander Graf #include <common.h>
10*50149ea3SAlexander Graf #include <command.h>
11*50149ea3SAlexander Graf #include <dm.h>
12*50149ea3SAlexander Graf #include <efi_loader.h>
13*50149ea3SAlexander Graf #include <rtc.h>
14*50149ea3SAlexander Graf #include <asm/global_data.h>
15*50149ea3SAlexander Graf 
16*50149ea3SAlexander Graf /* For manual relocation support */
17*50149ea3SAlexander Graf DECLARE_GLOBAL_DATA_PTR;
18*50149ea3SAlexander Graf 
19*50149ea3SAlexander Graf static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_unimplemented(void);
20*50149ea3SAlexander Graf static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_device_error(void);
21*50149ea3SAlexander Graf static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_invalid_parameter(void);
22*50149ea3SAlexander Graf 
23*50149ea3SAlexander Graf #if defined(CONFIG_ARM64)
24*50149ea3SAlexander Graf #define R_RELATIVE	1027
25*50149ea3SAlexander Graf #define R_MASK		0xffffffffULL
26*50149ea3SAlexander Graf #define IS_RELA		1
27*50149ea3SAlexander Graf #elif defined(CONFIG_ARM)
28*50149ea3SAlexander Graf #define R_RELATIVE	23
29*50149ea3SAlexander Graf #define R_MASK		0xffULL
30*50149ea3SAlexander Graf #else
31*50149ea3SAlexander Graf #error Need to add relocation awareness
32*50149ea3SAlexander Graf #endif
33*50149ea3SAlexander Graf 
34*50149ea3SAlexander Graf struct elf_rel {
35*50149ea3SAlexander Graf 	ulong *offset;
36*50149ea3SAlexander Graf 	ulong info;
37*50149ea3SAlexander Graf };
38*50149ea3SAlexander Graf 
39*50149ea3SAlexander Graf struct elf_rela {
40*50149ea3SAlexander Graf 	ulong *offset;
41*50149ea3SAlexander Graf 	ulong info;
42*50149ea3SAlexander Graf 	long addend;
43*50149ea3SAlexander Graf };
44*50149ea3SAlexander Graf 
45*50149ea3SAlexander Graf /*
46*50149ea3SAlexander Graf  * EFI Runtime code lives in 2 stages. In the first stage, U-Boot and an EFI
47*50149ea3SAlexander Graf  * payload are running concurrently at the same time. In this mode, we can
48*50149ea3SAlexander Graf  * handle a good number of runtime callbacks
49*50149ea3SAlexander Graf  */
50*50149ea3SAlexander Graf 
51*50149ea3SAlexander Graf static void EFIAPI efi_reset_system(enum efi_reset_type reset_type,
52*50149ea3SAlexander Graf 				    efi_status_t reset_status,
53*50149ea3SAlexander Graf 				    unsigned long data_size, void *reset_data)
54*50149ea3SAlexander Graf {
55*50149ea3SAlexander Graf 	EFI_ENTRY("%d %lx %lx %p", reset_type, reset_status, data_size,
56*50149ea3SAlexander Graf 		  reset_data);
57*50149ea3SAlexander Graf 
58*50149ea3SAlexander Graf 	switch (reset_type) {
59*50149ea3SAlexander Graf 	case EFI_RESET_COLD:
60*50149ea3SAlexander Graf 	case EFI_RESET_WARM:
61*50149ea3SAlexander Graf 		do_reset(NULL, 0, 0, NULL);
62*50149ea3SAlexander Graf 		break;
63*50149ea3SAlexander Graf 	case EFI_RESET_SHUTDOWN:
64*50149ea3SAlexander Graf 		/* We don't have anything to map this to */
65*50149ea3SAlexander Graf 		break;
66*50149ea3SAlexander Graf 	}
67*50149ea3SAlexander Graf 
68*50149ea3SAlexander Graf 	EFI_EXIT(EFI_SUCCESS);
69*50149ea3SAlexander Graf }
70*50149ea3SAlexander Graf 
71*50149ea3SAlexander Graf static efi_status_t EFIAPI efi_get_time(struct efi_time *time,
72*50149ea3SAlexander Graf 					struct efi_time_cap *capabilities)
73*50149ea3SAlexander Graf {
74*50149ea3SAlexander Graf #if defined(CONFIG_CMD_DATE) && defined(CONFIG_DM_RTC)
75*50149ea3SAlexander Graf 	struct rtc_time tm;
76*50149ea3SAlexander Graf 	int r;
77*50149ea3SAlexander Graf 	struct udevice *dev;
78*50149ea3SAlexander Graf 
79*50149ea3SAlexander Graf 	EFI_ENTRY("%p %p", time, capabilities);
80*50149ea3SAlexander Graf 
81*50149ea3SAlexander Graf 	r = uclass_get_device(UCLASS_RTC, 0, &dev);
82*50149ea3SAlexander Graf 	if (r)
83*50149ea3SAlexander Graf 		return EFI_EXIT(EFI_DEVICE_ERROR);
84*50149ea3SAlexander Graf 
85*50149ea3SAlexander Graf 	r = dm_rtc_get(dev, &tm);
86*50149ea3SAlexander Graf 	if (r)
87*50149ea3SAlexander Graf 		return EFI_EXIT(EFI_DEVICE_ERROR);
88*50149ea3SAlexander Graf 
89*50149ea3SAlexander Graf 	memset(time, 0, sizeof(*time));
90*50149ea3SAlexander Graf 	time->year = tm.tm_year;
91*50149ea3SAlexander Graf 	time->month = tm.tm_mon;
92*50149ea3SAlexander Graf 	time->day = tm.tm_mday;
93*50149ea3SAlexander Graf 	time->hour = tm.tm_hour;
94*50149ea3SAlexander Graf 	time->minute = tm.tm_min;
95*50149ea3SAlexander Graf 	time->daylight = tm.tm_isdst;
96*50149ea3SAlexander Graf 
97*50149ea3SAlexander Graf 	return EFI_EXIT(EFI_SUCCESS);
98*50149ea3SAlexander Graf #else
99*50149ea3SAlexander Graf 	return EFI_DEVICE_ERROR;
100*50149ea3SAlexander Graf #endif
101*50149ea3SAlexander Graf }
102*50149ea3SAlexander Graf 
103*50149ea3SAlexander Graf struct efi_runtime_detach_list_struct {
104*50149ea3SAlexander Graf 	void *ptr;
105*50149ea3SAlexander Graf 	void *patchto;
106*50149ea3SAlexander Graf };
107*50149ea3SAlexander Graf 
108*50149ea3SAlexander Graf static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
109*50149ea3SAlexander Graf 	{
110*50149ea3SAlexander Graf 		/* do_reset is gone */
111*50149ea3SAlexander Graf 		.ptr = &efi_runtime_services.reset_system,
112*50149ea3SAlexander Graf 		.patchto = NULL,
113*50149ea3SAlexander Graf 	}, {
114*50149ea3SAlexander Graf 		/* invalidate_*cache_all are gone */
115*50149ea3SAlexander Graf 		.ptr = &efi_runtime_services.set_virtual_address_map,
116*50149ea3SAlexander Graf 		.patchto = &efi_invalid_parameter,
117*50149ea3SAlexander Graf 	}, {
118*50149ea3SAlexander Graf 		/* RTC accessors are gone */
119*50149ea3SAlexander Graf 		.ptr = &efi_runtime_services.get_time,
120*50149ea3SAlexander Graf 		.patchto = &efi_device_error,
121*50149ea3SAlexander Graf 	},
122*50149ea3SAlexander Graf };
123*50149ea3SAlexander Graf 
124*50149ea3SAlexander Graf static bool efi_runtime_tobedetached(void *p)
125*50149ea3SAlexander Graf {
126*50149ea3SAlexander Graf 	int i;
127*50149ea3SAlexander Graf 
128*50149ea3SAlexander Graf 	for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++)
129*50149ea3SAlexander Graf 		if (efi_runtime_detach_list[i].ptr == p)
130*50149ea3SAlexander Graf 			return true;
131*50149ea3SAlexander Graf 
132*50149ea3SAlexander Graf 	return false;
133*50149ea3SAlexander Graf }
134*50149ea3SAlexander Graf 
135*50149ea3SAlexander Graf static void efi_runtime_detach(ulong offset)
136*50149ea3SAlexander Graf {
137*50149ea3SAlexander Graf 	int i;
138*50149ea3SAlexander Graf 	ulong patchoff = offset - (ulong)gd->relocaddr;
139*50149ea3SAlexander Graf 
140*50149ea3SAlexander Graf 	for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++) {
141*50149ea3SAlexander Graf 		ulong patchto = (ulong)efi_runtime_detach_list[i].patchto;
142*50149ea3SAlexander Graf 		ulong *p = efi_runtime_detach_list[i].ptr;
143*50149ea3SAlexander Graf 		ulong newaddr = patchto ? (patchto + patchoff) : 0;
144*50149ea3SAlexander Graf 
145*50149ea3SAlexander Graf #ifdef DEBUG_EFI
146*50149ea3SAlexander Graf 		printf("%s: Setting %p to %lx\n", __func__, p, newaddr);
147*50149ea3SAlexander Graf #endif
148*50149ea3SAlexander Graf 		*p = newaddr;
149*50149ea3SAlexander Graf 	}
150*50149ea3SAlexander Graf }
151*50149ea3SAlexander Graf 
152*50149ea3SAlexander Graf /* Relocate EFI runtime to uboot_reloc_base = offset */
153*50149ea3SAlexander Graf void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map)
154*50149ea3SAlexander Graf {
155*50149ea3SAlexander Graf #ifdef IS_RELA
156*50149ea3SAlexander Graf 	struct elf_rela *rel = (void*)&__efi_runtime_rel_start;
157*50149ea3SAlexander Graf #else
158*50149ea3SAlexander Graf 	struct elf_rel *rel = (void*)&__efi_runtime_rel_start;
159*50149ea3SAlexander Graf 	static ulong lastoff = CONFIG_SYS_TEXT_BASE;
160*50149ea3SAlexander Graf #endif
161*50149ea3SAlexander Graf 
162*50149ea3SAlexander Graf #ifdef DEBUG_EFI
163*50149ea3SAlexander Graf 	printf("%s: Relocating to offset=%lx\n", __func__, offset);
164*50149ea3SAlexander Graf #endif
165*50149ea3SAlexander Graf 
166*50149ea3SAlexander Graf 	for (; (ulong)rel < (ulong)&__efi_runtime_rel_stop; rel++) {
167*50149ea3SAlexander Graf 		ulong base = CONFIG_SYS_TEXT_BASE;
168*50149ea3SAlexander Graf 		ulong *p;
169*50149ea3SAlexander Graf 		ulong newaddr;
170*50149ea3SAlexander Graf 
171*50149ea3SAlexander Graf 		p = (void*)((ulong)rel->offset - base) + gd->relocaddr;
172*50149ea3SAlexander Graf 
173*50149ea3SAlexander Graf 		if ((rel->info & R_MASK) != R_RELATIVE) {
174*50149ea3SAlexander Graf 			continue;
175*50149ea3SAlexander Graf 		}
176*50149ea3SAlexander Graf 
177*50149ea3SAlexander Graf #ifdef IS_RELA
178*50149ea3SAlexander Graf 		newaddr = rel->addend + offset - CONFIG_SYS_TEXT_BASE;
179*50149ea3SAlexander Graf #else
180*50149ea3SAlexander Graf 		newaddr = *p - lastoff + offset;
181*50149ea3SAlexander Graf #endif
182*50149ea3SAlexander Graf 
183*50149ea3SAlexander Graf 		/* Check if the relocation is inside bounds */
184*50149ea3SAlexander Graf 		if (map && ((newaddr < map->virtual_start) ||
185*50149ea3SAlexander Graf 		    newaddr > (map->virtual_start + (map->num_pages << 12)))) {
186*50149ea3SAlexander Graf 			if (!efi_runtime_tobedetached(p))
187*50149ea3SAlexander Graf 				printf("U-Boot EFI: Relocation at %p is out of "
188*50149ea3SAlexander Graf 				       "range (%lx)\n", p, newaddr);
189*50149ea3SAlexander Graf 			continue;
190*50149ea3SAlexander Graf 		}
191*50149ea3SAlexander Graf 
192*50149ea3SAlexander Graf #ifdef DEBUG_EFI
193*50149ea3SAlexander Graf 		printf("%s: Setting %p to %lx\n", __func__, p, newaddr);
194*50149ea3SAlexander Graf #endif
195*50149ea3SAlexander Graf 
196*50149ea3SAlexander Graf 		*p = newaddr;
197*50149ea3SAlexander Graf 		flush_dcache_range((ulong)p, (ulong)&p[1]);
198*50149ea3SAlexander Graf 	}
199*50149ea3SAlexander Graf 
200*50149ea3SAlexander Graf #ifndef IS_RELA
201*50149ea3SAlexander Graf 	lastoff = offset;
202*50149ea3SAlexander Graf #endif
203*50149ea3SAlexander Graf 
204*50149ea3SAlexander Graf         invalidate_icache_all();
205*50149ea3SAlexander Graf }
206*50149ea3SAlexander Graf 
207*50149ea3SAlexander Graf static efi_status_t EFIAPI efi_set_virtual_address_map(
208*50149ea3SAlexander Graf 			unsigned long memory_map_size,
209*50149ea3SAlexander Graf 			unsigned long descriptor_size,
210*50149ea3SAlexander Graf 			uint32_t descriptor_version,
211*50149ea3SAlexander Graf 			struct efi_mem_desc *virtmap)
212*50149ea3SAlexander Graf {
213*50149ea3SAlexander Graf 	ulong runtime_start = (ulong)&__efi_runtime_start & ~0xfffULL;
214*50149ea3SAlexander Graf 	int n = memory_map_size / descriptor_size;
215*50149ea3SAlexander Graf 	int i;
216*50149ea3SAlexander Graf 
217*50149ea3SAlexander Graf 	EFI_ENTRY("%lx %lx %x %p", memory_map_size, descriptor_size,
218*50149ea3SAlexander Graf 		  descriptor_version, virtmap);
219*50149ea3SAlexander Graf 
220*50149ea3SAlexander Graf 	for (i = 0; i < n; i++) {
221*50149ea3SAlexander Graf 		struct efi_mem_desc *map;
222*50149ea3SAlexander Graf 
223*50149ea3SAlexander Graf 		map = (void*)virtmap + (descriptor_size * i);
224*50149ea3SAlexander Graf 		if (map->type == EFI_RUNTIME_SERVICES_CODE) {
225*50149ea3SAlexander Graf 			ulong new_offset = map->virtual_start - (runtime_start - gd->relocaddr);
226*50149ea3SAlexander Graf 
227*50149ea3SAlexander Graf 			efi_runtime_relocate(new_offset, map);
228*50149ea3SAlexander Graf 			/* Once we're virtual, we can no longer handle
229*50149ea3SAlexander Graf 			   complex callbacks */
230*50149ea3SAlexander Graf 			efi_runtime_detach(new_offset);
231*50149ea3SAlexander Graf 			return EFI_EXIT(EFI_SUCCESS);
232*50149ea3SAlexander Graf 		}
233*50149ea3SAlexander Graf 	}
234*50149ea3SAlexander Graf 
235*50149ea3SAlexander Graf 	return EFI_EXIT(EFI_INVALID_PARAMETER);
236*50149ea3SAlexander Graf }
237*50149ea3SAlexander Graf 
238*50149ea3SAlexander Graf /*
239*50149ea3SAlexander Graf  * In the second stage, U-Boot has disappeared. To isolate our runtime code
240*50149ea3SAlexander Graf  * that at this point still exists from the rest, we put it into a special
241*50149ea3SAlexander Graf  * section.
242*50149ea3SAlexander Graf  *
243*50149ea3SAlexander Graf  *        !!WARNING!!
244*50149ea3SAlexander Graf  *
245*50149ea3SAlexander Graf  * This means that we can not rely on any code outside of this file in any
246*50149ea3SAlexander Graf  * function or variable below this line.
247*50149ea3SAlexander Graf  *
248*50149ea3SAlexander Graf  * Please keep everything fully self-contained and annotated with
249*50149ea3SAlexander Graf  * EFI_RUNTIME_TEXT and EFI_RUNTIME_DATA markers.
250*50149ea3SAlexander Graf  */
251*50149ea3SAlexander Graf 
252*50149ea3SAlexander Graf /*
253*50149ea3SAlexander Graf  * Relocate the EFI runtime stub to a different place. We need to call this
254*50149ea3SAlexander Graf  * the first time we expose the runtime interface to a user and on set virtual
255*50149ea3SAlexander Graf  * address map calls.
256*50149ea3SAlexander Graf  */
257*50149ea3SAlexander Graf 
258*50149ea3SAlexander Graf static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_unimplemented(void)
259*50149ea3SAlexander Graf {
260*50149ea3SAlexander Graf 	return EFI_UNSUPPORTED;
261*50149ea3SAlexander Graf }
262*50149ea3SAlexander Graf 
263*50149ea3SAlexander Graf static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_device_error(void)
264*50149ea3SAlexander Graf {
265*50149ea3SAlexander Graf 	return EFI_DEVICE_ERROR;
266*50149ea3SAlexander Graf }
267*50149ea3SAlexander Graf 
268*50149ea3SAlexander Graf static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_invalid_parameter(void)
269*50149ea3SAlexander Graf {
270*50149ea3SAlexander Graf 	return EFI_INVALID_PARAMETER;
271*50149ea3SAlexander Graf }
272*50149ea3SAlexander Graf 
273*50149ea3SAlexander Graf struct efi_runtime_services EFI_RUNTIME_DATA efi_runtime_services = {
274*50149ea3SAlexander Graf 	.hdr = {
275*50149ea3SAlexander Graf 		.signature = EFI_RUNTIME_SERVICES_SIGNATURE,
276*50149ea3SAlexander Graf 		.revision = EFI_RUNTIME_SERVICES_REVISION,
277*50149ea3SAlexander Graf 		.headersize = sizeof(struct efi_table_hdr),
278*50149ea3SAlexander Graf 	},
279*50149ea3SAlexander Graf 	.get_time = &efi_get_time,
280*50149ea3SAlexander Graf 	.set_time = (void *)&efi_device_error,
281*50149ea3SAlexander Graf 	.get_wakeup_time = (void *)&efi_unimplemented,
282*50149ea3SAlexander Graf 	.set_wakeup_time = (void *)&efi_unimplemented,
283*50149ea3SAlexander Graf 	.set_virtual_address_map = &efi_set_virtual_address_map,
284*50149ea3SAlexander Graf 	.convert_pointer = (void *)&efi_invalid_parameter,
285*50149ea3SAlexander Graf 	.get_variable = (void *)&efi_device_error,
286*50149ea3SAlexander Graf 	.get_next_variable = (void *)&efi_device_error,
287*50149ea3SAlexander Graf 	.set_variable = (void *)&efi_device_error,
288*50149ea3SAlexander Graf 	.get_next_high_mono_count = (void *)&efi_device_error,
289*50149ea3SAlexander Graf 	.reset_system = &efi_reset_system,
290*50149ea3SAlexander Graf };
291