* [PATCHv6 01/13] bpf: Introduce kfuncs to parser buffer content
2026-01-19 3:24 [PATCHv6 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
@ 2026-01-19 3:24 ` Pingfan Liu
2026-01-19 18:45 ` bot+bpf-ci
2026-01-19 3:24 ` [PATCHv6 02/13] kexec_file: Move signature validation ahead Pingfan Liu
` (12 subsequent siblings)
13 siblings, 1 reply; 30+ messages in thread
From: Pingfan Liu @ 2026-01-19 3:24 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_TRUSTED_ARGS | 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 | 19 ++++
kernel/bpf/Makefile | 3 +
kernel/bpf/bpf_buffer_parser.c | 170 +++++++++++++++++++++++++++++++++
3 files changed, 192 insertions(+)
create mode 100644 kernel/bpf/bpf_buffer_parser.c
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index e5be698256d15..25bc1b6b8a600 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -3843,4 +3843,23 @@ 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;
+ 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 232cbc97434db..309b905a81736 100644
--- a/kernel/bpf/Makefile
+++ b/kernel/bpf/Makefile
@@ -56,6 +56,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..6acb4b5da71b3
--- /dev/null
+++ b/kernel/bpf/bpf_buffer_parser.c
@@ -0,0 +1,170 @@
+// 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;
+ int ret;
+ char *b;
+
+ if (unlikely(context->func == NULL))
+ return -EINVAL;
+
+ b = __vmalloc(buf_sz, GFP_KERNEL_ACCOUNT | __GFP_ZERO);
+ if (!b)
+ return -ENOMEM;
+ ret = copy_from_kernel_nofault(b, buf, buf_sz);
+ if (!!ret) {
+ vfree(b);
+ return ret;
+ }
+
+ parser_buf = kmalloc(sizeof(struct bpf_parser_buf), GFP_KERNEL);
+ if (!parser_buf) {
+ vfree(b);
+ return -ENOMEM;
+ }
+ parser_buf->buf = b;
+ parser_buf->size = buf_sz;
+ context->buf = parser_buf;
+ ret = context->func(context);
+
+ 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_TRUSTED_ARGS | 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] 30+ messages in thread* Re: [PATCHv6 01/13] bpf: Introduce kfuncs to parser buffer content
2026-01-19 3:24 ` [PATCHv6 01/13] bpf: Introduce kfuncs to parser buffer content Pingfan Liu
@ 2026-01-19 18:45 ` bot+bpf-ci
0 siblings, 0 replies; 30+ messages in thread
From: bot+bpf-ci @ 2026-01-19 18:45 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: 2219 bytes --]
> diff --git a/kernel/bpf/bpf_buffer_parser.c b/kernel/bpf/bpf_buffer_parser.c
> new file mode 100644
> index 000000000000..6acb4b5da71b
> --- /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);
> + }
[ ... ]
> +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);
ctx->buf is NULL here from kzalloc.
[ ... ]
> +__bpf_kfunc int bpf_buffer_parser(char *buf, int buf_sz,
> + struct bpf_parser_context *context)
> +{
> + struct bpf_parser_buf *parser_buf;
> + int ret;
> + char *b;
> +
> + if (unlikely(context->func == NULL))
> + return -EINVAL;
> +
> + b = __vmalloc(buf_sz, GFP_KERNEL_ACCOUNT | __GFP_ZERO);
> + if (!b)
> + return -ENOMEM;
> + ret = copy_from_kernel_nofault(b, buf, buf_sz);
> + if (!!ret) {
> + vfree(b);
> + return ret;
> + }
> +
> + parser_buf = kmalloc(sizeof(struct bpf_parser_buf), GFP_KERNEL);
> + if (!parser_buf) {
> + vfree(b);
> + return -ENOMEM;
> + }
> + parser_buf->buf = b;
> + parser_buf->size = buf_sz;
> + context->buf = parser_buf;
If bpf_buffer_parser() is called multiple times on the same context, does
this leak the previous parser_buf and its backing vmalloc buffer? The
assignment to context->buf overwrites any existing pointer without first
freeing it.
release_bpf_parser_context() will only free the final context->buf value
when the context is destroyed, so any intermediate buffers from prior
calls would be lost.
Should there be a check here to either return an error if context->buf is
already set, or free the old buffer before assigning the new one?
> + ret = context->func(context);
> +
> + return 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/21147860407
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCHv6 02/13] kexec_file: Move signature validation ahead
2026-01-19 3:24 [PATCHv6 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
2026-01-19 3:24 ` [PATCHv6 01/13] bpf: Introduce kfuncs to parser buffer content Pingfan Liu
@ 2026-01-19 3:24 ` Pingfan Liu
2026-01-19 18:45 ` bot+bpf-ci
2026-02-26 13:37 ` Philipp Rudo
2026-01-19 3:24 ` [PATCHv6 03/13] kexec_file: Introduce routines to parse PE file Pingfan Liu
` (11 subsequent siblings)
13 siblings, 2 replies; 30+ messages in thread
From: Pingfan Liu @ 2026-01-19 3:24 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
Move the signature validation at the head of the function, so the image
can be unfold and handled later.
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 | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index eb62a97942428..0222d17072d40 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -231,18 +231,19 @@ 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 */
- 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;
#endif
+
+ /* Call arch image probe handlers */
+ ret = arch_kexec_kernel_image_probe(image, image->kernel_buf,
+ image->kernel_buf_len);
+ if (ret)
+ goto out;
+
/* It is possible that there no initramfs is being loaded */
if (!(flags & KEXEC_FILE_NO_INITRAMFS)) {
ret = kernel_read_file_from_fd(initrd_fd, 0, &image->initrd_buf,
--
2.49.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCHv6 02/13] kexec_file: Move signature validation ahead
2026-01-19 3:24 ` [PATCHv6 02/13] kexec_file: Move signature validation ahead Pingfan Liu
@ 2026-01-19 18:45 ` bot+bpf-ci
2026-02-26 13:37 ` Philipp Rudo
1 sibling, 0 replies; 30+ messages in thread
From: bot+bpf-ci @ 2026-01-19 18:45 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: 2236 bytes --]
> diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
> index eb62a9794242..0222d17072d4 100644
> --- a/kernel/kexec_file.c
> +++ b/kernel/kexec_file.c
> @@ -231,18 +231,19 @@ 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 */
> - 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;
> #endif
Does this reordering break signature verification? The kimage_validate_signature()
function calls kexec_image_verify_sig(), which accesses image->fops->verify_sig:
static int kexec_image_verify_sig(struct kimage *image, void *buf,
unsigned long buf_len)
{
if (!image->fops || !image->fops->verify_sig) {
pr_debug("kernel loader does not support signature verification.\n");
return -EKEYREJECTED;
}
return image->fops->verify_sig(buf, buf_len);
}
However, image->fops is set by arch_kexec_kernel_image_probe() via
kexec_image_probe_default(), which now runs after kimage_validate_signature().
Since the kimage struct is zero-initialized by do_kimage_alloc_init(),
image->fops will be NULL when kexec_image_verify_sig() is called.
With this ordering, kexec_image_verify_sig() will always return -EKEYREJECTED.
When CONFIG_KEXEC_SIG_FORCE is enabled (sig_enforce = true), this causes
kimage_validate_signature() to return the error and kexec_file_load() fails.
Is this intentional, or should arch_kexec_kernel_image_probe() remain before
the signature validation call?
> +
> + /* Call arch image probe handlers */
> + ret = arch_kexec_kernel_image_probe(image, image->kernel_buf,
> + image->kernel_buf_len);
> + if (ret)
> + goto out;
> +
[ ... ]
---
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/21147860407
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCHv6 02/13] kexec_file: Move signature validation ahead
2026-01-19 3:24 ` [PATCHv6 02/13] kexec_file: Move signature validation ahead Pingfan Liu
2026-01-19 18:45 ` bot+bpf-ci
@ 2026-02-26 13:37 ` Philipp Rudo
2026-02-27 2:33 ` Pingfan Liu
1 sibling, 1 reply; 30+ messages in thread
From: Philipp Rudo @ 2026-02-26 13:37 UTC (permalink / raw)
To: Pingfan Liu
Cc: kexec, 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, Viktor Malik, Jan Hendrik Farr,
Baoquan He, Dave Young, Andrew Morton, bpf, systemd-devel,
linux-kernel
Hi Pingfan,
On Mon, 19 Jan 2026 11:24:13 +0800
Pingfan Liu <piliu@redhat.com> wrote:
> Move the signature validation at the head of the function, so the image
> can be unfold and handled later.
besides what the bpf-bot already mentioned.
What do you want to achieve by moving signature verification in front
of probing the image? Do you want to avoid calling
arch_kexec_kernel_image_probe twice? It would be great if you could add
the reasoning why the change is needed to the commit message in the
future. Not only to make review easier but also to document it for the
future.
Thanks
Philipp
> 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 | 13 +++++++------
> 1 file changed, 7 insertions(+), 6 deletions(-)
>
> diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
> index eb62a97942428..0222d17072d40 100644
> --- a/kernel/kexec_file.c
> +++ b/kernel/kexec_file.c
> @@ -231,18 +231,19 @@ 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 */
> - 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;
> #endif
> +
> + /* Call arch image probe handlers */
> + ret = arch_kexec_kernel_image_probe(image, image->kernel_buf,
> + image->kernel_buf_len);
> + if (ret)
> + goto out;
> +
> /* It is possible that there no initramfs is being loaded */
> if (!(flags & KEXEC_FILE_NO_INITRAMFS)) {
> ret = kernel_read_file_from_fd(initrd_fd, 0, &image->initrd_buf,
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCHv6 02/13] kexec_file: Move signature validation ahead
2026-02-26 13:37 ` Philipp Rudo
@ 2026-02-27 2:33 ` Pingfan Liu
0 siblings, 0 replies; 30+ messages in thread
From: Pingfan Liu @ 2026-02-27 2:33 UTC (permalink / raw)
To: Philipp Rudo
Cc: kexec, 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, Viktor Malik, Jan Hendrik Farr,
Baoquan He, Dave Young, Andrew Morton, bpf, systemd-devel,
linux-kernel
On Thu, Feb 26, 2026 at 9:37 PM Philipp Rudo <prudo@redhat.com> wrote:
>
> Hi Pingfan,
>
> On Mon, 19 Jan 2026 11:24:13 +0800
> Pingfan Liu <piliu@redhat.com> wrote:
>
> > Move the signature validation at the head of the function, so the image
> > can be unfold and handled later.
>
> besides what the bpf-bot already mentioned.
>
> What do you want to achieve by moving signature verification in front
> of probing the image? Do you want to avoid calling
> arch_kexec_kernel_image_probe twice? It would be great if you could add
> the reasoning why the change is needed to the commit message in the
> future. Not only to make review easier but also to document it for the
> future.
>
Here I made a mistake. I think the UKI case and the convention case
should be handled separately. For the UKI case, signature verification
should be applied to the UKI envelope but skipped for the unpacked
kernel image since it has no signature.
Thanks,
Pingfan
> Thanks
> Philipp
>
> > 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 | 13 +++++++------
> > 1 file changed, 7 insertions(+), 6 deletions(-)
> >
> > diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
> > index eb62a97942428..0222d17072d40 100644
> > --- a/kernel/kexec_file.c
> > +++ b/kernel/kexec_file.c
> > @@ -231,18 +231,19 @@ 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 */
> > - 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;
> > #endif
> > +
> > + /* Call arch image probe handlers */
> > + ret = arch_kexec_kernel_image_probe(image, image->kernel_buf,
> > + image->kernel_buf_len);
> > + if (ret)
> > + goto out;
> > +
> > /* It is possible that there no initramfs is being loaded */
> > if (!(flags & KEXEC_FILE_NO_INITRAMFS)) {
> > ret = kernel_read_file_from_fd(initrd_fd, 0, &image->initrd_buf,
>
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCHv6 03/13] kexec_file: Introduce routines to parse PE file
2026-01-19 3:24 [PATCHv6 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
2026-01-19 3:24 ` [PATCHv6 01/13] bpf: Introduce kfuncs to parser buffer content Pingfan Liu
2026-01-19 3:24 ` [PATCHv6 02/13] kexec_file: Move signature validation ahead Pingfan Liu
@ 2026-01-19 3:24 ` Pingfan Liu
2026-01-19 18:45 ` bot+bpf-ci
2026-01-19 3:24 ` [PATCHv6 04/13] kexec_file: Use bpf-prog to decompose image Pingfan Liu
` (10 subsequent siblings)
13 siblings, 1 reply; 30+ messages in thread
From: Pingfan Liu @ 2026-01-19 3:24 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 incoming patches need to handle UEFI format image, which is in PE
format. Hence introducing some routines here.
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/Makefile | 1 +
kernel/kexec_internal.h | 3 ++
kernel/kexec_uefi_app.c | 81 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 85 insertions(+)
create mode 100644 kernel/kexec_uefi_app.c
diff --git a/kernel/Makefile b/kernel/Makefile
index e83669841b8cc..f9e85c4a0622b 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -83,6 +83,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_uefi_app.o
obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o
obj-$(CONFIG_COMPAT) += compat.o
obj-$(CONFIG_CGROUPS) += cgroup/
diff --git a/kernel/kexec_internal.h b/kernel/kexec_internal.h
index 228bb88c018bc..8e5e5c1237732 100644
--- a/kernel/kexec_internal.h
+++ b/kernel/kexec_internal.h
@@ -36,6 +36,9 @@ static inline void kexec_unlock(void)
void kimage_file_post_load_cleanup(struct kimage *image);
extern char kexec_purgatory[];
extern size_t kexec_purgatory_size;
+extern bool pe_has_bpf_section(const char *file_buf, unsigned long pe_sz);
+extern int pe_get_section(const char *file_buf, const char *sect_name,
+ char **sect_start, unsigned long *sect_sz);
#else /* CONFIG_KEXEC_FILE */
static inline void kimage_file_post_load_cleanup(struct kimage *image) { }
#endif /* CONFIG_KEXEC_FILE */
diff --git a/kernel/kexec_uefi_app.c b/kernel/kexec_uefi_app.c
new file mode 100644
index 0000000000000..dbe6d76d47ffa
--- /dev/null
+++ b/kernel/kexec_uefi_app.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * UEFI appilication file helpers
+ *
+ * Copyright (C) 2025, 2026 Red Hat, Inc
+ */
+
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/pe.h>
+#include <linux/string.h>
+#include "kexec_internal.h"
+
+/*
+ * The UEFI Terse Executable (TE) image has MZ header.
+ */
+static bool is_valid_pe(const char *kernel_buf, unsigned long kernel_len)
+{
+ struct mz_hdr *mz;
+ struct pe_hdr *pe;
+
+ if (!kernel_buf)
+ return false;
+ mz = (struct mz_hdr *)kernel_buf;
+ if (mz->magic != IMAGE_DOS_SIGNATURE)
+ return false;
+ pe = (struct pe_hdr *)(kernel_buf + mz->peaddr);
+ if (pe->magic != IMAGE_NT_SIGNATURE)
+ return false;
+ if (pe->opt_hdr_size == 0) {
+ pr_err("optional header is missing\n");
+ return false;
+ }
+ return true;
+}
+
+bool pe_has_bpf_section(const char *file_buf, unsigned long pe_sz)
+{
+ char *sect_start = NULL;
+ unsigned long sect_sz = 0;
+ int ret;
+
+ if (!is_valid_pe(file_buf, pe_sz))
+ return false;
+ ret = pe_get_section(file_buf, ".bpf", §_start, §_sz);
+ if (ret < 0)
+ return false;
+ return true;
+}
+
+int pe_get_section(const char *file_buf, const char *sect_name,
+ char **sect_start, unsigned long *sect_sz)
+{
+ struct pe_hdr *pe_hdr;
+ struct pe32plus_opt_hdr *opt_hdr;
+ struct section_header *sect_hdr;
+ int section_nr, i;
+ struct mz_hdr *mz = (struct mz_hdr *)file_buf;
+
+ *sect_start = NULL;
+ *sect_sz = 0;
+ pe_hdr = (struct pe_hdr *)(file_buf + mz->peaddr);
+ section_nr = pe_hdr->sections;
+ opt_hdr = (struct pe32plus_opt_hdr *)(file_buf + mz->peaddr +
+ sizeof(struct pe_hdr));
+ sect_hdr = (struct section_header *)((char *)opt_hdr +
+ pe_hdr->opt_hdr_size);
+
+ for (i = 0; i < section_nr; i++) {
+ if (strcmp(sect_hdr->name, sect_name) == 0) {
+ *sect_start = (char *)file_buf + sect_hdr->data_addr;
+ *sect_sz = sect_hdr->raw_data_size;
+ return 0;
+ }
+ sect_hdr++;
+ }
+
+ return -1;
+}
--
2.49.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCHv6 03/13] kexec_file: Introduce routines to parse PE file
2026-01-19 3:24 ` [PATCHv6 03/13] kexec_file: Introduce routines to parse PE file Pingfan Liu
@ 2026-01-19 18:45 ` bot+bpf-ci
0 siblings, 0 replies; 30+ messages in thread
From: bot+bpf-ci @ 2026-01-19 18:45 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: 2447 bytes --]
> diff --git a/kernel/kexec_uefi_app.c b/kernel/kexec_uefi_app.c
> new file mode 100644
> index 000000000000..dbe6d76d47ff
> --- /dev/null
> +++ b/kernel/kexec_uefi_app.c
[ ... ]
> +static bool is_valid_pe(const char *kernel_buf, unsigned long kernel_len)
> +{
> + struct mz_hdr *mz;
> + struct pe_hdr *pe;
> +
> + if (!kernel_buf)
> + return false;
> + mz = (struct mz_hdr *)kernel_buf;
> + if (mz->magic != IMAGE_DOS_SIGNATURE)
> + return false;
> + pe = (struct pe_hdr *)(kernel_buf + mz->peaddr);
The kernel_len parameter is declared but never used in this function.
Should this validate that mz->peaddr is within bounds of kernel_len before
accessing pe->magic? If mz->peaddr is larger than the buffer size, this
would read out of bounds.
> + if (pe->magic != IMAGE_NT_SIGNATURE)
> + return false;
> + if (pe->opt_hdr_size == 0) {
> + pr_err("optional header is missing\n");
> + return false;
> + }
> + return true;
> +}
[ ... ]
> +int pe_get_section(const char *file_buf, const char *sect_name,
> + char **sect_start, unsigned long *sect_sz)
> +{
> + struct pe_hdr *pe_hdr;
> + struct pe32plus_opt_hdr *opt_hdr;
> + struct section_header *sect_hdr;
> + int section_nr, i;
> + struct mz_hdr *mz = (struct mz_hdr *)file_buf;
> +
> + *sect_start = NULL;
> + *sect_sz = 0;
> + pe_hdr = (struct pe_hdr *)(file_buf + mz->peaddr);
> + section_nr = pe_hdr->sections;
> + opt_hdr = (struct pe32plus_opt_hdr *)(file_buf + mz->peaddr +
> + sizeof(struct pe_hdr));
> + sect_hdr = (struct section_header *)((char *)opt_hdr +
> + pe_hdr->opt_hdr_size);
> +
> + for (i = 0; i < section_nr; i++) {
pe_get_section() has no buffer size parameter. The values of mz->peaddr,
pe_hdr->sections, and pe_hdr->opt_hdr_size are all read from the PE file
content. If the file is malformed or crafted, section_nr could be set to an
arbitrarily large value, causing sect_hdr to iterate past the end of
file_buf. Is there a way to validate these offsets against the actual
buffer size?
> + if (strcmp(sect_hdr->name, sect_name) == 0) {
> + *sect_start = (char *)file_buf + sect_hdr->data_addr;
> + *sect_sz = sect_hdr->raw_data_size;
> + return 0;
> + }
> + sect_hdr++;
> + }
> +
> + return -1;
> +}
---
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/21147860407
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCHv6 04/13] kexec_file: Use bpf-prog to decompose image
2026-01-19 3:24 [PATCHv6 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
` (2 preceding siblings ...)
2026-01-19 3:24 ` [PATCHv6 03/13] kexec_file: Introduce routines to parse PE file Pingfan Liu
@ 2026-01-19 3:24 ` Pingfan Liu
2026-01-19 18:45 ` bot+bpf-ci
2026-02-26 13:37 ` Philipp Rudo
2026-01-19 3:24 ` [PATCHv6 05/13] lib/decompress: Keep decompressor when CONFIG_KEEP_DECOMPRESSOR Pingfan Liu
` (9 subsequent siblings)
13 siblings, 2 replies; 30+ messages in thread
From: Pingfan Liu @ 2026-01-19 3:24 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 | 2 +-
kernel/kexec_bpf_loader.c | 161 ++++++++++++++++++++++++++++++++++++++
kernel/kexec_file.c | 9 ++-
kernel/kexec_internal.h | 1 +
5 files changed, 179 insertions(+), 2 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 f9e85c4a0622b..05177a867690d 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -83,7 +83,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_uefi_app.o
+obj-$(CONFIG_KEXEC_BPF) += kexec_bpf_loader.o kexec_uefi_app.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..dc59e1389da94
--- /dev/null
+++ b/kernel/kexec_bpf_loader.c
@@ -0,0 +1,161 @@
+// 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/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 0;
+}
+
+static void disarm_bpf_prog(void)
+{
+}
+
+struct kexec_context {
+ bool kdump;
+ char *kernel;
+ int kernel_sz;
+ char *initrd;
+ int initrd_sz;
+ char *cmdline;
+ int cmdline_sz;
+};
+
+void kexec_image_parser_anchor(struct kexec_context *context,
+ unsigned long parser_id);
+
+/*
+ * optimize("O0") prevents inline, compiler constant propagation
+ *
+ * Let bpf be the program context pointer so that it will not be spilled into
+ * stack.
+ */
+__attribute__((used, optimize("O0"))) void kexec_image_parser_anchor(
+ struct kexec_context *context,
+ unsigned long parser_id)
+{
+ /*
+ * To prevent linker from Identical Code Folding (ICF) with kexec_image_parser_anchor,
+ * making them have different code.
+ */
+ volatile int dummy = 0;
+
+ dummy += 1;
+}
+
+
+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;
+}
+
+/* At present, only PE format file with .bpf section is supported */
+#define file_has_bpf_section pe_has_bpf_section
+#define file_get_section pe_get_section
+
+int decompose_kexec_image(struct kimage *image, int extended_fd)
+{
+ struct kexec_context context = { 0 };
+ struct bpf_parser_context *bpf;
+ unsigned long kernel_sz, bpf_sz;
+ char *kernel_start, *bpf_start;
+ int ret = 0;
+
+ if (image->type != KEXEC_TYPE_CRASH)
+ context.kdump = false;
+ else
+ context.kdump = true;
+
+ kernel_start = image->kernel_buf;
+ kernel_sz = image->kernel_buf_len;
+
+ while (file_has_bpf_section(kernel_start, kernel_sz)) {
+
+ bpf = alloc_bpf_parser_context(kexec_buff_parser, &context);
+ if (!bpf)
+ return -ENOMEM;
+ file_get_section((const char *)kernel_start, ".bpf", &bpf_start, &bpf_sz);
+ if (!!bpf_sz) {
+ /* load and attach bpf-prog */
+ ret = arm_bpf_prog(bpf_start, bpf_sz);
+ if (ret) {
+ put_bpf_parser_context(bpf);
+ pr_err("Fail to load .bpf section\n");
+ goto err;
+ }
+ }
+ context.kernel = kernel_start;
+ context.kernel_sz = kernel_sz;
+ /* bpf-prog fentry, which handle above buffers. */
+ kexec_image_parser_anchor(&context, (unsigned long)bpf);
+
+ /*
+ * Container may be nested and should be unfold one by one.
+ * The former bpf-prog should prepare 'kernel', 'initrd',
+ * 'cmdline' for the next phase by calling kexec_buff_parser()
+ */
+ kernel_start = context.kernel;
+ kernel_sz = context.kernel_sz;
+
+ /*
+ * detach the current bpf-prog from their attachment points.
+ */
+ disarm_bpf_prog();
+ put_bpf_parser_context(bpf);
+ }
+
+ /*
+ * image's kernel_buf, initrd_buf, cmdline_buf are set. Now they should
+ * be updated to the new content.
+ */
+ image->kernel_buf = context.kernel;
+ image->kernel_buf_len = context.kernel_sz;
+ image->initrd_buf = context.initrd;
+ image->initrd_buf_len = context.initrd_sz;
+ image->cmdline_buf = context.cmdline;
+ image->cmdline_buf_len = context.cmdline_sz;
+
+ return 0;
+err:
+ vfree(context.kernel);
+ vfree(context.initrd);
+ vfree(context.cmdline);
+ return ret;
+}
+
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index 0222d17072d40..f9674bb5bd8db 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -238,7 +238,14 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
goto out;
#endif
- /* Call arch image probe handlers */
+ if (IS_ENABLED(CONFIG_KEXEC_BPF))
+ decompose_kexec_image(image, initrd_fd);
+
+ /*
+ * 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)
diff --git a/kernel/kexec_internal.h b/kernel/kexec_internal.h
index 8e5e5c1237732..ee01d0c8bb377 100644
--- a/kernel/kexec_internal.h
+++ b/kernel/kexec_internal.h
@@ -39,6 +39,7 @@ extern size_t kexec_purgatory_size;
extern bool pe_has_bpf_section(const char *file_buf, unsigned long pe_sz);
extern int pe_get_section(const char *file_buf, const char *sect_name,
char **sect_start, unsigned long *sect_sz);
+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] 30+ messages in thread* Re: [PATCHv6 04/13] kexec_file: Use bpf-prog to decompose image
2026-01-19 3:24 ` [PATCHv6 04/13] kexec_file: Use bpf-prog to decompose image Pingfan Liu
@ 2026-01-19 18:45 ` bot+bpf-ci
2026-02-26 13:37 ` Philipp Rudo
1 sibling, 0 replies; 30+ messages in thread
From: bot+bpf-ci @ 2026-01-19 18:45 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: 3973 bytes --]
> diff --git a/kernel/kexec_bpf_loader.c b/kernel/kexec_bpf_loader.c
> new file mode 100644
> index 000000000000..dc59e1389da9
> --- /dev/null
> +++ b/kernel/kexec_bpf_loader.c
[ ... ]
> +struct kexec_context {
> + bool kdump;
> + char *kernel;
> + int kernel_sz;
> + char *initrd;
> + int initrd_sz;
> + char *cmdline;
> + int cmdline_sz;
> +};
The *_sz fields here are int, but image->kernel_buf_len and related fields
are unsigned long. Could this cause truncation issues for buffers larger
than INT_MAX?
[ ... ]
> +int decompose_kexec_image(struct kimage *image, int extended_fd)
> +{
> + struct kexec_context context = { 0 };
> + struct bpf_parser_context *bpf;
> + unsigned long kernel_sz, bpf_sz;
> + char *kernel_start, *bpf_start;
> + int ret = 0;
> +
> + if (image->type != KEXEC_TYPE_CRASH)
> + context.kdump = false;
> + else
> + context.kdump = true;
> +
> + kernel_start = image->kernel_buf;
> + kernel_sz = image->kernel_buf_len;
> +
> + while (file_has_bpf_section(kernel_start, kernel_sz)) {
With the placeholder arm_bpf_prog/disarm_bpf_prog functions that do nothing,
if an image has a .bpf section, won't this loop iterate forever? The BPF
program is supposed to modify context.kernel to point to the inner content,
but without a real implementation, context.kernel stays unchanged after
kexec_image_parser_anchor() returns.
Note: commit 59dbdff4e55b ("kexec_file: Integrate bpf light skeleton to load
image with bpf-prog") later in the series fills in these placeholders.
> + bpf = alloc_bpf_parser_context(kexec_buff_parser, &context);
> + if (!bpf)
> + return -ENOMEM;
[ ... ]
> + kernel_start = context.kernel;
> + kernel_sz = context.kernel_sz;
> +
> + /*
> + * detach the current bpf-prog from their attachment points.
> + */
> + disarm_bpf_prog();
> + put_bpf_parser_context(bpf);
> + }
> +
> + /*
> + * image's kernel_buf, initrd_buf, cmdline_buf are set. Now they should
> + * be updated to the new content.
> + */
> + image->kernel_buf = context.kernel;
> + image->kernel_buf_len = context.kernel_sz;
> + image->initrd_buf = context.initrd;
> + image->initrd_buf_len = context.initrd_sz;
> + image->cmdline_buf = context.cmdline;
> + image->cmdline_buf_len = context.cmdline_sz;
When the kernel image has no .bpf section, the while loop never executes.
In that case, context.kernel remains NULL (from the zero initializer), and
these lines set image->kernel_buf = NULL, image->kernel_buf_len = 0.
This appears to leak the original image->kernel_buf that was allocated by
kernel_read_file_from_fd() in kimage_file_prepare_segments(). The subsequent
arch_kexec_kernel_image_probe() call then receives NULL instead of the valid
kernel buffer.
Should there be a check to preserve the original buffers when no
decomposition occurred?
> +
> + return 0;
[ ... ]
> diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
> index 0222d17072d4..f9674bb5bd8d 100644
> --- a/kernel/kexec_file.c
> +++ b/kernel/kexec_file.c
> @@ -238,7 +238,14 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
> goto out;
> #endif
>
> + if (IS_ENABLED(CONFIG_KEXEC_BPF))
> + decompose_kexec_image(image, initrd_fd);
The return value of decompose_kexec_image() is ignored here. If it returns
an error (e.g., -ENOMEM from alloc_bpf_parser_context()), the function
continues with potentially corrupted state instead of propagating the error.
Should this check the return value and goto out on failure?
> +
> + /*
> + * 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);
---
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/21147860407
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCHv6 04/13] kexec_file: Use bpf-prog to decompose image
2026-01-19 3:24 ` [PATCHv6 04/13] kexec_file: Use bpf-prog to decompose image Pingfan Liu
2026-01-19 18:45 ` bot+bpf-ci
@ 2026-02-26 13:37 ` Philipp Rudo
2026-02-27 2:40 ` Pingfan Liu
1 sibling, 1 reply; 30+ messages in thread
From: Philipp Rudo @ 2026-02-26 13:37 UTC (permalink / raw)
To: Pingfan Liu
Cc: kexec, 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, Viktor Malik, Jan Hendrik Farr,
Baoquan He, Dave Young, Andrew Morton, bpf, systemd-devel,
linux-kernel
Hi Pingfan,
On Mon, 19 Jan 2026 11:24:15 +0800
Pingfan Liu <piliu@redhat.com> wrote:
[...]
> diff --git a/kernel/kexec_bpf_loader.c b/kernel/kexec_bpf_loader.c
> new file mode 100644
> index 0000000000000..dc59e1389da94
> --- /dev/null
> +++ b/kernel/kexec_bpf_loader.c
> @@ -0,0 +1,161 @@
> +// 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/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 0;
> +}
> +
> +static void disarm_bpf_prog(void)
> +{
> +}
> +
> +struct kexec_context {
> + bool kdump;
> + char *kernel;
> + int kernel_sz;
> + char *initrd;
> + int initrd_sz;
> + char *cmdline;
> + int cmdline_sz;
> +};
> +
> +void kexec_image_parser_anchor(struct kexec_context *context,
> + unsigned long parser_id);
> +
> +/*
> + * optimize("O0") prevents inline, compiler constant propagation
> + *
> + * Let bpf be the program context pointer so that it will not be spilled into
> + * stack.
> + */
> +__attribute__((used, optimize("O0"))) void kexec_image_parser_anchor(
> + struct kexec_context *context,
> + unsigned long parser_id)
> +{
> + /*
> + * To prevent linker from Identical Code Folding (ICF) with kexec_image_parser_anchor,
> + * making them have different code.
> + */
> + volatile int dummy = 0;
> +
> + dummy += 1;
> +}
> +
> +
> +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;
> +}
> +
> +/* At present, only PE format file with .bpf section is supported */
> +#define file_has_bpf_section pe_has_bpf_section
> +#define file_get_section pe_get_section
> +
> +int decompose_kexec_image(struct kimage *image, int extended_fd)
> +{
> + struct kexec_context context = { 0 };
> + struct bpf_parser_context *bpf;
> + unsigned long kernel_sz, bpf_sz;
> + char *kernel_start, *bpf_start;
> + int ret = 0;
> +
> + if (image->type != KEXEC_TYPE_CRASH)
> + context.kdump = false;
> + else
> + context.kdump = true;
> +
> + kernel_start = image->kernel_buf;
> + kernel_sz = image->kernel_buf_len;
> +
> + while (file_has_bpf_section(kernel_start, kernel_sz)) {
> +
> + bpf = alloc_bpf_parser_context(kexec_buff_parser, &context);
> + if (!bpf)
> + return -ENOMEM;
> + file_get_section((const char *)kernel_start, ".bpf", &bpf_start, &bpf_sz);
> + if (!!bpf_sz) {
> + /* load and attach bpf-prog */
> + ret = arm_bpf_prog(bpf_start, bpf_sz);
> + if (ret) {
> + put_bpf_parser_context(bpf);
> + pr_err("Fail to load .bpf section\n");
> + goto err;
> + }
> + }
I'm not sure this works as intended. In case a .bpf section exists but
bpf_sz is 0, the function will skip arming the bpf-prog but still
continue. That doesn't look right to me. IIUC a zero size bpf-prog
should be an error. Or am I missing something?
Thanks
Philipp
> + context.kernel = kernel_start;
> + context.kernel_sz = kernel_sz;
> + /* bpf-prog fentry, which handle above buffers. */
> + kexec_image_parser_anchor(&context, (unsigned long)bpf);
> +
> + /*
> + * Container may be nested and should be unfold one by one.
> + * The former bpf-prog should prepare 'kernel', 'initrd',
> + * 'cmdline' for the next phase by calling kexec_buff_parser()
> + */
> + kernel_start = context.kernel;
> + kernel_sz = context.kernel_sz;
> +
> + /*
> + * detach the current bpf-prog from their attachment points.
> + */
> + disarm_bpf_prog();
> + put_bpf_parser_context(bpf);
> + }
> +
> + /*
> + * image's kernel_buf, initrd_buf, cmdline_buf are set. Now they should
> + * be updated to the new content.
> + */
> + image->kernel_buf = context.kernel;
> + image->kernel_buf_len = context.kernel_sz;
> + image->initrd_buf = context.initrd;
> + image->initrd_buf_len = context.initrd_sz;
> + image->cmdline_buf = context.cmdline;
> + image->cmdline_buf_len = context.cmdline_sz;
> +
> + return 0;
> +err:
> + vfree(context.kernel);
> + vfree(context.initrd);
> + vfree(context.cmdline);
> + return ret;
> +}
> +
> diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
> index 0222d17072d40..f9674bb5bd8db 100644
> --- a/kernel/kexec_file.c
> +++ b/kernel/kexec_file.c
> @@ -238,7 +238,14 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
> goto out;
> #endif
>
> - /* Call arch image probe handlers */
> + if (IS_ENABLED(CONFIG_KEXEC_BPF))
> + decompose_kexec_image(image, initrd_fd);
> +
> + /*
> + * 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)
> diff --git a/kernel/kexec_internal.h b/kernel/kexec_internal.h
> index 8e5e5c1237732..ee01d0c8bb377 100644
> --- a/kernel/kexec_internal.h
> +++ b/kernel/kexec_internal.h
> @@ -39,6 +39,7 @@ extern size_t kexec_purgatory_size;
> extern bool pe_has_bpf_section(const char *file_buf, unsigned long pe_sz);
> extern int pe_get_section(const char *file_buf, const char *sect_name,
> char **sect_start, unsigned long *sect_sz);
> +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 */
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCHv6 04/13] kexec_file: Use bpf-prog to decompose image
2026-02-26 13:37 ` Philipp Rudo
@ 2026-02-27 2:40 ` Pingfan Liu
0 siblings, 0 replies; 30+ messages in thread
From: Pingfan Liu @ 2026-02-27 2:40 UTC (permalink / raw)
To: Philipp Rudo
Cc: kexec, 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, Viktor Malik, Jan Hendrik Farr,
Baoquan He, Dave Young, Andrew Morton, bpf, systemd-devel,
linux-kernel
On Thu, Feb 26, 2026 at 9:37 PM Philipp Rudo <prudo@redhat.com> wrote:
>
> Hi Pingfan,
>
> On Mon, 19 Jan 2026 11:24:15 +0800
> Pingfan Liu <piliu@redhat.com> wrote:
>
> [...]
>
> > diff --git a/kernel/kexec_bpf_loader.c b/kernel/kexec_bpf_loader.c
> > new file mode 100644
> > index 0000000000000..dc59e1389da94
> > --- /dev/null
> > +++ b/kernel/kexec_bpf_loader.c
> > @@ -0,0 +1,161 @@
> > +// 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/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 0;
> > +}
> > +
> > +static void disarm_bpf_prog(void)
> > +{
> > +}
> > +
> > +struct kexec_context {
> > + bool kdump;
> > + char *kernel;
> > + int kernel_sz;
> > + char *initrd;
> > + int initrd_sz;
> > + char *cmdline;
> > + int cmdline_sz;
> > +};
> > +
> > +void kexec_image_parser_anchor(struct kexec_context *context,
> > + unsigned long parser_id);
> > +
> > +/*
> > + * optimize("O0") prevents inline, compiler constant propagation
> > + *
> > + * Let bpf be the program context pointer so that it will not be spilled into
> > + * stack.
> > + */
> > +__attribute__((used, optimize("O0"))) void kexec_image_parser_anchor(
> > + struct kexec_context *context,
> > + unsigned long parser_id)
> > +{
> > + /*
> > + * To prevent linker from Identical Code Folding (ICF) with kexec_image_parser_anchor,
> > + * making them have different code.
> > + */
> > + volatile int dummy = 0;
> > +
> > + dummy += 1;
> > +}
> > +
> > +
> > +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;
> > +}
> > +
> > +/* At present, only PE format file with .bpf section is supported */
> > +#define file_has_bpf_section pe_has_bpf_section
> > +#define file_get_section pe_get_section
> > +
> > +int decompose_kexec_image(struct kimage *image, int extended_fd)
> > +{
> > + struct kexec_context context = { 0 };
> > + struct bpf_parser_context *bpf;
> > + unsigned long kernel_sz, bpf_sz;
> > + char *kernel_start, *bpf_start;
> > + int ret = 0;
> > +
> > + if (image->type != KEXEC_TYPE_CRASH)
> > + context.kdump = false;
> > + else
> > + context.kdump = true;
> > +
> > + kernel_start = image->kernel_buf;
> > + kernel_sz = image->kernel_buf_len;
> > +
> > + while (file_has_bpf_section(kernel_start, kernel_sz)) {
> > +
> > + bpf = alloc_bpf_parser_context(kexec_buff_parser, &context);
> > + if (!bpf)
> > + return -ENOMEM;
> > + file_get_section((const char *)kernel_start, ".bpf", &bpf_start, &bpf_sz);
> > + if (!!bpf_sz) {
> > + /* load and attach bpf-prog */
> > + ret = arm_bpf_prog(bpf_start, bpf_sz);
> > + if (ret) {
> > + put_bpf_parser_context(bpf);
> > + pr_err("Fail to load .bpf section\n");
> > + goto err;
> > + }
> > + }
>
> I'm not sure this works as intended. In case a .bpf section exists but
> bpf_sz is 0, the function will skip arming the bpf-prog but still
> continue. That doesn't look right to me. IIUC a zero size bpf-prog
> should be an error. Or am I missing something?
>
Yes, you are right. It's better to treat a zero bpf_sz as an invalid format.
Thanks,
Pingfan
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCHv6 05/13] lib/decompress: Keep decompressor when CONFIG_KEEP_DECOMPRESSOR
2026-01-19 3:24 [PATCHv6 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
` (3 preceding siblings ...)
2026-01-19 3:24 ` [PATCHv6 04/13] kexec_file: Use bpf-prog to decompose image Pingfan Liu
@ 2026-01-19 3:24 ` Pingfan Liu
2026-01-19 3:24 ` [PATCHv6 06/13] kexec_file: Implement decompress method for parser Pingfan Liu
` (8 subsequent siblings)
13 siblings, 0 replies; 30+ messages in thread
From: Pingfan Liu @ 2026-01-19 3:24 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 2923924bea78c..4424ae14dcf12 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] 30+ messages in thread* [PATCHv6 06/13] kexec_file: Implement decompress method for parser
2026-01-19 3:24 [PATCHv6 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
` (4 preceding siblings ...)
2026-01-19 3:24 ` [PATCHv6 05/13] lib/decompress: Keep decompressor when CONFIG_KEEP_DECOMPRESSOR Pingfan Liu
@ 2026-01-19 3:24 ` Pingfan Liu
2026-01-19 18:45 ` bot+bpf-ci
2026-01-19 3:24 ` [PATCHv6 07/13] kexec_file: Implement copy " Pingfan Liu
` (7 subsequent siblings)
13 siblings, 1 reply; 30+ messages in thread
From: Pingfan Liu @ 2026-01-19 3:24 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 | 203 ++++++++++++++++++++++++++++++++++++++
2 files changed, 204 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 dc59e1389da94..bd6a47fc53ed3 100644
--- a/kernel/kexec_bpf_loader.c
+++ b/kernel/kexec_bpf_loader.c
@@ -20,6 +20,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 */
@@ -80,8 +81,210 @@ static int __init kexec_bpf_prog_run_init(void)
}
late_initcall(kexec_bpf_prog_run_init);
+#define KEXEC_BPF_CMD_DECOMPRESS 0x1
+
+#define KEXEC_BPF_SUBCMD_KERNEL 0x1
+#define KEXEC_BPF_SUBCMD_INITRD 0x2
+#define KEXEC_BPF_SUBCMD_CMDLINE 0x3
+
+struct cmd_hdr {
+ uint16_t cmd;
+ uint16_t subcmd;
+ uint32_t payload_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 int *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]);
+ }
+
+ memcpy(cur_dst, a->chunk_base_addr[i], last_chunk_sz);
+ vfree(a->chunk_base_addr[i]);
+ *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)
+{
+ 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, int *out_sz, struct kexec_context *ctx)
+{
+ struct decompress_mem_allocator *a = &dcmpr_allocator;
+ decompress_fn decompressor;
+ const char *name;
+ int ret;
+
+ decompress_mem_allocator_init(a, CHUNK_SIZE);
+ decompressor = decompress_method(compressed_data, image_gz_sz, &name);
+ if (!decompressor) {
+ pr_err("Can not find decompress method\n");
+ return -1;
+ }
+ 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;
+ int decompressed_sz, ret;
+
+ 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_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:
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
return 0;
}
--
2.49.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCHv6 06/13] kexec_file: Implement decompress method for parser
2026-01-19 3:24 ` [PATCHv6 06/13] kexec_file: Implement decompress method for parser Pingfan Liu
@ 2026-01-19 18:45 ` bot+bpf-ci
0 siblings, 0 replies; 30+ messages in thread
From: bot+bpf-ci @ 2026-01-19 18:45 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: 2955 bytes --]
> diff --git a/kernel/kexec_bpf_loader.c b/kernel/kexec_bpf_loader.c
> index dc59e1389da9..bd6a47fc53ed 100644
> --- a/kernel/kexec_bpf_loader.c
> +++ b/kernel/kexec_bpf_loader.c
[ ... ]
> +static void decompress_mem_allocator_fini(struct decompress_mem_allocator *a)
> +{
> + vfree(a->chunk_base_addr);
> +}
Should decompress_mem_allocator_fini() also free the individual chunks stored
in chunk_base_addr[0..next_idx-1]? On the success path, merge_decompressed_data()
frees them, but on error paths (decompressor failure or merge failure), the
chunks appear to be leaked since only the array pointer is freed here.
[ ... ]
> +static int parser_cmd_decompress(char *compressed_data, int image_gz_sz,
> + char **out_buf, int *out_sz, struct kexec_context *ctx)
> +{
> + struct decompress_mem_allocator *a = &dcmpr_allocator;
> + decompress_fn decompressor;
> + const char *name;
> + int ret;
> +
> + decompress_mem_allocator_init(a, CHUNK_SIZE);
Is the return value from decompress_mem_allocator_init() intentionally
ignored? If vmalloc fails inside that function, chunk_base_addr and the
other fields remain uninitialized, and subsequent code will use garbage
values when accessing the allocator.
> + decompressor = decompress_method(compressed_data, image_gz_sz, &name);
> + if (!decompressor) {
> + pr_err("Can not find decompress method\n");
> + return -1;
> + }
When decompress_method() returns NULL, should decompress_mem_allocator_fini()
be called before returning? The allocator was initialized above and allocated
both chunk_base_addr and the first chunk, which appear to leak on this path.
> + 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;
> +}
[ ... ]
> + switch (cmd->cmd) {
> + 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:
> + break;
When decompression succeeds but subcmd is not KEXEC_BPF_SUBCMD_KERNEL (for
example INITRD or CMDLINE), decompressed_buf is allocated by
merge_decompressed_data() but not stored or freed. Is this intentional, or
should decompressed_buf be freed in the default case?
> + }
> + }
> + break;
> + default:
> + break;
> + }
> +
> + return 0;
> +}
---
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/21147860407
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCHv6 07/13] kexec_file: Implement copy method for parser
2026-01-19 3:24 [PATCHv6 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
` (5 preceding siblings ...)
2026-01-19 3:24 ` [PATCHv6 06/13] kexec_file: Implement decompress method for parser Pingfan Liu
@ 2026-01-19 3:24 ` Pingfan Liu
2026-01-19 3:24 ` [PATCHv6 08/13] kexec_file: Introduce a bpf-prog lskel to parse PE file Pingfan Liu
` (6 subsequent siblings)
13 siblings, 0 replies; 30+ messages in thread
From: Pingfan Liu @ 2026-01-19 3:24 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 | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/kernel/kexec_bpf_loader.c b/kernel/kexec_bpf_loader.c
index bd6a47fc53ed3..5ad67672dead1 100644
--- a/kernel/kexec_bpf_loader.c
+++ b/kernel/kexec_bpf_loader.c
@@ -82,6 +82,7 @@ static int __init kexec_bpf_prog_run_init(void)
late_initcall(kexec_bpf_prog_run_init);
#define KEXEC_BPF_CMD_DECOMPRESS 0x1
+#define KEXEC_BPF_CMD_COPY 0x2
#define KEXEC_BPF_SUBCMD_KERNEL 0x1
#define KEXEC_BPF_SUBCMD_INITRD 0x2
@@ -281,6 +282,32 @@ 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;
+ case KEXEC_BPF_SUBCMD_INITRD:
+ vfree(ctx->initrd);
+ ctx->initrd = p;
+ ctx->initrd_sz = cmd->payload_len;
+ break;
+ 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] 30+ messages in thread* [PATCHv6 08/13] kexec_file: Introduce a bpf-prog lskel to parse PE file
2026-01-19 3:24 [PATCHv6 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
` (6 preceding siblings ...)
2026-01-19 3:24 ` [PATCHv6 07/13] kexec_file: Implement copy " Pingfan Liu
@ 2026-01-19 3:24 ` Pingfan Liu
2026-01-19 3:24 ` [PATCHv6 09/13] kexec_file: Factor out routine to find a symbol in ELF Pingfan Liu
` (5 subsequent siblings)
13 siblings, 0 replies; 30+ messages in thread
From: Pingfan Liu @ 2026-01-19 3:24 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 | 372 +++++++++++++++++++
kernel/kexec_bpf/template.c | 68 ++++
4 files changed, 514 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..d1e863fd0ff4f
--- /dev/null
+++ b/kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h
@@ -0,0 +1,372 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+/* 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 bss;
+ struct bpf_map_desc rodata_str1_1;
+ } 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.bss.map_fd);
+ skel_closenz(skel->maps.rodata_str1_1.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\xdc\x02\0\0\xdc\x02\0\0\x85\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\x39\x01\0\0\0\0\0\x0e\x19\0\0\0\0\0\0\0\x43\x01\0\0\0\0\0\x0e\x19\0\0\0\
+\0\0\0\0\x55\x01\0\0\x01\0\0\x0f\x10\0\0\0\x1b\0\0\0\0\0\0\0\x10\0\0\0\x5a\x01\
+\0\0\x01\0\0\x0f\x10\0\0\0\x1a\0\0\0\0\0\0\0\x10\0\0\0\x60\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\x66\x01\0\0\x01\0\0\x0f\x10\
+\0\0\0\x18\0\0\0\0\0\0\0\x10\0\0\0\x6e\x01\0\0\x01\0\0\x0f\x10\0\0\0\x1c\0\0\0\
+\0\0\0\0\x10\0\0\0\x7d\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\x62\x73\x73\0\x64\x75\x6d\x6d\x79\x5f\x72\x6f\x64\x61\x74\x61\x5f\
+\x73\x74\x72\x31\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\x2e\x72\x6f\x64\x61\x74\x61\x2e\x73\x74\x72\
+\x31\x2e\x31\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\0\0\0\x79\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\x20\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\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\x64\x61\x74\x61\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\x1e\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\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\x1d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\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\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\x21\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\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\xf4\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\x98\
+\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\
+\x94\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\x88\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\x80\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\x80\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\xac\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\xa0\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\xf4\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\xe8\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\x3c\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\x30\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\x84\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\x78\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\xf0\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\xcc\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\xc0\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\x08\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\x20\x0b\0\0\x63\x01\0\0\0\0\0\0\
+\x18\x60\0\0\0\0\0\0\0\0\0\0\x18\x0b\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x28\x0b\0\
+\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x08\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\xb7\x01\0\0\x02\0\0\0\x18\x62\0\0\
+\0\0\0\0\0\0\0\0\x20\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\x40\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\x40\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\x78\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\x54\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\x48\
+\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\x90\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\
+\x14\0\0\0\x61\x20\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xa8\x0b\0\0\x63\x01\
+\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xa0\x0b\0\0\x18\x61\0\0\0\0\0\0\0\0\0\
+\0\xb0\x0b\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x90\x0b\0\0\x18\
+\x61\0\0\0\0\0\0\0\0\0\0\xb8\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\xa8\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\xa0\x78\xff\0\0\0\0\x18\
+\x61\0\0\0\0\0\0\0\0\0\0\xf8\x0b\0\0\x63\x01\0\0\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\xd4\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\xc8\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\xeb\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\x10\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\x18\0\0\0\x61\x20\0\0\
+\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x28\x0c\0\0\x63\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\x30\x0c\0\0\x7b\x01\
+\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x10\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\
+\0\x38\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\x28\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\xc7\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\
+\x78\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\x54\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\x48\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\xb7\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\x90\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\xa8\x0c\0\0\x63\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xa0\
+\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xb0\x0c\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\
+\0\0\0\0\0\0\0\0\0\x90\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xb8\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\xa8\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\x93\xfe\0\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\xc8\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\xc8\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\x86\xfe\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xd0\x0c\0\0\
+\x18\x61\0\0\0\0\0\0\0\0\0\0\x10\x0d\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\
+\0\0\0\0\0\xd8\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x08\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\x50\
+\x0d\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xf0\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\x80\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\x78\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\x18\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\x1c\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\x20\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\
+\x48\x0d\0\0\x63\x01\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x90\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\x50\xfe\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\
+\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\0\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\x70\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\x3e\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..9f17a4952ecd4
--- /dev/null
+++ b/kernel/kexec_bpf/template.c
@@ -0,0 +1,68 @@
+// 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>
+
+/* Command to kernel kexec bpf loader, which is defined on the stream */
+#define KEXEC_BPF_CMD_DECOMPRESS 0x1
+#define KEXEC_BPF_CMD_COPY 0x2
+
+#define KEXEC_BPF_SUBCMD_KERNEL 0x1
+#define KEXEC_BPF_SUBCMD_INITRD 0x2
+#define KEXEC_BPF_SUBCMD_CMDLINE 0x3
+
+/*
+ * 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] 30+ messages in thread* [PATCHv6 09/13] kexec_file: Factor out routine to find a symbol in ELF
2026-01-19 3:24 [PATCHv6 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
` (7 preceding siblings ...)
2026-01-19 3:24 ` [PATCHv6 08/13] kexec_file: Introduce a bpf-prog lskel to parse PE file Pingfan Liu
@ 2026-01-19 3:24 ` Pingfan Liu
2026-01-19 3:24 ` [PATCHv6 10/13] kexec_file: Integrate bpf light skeleton to load image with bpf-prog Pingfan Liu
` (4 subsequent siblings)
13 siblings, 0 replies; 30+ messages in thread
From: Pingfan Liu @ 2026-01-19 3:24 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 | 86 ++++++++++++++++++++++-------------------
kernel/kexec_internal.h | 1 +
2 files changed, 47 insertions(+), 40 deletions(-)
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index f9674bb5bd8db..9731019ba2a1a 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -889,6 +889,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_setup_kbuf - prepare buffer to load purgatory.
@@ -1146,49 +1191,10 @@ int kexec_load_purgatory(struct kimage *image, struct kexec_buf *kbuf)
static const Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi,
const char *name)
{
- const Elf_Shdr *sechdrs;
- const Elf_Ehdr *ehdr;
- const Elf_Sym *syms;
- const char *strtab;
- int i, k;
-
if (!pi->ehdr)
return NULL;
- ehdr = pi->ehdr;
- 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;
+ return elf_find_symbol(pi->ehdr, name);
}
void *kexec_purgatory_get_symbol_addr(struct kimage *image, const char *name)
diff --git a/kernel/kexec_internal.h b/kernel/kexec_internal.h
index ee01d0c8bb377..2e8e0fedbe2a9 100644
--- a/kernel/kexec_internal.h
+++ b/kernel/kexec_internal.h
@@ -40,6 +40,7 @@ extern bool pe_has_bpf_section(const char *file_buf, unsigned long pe_sz);
extern int pe_get_section(const char *file_buf, const char *sect_name,
char **sect_start, unsigned long *sect_sz);
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] 30+ messages in thread* [PATCHv6 10/13] kexec_file: Integrate bpf light skeleton to load image with bpf-prog
2026-01-19 3:24 [PATCHv6 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
` (8 preceding siblings ...)
2026-01-19 3:24 ` [PATCHv6 09/13] kexec_file: Factor out routine to find a symbol in ELF Pingfan Liu
@ 2026-01-19 3:24 ` Pingfan Liu
2026-01-19 18:45 ` bot+bpf-ci
2026-01-19 3:24 ` [PATCHv6 11/13] arm64/kexec: Select KEXEC_BPF to support UEFI-style kernel image Pingfan Liu
` (3 subsequent siblings)
13 siblings, 1 reply; 30+ messages in thread
From: Pingfan Liu @ 2026-01-19 3:24 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 | 254 +------------------
kernel/kexec_bpf_loader.c | 48 ++++
4 files changed, 63 insertions(+), 248 deletions(-)
diff --git a/kernel/Makefile b/kernel/Makefile
index 05177a867690d..a1c5f5fb02770 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -143,6 +143,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 d1e863fd0ff4f..db71b2150789d 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;
@@ -14,8 +16,8 @@ struct kexec_pe_parser_bpf {
struct bpf_map_desc ringbuf_4;
struct bpf_map_desc rodata;
struct bpf_map_desc data;
- struct bpf_map_desc bss;
struct bpf_map_desc rodata_str1_1;
+ struct bpf_map_desc bss;
} maps;
struct {
struct bpf_prog_desc parse_pe;
@@ -63,8 +65,8 @@ kexec_pe_parser_bpf__destroy(struct kexec_pe_parser_bpf *skel)
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.bss.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 *
@@ -87,254 +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\xdc\x02\0\0\xdc\x02\0\0\x85\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\x39\x01\0\0\0\0\0\x0e\x19\0\0\0\0\0\0\0\x43\x01\0\0\0\0\0\x0e\x19\0\0\0\
-\0\0\0\0\x55\x01\0\0\x01\0\0\x0f\x10\0\0\0\x1b\0\0\0\0\0\0\0\x10\0\0\0\x5a\x01\
-\0\0\x01\0\0\x0f\x10\0\0\0\x1a\0\0\0\0\0\0\0\x10\0\0\0\x60\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\x66\x01\0\0\x01\0\0\x0f\x10\
-\0\0\0\x18\0\0\0\0\0\0\0\x10\0\0\0\x6e\x01\0\0\x01\0\0\x0f\x10\0\0\0\x1c\0\0\0\
-\0\0\0\0\x10\0\0\0\x7d\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\x62\x73\x73\0\x64\x75\x6d\x6d\x79\x5f\x72\x6f\x64\x61\x74\x61\x5f\
-\x73\x74\x72\x31\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\x2e\x72\x6f\x64\x61\x74\x61\x2e\x73\x74\x72\
-\x31\x2e\x31\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\0\0\0\x79\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\x20\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\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\x64\x61\x74\x61\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\x1e\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\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\x1d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\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\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\x21\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\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\xf4\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\x98\
-\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\
-\x94\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\x88\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\x80\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\x80\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\xac\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\xa0\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\xf4\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\xe8\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\x3c\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\x30\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\x84\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\x78\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\xf0\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\xcc\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\xc0\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\x08\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\x20\x0b\0\0\x63\x01\0\0\0\0\0\0\
-\x18\x60\0\0\0\0\0\0\0\0\0\0\x18\x0b\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x28\x0b\0\
-\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x08\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\xb7\x01\0\0\x02\0\0\0\x18\x62\0\0\
-\0\0\0\0\0\0\0\0\x20\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\x40\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\x40\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\x78\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\x54\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\x48\
-\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\x90\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\
-\x14\0\0\0\x61\x20\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xa8\x0b\0\0\x63\x01\
-\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xa0\x0b\0\0\x18\x61\0\0\0\0\0\0\0\0\0\
-\0\xb0\x0b\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x90\x0b\0\0\x18\
-\x61\0\0\0\0\0\0\0\0\0\0\xb8\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\xa8\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\xa0\x78\xff\0\0\0\0\x18\
-\x61\0\0\0\0\0\0\0\0\0\0\xf8\x0b\0\0\x63\x01\0\0\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\xd4\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\xc8\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\xeb\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\x10\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\x18\0\0\0\x61\x20\0\0\
-\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x28\x0c\0\0\x63\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\x30\x0c\0\0\x7b\x01\
-\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x10\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\
-\0\x38\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\x28\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\xc7\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\
-\x78\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\x54\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\x48\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\xb7\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\x90\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\xa8\x0c\0\0\x63\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xa0\
-\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xb0\x0c\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\
-\0\0\0\0\0\0\0\0\0\x90\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xb8\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\xa8\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\x93\xfe\0\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\xc8\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\xc8\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\x86\xfe\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xd0\x0c\0\0\
-\x18\x61\0\0\0\0\0\0\0\0\0\0\x10\x0d\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\
-\0\0\0\0\0\xd8\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x08\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\x50\
-\x0d\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xf0\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\x80\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\x78\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\x18\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\x1c\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\x20\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\
-\x48\x0d\0\0\x63\x01\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x90\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\x50\xfe\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\
-\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\0\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\x70\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\x3e\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 5ad67672dead1..9f1c5aede42ef 100644
--- a/kernel/kexec_bpf_loader.c
+++ b/kernel/kexec_bpf_loader.c
@@ -23,14 +23,62 @@
#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;
+
+ if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
+ pr_err("Not a valid ELF file\n");
+ return NULL;
+ }
+
+ sym = elf_find_symbol(ehdr, symbol_name);
+ if (!sym)
+ return NULL;
+ shdr = (struct elf_shdr *)(elf_data + ehdr->e_shoff);
+ dst_shdr = &shdr[sym->st_shndx];
+ symbol_data = (void *)(elf_data + dst_shdr->sh_offset + sym->st_value);
+ *symbol_size = sym->st_size;
+
+ return symbol_data;
+}
+
/* Load a ELF */
static int arm_bpf_prog(char *bpf_elf, unsigned long sz)
{
+ 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)
+ 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)
+ 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;
}
struct kexec_context {
--
2.49.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCHv6 10/13] kexec_file: Integrate bpf light skeleton to load image with bpf-prog
2026-01-19 3:24 ` [PATCHv6 10/13] kexec_file: Integrate bpf light skeleton to load image with bpf-prog Pingfan Liu
@ 2026-01-19 18:45 ` bot+bpf-ci
0 siblings, 0 replies; 30+ messages in thread
From: bot+bpf-ci @ 2026-01-19 18:45 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: 2699 bytes --]
> diff --git a/kernel/kexec_bpf_loader.c b/kernel/kexec_bpf_loader.c
> index 5ad67672dead..9f1c5aede42e 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)
> +{
> + Elf_Ehdr *ehdr = (Elf_Ehdr *)elf_data;
> + Elf_Shdr *shdr, *dst_shdr;
> + const Elf_Sym *sym;
> + void *symbol_data;
> +
> + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
> + pr_err("Not a valid ELF file\n");
> + return NULL;
> + }
> +
> + sym = elf_find_symbol(ehdr, symbol_name);
> + if (!sym)
> + return NULL;
> + shdr = (struct elf_shdr *)(elf_data + ehdr->e_shoff);
> + dst_shdr = &shdr[sym->st_shndx];
> + symbol_data = (void *)(elf_data + dst_shdr->sh_offset + sym->st_value);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The elf_size parameter is declared but never used in this function. The
pointer calculations at ehdr->e_shoff, shdr[sym->st_shndx], and
dst_shdr->sh_offset + sym->st_value could all access memory beyond the
ELF buffer if the ELF data is malformed. Should there be bounds checking
against elf_size to prevent out-of-bounds reads?
> + *symbol_size = sym->st_size;
> +
> + return symbol_data;
> +}
> +
> /* Load a ELF */
> static int arm_bpf_prog(char *bpf_elf, unsigned long sz)
> {
> + 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)
> + 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;
^^^^^^^^^^^^^^^^^^
If sym->st_size is 0 for either opts_data or opts_insn symbols in the ELF,
then opts_data_sz or opts_insn_sz would be 0 after get_symbol_from_elf()
returns. Subtracting 1 from an unsigned int with value 0 would cause an
integer underflow to UINT_MAX. This large value would then be passed to
bpf_load_and_run() via the opts structure. Would it make sense to check
that opts_data_sz and opts_insn_sz are non-zero before the subtraction?
> +
> + pe_parser = kexec_pe_parser_bpf__open_and_load();
> + if (!pe_parser)
> + return -1;
> + kexec_pe_parser_bpf__attach(pe_parser);
> +
> + return 0;
> }
[ ... ]
---
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/21147860407
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCHv6 11/13] arm64/kexec: Select KEXEC_BPF to support UEFI-style kernel image
2026-01-19 3:24 [PATCHv6 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
` (9 preceding siblings ...)
2026-01-19 3:24 ` [PATCHv6 10/13] kexec_file: Integrate bpf light skeleton to load image with bpf-prog Pingfan Liu
@ 2026-01-19 3:24 ` Pingfan Liu
2026-01-19 8:23 ` kernel test robot
2026-01-19 18:45 ` bot+bpf-ci
2026-01-19 3:24 ` [PATCHv6 12/13] tools/kexec: Introduce a bpf-prog to parse zboot image format Pingfan Liu
` (2 subsequent siblings)
13 siblings, 2 replies; 30+ messages in thread
From: Pingfan Liu @ 2026-01-19 3:24 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 93173f0a09c7d..922d58abbbd67 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -1587,6 +1587,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
config ARCH_SUPPORTS_KEXEC_SIG
def_bool y
--
2.49.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCHv6 11/13] arm64/kexec: Select KEXEC_BPF to support UEFI-style kernel image
2026-01-19 3:24 ` [PATCHv6 11/13] arm64/kexec: Select KEXEC_BPF to support UEFI-style kernel image Pingfan Liu
@ 2026-01-19 8:23 ` kernel test robot
2026-01-19 18:45 ` bot+bpf-ci
1 sibling, 0 replies; 30+ messages in thread
From: kernel test robot @ 2026-01-19 8:23 UTC (permalink / raw)
To: Pingfan Liu, linux-arm-kernel
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, kexec, bpf,
systemd-devel, linux-kernel
Hi Pingfan,
kernel test robot noticed the following build warnings:
[auto build test WARNING on bpf-next/net]
[also build test WARNING on bpf-next/master bpf/master akpm-mm/mm-nonmm-unstable linus/master v6.19-rc6 next-20260116]
[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/20260119-112939
base: https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git net
patch link: https://lore.kernel.org/r/20260119032424.10781-12-piliu%40redhat.com
patch subject: [PATCHv6 11/13] arm64/kexec: Select KEXEC_BPF to support UEFI-style kernel image
config: arm64-randconfig-004-20260119 (https://download.01.org/0day-ci/archive/20260119/202601191626.CUD61tIS-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/20260119/202601191626.CUD61tIS-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/202601191626.CUD61tIS-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> kernel/kexec_bpf_loader.c:103:22: warning: unknown attribute 'optimize' ignored [-Wunknown-attributes]
103 | __attribute__((used, optimize("O0"))) void kexec_image_parser_anchor(
| ^~~~~~~~~~~~~~
1 warning generated.
Kconfig warnings: (for reference only)
WARNING: unmet direct dependencies detected for KEXEC_BPF
Depends on [n]: KEXEC_FILE [=y] && DEBUG_INFO_BTF [=y] && BPF_SYSCALL [=y] && KEEP_DECOMPRESSOR [=n]
Selected by [y]:
- ARCH_SELECTS_KEXEC_FILE [=y] && KEXEC_FILE [=y] && DEBUG_INFO_BTF [=y] && BPF_SYSCALL [=y]
vim +/optimize +103 kernel/kexec_bpf_loader.c
e683ae405ea0458 Pingfan Liu 2026-01-19 93
e683ae405ea0458 Pingfan Liu 2026-01-19 94 void kexec_image_parser_anchor(struct kexec_context *context,
e683ae405ea0458 Pingfan Liu 2026-01-19 95 unsigned long parser_id);
e683ae405ea0458 Pingfan Liu 2026-01-19 96
e683ae405ea0458 Pingfan Liu 2026-01-19 97 /*
e683ae405ea0458 Pingfan Liu 2026-01-19 98 * optimize("O0") prevents inline, compiler constant propagation
e683ae405ea0458 Pingfan Liu 2026-01-19 99 *
e683ae405ea0458 Pingfan Liu 2026-01-19 100 * Let bpf be the program context pointer so that it will not be spilled into
e683ae405ea0458 Pingfan Liu 2026-01-19 101 * stack.
e683ae405ea0458 Pingfan Liu 2026-01-19 102 */
e683ae405ea0458 Pingfan Liu 2026-01-19 @103 __attribute__((used, optimize("O0"))) void kexec_image_parser_anchor(
e683ae405ea0458 Pingfan Liu 2026-01-19 104 struct kexec_context *context,
e683ae405ea0458 Pingfan Liu 2026-01-19 105 unsigned long parser_id)
e683ae405ea0458 Pingfan Liu 2026-01-19 106 {
e683ae405ea0458 Pingfan Liu 2026-01-19 107 /*
e683ae405ea0458 Pingfan Liu 2026-01-19 108 * To prevent linker from Identical Code Folding (ICF) with kexec_image_parser_anchor,
e683ae405ea0458 Pingfan Liu 2026-01-19 109 * making them have different code.
e683ae405ea0458 Pingfan Liu 2026-01-19 110 */
e683ae405ea0458 Pingfan Liu 2026-01-19 111 volatile int dummy = 0;
e683ae405ea0458 Pingfan Liu 2026-01-19 112
e683ae405ea0458 Pingfan Liu 2026-01-19 113 dummy += 1;
e683ae405ea0458 Pingfan Liu 2026-01-19 114 }
e683ae405ea0458 Pingfan Liu 2026-01-19 115
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCHv6 11/13] arm64/kexec: Select KEXEC_BPF to support UEFI-style kernel image
2026-01-19 3:24 ` [PATCHv6 11/13] arm64/kexec: Select KEXEC_BPF to support UEFI-style kernel image Pingfan Liu
2026-01-19 8:23 ` kernel test robot
@ 2026-01-19 18:45 ` bot+bpf-ci
1 sibling, 0 replies; 30+ messages in thread
From: bot+bpf-ci @ 2026-01-19 18:45 UTC (permalink / raw)
To: piliu, linux-arm-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, linux-kernel, ast,
andrii, daniel, martin.lau, eddyz87, yonghong.song, clm,
ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 1100 bytes --]
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index 93173f0a09c7..922d58abbbd6 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -1587,6 +1587,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
^^^
Should this condition also include KEEP_DECOMPRESSOR?
In kernel/Kconfig.kexec, KEXEC_BPF has:
depends on DEBUG_INFO_BTF && BPF_SYSCALL && KEEP_DECOMPRESSOR
Since Kconfig's select statement forces a symbol's value without visiting
the dependencies, this select will enable KEXEC_BPF even when
KEEP_DECOMPRESSOR is not set, creating an inconsistent configuration.
KEEP_DECOMPRESSOR defaults to n and nothing selects it, so this appears
to be a missing condition in the select statement.
---
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/21147860407
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCHv6 12/13] tools/kexec: Introduce a bpf-prog to parse zboot image format
2026-01-19 3:24 [PATCHv6 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
` (10 preceding siblings ...)
2026-01-19 3:24 ` [PATCHv6 11/13] arm64/kexec: Select KEXEC_BPF to support UEFI-style kernel image Pingfan Liu
@ 2026-01-19 3:24 ` Pingfan Liu
2026-01-19 18:45 ` bot+bpf-ci
2026-01-19 3:24 ` [PATCHv6 13/13] tools/kexec: Add a zboot image building tool Pingfan Liu
2026-02-26 13:36 ` [PATCHv6 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Philipp Rudo
13 siblings, 1 reply; 30+ messages in thread
From: Pingfan Liu @ 2026-01-19 3:24 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 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 size of the uncompressed file 'vmlinux.bin', which is usually less
than 64MB. 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: 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
---
tools/kexec/Makefile | 83 ++++++++++++++++++++++++
tools/kexec/template.c | 68 ++++++++++++++++++++
tools/kexec/zboot_parser_bpf.c | 114 +++++++++++++++++++++++++++++++++
3 files changed, 265 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..88db6d11bde61
--- /dev/null
+++ b/tools/kexec/Makefile
@@ -0,0 +1,83 @@
+# 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 -O2 -target bpf -Wall -I $(BPFDIR) -I .
+BPFTOOL = bpftool
+
+# List of generated target files
+HEADERS = vmlinux.h bpf_helper_defs.h image_size.h
+ZBOOT_TARGETS = zboot_parser_bpf.o zboot_parser_bpf.lskel.h bytecode.c bytecode.o
+
+
+# Targets
+zboot: $(HEADERS) $(ZBOOT_TARGETS)
+
+# 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
+
+image_size.h: $(KERNEL_IMAGE)
+ @{ \
+ if [ ! -f "$(KERNEL_IMAGE)" ]; then \
+ echo "Error: File '$(KERNEL_IMAGE)' does not exist"; \
+ exit 1; \
+ fi; \
+ FILE_SIZE=$$(stat -c '%s' "$(KERNEL_IMAGE)" 2>/dev/null); \
+ POWER=4096; \
+ while [ $$POWER -le $$FILE_SIZE ]; do \
+ POWER=$$((POWER * 2)); \
+ done; \
+ RINGBUF_SIZE=$$POWER; \
+ echo "#define IMAGE_SIZE_POWER2_ALIGN $$RINGBUF_SIZE" > $@; \
+ echo "#define IMAGE_SIZE $$FILE_SIZE" >> $@; \
+ }
+
+
+# Rule to generate zboot_parser_bpf.o, depends on vmlinux.h
+zboot_parser_bpf.o: zboot_parser_bpf.c vmlinux.h bpf_helper_defs.h
+ @$(CC) $(BPF_PROG_CFLAGS) -c zboot_parser_bpf.c -o zboot_parser_bpf.o
+
+# Generate zboot_parser_bpf.lskel.h using bpftool
+zboot_parser_bpf.lskel.h: zboot_parser_bpf.o
+ @$(BPFTOOL) gen skeleton -L zboot_parser_bpf.o > zboot_parser_bpf.lskel.h
+
+# Then, extract the opts_data[] and opts_insn[] arrays and remove 'static'
+# keywords to avoid being optimized away.
+bytecode.c: zboot_parser_bpf.lskel.h
+ @sed -n '/static const char opts_data\[\]/,/;/p' zboot_parser_bpf.lskel.h | sed 's/static const/const/' > $@
+ @sed -n '/static const char opts_insn\[\]/,/;/p' zboot_parser_bpf.lskel.h | sed 's/static const/const/' >> $@
+
+bytecode.o: bytecode.c
+ @$(CC) -c $< -o $@
+
+# Clean up generated files
+clean:
+ @rm -f $(HEADERS) $(ZBOOT_TARGETS)
+
+.PHONY: all clean
diff --git a/tools/kexec/template.c b/tools/kexec/template.c
new file mode 100644
index 0000000000000..9f17a4952ecd4
--- /dev/null
+++ b/tools/kexec/template.c
@@ -0,0 +1,68 @@
+// 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>
+
+/* Command to kernel kexec bpf loader, which is defined on the stream */
+#define KEXEC_BPF_CMD_DECOMPRESS 0x1
+#define KEXEC_BPF_CMD_COPY 0x2
+
+#define KEXEC_BPF_SUBCMD_KERNEL 0x1
+#define KEXEC_BPF_SUBCMD_INITRD 0x2
+#define KEXEC_BPF_SUBCMD_CMDLINE 0x3
+
+/*
+ * 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..54c4b762b3324
--- /dev/null
+++ b/tools/kexec/zboot_parser_bpf.c
@@ -0,0 +1,114 @@
+// 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"
+
+/* 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));
+
+
+SEC("fentry.s/kexec_image_parser_anchor")
+int BPF_PROG(parse_pe, struct kexec_context *context, unsigned long parser_id)
+{
+ struct linux_pe_zboot_header *zboot_header;
+ unsigned int image_sz;
+ char *buf;
+ int ret = 0;
+
+ image_sz = context->kernel_sz;
+ bpf_printk("begin parse PE\n");
+ /* BPF verifier should know each variable initial state */
+ if (!context->kernel || (image_sz > MAX_RECORD_SIZE)) {
+ bpf_printk("Err: image size is greater than 0x%lx\n", MAX_RECORD_SIZE);
+ return 0;
+ }
+
+ /* In order to access bytes not aligned on 2 order, copy into ringbuf.
+ * And allocate the memory all at once, later overwriting.
+ *
+ * R2 is ARG_CONST_ALLOC_SIZE_OR_ZERO, should be decided at compling time
+ */
+ buf = (char *)bpf_ringbuf_reserve(&ringbuf_1, MAX_RECORD_SIZE, 0);
+ if (!buf) {
+ bpf_printk("Err: fail to reserve ringbuf to parse zboot header\n");
+ return 0;
+ }
+ bpf_probe_read((void *)buf, sizeof(struct linux_pe_zboot_header), context->kernel);
+ zboot_header = (struct linux_pe_zboot_header *)buf;
+ if (!!__builtin_memcmp(&zboot_header->image_type, "zimg",
+ sizeof(zboot_header->image_type))) {
+ bpf_ringbuf_discard(buf, BPF_RB_NO_WAKEUP);
+ bpf_printk("Err: image is not zboot image\n");
+ return 0;
+ }
+
+ unsigned int payload_offset = zboot_header->payload_offset;
+ unsigned int payload_size = zboot_header->payload_size;
+ bpf_printk("zboot image payload offset=0x%x, size=0x%x\n", payload_offset, payload_size);
+ /* sane check */
+ if (payload_size > image_sz) {
+ bpf_ringbuf_discard(buf, BPF_RB_NO_WAKEUP);
+ bpf_printk("Invalid zboot image payload offset and size\n");
+ return 0;
+ }
+ unsigned int max_payload = MAX_RECORD_SIZE - sizeof(struct cmd_hdr);
+ if (payload_size >= max_payload) {
+ bpf_ringbuf_discard(buf, BPF_RB_NO_WAKEUP);
+ bpf_printk("Err: payload_size > MAX_RECORD_SIZE\n");
+ return 0;
+ }
+ void *dst = (void *)buf + sizeof(struct cmd_hdr);
+ /* Overwrite buf */
+ struct cmd_hdr *cmd = (struct cmd_hdr *)buf;
+ cmd->cmd = KEXEC_BPF_CMD_DECOMPRESS;
+ cmd->subcmd = KEXEC_BPF_SUBCMD_KERNEL;
+ /* 4 bytes original size is appended after vmlinuz.bin */
+ cmd->payload_len = payload_size - 4;
+ bpf_probe_read(dst, payload_size, context->kernel + payload_offset);
+ if (payload_size < 4) {
+ bpf_ringbuf_discard(buf, BPF_RB_NO_WAKEUP);
+ return 0;
+ }
+ bpf_printk("Calling bpf_kexec_decompress()\n");
+ struct bpf_parser_context *bpf = bpf_get_parser_context(parser_id);
+ if (!bpf) {
+ bpf_ringbuf_discard(buf, BPF_RB_NO_WAKEUP);
+ bpf_printk("No parser in kernel\n");
+ return 0;
+ }
+ ret = bpf_buffer_parser(buf, sizeof(struct cmd_hdr) + payload_size - 4, bpf);
+ if (ret < 0) {
+ bpf_ringbuf_discard(buf, BPF_RB_NO_WAKEUP);
+ bpf_put_parser_context(bpf);
+ bpf_printk("Decompression fails\n");
+ return 0;
+ }
+ bpf_ringbuf_discard(buf, BPF_RB_NO_WAKEUP);
+ bpf_put_parser_context(bpf);
+
+ return 0;
+}
--
2.49.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCHv6 12/13] tools/kexec: Introduce a bpf-prog to parse zboot image format
2026-01-19 3:24 ` [PATCHv6 12/13] tools/kexec: Introduce a bpf-prog to parse zboot image format Pingfan Liu
@ 2026-01-19 18:45 ` bot+bpf-ci
0 siblings, 0 replies; 30+ messages in thread
From: bot+bpf-ci @ 2026-01-19 18:45 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: 2685 bytes --]
> diff --git a/tools/kexec/zboot_parser_bpf.c b/tools/kexec/zboot_parser_bpf.c
> new file mode 100644
> index 000000000000..54c4b762b332
> --- /dev/null
> +++ b/tools/kexec/zboot_parser_bpf.c
[ ... ]
> +/* 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));
[ ... ]
> + unsigned int payload_offset = zboot_header->payload_offset;
> + unsigned int payload_size = zboot_header->payload_size;
> + bpf_printk("zboot image payload offset=0x%x, size=0x%x\n", payload_offset, payload_size);
> + /* sane check */
> + if (payload_size > image_sz) {
> + bpf_ringbuf_discard(buf, BPF_RB_NO_WAKEUP);
> + bpf_printk("Invalid zboot image payload offset and size\n");
> + return 0;
> + }
Should this bounds check also verify that payload_offset + payload_size
does not exceed image_sz? The current check only validates that payload_size
fits, but if payload_offset is large (e.g., near image_sz), the read below
could extend past the end of the kernel buffer.
For example, with payload_offset = image_sz - 10 and payload_size = 100,
the check "payload_size > image_sz" passes, but the bpf_probe_read below
would read 90 bytes past the valid buffer boundary.
> + unsigned int max_payload = MAX_RECORD_SIZE - sizeof(struct cmd_hdr);
> + if (payload_size >= max_payload) {
> + bpf_ringbuf_discard(buf, BPF_RB_NO_WAKEUP);
> + bpf_printk("Err: payload_size > MAX_RECORD_SIZE\n");
> + return 0;
> + }
> + void *dst = (void *)buf + sizeof(struct cmd_hdr);
> + /* Overwrite buf */
> + struct cmd_hdr *cmd = (struct cmd_hdr *)buf;
> + cmd->cmd = KEXEC_BPF_CMD_DECOMPRESS;
> + cmd->subcmd = KEXEC_BPF_SUBCMD_KERNEL;
> + /* 4 bytes original size is appended after vmlinuz.bin */
> + cmd->payload_len = payload_size - 4;
> + bpf_probe_read(dst, payload_size, context->kernel + payload_offset);
^^^^^^^^^^^^^^^
This is where the out-of-bounds read could occur if payload_offset +
payload_size > image_sz. While bpf_probe_read is safe and will not crash
(it uses copy_from_kernel_nofault internally), it could read from adjacent
kernel memory if the zboot header contains invalid offset/size values.
---
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/21147860407
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCHv6 13/13] tools/kexec: Add a zboot image building tool
2026-01-19 3:24 [PATCHv6 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
` (11 preceding siblings ...)
2026-01-19 3:24 ` [PATCHv6 12/13] tools/kexec: Introduce a bpf-prog to parse zboot image format Pingfan Liu
@ 2026-01-19 3:24 ` Pingfan Liu
2026-01-19 18:45 ` bot+bpf-ci
2026-02-26 13:36 ` [PATCHv6 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Philipp Rudo
13 siblings, 1 reply; 30+ messages in thread
From: Pingfan Liu @ 2026-01-19 3:24 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 objcopy binary can append a section to a PE file, but it disregards
the DOS header. However, the zboot format carries important information
in the DOS header: payload offset and size.
To track this information and append a new PE section, here a dedicated
binary tool is introduced to build zboot images. The payload's relative
offset within the .data section remains unchanged. Therefore, the .data
section offset in the new PE file, plus the payload offset within that
section, yields the payload offset within the new PE file.
Finally, the new PE file 'zboot.efi', with a .bpf section, can be got by
the command:
make -C tools/kexec zboot
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
---
tools/kexec/Makefile | 10 +-
tools/kexec/pe.h | 177 +++++++++++++++++++
tools/kexec/zboot_image_builder.c | 278 ++++++++++++++++++++++++++++++
3 files changed, 464 insertions(+), 1 deletion(-)
create mode 100644 tools/kexec/pe.h
create mode 100644 tools/kexec/zboot_image_builder.c
diff --git a/tools/kexec/Makefile b/tools/kexec/Makefile
index 88db6d11bde61..acb045367adfa 100644
--- a/tools/kexec/Makefile
+++ b/tools/kexec/Makefile
@@ -27,7 +27,7 @@ BPFTOOL = bpftool
# List of generated target files
HEADERS = vmlinux.h bpf_helper_defs.h image_size.h
-ZBOOT_TARGETS = zboot_parser_bpf.o zboot_parser_bpf.lskel.h bytecode.c bytecode.o
+ZBOOT_TARGETS = zboot_parser_bpf.o zboot_parser_bpf.lskel.h bytecode.c bytecode.o zboot_image_builder zboot.efi
# Targets
@@ -76,6 +76,14 @@ bytecode.c: zboot_parser_bpf.lskel.h
bytecode.o: bytecode.c
@$(CC) -c $< -o $@
+# Rule to build zboot_image_builder executable
+zboot_image_builder: zboot_image_builder.c
+ @$(CC) $(CFLAGS) $< -o $@
+
+zboot.efi: zboot_image_builder bytecode.o
+ @chmod +x zboot_image_builder
+ @./zboot_image_builder $(EFI_IMAGE) bytecode.o $@
+
# Clean up generated files
clean:
@rm -f $(HEADERS) $(ZBOOT_TARGETS)
diff --git a/tools/kexec/pe.h b/tools/kexec/pe.h
new file mode 100644
index 0000000000000..c2273d3fc3bb3
--- /dev/null
+++ b/tools/kexec/pe.h
@@ -0,0 +1,177 @@
+/*
+ * Extract from linux kernel include/linux/pe.h
+ */
+
+#ifndef __PE_H__
+#define __PE_H__
+
+#define IMAGE_DOS_SIGNATURE 0x5a4d /* "MZ" */
+#define IMAGE_NT_SIGNATURE 0x00004550 /* "PE\0\0" */
+
+struct mz_hdr {
+ uint16_t magic; /* MZ_MAGIC */
+ uint16_t lbsize; /* size of last used block */
+ uint16_t blocks; /* pages in file, 0x3 */
+ uint16_t relocs; /* relocations */
+ uint16_t hdrsize; /* header size in "paragraphs" */
+ uint16_t min_extra_pps; /* .bss */
+ uint16_t max_extra_pps; /* runtime limit for the arena size */
+ uint16_t ss; /* relative stack segment */
+ uint16_t sp; /* initial %sp register */
+ uint16_t checksum; /* word checksum */
+ uint16_t ip; /* initial %ip register */
+ uint16_t cs; /* initial %cs relative to load segment */
+ uint16_t reloc_table_offset; /* offset of the first relocation */
+ uint16_t overlay_num; /* overlay number. set to 0. */
+ uint16_t reserved0[4]; /* reserved */
+ uint16_t oem_id; /* oem identifier */
+ uint16_t oem_info; /* oem specific */
+ uint16_t reserved1[10]; /* reserved */
+ uint32_t peaddr; /* address of pe header */
+ char message[]; /* message to print */
+};
+
+struct pe_hdr {
+ uint32_t magic; /* PE magic */
+ uint16_t machine; /* machine type */
+ uint16_t sections; /* number of sections */
+ uint32_t timestamp; /* time_t */
+ uint32_t symbol_table; /* symbol table offset */
+ uint32_t symbols; /* number of symbols */
+ uint16_t opt_hdr_size; /* size of optional header */
+ uint16_t flags; /* flags */
+};
+
+/* the fact that pe32 isn't padded where pe32+ is 64-bit means union won't
+ * work right. vomit. */
+struct pe32_opt_hdr {
+ /* "standard" header */
+ uint16_t magic; /* file type */
+ uint8_t ld_major; /* linker major version */
+ uint8_t ld_minor; /* linker minor version */
+ uint32_t text_size; /* size of text section(s) */
+ uint32_t data_size; /* size of data section(s) */
+ uint32_t bss_size; /* size of bss section(s) */
+ uint32_t entry_point; /* file offset of entry point */
+ uint32_t code_base; /* relative code addr in ram */
+ uint32_t data_base; /* relative data addr in ram */
+ /* "windows" header */
+ uint32_t image_base; /* preferred load address */
+ uint32_t section_align; /* alignment in bytes */
+ uint32_t file_align; /* file alignment in bytes */
+ uint16_t os_major; /* major OS version */
+ uint16_t os_minor; /* minor OS version */
+ uint16_t image_major; /* major image version */
+ uint16_t image_minor; /* minor image version */
+ uint16_t subsys_major; /* major subsystem version */
+ uint16_t subsys_minor; /* minor subsystem version */
+ uint32_t win32_version; /* reserved, must be 0 */
+ uint32_t image_size; /* image size */
+ uint32_t header_size; /* header size rounded up to
+ file_align */
+ uint32_t csum; /* checksum */
+ uint16_t subsys; /* subsystem */
+ uint16_t dll_flags; /* more flags! */
+ uint32_t stack_size_req;/* amt of stack requested */
+ uint32_t stack_size; /* amt of stack required */
+ uint32_t heap_size_req; /* amt of heap requested */
+ uint32_t heap_size; /* amt of heap required */
+ uint32_t loader_flags; /* reserved, must be 0 */
+ uint32_t data_dirs; /* number of data dir entries */
+};
+
+struct pe32plus_opt_hdr {
+ uint16_t magic; /* file type */
+ uint8_t ld_major; /* linker major version */
+ uint8_t ld_minor; /* linker minor version */
+ uint32_t text_size; /* size of text section(s) */
+ uint32_t data_size; /* size of data section(s) */
+ uint32_t bss_size; /* size of bss section(s) */
+ uint32_t entry_point; /* file offset of entry point */
+ uint32_t code_base; /* relative code addr in ram */
+ /* "windows" header */
+ uint64_t image_base; /* preferred load address */
+ uint32_t section_align; /* alignment in bytes */
+ uint32_t file_align; /* file alignment in bytes */
+ uint16_t os_major; /* major OS version */
+ uint16_t os_minor; /* minor OS version */
+ uint16_t image_major; /* major image version */
+ uint16_t image_minor; /* minor image version */
+ uint16_t subsys_major; /* major subsystem version */
+ uint16_t subsys_minor; /* minor subsystem version */
+ uint32_t win32_version; /* reserved, must be 0 */
+ uint32_t image_size; /* image size */
+ uint32_t header_size; /* header size rounded up to
+ file_align */
+ uint32_t csum; /* checksum */
+ uint16_t subsys; /* subsystem */
+ uint16_t dll_flags; /* more flags! */
+ uint64_t stack_size_req;/* amt of stack requested */
+ uint64_t stack_size; /* amt of stack required */
+ uint64_t heap_size_req; /* amt of heap requested */
+ uint64_t heap_size; /* amt of heap required */
+ uint32_t loader_flags; /* reserved, must be 0 */
+ uint32_t data_dirs; /* number of data dir entries */
+};
+
+struct data_dirent {
+ uint32_t virtual_address; /* relative to load address */
+ uint32_t size;
+};
+
+struct data_directory {
+ struct data_dirent exports; /* .edata */
+ struct data_dirent imports; /* .idata */
+ struct data_dirent resources; /* .rsrc */
+ struct data_dirent exceptions; /* .pdata */
+ struct data_dirent certs; /* certs */
+ struct data_dirent base_relocations; /* .reloc */
+ struct data_dirent debug; /* .debug */
+ struct data_dirent arch; /* reservered */
+ struct data_dirent global_ptr; /* global pointer reg. Size=0 */
+ struct data_dirent tls; /* .tls */
+ struct data_dirent load_config; /* load configuration structure */
+ struct data_dirent bound_imports; /* no idea */
+ struct data_dirent import_addrs; /* import address table */
+ struct data_dirent delay_imports; /* delay-load import table */
+ struct data_dirent clr_runtime_hdr; /* .cor (object only) */
+ struct data_dirent reserved;
+};
+
+struct section_header {
+ char name[8]; /* name or "/12\0" string tbl offset */
+ uint32_t virtual_size; /* size of loaded section in ram */
+ uint32_t virtual_address; /* relative virtual address */
+ uint32_t raw_data_size; /* size of the section */
+ uint32_t data_addr; /* file pointer to first page of sec */
+ uint32_t relocs; /* file pointer to relocation entries */
+ uint32_t line_numbers; /* line numbers! */
+ uint16_t num_relocs; /* number of relocations */
+ uint16_t num_lin_numbers; /* srsly. */
+ uint32_t flags;
+};
+
+struct win_certificate {
+ uint32_t length;
+ uint16_t revision;
+ uint16_t cert_type;
+};
+
+/*
+ * Return -1 if not PE, else offset of the PE header
+ */
+static int get_pehdr_offset(const char *buf)
+{
+ int pe_hdr_offset;
+
+ pe_hdr_offset = *((int *)(buf + 0x3c));
+ buf += pe_hdr_offset;
+ if (!!memcmp(buf, "PE\0\0", 4)) {
+ printf("Not a PE file\n");
+ return -1;
+ }
+
+ return pe_hdr_offset;
+}
+
+#endif
diff --git a/tools/kexec/zboot_image_builder.c b/tools/kexec/zboot_image_builder.c
new file mode 100644
index 0000000000000..c0a785074970e
--- /dev/null
+++ b/tools/kexec/zboot_image_builder.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Red Hat, Inc.
+ * The zboot format carries the compressed kernel image offset and size
+ * information in the DOS header. The program appends a bpf section to PE file,
+ * meanwhile maintains the offset and size information, which is lost when using
+ * objcopy to handle zboot image.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "pe.h"
+
+#ifdef DEBUG_DETAIL
+ #define dprintf(...) printf(__VA_ARGS__)
+#else
+ #define dprintf(...) ((void)0)
+#endif
+
+typedef struct {
+ union {
+ struct {
+ unsigned int mz_magic;
+ char image_type[4];
+ /* offset to the whole file start */
+ unsigned int payload_offset;
+ unsigned int payload_size;
+ unsigned int reserved[2];
+ char comp_type[4];
+ };
+ char raw_bytes[56];
+ };
+ unsigned int linux_pe_magic;
+ /* offset at: 0x3c or 60 */
+ unsigned int pe_header_offset;
+} __attribute__((packed)) pe_zboot_header;
+
+typedef unsigned long uintptr_t;
+#define ALIGN_UP(p, size) (__typeof__(p))(((uintptr_t)(p) + ((size) - 1)) & ~((size) - 1))
+
+int main(int argc, char **argv)
+{
+ uint32_t payload_new_offset, payload_sect_off;
+ uint32_t payload_size;
+ uint32_t payload_sect_idx;
+ pe_zboot_header *zheader;
+ struct pe_hdr *pe_hdr;
+ struct pe32plus_opt_hdr *opt_hdr;
+ int base_fd, out_fd;
+ char *base_start_addr, *base_cur;
+ char *out_start_addr, *out_cur;
+ uint32_t out_sz, max_va_end = 0;
+ struct stat sb;
+ int i = 0, ret = 0;
+
+ if (argc != 4) {
+ fprintf(stderr, "Usage: %s <original_pe> <binary_file> <new_pe>\n", argv[0]);
+ return -1;
+ }
+
+ const char *original_pe = argv[1];
+ const char *binary_file = argv[2];
+ const char *new_pe = argv[3];
+ FILE *bin_fp = fopen(binary_file, "rb");
+ if (!bin_fp) {
+ perror("Failed to open binary file");
+ return -1;
+ }
+ fseek(bin_fp, 0, SEEK_END);
+ size_t bin_size = ftell(bin_fp);
+ fseek(bin_fp, 0, SEEK_SET);
+ base_fd = open(original_pe, O_RDWR);
+ out_fd = open(new_pe, O_RDWR | O_CREAT, 0644);
+ if (base_fd == -1 || out_fd == -1) {
+ perror("Error opening file");
+ exit(1);
+ }
+
+ if (fstat(base_fd, &sb) == -1) {
+ perror("Error getting file size");
+ exit(1);
+ }
+ base_start_addr = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, base_fd, 0);
+ if (base_start_addr == MAP_FAILED) {
+ perror("Error mmapping the file");
+ exit(1);
+ }
+ /* 64KB for section table extending */
+ out_sz = sb.st_size + bin_size + (1 << 16);
+ out_start_addr = mmap(NULL, out_sz, PROT_WRITE, MAP_SHARED, out_fd, 0);
+ if (ftruncate(out_fd, out_sz) == -1) {
+ perror("Failed to resize output file");
+ ret = -1;
+ goto err;
+ }
+ if (out_start_addr == MAP_FAILED) {
+ perror("Error mmapping the file");
+ exit(1);
+ }
+
+ zheader = (pe_zboot_header *)base_start_addr;
+ if (zheader->mz_magic != 0x5A4D) { // 'MZ'
+ fprintf(stderr, "Invalid DOS signature\n");
+ return -1;
+ }
+ uint32_t pe_hdr_offset = get_pehdr_offset((const char *)base_start_addr);
+ base_cur = base_start_addr + pe_hdr_offset;
+ pe_hdr = (struct pe_hdr *)base_cur;
+ if (pe_hdr->magic!= 0x00004550) { // 'PE\0\0'
+ fprintf(stderr, "Invalid PE signature\n");
+ return -1;
+ }
+ base_cur += sizeof(struct pe_hdr);
+ opt_hdr = (struct pe32plus_opt_hdr *)base_cur;
+ uint32_t file_align = opt_hdr->file_align;
+ uint32_t section_alignment = opt_hdr->section_align;
+
+ uint16_t num_sections = pe_hdr->sections;
+ struct section_header *base_sections, *sect;
+ uint32_t section_table_offset = pe_hdr_offset + sizeof(struct pe_hdr) + pe_hdr->opt_hdr_size;
+ base_sections = (struct section_header *)(base_start_addr + section_table_offset);
+
+ /* Decide the section idx and the payload offset within the section */
+ for (i = 0; i < num_sections; i++) {
+ sect = &base_sections[i];
+ if (zheader->payload_offset >= sect->data_addr &&
+ zheader->payload_offset < (sect->data_addr + sect->raw_data_size)) {
+ payload_sect_idx = i;
+ payload_sect_off = zheader->payload_offset - sect->data_addr;
+ }
+ }
+
+ /* Calculate the end of the last section in virtual memory */
+ for (i = 0; i < num_sections; i++) {
+ uint32_t section_end = base_sections[i].virtual_address + base_sections[i].virtual_size;
+ if (section_end > max_va_end) {
+ max_va_end = section_end;
+ }
+ }
+
+ /* Calculate virtual address for the new .bpf section */
+ uint32_t bpf_virtual_address = ALIGN_UP(max_va_end, section_alignment);
+
+ pe_zboot_header *new_zhdr = malloc(sizeof(pe_zboot_header));
+ memcpy(new_zhdr, zheader, sizeof(pe_zboot_header));
+ struct pe_hdr *new_hdr = malloc(sizeof(struct pe_hdr));
+ memcpy(new_hdr, pe_hdr, sizeof(struct pe_hdr));
+ new_hdr->sections += 1;
+ struct pe32plus_opt_hdr *new_opt_hdr = malloc(pe_hdr->opt_hdr_size);
+ memcpy(new_opt_hdr, opt_hdr, pe_hdr->opt_hdr_size);
+ /* Create new section headers array (original + new section) */
+ struct section_header *new_sections = calloc(1, new_hdr->sections * sizeof(struct section_header));
+ if (!new_sections) {
+ perror("Failed to allocate memory for new section headers");
+ return -1;
+ }
+ memcpy(new_sections, base_sections, pe_hdr->sections * sizeof(struct section_header));
+
+ /* Configure the new .bpf section */
+ struct section_header *bpf_section = &new_sections[new_hdr->sections - 1];
+ memset(bpf_section, 0, sizeof(struct section_header));
+ strncpy((char *)bpf_section->name, ".bpf", 8);
+ bpf_section->virtual_size = bin_size;
+ bpf_section->virtual_address = bpf_virtual_address;
+ bpf_section->raw_data_size = bin_size;
+ bpf_section->flags = 0x40000000; //Readable
+
+ /* Update headers */
+ uint32_t new_size_of_image = bpf_section->virtual_address + bpf_section->virtual_size;
+ new_size_of_image = ALIGN_UP(new_size_of_image, section_alignment);
+ new_opt_hdr->image_size = new_size_of_image;
+
+ size_t section_table_size = new_hdr->sections * (sizeof(struct section_header));
+ size_t headers_size = section_table_offset + section_table_size;
+ size_t aligned_headers_size = ALIGN_UP(headers_size, file_align);
+ new_opt_hdr->header_size = aligned_headers_size;
+
+
+ uint32_t current_offset = aligned_headers_size;
+ /*
+ * If the original PE data_addr is covered by enlarged header_size
+ * re-assign new data_addr for all sections
+ */
+ if (base_sections[0].data_addr < aligned_headers_size) {
+ for (i = 0; i < new_hdr->sections; i++) {
+ new_sections[i].data_addr = current_offset;
+ current_offset += ALIGN_UP(new_sections[i].raw_data_size, file_align);
+ }
+ /* Keep unchanged, just allocating file pointer for bpf section */
+ } else {
+ uint32_t t;
+ i = new_hdr->sections - 2;
+ t = new_sections[i].data_addr + new_sections[i].raw_data_size;
+ i++;
+ new_sections[i].data_addr = ALIGN_UP(t, file_align);
+ }
+
+ payload_new_offset = new_sections[payload_sect_idx].data_addr + payload_sect_off;
+ /* Update */
+ new_zhdr->payload_offset = payload_new_offset;
+ new_zhdr->payload_size = zheader->payload_size;
+ dprintf("zboot payload_offset updated from 0x%x to 0x%x, size:0x%x\n",
+ zheader->payload_offset, payload_new_offset, new_zhdr->payload_size);
+
+
+ /* compose the new PE file */
+
+ /* Write Dos header */
+ memcpy(out_start_addr, new_zhdr, sizeof(pe_zboot_header));
+ out_cur = out_start_addr + pe_hdr_offset;
+
+ /* Write PE header */
+ memcpy(out_cur, new_hdr, sizeof(struct pe_hdr));
+ out_cur += sizeof(struct pe_hdr);
+
+ /* Write PE optional header */
+ memcpy(out_cur, new_opt_hdr, new_hdr->opt_hdr_size);
+ out_cur += new_hdr->opt_hdr_size;
+
+ /* Write all section headers */
+ memcpy(out_cur, new_sections, new_hdr->sections * sizeof(struct section_header));
+
+ /* Skip padding and copy the section data */
+ for (i = 0; i < pe_hdr->sections; i++) {
+ base_cur = base_start_addr + base_sections[i].data_addr;
+ out_cur = out_start_addr + new_sections[i].data_addr;
+ memcpy(out_cur, base_cur, base_sections[i].raw_data_size);
+ }
+ msync(out_start_addr, new_sections[i].data_addr + new_sections[i].raw_data_size, MS_ASYNC);
+ /* For the bpf section */
+ out_cur = out_start_addr + new_sections[i].data_addr;
+
+ /* Write .bpf section data */
+ char *bin_data = calloc(1, bin_size);
+ if (!bin_data) {
+ perror("Failed to allocate memory for binary data");
+ free(new_sections);
+ ret = -1;
+ goto err;
+ }
+ if (fread(bin_data, bin_size, 1, bin_fp) != 1) {
+ perror("Failed to read binary data");
+ free(bin_data);
+ free(new_sections);
+ ret = -1;
+ goto err;
+ }
+
+ if (out_cur + bin_size > out_start_addr + out_sz) {
+ perror("out of out_fd mmap\n");
+ ret = -1;
+ goto err;
+ }
+ memcpy(out_cur, bin_data, bin_size);
+ /* calculate the real size */
+ out_sz = out_cur + bin_size - out_start_addr;
+ msync(out_start_addr, out_sz, MS_ASYNC);
+ /* truncate to the real size */
+ if (ftruncate(out_fd, out_sz) == -1) {
+ perror("Failed to resize output file");
+ ret = -1;
+ goto err;
+ }
+ printf("Create a new PE file with bpf section: %s\n", new_pe);
+err:
+ munmap(out_start_addr, out_sz);
+ munmap(base_start_addr, sb.st_size);
+ close(base_fd);
+ close(out_fd);
+ fclose(bin_fp);
+
+ return ret;
+}
--
2.49.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCHv6 13/13] tools/kexec: Add a zboot image building tool
2026-01-19 3:24 ` [PATCHv6 13/13] tools/kexec: Add a zboot image building tool Pingfan Liu
@ 2026-01-19 18:45 ` bot+bpf-ci
0 siblings, 0 replies; 30+ messages in thread
From: bot+bpf-ci @ 2026-01-19 18:45 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: 2634 bytes --]
> diff --git a/tools/kexec/pe.h b/tools/kexec/pe.h
> new file mode 100644
> index 000000000000..c2273d3fc3bb
> --- /dev/null
> +++ b/tools/kexec/pe.h
[ ... ]
> +/*
> + * Return -1 if not PE, else offset of the PE header
> + */
> +static int get_pehdr_offset(const char *buf)
> +{
> + int pe_hdr_offset;
> +
> + pe_hdr_offset = *((int *)(buf + 0x3c));
> + buf += pe_hdr_offset;
> + if (!!memcmp(buf, "PE\0\0", 4)) {
> + printf("Not a PE file\n");
> + return -1;
> + }
> +
> + return pe_hdr_offset;
> +}
This function returns int and can return -1 on error, but the caller stores
the result in a uint32_t without checking for errors (see below).
> diff --git a/tools/kexec/zboot_image_builder.c b/tools/kexec/zboot_image_builder.c
> new file mode 100644
> index 000000000000..c0a785074970
> --- /dev/null
> +++ b/tools/kexec/zboot_image_builder.c
[ ... ]
> +int main(int argc, char **argv)
> +{
> + uint32_t payload_new_offset, payload_sect_off;
> + uint32_t payload_size;
> + uint32_t payload_sect_idx;
^^^^^^^^^^^^^^^^
Are payload_sect_idx and payload_sect_off intentionally left uninitialized
here? If the loop below never finds a matching section, these variables
will contain garbage values.
[ ... ]
> + uint32_t pe_hdr_offset = get_pehdr_offset((const char *)base_start_addr);
> + base_cur = base_start_addr + pe_hdr_offset;
Is there a missing check for the return value of get_pehdr_offset()? The
function returns -1 on error (printing "Not a PE file"), but this code
stores it in a uint32_t and uses it directly. If get_pehdr_offset()
returns -1, pe_hdr_offset becomes 0xFFFFFFFF, and base_cur will point far
past the mapped region.
[ ... ]
> + /* Decide the section idx and the payload offset within the section */
> + for (i = 0; i < num_sections; i++) {
> + sect = &base_sections[i];
> + if (zheader->payload_offset >= sect->data_addr &&
> + zheader->payload_offset < (sect->data_addr + sect->raw_data_size)) {
> + payload_sect_idx = i;
> + payload_sect_off = zheader->payload_offset - sect->data_addr;
> + }
> + }
If the condition is never true (payload_offset not within any section),
payload_sect_idx and payload_sect_off remain uninitialized. They are used
later in:
payload_new_offset = new_sections[payload_sect_idx].data_addr + payload_sect_off;
Could this cause undefined behavior with a malformed input file?
---
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/21147860407
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCHv6 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image
2026-01-19 3:24 [PATCHv6 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
` (12 preceding siblings ...)
2026-01-19 3:24 ` [PATCHv6 13/13] tools/kexec: Add a zboot image building tool Pingfan Liu
@ 2026-02-26 13:36 ` Philipp Rudo
2026-02-27 6:03 ` Pingfan Liu
13 siblings, 1 reply; 30+ messages in thread
From: Philipp Rudo @ 2026-02-26 13:36 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, Viktor Malik, Jan Hendrik Farr,
Baoquan He, Dave Young, Andrew Morton, kexec, bpf, systemd-devel,
linux-kernel
Hi Pingfan,
please excuse my late reply. I've looked through the series and all in
all it looks fine to me. I've got two comments/questions in addition to
what the bpf-bot found. In general the findings from the bpf-bot look
valid to me. But fixing them should be rather straight forward. Only
for the first finding, where it "complains" that ctx->buf is NULL, I
don't understand what it is wants to tell us.
In my opinion the next step we should take is to implement an example
bpf-prog for UKIs. I expect it to be much more complex compared to the
zboot one. So having a example bpf-prog will show us if we run into any
limitations with bpf or need to adjust the interfaces.
Thanks
Philipp
On Mon, 19 Jan 2026 11:24:11 +0800
Pingfan Liu <piliu@redhat.com> wrote:
> *** 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. As a result, the kexec kernel code can remain
> relatively stable without introducing new parsers for different
> architectures. This approach introduces a dedicated '.bpf' section in
> the PE file, which stores BPF bytecode. The integrity of all
> components -- kernel, initramfs, cmdline, and even the BPF bytecode
> itself is protected by the PE file's signature. After signature
> verification, the BPF bytecode is loaded and executed from within the
> kernel using BPF lskel. Therefore, the bytecode itself is protected from
> malicious attacks on the BPF loader in user space.
>
> When a .bpf section is extracted from the current image file, its
> bytecode is attached to the kexec kernel function
> kexec_image_parser_anchor(). After the bytecode parses the image, the
> next-stage image is prepared, and the bytecode in the new .bpf section
> can be attached to kexec_image_parser_anchor(). In this way, nested
> image format issues (e.g., zboot image within UKI on arm64) can be
> resolved. (Theoretically not yet tested.)
>
>
> *** 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 kernel
> -2. get the zboot image with bpf-prog by 'make -C tools/kexec zboot'
> -3. compile kexec-tools from https://github.com/pfliu/kexec-tools/tree/pe_bpf
>
> The rest test process is the common convention to use kexec.
>
>
> [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 ***
> 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: Move signature validation ahead
> kexec_file: Introduce routines to parse PE file
> 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: Introduce a bpf-prog lskel to parse PE file
> 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 parse zboot image format
> tools/kexec: Add a zboot image building tool
>
> arch/arm64/Kconfig | 1 +
> include/linux/bpf.h | 19 +
> include/linux/decompress/mm.h | 8 +
> kernel/Kconfig.kexec | 8 +
> kernel/Makefile | 2 +
> kernel/bpf/Makefile | 3 +
> kernel/bpf/bpf_buffer_parser.c | 170 +++++++
> 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 | 68 +++
> kernel/kexec_bpf_loader.c | 439 +++++++++++++++++++
> kernel/kexec_file.c | 106 +++--
> kernel/kexec_internal.h | 5 +
> kernel/kexec_uefi_app.c | 81 ++++
> lib/Kconfig | 6 +
> lib/decompress.c | 6 +-
> tools/kexec/Makefile | 91 ++++
> tools/kexec/pe.h | 177 ++++++++
> tools/kexec/template.c | 68 +++
> tools/kexec/zboot_image_builder.c | 278 ++++++++++++
> tools/kexec/zboot_parser_bpf.c | 114 +++++
> 22 files changed, 1813 insertions(+), 49 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 kernel/kexec_uefi_app.c
> create mode 100644 tools/kexec/Makefile
> create mode 100644 tools/kexec/pe.h
> create mode 100644 tools/kexec/template.c
> create mode 100644 tools/kexec/zboot_image_builder.c
> create mode 100644 tools/kexec/zboot_parser_bpf.c
>
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCHv6 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image
2026-02-26 13:36 ` [PATCHv6 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Philipp Rudo
@ 2026-02-27 6:03 ` Pingfan Liu
0 siblings, 0 replies; 30+ messages in thread
From: Pingfan Liu @ 2026-02-27 6:03 UTC (permalink / raw)
To: Philipp Rudo
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, Viktor Malik, Jan Hendrik Farr,
Baoquan He, Dave Young, Andrew Morton, kexec, bpf, systemd-devel,
linux-kernel
Hi Philipp,
Thank you for taking the time to look into this.
On Thu, Feb 26, 2026 at 9:36 PM Philipp Rudo <prudo@redhat.com> wrote:
>
> Hi Pingfan,
>
> please excuse my late reply. I've looked through the series and all in
> all it looks fine to me. I've got two comments/questions in addition to
> what the bpf-bot found. In general the findings from the bpf-bot look
> valid to me. But fixing them should be rather straight forward. Only
> for the first finding, where it "complains" that ctx->buf is NULL, I
> don't understand what it is wants to tell us.
>
A race condition exists: when bpf_buffer_parser() is called multiple
times on ctx, later calls overwrite the previous ctx->buf without
releasing it. While re-entrancy is uncommon in this design, it still
needs to be addressed.
> In my opinion the next step we should take is to implement an example
> bpf-prog for UKIs. I expect it to be much more complex compared to the
> zboot one. So having a example bpf-prog will show us if we run into any
> limitations with bpf or need to adjust the interfaces.
>
Yeah, it's a little challenge. The traditional kexec_file_load syscall
interface was tailored for the kernel boot protocol. However, with the
emergence of Zboot and UKI images, it fails to automatically
accommodate these new formats.
Now, with the introduction of decompose_kexec_kimage() to
kexec_file_load, the design takes a different approach. It decomposes
these complex modern images and then reassembles them into the kernel
boot protocol. Consequently, the fds parameter in kexec_file_load()
can have more flexible meanings, no longer limited to kernel or initrd
file descriptors.
I'm considering two alternative approaches. The first is to introduce
an fd array to pass the file descriptors for UKI and cmdline add-ons.
The second option is to reuse the current interface but reinterpret
the meaning of fds. The first approach requires not just an fd array
but also metadata describing the purpose of each file descriptor. In
contrast, the second option avoids this complexity.
In the second option, the first fd in kexec_file_load can be used to
ship the bpf parsers. the second fd can be used to ship a temporary
file, which holds UKI and all addons. Inside the kernel, the bpf
parser can extract the PE files embeded inside the temporary file,
verify their signature, decide their role and reassemble them. So all
details are kept inside the bpf parser without changing the kernel
API.
To implement this, the design in this series requires some
adjustments. Rather than embedding BPF parsers in PE-based formats
like Zboot or UKI, this approach improves the design by keeping the
kernel image unmodified.
What is your opinion?
Thanks,
Pingfan
> Thanks
> Philipp
>
> On Mon, 19 Jan 2026 11:24:11 +0800
> Pingfan Liu <piliu@redhat.com> wrote:
>
> > *** 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. As a result, the kexec kernel code can remain
> > relatively stable without introducing new parsers for different
> > architectures. This approach introduces a dedicated '.bpf' section in
> > the PE file, which stores BPF bytecode. The integrity of all
> > components -- kernel, initramfs, cmdline, and even the BPF bytecode
> > itself is protected by the PE file's signature. After signature
> > verification, the BPF bytecode is loaded and executed from within the
> > kernel using BPF lskel. Therefore, the bytecode itself is protected from
> > malicious attacks on the BPF loader in user space.
> >
> > When a .bpf section is extracted from the current image file, its
> > bytecode is attached to the kexec kernel function
> > kexec_image_parser_anchor(). After the bytecode parses the image, the
> > next-stage image is prepared, and the bytecode in the new .bpf section
> > can be attached to kexec_image_parser_anchor(). In this way, nested
> > image format issues (e.g., zboot image within UKI on arm64) can be
> > resolved. (Theoretically not yet tested.)
> >
> >
> > *** 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 kernel
> > -2. get the zboot image with bpf-prog by 'make -C tools/kexec zboot'
> > -3. compile kexec-tools from https://github.com/pfliu/kexec-tools/tree/pe_bpf
> >
> > The rest test process is the common convention to use kexec.
> >
> >
> > [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 ***
> > 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: Move signature validation ahead
> > kexec_file: Introduce routines to parse PE file
> > 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: Introduce a bpf-prog lskel to parse PE file
> > 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 parse zboot image format
> > tools/kexec: Add a zboot image building tool
> >
> > arch/arm64/Kconfig | 1 +
> > include/linux/bpf.h | 19 +
> > include/linux/decompress/mm.h | 8 +
> > kernel/Kconfig.kexec | 8 +
> > kernel/Makefile | 2 +
> > kernel/bpf/Makefile | 3 +
> > kernel/bpf/bpf_buffer_parser.c | 170 +++++++
> > 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 | 68 +++
> > kernel/kexec_bpf_loader.c | 439 +++++++++++++++++++
> > kernel/kexec_file.c | 106 +++--
> > kernel/kexec_internal.h | 5 +
> > kernel/kexec_uefi_app.c | 81 ++++
> > lib/Kconfig | 6 +
> > lib/decompress.c | 6 +-
> > tools/kexec/Makefile | 91 ++++
> > tools/kexec/pe.h | 177 ++++++++
> > tools/kexec/template.c | 68 +++
> > tools/kexec/zboot_image_builder.c | 278 ++++++++++++
> > tools/kexec/zboot_parser_bpf.c | 114 +++++
> > 22 files changed, 1813 insertions(+), 49 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 kernel/kexec_uefi_app.c
> > create mode 100644 tools/kexec/Makefile
> > create mode 100644 tools/kexec/pe.h
> > create mode 100644 tools/kexec/template.c
> > create mode 100644 tools/kexec/zboot_image_builder.c
> > create mode 100644 tools/kexec/zboot_parser_bpf.c
> >
>
^ permalink raw reply [flat|nested] 30+ messages in thread