xref: /openbmc/u-boot/lib/efi_loader/efi_boottime.c (revision 0bc81a717d1ce90008f9f8ae8b3c086d4405a295)
1f739fcd8STom Rini // SPDX-License-Identifier: GPL-2.0+
2bee91169SAlexander Graf /*
3bee91169SAlexander Graf  *  EFI application boot time services
4bee91169SAlexander Graf  *
5bee91169SAlexander Graf  *  Copyright (c) 2016 Alexander Graf
6bee91169SAlexander Graf  */
7bee91169SAlexander Graf 
8bee91169SAlexander Graf #include <common.h>
97d963323SHeinrich Schuchardt #include <div64.h>
10bee91169SAlexander Graf #include <efi_loader.h>
11ad644e7cSRob Clark #include <environment.h>
12bee91169SAlexander Graf #include <malloc.h>
13b08c8c48SMasahiro Yamada #include <linux/libfdt_env.h>
14bee91169SAlexander Graf #include <u-boot/crc.h>
15bee91169SAlexander Graf #include <bootm.h>
16bee91169SAlexander Graf #include <watchdog.h>
17bee91169SAlexander Graf 
18bee91169SAlexander Graf DECLARE_GLOBAL_DATA_PTR;
19bee91169SAlexander Graf 
2032f4b2f8SHeinrich Schuchardt /* Task priority level */
21152cade3SHeinrich Schuchardt static efi_uintn_t efi_tpl = TPL_APPLICATION;
2232f4b2f8SHeinrich Schuchardt 
23bee91169SAlexander Graf /* This list contains all the EFI objects our payload has access to */
24bee91169SAlexander Graf LIST_HEAD(efi_obj_list);
25bee91169SAlexander Graf 
2643bce442SHeinrich Schuchardt /* List of all events */
27b095f3c8SHeinrich Schuchardt LIST_HEAD(efi_events);
2843bce442SHeinrich Schuchardt 
29f31239acSAlexander Graf /*
30f31239acSAlexander Graf  * If we're running on nasty systems (32bit ARM booting into non-EFI Linux)
31f31239acSAlexander Graf  * we need to do trickery with caches. Since we don't want to break the EFI
32f31239acSAlexander Graf  * aware boot path, only apply hacks when loading exiting directly (breaking
33f31239acSAlexander Graf  * direct Linux EFI booting along the way - oh well).
34f31239acSAlexander Graf  */
35f31239acSAlexander Graf static bool efi_is_direct_boot = true;
36f31239acSAlexander Graf 
3765e4c0b1SSimon Glass #ifdef CONFIG_ARM
38bee91169SAlexander Graf /*
39bee91169SAlexander Graf  * The "gd" pointer lives in a register on ARM and AArch64 that we declare
40bee91169SAlexander Graf  * fixed when compiling U-Boot. However, the payload does not know about that
41bee91169SAlexander Graf  * restriction so we need to manually swap its and our view of that register on
42bee91169SAlexander Graf  * EFI callback entry/exit.
43bee91169SAlexander Graf  */
44bee91169SAlexander Graf static volatile void *efi_gd, *app_gd;
4565e4c0b1SSimon Glass #endif
46bee91169SAlexander Graf 
47c160d2f5SRob Clark static int entry_count;
48af65db85SRob Clark static int nesting_level;
49bc4f9133SHeinrich Schuchardt /* GUID of the device tree table */
50bc4f9133SHeinrich Schuchardt const efi_guid_t efi_guid_fdt = EFI_FDT_GUID;
51f0959dbeSHeinrich Schuchardt /* GUID of the EFI_DRIVER_BINDING_PROTOCOL */
52f0959dbeSHeinrich Schuchardt const efi_guid_t efi_guid_driver_binding_protocol =
53f0959dbeSHeinrich Schuchardt 			EFI_DRIVER_BINDING_PROTOCOL_GUID;
54c160d2f5SRob Clark 
55a3a28f5fSHeinrich Schuchardt /* event group ExitBootServices() invoked */
56a3a28f5fSHeinrich Schuchardt const efi_guid_t efi_guid_event_group_exit_boot_services =
57a3a28f5fSHeinrich Schuchardt 			EFI_EVENT_GROUP_EXIT_BOOT_SERVICES;
58a3a28f5fSHeinrich Schuchardt /* event group SetVirtualAddressMap() invoked */
59a3a28f5fSHeinrich Schuchardt const efi_guid_t efi_guid_event_group_virtual_address_change =
60a3a28f5fSHeinrich Schuchardt 			EFI_EVENT_GROUP_VIRTUAL_ADDRESS_CHANGE;
61a3a28f5fSHeinrich Schuchardt /* event group memory map changed */
62a3a28f5fSHeinrich Schuchardt const efi_guid_t efi_guid_event_group_memory_map_change =
63a3a28f5fSHeinrich Schuchardt 			EFI_EVENT_GROUP_MEMORY_MAP_CHANGE;
64a3a28f5fSHeinrich Schuchardt /* event group boot manager about to boot */
65a3a28f5fSHeinrich Schuchardt const efi_guid_t efi_guid_event_group_ready_to_boot =
66a3a28f5fSHeinrich Schuchardt 			EFI_EVENT_GROUP_READY_TO_BOOT;
67a3a28f5fSHeinrich Schuchardt /* event group ResetSystem() invoked (before ExitBootServices) */
68a3a28f5fSHeinrich Schuchardt const efi_guid_t efi_guid_event_group_reset_system =
69a3a28f5fSHeinrich Schuchardt 			EFI_EVENT_GROUP_RESET_SYSTEM;
70a3a28f5fSHeinrich Schuchardt 
712074f700SHeinrich Schuchardt static efi_status_t EFIAPI efi_disconnect_controller(
722074f700SHeinrich Schuchardt 					efi_handle_t controller_handle,
732074f700SHeinrich Schuchardt 					efi_handle_t driver_image_handle,
742074f700SHeinrich Schuchardt 					efi_handle_t child_handle);
753f9b0042SHeinrich Schuchardt 
76c160d2f5SRob Clark /* Called on every callback entry */
77c160d2f5SRob Clark int __efi_entry_check(void)
78c160d2f5SRob Clark {
79c160d2f5SRob Clark 	int ret = entry_count++ == 0;
80c160d2f5SRob Clark #ifdef CONFIG_ARM
81c160d2f5SRob Clark 	assert(efi_gd);
82c160d2f5SRob Clark 	app_gd = gd;
83c160d2f5SRob Clark 	gd = efi_gd;
84c160d2f5SRob Clark #endif
85c160d2f5SRob Clark 	return ret;
86c160d2f5SRob Clark }
87c160d2f5SRob Clark 
88c160d2f5SRob Clark /* Called on every callback exit */
89c160d2f5SRob Clark int __efi_exit_check(void)
90c160d2f5SRob Clark {
91c160d2f5SRob Clark 	int ret = --entry_count == 0;
92c160d2f5SRob Clark #ifdef CONFIG_ARM
93c160d2f5SRob Clark 	gd = app_gd;
94c160d2f5SRob Clark #endif
95c160d2f5SRob Clark 	return ret;
96c160d2f5SRob Clark }
97c160d2f5SRob Clark 
98bee91169SAlexander Graf /* Called from do_bootefi_exec() */
99bee91169SAlexander Graf void efi_save_gd(void)
100bee91169SAlexander Graf {
10165e4c0b1SSimon Glass #ifdef CONFIG_ARM
102bee91169SAlexander Graf 	efi_gd = gd;
10365e4c0b1SSimon Glass #endif
104bee91169SAlexander Graf }
105bee91169SAlexander Graf 
106c160d2f5SRob Clark /*
10778a88f79SMario Six  * Special case handler for error/abort that just forces things back to u-boot
108b72aaa87SHeinrich Schuchardt  * world so we can dump out an abort message, without any care about returning
109b72aaa87SHeinrich Schuchardt  * back to UEFI world.
110c160d2f5SRob Clark  */
111bee91169SAlexander Graf void efi_restore_gd(void)
112bee91169SAlexander Graf {
11365e4c0b1SSimon Glass #ifdef CONFIG_ARM
114bee91169SAlexander Graf 	/* Only restore if we're already in EFI context */
115bee91169SAlexander Graf 	if (!efi_gd)
116bee91169SAlexander Graf 		return;
117bee91169SAlexander Graf 	gd = efi_gd;
11865e4c0b1SSimon Glass #endif
119bee91169SAlexander Graf }
120bee91169SAlexander Graf 
1216b03cd10SHeinrich Schuchardt /**
12278a88f79SMario Six  * indent_string() - returns a string for indenting with two spaces per level
12378a88f79SMario Six  * @level: indent level
124c8df80c5SHeinrich Schuchardt  *
1256b03cd10SHeinrich Schuchardt  * A maximum of ten indent levels is supported. Higher indent levels will be
1266b03cd10SHeinrich Schuchardt  * truncated.
1276b03cd10SHeinrich Schuchardt  *
12878a88f79SMario Six  * Return: A string for indenting with two spaces per level is
1296b03cd10SHeinrich Schuchardt  *         returned.
130af65db85SRob Clark  */
131af65db85SRob Clark static const char *indent_string(int level)
132af65db85SRob Clark {
133af65db85SRob Clark 	const char *indent = "                    ";
134af65db85SRob Clark 	const int max = strlen(indent);
135ab9efa97SHeinrich Schuchardt 
136af65db85SRob Clark 	level = min(max, level * 2);
137af65db85SRob Clark 	return &indent[max - level];
138af65db85SRob Clark }
139af65db85SRob Clark 
140ae0bd3a9SHeinrich Schuchardt const char *__efi_nesting(void)
141ae0bd3a9SHeinrich Schuchardt {
142ae0bd3a9SHeinrich Schuchardt 	return indent_string(nesting_level);
143ae0bd3a9SHeinrich Schuchardt }
144ae0bd3a9SHeinrich Schuchardt 
145af65db85SRob Clark const char *__efi_nesting_inc(void)
146af65db85SRob Clark {
147af65db85SRob Clark 	return indent_string(nesting_level++);
148af65db85SRob Clark }
149af65db85SRob Clark 
150af65db85SRob Clark const char *__efi_nesting_dec(void)
151af65db85SRob Clark {
152af65db85SRob Clark 	return indent_string(--nesting_level);
153af65db85SRob Clark }
154af65db85SRob Clark 
1556b03cd10SHeinrich Schuchardt /**
15678a88f79SMario Six  * efi_queue_event() - queue an EFI event
15778a88f79SMario Six  * @event:     event to signal
15878a88f79SMario Six  * @check_tpl: check the TPL level
159332468f7SHeinrich Schuchardt  *
160332468f7SHeinrich Schuchardt  * This function queues the notification function of the event for future
161332468f7SHeinrich Schuchardt  * execution.
162332468f7SHeinrich Schuchardt  *
16378a88f79SMario Six  * The notification function is called if the task priority level of the event
16478a88f79SMario Six  * is higher than the current task priority level.
165332468f7SHeinrich Schuchardt  *
166332468f7SHeinrich Schuchardt  * For the SignalEvent service see efi_signal_event_ext.
167332468f7SHeinrich Schuchardt  *
168332468f7SHeinrich Schuchardt  */
169b095f3c8SHeinrich Schuchardt static void efi_queue_event(struct efi_event *event, bool check_tpl)
170c6841592Sxypron.glpk@gmx.de {
171ca62a4f5SHeinrich Schuchardt 	if (event->notify_function) {
172e190e897SHeinrich Schuchardt 		event->is_queued = true;
17332f4b2f8SHeinrich Schuchardt 		/* Check TPL */
1749bc9664dSHeinrich Schuchardt 		if (check_tpl && efi_tpl >= event->notify_tpl)
17532f4b2f8SHeinrich Schuchardt 			return;
176ea630ce9SHeinrich Schuchardt 		EFI_CALL_VOID(event->notify_function(event,
177ea630ce9SHeinrich Schuchardt 						     event->notify_context));
178c6841592Sxypron.glpk@gmx.de 	}
179e190e897SHeinrich Schuchardt 	event->is_queued = false;
180c6841592Sxypron.glpk@gmx.de }
181c6841592Sxypron.glpk@gmx.de 
1826b03cd10SHeinrich Schuchardt /**
18321b3edfcSHeinrich Schuchardt  * is_valid_tpl() - check if the task priority level is valid
18421b3edfcSHeinrich Schuchardt  *
18521b3edfcSHeinrich Schuchardt  * @tpl:		TPL level to check
186b72aaa87SHeinrich Schuchardt  * Return:		status code
18721b3edfcSHeinrich Schuchardt  */
18821b3edfcSHeinrich Schuchardt efi_status_t is_valid_tpl(efi_uintn_t tpl)
18921b3edfcSHeinrich Schuchardt {
19021b3edfcSHeinrich Schuchardt 	switch (tpl) {
19121b3edfcSHeinrich Schuchardt 	case TPL_APPLICATION:
19221b3edfcSHeinrich Schuchardt 	case TPL_CALLBACK:
19321b3edfcSHeinrich Schuchardt 	case TPL_NOTIFY:
19421b3edfcSHeinrich Schuchardt 	case TPL_HIGH_LEVEL:
19521b3edfcSHeinrich Schuchardt 		return EFI_SUCCESS;
19621b3edfcSHeinrich Schuchardt 	default:
19721b3edfcSHeinrich Schuchardt 		return EFI_INVALID_PARAMETER;
19821b3edfcSHeinrich Schuchardt 	}
19921b3edfcSHeinrich Schuchardt }
20021b3edfcSHeinrich Schuchardt 
20121b3edfcSHeinrich Schuchardt /**
20278a88f79SMario Six  * efi_signal_event() - signal an EFI event
20378a88f79SMario Six  * @event:     event to signal
20478a88f79SMario Six  * @check_tpl: check the TPL level
205b095f3c8SHeinrich Schuchardt  *
20678a88f79SMario Six  * This function signals an event. If the event belongs to an event group all
20778a88f79SMario Six  * events of the group are signaled. If they are of type EVT_NOTIFY_SIGNAL
208b095f3c8SHeinrich Schuchardt  * their notification function is queued.
209b095f3c8SHeinrich Schuchardt  *
210b095f3c8SHeinrich Schuchardt  * For the SignalEvent service see efi_signal_event_ext.
211b095f3c8SHeinrich Schuchardt  */
212b095f3c8SHeinrich Schuchardt void efi_signal_event(struct efi_event *event, bool check_tpl)
213b095f3c8SHeinrich Schuchardt {
214b095f3c8SHeinrich Schuchardt 	if (event->group) {
215b095f3c8SHeinrich Schuchardt 		struct efi_event *evt;
216b095f3c8SHeinrich Schuchardt 
217b095f3c8SHeinrich Schuchardt 		/*
218b095f3c8SHeinrich Schuchardt 		 * The signaled state has to set before executing any
219b095f3c8SHeinrich Schuchardt 		 * notification function
220b095f3c8SHeinrich Schuchardt 		 */
221b095f3c8SHeinrich Schuchardt 		list_for_each_entry(evt, &efi_events, link) {
222b095f3c8SHeinrich Schuchardt 			if (!evt->group || guidcmp(evt->group, event->group))
223b095f3c8SHeinrich Schuchardt 				continue;
224b095f3c8SHeinrich Schuchardt 			if (evt->is_signaled)
225b095f3c8SHeinrich Schuchardt 				continue;
226b095f3c8SHeinrich Schuchardt 			evt->is_signaled = true;
227b095f3c8SHeinrich Schuchardt 			if (evt->type & EVT_NOTIFY_SIGNAL &&
228b095f3c8SHeinrich Schuchardt 			    evt->notify_function)
229b095f3c8SHeinrich Schuchardt 				evt->is_queued = true;
230b095f3c8SHeinrich Schuchardt 		}
231b095f3c8SHeinrich Schuchardt 		list_for_each_entry(evt, &efi_events, link) {
232b095f3c8SHeinrich Schuchardt 			if (!evt->group || guidcmp(evt->group, event->group))
233b095f3c8SHeinrich Schuchardt 				continue;
234b095f3c8SHeinrich Schuchardt 			if (evt->is_queued)
235b095f3c8SHeinrich Schuchardt 				efi_queue_event(evt, check_tpl);
236b095f3c8SHeinrich Schuchardt 		}
237b095f3c8SHeinrich Schuchardt 	} else if (!event->is_signaled) {
238b095f3c8SHeinrich Schuchardt 		event->is_signaled = true;
239b095f3c8SHeinrich Schuchardt 		if (event->type & EVT_NOTIFY_SIGNAL)
240b095f3c8SHeinrich Schuchardt 			efi_queue_event(event, check_tpl);
241b095f3c8SHeinrich Schuchardt 	}
242b095f3c8SHeinrich Schuchardt }
243b095f3c8SHeinrich Schuchardt 
2446b03cd10SHeinrich Schuchardt /**
24578a88f79SMario Six  * efi_raise_tpl() - raise the task priority level
24678a88f79SMario Six  * @new_tpl: new value of the task priority level
247332468f7SHeinrich Schuchardt  *
248332468f7SHeinrich Schuchardt  * This function implements the RaiseTpl service.
249332468f7SHeinrich Schuchardt  *
25078a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
25178a88f79SMario Six  * details.
25278a88f79SMario Six  *
25378a88f79SMario Six  * Return: old value of the task priority level
254332468f7SHeinrich Schuchardt  */
255152cade3SHeinrich Schuchardt static unsigned long EFIAPI efi_raise_tpl(efi_uintn_t new_tpl)
256bee91169SAlexander Graf {
257152cade3SHeinrich Schuchardt 	efi_uintn_t old_tpl = efi_tpl;
25832f4b2f8SHeinrich Schuchardt 
259503f2695Sxypron.glpk@gmx.de 	EFI_ENTRY("0x%zx", new_tpl);
26032f4b2f8SHeinrich Schuchardt 
26132f4b2f8SHeinrich Schuchardt 	if (new_tpl < efi_tpl)
26232f4b2f8SHeinrich Schuchardt 		debug("WARNING: new_tpl < current_tpl in %s\n", __func__);
26332f4b2f8SHeinrich Schuchardt 	efi_tpl = new_tpl;
26432f4b2f8SHeinrich Schuchardt 	if (efi_tpl > TPL_HIGH_LEVEL)
26532f4b2f8SHeinrich Schuchardt 		efi_tpl = TPL_HIGH_LEVEL;
26632f4b2f8SHeinrich Schuchardt 
26732f4b2f8SHeinrich Schuchardt 	EFI_EXIT(EFI_SUCCESS);
26832f4b2f8SHeinrich Schuchardt 	return old_tpl;
269bee91169SAlexander Graf }
270bee91169SAlexander Graf 
2716b03cd10SHeinrich Schuchardt /**
27278a88f79SMario Six  * efi_restore_tpl() - lower the task priority level
27378a88f79SMario Six  * @old_tpl: value of the task priority level to be restored
274332468f7SHeinrich Schuchardt  *
275332468f7SHeinrich Schuchardt  * This function implements the RestoreTpl service.
276332468f7SHeinrich Schuchardt  *
27778a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
27878a88f79SMario Six  * details.
279332468f7SHeinrich Schuchardt  */
280152cade3SHeinrich Schuchardt static void EFIAPI efi_restore_tpl(efi_uintn_t old_tpl)
281bee91169SAlexander Graf {
282503f2695Sxypron.glpk@gmx.de 	EFI_ENTRY("0x%zx", old_tpl);
28332f4b2f8SHeinrich Schuchardt 
28432f4b2f8SHeinrich Schuchardt 	if (old_tpl > efi_tpl)
28532f4b2f8SHeinrich Schuchardt 		debug("WARNING: old_tpl > current_tpl in %s\n", __func__);
28632f4b2f8SHeinrich Schuchardt 	efi_tpl = old_tpl;
28732f4b2f8SHeinrich Schuchardt 	if (efi_tpl > TPL_HIGH_LEVEL)
28832f4b2f8SHeinrich Schuchardt 		efi_tpl = TPL_HIGH_LEVEL;
28932f4b2f8SHeinrich Schuchardt 
2900f7fcc72SHeinrich Schuchardt 	/*
2910f7fcc72SHeinrich Schuchardt 	 * Lowering the TPL may have made queued events eligible for execution.
2920f7fcc72SHeinrich Schuchardt 	 */
2930f7fcc72SHeinrich Schuchardt 	efi_timer_check();
2940f7fcc72SHeinrich Schuchardt 
29532f4b2f8SHeinrich Schuchardt 	EFI_EXIT(EFI_SUCCESS);
296bee91169SAlexander Graf }
297bee91169SAlexander Graf 
2986b03cd10SHeinrich Schuchardt /**
29978a88f79SMario Six  * efi_allocate_pages_ext() - allocate memory pages
3006b03cd10SHeinrich Schuchardt  * @type:        type of allocation to be performed
3016b03cd10SHeinrich Schuchardt  * @memory_type: usage type of the allocated memory
3026b03cd10SHeinrich Schuchardt  * @pages:       number of pages to be allocated
3036b03cd10SHeinrich Schuchardt  * @memory:      allocated memory
30478a88f79SMario Six  *
30578a88f79SMario Six  * This function implements the AllocatePages service.
30678a88f79SMario Six  *
30778a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
30878a88f79SMario Six  * details.
30978a88f79SMario Six  *
31078a88f79SMario Six  * Return: status code
311332468f7SHeinrich Schuchardt  */
3126e0bf8d8SMasahiro Yamada static efi_status_t EFIAPI efi_allocate_pages_ext(int type, int memory_type,
313f5a2a938SHeinrich Schuchardt 						  efi_uintn_t pages,
314bee91169SAlexander Graf 						  uint64_t *memory)
315bee91169SAlexander Graf {
316bee91169SAlexander Graf 	efi_status_t r;
317bee91169SAlexander Graf 
318f5a2a938SHeinrich Schuchardt 	EFI_ENTRY("%d, %d, 0x%zx, %p", type, memory_type, pages, memory);
319bee91169SAlexander Graf 	r = efi_allocate_pages(type, memory_type, pages, memory);
320bee91169SAlexander Graf 	return EFI_EXIT(r);
321bee91169SAlexander Graf }
322bee91169SAlexander Graf 
3236b03cd10SHeinrich Schuchardt /**
32478a88f79SMario Six  * efi_free_pages_ext() - Free memory pages.
3256b03cd10SHeinrich Schuchardt  * @memory: start of the memory area to be freed
3266b03cd10SHeinrich Schuchardt  * @pages:  number of pages to be freed
32778a88f79SMario Six  *
32878a88f79SMario Six  * This function implements the FreePages service.
32978a88f79SMario Six  *
33078a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
33178a88f79SMario Six  * details.
33278a88f79SMario Six  *
33378a88f79SMario Six  * Return: status code
334332468f7SHeinrich Schuchardt  */
3356e0bf8d8SMasahiro Yamada static efi_status_t EFIAPI efi_free_pages_ext(uint64_t memory,
336f5a2a938SHeinrich Schuchardt 					      efi_uintn_t pages)
337bee91169SAlexander Graf {
338bee91169SAlexander Graf 	efi_status_t r;
339bee91169SAlexander Graf 
340dee37fc9SMasahiro Yamada 	EFI_ENTRY("%llx, 0x%zx", memory, pages);
341bee91169SAlexander Graf 	r = efi_free_pages(memory, pages);
342bee91169SAlexander Graf 	return EFI_EXIT(r);
343bee91169SAlexander Graf }
344bee91169SAlexander Graf 
3456b03cd10SHeinrich Schuchardt /**
34678a88f79SMario Six  * efi_get_memory_map_ext() - get map describing memory usage
3476b03cd10SHeinrich Schuchardt  * @memory_map_size:    on entry the size, in bytes, of the memory map buffer,
348332468f7SHeinrich Schuchardt  *                      on exit the size of the copied memory map
3496b03cd10SHeinrich Schuchardt  * @memory_map:         buffer to which the memory map is written
3506b03cd10SHeinrich Schuchardt  * @map_key:            key for the memory map
3516b03cd10SHeinrich Schuchardt  * @descriptor_size:    size of an individual memory descriptor
3526b03cd10SHeinrich Schuchardt  * @descriptor_version: version number of the memory descriptor structure
35378a88f79SMario Six  *
35478a88f79SMario Six  * This function implements the GetMemoryMap service.
35578a88f79SMario Six  *
35678a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
35778a88f79SMario Six  * details.
35878a88f79SMario Six  *
35978a88f79SMario Six  * Return: status code
360332468f7SHeinrich Schuchardt  */
3616e0bf8d8SMasahiro Yamada static efi_status_t EFIAPI efi_get_memory_map_ext(
362f5a2a938SHeinrich Schuchardt 					efi_uintn_t *memory_map_size,
363bee91169SAlexander Graf 					struct efi_mem_desc *memory_map,
364f5a2a938SHeinrich Schuchardt 					efi_uintn_t *map_key,
365f5a2a938SHeinrich Schuchardt 					efi_uintn_t *descriptor_size,
366bee91169SAlexander Graf 					uint32_t *descriptor_version)
367bee91169SAlexander Graf {
368bee91169SAlexander Graf 	efi_status_t r;
369bee91169SAlexander Graf 
370bee91169SAlexander Graf 	EFI_ENTRY("%p, %p, %p, %p, %p", memory_map_size, memory_map,
371bee91169SAlexander Graf 		  map_key, descriptor_size, descriptor_version);
372bee91169SAlexander Graf 	r = efi_get_memory_map(memory_map_size, memory_map, map_key,
373bee91169SAlexander Graf 			       descriptor_size, descriptor_version);
374bee91169SAlexander Graf 	return EFI_EXIT(r);
375bee91169SAlexander Graf }
376bee91169SAlexander Graf 
3776b03cd10SHeinrich Schuchardt /**
37878a88f79SMario Six  * efi_allocate_pool_ext() - allocate memory from pool
3796b03cd10SHeinrich Schuchardt  * @pool_type: type of the pool from which memory is to be allocated
3806b03cd10SHeinrich Schuchardt  * @size:      number of bytes to be allocated
3816b03cd10SHeinrich Schuchardt  * @buffer:    allocated memory
38278a88f79SMario Six  *
38378a88f79SMario Six  * This function implements the AllocatePool service.
38478a88f79SMario Six  *
38578a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
38678a88f79SMario Six  * details.
38778a88f79SMario Six  *
38878a88f79SMario Six  * Return: status code
389332468f7SHeinrich Schuchardt  */
390ead1274bSStefan Brüns static efi_status_t EFIAPI efi_allocate_pool_ext(int pool_type,
391f5a2a938SHeinrich Schuchardt 						 efi_uintn_t size,
392bee91169SAlexander Graf 						 void **buffer)
393bee91169SAlexander Graf {
3941cd29f0aSAlexander Graf 	efi_status_t r;
3951cd29f0aSAlexander Graf 
396f5a2a938SHeinrich Schuchardt 	EFI_ENTRY("%d, %zd, %p", pool_type, size, buffer);
397ead1274bSStefan Brüns 	r = efi_allocate_pool(pool_type, size, buffer);
3981cd29f0aSAlexander Graf 	return EFI_EXIT(r);
399bee91169SAlexander Graf }
400bee91169SAlexander Graf 
4016b03cd10SHeinrich Schuchardt /**
40278a88f79SMario Six  * efi_free_pool_ext() - free memory from pool
40378a88f79SMario Six  * @buffer: start of memory to be freed
404332468f7SHeinrich Schuchardt  *
405332468f7SHeinrich Schuchardt  * This function implements the FreePool service.
406332468f7SHeinrich Schuchardt  *
40778a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
40878a88f79SMario Six  * details.
40978a88f79SMario Six  *
41078a88f79SMario Six  * Return: status code
411332468f7SHeinrich Schuchardt  */
41242417bc8SStefan Brüns static efi_status_t EFIAPI efi_free_pool_ext(void *buffer)
413bee91169SAlexander Graf {
4141cd29f0aSAlexander Graf 	efi_status_t r;
4151cd29f0aSAlexander Graf 
4161cd29f0aSAlexander Graf 	EFI_ENTRY("%p", buffer);
41742417bc8SStefan Brüns 	r = efi_free_pool(buffer);
4181cd29f0aSAlexander Graf 	return EFI_EXIT(r);
419bee91169SAlexander Graf }
420bee91169SAlexander Graf 
4216b03cd10SHeinrich Schuchardt /**
42278a88f79SMario Six  * efi_add_handle() - add a new object to the object list
4236b03cd10SHeinrich Schuchardt  * @obj: object to be added
42478a88f79SMario Six  *
42578a88f79SMario Six  * The protocols list is initialized. The object handle is set.
42644549d62SHeinrich Schuchardt  */
427fae0118eSHeinrich Schuchardt void efi_add_handle(efi_handle_t handle)
42844549d62SHeinrich Schuchardt {
429fae0118eSHeinrich Schuchardt 	if (!handle)
43044549d62SHeinrich Schuchardt 		return;
431fae0118eSHeinrich Schuchardt 	INIT_LIST_HEAD(&handle->protocols);
432fae0118eSHeinrich Schuchardt 	list_add_tail(&handle->link, &efi_obj_list);
43344549d62SHeinrich Schuchardt }
43444549d62SHeinrich Schuchardt 
4356b03cd10SHeinrich Schuchardt /**
43678a88f79SMario Six  * efi_create_handle() - create handle
4376b03cd10SHeinrich Schuchardt  * @handle: new handle
43878a88f79SMario Six  *
43978a88f79SMario Six  * Return: status code
4402edab5e2SHeinrich Schuchardt  */
4412074f700SHeinrich Schuchardt efi_status_t efi_create_handle(efi_handle_t *handle)
4423cc6e3feSHeinrich Schuchardt {
4433cc6e3feSHeinrich Schuchardt 	struct efi_object *obj;
4443cc6e3feSHeinrich Schuchardt 
445d29e7824SHeinrich Schuchardt 	obj = calloc(1, sizeof(struct efi_object));
446d29e7824SHeinrich Schuchardt 	if (!obj)
447d29e7824SHeinrich Schuchardt 		return EFI_OUT_OF_RESOURCES;
448d29e7824SHeinrich Schuchardt 
44944549d62SHeinrich Schuchardt 	efi_add_handle(obj);
450fae0118eSHeinrich Schuchardt 	*handle = obj;
451d29e7824SHeinrich Schuchardt 
452d29e7824SHeinrich Schuchardt 	return EFI_SUCCESS;
4533cc6e3feSHeinrich Schuchardt }
4543cc6e3feSHeinrich Schuchardt 
4556b03cd10SHeinrich Schuchardt /**
45678a88f79SMario Six  * efi_search_protocol() - find a protocol on a handle.
4576b03cd10SHeinrich Schuchardt  * @handle:        handle
4586b03cd10SHeinrich Schuchardt  * @protocol_guid: GUID of the protocol
4596b03cd10SHeinrich Schuchardt  * @handler:       reference to the protocol
46078a88f79SMario Six  *
46178a88f79SMario Six  * Return: status code
462678e03a0SHeinrich Schuchardt  */
4632074f700SHeinrich Schuchardt efi_status_t efi_search_protocol(const efi_handle_t handle,
464678e03a0SHeinrich Schuchardt 				 const efi_guid_t *protocol_guid,
465678e03a0SHeinrich Schuchardt 				 struct efi_handler **handler)
466678e03a0SHeinrich Schuchardt {
467678e03a0SHeinrich Schuchardt 	struct efi_object *efiobj;
468678e03a0SHeinrich Schuchardt 	struct list_head *lhandle;
469678e03a0SHeinrich Schuchardt 
470678e03a0SHeinrich Schuchardt 	if (!handle || !protocol_guid)
471678e03a0SHeinrich Schuchardt 		return EFI_INVALID_PARAMETER;
472678e03a0SHeinrich Schuchardt 	efiobj = efi_search_obj(handle);
473678e03a0SHeinrich Schuchardt 	if (!efiobj)
474678e03a0SHeinrich Schuchardt 		return EFI_INVALID_PARAMETER;
475678e03a0SHeinrich Schuchardt 	list_for_each(lhandle, &efiobj->protocols) {
476678e03a0SHeinrich Schuchardt 		struct efi_handler *protocol;
477678e03a0SHeinrich Schuchardt 
478678e03a0SHeinrich Schuchardt 		protocol = list_entry(lhandle, struct efi_handler, link);
479678e03a0SHeinrich Schuchardt 		if (!guidcmp(protocol->guid, protocol_guid)) {
480678e03a0SHeinrich Schuchardt 			if (handler)
481678e03a0SHeinrich Schuchardt 				*handler = protocol;
482678e03a0SHeinrich Schuchardt 			return EFI_SUCCESS;
483678e03a0SHeinrich Schuchardt 		}
484678e03a0SHeinrich Schuchardt 	}
485678e03a0SHeinrich Schuchardt 	return EFI_NOT_FOUND;
486678e03a0SHeinrich Schuchardt }
487678e03a0SHeinrich Schuchardt 
4886b03cd10SHeinrich Schuchardt /**
48978a88f79SMario Six  * efi_remove_protocol() - delete protocol from a handle
4906b03cd10SHeinrich Schuchardt  * @handle:             handle from which the protocol shall be deleted
4916b03cd10SHeinrich Schuchardt  * @protocol:           GUID of the protocol to be deleted
4926b03cd10SHeinrich Schuchardt  * @protocol_interface: interface of the protocol implementation
49378a88f79SMario Six  *
49478a88f79SMario Six  * Return: status code
495678e03a0SHeinrich Schuchardt  */
4962074f700SHeinrich Schuchardt efi_status_t efi_remove_protocol(const efi_handle_t handle,
4972074f700SHeinrich Schuchardt 				 const efi_guid_t *protocol,
498678e03a0SHeinrich Schuchardt 				 void *protocol_interface)
499678e03a0SHeinrich Schuchardt {
500678e03a0SHeinrich Schuchardt 	struct efi_handler *handler;
501678e03a0SHeinrich Schuchardt 	efi_status_t ret;
502678e03a0SHeinrich Schuchardt 
503678e03a0SHeinrich Schuchardt 	ret = efi_search_protocol(handle, protocol, &handler);
504678e03a0SHeinrich Schuchardt 	if (ret != EFI_SUCCESS)
505678e03a0SHeinrich Schuchardt 		return ret;
506678e03a0SHeinrich Schuchardt 	if (guidcmp(handler->guid, protocol))
507678e03a0SHeinrich Schuchardt 		return EFI_INVALID_PARAMETER;
5081f470e17SHeinrich Schuchardt 	if (handler->protocol_interface != protocol_interface)
5091f470e17SHeinrich Schuchardt 		return EFI_INVALID_PARAMETER;
510678e03a0SHeinrich Schuchardt 	list_del(&handler->link);
511678e03a0SHeinrich Schuchardt 	free(handler);
512678e03a0SHeinrich Schuchardt 	return EFI_SUCCESS;
513678e03a0SHeinrich Schuchardt }
514678e03a0SHeinrich Schuchardt 
5156b03cd10SHeinrich Schuchardt /**
51678a88f79SMario Six  * efi_remove_all_protocols() - delete all protocols from a handle
5176b03cd10SHeinrich Schuchardt  * @handle: handle from which the protocols shall be deleted
51878a88f79SMario Six  *
51978a88f79SMario Six  * Return: status code
520678e03a0SHeinrich Schuchardt  */
5212074f700SHeinrich Schuchardt efi_status_t efi_remove_all_protocols(const efi_handle_t handle)
522678e03a0SHeinrich Schuchardt {
523678e03a0SHeinrich Schuchardt 	struct efi_object *efiobj;
52432e6fed6SHeinrich Schuchardt 	struct efi_handler *protocol;
52532e6fed6SHeinrich Schuchardt 	struct efi_handler *pos;
526678e03a0SHeinrich Schuchardt 
527678e03a0SHeinrich Schuchardt 	efiobj = efi_search_obj(handle);
528678e03a0SHeinrich Schuchardt 	if (!efiobj)
529678e03a0SHeinrich Schuchardt 		return EFI_INVALID_PARAMETER;
53032e6fed6SHeinrich Schuchardt 	list_for_each_entry_safe(protocol, pos, &efiobj->protocols, link) {
531678e03a0SHeinrich Schuchardt 		efi_status_t ret;
532678e03a0SHeinrich Schuchardt 
533678e03a0SHeinrich Schuchardt 		ret = efi_remove_protocol(handle, protocol->guid,
534678e03a0SHeinrich Schuchardt 					  protocol->protocol_interface);
535678e03a0SHeinrich Schuchardt 		if (ret != EFI_SUCCESS)
536678e03a0SHeinrich Schuchardt 			return ret;
537678e03a0SHeinrich Schuchardt 	}
538678e03a0SHeinrich Schuchardt 	return EFI_SUCCESS;
539678e03a0SHeinrich Schuchardt }
540678e03a0SHeinrich Schuchardt 
5416b03cd10SHeinrich Schuchardt /**
54278a88f79SMario Six  * efi_delete_handle() - delete handle
543678e03a0SHeinrich Schuchardt  *
5446b03cd10SHeinrich Schuchardt  * @obj: handle to delete
545678e03a0SHeinrich Schuchardt  */
546fae0118eSHeinrich Schuchardt void efi_delete_handle(efi_handle_t handle)
547678e03a0SHeinrich Schuchardt {
548fae0118eSHeinrich Schuchardt 	if (!handle)
549678e03a0SHeinrich Schuchardt 		return;
550fae0118eSHeinrich Schuchardt 	efi_remove_all_protocols(handle);
551fae0118eSHeinrich Schuchardt 	list_del(&handle->link);
552fae0118eSHeinrich Schuchardt 	free(handle);
553678e03a0SHeinrich Schuchardt }
554678e03a0SHeinrich Schuchardt 
5556b03cd10SHeinrich Schuchardt /**
55678a88f79SMario Six  * efi_is_event() - check if a pointer is a valid event
5576b03cd10SHeinrich Schuchardt  * @event: pointer to check
55878a88f79SMario Six  *
55978a88f79SMario Six  * Return: status code
560bee91169SAlexander Graf  */
56143bce442SHeinrich Schuchardt static efi_status_t efi_is_event(const struct efi_event *event)
56243bce442SHeinrich Schuchardt {
56343bce442SHeinrich Schuchardt 	const struct efi_event *evt;
56443bce442SHeinrich Schuchardt 
56543bce442SHeinrich Schuchardt 	if (!event)
56643bce442SHeinrich Schuchardt 		return EFI_INVALID_PARAMETER;
56743bce442SHeinrich Schuchardt 	list_for_each_entry(evt, &efi_events, link) {
56843bce442SHeinrich Schuchardt 		if (evt == event)
56943bce442SHeinrich Schuchardt 			return EFI_SUCCESS;
57043bce442SHeinrich Schuchardt 	}
57143bce442SHeinrich Schuchardt 	return EFI_INVALID_PARAMETER;
57243bce442SHeinrich Schuchardt }
573bee91169SAlexander Graf 
5746b03cd10SHeinrich Schuchardt /**
57578a88f79SMario Six  * efi_create_event() - create an event
5766b03cd10SHeinrich Schuchardt  * @type:            type of the event to create
5776b03cd10SHeinrich Schuchardt  * @notify_tpl:      task priority level of the event
5786b03cd10SHeinrich Schuchardt  * @notify_function: notification function of the event
5796b03cd10SHeinrich Schuchardt  * @notify_context:  pointer passed to the notification function
5806b03cd10SHeinrich Schuchardt  * @group:           event group
5816b03cd10SHeinrich Schuchardt  * @event:           created event
58278a88f79SMario Six  *
58378a88f79SMario Six  * This function is used inside U-Boot code to create an event.
58478a88f79SMario Six  *
58578a88f79SMario Six  * For the API function implementing the CreateEvent service see
58678a88f79SMario Six  * efi_create_event_ext.
58778a88f79SMario Six  *
58878a88f79SMario Six  * Return: status code
589332468f7SHeinrich Schuchardt  */
590152cade3SHeinrich Schuchardt efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
5912fd945feSxypron.glpk@gmx.de 			      void (EFIAPI *notify_function) (
5922fd945feSxypron.glpk@gmx.de 					struct efi_event *event,
593e275458cSSimon Glass 					void *context),
594b095f3c8SHeinrich Schuchardt 			      void *notify_context, efi_guid_t *group,
595b095f3c8SHeinrich Schuchardt 			      struct efi_event **event)
596bee91169SAlexander Graf {
59743bce442SHeinrich Schuchardt 	struct efi_event *evt;
598c6841592Sxypron.glpk@gmx.de 
599a95343b8SJonathan Gray 	if (event == NULL)
60049deb455Sxypron.glpk@gmx.de 		return EFI_INVALID_PARAMETER;
601a95343b8SJonathan Gray 
60221b3edfcSHeinrich Schuchardt 	switch (type) {
60321b3edfcSHeinrich Schuchardt 	case 0:
60421b3edfcSHeinrich Schuchardt 	case EVT_TIMER:
60521b3edfcSHeinrich Schuchardt 	case EVT_NOTIFY_SIGNAL:
60621b3edfcSHeinrich Schuchardt 	case EVT_TIMER | EVT_NOTIFY_SIGNAL:
60721b3edfcSHeinrich Schuchardt 	case EVT_NOTIFY_WAIT:
60821b3edfcSHeinrich Schuchardt 	case EVT_TIMER | EVT_NOTIFY_WAIT:
60921b3edfcSHeinrich Schuchardt 	case EVT_SIGNAL_EXIT_BOOT_SERVICES:
61021b3edfcSHeinrich Schuchardt 	case EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE:
61121b3edfcSHeinrich Schuchardt 		break;
61221b3edfcSHeinrich Schuchardt 	default:
61349deb455Sxypron.glpk@gmx.de 		return EFI_INVALID_PARAMETER;
61421b3edfcSHeinrich Schuchardt 	}
615a95343b8SJonathan Gray 
6163748ed90SAKASHI Takahiro 	if ((type & (EVT_NOTIFY_WAIT | EVT_NOTIFY_SIGNAL)) &&
6173748ed90SAKASHI Takahiro 	    (is_valid_tpl(notify_tpl) != EFI_SUCCESS))
61849deb455Sxypron.glpk@gmx.de 		return EFI_INVALID_PARAMETER;
619a95343b8SJonathan Gray 
62043bce442SHeinrich Schuchardt 	evt = calloc(1, sizeof(struct efi_event));
62143bce442SHeinrich Schuchardt 	if (!evt)
62249deb455Sxypron.glpk@gmx.de 		return EFI_OUT_OF_RESOURCES;
62343bce442SHeinrich Schuchardt 	evt->type = type;
62443bce442SHeinrich Schuchardt 	evt->notify_tpl = notify_tpl;
62543bce442SHeinrich Schuchardt 	evt->notify_function = notify_function;
62643bce442SHeinrich Schuchardt 	evt->notify_context = notify_context;
627b095f3c8SHeinrich Schuchardt 	evt->group = group;
62843bce442SHeinrich Schuchardt 	/* Disable timers on boot up */
62943bce442SHeinrich Schuchardt 	evt->trigger_next = -1ULL;
63043bce442SHeinrich Schuchardt 	evt->is_queued = false;
63143bce442SHeinrich Schuchardt 	evt->is_signaled = false;
63243bce442SHeinrich Schuchardt 	list_add_tail(&evt->link, &efi_events);
63343bce442SHeinrich Schuchardt 	*event = evt;
63443bce442SHeinrich Schuchardt 	return EFI_SUCCESS;
635c6841592Sxypron.glpk@gmx.de }
636bee91169SAlexander Graf 
637332468f7SHeinrich Schuchardt /*
63878a88f79SMario Six  * efi_create_event_ex() - create an event in a group
6396b03cd10SHeinrich Schuchardt  * @type:            type of the event to create
6406b03cd10SHeinrich Schuchardt  * @notify_tpl:      task priority level of the event
6416b03cd10SHeinrich Schuchardt  * @notify_function: notification function of the event
6426b03cd10SHeinrich Schuchardt  * @notify_context:  pointer passed to the notification function
6436b03cd10SHeinrich Schuchardt  * @event:           created event
6446b03cd10SHeinrich Schuchardt  * @event_group:     event group
64578a88f79SMario Six  *
64678a88f79SMario Six  * This function implements the CreateEventEx service.
64778a88f79SMario Six  *
64878a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
64978a88f79SMario Six  * details.
65078a88f79SMario Six  *
65178a88f79SMario Six  * Return: status code
6529f0930e5SHeinrich Schuchardt  */
6539f0930e5SHeinrich Schuchardt efi_status_t EFIAPI efi_create_event_ex(uint32_t type, efi_uintn_t notify_tpl,
6549f0930e5SHeinrich Schuchardt 					void (EFIAPI *notify_function) (
6559f0930e5SHeinrich Schuchardt 							struct efi_event *event,
6569f0930e5SHeinrich Schuchardt 							void *context),
6579f0930e5SHeinrich Schuchardt 					void *notify_context,
6589f0930e5SHeinrich Schuchardt 					efi_guid_t *event_group,
6599f0930e5SHeinrich Schuchardt 					struct efi_event **event)
6609f0930e5SHeinrich Schuchardt {
6619f0930e5SHeinrich Schuchardt 	EFI_ENTRY("%d, 0x%zx, %p, %p, %pUl", type, notify_tpl, notify_function,
6629f0930e5SHeinrich Schuchardt 		  notify_context, event_group);
6639f0930e5SHeinrich Schuchardt 	return EFI_EXIT(efi_create_event(type, notify_tpl, notify_function,
664b095f3c8SHeinrich Schuchardt 					 notify_context, event_group, event));
6659f0930e5SHeinrich Schuchardt }
6669f0930e5SHeinrich Schuchardt 
6676b03cd10SHeinrich Schuchardt /**
66878a88f79SMario Six  * efi_create_event_ext() - create an event
6696b03cd10SHeinrich Schuchardt  * @type:            type of the event to create
6706b03cd10SHeinrich Schuchardt  * @notify_tpl:      task priority level of the event
6716b03cd10SHeinrich Schuchardt  * @notify_function: notification function of the event
6726b03cd10SHeinrich Schuchardt  * @notify_context:  pointer passed to the notification function
6736b03cd10SHeinrich Schuchardt  * @event:           created event
67478a88f79SMario Six  *
67578a88f79SMario Six  * This function implements the CreateEvent service.
67678a88f79SMario Six  *
67778a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
67878a88f79SMario Six  * details.
67978a88f79SMario Six  *
68078a88f79SMario Six  * Return: status code
681332468f7SHeinrich Schuchardt  */
68249deb455Sxypron.glpk@gmx.de static efi_status_t EFIAPI efi_create_event_ext(
683152cade3SHeinrich Schuchardt 			uint32_t type, efi_uintn_t notify_tpl,
68449deb455Sxypron.glpk@gmx.de 			void (EFIAPI *notify_function) (
68549deb455Sxypron.glpk@gmx.de 					struct efi_event *event,
68649deb455Sxypron.glpk@gmx.de 					void *context),
68749deb455Sxypron.glpk@gmx.de 			void *notify_context, struct efi_event **event)
68849deb455Sxypron.glpk@gmx.de {
68949deb455Sxypron.glpk@gmx.de 	EFI_ENTRY("%d, 0x%zx, %p, %p", type, notify_tpl, notify_function,
69049deb455Sxypron.glpk@gmx.de 		  notify_context);
69149deb455Sxypron.glpk@gmx.de 	return EFI_EXIT(efi_create_event(type, notify_tpl, notify_function,
692b095f3c8SHeinrich Schuchardt 					 notify_context, NULL, event));
69349deb455Sxypron.glpk@gmx.de }
69449deb455Sxypron.glpk@gmx.de 
6956b03cd10SHeinrich Schuchardt /**
69678a88f79SMario Six  * efi_timer_check() - check if a timer event has occurred
6976b03cd10SHeinrich Schuchardt  *
698332468f7SHeinrich Schuchardt  * Check if a timer event has occurred or a queued notification function should
699332468f7SHeinrich Schuchardt  * be called.
700332468f7SHeinrich Schuchardt  *
701bee91169SAlexander Graf  * Our timers have to work without interrupts, so we check whenever keyboard
702332468f7SHeinrich Schuchardt  * input or disk accesses happen if enough time elapsed for them to fire.
703bee91169SAlexander Graf  */
704bee91169SAlexander Graf void efi_timer_check(void)
705bee91169SAlexander Graf {
70643bce442SHeinrich Schuchardt 	struct efi_event *evt;
707bee91169SAlexander Graf 	u64 now = timer_get_us();
708bee91169SAlexander Graf 
70943bce442SHeinrich Schuchardt 	list_for_each_entry(evt, &efi_events, link) {
71043bce442SHeinrich Schuchardt 		if (evt->is_queued)
711b095f3c8SHeinrich Schuchardt 			efi_queue_event(evt, true);
71243bce442SHeinrich Schuchardt 		if (!(evt->type & EVT_TIMER) || now < evt->trigger_next)
713ca62a4f5SHeinrich Schuchardt 			continue;
71443bce442SHeinrich Schuchardt 		switch (evt->trigger_type) {
715ca62a4f5SHeinrich Schuchardt 		case EFI_TIMER_RELATIVE:
71643bce442SHeinrich Schuchardt 			evt->trigger_type = EFI_TIMER_STOP;
717ca62a4f5SHeinrich Schuchardt 			break;
718ca62a4f5SHeinrich Schuchardt 		case EFI_TIMER_PERIODIC:
71943bce442SHeinrich Schuchardt 			evt->trigger_next += evt->trigger_time;
720ca62a4f5SHeinrich Schuchardt 			break;
721ca62a4f5SHeinrich Schuchardt 		default:
722ca62a4f5SHeinrich Schuchardt 			continue;
723bee91169SAlexander Graf 		}
724b095f3c8SHeinrich Schuchardt 		evt->is_signaled = false;
72543bce442SHeinrich Schuchardt 		efi_signal_event(evt, true);
726c6841592Sxypron.glpk@gmx.de 	}
727bee91169SAlexander Graf 	WATCHDOG_RESET();
728bee91169SAlexander Graf }
729bee91169SAlexander Graf 
7306b03cd10SHeinrich Schuchardt /**
73178a88f79SMario Six  * efi_set_timer() - set the trigger time for a timer event or stop the event
73278a88f79SMario Six  * @event:        event for which the timer is set
73378a88f79SMario Six  * @type:         type of the timer
73478a88f79SMario Six  * @trigger_time: trigger period in multiples of 100 ns
735332468f7SHeinrich Schuchardt  *
736332468f7SHeinrich Schuchardt  * This is the function for internal usage in U-Boot. For the API function
737332468f7SHeinrich Schuchardt  * implementing the SetTimer service see efi_set_timer_ext.
738332468f7SHeinrich Schuchardt  *
73978a88f79SMario Six  * Return: status code
740332468f7SHeinrich Schuchardt  */
741b521d29eSxypron.glpk@gmx.de efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type,
742bee91169SAlexander Graf 			   uint64_t trigger_time)
743bee91169SAlexander Graf {
74443bce442SHeinrich Schuchardt 	/* Check that the event is valid */
74543bce442SHeinrich Schuchardt 	if (efi_is_event(event) != EFI_SUCCESS || !(event->type & EVT_TIMER))
74643bce442SHeinrich Schuchardt 		return EFI_INVALID_PARAMETER;
747bee91169SAlexander Graf 
7488787b02eSxypron.glpk@gmx.de 	/*
7498787b02eSxypron.glpk@gmx.de 	 * The parameter defines a multiple of 100 ns.
7508787b02eSxypron.glpk@gmx.de 	 * We use multiples of 1000 ns. So divide by 10.
7518787b02eSxypron.glpk@gmx.de 	 */
7527d963323SHeinrich Schuchardt 	do_div(trigger_time, 10);
753bee91169SAlexander Graf 
754bee91169SAlexander Graf 	switch (type) {
755bee91169SAlexander Graf 	case EFI_TIMER_STOP:
756c6841592Sxypron.glpk@gmx.de 		event->trigger_next = -1ULL;
757bee91169SAlexander Graf 		break;
758bee91169SAlexander Graf 	case EFI_TIMER_PERIODIC:
759bee91169SAlexander Graf 	case EFI_TIMER_RELATIVE:
76043bce442SHeinrich Schuchardt 		event->trigger_next = timer_get_us() + trigger_time;
761bee91169SAlexander Graf 		break;
762bee91169SAlexander Graf 	default:
763bfc72462Sxypron.glpk@gmx.de 		return EFI_INVALID_PARAMETER;
764bee91169SAlexander Graf 	}
765c6841592Sxypron.glpk@gmx.de 	event->trigger_type = type;
766c6841592Sxypron.glpk@gmx.de 	event->trigger_time = trigger_time;
767e190e897SHeinrich Schuchardt 	event->is_signaled = false;
768bfc72462Sxypron.glpk@gmx.de 	return EFI_SUCCESS;
769bee91169SAlexander Graf }
770bfc72462Sxypron.glpk@gmx.de 
7716b03cd10SHeinrich Schuchardt /**
77278a88f79SMario Six  * efi_set_timer_ext() - Set the trigger time for a timer event or stop the
77378a88f79SMario Six  *                       event
7746b03cd10SHeinrich Schuchardt  * @event:        event for which the timer is set
7756b03cd10SHeinrich Schuchardt  * @type:         type of the timer
7766b03cd10SHeinrich Schuchardt  * @trigger_time: trigger period in multiples of 100 ns
77778a88f79SMario Six  *
77878a88f79SMario Six  * This function implements the SetTimer service.
77978a88f79SMario Six  *
78078a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
78178a88f79SMario Six  * details.
78278a88f79SMario Six  *
78378a88f79SMario Six  *
78478a88f79SMario Six  * Return: status code
785332468f7SHeinrich Schuchardt  */
786b521d29eSxypron.glpk@gmx.de static efi_status_t EFIAPI efi_set_timer_ext(struct efi_event *event,
787b521d29eSxypron.glpk@gmx.de 					     enum efi_timer_delay type,
788bfc72462Sxypron.glpk@gmx.de 					     uint64_t trigger_time)
789bfc72462Sxypron.glpk@gmx.de {
790dee37fc9SMasahiro Yamada 	EFI_ENTRY("%p, %d, %llx", event, type, trigger_time);
791bfc72462Sxypron.glpk@gmx.de 	return EFI_EXIT(efi_set_timer(event, type, trigger_time));
792c6841592Sxypron.glpk@gmx.de }
793bee91169SAlexander Graf 
7946b03cd10SHeinrich Schuchardt /**
79578a88f79SMario Six  * efi_wait_for_event() - wait for events to be signaled
7966b03cd10SHeinrich Schuchardt  * @num_events: number of events to be waited for
7976b03cd10SHeinrich Schuchardt  * @event:      events to be waited for
7986b03cd10SHeinrich Schuchardt  * @index:      index of the event that was signaled
79978a88f79SMario Six  *
80078a88f79SMario Six  * This function implements the WaitForEvent service.
80178a88f79SMario Six  *
80278a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
80378a88f79SMario Six  * details.
80478a88f79SMario Six  *
80578a88f79SMario Six  * Return: status code
806332468f7SHeinrich Schuchardt  */
807f5a2a938SHeinrich Schuchardt static efi_status_t EFIAPI efi_wait_for_event(efi_uintn_t num_events,
8082fd945feSxypron.glpk@gmx.de 					      struct efi_event **event,
809f5a2a938SHeinrich Schuchardt 					      efi_uintn_t *index)
810bee91169SAlexander Graf {
81143bce442SHeinrich Schuchardt 	int i;
812bee91169SAlexander Graf 
813f5a2a938SHeinrich Schuchardt 	EFI_ENTRY("%zd, %p, %p", num_events, event, index);
814bee91169SAlexander Graf 
815c6841592Sxypron.glpk@gmx.de 	/* Check parameters */
816c6841592Sxypron.glpk@gmx.de 	if (!num_events || !event)
817c6841592Sxypron.glpk@gmx.de 		return EFI_EXIT(EFI_INVALID_PARAMETER);
81832f4b2f8SHeinrich Schuchardt 	/* Check TPL */
81932f4b2f8SHeinrich Schuchardt 	if (efi_tpl != TPL_APPLICATION)
82032f4b2f8SHeinrich Schuchardt 		return EFI_EXIT(EFI_UNSUPPORTED);
821c6841592Sxypron.glpk@gmx.de 	for (i = 0; i < num_events; ++i) {
82243bce442SHeinrich Schuchardt 		if (efi_is_event(event[i]) != EFI_SUCCESS)
823c6841592Sxypron.glpk@gmx.de 			return EFI_EXIT(EFI_INVALID_PARAMETER);
824c6841592Sxypron.glpk@gmx.de 		if (!event[i]->type || event[i]->type & EVT_NOTIFY_SIGNAL)
825c6841592Sxypron.glpk@gmx.de 			return EFI_EXIT(EFI_INVALID_PARAMETER);
826e190e897SHeinrich Schuchardt 		if (!event[i]->is_signaled)
827b095f3c8SHeinrich Schuchardt 			efi_queue_event(event[i], true);
828c6841592Sxypron.glpk@gmx.de 	}
829c6841592Sxypron.glpk@gmx.de 
830c6841592Sxypron.glpk@gmx.de 	/* Wait for signal */
831c6841592Sxypron.glpk@gmx.de 	for (;;) {
832c6841592Sxypron.glpk@gmx.de 		for (i = 0; i < num_events; ++i) {
833e190e897SHeinrich Schuchardt 			if (event[i]->is_signaled)
834c6841592Sxypron.glpk@gmx.de 				goto out;
835c6841592Sxypron.glpk@gmx.de 		}
836c6841592Sxypron.glpk@gmx.de 		/* Allow events to occur. */
837bee91169SAlexander Graf 		efi_timer_check();
838c6841592Sxypron.glpk@gmx.de 	}
839c6841592Sxypron.glpk@gmx.de 
840c6841592Sxypron.glpk@gmx.de out:
841c6841592Sxypron.glpk@gmx.de 	/*
842c6841592Sxypron.glpk@gmx.de 	 * Reset the signal which is passed to the caller to allow periodic
843c6841592Sxypron.glpk@gmx.de 	 * events to occur.
844c6841592Sxypron.glpk@gmx.de 	 */
845e190e897SHeinrich Schuchardt 	event[i]->is_signaled = false;
846c6841592Sxypron.glpk@gmx.de 	if (index)
847c6841592Sxypron.glpk@gmx.de 		*index = i;
848bee91169SAlexander Graf 
849bee91169SAlexander Graf 	return EFI_EXIT(EFI_SUCCESS);
850bee91169SAlexander Graf }
851bee91169SAlexander Graf 
8526b03cd10SHeinrich Schuchardt /**
85378a88f79SMario Six  * efi_signal_event_ext() - signal an EFI event
85478a88f79SMario Six  * @event: event to signal
855332468f7SHeinrich Schuchardt  *
856332468f7SHeinrich Schuchardt  * This function implements the SignalEvent service.
85778a88f79SMario Six  *
85878a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
85978a88f79SMario Six  * details.
860332468f7SHeinrich Schuchardt  *
861332468f7SHeinrich Schuchardt  * This functions sets the signaled state of the event and queues the
862332468f7SHeinrich Schuchardt  * notification function for execution.
863332468f7SHeinrich Schuchardt  *
86478a88f79SMario Six  * Return: status code
865332468f7SHeinrich Schuchardt  */
866c6841592Sxypron.glpk@gmx.de static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event)
867bee91169SAlexander Graf {
868bee91169SAlexander Graf 	EFI_ENTRY("%p", event);
86943bce442SHeinrich Schuchardt 	if (efi_is_event(event) != EFI_SUCCESS)
87043bce442SHeinrich Schuchardt 		return EFI_EXIT(EFI_INVALID_PARAMETER);
8719bc9664dSHeinrich Schuchardt 	efi_signal_event(event, true);
872bee91169SAlexander Graf 	return EFI_EXIT(EFI_SUCCESS);
873bee91169SAlexander Graf }
874bee91169SAlexander Graf 
8756b03cd10SHeinrich Schuchardt /**
87678a88f79SMario Six  * efi_close_event() - close an EFI event
87778a88f79SMario Six  * @event: event to close
878332468f7SHeinrich Schuchardt  *
879332468f7SHeinrich Schuchardt  * This function implements the CloseEvent service.
880332468f7SHeinrich Schuchardt  *
88178a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
88278a88f79SMario Six  * details.
88378a88f79SMario Six  *
88478a88f79SMario Six  * Return: status code
885332468f7SHeinrich Schuchardt  */
8862fd945feSxypron.glpk@gmx.de static efi_status_t EFIAPI efi_close_event(struct efi_event *event)
887bee91169SAlexander Graf {
888bee91169SAlexander Graf 	EFI_ENTRY("%p", event);
88943bce442SHeinrich Schuchardt 	if (efi_is_event(event) != EFI_SUCCESS)
890c6841592Sxypron.glpk@gmx.de 		return EFI_EXIT(EFI_INVALID_PARAMETER);
89143bce442SHeinrich Schuchardt 	list_del(&event->link);
89243bce442SHeinrich Schuchardt 	free(event);
89343bce442SHeinrich Schuchardt 	return EFI_EXIT(EFI_SUCCESS);
894c6841592Sxypron.glpk@gmx.de }
895bee91169SAlexander Graf 
8966b03cd10SHeinrich Schuchardt /**
89778a88f79SMario Six  * efi_check_event() - check if an event is signaled
89878a88f79SMario Six  * @event: event to check
899332468f7SHeinrich Schuchardt  *
900332468f7SHeinrich Schuchardt  * This function implements the CheckEvent service.
901332468f7SHeinrich Schuchardt  *
90278a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
90378a88f79SMario Six  * details.
904332468f7SHeinrich Schuchardt  *
90578a88f79SMario Six  * If an event is not signaled yet, the notification function is queued. The
90678a88f79SMario Six  * signaled state is cleared.
90778a88f79SMario Six  *
90878a88f79SMario Six  * Return: status code
909332468f7SHeinrich Schuchardt  */
9102fd945feSxypron.glpk@gmx.de static efi_status_t EFIAPI efi_check_event(struct efi_event *event)
911bee91169SAlexander Graf {
912bee91169SAlexander Graf 	EFI_ENTRY("%p", event);
913c6841592Sxypron.glpk@gmx.de 	efi_timer_check();
91443bce442SHeinrich Schuchardt 	if (efi_is_event(event) != EFI_SUCCESS ||
91543bce442SHeinrich Schuchardt 	    event->type & EVT_NOTIFY_SIGNAL)
91643bce442SHeinrich Schuchardt 		return EFI_EXIT(EFI_INVALID_PARAMETER);
917e190e897SHeinrich Schuchardt 	if (!event->is_signaled)
918b095f3c8SHeinrich Schuchardt 		efi_queue_event(event, true);
9197069515eSHeinrich Schuchardt 	if (event->is_signaled) {
9207069515eSHeinrich Schuchardt 		event->is_signaled = false;
921c6841592Sxypron.glpk@gmx.de 		return EFI_EXIT(EFI_SUCCESS);
9227069515eSHeinrich Schuchardt 	}
923bee91169SAlexander Graf 	return EFI_EXIT(EFI_NOT_READY);
924bee91169SAlexander Graf }
925bee91169SAlexander Graf 
9266b03cd10SHeinrich Schuchardt /**
92778a88f79SMario Six  * efi_search_obj() - find the internal EFI object for a handle
9286b03cd10SHeinrich Schuchardt  * @handle: handle to find
92978a88f79SMario Six  *
93078a88f79SMario Six  * Return: EFI object
9317b9f8ad7SHeinrich Schuchardt  */
9322074f700SHeinrich Schuchardt struct efi_object *efi_search_obj(const efi_handle_t handle)
9337b9f8ad7SHeinrich Schuchardt {
9347b9f8ad7SHeinrich Schuchardt 	struct efi_object *efiobj;
9357b9f8ad7SHeinrich Schuchardt 
9361b68153aSHeinrich Schuchardt 	list_for_each_entry(efiobj, &efi_obj_list, link) {
937fae0118eSHeinrich Schuchardt 		if (efiobj == handle)
9387b9f8ad7SHeinrich Schuchardt 			return efiobj;
9397b9f8ad7SHeinrich Schuchardt 	}
9407b9f8ad7SHeinrich Schuchardt 
9417b9f8ad7SHeinrich Schuchardt 	return NULL;
9427b9f8ad7SHeinrich Schuchardt }
9437b9f8ad7SHeinrich Schuchardt 
9446b03cd10SHeinrich Schuchardt /**
94578a88f79SMario Six  * efi_open_protocol_info_entry() - create open protocol info entry and add it
9466b03cd10SHeinrich Schuchardt  *                                  to a protocol
9476b03cd10SHeinrich Schuchardt  * @handler: handler of a protocol
94878a88f79SMario Six  *
94978a88f79SMario Six  * Return: open protocol info entry
950fe1599daSHeinrich Schuchardt  */
951fe1599daSHeinrich Schuchardt static struct efi_open_protocol_info_entry *efi_create_open_info(
952fe1599daSHeinrich Schuchardt 			struct efi_handler *handler)
953fe1599daSHeinrich Schuchardt {
954fe1599daSHeinrich Schuchardt 	struct efi_open_protocol_info_item *item;
955fe1599daSHeinrich Schuchardt 
956fe1599daSHeinrich Schuchardt 	item = calloc(1, sizeof(struct efi_open_protocol_info_item));
957fe1599daSHeinrich Schuchardt 	if (!item)
958fe1599daSHeinrich Schuchardt 		return NULL;
959fe1599daSHeinrich Schuchardt 	/* Append the item to the open protocol info list. */
960fe1599daSHeinrich Schuchardt 	list_add_tail(&item->link, &handler->open_infos);
961fe1599daSHeinrich Schuchardt 
962fe1599daSHeinrich Schuchardt 	return &item->info;
963fe1599daSHeinrich Schuchardt }
964fe1599daSHeinrich Schuchardt 
9656b03cd10SHeinrich Schuchardt /**
96678a88f79SMario Six  * efi_delete_open_info() - remove an open protocol info entry from a protocol
9676b03cd10SHeinrich Schuchardt  * @item: open protocol info entry to delete
96878a88f79SMario Six  *
96978a88f79SMario Six  * Return: status code
970fe1599daSHeinrich Schuchardt  */
971fe1599daSHeinrich Schuchardt static efi_status_t efi_delete_open_info(
972fe1599daSHeinrich Schuchardt 			struct efi_open_protocol_info_item *item)
973fe1599daSHeinrich Schuchardt {
974fe1599daSHeinrich Schuchardt 	list_del(&item->link);
975fe1599daSHeinrich Schuchardt 	free(item);
976fe1599daSHeinrich Schuchardt 	return EFI_SUCCESS;
977fe1599daSHeinrich Schuchardt }
978fe1599daSHeinrich Schuchardt 
9796b03cd10SHeinrich Schuchardt /**
98078a88f79SMario Six  * efi_add_protocol() - install new protocol on a handle
9816b03cd10SHeinrich Schuchardt  * @handle:             handle on which the protocol shall be installed
9826b03cd10SHeinrich Schuchardt  * @protocol:           GUID of the protocol to be installed
9836b03cd10SHeinrich Schuchardt  * @protocol_interface: interface of the protocol implementation
98478a88f79SMario Six  *
98578a88f79SMario Six  * Return: status code
9863f79a2b5SHeinrich Schuchardt  */
9872074f700SHeinrich Schuchardt efi_status_t efi_add_protocol(const efi_handle_t handle,
9882074f700SHeinrich Schuchardt 			      const efi_guid_t *protocol,
9893f79a2b5SHeinrich Schuchardt 			      void *protocol_interface)
9903f79a2b5SHeinrich Schuchardt {
9913f79a2b5SHeinrich Schuchardt 	struct efi_object *efiobj;
9923f79a2b5SHeinrich Schuchardt 	struct efi_handler *handler;
9933f79a2b5SHeinrich Schuchardt 	efi_status_t ret;
9943f79a2b5SHeinrich Schuchardt 
9953f79a2b5SHeinrich Schuchardt 	efiobj = efi_search_obj(handle);
9963f79a2b5SHeinrich Schuchardt 	if (!efiobj)
9973f79a2b5SHeinrich Schuchardt 		return EFI_INVALID_PARAMETER;
9983f79a2b5SHeinrich Schuchardt 	ret = efi_search_protocol(handle, protocol, NULL);
9993f79a2b5SHeinrich Schuchardt 	if (ret != EFI_NOT_FOUND)
10003f79a2b5SHeinrich Schuchardt 		return EFI_INVALID_PARAMETER;
10013f79a2b5SHeinrich Schuchardt 	handler = calloc(1, sizeof(struct efi_handler));
10023f79a2b5SHeinrich Schuchardt 	if (!handler)
10033f79a2b5SHeinrich Schuchardt 		return EFI_OUT_OF_RESOURCES;
10043f79a2b5SHeinrich Schuchardt 	handler->guid = protocol;
10053f79a2b5SHeinrich Schuchardt 	handler->protocol_interface = protocol_interface;
1006fe1599daSHeinrich Schuchardt 	INIT_LIST_HEAD(&handler->open_infos);
100769fb6b1aSHeinrich Schuchardt 	list_add_tail(&handler->link, &efiobj->protocols);
1008d5504144SHeinrich Schuchardt 	if (!guidcmp(&efi_guid_device_path, protocol))
1009d5504144SHeinrich Schuchardt 		EFI_PRINT("installed device path '%pD'\n", protocol_interface);
10103f79a2b5SHeinrich Schuchardt 	return EFI_SUCCESS;
10113f79a2b5SHeinrich Schuchardt }
10123f79a2b5SHeinrich Schuchardt 
10136b03cd10SHeinrich Schuchardt /**
101478a88f79SMario Six  * efi_install_protocol_interface() - install protocol interface
10156b03cd10SHeinrich Schuchardt  * @handle:                  handle on which the protocol shall be installed
10166b03cd10SHeinrich Schuchardt  * @protocol:                GUID of the protocol to be installed
10176b03cd10SHeinrich Schuchardt  * @protocol_interface_type: type of the interface to be installed,
1018332468f7SHeinrich Schuchardt  *                           always EFI_NATIVE_INTERFACE
10196b03cd10SHeinrich Schuchardt  * @protocol_interface:      interface of the protocol implementation
102078a88f79SMario Six  *
102178a88f79SMario Six  * This function implements the InstallProtocolInterface service.
102278a88f79SMario Six  *
102378a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
102478a88f79SMario Six  * details.
102578a88f79SMario Six  *
102678a88f79SMario Six  * Return: status code
1027332468f7SHeinrich Schuchardt  */
10281760ef57SHeinrich Schuchardt static efi_status_t EFIAPI efi_install_protocol_interface(
1029faea1041SHeinrich Schuchardt 			efi_handle_t *handle, const efi_guid_t *protocol,
10301760ef57SHeinrich Schuchardt 			int protocol_interface_type, void *protocol_interface)
1031bee91169SAlexander Graf {
1032e0549f8aSxypron.glpk@gmx.de 	efi_status_t r;
1033e0549f8aSxypron.glpk@gmx.de 
10341760ef57SHeinrich Schuchardt 	EFI_ENTRY("%p, %pUl, %d, %p", handle, protocol, protocol_interface_type,
10351760ef57SHeinrich Schuchardt 		  protocol_interface);
10361760ef57SHeinrich Schuchardt 
1037e0549f8aSxypron.glpk@gmx.de 	if (!handle || !protocol ||
1038e0549f8aSxypron.glpk@gmx.de 	    protocol_interface_type != EFI_NATIVE_INTERFACE) {
1039e0549f8aSxypron.glpk@gmx.de 		r = EFI_INVALID_PARAMETER;
1040e0549f8aSxypron.glpk@gmx.de 		goto out;
1041bee91169SAlexander Graf 	}
1042e0549f8aSxypron.glpk@gmx.de 
1043e0549f8aSxypron.glpk@gmx.de 	/* Create new handle if requested. */
1044e0549f8aSxypron.glpk@gmx.de 	if (!*handle) {
10453cc6e3feSHeinrich Schuchardt 		r = efi_create_handle(handle);
10463cc6e3feSHeinrich Schuchardt 		if (r != EFI_SUCCESS)
1047e0549f8aSxypron.glpk@gmx.de 			goto out;
1048af1408e0SHeinrich Schuchardt 		debug("%sEFI: new handle %p\n", indent_string(nesting_level),
1049af1408e0SHeinrich Schuchardt 		      *handle);
1050af1408e0SHeinrich Schuchardt 	} else {
1051af1408e0SHeinrich Schuchardt 		debug("%sEFI: handle %p\n", indent_string(nesting_level),
1052af1408e0SHeinrich Schuchardt 		      *handle);
1053e0549f8aSxypron.glpk@gmx.de 	}
10541202530dSHeinrich Schuchardt 	/* Add new protocol */
10551202530dSHeinrich Schuchardt 	r = efi_add_protocol(*handle, protocol, protocol_interface);
1056e0549f8aSxypron.glpk@gmx.de out:
10571760ef57SHeinrich Schuchardt 	return EFI_EXIT(r);
1058e0549f8aSxypron.glpk@gmx.de }
1059e0549f8aSxypron.glpk@gmx.de 
10606b03cd10SHeinrich Schuchardt /**
106178a88f79SMario Six  * efi_get_drivers() - get all drivers associated to a controller
1062fae0118eSHeinrich Schuchardt  * @handle:               handle of the controller
1063b72aaa87SHeinrich Schuchardt  * @protocol:             protocol GUID (optional)
10646b03cd10SHeinrich Schuchardt  * @number_of_drivers:    number of child controllers
10656b03cd10SHeinrich Schuchardt  * @driver_handle_buffer: handles of the the drivers
106678a88f79SMario Six  *
106778a88f79SMario Six  * The allocated buffer has to be freed with free().
106878a88f79SMario Six  *
106978a88f79SMario Six  * Return: status code
10703f9b0042SHeinrich Schuchardt  */
1071fae0118eSHeinrich Schuchardt static efi_status_t efi_get_drivers(efi_handle_t handle,
10723f9b0042SHeinrich Schuchardt 				    const efi_guid_t *protocol,
10733f9b0042SHeinrich Schuchardt 				    efi_uintn_t *number_of_drivers,
10743f9b0042SHeinrich Schuchardt 				    efi_handle_t **driver_handle_buffer)
10753f9b0042SHeinrich Schuchardt {
10763f9b0042SHeinrich Schuchardt 	struct efi_handler *handler;
10773f9b0042SHeinrich Schuchardt 	struct efi_open_protocol_info_item *item;
10783f9b0042SHeinrich Schuchardt 	efi_uintn_t count = 0, i;
10793f9b0042SHeinrich Schuchardt 	bool duplicate;
10803f9b0042SHeinrich Schuchardt 
10813f9b0042SHeinrich Schuchardt 	/* Count all driver associations */
1082fae0118eSHeinrich Schuchardt 	list_for_each_entry(handler, &handle->protocols, link) {
10833f9b0042SHeinrich Schuchardt 		if (protocol && guidcmp(handler->guid, protocol))
10843f9b0042SHeinrich Schuchardt 			continue;
10853f9b0042SHeinrich Schuchardt 		list_for_each_entry(item, &handler->open_infos, link) {
10863f9b0042SHeinrich Schuchardt 			if (item->info.attributes &
10873f9b0042SHeinrich Schuchardt 			    EFI_OPEN_PROTOCOL_BY_DRIVER)
10883f9b0042SHeinrich Schuchardt 				++count;
10893f9b0042SHeinrich Schuchardt 		}
10903f9b0042SHeinrich Schuchardt 	}
10913f9b0042SHeinrich Schuchardt 	/*
10923f9b0042SHeinrich Schuchardt 	 * Create buffer. In case of duplicate driver assignments the buffer
10933f9b0042SHeinrich Schuchardt 	 * will be too large. But that does not harm.
10943f9b0042SHeinrich Schuchardt 	 */
10953f9b0042SHeinrich Schuchardt 	*number_of_drivers = 0;
10963f9b0042SHeinrich Schuchardt 	*driver_handle_buffer = calloc(count, sizeof(efi_handle_t));
10973f9b0042SHeinrich Schuchardt 	if (!*driver_handle_buffer)
10983f9b0042SHeinrich Schuchardt 		return EFI_OUT_OF_RESOURCES;
10993f9b0042SHeinrich Schuchardt 	/* Collect unique driver handles */
1100fae0118eSHeinrich Schuchardt 	list_for_each_entry(handler, &handle->protocols, link) {
11013f9b0042SHeinrich Schuchardt 		if (protocol && guidcmp(handler->guid, protocol))
11023f9b0042SHeinrich Schuchardt 			continue;
11033f9b0042SHeinrich Schuchardt 		list_for_each_entry(item, &handler->open_infos, link) {
11043f9b0042SHeinrich Schuchardt 			if (item->info.attributes &
11053f9b0042SHeinrich Schuchardt 			    EFI_OPEN_PROTOCOL_BY_DRIVER) {
11063f9b0042SHeinrich Schuchardt 				/* Check this is a new driver */
11073f9b0042SHeinrich Schuchardt 				duplicate = false;
11083f9b0042SHeinrich Schuchardt 				for (i = 0; i < *number_of_drivers; ++i) {
11093f9b0042SHeinrich Schuchardt 					if ((*driver_handle_buffer)[i] ==
11103f9b0042SHeinrich Schuchardt 					    item->info.agent_handle)
11113f9b0042SHeinrich Schuchardt 						duplicate = true;
11123f9b0042SHeinrich Schuchardt 				}
11133f9b0042SHeinrich Schuchardt 				/* Copy handle to buffer */
11143f9b0042SHeinrich Schuchardt 				if (!duplicate) {
11153f9b0042SHeinrich Schuchardt 					i = (*number_of_drivers)++;
11163f9b0042SHeinrich Schuchardt 					(*driver_handle_buffer)[i] =
11173f9b0042SHeinrich Schuchardt 						item->info.agent_handle;
11183f9b0042SHeinrich Schuchardt 				}
11193f9b0042SHeinrich Schuchardt 			}
11203f9b0042SHeinrich Schuchardt 		}
11213f9b0042SHeinrich Schuchardt 	}
11223f9b0042SHeinrich Schuchardt 	return EFI_SUCCESS;
11233f9b0042SHeinrich Schuchardt }
11243f9b0042SHeinrich Schuchardt 
11256b03cd10SHeinrich Schuchardt /**
112678a88f79SMario Six  * efi_disconnect_all_drivers() - disconnect all drivers from a controller
1127fae0118eSHeinrich Schuchardt  * @handle:       handle of the controller
1128b72aaa87SHeinrich Schuchardt  * @protocol:     protocol GUID (optional)
11296b03cd10SHeinrich Schuchardt  * @child_handle: handle of the child to destroy
113078a88f79SMario Six  *
113178a88f79SMario Six  * This function implements the DisconnectController service.
113278a88f79SMario Six  *
113378a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
113478a88f79SMario Six  * details.
113578a88f79SMario Six  *
113678a88f79SMario Six  * Return: status code
11373f9b0042SHeinrich Schuchardt  */
1138fae0118eSHeinrich Schuchardt static efi_status_t efi_disconnect_all_drivers
1139fae0118eSHeinrich Schuchardt 				(efi_handle_t handle,
11403f9b0042SHeinrich Schuchardt 				 const efi_guid_t *protocol,
11413f9b0042SHeinrich Schuchardt 				 efi_handle_t child_handle)
11423f9b0042SHeinrich Schuchardt {
11433f9b0042SHeinrich Schuchardt 	efi_uintn_t number_of_drivers;
11443f9b0042SHeinrich Schuchardt 	efi_handle_t *driver_handle_buffer;
11453f9b0042SHeinrich Schuchardt 	efi_status_t r, ret;
11463f9b0042SHeinrich Schuchardt 
1147fae0118eSHeinrich Schuchardt 	ret = efi_get_drivers(handle, protocol, &number_of_drivers,
11483f9b0042SHeinrich Schuchardt 			      &driver_handle_buffer);
11493f9b0042SHeinrich Schuchardt 	if (ret != EFI_SUCCESS)
11503f9b0042SHeinrich Schuchardt 		return ret;
11513f9b0042SHeinrich Schuchardt 
11523f9b0042SHeinrich Schuchardt 	ret = EFI_NOT_FOUND;
11533f9b0042SHeinrich Schuchardt 	while (number_of_drivers) {
11543f9b0042SHeinrich Schuchardt 		r = EFI_CALL(efi_disconnect_controller(
1155fae0118eSHeinrich Schuchardt 				handle,
11563f9b0042SHeinrich Schuchardt 				driver_handle_buffer[--number_of_drivers],
11573f9b0042SHeinrich Schuchardt 				child_handle));
11583f9b0042SHeinrich Schuchardt 		if (r == EFI_SUCCESS)
11593f9b0042SHeinrich Schuchardt 			ret = r;
11603f9b0042SHeinrich Schuchardt 	}
11613f9b0042SHeinrich Schuchardt 	free(driver_handle_buffer);
11623f9b0042SHeinrich Schuchardt 	return ret;
11633f9b0042SHeinrich Schuchardt }
11643f9b0042SHeinrich Schuchardt 
11656b03cd10SHeinrich Schuchardt /**
11669b47f13bSHeinrich Schuchardt  * efi_uninstall_protocol() - uninstall protocol interface
11679b47f13bSHeinrich Schuchardt  *
11686b03cd10SHeinrich Schuchardt  * @handle:             handle from which the protocol shall be removed
11696b03cd10SHeinrich Schuchardt  * @protocol:           GUID of the protocol to be removed
11706b03cd10SHeinrich Schuchardt  * @protocol_interface: interface to be removed
117178a88f79SMario Six  *
11729b47f13bSHeinrich Schuchardt  * This function DOES NOT delete a handle without installed protocol.
117378a88f79SMario Six  *
117478a88f79SMario Six  * Return: status code
1175332468f7SHeinrich Schuchardt  */
11769b47f13bSHeinrich Schuchardt static efi_status_t efi_uninstall_protocol
11779b47f13bSHeinrich Schuchardt 			(efi_handle_t handle, const efi_guid_t *protocol,
1178cd534083SHeinrich Schuchardt 			 void *protocol_interface)
1179bee91169SAlexander Graf {
1180ad97373bSHeinrich Schuchardt 	struct efi_object *efiobj;
11815810511dSHeinrich Schuchardt 	struct efi_handler *handler;
1182ad97373bSHeinrich Schuchardt 	struct efi_open_protocol_info_item *item;
1183ad97373bSHeinrich Schuchardt 	struct efi_open_protocol_info_item *pos;
11845810511dSHeinrich Schuchardt 	efi_status_t r;
11854b6ed0d7Sxypron.glpk@gmx.de 
1186ad97373bSHeinrich Schuchardt 	/* Check handle */
1187ad97373bSHeinrich Schuchardt 	efiobj = efi_search_obj(handle);
1188ad97373bSHeinrich Schuchardt 	if (!efiobj) {
11894b6ed0d7Sxypron.glpk@gmx.de 		r = EFI_INVALID_PARAMETER;
11904b6ed0d7Sxypron.glpk@gmx.de 		goto out;
11914b6ed0d7Sxypron.glpk@gmx.de 	}
11925810511dSHeinrich Schuchardt 	/* Find the protocol on the handle */
11935810511dSHeinrich Schuchardt 	r = efi_search_protocol(handle, protocol, &handler);
11945810511dSHeinrich Schuchardt 	if (r != EFI_SUCCESS)
11955810511dSHeinrich Schuchardt 		goto out;
1196ad97373bSHeinrich Schuchardt 	/* Disconnect controllers */
1197ad97373bSHeinrich Schuchardt 	efi_disconnect_all_drivers(efiobj, protocol, NULL);
1198ad97373bSHeinrich Schuchardt 	if (!list_empty(&handler->open_infos)) {
11994b6ed0d7Sxypron.glpk@gmx.de 		r =  EFI_ACCESS_DENIED;
1200ad97373bSHeinrich Schuchardt 		goto out;
12014b6ed0d7Sxypron.glpk@gmx.de 	}
1202ad97373bSHeinrich Schuchardt 	/* Close protocol */
1203ad97373bSHeinrich Schuchardt 	list_for_each_entry_safe(item, pos, &handler->open_infos, link) {
1204ad97373bSHeinrich Schuchardt 		if (item->info.attributes ==
1205ad97373bSHeinrich Schuchardt 			EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL ||
1206ad97373bSHeinrich Schuchardt 		    item->info.attributes == EFI_OPEN_PROTOCOL_GET_PROTOCOL ||
1207ad97373bSHeinrich Schuchardt 		    item->info.attributes == EFI_OPEN_PROTOCOL_TEST_PROTOCOL)
1208ad97373bSHeinrich Schuchardt 			list_del(&item->link);
1209ad97373bSHeinrich Schuchardt 	}
1210ad97373bSHeinrich Schuchardt 	if (!list_empty(&handler->open_infos)) {
1211ad97373bSHeinrich Schuchardt 		r =  EFI_ACCESS_DENIED;
1212ad97373bSHeinrich Schuchardt 		goto out;
1213ad97373bSHeinrich Schuchardt 	}
1214ad97373bSHeinrich Schuchardt 	r = efi_remove_protocol(handle, protocol, protocol_interface);
12154b6ed0d7Sxypron.glpk@gmx.de out:
12169b47f13bSHeinrich Schuchardt 	return r;
12179b47f13bSHeinrich Schuchardt }
12189b47f13bSHeinrich Schuchardt 
12199b47f13bSHeinrich Schuchardt /**
12209b47f13bSHeinrich Schuchardt  * efi_uninstall_protocol_interface() - uninstall protocol interface
12219b47f13bSHeinrich Schuchardt  * @handle:             handle from which the protocol shall be removed
12229b47f13bSHeinrich Schuchardt  * @protocol:           GUID of the protocol to be removed
12239b47f13bSHeinrich Schuchardt  * @protocol_interface: interface to be removed
12249b47f13bSHeinrich Schuchardt  *
12259b47f13bSHeinrich Schuchardt  * This function implements the UninstallProtocolInterface service.
12269b47f13bSHeinrich Schuchardt  *
12279b47f13bSHeinrich Schuchardt  * See the Unified Extensible Firmware Interface (UEFI) specification for
12289b47f13bSHeinrich Schuchardt  * details.
12299b47f13bSHeinrich Schuchardt  *
12309b47f13bSHeinrich Schuchardt  * Return: status code
12319b47f13bSHeinrich Schuchardt  */
12329b47f13bSHeinrich Schuchardt static efi_status_t EFIAPI efi_uninstall_protocol_interface
12339b47f13bSHeinrich Schuchardt 			(efi_handle_t handle, const efi_guid_t *protocol,
12349b47f13bSHeinrich Schuchardt 			 void *protocol_interface)
12359b47f13bSHeinrich Schuchardt {
12369b47f13bSHeinrich Schuchardt 	efi_status_t ret;
12379b47f13bSHeinrich Schuchardt 
12389b47f13bSHeinrich Schuchardt 	EFI_ENTRY("%p, %pUl, %p", handle, protocol, protocol_interface);
12399b47f13bSHeinrich Schuchardt 
12409b47f13bSHeinrich Schuchardt 	ret = efi_uninstall_protocol(handle, protocol, protocol_interface);
12419b47f13bSHeinrich Schuchardt 	if (ret != EFI_SUCCESS)
12429b47f13bSHeinrich Schuchardt 		goto out;
12439b47f13bSHeinrich Schuchardt 
12449b47f13bSHeinrich Schuchardt 	/* If the last protocol has been removed, delete the handle. */
12459b47f13bSHeinrich Schuchardt 	if (list_empty(&handle->protocols)) {
12469b47f13bSHeinrich Schuchardt 		list_del(&handle->link);
12479b47f13bSHeinrich Schuchardt 		free(handle);
12489b47f13bSHeinrich Schuchardt 	}
12499b47f13bSHeinrich Schuchardt out:
12509b47f13bSHeinrich Schuchardt 	return EFI_EXIT(ret);
1251bee91169SAlexander Graf }
1252bee91169SAlexander Graf 
12536b03cd10SHeinrich Schuchardt /**
125478a88f79SMario Six  * efi_register_protocol_notify() - register an event for notification when a
12556b03cd10SHeinrich Schuchardt  *                                  protocol is installed.
125678a88f79SMario Six  * @protocol:     GUID of the protocol whose installation shall be notified
125778a88f79SMario Six  * @event:        event to be signaled upon installation of the protocol
125878a88f79SMario Six  * @registration: key for retrieving the registration information
1259332468f7SHeinrich Schuchardt  *
1260332468f7SHeinrich Schuchardt  * This function implements the RegisterProtocolNotify service.
1261332468f7SHeinrich Schuchardt  * See the Unified Extensible Firmware Interface (UEFI) specification
1262332468f7SHeinrich Schuchardt  * for details.
1263332468f7SHeinrich Schuchardt  *
126478a88f79SMario Six  * Return: status code
1265332468f7SHeinrich Schuchardt  */
12665a9682d0SHeinrich Schuchardt static efi_status_t EFIAPI efi_register_protocol_notify(
12675a9682d0SHeinrich Schuchardt 						const efi_guid_t *protocol,
12682fd945feSxypron.glpk@gmx.de 						struct efi_event *event,
1269bee91169SAlexander Graf 						void **registration)
1270bee91169SAlexander Graf {
1271778e6af8SRob Clark 	EFI_ENTRY("%pUl, %p, %p", protocol, event, registration);
1272bee91169SAlexander Graf 	return EFI_EXIT(EFI_OUT_OF_RESOURCES);
1273bee91169SAlexander Graf }
1274bee91169SAlexander Graf 
12756b03cd10SHeinrich Schuchardt /**
127678a88f79SMario Six  * efi_search() - determine if an EFI handle implements a protocol
12776b03cd10SHeinrich Schuchardt  * @search_type: selection criterion
12786b03cd10SHeinrich Schuchardt  * @protocol:    GUID of the protocol
12796b03cd10SHeinrich Schuchardt  * @search_key:  registration key
1280fae0118eSHeinrich Schuchardt  * @handle:      handle
128178a88f79SMario Six  *
128278a88f79SMario Six  * See the documentation of the LocateHandle service in the UEFI specification.
128378a88f79SMario Six  *
128478a88f79SMario Six  * Return: 0 if the handle implements the protocol
1285332468f7SHeinrich Schuchardt  */
1286bee91169SAlexander Graf static int efi_search(enum efi_locate_search_type search_type,
12875a9682d0SHeinrich Schuchardt 		      const efi_guid_t *protocol, void *search_key,
1288fae0118eSHeinrich Schuchardt 		      efi_handle_t handle)
1289bee91169SAlexander Graf {
129042cf1242SHeinrich Schuchardt 	efi_status_t ret;
1291bee91169SAlexander Graf 
1292bee91169SAlexander Graf 	switch (search_type) {
12939f0770ffSHeinrich Schuchardt 	case ALL_HANDLES:
1294bee91169SAlexander Graf 		return 0;
12959f0770ffSHeinrich Schuchardt 	case BY_REGISTER_NOTIFY:
129642cf1242SHeinrich Schuchardt 		/* TODO: RegisterProtocolNotify is not implemented yet */
1297bee91169SAlexander Graf 		return -1;
12989f0770ffSHeinrich Schuchardt 	case BY_PROTOCOL:
1299fae0118eSHeinrich Schuchardt 		ret = efi_search_protocol(handle, protocol, NULL);
130042cf1242SHeinrich Schuchardt 		return (ret != EFI_SUCCESS);
130142cf1242SHeinrich Schuchardt 	default:
130242cf1242SHeinrich Schuchardt 		/* Invalid search type */
1303bee91169SAlexander Graf 		return -1;
1304bee91169SAlexander Graf 	}
1305bee91169SAlexander Graf }
1306bee91169SAlexander Graf 
13076b03cd10SHeinrich Schuchardt /**
130878a88f79SMario Six  * efi_locate_handle() - locate handles implementing a protocol
13096b03cd10SHeinrich Schuchardt  * @search_type: selection criterion
13106b03cd10SHeinrich Schuchardt  * @protocol:    GUID of the protocol
13116b03cd10SHeinrich Schuchardt  * @search_key: registration key
13126b03cd10SHeinrich Schuchardt  * @buffer_size: size of the buffer to receive the handles in bytes
13136b03cd10SHeinrich Schuchardt  * @buffer:      buffer to receive the relevant handles
131478a88f79SMario Six  *
131578a88f79SMario Six  * This function is meant for U-Boot internal calls. For the API implementation
131678a88f79SMario Six  * of the LocateHandle service see efi_locate_handle_ext.
131778a88f79SMario Six  *
131878a88f79SMario Six  * Return: status code
1319332468f7SHeinrich Schuchardt  */
1320ebf199b9Sxypron.glpk@gmx.de static efi_status_t efi_locate_handle(
1321bee91169SAlexander Graf 			enum efi_locate_search_type search_type,
13225a9682d0SHeinrich Schuchardt 			const efi_guid_t *protocol, void *search_key,
1323f5a2a938SHeinrich Schuchardt 			efi_uintn_t *buffer_size, efi_handle_t *buffer)
1324bee91169SAlexander Graf {
1325caf864e4SHeinrich Schuchardt 	struct efi_object *efiobj;
1326f5a2a938SHeinrich Schuchardt 	efi_uintn_t size = 0;
1327bee91169SAlexander Graf 
1328caf864e4SHeinrich Schuchardt 	/* Check parameters */
1329caf864e4SHeinrich Schuchardt 	switch (search_type) {
1330caf864e4SHeinrich Schuchardt 	case ALL_HANDLES:
1331caf864e4SHeinrich Schuchardt 		break;
1332caf864e4SHeinrich Schuchardt 	case BY_REGISTER_NOTIFY:
1333caf864e4SHeinrich Schuchardt 		if (!search_key)
1334caf864e4SHeinrich Schuchardt 			return EFI_INVALID_PARAMETER;
1335caf864e4SHeinrich Schuchardt 		/* RegisterProtocolNotify is not implemented yet */
1336caf864e4SHeinrich Schuchardt 		return EFI_UNSUPPORTED;
1337caf864e4SHeinrich Schuchardt 	case BY_PROTOCOL:
1338caf864e4SHeinrich Schuchardt 		if (!protocol)
1339caf864e4SHeinrich Schuchardt 			return EFI_INVALID_PARAMETER;
1340caf864e4SHeinrich Schuchardt 		break;
1341caf864e4SHeinrich Schuchardt 	default:
1342caf864e4SHeinrich Schuchardt 		return EFI_INVALID_PARAMETER;
1343bee91169SAlexander Graf 	}
1344caf864e4SHeinrich Schuchardt 
1345caf864e4SHeinrich Schuchardt 	/*
1346caf864e4SHeinrich Schuchardt 	 * efi_locate_handle_buffer uses this function for
1347caf864e4SHeinrich Schuchardt 	 * the calculation of the necessary buffer size.
1348caf864e4SHeinrich Schuchardt 	 * So do not require a buffer for buffersize == 0.
1349caf864e4SHeinrich Schuchardt 	 */
1350caf864e4SHeinrich Schuchardt 	if (!buffer_size || (*buffer_size && !buffer))
1351caf864e4SHeinrich Schuchardt 		return EFI_INVALID_PARAMETER;
1352caf864e4SHeinrich Schuchardt 
1353caf864e4SHeinrich Schuchardt 	/* Count how much space we need */
1354caf864e4SHeinrich Schuchardt 	list_for_each_entry(efiobj, &efi_obj_list, link) {
1355caf864e4SHeinrich Schuchardt 		if (!efi_search(search_type, protocol, search_key, efiobj))
1356caf864e4SHeinrich Schuchardt 			size += sizeof(void *);
1357bee91169SAlexander Graf 	}
1358bee91169SAlexander Graf 
1359bee91169SAlexander Graf 	if (*buffer_size < size) {
1360bee91169SAlexander Graf 		*buffer_size = size;
136126329584Sxypron.glpk@gmx.de 		return EFI_BUFFER_TOO_SMALL;
1362bee91169SAlexander Graf 	}
1363bee91169SAlexander Graf 
1364796a78cbSRob Clark 	*buffer_size = size;
1365796a78cbSRob Clark 	if (size == 0)
1366796a78cbSRob Clark 		return EFI_NOT_FOUND;
1367796a78cbSRob Clark 
1368bee91169SAlexander Graf 	/* Then fill the array */
1369caf864e4SHeinrich Schuchardt 	list_for_each_entry(efiobj, &efi_obj_list, link) {
1370caf864e4SHeinrich Schuchardt 		if (!efi_search(search_type, protocol, search_key, efiobj))
1371fae0118eSHeinrich Schuchardt 			*buffer++ = efiobj;
1372bee91169SAlexander Graf 	}
1373bee91169SAlexander Graf 
137426329584Sxypron.glpk@gmx.de 	return EFI_SUCCESS;
137526329584Sxypron.glpk@gmx.de }
137626329584Sxypron.glpk@gmx.de 
13776b03cd10SHeinrich Schuchardt /**
137878a88f79SMario Six  * efi_locate_handle_ext() - locate handles implementing a protocol.
13796b03cd10SHeinrich Schuchardt  * @search_type: selection criterion
13806b03cd10SHeinrich Schuchardt  * @protocol:    GUID of the protocol
13816b03cd10SHeinrich Schuchardt  * @search_key:  registration key
13826b03cd10SHeinrich Schuchardt  * @buffer_size: size of the buffer to receive the handles in bytes
13836b03cd10SHeinrich Schuchardt  * @buffer:      buffer to receive the relevant handles
138478a88f79SMario Six  *
138578a88f79SMario Six  * This function implements the LocateHandle service.
138678a88f79SMario Six  *
138778a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
138878a88f79SMario Six  * details.
138978a88f79SMario Six  *
139078a88f79SMario Six  * Return: 0 if the handle implements the protocol
1391332468f7SHeinrich Schuchardt  */
139226329584Sxypron.glpk@gmx.de static efi_status_t EFIAPI efi_locate_handle_ext(
139326329584Sxypron.glpk@gmx.de 			enum efi_locate_search_type search_type,
13945a9682d0SHeinrich Schuchardt 			const efi_guid_t *protocol, void *search_key,
1395f5a2a938SHeinrich Schuchardt 			efi_uintn_t *buffer_size, efi_handle_t *buffer)
139626329584Sxypron.glpk@gmx.de {
1397778e6af8SRob Clark 	EFI_ENTRY("%d, %pUl, %p, %p, %p", search_type, protocol, search_key,
139826329584Sxypron.glpk@gmx.de 		  buffer_size, buffer);
139926329584Sxypron.glpk@gmx.de 
140026329584Sxypron.glpk@gmx.de 	return EFI_EXIT(efi_locate_handle(search_type, protocol, search_key,
140126329584Sxypron.glpk@gmx.de 			buffer_size, buffer));
1402bee91169SAlexander Graf }
1403bee91169SAlexander Graf 
14046b03cd10SHeinrich Schuchardt /**
140578a88f79SMario Six  * efi_remove_configuration_table() - collapses configuration table entries,
14066b03cd10SHeinrich Schuchardt  *                                    removing index i
14076b03cd10SHeinrich Schuchardt  *
14086b03cd10SHeinrich Schuchardt  * @i: index of the table entry to be removed
14096b03cd10SHeinrich Schuchardt  */
1410d98cdf6aSAlexander Graf static void efi_remove_configuration_table(int i)
1411d98cdf6aSAlexander Graf {
14124182a129SHeinrich Schuchardt 	struct efi_configuration_table *this = &systab.tables[i];
14134182a129SHeinrich Schuchardt 	struct efi_configuration_table *next = &systab.tables[i + 1];
14144182a129SHeinrich Schuchardt 	struct efi_configuration_table *end = &systab.tables[systab.nr_tables];
1415d98cdf6aSAlexander Graf 
1416d98cdf6aSAlexander Graf 	memmove(this, next, (ulong)end - (ulong)next);
1417d98cdf6aSAlexander Graf 	systab.nr_tables--;
1418d98cdf6aSAlexander Graf }
1419d98cdf6aSAlexander Graf 
14206b03cd10SHeinrich Schuchardt /**
142178a88f79SMario Six  * efi_install_configuration_table() - adds, updates, or removes a
142278a88f79SMario Six  *                                     configuration table
142378a88f79SMario Six  * @guid:  GUID of the installed table
142478a88f79SMario Six  * @table: table to be installed
1425332468f7SHeinrich Schuchardt  *
1426332468f7SHeinrich Schuchardt  * This function is used for internal calls. For the API implementation of the
1427332468f7SHeinrich Schuchardt  * InstallConfigurationTable service see efi_install_configuration_table_ext.
1428332468f7SHeinrich Schuchardt  *
142978a88f79SMario Six  * Return: status code
1430332468f7SHeinrich Schuchardt  */
1431ab9efa97SHeinrich Schuchardt efi_status_t efi_install_configuration_table(const efi_guid_t *guid,
1432ab9efa97SHeinrich Schuchardt 					     void *table)
1433bee91169SAlexander Graf {
1434b095f3c8SHeinrich Schuchardt 	struct efi_event *evt;
1435bee91169SAlexander Graf 	int i;
1436bee91169SAlexander Graf 
1437eb68b4efSHeinrich Schuchardt 	if (!guid)
1438eb68b4efSHeinrich Schuchardt 		return EFI_INVALID_PARAMETER;
1439eb68b4efSHeinrich Schuchardt 
1440b72aaa87SHeinrich Schuchardt 	/* Check for GUID override */
1441bee91169SAlexander Graf 	for (i = 0; i < systab.nr_tables; i++) {
14424182a129SHeinrich Schuchardt 		if (!guidcmp(guid, &systab.tables[i].guid)) {
1443d98cdf6aSAlexander Graf 			if (table)
14444182a129SHeinrich Schuchardt 				systab.tables[i].table = table;
1445d98cdf6aSAlexander Graf 			else
1446d98cdf6aSAlexander Graf 				efi_remove_configuration_table(i);
1447b095f3c8SHeinrich Schuchardt 			goto out;
1448bee91169SAlexander Graf 		}
1449bee91169SAlexander Graf 	}
1450bee91169SAlexander Graf 
1451d98cdf6aSAlexander Graf 	if (!table)
1452d98cdf6aSAlexander Graf 		return EFI_NOT_FOUND;
1453d98cdf6aSAlexander Graf 
1454bee91169SAlexander Graf 	/* No override, check for overflow */
14554182a129SHeinrich Schuchardt 	if (i >= EFI_MAX_CONFIGURATION_TABLES)
1456488bf12dSAlexander Graf 		return EFI_OUT_OF_RESOURCES;
1457bee91169SAlexander Graf 
1458bee91169SAlexander Graf 	/* Add a new entry */
14594182a129SHeinrich Schuchardt 	memcpy(&systab.tables[i].guid, guid, sizeof(*guid));
14604182a129SHeinrich Schuchardt 	systab.tables[i].table = table;
1461aba5e919SAlexander Graf 	systab.nr_tables = i + 1;
1462bee91169SAlexander Graf 
1463b095f3c8SHeinrich Schuchardt out:
1464b72aaa87SHeinrich Schuchardt 	/* systab.nr_tables may have changed. So we need to update the CRC32 */
146555d8ee3bSHeinrich Schuchardt 	efi_update_table_header_crc32(&systab.hdr);
146655d8ee3bSHeinrich Schuchardt 
1467b095f3c8SHeinrich Schuchardt 	/* Notify that the configuration table was changed */
1468b095f3c8SHeinrich Schuchardt 	list_for_each_entry(evt, &efi_events, link) {
1469b095f3c8SHeinrich Schuchardt 		if (evt->group && !guidcmp(evt->group, guid)) {
1470b095f3c8SHeinrich Schuchardt 			efi_signal_event(evt, false);
1471b095f3c8SHeinrich Schuchardt 			break;
1472b095f3c8SHeinrich Schuchardt 		}
1473b095f3c8SHeinrich Schuchardt 	}
1474b095f3c8SHeinrich Schuchardt 
1475488bf12dSAlexander Graf 	return EFI_SUCCESS;
1476488bf12dSAlexander Graf }
1477488bf12dSAlexander Graf 
14786b03cd10SHeinrich Schuchardt /**
147978a88f79SMario Six  * efi_install_configuration_table_ex() - Adds, updates, or removes a
14806b03cd10SHeinrich Schuchardt  *                                        configuration table.
14816b03cd10SHeinrich Schuchardt  * @guid:  GUID of the installed table
14826b03cd10SHeinrich Schuchardt  * @table: table to be installed
148378a88f79SMario Six  *
148478a88f79SMario Six  * This function implements the InstallConfigurationTable service.
148578a88f79SMario Six  *
148678a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
148778a88f79SMario Six  * details.
148878a88f79SMario Six  *
148978a88f79SMario Six  * Return: status code
1490332468f7SHeinrich Schuchardt  */
1491488bf12dSAlexander Graf static efi_status_t EFIAPI efi_install_configuration_table_ext(efi_guid_t *guid,
1492488bf12dSAlexander Graf 							       void *table)
1493488bf12dSAlexander Graf {
1494778e6af8SRob Clark 	EFI_ENTRY("%pUl, %p", guid, table);
1495488bf12dSAlexander Graf 	return EFI_EXIT(efi_install_configuration_table(guid, table));
1496bee91169SAlexander Graf }
1497bee91169SAlexander Graf 
14986b03cd10SHeinrich Schuchardt /**
149978a88f79SMario Six  * efi_setup_loaded_image() - initialize a loaded image
150078a88f79SMario Six  * @info:        loaded image info to be passed to the entry point of the image
150178a88f79SMario Six  * @obj:         internal object associated with the loaded image
150278a88f79SMario Six  * @device_path: device path of the loaded image
150378a88f79SMario Six  * @file_path:   file path of the loaded image
15046b03cd10SHeinrich Schuchardt  *
15056b03cd10SHeinrich Schuchardt  * Initialize a loaded_image_info and loaded_image_info object with correct
150695c5553eSRob Clark  * protocols, boot-device, etc.
1507332468f7SHeinrich Schuchardt  *
150878a88f79SMario Six  * Return: status code
150995c5553eSRob Clark  */
1510c982874eSHeinrich Schuchardt efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path,
1511c982874eSHeinrich Schuchardt 				    struct efi_device_path *file_path,
1512c982874eSHeinrich Schuchardt 				    struct efi_loaded_image_obj **handle_ptr,
1513c982874eSHeinrich Schuchardt 				    struct efi_loaded_image **info_ptr)
151495c5553eSRob Clark {
151548b66230SHeinrich Schuchardt 	efi_status_t ret;
1516c982874eSHeinrich Schuchardt 	struct efi_loaded_image *info;
1517c982874eSHeinrich Schuchardt 	struct efi_loaded_image_obj *obj;
1518c982874eSHeinrich Schuchardt 
1519c982874eSHeinrich Schuchardt 	info = calloc(1, sizeof(*info));
1520c982874eSHeinrich Schuchardt 	if (!info)
1521c982874eSHeinrich Schuchardt 		return EFI_OUT_OF_RESOURCES;
1522c982874eSHeinrich Schuchardt 	obj = calloc(1, sizeof(*obj));
1523c982874eSHeinrich Schuchardt 	if (!obj) {
1524c982874eSHeinrich Schuchardt 		free(info);
1525c982874eSHeinrich Schuchardt 		return EFI_OUT_OF_RESOURCES;
1526c982874eSHeinrich Schuchardt 	}
152748b66230SHeinrich Schuchardt 
152844549d62SHeinrich Schuchardt 	/* Add internal object to object list */
1529d39646a3SHeinrich Schuchardt 	efi_add_handle(&obj->header);
1530c982874eSHeinrich Schuchardt 
1531c982874eSHeinrich Schuchardt 	if (info_ptr)
1532c982874eSHeinrich Schuchardt 		*info_ptr = info;
1533c982874eSHeinrich Schuchardt 	if (handle_ptr)
1534c982874eSHeinrich Schuchardt 		*handle_ptr = obj;
153595c5553eSRob Clark 
153695147313SHeinrich Schuchardt 	info->revision =  EFI_LOADED_IMAGE_PROTOCOL_REVISION;
153795c5553eSRob Clark 	info->file_path = file_path;
15387e1effceSHeinrich Schuchardt 	info->system_table = &systab;
153995c5553eSRob Clark 
15407df5af6fSHeinrich Schuchardt 	if (device_path) {
15417df5af6fSHeinrich Schuchardt 		info->device_handle = efi_dp_find_obj(device_path, NULL);
154248b66230SHeinrich Schuchardt 		/*
154348b66230SHeinrich Schuchardt 		 * When asking for the device path interface, return
154448b66230SHeinrich Schuchardt 		 * bootefi_device_path
154548b66230SHeinrich Schuchardt 		 */
1546d39646a3SHeinrich Schuchardt 		ret = efi_add_protocol(&obj->header,
1547c982874eSHeinrich Schuchardt 				       &efi_guid_device_path, device_path);
154848b66230SHeinrich Schuchardt 		if (ret != EFI_SUCCESS)
154948b66230SHeinrich Schuchardt 			goto failure;
15507df5af6fSHeinrich Schuchardt 	}
155148b66230SHeinrich Schuchardt 
155248b66230SHeinrich Schuchardt 	/*
155348b66230SHeinrich Schuchardt 	 * When asking for the loaded_image interface, just
155448b66230SHeinrich Schuchardt 	 * return handle which points to loaded_image_info
155548b66230SHeinrich Schuchardt 	 */
1556d39646a3SHeinrich Schuchardt 	ret = efi_add_protocol(&obj->header,
1557c982874eSHeinrich Schuchardt 			       &efi_guid_loaded_image, info);
155848b66230SHeinrich Schuchardt 	if (ret != EFI_SUCCESS)
155948b66230SHeinrich Schuchardt 		goto failure;
156048b66230SHeinrich Schuchardt 
156156d92888SHeinrich Schuchardt 	return ret;
156248b66230SHeinrich Schuchardt failure:
156348b66230SHeinrich Schuchardt 	printf("ERROR: Failure to install protocols for loaded image\n");
156456d92888SHeinrich Schuchardt 	return ret;
156595c5553eSRob Clark }
156695c5553eSRob Clark 
15676b03cd10SHeinrich Schuchardt /**
156878a88f79SMario Six  * efi_load_image_from_path() - load an image using a file path
15696b03cd10SHeinrich Schuchardt  * @file_path: the path of the image to load
15706b03cd10SHeinrich Schuchardt  * @buffer:    buffer containing the loaded image
157178a88f79SMario Six  *
157278a88f79SMario Six  * Return: status code
1573332468f7SHeinrich Schuchardt  */
15749975fe96SRob Clark efi_status_t efi_load_image_from_path(struct efi_device_path *file_path,
1575838ee4b4SRob Clark 				      void **buffer)
1576838ee4b4SRob Clark {
1577838ee4b4SRob Clark 	struct efi_file_info *info = NULL;
1578838ee4b4SRob Clark 	struct efi_file_handle *f;
1579838ee4b4SRob Clark 	static efi_status_t ret;
1580b6dd5777SHeinrich Schuchardt 	efi_uintn_t bs;
1581838ee4b4SRob Clark 
1582838ee4b4SRob Clark 	f = efi_file_from_path(file_path);
1583838ee4b4SRob Clark 	if (!f)
1584838ee4b4SRob Clark 		return EFI_DEVICE_ERROR;
1585838ee4b4SRob Clark 
1586838ee4b4SRob Clark 	bs = 0;
1587838ee4b4SRob Clark 	EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid,
1588838ee4b4SRob Clark 				  &bs, info));
1589838ee4b4SRob Clark 	if (ret == EFI_BUFFER_TOO_SMALL) {
1590838ee4b4SRob Clark 		info = malloc(bs);
1591838ee4b4SRob Clark 		EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid,
1592838ee4b4SRob Clark 					  &bs, info));
1593838ee4b4SRob Clark 	}
1594838ee4b4SRob Clark 	if (ret != EFI_SUCCESS)
1595838ee4b4SRob Clark 		goto error;
1596838ee4b4SRob Clark 
1597838ee4b4SRob Clark 	ret = efi_allocate_pool(EFI_LOADER_DATA, info->file_size, buffer);
1598838ee4b4SRob Clark 	if (ret)
1599838ee4b4SRob Clark 		goto error;
1600838ee4b4SRob Clark 
1601b6dd5777SHeinrich Schuchardt 	bs = info->file_size;
1602b6dd5777SHeinrich Schuchardt 	EFI_CALL(ret = f->read(f, &bs, *buffer));
1603838ee4b4SRob Clark 
1604838ee4b4SRob Clark error:
1605838ee4b4SRob Clark 	free(info);
1606838ee4b4SRob Clark 	EFI_CALL(f->close(f));
1607838ee4b4SRob Clark 
1608838ee4b4SRob Clark 	if (ret != EFI_SUCCESS) {
1609838ee4b4SRob Clark 		efi_free_pool(*buffer);
1610838ee4b4SRob Clark 		*buffer = NULL;
1611838ee4b4SRob Clark 	}
1612838ee4b4SRob Clark 
1613838ee4b4SRob Clark 	return ret;
1614838ee4b4SRob Clark }
1615838ee4b4SRob Clark 
16166b03cd10SHeinrich Schuchardt /**
161778a88f79SMario Six  * efi_load_image() - load an EFI image into memory
16186b03cd10SHeinrich Schuchardt  * @boot_policy:   true for request originating from the boot manager
16196b03cd10SHeinrich Schuchardt  * @parent_image:  the caller's image handle
16206b03cd10SHeinrich Schuchardt  * @file_path:     the path of the image to load
16216b03cd10SHeinrich Schuchardt  * @source_buffer: memory location from which the image is installed
162278a88f79SMario Six  * @source_size:   size of the memory area from which the image is installed
16236b03cd10SHeinrich Schuchardt  * @image_handle:  handle for the newly installed image
162478a88f79SMario Six  *
162578a88f79SMario Six  * This function implements the LoadImage service.
162678a88f79SMario Six  *
162778a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification
162878a88f79SMario Six  * for details.
162978a88f79SMario Six  *
163078a88f79SMario Six  * Return: status code
1631332468f7SHeinrich Schuchardt  */
1632bee91169SAlexander Graf static efi_status_t EFIAPI efi_load_image(bool boot_policy,
1633bee91169SAlexander Graf 					  efi_handle_t parent_image,
1634bee91169SAlexander Graf 					  struct efi_device_path *file_path,
1635bee91169SAlexander Graf 					  void *source_buffer,
16367fb96a10SHeinrich Schuchardt 					  efi_uintn_t source_size,
1637bee91169SAlexander Graf 					  efi_handle_t *image_handle)
1638bee91169SAlexander Graf {
16391c3b2f4aSTom Rini 	struct efi_loaded_image *info = NULL;
1640c982874eSHeinrich Schuchardt 	struct efi_loaded_image_obj **image_obj =
1641c982874eSHeinrich Schuchardt 		(struct efi_loaded_image_obj **)image_handle;
1642b9b17598SHeinrich Schuchardt 	efi_status_t ret;
1643bee91169SAlexander Graf 
16447fb96a10SHeinrich Schuchardt 	EFI_ENTRY("%d, %p, %pD, %p, %zd, %p", boot_policy, parent_image,
1645bee91169SAlexander Graf 		  file_path, source_buffer, source_size, image_handle);
1646838ee4b4SRob Clark 
164728a4fd46SHeinrich Schuchardt 	if (!image_handle || !parent_image) {
164828a4fd46SHeinrich Schuchardt 		ret = EFI_INVALID_PARAMETER;
164928a4fd46SHeinrich Schuchardt 		goto error;
165028a4fd46SHeinrich Schuchardt 	}
165128a4fd46SHeinrich Schuchardt 
165228a4fd46SHeinrich Schuchardt 	if (!source_buffer && !file_path) {
165328a4fd46SHeinrich Schuchardt 		ret = EFI_NOT_FOUND;
165428a4fd46SHeinrich Schuchardt 		goto error;
165528a4fd46SHeinrich Schuchardt 	}
165628a4fd46SHeinrich Schuchardt 
1657838ee4b4SRob Clark 	if (!source_buffer) {
1658838ee4b4SRob Clark 		struct efi_device_path *dp, *fp;
1659838ee4b4SRob Clark 
16609975fe96SRob Clark 		ret = efi_load_image_from_path(file_path, &source_buffer);
1661b9b17598SHeinrich Schuchardt 		if (ret != EFI_SUCCESS)
1662b9b17598SHeinrich Schuchardt 			goto failure;
1663838ee4b4SRob Clark 		/*
1664838ee4b4SRob Clark 		 * split file_path which contains both the device and
1665838ee4b4SRob Clark 		 * file parts:
1666838ee4b4SRob Clark 		 */
1667838ee4b4SRob Clark 		efi_dp_split_file_path(file_path, &dp, &fp);
1668c982874eSHeinrich Schuchardt 		ret = efi_setup_loaded_image(dp, fp, image_obj, &info);
1669b9b17598SHeinrich Schuchardt 		if (ret != EFI_SUCCESS)
1670b9b17598SHeinrich Schuchardt 			goto failure;
1671838ee4b4SRob Clark 	} else {
1672b72aaa87SHeinrich Schuchardt 		/* In this case, file_path is the "device" path, i.e.
1673838ee4b4SRob Clark 		 * something like a HARDWARE_DEVICE:MEMORY_MAPPED
1674838ee4b4SRob Clark 		 */
1675c982874eSHeinrich Schuchardt 		ret = efi_setup_loaded_image(file_path, NULL, image_obj, &info);
1676b9b17598SHeinrich Schuchardt 		if (ret != EFI_SUCCESS)
1677c982874eSHeinrich Schuchardt 			goto error;
1678838ee4b4SRob Clark 	}
1679c982874eSHeinrich Schuchardt 	(*image_obj)->entry = efi_load_pe(*image_obj, source_buffer, info);
1680c982874eSHeinrich Schuchardt 	if (!(*image_obj)->entry) {
1681b9b17598SHeinrich Schuchardt 		ret = EFI_UNSUPPORTED;
1682b9b17598SHeinrich Schuchardt 		goto failure;
1683bee91169SAlexander Graf 	}
168432fc2ac3SHeinrich Schuchardt 	info->system_table = &systab;
168532fc2ac3SHeinrich Schuchardt 	info->parent_handle = parent_image;
1686bee91169SAlexander Graf 	return EFI_EXIT(EFI_SUCCESS);
1687b9b17598SHeinrich Schuchardt failure:
1688c982874eSHeinrich Schuchardt 	efi_delete_handle(*image_handle);
1689c982874eSHeinrich Schuchardt 	*image_handle = NULL;
1690b9b17598SHeinrich Schuchardt 	free(info);
169128a4fd46SHeinrich Schuchardt error:
1692b9b17598SHeinrich Schuchardt 	return EFI_EXIT(ret);
1693bee91169SAlexander Graf }
1694bee91169SAlexander Graf 
16956b03cd10SHeinrich Schuchardt /**
1696b72aaa87SHeinrich Schuchardt  * efi_start_image() - call the entry point of an image
16976b03cd10SHeinrich Schuchardt  * @image_handle:   handle of the image
16986b03cd10SHeinrich Schuchardt  * @exit_data_size: size of the buffer
16996b03cd10SHeinrich Schuchardt  * @exit_data:      buffer to receive the exit data of the called image
170078a88f79SMario Six  *
170178a88f79SMario Six  * This function implements the StartImage service.
170278a88f79SMario Six  *
170378a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
170478a88f79SMario Six  * details.
170578a88f79SMario Six  *
170678a88f79SMario Six  * Return: status code
1707332468f7SHeinrich Schuchardt  */
1708bee91169SAlexander Graf static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
1709cc8e3417SHeinrich Schuchardt 					   efi_uintn_t *exit_data_size,
1710cc8e3417SHeinrich Schuchardt 					   u16 **exit_data)
1711bee91169SAlexander Graf {
1712c982874eSHeinrich Schuchardt 	struct efi_loaded_image_obj *image_obj =
1713c982874eSHeinrich Schuchardt 		(struct efi_loaded_image_obj *)image_handle;
1714727a1afbSHeinrich Schuchardt 	efi_status_t ret;
1715bee91169SAlexander Graf 
1716bee91169SAlexander Graf 	EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data);
1717bee91169SAlexander Graf 
1718f31239acSAlexander Graf 	efi_is_direct_boot = false;
1719f31239acSAlexander Graf 
1720bee91169SAlexander Graf 	/* call the image! */
1721c982874eSHeinrich Schuchardt 	if (setjmp(&image_obj->exit_jmp)) {
1722727a1afbSHeinrich Schuchardt 		/*
1723727a1afbSHeinrich Schuchardt 		 * We called the entry point of the child image with EFI_CALL
1724727a1afbSHeinrich Schuchardt 		 * in the lines below. The child image called the Exit() boot
1725727a1afbSHeinrich Schuchardt 		 * service efi_exit() which executed the long jump that brought
1726727a1afbSHeinrich Schuchardt 		 * us to the current line. This implies that the second half
1727727a1afbSHeinrich Schuchardt 		 * of the EFI_CALL macro has not been executed.
1728727a1afbSHeinrich Schuchardt 		 */
1729727a1afbSHeinrich Schuchardt #ifdef CONFIG_ARM
1730727a1afbSHeinrich Schuchardt 		/*
1731727a1afbSHeinrich Schuchardt 		 * efi_exit() called efi_restore_gd(). We have to undo this
1732727a1afbSHeinrich Schuchardt 		 * otherwise __efi_entry_check() will put the wrong value into
1733727a1afbSHeinrich Schuchardt 		 * app_gd.
1734727a1afbSHeinrich Schuchardt 		 */
1735727a1afbSHeinrich Schuchardt 		gd = app_gd;
1736727a1afbSHeinrich Schuchardt #endif
1737727a1afbSHeinrich Schuchardt 		/*
1738727a1afbSHeinrich Schuchardt 		 * To get ready to call EFI_EXIT below we have to execute the
1739727a1afbSHeinrich Schuchardt 		 * missed out steps of EFI_CALL.
1740727a1afbSHeinrich Schuchardt 		 */
1741727a1afbSHeinrich Schuchardt 		assert(__efi_entry_check());
1742727a1afbSHeinrich Schuchardt 		debug("%sEFI: %lu returned by started image\n",
1743727a1afbSHeinrich Schuchardt 		      __efi_nesting_dec(),
1744c982874eSHeinrich Schuchardt 		      (unsigned long)((uintptr_t)image_obj->exit_status &
1745727a1afbSHeinrich Schuchardt 				      ~EFI_ERROR_MASK));
1746c982874eSHeinrich Schuchardt 		return EFI_EXIT(image_obj->exit_status);
1747a86aeaf2SAlexander Graf 	}
1748a86aeaf2SAlexander Graf 
1749c982874eSHeinrich Schuchardt 	ret = EFI_CALL(image_obj->entry(image_handle, &systab));
1750bee91169SAlexander Graf 
175156672bf5SAlexander Graf 	/*
175256672bf5SAlexander Graf 	 * Usually UEFI applications call Exit() instead of returning.
1753b72aaa87SHeinrich Schuchardt 	 * But because the world doesn't consist of ponies and unicorns,
175456672bf5SAlexander Graf 	 * we're happy to emulate that behavior on behalf of a payload
175556672bf5SAlexander Graf 	 * that forgot.
175656672bf5SAlexander Graf 	 */
175756672bf5SAlexander Graf 	return EFI_CALL(systab.boottime->exit(image_handle, ret, 0, NULL));
1758bee91169SAlexander Graf }
1759bee91169SAlexander Graf 
17606b03cd10SHeinrich Schuchardt /**
176178a88f79SMario Six  * efi_exit() - leave an EFI application or driver
17626b03cd10SHeinrich Schuchardt  * @image_handle:   handle of the application or driver that is exiting
17636b03cd10SHeinrich Schuchardt  * @exit_status:    status code
17646b03cd10SHeinrich Schuchardt  * @exit_data_size: size of the buffer in bytes
17656b03cd10SHeinrich Schuchardt  * @exit_data:      buffer with data describing an error
176678a88f79SMario Six  *
176778a88f79SMario Six  * This function implements the Exit service.
176878a88f79SMario Six  *
176978a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
177078a88f79SMario Six  * details.
177178a88f79SMario Six  *
177278a88f79SMario Six  * Return: status code
1773332468f7SHeinrich Schuchardt  */
1774a86aeaf2SAlexander Graf static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
1775ab9efa97SHeinrich Schuchardt 				    efi_status_t exit_status,
1776cc8e3417SHeinrich Schuchardt 				    efi_uintn_t exit_data_size,
1777cc8e3417SHeinrich Schuchardt 				    u16 *exit_data)
1778bee91169SAlexander Graf {
177944549d62SHeinrich Schuchardt 	/*
178044549d62SHeinrich Schuchardt 	 * TODO: We should call the unload procedure of the loaded
178144549d62SHeinrich Schuchardt 	 *	 image protocol.
178244549d62SHeinrich Schuchardt 	 */
1783c982874eSHeinrich Schuchardt 	struct efi_loaded_image_obj *image_obj =
1784c982874eSHeinrich Schuchardt 		(struct efi_loaded_image_obj *)image_handle;
1785a86aeaf2SAlexander Graf 
1786cc8e3417SHeinrich Schuchardt 	EFI_ENTRY("%p, %ld, %zu, %p", image_handle, exit_status,
1787bee91169SAlexander Graf 		  exit_data_size, exit_data);
1788a86aeaf2SAlexander Graf 
1789a148920eSAlexander Graf 	/* Make sure entry/exit counts for EFI world cross-overs match */
1790727a1afbSHeinrich Schuchardt 	EFI_EXIT(exit_status);
1791da94073bSHeinrich Schuchardt 
1792a148920eSAlexander Graf 	/*
1793a148920eSAlexander Graf 	 * But longjmp out with the U-Boot gd, not the application's, as
1794a148920eSAlexander Graf 	 * the other end is a setjmp call inside EFI context.
1795a148920eSAlexander Graf 	 */
1796a148920eSAlexander Graf 	efi_restore_gd();
1797a148920eSAlexander Graf 
1798c982874eSHeinrich Schuchardt 	image_obj->exit_status = exit_status;
1799c982874eSHeinrich Schuchardt 	longjmp(&image_obj->exit_jmp, 1);
1800a86aeaf2SAlexander Graf 
1801a86aeaf2SAlexander Graf 	panic("EFI application exited");
1802bee91169SAlexander Graf }
1803bee91169SAlexander Graf 
18046b03cd10SHeinrich Schuchardt /**
180578a88f79SMario Six  * efi_unload_image() - unload an EFI image
180678a88f79SMario Six  * @image_handle: handle of the image to be unloaded
1807332468f7SHeinrich Schuchardt  *
1808332468f7SHeinrich Schuchardt  * This function implements the UnloadImage service.
1809332468f7SHeinrich Schuchardt  *
181078a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
181178a88f79SMario Six  * details.
181278a88f79SMario Six  *
181378a88f79SMario Six  * Return: status code
1814332468f7SHeinrich Schuchardt  */
18152074f700SHeinrich Schuchardt static efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle)
1816bee91169SAlexander Graf {
1817bee91169SAlexander Graf 	struct efi_object *efiobj;
1818bee91169SAlexander Graf 
1819bee91169SAlexander Graf 	EFI_ENTRY("%p", image_handle);
1820bee91169SAlexander Graf 	efiobj = efi_search_obj(image_handle);
1821bee91169SAlexander Graf 	if (efiobj)
1822bee91169SAlexander Graf 		list_del(&efiobj->link);
1823bee91169SAlexander Graf 
1824bee91169SAlexander Graf 	return EFI_EXIT(EFI_SUCCESS);
1825bee91169SAlexander Graf }
1826bee91169SAlexander Graf 
18276b03cd10SHeinrich Schuchardt /**
1828f31239acSAlexander Graf  * efi_exit_caches() - fix up caches for EFI payloads if necessary
1829f31239acSAlexander Graf  */
1830f31239acSAlexander Graf static void efi_exit_caches(void)
1831f31239acSAlexander Graf {
1832f31239acSAlexander Graf #if defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
1833f31239acSAlexander Graf 	/*
1834f31239acSAlexander Graf 	 * Grub on 32bit ARM needs to have caches disabled before jumping into
1835f31239acSAlexander Graf 	 * a zImage, but does not know of all cache layers. Give it a hand.
1836f31239acSAlexander Graf 	 */
1837f31239acSAlexander Graf 	if (efi_is_direct_boot)
1838f31239acSAlexander Graf 		cleanup_before_linux();
1839f31239acSAlexander Graf #endif
1840f31239acSAlexander Graf }
1841f31239acSAlexander Graf 
1842f31239acSAlexander Graf /**
184378a88f79SMario Six  * efi_exit_boot_services() - stop all boot services
184478a88f79SMario Six  * @image_handle: handle of the loaded image
184578a88f79SMario Six  * @map_key:      key of the memory map
1846332468f7SHeinrich Schuchardt  *
1847332468f7SHeinrich Schuchardt  * This function implements the ExitBootServices service.
184878a88f79SMario Six  *
1849332468f7SHeinrich Schuchardt  * See the Unified Extensible Firmware Interface (UEFI) specification
1850332468f7SHeinrich Schuchardt  * for details.
1851332468f7SHeinrich Schuchardt  *
185278a88f79SMario Six  * All timer events are disabled. For exit boot services events the
185378a88f79SMario Six  * notification function is called. The boot services are disabled in the
185478a88f79SMario Six  * system table.
1855cc20ed03SHeinrich Schuchardt  *
185678a88f79SMario Six  * Return: status code
1857332468f7SHeinrich Schuchardt  */
18582074f700SHeinrich Schuchardt static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
1859bee91169SAlexander Graf 						  unsigned long map_key)
1860bee91169SAlexander Graf {
186143bce442SHeinrich Schuchardt 	struct efi_event *evt;
1862152a263cSHeinrich Schuchardt 
1863bee91169SAlexander Graf 	EFI_ENTRY("%p, %ld", image_handle, map_key);
1864bee91169SAlexander Graf 
18651fcb7ea2SHeinrich Schuchardt 	/* Check that the caller has read the current memory map */
18661fcb7ea2SHeinrich Schuchardt 	if (map_key != efi_memory_map_key)
18671fcb7ea2SHeinrich Schuchardt 		return EFI_INVALID_PARAMETER;
18681fcb7ea2SHeinrich Schuchardt 
1869cc20ed03SHeinrich Schuchardt 	/* Make sure that notification functions are not called anymore */
1870cc20ed03SHeinrich Schuchardt 	efi_tpl = TPL_HIGH_LEVEL;
1871cc20ed03SHeinrich Schuchardt 
1872cc20ed03SHeinrich Schuchardt 	/* Check if ExitBootServices has already been called */
1873cc20ed03SHeinrich Schuchardt 	if (!systab.boottime)
1874cc20ed03SHeinrich Schuchardt 		return EFI_EXIT(EFI_SUCCESS);
1875cc20ed03SHeinrich Schuchardt 
1876b095f3c8SHeinrich Schuchardt 	/* Add related events to the event group */
1877b095f3c8SHeinrich Schuchardt 	list_for_each_entry(evt, &efi_events, link) {
1878b095f3c8SHeinrich Schuchardt 		if (evt->type == EVT_SIGNAL_EXIT_BOOT_SERVICES)
1879b095f3c8SHeinrich Schuchardt 			evt->group = &efi_guid_event_group_exit_boot_services;
1880b095f3c8SHeinrich Schuchardt 	}
1881152a263cSHeinrich Schuchardt 	/* Notify that ExitBootServices is invoked. */
188243bce442SHeinrich Schuchardt 	list_for_each_entry(evt, &efi_events, link) {
1883b095f3c8SHeinrich Schuchardt 		if (evt->group &&
1884b095f3c8SHeinrich Schuchardt 		    !guidcmp(evt->group,
1885b095f3c8SHeinrich Schuchardt 			     &efi_guid_event_group_exit_boot_services)) {
188643bce442SHeinrich Schuchardt 			efi_signal_event(evt, false);
1887b095f3c8SHeinrich Schuchardt 			break;
1888b095f3c8SHeinrich Schuchardt 		}
1889152a263cSHeinrich Schuchardt 	}
1890152a263cSHeinrich Schuchardt 
1891b72aaa87SHeinrich Schuchardt 	/* TODO: Should persist EFI variables here */
1892ad644e7cSRob Clark 
1893b7b8410aSAlexander Graf 	board_quiesce_devices();
1894b7b8410aSAlexander Graf 
1895f31239acSAlexander Graf 	/* Fix up caches for EFI payloads if necessary */
1896f31239acSAlexander Graf 	efi_exit_caches();
1897f31239acSAlexander Graf 
1898bee91169SAlexander Graf 	/* This stops all lingering devices */
1899bee91169SAlexander Graf 	bootm_disable_interrupts();
1900bee91169SAlexander Graf 
1901cc20ed03SHeinrich Schuchardt 	/* Disable boot time services */
1902cc20ed03SHeinrich Schuchardt 	systab.con_in_handle = NULL;
1903cc20ed03SHeinrich Schuchardt 	systab.con_in = NULL;
1904cc20ed03SHeinrich Schuchardt 	systab.con_out_handle = NULL;
1905cc20ed03SHeinrich Schuchardt 	systab.con_out = NULL;
1906cc20ed03SHeinrich Schuchardt 	systab.stderr_handle = NULL;
1907cc20ed03SHeinrich Schuchardt 	systab.std_err = NULL;
1908cc20ed03SHeinrich Schuchardt 	systab.boottime = NULL;
1909cc20ed03SHeinrich Schuchardt 
1910cc20ed03SHeinrich Schuchardt 	/* Recalculate CRC32 */
1911640adadfSHeinrich Schuchardt 	efi_update_table_header_crc32(&systab.hdr);
1912cc20ed03SHeinrich Schuchardt 
1913bee91169SAlexander Graf 	/* Give the payload some time to boot */
1914b3d60900SHeinrich Schuchardt 	efi_set_watchdog(0);
1915bee91169SAlexander Graf 	WATCHDOG_RESET();
1916bee91169SAlexander Graf 
1917bee91169SAlexander Graf 	return EFI_EXIT(EFI_SUCCESS);
1918bee91169SAlexander Graf }
1919bee91169SAlexander Graf 
19206b03cd10SHeinrich Schuchardt /**
192178a88f79SMario Six  * efi_get_next_monotonic_count() - get next value of the counter
192278a88f79SMario Six  * @count: returned value of the counter
1923332468f7SHeinrich Schuchardt  *
1924332468f7SHeinrich Schuchardt  * This function implements the NextMonotonicCount service.
1925332468f7SHeinrich Schuchardt  *
192678a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
192778a88f79SMario Six  * details.
192878a88f79SMario Six  *
192978a88f79SMario Six  * Return: status code
1930332468f7SHeinrich Schuchardt  */
1931bee91169SAlexander Graf static efi_status_t EFIAPI efi_get_next_monotonic_count(uint64_t *count)
1932bee91169SAlexander Graf {
1933ab9efa97SHeinrich Schuchardt 	static uint64_t mono;
1934ab9efa97SHeinrich Schuchardt 
1935bee91169SAlexander Graf 	EFI_ENTRY("%p", count);
1936bee91169SAlexander Graf 	*count = mono++;
1937bee91169SAlexander Graf 	return EFI_EXIT(EFI_SUCCESS);
1938bee91169SAlexander Graf }
1939bee91169SAlexander Graf 
19406b03cd10SHeinrich Schuchardt /**
194178a88f79SMario Six  * efi_stall() - sleep
19426b03cd10SHeinrich Schuchardt  * @microseconds: period to sleep in microseconds
194378a88f79SMario Six  *
194478a88f79SMario Six  * This function implements the Stall service.
194578a88f79SMario Six  *
194678a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
194778a88f79SMario Six  * details.
194878a88f79SMario Six  *
194978a88f79SMario Six  * Return:  status code
1950332468f7SHeinrich Schuchardt  */
1951bee91169SAlexander Graf static efi_status_t EFIAPI efi_stall(unsigned long microseconds)
1952bee91169SAlexander Graf {
1953bee91169SAlexander Graf 	EFI_ENTRY("%ld", microseconds);
1954bee91169SAlexander Graf 	udelay(microseconds);
1955bee91169SAlexander Graf 	return EFI_EXIT(EFI_SUCCESS);
1956bee91169SAlexander Graf }
1957bee91169SAlexander Graf 
19586b03cd10SHeinrich Schuchardt /**
195978a88f79SMario Six  * efi_set_watchdog_timer() - reset the watchdog timer
19606b03cd10SHeinrich Schuchardt  * @timeout:       seconds before reset by watchdog
19616b03cd10SHeinrich Schuchardt  * @watchdog_code: code to be logged when resetting
19626b03cd10SHeinrich Schuchardt  * @data_size:     size of buffer in bytes
19636b03cd10SHeinrich Schuchardt  * @watchdog_data: buffer with data describing the reset reason
196478a88f79SMario Six  *
196578a88f79SMario Six  * This function implements the SetWatchdogTimer service.
196678a88f79SMario Six  *
196778a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
196878a88f79SMario Six  * details.
196978a88f79SMario Six  *
197078a88f79SMario Six  * Return: status code
1971332468f7SHeinrich Schuchardt  */
1972bee91169SAlexander Graf static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout,
1973bee91169SAlexander Graf 						  uint64_t watchdog_code,
1974bee91169SAlexander Graf 						  unsigned long data_size,
1975bee91169SAlexander Graf 						  uint16_t *watchdog_data)
1976bee91169SAlexander Graf {
1977dee37fc9SMasahiro Yamada 	EFI_ENTRY("%ld, 0x%llx, %ld, %p", timeout, watchdog_code,
1978bee91169SAlexander Graf 		  data_size, watchdog_data);
1979b3d60900SHeinrich Schuchardt 	return EFI_EXIT(efi_set_watchdog(timeout));
1980bee91169SAlexander Graf }
1981bee91169SAlexander Graf 
19826b03cd10SHeinrich Schuchardt /**
198378a88f79SMario Six  * efi_close_protocol() - close a protocol
19846b03cd10SHeinrich Schuchardt  * @handle:            handle on which the protocol shall be closed
19856b03cd10SHeinrich Schuchardt  * @protocol:          GUID of the protocol to close
19866b03cd10SHeinrich Schuchardt  * @agent_handle:      handle of the driver
19876b03cd10SHeinrich Schuchardt  * @controller_handle: handle of the controller
198878a88f79SMario Six  *
198978a88f79SMario Six  * This function implements the CloseProtocol service.
199078a88f79SMario Six  *
199178a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
199278a88f79SMario Six  * details.
199378a88f79SMario Six  *
199478a88f79SMario Six  * Return: status code
1995332468f7SHeinrich Schuchardt  */
19962074f700SHeinrich Schuchardt static efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle,
19975a9682d0SHeinrich Schuchardt 					      const efi_guid_t *protocol,
19982074f700SHeinrich Schuchardt 					      efi_handle_t agent_handle,
19992074f700SHeinrich Schuchardt 					      efi_handle_t controller_handle)
2000bee91169SAlexander Graf {
20013b8a489cSHeinrich Schuchardt 	struct efi_handler *handler;
20023b8a489cSHeinrich Schuchardt 	struct efi_open_protocol_info_item *item;
20033b8a489cSHeinrich Schuchardt 	struct efi_open_protocol_info_item *pos;
20043b8a489cSHeinrich Schuchardt 	efi_status_t r;
20053b8a489cSHeinrich Schuchardt 
2006778e6af8SRob Clark 	EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, agent_handle,
2007bee91169SAlexander Graf 		  controller_handle);
20083b8a489cSHeinrich Schuchardt 
20093b8a489cSHeinrich Schuchardt 	if (!agent_handle) {
20103b8a489cSHeinrich Schuchardt 		r = EFI_INVALID_PARAMETER;
20113b8a489cSHeinrich Schuchardt 		goto out;
20123b8a489cSHeinrich Schuchardt 	}
20133b8a489cSHeinrich Schuchardt 	r = efi_search_protocol(handle, protocol, &handler);
20143b8a489cSHeinrich Schuchardt 	if (r != EFI_SUCCESS)
20153b8a489cSHeinrich Schuchardt 		goto out;
20163b8a489cSHeinrich Schuchardt 
20173b8a489cSHeinrich Schuchardt 	r = EFI_NOT_FOUND;
20183b8a489cSHeinrich Schuchardt 	list_for_each_entry_safe(item, pos, &handler->open_infos, link) {
20193b8a489cSHeinrich Schuchardt 		if (item->info.agent_handle == agent_handle &&
20203b8a489cSHeinrich Schuchardt 		    item->info.controller_handle == controller_handle) {
20213b8a489cSHeinrich Schuchardt 			efi_delete_open_info(item);
20223b8a489cSHeinrich Schuchardt 			r = EFI_SUCCESS;
20233b8a489cSHeinrich Schuchardt 			break;
20243b8a489cSHeinrich Schuchardt 		}
20253b8a489cSHeinrich Schuchardt 	}
20263b8a489cSHeinrich Schuchardt out:
20273b8a489cSHeinrich Schuchardt 	return EFI_EXIT(r);
2028bee91169SAlexander Graf }
2029bee91169SAlexander Graf 
20306b03cd10SHeinrich Schuchardt /**
203178a88f79SMario Six  * efi_open_protocol_information() - provide information about then open status
20326b03cd10SHeinrich Schuchardt  *                                   of a protocol on a handle
20336b03cd10SHeinrich Schuchardt  * @handle:       handle for which the information shall be retrieved
20346b03cd10SHeinrich Schuchardt  * @protocol:     GUID of the protocol
20356b03cd10SHeinrich Schuchardt  * @entry_buffer: buffer to receive the open protocol information
20366b03cd10SHeinrich Schuchardt  * @entry_count:  number of entries available in the buffer
203778a88f79SMario Six  *
203878a88f79SMario Six  * This function implements the OpenProtocolInformation service.
203978a88f79SMario Six  *
204078a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
204178a88f79SMario Six  * details.
204278a88f79SMario Six  *
204378a88f79SMario Six  * Return: status code
2044332468f7SHeinrich Schuchardt  */
2045ab9efa97SHeinrich Schuchardt static efi_status_t EFIAPI efi_open_protocol_information(
2046ab9efa97SHeinrich Schuchardt 			efi_handle_t handle, const efi_guid_t *protocol,
2047bee91169SAlexander Graf 			struct efi_open_protocol_info_entry **entry_buffer,
2048f5a2a938SHeinrich Schuchardt 			efi_uintn_t *entry_count)
2049bee91169SAlexander Graf {
2050e3fbbc36SHeinrich Schuchardt 	unsigned long buffer_size;
2051e3fbbc36SHeinrich Schuchardt 	unsigned long count;
2052e3fbbc36SHeinrich Schuchardt 	struct efi_handler *handler;
2053e3fbbc36SHeinrich Schuchardt 	struct efi_open_protocol_info_item *item;
2054e3fbbc36SHeinrich Schuchardt 	efi_status_t r;
2055e3fbbc36SHeinrich Schuchardt 
2056778e6af8SRob Clark 	EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, entry_buffer,
2057bee91169SAlexander Graf 		  entry_count);
2058e3fbbc36SHeinrich Schuchardt 
2059e3fbbc36SHeinrich Schuchardt 	/* Check parameters */
2060e3fbbc36SHeinrich Schuchardt 	if (!entry_buffer) {
2061e3fbbc36SHeinrich Schuchardt 		r = EFI_INVALID_PARAMETER;
2062e3fbbc36SHeinrich Schuchardt 		goto out;
2063e3fbbc36SHeinrich Schuchardt 	}
2064e3fbbc36SHeinrich Schuchardt 	r = efi_search_protocol(handle, protocol, &handler);
2065e3fbbc36SHeinrich Schuchardt 	if (r != EFI_SUCCESS)
2066e3fbbc36SHeinrich Schuchardt 		goto out;
2067e3fbbc36SHeinrich Schuchardt 
2068e3fbbc36SHeinrich Schuchardt 	/* Count entries */
2069e3fbbc36SHeinrich Schuchardt 	count = 0;
2070e3fbbc36SHeinrich Schuchardt 	list_for_each_entry(item, &handler->open_infos, link) {
2071e3fbbc36SHeinrich Schuchardt 		if (item->info.open_count)
2072e3fbbc36SHeinrich Schuchardt 			++count;
2073e3fbbc36SHeinrich Schuchardt 	}
2074e3fbbc36SHeinrich Schuchardt 	*entry_count = count;
2075e3fbbc36SHeinrich Schuchardt 	*entry_buffer = NULL;
2076e3fbbc36SHeinrich Schuchardt 	if (!count) {
2077e3fbbc36SHeinrich Schuchardt 		r = EFI_SUCCESS;
2078e3fbbc36SHeinrich Schuchardt 		goto out;
2079e3fbbc36SHeinrich Schuchardt 	}
2080e3fbbc36SHeinrich Schuchardt 
2081e3fbbc36SHeinrich Schuchardt 	/* Copy entries */
2082e3fbbc36SHeinrich Schuchardt 	buffer_size = count * sizeof(struct efi_open_protocol_info_entry);
2083eee6530eSHeinrich Schuchardt 	r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
2084e3fbbc36SHeinrich Schuchardt 			      (void **)entry_buffer);
2085e3fbbc36SHeinrich Schuchardt 	if (r != EFI_SUCCESS)
2086e3fbbc36SHeinrich Schuchardt 		goto out;
2087e3fbbc36SHeinrich Schuchardt 	list_for_each_entry_reverse(item, &handler->open_infos, link) {
2088e3fbbc36SHeinrich Schuchardt 		if (item->info.open_count)
2089e3fbbc36SHeinrich Schuchardt 			(*entry_buffer)[--count] = item->info;
2090e3fbbc36SHeinrich Schuchardt 	}
2091e3fbbc36SHeinrich Schuchardt out:
2092e3fbbc36SHeinrich Schuchardt 	return EFI_EXIT(r);
2093bee91169SAlexander Graf }
2094bee91169SAlexander Graf 
20956b03cd10SHeinrich Schuchardt /**
209678a88f79SMario Six  * efi_protocols_per_handle() - get protocols installed on a handle
20976b03cd10SHeinrich Schuchardt  * @handle:                handle for which the information is retrieved
20986b03cd10SHeinrich Schuchardt  * @protocol_buffer:       buffer with protocol GUIDs
20996b03cd10SHeinrich Schuchardt  * @protocol_buffer_count: number of entries in the buffer
210078a88f79SMario Six  *
210178a88f79SMario Six  * This function implements the ProtocolsPerHandleService.
210278a88f79SMario Six  *
210378a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
210478a88f79SMario Six  * details.
210578a88f79SMario Six  *
210678a88f79SMario Six  * Return: status code
2107332468f7SHeinrich Schuchardt  */
21082074f700SHeinrich Schuchardt static efi_status_t EFIAPI efi_protocols_per_handle(
21092074f700SHeinrich Schuchardt 			efi_handle_t handle, efi_guid_t ***protocol_buffer,
2110f5a2a938SHeinrich Schuchardt 			efi_uintn_t *protocol_buffer_count)
2111bee91169SAlexander Graf {
2112c0ebfc86Sxypron.glpk@gmx.de 	unsigned long buffer_size;
2113c0ebfc86Sxypron.glpk@gmx.de 	struct efi_object *efiobj;
211469fb6b1aSHeinrich Schuchardt 	struct list_head *protocol_handle;
2115c0ebfc86Sxypron.glpk@gmx.de 	efi_status_t r;
2116c0ebfc86Sxypron.glpk@gmx.de 
2117bee91169SAlexander Graf 	EFI_ENTRY("%p, %p, %p", handle, protocol_buffer,
2118bee91169SAlexander Graf 		  protocol_buffer_count);
2119c0ebfc86Sxypron.glpk@gmx.de 
2120c0ebfc86Sxypron.glpk@gmx.de 	if (!handle || !protocol_buffer || !protocol_buffer_count)
2121c0ebfc86Sxypron.glpk@gmx.de 		return EFI_EXIT(EFI_INVALID_PARAMETER);
2122c0ebfc86Sxypron.glpk@gmx.de 
2123c0ebfc86Sxypron.glpk@gmx.de 	*protocol_buffer = NULL;
2124661c8327SRob Clark 	*protocol_buffer_count = 0;
2125c0ebfc86Sxypron.glpk@gmx.de 
212669fb6b1aSHeinrich Schuchardt 	efiobj = efi_search_obj(handle);
212769fb6b1aSHeinrich Schuchardt 	if (!efiobj)
212869fb6b1aSHeinrich Schuchardt 		return EFI_EXIT(EFI_INVALID_PARAMETER);
2129c0ebfc86Sxypron.glpk@gmx.de 
2130c0ebfc86Sxypron.glpk@gmx.de 	/* Count protocols */
213169fb6b1aSHeinrich Schuchardt 	list_for_each(protocol_handle, &efiobj->protocols) {
2132c0ebfc86Sxypron.glpk@gmx.de 		++*protocol_buffer_count;
2133c0ebfc86Sxypron.glpk@gmx.de 	}
213469fb6b1aSHeinrich Schuchardt 
2135b72aaa87SHeinrich Schuchardt 	/* Copy GUIDs */
2136c0ebfc86Sxypron.glpk@gmx.de 	if (*protocol_buffer_count) {
213769fb6b1aSHeinrich Schuchardt 		size_t j = 0;
213869fb6b1aSHeinrich Schuchardt 
213969fb6b1aSHeinrich Schuchardt 		buffer_size = sizeof(efi_guid_t *) * *protocol_buffer_count;
2140eee6530eSHeinrich Schuchardt 		r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
2141c0ebfc86Sxypron.glpk@gmx.de 				      (void **)protocol_buffer);
2142c0ebfc86Sxypron.glpk@gmx.de 		if (r != EFI_SUCCESS)
2143c0ebfc86Sxypron.glpk@gmx.de 			return EFI_EXIT(r);
214469fb6b1aSHeinrich Schuchardt 		list_for_each(protocol_handle, &efiobj->protocols) {
214569fb6b1aSHeinrich Schuchardt 			struct efi_handler *protocol;
214669fb6b1aSHeinrich Schuchardt 
214769fb6b1aSHeinrich Schuchardt 			protocol = list_entry(protocol_handle,
214869fb6b1aSHeinrich Schuchardt 					      struct efi_handler, link);
214969fb6b1aSHeinrich Schuchardt 			(*protocol_buffer)[j] = (void *)protocol->guid;
2150c0ebfc86Sxypron.glpk@gmx.de 			++j;
2151c0ebfc86Sxypron.glpk@gmx.de 		}
2152c0ebfc86Sxypron.glpk@gmx.de 	}
2153c0ebfc86Sxypron.glpk@gmx.de 
2154c0ebfc86Sxypron.glpk@gmx.de 	return EFI_EXIT(EFI_SUCCESS);
2155bee91169SAlexander Graf }
2156bee91169SAlexander Graf 
21576b03cd10SHeinrich Schuchardt /**
215878a88f79SMario Six  * efi_locate_handle_buffer() - locate handles implementing a protocol
21596b03cd10SHeinrich Schuchardt  * @search_type: selection criterion
21606b03cd10SHeinrich Schuchardt  * @protocol:    GUID of the protocol
21616b03cd10SHeinrich Schuchardt  * @search_key:  registration key
21626b03cd10SHeinrich Schuchardt  * @no_handles:  number of returned handles
21636b03cd10SHeinrich Schuchardt  * @buffer:      buffer with the returned handles
216478a88f79SMario Six  *
216578a88f79SMario Six  * This function implements the LocateHandleBuffer service.
216678a88f79SMario Six  *
216778a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
216878a88f79SMario Six  * details.
216978a88f79SMario Six  *
217078a88f79SMario Six  * Return: status code
2171332468f7SHeinrich Schuchardt  */
2172bee91169SAlexander Graf static efi_status_t EFIAPI efi_locate_handle_buffer(
2173bee91169SAlexander Graf 			enum efi_locate_search_type search_type,
21745a9682d0SHeinrich Schuchardt 			const efi_guid_t *protocol, void *search_key,
2175f5a2a938SHeinrich Schuchardt 			efi_uintn_t *no_handles, efi_handle_t **buffer)
2176bee91169SAlexander Graf {
2177c2e703f9Sxypron.glpk@gmx.de 	efi_status_t r;
2178f5a2a938SHeinrich Schuchardt 	efi_uintn_t buffer_size = 0;
2179c2e703f9Sxypron.glpk@gmx.de 
2180778e6af8SRob Clark 	EFI_ENTRY("%d, %pUl, %p, %p, %p", search_type, protocol, search_key,
2181bee91169SAlexander Graf 		  no_handles, buffer);
2182c2e703f9Sxypron.glpk@gmx.de 
2183c2e703f9Sxypron.glpk@gmx.de 	if (!no_handles || !buffer) {
2184c2e703f9Sxypron.glpk@gmx.de 		r = EFI_INVALID_PARAMETER;
2185c2e703f9Sxypron.glpk@gmx.de 		goto out;
2186c2e703f9Sxypron.glpk@gmx.de 	}
2187c2e703f9Sxypron.glpk@gmx.de 	*no_handles = 0;
2188c2e703f9Sxypron.glpk@gmx.de 	*buffer = NULL;
2189c2e703f9Sxypron.glpk@gmx.de 	r = efi_locate_handle(search_type, protocol, search_key, &buffer_size,
2190c2e703f9Sxypron.glpk@gmx.de 			      *buffer);
2191c2e703f9Sxypron.glpk@gmx.de 	if (r != EFI_BUFFER_TOO_SMALL)
2192c2e703f9Sxypron.glpk@gmx.de 		goto out;
2193eee6530eSHeinrich Schuchardt 	r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
2194c2e703f9Sxypron.glpk@gmx.de 			      (void **)buffer);
2195c2e703f9Sxypron.glpk@gmx.de 	if (r != EFI_SUCCESS)
2196c2e703f9Sxypron.glpk@gmx.de 		goto out;
2197c2e703f9Sxypron.glpk@gmx.de 	r = efi_locate_handle(search_type, protocol, search_key, &buffer_size,
2198c2e703f9Sxypron.glpk@gmx.de 			      *buffer);
2199c2e703f9Sxypron.glpk@gmx.de 	if (r == EFI_SUCCESS)
22002074f700SHeinrich Schuchardt 		*no_handles = buffer_size / sizeof(efi_handle_t);
2201c2e703f9Sxypron.glpk@gmx.de out:
2202c2e703f9Sxypron.glpk@gmx.de 	return EFI_EXIT(r);
2203bee91169SAlexander Graf }
2204bee91169SAlexander Graf 
22056b03cd10SHeinrich Schuchardt /**
220678a88f79SMario Six  * efi_locate_protocol() - find an interface implementing a protocol
22076b03cd10SHeinrich Schuchardt  * @protocol:           GUID of the protocol
22086b03cd10SHeinrich Schuchardt  * @registration:       registration key passed to the notification function
22096b03cd10SHeinrich Schuchardt  * @protocol_interface: interface implementing the protocol
221078a88f79SMario Six  *
221178a88f79SMario Six  * This function implements the LocateProtocol service.
221278a88f79SMario Six  *
221378a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
221478a88f79SMario Six  * details.
221578a88f79SMario Six  *
221678a88f79SMario Six  * Return: status code
2217332468f7SHeinrich Schuchardt  */
22185a9682d0SHeinrich Schuchardt static efi_status_t EFIAPI efi_locate_protocol(const efi_guid_t *protocol,
2219bee91169SAlexander Graf 					       void *registration,
2220bee91169SAlexander Graf 					       void **protocol_interface)
2221bee91169SAlexander Graf {
222288adae5eSxypron.glpk@gmx.de 	struct list_head *lhandle;
22239172cd91SHeinrich Schuchardt 	efi_status_t ret;
2224bee91169SAlexander Graf 
2225778e6af8SRob Clark 	EFI_ENTRY("%pUl, %p, %p", protocol, registration, protocol_interface);
222688adae5eSxypron.glpk@gmx.de 
222788adae5eSxypron.glpk@gmx.de 	if (!protocol || !protocol_interface)
222888adae5eSxypron.glpk@gmx.de 		return EFI_EXIT(EFI_INVALID_PARAMETER);
222988adae5eSxypron.glpk@gmx.de 
223088adae5eSxypron.glpk@gmx.de 	list_for_each(lhandle, &efi_obj_list) {
223188adae5eSxypron.glpk@gmx.de 		struct efi_object *efiobj;
22329172cd91SHeinrich Schuchardt 		struct efi_handler *handler;
223388adae5eSxypron.glpk@gmx.de 
223488adae5eSxypron.glpk@gmx.de 		efiobj = list_entry(lhandle, struct efi_object, link);
223588adae5eSxypron.glpk@gmx.de 
2236fae0118eSHeinrich Schuchardt 		ret = efi_search_protocol(efiobj, protocol, &handler);
22379172cd91SHeinrich Schuchardt 		if (ret == EFI_SUCCESS) {
22389172cd91SHeinrich Schuchardt 			*protocol_interface = handler->protocol_interface;
2239bee91169SAlexander Graf 			return EFI_EXIT(EFI_SUCCESS);
2240bee91169SAlexander Graf 		}
2241bee91169SAlexander Graf 	}
224288adae5eSxypron.glpk@gmx.de 	*protocol_interface = NULL;
2243bee91169SAlexander Graf 
2244bee91169SAlexander Graf 	return EFI_EXIT(EFI_NOT_FOUND);
2245bee91169SAlexander Graf }
2246bee91169SAlexander Graf 
22476b03cd10SHeinrich Schuchardt /**
224878a88f79SMario Six  * efi_locate_device_path() - Get the device path and handle of an device
22496b03cd10SHeinrich Schuchardt  *                            implementing a protocol
22506b03cd10SHeinrich Schuchardt  * @protocol:    GUID of the protocol
22516b03cd10SHeinrich Schuchardt  * @device_path: device path
22526b03cd10SHeinrich Schuchardt  * @device:      handle of the device
225378a88f79SMario Six  *
225478a88f79SMario Six  * This function implements the LocateDevicePath service.
225578a88f79SMario Six  *
225678a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
225778a88f79SMario Six  * details.
225878a88f79SMario Six  *
225978a88f79SMario Six  * Return: status code
2260ae2c85c1SHeinrich Schuchardt  */
2261ae2c85c1SHeinrich Schuchardt static efi_status_t EFIAPI efi_locate_device_path(
2262ae2c85c1SHeinrich Schuchardt 			const efi_guid_t *protocol,
2263ae2c85c1SHeinrich Schuchardt 			struct efi_device_path **device_path,
2264ae2c85c1SHeinrich Schuchardt 			efi_handle_t *device)
2265ae2c85c1SHeinrich Schuchardt {
2266ae2c85c1SHeinrich Schuchardt 	struct efi_device_path *dp;
2267ae2c85c1SHeinrich Schuchardt 	size_t i;
2268ae2c85c1SHeinrich Schuchardt 	struct efi_handler *handler;
2269ae2c85c1SHeinrich Schuchardt 	efi_handle_t *handles;
2270ae2c85c1SHeinrich Schuchardt 	size_t len, len_dp;
2271ae2c85c1SHeinrich Schuchardt 	size_t len_best = 0;
2272ae2c85c1SHeinrich Schuchardt 	efi_uintn_t no_handles;
2273ae2c85c1SHeinrich Schuchardt 	u8 *remainder;
2274ae2c85c1SHeinrich Schuchardt 	efi_status_t ret;
2275ae2c85c1SHeinrich Schuchardt 
2276ae2c85c1SHeinrich Schuchardt 	EFI_ENTRY("%pUl, %p, %p", protocol, device_path, device);
2277ae2c85c1SHeinrich Schuchardt 
2278ae2c85c1SHeinrich Schuchardt 	if (!protocol || !device_path || !*device_path || !device) {
2279ae2c85c1SHeinrich Schuchardt 		ret = EFI_INVALID_PARAMETER;
2280ae2c85c1SHeinrich Schuchardt 		goto out;
2281ae2c85c1SHeinrich Schuchardt 	}
2282ae2c85c1SHeinrich Schuchardt 
2283ae2c85c1SHeinrich Schuchardt 	/* Find end of device path */
2284f6dd3f35SHeinrich Schuchardt 	len = efi_dp_instance_size(*device_path);
2285ae2c85c1SHeinrich Schuchardt 
2286ae2c85c1SHeinrich Schuchardt 	/* Get all handles implementing the protocol */
2287ae2c85c1SHeinrich Schuchardt 	ret = EFI_CALL(efi_locate_handle_buffer(BY_PROTOCOL, protocol, NULL,
2288ae2c85c1SHeinrich Schuchardt 						&no_handles, &handles));
2289ae2c85c1SHeinrich Schuchardt 	if (ret != EFI_SUCCESS)
2290ae2c85c1SHeinrich Schuchardt 		goto out;
2291ae2c85c1SHeinrich Schuchardt 
2292ae2c85c1SHeinrich Schuchardt 	for (i = 0; i < no_handles; ++i) {
2293ae2c85c1SHeinrich Schuchardt 		/* Find the device path protocol */
2294ae2c85c1SHeinrich Schuchardt 		ret = efi_search_protocol(handles[i], &efi_guid_device_path,
2295ae2c85c1SHeinrich Schuchardt 					  &handler);
2296ae2c85c1SHeinrich Schuchardt 		if (ret != EFI_SUCCESS)
2297ae2c85c1SHeinrich Schuchardt 			continue;
2298ae2c85c1SHeinrich Schuchardt 		dp = (struct efi_device_path *)handler->protocol_interface;
2299f6dd3f35SHeinrich Schuchardt 		len_dp = efi_dp_instance_size(dp);
2300ae2c85c1SHeinrich Schuchardt 		/*
2301ae2c85c1SHeinrich Schuchardt 		 * This handle can only be a better fit
2302ae2c85c1SHeinrich Schuchardt 		 * if its device path length is longer than the best fit and
2303ae2c85c1SHeinrich Schuchardt 		 * if its device path length is shorter of equal the searched
2304ae2c85c1SHeinrich Schuchardt 		 * device path.
2305ae2c85c1SHeinrich Schuchardt 		 */
2306ae2c85c1SHeinrich Schuchardt 		if (len_dp <= len_best || len_dp > len)
2307ae2c85c1SHeinrich Schuchardt 			continue;
2308ae2c85c1SHeinrich Schuchardt 		/* Check if dp is a subpath of device_path */
2309ae2c85c1SHeinrich Schuchardt 		if (memcmp(*device_path, dp, len_dp))
2310ae2c85c1SHeinrich Schuchardt 			continue;
2311ae2c85c1SHeinrich Schuchardt 		*device = handles[i];
2312ae2c85c1SHeinrich Schuchardt 		len_best = len_dp;
2313ae2c85c1SHeinrich Schuchardt 	}
2314ae2c85c1SHeinrich Schuchardt 	if (len_best) {
2315ae2c85c1SHeinrich Schuchardt 		remainder = (u8 *)*device_path + len_best;
2316ae2c85c1SHeinrich Schuchardt 		*device_path = (struct efi_device_path *)remainder;
2317ae2c85c1SHeinrich Schuchardt 		ret = EFI_SUCCESS;
2318ae2c85c1SHeinrich Schuchardt 	} else {
2319ae2c85c1SHeinrich Schuchardt 		ret = EFI_NOT_FOUND;
2320ae2c85c1SHeinrich Schuchardt 	}
2321ae2c85c1SHeinrich Schuchardt out:
2322ae2c85c1SHeinrich Schuchardt 	return EFI_EXIT(ret);
2323ae2c85c1SHeinrich Schuchardt }
2324ae2c85c1SHeinrich Schuchardt 
23256b03cd10SHeinrich Schuchardt /**
232678a88f79SMario Six  * efi_install_multiple_protocol_interfaces() - Install multiple protocol
232778a88f79SMario Six  *                                              interfaces
232878a88f79SMario Six  * @handle: handle on which the protocol interfaces shall be installed
232978a88f79SMario Six  * @...:    NULL terminated argument list with pairs of protocol GUIDS and
233078a88f79SMario Six  *          interfaces
2331332468f7SHeinrich Schuchardt  *
2332332468f7SHeinrich Schuchardt  * This function implements the MultipleProtocolInterfaces service.
2333332468f7SHeinrich Schuchardt  *
233478a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
233578a88f79SMario Six  * details.
233678a88f79SMario Six  *
233778a88f79SMario Six  * Return: status code
2338332468f7SHeinrich Schuchardt  */
2339faea1041SHeinrich Schuchardt static efi_status_t EFIAPI efi_install_multiple_protocol_interfaces
2340faea1041SHeinrich Schuchardt 				(efi_handle_t *handle, ...)
2341bee91169SAlexander Graf {
2342bee91169SAlexander Graf 	EFI_ENTRY("%p", handle);
234358b83586Sxypron.glpk@gmx.de 
2344beb077a2SAlexander Graf 	efi_va_list argptr;
23455a9682d0SHeinrich Schuchardt 	const efi_guid_t *protocol;
234658b83586Sxypron.glpk@gmx.de 	void *protocol_interface;
234758b83586Sxypron.glpk@gmx.de 	efi_status_t r = EFI_SUCCESS;
234858b83586Sxypron.glpk@gmx.de 	int i = 0;
234958b83586Sxypron.glpk@gmx.de 
235058b83586Sxypron.glpk@gmx.de 	if (!handle)
235158b83586Sxypron.glpk@gmx.de 		return EFI_EXIT(EFI_INVALID_PARAMETER);
235258b83586Sxypron.glpk@gmx.de 
2353beb077a2SAlexander Graf 	efi_va_start(argptr, handle);
235458b83586Sxypron.glpk@gmx.de 	for (;;) {
2355beb077a2SAlexander Graf 		protocol = efi_va_arg(argptr, efi_guid_t*);
235658b83586Sxypron.glpk@gmx.de 		if (!protocol)
235758b83586Sxypron.glpk@gmx.de 			break;
2358beb077a2SAlexander Graf 		protocol_interface = efi_va_arg(argptr, void*);
23591760ef57SHeinrich Schuchardt 		r = EFI_CALL(efi_install_protocol_interface(
23601760ef57SHeinrich Schuchardt 						handle, protocol,
236158b83586Sxypron.glpk@gmx.de 						EFI_NATIVE_INTERFACE,
23621760ef57SHeinrich Schuchardt 						protocol_interface));
236358b83586Sxypron.glpk@gmx.de 		if (r != EFI_SUCCESS)
236458b83586Sxypron.glpk@gmx.de 			break;
236558b83586Sxypron.glpk@gmx.de 		i++;
236658b83586Sxypron.glpk@gmx.de 	}
2367beb077a2SAlexander Graf 	efi_va_end(argptr);
236858b83586Sxypron.glpk@gmx.de 	if (r == EFI_SUCCESS)
236958b83586Sxypron.glpk@gmx.de 		return EFI_EXIT(r);
237058b83586Sxypron.glpk@gmx.de 
237162471e46SHeinrich Schuchardt 	/* If an error occurred undo all changes. */
2372beb077a2SAlexander Graf 	efi_va_start(argptr, handle);
237358b83586Sxypron.glpk@gmx.de 	for (; i; --i) {
2374beb077a2SAlexander Graf 		protocol = efi_va_arg(argptr, efi_guid_t*);
2375beb077a2SAlexander Graf 		protocol_interface = efi_va_arg(argptr, void*);
2376faea1041SHeinrich Schuchardt 		EFI_CALL(efi_uninstall_protocol_interface(*handle, protocol,
2377cd534083SHeinrich Schuchardt 							  protocol_interface));
237858b83586Sxypron.glpk@gmx.de 	}
2379beb077a2SAlexander Graf 	efi_va_end(argptr);
238058b83586Sxypron.glpk@gmx.de 
238158b83586Sxypron.glpk@gmx.de 	return EFI_EXIT(r);
2382bee91169SAlexander Graf }
2383bee91169SAlexander Graf 
23846b03cd10SHeinrich Schuchardt /**
238578a88f79SMario Six  * efi_uninstall_multiple_protocol_interfaces() - uninstall multiple protocol
238678a88f79SMario Six  *                                                interfaces
238778a88f79SMario Six  * @handle: handle from which the protocol interfaces shall be removed
238878a88f79SMario Six  * @...:    NULL terminated argument list with pairs of protocol GUIDS and
23896b03cd10SHeinrich Schuchardt  *          interfaces
2390332468f7SHeinrich Schuchardt  *
2391332468f7SHeinrich Schuchardt  * This function implements the UninstallMultipleProtocolInterfaces service.
2392332468f7SHeinrich Schuchardt  *
239378a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
239478a88f79SMario Six  * details.
239578a88f79SMario Six  *
239678a88f79SMario Six  * Return: status code
2397332468f7SHeinrich Schuchardt  */
2398bee91169SAlexander Graf static efi_status_t EFIAPI efi_uninstall_multiple_protocol_interfaces(
2399faea1041SHeinrich Schuchardt 			efi_handle_t handle, ...)
2400bee91169SAlexander Graf {
2401bee91169SAlexander Graf 	EFI_ENTRY("%p", handle);
2402843ce54cSHeinrich Schuchardt 
2403beb077a2SAlexander Graf 	efi_va_list argptr;
2404843ce54cSHeinrich Schuchardt 	const efi_guid_t *protocol;
2405843ce54cSHeinrich Schuchardt 	void *protocol_interface;
2406843ce54cSHeinrich Schuchardt 	efi_status_t r = EFI_SUCCESS;
2407843ce54cSHeinrich Schuchardt 	size_t i = 0;
2408843ce54cSHeinrich Schuchardt 
2409843ce54cSHeinrich Schuchardt 	if (!handle)
2410bee91169SAlexander Graf 		return EFI_EXIT(EFI_INVALID_PARAMETER);
2411843ce54cSHeinrich Schuchardt 
2412beb077a2SAlexander Graf 	efi_va_start(argptr, handle);
2413843ce54cSHeinrich Schuchardt 	for (;;) {
2414beb077a2SAlexander Graf 		protocol = efi_va_arg(argptr, efi_guid_t*);
2415843ce54cSHeinrich Schuchardt 		if (!protocol)
2416843ce54cSHeinrich Schuchardt 			break;
2417beb077a2SAlexander Graf 		protocol_interface = efi_va_arg(argptr, void*);
24189b47f13bSHeinrich Schuchardt 		r = efi_uninstall_protocol(handle, protocol,
24199b47f13bSHeinrich Schuchardt 					   protocol_interface);
2420843ce54cSHeinrich Schuchardt 		if (r != EFI_SUCCESS)
2421843ce54cSHeinrich Schuchardt 			break;
2422843ce54cSHeinrich Schuchardt 		i++;
2423843ce54cSHeinrich Schuchardt 	}
2424beb077a2SAlexander Graf 	efi_va_end(argptr);
24259b47f13bSHeinrich Schuchardt 	if (r == EFI_SUCCESS) {
24269b47f13bSHeinrich Schuchardt 		/* If the last protocol has been removed, delete the handle. */
24279b47f13bSHeinrich Schuchardt 		if (list_empty(&handle->protocols)) {
24289b47f13bSHeinrich Schuchardt 			list_del(&handle->link);
24299b47f13bSHeinrich Schuchardt 			free(handle);
24309b47f13bSHeinrich Schuchardt 		}
2431843ce54cSHeinrich Schuchardt 		return EFI_EXIT(r);
24329b47f13bSHeinrich Schuchardt 	}
2433843ce54cSHeinrich Schuchardt 
2434843ce54cSHeinrich Schuchardt 	/* If an error occurred undo all changes. */
2435beb077a2SAlexander Graf 	efi_va_start(argptr, handle);
2436843ce54cSHeinrich Schuchardt 	for (; i; --i) {
2437beb077a2SAlexander Graf 		protocol = efi_va_arg(argptr, efi_guid_t*);
2438beb077a2SAlexander Graf 		protocol_interface = efi_va_arg(argptr, void*);
2439843ce54cSHeinrich Schuchardt 		EFI_CALL(efi_install_protocol_interface(&handle, protocol,
2440843ce54cSHeinrich Schuchardt 							EFI_NATIVE_INTERFACE,
2441843ce54cSHeinrich Schuchardt 							protocol_interface));
2442843ce54cSHeinrich Schuchardt 	}
2443beb077a2SAlexander Graf 	efi_va_end(argptr);
2444843ce54cSHeinrich Schuchardt 
2445e2373021SHeinrich Schuchardt 	/* In case of an error always return EFI_INVALID_PARAMETER */
2446e2373021SHeinrich Schuchardt 	return EFI_EXIT(EFI_INVALID_PARAMETER);
2447bee91169SAlexander Graf }
2448bee91169SAlexander Graf 
24496b03cd10SHeinrich Schuchardt /**
245078a88f79SMario Six  * efi_calculate_crc32() - calculate cyclic redundancy code
24516b03cd10SHeinrich Schuchardt  * @data:      buffer with data
24526b03cd10SHeinrich Schuchardt  * @data_size: size of buffer in bytes
24536b03cd10SHeinrich Schuchardt  * @crc32_p:   cyclic redundancy code
245478a88f79SMario Six  *
245578a88f79SMario Six  * This function implements the CalculateCrc32 service.
245678a88f79SMario Six  *
245778a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
245878a88f79SMario Six  * details.
245978a88f79SMario Six  *
246078a88f79SMario Six  * Return: status code
2461332468f7SHeinrich Schuchardt  */
24628aa8360eSHeinrich Schuchardt static efi_status_t EFIAPI efi_calculate_crc32(const void *data,
24638aa8360eSHeinrich Schuchardt 					       efi_uintn_t data_size,
24648aa8360eSHeinrich Schuchardt 					       u32 *crc32_p)
2465bee91169SAlexander Graf {
24668aa8360eSHeinrich Schuchardt 	EFI_ENTRY("%p, %zu", data, data_size);
2467bee91169SAlexander Graf 	*crc32_p = crc32(0, data, data_size);
2468bee91169SAlexander Graf 	return EFI_EXIT(EFI_SUCCESS);
2469bee91169SAlexander Graf }
2470bee91169SAlexander Graf 
24716b03cd10SHeinrich Schuchardt /**
247278a88f79SMario Six  * efi_copy_mem() - copy memory
24736b03cd10SHeinrich Schuchardt  * @destination: destination of the copy operation
24746b03cd10SHeinrich Schuchardt  * @source:      source of the copy operation
24756b03cd10SHeinrich Schuchardt  * @length:      number of bytes to copy
247678a88f79SMario Six  *
247778a88f79SMario Six  * This function implements the CopyMem service.
247878a88f79SMario Six  *
247978a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
248078a88f79SMario Six  * details.
2481332468f7SHeinrich Schuchardt  */
2482fc05a959SHeinrich Schuchardt static void EFIAPI efi_copy_mem(void *destination, const void *source,
2483fc05a959SHeinrich Schuchardt 				size_t length)
2484bee91169SAlexander Graf {
2485fc05a959SHeinrich Schuchardt 	EFI_ENTRY("%p, %p, %ld", destination, source, (unsigned long)length);
2486*0bc81a71SHeinrich Schuchardt 	memmove(destination, source, length);
2487f7c78176SHeinrich Schuchardt 	EFI_EXIT(EFI_SUCCESS);
2488bee91169SAlexander Graf }
2489bee91169SAlexander Graf 
24906b03cd10SHeinrich Schuchardt /**
249178a88f79SMario Six  * efi_set_mem() - Fill memory with a byte value.
24926b03cd10SHeinrich Schuchardt  * @buffer: buffer to fill
24936b03cd10SHeinrich Schuchardt  * @size:   size of buffer in bytes
24946b03cd10SHeinrich Schuchardt  * @value:  byte to copy to the buffer
249578a88f79SMario Six  *
249678a88f79SMario Six  * This function implements the SetMem service.
249778a88f79SMario Six  *
249878a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
249978a88f79SMario Six  * details.
2500332468f7SHeinrich Schuchardt  */
2501fc05a959SHeinrich Schuchardt static void EFIAPI efi_set_mem(void *buffer, size_t size, uint8_t value)
2502bee91169SAlexander Graf {
2503fc05a959SHeinrich Schuchardt 	EFI_ENTRY("%p, %ld, 0x%x", buffer, (unsigned long)size, value);
2504bee91169SAlexander Graf 	memset(buffer, value, size);
2505f7c78176SHeinrich Schuchardt 	EFI_EXIT(EFI_SUCCESS);
2506bee91169SAlexander Graf }
2507bee91169SAlexander Graf 
25086b03cd10SHeinrich Schuchardt /**
250978a88f79SMario Six  * efi_protocol_open() - open protocol interface on a handle
25106b03cd10SHeinrich Schuchardt  * @handler:            handler of a protocol
25116b03cd10SHeinrich Schuchardt  * @protocol_interface: interface implementing the protocol
25126b03cd10SHeinrich Schuchardt  * @agent_handle:       handle of the driver
25136b03cd10SHeinrich Schuchardt  * @controller_handle:  handle of the controller
25146b03cd10SHeinrich Schuchardt  * @attributes:         attributes indicating how to open the protocol
251578a88f79SMario Six  *
251678a88f79SMario Six  * Return: status code
2517191a41ccSHeinrich Schuchardt  */
2518191a41ccSHeinrich Schuchardt static efi_status_t efi_protocol_open(
2519191a41ccSHeinrich Schuchardt 			struct efi_handler *handler,
2520191a41ccSHeinrich Schuchardt 			void **protocol_interface, void *agent_handle,
2521191a41ccSHeinrich Schuchardt 			void *controller_handle, uint32_t attributes)
2522191a41ccSHeinrich Schuchardt {
2523191a41ccSHeinrich Schuchardt 	struct efi_open_protocol_info_item *item;
2524191a41ccSHeinrich Schuchardt 	struct efi_open_protocol_info_entry *match = NULL;
2525191a41ccSHeinrich Schuchardt 	bool opened_by_driver = false;
2526191a41ccSHeinrich Schuchardt 	bool opened_exclusive = false;
2527191a41ccSHeinrich Schuchardt 
2528191a41ccSHeinrich Schuchardt 	/* If there is no agent, only return the interface */
2529191a41ccSHeinrich Schuchardt 	if (!agent_handle)
2530191a41ccSHeinrich Schuchardt 		goto out;
2531191a41ccSHeinrich Schuchardt 
2532191a41ccSHeinrich Schuchardt 	/* For TEST_PROTOCOL ignore interface attribute */
2533191a41ccSHeinrich Schuchardt 	if (attributes != EFI_OPEN_PROTOCOL_TEST_PROTOCOL)
2534191a41ccSHeinrich Schuchardt 		*protocol_interface = NULL;
2535191a41ccSHeinrich Schuchardt 
2536191a41ccSHeinrich Schuchardt 	/*
2537191a41ccSHeinrich Schuchardt 	 * Check if the protocol is already opened by a driver with the same
2538191a41ccSHeinrich Schuchardt 	 * attributes or opened exclusively
2539191a41ccSHeinrich Schuchardt 	 */
2540191a41ccSHeinrich Schuchardt 	list_for_each_entry(item, &handler->open_infos, link) {
2541191a41ccSHeinrich Schuchardt 		if (item->info.agent_handle == agent_handle) {
2542191a41ccSHeinrich Schuchardt 			if ((attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) &&
2543191a41ccSHeinrich Schuchardt 			    (item->info.attributes == attributes))
2544191a41ccSHeinrich Schuchardt 				return EFI_ALREADY_STARTED;
2545191a41ccSHeinrich Schuchardt 		}
2546191a41ccSHeinrich Schuchardt 		if (item->info.attributes & EFI_OPEN_PROTOCOL_EXCLUSIVE)
2547191a41ccSHeinrich Schuchardt 			opened_exclusive = true;
2548191a41ccSHeinrich Schuchardt 	}
2549191a41ccSHeinrich Schuchardt 
2550191a41ccSHeinrich Schuchardt 	/* Only one controller can open the protocol exclusively */
2551191a41ccSHeinrich Schuchardt 	if (opened_exclusive && attributes &
2552191a41ccSHeinrich Schuchardt 	    (EFI_OPEN_PROTOCOL_EXCLUSIVE | EFI_OPEN_PROTOCOL_BY_DRIVER))
2553191a41ccSHeinrich Schuchardt 		return EFI_ACCESS_DENIED;
2554191a41ccSHeinrich Schuchardt 
2555191a41ccSHeinrich Schuchardt 	/* Prepare exclusive opening */
2556191a41ccSHeinrich Schuchardt 	if (attributes & EFI_OPEN_PROTOCOL_EXCLUSIVE) {
2557191a41ccSHeinrich Schuchardt 		/* Try to disconnect controllers */
2558191a41ccSHeinrich Schuchardt 		list_for_each_entry(item, &handler->open_infos, link) {
2559191a41ccSHeinrich Schuchardt 			if (item->info.attributes ==
2560191a41ccSHeinrich Schuchardt 					EFI_OPEN_PROTOCOL_BY_DRIVER)
2561191a41ccSHeinrich Schuchardt 				EFI_CALL(efi_disconnect_controller(
2562191a41ccSHeinrich Schuchardt 						item->info.controller_handle,
2563191a41ccSHeinrich Schuchardt 						item->info.agent_handle,
2564191a41ccSHeinrich Schuchardt 						NULL));
2565191a41ccSHeinrich Schuchardt 		}
2566191a41ccSHeinrich Schuchardt 		opened_by_driver = false;
2567191a41ccSHeinrich Schuchardt 		/* Check if all controllers are disconnected */
2568191a41ccSHeinrich Schuchardt 		list_for_each_entry(item, &handler->open_infos, link) {
2569191a41ccSHeinrich Schuchardt 			if (item->info.attributes & EFI_OPEN_PROTOCOL_BY_DRIVER)
2570191a41ccSHeinrich Schuchardt 				opened_by_driver = true;
2571191a41ccSHeinrich Schuchardt 		}
25724f37fa47SHeinrich Schuchardt 		/* Only one controller can be connected */
2573191a41ccSHeinrich Schuchardt 		if (opened_by_driver)
2574191a41ccSHeinrich Schuchardt 			return EFI_ACCESS_DENIED;
2575191a41ccSHeinrich Schuchardt 	}
2576191a41ccSHeinrich Schuchardt 
2577191a41ccSHeinrich Schuchardt 	/* Find existing entry */
2578191a41ccSHeinrich Schuchardt 	list_for_each_entry(item, &handler->open_infos, link) {
2579191a41ccSHeinrich Schuchardt 		if (item->info.agent_handle == agent_handle &&
2580191a41ccSHeinrich Schuchardt 		    item->info.controller_handle == controller_handle)
2581191a41ccSHeinrich Schuchardt 			match = &item->info;
2582191a41ccSHeinrich Schuchardt 	}
2583191a41ccSHeinrich Schuchardt 	/* None found, create one */
2584191a41ccSHeinrich Schuchardt 	if (!match) {
2585191a41ccSHeinrich Schuchardt 		match = efi_create_open_info(handler);
2586191a41ccSHeinrich Schuchardt 		if (!match)
2587191a41ccSHeinrich Schuchardt 			return EFI_OUT_OF_RESOURCES;
2588191a41ccSHeinrich Schuchardt 	}
2589191a41ccSHeinrich Schuchardt 
2590191a41ccSHeinrich Schuchardt 	match->agent_handle = agent_handle;
2591191a41ccSHeinrich Schuchardt 	match->controller_handle = controller_handle;
2592191a41ccSHeinrich Schuchardt 	match->attributes = attributes;
2593191a41ccSHeinrich Schuchardt 	match->open_count++;
2594191a41ccSHeinrich Schuchardt 
2595191a41ccSHeinrich Schuchardt out:
2596191a41ccSHeinrich Schuchardt 	/* For TEST_PROTOCOL ignore interface attribute. */
2597191a41ccSHeinrich Schuchardt 	if (attributes != EFI_OPEN_PROTOCOL_TEST_PROTOCOL)
2598191a41ccSHeinrich Schuchardt 		*protocol_interface = handler->protocol_interface;
2599191a41ccSHeinrich Schuchardt 
2600191a41ccSHeinrich Schuchardt 	return EFI_SUCCESS;
2601191a41ccSHeinrich Schuchardt }
2602191a41ccSHeinrich Schuchardt 
26036b03cd10SHeinrich Schuchardt /**
260478a88f79SMario Six  * efi_open_protocol() - open protocol interface on a handle
26056b03cd10SHeinrich Schuchardt  * @handle:             handle on which the protocol shall be opened
26066b03cd10SHeinrich Schuchardt  * @protocol:           GUID of the protocol
26076b03cd10SHeinrich Schuchardt  * @protocol_interface: interface implementing the protocol
26086b03cd10SHeinrich Schuchardt  * @agent_handle:       handle of the driver
26096b03cd10SHeinrich Schuchardt  * @controller_handle:  handle of the controller
26106b03cd10SHeinrich Schuchardt  * @attributes:         attributes indicating how to open the protocol
261178a88f79SMario Six  *
261278a88f79SMario Six  * This function implements the OpenProtocol interface.
261378a88f79SMario Six  *
261478a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
261578a88f79SMario Six  * details.
261678a88f79SMario Six  *
261778a88f79SMario Six  * Return: status code
2618332468f7SHeinrich Schuchardt  */
2619faea1041SHeinrich Schuchardt static efi_status_t EFIAPI efi_open_protocol
2620faea1041SHeinrich Schuchardt 			(efi_handle_t handle, const efi_guid_t *protocol,
2621faea1041SHeinrich Schuchardt 			 void **protocol_interface, efi_handle_t agent_handle,
2622faea1041SHeinrich Schuchardt 			 efi_handle_t controller_handle, uint32_t attributes)
2623bee91169SAlexander Graf {
262480286e8fSHeinrich Schuchardt 	struct efi_handler *handler;
262569baec67Sxypron.glpk@gmx.de 	efi_status_t r = EFI_INVALID_PARAMETER;
2626bee91169SAlexander Graf 
2627778e6af8SRob Clark 	EFI_ENTRY("%p, %pUl, %p, %p, %p, 0x%x", handle, protocol,
2628bee91169SAlexander Graf 		  protocol_interface, agent_handle, controller_handle,
2629bee91169SAlexander Graf 		  attributes);
2630b5349f74Sxypron.glpk@gmx.de 
263169baec67Sxypron.glpk@gmx.de 	if (!handle || !protocol ||
263269baec67Sxypron.glpk@gmx.de 	    (!protocol_interface && attributes !=
263369baec67Sxypron.glpk@gmx.de 	     EFI_OPEN_PROTOCOL_TEST_PROTOCOL)) {
263469baec67Sxypron.glpk@gmx.de 		goto out;
263569baec67Sxypron.glpk@gmx.de 	}
263669baec67Sxypron.glpk@gmx.de 
263769baec67Sxypron.glpk@gmx.de 	switch (attributes) {
263869baec67Sxypron.glpk@gmx.de 	case EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL:
263969baec67Sxypron.glpk@gmx.de 	case EFI_OPEN_PROTOCOL_GET_PROTOCOL:
264069baec67Sxypron.glpk@gmx.de 	case EFI_OPEN_PROTOCOL_TEST_PROTOCOL:
264169baec67Sxypron.glpk@gmx.de 		break;
264269baec67Sxypron.glpk@gmx.de 	case EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER:
264369baec67Sxypron.glpk@gmx.de 		if (controller_handle == handle)
264469baec67Sxypron.glpk@gmx.de 			goto out;
2645191a41ccSHeinrich Schuchardt 		/* fall-through */
264669baec67Sxypron.glpk@gmx.de 	case EFI_OPEN_PROTOCOL_BY_DRIVER:
264769baec67Sxypron.glpk@gmx.de 	case EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE:
2648191a41ccSHeinrich Schuchardt 		/* Check that the controller handle is valid */
2649191a41ccSHeinrich Schuchardt 		if (!efi_search_obj(controller_handle))
265069baec67Sxypron.glpk@gmx.de 			goto out;
2651191a41ccSHeinrich Schuchardt 		/* fall-through */
265269baec67Sxypron.glpk@gmx.de 	case EFI_OPEN_PROTOCOL_EXCLUSIVE:
2653191a41ccSHeinrich Schuchardt 		/* Check that the agent handle is valid */
2654191a41ccSHeinrich Schuchardt 		if (!efi_search_obj(agent_handle))
265569baec67Sxypron.glpk@gmx.de 			goto out;
265669baec67Sxypron.glpk@gmx.de 		break;
265769baec67Sxypron.glpk@gmx.de 	default:
2658b5349f74Sxypron.glpk@gmx.de 		goto out;
2659b5349f74Sxypron.glpk@gmx.de 	}
2660b5349f74Sxypron.glpk@gmx.de 
266180286e8fSHeinrich Schuchardt 	r = efi_search_protocol(handle, protocol, &handler);
266280286e8fSHeinrich Schuchardt 	if (r != EFI_SUCCESS)
2663bee91169SAlexander Graf 		goto out;
2664bee91169SAlexander Graf 
2665191a41ccSHeinrich Schuchardt 	r = efi_protocol_open(handler, protocol_interface, agent_handle,
2666191a41ccSHeinrich Schuchardt 			      controller_handle, attributes);
2667bee91169SAlexander Graf out:
2668bee91169SAlexander Graf 	return EFI_EXIT(r);
2669bee91169SAlexander Graf }
2670bee91169SAlexander Graf 
26716b03cd10SHeinrich Schuchardt /**
267278a88f79SMario Six  * efi_handle_protocol() - get interface of a protocol on a handle
26736b03cd10SHeinrich Schuchardt  * @handle:             handle on which the protocol shall be opened
26746b03cd10SHeinrich Schuchardt  * @protocol:           GUID of the protocol
26756b03cd10SHeinrich Schuchardt  * @protocol_interface: interface implementing the protocol
267678a88f79SMario Six  *
267778a88f79SMario Six  * This function implements the HandleProtocol service.
267878a88f79SMario Six  *
267978a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
268078a88f79SMario Six  * details.
268178a88f79SMario Six  *
268278a88f79SMario Six  * Return: status code
2683332468f7SHeinrich Schuchardt  */
26842074f700SHeinrich Schuchardt static efi_status_t EFIAPI efi_handle_protocol(efi_handle_t handle,
26855a9682d0SHeinrich Schuchardt 					       const efi_guid_t *protocol,
2686bee91169SAlexander Graf 					       void **protocol_interface)
2687bee91169SAlexander Graf {
26888e1d329fSxypron.glpk@gmx.de 	return efi_open_protocol(handle, protocol, protocol_interface, NULL,
26898e1d329fSxypron.glpk@gmx.de 				 NULL, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL);
2690bee91169SAlexander Graf }
2691bee91169SAlexander Graf 
26926b03cd10SHeinrich Schuchardt /**
269378a88f79SMario Six  * efi_bind_controller() - bind a single driver to a controller
26946b03cd10SHeinrich Schuchardt  * @controller_handle:   controller handle
26956b03cd10SHeinrich Schuchardt  * @driver_image_handle: driver handle
26966b03cd10SHeinrich Schuchardt  * @remain_device_path:  remaining path
269778a88f79SMario Six  *
269878a88f79SMario Six  * Return: status code
26996b03cd10SHeinrich Schuchardt  */
2700f0959dbeSHeinrich Schuchardt static efi_status_t efi_bind_controller(
2701f0959dbeSHeinrich Schuchardt 			efi_handle_t controller_handle,
2702f0959dbeSHeinrich Schuchardt 			efi_handle_t driver_image_handle,
2703f0959dbeSHeinrich Schuchardt 			struct efi_device_path *remain_device_path)
2704f0959dbeSHeinrich Schuchardt {
2705f0959dbeSHeinrich Schuchardt 	struct efi_driver_binding_protocol *binding_protocol;
2706f0959dbeSHeinrich Schuchardt 	efi_status_t r;
2707f0959dbeSHeinrich Schuchardt 
2708f0959dbeSHeinrich Schuchardt 	r = EFI_CALL(efi_open_protocol(driver_image_handle,
2709f0959dbeSHeinrich Schuchardt 				       &efi_guid_driver_binding_protocol,
2710f0959dbeSHeinrich Schuchardt 				       (void **)&binding_protocol,
2711f0959dbeSHeinrich Schuchardt 				       driver_image_handle, NULL,
2712f0959dbeSHeinrich Schuchardt 				       EFI_OPEN_PROTOCOL_GET_PROTOCOL));
2713f0959dbeSHeinrich Schuchardt 	if (r != EFI_SUCCESS)
2714f0959dbeSHeinrich Schuchardt 		return r;
2715f0959dbeSHeinrich Schuchardt 	r = EFI_CALL(binding_protocol->supported(binding_protocol,
2716f0959dbeSHeinrich Schuchardt 						 controller_handle,
2717f0959dbeSHeinrich Schuchardt 						 remain_device_path));
2718f0959dbeSHeinrich Schuchardt 	if (r == EFI_SUCCESS)
2719f0959dbeSHeinrich Schuchardt 		r = EFI_CALL(binding_protocol->start(binding_protocol,
2720f0959dbeSHeinrich Schuchardt 						     controller_handle,
2721f0959dbeSHeinrich Schuchardt 						     remain_device_path));
2722f0959dbeSHeinrich Schuchardt 	EFI_CALL(efi_close_protocol(driver_image_handle,
2723f0959dbeSHeinrich Schuchardt 				    &efi_guid_driver_binding_protocol,
2724f0959dbeSHeinrich Schuchardt 				    driver_image_handle, NULL));
2725f0959dbeSHeinrich Schuchardt 	return r;
2726f0959dbeSHeinrich Schuchardt }
2727f0959dbeSHeinrich Schuchardt 
27286b03cd10SHeinrich Schuchardt /**
272978a88f79SMario Six  * efi_connect_single_controller() - connect a single driver to a controller
27306b03cd10SHeinrich Schuchardt  * @controller_handle:   controller
27316b03cd10SHeinrich Schuchardt  * @driver_image_handle: driver
2732b72aaa87SHeinrich Schuchardt  * @remain_device_path:  remaining path
273378a88f79SMario Six  *
273478a88f79SMario Six  * Return: status code
27356b03cd10SHeinrich Schuchardt  */
2736f0959dbeSHeinrich Schuchardt static efi_status_t efi_connect_single_controller(
2737f0959dbeSHeinrich Schuchardt 			efi_handle_t controller_handle,
2738f0959dbeSHeinrich Schuchardt 			efi_handle_t *driver_image_handle,
2739f0959dbeSHeinrich Schuchardt 			struct efi_device_path *remain_device_path)
2740f0959dbeSHeinrich Schuchardt {
2741f0959dbeSHeinrich Schuchardt 	efi_handle_t *buffer;
2742f0959dbeSHeinrich Schuchardt 	size_t count;
2743f0959dbeSHeinrich Schuchardt 	size_t i;
2744f0959dbeSHeinrich Schuchardt 	efi_status_t r;
2745f0959dbeSHeinrich Schuchardt 	size_t connected = 0;
2746f0959dbeSHeinrich Schuchardt 
2747f0959dbeSHeinrich Schuchardt 	/* Get buffer with all handles with driver binding protocol */
2748f0959dbeSHeinrich Schuchardt 	r = EFI_CALL(efi_locate_handle_buffer(BY_PROTOCOL,
2749f0959dbeSHeinrich Schuchardt 					      &efi_guid_driver_binding_protocol,
2750f0959dbeSHeinrich Schuchardt 					      NULL, &count, &buffer));
2751f0959dbeSHeinrich Schuchardt 	if (r != EFI_SUCCESS)
2752f0959dbeSHeinrich Schuchardt 		return r;
2753f0959dbeSHeinrich Schuchardt 
2754f0959dbeSHeinrich Schuchardt 	/*  Context Override */
2755f0959dbeSHeinrich Schuchardt 	if (driver_image_handle) {
2756f0959dbeSHeinrich Schuchardt 		for (; *driver_image_handle; ++driver_image_handle) {
2757f0959dbeSHeinrich Schuchardt 			for (i = 0; i < count; ++i) {
2758f0959dbeSHeinrich Schuchardt 				if (buffer[i] == *driver_image_handle) {
2759f0959dbeSHeinrich Schuchardt 					buffer[i] = NULL;
2760f0959dbeSHeinrich Schuchardt 					r = efi_bind_controller(
2761f0959dbeSHeinrich Schuchardt 							controller_handle,
2762f0959dbeSHeinrich Schuchardt 							*driver_image_handle,
2763f0959dbeSHeinrich Schuchardt 							remain_device_path);
2764f0959dbeSHeinrich Schuchardt 					/*
2765f0959dbeSHeinrich Schuchardt 					 * For drivers that do not support the
2766f0959dbeSHeinrich Schuchardt 					 * controller or are already connected
2767f0959dbeSHeinrich Schuchardt 					 * we receive an error code here.
2768f0959dbeSHeinrich Schuchardt 					 */
2769f0959dbeSHeinrich Schuchardt 					if (r == EFI_SUCCESS)
2770f0959dbeSHeinrich Schuchardt 						++connected;
2771f0959dbeSHeinrich Schuchardt 				}
2772f0959dbeSHeinrich Schuchardt 			}
2773f0959dbeSHeinrich Schuchardt 		}
2774f0959dbeSHeinrich Schuchardt 	}
2775f0959dbeSHeinrich Schuchardt 
2776f0959dbeSHeinrich Schuchardt 	/*
2777f0959dbeSHeinrich Schuchardt 	 * TODO: Some overrides are not yet implemented:
2778f0959dbeSHeinrich Schuchardt 	 * - Platform Driver Override
2779f0959dbeSHeinrich Schuchardt 	 * - Driver Family Override Search
2780f0959dbeSHeinrich Schuchardt 	 * - Bus Specific Driver Override
2781f0959dbeSHeinrich Schuchardt 	 */
2782f0959dbeSHeinrich Schuchardt 
2783f0959dbeSHeinrich Schuchardt 	/* Driver Binding Search */
2784f0959dbeSHeinrich Schuchardt 	for (i = 0; i < count; ++i) {
2785f0959dbeSHeinrich Schuchardt 		if (buffer[i]) {
2786f0959dbeSHeinrich Schuchardt 			r = efi_bind_controller(controller_handle,
2787f0959dbeSHeinrich Schuchardt 						buffer[i],
2788f0959dbeSHeinrich Schuchardt 						remain_device_path);
2789f0959dbeSHeinrich Schuchardt 			if (r == EFI_SUCCESS)
2790f0959dbeSHeinrich Schuchardt 				++connected;
2791f0959dbeSHeinrich Schuchardt 		}
2792f0959dbeSHeinrich Schuchardt 	}
2793f0959dbeSHeinrich Schuchardt 
2794f0959dbeSHeinrich Schuchardt 	efi_free_pool(buffer);
2795f0959dbeSHeinrich Schuchardt 	if (!connected)
2796f0959dbeSHeinrich Schuchardt 		return EFI_NOT_FOUND;
2797f0959dbeSHeinrich Schuchardt 	return EFI_SUCCESS;
2798f0959dbeSHeinrich Schuchardt }
2799f0959dbeSHeinrich Schuchardt 
28006b03cd10SHeinrich Schuchardt /**
280178a88f79SMario Six  * efi_connect_controller() - connect a controller to a driver
280278a88f79SMario Six  * @controller_handle:   handle of the controller
280378a88f79SMario Six  * @driver_image_handle: handle of the driver
280478a88f79SMario Six  * @remain_device_path:  device path of a child controller
280578a88f79SMario Six  * @recursive:           true to connect all child controllers
2806f0959dbeSHeinrich Schuchardt  *
2807f0959dbeSHeinrich Schuchardt  * This function implements the ConnectController service.
280878a88f79SMario Six  *
280978a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
281078a88f79SMario Six  * details.
2811f0959dbeSHeinrich Schuchardt  *
2812f0959dbeSHeinrich Schuchardt  * First all driver binding protocol handles are tried for binding drivers.
2813b72aaa87SHeinrich Schuchardt  * Afterwards all handles that have opened a protocol of the controller
2814f0959dbeSHeinrich Schuchardt  * with EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER are connected to drivers.
2815f0959dbeSHeinrich Schuchardt  *
281678a88f79SMario Six  * Return: status code
2817f0959dbeSHeinrich Schuchardt  */
2818f0959dbeSHeinrich Schuchardt static efi_status_t EFIAPI efi_connect_controller(
2819f0959dbeSHeinrich Schuchardt 			efi_handle_t controller_handle,
2820f0959dbeSHeinrich Schuchardt 			efi_handle_t *driver_image_handle,
2821f0959dbeSHeinrich Schuchardt 			struct efi_device_path *remain_device_path,
2822f0959dbeSHeinrich Schuchardt 			bool recursive)
2823f0959dbeSHeinrich Schuchardt {
2824f0959dbeSHeinrich Schuchardt 	efi_status_t r;
2825f0959dbeSHeinrich Schuchardt 	efi_status_t ret = EFI_NOT_FOUND;
2826f0959dbeSHeinrich Schuchardt 	struct efi_object *efiobj;
2827f0959dbeSHeinrich Schuchardt 
2828d178836bSHeinrich Schuchardt 	EFI_ENTRY("%p, %p, %pD, %d", controller_handle, driver_image_handle,
2829f0959dbeSHeinrich Schuchardt 		  remain_device_path, recursive);
2830f0959dbeSHeinrich Schuchardt 
2831f0959dbeSHeinrich Schuchardt 	efiobj = efi_search_obj(controller_handle);
2832f0959dbeSHeinrich Schuchardt 	if (!efiobj) {
2833f0959dbeSHeinrich Schuchardt 		ret = EFI_INVALID_PARAMETER;
2834f0959dbeSHeinrich Schuchardt 		goto out;
2835f0959dbeSHeinrich Schuchardt 	}
2836f0959dbeSHeinrich Schuchardt 
2837f0959dbeSHeinrich Schuchardt 	r = efi_connect_single_controller(controller_handle,
2838f0959dbeSHeinrich Schuchardt 					  driver_image_handle,
2839f0959dbeSHeinrich Schuchardt 					  remain_device_path);
2840f0959dbeSHeinrich Schuchardt 	if (r == EFI_SUCCESS)
2841f0959dbeSHeinrich Schuchardt 		ret = EFI_SUCCESS;
2842f0959dbeSHeinrich Schuchardt 	if (recursive) {
2843f0959dbeSHeinrich Schuchardt 		struct efi_handler *handler;
2844f0959dbeSHeinrich Schuchardt 		struct efi_open_protocol_info_item *item;
2845f0959dbeSHeinrich Schuchardt 
2846f0959dbeSHeinrich Schuchardt 		list_for_each_entry(handler, &efiobj->protocols, link) {
2847f0959dbeSHeinrich Schuchardt 			list_for_each_entry(item, &handler->open_infos, link) {
2848f0959dbeSHeinrich Schuchardt 				if (item->info.attributes &
2849f0959dbeSHeinrich Schuchardt 				    EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) {
2850f0959dbeSHeinrich Schuchardt 					r = EFI_CALL(efi_connect_controller(
2851f0959dbeSHeinrich Schuchardt 						item->info.controller_handle,
2852f0959dbeSHeinrich Schuchardt 						driver_image_handle,
2853f0959dbeSHeinrich Schuchardt 						remain_device_path,
2854f0959dbeSHeinrich Schuchardt 						recursive));
2855f0959dbeSHeinrich Schuchardt 					if (r == EFI_SUCCESS)
2856f0959dbeSHeinrich Schuchardt 						ret = EFI_SUCCESS;
2857f0959dbeSHeinrich Schuchardt 				}
2858f0959dbeSHeinrich Schuchardt 			}
2859f0959dbeSHeinrich Schuchardt 		}
2860f0959dbeSHeinrich Schuchardt 	}
2861f0959dbeSHeinrich Schuchardt 	/*  Check for child controller specified by end node */
2862f0959dbeSHeinrich Schuchardt 	if (ret != EFI_SUCCESS && remain_device_path &&
2863f0959dbeSHeinrich Schuchardt 	    remain_device_path->type == DEVICE_PATH_TYPE_END)
2864f0959dbeSHeinrich Schuchardt 		ret = EFI_SUCCESS;
2865f0959dbeSHeinrich Schuchardt out:
2866f0959dbeSHeinrich Schuchardt 	return EFI_EXIT(ret);
2867f0959dbeSHeinrich Schuchardt }
2868f0959dbeSHeinrich Schuchardt 
28696b03cd10SHeinrich Schuchardt /**
287078a88f79SMario Six  * efi_reinstall_protocol_interface() - reinstall protocol interface
287178a88f79SMario Six  * @handle:        handle on which the protocol shall be reinstalled
287278a88f79SMario Six  * @protocol:      GUID of the protocol to be installed
287378a88f79SMario Six  * @old_interface: interface to be removed
287478a88f79SMario Six  * @new_interface: interface to be installed
2875e861a120SHeinrich Schuchardt  *
2876e861a120SHeinrich Schuchardt  * This function implements the ReinstallProtocolInterface service.
287778a88f79SMario Six  *
287878a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
287978a88f79SMario Six  * details.
2880e861a120SHeinrich Schuchardt  *
2881e861a120SHeinrich Schuchardt  * The old interface is uninstalled. The new interface is installed.
2882e861a120SHeinrich Schuchardt  * Drivers are connected.
2883e861a120SHeinrich Schuchardt  *
288478a88f79SMario Six  * Return: status code
2885e861a120SHeinrich Schuchardt  */
2886e861a120SHeinrich Schuchardt static efi_status_t EFIAPI efi_reinstall_protocol_interface(
2887e861a120SHeinrich Schuchardt 			efi_handle_t handle, const efi_guid_t *protocol,
2888e861a120SHeinrich Schuchardt 			void *old_interface, void *new_interface)
2889e861a120SHeinrich Schuchardt {
2890e861a120SHeinrich Schuchardt 	efi_status_t ret;
2891e861a120SHeinrich Schuchardt 
2892e861a120SHeinrich Schuchardt 	EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, old_interface,
2893e861a120SHeinrich Schuchardt 		  new_interface);
28949b47f13bSHeinrich Schuchardt 
28959b47f13bSHeinrich Schuchardt 	/* Uninstall protocol but do not delete handle */
28969b47f13bSHeinrich Schuchardt 	ret = efi_uninstall_protocol(handle, protocol, old_interface);
2897e861a120SHeinrich Schuchardt 	if (ret != EFI_SUCCESS)
2898e861a120SHeinrich Schuchardt 		goto out;
28999b47f13bSHeinrich Schuchardt 
29009b47f13bSHeinrich Schuchardt 	/* Install the new protocol */
29019b47f13bSHeinrich Schuchardt 	ret = efi_add_protocol(handle, protocol, new_interface);
29029b47f13bSHeinrich Schuchardt 	/*
29039b47f13bSHeinrich Schuchardt 	 * The UEFI spec does not specify what should happen to the handle
29049b47f13bSHeinrich Schuchardt 	 * if in case of an error no protocol interface remains on the handle.
29059b47f13bSHeinrich Schuchardt 	 * So let's do nothing here.
29069b47f13bSHeinrich Schuchardt 	 */
2907e861a120SHeinrich Schuchardt 	if (ret != EFI_SUCCESS)
2908e861a120SHeinrich Schuchardt 		goto out;
2909e861a120SHeinrich Schuchardt 	/*
2910e861a120SHeinrich Schuchardt 	 * The returned status code has to be ignored.
2911e861a120SHeinrich Schuchardt 	 * Do not create an error if no suitable driver for the handle exists.
2912e861a120SHeinrich Schuchardt 	 */
2913e861a120SHeinrich Schuchardt 	EFI_CALL(efi_connect_controller(handle, NULL, NULL, true));
2914e861a120SHeinrich Schuchardt out:
2915e861a120SHeinrich Schuchardt 	return EFI_EXIT(ret);
2916e861a120SHeinrich Schuchardt }
2917e861a120SHeinrich Schuchardt 
29186b03cd10SHeinrich Schuchardt /**
291978a88f79SMario Six  * efi_get_child_controllers() - get all child controllers associated to a driver
29206b03cd10SHeinrich Schuchardt  * @efiobj:              handle of the controller
29216b03cd10SHeinrich Schuchardt  * @driver_handle:       handle of the driver
29226b03cd10SHeinrich Schuchardt  * @number_of_children:  number of child controllers
29236b03cd10SHeinrich Schuchardt  * @child_handle_buffer: handles of the the child controllers
292478a88f79SMario Six  *
292578a88f79SMario Six  * The allocated buffer has to be freed with free().
292678a88f79SMario Six  *
292778a88f79SMario Six  * Return: status code
29283f9b0042SHeinrich Schuchardt  */
29293f9b0042SHeinrich Schuchardt static efi_status_t efi_get_child_controllers(
29303f9b0042SHeinrich Schuchardt 				struct efi_object *efiobj,
29313f9b0042SHeinrich Schuchardt 				efi_handle_t driver_handle,
29323f9b0042SHeinrich Schuchardt 				efi_uintn_t *number_of_children,
29333f9b0042SHeinrich Schuchardt 				efi_handle_t **child_handle_buffer)
29343f9b0042SHeinrich Schuchardt {
29353f9b0042SHeinrich Schuchardt 	struct efi_handler *handler;
29363f9b0042SHeinrich Schuchardt 	struct efi_open_protocol_info_item *item;
29373f9b0042SHeinrich Schuchardt 	efi_uintn_t count = 0, i;
29383f9b0042SHeinrich Schuchardt 	bool duplicate;
29393f9b0042SHeinrich Schuchardt 
29403f9b0042SHeinrich Schuchardt 	/* Count all child controller associations */
29413f9b0042SHeinrich Schuchardt 	list_for_each_entry(handler, &efiobj->protocols, link) {
29423f9b0042SHeinrich Schuchardt 		list_for_each_entry(item, &handler->open_infos, link) {
29433f9b0042SHeinrich Schuchardt 			if (item->info.agent_handle == driver_handle &&
29443f9b0042SHeinrich Schuchardt 			    item->info.attributes &
29453f9b0042SHeinrich Schuchardt 			    EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER)
29463f9b0042SHeinrich Schuchardt 				++count;
29473f9b0042SHeinrich Schuchardt 		}
29483f9b0042SHeinrich Schuchardt 	}
29493f9b0042SHeinrich Schuchardt 	/*
29503f9b0042SHeinrich Schuchardt 	 * Create buffer. In case of duplicate child controller assignments
29513f9b0042SHeinrich Schuchardt 	 * the buffer will be too large. But that does not harm.
29523f9b0042SHeinrich Schuchardt 	 */
29533f9b0042SHeinrich Schuchardt 	*number_of_children = 0;
29543f9b0042SHeinrich Schuchardt 	*child_handle_buffer = calloc(count, sizeof(efi_handle_t));
29553f9b0042SHeinrich Schuchardt 	if (!*child_handle_buffer)
29563f9b0042SHeinrich Schuchardt 		return EFI_OUT_OF_RESOURCES;
29573f9b0042SHeinrich Schuchardt 	/* Copy unique child handles */
29583f9b0042SHeinrich Schuchardt 	list_for_each_entry(handler, &efiobj->protocols, link) {
29593f9b0042SHeinrich Schuchardt 		list_for_each_entry(item, &handler->open_infos, link) {
29603f9b0042SHeinrich Schuchardt 			if (item->info.agent_handle == driver_handle &&
29613f9b0042SHeinrich Schuchardt 			    item->info.attributes &
29623f9b0042SHeinrich Schuchardt 			    EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) {
29633f9b0042SHeinrich Schuchardt 				/* Check this is a new child controller */
29643f9b0042SHeinrich Schuchardt 				duplicate = false;
29653f9b0042SHeinrich Schuchardt 				for (i = 0; i < *number_of_children; ++i) {
29663f9b0042SHeinrich Schuchardt 					if ((*child_handle_buffer)[i] ==
29673f9b0042SHeinrich Schuchardt 					    item->info.controller_handle)
29683f9b0042SHeinrich Schuchardt 						duplicate = true;
29693f9b0042SHeinrich Schuchardt 				}
29703f9b0042SHeinrich Schuchardt 				/* Copy handle to buffer */
29713f9b0042SHeinrich Schuchardt 				if (!duplicate) {
29723f9b0042SHeinrich Schuchardt 					i = (*number_of_children)++;
29733f9b0042SHeinrich Schuchardt 					(*child_handle_buffer)[i] =
29743f9b0042SHeinrich Schuchardt 						item->info.controller_handle;
29753f9b0042SHeinrich Schuchardt 				}
29763f9b0042SHeinrich Schuchardt 			}
29773f9b0042SHeinrich Schuchardt 		}
29783f9b0042SHeinrich Schuchardt 	}
29793f9b0042SHeinrich Schuchardt 	return EFI_SUCCESS;
29803f9b0042SHeinrich Schuchardt }
29813f9b0042SHeinrich Schuchardt 
29826b03cd10SHeinrich Schuchardt /**
298378a88f79SMario Six  * efi_disconnect_controller() - disconnect a controller from a driver
29846b03cd10SHeinrich Schuchardt  * @controller_handle:   handle of the controller
29856b03cd10SHeinrich Schuchardt  * @driver_image_handle: handle of the driver
29866b03cd10SHeinrich Schuchardt  * @child_handle:        handle of the child to destroy
298778a88f79SMario Six  *
298878a88f79SMario Six  * This function implements the DisconnectController service.
298978a88f79SMario Six  *
299078a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
299178a88f79SMario Six  * details.
299278a88f79SMario Six  *
299378a88f79SMario Six  * Return: status code
29943f9b0042SHeinrich Schuchardt  */
29953f9b0042SHeinrich Schuchardt static efi_status_t EFIAPI efi_disconnect_controller(
29963f9b0042SHeinrich Schuchardt 				efi_handle_t controller_handle,
29973f9b0042SHeinrich Schuchardt 				efi_handle_t driver_image_handle,
29983f9b0042SHeinrich Schuchardt 				efi_handle_t child_handle)
29993f9b0042SHeinrich Schuchardt {
30003f9b0042SHeinrich Schuchardt 	struct efi_driver_binding_protocol *binding_protocol;
30013f9b0042SHeinrich Schuchardt 	efi_handle_t *child_handle_buffer = NULL;
30023f9b0042SHeinrich Schuchardt 	size_t number_of_children = 0;
30033f9b0042SHeinrich Schuchardt 	efi_status_t r;
30043f9b0042SHeinrich Schuchardt 	size_t stop_count = 0;
30053f9b0042SHeinrich Schuchardt 	struct efi_object *efiobj;
30063f9b0042SHeinrich Schuchardt 
30073f9b0042SHeinrich Schuchardt 	EFI_ENTRY("%p, %p, %p", controller_handle, driver_image_handle,
30083f9b0042SHeinrich Schuchardt 		  child_handle);
30093f9b0042SHeinrich Schuchardt 
30103f9b0042SHeinrich Schuchardt 	efiobj = efi_search_obj(controller_handle);
30113f9b0042SHeinrich Schuchardt 	if (!efiobj) {
30123f9b0042SHeinrich Schuchardt 		r = EFI_INVALID_PARAMETER;
30133f9b0042SHeinrich Schuchardt 		goto out;
30143f9b0042SHeinrich Schuchardt 	}
30153f9b0042SHeinrich Schuchardt 
30163f9b0042SHeinrich Schuchardt 	if (child_handle && !efi_search_obj(child_handle)) {
30173f9b0042SHeinrich Schuchardt 		r = EFI_INVALID_PARAMETER;
30183f9b0042SHeinrich Schuchardt 		goto out;
30193f9b0042SHeinrich Schuchardt 	}
30203f9b0042SHeinrich Schuchardt 
30213f9b0042SHeinrich Schuchardt 	/* If no driver handle is supplied, disconnect all drivers */
30223f9b0042SHeinrich Schuchardt 	if (!driver_image_handle) {
30233f9b0042SHeinrich Schuchardt 		r = efi_disconnect_all_drivers(efiobj, NULL, child_handle);
30243f9b0042SHeinrich Schuchardt 		goto out;
30253f9b0042SHeinrich Schuchardt 	}
30263f9b0042SHeinrich Schuchardt 
30273f9b0042SHeinrich Schuchardt 	/* Create list of child handles */
30283f9b0042SHeinrich Schuchardt 	if (child_handle) {
30293f9b0042SHeinrich Schuchardt 		number_of_children = 1;
30303f9b0042SHeinrich Schuchardt 		child_handle_buffer = &child_handle;
30313f9b0042SHeinrich Schuchardt 	} else {
30323f9b0042SHeinrich Schuchardt 		efi_get_child_controllers(efiobj,
30333f9b0042SHeinrich Schuchardt 					  driver_image_handle,
30343f9b0042SHeinrich Schuchardt 					  &number_of_children,
30353f9b0042SHeinrich Schuchardt 					  &child_handle_buffer);
30363f9b0042SHeinrich Schuchardt 	}
30373f9b0042SHeinrich Schuchardt 
30383f9b0042SHeinrich Schuchardt 	/* Get the driver binding protocol */
30393f9b0042SHeinrich Schuchardt 	r = EFI_CALL(efi_open_protocol(driver_image_handle,
30403f9b0042SHeinrich Schuchardt 				       &efi_guid_driver_binding_protocol,
30413f9b0042SHeinrich Schuchardt 				       (void **)&binding_protocol,
30423f9b0042SHeinrich Schuchardt 				       driver_image_handle, NULL,
30433f9b0042SHeinrich Schuchardt 				       EFI_OPEN_PROTOCOL_GET_PROTOCOL));
30443f9b0042SHeinrich Schuchardt 	if (r != EFI_SUCCESS)
30453f9b0042SHeinrich Schuchardt 		goto out;
30463f9b0042SHeinrich Schuchardt 	/* Remove the children */
30473f9b0042SHeinrich Schuchardt 	if (number_of_children) {
30483f9b0042SHeinrich Schuchardt 		r = EFI_CALL(binding_protocol->stop(binding_protocol,
30493f9b0042SHeinrich Schuchardt 						    controller_handle,
30503f9b0042SHeinrich Schuchardt 						    number_of_children,
30513f9b0042SHeinrich Schuchardt 						    child_handle_buffer));
30523f9b0042SHeinrich Schuchardt 		if (r == EFI_SUCCESS)
30533f9b0042SHeinrich Schuchardt 			++stop_count;
30543f9b0042SHeinrich Schuchardt 	}
30553f9b0042SHeinrich Schuchardt 	/* Remove the driver */
30563f9b0042SHeinrich Schuchardt 	if (!child_handle)
30573f9b0042SHeinrich Schuchardt 		r = EFI_CALL(binding_protocol->stop(binding_protocol,
30583f9b0042SHeinrich Schuchardt 						    controller_handle,
30593f9b0042SHeinrich Schuchardt 						    0, NULL));
30603f9b0042SHeinrich Schuchardt 	if (r == EFI_SUCCESS)
30613f9b0042SHeinrich Schuchardt 		++stop_count;
30623f9b0042SHeinrich Schuchardt 	EFI_CALL(efi_close_protocol(driver_image_handle,
30633f9b0042SHeinrich Schuchardt 				    &efi_guid_driver_binding_protocol,
30643f9b0042SHeinrich Schuchardt 				    driver_image_handle, NULL));
30653f9b0042SHeinrich Schuchardt 
30663f9b0042SHeinrich Schuchardt 	if (stop_count)
30673f9b0042SHeinrich Schuchardt 		r = EFI_SUCCESS;
30683f9b0042SHeinrich Schuchardt 	else
30693f9b0042SHeinrich Schuchardt 		r = EFI_NOT_FOUND;
30703f9b0042SHeinrich Schuchardt out:
30713f9b0042SHeinrich Schuchardt 	if (!child_handle)
30723f9b0042SHeinrich Schuchardt 		free(child_handle_buffer);
30733f9b0042SHeinrich Schuchardt 	return EFI_EXIT(r);
30743f9b0042SHeinrich Schuchardt }
30753f9b0042SHeinrich Schuchardt 
3076640adadfSHeinrich Schuchardt static struct efi_boot_services efi_boot_services = {
3077bee91169SAlexander Graf 	.hdr = {
3078112f2430SHeinrich Schuchardt 		.signature = EFI_BOOT_SERVICES_SIGNATURE,
3079112f2430SHeinrich Schuchardt 		.revision = EFI_SPECIFICATION_VERSION,
308071c846abSHeinrich Schuchardt 		.headersize = sizeof(struct efi_boot_services),
3081bee91169SAlexander Graf 	},
3082bee91169SAlexander Graf 	.raise_tpl = efi_raise_tpl,
3083bee91169SAlexander Graf 	.restore_tpl = efi_restore_tpl,
3084bee91169SAlexander Graf 	.allocate_pages = efi_allocate_pages_ext,
3085bee91169SAlexander Graf 	.free_pages = efi_free_pages_ext,
3086bee91169SAlexander Graf 	.get_memory_map = efi_get_memory_map_ext,
3087ead1274bSStefan Brüns 	.allocate_pool = efi_allocate_pool_ext,
308842417bc8SStefan Brüns 	.free_pool = efi_free_pool_ext,
308949deb455Sxypron.glpk@gmx.de 	.create_event = efi_create_event_ext,
3090bfc72462Sxypron.glpk@gmx.de 	.set_timer = efi_set_timer_ext,
3091bee91169SAlexander Graf 	.wait_for_event = efi_wait_for_event,
3092c6841592Sxypron.glpk@gmx.de 	.signal_event = efi_signal_event_ext,
3093bee91169SAlexander Graf 	.close_event = efi_close_event,
3094bee91169SAlexander Graf 	.check_event = efi_check_event,
30951760ef57SHeinrich Schuchardt 	.install_protocol_interface = efi_install_protocol_interface,
3096bee91169SAlexander Graf 	.reinstall_protocol_interface = efi_reinstall_protocol_interface,
3097cd534083SHeinrich Schuchardt 	.uninstall_protocol_interface = efi_uninstall_protocol_interface,
3098bee91169SAlexander Graf 	.handle_protocol = efi_handle_protocol,
3099bee91169SAlexander Graf 	.reserved = NULL,
3100bee91169SAlexander Graf 	.register_protocol_notify = efi_register_protocol_notify,
310126329584Sxypron.glpk@gmx.de 	.locate_handle = efi_locate_handle_ext,
3102bee91169SAlexander Graf 	.locate_device_path = efi_locate_device_path,
3103488bf12dSAlexander Graf 	.install_configuration_table = efi_install_configuration_table_ext,
3104bee91169SAlexander Graf 	.load_image = efi_load_image,
3105bee91169SAlexander Graf 	.start_image = efi_start_image,
3106a86aeaf2SAlexander Graf 	.exit = efi_exit,
3107bee91169SAlexander Graf 	.unload_image = efi_unload_image,
3108bee91169SAlexander Graf 	.exit_boot_services = efi_exit_boot_services,
3109bee91169SAlexander Graf 	.get_next_monotonic_count = efi_get_next_monotonic_count,
3110bee91169SAlexander Graf 	.stall = efi_stall,
3111bee91169SAlexander Graf 	.set_watchdog_timer = efi_set_watchdog_timer,
3112bee91169SAlexander Graf 	.connect_controller = efi_connect_controller,
3113bee91169SAlexander Graf 	.disconnect_controller = efi_disconnect_controller,
3114bee91169SAlexander Graf 	.open_protocol = efi_open_protocol,
3115bee91169SAlexander Graf 	.close_protocol = efi_close_protocol,
3116bee91169SAlexander Graf 	.open_protocol_information = efi_open_protocol_information,
3117bee91169SAlexander Graf 	.protocols_per_handle = efi_protocols_per_handle,
3118bee91169SAlexander Graf 	.locate_handle_buffer = efi_locate_handle_buffer,
3119bee91169SAlexander Graf 	.locate_protocol = efi_locate_protocol,
3120ab9efa97SHeinrich Schuchardt 	.install_multiple_protocol_interfaces =
3121ab9efa97SHeinrich Schuchardt 			efi_install_multiple_protocol_interfaces,
3122ab9efa97SHeinrich Schuchardt 	.uninstall_multiple_protocol_interfaces =
3123ab9efa97SHeinrich Schuchardt 			efi_uninstall_multiple_protocol_interfaces,
3124bee91169SAlexander Graf 	.calculate_crc32 = efi_calculate_crc32,
3125bee91169SAlexander Graf 	.copy_mem = efi_copy_mem,
3126bee91169SAlexander Graf 	.set_mem = efi_set_mem,
31279f0930e5SHeinrich Schuchardt 	.create_event_ex = efi_create_event_ex,
3128bee91169SAlexander Graf };
3129bee91169SAlexander Graf 
31300b386537SHeinrich Schuchardt static u16 __efi_runtime_data firmware_vendor[] = L"Das U-Boot";
3131bee91169SAlexander Graf 
31323c63db9cSAlexander Graf struct efi_system_table __efi_runtime_data systab = {
3133bee91169SAlexander Graf 	.hdr = {
3134bee91169SAlexander Graf 		.signature = EFI_SYSTEM_TABLE_SIGNATURE,
3135112f2430SHeinrich Schuchardt 		.revision = EFI_SPECIFICATION_VERSION,
313671c846abSHeinrich Schuchardt 		.headersize = sizeof(struct efi_system_table),
3137bee91169SAlexander Graf 	},
31380b386537SHeinrich Schuchardt 	.fw_vendor = firmware_vendor,
31390b386537SHeinrich Schuchardt 	.fw_revision = FW_VERSION << 16 | FW_PATCHLEVEL << 8,
3140bee91169SAlexander Graf 	.con_in = (void *)&efi_con_in,
3141bee91169SAlexander Graf 	.con_out = (void *)&efi_con_out,
3142bee91169SAlexander Graf 	.std_err = (void *)&efi_con_out,
3143bee91169SAlexander Graf 	.runtime = (void *)&efi_runtime_services,
3144bee91169SAlexander Graf 	.boottime = (void *)&efi_boot_services,
3145bee91169SAlexander Graf 	.nr_tables = 0,
31464182a129SHeinrich Schuchardt 	.tables = NULL,
3147bee91169SAlexander Graf };
3148640adadfSHeinrich Schuchardt 
3149640adadfSHeinrich Schuchardt /**
3150640adadfSHeinrich Schuchardt  * efi_initialize_system_table() - Initialize system table
3151640adadfSHeinrich Schuchardt  *
31520414359aSHeinrich Schuchardt  * Return:	status code
3153640adadfSHeinrich Schuchardt  */
3154640adadfSHeinrich Schuchardt efi_status_t efi_initialize_system_table(void)
3155640adadfSHeinrich Schuchardt {
31564182a129SHeinrich Schuchardt 	efi_status_t ret;
31574182a129SHeinrich Schuchardt 
31584182a129SHeinrich Schuchardt 	/* Allocate configuration table array */
31594182a129SHeinrich Schuchardt 	ret = efi_allocate_pool(EFI_RUNTIME_SERVICES_DATA,
31604182a129SHeinrich Schuchardt 				EFI_MAX_CONFIGURATION_TABLES *
31614182a129SHeinrich Schuchardt 				sizeof(struct efi_configuration_table),
31624182a129SHeinrich Schuchardt 				(void **)&systab.tables);
31634182a129SHeinrich Schuchardt 
3164b72aaa87SHeinrich Schuchardt 	/* Set CRC32 field in table headers */
3165640adadfSHeinrich Schuchardt 	efi_update_table_header_crc32(&systab.hdr);
3166640adadfSHeinrich Schuchardt 	efi_update_table_header_crc32(&efi_runtime_services.hdr);
3167640adadfSHeinrich Schuchardt 	efi_update_table_header_crc32(&efi_boot_services.hdr);
31684182a129SHeinrich Schuchardt 
31694182a129SHeinrich Schuchardt 	return ret;
3170640adadfSHeinrich Schuchardt }
3171