* 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 *
}
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" },
{ .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" },
{ .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 }
};
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.
if (retval != ERROR_OK)
return retval;
+ target_call_event_callbacks(target, TARGET_EVENT_RESUME_END);
+
return retval;
}
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:
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;
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)
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;
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. "
} 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. "
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;
}
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)
{
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)
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;
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],
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] <address> [<count>] - for 32 bit reads
+* mdh [phys] <address> [<count>] - for 16 bit reads
+* mdb [phys] <address> [<count>] - 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 [<count>] 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);
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 */
/* 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;
}
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);
target->head = head;
curr = curr->next;
}
- if (target->rtos)
+
+ if (target && target->rtos)
retval = rtos_smp_init(head->target);
+
return retval;
}