xref: /openbmc/u-boot/lib/efi_loader/efi_boottime.c (revision cb728e51a77e515659f4834c2829a956b9f5c9f0)
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 
1561c9bfb222SLeif Lindholm 	ret = efi_add_protocol(&obj->header,
1562c9bfb222SLeif Lindholm 			       &efi_guid_hii_string_protocol,
1563c9bfb222SLeif Lindholm 			       (void *)&efi_hii_string);
1564c9bfb222SLeif Lindholm 	if (ret != EFI_SUCCESS)
1565c9bfb222SLeif Lindholm 		goto failure;
1566c9bfb222SLeif Lindholm 
1567c9bfb222SLeif Lindholm 	ret = efi_add_protocol(&obj->header,
1568c9bfb222SLeif Lindholm 			       &efi_guid_hii_database_protocol,
1569c9bfb222SLeif Lindholm 			       (void *)&efi_hii_database);
1570c9bfb222SLeif Lindholm 	if (ret != EFI_SUCCESS)
1571c9bfb222SLeif Lindholm 		goto failure;
1572c9bfb222SLeif Lindholm 
1573*cb728e51SAKASHI Takahiro 	ret = efi_add_protocol(&obj->header,
1574*cb728e51SAKASHI Takahiro 			       &efi_guid_hii_config_routing_protocol,
1575*cb728e51SAKASHI Takahiro 			       (void *)&efi_hii_config_routing);
1576*cb728e51SAKASHI Takahiro 	if (ret != EFI_SUCCESS)
1577*cb728e51SAKASHI Takahiro 		goto failure;
1578*cb728e51SAKASHI Takahiro 
157956d92888SHeinrich Schuchardt 	return ret;
158048b66230SHeinrich Schuchardt failure:
158148b66230SHeinrich Schuchardt 	printf("ERROR: Failure to install protocols for loaded image\n");
158256d92888SHeinrich Schuchardt 	return ret;
158395c5553eSRob Clark }
158495c5553eSRob Clark 
15856b03cd10SHeinrich Schuchardt /**
158678a88f79SMario Six  * efi_load_image_from_path() - load an image using a file path
15876b03cd10SHeinrich Schuchardt  * @file_path: the path of the image to load
15886b03cd10SHeinrich Schuchardt  * @buffer:    buffer containing the loaded image
158978a88f79SMario Six  *
159078a88f79SMario Six  * Return: status code
1591332468f7SHeinrich Schuchardt  */
15929975fe96SRob Clark efi_status_t efi_load_image_from_path(struct efi_device_path *file_path,
1593838ee4b4SRob Clark 				      void **buffer)
1594838ee4b4SRob Clark {
1595838ee4b4SRob Clark 	struct efi_file_info *info = NULL;
1596838ee4b4SRob Clark 	struct efi_file_handle *f;
1597838ee4b4SRob Clark 	static efi_status_t ret;
1598b6dd5777SHeinrich Schuchardt 	efi_uintn_t bs;
1599838ee4b4SRob Clark 
1600838ee4b4SRob Clark 	f = efi_file_from_path(file_path);
1601838ee4b4SRob Clark 	if (!f)
1602838ee4b4SRob Clark 		return EFI_DEVICE_ERROR;
1603838ee4b4SRob Clark 
1604838ee4b4SRob Clark 	bs = 0;
1605838ee4b4SRob Clark 	EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid,
1606838ee4b4SRob Clark 				  &bs, info));
1607838ee4b4SRob Clark 	if (ret == EFI_BUFFER_TOO_SMALL) {
1608838ee4b4SRob Clark 		info = malloc(bs);
1609838ee4b4SRob Clark 		EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid,
1610838ee4b4SRob Clark 					  &bs, info));
1611838ee4b4SRob Clark 	}
1612838ee4b4SRob Clark 	if (ret != EFI_SUCCESS)
1613838ee4b4SRob Clark 		goto error;
1614838ee4b4SRob Clark 
1615838ee4b4SRob Clark 	ret = efi_allocate_pool(EFI_LOADER_DATA, info->file_size, buffer);
1616838ee4b4SRob Clark 	if (ret)
1617838ee4b4SRob Clark 		goto error;
1618838ee4b4SRob Clark 
1619b6dd5777SHeinrich Schuchardt 	bs = info->file_size;
1620b6dd5777SHeinrich Schuchardt 	EFI_CALL(ret = f->read(f, &bs, *buffer));
1621838ee4b4SRob Clark 
1622838ee4b4SRob Clark error:
1623838ee4b4SRob Clark 	free(info);
1624838ee4b4SRob Clark 	EFI_CALL(f->close(f));
1625838ee4b4SRob Clark 
1626838ee4b4SRob Clark 	if (ret != EFI_SUCCESS) {
1627838ee4b4SRob Clark 		efi_free_pool(*buffer);
1628838ee4b4SRob Clark 		*buffer = NULL;
1629838ee4b4SRob Clark 	}
1630838ee4b4SRob Clark 
1631838ee4b4SRob Clark 	return ret;
1632838ee4b4SRob Clark }
1633838ee4b4SRob Clark 
16346b03cd10SHeinrich Schuchardt /**
163578a88f79SMario Six  * efi_load_image() - load an EFI image into memory
16366b03cd10SHeinrich Schuchardt  * @boot_policy:   true for request originating from the boot manager
16376b03cd10SHeinrich Schuchardt  * @parent_image:  the caller's image handle
16386b03cd10SHeinrich Schuchardt  * @file_path:     the path of the image to load
16396b03cd10SHeinrich Schuchardt  * @source_buffer: memory location from which the image is installed
164078a88f79SMario Six  * @source_size:   size of the memory area from which the image is installed
16416b03cd10SHeinrich Schuchardt  * @image_handle:  handle for the newly installed image
164278a88f79SMario Six  *
164378a88f79SMario Six  * This function implements the LoadImage service.
164478a88f79SMario Six  *
164578a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification
164678a88f79SMario Six  * for details.
164778a88f79SMario Six  *
164878a88f79SMario Six  * Return: status code
1649332468f7SHeinrich Schuchardt  */
1650bee91169SAlexander Graf static efi_status_t EFIAPI efi_load_image(bool boot_policy,
1651bee91169SAlexander Graf 					  efi_handle_t parent_image,
1652bee91169SAlexander Graf 					  struct efi_device_path *file_path,
1653bee91169SAlexander Graf 					  void *source_buffer,
16547fb96a10SHeinrich Schuchardt 					  efi_uintn_t source_size,
1655bee91169SAlexander Graf 					  efi_handle_t *image_handle)
1656bee91169SAlexander Graf {
16571c3b2f4aSTom Rini 	struct efi_loaded_image *info = NULL;
1658c982874eSHeinrich Schuchardt 	struct efi_loaded_image_obj **image_obj =
1659c982874eSHeinrich Schuchardt 		(struct efi_loaded_image_obj **)image_handle;
1660b9b17598SHeinrich Schuchardt 	efi_status_t ret;
1661bee91169SAlexander Graf 
16627fb96a10SHeinrich Schuchardt 	EFI_ENTRY("%d, %p, %pD, %p, %zd, %p", boot_policy, parent_image,
1663bee91169SAlexander Graf 		  file_path, source_buffer, source_size, image_handle);
1664838ee4b4SRob Clark 
166528a4fd46SHeinrich Schuchardt 	if (!image_handle || !parent_image) {
166628a4fd46SHeinrich Schuchardt 		ret = EFI_INVALID_PARAMETER;
166728a4fd46SHeinrich Schuchardt 		goto error;
166828a4fd46SHeinrich Schuchardt 	}
166928a4fd46SHeinrich Schuchardt 
167028a4fd46SHeinrich Schuchardt 	if (!source_buffer && !file_path) {
167128a4fd46SHeinrich Schuchardt 		ret = EFI_NOT_FOUND;
167228a4fd46SHeinrich Schuchardt 		goto error;
167328a4fd46SHeinrich Schuchardt 	}
167428a4fd46SHeinrich Schuchardt 
1675838ee4b4SRob Clark 	if (!source_buffer) {
1676838ee4b4SRob Clark 		struct efi_device_path *dp, *fp;
1677838ee4b4SRob Clark 
16789975fe96SRob Clark 		ret = efi_load_image_from_path(file_path, &source_buffer);
1679b9b17598SHeinrich Schuchardt 		if (ret != EFI_SUCCESS)
1680b9b17598SHeinrich Schuchardt 			goto failure;
1681838ee4b4SRob Clark 		/*
1682838ee4b4SRob Clark 		 * split file_path which contains both the device and
1683838ee4b4SRob Clark 		 * file parts:
1684838ee4b4SRob Clark 		 */
1685838ee4b4SRob Clark 		efi_dp_split_file_path(file_path, &dp, &fp);
1686c982874eSHeinrich Schuchardt 		ret = efi_setup_loaded_image(dp, fp, image_obj, &info);
1687b9b17598SHeinrich Schuchardt 		if (ret != EFI_SUCCESS)
1688b9b17598SHeinrich Schuchardt 			goto failure;
1689838ee4b4SRob Clark 	} else {
1690b72aaa87SHeinrich Schuchardt 		/* In this case, file_path is the "device" path, i.e.
1691838ee4b4SRob Clark 		 * something like a HARDWARE_DEVICE:MEMORY_MAPPED
1692838ee4b4SRob Clark 		 */
1693c982874eSHeinrich Schuchardt 		ret = efi_setup_loaded_image(file_path, NULL, image_obj, &info);
1694b9b17598SHeinrich Schuchardt 		if (ret != EFI_SUCCESS)
1695c982874eSHeinrich Schuchardt 			goto error;
1696838ee4b4SRob Clark 	}
1697c982874eSHeinrich Schuchardt 	(*image_obj)->entry = efi_load_pe(*image_obj, source_buffer, info);
1698c982874eSHeinrich Schuchardt 	if (!(*image_obj)->entry) {
1699b9b17598SHeinrich Schuchardt 		ret = EFI_UNSUPPORTED;
1700b9b17598SHeinrich Schuchardt 		goto failure;
1701bee91169SAlexander Graf 	}
170232fc2ac3SHeinrich Schuchardt 	info->system_table = &systab;
170332fc2ac3SHeinrich Schuchardt 	info->parent_handle = parent_image;
1704bee91169SAlexander Graf 	return EFI_EXIT(EFI_SUCCESS);
1705b9b17598SHeinrich Schuchardt failure:
1706c982874eSHeinrich Schuchardt 	efi_delete_handle(*image_handle);
1707c982874eSHeinrich Schuchardt 	*image_handle = NULL;
1708b9b17598SHeinrich Schuchardt 	free(info);
170928a4fd46SHeinrich Schuchardt error:
1710b9b17598SHeinrich Schuchardt 	return EFI_EXIT(ret);
1711bee91169SAlexander Graf }
1712bee91169SAlexander Graf 
17136b03cd10SHeinrich Schuchardt /**
1714b72aaa87SHeinrich Schuchardt  * efi_start_image() - call the entry point of an image
17156b03cd10SHeinrich Schuchardt  * @image_handle:   handle of the image
17166b03cd10SHeinrich Schuchardt  * @exit_data_size: size of the buffer
17176b03cd10SHeinrich Schuchardt  * @exit_data:      buffer to receive the exit data of the called image
171878a88f79SMario Six  *
171978a88f79SMario Six  * This function implements the StartImage service.
172078a88f79SMario Six  *
172178a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
172278a88f79SMario Six  * details.
172378a88f79SMario Six  *
172478a88f79SMario Six  * Return: status code
1725332468f7SHeinrich Schuchardt  */
1726bee91169SAlexander Graf static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
1727cc8e3417SHeinrich Schuchardt 					   efi_uintn_t *exit_data_size,
1728cc8e3417SHeinrich Schuchardt 					   u16 **exit_data)
1729bee91169SAlexander Graf {
1730c982874eSHeinrich Schuchardt 	struct efi_loaded_image_obj *image_obj =
1731c982874eSHeinrich Schuchardt 		(struct efi_loaded_image_obj *)image_handle;
1732727a1afbSHeinrich Schuchardt 	efi_status_t ret;
1733bee91169SAlexander Graf 
1734bee91169SAlexander Graf 	EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data);
1735bee91169SAlexander Graf 
1736f31239acSAlexander Graf 	efi_is_direct_boot = false;
1737f31239acSAlexander Graf 
1738bee91169SAlexander Graf 	/* call the image! */
1739c982874eSHeinrich Schuchardt 	if (setjmp(&image_obj->exit_jmp)) {
1740727a1afbSHeinrich Schuchardt 		/*
1741727a1afbSHeinrich Schuchardt 		 * We called the entry point of the child image with EFI_CALL
1742727a1afbSHeinrich Schuchardt 		 * in the lines below. The child image called the Exit() boot
1743727a1afbSHeinrich Schuchardt 		 * service efi_exit() which executed the long jump that brought
1744727a1afbSHeinrich Schuchardt 		 * us to the current line. This implies that the second half
1745727a1afbSHeinrich Schuchardt 		 * of the EFI_CALL macro has not been executed.
1746727a1afbSHeinrich Schuchardt 		 */
1747727a1afbSHeinrich Schuchardt #ifdef CONFIG_ARM
1748727a1afbSHeinrich Schuchardt 		/*
1749727a1afbSHeinrich Schuchardt 		 * efi_exit() called efi_restore_gd(). We have to undo this
1750727a1afbSHeinrich Schuchardt 		 * otherwise __efi_entry_check() will put the wrong value into
1751727a1afbSHeinrich Schuchardt 		 * app_gd.
1752727a1afbSHeinrich Schuchardt 		 */
1753727a1afbSHeinrich Schuchardt 		gd = app_gd;
1754727a1afbSHeinrich Schuchardt #endif
1755727a1afbSHeinrich Schuchardt 		/*
1756727a1afbSHeinrich Schuchardt 		 * To get ready to call EFI_EXIT below we have to execute the
1757727a1afbSHeinrich Schuchardt 		 * missed out steps of EFI_CALL.
1758727a1afbSHeinrich Schuchardt 		 */
1759727a1afbSHeinrich Schuchardt 		assert(__efi_entry_check());
1760727a1afbSHeinrich Schuchardt 		debug("%sEFI: %lu returned by started image\n",
1761727a1afbSHeinrich Schuchardt 		      __efi_nesting_dec(),
1762c982874eSHeinrich Schuchardt 		      (unsigned long)((uintptr_t)image_obj->exit_status &
1763727a1afbSHeinrich Schuchardt 				      ~EFI_ERROR_MASK));
1764c982874eSHeinrich Schuchardt 		return EFI_EXIT(image_obj->exit_status);
1765a86aeaf2SAlexander Graf 	}
1766a86aeaf2SAlexander Graf 
1767c982874eSHeinrich Schuchardt 	ret = EFI_CALL(image_obj->entry(image_handle, &systab));
1768bee91169SAlexander Graf 
176956672bf5SAlexander Graf 	/*
177056672bf5SAlexander Graf 	 * Usually UEFI applications call Exit() instead of returning.
1771b72aaa87SHeinrich Schuchardt 	 * But because the world doesn't consist of ponies and unicorns,
177256672bf5SAlexander Graf 	 * we're happy to emulate that behavior on behalf of a payload
177356672bf5SAlexander Graf 	 * that forgot.
177456672bf5SAlexander Graf 	 */
177556672bf5SAlexander Graf 	return EFI_CALL(systab.boottime->exit(image_handle, ret, 0, NULL));
1776bee91169SAlexander Graf }
1777bee91169SAlexander Graf 
17786b03cd10SHeinrich Schuchardt /**
177978a88f79SMario Six  * efi_exit() - leave an EFI application or driver
17806b03cd10SHeinrich Schuchardt  * @image_handle:   handle of the application or driver that is exiting
17816b03cd10SHeinrich Schuchardt  * @exit_status:    status code
17826b03cd10SHeinrich Schuchardt  * @exit_data_size: size of the buffer in bytes
17836b03cd10SHeinrich Schuchardt  * @exit_data:      buffer with data describing an error
178478a88f79SMario Six  *
178578a88f79SMario Six  * This function implements the Exit service.
178678a88f79SMario Six  *
178778a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
178878a88f79SMario Six  * details.
178978a88f79SMario Six  *
179078a88f79SMario Six  * Return: status code
1791332468f7SHeinrich Schuchardt  */
1792a86aeaf2SAlexander Graf static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
1793ab9efa97SHeinrich Schuchardt 				    efi_status_t exit_status,
1794cc8e3417SHeinrich Schuchardt 				    efi_uintn_t exit_data_size,
1795cc8e3417SHeinrich Schuchardt 				    u16 *exit_data)
1796bee91169SAlexander Graf {
179744549d62SHeinrich Schuchardt 	/*
179844549d62SHeinrich Schuchardt 	 * TODO: We should call the unload procedure of the loaded
179944549d62SHeinrich Schuchardt 	 *	 image protocol.
180044549d62SHeinrich Schuchardt 	 */
1801c982874eSHeinrich Schuchardt 	struct efi_loaded_image_obj *image_obj =
1802c982874eSHeinrich Schuchardt 		(struct efi_loaded_image_obj *)image_handle;
1803a86aeaf2SAlexander Graf 
1804cc8e3417SHeinrich Schuchardt 	EFI_ENTRY("%p, %ld, %zu, %p", image_handle, exit_status,
1805bee91169SAlexander Graf 		  exit_data_size, exit_data);
1806a86aeaf2SAlexander Graf 
1807a148920eSAlexander Graf 	/* Make sure entry/exit counts for EFI world cross-overs match */
1808727a1afbSHeinrich Schuchardt 	EFI_EXIT(exit_status);
1809da94073bSHeinrich Schuchardt 
1810a148920eSAlexander Graf 	/*
1811a148920eSAlexander Graf 	 * But longjmp out with the U-Boot gd, not the application's, as
1812a148920eSAlexander Graf 	 * the other end is a setjmp call inside EFI context.
1813a148920eSAlexander Graf 	 */
1814a148920eSAlexander Graf 	efi_restore_gd();
1815a148920eSAlexander Graf 
1816c982874eSHeinrich Schuchardt 	image_obj->exit_status = exit_status;
1817c982874eSHeinrich Schuchardt 	longjmp(&image_obj->exit_jmp, 1);
1818a86aeaf2SAlexander Graf 
1819a86aeaf2SAlexander Graf 	panic("EFI application exited");
1820bee91169SAlexander Graf }
1821bee91169SAlexander Graf 
18226b03cd10SHeinrich Schuchardt /**
182378a88f79SMario Six  * efi_unload_image() - unload an EFI image
182478a88f79SMario Six  * @image_handle: handle of the image to be unloaded
1825332468f7SHeinrich Schuchardt  *
1826332468f7SHeinrich Schuchardt  * This function implements the UnloadImage service.
1827332468f7SHeinrich Schuchardt  *
182878a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
182978a88f79SMario Six  * details.
183078a88f79SMario Six  *
183178a88f79SMario Six  * Return: status code
1832332468f7SHeinrich Schuchardt  */
18332074f700SHeinrich Schuchardt static efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle)
1834bee91169SAlexander Graf {
1835bee91169SAlexander Graf 	struct efi_object *efiobj;
1836bee91169SAlexander Graf 
1837bee91169SAlexander Graf 	EFI_ENTRY("%p", image_handle);
1838bee91169SAlexander Graf 	efiobj = efi_search_obj(image_handle);
1839bee91169SAlexander Graf 	if (efiobj)
1840bee91169SAlexander Graf 		list_del(&efiobj->link);
1841bee91169SAlexander Graf 
1842bee91169SAlexander Graf 	return EFI_EXIT(EFI_SUCCESS);
1843bee91169SAlexander Graf }
1844bee91169SAlexander Graf 
18456b03cd10SHeinrich Schuchardt /**
1846f31239acSAlexander Graf  * efi_exit_caches() - fix up caches for EFI payloads if necessary
1847f31239acSAlexander Graf  */
1848f31239acSAlexander Graf static void efi_exit_caches(void)
1849f31239acSAlexander Graf {
1850f31239acSAlexander Graf #if defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
1851f31239acSAlexander Graf 	/*
1852f31239acSAlexander Graf 	 * Grub on 32bit ARM needs to have caches disabled before jumping into
1853f31239acSAlexander Graf 	 * a zImage, but does not know of all cache layers. Give it a hand.
1854f31239acSAlexander Graf 	 */
1855f31239acSAlexander Graf 	if (efi_is_direct_boot)
1856f31239acSAlexander Graf 		cleanup_before_linux();
1857f31239acSAlexander Graf #endif
1858f31239acSAlexander Graf }
1859f31239acSAlexander Graf 
1860f31239acSAlexander Graf /**
186178a88f79SMario Six  * efi_exit_boot_services() - stop all boot services
186278a88f79SMario Six  * @image_handle: handle of the loaded image
186378a88f79SMario Six  * @map_key:      key of the memory map
1864332468f7SHeinrich Schuchardt  *
1865332468f7SHeinrich Schuchardt  * This function implements the ExitBootServices service.
186678a88f79SMario Six  *
1867332468f7SHeinrich Schuchardt  * See the Unified Extensible Firmware Interface (UEFI) specification
1868332468f7SHeinrich Schuchardt  * for details.
1869332468f7SHeinrich Schuchardt  *
187078a88f79SMario Six  * All timer events are disabled. For exit boot services events the
187178a88f79SMario Six  * notification function is called. The boot services are disabled in the
187278a88f79SMario Six  * system table.
1873cc20ed03SHeinrich Schuchardt  *
187478a88f79SMario Six  * Return: status code
1875332468f7SHeinrich Schuchardt  */
18762074f700SHeinrich Schuchardt static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
1877bee91169SAlexander Graf 						  unsigned long map_key)
1878bee91169SAlexander Graf {
187943bce442SHeinrich Schuchardt 	struct efi_event *evt;
1880152a263cSHeinrich Schuchardt 
1881bee91169SAlexander Graf 	EFI_ENTRY("%p, %ld", image_handle, map_key);
1882bee91169SAlexander Graf 
18831fcb7ea2SHeinrich Schuchardt 	/* Check that the caller has read the current memory map */
18841fcb7ea2SHeinrich Schuchardt 	if (map_key != efi_memory_map_key)
18851fcb7ea2SHeinrich Schuchardt 		return EFI_INVALID_PARAMETER;
18861fcb7ea2SHeinrich Schuchardt 
1887cc20ed03SHeinrich Schuchardt 	/* Make sure that notification functions are not called anymore */
1888cc20ed03SHeinrich Schuchardt 	efi_tpl = TPL_HIGH_LEVEL;
1889cc20ed03SHeinrich Schuchardt 
1890cc20ed03SHeinrich Schuchardt 	/* Check if ExitBootServices has already been called */
1891cc20ed03SHeinrich Schuchardt 	if (!systab.boottime)
1892cc20ed03SHeinrich Schuchardt 		return EFI_EXIT(EFI_SUCCESS);
1893cc20ed03SHeinrich Schuchardt 
1894b095f3c8SHeinrich Schuchardt 	/* Add related events to the event group */
1895b095f3c8SHeinrich Schuchardt 	list_for_each_entry(evt, &efi_events, link) {
1896b095f3c8SHeinrich Schuchardt 		if (evt->type == EVT_SIGNAL_EXIT_BOOT_SERVICES)
1897b095f3c8SHeinrich Schuchardt 			evt->group = &efi_guid_event_group_exit_boot_services;
1898b095f3c8SHeinrich Schuchardt 	}
1899152a263cSHeinrich Schuchardt 	/* Notify that ExitBootServices is invoked. */
190043bce442SHeinrich Schuchardt 	list_for_each_entry(evt, &efi_events, link) {
1901b095f3c8SHeinrich Schuchardt 		if (evt->group &&
1902b095f3c8SHeinrich Schuchardt 		    !guidcmp(evt->group,
1903b095f3c8SHeinrich Schuchardt 			     &efi_guid_event_group_exit_boot_services)) {
190443bce442SHeinrich Schuchardt 			efi_signal_event(evt, false);
1905b095f3c8SHeinrich Schuchardt 			break;
1906b095f3c8SHeinrich Schuchardt 		}
1907152a263cSHeinrich Schuchardt 	}
1908152a263cSHeinrich Schuchardt 
1909b72aaa87SHeinrich Schuchardt 	/* TODO: Should persist EFI variables here */
1910ad644e7cSRob Clark 
1911b7b8410aSAlexander Graf 	board_quiesce_devices();
1912b7b8410aSAlexander Graf 
1913f31239acSAlexander Graf 	/* Fix up caches for EFI payloads if necessary */
1914f31239acSAlexander Graf 	efi_exit_caches();
1915f31239acSAlexander Graf 
1916bee91169SAlexander Graf 	/* This stops all lingering devices */
1917bee91169SAlexander Graf 	bootm_disable_interrupts();
1918bee91169SAlexander Graf 
1919cc20ed03SHeinrich Schuchardt 	/* Disable boot time services */
1920cc20ed03SHeinrich Schuchardt 	systab.con_in_handle = NULL;
1921cc20ed03SHeinrich Schuchardt 	systab.con_in = NULL;
1922cc20ed03SHeinrich Schuchardt 	systab.con_out_handle = NULL;
1923cc20ed03SHeinrich Schuchardt 	systab.con_out = NULL;
1924cc20ed03SHeinrich Schuchardt 	systab.stderr_handle = NULL;
1925cc20ed03SHeinrich Schuchardt 	systab.std_err = NULL;
1926cc20ed03SHeinrich Schuchardt 	systab.boottime = NULL;
1927cc20ed03SHeinrich Schuchardt 
1928cc20ed03SHeinrich Schuchardt 	/* Recalculate CRC32 */
1929640adadfSHeinrich Schuchardt 	efi_update_table_header_crc32(&systab.hdr);
1930cc20ed03SHeinrich Schuchardt 
1931bee91169SAlexander Graf 	/* Give the payload some time to boot */
1932b3d60900SHeinrich Schuchardt 	efi_set_watchdog(0);
1933bee91169SAlexander Graf 	WATCHDOG_RESET();
1934bee91169SAlexander Graf 
1935bee91169SAlexander Graf 	return EFI_EXIT(EFI_SUCCESS);
1936bee91169SAlexander Graf }
1937bee91169SAlexander Graf 
19386b03cd10SHeinrich Schuchardt /**
193978a88f79SMario Six  * efi_get_next_monotonic_count() - get next value of the counter
194078a88f79SMario Six  * @count: returned value of the counter
1941332468f7SHeinrich Schuchardt  *
1942332468f7SHeinrich Schuchardt  * This function implements the NextMonotonicCount service.
1943332468f7SHeinrich Schuchardt  *
194478a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
194578a88f79SMario Six  * details.
194678a88f79SMario Six  *
194778a88f79SMario Six  * Return: status code
1948332468f7SHeinrich Schuchardt  */
1949bee91169SAlexander Graf static efi_status_t EFIAPI efi_get_next_monotonic_count(uint64_t *count)
1950bee91169SAlexander Graf {
1951ab9efa97SHeinrich Schuchardt 	static uint64_t mono;
1952ab9efa97SHeinrich Schuchardt 
1953bee91169SAlexander Graf 	EFI_ENTRY("%p", count);
1954bee91169SAlexander Graf 	*count = mono++;
1955bee91169SAlexander Graf 	return EFI_EXIT(EFI_SUCCESS);
1956bee91169SAlexander Graf }
1957bee91169SAlexander Graf 
19586b03cd10SHeinrich Schuchardt /**
195978a88f79SMario Six  * efi_stall() - sleep
19606b03cd10SHeinrich Schuchardt  * @microseconds: period to sleep in microseconds
196178a88f79SMario Six  *
196278a88f79SMario Six  * This function implements the Stall service.
196378a88f79SMario Six  *
196478a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
196578a88f79SMario Six  * details.
196678a88f79SMario Six  *
196778a88f79SMario Six  * Return:  status code
1968332468f7SHeinrich Schuchardt  */
1969bee91169SAlexander Graf static efi_status_t EFIAPI efi_stall(unsigned long microseconds)
1970bee91169SAlexander Graf {
1971bee91169SAlexander Graf 	EFI_ENTRY("%ld", microseconds);
1972bee91169SAlexander Graf 	udelay(microseconds);
1973bee91169SAlexander Graf 	return EFI_EXIT(EFI_SUCCESS);
1974bee91169SAlexander Graf }
1975bee91169SAlexander Graf 
19766b03cd10SHeinrich Schuchardt /**
197778a88f79SMario Six  * efi_set_watchdog_timer() - reset the watchdog timer
19786b03cd10SHeinrich Schuchardt  * @timeout:       seconds before reset by watchdog
19796b03cd10SHeinrich Schuchardt  * @watchdog_code: code to be logged when resetting
19806b03cd10SHeinrich Schuchardt  * @data_size:     size of buffer in bytes
19816b03cd10SHeinrich Schuchardt  * @watchdog_data: buffer with data describing the reset reason
198278a88f79SMario Six  *
198378a88f79SMario Six  * This function implements the SetWatchdogTimer service.
198478a88f79SMario Six  *
198578a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
198678a88f79SMario Six  * details.
198778a88f79SMario Six  *
198878a88f79SMario Six  * Return: status code
1989332468f7SHeinrich Schuchardt  */
1990bee91169SAlexander Graf static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout,
1991bee91169SAlexander Graf 						  uint64_t watchdog_code,
1992bee91169SAlexander Graf 						  unsigned long data_size,
1993bee91169SAlexander Graf 						  uint16_t *watchdog_data)
1994bee91169SAlexander Graf {
1995dee37fc9SMasahiro Yamada 	EFI_ENTRY("%ld, 0x%llx, %ld, %p", timeout, watchdog_code,
1996bee91169SAlexander Graf 		  data_size, watchdog_data);
1997b3d60900SHeinrich Schuchardt 	return EFI_EXIT(efi_set_watchdog(timeout));
1998bee91169SAlexander Graf }
1999bee91169SAlexander Graf 
20006b03cd10SHeinrich Schuchardt /**
200178a88f79SMario Six  * efi_close_protocol() - close a protocol
20026b03cd10SHeinrich Schuchardt  * @handle:            handle on which the protocol shall be closed
20036b03cd10SHeinrich Schuchardt  * @protocol:          GUID of the protocol to close
20046b03cd10SHeinrich Schuchardt  * @agent_handle:      handle of the driver
20056b03cd10SHeinrich Schuchardt  * @controller_handle: handle of the controller
200678a88f79SMario Six  *
200778a88f79SMario Six  * This function implements the CloseProtocol service.
200878a88f79SMario Six  *
200978a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
201078a88f79SMario Six  * details.
201178a88f79SMario Six  *
201278a88f79SMario Six  * Return: status code
2013332468f7SHeinrich Schuchardt  */
20142074f700SHeinrich Schuchardt static efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle,
20155a9682d0SHeinrich Schuchardt 					      const efi_guid_t *protocol,
20162074f700SHeinrich Schuchardt 					      efi_handle_t agent_handle,
20172074f700SHeinrich Schuchardt 					      efi_handle_t controller_handle)
2018bee91169SAlexander Graf {
20193b8a489cSHeinrich Schuchardt 	struct efi_handler *handler;
20203b8a489cSHeinrich Schuchardt 	struct efi_open_protocol_info_item *item;
20213b8a489cSHeinrich Schuchardt 	struct efi_open_protocol_info_item *pos;
20223b8a489cSHeinrich Schuchardt 	efi_status_t r;
20233b8a489cSHeinrich Schuchardt 
2024778e6af8SRob Clark 	EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, agent_handle,
2025bee91169SAlexander Graf 		  controller_handle);
20263b8a489cSHeinrich Schuchardt 
20273b8a489cSHeinrich Schuchardt 	if (!agent_handle) {
20283b8a489cSHeinrich Schuchardt 		r = EFI_INVALID_PARAMETER;
20293b8a489cSHeinrich Schuchardt 		goto out;
20303b8a489cSHeinrich Schuchardt 	}
20313b8a489cSHeinrich Schuchardt 	r = efi_search_protocol(handle, protocol, &handler);
20323b8a489cSHeinrich Schuchardt 	if (r != EFI_SUCCESS)
20333b8a489cSHeinrich Schuchardt 		goto out;
20343b8a489cSHeinrich Schuchardt 
20353b8a489cSHeinrich Schuchardt 	r = EFI_NOT_FOUND;
20363b8a489cSHeinrich Schuchardt 	list_for_each_entry_safe(item, pos, &handler->open_infos, link) {
20373b8a489cSHeinrich Schuchardt 		if (item->info.agent_handle == agent_handle &&
20383b8a489cSHeinrich Schuchardt 		    item->info.controller_handle == controller_handle) {
20393b8a489cSHeinrich Schuchardt 			efi_delete_open_info(item);
20403b8a489cSHeinrich Schuchardt 			r = EFI_SUCCESS;
20413b8a489cSHeinrich Schuchardt 			break;
20423b8a489cSHeinrich Schuchardt 		}
20433b8a489cSHeinrich Schuchardt 	}
20443b8a489cSHeinrich Schuchardt out:
20453b8a489cSHeinrich Schuchardt 	return EFI_EXIT(r);
2046bee91169SAlexander Graf }
2047bee91169SAlexander Graf 
20486b03cd10SHeinrich Schuchardt /**
204978a88f79SMario Six  * efi_open_protocol_information() - provide information about then open status
20506b03cd10SHeinrich Schuchardt  *                                   of a protocol on a handle
20516b03cd10SHeinrich Schuchardt  * @handle:       handle for which the information shall be retrieved
20526b03cd10SHeinrich Schuchardt  * @protocol:     GUID of the protocol
20536b03cd10SHeinrich Schuchardt  * @entry_buffer: buffer to receive the open protocol information
20546b03cd10SHeinrich Schuchardt  * @entry_count:  number of entries available in the buffer
205578a88f79SMario Six  *
205678a88f79SMario Six  * This function implements the OpenProtocolInformation service.
205778a88f79SMario Six  *
205878a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
205978a88f79SMario Six  * details.
206078a88f79SMario Six  *
206178a88f79SMario Six  * Return: status code
2062332468f7SHeinrich Schuchardt  */
2063ab9efa97SHeinrich Schuchardt static efi_status_t EFIAPI efi_open_protocol_information(
2064ab9efa97SHeinrich Schuchardt 			efi_handle_t handle, const efi_guid_t *protocol,
2065bee91169SAlexander Graf 			struct efi_open_protocol_info_entry **entry_buffer,
2066f5a2a938SHeinrich Schuchardt 			efi_uintn_t *entry_count)
2067bee91169SAlexander Graf {
2068e3fbbc36SHeinrich Schuchardt 	unsigned long buffer_size;
2069e3fbbc36SHeinrich Schuchardt 	unsigned long count;
2070e3fbbc36SHeinrich Schuchardt 	struct efi_handler *handler;
2071e3fbbc36SHeinrich Schuchardt 	struct efi_open_protocol_info_item *item;
2072e3fbbc36SHeinrich Schuchardt 	efi_status_t r;
2073e3fbbc36SHeinrich Schuchardt 
2074778e6af8SRob Clark 	EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, entry_buffer,
2075bee91169SAlexander Graf 		  entry_count);
2076e3fbbc36SHeinrich Schuchardt 
2077e3fbbc36SHeinrich Schuchardt 	/* Check parameters */
2078e3fbbc36SHeinrich Schuchardt 	if (!entry_buffer) {
2079e3fbbc36SHeinrich Schuchardt 		r = EFI_INVALID_PARAMETER;
2080e3fbbc36SHeinrich Schuchardt 		goto out;
2081e3fbbc36SHeinrich Schuchardt 	}
2082e3fbbc36SHeinrich Schuchardt 	r = efi_search_protocol(handle, protocol, &handler);
2083e3fbbc36SHeinrich Schuchardt 	if (r != EFI_SUCCESS)
2084e3fbbc36SHeinrich Schuchardt 		goto out;
2085e3fbbc36SHeinrich Schuchardt 
2086e3fbbc36SHeinrich Schuchardt 	/* Count entries */
2087e3fbbc36SHeinrich Schuchardt 	count = 0;
2088e3fbbc36SHeinrich Schuchardt 	list_for_each_entry(item, &handler->open_infos, link) {
2089e3fbbc36SHeinrich Schuchardt 		if (item->info.open_count)
2090e3fbbc36SHeinrich Schuchardt 			++count;
2091e3fbbc36SHeinrich Schuchardt 	}
2092e3fbbc36SHeinrich Schuchardt 	*entry_count = count;
2093e3fbbc36SHeinrich Schuchardt 	*entry_buffer = NULL;
2094e3fbbc36SHeinrich Schuchardt 	if (!count) {
2095e3fbbc36SHeinrich Schuchardt 		r = EFI_SUCCESS;
2096e3fbbc36SHeinrich Schuchardt 		goto out;
2097e3fbbc36SHeinrich Schuchardt 	}
2098e3fbbc36SHeinrich Schuchardt 
2099e3fbbc36SHeinrich Schuchardt 	/* Copy entries */
2100e3fbbc36SHeinrich Schuchardt 	buffer_size = count * sizeof(struct efi_open_protocol_info_entry);
2101eee6530eSHeinrich Schuchardt 	r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
2102e3fbbc36SHeinrich Schuchardt 			      (void **)entry_buffer);
2103e3fbbc36SHeinrich Schuchardt 	if (r != EFI_SUCCESS)
2104e3fbbc36SHeinrich Schuchardt 		goto out;
2105e3fbbc36SHeinrich Schuchardt 	list_for_each_entry_reverse(item, &handler->open_infos, link) {
2106e3fbbc36SHeinrich Schuchardt 		if (item->info.open_count)
2107e3fbbc36SHeinrich Schuchardt 			(*entry_buffer)[--count] = item->info;
2108e3fbbc36SHeinrich Schuchardt 	}
2109e3fbbc36SHeinrich Schuchardt out:
2110e3fbbc36SHeinrich Schuchardt 	return EFI_EXIT(r);
2111bee91169SAlexander Graf }
2112bee91169SAlexander Graf 
21136b03cd10SHeinrich Schuchardt /**
211478a88f79SMario Six  * efi_protocols_per_handle() - get protocols installed on a handle
21156b03cd10SHeinrich Schuchardt  * @handle:                handle for which the information is retrieved
21166b03cd10SHeinrich Schuchardt  * @protocol_buffer:       buffer with protocol GUIDs
21176b03cd10SHeinrich Schuchardt  * @protocol_buffer_count: number of entries in the buffer
211878a88f79SMario Six  *
211978a88f79SMario Six  * This function implements the ProtocolsPerHandleService.
212078a88f79SMario Six  *
212178a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
212278a88f79SMario Six  * details.
212378a88f79SMario Six  *
212478a88f79SMario Six  * Return: status code
2125332468f7SHeinrich Schuchardt  */
21262074f700SHeinrich Schuchardt static efi_status_t EFIAPI efi_protocols_per_handle(
21272074f700SHeinrich Schuchardt 			efi_handle_t handle, efi_guid_t ***protocol_buffer,
2128f5a2a938SHeinrich Schuchardt 			efi_uintn_t *protocol_buffer_count)
2129bee91169SAlexander Graf {
2130c0ebfc86Sxypron.glpk@gmx.de 	unsigned long buffer_size;
2131c0ebfc86Sxypron.glpk@gmx.de 	struct efi_object *efiobj;
213269fb6b1aSHeinrich Schuchardt 	struct list_head *protocol_handle;
2133c0ebfc86Sxypron.glpk@gmx.de 	efi_status_t r;
2134c0ebfc86Sxypron.glpk@gmx.de 
2135bee91169SAlexander Graf 	EFI_ENTRY("%p, %p, %p", handle, protocol_buffer,
2136bee91169SAlexander Graf 		  protocol_buffer_count);
2137c0ebfc86Sxypron.glpk@gmx.de 
2138c0ebfc86Sxypron.glpk@gmx.de 	if (!handle || !protocol_buffer || !protocol_buffer_count)
2139c0ebfc86Sxypron.glpk@gmx.de 		return EFI_EXIT(EFI_INVALID_PARAMETER);
2140c0ebfc86Sxypron.glpk@gmx.de 
2141c0ebfc86Sxypron.glpk@gmx.de 	*protocol_buffer = NULL;
2142661c8327SRob Clark 	*protocol_buffer_count = 0;
2143c0ebfc86Sxypron.glpk@gmx.de 
214469fb6b1aSHeinrich Schuchardt 	efiobj = efi_search_obj(handle);
214569fb6b1aSHeinrich Schuchardt 	if (!efiobj)
214669fb6b1aSHeinrich Schuchardt 		return EFI_EXIT(EFI_INVALID_PARAMETER);
2147c0ebfc86Sxypron.glpk@gmx.de 
2148c0ebfc86Sxypron.glpk@gmx.de 	/* Count protocols */
214969fb6b1aSHeinrich Schuchardt 	list_for_each(protocol_handle, &efiobj->protocols) {
2150c0ebfc86Sxypron.glpk@gmx.de 		++*protocol_buffer_count;
2151c0ebfc86Sxypron.glpk@gmx.de 	}
215269fb6b1aSHeinrich Schuchardt 
2153b72aaa87SHeinrich Schuchardt 	/* Copy GUIDs */
2154c0ebfc86Sxypron.glpk@gmx.de 	if (*protocol_buffer_count) {
215569fb6b1aSHeinrich Schuchardt 		size_t j = 0;
215669fb6b1aSHeinrich Schuchardt 
215769fb6b1aSHeinrich Schuchardt 		buffer_size = sizeof(efi_guid_t *) * *protocol_buffer_count;
2158eee6530eSHeinrich Schuchardt 		r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
2159c0ebfc86Sxypron.glpk@gmx.de 				      (void **)protocol_buffer);
2160c0ebfc86Sxypron.glpk@gmx.de 		if (r != EFI_SUCCESS)
2161c0ebfc86Sxypron.glpk@gmx.de 			return EFI_EXIT(r);
216269fb6b1aSHeinrich Schuchardt 		list_for_each(protocol_handle, &efiobj->protocols) {
216369fb6b1aSHeinrich Schuchardt 			struct efi_handler *protocol;
216469fb6b1aSHeinrich Schuchardt 
216569fb6b1aSHeinrich Schuchardt 			protocol = list_entry(protocol_handle,
216669fb6b1aSHeinrich Schuchardt 					      struct efi_handler, link);
216769fb6b1aSHeinrich Schuchardt 			(*protocol_buffer)[j] = (void *)protocol->guid;
2168c0ebfc86Sxypron.glpk@gmx.de 			++j;
2169c0ebfc86Sxypron.glpk@gmx.de 		}
2170c0ebfc86Sxypron.glpk@gmx.de 	}
2171c0ebfc86Sxypron.glpk@gmx.de 
2172c0ebfc86Sxypron.glpk@gmx.de 	return EFI_EXIT(EFI_SUCCESS);
2173bee91169SAlexander Graf }
2174bee91169SAlexander Graf 
21756b03cd10SHeinrich Schuchardt /**
217678a88f79SMario Six  * efi_locate_handle_buffer() - locate handles implementing a protocol
21776b03cd10SHeinrich Schuchardt  * @search_type: selection criterion
21786b03cd10SHeinrich Schuchardt  * @protocol:    GUID of the protocol
21796b03cd10SHeinrich Schuchardt  * @search_key:  registration key
21806b03cd10SHeinrich Schuchardt  * @no_handles:  number of returned handles
21816b03cd10SHeinrich Schuchardt  * @buffer:      buffer with the returned handles
218278a88f79SMario Six  *
218378a88f79SMario Six  * This function implements the LocateHandleBuffer service.
218478a88f79SMario Six  *
218578a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
218678a88f79SMario Six  * details.
218778a88f79SMario Six  *
218878a88f79SMario Six  * Return: status code
2189332468f7SHeinrich Schuchardt  */
2190bee91169SAlexander Graf static efi_status_t EFIAPI efi_locate_handle_buffer(
2191bee91169SAlexander Graf 			enum efi_locate_search_type search_type,
21925a9682d0SHeinrich Schuchardt 			const efi_guid_t *protocol, void *search_key,
2193f5a2a938SHeinrich Schuchardt 			efi_uintn_t *no_handles, efi_handle_t **buffer)
2194bee91169SAlexander Graf {
2195c2e703f9Sxypron.glpk@gmx.de 	efi_status_t r;
2196f5a2a938SHeinrich Schuchardt 	efi_uintn_t buffer_size = 0;
2197c2e703f9Sxypron.glpk@gmx.de 
2198778e6af8SRob Clark 	EFI_ENTRY("%d, %pUl, %p, %p, %p", search_type, protocol, search_key,
2199bee91169SAlexander Graf 		  no_handles, buffer);
2200c2e703f9Sxypron.glpk@gmx.de 
2201c2e703f9Sxypron.glpk@gmx.de 	if (!no_handles || !buffer) {
2202c2e703f9Sxypron.glpk@gmx.de 		r = EFI_INVALID_PARAMETER;
2203c2e703f9Sxypron.glpk@gmx.de 		goto out;
2204c2e703f9Sxypron.glpk@gmx.de 	}
2205c2e703f9Sxypron.glpk@gmx.de 	*no_handles = 0;
2206c2e703f9Sxypron.glpk@gmx.de 	*buffer = NULL;
2207c2e703f9Sxypron.glpk@gmx.de 	r = efi_locate_handle(search_type, protocol, search_key, &buffer_size,
2208c2e703f9Sxypron.glpk@gmx.de 			      *buffer);
2209c2e703f9Sxypron.glpk@gmx.de 	if (r != EFI_BUFFER_TOO_SMALL)
2210c2e703f9Sxypron.glpk@gmx.de 		goto out;
2211eee6530eSHeinrich Schuchardt 	r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
2212c2e703f9Sxypron.glpk@gmx.de 			      (void **)buffer);
2213c2e703f9Sxypron.glpk@gmx.de 	if (r != EFI_SUCCESS)
2214c2e703f9Sxypron.glpk@gmx.de 		goto out;
2215c2e703f9Sxypron.glpk@gmx.de 	r = efi_locate_handle(search_type, protocol, search_key, &buffer_size,
2216c2e703f9Sxypron.glpk@gmx.de 			      *buffer);
2217c2e703f9Sxypron.glpk@gmx.de 	if (r == EFI_SUCCESS)
22182074f700SHeinrich Schuchardt 		*no_handles = buffer_size / sizeof(efi_handle_t);
2219c2e703f9Sxypron.glpk@gmx.de out:
2220c2e703f9Sxypron.glpk@gmx.de 	return EFI_EXIT(r);
2221bee91169SAlexander Graf }
2222bee91169SAlexander Graf 
22236b03cd10SHeinrich Schuchardt /**
222478a88f79SMario Six  * efi_locate_protocol() - find an interface implementing a protocol
22256b03cd10SHeinrich Schuchardt  * @protocol:           GUID of the protocol
22266b03cd10SHeinrich Schuchardt  * @registration:       registration key passed to the notification function
22276b03cd10SHeinrich Schuchardt  * @protocol_interface: interface implementing the protocol
222878a88f79SMario Six  *
222978a88f79SMario Six  * This function implements the LocateProtocol service.
223078a88f79SMario Six  *
223178a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
223278a88f79SMario Six  * details.
223378a88f79SMario Six  *
223478a88f79SMario Six  * Return: status code
2235332468f7SHeinrich Schuchardt  */
22365a9682d0SHeinrich Schuchardt static efi_status_t EFIAPI efi_locate_protocol(const efi_guid_t *protocol,
2237bee91169SAlexander Graf 					       void *registration,
2238bee91169SAlexander Graf 					       void **protocol_interface)
2239bee91169SAlexander Graf {
224088adae5eSxypron.glpk@gmx.de 	struct list_head *lhandle;
22419172cd91SHeinrich Schuchardt 	efi_status_t ret;
2242bee91169SAlexander Graf 
2243778e6af8SRob Clark 	EFI_ENTRY("%pUl, %p, %p", protocol, registration, protocol_interface);
224488adae5eSxypron.glpk@gmx.de 
224588adae5eSxypron.glpk@gmx.de 	if (!protocol || !protocol_interface)
224688adae5eSxypron.glpk@gmx.de 		return EFI_EXIT(EFI_INVALID_PARAMETER);
224788adae5eSxypron.glpk@gmx.de 
224888adae5eSxypron.glpk@gmx.de 	list_for_each(lhandle, &efi_obj_list) {
224988adae5eSxypron.glpk@gmx.de 		struct efi_object *efiobj;
22509172cd91SHeinrich Schuchardt 		struct efi_handler *handler;
225188adae5eSxypron.glpk@gmx.de 
225288adae5eSxypron.glpk@gmx.de 		efiobj = list_entry(lhandle, struct efi_object, link);
225388adae5eSxypron.glpk@gmx.de 
2254fae0118eSHeinrich Schuchardt 		ret = efi_search_protocol(efiobj, protocol, &handler);
22559172cd91SHeinrich Schuchardt 		if (ret == EFI_SUCCESS) {
22569172cd91SHeinrich Schuchardt 			*protocol_interface = handler->protocol_interface;
2257bee91169SAlexander Graf 			return EFI_EXIT(EFI_SUCCESS);
2258bee91169SAlexander Graf 		}
2259bee91169SAlexander Graf 	}
226088adae5eSxypron.glpk@gmx.de 	*protocol_interface = NULL;
2261bee91169SAlexander Graf 
2262bee91169SAlexander Graf 	return EFI_EXIT(EFI_NOT_FOUND);
2263bee91169SAlexander Graf }
2264bee91169SAlexander Graf 
22656b03cd10SHeinrich Schuchardt /**
226678a88f79SMario Six  * efi_locate_device_path() - Get the device path and handle of an device
22676b03cd10SHeinrich Schuchardt  *                            implementing a protocol
22686b03cd10SHeinrich Schuchardt  * @protocol:    GUID of the protocol
22696b03cd10SHeinrich Schuchardt  * @device_path: device path
22706b03cd10SHeinrich Schuchardt  * @device:      handle of the device
227178a88f79SMario Six  *
227278a88f79SMario Six  * This function implements the LocateDevicePath service.
227378a88f79SMario Six  *
227478a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
227578a88f79SMario Six  * details.
227678a88f79SMario Six  *
227778a88f79SMario Six  * Return: status code
2278ae2c85c1SHeinrich Schuchardt  */
2279ae2c85c1SHeinrich Schuchardt static efi_status_t EFIAPI efi_locate_device_path(
2280ae2c85c1SHeinrich Schuchardt 			const efi_guid_t *protocol,
2281ae2c85c1SHeinrich Schuchardt 			struct efi_device_path **device_path,
2282ae2c85c1SHeinrich Schuchardt 			efi_handle_t *device)
2283ae2c85c1SHeinrich Schuchardt {
2284ae2c85c1SHeinrich Schuchardt 	struct efi_device_path *dp;
2285ae2c85c1SHeinrich Schuchardt 	size_t i;
2286ae2c85c1SHeinrich Schuchardt 	struct efi_handler *handler;
2287ae2c85c1SHeinrich Schuchardt 	efi_handle_t *handles;
2288ae2c85c1SHeinrich Schuchardt 	size_t len, len_dp;
2289ae2c85c1SHeinrich Schuchardt 	size_t len_best = 0;
2290ae2c85c1SHeinrich Schuchardt 	efi_uintn_t no_handles;
2291ae2c85c1SHeinrich Schuchardt 	u8 *remainder;
2292ae2c85c1SHeinrich Schuchardt 	efi_status_t ret;
2293ae2c85c1SHeinrich Schuchardt 
2294ae2c85c1SHeinrich Schuchardt 	EFI_ENTRY("%pUl, %p, %p", protocol, device_path, device);
2295ae2c85c1SHeinrich Schuchardt 
2296ae2c85c1SHeinrich Schuchardt 	if (!protocol || !device_path || !*device_path || !device) {
2297ae2c85c1SHeinrich Schuchardt 		ret = EFI_INVALID_PARAMETER;
2298ae2c85c1SHeinrich Schuchardt 		goto out;
2299ae2c85c1SHeinrich Schuchardt 	}
2300ae2c85c1SHeinrich Schuchardt 
2301ae2c85c1SHeinrich Schuchardt 	/* Find end of device path */
2302f6dd3f35SHeinrich Schuchardt 	len = efi_dp_instance_size(*device_path);
2303ae2c85c1SHeinrich Schuchardt 
2304ae2c85c1SHeinrich Schuchardt 	/* Get all handles implementing the protocol */
2305ae2c85c1SHeinrich Schuchardt 	ret = EFI_CALL(efi_locate_handle_buffer(BY_PROTOCOL, protocol, NULL,
2306ae2c85c1SHeinrich Schuchardt 						&no_handles, &handles));
2307ae2c85c1SHeinrich Schuchardt 	if (ret != EFI_SUCCESS)
2308ae2c85c1SHeinrich Schuchardt 		goto out;
2309ae2c85c1SHeinrich Schuchardt 
2310ae2c85c1SHeinrich Schuchardt 	for (i = 0; i < no_handles; ++i) {
2311ae2c85c1SHeinrich Schuchardt 		/* Find the device path protocol */
2312ae2c85c1SHeinrich Schuchardt 		ret = efi_search_protocol(handles[i], &efi_guid_device_path,
2313ae2c85c1SHeinrich Schuchardt 					  &handler);
2314ae2c85c1SHeinrich Schuchardt 		if (ret != EFI_SUCCESS)
2315ae2c85c1SHeinrich Schuchardt 			continue;
2316ae2c85c1SHeinrich Schuchardt 		dp = (struct efi_device_path *)handler->protocol_interface;
2317f6dd3f35SHeinrich Schuchardt 		len_dp = efi_dp_instance_size(dp);
2318ae2c85c1SHeinrich Schuchardt 		/*
2319ae2c85c1SHeinrich Schuchardt 		 * This handle can only be a better fit
2320ae2c85c1SHeinrich Schuchardt 		 * if its device path length is longer than the best fit and
2321ae2c85c1SHeinrich Schuchardt 		 * if its device path length is shorter of equal the searched
2322ae2c85c1SHeinrich Schuchardt 		 * device path.
2323ae2c85c1SHeinrich Schuchardt 		 */
2324ae2c85c1SHeinrich Schuchardt 		if (len_dp <= len_best || len_dp > len)
2325ae2c85c1SHeinrich Schuchardt 			continue;
2326ae2c85c1SHeinrich Schuchardt 		/* Check if dp is a subpath of device_path */
2327ae2c85c1SHeinrich Schuchardt 		if (memcmp(*device_path, dp, len_dp))
2328ae2c85c1SHeinrich Schuchardt 			continue;
2329ae2c85c1SHeinrich Schuchardt 		*device = handles[i];
2330ae2c85c1SHeinrich Schuchardt 		len_best = len_dp;
2331ae2c85c1SHeinrich Schuchardt 	}
2332ae2c85c1SHeinrich Schuchardt 	if (len_best) {
2333ae2c85c1SHeinrich Schuchardt 		remainder = (u8 *)*device_path + len_best;
2334ae2c85c1SHeinrich Schuchardt 		*device_path = (struct efi_device_path *)remainder;
2335ae2c85c1SHeinrich Schuchardt 		ret = EFI_SUCCESS;
2336ae2c85c1SHeinrich Schuchardt 	} else {
2337ae2c85c1SHeinrich Schuchardt 		ret = EFI_NOT_FOUND;
2338ae2c85c1SHeinrich Schuchardt 	}
2339ae2c85c1SHeinrich Schuchardt out:
2340ae2c85c1SHeinrich Schuchardt 	return EFI_EXIT(ret);
2341ae2c85c1SHeinrich Schuchardt }
2342ae2c85c1SHeinrich Schuchardt 
23436b03cd10SHeinrich Schuchardt /**
234478a88f79SMario Six  * efi_install_multiple_protocol_interfaces() - Install multiple protocol
234578a88f79SMario Six  *                                              interfaces
234678a88f79SMario Six  * @handle: handle on which the protocol interfaces shall be installed
234778a88f79SMario Six  * @...:    NULL terminated argument list with pairs of protocol GUIDS and
234878a88f79SMario Six  *          interfaces
2349332468f7SHeinrich Schuchardt  *
2350332468f7SHeinrich Schuchardt  * This function implements the MultipleProtocolInterfaces service.
2351332468f7SHeinrich Schuchardt  *
235278a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
235378a88f79SMario Six  * details.
235478a88f79SMario Six  *
235578a88f79SMario Six  * Return: status code
2356332468f7SHeinrich Schuchardt  */
2357faea1041SHeinrich Schuchardt static efi_status_t EFIAPI efi_install_multiple_protocol_interfaces
2358faea1041SHeinrich Schuchardt 				(efi_handle_t *handle, ...)
2359bee91169SAlexander Graf {
2360bee91169SAlexander Graf 	EFI_ENTRY("%p", handle);
236158b83586Sxypron.glpk@gmx.de 
2362beb077a2SAlexander Graf 	efi_va_list argptr;
23635a9682d0SHeinrich Schuchardt 	const efi_guid_t *protocol;
236458b83586Sxypron.glpk@gmx.de 	void *protocol_interface;
236558b83586Sxypron.glpk@gmx.de 	efi_status_t r = EFI_SUCCESS;
236658b83586Sxypron.glpk@gmx.de 	int i = 0;
236758b83586Sxypron.glpk@gmx.de 
236858b83586Sxypron.glpk@gmx.de 	if (!handle)
236958b83586Sxypron.glpk@gmx.de 		return EFI_EXIT(EFI_INVALID_PARAMETER);
237058b83586Sxypron.glpk@gmx.de 
2371beb077a2SAlexander Graf 	efi_va_start(argptr, handle);
237258b83586Sxypron.glpk@gmx.de 	for (;;) {
2373beb077a2SAlexander Graf 		protocol = efi_va_arg(argptr, efi_guid_t*);
237458b83586Sxypron.glpk@gmx.de 		if (!protocol)
237558b83586Sxypron.glpk@gmx.de 			break;
2376beb077a2SAlexander Graf 		protocol_interface = efi_va_arg(argptr, void*);
23771760ef57SHeinrich Schuchardt 		r = EFI_CALL(efi_install_protocol_interface(
23781760ef57SHeinrich Schuchardt 						handle, protocol,
237958b83586Sxypron.glpk@gmx.de 						EFI_NATIVE_INTERFACE,
23801760ef57SHeinrich Schuchardt 						protocol_interface));
238158b83586Sxypron.glpk@gmx.de 		if (r != EFI_SUCCESS)
238258b83586Sxypron.glpk@gmx.de 			break;
238358b83586Sxypron.glpk@gmx.de 		i++;
238458b83586Sxypron.glpk@gmx.de 	}
2385beb077a2SAlexander Graf 	efi_va_end(argptr);
238658b83586Sxypron.glpk@gmx.de 	if (r == EFI_SUCCESS)
238758b83586Sxypron.glpk@gmx.de 		return EFI_EXIT(r);
238858b83586Sxypron.glpk@gmx.de 
238962471e46SHeinrich Schuchardt 	/* If an error occurred undo all changes. */
2390beb077a2SAlexander Graf 	efi_va_start(argptr, handle);
239158b83586Sxypron.glpk@gmx.de 	for (; i; --i) {
2392beb077a2SAlexander Graf 		protocol = efi_va_arg(argptr, efi_guid_t*);
2393beb077a2SAlexander Graf 		protocol_interface = efi_va_arg(argptr, void*);
2394faea1041SHeinrich Schuchardt 		EFI_CALL(efi_uninstall_protocol_interface(*handle, protocol,
2395cd534083SHeinrich Schuchardt 							  protocol_interface));
239658b83586Sxypron.glpk@gmx.de 	}
2397beb077a2SAlexander Graf 	efi_va_end(argptr);
239858b83586Sxypron.glpk@gmx.de 
239958b83586Sxypron.glpk@gmx.de 	return EFI_EXIT(r);
2400bee91169SAlexander Graf }
2401bee91169SAlexander Graf 
24026b03cd10SHeinrich Schuchardt /**
240378a88f79SMario Six  * efi_uninstall_multiple_protocol_interfaces() - uninstall multiple protocol
240478a88f79SMario Six  *                                                interfaces
240578a88f79SMario Six  * @handle: handle from which the protocol interfaces shall be removed
240678a88f79SMario Six  * @...:    NULL terminated argument list with pairs of protocol GUIDS and
24076b03cd10SHeinrich Schuchardt  *          interfaces
2408332468f7SHeinrich Schuchardt  *
2409332468f7SHeinrich Schuchardt  * This function implements the UninstallMultipleProtocolInterfaces service.
2410332468f7SHeinrich Schuchardt  *
241178a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
241278a88f79SMario Six  * details.
241378a88f79SMario Six  *
241478a88f79SMario Six  * Return: status code
2415332468f7SHeinrich Schuchardt  */
2416bee91169SAlexander Graf static efi_status_t EFIAPI efi_uninstall_multiple_protocol_interfaces(
2417faea1041SHeinrich Schuchardt 			efi_handle_t handle, ...)
2418bee91169SAlexander Graf {
2419bee91169SAlexander Graf 	EFI_ENTRY("%p", handle);
2420843ce54cSHeinrich Schuchardt 
2421beb077a2SAlexander Graf 	efi_va_list argptr;
2422843ce54cSHeinrich Schuchardt 	const efi_guid_t *protocol;
2423843ce54cSHeinrich Schuchardt 	void *protocol_interface;
2424843ce54cSHeinrich Schuchardt 	efi_status_t r = EFI_SUCCESS;
2425843ce54cSHeinrich Schuchardt 	size_t i = 0;
2426843ce54cSHeinrich Schuchardt 
2427843ce54cSHeinrich Schuchardt 	if (!handle)
2428bee91169SAlexander Graf 		return EFI_EXIT(EFI_INVALID_PARAMETER);
2429843ce54cSHeinrich Schuchardt 
2430beb077a2SAlexander Graf 	efi_va_start(argptr, handle);
2431843ce54cSHeinrich Schuchardt 	for (;;) {
2432beb077a2SAlexander Graf 		protocol = efi_va_arg(argptr, efi_guid_t*);
2433843ce54cSHeinrich Schuchardt 		if (!protocol)
2434843ce54cSHeinrich Schuchardt 			break;
2435beb077a2SAlexander Graf 		protocol_interface = efi_va_arg(argptr, void*);
24369b47f13bSHeinrich Schuchardt 		r = efi_uninstall_protocol(handle, protocol,
24379b47f13bSHeinrich Schuchardt 					   protocol_interface);
2438843ce54cSHeinrich Schuchardt 		if (r != EFI_SUCCESS)
2439843ce54cSHeinrich Schuchardt 			break;
2440843ce54cSHeinrich Schuchardt 		i++;
2441843ce54cSHeinrich Schuchardt 	}
2442beb077a2SAlexander Graf 	efi_va_end(argptr);
24439b47f13bSHeinrich Schuchardt 	if (r == EFI_SUCCESS) {
24449b47f13bSHeinrich Schuchardt 		/* If the last protocol has been removed, delete the handle. */
24459b47f13bSHeinrich Schuchardt 		if (list_empty(&handle->protocols)) {
24469b47f13bSHeinrich Schuchardt 			list_del(&handle->link);
24479b47f13bSHeinrich Schuchardt 			free(handle);
24489b47f13bSHeinrich Schuchardt 		}
2449843ce54cSHeinrich Schuchardt 		return EFI_EXIT(r);
24509b47f13bSHeinrich Schuchardt 	}
2451843ce54cSHeinrich Schuchardt 
2452843ce54cSHeinrich Schuchardt 	/* If an error occurred undo all changes. */
2453beb077a2SAlexander Graf 	efi_va_start(argptr, handle);
2454843ce54cSHeinrich Schuchardt 	for (; i; --i) {
2455beb077a2SAlexander Graf 		protocol = efi_va_arg(argptr, efi_guid_t*);
2456beb077a2SAlexander Graf 		protocol_interface = efi_va_arg(argptr, void*);
2457843ce54cSHeinrich Schuchardt 		EFI_CALL(efi_install_protocol_interface(&handle, protocol,
2458843ce54cSHeinrich Schuchardt 							EFI_NATIVE_INTERFACE,
2459843ce54cSHeinrich Schuchardt 							protocol_interface));
2460843ce54cSHeinrich Schuchardt 	}
2461beb077a2SAlexander Graf 	efi_va_end(argptr);
2462843ce54cSHeinrich Schuchardt 
2463e2373021SHeinrich Schuchardt 	/* In case of an error always return EFI_INVALID_PARAMETER */
2464e2373021SHeinrich Schuchardt 	return EFI_EXIT(EFI_INVALID_PARAMETER);
2465bee91169SAlexander Graf }
2466bee91169SAlexander Graf 
24676b03cd10SHeinrich Schuchardt /**
246878a88f79SMario Six  * efi_calculate_crc32() - calculate cyclic redundancy code
24696b03cd10SHeinrich Schuchardt  * @data:      buffer with data
24706b03cd10SHeinrich Schuchardt  * @data_size: size of buffer in bytes
24716b03cd10SHeinrich Schuchardt  * @crc32_p:   cyclic redundancy code
247278a88f79SMario Six  *
247378a88f79SMario Six  * This function implements the CalculateCrc32 service.
247478a88f79SMario Six  *
247578a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
247678a88f79SMario Six  * details.
247778a88f79SMario Six  *
247878a88f79SMario Six  * Return: status code
2479332468f7SHeinrich Schuchardt  */
24808aa8360eSHeinrich Schuchardt static efi_status_t EFIAPI efi_calculate_crc32(const void *data,
24818aa8360eSHeinrich Schuchardt 					       efi_uintn_t data_size,
24828aa8360eSHeinrich Schuchardt 					       u32 *crc32_p)
2483bee91169SAlexander Graf {
24848aa8360eSHeinrich Schuchardt 	EFI_ENTRY("%p, %zu", data, data_size);
2485bee91169SAlexander Graf 	*crc32_p = crc32(0, data, data_size);
2486bee91169SAlexander Graf 	return EFI_EXIT(EFI_SUCCESS);
2487bee91169SAlexander Graf }
2488bee91169SAlexander Graf 
24896b03cd10SHeinrich Schuchardt /**
249078a88f79SMario Six  * efi_copy_mem() - copy memory
24916b03cd10SHeinrich Schuchardt  * @destination: destination of the copy operation
24926b03cd10SHeinrich Schuchardt  * @source:      source of the copy operation
24936b03cd10SHeinrich Schuchardt  * @length:      number of bytes to copy
249478a88f79SMario Six  *
249578a88f79SMario Six  * This function implements the CopyMem service.
249678a88f79SMario Six  *
249778a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
249878a88f79SMario Six  * details.
2499332468f7SHeinrich Schuchardt  */
2500fc05a959SHeinrich Schuchardt static void EFIAPI efi_copy_mem(void *destination, const void *source,
2501fc05a959SHeinrich Schuchardt 				size_t length)
2502bee91169SAlexander Graf {
2503fc05a959SHeinrich Schuchardt 	EFI_ENTRY("%p, %p, %ld", destination, source, (unsigned long)length);
25040bc81a71SHeinrich Schuchardt 	memmove(destination, source, length);
2505f7c78176SHeinrich Schuchardt 	EFI_EXIT(EFI_SUCCESS);
2506bee91169SAlexander Graf }
2507bee91169SAlexander Graf 
25086b03cd10SHeinrich Schuchardt /**
250978a88f79SMario Six  * efi_set_mem() - Fill memory with a byte value.
25106b03cd10SHeinrich Schuchardt  * @buffer: buffer to fill
25116b03cd10SHeinrich Schuchardt  * @size:   size of buffer in bytes
25126b03cd10SHeinrich Schuchardt  * @value:  byte to copy to the buffer
251378a88f79SMario Six  *
251478a88f79SMario Six  * This function implements the SetMem service.
251578a88f79SMario Six  *
251678a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
251778a88f79SMario Six  * details.
2518332468f7SHeinrich Schuchardt  */
2519fc05a959SHeinrich Schuchardt static void EFIAPI efi_set_mem(void *buffer, size_t size, uint8_t value)
2520bee91169SAlexander Graf {
2521fc05a959SHeinrich Schuchardt 	EFI_ENTRY("%p, %ld, 0x%x", buffer, (unsigned long)size, value);
2522bee91169SAlexander Graf 	memset(buffer, value, size);
2523f7c78176SHeinrich Schuchardt 	EFI_EXIT(EFI_SUCCESS);
2524bee91169SAlexander Graf }
2525bee91169SAlexander Graf 
25266b03cd10SHeinrich Schuchardt /**
252778a88f79SMario Six  * efi_protocol_open() - open protocol interface on a handle
25286b03cd10SHeinrich Schuchardt  * @handler:            handler of a protocol
25296b03cd10SHeinrich Schuchardt  * @protocol_interface: interface implementing the protocol
25306b03cd10SHeinrich Schuchardt  * @agent_handle:       handle of the driver
25316b03cd10SHeinrich Schuchardt  * @controller_handle:  handle of the controller
25326b03cd10SHeinrich Schuchardt  * @attributes:         attributes indicating how to open the protocol
253378a88f79SMario Six  *
253478a88f79SMario Six  * Return: status code
2535191a41ccSHeinrich Schuchardt  */
2536191a41ccSHeinrich Schuchardt static efi_status_t efi_protocol_open(
2537191a41ccSHeinrich Schuchardt 			struct efi_handler *handler,
2538191a41ccSHeinrich Schuchardt 			void **protocol_interface, void *agent_handle,
2539191a41ccSHeinrich Schuchardt 			void *controller_handle, uint32_t attributes)
2540191a41ccSHeinrich Schuchardt {
2541191a41ccSHeinrich Schuchardt 	struct efi_open_protocol_info_item *item;
2542191a41ccSHeinrich Schuchardt 	struct efi_open_protocol_info_entry *match = NULL;
2543191a41ccSHeinrich Schuchardt 	bool opened_by_driver = false;
2544191a41ccSHeinrich Schuchardt 	bool opened_exclusive = false;
2545191a41ccSHeinrich Schuchardt 
2546191a41ccSHeinrich Schuchardt 	/* If there is no agent, only return the interface */
2547191a41ccSHeinrich Schuchardt 	if (!agent_handle)
2548191a41ccSHeinrich Schuchardt 		goto out;
2549191a41ccSHeinrich Schuchardt 
2550191a41ccSHeinrich Schuchardt 	/* For TEST_PROTOCOL ignore interface attribute */
2551191a41ccSHeinrich Schuchardt 	if (attributes != EFI_OPEN_PROTOCOL_TEST_PROTOCOL)
2552191a41ccSHeinrich Schuchardt 		*protocol_interface = NULL;
2553191a41ccSHeinrich Schuchardt 
2554191a41ccSHeinrich Schuchardt 	/*
2555191a41ccSHeinrich Schuchardt 	 * Check if the protocol is already opened by a driver with the same
2556191a41ccSHeinrich Schuchardt 	 * attributes or opened exclusively
2557191a41ccSHeinrich Schuchardt 	 */
2558191a41ccSHeinrich Schuchardt 	list_for_each_entry(item, &handler->open_infos, link) {
2559191a41ccSHeinrich Schuchardt 		if (item->info.agent_handle == agent_handle) {
2560191a41ccSHeinrich Schuchardt 			if ((attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) &&
2561191a41ccSHeinrich Schuchardt 			    (item->info.attributes == attributes))
2562191a41ccSHeinrich Schuchardt 				return EFI_ALREADY_STARTED;
2563191a41ccSHeinrich Schuchardt 		}
2564191a41ccSHeinrich Schuchardt 		if (item->info.attributes & EFI_OPEN_PROTOCOL_EXCLUSIVE)
2565191a41ccSHeinrich Schuchardt 			opened_exclusive = true;
2566191a41ccSHeinrich Schuchardt 	}
2567191a41ccSHeinrich Schuchardt 
2568191a41ccSHeinrich Schuchardt 	/* Only one controller can open the protocol exclusively */
2569191a41ccSHeinrich Schuchardt 	if (opened_exclusive && attributes &
2570191a41ccSHeinrich Schuchardt 	    (EFI_OPEN_PROTOCOL_EXCLUSIVE | EFI_OPEN_PROTOCOL_BY_DRIVER))
2571191a41ccSHeinrich Schuchardt 		return EFI_ACCESS_DENIED;
2572191a41ccSHeinrich Schuchardt 
2573191a41ccSHeinrich Schuchardt 	/* Prepare exclusive opening */
2574191a41ccSHeinrich Schuchardt 	if (attributes & EFI_OPEN_PROTOCOL_EXCLUSIVE) {
2575191a41ccSHeinrich Schuchardt 		/* Try to disconnect controllers */
2576191a41ccSHeinrich Schuchardt 		list_for_each_entry(item, &handler->open_infos, link) {
2577191a41ccSHeinrich Schuchardt 			if (item->info.attributes ==
2578191a41ccSHeinrich Schuchardt 					EFI_OPEN_PROTOCOL_BY_DRIVER)
2579191a41ccSHeinrich Schuchardt 				EFI_CALL(efi_disconnect_controller(
2580191a41ccSHeinrich Schuchardt 						item->info.controller_handle,
2581191a41ccSHeinrich Schuchardt 						item->info.agent_handle,
2582191a41ccSHeinrich Schuchardt 						NULL));
2583191a41ccSHeinrich Schuchardt 		}
2584191a41ccSHeinrich Schuchardt 		opened_by_driver = false;
2585191a41ccSHeinrich Schuchardt 		/* Check if all controllers are disconnected */
2586191a41ccSHeinrich Schuchardt 		list_for_each_entry(item, &handler->open_infos, link) {
2587191a41ccSHeinrich Schuchardt 			if (item->info.attributes & EFI_OPEN_PROTOCOL_BY_DRIVER)
2588191a41ccSHeinrich Schuchardt 				opened_by_driver = true;
2589191a41ccSHeinrich Schuchardt 		}
25904f37fa47SHeinrich Schuchardt 		/* Only one controller can be connected */
2591191a41ccSHeinrich Schuchardt 		if (opened_by_driver)
2592191a41ccSHeinrich Schuchardt 			return EFI_ACCESS_DENIED;
2593191a41ccSHeinrich Schuchardt 	}
2594191a41ccSHeinrich Schuchardt 
2595191a41ccSHeinrich Schuchardt 	/* Find existing entry */
2596191a41ccSHeinrich Schuchardt 	list_for_each_entry(item, &handler->open_infos, link) {
2597191a41ccSHeinrich Schuchardt 		if (item->info.agent_handle == agent_handle &&
2598191a41ccSHeinrich Schuchardt 		    item->info.controller_handle == controller_handle)
2599191a41ccSHeinrich Schuchardt 			match = &item->info;
2600191a41ccSHeinrich Schuchardt 	}
2601191a41ccSHeinrich Schuchardt 	/* None found, create one */
2602191a41ccSHeinrich Schuchardt 	if (!match) {
2603191a41ccSHeinrich Schuchardt 		match = efi_create_open_info(handler);
2604191a41ccSHeinrich Schuchardt 		if (!match)
2605191a41ccSHeinrich Schuchardt 			return EFI_OUT_OF_RESOURCES;
2606191a41ccSHeinrich Schuchardt 	}
2607191a41ccSHeinrich Schuchardt 
2608191a41ccSHeinrich Schuchardt 	match->agent_handle = agent_handle;
2609191a41ccSHeinrich Schuchardt 	match->controller_handle = controller_handle;
2610191a41ccSHeinrich Schuchardt 	match->attributes = attributes;
2611191a41ccSHeinrich Schuchardt 	match->open_count++;
2612191a41ccSHeinrich Schuchardt 
2613191a41ccSHeinrich Schuchardt out:
2614191a41ccSHeinrich Schuchardt 	/* For TEST_PROTOCOL ignore interface attribute. */
2615191a41ccSHeinrich Schuchardt 	if (attributes != EFI_OPEN_PROTOCOL_TEST_PROTOCOL)
2616191a41ccSHeinrich Schuchardt 		*protocol_interface = handler->protocol_interface;
2617191a41ccSHeinrich Schuchardt 
2618191a41ccSHeinrich Schuchardt 	return EFI_SUCCESS;
2619191a41ccSHeinrich Schuchardt }
2620191a41ccSHeinrich Schuchardt 
26216b03cd10SHeinrich Schuchardt /**
262278a88f79SMario Six  * efi_open_protocol() - open protocol interface on a handle
26236b03cd10SHeinrich Schuchardt  * @handle:             handle on which the protocol shall be opened
26246b03cd10SHeinrich Schuchardt  * @protocol:           GUID of the protocol
26256b03cd10SHeinrich Schuchardt  * @protocol_interface: interface implementing the protocol
26266b03cd10SHeinrich Schuchardt  * @agent_handle:       handle of the driver
26276b03cd10SHeinrich Schuchardt  * @controller_handle:  handle of the controller
26286b03cd10SHeinrich Schuchardt  * @attributes:         attributes indicating how to open the protocol
262978a88f79SMario Six  *
263078a88f79SMario Six  * This function implements the OpenProtocol interface.
263178a88f79SMario Six  *
263278a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
263378a88f79SMario Six  * details.
263478a88f79SMario Six  *
263578a88f79SMario Six  * Return: status code
2636332468f7SHeinrich Schuchardt  */
2637faea1041SHeinrich Schuchardt static efi_status_t EFIAPI efi_open_protocol
2638faea1041SHeinrich Schuchardt 			(efi_handle_t handle, const efi_guid_t *protocol,
2639faea1041SHeinrich Schuchardt 			 void **protocol_interface, efi_handle_t agent_handle,
2640faea1041SHeinrich Schuchardt 			 efi_handle_t controller_handle, uint32_t attributes)
2641bee91169SAlexander Graf {
264280286e8fSHeinrich Schuchardt 	struct efi_handler *handler;
264369baec67Sxypron.glpk@gmx.de 	efi_status_t r = EFI_INVALID_PARAMETER;
2644bee91169SAlexander Graf 
2645778e6af8SRob Clark 	EFI_ENTRY("%p, %pUl, %p, %p, %p, 0x%x", handle, protocol,
2646bee91169SAlexander Graf 		  protocol_interface, agent_handle, controller_handle,
2647bee91169SAlexander Graf 		  attributes);
2648b5349f74Sxypron.glpk@gmx.de 
264969baec67Sxypron.glpk@gmx.de 	if (!handle || !protocol ||
265069baec67Sxypron.glpk@gmx.de 	    (!protocol_interface && attributes !=
265169baec67Sxypron.glpk@gmx.de 	     EFI_OPEN_PROTOCOL_TEST_PROTOCOL)) {
265269baec67Sxypron.glpk@gmx.de 		goto out;
265369baec67Sxypron.glpk@gmx.de 	}
265469baec67Sxypron.glpk@gmx.de 
265569baec67Sxypron.glpk@gmx.de 	switch (attributes) {
265669baec67Sxypron.glpk@gmx.de 	case EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL:
265769baec67Sxypron.glpk@gmx.de 	case EFI_OPEN_PROTOCOL_GET_PROTOCOL:
265869baec67Sxypron.glpk@gmx.de 	case EFI_OPEN_PROTOCOL_TEST_PROTOCOL:
265969baec67Sxypron.glpk@gmx.de 		break;
266069baec67Sxypron.glpk@gmx.de 	case EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER:
266169baec67Sxypron.glpk@gmx.de 		if (controller_handle == handle)
266269baec67Sxypron.glpk@gmx.de 			goto out;
2663191a41ccSHeinrich Schuchardt 		/* fall-through */
266469baec67Sxypron.glpk@gmx.de 	case EFI_OPEN_PROTOCOL_BY_DRIVER:
266569baec67Sxypron.glpk@gmx.de 	case EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE:
2666191a41ccSHeinrich Schuchardt 		/* Check that the controller handle is valid */
2667191a41ccSHeinrich Schuchardt 		if (!efi_search_obj(controller_handle))
266869baec67Sxypron.glpk@gmx.de 			goto out;
2669191a41ccSHeinrich Schuchardt 		/* fall-through */
267069baec67Sxypron.glpk@gmx.de 	case EFI_OPEN_PROTOCOL_EXCLUSIVE:
2671191a41ccSHeinrich Schuchardt 		/* Check that the agent handle is valid */
2672191a41ccSHeinrich Schuchardt 		if (!efi_search_obj(agent_handle))
267369baec67Sxypron.glpk@gmx.de 			goto out;
267469baec67Sxypron.glpk@gmx.de 		break;
267569baec67Sxypron.glpk@gmx.de 	default:
2676b5349f74Sxypron.glpk@gmx.de 		goto out;
2677b5349f74Sxypron.glpk@gmx.de 	}
2678b5349f74Sxypron.glpk@gmx.de 
267980286e8fSHeinrich Schuchardt 	r = efi_search_protocol(handle, protocol, &handler);
268080286e8fSHeinrich Schuchardt 	if (r != EFI_SUCCESS)
2681bee91169SAlexander Graf 		goto out;
2682bee91169SAlexander Graf 
2683191a41ccSHeinrich Schuchardt 	r = efi_protocol_open(handler, protocol_interface, agent_handle,
2684191a41ccSHeinrich Schuchardt 			      controller_handle, attributes);
2685bee91169SAlexander Graf out:
2686bee91169SAlexander Graf 	return EFI_EXIT(r);
2687bee91169SAlexander Graf }
2688bee91169SAlexander Graf 
26896b03cd10SHeinrich Schuchardt /**
269078a88f79SMario Six  * efi_handle_protocol() - get interface of a protocol on a handle
26916b03cd10SHeinrich Schuchardt  * @handle:             handle on which the protocol shall be opened
26926b03cd10SHeinrich Schuchardt  * @protocol:           GUID of the protocol
26936b03cd10SHeinrich Schuchardt  * @protocol_interface: interface implementing the protocol
269478a88f79SMario Six  *
269578a88f79SMario Six  * This function implements the HandleProtocol service.
269678a88f79SMario Six  *
269778a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
269878a88f79SMario Six  * details.
269978a88f79SMario Six  *
270078a88f79SMario Six  * Return: status code
2701332468f7SHeinrich Schuchardt  */
27022074f700SHeinrich Schuchardt static efi_status_t EFIAPI efi_handle_protocol(efi_handle_t handle,
27035a9682d0SHeinrich Schuchardt 					       const efi_guid_t *protocol,
2704bee91169SAlexander Graf 					       void **protocol_interface)
2705bee91169SAlexander Graf {
27068e1d329fSxypron.glpk@gmx.de 	return efi_open_protocol(handle, protocol, protocol_interface, NULL,
27078e1d329fSxypron.glpk@gmx.de 				 NULL, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL);
2708bee91169SAlexander Graf }
2709bee91169SAlexander Graf 
27106b03cd10SHeinrich Schuchardt /**
271178a88f79SMario Six  * efi_bind_controller() - bind a single driver to a controller
27126b03cd10SHeinrich Schuchardt  * @controller_handle:   controller handle
27136b03cd10SHeinrich Schuchardt  * @driver_image_handle: driver handle
27146b03cd10SHeinrich Schuchardt  * @remain_device_path:  remaining path
271578a88f79SMario Six  *
271678a88f79SMario Six  * Return: status code
27176b03cd10SHeinrich Schuchardt  */
2718f0959dbeSHeinrich Schuchardt static efi_status_t efi_bind_controller(
2719f0959dbeSHeinrich Schuchardt 			efi_handle_t controller_handle,
2720f0959dbeSHeinrich Schuchardt 			efi_handle_t driver_image_handle,
2721f0959dbeSHeinrich Schuchardt 			struct efi_device_path *remain_device_path)
2722f0959dbeSHeinrich Schuchardt {
2723f0959dbeSHeinrich Schuchardt 	struct efi_driver_binding_protocol *binding_protocol;
2724f0959dbeSHeinrich Schuchardt 	efi_status_t r;
2725f0959dbeSHeinrich Schuchardt 
2726f0959dbeSHeinrich Schuchardt 	r = EFI_CALL(efi_open_protocol(driver_image_handle,
2727f0959dbeSHeinrich Schuchardt 				       &efi_guid_driver_binding_protocol,
2728f0959dbeSHeinrich Schuchardt 				       (void **)&binding_protocol,
2729f0959dbeSHeinrich Schuchardt 				       driver_image_handle, NULL,
2730f0959dbeSHeinrich Schuchardt 				       EFI_OPEN_PROTOCOL_GET_PROTOCOL));
2731f0959dbeSHeinrich Schuchardt 	if (r != EFI_SUCCESS)
2732f0959dbeSHeinrich Schuchardt 		return r;
2733f0959dbeSHeinrich Schuchardt 	r = EFI_CALL(binding_protocol->supported(binding_protocol,
2734f0959dbeSHeinrich Schuchardt 						 controller_handle,
2735f0959dbeSHeinrich Schuchardt 						 remain_device_path));
2736f0959dbeSHeinrich Schuchardt 	if (r == EFI_SUCCESS)
2737f0959dbeSHeinrich Schuchardt 		r = EFI_CALL(binding_protocol->start(binding_protocol,
2738f0959dbeSHeinrich Schuchardt 						     controller_handle,
2739f0959dbeSHeinrich Schuchardt 						     remain_device_path));
2740f0959dbeSHeinrich Schuchardt 	EFI_CALL(efi_close_protocol(driver_image_handle,
2741f0959dbeSHeinrich Schuchardt 				    &efi_guid_driver_binding_protocol,
2742f0959dbeSHeinrich Schuchardt 				    driver_image_handle, NULL));
2743f0959dbeSHeinrich Schuchardt 	return r;
2744f0959dbeSHeinrich Schuchardt }
2745f0959dbeSHeinrich Schuchardt 
27466b03cd10SHeinrich Schuchardt /**
274778a88f79SMario Six  * efi_connect_single_controller() - connect a single driver to a controller
27486b03cd10SHeinrich Schuchardt  * @controller_handle:   controller
27496b03cd10SHeinrich Schuchardt  * @driver_image_handle: driver
2750b72aaa87SHeinrich Schuchardt  * @remain_device_path:  remaining path
275178a88f79SMario Six  *
275278a88f79SMario Six  * Return: status code
27536b03cd10SHeinrich Schuchardt  */
2754f0959dbeSHeinrich Schuchardt static efi_status_t efi_connect_single_controller(
2755f0959dbeSHeinrich Schuchardt 			efi_handle_t controller_handle,
2756f0959dbeSHeinrich Schuchardt 			efi_handle_t *driver_image_handle,
2757f0959dbeSHeinrich Schuchardt 			struct efi_device_path *remain_device_path)
2758f0959dbeSHeinrich Schuchardt {
2759f0959dbeSHeinrich Schuchardt 	efi_handle_t *buffer;
2760f0959dbeSHeinrich Schuchardt 	size_t count;
2761f0959dbeSHeinrich Schuchardt 	size_t i;
2762f0959dbeSHeinrich Schuchardt 	efi_status_t r;
2763f0959dbeSHeinrich Schuchardt 	size_t connected = 0;
2764f0959dbeSHeinrich Schuchardt 
2765f0959dbeSHeinrich Schuchardt 	/* Get buffer with all handles with driver binding protocol */
2766f0959dbeSHeinrich Schuchardt 	r = EFI_CALL(efi_locate_handle_buffer(BY_PROTOCOL,
2767f0959dbeSHeinrich Schuchardt 					      &efi_guid_driver_binding_protocol,
2768f0959dbeSHeinrich Schuchardt 					      NULL, &count, &buffer));
2769f0959dbeSHeinrich Schuchardt 	if (r != EFI_SUCCESS)
2770f0959dbeSHeinrich Schuchardt 		return r;
2771f0959dbeSHeinrich Schuchardt 
2772f0959dbeSHeinrich Schuchardt 	/*  Context Override */
2773f0959dbeSHeinrich Schuchardt 	if (driver_image_handle) {
2774f0959dbeSHeinrich Schuchardt 		for (; *driver_image_handle; ++driver_image_handle) {
2775f0959dbeSHeinrich Schuchardt 			for (i = 0; i < count; ++i) {
2776f0959dbeSHeinrich Schuchardt 				if (buffer[i] == *driver_image_handle) {
2777f0959dbeSHeinrich Schuchardt 					buffer[i] = NULL;
2778f0959dbeSHeinrich Schuchardt 					r = efi_bind_controller(
2779f0959dbeSHeinrich Schuchardt 							controller_handle,
2780f0959dbeSHeinrich Schuchardt 							*driver_image_handle,
2781f0959dbeSHeinrich Schuchardt 							remain_device_path);
2782f0959dbeSHeinrich Schuchardt 					/*
2783f0959dbeSHeinrich Schuchardt 					 * For drivers that do not support the
2784f0959dbeSHeinrich Schuchardt 					 * controller or are already connected
2785f0959dbeSHeinrich Schuchardt 					 * we receive an error code here.
2786f0959dbeSHeinrich Schuchardt 					 */
2787f0959dbeSHeinrich Schuchardt 					if (r == EFI_SUCCESS)
2788f0959dbeSHeinrich Schuchardt 						++connected;
2789f0959dbeSHeinrich Schuchardt 				}
2790f0959dbeSHeinrich Schuchardt 			}
2791f0959dbeSHeinrich Schuchardt 		}
2792f0959dbeSHeinrich Schuchardt 	}
2793f0959dbeSHeinrich Schuchardt 
2794f0959dbeSHeinrich Schuchardt 	/*
2795f0959dbeSHeinrich Schuchardt 	 * TODO: Some overrides are not yet implemented:
2796f0959dbeSHeinrich Schuchardt 	 * - Platform Driver Override
2797f0959dbeSHeinrich Schuchardt 	 * - Driver Family Override Search
2798f0959dbeSHeinrich Schuchardt 	 * - Bus Specific Driver Override
2799f0959dbeSHeinrich Schuchardt 	 */
2800f0959dbeSHeinrich Schuchardt 
2801f0959dbeSHeinrich Schuchardt 	/* Driver Binding Search */
2802f0959dbeSHeinrich Schuchardt 	for (i = 0; i < count; ++i) {
2803f0959dbeSHeinrich Schuchardt 		if (buffer[i]) {
2804f0959dbeSHeinrich Schuchardt 			r = efi_bind_controller(controller_handle,
2805f0959dbeSHeinrich Schuchardt 						buffer[i],
2806f0959dbeSHeinrich Schuchardt 						remain_device_path);
2807f0959dbeSHeinrich Schuchardt 			if (r == EFI_SUCCESS)
2808f0959dbeSHeinrich Schuchardt 				++connected;
2809f0959dbeSHeinrich Schuchardt 		}
2810f0959dbeSHeinrich Schuchardt 	}
2811f0959dbeSHeinrich Schuchardt 
2812f0959dbeSHeinrich Schuchardt 	efi_free_pool(buffer);
2813f0959dbeSHeinrich Schuchardt 	if (!connected)
2814f0959dbeSHeinrich Schuchardt 		return EFI_NOT_FOUND;
2815f0959dbeSHeinrich Schuchardt 	return EFI_SUCCESS;
2816f0959dbeSHeinrich Schuchardt }
2817f0959dbeSHeinrich Schuchardt 
28186b03cd10SHeinrich Schuchardt /**
281978a88f79SMario Six  * efi_connect_controller() - connect a controller to a driver
282078a88f79SMario Six  * @controller_handle:   handle of the controller
282178a88f79SMario Six  * @driver_image_handle: handle of the driver
282278a88f79SMario Six  * @remain_device_path:  device path of a child controller
282378a88f79SMario Six  * @recursive:           true to connect all child controllers
2824f0959dbeSHeinrich Schuchardt  *
2825f0959dbeSHeinrich Schuchardt  * This function implements the ConnectController service.
282678a88f79SMario Six  *
282778a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
282878a88f79SMario Six  * details.
2829f0959dbeSHeinrich Schuchardt  *
2830f0959dbeSHeinrich Schuchardt  * First all driver binding protocol handles are tried for binding drivers.
2831b72aaa87SHeinrich Schuchardt  * Afterwards all handles that have opened a protocol of the controller
2832f0959dbeSHeinrich Schuchardt  * with EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER are connected to drivers.
2833f0959dbeSHeinrich Schuchardt  *
283478a88f79SMario Six  * Return: status code
2835f0959dbeSHeinrich Schuchardt  */
2836f0959dbeSHeinrich Schuchardt static efi_status_t EFIAPI efi_connect_controller(
2837f0959dbeSHeinrich Schuchardt 			efi_handle_t controller_handle,
2838f0959dbeSHeinrich Schuchardt 			efi_handle_t *driver_image_handle,
2839f0959dbeSHeinrich Schuchardt 			struct efi_device_path *remain_device_path,
2840f0959dbeSHeinrich Schuchardt 			bool recursive)
2841f0959dbeSHeinrich Schuchardt {
2842f0959dbeSHeinrich Schuchardt 	efi_status_t r;
2843f0959dbeSHeinrich Schuchardt 	efi_status_t ret = EFI_NOT_FOUND;
2844f0959dbeSHeinrich Schuchardt 	struct efi_object *efiobj;
2845f0959dbeSHeinrich Schuchardt 
2846d178836bSHeinrich Schuchardt 	EFI_ENTRY("%p, %p, %pD, %d", controller_handle, driver_image_handle,
2847f0959dbeSHeinrich Schuchardt 		  remain_device_path, recursive);
2848f0959dbeSHeinrich Schuchardt 
2849f0959dbeSHeinrich Schuchardt 	efiobj = efi_search_obj(controller_handle);
2850f0959dbeSHeinrich Schuchardt 	if (!efiobj) {
2851f0959dbeSHeinrich Schuchardt 		ret = EFI_INVALID_PARAMETER;
2852f0959dbeSHeinrich Schuchardt 		goto out;
2853f0959dbeSHeinrich Schuchardt 	}
2854f0959dbeSHeinrich Schuchardt 
2855f0959dbeSHeinrich Schuchardt 	r = efi_connect_single_controller(controller_handle,
2856f0959dbeSHeinrich Schuchardt 					  driver_image_handle,
2857f0959dbeSHeinrich Schuchardt 					  remain_device_path);
2858f0959dbeSHeinrich Schuchardt 	if (r == EFI_SUCCESS)
2859f0959dbeSHeinrich Schuchardt 		ret = EFI_SUCCESS;
2860f0959dbeSHeinrich Schuchardt 	if (recursive) {
2861f0959dbeSHeinrich Schuchardt 		struct efi_handler *handler;
2862f0959dbeSHeinrich Schuchardt 		struct efi_open_protocol_info_item *item;
2863f0959dbeSHeinrich Schuchardt 
2864f0959dbeSHeinrich Schuchardt 		list_for_each_entry(handler, &efiobj->protocols, link) {
2865f0959dbeSHeinrich Schuchardt 			list_for_each_entry(item, &handler->open_infos, link) {
2866f0959dbeSHeinrich Schuchardt 				if (item->info.attributes &
2867f0959dbeSHeinrich Schuchardt 				    EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) {
2868f0959dbeSHeinrich Schuchardt 					r = EFI_CALL(efi_connect_controller(
2869f0959dbeSHeinrich Schuchardt 						item->info.controller_handle,
2870f0959dbeSHeinrich Schuchardt 						driver_image_handle,
2871f0959dbeSHeinrich Schuchardt 						remain_device_path,
2872f0959dbeSHeinrich Schuchardt 						recursive));
2873f0959dbeSHeinrich Schuchardt 					if (r == EFI_SUCCESS)
2874f0959dbeSHeinrich Schuchardt 						ret = EFI_SUCCESS;
2875f0959dbeSHeinrich Schuchardt 				}
2876f0959dbeSHeinrich Schuchardt 			}
2877f0959dbeSHeinrich Schuchardt 		}
2878f0959dbeSHeinrich Schuchardt 	}
2879f0959dbeSHeinrich Schuchardt 	/*  Check for child controller specified by end node */
2880f0959dbeSHeinrich Schuchardt 	if (ret != EFI_SUCCESS && remain_device_path &&
2881f0959dbeSHeinrich Schuchardt 	    remain_device_path->type == DEVICE_PATH_TYPE_END)
2882f0959dbeSHeinrich Schuchardt 		ret = EFI_SUCCESS;
2883f0959dbeSHeinrich Schuchardt out:
2884f0959dbeSHeinrich Schuchardt 	return EFI_EXIT(ret);
2885f0959dbeSHeinrich Schuchardt }
2886f0959dbeSHeinrich Schuchardt 
28876b03cd10SHeinrich Schuchardt /**
288878a88f79SMario Six  * efi_reinstall_protocol_interface() - reinstall protocol interface
288978a88f79SMario Six  * @handle:        handle on which the protocol shall be reinstalled
289078a88f79SMario Six  * @protocol:      GUID of the protocol to be installed
289178a88f79SMario Six  * @old_interface: interface to be removed
289278a88f79SMario Six  * @new_interface: interface to be installed
2893e861a120SHeinrich Schuchardt  *
2894e861a120SHeinrich Schuchardt  * This function implements the ReinstallProtocolInterface service.
289578a88f79SMario Six  *
289678a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
289778a88f79SMario Six  * details.
2898e861a120SHeinrich Schuchardt  *
2899e861a120SHeinrich Schuchardt  * The old interface is uninstalled. The new interface is installed.
2900e861a120SHeinrich Schuchardt  * Drivers are connected.
2901e861a120SHeinrich Schuchardt  *
290278a88f79SMario Six  * Return: status code
2903e861a120SHeinrich Schuchardt  */
2904e861a120SHeinrich Schuchardt static efi_status_t EFIAPI efi_reinstall_protocol_interface(
2905e861a120SHeinrich Schuchardt 			efi_handle_t handle, const efi_guid_t *protocol,
2906e861a120SHeinrich Schuchardt 			void *old_interface, void *new_interface)
2907e861a120SHeinrich Schuchardt {
2908e861a120SHeinrich Schuchardt 	efi_status_t ret;
2909e861a120SHeinrich Schuchardt 
2910e861a120SHeinrich Schuchardt 	EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, old_interface,
2911e861a120SHeinrich Schuchardt 		  new_interface);
29129b47f13bSHeinrich Schuchardt 
29139b47f13bSHeinrich Schuchardt 	/* Uninstall protocol but do not delete handle */
29149b47f13bSHeinrich Schuchardt 	ret = efi_uninstall_protocol(handle, protocol, old_interface);
2915e861a120SHeinrich Schuchardt 	if (ret != EFI_SUCCESS)
2916e861a120SHeinrich Schuchardt 		goto out;
29179b47f13bSHeinrich Schuchardt 
29189b47f13bSHeinrich Schuchardt 	/* Install the new protocol */
29199b47f13bSHeinrich Schuchardt 	ret = efi_add_protocol(handle, protocol, new_interface);
29209b47f13bSHeinrich Schuchardt 	/*
29219b47f13bSHeinrich Schuchardt 	 * The UEFI spec does not specify what should happen to the handle
29229b47f13bSHeinrich Schuchardt 	 * if in case of an error no protocol interface remains on the handle.
29239b47f13bSHeinrich Schuchardt 	 * So let's do nothing here.
29249b47f13bSHeinrich Schuchardt 	 */
2925e861a120SHeinrich Schuchardt 	if (ret != EFI_SUCCESS)
2926e861a120SHeinrich Schuchardt 		goto out;
2927e861a120SHeinrich Schuchardt 	/*
2928e861a120SHeinrich Schuchardt 	 * The returned status code has to be ignored.
2929e861a120SHeinrich Schuchardt 	 * Do not create an error if no suitable driver for the handle exists.
2930e861a120SHeinrich Schuchardt 	 */
2931e861a120SHeinrich Schuchardt 	EFI_CALL(efi_connect_controller(handle, NULL, NULL, true));
2932e861a120SHeinrich Schuchardt out:
2933e861a120SHeinrich Schuchardt 	return EFI_EXIT(ret);
2934e861a120SHeinrich Schuchardt }
2935e861a120SHeinrich Schuchardt 
29366b03cd10SHeinrich Schuchardt /**
293778a88f79SMario Six  * efi_get_child_controllers() - get all child controllers associated to a driver
29386b03cd10SHeinrich Schuchardt  * @efiobj:              handle of the controller
29396b03cd10SHeinrich Schuchardt  * @driver_handle:       handle of the driver
29406b03cd10SHeinrich Schuchardt  * @number_of_children:  number of child controllers
29416b03cd10SHeinrich Schuchardt  * @child_handle_buffer: handles of the the child controllers
294278a88f79SMario Six  *
294378a88f79SMario Six  * The allocated buffer has to be freed with free().
294478a88f79SMario Six  *
294578a88f79SMario Six  * Return: status code
29463f9b0042SHeinrich Schuchardt  */
29473f9b0042SHeinrich Schuchardt static efi_status_t efi_get_child_controllers(
29483f9b0042SHeinrich Schuchardt 				struct efi_object *efiobj,
29493f9b0042SHeinrich Schuchardt 				efi_handle_t driver_handle,
29503f9b0042SHeinrich Schuchardt 				efi_uintn_t *number_of_children,
29513f9b0042SHeinrich Schuchardt 				efi_handle_t **child_handle_buffer)
29523f9b0042SHeinrich Schuchardt {
29533f9b0042SHeinrich Schuchardt 	struct efi_handler *handler;
29543f9b0042SHeinrich Schuchardt 	struct efi_open_protocol_info_item *item;
29553f9b0042SHeinrich Schuchardt 	efi_uintn_t count = 0, i;
29563f9b0042SHeinrich Schuchardt 	bool duplicate;
29573f9b0042SHeinrich Schuchardt 
29583f9b0042SHeinrich Schuchardt 	/* Count all child controller associations */
29593f9b0042SHeinrich Schuchardt 	list_for_each_entry(handler, &efiobj->protocols, link) {
29603f9b0042SHeinrich Schuchardt 		list_for_each_entry(item, &handler->open_infos, link) {
29613f9b0042SHeinrich Schuchardt 			if (item->info.agent_handle == driver_handle &&
29623f9b0042SHeinrich Schuchardt 			    item->info.attributes &
29633f9b0042SHeinrich Schuchardt 			    EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER)
29643f9b0042SHeinrich Schuchardt 				++count;
29653f9b0042SHeinrich Schuchardt 		}
29663f9b0042SHeinrich Schuchardt 	}
29673f9b0042SHeinrich Schuchardt 	/*
29683f9b0042SHeinrich Schuchardt 	 * Create buffer. In case of duplicate child controller assignments
29693f9b0042SHeinrich Schuchardt 	 * the buffer will be too large. But that does not harm.
29703f9b0042SHeinrich Schuchardt 	 */
29713f9b0042SHeinrich Schuchardt 	*number_of_children = 0;
29723f9b0042SHeinrich Schuchardt 	*child_handle_buffer = calloc(count, sizeof(efi_handle_t));
29733f9b0042SHeinrich Schuchardt 	if (!*child_handle_buffer)
29743f9b0042SHeinrich Schuchardt 		return EFI_OUT_OF_RESOURCES;
29753f9b0042SHeinrich Schuchardt 	/* Copy unique child handles */
29763f9b0042SHeinrich Schuchardt 	list_for_each_entry(handler, &efiobj->protocols, link) {
29773f9b0042SHeinrich Schuchardt 		list_for_each_entry(item, &handler->open_infos, link) {
29783f9b0042SHeinrich Schuchardt 			if (item->info.agent_handle == driver_handle &&
29793f9b0042SHeinrich Schuchardt 			    item->info.attributes &
29803f9b0042SHeinrich Schuchardt 			    EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) {
29813f9b0042SHeinrich Schuchardt 				/* Check this is a new child controller */
29823f9b0042SHeinrich Schuchardt 				duplicate = false;
29833f9b0042SHeinrich Schuchardt 				for (i = 0; i < *number_of_children; ++i) {
29843f9b0042SHeinrich Schuchardt 					if ((*child_handle_buffer)[i] ==
29853f9b0042SHeinrich Schuchardt 					    item->info.controller_handle)
29863f9b0042SHeinrich Schuchardt 						duplicate = true;
29873f9b0042SHeinrich Schuchardt 				}
29883f9b0042SHeinrich Schuchardt 				/* Copy handle to buffer */
29893f9b0042SHeinrich Schuchardt 				if (!duplicate) {
29903f9b0042SHeinrich Schuchardt 					i = (*number_of_children)++;
29913f9b0042SHeinrich Schuchardt 					(*child_handle_buffer)[i] =
29923f9b0042SHeinrich Schuchardt 						item->info.controller_handle;
29933f9b0042SHeinrich Schuchardt 				}
29943f9b0042SHeinrich Schuchardt 			}
29953f9b0042SHeinrich Schuchardt 		}
29963f9b0042SHeinrich Schuchardt 	}
29973f9b0042SHeinrich Schuchardt 	return EFI_SUCCESS;
29983f9b0042SHeinrich Schuchardt }
29993f9b0042SHeinrich Schuchardt 
30006b03cd10SHeinrich Schuchardt /**
300178a88f79SMario Six  * efi_disconnect_controller() - disconnect a controller from a driver
30026b03cd10SHeinrich Schuchardt  * @controller_handle:   handle of the controller
30036b03cd10SHeinrich Schuchardt  * @driver_image_handle: handle of the driver
30046b03cd10SHeinrich Schuchardt  * @child_handle:        handle of the child to destroy
300578a88f79SMario Six  *
300678a88f79SMario Six  * This function implements the DisconnectController service.
300778a88f79SMario Six  *
300878a88f79SMario Six  * See the Unified Extensible Firmware Interface (UEFI) specification for
300978a88f79SMario Six  * details.
301078a88f79SMario Six  *
301178a88f79SMario Six  * Return: status code
30123f9b0042SHeinrich Schuchardt  */
30133f9b0042SHeinrich Schuchardt static efi_status_t EFIAPI efi_disconnect_controller(
30143f9b0042SHeinrich Schuchardt 				efi_handle_t controller_handle,
30153f9b0042SHeinrich Schuchardt 				efi_handle_t driver_image_handle,
30163f9b0042SHeinrich Schuchardt 				efi_handle_t child_handle)
30173f9b0042SHeinrich Schuchardt {
30183f9b0042SHeinrich Schuchardt 	struct efi_driver_binding_protocol *binding_protocol;
30193f9b0042SHeinrich Schuchardt 	efi_handle_t *child_handle_buffer = NULL;
30203f9b0042SHeinrich Schuchardt 	size_t number_of_children = 0;
30213f9b0042SHeinrich Schuchardt 	efi_status_t r;
30223f9b0042SHeinrich Schuchardt 	size_t stop_count = 0;
30233f9b0042SHeinrich Schuchardt 	struct efi_object *efiobj;
30243f9b0042SHeinrich Schuchardt 
30253f9b0042SHeinrich Schuchardt 	EFI_ENTRY("%p, %p, %p", controller_handle, driver_image_handle,
30263f9b0042SHeinrich Schuchardt 		  child_handle);
30273f9b0042SHeinrich Schuchardt 
30283f9b0042SHeinrich Schuchardt 	efiobj = efi_search_obj(controller_handle);
30293f9b0042SHeinrich Schuchardt 	if (!efiobj) {
30303f9b0042SHeinrich Schuchardt 		r = EFI_INVALID_PARAMETER;
30313f9b0042SHeinrich Schuchardt 		goto out;
30323f9b0042SHeinrich Schuchardt 	}
30333f9b0042SHeinrich Schuchardt 
30343f9b0042SHeinrich Schuchardt 	if (child_handle && !efi_search_obj(child_handle)) {
30353f9b0042SHeinrich Schuchardt 		r = EFI_INVALID_PARAMETER;
30363f9b0042SHeinrich Schuchardt 		goto out;
30373f9b0042SHeinrich Schuchardt 	}
30383f9b0042SHeinrich Schuchardt 
30393f9b0042SHeinrich Schuchardt 	/* If no driver handle is supplied, disconnect all drivers */
30403f9b0042SHeinrich Schuchardt 	if (!driver_image_handle) {
30413f9b0042SHeinrich Schuchardt 		r = efi_disconnect_all_drivers(efiobj, NULL, child_handle);
30423f9b0042SHeinrich Schuchardt 		goto out;
30433f9b0042SHeinrich Schuchardt 	}
30443f9b0042SHeinrich Schuchardt 
30453f9b0042SHeinrich Schuchardt 	/* Create list of child handles */
30463f9b0042SHeinrich Schuchardt 	if (child_handle) {
30473f9b0042SHeinrich Schuchardt 		number_of_children = 1;
30483f9b0042SHeinrich Schuchardt 		child_handle_buffer = &child_handle;
30493f9b0042SHeinrich Schuchardt 	} else {
30503f9b0042SHeinrich Schuchardt 		efi_get_child_controllers(efiobj,
30513f9b0042SHeinrich Schuchardt 					  driver_image_handle,
30523f9b0042SHeinrich Schuchardt 					  &number_of_children,
30533f9b0042SHeinrich Schuchardt 					  &child_handle_buffer);
30543f9b0042SHeinrich Schuchardt 	}
30553f9b0042SHeinrich Schuchardt 
30563f9b0042SHeinrich Schuchardt 	/* Get the driver binding protocol */
30573f9b0042SHeinrich Schuchardt 	r = EFI_CALL(efi_open_protocol(driver_image_handle,
30583f9b0042SHeinrich Schuchardt 				       &efi_guid_driver_binding_protocol,
30593f9b0042SHeinrich Schuchardt 				       (void **)&binding_protocol,
30603f9b0042SHeinrich Schuchardt 				       driver_image_handle, NULL,
30613f9b0042SHeinrich Schuchardt 				       EFI_OPEN_PROTOCOL_GET_PROTOCOL));
30623f9b0042SHeinrich Schuchardt 	if (r != EFI_SUCCESS)
30633f9b0042SHeinrich Schuchardt 		goto out;
30643f9b0042SHeinrich Schuchardt 	/* Remove the children */
30653f9b0042SHeinrich Schuchardt 	if (number_of_children) {
30663f9b0042SHeinrich Schuchardt 		r = EFI_CALL(binding_protocol->stop(binding_protocol,
30673f9b0042SHeinrich Schuchardt 						    controller_handle,
30683f9b0042SHeinrich Schuchardt 						    number_of_children,
30693f9b0042SHeinrich Schuchardt 						    child_handle_buffer));
30703f9b0042SHeinrich Schuchardt 		if (r == EFI_SUCCESS)
30713f9b0042SHeinrich Schuchardt 			++stop_count;
30723f9b0042SHeinrich Schuchardt 	}
30733f9b0042SHeinrich Schuchardt 	/* Remove the driver */
30743f9b0042SHeinrich Schuchardt 	if (!child_handle)
30753f9b0042SHeinrich Schuchardt 		r = EFI_CALL(binding_protocol->stop(binding_protocol,
30763f9b0042SHeinrich Schuchardt 						    controller_handle,
30773f9b0042SHeinrich Schuchardt 						    0, NULL));
30783f9b0042SHeinrich Schuchardt 	if (r == EFI_SUCCESS)
30793f9b0042SHeinrich Schuchardt 		++stop_count;
30803f9b0042SHeinrich Schuchardt 	EFI_CALL(efi_close_protocol(driver_image_handle,
30813f9b0042SHeinrich Schuchardt 				    &efi_guid_driver_binding_protocol,
30823f9b0042SHeinrich Schuchardt 				    driver_image_handle, NULL));
30833f9b0042SHeinrich Schuchardt 
30843f9b0042SHeinrich Schuchardt 	if (stop_count)
30853f9b0042SHeinrich Schuchardt 		r = EFI_SUCCESS;
30863f9b0042SHeinrich Schuchardt 	else
30873f9b0042SHeinrich Schuchardt 		r = EFI_NOT_FOUND;
30883f9b0042SHeinrich Schuchardt out:
30893f9b0042SHeinrich Schuchardt 	if (!child_handle)
30903f9b0042SHeinrich Schuchardt 		free(child_handle_buffer);
30913f9b0042SHeinrich Schuchardt 	return EFI_EXIT(r);
30923f9b0042SHeinrich Schuchardt }
30933f9b0042SHeinrich Schuchardt 
3094640adadfSHeinrich Schuchardt static struct efi_boot_services efi_boot_services = {
3095bee91169SAlexander Graf 	.hdr = {
3096112f2430SHeinrich Schuchardt 		.signature = EFI_BOOT_SERVICES_SIGNATURE,
3097112f2430SHeinrich Schuchardt 		.revision = EFI_SPECIFICATION_VERSION,
309871c846abSHeinrich Schuchardt 		.headersize = sizeof(struct efi_boot_services),
3099bee91169SAlexander Graf 	},
3100bee91169SAlexander Graf 	.raise_tpl = efi_raise_tpl,
3101bee91169SAlexander Graf 	.restore_tpl = efi_restore_tpl,
3102bee91169SAlexander Graf 	.allocate_pages = efi_allocate_pages_ext,
3103bee91169SAlexander Graf 	.free_pages = efi_free_pages_ext,
3104bee91169SAlexander Graf 	.get_memory_map = efi_get_memory_map_ext,
3105ead1274bSStefan Brüns 	.allocate_pool = efi_allocate_pool_ext,
310642417bc8SStefan Brüns 	.free_pool = efi_free_pool_ext,
310749deb455Sxypron.glpk@gmx.de 	.create_event = efi_create_event_ext,
3108bfc72462Sxypron.glpk@gmx.de 	.set_timer = efi_set_timer_ext,
3109bee91169SAlexander Graf 	.wait_for_event = efi_wait_for_event,
3110c6841592Sxypron.glpk@gmx.de 	.signal_event = efi_signal_event_ext,
3111bee91169SAlexander Graf 	.close_event = efi_close_event,
3112bee91169SAlexander Graf 	.check_event = efi_check_event,
31131760ef57SHeinrich Schuchardt 	.install_protocol_interface = efi_install_protocol_interface,
3114bee91169SAlexander Graf 	.reinstall_protocol_interface = efi_reinstall_protocol_interface,
3115cd534083SHeinrich Schuchardt 	.uninstall_protocol_interface = efi_uninstall_protocol_interface,
3116bee91169SAlexander Graf 	.handle_protocol = efi_handle_protocol,
3117bee91169SAlexander Graf 	.reserved = NULL,
3118bee91169SAlexander Graf 	.register_protocol_notify = efi_register_protocol_notify,
311926329584Sxypron.glpk@gmx.de 	.locate_handle = efi_locate_handle_ext,
3120bee91169SAlexander Graf 	.locate_device_path = efi_locate_device_path,
3121488bf12dSAlexander Graf 	.install_configuration_table = efi_install_configuration_table_ext,
3122bee91169SAlexander Graf 	.load_image = efi_load_image,
3123bee91169SAlexander Graf 	.start_image = efi_start_image,
3124a86aeaf2SAlexander Graf 	.exit = efi_exit,
3125bee91169SAlexander Graf 	.unload_image = efi_unload_image,
3126bee91169SAlexander Graf 	.exit_boot_services = efi_exit_boot_services,
3127bee91169SAlexander Graf 	.get_next_monotonic_count = efi_get_next_monotonic_count,
3128bee91169SAlexander Graf 	.stall = efi_stall,
3129bee91169SAlexander Graf 	.set_watchdog_timer = efi_set_watchdog_timer,
3130bee91169SAlexander Graf 	.connect_controller = efi_connect_controller,
3131bee91169SAlexander Graf 	.disconnect_controller = efi_disconnect_controller,
3132bee91169SAlexander Graf 	.open_protocol = efi_open_protocol,
3133bee91169SAlexander Graf 	.close_protocol = efi_close_protocol,
3134bee91169SAlexander Graf 	.open_protocol_information = efi_open_protocol_information,
3135bee91169SAlexander Graf 	.protocols_per_handle = efi_protocols_per_handle,
3136bee91169SAlexander Graf 	.locate_handle_buffer = efi_locate_handle_buffer,
3137bee91169SAlexander Graf 	.locate_protocol = efi_locate_protocol,
3138ab9efa97SHeinrich Schuchardt 	.install_multiple_protocol_interfaces =
3139ab9efa97SHeinrich Schuchardt 			efi_install_multiple_protocol_interfaces,
3140ab9efa97SHeinrich Schuchardt 	.uninstall_multiple_protocol_interfaces =
3141ab9efa97SHeinrich Schuchardt 			efi_uninstall_multiple_protocol_interfaces,
3142bee91169SAlexander Graf 	.calculate_crc32 = efi_calculate_crc32,
3143bee91169SAlexander Graf 	.copy_mem = efi_copy_mem,
3144bee91169SAlexander Graf 	.set_mem = efi_set_mem,
31459f0930e5SHeinrich Schuchardt 	.create_event_ex = efi_create_event_ex,
3146bee91169SAlexander Graf };
3147bee91169SAlexander Graf 
31480b386537SHeinrich Schuchardt static u16 __efi_runtime_data firmware_vendor[] = L"Das U-Boot";
3149bee91169SAlexander Graf 
31503c63db9cSAlexander Graf struct efi_system_table __efi_runtime_data systab = {
3151bee91169SAlexander Graf 	.hdr = {
3152bee91169SAlexander Graf 		.signature = EFI_SYSTEM_TABLE_SIGNATURE,
3153112f2430SHeinrich Schuchardt 		.revision = EFI_SPECIFICATION_VERSION,
315471c846abSHeinrich Schuchardt 		.headersize = sizeof(struct efi_system_table),
3155bee91169SAlexander Graf 	},
31560b386537SHeinrich Schuchardt 	.fw_vendor = firmware_vendor,
31570b386537SHeinrich Schuchardt 	.fw_revision = FW_VERSION << 16 | FW_PATCHLEVEL << 8,
3158bee91169SAlexander Graf 	.con_in = (void *)&efi_con_in,
3159bee91169SAlexander Graf 	.con_out = (void *)&efi_con_out,
3160bee91169SAlexander Graf 	.std_err = (void *)&efi_con_out,
3161bee91169SAlexander Graf 	.runtime = (void *)&efi_runtime_services,
3162bee91169SAlexander Graf 	.boottime = (void *)&efi_boot_services,
3163bee91169SAlexander Graf 	.nr_tables = 0,
31644182a129SHeinrich Schuchardt 	.tables = NULL,
3165bee91169SAlexander Graf };
3166640adadfSHeinrich Schuchardt 
3167640adadfSHeinrich Schuchardt /**
3168640adadfSHeinrich Schuchardt  * efi_initialize_system_table() - Initialize system table
3169640adadfSHeinrich Schuchardt  *
31700414359aSHeinrich Schuchardt  * Return:	status code
3171640adadfSHeinrich Schuchardt  */
3172640adadfSHeinrich Schuchardt efi_status_t efi_initialize_system_table(void)
3173640adadfSHeinrich Schuchardt {
31744182a129SHeinrich Schuchardt 	efi_status_t ret;
31754182a129SHeinrich Schuchardt 
31764182a129SHeinrich Schuchardt 	/* Allocate configuration table array */
31774182a129SHeinrich Schuchardt 	ret = efi_allocate_pool(EFI_RUNTIME_SERVICES_DATA,
31784182a129SHeinrich Schuchardt 				EFI_MAX_CONFIGURATION_TABLES *
31794182a129SHeinrich Schuchardt 				sizeof(struct efi_configuration_table),
31804182a129SHeinrich Schuchardt 				(void **)&systab.tables);
31814182a129SHeinrich Schuchardt 
3182b72aaa87SHeinrich Schuchardt 	/* Set CRC32 field in table headers */
3183640adadfSHeinrich Schuchardt 	efi_update_table_header_crc32(&systab.hdr);
3184640adadfSHeinrich Schuchardt 	efi_update_table_header_crc32(&efi_runtime_services.hdr);
3185640adadfSHeinrich Schuchardt 	efi_update_table_header_crc32(&efi_boot_services.hdr);
31864182a129SHeinrich Schuchardt 
31874182a129SHeinrich Schuchardt 	return ret;
3188640adadfSHeinrich Schuchardt }
3189