mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 03:44:45 +01:00
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>
This commit is contained in:
parent
55d990f008
commit
fc9c112f80
5 changed files with 109 additions and 1 deletions
|
|
@ -639,6 +639,7 @@ config ARCH_SUPPORTS_KEXEC_FILE
|
|||
config ARCH_SELECTS_KEXEC_FILE
|
||||
def_bool 64BIT
|
||||
depends on KEXEC_FILE
|
||||
select KEXEC_ELF
|
||||
select RELOCATABLE
|
||||
select HAVE_IMA_KEXEC if IMA
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ struct kimage_arch {
|
|||
|
||||
#ifdef CONFIG_KEXEC_FILE
|
||||
extern const struct kexec_file_ops kexec_efi_ops;
|
||||
extern const struct kexec_file_ops kexec_elf_ops;
|
||||
|
||||
int arch_kimage_file_post_load_cleanup(struct kimage *image);
|
||||
#define arch_kimage_file_post_load_cleanup arch_kimage_file_post_load_cleanup
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o
|
|||
obj-$(CONFIG_RELOCATABLE) += relocate.o
|
||||
|
||||
obj-$(CONFIG_KEXEC_CORE) += machine_kexec.o relocate_kernel.o
|
||||
obj-$(CONFIG_KEXEC_FILE) += machine_kexec_file.o kexec_efi.o
|
||||
obj-$(CONFIG_KEXEC_FILE) += machine_kexec_file.o kexec_efi.o kexec_elf.o
|
||||
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
|
||||
|
||||
obj-$(CONFIG_UNWINDER_GUESS) += unwind_guess.o
|
||||
|
|
|
|||
105
arch/loongarch/kernel/kexec_elf.c
Normal file
105
arch/loongarch/kernel/kexec_elf.c
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Load ELF vmlinux file for the kexec_file_load syscall.
|
||||
*
|
||||
* Author: Youling Tang <tangyouling@kylinos.cn>
|
||||
* Copyright (C) 2025 KylinSoft Corporation.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "kexec_file(ELF): " fmt
|
||||
|
||||
#include <linux/elf.h>
|
||||
#include <linux/kexec.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <asm/setup.h>
|
||||
|
||||
#define elf_kexec_probe kexec_elf_probe
|
||||
|
||||
static int _elf_kexec_load(struct kimage *image,
|
||||
struct elfhdr *ehdr, struct kexec_elf_info *elf_info,
|
||||
struct kexec_buf *kbuf, unsigned long *text_offset)
|
||||
{
|
||||
int i, ret = -1;
|
||||
|
||||
/* Read in the PT_LOAD segments. */
|
||||
for (i = 0; i < ehdr->e_phnum; i++) {
|
||||
size_t size;
|
||||
const struct elf_phdr *phdr;
|
||||
|
||||
phdr = &elf_info->proghdrs[i];
|
||||
if (phdr->p_type != PT_LOAD)
|
||||
continue;
|
||||
|
||||
size = phdr->p_filesz;
|
||||
if (size > phdr->p_memsz)
|
||||
size = phdr->p_memsz;
|
||||
|
||||
kbuf->buffer = (void *)elf_info->buffer + phdr->p_offset;
|
||||
kbuf->bufsz = size;
|
||||
kbuf->buf_align = phdr->p_align;
|
||||
*text_offset = __pa(phdr->p_paddr);
|
||||
kbuf->buf_min = *text_offset;
|
||||
kbuf->memsz = ALIGN(phdr->p_memsz, SZ_64K);
|
||||
kbuf->mem = KEXEC_BUF_MEM_UNKNOWN;
|
||||
ret = kexec_add_buffer(kbuf);
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *elf_kexec_load(struct kimage *image,
|
||||
char *kernel, unsigned long kernel_len,
|
||||
char *initrd, unsigned long initrd_len,
|
||||
char *cmdline, unsigned long cmdline_len)
|
||||
{
|
||||
int ret;
|
||||
unsigned long text_offset, kernel_segment_number;
|
||||
struct elfhdr ehdr;
|
||||
struct kexec_buf kbuf;
|
||||
struct kexec_elf_info elf_info;
|
||||
struct kexec_segment *kernel_segment;
|
||||
|
||||
ret = kexec_build_elf_info(kernel, kernel_len, &ehdr, &elf_info);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
/*
|
||||
* Load the kernel
|
||||
* FIXME: Non-relocatable kernel rejected for kexec_file (require CONFIG_RELOCATABLE)
|
||||
*/
|
||||
kbuf.image = image;
|
||||
kbuf.buf_max = ULONG_MAX;
|
||||
kbuf.top_down = false;
|
||||
|
||||
kernel_segment_number = image->nr_segments;
|
||||
|
||||
ret = _elf_kexec_load(image, &ehdr, &elf_info, &kbuf, &text_offset);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/* Load additional data */
|
||||
kernel_segment = &image->segment[kernel_segment_number];
|
||||
ret = load_other_segments(image, kernel_segment->mem, kernel_segment->memsz,
|
||||
initrd, initrd_len, cmdline, cmdline_len);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/* Make sure the second kernel jumps to the correct "kernel_entry". */
|
||||
image->start = kernel_segment->mem + __pa(ehdr.e_entry) - text_offset;
|
||||
|
||||
kexec_dprintk("Loaded kernel at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
|
||||
kernel_segment->mem, kbuf.bufsz, kernel_segment->memsz);
|
||||
|
||||
out:
|
||||
kexec_free_elf_info(&elf_info);
|
||||
return ret ? ERR_PTR(ret) : NULL;
|
||||
}
|
||||
|
||||
const struct kexec_file_ops kexec_elf_ops = {
|
||||
.probe = elf_kexec_probe,
|
||||
.load = elf_kexec_load,
|
||||
};
|
||||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
const struct kexec_file_ops * const kexec_file_loaders[] = {
|
||||
&kexec_efi_ops,
|
||||
&kexec_elf_ops,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue