]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
efi_loader: setting boot device
authorHeinrich Schuchardt <xypron.glpk@gmx.de>
Tue, 12 Jan 2021 11:46:24 +0000 (12:46 +0100)
committerHeinrich Schuchardt <xypron.glpk@gmx.de>
Wed, 13 Jan 2021 01:38:00 +0000 (02:38 +0100)
Up to now the bootefi command used the last file loaded to determine the
boot partition. This has led to errors when the fdt had been loaded from
another partition after the EFI binary.

Before setting the boot device from a loaded file check if it is a PE-COFF
image or a FIT image.

For a PE-COFF image remember address and size, boot device and path.

For a FIT image remember boot device and path.

If the PE-COFF image is overwritten by loading another file, forget it.

Do not allow to start an image via bootefi which is not the last loaded
PE-COFF image.

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
cmd/bootefi.c
doc/uefi/uefi.rst
fs/fs.c
include/efi_loader.h
net/tftp.c

index c82a5bacf632b8004b62a34097210b066ce9e8af..8fa4a1c28764b72d780f5612b38a326292cab22e 100644 (file)
@@ -29,6 +29,82 @@ DECLARE_GLOBAL_DATA_PTR;
 
 static struct efi_device_path *bootefi_image_path;
 static struct efi_device_path *bootefi_device_path;
+static void *image_addr;
+static size_t image_size;
+
+/**
+ * efi_clear_bootdev() - clear boot device
+ */
+static void efi_clear_bootdev(void)
+{
+       efi_free_pool(bootefi_device_path);
+       efi_free_pool(bootefi_image_path);
+       bootefi_device_path = NULL;
+       bootefi_image_path = NULL;
+       image_addr = NULL;
+       image_size = 0;
+}
+
+/**
+ * efi_set_bootdev() - set boot device
+ *
+ * This function is called when a file is loaded, e.g. via the 'load' command.
+ * We use the path to this file to inform the UEFI binary about the boot device.
+ *
+ * @dev:               device, e.g. "MMC"
+ * @devnr:             number of the device, e.g. "1:2"
+ * @path:              path to file loaded
+ * @buffer:            buffer with file loaded
+ * @buffer_size:       size of file loaded
+ */
+void efi_set_bootdev(const char *dev, const char *devnr, const char *path,
+                    void *buffer, size_t buffer_size)
+{
+       struct efi_device_path *device, *image;
+       efi_status_t ret;
+
+       /* Forget overwritten image */
+       if (buffer + buffer_size >= image_addr &&
+           image_addr + image_size >= buffer)
+               efi_clear_bootdev();
+
+       /* Remember only PE-COFF and FIT images */
+       if (efi_check_pe(buffer, buffer_size, NULL) != EFI_SUCCESS) {
+#ifdef CONFIG_FIT
+               if (!fit_check_format(buffer))
+                       return;
+               /*
+                * FIT images of type EFI_OS are started via command bootm.
+                * We should not use their boot device with the bootefi command.
+                */
+               buffer = 0;
+               buffer_size = 0;
+#else
+               return;
+#endif
+       }
+
+       /* efi_set_bootdev() is typically called repeatedly, recover memory */
+       efi_clear_bootdev();
+
+       image_addr = buffer;
+       image_size = buffer_size;
+
+       ret = efi_dp_from_name(dev, devnr, path, &device, &image);
+       if (ret == EFI_SUCCESS) {
+               bootefi_device_path = device;
+               if (image) {
+                       /* FIXME: image should not contain device */
+                       struct efi_device_path *image_tmp = image;
+
+                       efi_dp_split_file_path(image, &device, &image);
+                       efi_free_pool(image_tmp);
+               }
+               bootefi_image_path = image;
+       } else {
+               efi_clear_bootdev();
+       }
+}
 
 /**
  * efi_env_set_load_options() - set load options from environment variable
@@ -398,33 +474,28 @@ static int do_bootefi_image(const char *image_opt)
 {
        void *image_buf;
        unsigned long addr, size;
-       const char *size_str;
        efi_status_t ret;
 
 #ifdef CONFIG_CMD_BOOTEFI_HELLO
        if (!strcmp(image_opt, "hello")) {
                image_buf = __efi_helloworld_begin;
                size = __efi_helloworld_end - __efi_helloworld_begin;
-
-               efi_free_pool(bootefi_device_path);
-               efi_free_pool(bootefi_image_path);
-               bootefi_device_path = NULL;
-               bootefi_image_path = NULL;
+               efi_clear_bootdev();
        } else
 #endif
        {
-               size_str = env_get("filesize");
-               if (size_str)
-                       size = simple_strtoul(size_str, NULL, 16);
-               else
-                       size = 0;
-
-               addr = simple_strtoul(image_opt, NULL, 16);
+               addr = strtoul(image_opt, NULL, 16);
                /* Check that a numeric value was passed */
-               if (!addr && *image_opt != '0')
+               if (!addr)
                        return CMD_RET_USAGE;
 
-               image_buf = map_sysmem(addr, size);
+               image_buf = map_sysmem(addr, 0);
+
+               if (image_buf != image_addr) {
+                       log_err("No UEFI binary known at %s\n", image_opt);
+                       return CMD_RET_FAILURE;
+               }
+               size = image_size;
        }
        ret = efi_run_image(image_buf, size);
 
@@ -557,11 +628,8 @@ static efi_status_t bootefi_test_prepare
        if (ret == EFI_SUCCESS)
                return ret;
 
-       efi_free_pool(bootefi_image_path);
-       bootefi_image_path = NULL;
 failure:
-       efi_free_pool(bootefi_device_path);
-       bootefi_device_path = NULL;
+       efi_clear_bootdev();
        return ret;
 }
 
@@ -681,39 +749,3 @@ U_BOOT_CMD(
        "Boots an EFI payload from memory",
        bootefi_help_text
 );
-
-/**
- * efi_set_bootdev() - set boot device
- *
- * This function is called when a file is loaded, e.g. via the 'load' command.
- * We use the path to this file to inform the UEFI binary about the boot device.
- *
- * @dev:       device, e.g. "MMC"
- * @devnr:     number of the device, e.g. "1:2"
- * @path:      path to file loaded
- */
-void efi_set_bootdev(const char *dev, const char *devnr, const char *path)
-{
-       struct efi_device_path *device, *image;
-       efi_status_t ret;
-
-       /* efi_set_bootdev is typically called repeatedly, recover memory */
-       efi_free_pool(bootefi_device_path);
-       efi_free_pool(bootefi_image_path);
-
-       ret = efi_dp_from_name(dev, devnr, path, &device, &image);
-       if (ret == EFI_SUCCESS) {
-               bootefi_device_path = device;
-               if (image) {
-                       /* FIXME: image should not contain device */
-                       struct efi_device_path *image_tmp = image;
-
-                       efi_dp_split_file_path(image, &device, &image);
-                       efi_free_pool(image_tmp);
-               }
-               bootefi_image_path = image;
-       } else {
-               bootefi_device_path = NULL;
-               bootefi_image_path = NULL;
-       }
-}
index dc930d9240222fd27c776ad92c70aea2a0a774a4..5a67737c1579a25c82b066c6c1feb4022598c462 100644 (file)
@@ -59,13 +59,10 @@ Below you find the output of an example session starting GRUB::
     120832 bytes read in 7 ms (16.5 MiB/s)
     => bootefi ${kernel_addr_r} ${fdt_addr_r}
 
-The bootefi command uses the device, the file name, and the file size
-(environment variable 'filesize') of the most recently loaded file when setting
-up the binary for execution. So the UEFI binary should be loaded last.
-
-The environment variable 'bootargs' is passed as load options in the UEFI system
-table. The Linux kernel EFI stub uses the load options as command line
-arguments.
+When booting from a memory location it is unknown from which file it was loaded.
+Therefore the bootefi command uses the device path of the block device partition
+or the network adapter and the file name of the most recently loaded PE-COFF
+file when setting up the loaded image protocol.
 
 Launching a UEFI binary from a FIT image
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/fs/fs.c b/fs/fs.c
index 5e80648b5b56ce9b4f3f144f5959cb59e4296ec7..68a15553cc95366777f8c63bd209d15ac6d5e0e4 100644 (file)
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -752,7 +752,8 @@ int do_load(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[],
 
        if (IS_ENABLED(CONFIG_CMD_BOOTEFI))
                efi_set_bootdev(argv[1], (argc > 2) ? argv[2] : "",
-                               (argc > 4) ? argv[4] : "");
+                               (argc > 4) ? argv[4] : "", map_sysmem(addr, 0),
+                               len_read);
 
        printf("%llu bytes read in %lu ms", len_read, time);
        if (time > 0) {
index df29d45a34882dcb93ce98d95c363e6f7f41f047..2a69ef844b6c66a3b5931d137f5771b00016cd0c 100644 (file)
@@ -474,7 +474,8 @@ void efi_restore_gd(void);
 /* Call this to relocate the runtime section to an address space */
 void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map);
 /* Call this to set the current device name */
-void efi_set_bootdev(const char *dev, const char *devnr, const char *path);
+void efi_set_bootdev(const char *dev, const char *devnr, const char *path,
+                    void *buffer, size_t buffer_size);
 /* Add a new object to the object list. */
 void efi_add_handle(efi_handle_t obj);
 /* Create handle */
@@ -873,7 +874,8 @@ static inline efi_status_t efi_add_runtime_mmio(void *mmio_ptr, u64 len)
 /* No loader configured, stub out EFI_ENTRY */
 static inline void efi_restore_gd(void) { }
 static inline void efi_set_bootdev(const char *dev, const char *devnr,
-                                  const char *path) { }
+                                  const char *path, void *buffer,
+                                  size_t buffer_size) { }
 static inline void efi_net_set_dhcp_ack(void *pkt, int len) { }
 static inline void efi_print_image_infos(void *pc) { }
 static inline efi_status_t efi_launch_capsules(void)
index 6fdb1a821a8577387bd8a3b19e5123e0fecfcf66..2cfa0b1486fe42b623196f0120cdf8bbc15eca8e 100644 (file)
@@ -329,6 +329,12 @@ static void tftp_complete(void)
                        time_start * 1000, "/s");
        }
        puts("\ndone\n");
+       if (IS_ENABLED(CONFIG_CMD_BOOTEFI)) {
+               if (!tftp_put_active)
+                       efi_set_bootdev("Net", "", tftp_filename,
+                                       map_sysmem(tftp_load_addr, 0),
+                                       net_boot_file_size);
+       }
        net_set_state(NETLOOP_SUCCESS);
 }
 
@@ -841,9 +847,6 @@ void tftp_start(enum proto_t protocol)
                printf("Load address: 0x%lx\n", tftp_load_addr);
                puts("Loading: *\b");
                tftp_state = STATE_SEND_RRQ;
-#ifdef CONFIG_CMD_BOOTEFI
-               efi_set_bootdev("Net", "", tftp_filename);
-#endif
        }
 
        time_start = get_timer(0);