From: Pingfan Liu <piliu@redhat.com>
To: kexec@lists.infradead.org
Cc: Pingfan Liu <piliu@redhat.com>,
"David S. Miller" <davem@davemloft.net>,
Alexei Starovoitov <ast@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
John Fastabend <john.fastabend@gmail.com>,
Andrii Nakryiko <andrii@kernel.org>,
Martin KaFai Lau <martin.lau@linux.dev>,
Eduard Zingerman <eddyz87@gmail.com>, Song Liu <song@kernel.org>,
Yonghong Song <yonghong.song@linux.dev>,
Jeremy Linton <jeremy.linton@arm.com>,
Catalin Marinas <catalin.marinas@arm.com>,
Will Deacon <will@kernel.org>, Ard Biesheuvel <ardb@kernel.org>,
Simon Horman <horms@kernel.org>,
Gerd Hoffmann <kraxel@redhat.com>,
Vitaly Kuznetsov <vkuznets@redhat.com>,
Philipp Rudo <prudo@redhat.com>, Viktor Malik <vmalik@redhat.com>,
Jan Hendrik Farr <kernel@jfarr.cc>, Baoquan He <bhe@redhat.com>,
Dave Young <dyoung@redhat.com>,
Andrew Morton <akpm@linux-foundation.org>,
bpf@vger.kernel.org, systemd-devel@lists.freedesktop.org,
linux-kernel@vger.kernel.org
Subject: [PATCHv7 12/13] tools/kexec: Introduce a bpf-prog to handle UKI image
Date: Sun, 22 Mar 2026 09:44:01 +0800 [thread overview]
Message-ID: <20260322014402.8815-13-piliu@redhat.com> (raw)
In-Reply-To: <20260322014402.8815-1-piliu@redhat.com>
Signed-off-by: Pingfan Liu <piliu@redhat.com>
Cc: Baoquan He <bhe@redhat.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Philipp Rudo <prudo@redhat.com>
Cc: bpf@vger.kernel.org
To: kexec@lists.infradead.org
---
tools/kexec/Makefile | 18 ++-
tools/kexec/uki_parser_bpf.c | 235 +++++++++++++++++++++++++++++++++++
2 files changed, 252 insertions(+), 1 deletion(-)
create mode 100644 tools/kexec/uki_parser_bpf.c
diff --git a/tools/kexec/Makefile b/tools/kexec/Makefile
index a404a1453c888..c0e2ad44658e3 100644
--- a/tools/kexec/Makefile
+++ b/tools/kexec/Makefile
@@ -43,6 +43,7 @@ HEADERS = vmlinux.h bpf_helper_defs.h image_size.h
# <P>.bpf - final ELF wrapper with .bpf.1 section
# ---------------------------------------------------------------------------
BPF_TARGETS = zboot
+BPF_TARGETS += uki
define BPF_ARTIFACTS
$(1)_parser_bpf.o $(1)_parser_bpf.lskel.h $(1)_bytecode.c $(1)_bytecode.o $(1).bpf
@@ -54,8 +55,13 @@ ALL_BPF_ARTIFACTS = $(foreach t,$(BPF_TARGETS),$(call BPF_ARTIFACTS,$(t)))
# Top-level phony targets
# ---------------------------------------------------------------------------
zboot: $(HEADERS) $(call BPF_ARTIFACTS,zboot) build_zboot_image
+ifeq ($(ARCH),$(filter $(ARCH),arm64 riscv loongarch))
+uki: $(HEADERS) zboot.bpf $(call BPF_ARTIFACTS,uki)
+else
+uki: $(HEADERS) $(call BPF_ARTIFACTS,uki)
+endif
-.PHONY: zboot clean
+.PHONY: zboot uki clean
# ---------------------------------------------------------------------------
# Shared header rules
@@ -123,6 +129,15 @@ image_size.h: $(KERNEL_IMAGE)
# ---------------------------------------------------------------------------
ZBOOT_BPF_MAPS := .bpf.1:zboot_bytecode.o
+# uki.bpf sections depend on architecture:
+# arm64/riscv/loongarch: .bpf.1 (uki bytecode) + .bpf.nested (zboot.bpf ELF)
+# x86: .bpf.1 only. zboot format does not exist on x86
+ifeq ($(ARCH),$(filter $(ARCH),arm64 riscv loongarch))
+UKI_BPF_MAPS := .bpf.1:uki_bytecode.o .bpf.nested:zboot.bpf
+else
+UKI_BPF_MAPS := .bpf.1:uki_bytecode.o
+endif
+
# ---------------------------------------------------------------------------
# Helpers to build objcopy flags from a BPF_MAPS list
# ---------------------------------------------------------------------------
@@ -154,6 +169,7 @@ $(1).bpf: $(foreach m,$($(2)_BPF_MAPS),$(call source_file,$(m)))
endef
$(eval $(call BPF_WRAPPER_RULE,zboot,ZBOOT))
+$(eval $(call BPF_WRAPPER_RULE,uki,UKI))
# ---------------------------------------------------------------------------
# Clean
diff --git a/tools/kexec/uki_parser_bpf.c b/tools/kexec/uki_parser_bpf.c
new file mode 100644
index 0000000000000..1eb542d8acd4c
--- /dev/null
+++ b/tools/kexec/uki_parser_bpf.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2025, 2026 Red Hat, Inc
+//
+#include "vmlinux.h"
+#include <bpf_helpers.h>
+#include <bpf_tracing.h>
+#include "image_size.h"
+
+/* ringbuf 2,3,4 are useless */
+#define MIN_BUF_SIZE 1
+#define MAX_RECORD_SIZE (IMAGE_SIZE + 40960)
+#define RINGBUF1_SIZE IMAGE_SIZE_POWER2_ALIGN
+#define RINGBUF2_SIZE MIN_BUF_SIZE
+#define RINGBUF3_SIZE MIN_BUF_SIZE
+#define RINGBUF4_SIZE MIN_BUF_SIZE
+
+#include "template.c"
+
+#define MAX_PARSING_BUFS 16
+#define PE_SCAN_MAX 16
+#define ELF_SCAN_MAX 16
+
+/* SHN_UNDEF is a uapi macro not exported via BTF/vmlinux.h */
+#ifndef SHN_UNDEF
+#define SHN_UNDEF 0
+#endif
+
+#ifndef EIO
+#define EIO 5
+#endif
+#ifndef EINVAL
+#define EINVAL 22
+#endif
+
+static const char linux_sect_name[] = ".linux";
+static const char initrd_sect_name[] = ".initrd";
+static const char cmdline_sect_name[] = ".cmdline";
+
+
+#define MAKE_CMD(cmd, subcmd) ((__u32)(cmd) | ((__u32)(subcmd) << 16))
+
+static int fill_cmd(char *buf, __u32 cmd_word, __u32 pipeline_flag,
+ const char *src, __u32 data_len)
+{
+ struct cmd_hdr *hdr;
+ char *payload;
+
+ __u16 cmd = (__u16)(cmd_word & 0xffff);
+ __u16 subcmd = (__u16)(cmd_word >> 16);
+
+ hdr = (struct cmd_hdr *)buf;
+ hdr->cmd = cmd;
+ hdr->subcmd = subcmd;
+ hdr->pipeline_flag = pipeline_flag;
+ hdr->payload_len = data_len;
+ hdr->num_chunks = 0;
+
+ payload = (char *)(hdr + 1);
+ /* Only cmd, no payload */
+ if (!src || !data_len)
+ return sizeof(*hdr);
+ if (data_len > MAX_RECORD_SIZE - sizeof(struct cmd_hdr))
+ return -EINVAL;
+ bpf_probe_read_kernel(payload, data_len, src);
+
+ return sizeof(*hdr) + data_len;
+}
+
+static int process_uki_pe(const char *pe_buf, __u32 pe_sz, char *scratch,
+ struct bpf_parser_context *bpf_ctx)
+{
+ __u32 pe_offset, pe_sig, section_table_off;
+ __u16 dos_magic, num_sections, opt_hdr_sz;
+ __u16 pipeline_flag = 0;
+ int i, ret;
+
+ if (pe_sz < 64)
+ return -EINVAL;
+ if (pe_sz > MAX_RECORD_SIZE)
+ return -EINVAL;
+
+ if (bpf_probe_read_kernel(&dos_magic, sizeof(dos_magic), pe_buf) < 0)
+ return -EIO;
+ if (dos_magic != 0x5A4D)
+ return -EINVAL;
+
+ if (bpf_probe_read_kernel(&pe_offset, sizeof(pe_offset),
+ pe_buf + 0x3c) < 0)
+ return -EIO;
+ if (pe_offset + 24 > pe_sz)
+ return -EINVAL;
+
+ if (bpf_probe_read_kernel(&pe_sig, sizeof(pe_sig),
+ pe_buf + pe_offset) < 0)
+ return -EIO;
+ if (pe_sig != 0x00004550)
+ return -EINVAL;
+
+ if (bpf_probe_read_kernel(&num_sections, sizeof(num_sections),
+ pe_buf + pe_offset + 6) < 0)
+ return -EIO;
+ if (bpf_probe_read_kernel(&opt_hdr_sz, sizeof(opt_hdr_sz),
+ pe_buf + pe_offset + 20) < 0)
+ return -EIO;
+
+ section_table_off = pe_offset + 4 + 20 + opt_hdr_sz;
+ if (section_table_off >= pe_sz)
+ return -EINVAL;
+
+ for (i = 0; i < PE_SCAN_MAX; i++) {
+ __u32 raw_size, raw_off, shdr_off;
+ char sec_name[8];
+ __u16 subcmd;
+
+ if (i >= num_sections)
+ break;
+
+ shdr_off = section_table_off + i * 40;
+ if (shdr_off + 40 > pe_sz)
+ break;
+
+ if (bpf_probe_read_kernel(sec_name, sizeof(sec_name),
+ pe_buf + shdr_off) < 0)
+ continue;
+
+ pipeline_flag = 0;
+ if (__builtin_memcmp(sec_name, linux_sect_name, sizeof(linux_sect_name)) == 0) {
+ subcmd = KEXEC_BPF_SUBCMD_KERNEL;
+ /*
+ * .linux section may contain different format kernel, which should be
+ * passed to the next stage to handle
+ */
+ pipeline_flag = KEXEC_BPF_PIPELINE_FILL;
+ }
+ else if (__builtin_memcmp(sec_name, initrd_sect_name, sizeof(initrd_sect_name)) == 0)
+ subcmd = KEXEC_BPF_SUBCMD_INITRD;
+ else if (__builtin_memcmp(sec_name, cmdline_sect_name, sizeof(cmdline_sect_name)) == 0)
+ subcmd = KEXEC_BPF_SUBCMD_CMDLINE;
+ else
+ continue;
+
+ if (bpf_probe_read_kernel(&raw_size, sizeof(raw_size),
+ pe_buf + shdr_off + 16) < 0)
+ continue;
+ if (bpf_probe_read_kernel(&raw_off, sizeof(raw_off),
+ pe_buf + shdr_off + 20) < 0)
+ continue;
+
+ if (!raw_size || raw_off + raw_size > pe_sz)
+ continue;
+
+ ret = fill_cmd(scratch,
+ MAKE_CMD(KEXEC_BPF_CMD_COPY, subcmd),
+ pipeline_flag,
+ pe_buf + raw_off,
+ raw_size);
+ ret = bpf_buffer_parser(scratch, ret, bpf_ctx);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+SEC("fentry.s/kexec_image_parser_anchor")
+int BPF_PROG(parse_uki, struct kexec_context *context, unsigned long parser_id)
+{
+ struct bpf_parser_context *bpf_ctx;
+ char *buf0, *buf1, *scratch;
+ __u8 magic[4];
+ int ret;
+
+ bpf_printk("parse_uki: start\n");
+ buf0 = BPF_CORE_READ(context, parsing_buf[0]);
+ if (!buf0)
+ return 0;
+
+ bpf_ctx = bpf_get_parser_context(parser_id);
+ if (!bpf_ctx) {
+ bpf_printk("parse_uki: no parser context for id %lu\n",
+ parser_id);
+ return 0;
+ }
+
+ buf1 = BPF_CORE_READ(context, parsing_buf[1]);
+
+ /*
+ * Single-buffer path: original parse_uki behaviour.
+ * parsing_buf[0] is either a plain PE UKI or an ELF container
+ * with embedded .uki / .addon sections.
+ */
+ if (!buf1) {
+ unsigned long sz = BPF_CORE_READ(context, parsing_buf_sz[0]);
+
+ if (sz < 4)
+ goto out;
+
+ if (bpf_probe_read_kernel(magic, sizeof(magic), buf0) < 0)
+ goto out;
+
+ scratch = bpf_ringbuf_reserve(&ringbuf_1, MAX_RECORD_SIZE, 0);
+ if (!scratch) {
+ bpf_printk("ringbuf reserve failed\n");
+ goto out;
+ }
+
+ if (magic[0] == 'M' && magic[1] == 'Z') {
+ bpf_printk("call process_uki_pe\n");
+ ret = process_uki_pe(buf0, (__u32)sz, scratch, bpf_ctx);
+ if (ret) {
+ bpf_printk("parse_uki: PE path failed: %d\n",
+ ret);
+ }
+ else {
+ bpf_printk("fill KEXEC_BPF_CMD_DONE \n");
+ ret = fill_cmd(scratch, MAKE_CMD(KEXEC_BPF_CMD_DONE, 0),
+ 0, NULL, 0);
+ ret = bpf_buffer_parser(scratch, ret, bpf_ctx);
+ if (ret)
+ bpf_printk("parse_uki: inject KEXEC_BPF_CMD_DONE failed: %d\n",
+ ret);
+ }
+ } else {
+ bpf_printk("parse_uki: unrecognized format\n");
+ }
+
+ bpf_ringbuf_discard(scratch, BPF_RB_NO_WAKEUP);
+ goto out;
+ }
+
+out:
+ bpf_put_parser_context(bpf_ctx);
+ return 0;
+}
--
2.49.0
next prev parent reply other threads:[~2026-03-22 1:47 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-22 1:43 [PATCHv7 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Pingfan Liu
2026-03-22 1:43 ` [PATCHv7 01/13] bpf: Introduce kfuncs to parser buffer content Pingfan Liu
2026-03-22 2:14 ` bot+bpf-ci
2026-03-22 1:43 ` [PATCHv7 02/13] kexec_file: Use bpf-prog to decompose image Pingfan Liu
2026-03-22 2:14 ` bot+bpf-ci
2026-03-22 1:43 ` [PATCHv7 03/13] lib/decompress: Keep decompressor when CONFIG_KEEP_DECOMPRESSOR Pingfan Liu
2026-03-22 2:26 ` bot+bpf-ci
2026-03-22 1:43 ` [PATCHv7 04/13] kexec_file: Implement decompress method for parser Pingfan Liu
2026-03-22 2:26 ` bot+bpf-ci
2026-03-22 1:43 ` [PATCHv7 05/13] kexec_file: Implement copy " Pingfan Liu
2026-03-22 1:43 ` [PATCHv7 06/13] kexec_file: Chain the stages into a pipeline Pingfan Liu
2026-03-22 2:14 ` bot+bpf-ci
2026-03-22 1:43 ` [PATCHv7 07/13] kexec_file: Introduce a bpf-prog lskel to run a format parser Pingfan Liu
2026-03-22 1:43 ` [PATCHv7 08/13] kexec_file: Factor out routine to find a symbol in ELF Pingfan Liu
2026-03-22 2:14 ` bot+bpf-ci
2026-03-23 7:07 ` kernel test robot
2026-03-23 10:19 ` kernel test robot
2026-03-22 1:43 ` [PATCHv7 09/13] kexec_file: Integrate bpf light skeleton to load image with bpf-prog Pingfan Liu
2026-03-22 2:14 ` bot+bpf-ci
2026-03-22 1:43 ` [PATCHv7 10/13] arm64/kexec: Select KEXEC_BPF to support UEFI-style kernel image Pingfan Liu
2026-03-22 1:44 ` [PATCHv7 11/13] tools/kexec: Introduce a bpf-prog to handle zboot image Pingfan Liu
2026-03-22 2:26 ` bot+bpf-ci
2026-03-22 1:44 ` Pingfan Liu [this message]
2026-03-22 1:44 ` [PATCHv7 13/13] tools/kexec: Introduce a tool to build zboot envelop Pingfan Liu
2026-03-23 19:02 ` [PATCHv7 00/13] kexec: Use BPF lskel to enable kexec to load PE format boot image Andrew Morton
2026-03-24 7:09 ` Pingfan Liu
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260322014402.8815-13-piliu@redhat.com \
--to=piliu@redhat.com \
--cc=akpm@linux-foundation.org \
--cc=andrii@kernel.org \
--cc=ardb@kernel.org \
--cc=ast@kernel.org \
--cc=bhe@redhat.com \
--cc=bpf@vger.kernel.org \
--cc=catalin.marinas@arm.com \
--cc=daniel@iogearbox.net \
--cc=davem@davemloft.net \
--cc=dyoung@redhat.com \
--cc=eddyz87@gmail.com \
--cc=horms@kernel.org \
--cc=jeremy.linton@arm.com \
--cc=john.fastabend@gmail.com \
--cc=kernel@jfarr.cc \
--cc=kexec@lists.infradead.org \
--cc=kraxel@redhat.com \
--cc=linux-kernel@vger.kernel.org \
--cc=martin.lau@linux.dev \
--cc=prudo@redhat.com \
--cc=song@kernel.org \
--cc=systemd-devel@lists.freedesktop.org \
--cc=vkuznets@redhat.com \
--cc=vmalik@redhat.com \
--cc=will@kernel.org \
--cc=yonghong.song@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.