/* 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 "console.h" #include "ec_commands.h" #include "flash.h" #include "gpio.h" #include "hooks.h" #include "host_command.h" #include "system.h" #include "task.h" #include "test_util.h" #include "timer.h" #include "util.h" static int mock_wp = -1; static int mock_flash_op_fail = EC_SUCCESS; const char *testdata = "TestData00000000"; /* 16 bytes excluding NULL end */ char flash_recorded_data[128]; #define BOOT_WP_MASK TEST_STATE_MASK(TEST_STATE_STEP_2) /*****************************************************************************/ /* 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_pre_op(void) { return mock_flash_op_fail; } int gpio_get_level(enum gpio_signal signal) { if (mock_wp == -1) mock_wp = !!(test_get_state() & BOOT_WP_MASK); #if defined(CONFIG_WP_ACTIVE_HIGH) if (signal == GPIO_WP) return mock_wp; #else if (signal == GPIO_WP_L) return !mock_wp; #endif /* Signal other than write protect. Just return 0. */ return 0; } /*****************************************************************************/ /* Test utilities */ static void record_flash(int offset, int size) { memcpy(flash_recorded_data, __host_flash + offset, size); } static int verify_flash(int offset, int size) { TEST_ASSERT_ARRAY_EQ(flash_recorded_data, __host_flash + offset, size); return EC_SUCCESS; } static int verify_write(int offset, int size, const char *data) { int i; for (i = 0; i < size; ++i) if (__host_flash[offset + i] != data[i]) return EC_ERROR_UNKNOWN; return EC_SUCCESS; } static int verify_erase(int offset, int size) { int i; for (i = 0; i < size; ++i) if ((__host_flash[offset + i] & 0xff) != 0xff) return EC_ERROR_UNKNOWN; return EC_SUCCESS; } #define VERIFY_NO_WRITE(off, sz, d) \ do { \ record_flash(off, sz); \ TEST_ASSERT(host_command_write(off, sz, d) != EC_SUCCESS); \ TEST_ASSERT(verify_flash(off, sz) == EC_SUCCESS); \ } while (0) #define VERIFY_NO_ERASE(off, sz) \ do { \ record_flash(off, sz); \ TEST_ASSERT(host_command_erase(off, sz) != EC_SUCCESS); \ TEST_ASSERT(verify_flash(off, sz) == EC_SUCCESS); \ } while (0) #define VERIFY_WRITE(off, sz, d) \ do { \ TEST_ASSERT(host_command_write(off, sz, d) == EC_SUCCESS); \ TEST_ASSERT(verify_write(off, sz, d) == EC_SUCCESS); \ } while (0) #define VERIFY_ERASE(off, sz) \ do { \ TEST_ASSERT(host_command_erase(off, sz) == EC_SUCCESS); \ TEST_ASSERT(verify_erase(off, sz) == EC_SUCCESS); \ } 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) #define VERIFY_REGION_INFO(r, o, s) \ do { \ uint32_t offset, size; \ TEST_ASSERT(host_command_region_info(r, &offset, &size) == \ EC_RES_SUCCESS); \ TEST_ASSERT(offset == (o)); \ TEST_ASSERT(size == (s)); \ } while (0) int host_command_read(int offset, int size, char *out) { struct ec_params_flash_read params; params.offset = offset; params.size = size; return test_send_host_command(EC_CMD_FLASH_READ, 0, ¶ms, sizeof(params), out, size); } int host_command_write(int offset, int size, const char *data) { uint8_t buf[256]; struct ec_params_flash_write *params = (struct ec_params_flash_write *)buf; params->offset = offset; params->size = size; memcpy(params + 1, data, size); return test_send_host_command(EC_CMD_FLASH_WRITE, EC_VER_FLASH_WRITE, buf, size + sizeof(*params), NULL, 0); } int host_command_erase(int offset, int size) { struct ec_params_flash_write params; params.offset = offset; params.size = size; return test_send_host_command(EC_CMD_FLASH_ERASE, 0, ¶ms, sizeof(params), NULL, 0); } int host_command_protect(uint32_t mask, uint32_t flags, uint32_t *flags_out, uint32_t *valid_out, uint32_t *writable_out) { struct ec_params_flash_protect params; struct ec_response_flash_protect resp; int res; params.mask = mask; params.flags = flags; res = test_send_host_command(EC_CMD_FLASH_PROTECT, 1, ¶ms, sizeof(params), &resp, sizeof(resp)); if (res == EC_RES_SUCCESS) { if (flags_out) *flags_out = resp.flags; if (valid_out) *valid_out = resp.valid_flags; if (writable_out) *writable_out = resp.writable_flags; } return res; } int host_command_region_info(enum ec_flash_region reg, uint32_t *offset, uint32_t *size) { struct ec_params_flash_region_info params; struct ec_response_flash_region_info resp; int res; params.region = reg; res = test_send_host_command(EC_CMD_FLASH_REGION_INFO, 1, ¶ms, sizeof(params), &resp, sizeof(resp)); *offset = resp.offset; *size = resp.size; return res; } /*****************************************************************************/ /* Tests */ static int test_read(void) { char buf[16]; #ifdef EMU_BUILD int i; /* Fill in some numbers so they are not all 0xff */ for (i = 0; i < sizeof(buf); ++i) __host_flash[i] = i * i + i; #endif /* The first few bytes in the flash should always contain some code */ TEST_ASSERT(!flash_is_erased(0, sizeof(buf))); TEST_ASSERT(host_command_read(0, sizeof(buf), buf) == EC_RES_SUCCESS); TEST_ASSERT_ARRAY_EQ(buf, (char *)CONFIG_PROGRAM_MEMORY_BASE, sizeof(buf)); return EC_SUCCESS; } static int test_is_erased(void) { int i; #ifdef EMU_BUILD memset(__host_flash, 0xff, 1024); TEST_ASSERT(flash_is_erased(0, 1024)); for (i = 0; i < 1024; ++i) { __host_flash[i] = 0xec; TEST_ASSERT(!flash_is_erased(0, 1024)); __host_flash[i] = 0xff; } #else ccprintf("Skip. Emulator only test.\n"); #endif return EC_SUCCESS; } static int test_overwrite_current(void) { uint32_t offset, size; /* Test that we cannot overwrite current image */ if (system_get_image_copy() == SYSTEM_IMAGE_RO) { offset = CONFIG_RO_STORAGE_OFF; size = CONFIG_RO_SIZE; } else { offset = CONFIG_RW_STORAGE_OFF; size = CONFIG_RW_SIZE; } #ifdef EMU_BUILD mock_is_running_img = 1; #endif VERIFY_NO_ERASE(offset, strlen(testdata)); VERIFY_NO_ERASE(offset + size - strlen(testdata), strlen(testdata)); VERIFY_NO_WRITE(offset, strlen(testdata), testdata); VERIFY_NO_WRITE(offset + size - strlen(testdata), strlen(testdata), testdata); return EC_SUCCESS; } static int test_overwrite_other(void) { uint32_t offset, size; /* Test that we can overwrite the other image */ if (system_is_in_rw()) { offset = CONFIG_RO_STORAGE_OFF; size = CONFIG_RO_SIZE; } else { offset = CONFIG_RW_STORAGE_OFF; size = CONFIG_RW_SIZE; } #ifdef EMU_BUILD mock_is_running_img = 0; #endif VERIFY_ERASE(offset, strlen(testdata)); VERIFY_ERASE(offset + size - strlen(testdata), strlen(testdata)); VERIFY_WRITE(offset, strlen(testdata), testdata); VERIFY_WRITE(offset + size - strlen(testdata), strlen(testdata), testdata); return EC_SUCCESS; } static int test_op_failure(void) { mock_flash_op_fail = EC_ERROR_UNKNOWN; VERIFY_NO_WRITE(CONFIG_RO_STORAGE_OFF, sizeof(testdata), testdata); VERIFY_NO_WRITE(CONFIG_RW_STORAGE_OFF, sizeof(testdata), testdata); VERIFY_NO_ERASE(CONFIG_RO_STORAGE_OFF, CONFIG_FLASH_ERASE_SIZE); VERIFY_NO_ERASE(CONFIG_RW_STORAGE_OFF, CONFIG_FLASH_ERASE_SIZE); mock_flash_op_fail = EC_SUCCESS; return EC_SUCCESS; } static int test_flash_info(void) { struct ec_response_flash_info_1 resp; TEST_ASSERT(test_send_host_command(EC_CMD_FLASH_INFO, 1, NULL, 0, &resp, sizeof(resp)) == EC_RES_SUCCESS); TEST_CHECK((resp.flash_size == CONFIG_FLASH_SIZE) && (resp.write_block_size == CONFIG_FLASH_WRITE_SIZE) && (resp.erase_block_size == CONFIG_FLASH_ERASE_SIZE) && (resp.protect_block_size == CONFIG_FLASH_BANK_SIZE)); } static int test_region_info(void) { VERIFY_REGION_INFO(EC_FLASH_REGION_RO, CONFIG_EC_PROTECTED_STORAGE_OFF + CONFIG_RO_STORAGE_OFF, CONFIG_RO_SIZE); VERIFY_REGION_INFO(EC_FLASH_REGION_ACTIVE, CONFIG_EC_WRITABLE_STORAGE_OFF + CONFIG_RW_STORAGE_OFF, CONFIG_RW_SIZE); VERIFY_REGION_INFO(EC_FLASH_REGION_WP_RO, CONFIG_WP_STORAGE_OFF, CONFIG_WP_STORAGE_SIZE); VERIFY_REGION_INFO(EC_FLASH_REGION_UPDATE, CONFIG_EC_WRITABLE_STORAGE_OFF + CONFIG_RW_STORAGE_OFF, CONFIG_RW_SIZE); 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_RO_STORAGE_OFF, CONFIG_FLASH_ERASE_SIZE) != EC_SUCCESS); TEST_ASSERT(flash_physical_erase(CONFIG_RW_STORAGE_OFF, CONFIG_FLASH_ERASE_SIZE) != EC_SUCCESS); /* We should not even try to write/erase */ VERIFY_NO_ERASE(CONFIG_RO_STORAGE_OFF, CONFIG_FLASH_ERASE_SIZE); VERIFY_NO_ERASE(CONFIG_RW_STORAGE_OFF, CONFIG_FLASH_ERASE_SIZE); VERIFY_NO_WRITE(CONFIG_RO_STORAGE_OFF, sizeof(testdata), testdata); VERIFY_NO_WRITE(CONFIG_RW_STORAGE_OFF, sizeof(testdata), testdata); 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_RO_STORAGE_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; } int test_clean_up_(void) { SET_WP_FLAGS(EC_FLASH_PROTECT_RO_AT_BOOT, 0); return EC_SUCCESS; } void test_clean_up(void) { test_clean_up_(); /* Throw away return value */ } static void run_test_step1(void) { test_reset(); mock_wp = 0; RUN_TEST(test_read); RUN_TEST(test_is_erased); RUN_TEST(test_overwrite_current); RUN_TEST(test_overwrite_other); RUN_TEST(test_op_failure); RUN_TEST(test_flash_info); RUN_TEST(test_region_info); RUN_TEST(test_write_protect); if (test_get_error_count()) test_reboot_to_next_step(TEST_STATE_FAILED); else test_reboot_to_next_step(TEST_STATE_STEP_2); } static void run_test_step2(void) { RUN_TEST(test_boot_write_protect); if (test_get_error_count()) test_reboot_to_next_step(TEST_STATE_FAILED); else test_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()) test_reboot_to_next_step(TEST_STATE_FAILED); else test_reboot_to_next_step(TEST_STATE_PASSED); } void test_run_step(uint32_t state) { if (state & TEST_STATE_MASK(TEST_STATE_STEP_1)) run_test_step1(); else if (state & TEST_STATE_MASK(TEST_STATE_STEP_2)) run_test_step2(); else if (state & TEST_STATE_MASK(TEST_STATE_STEP_3)) run_test_step3(); } int task_test(void *data) { test_run_multistep(); return EC_SUCCESS; } void run_test(void) { msleep(30); /* Wait for TASK_ID_TEST to initialize */ task_wake(TASK_ID_TEST); }