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); 9027966721SAKASHI Takahiro if (ret == -FDT_ERR_NOTFOUND) 9127966721SAKASHI Takahiro ret = 0; 9227966721SAKASHI Takahiro else if (ret) 93121ca8e5SWill Deacon goto out; 94884143f6SAKASHI Takahiro 95884143f6SAKASHI Takahiro if (rng_is_initialized()) { 96121ca8e5SWill Deacon u64 seed = get_random_u64(); 97121ca8e5SWill Deacon ret = fdt_setprop_u64(dtb, off, FDT_PROP_KASLR_SEED, seed); 98884143f6SAKASHI Takahiro if (ret) 99121ca8e5SWill Deacon goto out; 100884143f6SAKASHI Takahiro } else { 101884143f6SAKASHI Takahiro pr_notice("RNG is not initialised: omitting \"%s\" property\n", 102121ca8e5SWill Deacon FDT_PROP_KASLR_SEED); 103884143f6SAKASHI Takahiro } 104884143f6SAKASHI Takahiro 105121ca8e5SWill Deacon out: 106121ca8e5SWill Deacon if (ret) 107121ca8e5SWill Deacon return (ret == -FDT_ERR_NOSPACE) ? -ENOMEM : -EINVAL; 108121ca8e5SWill Deacon 10952b2a8afSAKASHI Takahiro return 0; 11052b2a8afSAKASHI Takahiro } 11152b2a8afSAKASHI Takahiro 11252b2a8afSAKASHI Takahiro /* 113884143f6SAKASHI Takahiro * More space needed so that we can add initrd, bootargs and kaslr-seed. 11452b2a8afSAKASHI Takahiro */ 11552b2a8afSAKASHI Takahiro #define DTB_EXTRA_SPACE 0x1000 11652b2a8afSAKASHI Takahiro 11752b2a8afSAKASHI Takahiro static int create_dtb(struct kimage *image, 11852b2a8afSAKASHI Takahiro unsigned long initrd_load_addr, unsigned long initrd_len, 11952b2a8afSAKASHI Takahiro char *cmdline, void **dtb) 12052b2a8afSAKASHI Takahiro { 12152b2a8afSAKASHI Takahiro void *buf; 12252b2a8afSAKASHI Takahiro size_t buf_size; 12352b2a8afSAKASHI Takahiro int ret; 12452b2a8afSAKASHI Takahiro 12552b2a8afSAKASHI Takahiro buf_size = fdt_totalsize(initial_boot_params) 12652b2a8afSAKASHI Takahiro + strlen(cmdline) + DTB_EXTRA_SPACE; 12752b2a8afSAKASHI Takahiro 12852b2a8afSAKASHI Takahiro for (;;) { 12952b2a8afSAKASHI Takahiro buf = vmalloc(buf_size); 13052b2a8afSAKASHI Takahiro if (!buf) 13152b2a8afSAKASHI Takahiro return -ENOMEM; 13252b2a8afSAKASHI Takahiro 13352b2a8afSAKASHI Takahiro /* duplicate a device tree blob */ 13452b2a8afSAKASHI Takahiro ret = fdt_open_into(initial_boot_params, buf, buf_size); 13552b2a8afSAKASHI Takahiro if (ret) 13652b2a8afSAKASHI Takahiro return -EINVAL; 13752b2a8afSAKASHI Takahiro 13852b2a8afSAKASHI Takahiro ret = setup_dtb(image, initrd_load_addr, initrd_len, 13952b2a8afSAKASHI Takahiro cmdline, buf); 14052b2a8afSAKASHI Takahiro if (ret) { 14152b2a8afSAKASHI Takahiro vfree(buf); 14252b2a8afSAKASHI Takahiro if (ret == -ENOMEM) { 14352b2a8afSAKASHI Takahiro /* unlikely, but just in case */ 14452b2a8afSAKASHI Takahiro buf_size += DTB_EXTRA_SPACE; 14552b2a8afSAKASHI Takahiro continue; 14652b2a8afSAKASHI Takahiro } else { 14752b2a8afSAKASHI Takahiro return ret; 14852b2a8afSAKASHI Takahiro } 14952b2a8afSAKASHI Takahiro } 15052b2a8afSAKASHI Takahiro 15152b2a8afSAKASHI Takahiro /* trim it */ 15252b2a8afSAKASHI Takahiro fdt_pack(buf); 15352b2a8afSAKASHI Takahiro *dtb = buf; 15452b2a8afSAKASHI Takahiro 15552b2a8afSAKASHI Takahiro return 0; 15652b2a8afSAKASHI Takahiro } 15752b2a8afSAKASHI Takahiro } 15852b2a8afSAKASHI Takahiro 15952b2a8afSAKASHI Takahiro int load_other_segments(struct kimage *image, 16052b2a8afSAKASHI Takahiro unsigned long kernel_load_addr, 16152b2a8afSAKASHI Takahiro unsigned long kernel_size, 16252b2a8afSAKASHI Takahiro char *initrd, unsigned long initrd_len, 16352b2a8afSAKASHI Takahiro char *cmdline) 16452b2a8afSAKASHI Takahiro { 16552b2a8afSAKASHI Takahiro struct kexec_buf kbuf; 16652b2a8afSAKASHI Takahiro void *dtb = NULL; 16752b2a8afSAKASHI Takahiro unsigned long initrd_load_addr = 0, dtb_len; 16852b2a8afSAKASHI Takahiro int ret = 0; 16952b2a8afSAKASHI Takahiro 17052b2a8afSAKASHI Takahiro kbuf.image = image; 17152b2a8afSAKASHI Takahiro /* not allocate anything below the kernel */ 17252b2a8afSAKASHI Takahiro kbuf.buf_min = kernel_load_addr + kernel_size; 17352b2a8afSAKASHI Takahiro 17452b2a8afSAKASHI Takahiro /* load initrd */ 17552b2a8afSAKASHI Takahiro if (initrd) { 17652b2a8afSAKASHI Takahiro kbuf.buffer = initrd; 17752b2a8afSAKASHI Takahiro kbuf.bufsz = initrd_len; 17852b2a8afSAKASHI Takahiro kbuf.mem = 0; 17952b2a8afSAKASHI Takahiro kbuf.memsz = initrd_len; 18052b2a8afSAKASHI Takahiro kbuf.buf_align = 0; 18152b2a8afSAKASHI Takahiro /* within 1GB-aligned window of up to 32GB in size */ 18252b2a8afSAKASHI Takahiro kbuf.buf_max = round_down(kernel_load_addr, SZ_1G) 18352b2a8afSAKASHI Takahiro + (unsigned long)SZ_1G * 32; 18452b2a8afSAKASHI Takahiro kbuf.top_down = false; 18552b2a8afSAKASHI Takahiro 18652b2a8afSAKASHI Takahiro ret = kexec_add_buffer(&kbuf); 18752b2a8afSAKASHI Takahiro if (ret) 18852b2a8afSAKASHI Takahiro goto out_err; 18952b2a8afSAKASHI Takahiro initrd_load_addr = kbuf.mem; 19052b2a8afSAKASHI Takahiro 19152b2a8afSAKASHI Takahiro pr_debug("Loaded initrd at 0x%lx bufsz=0x%lx memsz=0x%lx\n", 19252b2a8afSAKASHI Takahiro initrd_load_addr, initrd_len, initrd_len); 19352b2a8afSAKASHI Takahiro } 19452b2a8afSAKASHI Takahiro 19552b2a8afSAKASHI Takahiro /* load dtb */ 19652b2a8afSAKASHI Takahiro ret = create_dtb(image, initrd_load_addr, initrd_len, cmdline, &dtb); 19752b2a8afSAKASHI Takahiro if (ret) { 19852b2a8afSAKASHI Takahiro pr_err("Preparing for new dtb failed\n"); 19952b2a8afSAKASHI Takahiro goto out_err; 20052b2a8afSAKASHI Takahiro } 20152b2a8afSAKASHI Takahiro 20252b2a8afSAKASHI Takahiro dtb_len = fdt_totalsize(dtb); 20352b2a8afSAKASHI Takahiro kbuf.buffer = dtb; 20452b2a8afSAKASHI Takahiro kbuf.bufsz = dtb_len; 20552b2a8afSAKASHI Takahiro kbuf.mem = 0; 20652b2a8afSAKASHI Takahiro kbuf.memsz = dtb_len; 20752b2a8afSAKASHI Takahiro /* not across 2MB boundary */ 20852b2a8afSAKASHI Takahiro kbuf.buf_align = SZ_2M; 20952b2a8afSAKASHI Takahiro kbuf.buf_max = ULONG_MAX; 21052b2a8afSAKASHI Takahiro kbuf.top_down = true; 21152b2a8afSAKASHI Takahiro 21252b2a8afSAKASHI Takahiro ret = kexec_add_buffer(&kbuf); 21352b2a8afSAKASHI Takahiro if (ret) 21452b2a8afSAKASHI Takahiro goto out_err; 21552b2a8afSAKASHI Takahiro image->arch.dtb = dtb; 21652b2a8afSAKASHI Takahiro image->arch.dtb_mem = kbuf.mem; 21752b2a8afSAKASHI Takahiro 21852b2a8afSAKASHI Takahiro pr_debug("Loaded dtb at 0x%lx bufsz=0x%lx memsz=0x%lx\n", 21952b2a8afSAKASHI Takahiro kbuf.mem, dtb_len, dtb_len); 22052b2a8afSAKASHI Takahiro 22152b2a8afSAKASHI Takahiro return 0; 22252b2a8afSAKASHI Takahiro 22352b2a8afSAKASHI Takahiro out_err: 22452b2a8afSAKASHI Takahiro vfree(dtb); 22552b2a8afSAKASHI Takahiro return ret; 22652b2a8afSAKASHI Takahiro } 227