[PATCH v3 22/31] bootstd: Add an implementation of EFI boot
Michael Walle
michael at walle.cc
Mon Mar 7 10:03:02 CET 2022
>On Wed, 19 Jan 2022 at 04:45, Heinrich Schuchardt <xypron.glpk at gmx.de> wrote:
>>
>> On 1/19/22 02:43, Simon Glass wrote:
>> > Add a bootmeth driver which handles EFI boot, using EFI_LOADER.
>> >
>> > In effect, this provides the same functionality as the 'bootefi' command
>> > and shares the same code. But the interface into it is via a bootmeth,
>> > so it does not require any special scripts, etc.
>> >
>> > For now this requires the 'bootefi' command be enabled. Future work may
>> > tidy this up so that it can be used without CONFIG_CMDLINE being enabled.
>> >
>> > There was much discussion about whether this is needed, but it seems
>> > that it is, at least for now.
>> >
>> > Signed-off-by: Simon Glass <sjg at chromium.org>
>> > ---
>> >
>> > Changes in v3:
>> > - Add a log category
>> > - Use a short name when BOOTSTD_FULL is not enabled
>> > - Align the EFI load address
>> > - Use common bootmeth functions
>> >
>> > boot/Kconfig | 21 +++++
>> > boot/Makefile | 1 +
>> > boot/bootmeth_efi.c | 183 ++++++++++++++++++++++++++++++++++++++++++++
>> > 3 files changed, 205 insertions(+)
>> > create mode 100644 boot/bootmeth_efi.c
>> >
>> > diff --git a/boot/Kconfig b/boot/Kconfig
>> > index 61df705141d..0fee35070b0 100644
>> > --- a/boot/Kconfig
>> > +++ b/boot/Kconfig
>> > @@ -338,6 +338,27 @@ config BOOTMETH_DISTRO_PXE
>> >
>> > This provides a way to try out standard boot on an existing boot flow.
>> >
>> > +config BOOTMETH_EFILOADER
>> > + bool "Bootdev support for EFI boot"
>> > + depends on CMD_BOOTEFI
>> > + default y
>> > + help
>> > + Enables support for EFI boot using bootdevs. This makes the
>> > + bootdevs look for a 'boot<arch>.efi' on each filesystem
>> > + they scan. The resulting file is booted after enabling U-Boot's
>> > + EFI loader support.
>> > +
>> > + The <arch> depends on the architecture of the board:
>> > +
>> > + aa64 - aarch64 (ARM 64-bit)
>> > + arm - ARM 32-bit
>> > + ia32 - x86 32-bit
>> > + x64 - x86 64-bit
>> > + riscv32 - RISC-V 32-bit
>> > + riscv64 - RISC-V 64-bit
>> > +
>> > + This provides a way to try out standard boot on an existing boot flow.
>> > +
>> > endif
>> >
>> > config LEGACY_IMAGE_FORMAT
>> > diff --git a/boot/Makefile b/boot/Makefile
>> > index 170fcac8ec4..c2345435201 100644
>> > --- a/boot/Makefile
>> > +++ b/boot/Makefile
>> > @@ -30,6 +30,7 @@ obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += bootstd-uclass.o
>> >
>> > obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_DISTRO) += bootmeth_distro.o
>> > obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_DISTRO_PXE) += bootmeth_pxe.o
>> > +obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_EFILOADER) += bootmeth_efi.o
>> >
>> > obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o
>> > obj-$(CONFIG_$(SPL_TPL_)FIT_SIGNATURE) += fdt_region.o
>> > diff --git a/boot/bootmeth_efi.c b/boot/bootmeth_efi.c
>> > new file mode 100644
>> > index 00000000000..4c0c3494c3b
>> > --- /dev/null
>> > +++ b/boot/bootmeth_efi.c
>> > @@ -0,0 +1,183 @@
>> > +// SPDX-License-Identifier: GPL-2.0+
>> > +/*
>> > + * Bootmethod for distro boot via EFI
>> > + *
>> > + * Copyright 2021 Google LLC
>> > + * Written by Simon Glass <sjg at chromium.org>
>> > + */
>> > +
>> > +#define LOG_CATEGORY UCLASS_BOOTSTD
>> > +
>> > +#include <common.h>
>> > +#include <bootdev.h>
>> > +#include <bootflow.h>
>> > +#include <bootmeth.h>
>> > +#include <command.h>
>> > +#include <dm.h>
>> > +#include <efi_loader.h>
>> > +#include <fs.h>
>> > +#include <malloc.h>
>> > +#include <mapmem.h>
>> > +#include <mmc.h>
>> > +#include <pxe_utils.h>
>> > +
>> > +#define EFI_DIRNAME "efi/boot/"
>> > +
>> > +/**
>> > + * get_efi_leafname() - Get the leaf name for the EFI file we expect
>> > + *
>> > + * @str: Place to put leaf name for this architecture, e.g. "bootaa64.efi".
>> > + * Must have at least 16 bytes of space
>> > + * @max_len: Length of @str, must be >=16
>> > + */
>> > +static int get_efi_leafname(char *str, int max_len)
>> > +{
>> > + const char *base;
>> > +
>> > + if (max_len < 16)
>> > + return log_msg_ret("spc", -ENOSPC);
>> > + if (IS_ENABLED(CONFIG_ARM64))
>> > + base = "bootaa64";
>> > + else if (IS_ENABLED(CONFIG_ARM))
>> > + base = "bootarm";
>> > + else if (IS_ENABLED(CONFIG_X86_RUN_32BIT))
>> > + base = "bootia32";
>> > + else if (IS_ENABLED(CONFIG_X86_RUN_64BIT))
>> > + base = "bootx64";
>> > + else if (IS_ENABLED(CONFIG_ARCH_RV32I))
>> > + base = "bootriscv32";
>> > + else if (IS_ENABLED(CONFIG_ARCH_RV64I))
>> > + base = "bootriscv64";
>> > + else if (IS_ENABLED(CONFIG_SANDBOX))
>> > + base = "bootsbox";
>> > + else
>> > + return -EINVAL;
>> > +
>> > + strcpy(str, base);
>> > + strcat(str, ".efi");
>> > +
>> > + return 0;
>> > +}
>> > +
>> > +static int efiload_read_file(struct blk_desc *desc, struct bootflow *bflow)
>> > +{
>> > + const struct udevice *media_dev;
>> > + int size = bflow->size;
>> > + char devnum_str[9];
>> > + char dirname[200];
>> > + char *last_slash;
>> > + int ret;
>> > +
>> > + ret = bootmeth_alloc_file(bflow, 0x2000000, 0x10000);
>> > + if (ret)
>> > + return log_msg_ret("read", ret);
>> > +
>> > + /*
>> > + * This is a horrible hack to tell EFI about this boot device. Once we
>> > + * unify EFI with the rest of U-Boot we can clean this up. The same hack
>> > + * exists in multiple places, e.g. in the fs, tftp and load commands.
>> > + *
>> > + * Once we can clean up the EFI code to make proper use of driver model,
>> > + * this can go away.
>> > + */
>> > + media_dev = dev_get_parent(bflow->dev);
>> > + snprintf(devnum_str, sizeof(devnum_str), "%x", dev_seq(media_dev));
>> > +
>> > + strlcpy(dirname, bflow->fname, sizeof(dirname));
>> > + last_slash = strrchr(dirname, '/');
>> > + if (last_slash)
>> > + *last_slash = '\0';
>> > +
>> > + log_debug("setting bootdev %s, %s\n", dev_get_uclass_name(media_dev),
>> > + bflow->fname);
>> > + efi_set_bootdev(dev_get_uclass_name(media_dev), devnum_str,
>> > + bflow->fname, bflow->buf, size);
>> > +
>> > + return 0;
>> > +}
>> > +
>> > +static int distro_efi_check(struct udevice *dev, struct bootflow_iter *iter)
>> > +{
>> > + int ret;
>> > +
>> > + /* This only works on block devices */
>> > + ret = bootflow_iter_uses_blk_dev(iter);
>> > + if (ret)
>> > + return log_msg_ret("blk", ret);
>> > +
>> > + return 0;
>> > +}
>> > +
>> > +static int distro_efi_read_bootflow(struct udevice *dev, struct bootflow *bflow)
>> > +{
>> > + struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
>> > + char fname[sizeof(EFI_DIRNAME) + 16];
>> > + int ret;
>> > +
>> > + /* We require a partition table */
>> > + if (!bflow->part)
>> > + return -ENOENT;
>> > +
>> > + strcpy(fname, EFI_DIRNAME);
>> > + ret = get_efi_leafname(fname + strlen(fname),
>> > + sizeof(fname) - strlen(fname));
>> > + if (ret)
>> > + return log_msg_ret("leaf", ret);
>> > +
>> > + ret = bootmeth_try_file(bflow, desc, NULL, fname);
>> > + if (ret)
>> > + return log_msg_ret("try", ret);
>> > +
>> > + ret = efiload_read_file(desc, bflow);
>> > + if (ret)
>> > + return log_msg_ret("read", -EINVAL);
>> > +
>> > + return 0;
>> > +}
>> > +
>> > +int distro_efi_boot(struct udevice *dev, struct bootflow *bflow)
>> > +{
>> > + char cmd[50];
>> > +
>> > + /*
>> > + * At some point we can add a real interface to bootefi so we can call
>> > + * this directly. For now, go through the CLI like distro boot.
>> > + */
>> > + snprintf(cmd, sizeof(cmd), "bootefi %lx %lx",
>> > + (ulong)map_to_sysmem(bflow->buf),
>> > + (ulong)map_to_sysmem(gd->fdt_blob));
>> > + if (run_command(cmd, 0))
>> > + return log_msg_ret("run", -EINVAL);
>> > +
>> > + return 0;
>> > +}
>> > +
>> > +static int distro_bootmeth_efi_bind(struct udevice *dev)
>> > +{
>> > + struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
>> > +
>> > + plat->desc = IS_ENABLED(CONFIG_BOOTSTD_FULL) ?
>> > + "EFI boot from a .efi file" : "EFI";
>>
>> nits:
>> %s/a/an/
>>
>> > +
>> > + return 0;
>> > +}
>> > +
>> > +static struct bootmeth_ops distro_efi_bootmeth_ops = {
>> > + .check = distro_efi_check,
>> > + .read_bootflow = distro_efi_read_bootflow,
>> > + .read_file = bootmeth_common_read_file,
>> > + .boot = distro_efi_boot,
>> > +};
>>
>> Where is the device tree read?
>
> I'm not sure...where is it read with the scripts?
>
> Do you know a distro that uses that feature? So far I don't have one.
At least the debian arm64 mini.iso should support it. See also
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=991141
-michael
More information about the U-Boot
mailing list