From c1e4c5aaa5cbe9b1fa8d16be9b23038bc5fbc9b8 Mon Sep 17 00:00:00 2001 From: Jakub Czapiga Date: Fri, 23 Jul 2021 13:35:14 +0200 Subject: tests: Add lib/lzma-test test case Files used by this test are in: tests/data/lib/lzma-test/ file.bin - files with uncompressed data file.lzma.bin - files with LZMA-compressed data from file.bin How to prepare compressed file: util/cbfs-compression-tool compress file.bin /tmp/file.lzma.bin lzma dd if=/tmp/file.lzma.bin of=file.lzma.bin skip=8 ibs=1 Signed-off-by: Jakub Czapiga Change-Id: Id75e0b41991382d4c391b031862106de58eacdf7 Reviewed-on: https://review.coreboot.org/c/coreboot/+/57555 Tested-by: build bot (Jenkins) Reviewed-by: Paul Fagerburg Reviewed-by: Julius Werner --- tests/Makefile.inc | 3 +- tests/data/lib/lzma-test/data.1.bin | Bin 0 -> 124784 bytes tests/data/lib/lzma-test/data.1.lzma.bin | Bin 0 -> 52133 bytes tests/data/lib/lzma-test/data.2.bin | 104 +++++ tests/data/lib/lzma-test/data.2.lzma.bin | Bin 0 -> 1537 bytes tests/data/lib/lzma-test/data.3.bin | 764 +++++++++++++++++++++++++++++++ tests/data/lib/lzma-test/data.3.lzma.bin | Bin 0 -> 3865 bytes tests/data/lib/lzma-test/data.4.bin | Bin 0 -> 60912 bytes tests/data/lib/lzma-test/data.4.lzma.bin | Bin 0 -> 17064 bytes tests/lib/Makefile.inc | 6 + tests/lib/lzma-test.c | 190 ++++++++ 11 files changed, 1066 insertions(+), 1 deletion(-) create mode 100644 tests/data/lib/lzma-test/data.1.bin create mode 100644 tests/data/lib/lzma-test/data.1.lzma.bin create mode 100644 tests/data/lib/lzma-test/data.2.bin create mode 100644 tests/data/lib/lzma-test/data.2.lzma.bin create mode 100644 tests/data/lib/lzma-test/data.3.bin create mode 100644 tests/data/lib/lzma-test/data.3.lzma.bin create mode 100644 tests/data/lib/lzma-test/data.4.bin create mode 100644 tests/data/lib/lzma-test/data.4.lzma.bin create mode 100644 tests/lib/lzma-test.c (limited to 'tests') diff --git a/tests/Makefile.inc b/tests/Makefile.inc index 982f42baf7a4..6397e9b67013 100644 --- a/tests/Makefile.inc +++ b/tests/Makefile.inc @@ -141,7 +141,8 @@ $$($(1)-config-file): $(TEST_KCONFIG_AUTOHEADER) $($(1)-objs): TEST_CFLAGS += -I$$(dir $$($(1)-config-file)) \ -D__$$(shell echo $$($(1)-stage) | tr '[:lower:]' '[:upper:]')__ \ - -D__TEST_NAME__=\"$(subst /,_,$(1))\" + -D__TEST_NAME__=\"$(subst /,_,$(1))\" \ + -D__TEST_DATA_DIR__=\"$(testsrc)/data\" # Give us a way to distinguish between coreboot source files and test files in code. $($(1)-srcobjs): TEST_CFLAGS += -D__TEST_SRCOBJ__ diff --git a/tests/data/lib/lzma-test/data.1.bin b/tests/data/lib/lzma-test/data.1.bin new file mode 100644 index 000000000000..2ac7245fd053 Binary files /dev/null and b/tests/data/lib/lzma-test/data.1.bin differ diff --git a/tests/data/lib/lzma-test/data.1.lzma.bin b/tests/data/lib/lzma-test/data.1.lzma.bin new file mode 100644 index 000000000000..c3565b863dc5 Binary files /dev/null and b/tests/data/lib/lzma-test/data.1.lzma.bin differ diff --git a/tests/data/lib/lzma-test/data.2.bin b/tests/data/lib/lzma-test/data.2.bin new file mode 100644 index 000000000000..14879c14842f --- /dev/null +++ b/tests/data/lib/lzma-test/data.2.bin @@ -0,0 +1,104 @@ +coreboot README +=============== + +coreboot is a Free Software project aimed at replacing the proprietary BIOS +(firmware) found in most computers. coreboot performs a little bit of +hardware initialization and then executes additional boot logic, called a +payload. + +With the separation of hardware initialization and later boot logic, +coreboot can scale from specialized applications that run directly +firmware, run operating systems in flash, load custom +bootloaders, or implement firmware standards, like PC BIOS services or +UEFI. This allows for systems to only include the features necessary +in the target application, reducing the amount of code and flash space +required. + +coreboot was formerly known as LinuxBIOS. + + +Payloads +-------- + +After the basic initialization of the hardware has been performed, any +desired "payload" can be started by coreboot. + +See for a list of supported payloads. + + +Supported Hardware +------------------ + +coreboot supports a wide range of chipsets, devices, and mainboards. + +For details please consult: + + * + + +Build Requirements +------------------ + + * make + * gcc / g++ + Because Linux distribution compilers tend to use lots of patches. coreboot + does lots of "unusual" things in its build system, some of which break due + to those patches, sometimes by gcc aborting, sometimes - and that's worse - + by generating broken object code. + Two options: use our toolchain (eg. make crosstools-i386) or enable the + `ANY_TOOLCHAIN` Kconfig option if you're feeling lucky (no support in this + case). + * iasl (for targets with ACPI support) + * pkg-config + * libssl-dev (openssl) + +Optional: + + * doxygen (for generating/viewing documentation) + * gdb (for better debugging facilities on some targets) + * ncurses (for `make menuconfig` and `make nconfig`) + * flex and bison (for regenerating parsers) + + +Building coreboot +----------------- + +Please consult for details. + + +Testing coreboot Without Modifying Your Hardware +------------------------------------------------ + +If you want to test coreboot without any risks before you really decide +to use it on your hardware, you can use the QEMU system emulator to run +coreboot virtually in QEMU. + +Please see for details. + + +Website and Mailing List +------------------------ + +Further details on the project, a FAQ, many HOWTOs, news, development +guidelines and more can be found on the coreboot website: + + + +You can contact us directly on the coreboot mailing list: + + + + +Copyright and License +--------------------- + +The copyright on coreboot is owned by quite a large number of individual +developers and companies. Please check the individual source files for details. + +coreboot is licensed under the terms of the GNU General Public License (GPL). +Some files are licensed under the "GPL (version 2, or any later version)", +and some files are licensed under the "GPL, version 2". For some parts, which +were derived from other projects, other (GPL-compatible) licenses may apply. +Please check the individual source files for details. + +This makes the resulting coreboot images licensed under the GPL, version 2. diff --git a/tests/data/lib/lzma-test/data.2.lzma.bin b/tests/data/lib/lzma-test/data.2.lzma.bin new file mode 100644 index 000000000000..e1dd3f7b047c Binary files /dev/null and b/tests/data/lib/lzma-test/data.2.lzma.bin differ diff --git a/tests/data/lib/lzma-test/data.3.bin b/tests/data/lib/lzma-test/data.3.bin new file mode 100644 index 000000000000..6da1ac926c6f --- /dev/null +++ b/tests/data/lib/lzma-test/data.3.bin @@ -0,0 +1,764 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Auxiliary functions and definitions. */ + +#define LG_ROOT_SIZE align_up_pow2(sizeof(struct imd_root_pointer) +\ + sizeof(struct imd_root) + 3 * sizeof(struct imd_entry)) +#define LG_ENTRY_ALIGN (2 * sizeof(int32_t)) +#define LG_ENTRY_SIZE (2 * sizeof(int32_t)) +#define LG_ENTRY_ID 0xA001 + +#define SM_ROOT_SIZE LG_ROOT_SIZE +#define SM_ENTRY_ALIGN sizeof(uint32_t) +#define SM_ENTRY_SIZE sizeof(uint32_t) +#define SM_ENTRY_ID 0xB001 + +#define INVALID_REGION_ID 0xC001 + +static uint32_t align_up_pow2(uint32_t x) +{ + return (1 << log2_ceil(x)); +} + +static size_t max_entries(size_t root_size) +{ + return (root_size - sizeof(struct imd_root_pointer) - sizeof(struct imd_root)) + / sizeof(struct imd_entry); +} + +/* + * Mainly, we should check that imd_handle_init() aligns upper_limit properly + * for various inputs. Upper limit is the _exclusive_ address, so we expect + * ALIGN_DOWN. + */ +static void test_imd_handle_init(void **state) +{ + int i; + void *base; + struct imd imd; + uintptr_t test_inputs[] = { + 0, /* Lowest possible address */ + 0xA000, /* Fits in 16 bits, should not get rounded down*/ + 0xDEAA, /* Fits in 16 bits */ + 0xB0B0B000, /* Fits in 32 bits, should not get rounded down */ + 0xF0F0F0F0, /* Fits in 32 bits */ + ((1ULL << 32) + 4), /* Just above 32-bit limit */ + 0x6666777788889000, /* Fits in 64 bits, should not get rounded down */ + ((1ULL << 60) - 100) /* Very large address, fitting in 64 bits */ + }; + + for (i = 0; i < ARRAY_SIZE(test_inputs); i++) { + base = (void *)test_inputs[i]; + + imd_handle_init(&imd, (void *)base); + + assert_int_equal(imd.lg.limit % LIMIT_ALIGN, 0); + assert_int_equal(imd.lg.limit, ALIGN_DOWN(test_inputs[i], LIMIT_ALIGN)); + assert_ptr_equal(imd.lg.r, NULL); + + /* Small allocations not initialized */ + assert_ptr_equal(imd.sm.limit, NULL); + assert_ptr_equal(imd.sm.r, NULL); + } +} + +static void test_imd_handle_init_partial_recovery(void **state) +{ + void *base; + struct imd imd = {0}; + const struct imd_entry *entry; + + imd_handle_init_partial_recovery(&imd); + assert_null(imd.lg.limit); + assert_null(imd.sm.limit); + + base = malloc(LIMIT_ALIGN); + if (base == NULL) + fail_msg("Cannot allocate enough memory - fail test"); + + imd_handle_init(&imd, (void *)(LIMIT_ALIGN + (uintptr_t)base)); + imd_handle_init_partial_recovery(&imd); + + assert_non_null(imd.lg.r); + assert_null(imd.sm.limit); + + assert_int_equal(0, imd_create_empty(&imd, LG_ROOT_SIZE, LG_ENTRY_ALIGN)); + entry = imd_entry_add(&imd, SMALL_REGION_ID, LG_ENTRY_SIZE); + assert_non_null(entry); + + imd_handle_init_partial_recovery(&imd); + + assert_non_null(imd.lg.r); + assert_non_null(imd.sm.limit); + assert_ptr_equal(imd.lg.r + entry->start_offset + LG_ENTRY_SIZE, imd.sm.limit); + assert_non_null(imd.sm.r); + + free(base); +} + +static void test_imd_create_empty(void **state) +{ + struct imd imd = {0}; + void *base; + struct imd_root *r; + struct imd_entry *e; + + /* Expect imd_create_empty to fail, since imd handle is not initialized */ + assert_int_equal(-1, imd_create_empty(&imd, LG_ROOT_SIZE, LG_ENTRY_ALIGN)); + base = malloc(sizeof(struct imd_root_pointer) + sizeof(struct imd_root)); + if (base == NULL) + fail_msg("Cannot allocate enough memory - fail test"); + + imd_handle_init(&imd, (void *)(LIMIT_ALIGN + (uintptr_t)base)); + + /* Try incorrect sizes */ + assert_int_equal(-1, imd_create_empty(&imd, + sizeof(struct imd_root_pointer), + LG_ENTRY_ALIGN)); + assert_int_equal(-1, imd_create_empty(&imd, LG_ROOT_SIZE, 2 * LG_ROOT_SIZE)); + + /* Working case */ + assert_int_equal(0, imd_create_empty(&imd, LG_ROOT_SIZE, LG_ENTRY_ALIGN)); + + /* Only large allocation initialized with one entry for the root region */ + r = (struct imd_root *) (imd.lg.r); + assert_non_null(r); + + e = &r->entries[r->num_entries - 1]; + + assert_int_equal(max_entries(LG_ROOT_SIZE), r->max_entries); + assert_int_equal(1, r->num_entries); + assert_int_equal(0, r->flags); + assert_int_equal(LG_ENTRY_ALIGN, r->entry_align); + assert_int_equal(0, r->max_offset); + assert_ptr_equal(e, &r->entries); + + assert_int_equal(IMD_ENTRY_MAGIC, e->magic); + assert_int_equal(0, e->start_offset); + assert_int_equal(LG_ROOT_SIZE, e->size); + assert_int_equal(CBMEM_ID_IMD_ROOT, e->id); + + free(base); +} + +static void test_imd_create_tiered_empty(void **state) +{ + void *base; + size_t sm_region_size, lg_region_wrong_size; + struct imd imd = {0}; + struct imd_root *r; + struct imd_entry *fst_lg_entry, *snd_lg_entry, *sm_entry; + + /* Uninitialized imd handle */ + assert_int_equal(-1, imd_create_tiered_empty(&imd, LG_ROOT_SIZE, LG_ENTRY_ALIGN, + LG_ROOT_SIZE, SM_ENTRY_ALIGN)); + + base = malloc(LIMIT_ALIGN); + if (base == NULL) + fail_msg("Cannot allocate enough memory - fail test"); + + imd_handle_init(&imd, (void *)(LIMIT_ALIGN + (uintptr_t)base)); + + /* Too small root_size for small region */ + assert_int_equal(-1, imd_create_tiered_empty(&imd, LG_ROOT_SIZE, LG_ENTRY_ALIGN, + sizeof(int32_t), 2 * sizeof(int32_t))); + + /* Fail when large region doesn't have capacity for more than 1 entry */ + lg_region_wrong_size = sizeof(struct imd_root_pointer) + sizeof(struct imd_root) + + sizeof(struct imd_entry); + expect_assert_failure( + imd_create_tiered_empty(&imd, lg_region_wrong_size, LG_ENTRY_ALIGN, + SM_ROOT_SIZE, SM_ENTRY_ALIGN) + ); + + assert_int_equal(0, imd_create_tiered_empty(&imd, LG_ROOT_SIZE, LG_ENTRY_ALIGN, + SM_ROOT_SIZE, SM_ENTRY_ALIGN)); + + r = imd.lg.r; + + /* One entry for root_region and one for small allocations */ + assert_int_equal(2, r->num_entries); + + fst_lg_entry = &r->entries[0]; + assert_int_equal(IMD_ENTRY_MAGIC, fst_lg_entry->magic); + assert_int_equal(0, fst_lg_entry->start_offset); + assert_int_equal(LG_ROOT_SIZE, fst_lg_entry->size); + assert_int_equal(CBMEM_ID_IMD_ROOT, fst_lg_entry->id); + + /* Calculated like in imd_create_tiered_empty */ + sm_region_size = max_entries(SM_ROOT_SIZE) * SM_ENTRY_ALIGN; + sm_region_size += SM_ROOT_SIZE; + sm_region_size = ALIGN_UP(sm_region_size, LG_ENTRY_ALIGN); + + snd_lg_entry = &r->entries[1]; + assert_int_equal(IMD_ENTRY_MAGIC, snd_lg_entry->magic); + assert_int_equal(-sm_region_size, snd_lg_entry->start_offset); + assert_int_equal(CBMEM_ID_IMD_SMALL, snd_lg_entry->id); + + assert_int_equal(sm_region_size, snd_lg_entry->size); + + r = imd.sm.r; + assert_int_equal(1, r->num_entries); + + sm_entry = &r->entries[0]; + assert_int_equal(IMD_ENTRY_MAGIC, sm_entry->magic); + assert_int_equal(0, sm_entry->start_offset); + assert_int_equal(SM_ROOT_SIZE, sm_entry->size); + assert_int_equal(CBMEM_ID_IMD_ROOT, sm_entry->id); + + free(base); +} + +/* Tests for imdr_recover. */ +static void test_imd_recover(void **state) +{ + int32_t offset_copy, max_offset_copy; + uint32_t rp_magic_copy, num_entries_copy; + uint32_t e_align_copy, e_magic_copy, e_id_copy; + uint32_t size_copy, diff; + void *base; + struct imd imd = {0}; + struct imd_root_pointer *rp; + struct imd_root *r; + struct imd_entry *lg_root_entry, *sm_root_entry, *ptr; + const struct imd_entry *lg_entry; + + /* Fail when the limit for lg was not set. */ + imd.lg.limit = (uintptr_t) NULL; + assert_int_equal(-1, imd_recover(&imd)); + + /* Set the limit for lg. */ + base = malloc(LIMIT_ALIGN); + if (base == NULL) + fail_msg("Cannot allocate enough memory - fail test"); + + imd_handle_init(&imd, (void *)(LIMIT_ALIGN + (uintptr_t)base)); + + /* Fail when the root pointer is not valid. */ + rp = (void *)imd.lg.limit - sizeof(struct imd_root_pointer); + assert_non_null(rp); + assert_int_equal(IMD_ROOT_PTR_MAGIC, rp->magic); + + rp_magic_copy = rp->magic; + rp->magic = 0; + assert_int_equal(-1, imd_recover(&imd)); + rp->magic = rp_magic_copy; + + /* Set the root pointer. */ + assert_int_equal(0, imd_create_tiered_empty(&imd, LG_ROOT_SIZE, LG_ENTRY_ALIGN, + SM_ROOT_SIZE, SM_ENTRY_ALIGN)); + assert_int_equal(2, ((struct imd_root *)imd.lg.r)->num_entries); + assert_int_equal(1, ((struct imd_root *)imd.sm.r)->num_entries); + + /* Fail if the number of entries exceeds the maximum number of entries. */ + r = imd.lg.r; + num_entries_copy = r->num_entries; + r->num_entries = r->max_entries + 1; + assert_int_equal(-1, imd_recover(&imd)); + r->num_entries = num_entries_copy; + + /* Fail if entry align is not a power of 2. */ + e_align_copy = r->entry_align; + r->entry_align++; + assert_int_equal(-1, imd_recover(&imd)); + r->entry_align = e_align_copy; + + /* Fail when an entry is not valid. */ + lg_root_entry = &r->entries[0]; + e_magic_copy = lg_root_entry->magic; + lg_root_entry->magic = 0; + assert_int_equal(-1, imd_recover(&imd)); + lg_root_entry->magic = e_magic_copy; + + /* Add new entries: large and small. */ + lg_entry = imd_entry_add(&imd, LG_ENTRY_ID, LG_ENTRY_SIZE); + assert_non_null(lg_entry); + assert_int_equal(3, r->num_entries); + + assert_non_null(imd_entry_add(&imd, SM_ENTRY_ID, SM_ENTRY_SIZE)); + assert_int_equal(2, ((struct imd_root *)imd.sm.r)->num_entries); + + /* Fail when start_addr is lower than low_limit. */ + r = imd.lg.r; + max_offset_copy = r->max_offset; + r->max_offset = lg_entry->start_offset + sizeof(int32_t); + assert_int_equal(-1, imd_recover(&imd)); + r->max_offset = max_offset_copy; + + /* Fail when start_addr is at least imdr->limit. */ + offset_copy = lg_entry->start_offset; + ptr = (struct imd_entry *)lg_entry; + ptr->start_offset = (void *)imd.lg.limit - (void *)r; + assert_int_equal(-1, imd_recover(&imd)); + ptr->start_offset = offset_copy; + + /* Fail when (start_addr + e->size) is higher than imdr->limit. */ + size_copy = lg_entry->size; + diff = (void *)imd.lg.limit - ((void *)r + lg_entry->start_offset); + ptr->size = diff + 1; + assert_int_equal(-1, imd_recover(&imd)); + ptr->size = size_copy; + + /* Succeed if small region is not present. */ + sm_root_entry = &r->entries[1]; + e_id_copy = sm_root_entry->id; + sm_root_entry->id = 0; + assert_int_equal(0, imd_recover(&imd)); + sm_root_entry->id = e_id_copy; + + assert_int_equal(0, imd_recover(&imd)); + + free(base); +} + +static void test_imd_limit_size(void **state) +{ + void *base; + struct imd imd = {0}; + size_t root_size, max_size; + + max_size = align_up_pow2(sizeof(struct imd_root_pointer) + + sizeof(struct imd_root) + 3 * sizeof(struct imd_entry)); + + assert_int_equal(-1, imd_limit_size(&imd, max_size)); + + base = malloc(LIMIT_ALIGN); + if (base == NULL) + fail_msg("Cannot allocate enough memory - fail test"); + imd_handle_init(&imd, (void *)(LIMIT_ALIGN + (uintptr_t)base)); + + root_size = align_up_pow2(sizeof(struct imd_root_pointer) + + sizeof(struct imd_root) + 2 * sizeof(struct imd_entry)); + imd.lg.r = (void *)imd.lg.limit - root_size; + + imd_create_empty(&imd, root_size, LG_ENTRY_ALIGN); + assert_int_equal(-1, imd_limit_size(&imd, root_size - 1)); + assert_int_equal(0, imd_limit_size(&imd, max_size)); + + /* Cannot create such a big entry */ + assert_null(imd_entry_add(&imd, LG_ENTRY_ID, max_size - root_size + 1)); + + free(base); +} + +static void test_imd_lockdown(void **state) +{ + struct imd imd = {0}; + struct imd_root *r_lg, *r_sm; + + assert_int_equal(-1, imd_lockdown(&imd)); + + imd.lg.r = malloc(sizeof(struct imd_root)); + if (imd.lg.r == NULL) + fail_msg("Cannot allocate enough memory - fail test"); + + r_lg = (struct imd_root *) (imd.lg.r); + + assert_int_equal(0, imd_lockdown(&imd)); + assert_true(r_lg->flags & IMD_FLAG_LOCKED); + + imd.sm.r = malloc(sizeof(struct imd_root)); + if (imd.sm.r == NULL) + fail_msg("Cannot allocate enough memory - fail test"); + r_sm = (struct imd_root *) (imd.sm.r); + + assert_int_equal(0, imd_lockdown(&imd)); + assert_true(r_sm->flags & IMD_FLAG_LOCKED); + + free(imd.lg.r); + free(imd.sm.r); +} + +static void test_imd_region_used(void **state) +{ + struct imd imd = {0}; + struct imd_entry *first_entry, *new_entry; + struct imd_root *r; + size_t size; + void *imd_base; + void *base; + + assert_int_equal(-1, imd_region_used(&imd, &base, &size)); + + imd_base = malloc(LIMIT_ALIGN); + if (imd_base == NULL) + fail_msg("Cannot allocate enough memory - fail test"); + imd_handle_init(&imd, (void *)(LIMIT_ALIGN + (uintptr_t)imd_base)); + + assert_int_equal(-1, imd_region_used(&imd, &base, &size)); + assert_int_equal(0, imd_create_empty(&imd, LG_ROOT_SIZE, LG_ENTRY_ALIGN)); + assert_int_equal(0, imd_region_used(&imd, &base, &size)); + + r = (struct imd_root *)imd.lg.r; + first_entry = &r->entries[r->num_entries - 1]; + + assert_int_equal(r + first_entry->start_offset, (uintptr_t)base); + assert_int_equal(first_entry->size, size); + + assert_non_null(imd_entry_add(&imd, LG_ENTRY_ID, LG_ENTRY_SIZE)); + assert_int_equal(2, r->num_entries); + + assert_int_equal(0, imd_region_used(&imd, &base, &size)); + + new_entry = &r->entries[r->num_entries - 1]; + + assert_true((void *)r + new_entry->start_offset == base); + assert_int_equal(first_entry->size + new_entry->size, size); + + free(imd_base); +} + +static void test_imd_entry_add(void **state) +{ + int i; + struct imd imd = {0}; + size_t entry_size = 0; + size_t used_size; + ssize_t entry_offset; + void *base; + struct imd_root *r, *sm_r, *lg_r; + struct imd_entry *first_entry, *new_entry; + uint32_t num_entries_copy; + int32_t max_offset_copy; + + /* No small region case. */ + assert_null(imd_entry_add(&imd, LG_ENTRY_ID, entry_size)); + + base = malloc(LIMIT_ALIGN); + if (base == NULL) + fail_msg("Cannot allocate enough memory - fail test"); + + imd_handle_init(&imd, (void *)(LIMIT_ALIGN + (uintptr_t)base)); + + assert_int_equal(0, imd_create_empty(&imd, LG_ROOT_SIZE, LG_ENTRY_ALIGN)); + + r = (struct imd_root *)imd.lg.r; + first_entry = &r->entries[r->num_entries - 1]; + + /* Cannot add an entry when root is locked. */ + r->flags = IMD_FLAG_LOCKED; + assert_null(imd_entry_add(&imd, LG_ENTRY_ID, entry_size)); + r->flags = 0; + + /* Fail when the maximum number of entries has been reached. */ + num_entries_copy = r->num_entries; + r->num_entries = r->max_entries; + assert_null(imd_entry_add(&imd, LG_ENTRY_ID, entry_size)); + r->num_entries = num_entries_copy; + + /* Fail when entry size is 0 */ + assert_null(imd_entry_add(&imd, LG_ENTRY_ID, 0)); + + /* Fail when entry size (after alignment) overflows imd total size. */ + entry_size = 2049; + max_offset_copy = r->max_offset; + r->max_offset = -entry_size; + assert_null(imd_entry_add(&imd, LG_ENTRY_ID, entry_size)); + r->max_offset = max_offset_copy; + + /* Finally succeed. */ + entry_size = 2 * sizeof(int32_t); + assert_non_null(imd_entry_add(&imd, LG_ENTRY_ID, entry_size)); + assert_int_equal(2, r->num_entries); + + new_entry = &r->entries[r->num_entries - 1]; + assert_int_equal(sizeof(struct imd_entry), (void *)new_entry - (void *)first_entry); + + assert_int_equal(IMD_ENTRY_MAGIC, new_entry->magic); + assert_int_equal(LG_ENTRY_ID, new_entry->id); + assert_int_equal(entry_size, new_entry->size); + + used_size = ALIGN_UP(entry_size, r->entry_align); + entry_offset = first_entry->start_offset - used_size; + assert_int_equal(entry_offset, new_entry->start_offset); + + /* Use small region case. */ + imd_create_tiered_empty(&imd, LG_ROOT_SIZE, LG_ENTRY_ALIGN, SM_ROOT_SIZE, + SM_ENTRY_ALIGN); + + lg_r = imd.lg.r; + sm_r = imd.sm.r; + + /* All five new entries should be added to small allocations */ + for (i = 0; i < 5; i++) { + assert_non_null(imd_entry_add(&imd, SM_ENTRY_ID, SM_ENTRY_SIZE)); + assert_int_equal(i+2, sm_r->num_entries); + assert_int_equal(2, lg_r->num_entries); + } + + /* But next should fall back on large region */ + assert_non_null(imd_entry_add(&imd, SM_ENTRY_ID, SM_ENTRY_SIZE)); + assert_int_equal(6, sm_r->num_entries); + assert_int_equal(3, lg_r->num_entries); + + /* + * Small allocation is created when occupies less than 1/4 of available + * small region. Verify this. + */ + imd_create_tiered_empty(&imd, LG_ROOT_SIZE, LG_ENTRY_ALIGN, SM_ROOT_SIZE, + SM_ENTRY_ALIGN); + + assert_non_null(imd_entry_add(&imd, SM_ENTRY_ID, -sm_r->max_offset / 4 + 1)); + assert_int_equal(1, sm_r->num_entries); + assert_int_equal(3, lg_r->num_entries); + + /* Next two should go into small region */ + assert_non_null(imd_entry_add(&imd, SM_ENTRY_ID, -sm_r->max_offset / 4)); + assert_int_equal(2, sm_r->num_entries); + assert_int_equal(3, lg_r->num_entries); + + /* (1/4 * 3/4) */ + assert_non_null(imd_entry_add(&imd, SM_ENTRY_ID, -sm_r->max_offset / 16 * 3)); + assert_int_equal(3, sm_r->num_entries); + assert_int_equal(3, lg_r->num_entries); + + free(base); +} + +static void test_imd_entry_find(void **state) +{ + struct imd imd = {0}; + void *base; + + base = malloc(LIMIT_ALIGN); + if (base == NULL) + fail_msg("Cannot allocate enough memory - fail test"); + imd_handle_init(&imd, (void *)(LIMIT_ALIGN + (uintptr_t)base)); + + assert_int_equal(0, imd_create_tiered_empty(&imd, LG_ROOT_SIZE, LG_ENTRY_ALIGN, + SM_ROOT_SIZE, SM_ENTRY_ALIGN)); + + assert_non_null(imd_entry_add(&imd, LG_ENTRY_ID, LG_ENTRY_SIZE)); + + assert_non_null(imd_entry_find(&imd, LG_ENTRY_ID)); + assert_non_null(imd_entry_find(&imd, SMALL_REGION_ID)); + + /* Try invalid id, should fail */ + assert_null(imd_entry_find(&imd, INVALID_REGION_ID)); + + free(base); +} + +static void test_imd_entry_find_or_add(void **state) +{ + struct imd imd = {0}; + const struct imd_entry *entry; + struct imd_root *r; + void *base; + + base = malloc(LIMIT_ALIGN); + if (base == NULL) + fail_msg("Cannot allocate enough memory - fail test"); + imd_handle_init(&imd, (void *)(LIMIT_ALIGN + (uintptr_t)base)); + + assert_null(imd_entry_find_or_add(&imd, LG_ENTRY_ID, LG_ENTRY_SIZE)); + + assert_int_equal(0, imd_create_empty(&imd, LG_ROOT_SIZE, LG_ENTRY_ALIGN)); + entry = imd_entry_find_or_add(&imd, LG_ENTRY_ID, LG_ENTRY_SIZE); + assert_non_null(entry); + + r = (struct imd_root *)imd.lg.r; + + assert_int_equal(entry->id, LG_ENTRY_ID); + assert_int_equal(2, r->num_entries); + assert_non_null(imd_entry_find_or_add(&imd, LG_ENTRY_ID, LG_ENTRY_SIZE)); + assert_int_equal(2, r->num_entries); + + free(base); +} + +static void test_imd_entry_size(void **state) +{ + struct imd_entry entry = { .size = LG_ENTRY_SIZE }; + + assert_int_equal(LG_ENTRY_SIZE, imd_entry_size(&entry)); + + entry.size = 0; + assert_int_equal(0, imd_entry_size(&entry)); +} + +static void test_imd_entry_at(void **state) +{ + struct imd imd = {0}; + struct imd_root *r; + struct imd_entry *e = NULL; + const struct imd_entry *entry; + void *base; + + base = malloc(LIMIT_ALIGN); + if (base == NULL) + fail_msg("Cannot allocate enough memory - fail test"); + imd_handle_init(&imd, (void *)(LIMIT_ALIGN + (uintptr_t)base)); + + assert_int_equal(0, imd_create_empty(&imd, LG_ROOT_SIZE, LG_ENTRY_ALIGN)); + + /* Fail when entry is NULL */ + assert_null(imd_entry_at(&imd, e)); + + entry = imd_entry_add(&imd, LG_ENTRY_ID, LG_ENTRY_SIZE); + assert_non_null(entry); + + r = (struct imd_root *)imd.lg.r; + assert_ptr_equal((void *)r + entry->start_offset, imd_entry_at(&imd, entry)); + + free(base); +} + +static void test_imd_entry_id(void **state) +{ + struct imd_entry entry = { .id = LG_ENTRY_ID }; + + assert_int_equal(LG_ENTRY_ID, imd_entry_id(&entry)); +} + +static void test_imd_entry_remove(void **state) +{ + void *base; + struct imd imd = {0}; + struct imd_root *r; + const struct imd_entry *fst_lg_entry, *snd_lg_entry, *fst_sm_entry; + const struct imd_entry *e = NULL; + + /* Uninitialized handle */ + assert_int_equal(-1, imd_entry_remove(&imd, e)); + + base = malloc(LIMIT_ALIGN); + if (base == NULL) + fail_msg("Cannot allocate enough memory - fail test"); + + imd_handle_init(&imd, (void *)(LIMIT_ALIGN + (uintptr_t)base)); + + assert_int_equal(0, imd_create_tiered_empty(&imd, LG_ROOT_SIZE, LG_ENTRY_ALIGN, + SM_ROOT_SIZE, SM_ENTRY_ALIGN)); + + r = imd.lg.r; + assert_int_equal(2, r->num_entries); + fst_lg_entry = &r->entries[0]; + snd_lg_entry = &r->entries[1]; + + /* Only last entry can be removed */ + assert_int_equal(-1, imd_entry_remove(&imd, fst_lg_entry)); + r->flags = IMD_FLAG_LOCKED; + assert_int_equal(-1, imd_entry_remove(&imd, snd_lg_entry)); + r->flags = 0; + + r = imd.sm.r; + assert_int_equal(1, r->num_entries); + fst_sm_entry = &r->entries[0]; + + /* Fail trying to remove root entry */ + assert_int_equal(-1, imd_entry_remove(&imd, fst_sm_entry)); + assert_int_equal(1, r->num_entries); + + r = imd.lg.r; + assert_int_equal(0, imd_entry_remove(&imd, snd_lg_entry)); + assert_int_equal(1, r->num_entries); + + /* Fail trying to remove root entry */ + assert_int_equal(-1, imd_entry_remove(&imd, fst_lg_entry)); + assert_int_equal(1, r->num_entries); + + free(base); +} + +static void test_imd_cursor_init(void **state) +{ + struct imd imd = {0}; + struct imd_cursor cursor; + + assert_int_equal(-1, imd_cursor_init(NULL, NULL)); + assert_int_equal(-1, imd_cursor_init(NULL, &cursor)); + assert_int_equal(-1, imd_cursor_init(&imd, NULL)); + assert_int_equal(0, imd_cursor_init(&imd, &cursor)); + + assert_ptr_equal(cursor.imdr[0], &imd.lg); + assert_ptr_equal(cursor.imdr[1], &imd.sm); +} + +static void test_imd_cursor_next(void **state) +{ + void *base; + struct imd imd = {0}; + struct imd_cursor cursor; + struct imd_root *r; + const struct imd_entry *entry; + struct imd_entry *fst_lg_entry, *snd_lg_entry, *fst_sm_entry; + assert_int_equal(0, imd_cursor_init(&imd, &cursor)); + + cursor.current_imdr = 3; + cursor.current_entry = 0; + assert_null(imd_cursor_next(&cursor)); + + cursor.current_imdr = 0; + assert_null(imd_cursor_next(&cursor)); + + base = malloc(LIMIT_ALIGN); + if (base == NULL) + fail_msg("Cannot allocate enough memory - fail test"); + imd_handle_init(&imd, (void *)(LIMIT_ALIGN + (uintptr_t)base)); + + assert_int_equal(0, imd_create_tiered_empty(&imd, LG_ROOT_SIZE, LG_ENTRY_ALIGN, + SM_ROOT_SIZE, SM_ENTRY_ALIGN)); + + r = imd.lg.r; + entry = imd_cursor_next(&cursor); + assert_non_null(entry); + + fst_lg_entry = &r->entries[0]; + assert_int_equal(fst_lg_entry->id, entry->id); + assert_ptr_equal(fst_lg_entry, entry); + + entry = imd_cursor_next(&cursor); + assert_non_null(entry); + + snd_lg_entry = &r->entries[1]; + assert_int_equal(snd_lg_entry->id, entry->id); + assert_ptr_equal(snd_lg_entry, entry); + + entry = imd_cursor_next(&cursor); + assert_non_null(entry); + + r = imd.sm.r; + fst_sm_entry = &r->entries[0]; + assert_int_equal(fst_sm_entry->id, entry->id); + assert_ptr_equal(fst_sm_entry, entry); + + entry = imd_cursor_next(&cursor); + assert_null(entry); +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_imd_handle_init), + cmocka_unit_test(test_imd_handle_init_partial_recovery), + cmocka_unit_test(test_imd_create_empty), + cmocka_unit_test(test_imd_create_tiered_empty), + cmocka_unit_test(test_imd_recover), + cmocka_unit_test(test_imd_limit_size), + cmocka_unit_test(test_imd_lockdown), + cmocka_unit_test(test_imd_region_used), + cmocka_unit_test(test_imd_entry_add), + cmocka_unit_test(test_imd_entry_find), + cmocka_unit_test(test_imd_entry_find_or_add), + cmocka_unit_test(test_imd_entry_size), + cmocka_unit_test(test_imd_entry_at), + cmocka_unit_test(test_imd_entry_id), + cmocka_unit_test(test_imd_entry_remove), + cmocka_unit_test(test_imd_cursor_init), + cmocka_unit_test(test_imd_cursor_next), + }; + + return cb_run_group_tests(tests, NULL, NULL); +} + diff --git a/tests/data/lib/lzma-test/data.3.lzma.bin b/tests/data/lib/lzma-test/data.3.lzma.bin new file mode 100644 index 000000000000..ca20babe8260 Binary files /dev/null and b/tests/data/lib/lzma-test/data.3.lzma.bin differ diff --git a/tests/data/lib/lzma-test/data.4.bin b/tests/data/lib/lzma-test/data.4.bin new file mode 100644 index 000000000000..892c2df4f888 Binary files /dev/null and b/tests/data/lib/lzma-test/data.4.bin differ diff --git a/tests/data/lib/lzma-test/data.4.lzma.bin b/tests/data/lib/lzma-test/data.4.lzma.bin new file mode 100644 index 000000000000..ca8ff3b19629 Binary files /dev/null and b/tests/data/lib/lzma-test/data.4.lzma.bin differ diff --git a/tests/lib/Makefile.inc b/tests/lib/Makefile.inc index f28cd52fa031..0aba0f551304 100644 --- a/tests/lib/Makefile.inc +++ b/tests/lib/Makefile.inc @@ -38,6 +38,7 @@ tests-y += cbfs-no-verification-no-sha512-test tests-y += cbfs-no-verification-has-sha512-test tests-y += cbfs-lookup-no-mcache-test tests-y += cbfs-lookup-has-mcache-test +tests-y += lzma-test string-test-srcs += tests/lib/string-test.c string-test-srcs += src/lib/string.c @@ -231,3 +232,8 @@ cbfs-lookup-no-mcache-test-config += CONFIG_ARCH_X86=0 \ $(call copy-test,cbfs-lookup-no-mcache-test,cbfs-lookup-has-mcache-test) cbfs-lookup-has-mcache-test-config += CONFIG_NO_CBFS_MCACHE=0 + +lzma-test-srcs += tests/lib/lzma-test.c +lzma-test-srcs += tests/stubs/console.c +lzma-test-srcs += src/lib/lzma.c +lzma-test-srcs += src/lib/lzmadecode.c diff --git a/tests/lib/lzma-test.c b/tests/lib/lzma-test.c new file mode 100644 index 000000000000..3918890e8e83 --- /dev/null +++ b/tests/lib/lzma-test.c @@ -0,0 +1,190 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +struct lzma_test_state { + char *raw_filename; + size_t raw_file_sz; + char *comp_filename; + size_t comp_file_sz; +}; + +static int get_file_size(const char *fname) +{ + struct stat st; + if (stat(fname, &st) == -1) + return -1; + return st.st_size; +} + +static int teardown_ulzman_file(void **state) +{ + struct lzma_test_state *s = *state; + + test_free(s->raw_filename); + test_free(s->comp_filename); + test_free(s); + + return 0; +} + +/* Set data file with prestate */ +static int setup_ulzman_file(void **state) +{ + int ret = 0; + const char *fname_base = *state; + const char path_prefix[] = __TEST_DATA_DIR__ "/lib/lzma-test/%s%s"; + const char raw_file_suffix[] = ".bin"; + const char comp_file_suffix[] = ".lzma.bin"; + struct lzma_test_state *s = test_malloc(sizeof(*s)); + memset(s, 0, sizeof(*s)); + + if (!s) + return 1; + + const size_t raw_filename_size = strlen(path_prefix) + strlen(fname_base) + + ARRAY_SIZE(raw_file_suffix); + s->raw_filename = test_malloc(raw_filename_size); + + const size_t comp_filename_size = strlen(path_prefix) + strlen(fname_base) + + ARRAY_SIZE(comp_file_suffix); + s->comp_filename = test_malloc(comp_filename_size); + + if (!s->raw_filename || !s->comp_filename) { + print_error("File path allocation error\n"); + ret = 2; + goto error; + } + + snprintf(s->raw_filename, raw_filename_size, path_prefix, fname_base, raw_file_suffix); + snprintf(s->comp_filename, comp_filename_size, path_prefix, fname_base, + comp_file_suffix); + + s->raw_file_sz = get_file_size(s->raw_filename); + s->comp_file_sz = get_file_size(s->comp_filename); + + if (s->raw_file_sz == -1) { + print_error("Unable to open file: %s\n", s->raw_filename); + ret = 3; + goto error; + } + + if (s->comp_file_sz == -1) { + print_error("Unable to open file: %s\n", s->comp_filename); + ret = 3; + goto error; + } + + *state = s; + return 0; +error: + teardown_ulzman_file((void **)&s); + return ret; +} + +static int read_file(const char *fname, uint8_t *buf, size_t sz) +{ + int f = open(fname, O_RDONLY); + int read_sz = 0; + + if (f == -1) + return -1; + + read_sz = read(f, buf, sz); + + close(f); + return read_sz; +} + +static void test_ulzman_correct_file(void **state) +{ + struct lzma_test_state *s = *state; + uint8_t *raw_buf = test_malloc(s->raw_file_sz); + uint8_t *decomp_buf = test_malloc(s->raw_file_sz); + uint8_t *comp_buf = test_malloc(s->comp_file_sz); + + assert_non_null(raw_buf); + assert_non_null(decomp_buf); + assert_non_null(comp_buf); + assert_int_equal(s->raw_file_sz, read_file(s->raw_filename, raw_buf, s->raw_file_sz)); + assert_int_equal(s->comp_file_sz, + read_file(s->comp_filename, comp_buf, s->comp_file_sz)); + + assert_int_equal(s->raw_file_sz, + ulzman(comp_buf, s->comp_file_sz, decomp_buf, s->raw_file_sz)); + assert_memory_equal(raw_buf, decomp_buf, s->raw_file_sz); + + test_free(raw_buf); + test_free(decomp_buf); + test_free(comp_buf); +} + +static void test_ulzman_input_too_small(void **state) +{ + uint8_t in_buf[32]; + uint8_t out_buf[32]; + + assert_int_equal(0, ulzman(in_buf, LZMA_PROPERTIES_SIZE, out_buf, sizeof(out_buf))); +} + +static void test_ulzman_zero_buffer(void **state) +{ + uint8_t in_buf[LZMA_PROPERTIES_SIZE + 1 * KiB]; + uint8_t out_buf[2 * KiB]; + + memset(in_buf, 0, sizeof(in_buf)); + memset(out_buf, 0, sizeof(out_buf)); + + assert_int_equal(0, ulzman(in_buf, sizeof(in_buf), out_buf, sizeof(out_buf))); +} + +#define ULZMAN_CORRECT_FILE_TEST(_file_prefix) \ +{ \ + .name = "test_ulzman_correct_file(" _file_prefix ")", \ + .test_func = test_ulzman_correct_file, \ + .setup_func = setup_ulzman_file, \ + .teardown_func = teardown_ulzman_file, \ + .initial_state = (_file_prefix) \ +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + /* "data.N" in macros below refers to files: + - __TEST_DATA_DIR__ /lib/lzma-test/data.N.bin + - __TEST_DATA_DIR__ /lib/lzma-test/data.N.bin.lzma + Files data.N.bin suffix are raw data, and data.N.lzma.bin are its + LZMA-compressed form. Both are required to exist. + */ + + /* util/cbfs-compression-tool compressed by itself. + To test compression of executable files like payloads. */ + ULZMAN_CORRECT_FILE_TEST("data.1"), + + /* README.md compressed by util/cbfs-compression-tool. */ + ULZMAN_CORRECT_FILE_TEST("data.2"), + + /* tests/lib/imd-test.c compressed by util/cbfs-compression-tool + Structured text file. */ + ULZMAN_CORRECT_FILE_TEST("data.3"), + + /* libcmocka.so.0.7.0 compressed by util/cbfs-compression-tool + Another binary file, shared object. */ + ULZMAN_CORRECT_FILE_TEST("data.4"), + + cmocka_unit_test(test_ulzman_input_too_small), + + cmocka_unit_test(test_ulzman_zero_buffer), + }; + + return cb_run_group_tests(tests, NULL, NULL); +} -- cgit v1.2.3