- more 2.6.17 port work (still does not build)
[linux-flexiantxendom0-3.2.10.git] / drivers / dump / dump_fmt.c
1 /*
2  * Implements the routines which handle the format specific
3  * aspects of dump for the default dump format.
4  *
5  * Used in single stage dumping and stage 1 of soft-boot based dumping
6  * Saves data in LKCD (lcrash) format
7  *
8  * Previously a part of dump_base.c
9  *
10  * Started: Oct 2002 -  Suparna Bhattacharya <suparna@in.ibm.com>
11  *      Split off and reshuffled LKCD dump format code around generic
12  *      dump method interfaces.
13  *
14  * Derived from original code created by
15  *      Matt Robinson <yakker@sourceforge.net>)
16  *
17  * Contributions from SGI, IBM, HP, MCL, and others.
18  *
19  * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved.
20  * Copyright (C) 2000 - 2002 TurboLinux, Inc.  All rights reserved.
21  * Copyright (C) 2001 - 2002 Matt D. Robinson.  All rights reserved.
22  * Copyright (C) 2002 International Business Machines Corp.
23  *
24  * This code is released under version 2 of the GNU GPL.
25  */
26
27 #include <linux/types.h>
28 #include <linux/kernel.h>
29 #include <linux/time.h>
30 #include <linux/sched.h>
31 #include <linux/ptrace.h>
32 #include <linux/utsname.h>
33 #include <linux/dump.h>
34 #include <asm/dump.h>
35 #include "dump_methods.h"
36
37 #define DUMP_DEBUG 0
38 /*
39  * SYSTEM DUMP LAYOUT
40  *
41  * System dumps are currently the combination of a dump header and a set
42  * of data pages which contain the system memory.  The layout of the dump
43  * (for full dumps) is as follows:
44  *
45  *             +-----------------------------+
46  *             |     generic dump header     |
47  *             +-----------------------------+
48  *             |   architecture dump header  |
49  *             +-----------------------------+
50  *             |         page header         |
51  *             +-----------------------------+
52  *             |          page data          |
53  *             +-----------------------------+
54  *             |         page header         |
55  *             +-----------------------------+
56  *             |          page data          |
57  *             +-----------------------------+
58  *             |              |              |
59  *             |              |              |
60  *             |              |              |
61  *             |              |              |
62  *             |              V              |
63  *             +-----------------------------+
64  *             |        PAGE_END header      |
65  *             +-----------------------------+
66  *
67  * There are two dump headers, the first which is architecture
68  * independent, and the other which is architecture dependent.  This
69  * allows different architectures to dump different data structures
70  * which are specific to their chipset, CPU, etc.
71  *
72  * After the dump headers come a succession of dump page headers along
73  * with dump pages.  The page header contains information about the page
74  * size, any flags associated with the page (whether it's compressed or
75  * not), and the address of the page.  After the page header is the page
76  * data, which is either compressed (or not).  Each page of data is
77  * dumped in succession, until the final dump header (PAGE_END) is
78  * placed at the end of the dump, assuming the dump device isn't out
79  * of space.
80  *
81  * This mechanism allows for multiple compression types, different
82  * types of data structures, different page ordering, etc., etc., etc.
83  * It's a very straightforward mechanism for dumping system memory.
84  */
85
86 struct __dump_header dump_header;  /* the primary dump header              */
87 struct __dump_header_asm dump_header_asm; /* the arch-specific dump header */
88
89 /*
90  *  Set up common header fields (mainly the arch indep section)
91  *  Per-cpu state is handled by lcrash_save_context
92  *  Returns the size of the header in bytes.
93  */
94 static int lcrash_init_dump_header(const char *panic_str)
95 {
96         struct timeval dh_time;
97         u64 temp_memsz = dump_header.dh_memory_size;
98
99         /* make sure the dump header isn't TOO big */
100         if ((sizeof(struct __dump_header) +
101                 sizeof(struct __dump_header_asm)) > DUMP_BUFFER_SIZE) {
102                         printk("LKCD: combined "
103                                 "headers larger than DUMP_BUFFER_SIZE!\n");
104                         return -E2BIG;
105         }
106
107         /* initialize the dump headers to zero */
108         /* save dha_stack pointer because it may contains pointer for stack! */
109         memset(&dump_header, 0, sizeof(dump_header));
110         memset(&dump_header_asm, 0,
111                 offsetof(struct __dump_header_asm, dha_stack));
112         memset(&dump_header_asm.dha_stack+1, 0,
113                 sizeof(dump_header_asm) -
114                 offsetof(struct __dump_header_asm, dha_stack) -
115                 sizeof(dump_header_asm.dha_stack));
116         dump_header.dh_memory_size = temp_memsz;
117
118         /* configure dump header values */
119         dump_header.dh_magic_number = DUMP_MAGIC_NUMBER;
120         dump_header.dh_version = DUMP_VERSION_NUMBER;
121         dump_header.dh_memory_start = PAGE_OFFSET;
122         dump_header.dh_memory_end = DUMP_MAGIC_NUMBER;
123         dump_header.dh_header_size = sizeof(struct __dump_header);
124         dump_header.dh_page_size = PAGE_SIZE;
125         dump_header.dh_dump_level = dump_config.level;
126         dump_header.dh_current_task = (unsigned long) current;
127         dump_header.dh_dump_compress = dump_config.dumper->compress->
128                 compress_type;
129         dump_header.dh_dump_polling = dump_config.polling;
130         dump_header.dh_dump_device = dump_config.dumper->dev->device_id;
131         dump_header.dh_dump_buffer_size = DUMP_BUFFER_SIZE;
132
133 #if DUMP_DEBUG >= 6
134         dump_header.dh_num_bytes = 0;
135 #endif
136         dump_header.dh_num_dump_pages = 0;
137         do_gettimeofday(&dh_time);
138         dump_header.dh_time.tv_sec = dh_time.tv_sec;
139         dump_header.dh_time.tv_usec = dh_time.tv_usec;
140
141         memcpy((void *)&(dump_header.dh_utsname_sysname),
142                 (const void *)&(system_utsname.sysname), __NEW_UTS_LEN + 1);
143         memcpy((void *)&(dump_header.dh_utsname_nodename),
144                 (const void *)&(system_utsname.nodename), __NEW_UTS_LEN + 1);
145         memcpy((void *)&(dump_header.dh_utsname_release),
146                 (const void *)&(system_utsname.release), __NEW_UTS_LEN + 1);
147         memcpy((void *)&(dump_header.dh_utsname_version),
148                 (const void *)&(system_utsname.version), __NEW_UTS_LEN + 1);
149         memcpy((void *)&(dump_header.dh_utsname_machine),
150                 (const void *)&(system_utsname.machine), __NEW_UTS_LEN + 1);
151         memcpy((void *)&(dump_header.dh_utsname_domainname),
152                 (const void *)&(system_utsname.domainname), __NEW_UTS_LEN + 1);
153
154         if (panic_str) {
155                 memcpy((void *)&(dump_header.dh_panic_string),
156                         (const void *)panic_str, DUMP_PANIC_LEN);
157         }
158
159         dump_header_asm.dha_magic_number = DUMP_ASM_MAGIC_NUMBER;
160         dump_header_asm.dha_version = DUMP_ASM_VERSION_NUMBER;
161         dump_header_asm.dha_header_size = sizeof(dump_header_asm);
162
163         dump_header_asm.dha_smp_num_cpus = num_online_cpus();
164         printk("LKCD: smp_num_cpus in header %d\n",
165                 dump_header_asm.dha_smp_num_cpus);
166
167         dump_header_asm.dha_dumping_cpu = smp_processor_id();
168
169         return sizeof(dump_header) + sizeof(dump_header_asm);
170 }
171
172
173 int dump_lcrash_configure_header(const char *panic_str,
174         const struct pt_regs *regs)
175 {
176         int retval = 0;
177
178         dump_config.dumper->header_len = lcrash_init_dump_header(panic_str);
179
180         /* capture register states for all processors */
181         dump_save_this_cpu(regs);
182         __dump_save_other_cpus(); /* side effect:silence cpus */
183
184         /* configure architecture-specific dump header values */
185         if ((retval = __dump_configure_header(regs)))
186                 return retval;
187
188         dump_config.dumper->header_dirty++;
189         return 0;
190 }
191 /* save register and task context */
192 void dump_lcrash_save_context(int cpu, const struct pt_regs *regs,
193         struct task_struct *tsk)
194 {
195         /* This level of abstraction might be redundantly redundant */
196         __dump_save_context(cpu, regs, tsk);
197 }
198
199 /* write out the header */
200 int dump_write_header(void)
201 {
202         int retval = 0, size;
203         void *buf = dump_config.dumper->dump_buf;
204
205         /* accounts for DUMP_HEADER_OFFSET if applicable */
206         if ((retval = dump_dev_seek(0))) {
207                 printk("LKCD: Unable to seek to dump header offset: %d\n",
208                         retval);
209                 return retval;
210         }
211
212         memcpy(buf, (void *)&dump_header, sizeof(dump_header));
213         size = sizeof(dump_header);
214         memcpy(buf + size, (void *)&dump_header_asm, sizeof(dump_header_asm));
215         size += sizeof(dump_header_asm);
216         size = PAGE_ALIGN(size);
217         retval = dump_ll_write(buf , size);
218
219         if (retval < size)
220                 return (retval >= 0) ? ENOSPC : retval;
221         return 0;
222 }
223
224 int dump_generic_update_header(void)
225 {
226         int err = 0;
227
228         if (dump_config.dumper->header_dirty) {
229                 if ((err = dump_write_header())) {
230                         printk("LKCD: dump write header failed !err %d\n", err);
231                 } else {
232                         dump_config.dumper->header_dirty = 0;
233                 }
234         }
235
236         return err;
237 }
238
239 static inline int is_curr_stack_page(struct page *page, unsigned long size)
240 {
241         unsigned long thread_addr = (unsigned long)current_thread_info();
242         unsigned long addr = (unsigned long)page_address(page);
243
244         return !PageHighMem(page) && (addr < thread_addr + THREAD_SIZE)
245                 && (addr + size > thread_addr);
246 }
247
248 static inline int is_dump_page(struct page *page, unsigned long size)
249 {
250         unsigned long addr = (unsigned long)page_address(page);
251         unsigned long dump_buf = (unsigned long)dump_config.dumper->dump_buf;
252
253         return !PageHighMem(page) && (addr < dump_buf + DUMP_BUFFER_SIZE)
254                 && (addr + size > dump_buf);
255 }
256
257 int dump_allow_compress(struct page *page, unsigned long size)
258 {
259         /*
260          * Don't compress the page if any part of it overlaps
261          * with the current stack or dump buffer (since the contents
262          * in these could be changing while compression is going on)
263          */
264         return !is_curr_stack_page(page, size) && !is_dump_page(page, size);
265 }
266
267 void lcrash_init_pageheader(struct __dump_page *dp, struct page *page,
268         unsigned long sz)
269 {
270         memset(dp, sizeof(struct __dump_page), 0);
271         dp->dp_flags = 0;
272         dp->dp_size = 0;
273         if (sz > 0)
274                 dp->dp_address = (loff_t)page_to_pfn(page) << PAGE_SHIFT;
275
276 #if DUMP_DEBUG > 6
277         dp->dp_page_index = dump_header.dh_num_dump_pages;
278         dp->dp_byte_offset = dump_header.dh_num_bytes + DUMP_BUFFER_SIZE
279                 + DUMP_HEADER_OFFSET; /* ?? */
280 #endif /* DUMP_DEBUG */
281 }
282
283 int dump_lcrash_add_data(unsigned long loc, unsigned long len)
284 {
285         struct page *page = (struct page *)loc;
286         void *addr, *buf = dump_config.dumper->curr_buf;
287         struct __dump_page *dp = (struct __dump_page *)buf;
288         int bytes, size;
289
290         if (buf > dump_config.dumper->dump_buf + DUMP_BUFFER_SIZE)
291                 return -ENOMEM;
292
293         lcrash_init_pageheader(dp, page, len);
294         buf += sizeof(struct __dump_page);
295
296         while (len) {
297                 addr = kmap_atomic(page, KM_DUMP);
298                 size = bytes = (len > PAGE_SIZE) ? PAGE_SIZE : len;
299                 /* check for compression */
300                 if (dump_allow_compress(page, bytes)) {
301                         size = dump_compress_data((char *)addr, bytes,
302                                 (char *)buf, loc);
303                 }
304                 /* set the compressed flag if the page did compress */
305                 if (size && (size < bytes)) {
306                         dp->dp_flags |= DUMP_DH_COMPRESSED;
307                 } else {
308                         /* compression failed -- default to raw mode */
309                         dp->dp_flags |= DUMP_DH_RAW;
310                         memcpy(buf, addr, bytes);
311                         size = bytes;
312                 }
313                 /* memset(buf, 'A', size); temporary: testing only !! */
314                 kunmap_atomic(addr, KM_DUMP);
315                 dp->dp_size += size;
316                 buf += size;
317                 len -= bytes;
318                 page++;
319         }
320
321         /* now update the header */
322 #if DUMP_DEBUG > 6
323         dump_header.dh_num_bytes += dp->dp_size + sizeof(*dp);
324 #endif
325         dump_header.dh_num_dump_pages++;
326         dump_config.dumper->header_dirty++;
327
328         dump_config.dumper->curr_buf = buf;
329
330         return len;
331 }
332
333 int dump_lcrash_update_end_marker(void)
334 {
335         struct __dump_page *dp =
336                 (struct __dump_page *)dump_config.dumper->curr_buf;
337         unsigned long left;
338         int ret = 0;
339
340         lcrash_init_pageheader(dp, NULL, 0);
341         dp->dp_flags |= DUMP_DH_END; /* tbd: truncation test ? */
342
343         /* now update the header */
344 #if DUMP_DEBUG > 6
345         dump_header.dh_num_bytes += sizeof(*dp);
346 #endif
347         dump_config.dumper->curr_buf += sizeof(*dp);
348         left = dump_config.dumper->curr_buf - dump_config.dumper->dump_buf;
349
350         printk("\n");
351
352         while (left) {
353                 if ((ret = dump_dev_seek(dump_config.dumper->curr_offset))) {
354                         printk("LKCD: Seek failed at offset 0x%llx\n",
355                         dump_config.dumper->curr_offset);
356                         return ret;
357                 }
358
359                 if (DUMP_BUFFER_SIZE > left)
360                         memset(dump_config.dumper->curr_buf, 'm',
361                                 DUMP_BUFFER_SIZE - left);
362
363                 if ((ret = dump_ll_write(dump_config.dumper->dump_buf,
364                         DUMP_BUFFER_SIZE)) < DUMP_BUFFER_SIZE) {
365                         return (ret < 0) ? ret : -ENOSPC;
366                 }
367
368                 dump_config.dumper->curr_offset += DUMP_BUFFER_SIZE;
369
370                 if (left > DUMP_BUFFER_SIZE) {
371                         left -= DUMP_BUFFER_SIZE;
372                         memcpy(dump_config.dumper->dump_buf,
373                         dump_config.dumper->dump_buf + DUMP_BUFFER_SIZE, left);
374                         dump_config.dumper->curr_buf -= DUMP_BUFFER_SIZE;
375                 } else {
376                         left = 0;
377                 }
378         }
379         return 0;
380 }
381
382
383 /* Default Formatter (lcrash) */
384 struct dump_fmt_ops dump_fmt_lcrash_ops = {
385         .configure_header       = dump_lcrash_configure_header,
386         .update_header          = dump_generic_update_header,
387         .save_context           = dump_lcrash_save_context,
388         .add_data               = dump_lcrash_add_data,
389         .update_end_marker      = dump_lcrash_update_end_marker
390 };
391
392 struct dump_fmt dump_fmt_lcrash = {
393         .name   = "lcrash",
394         .ops    = &dump_fmt_lcrash_ops
395 };
396