From: Itsuro O. <od...@us...> - 2006-02-14 06:14:04
|
Update of /cvsroot/mkdump/minik/3.0/2.6/kernel In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv23324/kernel Added Files: Makefile Makefile.base minik_dump.c mklds Log Message: first registration for mkdump v3.0 minik-module --- NEW FILE: minik_dump.c --- /* * kernel/minik_dump.c * * $Id: minik_dump.c,v 1.1 2006/02/14 06:13:53 odaodab Exp $ * * Portions Copyright (C) 2004-2005 NTT DATA CORPORATION. * Portions Copyright (C) 2004-2005 VA Linux Systems Japan K.K. * * This file is part of Mkdump. * * Mkdump 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 (version 2 of the License). * * Mkdump 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 Mkdump; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <linux/config.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/gfp.h> #include <linux/delay.h> #include <linux/reboot.h> #include <linux/blkdev.h> #include <linux/bootmem.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <asm/tlbflush.h> #include <asm/setup.h> MODULE_DESCRIPTION("Mini Kernel Dump"); MODULE_LICENSE("GPL"); #define MAX_MEM_SEG 62 /* struct mem_seg <= 4KB (1 page) */ #define MAX_RESERVE_SEG 32 struct mem_seg_list { u64 seg_start_pfn; u64 seg_size_pfn; }; struct mem_seg { u32 page_size; u32 seg_num; struct mem_seg_list seg_list[MAX_MEM_SEG]; }; static int dump_dev = 0; static unsigned long dump_pfn = 0; #define MAX_DUMP_DELAY 30 static int dump_delay = 0; #define MKDUMP_REBOOT 0 #define MKDUMP_HALT 1 #define MKDUMP_POWER_OFF 2 #define MKDUMP_NO_REBOOT 3 static int mkdump_reboot = MKDUMP_REBOOT; void do_dump(void); static int parse_mkdump_params(char *param, char *val) { if (!strcmp(param, "dump_dev")) { get_option(&val, &dump_dev); } else if (!strcmp(param, "dump_pfn")) { dump_pfn = simple_strtoul(val, NULL, 0); } else if (!strcmp(param, "mkdump_reboot")) { if (!strcmp(val, "halt")) { mkdump_reboot = MKDUMP_HALT; } else if (!strcmp(val, "poweroff")) { mkdump_reboot = MKDUMP_POWER_OFF; } else if (!strcmp(val, "no")) { mkdump_reboot = MKDUMP_NO_REBOOT; } } else if (!strcmp(param, "dump_delay")) { get_option(&val, &dump_delay); if (dump_delay > MAX_DUMP_DELAY) { dump_delay = MAX_DUMP_DELAY; } } return 0; } static int mkdump_init(void) { char tmp_cmdline[COMMAND_LINE_SIZE]; strlcpy(tmp_cmdline, saved_command_line, COMMAND_LINE_SIZE); parse_args("mkdump", tmp_cmdline, NULL, 0, parse_mkdump_params); if (dump_dev == 0) { printk("mkdump: no dump_dev defined. skip dump.\n"); return 0; } do_dump(); return 0; } static void mkdump_exit(void) { /* never called */ } module_init(mkdump_init); module_exit(mkdump_exit); /* * I/O stuff * * Mini dump kernel does not use filesystem layer but uses bio layer directly. * So the dump device must be a block device. */ static struct semaphore io_buff_sem; static int io_buff_io_err = 0; #define MINI_DUMP_BUFF_NUM 32 struct io_buff { struct io_buff *next; struct page *page; unsigned long pfn; }; static struct io_buff io_buff_array[MINI_DUMP_BUFF_NUM]; static struct io_buff *io_buff_head; static DEFINE_SPINLOCK(io_buff_lock); static struct block_device *bdev; static struct io_buff *get_io_buff(void) { struct io_buff *io_buff; unsigned long flags; down(&io_buff_sem); spin_lock_irqsave(&io_buff_lock, flags); io_buff = io_buff_head; io_buff_head = io_buff->next; spin_unlock_irqrestore(&io_buff_lock, flags); return io_buff; } static void put_io_buff(struct io_buff *io_buff) { unsigned long flags; spin_lock_irqsave(&io_buff_lock, flags); io_buff->next = io_buff_head; io_buff_head = io_buff; spin_unlock_irqrestore(&io_buff_lock, flags); up(&io_buff_sem); } static int mkdump_io_init(void) { int err; int i; bdev = bdget(dump_dev); if (bdev == NULL) { printk("mkdump: can't bdget\n"); return -1; } err = blkdev_get(bdev, 0, O_RDWR); if (err != 0) { printk("mkdump: blkdev_get error %d\n", err); return -1; } sema_init(&io_buff_sem, 0); for (i = 0; i < MINI_DUMP_BUFF_NUM; i++) { if ((io_buff_array[i].page = alloc_pages(GFP_KERNEL, 0)) == NULL) { printk("mkdump: alloc_pages error\n"); return -1; } put_io_buff(&io_buff_array[i]); } return 0; } static int mkdump_end_io(struct bio *bio, unsigned int bytes_done, int err) { struct io_buff *io_buff = bio->bi_private; if (err != 0) { printk("mkdump: I/O error %d. memory pfn = %lu\n", err, io_buff->pfn); io_buff_io_err = 1; } bio_put(bio); put_io_buff(io_buff); return 0; } #ifdef CONFIG_x86_64 static void set_pte_pfn(unsigned long vaddr, unsigned long pfn, pgprot_t prot) { pgd_t *pgd; pud_t *pud; pmd_t *pmd; pte_t *pte; pgd = pgd_offset_k(vaddr); if (pgd_none(*pgd)) { BUG(); return; } pud = pud_offset(pgd, vaddr); if (pud_none(*pud)) { BUG(); return; } pmd = pmd_offset(pud, vaddr); if (pmd_none(*pmd)) { BUG(); return; } pte = pte_offset_kernel(pmd, vaddr); set_pte(pte, pfn_pte(pfn, prot)); __flush_tlb_one(vaddr); } #endif static void *pfn_to_vaddr(unsigned long pfn) { void *vaddr = (void *)fix_to_virt(0); #ifdef CONFIG_x86_64 set_pte_pfn((unsigned long)vaddr, pfn, PAGE_READONLY); #else __set_fixmap(0, pfn << PAGE_SHIFT, PAGE_READONLY); #endif return vaddr; } static void mkdump_write(unsigned long pfn) { static unsigned long disk_page = 0; struct bio *bio; struct io_buff *io_buff; io_buff = get_io_buff(); memcpy(page_address(io_buff->page), pfn_to_vaddr(pfn), PAGE_SIZE); io_buff->pfn = pfn; bio = bio_alloc(GFP_NOIO, 1); /* no error ? */ bio->bi_bdev = bdev; bio->bi_vcnt = 1; bio->bi_idx = 0; bio->bi_end_io = mkdump_end_io; bio->bi_private = io_buff; bio->bi_sector = ((unsigned long long)disk_page << PAGE_SHIFT) >> 9; /* XXX */ bio->bi_size = PAGE_SIZE; bio->bi_io_vec[0].bv_page = io_buff->page; bio->bi_io_vec[0].bv_len = PAGE_SIZE; bio->bi_io_vec[0].bv_offset = 0; submit_bio(WRITE, bio); disk_page++; } static void mkdump_io_complete(void) { int i; for (i = 0; i < MINI_DUMP_BUFF_NUM; i++) { down(&io_buff_sem); } } static struct mem_seg mem_seg; static struct mem_seg reserve_seg; unsigned long convert_pfn(unsigned long pfn) { static int i = 0; static unsigned long vpfn = 0; for (; i < reserve_seg.seg_num; i++) { if (pfn < vpfn + reserve_seg.seg_list[i].seg_size_pfn) { return reserve_seg.seg_list[i].seg_start_pfn + pfn - vpfn; } vpfn += reserve_seg.seg_list[i].seg_size_pfn; } return pfn; } void do_dump(void) { int i; if (dump_pfn <= 0) { printk("mkdump: Invalid dump_pfn==0x%lx !\n", dump_pfn); goto reboot; } if (dump_delay > 0) { set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(dump_delay * HZ); } memcpy(&mem_seg, pfn_to_vaddr(dump_pfn + 1), sizeof(struct mem_seg)); if (mem_seg.seg_num == 0 || mem_seg.seg_num > MAX_MEM_SEG || mem_seg.page_size != PAGE_SIZE) { printk("mkdump: Invalid 'mem_seg' of dump_pfn!\n"); goto reboot; } memcpy(&reserve_seg, pfn_to_vaddr(dump_pfn + 2), sizeof(struct mem_seg)); if (reserve_seg.seg_num == 0 || reserve_seg.seg_num > MAX_RESERVE_SEG || reserve_seg.page_size != PAGE_SIZE) { printk("mkdump: Invalid 'reserve_seg' of dump_pfn!\n"); goto reboot; } if (mkdump_io_init() < 0) { goto reboot; } printk("Dump start. dump device = 0x%lx\n", (unsigned long)dump_dev); mkdump_write(dump_pfn); /* at first write dump header & memory segment list */ mkdump_write(dump_pfn + 1); /* 'mem_seg' memory map. */ /* dump real pages */ for (i = 0; i < mem_seg.seg_num; i++) { struct mem_seg_list *list = &mem_seg.seg_list[i]; unsigned long pfn; printk(" - Dumping pfn 0x%lx - 0x%lx (%ld pages) ", (unsigned long)(list->seg_start_pfn), (unsigned long)(list->seg_start_pfn + list->seg_size_pfn - 1), (unsigned long)(list->seg_size_pfn)); for (pfn = list->seg_start_pfn; pfn < list->seg_start_pfn + list->seg_size_pfn; pfn++) { if (io_buff_io_err) { goto reboot; } mkdump_write(convert_pfn(pfn)); if (pfn % 1024 == 0) printk("."); } printk("\n"); } mkdump_write(dump_pfn); /* at last write dump header once more */ mkdump_io_complete(); /* wait all I/Os completion */ printk("Dump done.\n"); reboot: switch (mkdump_reboot) { case MKDUMP_NO_REBOOT: return; case MKDUMP_REBOOT: printk("Preparing for the system reboot...\n"); mdelay(3000); system_state = SYSTEM_RESTART; device_shutdown(); printk("System reboot.\n"); mdelay(2000); /* Show the messages above for a while. */ machine_restart(NULL); break; case MKDUMP_HALT: printk("Preparing for the system halt...\n"); mdelay(3000); system_state = SYSTEM_HALT; device_shutdown(); printk("System halted.\n"); machine_halt(); break; case MKDUMP_POWER_OFF: printk("Preparing for the power down...\n"); mdelay(3000); system_state = SYSTEM_POWER_OFF; device_shutdown(); printk("Power down.\n"); mdelay(2000); /* Show the messages above for a while. */ machine_power_off(); break; } while (1) { cpu_relax(); } } #define IMPORT_SYMBOL(symbol) IMPORT_SYMBOL(parse_args); IMPORT_SYMBOL(__set_fixmap); IMPORT_SYMBOL(saved_command_line); IMPORT_SYMBOL(device_shutdown); --- NEW FILE: Makefile --- # # Makefile # # Portions Copyright 2004 NTT DATA CORPORATION. # Portions Copyright 2004 VA Linux Systems Japan K.K. # ARCH = i386 KMODSRC := mkdump.c KMODOBJ := $(KMODSRC:.c=.o) ifeq ($(ARCH),i386) KARCHOBJ := endif ifeq ($(ARCH),x86_64) KARCHOBJ := endif EXTRA_LDFLAGS := -T $(PWD)/sym.lds mkdump-objs:= minik_dump.o $(KARCHOBJ) KMOD_KO := $(KMODOBJ:.o=.ko) ifneq ($(KERNELRELEASE),) obj-m := $(KMODOBJ) else include Makefile.base endif --- NEW FILE: mklds --- #!/bin/bash SYSTEMMAP=$1 IMPORT_FILE=$2 SYMS=`grep '^IMPORT_SYMBOL' $IMPORT_FILE | sed -r 's/^IMPORT_SYMBOL\((\w+)\)\;/\1/'` for i in $SYMS do ADDR=`cat $SYSTEMMAP | awk '$3=="'$i'"{ print $1 }'` echo "$i = 0x$ADDR ;" done --- NEW FILE: Makefile.base --- # # Makefile.base # # Portions Copyright 2004 NTT DATA CORPORATION. # Portions Copyright 2004 VA Linux Systems Japan K.K. # CFLAGS= CC= gcc LD= ld KVER= 2.6.12small KDIR= /home/src/linux-2.6.12-small #KDIR= /home/src/linux-2.6.12-mkexec-test SBINDIR= /usr/sbin KMODDIR= /lib/modules/2.6.12small ARCHDIR= arch/$(ARCH)/kernel PWD= $(shell pwd) all: build build: sym.lds kmodules kmodules: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules sym.lds: ./mklds $(KDIR)/System.map minik_dump.c > sym.lds install: kmodules [ -d $(KMODDIR)/kernel/$(ARCHDIR) ] || \ mkdir -p $(KMODDIR)/kernel/$(ARCHDIR) for p in $(KMOD_KO); do \ install -c -m 644 $$p $(KMODDIR)/kernel/$(ARCHDIR)/$$p; \ done uninstall: for p in $(KMOD_KO); do \ rm -f $(KMODDIR)/kernel/$(ARCHDIR)/$$p; \ done clean : -rm -rf $(KMOD_KO) *.o .?*.cmd ?*.mod.c core .tmp_versions ../$(ARCHDIR)/*.o ../$(ARCHDIR)/.?*.cmd ../$(ARCHDIR)/?*.mod.c sym.lds |