Skip to content

Commit

Permalink
sd-boot: add EFI boot manager and stub loader
Browse files Browse the repository at this point in the history
  • Loading branch information
kaysievers committed Feb 17, 2015
1 parent 484adfd commit 0fa2cac
Show file tree
Hide file tree
Showing 19 changed files with 3,690 additions and 6 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -45,6 +45,9 @@
/machinectl
/mtd_probe
/networkctl
/linuxx64.efi.stub
/sd-bootx64.efi
/test-efi-disk.img
/scsi_id
/systemadm
/systemctl
Expand Down
121 changes: 121 additions & 0 deletions Makefile.am
Expand Up @@ -111,6 +111,7 @@ catalogdir=$(prefix)/lib/systemd/catalog
kernelinstalldir = $(prefix)/lib/kernel/install.d
factory_etcdir = $(prefix)/share/factory/etc
factory_pamdir = $(prefix)/share/factory/etc/pam.d
sd_bootlibdir = $(prefix)/lib/systemd/sd-boot

# And these are the special ones for /
rootprefix=@rootprefix@
Expand Down Expand Up @@ -2497,6 +2498,126 @@ dist_bashcompletion_DATA += \
dist_zshcompletion_DATA += \
shell-completion/zsh/_bootctl

# ------------------------------------------------------------------------------
efi_cppflags = \
$(EFI_CPPFLAGS) \
-I$(top_builddir) -include config.h \
-I$(EFI_INC_DIR)/efi \
-I$(EFI_INC_DIR)/efi/$(EFI_ARCH) \
-DEFI_MACHINE_TYPE_NAME=\"$(EFI_MACHINE_TYPE_NAME)\"

efi_cflags = \
$(EFI_CFLAGS) \
-Wall \
-Wextra \
-nostdinc \
-ggdb -O0 \
-fpic \
-fshort-wchar \
-nostdinc \
-ffreestanding \
-fno-strict-aliasing \
-fno-stack-protector \
-Wsign-compare \
-mno-sse \
-mno-mmx

if ARCH_X86_64
efi_cflags += \
-mno-red-zone \
-DEFI_FUNCTION_WRAPPER \
-DGNU_EFI_USE_MS_ABI
endif

efi_ldflags = \
$(EFI_LDFLAGS) \
-T $(EFI_LDS_DIR)/elf_$(EFI_ARCH)_efi.lds \
-shared \
-Bsymbolic \
-nostdlib \
-znocombreloc \
-L $(EFI_LIB_DIR) \
$(EFI_LDS_DIR)/crt0-efi-$(EFI_ARCH).o

# ------------------------------------------------------------------------------
sd_boot_headers = \
src/sd-boot/util.h \
src/sd-boot/console.h \
src/sd-boot/graphics.h \
src/sd-boot/pefile.h

sd_boot_sources = \
src/sd-boot/util.c \
src/sd-boot/console.c \
src/sd-boot/graphics.c \
src/sd-boot/pefile.c \
src/sd-boot/sd-boot.c

sd_boot_objects = $(addprefix $(top_builddir)/,$(sd_boot_sources:.c=.o))
sd_boot_solib = $(top_builddir)/src/sd-boot/sd_boot.so
sd_boot = sd-boot$(EFI_MACHINE_TYPE_NAME).efi

sd_bootlib_DATA = $(sd_boot)
CLEANFILES += $(sd_boot_objects) $(sd_boot_solib) $(sd_boot)
EXTRA_DIST += $(sd_boot_sources) $(sd_boot_headers)

$(top_builddir)/src/sd-boot/%.o: $(top_srcdir)/src/sd-boot/%.c $(addprefix $(top_srcdir)/,$(sd_boot_headers))
@$(MKDIR_P) $(top_builddir)/src/sd-boot/
$(AM_V_CC)$(EFI_CC) $(efi_cppflags) $(efi_cflags) -c $< -o $@

$(sd_boot_solib): $(sd_boot_objects)
$(AM_V_CCLD)$(LD) $(efi_ldflags) $(sd_boot_objects) \
-o $@ -lefi -lgnuefi $(shell $(CC) -print-libgcc-file-name); \
nm -D -u $@ | grep ' U ' && exit 1 || :

$(sd_boot): $(sd_boot_solib)
$(AM_V_GEN) objcopy -j .text -j .sdata -j .data -j .dynamic \
-j .dynsym -j .rel -j .rela -j .reloc \
--target=efi-app-$(EFI_ARCH) $< $@

# ------------------------------------------------------------------------------
stub_headers = \
src/sd-boot/util.h \
src/sd-boot/pefile.h \
src/sd-boot/linux.h

stub_sources = \
src/sd-boot/util.c \
src/sd-boot/pefile.c \
src/sd-boot/linux.c \
src/sd-boot/stub.c

stub_objects = $(addprefix $(top_builddir)/,$(stub_sources:.c=.o))
stub_solib = $(top_builddir)/src/sd-boot/stub.so
stub = linux$(EFI_MACHINE_TYPE_NAME).efi.stub

sd_bootlib_DATA += $(stub)
CLEANFILES += $(stub_objects) $(stub_solib) $(stub)
EXTRA_DIST += $(stub_sources) $(stub_headers)

$(top_builddir)/src/sd-boot/%.o: $(top_srcdir)/src/sd-boot/%.c $(addprefix $(top_srcdir)/,$(stub_headers))
@$(MKDIR_P) $(top_builddir)/src/sd-boot/
$(AM_V_CC)$(EFI_CC) $(efi_cppflags) $(efi_cflags) -c $< -o $@

$(stub_solib): $(stub_objects)
$(AM_V_CCLD)$(LD) $(efi_ldflags) $(stub_objects) \
-o $@ -lefi -lgnuefi $(shell $(CC) -print-libgcc-file-name); \
nm -D -u $@ | grep ' U ' && exit 1 || :

$(stub): $(stub_solib)
$(AM_V_GEN) objcopy -j .text -j .sdata -j .data -j .dynamic \
-j .dynsym -j .rel -j .rela -j .reloc \
--target=efi-app-$(EFI_ARCH) $< $@

# ------------------------------------------------------------------------------
CLEANFILES += test-efi-disk.img
EXTRA_DIST += test/test-efi-create-disk.sh

test-efi-disk.img: $(sd_boot) $(stub) test/test-efi-create-disk.sh
$(AM_V_GEN)test/test-efi-create-disk.sh

test-efi: test-efi-disk.img
$(QEMU) -machine accel=kvm -m 1024 -bios $(QEMU_BIOS) -snapshot test-efi-disk.img
endif

# ------------------------------------------------------------------------------
Expand Down
82 changes: 76 additions & 6 deletions configure.ac
Expand Up @@ -38,19 +38,17 @@ AM_INIT_AUTOMAKE([foreign 1.11 -Wall -Wno-portability silent-rules tar-pax no-di
AM_SILENT_RULES([yes])
AC_CANONICAL_HOST
AC_DEFINE_UNQUOTED([CANONICAL_HOST], "$host", [Canonical host string.])
AS_IF([test "x$host_cpu" = "xmips" || test "x$host_cpu" = "xmipsel" ||
test "x$host_cpu" = "xmips64" || test "x$host_cpu" = "xmips64el"],
[AC_DEFINE(ARCH_MIPS, [], [Whether on mips arch])])

LT_PREREQ(2.2)
LT_INIT([disable-static])

AS_IF([test "x$enable_static" = "xyes"], [AC_MSG_ERROR([--enable-static is not supported by systemd])])
AS_IF([test "x$enable_largefile" = "xno"], [AC_MSG_ERROR([--disable-largefile is not supported by systemd])])

# i18n stuff for the PolicyKit policy files
SET_ARCH(X86_64, x86_64*)
SET_ARCH(IA32, i*86*)
SET_ARCH(MIPS, mips*)

# Check whether intltool can be found, disable NLS otherwise
# i18n stuff for the PolicyKit policy files, heck whether intltool can be found, disable NLS otherwise
AC_CHECK_PROG(intltool_found, [intltool-merge], [yes], [no])
AS_IF([test x"$intltool_found" != xyes],
[AS_IF([test x"$enable_nls" = xyes],
Expand Down Expand Up @@ -1144,6 +1142,63 @@ if test "x$enable_efi" != "xno"; then
fi
AM_CONDITIONAL(ENABLE_EFI, [test "x$have_efi" = "xyes"])

# ------------------------------------------------------------------------------
EFI_CC=gcc
AC_SUBST([EFI_CC])

EFI_ARCH=`echo $host | sed "s/\(-\).*$//"`

AM_COND_IF(ARCH_IA32, [
EFI_ARCH=ia32
EFI_MACHINE_TYPE_NAME=ia32])

AM_COND_IF(ARCH_X86_64, [
EFI_MACHINE_TYPE_NAME=x64])

AC_SUBST([EFI_ARCH])
AC_SUBST([EFI_MACHINE_TYPE_NAME])

have_gnuefi=no
AC_ARG_ENABLE(gnuefi, AS_HELP_STRING([--enable-gnuefi], [Disable optional gnuefi support]))
AS_IF([test "x$enable_gnuefi" != "xno"], [
AC_CHECK_HEADERS(efi/${EFI_ARCH}/efibind.h,
[AC_DEFINE(HAVE_GNUEFI, 1, [Define if gnuefi is available])
have_gnuefi=yes],
[AS_IF([test "x$have_gnuefi" = xyes], [AC_MSG_ERROR([*** gnuefi support requested but headers not found])])
])
])
AM_CONDITIONAL(HAVE_GNUEFI, [test "$have_gnuefi" = "yes"])

if test "x$enable_gnuefi" != "xno"; then
efiroot=$(echo $(cd /usr/lib/$(gcc -print-multi-os-directory); pwd))

EFI_LIB_DIR="$efiroot"
AC_ARG_WITH(efi-libdir,
AS_HELP_STRING([--with-efi-libdir=PATH], [Path to efi lib directory]),
[EFI_LIB_DIR="$withval"], [EFI_LIB_DIR="$efiroot"]
)
AC_SUBST([EFI_LIB_DIR])

AC_ARG_WITH(efi-ldsdir,
AS_HELP_STRING([--with-efi-ldsdir=PATH], [Path to efi lds directory]),
[EFI_LDS_DIR="$withval"],
[
for EFI_LDS_DIR in "${efiroot}/gnuefi" "${efiroot}"; do
for lds in ${EFI_LDS_DIR}/elf_${EFI_ARCH}_efi.lds; do
test -f ${lds} && break 2
done
done
]
)
AC_SUBST([EFI_LDS_DIR])

AC_ARG_WITH(efi-includedir,
AS_HELP_STRING([--with-efi-includedir=PATH], [Path to efi include directory]),
[EFI_INC_DIR="$withval"], [EFI_INC_DIR="/usr/include"]
)
AC_SUBST([EFI_INC_DIR])
fi

# ------------------------------------------------------------------------------
AC_ARG_WITH(unifont,
AS_HELP_STRING([--with-unifont=PATH],
Expand Down Expand Up @@ -1392,6 +1447,14 @@ AS_IF([test "x$0" != "x./configure"], [
AC_SUBST([INTLTOOL_UPDATE], [/bin/true])
])

# QEMU and OVMF UEFI firmware
AS_IF([test x"$cross_compiling" = "xyes"], [], [
AC_PATH_PROG([QEMU], [qemu-system-x86_64])
AC_CHECK_FILE([/usr/share/qemu/bios-ovmf.bin], [QEMU_BIOS=/usr/share/qemu/bios-ovmf.bin])
AC_CHECK_FILE([/usr/share/qemu-ovmf/bios.bin], [QEMU_BIOS=/usr/share/qemu-ovmf/bios.bin])
AC_SUBST([QEMU_BIOS])
])

AC_ARG_ENABLE(tests,
[AC_HELP_STRING([--disable-tests], [disable tests])],
enable_tests=$enableval, enable_tests=yes)
Expand Down Expand Up @@ -1496,6 +1559,13 @@ AC_MSG_RESULT([
coredump: ${have_coredump}
polkit: ${have_polkit}
efi: ${have_efi}
gnuefi: ${have_gnuefi}
efi arch: ${EFI_ARCH}
EFI machine type: ${EFI_MACHINE_TYPE_NAME}
EFI CC ${EFI_CC}
EFI libdir: ${EFI_LIB_DIR}
EFI ldsdir: ${EFI_LDS_DIR}
EFI includedir: ${EFI_INC_DIR}
kmod: ${have_kmod}
xkbcommon: ${have_xkbcommon}
blkid: ${have_blkid}
Expand Down
13 changes: 13 additions & 0 deletions m4/arch.m4
@@ -0,0 +1,13 @@

dnl SET_ARCH(ARCHNAME, PATTERN)
dnl
dnl Define ARCH_<archname> condition if the pattern match with the current
dnl architecture
dnl
AC_DEFUN([SET_ARCH], [
cpu_$1=false
case "$host" in
$2) cpu_$1=true ;;
esac
AM_CONDITIONAL(AS_TR_CPP(ARCH_$1), [test "x$cpu_$1" = xtrue])
])
2 changes: 2 additions & 0 deletions src/sd-boot/.gitignore
@@ -0,0 +1,2 @@
/sd_boot.so
/stub.so

10 comments on commit 0fa2cac

@grahamc
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

objcopy \
  --add-section .osrel=/etc/os-release --change-section-vma .osrel=0x20000 \
  --add-section .cmdline=mnt/cmdline.txt --change-section-vma .cmdline=0x30000 \
  --add-section .linux=/boot/$(cat /etc/machine-id)/$(uname -r)/linux --change-section-vma .linux=0x40000 \
  --add-section .initrd=/boot/$(cat /etc/machine-id)/$(uname -r)/initrd --change-section-vma .initrd=0x3000000 \
  linuxx64.efi.stub mnt/EFI/Linux/linux-test.efi

These magic numbers have propagated around the internet, cargo-culted in to various guides to setting up secureboot. As far as I can tell, this is where they first came from. Do you know the origins of these values?

@kaysievers
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some time of fiddling in QEMU and later on real hardware until it started to work reliably. Just straight-forward guesswork :)

@grahamc
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Just checking, would it be reasonable to set each of those to, say (pseudocode):

offsetosrelease=0x20000
offsetcmdline=offsetosrelease+$(sizeof /etc/os-release)
offsetlinux=offsetcmdline+$(sizeof mnt/cmdline.txt)
offsetinitrd=offsetlinux+$(sizeof /boot/$(cat /etc/machine-id)/$(uname -r)/linux)

objcopy \
  --add-section .osrel=/etc/os-release --change-section-vma .osrel=$offsetosrelease \
  --add-section .cmdline=mnt/cmdline.txt --change-section-vma .cmdline=$offsetcmdline \
  --add-section .linux=/boot/$(cat /etc/machine-id)/$(uname -r)/linux --change-section-vma .linux=$offsetlinux \
  --add-section .initrd=/boot/$(cat /etc/machine-id)/$(uname -r)/initrd --change-section-vma .initrd=$offsetinitrd \
  linuxx64.efi.stub mnt/EFI/Linux/linux-test.efi

or am I misunderstanding what is going on here?

@kaysievers
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I never tried to figure out why exactly, but the initrd needed to be placed at the higher address. That wasn't just a different number.

All the other addresses did not matter, as long as there it enough room for the data, I think.

The time this all was tested, your setup would not boot. Might be all different today.

@gustingonzalez
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@grahamc, I tried the solution proposed by you and works well (the size can be obtained with 'stat -c%s $FILE'). Thanks!

@gustingonzalez
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to provide more information about my use case. Particularly, I also have a splash, and I had a problem with it, because (with the magic numbers, not the same as the ones presented here, but different) it was showing "incomplete". By implementing the proposed by @grahamc, this is fixed now.

Here is the implementation:

# Computes vma offsets.
OSRELEASE_OFFSET=$(echo $((0x20000)))
CMDLINE_OFFSET=$(expr $OSRELEASE_OFFSET + $(stat -c%s $OS_RELEASE))
KERNEL_OFFSET=$(expr $CMDLINE_OFFSET + $(stat -c%s $CMDLINE))
INITRD_OFFSET=$(expr $KERNEL_OFFSET + $(stat -c%s $KERNEL))
SPLASH_OFFSET=$(expr $INITRD_OFFSET + $(stat -c%s $INITRD))

# Converts vma offsets to hexas.
OSRELEASE_HEXA_OFFSET=$(printf "%x\n" $OSRELEASE_OFFSET)
CMDLINE_HEXA_OFFSET=$(printf "%x\n" $CMDLINE_OFFSET)
KERNEL_HEXA_OFFSET=$(printf "%x\n" $KERNEL_OFFSET)
INITRD_HEXA_OFFSET=$(printf "%x\n" $INITRD_OFFSET)
SPLASH_HEXA_OFFSET=$(printf "%x\n" $SPLASH_OFFSET)

# Generates EFI executable.
objcopy \
    --add-section .osrel=/etc/os-release --change-section-vma .osrel=0x$OSRELEASE_HEXA_OFFSET \
    --add-section .cmdline="$CMDLINE_DIR/cmdline.txt" --change-section-vma .cmdline=0x$CMDLINE_HEXA_OFFSET \
    --add-section .linux="$KERNEL" --change-section-vma .linux=0x$KERNEL_HEXA_OFFSET \
    --add-section .initrd="$INITRD" --change-section-vma .initrd=0x$INITRD_HEXA_OFFSET \
    --add-section .splash="$SPLASH" --change-section-vma .splash=0x$SPLASH_HEXA_OFFSET \
   linuxx64.efi.stub mnt/EFI/Linux/linux-test.efi

Anyway, I've also tried without the 'change-section-vma' and it seems to work fine. Am I missing something?

@random-user-00
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, objcopy is used to modify existing 'systemd-stub binary' linuxx64.efi.stub by adding new sections(--add-section) and save it as new EFI kernel image. Theoretically, if the address of newly added section don't overlap with the previous one, there should not be any problem.
Address layout and size of 'systemd-stub binary' can be checked using objdump utility.

$ objdump -f -h /usr/lib/systemd/boot/efi/linuxx64.efi.stub

/usr/lib/systemd/boot/efi/linuxx64.efi.stub:     file format pei-x86-64
architecture: i386:x86-64, flags 0x00000133:
HAS_RELOC, EXEC_P, HAS_SYMS, HAS_LOCALS, D_PAGED
start address 0x0000000000004000

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         0000ccc0  0000000000004000  0000000000004000  000002e0  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .reloc        0000000a  0000000000011000  0000000000011000  0000d0e0  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .data         00003348  0000000000012000  0000000000012000  0000d2e0  2**4
                  CONTENTS, ALLOC, LOAD, DATA
  3 .dynamic      00000120  0000000000016000  0000000000016000  000106e0  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  4 .rela         00000f78  0000000000017000  0000000000017000  000108e0  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .dynsym       00000018  0000000000018000  0000000000018000  000118e0  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  6 .sbat         0000010f  000000000001a7a0  000000000001a7a0  00011ae0  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  7 .sdmagic      00000027  000000000001a8c0  000000000001a8c0  00011ce0  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA

Last section .sdmagic is at 1a8c0(VMA column) and has size of 27. So the next section(.osrel) can start after 0x1a8c0 + 0x27 = 1a8e7, and so on. The caveat is that, future versions of systemd-stub binary might look different than this.

One thing I noticed that, even the systemd-stub binary is not aligned perfectly and section addresses start with some round numbers(4000, 11000, 12000, 16000, 17000...). Or these might be some magic addresses.

Using the script provided by @gustingonzalez , I set the OSRELEASE_OFFSET=$(echo $((0x1a8e8))) which works perfectly.

Kernel EFI image now includes original sections from systemd-stub and added sections .osrel, .cmdline, .linux, .initrd.
.osrel section is exactly at 0x1a8e8 as expected.

$ objdump -f -h clearlinux.native.5.18.14-1162.efi

clearlinux.native.5.18.14-1162.efi:     file format pei-x86-64
architecture: i386:x86-64, flags 0x00000133:
HAS_RELOC, EXEC_P, HAS_SYMS, HAS_LOCALS, D_PAGED
start address 0x0000000000004000

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         0000ccc0  0000000000004000  0000000000004000  00000370  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .reloc        0000000a  0000000000011000  0000000000011000  0000d170  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .data         00003348  0000000000012000  0000000000012000  0000d370  2**4
                  CONTENTS, ALLOC, LOAD, DATA
  3 .dynamic      00000120  0000000000016000  0000000000016000  00010770  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  4 .rela         00000f78  0000000000017000  0000000000017000  00010970  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .dynsym       00000018  0000000000018000  0000000000018000  00011970  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  6 .sbat         0000010f  000000000001a7a0  000000000001a7a0  00011b70  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  7 .sdmagic      00000027  000000000001a8c0  000000000001a8c0  00011d70  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  8 .osrel        00000142  000000000001a8e8  000000000001a8e8  00011f70  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  9 .cmdline      0000018c  000000000001aa2a  000000000001aa2a  00012170  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 10 .linux        00bdbb00  000000000001abb6  000000000001abb6  00012370  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 11 .initrd       023955b7  0000000000bf66b6  0000000000bf66b6  00bedf70  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA

@gustingonzalez
Copy link

@gustingonzalez gustingonzalez commented on 0fa2cac Sep 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I bring this topic up for discussion again. Previously, I commented that the proposed implementation by using dynamic offsets (originally outlined by @grahamc) was working well for me. Well... That was true on Ubuntu 20.04 with 5.x kernels. Latter, I realized that it wasn’t true for Ubuntu 16.04 and 4.x kernels, where I was getting the following error:

imagen

For the case, the EFI components were arranged as the following: [OSRELEASE, CMDLINE, SPLASH, KERNEL, INITRD]. Although that is in line with the indicated by @kaysievers, placing the initrd at the higher address was not working for me in 16.04, of course, when using dynamic offsets and the proposed EFI components arrangement. Then, and following the idea of the initrd position, I tried placing it in the lowest memory space (instead of the highest one). That is: [INITRD, OSRELEASE, CMDLINE, SPLASH, KERNEL]. Luckily, this worked fine on both 16.04 and 20.04! However, this was still some shady for me...

In a following experiment, I changed my original configuration, but now moving the kernel after the splash, that is: [OSRELEASE, CMDLINE, KERNEL, SPLASH, INITRD]. And that worked fine, too! So, I started to suspect that the problem with that was actually the kernel position, and not the initrd one. Searching on how these components are loaded, I found the following article: Linux Boot Process — Part 2. More specifically, the part that shed light for me was:

“The kernel is decompressed in place: the memory pages holding the compressed image are replaced by the decompressed data.”

Then, if at an early initialization stage the kernel is decompressed in place, it now made more sense that initrd could not be loaded with the configuration [OSRELEASE, CMDLINE, SPLASH, KERNEL, INITRD]. Specifically, it seems that in this case, the decompressed kernel (which, in theory, now takes up more space than before) overlaps some bytes of the initrd.

In a nutshell, the solution was to put the kernel in the last position. For the case, the configuration [OSRELEASE, CMDLINE, SPLASH, INITRD, KERNEL] works fine for me, regardless of the arrangement of the other components behind the kernel.

But, what about this? In the following case, the kernel is behind the initrd!:

objcopy \
  --add-section .osrel=/etc/os-release --change-section-vma .osrel=0x20000 \
  --add-section .cmdline=mnt/cmdline.txt --change-section-vma .cmdline=0x30000 \
  --add-section .linux=/boot/$(cat /etc/machine-id)/$(uname -r)/linux --change-section-vma .linux=0x40000 \
  --add-section .initrd=/boot/$(cat /etc/machine-id)/$(uname -r)/initrd --change-section-vma .initrd=0x3000000 \
  linuxx64.efi.stub mnt/EFI/Linux/linux-test.efi

Well, if we look at the difference between the initrd section beginning and the kernel section beginning, we have 0x3000000 - 0x40000 = 0x2FC0000 = 50,069,504 bytes for the kernel area. That is, we have ~50 MB available. For the case of the kernels that I tested, these did not exceed ~20 MB. So it seems reasonable that reserving more than twice the size of the kernel works well.

However, what about the fact that the initially described EFI components arrangement fails with 16.04, but works well with 20.04? Well, in this case, I don't have a formed answer. I honestly don't know how kernel decompression works in more technical terms, but I guess maybe this is done differently between both versions. Suggestions and explanations are of course welcome!

Finally, I think that kernel decompression as the cause of the issue I had makes a lot of sense, but if I'm wrong about anything, please let me know. As I said, I don't really have a very technical understanding of how the kernel decompression is done, beyond what is described above.

@gustingonzalez
Copy link

@gustingonzalez gustingonzalez commented on 0fa2cac Sep 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The following is a gist containing an implementation to generate an image in the mentioned way: https://gist.github.com/gustingonzalez/d92338c63396f0a34b01d9da781735cb.

@brandsimon
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.