aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/ch341a_spi.c136
-rw-r--r--tests/chip.c615
-rw-r--r--tests/chip_wp.c394
-rw-r--r--tests/dediprog.c58
-rw-r--r--tests/dummyflasher.c153
-rw-r--r--tests/flashrom.c71
-rw-r--r--tests/helpers.c79
-rw-r--r--tests/include/test.h45
-rw-r--r--tests/io_mock.c31
-rw-r--r--tests/io_mock.h130
-rw-r--r--tests/io_real.c78
-rw-r--r--tests/io_real.h24
-rw-r--r--tests/layout.c209
-rw-r--r--tests/libusb_wraps.c222
-rw-r--r--tests/libusb_wraps.h57
-rw-r--r--tests/lifecycle.c98
-rw-r--r--tests/lifecycle.h39
-rw-r--r--tests/linux_mtd.c94
-rw-r--r--tests/linux_spi.c72
-rw-r--r--tests/mediatek_i2c_spi.c52
-rw-r--r--tests/meson.build134
-rw-r--r--tests/nicrealtek.c33
-rw-r--r--tests/parade_lspcon.c138
-rw-r--r--tests/raiden_debug_spi.c118
-rw-r--r--tests/realtek_mst_i2c_spi.c79
-rw-r--r--tests/selfcheck.c156
-rw-r--r--tests/spi25.c282
-rw-r--r--tests/tests.c515
-rw-r--r--tests/tests.h103
-rw-r--r--tests/unittest_env.h48
-rw-r--r--tests/usb_unittests.h62
-rw-r--r--tests/wraps.h79
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 = &registered_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 = &registered_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 = &parade_lspcon_io_state,
+ .iom_ioctl = parade_lspcon_ioctl,
+ .iom_read = parade_lspcon_read,
+ .iom_write = parade_lspcon_write,
+ .fallback_open_state = &parade_lspcon_fallback_open_state,
+ };
+
+ run_basic_lifecycle(state, &parade_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 = &parade_lspcon_fallback_open_state,
+ };
+
+ run_init_error_path(state, &parade_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 */