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.top_down = false; 99 ret = arch_kexec_locate_mem_hole(&kbuf); 100 if (!ret) { 101 *old_pbase = lowest_paddr; 102 *new_pbase = kbuf.mem; 103 image->start = ehdr->e_entry - lowest_vaddr + kbuf.mem; 104 } 105 return ret; 106 } 107 108 static void *elf_kexec_load(struct kimage *image, char *kernel_buf, 109 unsigned long kernel_len, char *initrd, 110 unsigned long initrd_len, char *cmdline, 111 unsigned long cmdline_len) 112 { 113 int ret; 114 unsigned long old_kernel_pbase = ULONG_MAX; 115 unsigned long new_kernel_pbase = 0UL; 116 struct elfhdr ehdr; 117 struct kexec_elf_info elf_info; 118 119 ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info); 120 if (ret) 121 return ERR_PTR(ret); 122 123 ret = elf_find_pbase(image, kernel_len, &ehdr, &elf_info, 124 &old_kernel_pbase, &new_kernel_pbase); 125 if (ret) 126 goto out; 127 128 /* Add the kernel binary to the image */ 129 ret = riscv_kexec_elf_load(image, &ehdr, &elf_info, 130 old_kernel_pbase, new_kernel_pbase); 131 if (ret) 132 goto out; 133 134 ret = load_extra_segments(image, image->start, kernel_len, 135 initrd, initrd_len, cmdline, cmdline_len); 136 out: 137 kexec_free_elf_info(&elf_info); 138 return ret ? ERR_PTR(ret) : NULL; 139 } 140 141 const struct kexec_file_ops elf_kexec_ops = { 142 .probe = kexec_elf_probe, 143 .load = elf_kexec_load, 144 }; 145