cbfs: Add cbfs_alloc() primitive and combine cbfs_load() and cbfs_map()

This patchs adds a new CBFS primitive that allows callers to pass in an
allocator function that will be called once the size of the file to load
is known, to decide on its final location. This can be useful for
loading a CBFS file straight into CBMEM, for example. The new primitive
is combined with cbfs_map() and cbfs_load() into a single underlying
function that can handle all operations, to reduce the amount of code
that needs to be duplicated (especially later when file verification is
added). Also add a new variation that allows restraining or querying the
CBFS type of a file as it is being loaded, and reorganize the
documentation/definition of all these accessors and variations in the
header file a little.

Signed-off-by: Julius Werner <jwerner@chromium.org>
Change-Id: I5fe0645387c0e9053ad5c15744437940fc904392
Reviewed-on: https://review.coreboot.org/c/coreboot/+/49334
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
diff --git a/src/lib/cbfs.c b/src/lib/cbfs.c
index 93dbb68..7df9dc6 100644
--- a/src/lib/cbfs.c
+++ b/src/lib/cbfs.c
@@ -102,48 +102,6 @@
 	return 0;
 }
 
-void *_cbfs_map(const char *name, size_t *size_out, bool force_ro)
-{
-	struct region_device rdev;
-	union cbfs_mdata mdata;
-
-	if (cbfs_boot_lookup(name, force_ro, &mdata, &rdev))
-		return NULL;
-
-	if (size_out != NULL)
-		*size_out = region_device_sz(&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;
-}
-
 void cbfs_unmap(void *mapping)
 {
 	/*
@@ -234,6 +192,8 @@
 	size_t out_size;
 	void *map;
 
+	DEBUG("Decompressing %zu bytes to %p with algo %d\n", buffer_size, buffer, compression);
+
 	switch (compression) {
 	case CBFS_COMPRESS_NONE:
 		if (buffer_size < in_size)
@@ -348,25 +308,78 @@
 	return cbfs_map(name, NULL);
 }
 
-size_t _cbfs_load(const char *name, void *buf, size_t buf_size, bool force_ro)
+void *_cbfs_alloc(const char *name, cbfs_allocator_t allocator, void *arg,
+		  size_t *size_out, bool force_ro, enum cbfs_type *type)
 {
 	struct region_device rdev;
 	union cbfs_mdata mdata;
+	void *loc;
+
+	DEBUG("%s(name='%s', alloc=%p(%p), force_ro=%s, type=%d)\n", __func__, name, allocator,
+	      arg, force_ro ? "true" : "false", type ? *type : -1);
 
 	if (cbfs_boot_lookup(name, force_ro, &mdata, &rdev))
-		return 0;
+		return NULL;
 
-	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 (buf_size < be32toh(attr->decompressed_size))
-			return 0;
+	if (type) {
+		const enum cbfs_type real_type = be32toh(mdata.h.type);
+		if (*type == CBFS_TYPE_QUERY)
+			*type = real_type;
+		else if (*type != real_type) {
+			ERROR("'%s' type mismatch (is %u, expected %u)\n",
+			      mdata.h.filename, real_type, *type);
+			return NULL;
+		}
 	}
 
-	return cbfs_load_and_decompress(&rdev, 0, region_device_sz(&rdev),
-					buf, buf_size, compression);
+	size_t size = region_device_sz(&rdev);
+	uint32_t compression = CBFS_COMPRESS_NONE;
+	const struct cbfs_file_attr_compression *cattr = cbfs_find_attr(&mdata,
+				CBFS_FILE_ATTR_TAG_COMPRESSION, sizeof(*cattr));
+	if (cattr) {
+		compression = be32toh(cattr->compression);
+		size = be32toh(cattr->decompressed_size);
+	}
+
+	if (size_out)
+		*size_out = size;
+
+	/* allocator == NULL means do a cbfs_map() */
+	if (allocator) {
+		loc = allocator(arg, size, &mdata);
+	} else if (compression == CBFS_COMPRESS_NONE) {
+		return rdev_mmap_full(&rdev);
+	} else if (!CBFS_CACHE_AVAILABLE) {
+		ERROR("Cannot map compressed file %s on x86\n", mdata.h.filename);
+		return NULL;
+	} else {
+		loc = mem_pool_alloc(&cbfs_cache, size);
+	}
+
+	if (!loc) {
+		ERROR("'%s' allocation failure\n", mdata.h.filename);
+		return NULL;
+	}
+
+	size = cbfs_load_and_decompress(&rdev, 0, region_device_sz(&rdev),
+					loc, size, compression);
+	if (!size)
+		return NULL;
+
+	return loc;
+}
+
+void *_cbfs_default_allocator(void *arg, size_t size, union cbfs_mdata *unused)
+{
+	struct _cbfs_default_allocator_arg *darg = arg;
+	if (size > darg->buf_size)
+		return NULL;
+	return darg->buf;
+}
+
+void *_cbfs_cbmem_allocator(void *arg, size_t size, union cbfs_mdata *unused)
+{
+	return cbmem_add((uintptr_t)arg, size);
 }
 
 int cbfs_prog_stage_load(struct prog *pstage)