drivers/intel/fsp2_0: add option to compress FSP-M in cbfs

Allow the ability for chipset or mainboard to choose to
compress FSP-M in cbfs using LZMA or LZ4 routines. However, only
non-XIP platforms will support FSP-M compression. Since the main
cbfs decompression paths are utilized add the appropriate checks
for including compression algorithms under the FSP-M compression
options.

On picasso FSP-M (debug builds) the following savings were measured:
no-compression:
	fspm.bin	720896	none
FSP_COMPRESS_FSP_M_LZ4:
	fspm.bin	138379	LZ4  (720896 decompressed)	-80%
FSP_COMPRESS_FSP_M_LZMA:
	fspm.bin	98921	LZMA (720896 decompressed)	-86%

BUG=b:155322763,b:150746858,b:152909132

Change-Id: I5c88510c134b56a36ff1cd97a64b51ab2fea0ab0
Signed-off-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/41450
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Furquan Shaikh <furquan@google.com>
diff --git a/src/drivers/intel/fsp2_0/Kconfig b/src/drivers/intel/fsp2_0/Kconfig
index 6c4344e..0344116 100644
--- a/src/drivers/intel/fsp2_0/Kconfig
+++ b/src/drivers/intel/fsp2_0/Kconfig
@@ -187,6 +187,19 @@
 config FSP_COMPRESS_FSP_S_LZ4
 	bool
 
+config FSP_COMPRESS_FSP_M_LZMA
+	bool
+	depends on !FSP_M_XIP
+
+config FSP_COMPRESS_FSP_M_LZ4
+	bool
+	depends on !FSP_M_XIP
+
+config FSP_M_ADDR
+	hex
+	help
+	  The address FSP-M will be relocated to during build time
+
 if FSP_PEIM_TO_PEIM_INTERFACE
 source "src/drivers/intel/fsp2_0/ppi/Kconfig"
 endif
diff --git a/src/drivers/intel/fsp2_0/Makefile.inc b/src/drivers/intel/fsp2_0/Makefile.inc
index 6c82b28..e954a46 100644
--- a/src/drivers/intel/fsp2_0/Makefile.inc
+++ b/src/drivers/intel/fsp2_0/Makefile.inc
@@ -52,6 +52,12 @@
 ifeq ($(CONFIG_FSP_M_XIP),y)
 $(FSP_M_CBFS)-options := --xip $(TXTIBB)
 endif
+ifeq ($(CONFIG_FSP_COMPRESS_FSP_M_LZMA),y)
+$(FSP_M_CBFS)-compression := LZMA
+endif
+ifeq ($(CONFIG_FSP_COMPRESS_FSP_M_LZ4),y)
+$(FSP_M_CBFS)-compression := LZ4
+endif
 
 cbfs-files-$(CONFIG_ADD_FSP_BINARIES) += $(FSP_S_CBFS)
 $(FSP_S_CBFS)-file := $(call strip_quotes,$(CONFIG_FSP_S_FILE))
diff --git a/src/drivers/intel/fsp2_0/include/fsp/util.h b/src/drivers/intel/fsp2_0/include/fsp/util.h
index f531e36..29c393b 100644
--- a/src/drivers/intel/fsp2_0/include/fsp/util.h
+++ b/src/drivers/intel/fsp2_0/include/fsp/util.h
@@ -89,6 +89,8 @@
 	int (*get_destination)(const struct fsp_load_descriptor *fspld,
 			void **dest, size_t final_load_size,
 			const struct region_device *source);
+	/* Optional argument to be utilized by get_destination() callback. */
+	void *arg;
 };
 
 /* Load the FSP component described by fsp_load_descriptor from cbfs. The FSP
diff --git a/src/drivers/intel/fsp2_0/memory_init.c b/src/drivers/intel/fsp2_0/memory_init.c
index ebd5213..d90b181 100644
--- a/src/drivers/intel/fsp2_0/memory_init.c
+++ b/src/drivers/intel/fsp2_0/memory_init.c
@@ -254,14 +254,20 @@
 	return ver;
 }
 
-static void do_fsp_memory_init(struct fsp_header *hdr, bool s3wake,
-					const struct memranges *memmap)
+struct fspm_context {
+	struct fsp_header header;
+	struct memranges memmap;
+};
+
+static void do_fsp_memory_init(const struct fspm_context *context, bool s3wake)
 {
 	uint32_t status;
 	fsp_memory_init_fn fsp_raminit;
 	FSPM_UPD fspm_upd, *upd;
 	FSPM_ARCH_UPD *arch_upd;
 	uint32_t fsp_version;
+	const struct fsp_header *hdr = &context->header;
+	const struct memranges *memmap = &context->memmap;
 
 	post_code(POST_MEM_PREINIT_PREP_START);
 
@@ -323,94 +329,69 @@
 	fsp_debug_after_memory_init(status);
 }
 
-/* Load the binary into the memory specified by the info header. */
-static enum cb_err load_fspm_mem(struct fsp_header *hdr,
-					const struct region_device *rdev,
-					const struct memranges *memmap)
+static int fspm_get_dest(const struct fsp_load_descriptor *fspld, void **dest,
+				size_t size, const struct region_device *source)
 {
+	struct fspm_context *context = fspld->arg;
+	struct fsp_header *hdr = &context->header;
+	struct memranges *memmap = &context->memmap;
 	uintptr_t fspm_begin;
 	uintptr_t fspm_end;
 
-	if (fsp_validate_component(hdr, rdev) != CB_SUCCESS)
-		return CB_ERR;
+	if (CONFIG(FSP_M_XIP)) {
+		if (fsp_validate_component(hdr, source) != CB_SUCCESS)
+			return -1;
 
-	fspm_begin = hdr->image_base;
-	fspm_end = fspm_begin + hdr->image_size;
-
-	if (check_region_overlap(memmap, "FSPM", fspm_begin, fspm_end) !=
-		CB_SUCCESS)
-		return CB_ERR;
-
-	/* Load binary into memory at provided address. */
-	if (rdev_readat(rdev, (void *)fspm_begin, 0, fspm_end - fspm_begin) < 0)
-		return CB_ERR;
-
-	return CB_SUCCESS;
-}
-
-/* Handle the case when FSPM is running XIP. */
-static enum cb_err load_fspm_xip(struct fsp_header *hdr,
-					const struct region_device *rdev)
-{
-	void *base;
-
-	if (fsp_validate_component(hdr, rdev) != CB_SUCCESS)
-		return CB_ERR;
-
-	base = rdev_mmap_full(rdev);
-	if ((uintptr_t)base != hdr->image_base) {
-		printk(BIOS_CRIT, "FSPM XIP base does not match: %p vs %p\n",
-			(void *)(uintptr_t)hdr->image_base, base);
-		return CB_ERR;
+		*dest = rdev_mmap_full(source);
+		if ((uintptr_t)*dest != hdr->image_base) {
+			printk(BIOS_CRIT, "FSPM XIP base does not match: %p vs %p\n",
+				(void *)(uintptr_t)hdr->image_base, *dest);
+			return -1;
+		}
+		/* Since the component is XIP it's already in the address space.
+		   Thus, there's no need to rdev_munmap(). */
+		return 0;
 	}
 
-	/*
-	 * Since the component is XIP it's already in the address space. Thus,
-	 * there's no need to rdev_munmap().
-	 */
-	return CB_SUCCESS;
+	/* Non XIP FSP-M uses FSP-M address */
+	fspm_begin = (uintptr_t)CONFIG_FSP_M_ADDR;
+	fspm_end = fspm_begin + size;
+
+	if (check_region_overlap(memmap, "FSPM", fspm_begin, fspm_end) != CB_SUCCESS)
+		return -1;
+
+	*dest = (void *)fspm_begin;
+
+	return 0;
 }
 
 void fsp_memory_init(bool s3wake)
 {
-	struct fsp_header hdr;
-	enum cb_err status;
-	struct cbfsf file_desc;
-	struct region_device file_data;
-	const char *name = CONFIG_FSP_M_CBFS;
-	struct memranges memmap;
 	struct range_entry prog_ranges[2];
+	struct fspm_context context;
+	struct fsp_load_descriptor fspld = {
+		.fsp_prog = PROG_INIT(PROG_REFCODE, CONFIG_FSP_M_CBFS),
+		.get_destination = fspm_get_dest,
+		.arg = &context,
+	};
+	struct fsp_header *hdr = &context.header;
+	struct memranges *memmap = &context.memmap;
 
 	elog_boot_notify(s3wake);
 
-	if (cbfs_boot_locate(&file_desc, name, NULL)) {
-		printk(BIOS_CRIT, "Could not locate %s in CBFS\n", name);
-		die("FSPM not available!\n");
-	}
-
-	cbfs_file_data(&file_data, &file_desc);
-
 	/* Build up memory map of romstage address space including CAR. */
-	memranges_init_empty(&memmap, &prog_ranges[0], ARRAY_SIZE(prog_ranges));
+	memranges_init_empty(memmap, &prog_ranges[0], ARRAY_SIZE(prog_ranges));
 	if (ENV_CACHE_AS_RAM)
-		memranges_insert(&memmap, (uintptr_t)_car_region_start,
+		memranges_insert(memmap, (uintptr_t)_car_region_start,
 			_car_unallocated_start - _car_region_start, 0);
-	memranges_insert(&memmap, (uintptr_t)_program, REGION_SIZE(program), 0);
+	memranges_insert(memmap, (uintptr_t)_program, REGION_SIZE(program), 0);
 
-	if (!CONFIG(FSP_M_XIP))
-		status = load_fspm_mem(&hdr, &file_data, &memmap);
-	else
-		status = load_fspm_xip(&hdr, &file_data);
-
-	if (status != CB_SUCCESS)
-		die("Loading FSPM failed!\n");
-
-	/* Signal that FSP component has been loaded. */
-	prog_segment_loaded(hdr.image_base, hdr.image_size, SEG_FINAL);
+	if (fsp_load_component(&fspld, hdr) != CB_SUCCESS)
+		die("FSPM not available or failed to load!\n");
 
 	timestamp_add_now(TS_BEFORE_INITRAM);
 
-	do_fsp_memory_init(&hdr, s3wake, &memmap);
+	do_fsp_memory_init(&context, s3wake);
 
 	timestamp_add_now(TS_AFTER_INITRAM);
 }
diff --git a/src/drivers/intel/fsp2_0/util.c b/src/drivers/intel/fsp2_0/util.c
index 3b7516a..a00b684 100644
--- a/src/drivers/intel/fsp2_0/util.c
+++ b/src/drivers/intel/fsp2_0/util.c
@@ -119,6 +119,21 @@
 	}
 }
 
+static inline bool fspm_env(void)
+{
+	if (ENV_ROMSTAGE)
+		return true;
+	return false;
+}
+
+static inline bool fspm_xip(void)
+{
+	/* FSP-M is assumed to be loaded in romstage. */
+	if (fspm_env() && CONFIG(FSP_M_XIP))
+		return true;
+	return false;
+}
+
 static void *fsp_get_dest_and_load(struct fsp_load_descriptor *fspld, size_t size,
 				const struct region_device *source_rdev,
 				uint32_t compression_algo)
@@ -130,12 +145,20 @@
 		return NULL;
 	}
 
+	/* Don't load when executing in place. */
+	if (fspm_xip())
+		return dest;
+
 	if (cbfs_load_and_decompress(source_rdev, 0, region_device_sz(source_rdev),
 			dest, size, compression_algo) != size) {
 		printk(BIOS_ERR, "Failed to load FSP component.\n");
 		return NULL;
 	}
 
+	/* Don't allow FSP-M relocation. */
+	if (fspm_env())
+		return dest;
+
 	if (fsp_component_relocate((uintptr_t)dest, dest, size) < 0) {
 		printk(BIOS_ERR, "Unable to relocate FSP component!\n");
 		return NULL;