ELOG: Support for non-memory mapped flash

If the event log is stored in flash that is not memory
mapped then it must use the SPI controller to read from
the flash device instead of relying on memory accesses.

In addition a new CBMEM ID is added to keep an resident
copy of the ELOG around if needed.  The use of CBMEM for
this is guarded by a new CONFIG_ELOG_CBMEM config option.
This CBMEM buffer is created and filled late in the process
when the SMBIOS table is being created because CBMEM is
not functional when ELOG is first initialized.

The downside to using CBMEM is that events added via the
SMI handler at runtime are not reflected in the CBMEM copy
because I don't want to let the SMM handler write to memory
outside the TSEG region.

In reality the only time we add runtime events is at kernel
shutdown so the impact is limited.

Test:
1) Test with CONFIG_ELOG_CBMEM enabled to ensure the event
log is operational and SMBIOS points to address in CBMEM.
The test should involve at least on reboot to ensure that the
kernel is able to write events as well.

> mosys -l smbios info log | grep ^address
address              | 0xacedd000

> mosys eventlog list
0 | 2012-10-10 14:02:46 | Log area cleared | 4096
1 | 2012-10-10 14:02:46 | System boot | 478
2 | 2012-10-10 14:02:46 | System Reset
3 | 2012-10-10 14:03:33 | Kernel Event | Clean Shutdown
4 | 2012-10-10 14:03:34 | System boot | 479
5 | 2012-10-10 14:03:34 | System Reset

2) Test with CONFIG_ELOG_CBMEM disabled to ensure the event
log is operational and SMBIOS points to memory mapped flash.
The test should involve at least on reboot to ensure that the
kernel is able to write events as well.

> mosys -l smbios info log | grep ^address
address              | 0xffbf0000

> mosys eventlog list
0 | 2012-10-10 14:33:17 | Log area cleared | 4096
1 | 2012-10-10 14:33:18 | System boot | 480
2 | 2012-10-10 14:33:18 | System Reset
3 | 2012-10-10 14:33:35 | Kernel Event | Clean Shutdown
4 | 2012-10-10 14:33:36 | System boot | 481
5 | 2012-10-10 14:33:36 | System Reset

Change-Id: I87755d5291ce209c1e647792227c433dc966615d
Signed-off-by: Duncan Laurie <dlaurie@chromium.org>
Reviewed-on: http://review.coreboot.org/1776
Tested-by: build bot (Jenkins)
Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
diff --git a/src/drivers/elog/elog.c b/src/drivers/elog/elog.c
index 2f6beef..e6baace 100644
--- a/src/drivers/elog/elog.c
+++ b/src/drivers/elog/elog.c
@@ -18,6 +18,7 @@
  */
 
 #include <arch/acpi.h>
+#include <cbmem.h>
 #include <console/console.h>
 #include <pc80/mc146818rtc.h>
 #include <smbios.h>
@@ -267,18 +268,23 @@
  */
 static void elog_flash_write(u8 *address, u8 *buffer, u32 size)
 {
+	struct elog_descriptor *flash = elog_get_flash();
 	u32 offset;
 
 	if (!address || !buffer || !size || !elog_spi)
 		return;
 
-	offset = elog_flash_address_to_offset(address);
+	offset = flash->flash_base;
+	offset += address - (u8*)flash->backing_store;
 
 	elog_debug("elog_flash_write(address=0x%p offset=0x%08x buffer=0x%p "
 		   "size=%u)\n", address, offset, buffer, size);
 
 	/* Write the data to flash */
 	elog_spi->write(elog_spi, offset, size, buffer);
+
+	/* Update the copy in memory */
+	memcpy(address, buffer, size);
 }
 
 /*
@@ -287,12 +293,14 @@
  */
 static void elog_flash_erase(u8 *address, u32 size)
 {
+	struct elog_descriptor *flash = elog_get_flash();
 	u32 offset;
 
 	if (!address || !size || !elog_spi)
 		return;
 
-	offset = elog_flash_address_to_offset(address);
+	offset = flash->flash_base;
+	offset += address - (u8*)flash->backing_store;
 
 	elog_debug("elog_flash_erase(address=0x%p offset=0x%08x size=%u)\n",
 		   address, offset, size);
@@ -395,6 +403,10 @@
 	elog->backing_store = buffer;
 	elog->total_size = size;
 
+	/* Fill memory buffer by reading from SPI */
+	if (type == ELOG_DESCRIPTOR_FLASH)
+		elog_spi->read(elog_spi, elog->flash_base, size, buffer);
+
 	/* Get staging header from backing store */
 	elog->staging_header = header;
 	memcpy(header, buffer, sizeof(struct elog_header));
@@ -444,11 +456,12 @@
 		return -1;
 	}
 
-	area = elog_flash_offset_to_address(flash_base);
+	area = malloc(area_size);
 	if (!area) {
 		printk(BIOS_ERR, "ELOG: Unable to determine flash address\n");
 		return -1;
 	}
+	elog_get_flash()->flash_base = flash_base;
 	elog_init_descriptor(elog_get_flash(), ELOG_DESCRIPTOR_FLASH,
 			     area, area_size, staging_header);
 
@@ -472,6 +485,7 @@
 	elog_debug("elog_flash_erase_area()\n");
 
 	elog_flash_erase(elog->backing_store, elog->total_size);
+	memset(elog->backing_store, ELOG_TYPE_EOL, elog->total_size);
 	elog_reinit_descriptor(elog);
 }
 
@@ -516,12 +530,13 @@
 	/* Fill with empty pattern first */
 	memset(mem->backing_store, ELOG_TYPE_EOL, mem->total_size);
 
-	/* Copy the header to memory */
-	memcpy(mem->backing_store, flash->backing_store,
-	       sizeof(struct elog_header));
+	/* Read the header from SPI to memory */
+	elog_spi->read(elog_spi, flash->flash_base,
+		       sizeof(struct elog_header), mem->backing_store);
 
-	/* Copy the valid flash contents to memory */
-	memcpy(mem->data, flash->data, flash->next_event_offset);
+	/* Read the valid flash contents from SPI to memory */
+	elog_spi->read(elog_spi, flash->flash_base + sizeof(struct elog_header),
+		       flash->next_event_offset, mem->data);
 
 	elog_reinit_descriptor(mem);
 
@@ -679,26 +694,40 @@
 	return elog_spi ? 0 : -1;
 }
 
+#ifndef __SMM__
 /*
  * Fill out SMBIOS Type 15 table entry so the
  * event log can be discovered at runtime.
  */
 int elog_smbios_write_type15(unsigned long *current, int handle)
 {
+	struct elog_descriptor *flash = elog_get_flash();
 	struct smbios_type15 *t = (struct smbios_type15 *)*current;
 	int len = sizeof(struct smbios_type15);
 
+#if CONFIG_ELOG_CBMEM
+	/* Save event log buffer into CBMEM for the OS to read */
+	void *cbmem = cbmem_add(CBMEM_ID_ELOG, flash->total_size);
+	if (!cbmem)
+		return 0;
+	memcpy(cbmem, flash->backing_store, flash->total_size);
+#endif
+
 	memset(t, 0, len);
 	t->type = SMBIOS_EVENT_LOG;
 	t->length = len - 2;
 	t->handle = handle;
-	t->area_length = elog_get_flash()->total_size - 1;
+	t->area_length = flash->total_size - 1;
 	t->header_offset = 0;
 	t->data_offset = sizeof(struct elog_header);
 	t->access_method = SMBIOS_EVENTLOG_ACCESS_METHOD_MMIO32;
 	t->log_status = SMBIOS_EVENTLOG_STATUS_VALID;
 	t->change_token = 0;
-	t->address = (u32)elog_get_flash()->backing_store;
+#if CONFIG_ELOG_CBMEM
+	t->address = (u32)cbmem;
+#else
+	t->address = (u32)elog_flash_offset_to_address(flash->flash_base);
+#endif
 	t->header_format = ELOG_HEADER_TYPE_OEM;
 	t->log_type_descriptors = 0;
 	t->log_type_descriptor_length = 2;
@@ -706,6 +735,7 @@
 	*current += len;
 	return len;
 }
+#endif
 
 /*
  * Clear the entire event log
@@ -790,9 +820,9 @@
 
 	elog_initialized = 1;
 
-	printk(BIOS_INFO, "ELOG: MEM @0x%p FLASH @0x%p\n",
+	printk(BIOS_INFO, "ELOG: MEM @0x%p FLASH @0x%p [SPI 0x%08x]\n",
 	       elog_get_mem()->backing_store,
-	       elog_get_flash()->backing_store);
+	       elog_get_flash()->backing_store, elog_get_flash()->flash_base);
 
 	printk(BIOS_INFO, "ELOG: areas are %d bytes, full threshold %d,"
 	       " shrink size %d\n", CONFIG_ELOG_AREA_SIZE,