Implement VbExLegacy()

The VbExLegacy() functions was not implemented preventing boards
using depthcharge as a boot loader to fail Ctrl+L legacy boot.
Implement this functionality. The arch_final_cleanup() was
implemented on the two architectures so that legacy boot
could properly cleanup before handoff.

BUG=chrome-os-partner:19691
BUG=chrome-os-partner:16685
BRANCH=None
TEST=Enabled SeaBIOS serial and noted messages from SeaBIOS after
     invoking Ctrl+L at dev screen on wtm2.

Change-Id: I55d5c02e8c08db1eb2d8b72a09c9c0072ad0ee1e
Signed-off-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/56636
Reviewed-by: Duncan Laurie <dlaurie@chromium.org>
Reviewed-by: Stefan Reinauer <reinauer@google.com>
diff --git a/src/arch/arm/boot.c b/src/arch/arm/boot.c
index 5f2fe0d..446afa9 100644
--- a/src/arch/arm/boot.c
+++ b/src/arch/arm/boot.c
@@ -24,6 +24,7 @@
 
 #include "arch/arm/boot.h"
 #include "base/cleanup_funcs.h"
+#include "vboot/boot.h"
 
 static inline uint32_t get_cpsr(void)
 {
@@ -55,7 +56,7 @@
 
 int boot_arm_linux(uint32_t machine_type, void *fdt, void *entry)
 {
-	run_cleanup_funcs(CleanupOnHandoff);
+	arch_final_cleanup();
 
 	static const uint32_t CpsrF = (0x1 << 6);
 	static const uint32_t CpsrI = (0x1 << 7);
@@ -91,3 +92,11 @@
 
 	return 0;
 }
+
+
+int arch_final_cleanup(void)
+{
+	run_cleanup_funcs(CleanupOnHandoff);
+
+	return 0;
+}
diff --git a/src/arch/x86/boot.c b/src/arch/x86/boot.c
index ea09e27..b921e5a 100644
--- a/src/arch/x86/boot.c
+++ b/src/arch/x86/boot.c
@@ -28,6 +28,7 @@
 #include "arch/x86/cpu.h"
 #include "base/cleanup_funcs.h"
 #include "base/timestamp.h"
+#include "vboot/boot.h"
 
 static void * const ParamsBuff = (void *)(uintptr_t)0x1000;
 static void * const CmdLineBuff = (void *)(uintptr_t)0x2000;
@@ -80,6 +81,31 @@
 
 	hdr->cmd_line_ptr = (uintptr_t)cmd_line;
 
+	arch_final_cleanup();
+
+	puts("\nStarting kernel ...\n\n");
+	timestamp_add_now(TS_START_KERNEL);
+
+	/*
+	 * Set %ebx, %ebp, and %edi to 0, %esi to point to the boot_params
+	 * structure, and then jump to the kernel. We assume that %cs is
+	 * 0x10, 4GB flat, and read/execute, and the data segments are 0x18,
+	 * 4GB flat, and read/write.
+	 */
+	__asm__ __volatile__ (
+	"movl $0, %%ebp		\n"
+	"cli			\n"
+	"jmp *%[kernel_entry]	\n"
+	:: [kernel_entry]"a"(entry),
+	   [boot_params] "S"(boot_params),
+	   "b"(0), "D"(0)
+	:  "%ebp"
+	);
+	return 0;
+}
+
+int arch_final_cleanup(void)
+{
 	/*
 	 * Un-cache the ROM so the kernel has one more MTRR available.
 	 * Coreboot should have assigned this to the top available variable
@@ -102,23 +128,5 @@
 
 	run_cleanup_funcs(CleanupOnHandoff);
 
-	puts("\nStarting kernel ...\n\n");
-	timestamp_add_now(TS_START_KERNEL);
-
-	/*
-	 * Set %ebx, %ebp, and %edi to 0, %esi to point to the boot_params
-	 * structure, and then jump to the kernel. We assume that %cs is
-	 * 0x10, 4GB flat, and read/execute, and the data segments are 0x18,
-	 * 4GB flat, and read/write.
-	 */
-	__asm__ __volatile__ (
-	"movl $0, %%ebp		\n"
-	"cli			\n"
-	"jmp *%[kernel_entry]	\n"
-	:: [kernel_entry]"a"(entry),
-	   [boot_params] "S"(boot_params),
-	   "b"(0), "D"(0)
-	:  "%ebp"
-	);
 	return 0;
 }
diff --git a/src/vboot/callbacks/legacy.c b/src/vboot/callbacks/legacy.c
index 485fdf6..3d4f53c 100644
--- a/src/vboot/callbacks/legacy.c
+++ b/src/vboot/callbacks/legacy.c
@@ -20,11 +20,112 @@
  * MA 02111-1307 USA
  */
 
+#include <string.h>
+#include <endian.h>
 #include <libpayload.h>
+#include <lzma.h>
 #include <vboot_api.h>
+#include <cbfs.h>
+#include <cbfs_ram.h>
+
+#include "drivers/flash/flash.h"
+#include "image/fmap.h"
+#include "vboot/boot.h"
+
+static void load_payload_and_run(struct cbfs_payload *payload);
 
 int VbExLegacy(void)
 {
-	printf("VbExLegacy not implemented.\n");
+	FmapArea area;
+	struct cbfs_media media;
+	void *data;
+	struct cbfs_payload *payload;
+	const char *area_name = "RW_LEGACY";
+
+	if (fmap_find_area(area_name, &area)) {
+		printf("Fmap region %s not found.\n", area_name);
+		return 1;
+	}
+	data = flash_read(area.offset, area.size);
+
+	if (data == NULL) {
+		printf("Could not read in legacy cbfs data.\n");
+		return 1;
+	}
+
+	if (init_cbfs_ram_media(&media, data, area.size)) {
+		printf("Could not initialize legacy cbfs.\n");
+		return 1;
+	}
+
+	payload = cbfs_load_payload(&media, "payload");
+
+	if (payload == NULL) {
+		printf("Could not find payload in legacy cbfs.\n");
+		return 1;
+	}
+
+	load_payload_and_run(payload);
+
+	/* Should never return unless there is an error. */
 	return 1;
 }
+
+static void load_payload_and_run(struct cbfs_payload *payload)
+{
+	/* This is a minimalistic SELF parser.  */
+	struct cbfs_payload_segment *seg = &payload->segments;
+	char *base = (void *)seg;
+
+	while (1) {
+		void (*payload_entry)(void);
+		void *src = base + be32toh(seg->offset);
+		void *dst = (void *)(unsigned long)be64toh(seg->load_addr);
+		u32 src_len = be32toh(seg->len);
+		u32 dst_len = be32toh(seg->mem_len);
+
+		switch (seg->type) {
+		case PAYLOAD_SEGMENT_CODE:
+		case PAYLOAD_SEGMENT_DATA:
+			printf("CODE/DATA: dst=%p dst_len=%d src=%p "
+				"src_len=%d compression=%d\n", dst, dst_len,
+				src, src_len, be32toh(seg->compression));
+			if (be32toh(seg->compression) ==
+						CBFS_COMPRESS_NONE) {
+				memcpy(dst, src, src_len);
+			} else if (be32toh(seg->compression) ==
+						CBFS_COMPRESS_LZMA) {
+				unsigned long ret;
+				ret = ulzma(src, dst);
+				if (ret != dst_len) {
+					printf("LZMA: Decompression failed. "
+						"ret=%ld, expected %d.\n", ret,
+						dst_len);
+					return;
+				}
+			} else {
+				printf("Compression type %x not supported\n",
+					be32toh(seg->compression));
+				return;
+			}
+			break;
+		case PAYLOAD_SEGMENT_BSS:
+			printf("BSS: dst=%p len=%d\n", dst, dst_len);
+			memset(dst, 0, dst_len);
+			break;
+		case PAYLOAD_SEGMENT_PARAMS:
+			printf("PARAMS: skipped\n");
+			break;
+		case PAYLOAD_SEGMENT_ENTRY:
+			arch_final_cleanup();
+			payload_entry = dst;
+			payload_entry();
+			return;
+		default:
+			printf("segment type %x not implemented. Exiting\n",
+				seg->type);
+			return;
+		}
+		seg++;
+	}
+}