blob: 4c5d81826db7341647fc90003d4f1e9e13c64bcf [file] [log] [blame]
/* 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");
}
}
}
/******************************************************************************/