load_kexec_kernel() retries kexec_file_load() with an extracted kernel
(decompressed Image / ZBOOT PE / UKI) when the kernel rejects the image,
but it only does so when kexec_file_load() failed with ENOEXEC. On arm64
that retry never happens: arm64's image_probe()
(arch/arm64/kernel/kexec_image.c) returns -EINVAL on an ARM64_IMAGE_MAGIC
mismatch, whereas x86's bzImage64_probe() and the generic
kexec_image_probe_default() return -ENOEXEC. So `systemctl kexec` of a
UKI on arm64 skips the extraction path and falls back to the
/usr/sbin/kexec binary, which is no longer a dependency since
e107c7ead0
("systemctl: replace kexec-tools dependency with direct kexec_file_load()
syscall") -- leaving kexec broken.
Accept EINVAL in addition to ENOEXEC. This is safe: the extraction in
kexec_maybe_decompress_kernel() re-gates on the actual file magic (MZ /
compression headers) and is a no-op returning 0 for anything else, so an
EINVAL that is not a format mismatch just falls through to the existing
fallback as before.
Fixing this in systemd (rather than only in the kernel) is appropriate:
systemd must keep working with already-shipped arm64 kernels whose
kexec_file_load() returns EINVAL for an unrecognized image magic.
Relates to: https://github.com/systemd/systemd/issues/28538
Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
int saved_errno = errno;
- if (saved_errno == ENOEXEC) {
+ if (IN_SET(saved_errno, ENOEXEC, EINVAL)) {
/* The kernel didn't recognize the image format. Try decompressing or extracting the
- * kernel (e.g. compressed Image, ZBOOT PE, or UKI) and loading again. */
+ * kernel (e.g. compressed Image, ZBOOT PE, or UKI) and loading again.
+ *
+ * Most architectures' kexec_file_load() returns ENOEXEC when no image loader matches
+ * (the default behavior of kexec_image_probe_default()), but arm64's image_probe()
+ * returns EINVAL for an unrecognized magic. Accept both, otherwise `systemctl kexec`
+ * of a UKI never reaches the extraction path on arm64. */
log_debug_errno(saved_errno, "Kernel rejected image, trying decompression/extraction: %m");
_cleanup_close_ int extracted_kernel_fd = -EBADF, extracted_initrd_fd = -EBADF;