18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * kexec: kexec_file_load system call 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014 Red Hat Inc. 68c2ecf20Sopenharmony_ci * Authors: 78c2ecf20Sopenharmony_ci * Vivek Goyal <vgoyal@redhat.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/capability.h> 138c2ecf20Sopenharmony_ci#include <linux/mm.h> 148c2ecf20Sopenharmony_ci#include <linux/file.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/kexec.h> 178c2ecf20Sopenharmony_ci#include <linux/memblock.h> 188c2ecf20Sopenharmony_ci#include <linux/mutex.h> 198c2ecf20Sopenharmony_ci#include <linux/list.h> 208c2ecf20Sopenharmony_ci#include <linux/fs.h> 218c2ecf20Sopenharmony_ci#include <linux/ima.h> 228c2ecf20Sopenharmony_ci#include <crypto/hash.h> 238c2ecf20Sopenharmony_ci#include <crypto/sha.h> 248c2ecf20Sopenharmony_ci#include <linux/elf.h> 258c2ecf20Sopenharmony_ci#include <linux/elfcore.h> 268c2ecf20Sopenharmony_ci#include <linux/kernel.h> 278c2ecf20Sopenharmony_ci#include <linux/kernel_read_file.h> 288c2ecf20Sopenharmony_ci#include <linux/syscalls.h> 298c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 308c2ecf20Sopenharmony_ci#include "kexec_internal.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#ifdef CONFIG_KEXEC_SIG 338c2ecf20Sopenharmony_cistatic bool sig_enforce = IS_ENABLED(CONFIG_KEXEC_SIG_FORCE); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_civoid set_kexec_sig_enforced(void) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci sig_enforce = true; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci#endif 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic int kexec_calculate_store_digests(struct kimage *image); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* 448c2ecf20Sopenharmony_ci * Currently this is the only default function that is exported as some 458c2ecf20Sopenharmony_ci * architectures need it to do additional handlings. 468c2ecf20Sopenharmony_ci * In the future, other default functions may be exported too if required. 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_ciint kexec_image_probe_default(struct kimage *image, void *buf, 498c2ecf20Sopenharmony_ci unsigned long buf_len) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci const struct kexec_file_ops * const *fops; 528c2ecf20Sopenharmony_ci int ret = -ENOEXEC; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci for (fops = &kexec_file_loaders[0]; *fops && (*fops)->probe; ++fops) { 558c2ecf20Sopenharmony_ci ret = (*fops)->probe(buf, buf_len); 568c2ecf20Sopenharmony_ci if (!ret) { 578c2ecf20Sopenharmony_ci image->fops = *fops; 588c2ecf20Sopenharmony_ci return ret; 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return ret; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* Architectures can provide this probe function */ 668c2ecf20Sopenharmony_ciint __weak arch_kexec_kernel_image_probe(struct kimage *image, void *buf, 678c2ecf20Sopenharmony_ci unsigned long buf_len) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci return kexec_image_probe_default(image, buf, buf_len); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic void *kexec_image_load_default(struct kimage *image) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci if (!image->fops || !image->fops->load) 758c2ecf20Sopenharmony_ci return ERR_PTR(-ENOEXEC); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return image->fops->load(image, image->kernel_buf, 788c2ecf20Sopenharmony_ci image->kernel_buf_len, image->initrd_buf, 798c2ecf20Sopenharmony_ci image->initrd_buf_len, image->cmdline_buf, 808c2ecf20Sopenharmony_ci image->cmdline_buf_len); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_civoid * __weak arch_kexec_kernel_image_load(struct kimage *image) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci return kexec_image_load_default(image); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ciint kexec_image_post_load_cleanup_default(struct kimage *image) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci if (!image->fops || !image->fops->cleanup) 918c2ecf20Sopenharmony_ci return 0; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return image->fops->cleanup(image->image_loader_data); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ciint __weak arch_kimage_file_post_load_cleanup(struct kimage *image) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci return kexec_image_post_load_cleanup_default(image); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci#ifdef CONFIG_KEXEC_SIG 1028c2ecf20Sopenharmony_cistatic int kexec_image_verify_sig_default(struct kimage *image, void *buf, 1038c2ecf20Sopenharmony_ci unsigned long buf_len) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci if (!image->fops || !image->fops->verify_sig) { 1068c2ecf20Sopenharmony_ci pr_debug("kernel loader does not support signature verification.\n"); 1078c2ecf20Sopenharmony_ci return -EKEYREJECTED; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return image->fops->verify_sig(buf, buf_len); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ciint __weak arch_kexec_kernel_verify_sig(struct kimage *image, void *buf, 1148c2ecf20Sopenharmony_ci unsigned long buf_len) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci return kexec_image_verify_sig_default(image, buf, buf_len); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci#endif 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/* 1218c2ecf20Sopenharmony_ci * Free up memory used by kernel, initrd, and command line. This is temporary 1228c2ecf20Sopenharmony_ci * memory allocation which is not needed any more after these buffers have 1238c2ecf20Sopenharmony_ci * been loaded into separate segments and have been copied elsewhere. 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_civoid kimage_file_post_load_cleanup(struct kimage *image) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct purgatory_info *pi = &image->purgatory_info; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci vfree(image->kernel_buf); 1308c2ecf20Sopenharmony_ci image->kernel_buf = NULL; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci vfree(image->initrd_buf); 1338c2ecf20Sopenharmony_ci image->initrd_buf = NULL; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci kfree(image->cmdline_buf); 1368c2ecf20Sopenharmony_ci image->cmdline_buf = NULL; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci vfree(pi->purgatory_buf); 1398c2ecf20Sopenharmony_ci pi->purgatory_buf = NULL; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci vfree(pi->sechdrs); 1428c2ecf20Sopenharmony_ci pi->sechdrs = NULL; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci#ifdef CONFIG_IMA_KEXEC 1458c2ecf20Sopenharmony_ci vfree(image->ima_buffer); 1468c2ecf20Sopenharmony_ci image->ima_buffer = NULL; 1478c2ecf20Sopenharmony_ci#endif /* CONFIG_IMA_KEXEC */ 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* See if architecture has anything to cleanup post load */ 1508c2ecf20Sopenharmony_ci arch_kimage_file_post_load_cleanup(image); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* 1538c2ecf20Sopenharmony_ci * Above call should have called into bootloader to free up 1548c2ecf20Sopenharmony_ci * any data stored in kimage->image_loader_data. It should 1558c2ecf20Sopenharmony_ci * be ok now to free it up. 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_ci kfree(image->image_loader_data); 1588c2ecf20Sopenharmony_ci image->image_loader_data = NULL; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci#ifdef CONFIG_KEXEC_SIG 1628c2ecf20Sopenharmony_cistatic int 1638c2ecf20Sopenharmony_cikimage_validate_signature(struct kimage *image) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci int ret; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci ret = arch_kexec_kernel_verify_sig(image, image->kernel_buf, 1688c2ecf20Sopenharmony_ci image->kernel_buf_len); 1698c2ecf20Sopenharmony_ci if (ret) { 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (sig_enforce) { 1728c2ecf20Sopenharmony_ci pr_notice("Enforced kernel signature verification failed (%d).\n", ret); 1738c2ecf20Sopenharmony_ci return ret; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* 1778c2ecf20Sopenharmony_ci * If IMA is guaranteed to appraise a signature on the kexec 1788c2ecf20Sopenharmony_ci * image, permit it even if the kernel is otherwise locked 1798c2ecf20Sopenharmony_ci * down. 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_ci if (!ima_appraise_signature(READING_KEXEC_IMAGE) && 1828c2ecf20Sopenharmony_ci security_locked_down(LOCKDOWN_KEXEC)) 1838c2ecf20Sopenharmony_ci return -EPERM; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci pr_debug("kernel signature verification failed (%d).\n", ret); 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return 0; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci#endif 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci/* 1938c2ecf20Sopenharmony_ci * In file mode list of segments is prepared by kernel. Copy relevant 1948c2ecf20Sopenharmony_ci * data from user space, do error checking, prepare segment list 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_cistatic int 1978c2ecf20Sopenharmony_cikimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd, 1988c2ecf20Sopenharmony_ci const char __user *cmdline_ptr, 1998c2ecf20Sopenharmony_ci unsigned long cmdline_len, unsigned flags) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci int ret; 2028c2ecf20Sopenharmony_ci void *ldata; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci ret = kernel_read_file_from_fd(kernel_fd, 0, &image->kernel_buf, 2058c2ecf20Sopenharmony_ci INT_MAX, NULL, READING_KEXEC_IMAGE); 2068c2ecf20Sopenharmony_ci if (ret < 0) 2078c2ecf20Sopenharmony_ci return ret; 2088c2ecf20Sopenharmony_ci image->kernel_buf_len = ret; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* Call arch image probe handlers */ 2118c2ecf20Sopenharmony_ci ret = arch_kexec_kernel_image_probe(image, image->kernel_buf, 2128c2ecf20Sopenharmony_ci image->kernel_buf_len); 2138c2ecf20Sopenharmony_ci if (ret) 2148c2ecf20Sopenharmony_ci goto out; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci#ifdef CONFIG_KEXEC_SIG 2178c2ecf20Sopenharmony_ci ret = kimage_validate_signature(image); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (ret) 2208c2ecf20Sopenharmony_ci goto out; 2218c2ecf20Sopenharmony_ci#endif 2228c2ecf20Sopenharmony_ci /* It is possible that there no initramfs is being loaded */ 2238c2ecf20Sopenharmony_ci if (!(flags & KEXEC_FILE_NO_INITRAMFS)) { 2248c2ecf20Sopenharmony_ci ret = kernel_read_file_from_fd(initrd_fd, 0, &image->initrd_buf, 2258c2ecf20Sopenharmony_ci INT_MAX, NULL, 2268c2ecf20Sopenharmony_ci READING_KEXEC_INITRAMFS); 2278c2ecf20Sopenharmony_ci if (ret < 0) 2288c2ecf20Sopenharmony_ci goto out; 2298c2ecf20Sopenharmony_ci image->initrd_buf_len = ret; 2308c2ecf20Sopenharmony_ci ret = 0; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (cmdline_len) { 2348c2ecf20Sopenharmony_ci image->cmdline_buf = memdup_user(cmdline_ptr, cmdline_len); 2358c2ecf20Sopenharmony_ci if (IS_ERR(image->cmdline_buf)) { 2368c2ecf20Sopenharmony_ci ret = PTR_ERR(image->cmdline_buf); 2378c2ecf20Sopenharmony_ci image->cmdline_buf = NULL; 2388c2ecf20Sopenharmony_ci goto out; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci image->cmdline_buf_len = cmdline_len; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* command line should be a string with last byte null */ 2448c2ecf20Sopenharmony_ci if (image->cmdline_buf[cmdline_len - 1] != '\0') { 2458c2ecf20Sopenharmony_ci ret = -EINVAL; 2468c2ecf20Sopenharmony_ci goto out; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci ima_kexec_cmdline(kernel_fd, image->cmdline_buf, 2508c2ecf20Sopenharmony_ci image->cmdline_buf_len - 1); 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* IMA needs to pass the measurement list to the next kernel. */ 2548c2ecf20Sopenharmony_ci ima_add_kexec_buffer(image); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* Call arch image load handlers */ 2578c2ecf20Sopenharmony_ci ldata = arch_kexec_kernel_image_load(image); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (IS_ERR(ldata)) { 2608c2ecf20Sopenharmony_ci ret = PTR_ERR(ldata); 2618c2ecf20Sopenharmony_ci goto out; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci image->image_loader_data = ldata; 2658c2ecf20Sopenharmony_ciout: 2668c2ecf20Sopenharmony_ci /* In case of error, free up all allocated memory in this function */ 2678c2ecf20Sopenharmony_ci if (ret) 2688c2ecf20Sopenharmony_ci kimage_file_post_load_cleanup(image); 2698c2ecf20Sopenharmony_ci return ret; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic int 2738c2ecf20Sopenharmony_cikimage_file_alloc_init(struct kimage **rimage, int kernel_fd, 2748c2ecf20Sopenharmony_ci int initrd_fd, const char __user *cmdline_ptr, 2758c2ecf20Sopenharmony_ci unsigned long cmdline_len, unsigned long flags) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci int ret; 2788c2ecf20Sopenharmony_ci struct kimage *image; 2798c2ecf20Sopenharmony_ci bool kexec_on_panic = flags & KEXEC_FILE_ON_CRASH; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci image = do_kimage_alloc_init(); 2828c2ecf20Sopenharmony_ci if (!image) 2838c2ecf20Sopenharmony_ci return -ENOMEM; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci image->file_mode = 1; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (kexec_on_panic) { 2888c2ecf20Sopenharmony_ci /* Enable special crash kernel control page alloc policy. */ 2898c2ecf20Sopenharmony_ci image->control_page = crashk_res.start; 2908c2ecf20Sopenharmony_ci image->type = KEXEC_TYPE_CRASH; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci ret = kimage_file_prepare_segments(image, kernel_fd, initrd_fd, 2948c2ecf20Sopenharmony_ci cmdline_ptr, cmdline_len, flags); 2958c2ecf20Sopenharmony_ci if (ret) 2968c2ecf20Sopenharmony_ci goto out_free_image; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci ret = sanity_check_segment_list(image); 2998c2ecf20Sopenharmony_ci if (ret) 3008c2ecf20Sopenharmony_ci goto out_free_post_load_bufs; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci ret = -ENOMEM; 3038c2ecf20Sopenharmony_ci image->control_code_page = kimage_alloc_control_pages(image, 3048c2ecf20Sopenharmony_ci get_order(KEXEC_CONTROL_PAGE_SIZE)); 3058c2ecf20Sopenharmony_ci if (!image->control_code_page) { 3068c2ecf20Sopenharmony_ci pr_err("Could not allocate control_code_buffer\n"); 3078c2ecf20Sopenharmony_ci goto out_free_post_load_bufs; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (!kexec_on_panic) { 3118c2ecf20Sopenharmony_ci image->swap_page = kimage_alloc_control_pages(image, 0); 3128c2ecf20Sopenharmony_ci if (!image->swap_page) { 3138c2ecf20Sopenharmony_ci pr_err("Could not allocate swap buffer\n"); 3148c2ecf20Sopenharmony_ci goto out_free_control_pages; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci *rimage = image; 3198c2ecf20Sopenharmony_ci return 0; 3208c2ecf20Sopenharmony_ciout_free_control_pages: 3218c2ecf20Sopenharmony_ci kimage_free_page_list(&image->control_pages); 3228c2ecf20Sopenharmony_ciout_free_post_load_bufs: 3238c2ecf20Sopenharmony_ci kimage_file_post_load_cleanup(image); 3248c2ecf20Sopenharmony_ciout_free_image: 3258c2ecf20Sopenharmony_ci kfree(image); 3268c2ecf20Sopenharmony_ci return ret; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ciSYSCALL_DEFINE5(kexec_file_load, int, kernel_fd, int, initrd_fd, 3308c2ecf20Sopenharmony_ci unsigned long, cmdline_len, const char __user *, cmdline_ptr, 3318c2ecf20Sopenharmony_ci unsigned long, flags) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci int ret = 0, i; 3348c2ecf20Sopenharmony_ci struct kimage **dest_image, *image; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* We only trust the superuser with rebooting the system. */ 3378c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_BOOT) || kexec_load_disabled) 3388c2ecf20Sopenharmony_ci return -EPERM; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* Make sure we have a legal set of flags */ 3418c2ecf20Sopenharmony_ci if (flags != (flags & KEXEC_FILE_FLAGS)) 3428c2ecf20Sopenharmony_ci return -EINVAL; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci image = NULL; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (!kexec_trylock()) 3478c2ecf20Sopenharmony_ci return -EBUSY; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci dest_image = &kexec_image; 3508c2ecf20Sopenharmony_ci if (flags & KEXEC_FILE_ON_CRASH) { 3518c2ecf20Sopenharmony_ci dest_image = &kexec_crash_image; 3528c2ecf20Sopenharmony_ci if (kexec_crash_image) 3538c2ecf20Sopenharmony_ci arch_kexec_unprotect_crashkres(); 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (flags & KEXEC_FILE_UNLOAD) 3578c2ecf20Sopenharmony_ci goto exchange; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* 3608c2ecf20Sopenharmony_ci * In case of crash, new kernel gets loaded in reserved region. It is 3618c2ecf20Sopenharmony_ci * same memory where old crash kernel might be loaded. Free any 3628c2ecf20Sopenharmony_ci * current crash dump kernel before we corrupt it. 3638c2ecf20Sopenharmony_ci */ 3648c2ecf20Sopenharmony_ci if (flags & KEXEC_FILE_ON_CRASH) 3658c2ecf20Sopenharmony_ci kimage_free(xchg(&kexec_crash_image, NULL)); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci ret = kimage_file_alloc_init(&image, kernel_fd, initrd_fd, cmdline_ptr, 3688c2ecf20Sopenharmony_ci cmdline_len, flags); 3698c2ecf20Sopenharmony_ci if (ret) 3708c2ecf20Sopenharmony_ci goto out; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci ret = machine_kexec_prepare(image); 3738c2ecf20Sopenharmony_ci if (ret) 3748c2ecf20Sopenharmony_ci goto out; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* 3778c2ecf20Sopenharmony_ci * Some architecture(like S390) may touch the crash memory before 3788c2ecf20Sopenharmony_ci * machine_kexec_prepare(), we must copy vmcoreinfo data after it. 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_ci ret = kimage_crash_copy_vmcoreinfo(image); 3818c2ecf20Sopenharmony_ci if (ret) 3828c2ecf20Sopenharmony_ci goto out; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci ret = kexec_calculate_store_digests(image); 3858c2ecf20Sopenharmony_ci if (ret) 3868c2ecf20Sopenharmony_ci goto out; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci for (i = 0; i < image->nr_segments; i++) { 3898c2ecf20Sopenharmony_ci struct kexec_segment *ksegment; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci ksegment = &image->segment[i]; 3928c2ecf20Sopenharmony_ci pr_debug("Loading segment %d: buf=0x%p bufsz=0x%zx mem=0x%lx memsz=0x%zx\n", 3938c2ecf20Sopenharmony_ci i, ksegment->buf, ksegment->bufsz, ksegment->mem, 3948c2ecf20Sopenharmony_ci ksegment->memsz); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci ret = kimage_load_segment(image, &image->segment[i]); 3978c2ecf20Sopenharmony_ci if (ret) 3988c2ecf20Sopenharmony_ci goto out; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci kimage_terminate(image); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci ret = machine_kexec_post_load(image); 4048c2ecf20Sopenharmony_ci if (ret) 4058c2ecf20Sopenharmony_ci goto out; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* 4088c2ecf20Sopenharmony_ci * Free up any temporary buffers allocated which are not needed 4098c2ecf20Sopenharmony_ci * after image has been loaded 4108c2ecf20Sopenharmony_ci */ 4118c2ecf20Sopenharmony_ci kimage_file_post_load_cleanup(image); 4128c2ecf20Sopenharmony_ciexchange: 4138c2ecf20Sopenharmony_ci image = xchg(dest_image, image); 4148c2ecf20Sopenharmony_ciout: 4158c2ecf20Sopenharmony_ci if ((flags & KEXEC_FILE_ON_CRASH) && kexec_crash_image) 4168c2ecf20Sopenharmony_ci arch_kexec_protect_crashkres(); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci kexec_unlock(); 4198c2ecf20Sopenharmony_ci kimage_free(image); 4208c2ecf20Sopenharmony_ci return ret; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic int locate_mem_hole_top_down(unsigned long start, unsigned long end, 4248c2ecf20Sopenharmony_ci struct kexec_buf *kbuf) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci struct kimage *image = kbuf->image; 4278c2ecf20Sopenharmony_ci unsigned long temp_start, temp_end; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci temp_end = min(end, kbuf->buf_max); 4308c2ecf20Sopenharmony_ci temp_start = temp_end - kbuf->memsz; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci do { 4338c2ecf20Sopenharmony_ci /* align down start */ 4348c2ecf20Sopenharmony_ci temp_start = temp_start & (~(kbuf->buf_align - 1)); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (temp_start < start || temp_start < kbuf->buf_min) 4378c2ecf20Sopenharmony_ci return 0; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci temp_end = temp_start + kbuf->memsz - 1; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* 4428c2ecf20Sopenharmony_ci * Make sure this does not conflict with any of existing 4438c2ecf20Sopenharmony_ci * segments 4448c2ecf20Sopenharmony_ci */ 4458c2ecf20Sopenharmony_ci if (kimage_is_destination_range(image, temp_start, temp_end)) { 4468c2ecf20Sopenharmony_ci temp_start = temp_start - PAGE_SIZE; 4478c2ecf20Sopenharmony_ci continue; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* We found a suitable memory range */ 4518c2ecf20Sopenharmony_ci break; 4528c2ecf20Sopenharmony_ci } while (1); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci /* If we are here, we found a suitable memory range */ 4558c2ecf20Sopenharmony_ci kbuf->mem = temp_start; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* Success, stop navigating through remaining System RAM ranges */ 4588c2ecf20Sopenharmony_ci return 1; 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic int locate_mem_hole_bottom_up(unsigned long start, unsigned long end, 4628c2ecf20Sopenharmony_ci struct kexec_buf *kbuf) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci struct kimage *image = kbuf->image; 4658c2ecf20Sopenharmony_ci unsigned long temp_start, temp_end; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci temp_start = max(start, kbuf->buf_min); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci do { 4708c2ecf20Sopenharmony_ci temp_start = ALIGN(temp_start, kbuf->buf_align); 4718c2ecf20Sopenharmony_ci temp_end = temp_start + kbuf->memsz - 1; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci if (temp_end > end || temp_end > kbuf->buf_max) 4748c2ecf20Sopenharmony_ci return 0; 4758c2ecf20Sopenharmony_ci /* 4768c2ecf20Sopenharmony_ci * Make sure this does not conflict with any of existing 4778c2ecf20Sopenharmony_ci * segments 4788c2ecf20Sopenharmony_ci */ 4798c2ecf20Sopenharmony_ci if (kimage_is_destination_range(image, temp_start, temp_end)) { 4808c2ecf20Sopenharmony_ci temp_start = temp_start + PAGE_SIZE; 4818c2ecf20Sopenharmony_ci continue; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* We found a suitable memory range */ 4858c2ecf20Sopenharmony_ci break; 4868c2ecf20Sopenharmony_ci } while (1); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci /* If we are here, we found a suitable memory range */ 4898c2ecf20Sopenharmony_ci kbuf->mem = temp_start; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* Success, stop navigating through remaining System RAM ranges */ 4928c2ecf20Sopenharmony_ci return 1; 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic int locate_mem_hole_callback(struct resource *res, void *arg) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci struct kexec_buf *kbuf = (struct kexec_buf *)arg; 4988c2ecf20Sopenharmony_ci u64 start = res->start, end = res->end; 4998c2ecf20Sopenharmony_ci unsigned long sz = end - start + 1; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* Returning 0 will take to next memory range */ 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci /* Don't use memory that will be detected and handled by a driver. */ 5048c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_SYSRAM_DRIVER_MANAGED) 5058c2ecf20Sopenharmony_ci return 0; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci if (sz < kbuf->memsz) 5088c2ecf20Sopenharmony_ci return 0; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (end < kbuf->buf_min || start > kbuf->buf_max) 5118c2ecf20Sopenharmony_ci return 0; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci /* 5148c2ecf20Sopenharmony_ci * Allocate memory top down with-in ram range. Otherwise bottom up 5158c2ecf20Sopenharmony_ci * allocation. 5168c2ecf20Sopenharmony_ci */ 5178c2ecf20Sopenharmony_ci if (kbuf->top_down) 5188c2ecf20Sopenharmony_ci return locate_mem_hole_top_down(start, end, kbuf); 5198c2ecf20Sopenharmony_ci return locate_mem_hole_bottom_up(start, end, kbuf); 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_KEEP_MEMBLOCK 5238c2ecf20Sopenharmony_cistatic int kexec_walk_memblock(struct kexec_buf *kbuf, 5248c2ecf20Sopenharmony_ci int (*func)(struct resource *, void *)) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci int ret = 0; 5278c2ecf20Sopenharmony_ci u64 i; 5288c2ecf20Sopenharmony_ci phys_addr_t mstart, mend; 5298c2ecf20Sopenharmony_ci struct resource res = { }; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (kbuf->image->type == KEXEC_TYPE_CRASH) 5328c2ecf20Sopenharmony_ci return func(&crashk_res, kbuf); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci if (kbuf->top_down) { 5358c2ecf20Sopenharmony_ci for_each_free_mem_range_reverse(i, NUMA_NO_NODE, MEMBLOCK_NONE, 5368c2ecf20Sopenharmony_ci &mstart, &mend, NULL) { 5378c2ecf20Sopenharmony_ci /* 5388c2ecf20Sopenharmony_ci * In memblock, end points to the first byte after the 5398c2ecf20Sopenharmony_ci * range while in kexec, end points to the last byte 5408c2ecf20Sopenharmony_ci * in the range. 5418c2ecf20Sopenharmony_ci */ 5428c2ecf20Sopenharmony_ci res.start = mstart; 5438c2ecf20Sopenharmony_ci res.end = mend - 1; 5448c2ecf20Sopenharmony_ci ret = func(&res, kbuf); 5458c2ecf20Sopenharmony_ci if (ret) 5468c2ecf20Sopenharmony_ci break; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci } else { 5498c2ecf20Sopenharmony_ci for_each_free_mem_range(i, NUMA_NO_NODE, MEMBLOCK_NONE, 5508c2ecf20Sopenharmony_ci &mstart, &mend, NULL) { 5518c2ecf20Sopenharmony_ci /* 5528c2ecf20Sopenharmony_ci * In memblock, end points to the first byte after the 5538c2ecf20Sopenharmony_ci * range while in kexec, end points to the last byte 5548c2ecf20Sopenharmony_ci * in the range. 5558c2ecf20Sopenharmony_ci */ 5568c2ecf20Sopenharmony_ci res.start = mstart; 5578c2ecf20Sopenharmony_ci res.end = mend - 1; 5588c2ecf20Sopenharmony_ci ret = func(&res, kbuf); 5598c2ecf20Sopenharmony_ci if (ret) 5608c2ecf20Sopenharmony_ci break; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci return ret; 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci#else 5678c2ecf20Sopenharmony_cistatic int kexec_walk_memblock(struct kexec_buf *kbuf, 5688c2ecf20Sopenharmony_ci int (*func)(struct resource *, void *)) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci return 0; 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci#endif 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci/** 5758c2ecf20Sopenharmony_ci * kexec_walk_resources - call func(data) on free memory regions 5768c2ecf20Sopenharmony_ci * @kbuf: Context info for the search. Also passed to @func. 5778c2ecf20Sopenharmony_ci * @func: Function to call for each memory region. 5788c2ecf20Sopenharmony_ci * 5798c2ecf20Sopenharmony_ci * Return: The memory walk will stop when func returns a non-zero value 5808c2ecf20Sopenharmony_ci * and that value will be returned. If all free regions are visited without 5818c2ecf20Sopenharmony_ci * func returning non-zero, then zero will be returned. 5828c2ecf20Sopenharmony_ci */ 5838c2ecf20Sopenharmony_cistatic int kexec_walk_resources(struct kexec_buf *kbuf, 5848c2ecf20Sopenharmony_ci int (*func)(struct resource *, void *)) 5858c2ecf20Sopenharmony_ci{ 5868c2ecf20Sopenharmony_ci if (kbuf->image->type == KEXEC_TYPE_CRASH) 5878c2ecf20Sopenharmony_ci return walk_iomem_res_desc(crashk_res.desc, 5888c2ecf20Sopenharmony_ci IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY, 5898c2ecf20Sopenharmony_ci crashk_res.start, crashk_res.end, 5908c2ecf20Sopenharmony_ci kbuf, func); 5918c2ecf20Sopenharmony_ci else 5928c2ecf20Sopenharmony_ci return walk_system_ram_res(0, ULONG_MAX, kbuf, func); 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci/** 5968c2ecf20Sopenharmony_ci * kexec_locate_mem_hole - find free memory for the purgatory or the next kernel 5978c2ecf20Sopenharmony_ci * @kbuf: Parameters for the memory search. 5988c2ecf20Sopenharmony_ci * 5998c2ecf20Sopenharmony_ci * On success, kbuf->mem will have the start address of the memory region found. 6008c2ecf20Sopenharmony_ci * 6018c2ecf20Sopenharmony_ci * Return: 0 on success, negative errno on error. 6028c2ecf20Sopenharmony_ci */ 6038c2ecf20Sopenharmony_ciint kexec_locate_mem_hole(struct kexec_buf *kbuf) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci int ret; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci /* Arch knows where to place */ 6088c2ecf20Sopenharmony_ci if (kbuf->mem != KEXEC_BUF_MEM_UNKNOWN) 6098c2ecf20Sopenharmony_ci return 0; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_ARCH_KEEP_MEMBLOCK)) 6128c2ecf20Sopenharmony_ci ret = kexec_walk_resources(kbuf, locate_mem_hole_callback); 6138c2ecf20Sopenharmony_ci else 6148c2ecf20Sopenharmony_ci ret = kexec_walk_memblock(kbuf, locate_mem_hole_callback); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci return ret == 1 ? 0 : -EADDRNOTAVAIL; 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci/** 6208c2ecf20Sopenharmony_ci * arch_kexec_locate_mem_hole - Find free memory to place the segments. 6218c2ecf20Sopenharmony_ci * @kbuf: Parameters for the memory search. 6228c2ecf20Sopenharmony_ci * 6238c2ecf20Sopenharmony_ci * On success, kbuf->mem will have the start address of the memory region found. 6248c2ecf20Sopenharmony_ci * 6258c2ecf20Sopenharmony_ci * Return: 0 on success, negative errno on error. 6268c2ecf20Sopenharmony_ci */ 6278c2ecf20Sopenharmony_ciint __weak arch_kexec_locate_mem_hole(struct kexec_buf *kbuf) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci return kexec_locate_mem_hole(kbuf); 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci/** 6338c2ecf20Sopenharmony_ci * kexec_add_buffer - place a buffer in a kexec segment 6348c2ecf20Sopenharmony_ci * @kbuf: Buffer contents and memory parameters. 6358c2ecf20Sopenharmony_ci * 6368c2ecf20Sopenharmony_ci * This function assumes that kexec_mutex is held. 6378c2ecf20Sopenharmony_ci * On successful return, @kbuf->mem will have the physical address of 6388c2ecf20Sopenharmony_ci * the buffer in memory. 6398c2ecf20Sopenharmony_ci * 6408c2ecf20Sopenharmony_ci * Return: 0 on success, negative errno on error. 6418c2ecf20Sopenharmony_ci */ 6428c2ecf20Sopenharmony_ciint kexec_add_buffer(struct kexec_buf *kbuf) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci struct kexec_segment *ksegment; 6458c2ecf20Sopenharmony_ci int ret; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci /* Currently adding segment this way is allowed only in file mode */ 6488c2ecf20Sopenharmony_ci if (!kbuf->image->file_mode) 6498c2ecf20Sopenharmony_ci return -EINVAL; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci if (kbuf->image->nr_segments >= KEXEC_SEGMENT_MAX) 6528c2ecf20Sopenharmony_ci return -EINVAL; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci /* 6558c2ecf20Sopenharmony_ci * Make sure we are not trying to add buffer after allocating 6568c2ecf20Sopenharmony_ci * control pages. All segments need to be placed first before 6578c2ecf20Sopenharmony_ci * any control pages are allocated. As control page allocation 6588c2ecf20Sopenharmony_ci * logic goes through list of segments to make sure there are 6598c2ecf20Sopenharmony_ci * no destination overlaps. 6608c2ecf20Sopenharmony_ci */ 6618c2ecf20Sopenharmony_ci if (!list_empty(&kbuf->image->control_pages)) { 6628c2ecf20Sopenharmony_ci WARN_ON(1); 6638c2ecf20Sopenharmony_ci return -EINVAL; 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci /* Ensure minimum alignment needed for segments. */ 6678c2ecf20Sopenharmony_ci kbuf->memsz = ALIGN(kbuf->memsz, PAGE_SIZE); 6688c2ecf20Sopenharmony_ci kbuf->buf_align = max(kbuf->buf_align, PAGE_SIZE); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci /* Walk the RAM ranges and allocate a suitable range for the buffer */ 6718c2ecf20Sopenharmony_ci ret = arch_kexec_locate_mem_hole(kbuf); 6728c2ecf20Sopenharmony_ci if (ret) 6738c2ecf20Sopenharmony_ci return ret; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci /* Found a suitable memory range */ 6768c2ecf20Sopenharmony_ci ksegment = &kbuf->image->segment[kbuf->image->nr_segments]; 6778c2ecf20Sopenharmony_ci ksegment->kbuf = kbuf->buffer; 6788c2ecf20Sopenharmony_ci ksegment->bufsz = kbuf->bufsz; 6798c2ecf20Sopenharmony_ci ksegment->mem = kbuf->mem; 6808c2ecf20Sopenharmony_ci ksegment->memsz = kbuf->memsz; 6818c2ecf20Sopenharmony_ci kbuf->image->nr_segments++; 6828c2ecf20Sopenharmony_ci return 0; 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci/* Calculate and store the digest of segments */ 6868c2ecf20Sopenharmony_cistatic int kexec_calculate_store_digests(struct kimage *image) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci struct crypto_shash *tfm; 6898c2ecf20Sopenharmony_ci struct shash_desc *desc; 6908c2ecf20Sopenharmony_ci int ret = 0, i, j, zero_buf_sz, sha_region_sz; 6918c2ecf20Sopenharmony_ci size_t desc_size, nullsz; 6928c2ecf20Sopenharmony_ci char *digest; 6938c2ecf20Sopenharmony_ci void *zero_buf; 6948c2ecf20Sopenharmony_ci struct kexec_sha_region *sha_regions; 6958c2ecf20Sopenharmony_ci struct purgatory_info *pi = &image->purgatory_info; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_ARCH_HAS_KEXEC_PURGATORY)) 6988c2ecf20Sopenharmony_ci return 0; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci zero_buf = __va(page_to_pfn(ZERO_PAGE(0)) << PAGE_SHIFT); 7018c2ecf20Sopenharmony_ci zero_buf_sz = PAGE_SIZE; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci tfm = crypto_alloc_shash("sha256", 0, 0); 7048c2ecf20Sopenharmony_ci if (IS_ERR(tfm)) { 7058c2ecf20Sopenharmony_ci ret = PTR_ERR(tfm); 7068c2ecf20Sopenharmony_ci goto out; 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); 7108c2ecf20Sopenharmony_ci desc = kzalloc(desc_size, GFP_KERNEL); 7118c2ecf20Sopenharmony_ci if (!desc) { 7128c2ecf20Sopenharmony_ci ret = -ENOMEM; 7138c2ecf20Sopenharmony_ci goto out_free_tfm; 7148c2ecf20Sopenharmony_ci } 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci sha_region_sz = KEXEC_SEGMENT_MAX * sizeof(struct kexec_sha_region); 7178c2ecf20Sopenharmony_ci sha_regions = vzalloc(sha_region_sz); 7188c2ecf20Sopenharmony_ci if (!sha_regions) { 7198c2ecf20Sopenharmony_ci ret = -ENOMEM; 7208c2ecf20Sopenharmony_ci goto out_free_desc; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci desc->tfm = tfm; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci ret = crypto_shash_init(desc); 7268c2ecf20Sopenharmony_ci if (ret < 0) 7278c2ecf20Sopenharmony_ci goto out_free_sha_regions; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci digest = kzalloc(SHA256_DIGEST_SIZE, GFP_KERNEL); 7308c2ecf20Sopenharmony_ci if (!digest) { 7318c2ecf20Sopenharmony_ci ret = -ENOMEM; 7328c2ecf20Sopenharmony_ci goto out_free_sha_regions; 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci for (j = i = 0; i < image->nr_segments; i++) { 7368c2ecf20Sopenharmony_ci struct kexec_segment *ksegment; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci ksegment = &image->segment[i]; 7398c2ecf20Sopenharmony_ci /* 7408c2ecf20Sopenharmony_ci * Skip purgatory as it will be modified once we put digest 7418c2ecf20Sopenharmony_ci * info in purgatory. 7428c2ecf20Sopenharmony_ci */ 7438c2ecf20Sopenharmony_ci if (ksegment->kbuf == pi->purgatory_buf) 7448c2ecf20Sopenharmony_ci continue; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci ret = crypto_shash_update(desc, ksegment->kbuf, 7478c2ecf20Sopenharmony_ci ksegment->bufsz); 7488c2ecf20Sopenharmony_ci if (ret) 7498c2ecf20Sopenharmony_ci break; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci /* 7528c2ecf20Sopenharmony_ci * Assume rest of the buffer is filled with zero and 7538c2ecf20Sopenharmony_ci * update digest accordingly. 7548c2ecf20Sopenharmony_ci */ 7558c2ecf20Sopenharmony_ci nullsz = ksegment->memsz - ksegment->bufsz; 7568c2ecf20Sopenharmony_ci while (nullsz) { 7578c2ecf20Sopenharmony_ci unsigned long bytes = nullsz; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci if (bytes > zero_buf_sz) 7608c2ecf20Sopenharmony_ci bytes = zero_buf_sz; 7618c2ecf20Sopenharmony_ci ret = crypto_shash_update(desc, zero_buf, bytes); 7628c2ecf20Sopenharmony_ci if (ret) 7638c2ecf20Sopenharmony_ci break; 7648c2ecf20Sopenharmony_ci nullsz -= bytes; 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci if (ret) 7688c2ecf20Sopenharmony_ci break; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci sha_regions[j].start = ksegment->mem; 7718c2ecf20Sopenharmony_ci sha_regions[j].len = ksegment->memsz; 7728c2ecf20Sopenharmony_ci j++; 7738c2ecf20Sopenharmony_ci } 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci if (!ret) { 7768c2ecf20Sopenharmony_ci ret = crypto_shash_final(desc, digest); 7778c2ecf20Sopenharmony_ci if (ret) 7788c2ecf20Sopenharmony_ci goto out_free_digest; 7798c2ecf20Sopenharmony_ci ret = kexec_purgatory_get_set_symbol(image, "purgatory_sha_regions", 7808c2ecf20Sopenharmony_ci sha_regions, sha_region_sz, 0); 7818c2ecf20Sopenharmony_ci if (ret) 7828c2ecf20Sopenharmony_ci goto out_free_digest; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci ret = kexec_purgatory_get_set_symbol(image, "purgatory_sha256_digest", 7858c2ecf20Sopenharmony_ci digest, SHA256_DIGEST_SIZE, 0); 7868c2ecf20Sopenharmony_ci if (ret) 7878c2ecf20Sopenharmony_ci goto out_free_digest; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ciout_free_digest: 7918c2ecf20Sopenharmony_ci kfree(digest); 7928c2ecf20Sopenharmony_ciout_free_sha_regions: 7938c2ecf20Sopenharmony_ci vfree(sha_regions); 7948c2ecf20Sopenharmony_ciout_free_desc: 7958c2ecf20Sopenharmony_ci kfree(desc); 7968c2ecf20Sopenharmony_ciout_free_tfm: 7978c2ecf20Sopenharmony_ci kfree(tfm); 7988c2ecf20Sopenharmony_ciout: 7998c2ecf20Sopenharmony_ci return ret; 8008c2ecf20Sopenharmony_ci} 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_HAS_KEXEC_PURGATORY 8038c2ecf20Sopenharmony_ci/* 8048c2ecf20Sopenharmony_ci * kexec_purgatory_setup_kbuf - prepare buffer to load purgatory. 8058c2ecf20Sopenharmony_ci * @pi: Purgatory to be loaded. 8068c2ecf20Sopenharmony_ci * @kbuf: Buffer to setup. 8078c2ecf20Sopenharmony_ci * 8088c2ecf20Sopenharmony_ci * Allocates the memory needed for the buffer. Caller is responsible to free 8098c2ecf20Sopenharmony_ci * the memory after use. 8108c2ecf20Sopenharmony_ci * 8118c2ecf20Sopenharmony_ci * Return: 0 on success, negative errno on error. 8128c2ecf20Sopenharmony_ci */ 8138c2ecf20Sopenharmony_cistatic int kexec_purgatory_setup_kbuf(struct purgatory_info *pi, 8148c2ecf20Sopenharmony_ci struct kexec_buf *kbuf) 8158c2ecf20Sopenharmony_ci{ 8168c2ecf20Sopenharmony_ci const Elf_Shdr *sechdrs; 8178c2ecf20Sopenharmony_ci unsigned long bss_align; 8188c2ecf20Sopenharmony_ci unsigned long bss_sz; 8198c2ecf20Sopenharmony_ci unsigned long align; 8208c2ecf20Sopenharmony_ci int i, ret; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci sechdrs = (void *)pi->ehdr + pi->ehdr->e_shoff; 8238c2ecf20Sopenharmony_ci kbuf->buf_align = bss_align = 1; 8248c2ecf20Sopenharmony_ci kbuf->bufsz = bss_sz = 0; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci for (i = 0; i < pi->ehdr->e_shnum; i++) { 8278c2ecf20Sopenharmony_ci if (!(sechdrs[i].sh_flags & SHF_ALLOC)) 8288c2ecf20Sopenharmony_ci continue; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci align = sechdrs[i].sh_addralign; 8318c2ecf20Sopenharmony_ci if (sechdrs[i].sh_type != SHT_NOBITS) { 8328c2ecf20Sopenharmony_ci if (kbuf->buf_align < align) 8338c2ecf20Sopenharmony_ci kbuf->buf_align = align; 8348c2ecf20Sopenharmony_ci kbuf->bufsz = ALIGN(kbuf->bufsz, align); 8358c2ecf20Sopenharmony_ci kbuf->bufsz += sechdrs[i].sh_size; 8368c2ecf20Sopenharmony_ci } else { 8378c2ecf20Sopenharmony_ci if (bss_align < align) 8388c2ecf20Sopenharmony_ci bss_align = align; 8398c2ecf20Sopenharmony_ci bss_sz = ALIGN(bss_sz, align); 8408c2ecf20Sopenharmony_ci bss_sz += sechdrs[i].sh_size; 8418c2ecf20Sopenharmony_ci } 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci kbuf->bufsz = ALIGN(kbuf->bufsz, bss_align); 8448c2ecf20Sopenharmony_ci kbuf->memsz = kbuf->bufsz + bss_sz; 8458c2ecf20Sopenharmony_ci if (kbuf->buf_align < bss_align) 8468c2ecf20Sopenharmony_ci kbuf->buf_align = bss_align; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci kbuf->buffer = vzalloc(kbuf->bufsz); 8498c2ecf20Sopenharmony_ci if (!kbuf->buffer) 8508c2ecf20Sopenharmony_ci return -ENOMEM; 8518c2ecf20Sopenharmony_ci pi->purgatory_buf = kbuf->buffer; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci ret = kexec_add_buffer(kbuf); 8548c2ecf20Sopenharmony_ci if (ret) 8558c2ecf20Sopenharmony_ci goto out; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci return 0; 8588c2ecf20Sopenharmony_ciout: 8598c2ecf20Sopenharmony_ci vfree(pi->purgatory_buf); 8608c2ecf20Sopenharmony_ci pi->purgatory_buf = NULL; 8618c2ecf20Sopenharmony_ci return ret; 8628c2ecf20Sopenharmony_ci} 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci/* 8658c2ecf20Sopenharmony_ci * kexec_purgatory_setup_sechdrs - prepares the pi->sechdrs buffer. 8668c2ecf20Sopenharmony_ci * @pi: Purgatory to be loaded. 8678c2ecf20Sopenharmony_ci * @kbuf: Buffer prepared to store purgatory. 8688c2ecf20Sopenharmony_ci * 8698c2ecf20Sopenharmony_ci * Allocates the memory needed for the buffer. Caller is responsible to free 8708c2ecf20Sopenharmony_ci * the memory after use. 8718c2ecf20Sopenharmony_ci * 8728c2ecf20Sopenharmony_ci * Return: 0 on success, negative errno on error. 8738c2ecf20Sopenharmony_ci */ 8748c2ecf20Sopenharmony_cistatic int kexec_purgatory_setup_sechdrs(struct purgatory_info *pi, 8758c2ecf20Sopenharmony_ci struct kexec_buf *kbuf) 8768c2ecf20Sopenharmony_ci{ 8778c2ecf20Sopenharmony_ci unsigned long bss_addr; 8788c2ecf20Sopenharmony_ci unsigned long offset; 8798c2ecf20Sopenharmony_ci Elf_Shdr *sechdrs; 8808c2ecf20Sopenharmony_ci int i; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci /* 8838c2ecf20Sopenharmony_ci * The section headers in kexec_purgatory are read-only. In order to 8848c2ecf20Sopenharmony_ci * have them modifiable make a temporary copy. 8858c2ecf20Sopenharmony_ci */ 8868c2ecf20Sopenharmony_ci sechdrs = vzalloc(array_size(sizeof(Elf_Shdr), pi->ehdr->e_shnum)); 8878c2ecf20Sopenharmony_ci if (!sechdrs) 8888c2ecf20Sopenharmony_ci return -ENOMEM; 8898c2ecf20Sopenharmony_ci memcpy(sechdrs, (void *)pi->ehdr + pi->ehdr->e_shoff, 8908c2ecf20Sopenharmony_ci pi->ehdr->e_shnum * sizeof(Elf_Shdr)); 8918c2ecf20Sopenharmony_ci pi->sechdrs = sechdrs; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci offset = 0; 8948c2ecf20Sopenharmony_ci bss_addr = kbuf->mem + kbuf->bufsz; 8958c2ecf20Sopenharmony_ci kbuf->image->start = pi->ehdr->e_entry; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci for (i = 0; i < pi->ehdr->e_shnum; i++) { 8988c2ecf20Sopenharmony_ci unsigned long align; 8998c2ecf20Sopenharmony_ci void *src, *dst; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci if (!(sechdrs[i].sh_flags & SHF_ALLOC)) 9028c2ecf20Sopenharmony_ci continue; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci align = sechdrs[i].sh_addralign; 9058c2ecf20Sopenharmony_ci if (sechdrs[i].sh_type == SHT_NOBITS) { 9068c2ecf20Sopenharmony_ci bss_addr = ALIGN(bss_addr, align); 9078c2ecf20Sopenharmony_ci sechdrs[i].sh_addr = bss_addr; 9088c2ecf20Sopenharmony_ci bss_addr += sechdrs[i].sh_size; 9098c2ecf20Sopenharmony_ci continue; 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci offset = ALIGN(offset, align); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci /* 9158c2ecf20Sopenharmony_ci * Check if the segment contains the entry point, if so, 9168c2ecf20Sopenharmony_ci * calculate the value of image->start based on it. 9178c2ecf20Sopenharmony_ci * If the compiler has produced more than one .text section 9188c2ecf20Sopenharmony_ci * (Eg: .text.hot), they are generally after the main .text 9198c2ecf20Sopenharmony_ci * section, and they shall not be used to calculate 9208c2ecf20Sopenharmony_ci * image->start. So do not re-calculate image->start if it 9218c2ecf20Sopenharmony_ci * is not set to the initial value, and warn the user so they 9228c2ecf20Sopenharmony_ci * have a chance to fix their purgatory's linker script. 9238c2ecf20Sopenharmony_ci */ 9248c2ecf20Sopenharmony_ci if (sechdrs[i].sh_flags & SHF_EXECINSTR && 9258c2ecf20Sopenharmony_ci pi->ehdr->e_entry >= sechdrs[i].sh_addr && 9268c2ecf20Sopenharmony_ci pi->ehdr->e_entry < (sechdrs[i].sh_addr 9278c2ecf20Sopenharmony_ci + sechdrs[i].sh_size) && 9288c2ecf20Sopenharmony_ci !WARN_ON(kbuf->image->start != pi->ehdr->e_entry)) { 9298c2ecf20Sopenharmony_ci kbuf->image->start -= sechdrs[i].sh_addr; 9308c2ecf20Sopenharmony_ci kbuf->image->start += kbuf->mem + offset; 9318c2ecf20Sopenharmony_ci } 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci src = (void *)pi->ehdr + sechdrs[i].sh_offset; 9348c2ecf20Sopenharmony_ci dst = pi->purgatory_buf + offset; 9358c2ecf20Sopenharmony_ci memcpy(dst, src, sechdrs[i].sh_size); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci sechdrs[i].sh_addr = kbuf->mem + offset; 9388c2ecf20Sopenharmony_ci sechdrs[i].sh_offset = offset; 9398c2ecf20Sopenharmony_ci offset += sechdrs[i].sh_size; 9408c2ecf20Sopenharmony_ci } 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci return 0; 9438c2ecf20Sopenharmony_ci} 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_cistatic int kexec_apply_relocations(struct kimage *image) 9468c2ecf20Sopenharmony_ci{ 9478c2ecf20Sopenharmony_ci int i, ret; 9488c2ecf20Sopenharmony_ci struct purgatory_info *pi = &image->purgatory_info; 9498c2ecf20Sopenharmony_ci const Elf_Shdr *sechdrs; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci sechdrs = (void *)pi->ehdr + pi->ehdr->e_shoff; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci for (i = 0; i < pi->ehdr->e_shnum; i++) { 9548c2ecf20Sopenharmony_ci const Elf_Shdr *relsec; 9558c2ecf20Sopenharmony_ci const Elf_Shdr *symtab; 9568c2ecf20Sopenharmony_ci Elf_Shdr *section; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci relsec = sechdrs + i; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci if (relsec->sh_type != SHT_RELA && 9618c2ecf20Sopenharmony_ci relsec->sh_type != SHT_REL) 9628c2ecf20Sopenharmony_ci continue; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci /* 9658c2ecf20Sopenharmony_ci * For section of type SHT_RELA/SHT_REL, 9668c2ecf20Sopenharmony_ci * ->sh_link contains section header index of associated 9678c2ecf20Sopenharmony_ci * symbol table. And ->sh_info contains section header 9688c2ecf20Sopenharmony_ci * index of section to which relocations apply. 9698c2ecf20Sopenharmony_ci */ 9708c2ecf20Sopenharmony_ci if (relsec->sh_info >= pi->ehdr->e_shnum || 9718c2ecf20Sopenharmony_ci relsec->sh_link >= pi->ehdr->e_shnum) 9728c2ecf20Sopenharmony_ci return -ENOEXEC; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci section = pi->sechdrs + relsec->sh_info; 9758c2ecf20Sopenharmony_ci symtab = sechdrs + relsec->sh_link; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci if (!(section->sh_flags & SHF_ALLOC)) 9788c2ecf20Sopenharmony_ci continue; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci /* 9818c2ecf20Sopenharmony_ci * symtab->sh_link contain section header index of associated 9828c2ecf20Sopenharmony_ci * string table. 9838c2ecf20Sopenharmony_ci */ 9848c2ecf20Sopenharmony_ci if (symtab->sh_link >= pi->ehdr->e_shnum) 9858c2ecf20Sopenharmony_ci /* Invalid section number? */ 9868c2ecf20Sopenharmony_ci continue; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci /* 9898c2ecf20Sopenharmony_ci * Respective architecture needs to provide support for applying 9908c2ecf20Sopenharmony_ci * relocations of type SHT_RELA/SHT_REL. 9918c2ecf20Sopenharmony_ci */ 9928c2ecf20Sopenharmony_ci if (relsec->sh_type == SHT_RELA) 9938c2ecf20Sopenharmony_ci ret = arch_kexec_apply_relocations_add(pi, section, 9948c2ecf20Sopenharmony_ci relsec, symtab); 9958c2ecf20Sopenharmony_ci else if (relsec->sh_type == SHT_REL) 9968c2ecf20Sopenharmony_ci ret = arch_kexec_apply_relocations(pi, section, 9978c2ecf20Sopenharmony_ci relsec, symtab); 9988c2ecf20Sopenharmony_ci if (ret) 9998c2ecf20Sopenharmony_ci return ret; 10008c2ecf20Sopenharmony_ci } 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci return 0; 10038c2ecf20Sopenharmony_ci} 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci/* 10068c2ecf20Sopenharmony_ci * kexec_load_purgatory - Load and relocate the purgatory object. 10078c2ecf20Sopenharmony_ci * @image: Image to add the purgatory to. 10088c2ecf20Sopenharmony_ci * @kbuf: Memory parameters to use. 10098c2ecf20Sopenharmony_ci * 10108c2ecf20Sopenharmony_ci * Allocates the memory needed for image->purgatory_info.sechdrs and 10118c2ecf20Sopenharmony_ci * image->purgatory_info.purgatory_buf/kbuf->buffer. Caller is responsible 10128c2ecf20Sopenharmony_ci * to free the memory after use. 10138c2ecf20Sopenharmony_ci * 10148c2ecf20Sopenharmony_ci * Return: 0 on success, negative errno on error. 10158c2ecf20Sopenharmony_ci */ 10168c2ecf20Sopenharmony_ciint kexec_load_purgatory(struct kimage *image, struct kexec_buf *kbuf) 10178c2ecf20Sopenharmony_ci{ 10188c2ecf20Sopenharmony_ci struct purgatory_info *pi = &image->purgatory_info; 10198c2ecf20Sopenharmony_ci int ret; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci if (kexec_purgatory_size <= 0) 10228c2ecf20Sopenharmony_ci return -EINVAL; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci pi->ehdr = (const Elf_Ehdr *)kexec_purgatory; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci ret = kexec_purgatory_setup_kbuf(pi, kbuf); 10278c2ecf20Sopenharmony_ci if (ret) 10288c2ecf20Sopenharmony_ci return ret; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci ret = kexec_purgatory_setup_sechdrs(pi, kbuf); 10318c2ecf20Sopenharmony_ci if (ret) 10328c2ecf20Sopenharmony_ci goto out_free_kbuf; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci ret = kexec_apply_relocations(image); 10358c2ecf20Sopenharmony_ci if (ret) 10368c2ecf20Sopenharmony_ci goto out; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci return 0; 10398c2ecf20Sopenharmony_ciout: 10408c2ecf20Sopenharmony_ci vfree(pi->sechdrs); 10418c2ecf20Sopenharmony_ci pi->sechdrs = NULL; 10428c2ecf20Sopenharmony_ciout_free_kbuf: 10438c2ecf20Sopenharmony_ci vfree(pi->purgatory_buf); 10448c2ecf20Sopenharmony_ci pi->purgatory_buf = NULL; 10458c2ecf20Sopenharmony_ci return ret; 10468c2ecf20Sopenharmony_ci} 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci/* 10498c2ecf20Sopenharmony_ci * kexec_purgatory_find_symbol - find a symbol in the purgatory 10508c2ecf20Sopenharmony_ci * @pi: Purgatory to search in. 10518c2ecf20Sopenharmony_ci * @name: Name of the symbol. 10528c2ecf20Sopenharmony_ci * 10538c2ecf20Sopenharmony_ci * Return: pointer to symbol in read-only symtab on success, NULL on error. 10548c2ecf20Sopenharmony_ci */ 10558c2ecf20Sopenharmony_cistatic const Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi, 10568c2ecf20Sopenharmony_ci const char *name) 10578c2ecf20Sopenharmony_ci{ 10588c2ecf20Sopenharmony_ci const Elf_Shdr *sechdrs; 10598c2ecf20Sopenharmony_ci const Elf_Ehdr *ehdr; 10608c2ecf20Sopenharmony_ci const Elf_Sym *syms; 10618c2ecf20Sopenharmony_ci const char *strtab; 10628c2ecf20Sopenharmony_ci int i, k; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci if (!pi->ehdr) 10658c2ecf20Sopenharmony_ci return NULL; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci ehdr = pi->ehdr; 10688c2ecf20Sopenharmony_ci sechdrs = (void *)ehdr + ehdr->e_shoff; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci for (i = 0; i < ehdr->e_shnum; i++) { 10718c2ecf20Sopenharmony_ci if (sechdrs[i].sh_type != SHT_SYMTAB) 10728c2ecf20Sopenharmony_ci continue; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci if (sechdrs[i].sh_link >= ehdr->e_shnum) 10758c2ecf20Sopenharmony_ci /* Invalid strtab section number */ 10768c2ecf20Sopenharmony_ci continue; 10778c2ecf20Sopenharmony_ci strtab = (void *)ehdr + sechdrs[sechdrs[i].sh_link].sh_offset; 10788c2ecf20Sopenharmony_ci syms = (void *)ehdr + sechdrs[i].sh_offset; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci /* Go through symbols for a match */ 10818c2ecf20Sopenharmony_ci for (k = 0; k < sechdrs[i].sh_size/sizeof(Elf_Sym); k++) { 10828c2ecf20Sopenharmony_ci if (ELF_ST_BIND(syms[k].st_info) != STB_GLOBAL) 10838c2ecf20Sopenharmony_ci continue; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci if (strcmp(strtab + syms[k].st_name, name) != 0) 10868c2ecf20Sopenharmony_ci continue; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci if (syms[k].st_shndx == SHN_UNDEF || 10898c2ecf20Sopenharmony_ci syms[k].st_shndx >= ehdr->e_shnum) { 10908c2ecf20Sopenharmony_ci pr_debug("Symbol: %s has bad section index %d.\n", 10918c2ecf20Sopenharmony_ci name, syms[k].st_shndx); 10928c2ecf20Sopenharmony_ci return NULL; 10938c2ecf20Sopenharmony_ci } 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci /* Found the symbol we are looking for */ 10968c2ecf20Sopenharmony_ci return &syms[k]; 10978c2ecf20Sopenharmony_ci } 10988c2ecf20Sopenharmony_ci } 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci return NULL; 11018c2ecf20Sopenharmony_ci} 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_civoid *kexec_purgatory_get_symbol_addr(struct kimage *image, const char *name) 11048c2ecf20Sopenharmony_ci{ 11058c2ecf20Sopenharmony_ci struct purgatory_info *pi = &image->purgatory_info; 11068c2ecf20Sopenharmony_ci const Elf_Sym *sym; 11078c2ecf20Sopenharmony_ci Elf_Shdr *sechdr; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci sym = kexec_purgatory_find_symbol(pi, name); 11108c2ecf20Sopenharmony_ci if (!sym) 11118c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci sechdr = &pi->sechdrs[sym->st_shndx]; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci /* 11168c2ecf20Sopenharmony_ci * Returns the address where symbol will finally be loaded after 11178c2ecf20Sopenharmony_ci * kexec_load_segment() 11188c2ecf20Sopenharmony_ci */ 11198c2ecf20Sopenharmony_ci return (void *)(sechdr->sh_addr + sym->st_value); 11208c2ecf20Sopenharmony_ci} 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci/* 11238c2ecf20Sopenharmony_ci * Get or set value of a symbol. If "get_value" is true, symbol value is 11248c2ecf20Sopenharmony_ci * returned in buf otherwise symbol value is set based on value in buf. 11258c2ecf20Sopenharmony_ci */ 11268c2ecf20Sopenharmony_ciint kexec_purgatory_get_set_symbol(struct kimage *image, const char *name, 11278c2ecf20Sopenharmony_ci void *buf, unsigned int size, bool get_value) 11288c2ecf20Sopenharmony_ci{ 11298c2ecf20Sopenharmony_ci struct purgatory_info *pi = &image->purgatory_info; 11308c2ecf20Sopenharmony_ci const Elf_Sym *sym; 11318c2ecf20Sopenharmony_ci Elf_Shdr *sec; 11328c2ecf20Sopenharmony_ci char *sym_buf; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci sym = kexec_purgatory_find_symbol(pi, name); 11358c2ecf20Sopenharmony_ci if (!sym) 11368c2ecf20Sopenharmony_ci return -EINVAL; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci if (sym->st_size != size) { 11398c2ecf20Sopenharmony_ci pr_err("symbol %s size mismatch: expected %lu actual %u\n", 11408c2ecf20Sopenharmony_ci name, (unsigned long)sym->st_size, size); 11418c2ecf20Sopenharmony_ci return -EINVAL; 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci sec = pi->sechdrs + sym->st_shndx; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci if (sec->sh_type == SHT_NOBITS) { 11478c2ecf20Sopenharmony_ci pr_err("symbol %s is in a bss section. Cannot %s\n", name, 11488c2ecf20Sopenharmony_ci get_value ? "get" : "set"); 11498c2ecf20Sopenharmony_ci return -EINVAL; 11508c2ecf20Sopenharmony_ci } 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci sym_buf = (char *)pi->purgatory_buf + sec->sh_offset + sym->st_value; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci if (get_value) 11558c2ecf20Sopenharmony_ci memcpy((void *)buf, sym_buf, size); 11568c2ecf20Sopenharmony_ci else 11578c2ecf20Sopenharmony_ci memcpy((void *)sym_buf, buf, size); 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci return 0; 11608c2ecf20Sopenharmony_ci} 11618c2ecf20Sopenharmony_ci#endif /* CONFIG_ARCH_HAS_KEXEC_PURGATORY */ 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ciint crash_exclude_mem_range(struct crash_mem *mem, 11648c2ecf20Sopenharmony_ci unsigned long long mstart, unsigned long long mend) 11658c2ecf20Sopenharmony_ci{ 11668c2ecf20Sopenharmony_ci int i, j; 11678c2ecf20Sopenharmony_ci unsigned long long start, end, p_start, p_end; 11688c2ecf20Sopenharmony_ci struct crash_mem_range temp_range = {0, 0}; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci for (i = 0; i < mem->nr_ranges; i++) { 11718c2ecf20Sopenharmony_ci start = mem->ranges[i].start; 11728c2ecf20Sopenharmony_ci end = mem->ranges[i].end; 11738c2ecf20Sopenharmony_ci p_start = mstart; 11748c2ecf20Sopenharmony_ci p_end = mend; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci if (mstart > end || mend < start) 11778c2ecf20Sopenharmony_ci continue; 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci /* Truncate any area outside of range */ 11808c2ecf20Sopenharmony_ci if (mstart < start) 11818c2ecf20Sopenharmony_ci p_start = start; 11828c2ecf20Sopenharmony_ci if (mend > end) 11838c2ecf20Sopenharmony_ci p_end = end; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci /* Found completely overlapping range */ 11868c2ecf20Sopenharmony_ci if (p_start == start && p_end == end) { 11878c2ecf20Sopenharmony_ci mem->ranges[i].start = 0; 11888c2ecf20Sopenharmony_ci mem->ranges[i].end = 0; 11898c2ecf20Sopenharmony_ci if (i < mem->nr_ranges - 1) { 11908c2ecf20Sopenharmony_ci /* Shift rest of the ranges to left */ 11918c2ecf20Sopenharmony_ci for (j = i; j < mem->nr_ranges - 1; j++) { 11928c2ecf20Sopenharmony_ci mem->ranges[j].start = 11938c2ecf20Sopenharmony_ci mem->ranges[j+1].start; 11948c2ecf20Sopenharmony_ci mem->ranges[j].end = 11958c2ecf20Sopenharmony_ci mem->ranges[j+1].end; 11968c2ecf20Sopenharmony_ci } 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci /* 11998c2ecf20Sopenharmony_ci * Continue to check if there are another overlapping ranges 12008c2ecf20Sopenharmony_ci * from the current position because of shifting the above 12018c2ecf20Sopenharmony_ci * mem ranges. 12028c2ecf20Sopenharmony_ci */ 12038c2ecf20Sopenharmony_ci i--; 12048c2ecf20Sopenharmony_ci mem->nr_ranges--; 12058c2ecf20Sopenharmony_ci continue; 12068c2ecf20Sopenharmony_ci } 12078c2ecf20Sopenharmony_ci mem->nr_ranges--; 12088c2ecf20Sopenharmony_ci return 0; 12098c2ecf20Sopenharmony_ci } 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci if (p_start > start && p_end < end) { 12128c2ecf20Sopenharmony_ci /* Split original range */ 12138c2ecf20Sopenharmony_ci mem->ranges[i].end = p_start - 1; 12148c2ecf20Sopenharmony_ci temp_range.start = p_end + 1; 12158c2ecf20Sopenharmony_ci temp_range.end = end; 12168c2ecf20Sopenharmony_ci } else if (p_start != start) 12178c2ecf20Sopenharmony_ci mem->ranges[i].end = p_start - 1; 12188c2ecf20Sopenharmony_ci else 12198c2ecf20Sopenharmony_ci mem->ranges[i].start = p_end + 1; 12208c2ecf20Sopenharmony_ci break; 12218c2ecf20Sopenharmony_ci } 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci /* If a split happened, add the split to array */ 12248c2ecf20Sopenharmony_ci if (!temp_range.end) 12258c2ecf20Sopenharmony_ci return 0; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci /* Split happened */ 12288c2ecf20Sopenharmony_ci if (i == mem->max_nr_ranges - 1) 12298c2ecf20Sopenharmony_ci return -ENOMEM; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci /* Location where new range should go */ 12328c2ecf20Sopenharmony_ci j = i + 1; 12338c2ecf20Sopenharmony_ci if (j < mem->nr_ranges) { 12348c2ecf20Sopenharmony_ci /* Move over all ranges one slot towards the end */ 12358c2ecf20Sopenharmony_ci for (i = mem->nr_ranges - 1; i >= j; i--) 12368c2ecf20Sopenharmony_ci mem->ranges[i + 1] = mem->ranges[i]; 12378c2ecf20Sopenharmony_ci } 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci mem->ranges[j].start = temp_range.start; 12408c2ecf20Sopenharmony_ci mem->ranges[j].end = temp_range.end; 12418c2ecf20Sopenharmony_ci mem->nr_ranges++; 12428c2ecf20Sopenharmony_ci return 0; 12438c2ecf20Sopenharmony_ci} 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ciint crash_prepare_elf64_headers(struct crash_mem *mem, int kernel_map, 12468c2ecf20Sopenharmony_ci void **addr, unsigned long *sz) 12478c2ecf20Sopenharmony_ci{ 12488c2ecf20Sopenharmony_ci Elf64_Ehdr *ehdr; 12498c2ecf20Sopenharmony_ci Elf64_Phdr *phdr; 12508c2ecf20Sopenharmony_ci unsigned long nr_cpus = num_possible_cpus(), nr_phdr, elf_sz; 12518c2ecf20Sopenharmony_ci unsigned char *buf; 12528c2ecf20Sopenharmony_ci unsigned int cpu, i; 12538c2ecf20Sopenharmony_ci unsigned long long notes_addr; 12548c2ecf20Sopenharmony_ci unsigned long mstart, mend; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci /* extra phdr for vmcoreinfo ELF note */ 12578c2ecf20Sopenharmony_ci nr_phdr = nr_cpus + 1; 12588c2ecf20Sopenharmony_ci nr_phdr += mem->nr_ranges; 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci /* 12618c2ecf20Sopenharmony_ci * kexec-tools creates an extra PT_LOAD phdr for kernel text mapping 12628c2ecf20Sopenharmony_ci * area (for example, ffffffff80000000 - ffffffffa0000000 on x86_64). 12638c2ecf20Sopenharmony_ci * I think this is required by tools like gdb. So same physical 12648c2ecf20Sopenharmony_ci * memory will be mapped in two ELF headers. One will contain kernel 12658c2ecf20Sopenharmony_ci * text virtual addresses and other will have __va(physical) addresses. 12668c2ecf20Sopenharmony_ci */ 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci nr_phdr++; 12698c2ecf20Sopenharmony_ci elf_sz = sizeof(Elf64_Ehdr) + nr_phdr * sizeof(Elf64_Phdr); 12708c2ecf20Sopenharmony_ci elf_sz = ALIGN(elf_sz, ELF_CORE_HEADER_ALIGN); 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci buf = vzalloc(elf_sz); 12738c2ecf20Sopenharmony_ci if (!buf) 12748c2ecf20Sopenharmony_ci return -ENOMEM; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci ehdr = (Elf64_Ehdr *)buf; 12778c2ecf20Sopenharmony_ci phdr = (Elf64_Phdr *)(ehdr + 1); 12788c2ecf20Sopenharmony_ci memcpy(ehdr->e_ident, ELFMAG, SELFMAG); 12798c2ecf20Sopenharmony_ci ehdr->e_ident[EI_CLASS] = ELFCLASS64; 12808c2ecf20Sopenharmony_ci ehdr->e_ident[EI_DATA] = ELFDATA2LSB; 12818c2ecf20Sopenharmony_ci ehdr->e_ident[EI_VERSION] = EV_CURRENT; 12828c2ecf20Sopenharmony_ci ehdr->e_ident[EI_OSABI] = ELF_OSABI; 12838c2ecf20Sopenharmony_ci memset(ehdr->e_ident + EI_PAD, 0, EI_NIDENT - EI_PAD); 12848c2ecf20Sopenharmony_ci ehdr->e_type = ET_CORE; 12858c2ecf20Sopenharmony_ci ehdr->e_machine = ELF_ARCH; 12868c2ecf20Sopenharmony_ci ehdr->e_version = EV_CURRENT; 12878c2ecf20Sopenharmony_ci ehdr->e_phoff = sizeof(Elf64_Ehdr); 12888c2ecf20Sopenharmony_ci ehdr->e_ehsize = sizeof(Elf64_Ehdr); 12898c2ecf20Sopenharmony_ci ehdr->e_phentsize = sizeof(Elf64_Phdr); 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci /* Prepare one phdr of type PT_NOTE for each present CPU */ 12928c2ecf20Sopenharmony_ci for_each_present_cpu(cpu) { 12938c2ecf20Sopenharmony_ci phdr->p_type = PT_NOTE; 12948c2ecf20Sopenharmony_ci notes_addr = per_cpu_ptr_to_phys(per_cpu_ptr(crash_notes, cpu)); 12958c2ecf20Sopenharmony_ci phdr->p_offset = phdr->p_paddr = notes_addr; 12968c2ecf20Sopenharmony_ci phdr->p_filesz = phdr->p_memsz = sizeof(note_buf_t); 12978c2ecf20Sopenharmony_ci (ehdr->e_phnum)++; 12988c2ecf20Sopenharmony_ci phdr++; 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci /* Prepare one PT_NOTE header for vmcoreinfo */ 13028c2ecf20Sopenharmony_ci phdr->p_type = PT_NOTE; 13038c2ecf20Sopenharmony_ci phdr->p_offset = phdr->p_paddr = paddr_vmcoreinfo_note(); 13048c2ecf20Sopenharmony_ci phdr->p_filesz = phdr->p_memsz = VMCOREINFO_NOTE_SIZE; 13058c2ecf20Sopenharmony_ci (ehdr->e_phnum)++; 13068c2ecf20Sopenharmony_ci phdr++; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci /* Prepare PT_LOAD type program header for kernel text region */ 13098c2ecf20Sopenharmony_ci if (kernel_map) { 13108c2ecf20Sopenharmony_ci phdr->p_type = PT_LOAD; 13118c2ecf20Sopenharmony_ci phdr->p_flags = PF_R|PF_W|PF_X; 13128c2ecf20Sopenharmony_ci phdr->p_vaddr = (unsigned long) _text; 13138c2ecf20Sopenharmony_ci phdr->p_filesz = phdr->p_memsz = _end - _text; 13148c2ecf20Sopenharmony_ci phdr->p_offset = phdr->p_paddr = __pa_symbol(_text); 13158c2ecf20Sopenharmony_ci ehdr->e_phnum++; 13168c2ecf20Sopenharmony_ci phdr++; 13178c2ecf20Sopenharmony_ci } 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci /* Go through all the ranges in mem->ranges[] and prepare phdr */ 13208c2ecf20Sopenharmony_ci for (i = 0; i < mem->nr_ranges; i++) { 13218c2ecf20Sopenharmony_ci mstart = mem->ranges[i].start; 13228c2ecf20Sopenharmony_ci mend = mem->ranges[i].end; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci phdr->p_type = PT_LOAD; 13258c2ecf20Sopenharmony_ci phdr->p_flags = PF_R|PF_W|PF_X; 13268c2ecf20Sopenharmony_ci phdr->p_offset = mstart; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci phdr->p_paddr = mstart; 13298c2ecf20Sopenharmony_ci phdr->p_vaddr = (unsigned long) __va(mstart); 13308c2ecf20Sopenharmony_ci phdr->p_filesz = phdr->p_memsz = mend - mstart + 1; 13318c2ecf20Sopenharmony_ci phdr->p_align = 0; 13328c2ecf20Sopenharmony_ci ehdr->e_phnum++; 13338c2ecf20Sopenharmony_ci pr_debug("Crash PT_LOAD ELF header. phdr=%p vaddr=0x%llx, paddr=0x%llx, sz=0x%llx e_phnum=%d p_offset=0x%llx\n", 13348c2ecf20Sopenharmony_ci phdr, phdr->p_vaddr, phdr->p_paddr, phdr->p_filesz, 13358c2ecf20Sopenharmony_ci ehdr->e_phnum, phdr->p_offset); 13368c2ecf20Sopenharmony_ci phdr++; 13378c2ecf20Sopenharmony_ci } 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci *addr = buf; 13408c2ecf20Sopenharmony_ci *sz = elf_sz; 13418c2ecf20Sopenharmony_ci return 0; 13428c2ecf20Sopenharmony_ci} 1343