Skip to content

Commit fc9c112

Browse files
tangyoulingchenhuacai
authored andcommitted
LoongArch: Add ELF binary support for kexec_file
This patch creates kexec_elf_ops to load ELF binary file for kexec_file_load() syscall. However, for `kbuf->memsz` and `kbuf->buf_min`, special handling is required, and the generic `kexec_elf_load()` cannot be used directly. $ readelf -l vmlinux ... Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align LOAD 0x0000000000010000 0x9000000000200000 0x9000000000200000 0x0000000002747a00 0x000000000287a0d8 RWE 0x10000 NOTE 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 R 0x8 phdr->p_paddr should have been a physical address, but it is a virtual address on the current LoongArch. This will cause kexec_file to fail when loading the kernel and need to be converted to a physical address. From the above MemSiz, it can be seen that 0x287a0d8 isn't page aligned. Although kexec_add_buffer() will perform PAGE_SIZE alignment on kbuf-> memsz, there is still a stampeding in the loaded kernel space and initrd space. The initrd resolution failed when starting the second kernel. It can be known from the link script vmlinux.lds.S that, BSS_SECTION(0, SZ_64K, 8) . = ALIGN(PECOFF_SEGMENT_ALIGN); It needs to be aligned according to SZ_64K, so that after alignment, its size is consistent with _kernel_asize. The basic usage (vmlinux): 1) Load second kernel image: # kexec -s -l vmlinux --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 55d990f commit fc9c112

File tree

5 files changed

+109
-1
lines changed

5 files changed

+109
-1
lines changed

arch/loongarch/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,7 @@ config ARCH_SUPPORTS_KEXEC_FILE
639639
config ARCH_SELECTS_KEXEC_FILE
640640
def_bool 64BIT
641641
depends on KEXEC_FILE
642+
select KEXEC_ELF
642643
select RELOCATABLE
643644
select HAVE_IMA_KEXEC if IMA
644645

arch/loongarch/include/asm/kexec.h

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

4444
#ifdef CONFIG_KEXEC_FILE
4545
extern const struct kexec_file_ops kexec_efi_ops;
46+
extern const struct kexec_file_ops kexec_elf_ops;
4647

4748
int arch_kimage_file_post_load_cleanup(struct kimage *image);
4849
#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 kexec_efi.o
65+
obj-$(CONFIG_KEXEC_FILE) += machine_kexec_file.o kexec_efi.o kexec_elf.o
6666
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
6767

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

arch/loongarch/kernel/kexec_elf.c

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Load ELF 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(ELF): " fmt
10+
11+
#include <linux/elf.h>
12+
#include <linux/kexec.h>
13+
#include <linux/slab.h>
14+
#include <linux/types.h>
15+
#include <linux/memblock.h>
16+
#include <asm/setup.h>
17+
18+
#define elf_kexec_probe kexec_elf_probe
19+
20+
static int _elf_kexec_load(struct kimage *image,
21+
struct elfhdr *ehdr, struct kexec_elf_info *elf_info,
22+
struct kexec_buf *kbuf, unsigned long *text_offset)
23+
{
24+
int i, ret = -1;
25+
26+
/* Read in the PT_LOAD segments. */
27+
for (i = 0; i < ehdr->e_phnum; i++) {
28+
size_t size;
29+
const struct elf_phdr *phdr;
30+
31+
phdr = &elf_info->proghdrs[i];
32+
if (phdr->p_type != PT_LOAD)
33+
continue;
34+
35+
size = phdr->p_filesz;
36+
if (size > phdr->p_memsz)
37+
size = phdr->p_memsz;
38+
39+
kbuf->buffer = (void *)elf_info->buffer + phdr->p_offset;
40+
kbuf->bufsz = size;
41+
kbuf->buf_align = phdr->p_align;
42+
*text_offset = __pa(phdr->p_paddr);
43+
kbuf->buf_min = *text_offset;
44+
kbuf->memsz = ALIGN(phdr->p_memsz, SZ_64K);
45+
kbuf->mem = KEXEC_BUF_MEM_UNKNOWN;
46+
ret = kexec_add_buffer(kbuf);
47+
if (ret < 0)
48+
break;
49+
}
50+
51+
return ret;
52+
}
53+
54+
static void *elf_kexec_load(struct kimage *image,
55+
char *kernel, unsigned long kernel_len,
56+
char *initrd, unsigned long initrd_len,
57+
char *cmdline, unsigned long cmdline_len)
58+
{
59+
int ret;
60+
unsigned long text_offset, kernel_segment_number;
61+
struct elfhdr ehdr;
62+
struct kexec_buf kbuf;
63+
struct kexec_elf_info elf_info;
64+
struct kexec_segment *kernel_segment;
65+
66+
ret = kexec_build_elf_info(kernel, kernel_len, &ehdr, &elf_info);
67+
if (ret < 0)
68+
return ERR_PTR(ret);
69+
70+
/*
71+
* Load the kernel
72+
* FIXME: Non-relocatable kernel rejected for kexec_file (require CONFIG_RELOCATABLE)
73+
*/
74+
kbuf.image = image;
75+
kbuf.buf_max = ULONG_MAX;
76+
kbuf.top_down = false;
77+
78+
kernel_segment_number = image->nr_segments;
79+
80+
ret = _elf_kexec_load(image, &ehdr, &elf_info, &kbuf, &text_offset);
81+
if (ret < 0)
82+
goto out;
83+
84+
/* Load additional data */
85+
kernel_segment = &image->segment[kernel_segment_number];
86+
ret = load_other_segments(image, kernel_segment->mem, kernel_segment->memsz,
87+
initrd, initrd_len, cmdline, cmdline_len);
88+
if (ret < 0)
89+
goto out;
90+
91+
/* Make sure the second kernel jumps to the correct "kernel_entry". */
92+
image->start = kernel_segment->mem + __pa(ehdr.e_entry) - text_offset;
93+
94+
kexec_dprintk("Loaded kernel at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
95+
kernel_segment->mem, kbuf.bufsz, kernel_segment->memsz);
96+
97+
out:
98+
kexec_free_elf_info(&elf_info);
99+
return ret ? ERR_PTR(ret) : NULL;
100+
}
101+
102+
const struct kexec_file_ops kexec_elf_ops = {
103+
.probe = elf_kexec_probe,
104+
.load = elf_kexec_load,
105+
};

arch/loongarch/kernel/machine_kexec_file.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
const struct kexec_file_ops * const kexec_file_loaders[] = {
2424
&kexec_efi_ops,
25+
&kexec_elf_ops,
2526
NULL
2627
};
2728

0 commit comments

Comments
 (0)