diff options
Diffstat (limited to 'gdb/arc-jtag-actionpoints.c')
-rw-r--r-- | gdb/arc-jtag-actionpoints.c | 1337 |
1 files changed, 1337 insertions, 0 deletions
diff --git a/gdb/arc-jtag-actionpoints.c b/gdb/arc-jtag-actionpoints.c new file mode 100644 index 00000000000..4c5d81826db --- /dev/null +++ b/gdb/arc-jtag-actionpoints.c @@ -0,0 +1,1337 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Author: + Richard Stuckey <richard.stuckey@arc.com> + + This file is part of GDB. + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This module implements hardware breakpoints and watchpoints on the ARC */ +/* processor using actionpoints. */ +/* */ +/* See */ +/* ARC 700 System Components Reference */ +/* 5126-016 */ +/* */ +/* for a full description of the processor actionpoints mechanism. */ +/* */ +/******************************************************************************/ + +/* gdb header files */ +#include "defs.h" +#include "inferior.h" +#include "gdb_assert.h" + +/* ARC header files */ +#include "arc-jtag-actionpoints.h" +#include "arc-registers.h" +#include "arc-elf32-tdep.h" +#include "arc-jtag.h" +#include "arc-jtag-ops.h" +#include "arc-support.h" + + +/* -------------------------------------------------------------------------- */ +/* local types */ +/* -------------------------------------------------------------------------- */ + +typedef enum +{ + NOT_IN_USE, + SINGLE, + PAIR_0, + PAIR_1, + QUAD_0, + QUAD_1, + QUAD_2, + QUAD_3 +} Actionpoint_Usage; + + +/* This information describes an individual actionpoint. */ +typedef struct arc_actionpoint +{ + Actionpoint_Usage usage; + Boolean triggered; + Boolean is_exclude; + unsigned int length; + ARC_RegisterContents match_value; + ARC_RegisterContents match_mask; + ARC_RegisterContents control; + ARC_RegisterContents point; +} ARC_ActionPoint; + + +/* -------------------------------------------------------------------------- */ +/* local data */ +/* -------------------------------------------------------------------------- */ + +#define SUCCESS 0 +#define FAILURE (-1) + +#define MAX_ACTION_POINTS 8 +#define MAX_ACTION_POINTS_IN_GROUP 4 +#define HW_BP_SIZE 1 + + +/* Bit masks for the Actionpoint Control Registers AP_ACx (10 low-order bits only). */ +#define AP_TARGET_INSTRUCTION_ADDRESS 0x000 +#define AP_TARGET_INSTRUCTION_DATA 0x001 +#define AP_TARGET_LOAD_STORE_ADDRESS 0x002 +#define AP_TARGET_LOAD_STORE_DATA 0x003 +#define AP_TARGET_AUX_REGISTER_ADDRESS 0x004 +#define AP_TARGET_AUX_REGISTER_DATA 0x005 +#define AP_TARGET_EXTERNAL_PARAMETER_0 0x006 +#define AP_TARGET_EXTERNAL_PARAMETER_1 0x007 +#define AP_TARGET_MASK 0x007 +#define AP_TARGET_SHIFT 0 + +#define AP_TRANSACTION_TYPE_DISABLED 0x000 +#define AP_TRANSACTION_TYPE_WRITE 0x010 +#define AP_TRANSACTION_TYPE_READ 0x020 +#define AP_TRANSACTION_TYPE_ACCESS 0x030 +#define AP_TRANSACTION_TYPE_MASK 0x030 +#define AP_TRANSACTION_TYPE_SHIFT 4 + +#define AP_MODE_TRIGGER_IN_RANGE 0x000 +#define AP_MODE_TRIGGER_OUTSIDE_RANGE 0x040 +#define AP_MODE_MASK 0x040 + +#define AP_ACTION_BREAK 0x000 +#define AP_ACTION_EXCEPTION 0x100 +#define AP_ACTION_MASK 0x100 + +#define AP_PAIR 0x080 +#define AP_QUAD 0x200 + + +/* Data defining the actionpoints. */ +static unsigned int num_actionpoints; +static Boolean full_target_actionpoints; +static ARC_ActionPoint actionpoints[MAX_ACTION_POINTS]; + +/* The h/w numbers of the AMV0, AMM0 and AC0 auxiliary registers. */ +static ARC_RegisterNumber amv0_regnum; +static ARC_RegisterNumber amm0_regnum; +static ARC_RegisterNumber ac0_regnum; + + +/* -------------------------------------------------------------------------- */ +/* local macros */ +/* -------------------------------------------------------------------------- */ + +/* The N actionpoint auxiliary registers (where N is 0 .. MAX_ACTION_POINTS - 1) + (fortunately, the numbers of the registers are one contiguous block, so + a simple addition is sufficient here). */ +#define ARC_HW_AMV_REGNUM(n) (ARC_RegisterNumber) (amv0_regnum + 3 * (n)) +#define ARC_HW_AMM_REGNUM(n) (ARC_RegisterNumber) (amm0_regnum + 3 * (n)) +#define ARC_HW_AC_REGNUM(n) (ARC_RegisterNumber) (ac0_regnum + 3 * (n)) + +/* This will give a value in range 0 .. MAX_ACTION_POINTS - 1. */ +#define AP_INSTANCE(ap) ((ap) - actionpoints) +#define IN_USE(ap) ((ap)->usage != NOT_IN_USE) + + +/* -------------------------------------------------------------------------- */ +/* local functions */ +/* -------------------------------------------------------------------------- */ + +#if 0 +/* This function checks that a given number is a power of two, and, if so, + returns a bitmask corresponding to (2 ** number - 1). */ + +static Boolean +is_power_of_two (int number, ARC_Word *mask) +{ + unsigned int power = 1; + unsigned int i; + + *mask = 0; + + for (i = 0; (i < BITS_IN_WORD) && (power <= (unsigned int) number); i++) + { + if (power == (unsigned int) number) + return TRUE; + + power <<= 1; + *mask = (*mask << 1) + 1; + } + + /* not a power */ + return FALSE; +} +#endif + + +/* This function determines whether the ARC processor in the connected target + provides support for actionpoints (that is a configuratiopn option). */ + +static Boolean +target_has_actionpoints (void) +{ + ARC_RegisterNumber ap_build_regnum; + ARC_RegisterContents ap_build; + + ap_build_regnum = arc_aux_find_register_number("AP_BUILD", ARC_HW_AP_BUILD_REGNUM); + amv0_regnum = arc_aux_find_register_number("AMV0", ARC_HW_AMV0_REGNUM); + amm0_regnum = arc_aux_find_register_number("AMM0", ARC_HW_AMM0_REGNUM); + ac0_regnum = arc_aux_find_register_number("AC0", ARC_HW_AC0_REGNUM); + + num_actionpoints = 0; + + if (arc_read_jtag_aux_register(ap_build_regnum, &ap_build, TRUE)) + { + DEBUG("AP_BUILD BCR: 0x%08X\n", ap_build); + + /* AP_BUILD returns 0 if actionpoints are not selected in the + processor configuration. */ + if (ap_build != 0) + { + /* If the processor's implementation of the actionpoint mechanism is + the one we know about. */ + if ((ap_build & AP_BUILD_VERSION) == 0x4) + { + unsigned int type = (ap_build & AP_BUILD_TYPE) >> AP_BUILD_TYPE_SHIFT; + unsigned int i; + + /* The low-order two bits of the type field encode the number + of actionpoints supported by the processor. */ + switch (type % 4) + { + case 0 : num_actionpoints = 2; break; + case 1 : num_actionpoints = 4; break; + case 2 : num_actionpoints = 8; break; + default: internal_error (__FILE__, __LINE__, _("invalid AP_BUILD.TYPE: 0x%X"), type); + } + + /* The next bit specifies whether the processor supports full + or minimum targets for the actionpoints. */ + full_target_actionpoints = ((type & 4) == 0); + + for (i = 0; i < MAX_ACTION_POINTS; i++) + { + actionpoints[i].usage = NOT_IN_USE; + actionpoints[i].triggered = FALSE; + actionpoints[i].is_exclude = FALSE; + } + + DEBUG("ARC processor supports %u %s actionpoints\n", + num_actionpoints, (full_target_actionpoints) ? "full" : "minimum"); + + return TRUE; + } + else + warning(_("ARC processor actionpoint mechanism version (0x%x) is not supported."), + ap_build & AP_BUILD_VERSION); + } + } + + DEBUG("ARC processor supports no actionpoints\n"); + + return FALSE; +} + + +/* This function determines the set of actionpoints that would be required to + cover exactly the memory region specified by (addr,length), by using one + actionpoint with an inclusive range, and zero or more actionpoints with + exclusive ranges. + + The set of values and masks for the actionpoint AMV and AMM registers are + returned in the actionpoint_value and actionpoint_mask arrays - these must + be able to hold 4 entries. The value and mask for the inclusive actionpoint + are returned as the first elements in the arrays. */ + +static unsigned int +map_actionpoints (ARC_Address addr, + unsigned int length, + ARC_Address actionpoint_value[], + ARC_Word actionpoint_mask[]) +{ + ARC_Address first_data = addr; + ARC_Address last_data = first_data + length - 1; + ARC_Address include_start; + ARC_Address include_end; + ARC_Word mask; + unsigned int power_of_two; + unsigned int points; + + ENTERARGS("addr 0x%08X, length %d", addr, length); +// DEBUG("range: %08X .. %08X\n", first_data, last_data); + + /* If the range extends across the midpoint of the address space. */ + if (((first_data & 0x80000000) == 0) && ((last_data & 0x80000000) != 0)) + { +// DEBUG("pathological case!\n"); + + /* Must cover entire address space. */ + include_start = 0x00000000; + include_end = 0xFFFFFFFF; + mask = 0xFFFFFFFF; // Ignore all bits! + } + else + { + unsigned int i; + + mask = 0; + power_of_two = 1; + + /* Determine what actionpoint would be required to include all of the given + memory region (this include range may have leading and trailing parts + that extend beyond the given region). */ + for (i = 0; i < 32; i++) + { + unsigned int include_size = power_of_two; + + include_start = (first_data / include_size) * include_size; + include_end = include_start + include_size - 1; + + if (include_start <= first_data && include_end >= last_data) + { +// DEBUG("include: 0x%08X .. 0x%08X (mask 0x%08x)\n", +// include_start, include_end, mask); + break; + } + + mask = (mask << 1) + 1; + power_of_two <<= 1; + } + } + + /* This is the first actionpoint in the list. */ + actionpoint_value[0] = include_start; + actionpoint_mask [0] = mask; + points = 1; + + + /* Determine what actionpoints would be required to mask out the leading part + of the include range. */ + { + unsigned int to_be_excluded = first_data - include_start; + ARC_Address boundary = include_start; + + while (to_be_excluded > 0) + { + unsigned int j; + + mask = 0; + power_of_two = 1; + + for (j = 0; j < 32; j++) + { + unsigned int exclude_size = power_of_two; + ARC_Address exclude_start = (first_data / exclude_size - 1) * exclude_size; + ARC_Address exclude_end = exclude_start + exclude_size - 1; + + if (exclude_end < first_data && exclude_start <= boundary) + { +// DEBUG("leading exclude: 0x%08X .. 0x%08X (mask 0x%08x)\n", +// exclude_start, exclude_end, mask); + + to_be_excluded = first_data - exclude_end - 1; + boundary = exclude_end + 1; + + /* There is no point in returning the details of + more than the maximum number of actionpoints that + can be linked together (in a quad). */ + if (points < MAX_ACTION_POINTS_IN_GROUP) + { + actionpoint_value[points] = exclude_start; + actionpoint_mask [points] = mask; + } + points++; + + break; + } + + mask = (mask << 1) + 1; + power_of_two <<= 1; + } + } + } + + /* Determine what actionpoints would be required to mask out the trailing + part of the include range. */ + { + unsigned int to_be_excluded = include_end - last_data; + ARC_Address boundary = include_end; + + while (to_be_excluded > 0) + { + unsigned int j; + + mask = 0; + power_of_two = 1; + + for (j = 0; j < 32; j++) + { + unsigned int exclude_size = power_of_two; + ARC_Address exclude_start = (last_data / exclude_size + 1) * exclude_size; + ARC_Address exclude_end = exclude_start + exclude_size - 1; + + if (exclude_start > last_data && exclude_end >= boundary) + { +// DEBUG("trailing exclude: 0x%08X .. 0x%08X (mask 0x%08x)\n", +// exclude_start, exclude_end, mask); + + to_be_excluded = exclude_start - last_data - 1; + boundary = exclude_start - 1; + + /* There is no point in returning the details of + more than the maximum number of actionpoints that + can be linked together (in a quad). */ + if (points < MAX_ACTION_POINTS_IN_GROUP) + { + actionpoint_value[points] = exclude_start; + actionpoint_mask [points] = mask; + } + points++; + + break; + } + + mask = (mask << 1) + 1; + power_of_two <<= 1; + } + } + } + + return points; +} + + +/* Set the given actionpoint on the target by writing to the corresponding set + of AMV, AMM and AC auxiliary registers. + Return TRUE if it is set successfully. */ + +static Boolean +set_actionpoint_on_target (ARC_ActionPoint *actionpoint) +{ + unsigned int instance = AP_INSTANCE(actionpoint); + + return arc_write_jtag_aux_register(ARC_HW_AMV_REGNUM(instance), actionpoint->match_value, TRUE) && + arc_write_jtag_aux_register(ARC_HW_AMM_REGNUM(instance), actionpoint->match_mask, TRUE) && + arc_write_jtag_aux_register(ARC_HW_AC_REGNUM (instance), actionpoint->control, TRUE); +} + + +/* Clear the given actionpoint on the target by writing 'DISABLED' to the + corresponding AC auxiliary register. + Return TRUE if it is cleared successfully. */ + +static Boolean +clear_actionpoint_from_target (ARC_ActionPoint *actionpoint) +{ + return arc_write_jtag_aux_register(ARC_HW_AC_REGNUM (AP_INSTANCE(actionpoint)), + AP_TRANSACTION_TYPE_DISABLED, + TRUE); +} + + +/* Set the given actionpoint on the target, and update its data structure. + Return TRUE if it is set successfully. */ + +static Boolean +set_actionpoint (ARC_ActionPoint *actionpoint) +{ + Boolean set = set_actionpoint_on_target(actionpoint); + + if (set) + { + actionpoint->triggered = FALSE; + actionpoint->point = 0; + } + else + actionpoint->usage = NOT_IN_USE; + + return set; +} + + +/* Insert an actionpoint to cover a range of target memory. + + Parameters: + bpt : the information describing a breakpoint (NULL for a watchpoint) + length : the length in bytes of the range + match_value: the value for the actionpoint value (AMV) aux register + match_mask : the value for the actionpoint mask (AMM) aux register + control : the value for the actionpoint control (AC) aux register + + Returns 0 for success, -1 for failure. */ + +static int +insert_actionpoint (struct bp_target_info *bpt, + unsigned int length, + ARC_RegisterContents match_value, + ARC_RegisterContents match_mask, + ARC_RegisterContents control) +{ + unsigned int i; + + /* Look for an unused actionpoint. */ + for (i = 0; i < num_actionpoints; i++) + { + ARC_ActionPoint *actionpoint = &actionpoints[i]; + + /* Got one! */ + if (!IN_USE(actionpoint)) + { + /* Record its data. */ + actionpoint->match_value = match_value; + actionpoint->match_mask = match_mask; + actionpoint->control = control; + actionpoint->is_exclude = FALSE; + actionpoint->length = length; + + /* Try to set it on the target. */ + if (set_actionpoint(actionpoint)) + { + /* Now it is in use. */ + actionpoint->usage = SINGLE; + + /* Is it a breakpoint? */ + if (bpt) + { + /* We have not actually saved code from the target program. */ + bpt->shadow_len = 0; + bpt->placed_size = (int) actionpoint->length; + } + + return SUCCESS; + } + } + } + + /* Failed: no free actionpoints. */ +// warning(_("no actionpoints available")); + return FAILURE; +} + + +/* Restore the actionpoints on the target according to their current settings. + If 'clear_unused' is TRUE, any actionpoints which are unused are explicitly + cleared on the target. + + Returns 0 for success, -1 for failure. */ + +static int +restore_actionpoints (Boolean clear_unused) +{ + unsigned int i; + + /* Look at each of the actionpoints. */ + for (i = 0; i < num_actionpoints; i++) + { + ARC_ActionPoint *actionpoint = &actionpoints[i]; + + if (IN_USE(actionpoint)) + { + if (!set_actionpoint_on_target(actionpoint)) + { + actionpoint->usage = NOT_IN_USE; + return FAILURE; + } + } + else if (clear_unused) + { + if (!clear_actionpoint_from_target (actionpoint)) + return FAILURE; + } + } + + return SUCCESS; +} + + +/* Find a number of unused actionpoints whose numbers (0..7) lie in a contiguous + range (allowing for wraparound of the numbers, i.e. % 8). + + Parameters: + required : the number of unused actionpoints required + from : set to the number (0..7) of the first actionpoint + compacted: set to TRUE if the currently used set of actionpoints + had to be compacted to give a contiguous range of unused + actionpoints + + Returns TRUE if the required number was found, FALSE otherwise. */ + +static Boolean +find_unused_actionpoints (unsigned int required, + unsigned int *from, + Boolean *compacted) +{ + unsigned int unused = 0; + unsigned int first_unused = 0; + unsigned int i; + + /* How many slots are not currently used? */ + for (i = 0; i < num_actionpoints; i++) + { + ARC_ActionPoint *actionpoint = &actionpoints[i]; + + if (!IN_USE(actionpoint)) + unused++; + } + + DEBUG("%u actionpoints unused, %u required\n", unused, required); + + if (required > unused) + return FALSE; + + /* When used in pairs or quads, the action points wrap around, e.g. a pair + might be actionpoints (3, 0), if the target has 4 actionpoints; and a + quad might be (6, 7, 0, 1), if the target has 8 actionpoints. */ + + /* First try to find 'required' contiguous unused slots. */ + for (i = 0; i < num_actionpoints + required - 2; i++) + { + ARC_ActionPoint *actionpoint = &actionpoints[i % num_actionpoints]; + + if (IN_USE(actionpoint)) + { + /* The first unused one MAY be the next one after this one. */ + first_unused = i + 1; + } + else + { + DEBUG("%u: AP%u is unused\n", i, i % num_actionpoints); + + if (i - first_unused + 1 >= required) + { + /* A sufficiently large sequence of unused actionpoints has been + found. */ + *from = first_unused % num_actionpoints; + *compacted = FALSE; + return TRUE; + } + } + } + + DEBUG("compacting array\n"); + + /* There are sufficient unused slots, but they are not contiguous - so move + all the used ones towards the start of the array so that all the unused + ones are contiguous at the end of the array. */ + first_unused = MAX_ACTION_POINTS; + + for (i = 0; i < num_actionpoints; i++) + { + ARC_ActionPoint *actionpoint = &actionpoints[i]; + + if (IN_USE(actionpoint)) + { + if (first_unused != MAX_ACTION_POINTS) + { + DEBUG("moving %u to %u\n", i, first_unused); + + /* Move the used one into the unused slot. */ + actionpoints[first_unused] = *actionpoint; + + actionpoint->usage = NOT_IN_USE; + + /* The first unused entry in the array is now the next one after + it - this is true whether that next one was the used one that + has just been moved, or was the next in a sequence of unused + entries. */ + first_unused++; + } + } + else if (first_unused == MAX_ACTION_POINTS) + { + /* This one really is the first unused one we have found. */ + first_unused = i; + } + } + + *from = num_actionpoints - unused; + + DEBUG("from = %u\n", *from); + + *compacted = TRUE; + return TRUE; +} + + +/* Insert an actionpoint group to cover a range of target memory. + + Parameters: + length : the length in bytes of the range + number : the number of actionpoints required + match_value: the values for the actionpoint value (AMV) aux registers + match_mask : the values for the actionpoint mask (AMM) aux registers + control : the value for the actionpoint control (AC) aux registers + + Returns 0 for success, -1 for failure. */ + +static int +insert_actionpoint_group (unsigned int length, + unsigned int number, + ARC_RegisterContents match_value[], + ARC_RegisterContents match_mask[], + ARC_RegisterContents control) +{ + /* For 2 actionpoints, we can use a pair; for 3 or 4, we must use a quad. */ + unsigned int required = (number == 2) ? 2 : 4; + unsigned int first_free; + Boolean is_pair = (required == 2); + Boolean compacted; + + gdb_assert(2 <= number && number <= 4); + + /* Try to find the required number of unused actionpoints. */ + if (find_unused_actionpoints(required, &first_free, &compacted)) + { + ARC_ActionPoint *actionpoint[MAX_ACTION_POINTS_IN_GROUP]; + unsigned int i; + + /* Get an array of pointers to the data for those actionpoints. */ + for (i = 0; i < required; i++) + actionpoint[i] = &actionpoints[(first_free + i) % num_actionpoints]; + + actionpoint[0]->length = length; + actionpoint[0]->is_exclude = FALSE; + + /* The Control register for the first actionpoint in the group must be + set to indicate whether the group is a pair or a quad. */ + actionpoint[0]->usage = (is_pair) ? PAIR_0 : QUAD_0; + actionpoint[0]->match_value = match_value[0]; + actionpoint[0]->match_mask = match_mask[0]; + actionpoint[0]->control = control | ((is_pair) ? AP_PAIR : AP_QUAD); + + /* All subsequent actionpoints in the group have exclusive rather than + inclusive address ranges. */ + control &= ~AP_MODE_TRIGGER_IN_RANGE; + control |= AP_MODE_TRIGGER_OUTSIDE_RANGE; + + for (i = 1; i < number; i++) + { + actionpoint[i]->usage = actionpoint[0]->usage + i; + actionpoint[i]->match_value = match_value[i]; + actionpoint[i]->match_mask = match_mask[i]; + actionpoint[i]->control = control; + actionpoint[i]->length = 0; + actionpoint[i]->is_exclude = TRUE; + } + + /* If we are using only 3 of the 4 actionpoints in a quad, the 4th one + must be disabled (or we could just make it the same as one of the + other exclusive ones). */ + if (number == 3) + { + ARC_ActionPoint *disabled = actionpoint[3]; + + disabled->usage = QUAD_3; + disabled->match_value = 0; + disabled->match_mask = 0; + disabled->control = AP_TRANSACTION_TYPE_DISABLED; + disabled->length = 0; + } + + /* If we had to compact the array of actionpoints in order to get a + long enough contiguous sequence of unused entries, then set ALL of + the actionpoints that are now in use, and explicitly clear all that + are not in use (this is simplest!). */ + if (compacted) + return restore_actionpoints(TRUE); + + /* Otherwise, just set the ones for the group, which were previously + unused. */ + for (i = 0; i < required; i++) + if (!set_actionpoint(actionpoint[i])) + return FAILURE; + + return SUCCESS; + } + +// warning(_("insufficient actionpoints available")); + return FAILURE; +} + + +/* Insert a h/w breakpoint or watchpoint to cover a range of target memory. + + Parameters: + address : the start address of the range + control : the value for the actionpoint control (AC) aux register + length : the length in bytes of the range + bpt : the information describing the breakpoint (NULL for a watchpoint) + + Returns 0 for success, -1 for failure. */ + +static int +insert_range (ARC_RegisterContents address, + ARC_RegisterContents control, + unsigned int length, + struct bp_target_info *bpt) +{ + /* At most 4 actionpoints can be connected (as a quad). */ + ARC_Address actionpoint_value[MAX_ACTION_POINTS_IN_GROUP]; + ARC_Word actionpoint_mask [MAX_ACTION_POINTS_IN_GROUP]; + unsigned int actionpoints_needed; + + /* Work out how many actionpoints would be required to exactly cover the + given memory range. */ + actionpoints_needed = map_actionpoints(address, + length, + actionpoint_value, + actionpoint_mask); + + if (actionpoints_needed == 1) + return insert_actionpoint(bpt, + length, + actionpoint_value[0], + actionpoint_mask[0], + control); + + if (actionpoints_needed <= MAX_ACTION_POINTS_IN_GROUP) + return insert_actionpoint_group(length, + actionpoints_needed, + actionpoint_value, + actionpoint_mask, + control); + + warning (_("break/watchpoint would require %u linked actionpoints, " + "but at most %u actionpoints may be linked together"), + actionpoints_needed, MAX_ACTION_POINTS_IN_GROUP); + + return FAILURE; +} + + +/* Remove an actionpoint from a range of target memory. + + Parameters: + address : the start address of the range + length : the length in bytes of the range + + Returns -1 for failure, 0 for success. */ + +static int +remove_actionpoint (CORE_ADDR address, unsigned int length) +{ + unsigned int i; + + /* Look at all the actionpoints. */ + for (i = 0; i < num_actionpoints; i++) + { + ARC_ActionPoint *actionpoint = &actionpoints[i]; + + if (IN_USE(actionpoint) && !actionpoint->is_exclude) + { + if (actionpoint->match_value == (ARC_RegisterContents) address && + actionpoint->length == length) + { + unsigned int points; + unsigned int p; + + /* Is this the first of a pair or quad? */ + if ((actionpoint->control & AP_PAIR) != 0) + points = 2; + else if ((actionpoint->control & AP_QUAD) != 0) + points = 4; + else + points = 1; + +// DEBUG("points = %u\n", points); + + for (p = 1; p < points; p++) + { + ARC_ActionPoint *next = &actionpoints[(i + p) % num_actionpoints]; + + if (clear_actionpoint_from_target (next)) + next->usage = NOT_IN_USE; + else + return FAILURE; + } + + if (clear_actionpoint_from_target (actionpoint)) + { + actionpoint->usage = NOT_IN_USE; + return SUCCESS; + } + + break; + } + } + } + + /* Failed: could not find actionpoint, or could not clear it. */ + return FAILURE; +} + + +/* -------------------------------------------------------------------------- */ +/* local functions called from outside this module */ +/* -------------------------------------------------------------------------- */ + +/* Check if we can set a hardware watchpoint of type TYPE. TYPE is + one of bp_hardware_watchpoint, bp_read_watchpoint, bp_write_watchpoint, or + bp_hardware_breakpoint. COUNT is the number of such watchpoints used so far + (including this one). OTHERTYPE is the total number of hardware breakpoints + and watchpoints of other types that are "already" set (0 if type == bp_hardware_breakpoint). + + Result: 0 if hardware watchpoints are not supported + -1 if there are not enough hardware watchpoints + 1 if there are enough hardware watchpoints + + N.B. this is not what is stated in target.h, but it does conform to the use + made of this function's result in breakpoint.c! */ + +static int +arc_debug_can_use_hw_breakpoint (int type, int count, int othertype) +{ + ENTERARGS("type %d, count %d", type, count); + + if (num_actionpoints == 0) + return 0; + + /* N.B. this will sometimes give a "false positive" result, i.e. that there + sufficient actionpoints available when in fact there are not: the + ARC processor actionpoints can be used for all of the types, but gdb + assumes that there are separate sets of resources for breakpoints + and watchpoints, and when asking for a breakpoint does not give the + number of watchpoints "already" set. + + It is not possible simply to check how many actionpoints are currently + set, as gdb does not actually set the breakpoints and watchpoints + until program execution is started or resumed - so when this function + is called, none are actually set. + + Also, the breakpoints and watchpoints may require pairs or quads of + actionpoints, rather than single actionpoints, and this will not be + known until they are set, and their addresses and ranges are known! */ + return ((int) num_actionpoints >= count + othertype) ? 1 : -1; +} + + +/* Insert a hardware breakpoint on the target. + Returns 0 for success, -1 for failure. */ + +static int +arc_debug_insert_hw_breakpoint (struct bp_target_info *bpt) +{ + ARC_RegisterContents control = AP_TARGET_INSTRUCTION_ADDRESS | + AP_TRANSACTION_TYPE_READ | + AP_MODE_TRIGGER_IN_RANGE | + AP_ACTION_BREAK; + + ENTERARGS("0x%x : %u", (unsigned int) bpt->placed_address, bpt->range); + + /* Is it a range breakpoint? */ + if (bpt->range) + return insert_range((ARC_RegisterContents) bpt->placed_address, + control, + bpt->range, + bpt); + + /* No, just a single-instruction breakpoint? */ + return insert_actionpoint(bpt, + HW_BP_SIZE, + (ARC_RegisterContents) bpt->placed_address, + 0, /* All bits of address. */ + control); +} + + +/* Remove a hardware breakpoint from the target. + Returns 0 for success, non-zero for failure. */ + +static int +arc_debug_remove_hw_breakpoint (struct bp_target_info *bpt) +{ + unsigned int range = (bpt->range) ? bpt->range : HW_BP_SIZE; + + ENTERARGS("0x%x : %u", (unsigned int) bpt->placed_address, range); + + return remove_actionpoint(bpt->placed_address, range); +} + + +/* Insert a hardware watchpoint on the target. + + Parameters: + addr : the start address of the region of memory to be watched + length: the length in bytes of the region of memory + type : 0 => write, 1 => read, 2 => read/write + + Returns 0 for success, -1 for failure. */ + +static int +arc_debug_insert_watchpoint (CORE_ADDR addr, int length, int type) +{ + ARC_RegisterContents control = AP_TARGET_LOAD_STORE_ADDRESS | + AP_MODE_TRIGGER_IN_RANGE | + AP_ACTION_BREAK; + + ENTERARGS("0x%08X:%d %d", (unsigned int) addr, length, type); + + gdb_assert(length > 0); + + switch (type) + { + case 0: + control |= AP_TRANSACTION_TYPE_WRITE; break; + case 1: + control |= AP_TRANSACTION_TYPE_READ; break; + case 2: + control |= AP_TRANSACTION_TYPE_ACCESS; break; + default: + internal_error (__FILE__, __LINE__, _("invalid watchpoint type: %d"), type); + } + + return insert_range((ARC_RegisterContents) addr, + control, + (unsigned int) length, + NULL); +} + + +/* Remove a hardware watchpoint from the target. + + Parameters: + addr : the start address of the region of memory being watched + length: the length in bytes of the region of memory + type : 0 => write, 1 => read, 2 => read/write + + Returns 0 for success, non-zero for failure. */ + +static int +arc_debug_remove_watchpoint (CORE_ADDR addr, int length, int type) +{ + ENTERARGS("0x%x:%d %d", (unsigned int) addr, length, type); + + return remove_actionpoint(addr, (unsigned int) length); +} + + +/* Returns non-zero if the execution of the target program has been stopped by + the trigger of a hardware watchpoint (i.e. on memory read or write), zero + otherwise. */ + +static int +arc_debug_stopped_by_watchpoint (void) +{ + unsigned int i; + + ENTERMSG; + + /* Look at all of the actionpoints. */ + for (i = 0; i < num_actionpoints; i++) + { + ARC_ActionPoint *actionpoint = &actionpoints[i]; + + if (IN_USE(actionpoint) && actionpoint->triggered) + { + /* Is it a memory read or write actionpoint? */ + if ((actionpoint->control & AP_TARGET_LOAD_STORE_ADDRESS) != 0) + { + DEBUG("actionpoint %d (load/store) triggered\n", i); + return 1; + } + } + } + + return 0; +} + + +/* Get the address of the data that was read/written causing a h/w watchpoint to + trigger; the address is returned in the '*addr' parameter. + Returns 0 for failure, non-zero for success. */ + +static int +arc_debug_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr) +{ + unsigned int i; + + ENTERMSG; + + /* Look at each of the actionpoints. */ + for (i = 0; i < num_actionpoints; i++) + { + ARC_ActionPoint *actionpoint = &actionpoints[i]; + + DEBUG("AP%u: in use = %d, triggered = %d\n", i, IN_USE(actionpoint), actionpoint->triggered); + + /* If this actionpoint has been triggered. */ + if (IN_USE(actionpoint) && actionpoint->triggered) + { + /* Is it a memory read or write actionpoint? */ + if ((actionpoint->control & AP_TARGET_LOAD_STORE_ADDRESS) != 0) + { + DEBUG("actionpoint %d (load/store) triggered by access at 0x%08X\n", i, actionpoint->point); + + /* OK, got the data address! */ + *addr = (CORE_ADDR) actionpoint->point; + return 1; + } + } + } + + DEBUG("no watchpoint triggered\n"); + + return 0; +} + + +/* Can a h/w watchpoint 'length' bytes long be set at address 'addr' in target memory? */ + +static int +arc_debug_region_ok_for_hw_watchpoint (CORE_ADDR addr, int length) +{ + /* As far as we know, we can set a h/w watchpoint anywhere! */ + return 1; +} + + +/* -------------------------------------------------------------------------- */ +/* externally visible functions */ +/* -------------------------------------------------------------------------- */ + +/* This function is called after a reset of the target has been performed (which + clears all the aux registers associated with actionpoints). It attempts to + restore all actionpoints to their pre-reset settings. + + Returns TRUE if the actionpoints are restored, FALSE otherwise. */ + +Boolean +arc_restore_actionpoints_after_reset (void) +{ + return (restore_actionpoints(FALSE) == SUCCESS); +} + + +/* If the debug target supports actionpoints, set up the function pointers in + the given target operations structure to point to the functions which + implement the associated operations. + + Returns TRUE if actionpoints are supported, FALSE otherwise. */ + +Boolean +arc_initialize_actionpoint_ops (struct target_ops *debug_ops) +{ + if (target_has_actionpoints()) + { + debug_ops->to_can_use_hw_breakpoint = arc_debug_can_use_hw_breakpoint; + debug_ops->to_insert_hw_breakpoint = arc_debug_insert_hw_breakpoint; + debug_ops->to_remove_hw_breakpoint = arc_debug_remove_hw_breakpoint; + debug_ops->to_insert_watchpoint = arc_debug_insert_watchpoint; + debug_ops->to_remove_watchpoint = arc_debug_remove_watchpoint; + debug_ops->to_stopped_by_watchpoint = arc_debug_stopped_by_watchpoint; + debug_ops->to_stopped_data_address = arc_debug_stopped_data_address; + debug_ops->to_region_ok_for_hw_watchpoint = arc_debug_region_ok_for_hw_watchpoint; + + /* This is the default, but just to make it clear that watchpoints must + be cleared before execution can resume. */ + debug_ops->to_have_continuable_watchpoint = 0; + + return TRUE; + } + + return FALSE; +} + + +/* Display all the target actionpoints. */ + +void +arc_display_actionpoints (void) +{ + unsigned int i; + + char *targets[8] = + { + _("Instruction Address"), + _("Instruction Data"), + _("Load/Store Address"), + _("Load/Store Data"), + _("Aux Register Address"), + _("Aux Register Data"), + _("Ext Parameter 0"), + _("Ext Parameter 1") + }; + + char *transactions[4] = + { + _("disabled"), + _("write"), + _("read"), + _("read/write") + }; + + char *explanations[8] = + { + _("execution of instruction at address"), + _("execution of instruction"), + _("load or store of data at address"), + _("load or store of data"), + _("read or write of auxiliary register"), + _("read or write of auxiliary register contents"), + _("value"), + _("value") + }; + + + /* Look at each of the actionpoints in turn. */ + for (i = 0; i < num_actionpoints; i++) + { + ARC_ActionPoint *actionpoint = &actionpoints[i]; + + if (IN_USE(actionpoint)) + { + ARC_RegisterContents control = actionpoint->control; + const unsigned int targ = (control & AP_TARGET_MASK ) >> AP_TARGET_SHIFT; + const unsigned int trans = (control & AP_TRANSACTION_TYPE_MASK) >> AP_TRANSACTION_TYPE_SHIFT; + const char *target = targets [targ]; + const char *type = transactions[trans]; + const char *mode = ((control & AP_MODE_MASK) == + AP_MODE_TRIGGER_OUTSIDE_RANGE) ? _("outside range") : _("in range"); + const char *action = ((control & AP_ACTION_MASK) == + AP_ACTION_BREAK) ? _("break") : _("raise exception"); + const char *usage; + + switch (actionpoint->usage) + { + case SINGLE: usage = _(" "); break; + case PAIR_0: usage = _(" (Pair 0)"); break; + case PAIR_1: usage = _(" (Pair 1)"); break; + case QUAD_0: usage = _(" (Quad 0)"); break; + case QUAD_1: usage = _(" (Quad 1)"); break; + case QUAD_2: usage = _(" (Quad 2)"); break; + case QUAD_3: usage = _(" (Quad 3)"); break; + default: + internal_error (__FILE__, __LINE__, _("invalid AP usage: %u"), actionpoint->usage); + return; + } + + printf_filtered(_("AP %u%s :: "), i, usage); + + printf_filtered( _("value : %08X\n"), actionpoint->match_value); + printf_filtered( _(" mask : %08X\n"), actionpoint->match_mask); + if ((control & AP_TRANSACTION_TYPE_MASK) == AP_TRANSACTION_TYPE_DISABLED) + printf_filtered(_(" control : %08X disabled\n"), actionpoint->control); + else + printf_filtered(_(" control : %08X %s, %s on %s %s\n"), actionpoint->control, target, action, type, mode); + if (actionpoint->triggered) + { + const char *explain = explanations[targ]; + + printf_filtered(_(" triggered by %s %08x\n"), explain, actionpoint->point); + } + } + else + { + printf_filtered(_("AP %u :: not in use\n"), i); + } + } +} + + +/* This function is called as soon as execution of the target program has halted. + It checks whether the halt is due to an actionpoint trigger, and, if so, + identifies the actionpoint that has been triggered and finds the address (code + or data) at which memory access (read, write or execute) has caused the trigger. */ + +void +arc_target_halted (void) +{ + ARC_RegisterContents debug; + + ENTERMSG; + + if (arc_read_jtag_aux_register(arc_debug_regnum, &debug, TRUE)) + { + /* If the bit indicating that an actionpoint has halted the processor is + set. */ + if ((debug & DEBUG_ACTIONPOINT_HALT) != 0) + { + /* Get the Actionpoints Status Register from the DEBUG register: + this contains one bit for each actionpoint in the processor + configuration. */ + unsigned int ASR = (debug & DEBUG_ACTIONPOINT_STATUS) >> + DEBUG_ACTIONPOINT_STATUS_SHIFT; + unsigned int i; + + /* Now look at each of the actionpoints. */ + for (i = 0; i < num_actionpoints; i++) + { + ARC_ActionPoint *actionpoint = &actionpoints[i]; + + actionpoint->triggered = FALSE; + + /* Is the ASR bit for this actionpoint set? */ + if ((ASR & 1) != 0) + { + if (IN_USE(actionpoint)) + { + actionpoint->triggered = TRUE; + + /* The AMV register for this action point has been + updated with the address to which access has caused + the actionpoint to trigger. */ + (void) arc_read_jtag_aux_register(ARC_HW_AMV_REGNUM(AP_INSTANCE(actionpoint)), + &actionpoint->point, + TRUE); + } + else + internal_error (__FILE__, __LINE__, _("actionpoint %u triggered but not set"), i); + } + + ASR >>= 1; + } + } + } +} + + +/* For debugging - just give the values. */ + +void +arc_dump_actionpoints (const char *message) +{ + unsigned int i; + + DEBUG("%s\n", message); + + /* Look at each of the actionpoints in turn. */ + for (i = 0; i < num_actionpoints; i++) + { + ARC_ActionPoint *actionpoint = &actionpoints[i]; + + DEBUG("slot %u:: ", i); + + if (IN_USE(actionpoint)) + { + DEBUG( "value : %08X\n", actionpoint->match_value); + DEBUG(" mask : %08X\n", actionpoint->match_mask); + DEBUG(" control : %08X\n", actionpoint->control); + DEBUG(" triggered: %u\n", actionpoint->triggered); + DEBUG(" point : %08x\n", actionpoint->point); + } + else + { + DEBUG("not in use\n"); + } + } +} + +/******************************************************************************/ |