]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - arch/powerpc/platforms/pseries/phyp_dump.c
[POWERPC] pseries: phyp dump: Register dump area
[linux-2.6-omap-h63xx.git] / arch / powerpc / platforms / pseries / phyp_dump.c
1 /*
2  * Hypervisor-assisted dump
3  *
4  * Linas Vepstas, Manish Ahuja 2008
5  * Copyright 2008 IBM Corp.
6  *
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.
11  *
12  */
13
14 #include <linux/init.h>
15 #include <linux/kobject.h>
16 #include <linux/mm.h>
17 #include <linux/of.h>
18 #include <linux/pfn.h>
19 #include <linux/swap.h>
20 #include <linux/sysfs.h>
21
22 #include <asm/page.h>
23 #include <asm/phyp_dump.h>
24 #include <asm/machdep.h>
25 #include <asm/prom.h>
26 #include <asm/rtas.h>
27
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;
31
32 static int ibm_configure_kernel_dump;
33 /* ------------------------------------------------- */
34 /* RTAS interfaces to declare the dump regions */
35
36 struct dump_section {
37         u32 dump_flags;
38         u16 source_type;
39         u16 error_flags;
40         u64 source_address;
41         u64 source_length;
42         u64 length_copied;
43         u64 destination_address;
44 };
45
46 struct phyp_dump_header {
47         u32 version;
48         u16 num_of_sections;
49         u16 status;
50
51         u32 first_offset_section;
52         u32 dump_disk_section;
53         u64 block_num_dd;
54         u64 num_of_blocks_dd;
55         u32 offset_dd;
56         u32 maxtime_to_auto;
57         /* No dump disk path string used */
58
59         struct dump_section cpu_data;
60         struct dump_section hpte_data;
61         struct dump_section kernel_data;
62 };
63
64 /* The dump header *must be* in low memory, so .bss it */
65 static struct phyp_dump_header phdr;
66
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
73
74 /**
75  * init_dump_header() - initialize the header declaring a dump
76  * Returns: length of dump save area.
77  *
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.
81  */
82 static unsigned long init_dump_header(struct phyp_dump_header *ph)
83 {
84         unsigned long addr_offset = 0;
85
86         /* Set up the dump header */
87         ph->version = DUMP_HEADER_VERSION;
88         ph->num_of_sections = NUM_DUMP_SECTIONS;
89         ph->status = 0;
90
91         ph->first_offset_section =
92                 (u32)offsetof(struct phyp_dump_header, cpu_data);
93         ph->dump_disk_section = 0;
94         ph->block_num_dd = 0;
95         ph->num_of_blocks_dd = 0;
96         ph->offset_dd = 0;
97
98         ph->maxtime_to_auto = 0; /* disabled */
99
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;
107
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;
114
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;
122
123         return addr_offset;
124 }
125
126 static void register_dump_area(struct phyp_dump_header *ph, unsigned long addr)
127 {
128         int rc;
129         ph->cpu_data.destination_address += addr;
130         ph->hpte_data.destination_address += addr;
131         ph->kernel_data.destination_address += addr;
132
133         do {
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));
137
138         if (rc)
139                 printk(KERN_ERR "phyp-dump: unexpected error (%d) on "
140                                                 "register\n", rc);
141 }
142
143 /* ------------------------------------------------- */
144 /**
145  * release_memory_range -- release memory previously lmb_reserved
146  * @start_pfn: starting physical frame number
147  * @nr_pages: number of pages to free.
148  *
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.
152  */
153 static void
154 release_memory_range(unsigned long start_pfn, unsigned long nr_pages)
155 {
156         struct page *rpage;
157         unsigned long end_pfn;
158         long i;
159
160         end_pfn = start_pfn + nr_pages;
161
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);
167                         __free_page(rpage);
168                         totalram_pages++;
169                 }
170         }
171 }
172
173 /* ------------------------------------------------- */
174 /**
175  * sysfs_release_region -- sysfs interface to release memory range.
176  *
177  * Usage:
178  *   "echo <start addr> <length> > /sys/kernel/release_region"
179  *
180  * Example:
181  *   "echo 0x40000000 0x10000000 > /sys/kernel/release_region"
182  *
183  * will release 256MB starting at 1GB.
184  */
185 static ssize_t store_release_region(struct kobject *kobj,
186                                 struct kobj_attribute *attr,
187                                 const char *buf, size_t count)
188 {
189         unsigned long start_addr, length, end_addr;
190         unsigned long start_pfn, nr_pages;
191         ssize_t ret;
192
193         ret = sscanf(buf, "%lx %lx", &start_addr, &length);
194         if (ret != 2)
195                 return -EINVAL;
196
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;
201
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;
206
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);
211
212         return count;
213 }
214
215 static struct kobj_attribute rr = __ATTR(release_region, 0600,
216                                          NULL, store_release_region);
217
218 static int __init phyp_dump_setup(void)
219 {
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;
224         int header_len = 0;
225         int rc;
226
227         /* If no memory was reserved in early boot, there is nothing to do */
228         if (phyp_dump_info->init_reserve_size == 0)
229                 return 0;
230
231         /* Return if phyp dump not supported */
232         if (!phyp_dump_info->phyp_dump_configured)
233                 return -ENOSYS;
234
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.
238          *
239          * The /rtas/ibm,kernel-dump rtas node is present only
240          * if there is dump data waiting for us.
241          */
242         rtas = of_find_node_by_path("/rtas");
243         if (rtas) {
244                 dump_header = of_get_property(rtas, "ibm,kernel-dump",
245                                                 &header_len);
246                 of_node_put(rtas);
247         }
248
249         dump_area_length = init_dump_header(&phdr);
250
251         /* align down */
252         dump_area_start = phyp_dump_info->init_reserve_start & PAGE_MASK;
253
254         if (dump_header == NULL) {
255                 register_dump_area(&phdr, dump_area_start);
256                 return 0;
257         }
258
259         /* Should we create a dump_subsys, analogous to s390/ipl.c ? */
260         rc = sysfs_create_file(kernel_kobj, &rr.attr);
261         if (rc)
262                 printk(KERN_ERR "phyp-dump: unable to create sysfs file (%d)\n",
263                                                                         rc);
264
265         /* ToDo: re-register the dump area, for next time. */
266         return 0;
267 }
268 machine_subsys_initcall(pseries, phyp_dump_setup);
269
270 int __init early_init_dt_scan_phyp_dump(unsigned long node,
271                 const char *uname, int depth, void *data)
272 {
273         const unsigned int *sizes;
274
275         phyp_dump_info->phyp_dump_configured = 0;
276         phyp_dump_info->phyp_dump_is_active = 0;
277
278         if (depth != 1 || strcmp(uname, "rtas") != 0)
279                 return 0;
280
281         if (of_get_flat_dt_prop(node, "ibm,configure-kernel-dump", NULL))
282                 phyp_dump_info->phyp_dump_configured++;
283
284         if (of_get_flat_dt_prop(node, "ibm,dump-kernel", NULL))
285                 phyp_dump_info->phyp_dump_is_active++;
286
287         sizes = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump-sizes",
288                                     NULL);
289         if (!sizes)
290                 return 0;
291
292         if (sizes[0] == 1)
293                 phyp_dump_info->cpu_state_size = *((unsigned long *)&sizes[1]);
294
295         if (sizes[3] == 2)
296                 phyp_dump_info->hpte_region_size =
297                                                 *((unsigned long *)&sizes[4]);
298         return 1;
299 }