162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Load ELF vmlinux file for the kexec_file_load syscall.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2019 Sven Schnelle <svens@stackframe.org>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/elf.h>
962306a36Sopenharmony_ci#include <linux/kexec.h>
1062306a36Sopenharmony_ci#include <linux/libfdt.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/of_fdt.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/types.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic void *elf_load(struct kimage *image, char *kernel_buf,
1762306a36Sopenharmony_ci			unsigned long kernel_len, char *initrd,
1862306a36Sopenharmony_ci			unsigned long initrd_len, char *cmdline,
1962306a36Sopenharmony_ci			unsigned long cmdline_len)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	int ret, i;
2262306a36Sopenharmony_ci	unsigned long kernel_load_addr;
2362306a36Sopenharmony_ci	struct elfhdr ehdr;
2462306a36Sopenharmony_ci	struct kexec_elf_info elf_info;
2562306a36Sopenharmony_ci	struct kexec_buf kbuf = { .image = image, .buf_min = 0,
2662306a36Sopenharmony_ci				  .buf_max = -1UL, };
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info);
2962306a36Sopenharmony_ci	if (ret)
3062306a36Sopenharmony_ci		goto out;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	ret = kexec_elf_load(image, &ehdr, &elf_info, &kbuf, &kernel_load_addr);
3362306a36Sopenharmony_ci	if (ret)
3462306a36Sopenharmony_ci		goto out;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	image->start = __pa(elf_info.ehdr->e_entry);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	for (i = 0; i < image->nr_segments; i++)
3962306a36Sopenharmony_ci		image->segment[i].mem = __pa(image->segment[i].mem);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	pr_debug("Loaded the kernel at 0x%lx, entry at 0x%lx\n",
4262306a36Sopenharmony_ci		 kernel_load_addr, image->start);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	if (initrd != NULL) {
4562306a36Sopenharmony_ci		kbuf.buffer = initrd;
4662306a36Sopenharmony_ci		kbuf.bufsz = kbuf.memsz = initrd_len;
4762306a36Sopenharmony_ci		kbuf.buf_align = PAGE_SIZE;
4862306a36Sopenharmony_ci		kbuf.top_down = false;
4962306a36Sopenharmony_ci		kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
5062306a36Sopenharmony_ci		ret = kexec_add_buffer(&kbuf);
5162306a36Sopenharmony_ci		if (ret)
5262306a36Sopenharmony_ci			goto out;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci		pr_debug("Loaded initrd at 0x%lx\n", kbuf.mem);
5562306a36Sopenharmony_ci		image->arch.initrd_start = kbuf.mem;
5662306a36Sopenharmony_ci		image->arch.initrd_end = kbuf.mem + initrd_len;
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (cmdline != NULL) {
6062306a36Sopenharmony_ci		kbuf.buffer = cmdline;
6162306a36Sopenharmony_ci		kbuf.bufsz = kbuf.memsz = ALIGN(cmdline_len, 8);
6262306a36Sopenharmony_ci		kbuf.buf_align = PAGE_SIZE;
6362306a36Sopenharmony_ci		kbuf.top_down = false;
6462306a36Sopenharmony_ci		kbuf.buf_min = PAGE0->mem_free + PAGE_SIZE;
6562306a36Sopenharmony_ci		kbuf.buf_max = kernel_load_addr;
6662306a36Sopenharmony_ci		kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
6762306a36Sopenharmony_ci		ret = kexec_add_buffer(&kbuf);
6862306a36Sopenharmony_ci		if (ret)
6962306a36Sopenharmony_ci			goto out;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci		pr_debug("Loaded cmdline at 0x%lx\n", kbuf.mem);
7262306a36Sopenharmony_ci		image->arch.cmdline = kbuf.mem;
7362306a36Sopenharmony_ci	}
7462306a36Sopenharmony_ciout:
7562306a36Sopenharmony_ci	return NULL;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ciconst struct kexec_file_ops kexec_elf_ops = {
7962306a36Sopenharmony_ci	.probe = kexec_elf_probe,
8062306a36Sopenharmony_ci	.load = elf_load,
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ciconst struct kexec_file_ops * const kexec_file_loaders[] = {
8462306a36Sopenharmony_ci	&kexec_elf_ops,
8562306a36Sopenharmony_ci	NULL
8662306a36Sopenharmony_ci};
87