diff --git a/contrib/loaders/erase_check/Makefile b/contrib/loaders/erase_check/Makefile index 427fa0c07..1a0fd9e3f 100644 --- a/contrib/loaders/erase_check/Makefile +++ b/contrib/loaders/erase_check/Makefile @@ -12,7 +12,7 @@ STM8_OBJCOPY ?= $(STM8_CROSS_COMPILE)objcopy STM8_AFLAGS = -arm: armv4_5_erase_check.inc armv7m_erase_check.inc armv7m_0_erase_check.inc +arm: armv4_5_erase_check.inc armv7m_erase_check.inc armv4_5_%.elf: armv4_5_%.s $(ARM_AS) $(ARM_AFLAGS) $< -o $@ diff --git a/contrib/loaders/erase_check/armv7m_0_erase_check.inc b/contrib/loaders/erase_check/armv7m_0_erase_check.inc deleted file mode 100644 index 76115ec17..000000000 --- a/contrib/loaders/erase_check/armv7m_0_erase_check.inc +++ /dev/null @@ -1,2 +0,0 @@ -/* Autogenerated with ../../../src/helper/bin2char.sh */ -0x03,0x78,0x01,0x30,0x1a,0x43,0x01,0x39,0xfa,0xd1,0x00,0xbe, diff --git a/contrib/loaders/erase_check/armv7m_0_erase_check.s b/contrib/loaders/erase_check/armv7m_0_erase_check.s deleted file mode 100644 index 6b1e92a85..000000000 --- a/contrib/loaders/erase_check/armv7m_0_erase_check.s +++ /dev/null @@ -1,45 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2014 by Jeff Ciesielski * - * jeffciesielski@gmail.com * - * * - * Based on the armv7m erase checker by: * - * Copyright (C) 2010 by Spencer Oliver * - * spen@spen-soft.co.uk * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - ***************************************************************************/ - -/* - parameters: - r0 - address in - r1 - byte count - r2 - mask - result out -*/ - - .text - .syntax unified - .cpu cortex-m0 - .thumb - .thumb_func - - .align 2 - -loop: - ldrb r3, [r0] - adds r0, #1 - orrs r2, r2, r3 - subs r1, r1, #1 - bne loop -end: - bkpt #0 - - .end diff --git a/contrib/loaders/erase_check/armv7m_erase_check.inc b/contrib/loaders/erase_check/armv7m_erase_check.inc index 1fe25cd51..4ee96e19e 100644 --- a/contrib/loaders/erase_check/armv7m_erase_check.inc +++ b/contrib/loaders/erase_check/armv7m_erase_check.inc @@ -1,2 +1,4 @@ /* Autogenerated with ../../../src/helper/bin2char.sh */ -0x03,0x78,0x01,0x30,0x1a,0x40,0x01,0x39,0xfa,0xd1,0x00,0xbe, +0x02,0x68,0x12,0x42,0x0d,0xd0,0x43,0x68,0x1c,0x68,0x04,0x33,0x8c,0x42,0x05,0xd1, +0x01,0x3a,0xf9,0xd1,0x01,0x24,0x04,0x60,0x08,0x30,0xf1,0xe7,0x00,0x24,0xfa,0xe7, +0x00,0x00,0x00,0xbe, diff --git a/contrib/loaders/erase_check/armv7m_erase_check.s b/contrib/loaders/erase_check/armv7m_erase_check.s index 886e3e280..3303c8778 100644 --- a/contrib/loaders/erase_check/armv7m_erase_check.s +++ b/contrib/loaders/erase_check/armv7m_erase_check.s @@ -20,9 +20,8 @@ /* parameters: - r0 - address in - r1 - byte count - r2 - mask - result out + r0 - pointer to struct { uint32_t size_in_result_out, uint32_t addr } + r1 - value to check */ .text @@ -33,13 +32,42 @@ .align 2 -loop: - ldrb r3, [r0] - adds r0, #1 - ands r2, r2, r3 - subs r1, r1, #1 - bne loop -end: +BLOCK_SIZE_RESULT = 0 +BLOCK_ADDRESS = 4 +SIZEOF_STRUCT_BLOCK = 8 + +start: +block_loop: + ldr r2, [r0, #BLOCK_SIZE_RESULT] /* get size */ + tst r2, r2 + beq done + + ldr r3, [r0, #BLOCK_ADDRESS] /* get address */ + +word_loop: + ldr r4, [r3] /* read word */ + adds r3, #4 + + cmp r4, r1 + bne not_erased + + subs r2, #1 + bne word_loop + + movs r4, #1 /* block is erased */ +save_result: + str r4, [r0, #BLOCK_SIZE_RESULT] + adds r0, #SIZEOF_STRUCT_BLOCK + b block_loop + +not_erased: + movs r4, #0 + b save_result + +/* Avoid padding at .text segment end. Otherwise exit point check fails. */ + .skip ( . - start + 2) & 2, 0 + +done: bkpt #0 .end diff --git a/src/target/armv7m.c b/src/target/armv7m.c index 696f85cb2..7b7893f64 100644 --- a/src/target/armv7m.c +++ b/src/target/armv7m.c @@ -731,34 +731,23 @@ cleanup: return retval; } -/** Checks whether a memory region is erased. */ +/** Checks an array of memory regions whether they are erased. */ int armv7m_blank_check_memory(struct target *target, struct target_memory_check_block *blocks, int num_blocks, uint8_t erased_value) { struct working_area *erase_check_algorithm; - struct reg_param reg_params[3]; + struct working_area *erase_check_params; + struct reg_param reg_params[2]; struct armv7m_algorithm armv7m_info; - const uint8_t *code; - uint32_t code_size; int retval; + static bool timed_out; + static const uint8_t erase_check_code[] = { #include "../../contrib/loaders/erase_check/armv7m_erase_check.inc" }; - static const uint8_t zero_erase_check_code[] = { -#include "../../contrib/loaders/erase_check/armv7m_0_erase_check.inc" - }; - switch (erased_value) { - case 0x00: - code = zero_erase_check_code; - code_size = sizeof(zero_erase_check_code); - break; - case 0xff: - default: - code = erase_check_code; - code_size = sizeof(erase_check_code); - } + const uint32_t code_size = sizeof(erase_check_code); /* make sure we have a working area */ if (target_alloc_working_area(target, code_size, @@ -766,46 +755,113 @@ int armv7m_blank_check_memory(struct target *target, return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; retval = target_write_buffer(target, erase_check_algorithm->address, - code_size, code); + code_size, erase_check_code); if (retval != ERROR_OK) - goto cleanup; + goto cleanup1; + + /* prepare blocks array for algo */ + struct algo_block { + union { + uint32_t size; + uint32_t result; + }; + uint32_t address; + }; + + uint32_t avail = target_get_working_area_avail(target); + int blocks_to_check = avail / sizeof(struct algo_block) - 1; + if (num_blocks < blocks_to_check) + blocks_to_check = num_blocks; + + struct algo_block *params = malloc((blocks_to_check+1)*sizeof(struct algo_block)); + if (params == NULL) { + retval = ERROR_FAIL; + goto cleanup1; + } + + int i; + uint32_t total_size = 0; + for (i = 0; i < blocks_to_check; i++) { + total_size += blocks[i].size; + target_buffer_set_u32(target, (uint8_t *)&(params[i].size), + blocks[i].size / sizeof(uint32_t)); + target_buffer_set_u32(target, (uint8_t *)&(params[i].address), + blocks[i].address); + } + target_buffer_set_u32(target, (uint8_t *)&(params[blocks_to_check].size), 0); + + uint32_t param_size = (blocks_to_check + 1) * sizeof(struct algo_block); + if (target_alloc_working_area(target, param_size, + &erase_check_params) != ERROR_OK) { + retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + goto cleanup2; + } + + retval = target_write_buffer(target, erase_check_params->address, + param_size, (uint8_t *)params); + if (retval != ERROR_OK) + goto cleanup3; + + uint32_t erased_word = erased_value | (erased_value << 8) + | (erased_value << 16) | (erased_value << 24); + + LOG_DEBUG("Starting erase check of %d blocks, parameters@" + TARGET_ADDR_FMT, blocks_to_check, erase_check_params->address); armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; armv7m_info.core_mode = ARM_MODE_THREAD; init_reg_param(®_params[0], "r0", 32, PARAM_OUT); - buf_set_u32(reg_params[0].value, 0, 32, blocks->address); + buf_set_u32(reg_params[0].value, 0, 32, erase_check_params->address); init_reg_param(®_params[1], "r1", 32, PARAM_OUT); - buf_set_u32(reg_params[1].value, 0, 32, blocks->size); + buf_set_u32(reg_params[1].value, 0, 32, erased_word); - init_reg_param(®_params[2], "r2", 32, PARAM_IN_OUT); - buf_set_u32(reg_params[2].value, 0, 32, erased_value); + /* assume CPU clk at least 1 MHz */ + int timeout = (timed_out ? 30000 : 2000) + total_size * 3 / 1000; retval = target_run_algorithm(target, - 0, - NULL, - 3, - reg_params, - erase_check_algorithm->address, - erase_check_algorithm->address + (code_size - 2), - 10000, - &armv7m_info); + 0, NULL, + ARRAY_SIZE(reg_params), reg_params, + erase_check_algorithm->address, + erase_check_algorithm->address + (code_size - 2), + timeout, + &armv7m_info); - if (retval == ERROR_OK) - blocks->result = buf_get_u32(reg_params[2].value, 0, 32); + timed_out = retval == ERROR_TARGET_TIMEOUT; + if (retval != ERROR_OK && !timed_out) + goto cleanup4; + retval = target_read_buffer(target, erase_check_params->address, + param_size, (uint8_t *)params); + if (retval != ERROR_OK) + goto cleanup4; + + for (i = 0; i < blocks_to_check; i++) { + uint32_t result = target_buffer_get_u32(target, + (uint8_t *)&(params[i].result)); + if (result != 0 && result != 1) + break; + + blocks[i].result = result; + } + if (i && timed_out) + LOG_INFO("Slow CPU clock: %d blocks checked, %d remain. Continuing...", i, num_blocks-i); + + retval = i; /* return number of blocks really checked */ + +cleanup4: destroy_reg_param(®_params[0]); destroy_reg_param(®_params[1]); - destroy_reg_param(®_params[2]); -cleanup: +cleanup3: + target_free_working_area(target, erase_check_params); +cleanup2: + free(params); +cleanup1: target_free_working_area(target, erase_check_algorithm); - if (retval != ERROR_OK) - return retval; - - return 1; /* only one block checked */ + return retval; } int armv7m_maybe_skip_bkpt_inst(struct target *target, bool *inst_found)