xref: /openbmc/linux/arch/powerpc/kernel/fadump.c (revision eb39c8803d0e3d98fe74825f99287f63d55e6460)
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(&sections[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(&sections[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