xref: /linux/arch/loongarch/kernel/machine_kexec_file.c (revision fc9c112f804abcd984a20a66a909332a147a23e8)
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 /*
59  * Try to add the initrd to the image. If it is not possible to find valid
60  * locations, this function will undo changes to the image and return non zero.
61  */
62 int load_other_segments(struct kimage *image,
63 			unsigned long kernel_load_addr, unsigned long kernel_size,
64 			char *initrd, unsigned long initrd_len, char *cmdline, unsigned long cmdline_len)
65 {
66 	int ret = 0;
67 	unsigned long cmdline_tmplen = 0;
68 	unsigned long initrd_load_addr = 0;
69 	unsigned long orig_segments = image->nr_segments;
70 	char *modified_cmdline = NULL;
71 	struct kexec_buf kbuf;
72 
73 	kbuf.image = image;
74 	/* Don't allocate anything below the kernel */
75 	kbuf.buf_min = kernel_load_addr + kernel_size;
76 
77 	modified_cmdline = kzalloc(COMMAND_LINE_SIZE, GFP_KERNEL);
78 	if (!modified_cmdline)
79 		return -EINVAL;
80 
81 	cmdline_add_loader(&cmdline_tmplen, modified_cmdline);
82 	/* Ensure it's null terminated */
83 	modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0';
84 
85 	/* Load initrd */
86 	if (initrd) {
87 		kbuf.buffer = initrd;
88 		kbuf.bufsz = initrd_len;
89 		kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
90 		kbuf.memsz = initrd_len;
91 		kbuf.buf_align = 0;
92 		/* within 1GB-aligned window of up to 32GB in size */
93 		kbuf.buf_max = round_down(kernel_load_addr, SZ_1G) + (unsigned long)SZ_1G * 32;
94 		kbuf.top_down = false;
95 
96 		ret = kexec_add_buffer(&kbuf);
97 		if (ret < 0)
98 			goto out_err;
99 		initrd_load_addr = kbuf.mem;
100 
101 		kexec_dprintk("Loaded initrd at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
102 			      initrd_load_addr, kbuf.bufsz, kbuf.memsz);
103 
104 		/* Add the initrd=start,size parameter to the command line */
105 		cmdline_add_initrd(image, &cmdline_tmplen, modified_cmdline, initrd_load_addr);
106 	}
107 
108 	if (cmdline_len + cmdline_tmplen > COMMAND_LINE_SIZE) {
109 		pr_err("Appending command line exceeds COMMAND_LINE_SIZE\n");
110 		ret = -EINVAL;
111 		goto out_err;
112 	}
113 
114 	memcpy(modified_cmdline + cmdline_tmplen, cmdline, cmdline_len);
115 	cmdline = modified_cmdline;
116 	image->arch.cmdline_ptr = (unsigned long)cmdline;
117 
118 	return 0;
119 
120 out_err:
121 	image->nr_segments = orig_segments;
122 	kfree(modified_cmdline);
123 	return ret;
124 }
125