Replace the fork+exec of /usr/bin/kexec in load_kexec_kernel() with a direct kexec_file_load() syscall, removing the runtime dependency on kexec-tools for systemctl kexec. The kexec_file_load() syscall (available since Linux 3.17) accepts kernel and initrd file descriptors directly, letting the kernel handle image parsing, segment setup, and purgatory internally. This is much simpler than the older kexec_load() syscall which requires complex userspace setup of memory segments and boot protocol structures — that complexity is the raison d'être of kexec-tools. The implementation follows the established libc wrapper pattern: a missing_kexec_file_load() fallback in src/libc/kexec.c calls the syscall directly when glibc doesn't provide a wrapper (which is currently always the case). The syscall is not available on all architectures — alpha, i386, ia64, m68k, mips, sh, and sparc lack __NR_kexec_file_load — so the wrapper and caller are guarded with HAVE_KEXEC_FILE_LOAD_SYSCALL to compile cleanly everywhere. When kexec_file_load() rejects the kernel image with ENOEXEC (e.g. the image is compressed or wrapped in a PE container that the kernel's kexec handler doesn't understand natively), we attempt to unwrap/decompress and retry. This is effectively the same decompression and extraction logic that already lives in src/ukify/ukify.py (maybe_decompress() and get_zboot_kernel()), now implemented in C so that systemctl can handle it natively without shelling out to external tools: - Compressed kernels (Image.gz, Image.zst, Image.xz, Image.lz4): the format is detected by magic bytes (per RFC 1952, RFC 8878, tukaani.org xz spec, and lz4 frame format spec) and decompressed to a memfd using the existing decompress_stream_*() infrastructure plus the new gzip support from the previous commit. This is primarily needed on arm64 where kexec_file_load() only accepts raw Image files. On x86_64, bzImage is already the native format and works directly. - EFI ZBOOT PE images (vmlinuz.efi): detected by "MZ" + "zimg" magic at the start of the file. The compressed payload offset, size, and compression type are read from the ZBOOT header defined in linux/drivers/firmware/efi/libstub/zboot-header.S. - Unified Kernel Images (UKI): detected as PE files with a .linux section via the existing pe_is_uki() infrastructure. The .linux section (kernel) and optionally .initrd section are extracted to memfds. When a UKI provides an embedded initrd and the boot entry doesn't specify one separately, the embedded initrd is used. The try-first-then-decompress approach means we never decompress unnecessarily: on x86_64 the first kexec_file_load() call succeeds immediately with the raw bzImage, and on architectures where the kernel's kexec handler natively understands PE (like LoongArch with kexec_efi_ops), ZBOOT/UKI images work without decompression too. If kexec_file_load() is unavailable (architectures without the syscall) or all attempts fail, we fall back to forking+execing the kexec binary. This preserves compatibility on architectures like i386 and mips where only the older kexec_load() syscall exists and kexec-tools is needed to handle the complex userspace setup. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
34 lines
1.3 KiB
C
34 lines
1.3 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
#pragma once
|
|
|
|
#include <linux/reboot.h> /* IWYU pragma: export */
|
|
#include <sys/reboot.h> /* IWYU pragma: export */
|
|
|
|
#include "shared-forward.h"
|
|
|
|
/* glibc defines the reboot() API call, which is a wrapper around the system call of the same name, but
|
|
* without the extra "arg" parameter. Since we need that parameter for some calls, let's add a "raw" wrapper
|
|
* that is defined the same way, except it takes the additional argument. */
|
|
int raw_reboot(int cmd, const void *arg);
|
|
|
|
bool reboot_parameter_is_valid(const char *parameter);
|
|
int update_reboot_parameter_and_warn(const char *parameter, bool keep);
|
|
|
|
typedef enum RebootFlags {
|
|
REBOOT_LOG = 1 << 0, /* log about what we are going to do and all errors */
|
|
REBOOT_DRY_RUN = 1 << 1, /* return 0 right before actually doing the reboot */
|
|
REBOOT_FALLBACK = 1 << 2, /* fall back to plain reboot() if argument-based reboot doesn't work, isn't configured or doesn't apply otherwise */
|
|
} RebootFlags;
|
|
|
|
int read_reboot_parameter(char **parameter);
|
|
int reboot_with_parameter(RebootFlags flags);
|
|
|
|
bool shall_restore_state(void);
|
|
|
|
bool kexec_loaded(void);
|
|
int kexec(void);
|
|
|
|
int kexec_maybe_decompress_kernel(const char *path, int fd, int *ret_kernel_fd, int *ret_initrd_fd);
|
|
|
|
int create_shutdown_run_nologin_or_warn(void);
|