Skip to content

Commit 55d990f

Browse files
tangyoulingchenhuacai
authored andcommitted
LoongArch: Add EFI binary support for kexec_file
This patch creates kexec_efi_ops to load EFI binary file for kexec_file_load() syscall. The efi_kexec_load() as two parts: - the first part loads the kernel image (vmlinuz.efi or vmlinux.efi) - the second part loads other segments (e.g: initrd, cmdline, etc) Currently, pez (vmlinuz.efi) and pei (vmlinux.efi) format images are supported. The basic usage (vmlinuz.efi or vmlinux.efi): 1) Load second kernel image: # kexec -s -l vmlinuz.efi --initrd=initrd.img --reuse-cmdline 2) Startup second kernel: # kexec -e Signed-off-by: Youling Tang <tangyouling@kylinos.cn> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
1 parent d162fee commit 55d990f

File tree

5 files changed

+130
-1
lines changed

5 files changed

+130
-1
lines changed

arch/loongarch/include/asm/image.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,20 @@ struct loongarch_image_header {
3333
uint32_t pe_header;
3434
};
3535

36+
/*
37+
* loongarch_header_check_dos_sig - Helper to check the header
38+
*
39+
* Returns true (non-zero) if 'MZ' signature is found.
40+
*/
41+
42+
static inline int loongarch_header_check_dos_sig(const struct loongarch_image_header *h)
43+
{
44+
if (!h)
45+
return 0;
46+
47+
return (h->dos_sig[0] == 'M' && h->dos_sig[1] == 'Z');
48+
}
49+
3650
#endif /* __ASSEMBLER__ */
3751

3852
#endif /* __ASM_IMAGE_H */

arch/loongarch/include/asm/kexec.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ struct kimage_arch {
4242
};
4343

4444
#ifdef CONFIG_KEXEC_FILE
45+
extern const struct kexec_file_ops kexec_efi_ops;
4546

4647
int arch_kimage_file_post_load_cleanup(struct kimage *image);
4748
#define arch_kimage_file_post_load_cleanup arch_kimage_file_post_load_cleanup

arch/loongarch/kernel/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o
6262
obj-$(CONFIG_RELOCATABLE) += relocate.o
6363

6464
obj-$(CONFIG_KEXEC_CORE) += machine_kexec.o relocate_kernel.o
65-
obj-$(CONFIG_KEXEC_FILE) += machine_kexec_file.o
65+
obj-$(CONFIG_KEXEC_FILE) += machine_kexec_file.o kexec_efi.o
6666
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
6767

6868
obj-$(CONFIG_UNWINDER_GUESS) += unwind_guess.o

arch/loongarch/kernel/kexec_efi.c

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Load EFI vmlinux file for the kexec_file_load syscall.
4+
*
5+
* Author: Youling Tang <tangyouling@kylinos.cn>
6+
* Copyright (C) 2025 KylinSoft Corporation.
7+
*/
8+
9+
#define pr_fmt(fmt) "kexec_file(EFI): " fmt
10+
11+
#include <linux/err.h>
12+
#include <linux/errno.h>
13+
#include <linux/kernel.h>
14+
#include <linux/kexec.h>
15+
#include <linux/pe.h>
16+
#include <linux/string.h>
17+
#include <asm/byteorder.h>
18+
#include <asm/cpufeature.h>
19+
#include <asm/image.h>
20+
21+
static int efi_kexec_probe(const char *kernel_buf, unsigned long kernel_len)
22+
{
23+
const struct loongarch_image_header *h = (const struct loongarch_image_header *)kernel_buf;
24+
25+
if (!h || (kernel_len < sizeof(*h))) {
26+
kexec_dprintk("No LoongArch image header.\n");
27+
return -EINVAL;
28+
}
29+
30+
if (!loongarch_header_check_dos_sig(h)) {
31+
kexec_dprintk("No LoongArch PE image header.\n");
32+
return -EINVAL;
33+
}
34+
35+
return 0;
36+
}
37+
38+
static void *efi_kexec_load(struct kimage *image,
39+
char *kernel, unsigned long kernel_len,
40+
char *initrd, unsigned long initrd_len,
41+
char *cmdline, unsigned long cmdline_len)
42+
{
43+
int ret;
44+
unsigned long text_offset, kernel_segment_number;
45+
struct kexec_buf kbuf;
46+
struct kexec_segment *kernel_segment;
47+
struct loongarch_image_header *h;
48+
49+
h = (struct loongarch_image_header *)kernel;
50+
if (!h->kernel_asize)
51+
return ERR_PTR(-EINVAL);
52+
53+
/*
54+
* Load the kernel
55+
* FIXME: Non-relocatable kernel rejected for kexec_file (require CONFIG_RELOCATABLE)
56+
*/
57+
kbuf.image = image;
58+
kbuf.buf_max = ULONG_MAX;
59+
kbuf.top_down = false;
60+
61+
kbuf.buffer = kernel;
62+
kbuf.bufsz = kernel_len;
63+
kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
64+
kbuf.memsz = le64_to_cpu(h->kernel_asize);
65+
text_offset = le64_to_cpu(h->text_offset);
66+
kbuf.buf_min = text_offset;
67+
kbuf.buf_align = SZ_2M;
68+
69+
kernel_segment_number = image->nr_segments;
70+
71+
/*
72+
* The location of the kernel segment may make it impossible to
73+
* satisfy the other segment requirements, so we try repeatedly
74+
* to find a location that will work.
75+
*/
76+
while ((ret = kexec_add_buffer(&kbuf)) == 0) {
77+
/* Try to load additional data */
78+
kernel_segment = &image->segment[kernel_segment_number];
79+
ret = load_other_segments(image, kernel_segment->mem,
80+
kernel_segment->memsz, initrd,
81+
initrd_len, cmdline, cmdline_len);
82+
if (!ret)
83+
break;
84+
85+
/*
86+
* We couldn't find space for the other segments; erase the
87+
* kernel segment and try the next available hole.
88+
*/
89+
image->nr_segments -= 1;
90+
kbuf.buf_min = kernel_segment->mem + kernel_segment->memsz;
91+
kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
92+
}
93+
94+
if (ret < 0) {
95+
pr_err("Could not find any suitable kernel location!");
96+
return ERR_PTR(ret);
97+
}
98+
99+
kernel_segment = &image->segment[kernel_segment_number];
100+
101+
/* Make sure the second kernel jumps to the correct "kernel_entry" */
102+
image->start = kernel_segment->mem + h->kernel_entry - text_offset;
103+
104+
kexec_dprintk("Loaded kernel at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
105+
kernel_segment->mem, kbuf.bufsz, kernel_segment->memsz);
106+
107+
return NULL;
108+
}
109+
110+
const struct kexec_file_ops kexec_efi_ops = {
111+
.probe = efi_kexec_probe,
112+
.load = efi_kexec_load,
113+
};

arch/loongarch/kernel/machine_kexec_file.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <asm/bootinfo.h>
2222

2323
const struct kexec_file_ops * const kexec_file_loaders[] = {
24+
&kexec_efi_ops,
2425
NULL
2526
};
2627

0 commit comments

Comments
 (0)