From nobody Sat Apr 4 00:07:09 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4C1C437BE85 for ; Sun, 22 Mar 2026 01:44:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774143897; cv=none; b=lMLmV+C8lP8dS2XCIoO5M7bBoTYl5vhzE3Ya+eIR/DKdNChrpm2/yVNR8ab2KRAyqDdoZqVtRnehNxDIfEvMzwkgpY+h8d/SgaNN127YYrnhnEnSy7Doe0WyRj4ob5Y0MBls95LGJwfBS7apL7nnlwaGHo2Kd8yQnSs0kVu+TBw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774143897; c=relaxed/simple; bh=FC5uQXV7crRBTVBElwFW7HLe2rG6bGvWT6YjTy65euI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=aDL4UdwzMyTVIHgR47wq9bqsEOlja03OOwUOG3SF76KVZ9dkB1AJQ4Qp7MKOn01VOd3R5dgJAus5ZV9HNrTJ+zgF1/2BNo6QP/yHNIa9ntGneWKgWWkzIjVmgu0NCVOQTL86eQv7BD+t/fZnXXrYGlB8hTeb9krXA1w1BLG0KNs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=jKemwgFq; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="jKemwgFq" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774143894; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=CwWLC1ynWW8IemkY1+a4g7N2hZG2j6xq11JxcVWyNu4=; b=jKemwgFqNTXpXVzRj3MIQsJSFVE5ipcinEAD/2b+Jm0OD8Md2TLc1iWwk+WKI/j0CAqdgG 5Fkl43uhLnSGT3RiZLSeuQbjz+COQeQqTlnFTkvu0z7gYkc4FQvZIOb89f65pEkuv/f15a zEEnMpZuHC8Dt0DHDfQdcqeiPZRtknk= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-136-nfl0JzubPsOR4KCoDrX7qQ-1; Sat, 21 Mar 2026 21:44:51 -0400 X-MC-Unique: nfl0JzubPsOR4KCoDrX7qQ-1 X-Mimecast-MFC-AGG-ID: nfl0JzubPsOR4KCoDrX7qQ_1774143888 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id DA1D61956048; Sun, 22 Mar 2026 01:44:47 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.72.112.22]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 1177730001A2; Sun, 22 Mar 2026 01:44:32 +0000 (UTC) From: Pingfan Liu To: bpf@vger.kernel.org Cc: Pingfan Liu , "David S. Miller" , Alexei Starovoitov , Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Eduard Zingerman , Song Liu , Yonghong Song , Jeremy Linton , Catalin Marinas , Will Deacon , Ard Biesheuvel , Simon Horman , Gerd Hoffmann , Vitaly Kuznetsov , Philipp Rudo , Viktor Malik , Jan Hendrik Farr , Baoquan He , Dave Young , Andrew Morton , kexec@lists.infradead.org, systemd-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa Subject: [PATCHv7 01/13] bpf: Introduce kfuncs to parser buffer content Date: Sun, 22 Mar 2026 09:43:50 +0800 Message-ID: <20260322014402.8815-2-piliu@redhat.com> In-Reply-To: <20260322014402.8815-1-piliu@redhat.com> References: <20260322014402.8815-1-piliu@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" In the security kexec_file_load case, the buffer holding the kernel image should not be accessible from userspace. Typically, BPF data flow occurs between user space and kernel space in either direction. However, the above case presents a unique scenario where the kernel, instead of a user task, reads data from a file, passes it to a BPF program for parsing, and finally stores the parsed result. This requires a mechanism to channel the intermediate data from the BPF program directly to the kernel. BPF buffer parser kfuncs are introduced to serve this purpose: BTF_ID_FLAGS(func, bpf_get_parser_context, KF_ACQUIRE | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_put_parser_context, KF_RELEASE) BTF_ID_FLAGS(func, bpf_buffer_parser, KF_SLEEPABLE) where bpf_get_parser_context() and bpf_put_parser_context() manage the trusted argument, and bpf_buffer_parser() forwards data to a callback that processes the structured buffer constructed by the BPF program. Signed-off-by: Pingfan Liu Cc: Alexei Starovoitov Cc: Daniel Borkmann Cc: David S. Miller Cc: John Fastabend Cc: Andrii Nakryiko Cc: Martin KaFai Lau Cc: Eduard Zingerman Cc: Song Liu Cc: Yonghong Song Cc: KP Singh Cc: Stanislav Fomichev Cc: Hao Luo Cc: Jiri Olsa To: bpf@vger.kernel.org --- include/linux/bpf.h | 20 ++++ kernel/bpf/Makefile | 3 + kernel/bpf/bpf_buffer_parser.c | 186 +++++++++++++++++++++++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 kernel/bpf/bpf_buffer_parser.c diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 05b34a6355b03..93a1c9163685f 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -4005,4 +4005,24 @@ static inline int bpf_map_check_op_flags(struct bpf_= map *map, u64 flags, u64 all return 0; } =20 +struct bpf_parser_buf { + char *buf; + int size; +}; + +struct bpf_parser_context; +typedef int (*bpf_parser_handler_t)(struct bpf_parser_context *ctx); + +struct bpf_parser_context { + struct kref ref; + struct hlist_node hash_node; + /* This callback should be sync so that @buf can be freed */ + bpf_parser_handler_t func; + struct bpf_parser_buf *buf; + void *data; +}; + +struct bpf_parser_context *alloc_bpf_parser_context(bpf_parser_handler_t f= unc, + void *data); +void put_bpf_parser_context(struct bpf_parser_context *ctx); #endif /* _LINUX_BPF_H */ diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile index 79cf22860a99b..cceff165b4037 100644 --- a/kernel/bpf/Makefile +++ b/kernel/bpf/Makefile @@ -66,6 +66,9 @@ obj-$(CONFIG_BPF_SYSCALL) +=3D kmem_cache_iter.o ifeq ($(CONFIG_DMA_SHARED_BUFFER),y) obj-$(CONFIG_BPF_SYSCALL) +=3D dmabuf_iter.o endif +ifeq ($(CONFIG_KEXEC_BPF),y) +obj-$(CONFIG_BPF_SYSCALL) +=3D bpf_buffer_parser.o +endif =20 CFLAGS_REMOVE_percpu_freelist.o =3D $(CC_FLAGS_FTRACE) CFLAGS_REMOVE_bpf_lru_list.o =3D $(CC_FLAGS_FTRACE) diff --git a/kernel/bpf/bpf_buffer_parser.c b/kernel/bpf/bpf_buffer_parser.c new file mode 100644 index 0000000000000..5d5c068330791 --- /dev/null +++ b/kernel/bpf/bpf_buffer_parser.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include + +#define BPF_CONTEXT_HASH_BITS 10 + +static DEFINE_SPINLOCK(bpf_parser_context_lock); +static DEFINE_HASHTABLE(bpf_parser_context_map, BPF_CONTEXT_HASH_BITS); + +/* Generate a simple hash key from pointer address */ +static inline unsigned int bpf_parser_context_hash_key(struct bpf_parser_c= ontext *ctx) +{ + return hash_ptr(ctx, BPF_CONTEXT_HASH_BITS); +} + +static void release_bpf_parser_context(struct kref *kref) +{ + struct bpf_parser_context *ctx =3D container_of(kref, struct bpf_parser_c= ontext, ref); + + if (ctx->buf) { + vfree(ctx->buf->buf); + kfree(ctx->buf); + } + spin_lock(&bpf_parser_context_lock); + hash_del(&ctx->hash_node); + spin_unlock(&bpf_parser_context_lock); + kfree(ctx); +} + +struct bpf_parser_context *alloc_bpf_parser_context(bpf_parser_handler_t f= unc, + void *data) +{ + struct bpf_parser_context *ctx; + unsigned int key; + + ctx =3D kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return NULL; + ctx->func =3D func; + ctx->data =3D data; + kref_init(&ctx->ref); + key =3D bpf_parser_context_hash_key(ctx); + spin_lock(&bpf_parser_context_lock); + hash_add(bpf_parser_context_map, &ctx->hash_node, key); + spin_unlock(&bpf_parser_context_lock); + + return ctx; +} + +void put_bpf_parser_context(struct bpf_parser_context *ctx) +{ + if (!ctx) + return; + kref_put(&ctx->ref, release_bpf_parser_context); +} + +static struct bpf_parser_context *find_bpf_parser_context(unsigned long id) +{ + struct bpf_parser_context *ctx; + unsigned int key; + int cnt; + + key =3D bpf_parser_context_hash_key((struct bpf_parser_context *)id); + spin_lock(&bpf_parser_context_lock); + hash_for_each_possible(bpf_parser_context_map, ctx, hash_node, key) { + if (ctx =3D=3D (struct bpf_parser_context *)id) { + cnt =3D kref_get_unless_zero(&ctx->ref); + if (!cnt) + ctx =3D NULL; + spin_unlock(&bpf_parser_context_lock); + return ctx; + } + } + spin_unlock(&bpf_parser_context_lock); + + return NULL; +} + +__bpf_kfunc_start_defs() + +__bpf_kfunc struct bpf_parser_context *bpf_get_parser_context(unsigned lon= g id) +{ + struct bpf_parser_context *ctx; + + ctx =3D find_bpf_parser_context(id); + + return ctx; +} + +__bpf_kfunc void bpf_put_parser_context(struct bpf_parser_context *ctx) +{ + put_bpf_parser_context(ctx); +} + +__bpf_kfunc void bpf_parser_context_release_dtor(void *ctx) +{ + put_bpf_parser_context(ctx); +} +CFI_NOSEAL(bpf_parser_context_release_dtor); + +__bpf_kfunc int bpf_buffer_parser(char *buf, int buf_sz, + struct bpf_parser_context *context) +{ + struct bpf_parser_buf *parser_buf; + void *old_val; + int ret; + char *b; + + if (buf =3D=3D NULL || buf_sz <=3D 0) + return -EINVAL; + + if (unlikely(context->func =3D=3D NULL)) + return -EINVAL; + + /* Lock the pointer */ + old_val =3D cmpxchg(&context->buf, NULL, (void *)1); + if (old_val !=3D NULL) + return -EBUSY; + b =3D __vmalloc(buf_sz, GFP_KERNEL_ACCOUNT | __GFP_ZERO); + if (!b) { + context->buf =3D NULL; + return -ENOMEM; + } + ret =3D copy_from_kernel_nofault(b, buf, buf_sz); + if (!!ret) { + context->buf =3D NULL; + vfree(b); + return ret; + } + + parser_buf =3D kmalloc(sizeof(struct bpf_parser_buf), GFP_KERNEL); + if (!parser_buf) { + vfree(b); + context->buf =3D NULL; + return -ENOMEM; + } + parser_buf->buf =3D b; + parser_buf->size =3D buf_sz; + context->buf =3D parser_buf; + /* @func should be a sync call */ + ret =3D context->func(context); + context->buf =3D NULL; + vfree(b); + kfree(parser_buf); + + return ret; +} +__bpf_kfunc_end_defs(); + +BTF_KFUNCS_START(buffer_parser_ids) +BTF_ID_FLAGS(func, bpf_get_parser_context, KF_ACQUIRE | KF_RET_NULL) +BTF_ID_FLAGS(func, bpf_put_parser_context, KF_RELEASE) +BTF_ID_FLAGS(func, bpf_buffer_parser, KF_SLEEPABLE) +BTF_KFUNCS_END(buffer_parser_ids) + +static const struct btf_kfunc_id_set buffer_parser_kfunc_set =3D { + .owner =3D THIS_MODULE, + .set =3D &buffer_parser_ids, +}; + + +BTF_ID_LIST(buffer_parser_dtor_ids) +BTF_ID(struct, bpf_parser_context) +BTF_ID(func, bpf_parser_context_release_dtor) + +static int __init buffer_parser_kfunc_init(void) +{ + int ret; + const struct btf_id_dtor_kfunc buffer_parser_dtors[] =3D { + { + .btf_id =3D buffer_parser_dtor_ids[0], + .kfunc_btf_id =3D buffer_parser_dtor_ids[1] + }, + }; + + ret =3D register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &buffer_parser_k= func_set); + return ret ?: register_btf_id_dtor_kfuncs(buffer_parser_dtors, + ARRAY_SIZE(buffer_parser_dtors), + THIS_MODULE); +} + +late_initcall(buffer_parser_kfunc_init); --=20 2.49.0 From nobody Sat Apr 4 00:07:09 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 37FA337C926 for ; Sun, 22 Mar 2026 01:45:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774143914; cv=none; b=NwWh2Yc944IAdLpSX1EoUzobfUbj1RXlaIktjniCxx88NWLHaGJN6TUu+oyEY/tEbWMx3UCQMrkpEFIT5aWfLYv3snUPGgAPAVU9iXIdZpWwZSjj4AnQP1PTYm90enX18+p5OcXefXOnZam1d9YWfpVmEnDy55XTWvdF0fbHmJI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774143914; c=relaxed/simple; bh=7J/0j0UT8bCyBT32UJ4DUDkUOg91zFMIdJHaBnIURbg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Zd4JyYTjx6or7SKlQg9dHT7cM+WC2v1L+M0XXKDOGBW3IgqivXVMLzrigEjsbP9f+DbfkUMRNCfcPq27g1tPO561/cftsnkg5654Ay+M3kmdg7Ug7HY/v3O+lQ4/dqgKBFBHYvw5xCV27vPNH3C+KDtXRY8cKkJXVjNMm/jgAzg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=EbzSyNL0; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="EbzSyNL0" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774143910; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=QBEyWtSTw7eVHcGhT5GAHiaIPcwZtFDY6ONhgAp2N3k=; b=EbzSyNL0uPYQ0MKh38XokyvtyvWDAq3Prja+Q/EUIMkn5qFbIXUvSKrS94JpeMz8SmDYtu HrXXiqnkXQA415fGqceIhGA3Rr2IwZNKZaNwuc2AR7cEw1yXleVw4K72L5q6SdTO71yqo6 J2S13ERtM0asSLiIxaXS+13PZeHbFwM= Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-461-9hVuChQdM_m5a9ZsPwmIag-1; Sat, 21 Mar 2026 21:45:05 -0400 X-MC-Unique: 9hVuChQdM_m5a9ZsPwmIag-1 X-Mimecast-MFC-AGG-ID: 9hVuChQdM_m5a9ZsPwmIag_1774143902 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 4B7C9180034F; Sun, 22 Mar 2026 01:45:02 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.72.112.22]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id C6E5B300019F; Sun, 22 Mar 2026 01:44:48 +0000 (UTC) From: Pingfan Liu To: kexec@lists.infradead.org Cc: Pingfan Liu , "David S. Miller" , Alexei Starovoitov , Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Eduard Zingerman , Song Liu , Yonghong Song , Jeremy Linton , Catalin Marinas , Will Deacon , Ard Biesheuvel , Simon Horman , Gerd Hoffmann , Vitaly Kuznetsov , Philipp Rudo , Viktor Malik , Jan Hendrik Farr , Baoquan He , Dave Young , Andrew Morton , bpf@vger.kernel.org, systemd-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org Subject: [PATCHv7 02/13] kexec_file: Use bpf-prog to decompose image Date: Sun, 22 Mar 2026 09:43:51 +0800 Message-ID: <20260322014402.8815-3-piliu@redhat.com> In-Reply-To: <20260322014402.8815-1-piliu@redhat.com> References: <20260322014402.8815-1-piliu@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" As UEFI becomes popular, a few architectures support to boot a PE format kernel image directly. But the internal of PE format varies, which means each parser for each format. This patch (with the rest in this series) introduces a common skeleton to all parsers, and leave the format parsing in bpf-prog, so the kernel code can keep relative stable. History, the syscall SYSCALL_DEFINE5(kexec_file_load, int, kernel_fd, int, initrd_fd, unsigned long, cmdline_len, const char __user *, cmdline_pt= r, unsigned long, flags) complies with the kernel protocol: bootable kernel, initramfs, cmdline. But the occurrence of UKI images challenges the traditional model. The image itself contains the kernel, initrd, and cmdline. To be compatible with both the old and new models, kexec_file_load can be reorganized into two stages. In the first stage, "decompose_kexec_image()" breaks down the passed-in image into the components required by the kernel boot protocol. In the second stage, the traditional image loader "arch_kexec_kernel_image_load()" prepares the switch to the next kernel. During the decomposition stage, the decomposition process can be nested. In each sub-process, BPF bytecode is extracted from the '.bpf' section to parse the current PE file. If the data section in the PE file contains another PE file, the sub-process is repeated. This is designed to handle the zboot format embedded in UKI format on the arm64 platform. There are some placeholder functions in this patch. (They will take effect after the introduction of kexec BPF light skeleton and BPF helpers.) Signed-off-by: Pingfan Liu Cc: Baoquan He Cc: Dave Young Cc: Andrew Morton Cc: Philipp Rudo To: kexec@lists.infradead.org --- kernel/Kconfig.kexec | 8 + kernel/Makefile | 1 + kernel/kexec_bpf_loader.c | 472 ++++++++++++++++++++++++++++++++++++++ kernel/kexec_file.c | 43 +++- kernel/kexec_internal.h | 4 + 5 files changed, 517 insertions(+), 11 deletions(-) create mode 100644 kernel/kexec_bpf_loader.c diff --git a/kernel/Kconfig.kexec b/kernel/Kconfig.kexec index 15632358bcf71..0c5d619820bcd 100644 --- a/kernel/Kconfig.kexec +++ b/kernel/Kconfig.kexec @@ -46,6 +46,14 @@ config KEXEC_FILE for kernel and initramfs as opposed to list of segments as accepted by kexec system call. =20 +config KEXEC_BPF + bool "Enable bpf-prog to parse the kexec image" + depends on KEXEC_FILE + depends on DEBUG_INFO_BTF && BPF_SYSCALL + help + This is a feature to run bpf section inside a kexec image file, which + parses the image properly and help kernel set up kexec boot protocol + config KEXEC_SIG bool "Verify kernel signature during kexec_file_load() syscall" depends on ARCH_SUPPORTS_KEXEC_SIG diff --git a/kernel/Makefile b/kernel/Makefile index 6785982013dce..9e17ad2a44b6f 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -85,6 +85,7 @@ obj-$(CONFIG_CRASH_DUMP_KUNIT_TEST) +=3D crash_core_test.o obj-$(CONFIG_KEXEC) +=3D kexec.o obj-$(CONFIG_KEXEC_FILE) +=3D kexec_file.o obj-$(CONFIG_KEXEC_ELF) +=3D kexec_elf.o +obj-$(CONFIG_KEXEC_BPF) +=3D kexec_bpf_loader.o obj-$(CONFIG_BACKTRACE_SELF_TEST) +=3D backtracetest.o obj-$(CONFIG_COMPAT) +=3D compat.o obj-$(CONFIG_CGROUPS) +=3D cgroup/ diff --git a/kernel/kexec_bpf_loader.c b/kernel/kexec_bpf_loader.c new file mode 100644 index 0000000000000..bd1800a767824 --- /dev/null +++ b/kernel/kexec_bpf_loader.c @@ -0,0 +1,472 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Kexec image bpf section helpers + * + * Copyright (C) 2025, 2026 Red Hat, Inc + */ + +#define pr_fmt(fmt) "kexec_file(Image): " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kexec_internal.h" + +/* Load a ELF */ +static int arm_bpf_prog(char *bpf_elf, unsigned long sz) +{ + return -1; +} + +static void disarm_bpf_prog(void) +{ +} + +#define MAX_PARSING_BUF_NUM 16 + +struct kexec_context { + bool kdump; + bool parsed; + char *parsing_buf[MAX_PARSING_BUF_NUM]; + unsigned long parsing_buf_sz[MAX_PARSING_BUF_NUM]; + + char *kernel; + unsigned long kernel_sz; + char *initrd; + unsigned long initrd_sz; + char *cmdline; + unsigned long cmdline_sz; +}; + +void kexec_image_parser_anchor(struct kexec_context *context, + unsigned long parser_id); + +void noinline __used kexec_image_parser_anchor(struct kexec_context *conte= xt, + unsigned long parser_id) +{ + barrier(); +} + +BTF_KFUNCS_START(kexec_modify_return_ids) +BTF_ID_FLAGS(func, kexec_image_parser_anchor, KF_SLEEPABLE) +BTF_KFUNCS_END(kexec_modify_return_ids) + +static const struct btf_kfunc_id_set kexec_modify_return_set =3D { + .owner =3D THIS_MODULE, + .set =3D &kexec_modify_return_ids, +}; + +static int __init kexec_bpf_prog_run_init(void) +{ + return register_btf_fmodret_id_set(&kexec_modify_return_set); +} +late_initcall(kexec_bpf_prog_run_init); + +static int kexec_buff_parser(struct bpf_parser_context *parser) +{ + return 0; +} + +#define KEXEC_ELF_BPF_PREFIX ".bpf." +#define KEXEC_ELF_BPF_NESTED ".bpf.nested" +#define KEXEC_ELF_BPF_MAX_IDX 8 +#define KEXEC_ELF_BPF_MAX_DEPTH 4 + +static bool is_elf_image(const char *buf, size_t sz) +{ + if (sz < SELFMAG) + return false; + + return memcmp(buf, ELFMAG, SELFMAG) =3D=3D 0; +} + +/* + * elf_get_shstrtab - resolve the section-name string table of an ELF image + * @buf: ELF image buffer + * @sz: buffer length + * @ehdr_out: receives a pointer to the ELF header inside @buf + * @shdrs_out: receives a pointer to the section-header table inside @buf + * @shstrtab_out: receives a pointer to the section-name string table + * + * All output pointers are interior pointers into @buf; callers must not + * free them independently. + * + * Returns 0 on success, -EINVAL if any structural check fails. + */ +static int elf_get_shstrtab(const char *buf, size_t sz, + const Elf64_Ehdr **ehdr_out, + const Elf64_Shdr **shdrs_out, + const char **shstrtab_out) +{ + const Elf64_Ehdr *ehdr; + const Elf64_Shdr *shdrs; + const Elf64_Shdr *shstr_shdr; + + if (sz < sizeof(*ehdr)) + return -EINVAL; + + ehdr =3D (const Elf64_Ehdr *)buf; + + if (ehdr->e_shoff =3D=3D 0 || ehdr->e_shnum =3D=3D 0) + return -EINVAL; + + if (ehdr->e_shstrndx >=3D ehdr->e_shnum) + return -EINVAL; + + /* section-header table must fit inside the buffer */ + if (ehdr->e_shoff > sz || + ehdr->e_shnum > (sz - ehdr->e_shoff) / sizeof(Elf64_Shdr)) + return -EINVAL; + + shdrs =3D (const Elf64_Shdr *)(buf + ehdr->e_shoff); + shstr_shdr =3D &shdrs[ehdr->e_shstrndx]; + + /* string table itself must fit inside the buffer */ + if (shstr_shdr->sh_offset > sz || + shstr_shdr->sh_size > sz - shstr_shdr->sh_offset) + return -EINVAL; + + *ehdr_out =3D ehdr; + *shdrs_out =3D shdrs; + *shstrtab_out =3D buf + shstr_shdr->sh_offset; + + return 0; +} + +/* + * validate_elf_bpf_sections - enforce the section-naming contract + * @buf: ELF image buffer + * @sz: buffer length + * + * Every section other than the null entry (index 0) and ".shstrtab" must + * be named either ".bpf.N" (N in 1..KEXEC_ELF_BPF_MAX_IDX, no gaps, no + * duplicates) or ".bpf.nested" (at most once). Any other name, any + * duplicate, or a gap in the numeric sequence is an error. + * + * Returns 0 if the ELF passes all checks, -EINVAL otherwise. + */ +static int validate_elf_bpf_sections(const char *buf, size_t sz) +{ + const Elf64_Ehdr *ehdr; + const Elf64_Shdr *shdrs; + const Elf64_Shdr *shstr_shdr; + const char *shstrtab; + bool seen[KEXEC_ELF_BPF_MAX_IDX + 1] =3D {}; + bool seen_nested =3D false; + int max_idx =3D 0; + int ret; + int i; + + if (!is_elf_image(buf, sz)) + return -EINVAL; + + ret =3D elf_get_shstrtab(buf, sz, &ehdr, &shdrs, &shstrtab); + if (ret) + return ret; + + shstr_shdr =3D &shdrs[ehdr->e_shstrndx]; + + for (i =3D 0; i < ehdr->e_shnum; i++) { + const char *name; + const char *num_str; + int idx; + + if (shdrs[i].sh_name >=3D shstr_shdr->sh_size) + return -EINVAL; + + name =3D shstrtab + shdrs[i].sh_name; + + /* structural ELF sections: null entry and section-name table */ + if (name[0] =3D=3D '\0' || strcmp(name, ".shstrtab") =3D=3D 0) + continue; + + /* .bpf.nested must appear at most once */ + if (strcmp(name, KEXEC_ELF_BPF_NESTED) =3D=3D 0) { + if (seen_nested) { + pr_err("kexec: duplicate .bpf.nested section\n"); + return -EINVAL; + } + seen_nested =3D true; + continue; + } + + /* every remaining section must start with the ".bpf." prefix */ + if (strncmp(name, KEXEC_ELF_BPF_PREFIX, + sizeof(KEXEC_ELF_BPF_PREFIX) - 1) !=3D 0) { + pr_err("kexec: invalid ELF section name: %s\n", name); + return -EINVAL; + } + + /* + * Suffix must be exactly one digit in [1, KEXEC_ELF_BPF_MAX_IDX]. + * Multi-digit numbers and leading zeros are rejected. + */ + num_str =3D name + sizeof(KEXEC_ELF_BPF_PREFIX) - 1; + if (num_str[0] < '1' || + num_str[0] > '0' + KEXEC_ELF_BPF_MAX_IDX || + num_str[1] !=3D '\0') { + pr_err("kexec: invalid BPF section index in: %s\n", name); + return -EINVAL; + } + + idx =3D num_str[0] - '0'; + if (seen[idx]) { + pr_err("kexec: duplicate BPF section: %s\n", name); + return -EINVAL; + } + seen[idx] =3D true; + if (idx > max_idx) + max_idx =3D idx; + } + + /* indices must be consecutive starting from 1 */ + for (i =3D 1; i <=3D max_idx; i++) { + if (!seen[i]) { + pr_err("kexec: missing .bpf.%d section\n", i); + return -EINVAL; + } + } + + return 0; +} + +/* + * elf_find_section - locate a named section in an ELF image + * @buf: ELF image buffer + * @sz: buffer length + * @name: section name to find + * @out_buf: receives a pointer to the section data (NULL if not found) + * @out_sz: receives the section size in bytes (0 if not found) + * + * Returns 0 on success (including the "not found" case), -EINVAL on a + * structural error. + */ +static int elf_find_section(const char *buf, size_t sz, const char *name, + char **out_buf, size_t *out_sz) +{ + const Elf64_Ehdr *ehdr; + const Elf64_Shdr *shdrs; + const Elf64_Shdr *shstr_shdr; + const char *shstrtab; + int ret; + int i; + + ret =3D elf_get_shstrtab(buf, sz, &ehdr, &shdrs, &shstrtab); + if (ret) + return ret; + + shstr_shdr =3D &shdrs[ehdr->e_shstrndx]; + + for (i =3D 0; i < ehdr->e_shnum; i++) { + if (shdrs[i].sh_name >=3D shstr_shdr->sh_size) + return -EINVAL; + + if (strcmp(shstrtab + shdrs[i].sh_name, name) !=3D 0) + continue; + + /* section data must be within the buffer */ + if (shdrs[i].sh_offset > sz || + shdrs[i].sh_size > sz - shdrs[i].sh_offset) + return -EINVAL; + + *out_buf =3D (char *)(buf + shdrs[i].sh_offset); + *out_sz =3D shdrs[i].sh_size; + return 0; + } + + *out_buf =3D NULL; + *out_sz =3D 0; + return 0; +} + +/* + * process_bpf_parsers_container - recursively process an ELF container, w= hich holds a + * batch of bpf parsers + * + * @elf_buf: ELF image buffer at this level + * @elf_sz: buffer length + * @context: shared kexec parsing context + * @depth: current recursion depth (call with 1 for the top level) + * + * 1. a valid section names should be .bpf.1, .bpf.2, ... in order. + * They are different parser for the current layer. + * 2. Only a .bpf.nested section is allowed for the internal level. + * 3. At each level, stop trying at the first attempt where context->par= sed becomes + * true, then try to load .bpf.nested to parse the internal layer + * + * Returns 0 on success, -EINVAL on any error. + */ +static int process_bpf_parsers_container(const char *elf_buf, size_t elf_s= z, + struct kexec_context *context, int depth) +{ + struct bpf_parser_context *bpf; + char *section_buf, *nested_buf; + size_t section_sz; + size_t nested_sz; + /* .bpf.1 etc */ + char section_name[sizeof(KEXEC_ELF_BPF_PREFIX) + 1]; + bool found =3D false; + int ret; + int i; + + if (depth > KEXEC_ELF_BPF_MAX_DEPTH) { + pr_err("kexec: ELF BPF nesting depth exceeds %d\n", + KEXEC_ELF_BPF_MAX_DEPTH); + return -EINVAL; + } + + ret =3D validate_elf_bpf_sections(elf_buf, elf_sz); + if (ret) + return ret; + + for (i =3D 1; i <=3D KEXEC_ELF_BPF_MAX_IDX && !found; i++) { + snprintf(section_name, sizeof(section_name), ".bpf.%d", i); + + ret =3D elf_find_section(elf_buf, elf_sz, section_name, + §ion_buf, §ion_sz); + if (ret) + return ret; + + /* no section at this index means the sequence is exhausted */ + if (!section_buf) + break; + + bpf =3D alloc_bpf_parser_context(kexec_buff_parser, context); + if (!bpf) + return -ENOMEM; + + ret =3D arm_bpf_prog(section_buf, section_sz); + if (ret) { + /* arm failed: no disarm needed, try next index */ + put_bpf_parser_context(bpf); + pr_info("kexec: arm_bpf_prog failed for %s (depth %d), trying next\n", + section_name, depth); + continue; + } + + /* + * Give the BPF prog a clean slate so context->parsed reliably + * reflects whether *this* invocation succeeded. + */ + context->parsed =3D false; + /* This is the hook point for bpf-prog */ + kexec_image_parser_anchor(context, (unsigned long)bpf); + disarm_bpf_prog(); + + /* Free the old parsing context, and reload the new */ + for (int i =3D 0; i < MAX_PARSING_BUF_NUM; i++) { + if (!!context->parsing_buf[i]) + break; + vfree(context->parsing_buf[i]); + context->parsing_buf[i] =3D NULL; + context->parsing_buf_sz[i] =3D 0; + } + + put_bpf_parser_context(bpf); + /* If the bpf-prog success, it flags by KEXEC_BPF_CMD_DONE */ + if (context->parsed) + found =3D true; + } + + if (!found) { + pr_err("kexec: no BPF section succeeded at depth %d\n", depth); + return -EINVAL; + } + + /* + * A numbered section succeeded. If .bpf.nested is present, the + * current context->kernel may still be in a container format that + * the next level of BPF progs knows how to unpack. + */ + ret =3D elf_find_section(elf_buf, elf_sz, KEXEC_ELF_BPF_NESTED, + &nested_buf, &nested_sz); + if (ret) + return ret; + + if (!nested_buf) + return 0; + + context->parsed =3D false; + return process_bpf_parsers_container(nested_buf, nested_sz, context, + depth + 1); +} + +int decompose_kexec_image(struct kimage *image, int extended_fd) +{ + struct kexec_context ctx =3D { 0 }; + unsigned long parser_sz; + char *parser_start; + int ret =3D -EINVAL; + + if (extended_fd < 0) + return ret; + + if (image->type !=3D KEXEC_TYPE_CRASH) + ctx.kdump =3D false; + else + ctx.kdump =3D true; + + parser_start =3D image->kernel_buf; + parser_sz =3D image->kernel_buf_len; + + if (!validate_elf_bpf_sections(parser_start, parser_sz)) { + + ret =3D kernel_read_file_from_fd(extended_fd, + 0, + (void **)&ctx.parsing_buf[0], + KEXEC_FILE_SIZE_MAX, + NULL, + 0); + if (ret < 0) { + pr_err("Fail to read image container\n"); + return -EINVAL; + } + ctx.parsing_buf_sz[0] =3D ret; + ret =3D process_bpf_parsers_container(parser_start, parser_sz, &ctx, 0); + if (!ret) { + char *p; + + /* Envelop should hold valid kernel, initrd, cmdline sections */ + if (!ctx.kernel || !ctx.initrd || !ctx.cmdline) { + vfree(ctx.kernel); + vfree(ctx.initrd); + vfree(ctx.cmdline); + return -EINVAL; + } + /* + * kimage_file_post_load_cleanup() calls kfree() to free + * cmdline + */ + p =3D kmalloc(ctx.cmdline_sz, GFP_KERNEL); + if (!p) { + vfree(ctx.kernel); + vfree(ctx.initrd); + vfree(ctx.cmdline); + return -ENOMEM; + } + vfree(image->kernel_buf); + image->kernel_buf =3D ctx.kernel; + image->kernel_buf_len =3D ctx.kernel_sz; + image->initrd_buf =3D ctx.initrd; + image->initrd_buf_len =3D ctx.initrd_sz; + memcpy(p, ctx.cmdline, ctx.cmdline_sz); + image->cmdline_buf =3D p; + image->cmdline_buf_len =3D ctx.cmdline_sz; + vfree(ctx.cmdline); + } + return ret; + } + + return -EINVAL; +} diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c index 2bfbb2d144e69..aca265034b4ed 100644 --- a/kernel/kexec_file.c +++ b/kernel/kexec_file.c @@ -55,9 +55,6 @@ static bool check_ima_segment_index(struct kimage *image,= int i) =20 static int kexec_calculate_store_digests(struct kimage *image); =20 -/* Maximum size in bytes for kernel/initrd files. */ -#define KEXEC_FILE_SIZE_MAX min_t(s64, 4LL << 30, SSIZE_MAX) - /* * Currently this is the only default function that is exported as some * architectures need it to do additional handlings. @@ -221,6 +218,7 @@ kimage_file_prepare_segments(struct kimage *image, int = kernel_fd, int initrd_fd, { ssize_t ret; void *ldata; + bool envelop =3D false; =20 ret =3D kernel_read_file_from_fd(kernel_fd, 0, &image->kernel_buf, KEXEC_FILE_SIZE_MAX, NULL, @@ -231,20 +229,40 @@ kimage_file_prepare_segments(struct kimage *image, in= t kernel_fd, int initrd_fd, kexec_dprintk("kernel: %p kernel_size: %#lx\n", image->kernel_buf, image->kernel_buf_len); =20 - /* Call arch image probe handlers */ + if (IS_ENABLED(CONFIG_KEXEC_BPF)) { + /* Fill up image's kernel_buf, initrd_buf, cmdline_buf */ + ret =3D decompose_kexec_image(image, initrd_fd); + switch (ret) { + case 0: + envelop =3D true; + break; + /* Valid format, but fail to parse */ + case -EINVAL: + break; + default: + goto out; + } + } + + /* + * From this point, the kexec subsystem handle the kernel boot protocol. + * + * Call arch image probe handlers + */ ret =3D arch_kexec_kernel_image_probe(image, image->kernel_buf, image->kernel_buf_len); if (ret) goto out; =20 #ifdef CONFIG_KEXEC_SIG - ret =3D kimage_validate_signature(image); - - if (ret) - goto out; + if (!envelop) { + ret =3D kimage_validate_signature(image); + if (ret) + goto out; + } #endif /* It is possible that there no initramfs is being loaded */ - if (!(flags & KEXEC_FILE_NO_INITRAMFS)) { + if (!(flags & KEXEC_FILE_NO_INITRAMFS) && !envelop) { ret =3D kernel_read_file_from_fd(initrd_fd, 0, &image->initrd_buf, KEXEC_FILE_SIZE_MAX, NULL, READING_KEXEC_INITRAMFS); @@ -257,7 +275,8 @@ kimage_file_prepare_segments(struct kimage *image, int = kernel_fd, int initrd_fd, image->no_cma =3D !!(flags & KEXEC_FILE_NO_CMA); image->force_dtb =3D flags & KEXEC_FILE_FORCE_DTB; =20 - if (cmdline_len) { + /* For envelop case, the cmdline should be passed in as a section */ + if (cmdline_len && !envelop) { image->cmdline_buf =3D memdup_user(cmdline_ptr, cmdline_len); if (IS_ERR(image->cmdline_buf)) { ret =3D PTR_ERR(image->cmdline_buf); @@ -273,9 +292,11 @@ kimage_file_prepare_segments(struct kimage *image, int= kernel_fd, int initrd_fd, goto out; } =20 + } + + if (image->cmdline_buf) ima_kexec_cmdline(kernel_fd, image->cmdline_buf, image->cmdline_buf_len - 1); - } =20 /* IMA needs to pass the measurement list to the next kernel. */ ima_add_kexec_buffer(image); diff --git a/kernel/kexec_internal.h b/kernel/kexec_internal.h index 228bb88c018bc..731ff02110b3c 100644 --- a/kernel/kexec_internal.h +++ b/kernel/kexec_internal.h @@ -33,9 +33,13 @@ static inline void kexec_unlock(void) =20 #ifdef CONFIG_KEXEC_FILE #include + +/* Maximum size in bytes for kernel/initrd files. */ +#define KEXEC_FILE_SIZE_MAX min_t(s64, 4LL << 30, SSIZE_MAX) void kimage_file_post_load_cleanup(struct kimage *image); extern char kexec_purgatory[]; extern size_t kexec_purgatory_size; +extern int decompose_kexec_image(struct kimage *image, int extended_fd); #else /* CONFIG_KEXEC_FILE */ static inline void kimage_file_post_load_cleanup(struct kimage *image) { } #endif /* CONFIG_KEXEC_FILE */ --=20 2.49.0 From nobody Sat Apr 4 00:07:09 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DE1902877C3 for ; Sun, 22 Mar 2026 01:45:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774143925; cv=none; b=R79/W6ptCOW0Oz3MtVDiiUbspswQLCc7MfDs54yqXnz/ON+1JpmreIx5z3ETA4OoAFP+wj4NUxThp+ml17dPPUSL1BpSkMybmOLV8ubvYDtxMTB3XtbQcJVzjYqP5d05a6tp/az03EM/mTreU8GuqWzCgZ2AT9zaqTO1SF84Foo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774143925; c=relaxed/simple; bh=UuAqXKcT5q6g2TiflwpZ5mdUuQS0bA73dzrBlncB7JA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jM+/qdmil+N8+dMKdXUOItKTJ798JbLxZHnQj3/393ONnoYQPyZLm06yfLs//zrcGdWVMe9zTz/wNuYTYYQ4KQqHu6MCskUp1yySkSKQiIlwEyVuSOG7No/BuJjpGUZCQbbXjvEOjdyWyFkaJ72KlcBHTxKszqzHWxxzsQmt7OA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=Hawh6WI7; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Hawh6WI7" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774143922; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=PRFUJjfw8UMJDn1AP+nn88xxm2eq7v+YRmLLVNEOIyw=; b=Hawh6WI7hXb8S0TG+06Z45Td+HtpiygouzkBladTtgws5qQVQfHWpCkjppKYQkElZTfsuP +I1whJnLWRw4uTkkFYrOdUaLtmIfAsh36E5zh649TpS1UKstXDyVJhhQcoB5MKzwTXKnAa +QBkYStrRe+YEaH9ll24WIPNVb0zoMM= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-488-ffZhTjeJPqmBcyCxX4zRnQ-1; Sat, 21 Mar 2026 21:45:19 -0400 X-MC-Unique: ffZhTjeJPqmBcyCxX4zRnQ-1 X-Mimecast-MFC-AGG-ID: ffZhTjeJPqmBcyCxX4zRnQ_1774143917 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id AA91E19560B4; Sun, 22 Mar 2026 01:45:16 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.72.112.22]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 287AF300019F; Sun, 22 Mar 2026 01:45:02 +0000 (UTC) From: Pingfan Liu To: linux-kernel@vger.kernel.org Cc: Pingfan Liu , "David S. Miller" , Alexei Starovoitov , Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Eduard Zingerman , Song Liu , Yonghong Song , Jeremy Linton , Catalin Marinas , Will Deacon , Ard Biesheuvel , Simon Horman , Gerd Hoffmann , Vitaly Kuznetsov , Philipp Rudo , Viktor Malik , Jan Hendrik Farr , Baoquan He , Dave Young , Andrew Morton , kexec@lists.infradead.org, bpf@vger.kernel.org, systemd-devel@lists.freedesktop.org Subject: [PATCHv7 03/13] lib/decompress: Keep decompressor when CONFIG_KEEP_DECOMPRESSOR Date: Sun, 22 Mar 2026 09:43:52 +0800 Message-ID: <20260322014402.8815-4-piliu@redhat.com> In-Reply-To: <20260322014402.8815-1-piliu@redhat.com> References: <20260322014402.8815-1-piliu@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" 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 Cc: Andrew Morton To: linux-kernel@vger.kernel.org --- include/linux/decompress/mm.h | 8 ++++++++ lib/Kconfig | 6 ++++++ lib/decompress.c | 6 +++--- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/include/linux/decompress/mm.h b/include/linux/decompress/mm.h index ac862422df158..39df02bcbc661 100644 --- a/include/linux/decompress/mm.h +++ b/include/linux/decompress/mm.h @@ -81,6 +81,7 @@ MALLOC_VISIBLE void free(void *where) #include #include #include +#include =20 /* Use defines rather than static inline in order to avoid spurious * warnings when not needed (indeed large_malloc / large_free are not @@ -92,7 +93,14 @@ MALLOC_VISIBLE void free(void *where) #define large_malloc(a) vmalloc(a) #define large_free(a) vfree(a) =20 +#ifdef CONFIG_KEEP_DECOMPRESSOR +#define INIT +#define INITCONST +#else #define INIT __init +#define INITCONST __initconst +#endif + #define STATIC =20 #include diff --git a/lib/Kconfig b/lib/Kconfig index 0f2fb96106476..e91347da12906 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -165,6 +165,12 @@ config RANDOM32_SELFTEST # # compression support is select'ed if needed # +config KEEP_DECOMPRESSOR + bool "keeps the decompress routines after kernel initialization" + default n + help + This option keeps the decompress routines after kernel initialization + config 842_COMPRESS select CRC32 tristate diff --git a/lib/decompress.c b/lib/decompress.c index 7785471586c62..29d4c749f1fc4 100644 --- a/lib/decompress.c +++ b/lib/decompress.c @@ -6,7 +6,7 @@ */ =20 #include - +#include #include #include #include @@ -48,7 +48,7 @@ struct compress_format { decompress_fn decompressor; }; =20 -static const struct compress_format compressed_formats[] __initconst =3D { +static const struct compress_format compressed_formats[] INITCONST =3D { { .magic =3D {0x1f, 0x8b}, .name =3D "gzip", .decompressor =3D gunzip }, { .magic =3D {0x1f, 0x9e}, .name =3D "gzip", .decompressor =3D gunzip }, { .magic =3D {0x42, 0x5a}, .name =3D "bzip2", .decompressor =3D bunzip2 }, @@ -60,7 +60,7 @@ static const struct compress_format compressed_formats[] = __initconst =3D { { /* sentinel */ } }; =20 -decompress_fn __init decompress_method(const unsigned char *inbuf, long le= n, +decompress_fn INIT decompress_method(const unsigned char *inbuf, long len, const char **name) { const struct compress_format *cf; --=20 2.49.0 From nobody Sat Apr 4 00:07:09 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 37A9237C92E for ; Sun, 22 Mar 2026 01:45:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774143940; cv=none; b=sxati+Jw8hujOxLXXEl50mZ6UlUNVxRMO+A49ZyJbRpC34DngorfBxVYB9ikNUJjwHsNPQ8gK96zKDSP7xhsP5J/h3v8WFzx7gyC9NMxoxnFT8RvnhYmgo3QMcwBj6W4+ltJET4FBZz0WCKOWjqDFuqpMn7MSoX6PSRJD3REXss= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774143940; c=relaxed/simple; bh=4AygUGA7GidwgIaRkeRWkQs7UXdN6zzPVEfzOnx4rxo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=nV+/0MlZSpk0LlIzIXYbzH1lWpkqn+a1H5VQWbe7KOu7ZxZtR9Y+9LhrMpVDbyslwI22wJHd/1pTzriDD6pvrwZOZUTv1IKoDdyK0C48SdmFNcs30xGFG0XRkVW07QvXw59Q09z51CyGmx67LRr243hxWeEJ5EFliUr2j0+hmKc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=TX6Ws+j9; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="TX6Ws+j9" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774143937; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=KqZTaT+kdyZRnQ5tH5b3qqrQ/FmskQWQAl3eHGY05TE=; b=TX6Ws+j9H0kaHcHHU3THLcubmjHHKxnaQK0lUTF4OkYoGnrXiULYmVufjstGw23DoRu4R5 2T2lz19CEwpMQqJQS/ALTPmlnxZ3C7qJEvraaRhGSvWfaGYm9CqpxsDSmSPg1KId4Zdso5 4BWKECAKDa5a5tWWaZ62dE+HDlDeNFs= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-532-DIRbQntINgig3MQLEVGIhw-1; Sat, 21 Mar 2026 21:45:32 -0400 X-MC-Unique: DIRbQntINgig3MQLEVGIhw-1 X-Mimecast-MFC-AGG-ID: DIRbQntINgig3MQLEVGIhw_1774143930 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id E19E519560A2; Sun, 22 Mar 2026 01:45:29 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.72.112.22]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id D6248300019F; Sun, 22 Mar 2026 01:45:17 +0000 (UTC) From: Pingfan Liu To: kexec@lists.infradead.org Cc: Pingfan Liu , "David S. Miller" , Alexei Starovoitov , Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Eduard Zingerman , Song Liu , Yonghong Song , Jeremy Linton , Catalin Marinas , Will Deacon , Ard Biesheuvel , Simon Horman , Gerd Hoffmann , Vitaly Kuznetsov , Philipp Rudo , Viktor Malik , Jan Hendrik Farr , Baoquan He , Dave Young , Andrew Morton , bpf@vger.kernel.org, systemd-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org Subject: [PATCHv7 04/13] kexec_file: Implement decompress method for parser Date: Sun, 22 Mar 2026 09:43:53 +0800 Message-ID: <20260322014402.8815-5-piliu@redhat.com> In-Reply-To: <20260322014402.8815-1-piliu@redhat.com> References: <20260322014402.8815-1-piliu@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" On arm64, there is no boot-time decompression for the kernel image. Therefore, when a compressed kernel image is loaded, it must be decompressed. It is impractical to implement the complex decompression methods in BPF bytecode. However, decompression routines exist in the kernel. This patch bridges the compressed data with the kernel's decompression methods. Signed-off-by: Pingfan Liu Cc: Baoquan He Cc: Dave Young Cc: Andrew Morton Cc: Philipp Rudo To: kexec@lists.infradead.org --- kernel/Kconfig.kexec | 2 +- kernel/kexec_bpf_loader.c | 236 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 237 insertions(+), 1 deletion(-) diff --git a/kernel/Kconfig.kexec b/kernel/Kconfig.kexec index 0c5d619820bcd..dbfdf34a78aa0 100644 --- a/kernel/Kconfig.kexec +++ b/kernel/Kconfig.kexec @@ -49,7 +49,7 @@ config KEXEC_FILE config KEXEC_BPF bool "Enable bpf-prog to parse the kexec image" depends on KEXEC_FILE - depends on DEBUG_INFO_BTF && BPF_SYSCALL + depends on DEBUG_INFO_BTF && BPF_SYSCALL && KEEP_DECOMPRESSOR help This is a feature to run bpf section inside a kexec image file, which parses the image properly and help kernel set up kexec boot protocol diff --git a/kernel/kexec_bpf_loader.c b/kernel/kexec_bpf_loader.c index bd1800a767824..c79fb777d51bf 100644 --- a/kernel/kexec_bpf_loader.c +++ b/kernel/kexec_bpf_loader.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "kexec_internal.h" =20 /* Load a ELF */ @@ -73,8 +74,243 @@ static int __init kexec_bpf_prog_run_init(void) } late_initcall(kexec_bpf_prog_run_init); =20 +/* Mark the bpf parser success */ +#define KEXEC_BPF_CMD_INVALID 0x0 +#define KEXEC_BPF_CMD_DONE 0x1 +#define KEXEC_BPF_CMD_DECOMPRESS 0x2 + +#define KEXEC_BPF_SUBCMD_INVALID 0x0 +#define KEXEC_BPF_SUBCMD_KERNEL 0x1 +#define KEXEC_BPF_SUBCMD_INITRD 0x2 +#define KEXEC_BPF_SUBCMD_CMDLINE 0x3 + +#define KEXEC_BPF_PIPELINE_INVALID 0x0 +#define KEXEC_BPF_PIPELINE_FILL 0x1 + +struct cmd_hdr { + uint16_t cmd; + uint8_t subcmd; + uint8_t pipeline_flag; + /* sizeof(chunks) + sizeof(all data) */ + uint32_t payload_len; + /* 0 */ + uint16_t num_chunks; +} __packed; + +/* Reserved for extension */ +struct cmd_chunk { + uint16_t type; + uint32_t len; +} __packed; + + +/* Max decompressed size is capped at 512M */ +#define MAX_UNCOMPRESSED_BUF_SIZE (1 << 29) +#define CHUNK_SIZE (1 << 23) + +struct decompress_mem_allocator { + void *chunk_start; + unsigned int chunk_size; + void *chunk_cur; + unsigned int next_idx; + char **chunk_base_addr; +}; + +/* + * This global allocator for decompression is protected by kexec lock. + */ +static struct decompress_mem_allocator dcmpr_allocator; + +/* + * Set up an active chunk to hold partial decompressed data. + */ +static char *allocate_chunk_memory(void) +{ + struct decompress_mem_allocator *a =3D &dcmpr_allocator; + char *p; + + if (unlikely((a->next_idx * a->chunk_size >=3D MAX_UNCOMPRESSED_BUF_SIZE)= )) + return NULL; + + p =3D __vmalloc(a->chunk_size, GFP_KERNEL | __GFP_ACCOUNT); + if (!p) + return NULL; + a->chunk_base_addr[a->next_idx++] =3D p; + a->chunk_start =3D a->chunk_cur =3D p; + + return p; +} + +static int merge_decompressed_data(struct decompress_mem_allocator *a, + char **out, unsigned long *size) +{ + unsigned int last_chunk_sz =3D a->chunk_cur - a->chunk_start; + unsigned long total_sz; + char *dst, *cur_dst; + int i; + + total_sz =3D (a->next_idx - 1) * a->chunk_size + last_chunk_sz; + cur_dst =3D dst =3D __vmalloc(total_sz, GFP_KERNEL | __GFP_ACCOUNT); + if (!dst) + return -ENOMEM; + + for (i =3D 0; i < a->next_idx - 1; i++) { + memcpy(cur_dst, a->chunk_base_addr[i], a->chunk_size); + cur_dst +=3D a->chunk_size; + vfree(a->chunk_base_addr[i]); + a->chunk_base_addr[i] =3D NULL; + } + + memcpy(cur_dst, a->chunk_base_addr[i], last_chunk_sz); + vfree(a->chunk_base_addr[i]); + a->chunk_base_addr[i] =3D NULL; + *out =3D dst; + *size =3D total_sz; + + return 0; +} + +static int decompress_mem_allocator_init( + struct decompress_mem_allocator *a, + unsigned int chunk_size) +{ + unsigned long sz =3D (MAX_UNCOMPRESSED_BUF_SIZE / chunk_size) * sizeof(vo= id *); + char *buf; + + a->chunk_base_addr =3D __vmalloc(sz, GFP_KERNEL | __GFP_ACCOUNT); + if (!a->chunk_base_addr) + return -ENOMEM; + + /* Pre-allocate the memory for the first chunk */ + buf =3D __vmalloc(chunk_size, GFP_KERNEL | __GFP_ACCOUNT); + if (!buf) { + vfree(a->chunk_base_addr); + return -ENOMEM; + } + a->chunk_base_addr[0] =3D buf; + a->chunk_start =3D a->chunk_cur =3D buf; + a->chunk_size =3D chunk_size; + a->next_idx =3D 1; + return 0; +} + +static void decompress_mem_allocator_fini(struct decompress_mem_allocator = *a) +{ + int i; + + for (i =3D 0; i < a->next_idx; i++) { + if (a->chunk_base_addr[i] !=3D NULL) + vfree(a->chunk_base_addr[i]); + } + vfree(a->chunk_base_addr); +} + +/* + * This is a callback for decompress_fn. + * + * It copies the partial decompressed content in [buf, buf + len) to dst. = If the + * active chunk is not large enough, retire it and activate a new chunk to= hold + * the remaining data. + */ +static long flush(void *buf, unsigned long len) +{ + struct decompress_mem_allocator *a =3D &dcmpr_allocator; + long free, copied =3D 0; + + if (unlikely(len > a->chunk_size)) { + pr_info("Chunk size is too small to hold decompressed data\n"); + return -1; + } + free =3D a->chunk_start + a->chunk_size - a->chunk_cur; + BUG_ON(free < 0); + if (free < len) { + memcpy(a->chunk_cur, buf, free); + copied +=3D free; + a->chunk_cur +=3D free; + buf +=3D free; + len -=3D free; + a->chunk_start =3D a->chunk_cur =3D allocate_chunk_memory(); + if (unlikely(!a->chunk_start)) { + pr_info("Decompression runs out of memory\n"); + return -1; + } + } + memcpy(a->chunk_cur, buf, len); + copied +=3D len; + a->chunk_cur +=3D len; + return copied; +} + +static int parser_cmd_decompress(char *compressed_data, int image_gz_sz, + char **out_buf, unsigned long *out_sz, struct kexec_context *ctx) +{ + struct decompress_mem_allocator *a =3D &dcmpr_allocator; + decompress_fn decompressor; + const char *name; + int ret; + + ret =3D decompress_mem_allocator_init(a, CHUNK_SIZE); + if (ret < 0) + return ret; + decompressor =3D decompress_method(compressed_data, image_gz_sz, &name); + if (!decompressor) { + pr_err("Can not find decompress method\n"); + ret =3D -1; + goto err; + } + pr_debug("Find decompressing method: %s, compressed sz:0x%x\n", + name, image_gz_sz); + ret =3D decompressor(compressed_data, image_gz_sz, NULL, flush, + NULL, NULL, NULL); + if (!!ret) + goto err; + ret =3D merge_decompressed_data(a, out_buf, out_sz); + +err: + decompress_mem_allocator_fini(a); + + return ret; +} + static int kexec_buff_parser(struct bpf_parser_context *parser) { + struct bpf_parser_buf *pbuf =3D parser->buf; + struct kexec_context *ctx =3D (struct kexec_context *)parser->data; + struct cmd_hdr *cmd =3D (struct cmd_hdr *)pbuf->buf; + char *decompressed_buf, *buf, *p; + unsigned long decompressed_sz; + int ret =3D 0; + + buf =3D pbuf->buf + sizeof(struct cmd_hdr); + if (cmd->payload_len + sizeof(struct cmd_hdr) > pbuf->size) { + pr_info("Invalid payload size:0x%x, while buffer size:0x%x\n", + cmd->payload_len, pbuf->size); + return -EINVAL; + } + switch (cmd->cmd) { + case KEXEC_BPF_CMD_DONE: + ctx->parsed =3D true; + break; + case KEXEC_BPF_CMD_DECOMPRESS: + ret =3D parser_cmd_decompress(buf, cmd->payload_len, &decompressed_buf, + &decompressed_sz, ctx); + if (!ret) { + switch (cmd->subcmd) { + case KEXEC_BPF_SUBCMD_KERNEL: + vfree(ctx->kernel); + ctx->kernel =3D decompressed_buf; + ctx->kernel_sz =3D decompressed_sz; + break; + default: + vfree(decompressed_buf); + break; + } + } + break; + default: + break; + } + return 0; } =20 --=20 2.49.0 From nobody Sat Apr 4 00:07:09 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A3A55366079 for ; Sun, 22 Mar 2026 01:45:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774143949; cv=none; b=QXibZY6/3It/teZYQL6s5DEndp8Dw9P++jPxNUKPlU7FO7wzRe2p/AXp4R1FL6j9zEGfKnSYhiyiPpzkIfIx4DOmT5aEy8gdIea5+hTzsIvhQyj4wXVWRyNRm39EXRdtWL3xvLtgFA8Kvh+IVramFYF2kYeGrbRa5eC1KQFYato= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774143949; c=relaxed/simple; bh=hB7HnwUK3/ELtqDqgPJBn1gu4lTECYsJwrNfv2JnJkE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=EYGz4Dt0/XWJDgLNfnD8iPFnf8lktjpO1giBI9DDMVJ3UBtlKws7dnn8+KRSLCK0B/jhIfEG2zKlUIiQD2/b7GBVTiuG2iPRMuQ8cenQdEzVPtaULPZ8M3y/VZs0GtuhpPZeOxlTddnERjzUE6IG4+B4K30iXn8ZtBRbcVQZMHg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=Gq53H2/7; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Gq53H2/7" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774143947; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=nXTsiAuJ0Ml5eEoT2v3CB8gGptbXj7qArGf4X4fCTX0=; b=Gq53H2/79u7mRYte/br+QMpmLl8PKDXCfJH+MZ/S5WtYhGDZzr10o42Qmlv5xx3+A3VWt4 c4dxgdWuDp3mU5PcWBsTeg8K/aTRUNTYYeVmtbar8O/mkdIc5h4H74w3xEuv0wp6fl2C43 rvDHE8nI93eGA83Kjhi/qsEzfuSgclU= Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-645-NyNUN4QhNkmZBjcJYupS-w-1; Sat, 21 Mar 2026 21:45:44 -0400 X-MC-Unique: NyNUN4QhNkmZBjcJYupS-w-1 X-Mimecast-MFC-AGG-ID: NyNUN4QhNkmZBjcJYupS-w_1774143942 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 1281B18005B3; Sun, 22 Mar 2026 01:45:42 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.72.112.22]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 94A1C300019F; Sun, 22 Mar 2026 01:45:30 +0000 (UTC) From: Pingfan Liu To: kexec@lists.infradead.org Cc: Pingfan Liu , "David S. Miller" , Alexei Starovoitov , Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Eduard Zingerman , Song Liu , Yonghong Song , Jeremy Linton , Catalin Marinas , Will Deacon , Ard Biesheuvel , Simon Horman , Gerd Hoffmann , Vitaly Kuznetsov , Philipp Rudo , Viktor Malik , Jan Hendrik Farr , Baoquan He , Dave Young , Andrew Morton , bpf@vger.kernel.org, systemd-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org Subject: [PATCHv7 05/13] kexec_file: Implement copy method for parser Date: Sun, 22 Mar 2026 09:43:54 +0800 Message-ID: <20260322014402.8815-6-piliu@redhat.com> In-Reply-To: <20260322014402.8815-1-piliu@redhat.com> References: <20260322014402.8815-1-piliu@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" Copying the bpf code parsing result to the proper place: image->kernel_buf, initrd_buf, cmdline_buf. Signed-off-by: Pingfan Liu Cc: Baoquan He Cc: Dave Young Cc: Andrew Morton Cc: Philipp Rudo To: kexec@lists.infradead.org --- kernel/kexec_bpf_loader.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/kernel/kexec_bpf_loader.c b/kernel/kexec_bpf_loader.c index c79fb777d51bf..af16f7b685d9a 100644 --- a/kernel/kexec_bpf_loader.c +++ b/kernel/kexec_bpf_loader.c @@ -78,6 +78,7 @@ late_initcall(kexec_bpf_prog_run_init); #define KEXEC_BPF_CMD_INVALID 0x0 #define KEXEC_BPF_CMD_DONE 0x1 #define KEXEC_BPF_CMD_DECOMPRESS 0x2 +#define KEXEC_BPF_CMD_COPY 0x3 =20 #define KEXEC_BPF_SUBCMD_INVALID 0x0 #define KEXEC_BPF_SUBCMD_KERNEL 0x1 @@ -307,6 +308,34 @@ static int kexec_buff_parser(struct bpf_parser_context= *parser) } } break; + case KEXEC_BPF_CMD_COPY: + p =3D __vmalloc(cmd->payload_len, GFP_KERNEL | __GFP_ACCOUNT); + if (!p) + return -ENOMEM; + memcpy(p, buf, cmd->payload_len); + switch (cmd->subcmd) { + case KEXEC_BPF_SUBCMD_KERNEL: + vfree(ctx->kernel); + ctx->kernel =3D p; + ctx->kernel_sz =3D cmd->payload_len; + break; + /* Todo: allow the concatenation of multiple initrd */ + case KEXEC_BPF_SUBCMD_INITRD: + vfree(ctx->initrd); + ctx->initrd =3D p; + ctx->initrd_sz =3D cmd->payload_len; + break; + /* Todo: allow the concatenation of multiple cmdline */ + case KEXEC_BPF_SUBCMD_CMDLINE: + vfree(ctx->cmdline); + ctx->cmdline =3D p; + ctx->cmdline_sz =3D cmd->payload_len; + break; + default: + vfree(p); + break; + } + break; default: break; } --=20 2.49.0 From nobody Sat Apr 4 00:07:09 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EA2B519CD19 for ; Sun, 22 Mar 2026 01:46:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774143963; cv=none; b=UHFp7Kn1Vvz+NRKn1iwBmjB2ZLtWmABbYWBKcR59BPZ9IICvzLVzUikAOhQfuDFmqQ2ZNRgSRH/vFR/+dFFpMpDWZps9SkOnfQVZcyVHMxuwC9Y3ssL9JtPLcn0MjcObtYNjc554LRgq6D2+1EKaHxm0GsUxdpUFj8NWIO2D0TE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774143963; c=relaxed/simple; bh=pmAjCVodGJSLBePhsyOw8FKCOcP1pDL6atUspo9KOyw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=PoP4f9NLTHi1c5KhRlG04VXQaQJeu9wBXJOmgmGDuqVDPL96WTTx7p5KFq6rw0jqyjzrOPtKY+nsAPYiH8FoHfn0qxRnQ7gL05iRjXZPagE4tu4Tm7V0mFSEuL8VCz1/4ivNm9dxs6dRhHYjQOOJFl8iPNcGy5If32RJ8kOlJO8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=D5ErhEj5; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="D5ErhEj5" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774143960; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Q0yOeqibN7XflIQPgK3OVma/nUGOvOfi7m24NVY99u0=; b=D5ErhEj5n4q5KtkrYSE+K7R0noIyuzYKTHsrzzft3yjtn0kDHblXvkefznutDLYu0vAzKU LGaBG8A9vTnisBy7gFd3coWXJYQMeVpLcyzAe0lHv1G9kE7BuL5QLOh2Lf9NSTjFYtxljI TqHjolD0ES/wuxu56reU7/0pJh5BH6Q= Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-683-m1NarEj3M--uXAdcyh1NsA-1; Sat, 21 Mar 2026 21:45:57 -0400 X-MC-Unique: m1NarEj3M--uXAdcyh1NsA-1 X-Mimecast-MFC-AGG-ID: m1NarEj3M--uXAdcyh1NsA_1774143954 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 8A84F180047F; Sun, 22 Mar 2026 01:45:54 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.72.112.22]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 2A6B6300019F; Sun, 22 Mar 2026 01:45:42 +0000 (UTC) From: Pingfan Liu To: kexec@lists.infradead.org Cc: Pingfan Liu , "David S. Miller" , Alexei Starovoitov , Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Eduard Zingerman , Song Liu , Yonghong Song , Jeremy Linton , Catalin Marinas , Will Deacon , Ard Biesheuvel , Simon Horman , Gerd Hoffmann , Vitaly Kuznetsov , Philipp Rudo , Viktor Malik , Jan Hendrik Farr , Baoquan He , Dave Young , Andrew Morton , bpf@vger.kernel.org, systemd-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org Subject: [PATCHv7 06/13] kexec_file: Chain the stages into a pipeline Date: Sun, 22 Mar 2026 09:43:55 +0800 Message-ID: <20260322014402.8815-7-piliu@redhat.com> In-Reply-To: <20260322014402.8815-1-piliu@redhat.com> References: <20260322014402.8815-1-piliu@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" Images may consist of multiple layers, each with a distinct format. For example, an AArch64 UKI image typically embeds a zboot image in the .linux section. The parser therefore determines whether its output should be forwarded to the next stage. Intermediate results are stored in next_parsing_buf and then promoted to parsing_buf for the subsequent stage. Signed-off-by: Pingfan Liu Cc: Baoquan He Cc: Dave Young Cc: Andrew Morton Cc: Philipp Rudo To: kexec@lists.infradead.org --- kernel/kexec_bpf_loader.c | 47 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/kernel/kexec_bpf_loader.c b/kernel/kexec_bpf_loader.c index af16f7b685d9a..7f7884411e2c7 100644 --- a/kernel/kexec_bpf_loader.c +++ b/kernel/kexec_bpf_loader.c @@ -41,6 +41,8 @@ struct kexec_context { bool parsed; char *parsing_buf[MAX_PARSING_BUF_NUM]; unsigned long parsing_buf_sz[MAX_PARSING_BUF_NUM]; + char *next_parsing_buf[MAX_PARSING_BUF_NUM]; + unsigned long next_parsing_buf_sz[MAX_PARSING_BUF_NUM]; =20 char *kernel; unsigned long kernel_sz; @@ -278,8 +280,9 @@ static int kexec_buff_parser(struct bpf_parser_context = *parser) struct bpf_parser_buf *pbuf =3D parser->buf; struct kexec_context *ctx =3D (struct kexec_context *)parser->data; struct cmd_hdr *cmd =3D (struct cmd_hdr *)pbuf->buf; - char *decompressed_buf, *buf, *p; + char *decompressed_buf, *buf, *p, *pn; unsigned long decompressed_sz; + bool fill_pipeline =3D false; int ret =3D 0; =20 buf =3D pbuf->buf + sizeof(struct cmd_hdr); @@ -288,6 +291,7 @@ static int kexec_buff_parser(struct bpf_parser_context = *parser) cmd->payload_len, pbuf->size); return -EINVAL; } + fill_pipeline =3D cmd->pipeline_flag & KEXEC_BPF_PIPELINE_FILL; switch (cmd->cmd) { case KEXEC_BPF_CMD_DONE: ctx->parsed =3D true; @@ -301,6 +305,23 @@ static int kexec_buff_parser(struct bpf_parser_context= *parser) vfree(ctx->kernel); ctx->kernel =3D decompressed_buf; ctx->kernel_sz =3D decompressed_sz; + if (fill_pipeline) { + int i; + + for (i =3D 0; i < MAX_PARSING_BUF_NUM; i++) { + if (ctx->next_parsing_buf[i]) + continue; + ctx->next_parsing_buf[i] =3D decompressed_buf; + ctx->next_parsing_buf_sz[i] =3D decompressed_sz; + break; + } + /* No enough parsing slot */ + if (i =3D=3D MAX_PARSING_BUF_NUM) { + ctx->kernel =3D NULL; + vfree(decompressed_buf); + return -ENOMEM; + } + } break; default: vfree(decompressed_buf); @@ -313,6 +334,22 @@ static int kexec_buff_parser(struct bpf_parser_context= *parser) if (!p) return -ENOMEM; memcpy(p, buf, cmd->payload_len); + if (fill_pipeline) { + pn =3D __vmalloc(cmd->payload_len, GFP_KERNEL | __GFP_ACCOUNT); + if (!pn) { + vfree(p); + return -ENOMEM; + } + memcpy(pn, buf, cmd->payload_len); + for (int i =3D 0; i < MAX_PARSING_BUF_NUM; i++) { + if (!ctx->next_parsing_buf[i]) { + ctx->next_parsing_buf[i] =3D pn; + ctx->next_parsing_buf_sz[i] =3D cmd->payload_len; + break; + } + } + } + switch (cmd->subcmd) { case KEXEC_BPF_SUBCMD_KERNEL: vfree(ctx->kernel); @@ -637,6 +674,14 @@ static int process_bpf_parsers_container(const char *e= lf_buf, size_t elf_sz, context->parsing_buf[i] =3D NULL; context->parsing_buf_sz[i] =3D 0; } + for (int i =3D 0; i < MAX_PARSING_BUF_NUM; i++) { + if (!context->next_parsing_buf[i]) + break; + context->parsing_buf[i] =3D context->next_parsing_buf[i]; + context->parsing_buf_sz[i] =3D context->next_parsing_buf_sz[i]; + context->next_parsing_buf[i] =3D NULL; + context->next_parsing_buf_sz[i] =3D 0; + } =20 put_bpf_parser_context(bpf); /* If the bpf-prog success, it flags by KEXEC_BPF_CMD_DONE */ --=20 2.49.0 From nobody Sat Apr 4 00:07:09 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6EA5B3009F6 for ; Sun, 22 Mar 2026 01:46:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774143974; cv=none; b=bBWc30+Xltc16Rdulsmm/QqIVJQAr+xpiXMbEuKykkrGfvIR4I4pCFW/MhtcydIqiucPrkv9gQNRUNyAHssSG2+P3VzTL5o8IAZQd+WoxfdZ84IapngdQaCl5CvsWg45/FCnWfnpXXZcgsDJKfeuwt3pINo+PRddvqfVBQKjwNU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774143974; c=relaxed/simple; bh=N6BG3CbC61JhYzmYdvYLNW/nOPhbTe9kUyzqRUEdd3k=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=NRUml4qJT2ulv4ZjayEdWur7Uwgm3Y7PC9KbNomnmbG4ITe/+j0advu/iBwhlaUlOgz+IeNzpGQgVS/Ras6bwBbn394V316zBn2JY+TTJDeA0RJSBOkgLMmKjWgTJWCEH/DLm2MZC34oPp84LoRbpd29RYCo80kO13wiWkSn7L0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=TQxHDOV8; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="TQxHDOV8" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774143971; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=w5SzaA/gypihrr2L7AVhlXARC0GH+mqKFi+dlcz4cPU=; b=TQxHDOV8ZmMBnbUDDKvLVhuklCn4sX6W/td0wdTKiUqPpkOxl8tePMOjBQKlPdDyMssZWm AF9tA9Hzk2saByRVAMWTsaQgedsYlI5ltYgSNLdcW73U+DDY1+swfDWinL0p5Z1t8EKHoR 88wVvT1OQe6QK+jv94nOn1po9gjVbrw= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-161-teSkmKDxMgugP8EYVhXddQ-1; Sat, 21 Mar 2026 21:46:09 -0400 X-MC-Unique: teSkmKDxMgugP8EYVhXddQ-1 X-Mimecast-MFC-AGG-ID: teSkmKDxMgugP8EYVhXddQ_1774143967 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 58596195608D; Sun, 22 Mar 2026 01:46:07 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.72.112.22]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 8A3D030001BB; Sun, 22 Mar 2026 01:45:54 +0000 (UTC) From: Pingfan Liu To: kexec@lists.infradead.org Cc: Pingfan Liu , "David S. Miller" , Alexei Starovoitov , Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Eduard Zingerman , Song Liu , Yonghong Song , Jeremy Linton , Catalin Marinas , Will Deacon , Ard Biesheuvel , Simon Horman , Gerd Hoffmann , Vitaly Kuznetsov , Philipp Rudo , Viktor Malik , Jan Hendrik Farr , Baoquan He , Dave Young , Andrew Morton , bpf@vger.kernel.org, systemd-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org Subject: [PATCHv7 07/13] kexec_file: Introduce a bpf-prog lskel to run a format parser Date: Sun, 22 Mar 2026 09:43:56 +0800 Message-ID: <20260322014402.8815-8-piliu@redhat.com> In-Reply-To: <20260322014402.8815-1-piliu@redhat.com> References: <20260322014402.8815-1-piliu@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" Analague to kernel/bpf/preload/iterators/Makefile, this Makefile is not invoked by the Kbuild system. It needs to be invoked manually when kexec_pe_parser_bpf.c is changed so that kexec_pe_parser_bpf.lskel.h can be re-generated by the command "bpftool gen skeleton -L kexec_pe_parser_bpf.o". kexec_pe_parser_bpf.lskel.h is used directly by the kernel kexec code in later patch. For this patch, there are bpf bytecode contained in opts_data[] and opts_insn[] in kexec_pe_parser_bpf.lskel.h, but in the following patch, they will be removed and only the function API in kexec_pe_parser_bpf.lskel.h left. As exposed in kexec_pe_parser_bpf.lskel.h, the interface between bpf-prog and the kernel are constituted by: four maps: struct bpf_map_desc ringbuf_1; struct bpf_map_desc ringbuf_2; struct bpf_map_desc ringbuf_3; struct bpf_map_desc ringbuf_4; four sections: struct bpf_map_desc rodata; struct bpf_map_desc data; struct bpf_map_desc rodata_str1_1; struct bpf_map_desc bss; one prog: SEC("fentry.s/kexec_image_parser_anchor") The interface of maps and sections are extracted in template.c, which should be included by any bpf-prog to interact with kexec bpf component Signed-off-by: Pingfan Liu Cc: Alexei Starovoitov Cc: Baoquan He Cc: Dave Young Cc: Andrew Morton Cc: Philipp Rudo Cc: bpf@vger.kernel.org To: kexec@lists.infradead.org --- kernel/kexec_bpf/Makefile | 62 ++++ kernel/kexec_bpf/kexec_pe_parser_bpf.c | 12 + kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h | 371 +++++++++++++++++++ kernel/kexec_bpf/template.c | 72 ++++ 4 files changed, 517 insertions(+) create mode 100644 kernel/kexec_bpf/Makefile create mode 100644 kernel/kexec_bpf/kexec_pe_parser_bpf.c create mode 100644 kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h create mode 100644 kernel/kexec_bpf/template.c diff --git a/kernel/kexec_bpf/Makefile b/kernel/kexec_bpf/Makefile new file mode 100644 index 0000000000000..45d45cc0855a3 --- /dev/null +++ b/kernel/kexec_bpf/Makefile @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: GPL-2.0 +OUTPUT :=3D .output +CLANG ?=3D clang +LLC ?=3D llc +LLVM_STRIP ?=3D llvm-strip +DEFAULT_BPFTOOL :=3D $(OUTPUT)/sbin/bpftool +BPFTOOL ?=3D $(DEFAULT_BPFTOOL) +LIBBPF_SRC :=3D $(abspath ../../tools/lib/bpf) +BPFOBJ :=3D $(OUTPUT)/libbpf.a +BPF_INCLUDE :=3D $(OUTPUT) +INCLUDES :=3D -I$(OUTPUT) -I$(BPF_INCLUDE) -I$(abspath ../../tools/lib) = \ + -I$(abspath ../../tools/include/uapi) +CFLAGS :=3D -g -Wall + +srctree :=3D $(patsubst %/kernel/kexec_bpf,%,$(CURDIR)) +VMLINUX =3D $(srctree)/vmlinux + +abs_out :=3D $(abspath $(OUTPUT)) +ifeq ($(V),1) +Q =3D +msg =3D +else +Q =3D @ +msg =3D @printf ' %-8s %s%s\n' "$(1)" "$(notdir $(2))" "$(if $(3), $(3))"; +MAKEFLAGS +=3D --no-print-directory +submake_extras :=3D feature_display=3D0 +endif + +.DELETE_ON_ERROR: + +.PHONY: all clean + +all: $(OUTPUT) kexec_pe_parser_bpf.lskel.h + +clean: + $(call msg,CLEAN) + $(Q)rm -rf $(OUTPUT) kexec_pe_parser_bpf.lskel.h + +kexec_pe_parser_bpf.lskel.h: $(OUTPUT)/kexec_pe_parser_bpf.o | $(BPFTOOL) + $(call msg,GEN-SKEL,$@) + $(Q)$(BPFTOOL) gen skeleton -L $< > $@ + +$(OUTPUT)/vmlinux.h: $(VMLINUX) $(DEFAULT_BPFTOOL) $(BPFOBJ) | $(OUTPUT) + @$(BPFTOOL) btf dump file $(VMLINUX) format c > $(OUTPUT)/vmlinux.h + +$(OUTPUT)/kexec_pe_parser_bpf.o: kexec_pe_parser_bpf.c $(OUTPUT)/vmlinux.h= $(BPFOBJ) | $(OUTPUT) + $(call msg,BPF,$@) + $(Q)$(CLANG) -g -O2 -target bpf $(INCLUDES) \ + -c $(filter %.c,$^) -o $@ && \ + $(LLVM_STRIP) -g $@ + +$(OUTPUT): + $(call msg,MKDIR,$@) + $(Q)mkdir -p $(OUTPUT) + +$(BPFOBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUT= PUT) + $(Q)$(MAKE) $(submake_extras) -C $(LIBBPF_SRC) \ + OUTPUT=3D$(abspath $(dir $@))/ $(abspath $@) + +$(DEFAULT_BPFTOOL): + $(Q)$(MAKE) $(submake_extras) -C ../../tools/bpf/bpftool \ + prefix=3D OUTPUT=3D$(abs_out)/ DESTDIR=3D$(abs_out) install diff --git a/kernel/kexec_bpf/kexec_pe_parser_bpf.c b/kernel/kexec_bpf/kexe= c_pe_parser_bpf.c new file mode 100644 index 0000000000000..e4a2419240673 --- /dev/null +++ b/kernel/kexec_bpf/kexec_pe_parser_bpf.c @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2025, 2026 Red Hat, Inc + +#include "template.c" + +SEC("fentry.s/kexec_image_parser_anchor") +__attribute__((used)) int BPF_PROG(parse_pe, struct kexec_context *context, + unsigned long parser_id) +{ + return 0; +} diff --git a/kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h b/kernel/kexec_bp= f/kexec_pe_parser_bpf.lskel.h new file mode 100644 index 0000000000000..22fd77a872fc0 --- /dev/null +++ b/kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* THIS FILE IS AUTOGENERATED BY BPFTOOL! */ +#ifndef __KEXEC_PE_PARSER_BPF_SKEL_H__ +#define __KEXEC_PE_PARSER_BPF_SKEL_H__ + +#include + +struct kexec_pe_parser_bpf { + struct bpf_loader_ctx ctx; + struct { + struct bpf_map_desc ringbuf_1; + struct bpf_map_desc ringbuf_2; + struct bpf_map_desc ringbuf_3; + struct bpf_map_desc ringbuf_4; + struct bpf_map_desc rodata; + struct bpf_map_desc data; + struct bpf_map_desc rodata_str1_1; + struct bpf_map_desc bss; + } maps; + struct { + struct bpf_prog_desc parse_pe; + } progs; + struct { + int parse_pe_fd; + } links; +}; + +static inline int +kexec_pe_parser_bpf__parse_pe__attach(struct kexec_pe_parser_bpf *skel) +{ + int prog_fd =3D skel->progs.parse_pe.prog_fd; + int fd =3D skel_raw_tracepoint_open(NULL, prog_fd); + + if (fd > 0) + skel->links.parse_pe_fd =3D fd; + return fd; +} + +static inline int +kexec_pe_parser_bpf__attach(struct kexec_pe_parser_bpf *skel) +{ + int ret =3D 0; + + ret =3D ret < 0 ? ret : kexec_pe_parser_bpf__parse_pe__attach(skel); + return ret < 0 ? ret : 0; +} + +static inline void +kexec_pe_parser_bpf__detach(struct kexec_pe_parser_bpf *skel) +{ + skel_closenz(skel->links.parse_pe_fd); +} +static void +kexec_pe_parser_bpf__destroy(struct kexec_pe_parser_bpf *skel) +{ + if (!skel) + return; + kexec_pe_parser_bpf__detach(skel); + skel_closenz(skel->progs.parse_pe.prog_fd); + skel_closenz(skel->maps.ringbuf_1.map_fd); + skel_closenz(skel->maps.ringbuf_2.map_fd); + skel_closenz(skel->maps.ringbuf_3.map_fd); + skel_closenz(skel->maps.ringbuf_4.map_fd); + skel_closenz(skel->maps.rodata.map_fd); + skel_closenz(skel->maps.data.map_fd); + skel_closenz(skel->maps.rodata_str1_1.map_fd); + skel_closenz(skel->maps.bss.map_fd); + skel_free(skel); +} +static inline struct kexec_pe_parser_bpf * +kexec_pe_parser_bpf__open(void) +{ + struct kexec_pe_parser_bpf *skel; + + skel =3D skel_alloc(sizeof(*skel)); + if (!skel) + goto cleanup; + skel->ctx.sz =3D (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 =3D {}; + int err; + static const char opts_data[] __attribute__((__aligned__(8))) =3D "\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\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\x= 01\0\ +\x18\0\0\0\0\0\0\0\xf4\x02\0\0\xf4\x02\0\0\x78\x01\0\0\0\0\0\0\0\0\0\x02\x= 03\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\x= 02\0\ +\0\0\x04\0\0\0\x1b\0\0\0\x05\0\0\0\0\0\0\x01\x04\0\0\0\x20\0\0\0\0\0\0\0\0= \0\0\ +\x02\x06\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\x04\0\0\0\0\0= \0\0\ +\x02\0\0\x04\x10\0\0\0\x19\0\0\0\x01\0\0\0\0\0\0\0\x1e\0\0\0\x05\0\0\0\x40= \0\0\ +\0\x2a\0\0\0\0\0\0\x0e\x07\0\0\0\x01\0\0\0\0\0\0\0\x02\0\0\x04\x10\0\0\0\x= 19\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\x= 09\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\x= 1e\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\x= 02\0\ +\0\x04\x10\0\0\0\x19\0\0\0\x01\0\0\0\0\0\0\0\x1e\0\0\0\x05\0\0\0\x40\0\0\0= \x48\ +\0\0\0\0\0\0\x0e\x0d\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\x02\x10\0\0\0\x52\0\0\0= \0\0\ +\0\x01\x08\0\0\0\x40\0\0\0\0\0\0\0\x01\0\0\x0d\x02\0\0\0\x65\0\0\0\x0f\0\0= \0\ +\x69\0\0\0\x01\0\0\x0c\x11\0\0\0\x14\x01\0\0\0\0\0\x01\x01\0\0\0\x08\0\0\x= 01\0\ +\0\0\0\0\0\0\x03\0\0\0\0\x13\0\0\0\x04\0\0\0\x04\0\0\0\x19\x01\0\0\0\0\0\x= 0e\ +\x14\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\x0a\x13\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0= \x16\ +\0\0\0\x04\0\0\0\x10\0\0\0\x21\x01\0\0\0\0\0\x0e\x17\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\x03\0\0\0\0\x13\0\0\0\x04\0\0\0\x10\0\0\0\x2e\x01\0\0\0\0\0\x0e\x19\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x13\0\0\0\x04\0\0\0\x0f\0\0\0\0\0\0\0\0\0= \0\ +\x02\x13\0\0\0\x39\x01\0\0\0\0\0\x0e\x1c\0\0\0\0\0\0\0\x4d\x01\0\0\0\0\0\x= 0e\ +\x19\0\0\0\0\0\0\0\x57\x01\0\0\x01\0\0\x0f\x10\0\0\0\x1e\0\0\0\0\0\0\0\x10= \0\0\ +\0\x5c\x01\0\0\x02\0\0\x0f\x18\0\0\0\x1a\0\0\0\0\0\0\0\x10\0\0\0\x1d\0\0\0= \x10\ +\0\0\0\x08\0\0\0\x62\x01\0\0\x04\0\0\x0f\x40\0\0\0\x08\0\0\0\0\0\0\0\x10\0= \0\0\ +\x0a\0\0\0\x10\0\0\0\x10\0\0\0\x0c\0\0\0\x20\0\0\0\x10\0\0\0\x0e\0\0\0\x30= \0\0\ +\0\x10\0\0\0\x68\x01\0\0\x01\0\0\x0f\x10\0\0\0\x18\0\0\0\0\0\0\0\x10\0\0\0= \x70\ +\x01\0\0\x01\0\0\x0f\x04\0\0\0\x15\0\0\0\0\0\0\0\x04\0\0\0\0\x69\x6e\x74\0= \x5f\ +\x5f\x41\x52\x52\x41\x59\x5f\x53\x49\x5a\x45\x5f\x54\x59\x50\x45\x5f\x5f\0= \x74\ +\x79\x70\x65\0\x6d\x61\x78\x5f\x65\x6e\x74\x72\x69\x65\x73\0\x72\x69\x6e\x= 67\ +\x62\x75\x66\x5f\x31\0\x72\x69\x6e\x67\x62\x75\x66\x5f\x32\0\x72\x69\x6e\x= 67\ +\x62\x75\x66\x5f\x33\0\x72\x69\x6e\x67\x62\x75\x66\x5f\x34\0\x75\x6e\x73\x= 69\ +\x67\x6e\x65\x64\x20\x6c\x6f\x6e\x67\x20\x6c\x6f\x6e\x67\0\x63\x74\x78\0\x= 70\ +\x61\x72\x73\x65\x5f\x70\x65\0\x66\x65\x6e\x74\x72\x79\x2e\x73\x2f\x6b\x65= \x78\ +\x65\x63\x5f\x69\x6d\x61\x67\x65\x5f\x70\x61\x72\x73\x65\x72\x5f\x61\x6e\x= 63\ +\x68\x6f\x72\0\x2f\x68\x6f\x6d\x65\x2f\x6c\x69\x6e\x75\x78\x2f\x6b\x65\x72= \x6e\ +\x65\x6c\x2f\x6b\x65\x78\x65\x63\x5f\x62\x70\x66\x2f\x6b\x65\x78\x65\x63\x= 5f\ +\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\x= 69\ +\x6e\x74\x20\x42\x50\x46\x5f\x50\x52\x4f\x47\x28\x70\x61\x72\x73\x65\x5f\x= 70\ +\x65\x2c\x20\x73\x74\x72\x75\x63\x74\x20\x6b\x65\x78\x65\x63\x5f\x63\x6f\x= 6e\ +\x74\x65\x78\x74\x20\x2a\x63\x6f\x6e\x74\x65\x78\x74\x2c\0\x63\x68\x61\x72= \0\ +\x4c\x49\x43\x45\x4e\x53\x45\0\x64\x75\x6d\x6d\x79\x5f\x72\x6f\x64\x61\x74= \x61\ +\0\x64\x75\x6d\x6d\x79\x5f\x64\x61\x74\x61\0\x64\x75\x6d\x6d\x79\x5f\x6d\x= 65\ +\x72\x67\x65\x61\x62\x6c\x65\x5f\x73\x74\x72\0\x64\x75\x6d\x6d\x79\x5f\x62= \x73\ +\x73\0\x2e\x62\x73\x73\0\x2e\x64\x61\x74\x61\0\x2e\x6d\x61\x70\x73\0\x2e\x= 72\ +\x6f\x64\x61\x74\x61\0\x6c\x69\x63\x65\x6e\x73\x65\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\x84\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1b\0\0\0\0\0\0\0\0\0\0= \0\0\ +\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x72\x69\x6e\x67\x62\x75\x66\x5f\x31\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1b\0\0\0\0\0= \0\0\ +\0\0\0\0\0\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x72\x69\x6e\x67\x62\x75\x66\x5f= \x32\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1b= \0\0\ +\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x72\x69\x6e\x67\x62\x= 75\ +\x66\x5f\x33\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\x1b\0\0\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x72\x69\x= 6e\ +\x67\x62\x75\x66\x5f\x34\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\x02\0\0\0\x04\0\0\0\x10\0\0\0\x01\0\0\0\x80\0\0\0\0\0\0\0= \0\0\ +\0\0\x6b\x65\x78\x65\x63\x5f\x70\x65\x2e\x72\x6f\x64\x61\x74\x61\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\x22\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x72\x6f\x64\x61\x74\x61\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\0\x04\0\0\0\x18\0\0\0\x01\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\x6b\x65\x78\x65\x63\x5f\x70\x65\x2e\x64\x61\x74\x61\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\x20\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x64\x61\x74\x61\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\0\x04\0\0\0\x0f\0\0\0\x01\0\0\0= \x80\ +\0\0\0\0\0\0\0\0\0\0\0\x2e\x72\x6f\x64\x61\x74\x61\x2e\x73\x74\x72\x31\x2e= \x31\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x2e\x72\x6f\x= 64\ +\x61\x74\x61\x2e\x73\x74\x72\x31\x2e\x31\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\0\x= 04\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\x= 70\ +\x65\x2e\x62\x73\x73\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1f\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x47\x50\x4c\0\0\0\0\0\xb4\0\0\0= \0\0\ +\0\0\x95\0\0\0\0\0\0\0\0\0\0\0\x12\0\0\0\0\0\0\0\x95\0\0\0\xc8\0\0\0\x1b\x= 20\0\ +\0\x1a\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ +\0\0\0\0\0\0\0\0\x10\0\0\0\x70\x61\x72\x73\x65\x5f\x70\x65\0\0\0\0\0\0\0\0= \0\0\ +\0\0\x18\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x10\0\0\0\0\0\0= \0\0\ +\0\0\0\x01\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \x10\ +\0\0\0\0\0\0\0\x6b\x65\x78\x65\x63\x5f\x69\x6d\x61\x67\x65\x5f\x70\x61\x72= \x73\ +\x65\x72\x5f\x61\x6e\x63\x68\x6f\x72\0\0\0\0\0\0\0"; + static const char opts_insn[] __attribute__((__aligned__(8))) =3D "\ +\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\x= 02\0\ +\0\x88\0\0\0\xb7\x03\0\0\0\0\0\0\x85\0\0\0\x71\0\0\0\x05\0\x3b\0\0\0\0\0\x= 61\ +\xa1\x78\xff\0\0\0\0\xd5\x01\x01\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa1\x7c= \xff\ +\0\0\0\0\xd5\x01\x01\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa1\x80\xff\0\0\0\0= \xd5\ +\x01\x01\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \x61\ +\x01\0\0\0\0\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0= \0\0\ +\x18\x60\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\x61\x01\0\0\0\0\0\0\xd5\x01\x02\0\0= \0\0\ +\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x08= \0\0\ +\0\x61\x01\0\0\0\0\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0= \xa8\ +\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x61\x01\0\0\0\0\0\0\xd5\x01\x= 02\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\x= 85\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\x= d5\ +\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x18\x60\0\0\0\0= \0\0\ +\0\0\0\0\x18\0\0\0\x61\x01\0\0\0\0\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0= \0\0\ +\0\x85\0\0\0\xa8\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x1c\0\0\0\x61\x01\0\0\0= \0\0\ +\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\xbf\x70\0= \0\0\ +\0\0\0\x95\0\0\0\0\0\0\0\x61\x60\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0= \xa0\ +\x09\0\0\x63\x01\0\0\0\0\0\0\x61\x60\x0c\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0= \0\0\ +\x9c\x09\0\0\x63\x01\0\0\0\0\0\0\x79\x60\x10\0\0\0\0\0\x18\x61\0\0\0\0\0\0= \0\0\ +\0\0\x90\x09\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\x05\0\0= \x18\ +\x61\0\0\0\0\0\0\0\0\0\0\x88\x09\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\0\0\x12\0= \0\0\ +\x18\x62\0\0\0\0\0\0\0\0\0\0\x88\x09\0\0\xb7\x03\0\0\x1c\0\0\0\x85\0\0\0\x= a6\0\ +\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xad\xff\0\0\0\0\x63\x7a\x78\xff\0\0\0\0\x= 61\ +\x60\x1c\0\0\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xb4\x09= \0\0\ +\x63\x01\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\xa8\x= 09\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\x= a0\ +\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\x= 60\ +\x2c\0\0\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xfc\x09\0\0= \x63\ +\x01\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\xf0\x09\0= \0\ +\xb7\x03\0\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x91= \xff\ +\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\x63\x71\0\0\0\0\0\0\x61\x60= \x3c\ +\0\0\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x44\x0a\0\0\x63= \x01\ +\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x38\x0a\0\0\x= b7\ +\x03\0\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x82\xff= \0\0\ +\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\x63\x71\0\0\0\0\0\0\x61\x60\x4c= \0\0\ +\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x8c\x0a\0\0\x63\x01= \0\0\ +\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x80\x0a\0\0\xb7\x= 03\0\ +\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x73\xff\0\0\0= \0\ +\x18\x61\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x63\x71\0\0\0\0\0\0\x61\xa0\x78\xff= \0\0\ +\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xf8\x0a\0\0\x63\x01\0\0\0\0\0\0\x61\x60\x= 5c\0\ +\0\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xd4\x0a\0\0\x63\x= 01\0\ +\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\xc8\x0a\0\0\xb7= \x03\ +\0\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x60\xff\0\0= \0\0\ +\x18\x61\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\x63\x71\0\0\0\0\0\0\x79\x63\x60\0\0= \0\0\ +\0\x15\x03\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x10\x0b\0\0\xb7\x02\0= \0\ +\x10\0\0\0\x61\x60\x04\0\0\0\0\0\x45\0\x02\0\x01\0\0\0\x85\0\0\0\x94\0\0\0= \x05\ +\0\x01\0\0\0\0\0\x85\0\0\0\x71\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x10\0\0\0= \x61\ +\x20\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x28\x0b\0\0\x63\x01\0\0\0\0\0= \0\ +\x18\x60\0\0\0\0\0\0\0\0\0\0\x20\x0b\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x30\x= 0b\0\ +\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x10\x0b\0\0\x18\x61\0\0= \0\0\ +\0\0\0\0\0\0\x38\x0b\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\0\0\x02\0\0\0\x18\x62= \0\0\ +\0\0\0\0\0\0\0\0\x28\x0b\0\0\xb7\x03\0\0\x20\0\0\0\x85\0\0\0\xa6\0\0\0\xbf= \x07\ +\0\0\0\0\0\0\xc5\x07\x3c\xff\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x10\0\0\0= \x61\ +\x20\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x48\x0b\0\0\x63\x01\0\0\0\0\0= \0\ +\xb7\x01\0\0\x16\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x48\x0b\0\0\xb7\x03\0\0= \x04\ +\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x2f\xff\0\0\0\0\x61= \xa0\ +\x78\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x80\x0b\0\0\x63\x01\0\0\0\0\0= \0\ +\x61\x60\x6c\0\0\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x5c= \x0b\ +\0\0\x63\x01\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x= 50\ +\x0b\0\0\xb7\x03\0\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5= \x07\ +\x1f\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x14\0\0\0\x63\x71\0\0\0\0\0\0= \x79\ +\x63\x70\0\0\0\0\0\x15\x03\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x98\x= 0b\0\ +\0\xb7\x02\0\0\x18\0\0\0\x61\x60\x04\0\0\0\0\0\x45\0\x02\0\x01\0\0\0\x85\0= \0\0\ +\x94\0\0\0\x05\0\x01\0\0\0\0\0\x85\0\0\0\x71\0\0\0\x18\x62\0\0\0\0\0\0\0\0= \0\0\ +\x14\0\0\0\x61\x20\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xb8\x0b\0\0\x63= \x01\ +\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xb0\x0b\0\0\x18\x61\0\0\0\0\0\0\0= \0\0\ +\0\xc0\x0b\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x98\x0b\0\0= \x18\ +\x61\0\0\0\0\0\0\0\0\0\0\xc8\x0b\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\0\0\x02\0= \0\0\ +\x18\x62\0\0\0\0\0\0\0\0\0\0\xb8\x0b\0\0\xb7\x03\0\0\x20\0\0\0\x85\0\0\0\x= a6\0\ +\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xfb\xfe\0\0\0\0\x61\x60\x7c\0\0\0\0\0\x15= \0\ +\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xe4\x0b\0\0\x63\x01\0\0\0\0\0\0= \xb7\ +\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\xd8\x0b\0\0\xb7\x03\0\0\x48\0= \0\0\ +\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xef\xfe\0\0\0\0\x18\x61\0= \0\0\ +\0\0\0\0\0\0\0\x18\0\0\0\x63\x71\0\0\0\0\0\0\x79\x63\x80\0\0\0\0\0\x15\x03= \x08\ +\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x20\x0c\0\0\xb7\x02\0\0\x0f\0\0\0\x= 61\ +\x60\x04\0\0\0\0\0\x45\0\x02\0\x01\0\0\0\x85\0\0\0\x94\0\0\0\x05\0\x01\0\0= \0\0\ +\0\x85\0\0\0\x71\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x18\0\0\0\x61\x20\0\0\0= \0\0\ +\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x38\x0c\0\0\x63\x01\0\0\0\0\0\0\x18\x60\0\0= \0\0\ +\0\0\0\0\0\0\x30\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x40\x0c\0\0\x7b\x01\0= \0\0\ +\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x20\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0= \x48\ +\x0c\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\0\0\x02\0\0\0\x18\x62\0\0\0\0\0\0\0\0= \0\0\ +\x38\x0c\0\0\xb7\x03\0\0\x20\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0= \xc5\ +\x07\xcb\xfe\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x18\0\0\0\x61\x20\0\0\0\0= \0\0\ +\x18\x61\0\0\0\0\0\0\0\0\0\0\x58\x0c\0\0\x63\x01\0\0\0\0\0\0\xb7\x01\0\0\x= 16\0\ +\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x58\x0c\0\0\xb7\x03\0\0\x04\0\0\0\x85\0\0= \0\ +\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xbe\xfe\0\0\0\0\x61\xa0\x78\xff\0\0= \0\0\ +\x18\x61\0\0\0\0\0\0\0\0\0\0\x90\x0c\0\0\x63\x01\0\0\0\0\0\0\x61\x60\x8c\0= \0\0\ +\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x6c\x0c\0\0\x63\x01\0= \0\0\ +\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x60\x0c\0\0\xb7\x03= \0\0\ +\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xae\xfe\0\0\0\0= \x18\ +\x61\0\0\0\0\0\0\0\0\0\0\x1c\0\0\0\x63\x71\0\0\0\0\0\0\x79\x63\x90\0\0\0\0= \0\ +\x15\x03\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xa8\x0c\0\0\xb7\x02\0\0= \x10\ +\0\0\0\x61\x60\x04\0\0\0\0\0\x45\0\x02\0\x01\0\0\0\x85\0\0\0\x94\0\0\0\x05= \0\ +\x01\0\0\0\0\0\x85\0\0\0\x71\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x1c\0\0\0\x= 61\ +\x20\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xc0\x0c\0\0\x63\x01\0\0\0\0\0= \0\ +\x18\x60\0\0\0\0\0\0\0\0\0\0\xb8\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xc8\x= 0c\0\ +\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xa8\x0c\0\0\x18\x61\0\0= \0\0\ +\0\0\0\0\0\0\xd0\x0c\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\0\0\x02\0\0\0\x18\x62= \0\0\ +\0\0\0\0\0\0\0\0\xc0\x0c\0\0\xb7\x03\0\0\x20\0\0\0\x85\0\0\0\xa6\0\0\0\xbf= \x07\ +\0\0\0\0\0\0\xc5\x07\x8a\xfe\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xe0\x0c\0= \0\ +\x18\x61\0\0\0\0\0\0\0\0\0\0\x20\x0d\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0= \0\0\ +\0\0\0\0\0\xe8\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x18\x0d\0\0\x7b\x01\0\0= \0\0\ +\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xf8\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x= 60\ +\x0d\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\x0d\0\0\x18\x61= \0\0\ +\0\0\0\0\0\0\0\0\x70\x0d\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0= \0\ +\x10\x0d\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x90\x0d\0\0\x7b\x01\0\0\0\0\0\0\x= 18\ +\x60\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x88\x0d\0\0\x= 7b\ +\x01\0\0\0\0\0\0\x61\x60\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x28\x0d= \0\0\ +\x63\x01\0\0\0\0\0\0\x61\x60\x0c\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x2c= \x0d\ +\0\0\x63\x01\0\0\0\0\0\0\x79\x60\x10\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0= \x30\ +\x0d\0\0\x7b\x01\0\0\0\0\0\0\x61\xa0\x78\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0= \0\0\ +\0\x58\x0d\0\0\x63\x01\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xa0\x0d\0\0= \xb7\ +\x02\0\0\x1a\0\0\0\xb7\x03\0\0\x0c\0\0\0\xb7\x04\0\0\0\0\0\0\x85\0\0\0\xa7= \0\0\ +\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x54\xfe\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0= \x10\ +\x0d\0\0\x63\x70\x6c\0\0\0\0\0\x77\x07\0\0\x20\0\0\0\x63\x70\x70\0\0\0\0\0= \xb7\ +\x01\0\0\x05\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x10\x0d\0\0\xb7\x03\0\0\x8c= \0\0\ +\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x80= \x0d\ +\0\0\x61\x01\0\0\0\0\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0= \0\ +\xa8\0\0\0\xc5\x07\x42\xfe\0\0\0\0\x63\x7a\x80\xff\0\0\0\0\x61\xa1\x78\xff= \0\0\ +\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa0= \x80\ +\xff\0\0\0\0\x63\x06\x98\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x61= \x10\ +\0\0\0\0\0\0\x63\x06\x18\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\x= 61\ +\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\x= 10\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\x= 95\0\ +\0\0\0\0\0\0"; + opts.ctx =3D (struct bpf_loader_ctx *)skel; + opts.data_sz =3D sizeof(opts_data) - 1; + opts.data =3D (void *)opts_data; + opts.insns_sz =3D sizeof(opts_insn) - 1; + opts.insns =3D (void *)opts_insn; + + err =3D 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 =3D 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__((u= nused))) +{ +#ifdef __cplusplus +#define _Static_assert static_assert +#endif +#ifdef __cplusplus +#undef _Static_assert +#endif +} + +#endif /* __KEXEC_PE_PARSER_BPF_SKEL_H__ */ diff --git a/kernel/kexec_bpf/template.c b/kernel/kexec_bpf/template.c new file mode 100644 index 0000000000000..7f1557cb38223 --- /dev/null +++ b/kernel/kexec_bpf/template.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2026 Red Hat, Inc +// +// Original file: kernel/kexec_bpf/template.c +// +#include "vmlinux.h" +#include +#include +#include +#include + +/* 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 pro= vided. + */ +#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") =3D "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)) =3D "rodata"; +static char dummy_data[16] __attribute__((used)) =3D "data"; +static char *dummy_mergeable_str __attribute__((used)) =3D ".rodata.str1.= 1"; +static char dummy_bss[16] __attribute__((used)); + --=20 2.49.0 From nobody Sat Apr 4 00:07:09 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2397737C931 for ; Sun, 22 Mar 2026 01:46:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774143987; cv=none; b=T6DMdZ7sUJcvkj2uHoILZBwX9zrdFEaJBWzzcownU6SE/+hsNJDcD/fRK4Gc72keU/XpPSBA7+1axrzYC+aMSnS8Orv2znPAhTrMmx0ho99BV2LJhS0abH9RVW1sl8EGpFyauaDhMHV6bLqcdwcZtZrN/x7HbOWdcDzYY4itz3s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774143987; c=relaxed/simple; bh=jPwmEm3SqqyenRy92iZpQRjfJuisk08VLbzKctwYVi0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=UA4E80tWoORhAM3HYYhnFJT4kr+830bvuJWkRDy+AH/85UWjSx4T1gSshCbXWvEuy6/nHacvUi2xTWYUZqxUG7J4+j0ADMFWMmshEzG2ffzQq4hJlFN6jbO6VMNGSJ+credujZUbXhOG/jGmTYs+l/6BRAyneu3CyWOP+r6qWUw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=HlWHfY6a; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="HlWHfY6a" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774143984; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=15KtbOSZZwPvaDkTFOfNSBs8+xeaxRwo/zCVjX7fmi8=; b=HlWHfY6aeN5JwvUnWjhJu5ctHzQVhTsm69PdbConbEFKqznZ5DcTcpUr+aCoPRbkv+DX7/ 931xznMB2vw4Z6PXEIiKZWHArkVtjJ6Qz0iKSNvjLQroZBxbB6X4vHQhzGTs+mWF8AJDRE ENdWYbSwFdDBt85ZMbzkDOckA7lA0xw= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-439-idsQIo93NDiI8WZvZibClA-1; Sat, 21 Mar 2026 21:46:22 -0400 X-MC-Unique: idsQIo93NDiI8WZvZibClA-1 X-Mimecast-MFC-AGG-ID: idsQIo93NDiI8WZvZibClA_1774143980 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 5C6F819560A1; Sun, 22 Mar 2026 01:46:20 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.72.112.22]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id A5E00300019F; Sun, 22 Mar 2026 01:46:07 +0000 (UTC) From: Pingfan Liu To: kexec@lists.infradead.org Cc: Pingfan Liu , "David S. Miller" , Alexei Starovoitov , Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Eduard Zingerman , Song Liu , Yonghong Song , Jeremy Linton , Catalin Marinas , Will Deacon , Ard Biesheuvel , Simon Horman , Gerd Hoffmann , Vitaly Kuznetsov , Philipp Rudo , Viktor Malik , Jan Hendrik Farr , Baoquan He , Dave Young , Andrew Morton , bpf@vger.kernel.org, systemd-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org Subject: [PATCHv7 08/13] kexec_file: Factor out routine to find a symbol in ELF Date: Sun, 22 Mar 2026 09:43:57 +0800 Message-ID: <20260322014402.8815-9-piliu@redhat.com> In-Reply-To: <20260322014402.8815-1-piliu@redhat.com> References: <20260322014402.8815-1-piliu@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" The routine to search a symbol in ELF can be shared, so split it out. Signed-off-by: Pingfan Liu Cc: Baoquan He Cc: Dave Young Cc: Andrew Morton Cc: Philipp Rudo To: kexec@lists.infradead.org --- kernel/kexec_file.c | 61 +++++++++++++++++++++++++++++++++++++++++ kernel/kexec_internal.h | 1 + 2 files changed, 62 insertions(+) diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c index aca265034b4ed..15857a56e6278 100644 --- a/kernel/kexec_file.c +++ b/kernel/kexec_file.c @@ -902,6 +902,51 @@ static int kexec_calculate_store_digests(struct kimage= *image) return ret; } =20 +#if defined(CONFIG_ARCH_SUPPORTS_KEXEC_PURGATORY) || defined(CONFIG_KEXEC_= BPF) +const Elf_Sym *elf_find_symbol(const Elf_Ehdr *ehdr, const char *name) +{ + const Elf_Shdr *sechdrs; + const Elf_Sym *syms; + const char *strtab; + int i, k; + + sechdrs =3D (void *)ehdr + ehdr->e_shoff; + + for (i =3D 0; i < ehdr->e_shnum; i++) { + if (sechdrs[i].sh_type !=3D SHT_SYMTAB) + continue; + + if (sechdrs[i].sh_link >=3D ehdr->e_shnum) + /* Invalid strtab section number */ + continue; + strtab =3D (void *)ehdr + sechdrs[sechdrs[i].sh_link].sh_offset; + syms =3D (void *)ehdr + sechdrs[i].sh_offset; + + /* Go through symbols for a match */ + for (k =3D 0; k < sechdrs[i].sh_size/sizeof(Elf_Sym); k++) { + if (ELF_ST_BIND(syms[k].st_info) !=3D STB_GLOBAL) + continue; + + if (strcmp(strtab + syms[k].st_name, name) !=3D 0) + continue; + + if (syms[k].st_shndx =3D=3D SHN_UNDEF || + syms[k].st_shndx >=3D ehdr->e_shnum) { + pr_debug("Symbol: %s has bad section index %d.\n", + name, syms[k].st_shndx); + return NULL; + } + + /* Found the symbol we are looking for */ + return &syms[k]; + } + } + + return NULL; +} + +#endif + #ifdef CONFIG_ARCH_SUPPORTS_KEXEC_PURGATORY /* * kexec_purgatory_find_symbol - find a symbol in the purgatory @@ -1221,6 +1266,22 @@ int kexec_load_purgatory(struct kimage *image, struc= t kexec_buf *kbuf) return ret; } =20 +/* + * kexec_purgatory_find_symbol - find a symbol in the purgatory + * @pi: Purgatory to search in. + * @name: Name of the symbol. + * + * Return: pointer to symbol in read-only symtab on success, NULL on error. + */ +static const Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *p= i, + const char *name) +{ + if (!pi->ehdr) + return NULL; + + return elf_find_symbol(pi->ehdr, name); +} + void *kexec_purgatory_get_symbol_addr(struct kimage *image, const char *na= me) { struct purgatory_info *pi =3D &image->purgatory_info; diff --git a/kernel/kexec_internal.h b/kernel/kexec_internal.h index 731ff02110b3c..224f09188db23 100644 --- a/kernel/kexec_internal.h +++ b/kernel/kexec_internal.h @@ -40,6 +40,7 @@ void kimage_file_post_load_cleanup(struct kimage *image); extern char kexec_purgatory[]; extern size_t kexec_purgatory_size; extern int decompose_kexec_image(struct kimage *image, int extended_fd); +extern const Elf_Sym *elf_find_symbol(const Elf_Ehdr *ehdr, const char *na= me); #else /* CONFIG_KEXEC_FILE */ static inline void kimage_file_post_load_cleanup(struct kimage *image) { } #endif /* CONFIG_KEXEC_FILE */ --=20 2.49.0 From nobody Sat Apr 4 00:07:09 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1EA4C224D6 for ; Sun, 22 Mar 2026 01:46:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774144002; cv=none; b=jH3IU616pmQNsKQnLHrMOo1IIoLGOAQj4ZrmBAuBWTYgsHi+3RQvuzZuPtNGup69o0PcD5imtDJfsd5emY3mIjx3ByPmzBFOZV5w0u0GkvGPLVS/a1vx6hD7OdmRw6KySMsJajV6tyZimtrn3EADsyZJADWH/5qSaR7ZmAxQIi4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774144002; c=relaxed/simple; bh=5XtvBRZ5WcY4SFxsc684Dl7uyLUy40m3Ja+3E2/7gk8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Ac81b/YHDKog3jJyV6wfbUfO7NLbH54yOnrb7ILks7xsirkmePk2Kb5/REXVg/xh4jfCe8u3dt/yPg4mGQQ+W0G/7LsE/vJ5pu6DXbyiAtJms4Sr6gOC1cIiipCU9/Q1hicKt87sQk7eK8gbwN9u4TbBSY6UmDDT46Zicc4UUZ0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=LTSEPCvt; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="LTSEPCvt" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774143998; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=w4tVWoFxNEqGP2RabjVuO1zRJPVAO5j1EBDaSU+m4ww=; b=LTSEPCvtqF+x7kCJ2m3gsP4Lcw8KtasaqXLnNAZEyeTEnxxTwuJSes4AAh47DGOq38Fj/B VDuZrms7i+CgjxTfyEBT8vXEZh8YmdCxmpcnRRyRFAOzSInSqn3cKQbK1N5G7mH9zBLKM1 u40Qni1aL7UCP/d7pemfodQx/KIhQwc= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-96-pUr0syoTNAK6SNdXzFqqtA-1; Sat, 21 Mar 2026 21:46:34 -0400 X-MC-Unique: pUr0syoTNAK6SNdXzFqqtA-1 X-Mimecast-MFC-AGG-ID: pUr0syoTNAK6SNdXzFqqtA_1774143992 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 42639195608B; Sun, 22 Mar 2026 01:46:32 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.72.112.22]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 294EA300019F; Sun, 22 Mar 2026 01:46:20 +0000 (UTC) From: Pingfan Liu To: kexec@lists.infradead.org Cc: Pingfan Liu , "David S. Miller" , Alexei Starovoitov , Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Eduard Zingerman , Song Liu , Yonghong Song , Jeremy Linton , Catalin Marinas , Will Deacon , Ard Biesheuvel , Simon Horman , Gerd Hoffmann , Vitaly Kuznetsov , Philipp Rudo , Viktor Malik , Jan Hendrik Farr , Baoquan He , Dave Young , Andrew Morton , bpf@vger.kernel.org, systemd-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org Subject: [PATCHv7 09/13] kexec_file: Integrate bpf light skeleton to load image with bpf-prog Date: Sun, 22 Mar 2026 09:43:58 +0800 Message-ID: <20260322014402.8815-10-piliu@redhat.com> In-Reply-To: <20260322014402.8815-1-piliu@redhat.com> References: <20260322014402.8815-1-piliu@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" All kexec PE bpf prog should align with the interface exposed by the light skeleton four maps: struct bpf_map_desc ringbuf_1; struct bpf_map_desc ringbuf_2; struct bpf_map_desc ringbuf_3; struct bpf_map_desc ringbuf_4; four sections: struct bpf_map_desc rodata; struct bpf_map_desc data; struct bpf_map_desc rodata_str1_1; struct bpf_map_desc bss; one prog: SEC("fentry.s/kexec_image_parser_anchor") With the above presumption, the integration consists of two parts: -1. Call API exposed by light skeleton from kexec -2. The opts_insn[] and opts_data[] are bpf-prog dependent and can be extracted and passed in from the user space. In the kexec_file_load design, a PE file has a .bpf section, which data content is a ELF, and the ELF contains opts_insn[] opts_data[]. As a bonus, BPF bytecode can be placed under the protection of the entire PE signature. (Note, since opts_insn[] contains the information of the ringbuf size, the bpf-prog writer can change its proper size according to the kernel image size without modifying the kernel code) Signed-off-by: Pingfan Liu Cc: Alexei Starovoitov Cc: Baoquan He Cc: Dave Young Cc: Andrew Morton Cc: Philipp Rudo Cc: bpf@vger.kernel.org To: kexec@lists.infradead.org --- kernel/Makefile | 1 + kernel/kexec_bpf/Makefile | 8 + kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h | 249 +------------------ kernel/kexec_bpf_loader.c | 93 ++++++- 4 files changed, 105 insertions(+), 246 deletions(-) diff --git a/kernel/Makefile b/kernel/Makefile index 9e17ad2a44b6f..caa9b3642ae8c 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -145,6 +145,7 @@ obj-$(CONFIG_SYSCTL_KUNIT_TEST) +=3D sysctl-test.o =20 CFLAGS_kstack_erase.o +=3D $(DISABLE_KSTACK_ERASE) CFLAGS_kstack_erase.o +=3D $(call cc-option,-mgeneral-regs-only) +CFLAGS_kexec_bpf_loader.o +=3D -I$(srctree)/tools/lib obj-$(CONFIG_KSTACK_ERASE) +=3D kstack_erase.o KASAN_SANITIZE_kstack_erase.o :=3D n KCSAN_SANITIZE_kstack_erase.o :=3D n diff --git a/kernel/kexec_bpf/Makefile b/kernel/kexec_bpf/Makefile index 45d45cc0855a3..88e92eb910f64 100644 --- a/kernel/kexec_bpf/Makefile +++ b/kernel/kexec_bpf/Makefile @@ -39,6 +39,14 @@ clean: kexec_pe_parser_bpf.lskel.h: $(OUTPUT)/kexec_pe_parser_bpf.o | $(BPFTOOL) $(call msg,GEN-SKEL,$@) $(Q)$(BPFTOOL) gen skeleton -L $< > $@ + @# The following sed commands make opts_data[] and opts_insn[] visible in= a file instead of only in a function. + @# And it removes the bytecode + $(Q) sed -i '/static const char opts_data\[\].*=3D/,/";$$/d' $@ + $(Q) sed -i '/static const char opts_insn\[\].*=3D/,/";$$/d' $@ + $(Q) sed -i \ + -e 's/opts\.data_sz =3D sizeof(opts_data) - 1;/opts.data_sz =3D opts_dat= a_sz;/' \ + -e 's/opts\.insns_sz =3D sizeof(opts_insn) - 1;/opts.insns_sz =3D opts_i= nsn_sz;/' $@ + $(Q) sed -i '7i static char *opts_data, *opts_insn;\nstatic unsigned int = opts_data_sz, opts_insn_sz;' $@ =20 $(OUTPUT)/vmlinux.h: $(VMLINUX) $(DEFAULT_BPFTOOL) $(BPFOBJ) | $(OUTPUT) @$(BPFTOOL) btf dump file $(VMLINUX) format c > $(OUTPUT)/vmlinux.h diff --git a/kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h b/kernel/kexec_bp= f/kexec_pe_parser_bpf.lskel.h index 22fd77a872fc0..00e54301c394a 100644 --- a/kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h +++ b/kernel/kexec_bpf/kexec_pe_parser_bpf.lskel.h @@ -4,6 +4,8 @@ #define __KEXEC_PE_PARSER_BPF_SKEL_H__ =20 #include +static char *opts_data, *opts_insn; +static unsigned int opts_data_sz, opts_insn_sz; =20 struct kexec_pe_parser_bpf { struct bpf_loader_ctx ctx; @@ -87,253 +89,10 @@ kexec_pe_parser_bpf__load(struct kexec_pe_parser_bpf *= skel) { struct bpf_load_and_run_opts opts =3D {}; int err; - static const char opts_data[] __attribute__((__aligned__(8))) =3D "\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\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\x= 01\0\ -\x18\0\0\0\0\0\0\0\xf4\x02\0\0\xf4\x02\0\0\x78\x01\0\0\0\0\0\0\0\0\0\x02\x= 03\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\x= 02\0\ -\0\0\x04\0\0\0\x1b\0\0\0\x05\0\0\0\0\0\0\x01\x04\0\0\0\x20\0\0\0\0\0\0\0\0= \0\0\ -\x02\x06\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\x04\0\0\0\0\0= \0\0\ -\x02\0\0\x04\x10\0\0\0\x19\0\0\0\x01\0\0\0\0\0\0\0\x1e\0\0\0\x05\0\0\0\x40= \0\0\ -\0\x2a\0\0\0\0\0\0\x0e\x07\0\0\0\x01\0\0\0\0\0\0\0\x02\0\0\x04\x10\0\0\0\x= 19\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\x= 09\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\x= 1e\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\x= 02\0\ -\0\x04\x10\0\0\0\x19\0\0\0\x01\0\0\0\0\0\0\0\x1e\0\0\0\x05\0\0\0\x40\0\0\0= \x48\ -\0\0\0\0\0\0\x0e\x0d\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\x02\x10\0\0\0\x52\0\0\0= \0\0\ -\0\x01\x08\0\0\0\x40\0\0\0\0\0\0\0\x01\0\0\x0d\x02\0\0\0\x65\0\0\0\x0f\0\0= \0\ -\x69\0\0\0\x01\0\0\x0c\x11\0\0\0\x14\x01\0\0\0\0\0\x01\x01\0\0\0\x08\0\0\x= 01\0\ -\0\0\0\0\0\0\x03\0\0\0\0\x13\0\0\0\x04\0\0\0\x04\0\0\0\x19\x01\0\0\0\0\0\x= 0e\ -\x14\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\x0a\x13\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0= \x16\ -\0\0\0\x04\0\0\0\x10\0\0\0\x21\x01\0\0\0\0\0\x0e\x17\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\x03\0\0\0\0\x13\0\0\0\x04\0\0\0\x10\0\0\0\x2e\x01\0\0\0\0\0\x0e\x19\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x13\0\0\0\x04\0\0\0\x0f\0\0\0\0\0\0\0\0\0= \0\ -\x02\x13\0\0\0\x39\x01\0\0\0\0\0\x0e\x1c\0\0\0\0\0\0\0\x4d\x01\0\0\0\0\0\x= 0e\ -\x19\0\0\0\0\0\0\0\x57\x01\0\0\x01\0\0\x0f\x10\0\0\0\x1e\0\0\0\0\0\0\0\x10= \0\0\ -\0\x5c\x01\0\0\x02\0\0\x0f\x18\0\0\0\x1a\0\0\0\0\0\0\0\x10\0\0\0\x1d\0\0\0= \x10\ -\0\0\0\x08\0\0\0\x62\x01\0\0\x04\0\0\x0f\x40\0\0\0\x08\0\0\0\0\0\0\0\x10\0= \0\0\ -\x0a\0\0\0\x10\0\0\0\x10\0\0\0\x0c\0\0\0\x20\0\0\0\x10\0\0\0\x0e\0\0\0\x30= \0\0\ -\0\x10\0\0\0\x68\x01\0\0\x01\0\0\x0f\x10\0\0\0\x18\0\0\0\0\0\0\0\x10\0\0\0= \x70\ -\x01\0\0\x01\0\0\x0f\x04\0\0\0\x15\0\0\0\0\0\0\0\x04\0\0\0\0\x69\x6e\x74\0= \x5f\ -\x5f\x41\x52\x52\x41\x59\x5f\x53\x49\x5a\x45\x5f\x54\x59\x50\x45\x5f\x5f\0= \x74\ -\x79\x70\x65\0\x6d\x61\x78\x5f\x65\x6e\x74\x72\x69\x65\x73\0\x72\x69\x6e\x= 67\ -\x62\x75\x66\x5f\x31\0\x72\x69\x6e\x67\x62\x75\x66\x5f\x32\0\x72\x69\x6e\x= 67\ -\x62\x75\x66\x5f\x33\0\x72\x69\x6e\x67\x62\x75\x66\x5f\x34\0\x75\x6e\x73\x= 69\ -\x67\x6e\x65\x64\x20\x6c\x6f\x6e\x67\x20\x6c\x6f\x6e\x67\0\x63\x74\x78\0\x= 70\ -\x61\x72\x73\x65\x5f\x70\x65\0\x66\x65\x6e\x74\x72\x79\x2e\x73\x2f\x6b\x65= \x78\ -\x65\x63\x5f\x69\x6d\x61\x67\x65\x5f\x70\x61\x72\x73\x65\x72\x5f\x61\x6e\x= 63\ -\x68\x6f\x72\0\x2f\x68\x6f\x6d\x65\x2f\x6c\x69\x6e\x75\x78\x2f\x6b\x65\x72= \x6e\ -\x65\x6c\x2f\x6b\x65\x78\x65\x63\x5f\x62\x70\x66\x2f\x6b\x65\x78\x65\x63\x= 5f\ -\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\x= 69\ -\x6e\x74\x20\x42\x50\x46\x5f\x50\x52\x4f\x47\x28\x70\x61\x72\x73\x65\x5f\x= 70\ -\x65\x2c\x20\x73\x74\x72\x75\x63\x74\x20\x6b\x65\x78\x65\x63\x5f\x63\x6f\x= 6e\ -\x74\x65\x78\x74\x20\x2a\x63\x6f\x6e\x74\x65\x78\x74\x2c\0\x63\x68\x61\x72= \0\ -\x4c\x49\x43\x45\x4e\x53\x45\0\x64\x75\x6d\x6d\x79\x5f\x72\x6f\x64\x61\x74= \x61\ -\0\x64\x75\x6d\x6d\x79\x5f\x64\x61\x74\x61\0\x64\x75\x6d\x6d\x79\x5f\x6d\x= 65\ -\x72\x67\x65\x61\x62\x6c\x65\x5f\x73\x74\x72\0\x64\x75\x6d\x6d\x79\x5f\x62= \x73\ -\x73\0\x2e\x62\x73\x73\0\x2e\x64\x61\x74\x61\0\x2e\x6d\x61\x70\x73\0\x2e\x= 72\ -\x6f\x64\x61\x74\x61\0\x6c\x69\x63\x65\x6e\x73\x65\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\x84\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1b\0\0\0\0\0\0\0\0\0\0= \0\0\ -\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x72\x69\x6e\x67\x62\x75\x66\x5f\x31\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1b\0\0\0\0\0= \0\0\ -\0\0\0\0\0\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x72\x69\x6e\x67\x62\x75\x66\x5f= \x32\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1b= \0\0\ -\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x72\x69\x6e\x67\x62\x= 75\ -\x66\x5f\x33\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\x1b\0\0\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x72\x69\x= 6e\ -\x67\x62\x75\x66\x5f\x34\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\x02\0\0\0\x04\0\0\0\x10\0\0\0\x01\0\0\0\x80\0\0\0\0\0\0\0= \0\0\ -\0\0\x6b\x65\x78\x65\x63\x5f\x70\x65\x2e\x72\x6f\x64\x61\x74\x61\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\x22\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x72\x6f\x64\x61\x74\x61\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\0\x04\0\0\0\x18\0\0\0\x01\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\x6b\x65\x78\x65\x63\x5f\x70\x65\x2e\x64\x61\x74\x61\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\x20\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x64\x61\x74\x61\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\0\x04\0\0\0\x0f\0\0\0\x01\0\0\0= \x80\ -\0\0\0\0\0\0\0\0\0\0\0\x2e\x72\x6f\x64\x61\x74\x61\x2e\x73\x74\x72\x31\x2e= \x31\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x2e\x72\x6f\x= 64\ -\x61\x74\x61\x2e\x73\x74\x72\x31\x2e\x31\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\0\x= 04\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\x= 70\ -\x65\x2e\x62\x73\x73\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1f\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x47\x50\x4c\0\0\0\0\0\xb4\0\0\0= \0\0\ -\0\0\x95\0\0\0\0\0\0\0\0\0\0\0\x12\0\0\0\0\0\0\0\x95\0\0\0\xc8\0\0\0\x1b\x= 20\0\ -\0\x1a\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \0\0\ -\0\0\0\0\0\0\0\0\x10\0\0\0\x70\x61\x72\x73\x65\x5f\x70\x65\0\0\0\0\0\0\0\0= \0\0\ -\0\0\x18\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x10\0\0\0\0\0\0= \0\0\ -\0\0\0\x01\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \x10\ -\0\0\0\0\0\0\0\x6b\x65\x78\x65\x63\x5f\x69\x6d\x61\x67\x65\x5f\x70\x61\x72= \x73\ -\x65\x72\x5f\x61\x6e\x63\x68\x6f\x72\0\0\0\0\0\0\0"; - static const char opts_insn[] __attribute__((__aligned__(8))) =3D "\ -\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\x= 02\0\ -\0\x88\0\0\0\xb7\x03\0\0\0\0\0\0\x85\0\0\0\x71\0\0\0\x05\0\x3b\0\0\0\0\0\x= 61\ -\xa1\x78\xff\0\0\0\0\xd5\x01\x01\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa1\x7c= \xff\ -\0\0\0\0\xd5\x01\x01\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa1\x80\xff\0\0\0\0= \xd5\ -\x01\x01\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\0\0\0= \x61\ -\x01\0\0\0\0\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0= \0\0\ -\x18\x60\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\x61\x01\0\0\0\0\0\0\xd5\x01\x02\0\0= \0\0\ -\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x08= \0\0\ -\0\x61\x01\0\0\0\0\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0= \xa8\ -\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x61\x01\0\0\0\0\0\0\xd5\x01\x= 02\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\x= 85\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\x= d5\ -\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x18\x60\0\0\0\0= \0\0\ -\0\0\0\0\x18\0\0\0\x61\x01\0\0\0\0\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0= \0\0\ -\0\x85\0\0\0\xa8\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x1c\0\0\0\x61\x01\0\0\0= \0\0\ -\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\xbf\x70\0= \0\0\ -\0\0\0\x95\0\0\0\0\0\0\0\x61\x60\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0= \xa0\ -\x09\0\0\x63\x01\0\0\0\0\0\0\x61\x60\x0c\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0= \0\0\ -\x9c\x09\0\0\x63\x01\0\0\0\0\0\0\x79\x60\x10\0\0\0\0\0\x18\x61\0\0\0\0\0\0= \0\0\ -\0\0\x90\x09\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\x05\0\0= \x18\ -\x61\0\0\0\0\0\0\0\0\0\0\x88\x09\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\0\0\x12\0= \0\0\ -\x18\x62\0\0\0\0\0\0\0\0\0\0\x88\x09\0\0\xb7\x03\0\0\x1c\0\0\0\x85\0\0\0\x= a6\0\ -\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xad\xff\0\0\0\0\x63\x7a\x78\xff\0\0\0\0\x= 61\ -\x60\x1c\0\0\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xb4\x09= \0\0\ -\x63\x01\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\xa8\x= 09\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\x= a0\ -\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\x= 60\ -\x2c\0\0\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xfc\x09\0\0= \x63\ -\x01\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\xf0\x09\0= \0\ -\xb7\x03\0\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x91= \xff\ -\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\x63\x71\0\0\0\0\0\0\x61\x60= \x3c\ -\0\0\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x44\x0a\0\0\x63= \x01\ -\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x38\x0a\0\0\x= b7\ -\x03\0\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x82\xff= \0\0\ -\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\x63\x71\0\0\0\0\0\0\x61\x60\x4c= \0\0\ -\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x8c\x0a\0\0\x63\x01= \0\0\ -\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x80\x0a\0\0\xb7\x= 03\0\ -\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x73\xff\0\0\0= \0\ -\x18\x61\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x63\x71\0\0\0\0\0\0\x61\xa0\x78\xff= \0\0\ -\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xf8\x0a\0\0\x63\x01\0\0\0\0\0\0\x61\x60\x= 5c\0\ -\0\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xd4\x0a\0\0\x63\x= 01\0\ -\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\xc8\x0a\0\0\xb7= \x03\ -\0\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x60\xff\0\0= \0\0\ -\x18\x61\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\x63\x71\0\0\0\0\0\0\x79\x63\x60\0\0= \0\0\ -\0\x15\x03\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x10\x0b\0\0\xb7\x02\0= \0\ -\x10\0\0\0\x61\x60\x04\0\0\0\0\0\x45\0\x02\0\x01\0\0\0\x85\0\0\0\x94\0\0\0= \x05\ -\0\x01\0\0\0\0\0\x85\0\0\0\x71\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x10\0\0\0= \x61\ -\x20\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x28\x0b\0\0\x63\x01\0\0\0\0\0= \0\ -\x18\x60\0\0\0\0\0\0\0\0\0\0\x20\x0b\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x30\x= 0b\0\ -\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x10\x0b\0\0\x18\x61\0\0= \0\0\ -\0\0\0\0\0\0\x38\x0b\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\0\0\x02\0\0\0\x18\x62= \0\0\ -\0\0\0\0\0\0\0\0\x28\x0b\0\0\xb7\x03\0\0\x20\0\0\0\x85\0\0\0\xa6\0\0\0\xbf= \x07\ -\0\0\0\0\0\0\xc5\x07\x3c\xff\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x10\0\0\0= \x61\ -\x20\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x48\x0b\0\0\x63\x01\0\0\0\0\0= \0\ -\xb7\x01\0\0\x16\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x48\x0b\0\0\xb7\x03\0\0= \x04\ -\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x2f\xff\0\0\0\0\x61= \xa0\ -\x78\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x80\x0b\0\0\x63\x01\0\0\0\0\0= \0\ -\x61\x60\x6c\0\0\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x5c= \x0b\ -\0\0\x63\x01\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x= 50\ -\x0b\0\0\xb7\x03\0\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5= \x07\ -\x1f\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x14\0\0\0\x63\x71\0\0\0\0\0\0= \x79\ -\x63\x70\0\0\0\0\0\x15\x03\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x98\x= 0b\0\ -\0\xb7\x02\0\0\x18\0\0\0\x61\x60\x04\0\0\0\0\0\x45\0\x02\0\x01\0\0\0\x85\0= \0\0\ -\x94\0\0\0\x05\0\x01\0\0\0\0\0\x85\0\0\0\x71\0\0\0\x18\x62\0\0\0\0\0\0\0\0= \0\0\ -\x14\0\0\0\x61\x20\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xb8\x0b\0\0\x63= \x01\ -\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xb0\x0b\0\0\x18\x61\0\0\0\0\0\0\0= \0\0\ -\0\xc0\x0b\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x98\x0b\0\0= \x18\ -\x61\0\0\0\0\0\0\0\0\0\0\xc8\x0b\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\0\0\x02\0= \0\0\ -\x18\x62\0\0\0\0\0\0\0\0\0\0\xb8\x0b\0\0\xb7\x03\0\0\x20\0\0\0\x85\0\0\0\x= a6\0\ -\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xfb\xfe\0\0\0\0\x61\x60\x7c\0\0\0\0\0\x15= \0\ -\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xe4\x0b\0\0\x63\x01\0\0\0\0\0\0= \xb7\ -\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\xd8\x0b\0\0\xb7\x03\0\0\x48\0= \0\0\ -\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xef\xfe\0\0\0\0\x18\x61\0= \0\0\ -\0\0\0\0\0\0\0\x18\0\0\0\x63\x71\0\0\0\0\0\0\x79\x63\x80\0\0\0\0\0\x15\x03= \x08\ -\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x20\x0c\0\0\xb7\x02\0\0\x0f\0\0\0\x= 61\ -\x60\x04\0\0\0\0\0\x45\0\x02\0\x01\0\0\0\x85\0\0\0\x94\0\0\0\x05\0\x01\0\0= \0\0\ -\0\x85\0\0\0\x71\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x18\0\0\0\x61\x20\0\0\0= \0\0\ -\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x38\x0c\0\0\x63\x01\0\0\0\0\0\0\x18\x60\0\0= \0\0\ -\0\0\0\0\0\0\x30\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x40\x0c\0\0\x7b\x01\0= \0\0\ -\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x20\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0= \x48\ -\x0c\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\0\0\x02\0\0\0\x18\x62\0\0\0\0\0\0\0\0= \0\0\ -\x38\x0c\0\0\xb7\x03\0\0\x20\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0= \xc5\ -\x07\xcb\xfe\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x18\0\0\0\x61\x20\0\0\0\0= \0\0\ -\x18\x61\0\0\0\0\0\0\0\0\0\0\x58\x0c\0\0\x63\x01\0\0\0\0\0\0\xb7\x01\0\0\x= 16\0\ -\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x58\x0c\0\0\xb7\x03\0\0\x04\0\0\0\x85\0\0= \0\ -\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xbe\xfe\0\0\0\0\x61\xa0\x78\xff\0\0= \0\0\ -\x18\x61\0\0\0\0\0\0\0\0\0\0\x90\x0c\0\0\x63\x01\0\0\0\0\0\0\x61\x60\x8c\0= \0\0\ -\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x6c\x0c\0\0\x63\x01\0= \0\0\ -\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x60\x0c\0\0\xb7\x03= \0\0\ -\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xae\xfe\0\0\0\0= \x18\ -\x61\0\0\0\0\0\0\0\0\0\0\x1c\0\0\0\x63\x71\0\0\0\0\0\0\x79\x63\x90\0\0\0\0= \0\ -\x15\x03\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xa8\x0c\0\0\xb7\x02\0\0= \x10\ -\0\0\0\x61\x60\x04\0\0\0\0\0\x45\0\x02\0\x01\0\0\0\x85\0\0\0\x94\0\0\0\x05= \0\ -\x01\0\0\0\0\0\x85\0\0\0\x71\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x1c\0\0\0\x= 61\ -\x20\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xc0\x0c\0\0\x63\x01\0\0\0\0\0= \0\ -\x18\x60\0\0\0\0\0\0\0\0\0\0\xb8\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xc8\x= 0c\0\ -\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xa8\x0c\0\0\x18\x61\0\0= \0\0\ -\0\0\0\0\0\0\xd0\x0c\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\0\0\x02\0\0\0\x18\x62= \0\0\ -\0\0\0\0\0\0\0\0\xc0\x0c\0\0\xb7\x03\0\0\x20\0\0\0\x85\0\0\0\xa6\0\0\0\xbf= \x07\ -\0\0\0\0\0\0\xc5\x07\x8a\xfe\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xe0\x0c\0= \0\ -\x18\x61\0\0\0\0\0\0\0\0\0\0\x20\x0d\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0= \0\0\ -\0\0\0\0\0\xe8\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x18\x0d\0\0\x7b\x01\0\0= \0\0\ -\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xf8\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x= 60\ -\x0d\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\x0d\0\0\x18\x61= \0\0\ -\0\0\0\0\0\0\0\0\x70\x0d\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0= \0\ -\x10\x0d\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x90\x0d\0\0\x7b\x01\0\0\0\0\0\0\x= 18\ -\x60\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x88\x0d\0\0\x= 7b\ -\x01\0\0\0\0\0\0\x61\x60\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x28\x0d= \0\0\ -\x63\x01\0\0\0\0\0\0\x61\x60\x0c\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x2c= \x0d\ -\0\0\x63\x01\0\0\0\0\0\0\x79\x60\x10\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0= \x30\ -\x0d\0\0\x7b\x01\0\0\0\0\0\0\x61\xa0\x78\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0= \0\0\ -\0\x58\x0d\0\0\x63\x01\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xa0\x0d\0\0= \xb7\ -\x02\0\0\x1a\0\0\0\xb7\x03\0\0\x0c\0\0\0\xb7\x04\0\0\0\0\0\0\x85\0\0\0\xa7= \0\0\ -\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x54\xfe\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0= \x10\ -\x0d\0\0\x63\x70\x6c\0\0\0\0\0\x77\x07\0\0\x20\0\0\0\x63\x70\x70\0\0\0\0\0= \xb7\ -\x01\0\0\x05\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x10\x0d\0\0\xb7\x03\0\0\x8c= \0\0\ -\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x80= \x0d\ -\0\0\x61\x01\0\0\0\0\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0= \0\ -\xa8\0\0\0\xc5\x07\x42\xfe\0\0\0\0\x63\x7a\x80\xff\0\0\0\0\x61\xa1\x78\xff= \0\0\ -\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa0= \x80\ -\xff\0\0\0\0\x63\x06\x98\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x61= \x10\ -\0\0\0\0\0\0\x63\x06\x18\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\x= 61\ -\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\x= 10\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\x= 95\0\ -\0\0\0\0\0\0"; opts.ctx =3D (struct bpf_loader_ctx *)skel; - opts.data_sz =3D sizeof(opts_data) - 1; + opts.data_sz =3D opts_data_sz; opts.data =3D (void *)opts_data; - opts.insns_sz =3D sizeof(opts_insn) - 1; + opts.insns_sz =3D opts_insn_sz; opts.insns =3D (void *)opts_insn; =20 err =3D bpf_load_and_run(&opts); diff --git a/kernel/kexec_bpf_loader.c b/kernel/kexec_bpf_loader.c index 7f7884411e2c7..430f6c3970b61 100644 --- a/kernel/kexec_bpf_loader.c +++ b/kernel/kexec_bpf_loader.c @@ -24,14 +24,105 @@ #include #include "kexec_internal.h" =20 +#include "kexec_bpf/kexec_pe_parser_bpf.lskel.h" + +static struct kexec_pe_parser_bpf *pe_parser; + +static void *get_symbol_from_elf(const char *elf_data, size_t elf_size, + const char *symbol_name, + unsigned int *symbol_size) +{ + Elf_Ehdr *ehdr =3D (Elf_Ehdr *)elf_data; + Elf_Shdr *shdr, *dst_shdr; + const Elf_Sym *sym; + void *symbol_data; + + /* Check minimum size for ELF header */ + if (elf_size < sizeof(Elf_Ehdr)) { + pr_err("ELF file too small\n"); + return NULL; + } + + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) !=3D 0) { + pr_err("Not a valid ELF file\n"); + return NULL; + } + + /* Check section header table bounds */ + if (ehdr->e_shoff > elf_size || + ehdr->e_shoff + (ehdr->e_shnum * sizeof(Elf_Shdr)) > elf_size) { + pr_err("Section header table out of bounds\n"); + return NULL; + } + + sym =3D elf_find_symbol(ehdr, symbol_name); + if (!sym) + return NULL; + + /* Check symbol section index */ + if (sym->st_shndx >=3D ehdr->e_shnum) { + pr_err("Symbol section index out of bounds\n"); + return NULL; + } + + shdr =3D (struct elf_shdr *)(elf_data + ehdr->e_shoff); + dst_shdr =3D &shdr[sym->st_shndx]; + + /* Check section data bounds */ + if (dst_shdr->sh_offset > elf_size || + dst_shdr->sh_offset + dst_shdr->sh_size > elf_size || + sym->st_value > dst_shdr->sh_size) { + pr_err("Symbol data out of bounds\n"); + return NULL; + } + + symbol_data =3D (void *)(elf_data + dst_shdr->sh_offset + sym->st_value); + + if (symbol_size) + *symbol_size =3D sym->st_size; + + return symbol_data; +} + /* Load a ELF */ static int arm_bpf_prog(char *bpf_elf, unsigned long sz) { - return -1; + opts_data =3D get_symbol_from_elf(bpf_elf, sz, "opts_data", &opts_data_sz= ); + opts_insn =3D get_symbol_from_elf(bpf_elf, sz, "opts_insn", &opts_insn_sz= ); + if (!opts_data || !opts_insn) { + pr_err("Cannot get symbol from ELF: opts_data=3D%px, opts_insn=3D%px\n", + opts_data, opts_insn); + return -1; + } + + if (opts_data_sz < 1 || opts_insn_sz < 1) { + pr_err("Symbol size too small (opts_data_sz=3D%u, opts_insn_sz=3D%u)\n", + opts_data_sz, opts_insn_sz); + return -1; + } + /* + * When light skeleton generates opts_data[] and opts_insn[], it appends a + * NULL terminator at the end of string + */ + opts_data_sz =3D opts_data_sz - 1; + opts_insn_sz =3D opts_insn_sz - 1; + + pe_parser =3D kexec_pe_parser_bpf__open_and_load(); + if (!pe_parser) { + pr_info("Can not open and load bpf parser\n"); + return -1; + } + kexec_pe_parser_bpf__attach(pe_parser); + + return 0; } =20 static void disarm_bpf_prog(void) { + kexec_pe_parser_bpf__destroy(pe_parser); + pe_parser =3D NULL; + opts_data =3D NULL; + opts_insn =3D NULL; } =20 #define MAX_PARSING_BUF_NUM 16 --=20 2.49.0 From nobody Sat Apr 4 00:07:09 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4F9EE2BCF45 for ; Sun, 22 Mar 2026 01:46:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774144014; cv=none; b=kHH0qvtKiaGIrqQIqRUveKqa+T7eZsblFQ0rABfZ0wMuEr9GP+XKuslirWdEO2lxJjlF+7RvFZeY3Rq5WE7B12t5UAEugH5aljj7eTpT9Hrg2BLKv5UFS0lhmsU1LhicwRMAwfOoRPt3Fi4ZsgZJcGeAID3B1drerb6agBwSQtY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774144014; c=relaxed/simple; bh=+oD8gvoMdhTJTWjxuSGGXI9V4+JveNwwN2c1sWuG6pk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=CY7STSstAX8u5meGN/jzzaEpTd4+Sf9ZVH4P1wqsfQ0q2InZXlQgxpv4bkjCr87IxK0QC3KILUvr6NCIG2lIK6YeqWzgJcO9tM7Dmbj6hH3MZGd8QOiIOKTicRLA25eHEZ9KV1JEi2sVU85CZUEu1u64C9HFrq9ADdqoHyGKcE0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=bW6nfAR8; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="bW6nfAR8" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774144012; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=nd72fcDAH65f/XPbi1wNaLTF0mozFaAntHhS92PxMzM=; b=bW6nfAR8rketciwV/sFBv4BR/ad338ZFcn5Mc4DDgnO6tAtPs+9kromdHCvjk267SAcU8l 8X6KFSZa0NljfsNOnUPlZuDup08CJTzrzG6F8eFUDA02EkTT02nng+qSnoyI2aMoAL5ODx p5Xxr+6rjT425Ssv6j4fS0T8EvPdlwY= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-680-U9mpWWl6MLm_891dk8x9EQ-1; Sat, 21 Mar 2026 21:46:48 -0400 X-MC-Unique: U9mpWWl6MLm_891dk8x9EQ-1 X-Mimecast-MFC-AGG-ID: U9mpWWl6MLm_891dk8x9EQ_1774144006 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 139ED1956055; Sun, 22 Mar 2026 01:46:46 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.72.112.22]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id E73E8300019F; Sun, 22 Mar 2026 01:46:32 +0000 (UTC) From: Pingfan Liu To: linux-arm-kernel@lists.infradead.org Cc: Pingfan Liu , "David S. Miller" , Alexei Starovoitov , Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Eduard Zingerman , Song Liu , Yonghong Song , Jeremy Linton , Catalin Marinas , Will Deacon , Ard Biesheuvel , Simon Horman , Gerd Hoffmann , Vitaly Kuznetsov , Philipp Rudo , Viktor Malik , Jan Hendrik Farr , Baoquan He , Dave Young , Andrew Morton , kexec@lists.infradead.org, bpf@vger.kernel.org, systemd-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org Subject: [PATCHv7 10/13] arm64/kexec: Select KEXEC_BPF to support UEFI-style kernel image Date: Sun, 22 Mar 2026 09:43:59 +0800 Message-ID: <20260322014402.8815-11-piliu@redhat.com> In-Reply-To: <20260322014402.8815-1-piliu@redhat.com> References: <20260322014402.8815-1-piliu@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" Now everything is ready for kexec PE image parser. Select it on arm64 for zboot and UKI image support. Signed-off-by: Pingfan Liu Cc: Catalin Marinas Cc: Will Deacon To: linux-arm-kernel@lists.infradead.org --- arch/arm64/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 38dba5f7e4d2d..59f948a6f69d7 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1608,6 +1608,7 @@ config ARCH_SELECTS_KEXEC_FILE def_bool y depends on KEXEC_FILE select HAVE_IMA_KEXEC if IMA + select KEXEC_BPF if DEBUG_INFO_BTF && BPF_SYSCALL && KEEP_DECOMPRESSOR =20 config ARCH_SUPPORTS_KEXEC_SIG def_bool y --=20 2.49.0 From nobody Sat Apr 4 00:07:09 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2AB3619CD19 for ; Sun, 22 Mar 2026 01:47:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774144031; cv=none; b=VhS9KZT0VY8trd8GEpR9Vn7VfzmEPoFOM5xjDne2MAyxo3cM0+wa/i6M2JSEkMltR0mxFTGfjyLZ98yC1q6hNo8dy1eCSmeuygyMpfkKKVBRNeXZBnXgIhFg71HiQX7eJA+MkNIsWN6103r3RxxnqEClVW+f61OhoU+bEiLLkaQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774144031; c=relaxed/simple; bh=847v99Xg8escEA9StT0Cvn7kNsbxecQGSWfm3Mvaplg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=VhFAb1FVq3ULkiaNaBu4uBsChU02wtEM3xP1pdzxBjGeewEWJjR8DVuxDVn/RGnWudGS2RiSn23JdfFFcvt7BF2rNPTHlVW2aaPX2cQK8YdLbBwzj38rWPh4vyeDdB5q5or9ZPiZnfVAoOxJ9Yg993BH1c2DQUmGLGpAOrghbC4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=X1Mqa8sf; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="X1Mqa8sf" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774144028; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=PptI0EZ3v1cDZRQzhYiPLVw5pozAx5EwgN2XH6wjJnI=; b=X1Mqa8sfmln/y0f8alRzqz7eSjCSVq1gJ4/7Ez/JfT/XLUuC5zf1E0hKk9WYB6tamYUSDi pLPoWOn0yWFz/XdCTU0ImS3JYIVgxtl2hQpUQD2h+P1LusOvjdua2CNvIK7WCRBpq/Avig Wi7VfB5oSeiZpMg/VXPkqitTbwWsWII= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-486-6FAuPjxBNuuEhuC8Jl-sDQ-1; Sat, 21 Mar 2026 21:47:03 -0400 X-MC-Unique: 6FAuPjxBNuuEhuC8Jl-sDQ-1 X-Mimecast-MFC-AGG-ID: 6FAuPjxBNuuEhuC8Jl-sDQ_1774144021 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 4B0751955D84; Sun, 22 Mar 2026 01:47:01 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.72.112.22]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 18A7130001A2; Sun, 22 Mar 2026 01:46:46 +0000 (UTC) From: Pingfan Liu To: kexec@lists.infradead.org Cc: Pingfan Liu , "David S. Miller" , Alexei Starovoitov , Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Eduard Zingerman , Song Liu , Yonghong Song , Jeremy Linton , Catalin Marinas , Will Deacon , Ard Biesheuvel , Simon Horman , Gerd Hoffmann , Vitaly Kuznetsov , Philipp Rudo , Viktor Malik , Jan Hendrik Farr , Baoquan He , Dave Young , Andrew Morton , bpf@vger.kernel.org, systemd-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org Subject: [PATCHv7 11/13] tools/kexec: Introduce a bpf-prog to handle zboot image Date: Sun, 22 Mar 2026 09:44:00 +0800 Message-ID: <20260322014402.8815-12-piliu@redhat.com> In-Reply-To: <20260322014402.8815-1-piliu@redhat.com> References: <20260322014402.8815-1-piliu@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" 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 Cc: Baoquan He Cc: Dave Young Cc: Andrew Morton Cc: Philipp Rudo Cc: bpf@vger.kernel.org To: kexec@lists.infradead.org --- tools/kexec/Makefile | 162 +++++++++++++++ tools/kexec/template.c | 72 +++++++ tools/kexec/zboot_parser_bpf.c | 347 +++++++++++++++++++++++++++++++++ 3 files changed, 581 insertions(+) create mode 100644 tools/kexec/Makefile create mode 100644 tools/kexec/template.c create mode 100644 tools/kexec/zboot_parser_bpf.c diff --git a/tools/kexec/Makefile b/tools/kexec/Makefile new file mode 100644 index 0000000000000..a404a1453c888 --- /dev/null +++ b/tools/kexec/Makefile @@ -0,0 +1,162 @@ +# SPDX-License-Identifier: GPL-2.0 + +# Ensure Kbuild variables are available +include ../scripts/Makefile.include + +srctree :=3D $(patsubst %/tools/kexec,%,$(CURDIR)) +VMLINUX =3D $(srctree)/vmlinux +TOOLSDIR :=3D $(srctree)/tools +LIBDIR :=3D $(TOOLSDIR)/lib +BPFDIR :=3D $(LIBDIR)/bpf +ARCH ?=3D $(shell uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ -e s/aarc= h64.*/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 ar= ch/$(ARCH)/boot/Image +ifeq ($(ARCH),$(filter $(ARCH),arm64 riscv loongarch)) + EFI_IMAGE :=3D $(srctree)/arch/$(ARCH)/boot/vmlinuz.efi + KERNEL_IMAGE :=3D $(srctree)/arch/$(ARCH)/boot/vmlinux.bin +else + @echo "Unsupported architecture: $(ARCH)" + @exit 1 +endif + +CC =3D clang +CFLAGS =3D -O2 +BPF_PROG_CFLAGS =3D -g -fno-merge-all-constants -O2 -target bpf -Wall -I $= (BPFDIR) -I . +BPFTOOL =3D bpftool + +# ------------------------------------------------------------------------= --- +# Shared generated headers (common to all targets) +# ------------------------------------------------------------------------= --- +HEADERS =3D 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 +# : phony rule below. All build rules are driven by pattern rules +# and require no further changes. +# +# Artifacts produced per prefix

: +#

_parser_bpf.o - compiled BPF object +#

_parser_bpf.lskel.h - light skeleton header +#

_bytecode.c - extracted opts_data / opts_insn arrays +#

_bytecode.o - compiled bytecode object +#

.bpf - final ELF wrapper with .bpf.1 section +# ------------------------------------------------------------------------= --- +BPF_TARGETS =3D 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 =3D $(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 requi= red 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 ?=3D 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=3D$$(stat -c '%s' "$(KERNEL_IMAGE)" 2>/dev/null); \ + ELF_OVERHEAD=3D4096; \ + TOTAL_SIZE=3D$$((KERNEL_SIZE + $(INITRD_ESTIMATE_SIZE) + ELF_OVERHEAD));= \ + POWER=3D4096; \ + while [ $$POWER -le $$TOTAL_SIZE ]; do \ + POWER=3D$$((POWER * 2)); \ + done; \ + RINGBUF_SIZE=3D$$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 n= ames. +%_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 :=3D .bpf.1:zboot_bytecode.o + +# ------------------------------------------------------------------------= --- +# Helpers to build objcopy flags from a BPF_MAPS list +# ------------------------------------------------------------------------= --- +section_name =3D $(firstword $(subst :, ,$(1))) +source_file =3D $(lastword $(subst :, ,$(1))) + +only_section_flags =3D $(foreach m,$(1),--only-section=3D$(call section_na= me,$(m))) + +# ------------------------------------------------------------------------= --- +# Template: generates the %.bpf rule for a given target +# $(1) =3D lowercase target name, e.g. zboot +# $(2) =3D 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))=3D$(call source_file,$(m= )) \ + --set-section-flags $(call section_name,$(m))=3Dreadonly,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 +#include +#include +#include + +/* 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 pro= vided. + */ +#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") =3D "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)) =3D "rodata"; +static char dummy_data[16] __attribute__((used)) =3D "data"; +static char *dummy_mergeable_str __attribute__((used)) =3D ".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 +#include +#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[] =3D ".kernel"; +static const char initrd_sect_name[] =3D ".initrd"; +static const char cmdline_sect_name[] =3D ".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 =3D (struct cmd_hdr *)buf; + hdr->cmd =3D cmd; + hdr->subcmd =3D subcmd; + hdr->payload_len =3D data_len; + hdr->num_chunks =3D 0; + + payload =3D (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 !=3D SIG_ENFORCE_NONE) { + total =3D fill_cmd(ringbuf, + KEXEC_BPF_CMD_VERIFY_SIG, + 0, + pe_buf, + pe_sz); + ret =3D 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 =3D zboot_header.payload_offset; + payload_size =3D zboot_header.payload_size; + bpf_printk("do_zboot_decompress: payload offset=3D0x%x size=3D0x%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 =3D MAX_RECORD_SIZE - sizeof(struct cmd_hdr); + if (payload_size - 4 >=3D 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 =3D 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 DECOMPRE= SS\n"); + ret =3D 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 par= ser_id) +{ + kexec_sig_enforced sig_mode; + struct bpf_parser_context *bpf =3D 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 =3D BPF_CORE_READ(context, parsing_buf[0]); + buf_sz =3D BPF_CORE_READ(context, parsing_buf_sz[0]); + sig_mode =3D 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 =3D (char *)bpf_ringbuf_reserve(&ringbuf_1, MAX_RECORD_SIZE, 0); + if (!ringbuf) { + bpf_printk("parse_zboot: failed to reserve ringbuf\n"); + return 0; + } + + bpf =3D 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] =3D=3D 'M' && magic[1] =3D=3D 'Z') { + ret =3D 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] !=3D 0x7f || magic[1] !=3D 'E' || + magic[2] !=3D 'L' || magic[3] !=3D '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 =3D=3D 0 || ehdr.e_shnum =3D=3D 0 || + ehdr.e_shstrndx =3D=3D 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 =3D shstr_shdr.sh_offset; + shstrtab_sz =3D shstr_shdr.sh_size; + + for (i =3D 1; i < ELF_SCAN_MAX; i++) { + Elf64_Shdr shdr; + char sec_name[16]; + __u64 name_off; + + if (i >=3D 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 =3D 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= )) =3D=3D 0) { + total =3D fill_cmd(ringbuf, + KEXEC_BPF_CMD_COPY, + KEXEC_BPF_SUBCMD_INITRD, + buf_elf + shdr.sh_offset, + (__u32)shdr.sh_size); + ret =3D 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_na= me)) =3D=3D 0) { + total =3D fill_cmd(ringbuf, + KEXEC_BPF_CMD_COPY, + KEXEC_BPF_SUBCMD_CMDLINE, + buf_elf + shdr.sh_offset, + (__u32)shdr.sh_size); + ret =3D 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))= !=3D 0) + continue; + + ret =3D 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 =3D fill_cmd(ringbuf, KEXEC_BPF_CMD_DONE, 0, NULL, 0); + ret =3D 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; +} --=20 2.49.0 From nobody Sat Apr 4 00:07:09 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6DEC535A3A0 for ; Sun, 22 Mar 2026 01:47:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774144043; cv=none; b=QeD/J2hlVSMt6FJ1mEQlhChFYOogOludlSvKLnaO8gAARfneT9XCL6dKCJEIkF4DL36ccT60Gf2HZklIeg83PxgbHGJKSw5m+IYvEvYLRvrZIznODbhIu1VXTEvO5T7duixGZFP1L0pzaMWuvrp4QBBGWOQdnrGaJ0jfzA0OI2c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774144043; c=relaxed/simple; bh=STlGzMGeEeiUitGF7IFp6nDs8a4NZLF8OIdOxzt6Los=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MqhJ8UanzPgOYfl6RP/p9w27oQhfo4cnsF8XGOSRT8j88IRncIdIVxuN+R9OEJxI+PdAHHscTu/XUcnukKeXXNfXW+Nm5RA2p2oXggkh0ubcKrbPyF5X1w4IDaOXhtKh/g38vKl1wwmXOjDcqc6Dvfd8ecrdn1rjMzkmbJyjaCk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=NrVzSwjp; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="NrVzSwjp" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774144041; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=fSe22gQ7YSBoSx0BRUyY4Y8cXier6OyQlbQMTQ/OwNI=; b=NrVzSwjpxdli4G64YgaChUBH5EVnyudsGuOdTb77q6AE406VXwt4b0wwA3eK3BdCiUndwH PD2UEAAN04vN3atNWlWE2UjUziJq+qVh0h0GPBPld2uHMRrDnMz5asA6RWtngH5Ya41lAc xf2znnL5vZNRP9OlI+ZdhXX77aLpThs= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-687-SdzToAr3PPGCkDDcyaTQFQ-1; Sat, 21 Mar 2026 21:47:17 -0400 X-MC-Unique: SdzToAr3PPGCkDDcyaTQFQ-1 X-Mimecast-MFC-AGG-ID: SdzToAr3PPGCkDDcyaTQFQ_1774144034 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 93B8B19560A6; Sun, 22 Mar 2026 01:47:14 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.72.112.22]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 2ECFE300019F; Sun, 22 Mar 2026 01:47:01 +0000 (UTC) From: Pingfan Liu To: kexec@lists.infradead.org Cc: Pingfan Liu , "David S. Miller" , Alexei Starovoitov , Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Eduard Zingerman , Song Liu , Yonghong Song , Jeremy Linton , Catalin Marinas , Will Deacon , Ard Biesheuvel , Simon Horman , Gerd Hoffmann , Vitaly Kuznetsov , Philipp Rudo , Viktor Malik , Jan Hendrik Farr , Baoquan He , Dave Young , Andrew Morton , bpf@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 Message-ID: <20260322014402.8815-13-piliu@redhat.com> In-Reply-To: <20260322014402.8815-1-piliu@redhat.com> References: <20260322014402.8815-1-piliu@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" Signed-off-by: Pingfan Liu Cc: Baoquan He Cc: Dave Young Cc: Andrew Morton Cc: Philipp Rudo 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 =3D vmlinux.h bpf_helper_defs.h image_size.h #

.bpf - final ELF wrapper with .bpf.1 section # ------------------------------------------------------------------------= --- BPF_TARGETS =3D zboot +BPF_TARGETS +=3D uki =20 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 =3D $(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 =20 -.PHONY: zboot clean +.PHONY: zboot uki clean =20 # ------------------------------------------------------------------------= --- # Shared header rules @@ -123,6 +129,15 @@ image_size.h: $(KERNEL_IMAGE) # ------------------------------------------------------------------------= --- ZBOOT_BPF_MAPS :=3D .bpf.1:zboot_bytecode.o =20 +# 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 :=3D .bpf.1:uki_bytecode.o .bpf.nested:zboot.bpf +else +UKI_BPF_MAPS :=3D .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_fi= le,$(m))) endef =20 $(eval $(call BPF_WRAPPER_RULE,zboot,ZBOOT)) +$(eval $(call BPF_WRAPPER_RULE,uki,UKI)) =20 # ------------------------------------------------------------------------= --- # 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 +#include +#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[] =3D ".linux"; +static const char initrd_sect_name[] =3D ".initrd"; +static const char cmdline_sect_name[] =3D ".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 =3D (__u16)(cmd_word & 0xffff); + __u16 subcmd =3D (__u16)(cmd_word >> 16); + + hdr =3D (struct cmd_hdr *)buf; + hdr->cmd =3D cmd; + hdr->subcmd =3D subcmd; + hdr->pipeline_flag =3D pipeline_flag; + hdr->payload_len =3D data_len; + hdr->num_chunks =3D 0; + + payload =3D (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 =3D 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 !=3D 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 !=3D 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 =3D pe_offset + 4 + 20 + opt_hdr_sz; + if (section_table_off >=3D pe_sz) + return -EINVAL; + + for (i =3D 0; i < PE_SCAN_MAX; i++) { + __u32 raw_size, raw_off, shdr_off; + char sec_name[8]; + __u16 subcmd; + + if (i >=3D num_sections) + break; + + shdr_off =3D 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 =3D 0; + if (__builtin_memcmp(sec_name, linux_sect_name, sizeof(linux_sect_name))= =3D=3D 0) { + subcmd =3D KEXEC_BPF_SUBCMD_KERNEL; + /* + * .linux section may contain different format kernel, which should be + * passed to the next stage to handle + */ + pipeline_flag =3D KEXEC_BPF_PIPELINE_FILL; + } + else if (__builtin_memcmp(sec_name, initrd_sect_name, sizeof(initrd_sect= _name)) =3D=3D 0) + subcmd =3D KEXEC_BPF_SUBCMD_INITRD; + else if (__builtin_memcmp(sec_name, cmdline_sect_name, sizeof(cmdline_se= ct_name)) =3D=3D 0) + subcmd =3D 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 =3D fill_cmd(scratch, + MAKE_CMD(KEXEC_BPF_CMD_COPY, subcmd), + pipeline_flag, + pe_buf + raw_off, + raw_size); + ret =3D 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 parse= r_id) +{ + struct bpf_parser_context *bpf_ctx; + char *buf0, *buf1, *scratch; + __u8 magic[4]; + int ret; + + bpf_printk("parse_uki: start\n"); + buf0 =3D BPF_CORE_READ(context, parsing_buf[0]); + if (!buf0) + return 0; + + bpf_ctx =3D 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 =3D 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 =3D 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 =3D bpf_ringbuf_reserve(&ringbuf_1, MAX_RECORD_SIZE, 0); + if (!scratch) { + bpf_printk("ringbuf reserve failed\n"); + goto out; + } + + if (magic[0] =3D=3D 'M' && magic[1] =3D=3D 'Z') { + bpf_printk("call process_uki_pe\n"); + ret =3D 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 =3D fill_cmd(scratch, MAKE_CMD(KEXEC_BPF_CMD_DONE, 0), + 0, NULL, 0); + ret =3D 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; +} --=20 2.49.0 From nobody Sat Apr 4 00:07:09 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 53BD637CD3C for ; Sun, 22 Mar 2026 01:47:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774144060; cv=none; b=AFMr0NNZD2XECgBwOkjUl3RXTTEDEP36aqVp+Zjjg3XwEnj42oXg1DPm9Ia99WDij6Vl1HKVTF0wafBpKQUh3Y8sx1tmWrbBFCUSENJAPQm0b2xqJN5W7YMgmqL5syzlC7XqzvlNl6Q50Pg8mBp4NP7S/4fPuLlslG2cMrb803o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774144060; c=relaxed/simple; bh=h861BgEA28NVQYovTVGYsXvwnS2jXgU3AxLU+H1tVoE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=tlJXXGPt4qTGknTVfgWn0mxp7ll0BTIrQLTInO74S5vBgYBSF80ysdY53TvNcLTMBNr3TVAFzr8JULBG69t+0ccVQbBp141EZRtEH76WFAEsRxQmhSZpjR+pgwUQ+TK+72MCavvGCun1LgNrnFWWjTa3hSw5ZPqnuTsx6UNJpLc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=it7BxrkL; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="it7BxrkL" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774144056; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=WhbO8v48EvSUcQMKHFPvmGE+SqNO4MFfc9uap8rjv+I=; b=it7BxrkLvtkZJGaGHN9fiRNAf2QRnCD1TG8CyfV4F4veqZHxbslSbUrIMip3IS3Wq4utli McdVotLTcbN7y59/U4QupVneBpamN7Zdclv6Lg5WmnlArSmyoUVEcG7/j9kSzm+Dmobonk 2wsnkr1DdhNOBmYk+AK6ubFP983PbWI= Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-512-4pqY6ZvGNDOOBMaq2YxDqg-1; Sat, 21 Mar 2026 21:47:30 -0400 X-MC-Unique: 4pqY6ZvGNDOOBMaq2YxDqg-1 X-Mimecast-MFC-AGG-ID: 4pqY6ZvGNDOOBMaq2YxDqg_1774144048 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 5D4AB1800359; Sun, 22 Mar 2026 01:47:28 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.72.112.22]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 4BCFE300019F; Sun, 22 Mar 2026 01:47:14 +0000 (UTC) From: Pingfan Liu To: kexec@lists.infradead.org Cc: Pingfan Liu , "David S. Miller" , Alexei Starovoitov , Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Eduard Zingerman , Song Liu , Yonghong Song , Jeremy Linton , Catalin Marinas , Will Deacon , Ard Biesheuvel , Simon Horman , Gerd Hoffmann , Vitaly Kuznetsov , Philipp Rudo , Viktor Malik , Jan Hendrik Farr , Baoquan He , Dave Young , Andrew Morton , bpf@vger.kernel.org, systemd-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org Subject: [PATCHv7 13/13] tools/kexec: Introduce a tool to build zboot envelop Date: Sun, 22 Mar 2026 09:44:02 +0800 Message-ID: <20260322014402.8815-14-piliu@redhat.com> In-Reply-To: <20260322014402.8815-1-piliu@redhat.com> References: <20260322014402.8815-1-piliu@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" The new tool builds a ELF container around zboot image. It contains three key sections: .kernel, .initrd, .cmdline. Later zboot_bpf_parser will parse this container format. Signed-off-by: Pingfan Liu Cc: Baoquan He Cc: Dave Young Cc: Andrew Morton Cc: Philipp Rudo Cc: bpf@vger.kernel.org To: kexec@lists.infradead.org --- tools/kexec/Makefile | 31 ++- tools/kexec/build_zboot_envelop.c | 362 ++++++++++++++++++++++++++++++ tools/kexec/zboot_envelop.h | 7 + tools/kexec/zboot_parser_bpf.c | 7 +- 4 files changed, 401 insertions(+), 6 deletions(-) create mode 100644 tools/kexec/build_zboot_envelop.c create mode 100644 tools/kexec/zboot_envelop.h diff --git a/tools/kexec/Makefile b/tools/kexec/Makefile index c0e2ad44658e3..c168a6eeea09d 100644 --- a/tools/kexec/Makefile +++ b/tools/kexec/Makefile @@ -21,9 +21,21 @@ endif =20 CC =3D clang CFLAGS =3D -O2 -BPF_PROG_CFLAGS =3D -g -fno-merge-all-constants -O2 -target bpf -Wall -I $= (BPFDIR) -I . +BPF_PROG_CFLAGS =3D -g -O2 -target bpf -Wall -I $(BPFDIR) -I . BPFTOOL =3D bpftool =20 +# Host compiler for native tools (must not be the BPF clang cross-compiler) +HOSTCC ?=3D gcc +HOSTCFLAGS =3D -std=3Dc11 -O2 -Wall -Wextra + +# ------------------------------------------------------------------------= --- +# zboot ELF image parameters +# INITRAMFS: path to the initramfs image (required) +# CMDLINE: kernel command line string (optional, defaults to empty) +# ------------------------------------------------------------------------= --- +INITRAMFS ?=3D +CMDLINE ?=3D + # ------------------------------------------------------------------------= --- # Shared generated headers (common to all targets) # ------------------------------------------------------------------------= --- @@ -54,7 +66,7 @@ ALL_BPF_ARTIFACTS =3D $(foreach t,$(BPF_TARGETS),$(call B= PF_ARTIFACTS,$(t))) # ------------------------------------------------------------------------= --- # Top-level phony targets # ------------------------------------------------------------------------= --- -zboot: $(HEADERS) $(call BPF_ARTIFACTS,zboot) build_zboot_image +zboot: $(HEADERS) $(call BPF_ARTIFACTS,zboot) build_zboot_envelop ifeq ($(ARCH),$(filter $(ARCH),arm64 riscv loongarch)) uki: $(HEADERS) zboot.bpf $(call BPF_ARTIFACTS,uki) else @@ -171,8 +183,21 @@ endef $(eval $(call BPF_WRAPPER_RULE,zboot,ZBOOT)) $(eval $(call BPF_WRAPPER_RULE,uki,UKI)) =20 +# ------------------------------------------------------------------------= --- +# Host tool: build_zboot_envelop +# Packs EFI_IMAGE (.kernel), INITRAMFS (.initrd) and CMDLINE (.cmdline) +# into a single ELF file consumed by the kexec loader. +# ------------------------------------------------------------------------= --- +build_zboot_envelop: build_zboot_envelop.c + @$(HOSTCC) $(HOSTCFLAGS) -o $@ $< + +zboot_image.elf: build_zboot_envelop $(EFI_IMAGE) $(INITRAMFS) + $(if $(INITRAMFS),,$(error INITRAMFS is not set. Usage: make INITRAMFS=3D= [CMDLINE=3D"..."])) + @./build_zboot_envelop $(EFI_IMAGE) $(INITRAMFS) "$(CMDLINE)" + # ------------------------------------------------------------------------= --- # Clean # ------------------------------------------------------------------------= --- clean: - @rm -f $(HEADERS) $(ALL_BPF_ARTIFACTS) *.base.o + @rm -f $(HEADERS) $(ALL_BPF_ARTIFACTS) *.base.o \ + build_zboot_envelop zboot_image.elf diff --git a/tools/kexec/build_zboot_envelop.c b/tools/kexec/build_zboot_en= velop.c new file mode 100644 index 0000000000000..d2d9ffc11fdc1 --- /dev/null +++ b/tools/kexec/build_zboot_envelop.c @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * build_zboot_envelop.c - Pack zboot image, initramfs and cmdline into an= ELF file. + * + * Usage: build_zboot_envelop [output.= elf] + * + * Output ELF sections: + * .kernel - zboot image (PE signature preserved, no padding) + * .initrd - initramfs image + * .cmdline - kernel command line string (NUL-terminated) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "zboot_envelop.h" + +#define DEFAULT_OUTPUT "zboot_image.elf" + +/* + * Section indices into the section header table. + */ +enum { + SHN_UNDEF_IDX =3D 0, + SHN_KERNEL_IDX =3D 1, + SHN_INITRD_IDX =3D 2, + SHN_CMDLINE_IDX =3D 3, + SHN_SHSTRTAB_IDX =3D 4, + SHN_NUM =3D 5, +}; + +/* + * String table layout (offsets are fixed at compile time): + * + * off 0 : '\0' (mandatory empty string) + * off 1 : ".kernel\0" + * off 9 : ".initrd\0" + * off 17 : ".cmdline\0" + * off 26 : ".shstrtab\0" + */ +#define SHSTR_OFF_KERNEL 1 +#define SHSTR_OFF_INITRD 9 +#define SHSTR_OFF_CMDLINE 17 +#define SHSTR_OFF_SHSTRTAB 26 + +#define SHSTRTAB_CONTENT \ + "\0" KERNEL_SECT_NAME \ + "\0" INITRD_SECT_NAME \ + "\0" CMDLINE_SECT_NAME \ + "\0.shstrtab" + +/* sizeof() includes the final NUL from the string literal. */ +#define SHSTRTAB_SIZE (sizeof(SHSTRTAB_CONTENT)) + +/* + * struct input_file - holds a memory-mapped input file together with its = size. + * @data: pointer to the mapped region (or NULL if not mmap'd) + * @size: exact byte size of the file + * @fd: open file descriptor (-1 when closed) + */ +struct input_file { + const void *data; + size_t size; + int fd; +}; + +/* + * align8 - round @off up to the next multiple of 8. + */ +static inline size_t align8(size_t off) +{ + return (off + 7) & ~(size_t)7; +} + +/* + * open_and_map - open @path read-only and mmap its entire content. + * + * Returns 0 on success, -1 on error (errno is set by the failing syscall = and + * a diagnostic is printed to stderr). + */ +static int open_and_map(const char *path, struct input_file *f) +{ + struct stat st; + + f->fd =3D open(path, O_RDONLY); + if (f->fd < 0) { + fprintf(stderr, "build_zboot_envelop: cannot open '%s': %s\n", path, + strerror(errno)); + return -1; + } + + if (fstat(f->fd, &st) < 0) { + fprintf(stderr, "build_zboot_envelop: cannot stat '%s': %s\n", path, + strerror(errno)); + goto err_close; + } + + f->size =3D (size_t)st.st_size; + f->data =3D mmap(NULL, f->size, PROT_READ, MAP_PRIVATE, f->fd, 0); + if (f->data =3D=3D MAP_FAILED) { + fprintf(stderr, "build_zboot_envelop: cannot mmap '%s': %s\n", path, + strerror(errno)); + goto err_close; + } + + return 0; + +err_close: + close(f->fd); + f->fd =3D -1; + return -1; +} + +static void close_and_unmap(struct input_file *f) +{ + if (f->data && f->data !=3D MAP_FAILED) + munmap((void *)f->data, f->size); + if (f->fd >=3D 0) + close(f->fd); +} + +static int write_all(int fd, const void *buf, size_t len) +{ + const uint8_t *p =3D buf; + ssize_t n; + + while (len > 0) { + n =3D write(fd, p, len); + if (n < 0) { + if (errno =3D=3D EINTR) + continue; + return -1; + } + p +=3D n; + len -=3D n; + } + return 0; +} + +/* + * write_padding - write @len zero bytes to @fd. + * + * Returns 0 on success, -1 on error. + */ +static int write_padding(int fd, size_t len) +{ + static const uint8_t zero[8]; + size_t chunk; + ssize_t n; + + while (len > 0) { + chunk =3D (len < sizeof(zero)) ? len : sizeof(zero); + n =3D write(fd, zero, chunk); + if (n < 0) { + if (errno =3D=3D EINTR) + continue; + return -1; + } + len -=3D n; + } + return 0; +} + +/* + * fill_shdr - populate a Elf64_Shdr for a SHT_PROGBITS section. + * + * @shdr: section header to fill + * @name_off: offset of the section name inside .shstrtab + * @offset: file offset at which the section data begins + * @size: exact byte count of the section data (no padding included) + * @flags: section flags (e.g. SHF_ALLOC) + */ +static void fill_shdr(Elf64_Shdr *shdr, uint32_t name_off, uint64_t offset, + uint64_t size, uint64_t flags) +{ + memset(shdr, 0, sizeof(*shdr)); + shdr->sh_name =3D name_off; + shdr->sh_type =3D SHT_PROGBITS; + shdr->sh_flags =3D flags; + shdr->sh_offset =3D offset; + shdr->sh_size =3D size; + shdr->sh_addralign =3D 1; +} + +int main(int argc, char *argv[]) +{ + const char *kernel_path, *initrd_path, *cmdline, *output_path; + struct input_file kernel =3D { NULL, 0, -1 }; + struct input_file initrd =3D { NULL, 0, -1 }; + Elf64_Ehdr ehdr; + Elf64_Shdr shdrs[SHN_NUM]; + size_t cmdline_size; /* includes terminating NUL */ + + /* + * File layout (all section-data offsets are 8-byte aligned except + * .kernel which must start directly after the headers to guarantee + * that no byte is inserted before the PE signature): + * + * [ELF header] 64 B + * [Section headers] SHN_NUM * sizeof(Elf64_Shdr) + * [.kernel data] kernel.size bytes (NO internal padding) + * + * [.initrd data] initrd.size bytes + * + * [.cmdline data] cmdline_size bytes + * + * [.shstrtab data] SHSTRTAB_SIZE bytes + */ + size_t off_shdrs, off_kernel, off_initrd, off_cmdline, off_shstrtab; + size_t pad_after_kernel, pad_after_initrd, pad_after_cmdline; + int outfd; + int ret =3D EXIT_FAILURE; + + if (argc < 4 || argc > 5) { + fprintf(stderr, + "Usage: %s " + "[output.elf]\n", + argv[0]); + return EXIT_FAILURE; + } + + kernel_path =3D argv[1]; + initrd_path =3D argv[2]; + cmdline =3D argv[3]; + output_path =3D (argc =3D=3D 5) ? argv[4] : DEFAULT_OUTPUT; + cmdline_size =3D strlen(cmdline) + 1; /* +1 for NUL terminator */ + + /* ------------------------------------------------------------------ */ + /* 1. Map input files */ + /* ------------------------------------------------------------------ */ + if (open_and_map(kernel_path, &kernel) < 0) + goto out; + + if (open_and_map(initrd_path, &initrd) < 0) + goto out; + + /* Compute file layout */ + off_shdrs =3D sizeof(Elf64_Ehdr); + off_kernel =3D off_shdrs + SHN_NUM * sizeof(Elf64_Shdr); + + /* + * .kernel must not contain any padding - its sh_size equals the + * exact file size so that any PE authenticode signature is intact. + * Alignment padding goes *after* the raw bytes, between sections. + */ + pad_after_kernel =3D 0; + off_initrd =3D off_kernel + kernel.size + pad_after_kernel; + + pad_after_initrd =3D + align8(off_initrd + initrd.size) - (off_initrd + initrd.size); + off_cmdline =3D off_initrd + initrd.size + pad_after_initrd; + + pad_after_cmdline =3D align8(off_cmdline + cmdline_size) - + (off_cmdline + cmdline_size); + off_shstrtab =3D off_cmdline + cmdline_size + pad_after_cmdline; + + memset(&ehdr, 0, sizeof(ehdr)); + + ehdr.e_ident[EI_MAG0] =3D ELFMAG0; + ehdr.e_ident[EI_MAG1] =3D ELFMAG1; + ehdr.e_ident[EI_MAG2] =3D ELFMAG2; + ehdr.e_ident[EI_MAG3] =3D ELFMAG3; + ehdr.e_ident[EI_CLASS] =3D ELFCLASS64; + ehdr.e_ident[EI_DATA] =3D ELFDATA2LSB; + ehdr.e_ident[EI_VERSION] =3D EV_CURRENT; + ehdr.e_ident[EI_OSABI] =3D ELFOSABI_NONE; + + ehdr.e_type =3D ET_EXEC; + ehdr.e_machine =3D EM_AARCH64; + ehdr.e_version =3D EV_CURRENT; + ehdr.e_ehsize =3D sizeof(Elf64_Ehdr); + ehdr.e_shentsize =3D sizeof(Elf64_Shdr); + ehdr.e_shnum =3D SHN_NUM; + ehdr.e_shoff =3D (Elf64_Off)off_shdrs; + ehdr.e_shstrndx =3D SHN_SHSTRTAB_IDX; + + /* Build section headers */ + memset(shdrs, 0, sizeof(shdrs)); + + /* [0] SHN_UNDEF - mandatory null entry */ + + /* [1] .kernel */ + fill_shdr(&shdrs[SHN_KERNEL_IDX], SHSTR_OFF_KERNEL, + (uint64_t)off_kernel, (uint64_t)kernel.size, SHF_ALLOC); + + /* [2] .initrd */ + fill_shdr(&shdrs[SHN_INITRD_IDX], SHSTR_OFF_INITRD, + (uint64_t)off_initrd, (uint64_t)initrd.size, SHF_ALLOC); + + /* [3] .cmdline */ + fill_shdr(&shdrs[SHN_CMDLINE_IDX], SHSTR_OFF_CMDLINE, + (uint64_t)off_cmdline, (uint64_t)cmdline_size, SHF_ALLOC); + + /* [4] .shstrtab */ + memset(&shdrs[SHN_SHSTRTAB_IDX], 0, sizeof(Elf64_Shdr)); + shdrs[SHN_SHSTRTAB_IDX].sh_name =3D SHSTR_OFF_SHSTRTAB; + shdrs[SHN_SHSTRTAB_IDX].sh_type =3D SHT_STRTAB; + shdrs[SHN_SHSTRTAB_IDX].sh_offset =3D (Elf64_Off)off_shstrtab; + shdrs[SHN_SHSTRTAB_IDX].sh_size =3D (uint64_t)SHSTRTAB_SIZE; + shdrs[SHN_SHSTRTAB_IDX].sh_addralign =3D 1; + + outfd =3D open(output_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (outfd < 0) { + fprintf(stderr, "build_zboot_envelop: cannot create '%s': %s\n", output_= path, + strerror(errno)); + goto out; + } + + if (write_all(outfd, &ehdr, sizeof(ehdr)) < 0) + goto err_write; + + if (write_all(outfd, shdrs, sizeof(shdrs)) < 0) + goto err_write; + + /* .kernel - raw bytes, no padding inside the section */ + if (write_all(outfd, kernel.data, kernel.size) < 0) + goto err_write; + if (write_padding(outfd, pad_after_kernel) < 0) + goto err_write; + + /* .initrd */ + if (write_all(outfd, initrd.data, initrd.size) < 0) + goto err_write; + if (write_padding(outfd, pad_after_initrd) < 0) + goto err_write; + + /* .cmdline - NUL-terminated string */ + if (write_all(outfd, cmdline, cmdline_size) < 0) + goto err_write; + if (write_padding(outfd, pad_after_cmdline) < 0) + goto err_write; + + /* .shstrtab */ + if (write_all(outfd, SHSTRTAB_CONTENT, SHSTRTAB_SIZE) < 0) + goto err_write; + + printf("build_zboot_envelop: wrote '%s' (.kernel=3D%zuB .initrd=3D%zuB = " + ".cmdline=3D%zuB)\n", + output_path, kernel.size, initrd.size, cmdline_size - 1); + + close(outfd); + ret =3D EXIT_SUCCESS; + goto out; + +err_write: + fprintf(stderr, "build_zboot_envelop: write error on '%s': %s\n", output_= path, + strerror(errno)); + close(outfd); + unlink(output_path); + +out: + close_and_unmap(&kernel); + close_and_unmap(&initrd); + return ret; +} diff --git a/tools/kexec/zboot_envelop.h b/tools/kexec/zboot_envelop.h new file mode 100644 index 0000000000000..813723c64ecf3 --- /dev/null +++ b/tools/kexec/zboot_envelop.h @@ -0,0 +1,7 @@ +#ifndef ZBOOT_ENVELOP_H +#define ZBOOT_ENVELOP_H + +#define KERNEL_SECT_NAME ".kernel" +#define INITRD_SECT_NAME ".initrd" +#define CMDLINE_SECT_NAME ".cmdline" +#endif diff --git a/tools/kexec/zboot_parser_bpf.c b/tools/kexec/zboot_parser_bpf.c index 10098dca2a27a..16fbad83a9bde 100644 --- a/tools/kexec/zboot_parser_bpf.c +++ b/tools/kexec/zboot_parser_bpf.c @@ -16,6 +16,7 @@ #define RINGBUF4_SIZE MIN_BUF_SIZE =20 #include "template.c" +#include "zboot_envelop.h" =20 #define ELF_SCAN_MAX 8 =20 @@ -43,9 +44,9 @@ struct linux_pe_zboot_header { unsigned int pe_header_offset; } __attribute__((packed)); =20 -static const char linux_sect_name[] =3D ".kernel"; -static const char initrd_sect_name[] =3D ".initrd"; -static const char cmdline_sect_name[] =3D ".cmdline"; +static const char linux_sect_name[] =3D KERNEL_SECT_NAME; +static const char initrd_sect_name[] =3D INITRD_SECT_NAME; +static const char cmdline_sect_name[] =3D CMDLINE_SECT_NAME; =20 /* * fill_cmd - overwrite the cmd_hdr at the start of @buf and copy @data_len --=20 2.49.0