efi_loader: implement support of exit data
authorHeinrich Schuchardt <xypron.glpk@gmx.de>
Tue, 30 Apr 2019 15:57:30 +0000 (17:57 +0200)
committerHeinrich Schuchardt <xypron.glpk@gmx.de>
Thu, 2 May 2019 16:17:50 +0000 (18:17 +0200)
In case of a failure exit data may be passed to Exit() which in turn is
returned by StartImage().

Let the `bootefi` command print the exit data string in case of an error.

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
cmd/bootefi.c
include/efi_loader.h
lib/efi_loader/efi_boottime.c

index b93d8c6a32cd306f56a72fa0ddf28fbfe2e8c9a6..f1d7d8bc6634f4b1624959667457bcd0bbb21372 100644 (file)
@@ -297,6 +297,8 @@ static efi_status_t efi_install_fdt(const char *fdt_opt)
 static efi_status_t do_bootefi_exec(efi_handle_t handle)
 {
        efi_status_t ret;
+       efi_uintn_t exit_data_size = 0;
+       u16 *exit_data = NULL;
 
        /* Transfer environment variable as load options */
        ret = set_load_options(handle, "bootargs");
@@ -304,7 +306,12 @@ static efi_status_t do_bootefi_exec(efi_handle_t handle)
                return ret;
 
        /* Call our payload! */
-       ret = EFI_CALL(efi_start_image(handle, NULL, NULL));
+       ret = EFI_CALL(efi_start_image(handle, &exit_data_size, &exit_data));
+       printf("## Application terminated, r = %lu\n", ret & ~EFI_ERROR_MASK);
+       if (ret && exit_data) {
+               printf("## %ls\n", exit_data);
+               efi_free_pool(exit_data);
+       }
 
        efi_restore_gd();
 
@@ -357,7 +364,6 @@ static int do_efibootmgr(const char *fdt_opt)
        }
 
        ret = do_bootefi_exec(handle);
-       printf("## Application terminated, r = %lu\n", ret & ~EFI_ERROR_MASK);
 
        if (ret != EFI_SUCCESS)
                return CMD_RET_FAILURE;
@@ -472,7 +478,6 @@ static int do_bootefi_image(const char *image_opt, const char *fdt_opt)
                goto out;
 
        ret = do_bootefi_exec(handle);
-       printf("## Application terminated, r = %lu\n", ret & ~EFI_ERROR_MASK);
 
 out:
        if (mem_handle)
index 07b913d256cbd1a9f8aba2527bed250179f1b32c..7af3f16ef840dd85856559825b3567025c4f0e02 100644 (file)
@@ -207,12 +207,17 @@ struct efi_object {
  * struct efi_loaded_image_obj - handle of a loaded image
  *
  * @header:            EFI object header
+ * @exit_status:       exit status passed to Exit()
+ * @exit_data_size:    exit data size passed to Exit()
+ * @exit_data:         exit data passed to Exit()
  * @exit_jmp:          long jump buffer for returning form started image
  * @entry:             entry address of the relocated image
  */
 struct efi_loaded_image_obj {
        struct efi_object header;
        efi_status_t exit_status;
+       efi_uintn_t *exit_data_size;
+       u16 **exit_data;
        struct jmp_buf_data exit_jmp;
        EFIAPI efi_status_t (*entry)(efi_handle_t image_handle,
                                     struct efi_system_table *st);
index a72cbe1559d854680611b9f16da2c124ba601396..6d2dc2dda36dcacbdcc092cf4cf8f525b4eea2d7 100644 (file)
@@ -2626,6 +2626,9 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
 
        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)) {
                /*
@@ -2669,6 +2672,41 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
        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
@@ -2709,6 +2747,15 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
        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);