mbox series

[v3,0/6] efi: implement generic compressed boot support

Message ID 20220817110345.1771267-1-ardb@kernel.org
Headers show
Series efi: implement generic compressed boot support | expand

Message

Ard Biesheuvel Aug. 17, 2022, 11:03 a.m. UTC
Relatively modern architectures such as arm64 or RISC-V don't implement
a self-decompressing kernel, and leave it up to the bootloader to
decompress the compressed image before executing it. For bare metal
boot, this policy makes sense, as a self-decompressing image essentially
duplicates a lot of fiddly preparation work to create a 1:1 mapping and
set up the C runtime, and to discover or infer where DRAM lives from
device trees or other firmware tables.

For EFI boot, the situation is a bit different: the EFI entrypoint is
called with a 1:1 cached mapping covering all of DRAM already active,
and with a stack, a heap, a memory map and boot services to load and
start images. This means it is rather trivial to implement a
self-decompressing wrapper for EFI boot in a generic manner, and reuse
it across architectures that implement EFI boot.

The only slight downside is that when UEFI secure boot is enabled, the
generic LoadImage/StartImage only allow signed images to be loaded and
started, and we would prefer to avoid the need to sign both the inner
and outer PE/COFF images.

However, the only truly generic and portable way to achieve this is to
rely on LoadImage/StartImage as the EFI spec defines them, and avoid
making assumptions about how things might work under the hood, and how
we might circumvent that. This includes just loading the image into
memory and jumping to the PE entry point: in the context of secure boot,
measured boot and other hardening measures the firmware may take (such
as disallowing mappings that are both writable and executable), using
the firmware's image loading API is the only maintainable choice.

For this reason, this version of the series includes support for signing
the images using sbsign, if the signing key and cert are specified in
Kconfig.

The code is wired up for arm64 and RISC-V. The latter was build tested
only. I also tested the code with the upcoming LoongArch EFI stub
support, which built correctly (using binutils 2.39) but needs some
additional work before it will run as expected.

Changes since v2:
- drop some of the refactoring work to make efi_printk() available in
  the decompressor, and just use fixed strings instead;
- provide memcpy/memmove/memset based on the UEFI boot services, instead
  of having to specify for each architecture how to wire these up;
- drop PI/DXE based signature check circumvention, and just sign the
  inner image instead, if needed;
- add a header to the zimage binary that identifies it as a EFI zboot
  image, and describes the compression algorithm and where the payload
  lives in the image - this might be used by non-EFI loaders to locate
  and decompress the bare metal image, given that the EFI zboot one is
  not a hybrid like the one it encapsulates.

Cc: "James E.J. Bottomley" <James.Bottomley@HansenPartnership.com>
Cc: Matthew Garrett <mjg59@srcf.ucam.org>
Cc: Peter Jones <pjones@redhat.com>
Cc: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Cc: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
Cc: AKASHI Takahiro <takahiro.akashi@linaro.org>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Atish Patra <atishp@atishpatra.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Huacai Chen <chenhuacai@loongson.cn>
Cc: Lennart Poettering <lennart@poettering.net>
Cc: Jeremy Linton <jeremy.linton@arm.com>

Ard Biesheuvel (6):
  efi/libstub: use EFI provided memcpy/memset routines
  efi/libstub: add some missing boot service prototypes
  efi/libstub: move efi_system_table global var into separate object
  efi/libstub: implement generic EFI zboot
  arm64: efi: enable generic EFI compressed boot
  riscv: efi: enable generic EFI compressed boot

 arch/arm64/Makefile                         |   7 +-
 arch/arm64/boot/Makefile                    |   6 +
 arch/arm64/kernel/image-vars.h              |  13 --
 arch/riscv/Makefile                         |   5 +
 arch/riscv/boot/Makefile                    |   6 +
 arch/riscv/kernel/image-vars.h              |   9 --
 drivers/firmware/efi/Kconfig                |  31 ++++-
 drivers/firmware/efi/libstub/Makefile       |  11 +-
 drivers/firmware/efi/libstub/Makefile.zboot |  69 ++++++++++
 drivers/firmware/efi/libstub/efi-stub.c     |   2 -
 drivers/firmware/efi/libstub/efistub.h      |  12 +-
 drivers/firmware/efi/libstub/intrinsics.c   |  30 +++++
 drivers/firmware/efi/libstub/systable.c     |   8 ++
 drivers/firmware/efi/libstub/zboot-header.S | 139 ++++++++++++++++++++
 drivers/firmware/efi/libstub/zboot.c        | 101 ++++++++++++++
 drivers/firmware/efi/libstub/zboot.lds      |  39 ++++++
 16 files changed, 453 insertions(+), 35 deletions(-)
 create mode 100644 drivers/firmware/efi/libstub/Makefile.zboot
 create mode 100644 drivers/firmware/efi/libstub/intrinsics.c
 create mode 100644 drivers/firmware/efi/libstub/systable.c
 create mode 100644 drivers/firmware/efi/libstub/zboot-header.S
 create mode 100644 drivers/firmware/efi/libstub/zboot.c
 create mode 100644 drivers/firmware/efi/libstub/zboot.lds

Comments

Ard Biesheuvel Aug. 17, 2022, 12:31 p.m. UTC | #1
On Wed, 17 Aug 2022 at 13:53, Arnd Bergmann <arnd@arndb.de> wrote:
>
> On Wed, Aug 17, 2022 at 1:03 PM Ard Biesheuvel <ardb@kernel.org> wrote:
> > BZIP2 has been omitted from the set of supported compression algorithms,
> > given that its performance is mediocre both in speed and size, and it
> > uses a disproportionate amount of memory. For optimal compression, use
> > LZMA. For the fastest boot speed, use LZO.
> ...
> > +config EFI_ZBOOT
> > +       bool "Enable the generic EFI decompressor"
> > +       depends on EFI_GENERIC_STUB && !ARM
> > +       select HAVE_KERNEL_GZIP
> > +       select HAVE_KERNEL_LZ4
> > +       select HAVE_KERNEL_LZMA
> > +       select HAVE_KERNEL_LZO
>
> I hope I don't turn this into a bike-shed discussion, but it feels
> like if you give
> the choice between these four, you should also offer ZSTD, which combines
> high compression ratio with fast decompression speed.
> XZ is probably more widely installed than LZMA.
>
> I would be happy with just gzip (to minimize build dependencies) and
> zstd, but there is little harm in also including the other ones you have
> here, or all seven of them.
>

Let's add whatever people feel is useful. The reason I dropped bzip2
is because it uses much more memory. This is not a problem per se, but
it seemed pointless to accommodate bzip2 if we have better options
anyway.

zstd seems trivial to add if i bump the heap size to 256k, and xz just
needs a little tweak so it doesn't redefine memmove and memcpy, but
beyond that, it's just copy/paste'ing the pattern another couple of
times.