162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Kexec image loader
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci * Copyright (C) 2018 Linaro Limited
662306a36Sopenharmony_ci * Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#define pr_fmt(fmt)	"kexec_file(Image): " fmt
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/err.h>
1262306a36Sopenharmony_ci#include <linux/errno.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/kexec.h>
1562306a36Sopenharmony_ci#include <linux/pe.h>
1662306a36Sopenharmony_ci#include <linux/string.h>
1762306a36Sopenharmony_ci#include <asm/byteorder.h>
1862306a36Sopenharmony_ci#include <asm/cpufeature.h>
1962306a36Sopenharmony_ci#include <asm/image.h>
2062306a36Sopenharmony_ci#include <asm/memory.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic int image_probe(const char *kernel_buf, unsigned long kernel_len)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	const struct arm64_image_header *h =
2562306a36Sopenharmony_ci		(const struct arm64_image_header *)(kernel_buf);
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	if (!h || (kernel_len < sizeof(*h)))
2862306a36Sopenharmony_ci		return -EINVAL;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	if (memcmp(&h->magic, ARM64_IMAGE_MAGIC, sizeof(h->magic)))
3162306a36Sopenharmony_ci		return -EINVAL;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	return 0;
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic void *image_load(struct kimage *image,
3762306a36Sopenharmony_ci				char *kernel, unsigned long kernel_len,
3862306a36Sopenharmony_ci				char *initrd, unsigned long initrd_len,
3962306a36Sopenharmony_ci				char *cmdline, unsigned long cmdline_len)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct arm64_image_header *h;
4262306a36Sopenharmony_ci	u64 flags, value;
4362306a36Sopenharmony_ci	bool be_image, be_kernel;
4462306a36Sopenharmony_ci	struct kexec_buf kbuf;
4562306a36Sopenharmony_ci	unsigned long text_offset, kernel_segment_number;
4662306a36Sopenharmony_ci	struct kexec_segment *kernel_segment;
4762306a36Sopenharmony_ci	int ret;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	/*
5062306a36Sopenharmony_ci	 * We require a kernel with an unambiguous Image header. Per
5162306a36Sopenharmony_ci	 * Documentation/arch/arm64/booting.rst, this is the case when image_size
5262306a36Sopenharmony_ci	 * is non-zero (practically speaking, since v3.17).
5362306a36Sopenharmony_ci	 */
5462306a36Sopenharmony_ci	h = (struct arm64_image_header *)kernel;
5562306a36Sopenharmony_ci	if (!h->image_size)
5662306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	/* Check cpu features */
5962306a36Sopenharmony_ci	flags = le64_to_cpu(h->flags);
6062306a36Sopenharmony_ci	be_image = arm64_image_flag_field(flags, ARM64_IMAGE_FLAG_BE);
6162306a36Sopenharmony_ci	be_kernel = IS_ENABLED(CONFIG_CPU_BIG_ENDIAN);
6262306a36Sopenharmony_ci	if ((be_image != be_kernel) && !system_supports_mixed_endian())
6362306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	value = arm64_image_flag_field(flags, ARM64_IMAGE_FLAG_PAGE_SIZE);
6662306a36Sopenharmony_ci	if (((value == ARM64_IMAGE_FLAG_PAGE_SIZE_4K) &&
6762306a36Sopenharmony_ci			!system_supports_4kb_granule()) ||
6862306a36Sopenharmony_ci	    ((value == ARM64_IMAGE_FLAG_PAGE_SIZE_64K) &&
6962306a36Sopenharmony_ci			!system_supports_64kb_granule()) ||
7062306a36Sopenharmony_ci	    ((value == ARM64_IMAGE_FLAG_PAGE_SIZE_16K) &&
7162306a36Sopenharmony_ci			!system_supports_16kb_granule()))
7262306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	/* Load the kernel */
7562306a36Sopenharmony_ci	kbuf.image = image;
7662306a36Sopenharmony_ci	kbuf.buf_min = 0;
7762306a36Sopenharmony_ci	kbuf.buf_max = ULONG_MAX;
7862306a36Sopenharmony_ci	kbuf.top_down = false;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	kbuf.buffer = kernel;
8162306a36Sopenharmony_ci	kbuf.bufsz = kernel_len;
8262306a36Sopenharmony_ci	kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
8362306a36Sopenharmony_ci	kbuf.memsz = le64_to_cpu(h->image_size);
8462306a36Sopenharmony_ci	text_offset = le64_to_cpu(h->text_offset);
8562306a36Sopenharmony_ci	kbuf.buf_align = MIN_KIMG_ALIGN;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/* Adjust kernel segment with TEXT_OFFSET */
8862306a36Sopenharmony_ci	kbuf.memsz += text_offset;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	kernel_segment_number = image->nr_segments;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	/*
9362306a36Sopenharmony_ci	 * The location of the kernel segment may make it impossible to satisfy
9462306a36Sopenharmony_ci	 * the other segment requirements, so we try repeatedly to find a
9562306a36Sopenharmony_ci	 * location that will work.
9662306a36Sopenharmony_ci	 */
9762306a36Sopenharmony_ci	while ((ret = kexec_add_buffer(&kbuf)) == 0) {
9862306a36Sopenharmony_ci		/* Try to load additional data */
9962306a36Sopenharmony_ci		kernel_segment = &image->segment[kernel_segment_number];
10062306a36Sopenharmony_ci		ret = load_other_segments(image, kernel_segment->mem,
10162306a36Sopenharmony_ci					  kernel_segment->memsz, initrd,
10262306a36Sopenharmony_ci					  initrd_len, cmdline);
10362306a36Sopenharmony_ci		if (!ret)
10462306a36Sopenharmony_ci			break;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci		/*
10762306a36Sopenharmony_ci		 * We couldn't find space for the other segments; erase the
10862306a36Sopenharmony_ci		 * kernel segment and try the next available hole.
10962306a36Sopenharmony_ci		 */
11062306a36Sopenharmony_ci		image->nr_segments -= 1;
11162306a36Sopenharmony_ci		kbuf.buf_min = kernel_segment->mem + kernel_segment->memsz;
11262306a36Sopenharmony_ci		kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (ret) {
11662306a36Sopenharmony_ci		pr_err("Could not find any suitable kernel location!");
11762306a36Sopenharmony_ci		return ERR_PTR(ret);
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	kernel_segment = &image->segment[kernel_segment_number];
12162306a36Sopenharmony_ci	kernel_segment->mem += text_offset;
12262306a36Sopenharmony_ci	kernel_segment->memsz -= text_offset;
12362306a36Sopenharmony_ci	image->start = kernel_segment->mem;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	pr_debug("Loaded kernel at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
12662306a36Sopenharmony_ci				kernel_segment->mem, kbuf.bufsz,
12762306a36Sopenharmony_ci				kernel_segment->memsz);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	return NULL;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ciconst struct kexec_file_ops kexec_image_ops = {
13362306a36Sopenharmony_ci	.probe = image_probe,
13462306a36Sopenharmony_ci	.load = image_load,
13562306a36Sopenharmony_ci#ifdef CONFIG_KEXEC_IMAGE_VERIFY_SIG
13662306a36Sopenharmony_ci	.verify_sig = kexec_kernel_verify_pe_sig,
13762306a36Sopenharmony_ci#endif
13862306a36Sopenharmony_ci};
139