aboutsummaryrefslogtreecommitdiff
path: root/src/target/cortex_a.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/target/cortex_a.c')
-rw-r--r--src/target/cortex_a.c247
1 files changed, 235 insertions, 12 deletions
diff --git a/src/target/cortex_a.c b/src/target/cortex_a.c
index 5018a91..6b76656 100644
--- a/src/target/cortex_a.c
+++ b/src/target/cortex_a.c
@@ -23,6 +23,9 @@
* Copyright (C) 2013 Kamal Dasu *
* kdasu.kdev@gmail.com *
* *
+ * Copyright (C) 2016 Chengyu Zheng *
+ * chengyu.zheng@polimi.it : watchpoint support *
+ * *
* 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 *
@@ -58,6 +61,7 @@
#include "jtag/interface.h"
#include "transport/transport.h"
#include "smp.h"
+#include <helper/bits.h>
#include <helper/time_support.h>
static int cortex_a_poll(struct target *target);
@@ -80,6 +84,16 @@ static int cortex_a_virt2phys(struct target *target,
static int cortex_a_read_cpu_memory(struct target *target,
uint32_t address, uint32_t size, uint32_t count, uint8_t *buffer);
+static unsigned int ilog2(unsigned int x)
+{
+ unsigned int y = 0;
+ x /= 2;
+ while (x) {
+ ++y;
+ x /= 2;
+ }
+ return y;
+}
/* restore cp15_control_reg at resume */
static int cortex_a_restore_cp15_control_reg(struct target *target)
@@ -1658,6 +1672,200 @@ static int cortex_a_remove_breakpoint(struct target *target, struct breakpoint *
return ERROR_OK;
}
+/**
+ * Sets a watchpoint for an Cortex-A target in one of the watchpoint units. It is
+ * considered a bug to call this function when there are no available watchpoint
+ * units.
+ *
+ * @param target Pointer to an Cortex-A target to set a watchpoint on
+ * @param watchpoint Pointer to the watchpoint to be set
+ * @return Error status if watchpoint set fails or the result of executing the
+ * JTAG queue
+ */
+static int cortex_a_set_watchpoint(struct target *target, struct watchpoint *watchpoint)
+{
+ int retval = ERROR_OK;
+ int wrp_i = 0;
+ uint32_t control;
+ uint32_t address;
+ uint8_t address_mask;
+ uint8_t byte_address_select;
+ uint8_t load_store_access_control = 0x3;
+ struct cortex_a_common *cortex_a = target_to_cortex_a(target);
+ struct armv7a_common *armv7a = &cortex_a->armv7a_common;
+ struct cortex_a_wrp *wrp_list = cortex_a->wrp_list;
+
+ if (watchpoint->set) {
+ LOG_WARNING("watchpoint already set");
+ return retval;
+ }
+
+ /* check available context WRPs */
+ while (wrp_list[wrp_i].used && (wrp_i < cortex_a->wrp_num))
+ wrp_i++;
+
+ if (wrp_i >= cortex_a->wrp_num) {
+ LOG_ERROR("ERROR Can not find free Watchpoint Register Pair");
+ return ERROR_FAIL;
+ }
+
+ if (watchpoint->length == 0 || watchpoint->length > 0x80000000U ||
+ (watchpoint->length & (watchpoint->length - 1))) {
+ LOG_WARNING("watchpoint length must be a power of 2");
+ return ERROR_FAIL;
+ }
+
+ if (watchpoint->address & (watchpoint->length - 1)) {
+ LOG_WARNING("watchpoint address must be aligned at length");
+ return ERROR_FAIL;
+ }
+
+ /* FIXME: ARM DDI 0406C: address_mask is optional. What to do if it's missing? */
+ /* handle wp length 1 and 2 through byte select */
+ switch (watchpoint->length) {
+ case 1:
+ byte_address_select = BIT(watchpoint->address & 0x3);
+ address = watchpoint->address & ~0x3;
+ address_mask = 0;
+ break;
+
+ case 2:
+ byte_address_select = 0x03 << (watchpoint->address & 0x2);
+ address = watchpoint->address & ~0x3;
+ address_mask = 0;
+ break;
+
+ case 4:
+ byte_address_select = 0x0f;
+ address = watchpoint->address;
+ address_mask = 0;
+ break;
+
+ default:
+ byte_address_select = 0xff;
+ address = watchpoint->address;
+ address_mask = ilog2(watchpoint->length);
+ break;
+ }
+
+ watchpoint->set = wrp_i + 1;
+ control = (address_mask << 24) |
+ (byte_address_select << 5) |
+ (load_store_access_control << 3) |
+ (0x3 << 1) | 1;
+ wrp_list[wrp_i].used = 1;
+ wrp_list[wrp_i].value = address;
+ wrp_list[wrp_i].control = control;
+
+ retval = cortex_a_dap_write_memap_register_u32(target, armv7a->debug_base
+ + CPUDBG_WVR_BASE + 4 * wrp_list[wrp_i].WRPn,
+ wrp_list[wrp_i].value);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = cortex_a_dap_write_memap_register_u32(target, armv7a->debug_base
+ + CPUDBG_WCR_BASE + 4 * wrp_list[wrp_i].WRPn,
+ wrp_list[wrp_i].control);
+ if (retval != ERROR_OK)
+ return retval;
+
+ LOG_DEBUG("wp %i control 0x%0" PRIx32 " value 0x%0" PRIx32, wrp_i,
+ wrp_list[wrp_i].control,
+ wrp_list[wrp_i].value);
+
+ return ERROR_OK;
+}
+
+/**
+ * Unset an existing watchpoint and clear the used watchpoint unit.
+ *
+ * @param target Pointer to the target to have the watchpoint removed
+ * @param watchpoint Pointer to the watchpoint to be removed
+ * @return Error status while trying to unset the watchpoint or the result of
+ * executing the JTAG queue
+ */
+static int cortex_a_unset_watchpoint(struct target *target, struct watchpoint *watchpoint)
+{
+ int retval;
+ struct cortex_a_common *cortex_a = target_to_cortex_a(target);
+ struct armv7a_common *armv7a = &cortex_a->armv7a_common;
+ struct cortex_a_wrp *wrp_list = cortex_a->wrp_list;
+
+ if (!watchpoint->set) {
+ LOG_WARNING("watchpoint not set");
+ return ERROR_OK;
+ }
+
+ int wrp_i = watchpoint->set - 1;
+ if (wrp_i < 0 || wrp_i >= cortex_a->wrp_num) {
+ LOG_DEBUG("Invalid WRP number in watchpoint");
+ return ERROR_OK;
+ }
+ LOG_DEBUG("wrp %i control 0x%0" PRIx32 " value 0x%0" PRIx32, wrp_i,
+ wrp_list[wrp_i].control, wrp_list[wrp_i].value);
+ wrp_list[wrp_i].used = 0;
+ wrp_list[wrp_i].value = 0;
+ wrp_list[wrp_i].control = 0;
+ retval = cortex_a_dap_write_memap_register_u32(target, armv7a->debug_base
+ + CPUDBG_WCR_BASE + 4 * wrp_list[wrp_i].WRPn,
+ wrp_list[wrp_i].control);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = cortex_a_dap_write_memap_register_u32(target, armv7a->debug_base
+ + CPUDBG_WVR_BASE + 4 * wrp_list[wrp_i].WRPn,
+ wrp_list[wrp_i].value);
+ if (retval != ERROR_OK)
+ return retval;
+ watchpoint->set = 0;
+
+ return ERROR_OK;
+}
+
+/**
+ * Add a watchpoint to an Cortex-A target. If there are no watchpoint units
+ * available, an error response is returned.
+ *
+ * @param target Pointer to the Cortex-A target to add a watchpoint to
+ * @param watchpoint Pointer to the watchpoint to be added
+ * @return Error status while trying to add the watchpoint
+ */
+static int cortex_a_add_watchpoint(struct target *target, struct watchpoint *watchpoint)
+{
+ struct cortex_a_common *cortex_a = target_to_cortex_a(target);
+
+ if (cortex_a->wrp_num_available < 1) {
+ LOG_INFO("no hardware watchpoint available");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+
+ int retval = cortex_a_set_watchpoint(target, watchpoint);
+ if (retval != ERROR_OK)
+ return retval;
+
+ cortex_a->wrp_num_available--;
+ return ERROR_OK;
+}
+
+/**
+ * Remove a watchpoint from an Cortex-A target. The watchpoint will be unset and
+ * the used watchpoint unit will be reopened.
+ *
+ * @param target Pointer to the target to remove a watchpoint from
+ * @param watchpoint Pointer to the watchpoint to be removed
+ * @return Result of trying to unset the watchpoint
+ */
+static int cortex_a_remove_watchpoint(struct target *target, struct watchpoint *watchpoint)
+{
+ struct cortex_a_common *cortex_a = target_to_cortex_a(target);
+
+ if (watchpoint->set) {
+ cortex_a->wrp_num_available++;
+ cortex_a_unset_watchpoint(target, watchpoint);
+ }
+ return ERROR_OK;
+}
+
+
/*
* Cortex-A Reset functions
*/
@@ -2837,6 +3045,20 @@ static int cortex_a_examine_first(struct target *target)
LOG_DEBUG("Configured %i hw breakpoints", cortex_a->brp_num);
+ /* Setup Watchpoint Register Pairs */
+ cortex_a->wrp_num = ((didr >> 28) & 0x0F) + 1;
+ cortex_a->wrp_num_available = cortex_a->wrp_num;
+ free(cortex_a->wrp_list);
+ cortex_a->wrp_list = calloc(cortex_a->wrp_num, sizeof(struct cortex_a_wrp));
+ for (i = 0; i < cortex_a->wrp_num; i++) {
+ cortex_a->wrp_list[i].used = 0;
+ cortex_a->wrp_list[i].value = 0;
+ cortex_a->wrp_list[i].control = 0;
+ cortex_a->wrp_list[i].WRPn = i;
+ }
+
+ LOG_DEBUG("Configured %i hw watchpoints", cortex_a->wrp_num);
+
/* select debug_ap as default */
swjdp->apsel = armv7a->debug_ap->ap_num;
@@ -2959,6 +3181,7 @@ static void cortex_a_deinit_target(struct target *target)
dscr & ~DSCR_HALT_DBG_MODE);
}
+ free(cortex_a->wrp_list);
free(cortex_a->brp_list);
arm_free_reg_cache(dpm->arm);
free(dpm->dbp);
@@ -3036,15 +3259,15 @@ COMMAND_HANDLER(handle_cortex_a_mask_interrupts_command)
struct target *target = get_current_target(CMD_CTX);
struct cortex_a_common *cortex_a = target_to_cortex_a(target);
- static const Jim_Nvp nvp_maskisr_modes[] = {
+ static const struct jim_nvp nvp_maskisr_modes[] = {
{ .name = "off", .value = CORTEX_A_ISRMASK_OFF },
{ .name = "on", .value = CORTEX_A_ISRMASK_ON },
{ .name = NULL, .value = -1 },
};
- const Jim_Nvp *n;
+ const struct jim_nvp *n;
if (CMD_ARGC > 0) {
- n = Jim_Nvp_name2value_simple(nvp_maskisr_modes, CMD_ARGV[0]);
+ n = jim_nvp_name2value_simple(nvp_maskisr_modes, CMD_ARGV[0]);
if (n->name == NULL) {
LOG_ERROR("Unknown parameter: %s - should be off or on", CMD_ARGV[0]);
return ERROR_COMMAND_SYNTAX_ERROR;
@@ -3053,7 +3276,7 @@ COMMAND_HANDLER(handle_cortex_a_mask_interrupts_command)
cortex_a->isrmasking_mode = n->value;
}
- n = Jim_Nvp_value2name_simple(nvp_maskisr_modes, cortex_a->isrmasking_mode);
+ n = jim_nvp_value2name_simple(nvp_maskisr_modes, cortex_a->isrmasking_mode);
command_print(CMD, "cortex_a interrupt mask %s", n->name);
return ERROR_OK;
@@ -3064,22 +3287,22 @@ COMMAND_HANDLER(handle_cortex_a_dacrfixup_command)
struct target *target = get_current_target(CMD_CTX);
struct cortex_a_common *cortex_a = target_to_cortex_a(target);
- static const Jim_Nvp nvp_dacrfixup_modes[] = {
+ static const struct jim_nvp nvp_dacrfixup_modes[] = {
{ .name = "off", .value = CORTEX_A_DACRFIXUP_OFF },
{ .name = "on", .value = CORTEX_A_DACRFIXUP_ON },
{ .name = NULL, .value = -1 },
};
- const Jim_Nvp *n;
+ const struct jim_nvp *n;
if (CMD_ARGC > 0) {
- n = Jim_Nvp_name2value_simple(nvp_dacrfixup_modes, CMD_ARGV[0]);
+ n = jim_nvp_name2value_simple(nvp_dacrfixup_modes, CMD_ARGV[0]);
if (n->name == NULL)
return ERROR_COMMAND_SYNTAX_ERROR;
cortex_a->dacrfixup_mode = n->value;
}
- n = Jim_Nvp_value2name_simple(nvp_dacrfixup_modes, cortex_a->dacrfixup_mode);
+ n = jim_nvp_value2name_simple(nvp_dacrfixup_modes, cortex_a->dacrfixup_mode);
command_print(CMD, "cortex_a domain access control fixup %s", n->name);
return ERROR_OK;
@@ -3173,8 +3396,8 @@ struct target_type cortexa_target = {
.add_context_breakpoint = cortex_a_add_context_breakpoint,
.add_hybrid_breakpoint = cortex_a_add_hybrid_breakpoint,
.remove_breakpoint = cortex_a_remove_breakpoint,
- .add_watchpoint = NULL,
- .remove_watchpoint = NULL,
+ .add_watchpoint = cortex_a_add_watchpoint,
+ .remove_watchpoint = cortex_a_remove_watchpoint,
.commands = cortex_a_command_handlers,
.target_create = cortex_a_target_create,
@@ -3250,8 +3473,8 @@ struct target_type cortexr4_target = {
.add_context_breakpoint = cortex_a_add_context_breakpoint,
.add_hybrid_breakpoint = cortex_a_add_hybrid_breakpoint,
.remove_breakpoint = cortex_a_remove_breakpoint,
- .add_watchpoint = NULL,
- .remove_watchpoint = NULL,
+ .add_watchpoint = cortex_a_add_watchpoint,
+ .remove_watchpoint = cortex_a_remove_watchpoint,
.commands = cortex_r4_command_handlers,
.target_create = cortex_r4_target_create,