target armv7m: multi-block erase check
[fw/openocd] / src / target / armv7m.c
index 696f85cb2721b05d677f0c170bf27ac567b9c1b2..7b7893f6415bd5d498537a017ee865df1ceebbe6 100644 (file)
@@ -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(&reg_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(&reg_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(&reg_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);
+
+       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;
 
-       if (retval == ERROR_OK)
-               blocks->result = buf_get_u32(reg_params[2].value, 0, 32);
+       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(&reg_params[0]);
        destroy_reg_param(&reg_params[1]);
-       destroy_reg_param(&reg_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)