enable floppy module generation for boot cd
[linux-2.6.git] / drivers / dump / dump_overlay.c
1 /*
2  * Two-stage soft-boot based dump scheme methods (memory overlay
3  * with post soft-boot writeout)
4  *
5  * Started: Oct 2002 -  Suparna Bhattacharya <suparna@in.ibm.com>
6  *
7  * This approach of saving the dump in memory and writing it 
8  * out after a softboot without clearing memory is derived from the 
9  * Mission Critical Linux dump implementation. Credits and a big
10  * thanks for letting the lkcd project make use of the excellent 
11  * piece of work and also for helping with clarifications and 
12  * tips along the way are due to:
13  *      Dave Winchell <winchell@mclx.com> (primary author of mcore)
14  *      and also to
15  *      Jeff Moyer <moyer@mclx.com>
16  *      Josh Huber <huber@mclx.com>
17  * 
18  * For those familiar with the mcore implementation, the key 
19  * differences/extensions here are in allowing entire memory to be 
20  * saved (in compressed form) through a careful ordering scheme 
21  * on both the way down as well on the way up after boot, the latter
22  * for supporting the LKCD notion of passes in which most critical 
23  * data is the first to be saved to the dump device. Also the post 
24  * boot writeout happens from within the kernel rather than driven 
25  * from userspace.
26  *
27  * The sequence is orchestrated through the abstraction of "dumpers",
28  * one for the first stage which then sets up the dumper for the next 
29  * stage, providing for a smooth and flexible reuse of the singlestage 
30  * dump scheme methods and a handle to pass dump device configuration 
31  * information across the soft boot. 
32  *
33  * Copyright (C) 2002 International Business Machines Corp. 
34  *
35  * This code is released under version 2 of the GNU GPL.
36  */
37
38 /*
39  * Disruptive dumping using the second kernel soft-boot option
40  * for issuing dump i/o operates in 2 stages:
41  * 
42  * (1) - Saves the (compressed & formatted) dump in memory using a 
43  *       carefully ordered overlay scheme designed to capture the 
44  *       entire physical memory or selective portions depending on 
45  *       dump config settings, 
46  *     - Registers the stage 2 dumper and 
47  *     - Issues a soft reboot w/o clearing memory. 
48  *
49  *     The overlay scheme starts with a small bootstrap free area
50  *     and follows a reverse ordering of passes wherein it 
51  *     compresses and saves data starting with the least critical 
52  *     areas first, thus freeing up the corresponding pages to 
53  *     serve as destination for subsequent data to be saved, and
54  *     so on. With a good compression ratio, this makes it feasible
55  *     to capture an entire physical memory dump without significantly
56  *     reducing memory available during regular operation.
57  *
58  * (2) Post soft-reboot, runs through the saved memory dump and
59  *     writes it out to disk, this time around, taking care to
60  *     save the more critical data first (i.e. pages which figure 
61  *     in early passes for a regular dump). Finally issues a 
62  *     clean reboot.
63  *     
64  *     Since the data was saved in memory after selection/filtering
65  *     and formatted as per the chosen output dump format, at this 
66  *     stage the filter and format actions are just dummy (or
67  *     passthrough) actions, except for influence on ordering of
68  *     passes.
69  */
70
71 #include <linux/types.h>
72 #include <linux/kernel.h>
73 #include <linux/highmem.h>
74 #include <linux/bootmem.h>
75 #include <linux/dump.h>
76 #ifdef CONFIG_KEXEC
77 #include <linux/delay.h>
78 #include <linux/reboot.h>
79 #include <linux/kexec.h>
80 #endif
81 #include "dump_methods.h"
82
83 extern struct list_head dumper_list_head;
84 extern struct dump_memdev *dump_memdev;
85 extern struct dumper dumper_stage2;
86 struct dump_config_block *dump_saved_config = NULL;
87 extern struct dump_blockdev *dump_blockdev;
88 static struct dump_memdev *saved_dump_memdev = NULL;
89 static struct dumper *saved_dumper = NULL;
90
91 #ifdef CONFIG_KEXEC
92 extern int panic_timeout;
93 #endif
94
95 /* For testing 
96 extern void dump_display_map(struct dump_memdev *);
97 */
98
99 struct dumper *dumper_by_name(char *name)
100 {
101 #ifdef LATER
102         struct dumper *dumper;
103         list_for_each_entry(dumper, &dumper_list_head, dumper_list)
104                 if (!strncmp(dumper->name, name, 32))
105                         return dumper;
106
107         /* not found */
108         return NULL; 
109 #endif
110         /* Temporary proof of concept */
111         if (!strncmp(dumper_stage2.name, name, 32))
112                 return &dumper_stage2;
113         else
114                 return NULL;
115 }
116
117 #ifdef CONFIG_CRASH_DUMP_SOFTBOOT
118 extern void dump_early_reserve_map(struct dump_memdev *);
119
120 void crashdump_reserve(void)
121 {
122         extern unsigned long crashdump_addr;
123
124         if (crashdump_addr == 0xdeadbeef) 
125                 return;
126
127         /* reserve dump config and saved dump pages */
128         dump_saved_config = (struct dump_config_block *)crashdump_addr;
129         /* magic verification */
130         if (dump_saved_config->magic != DUMP_MAGIC_LIVE) {
131                 printk("Invalid dump magic. Ignoring dump\n");
132                 dump_saved_config = NULL;
133                 return;
134         }
135                         
136         printk("Dump may be available from previous boot\n");
137
138         reserve_bootmem(virt_to_phys((void *)crashdump_addr), 
139                 PAGE_ALIGN(sizeof(struct dump_config_block)));
140         dump_early_reserve_map(&dump_saved_config->memdev);
141
142 }
143 #endif
144
145 /* 
146  * Loads the dump configuration from a memory block saved across soft-boot
147  * The ops vectors need fixing up as the corresp. routines may have 
148  * relocated in the new soft-booted kernel.
149  */
150 int dump_load_config(struct dump_config_block *config)
151 {
152         struct dumper *dumper;
153         struct dump_data_filter *filter_table, *filter;
154         struct dump_dev *dev;
155         int i;
156
157         if (config->magic != DUMP_MAGIC_LIVE)
158                 return -ENOENT; /* not a valid config */
159
160         /* initialize generic config data */
161         memcpy(&dump_config, &config->config, sizeof(dump_config));
162
163         /* initialize dumper state */
164         if (!(dumper = dumper_by_name(config->dumper.name)))  {
165                 printk("dumper name mismatch\n");
166                 return -ENOENT; /* dumper mismatch */
167         }
168         
169         /* verify and fixup schema */
170         if (strncmp(dumper->scheme->name, config->scheme.name, 32)) {
171                 printk("dumper scheme mismatch\n");
172                 return -ENOENT; /* mismatch */
173         }
174         config->scheme.ops = dumper->scheme->ops;
175         config->dumper.scheme = &config->scheme;
176         
177         /* verify and fixup filter operations */
178         filter_table = dumper->filter;
179         for (i = 0, filter = config->filter_table; 
180                 ((i < MAX_PASSES) && filter_table[i].selector); 
181                 i++, filter++) {
182                 if (strncmp(filter_table[i].name, filter->name, 32)) {
183                         printk("dump filter mismatch\n");
184                         return -ENOENT; /* filter name mismatch */
185                 }
186                 filter->selector = filter_table[i].selector;
187         }
188         config->dumper.filter = config->filter_table;
189
190         /* fixup format */
191         if (strncmp(dumper->fmt->name, config->fmt.name, 32)) {
192                 printk("dump format mismatch\n");
193                 return -ENOENT; /* mismatch */
194         }
195         config->fmt.ops = dumper->fmt->ops;
196         config->dumper.fmt = &config->fmt;
197
198         /* fixup target device */
199         dev = (struct dump_dev *)(&config->dev[0]);
200         if (dumper->dev == NULL) {
201                 pr_debug("Vanilla dumper - assume default\n");
202                 if (dump_dev == NULL)
203                         return -ENODEV;
204                 dumper->dev = dump_dev;
205         }
206
207         if (strncmp(dumper->dev->type_name, dev->type_name, 32)) { 
208                 printk("dump dev type mismatch %s instead of %s\n",
209                                 dev->type_name, dumper->dev->type_name);
210                 return -ENOENT; /* mismatch */
211         }
212         dev->ops = dumper->dev->ops; 
213         config->dumper.dev = dev;
214         
215         /* fixup memory device containing saved dump pages */
216         /* assume statically init'ed dump_memdev */
217         config->memdev.ddev.ops = dump_memdev->ddev.ops; 
218         /* switch to memdev from prev boot */
219         saved_dump_memdev = dump_memdev; /* remember current */
220         dump_memdev = &config->memdev;
221
222         /* Make this the current primary dumper */
223         dump_config.dumper = &config->dumper;
224
225         return 0;
226 }
227
228 /* Saves the dump configuration in a memory block for use across a soft-boot */
229 int dump_save_config(struct dump_config_block *config)
230 {
231         printk("saving dump config settings\n");
232
233         /* dump config settings */
234         memcpy(&config->config, &dump_config, sizeof(dump_config));
235
236         /* dumper state */
237         memcpy(&config->dumper, dump_config.dumper, sizeof(struct dumper));
238         memcpy(&config->scheme, dump_config.dumper->scheme, 
239                 sizeof(struct dump_scheme));
240         memcpy(&config->fmt, dump_config.dumper->fmt, sizeof(struct dump_fmt));
241         memcpy(&config->dev[0], dump_config.dumper->dev, 
242                 sizeof(struct dump_anydev));
243         memcpy(&config->filter_table, dump_config.dumper->filter, 
244                 sizeof(struct dump_data_filter)*MAX_PASSES);
245
246         /* handle to saved mem pages */
247         memcpy(&config->memdev, dump_memdev, sizeof(struct dump_memdev));
248
249         config->magic = DUMP_MAGIC_LIVE;
250         
251         return 0;
252 }
253
254 int dump_init_stage2(struct dump_config_block *saved_config)
255 {
256         int err = 0;
257
258         pr_debug("dump_init_stage2\n");
259         /* Check if dump from previous boot exists */
260         if (saved_config) {
261                 printk("loading dumper from previous boot \n");
262                 /* load and configure dumper from previous boot */
263                 if ((err = dump_load_config(saved_config)))
264                         return err;
265
266                 if (!dump_oncpu) {
267                         if ((err = dump_configure(dump_config.dump_device))) {
268                                 printk("Stage 2 dump configure failed\n");
269                                 return err;
270                         }
271                 }
272
273                 dumper_reset();
274                 dump_dev = dump_config.dumper->dev;
275                 /* write out the dump */
276                 err = dump_generic_execute(NULL, NULL);
277                 
278                 dump_saved_config = NULL;
279
280                 if (!dump_oncpu) {
281                         dump_unconfigure(); 
282                 }
283                 
284                 return err;
285
286         } else {
287                 /* no dump to write out */
288                 printk("no dumper from previous boot \n");
289                 return 0;
290         }
291 }
292
293 extern void dump_mem_markpages(struct dump_memdev *);
294
295 int dump_switchover_stage(void)
296 {
297         int ret = 0;
298
299         /* trigger stage 2 rightaway - in real life would be after soft-boot */
300         /* dump_saved_config would be a boot param */
301         saved_dump_memdev = dump_memdev;
302         saved_dumper = dump_config.dumper;
303         ret = dump_init_stage2(dump_saved_config);
304         dump_memdev = saved_dump_memdev;
305         dump_config.dumper = saved_dumper;
306         return ret;
307 }
308
309 int dump_activate_softboot(void) 
310 {
311         int err = 0;
312 #ifdef CONFIG_KEXEC
313         int num_cpus_online = 0;
314         struct kimage *image;
315 #endif
316
317         /* temporary - switchover to writeout previously saved dump */
318 #ifndef CONFIG_KEXEC
319         err = dump_switchover_stage(); /* non-disruptive case */
320         if (dump_oncpu)
321                         dump_config.dumper = &dumper_stage1; /* set things back */
322
323         return err;
324 #else
325
326         dump_silence_level = DUMP_HALT_CPUS;
327         /* wait till we become the only cpu */
328         /* maybe by checking for online cpus ? */
329
330         while((num_cpus_online = num_online_cpus()) > 1);
331
332         /* now call into kexec */
333
334         image = xchg(&kexec_image, 0);
335         if (image) {
336                         mdelay(panic_timeout*1000);
337                                 machine_kexec(image);
338                                 }
339
340
341         /* TBD/Fixme:
342          *          * should we call reboot notifiers ? inappropriate for panic ?
343          *                   * what about device_shutdown() ?
344          *                            * is explicit bus master disabling needed or can we do that
345          *                                     * through driverfs ?
346          *                                              */
347         return 0;
348 #endif
349 }
350
351 /* --- DUMP SCHEME ROUTINES  --- */
352
353 static inline int dump_buf_pending(struct dumper *dumper)
354 {
355         return (dumper->curr_buf - dumper->dump_buf);
356 }
357
358 /* Invoked during stage 1 of soft-reboot based dumping */
359 int dump_overlay_sequencer(void)
360 {
361         struct dump_data_filter *filter = dump_config.dumper->filter;
362         struct dump_data_filter *filter2 = dumper_stage2.filter;
363         int pass = 0, err = 0, save = 0;
364         int (*action)(unsigned long, unsigned long);
365
366         /* Make sure gzip compression is being used */
367         if (dump_config.dumper->compress->compress_type != DUMP_COMPRESS_GZIP) {
368                 printk(" Please set GZIP compression \n");
369                 return -EINVAL;
370         }
371
372         /* start filling in dump data right after the header */
373         dump_config.dumper->curr_offset = 
374                 PAGE_ALIGN(dump_config.dumper->header_len);
375
376         /* Locate the last pass */
377         for (;filter->selector; filter++, pass++);
378         
379         /* 
380          * Start from the end backwards: overlay involves a reverse 
381          * ordering of passes, since less critical pages are more
382          * likely to be reusable as scratch space once we are through
383          * with them. 
384          */
385         for (--pass, --filter; pass >= 0; pass--, filter--)
386         {
387                 /* Assumes passes are exclusive (even across dumpers) */
388                 /* Requires care when coding the selection functions */
389                 if ((save = filter->level_mask & dump_config.level))
390                         action = dump_save_data;
391                 else
392                         action = dump_skip_data;
393
394                 /* Remember the offset where this pass started */
395                 /* The second stage dumper would use this */
396                 if (dump_buf_pending(dump_config.dumper) & (PAGE_SIZE - 1)) {
397                         pr_debug("Starting pass %d with pending data\n", pass);
398                         pr_debug("filling dummy data to page-align it\n");
399                         dump_config.dumper->curr_buf = (void *)PAGE_ALIGN(
400                                 (unsigned long)dump_config.dumper->curr_buf);
401                 }
402                 
403                 filter2[pass].start[0] = dump_config.dumper->curr_offset
404                         + dump_buf_pending(dump_config.dumper);
405
406                 err = dump_iterator(pass, action, filter);
407
408                 filter2[pass].end[0] = dump_config.dumper->curr_offset
409                         + dump_buf_pending(dump_config.dumper);
410                 filter2[pass].num_mbanks = 1;
411
412                 if (err < 0) {
413                         printk("dump_overlay_seq: failure %d in pass %d\n", 
414                                 err, pass);
415                         break;
416                 }       
417                 printk("\n %d overlay pages %s of %d each in pass %d\n", 
418                 err, save ? "saved" : "skipped", DUMP_PAGE_SIZE, pass);
419         }
420
421         return err;
422 }
423
424 /* from dump_memdev.c */
425 extern struct page *dump_mem_lookup(struct dump_memdev *dev, unsigned long loc);
426 extern struct page *dump_mem_next_page(struct dump_memdev *dev);
427
428 static inline struct page *dump_get_saved_page(loff_t loc)
429 {
430         return (dump_mem_lookup(dump_memdev, loc >> PAGE_SHIFT));
431 }
432
433 static inline struct page *dump_next_saved_page(void)
434 {
435         return (dump_mem_next_page(dump_memdev));
436 }
437
438 /* 
439  * Iterates over list of saved dump pages. Invoked during second stage of 
440  * soft boot dumping
441  *
442  * Observation: If additional selection is desired at this stage then
443  * a different iterator could be written which would advance 
444  * to the next page header everytime instead of blindly picking up
445  * the data. In such a case loc would be interpreted differently. 
446  * At this moment however a blind pass seems sufficient, cleaner and
447  * faster.
448  */
449 int dump_saved_data_iterator(int pass, int (*action)(unsigned long, 
450         unsigned long), struct dump_data_filter *filter)
451 {
452         loff_t loc, end;
453         struct page *page;
454         unsigned long count = 0;
455         int i, err = 0;
456         unsigned long sz;
457
458         for (i = 0; i < filter->num_mbanks; i++) {
459                 loc  = filter->start[i];
460                 end = filter->end[i];
461                 printk("pass %d, start off 0x%llx end offset 0x%llx\n", pass,
462                         loc, end);
463
464                 /* loc will get treated as logical offset into stage 1 */
465                 page = dump_get_saved_page(loc);
466                         
467                 for (; loc < end; loc += PAGE_SIZE) {
468                         dump_config.dumper->curr_loc = loc;
469                         if (!page) {
470                                 printk("no more saved data for pass %d\n", 
471                                         pass);
472                                 break;
473                         }
474                         sz = (loc + PAGE_SIZE > end) ? end - loc : PAGE_SIZE;
475
476                         if (page && filter->selector(pass, (unsigned long)page, 
477                                 PAGE_SIZE))  {
478                                 pr_debug("mem offset 0x%llx\n", loc);
479                                 if ((err = action((unsigned long)page, sz))) 
480                                         break;
481                                 else
482                                         count++;
483                                 /* clear the contents of page */
484                                 /* fixme: consider using KM_DUMP instead */
485                                 clear_highpage(page);
486                         
487                         }
488                         page = dump_next_saved_page();
489                 }
490         }
491
492         return err ? err : count;
493 }
494
495 static inline int dump_overlay_pages_done(struct page *page, int nr)
496 {
497         int ret=0;
498
499         for (; nr ; page++, nr--) {
500                 if (dump_check_and_free_page(dump_memdev, page))
501                         ret++;
502         }
503         return ret;
504 }
505
506 int dump_overlay_save_data(unsigned long loc, unsigned long len)
507 {
508         int err = 0;
509         struct page *page = (struct page *)loc;
510         static unsigned long cnt = 0;
511
512         if ((err = dump_generic_save_data(loc, len)))
513                 return err;
514
515         if (dump_overlay_pages_done(page, len >> PAGE_SHIFT)) {
516                 cnt++;
517                 if (!(cnt & 0x7f))
518                         pr_debug("released page 0x%lx\n", page_to_pfn(page));
519         }
520         
521         return err;
522 }
523
524
525 int dump_overlay_skip_data(unsigned long loc, unsigned long len)
526 {
527         struct page *page = (struct page *)loc;
528
529         dump_overlay_pages_done(page, len >> PAGE_SHIFT);
530         return 0;
531 }
532
533 int dump_overlay_resume(void)
534 {
535         int err = 0;
536
537         /* 
538          * switch to stage 2 dumper, save dump_config_block
539          * and then trigger a soft-boot
540          */
541         dumper_stage2.header_len = dump_config.dumper->header_len;
542         dump_config.dumper = &dumper_stage2;
543         if ((err = dump_save_config(dump_saved_config)))
544                 return err;
545
546         dump_dev = dump_config.dumper->dev;
547
548 #ifdef CONFIG_KEXEC
549         /* If we are doing a disruptive dump, activate softboot now */
550         if((panic_timeout > 0) && (!(dump_config.flags & DUMP_FLAGS_NONDISRUPT)))
551         err = dump_activate_softboot();
552 #endif
553                 
554         return err;
555         err = dump_switchover_stage();  /* plugs into soft boot mechanism */
556         dump_config.dumper = &dumper_stage1; /* set things back */
557         return err;
558 }
559
560 int dump_overlay_configure(unsigned long devid)
561 {
562         struct dump_dev *dev;
563         struct dump_config_block *saved_config = dump_saved_config;
564         int err = 0;
565
566         /* If there is a previously saved dump, write it out first */
567         if (saved_config) {
568                 printk("Processing old dump pending writeout\n");
569                 err = dump_switchover_stage();
570                 if (err) {
571                         printk("failed to writeout saved dump\n");
572                         return err;
573                 }
574                 dump_free_mem(saved_config); /* testing only: not after boot */
575         }
576
577         dev = dumper_stage2.dev = dump_config.dumper->dev;
578         /* From here on the intermediate dump target is memory-only */
579         dump_dev = dump_config.dumper->dev = &dump_memdev->ddev;
580         if ((err = dump_generic_configure(0))) {
581                 printk("dump generic configure failed: err %d\n", err);
582                 return err;
583         }
584         /* temporary */
585         dumper_stage2.dump_buf = dump_config.dumper->dump_buf;
586
587         /* Sanity check on the actual target dump device */
588         if (!dev || (err = dev->ops->open(dev, devid))) {
589                 return err;
590         }
591         /* TBD: should we release the target if this is soft-boot only ? */
592
593         /* alloc a dump config block area to save across reboot */
594         if (!(dump_saved_config = dump_alloc_mem(sizeof(struct 
595                 dump_config_block)))) {
596                 printk("dump config block alloc failed\n");
597                 /* undo configure */
598                 dump_generic_unconfigure();
599                 return -ENOMEM;
600         }
601         dump_config.dump_addr = (unsigned long)dump_saved_config;
602         printk("Dump config block of size %d set up at 0x%lx\n", 
603                 sizeof(*dump_saved_config), (unsigned long)dump_saved_config);
604         return 0;
605 }
606
607 int dump_overlay_unconfigure(void)
608 {
609         struct dump_dev *dev = dumper_stage2.dev;
610         int err = 0;
611
612         pr_debug("dump_overlay_unconfigure\n");
613         /* Close the secondary device */
614         dev->ops->release(dev); 
615         pr_debug("released secondary device\n");
616
617         err = dump_generic_unconfigure();
618         pr_debug("Unconfigured generic portions\n");
619         dump_free_mem(dump_saved_config);
620         dump_saved_config = NULL;
621         pr_debug("Freed saved config block\n");
622         dump_dev = dump_config.dumper->dev = dumper_stage2.dev;
623
624         printk("Unconfigured overlay dumper\n");
625         return err;
626 }
627
628 int dump_staged_unconfigure(void)
629 {
630         int err = 0;
631         struct dump_config_block *saved_config = dump_saved_config;
632         struct dump_dev *dev;
633
634         pr_debug("dump_staged_unconfigure\n");
635         err = dump_generic_unconfigure();
636
637         /* now check if there is a saved dump waiting to be written out */
638         if (saved_config) {
639                 printk("Processing saved dump pending writeout\n");
640                 if ((err = dump_switchover_stage())) {
641                         printk("Error in commiting saved dump at 0x%lx\n", 
642                                 (unsigned long)saved_config);
643                         printk("Old dump may hog memory\n");
644                 } else {
645                         dump_free_mem(saved_config);
646                         pr_debug("Freed saved config block\n");
647                 }
648                 dump_saved_config = NULL;
649         } else {
650                 dev = &dump_memdev->ddev;
651                 dev->ops->release(dev);
652         }
653         printk("Unconfigured second stage dumper\n");
654
655         return 0;
656 }
657
658 /* ----- PASSTHRU FILTER ROUTINE --------- */
659
660 /* transparent - passes everything through */
661 int dump_passthru_filter(int pass, unsigned long loc, unsigned long sz)
662 {
663         return 1;
664 }
665
666 /* ----- PASSTRU FORMAT ROUTINES ---- */
667
668
669 int dump_passthru_configure_header(const char *panic_str, const struct pt_regs *regs)
670 {
671         dump_config.dumper->header_dirty++;
672         return 0;
673 }
674
675 /* Copies bytes of data from page(s) to the specified buffer */
676 int dump_copy_pages(void *buf, struct page *page, unsigned long sz)
677 {
678         unsigned long len = 0, bytes;
679         void *addr;
680
681         while (len < sz) {
682                 addr = kmap_atomic(page, KM_DUMP);
683                 bytes = (sz > len + PAGE_SIZE) ? PAGE_SIZE : sz - len;  
684                 memcpy(buf, addr, bytes); 
685                 kunmap_atomic(addr, KM_DUMP);
686                 buf += bytes;
687                 len += bytes;
688                 page++;
689         }
690         /* memset(dump_config.dumper->curr_buf, 0x57, len); temporary */
691
692         return sz - len;
693 }
694
695 int dump_passthru_update_header(void)
696 {
697         long len = dump_config.dumper->header_len;
698         struct page *page;
699         void *buf = dump_config.dumper->dump_buf;
700         int err = 0;
701
702         if (!dump_config.dumper->header_dirty)
703                 return 0;
704
705         pr_debug("Copying header of size %ld bytes from memory\n", len);
706         if (len > DUMP_BUFFER_SIZE) 
707                 return -E2BIG;
708
709         page = dump_mem_lookup(dump_memdev, 0);
710         for (; (len > 0) && page; buf += PAGE_SIZE, len -= PAGE_SIZE) {
711                 if ((err = dump_copy_pages(buf, page, PAGE_SIZE)))
712                         return err;
713                 page = dump_mem_next_page(dump_memdev);
714         }
715         if (len > 0) {
716                 printk("Incomplete header saved in mem\n");
717                 return -ENOENT;
718         }
719
720         if ((err = dump_dev_seek(0))) {
721                 printk("Unable to seek to dump header offset\n");
722                 return err;
723         }
724         err = dump_ll_write(dump_config.dumper->dump_buf, 
725                 buf - dump_config.dumper->dump_buf);
726         if (err < dump_config.dumper->header_len)
727                 return (err < 0) ? err : -ENOSPC;
728
729         dump_config.dumper->header_dirty = 0;
730         return 0;
731 }
732
733 static loff_t next_dph_offset = 0;
734
735 static int dph_valid(struct __dump_page *dph)
736 {
737         if ((dph->dp_address & (PAGE_SIZE - 1)) || (dph->dp_flags 
738               > DUMP_DH_COMPRESSED) || (!dph->dp_flags) ||
739                 (dph->dp_size > PAGE_SIZE)) {
740         printk("dp->address = 0x%llx, dp->size = 0x%x, dp->flag = 0x%x\n",
741                 dph->dp_address, dph->dp_size, dph->dp_flags);
742                 return 0;
743         }
744         return 1;
745 }
746
747 int dump_verify_lcrash_data(void *buf, unsigned long sz)
748 {
749         struct __dump_page *dph;
750
751         /* sanity check for page headers */
752         while (next_dph_offset + sizeof(*dph) < sz) {
753                 dph = (struct __dump_page *)(buf + next_dph_offset);
754                 if (!dph_valid(dph)) {
755                         printk("Invalid page hdr at offset 0x%llx\n",
756                                 next_dph_offset);
757                         return -EINVAL;
758                 }
759                 next_dph_offset += dph->dp_size + sizeof(*dph);
760         }
761
762         next_dph_offset -= sz;  
763         return 0;
764 }
765
766 /* 
767  * TBD/Later: Consider avoiding the copy by using a scatter/gather 
768  * vector representation for the dump buffer
769  */
770 int dump_passthru_add_data(unsigned long loc, unsigned long sz)
771 {
772         struct page *page = (struct page *)loc;
773         void *buf = dump_config.dumper->curr_buf;
774         int err = 0;
775
776         if ((err = dump_copy_pages(buf, page, sz))) {
777                 printk("dump_copy_pages failed");
778                 return err;
779         }
780
781         if ((err = dump_verify_lcrash_data(buf, sz))) {
782                 printk("dump_verify_lcrash_data failed\n");
783                 printk("Invalid data for pfn 0x%lx\n", page_to_pfn(page));
784                 printk("Page flags 0x%lx\n", page->flags);
785                 printk("Page count 0x%x\n", atomic_read(&page->count));
786                 return err;
787         }
788
789         dump_config.dumper->curr_buf = buf + sz;
790
791         return 0;
792 }
793
794
795 /* Stage 1 dumper: Saves compressed dump in memory and soft-boots system */
796
797 /* Scheme to overlay saved data in memory for writeout after a soft-boot */
798 struct dump_scheme_ops dump_scheme_overlay_ops = {
799         .configure      = dump_overlay_configure,
800         .unconfigure    = dump_overlay_unconfigure,
801         .sequencer      = dump_overlay_sequencer,
802         .iterator       = dump_page_iterator,
803         .save_data      = dump_overlay_save_data,
804         .skip_data      = dump_overlay_skip_data,
805         .write_buffer   = dump_generic_write_buffer
806 };
807
808 struct dump_scheme dump_scheme_overlay = {
809         .name           = "overlay",
810         .ops            = &dump_scheme_overlay_ops
811 };
812
813
814 /* Stage 1 must use a good compression scheme - default to gzip */
815 extern struct __dump_compress dump_gzip_compression;
816
817 struct dumper dumper_stage1 = {
818         .name           = "stage1",
819         .scheme         = &dump_scheme_overlay,
820         .fmt            = &dump_fmt_lcrash,
821         .compress       = &dump_none_compression, /* needs to be gzip */
822         .filter         = dump_filter_table,
823         .dev            = NULL,
824 };              
825
826 /* Stage 2 dumper: Activated after softboot to write out saved dump to device */
827
828 /* Formatter that transfers data as is (transparent) w/o further conversion */
829 struct dump_fmt_ops dump_fmt_passthru_ops = {
830         .configure_header       = dump_passthru_configure_header,
831         .update_header          = dump_passthru_update_header,
832         .save_context           = NULL, /* unused */
833         .add_data               = dump_passthru_add_data,
834         .update_end_marker      = dump_lcrash_update_end_marker
835 };
836
837 struct dump_fmt dump_fmt_passthru = {
838         .name   = "passthru",
839         .ops    = &dump_fmt_passthru_ops
840 };
841
842 /* Filter that simply passes along any data within the range (transparent)*/
843 /* Note: The start and end ranges in the table are filled in at run-time */
844
845 extern int dump_filter_none(int pass, unsigned long loc, unsigned long sz);
846
847 struct dump_data_filter dump_passthru_filtertable[MAX_PASSES] = {
848 {.name = "passkern", .selector = dump_passthru_filter, 
849         .level_mask = DUMP_MASK_KERN },
850 {.name = "passuser", .selector = dump_passthru_filter, 
851         .level_mask = DUMP_MASK_USED },
852 {.name = "passunused", .selector = dump_passthru_filter, 
853         .level_mask = DUMP_MASK_UNUSED },
854 {.name = "none", .selector = dump_filter_none, 
855         .level_mask = DUMP_MASK_REST }
856 };
857
858
859 /* Scheme to handle data staged / preserved across a soft-boot */
860 struct dump_scheme_ops dump_scheme_staged_ops = {
861         .configure      = dump_generic_configure,
862         .unconfigure    = dump_staged_unconfigure,
863         .sequencer      = dump_generic_sequencer,
864         .iterator       = dump_saved_data_iterator,
865         .save_data      = dump_generic_save_data,
866         .skip_data      = dump_generic_skip_data,
867         .write_buffer   = dump_generic_write_buffer
868 };
869
870 struct dump_scheme dump_scheme_staged = {
871         .name           = "staged",
872         .ops            = &dump_scheme_staged_ops
873 };
874
875 /* The stage 2 dumper comprising all these */
876 struct dumper dumper_stage2 = {
877         .name           = "stage2",
878         .scheme         = &dump_scheme_staged,
879         .fmt            = &dump_fmt_passthru,
880         .compress       = &dump_none_compression,
881         .filter         = dump_passthru_filtertable,
882         .dev            = NULL,
883 };              
884