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