commit 58316c061f10839dfd5ebfd6ed4def6370de7b62 Author: Maxim Levitsky Date: Thu May 27 22:49:01 2010 +0300 printk: Allow to store log buffer into video memory This patch adds new kernel parameter printk_address= that will allow it to store the printk buffer in arbitary (I/O) memory address. If you own a system that has discrete video ram, and it isn't cleared automaticly by bios on reboot, you can use this as a black box recorder of crashes. If debugfs is enabled, log of last boot is copied into system ram, and can be accessed via debugfs, for example cat /sys/kernel/debug/printk/crash_dmesg Signed-off-by: Maxim Levitsky diff --git a/kernel/printk.c b/kernel/printk.c index 444b770..68df9fd 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -39,6 +39,7 @@ #include #include +#include /* * for_each_console() allows you to iterate on each console @@ -146,6 +147,7 @@ static char *log_buf = __log_buf; static int log_buf_len = __LOG_BUF_LEN; static unsigned logged_chars; /* Number of chars produced since last read+clear operation */ static int saved_console_loglevel = -1; +static unsigned int printk_phys_address; #ifdef CONFIG_KEXEC /* @@ -170,6 +172,9 @@ static int __init log_buf_len_setup(char *str) unsigned size = memparse(str, &str); unsigned long flags; + if (printk_phys_address) + return 1; + if (size) size = roundup_pow_of_two(size); if (size > log_buf_len) { @@ -206,6 +211,103 @@ out: __setup("log_buf_len=", log_buf_len_setup); + +#ifdef CONFIG_HWMEM_PRINTK + +#define EARLY_LOG_PAGES (8 * PAGE_SIZE) +char *old_log_buf; +struct debugfs_blob_wrapper crash_dmesg_wrapper; + +static int __init printk_phys_address_setup(char *str) +{ + unsigned long flags; + void *mem_address; + + get_option(&str, &printk_phys_address); + if (!printk_phys_address) + return 0; + + /* temporarly map first few pages of log memory */ + mem_address = early_ioremap(printk_phys_address, EARLY_LOG_PAGES); + if (!mem_address) + return 0; + + printk(KERN_INFO "Logging kernel messages into HW memory at %08x\n", + printk_phys_address); + /* allocate saved log buffer, and save the log memory that we + will otherwise overwrite */ + old_log_buf = alloc_bootmem(__LOG_BUF_LEN); + if (old_log_buf) + memcpy(old_log_buf, mem_address, EARLY_LOG_PAGES); + + /* clear log memory now */ + memset(mem_address, 0, EARLY_LOG_PAGES); + + /* copy current printk buffer to log memory, and switch to new buffer */ + spin_lock_irqsave(&logbuf_lock, flags); + memcpy(mem_address, log_buf, EARLY_LOG_PAGES); + log_buf = mem_address; + log_buf_len = EARLY_LOG_PAGES; + spin_unlock_irqrestore(&logbuf_lock, flags); + + return 1; +} +__setup("printk_address=", printk_phys_address_setup); + +static int printk_phys_address_late(void) +{ + + char *mem_address, *early_mem_address; + unsigned long flags; + struct dentry *dbgfs_dir; + + if (!printk_phys_address) + return 0; + + /* now do late initialization */ + mem_address = ioremap(printk_phys_address, __LOG_BUF_LEN); + + if (!mem_address) { + printk(KERN_ALERT "Can't fully map hardware kernel log memory." + " Log buffer limited to %lu KB\n", EARLY_LOG_PAGES); + return 0; + } + + /* save the rest of log memory, and publish it */ + if (old_log_buf) { + memcpy(old_log_buf + EARLY_LOG_PAGES, + mem_address + EARLY_LOG_PAGES, + __LOG_BUF_LEN - EARLY_LOG_PAGES); + + crash_dmesg_wrapper.data = old_log_buf; + crash_dmesg_wrapper.size = __LOG_BUF_LEN; + + dbgfs_dir = debugfs_create_dir("printk", NULL); + + if (dbgfs_dir > 0) + debugfs_create_blob("crash_dmesg", S_IRUSR, dbgfs_dir, + &crash_dmesg_wrapper); + } + + /* clear rest of the log memory now */ + memset(mem_address + EARLY_LOG_PAGES , 0, + __LOG_BUF_LEN - EARLY_LOG_PAGES); + + + /* switch to the full log memory now */ + spin_lock_irqsave(&logbuf_lock, flags); + early_mem_address = log_buf; + log_buf = mem_address; + log_buf_len = __LOG_BUF_LEN; + spin_unlock_irqrestore(&logbuf_lock, flags); + + /* free temp mapping of the log memory */ + early_iounmap(early_mem_address, EARLY_LOG_PAGES); + return 1; +} +postcore_initcall(printk_phys_address_late); +#endif + #ifdef CONFIG_BOOT_PRINTK_DELAY static unsigned int boot_delay; /* msecs delay after each printk during bootup */ diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index e722e9d..aa714ce 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -758,6 +758,37 @@ config BOOT_PRINTK_DELAY BOOT_PRINTK_DELAY also may cause DETECT_SOFTLOCKUP to detect what it believes to be lockup conditions. +config HWMEM_PRINTK + bool "Log printk message buffer into video ram (DANGEROUS)" + depends on DEBUG_KERNEL && PRINTK + help + This option allows to place kernel log buffer into pre-defined + area, somewhere in memory space. + It is intended to place this buffer into video ram assuming it + isn't cleared on reboot. + This creates some sort of black box recorder and can be very useful + to debug several problems, especially 'panics' that happen while you + use the X window system. + + To use, first ensure that you aren't using X, or that you tell video + driver not to use all the video ram + (easy way is to use the 'vesa' X driver) + + Then, pick an address within the video memory, + (best somewhere in middle), and boot kernel with + printk_address=$ADDRESS + + If you also select debugfs support, you can easily look at + kernel log of failed boot at: + /sys/kernel/debug/printk/crash_dmesg + + (Assuming you mounted debugfs on /sys/kernel/debug) + + Misuse of this option can be DANGEROUS, as it makes kernel write at + arbitary (selected by you) hardware memory range. + + It is only intended for debbuging, so say 'no' if not sure + config RCU_TORTURE_TEST tristate "torture tests for RCU" depends on DEBUG_KERNEL