]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
bootstd: Avoid allocating memory for the EFI file
authorSimon Glass <sjg@chromium.org>
Thu, 27 Jul 2023 03:01:23 +0000 (21:01 -0600)
committerTom Rini <trini@konsulko.com>
Thu, 3 Aug 2023 19:30:54 +0000 (15:30 -0400)
The current bootflow-iteration algorithm reads the bootflow file into
an allocated memory buffer so it can be examined. This works well in
most cases.

However, while the common case is that the first bootflow is immediately
booted, it is also possible just to scan for available bootflows, perhaps
selecting one to boot later.

Even with the common case, EFI bootflows can be quite large. It doesn't
make sense to read it into an allocated buffer when we have kernel_addr_t
providing a suitable address for it. Even if we do have plenty of malloc()
space available, it is a violation of U-Boot's lazy-init principle to
read the bootflow before it is needed.

So overall it seems better to make a change.

Adjust the logic to read just the size of the EFI file at first. Later,
when the bootflow is booted, read the rest of the file into the designated
kernel buffer.

Signed-off-by: Simon Glass <sjg@chromium.org>
Reported-by: Da Xue <da@libre.computer>
Reported-by: Vincent Stehlé <vincent.stehle@arm.com>
boot/bootmeth_efi.c

index e88e171ccc1bc4a348b072c0769bbbb995f2a049..bceec0d12ef30fb930a5f5f90ae0e84626356a7f 100644 (file)
@@ -143,13 +143,24 @@ static void set_efi_bootdev(struct blk_desc *desc, struct bootflow *bflow)
        efi_set_bootdev(dev_name, devnum_str, bflow->fname, bflow->buf, size);
 }
 
-static int efiload_read_file(struct blk_desc *desc, struct bootflow *bflow)
+static int efiload_read_file(struct bootflow *bflow, ulong addr)
 {
+       struct blk_desc *desc = NULL;
+       loff_t bytes_read;
        int ret;
 
-       ret = bootmeth_alloc_file(bflow, 0x2000000, 0x10000);
+       if (bflow->blk)
+                desc = dev_get_uclass_plat(bflow->blk);
+       ret = bootmeth_setup_fs(bflow, desc);
+       if (ret)
+               return log_msg_ret("set", ret);
+
+       ret = fs_read(bflow->fname, addr, 0, bflow->size, &bytes_read);
        if (ret)
                return log_msg_ret("read", ret);
+       bflow->buf = map_sysmem(addr, bflow->size);
+
+       set_efi_bootdev(desc, bflow);
 
        return 0;
 }
@@ -223,7 +234,18 @@ static int distro_efi_get_fdt_name(char *fname, int size, int seq)
        return 0;
 }
 
-static int distro_efi_read_bootflow_file(struct udevice *dev,
+/*
+ * distro_efi_try_bootflow_files() - Check that files are present
+ *
+ * This reads any FDT file and checks whether the bootflow file is present, for
+ * later reading. We avoid reading the bootflow now, since it is likely large,
+ * it may take a long time and we want to avoid needing to allocate memory for
+ * it
+ *
+ * @dev: bootmeth device to use
+ * @bflow: bootflow to update
+ */
+static int distro_efi_try_bootflow_files(struct udevice *dev,
                                         struct bootflow *bflow)
 {
        struct blk_desc *desc = NULL;
@@ -247,9 +269,8 @@ static int distro_efi_read_bootflow_file(struct udevice *dev,
        if (ret)
                return log_msg_ret("try", ret);
 
-       ret = efiload_read_file(desc, bflow);
-       if (ret)
-               return log_msg_ret("read", ret);
+       /* Since we can access the file, let's call it ready */
+       bflow->state = BOOTFLOWST_READY;
 
        fdt_addr = env_get_hex("fdt_addr_r", 0);
 
@@ -376,7 +397,7 @@ static int distro_efi_read_bootflow(struct udevice *dev, struct bootflow *bflow)
                if (ret)
                        return log_msg_ret("net", ret);
        } else {
-               ret = distro_efi_read_bootflow_file(dev, bflow);
+               ret = distro_efi_try_bootflow_files(dev, bflow);
                if (ret)
                        return log_msg_ret("blk", ret);
        }
@@ -388,17 +409,13 @@ int distro_efi_boot(struct udevice *dev, struct bootflow *bflow)
 {
        ulong kernel, fdt;
        char cmd[50];
+       int ret;
 
-       /* A non-zero buffer indicates the kernel is there */
+       kernel = env_get_hex("kernel_addr_r", 0);
        if (!bootmeth_uses_network(bflow)) {
-               /* Set the EFI bootdev again, since reading an FDT loses it! */
-               if (bflow->blk) {
-                       struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
-
-                       set_efi_bootdev(desc, bflow);
-               }
-
-               kernel = (ulong)map_to_sysmem(bflow->buf);
+               ret = efiload_read_file(bflow, kernel);
+               if (ret)
+                       return log_msg_ret("read", ret);
 
                /*
                 * use the provided device tree if available, else fall back to
@@ -417,7 +434,6 @@ int distro_efi_boot(struct udevice *dev, struct bootflow *bflow)
                 * But this is the same behaviour for distro boot, so it can be
                 * fixed here.
                 */
-               kernel = env_get_hex("kernel_addr_r", 0);
                fdt = env_get_hex("fdt_addr_r", 0);
        }