diff --git a/src/include/target.h b/src/include/target.h index 5b4c9a15346..2f3d1198842 100644 --- a/src/include/target.h +++ b/src/include/target.h @@ -71,6 +71,7 @@ bool target_flash_erase(target_s *target, target_addr_t addr, size_t len); bool target_flash_write(target_s *target, target_addr_t dest, const void *src, size_t len); bool target_flash_complete(target_s *target); bool target_flash_mass_erase(target_s *target); +bool target_flash_blank_check(target_s *target); /* Register access functions */ size_t target_regs_size(target_s *target); diff --git a/src/target/target.c b/src/target/target.c index 156326d5af7..d9a9f3b46db 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -45,11 +45,13 @@ target_s *target_list = NULL; static bool target_cmd_mass_erase(target_s *target, int argc, const char **argv); static bool target_cmd_range_erase(target_s *target, int argc, const char **argv); +static bool target_cmd_blank_check(target_s *target, int argc, const char **argv); static bool target_cmd_redirect_output(target_s *target, int argc, const char **argv); const command_s target_cmd_list[] = { {"erase_mass", target_cmd_mass_erase, "Erase whole device Flash"}, {"erase_range", target_cmd_range_erase, "Erase a range of memory on a device"}, + {"blank_check", target_cmd_blank_check, "Blank-check device Flash"}, {"redirect_stdout", target_cmd_redirect_output, "Redirect semihosting output to aux USB serial"}, {NULL, NULL, NULL}, }; @@ -516,6 +518,16 @@ static bool target_cmd_range_erase(target_s *const target, const int argc, const return target_flash_erase(target, addr, length); } +static bool target_cmd_blank_check(target_s *const target, const int argc, const char **const argv) +{ + (void)argc; + (void)argv; + gdb_out("Blank-checking device Flash: "); + const bool result = target_flash_blank_check(target); + gdb_out("done\n"); + return result; +} + static bool target_cmd_redirect_output(target_s *target, int argc, const char **argv) { if (argc == 1) { diff --git a/src/target/target_flash.c b/src/target/target_flash.c index ad7dcef69b9..84eb6dadcb4 100644 --- a/src/target/target_flash.c +++ b/src/target/target_flash.c @@ -382,3 +382,57 @@ bool target_flash_complete(target_s *target) target_exit_flash_mode(target); return result; } + +static bool flash_blank_check(target_flash_s *flash, target_addr_t src, size_t len, target_addr_t *mismatch) +{ + bool result = true; /* Catch false returns with &= */ + target_s *target = flash->t; + platform_timeout_s timeout; + platform_timeout_set(&timeout, 500); + + for (size_t offset = 0U; offset < len; offset += flash->writebufsize) { + /* Fetch chunk into sector buffer */ + target_mem32_read(target, flash->buf, src + offset, flash->writebufsize); + + /* Compare bytewise with erased value */ + const uint8_t erased = flash->erased; + for (size_t i = 0; i < flash->writebufsize; i++) { + if (flash->buf[i] != erased) { + *mismatch = src + i; + return false; + } + } + target_print_progress(&timeout); + } + return result; +} + +bool target_flash_blank_check(target_s *target) +{ + if (!target->flash) + return false; + + bool result = true; + target_addr_t mismatch = 0; + + for (target_flash_s *flash = target->flash; flash; flash = flash->next) { + if (!flash->buf && !flash_buffer_alloc(flash)) + return false; + + const target_addr_t local_end = flash->start + flash->length; + for (target_addr_t local_start = flash->start; local_start < local_end; local_start += flash->blocksize) { + result = flash_blank_check(flash, local_start, flash->blocksize, &mismatch); + if (!result) + tc_printf(target, "Has data at 0x%08" PRIx32 "\n", mismatch); + else + tc_printf(target, "Blank 0x%08" PRIx32 "+%" PRIu32 "\n", local_start, (uint32_t)flash->blocksize); + } + /* Free the operation buffer */ + if (flash->buf) { + free(flash->buf); + flash->buf = NULL; + } + } + + return result; +}