/* List of all events */
LIST_HEAD(efi_events);
+/* Handle of the currently executing image */
+static efi_handle_t current_image;
+
/*
* 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
}
/**
- * efi_add_handle() - add a new object to the object list
- * @obj: object to be added
+ * efi_add_handle() - add a new handle to the object list
+ *
+ * @handle: handle to be added
*
- * The protocols list is initialized. The object handle is set.
+ * The protocols list is initialized. The handle is added to the list of known
+ * UEFI objects.
*/
void efi_add_handle(efi_handle_t handle)
{
}
if ((type & (EVT_NOTIFY_WAIT | EVT_NOTIFY_SIGNAL)) &&
- (is_valid_tpl(notify_tpl) != EFI_SUCCESS))
+ (!notify_function || is_valid_tpl(notify_tpl) != EFI_SUCCESS))
return EFI_INVALID_PARAMETER;
evt = calloc(1, sizeof(struct efi_event));
efi_status_t ret;
struct efi_loaded_image *info = NULL;
struct efi_loaded_image_obj *obj = NULL;
+ struct efi_device_path *dp;
/* In case of EFI_OUT_OF_RESOURCES avoid illegal free by caller. */
*handle_ptr = NULL;
if (device_path) {
info->device_handle = efi_dp_find_obj(device_path, NULL);
- /*
- * When asking for the device path interface, return
- * bootefi_device_path
- */
- ret = efi_add_protocol(&obj->header,
- &efi_guid_device_path, device_path);
- if (ret != EFI_SUCCESS)
+
+ dp = efi_dp_append(device_path, file_path);
+ if (!dp) {
+ ret = EFI_OUT_OF_RESOURCES;
goto failure;
+ }
+ } else {
+ dp = NULL;
}
+ ret = efi_add_protocol(&obj->header,
+ &efi_guid_loaded_image_device_path, dp);
+ if (ret != EFI_SUCCESS)
+ goto failure;
/*
* When asking for the loaded_image interface, just
if (ret != EFI_SUCCESS)
goto failure;
-#if CONFIG_IS_ENABLED(EFI_LOADER_HII)
- ret = efi_add_protocol(&obj->header,
- &efi_guid_hii_string_protocol,
- (void *)&efi_hii_string);
- if (ret != EFI_SUCCESS)
- goto failure;
-
- ret = efi_add_protocol(&obj->header,
- &efi_guid_hii_database_protocol,
- (void *)&efi_hii_database);
- if (ret != EFI_SUCCESS)
- goto failure;
-
- ret = efi_add_protocol(&obj->header,
- &efi_guid_hii_config_routing_protocol,
- (void *)&efi_hii_config_routing);
- if (ret != EFI_SUCCESS)
- goto failure;
-#endif
-
*info_ptr = info;
*handle_ptr = obj;
* @size: size of the loaded image
* Return: status code
*/
+static
efi_status_t efi_load_image_from_path(struct efi_device_path *file_path,
void **buffer, efi_uintn_t *size)
{
*
* Return: status code
*/
-static efi_status_t EFIAPI efi_load_image(bool boot_policy,
- efi_handle_t parent_image,
- struct efi_device_path *file_path,
- void *source_buffer,
- efi_uintn_t source_size,
- efi_handle_t *image_handle)
+efi_status_t EFIAPI efi_load_image(bool boot_policy,
+ efi_handle_t parent_image,
+ struct efi_device_path *file_path,
+ void *source_buffer,
+ efi_uintn_t source_size,
+ efi_handle_t *image_handle)
{
struct efi_device_path *dp, *fp;
struct efi_loaded_image *info = NULL;
struct efi_loaded_image_obj **image_obj =
(struct efi_loaded_image_obj **)image_handle;
efi_status_t ret;
+ void *dest_buffer;
EFI_ENTRY("%d, %p, %pD, %p, %zd, %p", boot_policy, parent_image,
file_path, source_buffer, source_size, image_handle);
}
if (!source_buffer) {
- ret = efi_load_image_from_path(file_path, &source_buffer,
+ ret = efi_load_image_from_path(file_path, &dest_buffer,
&source_size);
if (ret != EFI_SUCCESS)
goto error;
- /*
- * split file_path which contains both the device and
- * file parts:
- */
- efi_dp_split_file_path(file_path, &dp, &fp);
} else {
- /* In this case, file_path is the "device" path, i.e.
- * something like a HARDWARE_DEVICE:MEMORY_MAPPED
- */
- u64 addr;
- void *dest_buffer;
-
- ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
- EFI_RUNTIME_SERVICES_CODE,
- efi_size_in_pages(source_size), &addr);
- if (ret != EFI_SUCCESS)
- goto error;
- dest_buffer = (void *)(uintptr_t)addr;
- memcpy(dest_buffer, source_buffer, source_size);
- source_buffer = dest_buffer;
-
- dp = file_path;
- fp = NULL;
+ dest_buffer = source_buffer;
}
+ /* split file_path which contains both the device and file parts */
+ efi_dp_split_file_path(file_path, &dp, &fp);
ret = efi_setup_loaded_image(dp, fp, image_obj, &info);
- if (ret != EFI_SUCCESS)
- goto error_invalid_image;
- ret = efi_load_pe(*image_obj, source_buffer, info);
- if (ret != EFI_SUCCESS)
- goto error_invalid_image;
- /* Update the type of the allocated memory */
- efi_add_memory_map((uintptr_t)source_buffer,
- efi_size_in_pages(source_size),
- info->image_code_type, false);
- info->system_table = &systab;
- info->parent_handle = parent_image;
- return EFI_EXIT(EFI_SUCCESS);
-error_invalid_image:
- /* The image is invalid. Release all associated resources. */
- efi_free_pages((uintptr_t)source_buffer,
- efi_size_in_pages(source_size));
- efi_delete_handle(*image_handle);
- *image_handle = NULL;
- free(info);
+ if (ret == EFI_SUCCESS)
+ ret = efi_load_pe(*image_obj, dest_buffer, info);
+ if (!source_buffer)
+ /* Release buffer to which file was loaded */
+ efi_free_pages((uintptr_t)dest_buffer,
+ efi_size_in_pages(source_size));
+ if (ret == EFI_SUCCESS) {
+ info->system_table = &systab;
+ info->parent_handle = parent_image;
+ } else {
+ /* The image is invalid. Release all associated resources. */
+ efi_delete_handle(*image_handle);
+ *image_handle = NULL;
+ free(info);
+ }
error:
return EFI_EXIT(ret);
}
-/**
- * efi_start_image() - call the entry point of an image
- * @image_handle: handle of the image
- * @exit_data_size: size of the buffer
- * @exit_data: buffer to receive the exit data of the called image
- *
- * This function implements the StartImage service.
- *
- * See the Unified Extensible Firmware Interface (UEFI) specification for
- * details.
- *
- * Return: status code
- */
-efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
- efi_uintn_t *exit_data_size,
- u16 **exit_data)
-{
- 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);
-
- efi_is_direct_boot = false;
-
- /* call the image! */
- 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
- * service efi_exit() which executed the long jump that brought
- * us to the current line. This implies that the second half
- * of the EFI_CALL macro has not been executed.
- */
-#ifdef CONFIG_ARM
- /*
- * efi_exit() called efi_restore_gd(). We have to undo this
- * otherwise __efi_entry_check() will put the wrong value into
- * app_gd.
- */
- gd = app_gd;
-#endif
- /*
- * To get ready to call EFI_EXIT below we have to execute the
- * missed out steps of EFI_CALL.
- */
- assert(__efi_entry_check());
- debug("%sEFI: %lu returned by started image\n",
- __efi_nesting_dec(),
- (unsigned long)((uintptr_t)image_obj->exit_status &
- ~EFI_ERROR_MASK));
- return EFI_EXIT(image_obj->exit_status);
- }
-
- ret = EFI_CALL(image_obj->entry(image_handle, &systab));
-
- /*
- * Usually UEFI applications call Exit() instead of returning.
- * But because the world doesn't consist of ponies and unicorns,
- * we're happy to emulate that behavior on behalf of a payload
- * that forgot.
- */
- return EFI_CALL(systab.boottime->exit(image_handle, ret, 0, NULL));
-}
-
-/**
- * efi_exit() - leave an EFI application or driver
- * @image_handle: handle of the application or driver that is exiting
- * @exit_status: status code
- * @exit_data_size: size of the buffer in bytes
- * @exit_data: buffer with data describing an error
- *
- * This function implements the Exit service.
- *
- * See the Unified Extensible Firmware Interface (UEFI) specification for
- * details.
- *
- * Return: status code
- */
-static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
- efi_status_t exit_status,
- efi_uintn_t exit_data_size,
- u16 *exit_data)
-{
- /*
- * TODO: We should call the unload procedure of the loaded
- * image protocol.
- */
- struct efi_loaded_image_obj *image_obj =
- (struct efi_loaded_image_obj *)image_handle;
-
- EFI_ENTRY("%p, %ld, %zu, %p", image_handle, exit_status,
- exit_data_size, exit_data);
-
- /* Make sure entry/exit counts for EFI world cross-overs match */
- EFI_EXIT(exit_status);
-
- /*
- * But longjmp out with the U-Boot gd, not the application's, as
- * the other end is a setjmp call inside EFI context.
- */
- efi_restore_gd();
-
- image_obj->exit_status = exit_status;
- longjmp(&image_obj->exit_jmp, 1);
-
- panic("EFI application exited");
-}
-
/**
* efi_unload_image() - unload an EFI image
* @image_handle: handle of the image to be unloaded
*
* Return: status code
*/
-static efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle)
+efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle)
{
struct efi_object *efiobj;
*
* Return: status code
*/
-static efi_status_t EFIAPI efi_install_multiple_protocol_interfaces
+efi_status_t EFIAPI efi_install_multiple_protocol_interfaces
(efi_handle_t *handle, ...)
{
EFI_ENTRY("%p", handle);
return EFI_EXIT(r);
}
+/**
+ * efi_start_image() - call the entry point of an image
+ * @image_handle: handle of the image
+ * @exit_data_size: size of the buffer
+ * @exit_data: buffer to receive the exit data of the called image
+ *
+ * This function implements the StartImage service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
+ efi_uintn_t *exit_data_size,
+ u16 **exit_data)
+{
+ struct efi_loaded_image_obj *image_obj =
+ (struct efi_loaded_image_obj *)image_handle;
+ efi_status_t ret;
+ void *info;
+ efi_handle_t parent_image = current_image;
+
+ EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data);
+
+ /* Check parameters */
+ ret = EFI_CALL(efi_open_protocol(image_handle, &efi_guid_loaded_image,
+ &info, NULL, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL));
+ if (ret != EFI_SUCCESS)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ efi_is_direct_boot = false;
+
+ image_obj->exit_data_size = exit_data_size;
+ image_obj->exit_data = exit_data;
+
+ /* call the image! */
+ 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
+ * service efi_exit() which executed the long jump that brought
+ * us to the current line. This implies that the second half
+ * of the EFI_CALL macro has not been executed.
+ */
+#ifdef CONFIG_ARM
+ /*
+ * efi_exit() called efi_restore_gd(). We have to undo this
+ * otherwise __efi_entry_check() will put the wrong value into
+ * app_gd.
+ */
+ gd = app_gd;
+#endif
+ /*
+ * To get ready to call EFI_EXIT below we have to execute the
+ * missed out steps of EFI_CALL.
+ */
+ assert(__efi_entry_check());
+ debug("%sEFI: %lu returned by started image\n",
+ __efi_nesting_dec(),
+ (unsigned long)((uintptr_t)image_obj->exit_status &
+ ~EFI_ERROR_MASK));
+ current_image = parent_image;
+ return EFI_EXIT(image_obj->exit_status);
+ }
+
+ current_image = image_handle;
+ EFI_PRINT("Jumping into 0x%p\n", image_obj->entry);
+ ret = EFI_CALL(image_obj->entry(image_handle, &systab));
+
+ /*
+ * Usually UEFI applications call Exit() instead of returning.
+ * But because the world doesn't consist of ponies and unicorns,
+ * we're happy to emulate that behavior on behalf of a payload
+ * that forgot.
+ */
+ return EFI_CALL(systab.boottime->exit(image_handle, ret, 0, NULL));
+}
+
+/**
+ * efi_update_exit_data() - fill exit data parameters of StartImage()
+ *
+ * @image_obj image handle
+ * @exit_data_size size of the exit data buffer
+ * @exit_data buffer with data returned by UEFI payload
+ * Return: status code
+ */
+static efi_status_t efi_update_exit_data(struct efi_loaded_image_obj *image_obj,
+ efi_uintn_t exit_data_size,
+ u16 *exit_data)
+{
+ efi_status_t ret;
+
+ /*
+ * If exit_data is not provided to StartImage(), exit_data_size must be
+ * ignored.
+ */
+ if (!image_obj->exit_data)
+ return EFI_SUCCESS;
+ if (image_obj->exit_data_size)
+ *image_obj->exit_data_size = exit_data_size;
+ if (exit_data_size && exit_data) {
+ ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA,
+ exit_data_size,
+ (void **)image_obj->exit_data);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ memcpy(*image_obj->exit_data, exit_data, exit_data_size);
+ } else {
+ image_obj->exit_data = NULL;
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_exit() - leave an EFI application or driver
+ * @image_handle: handle of the application or driver that is exiting
+ * @exit_status: status code
+ * @exit_data_size: size of the buffer in bytes
+ * @exit_data: buffer with data describing an error
+ *
+ * This function implements the Exit service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
+ efi_status_t exit_status,
+ efi_uintn_t exit_data_size,
+ u16 *exit_data)
+{
+ /*
+ * TODO: We should call the unload procedure of the loaded
+ * image protocol.
+ */
+ efi_status_t ret;
+ void *info;
+ struct efi_loaded_image_obj *image_obj =
+ (struct efi_loaded_image_obj *)image_handle;
+
+ EFI_ENTRY("%p, %ld, %zu, %p", image_handle, exit_status,
+ exit_data_size, exit_data);
+
+ /* Check parameters */
+ if (image_handle != current_image)
+ goto out;
+ ret = EFI_CALL(efi_open_protocol(image_handle, &efi_guid_loaded_image,
+ &info, NULL, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL));
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /* Exit data is only foreseen in case of failure. */
+ if (exit_status != EFI_SUCCESS) {
+ ret = efi_update_exit_data(image_obj, exit_data_size,
+ exit_data);
+ /* Exiting has priority. Don't return error to caller. */
+ if (ret != EFI_SUCCESS)
+ EFI_PRINT("%s: out of memory\n", __func__);
+ }
+
+ /* Make sure entry/exit counts for EFI world cross-overs match */
+ EFI_EXIT(exit_status);
+
+ /*
+ * But longjmp out with the U-Boot gd, not the application's, as
+ * the other end is a setjmp call inside EFI context.
+ */
+ efi_restore_gd();
+
+ image_obj->exit_status = exit_status;
+ longjmp(&image_obj->exit_jmp, 1);
+
+ panic("EFI application exited");
+out:
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+}
+
/**
* efi_handle_protocol() - get interface of a protocol on a handle
* @handle: handle on which the protocol shall be opened