summaryrefslogtreecommitdiff
path: root/tests/vb2_host_nvdata_flashrom_tests.c
diff options
context:
space:
mode:
authorJack Rosenthal <jrosenth@chromium.org>2020-05-27 08:59:18 -0600
committerCommit Bot <commit-bot@chromium.org>2020-06-09 16:30:57 +0000
commitc95ea3f2cb7b9aaf64d8b2d7ede13783191e0b73 (patch)
treed7ca19aa9b9df2490c035cbf2b423d30da73e9c3 /tests/vb2_host_nvdata_flashrom_tests.c
parent1978c84e51cc8eb99f55167c6b5b6cc85439f832 (diff)
downloadvboot-c95ea3f2cb7b9aaf64d8b2d7ede13783191e0b73.tar.gz
crossystem: add functions to read and write VBNV via flashrom
This will replace the usage of "mosys nvram vboot {read,write}" on x86 platforms, and all ARM platforms except veyron (chromebooks only) and nyan_kitty (which use VBNV storage in the ChromeOS EC, deprecated for new platforms). These affected ARM devices will be going AUE sometime this summer, and we can expect to remove the mosys usage in crossystem later this year. The code to find the active VBNV in SPI flash was modeled to match the logic in mosys (see mosys/lib/vbnv/vbnv_flash.c). BUG=chromium:1032351,chromium:1030473,chromium:789276 BRANCH=none TEST=provided unit tests Signed-off-by: Jack Rosenthal <jrosenth@chromium.org> Change-Id: I4f42af2f9a6b0703302635f8d8ebb2d7599d9847 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/2218889
Diffstat (limited to 'tests/vb2_host_nvdata_flashrom_tests.c')
-rw-r--r--tests/vb2_host_nvdata_flashrom_tests.c279
1 files changed, 279 insertions, 0 deletions
diff --git a/tests/vb2_host_nvdata_flashrom_tests.c b/tests/vb2_host_nvdata_flashrom_tests.c
new file mode 100644
index 00000000..7a147856
--- /dev/null
+++ b/tests/vb2_host_nvdata_flashrom_tests.c
@@ -0,0 +1,279 @@
+/* Copyright 2020 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Tests for crossystem flashrom-based nvdata functions.
+ */
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "2api.h"
+#include "2common.h"
+#include "2constants.h"
+#include "2nvstorage.h"
+#include "2return_codes.h"
+#include "crossystem_vbnv.h"
+#include "flashrom.h"
+#include "test_common.h"
+
+/* Mocked flashrom only supports host programmer, and RW_NVRAM
+ region. */
+static void assert_mock_params(const char *programmer, const char *region)
+{
+ TEST_STR_EQ(programmer, FLASHROM_PROGRAMMER_INTERNAL_AP,
+ "Using internal AP programmer");
+ TEST_STR_EQ(region, "RW_NVRAM", "Using NVRAM region");
+}
+
+static bool mock_flashrom_fail;
+
+/* To support both 16-byte and 64-byte nvdata with the same fake
+ eeprom, we can size the flash chip to be 16x64. So, for 16-byte
+ nvdata, this is a flash chip with 64 entries, and for 64-byte
+ nvdata, this is a flash chip with 16 entries. */
+static uint8_t fake_flash_region[VB2_NVDATA_SIZE * VB2_NVDATA_SIZE_V2];
+static int fake_flash_entry_count;
+
+static const uint8_t test_nvdata_16b[] = {
+ 0x60, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x4e,
+ 0x00, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0x5e,
+};
+
+static const uint8_t test_nvdata2_16b[] = {
+ 0x60, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x4c,
+ 0x00, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0x78,
+};
+
+static void reset_test_data(struct vb2_context *ctx, int nvdata_size)
+{
+ /* Initialize the context value. */
+ ctx->flags = 0;
+
+ switch (nvdata_size) {
+ case VB2_NVDATA_SIZE:
+ fake_flash_entry_count = VB2_NVDATA_SIZE_V2;
+ memcpy(ctx->nvdata, test_nvdata_16b, sizeof(test_nvdata_16b));
+ break;
+ case VB2_NVDATA_SIZE_V2:
+ ctx->flags |= VB2_CONTEXT_NVDATA_V2;
+ fake_flash_entry_count = VB2_NVDATA_SIZE;
+ /* TODO: create some test data for 64-byte nvdata and
+ put it here. Right now, this only tests 16-byte
+ nvdata. */
+ break;
+ default:
+ /* This is not valid. */
+ TEST_TRUE(false, "Test failed, invalid nvdata size");
+ fake_flash_entry_count = 0;
+ break;
+ }
+
+ /* Clear the fake flash chip. */
+ memset(fake_flash_region, 0xff, sizeof(fake_flash_region));
+
+ /* Flashrom succeeds unless the test says otherwise. */
+ mock_flashrom_fail = false;
+}
+
+/* Mocked flashrom_read for tests. */
+vb2_error_t flashrom_read(const char *programmer, const char *region,
+ uint8_t **data_out, uint32_t *size_out)
+{
+ if (mock_flashrom_fail) {
+ *data_out = NULL;
+ *size_out = 0;
+ return VB2_ERROR_FLASHROM;
+ }
+
+ assert_mock_params(programmer, region);
+
+ *data_out = malloc(sizeof(fake_flash_region));
+ *size_out = sizeof(fake_flash_region);
+ memcpy(*data_out, fake_flash_region, sizeof(fake_flash_region));
+ return VB2_SUCCESS;
+}
+
+/* Mocked flashrom_write for tests. */
+vb2_error_t flashrom_write(const char *programmer, const char *region,
+ uint8_t *data, uint32_t data_size)
+{
+ if (mock_flashrom_fail)
+ return VB2_ERROR_FLASHROM;
+
+ assert_mock_params(programmer, region);
+
+ TEST_EQ(data_size, sizeof(fake_flash_region),
+ "The flash size is correct");
+ memcpy(fake_flash_region, data, data_size);
+ return VB2_SUCCESS;
+}
+
+static void test_read_ok_beginning(void)
+{
+ struct vb2_context ctx;
+
+ reset_test_data(&ctx, sizeof(test_nvdata_16b));
+ memcpy(fake_flash_region, test_nvdata2_16b, sizeof(test_nvdata2_16b));
+
+ TEST_EQ(vb2_read_nv_storage_flashrom(&ctx), 0,
+ "Reading storage succeeds");
+ TEST_EQ(memcmp(ctx.nvdata, test_nvdata2_16b, sizeof(test_nvdata2_16b)),
+ 0, "The nvdata in the vb2_context was updated from flash");
+}
+
+static void test_read_ok_2ndentry(void)
+{
+ struct vb2_context ctx;
+
+ reset_test_data(&ctx, sizeof(test_nvdata_16b));
+ memcpy(fake_flash_region, test_nvdata_16b, sizeof(test_nvdata_16b));
+ memcpy(fake_flash_region + VB2_NVDATA_SIZE, test_nvdata2_16b,
+ sizeof(test_nvdata2_16b));
+
+ TEST_EQ(vb2_read_nv_storage_flashrom(&ctx), 0,
+ "Reading storage succeeds");
+ TEST_EQ(memcmp(ctx.nvdata, test_nvdata2_16b, sizeof(test_nvdata2_16b)),
+ 0, "The nvdata in the vb2_context was updated from flash");
+}
+
+static void test_read_ok_full(void)
+{
+ struct vb2_context ctx;
+
+ reset_test_data(&ctx, sizeof(test_nvdata_16b));
+
+ for (int entry = 0; entry < fake_flash_entry_count - 2; entry++)
+ memcpy(fake_flash_region + (entry * VB2_NVDATA_SIZE),
+ test_nvdata_16b, sizeof(test_nvdata_16b));
+
+ memcpy(fake_flash_region +
+ ((fake_flash_entry_count - 2) * VB2_NVDATA_SIZE),
+ test_nvdata2_16b, sizeof(test_nvdata2_16b));
+
+ TEST_EQ(vb2_read_nv_storage_flashrom(&ctx), 0,
+ "Reading storage succeeds");
+ TEST_EQ(memcmp(ctx.nvdata, test_nvdata2_16b, sizeof(test_nvdata2_16b)),
+ 0, "The nvdata in the vb2_context was updated from flash");
+}
+
+static void test_read_fail_uninitialized(void)
+{
+ struct vb2_context ctx;
+
+ reset_test_data(&ctx, sizeof(test_nvdata_16b));
+
+ TEST_NEQ(vb2_read_nv_storage_flashrom(&ctx), 0,
+ "Reading storage fails when flash is erased");
+}
+
+static void test_read_fail_flashrom(void)
+{
+ struct vb2_context ctx;
+
+ reset_test_data(&ctx, sizeof(test_nvdata_16b));
+ memcpy(fake_flash_region, test_nvdata_16b, sizeof(test_nvdata_16b));
+ mock_flashrom_fail = true;
+
+ TEST_NEQ(vb2_read_nv_storage_flashrom(&ctx), 0,
+ "Reading storage fails when flashrom fails");
+}
+
+static void test_write_ok_beginning(void)
+{
+ struct vb2_context ctx;
+
+ reset_test_data(&ctx, sizeof(test_nvdata_16b));
+ memcpy(fake_flash_region, test_nvdata_16b, sizeof(test_nvdata_16b));
+ memcpy(ctx.nvdata, test_nvdata2_16b, sizeof(test_nvdata2_16b));
+
+ TEST_EQ(vb2_write_nv_storage_flashrom(&ctx), 0,
+ "Writing storage succeeds");
+ TEST_EQ(memcmp(fake_flash_region + VB2_NVDATA_SIZE, test_nvdata2_16b,
+ sizeof(test_nvdata2_16b)),
+ 0, "The flash was updated with a new entry");
+}
+
+static void test_write_ok_2ndentry(void)
+{
+ struct vb2_context ctx;
+
+ reset_test_data(&ctx, sizeof(test_nvdata_16b));
+ memcpy(fake_flash_region, test_nvdata_16b, sizeof(test_nvdata_16b));
+ memcpy(fake_flash_region + VB2_NVDATA_SIZE, test_nvdata_16b,
+ sizeof(test_nvdata_16b));
+ memcpy(ctx.nvdata, test_nvdata2_16b, sizeof(test_nvdata2_16b));
+
+ TEST_EQ(vb2_write_nv_storage_flashrom(&ctx), 0,
+ "Writing storage succeeds");
+ TEST_EQ(memcmp(fake_flash_region + (2 * VB2_NVDATA_SIZE),
+ test_nvdata2_16b, sizeof(test_nvdata2_16b)),
+ 0, "The flash was updated with a new entry");
+}
+
+static void test_write_ok_full(void)
+{
+ struct vb2_context ctx;
+ uint8_t expected_flash[sizeof(fake_flash_region)];
+
+ reset_test_data(&ctx, sizeof(test_nvdata_16b));
+
+ for (int entry = 0; entry < fake_flash_entry_count - 1; entry++)
+ memcpy(fake_flash_region + (entry * VB2_NVDATA_SIZE),
+ test_nvdata_16b, sizeof(test_nvdata_16b));
+
+ memcpy(expected_flash, test_nvdata2_16b, sizeof(test_nvdata2_16b));
+ memset(expected_flash + VB2_NVDATA_SIZE, 0xff,
+ sizeof(expected_flash) - VB2_NVDATA_SIZE);
+ memcpy(ctx.nvdata, test_nvdata2_16b, sizeof(test_nvdata2_16b));
+
+ TEST_EQ(vb2_write_nv_storage_flashrom(&ctx), 0,
+ "Writing storage succeeds");
+ TEST_EQ(memcmp(fake_flash_region, expected_flash,
+ sizeof(expected_flash)),
+ 0,
+ "The flash was erased and the new entry was placed at "
+ "the beginning");
+}
+
+static void test_write_fail_uninitialized(void)
+{
+ struct vb2_context ctx;
+
+ reset_test_data(&ctx, sizeof(test_nvdata_16b));
+
+ TEST_NEQ(vb2_write_nv_storage_flashrom(&ctx), 0,
+ "Writing storage fails when the flash is erased");
+}
+
+static void test_write_fail_flashrom(void)
+{
+ struct vb2_context ctx;
+
+ reset_test_data(&ctx, sizeof(test_nvdata_16b));
+ memcpy(fake_flash_region, test_nvdata_16b, sizeof(test_nvdata_16b));
+ mock_flashrom_fail = true;
+
+ TEST_NEQ(vb2_write_nv_storage_flashrom(&ctx), 0,
+ "Writing storage fails when flashrom fails");
+}
+
+int main(int argc, char *argv[])
+{
+ test_read_ok_beginning();
+ test_read_ok_2ndentry();
+ test_read_ok_full();
+ test_read_fail_uninitialized();
+ test_read_fail_flashrom();
+ test_write_ok_beginning();
+ test_write_ok_2ndentry();
+ test_write_ok_full();
+ test_write_fail_uninitialized();
+ test_write_fail_flashrom();
+
+ return gTestSuccess ? 0 : 255;
+}