18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Code to handle transition of Linux booting another kernel. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2002-2003 Eric Biederman <ebiederm@xmission.com> 68c2ecf20Sopenharmony_ci * GameCube/ppc32 port Copyright (C) 2004 Albert Herranz 78c2ecf20Sopenharmony_ci * Copyright (C) 2005 IBM Corporation. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kexec.h> 118c2ecf20Sopenharmony_ci#include <linux/reboot.h> 128c2ecf20Sopenharmony_ci#include <linux/threads.h> 138c2ecf20Sopenharmony_ci#include <linux/memblock.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/irq.h> 168c2ecf20Sopenharmony_ci#include <linux/ftrace.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <asm/kdump.h> 198c2ecf20Sopenharmony_ci#include <asm/machdep.h> 208c2ecf20Sopenharmony_ci#include <asm/pgalloc.h> 218c2ecf20Sopenharmony_ci#include <asm/prom.h> 228c2ecf20Sopenharmony_ci#include <asm/sections.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_civoid machine_kexec_mask_interrupts(void) { 258c2ecf20Sopenharmony_ci unsigned int i; 268c2ecf20Sopenharmony_ci struct irq_desc *desc; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci for_each_irq_desc(i, desc) { 298c2ecf20Sopenharmony_ci struct irq_chip *chip; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci chip = irq_desc_get_chip(desc); 328c2ecf20Sopenharmony_ci if (!chip) 338c2ecf20Sopenharmony_ci continue; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci if (chip->irq_eoi && irqd_irq_inprogress(&desc->irq_data)) 368c2ecf20Sopenharmony_ci chip->irq_eoi(&desc->irq_data); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci if (chip->irq_mask) 398c2ecf20Sopenharmony_ci chip->irq_mask(&desc->irq_data); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci if (chip->irq_disable && !irqd_irq_disabled(&desc->irq_data)) 428c2ecf20Sopenharmony_ci chip->irq_disable(&desc->irq_data); 438c2ecf20Sopenharmony_ci } 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_civoid machine_crash_shutdown(struct pt_regs *regs) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci default_machine_crash_shutdown(regs); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* 528c2ecf20Sopenharmony_ci * Do what every setup is needed on image and the 538c2ecf20Sopenharmony_ci * reboot code buffer to allow us to avoid allocations 548c2ecf20Sopenharmony_ci * later. 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ciint machine_kexec_prepare(struct kimage *image) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci if (ppc_md.machine_kexec_prepare) 598c2ecf20Sopenharmony_ci return ppc_md.machine_kexec_prepare(image); 608c2ecf20Sopenharmony_ci else 618c2ecf20Sopenharmony_ci return default_machine_kexec_prepare(image); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_civoid machine_kexec_cleanup(struct kimage *image) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_civoid arch_crash_save_vmcoreinfo(void) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#ifdef CONFIG_NEED_MULTIPLE_NODES 728c2ecf20Sopenharmony_ci VMCOREINFO_SYMBOL(node_data); 738c2ecf20Sopenharmony_ci VMCOREINFO_LENGTH(node_data, MAX_NUMNODES); 748c2ecf20Sopenharmony_ci#endif 758c2ecf20Sopenharmony_ci#ifndef CONFIG_NEED_MULTIPLE_NODES 768c2ecf20Sopenharmony_ci VMCOREINFO_SYMBOL(contig_page_data); 778c2ecf20Sopenharmony_ci#endif 788c2ecf20Sopenharmony_ci#if defined(CONFIG_PPC64) && defined(CONFIG_SPARSEMEM_VMEMMAP) 798c2ecf20Sopenharmony_ci VMCOREINFO_SYMBOL(vmemmap_list); 808c2ecf20Sopenharmony_ci VMCOREINFO_SYMBOL(mmu_vmemmap_psize); 818c2ecf20Sopenharmony_ci VMCOREINFO_SYMBOL(mmu_psize_defs); 828c2ecf20Sopenharmony_ci VMCOREINFO_STRUCT_SIZE(vmemmap_backing); 838c2ecf20Sopenharmony_ci VMCOREINFO_OFFSET(vmemmap_backing, list); 848c2ecf20Sopenharmony_ci VMCOREINFO_OFFSET(vmemmap_backing, phys); 858c2ecf20Sopenharmony_ci VMCOREINFO_OFFSET(vmemmap_backing, virt_addr); 868c2ecf20Sopenharmony_ci VMCOREINFO_STRUCT_SIZE(mmu_psize_def); 878c2ecf20Sopenharmony_ci VMCOREINFO_OFFSET(mmu_psize_def, shift); 888c2ecf20Sopenharmony_ci#endif 898c2ecf20Sopenharmony_ci vmcoreinfo_append_str("KERNELOFFSET=%lx\n", kaslr_offset()); 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* 938c2ecf20Sopenharmony_ci * Do not allocate memory (or fail in any way) in machine_kexec(). 948c2ecf20Sopenharmony_ci * We are past the point of no return, committed to rebooting now. 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_civoid machine_kexec(struct kimage *image) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci int save_ftrace_enabled; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci save_ftrace_enabled = __ftrace_enabled_save(); 1018c2ecf20Sopenharmony_ci this_cpu_disable_ftrace(); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (ppc_md.machine_kexec) 1048c2ecf20Sopenharmony_ci ppc_md.machine_kexec(image); 1058c2ecf20Sopenharmony_ci else 1068c2ecf20Sopenharmony_ci default_machine_kexec(image); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci this_cpu_enable_ftrace(); 1098c2ecf20Sopenharmony_ci __ftrace_enabled_restore(save_ftrace_enabled); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* Fall back to normal restart if we're still alive. */ 1128c2ecf20Sopenharmony_ci machine_restart(NULL); 1138c2ecf20Sopenharmony_ci for(;;); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_civoid __init reserve_crashkernel(void) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci unsigned long long crash_size, crash_base, total_mem_sz; 1198c2ecf20Sopenharmony_ci int ret; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci total_mem_sz = memory_limit ? memory_limit : memblock_phys_mem_size(); 1228c2ecf20Sopenharmony_ci /* use common parsing */ 1238c2ecf20Sopenharmony_ci ret = parse_crashkernel(boot_command_line, total_mem_sz, 1248c2ecf20Sopenharmony_ci &crash_size, &crash_base); 1258c2ecf20Sopenharmony_ci if (ret == 0 && crash_size > 0) { 1268c2ecf20Sopenharmony_ci crashk_res.start = crash_base; 1278c2ecf20Sopenharmony_ci crashk_res.end = crash_base + crash_size - 1; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (crashk_res.end == crashk_res.start) { 1318c2ecf20Sopenharmony_ci crashk_res.start = crashk_res.end = 0; 1328c2ecf20Sopenharmony_ci return; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* We might have got these values via the command line or the 1368c2ecf20Sopenharmony_ci * device tree, either way sanitise them now. */ 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci crash_size = resource_size(&crashk_res); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci#ifndef CONFIG_NONSTATIC_KERNEL 1418c2ecf20Sopenharmony_ci if (crashk_res.start != KDUMP_KERNELBASE) 1428c2ecf20Sopenharmony_ci printk("Crash kernel location must be 0x%x\n", 1438c2ecf20Sopenharmony_ci KDUMP_KERNELBASE); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci crashk_res.start = KDUMP_KERNELBASE; 1468c2ecf20Sopenharmony_ci#else 1478c2ecf20Sopenharmony_ci if (!crashk_res.start) { 1488c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64 1498c2ecf20Sopenharmony_ci /* 1508c2ecf20Sopenharmony_ci * On the LPAR platform place the crash kernel to mid of 1518c2ecf20Sopenharmony_ci * RMA size (512MB or more) to ensure the crash kernel 1528c2ecf20Sopenharmony_ci * gets enough space to place itself and some stack to be 1538c2ecf20Sopenharmony_ci * in the first segment. At the same time normal kernel 1548c2ecf20Sopenharmony_ci * also get enough space to allocate memory for essential 1558c2ecf20Sopenharmony_ci * system resource in the first segment. Keep the crash 1568c2ecf20Sopenharmony_ci * kernel starts at 128MB offset on other platforms. 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_ci if (firmware_has_feature(FW_FEATURE_LPAR)) 1598c2ecf20Sopenharmony_ci crashk_res.start = ppc64_rma_size / 2; 1608c2ecf20Sopenharmony_ci else 1618c2ecf20Sopenharmony_ci crashk_res.start = min(0x8000000ULL, (ppc64_rma_size / 2)); 1628c2ecf20Sopenharmony_ci#else 1638c2ecf20Sopenharmony_ci crashk_res.start = KDUMP_KERNELBASE; 1648c2ecf20Sopenharmony_ci#endif 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci crash_base = PAGE_ALIGN(crashk_res.start); 1688c2ecf20Sopenharmony_ci if (crash_base != crashk_res.start) { 1698c2ecf20Sopenharmony_ci printk("Crash kernel base must be aligned to 0x%lx\n", 1708c2ecf20Sopenharmony_ci PAGE_SIZE); 1718c2ecf20Sopenharmony_ci crashk_res.start = crash_base; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci#endif 1758c2ecf20Sopenharmony_ci crash_size = PAGE_ALIGN(crash_size); 1768c2ecf20Sopenharmony_ci crashk_res.end = crashk_res.start + crash_size - 1; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* The crash region must not overlap the current kernel */ 1798c2ecf20Sopenharmony_ci if (overlaps_crashkernel(__pa(_stext), _end - _stext)) { 1808c2ecf20Sopenharmony_ci printk(KERN_WARNING 1818c2ecf20Sopenharmony_ci "Crash kernel can not overlap current kernel\n"); 1828c2ecf20Sopenharmony_ci crashk_res.start = crashk_res.end = 0; 1838c2ecf20Sopenharmony_ci return; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* Crash kernel trumps memory limit */ 1878c2ecf20Sopenharmony_ci if (memory_limit && memory_limit <= crashk_res.end) { 1888c2ecf20Sopenharmony_ci memory_limit = crashk_res.end + 1; 1898c2ecf20Sopenharmony_ci total_mem_sz = memory_limit; 1908c2ecf20Sopenharmony_ci printk("Adjusted memory limit for crashkernel, now 0x%llx\n", 1918c2ecf20Sopenharmony_ci memory_limit); 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci printk(KERN_INFO "Reserving %ldMB of memory at %ldMB " 1958c2ecf20Sopenharmony_ci "for crashkernel (System RAM: %ldMB)\n", 1968c2ecf20Sopenharmony_ci (unsigned long)(crash_size >> 20), 1978c2ecf20Sopenharmony_ci (unsigned long)(crashk_res.start >> 20), 1988c2ecf20Sopenharmony_ci (unsigned long)(total_mem_sz >> 20)); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (!memblock_is_region_memory(crashk_res.start, crash_size) || 2018c2ecf20Sopenharmony_ci memblock_reserve(crashk_res.start, crash_size)) { 2028c2ecf20Sopenharmony_ci pr_err("Failed to reserve memory for crashkernel!\n"); 2038c2ecf20Sopenharmony_ci crashk_res.start = crashk_res.end = 0; 2048c2ecf20Sopenharmony_ci return; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ciint overlaps_crashkernel(unsigned long start, unsigned long size) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci return (start + size) > crashk_res.start && start <= crashk_res.end; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci/* Values we need to export to the second kernel via the device tree. */ 2148c2ecf20Sopenharmony_cistatic phys_addr_t kernel_end; 2158c2ecf20Sopenharmony_cistatic phys_addr_t crashk_base; 2168c2ecf20Sopenharmony_cistatic phys_addr_t crashk_size; 2178c2ecf20Sopenharmony_cistatic unsigned long long mem_limit; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic struct property kernel_end_prop = { 2208c2ecf20Sopenharmony_ci .name = "linux,kernel-end", 2218c2ecf20Sopenharmony_ci .length = sizeof(phys_addr_t), 2228c2ecf20Sopenharmony_ci .value = &kernel_end, 2238c2ecf20Sopenharmony_ci}; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic struct property crashk_base_prop = { 2268c2ecf20Sopenharmony_ci .name = "linux,crashkernel-base", 2278c2ecf20Sopenharmony_ci .length = sizeof(phys_addr_t), 2288c2ecf20Sopenharmony_ci .value = &crashk_base 2298c2ecf20Sopenharmony_ci}; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic struct property crashk_size_prop = { 2328c2ecf20Sopenharmony_ci .name = "linux,crashkernel-size", 2338c2ecf20Sopenharmony_ci .length = sizeof(phys_addr_t), 2348c2ecf20Sopenharmony_ci .value = &crashk_size, 2358c2ecf20Sopenharmony_ci}; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic struct property memory_limit_prop = { 2388c2ecf20Sopenharmony_ci .name = "linux,memory-limit", 2398c2ecf20Sopenharmony_ci .length = sizeof(unsigned long long), 2408c2ecf20Sopenharmony_ci .value = &mem_limit, 2418c2ecf20Sopenharmony_ci}; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci#define cpu_to_be_ulong __PASTE(cpu_to_be, BITS_PER_LONG) 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic void __init export_crashk_values(struct device_node *node) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci /* There might be existing crash kernel properties, but we can't 2488c2ecf20Sopenharmony_ci * be sure what's in them, so remove them. */ 2498c2ecf20Sopenharmony_ci of_remove_property(node, of_find_property(node, 2508c2ecf20Sopenharmony_ci "linux,crashkernel-base", NULL)); 2518c2ecf20Sopenharmony_ci of_remove_property(node, of_find_property(node, 2528c2ecf20Sopenharmony_ci "linux,crashkernel-size", NULL)); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (crashk_res.start != 0) { 2558c2ecf20Sopenharmony_ci crashk_base = cpu_to_be_ulong(crashk_res.start), 2568c2ecf20Sopenharmony_ci of_add_property(node, &crashk_base_prop); 2578c2ecf20Sopenharmony_ci crashk_size = cpu_to_be_ulong(resource_size(&crashk_res)); 2588c2ecf20Sopenharmony_ci of_add_property(node, &crashk_size_prop); 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* 2628c2ecf20Sopenharmony_ci * memory_limit is required by the kexec-tools to limit the 2638c2ecf20Sopenharmony_ci * crash regions to the actual memory used. 2648c2ecf20Sopenharmony_ci */ 2658c2ecf20Sopenharmony_ci mem_limit = cpu_to_be_ulong(memory_limit); 2668c2ecf20Sopenharmony_ci of_update_property(node, &memory_limit_prop); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic int __init kexec_setup(void) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct device_node *node; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci node = of_find_node_by_path("/chosen"); 2748c2ecf20Sopenharmony_ci if (!node) 2758c2ecf20Sopenharmony_ci return -ENOENT; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* remove any stale properties so ours can be found */ 2788c2ecf20Sopenharmony_ci of_remove_property(node, of_find_property(node, kernel_end_prop.name, NULL)); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* information needed by userspace when using default_machine_kexec */ 2818c2ecf20Sopenharmony_ci kernel_end = cpu_to_be_ulong(__pa(_end)); 2828c2ecf20Sopenharmony_ci of_add_property(node, &kernel_end_prop); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci export_crashk_values(node); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci of_node_put(node); 2878c2ecf20Sopenharmony_ci return 0; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_cilate_initcall(kexec_setup); 290