[PATCH v3 22/31] bootstd: Add an implementation of EFI boot

Simon Glass sjg at chromium.org
Sun Mar 6 04:08:23 CET 2022


Hi Heinrich,

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.

Of course there are no tests with the scripts so I am flying blind here.

Regards,
Simon


More information about the U-Boot mailing list