13ddd9992SAKASHI Takahiro // SPDX-License-Identifier: GPL-2.0
23ddd9992SAKASHI Takahiro /*
33ddd9992SAKASHI Takahiro  * kexec_file for arm64
43ddd9992SAKASHI Takahiro  *
53ddd9992SAKASHI Takahiro  * Copyright (C) 2018 Linaro Limited
63ddd9992SAKASHI Takahiro  * Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
73ddd9992SAKASHI Takahiro  *
852b2a8afSAKASHI Takahiro  * Most code is derived from arm64 port of kexec-tools
93ddd9992SAKASHI Takahiro  */
103ddd9992SAKASHI Takahiro 
113ddd9992SAKASHI Takahiro #define pr_fmt(fmt) "kexec_file: " fmt
123ddd9992SAKASHI Takahiro 
1352b2a8afSAKASHI Takahiro #include <linux/ioport.h>
1452b2a8afSAKASHI Takahiro #include <linux/kernel.h>
153ddd9992SAKASHI Takahiro #include <linux/kexec.h>
1652b2a8afSAKASHI Takahiro #include <linux/libfdt.h>
1752b2a8afSAKASHI Takahiro #include <linux/memblock.h>
1852b2a8afSAKASHI Takahiro #include <linux/of_fdt.h>
19884143f6SAKASHI Takahiro #include <linux/random.h>
2052b2a8afSAKASHI Takahiro #include <linux/string.h>
2152b2a8afSAKASHI Takahiro #include <linux/types.h>
22732291c4SArnd Bergmann #include <linux/vmalloc.h>
2352b2a8afSAKASHI Takahiro #include <asm/byteorder.h>
2452b2a8afSAKASHI Takahiro 
2552b2a8afSAKASHI Takahiro /* relevant device tree properties */
26121ca8e5SWill Deacon #define FDT_PROP_INITRD_START	"linux,initrd-start"
27121ca8e5SWill Deacon #define FDT_PROP_INITRD_END	"linux,initrd-end"
28121ca8e5SWill Deacon #define FDT_PROP_BOOTARGS	"bootargs"
29121ca8e5SWill Deacon #define FDT_PROP_KASLR_SEED	"kaslr-seed"
303ddd9992SAKASHI Takahiro 
313ddd9992SAKASHI Takahiro const struct kexec_file_ops * const kexec_file_loaders[] = {
32f3b70e50SAKASHI Takahiro 	&kexec_image_ops,
333ddd9992SAKASHI Takahiro 	NULL
343ddd9992SAKASHI Takahiro };
3552b2a8afSAKASHI Takahiro 
3652b2a8afSAKASHI Takahiro int arch_kimage_file_post_load_cleanup(struct kimage *image)
3752b2a8afSAKASHI Takahiro {
3852b2a8afSAKASHI Takahiro 	vfree(image->arch.dtb);
3952b2a8afSAKASHI Takahiro 	image->arch.dtb = NULL;
4052b2a8afSAKASHI Takahiro 
4152b2a8afSAKASHI Takahiro 	return kexec_image_post_load_cleanup_default(image);
4252b2a8afSAKASHI Takahiro }
4352b2a8afSAKASHI Takahiro 
4452b2a8afSAKASHI Takahiro static int setup_dtb(struct kimage *image,
4552b2a8afSAKASHI Takahiro 		     unsigned long initrd_load_addr, unsigned long initrd_len,
4652b2a8afSAKASHI Takahiro 		     char *cmdline, void *dtb)
4752b2a8afSAKASHI Takahiro {
48121ca8e5SWill Deacon 	int off, ret;
4952b2a8afSAKASHI Takahiro 
50121ca8e5SWill Deacon 	ret = fdt_path_offset(dtb, "/chosen");
51121ca8e5SWill Deacon 	if (ret < 0)
52121ca8e5SWill Deacon 		goto out;
53121ca8e5SWill Deacon 
54121ca8e5SWill Deacon 	off = ret;
5552b2a8afSAKASHI Takahiro 
5652b2a8afSAKASHI Takahiro 	/* add bootargs */
5752b2a8afSAKASHI Takahiro 	if (cmdline) {
58121ca8e5SWill Deacon 		ret = fdt_setprop_string(dtb, off, FDT_PROP_BOOTARGS, cmdline);
5952b2a8afSAKASHI Takahiro 		if (ret)
60121ca8e5SWill Deacon 			goto out;
6152b2a8afSAKASHI Takahiro 	} else {
62121ca8e5SWill Deacon 		ret = fdt_delprop(dtb, off, FDT_PROP_BOOTARGS);
6352b2a8afSAKASHI Takahiro 		if (ret && (ret != -FDT_ERR_NOTFOUND))
64121ca8e5SWill Deacon 			goto out;
6552b2a8afSAKASHI Takahiro 	}
6652b2a8afSAKASHI Takahiro 
6752b2a8afSAKASHI Takahiro 	/* add initrd-* */
6852b2a8afSAKASHI Takahiro 	if (initrd_load_addr) {
69121ca8e5SWill Deacon 		ret = fdt_setprop_u64(dtb, off, FDT_PROP_INITRD_START,
7052b2a8afSAKASHI Takahiro 				      initrd_load_addr);
7152b2a8afSAKASHI Takahiro 		if (ret)
72121ca8e5SWill Deacon 			goto out;
7352b2a8afSAKASHI Takahiro 
74121ca8e5SWill Deacon 		ret = fdt_setprop_u64(dtb, off, FDT_PROP_INITRD_END,
7552b2a8afSAKASHI Takahiro 				      initrd_load_addr + initrd_len);
7652b2a8afSAKASHI Takahiro 		if (ret)
77121ca8e5SWill Deacon 			goto out;
7852b2a8afSAKASHI Takahiro 	} else {
79121ca8e5SWill Deacon 		ret = fdt_delprop(dtb, off, FDT_PROP_INITRD_START);
8052b2a8afSAKASHI Takahiro 		if (ret && (ret != -FDT_ERR_NOTFOUND))
81121ca8e5SWill Deacon 			goto out;
8252b2a8afSAKASHI Takahiro 
83121ca8e5SWill Deacon 		ret = fdt_delprop(dtb, off, FDT_PROP_INITRD_END);
8452b2a8afSAKASHI Takahiro 		if (ret && (ret != -FDT_ERR_NOTFOUND))
85121ca8e5SWill Deacon 			goto out;
8652b2a8afSAKASHI Takahiro 	}
8752b2a8afSAKASHI Takahiro 
88884143f6SAKASHI Takahiro 	/* add kaslr-seed */
89121ca8e5SWill Deacon 	ret = fdt_delprop(dtb, off, FDT_PROP_KASLR_SEED);
90884143f6SAKASHI Takahiro 	if (ret && (ret != -FDT_ERR_NOTFOUND))
91121ca8e5SWill Deacon 		goto out;
92884143f6SAKASHI Takahiro 
93884143f6SAKASHI Takahiro 	if (rng_is_initialized()) {
94121ca8e5SWill Deacon 		u64 seed = get_random_u64();
95121ca8e5SWill Deacon 		ret = fdt_setprop_u64(dtb, off, FDT_PROP_KASLR_SEED, seed);
96884143f6SAKASHI Takahiro 		if (ret)
97121ca8e5SWill Deacon 			goto out;
98884143f6SAKASHI Takahiro 	} else {
99884143f6SAKASHI Takahiro 		pr_notice("RNG is not initialised: omitting \"%s\" property\n",
100121ca8e5SWill Deacon 				FDT_PROP_KASLR_SEED);
101884143f6SAKASHI Takahiro 	}
102884143f6SAKASHI Takahiro 
103121ca8e5SWill Deacon out:
104121ca8e5SWill Deacon 	if (ret)
105121ca8e5SWill Deacon 		return (ret == -FDT_ERR_NOSPACE) ? -ENOMEM : -EINVAL;
106121ca8e5SWill Deacon 
10752b2a8afSAKASHI Takahiro 	return 0;
10852b2a8afSAKASHI Takahiro }
10952b2a8afSAKASHI Takahiro 
11052b2a8afSAKASHI Takahiro /*
111884143f6SAKASHI Takahiro  * More space needed so that we can add initrd, bootargs and kaslr-seed.
11252b2a8afSAKASHI Takahiro  */
11352b2a8afSAKASHI Takahiro #define DTB_EXTRA_SPACE 0x1000
11452b2a8afSAKASHI Takahiro 
11552b2a8afSAKASHI Takahiro static int create_dtb(struct kimage *image,
11652b2a8afSAKASHI Takahiro 		      unsigned long initrd_load_addr, unsigned long initrd_len,
11752b2a8afSAKASHI Takahiro 		      char *cmdline, void **dtb)
11852b2a8afSAKASHI Takahiro {
11952b2a8afSAKASHI Takahiro 	void *buf;
12052b2a8afSAKASHI Takahiro 	size_t buf_size;
12152b2a8afSAKASHI Takahiro 	int ret;
12252b2a8afSAKASHI Takahiro 
12352b2a8afSAKASHI Takahiro 	buf_size = fdt_totalsize(initial_boot_params)
12452b2a8afSAKASHI Takahiro 			+ strlen(cmdline) + DTB_EXTRA_SPACE;
12552b2a8afSAKASHI Takahiro 
12652b2a8afSAKASHI Takahiro 	for (;;) {
12752b2a8afSAKASHI Takahiro 		buf = vmalloc(buf_size);
12852b2a8afSAKASHI Takahiro 		if (!buf)
12952b2a8afSAKASHI Takahiro 			return -ENOMEM;
13052b2a8afSAKASHI Takahiro 
13152b2a8afSAKASHI Takahiro 		/* duplicate a device tree blob */
13252b2a8afSAKASHI Takahiro 		ret = fdt_open_into(initial_boot_params, buf, buf_size);
13352b2a8afSAKASHI Takahiro 		if (ret)
13452b2a8afSAKASHI Takahiro 			return -EINVAL;
13552b2a8afSAKASHI Takahiro 
13652b2a8afSAKASHI Takahiro 		ret = setup_dtb(image, initrd_load_addr, initrd_len,
13752b2a8afSAKASHI Takahiro 				cmdline, buf);
13852b2a8afSAKASHI Takahiro 		if (ret) {
13952b2a8afSAKASHI Takahiro 			vfree(buf);
14052b2a8afSAKASHI Takahiro 			if (ret == -ENOMEM) {
14152b2a8afSAKASHI Takahiro 				/* unlikely, but just in case */
14252b2a8afSAKASHI Takahiro 				buf_size += DTB_EXTRA_SPACE;
14352b2a8afSAKASHI Takahiro 				continue;
14452b2a8afSAKASHI Takahiro 			} else {
14552b2a8afSAKASHI Takahiro 				return ret;
14652b2a8afSAKASHI Takahiro 			}
14752b2a8afSAKASHI Takahiro 		}
14852b2a8afSAKASHI Takahiro 
14952b2a8afSAKASHI Takahiro 		/* trim it */
15052b2a8afSAKASHI Takahiro 		fdt_pack(buf);
15152b2a8afSAKASHI Takahiro 		*dtb = buf;
15252b2a8afSAKASHI Takahiro 
15352b2a8afSAKASHI Takahiro 		return 0;
15452b2a8afSAKASHI Takahiro 	}
15552b2a8afSAKASHI Takahiro }
15652b2a8afSAKASHI Takahiro 
15752b2a8afSAKASHI Takahiro int load_other_segments(struct kimage *image,
15852b2a8afSAKASHI Takahiro 			unsigned long kernel_load_addr,
15952b2a8afSAKASHI Takahiro 			unsigned long kernel_size,
16052b2a8afSAKASHI Takahiro 			char *initrd, unsigned long initrd_len,
16152b2a8afSAKASHI Takahiro 			char *cmdline)
16252b2a8afSAKASHI Takahiro {
16352b2a8afSAKASHI Takahiro 	struct kexec_buf kbuf;
16452b2a8afSAKASHI Takahiro 	void *dtb = NULL;
16552b2a8afSAKASHI Takahiro 	unsigned long initrd_load_addr = 0, dtb_len;
16652b2a8afSAKASHI Takahiro 	int ret = 0;
16752b2a8afSAKASHI Takahiro 
16852b2a8afSAKASHI Takahiro 	kbuf.image = image;
16952b2a8afSAKASHI Takahiro 	/* not allocate anything below the kernel */
17052b2a8afSAKASHI Takahiro 	kbuf.buf_min = kernel_load_addr + kernel_size;
17152b2a8afSAKASHI Takahiro 
17252b2a8afSAKASHI Takahiro 	/* load initrd */
17352b2a8afSAKASHI Takahiro 	if (initrd) {
17452b2a8afSAKASHI Takahiro 		kbuf.buffer = initrd;
17552b2a8afSAKASHI Takahiro 		kbuf.bufsz = initrd_len;
17652b2a8afSAKASHI Takahiro 		kbuf.mem = 0;
17752b2a8afSAKASHI Takahiro 		kbuf.memsz = initrd_len;
17852b2a8afSAKASHI Takahiro 		kbuf.buf_align = 0;
17952b2a8afSAKASHI Takahiro 		/* within 1GB-aligned window of up to 32GB in size */
18052b2a8afSAKASHI Takahiro 		kbuf.buf_max = round_down(kernel_load_addr, SZ_1G)
18152b2a8afSAKASHI Takahiro 						+ (unsigned long)SZ_1G * 32;
18252b2a8afSAKASHI Takahiro 		kbuf.top_down = false;
18352b2a8afSAKASHI Takahiro 
18452b2a8afSAKASHI Takahiro 		ret = kexec_add_buffer(&kbuf);
18552b2a8afSAKASHI Takahiro 		if (ret)
18652b2a8afSAKASHI Takahiro 			goto out_err;
18752b2a8afSAKASHI Takahiro 		initrd_load_addr = kbuf.mem;
18852b2a8afSAKASHI Takahiro 
18952b2a8afSAKASHI Takahiro 		pr_debug("Loaded initrd at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
19052b2a8afSAKASHI Takahiro 				initrd_load_addr, initrd_len, initrd_len);
19152b2a8afSAKASHI Takahiro 	}
19252b2a8afSAKASHI Takahiro 
19352b2a8afSAKASHI Takahiro 	/* load dtb */
19452b2a8afSAKASHI Takahiro 	ret = create_dtb(image, initrd_load_addr, initrd_len, cmdline, &dtb);
19552b2a8afSAKASHI Takahiro 	if (ret) {
19652b2a8afSAKASHI Takahiro 		pr_err("Preparing for new dtb failed\n");
19752b2a8afSAKASHI Takahiro 		goto out_err;
19852b2a8afSAKASHI Takahiro 	}
19952b2a8afSAKASHI Takahiro 
20052b2a8afSAKASHI Takahiro 	dtb_len = fdt_totalsize(dtb);
20152b2a8afSAKASHI Takahiro 	kbuf.buffer = dtb;
20252b2a8afSAKASHI Takahiro 	kbuf.bufsz = dtb_len;
20352b2a8afSAKASHI Takahiro 	kbuf.mem = 0;
20452b2a8afSAKASHI Takahiro 	kbuf.memsz = dtb_len;
20552b2a8afSAKASHI Takahiro 	/* not across 2MB boundary */
20652b2a8afSAKASHI Takahiro 	kbuf.buf_align = SZ_2M;
20752b2a8afSAKASHI Takahiro 	kbuf.buf_max = ULONG_MAX;
20852b2a8afSAKASHI Takahiro 	kbuf.top_down = true;
20952b2a8afSAKASHI Takahiro 
21052b2a8afSAKASHI Takahiro 	ret = kexec_add_buffer(&kbuf);
21152b2a8afSAKASHI Takahiro 	if (ret)
21252b2a8afSAKASHI Takahiro 		goto out_err;
21352b2a8afSAKASHI Takahiro 	image->arch.dtb = dtb;
21452b2a8afSAKASHI Takahiro 	image->arch.dtb_mem = kbuf.mem;
21552b2a8afSAKASHI Takahiro 
21652b2a8afSAKASHI Takahiro 	pr_debug("Loaded dtb at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
21752b2a8afSAKASHI Takahiro 			kbuf.mem, dtb_len, dtb_len);
21852b2a8afSAKASHI Takahiro 
21952b2a8afSAKASHI Takahiro 	return 0;
22052b2a8afSAKASHI Takahiro 
22152b2a8afSAKASHI Takahiro out_err:
22252b2a8afSAKASHI Takahiro 	vfree(dtb);
22352b2a8afSAKASHI Takahiro 	return ret;
22452b2a8afSAKASHI Takahiro }
225