[kexec-tools][PATCH] RISC-V: add kexec_load support for vmlinuz

Tao Liu ltao at redhat.com
Mon May 25 21:58:07 PDT 2026


Copy arm64 code and change for riscv so that the kexec -c can load
a vmlinuz image.

Signed-off-by: Tao Liu <ltao at redhat.com>
---
 kexec/arch/riscv/Makefile              |   1 +
 kexec/arch/riscv/image-header.h        |  12 +++
 kexec/arch/riscv/kexec-riscv.c         |   1 +
 kexec/arch/riscv/kexec-riscv.h         |   6 ++
 kexec/arch/riscv/kexec-vmlinuz-riscv.c | 112 +++++++++++++++++++++++++
 5 files changed, 132 insertions(+)
 create mode 100644 kexec/arch/riscv/kexec-vmlinuz-riscv.c

diff --git a/kexec/arch/riscv/Makefile b/kexec/arch/riscv/Makefile
index 18a997b..6ba8976 100644
--- a/kexec/arch/riscv/Makefile
+++ b/kexec/arch/riscv/Makefile
@@ -4,6 +4,7 @@
 riscv_KEXEC_SRCS = kexec/arch/riscv/crashdump-riscv.c
 riscv_KEXEC_SRCS += kexec/arch/riscv/kexec-elf-riscv.c
 riscv_KEXEC_SRCS += kexec/arch/riscv/kexec-image-riscv.c
+riscv_KEXEC_SRCS += kexec/arch/riscv/kexec-vmlinuz-riscv.c
 riscv_KEXEC_SRCS += kexec/arch/riscv/kexec-riscv.c
 
 riscv_DT_OPS += kexec/dt-ops.c
diff --git a/kexec/arch/riscv/image-header.h b/kexec/arch/riscv/image-header.h
index a677546..7e950ee 100644
--- a/kexec/arch/riscv/image-header.h
+++ b/kexec/arch/riscv/image-header.h
@@ -85,4 +85,16 @@ static inline uint64_t riscv_header_image_size(const struct riscv_image_header *
 	return le64toh(h->image_size);
 }
 
+static const uint8_t riscv_image_pe_sig[2] = {'M', 'Z'};
+static const uint8_t riscv_pe_machtype[6] = {'P','E', 0x0, 0x0, 0x64, 0x50};
+
+static inline int riscv_header_check_pe_sig(const struct riscv_image_header *h)
+{
+	if (!h)
+		return 0;
+
+	return (*((char *)&(h->code0)) == riscv_image_pe_sig[0] &&
+		*((char *)&(h->code0) + 1) == riscv_image_pe_sig[1]);
+}
+
 #endif
diff --git a/kexec/arch/riscv/kexec-riscv.c b/kexec/arch/riscv/kexec-riscv.c
index a5a12a0..a7188ac 100644
--- a/kexec/arch/riscv/kexec-riscv.c
+++ b/kexec/arch/riscv/kexec-riscv.c
@@ -34,6 +34,7 @@ const struct arch_map_entry arches[] = {
 struct file_type file_type[] = {
 	{"elf-riscv", elf_riscv_probe, elf_riscv_load, elf_riscv_usage},
 	{"image-riscv", image_riscv_probe, image_riscv_load, image_riscv_usage},
+	{"vmlinuz-riscv", pez_riscv_probe, pez_riscv_load, pez_riscv_usage},
 };
 int file_types = sizeof(file_type) / sizeof(file_type[0]);
 
diff --git a/kexec/arch/riscv/kexec-riscv.h b/kexec/arch/riscv/kexec-riscv.h
index cfb0377..f487b27 100644
--- a/kexec/arch/riscv/kexec-riscv.h
+++ b/kexec/arch/riscv/kexec-riscv.h
@@ -51,3 +51,9 @@ int image_riscv_probe(const char *buf, off_t len);
 void image_riscv_usage(void);
 int image_riscv_load(int argc, char **argv, const char *buf, off_t len,
 		   struct kexec_info *info);
+
+/* kexec-vmlinuz-riscv.c */
+int pez_riscv_probe(const char *buf, off_t len);
+void pez_riscv_usage(void);
+int pez_riscv_load(int argc, char **argv, const char *buf, off_t len,
+		   struct kexec_info *info);
diff --git a/kexec/arch/riscv/kexec-vmlinuz-riscv.c b/kexec/arch/riscv/kexec-vmlinuz-riscv.c
new file mode 100644
index 0000000..6430e37
--- /dev/null
+++ b/kexec/arch/riscv/kexec-vmlinuz-riscv.c
@@ -0,0 +1,112 @@
+/*
+ * RISC-V PE compressed Image (vmlinuz, ZBOOT) support.
+ * Based on arm64 code
+ */
+
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "kexec.h"
+#include "kexec-riscv.h"
+#include "image-header.h"
+#include <kexec-pe-zboot.h>
+#include "arch/options.h"
+
+static int kernel_fd = -1;
+static off_t decompressed_size;
+
+/* Returns:
+ * -1 : in case of error/invalid format (not a valid PE+compressed ZBOOT format.
+ */
+int pez_riscv_probe(const char *kernel_buf, off_t kernel_size)
+{
+	int ret = -1;
+	const struct riscv_image_header *h;
+	char *buf;
+	off_t buf_sz;
+
+	buf = (char *)kernel_buf;
+	buf_sz = kernel_size;
+	if (!buf)
+		return -1;
+	h = (const struct riscv_image_header *)buf;
+
+	dbgprintf("%s: PROBE.\n", __func__);
+	if (buf_sz < sizeof(struct riscv_image_header)) {
+		dbgprintf("%s: Not large enough to be a PE image.\n", __func__);
+		return -1;
+	}
+	if (!riscv_header_check_pe_sig(h)) {
+		dbgprintf("%s: Not an PE image.\n", __func__);
+		return -1;
+	}
+
+	if (buf_sz < sizeof(struct riscv_image_header) + h->res3) {
+		dbgprintf("%s: PE image offset larger than image.\n", __func__);
+		return -1;
+	}
+
+	if (memcmp(&buf[h->res3],
+		   riscv_pe_machtype, sizeof(riscv_pe_machtype))) {
+		dbgprintf("%s: PE header doesn't match machine type.\n", __func__);
+		return -1;
+	}
+
+	ret = pez_prepare(buf, buf_sz, &kernel_fd, &decompressed_size);
+
+	if (!ret) {
+	    /* validate the riscv specific header */
+	    struct riscv_image_header hdr_check;
+	    if (read(kernel_fd, &hdr_check, sizeof(hdr_check)) != sizeof(hdr_check))
+		goto bad_header;
+
+	    lseek(kernel_fd, 0, SEEK_SET);
+
+	    if (!riscv_header_check_magic(&hdr_check)) {
+		dbgprintf("%s: Bad riscv image header.\n", __func__);
+		goto bad_header;
+	    }
+	}
+
+	return ret;
+bad_header:
+	close(kernel_fd);
+	free(buf);
+	return -1;
+}
+
+int pez_riscv_load(int argc, char **argv, const char *buf, off_t len,
+			struct kexec_info *info)
+{
+	if (kernel_fd > 0 && decompressed_size > 0) {
+		char *kbuf;
+		off_t nread;
+		int fd;
+
+		if (info->kernel_fd > 0)
+			close(info->kernel_fd);
+		info->kernel_fd = kernel_fd;
+		fd = dup(kernel_fd);
+		if (fd < 0) {
+			dbgprintf("%s: dup fd failed.\n", __func__);
+			return -1;
+		}
+		kbuf = slurp_fd(fd, NULL, decompressed_size, &nread);
+		if (!kbuf || nread != decompressed_size) {
+			dbgprintf("%s: slurp_fd failed.\n", __func__);
+			return -1;
+		}
+		return image_riscv_load(argc, argv, kbuf, decompressed_size, info);
+	}
+
+	dbgprintf("%s: wrong kernel file descriptor.\n", __func__);
+	return -1;
+}
+
+void pez_riscv_usage(void)
+{
+	printf(
+"     An RISC-V vmlinuz, PE image of a compressed, little endian.\n"
+"     kernel, built with ZBOOT enabled.\n\n");
+}
-- 
2.47.0




More information about the kexec mailing list