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