cbfs: Pull handling of the CBFS_CACHE mem_pool into CBFS core

This patch pulls control of the memory pool serving allocations from the
CBFS_CACHE memlayout area into cbfs.c and makes it a core part of the
CBFS API. Previously, platforms would independently instantiate this as
part of boot_device_ro() (mostly through cbfs_spi.c). The new cbfs_cache
pool is exported as a global so these platforms can still use it to
directly back rdev_mmap() on their boot device, but the cbfs_cache can
now also use it to directly make allocations itself. This is used to
allow transparent decompression support in cbfs_map().

Signed-off-by: Julius Werner <jwerner@chromium.org>
Change-Id: I0d52b6a8f582a81a19fd0fd663bb89eab55a49d9
Reviewed-on: https://review.coreboot.org/c/coreboot/+/49333
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
diff --git a/src/commonlib/Makefile.inc b/src/commonlib/Makefile.inc
index 1a38e4a..c5fa8ed 100644
--- a/src/commonlib/Makefile.inc
+++ b/src/commonlib/Makefile.inc
@@ -5,6 +5,7 @@
 romstage-y += mem_pool.c
 ramstage-y += mem_pool.c
 postcar-y += mem_pool.c
+smm-y += mem_pool.c
 
 bootblock-y += iobuf.c
 verstage-y += iobuf.c
diff --git a/src/commonlib/include/commonlib/region.h b/src/commonlib/include/commonlib/region.h
index 4d095b7..0080c44 100644
--- a/src/commonlib/include/commonlib/region.h
+++ b/src/commonlib/include/commonlib/region.h
@@ -196,18 +196,16 @@
 		MEM_REGION_DEV_INIT(base_, size_, &mem_rdev_rw_ops)	\
 
 struct mmap_helper_region_device {
-	struct mem_pool pool;
+	struct mem_pool *pool;
 	struct region_device rdev;
 };
 
-#define MMAP_HELPER_REGION_INIT(ops_, offset_, size_)			\
+#define MMAP_HELPER_DEV_INIT(ops_, offset_, size_, mpool_)		\
 	{								\
 		.rdev = REGION_DEV_INIT((ops_), (offset_), (size_)),	\
+		.pool = (mpool_),					\
 	}
 
-void mmap_helper_device_init(struct mmap_helper_region_device *mdev,
-				void *cache, size_t cache_size);
-
 void *mmap_helper_rdev_mmap(const struct region_device *, size_t, size_t);
 int mmap_helper_rdev_munmap(const struct region_device *, void *);
 
diff --git a/src/commonlib/region.c b/src/commonlib/region.c
index a10702a..55c0679 100644
--- a/src/commonlib/region.c
+++ b/src/commonlib/region.c
@@ -287,12 +287,6 @@
 	.eraseat = mdev_eraseat,
 };
 
-void mmap_helper_device_init(struct mmap_helper_region_device *mdev,
-				void *cache, size_t cache_size)
-{
-	mem_pool_init(&mdev->pool, cache, cache_size);
-}
-
 void *mmap_helper_rdev_mmap(const struct region_device *rd, size_t offset,
 				size_t size)
 {
@@ -301,13 +295,13 @@
 
 	mdev = container_of((void *)rd, __typeof__(*mdev), rdev);
 
-	mapping = mem_pool_alloc(&mdev->pool, size);
+	mapping = mem_pool_alloc(mdev->pool, size);
 
 	if (mapping == NULL)
 		return NULL;
 
 	if (rd->ops->readat(rd, mapping, offset, size) != size) {
-		mem_pool_free(&mdev->pool, mapping);
+		mem_pool_free(mdev->pool, mapping);
 		return NULL;
 	}
 
@@ -320,7 +314,7 @@
 
 	mdev = container_of((void *)rd, __typeof__(*mdev), rdev);
 
-	mem_pool_free(&mdev->pool, mapping);
+	mem_pool_free(mdev->pool, mapping);
 
 	return 0;
 }
diff --git a/src/drivers/spi/cbfs_spi.c b/src/drivers/spi/cbfs_spi.c
index 0034460..7e648a3 100644
--- a/src/drivers/spi/cbfs_spi.c
+++ b/src/drivers/spi/cbfs_spi.c
@@ -7,6 +7,7 @@
  */
 
 #include <boot_device.h>
+#include <cbfs.h>
 #include <console/console.h>
 #include <spi_flash.h>
 #include <symbols.h>
@@ -77,21 +78,7 @@
 };
 
 static struct mmap_helper_region_device mdev =
-	MMAP_HELPER_REGION_INIT(&spi_ops, 0, CONFIG_ROM_SIZE);
-
-static void switch_to_postram_cache(int unused)
-{
-	/*
-	 * Call boot_device_init() to ensure spi_flash is initialized before
-	 * backing mdev with postram cache. This prevents the mdev backing from
-	 * being overwritten if spi_flash was not accessed before dram was up.
-	 */
-	boot_device_init();
-	if (_preram_cbfs_cache != _postram_cbfs_cache)
-		mmap_helper_device_init(&mdev, _postram_cbfs_cache,
-					REGION_SIZE(postram_cbfs_cache));
-}
-ROMSTAGE_CBMEM_INIT_HOOK(switch_to_postram_cache);
+	MMAP_HELPER_DEV_INIT(&spi_ops, 0, CONFIG_ROM_SIZE, &cbfs_cache);
 
 void boot_device_init(void)
 {
@@ -105,8 +92,6 @@
 		return;
 
 	spi_flash_init_done = true;
-
-	mmap_helper_device_init(&mdev, _cbfs_cache, REGION_SIZE(cbfs_cache));
 }
 
 /* Return the CBFS boot device. */
diff --git a/src/include/cbfs.h b/src/include/cbfs.h
index e08af56..426757d 100644
--- a/src/include/cbfs.h
+++ b/src/include/cbfs.h
@@ -5,6 +5,7 @@
 
 #include <cbmem.h>
 #include <commonlib/cbfs.h>
+#include <commonlib/mem_pool.h>
 #include <program_loading.h>
 #include <types.h>
 #include <vb2_sha.h>
@@ -26,7 +27,7 @@
 
 /* Removes a previously allocated CBFS mapping. Should try to unmap mappings in strict LIFO
    order where possible, since mapping backends often don't support more complicated cases. */
-int cbfs_unmap(void *mapping);
+void cbfs_unmap(void *mapping);
 
 /* Load a file from CBFS into a buffer. Returns amount of loaded bytes on success or 0 on error.
    File will get decompressed as necessary. */
@@ -38,11 +39,22 @@
 /* Load stage into memory filling in prog. Return 0 on success. < 0 on error. */
 int cbfs_prog_stage_load(struct prog *prog);
 
+
 /**********************************************************************************************
  *                                BOOT DEVICE HELPER APIs                                     *
  **********************************************************************************************/
 
 /*
+ * The shared memory pool for backing mapped CBFS files, and other CBFS allocation needs.
+ * On x86 platforms, this would only be needed to transparently map compressed files, but it
+ * would require a permanent CBMEM carveout to be safe to use during S3 resume. Since it's not
+ * clear whether this feature is necessary or worth the wasted memory, it is currently disabled
+ * but could be added behind a Kconfig later if desired.
+ */
+#define CBFS_CACHE_AVAILABLE (!CONFIG(ARCH_X86))
+extern struct mem_pool cbfs_cache;
+
+/*
  * Data structure that represents "a" CBFS boot device, with optional metadata cache. Generally
  * we only have one of these, or two (RO and RW) when CONFIG(VBOOT) is set. The region device
  * stored here must always be a subregion of boot_device_ro().
@@ -95,6 +107,7 @@
 size_t cbfs_load_and_decompress(const struct region_device *rdev, size_t offset,
 	size_t in_size, void *buffer, size_t buffer_size, uint32_t compression);
 
+
 /**********************************************************************************************
  *                         INTERNAL HELPERS FOR INLINES, DO NOT USE.                          *
  **********************************************************************************************/
diff --git a/src/include/symbols.h b/src/include/symbols.h
index fe449d9..3e4694b 100644
--- a/src/include/symbols.h
+++ b/src/include/symbols.h
@@ -32,9 +32,9 @@
 DECLARE_REGION(preram_cbmem_console)
 DECLARE_REGION(cbmem_init_hooks)
 DECLARE_REGION(stack)
-DECLARE_REGION(preram_cbfs_cache)
-DECLARE_REGION(postram_cbfs_cache)
-DECLARE_REGION(cbfs_cache)
+DECLARE_OPTIONAL_REGION(preram_cbfs_cache)
+DECLARE_OPTIONAL_REGION(postram_cbfs_cache)
+DECLARE_OPTIONAL_REGION(cbfs_cache)
 DECLARE_REGION(cbfs_mcache)
 DECLARE_REGION(fmap_cache)
 DECLARE_REGION(tpm_tcpa_log)
diff --git a/src/lib/cbfs.c b/src/lib/cbfs.c
index fa6c2e30..93dbb68 100644
--- a/src/lib/cbfs.c
+++ b/src/lib/cbfs.c
@@ -18,6 +18,18 @@
 #include <symbols.h>
 #include <timestamp.h>
 
+#if CBFS_CACHE_AVAILABLE
+struct mem_pool cbfs_cache = MEM_POOL_INIT(_cbfs_cache, REGION_SIZE(cbfs_cache));
+
+static void switch_to_postram_cache(int unused)
+{
+	if (_preram_cbfs_cache != _postram_cbfs_cache)
+		mem_pool_init(&cbfs_cache, _postram_cbfs_cache,
+			      REGION_SIZE(postram_cbfs_cache));
+}
+ROMSTAGE_CBMEM_INIT_HOOK(switch_to_postram_cache);
+#endif
+
 cb_err_t cbfs_boot_lookup(const char *name, bool force_ro,
 			  union cbfs_mdata *mdata, struct region_device *rdev)
 {
@@ -101,14 +113,48 @@
 	if (size_out != NULL)
 		*size_out = region_device_sz(&rdev);
 
-	return rdev_mmap_full(&rdev);
+	uint32_t compression = CBFS_COMPRESS_NONE;
+	const struct cbfs_file_attr_compression *attr = cbfs_find_attr(&mdata,
+				CBFS_FILE_ATTR_TAG_COMPRESSION, sizeof(*attr));
+	if (attr)
+		compression = be32toh(attr->compression);
+
+	if (compression == CBFS_COMPRESS_NONE)
+		return rdev_mmap_full(&rdev);
+
+	if (!CBFS_CACHE_AVAILABLE) {
+		ERROR("Cannot map compressed file %s on x86\n", mdata.h.filename);
+		return NULL;
+	}
+
+	size_t size = be32toh(attr->decompressed_size);
+	void *buf = mem_pool_alloc(&cbfs_cache, size);
+	if (!buf) {
+		ERROR("CBFS cache out of memory when mapping %s\n", mdata.h.filename);
+		return NULL;
+	}
+
+	size = cbfs_load_and_decompress(&rdev, 0, region_device_sz(&rdev), buf, size,
+					compression);
+	if (!size)
+		return NULL;
+
+	if (size_out != NULL)
+		*size_out = size;
+	return buf;
 }
 
-int cbfs_unmap(void *mapping)
+void cbfs_unmap(void *mapping)
 {
-	/* This works because munmap() only works on the root rdev and never cares about which
-	   chained subregion something was mapped from. */
-	return rdev_munmap(boot_device_ro(), mapping);
+	/*
+	 * This is save to call with mappings that weren't allocated in the cache (e.g. x86
+	 * direct mappings) -- mem_pool_free() just does nothing for addresses it doesn't
+	 * recognize. This hardcodes the assumption that if platforms implement an rdev_mmap()
+	 * that requires a free() for the boot_device, they need to implement it via the
+	 * cbfs_cache mem_pool.
+	 */
+	if (CBFS_CACHE_AVAILABLE)
+		mem_pool_free(&cbfs_cache, mapping);
 }
 
 int cbfs_locate_file_in_region(struct cbfsf *fh, const char *region_name,
@@ -158,6 +204,9 @@
 	if ((ENV_BOOTBLOCK || ENV_SEPARATE_VERSTAGE) && !CONFIG(COMPRESS_PRERAM_STAGES))
 		return false;
 
+	if (ENV_SMM)
+		return false;
+
 	return true;
 }
 
@@ -174,6 +223,8 @@
 		return false;
 	if ((ENV_ROMSTAGE || ENV_POSTCAR) && !CONFIG(COMPRESS_RAMSTAGE))
 		return false;
+	if (ENV_SMM)
+		return false;
 	return true;
 }
 
diff --git a/src/mainboard/sifive/hifive-unleashed/media.c b/src/mainboard/sifive/hifive-unleashed/media.c
index 2ff375a..f3dcf7f 100644
--- a/src/mainboard/sifive/hifive-unleashed/media.c
+++ b/src/mainboard/sifive/hifive-unleashed/media.c
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 
 #include <boot_device.h>
+#include <cbfs.h>
 #include <symbols.h>
 #include <device/mmio.h>
 #include <soc/addressmap.h>
@@ -42,7 +43,7 @@
 };
 
 static struct mmap_helper_region_device sd_mdev =
-	MMAP_HELPER_REGION_INIT(&unleashed_sd_ops, 0, CONFIG_ROM_SIZE);
+	MMAP_HELPER_DEV_INIT(&unleashed_sd_ops, 0, CONFIG_ROM_SIZE, &cbfs_cache);
 
 const struct region_device *boot_device_ro(void)
 {
@@ -79,8 +80,6 @@
 	}
 	if (MSEL_SPI2SD(m)) {
 		spi_sdcard_init(&card, 2, 0);
-		mmap_helper_device_init(&sd_mdev,
-			_cbfs_cache, REGION_SIZE(cbfs_cache));
 		return;
 	}
 	die("Wrong configuration of MSEL\n");
diff --git a/src/soc/samsung/exynos5250/spi.c b/src/soc/samsung/exynos5250/spi.c
index e630090..c7e0707 100644
--- a/src/soc/samsung/exynos5250/spi.c
+++ b/src/soc/samsung/exynos5250/spi.c
@@ -2,6 +2,8 @@
 
 #include <device/mmio.h>
 #include <assert.h>
+#include <boot_device.h>
+#include <cbfs.h>
 #include <commonlib/region.h>
 #include <console/console.h>
 #include <soc/clk.h>
@@ -160,13 +162,11 @@
 };
 
 static struct mmap_helper_region_device mdev =
-	MMAP_HELPER_REGION_INIT(&exynos_spi_ops, 0, CONFIG_ROM_SIZE);
+	MMAP_HELPER_DEV_INIT(&exynos_spi_ops, 0, CONFIG_ROM_SIZE, &cbfs_cache);
 
 void exynos_init_spi_boot_device(void)
 {
 	boot_slave_regs = (void *)EXYNOS5_SPI1_BASE;
-
-	mmap_helper_device_init(&mdev, _cbfs_cache, REGION_SIZE(cbfs_cache));
 }
 
 const struct region_device *exynos_spi_boot_device(void)
diff --git a/src/soc/samsung/exynos5420/spi.c b/src/soc/samsung/exynos5420/spi.c
index 0a2f027..2fe9d5c 100644
--- a/src/soc/samsung/exynos5420/spi.c
+++ b/src/soc/samsung/exynos5420/spi.c
@@ -2,6 +2,7 @@
 
 #include <device/mmio.h>
 #include <assert.h>
+#include <cbfs.h>
 #include <console/console.h>
 #include <soc/cpu.h>
 #include <soc/spi.h>
@@ -268,13 +269,11 @@
 };
 
 static struct mmap_helper_region_device mdev =
-	MMAP_HELPER_REGION_INIT(&exynos_spi_ops, 0, CONFIG_ROM_SIZE);
+	MMAP_HELPER_DEV_INIT(&exynos_spi_ops, 0, CONFIG_ROM_SIZE, &cbfs_cache);
 
 void exynos_init_spi_boot_device(void)
 {
 	boot_slave = &exynos_spi_slaves[1];
-
-	mmap_helper_device_init(&mdev, _cbfs_cache, REGION_SIZE(cbfs_cache));
 }
 
 const struct region_device *exynos_spi_boot_device(void)