/* * Copyright (c) 2023 Bjarki Arge Andreasen * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include LOG_MODULE_REGISTER(test_flash); #ifdef CONFIG_BOOTLOADER_MCUBOOT #define TEST_FLASH_PART_NODE \ DT_NODELABEL(boot_partition) #else #define TEST_FLASH_PART_NODE \ DT_NODELABEL(slot1_partition) #endif #define TEST_FLASH_PART_OFFSET \ DT_REG_ADDR(TEST_FLASH_PART_NODE) #define TEST_FLASH_PART_SIZE \ DT_REG_SIZE(TEST_FLASH_PART_NODE) #define TEST_FLASH_PART_END_OFFSET \ (TEST_FLASH_PART_OFFSET + TEST_FLASH_PART_SIZE) #define TEST_FLASH_CONTROLLER_NODE \ DT_MTD_FROM_FIXED_PARTITION(TEST_FLASH_PART_NODE) static const struct device *flash_controller = DEVICE_DT_GET(TEST_FLASH_CONTROLLER_NODE); static uint8_t test_write_block[512]; static uint8_t test_read_block[512]; static void test_flash_fill_test_write_block(void) { for (size_t i = 0; i < sizeof(test_write_block); i++) { test_write_block[i] = (uint8_t)i; } } static void *test_flash_setup(void) { test_flash_fill_test_write_block(); return NULL; } static bool test_flash_mem_is_set_to(const uint8_t *data, uint8_t to, size_t size) { for (size_t i = 0; i < size; i++) { if (data[i] != to) { return false; } } return true; } static bool test_flash_is_erased(off_t offset, size_t size) { static uint8_t test_erase_buffer[99]; const struct flash_parameters *parameters = flash_get_parameters(flash_controller); size_t remaining; size_t readsize; while (offset < size) { remaining = size - (size_t)offset; readsize = MIN(sizeof(test_erase_buffer), remaining); if (flash_read(flash_controller, offset, test_erase_buffer, readsize) < 0) { return false; } if (!test_flash_mem_is_set_to(test_erase_buffer, parameters->erase_value, readsize)) { return false; } offset += (off_t)readsize; } return true; } static bool test_flash_verify_partition_is_erased(void) { return test_flash_is_erased(TEST_FLASH_PART_OFFSET, TEST_FLASH_PART_SIZE); } static int test_flash_erase_partition(void) { LOG_INF("Erasing section of size %zu at offset %zu controlled by %s", TEST_FLASH_PART_SIZE, TEST_FLASH_PART_OFFSET, flash_controller->name); return flash_erase(flash_controller, TEST_FLASH_PART_OFFSET, TEST_FLASH_PART_SIZE); } static void test_flash_before(void *f) { ARG_UNUSED(f); __ASSERT(test_flash_erase_partition() == 0, "Failed to erase partition"); __ASSERT(test_flash_verify_partition_is_erased(), "Failed to erase partition"); } static void test_flash_write_block_at_offset(off_t offset, size_t size) { zassert_ok(flash_write(flash_controller, offset, test_write_block, size), "Failed to write block at offset %zu, of size %zu", (size_t)offset, size); zassert_ok(flash_read(flash_controller, offset, test_read_block, size), "Failed to read block at offset %zu, of size %zu", (size_t)offset, size); zassert_ok(memcmp(test_write_block, test_read_block, size), "Failed to write block at offset %zu, of size %zu to page", (size_t)offset, size); } static void test_flash_write_across_page_boundary(const struct flash_pages_info *info, size_t write_block_size) { off_t page_boundary = info->start_offset; uint32_t page0_index = info->index - 1; uint32_t page1_index = info->index; off_t cross_write_start_offset = page_boundary - (off_t)write_block_size; size_t cross_write_size = write_block_size * 2; LOG_INF("Writing across page boundary at %zu, between page index %u and %u", (size_t)page_boundary, page0_index, page1_index); test_flash_write_block_at_offset(cross_write_start_offset, cross_write_size); } static bool test_flash_write_across_page_boundaries(const struct flash_pages_info *info, void *data) { size_t write_block_size = *((size_t *)data); if (info->start_offset <= TEST_FLASH_PART_OFFSET) { /* Not yet reached second page within partition */ return true; } if (info->start_offset >= TEST_FLASH_PART_END_OFFSET) { /* Reached first page after partition end */ return false; } test_flash_write_across_page_boundary(info, write_block_size); return true; } ZTEST(flash_page_layout, test_write_across_page_boundaries_in_part) { const struct flash_parameters *parameters = flash_get_parameters(flash_controller); size_t write_block_size = parameters->write_block_size; flash_page_foreach(flash_controller, test_flash_write_across_page_boundaries, &write_block_size); } static void test_flash_erase_page(const struct flash_pages_info *info) { off_t page_offset = info->start_offset; size_t page_size = info->size; size_t page_index = info->index; LOG_INF("Erasing page at %zu of size %zu with index %zu", (size_t)page_offset, page_size, page_index); zassert_ok(flash_erase(flash_controller, page_offset, page_size), "Failed to erase page"); zassert_true(test_flash_is_erased(page_offset, page_size), "Failed to erase page"); } static bool test_flash_erase_pages(const struct flash_pages_info *info, void *data) { if (info->start_offset < TEST_FLASH_PART_OFFSET) { /* Not yet reached first page within partition */ return true; } if (info->start_offset >= TEST_FLASH_PART_END_OFFSET) { /* Reached first page after partition end */ return false; } test_flash_erase_page(info); return true; } ZTEST(flash_page_layout, test_erase_single_pages_in_part) { const struct flash_parameters *parameters = flash_get_parameters(flash_controller); size_t write_block_size = parameters->write_block_size; flash_page_foreach(flash_controller, test_flash_write_across_page_boundaries, &write_block_size); flash_page_foreach(flash_controller, test_flash_erase_pages, NULL); } ZTEST_SUITE(flash_page_layout, NULL, test_flash_setup, test_flash_before, NULL, NULL);