These strings didn't match the license names exactly, so update them to match. Change-Id: Ib946eb15ca5fa64cbd6b657350b989b4a4c1b7b7 Signed-off-by: Martin Roth <gaumless@gmail.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/80583 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Felix Singer <service+coreboot-gerrit@felixsinger.de>
		
			
				
	
	
		
			730 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			730 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* SPDX-License-Identifier: GPL-2.0-only */
 | 
						|
 | 
						|
#include <libpayload-config.h>
 | 
						|
#include <cbfs.h>
 | 
						|
#include <cbfs_glue.h>
 | 
						|
#include <commonlib/bsd/cb_err.h>
 | 
						|
#include <commonlib/bsd/cbfs_mdata.h>
 | 
						|
#include <endian.h>
 | 
						|
#include <mocks/cbfs_util.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <sysinfo.h>
 | 
						|
#include <tests/test.h>
 | 
						|
 | 
						|
#include "../libcbfs/cbfs.c"
 | 
						|
 | 
						|
/* Mocks */
 | 
						|
 | 
						|
unsigned long virtual_offset = 0;
 | 
						|
struct sysinfo_t lib_sysinfo;
 | 
						|
 | 
						|
unsigned long ulzman(const unsigned char *src, unsigned long srcn, unsigned char *dst,
 | 
						|
		     unsigned long dstn)
 | 
						|
{
 | 
						|
	assert_true(dstn != 0);
 | 
						|
	check_expected(srcn);
 | 
						|
	check_expected(dstn);
 | 
						|
	memcpy(dst, src, dstn);
 | 
						|
	return dstn;
 | 
						|
}
 | 
						|
 | 
						|
size_t ulz4fn(const void *src, size_t srcn, void *dst, size_t dstn)
 | 
						|
{
 | 
						|
	assert_non_null(dstn);
 | 
						|
	check_expected(srcn);
 | 
						|
	check_expected(dstn);
 | 
						|
	memcpy(dst, src, dstn);
 | 
						|
	return dstn;
 | 
						|
}
 | 
						|
 | 
						|
static size_t test_fmap_offset = 0;
 | 
						|
static size_t test_fmap_size = 0;
 | 
						|
static enum cb_err test_fmap_result = CB_SUCCESS;
 | 
						|
 | 
						|
static void set_fmap_locate_area_results(size_t offset, size_t size, size_t result)
 | 
						|
{
 | 
						|
	test_fmap_offset = offset;
 | 
						|
	test_fmap_size = size;
 | 
						|
	test_fmap_result = result;
 | 
						|
}
 | 
						|
 | 
						|
enum cb_err fmap_locate_area(const char *name, size_t *offset, size_t *size)
 | 
						|
{
 | 
						|
	*offset = test_fmap_offset;
 | 
						|
	*size = test_fmap_size;
 | 
						|
	return test_fmap_result;
 | 
						|
}
 | 
						|
 | 
						|
enum cb_err cbfs_mcache_lookup(const void *mcache, size_t mcache_size, const char *name,
 | 
						|
			       union cbfs_mdata *mdata_out, size_t *data_offset_out)
 | 
						|
{
 | 
						|
	assert_non_null(mcache);
 | 
						|
	assert_true(mcache_size > 0 && mcache_size % CBFS_MCACHE_ALIGNMENT == 0);
 | 
						|
	assert_non_null(mdata_out);
 | 
						|
	assert_non_null(data_offset_out);
 | 
						|
 | 
						|
	check_expected(name);
 | 
						|
 | 
						|
	enum cb_err ret = mock_type(enum cb_err);
 | 
						|
	if (ret != CB_SUCCESS)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	memcpy(mdata_out, mock_ptr_type(const union cbfs_mdata *), sizeof(union cbfs_mdata));
 | 
						|
	*data_offset_out = mock_type(size_t);
 | 
						|
	return CB_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
static void expect_cbfs_mcache_lookup(const char *name, enum cb_err err,
 | 
						|
				      const union cbfs_mdata *mdata, size_t data_offset_out)
 | 
						|
{
 | 
						|
	expect_string(cbfs_mcache_lookup, name, name);
 | 
						|
	will_return(cbfs_mcache_lookup, err);
 | 
						|
 | 
						|
	if (err == CB_SUCCESS) {
 | 
						|
		will_return(cbfs_mcache_lookup, mdata);
 | 
						|
		will_return(cbfs_mcache_lookup, data_offset_out);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
enum cb_err cbfs_lookup(cbfs_dev_t dev, const char *name, union cbfs_mdata *mdata_out,
 | 
						|
			size_t *data_offset_out, struct vb2_hash *metadata_hash)
 | 
						|
{
 | 
						|
	assert_non_null(dev);
 | 
						|
	check_expected(name);
 | 
						|
 | 
						|
	enum cb_err ret = mock_type(enum cb_err);
 | 
						|
	if (ret != CB_SUCCESS)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	memcpy(mdata_out, mock_ptr_type(const union cbfS_mdata *), sizeof(union cbfs_mdata));
 | 
						|
	*data_offset_out = mock_type(size_t);
 | 
						|
	return CB_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
static void expect_cbfs_lookup(const char *name, enum cb_err err, const union cbfs_mdata *mdata,
 | 
						|
			       size_t data_offset_out)
 | 
						|
{
 | 
						|
	expect_string(cbfs_lookup, name, name);
 | 
						|
	will_return(cbfs_lookup, err);
 | 
						|
 | 
						|
	if (err == CB_SUCCESS) {
 | 
						|
		will_return(cbfs_lookup, mdata);
 | 
						|
		will_return(cbfs_lookup, data_offset_out);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
const void *cbfs_find_attr(const union cbfs_mdata *mdata, uint32_t attr_tag, size_t size_check)
 | 
						|
{
 | 
						|
	return mock_ptr_type(void *);
 | 
						|
}
 | 
						|
 | 
						|
static bool force_single_boot_device_size_failure = false;
 | 
						|
 | 
						|
ssize_t boot_device_read(void *buf, size_t offset, size_t size)
 | 
						|
{
 | 
						|
	memcpy(buf, (void *)offset, size);
 | 
						|
	if (force_single_boot_device_size_failure) {
 | 
						|
		force_single_boot_device_size_failure = false;
 | 
						|
		return CB_ERR;
 | 
						|
	}
 | 
						|
	return size;
 | 
						|
}
 | 
						|
 | 
						|
/* Utils */
 | 
						|
 | 
						|
static size_t get_cbfs_file_size(const void *file_ptr)
 | 
						|
{
 | 
						|
	const struct cbfs_file *f = file_ptr;
 | 
						|
	return be32toh(f->offset) + be32toh(f->len);
 | 
						|
}
 | 
						|
 | 
						|
static void create_cbfs(const struct cbfs_test_file *files[], const size_t nfiles,
 | 
						|
			uint8_t *buffer, const size_t buffer_size)
 | 
						|
{
 | 
						|
	uint8_t *data_ptr = buffer;
 | 
						|
	size_t file_size = 0;
 | 
						|
	memset(buffer, 0, buffer_size);
 | 
						|
	for (size_t i = 0; i < nfiles; ++i) {
 | 
						|
		if (files[i] == NULL) {
 | 
						|
			file_size = CBFS_ALIGNMENT;
 | 
						|
			assert_true(&data_ptr[file_size] < &buffer[buffer_size]);
 | 
						|
		} else {
 | 
						|
			file_size = get_cbfs_file_size(files[i]);
 | 
						|
			assert_true(&data_ptr[file_size] < &buffer[buffer_size]);
 | 
						|
			memcpy(data_ptr, files[i], file_size);
 | 
						|
		}
 | 
						|
		data_ptr = &data_ptr[file_size];
 | 
						|
		data_ptr = &buffer[ALIGN_UP((uintptr_t)data_ptr - (uintptr_t)buffer,
 | 
						|
					    CBFS_ALIGNMENT)];
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static size_t get_created_cbfs_file_start_offset(const struct cbfs_test_file *files[],
 | 
						|
						 const size_t nfile)
 | 
						|
{
 | 
						|
	size_t offset_out = 0;
 | 
						|
	size_t offset = 0;
 | 
						|
	for (size_t i = 0; i < nfile; ++i) {
 | 
						|
		offset = files[i] ? get_cbfs_file_size(files[i]) : CBFS_ALIGNMENT;
 | 
						|
		offset_out = ALIGN_UP(offset_out + offset, CBFS_ALIGNMENT);
 | 
						|
	}
 | 
						|
	return offset_out;
 | 
						|
}
 | 
						|
 | 
						|
/* Setup */
 | 
						|
 | 
						|
static uint8_t
 | 
						|
	aligned_cbfs_ro_buffer[(sizeof(struct cbfs_test_file) + CBFS_ALIGNMENT * 50)] __aligned(
 | 
						|
		CBFS_ALIGNMENT);
 | 
						|
static const size_t aligned_cbfs_ro_buffer_size = sizeof(aligned_cbfs_ro_buffer);
 | 
						|
static uint8_t
 | 
						|
	aligned_cbfs_rw_buffer[(sizeof(struct cbfs_test_file) + CBFS_ALIGNMENT * 50)] __aligned(
 | 
						|
		CBFS_ALIGNMENT);
 | 
						|
static const size_t aligned_cbfs_rw_buffer_size = sizeof(aligned_cbfs_rw_buffer);
 | 
						|
 | 
						|
static uint8_t *unaligned_cbfs_ro_buffer = &aligned_cbfs_ro_buffer[5];
 | 
						|
static const size_t unaligned_cbfs_ro_buffer_size = aligned_cbfs_ro_buffer_size - 5;
 | 
						|
static uint8_t *unaligned_cbfs_rw_buffer = &aligned_cbfs_rw_buffer[5];
 | 
						|
static const size_t unaligned_cbfs_rw_buffer_size = aligned_cbfs_rw_buffer_size - 5;
 | 
						|
 | 
						|
struct cbfs_test_state {
 | 
						|
	uint8_t *cbfs_ro_buf;
 | 
						|
	uint64_t cbfs_ro_size;
 | 
						|
	uint8_t *cbfs_rw_buf;
 | 
						|
	uint64_t cbfs_rw_size;
 | 
						|
 | 
						|
	size_t mcache_ro_offset;
 | 
						|
	size_t mcache_ro_size;
 | 
						|
	size_t mcache_rw_offset;
 | 
						|
	size_t mcache_rw_size;
 | 
						|
 | 
						|
	struct cbfs_test_setup {
 | 
						|
		bool unaligned;
 | 
						|
		bool init_ro;
 | 
						|
		bool init_rw;
 | 
						|
	} ex;
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/* Because of how CMocka works, it should be called in the test function, or in the setup
 | 
						|
   function only if CBFS API capable of initializing RO CBFS boot device is called. */
 | 
						|
static void setup_cbfs_boot_device(struct cbfs_test_state *s)
 | 
						|
{
 | 
						|
	set_fmap_locate_area_results(0, 0, CB_SUCCESS);
 | 
						|
	lib_sysinfo.cbfs_ro_mcache_offset = 0;
 | 
						|
	lib_sysinfo.cbfs_ro_mcache_size = 0;
 | 
						|
	memset((void *)cbfs_get_boot_device(true), 0, sizeof(struct cbfs_boot_device));
 | 
						|
	if (s->ex.init_ro) {
 | 
						|
		set_fmap_locate_area_results((size_t)s->cbfs_ro_buf, s->cbfs_ro_size,
 | 
						|
					     CB_SUCCESS);
 | 
						|
		lib_sysinfo.cbfs_ro_mcache_offset = s->mcache_ro_offset;
 | 
						|
		lib_sysinfo.cbfs_ro_mcache_size = s->mcache_ro_size;
 | 
						|
	}
 | 
						|
 | 
						|
	lib_sysinfo.cbfs_offset = 0;
 | 
						|
	lib_sysinfo.cbfs_size = 0;
 | 
						|
	lib_sysinfo.cbfs_rw_mcache_offset = 0;
 | 
						|
	lib_sysinfo.cbfs_rw_mcache_size = 0;
 | 
						|
	memset((void *)cbfs_get_boot_device(false), 0, sizeof(struct cbfs_boot_device));
 | 
						|
	if (s->ex.init_rw) {
 | 
						|
		lib_sysinfo.cbfs_offset = (uint64_t)s->cbfs_rw_buf;
 | 
						|
		lib_sysinfo.cbfs_size = s->cbfs_rw_size;
 | 
						|
		lib_sysinfo.cbfs_rw_mcache_offset = s->mcache_rw_offset;
 | 
						|
		lib_sysinfo.cbfs_rw_mcache_size = s->mcache_rw_size;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static int setup_cbfs_test(void **state)
 | 
						|
{
 | 
						|
	struct cbfs_test_state *s = calloc(1, sizeof(*s));
 | 
						|
 | 
						|
	if (!s)
 | 
						|
		return 1;
 | 
						|
 | 
						|
	if (*state)
 | 
						|
		memcpy(&s->ex, *state, sizeof(s->ex));
 | 
						|
 | 
						|
	if (s->ex.init_ro) {
 | 
						|
		if (s->ex.unaligned) {
 | 
						|
			s->cbfs_ro_buf = unaligned_cbfs_ro_buffer;
 | 
						|
			s->cbfs_ro_size = unaligned_cbfs_ro_buffer_size;
 | 
						|
		} else {
 | 
						|
			s->cbfs_ro_buf = aligned_cbfs_ro_buffer;
 | 
						|
			s->cbfs_ro_size = aligned_cbfs_ro_buffer_size;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (s->ex.init_rw) {
 | 
						|
		if (s->ex.unaligned) {
 | 
						|
			s->cbfs_rw_buf = unaligned_cbfs_rw_buffer;
 | 
						|
			s->cbfs_rw_size = unaligned_cbfs_rw_buffer_size;
 | 
						|
		} else {
 | 
						|
			s->cbfs_rw_buf = aligned_cbfs_rw_buffer;
 | 
						|
			s->cbfs_rw_size = aligned_cbfs_rw_buffer_size;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	*state = s;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int teardown_cbfs_test(void **state)
 | 
						|
{
 | 
						|
	if (*state)
 | 
						|
		free(*state);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Tests */
 | 
						|
 | 
						|
static void test_cbfs_boot_device_init(void **state)
 | 
						|
{
 | 
						|
	const struct cbfs_boot_device *cbd = NULL;
 | 
						|
 | 
						|
	/* No valid RO, should fail */
 | 
						|
	set_fmap_locate_area_results(0, 0, CB_ERR);
 | 
						|
	lib_sysinfo.cbfs_offset = 0;
 | 
						|
	lib_sysinfo.cbfs_size = 0;
 | 
						|
	lib_sysinfo.cbfs_rw_mcache_size = 0;
 | 
						|
	lib_sysinfo.cbfs_rw_mcache_offset = 0;
 | 
						|
	lib_sysinfo.cbfs_ro_mcache_offset = 0;
 | 
						|
	lib_sysinfo.cbfs_ro_mcache_size = 0;
 | 
						|
	assert_int_equal(NULL, cbfs_get_boot_device(true));
 | 
						|
	assert_null(cbfs_ro_map("file", NULL));
 | 
						|
 | 
						|
	/* Valid RO */
 | 
						|
	set_fmap_locate_area_results(0x12345678, 0x90ABCDEF, CB_SUCCESS);
 | 
						|
	lib_sysinfo.cbfs_ro_mcache_offset = 0x600D41C3;
 | 
						|
	lib_sysinfo.cbfs_ro_mcache_size = 0xBADBEEFF;
 | 
						|
	cbd = cbfs_get_boot_device(true);
 | 
						|
	assert_non_null(cbd);
 | 
						|
	assert_int_equal(0x12345678, cbd->dev.offset);
 | 
						|
	assert_int_equal(0x90ABCDEF, cbd->dev.size);
 | 
						|
	assert_int_equal(0xBADBEEFF, cbd->mcache_size);
 | 
						|
	assert_int_equal(0x600D41C3, cbd->mcache);
 | 
						|
 | 
						|
	lib_sysinfo.cbfs_offset = 0xAABBCCDD;
 | 
						|
	lib_sysinfo.cbfs_size = 0x1000;
 | 
						|
	lib_sysinfo.cbfs_rw_mcache_offset = 0x8F8F8F8F;
 | 
						|
	lib_sysinfo.cbfs_rw_mcache_size = 0x500;
 | 
						|
	cbd = cbfs_get_boot_device(false);
 | 
						|
	assert_non_null(cbd);
 | 
						|
	assert_int_equal(0xAABBCCDD, cbd->dev.offset);
 | 
						|
	assert_int_equal(0x1000, cbd->dev.size);
 | 
						|
	assert_int_equal(0x8F8F8F8F, cbd->mcache);
 | 
						|
	assert_int_equal(0x500, cbd->mcache_size);
 | 
						|
}
 | 
						|
 | 
						|
/* This test checks cbfs_map() basic cases and covers only RW CBFS. */
 | 
						|
void test_cbfs_map(void **state)
 | 
						|
{
 | 
						|
	struct cbfs_test_state *s = *state;
 | 
						|
	void *mapping = NULL;
 | 
						|
	size_t size_out = 0;
 | 
						|
	const struct cbfs_test_file *cbfs_files[] = {
 | 
						|
		&test_file_int_1, &test_file_2, NULL, &test_file_int_3,
 | 
						|
		&test_file_int_2, NULL,		NULL, &test_file_1,
 | 
						|
	};
 | 
						|
	uint8_t *cbfs_buf = NULL;
 | 
						|
	size_t foffset = 0;
 | 
						|
 | 
						|
	setup_cbfs_boot_device(s);
 | 
						|
	cbfs_buf = s->cbfs_rw_buf;
 | 
						|
	create_cbfs(cbfs_files, ARRAY_SIZE(cbfs_files), s->cbfs_rw_buf, s->cbfs_rw_size);
 | 
						|
 | 
						|
	size_out = 0;
 | 
						|
	foffset = get_created_cbfs_file_start_offset(cbfs_files, 0);
 | 
						|
	expect_cbfs_lookup(TEST_DATA_INT_1_FILENAME, CB_SUCCESS,
 | 
						|
			   (const union cbfs_mdata *)&cbfs_buf[foffset],
 | 
						|
			   foffset + be32toh(test_file_int_1.header.offset));
 | 
						|
	will_return(cbfs_find_attr, NULL);
 | 
						|
	mapping = cbfs_map(TEST_DATA_INT_1_FILENAME, &size_out);
 | 
						|
	assert_non_null(mapping);
 | 
						|
	assert_int_equal(TEST_DATA_INT_1_SIZE, size_out);
 | 
						|
	assert_memory_equal(test_data_int_1, mapping, TEST_DATA_INT_1_SIZE);
 | 
						|
	cbfs_unmap(mapping);
 | 
						|
 | 
						|
	size_out = 0;
 | 
						|
	foffset = get_created_cbfs_file_start_offset(cbfs_files, 1);
 | 
						|
	expect_cbfs_lookup(TEST_DATA_2_FILENAME, CB_SUCCESS,
 | 
						|
			   (const union cbfs_mdata *)&cbfs_buf[foffset],
 | 
						|
			   foffset + be32toh(test_file_2.header.offset));
 | 
						|
	will_return(cbfs_find_attr, &test_file_2.attrs_and_data);
 | 
						|
	expect_value(ulzman, srcn, TEST_DATA_2_SIZE);
 | 
						|
	expect_value(ulzman, dstn, TEST_DATA_2_SIZE);
 | 
						|
	mapping = cbfs_map(TEST_DATA_2_FILENAME, &size_out);
 | 
						|
	assert_non_null(mapping);
 | 
						|
	assert_int_equal(TEST_DATA_2_SIZE, size_out);
 | 
						|
	assert_memory_equal(test_data_2, mapping, TEST_DATA_2_SIZE);
 | 
						|
	cbfs_unmap(mapping);
 | 
						|
 | 
						|
	size_out = 0;
 | 
						|
	foffset = get_created_cbfs_file_start_offset(cbfs_files, 3);
 | 
						|
	expect_cbfs_lookup(TEST_DATA_INT_3_FILENAME, CB_SUCCESS,
 | 
						|
			   (const union cbfs_mdata *)&cbfs_buf[foffset],
 | 
						|
			   foffset + be32toh(test_file_int_3.header.offset));
 | 
						|
	will_return(cbfs_find_attr, &test_file_int_3.attrs_and_data);
 | 
						|
	expect_value(ulz4fn, srcn, TEST_DATA_INT_3_SIZE);
 | 
						|
	expect_value(ulz4fn, dstn, TEST_DATA_INT_3_SIZE);
 | 
						|
	mapping = cbfs_map(TEST_DATA_INT_3_FILENAME, &size_out);
 | 
						|
	assert_non_null(mapping);
 | 
						|
	assert_int_equal(TEST_DATA_INT_3_SIZE, size_out);
 | 
						|
	assert_memory_equal(test_data_int_3, mapping, TEST_DATA_INT_3_SIZE);
 | 
						|
	cbfs_unmap(mapping);
 | 
						|
 | 
						|
	size_out = 0;
 | 
						|
	foffset = get_created_cbfs_file_start_offset(cbfs_files, 4);
 | 
						|
	expect_cbfs_lookup(TEST_DATA_INT_2_FILENAME, CB_SUCCESS,
 | 
						|
			   (const union cbfs_mdata *)&cbfs_buf[foffset],
 | 
						|
			   foffset + be32toh(test_file_int_2.header.offset));
 | 
						|
	will_return(cbfs_find_attr, NULL);
 | 
						|
	mapping = cbfs_map(TEST_DATA_INT_2_FILENAME, &size_out);
 | 
						|
	assert_non_null(mapping);
 | 
						|
	assert_int_equal(TEST_DATA_INT_2_SIZE, size_out);
 | 
						|
	assert_memory_equal(test_data_int_2, mapping, TEST_DATA_INT_2_SIZE);
 | 
						|
	cbfs_unmap(mapping);
 | 
						|
 | 
						|
	size_out = 0;
 | 
						|
	foffset = get_created_cbfs_file_start_offset(cbfs_files, 7);
 | 
						|
	expect_cbfs_lookup(TEST_DATA_1_FILENAME, CB_SUCCESS,
 | 
						|
			   (const union cbfs_mdata *)&cbfs_buf[foffset],
 | 
						|
			   foffset + be32toh(test_file_1.header.offset));
 | 
						|
	will_return(cbfs_find_attr, NULL);
 | 
						|
	mapping = cbfs_map(TEST_DATA_1_FILENAME, &size_out);
 | 
						|
	assert_non_null(mapping);
 | 
						|
	assert_int_equal(TEST_DATA_1_SIZE, size_out);
 | 
						|
	assert_memory_equal(test_data_1, mapping, TEST_DATA_1_SIZE);
 | 
						|
	cbfs_unmap(mapping);
 | 
						|
 | 
						|
	size_out = 0;
 | 
						|
	expect_cbfs_lookup("invalid_file", CB_CBFS_NOT_FOUND, 0, 0);
 | 
						|
	if (s->ex.init_rw && CONFIG(LP_ENABLE_CBFS_FALLBACK))
 | 
						|
		expect_cbfs_lookup("invalid_file", CB_CBFS_NOT_FOUND, 0, 0);
 | 
						|
	mapping = cbfs_map("invalid_file", &size_out);
 | 
						|
	assert_null(mapping);
 | 
						|
}
 | 
						|
 | 
						|
static void test_cbfs_invalid_compression_algo(void **state)
 | 
						|
{
 | 
						|
	struct cbfs_test_state *s = *state;
 | 
						|
	void *mapping = NULL;
 | 
						|
	size_t size_out = 0;
 | 
						|
	uint8_t *cbfs_buf = NULL;
 | 
						|
	struct cbfs_test_file *f;
 | 
						|
	struct cbfs_file_attr_compression *comp;
 | 
						|
	const struct cbfs_test_file *cbfs_files[] = {
 | 
						|
		&test_file_2,
 | 
						|
	};
 | 
						|
 | 
						|
	setup_cbfs_boot_device(s);
 | 
						|
	cbfs_buf = s->cbfs_rw_buf;
 | 
						|
	create_cbfs(cbfs_files, ARRAY_SIZE(cbfs_files), s->cbfs_rw_buf, s->cbfs_rw_size);
 | 
						|
 | 
						|
	f = (struct cbfs_test_file *)cbfs_buf;
 | 
						|
	comp = (struct cbfs_file_attr_compression *)&f->attrs_and_data[0];
 | 
						|
	comp->compression = 0xFFFFFFF0;
 | 
						|
 | 
						|
	size_out = 0;
 | 
						|
	expect_cbfs_lookup(TEST_DATA_2_FILENAME, CB_SUCCESS, (const union cbfs_mdata *)cbfs_buf,
 | 
						|
			   be32toh(test_file_1.header.offset));
 | 
						|
	will_return(cbfs_find_attr, comp);
 | 
						|
	mapping = cbfs_map(TEST_DATA_2_FILENAME, &size_out);
 | 
						|
	assert_null(mapping);
 | 
						|
}
 | 
						|
 | 
						|
static void test_cbfs_io_error(void **state)
 | 
						|
{
 | 
						|
	struct cbfs_test_state *s = *state;
 | 
						|
	setup_cbfs_boot_device(s);
 | 
						|
 | 
						|
	expect_cbfs_lookup(TEST_DATA_1_FILENAME, CB_CBFS_IO, 0, 0);
 | 
						|
	assert_null(cbfs_map(TEST_DATA_1_FILENAME, NULL));
 | 
						|
}
 | 
						|
 | 
						|
static void test_cbfs_successful_fallback_to_ro(void **state)
 | 
						|
{
 | 
						|
	struct cbfs_test_state *s = *state;
 | 
						|
	void *mapping = NULL;
 | 
						|
	size_t size_out = 0;
 | 
						|
	const struct cbfs_test_file *cbfs_files[] = {
 | 
						|
		&test_file_1,	  &test_file_2,	    &test_file_int_1,
 | 
						|
		&test_file_int_1, &test_file_int_2, &test_file_int_3,
 | 
						|
	};
 | 
						|
	uint8_t *cbfs_buf = NULL;
 | 
						|
	size_t foffset = 0;
 | 
						|
 | 
						|
	if (!CONFIG(LP_ENABLE_CBFS_FALLBACK)) {
 | 
						|
		print_message("Skipping test, because LP_ENABLE_CBFS_FALLBACK == 0\n");
 | 
						|
		skip();
 | 
						|
	}
 | 
						|
 | 
						|
	setup_cbfs_boot_device(s);
 | 
						|
	cbfs_buf = s->cbfs_ro_buf;
 | 
						|
	create_cbfs(cbfs_files, ARRAY_SIZE(cbfs_files), s->cbfs_ro_buf, s->cbfs_ro_size);
 | 
						|
	if (s->ex.init_rw)
 | 
						|
		create_cbfs(cbfs_files, ARRAY_SIZE(cbfs_files) - 2, s->cbfs_rw_buf,
 | 
						|
			    s->cbfs_rw_size);
 | 
						|
 | 
						|
	size_out = 0;
 | 
						|
	foffset = get_created_cbfs_file_start_offset(cbfs_files, 1);
 | 
						|
	expect_cbfs_lookup(TEST_DATA_2_FILENAME, CB_CBFS_NOT_FOUND, 0, 0);
 | 
						|
	expect_cbfs_lookup(TEST_DATA_2_FILENAME, CB_SUCCESS,
 | 
						|
			   (const union cbfs_mdata *)&cbfs_buf[foffset],
 | 
						|
			   foffset + be32toh(test_file_2.header.offset));
 | 
						|
	will_return(cbfs_find_attr, &test_file_2.attrs_and_data);
 | 
						|
	expect_value(ulzman, srcn, TEST_DATA_2_SIZE);
 | 
						|
	expect_value(ulzman, dstn, TEST_DATA_2_SIZE);
 | 
						|
	mapping = cbfs_map(TEST_DATA_2_FILENAME, &size_out);
 | 
						|
	assert_non_null(mapping);
 | 
						|
	assert_int_equal(TEST_DATA_2_SIZE, size_out);
 | 
						|
	assert_memory_equal(test_data_2, mapping, TEST_DATA_2_SIZE);
 | 
						|
	cbfs_unmap(mapping);
 | 
						|
 | 
						|
	size_out = 0;
 | 
						|
	foffset = get_created_cbfs_file_start_offset(cbfs_files, 5);
 | 
						|
	expect_cbfs_lookup(TEST_DATA_INT_3_FILENAME, CB_CBFS_NOT_FOUND, 0, 0);
 | 
						|
	expect_cbfs_lookup(TEST_DATA_INT_3_FILENAME, CB_SUCCESS,
 | 
						|
			   (const union cbfs_mdata *)&cbfs_buf[foffset],
 | 
						|
			   foffset + be32toh(test_file_int_3.header.offset));
 | 
						|
	will_return(cbfs_find_attr, &test_file_int_3.attrs_and_data);
 | 
						|
	expect_value(ulz4fn, srcn, TEST_DATA_INT_3_SIZE);
 | 
						|
	expect_value(ulz4fn, dstn, TEST_DATA_INT_3_SIZE);
 | 
						|
	mapping = cbfs_map(TEST_DATA_INT_3_FILENAME, &size_out);
 | 
						|
	assert_non_null(mapping);
 | 
						|
	assert_int_equal(TEST_DATA_INT_3_SIZE, size_out);
 | 
						|
	assert_memory_equal(test_data_int_3, mapping, TEST_DATA_INT_3_SIZE);
 | 
						|
	cbfs_unmap(mapping);
 | 
						|
}
 | 
						|
 | 
						|
static void test_cbfs_load(void **state)
 | 
						|
{
 | 
						|
	struct cbfs_test_state *s = *state;
 | 
						|
	size_t size_out = 0;
 | 
						|
	const struct cbfs_test_file *cbfs_files[] = {
 | 
						|
		&test_file_int_1, &test_file_2, NULL, &test_file_int_3,
 | 
						|
		&test_file_int_2, NULL,		NULL, &test_file_1,
 | 
						|
	};
 | 
						|
	uint8_t *cbfs_buf = NULL;
 | 
						|
	uint8_t load_buf[1 * KiB];
 | 
						|
	size_t foffset = 0;
 | 
						|
 | 
						|
	setup_cbfs_boot_device(s);
 | 
						|
	cbfs_buf = s->cbfs_rw_buf;
 | 
						|
	create_cbfs(cbfs_files, ARRAY_SIZE(cbfs_files), s->cbfs_rw_buf, s->cbfs_rw_size);
 | 
						|
 | 
						|
	/* Successful load */
 | 
						|
	size_out = 0;
 | 
						|
	foffset = get_created_cbfs_file_start_offset(cbfs_files, 0);
 | 
						|
	expect_cbfs_lookup(TEST_DATA_INT_1_FILENAME, CB_SUCCESS,
 | 
						|
			   (const union cbfs_mdata *)&cbfs_buf[foffset],
 | 
						|
			   foffset + be32toh(test_file_int_1.header.offset));
 | 
						|
	will_return(cbfs_find_attr, NULL);
 | 
						|
	size_out = cbfs_load(TEST_DATA_INT_1_FILENAME, load_buf, sizeof(load_buf));
 | 
						|
	assert_int_equal(TEST_DATA_INT_1_SIZE, size_out);
 | 
						|
	assert_memory_equal(test_data_int_1, load_buf, TEST_DATA_INT_1_SIZE);
 | 
						|
 | 
						|
	/* Buffer too small */
 | 
						|
	size_out = 0;
 | 
						|
	foffset = get_created_cbfs_file_start_offset(cbfs_files, 7);
 | 
						|
	expect_cbfs_lookup(TEST_DATA_1_FILENAME, CB_SUCCESS,
 | 
						|
			   (const union cbfs_mdata *)&cbfs_buf[foffset],
 | 
						|
			   foffset + be32toh(test_file_1.header.offset));
 | 
						|
	will_return(cbfs_find_attr, NULL);
 | 
						|
	size_out = cbfs_load(TEST_DATA_1_FILENAME, load_buf, TEST_DATA_1_SIZE / 2);
 | 
						|
	assert_int_equal(0, size_out);
 | 
						|
}
 | 
						|
 | 
						|
static void test_cbfs_map_with_mcache(void **state)
 | 
						|
{
 | 
						|
	struct cbfs_test_state *s = *state;
 | 
						|
	void *mapping = NULL;
 | 
						|
	size_t size_out = 0;
 | 
						|
	const struct cbfs_test_file *cbfs_files[] = {
 | 
						|
		&test_file_int_2, &test_file_1,	    NULL,
 | 
						|
		&test_file_int_3, &test_file_int_1, &test_file_2,
 | 
						|
	};
 | 
						|
	uint8_t *cbfs_buf = NULL;
 | 
						|
	size_t foffset = 0;
 | 
						|
 | 
						|
	/* Will not be accessed, just needs to be valid. */
 | 
						|
	s->mcache_ro_offset = ALIGN_UP(0x1000, CBFS_MCACHE_ALIGNMENT);
 | 
						|
	s->mcache_ro_size = ALIGN_UP(0x500, CBFS_MCACHE_ALIGNMENT);
 | 
						|
	s->mcache_rw_offset = ALIGN_UP(0x3000, CBFS_MCACHE_ALIGNMENT);
 | 
						|
	s->mcache_rw_size = ALIGN_UP(0x600, CBFS_MCACHE_ALIGNMENT);
 | 
						|
	setup_cbfs_boot_device(s);
 | 
						|
	cbfs_buf = s->cbfs_rw_buf;
 | 
						|
	create_cbfs(cbfs_files, ARRAY_SIZE(cbfs_files), s->cbfs_rw_buf, s->cbfs_rw_size);
 | 
						|
 | 
						|
	size_out = 0;
 | 
						|
	foffset = get_created_cbfs_file_start_offset(cbfs_files, 4);
 | 
						|
	expect_cbfs_mcache_lookup(TEST_DATA_INT_1_FILENAME, CB_SUCCESS,
 | 
						|
				  (const union cbfs_mdata *)&cbfs_buf[foffset],
 | 
						|
				  foffset + be32toh(test_file_int_1.header.offset));
 | 
						|
	will_return(cbfs_find_attr, NULL);
 | 
						|
	mapping = cbfs_map(TEST_DATA_INT_1_FILENAME, &size_out);
 | 
						|
	assert_non_null(mapping);
 | 
						|
	assert_int_equal(TEST_DATA_INT_1_SIZE, size_out);
 | 
						|
	assert_memory_equal(test_data_int_1, mapping, TEST_DATA_INT_1_SIZE);
 | 
						|
	cbfs_unmap(mapping);
 | 
						|
}
 | 
						|
 | 
						|
static void test_cbfs_boot_device_read_failure(void **state)
 | 
						|
{
 | 
						|
	struct cbfs_test_state *s = *state;
 | 
						|
	void *mapping = NULL;
 | 
						|
	size_t size_out = 0;
 | 
						|
	const struct cbfs_test_file *cbfs_files[] = {
 | 
						|
		&test_file_int_3, &test_file_1,	    NULL,
 | 
						|
		&test_file_int_3, &test_file_int_1, &test_file_2,
 | 
						|
	};
 | 
						|
	uint8_t *cbfs_buf = NULL;
 | 
						|
	size_t foffset = 0;
 | 
						|
 | 
						|
	setup_cbfs_boot_device(s);
 | 
						|
	cbfs_buf = s->cbfs_rw_buf;
 | 
						|
	create_cbfs(cbfs_files, ARRAY_SIZE(cbfs_files), s->cbfs_rw_buf, s->cbfs_rw_size);
 | 
						|
 | 
						|
	size_out = 0;
 | 
						|
	foffset = get_created_cbfs_file_start_offset(cbfs_files, 1);
 | 
						|
	expect_cbfs_lookup(TEST_DATA_1_FILENAME, CB_SUCCESS,
 | 
						|
			   (const union cbfs_mdata *)&cbfs_buf[foffset],
 | 
						|
			   foffset + be32toh(test_file_1.header.offset));
 | 
						|
	will_return(cbfs_find_attr, NULL);
 | 
						|
	force_single_boot_device_size_failure = true;
 | 
						|
	mapping = cbfs_map(TEST_DATA_1_FILENAME, &size_out);
 | 
						|
	assert_null(mapping);
 | 
						|
}
 | 
						|
 | 
						|
/* This test uses RW CBFS only */
 | 
						|
static void test_cbfs_unverified_area_map(void **state)
 | 
						|
{
 | 
						|
	struct cbfs_test_state *s = *state;
 | 
						|
	void *mapping = NULL;
 | 
						|
	size_t size_out = 0;
 | 
						|
	const struct cbfs_test_file *cbfs_files[] = {
 | 
						|
		&test_file_int_1, &test_file_2, NULL, &test_file_int_3,
 | 
						|
		&test_file_int_2, NULL,		NULL, &test_file_1,
 | 
						|
	};
 | 
						|
	uint8_t *cbfs_buf = NULL;
 | 
						|
	size_t foffset = 0;
 | 
						|
 | 
						|
	cbfs_buf = s->cbfs_rw_buf;
 | 
						|
	set_fmap_locate_area_results((size_t)cbfs_buf, s->cbfs_rw_size, CB_SUCCESS);
 | 
						|
	create_cbfs(cbfs_files, ARRAY_SIZE(cbfs_files), s->cbfs_rw_buf, s->cbfs_rw_size);
 | 
						|
 | 
						|
	size_out = 0;
 | 
						|
	foffset = get_created_cbfs_file_start_offset(cbfs_files, 0);
 | 
						|
	expect_cbfs_lookup(TEST_DATA_INT_1_FILENAME, CB_SUCCESS,
 | 
						|
			   (const union cbfs_mdata *)&cbfs_buf[foffset],
 | 
						|
			   foffset + be32toh(test_file_int_1.header.offset));
 | 
						|
	will_return(cbfs_find_attr, NULL);
 | 
						|
	mapping = cbfs_unverified_area_map("TEST_AREA", TEST_DATA_INT_1_FILENAME, &size_out);
 | 
						|
	assert_non_null(mapping);
 | 
						|
	assert_int_equal(TEST_DATA_INT_1_SIZE, size_out);
 | 
						|
	assert_memory_equal(test_data_int_1, mapping, TEST_DATA_INT_1_SIZE);
 | 
						|
	cbfs_unmap(mapping);
 | 
						|
 | 
						|
	size_out = 0;
 | 
						|
	foffset = get_created_cbfs_file_start_offset(cbfs_files, 1);
 | 
						|
	expect_cbfs_lookup(TEST_DATA_2_FILENAME, CB_SUCCESS,
 | 
						|
			   (const union cbfs_mdata *)&cbfs_buf[foffset],
 | 
						|
			   foffset + be32toh(test_file_2.header.offset));
 | 
						|
	will_return(cbfs_find_attr, &test_file_2.attrs_and_data);
 | 
						|
	expect_value(ulzman, srcn, TEST_DATA_2_SIZE);
 | 
						|
	expect_value(ulzman, dstn, TEST_DATA_2_SIZE);
 | 
						|
	mapping = cbfs_unverified_area_map("TEST_AREA", TEST_DATA_2_FILENAME, &size_out);
 | 
						|
	assert_non_null(mapping);
 | 
						|
	assert_int_equal(TEST_DATA_2_SIZE, size_out);
 | 
						|
	assert_memory_equal(test_data_2, mapping, TEST_DATA_2_SIZE);
 | 
						|
	cbfs_unmap(mapping);
 | 
						|
 | 
						|
	size_out = 0;
 | 
						|
	foffset = get_created_cbfs_file_start_offset(cbfs_files, 3);
 | 
						|
	expect_cbfs_lookup(TEST_DATA_INT_3_FILENAME, CB_SUCCESS,
 | 
						|
			   (const union cbfs_mdata *)&cbfs_buf[foffset],
 | 
						|
			   foffset + be32toh(test_file_int_3.header.offset));
 | 
						|
	will_return(cbfs_find_attr, &test_file_int_3.attrs_and_data);
 | 
						|
	expect_value(ulz4fn, srcn, TEST_DATA_INT_3_SIZE);
 | 
						|
	expect_value(ulz4fn, dstn, TEST_DATA_INT_3_SIZE);
 | 
						|
	mapping = cbfs_unverified_area_map("TEST_AREA", TEST_DATA_INT_3_FILENAME, &size_out);
 | 
						|
	assert_non_null(mapping);
 | 
						|
	assert_int_equal(TEST_DATA_INT_3_SIZE, size_out);
 | 
						|
	assert_memory_equal(test_data_int_3, mapping, TEST_DATA_INT_3_SIZE);
 | 
						|
	cbfs_unmap(mapping);
 | 
						|
 | 
						|
	size_out = 0;
 | 
						|
	foffset = get_created_cbfs_file_start_offset(cbfs_files, 4);
 | 
						|
	expect_cbfs_lookup(TEST_DATA_INT_2_FILENAME, CB_SUCCESS,
 | 
						|
			   (const union cbfs_mdata *)&cbfs_buf[foffset],
 | 
						|
			   foffset + be32toh(test_file_int_2.header.offset));
 | 
						|
	will_return(cbfs_find_attr, NULL);
 | 
						|
	mapping = cbfs_unverified_area_map("TEST_AREA", TEST_DATA_INT_2_FILENAME, &size_out);
 | 
						|
	assert_non_null(mapping);
 | 
						|
	assert_int_equal(TEST_DATA_INT_2_SIZE, size_out);
 | 
						|
	assert_memory_equal(test_data_int_2, mapping, TEST_DATA_INT_2_SIZE);
 | 
						|
	cbfs_unmap(mapping);
 | 
						|
 | 
						|
	size_out = 0;
 | 
						|
	foffset = get_created_cbfs_file_start_offset(cbfs_files, 7);
 | 
						|
	expect_cbfs_lookup(TEST_DATA_1_FILENAME, CB_SUCCESS,
 | 
						|
			   (const union cbfs_mdata *)&cbfs_buf[foffset],
 | 
						|
			   foffset + be32toh(test_file_1.header.offset));
 | 
						|
	will_return(cbfs_find_attr, NULL);
 | 
						|
	mapping = cbfs_unverified_area_map("TEST_AREA", TEST_DATA_1_FILENAME, &size_out);
 | 
						|
	assert_non_null(mapping);
 | 
						|
	assert_int_equal(TEST_DATA_1_SIZE, size_out);
 | 
						|
	assert_memory_equal(test_data_1, mapping, TEST_DATA_1_SIZE);
 | 
						|
	cbfs_unmap(mapping);
 | 
						|
 | 
						|
	size_out = 0;
 | 
						|
	expect_cbfs_lookup("invalid_file", CB_CBFS_NOT_FOUND, 0, 0);
 | 
						|
	mapping = cbfs_unverified_area_map("TEST_AREA", "invalid_file", &size_out);
 | 
						|
	assert_null(mapping);
 | 
						|
}
 | 
						|
 | 
						|
#define TEST_CBFS_NAME_ALIGN_RO_RW(fn, test_name, enable_unaligned, enable_init_ro,            \
 | 
						|
				   enable_init_rw)                                             \
 | 
						|
	((struct CMUnitTest){                                                                  \
 | 
						|
		.name = (test_name),                                                           \
 | 
						|
		.test_func = (fn),                                                             \
 | 
						|
		.setup_func = setup_cbfs_test,                                                 \
 | 
						|
		.teardown_func = teardown_cbfs_test,                                           \
 | 
						|
		.initial_state =                                                               \
 | 
						|
			&(struct cbfs_test_setup){                                             \
 | 
						|
				.unaligned = enable_unaligned,                                 \
 | 
						|
				.init_ro = enable_init_ro,                                     \
 | 
						|
				.init_rw = enable_init_rw,                                     \
 | 
						|
			},                                                                     \
 | 
						|
	})
 | 
						|
 | 
						|
#define TEST_CBFS_LOOKUP(fn)                                                                   \
 | 
						|
	EMPTY_WRAP(TEST_CBFS_NAME_ALIGN_RO_RW(fn, #fn ", RW, aligned", false, false, true),    \
 | 
						|
		   TEST_CBFS_NAME_ALIGN_RO_RW(fn, #fn ", RW, unaligned", true, false, true))
 | 
						|
 | 
						|
#define TEST_CBFS_RO_FALLBACK(fn)                                                              \
 | 
						|
	EMPTY_WRAP(TEST_CBFS_NAME_ALIGN_RO_RW(fn, #fn ", RW+RO, aligned", false, true, true),  \
 | 
						|
		   TEST_CBFS_NAME_ALIGN_RO_RW(fn, #fn ", RW+RO, unaligned", true, true, true), \
 | 
						|
		   TEST_CBFS_NAME_ALIGN_RO_RW(fn, #fn ", RO, aligned", false, true, false),    \
 | 
						|
		   TEST_CBFS_NAME_ALIGN_RO_RW(fn, #fn ", RO, unaligned", true, true, false))
 | 
						|
 | 
						|
 | 
						|
int main(void)
 | 
						|
{
 | 
						|
	const struct CMUnitTest tests[] = {
 | 
						|
		cmocka_unit_test(test_cbfs_boot_device_init),
 | 
						|
		TEST_CBFS_LOOKUP(test_cbfs_map),
 | 
						|
		TEST_CBFS_LOOKUP(test_cbfs_invalid_compression_algo),
 | 
						|
		TEST_CBFS_LOOKUP(test_cbfs_io_error),
 | 
						|
		TEST_CBFS_RO_FALLBACK(test_cbfs_successful_fallback_to_ro),
 | 
						|
		TEST_CBFS_LOOKUP(test_cbfs_load),
 | 
						|
		TEST_CBFS_LOOKUP(test_cbfs_map_with_mcache),
 | 
						|
		TEST_CBFS_LOOKUP(test_cbfs_boot_device_read_failure),
 | 
						|
		TEST_CBFS_LOOKUP(test_cbfs_unverified_area_map),
 | 
						|
	};
 | 
						|
 | 
						|
	return lp_run_group_tests(tests, NULL, NULL);
 | 
						|
}
 |