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