xref: /linux/arch/loongarch/kernel/kexec_elf.c (revision e3966940559d52aa1800a008dcfeec218dd31f88)
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 };
106