]> Git Repo - u-boot.git/blobdiff - lib/efi_loader/efi_boottime.c
common: Drop asm/global_data.h from common header
[u-boot.git] / lib / efi_loader / efi_boottime.c
index 3103a50158ad6bed852b7ed67aecdde7f4556302..41b8949b04249737410cba6bd8d886948e7a3262 100644 (file)
@@ -6,17 +6,21 @@
  */
 
 #include <common.h>
+#include <bootm.h>
 #include <div64.h>
+#include <dm/device.h>
+#include <dm/root.h>
 #include <efi_loader.h>
 #include <irq_func.h>
+#include <log.h>
 #include <malloc.h>
-#include <time.h>
-#include <linux/libfdt_env.h>
-#include <u-boot/crc.h>
-#include <bootm.h>
 #include <pe.h>
+#include <time.h>
 #include <u-boot/crc.h>
+#include <usb.h>
 #include <watchdog.h>
+#include <asm/global_data.h>
+#include <linux/libfdt_env.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -35,20 +39,23 @@ LIST_HEAD(efi_event_queue);
 /* Flag to disable timer activity in ExitBootServices() */
 static bool timers_enabled = true;
 
+/* Flag used by the selftest to avoid detaching devices in ExitBootServices() */
+bool efi_st_keep_devices;
+
 /* List of all events registered by RegisterProtocolNotify() */
 LIST_HEAD(efi_register_notify_events);
 
 /* Handle of the currently executing image */
 static efi_handle_t current_image;
 
-#ifdef CONFIG_ARM
+#if defined(CONFIG_ARM) || defined(CONFIG_RISCV)
 /*
- * The "gd" pointer lives in a register on ARM and AArch64 that we declare
+ * The "gd" pointer lives in a register on ARM and RISC-V that we declare
  * fixed when compiling U-Boot. However, the payload does not know about that
  * restriction so we need to manually swap its and our view of that register on
  * EFI callback entry/exit.
  */
-static volatile void *efi_gd, *app_gd;
+static volatile gd_t *efi_gd, *app_gd;
 #endif
 
 /* 1 if inside U-Boot code, 0 if inside EFI payload code */
@@ -75,6 +82,9 @@ const efi_guid_t efi_guid_event_group_ready_to_boot =
 /* event group ResetSystem() invoked (before ExitBootServices) */
 const efi_guid_t efi_guid_event_group_reset_system =
                        EFI_EVENT_GROUP_RESET_SYSTEM;
+/* GUIDs of the Load File and Load File2 protocols */
+const efi_guid_t efi_guid_load_file_protocol = EFI_LOAD_FILE_PROTOCOL_GUID;
+const efi_guid_t efi_guid_load_file2_protocol = EFI_LOAD_FILE2_PROTOCOL_GUID;
 
 static efi_status_t EFIAPI efi_disconnect_controller(
                                        efi_handle_t controller_handle,
@@ -85,10 +95,10 @@ static efi_status_t EFIAPI efi_disconnect_controller(
 int __efi_entry_check(void)
 {
        int ret = entry_count++ == 0;
-#ifdef CONFIG_ARM
+#if defined(CONFIG_ARM) || defined(CONFIG_RISCV)
        assert(efi_gd);
        app_gd = gd;
-       gd = efi_gd;
+       set_gd(efi_gd);
 #endif
        return ret;
 }
@@ -97,32 +107,42 @@ int __efi_entry_check(void)
 int __efi_exit_check(void)
 {
        int ret = --entry_count == 0;
-#ifdef CONFIG_ARM
-       gd = app_gd;
+#if defined(CONFIG_ARM) || defined(CONFIG_RISCV)
+       set_gd(app_gd);
 #endif
        return ret;
 }
 
-/* Called from do_bootefi_exec() */
+/**
+ * efi_save_gd() - save global data register
+ *
+ * On the ARM and RISC-V architectures gd is mapped to a fixed register.
+ * As this register may be overwritten by an EFI payload we save it here
+ * and restore it on every callback entered.
+ *
+ * This function is called after relocation from initr_reloc_global_data().
+ */
 void efi_save_gd(void)
 {
-#ifdef CONFIG_ARM
+#if defined(CONFIG_ARM) || defined(CONFIG_RISCV)
        efi_gd = gd;
 #endif
 }
 
-/*
- * Special case handler for error/abort that just forces things back to u-boot
- * world so we can dump out an abort message, without any care about returning
- * back to UEFI world.
+/**
+ * efi_restore_gd() - restore global data register
+ *
+ * On the ARM and RISC-V architectures gd is mapped to a fixed register.
+ * Restore it after returning from the UEFI world to the value saved via
+ * efi_save_gd().
  */
 void efi_restore_gd(void)
 {
-#ifdef CONFIG_ARM
+#if defined(CONFIG_ARM) || defined(CONFIG_RISCV)
        /* Only restore if we're already in EFI context */
        if (!efi_gd)
                return;
-       gd = efi_gd;
+       set_gd(efi_gd);
 #endif
 }
 
@@ -208,7 +228,7 @@ static void efi_process_event_queue(void)
  */
 static void efi_queue_event(struct efi_event *event)
 {
-       struct efi_event *item = NULL;
+       struct efi_event *item;
 
        if (!event->notify_function)
                return;
@@ -228,8 +248,8 @@ static void efi_queue_event(struct efi_event *event)
                }
                if (event)
                        list_add_tail(&event->queue_link, &efi_event_queue);
+               efi_process_event_queue();
        }
-       efi_process_event_queue();
 }
 
 /**
@@ -255,8 +275,8 @@ efi_status_t is_valid_tpl(efi_uintn_t tpl)
  * efi_signal_event() - signal an EFI event
  * @event:     event to signal
  *
- * This function signals an event. If the event belongs to an event group all
- * events of the group are signaled. If they are of type EVT_NOTIFY_SIGNAL
+ * This function signals an event. If the event belongs to an event group, all
+ * events of the group are signaled. If they are of type EVT_NOTIFY_SIGNAL,
  * their notification function is queued.
  *
  * For the SignalEvent service see efi_signal_event_ext.
@@ -668,8 +688,15 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
                return EFI_INVALID_PARAMETER;
        }
 
+       /*
+        * The UEFI specification requires event notification levels to be
+        * > TPL_APPLICATION and <= TPL_HIGH_LEVEL.
+        *
+        * Parameter NotifyTpl should not be checked if it is not used.
+        */
        if ((type & (EVT_NOTIFY_WAIT | EVT_NOTIFY_SIGNAL)) &&
-           (!notify_function || is_valid_tpl(notify_tpl) != EFI_SUCCESS))
+           (!notify_function || is_valid_tpl(notify_tpl) != EFI_SUCCESS ||
+            notify_tpl == TPL_APPLICATION))
                return EFI_INVALID_PARAMETER;
 
        ret = efi_allocate_pool(pool_type, sizeof(struct efi_event),
@@ -1754,30 +1781,108 @@ failure:
 }
 
 /**
- * efi_load_image_from_path() - load an image using a file path
+ * efi_locate_device_path() - Get the device path and handle of an device
+ *                            implementing a protocol
+ * @protocol:    GUID of the protocol
+ * @device_path: device path
+ * @device:      handle of the device
+ *
+ * This function implements the LocateDevicePath service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_locate_device_path(
+                       const efi_guid_t *protocol,
+                       struct efi_device_path **device_path,
+                       efi_handle_t *device)
+{
+       struct efi_device_path *dp;
+       size_t i;
+       struct efi_handler *handler;
+       efi_handle_t *handles;
+       size_t len, len_dp;
+       size_t len_best = 0;
+       efi_uintn_t no_handles;
+       u8 *remainder;
+       efi_status_t ret;
+
+       EFI_ENTRY("%pUl, %p, %p", protocol, device_path, device);
+
+       if (!protocol || !device_path || !*device_path) {
+               ret = EFI_INVALID_PARAMETER;
+               goto out;
+       }
+
+       /* Find end of device path */
+       len = efi_dp_instance_size(*device_path);
+
+       /* Get all handles implementing the protocol */
+       ret = EFI_CALL(efi_locate_handle_buffer(BY_PROTOCOL, protocol, NULL,
+                                               &no_handles, &handles));
+       if (ret != EFI_SUCCESS)
+               goto out;
+
+       for (i = 0; i < no_handles; ++i) {
+               /* Find the device path protocol */
+               ret = efi_search_protocol(handles[i], &efi_guid_device_path,
+                                         &handler);
+               if (ret != EFI_SUCCESS)
+                       continue;
+               dp = (struct efi_device_path *)handler->protocol_interface;
+               len_dp = efi_dp_instance_size(dp);
+               /*
+                * This handle can only be a better fit
+                * if its device path length is longer than the best fit and
+                * if its device path length is shorter of equal the searched
+                * device path.
+                */
+               if (len_dp <= len_best || len_dp > len)
+                       continue;
+               /* Check if dp is a subpath of device_path */
+               if (memcmp(*device_path, dp, len_dp))
+                       continue;
+               if (!device) {
+                       ret = EFI_INVALID_PARAMETER;
+                       goto out;
+               }
+               *device = handles[i];
+               len_best = len_dp;
+       }
+       if (len_best) {
+               remainder = (u8 *)*device_path + len_best;
+               *device_path = (struct efi_device_path *)remainder;
+               ret = EFI_SUCCESS;
+       } else {
+               ret = EFI_NOT_FOUND;
+       }
+out:
+       return EFI_EXIT(ret);
+}
+
+/**
+ * efi_load_image_from_file() - load an image from file system
  *
  * Read a file into a buffer allocated as EFI_BOOT_SERVICES_DATA. It is the
  * callers obligation to update the memory type as needed.
  *
- * @file_path: the path of the image to load
- * @buffer:    buffer containing the loaded image
- * @size:      size of the loaded image
- * Return:     status code
+ * @file_path:         the path of the image to load
+ * @buffer:            buffer containing the loaded image
+ * @size:              size of the loaded image
+ * Return:             status code
  */
 static
-efi_status_t efi_load_image_from_path(struct efi_device_path *file_path,
+efi_status_t efi_load_image_from_file(struct efi_device_path *file_path,
                                      void **buffer, efi_uintn_t *size)
 {
        struct efi_file_info *info = NULL;
        struct efi_file_handle *f;
-       static efi_status_t ret;
+       efi_status_t ret;
        u64 addr;
        efi_uintn_t bs;
 
-       /* In case of failure nothing is returned */
-       *buffer = NULL;
-       *size = 0;
-
        /* Open file */
        f = efi_file_from_path(file_path);
        if (!f)
@@ -1825,6 +1930,83 @@ error:
        return ret;
 }
 
+/**
+ * efi_load_image_from_path() - load an image using a file path
+ *
+ * Read a file into a buffer allocated as EFI_BOOT_SERVICES_DATA. It is the
+ * callers obligation to update the memory type as needed.
+ *
+ * @boot_policy:       true for request originating from the boot manager
+ * @file_path:         the path of the image to load
+ * @buffer:            buffer containing the loaded image
+ * @size:              size of the loaded image
+ * Return:             status code
+ */
+static
+efi_status_t efi_load_image_from_path(bool boot_policy,
+                                     struct efi_device_path *file_path,
+                                     void **buffer, efi_uintn_t *size)
+{
+       efi_handle_t device;
+       efi_status_t ret;
+       struct efi_device_path *dp;
+       struct efi_load_file_protocol *load_file_protocol = NULL;
+       efi_uintn_t buffer_size;
+       uint64_t addr, pages;
+       const efi_guid_t *guid;
+
+       /* In case of failure nothing is returned */
+       *buffer = NULL;
+       *size = 0;
+
+       dp = file_path;
+       ret = EFI_CALL(efi_locate_device_path(
+                      &efi_simple_file_system_protocol_guid, &dp, &device));
+       if (ret == EFI_SUCCESS)
+               return efi_load_image_from_file(file_path, buffer, size);
+
+       ret = EFI_CALL(efi_locate_device_path(
+                      &efi_guid_load_file_protocol, &dp, &device));
+       if (ret == EFI_SUCCESS) {
+               guid = &efi_guid_load_file_protocol;
+       } else if (!boot_policy) {
+               guid = &efi_guid_load_file2_protocol;
+               ret = EFI_CALL(efi_locate_device_path(guid, &dp, &device));
+       }
+       if (ret != EFI_SUCCESS)
+               return EFI_NOT_FOUND;
+       ret = EFI_CALL(efi_handle_protocol(device, guid,
+                                          (void **)&load_file_protocol));
+       if (ret != EFI_SUCCESS)
+               return EFI_NOT_FOUND;
+       buffer_size = 0;
+       ret = load_file_protocol->load_file(load_file_protocol, dp,
+                                           boot_policy, &buffer_size,
+                                           NULL);
+       if (ret != EFI_BUFFER_TOO_SMALL)
+               goto out;
+       pages = efi_size_in_pages(buffer_size);
+       ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, EFI_BOOT_SERVICES_DATA,
+                                pages, &addr);
+       if (ret != EFI_SUCCESS) {
+               ret = EFI_OUT_OF_RESOURCES;
+               goto out;
+       }
+       ret = EFI_CALL(load_file_protocol->load_file(
+                                       load_file_protocol, dp, boot_policy,
+                                       &buffer_size, (void *)(uintptr_t)addr));
+       if (ret != EFI_SUCCESS)
+               efi_free_pages(addr, pages);
+out:
+       EFI_CALL(efi_close_protocol(device, guid, efi_root, NULL));
+       if (ret == EFI_SUCCESS) {
+               *buffer = (void *)(uintptr_t)addr;
+               *size = buffer_size;
+       }
+
+       return ret;
+}
+
 /**
  * efi_load_image() - load an EFI image into memory
  * @boot_policy:   true for request originating from the boot manager
@@ -1867,27 +2049,23 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy,
        }
 
        if (!source_buffer) {
-               ret = efi_load_image_from_path(file_path, &dest_buffer,
-                                              &source_size);
+               ret = efi_load_image_from_path(boot_policy, file_path,
+                                              &dest_buffer, &source_size);
                if (ret != EFI_SUCCESS)
                        goto error;
        } else {
-               if (!source_size) {
-                       ret = EFI_LOAD_ERROR;
-                       goto error;
-               }
                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)
-               ret = efi_load_pe(*image_obj, dest_buffer, info);
+               ret = efi_load_pe(*image_obj, dest_buffer, source_size, 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) {
+       if (ret == EFI_SUCCESS || ret == EFI_SECURITY_VIOLATION) {
                info->system_table = &systab;
                info->parent_handle = parent_image;
        } else {
@@ -1987,7 +2165,12 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
                        list_del(&evt->link);
        }
 
-       board_quiesce_devices();
+       if (!efi_st_keep_devices) {
+               if (IS_ENABLED(CONFIG_USB_DEVICE))
+                       udc_disconnect();
+               board_quiesce_devices();
+               dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL);
+       }
 
        /* Patch out unsupported runtime function */
        efi_runtime_detach();
@@ -2106,10 +2289,10 @@ static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout,
  *
  * Return: status code
  */
-static efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle,
-                                             const efi_guid_t *protocol,
-                                             efi_handle_t agent_handle,
-                                             efi_handle_t controller_handle)
+efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle,
+                                      const efi_guid_t *protocol,
+                                      efi_handle_t agent_handle,
+                                      efi_handle_t controller_handle)
 {
        struct efi_handler *handler;
        struct efi_open_protocol_info_item *item;
@@ -2282,7 +2465,7 @@ static efi_status_t EFIAPI efi_protocols_per_handle(
  *
  * Return: status code
  */
-static efi_status_t EFIAPI efi_locate_handle_buffer(
+efi_status_t EFIAPI efi_locate_handle_buffer(
                        enum efi_locate_search_type search_type,
                        const efi_guid_t *protocol, void *search_key,
                        efi_uintn_t *no_handles, efi_handle_t **buffer)
@@ -2386,88 +2569,6 @@ found:
        return EFI_EXIT(EFI_SUCCESS);
 }
 
-/**
- * efi_locate_device_path() - Get the device path and handle of an device
- *                            implementing a protocol
- * @protocol:    GUID of the protocol
- * @device_path: device path
- * @device:      handle of the device
- *
- * This function implements the LocateDevicePath service.
- *
- * See the Unified Extensible Firmware Interface (UEFI) specification for
- * details.
- *
- * Return: status code
- */
-static efi_status_t EFIAPI efi_locate_device_path(
-                       const efi_guid_t *protocol,
-                       struct efi_device_path **device_path,
-                       efi_handle_t *device)
-{
-       struct efi_device_path *dp;
-       size_t i;
-       struct efi_handler *handler;
-       efi_handle_t *handles;
-       size_t len, len_dp;
-       size_t len_best = 0;
-       efi_uintn_t no_handles;
-       u8 *remainder;
-       efi_status_t ret;
-
-       EFI_ENTRY("%pUl, %p, %p", protocol, device_path, device);
-
-       if (!protocol || !device_path || !*device_path) {
-               ret = EFI_INVALID_PARAMETER;
-               goto out;
-       }
-
-       /* Find end of device path */
-       len = efi_dp_instance_size(*device_path);
-
-       /* Get all handles implementing the protocol */
-       ret = EFI_CALL(efi_locate_handle_buffer(BY_PROTOCOL, protocol, NULL,
-                                               &no_handles, &handles));
-       if (ret != EFI_SUCCESS)
-               goto out;
-
-       for (i = 0; i < no_handles; ++i) {
-               /* Find the device path protocol */
-               ret = efi_search_protocol(handles[i], &efi_guid_device_path,
-                                         &handler);
-               if (ret != EFI_SUCCESS)
-                       continue;
-               dp = (struct efi_device_path *)handler->protocol_interface;
-               len_dp = efi_dp_instance_size(dp);
-               /*
-                * This handle can only be a better fit
-                * if its device path length is longer than the best fit and
-                * if its device path length is shorter of equal the searched
-                * device path.
-                */
-               if (len_dp <= len_best || len_dp > len)
-                       continue;
-               /* Check if dp is a subpath of device_path */
-               if (memcmp(*device_path, dp, len_dp))
-                       continue;
-               if (!device) {
-                       ret = EFI_INVALID_PARAMETER;
-                       goto out;
-               }
-               *device = handles[i];
-               len_best = len_dp;
-       }
-       if (len_best) {
-               remainder = (u8 *)*device_path + len_best;
-               *device_path = (struct efi_device_path *)remainder;
-               ret = EFI_SUCCESS;
-       } else {
-               ret = EFI_NOT_FOUND;
-       }
-out:
-       return EFI_EXIT(ret);
-}
-
 /**
  * efi_install_multiple_protocol_interfaces() - Install multiple protocol
  *                                              interfaces
@@ -2683,7 +2784,7 @@ static void EFIAPI efi_set_mem(void *buffer, size_t size, uint8_t value)
  *
  * Return: status code
  */
-static efi_status_t efi_protocol_open(
+efi_status_t efi_protocol_open(
                        struct efi_handler *handler,
                        void **protocol_interface, void *agent_handle,
                        void *controller_handle, uint32_t attributes)
@@ -2882,13 +2983,21 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
        efi_status_t ret;
        void *info;
        efi_handle_t parent_image = current_image;
+       efi_status_t exit_status;
+       struct jmp_buf_data exit_jmp;
 
        EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data);
 
+       if (!efi_search_obj(image_handle))
+               return EFI_EXIT(EFI_INVALID_PARAMETER);
+
        /* Check parameters */
        if (image_obj->header.type != EFI_OBJECT_TYPE_LOADED_IMAGE)
                return EFI_EXIT(EFI_INVALID_PARAMETER);
 
+       if (image_obj->auth_status != EFI_IMAGE_AUTH_PASSED)
+               return EFI_EXIT(EFI_SECURITY_VIOLATION);
+
        ret = EFI_CALL(efi_open_protocol(image_handle, &efi_guid_loaded_image,
                                         &info, NULL, NULL,
                                         EFI_OPEN_PROTOCOL_GET_PROTOCOL));
@@ -2897,9 +3006,11 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
 
        image_obj->exit_data_size = exit_data_size;
        image_obj->exit_data = exit_data;
+       image_obj->exit_status = &exit_status;
+       image_obj->exit_jmp = &exit_jmp;
 
        /* call the image! */
-       if (setjmp(&image_obj->exit_jmp)) {
+       if (setjmp(&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
@@ -2907,13 +3018,13 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
                 * us to the current line. This implies that the second half
                 * of the EFI_CALL macro has not been executed.
                 */
-#ifdef CONFIG_ARM
+#if defined(CONFIG_ARM) || defined(CONFIG_RISCV)
                /*
                 * 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;
+               set_gd(app_gd);
 #endif
                /*
                 * To get ready to call EFI_EXIT below we have to execute the
@@ -2921,10 +3032,10 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
                 */
                assert(__efi_entry_check());
                EFI_PRINT("%lu returned by started image\n",
-                         (unsigned long)((uintptr_t)image_obj->exit_status &
+                         (unsigned long)((uintptr_t)exit_status &
                          ~EFI_ERROR_MASK));
                current_image = parent_image;
-               return EFI_EXIT(image_obj->exit_status);
+               return EFI_EXIT(exit_status);
        }
 
        current_image = image_handle;
@@ -2933,10 +3044,10 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
        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.
+        * Control is returned from a started UEFI image either by calling
+        * Exit() (where exit data can be provided) or by simply returning from
+        * the entry point. In the latter case call Exit() on behalf of the
+        * image.
         */
        return EFI_CALL(systab.boottime->exit(image_handle, ret, 0, NULL));
 }
@@ -3107,6 +3218,7 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
        struct efi_loaded_image *loaded_image_protocol;
        struct efi_loaded_image_obj *image_obj =
                (struct efi_loaded_image_obj *)image_handle;
+       struct jmp_buf_data *exit_jmp;
 
        EFI_ENTRY("%p, %ld, %zu, %p", image_handle, exit_status,
                  exit_data_size, exit_data);
@@ -3148,6 +3260,9 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
                if (ret != EFI_SUCCESS)
                        EFI_PRINT("%s: out of memory\n", __func__);
        }
+       /* efi_delete_image() frees image_obj. Copy before the call. */
+       exit_jmp = image_obj->exit_jmp;
+       *image_obj->exit_status = exit_status;
        if (image_obj->image_type == IMAGE_SUBSYSTEM_EFI_APPLICATION ||
            exit_status != EFI_SUCCESS)
                efi_delete_image(image_obj, loaded_image_protocol);
@@ -3161,8 +3276,7 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
         */
        efi_restore_gd();
 
-       image_obj->exit_status = exit_status;
-       longjmp(&image_obj->exit_jmp, 1);
+       longjmp(exit_jmp, 1);
 
        panic("EFI application exited");
 out:
@@ -3182,9 +3296,9 @@ out:
  *
  * Return: status code
  */
-static efi_status_t EFIAPI efi_handle_protocol(efi_handle_t handle,
-                                              const efi_guid_t *protocol,
-                                              void **protocol_interface)
+efi_status_t EFIAPI efi_handle_protocol(efi_handle_t handle,
+                                       const efi_guid_t *protocol,
+                                       void **protocol_interface)
 {
        return efi_open_protocol(handle, protocol, protocol_interface, efi_root,
                                 NULL, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL);
@@ -3452,6 +3566,8 @@ static efi_status_t efi_get_child_controllers(
         * the buffer will be too large. But that does not harm.
         */
        *number_of_children = 0;
+       if (!count)
+               return EFI_SUCCESS;
        *child_handle_buffer = calloc(count, sizeof(efi_handle_t));
        if (!*child_handle_buffer)
                return EFI_OUT_OF_RESOURCES;
@@ -3503,6 +3619,7 @@ static efi_status_t EFIAPI efi_disconnect_controller(
        size_t number_of_children = 0;
        efi_status_t r;
        struct efi_object *efiobj;
+       bool sole_child;
 
        EFI_ENTRY("%p, %p, %p", controller_handle, driver_image_handle,
                  child_handle);
@@ -3525,14 +3642,18 @@ static efi_status_t EFIAPI efi_disconnect_controller(
        }
 
        /* Create list of child handles */
+       r = efi_get_child_controllers(efiobj,
+                                     driver_image_handle,
+                                     &number_of_children,
+                                     &child_handle_buffer);
+       if (r != EFI_SUCCESS)
+               return r;
+       sole_child = (number_of_children == 1);
+
        if (child_handle) {
                number_of_children = 1;
+               free(child_handle_buffer);
                child_handle_buffer = &child_handle;
-       } else {
-               efi_get_child_controllers(efiobj,
-                                         driver_image_handle,
-                                         &number_of_children,
-                                         &child_handle_buffer);
        }
 
        /* Get the driver binding protocol */
@@ -3557,7 +3678,7 @@ static efi_status_t EFIAPI efi_disconnect_controller(
                }
        }
        /* Remove the driver */
-       if (!child_handle) {
+       if (!child_handle || sole_child) {
                r = EFI_CALL(binding_protocol->stop(binding_protocol,
                                                    controller_handle,
                                                    0, NULL));
This page took 0.049517 seconds and 4 git commands to generate.