[PATCHv7 11/13] tools/kexec: Introduce a bpf-prog to handle zboot image

Pingfan Liu piliu at redhat.com
Sat Mar 21 18:44:00 PDT 2026


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

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

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

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

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

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




More information about the kexec mailing list