From: Takao I. <ind...@so...> - 2004-11-29 10:35:59
|
This is a patch for diskdump common layer. diff -Nur linux-2.6.9.org/arch/i386/kernel/nmi.c linux-2.6.9/arch/i386/kernel/nmi.c --- linux-2.6.9.org/arch/i386/kernel/nmi.c 2004-10-19 06:54:31.000000000 +0900 +++ linux-2.6.9/arch/i386/kernel/nmi.c 2004-11-26 20:13:17.770808053 +0900 @@ -564,3 +564,4 @@ EXPORT_SYMBOL(release_lapic_nmi); EXPORT_SYMBOL(disable_timer_nmi_watchdog); EXPORT_SYMBOL(enable_timer_nmi_watchdog); +EXPORT_SYMBOL_GPL(touch_nmi_watchdog); diff -Nur linux-2.6.9.org/arch/i386/kernel/reboot.c linux-2.6.9/arch/i386/kernel/reboot.c --- linux-2.6.9.org/arch/i386/kernel/reboot.c 2004-10-19 06:53:45.000000000 +0900 +++ linux-2.6.9/arch/i386/kernel/reboot.c 2004-11-26 20:13:17.770808053 +0900 @@ -330,7 +330,8 @@ * Stop all CPUs and turn off local APICs and the IO-APIC, so * other OSs see a clean IRQ state. */ - smp_send_stop(); + if (!crashdump_mode()) + smp_send_stop(); #elif defined(CONFIG_X86_LOCAL_APIC) if (cpu_has_apic) { local_irq_disable(); diff -Nur linux-2.6.9.org/arch/i386/kernel/smp.c linux-2.6.9/arch/i386/kernel/smp.c --- linux-2.6.9.org/arch/i386/kernel/smp.c 2004-10-19 06:53:12.000000000 +0900 +++ linux-2.6.9/arch/i386/kernel/smp.c 2004-11-26 20:14:15.497369846 +0900 @@ -520,7 +520,7 @@ return 0; /* Can deadlock when called with interrupts disabled */ - WARN_ON(irqs_disabled()); + WARN_ON(irqs_disabled() && !crashdump_mode()); data.func = func; data.info = info; diff -Nur linux-2.6.9.org/arch/i386/kernel/traps.c linux-2.6.9/arch/i386/kernel/traps.c --- linux-2.6.9.org/arch/i386/kernel/traps.c 2004-10-19 06:53:23.000000000 +0900 +++ linux-2.6.9/arch/i386/kernel/traps.c 2004-11-26 20:13:17.771784616 +0900 @@ -349,6 +349,7 @@ printk("\n"); notify_die(DIE_OOPS, (char *)str, regs, err, 255, SIGSEGV); show_registers(regs); + try_crashdump(regs); } else printk(KERN_ERR "Recursive die() failure, output suppressed\n"); diff -Nur linux-2.6.9.org/arch/i386/mm/init.c linux-2.6.9/arch/i386/mm/init.c --- linux-2.6.9.org/arch/i386/mm/init.c 2004-10-19 06:54:54.000000000 +0900 +++ linux-2.6.9/arch/i386/mm/init.c 2004-11-26 20:13:17.771784616 +0900 @@ -187,6 +187,53 @@ extern int is_available_memory(efi_memory_desc_t *); +unsigned long next_ram_page(unsigned long pagenr) +{ + int i; + unsigned long addr, end; + unsigned long min_pageno = ULONG_MAX; + + pagenr++; + + if (efi_enabled) { + efi_memory_desc_t *md; + + for (i = 0; i < memmap.nr_map; i++) { + md = &memmap.map[i]; + if (!is_available_memory(md)) + continue; + addr = (md->phys_addr+PAGE_SIZE-1) >> PAGE_SHIFT; + end = (md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT)) >> PAGE_SHIFT; + + if ((pagenr >= addr) && (pagenr < end)) + return pagenr; + if ((pagenr < addr) && (addr < min_pageno)) + min_pageno = addr; + } + return min_pageno; + } + + for (i = 0; i < e820.nr_map; i++) { + + if (e820.map[i].type != E820_RAM) /* not usable memory */ + continue; + /* + * !!!FIXME!!! Some BIOSen report areas as RAM that + * are not. Notably the 640->1Mb area. We need a sanity + * check here. + */ + addr = (e820.map[i].addr+PAGE_SIZE-1) >> PAGE_SHIFT; + end = (e820.map[i].addr+e820.map[i].size) >> PAGE_SHIFT; + if ((pagenr >= addr) && (pagenr < end)) + return pagenr; + if ((pagenr < addr) && (addr < min_pageno)) + min_pageno = addr; + } + return min_pageno; +} + +EXPORT_SYMBOL_GPL(next_ram_page); + static inline int page_is_ram(unsigned long pagenr) { int i; @@ -225,6 +272,8 @@ return 0; } +EXPORT_SYMBOL_GPL(page_is_ram); + #ifdef CONFIG_HIGHMEM pte_t *kmap_pte; pgprot_t kmap_prot; diff -Nur linux-2.6.9.org/arch/ia64/kernel/process.c linux-2.6.9/arch/ia64/kernel/process.c --- linux-2.6.9.org/arch/ia64/kernel/process.c 2004-10-19 06:54:08.000000000 +0900 +++ linux-2.6.9/arch/ia64/kernel/process.c 2004-11-26 20:13:17.772761178 +0900 @@ -38,6 +38,7 @@ #include <asm/uaccess.h> #include <asm/unwind.h> #include <asm/user.h> +#include <asm/diskdump.h> #ifdef CONFIG_PERFMON # include <asm/perfmon.h> @@ -555,11 +556,13 @@ } void -do_copy_regs (struct unw_frame_info *info, void *arg) +ia64_do_copy_regs (struct unw_frame_info *info, void *arg) { do_copy_task_regs(current, info, arg); } +EXPORT_SYMBOL_GPL(ia64_do_copy_regs); + void do_dump_fpu (struct unw_frame_info *info, void *arg) { @@ -572,7 +575,7 @@ struct unw_frame_info tcore_info; if (current == task) { - unw_init_running(do_copy_regs, regs); + unw_init_running(ia64_do_copy_regs, regs); } else { memset(&tcore_info, 0, sizeof(tcore_info)); unw_init_from_blocked_task(&tcore_info, task); @@ -584,7 +587,7 @@ void ia64_elf_core_copy_regs (struct pt_regs *pt, elf_gregset_t dst) { - unw_init_running(do_copy_regs, dst); + unw_init_running(ia64_do_copy_regs, dst); } int @@ -775,3 +778,22 @@ } EXPORT_SYMBOL(machine_power_off); + +void +ia64_freeze_cpu (struct unw_frame_info *info, void *arg) +{ + current->thread.ksp = (__u64)(info->sw) - 16; + for (;;) local_irq_disable(); +} + +EXPORT_SYMBOL_GPL(ia64_freeze_cpu); + +void +ia64_start_dump (struct unw_frame_info *info, void *arg) +{ + struct dump_call_param *param = arg; + + param->func(param->regs, info); +} + +EXPORT_SYMBOL_GPL(ia64_start_dump); diff -Nur linux-2.6.9.org/arch/ia64/kernel/smp.c linux-2.6.9/arch/ia64/kernel/smp.c --- linux-2.6.9.org/arch/ia64/kernel/smp.c 2004-10-19 06:55:29.000000000 +0900 +++ linux-2.6.9/arch/ia64/kernel/smp.c 2004-11-26 20:14:45.477838229 +0900 @@ -332,7 +332,7 @@ return 0; /* Can deadlock when called with interrupts disabled */ - WARN_ON(irqs_disabled()); + WARN_ON(irqs_disabled() && !crashdump_mode()); data.func = func; data.info = info; diff -Nur linux-2.6.9.org/arch/ia64/kernel/traps.c linux-2.6.9/arch/ia64/kernel/traps.c --- linux-2.6.9.org/arch/ia64/kernel/traps.c 2004-10-19 06:55:28.000000000 +0900 +++ linux-2.6.9/arch/ia64/kernel/traps.c 2004-11-26 20:13:17.773737741 +0900 @@ -92,6 +92,7 @@ } else printk(KERN_ERR "Recursive die() failure, output suppressed\n"); + try_crashdump(regs); bust_spinlocks(0); die.lock_owner = -1; spin_unlock_irq(&die.lock); diff -Nur linux-2.6.9.org/arch/ia64/mm/init.c linux-2.6.9/arch/ia64/mm/init.c --- linux-2.6.9.org/arch/ia64/mm/init.c 2004-10-19 06:53:51.000000000 +0900 +++ linux-2.6.9/arch/ia64/mm/init.c 2004-11-26 20:13:17.773737741 +0900 @@ -254,6 +254,94 @@ return page; } +struct curr_mem_request { + unsigned long requested; + unsigned long min_physaddr; + int found; +}; + +/* ++ * Check whether a physical address fits within the memory descriptor ++ * block sent from efi_mmap_walk(). If it fits, set found. ++ */ +static int +verify_physaddr (unsigned long start, unsigned long end, void *arg) +{ + struct curr_mem_request *cr = arg; + + start = __pa(start); + end = __pa(end); + + if ((cr->requested >= start) && (cr->requested + PAGE_SIZE) <= end) { + cr->found = 1; + return -1; + } + + return 0; +} + +/* + * If physical page 'nr' is valid RAM then return 1. Otherwise return 0. + */ +int +page_is_ram (unsigned long pagenr) +{ + struct curr_mem_request cr; + + if (!pfn_valid(pagenr)) + return 0; + + cr.requested = pagenr << PAGE_SHIFT; + cr.found = 0; + + efi_memmap_walk(verify_physaddr, &cr); + + return cr.found; +} +EXPORT_SYMBOL_GPL(page_is_ram); + +static int +find_next (unsigned long start, unsigned long end, void *arg) +{ + struct curr_mem_request *cr = (struct curr_mem_request *)arg; + + start = __pa(start); + end = __pa(end); + + if ((cr->requested >= start) && (cr->requested + PAGE_SIZE) <= end) { + cr->min_physaddr = cr->requested; + cr->found = 1; + return -1; + } + if ((cr->requested < start) && (start + PAGE_SIZE) <= end) + if (start < cr->min_physaddr) { + cr->min_physaddr = start; + cr->found = 1; + } + + return 0; +} + +unsigned long +next_ram_page (unsigned long pagenr) +{ + struct curr_mem_request cr; + + pagenr++; + + cr.requested = pagenr << PAGE_SHIFT; + cr.found = 0; + cr.min_physaddr = ULONG_MAX; + + efi_memmap_walk(find_next, &cr); + + if (cr.found) + return cr.min_physaddr >> PAGE_SHIFT; + else + return ULONG_MAX; +} +EXPORT_SYMBOL_GPL(next_ram_page); + static void setup_gate (void) { diff -Nur linux-2.6.9.org/arch/ppc64/kernel/smp.c linux-2.6.9/arch/ppc64/kernel/smp.c --- linux-2.6.9.org/arch/ppc64/kernel/smp.c 2004-10-19 06:54:40.000000000 +0900 +++ linux-2.6.9/arch/ppc64/kernel/smp.c 2004-11-26 20:17:51.303031265 +0900 @@ -684,7 +684,7 @@ unsigned long timeout; /* Can deadlock when called with interrupts disabled */ - WARN_ON(irqs_disabled()); + WARN_ON(irqs_disabled() && !crashdump_mode()); data.func = func; data.info = info; diff -Nur linux-2.6.9.org/arch/ppc64/kernel/traps.c linux-2.6.9/arch/ppc64/kernel/traps.c --- linux-2.6.9.org/arch/ppc64/kernel/traps.c 2004-10-19 06:54:19.000000000 +0900 +++ linux-2.6.9/arch/ppc64/kernel/traps.c 2004-11-26 20:13:17.774714303 +0900 @@ -116,6 +116,7 @@ if (nl) printk("\n"); show_regs(regs); + try_crashdump(regs); bust_spinlocks(0); spin_unlock_irq(&die_lock); diff -Nur linux-2.6.9.org/arch/ppc64/mm/init.c linux-2.6.9/arch/ppc64/mm/init.c --- linux-2.6.9.org/arch/ppc64/mm/init.c 2004-10-19 06:54:32.000000000 +0900 +++ linux-2.6.9/arch/ppc64/mm/init.c 2004-11-26 20:13:17.774714303 +0900 @@ -593,6 +593,34 @@ } EXPORT_SYMBOL(page_is_ram); +unsigned long next_ram_page(unsigned long pfn) +{ + int i; + unsigned long paddr, base; + unsigned long best_base = (ULONG_MAX << PAGE_SHIFT); + + pfn++; + paddr = (pfn << PAGE_SHIFT); + + for (i=0; i < lmb.memory.cnt; i++) { +#ifdef CONFIG_MSCHUNKS + base = lmb.memory.region[i].physbase; +#else + base = lmb.memory.region[i].base; +#endif + if ((paddr >= base) + && (paddr < (base + lmb.memory.region[i].size))) + return (paddr >> PAGE_SHIFT); + if ((paddr < base) && (base < best_base)) + best_base = base; + } + if (best_base < (ULONG_MAX << PAGE_SHIFT)) + return (best_base >> PAGE_SHIFT); + else + return ULONG_MAX; +} +EXPORT_SYMBOL_GPL(next_ram_page); + /* * Initialize the bootmem system and give it all the memory we * have available. diff -Nur linux-2.6.9.org/arch/x86_64/kernel/reboot.c linux-2.6.9/arch/x86_64/kernel/reboot.c --- linux-2.6.9.org/arch/x86_64/kernel/reboot.c 2004-10-19 06:54:55.000000000 +0900 +++ linux-2.6.9/arch/x86_64/kernel/reboot.c 2004-11-26 20:13:17.774714303 +0900 @@ -130,7 +130,8 @@ int i; #ifdef CONFIG_SMP - smp_halt(); + if (!crashdump_mode()) + smp_halt(); #endif local_irq_disable(); @@ -141,7 +142,8 @@ disable_IO_APIC(); - local_irq_enable(); + if (!crashdump_mode()) + local_irq_enable(); /* Tell the BIOS if we want cold or warm reboot */ *((unsigned short *)__va(0x472)) = reboot_mode; diff -Nur linux-2.6.9.org/arch/x86_64/kernel/traps.c linux-2.6.9/arch/x86_64/kernel/traps.c --- linux-2.6.9.org/arch/x86_64/kernel/traps.c 2004-10-19 06:53:06.000000000 +0900 +++ linux-2.6.9/arch/x86_64/kernel/traps.c 2004-11-26 20:13:17.775690866 +0900 @@ -367,6 +367,7 @@ oops_begin(); handle_BUG(regs); __die(str, regs, err); + try_crashdump(regs); oops_end(); do_exit(SIGSEGV); } diff -Nur linux-2.6.9.org/arch/x86_64/mm/fault.c linux-2.6.9/arch/x86_64/mm/fault.c --- linux-2.6.9.org/arch/x86_64/mm/fault.c 2004-10-19 06:53:06.000000000 +0900 +++ linux-2.6.9/arch/x86_64/mm/fault.c 2004-11-26 20:13:17.775690866 +0900 @@ -484,6 +484,7 @@ __die("Oops", regs, error_code); /* Executive summary in case the body of the oops scrolled away */ printk(KERN_EMERG "CR2: %016lx\n", address); + try_crashdump(regs); oops_end(); do_exit(SIGKILL); diff -Nur linux-2.6.9.org/arch/x86_64/mm/init.c linux-2.6.9/arch/x86_64/mm/init.c --- linux-2.6.9.org/arch/x86_64/mm/init.c 2004-10-19 06:53:43.000000000 +0900 +++ linux-2.6.9/arch/x86_64/mm/init.c 2004-11-26 20:13:17.776667428 +0900 @@ -22,6 +22,7 @@ #include <linux/pagemap.h> #include <linux/bootmem.h> #include <linux/proc_fs.h> +#include <linux/module.h> #include <asm/processor.h> #include <asm/system.h> @@ -378,6 +379,35 @@ __flush_tlb_all(); } +unsigned long next_ram_page (unsigned long pagenr) +{ + int i; + unsigned long min_pageno = ULONG_MAX; + + pagenr++; + + for (i = 0; i < e820.nr_map; i++) { + unsigned long addr, end; + + if (e820.map[i].type != E820_RAM) /* not usable memory */ + continue; + /* + * !!!FIXME!!! Some BIOSen report areas as RAM that + * are not. Notably the 640->1Mb area. We need a sanity + * check here. + */ + addr = (e820.map[i].addr+PAGE_SIZE-1) >> PAGE_SHIFT; + end = (e820.map[i].addr+e820.map[i].size) >> PAGE_SHIFT; + if ((pagenr >= addr) && (pagenr < end)) + return pagenr; + if ((pagenr < addr) && (addr < min_pageno)) + min_pageno = addr; + } + return min_pageno; +} + +EXPORT_SYMBOL_GPL(next_ram_page); + static inline int page_is_ram (unsigned long pagenr) { int i; @@ -400,6 +430,8 @@ return 0; } +EXPORT_SYMBOL_GPL(page_is_ram); + extern int swiotlb_force; static struct kcore_list kcore_mem, kcore_vmalloc, kcore_kernel, kcore_modules, @@ -557,6 +589,7 @@ return 0; return pfn_valid(pte_pfn(*pte)); } +EXPORT_SYMBOL_GPL(kern_addr_valid); #ifdef CONFIG_SYSCTL #include <linux/sysctl.h> diff -Nur linux-2.6.9.org/drivers/block/Kconfig linux-2.6.9/drivers/block/Kconfig --- linux-2.6.9.org/drivers/block/Kconfig 2004-10-19 06:53:43.000000000 +0900 +++ linux-2.6.9/drivers/block/Kconfig 2004-11-26 20:13:17.776667428 +0900 @@ -356,6 +356,12 @@ your machine, or if you want to have a raid or loopback device bigger than 2TB. Otherwise say N. +config DISKDUMP + tristate "Disk dump support" + depends on MODULES && m + ---help--- + Disk dump support. + source "drivers/s390/block/Kconfig" endmenu diff -Nur linux-2.6.9.org/drivers/block/Makefile linux-2.6.9/drivers/block/Makefile --- linux-2.6.9.org/drivers/block/Makefile 2004-10-19 06:54:55.000000000 +0900 +++ linux-2.6.9/drivers/block/Makefile 2004-11-26 20:13:17.777643991 +0900 @@ -44,3 +44,4 @@ obj-$(CONFIG_BLK_DEV_SX8) += sx8.o obj-$(CONFIG_BLK_DEV_UB) += ub.o +obj-$(CONFIG_DISKDUMP) += diskdump.o diff -Nur linux-2.6.9.org/drivers/block/diskdump.c linux-2.6.9/drivers/block/diskdump.c --- linux-2.6.9.org/drivers/block/diskdump.c 1970-01-01 09:00:00.000000000 +0900 +++ linux-2.6.9/drivers/block/diskdump.c 2004-11-26 20:13:17.780573678 +0900 @@ -0,0 +1,1123 @@ +/* + * linux/drivers/block/diskdump.c + * + * Copyright (C) 2004 FUJITSU LIMITED + * Copyright (C) 2002 Red Hat, Inc. + * Written by Nobuhiro Tachino (nta...@jp...) + * + * Some codes were derived from netdump and copyright belongs to + * Red Hat, Inc. + */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <linux/mm.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/reboot.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/highmem.h> +#include <linux/smp_lock.h> +#include <linux/nmi.h> +#include <linux/crc32.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/seq_file.h> +#include <linux/proc_fs.h> +#include <linux/diskdump.h> +#include <asm/diskdump.h> + +#define Dbg(x, ...) pr_debug("disk_dump: " x "\n", ## __VA_ARGS__) +#define Err(x, ...) pr_err ("disk_dump: " x "\n", ## __VA_ARGS__) +#define Warn(x, ...) pr_warn ("disk_dump: " x "\n", ## __VA_ARGS__) +#define Info(x, ...) pr_info ("disk_dump: " x "\n", ## __VA_ARGS__) + +#define ROUNDUP(x, y) (((x) + ((y)-1))/(y)) + +/* 512byte sectors to blocks */ +#define SECTOR_BLOCK(s) ((s) >> (DUMP_BLOCK_SHIFT - 9)) + +/* The number of block which is used for saving format information */ +#define USER_PARAM_BLOCK 2 + +static int fallback_on_err = 1; +static int allow_risky_dumps = 1; +static unsigned int block_order = 2; +static int sample_rate = 8; +module_param_named(fallback_on_err, fallback_on_err, bool, S_IRUGO|S_IWUSR); +module_param_named(allow_risky_dumps, allow_risky_dumps, bool, S_IRUGO|S_IWUSR); +module_param_named(block_order, block_order, uint, S_IRUGO|S_IWUSR); +module_param_named(sample_rate, sample_rate, int, S_IRUGO|S_IWUSR); + +static unsigned long timestamp_1sec; +static uint32_t module_crc; +static char *scratch; +static struct disk_dump_header dump_header; +static struct disk_dump_sub_header dump_sub_header; + +/* Registered dump devices */ +static LIST_HEAD(disk_dump_devices); + +/* Registered dump types, e.g. SCSI, ... */ +static LIST_HEAD(disk_dump_types); + +static DECLARE_MUTEX(disk_dump_mutex); + +static unsigned int header_blocks; /* The size of all headers */ +static unsigned int bitmap_blocks; /* The size of bitmap header */ +static unsigned int total_ram_blocks; /* The size of memory */ +static unsigned int total_blocks; /* The sum of above */ +/* + * This is not a parameter actually, but used to pass the number of + * required blocks to userland tools + */ +module_param_named(total_blocks, total_blocks, uint, S_IRUGO); + +struct notifier_block *disk_dump_notifier_list; +EXPORT_SYMBOL_GPL(disk_dump_notifier_list); + +unsigned long volatile diskdump_base_jiffies; +void *diskdump_stack; +enum disk_dump_states disk_dump_state = DISK_DUMP_INITIAL; + +extern int panic_timeout; +extern unsigned long max_pfn; + +static asmlinkage void disk_dump(struct pt_regs *, void *); + + +#if CONFIG_SMP +static void freeze_cpu(void *dummy) +{ + unsigned int cpu = smp_processor_id(); + + dump_header.tasks[cpu] = current; + + platform_freeze_cpu(); +} +#endif + +static int lapse = 0; /* 200msec unit */ + +static inline unsigned long eta(unsigned long nr, unsigned long maxnr) +{ + unsigned long long eta; + + if (nr == 0) + nr = 1; + + eta = ((maxnr << 8) / nr) * (unsigned long long)lapse; + + return (unsigned long)(eta >> 8) - lapse; +} + +static inline void print_status(unsigned int nr, unsigned int maxnr) +{ + static char *spinner = "/|\\-"; + static unsigned long long prev_timestamp = 0; + unsigned long long timestamp; + + if (nr == 0) + nr++; + + platform_timestamp(timestamp); + + if (timestamp - prev_timestamp > (timestamp_1sec/5)) { + prev_timestamp = timestamp; + lapse++; + printk("%u/%u %lu ETA %c \r", + nr, maxnr, eta(nr, maxnr) / 5, spinner[lapse & 3]); + } +} + +static inline void clear_status(int nr, int maxnr) +{ + printk(" \r"); + lapse = 0; +} + +/* + * Checking the signature on a block. The format is as follows. + * + * 1st word = 'disk' + * 2nd word = 'dump' + * 3rd word = block number + * 4th word = ((block number + 7) * 11) & 0xffffffff + * 5th word = ((4th word + 7)* 11) & 0xffffffff + * .. + * + * Return 1 if the signature is correct, else return 0 + */ +static int check_block_signature(void *buf, unsigned int block_nr) +{ + int word_nr = PAGE_SIZE / sizeof(int); + int *words = buf; + unsigned int val; + int i; + + /* + * Block 2 is used for the area which formatter saves options like + * the sampling rate or the number of blocks. the Kernel part does not + * check this block. + */ + if (block_nr == USER_PARAM_BLOCK) + return 1; + + if (memcmp(buf, DUMP_PARTITION_SIGNATURE, sizeof(*words))) + return 0; + + val = block_nr; + for (i = 2; i < word_nr; i++) { + if (words[i] != val) + return 0; + val = (val + 7) * 11; + } + + return 1; +} + +/* + * Read one block into the dump partition + */ +static int read_blocks(struct disk_dump_partition *dump_part, unsigned int nr, + char *buf, int len) +{ + struct disk_dump_device *device = dump_part->device; + int ret; + + local_irq_disable(); + touch_nmi_watchdog(); + ret = device->ops.rw_block(dump_part, READ, nr, buf, len); + if (ret < 0) { + Err("read error on block %u", nr); + return ret; + } + return 0; +} + +static int write_blocks(struct disk_dump_partition *dump_part, unsigned int offs, char *buf, int len) +{ + struct disk_dump_device *device = dump_part->device; + int ret; + + local_irq_disable(); + touch_nmi_watchdog(); + ret = device->ops.rw_block(dump_part, WRITE, offs, buf, len); + if (ret < 0) { + Err("write error on block %u", offs); + return ret; + } + return 0; +} + +/* + * Initialize the common header + */ + +/* + * Write the common header + */ +static int write_header(struct disk_dump_partition *dump_part) +{ + memset(scratch, 0, PAGE_SIZE); + memcpy(scratch, &dump_header, sizeof(dump_header)); + + return write_blocks(dump_part, 1, scratch, 1); +} + +/* + * Check the signaures in all blocks of the dump partition + * Return 1 if the signature is correct, else return 0 + */ +static int check_dump_partition(struct disk_dump_partition *dump_part, + unsigned int partition_size) +{ + unsigned int blk; + int ret; + unsigned int chunk_blks, skips; + int i; + + if (sample_rate < 0) /* No check */ + return 1; + + /* + * If the device has limitations of transfer size, use it. + */ + chunk_blks = 1 << block_order; + if (dump_part->device->max_blocks) + chunk_blks = min(chunk_blks, dump_part->device->max_blocks); + skips = chunk_blks << sample_rate; + + lapse = 0; + for (blk = 0; blk < partition_size; blk += skips) { + unsigned int len; +redo: + len = min(chunk_blks, partition_size - blk); + if ((ret = read_blocks(dump_part, blk, scratch, len)) < 0) + return 0; + print_status(blk + 1, partition_size); + for (i = 0; i < len; i++) + if (!check_block_signature(scratch + i * DUMP_BLOCK_SIZE, blk + i)) { + Err("bad signature in block %u", blk + i); + return 0; + } + } + /* Check the end of the dump partition */ + if (blk - skips + chunk_blks < partition_size) { + blk = partition_size - chunk_blks; + goto redo; + } + clear_status(blk, partition_size); + return 1; +} + +/* + * Write memory bitmap after location of dump headers. + */ +#define PAGE_PER_BLOCK (PAGE_SIZE * 8) +#define idx_to_pfn(nr, byte, bit) (((nr) * PAGE_SIZE + (byte)) * 8 + (bit)) + +static int write_bitmap(struct disk_dump_partition *dump_part, + unsigned int bitmap_offset, unsigned int bitmap_blocks) +{ + unsigned int nr; + unsigned long pfn, next_ram_pfn; + int bit, byte; + int ret = 0; + unsigned char val; + + for (nr = 0; nr < bitmap_blocks; nr++) { + pfn = idx_to_pfn(nr, 0, 0); + next_ram_pfn = next_ram_page(pfn - 1); + + if (pfn + PAGE_PER_BLOCK <= next_ram_pfn) + memset(scratch, 0, PAGE_SIZE); + else + for (byte = 0; byte < PAGE_SIZE; byte++) { + val = 0; + for (bit = 0; bit < 8; bit++) + if (page_is_ram(idx_to_pfn(nr, byte, + bit))) + val |= (1 << bit); + scratch[byte] = (char)val; + } + if ((ret = write_blocks(dump_part, bitmap_offset + nr, + scratch, 1)) < 0) { + Err("I/O error %d on block %u", ret, bitmap_offset + nr); + break; + } + } + return ret; +} + +/* + * Write whole memory to dump partition. + * Return value is the number of writen blocks. + */ +static int write_memory(struct disk_dump_partition *dump_part, int offset, + unsigned int max_blocks_written, + unsigned int *blocks_written) +{ + char *kaddr; + unsigned int blocks = 0; + struct page *page; + unsigned long nr; + int ret = 0; + int blk_in_chunk = 0; + + for (nr = next_ram_page(ULONG_MAX); nr < max_pfn; nr = next_ram_page(nr)) { + print_status(blocks, max_blocks_written); + + + if (blocks >= max_blocks_written) { + Warn("dump device is too small. %lu pages were not saved", max_pfn - blocks); + goto out; + } + + page = pfn_to_page(nr); + if (nr != page_to_pfn(page)) { + /* page_to_pfn() is called from kmap_atomic(). + * If page->flag is broken, it specified a wrong + * zone and it causes kmap_atomic() fail. + */ + Err("Bad page. PFN %lu flags %lx\n", + nr, (unsigned long)page->flags); + memset(scratch + blk_in_chunk * PAGE_SIZE, 0, + PAGE_SIZE); + sprintf(scratch + blk_in_chunk * PAGE_SIZE, + "Bad page. PFN %lu flags %lx\n", + nr, (unsigned long)page->flags); + goto write; + } + + if (!kern_addr_valid((unsigned long)pfn_to_kaddr(nr))) { + memset(scratch + blk_in_chunk * PAGE_SIZE, 0, + PAGE_SIZE); + sprintf(scratch + blk_in_chunk * PAGE_SIZE, + "Unmapped page. PFN %lu\n", nr); + goto write; + } + + kaddr = kmap_atomic(page, KM_CRASHDUMP); + /* + * need to copy because adapter drivers use + * virt_to_bus() + */ + memcpy(scratch + blk_in_chunk * PAGE_SIZE, kaddr, PAGE_SIZE); + kunmap_atomic(kaddr, KM_CRASHDUMP); + +write: + blk_in_chunk++; + blocks++; + + if (blk_in_chunk >= (1 << block_order)) { + ret = write_blocks(dump_part, offset, scratch, + blk_in_chunk); + if (ret < 0) { + Err("I/O error %d on block %u", ret, offset); + break; + } + offset += blk_in_chunk; + blk_in_chunk = 0; + } + } + if (ret >= 0 && blk_in_chunk > 0) { + ret = write_blocks(dump_part, offset, scratch, blk_in_chunk); + if (ret < 0) + Err("I/O error %d on block %u", ret, offset); + } + +out: + clear_status(nr, max_blocks_written); + + *blocks_written = blocks; + return ret; +} + +/* + * Select most suitable dump device. sanity_check() returns the state + * of each dump device. 0 means OK, negative value means NG, and + * positive value means it maybe work. select_dump_partition() first + * try to select a sane device and if it has no sane device and + * allow_risky_dumps is set, it select one from maybe OK devices. + * + * XXX We cannot handle multiple partitions yet. + */ +static struct disk_dump_partition *select_dump_partition(void) +{ + struct disk_dump_device *dump_device; + struct disk_dump_partition *dump_part; + int sanity; + int strict_check = 1; + +redo: + /* + * Select a sane polling driver. + */ + list_for_each_entry(dump_device, &disk_dump_devices, list) { + sanity = 0; + if (dump_device->ops.sanity_check) + sanity = dump_device->ops.sanity_check(dump_device); + if (sanity < 0 || (sanity > 0 && strict_check)) + continue; + list_for_each_entry(dump_part, &dump_device->partitions, list) + return dump_part; + } + if (allow_risky_dumps && strict_check) { + strict_check = 0; + goto redo; + } + return NULL; +} + +static int dump_err = 0; /* Indicate Error state which occured in + * disk_dump(). We need to make it global + * because disk_dump() can't pass + * error state as return value. + */ + +static void freeze_other_cpus(void) +{ +#if CONFIG_SMP + int i; + + smp_call_function(freeze_cpu, NULL, 1, 0); + diskdump_mdelay(3000); + printk("CPU frozen: "); + for (i = 0; i < NR_CPUS; i++) { + if (dump_header.tasks[i] != NULL) + printk("#%d", i); + + } + printk("\n"); + printk("CPU#%d is executing diskdump.\n", smp_processor_id()); +#else + diskdump_mdelay(1000); +#endif + dump_header.tasks[smp_processor_id()] = current; +} + +static void start_disk_dump(struct pt_regs *regs) +{ + unsigned long flags; + + /* Inhibit interrupt and stop other CPUs */ + local_irq_save(flags); + preempt_disable(); + + /* + * Check the checksum of myself + */ + if (down_trylock(&disk_dump_mutex)) { + Err("down_trylock(disk_dump_mutex) failed."); + goto done; + } + + if (!check_crc_module()) { + Err("checksum error. diskdump common module may be compromised."); + goto done; + } + + disk_dump_state = DISK_DUMP_RUNNING; + + diskdump_mode = 1; + + Dbg("notify dump start."); + notifier_call_chain(&disk_dump_notifier_list, 0, NULL); + + touch_nmi_watchdog(); + freeze_other_cpus(); + + /* + * Some platforms may want to execute netdump on its own stack. + */ + platform_start_crashdump(diskdump_stack, disk_dump, regs); + +done: + /* + * If diskdump failed and fallback_on_err is set, + * We just return and leave panic to netdump. + */ + if (dump_err) { + Info("dump failed with error"); + disk_dump_state = DISK_DUMP_FAILURE; + if (fallback_on_err && dump_err) + return; + } else { + Info("dump succeeded"); + disk_dump_state = DISK_DUMP_SUCCESS; + } + + Dbg("notify panic."); + notifier_call_chain(&panic_notifier_list, 0, NULL); + + if (panic_timeout > 0) { + int i; + /* + * Delay timeout seconds before rebooting the machine. + * We can't use the "normal" timers since we just panicked.. + */ + printk(KERN_EMERG "Rebooting in %d seconds..",panic_timeout); + for (i = 0; i < panic_timeout; i++) { + touch_nmi_watchdog(); + diskdump_mdelay(1000); + } + + /* + * Should we run the reboot notifier. For the moment Im + * choosing not too. It might crash, be corrupt or do + * more harm than good for other reasons. + */ + machine_restart(NULL); + } + printk(KERN_EMERG "halt\n"); + for (;;) { + touch_nmi_watchdog(); + machine_halt(); + diskdump_mdelay(1000); + } +} + +static asmlinkage void disk_dump(struct pt_regs *regs, void *platform_arg) +{ + struct pt_regs myregs; + unsigned int max_written_blocks, written_blocks; + struct disk_dump_device *dump_device = NULL; + struct disk_dump_partition *dump_part = NULL; + int ret; + + dump_err = -EIO; + + /* + * Setup timer/tasklet + */ + dump_clear_timers(); + dump_clear_tasklet(); + dump_clear_workqueue(); + + /* Save original jiffies value */ + diskdump_base_jiffies = jiffies; + + diskdump_setup_timestamp(); + + platform_fix_regs(); + + if (list_empty(&disk_dump_devices)) { + Err("adapter driver is not registered."); + goto done; + } + + printk("start dumping\n"); + + if (!(dump_part = select_dump_partition())) { + Err("No sane dump device found"); + goto done; + } + dump_device = dump_part->device; + + /* + * Stop ongoing I/O with polling driver and make the shift to I/O mode + * for dump + */ + Dbg("do quiesce"); + if (dump_device->ops.quiesce) + if ((ret = dump_device->ops.quiesce(dump_device)) < 0) { + Err("quiesce failed. error %d", ret); + goto done; + } + + if (SECTOR_BLOCK(dump_part->nr_sects) < header_blocks + bitmap_blocks) { + Warn("dump partition is too small. Aborted"); + goto done; + } + + /* Check dump partition */ + printk("check dump partition...\n"); + if (!check_dump_partition(dump_part, total_blocks)) { + Err("check partition failed."); + goto done; + } + + /* + * Write the common header + */ + memcpy(dump_header.signature, DISK_DUMP_SIGNATURE, + sizeof(dump_header.signature)); + dump_header.utsname = system_utsname; + dump_header.timestamp = xtime; + dump_header.status = DUMP_HEADER_INCOMPLETED; + dump_header.block_size = PAGE_SIZE; + dump_header.sub_hdr_size = size_of_sub_header(); + dump_header.bitmap_blocks = bitmap_blocks; + dump_header.max_mapnr = max_pfn; + dump_header.total_ram_blocks = total_ram_blocks; + dump_header.device_blocks = SECTOR_BLOCK(dump_part->nr_sects); + dump_header.current_cpu = smp_processor_id(); + dump_header.nr_cpus = num_online_cpus(); + dump_header.written_blocks = 2; + + write_header(dump_part); + + /* + * Write the architecture dependent header + */ + Dbg("write sub header"); + if ((ret = write_sub_header()) < 0) { + Err("writing sub header failed. error %d", ret); + goto done; + } + + Dbg("writing memory bitmaps.."); + if ((ret = write_bitmap(dump_part, header_blocks, bitmap_blocks)) < 0) + goto done; + + max_written_blocks = total_ram_blocks; + if (dump_header.device_blocks < total_blocks) { + Warn("dump partition is too small. actual blocks %u. expected blocks %u. whole memory will not be saved", + dump_header.device_blocks, total_blocks); + max_written_blocks -= (total_blocks - dump_header.device_blocks); + } + + dump_header.written_blocks += dump_header.sub_hdr_size; + dump_header.written_blocks += dump_header.bitmap_blocks; + write_header(dump_part); + + printk("dumping memory..\n"); + if ((ret = write_memory(dump_part, header_blocks + bitmap_blocks, + max_written_blocks, &written_blocks)) < 0) + goto done; + + /* + * Set the number of block that is written into and write it + * into partition again. + */ + dump_header.written_blocks += written_blocks; + dump_header.status = DUMP_HEADER_COMPLETED; + write_header(dump_part); + + dump_err = 0; + +done: + Dbg("do adapter shutdown."); + if (dump_device && dump_device->ops.shutdown) + if (dump_device->ops.shutdown(dump_device)) + Err("adapter shutdown failed."); +} + +static struct disk_dump_partition *find_dump_partition(struct block_device *bdev) +{ + struct disk_dump_device *dump_device; + struct disk_dump_partition *dump_part; + + list_for_each_entry(dump_device, &disk_dump_devices, list) + list_for_each_entry(dump_part, &dump_device->partitions, list) + if (dump_part->bdev == bdev) + return dump_part; + return NULL; +} + +static struct disk_dump_device *find_dump_device(struct disk_dump_device *device) +{ + struct disk_dump_device *dump_device; + + list_for_each_entry(dump_device, &disk_dump_devices, list) + if (device->device == dump_device->device) + return dump_device; + return NULL; +} + +static void *find_real_device(struct device *dev, + struct disk_dump_type **_dump_type) +{ + void *real_device; + struct disk_dump_type *dump_type; + + list_for_each_entry(dump_type, &disk_dump_types, list) + if ((real_device = dump_type->probe(dev)) != NULL) { + *_dump_type = dump_type; + return real_device; + } + return NULL; +} + +/* + * Add dump partition structure corresponding to file to the dump device + * structure. + */ +static int add_dump_partition(struct disk_dump_device *dump_device, + struct block_device *bdev) +{ + struct disk_dump_partition *dump_part; + char buffer[BDEVNAME_SIZE]; + + if (!(dump_part = kmalloc(sizeof(*dump_part), GFP_KERNEL))) + return -ENOMEM; + + dump_part->device = dump_device; + dump_part->bdev = bdev; + + if (!bdev || !bdev->bd_part) + return -EINVAL; + dump_part->nr_sects = bdev->bd_part->nr_sects; + dump_part->start_sect = bdev->bd_part->start_sect; + + if (SECTOR_BLOCK(dump_part->nr_sects) < total_blocks) + Warn("%s is too small to save whole system memory\n", + bdevname(bdev, buffer)); + + list_add(&dump_part->list, &dump_device->partitions); + + return 0; +} + +/* + * Add dump device and partition. + * Must be called with disk_dump_mutex held. + */ +static int add_dump(struct device *dev, struct block_device *bdev) +{ + struct disk_dump_type *dump_type = NULL; + struct disk_dump_device *dump_device; + void *real_device; + int ret; + + if ((ret = blkdev_get(bdev, FMODE_READ, 0)) < 0) + return ret; + + /* Check whether this block device is already registered */ + if (find_dump_partition(bdev)) { + blkdev_put(bdev); + return -EEXIST; + } + + /* find dump_type and real device for this inode */ + if (!(real_device = find_real_device(dev, &dump_type))) { + blkdev_put(bdev); + return -ENXIO; + } + + /* Check whether this device is already registered */ + dump_device = find_dump_device(real_device); + if (dump_device == NULL) { + /* real_device is not registered. create new dump_device */ + if (!(dump_device = kmalloc(sizeof(*dump_device), GFP_KERNEL))) { + blkdev_put(bdev); + return -ENOMEM; + } + + memset(dump_device, 0, sizeof(*dump_device)); + INIT_LIST_HEAD(&dump_device->partitions); + + dump_device->dump_type = dump_type; + dump_device->device = real_device; + if ((ret = dump_type->add_device(dump_device)) < 0) { + kfree(dump_device); + blkdev_put(bdev); + return ret; + } + if (!try_module_get(dump_type->owner)) + return -EINVAL; + list_add(&dump_device->list, &disk_dump_devices); + } + + ret = add_dump_partition(dump_device, bdev); + if (ret < 0 && list_empty(&dump_device->list)) { + dump_type->remove_device(dump_device); + module_put(dump_type->owner); + list_del(&dump_device->list); + kfree(dump_device); + } + if (ret < 0) + blkdev_put(bdev); + + return ret; +} + +/* + * Remove dump partition corresponding to bdev. + * Must be called with disk_dump_mutex held. + */ +static int remove_dump(struct block_device *bdev) +{ + struct disk_dump_device *dump_device; + struct disk_dump_partition *dump_part; + struct disk_dump_type *dump_type; + + if (!(dump_part = find_dump_partition(bdev))) { + bdput(bdev); + return -ENOENT; + } + + blkdev_put(bdev); + dump_device = dump_part->device; + list_del(&dump_part->list); + kfree(dump_part); + + if (list_empty(&dump_device->partitions)) { + dump_type = dump_device->dump_type; + dump_type->remove_device(dump_device); + module_put(dump_type->owner); + list_del(&dump_device->list); + kfree(dump_device); + } + + return 0; +} + +#ifdef CONFIG_PROC_FS +static struct disk_dump_partition *dump_part_by_pos(struct seq_file *seq, + loff_t pos) +{ + struct disk_dump_device *dump_device; + struct disk_dump_partition *dump_part; + + list_for_each_entry(dump_device, &disk_dump_devices, list) { + seq->private = dump_device; + list_for_each_entry(dump_part, &dump_device->partitions, list) + if (!pos--) + return dump_part; + } + return NULL; +} + +static void *disk_dump_seq_start(struct seq_file *seq, loff_t *pos) +{ + loff_t n = *pos; + + down(&disk_dump_mutex); + + if (!n--) + return (void *)1; /* header */ + + return dump_part_by_pos(seq, n); +} + +static void *disk_dump_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct list_head *partition = v; + struct list_head *device = seq->private; + struct disk_dump_device *dump_device; + + (*pos)++; + if (v == (void *)1) + return dump_part_by_pos(seq, 0); + + dump_device = list_entry(device, struct disk_dump_device, list); + + partition = partition->next; + if (partition != &dump_device->partitions) + return partition; + + device = device->next; + seq->private = device; + if (device == &disk_dump_devices) + return NULL; + + dump_device = list_entry(device, struct disk_dump_device, list); + + return dump_device->partitions.next; +} + +static void disk_dump_seq_stop(struct seq_file *seq, void *v) +{ + up(&disk_dump_mutex); +} + +static int disk_dump_seq_show(struct seq_file *seq, void *v) +{ + struct disk_dump_partition *dump_part = v; + char buf[BDEVNAME_SIZE]; + + if (v == (void *)1) { /* header */ + seq_printf(seq, "# sample_rate: %u\n", sample_rate); + seq_printf(seq, "# block_order: %u\n", block_order); + seq_printf(seq, "# fallback_on_err: %u\n", fallback_on_err); + seq_printf(seq, "# allow_risky_dumps: %u\n", allow_risky_dumps); + seq_printf(seq, "# total_blocks: %u\n", total_blocks); + seq_printf(seq, "#\n"); + + return 0; + } + + seq_printf(seq, "%s %lu %lu\n", bdevname(dump_part->bdev, buf), + dump_part->start_sect, dump_part->nr_sects); + return 0; +} + +static struct seq_operations disk_dump_seq_ops = { + .start = disk_dump_seq_start, + .next = disk_dump_seq_next, + .stop = disk_dump_seq_stop, + .show = disk_dump_seq_show, +}; + +static int disk_dump_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &disk_dump_seq_ops); +} + +static struct file_operations disk_dump_fops = { + .owner = THIS_MODULE, + .open = disk_dump_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif + +int register_disk_dump_device(struct device *dev, struct block_device *bdev) +{ + int ret; + + down(&disk_dump_mutex); + ret = add_dump(dev, bdev); + set_crc_modules(); + up(&disk_dump_mutex); + + return ret; +} + +int unregister_disk_dump_device(struct block_device *bdev) +{ + int ret; + + down(&disk_dump_mutex); + ret = remove_dump(bdev); + set_crc_modules(); + up(&disk_dump_mutex); + + return ret; +} + +int find_disk_dump_device(struct block_device *bdev) +{ + int ret; + + down(&disk_dump_mutex); + ret = (find_dump_partition(bdev) != NULL); + up(&disk_dump_mutex); + + return ret; +} + +int register_disk_dump_type(struct disk_dump_type *dump_type) +{ + down(&disk_dump_mutex); + list_add(&dump_type->list, &disk_dump_types); + set_crc_modules(); + up(&disk_dump_mutex); + + return 0; +} + +EXPORT_SYMBOL_GPL(register_disk_dump_type); + +int unregister_disk_dump_type(struct disk_dump_type *dump_type) +{ + down(&disk_dump_mutex); + list_del(&dump_type->list); + set_crc_modules(); + up(&disk_dump_mutex); + + return 0; +} + +EXPORT_SYMBOL_GPL(unregister_disk_dump_type); + +static void compute_total_blocks(void) +{ + unsigned long nr; + + /* + * the number of block of the common header and the header + * that is depend on the architecture + * + * block 0: dump partition header + * block 1: dump header + * block 2: dump subheader + * block 3..n: memory bitmap + * block (n + 1)...: saved memory + * + * We never overwrite block 0 + */ + header_blocks = 2 + size_of_sub_header(); + + total_ram_blocks = 0; + for (nr = next_ram_page(ULONG_MAX); nr < max_pfn; nr = next_ram_page(nr)) + total_ram_blocks++; + + bitmap_blocks = ROUNDUP(max_pfn, 8 * PAGE_SIZE); + + /* + * The necessary size of area for dump is: + * 1 block for common header + * m blocks for architecture dependent header + * n blocks for memory bitmap + * and whole memory + */ + total_blocks = header_blocks + bitmap_blocks + total_ram_blocks; + + Info("total blocks required: %u (header %u + bitmap %u + memory %u)", + total_blocks, header_blocks, bitmap_blocks, total_ram_blocks); +} + +struct disk_dump_ops dump_ops = { + .add_dump = register_disk_dump_device, + .remove_dump = unregister_disk_dump_device, + .find_dump = find_disk_dump_device, +}; + +static int init_diskdump(void) +{ + unsigned long long t0; + unsigned long long t1; + struct page *page; + + if (!platform_supports_diskdump) { + Err("platform does not support diskdump."); + return -1; + } + + /* Allocate one block that is used temporally */ + do { + page = alloc_pages(GFP_KERNEL, block_order); + if (page != NULL) + break; + } while (--block_order >= 0); + if (!page) { + Err("alloc_pages failed."); + return -1; + } + scratch = page_address(page); + Info("Maximum block size: %lu", PAGE_SIZE << block_order); + + if (diskdump_register_hook(start_disk_dump)) { + Err("failed to register hooks."); + return -1; + } + + if (diskdump_register_ops(&dump_ops)) { + Err("failed to register ops."); + return -1; + } + + compute_total_blocks(); + + platform_timestamp(t0); + diskdump_mdelay(1); + platform_timestamp(t1); + timestamp_1sec = (unsigned long)(t1 - t0) * 1000; + + /* + * Allocate a separate stack for diskdump. + */ + platform_init_stack(&diskdump_stack); + + down(&disk_dump_mutex); + set_crc_modules(); + up(&disk_dump_mutex); + +#ifdef CONFIG_PROC_FS + { + struct proc_dir_entry *p; + + p = create_proc_entry("diskdump", S_IRUGO|S_IWUSR, NULL); + if (p) + p->proc_fops = &disk_dump_fops; + } +#endif + + return 0; +} + +static void cleanup_diskdump(void) +{ + Info("shut down."); + diskdump_unregister_hook(); + diskdump_unregister_ops(); + platform_cleanup_stack(diskdump_stack); + free_pages((unsigned long)scratch, block_order); +#ifdef CONFIG_PROC_FS + remove_proc_entry("diskdump", NULL); +#endif +} + +module_init(init_diskdump); +module_exit(cleanup_diskdump); + +MODULE_LICENSE("GPL"); diff -Nur linux-2.6.9.org/drivers/char/sysrq.c linux-2.6.9/drivers/char/sysrq.c --- linux-2.6.9.org/drivers/char/sysrq.c 2004-10-19 06:55:06.000000000 +0900 +++ linux-2.6.9/drivers/char/sysrq.c 2004-11-26 20:13:17.780573678 +0900 @@ -107,6 +107,17 @@ .action_msg = "Resetting", }; +/* crash sysrq handler */ +static void sysrq_handle_crash(int key, struct pt_regs *pt_regs, + struct tty_struct *tty) { + *( (char *) 0) = 0; +} +static struct sysrq_key_op sysrq_crash_op = { + .handler = sysrq_handle_crash, + .help_msg = "Crash", + .action_msg = "Crashing the kernel by request", +}; + static void sysrq_handle_sync(int key, struct pt_regs *pt_regs, struct tty_struct *tty) { @@ -235,7 +246,7 @@ it is handled specially on the sparc and will never arrive */ /* b */ &sysrq_reboot_op, -/* c */ NULL, +/* c */ &sysrq_crash_op, /* d */ NULL, /* e */ &sysrq_term_op, /* f */ NULL, diff -Nur linux-2.6.9.org/include/asm-generic/crashdump.h linux-2.6.9/include/asm-generic/crashdump.h --- linux-2.6.9.org/include/asm-generic/crashdump.h 1970-01-01 09:00:00.000000000 +0900 +++ linux-2.6.9/include/asm-generic/crashdump.h 2004-11-26 20:13:17.780573678 +0900 @@ -0,0 +1,47 @@ +#ifndef _ASM_GENERIC_CRASHDUMP_H_ +#define _ASM_GENERIC_CRASHDUMP_H_ + +/* + * linux/include/asm-generic/crashdump.h + * + * Copyright (c) 2003, 2004 Red Hat, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifdef __KERNEL__ + +#define platform_timestamp(x) do { (x) = 0; } while (0) + +#define platform_fix_regs() do { } while (0) +#define platform_init_stack(stackptr) do { } while (0) +#define platform_cleanup_stack(stackptr) do { } while (0) +#define platform_start_crashdump(stackptr,dumpfunc,regs) (0) + +#undef ELF_CORE_COPY_REGS +#define ELF_CORE_COPY_REGS(x, y) do { struct pt_regs *z; z = (y); } while (0) + +#define show_mem() do {} while (0) + +#define show_state() do {} while (0) + +#define show_regs(x) do { struct pt_regs *z; z = (x); } while (0) + +#undef KM_CRASHDUMP +#define KM_CRASHDUMP 0 + +#endif /* __KERNEL__ */ + +#endif /* _ASM_GENERIC_CRASHDUMP_H */ diff -Nur linux-2.6.9.org/include/asm-generic/diskdump.h linux-2.6.9/include/asm-generic/diskdump.h --- linux-2.6.9.org/include/asm-generic/diskdump.h 1970-01-01 09:00:00.000000000 +0900 +++ linux-2.6.9/include/asm-generic/diskdump.h 2004-11-26 20:13:17.781550241 +0900 @@ -0,0 +1,13 @@ +#ifndef _ASM_GENERIC_DISKDUMP_H_ +#define _ASM_GENERIC_DISKDUMP_H_ + +#include <asm-generic/crashdump.h> + +const static int platform_supports_diskdump = 0; + +struct disk_dump_sub_header {}; + +#define size_of_sub_header() 1 +#define write_sub_header() 1 + +#endif /* _ASM_GENERIC_DISKDUMP_H */ diff -Nur linux-2.6.9.org/include/asm-i386/crashdump.h linux-2.6.9/include/asm-i386/crashdump.h --- linux-2.6.9.org/include/asm-i386/crashdump.h 1970-01-01 09:00:00.000000000 +0900 +++ linux-2.6.9/include/asm-i386/crashdump.h 2004-11-26 20:13:17.781550241 +0900 @@ -0,0 +1,113 @@ +#ifndef _ASM_I386_CRASHDUMP_H +#define _ASM_I386_CRASHDUMP_H + +/* + * linux/include/asm-i386/crashdump.h + * + * Copyright (c) 2003, 2004 Red Hat, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifdef __KERNEL__ + +#include <asm/irq.h> + +extern int page_is_ram (unsigned long); +extern unsigned long next_ram_page (unsigned long); + +#define platform_timestamp(x) rdtscll(x) + +#define platform_fix_regs() \ +{ \ + unsigned long esp; \ + unsigned short ss; \ + esp = (unsigned long) ((char *)regs + sizeof (struct pt_regs)); \ + ss = __KERNEL_DS; \ + if (regs->xcs & 3) { \ + esp = regs->esp; \ + ss = regs->xss & 0xffff; \ + } \ + myregs = *regs; \ + myregs.esp = esp; \ + myregs.xss = (myregs.xss & 0xffff0000) | ss; \ +}; + +static inline void platform_init_stack(void **stackptr) +{ +#ifdef CONFIG_4KSTACKS + *stackptr = (void *)kmalloc(sizeof(union irq_ctx), GFP_KERNEL); + if (*stackptr) + memset(*stackptr, 0, sizeof(union irq_ctx)); + else + printk(KERN_WARNING + "crashdump: unable to allocate separate stack\n"); +#endif +} + +typedef asmlinkage void (*crashdump_func_t)(struct pt_regs *, void *); + +static inline void platform_start_crashdump(void *stackptr, + crashdump_func_t dumpfunc, + struct pt_regs *regs) +{ +#ifdef CONFIG_4KSTACKS + u32 *dsp; + union irq_ctx * curctx; + union irq_ctx * dumpctx; + + if (!stackptr) + dumpfunc(regs, NULL); + else { + curctx = (union irq_ctx *) current_thread_info(); + dumpctx = (union irq_ctx *) stackptr; + + /* build the stack frame on the IRQ stack */ + dsp = (u32*) ((char*)dumpctx + sizeof(*dumpctx)); + dumpctx->tinfo.task = curctx->tinfo.task; + dumpctx->tinfo.previous_esp = current_stack_pointer(); + + *--dsp = (u32) NULL; + *--dsp = (u32) regs; + + asm volatile( + " xchgl %%ebx,%%esp \n" + " call *%%eax \n" + " xchgl %%ebx,%%esp \n" + : : "a"(dumpfunc), "b"(dsp) + : "memory", "cc", "edx", "ecx" + ); + } +#else + dumpfunc(regs, NULL); +#endif +} + +#define platform_cleanup_stack(stackptr) \ +do { \ + if (stackptr) \ + kfree(stackptr); \ +} while (0) + +#define platform_freeze_cpu() \ +{ \ + for (;;) local_irq_disable(); \ +} + + +#endif /* __KERNEL__ */ + +#endif /* _ASM_I386_CRASHDUMP_H */ diff -Nur linux-2.6.9.org/include/asm-i386/diskdump.h linux-2.6.9/include/asm-i386/diskdump.h --- linux-2.6.9.org/include/asm-i386/diskdump.h 1970-01-01 09:00:00.000000000 +0900 +++ linux-2.6.9/include/asm-i386/diskdump.h 2004-11-26 20:13:17.781550241 +0900 @@ -0,0 +1,55 @@ +#ifndef _ASM_I386_DISKDUMP_H +#define _ASM_I386_DISKDUMP_H + +/* + * linux/include/asm-i386/diskdump.h + * + * Copyright (c) 2004 FUJITSU LIMITED + * Copyright (c) 2003 Red Hat, Inc. All rights reserved. + */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifdef __KERNEL__ + +#include <linux/elf.h> +#include <asm/crashdump.h> + +const static int platform_supports_diskdump = 1; + +struct disk_dump_sub_header { + elf_gregset_t elf_regs; +}; + +#define size_of_sub_header() ((sizeof(struct disk_dump_sub_header) + PAGE_SIZE - 1) / DUMP_BLOCK_SIZE) + +#define write_sub_header() \ +({ \ + int ret; \ + \ + ELF_CORE_COPY_REGS(dump_sub_header.elf_regs, (&myregs)); \ + clear_page(scratch); \ + memcpy(scratch, &dump_sub_header, sizeof(dump_sub_header)); \ + \ + if ((ret = write_blocks(dump_part, 2, scratch, 1)) >= 0) \ + ret = 1; /* size of sub header in page */; \ + ret; \ +}) + +#endif /* __KERNEL__ */ + +#endif /* _ASM_I386_DISKDUMP_H */ diff -Nur linux-2.6.9.org/include/asm-i386/kmap_types.h linux-2.6.9/include/asm-i386/kmap_types.h --- linux-2.6.9.org/include/asm-i386/kmap_types.h 2004-10-19 06:54:40.000000000 +0900 +++ linux-2.6.9/include/asm-i386/kmap_types.h 2004-11-26 20:13:17.782526803 +0900 @@ -23,7 +23,8 @@ D(10) KM_IRQ1, D(11) KM_SOFTIRQ0, D(12) KM_SOFTIRQ1, -D(13) KM_TYPE_NR +D(13) KM_CRASHDUMP, +D(14) KM_TYPE_NR }; #undef D diff -Nur linux-2.6.9.org/include/asm-ia64/crashdump.h linux-2.6.9/include/asm-ia64/crashdump.h --- linux-2.6.9.org/include/asm-ia64/crashdump.h 1970-01-01 09:00:00.000000000 +0900 +++ linux-2.6.9/include/asm-ia64/crashdump.h 2004-11-26 20:13:17.782526803 +0900 @@ -0,0 +1,76 @@ +#ifndef _ASM_IA64_CRASHDUMP_H +#define _ASM_IA64_CRASHDUMP_H + +/* + * linux/include/asm-ia64/crashdump.h + * + * Copyright (c) 2004 FUJITSU LIMITED + * Copyright (c) 2003 Red Hat, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifdef __KERNEL__ + +#include <linux/elf.h> +#include <asm/unwind.h> +#include <asm/ptrace.h> + +extern void ia64_freeze_cpu(struct unw_frame_info *, void *arg); +extern void ia64_start_dump(struct unw_frame_info *, void *arg); +extern int page_is_ram(unsigned long); +extern unsigned long next_ram_page(unsigned long); + +#define platform_timestamp(x) ({ x = ia64_get_itc(); }) + +#define platform_fix_regs() \ +{ \ + struct unw_frame_info *info = platform_arg; \ + \ + current->thread.ksp = (__u64)info->sw - 16; \ + myregs = *regs; \ +} + +#define platform_freeze_cpu() \ +{ \ + unw_init_running(ia64_freeze_cpu, NULL); \ +} + +#define platform_init_stack(stackptr) do { } while (0) +#define platform_cleanup_stack(stackptr) do { } while (0) + +typedef asmlinkage void (*crashdump_func_t)(struct pt_regs *, void *); + +/* Container to hold dump hander information */ +struct dump_call_param { + crashdump_func_t func; + struct pt_regs *regs; +}; + +static inline void platform_start_crashdump(void *stackptr, + crashdump_func_t dumpfunc, + struct pt_regs *regs) +{ + struct dump_call_param param; + + param.func = dumpfunc; + param.regs = regs; + unw_init_running(ia64_start_dump, ¶m); +} + +#endif /* __KERNEL__ */ + +#endif /* _ASM_IA64_CRASHDUMP_H */ diff -Nur linux-2.6.9.org/include/asm-ia64/diskdump.h linux-2.6.9/include/asm-ia64/diskdump.h --- linux-2.6.9.org/include/asm-ia64/diskdump.h 1970-01-01 09:00:00.000000000 +0900 +++ linux-2.6.9/include/asm-ia64/diskdump.h 2004-11-26 20:13:17.782526803 +0900 @@ -0,0 +1,58 @@ +#ifndef _ASM_IA64_DISKDUMP_H +#define _ASM_IA64_DISKDUMP_H + +/* + * linux/include/asm-ia64/diskdump.h + * + * Copyright (c) 2004 FUJITSU LIMITED + * Copyright (c) 2003 Red Hat, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifdef __KERNEL__ + +#include <linux/elf.h> +#include <asm/crashdump.h> +#include <asm/unwind.h> + +extern void ia64_do_copy_regs(struct unw_frame_info *, void *arg); + +const static int platform_supports_diskdump = 1; + +struct disk_dump_sub_header { + elf_gregset_t elf_regs; +}; + +#define size_of_sub_header() ((sizeof(struct disk_dump_sub_header) + PAGE_SIZE - 1) / DUMP_BLOCK_SIZE) + +#define write_sub_header() \ +({ \ + int ret; \ + struct unw_frame_info *info = platform_arg; \ + \ + ia64_do_copy_regs(info, &dump_sub_header.elf_regs); \ + clear_page(scratch); \ + memcpy(scratch, &dump_sub_header, sizeof(dump_sub_header)); \ + \ + if ((ret = write_blocks(dump_part, 2, scratch, 1)) >= 0) \ + ret = 1; /* size of sub header in page */; \ + ret; \ +}) + +#endif /* __KERNEL__ */ + +#endif /* _ASM_IA64_DISKDUMP_H */ diff -Nur linux-2.6.9.org/include/asm-ppc/diskdump.h linux-2.6.9/include/asm-ppc/diskdump.h --- linux-2.6.9.org/include/asm-ppc/diskdump.h 1970-01-01 09:00:00.000000000 +0900 +++ linux-2.6.9/include/asm-ppc/diskdump.h 2004-11-26 20:13:17.783503366 +0900 @@ -0,0 +1,6 @@ +#ifndef _ASM_PPC_DISKDUMP_H_ +#define _ASM_PPC_DISKDUMP_H_ + +#include <asm-generic/diskdump.h> + +#endif /* _ASM_PPC_DISKDUMP_H_ */ diff -Nur linux-2.6.9.org/include/asm-ppc64/crashdump.h linux-2.6.9/include/asm-ppc64/crashdump.h --- linux-2.6.9.org/include/asm-ppc64/crashdump.h 1970-01-01 09:00:00.000000000 +0900 +++ linux-2.6.9/include/asm-ppc64/crashdump.h 2004-11-26 20:13:17.783503366 +0900 @@ -0,0 +1,61 @@ +#ifndef _ASM_PPC64_CRASHDUMP_H +#define _ASM_PPC64_CRASHDUMP_H + +/* + * linux/include/asm-ppc64/crashdump.h + * + * Copyright (c) 2003, 2004 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004 IBM Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifdef __KERNEL__ + +#include <asm/time.h> + +extern int page_is_ram (unsigned long); +extern unsigned long next_ram_page (unsigned long); + +#define platform_timestamp(x) (x = get_tb()) + +#define platform_fix_regs() \ +{ \ + memcpy(&myregs, regs, sizeof(struct pt_regs)); \ +}; + +#define platform_init_stack(stackptr) do { } while (0) +#define platform_cleanup_stack(stackptr) do { } while (0) + +typedef asmlinkage void (*crashdump_func_t)(struct pt_regs *, void *); + +static inline void platform_start_crashdump(void *stackptr, + crashdump_func_t dumpfunc, + struct pt_regs *regs) +{ + dumpfunc(regs, NULL); +} + +#define platform_freeze_cpu() \ +{ \ + current->thread.ksp = __get_SP(); \ + for (;;) local_irq_disable(); \ +} + + +#endif /* __KERNEL__ */ + +#endif /* _ASM_PPC64_CRASHDUMP_H */ diff -Nur linux-2.6.9.org/include/asm-ppc64/diskdump.h linux-2.6.9/include/asm-ppc64/diskdump.h --- linux-2.6.9.org/include/asm-ppc64/diskdump.h 1970-01-01 09:00:00.000000000 +0900 +++ linux-2.6.9/include/asm-ppc64/diskdump.h 2004-11-26 20:13:17.783503366 +0900 @@ -0,0 +1,55 @@ +#ifndef _ASM_PPC64_DISKDUMP_H_ +#define _ASM_PPC64_DISKDUMP_H_ + +/* + * linux/include/asm-ppc64/diskdump.h + * + * Copyright (c) 2004 FUJITSU LIMITED + * Copyright (c) 2003 Red Hat, Inc. All rights reserved. + */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifdef __KERNEL__ + +#include <linux/elf.h> +#include <asm/crashdump.h> + +const static int platform_suppor... [truncated message content] |