This is a squashed commit of the printk_persist changes.

	- Add PRINTK_PERSIST support to new kernel.
	- Port over BOOTLOG_COPY from oldkernel.
	- Change the magic number for the new kernel.
	- Fix build error when CONFIG_PRINTK_PERSIST=y is removed
	- Use crc for printk_persist corruption check
	- Fixed b/20084294 printk data validation problem
	- armada printk_persist: this seems to work
	- Copy physmem_reserve change from kernel/mindspeed.

Change-Id: I2ff04cb6986df39fa830c869d8bb00d2d91ba079
diff --git a/init/Kconfig b/init/Kconfig
index 8b9521a..d7a54a9 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1343,6 +1343,27 @@
 	  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 BOOTLOG_COPY
+	default n
+	bool "copy boot log to printk log" if PRINTK
+	help
+	  This option copies the boot log stored in bootlog memory and save it
+	  in the printk log.
+	  Note: you must supply the bootlog= and log_buf_len= kernel parameters
+	  to activate this feature.
+
 config BUG
 	bool "BUG() support" if EXPERT
 	default y
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 8c086e6..ff9937c 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -45,6 +45,8 @@
 #include <linux/poll.h>
 #include <linux/irq_work.h>
 #include <linux/utsname.h>
+#include <linux/ctype.h>
+#include <linux/crc32.h>
 
 #include <asm/uaccess.h>
 
@@ -201,6 +203,9 @@
 	u16 text_len;		/* length of text buffer */
 	u16 dict_len;		/* length of dictionary buffer */
 	u8 facility;		/* syslog facility */
+#ifdef CONFIG_PRINTK_PERSIST
+	u32 crc;		/* the flags may change, so don't include them */
+#endif
 	u8 flags:5;		/* internal record flags */
 	u8 level:3;		/* syslog level */
 };
@@ -213,6 +218,99 @@
 
 #ifdef CONFIG_PRINTK
 DECLARE_WAIT_QUEUE_HEAD(log_wait);
+
+#define PREFIX_MAX		32
+#define LOG_LINE_MAX		1024 - PREFIX_MAX
+
+/* record buffer */
+#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
+#define LOG_ALIGN 4
+#else
+#define LOG_ALIGN __alignof__(struct printk_log)
+#endif
+#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
+static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN);
+static char *log_buf = __log_buf;
+
+static int log_make_free_space(u32 msg_size);
+static void log_store(int facility, int level,
+		     enum log_flags flags, u64 ts_nsec,
+		     const char *dict, u16 dict_len,
+		     const char *text, u16 text_len);
+
+#ifdef CONFIG_BOOTLOG_COPY
+#define BOOTLOG_MAGIC (0x1090091e)
+struct bloghdr {
+	unsigned int magic; /* for kernel verification */
+	unsigned int offset; /* current log offset */
+};
+
+extern unsigned long bootlog_get_addr(void);
+extern unsigned long bootlog_get_size(void);
+
+static __init inline struct bloghdr *get_bootlog_hdr(void)
+{
+	unsigned long bootlog_size = bootlog_get_size();
+	if (bootlog_size) {
+		struct bloghdr *blog_hdr = (struct bloghdr *)
+				phys_to_virt(bootlog_get_addr());
+		if (BOOTLOG_MAGIC != blog_hdr->magic ||
+		    (blog_hdr->offset + sizeof(struct bloghdr) >
+                     bootlog_size)) {
+			printk(KERN_INFO "bootlog: header invalid m:0x%08x "
+			       "o:0x%08x s:0x%08lx\n", blog_hdr->magic,
+                               blog_hdr->offset, bootlog_size);
+			return NULL;
+		}
+		return blog_hdr;
+	}
+        printk(KERN_INFO "bootlog: bootlog_size was 0.\n");
+	return NULL;
+}
+
+#define printable(c) (((c)=='\t') || ((c)=='\n') || (0x20<=(c) && (c)<=0x7e))
+
+static inline void __init copy_bootlog(struct bloghdr *blog_hdr)
+{
+	if (blog_hdr) {
+		char *blog_buf = (char *)(blog_hdr + 1);
+		int i,j;
+		char *tmp;
+		/* Strip out nonprintable characters from the bootlog. */
+		for (i=0,j=0; i < blog_hdr->offset; i++) {
+			if (printable(blog_buf[i])) {
+				blog_buf[j++] = blog_buf[i];
+			}
+		}
+		/* Loop over each line in the boot loader log, and insert them into the
+		 * kernel log one at a time.  Attempting to insert the entire thing fails due
+		 * to some logic that truncates long messages.
+		 */
+		tmp = &blog_buf[0];
+		for (i=0; i < j; i++) {
+			if (blog_buf[i] == '\n') {
+				/* Insert with facility=0, level=INFO, time=0, No dict. */
+				log_store(0, 6, LOG_NEWLINE, 0,
+					NULL, 0, tmp, &blog_buf[i] - tmp);
+				tmp = &blog_buf[i+1];
+			}
+		}
+
+		/* Insert any trailing line into the kernel buffer. */
+		if (tmp != &blog_buf[i]) {
+			log_store(0, 6, 0, 0,
+				NULL, 0, tmp, &blog_buf[i] - tmp);
+		}
+	}
+}
+
+static inline void __init free_bootlog(void)
+{
+	free_bootmem(bootlog_get_addr(), bootlog_get_size());
+}
+#endif
+
+#ifndef CONFIG_PRINTK_PERSIST
 /* the next printk record to read by syslog(READ) or /proc/kmsg */
 static u64 syslog_seq;
 static u32 syslog_idx;
@@ -236,20 +334,197 @@
 static u64 clear_seq;
 static u32 clear_idx;
 
-#define PREFIX_MAX		32
-#define LOG_LINE_MAX		1024 - PREFIX_MAX
-
-/* record buffer */
-#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
-#define LOG_ALIGN 4
-#else
-#define LOG_ALIGN __alignof__(struct printk_log)
-#endif
-#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
-static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN);
-static char *log_buf = __log_buf;
 static u32 log_buf_len = __LOG_BUF_LEN;
 
+#else  /*  CONFIG_PRINTK_PERSIST */
+
+struct logbits {
+	int magic; /* needed to verify the memory across reboots */
+	u32 _log_buf_len;
+	u64 _syslog_seq; /* leading _ so they aren't replaced by #define */
+	u32 _syslog_idx;
+	enum log_flags _syslog_prev;
+	size_t _syslog_partial;
+
+/* index and sequence number of the first record stored in the buffer */
+	u64 _log_first_seq;
+	u32 _log_first_idx;
+
+/* index and sequence number of the next record to store in the buffer */
+	u64 _log_next_seq;
+	u32 _log_next_idx;
+
+/* the next printk record to write to the console */
+	u64 _console_seq;
+	u32 _console_idx;
+	enum log_flags _console_prev;
+
+/* the next printk record to read after the last 'clear' command */
+	u64 _clear_seq;
+	u32 _clear_idx;
+};
+
+static struct logbits __logbits = {
+	._log_buf_len = __LOG_BUF_LEN,
+};
+static struct logbits *logbits = &__logbits;
+
+#define log_buf_len    logbits->_log_buf_len
+#define syslog_seq     logbits->_syslog_seq
+#define syslog_idx     logbits->_syslog_idx
+#define syslog_prev    logbits->_syslog_prev
+#define syslog_partial logbits->_syslog_partial
+#define log_first_seq  logbits->_log_first_seq
+#define log_first_idx  logbits->_log_first_idx
+#define log_next_seq   logbits->_log_next_seq
+#define log_next_idx   logbits->_log_next_idx
+#define console_seq    logbits->_console_seq
+#define console_idx    logbits->_console_idx
+#define console_prev   logbits->_console_prev
+#define clear_seq      logbits->_clear_seq
+#define clear_idx      logbits->_clear_idx
+
+#define PERSIST_SEARCH_START 0
+#ifdef CONFIG_NO_BOOTMEM
+#define PERSIST_SEARCH_END 0x5e000000
+#else
+#define PERSIST_SEARCH_END 0xfe000000
+#endif
+#define PERSIST_SEARCH_JUMP (16*1024*1024)
+#define PERSIST_MAGIC 0xba5eba11
+
+/*
+ * arm uses one memory model, mips uses another
+ */
+static __init phys_addr_t physmem_reserve(phys_addr_t size) {
+#ifdef CONFIG_NO_BOOTMEM
+	phys_addr_t alloc;
+	alloc = memblock_find_in_range_node(size, SMP_CACHE_BYTES,
+			PERSIST_SEARCH_START, PERSIST_SEARCH_END,
+			NUMA_NO_NODE);
+	if (!alloc) return alloc;
+	if (memblock_reserve(alloc, size)) {
+		pr_err("printk_persist: memblock_reserve failed\n");
+		return 0;
+	}
+	return alloc;
+#else
+	unsigned long where;
+	for (where = PERSIST_SEARCH_END - size;
+	     where >= PERSIST_SEARCH_START && where <= PERSIST_SEARCH_END - size;
+	     where -= PERSIST_SEARCH_JUMP) {
+		if (reserve_bootmem(where, size, BOOTMEM_EXCLUSIVE))
+			continue;
+		else
+			return where;
+	}
+	return 0;
+#endif
+}
+
+/*
+ * 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 struct logbits *log_buf_alloc(unsigned long size, char **new_logbuf)
+{
+	char *buf;
+	phys_addr_t alloc;
+	unsigned long full_size = size + sizeof(struct logbits);
+	struct logbits *new_logbits;
+	u64 seq;
+	int idx, lost_entries;
+	struct printk_log *prlog;
+	u32 curr_crc;
+
+	alloc = physmem_reserve(full_size);
+	if (alloc) {
+		buf = phys_to_virt(alloc);
+		*new_logbuf = buf;
+		new_logbits = (void*)buf + size;
+		printk(KERN_INFO "printk_persist: memory reserved @ 0x%08llx\n",
+			alloc);
+		if ((new_logbits->magic != PERSIST_MAGIC) ||
+		    (new_logbits->_log_buf_len != size) ||
+			(new_logbits->_log_first_seq >
+				new_logbits->_log_next_seq) ||
+			(new_logbits->_log_first_idx > size) ||
+			(new_logbits->_syslog_idx > size) ||
+			(new_logbits->_syslog_seq >
+				new_logbits->_log_next_seq) ||
+			(new_logbits->_log_next_idx > size) ||
+			(new_logbits->_console_seq >
+				new_logbits->_log_next_seq) ||
+			(new_logbits->_console_idx > size) ||
+			(new_logbits->_clear_seq >
+				new_logbits->_log_next_seq) ||
+			(new_logbits->_clear_idx > size)) {
+			printk(KERN_INFO "printk_persist: header invalid, "
+				"cleared.\n");
+			memset(buf, 0, full_size);
+			memset(new_logbits, 0, sizeof(*new_logbits));
+			new_logbits->magic = PERSIST_MAGIC;
+			new_logbits->_log_buf_len = size;
+		} else {
+			printk(KERN_INFO "printk_persist: header valid; "
+				"log_first_idx=%d\n"
+				"log_next_idx=%d\n"
+				"log_first_seq=%lld\n"
+				"log_next_seq=%lld\n"
+				"console_seq=%lld\n"
+				"console_idx=%d\n"
+				"syslog_seq=%lld\n"
+				"syslog_idx=%d\n",
+				new_logbits->_log_first_idx,
+				new_logbits->_log_next_idx,
+				new_logbits->_log_first_seq,
+				new_logbits->_log_next_seq,
+				new_logbits->_console_seq,
+				new_logbits->_console_idx,
+				new_logbits->_syslog_seq,
+				new_logbits->_syslog_idx);
+			printk(KERN_INFO "printk_persist: validating records\n");
+			for (seq = new_logbits->_log_first_seq, idx = new_logbits->_log_first_idx;
+				seq < new_logbits->_log_next_seq; ++seq) {
+				prlog = (struct printk_log *) &buf[idx];
+				// Verify validity of this record. If its bad, then move the next
+				// pointer to be where this record is at.
+				curr_crc = crc32(~0, prlog, offsetof(struct printk_log, crc));
+				if (prlog->crc != curr_crc) {
+					lost_entries = (int)(new_logbits->_log_next_seq - seq);
+					printk(KERN_INFO "printk_persist: corruption found, lost %d entries\n",
+						lost_entries);
+					new_logbits->_log_next_seq = seq;
+					new_logbits->_log_next_idx = idx;
+					break;
+				}
+				if (prlog->len == 0) {
+					idx = 0;
+					// Do not increment the sequence counter for the
+					// record that's used to indicate wrapping. We
+					// do still want to verify its CRC above though.
+					--seq;
+				}
+				else
+					idx += prlog->len;
+			}
+		}
+		return new_logbits;
+	} else {
+		/* replace the buffer, but don't bother to swap struct logbits */
+		printk(KERN_ERR "printk_persist: failed to reserve bootmem "
+			"area. disabled.\n");
+		buf = alloc_bootmem(full_size);
+		*new_logbuf = buf;
+		new_logbits = (struct logbits*)(buf + size);
+		memset(buf, 0, full_size);
+    }
+
+	return new_logbits;
+}
+#endif
+
 /* cpu currently holding logbuf_lock */
 static volatile unsigned int logbuf_cpu = UINT_MAX;
 
@@ -297,6 +572,48 @@
 	return idx + msg->len;
 }
 
+/*
+ * Check whether there is enough free space for the given message.
+ *
+ * The same values of first_idx and next_idx mean that the buffer
+ * is either empty or full.
+ *
+ * If the buffer is empty, we must respect the position of the indexes.
+ * They cannot be reset to the beginning of the buffer.
+ */
+static int logbuf_has_space(u32 msg_size, bool empty)
+{
+	u32 free;
+
+	if (log_next_idx > log_first_idx || empty)
+		free = max(log_buf_len - log_next_idx, log_first_idx);
+	else
+		free = log_first_idx - log_next_idx;
+
+	/*
+	 * We need space also for an empty header that signalizes wrapping
+	 * of the buffer.
+	 */
+	return free >= msg_size + sizeof(struct printk_log);
+}
+
+static int log_make_free_space(u32 msg_size)
+{
+	while (log_first_seq < log_next_seq) {
+		if (logbuf_has_space(msg_size, false))
+			return 0;
+		/* drop old messages until we have enough contiguous space */
+		log_first_idx = log_next(log_first_idx);
+		log_first_seq++;
+	}
+
+	/* sequence numbers are equal, so the log buffer is empty */
+	if (logbuf_has_space(msg_size, true))
+		return 0;
+
+	return -ENOMEM;
+}
+
 /* insert record into the buffer, discard old ones, update heads */
 static void log_store(int facility, int level,
 		      enum log_flags flags, u64 ts_nsec,
@@ -334,6 +651,10 @@
 		 * to signify a wrap around.
 		 */
 		memset(log_buf + log_next_idx, 0, sizeof(struct printk_log));
+#ifdef CONFIG_PRINTK_PERSIST
+		((struct printk_log*) (log_buf + log_next_idx))->crc =
+			crc32(~0, log_buf + log_next_idx, offsetof(struct printk_log, crc));
+#endif
 		log_next_idx = 0;
 	}
 
@@ -352,6 +673,9 @@
 		msg->ts_nsec = local_clock();
 	memset(log_dict(msg) + dict_len, 0, pad_len);
 	msg->len = sizeof(struct printk_log) + text_len + dict_len + pad_len;
+#ifdef CONFIG_PRINTK_PERSIST
+	msg->crc = crc32(~0, msg, offsetof(struct printk_log, crc));
+#endif
 
 	/* insert message */
 	log_next_idx += msg->len;
@@ -752,16 +1076,30 @@
 	unsigned long flags;
 	char *new_log_buf;
 	int free;
+#ifdef CONFIG_PRINTK_PERSIST
+	struct logbits *new_logbits;
+	struct logbits *old_logbits;
+	struct printk_log *prlog;
+	int seq, idx;
+	int console_found=0, syslog_found=0;
+#endif
+#ifdef CONFIG_BOOTLOG_COPY
+	struct bloghdr *blog_hdr = NULL;
+#endif
 
 	if (!new_log_buf_len)
 		return;
 
+#ifndef CONFIG_PRINTK_PERSIST
 	if (early) {
 		new_log_buf =
 			memblock_virt_alloc(new_log_buf_len, PAGE_SIZE);
 	} else {
 		new_log_buf = memblock_virt_alloc_nopanic(new_log_buf_len, 0);
 	}
+#else
+	new_logbits = log_buf_alloc(new_log_buf_len, &new_log_buf);
+#endif
 
 	if (unlikely(!new_log_buf)) {
 		pr_err("log_buf_len: %ld bytes not available\n",
@@ -769,14 +1107,93 @@
 		return;
 	}
 
+#ifdef CONFIG_BOOTLOG_COPY
+	/* Read out the blog_hdr before logbuf is locked in case print
+	 * is needed. */
+	blog_hdr = get_bootlog_hdr();
+#endif
+
 	raw_spin_lock_irqsave(&logbuf_lock, flags);
 	log_buf_len = new_log_buf_len;
 	log_buf = new_log_buf;
 	new_log_buf_len = 0;
 	free = __LOG_BUF_LEN - log_next_idx;
+
+#ifndef CONFIG_PRINTK_PERSIST
 	memcpy(log_buf, __log_buf, __LOG_BUF_LEN);
+#else
+	/* We have to copy over entries one at a time from the old
+	 * buffer to the new buffer.
+	 */
+	old_logbits = logbits;
+	logbits = new_logbits;
+
+#ifdef CONFIG_BOOTLOG_COPY
+	copy_bootlog(blog_hdr);
+#endif
+
+	for (seq = old_logbits->_log_first_seq, idx = old_logbits->_log_first_idx;
+		 seq < old_logbits->_log_next_seq; ++seq) {
+		prlog = (struct printk_log *)&__log_buf[idx];
+		if (prlog->len == 0) {
+			idx = 0;
+			prlog = (struct printk_log *)&__log_buf[0];
+		}
+
+		if (log_make_free_space(prlog->len)) {
+			pr_err("not copying entry due to it being too huge\n");
+			idx += prlog->len;
+			continue;
+		}
+
+		if (log_next_idx + prlog->len + sizeof(struct printk_log) > log_buf_len) {
+			/*
+			 * This message + an additional empty header does not fit
+			 * at the end of the buffer. Add an empty header with len == 0
+			 * to signify a wrap around.
+			 */
+			memset(log_buf + log_next_idx, 0, sizeof(struct printk_log));
+#ifdef CONFIG_PRINTK_PERSIST
+			((struct printk_log*) (log_buf + log_next_idx))->crc =
+				crc32(~0, log_buf + log_next_idx, offsetof(struct printk_log, crc));
+#endif
+			log_next_idx = 0;
+		}
+		memcpy(&log_buf[log_next_idx], &__log_buf[idx], prlog->len);
+
+		if (old_logbits->_syslog_seq == seq) {
+			syslog_seq = log_next_seq;
+			syslog_idx = log_next_idx;
+			syslog_found = 1;
+		}
+
+		if (old_logbits->_console_seq == seq) {
+			console_seq = log_next_seq;
+			console_idx = log_next_idx;
+			console_found = 1;
+		}
+
+		idx += prlog->len;
+		log_next_seq++;
+		log_next_idx += prlog->len;
+	}
+
+	if (!syslog_found) {
+		syslog_seq = log_next_seq;
+		syslog_idx = log_next_idx;
+	}
+
+	if (!console_found) {
+		console_seq = log_next_seq;
+		console_idx = log_next_idx;
+	}
+#endif
 	raw_spin_unlock_irqrestore(&logbuf_lock, flags);
 
+#ifdef CONFIG_BOOTLOG_COPY
+	free_bootlog();
+#endif
+
 	pr_info("log_buf_len: %d\n", log_buf_len);
 	pr_info("early log buf free: %d(%d%%)\n",
 		free, (free * 100) / __LOG_BUF_LEN);