Revert "target: remove unused working area 'user' field"
[fw/openocd] / src / target / target.c
index f8326ea72b0800362140dab39aaccb2bac1b2ce4..b8e4c2ff9ed937dc0720b2f98634a3c6923aa074 100644 (file)
@@ -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] <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);
@@ -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;
 }