File Index
Files and directories
Sign in
FILE

kexec_elf.c

File preview with syntax highlighting.

linux-6.19.14/arch/riscv/kernel/kexec_elf.c C
  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
 24static 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 */
 63static 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
109static 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);
137out:
138	kexec_free_elf_info(&elf_info);
139	return ret ? ERR_PTR(ret) : NULL;
140}
141
142const struct kexec_file_ops elf_kexec_ops = {
143	.probe = kexec_elf_probe,
144	.load  = elf_kexec_load,
145};
~/ Read-only file preview.