mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-28 02:35:28 +00:00
BUG=chrome-os-partner:19236 TEST=Pass all tests BRANCH=None Change-Id: I09276292499b94b2d4810830de51e4c63a9b7342 Signed-off-by: Vic Yang <victoryang@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/56704 Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
427 lines
9.8 KiB
C
427 lines
9.8 KiB
C
/* Copyright (c) 2012 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.
|
|
*/
|
|
|
|
/* Console commands to trigger flash host commands */
|
|
|
|
#include "board.h"
|
|
#include "console.h"
|
|
#include "ec_commands.h"
|
|
#include "flash.h"
|
|
#include "gpio.h"
|
|
#include "hooks.h"
|
|
#include "host_command.h"
|
|
#include "system.h"
|
|
#include "test_util.h"
|
|
#include "timer.h"
|
|
#include "util.h"
|
|
|
|
static int last_write_offset;
|
|
static int last_write_size;
|
|
static char last_write_data[64];
|
|
|
|
static int last_erase_offset;
|
|
static int last_erase_size;
|
|
|
|
static int mock_wp = -1;
|
|
|
|
#define TEST_STATE_CLEAN_UP (1 << 0)
|
|
#define TEST_STATE_STEP_2 (1 << 1)
|
|
#define TEST_STATE_STEP_3 (1 << 2)
|
|
#define TEST_STATE_BOOT_WP_ON (1 << 3)
|
|
#define TEST_STATE_PASSED (1 << 4)
|
|
#define TEST_STATE_FAILED (1 << 5)
|
|
|
|
#define CLEAN_UP_FLAG_PASSED TEST_STATE_PASSED
|
|
#define CLEAN_UP_FLAG_FAILED TEST_STATE_FAILED
|
|
|
|
/*****************************************************************************/
|
|
/* Emulator-only mock functions */
|
|
#ifdef EMU_BUILD
|
|
static int mock_is_running_img;
|
|
|
|
int system_unsafe_to_overwrite(uint32_t offset, uint32_t size)
|
|
{
|
|
return mock_is_running_img;
|
|
}
|
|
#endif
|
|
|
|
/*****************************************************************************/
|
|
/* Mock functions */
|
|
void host_send_response(struct host_cmd_handler_args *args)
|
|
{
|
|
/* Do nothing */
|
|
}
|
|
|
|
int flash_write(int offset, int size, const char *data)
|
|
{
|
|
last_write_offset = offset;
|
|
last_write_size = size;
|
|
memcpy(last_write_data, data, size);
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
int flash_erase(int offset, int size)
|
|
{
|
|
last_erase_offset = offset;
|
|
last_erase_size = size;
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
int gpio_get_level(enum gpio_signal signal)
|
|
{
|
|
const char *name = gpio_list[signal].name;
|
|
|
|
if (mock_wp == -1)
|
|
mock_wp = !!(system_get_scratchpad() & TEST_STATE_BOOT_WP_ON);
|
|
|
|
if (strcasecmp(name, "WP_L") == 0)
|
|
return !mock_wp;
|
|
if (strcasecmp(name, "WP") == 0)
|
|
return mock_wp;
|
|
|
|
/* Signal other than write protect. Just return 0. */
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Test utilities */
|
|
|
|
static void begin_verify(void)
|
|
{
|
|
last_write_offset = -1;
|
|
last_write_size = -1;
|
|
last_write_data[0] = '\0';
|
|
last_erase_offset = -1;
|
|
last_erase_size = -1;
|
|
}
|
|
|
|
static int verify_write(int offset, int size, const char *data)
|
|
{
|
|
int i;
|
|
|
|
if (offset != last_write_offset || size != last_write_size)
|
|
return EC_ERROR_UNKNOWN;
|
|
for (i = 0; i < size; ++i)
|
|
if (data[i] != last_write_data[i])
|
|
return EC_ERROR_UNKNOWN;
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
|
|
#define VERIFY_NO_WRITE(off, sz, d) \
|
|
do { \
|
|
begin_verify(); \
|
|
host_command_write(off, sz, d); \
|
|
TEST_ASSERT(last_write_offset == -1 && last_write_size == -1); \
|
|
} while (0)
|
|
|
|
#define VERIFY_NO_ERASE(off, sz) \
|
|
do { \
|
|
begin_verify(); \
|
|
host_command_erase(off, sz); \
|
|
TEST_ASSERT(last_erase_offset == -1 && last_erase_size == -1); \
|
|
} while (0)
|
|
|
|
#define VERIFY_WRITE(off, sz, d) \
|
|
do { \
|
|
begin_verify(); \
|
|
host_command_write(off, sz, d); \
|
|
TEST_ASSERT(verify_write(off, sz, d) == EC_SUCCESS); \
|
|
} while (0)
|
|
|
|
#define VERIFY_ERASE(off, sz) \
|
|
do { \
|
|
begin_verify(); \
|
|
host_command_erase(off, sz); \
|
|
TEST_ASSERT(last_erase_offset == off && \
|
|
last_erase_size == sz); \
|
|
} while (0)
|
|
|
|
#define SET_WP_FLAGS(m, f) \
|
|
TEST_ASSERT(host_command_protect(m, ((f) ? m : 0), \
|
|
NULL, NULL, NULL) == EC_RES_SUCCESS)
|
|
|
|
#define ASSERT_WP_FLAGS(f) \
|
|
do { \
|
|
uint32_t flags; \
|
|
TEST_ASSERT(host_command_protect(0, 0, &flags, NULL, NULL) == \
|
|
EC_RES_SUCCESS); \
|
|
TEST_ASSERT(flags & (f)); \
|
|
} while (0)
|
|
|
|
#define ASSERT_WP_NO_FLAGS(f) \
|
|
do { \
|
|
uint32_t flags; \
|
|
TEST_ASSERT(host_command_protect(0, 0, &flags, NULL, NULL) == \
|
|
EC_RES_SUCCESS); \
|
|
TEST_ASSERT((flags & (f)) == 0); \
|
|
} while (0)
|
|
|
|
int host_command_write(int offset, int size, const char *data)
|
|
{
|
|
struct host_cmd_handler_args args;
|
|
struct ec_params_flash_write params;
|
|
|
|
params.offset = offset;
|
|
params.size = size;
|
|
memcpy(params.data, data, size);
|
|
|
|
args.version = 0;
|
|
args.command = EC_CMD_FLASH_WRITE;
|
|
args.params = ¶ms;
|
|
args.params_size = sizeof(params);
|
|
args.response = NULL;
|
|
args.response_max = 0;
|
|
args.response_size = 0;
|
|
|
|
return host_command_process(&args);
|
|
}
|
|
|
|
int host_command_erase(int offset, int size)
|
|
{
|
|
struct host_cmd_handler_args args;
|
|
struct ec_params_flash_write params;
|
|
|
|
params.offset = offset;
|
|
params.size = size;
|
|
|
|
args.version = 0;
|
|
args.command = EC_CMD_FLASH_ERASE;
|
|
args.params = ¶ms;
|
|
args.params_size = sizeof(params);
|
|
args.response = NULL;
|
|
args.response_max = 0;
|
|
args.response_size = 0;
|
|
|
|
return host_command_process(&args);
|
|
}
|
|
|
|
int host_command_protect(uint32_t mask, uint32_t flags,
|
|
uint32_t *flags_out, uint32_t *valid_out,
|
|
uint32_t *writable_out)
|
|
{
|
|
struct host_cmd_handler_args args;
|
|
struct ec_params_flash_protect params;
|
|
struct ec_response_flash_protect *r =
|
|
(struct ec_response_flash_protect *)¶ms;
|
|
int res;
|
|
|
|
params.mask = mask;
|
|
params.flags = flags;
|
|
|
|
args.version = 1;
|
|
args.command = EC_CMD_FLASH_PROTECT;
|
|
args.params = ¶ms;
|
|
args.params_size = sizeof(params);
|
|
args.response = ¶ms;
|
|
args.response_max = EC_HOST_PARAM_SIZE;
|
|
args.response_size = 0;
|
|
|
|
res = host_command_process(&args);
|
|
|
|
if (res == EC_RES_SUCCESS) {
|
|
if (flags_out)
|
|
*flags_out = r->flags;
|
|
if (valid_out)
|
|
*valid_out = r->valid_flags;
|
|
if (writable_out)
|
|
*writable_out = r->writable_flags;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Tests */
|
|
static int test_overwrite_current(void)
|
|
{
|
|
uint32_t offset, size;
|
|
const char *d = "TestData0000000"; /* 16 bytes */
|
|
|
|
/* Test that we cannot overwrite current image */
|
|
if (system_get_image_copy() == SYSTEM_IMAGE_RO) {
|
|
offset = CONFIG_FW_RO_OFF;
|
|
size = CONFIG_FW_RO_SIZE;
|
|
} else {
|
|
offset = CONFIG_FW_RW_OFF;
|
|
size = CONFIG_FW_RW_SIZE;
|
|
}
|
|
|
|
#ifdef EMU_BUILD
|
|
mock_is_running_img = 1;
|
|
#endif
|
|
|
|
VERIFY_NO_ERASE(offset, sizeof(d));
|
|
VERIFY_NO_ERASE(offset + size - sizeof(d), sizeof(d));
|
|
VERIFY_NO_WRITE(offset, sizeof(d), d);
|
|
VERIFY_NO_WRITE(offset + size - sizeof(d), sizeof(d), d);
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
static int test_overwrite_other(void)
|
|
{
|
|
uint32_t offset, size;
|
|
const char *d = "TestData0000000"; /* 16 bytes */
|
|
|
|
/* Test that we can overwrite the other image */
|
|
if (system_get_image_copy() == SYSTEM_IMAGE_RW) {
|
|
offset = CONFIG_FW_RO_OFF;
|
|
size = CONFIG_FW_RO_SIZE;
|
|
} else {
|
|
offset = CONFIG_FW_RW_OFF;
|
|
size = CONFIG_FW_RW_SIZE;
|
|
}
|
|
|
|
#ifdef EMU_BUILD
|
|
mock_is_running_img = 0;
|
|
#endif
|
|
|
|
VERIFY_ERASE(offset, sizeof(d));
|
|
VERIFY_ERASE(offset + size - sizeof(d), sizeof(d));
|
|
VERIFY_WRITE(offset, sizeof(d), d);
|
|
VERIFY_WRITE(offset + size - sizeof(d), sizeof(d), d);
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
static int test_write_protect(void)
|
|
{
|
|
/* Test we can control write protect GPIO */
|
|
mock_wp = 0;
|
|
ASSERT_WP_NO_FLAGS(EC_FLASH_PROTECT_GPIO_ASSERTED);
|
|
|
|
mock_wp = 1;
|
|
ASSERT_WP_FLAGS(EC_FLASH_PROTECT_GPIO_ASSERTED);
|
|
|
|
/* Test software WP can be disable if nothing is actually protected */
|
|
SET_WP_FLAGS(EC_FLASH_PROTECT_RO_AT_BOOT, 1);
|
|
SET_WP_FLAGS(EC_FLASH_PROTECT_RO_AT_BOOT, 0);
|
|
ASSERT_WP_NO_FLAGS(EC_FLASH_PROTECT_RO_AT_BOOT);
|
|
|
|
/* Actually protect flash and test software WP cannot be disabled */
|
|
SET_WP_FLAGS(EC_FLASH_PROTECT_RO_AT_BOOT, 1);
|
|
SET_WP_FLAGS(EC_FLASH_PROTECT_ALL_NOW, 1);
|
|
SET_WP_FLAGS(EC_FLASH_PROTECT_RO_AT_BOOT, 0);
|
|
SET_WP_FLAGS(EC_FLASH_PROTECT_ALL_NOW, 0);
|
|
ASSERT_WP_FLAGS(EC_FLASH_PROTECT_ALL_NOW | EC_FLASH_PROTECT_RO_AT_BOOT);
|
|
|
|
/* Check we cannot erase anything */
|
|
TEST_ASSERT(flash_physical_erase(CONFIG_FW_RO_OFF,
|
|
CONFIG_FLASH_ERASE_SIZE) != EC_SUCCESS);
|
|
TEST_ASSERT(flash_physical_erase(CONFIG_FW_RW_OFF,
|
|
CONFIG_FLASH_ERASE_SIZE) != EC_SUCCESS);
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
static int test_boot_write_protect(void)
|
|
{
|
|
/* Check write protect state persists through reboot */
|
|
ASSERT_WP_FLAGS(EC_FLASH_PROTECT_RO_NOW | EC_FLASH_PROTECT_RO_AT_BOOT);
|
|
TEST_ASSERT(flash_physical_erase(CONFIG_FW_RO_OFF,
|
|
CONFIG_FLASH_ERASE_SIZE) != EC_SUCCESS);
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
static int test_boot_no_write_protect(void)
|
|
{
|
|
/* Check write protect is not enabled if WP GPIO is deasserted */
|
|
ASSERT_WP_NO_FLAGS(EC_FLASH_PROTECT_RO_NOW);
|
|
ASSERT_WP_FLAGS(EC_FLASH_PROTECT_RO_AT_BOOT);
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
static int clean_up(void)
|
|
{
|
|
system_set_scratchpad(0);
|
|
SET_WP_FLAGS(EC_FLASH_PROTECT_RO_AT_BOOT, 0);
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
static void reboot_to_clean_up(uint32_t flags)
|
|
{
|
|
ccprintf("Rebooting to clear WP...\n");
|
|
cflush();
|
|
system_set_scratchpad(TEST_STATE_CLEAN_UP | flags);
|
|
system_reset(SYSTEM_RESET_HARD);
|
|
}
|
|
|
|
static void reboot_to_next_step(uint32_t step)
|
|
{
|
|
ccprintf("Rebooting to next test step...\n");
|
|
cflush();
|
|
system_set_scratchpad(step);
|
|
system_reset(SYSTEM_RESET_HARD);
|
|
}
|
|
|
|
static void run_test_step1(void)
|
|
{
|
|
test_reset();
|
|
mock_wp = 0;
|
|
|
|
RUN_TEST(test_overwrite_current);
|
|
RUN_TEST(test_overwrite_other);
|
|
RUN_TEST(test_write_protect);
|
|
|
|
if (test_get_error_count())
|
|
reboot_to_clean_up(CLEAN_UP_FLAG_FAILED);
|
|
else
|
|
reboot_to_next_step(TEST_STATE_STEP_2 | TEST_STATE_BOOT_WP_ON);
|
|
}
|
|
|
|
static void run_test_step2(void)
|
|
{
|
|
RUN_TEST(test_boot_write_protect);
|
|
|
|
if (test_get_error_count())
|
|
reboot_to_clean_up(CLEAN_UP_FLAG_FAILED);
|
|
else
|
|
reboot_to_next_step(TEST_STATE_STEP_3);
|
|
}
|
|
|
|
static void run_test_step3(void)
|
|
{
|
|
RUN_TEST(test_boot_no_write_protect);
|
|
|
|
if (test_get_error_count())
|
|
reboot_to_clean_up(CLEAN_UP_FLAG_FAILED);
|
|
else
|
|
reboot_to_clean_up(CLEAN_UP_FLAG_PASSED);
|
|
}
|
|
|
|
int TaskTest(void *data)
|
|
{
|
|
uint32_t state = system_get_scratchpad();
|
|
|
|
if (state & TEST_STATE_PASSED)
|
|
ccprintf("Pass!\n");
|
|
else if (state & TEST_STATE_FAILED)
|
|
ccprintf("Fail!\n");
|
|
|
|
if (state & TEST_STATE_STEP_2)
|
|
run_test_step2();
|
|
else if (state & TEST_STATE_STEP_3)
|
|
run_test_step3();
|
|
else if (state & TEST_STATE_CLEAN_UP)
|
|
clean_up();
|
|
#ifdef EMU_BUILD
|
|
else
|
|
run_test_step1();
|
|
#endif
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
#ifndef EMU_BUILD
|
|
void run_test(void)
|
|
{
|
|
run_test_step1();
|
|
}
|
|
#endif
|