X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=blobdiff_plain;f=src%2Ftarget%2Ftarget.c;h=b8e4c2ff9ed937dc0720b2f98634a3c6923aa074;hp=f8326ea72b0800362140dab39aaccb2bac1b2ce4;hb=a7f320e919125abe4ee05a7f3d0b61c341333c9f;hpb=4db24acb931304355f59560789c5d92b99fb2962 diff --git a/src/target/target.c b/src/target/target.c index f8326ea72b..b8e4c2ff9e 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -20,6 +20,9 @@ * Copyright (C) ST-Ericsson SA 2011 * * michel.jaouen@stericsson.com : smp minimum support * * * + * Copyright (C) 2011 Andreas Fritiofson * + * andreas.fritiofson@gmail.com * + * * * 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 * @@ -153,8 +156,6 @@ static const char *target_strerror_safe(int err) } static const Jim_Nvp nvp_target_event[] = { - { .value = TARGET_EVENT_OLD_gdb_program_config , .name = "old-gdb_program_config" }, - { .value = TARGET_EVENT_OLD_pre_resume , .name = "old-pre_resume" }, { .value = TARGET_EVENT_GDB_HALT, .name = "gdb-halt" }, { .value = TARGET_EVENT_HALTED, .name = "halted" }, @@ -165,10 +166,7 @@ static const Jim_Nvp nvp_target_event[] = { { .name = "gdb-start", .value = TARGET_EVENT_GDB_START }, { .name = "gdb-end", .value = TARGET_EVENT_GDB_END }, - /* historical name */ - - { .value = TARGET_EVENT_RESET_START, .name = "reset-start" }, - + { .value = TARGET_EVENT_RESET_START, .name = "reset-start" }, { .value = TARGET_EVENT_RESET_ASSERT_PRE, .name = "reset-assert-pre" }, { .value = TARGET_EVENT_RESET_ASSERT, .name = "reset-assert" }, { .value = TARGET_EVENT_RESET_ASSERT_POST, .name = "reset-assert-post" }, @@ -196,10 +194,6 @@ static const Jim_Nvp nvp_target_event[] = { { .value = TARGET_EVENT_GDB_FLASH_ERASE_START, .name = "gdb-flash-erase-start" }, { .value = TARGET_EVENT_GDB_FLASH_ERASE_END , .name = "gdb-flash-erase-end" }, - { .value = TARGET_EVENT_RESUME_START, .name = "resume-start" }, - { .value = TARGET_EVENT_RESUMED , .name = "resume-ok" }, - { .value = TARGET_EVENT_RESUME_END , .name = "resume-end" }, - { .name = NULL, .value = -1 } }; @@ -524,6 +518,8 @@ int target_resume(struct target *target, int current, uint32_t address, int hand return ERROR_FAIL; } + target_call_event_callbacks(target, TARGET_EVENT_RESUME_START); + /* note that resume *must* be asynchronous. The CPU can halt before * we poll. The CPU can even halt at the current PC as a result of * a software breakpoint being inserted by (a bug?) the application. @@ -532,6 +528,8 @@ int target_resume(struct target *target, int current, uint32_t address, int hand if (retval != ERROR_OK) return retval; + target_call_event_callbacks(target, TARGET_EVENT_RESUME_END); + return retval; } @@ -613,9 +611,17 @@ static int jtag_enable_callback(enum jtag_event event, void *priv) return ERROR_OK; jtag_unregister_event_callback(jtag_enable_callback, target); - return target_examine_one(target); -} + target_call_event_callbacks(target, TARGET_EVENT_EXAMINE_START); + + int retval = target_examine_one(target); + if (retval != ERROR_OK) + return retval; + + target_call_event_callbacks(target, TARGET_EVENT_EXAMINE_END); + + return retval; +} /* Targets that correctly implement init + examine, i.e. * no communication with target during init: @@ -634,12 +640,18 @@ int target_examine(void) target); continue; } + + target_call_event_callbacks(target, TARGET_EVENT_EXAMINE_START); + retval = target_examine_one(target); if (retval != ERROR_OK) return retval; + + target_call_event_callbacks(target, TARGET_EVENT_EXAMINE_END); } return retval; } + const char *target_type_name(struct target *target) { return target->type->name; @@ -793,6 +805,137 @@ done: return retval; } +/** + * Executes a target-specific native code algorithm in the target. + * It differs from target_run_algorithm in that the algorithm is asynchronous. + * Because of this it requires an compliant algorithm: + * see contrib/loaders/flash/stm32f1x.S for example. + * + * @param target used to run the algorithm + */ + +int target_run_flash_async_algorithm(struct target *target, + uint8_t *buffer, uint32_t count, int block_size, + int num_mem_params, struct mem_param *mem_params, + int num_reg_params, struct reg_param *reg_params, + uint32_t buffer_start, uint32_t buffer_size, + uint32_t entry_point, uint32_t exit_point, void *arch_info) +{ + int retval; + + /* Set up working area. First word is write pointer, second word is read pointer, + * rest is fifo data area. */ + uint32_t wp_addr = buffer_start; + uint32_t rp_addr = buffer_start + 4; + uint32_t fifo_start_addr = buffer_start + 8; + uint32_t fifo_end_addr = buffer_start + buffer_size; + + uint32_t wp = fifo_start_addr; + uint32_t rp = fifo_start_addr; + + /* validate block_size is 2^n */ + assert(!block_size || !(block_size & (block_size - 1))); + + retval = target_write_u32(target, wp_addr, wp); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, rp_addr, rp); + if (retval != ERROR_OK) + return retval; + + /* Start up algorithm on target and let it idle while writing the first chunk */ + retval = target_start_algorithm(target, num_mem_params, mem_params, + num_reg_params, reg_params, + entry_point, + exit_point, + arch_info); + + if (retval != ERROR_OK) { + LOG_ERROR("error starting target flash write algorithm"); + return retval; + } + + while (count > 0) { + + retval = target_read_u32(target, rp_addr, &rp); + if (retval != ERROR_OK) { + LOG_ERROR("failed to get read pointer"); + break; + } + + LOG_DEBUG("count 0x%" PRIx32 " wp 0x%" PRIx32 " rp 0x%" PRIx32, count, wp, rp); + + if (rp == 0) { + LOG_ERROR("flash write algorithm aborted by target"); + retval = ERROR_FLASH_OPERATION_FAILED; + break; + } + + if ((rp & (block_size - 1)) || rp < fifo_start_addr || rp >= fifo_end_addr) { + LOG_ERROR("corrupted fifo read pointer 0x%" PRIx32, rp); + break; + } + + /* Count the number of bytes available in the fifo without + * crossing the wrap around. Make sure to not fill it completely, + * because that would make wp == rp and that's the empty condition. */ + uint32_t thisrun_bytes; + if (rp > wp) + thisrun_bytes = rp - wp - block_size; + else if (rp > fifo_start_addr) + thisrun_bytes = fifo_end_addr - wp; + else + thisrun_bytes = fifo_end_addr - wp - block_size; + + if (thisrun_bytes == 0) { + /* Throttle polling a bit if transfer is (much) faster than flash + * programming. The exact delay shouldn't matter as long as it's + * less than buffer size / flash speed. This is very unlikely to + * run when using high latency connections such as USB. */ + alive_sleep(10); + continue; + } + + /* Limit to the amount of data we actually want to write */ + if (thisrun_bytes > count * block_size) + thisrun_bytes = count * block_size; + + /* Write data to fifo */ + retval = target_write_buffer(target, wp, thisrun_bytes, buffer); + if (retval != ERROR_OK) + break; + + /* Update counters and wrap write pointer */ + buffer += thisrun_bytes; + count -= thisrun_bytes / block_size; + wp += thisrun_bytes; + if (wp >= fifo_end_addr) + wp = fifo_start_addr; + + /* Store updated write pointer to target */ + retval = target_write_u32(target, wp_addr, wp); + if (retval != ERROR_OK) + break; + } + + if (retval != ERROR_OK) { + /* abort flash write algorithm on target */ + target_write_u32(target, wp_addr, 0); + } + + int retval2 = target_wait_algorithm(target, num_mem_params, mem_params, + num_reg_params, reg_params, + exit_point, + 10000, + arch_info); + + if (retval2 != ERROR_OK) { + LOG_ERROR("error waiting for target flash write algorithm"); + retval = retval2; + } + + return retval; +} int target_read_memory(struct target *target, uint32_t address, uint32_t size, uint32_t count, uint8_t *buffer) @@ -1232,11 +1375,85 @@ int target_call_timer_callbacks_now(void) return target_call_timer_callbacks_check_time(0); } -int target_alloc_working_area_try(struct target *target, uint32_t size, struct working_area **area) +/* Prints the working area layout for debug purposes */ +static void print_wa_layout(struct target *target) { struct working_area *c = target->working_areas; - struct working_area *new_wa = NULL; + while (c) { + LOG_DEBUG("%c%c 0x%08"PRIx32"-0x%08"PRIx32" (%"PRIu32" bytes)", + c->backup ? 'b' : ' ', c->free ? ' ' : '*', + c->address, c->address + c->size - 1, c->size); + c = c->next; + } +} + +/* Reduce area to size bytes, create a new free area from the remaining bytes, if any. */ +static void target_split_working_area(struct working_area *area, uint32_t size) +{ + assert(area->free); /* Shouldn't split an allocated area */ + assert(size <= area->size); /* Caller should guarantee this */ + + /* Split only if not already the right size */ + if (size < area->size) { + struct working_area *new_wa = malloc(sizeof(*new_wa)); + + if (new_wa == NULL) + return; + + new_wa->next = area->next; + new_wa->size = area->size - size; + new_wa->address = area->address + size; + new_wa->backup = NULL; + new_wa->user = NULL; + new_wa->free = true; + + area->next = new_wa; + area->size = size; + + /* If backup memory was allocated to this area, it has the wrong size + * now so free it and it will be reallocated if/when needed */ + if (area->backup) { + free(area->backup); + area->backup = NULL; + } + } +} + +/* Merge all adjacent free areas into one */ +static void target_merge_working_areas(struct target *target) +{ + struct working_area *c = target->working_areas; + + while (c && c->next) { + assert(c->next->address == c->address + c->size); /* This is an invariant */ + + /* Find two adjacent free areas */ + if (c->free && c->next->free) { + /* Merge the last into the first */ + c->size += c->next->size; + + /* Remove the last */ + struct working_area *to_be_freed = c->next; + c->next = c->next->next; + if (to_be_freed->backup) + free(to_be_freed->backup); + free(to_be_freed); + + /* If backup memory was allocated to the remaining area, it's has + * the wrong size now */ + if (c->backup) { + free(c->backup); + c->backup = NULL; + } + } else { + c = c->next; + } + } +} + +int target_alloc_working_area_try(struct target *target, uint32_t size, struct working_area **area) +{ /* Reevaluate working area address based on MMU state*/ if (target->working_areas == NULL) { int retval; @@ -1249,8 +1466,8 @@ int target_alloc_working_area_try(struct target *target, uint32_t size, struct w if (!enabled) { if (target->working_area_phys_spec) { LOG_DEBUG("MMU disabled, using physical " - "address for working memory 0x%08x", - (unsigned)target->working_area_phys); + "address for working memory 0x%08"PRIx32, + target->working_area_phys); target->working_area = target->working_area_phys; } else { LOG_ERROR("No working memory available. " @@ -1260,8 +1477,8 @@ int target_alloc_working_area_try(struct target *target, uint32_t size, struct w } else { if (target->working_area_virt_spec) { LOG_DEBUG("MMU enabled, using virtual " - "address for working memory 0x%08x", - (unsigned)target->working_area_virt); + "address for working memory 0x%08"PRIx32, + target->working_area_virt); target->working_area = target->working_area_virt; } else { LOG_ERROR("No working memory available. " @@ -1269,70 +1486,62 @@ int target_alloc_working_area_try(struct target *target, uint32_t size, struct w return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } } + + /* Set up initial working area on first call */ + struct working_area *new_wa = malloc(sizeof(*new_wa)); + if (new_wa) { + new_wa->next = NULL; + new_wa->size = target->working_area_size & ~3UL; /* 4-byte align */ + new_wa->address = target->working_area; + new_wa->backup = NULL; + new_wa->user = NULL; + new_wa->free = true; + } + + target->working_areas = new_wa; } /* only allocate multiples of 4 byte */ - if (size % 4) { - LOG_ERROR("BUG: code tried to allocate unaligned number of bytes (0x%08x), padding", ((unsigned)(size))); - size = (size + 3) & (~3); - } + if (size % 4) + size = (size + 3) & (~3UL); + + struct working_area *c = target->working_areas; - /* see if there's already a matching working area */ + /* Find the first large enough working area */ while (c) { - if ((c->free) && (c->size == size)) { - new_wa = c; + if (c->free && c->size >= size) break; - } c = c->next; } - /* if not, allocate a new one */ - if (!new_wa) { - struct working_area **p = &target->working_areas; - uint32_t first_free = target->working_area; - uint32_t free_size = target->working_area_size; - - c = target->working_areas; - while (c) { - first_free += c->size; - free_size -= c->size; - p = &c->next; - c = c->next; - } - - if (free_size < size) - return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + if (c == NULL) + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; - LOG_DEBUG("allocated new working area at address 0x%08x", (unsigned)first_free); + /* Split the working area into the requested size */ + target_split_working_area(c, size); - new_wa = malloc(sizeof(struct working_area)); - new_wa->next = NULL; - new_wa->size = size; - new_wa->address = first_free; + LOG_DEBUG("allocated new working area of %"PRIu32" bytes at address 0x%08"PRIx32, size, c->address); - if (target->backup_working_area) { - int retval; - new_wa->backup = malloc(new_wa->size); - retval = target_read_memory(target, new_wa->address, 4, - new_wa->size / 4, new_wa->backup); - if (retval != ERROR_OK) { - free(new_wa->backup); - free(new_wa); - return retval; - } - } else - new_wa->backup = NULL; + if (target->backup_working_area) { + if (c->backup == NULL) { + c->backup = malloc(c->size); + if (c->backup == NULL) + return ERROR_FAIL; + } - /* put new entry in list */ - *p = new_wa; + int retval = target_read_memory(target, c->address, 4, c->size / 4, c->backup); + if (retval != ERROR_OK) + return retval; } /* mark as used, and return the new (reused) area */ - new_wa->free = false; - *area = new_wa; + c->free = false; + *area = c; /* user pointer */ - new_wa->user = area; + c->user = area; + + print_wa_layout(target); return ERROR_OK; } @@ -1343,30 +1552,57 @@ int target_alloc_working_area(struct target *target, uint32_t size, struct worki retval = target_alloc_working_area_try(target, size, area); if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) - LOG_WARNING("not enough working area available(requested %u)", (unsigned)(size)); + LOG_WARNING("not enough working area available(requested %"PRIu32")", size); return retval; } +static int target_restore_working_area(struct target *target, struct working_area *area) +{ + int retval = ERROR_OK; + + if (target->backup_working_area && area->backup != NULL) { + retval = target_write_memory(target, area->address, 4, area->size / 4, area->backup); + if (retval != ERROR_OK) + LOG_ERROR("failed to restore %"PRIu32" bytes of working area at address 0x%08"PRIx32, + area->size, area->address); + } + + return retval; +} + +/* Restore the area's backup memory, if any, and return the area to the allocation pool */ static int target_free_working_area_restore(struct target *target, struct working_area *area, int restore) { + int retval = ERROR_OK; + if (area->free) - return ERROR_OK; + return retval; - if (restore && target->backup_working_area) { - int retval = target_write_memory(target, - area->address, 4, area->size / 4, area->backup); + if (restore) { + retval = target_restore_working_area(target, area); + /* REVISIT: Perhaps the area should be freed even if restoring fails. */ if (retval != ERROR_OK) return retval; } area->free = true; + LOG_DEBUG("freed %"PRIu32" bytes of working area at address 0x%08"PRIx32, + area->size, area->address); + /* mark user pointer invalid */ + /* TODO: Is this really safe? It points to some previous caller's memory. + * How could we know that the area pointer is still in that place and not + * some other vital data? What's the purpose of this, anyway? */ *area->user = NULL; area->user = NULL; - return ERROR_OK; + target_merge_working_areas(target); + + print_wa_layout(target); + + return retval; } int target_free_working_area(struct target *target, struct working_area *area) @@ -1381,19 +1617,24 @@ static void target_free_all_working_areas_restore(struct target *target, int res { struct working_area *c = target->working_areas; - while (c) { - struct working_area *next = c->next; - target_free_working_area_restore(target, c, restore); - - if (c->backup) - free(c->backup); + LOG_DEBUG("freeing all working areas"); - free(c); - - c = next; + /* Loop through all areas, restoring the allocated ones and marking them as free */ + while (c) { + if (!c->free) { + if (restore) + target_restore_working_area(target, c); + c->free = true; + *c->user = NULL; /* Same as above */ + c->user = NULL; + } + c = c->next; } - target->working_areas = NULL; + /* Run a merge pass to combine all areas into one */ + target_merge_working_areas(target); + + print_wa_layout(target); } void target_free_all_working_areas(struct target *target) @@ -1401,6 +1642,25 @@ void target_free_all_working_areas(struct target *target) target_free_all_working_areas_restore(target, 1); } +/* Find the largest number of bytes that can be allocated */ +uint32_t target_get_working_area_avail(struct target *target) +{ + struct working_area *c = target->working_areas; + uint32_t max_size = 0; + + if (c == NULL) + return target->working_area_size; + + while (c) { + if (c->free && max_size < c->size) + max_size = c->size; + + c = c->next; + } + + return max_size; +} + int target_arch_state(struct target *target) { int retval; @@ -2260,7 +2520,6 @@ COMMAND_HANDLER(handle_resume_command) return ERROR_COMMAND_SYNTAX_ERROR; struct target *target = get_current_target(CMD_CTX); - target_handle_event(target, TARGET_EVENT_OLD_pre_resume); /* with no CMD_ARGV, resume from current pc, addr = 0, * with one arguments, addr = CMD_ARGV[0], @@ -4084,6 +4343,34 @@ static int jim_target_mw(Jim_Interp *interp, int argc, Jim_Obj *const *argv) return (target_fill_mem(target, a, fn, data_size, b, c) == ERROR_OK) ? JIM_OK : JIM_ERR; } +/** +* @brief Reads an array of words/halfwords/bytes from target memory starting at specified address. +* +* Usage: mdw [phys]
[] - for 32 bit reads +* mdh [phys]
[] - for 16 bit reads +* mdb [phys]
[] - for 8 bit reads +* +* Count defaults to 1. +* +* Calls target_read_memory or target_read_phys_memory depending on +* the presence of the "phys" argument +* Reads the target memory in blocks of max. 32 bytes, and returns an array of ints formatted +* to int representation in base16. +* Also outputs read data in a human readable form using command_print +* +* @param phys if present target_read_phys_memory will be used instead of target_read_memory +* @param address address where to start the read. May be specified in decimal or hex using the standard "0x" prefix +* @param count optional count parameter to read an array of values. If not specified, defaults to 1. +* @returns: JIM_ERR on error or JIM_OK on success and sets the result string to an array of ascii formatted numbers +* on success, with [] number of elements. +* +* In case of little endian target: +* Example1: "mdw 0x00000000" returns "10123456" +* Exmaple2: "mdh 0x00000000 1" returns "3456" +* Example3: "mdb 0x00000000" returns "56" +* Example4: "mdh 0x00000000 2" returns "3456 1012" +* Example5: "mdb 0x00000000 3" returns "56 34 12" +**/ static int jim_target_md(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { const char *cmd_name = Jim_GetString(argv[0], NULL); @@ -4112,78 +4399,80 @@ static int jim_target_md(Jim_Interp *interp, int argc, Jim_Obj *const *argv) fn = target_read_phys_memory; } - jim_wide a; - e = Jim_GetOpt_Wide(&goi, &a); + /* Read address parameter */ + jim_wide addr; + e = Jim_GetOpt_Wide(&goi, &addr); if (e != JIM_OK) return JIM_ERR; - jim_wide c; + + /* If next parameter exists, read it out as the count parameter, if not, set it to 1 (default) */ + jim_wide count; if (goi.argc == 1) { - e = Jim_GetOpt_Wide(&goi, &c); + e = Jim_GetOpt_Wide(&goi, &count); if (e != JIM_OK) return JIM_ERR; } else - c = 1; + count = 1; /* all args must be consumed */ if (goi.argc != 0) return JIM_ERR; - jim_wide b = 1; /* shut up gcc */ + jim_wide dwidth = 1; /* shut up gcc */ if (strcasecmp(cmd_name, "mdw") == 0) - b = 4; + dwidth = 4; else if (strcasecmp(cmd_name, "mdh") == 0) - b = 2; + dwidth = 2; else if (strcasecmp(cmd_name, "mdb") == 0) - b = 1; + dwidth = 1; else { LOG_ERROR("command '%s' unknown: ", cmd_name); return JIM_ERR; } /* convert count to "bytes" */ - c = c * b; + int bytes = count * dwidth; struct target *target = Jim_CmdPrivData(goi.interp); uint8_t target_buf[32]; jim_wide x, y, z; - while (c > 0) { - y = c; - if (y > 16) - y = 16; - e = fn(target, a, b, y / b, target_buf); + while (bytes > 0) { + y = (bytes < 16) ? bytes : 16; /* y = min(bytes, 16); */ + + /* Try to read out next block */ + e = fn(target, addr, dwidth, y / dwidth, target_buf); + if (e != ERROR_OK) { - char tmp[10]; - snprintf(tmp, sizeof(tmp), "%08lx", (long)a); - Jim_SetResultFormatted(interp, "error reading target @ 0x%s", tmp); + Jim_SetResultFormatted(interp, "error reading target @ 0x%08lx", (long)addr); return JIM_ERR; } - command_print(NULL, "0x%08x ", (int)(a)); - switch (b) { + command_print_sameline(NULL, "0x%08x ", (int)(addr)); + switch (dwidth) { case 4: for (x = 0; x < 16 && x < y; x += 4) { z = target_buffer_get_u32(target, &(target_buf[x])); - command_print(NULL, "%08x ", (int)(z)); + command_print_sameline(NULL, "%08x ", (int)(z)); } for (; (x < 16) ; x += 4) - command_print(NULL, " "); + command_print_sameline(NULL, " "); break; case 2: for (x = 0; x < 16 && x < y; x += 2) { z = target_buffer_get_u16(target, &(target_buf[x])); - command_print(NULL, "%04x ", (int)(z)); + command_print_sameline(NULL, "%04x ", (int)(z)); } for (; (x < 16) ; x += 2) - command_print(NULL, " "); + command_print_sameline(NULL, " "); break; case 1: default: for (x = 0 ; (x < 16) && (x < y) ; x += 1) { z = target_buffer_get_u8(target, &(target_buf[x])); - command_print(NULL, "%02x ", (int)(z)); + command_print_sameline(NULL, "%02x ", (int)(z)); } for (; (x < 16) ; x += 1) - command_print(NULL, " "); + command_print_sameline(NULL, " "); break; } /* ascii-ify the bytes */ @@ -4204,10 +4493,10 @@ static int jim_target_md(Jim_Interp *interp, int argc, Jim_Obj *const *argv) /* terminate */ target_buf[16] = 0; /* print - with a newline */ - command_print(NULL, "%s\n", target_buf); + command_print_sameline(NULL, "%s\n", target_buf); /* NEXT... */ - c -= 16; - a += 16; + bytes -= 16; + addr += 16; } return JIM_OK; } @@ -4807,7 +5096,6 @@ static int jim_target_smp(Jim_Interp *interp, int argc, Jim_Obj *const *argv) struct target_list *head, *curr, *new; curr = (struct target_list *) NULL; head = (struct target_list *) NULL; - new = (struct target_list *) NULL; retval = 0; LOG_DEBUG("%d", argc); @@ -4843,8 +5131,10 @@ static int jim_target_smp(Jim_Interp *interp, int argc, Jim_Obj *const *argv) target->head = head; curr = curr->next; } - if (target->rtos) + + if (target && target->rtos) retval = rtos_smp_init(head->target); + return retval; }