1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * kexec_file for riscv, use vmlinux as the dump-capture kernel image. 4 * 5 * Copyright (C) 2021 Huawei Technologies Co, Ltd. 6 * 7 * Author: Liao Chang (liaochang1@huawei.com) 8 */ 9 #include <linux/kexec.h> 10 #include <linux/elf.h> 11 #include <linux/slab.h> 12 #include <linux/of.h> 13 #include <linux/libfdt.h> 14 #include <linux/types.h> 15 #include <linux/memblock.h> 16 #include <linux/pfn.h> 17 #include <linux/vmalloc.h> 18 #include <asm/setup.h> 19 #include <asm/insn.h> 20 21 const struct kexec_file_ops * const kexec_file_loaders[] = { 22 &elf_kexec_ops, 23 &image_kexec_ops, 24 NULL 25 }; 26 27 int arch_kimage_file_post_load_cleanup(struct kimage *image) 28 { 29 kvfree(image->arch.fdt); 30 image->arch.fdt = NULL; 31 32 vfree(image->elf_headers); 33 image->elf_headers = NULL; 34 image->elf_headers_sz = 0; 35 36 return kexec_image_post_load_cleanup_default(image); 37 } 38 39 #ifdef CONFIG_CRASH_DUMP 40 static int get_nr_ram_ranges_callback(struct resource *res, void *arg) 41 { 42 unsigned int *nr_ranges = arg; 43 44 (*nr_ranges)++; 45 return 0; 46 } 47 48 static int prepare_elf64_ram_headers_callback(struct resource *res, void *arg) 49 { 50 struct crash_mem *cmem = arg; 51 52 cmem->ranges[cmem->nr_ranges].start = res->start; 53 cmem->ranges[cmem->nr_ranges].end = res->end; 54 cmem->nr_ranges++; 55 56 return 0; 57 } 58 59 static int prepare_elf_headers(void **addr, unsigned long *sz) 60 { 61 struct crash_mem *cmem; 62 unsigned int nr_ranges; 63 int ret; 64 65 nr_ranges = 1; /* For exclusion of crashkernel region */ 66 walk_system_ram_res(0, -1, &nr_ranges, get_nr_ram_ranges_callback); 67 68 cmem = kmalloc_flex(*cmem, ranges, nr_ranges); 69 if (!cmem) 70 return -ENOMEM; 71 72 cmem->max_nr_ranges = nr_ranges; 73 cmem->nr_ranges = 0; 74 ret = walk_system_ram_res(0, -1, cmem, prepare_elf64_ram_headers_callback); 75 if (ret) 76 goto out; 77 78 /* Exclude crashkernel region */ 79 ret = crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end); 80 if (!ret) 81 ret = crash_prepare_elf64_headers(cmem, true, addr, sz); 82 83 out: 84 kfree(cmem); 85 return ret; 86 } 87 88 static char *setup_kdump_cmdline(struct kimage *image, char *cmdline, 89 unsigned long cmdline_len) 90 { 91 int elfcorehdr_strlen; 92 char *cmdline_ptr; 93 94 cmdline_ptr = kzalloc(COMMAND_LINE_SIZE, GFP_KERNEL); 95 if (!cmdline_ptr) 96 return NULL; 97 98 elfcorehdr_strlen = sprintf(cmdline_ptr, "elfcorehdr=0x%lx ", 99 image->elf_load_addr); 100 101 if (elfcorehdr_strlen + cmdline_len > COMMAND_LINE_SIZE) { 102 pr_err("Appending elfcorehdr=<addr> exceeds cmdline size\n"); 103 kfree(cmdline_ptr); 104 return NULL; 105 } 106 107 memcpy(cmdline_ptr + elfcorehdr_strlen, cmdline, cmdline_len); 108 /* Ensure it's nul terminated */ 109 cmdline_ptr[COMMAND_LINE_SIZE - 1] = '\0'; 110 return cmdline_ptr; 111 } 112 #endif 113 114 #define RISCV_IMM_BITS 12 115 #define RISCV_IMM_REACH (1LL << RISCV_IMM_BITS) 116 #define RISCV_CONST_HIGH_PART(x) \ 117 (((x) + (RISCV_IMM_REACH >> 1)) & ~(RISCV_IMM_REACH - 1)) 118 #define RISCV_CONST_LOW_PART(x) ((x) - RISCV_CONST_HIGH_PART(x)) 119 120 #define ENCODE_ITYPE_IMM(x) \ 121 (RV_X(x, 0, 12) << 20) 122 #define ENCODE_BTYPE_IMM(x) \ 123 ((RV_X(x, 1, 4) << 8) | (RV_X(x, 5, 6) << 25) | \ 124 (RV_X(x, 11, 1) << 7) | (RV_X(x, 12, 1) << 31)) 125 #define ENCODE_UTYPE_IMM(x) \ 126 (RV_X(x, 12, 20) << 12) 127 #define ENCODE_JTYPE_IMM(x) \ 128 ((RV_X(x, 1, 10) << 21) | (RV_X(x, 11, 1) << 20) | \ 129 (RV_X(x, 12, 8) << 12) | (RV_X(x, 20, 1) << 31)) 130 #define ENCODE_CBTYPE_IMM(x) \ 131 ((RV_X(x, 1, 2) << 3) | (RV_X(x, 3, 2) << 10) | (RV_X(x, 5, 1) << 2) | \ 132 (RV_X(x, 6, 2) << 5) | (RV_X(x, 8, 1) << 12)) 133 #define ENCODE_CJTYPE_IMM(x) \ 134 ((RV_X(x, 1, 3) << 3) | (RV_X(x, 4, 1) << 11) | (RV_X(x, 5, 1) << 2) | \ 135 (RV_X(x, 6, 1) << 7) | (RV_X(x, 7, 1) << 6) | (RV_X(x, 8, 2) << 9) | \ 136 (RV_X(x, 10, 1) << 8) | (RV_X(x, 11, 1) << 12)) 137 #define ENCODE_UJTYPE_IMM(x) \ 138 (ENCODE_UTYPE_IMM(RISCV_CONST_HIGH_PART(x)) | \ 139 (ENCODE_ITYPE_IMM(RISCV_CONST_LOW_PART(x)) << 32)) 140 #define ENCODE_UITYPE_IMM(x) \ 141 (ENCODE_UTYPE_IMM(x) | (ENCODE_ITYPE_IMM(x) << 32)) 142 143 #define CLEAN_IMM(type, x) \ 144 ((~ENCODE_##type##_IMM((uint64_t)(-1))) & (x)) 145 146 int arch_kexec_apply_relocations_add(struct purgatory_info *pi, 147 Elf_Shdr *section, 148 const Elf_Shdr *relsec, 149 const Elf_Shdr *symtab) 150 { 151 const char *strtab, *name, *shstrtab; 152 const Elf_Shdr *sechdrs; 153 Elf64_Rela *relas; 154 int i, r_type; 155 156 /* String & section header string table */ 157 sechdrs = (void *)pi->ehdr + pi->ehdr->e_shoff; 158 strtab = (char *)pi->ehdr + sechdrs[symtab->sh_link].sh_offset; 159 shstrtab = (char *)pi->ehdr + sechdrs[pi->ehdr->e_shstrndx].sh_offset; 160 161 relas = (void *)pi->ehdr + relsec->sh_offset; 162 163 for (i = 0; i < relsec->sh_size / sizeof(*relas); i++) { 164 const Elf_Sym *sym; /* symbol to relocate */ 165 unsigned long addr; /* final location after relocation */ 166 unsigned long val; /* relocated symbol value */ 167 unsigned long sec_base; /* relocated symbol value */ 168 void *loc; /* tmp location to modify */ 169 170 sym = (void *)pi->ehdr + symtab->sh_offset; 171 sym += ELF64_R_SYM(relas[i].r_info); 172 173 if (sym->st_name) 174 name = strtab + sym->st_name; 175 else 176 name = shstrtab + sechdrs[sym->st_shndx].sh_name; 177 178 loc = pi->purgatory_buf; 179 loc += section->sh_offset; 180 loc += relas[i].r_offset; 181 182 if (sym->st_shndx == SHN_ABS) 183 sec_base = 0; 184 else if (sym->st_shndx >= pi->ehdr->e_shnum) { 185 pr_err("Invalid section %d for symbol %s\n", 186 sym->st_shndx, name); 187 return -ENOEXEC; 188 } else 189 sec_base = pi->sechdrs[sym->st_shndx].sh_addr; 190 191 val = sym->st_value; 192 val += sec_base; 193 val += relas[i].r_addend; 194 195 addr = section->sh_addr + relas[i].r_offset; 196 197 r_type = ELF64_R_TYPE(relas[i].r_info); 198 199 switch (r_type) { 200 case R_RISCV_BRANCH: 201 *(u32 *)loc = CLEAN_IMM(BTYPE, *(u32 *)loc) | 202 ENCODE_BTYPE_IMM(val - addr); 203 break; 204 case R_RISCV_JAL: 205 *(u32 *)loc = CLEAN_IMM(JTYPE, *(u32 *)loc) | 206 ENCODE_JTYPE_IMM(val - addr); 207 break; 208 /* 209 * With no R_RISCV_PCREL_LO12_S, R_RISCV_PCREL_LO12_I 210 * sym is expected to be next to R_RISCV_PCREL_HI20 211 * in purgatory relsec. Handle it like R_RISCV_CALL 212 * sym, instead of searching the whole relsec. 213 */ 214 case R_RISCV_PCREL_HI20: 215 case R_RISCV_CALL_PLT: 216 case R_RISCV_CALL: 217 *(u64 *)loc = CLEAN_IMM(UITYPE, *(u64 *)loc) | 218 ENCODE_UJTYPE_IMM(val - addr); 219 break; 220 case R_RISCV_RVC_BRANCH: 221 *(u32 *)loc = CLEAN_IMM(CBTYPE, *(u32 *)loc) | 222 ENCODE_CBTYPE_IMM(val - addr); 223 break; 224 case R_RISCV_RVC_JUMP: 225 *(u32 *)loc = CLEAN_IMM(CJTYPE, *(u32 *)loc) | 226 ENCODE_CJTYPE_IMM(val - addr); 227 break; 228 case R_RISCV_ADD16: 229 *(u16 *)loc += val; 230 break; 231 case R_RISCV_SUB16: 232 *(u16 *)loc -= val; 233 break; 234 case R_RISCV_ADD32: 235 *(u32 *)loc += val; 236 break; 237 case R_RISCV_SUB32: 238 *(u32 *)loc -= val; 239 break; 240 /* It has been applied by R_RISCV_PCREL_HI20 sym */ 241 case R_RISCV_PCREL_LO12_I: 242 case R_RISCV_ALIGN: 243 case R_RISCV_RELAX: 244 break; 245 case R_RISCV_64: 246 *(u64 *)loc = val; 247 break; 248 default: 249 pr_err("Unknown rela relocation: %d\n", r_type); 250 return -ENOEXEC; 251 } 252 } 253 return 0; 254 } 255 256 257 int load_extra_segments(struct kimage *image, unsigned long kernel_start, 258 unsigned long kernel_len, char *initrd, 259 unsigned long initrd_len, char *cmdline, 260 unsigned long cmdline_len) 261 { 262 int ret; 263 void *fdt; 264 unsigned long initrd_pbase = 0UL; 265 struct kexec_buf kbuf = {}; 266 char *modified_cmdline = NULL; 267 268 kbuf.image = image; 269 kbuf.buf_min = kernel_start + kernel_len; 270 kbuf.buf_max = PFN_PHYS(max_low_pfn); 271 272 #ifdef CONFIG_CRASH_DUMP 273 /* Add elfcorehdr */ 274 if (image->type == KEXEC_TYPE_CRASH) { 275 void *headers; 276 unsigned long headers_sz; 277 ret = prepare_elf_headers(&headers, &headers_sz); 278 if (ret) { 279 pr_err("Preparing elf core header failed\n"); 280 goto out; 281 } 282 283 kbuf.buffer = headers; 284 kbuf.bufsz = headers_sz; 285 kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; 286 kbuf.memsz = headers_sz; 287 kbuf.buf_align = ELF_CORE_HEADER_ALIGN; 288 kbuf.top_down = true; 289 290 ret = kexec_add_buffer(&kbuf); 291 if (ret) { 292 vfree(headers); 293 goto out; 294 } 295 image->elf_headers = headers; 296 image->elf_load_addr = kbuf.mem; 297 image->elf_headers_sz = headers_sz; 298 299 kexec_dprintk("Loaded elf core header at 0x%lx bufsz=0x%lx memsz=0x%lx\n", 300 image->elf_load_addr, kbuf.bufsz, kbuf.memsz); 301 302 /* Setup cmdline for kdump kernel case */ 303 modified_cmdline = setup_kdump_cmdline(image, cmdline, 304 cmdline_len); 305 if (!modified_cmdline) { 306 pr_err("Setting up cmdline for kdump kernel failed\n"); 307 ret = -EINVAL; 308 goto out; 309 } 310 cmdline = modified_cmdline; 311 } 312 #endif 313 314 #ifdef CONFIG_ARCH_SUPPORTS_KEXEC_PURGATORY 315 /* Add purgatory to the image */ 316 kbuf.top_down = true; 317 kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; 318 ret = kexec_load_purgatory(image, &kbuf); 319 if (ret) { 320 pr_err("Error loading purgatory ret=%d\n", ret); 321 goto out; 322 } 323 kexec_dprintk("Loaded purgatory at 0x%lx\n", kbuf.mem); 324 325 ret = kexec_purgatory_get_set_symbol(image, "riscv_kernel_entry", 326 &kernel_start, 327 sizeof(kernel_start), 0); 328 if (ret) 329 pr_err("Error update purgatory ret=%d\n", ret); 330 #endif /* CONFIG_ARCH_SUPPORTS_KEXEC_PURGATORY */ 331 332 /* Add the initrd to the image */ 333 if (initrd != NULL) { 334 kbuf.buffer = initrd; 335 kbuf.bufsz = kbuf.memsz = initrd_len; 336 kbuf.buf_align = PAGE_SIZE; 337 kbuf.top_down = true; 338 kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; 339 ret = kexec_add_buffer(&kbuf); 340 if (ret) 341 goto out; 342 initrd_pbase = kbuf.mem; 343 kexec_dprintk("Loaded initrd at 0x%lx\n", initrd_pbase); 344 } 345 346 /* Add the DTB to the image */ 347 fdt = of_kexec_alloc_and_setup_fdt(image, initrd_pbase, 348 initrd_len, cmdline, 0); 349 if (!fdt) { 350 pr_err("Error setting up the new device tree.\n"); 351 ret = -EINVAL; 352 goto out; 353 } 354 355 fdt_pack(fdt); 356 kbuf.buffer = fdt; 357 kbuf.bufsz = kbuf.memsz = fdt_totalsize(fdt); 358 kbuf.buf_align = PAGE_SIZE; 359 kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; 360 kbuf.top_down = true; 361 ret = kexec_add_buffer(&kbuf); 362 if (ret) { 363 pr_err("Error add DTB kbuf ret=%d\n", ret); 364 goto out_free_fdt; 365 } 366 /* Cache the fdt buffer address for memory cleanup */ 367 image->arch.fdt = fdt; 368 kexec_dprintk("Loaded device tree at 0x%lx\n", kbuf.mem); 369 goto out; 370 371 out_free_fdt: 372 kvfree(fdt); 373 out: 374 kfree(modified_cmdline); 375 return ret; 376 } 377