From: Itsuro O. <od...@us...> - 2006-02-14 05:30:36
|
Update of /cvsroot/mkdump/mkexec/3.0/2.6/kernel In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv12378/3.0/2.6/kernel Added Files: Makefile Makefile.24 Makefile.26 Makefile.base minik_dump.c mkexec_main.c mklds Log Message: register for 3.0: based on 2.1 mkexec-2_0-linux-2_6-2_r --- NEW FILE: Makefile.26 --- # # kernel/Makefile.26 # # 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 # #ARCH = i386 KMODSRC := mkexec.c KMODOBJ := $(KMODSRC:.c=.o) ifeq ($(ARCH),i386) KARCHOBJ := ../arch/i386/kernel/machine_mkexec.o ../arch/i386/kernel/mkexec-vmlinux.o ../arch/i386/kernel/minik_dump.o ../arch/i386/kernel/start_new_kernel.o ../arch/i386/kernel/x86-setup-32.o endif ifeq ($(ARCH),x86_64) KARCHOBJ := ../arch/x86_64/kernel/machine_mkexec.o ../arch/x86_64/kernel/mkexec-vmlinux.o ../arch/x86_64/kernel/minik_dump.o ../arch/x86_64/kernel/start_new_kernel.o ../arch/x86_64/kernel/x86_64-setup-32.o endif EXTRA_LDFLAGS := -T $(PWD)/sym.lds mkexec-objs:= mkexec_main.o minik_dump.o $(KARCHOBJ) KMOD_KO := $(KMODOBJ:.o=.ko) ifneq ($(KERNELRELEASE),) obj-m := $(KMODOBJ) else include Makefile.base endif --- NEW FILE: mklds --- #!/bin/bash # # kernel/mklds # # 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 # 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.24 --- # # kernel/Makefile.24 # # 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 # # Echo Make Variable. Use via: make emv=CFLAGS ifneq ($(emv),) emv: @echo $($(emv)) endif # We are not interedted in failed products .DELETE_ON_ERROR: IFLAGS += -I../include #ARCH = i386 #ARCH = x86_64 ifeq ($(ARCH),i386) KARCHOBJ := \ ../arch/i386/kernel/machine_mkexec.o \ ../arch/i386/kernel/mkexec-vmlinux.o \ ../arch/i386/kernel/minik_dump.o \ ../arch/i386/kernel/start_new_kernel.o \ ../arch/i386/kernel/x86-setup-32.o endif ifeq ($(ARCH),x86_64) KARCHOBJ := \ ../arch/x86_64/kernel/machine_mkexec.o \ ../arch/x86_64/kernel/mkexec-vmlinux.o \ ../arch/x86_64/kernel/minik_dump.o \ ../arch/x86_64/kernel/start_new_kernel.o \ ../arch/x86_64/kernel/x86_64-setup-32.o endif KM_OBJS = mkexec_main.o minik_dump.o $(KARCHOBJ) mkexec-objs := $(KM_OBJS) # TOPDIR := /usr/src/linux-2.4 #include Make.local ifeq ($(TOPDIR),) $(error TOPDIR must be specified) endif KMODOBJ = mkexec.o #export TOPDIR SOURCEDIR = $(TOPDIR) PWD := $(shell /bin/pwd) EXTRA_CFLAGS += $(CPPFLAGS) $(IFLAGS) $(MDFLAGS) # Add TOPDIR/include to 'standard' to decrease dependencies output EXTRA_CFLAGS += -isystem $(TOPDIR)/include OBJDIR=. # obj-m is a keyword for Linux Rules.make obj-m = $(KMODOBJ) DEPS = $(patsubst %.o, %.d, $(obj-m)) default: all # In Rules.make there is a tricky $(shell ... pathdown) function # that may break, are caused ugly warning, if TOPDIR is not # defined as environment variable. # This is the reason of artifical recursion that we make here. ifeq ($(MK_RECURSE),) # Any nicer way ? # THIS_MAKEFILE = Makefile.24 THIS_MAKEFILE = Makefile all install: $(MAKE) -w -f $(THIS_MAKEFILE) MK_RECURSE=1 EXTRA_CFLAGS="$(EXTRA_CFLAGS)" $@ else # Set CONFIG_SHELL for the sake of Rules.make's MOD_DESTDIR definition CONFIG_SHELL ?= /bin/bash include $(TOPDIR)/Rules.make all: $(MAKE) -w -C $(TOPDIR) modules SUBDIRS=$(PWD) # Explicitly empty-set MOD_DESTDIR to avoid Rules.make's crappy pathdown.sh install: $(MAKE) -w -C $(TOPDIR) modules_install SUBDIRS=$(PWD) \ INSTALL_MOD_PATH=$(INSTALL_MOD_PATH) \ MOD_DESTDIR= endif $(KMODOBJ): $(KM_OBJS) sym.lds $(LD) -r -o $@ $(KM_OBJS) -T sym.lds sym.lds: ../include/asm/mkexec_import.h ./mklds $(TOPDIR)/System.map ../include/asm/mkexec_import.h > sym.lds clean: rm -f $(obj-m) $(DEPS) rm -f $(KM_OBJS) rm -f sym.lds debug: @echo makevar: TOPDIR=$(TOPDIR) @echo envvar: TOPDIR=$$TOPDIR @echo ALL_MOBJS=$(ALL_MOBJS) @echo CONFIG_SHELL=$(CONFIG_SHELL) @echo CFLAGS=$(CFLAGS) --- NEW FILE: Makefile --- # # kernel/Makefile # # 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 # ARCH = i386 KTYPE = 26 TOPDIR = /home/src/linux-2.6.12-mkexec-v2-update THISDIR = /home/src/mkdump-v2.0/mkexec-module-2.1/kernel KVER = 2.6.12-mkexec ifeq ($(KTYPE),26) include $(THISDIR)/Makefile.26 endif ifeq ($(KTYPE),24) export TOPDIR include $(THISDIR)/Makefile.24 endif --- NEW FILE: Makefile.base --- # # kernel/Makefile.base # # 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 # CFLAGS= CC= gcc LD= ld #KVER= 2.6.12-mkexec-v2 KDIR= $(TOPDIR) SBINDIR= /usr/sbin KMODDIR= /lib/modules/$(KVER) ARCHDIR= arch/$(ARCH)/kernel PWD= $(shell pwd) MKEXECVER= 0.0.1 all: build build: sym.lds kmodules kmodules: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules sym.lds: ./mklds $(KDIR)/System.map ../include/asm/mkexec_import.h > 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 --- NEW FILE: minik_dump.c --- /* * kernel/minik_dump.c * * $Id: minik_dump.c,v 1.1 2006/02/14 05:30:27 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/init.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/smp.h> #include <linux/time.h> #include <linux/sched.h> #include <linux/vmalloc.h> #include <linux/mm.h> #include <linux/mmzone.h> #include <linux/ptrace.h> #include <linux/utsname.h> #include <linux/reboot.h> #include <linux/delay.h> #include <linux/mkexec.h> #include <linux/minik_param.h> #include <linux/cpus.h> #include <asm/processor.h> #include <asm/e820.h> #include <asm/hardirq.h> #include <asm/nmi.h> #include <asm/minik_dump.h> /* * start_dump() may crash and we get called again. * It occurs at least from: lapic_mkexec_restore() */ #define LOOPING_MAX 5 extern void (*do_dump_func)(char *, struct pt_regs *); static atomic_t minik_dump_sem = ATOMIC_INIT(1); static int minik_dump_cpu = NR_CPUS; static int looping_count = 0; void do_minik_dump(char *panic_str, struct pt_regs *regs) { int cpu; local_irq_disable(); /* turn off interrupt from now */ bust_spinlocks(1); /* avoid deadlock of printk */ cpu = get_processor_id(); /* check nested dump call */ if (atomic_dec_and_test(&minik_dump_sem)) { /* OK. first call */ minik_dump_cpu = cpu; } else { if (minik_dump_cpu == cpu) { looping_count++; printk("minik: Dump called by the dump routine (looped %d times, max is %d):\n", looping_count, LOOPING_MAX); dump_stack(); if (looping_count >= LOOPING_MAX) { printk("minik: Giving up.\n"); machine_restart(NULL); /* NOTREACHED */ } } else { printk("minik: Dump called by another cpu(%d)\n", cpu); for (;;) { cpu_relax(); } } } printk("minik: Mini Kernel Dump Start. dump cpu = %d\n", cpu); start_dump(panic_str, regs, cpu); /* defined in each arch */ /* not return */ } static void do_minik_netdump(struct pt_regs *regs) { do_minik_dump("do_minik_dump", regs); } void minik_dump_init(struct kimage *image) { init_dump_header(image); #ifdef MKEXEC_NO_PATCH /* only support RHEL currently */ netdump_func = do_minik_netdump; #else do_dump_func = do_minik_dump; #endif } --- NEW FILE: mkexec_main.c --- /* * kernel/mkexec_main.c * * mkexec: Linux boots Linux(Mini kernel) * * $Id: mkexec_main.c,v 1.1 2006/02/14 05:30:27 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. */ /* * Some codes were derived from kexec : * * kexec: Linux boots Linux * * Copyright (C) 2003,2004 Eric Biederman (ebi...@xm...) * * 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 (version 2 of the License). * * 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/config.h> #include <linux/module.h> #include <linux/stddef.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/stat.h> #include <linux/fcntl.h> #include <linux/proc_fs.h> #include <linux/stat.h> #include <linux/file.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/fs.h> #include <linux/version.h> #include <linux/compile.h> #include <linux/spinlock.h> #include <linux/smp_lock.h> #include <linux/list.h> #include <linux/mm.h> #include <linux/highmem.h> #include <linux/bootmem.h> #include <asm/page.h> #include <asm/uaccess.h> #include <asm/setup.h> #include <linux/mkexec.h> #include <asm/mkexec.h> #include <linux/minik_param.h> #include <asm/minik_dump.h> #include <asm/io.h> static void kimage_copy_reboot_code_pages(struct kimage *image); static int kimage_load(struct kimage *image); extern unsigned long kallsyms_lookup_name(const char *name); typedef struct crashmem *(crashmem_get_t)(int); static int kimage_alloc_reserve(struct kimage *image) { crashmem_get_t *crashmemget; /* ODA: header def minaosi */ struct crashmem *crashmem; /* ODA: header def minaosi */ #ifdef BACKPORT_24 if ((crashmemget = crashmem_get) == 0) { return -ENOMEM; } #else if ((crashmemget = (crashmem_get_t *)kallsyms_lookup_name("crashmem_get")) == 0) { return -ENOMEM; } #endif crashmem = crashmemget(1); if (crashmem && crashmem->size_bytes > 0) { image->reserve_mem_dma.base_pa = crashmem->base_pa; image->reserve_mem_dma.size_bytes = crashmem->size_bytes; } crashmem = crashmemget(0); if (crashmem && crashmem->size_bytes > 0) { image->reserve_mem[0].base_pa = crashmem->base_pa; image->reserve_mem[0].size_bytes = crashmem->size_bytes; } else { return -ENOMEM; } image->num_minik_mem = 1; image->new_kernel_paddr = image->reserve_mem[0].base_pa; /* ODA: common? */ image->reboot_code_addr = image->new_kernel_paddr; /* ODA: common? */ return 0; } static int kimage_alloc_new(struct kimage *image) { int num_seg, order; struct page *pages; int i; if ((mkexec_inf.mem & MINIK_SEG_MASK) != 0 || mkexec_inf.mem > MAX_MINIK_MEM_SEG * MINIK_SEG_SIZE) { return -EINVAL; }/* ODA: x86_64 ?? */ num_seg = mkexec_inf.mem >> MINIK_SEG_SHIFT; order = get_order(MINIK_SEG_SIZE); for (i = 0; i < num_seg; i++) { if (!(pages = alloc_pages(GFP_KERNEL, order))) { return -ENOMEM; } image->reserve_mem[i].base_pa = (unsigned long)(page_to_pfn(pages) << PAGE_SHIFT); image->reserve_mem[i].size_bytes = MINIK_SEG_SIZE; image->reserve_mem[i].pages = pages; image->reserve_mem[i].order = order; } image->num_minik_mem = num_seg; image->new_kernel_paddr = image->reserve_mem[0].base_pa; /* ODA: common? */ image->reboot_code_addr = image->new_kernel_paddr; /* ODA: common? */ return 0; } static int kimage_alloc(struct kimage *image) { int result; if (kimage_alloc_reserve(image) != 0 && (result = kimage_alloc_new(image)) != 0) { return result; } /* two pages: dump header + mem_seg */ image->dump_header_pages = alloc_pages(GFP_KERNEL, 1); if (!image->dump_header_pages) { return -ENOMEM; } image->nr_segments = 2; image->segment[0].mem = (void *)0x90000; /* setup */ image->segment[1].mem = (void *)0x100000; /* kernel 1MB */ // image->segment[2].mem = (void *)0x800000; /* initrd 8MB */ return 0; } static void kimage_copy_reboot_code_pages(struct kimage *image) { char *ptr ; /* copy it out */ ptr = (char *)kmap(pfn_to_page(image->reboot_code_addr >> PAGE_SHIFT)); memcpy(ptr, start_new_kernel, start_new_kernel_size); kunmap(pfn_to_page(image->reboot_code_addr >> PAGE_SHIFT)); } static int kimage_check(struct kimage *image) { if (image->segment[1].bufsz < MINIK_SIGNATURE_OFFSET + strlen(MINIK_SIGNATURE_HEAD)) { printk("mkexec: Loaded image minik is too small (%lu bytes)!\n", (unsigned long)image->segment[1].bufsz); return -EINVAL; } if (!memcmp((u8 *)image->segment[1].buf + MINIK_SIGNATURE_OFFSET, MINIK_SIGNATURE_HEAD, strlen(MINIK_SIGNATURE_HEAD))) { image->minik_type = MINIK_V2; } return 0; } static int kimage_load(struct kimage *image) { loff_t file_size; int read_size; struct file *file; int result = 0; mm_segment_t old_fs = get_fs(); set_fs(KERNEL_DS); file = filp_open(mkexec_inf.path, O_RDONLY, 0); if (IS_ERR(file)) { printk("mkexec: can't open %s\n", mkexec_inf.path); return -1; } image->segment[1].buf = __va(image->new_kernel_paddr + image->segment[1].mem); file_size = file->f_dentry->d_inode->i_size; if (image->reserve_mem[0].size_bytes < file_size+4) { printk("mkexec: Trying to load too big kernel image" " (file=%lld bytes, /proc/mkexec/mem=%lu bytes)\n", (long long)file_size, image->reserve_mem[0].size_bytes); result = -1; goto out; } if ((read_size = kernel_read(file, 0, image->segment[1].buf, file_size+4)) < 0) { printk("mkexec: mini-kernel read NG(%d): %s\n", read_size, mkexec_inf.path); result = -1; goto out; } if (file_size != read_size){ printk("mkexec: read_size is not file_size. read_size=%d, file_size=%lld\n", read_size, (long long)file_size); result = -1; goto out; } image->segment[1].bufsz = image->segment[1].memsz = file_size; result = kimage_check(image); out: set_fs(old_fs); filp_close(file, NULL); return result; } static int kimage_attr_prot(struct kimage *image, pgprot_t prot) { int err = 0; int numpages, i; struct page *page; return 0; /* not support yet */ if (!image){ return 0; } for (i = 0; i < image->num_minik_mem; i++) { numpages = image->reserve_mem[i].size_bytes >> PAGE_SHIFT; page = pfn_to_page(image->reserve_mem[i].base_pa >> PAGE_SHIFT); // if (i == 0 && image->minik_type == MINIK_V1) { // numpages -= 3; // page += 3; // } err = change_page_attr(page, numpages, prot); if (err) { printk("mkexec: Error setting minik protection (err=%d)!\n", err); break; } } global_flush_tlb(); return err; } static void kimage_free(struct kimage *image) { int i; if (!image){ return; } for (i = 0; i < image->num_minik_mem; i++) { if (image->reserve_mem[i].pages) { __free_pages(image->reserve_mem[i].pages, image->reserve_mem[i].order); } } if (image->dump_header_pages) { __free_pages(image->dump_header_pages, 1); } for (i = 0; i < image->num_control_page; i++) { __free_pages(image->control_page[i], 0); } kfree(image); } extern void minik_dump_init(struct kimage *kimage); struct kimage *mkexec_image = 0; static int mkexec_ddev_check(/* const */ char *ddev_string) { int dump_dev; unsigned long pages_system; unsigned long pages_ddev; struct block_device *bdev; pg_data_t *pgdat; int err; if (1 != get_option(&ddev_string, &dump_dev)) { printk(KERN_ERR "mkexec: Dump device id not a single number!\n"); return -EINVAL; } #ifdef BACKPORT_24 bdev = bdget(convert_old_dev(dump_dev)); #else bdev = bdget(dump_dev); #endif if (bdev == NULL) { printk(KERN_ERR "mkexec: Dump device id 0x%x not an existing device!\n", (unsigned)dump_dev); return -EINVAL; } #ifdef BACKPORT_24 err = blkdev_get(bdev, 0, O_RDWR, 0); #else err = blkdev_get(bdev, 0, O_RDWR); #endif if (err) { printk("mkexec: Dump device id 0x%x blkdev_get() error %d\n", (unsigned)dump_dev, err); /* No bdput(bdev); here. */ return -EINVAL; } pages_ddev = bdev->bd_inode->i_size >> PAGE_SHIFT; #ifdef BACKPORT_24 blkdev_put(bdev, 0); #else blkdev_put(bdev); #endif pages_system = 3; /* dump header pages */ for (pgdat = pgdat_list; pgdat; pgdat = next_pgdat(pgdat)) { pages_system += size_pgdat(pgdat); } if (pages_system > pages_ddev) { printk(KERN_ERR "mkexec: Dump device id 0x%x is too small (device = %lu pages, RAM = %lu pages)!\n", (unsigned)dump_dev, pages_ddev, pages_system); return -EINVAL; } return 0; } int mkexec_load(void) { struct kimage *image; int result; if ((image = kmalloc(sizeof(*image), GFP_KERNEL)) == NULL) { return -ENOMEM; } memset(image, 0, sizeof(*image)); if ((result = kimage_alloc(image)) != 0) { goto out; } if ((result = kimage_load(image)) != 0) { goto out; } if ((result = pimage_create(image)) != 0) { goto out; } if ((result = machine_mkexec_prepare(image)) != 0) { goto out; } kimage_copy_reboot_code_pages(image); minik_dump_init(image); if ((result = kimage_attr_prot(image, PAGE_KERNEL_RO)) != 0) { goto out; } image = xchg(&mkexec_image, image); out: (void)kimage_attr_prot(image, PAGE_KERNEL); kimage_free(image); return result; } int mkexec_exec(void) { struct kimage *image; image = xchg(&mkexec_image, 0); if (!image) { return -EINVAL; } printk(KERN_EMERG "Starting mini kernel\n"); machine_mkexec(image); return -1; } int mkexec_unload(void) { struct kimage *image; #ifdef MKEXEC_NO_PATCH /* only support RHEL currently */ cmpxchg(&netdump_func, (unsigned long)do_minik_dump, (unsigned long)NULL); #else cmpxchg(&do_dump_func, (unsigned long)do_minik_dump, (unsigned long)NULL); #endif image = xchg(&mkexec_image, 0); if (!image) { (void)kimage_attr_prot(image, PAGE_KERNEL); kimage_free(image); } return 0; } /* * mkexec module and /proc interface */ /* * module informations */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("C.Morita"); MODULE_DESCRIPTION("Mini Kernel Exec Module"); char mkexec_proc_base[] = "mkexec"; char mkexec_proc_ddev[] = "mkexec/dumpdev"; char mkexec_proc_mem[] = "mkexec/mem"; char mkexec_proc_stats[] = "mkexec/stats"; char mkexec_proc_path[] = "mkexec/path"; char mkexec_proc_parm[] = "mkexec/parameter"; mkexec_mod_t mkexec_inf; static ssize_t mkexec_proc_read(char __user *ubuf, char *src, size_t cnt, loff_t *ppos) { char tmp_buf[MKEXEC_MOD_MAXLEN]; int len = strlen(src); if (*ppos != 0 || cnt < len + 1) { /* must read at once */ return 0; } strcpy(tmp_buf, src); tmp_buf[len] = '\n'; if (copy_to_user(ubuf, tmp_buf, len + 1)) { return -EFAULT; } *ppos = len + 1; return len + 1; } static ssize_t mkexec_proc_write(const char __user *ubuf, char *dst, size_t cnt, loff_t *ppos, int maxcnt) { char tmp_buf[MKEXEC_MOD_MAXLEN]; if (*ppos != 0 || cnt > maxcnt) { /* must write at once */ return -EINVAL; } if (copy_from_user(tmp_buf, ubuf, cnt) != 0) { return -EFAULT; } if (tmp_buf[cnt - 1] != 0 && tmp_buf[cnt - 1] != '\n') { return -EINVAL; } tmp_buf[cnt - 1] = 0; memcpy(dst, tmp_buf, cnt); *ppos = cnt; return cnt; } static ssize_t mkexec_read_stats(struct file *file, char __user *ubuf, size_t cnt, loff_t *ppos) { char tmp_buf[2]; tmp_buf[0] = '0' + (char)mkexec_inf.stats; tmp_buf[1] = 0; return mkexec_proc_read(ubuf, tmp_buf, cnt, ppos); } static ssize_t mkexec_write_stats(struct file *file, const char __user *ubuf, size_t cnt, loff_t *ppos) { char tmp_buf[2]; int ret; if ((ret = mkexec_proc_write(ubuf, tmp_buf, cnt, ppos, 2)) < 0) { return ret; } switch (tmp_buf[0]) { case '0': /* mkexec unload */ (void)mkexec_unload(); mkexec_inf.stats = 0; break; case '1': /* mkexec load */ if (mkexec_load() < 0) { /* mini kernel loading */ return -EPERM; break; } mkexec_inf.stats = 1; break; case '2': if (mkexec_inf.stats == 1){ mkexec_inf.stats = 2; /* machine_mexec() is executed out of do_dump(). */ #ifdef MKEXEC_NO_PATCH panic("Compulsory dump(stat of mkexec was set as 2)."); #else do_dump("Compulsory dump(stat of mkexec was set as 2).", NULL); #endif } return -EBUSY; break; default: return -EINVAL; break; } return ret; } static ssize_t mkexec_read_parm(struct file *file, char __user *ubuf, size_t cnt, loff_t *ppos) { return mkexec_proc_read(ubuf, mkexec_inf.parm, cnt, ppos); } static ssize_t mkexec_write_parm(struct file *file, const char __user *ubuf, size_t cnt, loff_t *ppos) { return mkexec_proc_write(ubuf, mkexec_inf.parm, cnt, ppos, MKEXEC_MOD_MAXLEN); } static ssize_t mkexec_read_path(struct file *file, char __user *ubuf, size_t cnt, loff_t *ppos) { return mkexec_proc_read(ubuf, mkexec_inf.path, cnt, ppos); } static ssize_t mkexec_write_path(struct file *file, const char __user *ubuf, size_t cnt, loff_t *ppos) { return mkexec_proc_write(ubuf, mkexec_inf.path, cnt, ppos, MKEXEC_MOD_MAXLEN); } static ssize_t mkexec_read_ddev(struct file *file, char __user *ubuf, size_t cnt, loff_t *ppos) { return mkexec_proc_read(ubuf, mkexec_inf.ddev, cnt, ppos); } static ssize_t mkexec_write_ddev(struct file *file, const char __user *ubuf, size_t cnt, loff_t *ppos) { int ret, err; if ((ret = mkexec_proc_write(ubuf, mkexec_inf.ddev, cnt, ppos, MKEXEC_MOD_MAXDEVLEN)) < 0) { return ret; } if ((err = mkexec_ddev_check(mkexec_inf.ddev)) < 0) { ret = err; mkexec_inf.ddev[0] = 0; } return ret; } static ssize_t mkexec_read_mem(struct file *file, char *ubuf, size_t cnt, loff_t *ppos) { return mkexec_proc_read(ubuf, mkexec_inf.memstr, cnt, ppos); } static ssize_t mkexec_write_mem(struct file *file, const char *ubuf, size_t cnt, loff_t *ppos) { int ret; unsigned long val; char *endp; if ((ret = mkexec_proc_write(ubuf, mkexec_inf.memstr, cnt, ppos, MKEXEC_MOD_MAXDEVLEN)) < 0) { return ret; } val = simple_strtoul(mkexec_inf.memstr, &endp, 0); if (*endp != 0) { goto out; } val = (val + 3) / 4 * MINIK_SEG_SIZE ; /* XXX: sholud not use magic number */ if (val > MAX_MINIK_MEM_SEG * MINIK_SEG_SIZE) { goto out; } mkexec_inf.mem = val; return ret; out: mkexec_inf.memstr[0] = 0; return -EINVAL; } static int mkexec_proc_open(struct inode *ip, struct file *fp) { if (fp->f_mode & FMODE_WRITE && !capable(CAP_SYS_ADMIN)) { return -EPERM; } if (atomic_dec_and_test(&mkexec_inf.sem)) { return 0; } else { atomic_inc(&mkexec_inf.sem); return -EBUSY; } } static int mkexec_proc_close(struct inode *ip, struct file *fp) { atomic_inc(&mkexec_inf.sem); return 0; } static struct file_operations mkexec_proc_ddev_op = { .read = mkexec_read_ddev, .write = mkexec_write_ddev, .open = mkexec_proc_open, .release = mkexec_proc_close }; static struct file_operations mkexec_proc_mem_op = { .read = mkexec_read_mem, .write = mkexec_write_mem, .open = mkexec_proc_open, .release = mkexec_proc_close }; static struct file_operations mkexec_proc_stats_op = { .read = mkexec_read_stats, .write = mkexec_write_stats, .open = mkexec_proc_open, .release = mkexec_proc_close }; static struct file_operations mkexec_proc_path_op = { .read = mkexec_read_path, .write = mkexec_write_path, .open = mkexec_proc_open, .release = mkexec_proc_close }; static struct file_operations mkexec_proc_parm_op = { .read = mkexec_read_parm, .write = mkexec_write_parm, .open = mkexec_proc_open, .release = mkexec_proc_close }; void mkexec_mod_exit(void) { (void)mkexec_unload(); remove_proc_entry(mkexec_proc_path, 0); remove_proc_entry(mkexec_proc_stats, 0); remove_proc_entry(mkexec_proc_parm, 0); remove_proc_entry(mkexec_proc_mem, 0); remove_proc_entry(mkexec_proc_ddev, 0); remove_proc_entry(mkexec_proc_base, 0); } int mkexec_mod_init(void) { struct proc_dir_entry *pe; memset(&mkexec_inf, 0, sizeof(mkexec_inf)); atomic_set(&mkexec_inf.sem, 1); if (proc_mkdir(mkexec_proc_base, 0) == NULL){ printk(KERN_ERR "mkexec: can't create proc entrise.\n"); return -EINVAL; } if ((pe = create_proc_entry(mkexec_proc_stats, 0, 0)) == NULL) { goto out; } pe->proc_fops = &mkexec_proc_stats_op; if ((pe = create_proc_entry(mkexec_proc_parm, 0, 0)) == NULL) { goto out; } pe->proc_fops = &mkexec_proc_parm_op; if ((pe = create_proc_entry(mkexec_proc_path, 0, 0)) == NULL) { goto out; } pe->proc_fops = &mkexec_proc_path_op; if ((pe = create_proc_entry(mkexec_proc_mem, 0, 0)) == NULL) { goto out; } pe->proc_fops = &mkexec_proc_mem_op; if ((pe = create_proc_entry(mkexec_proc_ddev, 0, 0)) == NULL) { goto out; } pe->proc_fops = &mkexec_proc_ddev_op; return 0; out: mkexec_mod_exit(); return -EINVAL; } module_init(mkexec_mod_init); module_exit(mkexec_mod_exit); |