162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ppc64 code to implement the kexec_file_load syscall 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2004 Adam Litke (agl@us.ibm.com) 662306a36Sopenharmony_ci * Copyright (C) 2004 IBM Corp. 762306a36Sopenharmony_ci * Copyright (C) 2004,2005 Milton D Miller II, IBM Corporation 862306a36Sopenharmony_ci * Copyright (C) 2005 R Sharada (sharada@in.ibm.com) 962306a36Sopenharmony_ci * Copyright (C) 2006 Mohan Kumar M (mohan@in.ibm.com) 1062306a36Sopenharmony_ci * Copyright (C) 2020 IBM Corporation 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Based on kexec-tools' kexec-ppc64.c, kexec-elf-rel-ppc64.c, fs2dt.c. 1362306a36Sopenharmony_ci * Heavily modified for the kernel by 1462306a36Sopenharmony_ci * Hari Bathini, IBM Corporation. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/kexec.h> 1862306a36Sopenharmony_ci#include <linux/of_fdt.h> 1962306a36Sopenharmony_ci#include <linux/libfdt.h> 2062306a36Sopenharmony_ci#include <linux/of.h> 2162306a36Sopenharmony_ci#include <linux/memblock.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/vmalloc.h> 2462306a36Sopenharmony_ci#include <asm/setup.h> 2562306a36Sopenharmony_ci#include <asm/drmem.h> 2662306a36Sopenharmony_ci#include <asm/firmware.h> 2762306a36Sopenharmony_ci#include <asm/kexec_ranges.h> 2862306a36Sopenharmony_ci#include <asm/crashdump-ppc64.h> 2962306a36Sopenharmony_ci#include <asm/mmzone.h> 3062306a36Sopenharmony_ci#include <asm/iommu.h> 3162306a36Sopenharmony_ci#include <asm/prom.h> 3262306a36Sopenharmony_ci#include <asm/plpks.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistruct umem_info { 3562306a36Sopenharmony_ci u64 *buf; /* data buffer for usable-memory property */ 3662306a36Sopenharmony_ci u32 size; /* size allocated for the data buffer */ 3762306a36Sopenharmony_ci u32 max_entries; /* maximum no. of entries */ 3862306a36Sopenharmony_ci u32 idx; /* index of current entry */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci /* usable memory ranges to look up */ 4162306a36Sopenharmony_ci unsigned int nr_ranges; 4262306a36Sopenharmony_ci const struct range *ranges; 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ciconst struct kexec_file_ops * const kexec_file_loaders[] = { 4662306a36Sopenharmony_ci &kexec_elf64_ops, 4762306a36Sopenharmony_ci NULL 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/** 5162306a36Sopenharmony_ci * get_exclude_memory_ranges - Get exclude memory ranges. This list includes 5262306a36Sopenharmony_ci * regions like opal/rtas, tce-table, initrd, 5362306a36Sopenharmony_ci * kernel, htab which should be avoided while 5462306a36Sopenharmony_ci * setting up kexec load segments. 5562306a36Sopenharmony_ci * @mem_ranges: Range list to add the memory ranges to. 5662306a36Sopenharmony_ci * 5762306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_cistatic int get_exclude_memory_ranges(struct crash_mem **mem_ranges) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci int ret; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci ret = add_tce_mem_ranges(mem_ranges); 6462306a36Sopenharmony_ci if (ret) 6562306a36Sopenharmony_ci goto out; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci ret = add_initrd_mem_range(mem_ranges); 6862306a36Sopenharmony_ci if (ret) 6962306a36Sopenharmony_ci goto out; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci ret = add_htab_mem_range(mem_ranges); 7262306a36Sopenharmony_ci if (ret) 7362306a36Sopenharmony_ci goto out; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci ret = add_kernel_mem_range(mem_ranges); 7662306a36Sopenharmony_ci if (ret) 7762306a36Sopenharmony_ci goto out; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci ret = add_rtas_mem_range(mem_ranges); 8062306a36Sopenharmony_ci if (ret) 8162306a36Sopenharmony_ci goto out; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci ret = add_opal_mem_range(mem_ranges); 8462306a36Sopenharmony_ci if (ret) 8562306a36Sopenharmony_ci goto out; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci ret = add_reserved_mem_ranges(mem_ranges); 8862306a36Sopenharmony_ci if (ret) 8962306a36Sopenharmony_ci goto out; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* exclude memory ranges should be sorted for easy lookup */ 9262306a36Sopenharmony_ci sort_memory_ranges(*mem_ranges, true); 9362306a36Sopenharmony_ciout: 9462306a36Sopenharmony_ci if (ret) 9562306a36Sopenharmony_ci pr_err("Failed to setup exclude memory ranges\n"); 9662306a36Sopenharmony_ci return ret; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/** 10062306a36Sopenharmony_ci * get_usable_memory_ranges - Get usable memory ranges. This list includes 10162306a36Sopenharmony_ci * regions like crashkernel, opal/rtas & tce-table, 10262306a36Sopenharmony_ci * that kdump kernel could use. 10362306a36Sopenharmony_ci * @mem_ranges: Range list to add the memory ranges to. 10462306a36Sopenharmony_ci * 10562306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_cistatic int get_usable_memory_ranges(struct crash_mem **mem_ranges) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci int ret; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* 11262306a36Sopenharmony_ci * Early boot failure observed on guests when low memory (first memory 11362306a36Sopenharmony_ci * block?) is not added to usable memory. So, add [0, crashk_res.end] 11462306a36Sopenharmony_ci * instead of [crashk_res.start, crashk_res.end] to workaround it. 11562306a36Sopenharmony_ci * Also, crashed kernel's memory must be added to reserve map to 11662306a36Sopenharmony_ci * avoid kdump kernel from using it. 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_ci ret = add_mem_range(mem_ranges, 0, crashk_res.end + 1); 11962306a36Sopenharmony_ci if (ret) 12062306a36Sopenharmony_ci goto out; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci ret = add_rtas_mem_range(mem_ranges); 12362306a36Sopenharmony_ci if (ret) 12462306a36Sopenharmony_ci goto out; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci ret = add_opal_mem_range(mem_ranges); 12762306a36Sopenharmony_ci if (ret) 12862306a36Sopenharmony_ci goto out; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci ret = add_tce_mem_ranges(mem_ranges); 13162306a36Sopenharmony_ciout: 13262306a36Sopenharmony_ci if (ret) 13362306a36Sopenharmony_ci pr_err("Failed to setup usable memory ranges\n"); 13462306a36Sopenharmony_ci return ret; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/** 13862306a36Sopenharmony_ci * get_crash_memory_ranges - Get crash memory ranges. This list includes 13962306a36Sopenharmony_ci * first/crashing kernel's memory regions that 14062306a36Sopenharmony_ci * would be exported via an elfcore. 14162306a36Sopenharmony_ci * @mem_ranges: Range list to add the memory ranges to. 14262306a36Sopenharmony_ci * 14362306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_cistatic int get_crash_memory_ranges(struct crash_mem **mem_ranges) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci phys_addr_t base, end; 14862306a36Sopenharmony_ci struct crash_mem *tmem; 14962306a36Sopenharmony_ci u64 i; 15062306a36Sopenharmony_ci int ret; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci for_each_mem_range(i, &base, &end) { 15362306a36Sopenharmony_ci u64 size = end - base; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* Skip backup memory region, which needs a separate entry */ 15662306a36Sopenharmony_ci if (base == BACKUP_SRC_START) { 15762306a36Sopenharmony_ci if (size > BACKUP_SRC_SIZE) { 15862306a36Sopenharmony_ci base = BACKUP_SRC_END + 1; 15962306a36Sopenharmony_ci size -= BACKUP_SRC_SIZE; 16062306a36Sopenharmony_ci } else 16162306a36Sopenharmony_ci continue; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci ret = add_mem_range(mem_ranges, base, size); 16562306a36Sopenharmony_ci if (ret) 16662306a36Sopenharmony_ci goto out; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* Try merging adjacent ranges before reallocation attempt */ 16962306a36Sopenharmony_ci if ((*mem_ranges)->nr_ranges == (*mem_ranges)->max_nr_ranges) 17062306a36Sopenharmony_ci sort_memory_ranges(*mem_ranges, true); 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* Reallocate memory ranges if there is no space to split ranges */ 17462306a36Sopenharmony_ci tmem = *mem_ranges; 17562306a36Sopenharmony_ci if (tmem && (tmem->nr_ranges == tmem->max_nr_ranges)) { 17662306a36Sopenharmony_ci tmem = realloc_mem_ranges(mem_ranges); 17762306a36Sopenharmony_ci if (!tmem) 17862306a36Sopenharmony_ci goto out; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* Exclude crashkernel region */ 18262306a36Sopenharmony_ci ret = crash_exclude_mem_range(tmem, crashk_res.start, crashk_res.end); 18362306a36Sopenharmony_ci if (ret) 18462306a36Sopenharmony_ci goto out; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* 18762306a36Sopenharmony_ci * FIXME: For now, stay in parity with kexec-tools but if RTAS/OPAL 18862306a36Sopenharmony_ci * regions are exported to save their context at the time of 18962306a36Sopenharmony_ci * crash, they should actually be backed up just like the 19062306a36Sopenharmony_ci * first 64K bytes of memory. 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_ci ret = add_rtas_mem_range(mem_ranges); 19362306a36Sopenharmony_ci if (ret) 19462306a36Sopenharmony_ci goto out; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci ret = add_opal_mem_range(mem_ranges); 19762306a36Sopenharmony_ci if (ret) 19862306a36Sopenharmony_ci goto out; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* create a separate program header for the backup region */ 20162306a36Sopenharmony_ci ret = add_mem_range(mem_ranges, BACKUP_SRC_START, BACKUP_SRC_SIZE); 20262306a36Sopenharmony_ci if (ret) 20362306a36Sopenharmony_ci goto out; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci sort_memory_ranges(*mem_ranges, false); 20662306a36Sopenharmony_ciout: 20762306a36Sopenharmony_ci if (ret) 20862306a36Sopenharmony_ci pr_err("Failed to setup crash memory ranges\n"); 20962306a36Sopenharmony_ci return ret; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci/** 21362306a36Sopenharmony_ci * get_reserved_memory_ranges - Get reserve memory ranges. This list includes 21462306a36Sopenharmony_ci * memory regions that should be added to the 21562306a36Sopenharmony_ci * memory reserve map to ensure the region is 21662306a36Sopenharmony_ci * protected from any mischief. 21762306a36Sopenharmony_ci * @mem_ranges: Range list to add the memory ranges to. 21862306a36Sopenharmony_ci * 21962306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 22062306a36Sopenharmony_ci */ 22162306a36Sopenharmony_cistatic int get_reserved_memory_ranges(struct crash_mem **mem_ranges) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci int ret; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci ret = add_rtas_mem_range(mem_ranges); 22662306a36Sopenharmony_ci if (ret) 22762306a36Sopenharmony_ci goto out; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci ret = add_tce_mem_ranges(mem_ranges); 23062306a36Sopenharmony_ci if (ret) 23162306a36Sopenharmony_ci goto out; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci ret = add_reserved_mem_ranges(mem_ranges); 23462306a36Sopenharmony_ciout: 23562306a36Sopenharmony_ci if (ret) 23662306a36Sopenharmony_ci pr_err("Failed to setup reserved memory ranges\n"); 23762306a36Sopenharmony_ci return ret; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci/** 24162306a36Sopenharmony_ci * __locate_mem_hole_top_down - Looks top down for a large enough memory hole 24262306a36Sopenharmony_ci * in the memory regions between buf_min & buf_max 24362306a36Sopenharmony_ci * for the buffer. If found, sets kbuf->mem. 24462306a36Sopenharmony_ci * @kbuf: Buffer contents and memory parameters. 24562306a36Sopenharmony_ci * @buf_min: Minimum address for the buffer. 24662306a36Sopenharmony_ci * @buf_max: Maximum address for the buffer. 24762306a36Sopenharmony_ci * 24862306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_cistatic int __locate_mem_hole_top_down(struct kexec_buf *kbuf, 25162306a36Sopenharmony_ci u64 buf_min, u64 buf_max) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci int ret = -EADDRNOTAVAIL; 25462306a36Sopenharmony_ci phys_addr_t start, end; 25562306a36Sopenharmony_ci u64 i; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci for_each_mem_range_rev(i, &start, &end) { 25862306a36Sopenharmony_ci /* 25962306a36Sopenharmony_ci * memblock uses [start, end) convention while it is 26062306a36Sopenharmony_ci * [start, end] here. Fix the off-by-one to have the 26162306a36Sopenharmony_ci * same convention. 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_ci end -= 1; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (start > buf_max) 26662306a36Sopenharmony_ci continue; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* Memory hole not found */ 26962306a36Sopenharmony_ci if (end < buf_min) 27062306a36Sopenharmony_ci break; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* Adjust memory region based on the given range */ 27362306a36Sopenharmony_ci if (start < buf_min) 27462306a36Sopenharmony_ci start = buf_min; 27562306a36Sopenharmony_ci if (end > buf_max) 27662306a36Sopenharmony_ci end = buf_max; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci start = ALIGN(start, kbuf->buf_align); 27962306a36Sopenharmony_ci if (start < end && (end - start + 1) >= kbuf->memsz) { 28062306a36Sopenharmony_ci /* Suitable memory range found. Set kbuf->mem */ 28162306a36Sopenharmony_ci kbuf->mem = ALIGN_DOWN(end - kbuf->memsz + 1, 28262306a36Sopenharmony_ci kbuf->buf_align); 28362306a36Sopenharmony_ci ret = 0; 28462306a36Sopenharmony_ci break; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci return ret; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci/** 29262306a36Sopenharmony_ci * locate_mem_hole_top_down_ppc64 - Skip special memory regions to find a 29362306a36Sopenharmony_ci * suitable buffer with top down approach. 29462306a36Sopenharmony_ci * @kbuf: Buffer contents and memory parameters. 29562306a36Sopenharmony_ci * @buf_min: Minimum address for the buffer. 29662306a36Sopenharmony_ci * @buf_max: Maximum address for the buffer. 29762306a36Sopenharmony_ci * @emem: Exclude memory ranges. 29862306a36Sopenharmony_ci * 29962306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 30062306a36Sopenharmony_ci */ 30162306a36Sopenharmony_cistatic int locate_mem_hole_top_down_ppc64(struct kexec_buf *kbuf, 30262306a36Sopenharmony_ci u64 buf_min, u64 buf_max, 30362306a36Sopenharmony_ci const struct crash_mem *emem) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci int i, ret = 0, err = -EADDRNOTAVAIL; 30662306a36Sopenharmony_ci u64 start, end, tmin, tmax; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci tmax = buf_max; 30962306a36Sopenharmony_ci for (i = (emem->nr_ranges - 1); i >= 0; i--) { 31062306a36Sopenharmony_ci start = emem->ranges[i].start; 31162306a36Sopenharmony_ci end = emem->ranges[i].end; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (start > tmax) 31462306a36Sopenharmony_ci continue; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (end < tmax) { 31762306a36Sopenharmony_ci tmin = (end < buf_min ? buf_min : end + 1); 31862306a36Sopenharmony_ci ret = __locate_mem_hole_top_down(kbuf, tmin, tmax); 31962306a36Sopenharmony_ci if (!ret) 32062306a36Sopenharmony_ci return 0; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci tmax = start - 1; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (tmax < buf_min) { 32662306a36Sopenharmony_ci ret = err; 32762306a36Sopenharmony_ci break; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci ret = 0; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (!ret) { 33362306a36Sopenharmony_ci tmin = buf_min; 33462306a36Sopenharmony_ci ret = __locate_mem_hole_top_down(kbuf, tmin, tmax); 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci return ret; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci/** 34062306a36Sopenharmony_ci * __locate_mem_hole_bottom_up - Looks bottom up for a large enough memory hole 34162306a36Sopenharmony_ci * in the memory regions between buf_min & buf_max 34262306a36Sopenharmony_ci * for the buffer. If found, sets kbuf->mem. 34362306a36Sopenharmony_ci * @kbuf: Buffer contents and memory parameters. 34462306a36Sopenharmony_ci * @buf_min: Minimum address for the buffer. 34562306a36Sopenharmony_ci * @buf_max: Maximum address for the buffer. 34662306a36Sopenharmony_ci * 34762306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_cistatic int __locate_mem_hole_bottom_up(struct kexec_buf *kbuf, 35062306a36Sopenharmony_ci u64 buf_min, u64 buf_max) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci int ret = -EADDRNOTAVAIL; 35362306a36Sopenharmony_ci phys_addr_t start, end; 35462306a36Sopenharmony_ci u64 i; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci for_each_mem_range(i, &start, &end) { 35762306a36Sopenharmony_ci /* 35862306a36Sopenharmony_ci * memblock uses [start, end) convention while it is 35962306a36Sopenharmony_ci * [start, end] here. Fix the off-by-one to have the 36062306a36Sopenharmony_ci * same convention. 36162306a36Sopenharmony_ci */ 36262306a36Sopenharmony_ci end -= 1; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (end < buf_min) 36562306a36Sopenharmony_ci continue; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* Memory hole not found */ 36862306a36Sopenharmony_ci if (start > buf_max) 36962306a36Sopenharmony_ci break; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* Adjust memory region based on the given range */ 37262306a36Sopenharmony_ci if (start < buf_min) 37362306a36Sopenharmony_ci start = buf_min; 37462306a36Sopenharmony_ci if (end > buf_max) 37562306a36Sopenharmony_ci end = buf_max; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci start = ALIGN(start, kbuf->buf_align); 37862306a36Sopenharmony_ci if (start < end && (end - start + 1) >= kbuf->memsz) { 37962306a36Sopenharmony_ci /* Suitable memory range found. Set kbuf->mem */ 38062306a36Sopenharmony_ci kbuf->mem = start; 38162306a36Sopenharmony_ci ret = 0; 38262306a36Sopenharmony_ci break; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci return ret; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci/** 39062306a36Sopenharmony_ci * locate_mem_hole_bottom_up_ppc64 - Skip special memory regions to find a 39162306a36Sopenharmony_ci * suitable buffer with bottom up approach. 39262306a36Sopenharmony_ci * @kbuf: Buffer contents and memory parameters. 39362306a36Sopenharmony_ci * @buf_min: Minimum address for the buffer. 39462306a36Sopenharmony_ci * @buf_max: Maximum address for the buffer. 39562306a36Sopenharmony_ci * @emem: Exclude memory ranges. 39662306a36Sopenharmony_ci * 39762306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 39862306a36Sopenharmony_ci */ 39962306a36Sopenharmony_cistatic int locate_mem_hole_bottom_up_ppc64(struct kexec_buf *kbuf, 40062306a36Sopenharmony_ci u64 buf_min, u64 buf_max, 40162306a36Sopenharmony_ci const struct crash_mem *emem) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci int i, ret = 0, err = -EADDRNOTAVAIL; 40462306a36Sopenharmony_ci u64 start, end, tmin, tmax; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci tmin = buf_min; 40762306a36Sopenharmony_ci for (i = 0; i < emem->nr_ranges; i++) { 40862306a36Sopenharmony_ci start = emem->ranges[i].start; 40962306a36Sopenharmony_ci end = emem->ranges[i].end; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (end < tmin) 41262306a36Sopenharmony_ci continue; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (start > tmin) { 41562306a36Sopenharmony_ci tmax = (start > buf_max ? buf_max : start - 1); 41662306a36Sopenharmony_ci ret = __locate_mem_hole_bottom_up(kbuf, tmin, tmax); 41762306a36Sopenharmony_ci if (!ret) 41862306a36Sopenharmony_ci return 0; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci tmin = end + 1; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (tmin > buf_max) { 42462306a36Sopenharmony_ci ret = err; 42562306a36Sopenharmony_ci break; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci ret = 0; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (!ret) { 43162306a36Sopenharmony_ci tmax = buf_max; 43262306a36Sopenharmony_ci ret = __locate_mem_hole_bottom_up(kbuf, tmin, tmax); 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci return ret; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci/** 43862306a36Sopenharmony_ci * check_realloc_usable_mem - Reallocate buffer if it can't accommodate entries 43962306a36Sopenharmony_ci * @um_info: Usable memory buffer and ranges info. 44062306a36Sopenharmony_ci * @cnt: No. of entries to accommodate. 44162306a36Sopenharmony_ci * 44262306a36Sopenharmony_ci * Frees up the old buffer if memory reallocation fails. 44362306a36Sopenharmony_ci * 44462306a36Sopenharmony_ci * Returns buffer on success, NULL on error. 44562306a36Sopenharmony_ci */ 44662306a36Sopenharmony_cistatic u64 *check_realloc_usable_mem(struct umem_info *um_info, int cnt) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci u32 new_size; 44962306a36Sopenharmony_ci u64 *tbuf; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if ((um_info->idx + cnt) <= um_info->max_entries) 45262306a36Sopenharmony_ci return um_info->buf; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci new_size = um_info->size + MEM_RANGE_CHUNK_SZ; 45562306a36Sopenharmony_ci tbuf = krealloc(um_info->buf, new_size, GFP_KERNEL); 45662306a36Sopenharmony_ci if (tbuf) { 45762306a36Sopenharmony_ci um_info->buf = tbuf; 45862306a36Sopenharmony_ci um_info->size = new_size; 45962306a36Sopenharmony_ci um_info->max_entries = (um_info->size / sizeof(u64)); 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci return tbuf; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci/** 46662306a36Sopenharmony_ci * add_usable_mem - Add the usable memory ranges within the given memory range 46762306a36Sopenharmony_ci * to the buffer 46862306a36Sopenharmony_ci * @um_info: Usable memory buffer and ranges info. 46962306a36Sopenharmony_ci * @base: Base address of memory range to look for. 47062306a36Sopenharmony_ci * @end: End address of memory range to look for. 47162306a36Sopenharmony_ci * 47262306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 47362306a36Sopenharmony_ci */ 47462306a36Sopenharmony_cistatic int add_usable_mem(struct umem_info *um_info, u64 base, u64 end) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci u64 loc_base, loc_end; 47762306a36Sopenharmony_ci bool add; 47862306a36Sopenharmony_ci int i; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci for (i = 0; i < um_info->nr_ranges; i++) { 48162306a36Sopenharmony_ci add = false; 48262306a36Sopenharmony_ci loc_base = um_info->ranges[i].start; 48362306a36Sopenharmony_ci loc_end = um_info->ranges[i].end; 48462306a36Sopenharmony_ci if (loc_base >= base && loc_end <= end) 48562306a36Sopenharmony_ci add = true; 48662306a36Sopenharmony_ci else if (base < loc_end && end > loc_base) { 48762306a36Sopenharmony_ci if (loc_base < base) 48862306a36Sopenharmony_ci loc_base = base; 48962306a36Sopenharmony_ci if (loc_end > end) 49062306a36Sopenharmony_ci loc_end = end; 49162306a36Sopenharmony_ci add = true; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (add) { 49562306a36Sopenharmony_ci if (!check_realloc_usable_mem(um_info, 2)) 49662306a36Sopenharmony_ci return -ENOMEM; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci um_info->buf[um_info->idx++] = cpu_to_be64(loc_base); 49962306a36Sopenharmony_ci um_info->buf[um_info->idx++] = 50062306a36Sopenharmony_ci cpu_to_be64(loc_end - loc_base + 1); 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci return 0; 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci/** 50862306a36Sopenharmony_ci * kdump_setup_usable_lmb - This is a callback function that gets called by 50962306a36Sopenharmony_ci * walk_drmem_lmbs for every LMB to set its 51062306a36Sopenharmony_ci * usable memory ranges. 51162306a36Sopenharmony_ci * @lmb: LMB info. 51262306a36Sopenharmony_ci * @usm: linux,drconf-usable-memory property value. 51362306a36Sopenharmony_ci * @data: Pointer to usable memory buffer and ranges info. 51462306a36Sopenharmony_ci * 51562306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 51662306a36Sopenharmony_ci */ 51762306a36Sopenharmony_cistatic int kdump_setup_usable_lmb(struct drmem_lmb *lmb, const __be32 **usm, 51862306a36Sopenharmony_ci void *data) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci struct umem_info *um_info; 52162306a36Sopenharmony_ci int tmp_idx, ret; 52262306a36Sopenharmony_ci u64 base, end; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* 52562306a36Sopenharmony_ci * kdump load isn't supported on kernels already booted with 52662306a36Sopenharmony_ci * linux,drconf-usable-memory property. 52762306a36Sopenharmony_ci */ 52862306a36Sopenharmony_ci if (*usm) { 52962306a36Sopenharmony_ci pr_err("linux,drconf-usable-memory property already exists!"); 53062306a36Sopenharmony_ci return -EINVAL; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci um_info = data; 53462306a36Sopenharmony_ci tmp_idx = um_info->idx; 53562306a36Sopenharmony_ci if (!check_realloc_usable_mem(um_info, 1)) 53662306a36Sopenharmony_ci return -ENOMEM; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci um_info->idx++; 53962306a36Sopenharmony_ci base = lmb->base_addr; 54062306a36Sopenharmony_ci end = base + drmem_lmb_size() - 1; 54162306a36Sopenharmony_ci ret = add_usable_mem(um_info, base, end); 54262306a36Sopenharmony_ci if (!ret) { 54362306a36Sopenharmony_ci /* 54462306a36Sopenharmony_ci * Update the no. of ranges added. Two entries (base & size) 54562306a36Sopenharmony_ci * for every range added. 54662306a36Sopenharmony_ci */ 54762306a36Sopenharmony_ci um_info->buf[tmp_idx] = 54862306a36Sopenharmony_ci cpu_to_be64((um_info->idx - tmp_idx - 1) / 2); 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci return ret; 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci#define NODE_PATH_LEN 256 55562306a36Sopenharmony_ci/** 55662306a36Sopenharmony_ci * add_usable_mem_property - Add usable memory property for the given 55762306a36Sopenharmony_ci * memory node. 55862306a36Sopenharmony_ci * @fdt: Flattened device tree for the kdump kernel. 55962306a36Sopenharmony_ci * @dn: Memory node. 56062306a36Sopenharmony_ci * @um_info: Usable memory buffer and ranges info. 56162306a36Sopenharmony_ci * 56262306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 56362306a36Sopenharmony_ci */ 56462306a36Sopenharmony_cistatic int add_usable_mem_property(void *fdt, struct device_node *dn, 56562306a36Sopenharmony_ci struct umem_info *um_info) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci int n_mem_addr_cells, n_mem_size_cells, node; 56862306a36Sopenharmony_ci char path[NODE_PATH_LEN]; 56962306a36Sopenharmony_ci int i, len, ranges, ret; 57062306a36Sopenharmony_ci const __be32 *prop; 57162306a36Sopenharmony_ci u64 base, end; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci of_node_get(dn); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (snprintf(path, NODE_PATH_LEN, "%pOF", dn) > (NODE_PATH_LEN - 1)) { 57662306a36Sopenharmony_ci pr_err("Buffer (%d) too small for memory node: %pOF\n", 57762306a36Sopenharmony_ci NODE_PATH_LEN, dn); 57862306a36Sopenharmony_ci return -EOVERFLOW; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci pr_debug("Memory node path: %s\n", path); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci /* Now that we know the path, find its offset in kdump kernel's fdt */ 58362306a36Sopenharmony_ci node = fdt_path_offset(fdt, path); 58462306a36Sopenharmony_ci if (node < 0) { 58562306a36Sopenharmony_ci pr_err("Malformed device tree: error reading %s\n", path); 58662306a36Sopenharmony_ci ret = -EINVAL; 58762306a36Sopenharmony_ci goto out; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci /* Get the address & size cells */ 59162306a36Sopenharmony_ci n_mem_addr_cells = of_n_addr_cells(dn); 59262306a36Sopenharmony_ci n_mem_size_cells = of_n_size_cells(dn); 59362306a36Sopenharmony_ci pr_debug("address cells: %d, size cells: %d\n", n_mem_addr_cells, 59462306a36Sopenharmony_ci n_mem_size_cells); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci um_info->idx = 0; 59762306a36Sopenharmony_ci if (!check_realloc_usable_mem(um_info, 2)) { 59862306a36Sopenharmony_ci ret = -ENOMEM; 59962306a36Sopenharmony_ci goto out; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci prop = of_get_property(dn, "reg", &len); 60362306a36Sopenharmony_ci if (!prop || len <= 0) { 60462306a36Sopenharmony_ci ret = 0; 60562306a36Sopenharmony_ci goto out; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* 60962306a36Sopenharmony_ci * "reg" property represents sequence of (addr,size) tuples 61062306a36Sopenharmony_ci * each representing a memory range. 61162306a36Sopenharmony_ci */ 61262306a36Sopenharmony_ci ranges = (len >> 2) / (n_mem_addr_cells + n_mem_size_cells); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci for (i = 0; i < ranges; i++) { 61562306a36Sopenharmony_ci base = of_read_number(prop, n_mem_addr_cells); 61662306a36Sopenharmony_ci prop += n_mem_addr_cells; 61762306a36Sopenharmony_ci end = base + of_read_number(prop, n_mem_size_cells) - 1; 61862306a36Sopenharmony_ci prop += n_mem_size_cells; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci ret = add_usable_mem(um_info, base, end); 62162306a36Sopenharmony_ci if (ret) 62262306a36Sopenharmony_ci goto out; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci /* 62662306a36Sopenharmony_ci * No kdump kernel usable memory found in this memory node. 62762306a36Sopenharmony_ci * Write (0,0) tuple in linux,usable-memory property for 62862306a36Sopenharmony_ci * this region to be ignored. 62962306a36Sopenharmony_ci */ 63062306a36Sopenharmony_ci if (um_info->idx == 0) { 63162306a36Sopenharmony_ci um_info->buf[0] = 0; 63262306a36Sopenharmony_ci um_info->buf[1] = 0; 63362306a36Sopenharmony_ci um_info->idx = 2; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci ret = fdt_setprop(fdt, node, "linux,usable-memory", um_info->buf, 63762306a36Sopenharmony_ci (um_info->idx * sizeof(u64))); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ciout: 64062306a36Sopenharmony_ci of_node_put(dn); 64162306a36Sopenharmony_ci return ret; 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci/** 64662306a36Sopenharmony_ci * update_usable_mem_fdt - Updates kdump kernel's fdt with linux,usable-memory 64762306a36Sopenharmony_ci * and linux,drconf-usable-memory DT properties as 64862306a36Sopenharmony_ci * appropriate to restrict its memory usage. 64962306a36Sopenharmony_ci * @fdt: Flattened device tree for the kdump kernel. 65062306a36Sopenharmony_ci * @usable_mem: Usable memory ranges for kdump kernel. 65162306a36Sopenharmony_ci * 65262306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 65362306a36Sopenharmony_ci */ 65462306a36Sopenharmony_cistatic int update_usable_mem_fdt(void *fdt, struct crash_mem *usable_mem) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci struct umem_info um_info; 65762306a36Sopenharmony_ci struct device_node *dn; 65862306a36Sopenharmony_ci int node, ret = 0; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (!usable_mem) { 66162306a36Sopenharmony_ci pr_err("Usable memory ranges for kdump kernel not found\n"); 66262306a36Sopenharmony_ci return -ENOENT; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci node = fdt_path_offset(fdt, "/ibm,dynamic-reconfiguration-memory"); 66662306a36Sopenharmony_ci if (node == -FDT_ERR_NOTFOUND) 66762306a36Sopenharmony_ci pr_debug("No dynamic reconfiguration memory found\n"); 66862306a36Sopenharmony_ci else if (node < 0) { 66962306a36Sopenharmony_ci pr_err("Malformed device tree: error reading /ibm,dynamic-reconfiguration-memory.\n"); 67062306a36Sopenharmony_ci return -EINVAL; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci um_info.buf = NULL; 67462306a36Sopenharmony_ci um_info.size = 0; 67562306a36Sopenharmony_ci um_info.max_entries = 0; 67662306a36Sopenharmony_ci um_info.idx = 0; 67762306a36Sopenharmony_ci /* Memory ranges to look up */ 67862306a36Sopenharmony_ci um_info.ranges = &(usable_mem->ranges[0]); 67962306a36Sopenharmony_ci um_info.nr_ranges = usable_mem->nr_ranges; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci dn = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory"); 68262306a36Sopenharmony_ci if (dn) { 68362306a36Sopenharmony_ci ret = walk_drmem_lmbs(dn, &um_info, kdump_setup_usable_lmb); 68462306a36Sopenharmony_ci of_node_put(dn); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if (ret) { 68762306a36Sopenharmony_ci pr_err("Could not setup linux,drconf-usable-memory property for kdump\n"); 68862306a36Sopenharmony_ci goto out; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci ret = fdt_setprop(fdt, node, "linux,drconf-usable-memory", 69262306a36Sopenharmony_ci um_info.buf, (um_info.idx * sizeof(u64))); 69362306a36Sopenharmony_ci if (ret) { 69462306a36Sopenharmony_ci pr_err("Failed to update fdt with linux,drconf-usable-memory property: %s", 69562306a36Sopenharmony_ci fdt_strerror(ret)); 69662306a36Sopenharmony_ci goto out; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci /* 70162306a36Sopenharmony_ci * Walk through each memory node and set linux,usable-memory property 70262306a36Sopenharmony_ci * for the corresponding node in kdump kernel's fdt. 70362306a36Sopenharmony_ci */ 70462306a36Sopenharmony_ci for_each_node_by_type(dn, "memory") { 70562306a36Sopenharmony_ci ret = add_usable_mem_property(fdt, dn, &um_info); 70662306a36Sopenharmony_ci if (ret) { 70762306a36Sopenharmony_ci pr_err("Failed to set linux,usable-memory property for %s node", 70862306a36Sopenharmony_ci dn->full_name); 70962306a36Sopenharmony_ci of_node_put(dn); 71062306a36Sopenharmony_ci goto out; 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ciout: 71562306a36Sopenharmony_ci kfree(um_info.buf); 71662306a36Sopenharmony_ci return ret; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci/** 72062306a36Sopenharmony_ci * load_backup_segment - Locate a memory hole to place the backup region. 72162306a36Sopenharmony_ci * @image: Kexec image. 72262306a36Sopenharmony_ci * @kbuf: Buffer contents and memory parameters. 72362306a36Sopenharmony_ci * 72462306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 72562306a36Sopenharmony_ci */ 72662306a36Sopenharmony_cistatic int load_backup_segment(struct kimage *image, struct kexec_buf *kbuf) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci void *buf; 72962306a36Sopenharmony_ci int ret; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci /* 73262306a36Sopenharmony_ci * Setup a source buffer for backup segment. 73362306a36Sopenharmony_ci * 73462306a36Sopenharmony_ci * A source buffer has no meaning for backup region as data will 73562306a36Sopenharmony_ci * be copied from backup source, after crash, in the purgatory. 73662306a36Sopenharmony_ci * But as load segment code doesn't recognize such segments, 73762306a36Sopenharmony_ci * setup a dummy source buffer to keep it happy for now. 73862306a36Sopenharmony_ci */ 73962306a36Sopenharmony_ci buf = vzalloc(BACKUP_SRC_SIZE); 74062306a36Sopenharmony_ci if (!buf) 74162306a36Sopenharmony_ci return -ENOMEM; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci kbuf->buffer = buf; 74462306a36Sopenharmony_ci kbuf->mem = KEXEC_BUF_MEM_UNKNOWN; 74562306a36Sopenharmony_ci kbuf->bufsz = kbuf->memsz = BACKUP_SRC_SIZE; 74662306a36Sopenharmony_ci kbuf->top_down = false; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci ret = kexec_add_buffer(kbuf); 74962306a36Sopenharmony_ci if (ret) { 75062306a36Sopenharmony_ci vfree(buf); 75162306a36Sopenharmony_ci return ret; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci image->arch.backup_buf = buf; 75562306a36Sopenharmony_ci image->arch.backup_start = kbuf->mem; 75662306a36Sopenharmony_ci return 0; 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci/** 76062306a36Sopenharmony_ci * update_backup_region_phdr - Update backup region's offset for the core to 76162306a36Sopenharmony_ci * export the region appropriately. 76262306a36Sopenharmony_ci * @image: Kexec image. 76362306a36Sopenharmony_ci * @ehdr: ELF core header. 76462306a36Sopenharmony_ci * 76562306a36Sopenharmony_ci * Assumes an exclusive program header is setup for the backup region 76662306a36Sopenharmony_ci * in the ELF headers 76762306a36Sopenharmony_ci * 76862306a36Sopenharmony_ci * Returns nothing. 76962306a36Sopenharmony_ci */ 77062306a36Sopenharmony_cistatic void update_backup_region_phdr(struct kimage *image, Elf64_Ehdr *ehdr) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci Elf64_Phdr *phdr; 77362306a36Sopenharmony_ci unsigned int i; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci phdr = (Elf64_Phdr *)(ehdr + 1); 77662306a36Sopenharmony_ci for (i = 0; i < ehdr->e_phnum; i++) { 77762306a36Sopenharmony_ci if (phdr->p_paddr == BACKUP_SRC_START) { 77862306a36Sopenharmony_ci phdr->p_offset = image->arch.backup_start; 77962306a36Sopenharmony_ci pr_debug("Backup region offset updated to 0x%lx\n", 78062306a36Sopenharmony_ci image->arch.backup_start); 78162306a36Sopenharmony_ci return; 78262306a36Sopenharmony_ci } 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci/** 78762306a36Sopenharmony_ci * load_elfcorehdr_segment - Setup crash memory ranges and initialize elfcorehdr 78862306a36Sopenharmony_ci * segment needed to load kdump kernel. 78962306a36Sopenharmony_ci * @image: Kexec image. 79062306a36Sopenharmony_ci * @kbuf: Buffer contents and memory parameters. 79162306a36Sopenharmony_ci * 79262306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 79362306a36Sopenharmony_ci */ 79462306a36Sopenharmony_cistatic int load_elfcorehdr_segment(struct kimage *image, struct kexec_buf *kbuf) 79562306a36Sopenharmony_ci{ 79662306a36Sopenharmony_ci struct crash_mem *cmem = NULL; 79762306a36Sopenharmony_ci unsigned long headers_sz; 79862306a36Sopenharmony_ci void *headers = NULL; 79962306a36Sopenharmony_ci int ret; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci ret = get_crash_memory_ranges(&cmem); 80262306a36Sopenharmony_ci if (ret) 80362306a36Sopenharmony_ci goto out; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci /* Setup elfcorehdr segment */ 80662306a36Sopenharmony_ci ret = crash_prepare_elf64_headers(cmem, false, &headers, &headers_sz); 80762306a36Sopenharmony_ci if (ret) { 80862306a36Sopenharmony_ci pr_err("Failed to prepare elf headers for the core\n"); 80962306a36Sopenharmony_ci goto out; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci /* Fix the offset for backup region in the ELF header */ 81362306a36Sopenharmony_ci update_backup_region_phdr(image, headers); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci kbuf->buffer = headers; 81662306a36Sopenharmony_ci kbuf->mem = KEXEC_BUF_MEM_UNKNOWN; 81762306a36Sopenharmony_ci kbuf->bufsz = kbuf->memsz = headers_sz; 81862306a36Sopenharmony_ci kbuf->top_down = false; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci ret = kexec_add_buffer(kbuf); 82162306a36Sopenharmony_ci if (ret) { 82262306a36Sopenharmony_ci vfree(headers); 82362306a36Sopenharmony_ci goto out; 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci image->elf_load_addr = kbuf->mem; 82762306a36Sopenharmony_ci image->elf_headers_sz = headers_sz; 82862306a36Sopenharmony_ci image->elf_headers = headers; 82962306a36Sopenharmony_ciout: 83062306a36Sopenharmony_ci kfree(cmem); 83162306a36Sopenharmony_ci return ret; 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci/** 83562306a36Sopenharmony_ci * load_crashdump_segments_ppc64 - Initialize the additional segements needed 83662306a36Sopenharmony_ci * to load kdump kernel. 83762306a36Sopenharmony_ci * @image: Kexec image. 83862306a36Sopenharmony_ci * @kbuf: Buffer contents and memory parameters. 83962306a36Sopenharmony_ci * 84062306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 84162306a36Sopenharmony_ci */ 84262306a36Sopenharmony_ciint load_crashdump_segments_ppc64(struct kimage *image, 84362306a36Sopenharmony_ci struct kexec_buf *kbuf) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci int ret; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci /* Load backup segment - first 64K bytes of the crashing kernel */ 84862306a36Sopenharmony_ci ret = load_backup_segment(image, kbuf); 84962306a36Sopenharmony_ci if (ret) { 85062306a36Sopenharmony_ci pr_err("Failed to load backup segment\n"); 85162306a36Sopenharmony_ci return ret; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci pr_debug("Loaded the backup region at 0x%lx\n", kbuf->mem); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci /* Load elfcorehdr segment - to export crashing kernel's vmcore */ 85662306a36Sopenharmony_ci ret = load_elfcorehdr_segment(image, kbuf); 85762306a36Sopenharmony_ci if (ret) { 85862306a36Sopenharmony_ci pr_err("Failed to load elfcorehdr segment\n"); 85962306a36Sopenharmony_ci return ret; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci pr_debug("Loaded elf core header at 0x%lx, bufsz=0x%lx memsz=0x%lx\n", 86262306a36Sopenharmony_ci image->elf_load_addr, kbuf->bufsz, kbuf->memsz); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci return 0; 86562306a36Sopenharmony_ci} 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci/** 86862306a36Sopenharmony_ci * setup_purgatory_ppc64 - initialize PPC64 specific purgatory's global 86962306a36Sopenharmony_ci * variables and call setup_purgatory() to initialize 87062306a36Sopenharmony_ci * common global variable. 87162306a36Sopenharmony_ci * @image: kexec image. 87262306a36Sopenharmony_ci * @slave_code: Slave code for the purgatory. 87362306a36Sopenharmony_ci * @fdt: Flattened device tree for the next kernel. 87462306a36Sopenharmony_ci * @kernel_load_addr: Address where the kernel is loaded. 87562306a36Sopenharmony_ci * @fdt_load_addr: Address where the flattened device tree is loaded. 87662306a36Sopenharmony_ci * 87762306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 87862306a36Sopenharmony_ci */ 87962306a36Sopenharmony_ciint setup_purgatory_ppc64(struct kimage *image, const void *slave_code, 88062306a36Sopenharmony_ci const void *fdt, unsigned long kernel_load_addr, 88162306a36Sopenharmony_ci unsigned long fdt_load_addr) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci struct device_node *dn = NULL; 88462306a36Sopenharmony_ci int ret; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci ret = setup_purgatory(image, slave_code, fdt, kernel_load_addr, 88762306a36Sopenharmony_ci fdt_load_addr); 88862306a36Sopenharmony_ci if (ret) 88962306a36Sopenharmony_ci goto out; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci if (image->type == KEXEC_TYPE_CRASH) { 89262306a36Sopenharmony_ci u32 my_run_at_load = 1; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci /* 89562306a36Sopenharmony_ci * Tell relocatable kernel to run at load address 89662306a36Sopenharmony_ci * via the word meant for that at 0x5c. 89762306a36Sopenharmony_ci */ 89862306a36Sopenharmony_ci ret = kexec_purgatory_get_set_symbol(image, "run_at_load", 89962306a36Sopenharmony_ci &my_run_at_load, 90062306a36Sopenharmony_ci sizeof(my_run_at_load), 90162306a36Sopenharmony_ci false); 90262306a36Sopenharmony_ci if (ret) 90362306a36Sopenharmony_ci goto out; 90462306a36Sopenharmony_ci } 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci /* Tell purgatory where to look for backup region */ 90762306a36Sopenharmony_ci ret = kexec_purgatory_get_set_symbol(image, "backup_start", 90862306a36Sopenharmony_ci &image->arch.backup_start, 90962306a36Sopenharmony_ci sizeof(image->arch.backup_start), 91062306a36Sopenharmony_ci false); 91162306a36Sopenharmony_ci if (ret) 91262306a36Sopenharmony_ci goto out; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci /* Setup OPAL base & entry values */ 91562306a36Sopenharmony_ci dn = of_find_node_by_path("/ibm,opal"); 91662306a36Sopenharmony_ci if (dn) { 91762306a36Sopenharmony_ci u64 val; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci of_property_read_u64(dn, "opal-base-address", &val); 92062306a36Sopenharmony_ci ret = kexec_purgatory_get_set_symbol(image, "opal_base", &val, 92162306a36Sopenharmony_ci sizeof(val), false); 92262306a36Sopenharmony_ci if (ret) 92362306a36Sopenharmony_ci goto out; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci of_property_read_u64(dn, "opal-entry-address", &val); 92662306a36Sopenharmony_ci ret = kexec_purgatory_get_set_symbol(image, "opal_entry", &val, 92762306a36Sopenharmony_ci sizeof(val), false); 92862306a36Sopenharmony_ci } 92962306a36Sopenharmony_ciout: 93062306a36Sopenharmony_ci if (ret) 93162306a36Sopenharmony_ci pr_err("Failed to setup purgatory symbols"); 93262306a36Sopenharmony_ci of_node_put(dn); 93362306a36Sopenharmony_ci return ret; 93462306a36Sopenharmony_ci} 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci/** 93762306a36Sopenharmony_ci * cpu_node_size - Compute the size of a CPU node in the FDT. 93862306a36Sopenharmony_ci * This should be done only once and the value is stored in 93962306a36Sopenharmony_ci * a static variable. 94062306a36Sopenharmony_ci * Returns the max size of a CPU node in the FDT. 94162306a36Sopenharmony_ci */ 94262306a36Sopenharmony_cistatic unsigned int cpu_node_size(void) 94362306a36Sopenharmony_ci{ 94462306a36Sopenharmony_ci static unsigned int size; 94562306a36Sopenharmony_ci struct device_node *dn; 94662306a36Sopenharmony_ci struct property *pp; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci /* 94962306a36Sopenharmony_ci * Don't compute it twice, we are assuming that the per CPU node size 95062306a36Sopenharmony_ci * doesn't change during the system's life. 95162306a36Sopenharmony_ci */ 95262306a36Sopenharmony_ci if (size) 95362306a36Sopenharmony_ci return size; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci dn = of_find_node_by_type(NULL, "cpu"); 95662306a36Sopenharmony_ci if (WARN_ON_ONCE(!dn)) { 95762306a36Sopenharmony_ci // Unlikely to happen 95862306a36Sopenharmony_ci return 0; 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci /* 96262306a36Sopenharmony_ci * We compute the sub node size for a CPU node, assuming it 96362306a36Sopenharmony_ci * will be the same for all. 96462306a36Sopenharmony_ci */ 96562306a36Sopenharmony_ci size += strlen(dn->name) + 5; 96662306a36Sopenharmony_ci for_each_property_of_node(dn, pp) { 96762306a36Sopenharmony_ci size += strlen(pp->name); 96862306a36Sopenharmony_ci size += pp->length; 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci of_node_put(dn); 97262306a36Sopenharmony_ci return size; 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci/** 97662306a36Sopenharmony_ci * kexec_extra_fdt_size_ppc64 - Return the estimated additional size needed to 97762306a36Sopenharmony_ci * setup FDT for kexec/kdump kernel. 97862306a36Sopenharmony_ci * @image: kexec image being loaded. 97962306a36Sopenharmony_ci * 98062306a36Sopenharmony_ci * Returns the estimated extra size needed for kexec/kdump kernel FDT. 98162306a36Sopenharmony_ci */ 98262306a36Sopenharmony_ciunsigned int kexec_extra_fdt_size_ppc64(struct kimage *image) 98362306a36Sopenharmony_ci{ 98462306a36Sopenharmony_ci unsigned int cpu_nodes, extra_size = 0; 98562306a36Sopenharmony_ci struct device_node *dn; 98662306a36Sopenharmony_ci u64 usm_entries; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci // Budget some space for the password blob. There's already extra space 98962306a36Sopenharmony_ci // for the key name 99062306a36Sopenharmony_ci if (plpks_is_available()) 99162306a36Sopenharmony_ci extra_size += (unsigned int)plpks_get_passwordlen(); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci if (image->type != KEXEC_TYPE_CRASH) 99462306a36Sopenharmony_ci return extra_size; 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci /* 99762306a36Sopenharmony_ci * For kdump kernel, account for linux,usable-memory and 99862306a36Sopenharmony_ci * linux,drconf-usable-memory properties. Get an approximate on the 99962306a36Sopenharmony_ci * number of usable memory entries and use for FDT size estimation. 100062306a36Sopenharmony_ci */ 100162306a36Sopenharmony_ci if (drmem_lmb_size()) { 100262306a36Sopenharmony_ci usm_entries = ((memory_hotplug_max() / drmem_lmb_size()) + 100362306a36Sopenharmony_ci (2 * (resource_size(&crashk_res) / drmem_lmb_size()))); 100462306a36Sopenharmony_ci extra_size += (unsigned int)(usm_entries * sizeof(u64)); 100562306a36Sopenharmony_ci } 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci /* 100862306a36Sopenharmony_ci * Get the number of CPU nodes in the current DT. This allows to 100962306a36Sopenharmony_ci * reserve places for CPU nodes added since the boot time. 101062306a36Sopenharmony_ci */ 101162306a36Sopenharmony_ci cpu_nodes = 0; 101262306a36Sopenharmony_ci for_each_node_by_type(dn, "cpu") { 101362306a36Sopenharmony_ci cpu_nodes++; 101462306a36Sopenharmony_ci } 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci if (cpu_nodes > boot_cpu_node_count) 101762306a36Sopenharmony_ci extra_size += (cpu_nodes - boot_cpu_node_count) * cpu_node_size(); 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci return extra_size; 102062306a36Sopenharmony_ci} 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci/** 102362306a36Sopenharmony_ci * add_node_props - Reads node properties from device node structure and add 102462306a36Sopenharmony_ci * them to fdt. 102562306a36Sopenharmony_ci * @fdt: Flattened device tree of the kernel 102662306a36Sopenharmony_ci * @node_offset: offset of the node to add a property at 102762306a36Sopenharmony_ci * @dn: device node pointer 102862306a36Sopenharmony_ci * 102962306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 103062306a36Sopenharmony_ci */ 103162306a36Sopenharmony_cistatic int add_node_props(void *fdt, int node_offset, const struct device_node *dn) 103262306a36Sopenharmony_ci{ 103362306a36Sopenharmony_ci int ret = 0; 103462306a36Sopenharmony_ci struct property *pp; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci if (!dn) 103762306a36Sopenharmony_ci return -EINVAL; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci for_each_property_of_node(dn, pp) { 104062306a36Sopenharmony_ci ret = fdt_setprop(fdt, node_offset, pp->name, pp->value, pp->length); 104162306a36Sopenharmony_ci if (ret < 0) { 104262306a36Sopenharmony_ci pr_err("Unable to add %s property: %s\n", pp->name, fdt_strerror(ret)); 104362306a36Sopenharmony_ci return ret; 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci return ret; 104762306a36Sopenharmony_ci} 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci/** 105062306a36Sopenharmony_ci * update_cpus_node - Update cpus node of flattened device tree using of_root 105162306a36Sopenharmony_ci * device node. 105262306a36Sopenharmony_ci * @fdt: Flattened device tree of the kernel. 105362306a36Sopenharmony_ci * 105462306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 105562306a36Sopenharmony_ci */ 105662306a36Sopenharmony_cistatic int update_cpus_node(void *fdt) 105762306a36Sopenharmony_ci{ 105862306a36Sopenharmony_ci struct device_node *cpus_node, *dn; 105962306a36Sopenharmony_ci int cpus_offset, cpus_subnode_offset, ret = 0; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci cpus_offset = fdt_path_offset(fdt, "/cpus"); 106262306a36Sopenharmony_ci if (cpus_offset < 0 && cpus_offset != -FDT_ERR_NOTFOUND) { 106362306a36Sopenharmony_ci pr_err("Malformed device tree: error reading /cpus node: %s\n", 106462306a36Sopenharmony_ci fdt_strerror(cpus_offset)); 106562306a36Sopenharmony_ci return cpus_offset; 106662306a36Sopenharmony_ci } 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci if (cpus_offset > 0) { 106962306a36Sopenharmony_ci ret = fdt_del_node(fdt, cpus_offset); 107062306a36Sopenharmony_ci if (ret < 0) { 107162306a36Sopenharmony_ci pr_err("Error deleting /cpus node: %s\n", fdt_strerror(ret)); 107262306a36Sopenharmony_ci return -EINVAL; 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci } 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci /* Add cpus node to fdt */ 107762306a36Sopenharmony_ci cpus_offset = fdt_add_subnode(fdt, fdt_path_offset(fdt, "/"), "cpus"); 107862306a36Sopenharmony_ci if (cpus_offset < 0) { 107962306a36Sopenharmony_ci pr_err("Error creating /cpus node: %s\n", fdt_strerror(cpus_offset)); 108062306a36Sopenharmony_ci return -EINVAL; 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci /* Add cpus node properties */ 108462306a36Sopenharmony_ci cpus_node = of_find_node_by_path("/cpus"); 108562306a36Sopenharmony_ci ret = add_node_props(fdt, cpus_offset, cpus_node); 108662306a36Sopenharmony_ci of_node_put(cpus_node); 108762306a36Sopenharmony_ci if (ret < 0) 108862306a36Sopenharmony_ci return ret; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci /* Loop through all subnodes of cpus and add them to fdt */ 109162306a36Sopenharmony_ci for_each_node_by_type(dn, "cpu") { 109262306a36Sopenharmony_ci cpus_subnode_offset = fdt_add_subnode(fdt, cpus_offset, dn->full_name); 109362306a36Sopenharmony_ci if (cpus_subnode_offset < 0) { 109462306a36Sopenharmony_ci pr_err("Unable to add %s subnode: %s\n", dn->full_name, 109562306a36Sopenharmony_ci fdt_strerror(cpus_subnode_offset)); 109662306a36Sopenharmony_ci ret = cpus_subnode_offset; 109762306a36Sopenharmony_ci goto out; 109862306a36Sopenharmony_ci } 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci ret = add_node_props(fdt, cpus_subnode_offset, dn); 110162306a36Sopenharmony_ci if (ret < 0) 110262306a36Sopenharmony_ci goto out; 110362306a36Sopenharmony_ci } 110462306a36Sopenharmony_ciout: 110562306a36Sopenharmony_ci of_node_put(dn); 110662306a36Sopenharmony_ci return ret; 110762306a36Sopenharmony_ci} 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_cistatic int copy_property(void *fdt, int node_offset, const struct device_node *dn, 111062306a36Sopenharmony_ci const char *propname) 111162306a36Sopenharmony_ci{ 111262306a36Sopenharmony_ci const void *prop, *fdtprop; 111362306a36Sopenharmony_ci int len = 0, fdtlen = 0; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci prop = of_get_property(dn, propname, &len); 111662306a36Sopenharmony_ci fdtprop = fdt_getprop(fdt, node_offset, propname, &fdtlen); 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci if (fdtprop && !prop) 111962306a36Sopenharmony_ci return fdt_delprop(fdt, node_offset, propname); 112062306a36Sopenharmony_ci else if (prop) 112162306a36Sopenharmony_ci return fdt_setprop(fdt, node_offset, propname, prop, len); 112262306a36Sopenharmony_ci else 112362306a36Sopenharmony_ci return -FDT_ERR_NOTFOUND; 112462306a36Sopenharmony_ci} 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_cistatic int update_pci_dma_nodes(void *fdt, const char *dmapropname) 112762306a36Sopenharmony_ci{ 112862306a36Sopenharmony_ci struct device_node *dn; 112962306a36Sopenharmony_ci int pci_offset, root_offset, ret = 0; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci if (!firmware_has_feature(FW_FEATURE_LPAR)) 113262306a36Sopenharmony_ci return 0; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci root_offset = fdt_path_offset(fdt, "/"); 113562306a36Sopenharmony_ci for_each_node_with_property(dn, dmapropname) { 113662306a36Sopenharmony_ci pci_offset = fdt_subnode_offset(fdt, root_offset, of_node_full_name(dn)); 113762306a36Sopenharmony_ci if (pci_offset < 0) 113862306a36Sopenharmony_ci continue; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci ret = copy_property(fdt, pci_offset, dn, "ibm,dma-window"); 114162306a36Sopenharmony_ci if (ret < 0) 114262306a36Sopenharmony_ci break; 114362306a36Sopenharmony_ci ret = copy_property(fdt, pci_offset, dn, dmapropname); 114462306a36Sopenharmony_ci if (ret < 0) 114562306a36Sopenharmony_ci break; 114662306a36Sopenharmony_ci } 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci return ret; 114962306a36Sopenharmony_ci} 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci/** 115262306a36Sopenharmony_ci * setup_new_fdt_ppc64 - Update the flattend device-tree of the kernel 115362306a36Sopenharmony_ci * being loaded. 115462306a36Sopenharmony_ci * @image: kexec image being loaded. 115562306a36Sopenharmony_ci * @fdt: Flattened device tree for the next kernel. 115662306a36Sopenharmony_ci * @initrd_load_addr: Address where the next initrd will be loaded. 115762306a36Sopenharmony_ci * @initrd_len: Size of the next initrd, or 0 if there will be none. 115862306a36Sopenharmony_ci * @cmdline: Command line for the next kernel, or NULL if there will 115962306a36Sopenharmony_ci * be none. 116062306a36Sopenharmony_ci * 116162306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 116262306a36Sopenharmony_ci */ 116362306a36Sopenharmony_ciint setup_new_fdt_ppc64(const struct kimage *image, void *fdt, 116462306a36Sopenharmony_ci unsigned long initrd_load_addr, 116562306a36Sopenharmony_ci unsigned long initrd_len, const char *cmdline) 116662306a36Sopenharmony_ci{ 116762306a36Sopenharmony_ci struct crash_mem *umem = NULL, *rmem = NULL; 116862306a36Sopenharmony_ci int i, nr_ranges, ret; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci /* 117162306a36Sopenharmony_ci * Restrict memory usage for kdump kernel by setting up 117262306a36Sopenharmony_ci * usable memory ranges and memory reserve map. 117362306a36Sopenharmony_ci */ 117462306a36Sopenharmony_ci if (image->type == KEXEC_TYPE_CRASH) { 117562306a36Sopenharmony_ci ret = get_usable_memory_ranges(&umem); 117662306a36Sopenharmony_ci if (ret) 117762306a36Sopenharmony_ci goto out; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci ret = update_usable_mem_fdt(fdt, umem); 118062306a36Sopenharmony_ci if (ret) { 118162306a36Sopenharmony_ci pr_err("Error setting up usable-memory property for kdump kernel\n"); 118262306a36Sopenharmony_ci goto out; 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci /* 118662306a36Sopenharmony_ci * Ensure we don't touch crashed kernel's memory except the 118762306a36Sopenharmony_ci * first 64K of RAM, which will be backed up. 118862306a36Sopenharmony_ci */ 118962306a36Sopenharmony_ci ret = fdt_add_mem_rsv(fdt, BACKUP_SRC_END + 1, 119062306a36Sopenharmony_ci crashk_res.start - BACKUP_SRC_SIZE); 119162306a36Sopenharmony_ci if (ret) { 119262306a36Sopenharmony_ci pr_err("Error reserving crash memory: %s\n", 119362306a36Sopenharmony_ci fdt_strerror(ret)); 119462306a36Sopenharmony_ci goto out; 119562306a36Sopenharmony_ci } 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci /* Ensure backup region is not used by kdump/capture kernel */ 119862306a36Sopenharmony_ci ret = fdt_add_mem_rsv(fdt, image->arch.backup_start, 119962306a36Sopenharmony_ci BACKUP_SRC_SIZE); 120062306a36Sopenharmony_ci if (ret) { 120162306a36Sopenharmony_ci pr_err("Error reserving memory for backup: %s\n", 120262306a36Sopenharmony_ci fdt_strerror(ret)); 120362306a36Sopenharmony_ci goto out; 120462306a36Sopenharmony_ci } 120562306a36Sopenharmony_ci } 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci /* Update cpus nodes information to account hotplug CPUs. */ 120862306a36Sopenharmony_ci ret = update_cpus_node(fdt); 120962306a36Sopenharmony_ci if (ret < 0) 121062306a36Sopenharmony_ci goto out; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci ret = update_pci_dma_nodes(fdt, DIRECT64_PROPNAME); 121362306a36Sopenharmony_ci if (ret < 0) 121462306a36Sopenharmony_ci goto out; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci ret = update_pci_dma_nodes(fdt, DMA64_PROPNAME); 121762306a36Sopenharmony_ci if (ret < 0) 121862306a36Sopenharmony_ci goto out; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci /* Update memory reserve map */ 122162306a36Sopenharmony_ci ret = get_reserved_memory_ranges(&rmem); 122262306a36Sopenharmony_ci if (ret) 122362306a36Sopenharmony_ci goto out; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci nr_ranges = rmem ? rmem->nr_ranges : 0; 122662306a36Sopenharmony_ci for (i = 0; i < nr_ranges; i++) { 122762306a36Sopenharmony_ci u64 base, size; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci base = rmem->ranges[i].start; 123062306a36Sopenharmony_ci size = rmem->ranges[i].end - base + 1; 123162306a36Sopenharmony_ci ret = fdt_add_mem_rsv(fdt, base, size); 123262306a36Sopenharmony_ci if (ret) { 123362306a36Sopenharmony_ci pr_err("Error updating memory reserve map: %s\n", 123462306a36Sopenharmony_ci fdt_strerror(ret)); 123562306a36Sopenharmony_ci goto out; 123662306a36Sopenharmony_ci } 123762306a36Sopenharmony_ci } 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci // If we have PLPKS active, we need to provide the password to the new kernel 124062306a36Sopenharmony_ci if (plpks_is_available()) 124162306a36Sopenharmony_ci ret = plpks_populate_fdt(fdt); 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ciout: 124462306a36Sopenharmony_ci kfree(rmem); 124562306a36Sopenharmony_ci kfree(umem); 124662306a36Sopenharmony_ci return ret; 124762306a36Sopenharmony_ci} 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci/** 125062306a36Sopenharmony_ci * arch_kexec_locate_mem_hole - Skip special memory regions like rtas, opal, 125162306a36Sopenharmony_ci * tce-table, reserved-ranges & such (exclude 125262306a36Sopenharmony_ci * memory ranges) as they can't be used for kexec 125362306a36Sopenharmony_ci * segment buffer. Sets kbuf->mem when a suitable 125462306a36Sopenharmony_ci * memory hole is found. 125562306a36Sopenharmony_ci * @kbuf: Buffer contents and memory parameters. 125662306a36Sopenharmony_ci * 125762306a36Sopenharmony_ci * Assumes minimum of PAGE_SIZE alignment for kbuf->memsz & kbuf->buf_align. 125862306a36Sopenharmony_ci * 125962306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 126062306a36Sopenharmony_ci */ 126162306a36Sopenharmony_ciint arch_kexec_locate_mem_hole(struct kexec_buf *kbuf) 126262306a36Sopenharmony_ci{ 126362306a36Sopenharmony_ci struct crash_mem **emem; 126462306a36Sopenharmony_ci u64 buf_min, buf_max; 126562306a36Sopenharmony_ci int ret; 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci /* Look up the exclude ranges list while locating the memory hole */ 126862306a36Sopenharmony_ci emem = &(kbuf->image->arch.exclude_ranges); 126962306a36Sopenharmony_ci if (!(*emem) || ((*emem)->nr_ranges == 0)) { 127062306a36Sopenharmony_ci pr_warn("No exclude range list. Using the default locate mem hole method\n"); 127162306a36Sopenharmony_ci return kexec_locate_mem_hole(kbuf); 127262306a36Sopenharmony_ci } 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci buf_min = kbuf->buf_min; 127562306a36Sopenharmony_ci buf_max = kbuf->buf_max; 127662306a36Sopenharmony_ci /* Segments for kdump kernel should be within crashkernel region */ 127762306a36Sopenharmony_ci if (kbuf->image->type == KEXEC_TYPE_CRASH) { 127862306a36Sopenharmony_ci buf_min = (buf_min < crashk_res.start ? 127962306a36Sopenharmony_ci crashk_res.start : buf_min); 128062306a36Sopenharmony_ci buf_max = (buf_max > crashk_res.end ? 128162306a36Sopenharmony_ci crashk_res.end : buf_max); 128262306a36Sopenharmony_ci } 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci if (buf_min > buf_max) { 128562306a36Sopenharmony_ci pr_err("Invalid buffer min and/or max values\n"); 128662306a36Sopenharmony_ci return -EINVAL; 128762306a36Sopenharmony_ci } 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci if (kbuf->top_down) 129062306a36Sopenharmony_ci ret = locate_mem_hole_top_down_ppc64(kbuf, buf_min, buf_max, 129162306a36Sopenharmony_ci *emem); 129262306a36Sopenharmony_ci else 129362306a36Sopenharmony_ci ret = locate_mem_hole_bottom_up_ppc64(kbuf, buf_min, buf_max, 129462306a36Sopenharmony_ci *emem); 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci /* Add the buffer allocated to the exclude list for the next lookup */ 129762306a36Sopenharmony_ci if (!ret) { 129862306a36Sopenharmony_ci add_mem_range(emem, kbuf->mem, kbuf->memsz); 129962306a36Sopenharmony_ci sort_memory_ranges(*emem, true); 130062306a36Sopenharmony_ci } else { 130162306a36Sopenharmony_ci pr_err("Failed to locate memory buffer of size %lu\n", 130262306a36Sopenharmony_ci kbuf->memsz); 130362306a36Sopenharmony_ci } 130462306a36Sopenharmony_ci return ret; 130562306a36Sopenharmony_ci} 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci/** 130862306a36Sopenharmony_ci * arch_kexec_kernel_image_probe - Does additional handling needed to setup 130962306a36Sopenharmony_ci * kexec segments. 131062306a36Sopenharmony_ci * @image: kexec image being loaded. 131162306a36Sopenharmony_ci * @buf: Buffer pointing to elf data. 131262306a36Sopenharmony_ci * @buf_len: Length of the buffer. 131362306a36Sopenharmony_ci * 131462306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 131562306a36Sopenharmony_ci */ 131662306a36Sopenharmony_ciint arch_kexec_kernel_image_probe(struct kimage *image, void *buf, 131762306a36Sopenharmony_ci unsigned long buf_len) 131862306a36Sopenharmony_ci{ 131962306a36Sopenharmony_ci int ret; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci /* Get exclude memory ranges needed for setting up kexec segments */ 132262306a36Sopenharmony_ci ret = get_exclude_memory_ranges(&(image->arch.exclude_ranges)); 132362306a36Sopenharmony_ci if (ret) { 132462306a36Sopenharmony_ci pr_err("Failed to setup exclude memory ranges for buffer lookup\n"); 132562306a36Sopenharmony_ci return ret; 132662306a36Sopenharmony_ci } 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci return kexec_image_probe_default(image, buf, buf_len); 132962306a36Sopenharmony_ci} 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci/** 133262306a36Sopenharmony_ci * arch_kimage_file_post_load_cleanup - Frees up all the allocations done 133362306a36Sopenharmony_ci * while loading the image. 133462306a36Sopenharmony_ci * @image: kexec image being loaded. 133562306a36Sopenharmony_ci * 133662306a36Sopenharmony_ci * Returns 0 on success, negative errno on error. 133762306a36Sopenharmony_ci */ 133862306a36Sopenharmony_ciint arch_kimage_file_post_load_cleanup(struct kimage *image) 133962306a36Sopenharmony_ci{ 134062306a36Sopenharmony_ci kfree(image->arch.exclude_ranges); 134162306a36Sopenharmony_ci image->arch.exclude_ranges = NULL; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci vfree(image->arch.backup_buf); 134462306a36Sopenharmony_ci image->arch.backup_buf = NULL; 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci vfree(image->elf_headers); 134762306a36Sopenharmony_ci image->elf_headers = NULL; 134862306a36Sopenharmony_ci image->elf_headers_sz = 0; 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci kvfree(image->arch.fdt); 135162306a36Sopenharmony_ci image->arch.fdt = NULL; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci return kexec_image_post_load_cleanup_default(image); 135462306a36Sopenharmony_ci} 1355