target: check memory handlers before use for all types
[openocd.git] / src / target / target.c
index c87c88986c9f22678666af6d5611d1f81904a540..89fee4aaa5fa7899fed3dfd96ee448bd734ab997 100644 (file)
@@ -139,6 +139,7 @@ static struct target_type *target_types[] = {
 struct target *all_targets;
 static struct target_event_callback *target_event_callbacks;
 static struct target_timer_callback *target_timer_callbacks;
+LIST_HEAD(target_reset_callback_list);
 static const int polling_interval = 100;
 
 static const Jim_Nvp nvp_assert[] = {
@@ -216,6 +217,8 @@ 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_TRACE_CONFIG, .name = "trace-config" },
+
        { .name = NULL, .value = -1 }
 };
 
@@ -280,6 +283,28 @@ const char *target_state_name(struct target *t)
        return cp;
 }
 
+const char *target_event_name(enum target_event event)
+{
+       const char *cp;
+       cp = Jim_Nvp_value2name_simple(nvp_target_event, event)->name;
+       if (!cp) {
+               LOG_ERROR("Invalid target event: %d", (int)(event));
+               cp = "(*BUG*unknown*BUG*)";
+       }
+       return cp;
+}
+
+const char *target_reset_mode_name(enum target_reset_mode reset_mode)
+{
+       const char *cp;
+       cp = Jim_Nvp_value2name_simple(nvp_reset_modes, reset_mode)->name;
+       if (!cp) {
+               LOG_ERROR("Invalid target reset mode: %d", (int)(reset_mode));
+               cp = "(*BUG*unknown*BUG*)";
+       }
+       return cp;
+}
+
 /* determine the number of the new target */
 static int new_target_number(void)
 {
@@ -461,7 +486,7 @@ struct target *get_target(const char *id)
 }
 
 /* returns a pointer to the n-th configured target */
-static struct target *get_target_by_num(int num)
+struct target *get_target_by_num(int num)
 {
        struct target *target = all_targets;
 
@@ -601,6 +626,10 @@ static int target_process_reset(struct command_context *cmd_ctx, enum target_res
                return ERROR_FAIL;
        }
 
+       struct target *target;
+       for (target = all_targets; target; target = target->next)
+               target_call_reset_callbacks(target, reset_mode);
+
        /* disable polling during reset to make reset event scripts
         * more predictable, i.e. dr/irscan & pathmove in events will
         * not have JTAG operations injected into the middle of a sequence.
@@ -623,7 +652,6 @@ static int target_process_reset(struct command_context *cmd_ctx, enum target_res
        /* We want any events to be processed before the prompt */
        retval = target_call_timer_callbacks_now();
 
-       struct target *target;
        for (target = all_targets; target; target = target->next) {
                target->type->check_reset(target);
                target->running_alg = false;
@@ -659,7 +687,15 @@ static int default_check_reset(struct target *target)
 
 int target_examine_one(struct target *target)
 {
-       return target->type->examine(target);
+       target_call_event_callbacks(target, TARGET_EVENT_EXAMINE_START);
+
+       int retval = target->type->examine(target);
+       if (retval != ERROR_OK)
+               return retval;
+
+       target_call_event_callbacks(target, TARGET_EVENT_EXAMINE_END);
+
+       return ERROR_OK;
 }
 
 static int jtag_enable_callback(enum jtag_event event, void *priv)
@@ -671,15 +707,7 @@ static int jtag_enable_callback(enum jtag_event event, void *priv)
 
        jtag_unregister_event_callback(jtag_enable_callback, 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;
+       return target_examine_one(target);
 }
 
 /* Targets that correctly implement init + examine, i.e.
@@ -700,13 +728,9 @@ int target_examine(void)
                        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;
 }
@@ -914,7 +938,7 @@ int target_run_flash_async_algorithm(struct target *target,
                        break;
                }
 
-               if ((rp & (block_size - 1)) || rp < fifo_start_addr || rp >= fifo_end_addr) {
+               if (((rp - fifo_start_addr) & (block_size - 1)) || rp < fifo_start_addr || rp >= fifo_end_addr) {
                        LOG_ERROR("corrupted fifo read pointer 0x%" PRIx32, rp);
                        break;
                }
@@ -997,6 +1021,10 @@ int target_read_memory(struct target *target,
                LOG_ERROR("Target not examined yet");
                return ERROR_FAIL;
        }
+       if (!target->type->read_memory) {
+               LOG_ERROR("Target %s doesn't support read_memory", target_name(target));
+               return ERROR_FAIL;
+       }
        return target->type->read_memory(target, address, size, count, buffer);
 }
 
@@ -1007,6 +1035,10 @@ int target_read_phys_memory(struct target *target,
                LOG_ERROR("Target not examined yet");
                return ERROR_FAIL;
        }
+       if (!target->type->read_phys_memory) {
+               LOG_ERROR("Target %s doesn't support read_phys_memory", target_name(target));
+               return ERROR_FAIL;
+       }
        return target->type->read_phys_memory(target, address, size, count, buffer);
 }
 
@@ -1017,6 +1049,10 @@ int target_write_memory(struct target *target,
                LOG_ERROR("Target not examined yet");
                return ERROR_FAIL;
        }
+       if (!target->type->write_memory) {
+               LOG_ERROR("Target %s doesn't support write_memory", target_name(target));
+               return ERROR_FAIL;
+       }
        return target->type->write_memory(target, address, size, count, buffer);
 }
 
@@ -1027,6 +1063,10 @@ int target_write_phys_memory(struct target *target,
                LOG_ERROR("Target not examined yet");
                return ERROR_FAIL;
        }
+       if (!target->type->write_phys_memory) {
+               LOG_ERROR("Target %s doesn't support write_phys_memory", target_name(target));
+               return ERROR_FAIL;
+       }
        return target->type->write_phys_memory(target, address, size, count, buffer);
 }
 
@@ -1148,20 +1188,6 @@ static void target_reset_examined(struct target *target)
        target->examined = false;
 }
 
-static int err_read_phys_memory(struct target *target, uint32_t address,
-               uint32_t size, uint32_t count, uint8_t *buffer)
-{
-       LOG_ERROR("Not implemented: %s", __func__);
-       return ERROR_FAIL;
-}
-
-static int err_write_phys_memory(struct target *target, uint32_t address,
-               uint32_t size, uint32_t count, const uint8_t *buffer)
-{
-       LOG_ERROR("Not implemented: %s", __func__);
-       return ERROR_FAIL;
-}
-
 static int handle_target(void *priv);
 
 static int target_init_one(struct command_context *cmd_ctx,
@@ -1188,16 +1214,6 @@ static int target_init_one(struct command_context *cmd_ctx,
         * implement it in stages, but warn if we need to do so.
         */
        if (type->mmu) {
-               if (type->write_phys_memory == NULL) {
-                       LOG_ERROR("type '%s' is missing write_phys_memory",
-                                       type->name);
-                       type->write_phys_memory = err_write_phys_memory;
-               }
-               if (type->read_phys_memory == NULL) {
-                       LOG_ERROR("type '%s' is missing read_phys_memory",
-                                       type->name);
-                       type->read_phys_memory = err_read_phys_memory;
-               }
                if (type->virt2phys == NULL) {
                        LOG_ERROR("type '%s' is missing virt2phys", type->name);
                        type->virt2phys = identity_virt2phys;
@@ -1312,6 +1328,28 @@ int target_register_event_callback(int (*callback)(struct target *target,
        return ERROR_OK;
 }
 
+int target_register_reset_callback(int (*callback)(struct target *target,
+               enum target_reset_mode reset_mode, void *priv), void *priv)
+{
+       struct target_reset_callback *entry;
+
+       if (callback == NULL)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       entry = malloc(sizeof(struct target_reset_callback));
+       if (entry == NULL) {
+               LOG_ERROR("error allocating buffer for reset callback entry");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       entry->callback = callback;
+       entry->priv = priv;
+       list_add(&entry->list, &target_reset_callback_list);
+
+
+       return ERROR_OK;
+}
+
 int target_register_timer_callback(int (*callback)(void *priv), int time_ms, int periodic, void *priv)
 {
        struct target_timer_callback **callbacks_p = &target_timer_callbacks;
@@ -1330,6 +1368,7 @@ int target_register_timer_callback(int (*callback)(void *priv), int time_ms, int
        (*callbacks_p)->callback = callback;
        (*callbacks_p)->periodic = periodic;
        (*callbacks_p)->time_ms = time_ms;
+       (*callbacks_p)->removed = false;
 
        gettimeofday(&now, NULL);
        (*callbacks_p)->when.tv_usec = now.tv_usec + (time_ms % 1000) * 1000;
@@ -1369,26 +1408,39 @@ int target_unregister_event_callback(int (*callback)(struct target *target,
        return ERROR_OK;
 }
 
-int target_unregister_timer_callback(int (*callback)(void *priv), void *priv)
+int target_unregister_reset_callback(int (*callback)(struct target *target,
+               enum target_reset_mode reset_mode, void *priv), void *priv)
 {
-       struct target_timer_callback **p = &target_timer_callbacks;
-       struct target_timer_callback *c = target_timer_callbacks;
+       struct target_reset_callback *entry;
 
        if (callback == NULL)
                return ERROR_COMMAND_SYNTAX_ERROR;
 
-       while (c) {
-               struct target_timer_callback *next = c->next;
+       list_for_each_entry(entry, &target_reset_callback_list, list) {
+               if (entry->callback == callback && entry->priv == priv) {
+                       list_del(&entry->list);
+                       free(entry);
+                       break;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+int target_unregister_timer_callback(int (*callback)(void *priv), void *priv)
+{
+       if (callback == NULL)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       for (struct target_timer_callback *c = target_timer_callbacks;
+            c; c = c->next) {
                if ((c->callback == callback) && (c->priv == priv)) {
-                       *p = next;
-                       free(c);
+                       c->removed = true;
                        return ERROR_OK;
-               } else
-                       p = &(c->next);
-               c = next;
+               }
        }
 
-       return ERROR_OK;
+       return ERROR_FAIL;
 }
 
 int target_call_event_callbacks(struct target *target, enum target_event event)
@@ -1415,6 +1467,19 @@ int target_call_event_callbacks(struct target *target, enum target_event event)
        return ERROR_OK;
 }
 
+int target_call_reset_callbacks(struct target *target, enum target_reset_mode reset_mode)
+{
+       struct target_reset_callback *callback;
+
+       LOG_DEBUG("target reset %i (%s)", reset_mode,
+                       Jim_Nvp_value2name_simple(nvp_reset_modes, reset_mode)->name);
+
+       list_for_each_entry(callback, &target_reset_callback_list, list)
+               callback->callback(target, reset_mode, callback->priv);
+
+       return ERROR_OK;
+}
+
 static int target_timer_callback_periodic_restart(
                struct target_timer_callback *cb, struct timeval *now)
 {
@@ -1442,31 +1507,44 @@ static int target_call_timer_callback(struct target_timer_callback *cb,
 
 static int target_call_timer_callbacks_check_time(int checktime)
 {
+       static bool callback_processing;
+
+       /* Do not allow nesting */
+       if (callback_processing)
+               return ERROR_OK;
+
+       callback_processing = true;
+
        keep_alive();
 
        struct timeval now;
        gettimeofday(&now, NULL);
 
-       struct target_timer_callback *callback = target_timer_callbacks;
-       while (callback) {
-               /* cleaning up may unregister and free this callback */
-               struct target_timer_callback *next_callback = callback->next;
+       /* Store an address of the place containing a pointer to the
+        * next item; initially, that's a standalone "root of the
+        * list" variable. */
+       struct target_timer_callback **callback = &target_timer_callbacks;
+       while (*callback) {
+               if ((*callback)->removed) {
+                       struct target_timer_callback *p = *callback;
+                       *callback = (*callback)->next;
+                       free(p);
+                       continue;
+               }
 
-               bool call_it = callback->callback &&
-                       ((!checktime && callback->periodic) ||
-                         now.tv_sec > callback->when.tv_sec ||
-                        (now.tv_sec == callback->when.tv_sec &&
-                         now.tv_usec >= callback->when.tv_usec));
+               bool call_it = (*callback)->callback &&
+                       ((!checktime && (*callback)->periodic) ||
+                        now.tv_sec > (*callback)->when.tv_sec ||
+                        (now.tv_sec == (*callback)->when.tv_sec &&
+                         now.tv_usec >= (*callback)->when.tv_usec));
 
-               if (call_it) {
-                       int retval = target_call_timer_callback(callback, &now);
-                       if (retval != ERROR_OK)
-                               return retval;
-               }
+               if (call_it)
+                       target_call_timer_callback(*callback, &now);
 
-               callback = next_callback;
+               callback = &(*callback)->next;
        }
 
+       callback_processing = false;
        return ERROR_OK;
 }
 
@@ -1716,6 +1794,31 @@ int target_free_working_area(struct target *target, struct working_area *area)
        return target_free_working_area_restore(target, area, 1);
 }
 
+void target_quit(void)
+{
+       struct target_event_callback *pe = target_event_callbacks;
+       while (pe) {
+               struct target_event_callback *t = pe->next;
+               free(pe);
+               pe = t;
+       }
+       target_event_callbacks = NULL;
+
+       struct target_timer_callback *pt = target_timer_callbacks;
+       while (pt) {
+               struct target_timer_callback *t = pt->next;
+               free(pt);
+               pt = t;
+       }
+       target_timer_callbacks = NULL;
+
+       for (struct target *target = all_targets;
+            target; target = target->next) {
+               if (target->type->deinit_target)
+                       target->type->deinit_target(target);
+       }
+}
+
 /* free resources and restore memory, if restoring memory fails,
  * free up resources anyway
  */
@@ -2411,29 +2514,27 @@ static int handle_target(void *priv)
                                        target->backoff.times *= 2;
                                        target->backoff.times++;
                                }
-                               LOG_USER("Polling target %s failed, GDB will be halted. Polling again in %dms",
-                                               target_name(target),
-                                               target->backoff.times * polling_interval);
 
                                /* Tell GDB to halt the debugger. This allows the user to
                                 * run monitor commands to handle the situation.
                                 */
                                target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT);
-                               return retval;
                        }
-                       /* Since we succeeded, we reset backoff count */
                        if (target->backoff.times > 0) {
-                               LOG_USER("Polling target %s succeeded again, trying to reexamine", target_name(target));
+                               LOG_USER("Polling target %s failed, trying to reexamine", target_name(target));
                                target_reset_examined(target);
                                retval = target_examine_one(target);
                                /* Target examination could have failed due to unstable connection,
                                 * but we set the examined flag anyway to repoll it later */
                                if (retval != ERROR_OK) {
                                        target->examined = true;
+                                       LOG_USER("Examination failed, GDB will be halted. Polling again in %dms",
+                                                target->backoff.times * polling_interval);
                                        return retval;
                                }
                        }
 
+                       /* Since we succeeded, we reset backoff count */
                        target->backoff.times = 0;
                }
        }

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)