public inbox for kexec@lists.infradead.org
 help / color / mirror / Atom feed
* [PATCHv7 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image
@ 2026-03-22  1:43 Pingfan Liu
  2026-03-22  1:43 ` [PATCHv7 01/13] bpf: Introduce kfuncs to parser buffer content Pingfan Liu
                   ` (13 more replies)
  0 siblings, 14 replies; 26+ messages in thread
From: Pingfan Liu @ 2026-03-22  1:43 UTC (permalink / raw)
  Cc: Pingfan Liu, David S. Miller, Alexei Starovoitov, Daniel Borkmann,
	John Fastabend, Andrii Nakryiko, Martin KaFai Lau,
	Eduard Zingerman, Song Liu, Yonghong Song, Jeremy Linton,
	Catalin Marinas, Will Deacon, Ard Biesheuvel, Simon Horman,
	Gerd Hoffmann, Vitaly Kuznetsov, Philipp Rudo, Viktor Malik,
	Jan Hendrik Farr, Baoquan He, Dave Young, Andrew Morton, kexec,
	bpf, systemd-devel, linux-kernel

*** The history ***

Nowadays, UEFI PE bootable images are becoming increasingly popular
among distributions. Currently, we have several kinds of image format
parsers in user space (kexec-tools). However, this approach breaks the
integrity protection of the images. To address this integrity protection
concern, several approaches have been proposed to resolve this issue,
but none of them have been accepted upstream yet.

The summary of those approaches:
  -1. UEFI service emulator for UEFI stub
  -2. PE format parser in kernel
  -3. Signing the arm64/boot/Image


For the first approach, I tried a purgatory-style emulator [1], but it
encounters hardware scaling issues. For the second approach, both
zboot-format [2] and UKI-format [3] parsers were rejected due to
concerns that variant format parsers would bloat the kernel code.
Additionally, for example in arm64, both UKI and zboot format parsers
would need to be introduced and chained together to handle image
loading. For the third approach, I attempted [4], but since zboot or UKI
images already have signatures, upstream maintainers dislike the
additional signature on the Image. Moreover, for secure boot UKI, this
method cannot use signatures to protect the initramfs.


*** The approach in this series ***

This series introduces an approach that allows image formats to be
parsed by BPF programs. Through BPF, two main functions, collecting
kernel boot protocol data and decompression, are exposed to BPF programs
via the commands KEXEC_BPF_CMD_DECOMPRESS and KEXEC_BPF_CMD_COPY.
As a result, the kexec kernel code can remain relatively stable without
requiring new parsers for different architectures.

Beside above, this approach redesigns kexec_file_load(): kernel_fd is
used to pass the BPF parser, while initrd_fd can be used to carry
everything else, the kernel image, initrd, and cmdline data. So we can
avoid changing the kexe_file_load() protocol. 


The main purpose of this series is to provide format flexibility. This
is demonstrated by the patch 'tools/kexec: Introduce a bpf-prog to
handle zboot image', where the BPF program works in conjunction with a
private format defined in 'tools/kexec: Introduce a tool to build zboot
envelope'.


*** Outlook for the security support (the next step) ***

The remaining issue in this series is the security support.
The isolated bpf-prog can be signed as what is done on kernel module,
and verified as the trusted root later.
Each signed PE can be asked to be verified by the trusted bpf-prog.

For IMA, an out-most bpf-parser will be introduced to read each file by
kernel_read_file_from_path(..., READING_KEXEC_IMAGE).


*** Thanks ***
I would like to thank Philipp Rudo, whose insights inspired this
approach and who dedicated significant time to evaluating its
practicality. I am also grateful to Viktor Malik for his guidance on
using BPF light skeleton to prevent malicious attacks from user space.


*** Test approach ***
-1. compile kexec-tools from https://github.com/pfliu/kexec-tools/tree/bpf_loader
-2. compile kernel
-3. For UKI, get the UKI parser bpf-prog by 'make -C tools/kexec uki',
    there will be uki.bpf.
    Load it by:
        kexec -s -l uki.bpf image.uki

    For zboot, get the zboot parser by 'make -C tools/kexec zboot'
    create the zboot envelop (packing vmlinuz.efi, initrd, cmdline) by:
        tools/kexec/build_zboot_envelop vmlinuz.efi initrd "cmdline"
    The default envelop image is named "zboot_image.elf"
    Load it by:
        kexec -s -l zboot.bpf zboot_image.elf


[1]: https://lore.kernel.org/lkml/20240819145417.23367-1-piliu@redhat.com/T/
[2]: https://lore.kernel.org/kexec/20230306030305.15595-1-kernelfans@gmail.com/
[3]: https://lore.kernel.org/lkml/20230911052535.335770-1-kernel@jfarr.cc/
[4]: https://lore.kernel.org/linux-efi/20230921133703.39042-1-kernelfans@gmail.com/


*** Changes ***
v6 -> v7:
  - Redesign the kexec_file_load interface to prevent BPF section injection
    into UKI or zboot images. The kernel_fd now carries the BPF parser,
    while initrd_fd carries the UKI image.
  - Introduce a UKI image parser.
  - Enhance the zboot parser to support both UKI and direct zboot images.
  - Fix the bug reported by bot+bpf-ci


v5 -> v6
  - Re-organize the layers in kexec_file_load into two layers: format-parsing and kernel boot protocol handling.
  - Simplify the bpf kfunc interface.
  - rebased onto Linux 6.19-rc2

v4 -> v5
  - rebased onto Linux 6.17-rc2
  - [1/12], use a separate CONFIG_KEEP_COMPRESSOR to decide the section
    of decompressor method
  - [10/12], add Catalin's acked-by (Thanks Catalin!)

v3 -> v4
  - Use dynamic allocator in decompression ([4/12])
  - Fix issue caused by Identical Code Folding ([5/12])
  - Integrate the image generator tool in the kernel tree ([11,12/12])
  - Address the issue according to Philipp's comments in v3 reviewing.
    Thanks Philipp!

RFCv2 -> v3
  - move the introduced bpf kfuncs to kernel/bpf/* and mark them sleepable
  - use listener and publisher model to implement bpf_copy_to_kernel()
  - keep each introduced kfunc under the control of memcg

RFCv1 -> RFCv2
  - Use bpf kfunc instead of helper
  - Use C source code to generate the light skeleton file


Pingfan Liu (13):
  bpf: Introduce kfuncs to parser buffer content
  kexec_file: Use bpf-prog to decompose image
  lib/decompress: Keep decompressor when CONFIG_KEEP_DECOMPRESSOR
  kexec_file: Implement decompress method for parser
  kexec_file: Implement copy method for parser
  kexec_file: Chain the stages into a pipeline
  kexec_file: Introduce a bpf-prog lskel to run a format parser
  kexec_file: Factor out routine to find a symbol in ELF
  kexec_file: Integrate bpf light skeleton to load image with bpf-prog
  arm64/kexec: Select KEXEC_BPF to support UEFI-style kernel image
  tools/kexec: Introduce a bpf-prog to handle zboot image
  tools/kexec: Introduce a bpf-prog to handle UKI image
  tools/kexec: Introduce a tool to build zboot envelop

 arch/arm64/Kconfig                           |   1 +
 include/linux/bpf.h                          |  20 +
 include/linux/decompress/mm.h                |   8 +
 kernel/Kconfig.kexec                         |   8 +
 kernel/Makefile                              |   2 +
 kernel/bpf/Makefile                          |   3 +
 kernel/bpf/bpf_buffer_parser.c               | 186 ++++
 kernel/kexec_bpf/Makefile                    |  70 ++
 kernel/kexec_bpf/kexec_pe_parser_bpf.c       |  12 +
 kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h | 130 +++
 kernel/kexec_bpf/template.c                  |  72 ++
 kernel/kexec_bpf_loader.c                    | 873 +++++++++++++++++++
 kernel/kexec_file.c                          | 104 ++-
 kernel/kexec_internal.h                      |   5 +
 lib/Kconfig                                  |   6 +
 lib/decompress.c                             |   6 +-
 tools/kexec/Makefile                         | 203 +++++
 tools/kexec/build_zboot_envelop.c            | 362 ++++++++
 tools/kexec/template.c                       |  72 ++
 tools/kexec/uki_parser_bpf.c                 | 235 +++++
 tools/kexec/zboot_envelop.h                  |   7 +
 tools/kexec/zboot_parser_bpf.c               | 348 ++++++++
 22 files changed, 2719 insertions(+), 14 deletions(-)
 create mode 100644 kernel/bpf/bpf_buffer_parser.c
 create mode 100644 kernel/kexec_bpf/Makefile
 create mode 100644 kernel/kexec_bpf/kexec_pe_parser_bpf.c
 create mode 100644 kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h
 create mode 100644 kernel/kexec_bpf/template.c
 create mode 100644 kernel/kexec_bpf_loader.c
 create mode 100644 tools/kexec/Makefile
 create mode 100644 tools/kexec/build_zboot_envelop.c
 create mode 100644 tools/kexec/template.c
 create mode 100644 tools/kexec/uki_parser_bpf.c
 create mode 100644 tools/kexec/zboot_envelop.h
 create mode 100644 tools/kexec/zboot_parser_bpf.c

-- 
2.49.0



^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCHv7 01/13] bpf: Introduce kfuncs to parser buffer content
  2026-03-22  1:43 [PATCHv7 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
@ 2026-03-22  1:43 ` Pingfan Liu
  2026-03-22  2:14   ` bot+bpf-ci
  2026-03-22  1:43 ` [PATCHv7 02/13] kexec_file: Use bpf-prog to decompose image Pingfan Liu
                   ` (12 subsequent siblings)
  13 siblings, 1 reply; 26+ messages in thread
From: Pingfan Liu @ 2026-03-22  1:43 UTC (permalink / raw)
  To: bpf
  Cc: Pingfan Liu, David S. Miller, Alexei Starovoitov, Daniel Borkmann,
	John Fastabend, Andrii Nakryiko, Martin KaFai Lau,
	Eduard Zingerman, Song Liu, Yonghong Song, Jeremy Linton,
	Catalin Marinas, Will Deacon, Ard Biesheuvel, Simon Horman,
	Gerd Hoffmann, Vitaly Kuznetsov, Philipp Rudo, Viktor Malik,
	Jan Hendrik Farr, Baoquan He, Dave Young, Andrew Morton, kexec,
	systemd-devel, linux-kernel, KP Singh, Stanislav Fomichev,
	Hao Luo, Jiri Olsa

In the security kexec_file_load case, the buffer holding the kernel
image should not be accessible from userspace.

Typically, BPF data flow occurs between user space and kernel space in
either direction. However, the above case presents a unique scenario
where the kernel, instead of a user task, reads data from a file, passes
it to a BPF program for parsing, and finally stores the parsed result.

This requires a mechanism to channel the intermediate data from the BPF
program directly to the kernel. BPF buffer parser kfuncs are introduced
to serve this purpose:

    BTF_ID_FLAGS(func, bpf_get_parser_context, KF_ACQUIRE | KF_RET_NULL)
    BTF_ID_FLAGS(func, bpf_put_parser_context, KF_RELEASE)
    BTF_ID_FLAGS(func, bpf_buffer_parser, KF_SLEEPABLE)

where bpf_get_parser_context() and bpf_put_parser_context() manage the
trusted argument, and bpf_buffer_parser() forwards data to a callback
that processes the structured buffer constructed by the BPF program.

Signed-off-by: Pingfan Liu <piliu@redhat.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: David S. Miller <davem@davemloft.net>
Cc: John Fastabend <john.fastabend@gmail.com>
Cc: Andrii Nakryiko <andrii@kernel.org>
Cc: Martin KaFai Lau <martin.lau@linux.dev>
Cc: Eduard Zingerman <eddyz87@gmail.com>
Cc: Song Liu <song@kernel.org>
Cc: Yonghong Song <yonghong.song@linux.dev>
Cc: KP Singh <kpsingh@kernel.org>
Cc: Stanislav Fomichev <sdf@fomichev.me>
Cc: Hao Luo <haoluo@google.com>
Cc: Jiri Olsa <jolsa@kernel.org>
To: bpf@vger.kernel.org
---
 include/linux/bpf.h            |  20 ++++
 kernel/bpf/Makefile            |   3 +
 kernel/bpf/bpf_buffer_parser.c | 186 +++++++++++++++++++++++++++++++++
 3 files changed, 209 insertions(+)
 create mode 100644 kernel/bpf/bpf_buffer_parser.c

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 05b34a6355b03..93a1c9163685f 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -4005,4 +4005,24 @@ static inline int bpf_map_check_op_flags(struct bpf_map *map, u64 flags, u64 all
 	return 0;
 }
 
+struct bpf_parser_buf {
+	char *buf;
+	int size;
+};
+
+struct bpf_parser_context;
+typedef int (*bpf_parser_handler_t)(struct bpf_parser_context *ctx);
+
+struct bpf_parser_context {
+	struct kref ref;
+	struct hlist_node hash_node;
+	/* This callback should be sync so that @buf can be freed */
+	bpf_parser_handler_t func;
+	struct bpf_parser_buf *buf;
+	void *data;
+};
+
+struct bpf_parser_context *alloc_bpf_parser_context(bpf_parser_handler_t func,
+			void *data);
+void put_bpf_parser_context(struct bpf_parser_context *ctx);
 #endif /* _LINUX_BPF_H */
diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
index 79cf22860a99b..cceff165b4037 100644
--- a/kernel/bpf/Makefile
+++ b/kernel/bpf/Makefile
@@ -66,6 +66,9 @@ obj-$(CONFIG_BPF_SYSCALL) += kmem_cache_iter.o
 ifeq ($(CONFIG_DMA_SHARED_BUFFER),y)
 obj-$(CONFIG_BPF_SYSCALL) += dmabuf_iter.o
 endif
+ifeq ($(CONFIG_KEXEC_BPF),y)
+obj-$(CONFIG_BPF_SYSCALL) += bpf_buffer_parser.o
+endif
 
 CFLAGS_REMOVE_percpu_freelist.o = $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_bpf_lru_list.o = $(CC_FLAGS_FTRACE)
diff --git a/kernel/bpf/bpf_buffer_parser.c b/kernel/bpf/bpf_buffer_parser.c
new file mode 100644
index 0000000000000..5d5c068330791
--- /dev/null
+++ b/kernel/bpf/bpf_buffer_parser.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/hashtable.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+#include <linux/vmalloc.h>
+#include <linux/bpf.h>
+
+#define BPF_CONTEXT_HASH_BITS 10
+
+static DEFINE_SPINLOCK(bpf_parser_context_lock);
+static DEFINE_HASHTABLE(bpf_parser_context_map, BPF_CONTEXT_HASH_BITS);
+
+/* Generate a simple hash key from pointer address */
+static inline unsigned int bpf_parser_context_hash_key(struct bpf_parser_context *ctx)
+{
+	return hash_ptr(ctx, BPF_CONTEXT_HASH_BITS);
+}
+
+static void release_bpf_parser_context(struct kref *kref)
+{
+	struct bpf_parser_context *ctx = container_of(kref, struct bpf_parser_context, ref);
+
+	if (ctx->buf) {
+		vfree(ctx->buf->buf);
+		kfree(ctx->buf);
+	}
+	spin_lock(&bpf_parser_context_lock);
+	hash_del(&ctx->hash_node);
+	spin_unlock(&bpf_parser_context_lock);
+	kfree(ctx);
+}
+
+struct bpf_parser_context *alloc_bpf_parser_context(bpf_parser_handler_t func,
+		void *data)
+{
+	struct bpf_parser_context *ctx;
+	unsigned int key;
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return NULL;
+	ctx->func = func;
+	ctx->data = data;
+	kref_init(&ctx->ref);
+	key = bpf_parser_context_hash_key(ctx);
+	spin_lock(&bpf_parser_context_lock);
+	hash_add(bpf_parser_context_map, &ctx->hash_node, key);
+	spin_unlock(&bpf_parser_context_lock);
+
+	return ctx;
+}
+
+void put_bpf_parser_context(struct bpf_parser_context *ctx)
+{
+	if (!ctx)
+		return;
+	kref_put(&ctx->ref, release_bpf_parser_context);
+}
+
+static struct bpf_parser_context *find_bpf_parser_context(unsigned long id)
+{
+	struct bpf_parser_context *ctx;
+	unsigned int key;
+	int cnt;
+
+	key = bpf_parser_context_hash_key((struct bpf_parser_context *)id);
+	spin_lock(&bpf_parser_context_lock);
+	hash_for_each_possible(bpf_parser_context_map, ctx, hash_node, key) {
+		if (ctx == (struct bpf_parser_context *)id) {
+			cnt = kref_get_unless_zero(&ctx->ref);
+			if (!cnt)
+				ctx = NULL;
+			spin_unlock(&bpf_parser_context_lock);
+			return ctx;
+		}
+	}
+	spin_unlock(&bpf_parser_context_lock);
+
+	return NULL;
+}
+
+__bpf_kfunc_start_defs()
+
+__bpf_kfunc struct bpf_parser_context *bpf_get_parser_context(unsigned long id)
+{
+	struct bpf_parser_context *ctx;
+
+	ctx = find_bpf_parser_context(id);
+
+	return ctx;
+}
+
+__bpf_kfunc void bpf_put_parser_context(struct bpf_parser_context *ctx)
+{
+	put_bpf_parser_context(ctx);
+}
+
+__bpf_kfunc void bpf_parser_context_release_dtor(void *ctx)
+{
+	put_bpf_parser_context(ctx);
+}
+CFI_NOSEAL(bpf_parser_context_release_dtor);
+
+__bpf_kfunc int bpf_buffer_parser(char *buf, int buf_sz,
+		struct bpf_parser_context *context)
+{
+	struct bpf_parser_buf *parser_buf;
+	void *old_val;
+	int ret;
+	char *b;
+
+	if (buf == NULL || buf_sz <= 0)
+		return -EINVAL;
+
+	if (unlikely(context->func == NULL))
+		return -EINVAL;
+
+	/* Lock the pointer */
+	old_val = cmpxchg(&context->buf, NULL, (void *)1);
+	if (old_val != NULL)
+		return -EBUSY;
+	b = __vmalloc(buf_sz, GFP_KERNEL_ACCOUNT | __GFP_ZERO);
+	if (!b) {
+		context->buf = NULL;
+		return -ENOMEM;
+	}
+	ret = copy_from_kernel_nofault(b, buf, buf_sz);
+	if (!!ret) {
+		context->buf = NULL;
+		vfree(b);
+		return ret;
+	}
+
+	parser_buf = kmalloc(sizeof(struct bpf_parser_buf), GFP_KERNEL);
+	if (!parser_buf) {
+		vfree(b);
+		context->buf = NULL;
+		return -ENOMEM;
+	}
+	parser_buf->buf = b;
+	parser_buf->size = buf_sz;
+	context->buf = parser_buf;
+	/* @func should be a sync call */
+	ret = context->func(context);
+	context->buf = NULL;
+	vfree(b);
+	kfree(parser_buf);
+
+	return ret;
+}
+__bpf_kfunc_end_defs();
+
+BTF_KFUNCS_START(buffer_parser_ids)
+BTF_ID_FLAGS(func, bpf_get_parser_context, KF_ACQUIRE | KF_RET_NULL)
+BTF_ID_FLAGS(func, bpf_put_parser_context, KF_RELEASE)
+BTF_ID_FLAGS(func, bpf_buffer_parser, KF_SLEEPABLE)
+BTF_KFUNCS_END(buffer_parser_ids)
+
+static const struct btf_kfunc_id_set buffer_parser_kfunc_set = {
+        .owner = THIS_MODULE,
+        .set   = &buffer_parser_ids,
+};
+
+
+BTF_ID_LIST(buffer_parser_dtor_ids)
+BTF_ID(struct, bpf_parser_context)
+BTF_ID(func, bpf_parser_context_release_dtor)
+
+static int __init buffer_parser_kfunc_init(void)
+{
+	int ret;
+	const struct btf_id_dtor_kfunc buffer_parser_dtors[] = {
+		{
+			.btf_id	      = buffer_parser_dtor_ids[0],
+			.kfunc_btf_id = buffer_parser_dtor_ids[1]
+		},
+	};
+
+	ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &buffer_parser_kfunc_set);
+	return  ret ?: register_btf_id_dtor_kfuncs(buffer_parser_dtors,
+						   ARRAY_SIZE(buffer_parser_dtors),
+						   THIS_MODULE);
+}
+
+late_initcall(buffer_parser_kfunc_init);
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCHv7 02/13] kexec_file: Use bpf-prog to decompose image
  2026-03-22  1:43 [PATCHv7 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
  2026-03-22  1:43 ` [PATCHv7 01/13] bpf: Introduce kfuncs to parser buffer content Pingfan Liu
@ 2026-03-22  1:43 ` Pingfan Liu
  2026-03-22  2:14   ` bot+bpf-ci
  2026-03-22  1:43 ` [PATCHv7 03/13] lib/decompress: Keep decompressor when CONFIG_KEEP_DECOMPRESSOR Pingfan Liu
                   ` (11 subsequent siblings)
  13 siblings, 1 reply; 26+ messages in thread
From: Pingfan Liu @ 2026-03-22  1:43 UTC (permalink / raw)
  To: kexec
  Cc: Pingfan Liu, David S. Miller, Alexei Starovoitov, Daniel Borkmann,
	John Fastabend, Andrii Nakryiko, Martin KaFai Lau,
	Eduard Zingerman, Song Liu, Yonghong Song, Jeremy Linton,
	Catalin Marinas, Will Deacon, Ard Biesheuvel, Simon Horman,
	Gerd Hoffmann, Vitaly Kuznetsov, Philipp Rudo, Viktor Malik,
	Jan Hendrik Farr, Baoquan He, Dave Young, Andrew Morton, bpf,
	systemd-devel, linux-kernel

As UEFI becomes popular, a few architectures support to boot a PE format
kernel image directly. But the internal of PE format varies, which means
each parser for each format.

This patch (with the rest in this series) introduces a common skeleton
to all parsers, and leave the format parsing in
bpf-prog, so the kernel code can keep relative stable.

History, the syscall
SYSCALL_DEFINE5(kexec_file_load, int, kernel_fd, int, initrd_fd,
                unsigned long, cmdline_len, const char __user *, cmdline_ptr,
                unsigned long, flags)
complies with the kernel protocol: bootable kernel, initramfs, cmdline.

But the occurrence of UKI images challenges the traditional model. The
image itself contains the kernel, initrd, and cmdline. To be compatible
with both the old and new models, kexec_file_load can be reorganized into
two stages. In the first stage, "decompose_kexec_image()" breaks down the
passed-in image into the components required by the kernel boot protocol.
In the second stage, the traditional image loader
"arch_kexec_kernel_image_load()" prepares the switch to the next kernel.

During the decomposition stage, the decomposition process can be nested.
In each sub-process, BPF bytecode is extracted from the '.bpf' section
to parse the current PE file. If the data section in the PE file contains
another PE file, the sub-process is repeated. This is designed to handle
the zboot format embedded in UKI format on the arm64 platform.

There are some placeholder functions in this patch. (They will take effect
after the introduction of kexec BPF light skeleton and BPF helpers.)

Signed-off-by: Pingfan Liu <piliu@redhat.com>
Cc: Baoquan He <bhe@redhat.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Philipp Rudo <prudo@redhat.com>
To: kexec@lists.infradead.org
---
 kernel/Kconfig.kexec      |   8 +
 kernel/Makefile           |   1 +
 kernel/kexec_bpf_loader.c | 472 ++++++++++++++++++++++++++++++++++++++
 kernel/kexec_file.c       |  43 +++-
 kernel/kexec_internal.h   |   4 +
 5 files changed, 517 insertions(+), 11 deletions(-)
 create mode 100644 kernel/kexec_bpf_loader.c

diff --git a/kernel/Kconfig.kexec b/kernel/Kconfig.kexec
index 15632358bcf71..0c5d619820bcd 100644
--- a/kernel/Kconfig.kexec
+++ b/kernel/Kconfig.kexec
@@ -46,6 +46,14 @@ config KEXEC_FILE
 	  for kernel and initramfs as opposed to list of segments as
 	  accepted by kexec system call.
 
+config KEXEC_BPF
+	bool "Enable bpf-prog to parse the kexec image"
+	depends on KEXEC_FILE
+	depends on DEBUG_INFO_BTF && BPF_SYSCALL
+	help
+	  This is a feature to run bpf section inside a kexec image file, which
+	  parses the image properly and help kernel set up kexec boot protocol
+
 config KEXEC_SIG
 	bool "Verify kernel signature during kexec_file_load() syscall"
 	depends on ARCH_SUPPORTS_KEXEC_SIG
diff --git a/kernel/Makefile b/kernel/Makefile
index 6785982013dce..9e17ad2a44b6f 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -85,6 +85,7 @@ obj-$(CONFIG_CRASH_DUMP_KUNIT_TEST) += crash_core_test.o
 obj-$(CONFIG_KEXEC) += kexec.o
 obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
 obj-$(CONFIG_KEXEC_ELF) += kexec_elf.o
+obj-$(CONFIG_KEXEC_BPF) += kexec_bpf_loader.o
 obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o
 obj-$(CONFIG_COMPAT) += compat.o
 obj-$(CONFIG_CGROUPS) += cgroup/
diff --git a/kernel/kexec_bpf_loader.c b/kernel/kexec_bpf_loader.c
new file mode 100644
index 0000000000000..bd1800a767824
--- /dev/null
+++ b/kernel/kexec_bpf_loader.c
@@ -0,0 +1,472 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Kexec image bpf section helpers
+ *
+ * Copyright (C) 2025, 2026 Red Hat, Inc
+ */
+
+#define pr_fmt(fmt) "kexec_file(Image): " fmt
+
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/kexec.h>
+#include <linux/ima.h>
+#include <linux/elf.h>
+#include <linux/string.h>
+#include <linux/bpf.h>
+#include <linux/filter.h>
+#include <asm/byteorder.h>
+#include <asm/image.h>
+#include <asm/memory.h>
+#include "kexec_internal.h"
+
+/* Load a ELF */
+static int arm_bpf_prog(char *bpf_elf, unsigned long sz)
+{
+	return -1;
+}
+
+static void disarm_bpf_prog(void)
+{
+}
+
+#define MAX_PARSING_BUF_NUM    16
+
+struct kexec_context {
+	bool kdump;
+	bool parsed;
+	char *parsing_buf[MAX_PARSING_BUF_NUM];
+	unsigned long parsing_buf_sz[MAX_PARSING_BUF_NUM];
+
+	char *kernel;
+	unsigned long kernel_sz;
+	char *initrd;
+	unsigned long initrd_sz;
+	char *cmdline;
+	unsigned long cmdline_sz;
+};
+
+void kexec_image_parser_anchor(struct kexec_context *context,
+			       unsigned long parser_id);
+
+void noinline __used kexec_image_parser_anchor(struct kexec_context *context,
+					       unsigned long parser_id)
+{
+	barrier();
+}
+
+BTF_KFUNCS_START(kexec_modify_return_ids)
+BTF_ID_FLAGS(func, kexec_image_parser_anchor, KF_SLEEPABLE)
+BTF_KFUNCS_END(kexec_modify_return_ids)
+
+static const struct btf_kfunc_id_set kexec_modify_return_set = {
+	.owner = THIS_MODULE,
+	.set = &kexec_modify_return_ids,
+};
+
+static int __init kexec_bpf_prog_run_init(void)
+{
+	return register_btf_fmodret_id_set(&kexec_modify_return_set);
+}
+late_initcall(kexec_bpf_prog_run_init);
+
+static int kexec_buff_parser(struct bpf_parser_context *parser)
+{
+	return 0;
+}
+
+#define KEXEC_ELF_BPF_PREFIX		".bpf."
+#define KEXEC_ELF_BPF_NESTED		".bpf.nested"
+#define KEXEC_ELF_BPF_MAX_IDX		8
+#define KEXEC_ELF_BPF_MAX_DEPTH		4
+
+static bool is_elf_image(const char *buf, size_t sz)
+{
+	if (sz < SELFMAG)
+		return false;
+
+	return memcmp(buf, ELFMAG, SELFMAG) == 0;
+}
+
+/*
+ * elf_get_shstrtab - resolve the section-name string table of an ELF image
+ * @buf:       ELF image buffer
+ * @sz:        buffer length
+ * @ehdr_out:  receives a pointer to the ELF header inside @buf
+ * @shdrs_out: receives a pointer to the section-header table inside @buf
+ * @shstrtab_out: receives a pointer to the section-name string table
+ *
+ * All output pointers are interior pointers into @buf; callers must not
+ * free them independently.
+ *
+ * Returns 0 on success, -EINVAL if any structural check fails.
+ */
+static int elf_get_shstrtab(const char *buf, size_t sz,
+			    const Elf64_Ehdr **ehdr_out,
+			    const Elf64_Shdr **shdrs_out,
+			    const char **shstrtab_out)
+{
+	const Elf64_Ehdr *ehdr;
+	const Elf64_Shdr *shdrs;
+	const Elf64_Shdr *shstr_shdr;
+
+	if (sz < sizeof(*ehdr))
+		return -EINVAL;
+
+	ehdr = (const Elf64_Ehdr *)buf;
+
+	if (ehdr->e_shoff == 0 || ehdr->e_shnum == 0)
+		return -EINVAL;
+
+	if (ehdr->e_shstrndx >= ehdr->e_shnum)
+		return -EINVAL;
+
+	/* section-header table must fit inside the buffer */
+	if (ehdr->e_shoff > sz ||
+	    ehdr->e_shnum > (sz - ehdr->e_shoff) / sizeof(Elf64_Shdr))
+		return -EINVAL;
+
+	shdrs = (const Elf64_Shdr *)(buf + ehdr->e_shoff);
+	shstr_shdr = &shdrs[ehdr->e_shstrndx];
+
+	/* string table itself must fit inside the buffer */
+	if (shstr_shdr->sh_offset > sz ||
+	    shstr_shdr->sh_size > sz - shstr_shdr->sh_offset)
+		return -EINVAL;
+
+	*ehdr_out     = ehdr;
+	*shdrs_out    = shdrs;
+	*shstrtab_out = buf + shstr_shdr->sh_offset;
+
+	return 0;
+}
+
+/*
+ * validate_elf_bpf_sections - enforce the section-naming contract
+ * @buf: ELF image buffer
+ * @sz:  buffer length
+ *
+ * Every section other than the null entry (index 0) and ".shstrtab" must
+ * be named either ".bpf.N" (N in 1..KEXEC_ELF_BPF_MAX_IDX, no gaps, no
+ * duplicates) or ".bpf.nested" (at most once).  Any other name, any
+ * duplicate, or a gap in the numeric sequence is an error.
+ *
+ * Returns 0 if the ELF passes all checks, -EINVAL otherwise.
+ */
+static int validate_elf_bpf_sections(const char *buf, size_t sz)
+{
+	const Elf64_Ehdr *ehdr;
+	const Elf64_Shdr *shdrs;
+	const Elf64_Shdr *shstr_shdr;
+	const char *shstrtab;
+	bool seen[KEXEC_ELF_BPF_MAX_IDX + 1] = {};
+	bool seen_nested = false;
+	int max_idx = 0;
+	int ret;
+	int i;
+
+	if (!is_elf_image(buf, sz))
+		return -EINVAL;
+
+	ret = elf_get_shstrtab(buf, sz, &ehdr, &shdrs, &shstrtab);
+	if (ret)
+		return ret;
+
+	shstr_shdr = &shdrs[ehdr->e_shstrndx];
+
+	for (i = 0; i < ehdr->e_shnum; i++) {
+		const char *name;
+		const char *num_str;
+		int idx;
+
+		if (shdrs[i].sh_name >= shstr_shdr->sh_size)
+			return -EINVAL;
+
+		name = shstrtab + shdrs[i].sh_name;
+
+		/* structural ELF sections: null entry and section-name table */
+		if (name[0] == '\0' || strcmp(name, ".shstrtab") == 0)
+			continue;
+
+		/* .bpf.nested must appear at most once */
+		if (strcmp(name, KEXEC_ELF_BPF_NESTED) == 0) {
+			if (seen_nested) {
+				pr_err("kexec: duplicate .bpf.nested section\n");
+				return -EINVAL;
+			}
+			seen_nested = true;
+			continue;
+		}
+
+		/* every remaining section must start with the ".bpf." prefix */
+		if (strncmp(name, KEXEC_ELF_BPF_PREFIX,
+			    sizeof(KEXEC_ELF_BPF_PREFIX) - 1) != 0) {
+			pr_err("kexec: invalid ELF section name: %s\n", name);
+			return -EINVAL;
+		}
+
+		/*
+		 * Suffix must be exactly one digit in [1, KEXEC_ELF_BPF_MAX_IDX].
+		 * Multi-digit numbers and leading zeros are rejected.
+		 */
+		num_str = name + sizeof(KEXEC_ELF_BPF_PREFIX) - 1;
+		if (num_str[0] < '1' ||
+		    num_str[0] > '0' + KEXEC_ELF_BPF_MAX_IDX ||
+		    num_str[1] != '\0') {
+			pr_err("kexec: invalid BPF section index in: %s\n", name);
+			return -EINVAL;
+		}
+
+		idx = num_str[0] - '0';
+		if (seen[idx]) {
+			pr_err("kexec: duplicate BPF section: %s\n", name);
+			return -EINVAL;
+		}
+		seen[idx] = true;
+		if (idx > max_idx)
+			max_idx = idx;
+	}
+
+	/* indices must be consecutive starting from 1 */
+	for (i = 1; i <= max_idx; i++) {
+		if (!seen[i]) {
+			pr_err("kexec: missing .bpf.%d section\n", i);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * elf_find_section - locate a named section in an ELF image
+ * @buf:     ELF image buffer
+ * @sz:      buffer length
+ * @name:    section name to find
+ * @out_buf: receives a pointer to the section data (NULL if not found)
+ * @out_sz:  receives the section size in bytes (0 if not found)
+ *
+ * Returns 0 on success (including the "not found" case), -EINVAL on a
+ * structural error.
+ */
+static int elf_find_section(const char *buf, size_t sz, const char *name,
+			    char **out_buf, size_t *out_sz)
+{
+	const Elf64_Ehdr *ehdr;
+	const Elf64_Shdr *shdrs;
+	const Elf64_Shdr *shstr_shdr;
+	const char *shstrtab;
+	int ret;
+	int i;
+
+	ret = elf_get_shstrtab(buf, sz, &ehdr, &shdrs, &shstrtab);
+	if (ret)
+		return ret;
+
+	shstr_shdr = &shdrs[ehdr->e_shstrndx];
+
+	for (i = 0; i < ehdr->e_shnum; i++) {
+		if (shdrs[i].sh_name >= shstr_shdr->sh_size)
+			return -EINVAL;
+
+		if (strcmp(shstrtab + shdrs[i].sh_name, name) != 0)
+			continue;
+
+		/* section data must be within the buffer */
+		if (shdrs[i].sh_offset > sz ||
+		    shdrs[i].sh_size > sz - shdrs[i].sh_offset)
+			return -EINVAL;
+
+		*out_buf = (char *)(buf + shdrs[i].sh_offset);
+		*out_sz  = shdrs[i].sh_size;
+		return 0;
+	}
+
+	*out_buf = NULL;
+	*out_sz  = 0;
+	return 0;
+}
+
+/*
+ * process_bpf_parsers_container - recursively process an ELF container, which holds a
+ * batch of bpf parsers
+ *
+ * @elf_buf: ELF image buffer at this level
+ * @elf_sz:  buffer length
+ * @context: shared kexec parsing context
+ * @depth:   current recursion depth (call with 1 for the top level)
+ *
+ *   1. a valid section names should be .bpf.1, .bpf.2, ... in order.
+ *      They are different parser for the current layer.
+ *   2. Only a .bpf.nested section is allowed for the internal level.
+ *   3. At each level, stop trying at the first attempt where context->parsed becomes
+ *      true, then try to load .bpf.nested to parse the internal layer
+ *
+ * Returns 0 on success, -EINVAL on any error.
+ */
+static int process_bpf_parsers_container(const char *elf_buf, size_t elf_sz,
+				   struct kexec_context *context, int depth)
+{
+	struct bpf_parser_context *bpf;
+	char *section_buf, *nested_buf;
+	size_t section_sz;
+	size_t nested_sz;
+	/* .bpf.1 etc */
+	char section_name[sizeof(KEXEC_ELF_BPF_PREFIX) + 1];
+	bool found = false;
+	int ret;
+	int i;
+
+	if (depth > KEXEC_ELF_BPF_MAX_DEPTH) {
+		pr_err("kexec: ELF BPF nesting depth exceeds %d\n",
+		       KEXEC_ELF_BPF_MAX_DEPTH);
+		return -EINVAL;
+	}
+
+	ret = validate_elf_bpf_sections(elf_buf, elf_sz);
+	if (ret)
+		return ret;
+
+	for (i = 1; i <= KEXEC_ELF_BPF_MAX_IDX && !found; i++) {
+		snprintf(section_name, sizeof(section_name), ".bpf.%d", i);
+
+		ret = elf_find_section(elf_buf, elf_sz, section_name,
+				       &section_buf, &section_sz);
+		if (ret)
+			return ret;
+
+		/* no section at this index means the sequence is exhausted */
+		if (!section_buf)
+			break;
+
+		bpf = alloc_bpf_parser_context(kexec_buff_parser, context);
+		if (!bpf)
+			return -ENOMEM;
+
+		ret = arm_bpf_prog(section_buf, section_sz);
+		if (ret) {
+			/* arm failed: no disarm needed, try next index */
+			put_bpf_parser_context(bpf);
+			pr_info("kexec: arm_bpf_prog failed for %s (depth %d), trying next\n",
+				 section_name, depth);
+			continue;
+		}
+
+		/*
+		 * Give the BPF prog a clean slate so context->parsed reliably
+		 * reflects whether *this* invocation succeeded.
+		 */
+		context->parsed = false;
+		/* This is the hook point for bpf-prog */
+		kexec_image_parser_anchor(context, (unsigned long)bpf);
+		disarm_bpf_prog();
+
+		/* Free the old parsing context, and reload the new */
+		for (int i = 0; i < MAX_PARSING_BUF_NUM; i++) {
+			if (!!context->parsing_buf[i])
+				break;
+			vfree(context->parsing_buf[i]);
+			context->parsing_buf[i] = NULL;
+			context->parsing_buf_sz[i] = 0;
+		}
+
+		put_bpf_parser_context(bpf);
+		/* If the bpf-prog success, it flags by KEXEC_BPF_CMD_DONE */
+		if (context->parsed)
+			found = true;
+	}
+
+	if (!found) {
+		pr_err("kexec: no BPF section succeeded at depth %d\n", depth);
+		return -EINVAL;
+	}
+
+	/*
+	 * A numbered section succeeded.  If .bpf.nested is present, the
+	 * current context->kernel may still be in a container format that
+	 * the next level of BPF progs knows how to unpack.
+	 */
+	ret = elf_find_section(elf_buf, elf_sz, KEXEC_ELF_BPF_NESTED,
+			       &nested_buf, &nested_sz);
+	if (ret)
+		return ret;
+
+	if (!nested_buf)
+		return 0;
+
+	context->parsed = false;
+	return process_bpf_parsers_container(nested_buf, nested_sz, context,
+				       depth + 1);
+}
+
+int decompose_kexec_image(struct kimage *image, int extended_fd)
+{
+	struct kexec_context ctx = { 0 };
+	unsigned long parser_sz;
+	char *parser_start;
+	int ret = -EINVAL;
+
+	if (extended_fd < 0)
+		return ret;
+
+	if (image->type != KEXEC_TYPE_CRASH)
+		ctx.kdump = false;
+	else
+		ctx.kdump = true;
+
+	parser_start = image->kernel_buf;
+	parser_sz = image->kernel_buf_len;
+
+	if (!validate_elf_bpf_sections(parser_start, parser_sz)) {
+
+		ret = kernel_read_file_from_fd(extended_fd,
+						0,
+						(void **)&ctx.parsing_buf[0],
+						KEXEC_FILE_SIZE_MAX,
+						NULL,
+						0);
+		if (ret < 0) {
+			pr_err("Fail to read image container\n");
+			return -EINVAL;
+		}
+		ctx.parsing_buf_sz[0] = ret;
+		ret = process_bpf_parsers_container(parser_start, parser_sz, &ctx, 0);
+		if (!ret) {
+			char *p;
+
+			/* Envelop should hold valid kernel, initrd, cmdline sections */
+			if (!ctx.kernel || !ctx.initrd || !ctx.cmdline) {
+				vfree(ctx.kernel);
+				vfree(ctx.initrd);
+				vfree(ctx.cmdline);
+				return -EINVAL;
+			}
+			/*
+			 * kimage_file_post_load_cleanup() calls kfree() to free
+			 * cmdline
+			 */
+			p = kmalloc(ctx.cmdline_sz, GFP_KERNEL);
+			if (!p) {
+				vfree(ctx.kernel);
+				vfree(ctx.initrd);
+				vfree(ctx.cmdline);
+				return -ENOMEM;
+			}
+			vfree(image->kernel_buf);
+			image->kernel_buf = ctx.kernel;
+			image->kernel_buf_len = ctx.kernel_sz;
+			image->initrd_buf = ctx.initrd;
+			image->initrd_buf_len = ctx.initrd_sz;
+			memcpy(p, ctx.cmdline, ctx.cmdline_sz);
+			image->cmdline_buf = p;
+			image->cmdline_buf_len = ctx.cmdline_sz;
+			vfree(ctx.cmdline);
+		}
+		return ret;
+	}
+
+	return -EINVAL;
+}
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index 2bfbb2d144e69..aca265034b4ed 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -55,9 +55,6 @@ static bool check_ima_segment_index(struct kimage *image, int i)
 
 static int kexec_calculate_store_digests(struct kimage *image);
 
-/* Maximum size in bytes for kernel/initrd files. */
-#define KEXEC_FILE_SIZE_MAX	min_t(s64, 4LL << 30, SSIZE_MAX)
-
 /*
  * Currently this is the only default function that is exported as some
  * architectures need it to do additional handlings.
@@ -221,6 +218,7 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
 {
 	ssize_t ret;
 	void *ldata;
+	bool envelop = false;
 
 	ret = kernel_read_file_from_fd(kernel_fd, 0, &image->kernel_buf,
 				       KEXEC_FILE_SIZE_MAX, NULL,
@@ -231,20 +229,40 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
 	kexec_dprintk("kernel: %p kernel_size: %#lx\n",
 		      image->kernel_buf, image->kernel_buf_len);
 
-	/* Call arch image probe handlers */
+	if (IS_ENABLED(CONFIG_KEXEC_BPF)) {
+		/* Fill up image's kernel_buf, initrd_buf, cmdline_buf */
+		ret = decompose_kexec_image(image, initrd_fd);
+		switch (ret) {
+		case 0:
+			envelop = true;
+			break;
+		/* Valid format, but fail to parse */
+		case -EINVAL:
+			break;
+		default:
+			goto out;
+		}
+	}
+
+	/*
+	 * From this point, the kexec subsystem handle the kernel boot protocol.
+	 *
+	 * Call arch image probe handlers
+	 */
 	ret = arch_kexec_kernel_image_probe(image, image->kernel_buf,
 					    image->kernel_buf_len);
 	if (ret)
 		goto out;
 
 #ifdef CONFIG_KEXEC_SIG
-	ret = kimage_validate_signature(image);
-
-	if (ret)
-		goto out;
+	if (!envelop) {
+		ret = kimage_validate_signature(image);
+		if (ret)
+			goto out;
+	}
 #endif
 	/* It is possible that there no initramfs is being loaded */
-	if (!(flags & KEXEC_FILE_NO_INITRAMFS)) {
+	if (!(flags & KEXEC_FILE_NO_INITRAMFS) && !envelop) {
 		ret = kernel_read_file_from_fd(initrd_fd, 0, &image->initrd_buf,
 					       KEXEC_FILE_SIZE_MAX, NULL,
 					       READING_KEXEC_INITRAMFS);
@@ -257,7 +275,8 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
 	image->no_cma = !!(flags & KEXEC_FILE_NO_CMA);
 	image->force_dtb = flags & KEXEC_FILE_FORCE_DTB;
 
-	if (cmdline_len) {
+	/* For envelop case, the cmdline should be passed in as a section */
+	if (cmdline_len && !envelop) {
 		image->cmdline_buf = memdup_user(cmdline_ptr, cmdline_len);
 		if (IS_ERR(image->cmdline_buf)) {
 			ret = PTR_ERR(image->cmdline_buf);
@@ -273,9 +292,11 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
 			goto out;
 		}
 
+	}
+
+	if (image->cmdline_buf)
 		ima_kexec_cmdline(kernel_fd, image->cmdline_buf,
 				  image->cmdline_buf_len - 1);
-	}
 
 	/* IMA needs to pass the measurement list to the next kernel. */
 	ima_add_kexec_buffer(image);
diff --git a/kernel/kexec_internal.h b/kernel/kexec_internal.h
index 228bb88c018bc..731ff02110b3c 100644
--- a/kernel/kexec_internal.h
+++ b/kernel/kexec_internal.h
@@ -33,9 +33,13 @@ static inline void kexec_unlock(void)
 
 #ifdef CONFIG_KEXEC_FILE
 #include <linux/purgatory.h>
+
+/* Maximum size in bytes for kernel/initrd files. */
+#define KEXEC_FILE_SIZE_MAX	min_t(s64, 4LL << 30, SSIZE_MAX)
 void kimage_file_post_load_cleanup(struct kimage *image);
 extern char kexec_purgatory[];
 extern size_t kexec_purgatory_size;
+extern int decompose_kexec_image(struct kimage *image, int extended_fd);
 #else /* CONFIG_KEXEC_FILE */
 static inline void kimage_file_post_load_cleanup(struct kimage *image) { }
 #endif /* CONFIG_KEXEC_FILE */
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCHv7 03/13] lib/decompress: Keep decompressor when CONFIG_KEEP_DECOMPRESSOR
  2026-03-22  1:43 [PATCHv7 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
  2026-03-22  1:43 ` [PATCHv7 01/13] bpf: Introduce kfuncs to parser buffer content Pingfan Liu
  2026-03-22  1:43 ` [PATCHv7 02/13] kexec_file: Use bpf-prog to decompose image Pingfan Liu
@ 2026-03-22  1:43 ` Pingfan Liu
  2026-03-22  2:26   ` bot+bpf-ci
  2026-03-22  1:43 ` [PATCHv7 04/13] kexec_file: Implement decompress method for parser Pingfan Liu
                   ` (10 subsequent siblings)
  13 siblings, 1 reply; 26+ messages in thread
From: Pingfan Liu @ 2026-03-22  1:43 UTC (permalink / raw)
  To: linux-kernel
  Cc: Pingfan Liu, David S. Miller, Alexei Starovoitov, Daniel Borkmann,
	John Fastabend, Andrii Nakryiko, Martin KaFai Lau,
	Eduard Zingerman, Song Liu, Yonghong Song, Jeremy Linton,
	Catalin Marinas, Will Deacon, Ard Biesheuvel, Simon Horman,
	Gerd Hoffmann, Vitaly Kuznetsov, Philipp Rudo, Viktor Malik,
	Jan Hendrik Farr, Baoquan He, Dave Young, Andrew Morton, kexec,
	bpf, systemd-devel

The KEXE PE format parser needs the kernel built-in decompressor to
decompress the kernel image. So moving the decompressor out of __init
sections.

Signed-off-by: Pingfan Liu <piliu@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
To: linux-kernel@vger.kernel.org
---
 include/linux/decompress/mm.h | 8 ++++++++
 lib/Kconfig                   | 6 ++++++
 lib/decompress.c              | 6 +++---
 3 files changed, 17 insertions(+), 3 deletions(-)

diff --git a/include/linux/decompress/mm.h b/include/linux/decompress/mm.h
index ac862422df158..39df02bcbc661 100644
--- a/include/linux/decompress/mm.h
+++ b/include/linux/decompress/mm.h
@@ -81,6 +81,7 @@ MALLOC_VISIBLE void free(void *where)
 #include <linux/string.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
+#include <generated/autoconf.h>
 
 /* Use defines rather than static inline in order to avoid spurious
  * warnings when not needed (indeed large_malloc / large_free are not
@@ -92,7 +93,14 @@ MALLOC_VISIBLE void free(void *where)
 #define large_malloc(a) vmalloc(a)
 #define large_free(a) vfree(a)
 
+#ifdef CONFIG_KEEP_DECOMPRESSOR
+#define INIT
+#define INITCONST
+#else
 #define INIT __init
+#define INITCONST __initconst
+#endif
+
 #define STATIC
 
 #include <linux/init.h>
diff --git a/lib/Kconfig b/lib/Kconfig
index 0f2fb96106476..e91347da12906 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -165,6 +165,12 @@ config RANDOM32_SELFTEST
 #
 # compression support is select'ed if needed
 #
+config KEEP_DECOMPRESSOR
+	bool "keeps the decompress routines after kernel initialization"
+	default n
+	help
+	  This option keeps the decompress routines after kernel initialization
+
 config 842_COMPRESS
 	select CRC32
 	tristate
diff --git a/lib/decompress.c b/lib/decompress.c
index 7785471586c62..29d4c749f1fc4 100644
--- a/lib/decompress.c
+++ b/lib/decompress.c
@@ -6,7 +6,7 @@
  */
 
 #include <linux/decompress/generic.h>
-
+#include <linux/decompress/mm.h>
 #include <linux/decompress/bunzip2.h>
 #include <linux/decompress/unlzma.h>
 #include <linux/decompress/unxz.h>
@@ -48,7 +48,7 @@ struct compress_format {
 	decompress_fn decompressor;
 };
 
-static const struct compress_format compressed_formats[] __initconst = {
+static const struct compress_format compressed_formats[] INITCONST = {
 	{ .magic = {0x1f, 0x8b}, .name = "gzip", .decompressor = gunzip },
 	{ .magic = {0x1f, 0x9e}, .name = "gzip", .decompressor = gunzip },
 	{ .magic = {0x42, 0x5a}, .name = "bzip2", .decompressor = bunzip2 },
@@ -60,7 +60,7 @@ static const struct compress_format compressed_formats[] __initconst = {
 	{ /* sentinel */ }
 };
 
-decompress_fn __init decompress_method(const unsigned char *inbuf, long len,
+decompress_fn INIT decompress_method(const unsigned char *inbuf, long len,
 				const char **name)
 {
 	const struct compress_format *cf;
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCHv7 04/13] kexec_file: Implement decompress method for parser
  2026-03-22  1:43 [PATCHv7 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
                   ` (2 preceding siblings ...)
  2026-03-22  1:43 ` [PATCHv7 03/13] lib/decompress: Keep decompressor when CONFIG_KEEP_DECOMPRESSOR Pingfan Liu
@ 2026-03-22  1:43 ` Pingfan Liu
  2026-03-22  2:26   ` bot+bpf-ci
  2026-03-22  1:43 ` [PATCHv7 05/13] kexec_file: Implement copy " Pingfan Liu
                   ` (9 subsequent siblings)
  13 siblings, 1 reply; 26+ messages in thread
From: Pingfan Liu @ 2026-03-22  1:43 UTC (permalink / raw)
  To: kexec
  Cc: Pingfan Liu, David S. Miller, Alexei Starovoitov, Daniel Borkmann,
	John Fastabend, Andrii Nakryiko, Martin KaFai Lau,
	Eduard Zingerman, Song Liu, Yonghong Song, Jeremy Linton,
	Catalin Marinas, Will Deacon, Ard Biesheuvel, Simon Horman,
	Gerd Hoffmann, Vitaly Kuznetsov, Philipp Rudo, Viktor Malik,
	Jan Hendrik Farr, Baoquan He, Dave Young, Andrew Morton, bpf,
	systemd-devel, linux-kernel

On arm64, there is no boot-time decompression for the kernel image.
Therefore, when a compressed kernel image is loaded, it must be
decompressed.

It is impractical to implement the complex decompression methods in BPF
bytecode. However, decompression routines exist in the kernel.  This
patch bridges the compressed data with the kernel's decompression
methods.

Signed-off-by: Pingfan Liu <piliu@redhat.com>
Cc: Baoquan He <bhe@redhat.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Philipp Rudo <prudo@redhat.com>
To: kexec@lists.infradead.org
---
 kernel/Kconfig.kexec      |   2 +-
 kernel/kexec_bpf_loader.c | 236 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 237 insertions(+), 1 deletion(-)

diff --git a/kernel/Kconfig.kexec b/kernel/Kconfig.kexec
index 0c5d619820bcd..dbfdf34a78aa0 100644
--- a/kernel/Kconfig.kexec
+++ b/kernel/Kconfig.kexec
@@ -49,7 +49,7 @@ config KEXEC_FILE
 config KEXEC_BPF
 	bool "Enable bpf-prog to parse the kexec image"
 	depends on KEXEC_FILE
-	depends on DEBUG_INFO_BTF && BPF_SYSCALL
+	depends on DEBUG_INFO_BTF && BPF_SYSCALL && KEEP_DECOMPRESSOR
 	help
 	  This is a feature to run bpf section inside a kexec image file, which
 	  parses the image properly and help kernel set up kexec boot protocol
diff --git a/kernel/kexec_bpf_loader.c b/kernel/kexec_bpf_loader.c
index bd1800a767824..c79fb777d51bf 100644
--- a/kernel/kexec_bpf_loader.c
+++ b/kernel/kexec_bpf_loader.c
@@ -21,6 +21,7 @@
 #include <asm/byteorder.h>
 #include <asm/image.h>
 #include <asm/memory.h>
+#include <linux/decompress/generic.h>
 #include "kexec_internal.h"
 
 /* Load a ELF */
@@ -73,8 +74,243 @@ static int __init kexec_bpf_prog_run_init(void)
 }
 late_initcall(kexec_bpf_prog_run_init);
 
+/* Mark the bpf parser success */
+#define KEXEC_BPF_CMD_INVALID		0x0
+#define KEXEC_BPF_CMD_DONE		0x1
+#define KEXEC_BPF_CMD_DECOMPRESS	0x2
+
+#define KEXEC_BPF_SUBCMD_INVALID	0x0
+#define KEXEC_BPF_SUBCMD_KERNEL		0x1
+#define KEXEC_BPF_SUBCMD_INITRD		0x2
+#define KEXEC_BPF_SUBCMD_CMDLINE	0x3
+
+#define KEXEC_BPF_PIPELINE_INVALID	0x0
+#define KEXEC_BPF_PIPELINE_FILL		0x1
+
+struct cmd_hdr {
+	uint16_t cmd;
+	uint8_t subcmd;
+	uint8_t pipeline_flag;
+	/* sizeof(chunks) + sizeof(all data) */
+	uint32_t payload_len;
+	/* 0 */
+	uint16_t num_chunks;
+} __packed;
+
+/* Reserved for extension */
+struct cmd_chunk {
+	uint16_t type;
+	uint32_t len;
+} __packed;
+
+
+/* Max decompressed size is capped at 512M */
+#define MAX_UNCOMPRESSED_BUF_SIZE	(1 << 29)
+#define CHUNK_SIZE	(1 << 23)
+
+struct decompress_mem_allocator {
+	void *chunk_start;
+	unsigned int chunk_size;
+	void *chunk_cur;
+	unsigned int next_idx;
+	char **chunk_base_addr;
+};
+
+/*
+ * This global allocator for decompression is protected by kexec lock.
+ */
+static struct decompress_mem_allocator dcmpr_allocator;
+
+/*
+ * Set up an active chunk to hold partial decompressed data.
+ */
+static char *allocate_chunk_memory(void)
+{
+	struct decompress_mem_allocator *a = &dcmpr_allocator;
+	char *p;
+
+	if (unlikely((a->next_idx * a->chunk_size >= MAX_UNCOMPRESSED_BUF_SIZE)))
+		return NULL;
+
+	p = __vmalloc(a->chunk_size, GFP_KERNEL | __GFP_ACCOUNT);
+	if (!p)
+		return NULL;
+	a->chunk_base_addr[a->next_idx++] = p;
+	a->chunk_start = a->chunk_cur = p;
+
+	return p;
+}
+
+static int merge_decompressed_data(struct decompress_mem_allocator *a,
+			char **out, unsigned long *size)
+{
+	unsigned int last_chunk_sz = a->chunk_cur - a->chunk_start;
+	unsigned long total_sz;
+	char *dst, *cur_dst;
+	int i;
+
+	total_sz = (a->next_idx - 1) * a->chunk_size + last_chunk_sz;
+	cur_dst = dst = __vmalloc(total_sz, GFP_KERNEL | __GFP_ACCOUNT);
+	if (!dst)
+		return -ENOMEM;
+
+	for (i = 0; i < a->next_idx - 1; i++) {
+		memcpy(cur_dst, a->chunk_base_addr[i], a->chunk_size);
+		cur_dst += a->chunk_size;
+		vfree(a->chunk_base_addr[i]);
+		a->chunk_base_addr[i] = NULL;
+	}
+
+	memcpy(cur_dst, a->chunk_base_addr[i], last_chunk_sz);
+	vfree(a->chunk_base_addr[i]);
+	a->chunk_base_addr[i] = NULL;
+	*out = dst;
+	*size = total_sz;
+
+	return 0;
+}
+
+static int decompress_mem_allocator_init(
+	struct decompress_mem_allocator *a,
+	unsigned int chunk_size)
+{
+	unsigned long sz = (MAX_UNCOMPRESSED_BUF_SIZE / chunk_size) * sizeof(void *);
+	char *buf;
+
+	a->chunk_base_addr = __vmalloc(sz, GFP_KERNEL | __GFP_ACCOUNT);
+	if (!a->chunk_base_addr)
+		return -ENOMEM;
+
+	/* Pre-allocate the memory for the first chunk */
+	buf = __vmalloc(chunk_size, GFP_KERNEL | __GFP_ACCOUNT);
+	if (!buf) {
+		vfree(a->chunk_base_addr);
+		return -ENOMEM;
+	}
+	a->chunk_base_addr[0] = buf;
+	a->chunk_start = a->chunk_cur = buf;
+	a->chunk_size = chunk_size;
+	a->next_idx = 1;
+	return 0;
+}
+
+static void decompress_mem_allocator_fini(struct decompress_mem_allocator *a)
+{
+	int i;
+
+	for (i = 0; i < a->next_idx; i++) {
+		if (a->chunk_base_addr[i] != NULL)
+			vfree(a->chunk_base_addr[i]);
+	}
+	vfree(a->chunk_base_addr);
+}
+
+/*
+ * This is a callback for decompress_fn.
+ *
+ * It copies the partial decompressed content in [buf, buf + len) to dst. If the
+ * active chunk is not large enough, retire it and activate a new chunk to hold
+ * the remaining data.
+ */
+static long flush(void *buf, unsigned long len)
+{
+	struct decompress_mem_allocator *a = &dcmpr_allocator;
+	long free, copied = 0;
+
+	if (unlikely(len > a->chunk_size)) {
+		pr_info("Chunk size is too small to hold decompressed data\n");
+		return -1;
+	}
+	free = a->chunk_start + a->chunk_size - a->chunk_cur;
+	BUG_ON(free < 0);
+	if (free < len) {
+		memcpy(a->chunk_cur, buf, free);
+		copied += free;
+		a->chunk_cur += free;
+		buf += free;
+		len -= free;
+		a->chunk_start = a->chunk_cur = allocate_chunk_memory();
+		if (unlikely(!a->chunk_start)) {
+			pr_info("Decompression runs out of memory\n");
+			return -1;
+		}
+	}
+	memcpy(a->chunk_cur, buf, len);
+	copied += len;
+	a->chunk_cur += len;
+	return copied;
+}
+
+static int parser_cmd_decompress(char *compressed_data, int image_gz_sz,
+		char **out_buf, unsigned long *out_sz, struct kexec_context *ctx)
+{
+	struct decompress_mem_allocator *a = &dcmpr_allocator;
+	decompress_fn decompressor;
+	const char *name;
+	int ret;
+
+	ret = decompress_mem_allocator_init(a, CHUNK_SIZE);
+	if (ret < 0)
+		return ret;
+	decompressor = decompress_method(compressed_data, image_gz_sz, &name);
+	if (!decompressor) {
+		pr_err("Can not find decompress method\n");
+		ret = -1;
+		goto err;
+	}
+	pr_debug("Find decompressing method: %s, compressed sz:0x%x\n",
+			name, image_gz_sz);
+	ret = decompressor(compressed_data, image_gz_sz, NULL, flush,
+				NULL, NULL, NULL);
+	if (!!ret)
+		goto err;
+	ret = merge_decompressed_data(a, out_buf, out_sz);
+
+err:
+	decompress_mem_allocator_fini(a);
+
+	return ret;
+}
+
 static int kexec_buff_parser(struct bpf_parser_context *parser)
 {
+	struct bpf_parser_buf *pbuf = parser->buf;
+	struct kexec_context *ctx = (struct kexec_context *)parser->data;
+	struct cmd_hdr *cmd = (struct cmd_hdr *)pbuf->buf;
+	char *decompressed_buf, *buf, *p;
+	unsigned long decompressed_sz;
+	int ret = 0;
+
+	buf = pbuf->buf + sizeof(struct cmd_hdr);
+	if (cmd->payload_len + sizeof(struct cmd_hdr) > pbuf->size) {
+		pr_info("Invalid payload size:0x%x, while buffer size:0x%x\n",
+				cmd->payload_len, pbuf->size);
+		return -EINVAL;
+	}
+	switch (cmd->cmd) {
+	case KEXEC_BPF_CMD_DONE:
+		ctx->parsed = true;
+		break;
+	case KEXEC_BPF_CMD_DECOMPRESS:
+		ret = parser_cmd_decompress(buf, cmd->payload_len, &decompressed_buf,
+					&decompressed_sz, ctx);
+		if (!ret) {
+			switch (cmd->subcmd) {
+			case KEXEC_BPF_SUBCMD_KERNEL:
+				vfree(ctx->kernel);
+				ctx->kernel = decompressed_buf;
+				ctx->kernel_sz = decompressed_sz;
+				break;
+			default:
+				vfree(decompressed_buf);
+				break;
+			}
+		}
+		break;
+	default:
+		break;
+	}
+
 	return 0;
 }
 
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCHv7 05/13] kexec_file: Implement copy method for parser
  2026-03-22  1:43 [PATCHv7 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
                   ` (3 preceding siblings ...)
  2026-03-22  1:43 ` [PATCHv7 04/13] kexec_file: Implement decompress method for parser Pingfan Liu
@ 2026-03-22  1:43 ` Pingfan Liu
  2026-03-22  1:43 ` [PATCHv7 06/13] kexec_file: Chain the stages into a pipeline Pingfan Liu
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 26+ messages in thread
From: Pingfan Liu @ 2026-03-22  1:43 UTC (permalink / raw)
  To: kexec
  Cc: Pingfan Liu, David S. Miller, Alexei Starovoitov, Daniel Borkmann,
	John Fastabend, Andrii Nakryiko, Martin KaFai Lau,
	Eduard Zingerman, Song Liu, Yonghong Song, Jeremy Linton,
	Catalin Marinas, Will Deacon, Ard Biesheuvel, Simon Horman,
	Gerd Hoffmann, Vitaly Kuznetsov, Philipp Rudo, Viktor Malik,
	Jan Hendrik Farr, Baoquan He, Dave Young, Andrew Morton, bpf,
	systemd-devel, linux-kernel

Copying the bpf code parsing result to the proper place:
image->kernel_buf, initrd_buf, cmdline_buf.

Signed-off-by: Pingfan Liu <piliu@redhat.com>
Cc: Baoquan He <bhe@redhat.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Philipp Rudo <prudo@redhat.com>
To: kexec@lists.infradead.org
---
 kernel/kexec_bpf_loader.c | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/kernel/kexec_bpf_loader.c b/kernel/kexec_bpf_loader.c
index c79fb777d51bf..af16f7b685d9a 100644
--- a/kernel/kexec_bpf_loader.c
+++ b/kernel/kexec_bpf_loader.c
@@ -78,6 +78,7 @@ late_initcall(kexec_bpf_prog_run_init);
 #define KEXEC_BPF_CMD_INVALID		0x0
 #define KEXEC_BPF_CMD_DONE		0x1
 #define KEXEC_BPF_CMD_DECOMPRESS	0x2
+#define KEXEC_BPF_CMD_COPY		0x3
 
 #define KEXEC_BPF_SUBCMD_INVALID	0x0
 #define KEXEC_BPF_SUBCMD_KERNEL		0x1
@@ -307,6 +308,34 @@ static int kexec_buff_parser(struct bpf_parser_context *parser)
 			}
 		}
 		break;
+	case KEXEC_BPF_CMD_COPY:
+		p = __vmalloc(cmd->payload_len, GFP_KERNEL | __GFP_ACCOUNT);
+		if (!p)
+			return -ENOMEM;
+		memcpy(p, buf, cmd->payload_len);
+		switch (cmd->subcmd) {
+		case KEXEC_BPF_SUBCMD_KERNEL:
+			vfree(ctx->kernel);
+			ctx->kernel = p;
+			ctx->kernel_sz = cmd->payload_len;
+			break;
+		/* Todo: allow the concatenation of multiple initrd */
+		case KEXEC_BPF_SUBCMD_INITRD:
+			vfree(ctx->initrd);
+			ctx->initrd = p;
+			ctx->initrd_sz = cmd->payload_len;
+			break;
+		/* Todo: allow the concatenation of multiple cmdline */
+		case KEXEC_BPF_SUBCMD_CMDLINE:
+			vfree(ctx->cmdline);
+			ctx->cmdline = p;
+			ctx->cmdline_sz = cmd->payload_len;
+			break;
+		default:
+			vfree(p);
+			break;
+		}
+		break;
 	default:
 		break;
 	}
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCHv7 06/13] kexec_file: Chain the stages into a pipeline
  2026-03-22  1:43 [PATCHv7 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
                   ` (4 preceding siblings ...)
  2026-03-22  1:43 ` [PATCHv7 05/13] kexec_file: Implement copy " Pingfan Liu
@ 2026-03-22  1:43 ` Pingfan Liu
  2026-03-22  2:14   ` bot+bpf-ci
  2026-03-22  1:43 ` [PATCHv7 07/13] kexec_file: Introduce a bpf-prog lskel to run a format parser Pingfan Liu
                   ` (7 subsequent siblings)
  13 siblings, 1 reply; 26+ messages in thread
From: Pingfan Liu @ 2026-03-22  1:43 UTC (permalink / raw)
  To: kexec
  Cc: Pingfan Liu, David S. Miller, Alexei Starovoitov, Daniel Borkmann,
	John Fastabend, Andrii Nakryiko, Martin KaFai Lau,
	Eduard Zingerman, Song Liu, Yonghong Song, Jeremy Linton,
	Catalin Marinas, Will Deacon, Ard Biesheuvel, Simon Horman,
	Gerd Hoffmann, Vitaly Kuznetsov, Philipp Rudo, Viktor Malik,
	Jan Hendrik Farr, Baoquan He, Dave Young, Andrew Morton, bpf,
	systemd-devel, linux-kernel

Images may consist of multiple layers, each with a distinct format. For
example, an AArch64 UKI image typically embeds a zboot image in the
.linux section. The parser therefore determines whether its output
should be forwarded to the next stage.

Intermediate results are stored in next_parsing_buf and then promoted to
parsing_buf for the subsequent stage.

Signed-off-by: Pingfan Liu <piliu@redhat.com>
Cc: Baoquan He <bhe@redhat.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Philipp Rudo <prudo@redhat.com>
To: kexec@lists.infradead.org
---
 kernel/kexec_bpf_loader.c | 47 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 46 insertions(+), 1 deletion(-)

diff --git a/kernel/kexec_bpf_loader.c b/kernel/kexec_bpf_loader.c
index af16f7b685d9a..7f7884411e2c7 100644
--- a/kernel/kexec_bpf_loader.c
+++ b/kernel/kexec_bpf_loader.c
@@ -41,6 +41,8 @@ struct kexec_context {
 	bool parsed;
 	char *parsing_buf[MAX_PARSING_BUF_NUM];
 	unsigned long parsing_buf_sz[MAX_PARSING_BUF_NUM];
+	char *next_parsing_buf[MAX_PARSING_BUF_NUM];
+	unsigned long next_parsing_buf_sz[MAX_PARSING_BUF_NUM];
 
 	char *kernel;
 	unsigned long kernel_sz;
@@ -278,8 +280,9 @@ static int kexec_buff_parser(struct bpf_parser_context *parser)
 	struct bpf_parser_buf *pbuf = parser->buf;
 	struct kexec_context *ctx = (struct kexec_context *)parser->data;
 	struct cmd_hdr *cmd = (struct cmd_hdr *)pbuf->buf;
-	char *decompressed_buf, *buf, *p;
+	char *decompressed_buf, *buf, *p, *pn;
 	unsigned long decompressed_sz;
+	bool fill_pipeline = false;
 	int ret = 0;
 
 	buf = pbuf->buf + sizeof(struct cmd_hdr);
@@ -288,6 +291,7 @@ static int kexec_buff_parser(struct bpf_parser_context *parser)
 				cmd->payload_len, pbuf->size);
 		return -EINVAL;
 	}
+	fill_pipeline = cmd->pipeline_flag & KEXEC_BPF_PIPELINE_FILL;
 	switch (cmd->cmd) {
 	case KEXEC_BPF_CMD_DONE:
 		ctx->parsed = true;
@@ -301,6 +305,23 @@ static int kexec_buff_parser(struct bpf_parser_context *parser)
 				vfree(ctx->kernel);
 				ctx->kernel = decompressed_buf;
 				ctx->kernel_sz = decompressed_sz;
+				if (fill_pipeline) {
+					int i;
+
+					for (i = 0; i < MAX_PARSING_BUF_NUM; i++) {
+						if (ctx->next_parsing_buf[i])
+							continue;
+						ctx->next_parsing_buf[i] = decompressed_buf;
+						ctx->next_parsing_buf_sz[i] = decompressed_sz;
+						break;
+					}
+					/* No enough parsing slot */
+					if (i == MAX_PARSING_BUF_NUM) {
+						ctx->kernel = NULL;
+						vfree(decompressed_buf);
+						return -ENOMEM;
+					}
+				}
 				break;
 			default:
 				vfree(decompressed_buf);
@@ -313,6 +334,22 @@ static int kexec_buff_parser(struct bpf_parser_context *parser)
 		if (!p)
 			return -ENOMEM;
 		memcpy(p, buf, cmd->payload_len);
+		if (fill_pipeline) {
+			pn = __vmalloc(cmd->payload_len, GFP_KERNEL | __GFP_ACCOUNT);
+			if (!pn) {
+				vfree(p);
+				return -ENOMEM;
+			}
+			memcpy(pn, buf, cmd->payload_len);
+			for (int i = 0; i < MAX_PARSING_BUF_NUM; i++) {
+				if (!ctx->next_parsing_buf[i]) {
+					ctx->next_parsing_buf[i] = pn;
+					ctx->next_parsing_buf_sz[i] = cmd->payload_len;
+					break;
+				}
+			}
+		}
+
 		switch (cmd->subcmd) {
 		case KEXEC_BPF_SUBCMD_KERNEL:
 			vfree(ctx->kernel);
@@ -637,6 +674,14 @@ static int process_bpf_parsers_container(const char *elf_buf, size_t elf_sz,
 			context->parsing_buf[i] = NULL;
 			context->parsing_buf_sz[i] = 0;
 		}
+		for (int i = 0; i < MAX_PARSING_BUF_NUM; i++) {
+			if (!context->next_parsing_buf[i])
+				break;
+			context->parsing_buf[i] = context->next_parsing_buf[i];
+			context->parsing_buf_sz[i] = context->next_parsing_buf_sz[i];
+			context->next_parsing_buf[i] = NULL;
+			context->next_parsing_buf_sz[i] = 0;
+		}
 
 		put_bpf_parser_context(bpf);
 		/* If the bpf-prog success, it flags by KEXEC_BPF_CMD_DONE */
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCHv7 07/13] kexec_file: Introduce a bpf-prog lskel to run a format parser
  2026-03-22  1:43 [PATCHv7 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
                   ` (5 preceding siblings ...)
  2026-03-22  1:43 ` [PATCHv7 06/13] kexec_file: Chain the stages into a pipeline Pingfan Liu
@ 2026-03-22  1:43 ` Pingfan Liu
  2026-03-22  1:43 ` [PATCHv7 08/13] kexec_file: Factor out routine to find a symbol in ELF Pingfan Liu
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 26+ messages in thread
From: Pingfan Liu @ 2026-03-22  1:43 UTC (permalink / raw)
  To: kexec
  Cc: Pingfan Liu, David S. Miller, Alexei Starovoitov, Daniel Borkmann,
	John Fastabend, Andrii Nakryiko, Martin KaFai Lau,
	Eduard Zingerman, Song Liu, Yonghong Song, Jeremy Linton,
	Catalin Marinas, Will Deacon, Ard Biesheuvel, Simon Horman,
	Gerd Hoffmann, Vitaly Kuznetsov, Philipp Rudo, Viktor Malik,
	Jan Hendrik Farr, Baoquan He, Dave Young, Andrew Morton, bpf,
	systemd-devel, linux-kernel

Analague to kernel/bpf/preload/iterators/Makefile,
this Makefile is not invoked by the Kbuild system. It needs to be
invoked manually when kexec_pe_parser_bpf.c is changed so that
kexec_pe_parser_bpf.lskel.h can be re-generated by the command "bpftool
gen skeleton -L kexec_pe_parser_bpf.o".

kexec_pe_parser_bpf.lskel.h is used directly by the kernel kexec code in
later patch. For this patch, there are bpf bytecode contained in
opts_data[] and opts_insn[] in kexec_pe_parser_bpf.lskel.h, but in the
following patch, they will be removed and only the function API in
kexec_pe_parser_bpf.lskel.h left.

As exposed in kexec_pe_parser_bpf.lskel.h, the interface between
bpf-prog and the kernel are constituted by:

four maps:
                struct bpf_map_desc ringbuf_1;
                struct bpf_map_desc ringbuf_2;
                struct bpf_map_desc ringbuf_3;
                struct bpf_map_desc ringbuf_4;
four sections:
                struct bpf_map_desc rodata;
                struct bpf_map_desc data;
                struct bpf_map_desc rodata_str1_1;
                struct bpf_map_desc bss;

one prog:
	SEC("fentry.s/kexec_image_parser_anchor")

The interface of maps and sections are extracted in template.c, which
should be included by any bpf-prog to interact with kexec bpf component

Signed-off-by: Pingfan Liu <piliu@redhat.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Baoquan He <bhe@redhat.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Philipp Rudo <prudo@redhat.com>
Cc: bpf@vger.kernel.org
To: kexec@lists.infradead.org
---
 kernel/kexec_bpf/Makefile                    |  62 ++++
 kernel/kexec_bpf/kexec_pe_parser_bpf.c       |  12 +
 kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h | 371 +++++++++++++++++++
 kernel/kexec_bpf/template.c                  |  72 ++++
 4 files changed, 517 insertions(+)
 create mode 100644 kernel/kexec_bpf/Makefile
 create mode 100644 kernel/kexec_bpf/kexec_pe_parser_bpf.c
 create mode 100644 kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h
 create mode 100644 kernel/kexec_bpf/template.c

diff --git a/kernel/kexec_bpf/Makefile b/kernel/kexec_bpf/Makefile
new file mode 100644
index 0000000000000..45d45cc0855a3
--- /dev/null
+++ b/kernel/kexec_bpf/Makefile
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: GPL-2.0
+OUTPUT := .output
+CLANG ?= clang
+LLC ?= llc
+LLVM_STRIP ?= llvm-strip
+DEFAULT_BPFTOOL := $(OUTPUT)/sbin/bpftool
+BPFTOOL ?= $(DEFAULT_BPFTOOL)
+LIBBPF_SRC := $(abspath ../../tools/lib/bpf)
+BPFOBJ := $(OUTPUT)/libbpf.a
+BPF_INCLUDE := $(OUTPUT)
+INCLUDES := -I$(OUTPUT) -I$(BPF_INCLUDE) -I$(abspath ../../tools/lib)        \
+       -I$(abspath ../../tools/include/uapi)
+CFLAGS := -g -Wall
+
+srctree := $(patsubst %/kernel/kexec_bpf,%,$(CURDIR))
+VMLINUX = $(srctree)/vmlinux
+
+abs_out := $(abspath $(OUTPUT))
+ifeq ($(V),1)
+Q =
+msg =
+else
+Q = @
+msg = @printf '  %-8s %s%s\n' "$(1)" "$(notdir $(2))" "$(if $(3), $(3))";
+MAKEFLAGS += --no-print-directory
+submake_extras := feature_display=0
+endif
+
+.DELETE_ON_ERROR:
+
+.PHONY: all clean
+
+all: $(OUTPUT) kexec_pe_parser_bpf.lskel.h
+
+clean:
+	$(call msg,CLEAN)
+	$(Q)rm -rf $(OUTPUT) kexec_pe_parser_bpf.lskel.h
+
+kexec_pe_parser_bpf.lskel.h: $(OUTPUT)/kexec_pe_parser_bpf.o | $(BPFTOOL)
+	$(call msg,GEN-SKEL,$@)
+	$(Q)$(BPFTOOL) gen skeleton -L $< > $@
+
+$(OUTPUT)/vmlinux.h: $(VMLINUX) $(DEFAULT_BPFTOOL) $(BPFOBJ) | $(OUTPUT)
+	@$(BPFTOOL) btf dump file $(VMLINUX) format c > $(OUTPUT)/vmlinux.h
+
+$(OUTPUT)/kexec_pe_parser_bpf.o: kexec_pe_parser_bpf.c $(OUTPUT)/vmlinux.h $(BPFOBJ) | $(OUTPUT)
+	$(call msg,BPF,$@)
+	$(Q)$(CLANG) -g -O2 -target bpf $(INCLUDES)			      \
+		 -c $(filter %.c,$^) -o $@ &&				      \
+	$(LLVM_STRIP) -g $@
+
+$(OUTPUT):
+	$(call msg,MKDIR,$@)
+	$(Q)mkdir -p $(OUTPUT)
+
+$(BPFOBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)
+	$(Q)$(MAKE) $(submake_extras) -C $(LIBBPF_SRC)			       \
+		    OUTPUT=$(abspath $(dir $@))/ $(abspath $@)
+
+$(DEFAULT_BPFTOOL):
+	$(Q)$(MAKE) $(submake_extras) -C ../../tools/bpf/bpftool			      \
+		    prefix= OUTPUT=$(abs_out)/ DESTDIR=$(abs_out) install
diff --git a/kernel/kexec_bpf/kexec_pe_parser_bpf.c b/kernel/kexec_bpf/kexec_pe_parser_bpf.c
new file mode 100644
index 0000000000000..e4a2419240673
--- /dev/null
+++ b/kernel/kexec_bpf/kexec_pe_parser_bpf.c
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2025, 2026 Red Hat, Inc
+
+#include "template.c"
+
+SEC("fentry.s/kexec_image_parser_anchor")
+__attribute__((used)) int BPF_PROG(parse_pe, struct kexec_context *context,
+				unsigned long parser_id)
+{
+	return 0;
+}
diff --git a/kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h b/kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h
new file mode 100644
index 0000000000000..22fd77a872fc0
--- /dev/null
+++ b/kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h
@@ -0,0 +1,371 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* THIS FILE IS AUTOGENERATED BY BPFTOOL! */
+#ifndef __KEXEC_PE_PARSER_BPF_SKEL_H__
+#define __KEXEC_PE_PARSER_BPF_SKEL_H__
+
+#include <bpf/skel_internal.h>
+
+struct kexec_pe_parser_bpf {
+	struct bpf_loader_ctx ctx;
+	struct {
+		struct bpf_map_desc ringbuf_1;
+		struct bpf_map_desc ringbuf_2;
+		struct bpf_map_desc ringbuf_3;
+		struct bpf_map_desc ringbuf_4;
+		struct bpf_map_desc rodata;
+		struct bpf_map_desc data;
+		struct bpf_map_desc rodata_str1_1;
+		struct bpf_map_desc bss;
+	} maps;
+	struct {
+		struct bpf_prog_desc parse_pe;
+	} progs;
+	struct {
+		int parse_pe_fd;
+	} links;
+};
+
+static inline int
+kexec_pe_parser_bpf__parse_pe__attach(struct kexec_pe_parser_bpf *skel)
+{
+	int prog_fd = skel->progs.parse_pe.prog_fd;
+	int fd = skel_raw_tracepoint_open(NULL, prog_fd);
+
+	if (fd > 0)
+		skel->links.parse_pe_fd = fd;
+	return fd;
+}
+
+static inline int
+kexec_pe_parser_bpf__attach(struct kexec_pe_parser_bpf *skel)
+{
+	int ret = 0;
+
+	ret = ret < 0 ? ret : kexec_pe_parser_bpf__parse_pe__attach(skel);
+	return ret < 0 ? ret : 0;
+}
+
+static inline void
+kexec_pe_parser_bpf__detach(struct kexec_pe_parser_bpf *skel)
+{
+	skel_closenz(skel->links.parse_pe_fd);
+}
+static void
+kexec_pe_parser_bpf__destroy(struct kexec_pe_parser_bpf *skel)
+{
+	if (!skel)
+		return;
+	kexec_pe_parser_bpf__detach(skel);
+	skel_closenz(skel->progs.parse_pe.prog_fd);
+	skel_closenz(skel->maps.ringbuf_1.map_fd);
+	skel_closenz(skel->maps.ringbuf_2.map_fd);
+	skel_closenz(skel->maps.ringbuf_3.map_fd);
+	skel_closenz(skel->maps.ringbuf_4.map_fd);
+	skel_closenz(skel->maps.rodata.map_fd);
+	skel_closenz(skel->maps.data.map_fd);
+	skel_closenz(skel->maps.rodata_str1_1.map_fd);
+	skel_closenz(skel->maps.bss.map_fd);
+	skel_free(skel);
+}
+static inline struct kexec_pe_parser_bpf *
+kexec_pe_parser_bpf__open(void)
+{
+	struct kexec_pe_parser_bpf *skel;
+
+	skel = skel_alloc(sizeof(*skel));
+	if (!skel)
+		goto cleanup;
+	skel->ctx.sz = (void *)&skel->links - (void *)skel;
+	return skel;
+cleanup:
+	kexec_pe_parser_bpf__destroy(skel);
+	return NULL;
+}
+
+static inline int
+kexec_pe_parser_bpf__load(struct kexec_pe_parser_bpf *skel)
+{
+	struct bpf_load_and_run_opts opts = {};
+	int err;
+	static const char opts_data[] __attribute__((__aligned__(8))) = "\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x9f\xeb\x01\0\
+\x18\0\0\0\0\0\0\0\xf4\x02\0\0\xf4\x02\0\0\x78\x01\0\0\0\0\0\0\0\0\0\x02\x03\0\
+\0\0\x01\0\0\0\0\0\0\x01\x04\0\0\0\x20\0\0\x01\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\
+\0\0\x04\0\0\0\x1b\0\0\0\x05\0\0\0\0\0\0\x01\x04\0\0\0\x20\0\0\0\0\0\0\0\0\0\0\
+\x02\x06\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\x04\0\0\0\0\0\0\0\
+\x02\0\0\x04\x10\0\0\0\x19\0\0\0\x01\0\0\0\0\0\0\0\x1e\0\0\0\x05\0\0\0\x40\0\0\
+\0\x2a\0\0\0\0\0\0\x0e\x07\0\0\0\x01\0\0\0\0\0\0\0\x02\0\0\x04\x10\0\0\0\x19\0\
+\0\0\x01\0\0\0\0\0\0\0\x1e\0\0\0\x05\0\0\0\x40\0\0\0\x34\0\0\0\0\0\0\x0e\x09\0\
+\0\0\x01\0\0\0\0\0\0\0\x02\0\0\x04\x10\0\0\0\x19\0\0\0\x01\0\0\0\0\0\0\0\x1e\0\
+\0\0\x05\0\0\0\x40\0\0\0\x3e\0\0\0\0\0\0\x0e\x0b\0\0\0\x01\0\0\0\0\0\0\0\x02\0\
+\0\x04\x10\0\0\0\x19\0\0\0\x01\0\0\0\0\0\0\0\x1e\0\0\0\x05\0\0\0\x40\0\0\0\x48\
+\0\0\0\0\0\0\x0e\x0d\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\x02\x10\0\0\0\x52\0\0\0\0\0\
+\0\x01\x08\0\0\0\x40\0\0\0\0\0\0\0\x01\0\0\x0d\x02\0\0\0\x65\0\0\0\x0f\0\0\0\
+\x69\0\0\0\x01\0\0\x0c\x11\0\0\0\x14\x01\0\0\0\0\0\x01\x01\0\0\0\x08\0\0\x01\0\
+\0\0\0\0\0\0\x03\0\0\0\0\x13\0\0\0\x04\0\0\0\x04\0\0\0\x19\x01\0\0\0\0\0\x0e\
+\x14\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\x0a\x13\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x16\
+\0\0\0\x04\0\0\0\x10\0\0\0\x21\x01\0\0\0\0\0\x0e\x17\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\x03\0\0\0\0\x13\0\0\0\x04\0\0\0\x10\0\0\0\x2e\x01\0\0\0\0\0\x0e\x19\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x13\0\0\0\x04\0\0\0\x0f\0\0\0\0\0\0\0\0\0\0\
+\x02\x13\0\0\0\x39\x01\0\0\0\0\0\x0e\x1c\0\0\0\0\0\0\0\x4d\x01\0\0\0\0\0\x0e\
+\x19\0\0\0\0\0\0\0\x57\x01\0\0\x01\0\0\x0f\x10\0\0\0\x1e\0\0\0\0\0\0\0\x10\0\0\
+\0\x5c\x01\0\0\x02\0\0\x0f\x18\0\0\0\x1a\0\0\0\0\0\0\0\x10\0\0\0\x1d\0\0\0\x10\
+\0\0\0\x08\0\0\0\x62\x01\0\0\x04\0\0\x0f\x40\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\
+\x0a\0\0\0\x10\0\0\0\x10\0\0\0\x0c\0\0\0\x20\0\0\0\x10\0\0\0\x0e\0\0\0\x30\0\0\
+\0\x10\0\0\0\x68\x01\0\0\x01\0\0\x0f\x10\0\0\0\x18\0\0\0\0\0\0\0\x10\0\0\0\x70\
+\x01\0\0\x01\0\0\x0f\x04\0\0\0\x15\0\0\0\0\0\0\0\x04\0\0\0\0\x69\x6e\x74\0\x5f\
+\x5f\x41\x52\x52\x41\x59\x5f\x53\x49\x5a\x45\x5f\x54\x59\x50\x45\x5f\x5f\0\x74\
+\x79\x70\x65\0\x6d\x61\x78\x5f\x65\x6e\x74\x72\x69\x65\x73\0\x72\x69\x6e\x67\
+\x62\x75\x66\x5f\x31\0\x72\x69\x6e\x67\x62\x75\x66\x5f\x32\0\x72\x69\x6e\x67\
+\x62\x75\x66\x5f\x33\0\x72\x69\x6e\x67\x62\x75\x66\x5f\x34\0\x75\x6e\x73\x69\
+\x67\x6e\x65\x64\x20\x6c\x6f\x6e\x67\x20\x6c\x6f\x6e\x67\0\x63\x74\x78\0\x70\
+\x61\x72\x73\x65\x5f\x70\x65\0\x66\x65\x6e\x74\x72\x79\x2e\x73\x2f\x6b\x65\x78\
+\x65\x63\x5f\x69\x6d\x61\x67\x65\x5f\x70\x61\x72\x73\x65\x72\x5f\x61\x6e\x63\
+\x68\x6f\x72\0\x2f\x68\x6f\x6d\x65\x2f\x6c\x69\x6e\x75\x78\x2f\x6b\x65\x72\x6e\
+\x65\x6c\x2f\x6b\x65\x78\x65\x63\x5f\x62\x70\x66\x2f\x6b\x65\x78\x65\x63\x5f\
+\x70\x65\x5f\x70\x61\x72\x73\x65\x72\x5f\x62\x70\x66\x2e\x63\0\x5f\x5f\x61\x74\
+\x74\x72\x69\x62\x75\x74\x65\x5f\x5f\x28\x28\x75\x73\x65\x64\x29\x29\x20\x69\
+\x6e\x74\x20\x42\x50\x46\x5f\x50\x52\x4f\x47\x28\x70\x61\x72\x73\x65\x5f\x70\
+\x65\x2c\x20\x73\x74\x72\x75\x63\x74\x20\x6b\x65\x78\x65\x63\x5f\x63\x6f\x6e\
+\x74\x65\x78\x74\x20\x2a\x63\x6f\x6e\x74\x65\x78\x74\x2c\0\x63\x68\x61\x72\0\
+\x4c\x49\x43\x45\x4e\x53\x45\0\x64\x75\x6d\x6d\x79\x5f\x72\x6f\x64\x61\x74\x61\
+\0\x64\x75\x6d\x6d\x79\x5f\x64\x61\x74\x61\0\x64\x75\x6d\x6d\x79\x5f\x6d\x65\
+\x72\x67\x65\x61\x62\x6c\x65\x5f\x73\x74\x72\0\x64\x75\x6d\x6d\x79\x5f\x62\x73\
+\x73\0\x2e\x62\x73\x73\0\x2e\x64\x61\x74\x61\0\x2e\x6d\x61\x70\x73\0\x2e\x72\
+\x6f\x64\x61\x74\x61\0\x6c\x69\x63\x65\x6e\x73\x65\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\x84\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1b\0\0\0\0\0\0\0\0\0\0\0\0\
+\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x72\x69\x6e\x67\x62\x75\x66\x5f\x31\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1b\0\0\0\0\0\0\0\
+\0\0\0\0\0\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x72\x69\x6e\x67\x62\x75\x66\x5f\x32\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1b\0\0\
+\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x72\x69\x6e\x67\x62\x75\
+\x66\x5f\x33\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\x1b\0\0\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x72\x69\x6e\
+\x67\x62\x75\x66\x5f\x34\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\x02\0\0\0\x04\0\0\0\x10\0\0\0\x01\0\0\0\x80\0\0\0\0\0\0\0\0\0\
+\0\0\x6b\x65\x78\x65\x63\x5f\x70\x65\x2e\x72\x6f\x64\x61\x74\x61\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\x22\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x72\x6f\x64\x61\x74\x61\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\0\x04\0\0\0\x18\0\0\0\x01\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\x6b\x65\x78\x65\x63\x5f\x70\x65\x2e\x64\x61\x74\x61\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\x20\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x64\x61\x74\x61\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\0\x04\0\0\0\x0f\0\0\0\x01\0\0\0\x80\
+\0\0\0\0\0\0\0\0\0\0\0\x2e\x72\x6f\x64\x61\x74\x61\x2e\x73\x74\x72\x31\x2e\x31\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x2e\x72\x6f\x64\
+\x61\x74\x61\x2e\x73\x74\x72\x31\x2e\x31\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\0\x04\0\
+\0\0\x10\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x6b\x65\x78\x65\x63\x5f\x70\
+\x65\x2e\x62\x73\x73\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1f\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x47\x50\x4c\0\0\0\0\0\xb4\0\0\0\0\0\
+\0\0\x95\0\0\0\0\0\0\0\0\0\0\0\x12\0\0\0\0\0\0\0\x95\0\0\0\xc8\0\0\0\x1b\x20\0\
+\0\x1a\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\x10\0\0\0\x70\x61\x72\x73\x65\x5f\x70\x65\0\0\0\0\0\0\0\0\0\0\
+\0\0\x18\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x10\0\0\0\0\0\0\0\0\
+\0\0\0\x01\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x10\
+\0\0\0\0\0\0\0\x6b\x65\x78\x65\x63\x5f\x69\x6d\x61\x67\x65\x5f\x70\x61\x72\x73\
+\x65\x72\x5f\x61\x6e\x63\x68\x6f\x72\0\0\0\0\0\0\0";
+	static const char opts_insn[] __attribute__((__aligned__(8))) = "\
+\xbf\x16\0\0\0\0\0\0\xbf\xa1\0\0\0\0\0\0\x07\x01\0\0\x78\xff\xff\xff\xb7\x02\0\
+\0\x88\0\0\0\xb7\x03\0\0\0\0\0\0\x85\0\0\0\x71\0\0\0\x05\0\x3b\0\0\0\0\0\x61\
+\xa1\x78\xff\0\0\0\0\xd5\x01\x01\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa1\x7c\xff\
+\0\0\0\0\xd5\x01\x01\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa1\x80\xff\0\0\0\0\xd5\
+\x01\x01\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x61\
+\x01\0\0\0\0\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\
+\x18\x60\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\x61\x01\0\0\0\0\0\0\xd5\x01\x02\0\0\0\0\
+\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x08\0\0\
+\0\x61\x01\0\0\0\0\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\
+\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x61\x01\0\0\0\0\0\0\xd5\x01\x02\0\
+\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\
+\x10\0\0\0\x61\x01\0\0\0\0\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\
+\0\0\xa8\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x14\0\0\0\x61\x01\0\0\0\0\0\0\xd5\
+\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x18\x60\0\0\0\0\0\0\
+\0\0\0\0\x18\0\0\0\x61\x01\0\0\0\0\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\
+\0\x85\0\0\0\xa8\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x1c\0\0\0\x61\x01\0\0\0\0\0\
+\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\xbf\x70\0\0\0\
+\0\0\0\x95\0\0\0\0\0\0\0\x61\x60\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xa0\
+\x09\0\0\x63\x01\0\0\0\0\0\0\x61\x60\x0c\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\
+\x9c\x09\0\0\x63\x01\0\0\0\0\0\0\x79\x60\x10\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\
+\0\0\x90\x09\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\x05\0\0\x18\
+\x61\0\0\0\0\0\0\0\0\0\0\x88\x09\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\0\0\x12\0\0\0\
+\x18\x62\0\0\0\0\0\0\0\0\0\0\x88\x09\0\0\xb7\x03\0\0\x1c\0\0\0\x85\0\0\0\xa6\0\
+\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xad\xff\0\0\0\0\x63\x7a\x78\xff\0\0\0\0\x61\
+\x60\x1c\0\0\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xb4\x09\0\0\
+\x63\x01\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\xa8\x09\0\
+\0\xb7\x03\0\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xa0\
+\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x63\x71\0\0\0\0\0\0\x61\x60\
+\x2c\0\0\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xfc\x09\0\0\x63\
+\x01\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\xf0\x09\0\0\
+\xb7\x03\0\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x91\xff\
+\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\x63\x71\0\0\0\0\0\0\x61\x60\x3c\
+\0\0\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x44\x0a\0\0\x63\x01\
+\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x38\x0a\0\0\xb7\
+\x03\0\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x82\xff\0\0\
+\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\x63\x71\0\0\0\0\0\0\x61\x60\x4c\0\0\
+\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x8c\x0a\0\0\x63\x01\0\0\
+\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x80\x0a\0\0\xb7\x03\0\
+\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x73\xff\0\0\0\0\
+\x18\x61\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x63\x71\0\0\0\0\0\0\x61\xa0\x78\xff\0\0\
+\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xf8\x0a\0\0\x63\x01\0\0\0\0\0\0\x61\x60\x5c\0\
+\0\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xd4\x0a\0\0\x63\x01\0\
+\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\xc8\x0a\0\0\xb7\x03\
+\0\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x60\xff\0\0\0\0\
+\x18\x61\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\x63\x71\0\0\0\0\0\0\x79\x63\x60\0\0\0\0\
+\0\x15\x03\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x10\x0b\0\0\xb7\x02\0\0\
+\x10\0\0\0\x61\x60\x04\0\0\0\0\0\x45\0\x02\0\x01\0\0\0\x85\0\0\0\x94\0\0\0\x05\
+\0\x01\0\0\0\0\0\x85\0\0\0\x71\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\x61\
+\x20\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x28\x0b\0\0\x63\x01\0\0\0\0\0\0\
+\x18\x60\0\0\0\0\0\0\0\0\0\0\x20\x0b\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x30\x0b\0\
+\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x10\x0b\0\0\x18\x61\0\0\0\0\
+\0\0\0\0\0\0\x38\x0b\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\0\0\x02\0\0\0\x18\x62\0\0\
+\0\0\0\0\0\0\0\0\x28\x0b\0\0\xb7\x03\0\0\x20\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\
+\0\0\0\0\0\0\xc5\x07\x3c\xff\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\x61\
+\x20\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x48\x0b\0\0\x63\x01\0\0\0\0\0\0\
+\xb7\x01\0\0\x16\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x48\x0b\0\0\xb7\x03\0\0\x04\
+\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x2f\xff\0\0\0\0\x61\xa0\
+\x78\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x80\x0b\0\0\x63\x01\0\0\0\0\0\0\
+\x61\x60\x6c\0\0\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x5c\x0b\
+\0\0\x63\x01\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x50\
+\x0b\0\0\xb7\x03\0\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\
+\x1f\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x14\0\0\0\x63\x71\0\0\0\0\0\0\x79\
+\x63\x70\0\0\0\0\0\x15\x03\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x98\x0b\0\
+\0\xb7\x02\0\0\x18\0\0\0\x61\x60\x04\0\0\0\0\0\x45\0\x02\0\x01\0\0\0\x85\0\0\0\
+\x94\0\0\0\x05\0\x01\0\0\0\0\0\x85\0\0\0\x71\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\
+\x14\0\0\0\x61\x20\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xb8\x0b\0\0\x63\x01\
+\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xb0\x0b\0\0\x18\x61\0\0\0\0\0\0\0\0\0\
+\0\xc0\x0b\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x98\x0b\0\0\x18\
+\x61\0\0\0\0\0\0\0\0\0\0\xc8\x0b\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\0\0\x02\0\0\0\
+\x18\x62\0\0\0\0\0\0\0\0\0\0\xb8\x0b\0\0\xb7\x03\0\0\x20\0\0\0\x85\0\0\0\xa6\0\
+\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xfb\xfe\0\0\0\0\x61\x60\x7c\0\0\0\0\0\x15\0\
+\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xe4\x0b\0\0\x63\x01\0\0\0\0\0\0\xb7\
+\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\xd8\x0b\0\0\xb7\x03\0\0\x48\0\0\0\
+\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xef\xfe\0\0\0\0\x18\x61\0\0\0\
+\0\0\0\0\0\0\0\x18\0\0\0\x63\x71\0\0\0\0\0\0\x79\x63\x80\0\0\0\0\0\x15\x03\x08\
+\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x20\x0c\0\0\xb7\x02\0\0\x0f\0\0\0\x61\
+\x60\x04\0\0\0\0\0\x45\0\x02\0\x01\0\0\0\x85\0\0\0\x94\0\0\0\x05\0\x01\0\0\0\0\
+\0\x85\0\0\0\x71\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x18\0\0\0\x61\x20\0\0\0\0\0\
+\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x38\x0c\0\0\x63\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\
+\0\0\0\0\0\0\x30\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x40\x0c\0\0\x7b\x01\0\0\0\
+\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x20\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x48\
+\x0c\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\0\0\x02\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\
+\x38\x0c\0\0\xb7\x03\0\0\x20\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\
+\x07\xcb\xfe\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x18\0\0\0\x61\x20\0\0\0\0\0\0\
+\x18\x61\0\0\0\0\0\0\0\0\0\0\x58\x0c\0\0\x63\x01\0\0\0\0\0\0\xb7\x01\0\0\x16\0\
+\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x58\x0c\0\0\xb7\x03\0\0\x04\0\0\0\x85\0\0\0\
+\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xbe\xfe\0\0\0\0\x61\xa0\x78\xff\0\0\0\0\
+\x18\x61\0\0\0\0\0\0\0\0\0\0\x90\x0c\0\0\x63\x01\0\0\0\0\0\0\x61\x60\x8c\0\0\0\
+\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x6c\x0c\0\0\x63\x01\0\0\0\
+\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x60\x0c\0\0\xb7\x03\0\0\
+\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xae\xfe\0\0\0\0\x18\
+\x61\0\0\0\0\0\0\0\0\0\0\x1c\0\0\0\x63\x71\0\0\0\0\0\0\x79\x63\x90\0\0\0\0\0\
+\x15\x03\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xa8\x0c\0\0\xb7\x02\0\0\x10\
+\0\0\0\x61\x60\x04\0\0\0\0\0\x45\0\x02\0\x01\0\0\0\x85\0\0\0\x94\0\0\0\x05\0\
+\x01\0\0\0\0\0\x85\0\0\0\x71\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x1c\0\0\0\x61\
+\x20\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xc0\x0c\0\0\x63\x01\0\0\0\0\0\0\
+\x18\x60\0\0\0\0\0\0\0\0\0\0\xb8\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xc8\x0c\0\
+\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xa8\x0c\0\0\x18\x61\0\0\0\0\
+\0\0\0\0\0\0\xd0\x0c\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\0\0\x02\0\0\0\x18\x62\0\0\
+\0\0\0\0\0\0\0\0\xc0\x0c\0\0\xb7\x03\0\0\x20\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\
+\0\0\0\0\0\0\xc5\x07\x8a\xfe\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xe0\x0c\0\0\
+\x18\x61\0\0\0\0\0\0\0\0\0\0\x20\x0d\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\
+\0\0\0\0\0\xe8\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x18\x0d\0\0\x7b\x01\0\0\0\0\
+\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xf8\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x60\
+\x0d\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\x0d\0\0\x18\x61\0\0\
+\0\0\0\0\0\0\0\0\x70\x0d\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\
+\x10\x0d\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x90\x0d\0\0\x7b\x01\0\0\0\0\0\0\x18\
+\x60\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x88\x0d\0\0\x7b\
+\x01\0\0\0\0\0\0\x61\x60\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x28\x0d\0\0\
+\x63\x01\0\0\0\0\0\0\x61\x60\x0c\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x2c\x0d\
+\0\0\x63\x01\0\0\0\0\0\0\x79\x60\x10\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x30\
+\x0d\0\0\x7b\x01\0\0\0\0\0\0\x61\xa0\x78\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\
+\0\x58\x0d\0\0\x63\x01\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xa0\x0d\0\0\xb7\
+\x02\0\0\x1a\0\0\0\xb7\x03\0\0\x0c\0\0\0\xb7\x04\0\0\0\0\0\0\x85\0\0\0\xa7\0\0\
+\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x54\xfe\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x10\
+\x0d\0\0\x63\x70\x6c\0\0\0\0\0\x77\x07\0\0\x20\0\0\0\x63\x70\x70\0\0\0\0\0\xb7\
+\x01\0\0\x05\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x10\x0d\0\0\xb7\x03\0\0\x8c\0\0\
+\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x80\x0d\
+\0\0\x61\x01\0\0\0\0\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\
+\xa8\0\0\0\xc5\x07\x42\xfe\0\0\0\0\x63\x7a\x80\xff\0\0\0\0\x61\xa1\x78\xff\0\0\
+\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa0\x80\
+\xff\0\0\0\0\x63\x06\x98\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x61\x10\
+\0\0\0\0\0\0\x63\x06\x18\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\x61\
+\x10\0\0\0\0\0\0\x63\x06\x28\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\
+\x61\x10\0\0\0\0\0\0\x63\x06\x38\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x0c\0\0\
+\0\x61\x10\0\0\0\0\0\0\x63\x06\x48\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x10\0\
+\0\0\x61\x10\0\0\0\0\0\0\x63\x06\x58\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x14\
+\0\0\0\x61\x10\0\0\0\0\0\0\x63\x06\x68\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\
+\x18\0\0\0\x61\x10\0\0\0\0\0\0\x63\x06\x78\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\
+\0\x1c\0\0\0\x61\x10\0\0\0\0\0\0\x63\x06\x88\0\0\0\0\0\xb7\0\0\0\0\0\0\0\x95\0\
+\0\0\0\0\0\0";
+	opts.ctx = (struct bpf_loader_ctx *)skel;
+	opts.data_sz = sizeof(opts_data) - 1;
+	opts.data = (void *)opts_data;
+	opts.insns_sz = sizeof(opts_insn) - 1;
+	opts.insns = (void *)opts_insn;
+
+	err = bpf_load_and_run(&opts);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+static inline struct kexec_pe_parser_bpf *
+kexec_pe_parser_bpf__open_and_load(void)
+{
+	struct kexec_pe_parser_bpf *skel;
+
+	skel = kexec_pe_parser_bpf__open();
+	if (!skel)
+		return NULL;
+	if (kexec_pe_parser_bpf__load(skel)) {
+		kexec_pe_parser_bpf__destroy(skel);
+		return NULL;
+	}
+	return skel;
+}
+
+__attribute__((unused)) static void
+kexec_pe_parser_bpf__assert(struct kexec_pe_parser_bpf *s __attribute__((unused)))
+{
+#ifdef __cplusplus
+#define _Static_assert static_assert
+#endif
+#ifdef __cplusplus
+#undef _Static_assert
+#endif
+}
+
+#endif /* __KEXEC_PE_PARSER_BPF_SKEL_H__ */
diff --git a/kernel/kexec_bpf/template.c b/kernel/kexec_bpf/template.c
new file mode 100644
index 0000000000000..7f1557cb38223
--- /dev/null
+++ b/kernel/kexec_bpf/template.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2026 Red Hat, Inc
+//
+// Original file: kernel/kexec_bpf/template.c
+//
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_core_read.h>
+#include <bpf/bpf_endian.h>
+#include <bpf/bpf_tracing.h>
+
+/* Mark the bpf parser success */
+#define KEXEC_BPF_CMD_DONE		0x1
+#define KEXEC_BPF_CMD_DECOMPRESS	0x2
+#define KEXEC_BPF_CMD_COPY		0x3
+#define KEXEC_BPF_CMD_VERIFY_SIG	0x4
+
+#define KEXEC_BPF_SUBCMD_KERNEL		0x1
+#define KEXEC_BPF_SUBCMD_INITRD		0x2
+#define KEXEC_BPF_SUBCMD_CMDLINE	0x3
+
+#define KEXEC_BPF_PIPELINE_FILL		0x1
+
+/*
+ * The ringbufs can have different capacity. But only four ringbuf are provided.
+ */
+#ifndef RINGBUF1_SIZE
+#define RINGBUF1_SIZE	4
+#endif
+#ifndef RINGBUF2_SIZE
+#define RINGBUF2_SIZE	4
+#endif
+#ifndef RINGBUF3_SIZE
+#define RINGBUF3_SIZE	4
+#endif
+#ifndef RINGBUF4_SIZE
+#define RINGBUF4_SIZE	4
+#endif
+
+/* ringbuf is safe since the user space has no write access to them */
+struct {
+	__uint(type, BPF_MAP_TYPE_RINGBUF);
+	__uint(max_entries, RINGBUF1_SIZE);
+} ringbuf_1 SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_RINGBUF);
+	__uint(max_entries, RINGBUF2_SIZE);
+} ringbuf_2 SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_RINGBUF);
+	__uint(max_entries, RINGBUF3_SIZE);
+} ringbuf_3 SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_RINGBUF);
+	__uint(max_entries, RINGBUF4_SIZE);
+} ringbuf_4 SEC(".maps");
+
+char LICENSE[] SEC("license") = "GPL";
+
+/*
+ * This function ensures that the sections .rodata, .data, .rodata.str1.1 and .bss
+ * are created for a bpf prog.
+ */
+static const char dummy_rodata[16] __attribute__((used)) = "rodata";
+static char dummy_data[16] __attribute__((used)) = "data";
+static char *dummy_mergeable_str  __attribute__((used)) = ".rodata.str1.1";
+static char dummy_bss[16] __attribute__((used));
+
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCHv7 08/13] kexec_file: Factor out routine to find a symbol in ELF
  2026-03-22  1:43 [PATCHv7 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
                   ` (6 preceding siblings ...)
  2026-03-22  1:43 ` [PATCHv7 07/13] kexec_file: Introduce a bpf-prog lskel to run a format parser Pingfan Liu
@ 2026-03-22  1:43 ` Pingfan Liu
  2026-03-22  2:14   ` bot+bpf-ci
                     ` (2 more replies)
  2026-03-22  1:43 ` [PATCHv7 09/13] kexec_file: Integrate bpf light skeleton to load image with bpf-prog Pingfan Liu
                   ` (5 subsequent siblings)
  13 siblings, 3 replies; 26+ messages in thread
From: Pingfan Liu @ 2026-03-22  1:43 UTC (permalink / raw)
  To: kexec
  Cc: Pingfan Liu, David S. Miller, Alexei Starovoitov, Daniel Borkmann,
	John Fastabend, Andrii Nakryiko, Martin KaFai Lau,
	Eduard Zingerman, Song Liu, Yonghong Song, Jeremy Linton,
	Catalin Marinas, Will Deacon, Ard Biesheuvel, Simon Horman,
	Gerd Hoffmann, Vitaly Kuznetsov, Philipp Rudo, Viktor Malik,
	Jan Hendrik Farr, Baoquan He, Dave Young, Andrew Morton, bpf,
	systemd-devel, linux-kernel

The routine to search a symbol in ELF can be shared, so split it out.

Signed-off-by: Pingfan Liu <piliu@redhat.com>
Cc: Baoquan He <bhe@redhat.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Philipp Rudo <prudo@redhat.com>
To: kexec@lists.infradead.org
---
 kernel/kexec_file.c     | 61 +++++++++++++++++++++++++++++++++++++++++
 kernel/kexec_internal.h |  1 +
 2 files changed, 62 insertions(+)

diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index aca265034b4ed..15857a56e6278 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -902,6 +902,51 @@ static int kexec_calculate_store_digests(struct kimage *image)
 	return ret;
 }
 
+#if defined(CONFIG_ARCH_SUPPORTS_KEXEC_PURGATORY) || defined(CONFIG_KEXEC_BPF)
+const Elf_Sym *elf_find_symbol(const Elf_Ehdr *ehdr, const char *name)
+{
+	const Elf_Shdr *sechdrs;
+	const Elf_Sym *syms;
+	const char *strtab;
+	int i, k;
+
+	sechdrs = (void *)ehdr + ehdr->e_shoff;
+
+	for (i = 0; i < ehdr->e_shnum; i++) {
+		if (sechdrs[i].sh_type != SHT_SYMTAB)
+			continue;
+
+		if (sechdrs[i].sh_link >= ehdr->e_shnum)
+			/* Invalid strtab section number */
+			continue;
+		strtab = (void *)ehdr + sechdrs[sechdrs[i].sh_link].sh_offset;
+		syms = (void *)ehdr + sechdrs[i].sh_offset;
+
+		/* Go through symbols for a match */
+		for (k = 0; k < sechdrs[i].sh_size/sizeof(Elf_Sym); k++) {
+			if (ELF_ST_BIND(syms[k].st_info) != STB_GLOBAL)
+				continue;
+
+			if (strcmp(strtab + syms[k].st_name, name) != 0)
+				continue;
+
+			if (syms[k].st_shndx == SHN_UNDEF ||
+			    syms[k].st_shndx >= ehdr->e_shnum) {
+				pr_debug("Symbol: %s has bad section index %d.\n",
+						name, syms[k].st_shndx);
+				return NULL;
+			}
+
+			/* Found the symbol we are looking for */
+			return &syms[k];
+		}
+	}
+
+	return NULL;
+}
+
+#endif
+
 #ifdef CONFIG_ARCH_SUPPORTS_KEXEC_PURGATORY
 /*
  * kexec_purgatory_find_symbol - find a symbol in the purgatory
@@ -1221,6 +1266,22 @@ int kexec_load_purgatory(struct kimage *image, struct kexec_buf *kbuf)
 	return ret;
 }
 
+/*
+ * kexec_purgatory_find_symbol - find a symbol in the purgatory
+ * @pi:		Purgatory to search in.
+ * @name:	Name of the symbol.
+ *
+ * Return: pointer to symbol in read-only symtab on success, NULL on error.
+ */
+static const Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi,
+						  const char *name)
+{
+	if (!pi->ehdr)
+		return NULL;
+
+	return elf_find_symbol(pi->ehdr, name);
+}
+
 void *kexec_purgatory_get_symbol_addr(struct kimage *image, const char *name)
 {
 	struct purgatory_info *pi = &image->purgatory_info;
diff --git a/kernel/kexec_internal.h b/kernel/kexec_internal.h
index 731ff02110b3c..224f09188db23 100644
--- a/kernel/kexec_internal.h
+++ b/kernel/kexec_internal.h
@@ -40,6 +40,7 @@ void kimage_file_post_load_cleanup(struct kimage *image);
 extern char kexec_purgatory[];
 extern size_t kexec_purgatory_size;
 extern int decompose_kexec_image(struct kimage *image, int extended_fd);
+extern const Elf_Sym *elf_find_symbol(const Elf_Ehdr *ehdr, const char *name);
 #else /* CONFIG_KEXEC_FILE */
 static inline void kimage_file_post_load_cleanup(struct kimage *image) { }
 #endif /* CONFIG_KEXEC_FILE */
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCHv7 09/13] kexec_file: Integrate bpf light skeleton to load image with bpf-prog
  2026-03-22  1:43 [PATCHv7 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
                   ` (7 preceding siblings ...)
  2026-03-22  1:43 ` [PATCHv7 08/13] kexec_file: Factor out routine to find a symbol in ELF Pingfan Liu
@ 2026-03-22  1:43 ` Pingfan Liu
  2026-03-22  2:14   ` bot+bpf-ci
  2026-03-22  1:43 ` [PATCHv7 10/13] arm64/kexec: Select KEXEC_BPF to support UEFI-style kernel image Pingfan Liu
                   ` (4 subsequent siblings)
  13 siblings, 1 reply; 26+ messages in thread
From: Pingfan Liu @ 2026-03-22  1:43 UTC (permalink / raw)
  To: kexec
  Cc: Pingfan Liu, David S. Miller, Alexei Starovoitov, Daniel Borkmann,
	John Fastabend, Andrii Nakryiko, Martin KaFai Lau,
	Eduard Zingerman, Song Liu, Yonghong Song, Jeremy Linton,
	Catalin Marinas, Will Deacon, Ard Biesheuvel, Simon Horman,
	Gerd Hoffmann, Vitaly Kuznetsov, Philipp Rudo, Viktor Malik,
	Jan Hendrik Farr, Baoquan He, Dave Young, Andrew Morton, bpf,
	systemd-devel, linux-kernel

All kexec PE bpf prog should align with the interface exposed by the
light skeleton
    four maps:
                    struct bpf_map_desc ringbuf_1;
                    struct bpf_map_desc ringbuf_2;
                    struct bpf_map_desc ringbuf_3;
                    struct bpf_map_desc ringbuf_4;
    four sections:
                    struct bpf_map_desc rodata;
                    struct bpf_map_desc data;
                    struct bpf_map_desc rodata_str1_1;
                    struct bpf_map_desc bss;
    one prog:
            SEC("fentry.s/kexec_image_parser_anchor")

With the above presumption, the integration consists of two parts:
  -1. Call API exposed by light skeleton from kexec
  -2. The opts_insn[] and opts_data[] are bpf-prog dependent and
      can be extracted and passed in from the user space. In the
      kexec_file_load design, a PE file has a .bpf section, which data
      content is a ELF, and the ELF contains opts_insn[] opts_data[].
      As a bonus, BPF bytecode can be placed under the protection of the
      entire PE signature.
      (Note, since opts_insn[] contains the information of the ringbuf
       size, the bpf-prog writer can change its proper size according to
       the kernel image size without modifying the kernel code)

Signed-off-by: Pingfan Liu <piliu@redhat.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Baoquan He <bhe@redhat.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Philipp Rudo <prudo@redhat.com>
Cc: bpf@vger.kernel.org
To: kexec@lists.infradead.org
---
 kernel/Makefile                              |   1 +
 kernel/kexec_bpf/Makefile                    |   8 +
 kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h | 249 +------------------
 kernel/kexec_bpf_loader.c                    |  93 ++++++-
 4 files changed, 105 insertions(+), 246 deletions(-)

diff --git a/kernel/Makefile b/kernel/Makefile
index 9e17ad2a44b6f..caa9b3642ae8c 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -145,6 +145,7 @@ obj-$(CONFIG_SYSCTL_KUNIT_TEST) += sysctl-test.o
 
 CFLAGS_kstack_erase.o += $(DISABLE_KSTACK_ERASE)
 CFLAGS_kstack_erase.o += $(call cc-option,-mgeneral-regs-only)
+CFLAGS_kexec_bpf_loader.o += -I$(srctree)/tools/lib
 obj-$(CONFIG_KSTACK_ERASE) += kstack_erase.o
 KASAN_SANITIZE_kstack_erase.o := n
 KCSAN_SANITIZE_kstack_erase.o := n
diff --git a/kernel/kexec_bpf/Makefile b/kernel/kexec_bpf/Makefile
index 45d45cc0855a3..88e92eb910f64 100644
--- a/kernel/kexec_bpf/Makefile
+++ b/kernel/kexec_bpf/Makefile
@@ -39,6 +39,14 @@ clean:
 kexec_pe_parser_bpf.lskel.h: $(OUTPUT)/kexec_pe_parser_bpf.o | $(BPFTOOL)
 	$(call msg,GEN-SKEL,$@)
 	$(Q)$(BPFTOOL) gen skeleton -L $< > $@
+	@# The following sed commands make opts_data[] and opts_insn[] visible in a file instead of only in a function.
+	@# And it removes the bytecode
+	$(Q) sed -i '/static const char opts_data\[\].*=/,/";$$/d' $@
+	$(Q) sed -i '/static const char opts_insn\[\].*=/,/";$$/d' $@
+	$(Q) sed -i \
+		-e 's/opts\.data_sz = sizeof(opts_data) - 1;/opts.data_sz = opts_data_sz;/' \
+		-e 's/opts\.insns_sz = sizeof(opts_insn) - 1;/opts.insns_sz = opts_insn_sz;/' $@
+	$(Q) sed -i '7i static char *opts_data, *opts_insn;\nstatic unsigned int opts_data_sz, opts_insn_sz;' $@
 
 $(OUTPUT)/vmlinux.h: $(VMLINUX) $(DEFAULT_BPFTOOL) $(BPFOBJ) | $(OUTPUT)
 	@$(BPFTOOL) btf dump file $(VMLINUX) format c > $(OUTPUT)/vmlinux.h
diff --git a/kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h b/kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h
index 22fd77a872fc0..00e54301c394a 100644
--- a/kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h
+++ b/kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h
@@ -4,6 +4,8 @@
 #define __KEXEC_PE_PARSER_BPF_SKEL_H__
 
 #include <bpf/skel_internal.h>
+static char *opts_data, *opts_insn;
+static unsigned int opts_data_sz, opts_insn_sz;
 
 struct kexec_pe_parser_bpf {
 	struct bpf_loader_ctx ctx;
@@ -87,253 +89,10 @@ kexec_pe_parser_bpf__load(struct kexec_pe_parser_bpf *skel)
 {
 	struct bpf_load_and_run_opts opts = {};
 	int err;
-	static const char opts_data[] __attribute__((__aligned__(8))) = "\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x9f\xeb\x01\0\
-\x18\0\0\0\0\0\0\0\xf4\x02\0\0\xf4\x02\0\0\x78\x01\0\0\0\0\0\0\0\0\0\x02\x03\0\
-\0\0\x01\0\0\0\0\0\0\x01\x04\0\0\0\x20\0\0\x01\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\
-\0\0\x04\0\0\0\x1b\0\0\0\x05\0\0\0\0\0\0\x01\x04\0\0\0\x20\0\0\0\0\0\0\0\0\0\0\
-\x02\x06\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\x04\0\0\0\0\0\0\0\
-\x02\0\0\x04\x10\0\0\0\x19\0\0\0\x01\0\0\0\0\0\0\0\x1e\0\0\0\x05\0\0\0\x40\0\0\
-\0\x2a\0\0\0\0\0\0\x0e\x07\0\0\0\x01\0\0\0\0\0\0\0\x02\0\0\x04\x10\0\0\0\x19\0\
-\0\0\x01\0\0\0\0\0\0\0\x1e\0\0\0\x05\0\0\0\x40\0\0\0\x34\0\0\0\0\0\0\x0e\x09\0\
-\0\0\x01\0\0\0\0\0\0\0\x02\0\0\x04\x10\0\0\0\x19\0\0\0\x01\0\0\0\0\0\0\0\x1e\0\
-\0\0\x05\0\0\0\x40\0\0\0\x3e\0\0\0\0\0\0\x0e\x0b\0\0\0\x01\0\0\0\0\0\0\0\x02\0\
-\0\x04\x10\0\0\0\x19\0\0\0\x01\0\0\0\0\0\0\0\x1e\0\0\0\x05\0\0\0\x40\0\0\0\x48\
-\0\0\0\0\0\0\x0e\x0d\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\x02\x10\0\0\0\x52\0\0\0\0\0\
-\0\x01\x08\0\0\0\x40\0\0\0\0\0\0\0\x01\0\0\x0d\x02\0\0\0\x65\0\0\0\x0f\0\0\0\
-\x69\0\0\0\x01\0\0\x0c\x11\0\0\0\x14\x01\0\0\0\0\0\x01\x01\0\0\0\x08\0\0\x01\0\
-\0\0\0\0\0\0\x03\0\0\0\0\x13\0\0\0\x04\0\0\0\x04\0\0\0\x19\x01\0\0\0\0\0\x0e\
-\x14\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\x0a\x13\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x16\
-\0\0\0\x04\0\0\0\x10\0\0\0\x21\x01\0\0\0\0\0\x0e\x17\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\x03\0\0\0\0\x13\0\0\0\x04\0\0\0\x10\0\0\0\x2e\x01\0\0\0\0\0\x0e\x19\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x13\0\0\0\x04\0\0\0\x0f\0\0\0\0\0\0\0\0\0\0\
-\x02\x13\0\0\0\x39\x01\0\0\0\0\0\x0e\x1c\0\0\0\0\0\0\0\x4d\x01\0\0\0\0\0\x0e\
-\x19\0\0\0\0\0\0\0\x57\x01\0\0\x01\0\0\x0f\x10\0\0\0\x1e\0\0\0\0\0\0\0\x10\0\0\
-\0\x5c\x01\0\0\x02\0\0\x0f\x18\0\0\0\x1a\0\0\0\0\0\0\0\x10\0\0\0\x1d\0\0\0\x10\
-\0\0\0\x08\0\0\0\x62\x01\0\0\x04\0\0\x0f\x40\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\
-\x0a\0\0\0\x10\0\0\0\x10\0\0\0\x0c\0\0\0\x20\0\0\0\x10\0\0\0\x0e\0\0\0\x30\0\0\
-\0\x10\0\0\0\x68\x01\0\0\x01\0\0\x0f\x10\0\0\0\x18\0\0\0\0\0\0\0\x10\0\0\0\x70\
-\x01\0\0\x01\0\0\x0f\x04\0\0\0\x15\0\0\0\0\0\0\0\x04\0\0\0\0\x69\x6e\x74\0\x5f\
-\x5f\x41\x52\x52\x41\x59\x5f\x53\x49\x5a\x45\x5f\x54\x59\x50\x45\x5f\x5f\0\x74\
-\x79\x70\x65\0\x6d\x61\x78\x5f\x65\x6e\x74\x72\x69\x65\x73\0\x72\x69\x6e\x67\
-\x62\x75\x66\x5f\x31\0\x72\x69\x6e\x67\x62\x75\x66\x5f\x32\0\x72\x69\x6e\x67\
-\x62\x75\x66\x5f\x33\0\x72\x69\x6e\x67\x62\x75\x66\x5f\x34\0\x75\x6e\x73\x69\
-\x67\x6e\x65\x64\x20\x6c\x6f\x6e\x67\x20\x6c\x6f\x6e\x67\0\x63\x74\x78\0\x70\
-\x61\x72\x73\x65\x5f\x70\x65\0\x66\x65\x6e\x74\x72\x79\x2e\x73\x2f\x6b\x65\x78\
-\x65\x63\x5f\x69\x6d\x61\x67\x65\x5f\x70\x61\x72\x73\x65\x72\x5f\x61\x6e\x63\
-\x68\x6f\x72\0\x2f\x68\x6f\x6d\x65\x2f\x6c\x69\x6e\x75\x78\x2f\x6b\x65\x72\x6e\
-\x65\x6c\x2f\x6b\x65\x78\x65\x63\x5f\x62\x70\x66\x2f\x6b\x65\x78\x65\x63\x5f\
-\x70\x65\x5f\x70\x61\x72\x73\x65\x72\x5f\x62\x70\x66\x2e\x63\0\x5f\x5f\x61\x74\
-\x74\x72\x69\x62\x75\x74\x65\x5f\x5f\x28\x28\x75\x73\x65\x64\x29\x29\x20\x69\
-\x6e\x74\x20\x42\x50\x46\x5f\x50\x52\x4f\x47\x28\x70\x61\x72\x73\x65\x5f\x70\
-\x65\x2c\x20\x73\x74\x72\x75\x63\x74\x20\x6b\x65\x78\x65\x63\x5f\x63\x6f\x6e\
-\x74\x65\x78\x74\x20\x2a\x63\x6f\x6e\x74\x65\x78\x74\x2c\0\x63\x68\x61\x72\0\
-\x4c\x49\x43\x45\x4e\x53\x45\0\x64\x75\x6d\x6d\x79\x5f\x72\x6f\x64\x61\x74\x61\
-\0\x64\x75\x6d\x6d\x79\x5f\x64\x61\x74\x61\0\x64\x75\x6d\x6d\x79\x5f\x6d\x65\
-\x72\x67\x65\x61\x62\x6c\x65\x5f\x73\x74\x72\0\x64\x75\x6d\x6d\x79\x5f\x62\x73\
-\x73\0\x2e\x62\x73\x73\0\x2e\x64\x61\x74\x61\0\x2e\x6d\x61\x70\x73\0\x2e\x72\
-\x6f\x64\x61\x74\x61\0\x6c\x69\x63\x65\x6e\x73\x65\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\x84\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1b\0\0\0\0\0\0\0\0\0\0\0\0\
-\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x72\x69\x6e\x67\x62\x75\x66\x5f\x31\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1b\0\0\0\0\0\0\0\
-\0\0\0\0\0\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x72\x69\x6e\x67\x62\x75\x66\x5f\x32\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1b\0\0\
-\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x72\x69\x6e\x67\x62\x75\
-\x66\x5f\x33\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\x1b\0\0\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x72\x69\x6e\
-\x67\x62\x75\x66\x5f\x34\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\x02\0\0\0\x04\0\0\0\x10\0\0\0\x01\0\0\0\x80\0\0\0\0\0\0\0\0\0\
-\0\0\x6b\x65\x78\x65\x63\x5f\x70\x65\x2e\x72\x6f\x64\x61\x74\x61\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\x22\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x72\x6f\x64\x61\x74\x61\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\0\x04\0\0\0\x18\0\0\0\x01\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\x6b\x65\x78\x65\x63\x5f\x70\x65\x2e\x64\x61\x74\x61\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\x20\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x64\x61\x74\x61\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\0\x04\0\0\0\x0f\0\0\0\x01\0\0\0\x80\
-\0\0\0\0\0\0\0\0\0\0\0\x2e\x72\x6f\x64\x61\x74\x61\x2e\x73\x74\x72\x31\x2e\x31\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x2e\x72\x6f\x64\
-\x61\x74\x61\x2e\x73\x74\x72\x31\x2e\x31\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\0\x04\0\
-\0\0\x10\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x6b\x65\x78\x65\x63\x5f\x70\
-\x65\x2e\x62\x73\x73\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1f\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x47\x50\x4c\0\0\0\0\0\xb4\0\0\0\0\0\
-\0\0\x95\0\0\0\0\0\0\0\0\0\0\0\x12\0\0\0\0\0\0\0\x95\0\0\0\xc8\0\0\0\x1b\x20\0\
-\0\x1a\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\x10\0\0\0\x70\x61\x72\x73\x65\x5f\x70\x65\0\0\0\0\0\0\0\0\0\0\
-\0\0\x18\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x10\0\0\0\0\0\0\0\0\
-\0\0\0\x01\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x10\
-\0\0\0\0\0\0\0\x6b\x65\x78\x65\x63\x5f\x69\x6d\x61\x67\x65\x5f\x70\x61\x72\x73\
-\x65\x72\x5f\x61\x6e\x63\x68\x6f\x72\0\0\0\0\0\0\0";
-	static const char opts_insn[] __attribute__((__aligned__(8))) = "\
-\xbf\x16\0\0\0\0\0\0\xbf\xa1\0\0\0\0\0\0\x07\x01\0\0\x78\xff\xff\xff\xb7\x02\0\
-\0\x88\0\0\0\xb7\x03\0\0\0\0\0\0\x85\0\0\0\x71\0\0\0\x05\0\x3b\0\0\0\0\0\x61\
-\xa1\x78\xff\0\0\0\0\xd5\x01\x01\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa1\x7c\xff\
-\0\0\0\0\xd5\x01\x01\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa1\x80\xff\0\0\0\0\xd5\
-\x01\x01\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x61\
-\x01\0\0\0\0\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\
-\x18\x60\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\x61\x01\0\0\0\0\0\0\xd5\x01\x02\0\0\0\0\
-\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x08\0\0\
-\0\x61\x01\0\0\0\0\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\
-\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x61\x01\0\0\0\0\0\0\xd5\x01\x02\0\
-\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\
-\x10\0\0\0\x61\x01\0\0\0\0\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\
-\0\0\xa8\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x14\0\0\0\x61\x01\0\0\0\0\0\0\xd5\
-\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x18\x60\0\0\0\0\0\0\
-\0\0\0\0\x18\0\0\0\x61\x01\0\0\0\0\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\
-\0\x85\0\0\0\xa8\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x1c\0\0\0\x61\x01\0\0\0\0\0\
-\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\xbf\x70\0\0\0\
-\0\0\0\x95\0\0\0\0\0\0\0\x61\x60\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xa0\
-\x09\0\0\x63\x01\0\0\0\0\0\0\x61\x60\x0c\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\
-\x9c\x09\0\0\x63\x01\0\0\0\0\0\0\x79\x60\x10\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\
-\0\0\x90\x09\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\x05\0\0\x18\
-\x61\0\0\0\0\0\0\0\0\0\0\x88\x09\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\0\0\x12\0\0\0\
-\x18\x62\0\0\0\0\0\0\0\0\0\0\x88\x09\0\0\xb7\x03\0\0\x1c\0\0\0\x85\0\0\0\xa6\0\
-\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xad\xff\0\0\0\0\x63\x7a\x78\xff\0\0\0\0\x61\
-\x60\x1c\0\0\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xb4\x09\0\0\
-\x63\x01\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\xa8\x09\0\
-\0\xb7\x03\0\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xa0\
-\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x63\x71\0\0\0\0\0\0\x61\x60\
-\x2c\0\0\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xfc\x09\0\0\x63\
-\x01\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\xf0\x09\0\0\
-\xb7\x03\0\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x91\xff\
-\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\x63\x71\0\0\0\0\0\0\x61\x60\x3c\
-\0\0\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x44\x0a\0\0\x63\x01\
-\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x38\x0a\0\0\xb7\
-\x03\0\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x82\xff\0\0\
-\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\x63\x71\0\0\0\0\0\0\x61\x60\x4c\0\0\
-\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x8c\x0a\0\0\x63\x01\0\0\
-\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x80\x0a\0\0\xb7\x03\0\
-\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x73\xff\0\0\0\0\
-\x18\x61\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x63\x71\0\0\0\0\0\0\x61\xa0\x78\xff\0\0\
-\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xf8\x0a\0\0\x63\x01\0\0\0\0\0\0\x61\x60\x5c\0\
-\0\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xd4\x0a\0\0\x63\x01\0\
-\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\xc8\x0a\0\0\xb7\x03\
-\0\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x60\xff\0\0\0\0\
-\x18\x61\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\x63\x71\0\0\0\0\0\0\x79\x63\x60\0\0\0\0\
-\0\x15\x03\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x10\x0b\0\0\xb7\x02\0\0\
-\x10\0\0\0\x61\x60\x04\0\0\0\0\0\x45\0\x02\0\x01\0\0\0\x85\0\0\0\x94\0\0\0\x05\
-\0\x01\0\0\0\0\0\x85\0\0\0\x71\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\x61\
-\x20\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x28\x0b\0\0\x63\x01\0\0\0\0\0\0\
-\x18\x60\0\0\0\0\0\0\0\0\0\0\x20\x0b\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x30\x0b\0\
-\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x10\x0b\0\0\x18\x61\0\0\0\0\
-\0\0\0\0\0\0\x38\x0b\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\0\0\x02\0\0\0\x18\x62\0\0\
-\0\0\0\0\0\0\0\0\x28\x0b\0\0\xb7\x03\0\0\x20\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\
-\0\0\0\0\0\0\xc5\x07\x3c\xff\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\x61\
-\x20\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x48\x0b\0\0\x63\x01\0\0\0\0\0\0\
-\xb7\x01\0\0\x16\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x48\x0b\0\0\xb7\x03\0\0\x04\
-\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x2f\xff\0\0\0\0\x61\xa0\
-\x78\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x80\x0b\0\0\x63\x01\0\0\0\0\0\0\
-\x61\x60\x6c\0\0\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x5c\x0b\
-\0\0\x63\x01\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x50\
-\x0b\0\0\xb7\x03\0\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\
-\x1f\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x14\0\0\0\x63\x71\0\0\0\0\0\0\x79\
-\x63\x70\0\0\0\0\0\x15\x03\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x98\x0b\0\
-\0\xb7\x02\0\0\x18\0\0\0\x61\x60\x04\0\0\0\0\0\x45\0\x02\0\x01\0\0\0\x85\0\0\0\
-\x94\0\0\0\x05\0\x01\0\0\0\0\0\x85\0\0\0\x71\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\
-\x14\0\0\0\x61\x20\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xb8\x0b\0\0\x63\x01\
-\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xb0\x0b\0\0\x18\x61\0\0\0\0\0\0\0\0\0\
-\0\xc0\x0b\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x98\x0b\0\0\x18\
-\x61\0\0\0\0\0\0\0\0\0\0\xc8\x0b\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\0\0\x02\0\0\0\
-\x18\x62\0\0\0\0\0\0\0\0\0\0\xb8\x0b\0\0\xb7\x03\0\0\x20\0\0\0\x85\0\0\0\xa6\0\
-\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xfb\xfe\0\0\0\0\x61\x60\x7c\0\0\0\0\0\x15\0\
-\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xe4\x0b\0\0\x63\x01\0\0\0\0\0\0\xb7\
-\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\xd8\x0b\0\0\xb7\x03\0\0\x48\0\0\0\
-\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xef\xfe\0\0\0\0\x18\x61\0\0\0\
-\0\0\0\0\0\0\0\x18\0\0\0\x63\x71\0\0\0\0\0\0\x79\x63\x80\0\0\0\0\0\x15\x03\x08\
-\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x20\x0c\0\0\xb7\x02\0\0\x0f\0\0\0\x61\
-\x60\x04\0\0\0\0\0\x45\0\x02\0\x01\0\0\0\x85\0\0\0\x94\0\0\0\x05\0\x01\0\0\0\0\
-\0\x85\0\0\0\x71\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x18\0\0\0\x61\x20\0\0\0\0\0\
-\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x38\x0c\0\0\x63\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\
-\0\0\0\0\0\0\x30\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x40\x0c\0\0\x7b\x01\0\0\0\
-\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x20\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x48\
-\x0c\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\0\0\x02\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\
-\x38\x0c\0\0\xb7\x03\0\0\x20\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\
-\x07\xcb\xfe\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x18\0\0\0\x61\x20\0\0\0\0\0\0\
-\x18\x61\0\0\0\0\0\0\0\0\0\0\x58\x0c\0\0\x63\x01\0\0\0\0\0\0\xb7\x01\0\0\x16\0\
-\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x58\x0c\0\0\xb7\x03\0\0\x04\0\0\0\x85\0\0\0\
-\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xbe\xfe\0\0\0\0\x61\xa0\x78\xff\0\0\0\0\
-\x18\x61\0\0\0\0\0\0\0\0\0\0\x90\x0c\0\0\x63\x01\0\0\0\0\0\0\x61\x60\x8c\0\0\0\
-\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x6c\x0c\0\0\x63\x01\0\0\0\
-\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x60\x0c\0\0\xb7\x03\0\0\
-\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xae\xfe\0\0\0\0\x18\
-\x61\0\0\0\0\0\0\0\0\0\0\x1c\0\0\0\x63\x71\0\0\0\0\0\0\x79\x63\x90\0\0\0\0\0\
-\x15\x03\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xa8\x0c\0\0\xb7\x02\0\0\x10\
-\0\0\0\x61\x60\x04\0\0\0\0\0\x45\0\x02\0\x01\0\0\0\x85\0\0\0\x94\0\0\0\x05\0\
-\x01\0\0\0\0\0\x85\0\0\0\x71\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x1c\0\0\0\x61\
-\x20\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xc0\x0c\0\0\x63\x01\0\0\0\0\0\0\
-\x18\x60\0\0\0\0\0\0\0\0\0\0\xb8\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xc8\x0c\0\
-\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xa8\x0c\0\0\x18\x61\0\0\0\0\
-\0\0\0\0\0\0\xd0\x0c\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\0\0\x02\0\0\0\x18\x62\0\0\
-\0\0\0\0\0\0\0\0\xc0\x0c\0\0\xb7\x03\0\0\x20\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\
-\0\0\0\0\0\0\xc5\x07\x8a\xfe\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xe0\x0c\0\0\
-\x18\x61\0\0\0\0\0\0\0\0\0\0\x20\x0d\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\
-\0\0\0\0\0\xe8\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x18\x0d\0\0\x7b\x01\0\0\0\0\
-\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xf8\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x60\
-\x0d\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\x0d\0\0\x18\x61\0\0\
-\0\0\0\0\0\0\0\0\x70\x0d\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\
-\x10\x0d\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x90\x0d\0\0\x7b\x01\0\0\0\0\0\0\x18\
-\x60\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x88\x0d\0\0\x7b\
-\x01\0\0\0\0\0\0\x61\x60\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x28\x0d\0\0\
-\x63\x01\0\0\0\0\0\0\x61\x60\x0c\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x2c\x0d\
-\0\0\x63\x01\0\0\0\0\0\0\x79\x60\x10\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x30\
-\x0d\0\0\x7b\x01\0\0\0\0\0\0\x61\xa0\x78\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\
-\0\x58\x0d\0\0\x63\x01\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xa0\x0d\0\0\xb7\
-\x02\0\0\x1a\0\0\0\xb7\x03\0\0\x0c\0\0\0\xb7\x04\0\0\0\0\0\0\x85\0\0\0\xa7\0\0\
-\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x54\xfe\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x10\
-\x0d\0\0\x63\x70\x6c\0\0\0\0\0\x77\x07\0\0\x20\0\0\0\x63\x70\x70\0\0\0\0\0\xb7\
-\x01\0\0\x05\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x10\x0d\0\0\xb7\x03\0\0\x8c\0\0\
-\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x80\x0d\
-\0\0\x61\x01\0\0\0\0\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\
-\xa8\0\0\0\xc5\x07\x42\xfe\0\0\0\0\x63\x7a\x80\xff\0\0\0\0\x61\xa1\x78\xff\0\0\
-\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa0\x80\
-\xff\0\0\0\0\x63\x06\x98\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x61\x10\
-\0\0\0\0\0\0\x63\x06\x18\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\x61\
-\x10\0\0\0\0\0\0\x63\x06\x28\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\
-\x61\x10\0\0\0\0\0\0\x63\x06\x38\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x0c\0\0\
-\0\x61\x10\0\0\0\0\0\0\x63\x06\x48\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x10\0\
-\0\0\x61\x10\0\0\0\0\0\0\x63\x06\x58\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x14\
-\0\0\0\x61\x10\0\0\0\0\0\0\x63\x06\x68\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\
-\x18\0\0\0\x61\x10\0\0\0\0\0\0\x63\x06\x78\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\
-\0\x1c\0\0\0\x61\x10\0\0\0\0\0\0\x63\x06\x88\0\0\0\0\0\xb7\0\0\0\0\0\0\0\x95\0\
-\0\0\0\0\0\0";
 	opts.ctx = (struct bpf_loader_ctx *)skel;
-	opts.data_sz = sizeof(opts_data) - 1;
+	opts.data_sz = opts_data_sz;
 	opts.data = (void *)opts_data;
-	opts.insns_sz = sizeof(opts_insn) - 1;
+	opts.insns_sz = opts_insn_sz;
 	opts.insns = (void *)opts_insn;
 
 	err = bpf_load_and_run(&opts);
diff --git a/kernel/kexec_bpf_loader.c b/kernel/kexec_bpf_loader.c
index 7f7884411e2c7..430f6c3970b61 100644
--- a/kernel/kexec_bpf_loader.c
+++ b/kernel/kexec_bpf_loader.c
@@ -24,14 +24,105 @@
 #include <linux/decompress/generic.h>
 #include "kexec_internal.h"
 
+#include "kexec_bpf/kexec_pe_parser_bpf.lskel.h"
+
+static struct kexec_pe_parser_bpf *pe_parser;
+
+static void *get_symbol_from_elf(const char *elf_data, size_t elf_size,
+				 const char *symbol_name,
+				 unsigned int *symbol_size)
+{
+	Elf_Ehdr *ehdr = (Elf_Ehdr *)elf_data;
+	Elf_Shdr *shdr, *dst_shdr;
+	const Elf_Sym *sym;
+	void *symbol_data;
+
+	/* Check minimum size for ELF header */
+	if (elf_size < sizeof(Elf_Ehdr)) {
+		pr_err("ELF file too small\n");
+		return NULL;
+	}
+
+	if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
+		pr_err("Not a valid ELF file\n");
+		return NULL;
+	}
+
+	/* Check section header table bounds */
+	if (ehdr->e_shoff > elf_size ||
+	    ehdr->e_shoff + (ehdr->e_shnum * sizeof(Elf_Shdr)) > elf_size) {
+		pr_err("Section header table out of bounds\n");
+		return NULL;
+	}
+
+	sym = elf_find_symbol(ehdr, symbol_name);
+	if (!sym)
+		return NULL;
+
+	/* Check symbol section index */
+	if (sym->st_shndx >= ehdr->e_shnum) {
+		pr_err("Symbol section index out of bounds\n");
+		return NULL;
+	}
+
+	shdr = (struct elf_shdr *)(elf_data + ehdr->e_shoff);
+	dst_shdr = &shdr[sym->st_shndx];
+
+	/* Check section data bounds */
+	if (dst_shdr->sh_offset > elf_size ||
+	    dst_shdr->sh_offset + dst_shdr->sh_size > elf_size ||
+	    sym->st_value > dst_shdr->sh_size) {
+		pr_err("Symbol data out of bounds\n");
+		return NULL;
+	}
+
+	symbol_data = (void *)(elf_data + dst_shdr->sh_offset + sym->st_value);
+
+	if (symbol_size)
+		*symbol_size = sym->st_size;
+
+	return symbol_data;
+}
+
 /* Load a ELF */
 static int arm_bpf_prog(char *bpf_elf, unsigned long sz)
 {
-	return -1;
+	opts_data = get_symbol_from_elf(bpf_elf, sz, "opts_data", &opts_data_sz);
+	opts_insn = get_symbol_from_elf(bpf_elf, sz, "opts_insn", &opts_insn_sz);
+	if (!opts_data || !opts_insn) {
+		pr_err("Cannot get symbol from ELF: opts_data=%px, opts_insn=%px\n",
+			opts_data, opts_insn);
+		return -1;
+	}
+
+	if (opts_data_sz < 1 || opts_insn_sz < 1) {
+		pr_err("Symbol size too small (opts_data_sz=%u, opts_insn_sz=%u)\n",
+		       opts_data_sz, opts_insn_sz);
+		return -1;
+	}
+	/*
+	 * When light skeleton generates opts_data[] and opts_insn[], it appends a
+	 * NULL terminator at the end of string
+	 */
+	opts_data_sz = opts_data_sz - 1;
+	opts_insn_sz = opts_insn_sz - 1;
+
+	pe_parser = kexec_pe_parser_bpf__open_and_load();
+	if (!pe_parser) {
+		pr_info("Can not open and load bpf parser\n");
+		return -1;
+	}
+	kexec_pe_parser_bpf__attach(pe_parser);
+
+	return 0;
 }
 
 static void disarm_bpf_prog(void)
 {
+	kexec_pe_parser_bpf__destroy(pe_parser);
+	pe_parser = NULL;
+	opts_data = NULL;
+	opts_insn = NULL;
 }
 
 #define MAX_PARSING_BUF_NUM    16
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCHv7 10/13] arm64/kexec: Select KEXEC_BPF to support UEFI-style kernel image
  2026-03-22  1:43 [PATCHv7 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
                   ` (8 preceding siblings ...)
  2026-03-22  1:43 ` [PATCHv7 09/13] kexec_file: Integrate bpf light skeleton to load image with bpf-prog Pingfan Liu
@ 2026-03-22  1:43 ` Pingfan Liu
  2026-03-22  1:44 ` [PATCHv7 11/13] tools/kexec: Introduce a bpf-prog to handle zboot image Pingfan Liu
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 26+ messages in thread
From: Pingfan Liu @ 2026-03-22  1:43 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Pingfan Liu, David S. Miller, Alexei Starovoitov, Daniel Borkmann,
	John Fastabend, Andrii Nakryiko, Martin KaFai Lau,
	Eduard Zingerman, Song Liu, Yonghong Song, Jeremy Linton,
	Catalin Marinas, Will Deacon, Ard Biesheuvel, Simon Horman,
	Gerd Hoffmann, Vitaly Kuznetsov, Philipp Rudo, Viktor Malik,
	Jan Hendrik Farr, Baoquan He, Dave Young, Andrew Morton, kexec,
	bpf, systemd-devel, linux-kernel

Now everything is ready for kexec PE image parser. Select it on arm64
for zboot and UKI image support.

Signed-off-by: Pingfan Liu <piliu@redhat.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
To: linux-arm-kernel@lists.infradead.org
---
 arch/arm64/Kconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 38dba5f7e4d2d..59f948a6f69d7 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -1608,6 +1608,7 @@ config ARCH_SELECTS_KEXEC_FILE
 	def_bool y
 	depends on KEXEC_FILE
 	select HAVE_IMA_KEXEC if IMA
+	select KEXEC_BPF if DEBUG_INFO_BTF && BPF_SYSCALL && KEEP_DECOMPRESSOR
 
 config ARCH_SUPPORTS_KEXEC_SIG
 	def_bool y
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCHv7 11/13] tools/kexec: Introduce a bpf-prog to handle zboot image
  2026-03-22  1:43 [PATCHv7 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
                   ` (9 preceding siblings ...)
  2026-03-22  1:43 ` [PATCHv7 10/13] arm64/kexec: Select KEXEC_BPF to support UEFI-style kernel image Pingfan Liu
@ 2026-03-22  1:44 ` Pingfan Liu
  2026-03-22  2:26   ` bot+bpf-ci
  2026-03-22  1:44 ` [PATCHv7 12/13] tools/kexec: Introduce a bpf-prog to handle UKI image Pingfan Liu
                   ` (2 subsequent siblings)
  13 siblings, 1 reply; 26+ messages in thread
From: Pingfan Liu @ 2026-03-22  1:44 UTC (permalink / raw)
  To: kexec
  Cc: Pingfan Liu, David S. Miller, Alexei Starovoitov, Daniel Borkmann,
	John Fastabend, Andrii Nakryiko, Martin KaFai Lau,
	Eduard Zingerman, Song Liu, Yonghong Song, Jeremy Linton,
	Catalin Marinas, Will Deacon, Ard Biesheuvel, Simon Horman,
	Gerd Hoffmann, Vitaly Kuznetsov, Philipp Rudo, Viktor Malik,
	Jan Hendrik Farr, Baoquan He, Dave Young, Andrew Morton, bpf,
	systemd-devel, linux-kernel

This BPF program aligns with the convention defined in the kernel file
kexec_pe_parser_bpf.lskel.h. This can be easily achieved by include
"template.c", which includes:
    four maps:
                    struct bpf_map_desc ringbuf_1;
                    struct bpf_map_desc ringbuf_2;
                    struct bpf_map_desc ringbuf_3;
                    struct bpf_map_desc ringbuf_4;
    four sections:
                    struct bpf_map_desc rodata;
                    struct bpf_map_desc data;
                    struct bpf_map_desc bss;
                    struct bpf_map_desc rodata_str1_1;

The only left thing is to implement a prog
SEC("fentry.s/kexec_image_parser_anchor")
int BPF_PROG(parse_pe, struct kexec_context *context, unsigned long parser_id)

This bpf-prog can handle two kinds of formats:
-1. vmlinuz.efi, the zboot format, it can be derived from UKI's .linux
section.
-2. an envelop format, which is a ELF file holding three key sections:
.kernel, .initrd, .cmdline.

This BPF program only uses ringbuf_1, so it minimizes the size of the
other three ringbufs to one byte.  The size of ringbuf_1 is derived from
the combined size of vmlinuz.efi, initramfs, and cmdline, which
typically totals less than 128MB.  With the help of the BPF kfunc
bpf_buffer_parser(), the BPF program passes instructions to the kexec
BPF component to perform the appropriate actions.

Signed-off-by: Pingfan Liu <piliu@redhat.com>
Cc: Baoquan He <bhe@redhat.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Philipp Rudo <prudo@redhat.com>
Cc: bpf@vger.kernel.org
To: kexec@lists.infradead.org
---
 tools/kexec/Makefile           | 162 +++++++++++++++
 tools/kexec/template.c         |  72 +++++++
 tools/kexec/zboot_parser_bpf.c | 347 +++++++++++++++++++++++++++++++++
 3 files changed, 581 insertions(+)
 create mode 100644 tools/kexec/Makefile
 create mode 100644 tools/kexec/template.c
 create mode 100644 tools/kexec/zboot_parser_bpf.c

diff --git a/tools/kexec/Makefile b/tools/kexec/Makefile
new file mode 100644
index 0000000000000..a404a1453c888
--- /dev/null
+++ b/tools/kexec/Makefile
@@ -0,0 +1,162 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# Ensure Kbuild variables are available
+include ../scripts/Makefile.include
+
+srctree := $(patsubst %/tools/kexec,%,$(CURDIR))
+VMLINUX = $(srctree)/vmlinux
+TOOLSDIR := $(srctree)/tools
+LIBDIR := $(TOOLSDIR)/lib
+BPFDIR := $(LIBDIR)/bpf
+ARCH ?= $(shell uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ -e s/aarch64.*/arm64/ -e s/riscv64/riscv/ -e s/loongarch.*/loongarch/)
+# At present, zboot image format is used by arm64, riscv, loongarch
+# And arch/$(ARCH)/boot/vmlinux.bin is the uncompressed file instead of arch/$(ARCH)/boot/Image
+ifeq ($(ARCH),$(filter $(ARCH),arm64 riscv loongarch))
+	EFI_IMAGE := $(srctree)/arch/$(ARCH)/boot/vmlinuz.efi
+	KERNEL_IMAGE := $(srctree)/arch/$(ARCH)/boot/vmlinux.bin
+else
+	@echo "Unsupported architecture: $(ARCH)"
+	@exit 1
+endif
+
+CC = clang
+CFLAGS = -O2
+BPF_PROG_CFLAGS = -g -fno-merge-all-constants -O2 -target bpf -Wall -I $(BPFDIR) -I .
+BPFTOOL = bpftool
+
+# ---------------------------------------------------------------------------
+# Shared generated headers (common to all targets)
+# ---------------------------------------------------------------------------
+HEADERS = vmlinux.h bpf_helper_defs.h image_size.h
+
+# ---------------------------------------------------------------------------
+# Per-target artifact lists
+# To add a new target (e.g. uki), append to BPF_TARGETS and define a
+# <name>: phony rule below.  All build rules are driven by pattern rules
+# and require no further changes.
+#
+# Artifacts produced per prefix <P>:
+#   <P>_parser_bpf.o       - compiled BPF object
+#   <P>_parser_bpf.lskel.h - light skeleton header
+#   <P>_bytecode.c         - extracted opts_data / opts_insn arrays
+#   <P>_bytecode.o         - compiled bytecode object
+#   <P>.bpf                - final ELF wrapper with .bpf.1 section
+# ---------------------------------------------------------------------------
+BPF_TARGETS = zboot
+
+define BPF_ARTIFACTS
+$(1)_parser_bpf.o $(1)_parser_bpf.lskel.h $(1)_bytecode.c $(1)_bytecode.o $(1).bpf
+endef
+
+ALL_BPF_ARTIFACTS = $(foreach t,$(BPF_TARGETS),$(call BPF_ARTIFACTS,$(t)))
+
+# ---------------------------------------------------------------------------
+# Top-level phony targets
+# ---------------------------------------------------------------------------
+zboot: $(HEADERS) $(call BPF_ARTIFACTS,zboot) build_zboot_image
+
+.PHONY: zboot clean
+
+# ---------------------------------------------------------------------------
+# Shared header rules
+# ---------------------------------------------------------------------------
+
+# Rule to generate vmlinux.h from vmlinux
+vmlinux.h: $(VMLINUX)
+	@command -v $(BPFTOOL) >/dev/null 2>&1 || { echo >&2 "$(BPFTOOL) is required but not found. Please install it."; exit 1; }
+	@$(BPFTOOL) btf dump file $(VMLINUX) format c > vmlinux.h
+
+bpf_helper_defs.h: $(srctree)/tools/include/uapi/linux/bpf.h
+	@$(QUIET_GEN)$(srctree)/scripts/bpf_doc.py --header \
+		--file $(srctree)/tools/include/uapi/linux/bpf.h > bpf_helper_defs.h
+
+# Default estimated size for initramfs (can be overridden by user)
+INITRD_ESTIMATE_SIZE ?= 67108864   # 64MB
+
+# In worst case, this image includes vmlinuz.efi, initramfs and cmdline
+image_size.h: $(KERNEL_IMAGE)
+	@{ \
+		if [ ! -f "$(KERNEL_IMAGE)" ]; then \
+			echo "Error: File '$(KERNEL_IMAGE)' does not exist"; \
+			exit 1; \
+		fi; \
+		KERNEL_SIZE=$$(stat -c '%s' "$(KERNEL_IMAGE)" 2>/dev/null); \
+		ELF_OVERHEAD=4096; \
+		TOTAL_SIZE=$$((KERNEL_SIZE + $(INITRD_ESTIMATE_SIZE) + ELF_OVERHEAD)); \
+		POWER=4096; \
+		while [ $$POWER -le $$TOTAL_SIZE ]; do \
+			POWER=$$((POWER * 2)); \
+		done; \
+		RINGBUF_SIZE=$$POWER; \
+		echo "#define IMAGE_SIZE_POWER2_ALIGN $$RINGBUF_SIZE" > $@; \
+		echo "#define IMAGE_SIZE $$TOTAL_SIZE" >> $@; \
+		echo "#define KERNEL_SIZE $$KERNEL_SIZE" >> $@; \
+		echo "#define INITRD_SIZE $(INITRD_ESTIMATE_SIZE)" >> $@; \
+	}
+
+# ---------------------------------------------------------------------------
+# Pattern rules: BPF build pipeline
+# All rules below are prefix-agnostic; % matches zboot, uki, etc.
+# ---------------------------------------------------------------------------
+
+%_parser_bpf.o: %_parser_bpf.c vmlinux.h bpf_helper_defs.h
+	@$(CC) $(BPF_PROG_CFLAGS) -c $< -o $@
+
+%_parser_bpf.lskel.h: %_parser_bpf.o
+	@$(BPFTOOL) gen skeleton -L $< > $@
+
+# Extract opts_data[] and opts_insn[] arrays from the skeleton header,
+# stripping 'static' so the symbols are not optimized away by the compiler.
+# This rule is intentionally generic: all parsers expose the same symbol names.
+%_bytecode.c: %_parser_bpf.lskel.h
+	@sed -n '/static const char opts_data\[\]/,/;/p' $< | sed 's/static const/const/' > $@
+	@sed -n '/static const char opts_insn\[\]/,/;/p' $< | sed 's/static const/const/' >> $@
+
+%_bytecode.o: %_bytecode.c
+	@$(CC) $(CFLAGS) -c $< -o $@
+
+
+# Wrap the bytecode ELF object into a new ELF container as section .bpf.1
+# ---------------------------------------------------------------------------
+# Per-target BPF section definitions
+# Format: space-separated "sectionname:sourcefile" pairs
+# ---------------------------------------------------------------------------
+ZBOOT_BPF_MAPS := .bpf.1:zboot_bytecode.o
+
+# ---------------------------------------------------------------------------
+# Helpers to build objcopy flags from a BPF_MAPS list
+# ---------------------------------------------------------------------------
+section_name       = $(firstword $(subst :, ,$(1)))
+source_file        = $(lastword  $(subst :, ,$(1)))
+
+only_section_flags = $(foreach m,$(1),--only-section=$(call section_name,$(m)))
+
+# ---------------------------------------------------------------------------
+# Template: generates the %.bpf rule for a given target
+# $(1) = lowercase target name, e.g. zboot
+# $(2) = UPPER prefix for _BPF_MAPS variable, e.g. ZBOOT
+#
+# Sections are added one at a time in the order defined in $(2)_BPF_MAPS.
+# objcopy does not guarantee section order when all --add-section flags are
+# given in a single invocation, so we chain N calls through a .work.o file
+# to preserve the declared order.
+# ---------------------------------------------------------------------------
+define BPF_WRAPPER_RULE
+$(1).bpf: $(foreach m,$($(2)_BPF_MAPS),$(call source_file,$(m)))
+	@echo '' | $(CC) -x c - -c -o $$@.work.o
+	$(foreach m,$($(2)_BPF_MAPS),\
+	@objcopy --add-section $(call section_name,$(m))=$(call source_file,$(m)) \
+	         --set-section-flags $(call section_name,$(m))=readonly,data \
+	         $$@.work.o $$@.next.o && mv $$@.next.o $$@.work.o
+	)
+	@objcopy $(call only_section_flags,$($(2)_BPF_MAPS)) $$@.work.o $$@
+	@rm -f $$@.work.o
+endef
+
+$(eval $(call BPF_WRAPPER_RULE,zboot,ZBOOT))
+
+# ---------------------------------------------------------------------------
+# Clean
+# ---------------------------------------------------------------------------
+clean:
+	@rm -f $(HEADERS) $(ALL_BPF_ARTIFACTS) *.base.o
diff --git a/tools/kexec/template.c b/tools/kexec/template.c
new file mode 100644
index 0000000000000..7f1557cb38223
--- /dev/null
+++ b/tools/kexec/template.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2026 Red Hat, Inc
+//
+// Original file: kernel/kexec_bpf/template.c
+//
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_core_read.h>
+#include <bpf/bpf_endian.h>
+#include <bpf/bpf_tracing.h>
+
+/* Mark the bpf parser success */
+#define KEXEC_BPF_CMD_DONE		0x1
+#define KEXEC_BPF_CMD_DECOMPRESS	0x2
+#define KEXEC_BPF_CMD_COPY		0x3
+#define KEXEC_BPF_CMD_VERIFY_SIG	0x4
+
+#define KEXEC_BPF_SUBCMD_KERNEL		0x1
+#define KEXEC_BPF_SUBCMD_INITRD		0x2
+#define KEXEC_BPF_SUBCMD_CMDLINE	0x3
+
+#define KEXEC_BPF_PIPELINE_FILL		0x1
+
+/*
+ * The ringbufs can have different capacity. But only four ringbuf are provided.
+ */
+#ifndef RINGBUF1_SIZE
+#define RINGBUF1_SIZE	4
+#endif
+#ifndef RINGBUF2_SIZE
+#define RINGBUF2_SIZE	4
+#endif
+#ifndef RINGBUF3_SIZE
+#define RINGBUF3_SIZE	4
+#endif
+#ifndef RINGBUF4_SIZE
+#define RINGBUF4_SIZE	4
+#endif
+
+/* ringbuf is safe since the user space has no write access to them */
+struct {
+	__uint(type, BPF_MAP_TYPE_RINGBUF);
+	__uint(max_entries, RINGBUF1_SIZE);
+} ringbuf_1 SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_RINGBUF);
+	__uint(max_entries, RINGBUF2_SIZE);
+} ringbuf_2 SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_RINGBUF);
+	__uint(max_entries, RINGBUF3_SIZE);
+} ringbuf_3 SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_RINGBUF);
+	__uint(max_entries, RINGBUF4_SIZE);
+} ringbuf_4 SEC(".maps");
+
+char LICENSE[] SEC("license") = "GPL";
+
+/*
+ * This function ensures that the sections .rodata, .data, .rodata.str1.1 and .bss
+ * are created for a bpf prog.
+ */
+static const char dummy_rodata[16] __attribute__((used)) = "rodata";
+static char dummy_data[16] __attribute__((used)) = "data";
+static char *dummy_mergeable_str  __attribute__((used)) = ".rodata.str1.1";
+static char dummy_bss[16] __attribute__((used));
+
diff --git a/tools/kexec/zboot_parser_bpf.c b/tools/kexec/zboot_parser_bpf.c
new file mode 100644
index 0000000000000..10098dca2a27a
--- /dev/null
+++ b/tools/kexec/zboot_parser_bpf.c
@@ -0,0 +1,347 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2025, 2026 Red Hat, Inc
+//
+#include "vmlinux.h"
+#include <bpf_helpers.h>
+#include <bpf_tracing.h>
+#include "image_size.h"
+
+/* ringbuf 2,3,4 are useless */
+#define MIN_BUF_SIZE    1
+#define MAX_RECORD_SIZE (IMAGE_SIZE + 40960)
+#define RINGBUF1_SIZE   IMAGE_SIZE_POWER2_ALIGN
+#define RINGBUF2_SIZE   MIN_BUF_SIZE
+#define RINGBUF3_SIZE   MIN_BUF_SIZE
+#define RINGBUF4_SIZE   MIN_BUF_SIZE
+
+#include "template.c"
+
+#define ELF_SCAN_MAX 8
+
+/* SHN_UNDEF is a uapi macro not exported via BTF/vmlinux.h */
+#ifndef SHN_UNDEF
+#define SHN_UNDEF 0
+#endif
+
+#ifndef EIO
+#define EIO 5
+#endif
+#ifndef EINVAL
+#define EINVAL 22
+#endif
+
+/* see drivers/firmware/efi/libstub/zboot-header.S */
+struct linux_pe_zboot_header {
+	unsigned int mz_magic;
+	char image_type[4];
+	unsigned int payload_offset;
+	unsigned int payload_size;
+	unsigned int reserved[2];
+	char comp_type[4];
+	unsigned int linux_pe_magic;
+	unsigned int pe_header_offset;
+} __attribute__((packed));
+
+static const char linux_sect_name[]   = ".kernel";
+static const char initrd_sect_name[]   = ".initrd";
+static const char cmdline_sect_name[]   = ".cmdline";
+
+/*
+ * fill_cmd - overwrite the cmd_hdr at the start of @buf and copy @data_len
+ *            bytes from @src into the payload area.
+ *
+ * num_chunks is reserved for future use and always set to 0.
+ * payload_len directly describes the raw data length.
+ *
+ * Returns the total byte count to pass to bpf_buffer_parser().
+ */
+static int fill_cmd(char *buf, __u16 cmd, __u16 subcmd,
+				    const char *src, __u32 data_len)
+{
+	struct cmd_hdr *hdr;
+	char *payload;
+
+	hdr              = (struct cmd_hdr *)buf;
+	hdr->cmd         = cmd;
+	hdr->subcmd      = subcmd;
+	hdr->payload_len = data_len;
+	hdr->num_chunks  = 0;
+
+	payload = (char *)(hdr + 1);
+	/* Only cmd, no payload */
+	if (!src || !data_len)
+		return sizeof(*hdr);
+	if (data_len > MAX_RECORD_SIZE - sizeof(struct cmd_hdr))
+		return 0;
+	bpf_probe_read_kernel(payload, data_len, src);
+
+	return sizeof(*hdr) + data_len;
+}
+
+/*
+ * do_zboot_decompress - verify (if required) and decompress an arm64 zboot
+ *                       PE image.
+ *
+ * @ringbuf:      preallocated ringbuf to use for commands
+ * @pe_buf:       pointer to the start of the PE blob
+ * @pe_sz:        size of the PE blob
+ * @sig_mode: signature enforcement policy from kexec_context
+ * @bpf:          parser context
+ *
+ * Returns 0 on success, negative errno otherwise.
+ */
+static int do_zboot_decompress(char *ringbuf, const char *pe_buf,
+			       __u32 pe_sz,
+			       kexec_sig_enforced sig_mode,
+			       struct bpf_parser_context *bpf)
+{
+	struct linux_pe_zboot_header zboot_header;
+	unsigned int payload_offset, payload_size, max_payload;
+	int total, ret;
+
+	if (pe_sz > MAX_RECORD_SIZE) {
+		bpf_printk("do_zboot_decompress: PE image too large\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Verify PE signature before any further processing if
+	 * signature enforcement is requested.
+	 */
+	if (sig_mode != SIG_ENFORCE_NONE) {
+		total = fill_cmd(ringbuf,
+				 KEXEC_BPF_CMD_VERIFY_SIG,
+				 0,
+				 pe_buf,
+				 pe_sz);
+		ret = bpf_buffer_parser(ringbuf, total, bpf);
+		if (ret < 0) {
+			bpf_printk("do_zboot_decompress: VERIFY_SIG failed: %d\n",
+				   ret);
+			return ret;
+		}
+	}
+
+	/* Read and validate zboot header */
+	if (bpf_probe_read_kernel(&zboot_header, sizeof(zboot_header),
+				  pe_buf) < 0) {
+		bpf_printk("do_zboot_decompress: failed to read zboot header\n");
+		return -EIO;
+	}
+
+	if (__builtin_memcmp(&zboot_header.image_type, "zimg",
+			     sizeof(zboot_header.image_type))) {
+		bpf_printk("do_zboot_decompress: not a zboot image\n");
+		return -EINVAL;
+	}
+
+	payload_offset = zboot_header.payload_offset;
+	payload_size   = zboot_header.payload_size;
+	bpf_printk("do_zboot_decompress: payload offset=0x%x size=0x%x\n",
+		   payload_offset, payload_size);
+
+	if (payload_size < 4) {
+		bpf_printk("do_zboot_decompress: zboot payload too small\n");
+		return -EINVAL;
+	}
+	if (payload_offset > pe_sz ||
+	    payload_size   > pe_sz ||
+	    payload_offset > pe_sz - payload_size) {
+		bpf_printk("do_zboot_decompress: zboot payload out of bounds\n");
+		return -EINVAL;
+	}
+
+	max_payload = MAX_RECORD_SIZE - sizeof(struct cmd_hdr);
+	if (payload_size - 4 >= max_payload) {
+		bpf_printk("do_zboot_decompress: zboot payload exceeds MAX_RECORD_SIZE\n");
+		return -EINVAL;
+	}
+
+	/* 4 bytes original size is appended after vmlinuz.bin, strip them */
+	total = fill_cmd(ringbuf,
+			 KEXEC_BPF_CMD_DECOMPRESS,
+			 KEXEC_BPF_SUBCMD_KERNEL,
+			 pe_buf + payload_offset,
+			 payload_size - 4);
+
+	bpf_printk("do_zboot_decompress: calling bpf_buffer_parser() for DECOMPRESS\n");
+	ret = bpf_buffer_parser(ringbuf, total, bpf);
+	if (ret < 0) {
+		bpf_printk("do_zboot_decompress: decompression failed: %d\n",
+			   ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+SEC("fentry.s/kexec_image_parser_anchor")
+int BPF_PROG(parse_zboot, struct kexec_context *context, unsigned long parser_id)
+{
+	kexec_sig_enforced sig_mode;
+	struct bpf_parser_context *bpf = NULL;
+	Elf64_Ehdr ehdr;
+	Elf64_Shdr shstr_shdr;
+	__u64 shstrtab_off, shstrtab_sz;
+	unsigned long buf_sz;
+	char *buf_elf;
+	char *ringbuf;
+	__u8 magic[4];
+	int total, ret, i;
+
+	buf_elf      = BPF_CORE_READ(context, parsing_buf[0]);
+	buf_sz       = BPF_CORE_READ(context, parsing_buf_sz[0]);
+	sig_mode = BPF_CORE_READ(context, sig_mode);
+
+	if (!buf_elf || buf_sz < 4) {
+		bpf_printk("parse_zboot: invalid parsing_buf[0]\n");
+		return 0;
+	}
+
+	if (bpf_probe_read_kernel(magic, sizeof(magic), buf_elf) < 0) {
+		bpf_printk("parse_zboot: failed to read magic\n");
+		return 0;
+	}
+
+	ringbuf = (char *)bpf_ringbuf_reserve(&ringbuf_1, MAX_RECORD_SIZE, 0);
+	if (!ringbuf) {
+		bpf_printk("parse_zboot: failed to reserve ringbuf\n");
+		return 0;
+	}
+
+	bpf = bpf_get_parser_context(parser_id);
+	if (!bpf) {
+		bpf_printk("parse_zboot: no parser context\n");
+		goto discard;
+	}
+
+	/*
+	 * Plain PE (zboot) path: parsing_buf[0] is a PE image directly.
+	 * Mirrors the original parse_zboot behaviour.
+	 */
+	if (magic[0] == 'M' && magic[1] == 'Z') {
+		ret = do_zboot_decompress(ringbuf, buf_elf, (__u32)buf_sz,
+					  sig_mode, bpf);
+		if (ret < 0)
+			goto discard;
+
+		goto done;
+	}
+
+	/*
+	 * ELF container path: parsing_buf[0] is an ELF with .kernel,
+	 * .initrd, .cmdline sections.  .kernel contains a PE zboot image.
+	 */
+	if (magic[0] != 0x7f || magic[1] != 'E' ||
+	    magic[2] != 'L'  || magic[3] != 'F') {
+		bpf_printk("parse_zboot: unrecognized format\n");
+		goto discard;
+	}
+
+	if (buf_sz < sizeof(Elf64_Ehdr)) {
+		bpf_printk("parse_zboot: ELF too small\n");
+		goto discard;
+	}
+
+	if (bpf_probe_read_kernel(&ehdr, sizeof(ehdr), buf_elf) < 0) {
+		bpf_printk("parse_zboot: failed to read ELF header\n");
+		goto discard;
+	}
+	if (ehdr.e_shoff == 0 || ehdr.e_shnum == 0 ||
+	    ehdr.e_shstrndx == SHN_UNDEF) {
+		bpf_printk("parse_zboot: invalid ELF section info\n");
+		goto discard;
+	}
+
+	if (bpf_probe_read_kernel(&shstr_shdr, sizeof(shstr_shdr),
+				  buf_elf + ehdr.e_shoff +
+				  ehdr.e_shstrndx * sizeof(Elf64_Shdr)) < 0) {
+		bpf_printk("parse_zboot: failed to read shstrtab shdr\n");
+		goto discard;
+	}
+	shstrtab_off = shstr_shdr.sh_offset;
+	shstrtab_sz  = shstr_shdr.sh_size;
+
+	for (i = 1; i < ELF_SCAN_MAX; i++) {
+		Elf64_Shdr shdr;
+		char sec_name[16];
+		__u64 name_off;
+
+		if (i >= ehdr.e_shnum)
+			break;
+
+		if (bpf_probe_read_kernel(&shdr, sizeof(shdr),
+					  buf_elf + ehdr.e_shoff +
+					  i * sizeof(Elf64_Shdr)) < 0)
+			continue;
+
+		name_off = shstrtab_off + shdr.sh_name;
+		if (name_off + sizeof(sec_name) > shstrtab_off + shstrtab_sz)
+			continue;
+		if (bpf_probe_read_kernel(sec_name, sizeof(sec_name),
+					  buf_elf + name_off) < 0)
+			continue;
+
+		if (!shdr.sh_size || shdr.sh_offset + shdr.sh_size > buf_sz)
+			continue;
+
+		/* .initrd */
+		if (__builtin_memcmp(sec_name, initrd_sect_name, sizeof(initrd_sect_name)) == 0) {
+			total = fill_cmd(ringbuf,
+					 KEXEC_BPF_CMD_COPY,
+					 KEXEC_BPF_SUBCMD_INITRD,
+					 buf_elf + shdr.sh_offset,
+					 (__u32)shdr.sh_size);
+			ret = bpf_buffer_parser(ringbuf, total, bpf);
+			if (ret < 0) {
+				bpf_printk("parse_zboot: COPY initrd failed: %d\n",
+					   ret);
+				goto discard;
+			}
+			continue;
+		}
+
+		/* .cmdline */
+		if (__builtin_memcmp(sec_name, cmdline_sect_name, sizeof(cmdline_sect_name)) == 0) {
+			total = fill_cmd(ringbuf,
+					 KEXEC_BPF_CMD_COPY,
+					 KEXEC_BPF_SUBCMD_CMDLINE,
+					 buf_elf + shdr.sh_offset,
+					 (__u32)shdr.sh_size);
+			ret = bpf_buffer_parser(ringbuf, total, bpf);
+			if (ret < 0) {
+				bpf_printk("parse_zboot: COPY cmdline failed: %d\n",
+					   ret);
+				goto discard;
+			}
+			continue;
+		}
+
+		/* .kernel: vmlinuz.efi PE zboot image */
+		if (__builtin_memcmp(sec_name, linux_sect_name, sizeof(linux_sect_name)) != 0)
+			continue;
+
+		ret = do_zboot_decompress(ringbuf,
+					  buf_elf + shdr.sh_offset,
+					  (__u32)shdr.sh_size,
+					  sig_mode, bpf);
+		if (ret < 0)
+			goto discard;
+	}
+
+done:
+	/* Notify kernel that this BPF prog completed successfully */
+	total = fill_cmd(ringbuf, KEXEC_BPF_CMD_DONE, 0, NULL, 0);
+	ret = bpf_buffer_parser(ringbuf, total, bpf);
+	if (ret < 0) {
+		bpf_printk("parse_zboot: KEXEC_BPF_CMD_DONE, failed: %d\n", ret);
+		goto discard;
+	}
+
+discard:
+	bpf_ringbuf_discard(ringbuf, BPF_RB_NO_WAKEUP);
+	if (bpf)
+		bpf_put_parser_context(bpf);
+	return 0;
+}
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCHv7 12/13] tools/kexec: Introduce a bpf-prog to handle UKI image
  2026-03-22  1:43 [PATCHv7 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
                   ` (10 preceding siblings ...)
  2026-03-22  1:44 ` [PATCHv7 11/13] tools/kexec: Introduce a bpf-prog to handle zboot image Pingfan Liu
@ 2026-03-22  1:44 ` Pingfan Liu
  2026-03-22  1:44 ` [PATCHv7 13/13] tools/kexec: Introduce a tool to build zboot envelop Pingfan Liu
  2026-03-23 19:02 ` [PATCHv7 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Andrew Morton
  13 siblings, 0 replies; 26+ messages in thread
From: Pingfan Liu @ 2026-03-22  1:44 UTC (permalink / raw)
  To: kexec
  Cc: Pingfan Liu, David S. Miller, Alexei Starovoitov, Daniel Borkmann,
	John Fastabend, Andrii Nakryiko, Martin KaFai Lau,
	Eduard Zingerman, Song Liu, Yonghong Song, Jeremy Linton,
	Catalin Marinas, Will Deacon, Ard Biesheuvel, Simon Horman,
	Gerd Hoffmann, Vitaly Kuznetsov, Philipp Rudo, Viktor Malik,
	Jan Hendrik Farr, Baoquan He, Dave Young, Andrew Morton, bpf,
	systemd-devel, linux-kernel

Signed-off-by: Pingfan Liu <piliu@redhat.com>
Cc: Baoquan He <bhe@redhat.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Philipp Rudo <prudo@redhat.com>
Cc: bpf@vger.kernel.org
To: kexec@lists.infradead.org
---
 tools/kexec/Makefile         |  18 ++-
 tools/kexec/uki_parser_bpf.c | 235 +++++++++++++++++++++++++++++++++++
 2 files changed, 252 insertions(+), 1 deletion(-)
 create mode 100644 tools/kexec/uki_parser_bpf.c

diff --git a/tools/kexec/Makefile b/tools/kexec/Makefile
index a404a1453c888..c0e2ad44658e3 100644
--- a/tools/kexec/Makefile
+++ b/tools/kexec/Makefile
@@ -43,6 +43,7 @@ HEADERS = vmlinux.h bpf_helper_defs.h image_size.h
 #   <P>.bpf                - final ELF wrapper with .bpf.1 section
 # ---------------------------------------------------------------------------
 BPF_TARGETS = zboot
+BPF_TARGETS += uki
 
 define BPF_ARTIFACTS
 $(1)_parser_bpf.o $(1)_parser_bpf.lskel.h $(1)_bytecode.c $(1)_bytecode.o $(1).bpf
@@ -54,8 +55,13 @@ ALL_BPF_ARTIFACTS = $(foreach t,$(BPF_TARGETS),$(call BPF_ARTIFACTS,$(t)))
 # Top-level phony targets
 # ---------------------------------------------------------------------------
 zboot: $(HEADERS) $(call BPF_ARTIFACTS,zboot) build_zboot_image
+ifeq ($(ARCH),$(filter $(ARCH),arm64 riscv loongarch))
+uki: $(HEADERS) zboot.bpf $(call BPF_ARTIFACTS,uki)
+else
+uki: $(HEADERS) $(call BPF_ARTIFACTS,uki)
+endif
 
-.PHONY: zboot clean
+.PHONY: zboot uki clean
 
 # ---------------------------------------------------------------------------
 # Shared header rules
@@ -123,6 +129,15 @@ image_size.h: $(KERNEL_IMAGE)
 # ---------------------------------------------------------------------------
 ZBOOT_BPF_MAPS := .bpf.1:zboot_bytecode.o
 
+# uki.bpf sections depend on architecture:
+#   arm64/riscv/loongarch: .bpf.1 (uki bytecode) + .bpf.nested (zboot.bpf ELF)
+#   x86: .bpf.1 only.  zboot format does not exist on x86
+ifeq ($(ARCH),$(filter $(ARCH),arm64 riscv loongarch))
+UKI_BPF_MAPS   := .bpf.1:uki_bytecode.o .bpf.nested:zboot.bpf
+else
+UKI_BPF_MAPS   := .bpf.1:uki_bytecode.o
+endif
+
 # ---------------------------------------------------------------------------
 # Helpers to build objcopy flags from a BPF_MAPS list
 # ---------------------------------------------------------------------------
@@ -154,6 +169,7 @@ $(1).bpf: $(foreach m,$($(2)_BPF_MAPS),$(call source_file,$(m)))
 endef
 
 $(eval $(call BPF_WRAPPER_RULE,zboot,ZBOOT))
+$(eval $(call BPF_WRAPPER_RULE,uki,UKI))
 
 # ---------------------------------------------------------------------------
 # Clean
diff --git a/tools/kexec/uki_parser_bpf.c b/tools/kexec/uki_parser_bpf.c
new file mode 100644
index 0000000000000..1eb542d8acd4c
--- /dev/null
+++ b/tools/kexec/uki_parser_bpf.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2025, 2026 Red Hat, Inc
+//
+#include "vmlinux.h"
+#include <bpf_helpers.h>
+#include <bpf_tracing.h>
+#include "image_size.h"
+
+/* ringbuf 2,3,4 are useless */
+#define MIN_BUF_SIZE 1
+#define MAX_RECORD_SIZE (IMAGE_SIZE + 40960)
+#define RINGBUF1_SIZE IMAGE_SIZE_POWER2_ALIGN
+#define RINGBUF2_SIZE MIN_BUF_SIZE
+#define RINGBUF3_SIZE MIN_BUF_SIZE
+#define RINGBUF4_SIZE MIN_BUF_SIZE
+
+#include "template.c"
+
+#define MAX_PARSING_BUFS 16
+#define PE_SCAN_MAX      16
+#define ELF_SCAN_MAX     16
+
+/* SHN_UNDEF is a uapi macro not exported via BTF/vmlinux.h */
+#ifndef SHN_UNDEF
+#define SHN_UNDEF 0
+#endif
+
+#ifndef EIO
+#define EIO 5
+#endif
+#ifndef EINVAL
+#define EINVAL 22
+#endif
+
+static const char linux_sect_name[]   = ".linux";
+static const char initrd_sect_name[]   = ".initrd";
+static const char cmdline_sect_name[]   = ".cmdline";
+
+
+#define MAKE_CMD(cmd, subcmd)  ((__u32)(cmd) | ((__u32)(subcmd) << 16))
+
+static int fill_cmd(char *buf, __u32 cmd_word, __u32 pipeline_flag,
+			const char *src, __u32 data_len)
+{
+	struct cmd_hdr *hdr;
+	char *payload;
+
+	__u16 cmd = (__u16)(cmd_word & 0xffff);
+	__u16 subcmd = (__u16)(cmd_word >> 16);
+
+	hdr = (struct cmd_hdr *)buf;
+	hdr->cmd = cmd;
+	hdr->subcmd = subcmd;
+	hdr->pipeline_flag = pipeline_flag;
+	hdr->payload_len = data_len;
+	hdr->num_chunks = 0;
+
+	payload = (char *)(hdr + 1);
+	/* Only cmd, no payload */
+	if (!src || !data_len)
+		return sizeof(*hdr);
+	if (data_len > MAX_RECORD_SIZE - sizeof(struct cmd_hdr))
+		return -EINVAL;
+	bpf_probe_read_kernel(payload, data_len, src);
+
+	return sizeof(*hdr) + data_len;
+}
+
+static int process_uki_pe(const char *pe_buf, __u32 pe_sz, char *scratch,
+			  struct bpf_parser_context *bpf_ctx)
+{
+	__u32 pe_offset, pe_sig, section_table_off;
+	__u16 dos_magic, num_sections, opt_hdr_sz;
+	__u16 pipeline_flag = 0;
+	int i, ret;
+
+	if (pe_sz < 64)
+		return -EINVAL;
+	if (pe_sz > MAX_RECORD_SIZE)
+		return -EINVAL;
+
+	if (bpf_probe_read_kernel(&dos_magic, sizeof(dos_magic), pe_buf) < 0)
+		return -EIO;
+	if (dos_magic != 0x5A4D)
+		return -EINVAL;
+
+	if (bpf_probe_read_kernel(&pe_offset, sizeof(pe_offset),
+				  pe_buf + 0x3c) < 0)
+		return -EIO;
+	if (pe_offset + 24 > pe_sz)
+		return -EINVAL;
+
+	if (bpf_probe_read_kernel(&pe_sig, sizeof(pe_sig),
+				  pe_buf + pe_offset) < 0)
+		return -EIO;
+	if (pe_sig != 0x00004550)
+		return -EINVAL;
+
+	if (bpf_probe_read_kernel(&num_sections, sizeof(num_sections),
+				  pe_buf + pe_offset + 6) < 0)
+		return -EIO;
+	if (bpf_probe_read_kernel(&opt_hdr_sz, sizeof(opt_hdr_sz),
+				  pe_buf + pe_offset + 20) < 0)
+		return -EIO;
+
+	section_table_off = pe_offset + 4 + 20 + opt_hdr_sz;
+	if (section_table_off >= pe_sz)
+		return -EINVAL;
+
+	for (i = 0; i < PE_SCAN_MAX; i++) {
+		__u32 raw_size, raw_off, shdr_off;
+		char sec_name[8];
+		__u16 subcmd;
+
+		if (i >= num_sections)
+			break;
+
+		shdr_off = section_table_off + i * 40;
+		if (shdr_off + 40 > pe_sz)
+			break;
+
+		if (bpf_probe_read_kernel(sec_name, sizeof(sec_name),
+					  pe_buf + shdr_off) < 0)
+			continue;
+
+		pipeline_flag = 0;
+		if (__builtin_memcmp(sec_name, linux_sect_name, sizeof(linux_sect_name)) == 0) {
+			subcmd = KEXEC_BPF_SUBCMD_KERNEL;
+			/*
+			 * .linux section may contain different format kernel, which should be
+			 * passed to the next stage to handle
+			 */
+			pipeline_flag = KEXEC_BPF_PIPELINE_FILL;
+		}
+		else if (__builtin_memcmp(sec_name, initrd_sect_name, sizeof(initrd_sect_name)) == 0)
+			subcmd = KEXEC_BPF_SUBCMD_INITRD;
+		else if (__builtin_memcmp(sec_name, cmdline_sect_name, sizeof(cmdline_sect_name)) == 0)
+			subcmd = KEXEC_BPF_SUBCMD_CMDLINE;
+		else
+			continue;
+
+		if (bpf_probe_read_kernel(&raw_size, sizeof(raw_size),
+					  pe_buf + shdr_off + 16) < 0)
+			continue;
+		if (bpf_probe_read_kernel(&raw_off, sizeof(raw_off),
+					  pe_buf + shdr_off + 20) < 0)
+			continue;
+
+		if (!raw_size || raw_off + raw_size > pe_sz)
+			continue;
+
+		ret = fill_cmd(scratch,
+				MAKE_CMD(KEXEC_BPF_CMD_COPY, subcmd),
+				pipeline_flag,
+				pe_buf + raw_off,
+				raw_size);
+		ret = bpf_buffer_parser(scratch, ret, bpf_ctx);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+SEC("fentry.s/kexec_image_parser_anchor")
+int BPF_PROG(parse_uki, struct kexec_context *context, unsigned long parser_id)
+{
+	struct bpf_parser_context *bpf_ctx;
+	char *buf0, *buf1, *scratch;
+	__u8 magic[4];
+	int ret;
+
+	bpf_printk("parse_uki: start\n");
+	buf0 = BPF_CORE_READ(context, parsing_buf[0]);
+	if (!buf0)
+		return 0;
+
+	bpf_ctx = bpf_get_parser_context(parser_id);
+	if (!bpf_ctx) {
+		bpf_printk("parse_uki: no parser context for id %lu\n",
+			   parser_id);
+		return 0;
+	}
+
+	buf1 = BPF_CORE_READ(context, parsing_buf[1]);
+
+	/*
+	 * Single-buffer path: original parse_uki behaviour.
+	 * parsing_buf[0] is either a plain PE UKI or an ELF container
+	 * with embedded .uki / .addon sections.
+	 */
+	if (!buf1) {
+		unsigned long sz = BPF_CORE_READ(context, parsing_buf_sz[0]);
+
+		if (sz < 4)
+			goto out;
+
+		if (bpf_probe_read_kernel(magic, sizeof(magic), buf0) < 0)
+			goto out;
+
+		scratch = bpf_ringbuf_reserve(&ringbuf_1, MAX_RECORD_SIZE, 0);
+		if (!scratch) {
+			bpf_printk("ringbuf reserve failed\n");
+			goto out;
+		}
+
+		if (magic[0] == 'M' && magic[1] == 'Z') {
+			bpf_printk("call process_uki_pe\n");
+			ret = process_uki_pe(buf0, (__u32)sz, scratch, bpf_ctx);
+			if (ret) {
+				bpf_printk("parse_uki: PE path failed: %d\n",
+					   ret);
+			}
+			else {
+				bpf_printk("fill KEXEC_BPF_CMD_DONE \n");
+				ret = fill_cmd(scratch, MAKE_CMD(KEXEC_BPF_CMD_DONE, 0),
+						0, NULL, 0);
+				ret = bpf_buffer_parser(scratch, ret, bpf_ctx);
+				if (ret)
+					bpf_printk("parse_uki: inject KEXEC_BPF_CMD_DONE failed: %d\n",
+					   ret);
+			}
+		} else {
+			bpf_printk("parse_uki: unrecognized format\n");
+		}
+
+		bpf_ringbuf_discard(scratch, BPF_RB_NO_WAKEUP);
+		goto out;
+	}
+
+out:
+	bpf_put_parser_context(bpf_ctx);
+	return 0;
+}
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCHv7 13/13] tools/kexec: Introduce a tool to build zboot envelop
  2026-03-22  1:43 [PATCHv7 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
                   ` (11 preceding siblings ...)
  2026-03-22  1:44 ` [PATCHv7 12/13] tools/kexec: Introduce a bpf-prog to handle UKI image Pingfan Liu
@ 2026-03-22  1:44 ` Pingfan Liu
  2026-03-23 19:02 ` [PATCHv7 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Andrew Morton
  13 siblings, 0 replies; 26+ messages in thread
From: Pingfan Liu @ 2026-03-22  1:44 UTC (permalink / raw)
  To: kexec
  Cc: Pingfan Liu, David S. Miller, Alexei Starovoitov, Daniel Borkmann,
	John Fastabend, Andrii Nakryiko, Martin KaFai Lau,
	Eduard Zingerman, Song Liu, Yonghong Song, Jeremy Linton,
	Catalin Marinas, Will Deacon, Ard Biesheuvel, Simon Horman,
	Gerd Hoffmann, Vitaly Kuznetsov, Philipp Rudo, Viktor Malik,
	Jan Hendrik Farr, Baoquan He, Dave Young, Andrew Morton, bpf,
	systemd-devel, linux-kernel

The new tool builds a ELF container around zboot image. It contains
three key sections: .kernel, .initrd, .cmdline. Later zboot_bpf_parser
will parse this container format.

Signed-off-by: Pingfan Liu <piliu@redhat.com>
Cc: Baoquan He <bhe@redhat.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Philipp Rudo <prudo@redhat.com>
Cc: bpf@vger.kernel.org
To: kexec@lists.infradead.org
---
 tools/kexec/Makefile              |  31 ++-
 tools/kexec/build_zboot_envelop.c | 362 ++++++++++++++++++++++++++++++
 tools/kexec/zboot_envelop.h       |   7 +
 tools/kexec/zboot_parser_bpf.c    |   7 +-
 4 files changed, 401 insertions(+), 6 deletions(-)
 create mode 100644 tools/kexec/build_zboot_envelop.c
 create mode 100644 tools/kexec/zboot_envelop.h

diff --git a/tools/kexec/Makefile b/tools/kexec/Makefile
index c0e2ad44658e3..c168a6eeea09d 100644
--- a/tools/kexec/Makefile
+++ b/tools/kexec/Makefile
@@ -21,9 +21,21 @@ endif
 
 CC = clang
 CFLAGS = -O2
-BPF_PROG_CFLAGS = -g -fno-merge-all-constants -O2 -target bpf -Wall -I $(BPFDIR) -I .
+BPF_PROG_CFLAGS = -g -O2 -target bpf -Wall -I $(BPFDIR) -I .
 BPFTOOL = bpftool
 
+# Host compiler for native tools (must not be the BPF clang cross-compiler)
+HOSTCC ?= gcc
+HOSTCFLAGS = -std=c11 -O2 -Wall -Wextra
+
+# ---------------------------------------------------------------------------
+# zboot ELF image parameters
+# INITRAMFS: path to the initramfs image (required)
+# CMDLINE:   kernel command line string (optional, defaults to empty)
+# ---------------------------------------------------------------------------
+INITRAMFS ?=
+CMDLINE   ?=
+
 # ---------------------------------------------------------------------------
 # Shared generated headers (common to all targets)
 # ---------------------------------------------------------------------------
@@ -54,7 +66,7 @@ ALL_BPF_ARTIFACTS = $(foreach t,$(BPF_TARGETS),$(call BPF_ARTIFACTS,$(t)))
 # ---------------------------------------------------------------------------
 # Top-level phony targets
 # ---------------------------------------------------------------------------
-zboot: $(HEADERS) $(call BPF_ARTIFACTS,zboot) build_zboot_image
+zboot: $(HEADERS) $(call BPF_ARTIFACTS,zboot) build_zboot_envelop
 ifeq ($(ARCH),$(filter $(ARCH),arm64 riscv loongarch))
 uki: $(HEADERS) zboot.bpf $(call BPF_ARTIFACTS,uki)
 else
@@ -171,8 +183,21 @@ endef
 $(eval $(call BPF_WRAPPER_RULE,zboot,ZBOOT))
 $(eval $(call BPF_WRAPPER_RULE,uki,UKI))
 
+# ---------------------------------------------------------------------------
+# Host tool: build_zboot_envelop
+# Packs EFI_IMAGE (.kernel), INITRAMFS (.initrd) and CMDLINE (.cmdline)
+# into a single ELF file consumed by the kexec loader.
+# ---------------------------------------------------------------------------
+build_zboot_envelop: build_zboot_envelop.c
+	@$(HOSTCC) $(HOSTCFLAGS) -o $@ $<
+
+zboot_image.elf: build_zboot_envelop $(EFI_IMAGE) $(INITRAMFS)
+	$(if $(INITRAMFS),,$(error INITRAMFS is not set. Usage: make INITRAMFS=<path> [CMDLINE="..."]))
+	@./build_zboot_envelop $(EFI_IMAGE) $(INITRAMFS) "$(CMDLINE)"
+
 # ---------------------------------------------------------------------------
 # Clean
 # ---------------------------------------------------------------------------
 clean:
-	@rm -f $(HEADERS) $(ALL_BPF_ARTIFACTS) *.base.o
+	@rm -f $(HEADERS) $(ALL_BPF_ARTIFACTS) *.base.o \
+		build_zboot_envelop zboot_image.elf
diff --git a/tools/kexec/build_zboot_envelop.c b/tools/kexec/build_zboot_envelop.c
new file mode 100644
index 0000000000000..d2d9ffc11fdc1
--- /dev/null
+++ b/tools/kexec/build_zboot_envelop.c
@@ -0,0 +1,362 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * build_zboot_envelop.c - Pack zboot image, initramfs and cmdline into an ELF file.
+ *
+ * Usage: build_zboot_envelop <zboot_image> <initramfs> <cmdline> [output.elf]
+ *
+ * Output ELF sections:
+ *   .kernel     - zboot image (PE signature preserved, no padding)
+ *   .initrd     - initramfs image
+ *   .cmdline    - kernel command line string (NUL-terminated)
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "zboot_envelop.h"
+
+#define DEFAULT_OUTPUT "zboot_image.elf"
+
+/*
+ * Section indices into the section header table.
+ */
+enum {
+	SHN_UNDEF_IDX = 0,
+	SHN_KERNEL_IDX = 1,
+	SHN_INITRD_IDX = 2,
+	SHN_CMDLINE_IDX = 3,
+	SHN_SHSTRTAB_IDX = 4,
+	SHN_NUM = 5,
+};
+
+/*
+ * String table layout (offsets are fixed at compile time):
+ *
+ *   off 0  : '\0'           (mandatory empty string)
+ *   off 1  : ".kernel\0"
+ *   off 9  : ".initrd\0"
+ *   off 17 : ".cmdline\0"
+ *   off 26 : ".shstrtab\0"
+ */
+#define SHSTR_OFF_KERNEL 1
+#define SHSTR_OFF_INITRD 9
+#define SHSTR_OFF_CMDLINE 17
+#define SHSTR_OFF_SHSTRTAB 26
+
+#define SHSTRTAB_CONTENT \
+    "\0" KERNEL_SECT_NAME \
+    "\0" INITRD_SECT_NAME \
+    "\0" CMDLINE_SECT_NAME \
+    "\0.shstrtab"
+
+/* sizeof() includes the final NUL from the string literal. */
+#define SHSTRTAB_SIZE (sizeof(SHSTRTAB_CONTENT))
+
+/*
+ * struct input_file - holds a memory-mapped input file together with its size.
+ * @data:	pointer to the mapped region (or NULL if not mmap'd)
+ * @size:	exact byte size of the file
+ * @fd:		open file descriptor (-1 when closed)
+ */
+struct input_file {
+	const void *data;
+	size_t size;
+	int fd;
+};
+
+/*
+ * align8 - round @off up to the next multiple of 8.
+ */
+static inline size_t align8(size_t off)
+{
+	return (off + 7) & ~(size_t)7;
+}
+
+/*
+ * open_and_map - open @path read-only and mmap its entire content.
+ *
+ * Returns 0 on success, -1 on error (errno is set by the failing syscall and
+ * a diagnostic is printed to stderr).
+ */
+static int open_and_map(const char *path, struct input_file *f)
+{
+	struct stat st;
+
+	f->fd = open(path, O_RDONLY);
+	if (f->fd < 0) {
+		fprintf(stderr, "build_zboot_envelop: cannot open '%s': %s\n", path,
+			strerror(errno));
+		return -1;
+	}
+
+	if (fstat(f->fd, &st) < 0) {
+		fprintf(stderr, "build_zboot_envelop: cannot stat '%s': %s\n", path,
+			strerror(errno));
+		goto err_close;
+	}
+
+	f->size = (size_t)st.st_size;
+	f->data = mmap(NULL, f->size, PROT_READ, MAP_PRIVATE, f->fd, 0);
+	if (f->data == MAP_FAILED) {
+		fprintf(stderr, "build_zboot_envelop: cannot mmap '%s': %s\n", path,
+			strerror(errno));
+		goto err_close;
+	}
+
+	return 0;
+
+err_close:
+	close(f->fd);
+	f->fd = -1;
+	return -1;
+}
+
+static void close_and_unmap(struct input_file *f)
+{
+	if (f->data && f->data != MAP_FAILED)
+		munmap((void *)f->data, f->size);
+	if (f->fd >= 0)
+		close(f->fd);
+}
+
+static int write_all(int fd, const void *buf, size_t len)
+{
+	const uint8_t *p = buf;
+	ssize_t n;
+
+	while (len > 0) {
+		n = write(fd, p, len);
+		if (n < 0) {
+			if (errno == EINTR)
+				continue;
+			return -1;
+		}
+		p += n;
+		len -= n;
+	}
+	return 0;
+}
+
+/*
+ * write_padding - write @len zero bytes to @fd.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+static int write_padding(int fd, size_t len)
+{
+	static const uint8_t zero[8];
+	size_t chunk;
+	ssize_t n;
+
+	while (len > 0) {
+		chunk = (len < sizeof(zero)) ? len : sizeof(zero);
+		n = write(fd, zero, chunk);
+		if (n < 0) {
+			if (errno == EINTR)
+				continue;
+			return -1;
+		}
+		len -= n;
+	}
+	return 0;
+}
+
+/*
+ * fill_shdr - populate a Elf64_Shdr for a SHT_PROGBITS section.
+ *
+ * @shdr:	section header to fill
+ * @name_off:	offset of the section name inside .shstrtab
+ * @offset:	file offset at which the section data begins
+ * @size:	exact byte count of the section data (no padding included)
+ * @flags:	section flags (e.g. SHF_ALLOC)
+ */
+static void fill_shdr(Elf64_Shdr *shdr, uint32_t name_off, uint64_t offset,
+		      uint64_t size, uint64_t flags)
+{
+	memset(shdr, 0, sizeof(*shdr));
+	shdr->sh_name = name_off;
+	shdr->sh_type = SHT_PROGBITS;
+	shdr->sh_flags = flags;
+	shdr->sh_offset = offset;
+	shdr->sh_size = size;
+	shdr->sh_addralign = 1;
+}
+
+int main(int argc, char *argv[])
+{
+	const char *kernel_path, *initrd_path, *cmdline, *output_path;
+	struct input_file kernel = { NULL, 0, -1 };
+	struct input_file initrd = { NULL, 0, -1 };
+	Elf64_Ehdr ehdr;
+	Elf64_Shdr shdrs[SHN_NUM];
+	size_t cmdline_size; /* includes terminating NUL */
+
+	/*
+	 * File layout (all section-data offsets are 8-byte aligned except
+	 * .kernel which must start directly after the headers to guarantee
+	 * that no byte is inserted before the PE signature):
+	 *
+	 *   [ELF header]          64 B
+	 *   [Section headers]     SHN_NUM * sizeof(Elf64_Shdr)
+	 *   [.kernel data]        kernel.size bytes  (NO internal padding)
+	 *   <pad to 8B>
+	 *   [.initrd data]        initrd.size bytes
+	 *   <pad to 8B>
+	 *   [.cmdline data]       cmdline_size bytes
+	 *   <pad to 8B>
+	 *   [.shstrtab data]      SHSTRTAB_SIZE bytes
+	 */
+	size_t off_shdrs, off_kernel, off_initrd, off_cmdline, off_shstrtab;
+	size_t pad_after_kernel, pad_after_initrd, pad_after_cmdline;
+	int outfd;
+	int ret = EXIT_FAILURE;
+
+	if (argc < 4 || argc > 5) {
+		fprintf(stderr,
+			"Usage: %s <zboot_image> <initramfs> <cmdline> "
+			"[output.elf]\n",
+			argv[0]);
+		return EXIT_FAILURE;
+	}
+
+	kernel_path = argv[1];
+	initrd_path = argv[2];
+	cmdline = argv[3];
+	output_path = (argc == 5) ? argv[4] : DEFAULT_OUTPUT;
+	cmdline_size = strlen(cmdline) + 1; /* +1 for NUL terminator */
+
+	/* ------------------------------------------------------------------ */
+	/* 1. Map input files                                                  */
+	/* ------------------------------------------------------------------ */
+	if (open_and_map(kernel_path, &kernel) < 0)
+		goto out;
+
+	if (open_and_map(initrd_path, &initrd) < 0)
+		goto out;
+
+	/* Compute file layout */
+	off_shdrs = sizeof(Elf64_Ehdr);
+	off_kernel = off_shdrs + SHN_NUM * sizeof(Elf64_Shdr);
+
+	/*
+	 * .kernel must not contain any padding - its sh_size equals the
+	 * exact file size so that any PE authenticode signature is intact.
+	 * Alignment padding goes *after* the raw bytes, between sections.
+	 */
+	pad_after_kernel = 0;
+	off_initrd = off_kernel + kernel.size + pad_after_kernel;
+
+	pad_after_initrd =
+		align8(off_initrd + initrd.size) - (off_initrd + initrd.size);
+	off_cmdline = off_initrd + initrd.size + pad_after_initrd;
+
+	pad_after_cmdline = align8(off_cmdline + cmdline_size) -
+			    (off_cmdline + cmdline_size);
+	off_shstrtab = off_cmdline + cmdline_size + pad_after_cmdline;
+
+	memset(&ehdr, 0, sizeof(ehdr));
+
+	ehdr.e_ident[EI_MAG0] = ELFMAG0;
+	ehdr.e_ident[EI_MAG1] = ELFMAG1;
+	ehdr.e_ident[EI_MAG2] = ELFMAG2;
+	ehdr.e_ident[EI_MAG3] = ELFMAG3;
+	ehdr.e_ident[EI_CLASS] = ELFCLASS64;
+	ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
+	ehdr.e_ident[EI_VERSION] = EV_CURRENT;
+	ehdr.e_ident[EI_OSABI] = ELFOSABI_NONE;
+
+	ehdr.e_type = ET_EXEC;
+	ehdr.e_machine = EM_AARCH64;
+	ehdr.e_version = EV_CURRENT;
+	ehdr.e_ehsize = sizeof(Elf64_Ehdr);
+	ehdr.e_shentsize = sizeof(Elf64_Shdr);
+	ehdr.e_shnum = SHN_NUM;
+	ehdr.e_shoff = (Elf64_Off)off_shdrs;
+	ehdr.e_shstrndx = SHN_SHSTRTAB_IDX;
+
+	/* Build section headers */
+	memset(shdrs, 0, sizeof(shdrs));
+
+	/* [0] SHN_UNDEF - mandatory null entry */
+
+	/* [1] .kernel */
+	fill_shdr(&shdrs[SHN_KERNEL_IDX], SHSTR_OFF_KERNEL,
+		  (uint64_t)off_kernel, (uint64_t)kernel.size, SHF_ALLOC);
+
+	/* [2] .initrd */
+	fill_shdr(&shdrs[SHN_INITRD_IDX], SHSTR_OFF_INITRD,
+		  (uint64_t)off_initrd, (uint64_t)initrd.size, SHF_ALLOC);
+
+	/* [3] .cmdline */
+	fill_shdr(&shdrs[SHN_CMDLINE_IDX], SHSTR_OFF_CMDLINE,
+		  (uint64_t)off_cmdline, (uint64_t)cmdline_size, SHF_ALLOC);
+
+	/* [4] .shstrtab */
+	memset(&shdrs[SHN_SHSTRTAB_IDX], 0, sizeof(Elf64_Shdr));
+	shdrs[SHN_SHSTRTAB_IDX].sh_name = SHSTR_OFF_SHSTRTAB;
+	shdrs[SHN_SHSTRTAB_IDX].sh_type = SHT_STRTAB;
+	shdrs[SHN_SHSTRTAB_IDX].sh_offset = (Elf64_Off)off_shstrtab;
+	shdrs[SHN_SHSTRTAB_IDX].sh_size = (uint64_t)SHSTRTAB_SIZE;
+	shdrs[SHN_SHSTRTAB_IDX].sh_addralign = 1;
+
+	outfd = open(output_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+	if (outfd < 0) {
+		fprintf(stderr, "build_zboot_envelop: cannot create '%s': %s\n", output_path,
+			strerror(errno));
+		goto out;
+	}
+
+	if (write_all(outfd, &ehdr, sizeof(ehdr)) < 0)
+		goto err_write;
+
+	if (write_all(outfd, shdrs, sizeof(shdrs)) < 0)
+		goto err_write;
+
+	/* .kernel - raw bytes, no padding inside the section */
+	if (write_all(outfd, kernel.data, kernel.size) < 0)
+		goto err_write;
+	if (write_padding(outfd, pad_after_kernel) < 0)
+		goto err_write;
+
+	/* .initrd */
+	if (write_all(outfd, initrd.data, initrd.size) < 0)
+		goto err_write;
+	if (write_padding(outfd, pad_after_initrd) < 0)
+		goto err_write;
+
+	/* .cmdline - NUL-terminated string */
+	if (write_all(outfd, cmdline, cmdline_size) < 0)
+		goto err_write;
+	if (write_padding(outfd, pad_after_cmdline) < 0)
+		goto err_write;
+
+	/* .shstrtab */
+	if (write_all(outfd, SHSTRTAB_CONTENT, SHSTRTAB_SIZE) < 0)
+		goto err_write;
+
+	printf("build_zboot_envelop: wrote '%s'  (.kernel=%zuB  .initrd=%zuB  "
+	       ".cmdline=%zuB)\n",
+	       output_path, kernel.size, initrd.size, cmdline_size - 1);
+
+	close(outfd);
+	ret = EXIT_SUCCESS;
+	goto out;
+
+err_write:
+	fprintf(stderr, "build_zboot_envelop: write error on '%s': %s\n", output_path,
+		strerror(errno));
+	close(outfd);
+	unlink(output_path);
+
+out:
+	close_and_unmap(&kernel);
+	close_and_unmap(&initrd);
+	return ret;
+}
diff --git a/tools/kexec/zboot_envelop.h b/tools/kexec/zboot_envelop.h
new file mode 100644
index 0000000000000..813723c64ecf3
--- /dev/null
+++ b/tools/kexec/zboot_envelop.h
@@ -0,0 +1,7 @@
+#ifndef ZBOOT_ENVELOP_H
+#define ZBOOT_ENVELOP_H
+
+#define KERNEL_SECT_NAME ".kernel"
+#define INITRD_SECT_NAME ".initrd"
+#define CMDLINE_SECT_NAME ".cmdline"
+#endif
diff --git a/tools/kexec/zboot_parser_bpf.c b/tools/kexec/zboot_parser_bpf.c
index 10098dca2a27a..16fbad83a9bde 100644
--- a/tools/kexec/zboot_parser_bpf.c
+++ b/tools/kexec/zboot_parser_bpf.c
@@ -16,6 +16,7 @@
 #define RINGBUF4_SIZE   MIN_BUF_SIZE
 
 #include "template.c"
+#include "zboot_envelop.h"
 
 #define ELF_SCAN_MAX 8
 
@@ -43,9 +44,9 @@ struct linux_pe_zboot_header {
 	unsigned int pe_header_offset;
 } __attribute__((packed));
 
-static const char linux_sect_name[]   = ".kernel";
-static const char initrd_sect_name[]   = ".initrd";
-static const char cmdline_sect_name[]   = ".cmdline";
+static const char linux_sect_name[] = KERNEL_SECT_NAME;
+static const char initrd_sect_name[] = INITRD_SECT_NAME;
+static const char cmdline_sect_name[] = CMDLINE_SECT_NAME;
 
 /*
  * fill_cmd - overwrite the cmd_hdr at the start of @buf and copy @data_len
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCHv7 08/13] kexec_file: Factor out routine to find a symbol in ELF
  2026-03-22  1:43 ` [PATCHv7 08/13] kexec_file: Factor out routine to find a symbol in ELF Pingfan Liu
@ 2026-03-22  2:14   ` bot+bpf-ci
  2026-03-23  7:07   ` kernel test robot
  2026-03-23 10:19   ` kernel test robot
  2 siblings, 0 replies; 26+ messages in thread
From: bot+bpf-ci @ 2026-03-22  2:14 UTC (permalink / raw)
  To: piliu, kexec
  Cc: piliu, davem, ast, daniel, john.fastabend, andrii, martin.lau,
	eddyz87, song, yonghong.song, jeremy.linton, catalin.marinas,
	will, ardb, horms, kraxel, vkuznets, prudo, vmalik, kernel, bhe,
	dyoung, akpm, bpf, systemd-devel, linux-kernel, ast, andrii,
	daniel, martin.lau, eddyz87, yonghong.song, clm, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 1695 bytes --]

> diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
> --- a/kernel/kexec_file.c
> +++ b/kernel/kexec_file.c

[ ... ]

> @@ -1221,6 +1266,22 @@ int kexec_load_purgatory(struct kimage *image, struct kexec_buf *kbuf)
> 	return ret;
>  }
>
> +/*
> + * kexec_purgatory_find_symbol - find a symbol in the purgatory
> + * @pi:		Purgatory to search in.
> + * @name:	Name of the symbol.
> + *
> + * Return: pointer to symbol in read-only symtab on success, NULL on error.
> + */
> +static const Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi,
> +						  const char *name)
> +{
> +	if (!pi->ehdr)
> +		return NULL;
> +
> +	return elf_find_symbol(pi->ehdr, name);
> +}

This adds a new wrapper kexec_purgatory_find_symbol() here, but the
original full implementation of kexec_purgatory_find_symbol() earlier
in the file was not removed:

kernel/kexec_file.c:kexec_purgatory_find_symbol() {
    ...
    ehdr = pi->ehdr;
    sechdrs = (void *)ehdr + ehdr->e_shoff;

    for (i = 0; i < ehdr->e_shnum; i++) {
        ...
    }

    return NULL;
}

Both definitions are static and live inside the same
#ifdef CONFIG_ARCH_SUPPORTS_KEXEC_PURGATORY block, so this will fail
to compile on any architecture that selects that config (x86, s390,
powerpc, riscv).

It looks like the old function body should have been replaced with
the new elf_find_symbol() wrapper, as was done in v5 of this series.
Was the deletion of the old body lost during a rebase?


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/23393379437

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCHv7 06/13] kexec_file: Chain the stages into a pipeline
  2026-03-22  1:43 ` [PATCHv7 06/13] kexec_file: Chain the stages into a pipeline Pingfan Liu
@ 2026-03-22  2:14   ` bot+bpf-ci
  0 siblings, 0 replies; 26+ messages in thread
From: bot+bpf-ci @ 2026-03-22  2:14 UTC (permalink / raw)
  To: piliu, kexec
  Cc: piliu, davem, ast, daniel, john.fastabend, andrii, martin.lau,
	eddyz87, song, yonghong.song, jeremy.linton, catalin.marinas,
	will, ardb, horms, kraxel, vkuznets, prudo, vmalik, kernel, bhe,
	dyoung, akpm, bpf, systemd-devel, linux-kernel, ast, andrii,
	daniel, martin.lau, eddyz87, yonghong.song, clm, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 2693 bytes --]

> diff --git a/kernel/kexec_bpf_loader.c b/kernel/kexec_bpf_loader.c
> index af16f7b685d9..7f7884411e2c 100644
> --- a/kernel/kexec_bpf_loader.c
> +++ b/kernel/kexec_bpf_loader.c

[ ... ]

> @@ -301,6 +305,23 @@ static int kexec_buff_parser(struct bpf_parser_context *parser)
>  				vfree(ctx->kernel);
>  				ctx->kernel = decompressed_buf;
>  				ctx->kernel_sz = decompressed_sz;
> +				if (fill_pipeline) {
> +					int i;
> +
> +					for (i = 0; i < MAX_PARSING_BUF_NUM; i++) {
> +						if (ctx->next_parsing_buf[i])
> +							continue;
> +						ctx->next_parsing_buf[i] = decompressed_buf;
> +						ctx->next_parsing_buf_sz[i] = decompressed_sz;
> +						break;
> +					}

After this, both ctx->kernel and ctx->next_parsing_buf[i] point to
the same decompressed_buf allocation. When next_parsing_buf is later
promoted to parsing_buf in process_bpf_parsers_container(), there
are two independent references to the same memory.

If the next BPF stage issues another KEXEC_BPF_CMD_DECOMPRESS for
KEXEC_BPF_SUBCMD_KERNEL, vfree(ctx->kernel) frees the buffer while
parsing_buf[i] still references it.

The KEXEC_BPF_CMD_COPY path avoids this by allocating a separate
copy with __vmalloc(). Should the DECOMPRESS path do the same to
prevent aliased ownership?

> +					/* No enough parsing slot */
> +					if (i == MAX_PARSING_BUF_NUM) {
> +						ctx->kernel = NULL;
> +						vfree(decompressed_buf);
> +						return -ENOMEM;
> +					}
> +				}
>  				break;

[ ... ]

> @@ -313,6 +334,22 @@ static int kexec_buff_parser(struct bpf_parser_context *parser)
>  		if (!p)
>  			return -ENOMEM;
>  		memcpy(p, buf, cmd->payload_len);
> +		if (fill_pipeline) {
> +			pn = __vmalloc(cmd->payload_len, GFP_KERNEL | __GFP_ACCOUNT);
> +			if (!pn) {
> +				vfree(p);
> +				return -ENOMEM;
> +			}
> +			memcpy(pn, buf, cmd->payload_len);
> +			for (int i = 0; i < MAX_PARSING_BUF_NUM; i++) {
> +				if (!ctx->next_parsing_buf[i]) {
> +					ctx->next_parsing_buf[i] = pn;
> +					ctx->next_parsing_buf_sz[i] = cmd->payload_len;
> +					break;
> +				}
> +			}
> +		}

If all MAX_PARSING_BUF_NUM slots are already occupied, the loop
completes without storing pn anywhere, and the allocation is leaked.

The DECOMPRESS path handles this case with an explicit check:

    if (i == MAX_PARSING_BUF_NUM) {
        ctx->kernel = NULL;
        vfree(decompressed_buf);
        return -ENOMEM;
    }

Is a similar overflow check missing here for the COPY path?

[ ... ]


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/23393379437

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCHv7 01/13] bpf: Introduce kfuncs to parser buffer content
  2026-03-22  1:43 ` [PATCHv7 01/13] bpf: Introduce kfuncs to parser buffer content Pingfan Liu
@ 2026-03-22  2:14   ` bot+bpf-ci
  0 siblings, 0 replies; 26+ messages in thread
From: bot+bpf-ci @ 2026-03-22  2:14 UTC (permalink / raw)
  To: piliu, bpf
  Cc: piliu, davem, ast, daniel, john.fastabend, andrii, martin.lau,
	eddyz87, song, yonghong.song, jeremy.linton, catalin.marinas,
	will, ardb, horms, kraxel, vkuznets, prudo, vmalik, kernel, bhe,
	dyoung, akpm, kexec, systemd-devel, linux-kernel, kpsingh, sdf,
	haoluo, jolsa, ast, andrii, daniel, martin.lau, eddyz87,
	yonghong.song, clm, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 2123 bytes --]

> diff --git a/kernel/bpf/bpf_buffer_parser.c b/kernel/bpf/bpf_buffer_parser.c
> new file mode 100644
> index 000000000000..5d5c06833079
> --- /dev/null
> +++ b/kernel/bpf/bpf_buffer_parser.c

[ ... ]

> +static void release_bpf_parser_context(struct kref *kref)
> +{
> +	struct bpf_parser_context *ctx = container_of(kref, struct bpf_parser_context, ref);
> +
> +	if (ctx->buf) {
> +		vfree(ctx->buf->buf);
> +		kfree(ctx->buf);
> +	}

If release_bpf_parser_context() runs while bpf_buffer_parser() has
stored the sentinel (void *)1 in ctx->buf via:

    old_val = cmpxchg(&context->buf, NULL, (void *)1);

then ctx->buf is non-NULL but not a valid struct bpf_parser_buf
pointer, and vfree(ctx->buf->buf) will dereference address 1.

The BPF verifier prevents this today by holding the acquired reference
across the kfunc call, but release_bpf_parser_context() has no
awareness of the sentinel value.  Would it be worth adding a check
here to handle the sentinel, or using a separate flag/mutex instead
of overloading ctx->buf?

[ ... ]

> +__bpf_kfunc int bpf_buffer_parser(char *buf, int buf_sz,
> +		struct bpf_parser_context *context)
> +{

[ ... ]

> +	ret = copy_from_kernel_nofault(b, buf, buf_sz);
> +	if (!!ret) {

The !!ret is equivalent to just testing ret here.  Was the double
negation intentional?

> +		context->buf = NULL;
> +		vfree(b);
> +		return ret;
> +	}

[ ... ]

> +static const struct btf_kfunc_id_set buffer_parser_kfunc_set = {
> +        .owner = THIS_MODULE,
> +        .set   = &buffer_parser_ids,
> +};

These two lines use spaces for indentation instead of tabs.

> +
> +

There is a double blank line here.

> +BTF_ID_LIST(buffer_parser_dtor_ids)

[ ... ]

> +	ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &buffer_parser_kfunc_set);
> +	return  ret ?: register_btf_id_dtor_kfuncs(buffer_parser_dtors,

There is a double space before ret.


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/23393379437

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCHv7 09/13] kexec_file: Integrate bpf light skeleton to load image with bpf-prog
  2026-03-22  1:43 ` [PATCHv7 09/13] kexec_file: Integrate bpf light skeleton to load image with bpf-prog Pingfan Liu
@ 2026-03-22  2:14   ` bot+bpf-ci
  0 siblings, 0 replies; 26+ messages in thread
From: bot+bpf-ci @ 2026-03-22  2:14 UTC (permalink / raw)
  To: piliu, kexec
  Cc: piliu, davem, ast, daniel, john.fastabend, andrii, martin.lau,
	eddyz87, song, yonghong.song, jeremy.linton, catalin.marinas,
	will, ardb, horms, kraxel, vkuznets, prudo, vmalik, kernel, bhe,
	dyoung, akpm, bpf, systemd-devel, linux-kernel, ast, andrii,
	daniel, martin.lau, eddyz87, yonghong.song, clm, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 2920 bytes --]

> diff --git a/kernel/kexec_bpf_loader.c b/kernel/kexec_bpf_loader.c
> index 7f7884411e2c..430f6c3970b6 100644
> --- a/kernel/kexec_bpf_loader.c
> +++ b/kernel/kexec_bpf_loader.c

[ ... ]

> +static void *get_symbol_from_elf(const char *elf_data, size_t elf_size,
> +				 const char *symbol_name,
> +				 unsigned int *symbol_size)
> +{

[ ... ]

> +	/* Check section data bounds */
> +	if (dst_shdr->sh_offset > elf_size ||
> +	    dst_shdr->sh_offset + dst_shdr->sh_size > elf_size ||
> +	    sym->st_value > dst_shdr->sh_size) {
> +		pr_err("Symbol data out of bounds\n");
> +		return NULL;
> +	}
> +
> +	symbol_data = (void *)(elf_data + dst_shdr->sh_offset + sym->st_value);
> +
> +	if (symbol_size)
> +		*symbol_size = sym->st_size;
> +
> +	return symbol_data;
> +}

The bounds check validates that sym->st_value falls within the section,
but it does not validate that sym->st_value + sym->st_size also fits
within the section (or the ELF buffer).

The caller arm_bpf_prog() passes the returned sym->st_size through to
bpf_load_and_run() as opts.data_sz / opts.insns_sz, which controls how
many bytes are copied from the pointer:

    arm_bpf_prog()
      opts_data = get_symbol_from_elf(..., &opts_data_sz);
        -> *symbol_size = sym->st_size;
      ...
      kexec_pe_parser_bpf__open_and_load()
        kexec_pe_parser_bpf__load()
          opts.data_sz = opts_data_sz;
          bpf_load_and_run(&opts);
            skel_map_update_elem(map_fd, &key, opts->data, 0);

Since the ELF comes from userspace via the PE .bpf section, could a
crafted ELF with a valid st_value but an oversized st_size cause a
read beyond the buffer here? Something like:

    if (sym->st_value + sym->st_size > dst_shdr->sh_size) ...

might be needed alongside the existing st_value check.

> + /* Load a ELF */
>  static int arm_bpf_prog(char *bpf_elf, unsigned long sz)
>  {

[ ... ]

> +	pe_parser = kexec_pe_parser_bpf__open_and_load();
> +	if (!pe_parser) {
> +		pr_info("Can not open and load bpf parser\n");
> +		return -1;
> +	}
> +	kexec_pe_parser_bpf__attach(pe_parser);
> +
> +	return 0;
>  }

kexec_pe_parser_bpf__attach() returns an error code, but its return
value is not checked here. If attach fails (e.g.
skel_raw_tracepoint_open() returns an error), arm_bpf_prog() still
returns 0.

The caller process_bpf_parsers_container() then proceeds to call
kexec_image_parser_anchor(), which is a no-op without the fentry
hook. context->parsed stays false, and every section appears to fail
with "no BPF section succeeded" -- hiding the real cause.

Should the return value of kexec_pe_parser_bpf__attach() be checked,
with the parser destroyed and an error returned on failure?


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/23393379437

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCHv7 02/13] kexec_file: Use bpf-prog to decompose image
  2026-03-22  1:43 ` [PATCHv7 02/13] kexec_file: Use bpf-prog to decompose image Pingfan Liu
@ 2026-03-22  2:14   ` bot+bpf-ci
  0 siblings, 0 replies; 26+ messages in thread
From: bot+bpf-ci @ 2026-03-22  2:14 UTC (permalink / raw)
  To: piliu, kexec
  Cc: piliu, davem, ast, daniel, john.fastabend, andrii, martin.lau,
	eddyz87, song, yonghong.song, jeremy.linton, catalin.marinas,
	will, ardb, horms, kraxel, vkuznets, prudo, vmalik, kernel, bhe,
	dyoung, akpm, bpf, systemd-devel, linux-kernel, ast, andrii,
	daniel, martin.lau, eddyz87, yonghong.song, clm, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 3234 bytes --]

> diff --git a/kernel/kexec_bpf_loader.c b/kernel/kexec_bpf_loader.c
> new file mode 100644
> index 000000000000..bd1800a76782
> --- /dev/null
> +++ b/kernel/kexec_bpf_loader.c

[ ... ]

> +#include <linux/bpf.h>
> +#include <linux/filter.h>
> +#include <asm/byteorder.h>
> +#include <asm/image.h>
> +#include <asm/memory.h>
> +#include "kexec_internal.h"

Are asm/image.h and asm/memory.h needed here?  I could not find any
symbols from either header used in this file.  asm/image.h only exists
for arm64, loongarch, and riscv, and asm/memory.h only for arm, arm64,
and csky.

Since CONFIG_KEXEC_BPF has no architecture dependency, enabling it on
x86 would cause a build failure from these missing headers.

[ ... ]

> +static int process_bpf_parsers_container(const char *elf_buf, size_t elf_sz,
> +				   struct kexec_context *context, int depth)
> +{

[ ... ]

> +		/*
> +		 * Give the BPF prog a clean slate so context->parsed reliably
> +		 * reflects whether *this* invocation succeeded.
> +		 */
> +		context->parsed = false;
> +		/* This is the hook point for bpf-prog */
> +		kexec_image_parser_anchor(context, (unsigned long)bpf);
> +		disarm_bpf_prog();
> +
> +		/* Free the old parsing context, and reload the new */
> +		for (int i = 0; i < MAX_PARSING_BUF_NUM; i++) {
> +			if (!!context->parsing_buf[i])
> +				break;
> +			vfree(context->parsing_buf[i]);
> +			context->parsing_buf[i] = NULL;
> +			context->parsing_buf_sz[i] = 0;
> +		}

The comment says "free the old parsing context" but the condition
appears inverted.  !!context->parsing_buf[i] is true when the pointer
is non-NULL, so the loop breaks at the first populated entry and only
calls vfree() on NULL pointers (which is a no-op).

Should this be if (!context->parsing_buf[i]) instead?

[ ... ]

> +int decompose_kexec_image(struct kimage *image, int extended_fd)
> +{
> +	struct kexec_context ctx = { 0 };
> +	unsigned long parser_sz;
> +	char *parser_start;
> +	int ret = -EINVAL;

[ ... ]

> +	if (!validate_elf_bpf_sections(parser_start, parser_sz)) {
> +
> +		ret = kernel_read_file_from_fd(extended_fd,
> +						0,
> +						(void **)&ctx.parsing_buf[0],
> +						KEXEC_FILE_SIZE_MAX,
> +						NULL,
> +						0);
> +		if (ret < 0) {
> +			pr_err("Fail to read image container\n");
> +			return -EINVAL;
> +		}
> +		ctx.parsing_buf_sz[0] = ret;
> +		ret = process_bpf_parsers_container(parser_start, parser_sz, &ctx, 0);
> +		if (!ret) {
> +			char *p;
> +
> +			/* Envelop should hold valid kernel, initrd, cmdline sections */
> +			if (!ctx.kernel || !ctx.initrd || !ctx.cmdline) {
> +				vfree(ctx.kernel);
> +				vfree(ctx.initrd);
> +				vfree(ctx.cmdline);
> +				return -EINVAL;
> +			}

[ ... ]

> +		}
> +		return ret;
> +	}
> +
> +	return -EINVAL;
> +}

When process_bpf_parsers_container() fails, or when the
!ctx.kernel || !ctx.initrd || !ctx.cmdline check triggers,
ctx.parsing_buf[0] was allocated by kernel_read_file_from_fd() but
is never freed before returning.  Does this leak the buffer?


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/23393379437

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCHv7 11/13] tools/kexec: Introduce a bpf-prog to handle zboot image
  2026-03-22  1:44 ` [PATCHv7 11/13] tools/kexec: Introduce a bpf-prog to handle zboot image Pingfan Liu
@ 2026-03-22  2:26   ` bot+bpf-ci
  0 siblings, 0 replies; 26+ messages in thread
From: bot+bpf-ci @ 2026-03-22  2:26 UTC (permalink / raw)
  To: piliu, kexec
  Cc: piliu, davem, ast, daniel, john.fastabend, andrii, martin.lau,
	eddyz87, song, yonghong.song, jeremy.linton, catalin.marinas,
	will, ardb, horms, kraxel, vkuznets, prudo, vmalik, kernel, bhe,
	dyoung, akpm, bpf, systemd-devel, linux-kernel, ast, andrii,
	daniel, martin.lau, eddyz87, yonghong.song, clm, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 3664 bytes --]

> diff --git a/tools/kexec/Makefile b/tools/kexec/Makefile
> --- /dev/null
> +++ b/tools/kexec/Makefile

[ ... ]

> +ifeq ($(ARCH),$(filter $(ARCH),arm64 riscv loongarch))
> +	EFI_IMAGE := $(srctree)/arch/$(ARCH)/boot/vmlinuz.efi
> +	KERNEL_IMAGE := $(srctree)/arch/$(ARCH)/boot/vmlinux.bin
> +else
> +	@echo "Unsupported architecture: $(ARCH)"
> +	@exit 1
> +endif

The else branch uses recipe syntax (@echo, @exit) at top-level
Makefile scope, outside of any rule.  On unsupported architectures
this would produce a confusing GNU Make parse error rather than
the intended message.  Should this use $(error ...) instead?

> +zboot: $(HEADERS) $(call BPF_ARTIFACTS,zboot) build_zboot_image

There is no rule or file for the build_zboot_image prerequisite
in this Makefile.  Will this cause "make zboot" to fail with
"No rule to make target 'build_zboot_image'"?

[ ... ]

> diff --git a/tools/kexec/zboot_parser_bpf.c b/tools/kexec/zboot_parser_bpf.c
> --- /dev/null
> +++ b/tools/kexec/zboot_parser_bpf.c

[ ... ]

> +/* ringbuf 2,3,4 are useless */
> +#define MIN_BUF_SIZE    1
> +#define MAX_RECORD_SIZE (IMAGE_SIZE + 40960)
> +#define RINGBUF1_SIZE   IMAGE_SIZE_POWER2_ALIGN
> +#define RINGBUF2_SIZE   MIN_BUF_SIZE
> +#define RINGBUF3_SIZE   MIN_BUF_SIZE
> +#define RINGBUF4_SIZE   MIN_BUF_SIZE

ringbuf_map_alloc() in kernel/bpf/ringbuf.c requires max_entries
to be both power-of-2 and PAGE_ALIGNED.  With MIN_BUF_SIZE set
to 1, ringbufs 2/3/4 will fail map creation with -EINVAL since
PAGE_ALIGNED(1) is false.  The minimum valid size would be
PAGE_SIZE (typically 4096).  Would the BPF program fail to load
because of this?

[ ... ]

> +static int fill_cmd(char *buf, __u16 cmd, __u16 subcmd,
> +				    const char *src, __u32 data_len)
> +{
> +	struct cmd_hdr *hdr;
> +	char *payload;
> +
> +	hdr              = (struct cmd_hdr *)buf;
> +	hdr->cmd         = cmd;
> +	hdr->subcmd      = subcmd;
> +	hdr->payload_len = data_len;
> +	hdr->num_chunks  = 0;

The struct cmd_hdr (in kernel/kexec_bpf_loader.c) also has a
pipeline_flag field at offset 3:

    struct cmd_hdr {
        uint16_t cmd;
        uint8_t subcmd;
        uint8_t pipeline_flag;
        uint32_t payload_len;
        uint16_t num_chunks;
    };

This field is not initialized here, and bpf_ringbuf_reserve() does
not zero the returned memory.  The kernel's kexec_buff_parser()
reads this field:

    fill_pipeline = cmd->pipeline_flag & KEXEC_BPF_PIPELINE_FILL;

If the garbage byte happens to have bit 0x1 set, this would
trigger unintended pipeline fill behavior, causing extra
allocations and copies into next_parsing_buf[].  Should
pipeline_flag be explicitly zeroed?

[ ... ]

> +SEC("fentry.s/kexec_image_parser_anchor")
> +int BPF_PROG(parse_zboot, struct kexec_context *context, unsigned long parser_id)
> +{
> +	kexec_sig_enforced sig_mode;

[ ... ]

> +	sig_mode = BPF_CORE_READ(context, sig_mode);

The type kexec_sig_enforced does not appear to exist as a type
(enum, typedef, or struct) anywhere in the kernel sources.  The
only related symbol is the function set_kexec_sig_enforced().
Similarly, struct kexec_context (in kernel/kexec_bpf_loader.c)
has no sig_mode field.

Since the BPF program includes vmlinux.h generated from the
kernel's BTF, it would fail to compile without this type, and
the BPF_CORE_READ would fail CO-RE relocation without the
struct field.  Are these expected to be added by a patch not
yet in this series?


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/23393379437

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCHv7 04/13] kexec_file: Implement decompress method for parser
  2026-03-22  1:43 ` [PATCHv7 04/13] kexec_file: Implement decompress method for parser Pingfan Liu
@ 2026-03-22  2:26   ` bot+bpf-ci
  0 siblings, 0 replies; 26+ messages in thread
From: bot+bpf-ci @ 2026-03-22  2:26 UTC (permalink / raw)
  To: piliu, kexec
  Cc: piliu, davem, ast, daniel, john.fastabend, andrii, martin.lau,
	eddyz87, song, yonghong.song, jeremy.linton, catalin.marinas,
	will, ardb, horms, kraxel, vkuznets, prudo, vmalik, kernel, bhe,
	dyoung, akpm, bpf, systemd-devel, linux-kernel, ast, andrii,
	daniel, martin.lau, eddyz87, yonghong.song, clm, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 1950 bytes --]

> diff --git a/kernel/kexec_bpf_loader.c b/kernel/kexec_bpf_loader.c
> index bd1800a76782..c79fb777d51b 100644
> --- a/kernel/kexec_bpf_loader.c
> +++ b/kernel/kexec_bpf_loader.c

[ ... ]

> @@ -73,8 +74,243 @@ static int __init kexec_bpf_prog_run_init(void)
>  }
>  late_initcall(kexec_bpf_prog_run_init);

[ ... ]

>  static int kexec_buff_parser(struct bpf_parser_context *parser)
>  {
> +	struct bpf_parser_buf *pbuf = parser->buf;
> +	struct kexec_context *ctx = (struct kexec_context *)parser->data;
> +	struct cmd_hdr *cmd = (struct cmd_hdr *)pbuf->buf;
> +	char *decompressed_buf, *buf, *p;
> +	unsigned long decompressed_sz;
> +	int ret = 0;

[ ... ]

> +	case KEXEC_BPF_CMD_DECOMPRESS:
> +		ret = parser_cmd_decompress(buf, cmd->payload_len, &decompressed_buf,
> +					&decompressed_sz, ctx);
> +		if (!ret) {
> +			switch (cmd->subcmd) {
> +			case KEXEC_BPF_SUBCMD_KERNEL:
> +				vfree(ctx->kernel);
> +				ctx->kernel = decompressed_buf;
> +				ctx->kernel_sz = decompressed_sz;
> +				break;
> +			default:
> +				vfree(decompressed_buf);
> +				break;
> +			}
> +		}
> +		break;
> +	default:
> +		break;
> +	}
> +
>  	return 0;
>  }

Should this return ret instead of 0?  When parser_cmd_decompress()
fails, ret holds the error code, but the function discards it and
returns success.

bpf_buffer_parser() in bpf_buffer_parser.c propagates this return
value back to the calling BPF program:

    ret = context->func(context);
    ...
    return ret;

Returning 0 here means the BPF program sees success even when
decompression failed.  It would then proceed to send
KEXEC_BPF_CMD_DONE, setting ctx->parsed = true, causing kexec
to complete loading with a missing or undecompressed kernel image.


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/23393379437

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCHv7 03/13] lib/decompress: Keep decompressor when CONFIG_KEEP_DECOMPRESSOR
  2026-03-22  1:43 ` [PATCHv7 03/13] lib/decompress: Keep decompressor when CONFIG_KEEP_DECOMPRESSOR Pingfan Liu
@ 2026-03-22  2:26   ` bot+bpf-ci
  0 siblings, 0 replies; 26+ messages in thread
From: bot+bpf-ci @ 2026-03-22  2:26 UTC (permalink / raw)
  To: piliu, linux-kernel
  Cc: piliu, davem, ast, daniel, john.fastabend, andrii, martin.lau,
	eddyz87, song, yonghong.song, jeremy.linton, catalin.marinas,
	will, ardb, horms, kraxel, vkuznets, prudo, vmalik, kernel, bhe,
	dyoung, akpm, kexec, bpf, systemd-devel, ast, andrii, daniel,
	martin.lau, eddyz87, yonghong.song, clm, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 1326 bytes --]

> diff --git a/include/linux/decompress/mm.h b/include/linux/decompress/mm.h
> index ac862422df15..39df02bcbc66 100644
> --- a/include/linux/decompress/mm.h
> +++ b/include/linux/decompress/mm.h

[ ... ]

> @@ -92,7 +93,14 @@ MALLOC_VISIBLE void free(void *where)
>  #define large_malloc(a) vmalloc(a)
>  #define large_free(a) vfree(a)
>
> +#ifdef CONFIG_KEEP_DECOMPRESSOR
> +#define INIT
> +#define INITCONST
> +#else
>  #define INIT __init
> +#define INITCONST __initconst
> +#endif

The INITCONST macro is defined here in the kernel path (the #else
branch of #ifdef STATIC), but the pre-boot path earlier in this
header only defines INIT:

include/linux/decompress/mm.h:
    #ifdef STATIC
    ...
    #define INIT

Should INITCONST also be defined in the pre-boot path for
consistency? The individual decompressor files (decompress_inflate.c,
decompress_bunzip2.c, etc.) all include this header and are compiled
in both contexts. If any of them later uses INITCONST for a const
array following the pattern in lib/decompress.c, the pre-boot build
would fail with an undefined macro.


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/23393379437

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCHv7 08/13] kexec_file: Factor out routine to find a symbol in ELF
  2026-03-22  1:43 ` [PATCHv7 08/13] kexec_file: Factor out routine to find a symbol in ELF Pingfan Liu
  2026-03-22  2:14   ` bot+bpf-ci
@ 2026-03-23  7:07   ` kernel test robot
  2026-03-23 10:19   ` kernel test robot
  2 siblings, 0 replies; 26+ messages in thread
From: kernel test robot @ 2026-03-23  7:07 UTC (permalink / raw)
  To: Pingfan Liu, kexec
  Cc: llvm, oe-kbuild-all, Pingfan Liu, Alexei Starovoitov,
	Daniel Borkmann, John Fastabend, Andrii Nakryiko,
	Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
	Jeremy Linton, Catalin Marinas, Will Deacon, Ard Biesheuvel,
	Simon Horman, Gerd Hoffmann, Vitaly Kuznetsov, Philipp Rudo,
	Viktor Malik, Jan Hendrik Farr, Baoquan He, Dave Young,
	Andrew Morton, Linux Memory Management List, bpf, systemd-devel,
	linux-kernel

Hi Pingfan,

kernel test robot noticed the following build errors:

[auto build test ERROR on bpf-next/net]
[also build test ERROR on bpf-next/master bpf/master akpm-mm/mm-nonmm-unstable linus/master v7.0-rc5 next-20260320]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Pingfan-Liu/bpf-Introduce-kfuncs-to-parser-buffer-content/20260323-084500
base:   https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git net
patch link:    https://lore.kernel.org/r/20260322014402.8815-9-piliu%40redhat.com
patch subject: [PATCHv7 08/13] kexec_file: Factor out routine to find a symbol in ELF
config: x86_64-kexec (https://download.01.org/0day-ci/archive/20260323/202603230851.qOD27RaS-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260323/202603230851.qOD27RaS-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202603230851.qOD27RaS-lkp@intel.com/

All errors (new ones prefixed by >>):

>> kernel/kexec_file.c:1276:23: error: redefinition of 'kexec_purgatory_find_symbol'
    1276 | static const Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi,
         |                       ^
   kernel/kexec_file.c:958:23: note: previous definition is here
     958 | static const Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi,
         |                       ^
   1 error generated.


vim +/kexec_purgatory_find_symbol +1276 kernel/kexec_file.c

  1268	
  1269	/*
  1270	 * kexec_purgatory_find_symbol - find a symbol in the purgatory
  1271	 * @pi:		Purgatory to search in.
  1272	 * @name:	Name of the symbol.
  1273	 *
  1274	 * Return: pointer to symbol in read-only symtab on success, NULL on error.
  1275	 */
> 1276	static const Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi,
  1277							  const char *name)
  1278	{
  1279		if (!pi->ehdr)
  1280			return NULL;
  1281	
  1282		return elf_find_symbol(pi->ehdr, name);
  1283	}
  1284	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCHv7 08/13] kexec_file: Factor out routine to find a symbol in ELF
  2026-03-22  1:43 ` [PATCHv7 08/13] kexec_file: Factor out routine to find a symbol in ELF Pingfan Liu
  2026-03-22  2:14   ` bot+bpf-ci
  2026-03-23  7:07   ` kernel test robot
@ 2026-03-23 10:19   ` kernel test robot
  2 siblings, 0 replies; 26+ messages in thread
From: kernel test robot @ 2026-03-23 10:19 UTC (permalink / raw)
  To: Pingfan Liu, kexec
  Cc: oe-kbuild-all, Pingfan Liu, Alexei Starovoitov, Daniel Borkmann,
	John Fastabend, Andrii Nakryiko, Martin KaFai Lau,
	Eduard Zingerman, Song Liu, Yonghong Song, Jeremy Linton,
	Catalin Marinas, Will Deacon, Ard Biesheuvel, Simon Horman,
	Gerd Hoffmann, Vitaly Kuznetsov, Philipp Rudo, Viktor Malik,
	Jan Hendrik Farr, Baoquan He, Dave Young, Andrew Morton,
	Linux Memory Management List, bpf, systemd-devel, linux-kernel

Hi Pingfan,

kernel test robot noticed the following build errors:

[auto build test ERROR on bpf-next/net]
[also build test ERROR on bpf-next/master bpf/master akpm-mm/mm-nonmm-unstable linus/master v7.0-rc5 next-20260320]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Pingfan-Liu/bpf-Introduce-kfuncs-to-parser-buffer-content/20260323-084500
base:   https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git net
patch link:    https://lore.kernel.org/r/20260322014402.8815-9-piliu%40redhat.com
patch subject: [PATCHv7 08/13] kexec_file: Factor out routine to find a symbol in ELF
config: x86_64-randconfig-002-20260323 (https://download.01.org/0day-ci/archive/20260323/202603231812.ZGRRlzPe-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260323/202603231812.ZGRRlzPe-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202603231812.ZGRRlzPe-lkp@intel.com/

All errors (new ones prefixed by >>):

>> kernel/kexec_file.c:1276:23: error: redefinition of 'kexec_purgatory_find_symbol'
    1276 | static const Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi,
         |                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   kernel/kexec_file.c:958:23: note: previous definition of 'kexec_purgatory_find_symbol' with type 'const Elf64_Sym *(struct purgatory_info *, const char *)' {aka 'const struct elf64_sym *(struct purgatory_info *, const char *)'}
     958 | static const Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi,
         |                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~


vim +/kexec_purgatory_find_symbol +1276 kernel/kexec_file.c

  1268	
  1269	/*
  1270	 * kexec_purgatory_find_symbol - find a symbol in the purgatory
  1271	 * @pi:		Purgatory to search in.
  1272	 * @name:	Name of the symbol.
  1273	 *
  1274	 * Return: pointer to symbol in read-only symtab on success, NULL on error.
  1275	 */
> 1276	static const Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi,
  1277							  const char *name)
  1278	{
  1279		if (!pi->ehdr)
  1280			return NULL;
  1281	
  1282		return elf_find_symbol(pi->ehdr, name);
  1283	}
  1284	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCHv7 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image
  2026-03-22  1:43 [PATCHv7 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
                   ` (12 preceding siblings ...)
  2026-03-22  1:44 ` [PATCHv7 13/13] tools/kexec: Introduce a tool to build zboot envelop Pingfan Liu
@ 2026-03-23 19:02 ` Andrew Morton
  2026-03-24  7:09   ` Pingfan Liu
  13 siblings, 1 reply; 26+ messages in thread
From: Andrew Morton @ 2026-03-23 19:02 UTC (permalink / raw)
  To: Pingfan Liu
  Cc: David S. Miller, Alexei Starovoitov, Daniel Borkmann,
	John Fastabend, Andrii Nakryiko, Martin KaFai Lau,
	Eduard Zingerman, Song Liu, Yonghong Song, Jeremy Linton,
	Catalin Marinas, Will Deacon, Ard Biesheuvel, Simon Horman,
	Gerd Hoffmann, Vitaly Kuznetsov, Philipp Rudo, Viktor Malik,
	Jan Hendrik Farr, Baoquan He, Dave Young, kexec, bpf,
	systemd-devel, linux-kernel

On Sun, 22 Mar 2026 09:43:49 +0800 Pingfan Liu <piliu@redhat.com> wrote:

> Nowadays, UEFI PE bootable images are becoming increasingly popular
> among distributions. Currently, we have several kinds of image format
> parsers in user space (kexec-tools). However, this approach breaks the
> integrity protection of the images. To address this integrity protection
> concern, several approaches have been proposed to resolve this issue,
> but none of them have been accepted upstream yet.
> 
> The summary of those approaches:
>   -1. UEFI service emulator for UEFI stub
>   -2. PE format parser in kernel
>   -3. Signing the arm64/boot/Image
> 
> 
> For the first approach, I tried a purgatory-style emulator [1], but it
> encounters hardware scaling issues. For the second approach, both
> zboot-format [2] and UKI-format [3] parsers were rejected due to
> concerns that variant format parsers would bloat the kernel code.
> Additionally, for example in arm64, both UKI and zboot format parsers
> would need to be introduced and chained together to handle image
> loading. For the third approach, I attempted [4], but since zboot or UKI
> images already have signatures, upstream maintainers dislike the
> additional signature on the Image. Moreover, for secure boot UKI, this
> method cannot use signatures to protect the initramfs.
> 
> 
> *** The approach in this series ***
> 
> This series introduces an approach that allows image formats to be
> parsed by BPF programs.

AI review has a ton of questions:
	https://sashiko.dev/#/patchset/20260322014402.8815-1-piliu@redhat.com

Coverage is partial because some patches didn't apply.  Probably some
of these questions are legitimate, others will be false positives -
we're still figuring this out.


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCHv7 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image
  2026-03-23 19:02 ` [PATCHv7 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Andrew Morton
@ 2026-03-24  7:09   ` Pingfan Liu
  0 siblings, 0 replies; 26+ messages in thread
From: Pingfan Liu @ 2026-03-24  7:09 UTC (permalink / raw)
  To: Andrew Morton
  Cc: David S. Miller, Alexei Starovoitov, Daniel Borkmann,
	John Fastabend, Andrii Nakryiko, Martin KaFai Lau,
	Eduard Zingerman, Song Liu, Yonghong Song, Jeremy Linton,
	Catalin Marinas, Will Deacon, Ard Biesheuvel, Simon Horman,
	Gerd Hoffmann, Vitaly Kuznetsov, Philipp Rudo, Viktor Malik,
	Jan Hendrik Farr, Baoquan He, Dave Young, kexec, bpf,
	systemd-devel, linux-kernel

On Tue, Mar 24, 2026 at 3:02 AM Andrew Morton <akpm@linux-foundation.org> wrote:
>
> On Sun, 22 Mar 2026 09:43:49 +0800 Pingfan Liu <piliu@redhat.com> wrote:
>
> > Nowadays, UEFI PE bootable images are becoming increasingly popular
> > among distributions. Currently, we have several kinds of image format
> > parsers in user space (kexec-tools). However, this approach breaks the
> > integrity protection of the images. To address this integrity protection
> > concern, several approaches have been proposed to resolve this issue,
> > but none of them have been accepted upstream yet.
> >
> > The summary of those approaches:
> >   -1. UEFI service emulator for UEFI stub
> >   -2. PE format parser in kernel
> >   -3. Signing the arm64/boot/Image
> >
> >
> > For the first approach, I tried a purgatory-style emulator [1], but it
> > encounters hardware scaling issues. For the second approach, both
> > zboot-format [2] and UKI-format [3] parsers were rejected due to
> > concerns that variant format parsers would bloat the kernel code.
> > Additionally, for example in arm64, both UKI and zboot format parsers
> > would need to be introduced and chained together to handle image
> > loading. For the third approach, I attempted [4], but since zboot or UKI
> > images already have signatures, upstream maintainers dislike the
> > additional signature on the Image. Moreover, for secure boot UKI, this
> > method cannot use signatures to protect the initramfs.
> >
> >
> > *** The approach in this series ***
> >
> > This series introduces an approach that allows image formats to be
> > parsed by BPF programs.
>
> AI review has a ton of questions:
>         https://sashiko.dev/#/patchset/20260322014402.8815-1-piliu@redhat.com
>
> Coverage is partial because some patches didn't apply.  Probably some
> of these questions are legitimate, others will be false positives -
> we're still figuring this out.
>

Thank you, I will check them too.

Best Regards,

Pingfan



^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2026-03-24  7:10 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-22  1:43 [PATCHv7 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
2026-03-22  1:43 ` [PATCHv7 01/13] bpf: Introduce kfuncs to parser buffer content Pingfan Liu
2026-03-22  2:14   ` bot+bpf-ci
2026-03-22  1:43 ` [PATCHv7 02/13] kexec_file: Use bpf-prog to decompose image Pingfan Liu
2026-03-22  2:14   ` bot+bpf-ci
2026-03-22  1:43 ` [PATCHv7 03/13] lib/decompress: Keep decompressor when CONFIG_KEEP_DECOMPRESSOR Pingfan Liu
2026-03-22  2:26   ` bot+bpf-ci
2026-03-22  1:43 ` [PATCHv7 04/13] kexec_file: Implement decompress method for parser Pingfan Liu
2026-03-22  2:26   ` bot+bpf-ci
2026-03-22  1:43 ` [PATCHv7 05/13] kexec_file: Implement copy " Pingfan Liu
2026-03-22  1:43 ` [PATCHv7 06/13] kexec_file: Chain the stages into a pipeline Pingfan Liu
2026-03-22  2:14   ` bot+bpf-ci
2026-03-22  1:43 ` [PATCHv7 07/13] kexec_file: Introduce a bpf-prog lskel to run a format parser Pingfan Liu
2026-03-22  1:43 ` [PATCHv7 08/13] kexec_file: Factor out routine to find a symbol in ELF Pingfan Liu
2026-03-22  2:14   ` bot+bpf-ci
2026-03-23  7:07   ` kernel test robot
2026-03-23 10:19   ` kernel test robot
2026-03-22  1:43 ` [PATCHv7 09/13] kexec_file: Integrate bpf light skeleton to load image with bpf-prog Pingfan Liu
2026-03-22  2:14   ` bot+bpf-ci
2026-03-22  1:43 ` [PATCHv7 10/13] arm64/kexec: Select KEXEC_BPF to support UEFI-style kernel image Pingfan Liu
2026-03-22  1:44 ` [PATCHv7 11/13] tools/kexec: Introduce a bpf-prog to handle zboot image Pingfan Liu
2026-03-22  2:26   ` bot+bpf-ci
2026-03-22  1:44 ` [PATCHv7 12/13] tools/kexec: Introduce a bpf-prog to handle UKI image Pingfan Liu
2026-03-22  1:44 ` [PATCHv7 13/13] tools/kexec: Introduce a tool to build zboot envelop Pingfan Liu
2026-03-23 19:02 ` [PATCHv7 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Andrew Morton
2026-03-24  7:09   ` Pingfan Liu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox