Merge tag 'u-boot-atmel-fixes-2021.01-b' of https://gitlab.denx.de/u-boot/custodians...
[platform/kernel/u-boot.git] / lib / efi_loader / efi_boottime.c
index 1591ad8..246b59d 100644 (file)
@@ -6,18 +6,20 @@
  */
 
 #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 <linux/libfdt_env.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -36,15 +38,18 @@ 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.
@@ -86,7 +91,7 @@ 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;
        set_gd(efi_gd);
@@ -98,28 +103,38 @@ int __efi_entry_check(void)
 int __efi_exit_check(void)
 {
        int ret = --entry_count == 0;
-#ifdef CONFIG_ARM
+#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;
@@ -1873,10 +1888,6 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy,
                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 */
@@ -1988,7 +1999,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();
@@ -2914,7 +2930,7 @@ 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
@@ -3459,6 +3475,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;
@@ -3510,6 +3528,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);
@@ -3532,14 +3551,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 */
@@ -3564,7 +3587,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));