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 c2f8980..246b59d 100644 (file)
@@ -6,15 +6,20 @@
  */
 
 #include <common.h>
+#include <bootm.h>
 #include <div64.h>
+#include <dm/device.h>
+#include <dm/root.h>
 #include <efi_loader.h>
-#include <environment.h>
+#include <irq_func.h>
+#include <log.h>
 #include <malloc.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;
 
@@ -25,7 +30,7 @@ static efi_uintn_t efi_tpl = TPL_APPLICATION;
 LIST_HEAD(efi_obj_list);
 
 /* List of all events */
-LIST_HEAD(efi_events);
+__efi_runtime_data LIST_HEAD(efi_events);
 
 /* List of queued events */
 LIST_HEAD(efi_event_queue);
@@ -33,28 +38,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;
 
+#if defined(CONFIG_ARM) || defined(CONFIG_RISCV)
 /*
- * 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 "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 */
@@ -91,10 +91,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;
 }
@@ -103,32 +103,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
 }
 
@@ -214,7 +224,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;
@@ -596,7 +606,7 @@ efi_status_t efi_remove_all_protocols(const efi_handle_t handle)
 /**
  * efi_delete_handle() - delete handle
  *
- * @obj: handle to delete
+ * @handle: handle to delete
  */
 void efi_delete_handle(efi_handle_t handle)
 {
@@ -628,6 +638,7 @@ static efi_status_t efi_is_event(const struct efi_event *event)
 
 /**
  * efi_create_event() - create an event
+ *
  * @type:            type of the event to create
  * @notify_tpl:      task priority level of the event
  * @notify_function: notification function of the event
@@ -650,6 +661,8 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
                              struct efi_event **event)
 {
        struct efi_event *evt;
+       efi_status_t ret;
+       int pool_type;
 
        if (event == NULL)
                return EFI_INVALID_PARAMETER;
@@ -662,7 +675,10 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
        case EVT_NOTIFY_WAIT:
        case EVT_TIMER | EVT_NOTIFY_WAIT:
        case EVT_SIGNAL_EXIT_BOOT_SERVICES:
+               pool_type = EFI_BOOT_SERVICES_DATA;
+               break;
        case EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE:
+               pool_type = EFI_RUNTIME_SERVICES_DATA;
                break;
        default:
                return EFI_INVALID_PARAMETER;
@@ -672,9 +688,11 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
            (!notify_function || is_valid_tpl(notify_tpl) != EFI_SUCCESS))
                return EFI_INVALID_PARAMETER;
 
-       evt = calloc(1, sizeof(struct efi_event));
-       if (!evt)
-               return EFI_OUT_OF_RESOURCES;
+       ret = efi_allocate_pool(pool_type, sizeof(struct efi_event),
+                               (void **)&evt);
+       if (ret != EFI_SUCCESS)
+               return ret;
+       memset(evt, 0, sizeof(struct efi_event));
        evt->type = type;
        evt->notify_tpl = notify_tpl;
        evt->notify_function = notify_function;
@@ -982,7 +1000,7 @@ static efi_status_t EFIAPI efi_close_event(struct efi_event *event)
                list_del(&event->queue_link);
 
        list_del(&event->link);
-       free(event);
+       efi_free_pool(event);
        return EFI_EXIT(EFI_SUCCESS);
 }
 
@@ -1399,7 +1417,7 @@ static efi_status_t EFIAPI efi_register_protocol_notify(
        }
 
        item->event = event;
-       memcpy(&item->protocol, protocol, sizeof(efi_guid_t));
+       guidcpy(&item->protocol, protocol);
        INIT_LIST_HEAD(&item->handles);
 
        list_add_tail(&item->link, &efi_register_notify_events);
@@ -1411,9 +1429,9 @@ out:
 
 /**
  * efi_search() - determine if an EFI handle implements a protocol
+ *
  * @search_type: selection criterion
  * @protocol:    GUID of the protocol
- * @search_key:  registration key
  * @handle:      handle
  *
  * See the documentation of the LocateHandle service in the UEFI specification.
@@ -1630,7 +1648,7 @@ efi_status_t efi_install_configuration_table(const efi_guid_t *guid,
                return EFI_OUT_OF_RESOURCES;
 
        /* Add a new entry */
-       memcpy(&systab.tables[i].guid, guid, sizeof(*guid));
+       guidcpy(&systab.tables[i].guid, guid);
        systab.tables[i].table = table;
        systab.nr_tables = i + 1;
 
@@ -1675,7 +1693,7 @@ static efi_status_t EFIAPI efi_install_configuration_table_ext(efi_guid_t *guid,
  * Initialize a loaded_image_info and loaded_image_info object with correct
  * protocols, boot-device, etc.
  *
- * In case of an error *handle_ptr and *info_ptr are set to NULL and an error
+ * In case of an error \*handle_ptr and \*info_ptr are set to NULL and an error
  * code is returned.
  *
  * @device_path:       device path of the loaded image
@@ -1870,22 +1888,18 @@ 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 */
        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 {
@@ -1903,13 +1917,21 @@ error:
  */
 static void efi_exit_caches(void)
 {
-#if defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
+#if defined(CONFIG_EFI_GRUB_ARM32_WORKAROUND)
        /*
-        * 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.
+        * Boooting Linux via GRUB prior to version 2.04 fails on 32bit ARM if
+        * caches are enabled.
+        *
+        * TODO:
+        * According to the UEFI spec caches that can be managed via CP15
+        * operations should be enabled. Caches requiring platform information
+        * to manage should be disabled. This should not happen in
+        * ExitBootServices() but before invoking any UEFI binary is invoked.
+        *
+        * We want to keep the current workaround while GRUB prior to version
+        * 2.04 is still in use.
         */
-       if (efi_is_direct_boot)
-               cleanup_before_linux();
+       cleanup_before_linux();
 #endif
 }
 
@@ -1932,7 +1954,7 @@ static void efi_exit_caches(void)
 static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
                                                  efi_uintn_t map_key)
 {
-       struct efi_event *evt;
+       struct efi_event *evt, *next_event;
        efi_status_t ret = EFI_SUCCESS;
 
        EFI_ENTRY("%p, %zx", image_handle, map_key);
@@ -1971,7 +1993,18 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
        /* Notify variable services */
        efi_variables_boot_exit_notify();
 
-       board_quiesce_devices();
+       /* Remove all events except EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE */
+       list_for_each_entry_safe(evt, next_event, &efi_events, link) {
+               if (evt->type != EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE)
+                       list_del(&evt->link);
+       }
+
+       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();
@@ -2090,10 +2123,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;
@@ -2266,7 +2299,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)
@@ -2869,18 +2902,22 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
 
        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));
        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;
 
@@ -2893,13 +2930,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
@@ -2919,10 +2956,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));
 }
@@ -3034,9 +3071,9 @@ out:
 /**
  * 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
+ * @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,
@@ -3168,9 +3205,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);
@@ -3438,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;
@@ -3488,8 +3527,8 @@ static efi_status_t EFIAPI efi_disconnect_controller(
        efi_handle_t *child_handle_buffer = NULL;
        size_t number_of_children = 0;
        efi_status_t r;
-       size_t stop_count = 0;
        struct efi_object *efiobj;
+       bool sole_child;
 
        EFI_ENTRY("%p, %p, %p", controller_handle, driver_image_handle,
                  child_handle);
@@ -3512,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 */
@@ -3528,32 +3571,35 @@ static efi_status_t EFIAPI efi_disconnect_controller(
                                       (void **)&binding_protocol,
                                       driver_image_handle, NULL,
                                       EFI_OPEN_PROTOCOL_GET_PROTOCOL));
-       if (r != EFI_SUCCESS)
+       if (r != EFI_SUCCESS) {
+               r = EFI_INVALID_PARAMETER;
                goto out;
+       }
        /* Remove the children */
        if (number_of_children) {
                r = EFI_CALL(binding_protocol->stop(binding_protocol,
                                                    controller_handle,
                                                    number_of_children,
                                                    child_handle_buffer));
-               if (r == EFI_SUCCESS)
-                       ++stop_count;
+               if (r != EFI_SUCCESS) {
+                       r = EFI_DEVICE_ERROR;
+                       goto out;
+               }
        }
        /* Remove the driver */
-       if (!child_handle)
+       if (!child_handle || sole_child) {
                r = EFI_CALL(binding_protocol->stop(binding_protocol,
                                                    controller_handle,
                                                    0, NULL));
-       if (r == EFI_SUCCESS)
-               ++stop_count;
+               if (r != EFI_SUCCESS) {
+                       r = EFI_DEVICE_ERROR;
+                       goto out;
+               }
+       }
        EFI_CALL(efi_close_protocol(driver_image_handle,
                                    &efi_guid_driver_binding_protocol,
                                    driver_image_handle, NULL));
-
-       if (stop_count)
-               r = EFI_SUCCESS;
-       else
-               r = EFI_NOT_FOUND;
+       r = EFI_SUCCESS;
 out:
        if (!child_handle)
                free(child_handle_buffer);