diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/ch341a_spi.c | 136 | ||||
-rw-r--r-- | tests/chip.c | 615 | ||||
-rw-r--r-- | tests/chip_wp.c | 394 | ||||
-rw-r--r-- | tests/dediprog.c | 58 | ||||
-rw-r--r-- | tests/dummyflasher.c | 153 | ||||
-rw-r--r-- | tests/flashrom.c | 71 | ||||
-rw-r--r-- | tests/helpers.c | 79 | ||||
-rw-r--r-- | tests/include/test.h | 45 | ||||
-rw-r--r-- | tests/io_mock.c | 31 | ||||
-rw-r--r-- | tests/io_mock.h | 130 | ||||
-rw-r--r-- | tests/io_real.c | 78 | ||||
-rw-r--r-- | tests/io_real.h | 24 | ||||
-rw-r--r-- | tests/layout.c | 209 | ||||
-rw-r--r-- | tests/libusb_wraps.c | 222 | ||||
-rw-r--r-- | tests/libusb_wraps.h | 57 | ||||
-rw-r--r-- | tests/lifecycle.c | 98 | ||||
-rw-r--r-- | tests/lifecycle.h | 39 | ||||
-rw-r--r-- | tests/linux_mtd.c | 94 | ||||
-rw-r--r-- | tests/linux_spi.c | 72 | ||||
-rw-r--r-- | tests/mediatek_i2c_spi.c | 52 | ||||
-rw-r--r-- | tests/meson.build | 134 | ||||
-rw-r--r-- | tests/nicrealtek.c | 33 | ||||
-rw-r--r-- | tests/parade_lspcon.c | 138 | ||||
-rw-r--r-- | tests/raiden_debug_spi.c | 118 | ||||
-rw-r--r-- | tests/realtek_mst_i2c_spi.c | 79 | ||||
-rw-r--r-- | tests/selfcheck.c | 156 | ||||
-rw-r--r-- | tests/spi25.c | 282 | ||||
-rw-r--r-- | tests/tests.c | 515 | ||||
-rw-r--r-- | tests/tests.h | 103 | ||||
-rw-r--r-- | tests/unittest_env.h | 48 | ||||
-rw-r--r-- | tests/usb_unittests.h | 62 | ||||
-rw-r--r-- | tests/wraps.h | 79 |
32 files changed, 4404 insertions, 0 deletions
diff --git a/tests/ch341a_spi.c b/tests/ch341a_spi.c new file mode 100644 index 00000000..1cb26524 --- /dev/null +++ b/tests/ch341a_spi.c @@ -0,0 +1,136 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2022 Alexander Goncharov <chat@joursoir.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "lifecycle.h" + +#if CONFIG_CH341A_SPI == 1 + +/* Same macro as in ch341a_spi.c programmer. */ +#define WRITE_EP 0x02 +#define READ_EP 0x82 + +struct ch341a_spi_io_state { + struct libusb_transfer *transfer_out; + /* + * Since the test transfers a data that fits in one CH341 packet, we + * don't need an array of these transfers (as is done in the driver code). + */ + struct libusb_transfer *transfer_in; +}; + +static struct libusb_transfer *ch341a_libusb_alloc_transfer(void *state, int iso_packets) +{ + return calloc(1, sizeof(struct libusb_transfer)); +} + +/* + * The libusb code stores submitted transfers in their own context. But this + * function doesn't require a context pointer because libusb stores context + * pointers in libusb_transfer instances. Since our ch341 driver is using + * the default context, we store the transfer in our own. + */ +static int ch341a_libusb_submit_transfer(void *state, struct libusb_transfer *transfer) +{ + struct ch341a_spi_io_state *io_state = state; + + assert_true(transfer->endpoint == WRITE_EP || transfer->endpoint == READ_EP); + + if (transfer->endpoint == WRITE_EP) { + assert_null(io_state->transfer_out); + io_state->transfer_out = transfer; + } else if (transfer->endpoint == READ_EP) { + assert_null(io_state->transfer_in); + io_state->transfer_in = transfer; + } + + return 0; +} + +static void ch341a_libusb_free_transfer(void *state, struct libusb_transfer *transfer) +{ + free(transfer); +} + +/* + * Handle submitted transfers by pretending that a transfer is completed and + * invoking its callback (that is the flashrom code). + */ +static int ch341a_libusb_handle_events_timeout(void *state, libusb_context *ctx, struct timeval *tv) +{ + struct ch341a_spi_io_state *io_state = state; + + if (io_state->transfer_out) { + io_state->transfer_out->status = LIBUSB_TRANSFER_COMPLETED; + io_state->transfer_out->actual_length = io_state->transfer_out->length; + io_state->transfer_out->callback(io_state->transfer_out); + io_state->transfer_out = NULL; + } + + if (io_state->transfer_in) { + io_state->transfer_in->buffer[1] = reverse_byte(0xEF); /* WINBOND_NEX_ID */ + io_state->transfer_in->buffer[2] = reverse_byte(0x40); /* WINBOND_NEX_W25Q128_V left byte */ + io_state->transfer_in->buffer[3] = reverse_byte(0x18); /* WINBOND_NEX_W25Q128_V right byte */ + + io_state->transfer_in->status = LIBUSB_TRANSFER_COMPLETED; + io_state->transfer_in->actual_length = io_state->transfer_in->length; + io_state->transfer_in->callback(io_state->transfer_in); + io_state->transfer_in = NULL; + } + + return 0; +} + +void ch341a_spi_basic_lifecycle_test_success(void **state) +{ + struct ch341a_spi_io_state ch341a_spi_io_state = { 0 }; + struct io_mock_fallback_open_state ch341a_spi_fallback_open_state = { + .noc = 0, + .paths = { NULL }, + }; + const struct io_mock ch341a_spi_io = { + .state = &ch341a_spi_io_state, + .libusb_alloc_transfer = &ch341a_libusb_alloc_transfer, + .libusb_submit_transfer = &ch341a_libusb_submit_transfer, + .libusb_free_transfer = &ch341a_libusb_free_transfer, + .libusb_handle_events_timeout = &ch341a_libusb_handle_events_timeout, + .fallback_open_state = &ch341a_spi_fallback_open_state, + }; + + run_basic_lifecycle(state, &ch341a_spi_io, &programmer_ch341a_spi, ""); +} + +void ch341a_spi_probe_lifecycle_test_success(void **state) +{ + struct ch341a_spi_io_state ch341a_spi_io_state = { 0 }; + struct io_mock_fallback_open_state ch341a_spi_fallback_open_state = { + .noc = 0, + .paths = { NULL }, + }; + const struct io_mock ch341a_spi_io = { + .state = &ch341a_spi_io_state, + .libusb_alloc_transfer = &ch341a_libusb_alloc_transfer, + .libusb_submit_transfer = &ch341a_libusb_submit_transfer, + .libusb_free_transfer = &ch341a_libusb_free_transfer, + .libusb_handle_events_timeout = &ch341a_libusb_handle_events_timeout, + .fallback_open_state = &ch341a_spi_fallback_open_state, + }; + + run_probe_lifecycle(state, &ch341a_spi_io, &programmer_ch341a_spi, "", "W25Q128.V"); +} + +#else + SKIP_TEST(ch341a_spi_basic_lifecycle_test_success) + SKIP_TEST(ch341a_spi_probe_lifecycle_test_success) +#endif /* CONFIG_CH341A_SPI */ diff --git a/tests/chip.c b/tests/chip.c new file mode 100644 index 00000000..6a17d862 --- /dev/null +++ b/tests/chip.c @@ -0,0 +1,615 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2021 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This file contains tests for operations on flash chip. + * + * Two flash chip test variants are used: + * + * 1) Mock chip state backed by `g_chip_state`. + * Example of test: erase_chip_test_success. + * + * 2) Mock chip operations backed by `dummyflasher` emulation. + * Dummyflasher controls chip state and emulates read/write/unlock/erase. + * `g_chip_state` is NOT used for this type of tests. + * Example of test: erase_chip_with_dummyflasher_test_success. + */ + +#include <include/test.h> +#include <stdio.h> +#include <string.h> + +#include "tests.h" +#include "chipdrivers.h" +#include "flash.h" +#include "io_mock.h" +#include "libflashrom.h" +#include "programmer.h" + +#define MOCK_CHIP_SIZE (8*MiB) +#define MOCK_CHIP_CONTENT 0xCC /* 0x00 is a zeroed heap and 0xFF is an erased chip. */ + +static struct { + unsigned int unlock_calls; /* how many times unlock function was called */ + uint8_t buf[MOCK_CHIP_SIZE]; /* buffer of total size of chip, to emulate a chip */ +} g_chip_state = { + .unlock_calls = 0, + .buf = { 0 }, +}; + +static int read_chip(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len) +{ + printf("Read chip called with start=0x%x, len=0x%x\n", start, len); + if (!g_chip_state.unlock_calls) { + printf("Error while reading chip: unlock was not called.\n"); + return 1; + } + + assert_in_range(start + len, 0, MOCK_CHIP_SIZE); + + memcpy(buf, &g_chip_state.buf[start], len); + return 0; +} + +static int write_chip(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len) +{ + printf("Write chip called with start=0x%x, len=0x%x\n", start, len); + if (!g_chip_state.unlock_calls) { + printf("Error while writing chip: unlock was not called.\n"); + return 1; + } + + assert_in_range(start + len, 0, MOCK_CHIP_SIZE); + + memcpy(&g_chip_state.buf[start], buf, len); + return 0; +} + +static int unlock_chip(struct flashctx *flash) +{ + printf("Unlock chip called\n"); + g_chip_state.unlock_calls++; + + if (g_chip_state.unlock_calls > 1) { + printf("Error: Unlock called twice\n"); + return -1; + } + + return 0; +} + +static int block_erase_chip(struct flashctx *flash, unsigned int blockaddr, unsigned int blocklen) +{ + printf("Block erase called with blockaddr=0x%x, blocklen=0x%x\n", blockaddr, blocklen); + if (!g_chip_state.unlock_calls) { + printf("Error while erasing chip: unlock was not called.\n"); + return 1; + } + + assert_in_range(blockaddr + blocklen, 0, MOCK_CHIP_SIZE); + + memset(&g_chip_state.buf[blockaddr], 0xff, blocklen); + return 0; +} + +static void setup_chip(struct flashrom_flashctx *flashctx, struct flashrom_layout **layout, + struct flashchip *chip, const char *programmer_param, const struct io_mock *io) +{ + io_mock_register(io); + + flashctx->chip = chip; + + g_chip_state.unlock_calls = 0; + memset(g_chip_state.buf, MOCK_CHIP_CONTENT, sizeof(g_chip_state.buf)); + + printf("Creating layout with one included region... "); + assert_int_equal(0, flashrom_layout_new(layout)); + /* One region which covers total size of chip. */ + assert_int_equal(0, flashrom_layout_add_region(*layout, 0, chip->total_size * KiB - 1, "region")); + assert_int_equal(0, flashrom_layout_include_region(*layout, "region")); + + flashrom_layout_set(flashctx, *layout); + printf("done\n"); + + /* + * We need some programmer (any), and dummy is a very good one, + * because it doesn't need any mocking. So no extra complexity + * from a programmer side, and test can focus on working with the chip. + */ + printf("Dummyflasher initialising with param=\"%s\"... ", programmer_param); + assert_int_equal(0, programmer_init(&programmer_dummy, programmer_param)); + /* Assignment below normally happens while probing, but this test is not probing. */ + flashctx->mst = ®istered_masters[0]; + printf("done\n"); +} + +static void teardown(struct flashrom_layout **layout) +{ + printf("Dummyflasher shutdown... "); + assert_int_equal(0, programmer_shutdown()); + printf("done\n"); + + printf("Releasing layout... "); + flashrom_layout_release(*layout); + printf("done\n"); + + io_mock_register(NULL); +} + +extern write_func_t *g_test_write_injector; +extern read_func_t *g_test_read_injector; +extern erasefunc_t *g_test_erase_injector; +extern blockprotect_func_t *g_test_unlock_injector; + +static const struct flashchip chip_8MiB = { + .vendor = "aklm", + .total_size = MOCK_CHIP_SIZE / KiB, + .tested = TEST_OK_PREW, + .read = TEST_READ_INJECTOR, + .write = TEST_WRITE_INJECTOR, + .unlock = TEST_UNLOCK_INJECTOR, + .block_erasers = + {{ + /* All blocks within total size of the chip. */ + .eraseblocks = { {2 * MiB, 4} }, + .block_erase = TEST_ERASE_INJECTOR, + }}, +}; + +/* Setup the struct for W25Q128.V, all values come from flashchips.c */ +static const struct flashchip chip_W25Q128_V = { + .vendor = "aklm&dummyflasher", + .total_size = 16 * 1024, + .tested = TEST_OK_PREW, + .read = SPI_CHIP_READ, + .write = SPI_CHIP_WRITE256, + .unlock = SPI_DISABLE_BLOCKPROTECT, + .page_size = 256, + .block_erasers = + { + { + .eraseblocks = { {4 * 1024, 4096} }, + .block_erase = SPI_BLOCK_ERASE_20, + }, { + .eraseblocks = { {32 * 1024, 512} }, + .block_erase = SPI_BLOCK_ERASE_52, + }, { + .eraseblocks = { {64 * 1024, 256} }, + .block_erase = SPI_BLOCK_ERASE_D8, + }, { + .eraseblocks = { {16 * 1024 * 1024, 1} }, + .block_erase = SPI_BLOCK_ERASE_60, + }, { + .eraseblocks = { {16 * 1024 * 1024, 1} }, + .block_erase = SPI_BLOCK_ERASE_C7, + } + }, +}; + +void erase_chip_test_success(void **state) +{ + (void) state; /* unused */ + + static struct io_mock_fallback_open_state data = { + .noc = 0, + .paths = { NULL }, + }; + const struct io_mock chip_io = { + .fallback_open_state = &data, + }; + + g_test_write_injector = write_chip; + g_test_read_injector = read_chip; + g_test_erase_injector = block_erase_chip; + g_test_unlock_injector = unlock_chip; + struct flashrom_flashctx flashctx = { 0 }; + struct flashrom_layout *layout; + struct flashchip mock_chip = chip_8MiB; + const char *param = ""; /* Default values for all params. */ + + setup_chip(&flashctx, &layout, &mock_chip, param, &chip_io); + + printf("Erase chip operation started.\n"); + assert_int_equal(0, flashrom_flash_erase(&flashctx)); + printf("Erase chip operation done.\n"); + + teardown(&layout); +} + +void erase_chip_with_dummyflasher_test_success(void **state) +{ + (void) state; /* unused */ + + static struct io_mock_fallback_open_state data = { + .noc = 0, + .paths = { NULL }, + }; + const struct io_mock chip_io = { + .fallback_open_state = &data, + }; + + struct flashrom_flashctx flashctx = { 0 }; + struct flashrom_layout *layout; + struct flashchip mock_chip = chip_W25Q128_V; + /* + * Dummyflasher is capable to emulate W25Q128.V, so we ask it to do this. + * Nothing to mock, dummy is taking care of this already. + */ + const char *param_dup = "bus=spi,emulate=W25Q128FV"; + + setup_chip(&flashctx, &layout, &mock_chip, param_dup, &chip_io); + + printf("Erase chip operation started.\n"); + assert_int_equal(0, flashrom_flash_erase(&flashctx)); + printf("Erase chip operation done.\n"); + + teardown(&layout); +} + +void read_chip_test_success(void **state) +{ + (void) state; /* unused */ + + static struct io_mock_fallback_open_state data = { + .noc = 0, + .paths = { NULL }, + }; + const struct io_mock chip_io = { + .fallback_open_state = &data, + }; + + g_test_write_injector = write_chip; + g_test_read_injector = read_chip; + g_test_erase_injector = block_erase_chip; + g_test_unlock_injector = unlock_chip; + struct flashrom_flashctx flashctx = { 0 }; + struct flashrom_layout *layout; + struct flashchip mock_chip = chip_8MiB; + const char *param = ""; /* Default values for all params. */ + + setup_chip(&flashctx, &layout, &mock_chip, param, &chip_io); + + const char *const filename = "read_chip.test"; + unsigned long size = mock_chip.total_size * 1024; + unsigned char *buf = calloc(size, sizeof(unsigned char)); + assert_non_null(buf); + + printf("Read chip operation started.\n"); + assert_int_equal(0, flashrom_image_read(&flashctx, buf, size)); + assert_int_equal(0, write_buf_to_file(buf, size, filename)); + printf("Read chip operation done.\n"); + + teardown(&layout); + + free(buf); +} + +void read_chip_with_dummyflasher_test_success(void **state) +{ + (void) state; /* unused */ + + static struct io_mock_fallback_open_state data = { + .noc = 0, + .paths = { NULL }, + }; + const struct io_mock chip_io = { + .fallback_open_state = &data, + }; + + struct flashrom_flashctx flashctx = { 0 }; + struct flashrom_layout *layout; + struct flashchip mock_chip = chip_W25Q128_V; + /* + * Dummyflasher is capable to emulate W25Q128.V, so we ask it to do this. + * Nothing to mock, dummy is taking care of this already. + */ + const char *param_dup = "bus=spi,emulate=W25Q128FV"; + + setup_chip(&flashctx, &layout, &mock_chip, param_dup, &chip_io); + + const char *const filename = "read_chip.test"; + unsigned long size = mock_chip.total_size * 1024; + unsigned char *buf = calloc(size, sizeof(unsigned char)); + assert_non_null(buf); + + printf("Read chip operation started.\n"); + assert_int_equal(0, flashrom_image_read(&flashctx, buf, size)); + assert_int_equal(0, write_buf_to_file(buf, size, filename)); + printf("Read chip operation done.\n"); + + teardown(&layout); + + free(buf); +} + +void write_chip_test_success(void **state) +{ + (void) state; /* unused */ + + static struct io_mock_fallback_open_state data = { + .noc = 0, + .paths = { NULL }, + }; + const struct io_mock chip_io = { + .fallback_open_state = &data, + }; + + g_test_write_injector = write_chip; + g_test_read_injector = read_chip; + g_test_erase_injector = block_erase_chip; + g_test_unlock_injector = unlock_chip; + struct flashrom_flashctx flashctx = { 0 }; + struct flashrom_layout *layout; + struct flashchip mock_chip = chip_8MiB; + const char *param = ""; /* Default values for all params. */ + + setup_chip(&flashctx, &layout, &mock_chip, param, &chip_io); + + /* + * Providing filename "-" means content is taken from standard input. + * This doesn't change much because all file operations are mocked. + * However filename "-" makes a difference for + * flashrom.c#read_buf_from_file and allows to avoid mocking + * image_stat.st_size. + * + * Now this does mean test covers successful path only, but this test + * is designed to cover only successful write operation anyway. + * + * To cover error path of image_stat.st_size != flash size, filename + * needs to be provided and image_stat.st_size needs to be mocked. + */ + const char *const filename = "-"; + unsigned long size = mock_chip.total_size * 1024; + uint8_t *const newcontents = malloc(size); + assert_non_null(newcontents); + + printf("Write chip operation started.\n"); + assert_int_equal(0, read_buf_from_file(newcontents, size, filename)); + assert_int_equal(0, flashrom_image_write(&flashctx, newcontents, size, NULL)); + printf("Write chip operation done.\n"); + + teardown(&layout); + + free(newcontents); +} + +void write_chip_with_dummyflasher_test_success(void **state) +{ + (void) state; /* unused */ + + static struct io_mock_fallback_open_state data = { + .noc = 0, + .paths = { NULL }, + }; + const struct io_mock chip_io = { + .fallback_open_state = &data, + }; + + struct flashrom_flashctx flashctx = { 0 }; + struct flashrom_layout *layout; + struct flashchip mock_chip = chip_W25Q128_V; + /* + * Dummyflasher is capable to emulate W25Q128.V, so we ask it to do this. + * Nothing to mock, dummy is taking care of this already. + */ + const char *param_dup = "bus=spi,emulate=W25Q128FV"; + + setup_chip(&flashctx, &layout, &mock_chip, param_dup, &chip_io); + + /* See comment in write_chip_test_success */ + const char *const filename = "-"; + unsigned long size = mock_chip.total_size * 1024; + uint8_t *const newcontents = malloc(size); + assert_non_null(newcontents); + + printf("Write chip operation started.\n"); + assert_int_equal(0, read_buf_from_file(newcontents, size, filename)); + assert_int_equal(0, flashrom_image_write(&flashctx, newcontents, size, NULL)); + printf("Write chip operation done.\n"); + + teardown(&layout); + + free(newcontents); +} + +void write_nonaligned_region_with_dummyflasher_test_success(void **state) +{ + (void) state; /* unused */ + + static struct io_mock_fallback_open_state data = { + .noc = 0, + .paths = { NULL }, + }; + const struct io_mock chip_io = { + .fallback_open_state = &data, + }; + + struct flashrom_flashctx flashctx = { 0 }; + struct flashrom_layout *layout; + struct flashchip mock_chip = chip_W25Q128_V; + const uint32_t mock_chip_size = mock_chip.total_size * KiB; + /* + * Dummyflasher is capable to emulate W25Q128.V, so we ask it to do this. + * Nothing to mock, dummy is taking care of this already. + */ + const char *param_dup = "bus=spi,emulate=W25Q128FV"; + + /* FIXME: MOCK_CHIP_CONTENT is buggy within setup_chip, it should also + * not be either 0x00 or 0xFF as those are specific values related to + * either a erased chip or zero'ed heap thus ambigous. + */ +#define MOCK_CHIP_SUBREGION_CONTENTS 0xCC + /** + * Step 0 - Prepare newcontents as contiguous sample data bytes as follows: + * {MOCK_CHIP_SUBREGION_CONTENTS, [..]}. + */ + uint8_t *newcontents = calloc(1, mock_chip_size); + assert_non_null(newcontents); + memset(newcontents, MOCK_CHIP_SUBREGION_CONTENTS, mock_chip_size); + + setup_chip(&flashctx, &layout, &mock_chip, param_dup, &chip_io); + /* Expect to verify only the non-aligned write operation within the region. */ + flashrom_flag_set(&flashctx, FLASHROM_FLAG_VERIFY_AFTER_WRITE, true); + flashrom_flag_set(&flashctx, FLASHROM_FLAG_VERIFY_WHOLE_CHIP, false); + + /** + * Prepare mock chip content and release setup_chip() layout for our + * custom ones. + */ + assert_int_equal(0, flashrom_image_write(&flashctx, newcontents, mock_chip_size, NULL)); + flashrom_layout_release(layout); + + /** + * Create region smaller than erase granularity of chip. + */ + printf("Creating custom region layout... "); + assert_int_equal(0, flashrom_layout_new(&layout)); + printf("Adding and including region0... "); + assert_int_equal(0, flashrom_layout_add_region(layout, 0, (1 * KiB), "region0")); + assert_int_equal(0, flashrom_layout_include_region(layout, "region0")); + flashrom_layout_set(&flashctx, layout); + printf("Subregion layout configuration done.\n"); + + /** + * Step 1 - Modify newcontents as non-contiguous sample data bytes as follows: + * 0xAA 0xAA {MOCK_CHIP_SUBREGION_CONTENTS}, [..]}. + */ + printf("Subregion chip write op..\n"); + memset(newcontents, 0xAA, 2); + assert_int_equal(0, flashrom_image_write(&flashctx, newcontents, mock_chip_size, NULL)); + printf("Subregion chip write op done.\n"); + + /** + * FIXME: A 'NULL' layout should indicate a default layout however this + * causes a crash for a unknown reason. For now prepare a new default + * layout of the entire chip. flashrom_layout_set(&flashctx, NULL); // use default layout. + */ + flashrom_layout_release(layout); + assert_int_equal(0, flashrom_layout_new(&layout)); + assert_int_equal(0, flashrom_layout_add_region(layout, 0, mock_chip_size - 1, "entire")); + assert_int_equal(0, flashrom_layout_include_region(layout, "entire")); + flashrom_layout_set(&flashctx, layout); + + /** + * Expect a verification pass that the previous content within the region, however + * outside the region write length, is untouched. + */ + printf("Entire chip verify op..\n"); + assert_int_equal(0, flashrom_image_verify(&flashctx, newcontents, mock_chip_size)); + printf("Entire chip verify op done.\n"); + + teardown(&layout); + free(newcontents); +} + +static size_t verify_chip_fread(void *state, void *buf, size_t size, size_t len, FILE *fp) +{ + /* + * Verify operation compares contents of the file vs contents on the chip. + * To emulate successful verification we emulate file contents to be the + * same as what is on the chip. + */ + memset(buf, MOCK_CHIP_CONTENT, len); + return len; +} + +void verify_chip_test_success(void **state) +{ + (void) state; /* unused */ + + static struct io_mock_fallback_open_state data = { + .noc = 0, + .paths = { NULL }, + }; + const struct io_mock verify_chip_io = { + .iom_fread = verify_chip_fread, + .fallback_open_state = &data, + }; + + g_test_write_injector = write_chip; + g_test_read_injector = read_chip; + g_test_erase_injector = block_erase_chip; + g_test_unlock_injector = unlock_chip; + struct flashrom_flashctx flashctx = { 0 }; + struct flashrom_layout *layout; + struct flashchip mock_chip = chip_8MiB; + const char *param = ""; /* Default values for all params. */ + + setup_chip(&flashctx, &layout, &mock_chip, param, &verify_chip_io); + + /* See comment in write_chip_test_success */ + const char *const filename = "-"; + unsigned long size = mock_chip.total_size * 1024; + uint8_t *const newcontents = malloc(size); + assert_non_null(newcontents); + + printf("Verify chip operation started.\n"); + assert_int_equal(0, read_buf_from_file(newcontents, size, filename)); + assert_int_equal(0, flashrom_image_verify(&flashctx, newcontents, size)); + printf("Verify chip operation done.\n"); + + teardown(&layout); + + free(newcontents); +} + +void verify_chip_with_dummyflasher_test_success(void **state) +{ + (void) state; /* unused */ + + static struct io_mock_fallback_open_state data = { + .noc = 0, + .paths = { NULL }, + }; + const struct io_mock verify_chip_io = { + .iom_fread = verify_chip_fread, + .fallback_open_state = &data, + }; + + struct flashrom_flashctx flashctx = { 0 }; + struct flashrom_layout *layout; + struct flashchip mock_chip = chip_W25Q128_V; + /* + * Dummyflasher is capable to emulate W25Q128.V, so we ask it to do this. + * Nothing to mock, dummy is taking care of this already. + */ + const char *param_dup = "bus=spi,emulate=W25Q128FV"; + + setup_chip(&flashctx, &layout, &mock_chip, param_dup, &verify_chip_io); + + /* See comment in write_chip_test_success */ + const char *const filename = "-"; + unsigned long size = mock_chip.total_size * 1024; + uint8_t *const newcontents = malloc(size); + assert_non_null(newcontents); + + /* + * Dummyflasher controls chip state and fully emulates reads and writes, + * so to set up initial chip state we need to write on chip. Write + * operation takes content from file and writes on chip. File content is + * emulated in verify_chip_fread mock. + */ + + printf("Write chip operation started.\n"); + assert_int_equal(0, read_buf_from_file(newcontents, size, filename)); + assert_int_equal(0, flashrom_image_write(&flashctx, newcontents, size, NULL)); + printf("Write chip operation done.\n"); + + printf("Verify chip operation started.\n"); + assert_int_equal(0, flashrom_image_verify(&flashctx, newcontents, size)); + printf("Verify chip operation done.\n"); + + teardown(&layout); + + free(newcontents); +} diff --git a/tests/chip_wp.c b/tests/chip_wp.c new file mode 100644 index 00000000..2686f446 --- /dev/null +++ b/tests/chip_wp.c @@ -0,0 +1,394 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2021 3mdeb Embedded Systems Consulting + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <include/test.h> +#include <stdio.h> +#include <string.h> + +#include "chipdrivers.h" +#include "flash.h" +#include "libflashrom.h" +#include "programmer.h" +#include "tests.h" + +/* + * Tests in this file do not use any mocking, because using write-protect + * emulation in dummyflasher programmer is sufficient + */ + +#define LAYOUT_TAIL_REGION_START 0x1000 + +static void setup_chip(struct flashrom_flashctx *flash, struct flashrom_layout **layout, + struct flashchip *chip, const char *programmer_param) +{ + flash->chip = chip; + + if (layout) { + const size_t tail_start = LAYOUT_TAIL_REGION_START; + const size_t tail_len = chip->total_size * KiB - 1; + + assert_int_equal(0, flashrom_layout_new(layout)); + assert_int_equal(0, flashrom_layout_add_region(*layout, 0, tail_start - 1, "head")); + assert_int_equal(0, flashrom_layout_add_region(*layout, tail_start, tail_len, "tail")); + + flashrom_layout_set(flash, *layout); + } + + assert_int_equal(0, programmer_init(&programmer_dummy, programmer_param)); + /* Assignment below normally happens while probing, but this test is not probing. */ + flash->mst = ®istered_masters[0]; +} + +static void teardown(struct flashrom_layout **layout) +{ + assert_int_equal(0, programmer_shutdown()); + if (layout) + flashrom_layout_release(*layout); +} + +/* Setup the struct for W25Q128.V, all values come from flashchips.c */ +static const struct flashchip chip_W25Q128_V = { + .vendor = "aklm&dummyflasher", + .total_size = 16 * 1024, + .page_size = 1024, + .tested = TEST_OK_PREW, + .read = SPI_CHIP_READ, + .write = SPI_CHIP_WRITE256, + .unlock = SPI_DISABLE_BLOCKPROTECT, + .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_WRSR_EXT2 | FEATURE_WRSR2 | FEATURE_WRSR3, + .block_erasers = + { + { + .eraseblocks = { {4 * 1024, 4096} }, + .block_erase = SPI_BLOCK_ERASE_20, + }, { + .eraseblocks = { {32 * 1024, 512} }, + .block_erase = SPI_BLOCK_ERASE_52, + }, { + .eraseblocks = { {64 * 1024, 256} }, + .block_erase = SPI_BLOCK_ERASE_D8, + }, { + .eraseblocks = { {16 * 1024 * 1024, 1} }, + .block_erase = SPI_BLOCK_ERASE_60, + }, { + .eraseblocks = { {16 * 1024 * 1024, 1} }, + .block_erase = SPI_BLOCK_ERASE_C7, + } + }, + .reg_bits = + { + .srp = {STATUS1, 7, RW}, + .srl = {STATUS2, 0, RW}, + .bp = {{STATUS1, 2, RW}, {STATUS1, 3, RW}, {STATUS1, 4, RW}}, + .tb = {STATUS1, 5, RW}, + .sec = {STATUS1, 6, RW}, + .cmp = {STATUS2, 6, RW}, + .wps = {STATUS3, 2, RW}, + }, + .decode_range = DECODE_RANGE_SPI25, +}; + +/* Trying to set an unsupported WP range fails */ +void invalid_wp_range_dummyflasher_test_success(void **state) +{ + (void) state; /* unused */ + + const char *param_dup = "bus=spi,emulate=W25Q128FV,hwwp=no"; + + struct flashrom_flashctx flash = { 0 }; + struct flashchip mock_chip = chip_W25Q128_V; + struct flashrom_wp_cfg *wp_cfg; + + setup_chip(&flash, NULL, &mock_chip, param_dup); + + assert_int_equal(0, flashrom_wp_cfg_new(&wp_cfg)); + flashrom_wp_set_mode(wp_cfg, FLASHROM_WP_MODE_HARDWARE); + flashrom_wp_set_range(wp_cfg, 0x1000, 0x1000); + + assert_int_equal(FLASHROM_WP_ERR_RANGE_UNSUPPORTED, flashrom_wp_write_cfg(&flash, wp_cfg)); + + teardown(NULL); + + flashrom_wp_cfg_release(wp_cfg); +} + +/* Enabling hardware WP with a valid range succeeds */ +void set_wp_range_dummyflasher_test_success(void **state) +{ + (void) state; /* unused */ + + const char *param_dup = "bus=spi,emulate=W25Q128FV,hwwp=no"; + + struct flashrom_flashctx flash = { 0 }; + struct flashchip mock_chip = chip_W25Q128_V; + struct flashrom_wp_cfg *wp_cfg; + + size_t start; + size_t len; + + setup_chip(&flash, NULL, &mock_chip, param_dup); + + /* Use last 4 KiB for a range. */ + assert_int_equal(0, flashrom_wp_cfg_new(&wp_cfg)); + flashrom_wp_set_mode(wp_cfg, FLASHROM_WP_MODE_HARDWARE); + flashrom_wp_set_range(wp_cfg, mock_chip.total_size * KiB - 4 * KiB, 4 * KiB); + + assert_int_equal(0, flashrom_wp_write_cfg(&flash, wp_cfg)); + + /* Check that range was set correctly. */ + assert_int_equal(0, flashrom_wp_read_cfg(wp_cfg, &flash)); + flashrom_wp_get_range(&start, &len, wp_cfg); + assert_int_equal(16 * MiB - 4 * KiB, start); + assert_int_equal(4 * KiB, len); + + teardown(NULL); + + flashrom_wp_cfg_release(wp_cfg); +} + +/* Enable hardware WP and verify that it can not be unset */ +void switch_wp_mode_dummyflasher_test_success(void **state) +{ + (void) state; /* unused */ + + const char *param_dup = "bus=spi,emulate=W25Q128FV,hwwp=yes"; + + struct flashrom_flashctx flash = { 0 }; + struct flashchip mock_chip = chip_W25Q128_V; + struct flashrom_wp_cfg *wp_cfg; + + setup_chip(&flash, NULL, &mock_chip, param_dup); + + assert_int_equal(0, flashrom_wp_cfg_new(&wp_cfg)); + + /* Check initial mode. */ + assert_int_equal(0, flashrom_wp_read_cfg(wp_cfg, &flash)); + assert_int_equal(FLASHROM_WP_MODE_DISABLED, flashrom_wp_get_mode(wp_cfg)); + + /* Enable hardware protection, which can't be unset because simulated + HW WP pin is in active state. */ + flashrom_wp_set_mode(wp_cfg, FLASHROM_WP_MODE_HARDWARE); + assert_int_equal(0, flashrom_wp_write_cfg(&flash, wp_cfg)); + assert_int_equal(0, flashrom_wp_read_cfg(wp_cfg, &flash)); + assert_int_equal(FLASHROM_WP_MODE_HARDWARE, flashrom_wp_get_mode(wp_cfg)); + + /* Check that write-protection mode can't be unset. */ + flashrom_wp_set_mode(wp_cfg, FLASHROM_WP_MODE_DISABLED); + assert_int_equal(FLASHROM_WP_ERR_VERIFY_FAILED, flashrom_wp_write_cfg(&flash, wp_cfg)); + + /* Final mode should be "hardware". */ + assert_int_equal(0, flashrom_wp_read_cfg(wp_cfg, &flash)); + assert_int_equal(FLASHROM_WP_MODE_HARDWARE, flashrom_wp_get_mode(wp_cfg)); + + teardown(NULL); + + flashrom_wp_cfg_release(wp_cfg); +} + +/* WP state is decoded correctly from status registers */ +void wp_init_from_status_dummyflasher_test_success(void **state) +{ + (void) state; /* unused */ + + /* + * CMP (S14) = 1 (range complement) + * SRP1 (S8) = 1 + * SRP0 (S7) = 1 (`SRP1 == 1 && SRP0 == 1` is permanent mode) + * SEC (S6) = 1 (base unit is a 4 KiB sector) + * TB (S5) = 1 (bottom up range) + * BP2 (S4) = 0 + * BP1 (S3) = 1 + * BP0 (S2) = 1 (bp: BP2-0 == 0b011 == 3) + * + * Range coefficient is `2 ** (bp - 1)`, which is 4 in this case. + * Multiplaying that by base unit gives 16 KiB protected region at the + * bottom (start of the chip), which is then complemented. + */ + const char *param_dup = "bus=spi,emulate=W25Q128FV,spi_status=0x41ec"; + + struct flashrom_flashctx flash = { 0 }; + struct flashchip mock_chip = chip_W25Q128_V; + struct flashrom_wp_cfg *wp_cfg; + + size_t start; + size_t len; + + setup_chip(&flash, NULL, &mock_chip, param_dup); + + assert_int_equal(0, flashrom_wp_cfg_new(&wp_cfg)); + + /* Verify that WP mode reflects SPI status */ + assert_int_equal(0, flashrom_wp_read_cfg(wp_cfg, &flash)); + assert_int_equal(FLASHROM_WP_MODE_PERMANENT, flashrom_wp_get_mode(wp_cfg)); + flashrom_wp_get_range(&start, &len, wp_cfg); + assert_int_equal(0x004000, start); + assert_int_equal(0xffc000, len); + + teardown(NULL); + + flashrom_wp_cfg_release(wp_cfg); +} + +/* Enabled WP makes full chip erasure fail */ +void full_chip_erase_with_wp_dummyflasher_test_success(void **state) +{ + (void) state; /* unused */ + + struct flashrom_flashctx flash = { 0 }; + struct flashrom_layout *layout; + struct flashchip mock_chip = chip_W25Q128_V; + struct flashrom_wp_cfg *wp_cfg; + + const char *param_dup = "bus=spi,emulate=W25Q128FV,hwwp=yes"; + + setup_chip(&flash, &layout, &mock_chip, param_dup); + /* Layout regions are created by setup_chip(). */ + assert_int_equal(0, flashrom_layout_include_region(layout, "head")); + assert_int_equal(0, flashrom_layout_include_region(layout, "tail")); + + assert_int_equal(0, flashrom_wp_cfg_new(&wp_cfg)); + + /* Write protection takes effect only after changing SRP values, so at + this stage WP is not enabled and erase completes successfully. */ + assert_int_equal(0, flashrom_flash_erase(&flash)); + + /* Write non-erased value to entire chip so that erase operations cannot + * be optimized away. */ + unsigned long size = flashrom_flash_getsize(&flash); + uint8_t *const contents = malloc(size); + assert_non_null(contents); + memset(contents, UNERASED_VALUE(&flash), size); + assert_int_equal(0, flashrom_image_write(&flash, contents, size, NULL)); + free(contents); + + assert_int_equal(0, flashrom_wp_read_cfg(wp_cfg, &flash)); + + /* Hardware-protect first 4 KiB. */ + flashrom_wp_set_range(wp_cfg, 0, 4 * KiB); + flashrom_wp_set_mode(wp_cfg, FLASHROM_WP_MODE_HARDWARE); + + assert_int_equal(0, flashrom_wp_write_cfg(&flash, wp_cfg)); + + /* Try erasing the chip again. Now that WP is active, the first 4 KiB is + protected and we're trying to erase the whole chip, erase should + fail. */ + assert_int_equal(1, flashrom_flash_erase(&flash)); + + teardown(&layout); + + flashrom_wp_cfg_release(wp_cfg); +} + +/* Enabled WP does not block erasing unprotected parts of the chip */ +void partial_chip_erase_with_wp_dummyflasher_test_success(void **state) +{ + (void) state; /* unused */ + + struct flashrom_flashctx flash = { 0 }; + struct flashrom_layout *layout; + struct flashchip mock_chip = chip_W25Q128_V; + struct flashrom_wp_cfg *wp_cfg; + + const char *param_dup = "bus=spi,emulate=W25Q128FV,hwwp=yes"; + + setup_chip(&flash, &layout, &mock_chip, param_dup); + /* Layout region is created by setup_chip(). */ + assert_int_equal(0, flashrom_layout_include_region(layout, "tail")); + + assert_int_equal(0, flashrom_wp_cfg_new(&wp_cfg)); + + assert_int_equal(0, flashrom_wp_read_cfg(wp_cfg, &flash)); + + /* Hardware-protect head region. */ + flashrom_wp_set_mode(wp_cfg, FLASHROM_WP_MODE_HARDWARE); + flashrom_wp_set_range(wp_cfg, 0, LAYOUT_TAIL_REGION_START); + + assert_int_equal(0, flashrom_wp_write_cfg(&flash, wp_cfg)); + + /* First 4 KiB is the only protected part of the chip and the region + we included covers only unprotected part, so erase operation should + succeed. */ + assert_int_equal(0, flashrom_flash_erase(&flash)); + + teardown(&layout); + + flashrom_wp_cfg_release(wp_cfg); +} + +/* Chip register values & masks are calculated correctly by WP */ +void wp_get_register_values_and_masks(void **state) +{ + (void) state; /* unused */ + + /* + * Test with range: start = 0x004000, lengh = 0xffc000 + * + * WP should use these bit values: + * WPS (S17) = 0 (write protect scheme) + * CMP (S14) = 1 (range complement) + * SRP1 (S8) = 0 + * SRP0 (S7) = 1 (`SRP1 == 1 && SRP0 == 1` is permanent mode) + * SEC (S6) = 1 (base unit is a 4 KiB sector) + * TB (S5) = 1 (bottom up range) + * BP2 (S4) = 0 + * BP1 (S3) = 1 + * BP0 (S2) = 1 (bp: BP2-0 == 0b011 == 3) + * + * Register values: + * SR1 = 0b11101100 = 0xec + * SR2 = 0b01000000 = 0x40 + * SR3 = 0b00000000 = 0x00 + * + * Masks for WP bits in registers: + * SR1: 0b11111100 = 0xfc + * SR2: 0b01000000 = 0x41 + * SR3: 0b00000100 = 0x04 + * + * All WP bits are RW so write masks should be the same as the bit masks. + * + */ + struct flashrom_flashctx flash = { 0 }; + struct flashchip mock_chip = chip_W25Q128_V; + struct flashrom_wp_cfg *wp_cfg; + + uint8_t reg_values[MAX_REGISTERS]; + uint8_t bit_masks[MAX_REGISTERS]; + uint8_t write_masks[MAX_REGISTERS]; + + setup_chip(&flash, NULL, &mock_chip, "bus=spi,emulate=W25Q128FV"); + + assert_int_equal(0, flashrom_wp_cfg_new(&wp_cfg)); + flashrom_wp_set_mode(wp_cfg, FLASHROM_WP_MODE_HARDWARE); + flashrom_wp_set_range(wp_cfg, 0x004000, 0xffc000); + + assert_int_equal(0, wp_cfg_to_reg_values(reg_values, bit_masks, write_masks, &flash, wp_cfg)); + + assert_int_equal(0xec, reg_values[STATUS1]); + assert_int_equal(0x40, reg_values[STATUS2]); + assert_int_equal(0x00, reg_values[STATUS3]); + + assert_int_equal(0xfc, bit_masks[STATUS1]); + assert_int_equal(0x41, bit_masks[STATUS2]); + assert_int_equal(0x04, bit_masks[STATUS3]); + + assert_int_equal(0xfc, write_masks[STATUS1]); + assert_int_equal(0x41, write_masks[STATUS2]); + assert_int_equal(0x04, write_masks[STATUS3]); + + teardown(NULL); + + flashrom_wp_cfg_release(wp_cfg); +} diff --git a/tests/dediprog.c b/tests/dediprog.c new file mode 100644 index 00000000..26b1e751 --- /dev/null +++ b/tests/dediprog.c @@ -0,0 +1,58 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2021 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "lifecycle.h" + +#if CONFIG_DEDIPROG == 1 +static int dediprog_libusb_init(void *state, libusb_context **ctx) +{ + *ctx = not_null(); + return 0; +} + +static int dediprog_libusb_control_transfer(void *state, + libusb_device_handle *devh, + uint8_t bmRequestType, + uint8_t bRequest, + uint16_t wValue, + uint16_t wIndex, + unsigned char *data, + uint16_t wLength, + unsigned int timeout) +{ + if (bRequest == 0x08 /* dediprog_cmds CMD_READ_PROG_INFO */) { + /* Provide dediprog Device String into data buffer */ + memcpy(data, "SF600 V:7.2.2 ", wLength); + } + return wLength; +} + +void dediprog_basic_lifecycle_test_success(void **state) +{ + struct io_mock_fallback_open_state dediprog_fallback_open_state = { + .noc = 0, + .paths = { NULL }, + }; + const struct io_mock dediprog_io = { + .libusb_init = dediprog_libusb_init, + .libusb_control_transfer = dediprog_libusb_control_transfer, + .fallback_open_state = &dediprog_fallback_open_state, + }; + + run_basic_lifecycle(state, &dediprog_io, &programmer_dediprog, "voltage=3.5V"); +} +#else + SKIP_TEST(dediprog_basic_lifecycle_test_success) +#endif /* CONFIG_DEDIPROG */ diff --git a/tests/dummyflasher.c b/tests/dummyflasher.c new file mode 100644 index 00000000..052938be --- /dev/null +++ b/tests/dummyflasher.c @@ -0,0 +1,153 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2021 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "lifecycle.h" + +#if CONFIG_DUMMY == 1 +void dummy_basic_lifecycle_test_success(void **state) +{ + struct io_mock_fallback_open_state dummy_fallback_open_state = { + .noc = 0, + .paths = { NULL }, + }; + const struct io_mock dummy_io = { + .fallback_open_state = &dummy_fallback_open_state, + }; + + run_basic_lifecycle(state, &dummy_io, &programmer_dummy, "bus=parallel+lpc+fwh+spi+prog"); +} + +void dummy_probe_lifecycle_test_success(void **state) +{ + struct io_mock_fallback_open_state dummy_fallback_open_state = { + .noc = 0, + .paths = { NULL }, + }; + const struct io_mock dummy_io = { + .fallback_open_state = &dummy_fallback_open_state, + }; + + run_probe_lifecycle(state, &dummy_io, &programmer_dummy, "bus=spi,emulate=W25Q128FV", "W25Q128.V"); +} + +void dummy_probe_variable_size_test_success(void **state) +{ + struct io_mock_fallback_open_state dummy_fallback_open_state = { + .noc = 0, + .paths = { NULL }, + }; + const struct io_mock dummy_io = { + .fallback_open_state = &dummy_fallback_open_state, + }; + + run_probe_lifecycle(state, &dummy_io, &programmer_dummy, "size=8388608,emulate=VARIABLE_SIZE", "Opaque flash chip"); +} + +void dummy_init_fails_unhandled_param_test_success(void **state) +{ + struct io_mock_fallback_open_state dummy_fallback_open_state = { + .noc = 0, + .paths = { NULL }, + }; + const struct io_mock dummy_io = { + .fallback_open_state = &dummy_fallback_open_state, + }; + + /* + * Programmer init should fail due to `dummy_init` failure caused by + * invalid value of `emulate` param. There is unhandled param left + * at the end of param string. + */ + run_init_error_path(state, &dummy_io, &programmer_dummy, "bus=spi,emulate=INVALID,unhandled=value", 1); +} + +void dummy_init_success_invalid_param_test_success(void **state) +{ + struct io_mock_fallback_open_state dummy_fallback_open_state = { + .noc = 0, + .paths = { NULL }, + }; + const struct io_mock dummy_io = { + .fallback_open_state = &dummy_fallback_open_state, + }; + + /* + * Programmer init should fail despite of the fact that `dummy_init` + * is successful, due to invalid param at the end of param string. + */ + run_init_error_path(state, &dummy_io, &programmer_dummy, + "bus=spi,emulate=W25Q128FV,invalid=value", ERROR_FLASHROM_FATAL); +} + +void dummy_init_success_unhandled_param_test_success(void **state) +{ + struct io_mock_fallback_open_state dummy_fallback_open_state = { + .noc = 0, + .paths = { NULL }, + }; + const struct io_mock dummy_io = { + .fallback_open_state = &dummy_fallback_open_state, + }; + + /* + * Programmer init should fail despite of the fact that `dummy_init` + * is successful, due to unhandled param at the end of param string. + * Unhandled param `voltage` is not used for dummyflasher. + */ + run_init_error_path(state, &dummy_io, &programmer_dummy, + "bus=spi,emulate=W25Q128FV,voltage=3.5V", ERROR_FLASHROM_FATAL); +} + +void dummy_null_prog_param_test_success(void **state) +{ + struct io_mock_fallback_open_state dummy_fallback_open_state = { + .noc = 0, + .paths = { NULL }, + }; + const struct io_mock dummy_io = { + .fallback_open_state = &dummy_fallback_open_state, + }; + + run_basic_lifecycle(state, &dummy_io, &programmer_dummy, NULL); +} + +void dummy_all_buses_test_success(void **state) +{ + struct io_mock_fallback_open_state dummy_fallback_open_state = { + .noc = 0, + .paths = { NULL }, + }; + const struct io_mock dummy_io = { + .fallback_open_state = &dummy_fallback_open_state, + }; + + run_basic_lifecycle(state, &dummy_io, &programmer_dummy, "bus=lpc+fwh"); + run_basic_lifecycle(state, &dummy_io, &programmer_dummy, "bus=spi"); + run_basic_lifecycle(state, &dummy_io, &programmer_dummy, "bus=prog"); + run_basic_lifecycle(state, &dummy_io, &programmer_dummy, "bus=parallel+fwh+prog"); + run_basic_lifecycle(state, &dummy_io, &programmer_dummy, "bus=spi+prog"); + run_basic_lifecycle(state, &dummy_io, &programmer_dummy, "bus=parallel+lpc+spi"); +} + +#else + SKIP_TEST(dummy_basic_lifecycle_test_success) + SKIP_TEST(dummy_probe_lifecycle_test_success) + SKIP_TEST(dummy_probe_variable_size_test_success) + SKIP_TEST(dummy_init_fails_unhandled_param_test_success) + SKIP_TEST(dummy_init_success_invalid_param_test_success) + SKIP_TEST(dummy_init_success_unhandled_param_test_success) + SKIP_TEST(dummy_null_prog_param_test_success) + SKIP_TEST(dummy_all_buses_test_success) +#endif /* CONFIG_DUMMY */ diff --git a/tests/flashrom.c b/tests/flashrom.c new file mode 100644 index 00000000..cc888313 --- /dev/null +++ b/tests/flashrom.c @@ -0,0 +1,71 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2020 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <include/test.h> +#include "tests.h" + +#include "programmer.h" + +#define assert_equal_and_free(text, expected) \ + do { \ + assert_string_equal(text, expected); \ + free(text); \ + } while (0) + +#define assert_not_equal_and_free(text, expected) \ + do { \ + assert_string_not_equal(text, expected); \ + free(text); \ + } while (0) + + +void flashbuses_to_text_test_success(void **state) +{ + (void) state; /* unused */ + + enum chipbustype bustype; + char *text; + + bustype = BUS_NONSPI; + text = flashbuses_to_text(bustype); + assert_equal_and_free(text, "Non-SPI"); + + bustype |= BUS_PARALLEL; + text = flashbuses_to_text(bustype); + assert_not_equal_and_free(text, "Non-SPI, Parallel"); + + bustype = BUS_PARALLEL; + bustype |= BUS_LPC; + text = flashbuses_to_text(bustype); + assert_equal_and_free(text, "Parallel, LPC"); + + bustype |= BUS_FWH; + //BUS_NONSPI = BUS_PARALLEL | BUS_LPC | BUS_FWH, + text = flashbuses_to_text(bustype); + assert_equal_and_free(text, "Non-SPI"); + + bustype |= BUS_SPI; + text = flashbuses_to_text(bustype); + assert_equal_and_free(text, "Parallel, LPC, FWH, SPI"); + + bustype |= BUS_PROG; + text = flashbuses_to_text(bustype); + assert_equal_and_free(text, + "Parallel, LPC, FWH, SPI, Programmer-specific"); + + bustype = BUS_NONE; + text = flashbuses_to_text(bustype); + assert_equal_and_free(text, "None"); +} diff --git a/tests/helpers.c b/tests/helpers.c new file mode 100644 index 00000000..271fb483 --- /dev/null +++ b/tests/helpers.c @@ -0,0 +1,79 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2020 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <include/test.h> + +#include "tests.h" +#include "flash.h" + +#include <stdint.h> + +void address_to_bits_test_success(void **state) +{ + (void) state; /* unused */ + assert_int_equal(16, address_to_bits(0xAA55)); +} + +void bitcount_test_success(void **state) +{ + (void) state; /* unused */ + assert_int_equal(4, bitcount(0xAA)); +} + +void minmax_test_success(void **state) +{ + (void) state; /* unused */ + assert_int_equal(0x55, min(0xAA, 0x55)); + assert_int_equal(0xAA, max(0xAA, 0x55)); +} + +void strcat_realloc_test_success(void **state) +{ + (void) state; /* unused */ + const char src0[] = "hello"; + const char src1[] = " world"; + char *dest = calloc(1, 1); + assert_non_null(dest); + dest = strcat_realloc(dest, src0); + dest = strcat_realloc(dest, src1); + assert_string_equal("hello world", dest); + free(dest); +} + +void tolower_string_test_success(void **state) +{ + (void) state; /* unused */ + char str[] = "HELLO AGAIN"; + assert_string_equal("HELLO AGAIN", str); + tolower_string(str); + assert_string_equal("hello again", str); +} + +void reverse_byte_test_success(void **state) +{ + (void) state; /* unused */ + assert_int_equal(0x5A, reverse_byte(0x5A)); + assert_int_equal(0x0F, reverse_byte(0xF0)); +} + +void reverse_bytes_test_success(void **state) +{ + (void) state; /* unused */ + uint8_t src[] = { 0xAA, 0x55 }; + uint8_t dst[2]; + reverse_bytes(dst, src, 2); + assert_int_equal(src[0], dst[1]); + assert_int_equal(src[1], dst[0]); +} diff --git a/tests/include/test.h b/tests/include/test.h new file mode 100644 index 00000000..ef0b9a6f --- /dev/null +++ b/tests/include/test.h @@ -0,0 +1,45 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2020 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _TESTS_TEST_H +#define _TESTS_TEST_H + +/* + * Standard test header that should be included in all tests. For now it just encapsulates the + * include dependencies for Cmocka. Test-specific APIs that are so generic we would want them + * available everywhere could also be added here. + */ + +#include <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmocka.h> + +#define NON_ZERO (0xf000baaa) + +#define MOCK_FD (0x10ec) + +#define SKIP_TEST(name) \ + void name (void **state) { skip(); } + +/* + * Having this as function allows to set a breakpoint on the address, + * as it has a named symbol associated with the address number. + */ +void *not_null(void); + +#define LOG_ME printf("%s is called\n", __func__) + +#endif /* _TESTS_TEST_H */ diff --git a/tests/io_mock.c b/tests/io_mock.c new file mode 100644 index 00000000..4828e53c --- /dev/null +++ b/tests/io_mock.c @@ -0,0 +1,31 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2021, Google Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "io_mock.h" + +static const struct io_mock *current_io = NULL; + +void io_mock_register(const struct io_mock *io) +{ + /* A test can either register its own mock open function or fallback_open_state. */ + assert_true(io == NULL || io->iom_open == NULL || io->fallback_open_state == NULL); + current_io = io; +} + +const struct io_mock *get_io(void) +{ + return current_io; +} diff --git a/tests/io_mock.h b/tests/io_mock.h new file mode 100644 index 00000000..97980668 --- /dev/null +++ b/tests/io_mock.h @@ -0,0 +1,130 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (c) 2021 Nico Huber <nico.h@gmx.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _IO_MOCK_H_ +#define _IO_MOCK_H_ + +#include <include/test.h> + +/* Required for `FILE *` */ +#include <stdio.h> + +#include <stdint.h> + +#include "usb_unittests.h" + +/* Address value needs fit into uint8_t. */ +#define USB_DEVICE_ADDRESS 19 + +/* Define struct pci_dev to avoid dependency on pci.h */ +struct pci_dev { + char padding[18]; + unsigned int device_id; +}; + +/* Linux I2C interface constants, avoiding linux/i2c-dev.h */ +#define I2C_SLAVE 0x0703 + +/* Always return success for tests. */ +#define S_ISREG(x) 0 + +/* Maximum number of open calls to mock. This number is arbitrary. */ +#define MAX_MOCK_OPEN 4 + +struct io_mock_fallback_open_state { + unsigned int noc; + const char *paths[MAX_MOCK_OPEN]; + int flags[MAX_MOCK_OPEN]; +}; + +struct io_mock { + void *state; + + /* Port I/O */ + void (*outb)(void *state, unsigned char value, unsigned short port); + unsigned char (*inb)(void *state, unsigned short port); + + void (*outw)(void *state, unsigned short value, unsigned short port); + unsigned short (*inw)(void *state, unsigned short port); + + void (*outl)(void *state, unsigned int value, unsigned short port); + unsigned int (*inl)(void *state, unsigned short port); + + /* USB I/O */ + int (*libusb_init)(void *state, libusb_context **ctx); + int (*libusb_control_transfer)(void *state, + libusb_device_handle *devh, + uint8_t bmRequestType, + uint8_t bRequest, + uint16_t wValue, + uint16_t wIndex, + unsigned char *data, + uint16_t wLength, + unsigned int timeout); + ssize_t (*libusb_get_device_list)(void *state, libusb_context *, libusb_device ***list); + void (*libusb_free_device_list)(void *state, libusb_device **list, int unref_devices); + int (*libusb_get_device_descriptor)(void *state, libusb_device *, struct libusb_device_descriptor *); + int (*libusb_get_config_descriptor)(void *state, + libusb_device *, + uint8_t config_index, + struct libusb_config_descriptor **); + void (*libusb_free_config_descriptor)(void *state, struct libusb_config_descriptor *); + struct libusb_transfer* (*libusb_alloc_transfer)(void *state, int iso_packets); + int (*libusb_submit_transfer)(void *state, struct libusb_transfer *transfer); + void (*libusb_free_transfer)(void *state, struct libusb_transfer *transfer); + int (*libusb_handle_events_timeout)(void *state, libusb_context *ctx, struct timeval *tv); + + /* POSIX File I/O */ + int (*iom_open)(void *state, const char *pathname, int flags, mode_t mode); + int (*iom_ioctl)(void *state, int fd, unsigned long request, va_list args); + int (*iom_read)(void *state, int fd, void *buf, size_t sz); + int (*iom_write)(void *state, int fd, const void *buf, size_t sz); + + /* Standard I/O */ + FILE* (*iom_fopen)(void *state, const char *pathname, const char *mode); + char* (*iom_fgets)(void *state, char *buf, int len, FILE *fp); + size_t (*iom_fread)(void *state, void *buf, size_t size, size_t len, FILE *fp); + size_t (*iom_fwrite)(void *state, const void *buf, size_t size, size_t len, FILE *fp); + int (*iom_fprintf)(void *state, FILE *fp, const char *fmt, va_list args); + int (*iom_fclose)(void *state, FILE *fp); + FILE *(*iom_fdopen)(void *state, int fd, const char *mode); + + /* + * An alternative to custom open mock. A test can either register its + * own mock open function or fallback_open_state. + */ + struct io_mock_fallback_open_state *fallback_open_state; +}; + +void io_mock_register(const struct io_mock *io); + +const struct io_mock *get_io(void); + +#endif diff --git a/tests/io_real.c b/tests/io_real.c new file mode 100644 index 00000000..429027fb --- /dev/null +++ b/tests/io_real.c @@ -0,0 +1,78 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2022 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "io_mock.h" +#include "wraps.h" + +#include "io_real.h" +#include <string.h> + +static int io_real_open(void *state, const char *pathname, int flags, mode_t mode) +{ + LOG_ME; + return __real_open(pathname, flags, mode); +} + +static FILE *io_real_fopen(void *state, const char *pathname, const char *mode) +{ + LOG_ME; + return __real_fopen(pathname, mode); +} + +static FILE *io_real_fdopen(void *state, int fd, const char *mode) +{ + LOG_ME; + return __real_fdopen(fd, mode); +} + +static size_t io_real_fwrite(void *state, const void *ptr, size_t size, size_t nmemb, FILE *fp) +{ + return __real_fwrite(ptr, size, nmemb, fp); +} + +static int io_real_fclose(void *state, FILE *fp) +{ + LOG_ME; + return __real_fclose(fp); +} + +/* Mock ios that defer to the real io functions. + * These exist so that code coverage can print to real files on disk. + */ +static const struct io_mock real_io = { + .iom_open = io_real_open, + .iom_fopen = io_real_fopen, + .iom_fwrite = io_real_fwrite, + .iom_fdopen = io_real_fdopen, + .iom_fclose = io_real_fclose, +}; + +/* Return 0 if string ends with suffix. */ +static int check_suffix(const char *string, const char *suffix) +{ + int len_l = strlen(string); + int len_r = strlen(suffix); + if (len_l > len_r) + return strcmp(string + len_l - len_r, suffix); + return 1; +} + +void maybe_unmock_io(const char *pathname) +{ + const char *gcov_suffix = ".gcda"; + const char *llvm_cov_suffix = ".profraw"; + if (!check_suffix(pathname, gcov_suffix) || !check_suffix(pathname, llvm_cov_suffix)) + io_mock_register(&real_io); +} diff --git a/tests/io_real.h b/tests/io_real.h new file mode 100644 index 00000000..f2491c3a --- /dev/null +++ b/tests/io_real.h @@ -0,0 +1,24 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2022 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IO_REAL_H +#define IO_REAL_H + +/* Detect file io that should not be mocked, for example code coverage writing + * gcda files. Call io_mock_register with functions that defer to real io. + */ +void maybe_unmock_io(const char* pathname); + +#endif /* IO_REAL_H */ diff --git a/tests/layout.c b/tests/layout.c new file mode 100644 index 00000000..e71debfe --- /dev/null +++ b/tests/layout.c @@ -0,0 +1,209 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2021 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <include/test.h> +#include <stdio.h> + +#include "tests.h" +#include "flash.h" +#include "layout.h" +#include "libflashrom.h" + +void included_regions_dont_overlap_test_success(void **state) +{ + (void) state; /* unused */ + + printf("Creating layout... "); + struct flashrom_layout *layout; + assert_int_equal(0, flashrom_layout_new(&layout)); + printf("done\n"); + + printf("Adding and including first region... "); + assert_int_equal(0, flashrom_layout_add_region(layout, 0x00021000, 0x00031000, "first region")); + assert_int_equal(0, flashrom_layout_include_region(layout, "first region")); + printf("done"); + + printf(", second (non-overlapping) region... "); + assert_int_equal(0, flashrom_layout_add_region(layout, 0x00031001, 0x0023efc0, "second region")); + assert_int_equal(0, flashrom_layout_include_region(layout, "second region")); + printf("done\n"); + + printf("Asserting included regions do not overlap... "); + assert_int_equal(0, included_regions_overlap(layout)); + printf("done\n"); + + printf("Releasing layout... "); + flashrom_layout_release(layout); + printf("done\n"); +} + +void included_regions_overlap_test_success(void **state) +{ + (void) state; /* unused */ + + printf("Creating layout... "); + struct flashrom_layout *layout; + assert_int_equal(0, flashrom_layout_new(&layout)); + printf("done\n"); + + printf("Adding and including first region... "); + assert_int_equal(0, flashrom_layout_add_region(layout, 0x00021000, 0x00031000, "first region")); + assert_int_equal(0, flashrom_layout_include_region(layout, "first region")); + printf("done"); + + printf(", second (overlapping) region... "); + assert_int_equal(0, flashrom_layout_add_region(layout, 0x00027100, 0x0023efc0, "second region")); + assert_int_equal(0, flashrom_layout_include_region(layout, "second region")); + printf("done\n"); + + printf("Asserting included regions overlap... "); + assert_int_equal(1, included_regions_overlap(layout)); + printf("done\n"); + + printf("Releasing layout... "); + flashrom_layout_release(layout); + printf("done\n"); +} + +void region_not_included_overlap_test_success(void **state) +{ + (void) state; /* unused */ + + printf("Creating layout... "); + struct flashrom_layout *layout; + assert_int_equal(0, flashrom_layout_new(&layout)); + printf("done\n"); + + printf("Adding and including first region... "); + assert_int_equal(0, flashrom_layout_add_region(layout, 0x00021000, 0x00031000, "first region")); + assert_int_equal(0, flashrom_layout_include_region(layout, "first region")); + printf("done"); + + printf(", second (overlapping) region, not included... "); + assert_int_equal(0, flashrom_layout_add_region(layout, 0x00027100, 0x0023efc0, "second region")); + printf("done\n"); + + printf("Asserting included regions do not overlap... "); + assert_int_equal(0, included_regions_overlap(layout)); + printf("done\n"); + + printf("Releasing layout... "); + flashrom_layout_release(layout); + printf("done\n"); +} + +void layout_pass_sanity_checks_test_success(void **state) +{ + (void) state; /* unused */ + + unsigned int region_start = 0x00021000; + unsigned int region_end = 0x00031000; + unsigned int start = 0; + unsigned int len = 0; + + struct flashrom_layout *layout; + + printf("Creating layout with one included region... "); + assert_int_equal(0, flashrom_layout_new(&layout)); + assert_int_equal(0, flashrom_layout_add_region(layout, region_start, region_end, "region")); + assert_int_equal(0, flashrom_layout_include_region(layout, "region")); + printf("done\n"); + + printf("Asserting region range... "); + flashrom_layout_get_region_range(layout, "region", &start, &len); + assert_int_equal(start, region_start); + assert_int_equal(len, region_end - region_start + 1); + printf("done\n"); + + printf("Layout passes sanity checks... "); + + struct flashchip chip = { + .total_size = 1024, + }; + struct flashrom_flashctx flash = { + .chip = &chip, + }; + flashrom_layout_set(&flash, layout); + assert_int_equal(0, layout_sanity_checks(&flash)); + + printf("done\n"); + + printf("Releasing layout... "); + flashrom_layout_release(layout); + printf("done\n"); +} + +void layout_region_invalid_address_test_success(void **state) +{ + (void) state; /* unused */ + + struct flashrom_layout *layout; + + printf("Creating layout with one included region... "); + assert_int_equal(0, flashrom_layout_new(&layout)); + assert_int_equal(0, flashrom_layout_add_region(layout, 0x60000000, 0x70000000, "region")); + assert_int_equal(0, flashrom_layout_include_region(layout, "region")); + printf("done\n"); + + printf("Layout does not pass sanity checks... "); + + struct flashchip chip = { + /* Make sure layout region addresses exceed total size on chip. */ + .total_size = 1, + }; + struct flashrom_flashctx flash = { + .chip = &chip, + }; + flashrom_layout_set(&flash, layout); + assert_int_equal(1, layout_sanity_checks(&flash)); + + printf("done\n"); + + printf("Releasing layout... "); + flashrom_layout_release(layout); + printf("done\n"); +} + +void layout_region_invalid_range_test_success(void **state) +{ + (void) state; /* unused */ + + struct flashrom_layout *layout; + + printf("Creating layout with one included region... "); + assert_int_equal(0, flashrom_layout_new(&layout)); + /* Make sure address range of region is not positive i.e. start > end. */ + assert_int_equal(0, flashrom_layout_add_region(layout, 0x00000020, 0x00000010, "region")); + assert_int_equal(0, flashrom_layout_include_region(layout, "region")); + printf("done\n"); + + printf("Layout does not pass sanity checks... "); + + struct flashchip chip = { + /* Make sure layout region addresses fit into total size on chip. */ + .total_size = 1024, + }; + struct flashrom_flashctx flash = { + .chip = &chip, + }; + flashrom_layout_set(&flash, layout); + assert_int_equal(1, layout_sanity_checks(&flash)); + + printf("done\n"); + + printf("Releasing layout... "); + flashrom_layout_release(layout); + printf("done\n"); +} diff --git a/tests/libusb_wraps.c b/tests/libusb_wraps.c new file mode 100644 index 00000000..be990a12 --- /dev/null +++ b/tests/libusb_wraps.c @@ -0,0 +1,222 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2021 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <stdlib.h> + +#include <include/test.h> +#include "io_mock.h" +#include "libusb_wraps.h" + +void *__wrap_usb_dev_get_by_vid_pid_number( + libusb_context *usb_ctx, uint16_t vid, uint16_t pid, unsigned int num) +{ + LOG_ME; + return not_null(); +} + +int __wrap_libusb_init(libusb_context **ctx) +{ + LOG_ME; + if (get_io() && get_io()->libusb_init) + return get_io()->libusb_init(get_io()->state, ctx); + return 0; +} + +void __wrap_libusb_set_debug(libusb_context *ctx, int level) +{ + LOG_ME; +} + +int __wrap_libusb_set_option(libusb_context *ctx, int option, ...) +{ + LOG_ME; + return 0; +} + +int __wrap_libusb_open(libusb_device *dev, libusb_device_handle **devh) +{ + LOG_ME; + return 0; +} + +int __wrap_libusb_set_auto_detach_kernel_driver(libusb_device_handle *devh, int enable) +{ + LOG_ME; + return 0; +} + +int __wrap_libusb_detach_kernel_driver(libusb_device_handle *dev_handle, int interface_number) +{ + LOG_ME; + return 0; +} + +int __wrap_libusb_attach_kernel_driver(libusb_device_handle *dev_handle, int interface_number) +{ + LOG_ME; + return 0; +} + +struct libusb_device_handle *__wrap_libusb_open_device_with_vid_pid( + libusb_context *ctx, uint16_t vendor_id, uint16_t product_id) +{ + LOG_ME; + return not_null(); +} + +libusb_device *__wrap_libusb_get_device(libusb_device_handle *dev_handle) +{ + LOG_ME; + return not_null(); +} + +ssize_t __wrap_libusb_get_device_list(libusb_context *ctx, libusb_device ***list) +{ + LOG_ME; + if (get_io() && get_io()->libusb_get_device_list) + return get_io()->libusb_get_device_list(get_io()->state, ctx, list); + return 0; +} + +void __wrap_libusb_free_device_list(libusb_device **list, int unref_devices) +{ + LOG_ME; + if (get_io() && get_io()->libusb_free_device_list) + get_io()->libusb_free_device_list(get_io()->state, list, unref_devices); +} + +uint8_t __wrap_libusb_get_bus_number(libusb_device *dev) +{ + LOG_ME; + return 0; +} + +uint8_t __wrap_libusb_get_device_address(libusb_device *dev) +{ + LOG_ME; + return USB_DEVICE_ADDRESS; +} + +int __wrap_libusb_get_device_descriptor(libusb_device *dev, struct libusb_device_descriptor *desc) +{ + LOG_ME; + if (get_io() && get_io()->libusb_get_device_descriptor) + return get_io()->libusb_get_device_descriptor(get_io()->state, dev, desc); + return 0; +} + +int __wrap_libusb_get_config_descriptor( + libusb_device *dev, uint8_t config_index, struct libusb_config_descriptor **config) +{ + LOG_ME; + if (get_io() && get_io()->libusb_get_config_descriptor) + return get_io()->libusb_get_config_descriptor(get_io()->state, dev, config_index, config); + return 0; +} + +void __wrap_libusb_free_config_descriptor(struct libusb_config_descriptor *config) +{ + LOG_ME; + if (get_io() && get_io()->libusb_free_config_descriptor) + return get_io()->libusb_free_config_descriptor(get_io()->state, config); + return; +} + +int __wrap_libusb_get_configuration(libusb_device_handle *devh, int *config) +{ + LOG_ME; + return 0; +} + +int __wrap_libusb_set_configuration(libusb_device_handle *devh, int config) +{ + LOG_ME; + return 0; +} + +int __wrap_libusb_claim_interface(libusb_device_handle *devh, int interface_number) +{ + LOG_ME; + return 0; +} + +int __wrap_libusb_control_transfer(libusb_device_handle *devh, uint8_t bmRequestType, + uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned char *data, + uint16_t wLength, unsigned int timeout) +{ + LOG_ME; + if (get_io() && get_io()->libusb_control_transfer) + return get_io()->libusb_control_transfer(get_io()->state, + devh, bmRequestType, bRequest, wValue, wIndex, data, wLength, timeout); + return 0; +} + +int __wrap_libusb_release_interface(libusb_device_handle *devh, int interface_number) +{ + LOG_ME; + return 0; +} + +void __wrap_libusb_close(libusb_device_handle *devh) +{ + LOG_ME; +} + +libusb_device *__wrap_libusb_ref_device(libusb_device *dev) +{ + LOG_ME; + return NULL; +} + +void __wrap_libusb_unref_device(libusb_device *dev) +{ + LOG_ME; +} + +struct libusb_transfer *__wrap_libusb_alloc_transfer(int iso_packets) +{ + LOG_ME; + if (get_io() && get_io()->libusb_alloc_transfer) + return get_io()->libusb_alloc_transfer(get_io()->state, iso_packets); + return not_null(); +} + +int __wrap_libusb_submit_transfer(struct libusb_transfer *transfer) +{ + LOG_ME; + if (get_io() && get_io()->libusb_submit_transfer) + return get_io()->libusb_submit_transfer(get_io()->state, transfer); + return 0; +} + +void __wrap_libusb_free_transfer(struct libusb_transfer *transfer) +{ + LOG_ME; + if (get_io() && get_io()->libusb_free_transfer) + get_io()->libusb_free_transfer(get_io()->state, transfer); +} + +int __wrap_libusb_handle_events_timeout(libusb_context *ctx, struct timeval *tv) +{ + LOG_ME; + if (get_io() && get_io()->libusb_handle_events_timeout) + get_io()->libusb_handle_events_timeout(get_io()->state, ctx, tv); + return 0; +} + +void __wrap_libusb_exit(libusb_context *ctx) +{ + LOG_ME; +} diff --git a/tests/libusb_wraps.h b/tests/libusb_wraps.h new file mode 100644 index 00000000..8c3aa40a --- /dev/null +++ b/tests/libusb_wraps.h @@ -0,0 +1,57 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2022 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef LIBUSB_WRAPS_H +#define LIBUSB_WRAPS_H + +#include "usb_unittests.h" + +void *__wrap_usb_dev_get_by_vid_pid_number( + libusb_context *usb_ctx, uint16_t vid, uint16_t pid, unsigned int num); +int __wrap_libusb_init(libusb_context **ctx); +void __wrap_libusb_set_debug(libusb_context *ctx, int level); +int __wrap_libusb_set_option(libusb_context *ctx, int option, ...); +int __wrap_libusb_open(libusb_device *dev, libusb_device_handle **devh); +int __wrap_libusb_set_auto_detach_kernel_driver(libusb_device_handle *devh, int enable); +int __wrap_libusb_detach_kernel_driver(libusb_device_handle *dev_handle, int interface_number); +int __wrap_libusb_attach_kernel_driver(libusb_device_handle *dev_handle, int interface_number); +struct libusb_device_handle *__wrap_libusb_open_device_with_vid_pid( + libusb_context *ctx, uint16_t vendor_id, uint16_t product_id); +libusb_device *__wrap_libusb_get_device(libusb_device_handle *dev_handle); +ssize_t __wrap_libusb_get_device_list(libusb_context *ctx, libusb_device ***list); +void __wrap_libusb_free_device_list(libusb_device **list, int unref_devices); +uint8_t __wrap_libusb_get_bus_number(libusb_device *dev); +uint8_t __wrap_libusb_get_device_address(libusb_device *dev); +int __wrap_libusb_get_device_descriptor(libusb_device *dev, struct libusb_device_descriptor *desc); +int __wrap_libusb_get_config_descriptor( + libusb_device *dev, uint8_t config_index, struct libusb_config_descriptor **config); +void __wrap_libusb_free_config_descriptor(struct libusb_config_descriptor *config); +int __wrap_libusb_get_configuration(libusb_device_handle *devh, int *config); +int __wrap_libusb_set_configuration(libusb_device_handle *devh, int config); +int __wrap_libusb_claim_interface(libusb_device_handle *devh, int interface_number); +int __wrap_libusb_control_transfer(libusb_device_handle *devh, uint8_t bmRequestType, + uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned char *data, + uint16_t wLength, unsigned int timeout); +int __wrap_libusb_release_interface(libusb_device_handle *devh, int interface_number); +void __wrap_libusb_close(libusb_device_handle *devh); +libusb_device *__wrap_libusb_ref_device(libusb_device *dev); +void __wrap_libusb_unref_device(libusb_device *dev); +struct libusb_transfer *__wrap_libusb_alloc_transfer(int iso_packets); +int __wrap_libusb_submit_transfer(struct libusb_transfer *transfer); +void __wrap_libusb_free_transfer(struct libusb_transfer *transfer); +int __wrap_libusb_handle_events_timeout(libusb_context *ctx, struct timeval *tv); +void __wrap_libusb_exit(libusb_context *ctx); + +#endif /* LIBUSB_WRAPS_H */ diff --git a/tests/lifecycle.c b/tests/lifecycle.c new file mode 100644 index 00000000..95c42496 --- /dev/null +++ b/tests/lifecycle.c @@ -0,0 +1,98 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2021 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "lifecycle.h" + +static void probe_chip(const struct programmer_entry *prog, + struct flashrom_programmer *flashprog, + const char *const chip_name) +{ + struct flashrom_flashctx *flashctx; + + printf("Testing flashrom_flash_probe for programmer=%s, chip=%s ... \n", prog->name, chip_name); + assert_int_equal(0, flashrom_flash_probe(&flashctx, flashprog, chip_name)); + printf("... flashrom_flash_probe for programmer=%s successful\n", prog->name); + + flashrom_flash_release(flashctx); /* cleanup */ +} + +static void run_lifecycle(void **state, const struct io_mock *io, const struct programmer_entry *prog, + const char *param, const char *const chip_name, + void (*action)(const struct programmer_entry *prog, + struct flashrom_programmer *flashprog, + const char *const chip_name)) +{ + (void) state; /* unused */ + + io_mock_register(io); + + struct flashrom_programmer *flashprog; + + printf("Testing flashrom_programmer_init for programmer=%s ...\n", prog->name); + assert_int_equal(0, flashrom_programmer_init(&flashprog, prog->name, param)); + printf("... flashrom_programmer_init for programmer=%s successful\n", prog->name); + + if (action) + action(prog, flashprog, chip_name); + + printf("Testing flashrom_programmer_shutdown for programmer=%s ...\n", prog->name); + assert_int_equal(0, flashrom_programmer_shutdown(flashprog)); + printf("... flashrom_programmer_shutdown for programmer=%s successful\n", prog->name); + + io_mock_register(NULL); +} + +void run_basic_lifecycle(void **state, const struct io_mock *io, + const struct programmer_entry *prog, const char *param) +{ + /* Basic lifecycle only does init and shutdown, + * so neither chip name nor action is needed. */ + run_lifecycle(state, io, prog, param, NULL /* chip_name */, NULL /* action */); +} + +void run_probe_lifecycle(void **state, const struct io_mock *io, + const struct programmer_entry *prog, const char *param, const char *const chip_name) +{ + /* Each probe lifecycle should run independently, without cache. */ + clear_spi_id_cache(); + run_lifecycle(state, io, prog, param, chip_name, &probe_chip); +} + +void run_init_error_path(void **state, const struct io_mock *io, const struct programmer_entry *prog, + const char *param, const int error_code) +{ + (void) state; /* unused */ + + io_mock_register(io); + + struct flashrom_programmer *flashprog; + + printf("Testing init error path for programmer=%s with params: %s ...\n", prog->name, param); + assert_int_equal(error_code, flashrom_programmer_init(&flashprog, prog->name, param)); + printf("... init failed with error code %i as expected\n", error_code); + + /* + * `flashrom_programmer_shutdown` runs only registered shutdown functions, which means + * if nothing has been registered then nothing runs. + * Since this is testing error path on initialisation and error can happen at different + * phases of init, we don't know whether shutdown function has already been registered + * or not yet. Running `flashrom_programmer_shutdown` covers both situations. + */ + printf("Running programmer shutdown in case anything got registered...\n"); + assert_int_equal(0, flashrom_programmer_shutdown(flashprog)); + printf("... completed\n"); + + io_mock_register(NULL); +} diff --git a/tests/lifecycle.h b/tests/lifecycle.h new file mode 100644 index 00000000..b4c2fbed --- /dev/null +++ b/tests/lifecycle.h @@ -0,0 +1,39 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2021 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __LIFECYCLE_H__ +#define __LIFECYCLE_H__ + +#include <include/test.h> +#include <string.h> +#if defined(__linux__) && !defined(__ANDROID__) +#include <linux/spi/spidev.h> +#endif + +#include "tests.h" +#include "libflashrom.h" +#include "io_mock.h" +#include "programmer.h" +#include "spi.h" + +void run_basic_lifecycle(void **state, const struct io_mock *io, + const struct programmer_entry *prog, const char *param); + +void run_probe_lifecycle(void **state, const struct io_mock *io, + const struct programmer_entry *prog, const char *param, const char *const chip_name); + +void run_init_error_path(void **state, const struct io_mock *io, + const struct programmer_entry *prog, const char *param, const int error_code); +#endif /* __LIFECYCLE_H__ */ diff --git a/tests/linux_mtd.c b/tests/linux_mtd.c new file mode 100644 index 00000000..3aaa5abd --- /dev/null +++ b/tests/linux_mtd.c @@ -0,0 +1,94 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2021 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "lifecycle.h" + +#if CONFIG_LINUX_MTD == 1 +struct linux_mtd_io_state { + char *fopen_path; +}; + +static FILE *linux_mtd_fopen(void *state, const char *pathname, const char *mode) +{ + struct linux_mtd_io_state *io_state = state; + + io_state->fopen_path = strdup(pathname); + + return not_null(); +} + +static size_t linux_mtd_fread(void *state, void *buf, size_t size, size_t len, FILE *fp) +{ + struct linux_mtd_fread_mock_entry { + const char *path; + const char *data; + }; + const struct linux_mtd_fread_mock_entry fread_mock_map[] = { + { "/sys/class/mtd/mtd0//type", "nor" }, + { "/sys/class/mtd/mtd0//name", "Device" }, + { "/sys/class/mtd/mtd0//flags", "" }, + { "/sys/class/mtd/mtd0//size", "1024" }, + { "/sys/class/mtd/mtd0//erasesize", "512" }, + { "/sys/class/mtd/mtd0//numeraseregions", "0" }, + }; + + struct linux_mtd_io_state *io_state = state; + unsigned int i; + + if (!io_state->fopen_path) + return 0; + + for (i = 0; i < ARRAY_SIZE(fread_mock_map); i++) { + const struct linux_mtd_fread_mock_entry *entry = &fread_mock_map[i]; + + if (!strcmp(io_state->fopen_path, entry->path)) { + size_t data_len = min(size * len, strlen(entry->data)); + memcpy(buf, entry->data, data_len); + return data_len; + } + } + + return 0; +} + +static int linux_mtd_fclose(void *state, FILE *fp) +{ + struct linux_mtd_io_state *io_state = state; + + free(io_state->fopen_path); + + return 0; +} + +void linux_mtd_probe_lifecycle_test_success(void **state) +{ + struct linux_mtd_io_state linux_mtd_io_state = { NULL }; + struct io_mock_fallback_open_state linux_mtd_fallback_open_state = { + .noc = 0, + .paths = { NULL }, + }; + const struct io_mock linux_mtd_io = { + .state = &linux_mtd_io_state, + .iom_fopen = linux_mtd_fopen, + .iom_fread = linux_mtd_fread, + .iom_fclose = linux_mtd_fclose, + .fallback_open_state = &linux_mtd_fallback_open_state, + }; + + run_probe_lifecycle(state, &linux_mtd_io, &programmer_linux_mtd, "", "Opaque flash chip"); +} +#else + SKIP_TEST(linux_mtd_probe_lifecycle_test_success) +#endif /* CONFIG_LINUX_MTD */ diff --git a/tests/linux_spi.c b/tests/linux_spi.c new file mode 100644 index 00000000..1b653fef --- /dev/null +++ b/tests/linux_spi.c @@ -0,0 +1,72 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2021 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "lifecycle.h" + +#if CONFIG_LINUX_SPI == 1 +static int linux_spi_ioctl(void *state, int fd, unsigned long request, va_list args) { + + if (request == SPI_IOC_MESSAGE(2)) { /* ioctl code for read request */ + struct spi_ioc_transfer* msg = va_arg(args, struct spi_ioc_transfer*); + + /* First message has write array and write count */ + unsigned int writecnt = msg[0].len; + unsigned char *writearr = (unsigned char *)(uintptr_t)msg[0].tx_buf; + /* Second message has read array and read count */ + unsigned int readcnt = msg[1].len; + + /* Detect probing */ + if (writecnt == 1 && writearr[0] == JEDEC_RDID && readcnt == 3) { + /* We need to populate read array. */ + unsigned char *readarr = (unsigned char *)(uintptr_t)msg[1].rx_buf; + readarr[0] = 0xEF; /* WINBOND_NEX_ID */ + readarr[1] = 0x40; /* WINBOND_NEX_W25Q128_V left byte */ + readarr[2] = 0x18; /* WINBOND_NEX_W25Q128_V right byte */ + } + } + + return 0; +} + +static char *linux_spi_fgets(void *state, char *buf, int len, FILE *fp) +{ + /* Emulate reading max buffer size from sysfs. */ + const char *max_buf_size = "1048576"; + + return memcpy(buf, max_buf_size, min(len, strlen(max_buf_size) + 1)); +} + +void linux_spi_probe_lifecycle_test_success(void **state) +{ + /* + * Current implementation tests a particular path of the init procedure. + * Specifically, it is reading the buffer size from sysfs. + */ + struct io_mock_fallback_open_state linux_spi_fallback_open_state = { + .noc = 0, + .paths = { "/dev/null", NULL }, + .flags = { O_RDWR }, + }; + const struct io_mock linux_spi_io = { + .iom_fgets = linux_spi_fgets, + .iom_ioctl = linux_spi_ioctl, + .fallback_open_state = &linux_spi_fallback_open_state, + }; + + run_probe_lifecycle(state, &linux_spi_io, &programmer_linux_spi, "dev=/dev/null", "W25Q128.V"); +} +#else + SKIP_TEST(linux_spi_probe_lifecycle_test_success) +#endif /* CONFIG_LINUX_SPI */ diff --git a/tests/mediatek_i2c_spi.c b/tests/mediatek_i2c_spi.c new file mode 100644 index 00000000..d2780bc9 --- /dev/null +++ b/tests/mediatek_i2c_spi.c @@ -0,0 +1,52 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2022 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "lifecycle.h" + +#if CONFIG_MEDIATEK_I2C_SPI == 1 + +void mediatek_i2c_spi_basic_lifecycle_test_success(void **state) +{ + struct io_mock_fallback_open_state mediatek_i2c_spi_fallback_open_state = { + .noc = 0, + .paths = { "/dev/i2c-254", NULL }, + .flags = { O_RDWR }, + }; + const struct io_mock mediatek_i2c_spi_io = { + .fallback_open_state = &mediatek_i2c_spi_fallback_open_state, + }; + + run_basic_lifecycle(state, &mediatek_i2c_spi_io, &programmer_mediatek_i2c_spi, "bus=254,allow_brick=yes"); +} + +void mediatek_i2c_no_allow_brick_test_success(void **state) +{ + struct io_mock_fallback_open_state mediatek_i2c_spi_fallback_open_state = { + .noc = 0, + .paths = { "/dev/i2c-254", NULL }, + .flags = { O_RDWR }, + }; + const struct io_mock mediatek_i2c_spi_io = { + .fallback_open_state = &mediatek_i2c_spi_fallback_open_state, + }; + + run_init_error_path(state, &mediatek_i2c_spi_io, &programmer_mediatek_i2c_spi, + "bus=254", SPI_GENERIC_ERROR); +} + +#else + SKIP_TEST(mediatek_i2c_spi_basic_lifecycle_test_success) + SKIP_TEST(mediatek_i2c_no_allow_brick_test_success) +#endif /* CONFIG_MEDIATEK_I2C_SPI */ diff --git a/tests/meson.build b/tests/meson.build new file mode 100644 index 00000000..94f60e27 --- /dev/null +++ b/tests/meson.build @@ -0,0 +1,134 @@ +# This file is part of the flashrom project. +# +# Copyright 2020 Google LLC +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +test_srcs = files( + 'io_mock.c', + 'tests.c', + 'libusb_wraps.c', + 'helpers.c', + 'flashrom.c', + 'spi25.c', + 'lifecycle.c', + 'layout.c', + 'chip.c', + 'chip_wp.c', + 'selfcheck.c', + 'io_real.c', +) + +if not programmer.get('dummy').get('active') + test_srcs += programmer.get('dummy').get('srcs') +endif + +foreach p_name, p_data : programmer + test_srcs += p_data.get('test_srcs') +endforeach + +mocks = [ + '-Wl,--wrap=strdup', + '-Wl,--wrap=physunmap', + '-Wl,--wrap=physmap', + '-Wl,--wrap=pcidev_init', + '-Wl,--wrap=pcidev_readbar', + '-Wl,--wrap=spi_send_command', + '-Wl,--wrap=sio_write', + '-Wl,--wrap=sio_read', + '-Wl,--wrap=open', + '-Wl,--wrap=open64', + '-Wl,--wrap=__open64_2', + '-Wl,--wrap=ioctl', + '-Wl,--wrap=read', + '-Wl,--wrap=write', + '-Wl,--wrap=fopen', + '-Wl,--wrap=fopen64', + '-Wl,--wrap=fdopen', + '-Wl,--wrap=fwrite', + '-Wl,--wrap=fflush', + '-Wl,--wrap=stat', + '-Wl,--wrap=stat64', + '-Wl,--wrap=__xstat', + '-Wl,--wrap=__xstat64', + '-Wl,--wrap=fstat', + '-Wl,--wrap=fstat64', + '-Wl,--wrap=__fstat50', + '-Wl,--wrap=__fxstat', + '-Wl,--wrap=__fxstat64', + '-Wl,--wrap=fileno', + '-Wl,--wrap=fsync', + '-Wl,--wrap=fread', + '-Wl,--wrap=fgets', + '-Wl,--wrap=fprintf', + '-Wl,--wrap=fclose', + '-Wl,--wrap=feof', + '-Wl,--wrap=ferror', + '-Wl,--wrap=clearerr', + '-Wl,--wrap=setvbuf', + '-Wl,--wrap=rget_io_perms', + '-Wl,--wrap=OUTB', + '-Wl,--wrap=INB', + '-Wl,--wrap=OUTW', + '-Wl,--wrap=INW', + '-Wl,--wrap=OUTL', + '-Wl,--wrap=INL', + '-Wl,--wrap=usb_dev_get_by_vid_pid_number', + '-Wl,--wrap=libusb_init', + '-Wl,--wrap=libusb_set_debug', + '-Wl,--wrap=libusb_set_option', + '-Wl,--wrap=libusb_open', + '-Wl,--wrap=libusb_set_auto_detach_kernel_driver', + '-Wl,--wrap=libusb_detach_kernel_driver', + '-Wl,--wrap=libusb_attach_kernel_driver', + '-Wl,--wrap=libusb_open_device_with_vid_pid', + '-Wl,--wrap=libusb_get_device', + '-Wl,--wrap=libusb_get_device_list', + '-Wl,--wrap=libusb_free_device_list', + '-Wl,--wrap=libusb_get_bus_number', + '-Wl,--wrap=libusb_get_device_address', + '-Wl,--wrap=libusb_get_device_descriptor', + '-Wl,--wrap=libusb_get_config_descriptor', + '-Wl,--wrap=libusb_free_config_descriptor', + '-Wl,--wrap=libusb_get_configuration', + '-Wl,--wrap=libusb_set_configuration', + '-Wl,--wrap=libusb_claim_interface', + '-Wl,--wrap=libusb_control_transfer', + '-Wl,--wrap=libusb_release_interface', + '-Wl,--wrap=libusb_ref_device', + '-Wl,--wrap=libusb_unref_device', + '-Wl,--wrap=libusb_close', + '-Wl,--wrap=libusb_alloc_transfer', + '-Wl,--wrap=libusb_submit_transfer', + '-Wl,--wrap=libusb_free_transfer', + '-Wl,--wrap=libusb_handle_events_timeout', + '-Wl,--wrap=libusb_exit', + '-Wl,--gc-sections', +] + +threads_dep = dependency('threads') + +flashrom_tests = executable('flashrom_unit_tests', + test_srcs, + c_args : [ + cargs, + '-ffunction-sections', + '-fdata-sections', + '-U_FORTIFY_SOURCE', + ], + export_dynamic : true, + link_args : mocks + link_args, + dependencies : [cmocka_dep, flashrom_test_dep, threads_dep], +) +test('cmocka test flashrom', flashrom_tests) + +if get_option('llvm_cov').enabled() + run_target('llvm-cov-tests', command : ['../scripts/llvm-cov', flashrom_tests]) +endif diff --git a/tests/nicrealtek.c b/tests/nicrealtek.c new file mode 100644 index 00000000..2a7a9ffe --- /dev/null +++ b/tests/nicrealtek.c @@ -0,0 +1,33 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2021 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "lifecycle.h" + +#if CONFIG_NICREALTEK == 1 +void nicrealtek_basic_lifecycle_test_success(void **state) +{ + struct io_mock_fallback_open_state nicrealtek_fallback_open_state = { + .noc = 0, + .paths = { NULL }, + }; + const struct io_mock nicrealtek_io = { + .fallback_open_state = &nicrealtek_fallback_open_state, + }; + + run_basic_lifecycle(state, &nicrealtek_io, &programmer_nicrealtek, ""); +} +#else + SKIP_TEST(nicrealtek_basic_lifecycle_test_success) +#endif /* CONFIG_NICREALTEK */ diff --git a/tests/parade_lspcon.c b/tests/parade_lspcon.c new file mode 100644 index 00000000..980e1280 --- /dev/null +++ b/tests/parade_lspcon.c @@ -0,0 +1,138 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2022 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "lifecycle.h" + +#if CONFIG_PARADE_LSPCON == 1 + +/* Same macros as in parade_lspcon.c programmer. */ +/* FIXME(aklm): should driver register maps be defined in `include/drivers/` for sharing with tests? */ +#define REGISTER_ADDRESS 0x4a +#define SPISTATUS 0x9e +#define SPISTATUS_SECTOR_ERASE_FINISHED 0 +#define SWSPICTL 0x93 +#define SWSPICTL_ENABLE_READBACK 0x8 +#define SWSPI_RDATA 0x91 +/* Macros for test run. */ +#define DATA_TO_READ 0 +#define MAX_REG_BUF_LEN 2 + +struct parade_lspcon_io_state { + unsigned long addr; /* Address to read and write */ + uint8_t reg_buf[MAX_REG_BUF_LEN]; /* Last value written to the register address */ +}; + +static int parade_lspcon_ioctl(void *state, int fd, unsigned long request, va_list args) +{ + struct parade_lspcon_io_state *io_state = state; + if (request == I2C_SLAVE) + /* Addr is the next (and the only) argument in the parameters list for this ioctl call. */ + io_state->addr = va_arg(args, unsigned long); + + return 0; +} + +static int parade_lspcon_read(void *state, int fd, void *buf, size_t sz) +{ + struct parade_lspcon_io_state *io_state = state; + + /* + * Parade_lspcon programmer has operations over register address and + * page address. In the current emulation for basic lifecycle we need + * to emulate operations over register address. Page address can do + * nothing for now, and just return successful return value. + * + * For future, if this unit test is upgraded to run probing lifecycle, + * page address operations might need to be fully emulated. + */ + if (io_state->addr != REGISTER_ADDRESS) + return sz; + + assert_int_equal(sz, 1); + + switch (io_state->reg_buf[0]) { + case SPISTATUS: + memset(buf, SPISTATUS_SECTOR_ERASE_FINISHED, sz); + break; + case SWSPICTL: + memset(buf, SWSPICTL_ENABLE_READBACK, sz); + break; + case SWSPI_RDATA: + memset(buf, DATA_TO_READ, sz); + break; + default: + memset(buf, 0, sz); + break; + } + + return sz; +} + +static int parade_lspcon_write(void *state, int fd, const void *buf, size_t sz) +{ + struct parade_lspcon_io_state *io_state = state; + + /* + * Only register address operations are needed to be emulated for basic lifecycle. + * See also comment in `parade_lspcon_read`. + */ + if (io_state->addr != REGISTER_ADDRESS) + return sz; + + assert_true(sz <= MAX_REG_BUF_LEN); + + memcpy(io_state->reg_buf, buf, sz); + + return sz; +} + +void parade_lspcon_basic_lifecycle_test_success(void **state) +{ + struct parade_lspcon_io_state parade_lspcon_io_state = { 0 }; + struct io_mock_fallback_open_state parade_lspcon_fallback_open_state = { + .noc = 0, + .paths = { "/dev/i2c-254", NULL }, + .flags = { O_RDWR }, + }; + const struct io_mock parade_lspcon_io = { + .state = ¶de_lspcon_io_state, + .iom_ioctl = parade_lspcon_ioctl, + .iom_read = parade_lspcon_read, + .iom_write = parade_lspcon_write, + .fallback_open_state = ¶de_lspcon_fallback_open_state, + }; + + run_basic_lifecycle(state, ¶de_lspcon_io, &programmer_parade_lspcon, "bus=254,allow_brick=yes"); +} + +void parade_lspcon_no_allow_brick_test_success(void **state) +{ + struct io_mock_fallback_open_state parade_lspcon_fallback_open_state = { + .noc = 0, + .paths = { "/dev/i2c-254", NULL }, + .flags = { O_RDWR }, + }; + const struct io_mock parade_lspcon_io = { + .fallback_open_state = ¶de_lspcon_fallback_open_state, + }; + + run_init_error_path(state, ¶de_lspcon_io, &programmer_parade_lspcon, + "bus=254", SPI_GENERIC_ERROR); +} + +#else + SKIP_TEST(parade_lspcon_basic_lifecycle_test_success) + SKIP_TEST(parade_lspcon_no_allow_brick_test_success) +#endif /* CONFIG_PARADE_LSPCON */ diff --git a/tests/raiden_debug_spi.c b/tests/raiden_debug_spi.c new file mode 100644 index 00000000..5c79a909 --- /dev/null +++ b/tests/raiden_debug_spi.c @@ -0,0 +1,118 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2021 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "lifecycle.h" + +#if CONFIG_RAIDEN_DEBUG_SPI == 1 +static ssize_t raiden_debug_libusb_get_device_list(void *state, libusb_context *ctx, libusb_device ***list) +{ + *list = calloc(1, sizeof(**list)); + assert_non_null(*list); + + /* + * libusb_device is opaque type, it is tossed around between libusb functions but always + * stays opaque to the caller. + * Given that all libusb functions are mocked in tests, and raiden_debug test is mocking + * only one device, we don't need to initialise libusb_device. + */ + return 1; +} + +static void raiden_debug_libusb_free_device_list(void *state, libusb_device **list, int unref_devices) +{ + free(list); +} + +static int raiden_debug_libusb_get_device_descriptor( + void *state, libusb_device *dev, struct libusb_device_descriptor *desc) +{ + desc->idVendor = 0x18D1; /* GOOGLE_VID */ + desc->idProduct = 0; + desc->bNumConfigurations = 1; + + return 0; +} + +static int raiden_debug_libusb_get_config_descriptor( + void *state, libusb_device *dev, uint8_t config_index, struct libusb_config_descriptor **config) +{ + *config = calloc(1, sizeof(**config)); + assert_non_null(*config); + + struct libusb_endpoint_descriptor *tmp_endpoint = calloc(2, sizeof(*tmp_endpoint)); + assert_non_null(tmp_endpoint); + struct libusb_interface_descriptor *tmp_interface_desc = calloc(1, sizeof(*tmp_interface_desc)); + assert_non_null(tmp_interface_desc); + struct libusb_interface *tmp_interface = calloc(1, sizeof(*tmp_interface)); + assert_non_null(tmp_interface); + + /* in endpoint */ + tmp_endpoint[0].bEndpointAddress = 0x80; + tmp_endpoint[0].bmAttributes = 0x2; + /* out endpoint */ + tmp_endpoint[1].bEndpointAddress = 0x0; + tmp_endpoint[1].bmAttributes = 0x2; + + tmp_interface_desc->bInterfaceClass = 0xff; /* LIBUSB_CLASS_VENDOR_SPEC */ + tmp_interface_desc->bInterfaceSubClass = 0x51; /* GOOGLE_RAIDEN_SPI_SUBCLASS */ + tmp_interface_desc->bInterfaceProtocol = 0x01; /* GOOGLE_RAIDEN_SPI_PROTOCOL_V1 */ + tmp_interface_desc->bNumEndpoints = 2; /* in_endpoint and out_endpoint */ + tmp_interface_desc->endpoint = tmp_endpoint; + + tmp_interface->num_altsetting = 1; + tmp_interface->altsetting = tmp_interface_desc; + + (*config)->bConfigurationValue = 0; + (*config)->bNumInterfaces = 1; + (*config)->interface = tmp_interface; + + return 0; +} + +static void raiden_debug_libusb_free_config_descriptor(void *state, struct libusb_config_descriptor *config) +{ + free((void *)config->interface->altsetting->endpoint); + free((void *)config->interface->altsetting); + free((void *)config->interface); + free(config); +} + +void raiden_debug_basic_lifecycle_test_success(void **state) +{ + struct io_mock_fallback_open_state raiden_debug_fallback_open_state = { + .noc = 0, + .paths = { NULL }, + }; + const struct io_mock raiden_debug_io = { + .libusb_get_device_list = raiden_debug_libusb_get_device_list, + .libusb_free_device_list = raiden_debug_libusb_free_device_list, + .libusb_get_device_descriptor = raiden_debug_libusb_get_device_descriptor, + .libusb_get_config_descriptor = raiden_debug_libusb_get_config_descriptor, + .libusb_free_config_descriptor = raiden_debug_libusb_free_config_descriptor, + .fallback_open_state = &raiden_debug_fallback_open_state, + }; + + /* + * 12 is the length of programmer param string for 3-digit address. + * Address can be max 3-digit because it needs to fit into uint8_t. + */ + char raiden_debug_param[12]; + snprintf(raiden_debug_param, 12, "address=%d", USB_DEVICE_ADDRESS); + + run_basic_lifecycle(state, &raiden_debug_io, &programmer_raiden_debug_spi, raiden_debug_param); +} +#else + SKIP_TEST(raiden_debug_basic_lifecycle_test_success) +#endif /* CONFIG_RAIDEN_DEBUG_SPI */ diff --git a/tests/realtek_mst_i2c_spi.c b/tests/realtek_mst_i2c_spi.c new file mode 100644 index 00000000..753f06b9 --- /dev/null +++ b/tests/realtek_mst_i2c_spi.c @@ -0,0 +1,79 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2021 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "lifecycle.h" + +#if CONFIG_REALTEK_MST_I2C_SPI == 1 +static int realtek_mst_ioctl(void *state, int fd, unsigned long request, va_list args) +{ + assert_int_equal(fd, MOCK_FD); + assert_int_equal(request, I2C_SLAVE); + /* Only access to I2C address 0x4a is expected */ + unsigned long addr = va_arg(args, unsigned long); + assert_int_equal(addr, 0x4a); + + return 0; +} + +static int realtek_mst_read(void *state, int fd, void *buf, size_t sz) +{ + assert_int_equal(fd, MOCK_FD); + assert_int_equal(sz, 1); + return sz; +} + +static int realtek_mst_write(void *state, int fd, const void *buf, size_t sz) +{ + assert_int_equal(fd, MOCK_FD); + const LargestIntegralType accepted_sizes[] = {1, 2}; + assert_in_set(sz, accepted_sizes, ARRAY_SIZE(accepted_sizes)); + return sz; +} + +void realtek_mst_basic_lifecycle_test_success(void **state) +{ + struct io_mock_fallback_open_state realtek_mst_fallback_open_state = { + .noc = 0, + .paths = { "/dev/i2c-254", NULL }, + .flags = { O_RDWR }, + }; + const struct io_mock realtek_mst_io = { + .iom_ioctl = realtek_mst_ioctl, + .iom_read = realtek_mst_read, + .iom_write = realtek_mst_write, + .fallback_open_state = &realtek_mst_fallback_open_state, + }; + + run_basic_lifecycle(state, &realtek_mst_io, &programmer_realtek_mst_i2c_spi, "bus=254,enter_isp=0,allow_brick=yes"); +} + +void realtek_mst_no_allow_brick_test_success(void **state) +{ + struct io_mock_fallback_open_state realtek_mst_fallback_open_state = { + .noc = 0, + .paths = { "/dev/i2c-254", NULL }, + .flags = { O_RDWR }, + }; + const struct io_mock realtek_mst_io = { + .fallback_open_state = &realtek_mst_fallback_open_state, + }; + + run_init_error_path(state, &realtek_mst_io, &programmer_realtek_mst_i2c_spi, + "bus=254,enter_isp=0", SPI_GENERIC_ERROR); +} +#else + SKIP_TEST(realtek_mst_basic_lifecycle_test_success) + SKIP_TEST(realtek_mst_no_allow_brick_test_success) +#endif /* CONFIG_REALTEK_MST_I2C_SPI */ diff --git a/tests/selfcheck.c b/tests/selfcheck.c new file mode 100644 index 00000000..e235d8ef --- /dev/null +++ b/tests/selfcheck.c @@ -0,0 +1,156 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2022 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "flash.h" +#include "string.h" + +#include "include/test.h" +#include "programmer.h" +#include "tests.h" +#include <cmocka.h> + + +#define assert_table(assertion, message, index, name) \ + do { \ + if (!(assertion)) \ + fail_msg(message " for index:%zu name:%s", (index), (name) ? (name) : "unknown"); \ + } while (0) + + +void selfcheck_programmer_table(void **state) +{ + (void)state; /* unused */ + + size_t i; + for (i = 0; i < programmer_table_size; i++) { + const struct programmer_entry *const p = programmer_table[i]; + assert_table(p, "programmer entry is null", i, "unknown"); + assert_table(p->name, "programmer name is null", i, p->name); + bool type_good = false; + switch (p->type) { + case PCI: + case USB: + case OTHER: + type_good = true; + } + assert_table(type_good, "programmer type is invalid", i, p->name); + /* internal has its device list stored separately. */ + if (strcmp("internal", p->name) != 0) + assert_table(p->devs.note, "programmer devs.note is null", i, p->name); + assert_table(p->init, "programmer init is null", i, p->name); + } +} + +void selfcheck_flashchips_table(void **state) +{ + (void)state; /* unused */ + + size_t i; + assert_true(flashchips_size > 1); + assert_true(flashchips[flashchips_size - 1].name == NULL); + for (i = 0; i < flashchips_size - 1; i++) { + const struct flashchip *chip = &flashchips[i]; + assert_table(chip->vendor, "chip vendor is null", i, chip->name); + assert_table(chip->name, "chip name is null", i, chip->name); + assert_table(chip->bustype != BUS_NONE, "chip bustype is BUS_NONE", i, chip->name); + } +} + +void selfcheck_eraseblocks(void **state) +{ + (void)state; /* unused */ + + size_t chip_index; + for (chip_index = 0; chip_index < flashchips_size - 1; chip_index++) { + size_t i, j, k; + const struct flashchip *chip = &flashchips[chip_index]; + unsigned int prev_eraseblock_count = chip->total_size * 1024; + + for (k = 0; k < NUM_ERASEFUNCTIONS; k++) { + unsigned int done = 0; + struct block_eraser eraser = chip->block_erasers[k]; + unsigned int curr_eraseblock_count = 0; + + for (i = 0; i < NUM_ERASEREGIONS; i++) { + /* Blocks with zero size are bugs in flashchips.c. */ + if (eraser.eraseblocks[i].count && !eraser.eraseblocks[i].size) { + fail_msg("Flash chip %s erase function %zu region %zu has size 0", + chip->name, k, i); + } + /* Blocks with zero count are bugs in flashchips.c. */ + if (!eraser.eraseblocks[i].count && eraser.eraseblocks[i].size) { + fail_msg("Flash chip %s erase function %zu region %zu has count 0", + chip->name, k, i); + } + done += eraser.eraseblocks[i].count * eraser.eraseblocks[i].size; + curr_eraseblock_count += eraser.eraseblocks[i].count; + } + /* Empty eraseblock definition with erase function. */ + if (!done && eraser.block_erase) { + printf("Strange: Empty eraseblock definition with non-empty erase function chip %s function %zu. Not an error.\n", + chip->name, k); + } + + if (!done) + continue; + if (done != chip->total_size * 1024) { + fail_msg( + "Flash chip %s erase function %zu region walking resulted in 0x%06x bytes total, expected 0x%06x bytes.", + chip->name, k, done, chip->total_size * 1024); + assert_true(false); + } + + if (!eraser.block_erase) + continue; + /* Check if there are identical erase functions for different + * layouts. That would imply "magic" erase functions. The + * easiest way to check this is with function pointers. + */ + for (j = k + 1; j < NUM_ERASEFUNCTIONS; j++) { + if (eraser.block_erase == chip->block_erasers[j].block_erase) { + fail_msg("Flash chip %s erase function %zu and %zu are identical.", + chip->name, k, j); + } + } + if (curr_eraseblock_count > prev_eraseblock_count) { + fail_msg("Flash chip %s erase function %zu is not in order", chip->name, k); + } + prev_eraseblock_count = curr_eraseblock_count; + } + } +} + +#if CONFIG_INTERNAL == 1 +void selfcheck_board_matches_table(void **state) +{ + (void)state; /* unused */ + + size_t i; + + assert_true(board_matches_size > 0); + assert_true(board_matches[board_matches_size - 1].vendor_name == NULL); + for (i = 0; i < board_matches_size - 1; i++) { + const struct board_match *b = &board_matches[i]; + assert_table(b->vendor_name, "board vendor_name is null", i, b->board_name); + assert_table(b->board_name, "board boad_name is null", i, b->board_name); + if ((!b->first_vendor || !b->first_device || !b->second_vendor || !b->second_device) + || ((!b->lb_vendor) ^ (!b->lb_part)) || (!b->max_rom_decode_parallel && !b->enable)) + fail_msg("Board enable for %s %s is misdefined.\n", b->vendor_name, b->board_name); + } +} + +#else + SKIP_TEST(selfcheck_board_matches_table) +#endif /* CONFIG_INTERNAL */ diff --git a/tests/spi25.c b/tests/spi25.c new file mode 100644 index 00000000..87295934 --- /dev/null +++ b/tests/spi25.c @@ -0,0 +1,282 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2020 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <include/test.h> + +#include "wraps.h" +#include "tests.h" +#include "programmer.h" +#include "flashchips.h" +#include "chipdrivers.h" +#include "spi.h" + +struct flashchip mock_chip = { + .vendor = "Generic", + .name = "unknown SPI chip (RDID)", + .bustype = BUS_SPI, + .manufacture_id = GENERIC_MANUF_ID, + .model_id = GENERIC_DEVICE_ID, + .total_size = 0, + .page_size = 256, + .tested = TEST_BAD_PREW, + .probe = PROBE_SPI_RDID, + .write = NO_WRITE_FUNC, +}; + +/* + * This declaration is needed for visibility, so that wrap below could + * redirect to real function. + */ +int __real_spi_send_command(const struct flashctx *flash, + unsigned int writecnt, unsigned int readcnt, + const unsigned char *writearr, unsigned char *readarr); + +int __wrap_spi_send_command(const struct flashctx *flash, + unsigned int writecnt, unsigned int readcnt, + const unsigned char *writearr, unsigned char *readarr) +{ + if (flash->chip != &mock_chip) + /* + * Caller is some other test, redirecting to real function. + * This test is the only one which uses wrap of spi_send_command, + * all other tests use real function. + */ + return __real_spi_send_command(flash, writecnt, readcnt, writearr, readarr); + + check_expected_ptr(flash); + assert_int_equal(writecnt, mock_type(int)); + assert_int_equal(writearr[0], mock_type(int)); + + int rcnt = mock_type(int); + assert_int_equal(readcnt, rcnt); + for (int i = 0; i < rcnt; i++) + readarr[i] = i; + + return 0; +} + +static void spi_read_progress_cb(struct flashrom_flashctx *flashctx) +{ + struct flashrom_progress *progress_state = flashctx->progress_state; + uint32_t *cnt = (uint32_t *) progress_state->user_data; + assert_int_equal(0x300, progress_state->total); + switch (*cnt) { + case 0: + assert_int_equal(0x100, progress_state->current); + break; + case 1: + assert_int_equal(0x200, progress_state->current); + break; + case 2: + assert_int_equal(0x300, progress_state->current); + break; + case 3: + assert_int_equal(0x300, progress_state->current); + break; + case 4: + assert_int_equal(0x300, progress_state->current); + break; + default: + fail(); + } + (*cnt)++; +} + +void spi_read_chunked_test_success(void **state) +{ + (void) state; /* unused */ + uint8_t buf[0x400] = { 0x0 }; + uint32_t cnt = 0; + const unsigned int max_data_read = 0x100; + const unsigned int offset = 0x100; + struct registered_master mst = { + .spi.read = default_spi_read, + .spi.max_data_read = max_data_read + }; + + /* setup initial test state */ + struct flashctx flashctx = { + .chip = &mock_chip, + .mst = &mst + }; + struct flashrom_progress progress_state = { + .user_data = (void *) &cnt, + }; + flashrom_set_progress_callback(&flashctx, spi_read_progress_cb, &progress_state); + for (int i = 0; i < 4; i++) { + expect_memory(__wrap_spi_send_command, flash, + &flashctx, sizeof(flashctx)); + will_return(__wrap_spi_send_command, JEDEC_WRDI); + will_return(__wrap_spi_send_command, JEDEC_READ); + will_return(__wrap_spi_send_command, max_data_read); + } + assert_int_equal(0, spi_chip_read(&flashctx, buf, offset, sizeof(buf))); + assert_int_equal(5, cnt); +} + +void spi_write_enable_test_success(void **state) +{ + (void) state; /* unused */ + + /* setup initial test state. */ + struct flashctx flashctx = { .chip = &mock_chip }; + expect_memory(__wrap_spi_send_command, flash, + &flashctx, sizeof(flashctx)); + + will_return(__wrap_spi_send_command, JEDEC_WREN_OUTSIZE); + will_return(__wrap_spi_send_command, JEDEC_WREN); + will_return(__wrap_spi_send_command, JEDEC_WREN_INSIZE); + assert_int_equal(0, spi_write_enable(&flashctx)); +} + +void spi_write_disable_test_success(void **state) +{ + (void) state; /* unused */ + + /* setup initial test state. */ + struct flashctx flashctx = { .chip = &mock_chip }; + expect_memory(__wrap_spi_send_command, flash, + &flashctx, sizeof(flashctx)); + + will_return(__wrap_spi_send_command, JEDEC_WRDI_OUTSIZE); + will_return(__wrap_spi_send_command, JEDEC_WRDI); + will_return(__wrap_spi_send_command, JEDEC_WRDI_INSIZE); + assert_int_equal(0, spi_write_disable(&flashctx)); +} + +void probe_spi_rdid_test_success(void **state) +{ + (void) state; /* unused */ + + /* setup initial test state. */ + struct flashctx flashctx = { .chip = &mock_chip }; + expect_memory(__wrap_spi_send_command, flash, + &flashctx, sizeof(flashctx)); + + will_return(__wrap_spi_send_command, JEDEC_RDID_OUTSIZE); + will_return(__wrap_spi_send_command, JEDEC_RDID); + will_return(__wrap_spi_send_command, JEDEC_RDID_INSIZE); + assert_int_equal(0, probe_spi_rdid(&flashctx)); +} + +void probe_spi_rdid4_test_success(void **state) +{ + (void) state; /* unused */ + + /* setup initial test state. */ + struct flashctx flashctx = { .chip = &mock_chip }; + expect_memory(__wrap_spi_send_command, flash, + &flashctx, sizeof(flashctx)); + + will_return(__wrap_spi_send_command, JEDEC_RDID_OUTSIZE); + will_return(__wrap_spi_send_command, JEDEC_RDID); + will_return(__wrap_spi_send_command, JEDEC_RDID_INSIZE + 1); + assert_int_equal(0, probe_spi_rdid4(&flashctx)); +} + +void probe_spi_rems_test_success(void **state) +{ + (void) state; /* unused */ + + /* setup initial test state. */ + struct flashctx flashctx = { .chip = &mock_chip }; + expect_memory(__wrap_spi_send_command, flash, + &flashctx, sizeof(flashctx)); + + will_return(__wrap_spi_send_command, JEDEC_REMS_OUTSIZE); + will_return(__wrap_spi_send_command, JEDEC_REMS); + will_return(__wrap_spi_send_command, JEDEC_REMS_INSIZE); + assert_int_equal(0, probe_spi_rems(&flashctx)); +} + +void probe_spi_res1_test_success(void **state) +{ + (void) state; /* unused */ + + /* setup initial test state. */ + struct flashctx flashctx = { .chip = &mock_chip }; + expect_memory(__wrap_spi_send_command, flash, + &flashctx, sizeof(flashctx)); + + will_return(__wrap_spi_send_command, JEDEC_RES_OUTSIZE); + will_return(__wrap_spi_send_command, JEDEC_RES); + will_return(__wrap_spi_send_command, JEDEC_RES_INSIZE + 1); + assert_int_equal(0, probe_spi_res2(&flashctx)); +} + +void probe_spi_res2_test_success(void **state) +{ + (void) state; /* unused */ + + /* setup initial test state. */ + clear_spi_id_cache(); + struct flashctx flashctx = { .chip = &mock_chip }; + expect_memory(__wrap_spi_send_command, flash, + &flashctx, sizeof(flashctx)); + + will_return(__wrap_spi_send_command, JEDEC_RES_OUTSIZE); + will_return(__wrap_spi_send_command, JEDEC_RES); + will_return(__wrap_spi_send_command, JEDEC_RES_INSIZE + 1); + assert_int_equal(0, probe_spi_res2(&flashctx)); +} + +void probe_spi_res3_test_success(void **state) +{ + (void) state; /* unused */ + + /* setup initial test state. */ + struct flashctx flashctx = { .chip = &mock_chip }; + expect_memory(__wrap_spi_send_command, flash, + &flashctx, sizeof(flashctx)); + + will_return(__wrap_spi_send_command, JEDEC_RES_OUTSIZE); + will_return(__wrap_spi_send_command, JEDEC_RES); + will_return(__wrap_spi_send_command, JEDEC_RES_INSIZE + 2); + assert_int_equal(0, probe_spi_res3(&flashctx)); +} + +void probe_spi_at25f_test_success(void **state) +{ + (void) state; /* unused */ + + /* setup initial test state. */ + struct flashctx flashctx = { .chip = &mock_chip }; + expect_memory(__wrap_spi_send_command, flash, + &flashctx, sizeof(flashctx)); + + will_return(__wrap_spi_send_command, AT25F_RDID_OUTSIZE); + will_return(__wrap_spi_send_command, AT25F_RDID); + will_return(__wrap_spi_send_command, AT25F_RDID_INSIZE); + assert_int_equal(0, probe_spi_at25f(&flashctx)); +} + +/* spi95.c */ +void probe_spi_st95_test_success(void **state) +{ + (void) state; /* unused */ + + /* setup initial test state. */ + struct flashctx flashctx = { .chip = &mock_chip }; + expect_memory(__wrap_spi_send_command, flash, + &flashctx, sizeof(flashctx)); + + /* chip total size < 64K. */ + uint32_t rdid_outsize = ST_M95_RDID_2BA_OUTSIZE; // 16 bit address + + will_return(__wrap_spi_send_command, rdid_outsize); + will_return(__wrap_spi_send_command, ST_M95_RDID); + will_return(__wrap_spi_send_command, ST_M95_RDID_INSIZE); + assert_int_equal(0, probe_spi_st95(&flashctx)); +} diff --git a/tests/tests.c b/tests/tests.c new file mode 100644 index 00000000..8b4ad037 --- /dev/null +++ b/tests/tests.c @@ -0,0 +1,515 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2020 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <include/test.h> +#include "io_mock.h" +#include "tests.h" +#include "wraps.h" +#include "io_real.h" + +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <pthread.h> + +void *not_null(void) +{ + return (void *)MOCK_FD; +} + +/* Workaround for https://github.com/clibs/cmocka/issues/17 */ +char *__wrap_strdup(const char *s) +{ + size_t len = strlen(s) + 1; + void *new = malloc(len); + if (new == NULL) + return NULL; + return (char *)memcpy(new, s, len); +} + +void __wrap_physunmap(void *virt_addr, size_t len) +{ + LOG_ME; +} + +void *__wrap_physmap(const char *descr, uintptr_t phys_addr, size_t len) +{ + LOG_ME; + return NULL; +} + +struct pci_dev mock_pci_dev = { + .device_id = NON_ZERO, +}; + +struct pci_dev *__wrap_pcidev_init(const struct programmer_cfg *cfg, void *devs, int bar) +{ + LOG_ME; + return &mock_pci_dev; +} + +uintptr_t __wrap_pcidev_readbar(void *dev, int bar) +{ + LOG_ME; + return NON_ZERO; +} + +void __wrap_sio_write(uint16_t port, uint8_t reg, uint8_t data) +{ + LOG_ME; +} + +uint8_t __wrap_sio_read(uint16_t port, uint8_t reg) +{ + LOG_ME; + return (uint8_t)mock(); +} + +static int mock_open(const char *pathname, int flags, mode_t mode) +{ + maybe_unmock_io(pathname); + if (get_io() && get_io()->iom_open) + return get_io()->iom_open(get_io()->state, pathname, flags, mode); + + if (get_io() && get_io()->fallback_open_state) { + struct io_mock_fallback_open_state *io_state; + unsigned int open_state_flags; + + io_state = get_io()->fallback_open_state; + assert_true(io_state->noc < MAX_MOCK_OPEN); + assert_non_null(io_state->paths[io_state->noc]); + assert_string_equal(pathname, io_state->paths[io_state->noc]); + open_state_flags = io_state->flags[io_state->noc]; + assert_int_equal(flags & open_state_flags, open_state_flags); + io_state->noc++; // proceed to the next path upon next call. + } + + return MOCK_FD; +} + +int __wrap_open(const char *pathname, int flags, ...) +{ + LOG_ME; + int mode = 0; + if (flags & O_CREAT) { + va_list ap; + va_start(ap, flags); + mode = va_arg(ap, int); + va_end(ap); + } + return mock_open(pathname, flags, (mode_t) mode); +} + +int __wrap_open64(const char *pathname, int flags, ...) +{ + LOG_ME; + int mode = 0; + if (flags & O_CREAT) { + va_list ap; + va_start(ap, flags); + mode = va_arg(ap, int); + va_end(ap); + } + return mock_open(pathname, flags, (mode_t) mode); +} + +int __wrap___open64_2(const char *pathname, int flags, ...) +{ + LOG_ME; + int mode = 0; + if (flags & O_CREAT) { + va_list ap; + va_start(ap, flags); + mode = va_arg(ap, int); + va_end(ap); + } + return mock_open(pathname, flags, (mode_t) mode); +} + +int __wrap_ioctl(int fd, unsigned long int request, ...) +{ + LOG_ME; + if (get_io() && get_io()->iom_ioctl) { + va_list args; + int out; + va_start(args, request); + out = get_io()->iom_ioctl(get_io()->state, fd, request, args); + va_end(args); + return out; + } + return 0; +} + +int __wrap_write(int fd, const void *buf, size_t sz) +{ + LOG_ME; + if (get_io() && get_io()->iom_write) + return get_io()->iom_write(get_io()->state, fd, buf, sz); + return sz; +} + +int __wrap_read(int fd, void *buf, size_t sz) +{ + LOG_ME; + if (get_io() && get_io()->iom_read) + return get_io()->iom_read(get_io()->state, fd, buf, sz); + return sz; +} + +FILE *__wrap_fopen(const char *pathname, const char *mode) +{ + LOG_ME; + maybe_unmock_io(pathname); + if (get_io() && get_io()->iom_fopen) + return get_io()->iom_fopen(get_io()->state, pathname, mode); + return not_null(); +} + +FILE *__wrap_fopen64(const char *pathname, const char *mode) +{ + LOG_ME; + maybe_unmock_io(pathname); + if (get_io() && get_io()->iom_fopen) + return get_io()->iom_fopen(get_io()->state, pathname, mode); + return not_null(); +} + +FILE *__wrap_fdopen(int fd, const char *mode) +{ + LOG_ME; + if (get_io() && get_io()->iom_fdopen) + return get_io()->iom_fdopen(get_io()->state, fd, mode); + return not_null(); +} + +int __wrap_stat(const char *path, void *buf) +{ + LOG_ME; + return 0; +} + +int __wrap_stat64(const char *path, void *buf) +{ + LOG_ME; + return 0; +} + +int __wrap___xstat(const char *path, void *buf) +{ + LOG_ME; + return 0; +} + +int __wrap___xstat64(const char *path, void *buf) +{ + LOG_ME; + return 0; +} + +int __wrap_fstat(int fd, void *buf) +{ + LOG_ME; + return 0; +} + +int __wrap_fstat64(int fd, void *buf) +{ + LOG_ME; + return 0; +} + +int __wrap___fstat50(int fd, void *buf) +{ + LOG_ME; + return 0; +} + +int __wrap___fxstat(int fd, void *buf) +{ + LOG_ME; + return 0; +} + +int __wrap___fxstat64(int fd, void *buf) +{ + LOG_ME; + return 0; +} + +char *__wrap_fgets(char *buf, int len, FILE *fp) +{ + LOG_ME; + if (get_io() && get_io()->iom_fgets) + return get_io()->iom_fgets(get_io()->state, buf, len, fp); + return NULL; +} + +size_t __wrap_fread(void *ptr, size_t size, size_t nmemb, FILE *fp) +{ + LOG_ME; + if (get_io() && get_io()->iom_fread) + return get_io()->iom_fread(get_io()->state, ptr, size, nmemb, fp); + return nmemb; +} + +size_t __wrap_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *fp) +{ + LOG_ME; + if (get_io() && get_io()->iom_fwrite) + return get_io()->iom_fwrite(get_io()->state, ptr, size, nmemb, fp); + return nmemb; +} + +int __wrap_fflush(FILE *fp) +{ + LOG_ME; + return 0; +} + +int __wrap_fileno(FILE *fp) +{ + LOG_ME; + return MOCK_FD; +} + +int __wrap_fsync(int fd) +{ + LOG_ME; + return 0; +} + +int __wrap_setvbuf(FILE *fp, char *buf, int type, size_t size) +{ + LOG_ME; + return 0; +} + +int __wrap_fprintf(FILE *fp, const char *fmt, ...) +{ + LOG_ME; + if (get_io() && get_io()->iom_fprintf) { + va_list args; + int out; + va_start(args, fmt); + out = get_io()->iom_fprintf(get_io()->state, fp, fmt, args); + va_end(args); + return out; + } + return 0; +} + +int __wrap_fclose(FILE *fp) +{ + LOG_ME; + if (get_io() && get_io()->iom_fclose) + return get_io()->iom_fclose(get_io()->state, fp); + return 0; +} + +int __wrap_feof(FILE *fp) +{ + /* LOG_ME; */ + return 0; +} + +int __wrap_ferror(FILE *fp) +{ + /* LOG_ME; */ + return 0; +} +void __wrap_clearerr(FILE *fp) +{ + /* LOG_ME; */ + return; +} + +int __wrap_rget_io_perms(void) +{ + LOG_ME; + return 0; +} + +void __wrap_OUTB(unsigned char value, unsigned short port) +{ + /* LOG_ME; */ + if (get_io() && get_io()->outb) + get_io()->outb(get_io()->state, value, port); +} + +unsigned char __wrap_INB(unsigned short port) +{ + /* LOG_ME; */ + if (get_io() && get_io()->inb) + return get_io()->inb(get_io()->state, port); + return 0; +} + +void __wrap_OUTW(unsigned short value, unsigned short port) +{ + /* LOG_ME; */ + if (get_io() && get_io()->outw) + get_io()->outw(get_io()->state, value, port); +} + +unsigned short __wrap_INW(unsigned short port) +{ + /* LOG_ME; */ + if (get_io() && get_io()->inw) + return get_io()->inw(get_io()->state, port); + return 0; +} + +void __wrap_OUTL(unsigned int value, unsigned short port) +{ + /* LOG_ME; */ + if (get_io() && get_io()->outl) + get_io()->outl(get_io()->state, value, port); +} + +unsigned int __wrap_INL(unsigned short port) +{ + /* LOG_ME; */ + if (get_io() && get_io()->inl) + return get_io()->inl(get_io()->state, port); + return 0; +} + +static void *doing_nothing(void *vargp) { + return NULL; +} + +int main(int argc, char *argv[]) +{ + int ret = 0; + + if (argc > 1) + cmocka_set_test_filter(argv[1]); + + cmocka_set_message_output(CM_OUTPUT_STDOUT); + + /* + * Creating new thread which is doing nothing, to trigger __isthreaded being 1. + * This is a workaround for BSD family. In multi-threaded environment fileno + * macro is expanded into a function which is possible to mock in unit tests. + * Without this workaround, on a single-thread environment, fileno macro is + * expanded into an inline access of a private field of a file descriptor, + * which is impossible to mock. + * + * In other OSes this is just creating a thread which is doing nothing. + */ + pthread_t thread_id; + pthread_create(&thread_id, NULL, doing_nothing, NULL); + + const struct CMUnitTest helpers_tests[] = { + cmocka_unit_test(address_to_bits_test_success), + cmocka_unit_test(bitcount_test_success), + cmocka_unit_test(minmax_test_success), + cmocka_unit_test(strcat_realloc_test_success), + cmocka_unit_test(tolower_string_test_success), + cmocka_unit_test(reverse_byte_test_success), + cmocka_unit_test(reverse_bytes_test_success), + }; + ret |= cmocka_run_group_tests_name("helpers.c tests", helpers_tests, NULL, NULL); + + const struct CMUnitTest selfcheck[] = { + cmocka_unit_test(selfcheck_programmer_table), + cmocka_unit_test(selfcheck_flashchips_table), + cmocka_unit_test(selfcheck_eraseblocks), + cmocka_unit_test(selfcheck_board_matches_table), + }; + ret |= cmocka_run_group_tests_name("selfcheck.c tests", selfcheck, + NULL, NULL); + + const struct CMUnitTest flashrom_tests[] = { + cmocka_unit_test(flashbuses_to_text_test_success), + }; + ret |= cmocka_run_group_tests_name("flashrom.c tests", flashrom_tests, NULL, NULL); + + const struct CMUnitTest spi25_tests[] = { + cmocka_unit_test(spi_write_enable_test_success), + cmocka_unit_test(spi_write_disable_test_success), + cmocka_unit_test(spi_read_chunked_test_success), + cmocka_unit_test(probe_spi_rdid_test_success), + cmocka_unit_test(probe_spi_rdid4_test_success), + cmocka_unit_test(probe_spi_rems_test_success), + cmocka_unit_test(probe_spi_res1_test_success), + cmocka_unit_test(probe_spi_res2_test_success), + cmocka_unit_test(probe_spi_res3_test_success), + cmocka_unit_test(probe_spi_at25f_test_success), + cmocka_unit_test(probe_spi_st95_test_success), /* spi95.c */ + }; + ret |= cmocka_run_group_tests_name("spi25.c tests", spi25_tests, NULL, NULL); + + const struct CMUnitTest lifecycle_tests[] = { + cmocka_unit_test(dummy_basic_lifecycle_test_success), + cmocka_unit_test(dummy_probe_lifecycle_test_success), + cmocka_unit_test(dummy_probe_variable_size_test_success), + cmocka_unit_test(dummy_init_fails_unhandled_param_test_success), + cmocka_unit_test(dummy_init_success_invalid_param_test_success), + cmocka_unit_test(dummy_init_success_unhandled_param_test_success), + cmocka_unit_test(dummy_null_prog_param_test_success), + cmocka_unit_test(dummy_all_buses_test_success), + cmocka_unit_test(nicrealtek_basic_lifecycle_test_success), + cmocka_unit_test(raiden_debug_basic_lifecycle_test_success), + cmocka_unit_test(dediprog_basic_lifecycle_test_success), + cmocka_unit_test(linux_mtd_probe_lifecycle_test_success), + cmocka_unit_test(linux_spi_probe_lifecycle_test_success), + cmocka_unit_test(parade_lspcon_basic_lifecycle_test_success), + cmocka_unit_test(parade_lspcon_no_allow_brick_test_success), + cmocka_unit_test(mediatek_i2c_spi_basic_lifecycle_test_success), + cmocka_unit_test(mediatek_i2c_no_allow_brick_test_success), + cmocka_unit_test(realtek_mst_basic_lifecycle_test_success), + cmocka_unit_test(realtek_mst_no_allow_brick_test_success), + cmocka_unit_test(ch341a_spi_basic_lifecycle_test_success), + cmocka_unit_test(ch341a_spi_probe_lifecycle_test_success), + }; + ret |= cmocka_run_group_tests_name("lifecycle.c tests", lifecycle_tests, NULL, NULL); + + const struct CMUnitTest layout_tests[] = { + cmocka_unit_test(included_regions_dont_overlap_test_success), + cmocka_unit_test(included_regions_overlap_test_success), + cmocka_unit_test(region_not_included_overlap_test_success), + cmocka_unit_test(layout_pass_sanity_checks_test_success), + cmocka_unit_test(layout_region_invalid_address_test_success), + cmocka_unit_test(layout_region_invalid_range_test_success), + }; + ret |= cmocka_run_group_tests_name("layout.c tests", layout_tests, NULL, NULL); + + const struct CMUnitTest chip_tests[] = { + cmocka_unit_test(erase_chip_test_success), + cmocka_unit_test(erase_chip_with_dummyflasher_test_success), + cmocka_unit_test(read_chip_test_success), + cmocka_unit_test(read_chip_with_dummyflasher_test_success), + cmocka_unit_test(write_chip_test_success), + cmocka_unit_test(write_chip_with_dummyflasher_test_success), + cmocka_unit_test(write_nonaligned_region_with_dummyflasher_test_success), + cmocka_unit_test(verify_chip_test_success), + cmocka_unit_test(verify_chip_with_dummyflasher_test_success), + }; + ret |= cmocka_run_group_tests_name("chip.c tests", chip_tests, NULL, NULL); + + const struct CMUnitTest chip_wp_tests[] = { + cmocka_unit_test(invalid_wp_range_dummyflasher_test_success), + cmocka_unit_test(set_wp_range_dummyflasher_test_success), + cmocka_unit_test(switch_wp_mode_dummyflasher_test_success), + cmocka_unit_test(wp_init_from_status_dummyflasher_test_success), + cmocka_unit_test(full_chip_erase_with_wp_dummyflasher_test_success), + cmocka_unit_test(partial_chip_erase_with_wp_dummyflasher_test_success), + cmocka_unit_test(wp_get_register_values_and_masks), + }; + ret |= cmocka_run_group_tests_name("chip_wp.c tests", chip_wp_tests, NULL, NULL); + + return ret; +} diff --git a/tests/tests.h b/tests/tests.h new file mode 100644 index 00000000..e273e1db --- /dev/null +++ b/tests/tests.h @@ -0,0 +1,103 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2020 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef TESTS_H +#define TESTS_H + +#include <fcntl.h> + +/* helpers.c */ +void address_to_bits_test_success(void **state); +void bitcount_test_success(void **state); +void minmax_test_success(void **state); +void strcat_realloc_test_success(void **state); +void tolower_string_test_success(void **state); +void reverse_byte_test_success(void **state); +void reverse_bytes_test_success(void **state); + +/* flashrom.c */ +void flashbuses_to_text_test_success(void **state); + +/* spi25.c */ +void spi_write_enable_test_success(void **state); +void spi_write_disable_test_success(void **state); +void spi_read_chunked_test_success(void **state); +void probe_spi_rdid_test_success(void **state); +void probe_spi_rdid4_test_success(void **state); +void probe_spi_rems_test_success(void **state); +void probe_spi_res1_test_success(void **state); +void probe_spi_res2_test_success(void **state); +void probe_spi_res3_test_success(void **state); +void probe_spi_at25f_test_success(void **state); +void probe_spi_st95_test_success(void **state); /* spi95.c */ + +/* lifecycle.c */ +void dummy_basic_lifecycle_test_success(void **state); +void dummy_probe_lifecycle_test_success(void **state); +void dummy_probe_variable_size_test_success(void **state); +void dummy_init_fails_unhandled_param_test_success(void **state); +void dummy_init_success_invalid_param_test_success(void **state); +void dummy_init_success_unhandled_param_test_success(void **state); +void dummy_null_prog_param_test_success(void **state); +void dummy_all_buses_test_success(void **state); +void nicrealtek_basic_lifecycle_test_success(void **state); +void raiden_debug_basic_lifecycle_test_success(void **state); +void dediprog_basic_lifecycle_test_success(void **state); +void linux_mtd_probe_lifecycle_test_success(void **state); +void linux_spi_probe_lifecycle_test_success(void **state); +void parade_lspcon_basic_lifecycle_test_success(void **state); +void parade_lspcon_no_allow_brick_test_success(void **state); +void mediatek_i2c_spi_basic_lifecycle_test_success(void **state); +void mediatek_i2c_no_allow_brick_test_success(void **state); +void realtek_mst_basic_lifecycle_test_success(void **state); +void realtek_mst_no_allow_brick_test_success(void **state); +void ch341a_spi_basic_lifecycle_test_success(void **state); +void ch341a_spi_probe_lifecycle_test_success(void **state); + +/* layout.c */ +void included_regions_dont_overlap_test_success(void **state); +void included_regions_overlap_test_success(void **state); +void region_not_included_overlap_test_success(void **state); +void layout_pass_sanity_checks_test_success(void **state); +void layout_region_invalid_address_test_success(void **state); +void layout_region_invalid_range_test_success(void **state); + +/* chip.c */ +void erase_chip_test_success(void **state); +void erase_chip_with_dummyflasher_test_success(void **state); +void read_chip_test_success(void **state); +void read_chip_with_dummyflasher_test_success(void **state); +void write_chip_test_success(void **state); +void write_chip_with_dummyflasher_test_success(void **state); +void write_nonaligned_region_with_dummyflasher_test_success(void **state); +void verify_chip_test_success(void **state); +void verify_chip_with_dummyflasher_test_success(void **state); + +/* chip_wp.c */ +void invalid_wp_range_dummyflasher_test_success(void **state); +void set_wp_range_dummyflasher_test_success(void **state); +void switch_wp_mode_dummyflasher_test_success(void **state); +void wp_init_from_status_dummyflasher_test_success(void **state); +void full_chip_erase_with_wp_dummyflasher_test_success(void **state); +void partial_chip_erase_with_wp_dummyflasher_test_success(void **state); +void wp_get_register_values_and_masks(void **state); + +/* selfcheck.c */ +void selfcheck_programmer_table(void **state); +void selfcheck_flashchips_table(void **state); +void selfcheck_eraseblocks(void **state); +void selfcheck_board_matches_table(void **state); + +#endif /* TESTS_H */ diff --git a/tests/unittest_env.h b/tests/unittest_env.h new file mode 100644 index 00000000..9bd7509c --- /dev/null +++ b/tests/unittest_env.h @@ -0,0 +1,48 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2021 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * This header is included in all files when they are built for unit test + * environment, for all the files to be covered by dynamic memory allocation + * checks (checks for memory leaks, buffer overflows and underflows). + * + * See flashrom_test_dep in meson.build for more details. + * + * https://api.cmocka.org/group__cmocka__alloc.html + */ + +extern void* _test_malloc(const size_t size, const char* file, const int line); +extern void* _test_realloc(void *ptr, const size_t size, const char* file, const int line); +extern void* _test_calloc(const size_t number_of_elements, const size_t size, + const char* file, const int line); +extern void _test_free(void* const ptr, const char* file, const int line); + +#ifdef malloc +#undef malloc +#endif +#ifdef calloc +#undef calloc +#endif +#ifdef realloc +#undef realloc +#endif +#ifdef free +#undef free +#endif + +#define malloc(size) _test_malloc(size, __FILE__, __LINE__) +#define realloc(ptr, size) _test_realloc(ptr, size, __FILE__, __LINE__) +#define calloc(num, size) _test_calloc(num, size, __FILE__, __LINE__) +#define free(ptr) _test_free(ptr, __FILE__, __LINE__) diff --git a/tests/usb_unittests.h b/tests/usb_unittests.h new file mode 100644 index 00000000..c81e1814 --- /dev/null +++ b/tests/usb_unittests.h @@ -0,0 +1,62 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2022 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * This header provides a temporary solution to unblock build system + * work. The main goal is to remove unconditional dependency on libusb + * for unit tests. The dependency is still present, but now it is present + * only when it is needed and only when the header is present in the env. + * + * The contents of the file will be modified in a very near future. + */ + +#ifndef _USB_UNITTESTS_H_ +#define _USB_UNITTESTS_H_ + +#if CONFIG_RAIDEN_DEBUG_SPI == 1 || CONFIG_DEDIPROG == 1 || CONFIG_CH341A_SPI == 1 + +#include <libusb.h> + +#else + +struct libusb_context; +typedef struct libusb_context libusb_context; + +struct libusb_device_handle; +typedef struct libusb_device_handle libusb_device_handle; + +struct libusb_device_descriptor; +typedef struct libusb_device_descriptor libusb_device_descriptor; + +struct libusb_device; +typedef struct libusb_device libusb_device; + +struct libusb_config_descriptor; +typedef struct libusb_config_descriptor libusb_config_descriptor; + +struct libusb_interface; +typedef struct libusb_interface libusb_interface; + +struct libusb_interface_descriptor; +typedef struct libusb_interface_descriptor libusb_interface_descriptor; + +struct libusb_endpoint_descriptor; +typedef struct libusb_endpoint_descriptor libusb_endpoint_descriptor; + +struct libusb_transfer; + +#endif + +#endif /* _USB_UNITTESTS_H_ */ diff --git a/tests/wraps.h b/tests/wraps.h new file mode 100644 index 00000000..089d9927 --- /dev/null +++ b/tests/wraps.h @@ -0,0 +1,79 @@ +/* + * This file is part of the flashrom project. + * + * Copyright 2022 Google LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef WRAPS_H +#define WRAPS_H + +#include <stdio.h> +#include "flash.h" + +struct programmer_cfg; /* defined in programmer.h */ + +char *__wrap_strdup(const char *s); +void __wrap_physunmap(void *virt_addr, size_t len); +void *__wrap_physmap(const char *descr, uintptr_t phys_addr, size_t len); +struct pci_dev *__wrap_pcidev_init(const struct programmer_cfg *cfg, void *devs, int bar); +uintptr_t __wrap_pcidev_readbar(void *dev, int bar); +void __wrap_sio_write(uint16_t port, uint8_t reg, uint8_t data); +uint8_t __wrap_sio_read(uint16_t port, uint8_t reg); +int __wrap_open(const char *pathname, int flags, ...); +int __real_open(const char *pathname, int flags, ...); +int __wrap_open64(const char *pathname, int flags, ...); +int __wrap___open64_2(const char *pathname, int flags, ...); +int __wrap_ioctl(int fd, unsigned long int request, ...); +int __wrap_write(int fd, const void *buf, size_t sz); +int __wrap_read(int fd, void *buf, size_t sz); +FILE *__wrap_fopen(const char *pathname, const char *mode); +FILE *__real_fopen(const char *pathname, const char *mode); +FILE *__wrap_fopen64(const char *pathname, const char *mode); +FILE *__wrap_fdopen(int fd, const char *mode); +FILE *__real_fdopen(int fd, const char *mode); +int __wrap_stat(const char *path, void *buf); +int __wrap_stat64(const char *path, void *buf); +int __wrap___xstat(const char *path, void *buf); +int __wrap___xstat64(const char *path, void *buf); +int __wrap_fstat(int fd, void *buf); +int __wrap_fstat64(int fd, void *buf); +int __wrap___fstat50(int fd, void *buf); +int __wrap___fxstat(int fd, void *buf); +int __wrap___fxstat64(int fd, void *buf); +char *__wrap_fgets(char *buf, int len, FILE *fp); +char *__wrap___fgets_chk(char *buf, int len, FILE *fp); +size_t __wrap_fread(void *ptr, size_t size, size_t nmemb, FILE *fp); +size_t __wrap_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *fp); +size_t __real_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *fp); +int __wrap_fflush(FILE *fp); +int __wrap_fileno(FILE *fp); +int __wrap_fsync(int fd); +int __wrap_setvbuf(FILE *fp, char *buf, int type, size_t size); +int __wrap_fprintf(FILE *fp, const char *fmt, ...); +int __wrap___vfprintf_chk(FILE *fp, const char *fmt, va_list args); +int __wrap_fclose(FILE *fp); +int __real_fclose(FILE *fp); +int __wrap_feof(FILE *fp); +int __wrap_ferror(FILE *fp); +void __wrap_clearerr(FILE *fp); +int __wrap_rget_io_perms(void); +void __wrap_OUTB(unsigned char value, unsigned short port); +unsigned char __wrap_INB(unsigned short port); +void __wrap_OUTW(unsigned short value, unsigned short port); +unsigned short __wrap_INW(unsigned short port); +void __wrap_OUTL(unsigned int value, unsigned short port); +unsigned int __wrap_INL(unsigned short port); +int __wrap_spi_send_command(const struct flashctx *flash, + unsigned int writecnt, unsigned int readcnt, + const unsigned char *writearr, unsigned char *readarr); + +#endif /* WRAPS_H */ |