| // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later |
| /* Copyright 2017-2018 IBM Corp. */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdint.h> |
| #include <string.h> |
| #include <stdarg.h> |
| |
| #include <libflash/libflash.h> |
| #include <libflash/libflash-priv.h> |
| |
| #include "stubs.h" |
| #include "mbox-server.h" |
| |
| #define zalloc(n) calloc(1, n) |
| #define __unused __attribute__((unused)) |
| |
| #undef pr_fmt |
| |
| void mbox_init(void) |
| { |
| } |
| |
| #include "../libflash.c" |
| #include "../mbox-flash.c" |
| #include "../ecc.c" |
| #include "../blocklevel.c" |
| |
| #undef pr_fmt |
| #define pr_fmt(fmt) "MBOX-PROXY: " fmt |
| |
| /* client interface */ |
| |
| #include "../../include/lpc-mbox.h" |
| |
| #define ERR(...) FL_DBG(__VA_ARGS__) |
| |
| static int run_flash_test(struct blocklevel_device *bl) |
| { |
| struct mbox_flash_data *mbox_flash; |
| char hello[] = "Hello World"; |
| uint32_t erase_granule; |
| uint64_t total_size; |
| const char *name; |
| uint16_t *test; |
| char *tmp; |
| int i, rc; |
| |
| mbox_flash = container_of(bl, struct mbox_flash_data, bl); |
| |
| /* |
| * Do something first so that if it has been reset it does that |
| * before we check versions |
| */ |
| rc = blocklevel_get_info(bl, &name, &total_size, &erase_granule); |
| if (rc) { |
| ERR("blocklevel_get_info() failed with err %d\n", rc); |
| return 1; |
| } |
| if (total_size != mbox_server_total_size()) { |
| ERR("Total flash size is incorrect: 0x%08lx vs 0x%08x\n", |
| total_size, mbox_server_total_size()); |
| return 1; |
| } |
| if (erase_granule != mbox_server_erase_granule()) { |
| ERR("Erase granule is incorrect 0x%08x vs 0x%08x\n", |
| erase_granule, mbox_server_erase_granule()); |
| return 1; |
| } |
| |
| |
| /* Sanity check that mbox_flash has inited correctly */ |
| if (mbox_flash->version != mbox_server_version()) { |
| ERR("MBOX Flash didn't agree with the server version\n"); |
| return 1; |
| } |
| if (mbox_flash->version == 1 && mbox_flash->shift != 12) { |
| ERR("MBOX Flash version 1 isn't using a 4K shift\n"); |
| return 1; |
| } |
| |
| mbox_server_memset(0xff); |
| |
| test = calloc(erase_granule * 20, 1); |
| |
| /* Make up a test pattern */ |
| for (i = 0; i < erase_granule * 10; i++) |
| test[i] = i; |
| |
| /* Write 64k of stuff at 0 and at 128k */ |
| printf("Writing test patterns...\n"); |
| rc = blocklevel_write(bl, 0, test, erase_granule * 10); |
| if (rc) { |
| ERR("blocklevel_write(0, erase_granule * 10) failed with err %d\n", rc); |
| return 1; |
| } |
| rc = blocklevel_write(bl, erase_granule * 20, test, erase_granule * 10); |
| if (rc) { |
| ERR("blocklevel_write(0x20000, 0x10000) failed with err %d\n", rc); |
| return 1; |
| } |
| |
| if (mbox_server_memcmp(0, test, erase_granule * 10)) { |
| ERR("Test pattern mismatch !\n"); |
| return 1; |
| } |
| |
| /* Write "Hello world" straddling the 64k boundary */ |
| printf("Writing test string...\n"); |
| rc = blocklevel_write(bl, (erase_granule * 10) - 8, hello, sizeof(hello)); |
| if (rc) { |
| ERR("blocklevel_write(0xfffc, %s, %lu) failed with err %d\n", |
| hello, sizeof(hello), rc); |
| return 1; |
| } |
| |
| /* Check result */ |
| if (mbox_server_memcmp((erase_granule * 10) - 8, hello, sizeof(hello))) { |
| ERR("Test string mismatch!\n"); |
| return 1; |
| } |
| |
| /* Erase granule is something but never 0x50, this shouldn't succeed */ |
| rc = blocklevel_erase(bl, 0, 0x50); |
| if (!rc) { |
| ERR("blocklevel_erase(0, 0x50) didn't fail!\n"); |
| return 1; |
| } |
| |
| /* Check it didn't silently erase */ |
| if (mbox_server_memcmp(0, test, (erase_granule * 10) - 8)) { |
| ERR("Test pattern mismatch !\n"); |
| return 1; |
| } |
| |
| /* |
| * For v1 protocol this should NOT call MARK_WRITE_ERASED! |
| * The server MARK_WRITE_ERASED will call exit(1) if it gets a |
| * MARK_WRITE_ERASED and version == 1 |
| */ |
| rc = blocklevel_erase(bl, 0, erase_granule); |
| if (rc) { |
| ERR("blocklevel_erase(0, erase_granule) failed with err %d\n", rc); |
| return 1; |
| } |
| |
| /* |
| * Version 1 doesn't specify that the buffer actually becomes 0xff |
| * It is up to the daemon to do what it wants really - there are |
| * implementations that do nothing but writes to the same region |
| * work fine |
| */ |
| |
| /* This check is important for v2 */ |
| /* Check stuff got erased */ |
| tmp = malloc(erase_granule * 2); |
| if (!tmp) { |
| ERR("malloc failed\n"); |
| return 1; |
| } |
| if (mbox_server_version() > 1) { |
| memset(tmp, 0xff, erase_granule); |
| if (mbox_server_memcmp(0, tmp, erase_granule)) { |
| ERR("Buffer not erased\n"); |
| rc = 1; |
| goto out; |
| } |
| } |
| |
| /* Read beyond the end of flash */ |
| rc = blocklevel_read(bl, total_size, tmp, 0x1000); |
| if (!rc) { |
| ERR("blocklevel_read(total_size, 0x1000) (read beyond the end) succeeded\n"); |
| goto out; |
| } |
| |
| /* Test some simple write/read cases, avoid first page */ |
| rc = blocklevel_write(bl, erase_granule * 2, test, erase_granule / 2); |
| if (rc) { |
| ERR("blocklevel_write(erase_granule, erase_granule / 2) failed with err %d\n", rc); |
| goto out; |
| } |
| rc = blocklevel_write(bl, erase_granule * 2 + erase_granule / 2, test, erase_granule / 2); |
| if (rc) { |
| ERR("blocklevel_write(erase_granule * 2 + erase_granule / 2, erase_granule) failed with err %d\n", rc); |
| goto out; |
| } |
| |
| rc = mbox_server_memcmp(erase_granule * 2, test, erase_granule / 2); |
| if (rc) { |
| ERR("%s:%d mbox_server_memcmp miscompare\n", __FILE__, __LINE__); |
| goto out; |
| } |
| rc = mbox_server_memcmp(erase_granule * 2 + erase_granule / 2, test, erase_granule / 2); |
| if (rc) { |
| ERR("%s:%d mbox_server_memcmp miscompare\n", __FILE__, __LINE__); |
| goto out; |
| } |
| |
| /* Great so the writes made it, can we read them back? Do it in |
| * four small reads */ |
| for (i = 0; i < 4; i++) { |
| rc = blocklevel_read(bl, erase_granule * 2 + (i * erase_granule / 4), tmp + (i * erase_granule / 4), erase_granule / 4); |
| if (rc) { |
| ERR("blocklevel_read(0x%08x, erase_granule / 4) failed with err %d\n", |
| 2 * erase_granule + (i * erase_granule / 4), rc); |
| goto out; |
| } |
| } |
| rc = memcmp(test, tmp, erase_granule / 2); |
| if (rc) { |
| ERR("%s:%d read back miscompare\n", __FILE__, __LINE__); |
| goto out; |
| } |
| rc = memcmp(test, tmp + erase_granule / 2, erase_granule / 2); |
| if (rc) { |
| ERR("%s:%d read back miscompare\n", __FILE__, __LINE__); |
| goto out; |
| } |
| |
| /* |
| * Make sure we didn't corrupt other stuff, also make sure one |
| * blocklevel call will understand how to read from two windows |
| */ |
| for (i = 3; i < 9; i = i + 2) { |
| printf("i:%d erase: 0x%08x\n", i, erase_granule); |
| rc = blocklevel_read(bl, i * erase_granule, tmp, 2 * erase_granule); |
| if (rc) { |
| ERR("blocklevel_read(0x%08x, 2 * erase_granule) failed with err: %d\n", i * erase_granule, rc); |
| goto out; |
| } |
| rc = memcmp(((char *)test) + (i * erase_granule), tmp, 2 * erase_granule); |
| if (rc) { |
| ERR("%s:%d read back miscompare (pos: 0x%08x)\n", __FILE__, __LINE__, i * erase_granule); |
| goto out; |
| } |
| } |
| |
| srand(1); |
| /* |
| * Try to jump around the place doing a tonne of small reads. |
| * Worth doing the same with writes TODO |
| */ |
| #ifdef __STRICT_TEST__ |
| #define TEST_LOOPS 1000 |
| #else |
| #define TEST_LOOPS 100 |
| #endif |
| for (i = 0; i < TEST_LOOPS; i++) { |
| int r = rand(); |
| |
| printf("Loop %d of %d\n", i, TEST_LOOPS); |
| /* Avoid reading too far, just skip it */ |
| if ((r % erase_granule * 10) + (r % erase_granule * 2) > erase_granule * 10) |
| continue; |
| |
| rc = blocklevel_read(bl, erase_granule * 20 + (r % erase_granule * 10), tmp, r % erase_granule * 2); |
| if (rc) { |
| ERR("blocklevel_read(0x%08x, 0x%08x) failed with err %d\n", 0x20000 + (r % 0x100000), r % 0x2000, rc); |
| goto out; |
| } |
| rc = memcmp(((char *)test) + (r % erase_granule * 10), tmp, r % erase_granule * 2); |
| if (rc) { |
| ERR("%s:%d read back miscompare (pos: 0x%08x)\n", __FILE__, __LINE__, 0x20000 + (r % 0x10000)); |
| goto out; |
| } |
| } |
| out: |
| free(tmp); |
| return rc; |
| } |
| |
| int main(void) |
| { |
| struct blocklevel_device *bl; |
| int rc; |
| |
| libflash_debug = true; |
| |
| mbox_server_init(); |
| |
| #ifdef __STRICT_TEST__ |
| printf("Found __STRICT_TEST__, this may take time time.\n"); |
| #else |
| printf("__STRICT_TEST__ not found, use make strict-check for a more\n"); |
| printf("thorough test, it will take significantly longer.\n"); |
| #endif |
| |
| printf("Doing mbox-flash V1 tests\n"); |
| |
| /* run test */ |
| mbox_flash_init(&bl); |
| rc = run_flash_test(bl); |
| if (rc) |
| goto out; |
| /* |
| * Trick mbox-flash into thinking there was a reboot so we can |
| * switch to v2 |
| */ |
| |
| printf("Doing mbox-flash V2 tests\n"); |
| |
| mbox_server_reset(2, 12); |
| |
| /* Do all the tests again */ |
| rc = run_flash_test(bl); |
| if (rc) |
| goto out; |
| |
| mbox_server_reset(2, 17); |
| |
| /* Do all the tests again */ |
| rc = run_flash_test(bl); |
| if (rc) |
| goto out; |
| |
| |
| printf("Doing mbox-flash V3 tests\n"); |
| |
| mbox_server_reset(3, 20); |
| |
| /* Do all the tests again */ |
| rc = run_flash_test(bl); |
| |
| |
| out: |
| mbox_flash_exit(bl); |
| |
| mbox_server_destroy(); |
| |
| return rc; |
| } |