1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Load ELF vmlinux file for the kexec_file_load syscall. 4 * 5 * Copyright (C) 2021 Huawei Technologies Co, Ltd. 6 * 7 * Author: Liao Chang (liaochang1@huawei.com) 8 * 9 * Based on kexec-tools' kexec-elf-riscv.c, heavily modified 10 * for kernel. 11 */ 12 13 #define pr_fmt(fmt) "kexec_image: " fmt 14 15 #include <linux/elf.h> 16 #include <linux/kexec.h> 17 #include <linux/slab.h> 18 #include <linux/of.h> 19 #include <linux/libfdt.h> 20 #include <linux/types.h> 21 #include <linux/memblock.h> 22 #include <asm/setup.h> 23 24 static int riscv_kexec_elf_load(struct kimage *image, struct elfhdr *ehdr, 25 struct kexec_elf_info *elf_info, unsigned long old_pbase, 26 unsigned long new_pbase) 27 { 28 int i; 29 int ret = 0; 30 size_t size; 31 struct kexec_buf kbuf; 32 const struct elf_phdr *phdr; 33 34 kbuf.image = image; 35 36 for (i = 0; i < ehdr->e_phnum; i++) { 37 phdr = &elf_info->proghdrs[i]; 38 if (phdr->p_type != PT_LOAD) 39 continue; 40 41 size = phdr->p_filesz; 42 if (size > phdr->p_memsz) 43 size = phdr->p_memsz; 44 45 kbuf.buffer = (void *) elf_info->buffer + phdr->p_offset; 46 kbuf.bufsz = size; 47 kbuf.buf_align = phdr->p_align; 48 kbuf.mem = phdr->p_paddr - old_pbase + new_pbase; 49 kbuf.memsz = phdr->p_memsz; 50 kbuf.top_down = false; 51 ret = kexec_add_buffer(&kbuf); 52 if (ret) 53 break; 54 } 55 56 return ret; 57 } 58 59 /* 60 * Go through the available phsyical memory regions and find one that hold 61 * an image of the specified size. 62 */ 63 static int elf_find_pbase(struct kimage *image, unsigned long kernel_len, 64 struct elfhdr *ehdr, struct kexec_elf_info *elf_info, 65 unsigned long *old_pbase, unsigned long *new_pbase) 66 { 67 int i; 68 int ret; 69 struct kexec_buf kbuf; 70 const struct elf_phdr *phdr; 71 unsigned long lowest_paddr = ULONG_MAX; 72 unsigned long lowest_vaddr = ULONG_MAX; 73 74 for (i = 0; i < ehdr->e_phnum; i++) { 75 phdr = &elf_info->proghdrs[i]; 76 if (phdr->p_type != PT_LOAD) 77 continue; 78 79 if (lowest_paddr > phdr->p_paddr) 80 lowest_paddr = phdr->p_paddr; 81 82 if (lowest_vaddr > phdr->p_vaddr) 83 lowest_vaddr = phdr->p_vaddr; 84 } 85 86 kbuf.image = image; 87 kbuf.buf_min = lowest_paddr; 88 kbuf.buf_max = ULONG_MAX; 89 90 /* 91 * Current riscv boot protocol requires 2MB alignment for 92 * RV64 and 4MB alignment for RV32 93 * 94 */ 95 kbuf.buf_align = PMD_SIZE; 96 kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; 97 kbuf.memsz = ALIGN(kernel_len, PAGE_SIZE); 98 kbuf.cma = NULL; 99 kbuf.top_down = false; 100 ret = arch_kexec_locate_mem_hole(&kbuf); 101 if (!ret) { 102 *old_pbase = lowest_paddr; 103 *new_pbase = kbuf.mem; 104 image->start = ehdr->e_entry - lowest_vaddr + kbuf.mem; 105 } 106 return ret; 107 } 108 109 static void *elf_kexec_load(struct kimage *image, char *kernel_buf, 110 unsigned long kernel_len, char *initrd, 111 unsigned long initrd_len, char *cmdline, 112 unsigned long cmdline_len) 113 { 114 int ret; 115 unsigned long old_kernel_pbase = ULONG_MAX; 116 unsigned long new_kernel_pbase = 0UL; 117 struct elfhdr ehdr; 118 struct kexec_elf_info elf_info; 119 120 ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info); 121 if (ret) 122 return ERR_PTR(ret); 123 124 ret = elf_find_pbase(image, kernel_len, &ehdr, &elf_info, 125 &old_kernel_pbase, &new_kernel_pbase); 126 if (ret) 127 goto out; 128 129 /* Add the kernel binary to the image */ 130 ret = riscv_kexec_elf_load(image, &ehdr, &elf_info, 131 old_kernel_pbase, new_kernel_pbase); 132 if (ret) 133 goto out; 134 135 ret = load_extra_segments(image, image->start, kernel_len, 136 initrd, initrd_len, cmdline, cmdline_len); 137 out: 138 kexec_free_elf_info(&elf_info); 139 return ret ? ERR_PTR(ret) : NULL; 140 } 141 142 const struct kexec_file_ops elf_kexec_ops = { 143 .probe = kexec_elf_probe, 144 .load = elf_kexec_load, 145 }; 146