Merge "tpm: export vlan and ethertype." into prism_dev
diff --git a/arch/arm/configs/gflt200_defconfig b/arch/arm/configs/gflt200_defconfig
index d36faff..2457f5b 100644
--- a/arch/arm/configs/gflt200_defconfig
+++ b/arch/arm/configs/gflt200_defconfig
@@ -78,6 +78,8 @@
 # CONFIG_KALLSYMS_EXTRA_PASS is not set
 CONFIG_HOTPLUG=y
 CONFIG_PRINTK=y
+CONFIG_PRINTK_PERSIST=y
+CONFIG_PRINTK_TIME=y
 CONFIG_BUG=y
 CONFIG_ELF_CORE=y
 CONFIG_BASE_FULL=y
@@ -566,7 +568,7 @@
 #
 CONFIG_ZBOOT_ROM_TEXT=0x0
 CONFIG_ZBOOT_ROM_BSS=0x0
-CONFIG_CMDLINE="console=ttyS0,115200"
+CONFIG_CMDLINE="console=ttyS0,115200 log_buf_len=1048576"
 # CONFIG_XIP_KERNEL is not set
 # CONFIG_KEXEC is not set
 
diff --git a/init/Kconfig b/init/Kconfig
index eb4b337..072d249 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -818,6 +818,18 @@
 	  very difficult to diagnose system problems, saying N here is
 	  strongly discouraged.
 
+config PRINTK_PERSIST
+	default n
+	bool "printk log persists across reboots" if PRINTK
+	help
+	  This option tries to keep the printk memory buffer in a well-known
+	  location in physical memory. It isn't cleared on reboot (unless RAM
+	  is wiped by your boot loader or BIOS) so if your system crashes
+	  or panics, you might get to examine all the log messages next time you
+	  boot. The persisted log messages show up in your 'dmesg' output.
+	  Note: you must supply the log_buf_len= kernel parameter to
+	  activate this feature.
+
 config BUG
 	bool "BUG() support" if EMBEDDED
 	default y
diff --git a/kernel/printk.c b/kernel/printk.c
index d2cde12..ad1a79b 100755
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -71,8 +71,6 @@
 	DEFAULT_CONSOLE_LOGLEVEL,	/* default_console_loglevel */
 };
 
-static int saved_console_loglevel = -1;
-
 /*
  * Low level drivers may need that to know if they can schedule in
  * their unblank() callback or not. So let's export it.
@@ -115,7 +113,12 @@
  */
 static unsigned log_start;	/* Index into log_buf: next char to be read by syslog() */
 static unsigned con_start;	/* Index into log_buf: next char to be sent to consoles */
+
+#ifdef CONFIG_PRINTK_PERSIST
+#define log_end logbits->_log_end
+#else
 static unsigned log_end;	/* Index into log_buf: most-recently-written-char + 1 */
+#endif
 
 /*
  *	Array of consoles built from command line options (console=)
@@ -143,11 +146,6 @@
 
 #ifdef CONFIG_PRINTK
 
-static char __log_buf[__LOG_BUF_LEN];
-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 */
-
 #ifdef CONFIG_KEXEC
 /*
  * This appends the listed symbols to /proc/vmcoreinfo
@@ -166,6 +164,97 @@
 }
 #endif
 
+static int saved_console_loglevel = -1;
+static char __log_buf[__LOG_BUF_LEN];
+static char *log_buf = __log_buf;
+
+#ifndef CONFIG_PRINTK_PERSIST
+
+static int log_buf_len = __LOG_BUF_LEN;
+static unsigned logged_chars; /* Number of chars produced since last read+clear operation */
+
+static __init char *log_buf_alloc(unsigned long size, unsigned *dest_offset)
+{
+	return alloc_bootmem(size);
+}
+
+#else  /* CONFIG_PRINTK_PERSIST */
+
+struct logbits {
+	int magic; /* needed to verify the memory across reboots */
+	int _log_buf_len; /* leading _ so they aren't replaced by #define */
+	unsigned _logged_chars;
+	unsigned _log_end;
+};
+static struct logbits __logbits = {
+	._log_buf_len = __LOG_BUF_LEN,
+};
+static struct logbits *logbits = &__logbits;
+#define log_buf_len logbits->_log_buf_len
+#define logged_chars logbits->_logged_chars
+
+#define PERSIST_SEARCH_START 0
+#define PERSIST_SEARCH_END 0xfe000000
+#define PERSIST_SEARCH_JUMP (16*1024*1024)
+#define PERSIST_MAGIC 0xbabb1e
+
+/*
+ * size is a power of 2 so that the printk offset mask will work.  We'll add
+ * a bit more space to the end of the buffer for our extra data, but that
+ * won't change the offset of the buffers.
+ */
+static __init char *log_buf_alloc(unsigned long size, unsigned *dest_offset)
+{
+	unsigned long where;
+	char *buf;
+	unsigned long full_size = size + sizeof(struct logbits);
+	struct logbits *new_logbits;
+
+	for (where = PERSIST_SEARCH_END - size;
+			where >= PERSIST_SEARCH_START;
+			where -= PERSIST_SEARCH_JUMP) {
+		if (reserve_bootmem(where, full_size, BOOTMEM_EXCLUSIVE))
+			continue;
+
+		buf = phys_to_virt(where);
+		new_logbits = phys_to_virt(where + size);
+		printk(KERN_INFO "printk_persist: memory reserved @ 0x%08lx\n",
+			where);
+		if (new_logbits->magic != PERSIST_MAGIC ||
+				new_logbits->_log_buf_len != size ||
+				new_logbits->_logged_chars > size) {
+			printk(KERN_INFO "printk_persist: header invalid, "
+				"cleared.\n");
+			memset(buf, 0, full_size);
+			new_logbits->magic = PERSIST_MAGIC;
+			new_logbits->_log_buf_len = size;
+			new_logbits->_logged_chars = 0;
+			new_logbits->_log_end = 0;
+		} else {
+			printk(KERN_INFO "printk_persist: header valid; "
+				"logged=%d next=%d\n",
+				new_logbits->_logged_chars,
+				new_logbits->_log_end);
+		}
+		*dest_offset = new_logbits->_log_end;
+		new_logbits->_log_end = log_end;
+		if (new_logbits->_logged_chars + logged_chars <= size)
+			new_logbits->_logged_chars += logged_chars;
+		else
+			new_logbits->_logged_chars = size;
+		logbits = new_logbits;
+		return buf;
+	}
+	goto error;
+
+error:
+	/* replace the buffer, but don't bother to swap struct logbits */
+	printk(KERN_ERR "printk_persist: failed to reserve bootmem "
+		"area. disabled.\n");
+	return alloc_bootmem(full_size);
+}
+#endif  /* CONFIG_PRINTK_PERSIST */
+
 static int __init log_buf_len_setup(char *str)
 {
 	unsigned size = memparse(str, &str);
@@ -174,10 +263,10 @@
 	if (size)
 		size = roundup_pow_of_two(size);
 	if (size > log_buf_len) {
-		unsigned start, dest_idx, offset;
+		unsigned start, dest_offset = 0, dest_idx, offset;
 		char *new_log_buf;
 
-		new_log_buf = alloc_bootmem(size);
+		new_log_buf = log_buf_alloc(size, &dest_offset);
 		if (!new_log_buf) {
 			printk(KERN_WARNING "log_buf_len: allocation failed\n");
 			goto out;
@@ -188,15 +277,16 @@
 		log_buf = new_log_buf;
 
 		offset = start = min(con_start, log_start);
-		dest_idx = 0;
+		dest_idx = dest_offset;
 		while (start != log_end) {
-			log_buf[dest_idx] = __log_buf[start & (__LOG_BUF_LEN - 1)];
+			log_buf[dest_idx & (size - 1)] =
+				__log_buf[start & (__LOG_BUF_LEN - 1)];
 			start++;
 			dest_idx++;
 		}
-		log_start -= offset;
-		con_start -= offset;
-		log_end -= offset;
+		log_start += dest_offset - offset;
+		con_start += dest_offset - offset;
+		log_end += dest_offset - offset;
 		spin_unlock_irqrestore(&logbuf_lock, flags);
 
 		printk(KERN_NOTICE "log_buf_len: %d\n", log_buf_len);
@@ -260,6 +350,7 @@
 }
 #endif
 
+#define COPY_SIZE 4096
 /*
  * Commands to do_syslog:
  *
@@ -277,6 +368,7 @@
  */
 int do_syslog(int type, char __user *buf, int len)
 {
+	char *copybuf;
 	unsigned i, j, limit, count;
 	int do_clear = 0;
 	char c;
@@ -336,6 +428,11 @@
 			error = -EFAULT;
 			goto out;
 		}
+		copybuf = kmalloc(COPY_SIZE, GFP_KERNEL);
+		if (!copybuf) {
+			error = -ENOMEM;
+			goto out;
+		}
 		count = len;
 		if (count > log_buf_len)
 			count = log_buf_len;
@@ -346,7 +443,7 @@
 			logged_chars = 0;
 		limit = log_end;
 		/*
-		 * __put_user() could sleep, and while we sleep
+		 * copy_to_user() could sleep, and while we sleep
 		 * printk() could overwrite the messages
 		 * we try to copy to user space. Therefore
 		 * the messages are copied in reverse. <manfreds>
@@ -356,14 +453,26 @@
 			if (j + log_buf_len < log_end)
 				break;
 			c = LOG_BUF(j);
-			spin_unlock_irq(&logbuf_lock);
-			error = __put_user(c,&buf[count-1-i]);
-			cond_resched();
-			spin_lock_irq(&logbuf_lock);
+			copybuf[COPY_SIZE-1-(i % COPY_SIZE)] = c;
+			if ((i+1) % COPY_SIZE == 0) {
+				spin_unlock_irq(&logbuf_lock);
+				error = copy_to_user(&buf[count-1-i],
+						copybuf, COPY_SIZE);
+				cond_resched();
+				spin_lock_irq(&logbuf_lock);
+			}
 		}
 		spin_unlock_irq(&logbuf_lock);
-		if (error)
-			break;
+		if (!error) {
+			/* in case copybuf was only partially filled */
+			error = copy_to_user(&buf[count-i],
+				copybuf + COPY_SIZE - (i % COPY_SIZE),
+				i % COPY_SIZE);
+		}
+		if (error) {
+			error = -EFAULT;
+			goto copy_done;
+		}
 		error = i;
 		if (i != count) {
 			int offset = count-error;
@@ -377,6 +486,8 @@
 				cond_resched();
 			}
 		}
+copy_done:
+		kfree(copybuf);
 		break;
 	case 5:		/* Clear ring buffer */
 		logged_chars = 0;
@@ -757,7 +868,6 @@
 			emit_log_char('<');
 			emit_log_char(current_log_level + '0');
 			emit_log_char('>');
-			printed_len += 3;
 			new_text_line = 0;
 
 			if (printk_time) {
@@ -775,7 +885,6 @@
 
 				for (tp = tbuf; tp < tbuf + tlen; tp++)
 					emit_log_char(*tp);
-				printed_len += tlen;
 			}
 
 			if (!*p)