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
_elf_kexec_load(struct kimage * image,struct elfhdr * ehdr,struct kexec_elf_info * elf_info,struct kexec_buf * kbuf,unsigned long * text_offset)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
elf_kexec_load(struct kimage * image,char * kernel,unsigned long kernel_len,char * initrd,unsigned long initrd_len,char * cmdline,unsigned long cmdline_len)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