commit 8f1c423046c22dad6aaeca04bfcb0ab301843c36
Author: Maxim Levitsky <maximlevitsky@gmail.com>
Date:   Sat Jul 31 13:43:03 2010 +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 <maximlevitsky@gmail.com>

diff --git a/arch/x86/include/asm/setup.h b/arch/x86/include/asm/setup.h
index ef292c7..a6eaf40 100644
--- a/arch/x86/include/asm/setup.h
+++ b/arch/x86/include/asm/setup.h
@@ -46,6 +46,7 @@ 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 void early_reserve_printk_buffer(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 c3a4fbb..6fbf2a0 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -843,6 +843,7 @@ void __init setup_arch(char **cmdline_p)
 
 	/* after early param, so could get panic from serial */
 	reserve_early_setup_data();
+	early_reserve_printk_buffer();
 
 	if (acpi_mps_check()) {
 #ifdef CONFIG_X86_LOCAL_APIC
diff --git a/arch/x86/mm/ioremap.c b/arch/x86/mm/ioremap.c
index 3ba6e06..8854a91 100644
--- a/arch/x86/mm/ioremap.c
+++ b/arch/x86/mm/ioremap.c
@@ -106,7 +106,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 8fe465a..a6fff63 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -41,6 +41,7 @@
 #include <linux/notifier.h>
 
 #include <asm/uaccess.h>
+#include <linux/debugfs.h>
 
 /*
  * for_each_console() allows you to iterate on each console
@@ -167,6 +168,7 @@ void log_buf_kexec_setup(void)
 }
 #endif
 
+#ifndef CONFIG_HWMEM_PRINTK
 static int __init log_buf_len_setup(char *str)
 {
 	unsigned size = memparse(str, &str);
@@ -207,6 +209,93 @@ out:
 }
 
 __setup("log_buf_len=", log_buf_len_setup);
+#endif
+
+#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);
+
+
+void early_reserve_printk_buffer(void)
+{
+	if (printk_phys_address)
+		reserve_early(printk_phys_address,
+			printk_phys_address + __LOG_BUF_LEN, "printk buffer");
+}
+
+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 early_reserve_printk_buffer(void) {}
+#endif
 
 #ifdef CONFIG_BOOT_PRINTK_DELAY
 
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 1b4afd2..1f12584 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -788,6 +788,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
