diff --git a/src/include/target.h b/src/include/target.h index 182c745ce35..f742427112f 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 3e4b2f3693e..56eeaa6cabb 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}, }; @@ -553,6 +555,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 0cd19e6cb70..fe7374b753a 100644 --- a/src/target/target_flash.c +++ b/src/target/target_flash.c @@ -383,3 +383,60 @@ 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); + + /* Pick smaller of: len = option bytes (8), writebufsize (1024), len = flash page (8192) */ + const size_t chunksize = flash->writebufsize < len ? flash->writebufsize : len; + + for (size_t offset = 0U; offset < len; offset += chunksize) { + /* Fetch chunk into sector buffer */ + target_mem32_read(target, flash->buf, src + offset, chunksize); + + /* Compare bytewise with erased value */ + const uint8_t erased = flash->erased; + for (size_t i = 0; i < chunksize; 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; +}