1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Load EFI vmlinux file for the kexec_file_load syscall. 4 * 5 * Author: Youling Tang <tangyouling@kylinos.cn> 6 * Copyright (C) 2025 KylinSoft Corporation. 7 */ 8 9 #define pr_fmt(fmt) "kexec_file(EFI): " fmt 10 11 #include <linux/err.h> 12 #include <linux/errno.h> 13 #include <linux/kernel.h> 14 #include <linux/kexec.h> 15 #include <linux/pe.h> 16 #include <linux/string.h> 17 #include <asm/byteorder.h> 18 #include <asm/cpufeature.h> 19 #include <asm/image.h> 20 21 static int efi_kexec_probe(const char *kernel_buf, unsigned long kernel_len) 22 { 23 const struct loongarch_image_header *h = (const struct loongarch_image_header *)kernel_buf; 24 25 if (!h || (kernel_len < sizeof(*h))) { 26 kexec_dprintk("No LoongArch image header.\n"); 27 return -EINVAL; 28 } 29 30 if (!loongarch_header_check_dos_sig(h)) { 31 kexec_dprintk("No LoongArch PE image header.\n"); 32 return -EINVAL; 33 } 34 35 return 0; 36 } 37 38 static void *efi_kexec_load(struct kimage *image, 39 char *kernel, unsigned long kernel_len, 40 char *initrd, unsigned long initrd_len, 41 char *cmdline, unsigned long cmdline_len) 42 { 43 int ret; 44 unsigned long text_offset, kernel_segment_number; 45 struct kexec_buf kbuf; 46 struct kexec_segment *kernel_segment; 47 struct loongarch_image_header *h; 48 49 h = (struct loongarch_image_header *)kernel; 50 if (!h->kernel_asize) 51 return ERR_PTR(-EINVAL); 52 53 /* 54 * Load the kernel 55 * FIXME: Non-relocatable kernel rejected for kexec_file (require CONFIG_RELOCATABLE) 56 */ 57 kbuf.image = image; 58 kbuf.buf_max = ULONG_MAX; 59 kbuf.top_down = false; 60 61 kbuf.buffer = kernel; 62 kbuf.bufsz = kernel_len; 63 kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; 64 kbuf.memsz = le64_to_cpu(h->kernel_asize); 65 text_offset = le64_to_cpu(h->text_offset); 66 kbuf.buf_min = text_offset; 67 kbuf.buf_align = SZ_2M; 68 69 kernel_segment_number = image->nr_segments; 70 71 /* 72 * The location of the kernel segment may make it impossible to 73 * satisfy the other segment requirements, so we try repeatedly 74 * to find a location that will work. 75 */ 76 while ((ret = kexec_add_buffer(&kbuf)) == 0) { 77 /* Try to load additional data */ 78 kernel_segment = &image->segment[kernel_segment_number]; 79 ret = load_other_segments(image, kernel_segment->mem, 80 kernel_segment->memsz, initrd, 81 initrd_len, cmdline, cmdline_len); 82 if (!ret) 83 break; 84 85 /* 86 * We couldn't find space for the other segments; erase the 87 * kernel segment and try the next available hole. 88 */ 89 image->nr_segments -= 1; 90 kbuf.buf_min = kernel_segment->mem + kernel_segment->memsz; 91 kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; 92 } 93 94 if (ret < 0) { 95 pr_err("Could not find any suitable kernel location!"); 96 return ERR_PTR(ret); 97 } 98 99 kernel_segment = &image->segment[kernel_segment_number]; 100 101 /* Make sure the second kernel jumps to the correct "kernel_entry" */ 102 image->start = kernel_segment->mem + h->kernel_entry - text_offset; 103 104 kexec_dprintk("Loaded kernel at 0x%lx bufsz=0x%lx memsz=0x%lx\n", 105 kernel_segment->mem, kbuf.bufsz, kernel_segment->memsz); 106 107 return NULL; 108 } 109 110 const struct kexec_file_ops kexec_efi_ops = { 111 .probe = efi_kexec_probe, 112 .load = efi_kexec_load, 113 }; 114