lib/prog_loaders: Add payload_preload

This method will allow the SoC code to start loading the payload before
it is required.

BUG=b:177909625
TEST=Boot guybrush and see read/decompress drop by 23 ms.

Signed-off-by: Raul E Rangel <rrangel@chromium.org>
Change-Id: Ifa8f30a0f4f931ece803c2e8e022e4d33d3fe581
Reviewed-on: https://review.coreboot.org/c/coreboot/+/56051
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Martin Roth <martinroth@google.com>
diff --git a/src/lib/prog_loaders.c b/src/lib/prog_loaders.c
index 40f51eb..1a361ea 100644
--- a/src/lib/prog_loaders.c
+++ b/src/lib/prog_loaders.c
@@ -13,6 +13,7 @@
 #include <rmodule.h>
 #include <stage_cache.h>
 #include <symbols.h>
+#include <thread.h>
 #include <timestamp.h>
 #include <security/vboot/vboot_common.h>
 
@@ -126,27 +127,71 @@
 static struct prog global_payload =
 	PROG_INIT(PROG_PAYLOAD, CONFIG_CBFS_PREFIX "/payload");
 
+static struct thread_handle payload_preload_handle;
+
+static enum cb_err payload_preload_thread_entry(void *arg)
+{
+	size_t size;
+	struct prog *payload = &global_payload;
+
+	printk(BIOS_DEBUG, "Preloading payload\n");
+
+	payload->cbfs_type = CBFS_TYPE_QUERY;
+
+	size = cbfs_type_load(prog_name(payload), _payload_preload_cache,
+			      REGION_SIZE(payload_preload_cache), &payload->cbfs_type);
+
+	if (!size) {
+		printk(BIOS_ERR, "ERROR: Preloading payload failed\n");
+		return CB_ERR;
+	}
+
+	printk(BIOS_DEBUG, "Preloading payload complete\n");
+
+	return CB_SUCCESS;
+}
+
+void payload_preload(void)
+{
+	struct thread_handle *handle = &payload_preload_handle;
+
+	if (!CONFIG(PAYLOAD_PRELOAD))
+		return;
+
+	if (thread_run(handle, payload_preload_thread_entry, NULL))
+		printk(BIOS_ERR, "ERROR: Failed to start payload preload thread\n");
+}
+
 void payload_load(void)
 {
 	struct prog *payload = &global_payload;
+	struct thread_handle *handle = &payload_preload_handle;
+	void *mapping = NULL;
+	void *buffer;
 
 	timestamp_add_now(TS_LOAD_PAYLOAD);
 
 	if (prog_locate_hook(payload))
 		goto out;
 
-	payload->cbfs_type = CBFS_TYPE_QUERY;
-	void *mapping = cbfs_type_map(prog_name(payload), NULL, &payload->cbfs_type);
-	if (!mapping)
+	if (CONFIG(PAYLOAD_PRELOAD) && thread_join(handle) == CB_SUCCESS) {
+		buffer = _payload_preload_cache;
+	} else {
+		payload->cbfs_type = CBFS_TYPE_QUERY;
+		mapping = cbfs_type_map(prog_name(payload), NULL, &payload->cbfs_type);
+		buffer = mapping;
+	}
+
+	if (!buffer)
 		goto out;
 
 	switch (prog_cbfs_type(payload)) {
 	case CBFS_TYPE_SELF: /* Simple ELF */
-		selfload_mapped(payload, mapping, BM_MEM_RAM);
+		selfload_mapped(payload, buffer, BM_MEM_RAM);
 		break;
 	case CBFS_TYPE_FIT: /* Flattened image tree */
 		if (CONFIG(PAYLOAD_FIT_SUPPORT)) {
-			fit_payload(payload, mapping);
+			fit_payload(payload, buffer);
 			break;
 		} /* else fall-through */
 	default:
@@ -155,7 +200,8 @@
 		break;
 	}
 
-	cbfs_unmap(mapping);
+	if (mapping)
+		cbfs_unmap(mapping);
 out:
 	if (prog_entry(payload) == NULL)
 		die_with_post_code(POST_INVALID_ROM, "Payload not loaded.\n");