From: Felix Fietkau Date: Wed, 21 Jul 2010 16:06:29 +0000 (+0000) Subject: merge the crashlog feature from r22305, r22326 X-Git-Url: http://git.openwrt.org/?p=openwrt%2Fsvn-archive%2Farchive.git;a=commitdiff_plain;hb=97eb3a54407d86a177a435290bc0108379d8c38b merge the crashlog feature from r22305, r22326 SVN-Revision: 22333 --- diff --git a/target/linux/generic-2.6/config-2.6.30 b/target/linux/generic-2.6/config-2.6.30 index dc14bcdb41..ff553850b8 100644 --- a/target/linux/generic-2.6/config-2.6.30 +++ b/target/linux/generic-2.6/config-2.6.30 @@ -336,6 +336,7 @@ CONFIG_COMPAT_NET_DEV_OPS=y # CONFIG_CPU_FREQ is not set # CONFIG_CPU_IDLE is not set # CONFIG_CRAMFS is not set +CONFIG_CRASHLOG=y # CONFIG_CRASH_DUMP is not set # CONFIG_CRC16 is not set CONFIG_CRC32=y diff --git a/target/linux/generic-2.6/config-2.6.32 b/target/linux/generic-2.6/config-2.6.32 index c8b10b3fd3..522a30acba 100644 --- a/target/linux/generic-2.6/config-2.6.32 +++ b/target/linux/generic-2.6/config-2.6.32 @@ -351,6 +351,7 @@ CONFIG_CONSTRUCTORS=y # CONFIG_CPU_FREQ is not set # CONFIG_CPU_IDLE is not set # CONFIG_CRAMFS is not set +CONFIG_CRASHLOG=y # CONFIG_CRASH_DUMP is not set # CONFIG_CRC16 is not set CONFIG_CRC32=y diff --git a/target/linux/generic-2.6/patches-2.6.30/930-kmsg_dump_backport.patch b/target/linux/generic-2.6/patches-2.6.30/930-kmsg_dump_backport.patch new file mode 100644 index 0000000000..762b441114 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.30/930-kmsg_dump_backport.patch @@ -0,0 +1,205 @@ +--- /dev/null ++++ b/include/linux/kmsg_dump.h +@@ -0,0 +1,44 @@ ++/* ++ * linux/include/kmsg_dump.h ++ * ++ * Copyright (C) 2009 Net Insight AB ++ * ++ * Author: Simon Kagstrom ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ */ ++#ifndef _LINUX_KMSG_DUMP_H ++#define _LINUX_KMSG_DUMP_H ++ ++#include ++ ++enum kmsg_dump_reason { ++ KMSG_DUMP_OOPS, ++ KMSG_DUMP_PANIC, ++}; ++ ++/** ++ * struct kmsg_dumper - kernel crash message dumper structure ++ * @dump: The callback which gets called on crashes. The buffer is passed ++ * as two sections, where s1 (length l1) contains the older ++ * messages and s2 (length l2) contains the newer. ++ * @list: Entry in the dumper list (private) ++ * @registered: Flag that specifies if this is already registered ++ */ ++struct kmsg_dumper { ++ void (*dump)(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason, ++ const char *s1, unsigned long l1, ++ const char *s2, unsigned long l2); ++ struct list_head list; ++ int registered; ++}; ++ ++void kmsg_dump(enum kmsg_dump_reason reason); ++ ++int kmsg_dump_register(struct kmsg_dumper *dumper); ++ ++int kmsg_dump_unregister(struct kmsg_dumper *dumper); ++ ++#endif /* _LINUX_KMSG_DUMP_H */ +--- a/kernel/panic.c ++++ b/kernel/panic.c +@@ -10,6 +10,7 @@ + */ + #include + #include ++#include + #include + #include + #include +@@ -74,6 +75,7 @@ + dump_stack(); + #endif + ++ kmsg_dump(KMSG_DUMP_PANIC); + /* + * If we have crashed and we have a crash kernel loaded let it handle + * everything else. +@@ -337,6 +339,7 @@ + { + do_oops_enter_exit(); + print_oops_end_marker(); ++ kmsg_dump(KMSG_DUMP_OOPS); + } + + #ifdef WANT_WARN_ON_SLOWPATH +--- a/kernel/printk.c ++++ b/kernel/printk.c +@@ -33,6 +33,7 @@ + #include + #include + #include ++#include + + #include + +@@ -1322,3 +1323,121 @@ + } + EXPORT_SYMBOL(printk_timed_ratelimit); + #endif ++ ++static DEFINE_SPINLOCK(dump_list_lock); ++static LIST_HEAD(dump_list); ++ ++/** ++ * kmsg_dump_register - register a kernel log dumper. ++ * @dump: pointer to the kmsg_dumper structure ++ * ++ * Adds a kernel log dumper to the system. The dump callback in the ++ * structure will be called when the kernel oopses or panics and must be ++ * set. Returns zero on success and %-EINVAL or %-EBUSY otherwise. ++ */ ++int kmsg_dump_register(struct kmsg_dumper *dumper) ++{ ++ unsigned long flags; ++ int err = -EBUSY; ++ ++ /* The dump callback needs to be set */ ++ if (!dumper->dump) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&dump_list_lock, flags); ++ /* Don't allow registering multiple times */ ++ if (!dumper->registered) { ++ dumper->registered = 1; ++ list_add_tail(&dumper->list, &dump_list); ++ err = 0; ++ } ++ spin_unlock_irqrestore(&dump_list_lock, flags); ++ ++ return err; ++} ++EXPORT_SYMBOL_GPL(kmsg_dump_register); ++ ++/** ++ * kmsg_dump_unregister - unregister a kmsg dumper. ++ * @dump: pointer to the kmsg_dumper structure ++ * ++ * Removes a dump device from the system. Returns zero on success and ++ * %-EINVAL otherwise. ++ */ ++int kmsg_dump_unregister(struct kmsg_dumper *dumper) ++{ ++ unsigned long flags; ++ int err = -EINVAL; ++ ++ spin_lock_irqsave(&dump_list_lock, flags); ++ if (dumper->registered) { ++ dumper->registered = 0; ++ list_del(&dumper->list); ++ err = 0; ++ } ++ spin_unlock_irqrestore(&dump_list_lock, flags); ++ ++ return err; ++} ++EXPORT_SYMBOL_GPL(kmsg_dump_unregister); ++ ++static const char const *kmsg_reasons[] = { ++ [KMSG_DUMP_OOPS] = "oops", ++ [KMSG_DUMP_PANIC] = "panic", ++}; ++ ++static const char *kmsg_to_str(enum kmsg_dump_reason reason) ++{ ++ if (reason >= ARRAY_SIZE(kmsg_reasons) || reason < 0) ++ return "unknown"; ++ ++ return kmsg_reasons[reason]; ++} ++ ++/** ++ * kmsg_dump - dump kernel log to kernel message dumpers. ++ * @reason: the reason (oops, panic etc) for dumping ++ * ++ * Iterate through each of the dump devices and call the oops/panic ++ * callbacks with the log buffer. ++ */ ++void kmsg_dump(enum kmsg_dump_reason reason) ++{ ++ unsigned long end; ++ unsigned chars; ++ struct kmsg_dumper *dumper; ++ const char *s1, *s2; ++ unsigned long l1, l2; ++ unsigned long flags; ++ ++ /* Theoretically, the log could move on after we do this, but ++ there's not a lot we can do about that. The new messages ++ will overwrite the start of what we dump. */ ++ spin_lock_irqsave(&logbuf_lock, flags); ++ end = log_end & LOG_BUF_MASK; ++ chars = logged_chars; ++ spin_unlock_irqrestore(&logbuf_lock, flags); ++ ++ if (logged_chars > end) { ++ s1 = log_buf + log_buf_len - logged_chars + end; ++ l1 = logged_chars - end; ++ ++ s2 = log_buf; ++ l2 = end; ++ } else { ++ s1 = ""; ++ l1 = 0; ++ ++ s2 = log_buf + end - logged_chars; ++ l2 = logged_chars; ++ } ++ ++ if (!spin_trylock_irqsave(&dump_list_lock, flags)) { ++ printk(KERN_ERR "dump_kmsg: dump list lock is held during %s, skipping dump\n", ++ kmsg_to_str(reason)); ++ return; ++ } ++ list_for_each_entry(dumper, &dump_list, list) ++ dumper->dump(dumper, reason, s1, l1, s2, l2); ++ spin_unlock_irqrestore(&dump_list_lock, flags); ++} diff --git a/target/linux/generic-2.6/patches-2.6.30/931-crashlog.patch b/target/linux/generic-2.6/patches-2.6.30/931-crashlog.patch new file mode 100644 index 0000000000..d4424190c6 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.30/931-crashlog.patch @@ -0,0 +1,241 @@ +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -723,6 +723,9 @@ + Allow user space to create what appear to be multiple instances + of the network stack. + ++config CRASHLOG ++ bool "Crash logging" ++ + config BLK_DEV_INITRD + bool "Initial RAM filesystem and RAM disk (initramfs/initrd) support" + depends on BROKEN || !FRV +--- a/kernel/Makefile ++++ b/kernel/Makefile +@@ -95,6 +95,7 @@ + obj-$(CONFIG_TRACING) += trace/ + obj-$(CONFIG_SMP) += sched_cpupri.o + obj-$(CONFIG_SLOW_WORK) += slow-work.o ++obj-$(CONFIG_CRASHLOG) += crashlog.o + + ifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y) + # According to Alan Modra , the -fno-omit-frame-pointer is +--- a/mm/bootmem.c ++++ b/mm/bootmem.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -151,6 +152,7 @@ + if (!bdata->node_bootmem_map) + return 0; + ++ crashlog_init_mem(bdata); + start = bdata->node_min_pfn; + end = bdata->node_low_pfn; + +--- a/kernel/module.c ++++ b/kernel/module.c +@@ -71,6 +71,9 @@ + DEFINE_MUTEX(module_mutex); + EXPORT_SYMBOL_GPL(module_mutex); + static LIST_HEAD(modules); ++#ifdef CONFIG_CRASHLOG ++struct list_head *crashlog_modules = &modules; ++#endif + + /* Waiting for a module to finish initializing? */ + static DECLARE_WAIT_QUEUE_HEAD(module_wq); +--- /dev/null ++++ b/include/linux/crashlog.h +@@ -0,0 +1,12 @@ ++#ifndef __CRASHLOG_H ++#define __CRASHLOG_H ++ ++#ifdef CONFIG_CRASHLOG ++void __init crashlog_init_mem(struct bootmem_data *bdata); ++#else ++static inline void crashlog_init_mem(struct bootmem_data *bdata) ++{ ++} ++#endif ++ ++#endif +--- /dev/null ++++ b/kernel/crashlog.c +@@ -0,0 +1,171 @@ ++/* ++ * Crash information logger ++ * Copyright (C) 2010 Felix Fietkau ++ * ++ * Based on ramoops.c ++ * Copyright (C) 2010 Marco Stornelli ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * 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., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define CRASHLOG_PAGES 4 ++#define CRASHLOG_SIZE (CRASHLOG_PAGES * PAGE_SIZE) ++#define CRASHLOG_MAGIC 0xa1eedead ++ ++/* ++ * Start the log at 1M before the end of RAM, as some boot loaders like ++ * to use the end of the RAM for stack usage and other things ++ * If this fails, fall back to using the last part. ++ */ ++#define CRASHLOG_OFFSET (1024 * 1024) ++ ++struct crashlog_data { ++ u32 magic; ++ u32 len; ++ u8 data[]; ++}; ++ ++static struct debugfs_blob_wrapper crashlog_blob; ++static unsigned long crashlog_addr = 0; ++static struct crashlog_data *crashlog_buf; ++static struct kmsg_dumper dump; ++static bool first = true; ++ ++extern struct list_head *crashlog_modules; ++ ++void __init crashlog_init_mem(bootmem_data_t *bdata) ++{ ++ unsigned long addr; ++ ++ if (crashlog_addr) ++ return; ++ ++ addr = PFN_PHYS(bdata->node_low_pfn) - CRASHLOG_OFFSET; ++ if (reserve_bootmem(addr, CRASHLOG_SIZE, BOOTMEM_EXCLUSIVE) < 0) { ++ printk("Crashlog failed to allocate RAM at address 0x%lx\n", addr); ++ bdata->node_low_pfn -= CRASHLOG_PAGES; ++ addr = PFN_PHYS(bdata->node_low_pfn); ++ } ++ crashlog_addr = addr; ++} ++ ++static void __init crashlog_copy(void) ++{ ++ if (crashlog_buf->magic != CRASHLOG_MAGIC) ++ return; ++ ++ if (!crashlog_buf->len || crashlog_buf->len > ++ CRASHLOG_SIZE - sizeof(*crashlog_buf)) ++ return; ++ ++ crashlog_blob.size = crashlog_buf->len; ++ crashlog_blob.data = kmemdup(crashlog_buf->data, ++ crashlog_buf->len, GFP_KERNEL); ++ ++ debugfs_create_blob("crashlog", 0700, NULL, &crashlog_blob); ++} ++ ++static int get_maxlen(void) ++{ ++ return CRASHLOG_SIZE - sizeof(*crashlog_buf) - crashlog_buf->len; ++} ++ ++static void crashlog_printf(const char *fmt, ...) ++{ ++ va_list args; ++ int len = get_maxlen(); ++ ++ if (!len) ++ return; ++ ++ va_start(args, fmt); ++ crashlog_buf->len += vsnprintf( ++ &crashlog_buf->data[crashlog_buf->len], ++ len, fmt, args); ++ va_end(args); ++} ++ ++static void crashlog_do_dump(struct kmsg_dumper *dumper, ++ enum kmsg_dump_reason reason, const char *s1, unsigned long l1, ++ const char *s2, unsigned long l2) ++{ ++ unsigned long s1_start, s2_start; ++ unsigned long l1_cpy, l2_cpy; ++ struct timeval tv; ++ struct module *m; ++ char *buf; ++ int len; ++ ++ if (!first) ++ crashlog_printf("\n===================================\n"); ++ ++ do_gettimeofday(&tv); ++ crashlog_printf("Time: %lu.%lu\n", ++ (long)tv.tv_sec, (long)tv.tv_usec); ++ ++ if (first) { ++ crashlog_printf("Modules:"); ++ list_for_each_entry(m, crashlog_modules, list) { ++ crashlog_printf("\t%s@%p+%x", m->name, ++ m->module_core, m->core_size, ++ m->module_init, m->init_size); ++ } ++ crashlog_printf("\n"); ++ first = false; ++ } ++ ++ buf = (char *)&crashlog_buf->data[crashlog_buf->len]; ++ len = get_maxlen(); ++ ++ l2_cpy = min(l2, (unsigned long)len); ++ l1_cpy = min(l1, (unsigned long)len - l2_cpy); ++ ++ s2_start = l2 - l2_cpy; ++ s1_start = l1 - l1_cpy; ++ ++ memcpy(buf, s1 + s1_start, l1_cpy); ++ memcpy(buf + l1_cpy, s2 + s2_start, l2_cpy); ++ crashlog_buf->len += l1_cpy + l2_cpy; ++} ++ ++ ++int __init crashlog_init_fs(void) ++{ ++ if (!crashlog_addr) ++ return -ENOMEM; ++ ++ crashlog_buf = ioremap(crashlog_addr, CRASHLOG_SIZE); ++ ++ crashlog_copy(); ++ ++ crashlog_buf->magic = CRASHLOG_MAGIC; ++ crashlog_buf->len = 0; ++ ++ dump.dump = crashlog_do_dump; ++ kmsg_dump_register(&dump); ++ ++ return 0; ++} ++module_init(crashlog_init_fs); diff --git a/target/linux/generic-2.6/patches-2.6.32/930-kmsg_dump_backport.patch b/target/linux/generic-2.6/patches-2.6.32/930-kmsg_dump_backport.patch new file mode 100644 index 0000000000..4b0369059e --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.32/930-kmsg_dump_backport.patch @@ -0,0 +1,205 @@ +--- /dev/null ++++ b/include/linux/kmsg_dump.h +@@ -0,0 +1,44 @@ ++/* ++ * linux/include/kmsg_dump.h ++ * ++ * Copyright (C) 2009 Net Insight AB ++ * ++ * Author: Simon Kagstrom ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ */ ++#ifndef _LINUX_KMSG_DUMP_H ++#define _LINUX_KMSG_DUMP_H ++ ++#include ++ ++enum kmsg_dump_reason { ++ KMSG_DUMP_OOPS, ++ KMSG_DUMP_PANIC, ++}; ++ ++/** ++ * struct kmsg_dumper - kernel crash message dumper structure ++ * @dump: The callback which gets called on crashes. The buffer is passed ++ * as two sections, where s1 (length l1) contains the older ++ * messages and s2 (length l2) contains the newer. ++ * @list: Entry in the dumper list (private) ++ * @registered: Flag that specifies if this is already registered ++ */ ++struct kmsg_dumper { ++ void (*dump)(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason, ++ const char *s1, unsigned long l1, ++ const char *s2, unsigned long l2); ++ struct list_head list; ++ int registered; ++}; ++ ++void kmsg_dump(enum kmsg_dump_reason reason); ++ ++int kmsg_dump_register(struct kmsg_dumper *dumper); ++ ++int kmsg_dump_unregister(struct kmsg_dumper *dumper); ++ ++#endif /* _LINUX_KMSG_DUMP_H */ +--- a/kernel/panic.c ++++ b/kernel/panic.c +@@ -10,6 +10,7 @@ + */ + #include + #include ++#include + #include + #include + #include +@@ -74,6 +75,7 @@ NORET_TYPE void panic(const char * fmt, + dump_stack(); + #endif + ++ kmsg_dump(KMSG_DUMP_PANIC); + /* + * If we have crashed and we have a crash kernel loaded let it handle + * everything else. +@@ -339,6 +341,7 @@ void oops_exit(void) + { + do_oops_enter_exit(); + print_oops_end_marker(); ++ kmsg_dump(KMSG_DUMP_OOPS); + } + + #ifdef WANT_WARN_ON_SLOWPATH +--- a/kernel/printk.c ++++ b/kernel/printk.c +@@ -33,6 +33,7 @@ + #include + #include + #include ++#include + + #include + +@@ -1405,3 +1406,121 @@ bool printk_timed_ratelimit(unsigned lon + } + EXPORT_SYMBOL(printk_timed_ratelimit); + #endif ++ ++static DEFINE_SPINLOCK(dump_list_lock); ++static LIST_HEAD(dump_list); ++ ++/** ++ * kmsg_dump_register - register a kernel log dumper. ++ * @dump: pointer to the kmsg_dumper structure ++ * ++ * Adds a kernel log dumper to the system. The dump callback in the ++ * structure will be called when the kernel oopses or panics and must be ++ * set. Returns zero on success and %-EINVAL or %-EBUSY otherwise. ++ */ ++int kmsg_dump_register(struct kmsg_dumper *dumper) ++{ ++ unsigned long flags; ++ int err = -EBUSY; ++ ++ /* The dump callback needs to be set */ ++ if (!dumper->dump) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&dump_list_lock, flags); ++ /* Don't allow registering multiple times */ ++ if (!dumper->registered) { ++ dumper->registered = 1; ++ list_add_tail(&dumper->list, &dump_list); ++ err = 0; ++ } ++ spin_unlock_irqrestore(&dump_list_lock, flags); ++ ++ return err; ++} ++EXPORT_SYMBOL_GPL(kmsg_dump_register); ++ ++/** ++ * kmsg_dump_unregister - unregister a kmsg dumper. ++ * @dump: pointer to the kmsg_dumper structure ++ * ++ * Removes a dump device from the system. Returns zero on success and ++ * %-EINVAL otherwise. ++ */ ++int kmsg_dump_unregister(struct kmsg_dumper *dumper) ++{ ++ unsigned long flags; ++ int err = -EINVAL; ++ ++ spin_lock_irqsave(&dump_list_lock, flags); ++ if (dumper->registered) { ++ dumper->registered = 0; ++ list_del(&dumper->list); ++ err = 0; ++ } ++ spin_unlock_irqrestore(&dump_list_lock, flags); ++ ++ return err; ++} ++EXPORT_SYMBOL_GPL(kmsg_dump_unregister); ++ ++static const char const *kmsg_reasons[] = { ++ [KMSG_DUMP_OOPS] = "oops", ++ [KMSG_DUMP_PANIC] = "panic", ++}; ++ ++static const char *kmsg_to_str(enum kmsg_dump_reason reason) ++{ ++ if (reason >= ARRAY_SIZE(kmsg_reasons) || reason < 0) ++ return "unknown"; ++ ++ return kmsg_reasons[reason]; ++} ++ ++/** ++ * kmsg_dump - dump kernel log to kernel message dumpers. ++ * @reason: the reason (oops, panic etc) for dumping ++ * ++ * Iterate through each of the dump devices and call the oops/panic ++ * callbacks with the log buffer. ++ */ ++void kmsg_dump(enum kmsg_dump_reason reason) ++{ ++ unsigned long end; ++ unsigned chars; ++ struct kmsg_dumper *dumper; ++ const char *s1, *s2; ++ unsigned long l1, l2; ++ unsigned long flags; ++ ++ /* Theoretically, the log could move on after we do this, but ++ there's not a lot we can do about that. The new messages ++ will overwrite the start of what we dump. */ ++ spin_lock_irqsave(&logbuf_lock, flags); ++ end = log_end & LOG_BUF_MASK; ++ chars = logged_chars; ++ spin_unlock_irqrestore(&logbuf_lock, flags); ++ ++ if (logged_chars > end) { ++ s1 = log_buf + log_buf_len - logged_chars + end; ++ l1 = logged_chars - end; ++ ++ s2 = log_buf; ++ l2 = end; ++ } else { ++ s1 = ""; ++ l1 = 0; ++ ++ s2 = log_buf + end - logged_chars; ++ l2 = logged_chars; ++ } ++ ++ if (!spin_trylock_irqsave(&dump_list_lock, flags)) { ++ printk(KERN_ERR "dump_kmsg: dump list lock is held during %s, skipping dump\n", ++ kmsg_to_str(reason)); ++ return; ++ } ++ list_for_each_entry(dumper, &dump_list, list) ++ dumper->dump(dumper, reason, s1, l1, s2, l2); ++ spin_unlock_irqrestore(&dump_list_lock, flags); ++} diff --git a/target/linux/generic-2.6/patches-2.6.32/931-crashlog.patch b/target/linux/generic-2.6/patches-2.6.32/931-crashlog.patch new file mode 100644 index 0000000000..0c06700aef --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.32/931-crashlog.patch @@ -0,0 +1,241 @@ +--- /dev/null ++++ b/include/linux/crashlog.h +@@ -0,0 +1,12 @@ ++#ifndef __CRASHLOG_H ++#define __CRASHLOG_H ++ ++#ifdef CONFIG_CRASHLOG ++void __init crashlog_init_mem(struct bootmem_data *bdata); ++#else ++static inline void crashlog_init_mem(struct bootmem_data *bdata) ++{ ++} ++#endif ++ ++#endif +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -710,6 +710,9 @@ config NET_NS + Allow user space to create what appear to be multiple instances + of the network stack. + ++config CRASHLOG ++ bool "Crash logging" ++ + config BLK_DEV_INITRD + bool "Initial RAM filesystem and RAM disk (initramfs/initrd) support" + depends on BROKEN || !FRV +--- a/kernel/Makefile ++++ b/kernel/Makefile +@@ -96,6 +96,7 @@ obj-$(CONFIG_SMP) += sched_cpupri.o + obj-$(CONFIG_SLOW_WORK) += slow-work.o + obj-$(CONFIG_SLOW_WORK_DEBUG) += slow-work-debugfs.o + obj-$(CONFIG_PERF_EVENTS) += perf_event.o ++obj-$(CONFIG_CRASHLOG) += crashlog.o + + ifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y) + # According to Alan Modra , the -fno-omit-frame-pointer is +--- /dev/null ++++ b/kernel/crashlog.c +@@ -0,0 +1,171 @@ ++/* ++ * Crash information logger ++ * Copyright (C) 2010 Felix Fietkau ++ * ++ * Based on ramoops.c ++ * Copyright (C) 2010 Marco Stornelli ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * 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., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define CRASHLOG_PAGES 4 ++#define CRASHLOG_SIZE (CRASHLOG_PAGES * PAGE_SIZE) ++#define CRASHLOG_MAGIC 0xa1eedead ++ ++/* ++ * Start the log at 1M before the end of RAM, as some boot loaders like ++ * to use the end of the RAM for stack usage and other things ++ * If this fails, fall back to using the last part. ++ */ ++#define CRASHLOG_OFFSET (1024 * 1024) ++ ++struct crashlog_data { ++ u32 magic; ++ u32 len; ++ u8 data[]; ++}; ++ ++static struct debugfs_blob_wrapper crashlog_blob; ++static unsigned long crashlog_addr = 0; ++static struct crashlog_data *crashlog_buf; ++static struct kmsg_dumper dump; ++static bool first = true; ++ ++extern struct list_head *crashlog_modules; ++ ++void __init crashlog_init_mem(bootmem_data_t *bdata) ++{ ++ unsigned long addr; ++ ++ if (crashlog_addr) ++ return; ++ ++ addr = PFN_PHYS(bdata->node_low_pfn) - CRASHLOG_OFFSET; ++ if (reserve_bootmem(addr, CRASHLOG_SIZE, BOOTMEM_EXCLUSIVE) < 0) { ++ printk("Crashlog failed to allocate RAM at address 0x%lx\n", addr); ++ bdata->node_low_pfn -= CRASHLOG_PAGES; ++ addr = PFN_PHYS(bdata->node_low_pfn); ++ } ++ crashlog_addr = addr; ++} ++ ++static void __init crashlog_copy(void) ++{ ++ if (crashlog_buf->magic != CRASHLOG_MAGIC) ++ return; ++ ++ if (!crashlog_buf->len || crashlog_buf->len > ++ CRASHLOG_SIZE - sizeof(*crashlog_buf)) ++ return; ++ ++ crashlog_blob.size = crashlog_buf->len; ++ crashlog_blob.data = kmemdup(crashlog_buf->data, ++ crashlog_buf->len, GFP_KERNEL); ++ ++ debugfs_create_blob("crashlog", 0700, NULL, &crashlog_blob); ++} ++ ++static int get_maxlen(void) ++{ ++ return CRASHLOG_SIZE - sizeof(*crashlog_buf) - crashlog_buf->len; ++} ++ ++static void crashlog_printf(const char *fmt, ...) ++{ ++ va_list args; ++ int len = get_maxlen(); ++ ++ if (!len) ++ return; ++ ++ va_start(args, fmt); ++ crashlog_buf->len += vsnprintf( ++ &crashlog_buf->data[crashlog_buf->len], ++ len, fmt, args); ++ va_end(args); ++} ++ ++static void crashlog_do_dump(struct kmsg_dumper *dumper, ++ enum kmsg_dump_reason reason, const char *s1, unsigned long l1, ++ const char *s2, unsigned long l2) ++{ ++ unsigned long s1_start, s2_start; ++ unsigned long l1_cpy, l2_cpy; ++ struct timeval tv; ++ struct module *m; ++ char *buf; ++ int len; ++ ++ if (!first) ++ crashlog_printf("\n===================================\n"); ++ ++ do_gettimeofday(&tv); ++ crashlog_printf("Time: %lu.%lu\n", ++ (long)tv.tv_sec, (long)tv.tv_usec); ++ ++ if (first) { ++ crashlog_printf("Modules:"); ++ list_for_each_entry(m, crashlog_modules, list) { ++ crashlog_printf("\t%s@%p+%x", m->name, ++ m->module_core, m->core_size, ++ m->module_init, m->init_size); ++ } ++ crashlog_printf("\n"); ++ first = false; ++ } ++ ++ buf = (char *)&crashlog_buf->data[crashlog_buf->len]; ++ len = get_maxlen(); ++ ++ l2_cpy = min(l2, (unsigned long)len); ++ l1_cpy = min(l1, (unsigned long)len - l2_cpy); ++ ++ s2_start = l2 - l2_cpy; ++ s1_start = l1 - l1_cpy; ++ ++ memcpy(buf, s1 + s1_start, l1_cpy); ++ memcpy(buf + l1_cpy, s2 + s2_start, l2_cpy); ++ crashlog_buf->len += l1_cpy + l2_cpy; ++} ++ ++ ++int __init crashlog_init_fs(void) ++{ ++ if (!crashlog_addr) ++ return -ENOMEM; ++ ++ crashlog_buf = ioremap(crashlog_addr, CRASHLOG_SIZE); ++ ++ crashlog_copy(); ++ ++ crashlog_buf->magic = CRASHLOG_MAGIC; ++ crashlog_buf->len = 0; ++ ++ dump.dump = crashlog_do_dump; ++ kmsg_dump_register(&dump); ++ ++ return 0; ++} ++module_init(crashlog_init_fs); +--- a/mm/bootmem.c ++++ b/mm/bootmem.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -152,6 +153,7 @@ static unsigned long __init free_all_boo + if (!bdata->node_bootmem_map) + return 0; + ++ crashlog_init_mem(bdata); + start = bdata->node_min_pfn; + end = bdata->node_low_pfn; + +--- a/kernel/module.c ++++ b/kernel/module.c +@@ -79,6 +79,9 @@ EXPORT_TRACEPOINT_SYMBOL(module_get); + DEFINE_MUTEX(module_mutex); + EXPORT_SYMBOL_GPL(module_mutex); + static LIST_HEAD(modules); ++#ifdef CONFIG_CRASHLOG ++struct list_head *crashlog_modules = &modules; ++#endif + + /* Block module loading/unloading? */ + int modules_disabled = 0; diff --git a/target/linux/uml/config/i386 b/target/linux/uml/config/i386 index b8f78dc7e6..e1715c317a 100644 --- a/target/linux/uml/config/i386 +++ b/target/linux/uml/config/i386 @@ -15,6 +15,7 @@ CONFIG_CLASSIC_RCU=y CONFIG_CON_CHAN="xterm" CONFIG_CON_ZERO_CHAN="fd:0,fd:1" CONFIG_CRAMFS=y +# CONFIG_CRASHLOG is not set # CONFIG_CRYPTO_AES_586 is not set # CONFIG_CRYPTO_SALSA20_586 is not set # CONFIG_CRYPTO_TWOFISH_586 is not set diff --git a/target/linux/uml/config/x86_64 b/target/linux/uml/config/x86_64 index 8ef700fe6e..66c1c4b8df 100644 --- a/target/linux/uml/config/x86_64 +++ b/target/linux/uml/config/x86_64 @@ -13,6 +13,7 @@ CONFIG_BLK_DEV_UBD_SYNC=y CONFIG_CON_CHAN="xterm" CONFIG_CON_ZERO_CHAN="fd:0,fd:1" CONFIG_CRAMFS=y +# CONFIG_CRASHLOG is not set # CONFIG_CRYPTO_AES_NI_INTEL is not set # CONFIG_CRYPTO_AES_X86_64 is not set # CONFIG_CRYPTO_SALSA20_X86_64 is not set