bpf.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCHv3 0/9] kexec: Use BPF lskel to enable kexec to load PE format boot image
@ 2025-05-29  4:17 Pingfan Liu
  2025-05-29  4:17 ` [PATCHv3 1/9] kexec_file: Make kexec_image_load_default global visible Pingfan Liu
                   ` (8 more replies)
  0 siblings, 9 replies; 20+ messages in thread
From: Pingfan Liu @ 2025-05-29  4:17 UTC (permalink / raw)
  Cc: 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, kexec, bpf


*** Review the history ***

Nowadays UEFI PE bootable image is more and more popular on the distribution.
But it is still an open issue to load that kind of image by kexec with IMA enabled

There are several approaches to reslove this issue, but none of them are
accepted in upstream till now.

The summary of those approaches:
  -1. UEFI service emulator for UEFI stub
  -2. PE format parser in kernel

For the first one, I have tried a purgatory-style emulator [1]. But it
confronts the hardware scaling trouble.  For the second one, there are two
choices, one is to implement it inside the kernel, the other is inside the user
space.  Both zboot-format [2] and UKI-format [3] parsers are rejected due to
the concern that the variant format parsers will inflate the kernel code.  And
finally, we have these kinds of parsers in the user space 'kexec-tools'.


*** The approach in this series ***

This approach allows the various PE boot image to be parsed in the bpf-prog,
as a result, the kexec kernel code to remain relatively stable.

Benefits
And it abstracts architecture independent part and 
the API is limitted 

To protect against malicious attacks on the BPF loader in user space, it
employs BPF lskel to load and execute BPF programs from within the
kernel.

Each type of PE image contains a dedicated section '.bpf', which stores
the bpf-prog designed to parse the format.  This ensures that the PE's
signature also protects the integrity of the '.bpf' section.


The parsing process operates as a pipeline. The current BPF program
parser attaches to bpf_handle_pefile() and detaches at the end of the
current stage via disarm_bpf_prog(). The results parsed by the current
BPF program are buffered in the kernel through prepare_nested_pe() and
then delivered to the next stage. For each stage of the pipeline, the
BPF bytecode is stored in the '.bpf' section of the PE file. That means
a vmlinuz.efi embeded in UKI format can be handled.


Special thanks to Philipp Rudo, who spent significant time evaluating
the practicality of my solution, and to Viktor Malik, who guided me
toward using BPF light skeleton to prevent malicious attacks from user
space.



[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-arm-kernel/20230921133703.39042-2-kernelfans@gmail.com/T/


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 (9):
  kexec_file: Make kexec_image_load_default global visible
  lib/decompress: Keep decompressor when CONFIG_KEXEC_PE_IMAGE
  bpf: Introduce bpf_copy_to_kernel() to buffer the content from
    bpf-prog
  bpf: Introduce decompressor kfunc
  kexec: Introduce kexec_pe_image to parse and load PE file
  kexec: Integrate with the introduced bpf kfuncs
  kexec: Introduce a bpf-prog lskel to parse PE file
  kexec: Integrate bpf light skeleton to load zboot image
  arm64/kexec: Add PE image format support

 arch/arm64/Kconfig                           |   1 +
 arch/arm64/include/asm/kexec.h               |   1 +
 arch/arm64/kernel/machine_kexec_file.c       |   3 +
 include/linux/bpf.h                          |  23 +
 include/linux/decompress/mm.h                |   7 +
 include/linux/kexec.h                        |   2 +
 kernel/Kconfig.kexec                         |   8 +
 kernel/Makefile                              |   2 +
 kernel/bpf/Makefile                          |   2 +-
 kernel/bpf/helpers.c                         | 112 +++++
 kernel/bpf/helpers_carrier.c                 | 194 ++++++++
 kernel/kexec_bpf/Makefile                    |  65 +++
 kernel/kexec_bpf/kexec_pe_parser_bpf.c       |  65 +++
 kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h | 147 ++++++
 kernel/kexec_file.c                          |   2 +-
 kernel/kexec_pe_image.c                      | 487 +++++++++++++++++++
 lib/decompress.c                             |   6 +-
 17 files changed, 1122 insertions(+), 5 deletions(-)
 create mode 100644 kernel/bpf/helpers_carrier.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_pe_image.c

-- 
2.49.0


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

* [PATCHv3 1/9] kexec_file: Make kexec_image_load_default global visible
  2025-05-29  4:17 [PATCHv3 0/9] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
@ 2025-05-29  4:17 ` Pingfan Liu
  2025-05-29  4:17 ` [PATCHv3 2/9] lib/decompress: Keep decompressor when CONFIG_KEXEC_PE_IMAGE Pingfan Liu
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 20+ messages in thread
From: Pingfan Liu @ 2025-05-29  4:17 UTC (permalink / raw)
  To: kexec
  Cc: 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, bpf

In latter patches, PE format parser will extract the linux kernel inside
and try its real format parser. So making kexec_image_load_default
global.

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>
To: kexec@lists.infradead.org
---
 include/linux/kexec.h | 1 +
 kernel/kexec_file.c   | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index c8971861521a5..26398b269ac29 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -148,6 +148,7 @@ extern const struct kexec_file_ops * const kexec_file_loaders[];
 
 int kexec_image_probe_default(struct kimage *image, void *buf,
 			      unsigned long buf_len);
+void *kexec_image_load_default(struct kimage *image);
 int kexec_image_post_load_cleanup_default(struct kimage *image);
 
 /*
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index fba686487e3b5..6a72bdfab5f5c 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -65,7 +65,7 @@ int kexec_image_probe_default(struct kimage *image, void *buf,
 	return ret;
 }
 
-static void *kexec_image_load_default(struct kimage *image)
+void *kexec_image_load_default(struct kimage *image)
 {
 	if (!image->fops || !image->fops->load)
 		return ERR_PTR(-ENOEXEC);
-- 
2.49.0


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

* [PATCHv3 2/9] lib/decompress: Keep decompressor when CONFIG_KEXEC_PE_IMAGE
  2025-05-29  4:17 [PATCHv3 0/9] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
  2025-05-29  4:17 ` [PATCHv3 1/9] kexec_file: Make kexec_image_load_default global visible Pingfan Liu
@ 2025-05-29  4:17 ` Pingfan Liu
  2025-05-29  4:17 ` [PATCHv3 3/9] bpf: Introduce bpf_copy_to_kernel() to buffer the content from bpf-prog Pingfan Liu
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 20+ messages in thread
From: Pingfan Liu @ 2025-05-29  4:17 UTC (permalink / raw)
  To: linux-kernel
  Cc: 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, kexec, bpf

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 | 7 +++++++
 lib/decompress.c              | 6 +++---
 2 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/include/linux/decompress/mm.h b/include/linux/decompress/mm.h
index ac862422df158..e8948260e2bbe 100644
--- a/include/linux/decompress/mm.h
+++ b/include/linux/decompress/mm.h
@@ -92,7 +92,14 @@ MALLOC_VISIBLE void free(void *where)
 #define large_malloc(a) vmalloc(a)
 #define large_free(a) vfree(a)
 
+#ifdef CONFIG_KEXEC_PE_IMAGE
+#define INIT
+#define INITCONST
+#else
 #define INIT __init
+#define INITCONST __initconst
+#endif
+
 #define STATIC
 
 #include <linux/init.h>
diff --git a/lib/decompress.c b/lib/decompress.c
index ab3fc90ffc646..3d5b6304bb0f1 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 = {
 	{ {0x1f, 0x8b}, "gzip", gunzip },
 	{ {0x1f, 0x9e}, "gzip", gunzip },
 	{ {0x42, 0x5a}, "bzip2", bunzip2 },
@@ -60,7 +60,7 @@ static const struct compress_format compressed_formats[] __initconst = {
 	{ {0, 0}, NULL, NULL }
 };
 
-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] 20+ messages in thread

* [PATCHv3 3/9] bpf: Introduce bpf_copy_to_kernel() to buffer the content from bpf-prog
  2025-05-29  4:17 [PATCHv3 0/9] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
  2025-05-29  4:17 ` [PATCHv3 1/9] kexec_file: Make kexec_image_load_default global visible Pingfan Liu
  2025-05-29  4:17 ` [PATCHv3 2/9] lib/decompress: Keep decompressor when CONFIG_KEXEC_PE_IMAGE Pingfan Liu
@ 2025-05-29  4:17 ` Pingfan Liu
  2025-05-29 11:48   ` kernel test robot
  2025-06-25 18:10   ` Philipp Rudo
  2025-05-29  4:17 ` [PATCHv3 4/9] bpf: Introduce decompressor kfunc Pingfan Liu
                   ` (5 subsequent siblings)
  8 siblings, 2 replies; 20+ messages in thread
From: Pingfan Liu @ 2025-05-29  4:17 UTC (permalink / raw)
  To: bpf
  Cc: 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, kexec, KP Singh, Stanislav Fomichev,
	Hao Luo, Jiri Olsa

In the security kexec_file_load case, the buffer which holds the kernel
image is invisible to the userspace.

The common data flow in bpf scheme is from kernel to bpf-prog.  In the
case of kexec_file_load, the kexec component needs to buffer the parsed
result by bpf-prog (opposite the usual direction) to the next stage
parsing. bpf_kexec_carrier() makes the opposite data flow possible. A
bpf-prog can publish the parsed payload address to the kernel, and the
latter can copy them for future use.

Signed-off-by: Pingfan Liu <piliu@redhat.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.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          |  23 +++++
 kernel/bpf/Makefile          |   2 +-
 kernel/bpf/helpers.c         |   2 +
 kernel/bpf/helpers_carrier.c | 194 +++++++++++++++++++++++++++++++++++
 4 files changed, 220 insertions(+), 1 deletion(-)
 create mode 100644 kernel/bpf/helpers_carrier.c

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 3f0cc89c0622c..104974a6d18cb 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -3568,4 +3568,27 @@ static inline bool bpf_is_subprog(const struct bpf_prog *prog)
 	return prog->aux->func_idx != 0;
 }
 
+struct mem_range_result {
+	struct kref ref;
+	struct rcu_head rcu;
+	char *buf;
+	uint32_t buf_sz;
+	uint32_t data_sz;
+	/* kmalloc-ed or vmalloc-ed */
+	bool kmalloc;
+	int status;
+	struct mem_cgroup *memcg;
+};
+int mem_range_result_put(struct mem_range_result *result);
+
+typedef int (*resource_handler)(const char *name, struct mem_range_result *r);
+
+struct carrier_listener {
+	char *name;
+	bool kmalloc;
+	resource_handler handler;
+};
+
+int register_carrier_listener(struct carrier_listener *listener);
+int unregister_carrier_listener(char *str);
 #endif /* _LINUX_BPF_H */
diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
index 70502f038b921..d1f1f50e23cc8 100644
--- a/kernel/bpf/Makefile
+++ b/kernel/bpf/Makefile
@@ -6,7 +6,7 @@ cflags-nogcse-$(CONFIG_X86)$(CONFIG_CC_IS_GCC) := -fno-gcse
 endif
 CFLAGS_core.o += -Wno-override-init $(cflags-nogcse-yy)
 
-obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o log.o token.o
+obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o helpers_carrier.o tnum.o log.o token.o
 obj-$(CONFIG_BPF_SYSCALL) += bpf_iter.o map_iter.o task_iter.o prog_iter.o link_iter.o
 obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o bloom_filter.o
 obj-$(CONFIG_BPF_SYSCALL) += local_storage.o queue_stack_maps.o ringbuf.o
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index e3a2662f4e336..1f4284e58400b 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -3200,6 +3200,8 @@ BTF_KFUNCS_START(generic_btf_ids)
 #ifdef CONFIG_CRASH_DUMP
 BTF_ID_FLAGS(func, crash_kexec, KF_DESTRUCTIVE)
 #endif
+BTF_ID_FLAGS(func, bpf_mem_range_result_put, KF_RELEASE)
+BTF_ID_FLAGS(func, bpf_copy_to_kernel, KF_TRUSTED_ARGS | KF_SLEEPABLE)
 BTF_ID_FLAGS(func, bpf_obj_new_impl, KF_ACQUIRE | KF_RET_NULL)
 BTF_ID_FLAGS(func, bpf_percpu_obj_new_impl, KF_ACQUIRE | KF_RET_NULL)
 BTF_ID_FLAGS(func, bpf_obj_drop_impl, KF_RELEASE)
diff --git a/kernel/bpf/helpers_carrier.c b/kernel/bpf/helpers_carrier.c
new file mode 100644
index 0000000000000..c4e45fdf0ebb8
--- /dev/null
+++ b/kernel/bpf/helpers_carrier.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/bpf.h>
+#include <linux/bpf-cgroup.h>
+#include <linux/cgroup.h>
+#include <linux/rcupdate.h>
+#include <linux/hashtable.h>
+#include <linux/jhash.h>
+#include <linux/mutex.h>
+#include <linux/kref.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+
+
+struct str_listener {
+	struct hlist_node node;
+	char *str;
+	resource_handler handler;
+	bool kmalloc;
+};
+
+DEFINE_STATIC_SRCU(srcu);
+static DEFINE_MUTEX(str_listeners_mutex);
+static DEFINE_HASHTABLE(str_listeners, 8);
+
+static struct str_listener *find_listener(const char *str)
+{
+	struct str_listener *item;
+	unsigned int hash = jhash(str, strlen(str), 0);
+
+	hash_for_each_possible(str_listeners, item, node, hash) {
+		if (strcmp(item->str, str) == 0)
+			return item;
+	}
+	return NULL;
+}
+
+static void __mem_range_result_free(struct rcu_head *rcu)
+{
+	struct mem_range_result *result = container_of(rcu, struct mem_range_result, rcu);
+	struct mem_cgroup *memcg, *old_memcg;
+
+	memcg = result->memcg;
+	old_memcg = set_active_memcg(memcg);
+	if (likely(!!result->buf)) {
+		if (result->kmalloc)
+			kfree(result->buf);
+		else
+			vfree(result->buf);
+	}
+	kfree(result);
+	set_active_memcg(old_memcg);
+	mem_cgroup_put(memcg);
+}
+
+static void __mem_range_result_put(struct kref *kref)
+{
+	struct mem_range_result *result = container_of(kref, struct mem_range_result, ref);
+
+	call_srcu(&srcu, &result->rcu, __mem_range_result_free);
+}
+
+int mem_range_result_put(struct mem_range_result *result)
+{
+
+	if (!result) {
+		pr_err("%s, receive invalid range\n", __func__);
+		return -EINVAL;
+	}
+
+	kref_put(&result->ref, __mem_range_result_put);
+	return 0;
+}
+
+__bpf_kfunc int bpf_mem_range_result_put(struct mem_range_result *result)
+{
+	return mem_range_result_put(result);
+}
+
+/*
+ * Cache the content in @buf into kernel
+ */
+__bpf_kfunc int bpf_copy_to_kernel(const char *name, char *buf, int size)
+{
+	struct mem_range_result *range;
+	struct mem_cgroup *memcg, *old_memcg;
+	struct str_listener *item;
+	resource_handler handler;
+	bool kmalloc;
+	char *kbuf;
+	int id, ret = 0;
+
+	id = srcu_read_lock(&srcu);
+	item = find_listener(name);
+	if (!item) {
+		srcu_read_unlock(&srcu, id);
+		return -EINVAL;
+	}
+	kmalloc = item->kmalloc;
+	handler = item->handler;
+	srcu_read_unlock(&srcu, id);
+	memcg = get_mem_cgroup_from_current();
+	old_memcg = set_active_memcg(memcg);
+	range = kmalloc(sizeof(struct mem_range_result), GFP_KERNEL);
+	if (!range) {
+		pr_err("fail to allocate mem_range_result\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	kref_init(&range->ref);
+	if (item->kmalloc)
+		kbuf = kmalloc(size, GFP_KERNEL | __GFP_ACCOUNT);
+	else
+		kbuf = __vmalloc(size, GFP_KERNEL | __GFP_ACCOUNT);
+	if (!kbuf) {
+		kfree(range);
+		ret = -ENOMEM;
+		goto err;
+	}
+	ret = copy_from_kernel_nofault(kbuf, buf, size);
+	if (unlikely(ret < 0)) {
+		kfree(range);
+		if (item->kmalloc)
+			kfree(kbuf);
+		else
+			vfree(kbuf);
+		ret = -EINVAL;
+		goto err;
+	}
+	range->kmalloc = item->kmalloc;
+	range->buf = kbuf;
+	range->buf_sz = size;
+	range->data_sz = size;
+	range->memcg = memcg;
+	mem_cgroup_tryget(memcg);
+	range->status = 0;
+	ret = handler(name, range);
+	mem_range_result_put(range);
+err:
+	set_active_memcg(old_memcg);
+	mem_cgroup_put(memcg);
+	return ret;
+}
+
+int register_carrier_listener(struct carrier_listener *listener)
+{
+	struct str_listener *item;
+	unsigned int hash;
+	int ret;
+
+	if (!listener->name)
+		return -EINVAL;
+	item = kmalloc(sizeof(*item), GFP_KERNEL);
+	if (!item)
+		return -ENOMEM;
+	item->str = kstrdup(listener->name, GFP_KERNEL);
+	if (!item->str) {
+		kfree(item);
+		return -ENOMEM;
+	}
+	item->handler = listener->handler;
+	item->kmalloc = listener->kmalloc;
+	hash = jhash(item->str, strlen(item->str), 0);
+	mutex_lock(&str_listeners_mutex);
+	if (!find_listener(item->str)) {
+		hash_add(str_listeners, &item->node, hash);
+	} else {
+		kfree(item->str);
+		kfree(item);
+		ret = -EBUSY;
+	}
+	mutex_unlock(&str_listeners_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL(register_carrier_listener);
+
+int unregister_carrier_listener(char *str)
+{
+	struct str_listener *item;
+	int ret = 0;
+
+	mutex_lock(&str_listeners_mutex);
+	item = find_listener(str);
+	if (!!item)
+		hash_del(&item->node);
+	else
+		ret = -EINVAL;
+	mutex_unlock(&str_listeners_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL(unregister_carrier_listener);
+
-- 
2.49.0


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

* [PATCHv3 4/9] bpf: Introduce decompressor kfunc
  2025-05-29  4:17 [PATCHv3 0/9] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
                   ` (2 preceding siblings ...)
  2025-05-29  4:17 ` [PATCHv3 3/9] bpf: Introduce bpf_copy_to_kernel() to buffer the content from bpf-prog Pingfan Liu
@ 2025-05-29  4:17 ` Pingfan Liu
  2025-05-29 12:31   ` kernel test robot
  2025-05-29  4:17 ` [PATCHv3 5/9] kexec: Introduce kexec_pe_image to parse and load PE file Pingfan Liu
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 20+ messages in thread
From: Pingfan Liu @ 2025-05-29  4:17 UTC (permalink / raw)
  To: bpf
  Cc: 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, kexec, KP Singh, Stanislav Fomichev,
	Hao Luo, Jiri Olsa

This commit bridges the gap between bpf-prog and the kernel
decompression routines. At present, only a global memory allocator is
used for the decompression. Later, if needed, the decompress_fn's
prototype can be changed to pass in a task related allocator.

Signed-off-by: Pingfan Liu <piliu@redhat.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.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
---
 kernel/bpf/helpers.c | 111 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 111 insertions(+)

diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 1f4284e58400b..9748d6101d032 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -23,6 +23,7 @@
 #include <linux/btf_ids.h>
 #include <linux/bpf_mem_alloc.h>
 #include <linux/kasan.h>
+#include <linux/decompress/generic.h>
 
 #include "../../lib/kstrtox.h"
 
@@ -3194,12 +3195,122 @@ __bpf_kfunc void bpf_local_irq_restore(unsigned long *flags__irq_flag)
 	local_irq_restore(*flags__irq_flag);
 }
 
+#define MAX_UNCOMPRESSED_BUF_SIZE	(1 << 28)
+/*
+ * At present, one global allocator for decompression. Later if needed, changing the
+ * prototype of decompress_fn to introduce each task's allocator.
+ */
+static char *output_buf;
+static char *output_cur;
+static DEFINE_MUTEX(output_buf_mutex);
+
+/*
+ * Copy the partial decompressed content in [buf, buf + len) to dst.
+ * If the dst size is beyond the capacity, return -1 to indicate the
+ * decompress method that something is wrong.
+ */
+static long flush(void *buf, unsigned long len)
+{
+
+	if (output_cur - output_buf > MAX_UNCOMPRESSED_BUF_SIZE - len)
+		return -1;
+	memcpy(output_cur, buf, len);
+	output_cur += len;
+	return len;
+}
+
+__bpf_kfunc struct mem_range_result *bpf_decompress(char *image_gz_payload, int image_gz_sz)
+{
+	struct mem_cgroup *memcg, *old_memcg;
+	decompress_fn decompressor;
+	struct mem_range_result *range;
+	const char *name;
+	char *input_buf;
+	int ret;
+
+	memcg = get_mem_cgroup_from_current();
+	old_memcg = set_active_memcg(memcg);
+	range = kmalloc(sizeof(struct mem_range_result), GFP_KERNEL);
+	if (!range) {
+		pr_err("fail to allocate mem_range_result\n");
+		goto error;
+	}
+	kref_init(&range->ref);
+
+	input_buf = __vmalloc(image_gz_sz, GFP_KERNEL | __GFP_ACCOUNT);
+	if (!input_buf) {
+		kfree(range);
+		pr_err("fail to allocate input buffer\n");
+		goto error;
+	}
+
+	ret = copy_from_kernel_nofault(input_buf, image_gz_payload, image_gz_sz);
+	if (ret < 0) {
+		kfree(range);
+		vfree(input_buf);
+		pr_err("Error when copying from 0x%p, size:0x%x\n",
+				image_gz_payload, image_gz_sz);
+		goto error;
+	}
+
+	mutex_lock(&output_buf_mutex);
+	output_buf = __vmalloc(MAX_UNCOMPRESSED_BUF_SIZE, GFP_KERNEL | __GFP_ACCOUNT);
+	if (!output_buf) {
+		mutex_unlock(&output_buf_mutex);
+		kfree(range);
+		vfree(input_buf);
+		pr_err("fail to allocate output buffer\n");
+		goto error;
+	}
+	output_cur = output_buf;
+	decompressor = decompress_method(input_buf, image_gz_sz, &name);
+	if (!decompressor) {
+		kfree(range);
+		vfree(input_buf);
+		vfree(output_buf);
+		mutex_unlock(&output_buf_mutex);
+		pr_err("Can not find decompress method\n");
+		goto error;
+	}
+	ret = decompressor(input_buf, image_gz_sz, NULL, flush,
+				NULL, NULL, NULL);
+
+	vfree(input_buf);
+	/* Update the range map */
+	if (ret == 0) {
+		range->kmalloc = false;
+		range->buf = output_buf;
+		range->buf_sz = MAX_UNCOMPRESSED_BUF_SIZE;
+		range->data_sz = output_cur - output_buf;
+		output_buf = output_cur = NULL;
+		mutex_unlock(&output_buf_mutex);
+		range->status = 0;
+		/* Do not release the reference */
+		range->memcg = memcg;
+		set_active_memcg(old_memcg);
+		return range;
+	}
+
+	/* Decompression fails */
+	vfree(output_buf);
+	output_buf = output_cur = NULL;
+	mutex_unlock(&output_buf_mutex);
+	kfree(range);
+	pr_err("Decompress error\n");
+
+error:
+	set_active_memcg(old_memcg);
+	mem_cgroup_put(memcg);
+	return NULL;
+}
+
 __bpf_kfunc_end_defs();
 
 BTF_KFUNCS_START(generic_btf_ids)
 #ifdef CONFIG_CRASH_DUMP
 BTF_ID_FLAGS(func, crash_kexec, KF_DESTRUCTIVE)
 #endif
+BTF_ID_FLAGS(func, bpf_decompress, KF_TRUSTED_ARGS | KF_ACQUIRE | KF_SLEEPABLE)
 BTF_ID_FLAGS(func, bpf_mem_range_result_put, KF_RELEASE)
 BTF_ID_FLAGS(func, bpf_copy_to_kernel, KF_TRUSTED_ARGS | KF_SLEEPABLE)
 BTF_ID_FLAGS(func, bpf_obj_new_impl, KF_ACQUIRE | KF_RET_NULL)
-- 
2.49.0


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

* [PATCHv3 5/9] kexec: Introduce kexec_pe_image to parse and load PE file
  2025-05-29  4:17 [PATCHv3 0/9] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
                   ` (3 preceding siblings ...)
  2025-05-29  4:17 ` [PATCHv3 4/9] bpf: Introduce decompressor kfunc Pingfan Liu
@ 2025-05-29  4:17 ` Pingfan Liu
  2025-06-25 18:09   ` Philipp Rudo
  2025-05-29  4:17 ` [PATCHv3 6/9] kexec: Integrate with the introduced bpf kfuncs Pingfan Liu
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 20+ messages in thread
From: Pingfan Liu @ 2025-05-29  4:17 UTC (permalink / raw)
  To: kexec
  Cc: 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, bpf

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.

A new kexec_file_ops is implementation, named pe_image_ops.

There are some place holder function in this patch. (They will take
effect after the introduction of kexec bpf light skeleton and bpf
helpers). Overall the parsing progress is a pipeline, the current
bpf-prog parser is attached to bpf_handle_pefile(), and detatched at the
end of the current stage 'disarm_bpf_prog()' the current parsed result
by the current bpf-prog will be buffered in kernel 'prepare_nested_pe()'
, and deliver to the next stage.  For each stage, the bpf bytecode is
extracted from the '.bpf' section in the PE file.

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
---
 include/linux/kexec.h   |   1 +
 kernel/Kconfig.kexec    |   8 +
 kernel/Makefile         |   1 +
 kernel/kexec_pe_image.c | 356 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 366 insertions(+)
 create mode 100644 kernel/kexec_pe_image.c

diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index 26398b269ac29..bca8136dcf1fd 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -392,6 +392,7 @@ static inline int machine_kexec_post_load(struct kimage *image) { return 0; }
 
 extern struct kimage *kexec_image;
 extern struct kimage *kexec_crash_image;
+extern const struct kexec_file_ops pe_image_ops;
 
 bool kexec_load_permitted(int kexec_image_type);
 
diff --git a/kernel/Kconfig.kexec b/kernel/Kconfig.kexec
index 4d111f8719516..686eb7cb96142 100644
--- a/kernel/Kconfig.kexec
+++ b/kernel/Kconfig.kexec
@@ -47,6 +47,14 @@ config KEXEC_FILE
 	  for kernel and initramfs as opposed to list of segments as
 	  accepted by kexec system call.
 
+config KEXEC_PE_IMAGE
+	bool "Enable parsing UEFI PE file through kexec file based system call"
+	depends on KEXEC_FILE
+	depends on DEBUG_INFO_BTF && BPF_SYSCALL
+	help
+	  This option makes the kexec_file_load() syscall cooperates with bpf-prog
+	  to parse PE format file
+
 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 434929de17ef2..ab82d73d8ce81 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -79,6 +79,7 @@ obj-$(CONFIG_KEXEC_CORE) += kexec_core.o
 obj-$(CONFIG_CRASH_DUMP) += crash_core.o
 obj-$(CONFIG_KEXEC) += kexec.o
 obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
+obj-$(CONFIG_KEXEC_PE_IMAGE) += kexec_pe_image.o
 obj-$(CONFIG_KEXEC_ELF) += kexec_elf.o
 obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o
 obj-$(CONFIG_COMPAT) += compat.o
diff --git a/kernel/kexec_pe_image.c b/kernel/kexec_pe_image.c
new file mode 100644
index 0000000000000..3097efccb8502
--- /dev/null
+++ b/kernel/kexec_pe_image.c
@@ -0,0 +1,356 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Kexec PE image loader
+
+ * Copyright (C) 2025 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/pe.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>
+
+
+static LIST_HEAD(phase_head);
+
+struct parsed_phase {
+	struct list_head head;
+	struct list_head res_head;
+};
+
+static struct parsed_phase *cur_phase;
+
+static char *kexec_res_names[3] = {"kernel", "initrd", "cmdline"};
+
+struct kexec_res {
+	struct list_head node;
+	char *name;
+	/* The free of buffer is deferred to kimage_file_post_load_cleanup */
+	bool deferred_free;
+	struct mem_range_result *r;
+};
+
+static struct parsed_phase *alloc_new_phase(void)
+{
+	struct parsed_phase *phase = kzalloc(sizeof(struct parsed_phase), GFP_KERNEL);
+
+	INIT_LIST_HEAD(&phase->head);
+	INIT_LIST_HEAD(&phase->res_head);
+	list_add_tail(&phase->head, &phase_head);
+
+	return phase;
+}
+
+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 != MZ_MAGIC)
+		return false;
+	pe = (struct pe_hdr *)(kernel_buf + mz->peaddr);
+	if (pe->magic != PE_MAGIC)
+		return false;
+	if (pe->opt_hdr_size == 0) {
+		pr_err("optional header is missing\n");
+		return false;
+	}
+
+	return true;
+}
+
+static bool is_valid_format(const char *kernel_buf, unsigned long kernel_len)
+{
+	return is_valid_pe(kernel_buf, kernel_len);
+}
+
+/*
+ * The UEFI Terse Executable (TE) image has MZ header.
+ */
+static int pe_image_probe(const char *kernel_buf, unsigned long kernel_len)
+{
+	return is_valid_pe(kernel_buf, kernel_len) ? 0 : -1;
+}
+
+static int get_pe_section(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 = file_buf + sect_hdr->data_addr;
+			*sect_sz = sect_hdr->raw_data_size;
+			return 0;
+		}
+		sect_hdr++;
+	}
+
+	return -1;
+}
+
+static bool pe_has_bpf_section(char *file_buf, unsigned long pe_sz)
+{
+	char *sect_start = NULL;
+	unsigned long sect_sz = 0;
+	int ret;
+
+	ret = get_pe_section(file_buf, ".bpf", &sect_start, &sect_sz);
+	if (ret < 0)
+		return false;
+	return true;
+}
+
+/* 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 *image;
+	int image_sz;
+	char *initrd;
+	int initrd_sz;
+	char *cmdline;
+	int cmdline_sz;
+};
+
+void bpf_handle_pefile(struct kexec_context *context);
+void bpf_post_handle_pefile(struct kexec_context *context);
+
+
+/*
+ * optimize("O0") prevents inline, compiler constant propagation
+ */
+__attribute__((used, optimize("O0"))) void bpf_handle_pefile(struct kexec_context *context)
+{
+}
+
+__attribute__((used, optimize("O0"))) void bpf_post_handle_pefile(struct kexec_context *context)
+{
+}
+
+/*
+ * PE file may be nested and should be unfold one by one.
+ * Query 'kernel', 'initrd', 'cmdline' in cur_phase, as they are inputs for the
+ * next phase.
+ */
+static int prepare_nested_pe(char **kernel, unsigned long *kernel_len, char **initrd,
+		unsigned long *initrd_len, char **cmdline)
+{
+	struct kexec_res *res;
+	int ret = -1;
+
+	*kernel = NULL;
+	*kernel_len = 0;
+
+	list_for_each_entry(res, &cur_phase->res_head, node) {
+		if (res->name == kexec_res_names[0]) {
+			*kernel = res->r->buf;
+			*kernel_len = res->r->data_sz;
+			ret = 0;
+		} else if (res->name == kexec_res_names[1]) {
+			*initrd = res->r->buf;
+			*initrd_len = res->r->data_sz;
+		} else if (res->name == kexec_res_names[2]) {
+			*cmdline = res->r->buf;
+		}
+	}
+
+	return ret;
+}
+
+static void *pe_image_load(struct kimage *image,
+				char *kernel, unsigned long kernel_len,
+				char *initrd, unsigned long initrd_len,
+				char *cmdline, unsigned long cmdline_len)
+{
+	char *parsed_kernel = NULL;
+	unsigned long parsed_len;
+	char *linux_start, *initrd_start, *cmdline_start, *bpf_start;
+	unsigned long linux_sz, initrd_sz, cmdline_sz, bpf_sz;
+	struct parsed_phase *phase, *phase_tmp;
+	struct kexec_res *res, *res_tmp;
+	void *ldata;
+	int ret;
+
+	linux_start = kernel;
+	linux_sz = kernel_len;
+	initrd_start = initrd;
+	initrd_sz = initrd_len;
+	cmdline_start = cmdline;
+	cmdline_sz = cmdline_len;
+
+	while (is_valid_format(linux_start, linux_sz) &&
+	       pe_has_bpf_section(linux_start, linux_sz)) {
+		struct kexec_context context;
+
+		get_pe_section(linux_start, ".bpf", &bpf_start, &bpf_sz);
+		if (!!bpf_sz) {
+			/* load and attach bpf-prog */
+			ret = arm_bpf_prog(bpf_start, bpf_sz);
+			if (ret) {
+				pr_err("Fail to load .bpf section\n");
+				ldata = ERR_PTR(ret);
+				goto err;
+			}
+		}
+		cur_phase = alloc_new_phase();
+		if (image->type != KEXEC_TYPE_CRASH)
+			context.kdump = false;
+		else
+			context.kdump = true;
+		context.image = linux_start;
+		context.image_sz = linux_sz;
+		context.initrd = initrd_start;
+		context.initrd_sz = initrd_sz;
+		context.cmdline = cmdline_start;
+		context.cmdline_sz = strlen(cmdline_start);
+		/* bpf-prog fentry, which handle above buffers. */
+		bpf_handle_pefile(&context);
+
+		prepare_nested_pe(&linux_start, &linux_sz, &initrd_start,
+					&initrd_sz, &cmdline_start);
+		/* bpf-prog fentry */
+		bpf_post_handle_pefile(&context);
+		/*
+		 * detach the current bpf-prog from their attachment points.
+		 * It also a point to free any registered interim resource.
+		 * Any resource except attached to phase is interim.
+		 */
+		disarm_bpf_prog();
+	}
+
+	/* the rear of parsed phase contains the result */
+	list_for_each_entry_reverse(phase, &phase_head, head) {
+		if (initrd != NULL && cmdline != NULL && parsed_kernel != NULL)
+			break;
+		list_for_each_entry(res, &phase->res_head, node) {
+			if (!strcmp(res->name, "kernel") && !parsed_kernel) {
+				parsed_kernel = res->r->buf;
+				parsed_len = res->r->data_sz;
+				res->deferred_free = true;
+			} else if (!strcmp(res->name, "initrd") && !initrd) {
+				initrd = res->r->buf;
+				initrd_len = res->r->data_sz;
+				res->deferred_free = true;
+			} else if (!strcmp(res->name, "cmdline") && !cmdline) {
+				cmdline = res->r->buf;
+				cmdline_len = res->r->data_sz;
+				res->deferred_free = true;
+			}
+		}
+
+	}
+
+	if (initrd == NULL || cmdline == NULL || parsed_kernel == NULL) {
+		char *c, buf[64];
+
+		c = buf;
+		if (parsed_kernel == NULL) {
+			strcpy(c, "kernel ");
+			c += strlen("kernel ");
+		}
+		if (initrd == NULL) {
+			strcpy(c, "initrd ");
+			c += strlen("initrd ");
+		}
+		if (cmdline == NULL) {
+			strcpy(c, "cmdline ");
+			c += strlen("cmdline ");
+		}
+		c = '\0';
+		pr_err("Can not extract data for %s", buf);
+		ldata = ERR_PTR(-EINVAL);
+		goto err;
+	}
+	/*
+	 * image's kernel_buf, initrd_buf, cmdline_buf are set. Now they should
+	 * be updated to the new content.
+	 */
+	if (image->kernel_buf != parsed_kernel) {
+		vfree(image->kernel_buf);
+		image->kernel_buf = parsed_kernel;
+		image->kernel_buf_len = parsed_len;
+	}
+	if (image->initrd_buf != initrd) {
+		vfree(image->initrd_buf);
+		image->initrd_buf = initrd;
+		image->initrd_buf_len = initrd_len;
+	}
+	if (image->cmdline_buf != cmdline) {
+		kfree(image->cmdline_buf);
+		image->cmdline_buf = cmdline;
+		image->cmdline_buf_len = cmdline_len;
+	}
+	ret = arch_kexec_kernel_image_probe(image, image->kernel_buf,
+					    image->kernel_buf_len);
+	if (ret) {
+		pr_err("Fail to find suitable image loader\n");
+		ldata = ERR_PTR(ret);
+		goto err;
+	}
+	ldata = kexec_image_load_default(image);
+	if (IS_ERR(ldata)) {
+		pr_err("architecture code fails to load image\n");
+		goto err;
+	}
+	image->image_loader_data = ldata;
+
+err:
+	list_for_each_entry_safe(phase, phase_tmp, &phase_head, head) {
+		list_for_each_entry_safe(res, res_tmp, &phase->res_head, node) {
+			list_del(&res->node);
+			/* defer to kimage_file_post_load_cleanup() */
+			if (res->deferred_free) {
+				res->r->buf = NULL;
+				res->r->buf_sz = 0;
+			}
+			mem_range_result_put(res->r);
+			kfree(res);
+		}
+		list_del(&phase->head);
+		kfree(phase);
+	}
+
+	return ldata;
+}
+
+const struct kexec_file_ops kexec_pe_image_ops = {
+	.probe = pe_image_probe,
+	.load = pe_image_load,
+#ifdef CONFIG_KEXEC_IMAGE_VERIFY_SIG
+	.verify_sig = kexec_kernel_verify_pe_sig,
+#endif
+};
-- 
2.49.0


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

* [PATCHv3 6/9] kexec: Integrate with the introduced bpf kfuncs
  2025-05-29  4:17 [PATCHv3 0/9] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
                   ` (4 preceding siblings ...)
  2025-05-29  4:17 ` [PATCHv3 5/9] kexec: Introduce kexec_pe_image to parse and load PE file Pingfan Liu
@ 2025-05-29  4:17 ` Pingfan Liu
  2025-05-29  4:17 ` [PATCHv3 7/9] kexec: Introduce a bpf-prog lskel to parse PE file Pingfan Liu
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 20+ messages in thread
From: Pingfan Liu @ 2025-05-29  4:17 UTC (permalink / raw)
  To: kexec
  Cc: 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, bpf

First, register as a listener on bpf_copy_to_kernel() Second, in order
that the hooked bpf-prog can call the sleepable kfuncs,
bpf_handle_pefile and bpf_post_handle_pefile should also be marked as
KF_SLEEPABLE to allow that behavior.

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

diff --git a/kernel/kexec_pe_image.c b/kernel/kexec_pe_image.c
index 3097efccb8502..e49d6db3c329d 100644
--- a/kernel/kexec_pe_image.c
+++ b/kernel/kexec_pe_image.c
@@ -52,6 +52,43 @@ static struct parsed_phase *alloc_new_phase(void)
 	return phase;
 }
 
+/*
+ * @name should be one of : kernel, initrd, cmdline
+ */
+static int bpf_kexec_carrier(const char *name, struct mem_range_result *r)
+{
+	struct kexec_res *res;
+
+	if (!r || !name)
+		return -EINVAL;
+
+	res = kzalloc(sizeof(struct kexec_res), GFP_KERNEL);
+	if (!res)
+		return -ENOMEM;
+	res->name = kstrdup(name, GFP_KERNEL);
+	kref_get(&r->ref);
+	res->r = r;
+
+	INIT_LIST_HEAD(&res->node);
+	list_add_tail(&res->node, &cur_phase->res_head);
+	return 0;
+}
+
+static struct carrier_listener kexec_res_listener[3] = {
+	{ .name = "kernel",
+	  .kmalloc = false,
+	  .handler = bpf_kexec_carrier,
+	},
+	{ .name = "initrd",
+	  .kmalloc = false,
+	  .handler = bpf_kexec_carrier,
+	},
+	{ .name = "cmdline",
+	  .kmalloc = true,
+	  .handler = bpf_kexec_carrier,
+	},
+};
+
 static bool is_valid_pe(const char *kernel_buf, unsigned long kernel_len)
 {
 	struct mz_hdr *mz;
@@ -161,6 +198,22 @@ __attribute__((used, optimize("O0"))) void bpf_post_handle_pefile(struct kexec_c
 {
 }
 
+BTF_KFUNCS_START(kexec_modify_return_ids)
+BTF_ID_FLAGS(func, bpf_handle_pefile, KF_SLEEPABLE)
+BTF_ID_FLAGS(func, bpf_post_handle_pefile, 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);
+
 /*
  * PE file may be nested and should be unfold one by one.
  * Query 'kernel', 'initrd', 'cmdline' in cur_phase, as they are inputs for the
@@ -212,6 +265,9 @@ static void *pe_image_load(struct kimage *image,
 	cmdline_start = cmdline;
 	cmdline_sz = cmdline_len;
 
+	for (int i = 0; i < ARRAY_SIZE(kexec_res_listener); i++)
+		register_carrier_listener(&kexec_res_listener[i]);
+
 	while (is_valid_format(linux_start, linux_sz) &&
 	       pe_has_bpf_section(linux_start, linux_sz)) {
 		struct kexec_context context;
@@ -252,6 +308,9 @@ static void *pe_image_load(struct kimage *image,
 		disarm_bpf_prog();
 	}
 
+	for (int i = 0; i < ARRAY_SIZE(kexec_res_listener); i++)
+		unregister_carrier_listener(kexec_res_listener[i].name);
+
 	/* the rear of parsed phase contains the result */
 	list_for_each_entry_reverse(phase, &phase_head, head) {
 		if (initrd != NULL && cmdline != NULL && parsed_kernel != NULL)
-- 
2.49.0


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

* [PATCHv3 7/9] kexec: Introduce a bpf-prog lskel to parse PE file
  2025-05-29  4:17 [PATCHv3 0/9] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
                   ` (5 preceding siblings ...)
  2025-05-29  4:17 ` [PATCHv3 6/9] kexec: Integrate with the introduced bpf kfuncs Pingfan Liu
@ 2025-05-29  4:17 ` Pingfan Liu
  2025-05-29  4:17 ` [PATCHv3 8/9] kexec: Integrate bpf light skeleton to load zboot image Pingfan Liu
  2025-05-29  4:17 ` [PATCHv3 9/9] arm64/kexec: Add PE image format support Pingfan Liu
  8 siblings, 0 replies; 20+ messages in thread
From: Pingfan Liu @ 2025-05-29  4:17 UTC (permalink / raw)
  To: kexec
  Cc: 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, bpf

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 bss;
                struct bpf_map_desc rodata_str1_1;

two progs:
        SEC("fentry.s/bpf_handle_pefile")
        SEC("fentry.s/bpf_post_handle_pefile")

They are fixed and provided for all kinds of bpf-prog which interacts
with the kexec kernel 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                    |  57 +++
 kernel/kexec_bpf/kexec_pe_parser_bpf.c       |  65 +++
 kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h | 431 +++++++++++++++++++
 3 files changed, 553 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

diff --git a/kernel/kexec_bpf/Makefile b/kernel/kexec_bpf/Makefile
new file mode 100644
index 0000000000000..b772e78464f48
--- /dev/null
+++ b/kernel/kexec_bpf/Makefile
@@ -0,0 +1,57 @@
+# 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
+
+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: 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)/kexec_pe_parser_bpf.o: kexec_pe_parser_bpf.c $(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..ef2608c932aa5
--- /dev/null
+++ b/kernel/kexec_bpf/kexec_pe_parser_bpf.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_core_read.h>
+#include <bpf/bpf_endian.h>
+#include <bpf/bpf_tracing.h>
+
+
+/* 1GB =  1^28 * sizeof(__uint) */
+#define MAX_BUF_SIZE	(1 << 30)
+/* 512MB is big enough to hold either kernel or initramfs */
+#define MAX_RECORD_SIZE	(1 << 28)
+
+#define KEXEC_RES_KERNEL_NAME "kernel"
+#define KEXEC_RES_INITRD_NAME "initrd"
+#define KEXEC_RES_CMDLINE_NAME "cmdline"
+
+/* ringbuf is safe since the user space has no write access to them */
+struct {
+	__uint(type, BPF_MAP_TYPE_RINGBUF);
+	__uint(max_entries, MAX_BUF_SIZE >> 2);
+} ringbuf_1 SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_RINGBUF);
+	__uint(max_entries, MAX_BUF_SIZE >> 2);
+} ringbuf_2 SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_RINGBUF);
+	__uint(max_entries, MAX_BUF_SIZE >> 2);
+} ringbuf_3 SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_RINGBUF);
+	__uint(max_entries, MAX_BUF_SIZE >> 2);
+} ringbuf_4 SEC(".maps");
+
+char LICENSE[] SEC("license") = "GPL";
+
+/*
+ * This function ensures that the sections .rodata, .data .bss and .rodata.str1.1
+ * are created for a bpf prog.
+ */
+__attribute__((used)) static int dummy(void)
+{
+	static const char res_kernel[16] __attribute__((used, section(".rodata"))) = KEXEC_RES_KERNEL_NAME;
+	static char local_name[16] __attribute__((used, section(".data"))) = KEXEC_RES_CMDLINE_NAME;
+	static char res_cmdline[16] __attribute__((used, section(".bss")));
+
+	__builtin_memcpy(local_name, KEXEC_RES_INITRD_NAME, 16);
+	return __builtin_memcmp(local_name, res_kernel, 4);
+}
+
+SEC("fentry.s/bpf_handle_pefile")
+__attribute__((used)) int BPF_PROG(parse_pe, struct kexec_context *context)
+{
+	return 0;
+}
+
+SEC("fentry.s/bpf_post_handle_pefile")
+__attribute__((used)) int BPF_PROG(post_parse_pe, struct kexec_context *context)
+{
+	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..5fc3609d3ad50
--- /dev/null
+++ b/kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h
@@ -0,0 +1,431 @@
+/* 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;
+		struct bpf_prog_desc post_parse_pe;
+	} progs;
+	struct {
+		int parse_pe_fd;
+		int post_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__post_parse_pe__attach(struct kexec_pe_parser_bpf *skel)
+{
+	int prog_fd = skel->progs.post_parse_pe.prog_fd;
+	int fd = skel_raw_tracepoint_open(NULL, prog_fd);
+
+	if (fd > 0)
+		skel->links.post_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);
+	ret = ret < 0 ? ret : kexec_pe_parser_bpf__post_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);
+	skel_closenz(skel->links.post_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->progs.post_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\x04\x03\0\0\x04\x03\0\0\x95\x02\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\0\0\0\x10\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\x0d\x02\0\0\0\x52\0\0\0\0\0\
+\0\x0c\x0f\0\0\0\0\0\0\0\0\0\0\x02\x12\0\0\0\x2d\x01\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\x40\x01\0\0\x11\0\0\0\x44\x01\0\0\x01\
+\0\0\x0c\x13\0\0\0\0\0\0\0\x01\0\0\x0d\x02\0\0\0\x40\x01\0\0\x11\0\0\0\xb4\x01\
+\0\0\x01\0\0\x0c\x15\0\0\0\x33\x02\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\x17\0\0\0\x04\0\0\0\x04\0\0\0\x38\x02\0\0\0\0\0\x0e\x18\0\0\
+\0\x01\0\0\0\0\0\0\0\0\0\0\x0a\x17\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x1a\0\0\0\
+\x04\0\0\0\x10\0\0\0\x40\x02\0\0\0\0\0\x0e\x1b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\
+\0\0\0\0\x17\0\0\0\x04\0\0\0\x10\0\0\0\x51\x02\0\0\0\0\0\x0e\x1d\0\0\0\0\0\0\0\
+\x62\x02\0\0\0\0\0\x0e\x1d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x17\0\0\0\
+\x04\0\0\0\x07\0\0\0\x74\x02\0\0\x01\0\0\x0f\x10\0\0\0\x1f\0\0\0\0\0\0\0\x10\0\
+\0\0\x79\x02\0\0\x01\0\0\x0f\x10\0\0\0\x1e\0\0\0\0\0\0\0\x10\0\0\0\x7f\x02\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\x85\x02\0\0\x01\
+\0\0\x0f\x10\0\0\0\x1c\0\0\0\0\0\0\0\x10\0\0\0\x8d\x02\0\0\x01\0\0\x0f\x04\0\0\
+\0\x19\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\x64\x75\x6d\x6d\x79\0\x2e\x74\x65\x78\x74\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\x73\x74\x61\x74\
+\x69\x63\x20\x69\x6e\x74\x20\x64\x75\x6d\x6d\x79\x28\x76\x6f\x69\x64\x29\0\x09\
+\x5f\x5f\x62\x75\x69\x6c\x74\x69\x6e\x5f\x6d\x65\x6d\x63\x70\x79\x28\x6c\x6f\
+\x63\x61\x6c\x5f\x6e\x61\x6d\x65\x2c\x20\x4b\x45\x58\x45\x43\x5f\x52\x45\x53\
+\x5f\x49\x4e\x49\x54\x52\x44\x5f\x4e\x41\x4d\x45\x2c\x20\x31\x36\x29\x3b\0\x09\
+\x72\x65\x74\x75\x72\x6e\x20\x5f\x5f\x62\x75\x69\x6c\x74\x69\x6e\x5f\x6d\x65\
+\x6d\x63\x6d\x70\x28\x6c\x6f\x63\x61\x6c\x5f\x6e\x61\x6d\x65\x2c\x20\x72\x65\
+\x73\x5f\x6b\x65\x72\x6e\x65\x6c\x2c\x20\x34\x29\x3b\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\x62\x70\x66\x5f\x68\
+\x61\x6e\x64\x6c\x65\x5f\x70\x65\x66\x69\x6c\x65\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\x29\0\x70\x6f\x73\x74\x5f\x70\x61\x72\
+\x73\x65\x5f\x70\x65\0\x66\x65\x6e\x74\x72\x79\x2e\x73\x2f\x62\x70\x66\x5f\x70\
+\x6f\x73\x74\x5f\x68\x61\x6e\x64\x6c\x65\x5f\x70\x65\x66\x69\x6c\x65\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\x6f\x73\x74\x5f\
+\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\x29\0\x63\x68\x61\x72\0\x4c\x49\x43\x45\x4e\x53\x45\0\x64\x75\x6d\x6d\x79\
+\x2e\x72\x65\x73\x5f\x6b\x65\x72\x6e\x65\x6c\0\x64\x75\x6d\x6d\x79\x2e\x6c\x6f\
+\x63\x61\x6c\x5f\x6e\x61\x6d\x65\0\x64\x75\x6d\x6d\x79\x2e\x72\x65\x73\x5f\x63\
+\x6d\x64\x6c\x69\x6e\x65\0\x2e\x62\x73\x73\0\x2e\x64\x61\x74\x61\0\x2e\x6d\x61\
+\x70\x73\0\x2e\x72\x6f\x64\x61\x74\x61\0\x6c\x69\x63\x65\x6e\x73\x65\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb1\x05\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\0\0\x10\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\0\0\x10\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\0\0\x10\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\0\0\x10\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\x24\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x6b\
+\x65\x72\x6e\x65\x6c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\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\x22\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\x63\x6d\x64\x6c\x69\x6e\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\0\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\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\x02\0\0\0\x04\0\0\0\x07\0\0\0\x01\0\0\0\x80\
+\0\0\0\0\0\0\0\0\0\0\0\x2e\x72\x6f\x64\x61\x74\x61\x2e\x73\x74\x72\x31\x2e\x31\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x69\x6e\x69\x74\
+\x72\x64\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\x14\0\0\0\0\0\0\0\x5e\0\0\0\x68\x01\0\0\x1b\xe0\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\x62\x70\x66\x5f\x68\x61\x6e\x64\x6c\x65\x5f\x70\x65\x66\x69\x6c\x65\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\x16\
+\0\0\0\0\0\0\0\x5e\0\0\0\xe2\x01\0\0\x1b\xf8\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\
+\x6f\x73\x74\x5f\x70\x61\x72\x73\x65\x5f\x70\x65\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\x62\
+\x70\x66\x5f\x70\x6f\x73\x74\x5f\x68\x61\x6e\x64\x6c\x65\x5f\x70\x65\x66\x69\
+\x6c\x65\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\x41\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\x61\xa1\x84\xff\0\0\0\0\xd5\x01\x01\0\0\
+\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa1\x88\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\xd0\x0a\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\xcc\x0a\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\xc0\x0a\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\
+\xb8\x0a\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\xb8\x0a\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\xa7\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\xe4\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\xd8\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\x9a\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\x2c\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\x20\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\x8b\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\x74\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\x68\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\x7c\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\xbc\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\xb0\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\x6d\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\x28\x0c\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\x04\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\xf8\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\x5a\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\x40\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\x10\0\0\0\x61\x20\0\0\0\0\0\0\x18\x61\0\0\0\
+\0\0\0\0\0\0\0\x58\x0c\0\0\x63\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x50\
+\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x60\x0c\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\
+\0\0\0\0\0\0\0\0\0\x40\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x68\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\x58\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\x36\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\x78\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\x78\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\x29\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\xb0\x0c\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\x8c\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\x80\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\x19\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\xc8\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\x14\0\0\0\x61\x20\0\0\0\0\0\0\x18\x61\
+\0\0\0\0\0\0\0\0\0\0\xe0\x0c\0\0\x63\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\xe8\x0c\0\0\x7b\x01\0\0\0\0\0\0\x18\
+\x60\0\0\0\0\0\0\0\0\0\0\xc8\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xf0\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\xe0\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\xf5\
+\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\x30\x0d\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\x0c\x0d\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\0\x0d\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\xe5\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\x48\x0d\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\x60\
+\x0d\0\0\x63\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x58\x0d\0\0\x18\x61\0\
+\0\0\0\0\0\0\0\0\0\x68\x0d\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\
+\x48\x0d\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x70\x0d\0\0\x7b\x01\0\0\0\0\0\0\xb7\
+\x01\0\0\x02\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x60\x0d\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\xc1\xfe\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\x8c\x0d\0\0\x63\x01\
+\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x80\x0d\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\xb5\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\xc8\x0d\0\0\xb7\x02\0\
+\0\x07\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\xd8\x0d\0\0\x63\x01\0\0\0\0\0\
+\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xd0\x0d\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xe0\x0d\
+\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xc8\x0d\0\0\x18\x61\0\0\0\
+\0\0\0\0\0\0\0\xe8\x0d\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\xd8\x0d\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\x91\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\xf8\x0d\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\xf8\x0d\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\x84\xfe\0\0\0\0\x18\
+\x60\0\0\0\0\0\0\0\0\0\0\0\x0e\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x40\x0e\0\0\x7b\
+\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x08\x0e\0\0\x18\x61\0\0\0\0\0\0\0\
+\0\0\0\x38\x0e\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x18\x0e\0\0\
+\x18\x61\0\0\0\0\0\0\0\0\0\0\x80\x0e\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\
+\0\0\0\0\0\x20\x0e\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x90\x0e\0\0\x7b\x01\0\0\0\0\
+\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x30\x0e\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xb0\
+\x0e\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\xa8\x0e\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\x48\x0e\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\x4c\x0e\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\x50\x0e\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\x78\x0e\0\0\x63\x01\0\0\0\0\0\0\x18\x61\0\0\
+\0\0\0\0\0\0\0\0\xc0\x0e\0\0\xb7\x02\0\0\x12\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\x4e\xfe\0\0\0\
+\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x30\x0e\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\x30\x0e\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\xa0\x0e\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\x3c\xfe\0\0\0\0\x63\x7a\
+\x80\xff\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xd8\x0e\0\0\x18\x61\0\0\0\0\0\0\0\
+\0\0\0\x18\x0f\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xe0\x0e\0\0\
+\x18\x61\0\0\0\0\0\0\0\0\0\0\x10\x0f\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\
+\0\0\0\0\0\xf0\x0e\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x58\x0f\0\0\x7b\x01\0\0\0\0\
+\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xf8\x0e\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x68\
+\x0f\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x08\x0f\0\0\x18\x61\0\
+\0\0\0\0\0\0\0\0\0\x88\x0f\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\x80\x0f\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\x20\x0f\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\x24\x0f\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\x28\x0f\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\x50\x0f\0\0\x63\x01\0\
+\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x98\x0f\0\0\xb7\x02\0\0\x17\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\x05\xfe\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x08\x0f\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\x08\x0f\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\x78\x0f\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\xf3\
+\xfd\0\0\0\0\x63\x7a\x84\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\x61\xa0\x84\xff\0\0\0\0\x63\x06\x9c\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__ */
-- 
2.49.0


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

* [PATCHv3 8/9] kexec: Integrate bpf light skeleton to load zboot image
  2025-05-29  4:17 [PATCHv3 0/9] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
                   ` (6 preceding siblings ...)
  2025-05-29  4:17 ` [PATCHv3 7/9] kexec: Introduce a bpf-prog lskel to parse PE file Pingfan Liu
@ 2025-05-29  4:17 ` Pingfan Liu
  2025-06-25 18:10   ` Philipp Rudo
  2025-05-29  4:17 ` [PATCHv3 9/9] arm64/kexec: Add PE image format support Pingfan Liu
  8 siblings, 1 reply; 20+ messages in thread
From: Pingfan Liu @ 2025-05-29  4:17 UTC (permalink / raw)
  To: kexec
  Cc: 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, bpf

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 bss;
                    struct bpf_map_desc rodata_str1_1;
    two progs:
            SEC("fentry.s/bpf_handle_pefile")
            SEC("fentry.s/bpf_post_handle_pefile")

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 | 292 +------------------
 kernel/kexec_pe_image.c                      |  70 +++++
 4 files changed, 83 insertions(+), 288 deletions(-)

diff --git a/kernel/Makefile b/kernel/Makefile
index ab82d73d8ce81..7c384bf62535a 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -139,6 +139,7 @@ obj-$(CONFIG_RESOURCE_KUNIT_TEST) += resource_kunit.o
 obj-$(CONFIG_SYSCTL_KUNIT_TEST) += sysctl-test.o
 
 CFLAGS_stackleak.o += $(DISABLE_STACKLEAK_PLUGIN)
+CFLAGS_kexec_pe_image.o += -I$(srctree)/tools/lib
 obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += stackleak.o
 KASAN_SANITIZE_stackleak.o := n
 KCSAN_SANITIZE_stackleak.o := n
diff --git a/kernel/kexec_bpf/Makefile b/kernel/kexec_bpf/Makefile
index b772e78464f48..91d3fb8b4caf7 100644
--- a/kernel/kexec_bpf/Makefile
+++ b/kernel/kexec_bpf/Makefile
@@ -36,6 +36,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)/kexec_pe_parser_bpf.o: kexec_pe_parser_bpf.c $(BPFOBJ) | $(OUTPUT)
diff --git a/kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h b/kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h
index 5fc3609d3ad50..88a3aa90d5e04 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;
@@ -103,297 +105,11 @@ 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\x04\x03\0\0\x04\x03\0\0\x95\x02\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\0\0\0\x10\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\x0d\x02\0\0\0\x52\0\0\0\0\0\
-\0\x0c\x0f\0\0\0\0\0\0\0\0\0\0\x02\x12\0\0\0\x2d\x01\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\x40\x01\0\0\x11\0\0\0\x44\x01\0\0\x01\
-\0\0\x0c\x13\0\0\0\0\0\0\0\x01\0\0\x0d\x02\0\0\0\x40\x01\0\0\x11\0\0\0\xb4\x01\
-\0\0\x01\0\0\x0c\x15\0\0\0\x33\x02\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\x17\0\0\0\x04\0\0\0\x04\0\0\0\x38\x02\0\0\0\0\0\x0e\x18\0\0\
-\0\x01\0\0\0\0\0\0\0\0\0\0\x0a\x17\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x1a\0\0\0\
-\x04\0\0\0\x10\0\0\0\x40\x02\0\0\0\0\0\x0e\x1b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\
-\0\0\0\0\x17\0\0\0\x04\0\0\0\x10\0\0\0\x51\x02\0\0\0\0\0\x0e\x1d\0\0\0\0\0\0\0\
-\x62\x02\0\0\0\0\0\x0e\x1d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x17\0\0\0\
-\x04\0\0\0\x07\0\0\0\x74\x02\0\0\x01\0\0\x0f\x10\0\0\0\x1f\0\0\0\0\0\0\0\x10\0\
-\0\0\x79\x02\0\0\x01\0\0\x0f\x10\0\0\0\x1e\0\0\0\0\0\0\0\x10\0\0\0\x7f\x02\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\x85\x02\0\0\x01\
-\0\0\x0f\x10\0\0\0\x1c\0\0\0\0\0\0\0\x10\0\0\0\x8d\x02\0\0\x01\0\0\x0f\x04\0\0\
-\0\x19\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\x64\x75\x6d\x6d\x79\0\x2e\x74\x65\x78\x74\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\x73\x74\x61\x74\
-\x69\x63\x20\x69\x6e\x74\x20\x64\x75\x6d\x6d\x79\x28\x76\x6f\x69\x64\x29\0\x09\
-\x5f\x5f\x62\x75\x69\x6c\x74\x69\x6e\x5f\x6d\x65\x6d\x63\x70\x79\x28\x6c\x6f\
-\x63\x61\x6c\x5f\x6e\x61\x6d\x65\x2c\x20\x4b\x45\x58\x45\x43\x5f\x52\x45\x53\
-\x5f\x49\x4e\x49\x54\x52\x44\x5f\x4e\x41\x4d\x45\x2c\x20\x31\x36\x29\x3b\0\x09\
-\x72\x65\x74\x75\x72\x6e\x20\x5f\x5f\x62\x75\x69\x6c\x74\x69\x6e\x5f\x6d\x65\
-\x6d\x63\x6d\x70\x28\x6c\x6f\x63\x61\x6c\x5f\x6e\x61\x6d\x65\x2c\x20\x72\x65\
-\x73\x5f\x6b\x65\x72\x6e\x65\x6c\x2c\x20\x34\x29\x3b\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\x62\x70\x66\x5f\x68\
-\x61\x6e\x64\x6c\x65\x5f\x70\x65\x66\x69\x6c\x65\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\x29\0\x70\x6f\x73\x74\x5f\x70\x61\x72\
-\x73\x65\x5f\x70\x65\0\x66\x65\x6e\x74\x72\x79\x2e\x73\x2f\x62\x70\x66\x5f\x70\
-\x6f\x73\x74\x5f\x68\x61\x6e\x64\x6c\x65\x5f\x70\x65\x66\x69\x6c\x65\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\x6f\x73\x74\x5f\
-\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\x29\0\x63\x68\x61\x72\0\x4c\x49\x43\x45\x4e\x53\x45\0\x64\x75\x6d\x6d\x79\
-\x2e\x72\x65\x73\x5f\x6b\x65\x72\x6e\x65\x6c\0\x64\x75\x6d\x6d\x79\x2e\x6c\x6f\
-\x63\x61\x6c\x5f\x6e\x61\x6d\x65\0\x64\x75\x6d\x6d\x79\x2e\x72\x65\x73\x5f\x63\
-\x6d\x64\x6c\x69\x6e\x65\0\x2e\x62\x73\x73\0\x2e\x64\x61\x74\x61\0\x2e\x6d\x61\
-\x70\x73\0\x2e\x72\x6f\x64\x61\x74\x61\0\x6c\x69\x63\x65\x6e\x73\x65\0\0\0\0\0\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb1\x05\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\0\0\x10\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\0\0\x10\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\0\0\x10\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\0\0\x10\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\x24\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x6b\
-\x65\x72\x6e\x65\x6c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\0\0\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\x22\0\0\0\0\0\0\0\0\0\0\0\0\
-\0\0\0\x63\x6d\x64\x6c\x69\x6e\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\0\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\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\x02\0\0\0\x04\0\0\0\x07\0\0\0\x01\0\0\0\x80\
-\0\0\0\0\0\0\0\0\0\0\0\x2e\x72\x6f\x64\x61\x74\x61\x2e\x73\x74\x72\x31\x2e\x31\
-\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x69\x6e\x69\x74\
-\x72\x64\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\x14\0\0\0\0\0\0\0\x5e\0\0\0\x68\x01\0\0\x1b\xe0\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\x62\x70\x66\x5f\x68\x61\x6e\x64\x6c\x65\x5f\x70\x65\x66\x69\x6c\x65\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\x16\
-\0\0\0\0\0\0\0\x5e\0\0\0\xe2\x01\0\0\x1b\xf8\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\
-\x6f\x73\x74\x5f\x70\x61\x72\x73\x65\x5f\x70\x65\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\x62\
-\x70\x66\x5f\x70\x6f\x73\x74\x5f\x68\x61\x6e\x64\x6c\x65\x5f\x70\x65\x66\x69\
-\x6c\x65\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\x41\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\x61\xa1\x84\xff\0\0\0\0\xd5\x01\x01\0\0\
-\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa1\x88\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\xd0\x0a\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\xcc\x0a\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\xc0\x0a\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\
-\xb8\x0a\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\xb8\x0a\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\xa7\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\xe4\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\xd8\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\x9a\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\x2c\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\x20\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\x8b\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\x74\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\x68\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\x7c\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\xbc\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\xb0\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\x6d\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\x28\x0c\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\x04\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\xf8\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\x5a\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\x40\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\x10\0\0\0\x61\x20\0\0\0\0\0\0\x18\x61\0\0\0\
-\0\0\0\0\0\0\0\x58\x0c\0\0\x63\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x50\
-\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x60\x0c\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\
-\0\0\0\0\0\0\0\0\0\x40\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x68\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\x58\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\x36\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\x78\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\x78\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\x29\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\xb0\x0c\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\x8c\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\x80\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\x19\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\xc8\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\x14\0\0\0\x61\x20\0\0\0\0\0\0\x18\x61\
-\0\0\0\0\0\0\0\0\0\0\xe0\x0c\0\0\x63\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\xe8\x0c\0\0\x7b\x01\0\0\0\0\0\0\x18\
-\x60\0\0\0\0\0\0\0\0\0\0\xc8\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xf0\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\xe0\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\xf5\
-\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\x30\x0d\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\x0c\x0d\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\0\x0d\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\xe5\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\x48\x0d\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\x60\
-\x0d\0\0\x63\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x58\x0d\0\0\x18\x61\0\
-\0\0\0\0\0\0\0\0\0\x68\x0d\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\
-\x48\x0d\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x70\x0d\0\0\x7b\x01\0\0\0\0\0\0\xb7\
-\x01\0\0\x02\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x60\x0d\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\xc1\xfe\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\x8c\x0d\0\0\x63\x01\
-\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x80\x0d\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\xb5\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\xc8\x0d\0\0\xb7\x02\0\
-\0\x07\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\xd8\x0d\0\0\x63\x01\0\0\0\0\0\
-\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xd0\x0d\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xe0\x0d\
-\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xc8\x0d\0\0\x18\x61\0\0\0\
-\0\0\0\0\0\0\0\xe8\x0d\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\xd8\x0d\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\x91\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\xf8\x0d\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\xf8\x0d\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\x84\xfe\0\0\0\0\x18\
-\x60\0\0\0\0\0\0\0\0\0\0\0\x0e\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x40\x0e\0\0\x7b\
-\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x08\x0e\0\0\x18\x61\0\0\0\0\0\0\0\
-\0\0\0\x38\x0e\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x18\x0e\0\0\
-\x18\x61\0\0\0\0\0\0\0\0\0\0\x80\x0e\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\
-\0\0\0\0\0\x20\x0e\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x90\x0e\0\0\x7b\x01\0\0\0\0\
-\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x30\x0e\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xb0\
-\x0e\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\xa8\x0e\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\x48\x0e\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\x4c\x0e\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\x50\x0e\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\x78\x0e\0\0\x63\x01\0\0\0\0\0\0\x18\x61\0\0\
-\0\0\0\0\0\0\0\0\xc0\x0e\0\0\xb7\x02\0\0\x12\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\x4e\xfe\0\0\0\
-\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x30\x0e\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\x30\x0e\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\xa0\x0e\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\x3c\xfe\0\0\0\0\x63\x7a\
-\x80\xff\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xd8\x0e\0\0\x18\x61\0\0\0\0\0\0\0\
-\0\0\0\x18\x0f\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xe0\x0e\0\0\
-\x18\x61\0\0\0\0\0\0\0\0\0\0\x10\x0f\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\
-\0\0\0\0\0\xf0\x0e\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x58\x0f\0\0\x7b\x01\0\0\0\0\
-\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xf8\x0e\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x68\
-\x0f\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x08\x0f\0\0\x18\x61\0\
-\0\0\0\0\0\0\0\0\0\x88\x0f\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\x80\x0f\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\x20\x0f\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\x24\x0f\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\x28\x0f\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\x50\x0f\0\0\x63\x01\0\
-\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x98\x0f\0\0\xb7\x02\0\0\x17\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\x05\xfe\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x08\x0f\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\x08\x0f\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\x78\x0f\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\xf3\
-\xfd\0\0\0\0\x63\x7a\x84\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\x61\xa0\x84\xff\0\0\0\0\x63\x06\x9c\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_pe_image.c b/kernel/kexec_pe_image.c
index e49d6db3c329d..f47c1e46dba97 100644
--- a/kernel/kexec_pe_image.c
+++ b/kernel/kexec_pe_image.c
@@ -13,6 +13,7 @@
 #include <linux/kernel.h>
 #include <linux/vmalloc.h>
 #include <linux/kexec.h>
+#include <linux/elf.h>
 #include <linux/pe.h>
 #include <linux/string.h>
 #include <linux/bpf.h>
@@ -21,6 +22,7 @@
 #include <asm/image.h>
 #include <asm/memory.h>
 
+#include "kexec_bpf/kexec_pe_parser_bpf.lskel.h"
 
 static LIST_HEAD(phase_head);
 
@@ -163,14 +165,82 @@ static bool pe_has_bpf_section(char *file_buf, unsigned long pe_sz)
 	return true;
 }
 
+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, *symtab_shdr, *strtab_shdr, *dst_shdr;
+	Elf64_Sym *sym, *symtab = NULL;
+	char *strtab = NULL;
+	void *symbol_data = NULL;
+	int i;
+
+	symtab_shdr = strtab_shdr = NULL;
+	if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
+		pr_err("Not a valid ELF file\n");
+		goto out;
+	}
+
+	shdr = (struct elf_shdr *)(elf_data + ehdr->e_shoff);
+	for (i = 0; i < ehdr->e_shnum; i++) {
+		if (shdr[i].sh_type == SHT_SYMTAB)
+			symtab_shdr = &shdr[i];
+		else if (shdr[i].sh_type == SHT_STRTAB && i != ehdr->e_shstrndx)
+			strtab_shdr = &shdr[i];
+	}
+
+	if (!symtab_shdr || !strtab_shdr) {
+		pr_err("Symbol table or string table not found\n");
+		goto out;
+	}
+	symtab = (Elf64_Sym *)(elf_data + symtab_shdr->sh_offset);
+	strtab = (char *)(elf_data + strtab_shdr->sh_offset);
+	for (i = 0; i < symtab_shdr->sh_size / sizeof(Elf64_Sym); i++) {
+		sym = &symtab[i];
+		if (strcmp(&strtab[sym->st_name], symbol_name) == 0) {
+			if (sym->st_shndx >= SHN_LORESERVE)
+				return NULL; // No section data for these
+			dst_shdr = &shdr[sym->st_shndx];
+			symbol_data = (void *)(elf_data + dst_shdr->sh_offset + sym->st_value);
+			*symbol_size = symtab[i].st_size;
+			break;
+		}
+	}
+
+out:
+	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] 20+ messages in thread

* [PATCHv3 9/9] arm64/kexec: Add PE image format support
  2025-05-29  4:17 [PATCHv3 0/9] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
                   ` (7 preceding siblings ...)
  2025-05-29  4:17 ` [PATCHv3 8/9] kexec: Integrate bpf light skeleton to load zboot image Pingfan Liu
@ 2025-05-29  4:17 ` Pingfan Liu
  2025-05-29 15:34   ` kernel test robot
  8 siblings, 1 reply; 20+ messages in thread
From: Pingfan Liu @ 2025-05-29  4:17 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: 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, kexec, bpf

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 +
 arch/arm64/include/asm/kexec.h         | 1 +
 arch/arm64/kernel/machine_kexec_file.c | 3 +++
 3 files changed, 5 insertions(+)

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index a182295e6f08b..80b365fb7643e 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -1592,6 +1592,7 @@ config ARCH_SELECTS_KEXEC_FILE
 	def_bool y
 	depends on KEXEC_FILE
 	select HAVE_IMA_KEXEC if IMA
+	select KEXEC_PE_IMAGE
 
 config ARCH_SUPPORTS_KEXEC_SIG
 	def_bool y
diff --git a/arch/arm64/include/asm/kexec.h b/arch/arm64/include/asm/kexec.h
index 4d9cc7a76d9ca..d50796bd2f1e6 100644
--- a/arch/arm64/include/asm/kexec.h
+++ b/arch/arm64/include/asm/kexec.h
@@ -120,6 +120,7 @@ struct kimage_arch {
 
 #ifdef CONFIG_KEXEC_FILE
 extern const struct kexec_file_ops kexec_image_ops;
+extern const struct kexec_file_ops kexec_pe_image_ops;
 
 int arch_kimage_file_post_load_cleanup(struct kimage *image);
 #define arch_kimage_file_post_load_cleanup arch_kimage_file_post_load_cleanup
diff --git a/arch/arm64/kernel/machine_kexec_file.c b/arch/arm64/kernel/machine_kexec_file.c
index af1ca875c52ce..7c544c385a9ab 100644
--- a/arch/arm64/kernel/machine_kexec_file.c
+++ b/arch/arm64/kernel/machine_kexec_file.c
@@ -24,6 +24,9 @@
 
 const struct kexec_file_ops * const kexec_file_loaders[] = {
 	&kexec_image_ops,
+#ifdef CONFIG_KEXEC_PE_IMAGE
+	&kexec_pe_image_ops,
+#endif
 	NULL
 };
 
-- 
2.49.0


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

* Re: [PATCHv3 3/9] bpf: Introduce bpf_copy_to_kernel() to buffer the content from bpf-prog
  2025-05-29  4:17 ` [PATCHv3 3/9] bpf: Introduce bpf_copy_to_kernel() to buffer the content from bpf-prog Pingfan Liu
@ 2025-05-29 11:48   ` kernel test robot
  2025-06-25 18:10   ` Philipp Rudo
  1 sibling, 0 replies; 20+ messages in thread
From: kernel test robot @ 2025-05-29 11:48 UTC (permalink / raw)
  To: Pingfan Liu, bpf
  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, KP Singh,
	Stanislav Fomichev, Hao Luo, Jiri Olsa

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 arm64/for-next/core linus/master v6.15 next-20250529]
[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/kexec_file-Make-kexec_image_load_default-global-visible/20250529-122124
base:   https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git net
patch link:    https://lore.kernel.org/r/20250529041744.16458-4-piliu%40redhat.com
patch subject: [PATCHv3 3/9] bpf: Introduce bpf_copy_to_kernel() to buffer the content from bpf-prog
config: riscv-randconfig-001-20250529 (https://download.01.org/0day-ci/archive/20250529/202505291926.IPUSqCEj-lkp@intel.com/config)
compiler: clang version 21.0.0git (https://github.com/llvm/llvm-project f819f46284f2a79790038e1f6649172789734ae8)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250529/202505291926.IPUSqCEj-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/202505291926.IPUSqCEj-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> kernel/bpf/helpers_carrier.c:74:17: warning: no previous prototype for function 'bpf_mem_range_result_put' [-Wmissing-prototypes]
      74 | __bpf_kfunc int bpf_mem_range_result_put(struct mem_range_result *result)
         |                 ^
   kernel/bpf/helpers_carrier.c:74:13: note: declare 'static' if the function is not intended to be used outside of this translation unit
      74 | __bpf_kfunc int bpf_mem_range_result_put(struct mem_range_result *result)
         |             ^
         |             static 
   kernel/bpf/helpers_carrier.c:88:7: warning: variable 'kmalloc' set but not used [-Wunused-but-set-variable]
      88 |         bool kmalloc;
         |              ^
>> kernel/bpf/helpers_carrier.c:82:17: warning: no previous prototype for function 'bpf_copy_to_kernel' [-Wmissing-prototypes]
      82 | __bpf_kfunc int bpf_copy_to_kernel(const char *name, char *buf, int size)
         |                 ^
   kernel/bpf/helpers_carrier.c:82:13: note: declare 'static' if the function is not intended to be used outside of this translation unit
      82 | __bpf_kfunc int bpf_copy_to_kernel(const char *name, char *buf, int size)
         |             ^
         |             static 
>> kernel/bpf/helpers_carrier.c:165:6: warning: variable 'ret' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
     165 |         if (!find_listener(item->str)) {
         |             ^~~~~~~~~~~~~~~~~~~~~~~~~
   kernel/bpf/helpers_carrier.c:174:9: note: uninitialized use occurs here
     174 |         return ret;
         |                ^~~
   kernel/bpf/helpers_carrier.c:165:2: note: remove the 'if' if its condition is always false
     165 |         if (!find_listener(item->str)) {
         |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     166 |                 hash_add(str_listeners, &item->node, hash);
         |                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     167 |         } else {
         |         ~~~~~~
   kernel/bpf/helpers_carrier.c:149:9: note: initialize the variable 'ret' to silence this warning
     149 |         int ret;
         |                ^
         |                 = 0
   4 warnings generated.


vim +/bpf_mem_range_result_put +74 kernel/bpf/helpers_carrier.c

    73	
  > 74	__bpf_kfunc int bpf_mem_range_result_put(struct mem_range_result *result)
    75	{
    76		return mem_range_result_put(result);
    77	}
    78	
    79	/*
    80	 * Cache the content in @buf into kernel
    81	 */
  > 82	__bpf_kfunc int bpf_copy_to_kernel(const char *name, char *buf, int size)
    83	{
    84		struct mem_range_result *range;
    85		struct mem_cgroup *memcg, *old_memcg;
    86		struct str_listener *item;
    87		resource_handler handler;
    88		bool kmalloc;
    89		char *kbuf;
    90		int id, ret = 0;
    91	
    92		id = srcu_read_lock(&srcu);
    93		item = find_listener(name);
    94		if (!item) {
    95			srcu_read_unlock(&srcu, id);
    96			return -EINVAL;
    97		}
    98		kmalloc = item->kmalloc;
    99		handler = item->handler;
   100		srcu_read_unlock(&srcu, id);
   101		memcg = get_mem_cgroup_from_current();
   102		old_memcg = set_active_memcg(memcg);
   103		range = kmalloc(sizeof(struct mem_range_result), GFP_KERNEL);
   104		if (!range) {
   105			pr_err("fail to allocate mem_range_result\n");
   106			ret = -ENOMEM;
   107			goto err;
   108		}
   109	
   110		kref_init(&range->ref);
   111		if (item->kmalloc)
   112			kbuf = kmalloc(size, GFP_KERNEL | __GFP_ACCOUNT);
   113		else
   114			kbuf = __vmalloc(size, GFP_KERNEL | __GFP_ACCOUNT);
   115		if (!kbuf) {
   116			kfree(range);
   117			ret = -ENOMEM;
   118			goto err;
   119		}
   120		ret = copy_from_kernel_nofault(kbuf, buf, size);
   121		if (unlikely(ret < 0)) {
   122			kfree(range);
   123			if (item->kmalloc)
   124				kfree(kbuf);
   125			else
   126				vfree(kbuf);
   127			ret = -EINVAL;
   128			goto err;
   129		}
   130		range->kmalloc = item->kmalloc;
   131		range->buf = kbuf;
   132		range->buf_sz = size;
   133		range->data_sz = size;
   134		range->memcg = memcg;
   135		mem_cgroup_tryget(memcg);
   136		range->status = 0;
   137		ret = handler(name, range);
   138		mem_range_result_put(range);
   139	err:
   140		set_active_memcg(old_memcg);
   141		mem_cgroup_put(memcg);
   142		return ret;
   143	}
   144	
   145	int register_carrier_listener(struct carrier_listener *listener)
   146	{
   147		struct str_listener *item;
   148		unsigned int hash;
   149		int ret;
   150	
   151		if (!listener->name)
   152			return -EINVAL;
   153		item = kmalloc(sizeof(*item), GFP_KERNEL);
   154		if (!item)
   155			return -ENOMEM;
   156		item->str = kstrdup(listener->name, GFP_KERNEL);
   157		if (!item->str) {
   158			kfree(item);
   159			return -ENOMEM;
   160		}
   161		item->handler = listener->handler;
   162		item->kmalloc = listener->kmalloc;
   163		hash = jhash(item->str, strlen(item->str), 0);
   164		mutex_lock(&str_listeners_mutex);
 > 165		if (!find_listener(item->str)) {
   166			hash_add(str_listeners, &item->node, hash);
   167		} else {
   168			kfree(item->str);
   169			kfree(item);
   170			ret = -EBUSY;
   171		}
   172		mutex_unlock(&str_listeners_mutex);
   173	
   174		return ret;
   175	}
   176	EXPORT_SYMBOL(register_carrier_listener);
   177	

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

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

* Re: [PATCHv3 4/9] bpf: Introduce decompressor kfunc
  2025-05-29  4:17 ` [PATCHv3 4/9] bpf: Introduce decompressor kfunc Pingfan Liu
@ 2025-05-29 12:31   ` kernel test robot
  0 siblings, 0 replies; 20+ messages in thread
From: kernel test robot @ 2025-05-29 12:31 UTC (permalink / raw)
  To: Pingfan Liu, bpf
  Cc: oe-kbuild-all, Pingfan Liu, Alexei Starovoitov, Daniel Borkmann,
	John Fastabend, Andrii Nakryiko, Martin KaFai Lau,
	Eduard Zingerman, Song Liu, Yonghong Song, Jeremy Linton,
	Catalin Marinas, Will Deacon, Ard Biesheuvel, Simon Horman,
	Gerd Hoffmann, Vitaly Kuznetsov, Philipp Rudo, Viktor Malik,
	Jan Hendrik Farr, Baoquan He, Dave Young, Andrew Morton,
	Linux Memory Management List, kexec, KP Singh, Stanislav Fomichev,
	Hao Luo, Jiri Olsa

Hi Pingfan,

kernel test robot noticed the following build warnings:

[auto build test WARNING on bpf-next/net]
[also build test WARNING on bpf/master arm64/for-next/core v6.15]
[cannot apply to bpf-next/master linus/master next-20250529]
[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/kexec_file-Make-kexec_image_load_default-global-visible/20250529-122124
base:   https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git net
patch link:    https://lore.kernel.org/r/20250529041744.16458-5-piliu%40redhat.com
patch subject: [PATCHv3 4/9] bpf: Introduce decompressor kfunc
config: s390-randconfig-002-20250529 (https://download.01.org/0day-ci/archive/20250529/202505292046.qJy6UU9z-lkp@intel.com/config)
compiler: s390-linux-gcc (GCC) 12.4.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250529/202505292046.qJy6UU9z-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/202505292046.qJy6UU9z-lkp@intel.com/

All warnings (new ones prefixed by >>, old ones prefixed by <<):

>> WARNING: modpost: vmlinux: section mismatch in reference: bpf_decompress+0x36e (section: .text) -> decompress_method (section: .init.text)

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

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

* Re: [PATCHv3 9/9] arm64/kexec: Add PE image format support
  2025-05-29  4:17 ` [PATCHv3 9/9] arm64/kexec: Add PE image format support Pingfan Liu
@ 2025-05-29 15:34   ` kernel test robot
  0 siblings, 0 replies; 20+ messages in thread
From: kernel test robot @ 2025-05-29 15:34 UTC (permalink / raw)
  To: Pingfan Liu, linux-arm-kernel
  Cc: oe-kbuild-all, Pingfan Liu, Alexei Starovoitov, Daniel Borkmann,
	John Fastabend, Andrii Nakryiko, Martin KaFai Lau,
	Eduard Zingerman, Song Liu, Yonghong Song, Jeremy Linton,
	Catalin Marinas, Will Deacon, Ard Biesheuvel, Simon Horman,
	Gerd Hoffmann, Vitaly Kuznetsov, Philipp Rudo, Viktor Malik,
	Jan Hendrik Farr, Baoquan He, Dave Young, Andrew Morton,
	Linux Memory Management List, kexec, bpf

Hi Pingfan,

kernel test robot noticed the following build errors:

[auto build test ERROR on bpf-next/net]
[also build test ERROR on bpf/master arm64/for-next/core v6.15]
[cannot apply to bpf-next/master linus/master next-20250529]
[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/kexec_file-Make-kexec_image_load_default-global-visible/20250529-122124
base:   https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git net
patch link:    https://lore.kernel.org/r/20250529041744.16458-10-piliu%40redhat.com
patch subject: [PATCHv3 9/9] arm64/kexec: Add PE image format support
config: arm64-randconfig-004-20250529 (https://download.01.org/0day-ci/archive/20250529/202505292305.zHTx5StD-lkp@intel.com/config)
compiler: aarch64-linux-gcc (GCC) 13.3.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250529/202505292305.zHTx5StD-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/202505292305.zHTx5StD-lkp@intel.com/

All errors (new ones prefixed by >>):

   In file included from kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h:6,
                    from kernel/kexec_pe_image.c:25:
   tools/lib/bpf/skel_internal.h: In function 'skel_finalize_map_data':
   tools/lib/bpf/skel_internal.h:155:15: error: implicit declaration of function 'bpf_map_get'; did you mean 'bpf_map_put'? [-Werror=implicit-function-declaration]
     155 |         map = bpf_map_get(fd);
         |               ^~~~~~~~~~~
         |               bpf_map_put
   tools/lib/bpf/skel_internal.h:155:13: warning: assignment to 'struct bpf_map *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
     155 |         map = bpf_map_get(fd);
         |             ^
   kernel/kexec_pe_image.c: In function 'kexec_bpf_prog_run_init':
>> kernel/kexec_pe_image.c:283:16: error: implicit declaration of function 'register_btf_fmodret_id_set'; did you mean 'register_btf_kfunc_id_set'? [-Werror=implicit-function-declaration]
     283 |         return register_btf_fmodret_id_set(&kexec_modify_return_set);
         |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~
         |                register_btf_kfunc_id_set
   kernel/kexec_pe_image.c: In function 'pe_image_load':
   kernel/kexec_pe_image.c:325:44: warning: variable 'cmdline_sz' set but not used [-Wunused-but-set-variable]
     325 |         unsigned long linux_sz, initrd_sz, cmdline_sz, bpf_sz;
         |                                            ^~~~~~~~~~
   cc1: some warnings being treated as errors

Kconfig warnings: (for reference only)
   WARNING: unmet direct dependencies detected for KEXEC_PE_IMAGE
   Depends on [n]: KEXEC_FILE [=y] && DEBUG_INFO_BTF [=n] && BPF_SYSCALL [=n]
   Selected by [y]:
   - ARCH_SELECTS_KEXEC_FILE [=y] && KEXEC_FILE [=y]


vim +283 kernel/kexec_pe_image.c

536de0ba3b982c Pingfan Liu 2025-05-29  280  
536de0ba3b982c Pingfan Liu 2025-05-29  281  static int __init kexec_bpf_prog_run_init(void)
536de0ba3b982c Pingfan Liu 2025-05-29  282  {
536de0ba3b982c Pingfan Liu 2025-05-29 @283  	return register_btf_fmodret_id_set(&kexec_modify_return_set);
536de0ba3b982c Pingfan Liu 2025-05-29  284  }
536de0ba3b982c Pingfan Liu 2025-05-29  285  late_initcall(kexec_bpf_prog_run_init);
536de0ba3b982c Pingfan Liu 2025-05-29  286  

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

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

* Re: [PATCHv3 5/9] kexec: Introduce kexec_pe_image to parse and load PE file
  2025-05-29  4:17 ` [PATCHv3 5/9] kexec: Introduce kexec_pe_image to parse and load PE file Pingfan Liu
@ 2025-06-25 18:09   ` Philipp Rudo
  2025-06-30 13:45     ` Pingfan Liu
  0 siblings, 1 reply; 20+ messages in thread
From: Philipp Rudo @ 2025-06-25 18:09 UTC (permalink / raw)
  To: Pingfan Liu
  Cc: kexec, 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

Hi Pingfan,

On Thu, 29 May 2025 12:17:40 +0800
Pingfan Liu <piliu@redhat.com> wrote:

> 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.
> 
> A new kexec_file_ops is implementation, named pe_image_ops.
> 
> There are some place holder function in this patch. (They will take
> effect after the introduction of kexec bpf light skeleton and bpf
> helpers). Overall the parsing progress is a pipeline, the current
> bpf-prog parser is attached to bpf_handle_pefile(), and detatched at the
> end of the current stage 'disarm_bpf_prog()' the current parsed result
> by the current bpf-prog will be buffered in kernel 'prepare_nested_pe()'
> , and deliver to the next stage.  For each stage, the bpf bytecode is
> extracted from the '.bpf' section in the PE file.
> 
> 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
> ---
>  include/linux/kexec.h   |   1 +
>  kernel/Kconfig.kexec    |   8 +
>  kernel/Makefile         |   1 +
>  kernel/kexec_pe_image.c | 356 ++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 366 insertions(+)
>  create mode 100644 kernel/kexec_pe_image.c
> 
[...]

> diff --git a/kernel/kexec_pe_image.c b/kernel/kexec_pe_image.c
> new file mode 100644
> index 0000000000000..3097efccb8502
> --- /dev/null
> +++ b/kernel/kexec_pe_image.c
> @@ -0,0 +1,356 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Kexec PE image loader
> +
> + * Copyright (C) 2025 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/pe.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>
> +
> +
> +static LIST_HEAD(phase_head);
> +
> +struct parsed_phase {
> +	struct list_head head;
> +	struct list_head res_head;
> +};
> +
> +static struct parsed_phase *cur_phase;
> +
> +static char *kexec_res_names[3] = {"kernel", "initrd", "cmdline"};

Wouldn't it be better to use a enum rather than strings for the
different resources? Especially as in prepare_nested_pe you are
comparing two strings using == instead of strcmp(). So IIUC it should
always return false.

> +struct kexec_res {
> +	struct list_head node;
> +	char *name;
> +	/* The free of buffer is deferred to kimage_file_post_load_cleanup */
> +	bool deferred_free;
> +	struct mem_range_result *r;
> +};
> +
> +static struct parsed_phase *alloc_new_phase(void)
> +{
> +	struct parsed_phase *phase = kzalloc(sizeof(struct parsed_phase), GFP_KERNEL);
> +
> +	INIT_LIST_HEAD(&phase->head);
> +	INIT_LIST_HEAD(&phase->res_head);
> +	list_add_tail(&phase->head, &phase_head);
> +
> +	return phase;
> +}

I must admit I don't fully understand how you are handling the
different phases. In particular I don't understand why you are keeping
all the resources a phase returned once it is finished. The way I see
it those resources are only needed once as input for the next phase. So
it should be sufficient to only keep a single kexec_context and update
it when a phase returns a new resource. The way I see it this should
simplify pe_image_load quite a bit. Or am I missing something?

> +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 != MZ_MAGIC)
> +		return false;
> +	pe = (struct pe_hdr *)(kernel_buf + mz->peaddr);
> +	if (pe->magic != PE_MAGIC)
> +		return false;
> +	if (pe->opt_hdr_size == 0) {
> +		pr_err("optional header is missing\n");
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
> +static bool is_valid_format(const char *kernel_buf, unsigned long kernel_len)
> +{
> +	return is_valid_pe(kernel_buf, kernel_len);
> +}
> +
> +/*
> + * The UEFI Terse Executable (TE) image has MZ header.
> + */
> +static int pe_image_probe(const char *kernel_buf, unsigned long kernel_len)
> +{
> +	return is_valid_pe(kernel_buf, kernel_len) ? 0 : -1;

Every image, at least on x86, is a valid pe file. So we should check
for the .bpf section rather than the header.

> +}
> +
> +static int get_pe_section(char *file_buf, const char *sect_name,

s/get_pe_section/pe_get_section/ ?
that would make it more consistent with the other functions.

Thanks
Philipp

> +		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 = file_buf + sect_hdr->data_addr;
> +			*sect_sz = sect_hdr->raw_data_size;
> +			return 0;
> +		}
> +		sect_hdr++;
> +	}
> +
> +	return -1;
> +}
> +
> +static bool pe_has_bpf_section(char *file_buf, unsigned long pe_sz)
> +{
> +	char *sect_start = NULL;
> +	unsigned long sect_sz = 0;
> +	int ret;
> +
> +	ret = get_pe_section(file_buf, ".bpf", &sect_start, &sect_sz);
> +	if (ret < 0)
> +		return false;
> +	return true;
> +}
> +
> +/* 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 *image;
> +	int image_sz;
> +	char *initrd;
> +	int initrd_sz;
> +	char *cmdline;
> +	int cmdline_sz;
> +};
> +
> +void bpf_handle_pefile(struct kexec_context *context);
> +void bpf_post_handle_pefile(struct kexec_context *context);
> +
> +
> +/*
> + * optimize("O0") prevents inline, compiler constant propagation
> + */
> +__attribute__((used, optimize("O0"))) void bpf_handle_pefile(struct kexec_context *context)
> +{
> +}
> +
> +__attribute__((used, optimize("O0"))) void bpf_post_handle_pefile(struct kexec_context *context)
> +{
> +}
> +
> +/*
> + * PE file may be nested and should be unfold one by one.
> + * Query 'kernel', 'initrd', 'cmdline' in cur_phase, as they are inputs for the
> + * next phase.
> + */
> +static int prepare_nested_pe(char **kernel, unsigned long *kernel_len, char **initrd,
> +		unsigned long *initrd_len, char **cmdline)
> +{
> +	struct kexec_res *res;
> +	int ret = -1;
> +
> +	*kernel = NULL;
> +	*kernel_len = 0;
> +
> +	list_for_each_entry(res, &cur_phase->res_head, node) {
> +		if (res->name == kexec_res_names[0]) {
> +			*kernel = res->r->buf;
> +			*kernel_len = res->r->data_sz;
> +			ret = 0;
> +		} else if (res->name == kexec_res_names[1]) {
> +			*initrd = res->r->buf;
> +			*initrd_len = res->r->data_sz;
> +		} else if (res->name == kexec_res_names[2]) {
> +			*cmdline = res->r->buf;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static void *pe_image_load(struct kimage *image,
> +				char *kernel, unsigned long kernel_len,
> +				char *initrd, unsigned long initrd_len,
> +				char *cmdline, unsigned long cmdline_len)
> +{
> +	char *parsed_kernel = NULL;
> +	unsigned long parsed_len;
> +	char *linux_start, *initrd_start, *cmdline_start, *bpf_start;
> +	unsigned long linux_sz, initrd_sz, cmdline_sz, bpf_sz;
> +	struct parsed_phase *phase, *phase_tmp;
> +	struct kexec_res *res, *res_tmp;
> +	void *ldata;
> +	int ret;
> +
> +	linux_start = kernel;
> +	linux_sz = kernel_len;
> +	initrd_start = initrd;
> +	initrd_sz = initrd_len;
> +	cmdline_start = cmdline;
> +	cmdline_sz = cmdline_len;
> +
> +	while (is_valid_format(linux_start, linux_sz) &&
> +	       pe_has_bpf_section(linux_start, linux_sz)) {
> +		struct kexec_context context;
> +
> +		get_pe_section(linux_start, ".bpf", &bpf_start, &bpf_sz);
> +		if (!!bpf_sz) {
> +			/* load and attach bpf-prog */
> +			ret = arm_bpf_prog(bpf_start, bpf_sz);
> +			if (ret) {
> +				pr_err("Fail to load .bpf section\n");
> +				ldata = ERR_PTR(ret);
> +				goto err;
> +			}
> +		}
> +		cur_phase = alloc_new_phase();
> +		if (image->type != KEXEC_TYPE_CRASH)
> +			context.kdump = false;
> +		else
> +			context.kdump = true;
> +		context.image = linux_start;
> +		context.image_sz = linux_sz;
> +		context.initrd = initrd_start;
> +		context.initrd_sz = initrd_sz;
> +		context.cmdline = cmdline_start;
> +		context.cmdline_sz = strlen(cmdline_start);
> +		/* bpf-prog fentry, which handle above buffers. */
> +		bpf_handle_pefile(&context);
> +
> +		prepare_nested_pe(&linux_start, &linux_sz, &initrd_start,
> +					&initrd_sz, &cmdline_start);
> +		/* bpf-prog fentry */
> +		bpf_post_handle_pefile(&context);
> +		/*
> +		 * detach the current bpf-prog from their attachment points.
> +		 * It also a point to free any registered interim resource.
> +		 * Any resource except attached to phase is interim.
> +		 */
> +		disarm_bpf_prog();
> +	}
> +
> +	/* the rear of parsed phase contains the result */
> +	list_for_each_entry_reverse(phase, &phase_head, head) {
> +		if (initrd != NULL && cmdline != NULL && parsed_kernel != NULL)
> +			break;
> +		list_for_each_entry(res, &phase->res_head, node) {
> +			if (!strcmp(res->name, "kernel") && !parsed_kernel) {
> +				parsed_kernel = res->r->buf;
> +				parsed_len = res->r->data_sz;
> +				res->deferred_free = true;
> +			} else if (!strcmp(res->name, "initrd") && !initrd) {
> +				initrd = res->r->buf;
> +				initrd_len = res->r->data_sz;
> +				res->deferred_free = true;
> +			} else if (!strcmp(res->name, "cmdline") && !cmdline) {
> +				cmdline = res->r->buf;
> +				cmdline_len = res->r->data_sz;
> +				res->deferred_free = true;
> +			}
> +		}
> +
> +	}
> +
> +	if (initrd == NULL || cmdline == NULL || parsed_kernel == NULL) {
> +		char *c, buf[64];
> +
> +		c = buf;
> +		if (parsed_kernel == NULL) {
> +			strcpy(c, "kernel ");
> +			c += strlen("kernel ");
> +		}
> +		if (initrd == NULL) {
> +			strcpy(c, "initrd ");
> +			c += strlen("initrd ");
> +		}
> +		if (cmdline == NULL) {
> +			strcpy(c, "cmdline ");
> +			c += strlen("cmdline ");
> +		}
> +		c = '\0';
> +		pr_err("Can not extract data for %s", buf);
> +		ldata = ERR_PTR(-EINVAL);
> +		goto err;
> +	}
> +	/*
> +	 * image's kernel_buf, initrd_buf, cmdline_buf are set. Now they should
> +	 * be updated to the new content.
> +	 */
> +	if (image->kernel_buf != parsed_kernel) {
> +		vfree(image->kernel_buf);
> +		image->kernel_buf = parsed_kernel;
> +		image->kernel_buf_len = parsed_len;
> +	}
> +	if (image->initrd_buf != initrd) {
> +		vfree(image->initrd_buf);
> +		image->initrd_buf = initrd;
> +		image->initrd_buf_len = initrd_len;
> +	}
> +	if (image->cmdline_buf != cmdline) {
> +		kfree(image->cmdline_buf);
> +		image->cmdline_buf = cmdline;
> +		image->cmdline_buf_len = cmdline_len;
> +	}
> +	ret = arch_kexec_kernel_image_probe(image, image->kernel_buf,
> +					    image->kernel_buf_len);
> +	if (ret) {
> +		pr_err("Fail to find suitable image loader\n");
> +		ldata = ERR_PTR(ret);
> +		goto err;
> +	}
> +	ldata = kexec_image_load_default(image);
> +	if (IS_ERR(ldata)) {
> +		pr_err("architecture code fails to load image\n");
> +		goto err;
> +	}
> +	image->image_loader_data = ldata;
> +
> +err:
> +	list_for_each_entry_safe(phase, phase_tmp, &phase_head, head) {
> +		list_for_each_entry_safe(res, res_tmp, &phase->res_head, node) {
> +			list_del(&res->node);
> +			/* defer to kimage_file_post_load_cleanup() */
> +			if (res->deferred_free) {
> +				res->r->buf = NULL;
> +				res->r->buf_sz = 0;
> +			}
> +			mem_range_result_put(res->r);
> +			kfree(res);
> +		}
> +		list_del(&phase->head);
> +		kfree(phase);
> +	}
> +
> +	return ldata;
> +}
> +
> +const struct kexec_file_ops kexec_pe_image_ops = {
> +	.probe = pe_image_probe,
> +	.load = pe_image_load,
> +#ifdef CONFIG_KEXEC_IMAGE_VERIFY_SIG
> +	.verify_sig = kexec_kernel_verify_pe_sig,
> +#endif
> +};


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

* Re: [PATCHv3 8/9] kexec: Integrate bpf light skeleton to load zboot image
  2025-05-29  4:17 ` [PATCHv3 8/9] kexec: Integrate bpf light skeleton to load zboot image Pingfan Liu
@ 2025-06-25 18:10   ` Philipp Rudo
  2025-06-30 12:40     ` Pingfan Liu
  0 siblings, 1 reply; 20+ messages in thread
From: Philipp Rudo @ 2025-06-25 18:10 UTC (permalink / raw)
  To: Pingfan Liu
  Cc: kexec, 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

Hi Pingfan,

On Thu, 29 May 2025 12:17:43 +0800
Pingfan Liu <piliu@redhat.com> wrote:

> 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 bss;
>                     struct bpf_map_desc rodata_str1_1;
>     two progs:
>             SEC("fentry.s/bpf_handle_pefile")
>             SEC("fentry.s/bpf_post_handle_pefile")
> 
> 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 | 292 +------------------
>  kernel/kexec_pe_image.c                      |  70 +++++
>  4 files changed, 83 insertions(+), 288 deletions(-)
> 
[...]

> diff --git a/kernel/kexec_pe_image.c b/kernel/kexec_pe_image.c
> index e49d6db3c329d..f47c1e46dba97 100644
> --- a/kernel/kexec_pe_image.c
> +++ b/kernel/kexec_pe_image.c
> @@ -13,6 +13,7 @@
>  #include <linux/kernel.h>
>  #include <linux/vmalloc.h>
>  #include <linux/kexec.h>
> +#include <linux/elf.h>
>  #include <linux/pe.h>
>  #include <linux/string.h>
>  #include <linux/bpf.h>
> @@ -21,6 +22,7 @@
>  #include <asm/image.h>
>  #include <asm/memory.h>
>  
> +#include "kexec_bpf/kexec_pe_parser_bpf.lskel.h"
>  
>  static LIST_HEAD(phase_head);
>  
> @@ -163,14 +165,82 @@ static bool pe_has_bpf_section(char *file_buf, unsigned long pe_sz)
>  	return true;
>  }
>  
> +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, *symtab_shdr, *strtab_shdr, *dst_shdr;
> +	Elf64_Sym *sym, *symtab = NULL;
> +	char *strtab = NULL;
> +	void *symbol_data = NULL;
> +	int i;
> +
> +	symtab_shdr = strtab_shdr = NULL;
> +	if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
> +		pr_err("Not a valid ELF file\n");
> +		goto out;
> +	}
> +
> +	shdr = (struct elf_shdr *)(elf_data + ehdr->e_shoff);
> +	for (i = 0; i < ehdr->e_shnum; i++) {
> +		if (shdr[i].sh_type == SHT_SYMTAB)
> +			symtab_shdr = &shdr[i];
> +		else if (shdr[i].sh_type == SHT_STRTAB && i != ehdr->e_shstrndx)
> +			strtab_shdr = &shdr[i];
> +	}
> +
> +	if (!symtab_shdr || !strtab_shdr) {
> +		pr_err("Symbol table or string table not found\n");
> +		goto out;
> +	}
> +	symtab = (Elf64_Sym *)(elf_data + symtab_shdr->sh_offset);
> +	strtab = (char *)(elf_data + strtab_shdr->sh_offset);
> +	for (i = 0; i < symtab_shdr->sh_size / sizeof(Elf64_Sym); i++) {
> +		sym = &symtab[i];
> +		if (strcmp(&strtab[sym->st_name], symbol_name) == 0) {
> +			if (sym->st_shndx >= SHN_LORESERVE)
> +				return NULL; // No section data for these
> +			dst_shdr = &shdr[sym->st_shndx];
> +			symbol_data = (void *)(elf_data + dst_shdr->sh_offset + sym->st_value);
> +			*symbol_size = symtab[i].st_size;
> +			break;
> +		}
> +	}
> +
> +out:
> +	return symbol_data;
> +}

In kernel/kexec_file.c there is kexec_purgatory_find_symbol which is
basically identical to this function. With a little bit of refractoring
it should work for both cases. I prefer using
kexec_purgatory_find_symbol as your implementation cannot handle elf
files with multiple symtab and strtab sections.

Thanks
Philipp

> +
>  /* 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 {


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

* Re: [PATCHv3 3/9] bpf: Introduce bpf_copy_to_kernel() to buffer the content from bpf-prog
  2025-05-29  4:17 ` [PATCHv3 3/9] bpf: Introduce bpf_copy_to_kernel() to buffer the content from bpf-prog Pingfan Liu
  2025-05-29 11:48   ` kernel test robot
@ 2025-06-25 18:10   ` Philipp Rudo
  1 sibling, 0 replies; 20+ messages in thread
From: Philipp Rudo @ 2025-06-25 18:10 UTC (permalink / raw)
  To: Pingfan Liu
  Cc: bpf, 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, KP Singh, Stanislav Fomichev, Hao Luo,
	Jiri Olsa

Hi Pingfan,
Hi Alexei,

sorry for the late reply.

On Thu, 29 May 2025 12:17:38 +0800
Pingfan Liu <piliu@redhat.com> wrote:

> In the security kexec_file_load case, the buffer which holds the kernel
> image is invisible to the userspace.
> 
> The common data flow in bpf scheme is from kernel to bpf-prog.  In the
> case of kexec_file_load, the kexec component needs to buffer the parsed
> result by bpf-prog (opposite the usual direction) to the next stage
> parsing. bpf_kexec_carrier() makes the opposite data flow possible. A
> bpf-prog can publish the parsed payload address to the kernel, and the
> latter can copy them for future use.
> 
> Signed-off-by: Pingfan Liu <piliu@redhat.com>
> Cc: Alexei Starovoitov <ast@kernel.org>
> Cc: Daniel Borkmann <daniel@iogearbox.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          |  23 +++++
>  kernel/bpf/Makefile          |   2 +-
>  kernel/bpf/helpers.c         |   2 +
>  kernel/bpf/helpers_carrier.c | 194 +++++++++++++++++++++++++++++++++++
>  4 files changed, 220 insertions(+), 1 deletion(-)
>  create mode 100644 kernel/bpf/helpers_carrier.c
> 

[...]

> diff --git a/kernel/bpf/helpers_carrier.c b/kernel/bpf/helpers_carrier.c
> new file mode 100644
> index 0000000000000..c4e45fdf0ebb8
> --- /dev/null
> +++ b/kernel/bpf/helpers_carrier.c
> @@ -0,0 +1,194 @@

[...]

> +__bpf_kfunc int bpf_mem_range_result_put(struct mem_range_result *result)

I'm concerned about the use of kfuncs for our use case. I don't believe
they provide the stability we need.

With kexec we deal with two different kernels. The 1st kernel, aka. the
one that executes kexec to load the 2nd kernel, and the 2nd kernel that
is being loaded. In general both kernels are built from different
versions with different configs and it is expected that kexec works
even when both kernels are years apart.

The problem is that in our design the bpf-prog is part of the
image of and built from the sources of the 2nd kernel, but runs in the
1st kernel. So the definitions of the kfuncs in both kernels have to
match. What makes it worse is that for it to work with secure boot the
kernel image, including the bpf-prog, needs to be signed. Which means
that the bpf-prog is fixed after build and can no longer be updated.

All in all I'm afraid we need a uapi-like stability for those kfuncs
for our design to work. Do you have any comments on my concern? Or any
idea how we could archive the stability despite using kfuncs?

Thanks
Philipp



> +{
> +	return mem_range_result_put(result);
> +}
> +
> +/*
> + * Cache the content in @buf into kernel
> + */
> +__bpf_kfunc int bpf_copy_to_kernel(const char *name, char *buf, int size)
> +{
> +	struct mem_range_result *range;
> +	struct mem_cgroup *memcg, *old_memcg;
> +	struct str_listener *item;
> +	resource_handler handler;
> +	bool kmalloc;
> +	char *kbuf;
> +	int id, ret = 0;
> +
> +	id = srcu_read_lock(&srcu);
> +	item = find_listener(name);
> +	if (!item) {
> +		srcu_read_unlock(&srcu, id);
> +		return -EINVAL;
> +	}
> +	kmalloc = item->kmalloc;
> +	handler = item->handler;
> +	srcu_read_unlock(&srcu, id);
> +	memcg = get_mem_cgroup_from_current();
> +	old_memcg = set_active_memcg(memcg);
> +	range = kmalloc(sizeof(struct mem_range_result), GFP_KERNEL);
> +	if (!range) {
> +		pr_err("fail to allocate mem_range_result\n");
> +		ret = -ENOMEM;
> +		goto err;
> +	}
> +
> +	kref_init(&range->ref);
> +	if (item->kmalloc)
> +		kbuf = kmalloc(size, GFP_KERNEL | __GFP_ACCOUNT);
> +	else
> +		kbuf = __vmalloc(size, GFP_KERNEL | __GFP_ACCOUNT);
> +	if (!kbuf) {
> +		kfree(range);
> +		ret = -ENOMEM;
> +		goto err;
> +	}
> +	ret = copy_from_kernel_nofault(kbuf, buf, size);
> +	if (unlikely(ret < 0)) {
> +		kfree(range);
> +		if (item->kmalloc)
> +			kfree(kbuf);
> +		else
> +			vfree(kbuf);
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +	range->kmalloc = item->kmalloc;
> +	range->buf = kbuf;
> +	range->buf_sz = size;
> +	range->data_sz = size;
> +	range->memcg = memcg;
> +	mem_cgroup_tryget(memcg);
> +	range->status = 0;
> +	ret = handler(name, range);
> +	mem_range_result_put(range);
> +err:
> +	set_active_memcg(old_memcg);
> +	mem_cgroup_put(memcg);
> +	return ret;
> +}
> +
> +int register_carrier_listener(struct carrier_listener *listener)
> +{
> +	struct str_listener *item;
> +	unsigned int hash;
> +	int ret;
> +
> +	if (!listener->name)
> +		return -EINVAL;
> +	item = kmalloc(sizeof(*item), GFP_KERNEL);
> +	if (!item)
> +		return -ENOMEM;
> +	item->str = kstrdup(listener->name, GFP_KERNEL);
> +	if (!item->str) {
> +		kfree(item);
> +		return -ENOMEM;
> +	}
> +	item->handler = listener->handler;
> +	item->kmalloc = listener->kmalloc;
> +	hash = jhash(item->str, strlen(item->str), 0);
> +	mutex_lock(&str_listeners_mutex);
> +	if (!find_listener(item->str)) {
> +		hash_add(str_listeners, &item->node, hash);
> +	} else {
> +		kfree(item->str);
> +		kfree(item);
> +		ret = -EBUSY;
> +	}
> +	mutex_unlock(&str_listeners_mutex);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(register_carrier_listener);
> +
> +int unregister_carrier_listener(char *str)
> +{
> +	struct str_listener *item;
> +	int ret = 0;
> +
> +	mutex_lock(&str_listeners_mutex);
> +	item = find_listener(str);
> +	if (!!item)
> +		hash_del(&item->node);
> +	else
> +		ret = -EINVAL;
> +	mutex_unlock(&str_listeners_mutex);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(unregister_carrier_listener);
> +


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

* Re: [PATCHv3 8/9] kexec: Integrate bpf light skeleton to load zboot image
  2025-06-25 18:10   ` Philipp Rudo
@ 2025-06-30 12:40     ` Pingfan Liu
  0 siblings, 0 replies; 20+ messages in thread
From: Pingfan Liu @ 2025-06-30 12:40 UTC (permalink / raw)
  To: Philipp Rudo
  Cc: kexec, 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

On Wed, Jun 25, 2025 at 08:10:11PM +0200, Philipp Rudo wrote:
> Hi Pingfan,
> 
> On Thu, 29 May 2025 12:17:43 +0800
> Pingfan Liu <piliu@redhat.com> wrote:
> 
> > 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 bss;
> >                     struct bpf_map_desc rodata_str1_1;
> >     two progs:
> >             SEC("fentry.s/bpf_handle_pefile")
> >             SEC("fentry.s/bpf_post_handle_pefile")
> > 
> > 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 | 292 +------------------
> >  kernel/kexec_pe_image.c                      |  70 +++++
> >  4 files changed, 83 insertions(+), 288 deletions(-)
> > 
> [...]
> 
> > diff --git a/kernel/kexec_pe_image.c b/kernel/kexec_pe_image.c
> > index e49d6db3c329d..f47c1e46dba97 100644
> > --- a/kernel/kexec_pe_image.c
> > +++ b/kernel/kexec_pe_image.c
> > @@ -13,6 +13,7 @@
> >  #include <linux/kernel.h>
> >  #include <linux/vmalloc.h>
> >  #include <linux/kexec.h>
> > +#include <linux/elf.h>
> >  #include <linux/pe.h>
> >  #include <linux/string.h>
> >  #include <linux/bpf.h>
> > @@ -21,6 +22,7 @@
> >  #include <asm/image.h>
> >  #include <asm/memory.h>
> >  
> > +#include "kexec_bpf/kexec_pe_parser_bpf.lskel.h"
> >  
> >  static LIST_HEAD(phase_head);
> >  
> > @@ -163,14 +165,82 @@ static bool pe_has_bpf_section(char *file_buf, unsigned long pe_sz)
> >  	return true;
> >  }
> >  
> > +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, *symtab_shdr, *strtab_shdr, *dst_shdr;
> > +	Elf64_Sym *sym, *symtab = NULL;
> > +	char *strtab = NULL;
> > +	void *symbol_data = NULL;
> > +	int i;
> > +
> > +	symtab_shdr = strtab_shdr = NULL;
> > +	if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
> > +		pr_err("Not a valid ELF file\n");
> > +		goto out;
> > +	}
> > +
> > +	shdr = (struct elf_shdr *)(elf_data + ehdr->e_shoff);
> > +	for (i = 0; i < ehdr->e_shnum; i++) {
> > +		if (shdr[i].sh_type == SHT_SYMTAB)
> > +			symtab_shdr = &shdr[i];
> > +		else if (shdr[i].sh_type == SHT_STRTAB && i != ehdr->e_shstrndx)
> > +			strtab_shdr = &shdr[i];
> > +	}
> > +
> > +	if (!symtab_shdr || !strtab_shdr) {
> > +		pr_err("Symbol table or string table not found\n");
> > +		goto out;
> > +	}
> > +	symtab = (Elf64_Sym *)(elf_data + symtab_shdr->sh_offset);
> > +	strtab = (char *)(elf_data + strtab_shdr->sh_offset);
> > +	for (i = 0; i < symtab_shdr->sh_size / sizeof(Elf64_Sym); i++) {
> > +		sym = &symtab[i];
> > +		if (strcmp(&strtab[sym->st_name], symbol_name) == 0) {
> > +			if (sym->st_shndx >= SHN_LORESERVE)
> > +				return NULL; // No section data for these
> > +			dst_shdr = &shdr[sym->st_shndx];
> > +			symbol_data = (void *)(elf_data + dst_shdr->sh_offset + sym->st_value);
> > +			*symbol_size = symtab[i].st_size;
> > +			break;
> > +		}
> > +	}
> > +
> > +out:
> > +	return symbol_data;
> > +}
> 
> In kernel/kexec_file.c there is kexec_purgatory_find_symbol which is
> basically identical to this function. With a little bit of refractoring
> it should work for both cases. I prefer using
> kexec_purgatory_find_symbol as your implementation cannot handle elf
> files with multiple symtab and strtab sections.
> 

Thanks for your suggestion. I will refractor the code in next version.

Regards,

Pingfan 

> Thanks
> Philipp
> 
> > +
> >  /* 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 {
> 


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

* Re: [PATCHv3 5/9] kexec: Introduce kexec_pe_image to parse and load PE file
  2025-06-25 18:09   ` Philipp Rudo
@ 2025-06-30 13:45     ` Pingfan Liu
  2025-07-02  9:17       ` Philipp Rudo
  0 siblings, 1 reply; 20+ messages in thread
From: Pingfan Liu @ 2025-06-30 13:45 UTC (permalink / raw)
  To: Philipp Rudo
  Cc: kexec, 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

On Wed, Jun 25, 2025 at 08:09:50PM +0200, Philipp Rudo wrote:
> Hi Pingfan,
> 
> On Thu, 29 May 2025 12:17:40 +0800
> Pingfan Liu <piliu@redhat.com> wrote:
> 
> > 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.
> > 
> > A new kexec_file_ops is implementation, named pe_image_ops.
> > 
> > There are some place holder function in this patch. (They will take
> > effect after the introduction of kexec bpf light skeleton and bpf
> > helpers). Overall the parsing progress is a pipeline, the current
> > bpf-prog parser is attached to bpf_handle_pefile(), and detatched at the
> > end of the current stage 'disarm_bpf_prog()' the current parsed result
> > by the current bpf-prog will be buffered in kernel 'prepare_nested_pe()'
> > , and deliver to the next stage.  For each stage, the bpf bytecode is
> > extracted from the '.bpf' section in the PE file.
> > 
> > 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
> > ---
> >  include/linux/kexec.h   |   1 +
> >  kernel/Kconfig.kexec    |   8 +
> >  kernel/Makefile         |   1 +
> >  kernel/kexec_pe_image.c | 356 ++++++++++++++++++++++++++++++++++++++++
> >  4 files changed, 366 insertions(+)
> >  create mode 100644 kernel/kexec_pe_image.c
> > 
> [...]
> 
> > diff --git a/kernel/kexec_pe_image.c b/kernel/kexec_pe_image.c
> > new file mode 100644
> > index 0000000000000..3097efccb8502
> > --- /dev/null
> > +++ b/kernel/kexec_pe_image.c
> > @@ -0,0 +1,356 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Kexec PE image loader
> > +
> > + * Copyright (C) 2025 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/pe.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>
> > +
> > +
> > +static LIST_HEAD(phase_head);
> > +
> > +struct parsed_phase {
> > +	struct list_head head;
> > +	struct list_head res_head;
> > +};
> > +
> > +static struct parsed_phase *cur_phase;
> > +
> > +static char *kexec_res_names[3] = {"kernel", "initrd", "cmdline"};
> 
> Wouldn't it be better to use a enum rather than strings for the
> different resources? Especially as in prepare_nested_pe you are

I plan to make bpf_copy_to_kernel() fit for more cases besides kexec. So
string may be better choice, and I think it is better to have a
subsystem prefix, like "kexec:kernel"

> comparing two strings using == instead of strcmp(). So IIUC it should
> always return false.
> 

Oops, I will fix that. In fact, I meaned to assign the pointer
kexec_res_names[i] to kexec_res.name in bpf_kexec_carrier(). Later in
prepare_nested_pe() can compare two pointers.


> > +struct kexec_res {
> > +	struct list_head node;
> > +	char *name;
> > +	/* The free of buffer is deferred to kimage_file_post_load_cleanup */
> > +	bool deferred_free;
> > +	struct mem_range_result *r;
> > +};
> > +
> > +static struct parsed_phase *alloc_new_phase(void)
> > +{
> > +	struct parsed_phase *phase = kzalloc(sizeof(struct parsed_phase), GFP_KERNEL);
> > +
> > +	INIT_LIST_HEAD(&phase->head);
> > +	INIT_LIST_HEAD(&phase->res_head);
> > +	list_add_tail(&phase->head, &phase_head);
> > +
> > +	return phase;
> > +}
> 
> I must admit I don't fully understand how you are handling the
> different phases. In particular I don't understand why you are keeping
> all the resources a phase returned once it is finished. The way I see
> it those resources are only needed once as input for the next phase. So
> it should be sufficient to only keep a single kexec_context and update
> it when a phase returns a new resource. The way I see it this should
> simplify pe_image_load quite a bit. Or am I missing something?
> 

Let us say an aarch64 zboot image embeded in UKI's .linux section.
The UKI parser takes apart the image into kernel, initrd, cmdline.
And the kernel part contains the zboot PE, including zboot parser.
The zboot parser needn't to handle either initrd or cmdline.
So I use the phases, and the leaf node is the final parsed result.

> > +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 != MZ_MAGIC)
> > +		return false;
> > +	pe = (struct pe_hdr *)(kernel_buf + mz->peaddr);
> > +	if (pe->magic != PE_MAGIC)
> > +		return false;
> > +	if (pe->opt_hdr_size == 0) {
> > +		pr_err("optional header is missing\n");
> > +		return false;
> > +	}
> > +
> > +	return true;
> > +}
> > +
> > +static bool is_valid_format(const char *kernel_buf, unsigned long kernel_len)
> > +{
> > +	return is_valid_pe(kernel_buf, kernel_len);
> > +}
> > +
> > +/*
> > + * The UEFI Terse Executable (TE) image has MZ header.
> > + */
> > +static int pe_image_probe(const char *kernel_buf, unsigned long kernel_len)
> > +{
> > +	return is_valid_pe(kernel_buf, kernel_len) ? 0 : -1;
> 
> Every image, at least on x86, is a valid pe file. So we should check
> for the .bpf section rather than the header.
> 

You are right that it should include the check on the existence of .bpf
section. On the other hand, the check on PE header in kernel can ensure 
the kexec-tools passes the right candidate for this parser.

> > +}
> > +
> > +static int get_pe_section(char *file_buf, const char *sect_name,
> 
> s/get_pe_section/pe_get_section/ ?
> that would make it more consistent with the other functions.

Sure. I will fix it.


Thanks for your careful review.


Best Regards,

Pingfan

> 
> Thanks
> Philipp
> 
> > +		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 = file_buf + sect_hdr->data_addr;
> > +			*sect_sz = sect_hdr->raw_data_size;
> > +			return 0;
> > +		}
> > +		sect_hdr++;
> > +	}
> > +
> > +	return -1;
> > +}
> > +
> > +static bool pe_has_bpf_section(char *file_buf, unsigned long pe_sz)
> > +{
> > +	char *sect_start = NULL;
> > +	unsigned long sect_sz = 0;
> > +	int ret;
> > +
> > +	ret = get_pe_section(file_buf, ".bpf", &sect_start, &sect_sz);
> > +	if (ret < 0)
> > +		return false;
> > +	return true;
> > +}
> > +
> > +/* 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 *image;
> > +	int image_sz;
> > +	char *initrd;
> > +	int initrd_sz;
> > +	char *cmdline;
> > +	int cmdline_sz;
> > +};
> > +
> > +void bpf_handle_pefile(struct kexec_context *context);
> > +void bpf_post_handle_pefile(struct kexec_context *context);
> > +
> > +
> > +/*
> > + * optimize("O0") prevents inline, compiler constant propagation
> > + */
> > +__attribute__((used, optimize("O0"))) void bpf_handle_pefile(struct kexec_context *context)
> > +{
> > +}
> > +
> > +__attribute__((used, optimize("O0"))) void bpf_post_handle_pefile(struct kexec_context *context)
> > +{
> > +}
> > +
> > +/*
> > + * PE file may be nested and should be unfold one by one.
> > + * Query 'kernel', 'initrd', 'cmdline' in cur_phase, as they are inputs for the
> > + * next phase.
> > + */
> > +static int prepare_nested_pe(char **kernel, unsigned long *kernel_len, char **initrd,
> > +		unsigned long *initrd_len, char **cmdline)
> > +{
> > +	struct kexec_res *res;
> > +	int ret = -1;
> > +
> > +	*kernel = NULL;
> > +	*kernel_len = 0;
> > +
> > +	list_for_each_entry(res, &cur_phase->res_head, node) {
> > +		if (res->name == kexec_res_names[0]) {
> > +			*kernel = res->r->buf;
> > +			*kernel_len = res->r->data_sz;
> > +			ret = 0;
> > +		} else if (res->name == kexec_res_names[1]) {
> > +			*initrd = res->r->buf;
> > +			*initrd_len = res->r->data_sz;
> > +		} else if (res->name == kexec_res_names[2]) {
> > +			*cmdline = res->r->buf;
> > +		}
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +static void *pe_image_load(struct kimage *image,
> > +				char *kernel, unsigned long kernel_len,
> > +				char *initrd, unsigned long initrd_len,
> > +				char *cmdline, unsigned long cmdline_len)
> > +{
> > +	char *parsed_kernel = NULL;
> > +	unsigned long parsed_len;
> > +	char *linux_start, *initrd_start, *cmdline_start, *bpf_start;
> > +	unsigned long linux_sz, initrd_sz, cmdline_sz, bpf_sz;
> > +	struct parsed_phase *phase, *phase_tmp;
> > +	struct kexec_res *res, *res_tmp;
> > +	void *ldata;
> > +	int ret;
> > +
> > +	linux_start = kernel;
> > +	linux_sz = kernel_len;
> > +	initrd_start = initrd;
> > +	initrd_sz = initrd_len;
> > +	cmdline_start = cmdline;
> > +	cmdline_sz = cmdline_len;
> > +
> > +	while (is_valid_format(linux_start, linux_sz) &&
> > +	       pe_has_bpf_section(linux_start, linux_sz)) {
> > +		struct kexec_context context;
> > +
> > +		get_pe_section(linux_start, ".bpf", &bpf_start, &bpf_sz);
> > +		if (!!bpf_sz) {
> > +			/* load and attach bpf-prog */
> > +			ret = arm_bpf_prog(bpf_start, bpf_sz);
> > +			if (ret) {
> > +				pr_err("Fail to load .bpf section\n");
> > +				ldata = ERR_PTR(ret);
> > +				goto err;
> > +			}
> > +		}
> > +		cur_phase = alloc_new_phase();
> > +		if (image->type != KEXEC_TYPE_CRASH)
> > +			context.kdump = false;
> > +		else
> > +			context.kdump = true;
> > +		context.image = linux_start;
> > +		context.image_sz = linux_sz;
> > +		context.initrd = initrd_start;
> > +		context.initrd_sz = initrd_sz;
> > +		context.cmdline = cmdline_start;
> > +		context.cmdline_sz = strlen(cmdline_start);
> > +		/* bpf-prog fentry, which handle above buffers. */
> > +		bpf_handle_pefile(&context);
> > +
> > +		prepare_nested_pe(&linux_start, &linux_sz, &initrd_start,
> > +					&initrd_sz, &cmdline_start);
> > +		/* bpf-prog fentry */
> > +		bpf_post_handle_pefile(&context);
> > +		/*
> > +		 * detach the current bpf-prog from their attachment points.
> > +		 * It also a point to free any registered interim resource.
> > +		 * Any resource except attached to phase is interim.
> > +		 */
> > +		disarm_bpf_prog();
> > +	}
> > +
> > +	/* the rear of parsed phase contains the result */
> > +	list_for_each_entry_reverse(phase, &phase_head, head) {
> > +		if (initrd != NULL && cmdline != NULL && parsed_kernel != NULL)
> > +			break;
> > +		list_for_each_entry(res, &phase->res_head, node) {
> > +			if (!strcmp(res->name, "kernel") && !parsed_kernel) {
> > +				parsed_kernel = res->r->buf;
> > +				parsed_len = res->r->data_sz;
> > +				res->deferred_free = true;
> > +			} else if (!strcmp(res->name, "initrd") && !initrd) {
> > +				initrd = res->r->buf;
> > +				initrd_len = res->r->data_sz;
> > +				res->deferred_free = true;
> > +			} else if (!strcmp(res->name, "cmdline") && !cmdline) {
> > +				cmdline = res->r->buf;
> > +				cmdline_len = res->r->data_sz;
> > +				res->deferred_free = true;
> > +			}
> > +		}
> > +
> > +	}
> > +
> > +	if (initrd == NULL || cmdline == NULL || parsed_kernel == NULL) {
> > +		char *c, buf[64];
> > +
> > +		c = buf;
> > +		if (parsed_kernel == NULL) {
> > +			strcpy(c, "kernel ");
> > +			c += strlen("kernel ");
> > +		}
> > +		if (initrd == NULL) {
> > +			strcpy(c, "initrd ");
> > +			c += strlen("initrd ");
> > +		}
> > +		if (cmdline == NULL) {
> > +			strcpy(c, "cmdline ");
> > +			c += strlen("cmdline ");
> > +		}
> > +		c = '\0';
> > +		pr_err("Can not extract data for %s", buf);
> > +		ldata = ERR_PTR(-EINVAL);
> > +		goto err;
> > +	}
> > +	/*
> > +	 * image's kernel_buf, initrd_buf, cmdline_buf are set. Now they should
> > +	 * be updated to the new content.
> > +	 */
> > +	if (image->kernel_buf != parsed_kernel) {
> > +		vfree(image->kernel_buf);
> > +		image->kernel_buf = parsed_kernel;
> > +		image->kernel_buf_len = parsed_len;
> > +	}
> > +	if (image->initrd_buf != initrd) {
> > +		vfree(image->initrd_buf);
> > +		image->initrd_buf = initrd;
> > +		image->initrd_buf_len = initrd_len;
> > +	}
> > +	if (image->cmdline_buf != cmdline) {
> > +		kfree(image->cmdline_buf);
> > +		image->cmdline_buf = cmdline;
> > +		image->cmdline_buf_len = cmdline_len;
> > +	}
> > +	ret = arch_kexec_kernel_image_probe(image, image->kernel_buf,
> > +					    image->kernel_buf_len);
> > +	if (ret) {
> > +		pr_err("Fail to find suitable image loader\n");
> > +		ldata = ERR_PTR(ret);
> > +		goto err;
> > +	}
> > +	ldata = kexec_image_load_default(image);
> > +	if (IS_ERR(ldata)) {
> > +		pr_err("architecture code fails to load image\n");
> > +		goto err;
> > +	}
> > +	image->image_loader_data = ldata;
> > +
> > +err:
> > +	list_for_each_entry_safe(phase, phase_tmp, &phase_head, head) {
> > +		list_for_each_entry_safe(res, res_tmp, &phase->res_head, node) {
> > +			list_del(&res->node);
> > +			/* defer to kimage_file_post_load_cleanup() */
> > +			if (res->deferred_free) {
> > +				res->r->buf = NULL;
> > +				res->r->buf_sz = 0;
> > +			}
> > +			mem_range_result_put(res->r);
> > +			kfree(res);
> > +		}
> > +		list_del(&phase->head);
> > +		kfree(phase);
> > +	}
> > +
> > +	return ldata;
> > +}
> > +
> > +const struct kexec_file_ops kexec_pe_image_ops = {
> > +	.probe = pe_image_probe,
> > +	.load = pe_image_load,
> > +#ifdef CONFIG_KEXEC_IMAGE_VERIFY_SIG
> > +	.verify_sig = kexec_kernel_verify_pe_sig,
> > +#endif
> > +};
> 


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

* Re: [PATCHv3 5/9] kexec: Introduce kexec_pe_image to parse and load PE file
  2025-06-30 13:45     ` Pingfan Liu
@ 2025-07-02  9:17       ` Philipp Rudo
  2025-07-03  1:17         ` Pingfan Liu
  0 siblings, 1 reply; 20+ messages in thread
From: Philipp Rudo @ 2025-07-02  9:17 UTC (permalink / raw)
  To: Pingfan Liu
  Cc: kexec, 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

Hi Pingfan,

On Mon, 30 Jun 2025 21:45:05 +0800
Pingfan Liu <piliu@redhat.com> wrote:

> On Wed, Jun 25, 2025 at 08:09:50PM +0200, Philipp Rudo wrote:
> > Hi Pingfan,
> > 
> > On Thu, 29 May 2025 12:17:40 +0800
> > Pingfan Liu <piliu@redhat.com> wrote:
> >   
> > > 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.
> > > 
> > > A new kexec_file_ops is implementation, named pe_image_ops.
> > > 
> > > There are some place holder function in this patch. (They will take
> > > effect after the introduction of kexec bpf light skeleton and bpf
> > > helpers). Overall the parsing progress is a pipeline, the current
> > > bpf-prog parser is attached to bpf_handle_pefile(), and detatched at the
> > > end of the current stage 'disarm_bpf_prog()' the current parsed result
> > > by the current bpf-prog will be buffered in kernel 'prepare_nested_pe()'
> > > , and deliver to the next stage.  For each stage, the bpf bytecode is
> > > extracted from the '.bpf' section in the PE file.
> > > 
> > > 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
> > > ---
> > >  include/linux/kexec.h   |   1 +
> > >  kernel/Kconfig.kexec    |   8 +
> > >  kernel/Makefile         |   1 +
> > >  kernel/kexec_pe_image.c | 356 ++++++++++++++++++++++++++++++++++++++++
> > >  4 files changed, 366 insertions(+)
> > >  create mode 100644 kernel/kexec_pe_image.c
> > >   
> > [...]
> >   
> > > diff --git a/kernel/kexec_pe_image.c b/kernel/kexec_pe_image.c
> > > new file mode 100644
> > > index 0000000000000..3097efccb8502
> > > --- /dev/null
> > > +++ b/kernel/kexec_pe_image.c
> > > @@ -0,0 +1,356 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * Kexec PE image loader
> > > +
> > > + * Copyright (C) 2025 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/pe.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>
> > > +
> > > +
> > > +static LIST_HEAD(phase_head);
> > > +
> > > +struct parsed_phase {
> > > +	struct list_head head;
> > > +	struct list_head res_head;
> > > +};
> > > +
> > > +static struct parsed_phase *cur_phase;
> > > +
> > > +static char *kexec_res_names[3] = {"kernel", "initrd", "cmdline"};  
> > 
> > Wouldn't it be better to use a enum rather than strings for the
> > different resources? Especially as in prepare_nested_pe you are  
> 
> I plan to make bpf_copy_to_kernel() fit for more cases besides kexec. So
> string may be better choice, and I think it is better to have a
> subsystem prefix, like "kexec:kernel"

True, although an enum could be utilized directly as, e.g. an index for
an array directly. Anyway, I don't think there is a single 'best'
solution here. So feel free to use strings.

> > comparing two strings using == instead of strcmp(). So IIUC it should
> > always return false.
> >   
> 
> Oops, I will fix that. In fact, I meaned to assign the pointer
> kexec_res_names[i] to kexec_res.name in bpf_kexec_carrier(). Later in
> prepare_nested_pe() can compare two pointers.
> 
> 
> > > +struct kexec_res {
> > > +	struct list_head node;
> > > +	char *name;
> > > +	/* The free of buffer is deferred to kimage_file_post_load_cleanup */
> > > +	bool deferred_free;
> > > +	struct mem_range_result *r;
> > > +};
> > > +
> > > +static struct parsed_phase *alloc_new_phase(void)
> > > +{
> > > +	struct parsed_phase *phase = kzalloc(sizeof(struct parsed_phase), GFP_KERNEL);
> > > +
> > > +	INIT_LIST_HEAD(&phase->head);
> > > +	INIT_LIST_HEAD(&phase->res_head);
> > > +	list_add_tail(&phase->head, &phase_head);
> > > +
> > > +	return phase;
> > > +}  
> > 
> > I must admit I don't fully understand how you are handling the
> > different phases. In particular I don't understand why you are keeping
> > all the resources a phase returned once it is finished. The way I see
> > it those resources are only needed once as input for the next phase. So
> > it should be sufficient to only keep a single kexec_context and update
> > it when a phase returns a new resource. The way I see it this should
> > simplify pe_image_load quite a bit. Or am I missing something?
> >   
> 
> Let us say an aarch64 zboot image embeded in UKI's .linux section.
> The UKI parser takes apart the image into kernel, initrd, cmdline.
> And the kernel part contains the zboot PE, including zboot parser.
> The zboot parser needn't to handle either initrd or cmdline.
> So I use the phases, and the leaf node is the final parsed result.

Right, that's how the code is working. My point was that when you have
multiple phases working on the same component, e.g. the kernel image,
then you still keep all the intermediate kernel images in memory until
the end. Even though the intermediate images are only used as an input
for the next phase(s). So my suggestion is to remove them immediately
once a phase returns a new image. My expectation is that this not only
reduces the memory usage but also simplifies the code.

Thanks
Philipp

> > > +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 != MZ_MAGIC)
> > > +		return false;
> > > +	pe = (struct pe_hdr *)(kernel_buf + mz->peaddr);
> > > +	if (pe->magic != PE_MAGIC)
> > > +		return false;
> > > +	if (pe->opt_hdr_size == 0) {
> > > +		pr_err("optional header is missing\n");
> > > +		return false;
> > > +	}
> > > +
> > > +	return true;
> > > +}
> > > +
> > > +static bool is_valid_format(const char *kernel_buf, unsigned long kernel_len)
> > > +{
> > > +	return is_valid_pe(kernel_buf, kernel_len);
> > > +}
> > > +
> > > +/*
> > > + * The UEFI Terse Executable (TE) image has MZ header.
> > > + */
> > > +static int pe_image_probe(const char *kernel_buf, unsigned long kernel_len)
> > > +{
> > > +	return is_valid_pe(kernel_buf, kernel_len) ? 0 : -1;  
> > 
> > Every image, at least on x86, is a valid pe file. So we should check
> > for the .bpf section rather than the header.
> >   
> 
> You are right that it should include the check on the existence of .bpf
> section. On the other hand, the check on PE header in kernel can ensure 
> the kexec-tools passes the right candidate for this parser.
> 
> > > +}
> > > +
> > > +static int get_pe_section(char *file_buf, const char *sect_name,  
> > 
> > s/get_pe_section/pe_get_section/ ?
> > that would make it more consistent with the other functions.  
> 
> Sure. I will fix it.
> 
> 
> Thanks for your careful review.
> 
> 
> Best Regards,
> 
> Pingfan
> 
> > 
> > Thanks
> > Philipp
> >   
> > > +		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 = file_buf + sect_hdr->data_addr;
> > > +			*sect_sz = sect_hdr->raw_data_size;
> > > +			return 0;
> > > +		}
> > > +		sect_hdr++;
> > > +	}
> > > +
> > > +	return -1;
> > > +}
> > > +
> > > +static bool pe_has_bpf_section(char *file_buf, unsigned long pe_sz)
> > > +{
> > > +	char *sect_start = NULL;
> > > +	unsigned long sect_sz = 0;
> > > +	int ret;
> > > +
> > > +	ret = get_pe_section(file_buf, ".bpf", &sect_start, &sect_sz);
> > > +	if (ret < 0)
> > > +		return false;
> > > +	return true;
> > > +}
> > > +
> > > +/* 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 *image;
> > > +	int image_sz;
> > > +	char *initrd;
> > > +	int initrd_sz;
> > > +	char *cmdline;
> > > +	int cmdline_sz;
> > > +};
> > > +
> > > +void bpf_handle_pefile(struct kexec_context *context);
> > > +void bpf_post_handle_pefile(struct kexec_context *context);
> > > +
> > > +
> > > +/*
> > > + * optimize("O0") prevents inline, compiler constant propagation
> > > + */
> > > +__attribute__((used, optimize("O0"))) void bpf_handle_pefile(struct kexec_context *context)
> > > +{
> > > +}
> > > +
> > > +__attribute__((used, optimize("O0"))) void bpf_post_handle_pefile(struct kexec_context *context)
> > > +{
> > > +}
> > > +
> > > +/*
> > > + * PE file may be nested and should be unfold one by one.
> > > + * Query 'kernel', 'initrd', 'cmdline' in cur_phase, as they are inputs for the
> > > + * next phase.
> > > + */
> > > +static int prepare_nested_pe(char **kernel, unsigned long *kernel_len, char **initrd,
> > > +		unsigned long *initrd_len, char **cmdline)
> > > +{
> > > +	struct kexec_res *res;
> > > +	int ret = -1;
> > > +
> > > +	*kernel = NULL;
> > > +	*kernel_len = 0;
> > > +
> > > +	list_for_each_entry(res, &cur_phase->res_head, node) {
> > > +		if (res->name == kexec_res_names[0]) {
> > > +			*kernel = res->r->buf;
> > > +			*kernel_len = res->r->data_sz;
> > > +			ret = 0;
> > > +		} else if (res->name == kexec_res_names[1]) {
> > > +			*initrd = res->r->buf;
> > > +			*initrd_len = res->r->data_sz;
> > > +		} else if (res->name == kexec_res_names[2]) {
> > > +			*cmdline = res->r->buf;
> > > +		}
> > > +	}
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static void *pe_image_load(struct kimage *image,
> > > +				char *kernel, unsigned long kernel_len,
> > > +				char *initrd, unsigned long initrd_len,
> > > +				char *cmdline, unsigned long cmdline_len)
> > > +{
> > > +	char *parsed_kernel = NULL;
> > > +	unsigned long parsed_len;
> > > +	char *linux_start, *initrd_start, *cmdline_start, *bpf_start;
> > > +	unsigned long linux_sz, initrd_sz, cmdline_sz, bpf_sz;
> > > +	struct parsed_phase *phase, *phase_tmp;
> > > +	struct kexec_res *res, *res_tmp;
> > > +	void *ldata;
> > > +	int ret;
> > > +
> > > +	linux_start = kernel;
> > > +	linux_sz = kernel_len;
> > > +	initrd_start = initrd;
> > > +	initrd_sz = initrd_len;
> > > +	cmdline_start = cmdline;
> > > +	cmdline_sz = cmdline_len;
> > > +
> > > +	while (is_valid_format(linux_start, linux_sz) &&
> > > +	       pe_has_bpf_section(linux_start, linux_sz)) {
> > > +		struct kexec_context context;
> > > +
> > > +		get_pe_section(linux_start, ".bpf", &bpf_start, &bpf_sz);
> > > +		if (!!bpf_sz) {
> > > +			/* load and attach bpf-prog */
> > > +			ret = arm_bpf_prog(bpf_start, bpf_sz);
> > > +			if (ret) {
> > > +				pr_err("Fail to load .bpf section\n");
> > > +				ldata = ERR_PTR(ret);
> > > +				goto err;
> > > +			}
> > > +		}
> > > +		cur_phase = alloc_new_phase();
> > > +		if (image->type != KEXEC_TYPE_CRASH)
> > > +			context.kdump = false;
> > > +		else
> > > +			context.kdump = true;
> > > +		context.image = linux_start;
> > > +		context.image_sz = linux_sz;
> > > +		context.initrd = initrd_start;
> > > +		context.initrd_sz = initrd_sz;
> > > +		context.cmdline = cmdline_start;
> > > +		context.cmdline_sz = strlen(cmdline_start);
> > > +		/* bpf-prog fentry, which handle above buffers. */
> > > +		bpf_handle_pefile(&context);
> > > +
> > > +		prepare_nested_pe(&linux_start, &linux_sz, &initrd_start,
> > > +					&initrd_sz, &cmdline_start);
> > > +		/* bpf-prog fentry */
> > > +		bpf_post_handle_pefile(&context);
> > > +		/*
> > > +		 * detach the current bpf-prog from their attachment points.
> > > +		 * It also a point to free any registered interim resource.
> > > +		 * Any resource except attached to phase is interim.
> > > +		 */
> > > +		disarm_bpf_prog();
> > > +	}
> > > +
> > > +	/* the rear of parsed phase contains the result */
> > > +	list_for_each_entry_reverse(phase, &phase_head, head) {
> > > +		if (initrd != NULL && cmdline != NULL && parsed_kernel != NULL)
> > > +			break;
> > > +		list_for_each_entry(res, &phase->res_head, node) {
> > > +			if (!strcmp(res->name, "kernel") && !parsed_kernel) {
> > > +				parsed_kernel = res->r->buf;
> > > +				parsed_len = res->r->data_sz;
> > > +				res->deferred_free = true;
> > > +			} else if (!strcmp(res->name, "initrd") && !initrd) {
> > > +				initrd = res->r->buf;
> > > +				initrd_len = res->r->data_sz;
> > > +				res->deferred_free = true;
> > > +			} else if (!strcmp(res->name, "cmdline") && !cmdline) {
> > > +				cmdline = res->r->buf;
> > > +				cmdline_len = res->r->data_sz;
> > > +				res->deferred_free = true;
> > > +			}
> > > +		}
> > > +
> > > +	}
> > > +
> > > +	if (initrd == NULL || cmdline == NULL || parsed_kernel == NULL) {
> > > +		char *c, buf[64];
> > > +
> > > +		c = buf;
> > > +		if (parsed_kernel == NULL) {
> > > +			strcpy(c, "kernel ");
> > > +			c += strlen("kernel ");
> > > +		}
> > > +		if (initrd == NULL) {
> > > +			strcpy(c, "initrd ");
> > > +			c += strlen("initrd ");
> > > +		}
> > > +		if (cmdline == NULL) {
> > > +			strcpy(c, "cmdline ");
> > > +			c += strlen("cmdline ");
> > > +		}
> > > +		c = '\0';
> > > +		pr_err("Can not extract data for %s", buf);
> > > +		ldata = ERR_PTR(-EINVAL);
> > > +		goto err;
> > > +	}
> > > +	/*
> > > +	 * image's kernel_buf, initrd_buf, cmdline_buf are set. Now they should
> > > +	 * be updated to the new content.
> > > +	 */
> > > +	if (image->kernel_buf != parsed_kernel) {
> > > +		vfree(image->kernel_buf);
> > > +		image->kernel_buf = parsed_kernel;
> > > +		image->kernel_buf_len = parsed_len;
> > > +	}
> > > +	if (image->initrd_buf != initrd) {
> > > +		vfree(image->initrd_buf);
> > > +		image->initrd_buf = initrd;
> > > +		image->initrd_buf_len = initrd_len;
> > > +	}
> > > +	if (image->cmdline_buf != cmdline) {
> > > +		kfree(image->cmdline_buf);
> > > +		image->cmdline_buf = cmdline;
> > > +		image->cmdline_buf_len = cmdline_len;
> > > +	}
> > > +	ret = arch_kexec_kernel_image_probe(image, image->kernel_buf,
> > > +					    image->kernel_buf_len);
> > > +	if (ret) {
> > > +		pr_err("Fail to find suitable image loader\n");
> > > +		ldata = ERR_PTR(ret);
> > > +		goto err;
> > > +	}
> > > +	ldata = kexec_image_load_default(image);
> > > +	if (IS_ERR(ldata)) {
> > > +		pr_err("architecture code fails to load image\n");
> > > +		goto err;
> > > +	}
> > > +	image->image_loader_data = ldata;
> > > +
> > > +err:
> > > +	list_for_each_entry_safe(phase, phase_tmp, &phase_head, head) {
> > > +		list_for_each_entry_safe(res, res_tmp, &phase->res_head, node) {
> > > +			list_del(&res->node);
> > > +			/* defer to kimage_file_post_load_cleanup() */
> > > +			if (res->deferred_free) {
> > > +				res->r->buf = NULL;
> > > +				res->r->buf_sz = 0;
> > > +			}
> > > +			mem_range_result_put(res->r);
> > > +			kfree(res);
> > > +		}
> > > +		list_del(&phase->head);
> > > +		kfree(phase);
> > > +	}
> > > +
> > > +	return ldata;
> > > +}
> > > +
> > > +const struct kexec_file_ops kexec_pe_image_ops = {
> > > +	.probe = pe_image_probe,
> > > +	.load = pe_image_load,
> > > +#ifdef CONFIG_KEXEC_IMAGE_VERIFY_SIG
> > > +	.verify_sig = kexec_kernel_verify_pe_sig,
> > > +#endif
> > > +};  
> >   
> 


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

* Re: [PATCHv3 5/9] kexec: Introduce kexec_pe_image to parse and load PE file
  2025-07-02  9:17       ` Philipp Rudo
@ 2025-07-03  1:17         ` Pingfan Liu
  0 siblings, 0 replies; 20+ messages in thread
From: Pingfan Liu @ 2025-07-03  1:17 UTC (permalink / raw)
  To: Philipp Rudo
  Cc: kexec, 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

On Wed, Jul 2, 2025 at 5:18 PM Philipp Rudo <prudo@redhat.com> wrote:
>
> Hi Pingfan,
>
> On Mon, 30 Jun 2025 21:45:05 +0800
> Pingfan Liu <piliu@redhat.com> wrote:
>
> > On Wed, Jun 25, 2025 at 08:09:50PM +0200, Philipp Rudo wrote:
> > > Hi Pingfan,
> > >
> > > On Thu, 29 May 2025 12:17:40 +0800
> > > Pingfan Liu <piliu@redhat.com> wrote:
> > >
> > > > 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.
> > > >
> > > > A new kexec_file_ops is implementation, named pe_image_ops.
> > > >
> > > > There are some place holder function in this patch. (They will take
> > > > effect after the introduction of kexec bpf light skeleton and bpf
> > > > helpers). Overall the parsing progress is a pipeline, the current
> > > > bpf-prog parser is attached to bpf_handle_pefile(), and detatched at the
> > > > end of the current stage 'disarm_bpf_prog()' the current parsed result
> > > > by the current bpf-prog will be buffered in kernel 'prepare_nested_pe()'
> > > > , and deliver to the next stage.  For each stage, the bpf bytecode is
> > > > extracted from the '.bpf' section in the PE file.
> > > >
> > > > 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
> > > > ---
> > > >  include/linux/kexec.h   |   1 +
> > > >  kernel/Kconfig.kexec    |   8 +
> > > >  kernel/Makefile         |   1 +
> > > >  kernel/kexec_pe_image.c | 356 ++++++++++++++++++++++++++++++++++++++++
> > > >  4 files changed, 366 insertions(+)
> > > >  create mode 100644 kernel/kexec_pe_image.c
> > > >
> > > [...]
> > >
> > > > diff --git a/kernel/kexec_pe_image.c b/kernel/kexec_pe_image.c
> > > > new file mode 100644
> > > > index 0000000000000..3097efccb8502
> > > > --- /dev/null
> > > > +++ b/kernel/kexec_pe_image.c
> > > > @@ -0,0 +1,356 @@
> > > > +// SPDX-License-Identifier: GPL-2.0
> > > > +/*
> > > > + * Kexec PE image loader
> > > > +
> > > > + * Copyright (C) 2025 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/pe.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>
> > > > +
> > > > +
> > > > +static LIST_HEAD(phase_head);
> > > > +
> > > > +struct parsed_phase {
> > > > + struct list_head head;
> > > > + struct list_head res_head;
> > > > +};
> > > > +
> > > > +static struct parsed_phase *cur_phase;
> > > > +
> > > > +static char *kexec_res_names[3] = {"kernel", "initrd", "cmdline"};
> > >
> > > Wouldn't it be better to use a enum rather than strings for the
> > > different resources? Especially as in prepare_nested_pe you are
> >
> > I plan to make bpf_copy_to_kernel() fit for more cases besides kexec. So
> > string may be better choice, and I think it is better to have a
> > subsystem prefix, like "kexec:kernel"
>
> True, although an enum could be utilized directly as, e.g. an index for
> an array directly. Anyway, I don't think there is a single 'best'
> solution here. So feel free to use strings.
>
> > > comparing two strings using == instead of strcmp(). So IIUC it should
> > > always return false.
> > >
> >
> > Oops, I will fix that. In fact, I meaned to assign the pointer
> > kexec_res_names[i] to kexec_res.name in bpf_kexec_carrier(). Later in
> > prepare_nested_pe() can compare two pointers.
> >
> >
> > > > +struct kexec_res {
> > > > + struct list_head node;
> > > > + char *name;
> > > > + /* The free of buffer is deferred to kimage_file_post_load_cleanup */
> > > > + bool deferred_free;
> > > > + struct mem_range_result *r;
> > > > +};
> > > > +
> > > > +static struct parsed_phase *alloc_new_phase(void)
> > > > +{
> > > > + struct parsed_phase *phase = kzalloc(sizeof(struct parsed_phase), GFP_KERNEL);
> > > > +
> > > > + INIT_LIST_HEAD(&phase->head);
> > > > + INIT_LIST_HEAD(&phase->res_head);
> > > > + list_add_tail(&phase->head, &phase_head);
> > > > +
> > > > + return phase;
> > > > +}
> > >
> > > I must admit I don't fully understand how you are handling the
> > > different phases. In particular I don't understand why you are keeping
> > > all the resources a phase returned once it is finished. The way I see
> > > it those resources are only needed once as input for the next phase. So
> > > it should be sufficient to only keep a single kexec_context and update
> > > it when a phase returns a new resource. The way I see it this should
> > > simplify pe_image_load quite a bit. Or am I missing something?
> > >
> >
> > Let us say an aarch64 zboot image embeded in UKI's .linux section.
> > The UKI parser takes apart the image into kernel, initrd, cmdline.
> > And the kernel part contains the zboot PE, including zboot parser.
> > The zboot parser needn't to handle either initrd or cmdline.
> > So I use the phases, and the leaf node is the final parsed result.
>
> Right, that's how the code is working. My point was that when you have
> multiple phases working on the same component, e.g. the kernel image,
> then you still keep all the intermediate kernel images in memory until
> the end. Even though the intermediate images are only used as an input
> for the next phase(s). So my suggestion is to remove them immediately
> once a phase returns a new image. My expectation is that this not only
> reduces the memory usage but also simplifies the code.
>

Ah, got your point. It is a good suggestion especially that it can
save lots of code.

Thanks,

Pingfan

> Thanks
> Philipp
>
> > > > +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 != MZ_MAGIC)
> > > > +         return false;
> > > > + pe = (struct pe_hdr *)(kernel_buf + mz->peaddr);
> > > > + if (pe->magic != PE_MAGIC)
> > > > +         return false;
> > > > + if (pe->opt_hdr_size == 0) {
> > > > +         pr_err("optional header is missing\n");
> > > > +         return false;
> > > > + }
> > > > +
> > > > + return true;
> > > > +}
> > > > +
> > > > +static bool is_valid_format(const char *kernel_buf, unsigned long kernel_len)
> > > > +{
> > > > + return is_valid_pe(kernel_buf, kernel_len);
> > > > +}
> > > > +
> > > > +/*
> > > > + * The UEFI Terse Executable (TE) image has MZ header.
> > > > + */
> > > > +static int pe_image_probe(const char *kernel_buf, unsigned long kernel_len)
> > > > +{
> > > > + return is_valid_pe(kernel_buf, kernel_len) ? 0 : -1;
> > >
> > > Every image, at least on x86, is a valid pe file. So we should check
> > > for the .bpf section rather than the header.
> > >
> >
> > You are right that it should include the check on the existence of .bpf
> > section. On the other hand, the check on PE header in kernel can ensure
> > the kexec-tools passes the right candidate for this parser.
> >
> > > > +}
> > > > +
> > > > +static int get_pe_section(char *file_buf, const char *sect_name,
> > >
> > > s/get_pe_section/pe_get_section/ ?
> > > that would make it more consistent with the other functions.
> >
> > Sure. I will fix it.
> >
> >
> > Thanks for your careful review.
> >
> >
> > Best Regards,
> >
> > Pingfan
> >
> > >
> > > Thanks
> > > Philipp
> > >
> > > > +         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 = file_buf + sect_hdr->data_addr;
> > > > +                 *sect_sz = sect_hdr->raw_data_size;
> > > > +                 return 0;
> > > > +         }
> > > > +         sect_hdr++;
> > > > + }
> > > > +
> > > > + return -1;
> > > > +}
> > > > +
> > > > +static bool pe_has_bpf_section(char *file_buf, unsigned long pe_sz)
> > > > +{
> > > > + char *sect_start = NULL;
> > > > + unsigned long sect_sz = 0;
> > > > + int ret;
> > > > +
> > > > + ret = get_pe_section(file_buf, ".bpf", &sect_start, &sect_sz);
> > > > + if (ret < 0)
> > > > +         return false;
> > > > + return true;
> > > > +}
> > > > +
> > > > +/* 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 *image;
> > > > + int image_sz;
> > > > + char *initrd;
> > > > + int initrd_sz;
> > > > + char *cmdline;
> > > > + int cmdline_sz;
> > > > +};
> > > > +
> > > > +void bpf_handle_pefile(struct kexec_context *context);
> > > > +void bpf_post_handle_pefile(struct kexec_context *context);
> > > > +
> > > > +
> > > > +/*
> > > > + * optimize("O0") prevents inline, compiler constant propagation
> > > > + */
> > > > +__attribute__((used, optimize("O0"))) void bpf_handle_pefile(struct kexec_context *context)
> > > > +{
> > > > +}
> > > > +
> > > > +__attribute__((used, optimize("O0"))) void bpf_post_handle_pefile(struct kexec_context *context)
> > > > +{
> > > > +}
> > > > +
> > > > +/*
> > > > + * PE file may be nested and should be unfold one by one.
> > > > + * Query 'kernel', 'initrd', 'cmdline' in cur_phase, as they are inputs for the
> > > > + * next phase.
> > > > + */
> > > > +static int prepare_nested_pe(char **kernel, unsigned long *kernel_len, char **initrd,
> > > > +         unsigned long *initrd_len, char **cmdline)
> > > > +{
> > > > + struct kexec_res *res;
> > > > + int ret = -1;
> > > > +
> > > > + *kernel = NULL;
> > > > + *kernel_len = 0;
> > > > +
> > > > + list_for_each_entry(res, &cur_phase->res_head, node) {
> > > > +         if (res->name == kexec_res_names[0]) {
> > > > +                 *kernel = res->r->buf;
> > > > +                 *kernel_len = res->r->data_sz;
> > > > +                 ret = 0;
> > > > +         } else if (res->name == kexec_res_names[1]) {
> > > > +                 *initrd = res->r->buf;
> > > > +                 *initrd_len = res->r->data_sz;
> > > > +         } else if (res->name == kexec_res_names[2]) {
> > > > +                 *cmdline = res->r->buf;
> > > > +         }
> > > > + }
> > > > +
> > > > + return ret;
> > > > +}
> > > > +
> > > > +static void *pe_image_load(struct kimage *image,
> > > > +                         char *kernel, unsigned long kernel_len,
> > > > +                         char *initrd, unsigned long initrd_len,
> > > > +                         char *cmdline, unsigned long cmdline_len)
> > > > +{
> > > > + char *parsed_kernel = NULL;
> > > > + unsigned long parsed_len;
> > > > + char *linux_start, *initrd_start, *cmdline_start, *bpf_start;
> > > > + unsigned long linux_sz, initrd_sz, cmdline_sz, bpf_sz;
> > > > + struct parsed_phase *phase, *phase_tmp;
> > > > + struct kexec_res *res, *res_tmp;
> > > > + void *ldata;
> > > > + int ret;
> > > > +
> > > > + linux_start = kernel;
> > > > + linux_sz = kernel_len;
> > > > + initrd_start = initrd;
> > > > + initrd_sz = initrd_len;
> > > > + cmdline_start = cmdline;
> > > > + cmdline_sz = cmdline_len;
> > > > +
> > > > + while (is_valid_format(linux_start, linux_sz) &&
> > > > +        pe_has_bpf_section(linux_start, linux_sz)) {
> > > > +         struct kexec_context context;
> > > > +
> > > > +         get_pe_section(linux_start, ".bpf", &bpf_start, &bpf_sz);
> > > > +         if (!!bpf_sz) {
> > > > +                 /* load and attach bpf-prog */
> > > > +                 ret = arm_bpf_prog(bpf_start, bpf_sz);
> > > > +                 if (ret) {
> > > > +                         pr_err("Fail to load .bpf section\n");
> > > > +                         ldata = ERR_PTR(ret);
> > > > +                         goto err;
> > > > +                 }
> > > > +         }
> > > > +         cur_phase = alloc_new_phase();
> > > > +         if (image->type != KEXEC_TYPE_CRASH)
> > > > +                 context.kdump = false;
> > > > +         else
> > > > +                 context.kdump = true;
> > > > +         context.image = linux_start;
> > > > +         context.image_sz = linux_sz;
> > > > +         context.initrd = initrd_start;
> > > > +         context.initrd_sz = initrd_sz;
> > > > +         context.cmdline = cmdline_start;
> > > > +         context.cmdline_sz = strlen(cmdline_start);
> > > > +         /* bpf-prog fentry, which handle above buffers. */
> > > > +         bpf_handle_pefile(&context);
> > > > +
> > > > +         prepare_nested_pe(&linux_start, &linux_sz, &initrd_start,
> > > > +                                 &initrd_sz, &cmdline_start);
> > > > +         /* bpf-prog fentry */
> > > > +         bpf_post_handle_pefile(&context);
> > > > +         /*
> > > > +          * detach the current bpf-prog from their attachment points.
> > > > +          * It also a point to free any registered interim resource.
> > > > +          * Any resource except attached to phase is interim.
> > > > +          */
> > > > +         disarm_bpf_prog();
> > > > + }
> > > > +
> > > > + /* the rear of parsed phase contains the result */
> > > > + list_for_each_entry_reverse(phase, &phase_head, head) {
> > > > +         if (initrd != NULL && cmdline != NULL && parsed_kernel != NULL)
> > > > +                 break;
> > > > +         list_for_each_entry(res, &phase->res_head, node) {
> > > > +                 if (!strcmp(res->name, "kernel") && !parsed_kernel) {
> > > > +                         parsed_kernel = res->r->buf;
> > > > +                         parsed_len = res->r->data_sz;
> > > > +                         res->deferred_free = true;
> > > > +                 } else if (!strcmp(res->name, "initrd") && !initrd) {
> > > > +                         initrd = res->r->buf;
> > > > +                         initrd_len = res->r->data_sz;
> > > > +                         res->deferred_free = true;
> > > > +                 } else if (!strcmp(res->name, "cmdline") && !cmdline) {
> > > > +                         cmdline = res->r->buf;
> > > > +                         cmdline_len = res->r->data_sz;
> > > > +                         res->deferred_free = true;
> > > > +                 }
> > > > +         }
> > > > +
> > > > + }
> > > > +
> > > > + if (initrd == NULL || cmdline == NULL || parsed_kernel == NULL) {
> > > > +         char *c, buf[64];
> > > > +
> > > > +         c = buf;
> > > > +         if (parsed_kernel == NULL) {
> > > > +                 strcpy(c, "kernel ");
> > > > +                 c += strlen("kernel ");
> > > > +         }
> > > > +         if (initrd == NULL) {
> > > > +                 strcpy(c, "initrd ");
> > > > +                 c += strlen("initrd ");
> > > > +         }
> > > > +         if (cmdline == NULL) {
> > > > +                 strcpy(c, "cmdline ");
> > > > +                 c += strlen("cmdline ");
> > > > +         }
> > > > +         c = '\0';
> > > > +         pr_err("Can not extract data for %s", buf);
> > > > +         ldata = ERR_PTR(-EINVAL);
> > > > +         goto err;
> > > > + }
> > > > + /*
> > > > +  * image's kernel_buf, initrd_buf, cmdline_buf are set. Now they should
> > > > +  * be updated to the new content.
> > > > +  */
> > > > + if (image->kernel_buf != parsed_kernel) {
> > > > +         vfree(image->kernel_buf);
> > > > +         image->kernel_buf = parsed_kernel;
> > > > +         image->kernel_buf_len = parsed_len;
> > > > + }
> > > > + if (image->initrd_buf != initrd) {
> > > > +         vfree(image->initrd_buf);
> > > > +         image->initrd_buf = initrd;
> > > > +         image->initrd_buf_len = initrd_len;
> > > > + }
> > > > + if (image->cmdline_buf != cmdline) {
> > > > +         kfree(image->cmdline_buf);
> > > > +         image->cmdline_buf = cmdline;
> > > > +         image->cmdline_buf_len = cmdline_len;
> > > > + }
> > > > + ret = arch_kexec_kernel_image_probe(image, image->kernel_buf,
> > > > +                                     image->kernel_buf_len);
> > > > + if (ret) {
> > > > +         pr_err("Fail to find suitable image loader\n");
> > > > +         ldata = ERR_PTR(ret);
> > > > +         goto err;
> > > > + }
> > > > + ldata = kexec_image_load_default(image);
> > > > + if (IS_ERR(ldata)) {
> > > > +         pr_err("architecture code fails to load image\n");
> > > > +         goto err;
> > > > + }
> > > > + image->image_loader_data = ldata;
> > > > +
> > > > +err:
> > > > + list_for_each_entry_safe(phase, phase_tmp, &phase_head, head) {
> > > > +         list_for_each_entry_safe(res, res_tmp, &phase->res_head, node) {
> > > > +                 list_del(&res->node);
> > > > +                 /* defer to kimage_file_post_load_cleanup() */
> > > > +                 if (res->deferred_free) {
> > > > +                         res->r->buf = NULL;
> > > > +                         res->r->buf_sz = 0;
> > > > +                 }
> > > > +                 mem_range_result_put(res->r);
> > > > +                 kfree(res);
> > > > +         }
> > > > +         list_del(&phase->head);
> > > > +         kfree(phase);
> > > > + }
> > > > +
> > > > + return ldata;
> > > > +}
> > > > +
> > > > +const struct kexec_file_ops kexec_pe_image_ops = {
> > > > + .probe = pe_image_probe,
> > > > + .load = pe_image_load,
> > > > +#ifdef CONFIG_KEXEC_IMAGE_VERIFY_SIG
> > > > + .verify_sig = kexec_kernel_verify_pe_sig,
> > > > +#endif
> > > > +};
> > >
> >
>


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

end of thread, other threads:[~2025-07-03  1:17 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-29  4:17 [PATCHv3 0/9] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
2025-05-29  4:17 ` [PATCHv3 1/9] kexec_file: Make kexec_image_load_default global visible Pingfan Liu
2025-05-29  4:17 ` [PATCHv3 2/9] lib/decompress: Keep decompressor when CONFIG_KEXEC_PE_IMAGE Pingfan Liu
2025-05-29  4:17 ` [PATCHv3 3/9] bpf: Introduce bpf_copy_to_kernel() to buffer the content from bpf-prog Pingfan Liu
2025-05-29 11:48   ` kernel test robot
2025-06-25 18:10   ` Philipp Rudo
2025-05-29  4:17 ` [PATCHv3 4/9] bpf: Introduce decompressor kfunc Pingfan Liu
2025-05-29 12:31   ` kernel test robot
2025-05-29  4:17 ` [PATCHv3 5/9] kexec: Introduce kexec_pe_image to parse and load PE file Pingfan Liu
2025-06-25 18:09   ` Philipp Rudo
2025-06-30 13:45     ` Pingfan Liu
2025-07-02  9:17       ` Philipp Rudo
2025-07-03  1:17         ` Pingfan Liu
2025-05-29  4:17 ` [PATCHv3 6/9] kexec: Integrate with the introduced bpf kfuncs Pingfan Liu
2025-05-29  4:17 ` [PATCHv3 7/9] kexec: Introduce a bpf-prog lskel to parse PE file Pingfan Liu
2025-05-29  4:17 ` [PATCHv3 8/9] kexec: Integrate bpf light skeleton to load zboot image Pingfan Liu
2025-06-25 18:10   ` Philipp Rudo
2025-06-30 12:40     ` Pingfan Liu
2025-05-29  4:17 ` [PATCHv3 9/9] arm64/kexec: Add PE image format support Pingfan Liu
2025-05-29 15:34   ` kernel test robot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).