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> 1952b2a8afSAKASHI Takahiro #include <linux/string.h> 2052b2a8afSAKASHI Takahiro #include <linux/types.h> 2152b2a8afSAKASHI Takahiro #include <asm/byteorder.h> 2252b2a8afSAKASHI Takahiro 2352b2a8afSAKASHI Takahiro /* relevant device tree properties */ 2452b2a8afSAKASHI Takahiro #define FDT_PSTR_INITRD_STA "linux,initrd-start" 2552b2a8afSAKASHI Takahiro #define FDT_PSTR_INITRD_END "linux,initrd-end" 2652b2a8afSAKASHI Takahiro #define FDT_PSTR_BOOTARGS "bootargs" 273ddd9992SAKASHI Takahiro 283ddd9992SAKASHI Takahiro const struct kexec_file_ops * const kexec_file_loaders[] = { 293ddd9992SAKASHI Takahiro NULL 303ddd9992SAKASHI Takahiro }; 3152b2a8afSAKASHI Takahiro 3252b2a8afSAKASHI Takahiro int arch_kimage_file_post_load_cleanup(struct kimage *image) 3352b2a8afSAKASHI Takahiro { 3452b2a8afSAKASHI Takahiro vfree(image->arch.dtb); 3552b2a8afSAKASHI Takahiro image->arch.dtb = NULL; 3652b2a8afSAKASHI Takahiro 3752b2a8afSAKASHI Takahiro return kexec_image_post_load_cleanup_default(image); 3852b2a8afSAKASHI Takahiro } 3952b2a8afSAKASHI Takahiro 4052b2a8afSAKASHI Takahiro static int setup_dtb(struct kimage *image, 4152b2a8afSAKASHI Takahiro unsigned long initrd_load_addr, unsigned long initrd_len, 4252b2a8afSAKASHI Takahiro char *cmdline, void *dtb) 4352b2a8afSAKASHI Takahiro { 4452b2a8afSAKASHI Takahiro int nodeoffset; 4552b2a8afSAKASHI Takahiro int ret; 4652b2a8afSAKASHI Takahiro 4752b2a8afSAKASHI Takahiro nodeoffset = fdt_path_offset(dtb, "/chosen"); 4852b2a8afSAKASHI Takahiro if (nodeoffset < 0) 4952b2a8afSAKASHI Takahiro return -EINVAL; 5052b2a8afSAKASHI Takahiro 5152b2a8afSAKASHI Takahiro /* add bootargs */ 5252b2a8afSAKASHI Takahiro if (cmdline) { 5352b2a8afSAKASHI Takahiro ret = fdt_setprop_string(dtb, nodeoffset, FDT_PSTR_BOOTARGS, 5452b2a8afSAKASHI Takahiro cmdline); 5552b2a8afSAKASHI Takahiro if (ret) 5652b2a8afSAKASHI Takahiro return (ret == -FDT_ERR_NOSPACE ? -ENOMEM : -EINVAL); 5752b2a8afSAKASHI Takahiro } else { 5852b2a8afSAKASHI Takahiro ret = fdt_delprop(dtb, nodeoffset, FDT_PSTR_BOOTARGS); 5952b2a8afSAKASHI Takahiro if (ret && (ret != -FDT_ERR_NOTFOUND)) 6052b2a8afSAKASHI Takahiro return -EINVAL; 6152b2a8afSAKASHI Takahiro } 6252b2a8afSAKASHI Takahiro 6352b2a8afSAKASHI Takahiro /* add initrd-* */ 6452b2a8afSAKASHI Takahiro if (initrd_load_addr) { 6552b2a8afSAKASHI Takahiro ret = fdt_setprop_u64(dtb, nodeoffset, FDT_PSTR_INITRD_STA, 6652b2a8afSAKASHI Takahiro initrd_load_addr); 6752b2a8afSAKASHI Takahiro if (ret) 6852b2a8afSAKASHI Takahiro return (ret == -FDT_ERR_NOSPACE ? -ENOMEM : -EINVAL); 6952b2a8afSAKASHI Takahiro 7052b2a8afSAKASHI Takahiro ret = fdt_setprop_u64(dtb, nodeoffset, FDT_PSTR_INITRD_END, 7152b2a8afSAKASHI Takahiro initrd_load_addr + initrd_len); 7252b2a8afSAKASHI Takahiro if (ret) 7352b2a8afSAKASHI Takahiro return (ret == -FDT_ERR_NOSPACE ? -ENOMEM : -EINVAL); 7452b2a8afSAKASHI Takahiro } else { 7552b2a8afSAKASHI Takahiro ret = fdt_delprop(dtb, nodeoffset, FDT_PSTR_INITRD_STA); 7652b2a8afSAKASHI Takahiro if (ret && (ret != -FDT_ERR_NOTFOUND)) 7752b2a8afSAKASHI Takahiro return -EINVAL; 7852b2a8afSAKASHI Takahiro 7952b2a8afSAKASHI Takahiro ret = fdt_delprop(dtb, nodeoffset, FDT_PSTR_INITRD_END); 8052b2a8afSAKASHI Takahiro if (ret && (ret != -FDT_ERR_NOTFOUND)) 8152b2a8afSAKASHI Takahiro return -EINVAL; 8252b2a8afSAKASHI Takahiro } 8352b2a8afSAKASHI Takahiro 8452b2a8afSAKASHI Takahiro return 0; 8552b2a8afSAKASHI Takahiro } 8652b2a8afSAKASHI Takahiro 8752b2a8afSAKASHI Takahiro /* 8852b2a8afSAKASHI Takahiro * More space needed so that we can add initrd and bootargs. 8952b2a8afSAKASHI Takahiro */ 9052b2a8afSAKASHI Takahiro #define DTB_EXTRA_SPACE 0x1000 9152b2a8afSAKASHI Takahiro 9252b2a8afSAKASHI Takahiro static int create_dtb(struct kimage *image, 9352b2a8afSAKASHI Takahiro unsigned long initrd_load_addr, unsigned long initrd_len, 9452b2a8afSAKASHI Takahiro char *cmdline, void **dtb) 9552b2a8afSAKASHI Takahiro { 9652b2a8afSAKASHI Takahiro void *buf; 9752b2a8afSAKASHI Takahiro size_t buf_size; 9852b2a8afSAKASHI Takahiro int ret; 9952b2a8afSAKASHI Takahiro 10052b2a8afSAKASHI Takahiro buf_size = fdt_totalsize(initial_boot_params) 10152b2a8afSAKASHI Takahiro + strlen(cmdline) + DTB_EXTRA_SPACE; 10252b2a8afSAKASHI Takahiro 10352b2a8afSAKASHI Takahiro for (;;) { 10452b2a8afSAKASHI Takahiro buf = vmalloc(buf_size); 10552b2a8afSAKASHI Takahiro if (!buf) 10652b2a8afSAKASHI Takahiro return -ENOMEM; 10752b2a8afSAKASHI Takahiro 10852b2a8afSAKASHI Takahiro /* duplicate a device tree blob */ 10952b2a8afSAKASHI Takahiro ret = fdt_open_into(initial_boot_params, buf, buf_size); 11052b2a8afSAKASHI Takahiro if (ret) 11152b2a8afSAKASHI Takahiro return -EINVAL; 11252b2a8afSAKASHI Takahiro 11352b2a8afSAKASHI Takahiro ret = setup_dtb(image, initrd_load_addr, initrd_len, 11452b2a8afSAKASHI Takahiro cmdline, buf); 11552b2a8afSAKASHI Takahiro if (ret) { 11652b2a8afSAKASHI Takahiro vfree(buf); 11752b2a8afSAKASHI Takahiro if (ret == -ENOMEM) { 11852b2a8afSAKASHI Takahiro /* unlikely, but just in case */ 11952b2a8afSAKASHI Takahiro buf_size += DTB_EXTRA_SPACE; 12052b2a8afSAKASHI Takahiro continue; 12152b2a8afSAKASHI Takahiro } else { 12252b2a8afSAKASHI Takahiro return ret; 12352b2a8afSAKASHI Takahiro } 12452b2a8afSAKASHI Takahiro } 12552b2a8afSAKASHI Takahiro 12652b2a8afSAKASHI Takahiro /* trim it */ 12752b2a8afSAKASHI Takahiro fdt_pack(buf); 12852b2a8afSAKASHI Takahiro *dtb = buf; 12952b2a8afSAKASHI Takahiro 13052b2a8afSAKASHI Takahiro return 0; 13152b2a8afSAKASHI Takahiro } 13252b2a8afSAKASHI Takahiro } 13352b2a8afSAKASHI Takahiro 13452b2a8afSAKASHI Takahiro int load_other_segments(struct kimage *image, 13552b2a8afSAKASHI Takahiro unsigned long kernel_load_addr, 13652b2a8afSAKASHI Takahiro unsigned long kernel_size, 13752b2a8afSAKASHI Takahiro char *initrd, unsigned long initrd_len, 13852b2a8afSAKASHI Takahiro char *cmdline) 13952b2a8afSAKASHI Takahiro { 14052b2a8afSAKASHI Takahiro struct kexec_buf kbuf; 14152b2a8afSAKASHI Takahiro void *dtb = NULL; 14252b2a8afSAKASHI Takahiro unsigned long initrd_load_addr = 0, dtb_len; 14352b2a8afSAKASHI Takahiro int ret = 0; 14452b2a8afSAKASHI Takahiro 14552b2a8afSAKASHI Takahiro kbuf.image = image; 14652b2a8afSAKASHI Takahiro /* not allocate anything below the kernel */ 14752b2a8afSAKASHI Takahiro kbuf.buf_min = kernel_load_addr + kernel_size; 14852b2a8afSAKASHI Takahiro 14952b2a8afSAKASHI Takahiro /* load initrd */ 15052b2a8afSAKASHI Takahiro if (initrd) { 15152b2a8afSAKASHI Takahiro kbuf.buffer = initrd; 15252b2a8afSAKASHI Takahiro kbuf.bufsz = initrd_len; 15352b2a8afSAKASHI Takahiro kbuf.mem = 0; 15452b2a8afSAKASHI Takahiro kbuf.memsz = initrd_len; 15552b2a8afSAKASHI Takahiro kbuf.buf_align = 0; 15652b2a8afSAKASHI Takahiro /* within 1GB-aligned window of up to 32GB in size */ 15752b2a8afSAKASHI Takahiro kbuf.buf_max = round_down(kernel_load_addr, SZ_1G) 15852b2a8afSAKASHI Takahiro + (unsigned long)SZ_1G * 32; 15952b2a8afSAKASHI Takahiro kbuf.top_down = false; 16052b2a8afSAKASHI Takahiro 16152b2a8afSAKASHI Takahiro ret = kexec_add_buffer(&kbuf); 16252b2a8afSAKASHI Takahiro if (ret) 16352b2a8afSAKASHI Takahiro goto out_err; 16452b2a8afSAKASHI Takahiro initrd_load_addr = kbuf.mem; 16552b2a8afSAKASHI Takahiro 16652b2a8afSAKASHI Takahiro pr_debug("Loaded initrd at 0x%lx bufsz=0x%lx memsz=0x%lx\n", 16752b2a8afSAKASHI Takahiro initrd_load_addr, initrd_len, initrd_len); 16852b2a8afSAKASHI Takahiro } 16952b2a8afSAKASHI Takahiro 17052b2a8afSAKASHI Takahiro /* load dtb */ 17152b2a8afSAKASHI Takahiro ret = create_dtb(image, initrd_load_addr, initrd_len, cmdline, &dtb); 17252b2a8afSAKASHI Takahiro if (ret) { 17352b2a8afSAKASHI Takahiro pr_err("Preparing for new dtb failed\n"); 17452b2a8afSAKASHI Takahiro goto out_err; 17552b2a8afSAKASHI Takahiro } 17652b2a8afSAKASHI Takahiro 17752b2a8afSAKASHI Takahiro dtb_len = fdt_totalsize(dtb); 17852b2a8afSAKASHI Takahiro kbuf.buffer = dtb; 17952b2a8afSAKASHI Takahiro kbuf.bufsz = dtb_len; 18052b2a8afSAKASHI Takahiro kbuf.mem = 0; 18152b2a8afSAKASHI Takahiro kbuf.memsz = dtb_len; 18252b2a8afSAKASHI Takahiro /* not across 2MB boundary */ 18352b2a8afSAKASHI Takahiro kbuf.buf_align = SZ_2M; 18452b2a8afSAKASHI Takahiro kbuf.buf_max = ULONG_MAX; 18552b2a8afSAKASHI Takahiro kbuf.top_down = true; 18652b2a8afSAKASHI Takahiro 18752b2a8afSAKASHI Takahiro ret = kexec_add_buffer(&kbuf); 18852b2a8afSAKASHI Takahiro if (ret) 18952b2a8afSAKASHI Takahiro goto out_err; 19052b2a8afSAKASHI Takahiro image->arch.dtb = dtb; 19152b2a8afSAKASHI Takahiro image->arch.dtb_mem = kbuf.mem; 19252b2a8afSAKASHI Takahiro 19352b2a8afSAKASHI Takahiro pr_debug("Loaded dtb at 0x%lx bufsz=0x%lx memsz=0x%lx\n", 19452b2a8afSAKASHI Takahiro kbuf.mem, dtb_len, dtb_len); 19552b2a8afSAKASHI Takahiro 19652b2a8afSAKASHI Takahiro return 0; 19752b2a8afSAKASHI Takahiro 19852b2a8afSAKASHI Takahiro out_err: 19952b2a8afSAKASHI Takahiro vfree(dtb); 20052b2a8afSAKASHI Takahiro return ret; 20152b2a8afSAKASHI Takahiro } 202