xref: /linux/arch/loongarch/kernel/machine_kexec_file.c (revision 570f7e331f5febb30f1384817463c7e42b65ca7d)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * kexec_file for LoongArch
4  *
5  * Author: Youling Tang <tangyouling@kylinos.cn>
6  * Copyright (C) 2025 KylinSoft Corporation.
7  *
8  * Most code is derived from LoongArch port of kexec-tools
9  */
10 
11 #define pr_fmt(fmt) "kexec_file: " fmt
12 
13 #include <linux/ioport.h>
14 #include <linux/kernel.h>
15 #include <linux/kexec.h>
16 #include <linux/memblock.h>
17 #include <linux/slab.h>
18 #include <linux/string.h>
19 #include <linux/types.h>
20 #include <linux/vmalloc.h>
21 #include <asm/bootinfo.h>
22 
23 const struct kexec_file_ops * const kexec_file_loaders[] = {
24 	&kexec_efi_ops,
25 	&kexec_elf_ops,
26 	NULL
27 };
28 
29 int arch_kimage_file_post_load_cleanup(struct kimage *image)
30 {
31 	vfree(image->elf_headers);
32 	image->elf_headers = NULL;
33 	image->elf_headers_sz = 0;
34 
35 	return kexec_image_post_load_cleanup_default(image);
36 }
37 
38 /* Add the "kexec_file" command line parameter to command line. */
39 static void cmdline_add_loader(unsigned long *cmdline_tmplen, char *modified_cmdline)
40 {
41 	int loader_strlen;
42 
43 	loader_strlen = sprintf(modified_cmdline + (*cmdline_tmplen), "kexec_file ");
44 	*cmdline_tmplen += loader_strlen;
45 }
46 
47 /* Add the "initrd=start,size" command line parameter to command line. */
48 static void cmdline_add_initrd(struct kimage *image, unsigned long *cmdline_tmplen,
49 				char *modified_cmdline, unsigned long initrd)
50 {
51 	int initrd_strlen;
52 
53 	initrd_strlen = sprintf(modified_cmdline + (*cmdline_tmplen), "initrd=0x%lx,0x%lx ",
54 		initrd, image->initrd_buf_len);
55 	*cmdline_tmplen += initrd_strlen;
56 }
57 
58 #ifdef CONFIG_CRASH_DUMP
59 unsigned int arch_get_system_nr_ranges(void)
60 {
61 	int nr_ranges = 2; /* for exclusion of crashkernel region */
62 	phys_addr_t start, end;
63 	uint64_t i;
64 
65 	for_each_mem_range(i, &start, &end)
66 		nr_ranges++;
67 
68 	return nr_ranges;
69 }
70 
71 int arch_crash_populate_cmem(struct crash_mem *cmem)
72 {
73 	phys_addr_t start, end;
74 	uint64_t i;
75 
76 	for_each_mem_range(i, &start, &end) {
77 		cmem->ranges[cmem->nr_ranges].start = start;
78 		cmem->ranges[cmem->nr_ranges].end = end - 1;
79 		cmem->nr_ranges++;
80 	}
81 
82 	return 0;
83 }
84 
85 /*
86  * Add the "mem=size@start" command line parameter to command line, indicating the
87  * memory region the new kernel can use to boot into.
88  */
89 static void cmdline_add_mem(unsigned long *cmdline_tmplen, char *modified_cmdline)
90 {
91 	int mem_strlen = 0;
92 
93 	mem_strlen = sprintf(modified_cmdline + (*cmdline_tmplen), "mem=0x%llx@0x%llx ",
94 		crashk_res.end - crashk_res.start + 1, crashk_res.start);
95 	*cmdline_tmplen += mem_strlen;
96 
97 	if (crashk_low_res.end) {
98 		mem_strlen = sprintf(modified_cmdline + (*cmdline_tmplen), "mem=0x%llx@0x%llx ",
99 			crashk_low_res.end - crashk_low_res.start + 1, crashk_low_res.start);
100 		*cmdline_tmplen += mem_strlen;
101 	}
102 }
103 
104 /* Add the "elfcorehdr=size@start" command line parameter to command line. */
105 static void cmdline_add_elfcorehdr(struct kimage *image, unsigned long *cmdline_tmplen,
106 				   char *modified_cmdline, unsigned long elfcorehdr_sz)
107 {
108 	int elfcorehdr_strlen = 0;
109 
110 	elfcorehdr_strlen = sprintf(modified_cmdline + (*cmdline_tmplen), "elfcorehdr=0x%lx@0x%lx ",
111 		elfcorehdr_sz, image->elf_load_addr);
112 	*cmdline_tmplen += elfcorehdr_strlen;
113 }
114 
115 #endif
116 
117 /*
118  * Try to add the initrd to the image. If it is not possible to find valid
119  * locations, this function will undo changes to the image and return non zero.
120  */
121 int load_other_segments(struct kimage *image,
122 			unsigned long kernel_load_addr, unsigned long kernel_size,
123 			char *initrd, unsigned long initrd_len, char *cmdline, unsigned long cmdline_len)
124 {
125 	int ret = 0;
126 	unsigned long cmdline_tmplen = 0;
127 	unsigned long initrd_load_addr = 0;
128 	unsigned long orig_segments = image->nr_segments;
129 	char *modified_cmdline = NULL;
130 	struct kexec_buf kbuf = {};
131 
132 	kbuf.image = image;
133 	/* Don't allocate anything below the kernel */
134 	kbuf.buf_min = kernel_load_addr + kernel_size;
135 
136 	modified_cmdline = kzalloc(COMMAND_LINE_SIZE, GFP_KERNEL);
137 	if (!modified_cmdline)
138 		return -EINVAL;
139 
140 	cmdline_add_loader(&cmdline_tmplen, modified_cmdline);
141 	/* Ensure it's null terminated */
142 	modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0';
143 
144 #ifdef CONFIG_CRASH_DUMP
145 	/* Load elf core header */
146 	if (image->type == KEXEC_TYPE_CRASH) {
147 		void *headers;
148 		unsigned long headers_sz;
149 
150 		ret = crash_prepare_headers(true, &headers, &headers_sz, NULL);
151 		if (ret < 0) {
152 			pr_err("Preparing elf core header failed\n");
153 			goto out_err;
154 		}
155 
156 		kbuf.buffer = headers;
157 		kbuf.bufsz = headers_sz;
158 		kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
159 		kbuf.memsz = headers_sz;
160 		kbuf.buf_align = SZ_64K; /* largest supported page size */
161 		kbuf.buf_max = ULONG_MAX;
162 		kbuf.top_down = true;
163 
164 		ret = kexec_add_buffer(&kbuf);
165 		if (ret < 0) {
166 			vfree(headers);
167 			goto out_err;
168 		}
169 		image->elf_headers = headers;
170 		image->elf_load_addr = kbuf.mem;
171 		image->elf_headers_sz = headers_sz;
172 
173 		kexec_dprintk("Loaded elf core header at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
174 			      image->elf_load_addr, kbuf.bufsz, kbuf.memsz);
175 
176 		/* Add the mem=size@start parameter to the command line */
177 		cmdline_add_mem(&cmdline_tmplen, modified_cmdline);
178 
179 		/* Add the elfcorehdr=size@start parameter to the command line */
180 		cmdline_add_elfcorehdr(image, &cmdline_tmplen, modified_cmdline, headers_sz);
181 	}
182 #endif
183 
184 	/* Load initrd */
185 	if (initrd) {
186 		kbuf.buffer = initrd;
187 		kbuf.bufsz = initrd_len;
188 		kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
189 		kbuf.memsz = initrd_len;
190 		kbuf.buf_align = 0;
191 		/* within 1GB-aligned window of up to 32GB in size */
192 		kbuf.buf_max = round_down(kernel_load_addr, SZ_1G) + (unsigned long)SZ_1G * 32;
193 		kbuf.top_down = false;
194 
195 		ret = kexec_add_buffer(&kbuf);
196 		if (ret < 0)
197 			goto out_err;
198 		initrd_load_addr = kbuf.mem;
199 
200 		kexec_dprintk("Loaded initrd at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
201 			      initrd_load_addr, kbuf.bufsz, kbuf.memsz);
202 
203 		/* Add the initrd=start,size parameter to the command line */
204 		cmdline_add_initrd(image, &cmdline_tmplen, modified_cmdline, initrd_load_addr);
205 	}
206 
207 	if (cmdline_len + cmdline_tmplen > COMMAND_LINE_SIZE) {
208 		pr_err("Appending command line exceeds COMMAND_LINE_SIZE\n");
209 		ret = -EINVAL;
210 		goto out_err;
211 	}
212 
213 	memcpy(modified_cmdline + cmdline_tmplen, cmdline, cmdline_len);
214 	cmdline = modified_cmdline;
215 	image->arch.cmdline_ptr = (unsigned long)cmdline;
216 
217 	return 0;
218 
219 out_err:
220 	image->nr_segments = orig_segments;
221 	kfree(modified_cmdline);
222 	return ret;
223 }
224