[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