2010-04-22 Sergio Durigan Junior <sergiodj@linux.vnet.ibm.com>
Thiago Jung Bauermann <bauerman@br.ibm.com> * ppc-linux-nat.c (PTRACE_GET_DEBUGREG): Update comment. (PPC_PTRACE_GETWDBGINFO, PPC_PTRACE_SETHWDEBUG, PPC_PTRACE_DELHWDEBUG, ppc_debug_info, PPC_DEBUG_FEATURE_INSN_BP_RANGE, PPC_DEBUG_FEATURE_INSN_BP_MASK, PPC_DEBUG_FEATURE_DATA_BP_RANGE, PPC_DEBUG_FEATURE_DATA_BP_MASK, ppc_hw_breakpoint, PPC_BREAKPOINT_TRIGGER_EXECUTE, PPC_BREAKPOINT_TRIGGER READ, PPC_BREAKPOINT_TRIGGER_WRITE, PPC_BREAKPOINT_TRIGGER_RW, PPC_BREAKPOINT_MODE_EXACT PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE, PPC_BREAKPOINT_MODE_RANGE_EXCLUSIVE, PPC_BREAKPOINT_MODE_MASK, PPC_BREAKPOINT_CONDITION_NONE, PPC_BREAKPOINT_CONDITION_AND, PPC_BREAKPOINT_CONDITION_EXACT, PPC_BREAKPOINT_CONDITION_OR, PPC_BREAKPOINT_CONDITION_AND_OR, PPC_BREAKPOINT_CONDITION_BE_ALL, PPC_BREAKPOINT_CONDITION_BE_SHIFT, PPC_BREAKPOINT_CONDITION_BE): Define, in case <ptrace.h> doesn't provide it. (booke_debug_info): New variable. (max_slots_number): Ditto. (hw_break_tuple): New struct. (thread_points): Ditto. (ppc_threads): New variable. (PPC_DEBUG_CURRENT_VERSION): New define. (have_ptrace_new_debug_booke): New function. (ppc_linux_check_watch_resources): Renamed to ... (ppc_linux_can_use_hw_breakpoint): ... this. Handle BookE processors. (ppc_linux_region_ok_for_hw_watchpoint): Handle BookE processors. (booke_cmp_hw_point): New function. (booke_find_thread_points_by_tid): Ditto. (booke_insert_point): Ditto. (booke_remove_point): Ditto. (ppc_linux_insert_hw_breakpoint): Ditto. (ppc_linux_remove_hw_breakpoint): Ditto. (get_trigger_type): Ditto. (ppc_linux_insert_watchpoint): Handle BookE processors. (ppc_linux_remove_watchpoint): Ditto. (ppc_linux_new_thread): Ditto. (ppc_linux_thread_exit): New function.. (ppc_linux_stopped_data_address): Handle BookE processors. (ppc_linux_watchpoint_addr_within_range): Ditto. (_initialize_ppc_linux_nat): Initialize to_insert_hw_breakpoint and to_remove_hw_breakpoint fields of the target operations struct. Add observe for the thread_exit event.
This commit is contained in:
parent
758afad7fa
commit
6ffbb7abe4
|
@ -1,3 +1,47 @@
|
|||
2010-04-22 Sergio Durigan Junior <sergiodj@linux.vnet.ibm.com>
|
||||
Thiago Jung Bauermann <bauerman@br.ibm.com>
|
||||
|
||||
* ppc-linux-nat.c (PTRACE_GET_DEBUGREG): Update comment.
|
||||
(PPC_PTRACE_GETWDBGINFO, PPC_PTRACE_SETHWDEBUG, PPC_PTRACE_DELHWDEBUG,
|
||||
ppc_debug_info, PPC_DEBUG_FEATURE_INSN_BP_RANGE,
|
||||
PPC_DEBUG_FEATURE_INSN_BP_MASK, PPC_DEBUG_FEATURE_DATA_BP_RANGE,
|
||||
PPC_DEBUG_FEATURE_DATA_BP_MASK, ppc_hw_breakpoint,
|
||||
PPC_BREAKPOINT_TRIGGER_EXECUTE, PPC_BREAKPOINT_TRIGGER READ,
|
||||
PPC_BREAKPOINT_TRIGGER_WRITE, PPC_BREAKPOINT_TRIGGER_RW,
|
||||
PPC_BREAKPOINT_MODE_EXACT PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE,
|
||||
PPC_BREAKPOINT_MODE_RANGE_EXCLUSIVE, PPC_BREAKPOINT_MODE_MASK,
|
||||
PPC_BREAKPOINT_CONDITION_NONE, PPC_BREAKPOINT_CONDITION_AND,
|
||||
PPC_BREAKPOINT_CONDITION_EXACT, PPC_BREAKPOINT_CONDITION_OR,
|
||||
PPC_BREAKPOINT_CONDITION_AND_OR, PPC_BREAKPOINT_CONDITION_BE_ALL,
|
||||
PPC_BREAKPOINT_CONDITION_BE_SHIFT, PPC_BREAKPOINT_CONDITION_BE):
|
||||
Define, in case <ptrace.h> doesn't provide it.
|
||||
(booke_debug_info): New variable.
|
||||
(max_slots_number): Ditto.
|
||||
(hw_break_tuple): New struct.
|
||||
(thread_points): Ditto.
|
||||
(ppc_threads): New variable.
|
||||
(PPC_DEBUG_CURRENT_VERSION): New define.
|
||||
(have_ptrace_new_debug_booke): New function.
|
||||
(ppc_linux_check_watch_resources): Renamed to ...
|
||||
(ppc_linux_can_use_hw_breakpoint): ... this. Handle BookE processors.
|
||||
(ppc_linux_region_ok_for_hw_watchpoint): Handle BookE processors.
|
||||
(booke_cmp_hw_point): New function.
|
||||
(booke_find_thread_points_by_tid): Ditto.
|
||||
(booke_insert_point): Ditto.
|
||||
(booke_remove_point): Ditto.
|
||||
(ppc_linux_insert_hw_breakpoint): Ditto.
|
||||
(ppc_linux_remove_hw_breakpoint): Ditto.
|
||||
(get_trigger_type): Ditto.
|
||||
(ppc_linux_insert_watchpoint): Handle BookE processors.
|
||||
(ppc_linux_remove_watchpoint): Ditto.
|
||||
(ppc_linux_new_thread): Ditto.
|
||||
(ppc_linux_thread_exit): New function..
|
||||
(ppc_linux_stopped_data_address): Handle BookE processors.
|
||||
(ppc_linux_watchpoint_addr_within_range): Ditto.
|
||||
(_initialize_ppc_linux_nat): Initialize to_insert_hw_breakpoint and
|
||||
to_remove_hw_breakpoint fields of the target operations struct.
|
||||
Add observe for the thread_exit event.
|
||||
|
||||
2010-04-22 H.J. Lu <hongjiu.lu@intel.com>
|
||||
|
||||
* i386-linux-nat.c (regmap): Removed.
|
||||
|
|
|
@ -20,8 +20,10 @@
|
|||
|
||||
#include "defs.h"
|
||||
#include "gdb_string.h"
|
||||
#include "observer.h"
|
||||
#include "frame.h"
|
||||
#include "inferior.h"
|
||||
#include "gdbthread.h"
|
||||
#include "gdbcore.h"
|
||||
#include "regcache.h"
|
||||
#include "gdb_assert.h"
|
||||
|
@ -100,7 +102,8 @@
|
|||
#define PTRACE_SETEVRREGS 21
|
||||
#endif
|
||||
|
||||
/* Similarly for the hardware watchpoint support. */
|
||||
/* Similarly for the hardware watchpoint support. These requests are used
|
||||
when the BookE kernel interface is not available. */
|
||||
#ifndef PTRACE_GET_DEBUGREG
|
||||
#define PTRACE_GET_DEBUGREG 25
|
||||
#endif
|
||||
|
@ -111,6 +114,73 @@
|
|||
#define PTRACE_GETSIGINFO 0x4202
|
||||
#endif
|
||||
|
||||
/* These requests are used when the BookE kernel interface is available.
|
||||
It exposes the additional debug features of BookE processors, such as
|
||||
ranged breakpoints and watchpoints and hardware-accelerated condition
|
||||
evaluation. */
|
||||
#ifndef PPC_PTRACE_GETHWDBGINFO
|
||||
|
||||
/* Not having PPC_PTRACE_GETHWDBGINFO defined means that the new BookE
|
||||
interface is not present in ptrace.h, so we'll have to pretty much include
|
||||
it all here so that the code at least compiles on older systems. */
|
||||
#define PPC_PTRACE_GETHWDBGINFO 0x89
|
||||
#define PPC_PTRACE_SETHWDEBUG 0x88
|
||||
#define PPC_PTRACE_DELHWDEBUG 0x87
|
||||
|
||||
struct ppc_debug_info
|
||||
{
|
||||
uint32_t version; /* Only version 1 exists to date */
|
||||
uint32_t num_instruction_bps;
|
||||
uint32_t num_data_bps;
|
||||
uint32_t num_condition_regs;
|
||||
uint32_t data_bp_alignment;
|
||||
uint32_t sizeof_condition; /* size of the DVC register */
|
||||
uint64_t features;
|
||||
};
|
||||
|
||||
/* Features will have bits indicating whether there is support for: */
|
||||
#define PPC_DEBUG_FEATURE_INSN_BP_RANGE 0x1
|
||||
#define PPC_DEBUG_FEATURE_INSN_BP_MASK 0x2
|
||||
#define PPC_DEBUG_FEATURE_DATA_BP_RANGE 0x4
|
||||
#define PPC_DEBUG_FEATURE_DATA_BP_MASK 0x8
|
||||
|
||||
struct ppc_hw_breakpoint
|
||||
{
|
||||
uint32_t version; /* currently, version must be 1 */
|
||||
uint32_t trigger_type; /* only some combinations allowed */
|
||||
uint32_t addr_mode; /* address match mode */
|
||||
uint32_t condition_mode; /* break/watchpoint condition flags */
|
||||
uint64_t addr; /* break/watchpoint address */
|
||||
uint64_t addr2; /* range end or mask */
|
||||
uint64_t condition_value; /* contents of the DVC register */
|
||||
};
|
||||
|
||||
/* Trigger type. */
|
||||
#define PPC_BREAKPOINT_TRIGGER_EXECUTE 0x1
|
||||
#define PPC_BREAKPOINT_TRIGGER_READ 0x2
|
||||
#define PPC_BREAKPOINT_TRIGGER_WRITE 0x4
|
||||
#define PPC_BREAKPOINT_TRIGGER_RW 0x6
|
||||
|
||||
/* Address mode. */
|
||||
#define PPC_BREAKPOINT_MODE_EXACT 0x0
|
||||
#define PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE 0x1
|
||||
#define PPC_BREAKPOINT_MODE_RANGE_EXCLUSIVE 0x2
|
||||
#define PPC_BREAKPOINT_MODE_MASK 0x3
|
||||
|
||||
/* Condition mode. */
|
||||
#define PPC_BREAKPOINT_CONDITION_NONE 0x0
|
||||
#define PPC_BREAKPOINT_CONDITION_AND 0x1
|
||||
#define PPC_BREAKPOINT_CONDITION_EXACT 0x1
|
||||
#define PPC_BREAKPOINT_CONDITION_OR 0x2
|
||||
#define PPC_BREAKPOINT_CONDITION_AND_OR 0x3
|
||||
#define PPC_BREAKPOINT_CONDITION_BE_ALL 0x00ff0000
|
||||
#define PPC_BREAKPOINT_CONDITION_BE_SHIFT 16
|
||||
#define PPC_BREAKPOINT_CONDITION_BE(n) \
|
||||
(1<<((n)+PPC_BREAKPOINT_CONDITION_BE_SHIFT))
|
||||
#endif /* PPC_PTRACE_GETHWDBGINFO */
|
||||
|
||||
|
||||
|
||||
/* Similarly for the general-purpose (gp0 -- gp31)
|
||||
and floating-point registers (fp0 -- fp31). */
|
||||
#ifndef PTRACE_GETREGS
|
||||
|
@ -1270,30 +1340,6 @@ store_ppc_registers (const struct regcache *regcache, int tid)
|
|||
store_spe_register (regcache, tid, -1);
|
||||
}
|
||||
|
||||
static int
|
||||
ppc_linux_check_watch_resources (int type, int cnt, int ot)
|
||||
{
|
||||
int tid;
|
||||
ptid_t ptid = inferior_ptid;
|
||||
|
||||
/* DABR (data address breakpoint register) is optional for PPC variants.
|
||||
Some variants have one DABR, others have none. So CNT can't be larger
|
||||
than 1. */
|
||||
if (cnt > 1)
|
||||
return 0;
|
||||
|
||||
/* We need to know whether ptrace supports PTRACE_SET_DEBUGREG and whether
|
||||
the target has DABR. If either answer is no, the ptrace call will
|
||||
return -1. Fail in that case. */
|
||||
tid = TIDGET (ptid);
|
||||
if (tid == 0)
|
||||
tid = PIDGET (ptid);
|
||||
|
||||
if (ptrace (PTRACE_SET_DEBUGREG, tid, 0, 0) == -1)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Fetch the AT_HWCAP entry from the aux vector. */
|
||||
unsigned long ppc_linux_get_hwcap (void)
|
||||
{
|
||||
|
@ -1305,6 +1351,132 @@ unsigned long ppc_linux_get_hwcap (void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* The cached DABR value, to install in new threads.
|
||||
This variable is used when we are dealing with non-BookE
|
||||
processors. */
|
||||
static long saved_dabr_value;
|
||||
|
||||
/* Global structure that will store information about the available
|
||||
features on this BookE processor. */
|
||||
static struct ppc_debug_info booke_debug_info;
|
||||
|
||||
/* Global variable that holds the maximum number of slots that the
|
||||
kernel will use. This is only used when the processor is BookE. */
|
||||
static size_t max_slots_number = 0;
|
||||
|
||||
struct hw_break_tuple
|
||||
{
|
||||
long slot;
|
||||
struct ppc_hw_breakpoint *hw_break;
|
||||
};
|
||||
|
||||
/* This is an internal VEC created to store information about *points inserted
|
||||
for each thread. This is used for BookE processors. */
|
||||
typedef struct thread_points
|
||||
{
|
||||
/* The TID to which this *point relates. */
|
||||
int tid;
|
||||
/* Information about the *point, such as its address, type, etc.
|
||||
|
||||
Each element inside this vector corresponds to a hardware
|
||||
breakpoint or watchpoint in the thread represented by TID. The maximum
|
||||
size of these vector is MAX_SLOTS_NUMBER. If the hw_break element of
|
||||
the tuple is NULL, then the position in the vector is free. */
|
||||
struct hw_break_tuple *hw_breaks;
|
||||
} *thread_points_p;
|
||||
DEF_VEC_P (thread_points_p);
|
||||
|
||||
VEC(thread_points_p) *ppc_threads = NULL;
|
||||
|
||||
/* The version of the kernel interface that we will use if the processor is
|
||||
BookE. */
|
||||
#define PPC_DEBUG_CURRENT_VERSION 1
|
||||
|
||||
/* Returns non-zero if we support the ptrace interface which enables
|
||||
booke debugging resources. */
|
||||
static int
|
||||
have_ptrace_booke_interface (void)
|
||||
{
|
||||
static int have_ptrace_booke_interface = -1;
|
||||
|
||||
if (have_ptrace_booke_interface == -1)
|
||||
{
|
||||
int tid;
|
||||
|
||||
tid = TIDGET (inferior_ptid);
|
||||
if (tid == 0)
|
||||
tid = PIDGET (inferior_ptid);
|
||||
|
||||
/* Check for kernel support for BOOKE debug registers. */
|
||||
if (ptrace (PPC_PTRACE_GETHWDBGINFO, tid, 0, &booke_debug_info) >= 0)
|
||||
{
|
||||
have_ptrace_booke_interface = 1;
|
||||
max_slots_number = booke_debug_info.num_instruction_bps
|
||||
+ booke_debug_info.num_data_bps + booke_debug_info.num_condition_regs;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Old school interface and no BOOKE debug registers support. */
|
||||
have_ptrace_booke_interface = 0;
|
||||
memset (&booke_debug_info, 0, sizeof (struct ppc_debug_info));
|
||||
}
|
||||
}
|
||||
|
||||
return have_ptrace_booke_interface;
|
||||
}
|
||||
|
||||
static int
|
||||
ppc_linux_can_use_hw_breakpoint (int type, int cnt, int ot)
|
||||
{
|
||||
int total_hw_wp, total_hw_bp;
|
||||
|
||||
if (have_ptrace_booke_interface ())
|
||||
{
|
||||
/* For PPC BookE processors, the number of available hardware
|
||||
watchpoints and breakpoints is stored at the booke_debug_info
|
||||
struct. */
|
||||
total_hw_bp = booke_debug_info.num_instruction_bps;
|
||||
total_hw_wp = booke_debug_info.num_data_bps;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* For PPC server processors, we accept 1 hardware watchpoint and 0
|
||||
hardware breakpoints. */
|
||||
total_hw_bp = 0;
|
||||
total_hw_wp = 1;
|
||||
}
|
||||
|
||||
if (type == bp_hardware_watchpoint || type == bp_read_watchpoint
|
||||
|| type == bp_access_watchpoint || type == bp_watchpoint)
|
||||
{
|
||||
if (cnt > total_hw_wp)
|
||||
return -1;
|
||||
}
|
||||
else if (type == bp_hardware_breakpoint)
|
||||
{
|
||||
if (cnt > total_hw_bp)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!have_ptrace_booke_interface ())
|
||||
{
|
||||
int tid;
|
||||
ptid_t ptid = inferior_ptid;
|
||||
|
||||
/* We need to know whether ptrace supports PTRACE_SET_DEBUGREG and whether
|
||||
the target has DABR. If either answer is no, the ptrace call will
|
||||
return -1. Fail in that case. */
|
||||
tid = TIDGET (ptid);
|
||||
if (tid == 0)
|
||||
tid = PIDGET (ptid);
|
||||
|
||||
if (ptrace (PTRACE_SET_DEBUGREG, tid, 0, 0) == -1)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
ppc_linux_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
|
||||
{
|
||||
|
@ -1312,69 +1484,278 @@ ppc_linux_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
|
|||
if (len <= 0)
|
||||
return 0;
|
||||
|
||||
/* The new BookE ptrace interface tells if there are alignment restrictions
|
||||
for watchpoints in the processors. In that case, we use that information
|
||||
to determine the hardcoded watchable region for watchpoints. */
|
||||
if (have_ptrace_booke_interface ())
|
||||
{
|
||||
if (booke_debug_info.data_bp_alignment
|
||||
&& (addr + len > (addr & ~(booke_debug_info.data_bp_alignment - 1))
|
||||
+ booke_debug_info.data_bp_alignment))
|
||||
return 0;
|
||||
}
|
||||
/* addr+len must fall in the 8 byte watchable region for DABR-based
|
||||
processors. DAC-based processors, like the PowerPC 440, will use
|
||||
processors (i.e., server processors). Without the new BookE ptrace
|
||||
interface, DAC-based processors (i.e., embedded processors) will use
|
||||
addresses aligned to 4-bytes due to the way the read/write flags are
|
||||
passed at the moment. */
|
||||
if (((ppc_linux_get_hwcap () & PPC_FEATURE_BOOKE)
|
||||
&& (addr + len) > (addr & ~3) + 4)
|
||||
|| (addr + len) > (addr & ~7) + 8)
|
||||
passed in the old ptrace interface. */
|
||||
else if (((ppc_linux_get_hwcap () & PPC_FEATURE_BOOKE)
|
||||
&& (addr + len) > (addr & ~3) + 4)
|
||||
|| (addr + len) > (addr & ~7) + 8)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* The cached DABR value, to install in new threads. */
|
||||
static long saved_dabr_value;
|
||||
/* This function compares two ppc_hw_breakpoint structs field-by-field. */
|
||||
static inline int
|
||||
booke_cmp_hw_point (struct ppc_hw_breakpoint *a, struct ppc_hw_breakpoint *b)
|
||||
{
|
||||
return (a->trigger_type == b->trigger_type
|
||||
&& a->addr_mode == b->addr_mode
|
||||
&& a->condition_mode == b->condition_mode
|
||||
&& a->addr == b->addr
|
||||
&& a->addr2 == b->addr2
|
||||
&& a->condition_value == b->condition_value);
|
||||
}
|
||||
|
||||
/* This function can be used to retrieve a thread_points by the TID of the
|
||||
related process/thread. If nothing has been found, and ALLOC_NEW is 0,
|
||||
it returns NULL. If ALLOC_NEW is non-zero, a new thread_points for the
|
||||
provided TID will be created and returned. */
|
||||
static struct thread_points *
|
||||
booke_find_thread_points_by_tid (int tid, int alloc_new)
|
||||
{
|
||||
int i;
|
||||
struct thread_points *t;
|
||||
|
||||
for (i = 0; VEC_iterate (thread_points_p, ppc_threads, i, t); i++)
|
||||
if (t->tid == tid)
|
||||
return t;
|
||||
|
||||
t = NULL;
|
||||
|
||||
/* Do we need to allocate a new point_item
|
||||
if the wanted one does not exist? */
|
||||
if (alloc_new)
|
||||
{
|
||||
t = xmalloc (sizeof (struct thread_points));
|
||||
t->hw_breaks = xzalloc (max_slots_number * sizeof (struct hw_break_tuple));
|
||||
t->tid = tid;
|
||||
VEC_safe_push (thread_points_p, ppc_threads, t);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/* This function is a generic wrapper that is responsible for inserting a
|
||||
*point (i.e., calling `ptrace' in order to issue the request to the
|
||||
kernel) and registering it internally in GDB. */
|
||||
static void
|
||||
booke_insert_point (struct ppc_hw_breakpoint *b, int tid)
|
||||
{
|
||||
int i;
|
||||
long slot;
|
||||
struct ppc_hw_breakpoint *p = xmalloc (sizeof (struct ppc_hw_breakpoint));
|
||||
struct hw_break_tuple *hw_breaks;
|
||||
struct cleanup *c = make_cleanup (xfree, p);
|
||||
struct thread_points *t;
|
||||
struct hw_break_tuple *tuple;
|
||||
|
||||
memcpy (p, b, sizeof (struct ppc_hw_breakpoint));
|
||||
|
||||
errno = 0;
|
||||
slot = ptrace (PPC_PTRACE_SETHWDEBUG, tid, 0, p);
|
||||
if (slot < 0)
|
||||
perror_with_name (_("Unexpected error setting breakpoint or watchpoint"));
|
||||
|
||||
/* Everything went fine, so we have to register this *point. */
|
||||
t = booke_find_thread_points_by_tid (tid, 1);
|
||||
gdb_assert (t != NULL);
|
||||
hw_breaks = t->hw_breaks;
|
||||
|
||||
/* Find a free element in the hw_breaks vector. */
|
||||
for (i = 0; i < max_slots_number; i++)
|
||||
if (hw_breaks[i].hw_break == NULL)
|
||||
{
|
||||
hw_breaks[i].slot = slot;
|
||||
hw_breaks[i].hw_break = p;
|
||||
break;
|
||||
}
|
||||
|
||||
gdb_assert (i != max_slots_number);
|
||||
|
||||
discard_cleanups (c);
|
||||
}
|
||||
|
||||
/* This function is a generic wrapper that is responsible for removing a
|
||||
*point (i.e., calling `ptrace' in order to issue the request to the
|
||||
kernel), and unregistering it internally at GDB. */
|
||||
static void
|
||||
booke_remove_point (struct ppc_hw_breakpoint *b, int tid)
|
||||
{
|
||||
int i;
|
||||
struct hw_break_tuple *hw_breaks;
|
||||
struct thread_points *t;
|
||||
|
||||
t = booke_find_thread_points_by_tid (tid, 0);
|
||||
gdb_assert (t != NULL);
|
||||
hw_breaks = t->hw_breaks;
|
||||
|
||||
for (i = 0; i < max_slots_number; i++)
|
||||
if (hw_breaks[i].hw_break && booke_cmp_hw_point (hw_breaks[i].hw_break, b))
|
||||
break;
|
||||
|
||||
gdb_assert (i != max_slots_number);
|
||||
|
||||
/* We have to ignore ENOENT errors because the kernel implements hardware
|
||||
breakpoints/watchpoints as "one-shot", that is, they are automatically
|
||||
deleted when hit. */
|
||||
errno = 0;
|
||||
if (ptrace (PPC_PTRACE_DELHWDEBUG, tid, 0, hw_breaks[i].slot) < 0)
|
||||
if (errno != ENOENT)
|
||||
perror_with_name (_("Unexpected error deleting breakpoint or watchpoint"));
|
||||
|
||||
xfree (hw_breaks[i].hw_break);
|
||||
hw_breaks[i].hw_break = NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
ppc_linux_insert_hw_breakpoint (struct gdbarch *gdbarch,
|
||||
struct bp_target_info *bp_tgt)
|
||||
{
|
||||
ptid_t ptid;
|
||||
struct lwp_info *lp;
|
||||
struct ppc_hw_breakpoint p;
|
||||
|
||||
if (!have_ptrace_booke_interface ())
|
||||
return -1;
|
||||
|
||||
p.version = PPC_DEBUG_CURRENT_VERSION;
|
||||
p.trigger_type = PPC_BREAKPOINT_TRIGGER_EXECUTE;
|
||||
p.addr_mode = PPC_BREAKPOINT_MODE_EXACT;
|
||||
p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
|
||||
p.addr = (uint64_t) bp_tgt->placed_address;
|
||||
p.addr2 = 0;
|
||||
p.condition_value = 0;
|
||||
|
||||
ALL_LWPS (lp, ptid)
|
||||
booke_insert_point (&p, TIDGET (ptid));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ppc_linux_remove_hw_breakpoint (struct gdbarch *gdbarch,
|
||||
struct bp_target_info *bp_tgt)
|
||||
{
|
||||
ptid_t ptid;
|
||||
struct lwp_info *lp;
|
||||
struct ppc_hw_breakpoint p;
|
||||
|
||||
if (!have_ptrace_booke_interface ())
|
||||
return -1;
|
||||
|
||||
p.version = PPC_DEBUG_CURRENT_VERSION;
|
||||
p.trigger_type = PPC_BREAKPOINT_TRIGGER_EXECUTE;
|
||||
p.addr_mode = PPC_BREAKPOINT_MODE_EXACT;
|
||||
p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
|
||||
p.addr = (uint64_t) bp_tgt->placed_address;
|
||||
p.addr2 = 0;
|
||||
p.condition_value = 0;
|
||||
|
||||
ALL_LWPS (lp, ptid)
|
||||
booke_remove_point (&p, TIDGET (ptid));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
get_trigger_type (int rw)
|
||||
{
|
||||
int t;
|
||||
|
||||
if (rw == hw_read)
|
||||
t = PPC_BREAKPOINT_TRIGGER_READ;
|
||||
else if (rw == hw_write)
|
||||
t = PPC_BREAKPOINT_TRIGGER_WRITE;
|
||||
else
|
||||
t = PPC_BREAKPOINT_TRIGGER_READ | PPC_BREAKPOINT_TRIGGER_WRITE;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Set a watchpoint of type TYPE at address ADDR. */
|
||||
static int
|
||||
ppc_linux_insert_watchpoint (CORE_ADDR addr, int len, int rw)
|
||||
{
|
||||
struct lwp_info *lp;
|
||||
ptid_t ptid;
|
||||
long dabr_value;
|
||||
long read_mode, write_mode;
|
||||
int ret = -1;
|
||||
|
||||
if (ppc_linux_get_hwcap () & PPC_FEATURE_BOOKE)
|
||||
{
|
||||
/* PowerPC 440 requires only the read/write flags to be passed
|
||||
to the kernel. */
|
||||
read_mode = 1;
|
||||
write_mode = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* PowerPC 970 and other DABR-based processors are required to pass
|
||||
the Breakpoint Translation bit together with the flags. */
|
||||
read_mode = 5;
|
||||
write_mode = 6;
|
||||
}
|
||||
|
||||
dabr_value = addr & ~(read_mode | write_mode);
|
||||
switch (rw)
|
||||
if (have_ptrace_booke_interface ())
|
||||
{
|
||||
case hw_read:
|
||||
/* Set read and translate bits. */
|
||||
dabr_value |= read_mode;
|
||||
break;
|
||||
case hw_write:
|
||||
/* Set write and translate bits. */
|
||||
dabr_value |= write_mode;
|
||||
break;
|
||||
case hw_access:
|
||||
/* Set read, write and translate bits. */
|
||||
dabr_value |= read_mode | write_mode;
|
||||
break;
|
||||
struct ppc_hw_breakpoint p;
|
||||
|
||||
p.version = PPC_DEBUG_CURRENT_VERSION;
|
||||
p.trigger_type = get_trigger_type (rw);
|
||||
p.addr_mode = PPC_BREAKPOINT_MODE_EXACT;
|
||||
p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
|
||||
p.addr = (uint64_t) addr;
|
||||
p.addr2 = 0;
|
||||
p.condition_value = 0;
|
||||
|
||||
ALL_LWPS (lp, ptid)
|
||||
booke_insert_point (&p, TIDGET (ptid));
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
long dabr_value;
|
||||
long read_mode, write_mode;
|
||||
|
||||
if (ppc_linux_get_hwcap () & PPC_FEATURE_BOOKE)
|
||||
{
|
||||
/* PowerPC 440 requires only the read/write flags to be passed
|
||||
to the kernel. */
|
||||
read_mode = 1;
|
||||
write_mode = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* PowerPC 970 and other DABR-based processors are required to pass
|
||||
the Breakpoint Translation bit together with the flags. */
|
||||
read_mode = 5;
|
||||
write_mode = 6;
|
||||
}
|
||||
|
||||
dabr_value = addr & ~(read_mode | write_mode);
|
||||
switch (rw)
|
||||
{
|
||||
case hw_read:
|
||||
/* Set read and translate bits. */
|
||||
dabr_value |= read_mode;
|
||||
break;
|
||||
case hw_write:
|
||||
/* Set write and translate bits. */
|
||||
dabr_value |= write_mode;
|
||||
break;
|
||||
case hw_access:
|
||||
/* Set read, write and translate bits. */
|
||||
dabr_value |= read_mode | write_mode;
|
||||
break;
|
||||
}
|
||||
|
||||
saved_dabr_value = dabr_value;
|
||||
|
||||
ALL_LWPS (lp, ptid)
|
||||
if (ptrace (PTRACE_SET_DEBUGREG, TIDGET (ptid), 0, saved_dabr_value) < 0)
|
||||
return -1;
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
saved_dabr_value = dabr_value;
|
||||
|
||||
ALL_LWPS (lp, ptid)
|
||||
if (ptrace (PTRACE_SET_DEBUGREG, TIDGET (ptid), 0, saved_dabr_value) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -1382,19 +1763,96 @@ ppc_linux_remove_watchpoint (CORE_ADDR addr, int len, int rw)
|
|||
{
|
||||
struct lwp_info *lp;
|
||||
ptid_t ptid;
|
||||
long dabr_value = 0;
|
||||
int ret = -1;
|
||||
|
||||
saved_dabr_value = 0;
|
||||
ALL_LWPS (lp, ptid)
|
||||
if (ptrace (PTRACE_SET_DEBUGREG, TIDGET (ptid), 0, saved_dabr_value) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
if (have_ptrace_booke_interface ())
|
||||
{
|
||||
struct ppc_hw_breakpoint p;
|
||||
|
||||
p.version = PPC_DEBUG_CURRENT_VERSION;
|
||||
p.trigger_type = get_trigger_type (rw);
|
||||
p.addr_mode = PPC_BREAKPOINT_MODE_EXACT;
|
||||
p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
|
||||
p.addr = (uint64_t) addr;
|
||||
p.addr2 = 0;
|
||||
p.condition_value = 0;
|
||||
|
||||
ALL_LWPS (lp, ptid)
|
||||
booke_remove_point (&p, TIDGET (ptid));
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
saved_dabr_value = 0;
|
||||
ALL_LWPS (lp, ptid)
|
||||
if (ptrace (PTRACE_SET_DEBUGREG, TIDGET (ptid), 0, saved_dabr_value) < 0)
|
||||
return -1;
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
ppc_linux_new_thread (ptid_t ptid)
|
||||
{
|
||||
ptrace (PTRACE_SET_DEBUGREG, TIDGET (ptid), 0, saved_dabr_value);
|
||||
int tid = TIDGET (ptid);
|
||||
|
||||
if (have_ptrace_booke_interface ())
|
||||
{
|
||||
int i;
|
||||
struct thread_points *p;
|
||||
struct hw_break_tuple *hw_breaks;
|
||||
|
||||
if (VEC_empty (thread_points_p, ppc_threads))
|
||||
return;
|
||||
|
||||
/* Get a list of breakpoints from any thread. */
|
||||
p = VEC_last (thread_points_p, ppc_threads);
|
||||
hw_breaks = p->hw_breaks;
|
||||
|
||||
/* Copy that thread's breakpoints and watchpoints to the new thread. */
|
||||
for (i = 0; i < max_slots_number; i++)
|
||||
if (hw_breaks[i].hw_break)
|
||||
booke_insert_point (hw_breaks[i].hw_break, tid);
|
||||
}
|
||||
else
|
||||
ptrace (PTRACE_SET_DEBUGREG, tid, 0, saved_dabr_value);
|
||||
}
|
||||
|
||||
static void
|
||||
ppc_linux_thread_exit (struct thread_info *tp, int silent)
|
||||
{
|
||||
int i;
|
||||
int tid = TIDGET (tp->ptid);
|
||||
struct hw_break_tuple *hw_breaks;
|
||||
struct thread_points *t = NULL, *p;
|
||||
|
||||
if (!have_ptrace_booke_interface ())
|
||||
return;
|
||||
|
||||
for (i = 0; VEC_iterate (thread_points_p, ppc_threads, i, p); i++)
|
||||
if (p->tid == tid)
|
||||
{
|
||||
t = p;
|
||||
break;
|
||||
}
|
||||
|
||||
if (t == NULL)
|
||||
return;
|
||||
|
||||
VEC_unordered_remove (thread_points_p, ppc_threads, i);
|
||||
|
||||
hw_breaks = t->hw_breaks;
|
||||
|
||||
for (i = 0; i < max_slots_number; i++)
|
||||
if (hw_breaks[i].hw_break)
|
||||
xfree (hw_breaks[i].hw_break);
|
||||
|
||||
xfree (t->hw_breaks);
|
||||
xfree (t);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -1408,6 +1866,29 @@ ppc_linux_stopped_data_address (struct target_ops *target, CORE_ADDR *addr_p)
|
|||
|| (siginfo_p->si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
|
||||
return 0;
|
||||
|
||||
if (have_ptrace_booke_interface ())
|
||||
{
|
||||
int i;
|
||||
struct thread_points *t;
|
||||
struct hw_break_tuple *hw_breaks;
|
||||
/* The index (or slot) of the *point is passed in the si_errno field. */
|
||||
int slot = siginfo_p->si_errno;
|
||||
|
||||
t = booke_find_thread_points_by_tid (TIDGET (inferior_ptid), 0);
|
||||
|
||||
/* Find out if this *point is a hardware breakpoint.
|
||||
If so, we should return 0. */
|
||||
if (t)
|
||||
{
|
||||
hw_breaks = t->hw_breaks;
|
||||
for (i = 0; i < max_slots_number; i++)
|
||||
if (hw_breaks[i].hw_break && hw_breaks[i].slot == slot
|
||||
&& hw_breaks[i].hw_break->trigger_type
|
||||
== PPC_BREAKPOINT_TRIGGER_EXECUTE)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
*addr_p = (CORE_ADDR) (uintptr_t) siginfo_p->si_addr;
|
||||
return 1;
|
||||
}
|
||||
|
@ -1426,7 +1907,10 @@ ppc_linux_watchpoint_addr_within_range (struct target_ops *target,
|
|||
{
|
||||
int mask;
|
||||
|
||||
if (ppc_linux_get_hwcap () & PPC_FEATURE_BOOKE)
|
||||
if (have_ptrace_booke_interface ()
|
||||
&& ppc_linux_get_hwcap () & PPC_FEATURE_BOOKE)
|
||||
return start <= addr && start + length >= addr;
|
||||
else if (ppc_linux_get_hwcap () & PPC_FEATURE_BOOKE)
|
||||
mask = 3;
|
||||
else
|
||||
mask = 7;
|
||||
|
@ -1643,8 +2127,10 @@ _initialize_ppc_linux_nat (void)
|
|||
t->to_fetch_registers = ppc_linux_fetch_inferior_registers;
|
||||
t->to_store_registers = ppc_linux_store_inferior_registers;
|
||||
|
||||
/* Add our watchpoint methods. */
|
||||
t->to_can_use_hw_breakpoint = ppc_linux_check_watch_resources;
|
||||
/* Add our breakpoint/watchpoint methods. */
|
||||
t->to_can_use_hw_breakpoint = ppc_linux_can_use_hw_breakpoint;
|
||||
t->to_insert_hw_breakpoint = ppc_linux_insert_hw_breakpoint;
|
||||
t->to_remove_hw_breakpoint = ppc_linux_remove_hw_breakpoint;
|
||||
t->to_region_ok_for_hw_watchpoint = ppc_linux_region_ok_for_hw_watchpoint;
|
||||
t->to_insert_watchpoint = ppc_linux_insert_watchpoint;
|
||||
t->to_remove_watchpoint = ppc_linux_remove_watchpoint;
|
||||
|
@ -1655,6 +2141,8 @@ _initialize_ppc_linux_nat (void)
|
|||
t->to_read_description = ppc_linux_read_description;
|
||||
t->to_auxv_parse = ppc_linux_auxv_parse;
|
||||
|
||||
observer_attach_thread_exit (ppc_linux_thread_exit);
|
||||
|
||||
/* Register the target. */
|
||||
linux_nat_add_target (t);
|
||||
linux_nat_set_new_thread (t, ppc_linux_new_thread);
|
||||
|
|
Loading…
Reference in New Issue