commit edd00a7b0f4e879b43a0b2784eeedbbcbb55d895 Author: Maxim Levitsky Date: Mon Jun 27 01:06:36 2011 +0300 printk: Allow to fix the physical address of printk buffer Allows to put printk buffer at fixed location of ram (default 128M). 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/arch/x86/include/asm/setup.h b/arch/x86/include/asm/setup.h index 9756551..b12603f 100644 --- a/arch/x86/include/asm/setup.h +++ b/arch/x86/include/asm/setup.h @@ -46,6 +46,8 @@ extern unsigned long saved_video_mode; extern void reserve_standard_io_resources(void); extern void i386_reserve_resources(void); extern void setup_default_timer_irq(void); +extern unsigned int printk_buffer_address(void); +extern unsigned int printk_buffer_len(void); #ifdef CONFIG_X86_MRST extern void x86_mrst_early_setup(void); diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index afaf384..2b3867a 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -817,6 +817,12 @@ void __init setup_arch(char **cmdline_p) /* after early param, so could get panic from serial */ memblock_x86_reserve_range_setup_data(); + if (printk_buffer_address()) + memblock_x86_reserve_range(printk_buffer_address(), + printk_buffer_address() + printk_buffer_len(), + "printk buffer"); + + if (acpi_mps_check()) { #ifdef CONFIG_X86_LOCAL_APIC disable_apic = 1; diff --git a/arch/x86/mm/ioremap.c b/arch/x86/mm/ioremap.c index be1ef57..e77f049 100644 --- a/arch/x86/mm/ioremap.c +++ b/arch/x86/mm/ioremap.c @@ -99,7 +99,6 @@ static void __iomem *__ioremap_caller(resource_size_t phys_addr, if (is_ram && pfn_valid(pfn) && !PageReserved(pfn_to_page(pfn))) return NULL; - WARN_ON_ONCE(is_ram); } /* diff --git a/kernel/printk.c b/kernel/printk.c index 3518539..09c3fba 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -43,6 +43,7 @@ #include #include +#include /* * Architectures can override it: @@ -237,6 +238,96 @@ void __init setup_log_buf(int early) free, (free * 100) / __LOG_BUF_LEN); } + +unsigned int printk_buffer_len(void) +{ + return __LOG_BUF_LEN; +} + +#ifdef CONFIG_HWMEM_PRINTK + +char *old_log_buf; +struct debugfs_blob_wrapper crash_dmesg_wrapper; +static unsigned int printk_phys_address = CONFIG_HWMEM_PRINTK_DEFAULT_ADDRESS; + + +static int __init printk_address_setup(char *p) +{ + char *tmp; + + if (!strncmp(p, "off", 3)) + printk_phys_address = 0; + else + printk_phys_address = memparse(p, &tmp); + return 0; +} +early_param("printk_address", printk_address_setup); + + +unsigned int printk_buffer_address(void) +{ + return printk_phys_address; +} + +static int printk_move_to_fixed_address(void) +{ + + char *mem_address; + unsigned long flags; + struct dentry *dbgfs_dir; + + if (!printk_phys_address) + return 0; + + mem_address = ioremap(printk_phys_address, __LOG_BUF_LEN); + + if (!mem_address) { + printk(KERN_ALERT "Can't map hardware kernel log memory." + "printk buffer disabled\n"); + return 0; + } + + printk(KERN_INFO "Logging kernel messages into HW memory at 0x%08x\n", + printk_phys_address); + + /* allocate saved log buffer, and save the log memory that we + will otherwise overwrite */ + old_log_buf = kmalloc(__LOG_BUF_LEN, GFP_KERNEL); + if (old_log_buf) + memcpy(old_log_buf, mem_address, __LOG_BUF_LEN); + + + /* copy current log to the new memory */ + memcpy(mem_address, log_buf, __LOG_BUF_LEN); + + /* save the log memory, and publish it */ + if (old_log_buf) { + + 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); + } + + + + /* switch to the full log memory now */ + spin_lock_irqsave(&logbuf_lock, flags); + log_buf = mem_address; + spin_unlock_irqrestore(&logbuf_lock, flags); + + return 1; +} +postcore_initcall(printk_move_to_fixed_address); + +#else +void printk_buffer_address(void) { return 0; } +#endif + #ifdef CONFIG_BOOT_PRINTK_DELAY static int boot_delay; /* msecs delay after each printk during bootup */ diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index dd373c8..9a87ef8 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -869,6 +869,35 @@ 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 fixed physical address (DANGEROUS)" + depends on DEBUG_KERNEL && PRINTK + help + This option allows to place kernel log buffer into pre-defined + area, somewhere in memory space. + + 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. + + + 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 HWMEM_PRINTK_DEFAULT_ADDRESS + hex + depends on HWMEM_PRINTK + prompt "Default address at which store the printk buffer (default 60M)" + default "0x3C00000" + config RCU_TORTURE_TEST tristate "torture tests for RCU" depends on DEBUG_KERNEL