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> 203751e728SAKASHI Takahiro #include <linux/slab.h> 2152b2a8afSAKASHI Takahiro #include <linux/string.h> 2252b2a8afSAKASHI Takahiro #include <linux/types.h> 23732291c4SArnd Bergmann #include <linux/vmalloc.h> 2452b2a8afSAKASHI Takahiro #include <asm/byteorder.h> 2552b2a8afSAKASHI Takahiro 2652b2a8afSAKASHI Takahiro /* relevant device tree properties */ 273751e728SAKASHI Takahiro #define FDT_PROP_KEXEC_ELFHDR "linux,elfcorehdr" 283751e728SAKASHI Takahiro #define FDT_PROP_MEM_RANGE "linux,usable-memory-range" 29121ca8e5SWill Deacon #define FDT_PROP_INITRD_START "linux,initrd-start" 30121ca8e5SWill Deacon #define FDT_PROP_INITRD_END "linux,initrd-end" 31121ca8e5SWill Deacon #define FDT_PROP_BOOTARGS "bootargs" 32121ca8e5SWill Deacon #define FDT_PROP_KASLR_SEED "kaslr-seed" 337f591fa7SHsin-Yi Wang #define FDT_PROP_RNG_SEED "rng-seed" 347f591fa7SHsin-Yi Wang #define RNG_SEED_SIZE 128 353ddd9992SAKASHI Takahiro 363ddd9992SAKASHI Takahiro const struct kexec_file_ops * const kexec_file_loaders[] = { 37f3b70e50SAKASHI Takahiro &kexec_image_ops, 383ddd9992SAKASHI Takahiro NULL 393ddd9992SAKASHI Takahiro }; 4052b2a8afSAKASHI Takahiro 4152b2a8afSAKASHI Takahiro int arch_kimage_file_post_load_cleanup(struct kimage *image) 4252b2a8afSAKASHI Takahiro { 4352b2a8afSAKASHI Takahiro vfree(image->arch.dtb); 4452b2a8afSAKASHI Takahiro image->arch.dtb = NULL; 4552b2a8afSAKASHI Takahiro 463751e728SAKASHI Takahiro vfree(image->arch.elf_headers); 473751e728SAKASHI Takahiro image->arch.elf_headers = NULL; 483751e728SAKASHI Takahiro image->arch.elf_headers_sz = 0; 493751e728SAKASHI Takahiro 5052b2a8afSAKASHI Takahiro return kexec_image_post_load_cleanup_default(image); 5152b2a8afSAKASHI Takahiro } 5252b2a8afSAKASHI Takahiro 5352b2a8afSAKASHI Takahiro static int setup_dtb(struct kimage *image, 5452b2a8afSAKASHI Takahiro unsigned long initrd_load_addr, unsigned long initrd_len, 5552b2a8afSAKASHI Takahiro char *cmdline, void *dtb) 5652b2a8afSAKASHI Takahiro { 57121ca8e5SWill Deacon int off, ret; 5852b2a8afSAKASHI Takahiro 59121ca8e5SWill Deacon ret = fdt_path_offset(dtb, "/chosen"); 60121ca8e5SWill Deacon if (ret < 0) 61121ca8e5SWill Deacon goto out; 62121ca8e5SWill Deacon 63121ca8e5SWill Deacon off = ret; 6452b2a8afSAKASHI Takahiro 653751e728SAKASHI Takahiro ret = fdt_delprop(dtb, off, FDT_PROP_KEXEC_ELFHDR); 663751e728SAKASHI Takahiro if (ret && ret != -FDT_ERR_NOTFOUND) 673751e728SAKASHI Takahiro goto out; 683751e728SAKASHI Takahiro ret = fdt_delprop(dtb, off, FDT_PROP_MEM_RANGE); 693751e728SAKASHI Takahiro if (ret && ret != -FDT_ERR_NOTFOUND) 703751e728SAKASHI Takahiro goto out; 713751e728SAKASHI Takahiro 723751e728SAKASHI Takahiro if (image->type == KEXEC_TYPE_CRASH) { 733751e728SAKASHI Takahiro /* add linux,elfcorehdr */ 743751e728SAKASHI Takahiro ret = fdt_appendprop_addrrange(dtb, 0, off, 753751e728SAKASHI Takahiro FDT_PROP_KEXEC_ELFHDR, 763751e728SAKASHI Takahiro image->arch.elf_headers_mem, 773751e728SAKASHI Takahiro image->arch.elf_headers_sz); 783751e728SAKASHI Takahiro if (ret) 793751e728SAKASHI Takahiro return (ret == -FDT_ERR_NOSPACE ? -ENOMEM : -EINVAL); 803751e728SAKASHI Takahiro 813751e728SAKASHI Takahiro /* add linux,usable-memory-range */ 823751e728SAKASHI Takahiro ret = fdt_appendprop_addrrange(dtb, 0, off, 833751e728SAKASHI Takahiro FDT_PROP_MEM_RANGE, 843751e728SAKASHI Takahiro crashk_res.start, 853751e728SAKASHI Takahiro crashk_res.end - crashk_res.start + 1); 863751e728SAKASHI Takahiro if (ret) 873751e728SAKASHI Takahiro return (ret == -FDT_ERR_NOSPACE ? -ENOMEM : -EINVAL); 883751e728SAKASHI Takahiro } 893751e728SAKASHI Takahiro 9052b2a8afSAKASHI Takahiro /* add bootargs */ 9152b2a8afSAKASHI Takahiro if (cmdline) { 92121ca8e5SWill Deacon ret = fdt_setprop_string(dtb, off, FDT_PROP_BOOTARGS, cmdline); 9352b2a8afSAKASHI Takahiro if (ret) 94121ca8e5SWill Deacon goto out; 9552b2a8afSAKASHI Takahiro } else { 96121ca8e5SWill Deacon ret = fdt_delprop(dtb, off, FDT_PROP_BOOTARGS); 9752b2a8afSAKASHI Takahiro if (ret && (ret != -FDT_ERR_NOTFOUND)) 98121ca8e5SWill Deacon goto out; 9952b2a8afSAKASHI Takahiro } 10052b2a8afSAKASHI Takahiro 10152b2a8afSAKASHI Takahiro /* add initrd-* */ 10252b2a8afSAKASHI Takahiro if (initrd_load_addr) { 103121ca8e5SWill Deacon ret = fdt_setprop_u64(dtb, off, FDT_PROP_INITRD_START, 10452b2a8afSAKASHI Takahiro initrd_load_addr); 10552b2a8afSAKASHI Takahiro if (ret) 106121ca8e5SWill Deacon goto out; 10752b2a8afSAKASHI Takahiro 108121ca8e5SWill Deacon ret = fdt_setprop_u64(dtb, off, FDT_PROP_INITRD_END, 10952b2a8afSAKASHI Takahiro initrd_load_addr + initrd_len); 11052b2a8afSAKASHI Takahiro if (ret) 111121ca8e5SWill Deacon goto out; 11252b2a8afSAKASHI Takahiro } else { 113121ca8e5SWill Deacon ret = fdt_delprop(dtb, off, FDT_PROP_INITRD_START); 11452b2a8afSAKASHI Takahiro if (ret && (ret != -FDT_ERR_NOTFOUND)) 115121ca8e5SWill Deacon goto out; 11652b2a8afSAKASHI Takahiro 117121ca8e5SWill Deacon ret = fdt_delprop(dtb, off, FDT_PROP_INITRD_END); 11852b2a8afSAKASHI Takahiro if (ret && (ret != -FDT_ERR_NOTFOUND)) 119121ca8e5SWill Deacon goto out; 12052b2a8afSAKASHI Takahiro } 12152b2a8afSAKASHI Takahiro 122884143f6SAKASHI Takahiro /* add kaslr-seed */ 123121ca8e5SWill Deacon ret = fdt_delprop(dtb, off, FDT_PROP_KASLR_SEED); 12427966721SAKASHI Takahiro if (ret == -FDT_ERR_NOTFOUND) 12527966721SAKASHI Takahiro ret = 0; 12627966721SAKASHI Takahiro else if (ret) 127121ca8e5SWill Deacon goto out; 128884143f6SAKASHI Takahiro 129884143f6SAKASHI Takahiro if (rng_is_initialized()) { 130121ca8e5SWill Deacon u64 seed = get_random_u64(); 131121ca8e5SWill Deacon ret = fdt_setprop_u64(dtb, off, FDT_PROP_KASLR_SEED, seed); 132884143f6SAKASHI Takahiro if (ret) 133121ca8e5SWill Deacon goto out; 134884143f6SAKASHI Takahiro } else { 135884143f6SAKASHI Takahiro pr_notice("RNG is not initialised: omitting \"%s\" property\n", 136121ca8e5SWill Deacon FDT_PROP_KASLR_SEED); 137884143f6SAKASHI Takahiro } 138884143f6SAKASHI Takahiro 1397f591fa7SHsin-Yi Wang /* add rng-seed */ 1407f591fa7SHsin-Yi Wang if (rng_is_initialized()) { 14199ee28d9SGeorge Spelvin void *rng_seed; 14299ee28d9SGeorge Spelvin ret = fdt_setprop_placeholder(dtb, off, FDT_PROP_RNG_SEED, 14399ee28d9SGeorge Spelvin RNG_SEED_SIZE, &rng_seed); 1447f591fa7SHsin-Yi Wang if (ret) 1457f591fa7SHsin-Yi Wang goto out; 14699ee28d9SGeorge Spelvin get_random_bytes(rng_seed, RNG_SEED_SIZE); 1477f591fa7SHsin-Yi Wang } else { 1487f591fa7SHsin-Yi Wang pr_notice("RNG is not initialised: omitting \"%s\" property\n", 1497f591fa7SHsin-Yi Wang FDT_PROP_RNG_SEED); 1507f591fa7SHsin-Yi Wang } 1517f591fa7SHsin-Yi Wang 152121ca8e5SWill Deacon out: 153121ca8e5SWill Deacon if (ret) 154121ca8e5SWill Deacon return (ret == -FDT_ERR_NOSPACE) ? -ENOMEM : -EINVAL; 155121ca8e5SWill Deacon 15652b2a8afSAKASHI Takahiro return 0; 15752b2a8afSAKASHI Takahiro } 15852b2a8afSAKASHI Takahiro 15952b2a8afSAKASHI Takahiro /* 1603751e728SAKASHI Takahiro * More space needed so that we can add initrd, bootargs, kaslr-seed, 1613751e728SAKASHI Takahiro * rng-seed, userable-memory-range and elfcorehdr. 16252b2a8afSAKASHI Takahiro */ 16352b2a8afSAKASHI Takahiro #define DTB_EXTRA_SPACE 0x1000 16452b2a8afSAKASHI Takahiro 16552b2a8afSAKASHI Takahiro static int create_dtb(struct kimage *image, 16652b2a8afSAKASHI Takahiro unsigned long initrd_load_addr, unsigned long initrd_len, 16752b2a8afSAKASHI Takahiro char *cmdline, void **dtb) 16852b2a8afSAKASHI Takahiro { 16952b2a8afSAKASHI Takahiro void *buf; 17052b2a8afSAKASHI Takahiro size_t buf_size; 171ea573680SJean-Philippe Brucker size_t cmdline_len; 17252b2a8afSAKASHI Takahiro int ret; 17352b2a8afSAKASHI Takahiro 174ea573680SJean-Philippe Brucker cmdline_len = cmdline ? strlen(cmdline) : 0; 17552b2a8afSAKASHI Takahiro buf_size = fdt_totalsize(initial_boot_params) 176ea573680SJean-Philippe Brucker + cmdline_len + DTB_EXTRA_SPACE; 17752b2a8afSAKASHI Takahiro 17852b2a8afSAKASHI Takahiro for (;;) { 17952b2a8afSAKASHI Takahiro buf = vmalloc(buf_size); 18052b2a8afSAKASHI Takahiro if (!buf) 18152b2a8afSAKASHI Takahiro return -ENOMEM; 18252b2a8afSAKASHI Takahiro 18352b2a8afSAKASHI Takahiro /* duplicate a device tree blob */ 18452b2a8afSAKASHI Takahiro ret = fdt_open_into(initial_boot_params, buf, buf_size); 18552b2a8afSAKASHI Takahiro if (ret) 18652b2a8afSAKASHI Takahiro return -EINVAL; 18752b2a8afSAKASHI Takahiro 18852b2a8afSAKASHI Takahiro ret = setup_dtb(image, initrd_load_addr, initrd_len, 18952b2a8afSAKASHI Takahiro cmdline, buf); 19052b2a8afSAKASHI Takahiro if (ret) { 19152b2a8afSAKASHI Takahiro vfree(buf); 19252b2a8afSAKASHI Takahiro if (ret == -ENOMEM) { 19352b2a8afSAKASHI Takahiro /* unlikely, but just in case */ 19452b2a8afSAKASHI Takahiro buf_size += DTB_EXTRA_SPACE; 19552b2a8afSAKASHI Takahiro continue; 19652b2a8afSAKASHI Takahiro } else { 19752b2a8afSAKASHI Takahiro return ret; 19852b2a8afSAKASHI Takahiro } 19952b2a8afSAKASHI Takahiro } 20052b2a8afSAKASHI Takahiro 20152b2a8afSAKASHI Takahiro /* trim it */ 20252b2a8afSAKASHI Takahiro fdt_pack(buf); 20352b2a8afSAKASHI Takahiro *dtb = buf; 20452b2a8afSAKASHI Takahiro 20552b2a8afSAKASHI Takahiro return 0; 20652b2a8afSAKASHI Takahiro } 20752b2a8afSAKASHI Takahiro } 20852b2a8afSAKASHI Takahiro 2093751e728SAKASHI Takahiro static int prepare_elf_headers(void **addr, unsigned long *sz) 2103751e728SAKASHI Takahiro { 2113751e728SAKASHI Takahiro struct crash_mem *cmem; 2123751e728SAKASHI Takahiro unsigned int nr_ranges; 2133751e728SAKASHI Takahiro int ret; 2143751e728SAKASHI Takahiro u64 i; 2153751e728SAKASHI Takahiro phys_addr_t start, end; 2163751e728SAKASHI Takahiro 2173751e728SAKASHI Takahiro nr_ranges = 1; /* for exclusion of crashkernel region */ 2183751e728SAKASHI Takahiro for_each_mem_range(i, &memblock.memory, NULL, NUMA_NO_NODE, 2193751e728SAKASHI Takahiro MEMBLOCK_NONE, &start, &end, NULL) 2203751e728SAKASHI Takahiro nr_ranges++; 2213751e728SAKASHI Takahiro 222bf508ec9SGustavo A. R. Silva cmem = kmalloc(struct_size(cmem, ranges, nr_ranges), GFP_KERNEL); 2233751e728SAKASHI Takahiro if (!cmem) 2243751e728SAKASHI Takahiro return -ENOMEM; 2253751e728SAKASHI Takahiro 2263751e728SAKASHI Takahiro cmem->max_nr_ranges = nr_ranges; 2273751e728SAKASHI Takahiro cmem->nr_ranges = 0; 2283751e728SAKASHI Takahiro for_each_mem_range(i, &memblock.memory, NULL, NUMA_NO_NODE, 2293751e728SAKASHI Takahiro MEMBLOCK_NONE, &start, &end, NULL) { 2303751e728SAKASHI Takahiro cmem->ranges[cmem->nr_ranges].start = start; 2313751e728SAKASHI Takahiro cmem->ranges[cmem->nr_ranges].end = end - 1; 2323751e728SAKASHI Takahiro cmem->nr_ranges++; 2333751e728SAKASHI Takahiro } 2343751e728SAKASHI Takahiro 2353751e728SAKASHI Takahiro /* Exclude crashkernel region */ 2363751e728SAKASHI Takahiro ret = crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end); 2373751e728SAKASHI Takahiro 2383751e728SAKASHI Takahiro if (!ret) 2393751e728SAKASHI Takahiro ret = crash_prepare_elf64_headers(cmem, true, addr, sz); 2403751e728SAKASHI Takahiro 2413751e728SAKASHI Takahiro kfree(cmem); 2423751e728SAKASHI Takahiro return ret; 2433751e728SAKASHI Takahiro } 2443751e728SAKASHI Takahiro 24552b2a8afSAKASHI Takahiro int load_other_segments(struct kimage *image, 24652b2a8afSAKASHI Takahiro unsigned long kernel_load_addr, 24752b2a8afSAKASHI Takahiro unsigned long kernel_size, 24852b2a8afSAKASHI Takahiro char *initrd, unsigned long initrd_len, 24952b2a8afSAKASHI Takahiro char *cmdline) 25052b2a8afSAKASHI Takahiro { 25152b2a8afSAKASHI Takahiro struct kexec_buf kbuf; 2523751e728SAKASHI Takahiro void *headers, *dtb = NULL; 2533751e728SAKASHI Takahiro unsigned long headers_sz, initrd_load_addr = 0, dtb_len; 25452b2a8afSAKASHI Takahiro int ret = 0; 25552b2a8afSAKASHI Takahiro 25652b2a8afSAKASHI Takahiro kbuf.image = image; 25752b2a8afSAKASHI Takahiro /* not allocate anything below the kernel */ 25852b2a8afSAKASHI Takahiro kbuf.buf_min = kernel_load_addr + kernel_size; 25952b2a8afSAKASHI Takahiro 2603751e728SAKASHI Takahiro /* load elf core header */ 2613751e728SAKASHI Takahiro if (image->type == KEXEC_TYPE_CRASH) { 2623751e728SAKASHI Takahiro ret = prepare_elf_headers(&headers, &headers_sz); 2633751e728SAKASHI Takahiro if (ret) { 2643751e728SAKASHI Takahiro pr_err("Preparing elf core header failed\n"); 2653751e728SAKASHI Takahiro goto out_err; 2663751e728SAKASHI Takahiro } 2673751e728SAKASHI Takahiro 2683751e728SAKASHI Takahiro kbuf.buffer = headers; 2693751e728SAKASHI Takahiro kbuf.bufsz = headers_sz; 2703751e728SAKASHI Takahiro kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; 2713751e728SAKASHI Takahiro kbuf.memsz = headers_sz; 2723751e728SAKASHI Takahiro kbuf.buf_align = SZ_64K; /* largest supported page size */ 2733751e728SAKASHI Takahiro kbuf.buf_max = ULONG_MAX; 2743751e728SAKASHI Takahiro kbuf.top_down = true; 2753751e728SAKASHI Takahiro 2763751e728SAKASHI Takahiro ret = kexec_add_buffer(&kbuf); 2773751e728SAKASHI Takahiro if (ret) { 2783751e728SAKASHI Takahiro vfree(headers); 2793751e728SAKASHI Takahiro goto out_err; 2803751e728SAKASHI Takahiro } 2813751e728SAKASHI Takahiro image->arch.elf_headers = headers; 2823751e728SAKASHI Takahiro image->arch.elf_headers_mem = kbuf.mem; 2833751e728SAKASHI Takahiro image->arch.elf_headers_sz = headers_sz; 2843751e728SAKASHI Takahiro 2853751e728SAKASHI Takahiro pr_debug("Loaded elf core header at 0x%lx bufsz=0x%lx memsz=0x%lx\n", 28651075e0cSŁukasz Stelmach image->arch.elf_headers_mem, kbuf.bufsz, kbuf.memsz); 2873751e728SAKASHI Takahiro } 2883751e728SAKASHI Takahiro 28952b2a8afSAKASHI Takahiro /* load initrd */ 29052b2a8afSAKASHI Takahiro if (initrd) { 29152b2a8afSAKASHI Takahiro kbuf.buffer = initrd; 29252b2a8afSAKASHI Takahiro kbuf.bufsz = initrd_len; 293c19d050fSBhupesh Sharma kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; 29452b2a8afSAKASHI Takahiro kbuf.memsz = initrd_len; 29552b2a8afSAKASHI Takahiro kbuf.buf_align = 0; 29652b2a8afSAKASHI Takahiro /* within 1GB-aligned window of up to 32GB in size */ 29752b2a8afSAKASHI Takahiro kbuf.buf_max = round_down(kernel_load_addr, SZ_1G) 29852b2a8afSAKASHI Takahiro + (unsigned long)SZ_1G * 32; 29952b2a8afSAKASHI Takahiro kbuf.top_down = false; 30052b2a8afSAKASHI Takahiro 30152b2a8afSAKASHI Takahiro ret = kexec_add_buffer(&kbuf); 30252b2a8afSAKASHI Takahiro if (ret) 30352b2a8afSAKASHI Takahiro goto out_err; 30452b2a8afSAKASHI Takahiro initrd_load_addr = kbuf.mem; 30552b2a8afSAKASHI Takahiro 30652b2a8afSAKASHI Takahiro pr_debug("Loaded initrd at 0x%lx bufsz=0x%lx memsz=0x%lx\n", 30751075e0cSŁukasz Stelmach initrd_load_addr, kbuf.bufsz, kbuf.memsz); 30852b2a8afSAKASHI Takahiro } 30952b2a8afSAKASHI Takahiro 31052b2a8afSAKASHI Takahiro /* load dtb */ 31152b2a8afSAKASHI Takahiro ret = create_dtb(image, initrd_load_addr, initrd_len, cmdline, &dtb); 31252b2a8afSAKASHI Takahiro if (ret) { 31352b2a8afSAKASHI Takahiro pr_err("Preparing for new dtb failed\n"); 31452b2a8afSAKASHI Takahiro goto out_err; 31552b2a8afSAKASHI Takahiro } 31652b2a8afSAKASHI Takahiro 31752b2a8afSAKASHI Takahiro dtb_len = fdt_totalsize(dtb); 31852b2a8afSAKASHI Takahiro kbuf.buffer = dtb; 31952b2a8afSAKASHI Takahiro kbuf.bufsz = dtb_len; 320c19d050fSBhupesh Sharma kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; 32152b2a8afSAKASHI Takahiro kbuf.memsz = dtb_len; 32252b2a8afSAKASHI Takahiro /* not across 2MB boundary */ 32352b2a8afSAKASHI Takahiro kbuf.buf_align = SZ_2M; 32452b2a8afSAKASHI Takahiro kbuf.buf_max = ULONG_MAX; 32552b2a8afSAKASHI Takahiro kbuf.top_down = true; 32652b2a8afSAKASHI Takahiro 32752b2a8afSAKASHI Takahiro ret = kexec_add_buffer(&kbuf); 32852b2a8afSAKASHI Takahiro if (ret) 32952b2a8afSAKASHI Takahiro goto out_err; 33052b2a8afSAKASHI Takahiro image->arch.dtb = dtb; 33152b2a8afSAKASHI Takahiro image->arch.dtb_mem = kbuf.mem; 33252b2a8afSAKASHI Takahiro 33352b2a8afSAKASHI Takahiro pr_debug("Loaded dtb at 0x%lx bufsz=0x%lx memsz=0x%lx\n", 33451075e0cSŁukasz Stelmach kbuf.mem, kbuf.bufsz, kbuf.memsz); 33552b2a8afSAKASHI Takahiro 33652b2a8afSAKASHI Takahiro return 0; 33752b2a8afSAKASHI Takahiro 33852b2a8afSAKASHI Takahiro out_err: 33952b2a8afSAKASHI Takahiro vfree(dtb); 34052b2a8afSAKASHI Takahiro return ret; 34152b2a8afSAKASHI Takahiro } 342