/* List of all events */
LIST_HEAD(efi_events);
+/*
+ * If we're running on nasty systems (32bit ARM booting into non-EFI Linux)
+ * we need to do trickery with caches. Since we don't want to break the EFI
+ * aware boot path, only apply hacks when loading exiting directly (breaking
+ * direct Linux EFI booting along the way - oh well).
+ */
+static bool efi_is_direct_boot = true;
+
#ifdef CONFIG_ARM
/*
* The "gd" pointer lives in a register on ARM and AArch64 that we declare
*
* The protocols list is initialized. The object handle is set.
*/
-void efi_add_handle(struct efi_object *obj)
+void efi_add_handle(efi_handle_t handle)
{
- if (!obj)
+ if (!handle)
return;
- INIT_LIST_HEAD(&obj->protocols);
- obj->handle = obj;
- list_add_tail(&obj->link, &efi_obj_list);
+ INIT_LIST_HEAD(&handle->protocols);
+ list_add_tail(&handle->link, &efi_obj_list);
}
/**
return EFI_OUT_OF_RESOURCES;
efi_add_handle(obj);
- *handle = obj->handle;
+ *handle = obj;
return EFI_SUCCESS;
}
*
* @obj: handle to delete
*/
-void efi_delete_handle(struct efi_object *obj)
+void efi_delete_handle(efi_handle_t handle)
{
- if (!obj)
+ if (!handle)
return;
- efi_remove_all_protocols(obj->handle);
- list_del(&obj->link);
- free(obj);
+ efi_remove_all_protocols(handle);
+ list_del(&handle->link);
+ free(handle);
}
/**
struct efi_object *efiobj;
list_for_each_entry(efiobj, &efi_obj_list, link) {
- if (efiobj->handle == handle)
+ if (efiobj == handle)
return efiobj;
}
* Return: status code
*/
static efi_status_t EFIAPI efi_install_protocol_interface(
- void **handle, const efi_guid_t *protocol,
+ efi_handle_t *handle, const efi_guid_t *protocol,
int protocol_interface_type, void *protocol_interface)
{
efi_status_t r;
/**
* efi_get_drivers() - get all drivers associated to a controller
- * @efiobj: handle of the controller
+ * @handle: handle of the controller
* @protocol: protocol GUID (optional)
* @number_of_drivers: number of child controllers
* @driver_handle_buffer: handles of the the drivers
*
* Return: status code
*/
-static efi_status_t efi_get_drivers(struct efi_object *efiobj,
+static efi_status_t efi_get_drivers(efi_handle_t handle,
const efi_guid_t *protocol,
efi_uintn_t *number_of_drivers,
efi_handle_t **driver_handle_buffer)
bool duplicate;
/* Count all driver associations */
- list_for_each_entry(handler, &efiobj->protocols, link) {
+ list_for_each_entry(handler, &handle->protocols, link) {
if (protocol && guidcmp(handler->guid, protocol))
continue;
list_for_each_entry(item, &handler->open_infos, link) {
if (!*driver_handle_buffer)
return EFI_OUT_OF_RESOURCES;
/* Collect unique driver handles */
- list_for_each_entry(handler, &efiobj->protocols, link) {
+ list_for_each_entry(handler, &handle->protocols, link) {
if (protocol && guidcmp(handler->guid, protocol))
continue;
list_for_each_entry(item, &handler->open_infos, link) {
/**
* efi_disconnect_all_drivers() - disconnect all drivers from a controller
- * @efiobj: handle of the controller
+ * @handle: handle of the controller
* @protocol: protocol GUID (optional)
* @child_handle: handle of the child to destroy
*
*
* Return: status code
*/
-static efi_status_t efi_disconnect_all_drivers(
- struct efi_object *efiobj,
- const efi_guid_t *protocol,
- efi_handle_t child_handle)
+static efi_status_t efi_disconnect_all_drivers
+ (efi_handle_t handle,
+ const efi_guid_t *protocol,
+ efi_handle_t child_handle)
{
efi_uintn_t number_of_drivers;
efi_handle_t *driver_handle_buffer;
efi_status_t r, ret;
- ret = efi_get_drivers(efiobj, protocol, &number_of_drivers,
+ ret = efi_get_drivers(handle, protocol, &number_of_drivers,
&driver_handle_buffer);
if (ret != EFI_SUCCESS)
return ret;
ret = EFI_NOT_FOUND;
while (number_of_drivers) {
r = EFI_CALL(efi_disconnect_controller(
- efiobj->handle,
+ handle,
driver_handle_buffer[--number_of_drivers],
child_handle));
if (r == EFI_SUCCESS)
}
/**
- * efi_uninstall_protocol_interface() - uninstall protocol interface
+ * efi_uninstall_protocol() - uninstall protocol interface
+ *
* @handle: handle from which the protocol shall be removed
* @protocol: GUID of the protocol to be removed
* @protocol_interface: interface to be removed
*
- * This function implements the UninstallProtocolInterface service.
- *
- * See the Unified Extensible Firmware Interface (UEFI) specification for
- * details.
+ * This function DOES NOT delete a handle without installed protocol.
*
* Return: status code
*/
-static efi_status_t EFIAPI efi_uninstall_protocol_interface(
- efi_handle_t handle, const efi_guid_t *protocol,
- void *protocol_interface)
+static efi_status_t efi_uninstall_protocol
+ (efi_handle_t handle, const efi_guid_t *protocol,
+ void *protocol_interface)
{
struct efi_object *efiobj;
struct efi_handler *handler;
struct efi_open_protocol_info_item *pos;
efi_status_t r;
- EFI_ENTRY("%p, %pUl, %p", handle, protocol, protocol_interface);
-
/* Check handle */
efiobj = efi_search_obj(handle);
if (!efiobj) {
}
r = efi_remove_protocol(handle, protocol, protocol_interface);
out:
- return EFI_EXIT(r);
+ return r;
+}
+
+/**
+ * efi_uninstall_protocol_interface() - uninstall protocol interface
+ * @handle: handle from which the protocol shall be removed
+ * @protocol: GUID of the protocol to be removed
+ * @protocol_interface: interface to be removed
+ *
+ * This function implements the UninstallProtocolInterface service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_uninstall_protocol_interface
+ (efi_handle_t handle, const efi_guid_t *protocol,
+ void *protocol_interface)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%p, %pUl, %p", handle, protocol, protocol_interface);
+
+ ret = efi_uninstall_protocol(handle, protocol, protocol_interface);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /* If the last protocol has been removed, delete the handle. */
+ if (list_empty(&handle->protocols)) {
+ list_del(&handle->link);
+ free(handle);
+ }
+out:
+ return EFI_EXIT(ret);
}
/**
* @search_type: selection criterion
* @protocol: GUID of the protocol
* @search_key: registration key
- * @efiobj: handle
+ * @handle: handle
*
* See the documentation of the LocateHandle service in the UEFI specification.
*
*/
static int efi_search(enum efi_locate_search_type search_type,
const efi_guid_t *protocol, void *search_key,
- struct efi_object *efiobj)
+ efi_handle_t handle)
{
efi_status_t ret;
/* TODO: RegisterProtocolNotify is not implemented yet */
return -1;
case BY_PROTOCOL:
- ret = efi_search_protocol(efiobj->handle, protocol, NULL);
+ ret = efi_search_protocol(handle, protocol, NULL);
return (ret != EFI_SUCCESS);
default:
/* Invalid search type */
/* Then fill the array */
list_for_each_entry(efiobj, &efi_obj_list, link) {
if (!efi_search(search_type, protocol, search_key, efiobj))
- *buffer++ = efiobj->handle;
+ *buffer++ = efiobj;
}
return EFI_SUCCESS;
*
* Return: status code
*/
-efi_status_t efi_setup_loaded_image(
- struct efi_loaded_image *info, struct efi_object *obj,
- struct efi_device_path *device_path,
- struct efi_device_path *file_path)
+efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path,
+ struct efi_device_path *file_path,
+ struct efi_loaded_image_obj **handle_ptr,
+ struct efi_loaded_image **info_ptr)
{
efi_status_t ret;
+ struct efi_loaded_image *info;
+ struct efi_loaded_image_obj *obj;
+
+ info = calloc(1, sizeof(*info));
+ if (!info)
+ return EFI_OUT_OF_RESOURCES;
+ obj = calloc(1, sizeof(*obj));
+ if (!obj) {
+ free(info);
+ return EFI_OUT_OF_RESOURCES;
+ }
/* Add internal object to object list */
- efi_add_handle(obj);
- /* efi_exit() assumes that the handle points to the info */
- obj->handle = info;
+ efi_add_handle(&obj->header);
+
+ if (info_ptr)
+ *info_ptr = info;
+ if (handle_ptr)
+ *handle_ptr = obj;
info->revision = EFI_LOADED_IMAGE_PROTOCOL_REVISION;
info->file_path = file_path;
* When asking for the device path interface, return
* bootefi_device_path
*/
- ret = efi_add_protocol(obj->handle, &efi_guid_device_path,
- device_path);
+ ret = efi_add_protocol(&obj->header,
+ &efi_guid_device_path, device_path);
if (ret != EFI_SUCCESS)
goto failure;
}
* When asking for the loaded_image interface, just
* return handle which points to loaded_image_info
*/
- ret = efi_add_protocol(obj->handle, &efi_guid_loaded_image, info);
+ ret = efi_add_protocol(&obj->header,
+ &efi_guid_loaded_image, info);
if (ret != EFI_SUCCESS)
goto failure;
efi_uintn_t source_size,
efi_handle_t *image_handle)
{
- struct efi_loaded_image *info;
- struct efi_object *obj;
+ struct efi_loaded_image *info = NULL;
+ struct efi_loaded_image_obj **image_obj =
+ (struct efi_loaded_image_obj **)image_handle;
efi_status_t ret;
EFI_ENTRY("%d, %p, %pD, %p, %zd, %p", boot_policy, parent_image,
goto error;
}
- info = calloc(1, sizeof(*info));
- if (!info) {
- ret = EFI_OUT_OF_RESOURCES;
- goto error;
- }
- obj = calloc(1, sizeof(*obj));
- if (!obj) {
- free(info);
- ret = EFI_OUT_OF_RESOURCES;
- goto error;
- }
-
if (!source_buffer) {
struct efi_device_path *dp, *fp;
* file parts:
*/
efi_dp_split_file_path(file_path, &dp, &fp);
- ret = efi_setup_loaded_image(info, obj, dp, fp);
+ ret = efi_setup_loaded_image(dp, fp, image_obj, &info);
if (ret != EFI_SUCCESS)
goto failure;
} else {
/* In this case, file_path is the "device" path, i.e.
* something like a HARDWARE_DEVICE:MEMORY_MAPPED
*/
- ret = efi_setup_loaded_image(info, obj, file_path, NULL);
+ ret = efi_setup_loaded_image(file_path, NULL, image_obj, &info);
if (ret != EFI_SUCCESS)
- goto failure;
+ goto error;
}
- info->reserved = efi_load_pe(source_buffer, info);
- if (!info->reserved) {
+ (*image_obj)->entry = efi_load_pe(*image_obj, source_buffer, info);
+ if (!(*image_obj)->entry) {
ret = EFI_UNSUPPORTED;
goto failure;
}
info->system_table = &systab;
info->parent_handle = parent_image;
- *image_handle = obj->handle;
return EFI_EXIT(EFI_SUCCESS);
failure:
+ efi_delete_handle(*image_handle);
+ *image_handle = NULL;
free(info);
- efi_delete_handle(obj);
error:
return EFI_EXIT(ret);
}
unsigned long *exit_data_size,
s16 **exit_data)
{
- EFIAPI efi_status_t (*entry)(efi_handle_t image_handle,
- struct efi_system_table *st);
- struct efi_loaded_image *info = image_handle;
+ struct efi_loaded_image_obj *image_obj =
+ (struct efi_loaded_image_obj *)image_handle;
efi_status_t ret;
EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data);
- entry = info->reserved;
+
+ efi_is_direct_boot = false;
/* call the image! */
- if (setjmp(&info->exit_jmp)) {
+ if (setjmp(&image_obj->exit_jmp)) {
/*
* We called the entry point of the child image with EFI_CALL
* in the lines below. The child image called the Exit() boot
assert(__efi_entry_check());
debug("%sEFI: %lu returned by started image\n",
__efi_nesting_dec(),
- (unsigned long)((uintptr_t)info->exit_status &
+ (unsigned long)((uintptr_t)image_obj->exit_status &
~EFI_ERROR_MASK));
- return EFI_EXIT(info->exit_status);
+ return EFI_EXIT(image_obj->exit_status);
}
- ret = EFI_CALL(entry(image_handle, &systab));
+ ret = EFI_CALL(image_obj->entry(image_handle, &systab));
/*
* Usually UEFI applications call Exit() instead of returning.
int16_t *exit_data)
{
/*
- * We require that the handle points to the original loaded
- * image protocol interface.
- *
- * For getting the longjmp address this is safer than locating
- * the protocol because the protocol may have been reinstalled
- * pointing to another memory location.
- *
* TODO: We should call the unload procedure of the loaded
* image protocol.
*/
- struct efi_loaded_image *loaded_image_info = (void *)image_handle;
+ struct efi_loaded_image_obj *image_obj =
+ (struct efi_loaded_image_obj *)image_handle;
EFI_ENTRY("%p, %ld, %ld, %p", image_handle, exit_status,
exit_data_size, exit_data);
*/
efi_restore_gd();
- loaded_image_info->exit_status = exit_status;
- longjmp(&loaded_image_info->exit_jmp, 1);
+ image_obj->exit_status = exit_status;
+ longjmp(&image_obj->exit_jmp, 1);
panic("EFI application exited");
}
}
/**
+ * efi_exit_caches() - fix up caches for EFI payloads if necessary
+ */
+static void efi_exit_caches(void)
+{
+#if defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
+ /*
+ * Grub on 32bit ARM needs to have caches disabled before jumping into
+ * a zImage, but does not know of all cache layers. Give it a hand.
+ */
+ if (efi_is_direct_boot)
+ cleanup_before_linux();
+#endif
+}
+
+/**
* efi_exit_boot_services() - stop all boot services
* @image_handle: handle of the loaded image
* @map_key: key of the memory map
board_quiesce_devices();
+ /* Fix up caches for EFI payloads if necessary */
+ efi_exit_caches();
+
/* This stops all lingering devices */
bootm_disable_interrupts();
/* Copy entries */
buffer_size = count * sizeof(struct efi_open_protocol_info_entry);
- r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, buffer_size,
+ r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
(void **)entry_buffer);
if (r != EFI_SUCCESS)
goto out;
size_t j = 0;
buffer_size = sizeof(efi_guid_t *) * *protocol_buffer_count;
- r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, buffer_size,
+ r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
(void **)protocol_buffer);
if (r != EFI_SUCCESS)
return EFI_EXIT(r);
*buffer);
if (r != EFI_BUFFER_TOO_SMALL)
goto out;
- r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, buffer_size,
+ r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
(void **)buffer);
if (r != EFI_SUCCESS)
goto out;
efiobj = list_entry(lhandle, struct efi_object, link);
- ret = efi_search_protocol(efiobj->handle, protocol, &handler);
+ ret = efi_search_protocol(efiobj, protocol, &handler);
if (ret == EFI_SUCCESS) {
*protocol_interface = handler->protocol_interface;
return EFI_EXIT(EFI_SUCCESS);
*
* Return: status code
*/
-static efi_status_t EFIAPI efi_install_multiple_protocol_interfaces(
- void **handle, ...)
+static efi_status_t EFIAPI efi_install_multiple_protocol_interfaces
+ (efi_handle_t *handle, ...)
{
EFI_ENTRY("%p", handle);
for (; i; --i) {
protocol = efi_va_arg(argptr, efi_guid_t*);
protocol_interface = efi_va_arg(argptr, void*);
- EFI_CALL(efi_uninstall_protocol_interface(handle, protocol,
+ EFI_CALL(efi_uninstall_protocol_interface(*handle, protocol,
protocol_interface));
}
efi_va_end(argptr);
* Return: status code
*/
static efi_status_t EFIAPI efi_uninstall_multiple_protocol_interfaces(
- void *handle, ...)
+ efi_handle_t handle, ...)
{
EFI_ENTRY("%p", handle);
if (!protocol)
break;
protocol_interface = efi_va_arg(argptr, void*);
- r = EFI_CALL(efi_uninstall_protocol_interface(
- handle, protocol,
- protocol_interface));
+ r = efi_uninstall_protocol(handle, protocol,
+ protocol_interface);
if (r != EFI_SUCCESS)
break;
i++;
}
efi_va_end(argptr);
- if (r == EFI_SUCCESS)
+ if (r == EFI_SUCCESS) {
+ /* If the last protocol has been removed, delete the handle. */
+ if (list_empty(&handle->protocols)) {
+ list_del(&handle->link);
+ free(handle);
+ }
return EFI_EXIT(r);
+ }
/* If an error occurred undo all changes. */
efi_va_start(argptr, handle);
}
efi_va_end(argptr);
- return EFI_EXIT(r);
+ /* In case of an error always return EFI_INVALID_PARAMETER */
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
}
/**
if (item->info.attributes & EFI_OPEN_PROTOCOL_BY_DRIVER)
opened_by_driver = true;
}
- /* Only one controller can be conncected */
+ /* Only one controller can be connected */
if (opened_by_driver)
return EFI_ACCESS_DENIED;
}
*
* Return: status code
*/
-static efi_status_t EFIAPI efi_open_protocol(
- void *handle, const efi_guid_t *protocol,
- void **protocol_interface, void *agent_handle,
- void *controller_handle, uint32_t attributes)
+static efi_status_t EFIAPI efi_open_protocol
+ (efi_handle_t handle, const efi_guid_t *protocol,
+ void **protocol_interface, efi_handle_t agent_handle,
+ efi_handle_t controller_handle, uint32_t attributes)
{
struct efi_handler *handler;
efi_status_t r = EFI_INVALID_PARAMETER;
EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, old_interface,
new_interface);
- ret = EFI_CALL(efi_uninstall_protocol_interface(handle, protocol,
- old_interface));
+
+ /* Uninstall protocol but do not delete handle */
+ ret = efi_uninstall_protocol(handle, protocol, old_interface);
if (ret != EFI_SUCCESS)
goto out;
- ret = EFI_CALL(efi_install_protocol_interface(&handle, protocol,
- EFI_NATIVE_INTERFACE,
- new_interface));
+
+ /* Install the new protocol */
+ ret = efi_add_protocol(handle, protocol, new_interface);
+ /*
+ * The UEFI spec does not specify what should happen to the handle
+ * if in case of an error no protocol interface remains on the handle.
+ * So let's do nothing here.
+ */
if (ret != EFI_SUCCESS)
goto out;
/*