1*eb39c880SMahesh Salgaonkar /* 2*eb39c880SMahesh Salgaonkar * Firmware Assisted dump: A robust mechanism to get reliable kernel crash 3*eb39c880SMahesh Salgaonkar * dump with assistance from firmware. This approach does not use kexec, 4*eb39c880SMahesh Salgaonkar * instead firmware assists in booting the kdump kernel while preserving 5*eb39c880SMahesh Salgaonkar * memory contents. The most of the code implementation has been adapted 6*eb39c880SMahesh Salgaonkar * from phyp assisted dump implementation written by Linas Vepstas and 7*eb39c880SMahesh Salgaonkar * Manish Ahuja 8*eb39c880SMahesh Salgaonkar * 9*eb39c880SMahesh Salgaonkar * This program is free software; you can redistribute it and/or modify 10*eb39c880SMahesh Salgaonkar * it under the terms of the GNU General Public License as published by 11*eb39c880SMahesh Salgaonkar * the Free Software Foundation; either version 2 of the License, or 12*eb39c880SMahesh Salgaonkar * (at your option) any later version. 13*eb39c880SMahesh Salgaonkar * 14*eb39c880SMahesh Salgaonkar * This program is distributed in the hope that it will be useful, 15*eb39c880SMahesh Salgaonkar * but WITHOUT ANY WARRANTY; without even the implied warranty of 16*eb39c880SMahesh Salgaonkar * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17*eb39c880SMahesh Salgaonkar * GNU General Public License for more details. 18*eb39c880SMahesh Salgaonkar * 19*eb39c880SMahesh Salgaonkar * You should have received a copy of the GNU General Public License 20*eb39c880SMahesh Salgaonkar * along with this program; if not, write to the Free Software 21*eb39c880SMahesh Salgaonkar * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 22*eb39c880SMahesh Salgaonkar * 23*eb39c880SMahesh Salgaonkar * Copyright 2011 IBM Corporation 24*eb39c880SMahesh Salgaonkar * Author: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com> 25*eb39c880SMahesh Salgaonkar */ 26*eb39c880SMahesh Salgaonkar 27*eb39c880SMahesh Salgaonkar #undef DEBUG 28*eb39c880SMahesh Salgaonkar #define pr_fmt(fmt) "fadump: " fmt 29*eb39c880SMahesh Salgaonkar 30*eb39c880SMahesh Salgaonkar #include <linux/string.h> 31*eb39c880SMahesh Salgaonkar #include <linux/memblock.h> 32*eb39c880SMahesh Salgaonkar 33*eb39c880SMahesh Salgaonkar #include <asm/page.h> 34*eb39c880SMahesh Salgaonkar #include <asm/prom.h> 35*eb39c880SMahesh Salgaonkar #include <asm/rtas.h> 36*eb39c880SMahesh Salgaonkar #include <asm/fadump.h> 37*eb39c880SMahesh Salgaonkar 38*eb39c880SMahesh Salgaonkar static struct fw_dump fw_dump; 39*eb39c880SMahesh Salgaonkar 40*eb39c880SMahesh Salgaonkar /* Scan the Firmware Assisted dump configuration details. */ 41*eb39c880SMahesh Salgaonkar int __init early_init_dt_scan_fw_dump(unsigned long node, 42*eb39c880SMahesh Salgaonkar const char *uname, int depth, void *data) 43*eb39c880SMahesh Salgaonkar { 44*eb39c880SMahesh Salgaonkar __be32 *sections; 45*eb39c880SMahesh Salgaonkar int i, num_sections; 46*eb39c880SMahesh Salgaonkar unsigned long size; 47*eb39c880SMahesh Salgaonkar const int *token; 48*eb39c880SMahesh Salgaonkar 49*eb39c880SMahesh Salgaonkar if (depth != 1 || strcmp(uname, "rtas") != 0) 50*eb39c880SMahesh Salgaonkar return 0; 51*eb39c880SMahesh Salgaonkar 52*eb39c880SMahesh Salgaonkar /* 53*eb39c880SMahesh Salgaonkar * Check if Firmware Assisted dump is supported. if yes, check 54*eb39c880SMahesh Salgaonkar * if dump has been initiated on last reboot. 55*eb39c880SMahesh Salgaonkar */ 56*eb39c880SMahesh Salgaonkar token = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump", NULL); 57*eb39c880SMahesh Salgaonkar if (!token) 58*eb39c880SMahesh Salgaonkar return 0; 59*eb39c880SMahesh Salgaonkar 60*eb39c880SMahesh Salgaonkar fw_dump.fadump_supported = 1; 61*eb39c880SMahesh Salgaonkar fw_dump.ibm_configure_kernel_dump = *token; 62*eb39c880SMahesh Salgaonkar 63*eb39c880SMahesh Salgaonkar /* 64*eb39c880SMahesh Salgaonkar * The 'ibm,kernel-dump' rtas node is present only if there is 65*eb39c880SMahesh Salgaonkar * dump data waiting for us. 66*eb39c880SMahesh Salgaonkar */ 67*eb39c880SMahesh Salgaonkar if (of_get_flat_dt_prop(node, "ibm,kernel-dump", NULL)) 68*eb39c880SMahesh Salgaonkar fw_dump.dump_active = 1; 69*eb39c880SMahesh Salgaonkar 70*eb39c880SMahesh Salgaonkar /* Get the sizes required to store dump data for the firmware provided 71*eb39c880SMahesh Salgaonkar * dump sections. 72*eb39c880SMahesh Salgaonkar * For each dump section type supported, a 32bit cell which defines 73*eb39c880SMahesh Salgaonkar * the ID of a supported section followed by two 32 bit cells which 74*eb39c880SMahesh Salgaonkar * gives teh size of the section in bytes. 75*eb39c880SMahesh Salgaonkar */ 76*eb39c880SMahesh Salgaonkar sections = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump-sizes", 77*eb39c880SMahesh Salgaonkar &size); 78*eb39c880SMahesh Salgaonkar 79*eb39c880SMahesh Salgaonkar if (!sections) 80*eb39c880SMahesh Salgaonkar return 0; 81*eb39c880SMahesh Salgaonkar 82*eb39c880SMahesh Salgaonkar num_sections = size / (3 * sizeof(u32)); 83*eb39c880SMahesh Salgaonkar 84*eb39c880SMahesh Salgaonkar for (i = 0; i < num_sections; i++, sections += 3) { 85*eb39c880SMahesh Salgaonkar u32 type = (u32)of_read_number(sections, 1); 86*eb39c880SMahesh Salgaonkar 87*eb39c880SMahesh Salgaonkar switch (type) { 88*eb39c880SMahesh Salgaonkar case FADUMP_CPU_STATE_DATA: 89*eb39c880SMahesh Salgaonkar fw_dump.cpu_state_data_size = 90*eb39c880SMahesh Salgaonkar of_read_ulong(§ions[1], 2); 91*eb39c880SMahesh Salgaonkar break; 92*eb39c880SMahesh Salgaonkar case FADUMP_HPTE_REGION: 93*eb39c880SMahesh Salgaonkar fw_dump.hpte_region_size = 94*eb39c880SMahesh Salgaonkar of_read_ulong(§ions[1], 2); 95*eb39c880SMahesh Salgaonkar break; 96*eb39c880SMahesh Salgaonkar } 97*eb39c880SMahesh Salgaonkar } 98*eb39c880SMahesh Salgaonkar return 1; 99*eb39c880SMahesh Salgaonkar } 100*eb39c880SMahesh Salgaonkar 101*eb39c880SMahesh Salgaonkar /** 102*eb39c880SMahesh Salgaonkar * fadump_calculate_reserve_size(): reserve variable boot area 5% of System RAM 103*eb39c880SMahesh Salgaonkar * 104*eb39c880SMahesh Salgaonkar * Function to find the largest memory size we need to reserve during early 105*eb39c880SMahesh Salgaonkar * boot process. This will be the size of the memory that is required for a 106*eb39c880SMahesh Salgaonkar * kernel to boot successfully. 107*eb39c880SMahesh Salgaonkar * 108*eb39c880SMahesh Salgaonkar * This function has been taken from phyp-assisted dump feature implementation. 109*eb39c880SMahesh Salgaonkar * 110*eb39c880SMahesh Salgaonkar * returns larger of 256MB or 5% rounded down to multiples of 256MB. 111*eb39c880SMahesh Salgaonkar * 112*eb39c880SMahesh Salgaonkar * TODO: Come up with better approach to find out more accurate memory size 113*eb39c880SMahesh Salgaonkar * that is required for a kernel to boot successfully. 114*eb39c880SMahesh Salgaonkar * 115*eb39c880SMahesh Salgaonkar */ 116*eb39c880SMahesh Salgaonkar static inline unsigned long fadump_calculate_reserve_size(void) 117*eb39c880SMahesh Salgaonkar { 118*eb39c880SMahesh Salgaonkar unsigned long size; 119*eb39c880SMahesh Salgaonkar 120*eb39c880SMahesh Salgaonkar /* 121*eb39c880SMahesh Salgaonkar * Check if the size is specified through fadump_reserve_mem= cmdline 122*eb39c880SMahesh Salgaonkar * option. If yes, then use that. 123*eb39c880SMahesh Salgaonkar */ 124*eb39c880SMahesh Salgaonkar if (fw_dump.reserve_bootvar) 125*eb39c880SMahesh Salgaonkar return fw_dump.reserve_bootvar; 126*eb39c880SMahesh Salgaonkar 127*eb39c880SMahesh Salgaonkar /* divide by 20 to get 5% of value */ 128*eb39c880SMahesh Salgaonkar size = memblock_end_of_DRAM() / 20; 129*eb39c880SMahesh Salgaonkar 130*eb39c880SMahesh Salgaonkar /* round it down in multiples of 256 */ 131*eb39c880SMahesh Salgaonkar size = size & ~0x0FFFFFFFUL; 132*eb39c880SMahesh Salgaonkar 133*eb39c880SMahesh Salgaonkar /* Truncate to memory_limit. We don't want to over reserve the memory.*/ 134*eb39c880SMahesh Salgaonkar if (memory_limit && size > memory_limit) 135*eb39c880SMahesh Salgaonkar size = memory_limit; 136*eb39c880SMahesh Salgaonkar 137*eb39c880SMahesh Salgaonkar return (size > MIN_BOOT_MEM ? size : MIN_BOOT_MEM); 138*eb39c880SMahesh Salgaonkar } 139*eb39c880SMahesh Salgaonkar 140*eb39c880SMahesh Salgaonkar /* 141*eb39c880SMahesh Salgaonkar * Calculate the total memory size required to be reserved for 142*eb39c880SMahesh Salgaonkar * firmware-assisted dump registration. 143*eb39c880SMahesh Salgaonkar */ 144*eb39c880SMahesh Salgaonkar static unsigned long get_fadump_area_size(void) 145*eb39c880SMahesh Salgaonkar { 146*eb39c880SMahesh Salgaonkar unsigned long size = 0; 147*eb39c880SMahesh Salgaonkar 148*eb39c880SMahesh Salgaonkar size += fw_dump.cpu_state_data_size; 149*eb39c880SMahesh Salgaonkar size += fw_dump.hpte_region_size; 150*eb39c880SMahesh Salgaonkar size += fw_dump.boot_memory_size; 151*eb39c880SMahesh Salgaonkar 152*eb39c880SMahesh Salgaonkar size = PAGE_ALIGN(size); 153*eb39c880SMahesh Salgaonkar return size; 154*eb39c880SMahesh Salgaonkar } 155*eb39c880SMahesh Salgaonkar 156*eb39c880SMahesh Salgaonkar int __init fadump_reserve_mem(void) 157*eb39c880SMahesh Salgaonkar { 158*eb39c880SMahesh Salgaonkar unsigned long base, size, memory_boundary; 159*eb39c880SMahesh Salgaonkar 160*eb39c880SMahesh Salgaonkar if (!fw_dump.fadump_enabled) 161*eb39c880SMahesh Salgaonkar return 0; 162*eb39c880SMahesh Salgaonkar 163*eb39c880SMahesh Salgaonkar if (!fw_dump.fadump_supported) { 164*eb39c880SMahesh Salgaonkar printk(KERN_INFO "Firmware-assisted dump is not supported on" 165*eb39c880SMahesh Salgaonkar " this hardware\n"); 166*eb39c880SMahesh Salgaonkar fw_dump.fadump_enabled = 0; 167*eb39c880SMahesh Salgaonkar return 0; 168*eb39c880SMahesh Salgaonkar } 169*eb39c880SMahesh Salgaonkar /* Initialize boot memory size */ 170*eb39c880SMahesh Salgaonkar fw_dump.boot_memory_size = fadump_calculate_reserve_size(); 171*eb39c880SMahesh Salgaonkar 172*eb39c880SMahesh Salgaonkar /* 173*eb39c880SMahesh Salgaonkar * Calculate the memory boundary. 174*eb39c880SMahesh Salgaonkar * If memory_limit is less than actual memory boundary then reserve 175*eb39c880SMahesh Salgaonkar * the memory for fadump beyond the memory_limit and adjust the 176*eb39c880SMahesh Salgaonkar * memory_limit accordingly, so that the running kernel can run with 177*eb39c880SMahesh Salgaonkar * specified memory_limit. 178*eb39c880SMahesh Salgaonkar */ 179*eb39c880SMahesh Salgaonkar if (memory_limit && memory_limit < memblock_end_of_DRAM()) { 180*eb39c880SMahesh Salgaonkar size = get_fadump_area_size(); 181*eb39c880SMahesh Salgaonkar if ((memory_limit + size) < memblock_end_of_DRAM()) 182*eb39c880SMahesh Salgaonkar memory_limit += size; 183*eb39c880SMahesh Salgaonkar else 184*eb39c880SMahesh Salgaonkar memory_limit = memblock_end_of_DRAM(); 185*eb39c880SMahesh Salgaonkar printk(KERN_INFO "Adjusted memory_limit for firmware-assisted" 186*eb39c880SMahesh Salgaonkar " dump, now %#016llx\n", 187*eb39c880SMahesh Salgaonkar (unsigned long long)memory_limit); 188*eb39c880SMahesh Salgaonkar } 189*eb39c880SMahesh Salgaonkar if (memory_limit) 190*eb39c880SMahesh Salgaonkar memory_boundary = memory_limit; 191*eb39c880SMahesh Salgaonkar else 192*eb39c880SMahesh Salgaonkar memory_boundary = memblock_end_of_DRAM(); 193*eb39c880SMahesh Salgaonkar 194*eb39c880SMahesh Salgaonkar if (fw_dump.dump_active) { 195*eb39c880SMahesh Salgaonkar printk(KERN_INFO "Firmware-assisted dump is active.\n"); 196*eb39c880SMahesh Salgaonkar /* 197*eb39c880SMahesh Salgaonkar * If last boot has crashed then reserve all the memory 198*eb39c880SMahesh Salgaonkar * above boot_memory_size so that we don't touch it until 199*eb39c880SMahesh Salgaonkar * dump is written to disk by userspace tool. This memory 200*eb39c880SMahesh Salgaonkar * will be released for general use once the dump is saved. 201*eb39c880SMahesh Salgaonkar */ 202*eb39c880SMahesh Salgaonkar base = fw_dump.boot_memory_size; 203*eb39c880SMahesh Salgaonkar size = memory_boundary - base; 204*eb39c880SMahesh Salgaonkar memblock_reserve(base, size); 205*eb39c880SMahesh Salgaonkar printk(KERN_INFO "Reserved %ldMB of memory at %ldMB " 206*eb39c880SMahesh Salgaonkar "for saving crash dump\n", 207*eb39c880SMahesh Salgaonkar (unsigned long)(size >> 20), 208*eb39c880SMahesh Salgaonkar (unsigned long)(base >> 20)); 209*eb39c880SMahesh Salgaonkar } else { 210*eb39c880SMahesh Salgaonkar /* Reserve the memory at the top of memory. */ 211*eb39c880SMahesh Salgaonkar size = get_fadump_area_size(); 212*eb39c880SMahesh Salgaonkar base = memory_boundary - size; 213*eb39c880SMahesh Salgaonkar memblock_reserve(base, size); 214*eb39c880SMahesh Salgaonkar printk(KERN_INFO "Reserved %ldMB of memory at %ldMB " 215*eb39c880SMahesh Salgaonkar "for firmware-assisted dump\n", 216*eb39c880SMahesh Salgaonkar (unsigned long)(size >> 20), 217*eb39c880SMahesh Salgaonkar (unsigned long)(base >> 20)); 218*eb39c880SMahesh Salgaonkar } 219*eb39c880SMahesh Salgaonkar fw_dump.reserve_dump_area_start = base; 220*eb39c880SMahesh Salgaonkar fw_dump.reserve_dump_area_size = size; 221*eb39c880SMahesh Salgaonkar return 1; 222*eb39c880SMahesh Salgaonkar } 223*eb39c880SMahesh Salgaonkar 224*eb39c880SMahesh Salgaonkar /* Look for fadump= cmdline option. */ 225*eb39c880SMahesh Salgaonkar static int __init early_fadump_param(char *p) 226*eb39c880SMahesh Salgaonkar { 227*eb39c880SMahesh Salgaonkar if (!p) 228*eb39c880SMahesh Salgaonkar return 1; 229*eb39c880SMahesh Salgaonkar 230*eb39c880SMahesh Salgaonkar if (strncmp(p, "on", 2) == 0) 231*eb39c880SMahesh Salgaonkar fw_dump.fadump_enabled = 1; 232*eb39c880SMahesh Salgaonkar else if (strncmp(p, "off", 3) == 0) 233*eb39c880SMahesh Salgaonkar fw_dump.fadump_enabled = 0; 234*eb39c880SMahesh Salgaonkar 235*eb39c880SMahesh Salgaonkar return 0; 236*eb39c880SMahesh Salgaonkar } 237*eb39c880SMahesh Salgaonkar early_param("fadump", early_fadump_param); 238*eb39c880SMahesh Salgaonkar 239*eb39c880SMahesh Salgaonkar /* Look for fadump_reserve_mem= cmdline option */ 240*eb39c880SMahesh Salgaonkar static int __init early_fadump_reserve_mem(char *p) 241*eb39c880SMahesh Salgaonkar { 242*eb39c880SMahesh Salgaonkar if (p) 243*eb39c880SMahesh Salgaonkar fw_dump.reserve_bootvar = memparse(p, &p); 244*eb39c880SMahesh Salgaonkar return 0; 245*eb39c880SMahesh Salgaonkar } 246*eb39c880SMahesh Salgaonkar early_param("fadump_reserve_mem", early_fadump_reserve_mem); 247