From ff5ec942d80a34e20b5a3ca3328f7e6a55fb309b Mon Sep 17 00:00:00 2001 From: Andreas Fritiofson Date: Sat, 5 Oct 2013 00:19:08 +0200 Subject: [PATCH] arm7_9: Avoid infinite loops in bulk write dispatching Add a mandatory field in struct arm7_9_common for regular, non-optimized memory writes. Together with the existing bulk_memory_write field, this allows variants to select any combination of implementations for regular and bulk writes, without risking infinite loops from accidentally using bulk writes for implementing bulk writes. ARM 7/9 targets may now select arm7_9_memory_write_opt as their target.write_memory implementation, which will dispatch to arm7_9_common.bulk_write_memory if possible, or fallback to arm7_9_common.write_memory otherwise. To avoid loops, bulk write implementations mustn't call any other functions than arm7_9_write_memory_no_opt() to write memory; it will unconditionally call arm7_9_common.write_memory. If they fail, they should simply return error to allow the caller to fallback to regular writes. Tested on a regular ARM7TDMI only. Change-Id: Iae42a6e093e2df68c4823c927d757ae8f42ef388 Signed-off-by: Andreas Fritiofson Reviewed-on: http://openocd.zylin.com/1685 Tested-by: jenkins Reviewed-by: Sergey A. Borshch Reviewed-by: Spencer Oliver --- src/target/arm7_9_common.c | 37 +++++++++++++++++++++++++++++-------- src/target/arm7_9_common.h | 9 +++++++++ src/target/arm7tdmi.c | 1 + src/target/arm920t.c | 13 +------------ src/target/arm920t.h | 2 -- src/target/arm926ejs.c | 13 +------------ src/target/arm926ejs.h | 2 -- src/target/arm9tdmi.c | 1 + src/target/fa526.c | 3 ++- src/target/feroceon.c | 22 +++++++++++++++++----- 10 files changed, 61 insertions(+), 42 deletions(-) diff --git a/src/target/arm7_9_common.c b/src/target/arm7_9_common.c index 6eade96a..7687466f 100644 --- a/src/target/arm7_9_common.c +++ b/src/target/arm7_9_common.c @@ -2476,11 +2476,28 @@ int arm7_9_write_memory_opt(struct target *target, const uint8_t *buffer) { struct arm7_9_common *arm7_9 = target_to_arm7_9(target); + int retval; - if (size == 4 && count > 32 && arm7_9->bulk_write_memory) - return arm7_9->bulk_write_memory(target, address, count, buffer); - else - return arm7_9_write_memory(target, address, size, count, buffer); + if (size == 4 && count > 32 && arm7_9->bulk_write_memory) { + /* Attempt to do a bulk write */ + retval = arm7_9->bulk_write_memory(target, address, count, buffer); + + if (retval == ERROR_OK) + return ERROR_OK; + } + + return arm7_9->write_memory(target, address, size, count, buffer); +} + +int arm7_9_write_memory_no_opt(struct target *target, + uint32_t address, + uint32_t size, + uint32_t count, + const uint8_t *buffer) +{ + struct arm7_9_common *arm7_9 = target_to_arm7_9(target); + + return arm7_9->write_memory(target, address, size, count, buffer); } static int dcc_count; @@ -2561,8 +2578,11 @@ int arm7_9_bulk_write_memory(struct target *target, struct arm7_9_common *arm7_9 = target_to_arm7_9(target); int i; + if (address % 4 != 0) + return ERROR_TARGET_UNALIGNED_ACCESS; + if (!arm7_9->dcc_downloads) - return target_write_memory(target, address, 4, count, buffer); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; /* regrab previously allocated working_area, or allocate a new one */ if (!arm7_9->dcc_working_area) { @@ -2571,15 +2591,16 @@ int arm7_9_bulk_write_memory(struct target *target, /* make sure we have a working area */ if (target_alloc_working_area(target, 24, &arm7_9->dcc_working_area) != ERROR_OK) { LOG_INFO("no working area available, falling back to memory writes"); - return target_write_memory(target, address, 4, count, buffer); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } /* copy target instructions to target endianness */ for (i = 0; i < 6; i++) target_buffer_set_u32(target, dcc_code_buf + i*4, dcc_code[i]); - /* write DCC code to working area */ - retval = target_write_memory(target, + /* write DCC code to working area, using the non-optimized + * memory write to avoid ending up here again */ + retval = arm7_9_write_memory_no_opt(target, arm7_9->dcc_working_area->address, 4, 6, dcc_code_buf); if (retval != ERROR_OK) return retval; diff --git a/src/target/arm7_9_common.h b/src/target/arm7_9_common.h index 85e5ac04..5821e132 100644 --- a/src/target/arm7_9_common.h +++ b/src/target/arm7_9_common.h @@ -119,6 +119,13 @@ struct arm7_9_common { void (*pre_restore_context)(struct target *target); /**< Callback function called before restoring the processor context */ + /** + * Variant specific memory write function that does not dispatch to bulk_write_memory. + * Used as a fallback when bulk writes are unavailable, or for writing data needed to + * do the bulk writes. + */ + int (*write_memory)(struct target *target, uint32_t address, + uint32_t size, uint32_t count, const uint8_t *buffer); /** * Write target memory in multiples of 4 bytes, optimized for * writing large quantities of data. @@ -160,6 +167,8 @@ int arm7_9_write_memory(struct target *target, uint32_t address, uint32_t size, uint32_t count, const uint8_t *buffer); int arm7_9_write_memory_opt(struct target *target, uint32_t address, uint32_t size, uint32_t count, const uint8_t *buffer); +int arm7_9_write_memory_no_opt(struct target *target, uint32_t address, + uint32_t size, uint32_t count, const uint8_t *buffer); int arm7_9_bulk_write_memory(struct target *target, uint32_t address, uint32_t count, const uint8_t *buffer); diff --git a/src/target/arm7tdmi.c b/src/target/arm7tdmi.c index 4c7cce19..3dc00134 100644 --- a/src/target/arm7tdmi.c +++ b/src/target/arm7tdmi.c @@ -648,6 +648,7 @@ int arm7tdmi_init_arch_info(struct target *target, arm7_9->enable_single_step = arm7_9_enable_eice_step; arm7_9->disable_single_step = arm7_9_disable_eice_step; + arm7_9->write_memory = arm7_9_write_memory; arm7_9->bulk_write_memory = arm7_9_bulk_write_memory; arm7_9->post_debug_entry = NULL; diff --git a/src/target/arm920t.c b/src/target/arm920t.c index 98bd12f5..fbfa1703 100644 --- a/src/target/arm920t.c +++ b/src/target/arm920t.c @@ -740,17 +740,6 @@ int arm920t_write_memory(struct target *target, uint32_t address, return ERROR_OK; } -int arm920t_write_memory_opt(struct target *target, uint32_t address, - uint32_t size, uint32_t count, const uint8_t *buffer) -{ - struct arm7_9_common *arm7_9 = target_to_arm7_9(target); - - if (size == 4 && count > 32 && arm7_9->bulk_write_memory) - return arm7_9->bulk_write_memory(target, address, count, buffer); - else - return arm920t_write_memory(target, address, size, count, buffer); -} - /* EXPORTED to FA256 */ int arm920t_soft_reset_halt(struct target *target) { @@ -1708,7 +1697,7 @@ struct target_type arm920t_target = { .get_gdb_reg_list = arm_get_gdb_reg_list, .read_memory = arm920t_read_memory, - .write_memory = arm920t_write_memory_opt, + .write_memory = arm7_9_write_memory_opt, .read_phys_memory = arm920t_read_phys_memory, .write_phys_memory = arm920t_write_phys_memory, .mmu = arm920_mmu, diff --git a/src/target/arm920t.h b/src/target/arm920t.h index 37745130..71876a69 100644 --- a/src/target/arm920t.h +++ b/src/target/arm920t.h @@ -60,8 +60,6 @@ int arm920t_read_memory(struct target *target, uint32_t address, uint32_t size, uint32_t count, uint8_t *buffer); int arm920t_write_memory(struct target *target, uint32_t address, uint32_t size, uint32_t count, const uint8_t *buffer); -int arm920t_write_memory_opt(struct target *target, - uint32_t address, uint32_t size, uint32_t count, const uint8_t *buffer); int arm920t_post_debug_entry(struct target *target); void arm920t_pre_restore_context(struct target *target); int arm920t_get_ttb(struct target *target, uint32_t *result); diff --git a/src/target/arm926ejs.c b/src/target/arm926ejs.c index 1e0a5790..af806139 100644 --- a/src/target/arm926ejs.c +++ b/src/target/arm926ejs.c @@ -656,17 +656,6 @@ int arm926ejs_write_memory(struct target *target, uint32_t address, return retval; } -int arm926ejs_write_memory_opt(struct target *target, uint32_t address, - uint32_t size, uint32_t count, const uint8_t *buffer) -{ - struct arm7_9_common *arm7_9 = target_to_arm7_9(target); - - if (size == 4 && count > 32 && arm7_9->bulk_write_memory) - return arm7_9->bulk_write_memory(target, address, count, buffer); - else - return arm926ejs_write_memory(target, address, size, count, buffer); -} - static int arm926ejs_write_phys_memory(struct target *target, uint32_t address, uint32_t size, uint32_t count, const uint8_t *buffer) @@ -819,7 +808,7 @@ struct target_type arm926ejs_target = { .get_gdb_reg_list = arm_get_gdb_reg_list, .read_memory = arm7_9_read_memory, - .write_memory = arm926ejs_write_memory_opt, + .write_memory = arm7_9_write_memory_opt, .checksum_memory = arm_checksum_memory, .blank_check_memory = arm_blank_check_memory, diff --git a/src/target/arm926ejs.h b/src/target/arm926ejs.h index 6dde4c6f..cc19c9fc 100644 --- a/src/target/arm926ejs.h +++ b/src/target/arm926ejs.h @@ -50,8 +50,6 @@ int arm926ejs_init_arch_info(struct target *target, int arm926ejs_arch_state(struct target *target); int arm926ejs_write_memory(struct target *target, uint32_t address, uint32_t size, uint32_t count, const uint8_t *buffer); -int arm926ejs_write_memory_opt(struct target *target, - uint32_t address, uint32_t size, uint32_t count, const uint8_t *buffer); int arm926ejs_soft_reset_halt(struct target *target); extern const struct command_registration arm926ejs_command_handlers[]; diff --git a/src/target/arm9tdmi.c b/src/target/arm9tdmi.c index d93c15f7..ac07534f 100644 --- a/src/target/arm9tdmi.c +++ b/src/target/arm9tdmi.c @@ -752,6 +752,7 @@ int arm9tdmi_init_arch_info(struct target *target, arm7_9->enable_single_step = arm9tdmi_enable_single_step; arm7_9->disable_single_step = arm9tdmi_disable_single_step; + arm7_9->write_memory = arm7_9_write_memory; arm7_9->bulk_write_memory = arm7_9_bulk_write_memory; arm7_9->post_debug_entry = NULL; diff --git a/src/target/fa526.c b/src/target/fa526.c index a33b4a1c..dfb29b8e 100644 --- a/src/target/fa526.c +++ b/src/target/fa526.c @@ -284,6 +284,7 @@ static int fa526_init_arch_info_2(struct target *target, arm7_9->enable_single_step = arm9tdmi_enable_single_step; arm7_9->disable_single_step = arm9tdmi_disable_single_step; + arm7_9->write_memory = arm920t_write_memory; arm7_9->bulk_write_memory = arm7_9_bulk_write_memory; arm7_9->post_debug_entry = NULL; @@ -368,7 +369,7 @@ struct target_type fa526_target = { .get_gdb_reg_list = arm_get_gdb_reg_list, .read_memory = arm920t_read_memory, - .write_memory = arm920t_write_memory_opt, + .write_memory = arm7_9_write_memory_opt, .checksum_memory = arm_checksum_memory, .blank_check_memory = arm_blank_check_memory, diff --git a/src/target/feroceon.c b/src/target/feroceon.c index d3037c54..acaa1b3b 100644 --- a/src/target/feroceon.c +++ b/src/target/feroceon.c @@ -493,8 +493,11 @@ static int feroceon_bulk_write_memory(struct target *target, uint32_t dcc_size = sizeof(dcc_code); + if (address % 4 != 0) + return ERROR_TARGET_UNALIGNED_ACCESS; + if (!arm7_9->dcc_downloads) - return target_write_memory(target, address, 4, count, buffer); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; /* regrab previously allocated working_area, or allocate a new one */ if (!arm7_9->dcc_working_area) { @@ -503,15 +506,16 @@ static int feroceon_bulk_write_memory(struct target *target, /* make sure we have a working area */ if (target_alloc_working_area(target, dcc_size, &arm7_9->dcc_working_area) != ERROR_OK) { LOG_INFO("no working area available, falling back to memory writes"); - return target_write_memory(target, address, 4, count, buffer); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } /* copy target instructions to target endianness */ for (i = 0; i < dcc_size/4; i++) target_buffer_set_u32(target, dcc_code_buf + i*4, dcc_code[i]); - /* write DCC code to working area */ - retval = target_write_memory(target, + /* write DCC code to working area, using the non-optimized + * memory write to avoid ending up here again */ + retval = arm7_9_write_memory_no_opt(target, arm7_9->dcc_working_area->address, 4, dcc_size/4, dcc_code_buf); if (retval != ERROR_OK) return retval; @@ -627,6 +631,10 @@ static int feroceon_target_create(struct target *target, Jim_Interp *interp) arm926ejs_init_arch_info(target, arm926ejs, target->tap); feroceon_common_setup(target); + struct arm *arm = target->arch_info; + struct arm7_9_common *arm7_9 = arm->arch_info; + arm7_9->write_memory = arm926ejs_write_memory; + /* the standard ARM926 methods don't always work (don't ask...) */ arm926ejs->read_cp15 = feroceon_read_cp15; arm926ejs->write_cp15 = feroceon_write_cp15; @@ -641,6 +649,10 @@ static int dragonite_target_create(struct target *target, Jim_Interp *interp) arm966e_init_arch_info(target, arm966e, target->tap); feroceon_common_setup(target); + struct arm *arm = target->arch_info; + struct arm7_9_common *arm7_9 = arm->arch_info; + arm7_9->write_memory = arm7_9_write_memory; + return ERROR_OK; } @@ -697,7 +709,7 @@ struct target_type feroceon_target = { .get_gdb_reg_list = arm_get_gdb_reg_list, .read_memory = arm7_9_read_memory, - .write_memory = arm926ejs_write_memory_opt, + .write_memory = arm7_9_write_memory_opt, .checksum_memory = arm_checksum_memory, .blank_check_memory = arm_blank_check_memory, -- 2.39.5