2 * Hypervisor-assisted dump
4 * Linas Vepstas, Manish Ahuja 2008
5 * Copyright 2008 IBM Corp.
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
14 #include <linux/init.h>
15 #include <linux/kobject.h>
18 #include <linux/pfn.h>
19 #include <linux/swap.h>
20 #include <linux/sysfs.h>
23 #include <asm/phyp_dump.h>
24 #include <asm/machdep.h>
28 /* Variables, used to communicate data between early boot and late boot */
29 static struct phyp_dump phyp_dump_vars;
30 struct phyp_dump *phyp_dump_info = &phyp_dump_vars;
32 static int ibm_configure_kernel_dump;
33 /* ------------------------------------------------- */
34 /* RTAS interfaces to declare the dump regions */
43 u64 destination_address;
46 struct phyp_dump_header {
51 u32 first_offset_section;
52 u32 dump_disk_section;
57 /* No dump disk path string used */
59 struct dump_section cpu_data;
60 struct dump_section hpte_data;
61 struct dump_section kernel_data;
64 /* The dump header *must be* in low memory, so .bss it */
65 static struct phyp_dump_header phdr;
67 #define NUM_DUMP_SECTIONS 3
68 #define DUMP_HEADER_VERSION 0x1
69 #define DUMP_REQUEST_FLAG 0x1
70 #define DUMP_SOURCE_CPU 0x0001
71 #define DUMP_SOURCE_HPTE 0x0002
72 #define DUMP_SOURCE_RMO 0x0011
75 * init_dump_header() - initialize the header declaring a dump
76 * Returns: length of dump save area.
78 * When the hypervisor saves crashed state, it needs to put
79 * it somewhere. The dump header tells the hypervisor where
80 * the data can be saved.
82 static unsigned long init_dump_header(struct phyp_dump_header *ph)
84 unsigned long addr_offset = 0;
86 /* Set up the dump header */
87 ph->version = DUMP_HEADER_VERSION;
88 ph->num_of_sections = NUM_DUMP_SECTIONS;
91 ph->first_offset_section =
92 (u32)offsetof(struct phyp_dump_header, cpu_data);
93 ph->dump_disk_section = 0;
95 ph->num_of_blocks_dd = 0;
98 ph->maxtime_to_auto = 0; /* disabled */
100 /* The first two sections are mandatory */
101 ph->cpu_data.dump_flags = DUMP_REQUEST_FLAG;
102 ph->cpu_data.source_type = DUMP_SOURCE_CPU;
103 ph->cpu_data.source_address = 0;
104 ph->cpu_data.source_length = phyp_dump_info->cpu_state_size;
105 ph->cpu_data.destination_address = addr_offset;
106 addr_offset += phyp_dump_info->cpu_state_size;
108 ph->hpte_data.dump_flags = DUMP_REQUEST_FLAG;
109 ph->hpte_data.source_type = DUMP_SOURCE_HPTE;
110 ph->hpte_data.source_address = 0;
111 ph->hpte_data.source_length = phyp_dump_info->hpte_region_size;
112 ph->hpte_data.destination_address = addr_offset;
113 addr_offset += phyp_dump_info->hpte_region_size;
115 /* This section describes the low kernel region */
116 ph->kernel_data.dump_flags = DUMP_REQUEST_FLAG;
117 ph->kernel_data.source_type = DUMP_SOURCE_RMO;
118 ph->kernel_data.source_address = PHYP_DUMP_RMR_START;
119 ph->kernel_data.source_length = PHYP_DUMP_RMR_END;
120 ph->kernel_data.destination_address = addr_offset;
121 addr_offset += ph->kernel_data.source_length;
126 static void register_dump_area(struct phyp_dump_header *ph, unsigned long addr)
129 ph->cpu_data.destination_address += addr;
130 ph->hpte_data.destination_address += addr;
131 ph->kernel_data.destination_address += addr;
134 rc = rtas_call(ibm_configure_kernel_dump, 3, 1, NULL,
135 1, ph, sizeof(struct phyp_dump_header));
136 } while (rtas_busy_delay(rc));
139 printk(KERN_ERR "phyp-dump: unexpected error (%d) on "
143 /* ------------------------------------------------- */
145 * release_memory_range -- release memory previously lmb_reserved
146 * @start_pfn: starting physical frame number
147 * @nr_pages: number of pages to free.
149 * This routine will release memory that had been previously
150 * lmb_reserved in early boot. The released memory becomes
151 * available for genreal use.
154 release_memory_range(unsigned long start_pfn, unsigned long nr_pages)
157 unsigned long end_pfn;
160 end_pfn = start_pfn + nr_pages;
162 for (i = start_pfn; i <= end_pfn; i++) {
163 rpage = pfn_to_page(i);
164 if (PageReserved(rpage)) {
165 ClearPageReserved(rpage);
166 init_page_count(rpage);
173 /* ------------------------------------------------- */
175 * sysfs_release_region -- sysfs interface to release memory range.
178 * "echo <start addr> <length> > /sys/kernel/release_region"
181 * "echo 0x40000000 0x10000000 > /sys/kernel/release_region"
183 * will release 256MB starting at 1GB.
185 static ssize_t store_release_region(struct kobject *kobj,
186 struct kobj_attribute *attr,
187 const char *buf, size_t count)
189 unsigned long start_addr, length, end_addr;
190 unsigned long start_pfn, nr_pages;
193 ret = sscanf(buf, "%lx %lx", &start_addr, &length);
197 /* Range-check - don't free any reserved memory that
198 * wasn't reserved for phyp-dump */
199 if (start_addr < phyp_dump_info->init_reserve_start)
200 start_addr = phyp_dump_info->init_reserve_start;
202 end_addr = phyp_dump_info->init_reserve_start +
203 phyp_dump_info->init_reserve_size;
204 if (start_addr+length > end_addr)
205 length = end_addr - start_addr;
207 /* Release the region of memory assed in by user */
208 start_pfn = PFN_DOWN(start_addr);
209 nr_pages = PFN_DOWN(length);
210 release_memory_range(start_pfn, nr_pages);
215 static struct kobj_attribute rr = __ATTR(release_region, 0600,
216 NULL, store_release_region);
218 static int __init phyp_dump_setup(void)
220 struct device_node *rtas;
221 const struct phyp_dump_header *dump_header = NULL;
222 unsigned long dump_area_start;
223 unsigned long dump_area_length;
227 /* If no memory was reserved in early boot, there is nothing to do */
228 if (phyp_dump_info->init_reserve_size == 0)
231 /* Return if phyp dump not supported */
232 if (!phyp_dump_info->phyp_dump_configured)
235 /* Is there dump data waiting for us? If there isn't,
236 * then register a new dump area, and release all of
237 * the rest of the reserved ram.
239 * The /rtas/ibm,kernel-dump rtas node is present only
240 * if there is dump data waiting for us.
242 rtas = of_find_node_by_path("/rtas");
244 dump_header = of_get_property(rtas, "ibm,kernel-dump",
249 dump_area_length = init_dump_header(&phdr);
252 dump_area_start = phyp_dump_info->init_reserve_start & PAGE_MASK;
254 if (dump_header == NULL) {
255 register_dump_area(&phdr, dump_area_start);
259 /* Should we create a dump_subsys, analogous to s390/ipl.c ? */
260 rc = sysfs_create_file(kernel_kobj, &rr.attr);
262 printk(KERN_ERR "phyp-dump: unable to create sysfs file (%d)\n",
265 /* ToDo: re-register the dump area, for next time. */
268 machine_subsys_initcall(pseries, phyp_dump_setup);
270 int __init early_init_dt_scan_phyp_dump(unsigned long node,
271 const char *uname, int depth, void *data)
273 const unsigned int *sizes;
275 phyp_dump_info->phyp_dump_configured = 0;
276 phyp_dump_info->phyp_dump_is_active = 0;
278 if (depth != 1 || strcmp(uname, "rtas") != 0)
281 if (of_get_flat_dt_prop(node, "ibm,configure-kernel-dump", NULL))
282 phyp_dump_info->phyp_dump_configured++;
284 if (of_get_flat_dt_prop(node, "ibm,dump-kernel", NULL))
285 phyp_dump_info->phyp_dump_is_active++;
287 sizes = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump-sizes",
293 phyp_dump_info->cpu_state_size = *((unsigned long *)&sizes[1]);
296 phyp_dump_info->hpte_region_size =
297 *((unsigned long *)&sizes[4]);