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> 2252b2a8afSAKASHI Takahiro #include <asm/byteorder.h> 2352b2a8afSAKASHI Takahiro 2452b2a8afSAKASHI Takahiro /* relevant device tree properties */ 2552b2a8afSAKASHI Takahiro #define FDT_PSTR_INITRD_STA "linux,initrd-start" 2652b2a8afSAKASHI Takahiro #define FDT_PSTR_INITRD_END "linux,initrd-end" 2752b2a8afSAKASHI Takahiro #define FDT_PSTR_BOOTARGS "bootargs" 28884143f6SAKASHI Takahiro #define FDT_PSTR_KASLR_SEED "kaslr-seed" 293ddd9992SAKASHI Takahiro 303ddd9992SAKASHI Takahiro const struct kexec_file_ops * const kexec_file_loaders[] = { 31f3b70e50SAKASHI Takahiro &kexec_image_ops, 323ddd9992SAKASHI Takahiro NULL 333ddd9992SAKASHI Takahiro }; 3452b2a8afSAKASHI Takahiro 3552b2a8afSAKASHI Takahiro int arch_kimage_file_post_load_cleanup(struct kimage *image) 3652b2a8afSAKASHI Takahiro { 3752b2a8afSAKASHI Takahiro vfree(image->arch.dtb); 3852b2a8afSAKASHI Takahiro image->arch.dtb = NULL; 3952b2a8afSAKASHI Takahiro 4052b2a8afSAKASHI Takahiro return kexec_image_post_load_cleanup_default(image); 4152b2a8afSAKASHI Takahiro } 4252b2a8afSAKASHI Takahiro 4352b2a8afSAKASHI Takahiro static int setup_dtb(struct kimage *image, 4452b2a8afSAKASHI Takahiro unsigned long initrd_load_addr, unsigned long initrd_len, 4552b2a8afSAKASHI Takahiro char *cmdline, void *dtb) 4652b2a8afSAKASHI Takahiro { 4752b2a8afSAKASHI Takahiro int nodeoffset; 4852b2a8afSAKASHI Takahiro int ret; 4952b2a8afSAKASHI Takahiro 5052b2a8afSAKASHI Takahiro nodeoffset = fdt_path_offset(dtb, "/chosen"); 5152b2a8afSAKASHI Takahiro if (nodeoffset < 0) 5252b2a8afSAKASHI Takahiro return -EINVAL; 5352b2a8afSAKASHI Takahiro 5452b2a8afSAKASHI Takahiro /* add bootargs */ 5552b2a8afSAKASHI Takahiro if (cmdline) { 5652b2a8afSAKASHI Takahiro ret = fdt_setprop_string(dtb, nodeoffset, FDT_PSTR_BOOTARGS, 5752b2a8afSAKASHI Takahiro cmdline); 5852b2a8afSAKASHI Takahiro if (ret) 5952b2a8afSAKASHI Takahiro return (ret == -FDT_ERR_NOSPACE ? -ENOMEM : -EINVAL); 6052b2a8afSAKASHI Takahiro } else { 6152b2a8afSAKASHI Takahiro ret = fdt_delprop(dtb, nodeoffset, FDT_PSTR_BOOTARGS); 6252b2a8afSAKASHI Takahiro if (ret && (ret != -FDT_ERR_NOTFOUND)) 6352b2a8afSAKASHI Takahiro return -EINVAL; 6452b2a8afSAKASHI Takahiro } 6552b2a8afSAKASHI Takahiro 6652b2a8afSAKASHI Takahiro /* add initrd-* */ 6752b2a8afSAKASHI Takahiro if (initrd_load_addr) { 6852b2a8afSAKASHI Takahiro ret = fdt_setprop_u64(dtb, nodeoffset, FDT_PSTR_INITRD_STA, 6952b2a8afSAKASHI Takahiro initrd_load_addr); 7052b2a8afSAKASHI Takahiro if (ret) 7152b2a8afSAKASHI Takahiro return (ret == -FDT_ERR_NOSPACE ? -ENOMEM : -EINVAL); 7252b2a8afSAKASHI Takahiro 7352b2a8afSAKASHI Takahiro ret = fdt_setprop_u64(dtb, nodeoffset, FDT_PSTR_INITRD_END, 7452b2a8afSAKASHI Takahiro initrd_load_addr + initrd_len); 7552b2a8afSAKASHI Takahiro if (ret) 7652b2a8afSAKASHI Takahiro return (ret == -FDT_ERR_NOSPACE ? -ENOMEM : -EINVAL); 7752b2a8afSAKASHI Takahiro } else { 7852b2a8afSAKASHI Takahiro ret = fdt_delprop(dtb, nodeoffset, FDT_PSTR_INITRD_STA); 7952b2a8afSAKASHI Takahiro if (ret && (ret != -FDT_ERR_NOTFOUND)) 8052b2a8afSAKASHI Takahiro return -EINVAL; 8152b2a8afSAKASHI Takahiro 8252b2a8afSAKASHI Takahiro ret = fdt_delprop(dtb, nodeoffset, FDT_PSTR_INITRD_END); 8352b2a8afSAKASHI Takahiro if (ret && (ret != -FDT_ERR_NOTFOUND)) 8452b2a8afSAKASHI Takahiro return -EINVAL; 8552b2a8afSAKASHI Takahiro } 8652b2a8afSAKASHI Takahiro 87884143f6SAKASHI Takahiro /* add kaslr-seed */ 88884143f6SAKASHI Takahiro ret = fdt_delprop(dtb, nodeoffset, FDT_PSTR_KASLR_SEED); 89884143f6SAKASHI Takahiro if (ret && (ret != -FDT_ERR_NOTFOUND)) 90884143f6SAKASHI Takahiro return -EINVAL; 91884143f6SAKASHI Takahiro 92884143f6SAKASHI Takahiro if (rng_is_initialized()) { 93884143f6SAKASHI Takahiro u64 r = get_random_u64(); 94884143f6SAKASHI Takahiro ret = fdt_setprop_u64(dtb, nodeoffset, FDT_PSTR_KASLR_SEED, r); 95884143f6SAKASHI Takahiro if (ret) 96884143f6SAKASHI Takahiro return (ret == -FDT_ERR_NOSPACE ? -ENOMEM : -EINVAL); 97884143f6SAKASHI Takahiro } else { 98884143f6SAKASHI Takahiro pr_notice("RNG is not initialised: omitting \"%s\" property\n", 99884143f6SAKASHI Takahiro FDT_PSTR_KASLR_SEED); 100884143f6SAKASHI Takahiro } 101884143f6SAKASHI Takahiro 10252b2a8afSAKASHI Takahiro return 0; 10352b2a8afSAKASHI Takahiro } 10452b2a8afSAKASHI Takahiro 10552b2a8afSAKASHI Takahiro /* 106884143f6SAKASHI Takahiro * More space needed so that we can add initrd, bootargs and kaslr-seed. 10752b2a8afSAKASHI Takahiro */ 10852b2a8afSAKASHI Takahiro #define DTB_EXTRA_SPACE 0x1000 10952b2a8afSAKASHI Takahiro 11052b2a8afSAKASHI Takahiro static int create_dtb(struct kimage *image, 11152b2a8afSAKASHI Takahiro unsigned long initrd_load_addr, unsigned long initrd_len, 11252b2a8afSAKASHI Takahiro char *cmdline, void **dtb) 11352b2a8afSAKASHI Takahiro { 11452b2a8afSAKASHI Takahiro void *buf; 11552b2a8afSAKASHI Takahiro size_t buf_size; 11652b2a8afSAKASHI Takahiro int ret; 11752b2a8afSAKASHI Takahiro 11852b2a8afSAKASHI Takahiro buf_size = fdt_totalsize(initial_boot_params) 11952b2a8afSAKASHI Takahiro + strlen(cmdline) + DTB_EXTRA_SPACE; 12052b2a8afSAKASHI Takahiro 12152b2a8afSAKASHI Takahiro for (;;) { 12252b2a8afSAKASHI Takahiro buf = vmalloc(buf_size); 12352b2a8afSAKASHI Takahiro if (!buf) 12452b2a8afSAKASHI Takahiro return -ENOMEM; 12552b2a8afSAKASHI Takahiro 12652b2a8afSAKASHI Takahiro /* duplicate a device tree blob */ 12752b2a8afSAKASHI Takahiro ret = fdt_open_into(initial_boot_params, buf, buf_size); 12852b2a8afSAKASHI Takahiro if (ret) 12952b2a8afSAKASHI Takahiro return -EINVAL; 13052b2a8afSAKASHI Takahiro 13152b2a8afSAKASHI Takahiro ret = setup_dtb(image, initrd_load_addr, initrd_len, 13252b2a8afSAKASHI Takahiro cmdline, buf); 13352b2a8afSAKASHI Takahiro if (ret) { 13452b2a8afSAKASHI Takahiro vfree(buf); 13552b2a8afSAKASHI Takahiro if (ret == -ENOMEM) { 13652b2a8afSAKASHI Takahiro /* unlikely, but just in case */ 13752b2a8afSAKASHI Takahiro buf_size += DTB_EXTRA_SPACE; 13852b2a8afSAKASHI Takahiro continue; 13952b2a8afSAKASHI Takahiro } else { 14052b2a8afSAKASHI Takahiro return ret; 14152b2a8afSAKASHI Takahiro } 14252b2a8afSAKASHI Takahiro } 14352b2a8afSAKASHI Takahiro 14452b2a8afSAKASHI Takahiro /* trim it */ 14552b2a8afSAKASHI Takahiro fdt_pack(buf); 14652b2a8afSAKASHI Takahiro *dtb = buf; 14752b2a8afSAKASHI Takahiro 14852b2a8afSAKASHI Takahiro return 0; 14952b2a8afSAKASHI Takahiro } 15052b2a8afSAKASHI Takahiro } 15152b2a8afSAKASHI Takahiro 15252b2a8afSAKASHI Takahiro int load_other_segments(struct kimage *image, 15352b2a8afSAKASHI Takahiro unsigned long kernel_load_addr, 15452b2a8afSAKASHI Takahiro unsigned long kernel_size, 15552b2a8afSAKASHI Takahiro char *initrd, unsigned long initrd_len, 15652b2a8afSAKASHI Takahiro char *cmdline) 15752b2a8afSAKASHI Takahiro { 15852b2a8afSAKASHI Takahiro struct kexec_buf kbuf; 15952b2a8afSAKASHI Takahiro void *dtb = NULL; 16052b2a8afSAKASHI Takahiro unsigned long initrd_load_addr = 0, dtb_len; 16152b2a8afSAKASHI Takahiro int ret = 0; 16252b2a8afSAKASHI Takahiro 16352b2a8afSAKASHI Takahiro kbuf.image = image; 16452b2a8afSAKASHI Takahiro /* not allocate anything below the kernel */ 16552b2a8afSAKASHI Takahiro kbuf.buf_min = kernel_load_addr + kernel_size; 16652b2a8afSAKASHI Takahiro 16752b2a8afSAKASHI Takahiro /* load initrd */ 16852b2a8afSAKASHI Takahiro if (initrd) { 16952b2a8afSAKASHI Takahiro kbuf.buffer = initrd; 17052b2a8afSAKASHI Takahiro kbuf.bufsz = initrd_len; 17152b2a8afSAKASHI Takahiro kbuf.mem = 0; 17252b2a8afSAKASHI Takahiro kbuf.memsz = initrd_len; 17352b2a8afSAKASHI Takahiro kbuf.buf_align = 0; 17452b2a8afSAKASHI Takahiro /* within 1GB-aligned window of up to 32GB in size */ 17552b2a8afSAKASHI Takahiro kbuf.buf_max = round_down(kernel_load_addr, SZ_1G) 17652b2a8afSAKASHI Takahiro + (unsigned long)SZ_1G * 32; 17752b2a8afSAKASHI Takahiro kbuf.top_down = false; 17852b2a8afSAKASHI Takahiro 17952b2a8afSAKASHI Takahiro ret = kexec_add_buffer(&kbuf); 18052b2a8afSAKASHI Takahiro if (ret) 18152b2a8afSAKASHI Takahiro goto out_err; 18252b2a8afSAKASHI Takahiro initrd_load_addr = kbuf.mem; 18352b2a8afSAKASHI Takahiro 18452b2a8afSAKASHI Takahiro pr_debug("Loaded initrd at 0x%lx bufsz=0x%lx memsz=0x%lx\n", 18552b2a8afSAKASHI Takahiro initrd_load_addr, initrd_len, initrd_len); 18652b2a8afSAKASHI Takahiro } 18752b2a8afSAKASHI Takahiro 18852b2a8afSAKASHI Takahiro /* load dtb */ 18952b2a8afSAKASHI Takahiro ret = create_dtb(image, initrd_load_addr, initrd_len, cmdline, &dtb); 19052b2a8afSAKASHI Takahiro if (ret) { 19152b2a8afSAKASHI Takahiro pr_err("Preparing for new dtb failed\n"); 19252b2a8afSAKASHI Takahiro goto out_err; 19352b2a8afSAKASHI Takahiro } 19452b2a8afSAKASHI Takahiro 19552b2a8afSAKASHI Takahiro dtb_len = fdt_totalsize(dtb); 19652b2a8afSAKASHI Takahiro kbuf.buffer = dtb; 19752b2a8afSAKASHI Takahiro kbuf.bufsz = dtb_len; 19852b2a8afSAKASHI Takahiro kbuf.mem = 0; 19952b2a8afSAKASHI Takahiro kbuf.memsz = dtb_len; 20052b2a8afSAKASHI Takahiro /* not across 2MB boundary */ 20152b2a8afSAKASHI Takahiro kbuf.buf_align = SZ_2M; 20252b2a8afSAKASHI Takahiro kbuf.buf_max = ULONG_MAX; 20352b2a8afSAKASHI Takahiro kbuf.top_down = true; 20452b2a8afSAKASHI Takahiro 20552b2a8afSAKASHI Takahiro ret = kexec_add_buffer(&kbuf); 20652b2a8afSAKASHI Takahiro if (ret) 20752b2a8afSAKASHI Takahiro goto out_err; 20852b2a8afSAKASHI Takahiro image->arch.dtb = dtb; 20952b2a8afSAKASHI Takahiro image->arch.dtb_mem = kbuf.mem; 21052b2a8afSAKASHI Takahiro 21152b2a8afSAKASHI Takahiro pr_debug("Loaded dtb at 0x%lx bufsz=0x%lx memsz=0x%lx\n", 21252b2a8afSAKASHI Takahiro kbuf.mem, dtb_len, dtb_len); 21352b2a8afSAKASHI Takahiro 21452b2a8afSAKASHI Takahiro return 0; 21552b2a8afSAKASHI Takahiro 21652b2a8afSAKASHI Takahiro out_err: 21752b2a8afSAKASHI Takahiro vfree(dtb); 21852b2a8afSAKASHI Takahiro return ret; 21952b2a8afSAKASHI Takahiro } 220