blob: 7a1478564f135b6ada7f24d6d6e3331c57b0df5c [file] [log] [blame]
/* 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;
}