This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[PATCH 2/2] Support the new PPC476 processor -- Arch Dependent
- From: Sérgio Durigan Júnior <sergiodj at linux dot vnet dot ibm dot com>
- To: gdb-patches at sourceware dot org
- Cc: Thiago Jung Bauermann <bauerman at br dot ibm dot com>, Luis Machado <luisgpm at linux dot vnet dot ibm dot com>, Matt Tyrlik <tyrlik at us dot ibm dot com>
- Date: Wed, 16 Dec 2009 18:47:20 -0200
- Subject: [PATCH 2/2] Support the new PPC476 processor -- Arch Dependent
Hi,
This is the patch that implements the architecture-dependent (specific) logic
for PowerPC, which will handle the creation/deletion of the new (and old)
types of hardware breakpoints/watchpoints. It also defines a bunch of new
methods in the target_ops, so that other archs can implement them in the
future.
Regards,
--
Sérgio Durigan Júnior
Linux on Power Toolchain - Software Engineer
Linux Technology Center - LTC
IBM Brazil
gdb/ChangeLog:
2009-16-12 Sergio Durigan Junior <sergiodj@linux.vnet.ibm.com>
* i386-nat.c (i386_region_ok_for_watchpoint): Adding `is_big_blob'
parameter.
* spu-multiarch.c (spu_region_ok_for_hw_watchpoint): Ditto.
* ppc-linux-nat.c: Including `breakpoint.h'.
(have_ptrace_new_debug_booke): New flag.
(booke_debug_info): New variable.
(max_slots_number): Ditto.
(struct thread_points): New struct.
(ppc_threads): New variable.
(PPC_DEBUG_CURRENT_VERSION): New define.
(ppc_linux_can_use_hw_breakpoint): Handling BookE processors.
(ppc_linux_can_use_special_hw_point_p): New function.
(ppc_linux_region_ok_for_hw_watchpoint): Adding the `is_big_blob'
parameter. Handling BookE processors.
(booke_alloc_thread_points): New function.
(booke_cmp_hw_point): Ditto.
(booke_select_vector): Ditto.
(booke_find_thread_points_by_tid): Ditto.
(booke_find_thread_points_slot): Ditto.
(booke_insert_point): Ditto.
(booke_remove_point): Ditto.
(booke_linux_insert_normal_hw_breakpoint): Ditto.
(booke_linux_remove_normal_hw_breakpoint): Ditto.
(booke_linux_insert_ranged_hw_breakpoint): Ditto.
(booke_linux_remove_ranged_hw_breakpoint): Ditto.
(booke_linux_insert_hw_breakpoint): Ditto.
(booke_linux_remove_hw_breakpoint): Ditto.
(ppc_linux_insert_hw_breakpoint): Ditto.
(ppc_linux_remove_hw_breakpoint): Ditto.
(HW_WATCH_RW_TRIGGER): New define.
(booke_linux_insert_normal_watchpoint): New function.
(booke_linux_remove_normal_watchpoint): Ditto.
(booke_linux_insert_ranged_watchpoint): Ditto.
(booke_linux_remove_ranged_watchpoint): Ditto.
(booke_linux_insert_watchpoint): Ditto.
(booke_linux_remove_watchpoint): Ditto.
(booke_linux_insert_mask_watchpoint): Ditto.
(booke_linux_remove_mask_watchpoint): Ditto.
(booke_linux_fill_cond_mode): Ditto.
(booke_linux_insert_cond_accel_watchpoint): Ditto.
(booke_linux_remove_cond_accel_watchpoint): Ditto.
(ppc_insert_watchpoint): Ditto.
(ppc_remove_watchpoint): Ditto.
(ppc_linux_insert_watchpoint): Handling BookE processors.
(ppc_linux_remove_watchpoint): Handling BookE processors.
(ppc_linux_insert_mask_watchpoint): New function.
(ppc_linux_remove_mask_watchpoint): Ditto.
(ppc_linux_insert_cond_accel_watchpoint): Ditto.
(ppc_linux_remove_cond_accel_watchpoint): Ditto.
(ppc_linux_new_thread): Handling non-BookE processors.
(ppc_linux_stopped_data_address): Handling BookE processors.
(ppc_linux_watchpoint_addr_within_range): Handling BookE processors
with and without the new kernel interface.
(booke_linux_get_watchpoint_cond_accel_addr): New function.
(ppc_linux_get_watchpoint_cond_accel_addr): Ditto.
(booke_linux_can_use_watchpoint_cond_accel_p): Ditto.
(ppc_linux_can_use_watchpoint_cond_accel_p): Ditto.
(ppc_linux_hw_point_extra_slot_count): Ditto.
(ppc_linux_read_description): Querying the target to know if it
supports the new kernel BookE interface.
(_initialize_ppc_linux_nat): Initializing fields of the target
operations.
* target.c (default_region_ok_for_hw_watchpoint): Added `is_big_blob'
parameter.
(debug_to_region_ok_for_hw_watchpoint): Ditto.
(update_current_target): Inserting the new methods.
* target.h (struct target_ops <to_can_use_special_hw_point_p>,
<to_can_use_watchpoint_cond_accel_p>, <to_insert_mask_watchpoint>,
<to_remove_mask_watchpoint>, <to_insert_cond_accel_watchpoint>,
<to_remove_cond_accel_watchpoint>,
<to_get_watchpoint_cond_accel_addr>,
<to_hw_point_extra_slot_count>): New callbacks.
(TARGET_CAN_USE_SPECIAL_HW_POINT_P): New define.
(TARGET_CAN_USE_WATCHPOINT_COND_ACCEL_P): Ditto.
(TARGET_GET_WATCHPOINT_COND_ACCEL_ADDR): Ditto.
(target_insert_mask_watchpoint): Ditto.
(target_remove_mask_watchpoint): Ditto.
(target_insert_cond_accel_watchpoint): Ditto.
(target_remove_cond_accel_watchpoint): Ditto.
(target_hw_point_extra_slot_count): Ditto.
diff --git a/gdb/i386-nat.c b/gdb/i386-nat.c
index edb78bf..d065c74 100644
--- a/gdb/i386-nat.c
+++ b/gdb/i386-nat.c
@@ -530,8 +530,8 @@ i386_remove_watchpoint (CORE_ADDR addr, int len, int type)
/* Return non-zero if we can watch a memory region that starts at
address ADDR and whose length is LEN bytes. */
-static int
-i386_region_ok_for_watchpoint (CORE_ADDR addr, int len)
+int
+i386_region_ok_for_watchpoint (CORE_ADDR addr, int len, int is_big_blob)
{
int nregs;
diff --git a/gdb/ppc-linux-nat.c b/gdb/ppc-linux-nat.c
index 47b74ad..296dfcd 100644
--- a/gdb/ppc-linux-nat.c
+++ b/gdb/ppc-linux-nat.c
@@ -28,6 +28,8 @@
#include "target.h"
#include "linux-nat.h"
+#include "breakpoint.h"
+
#include <stdint.h>
#include <sys/types.h>
#include <sys/param.h>
@@ -100,7 +102,8 @@
#define PTRACE_SETEVRREGS 21
#endif
-/* Similarly for the hardware watchpoint support. */
+/* Similarly for the hardware watchpoint support. This request is used when
+ the BookE kernel interface is not available. */
#ifndef PTRACE_GET_DEBUGREG
#define PTRACE_GET_DEBUGREG 25
#endif
@@ -248,6 +251,10 @@ int have_ptrace_getsetregs = 1;
them and gotten an error. */
int have_ptrace_getsetfpregs = 1;
+/* Non-zero if we support the new Kernel interface that enables
+ new BOOKE debugging resources. */
+int have_ptrace_new_debug_booke = 1;
+
/* *INDENT-OFF* */
/* registers layout, as presented by the ptrace interface:
PT_R0, PT_R1, PT_R2, PT_R3, PT_R4, PT_R5, PT_R6, PT_R7,
@@ -1270,66 +1277,751 @@ store_ppc_registers (const struct regcache *regcache, int tid)
store_spe_register (regcache, tid, -1);
}
+/* Fetch the AT_HWCAP entry from the aux vector. */
+unsigned long ppc_linux_get_hwcap (void)
+{
+ CORE_ADDR field;
+
+ if (target_auxv_search (¤t_target, AT_HWCAP, &field))
+ return (unsigned long) field;
+
+ 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;
+
+/* 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 these vectors corresponds to a hardware
+ breakpoint or watchpoint in the thread represented by TID. The
+ maximum size of these vectors is stored at MAX_SLOTS_NUMBER. */
+ struct ppc_hw_breakpoint **hw_breaks;
+ struct ppc_hw_breakpoint **hw_watchs;
+ } *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
+
static int
-ppc_linux_check_watch_resources (int type, int cnt, int ot)
+ppc_linux_can_use_hw_breakpoint (int type, int cnt, int ot)
{
- int tid;
- ptid_t ptid = inferior_ptid;
+ int total_hw_wp, total_hw_bp;
- /* 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;
+ if (have_ptrace_new_debug_booke)
+ {
+ /* 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;
+ }
- /* 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 (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_new_debug_booke)
+ {
+ ptid_t ptid;
+ int tid;
+
+ /* 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;
+ }
- 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)
+static int
+ppc_linux_can_use_special_hw_point_p (enum hw_point_flag flag)
{
- CORE_ADDR field;
+ uint64_t features = booke_debug_info.features;
- if (target_auxv_search (¤t_target, AT_HWCAP, &field))
- return (unsigned long) field;
+ if (!have_ptrace_new_debug_booke)
+ return 0;
+
+ switch (flag)
+ {
+ case HW_POINT_RANGED_BREAK:
+ return features & PPC_DEBUG_FEATURE_INSN_BP_RANGE;
+ case HW_POINT_RANGED_WATCH:
+ return features & PPC_DEBUG_FEATURE_DATA_BP_RANGE;
+ case HW_POINT_MASKED_WATCH:
+ return features & PPC_DEBUG_FEATURE_DATA_BP_MASK;
+ case HW_POINT_COND_HW_ACCEL:
+ return booke_debug_info.num_condition_regs > 0;
+ /* We also accept non-special *points. */
+ case HW_POINT_NONE:
+ return 1;
+ }
return 0;
}
static int
-ppc_linux_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
+ppc_linux_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len, int is_big_blob)
{
/* Handle sub-8-byte quantities. */
if (len <= 0)
return 0;
+ /* DAC-based processors (i.e., embedded processors), like the PowerPC 440
+ have ranged watchpoints and can watch arbitrary lengths. This takes
+ two hardware watchpoints though. */
+ if (ppc_linux_can_use_special_hw_point_p (HW_POINT_RANGED_WATCH))
+ return is_big_blob ? 2 : 1;
/* addr+len must fall in the 8 byte watchable region for DABR-based
- processors. DAC-based processors, like the PowerPC 440, 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)
+ processors (i.e., server processors). */
+ else if (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 allocates a thread_points that will hold information
+ about hardware *points for an specific TID. */
+static struct thread_points *
+booke_alloc_thread_points (void)
+{
+ struct thread_points *p = xmalloc (sizeof (struct thread_points));
+ int size = max_slots_number + 1;
+
+ p->hw_breaks = xzalloc (size * sizeof (struct thread_points *));
+ p->hw_watchs = xzalloc (size * sizeof (struct thread_points *));
+
+ return p;
+}
+
+/* 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 is used to select which vector (i.e., `hw_breaks' or
+ `hw_watchs') need to be used for the ppc_hw_breakpoint provided. It
+ also sets the SIZE variable to the corresponding size of the vector. */
+static void
+booke_select_vector (struct thread_points *p,
+ struct ppc_hw_breakpoint *b,
+ int *size, struct ppc_hw_breakpoint ***vector)
+{
+ *size = max_slots_number + 1;
+ if (b->trigger_type == PPC_BREAKPOINT_TRIGGER_EXECUTE)
+ /* Dealing with a hardware breakpoint. */
+ *vector = p->hw_breaks;
+ else
+ /* Dealing with a hardware watchpoint. */
+ *vector = p->hw_watchs;
+}
+
+/* This function can be used to retrieve a thread_points by
+ the TID of the related process/thread. If nothing has been
+ found, it returns NULL. */
+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;
+
+ /* Did the user request us to allocate a new point_item
+ if the wanted one does not exist? */
+ if (alloc_new)
+ {
+ t = booke_alloc_thread_points ();
+ t->tid = tid;
+ VEC_safe_push (thread_points_p, ppc_threads, t);
+ }
+
+ return t;
+}
+
+/* This function finds the slot number where the ppc_hw_breakpoint B
+ is located inside the vector (`hw_breaks' or `hw_watchs'). It returns
+ the slot number if found, or issues an error call otherwise. */
+static int
+booke_find_thread_points_slot (struct thread_points *p,
+ struct ppc_hw_breakpoint *b)
+{
+ int i, size;
+ struct ppc_hw_breakpoint **t;
+
+ booke_select_vector (p, b, &size, &t);
+
+ for (i = 0; i < size; i++)
+ if (t[i] && booke_cmp_hw_point (t[i], b))
+ return i;
+
+ /* If we have reached this point, it means we were not able to
+ find the slot number for this specific *point. It is an error,
+ and should be reported. */
+ error (_("Could not find the slot for this specific \
+breakpoint/watchpoint (located at `0x%lx')."),
+ (unsigned long) b->addr);
+
+ return -1;
+}
+
+/* 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 at GDB. */
+static void
+booke_insert_point (struct ppc_hw_breakpoint *b, int tid)
+{
+ struct ppc_hw_breakpoint *p = xmalloc (sizeof (struct ppc_hw_breakpoint));
+ struct ppc_hw_breakpoint **t;
+ struct cleanup *c = make_cleanup (xfree, p);
+ struct thread_points *h;
+ int slot, size;
+
+ memcpy (p, b, sizeof (struct ppc_hw_breakpoint));
+
+ errno = 0;
+ if ((slot = ptrace (PPC_PTRACE_SETHWDEBUG, tid, 0, p)) < 0)
+ perror_with_name (_("\n\
+Please review your watchpoints/breakpoints and make sure the mask, ranges \n\
+and addresses are within acceptable limits."));
+
+ /* Everything went fine, so we have to register this *point. */
+ h = booke_find_thread_points_by_tid (tid, 1);
+ gdb_assert (h != NULL);
+
+ /* Registering the hardware *point. */
+ booke_select_vector (h, p, &size, &t);
+ gdb_assert (t[slot] == NULL);
+ t[slot] = p;
+
+ 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)
+{
+ struct ppc_hw_breakpoint **t;
+ struct thread_points *cur;
+ int slot, size;
+
+ cur = booke_find_thread_points_by_tid (tid, 0);
+ gdb_assert (cur != NULL);
+ slot = booke_find_thread_points_slot (cur, b);
+
+ errno = 0;
+ if (ptrace (PPC_PTRACE_DELHWDEBUG, tid, 0, slot) < 0)
+ perror_with_name (_("\n\
+Could not delete the breakpoint/watchpoint."));
+
+ /* Unregistering the hardware *point. */
+ booke_select_vector (cur, b, &size, &t);
+ gdb_assert (t[slot] != NULL);
+ xfree (t[slot]);
+ t[slot] = NULL;
+}
+
+static int
+booke_linux_insert_normal_hw_breakpoint (unsigned long addr, int tid)
+{
+ struct ppc_hw_breakpoint p;
+
+ 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) addr;
+ p.addr2 = 0;
+ p.condition_value = 0;
+
+ booke_insert_point (&p, tid);
+
+ return 0;
+}
+
+static int
+booke_linux_remove_normal_hw_breakpoint (unsigned long addr, int tid)
+{
+ struct ppc_hw_breakpoint p;
+
+ 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) addr;
+ p.addr2 = 0;
+ p.condition_value = 0;
+
+ booke_remove_point (&p, tid);
+
+ return 0;
+}
+
+static int
+booke_linux_insert_ranged_hw_breakpoint (unsigned long begin,
+ unsigned long end,
+ int tid)
+{
+ struct ppc_hw_breakpoint p;
+
+ p.version = PPC_DEBUG_CURRENT_VERSION;
+ p.trigger_type = PPC_BREAKPOINT_TRIGGER_EXECUTE;
+ p.addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
+ p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
+ p.addr = (uint64_t) begin;
+ p.addr2 = (uint64_t) end;
+ p.condition_value = 0;
+
+ booke_insert_point (&p, tid);
+
+ return 0;
+}
+
+static int
+booke_linux_remove_ranged_hw_breakpoint (unsigned long begin,
+ unsigned long end,
+ int tid)
+{
+ struct ppc_hw_breakpoint p;
+
+ p.version = PPC_DEBUG_CURRENT_VERSION;
+ p.trigger_type = PPC_BREAKPOINT_TRIGGER_EXECUTE;
+ p.addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
+ p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
+ p.addr = (uint64_t) begin;
+ p.addr2 = (uint64_t) end;
+ p.condition_value = 0;
+
+ booke_remove_point (&p, tid);
+
+ return 0;
+}
+
+static int
+booke_linux_insert_hw_breakpoint (struct gdbarch *gdbarch,
+ struct bp_target_info *bp_tgt)
+{
+ struct lwp_info *lp;
+ ptid_t ptid;
+ int ret;
+
+ if (bp_tgt->length == 0)
+ {
+ unsigned long addr = (unsigned long) bp_tgt->placed_address;
+
+ ALL_LWPS (lp, ptid)
+ ret = booke_linux_insert_normal_hw_breakpoint (addr, TIDGET (ptid));
+ }
+ else
+ {
+ unsigned long begin = (unsigned long) bp_tgt->placed_address,
+ end = (unsigned long) bp_tgt->placed_address +
+ (unsigned long) bp_tgt->length;
+
+ ALL_LWPS (lp, ptid)
+ ret = booke_linux_insert_ranged_hw_breakpoint (begin, end,
+ TIDGET (ptid));
+ }
+
+ return ret;
+}
+
+static int
+booke_linux_remove_hw_breakpoint (struct gdbarch *gdbarch,
+ struct bp_target_info *bp_tgt)
+{
+ struct lwp_info *lp;
+ ptid_t ptid;
+ int ret;
+
+ if (bp_tgt->length == 0)
+ {
+ unsigned long addr = (unsigned long) bp_tgt->placed_address;
+
+ ALL_LWPS (lp, ptid)
+ ret = booke_linux_remove_normal_hw_breakpoint (addr, TIDGET (ptid));
+ }
+ else
+ {
+ unsigned long begin = (unsigned long) bp_tgt->placed_address,
+ end = (unsigned long) bp_tgt->placed_address +
+ (unsigned long) bp_tgt->length;
+
+ ALL_LWPS (lp, ptid)
+ ret = booke_linux_remove_ranged_hw_breakpoint (begin, end,
+ TIDGET (ptid));
+ }
+
+ return ret;
+}
+
+static int
+ppc_linux_insert_hw_breakpoint (struct gdbarch *gdbarch,
+ struct bp_target_info *bp_tgt)
+{
+ if (have_ptrace_new_debug_booke)
+ return booke_linux_insert_hw_breakpoint (gdbarch, bp_tgt);
+
+ return -1;
+}
+
+static int
+ppc_linux_remove_hw_breakpoint (struct gdbarch *gdbarch,
+ struct bp_target_info *bp_tgt)
+{
+ if (have_ptrace_new_debug_booke)
+ return booke_linux_remove_hw_breakpoint (gdbarch, bp_tgt);
+
+ return -1;
+}
+
+#define HW_WATCH_RW_TRIGGER(t, rw) \
+ 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;
+
+static int
+booke_linux_insert_normal_watchpoint (unsigned long addr, int tid, int rw)
+{
+ struct ppc_hw_breakpoint p;
+ uint32_t trigger;
+
+ HW_WATCH_RW_TRIGGER (trigger, rw);
+
+ p.version = PPC_DEBUG_CURRENT_VERSION;
+ p.trigger_type = trigger;
+ 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;
+
+ booke_insert_point (&p, tid);
+
+ return 0;
+}
+
+static int
+booke_linux_remove_normal_watchpoint (unsigned long addr, int tid, int rw)
+{
+ struct ppc_hw_breakpoint p;
+ uint32_t trigger;
+
+ HW_WATCH_RW_TRIGGER (trigger, rw);
+
+ p.version = PPC_DEBUG_CURRENT_VERSION;
+ p.trigger_type = trigger;
+ 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;
+
+ booke_remove_point (&p, tid);
+
+ return 0;
+}
+
+static int
+booke_linux_insert_ranged_watchpoint (unsigned long begin,
+ unsigned long end,
+ int tid, int rw)
+{
+ struct ppc_hw_breakpoint p;
+ uint32_t trigger;
+
+ HW_WATCH_RW_TRIGGER (trigger, rw);
+
+ p.version = PPC_DEBUG_CURRENT_VERSION;
+ p.trigger_type = trigger;
+ p.addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
+ p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
+ p.addr = (uint64_t) begin;
+ p.addr2 = (uint64_t) end;
+ p.condition_value = 0;
+
+ booke_insert_point (&p, tid);
+
+ return 0;
+}
+
+static int
+booke_linux_remove_ranged_watchpoint (unsigned long begin,
+ unsigned long end,
+ int tid, int rw)
+{
+ struct ppc_hw_breakpoint p;
+ uint32_t trigger;
+
+ HW_WATCH_RW_TRIGGER (trigger, rw);
+
+ p.version = PPC_DEBUG_CURRENT_VERSION;
+ p.trigger_type = trigger;
+ p.addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
+ p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
+ p.addr = (uint64_t) begin;
+ p.addr2 = (uint64_t) end;
+ p.condition_value = 0;
+
+ booke_remove_point (&p, tid);
+
+ return 0;
+}
+
+static int
+booke_linux_insert_watchpoint (CORE_ADDR addr, int len, int rw)
+{
+ struct lwp_info *lp;
+ ptid_t ptid;
+ int ret;
+
+ if (len <= 4)
+ {
+ unsigned long a = (unsigned long) addr;
+
+ ALL_LWPS (lp, ptid)
+ ret = booke_linux_insert_normal_watchpoint (a, TIDGET (ptid), rw);
+ }
+ else
+ {
+ unsigned long begin = (unsigned long) addr,
+ end = (unsigned long) addr + (unsigned long) len;
+
+ ALL_LWPS (lp, ptid)
+ ret = booke_linux_insert_ranged_watchpoint (begin, end,
+ TIDGET (ptid), rw);
+ }
+ return ret;
+}
+
+static int
+booke_linux_remove_watchpoint (CORE_ADDR addr, int len, int rw)
+{
+ struct lwp_info *lp;
+ ptid_t ptid;
+ int ret;
+
+ if (len <= 4)
+ {
+ unsigned long a = (unsigned long) addr;
+
+ ALL_LWPS (lp, ptid)
+ ret = booke_linux_remove_normal_watchpoint (a, TIDGET (ptid), rw);
+ }
+ else
+ {
+ unsigned long begin = (unsigned long) addr,
+ end = (unsigned long) addr + (unsigned long) len;
+
+ ALL_LWPS (lp, ptid)
+ ret = booke_linux_remove_ranged_watchpoint (begin, end,
+ TIDGET (ptid), rw);
+ }
+ return ret;
+}
+
+static int
+booke_linux_insert_mask_watchpoint (CORE_ADDR addr, int len, int rw,
+ CORE_ADDR mask)
+{
+ struct ppc_hw_breakpoint p;
+ uint32_t trigger;
+ unsigned long a = (unsigned long) addr,
+ m = (unsigned long) mask;
+ struct lwp_info *lp;
+ ptid_t ptid;
+
+ HW_WATCH_RW_TRIGGER (trigger, rw);
+
+ p.version = PPC_DEBUG_CURRENT_VERSION;
+ p.trigger_type = trigger;
+ p.addr_mode = PPC_BREAKPOINT_MODE_MASK;
+ p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
+ p.addr = (uint64_t) a;
+ p.addr2 = (uint64_t) m;
+ p.condition_value = 0;
+
+ ALL_LWPS (lp, ptid)
+ booke_insert_point (&p, TIDGET (ptid));
+
+ return 0;
+}
+
+static int
+booke_linux_remove_mask_watchpoint (CORE_ADDR addr, int len, int rw,
+ CORE_ADDR mask)
+{
+ struct ppc_hw_breakpoint p;
+ uint32_t trigger;
+ unsigned long a = (unsigned long) addr,
+ m = (unsigned long) mask;
+ struct lwp_info *lp;
+ ptid_t ptid;
+
+ HW_WATCH_RW_TRIGGER (trigger, rw);
+
+ p.version = PPC_DEBUG_CURRENT_VERSION;
+ p.trigger_type = trigger;
+ p.addr_mode = PPC_BREAKPOINT_MODE_MASK;
+ p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
+ p.addr = (uint64_t) a;
+ p.addr2 = (uint64_t) m;
+ p.condition_value = 0;
+
+ ALL_LWPS (lp, ptid)
+ booke_remove_point (&p, TIDGET (ptid));
+
+ return 0;
+}
+
+static inline uint32_t
+booke_linux_fill_cond_mode (int byte_to_enable)
+{
+ uint32_t mode = PPC_BREAKPOINT_CONDITION_AND;
+
+ switch (byte_to_enable)
+ {
+ case 0:
+ mode |= PPC_BREAKPOINT_CONDITION_BE (0);
+ case 1:
+ mode |= PPC_BREAKPOINT_CONDITION_BE (1);
+ case 2:
+ mode |= PPC_BREAKPOINT_CONDITION_BE (2);
+ case 3:
+ mode |= PPC_BREAKPOINT_CONDITION_BE (3);
+ break;
+ default:
+ error (_("Wrong byte to enable for the hardware watchpoint \
+with a condition associated."));
+ }
+
+ return mode;
+}
+
+static int
+booke_linux_insert_cond_accel_watchpoint (CORE_ADDR addr, int len, int rw,
+ CORE_ADDR cond)
+{
+ struct ppc_hw_breakpoint p;
+ uint32_t trigger;
+ unsigned long a = (unsigned long) addr,
+ c = (unsigned long) cond;
+ struct lwp_info *lp;
+ ptid_t ptid;
+ int byte_to_enable;
+
+ HW_WATCH_RW_TRIGGER (trigger, rw);
+
+ byte_to_enable = a % 4;
+ c >>= (byte_to_enable * 8);
+
+ p.version = PPC_DEBUG_CURRENT_VERSION;
+ p.trigger_type = trigger;
+ p.addr_mode = PPC_BREAKPOINT_MODE_EXACT;
+ p.condition_mode = booke_linux_fill_cond_mode (byte_to_enable);
+ p.addr = (uint64_t) a;
+ p.addr2 = 0;
+ p.condition_value = (uint64_t) c;
+
+ ALL_LWPS (lp, ptid)
+ booke_insert_point (&p, TIDGET (ptid));
+
+ return 0;
+}
+
+static int
+booke_linux_remove_cond_accel_watchpoint (CORE_ADDR addr, int len, int rw,
+ CORE_ADDR cond)
+{
+ struct ppc_hw_breakpoint p;
+ uint32_t trigger;
+ unsigned long a = (unsigned long) addr,
+ c = (unsigned long) cond;
+ struct lwp_info *lp;
+ ptid_t ptid;
+ int byte_to_enable;
+
+ HW_WATCH_RW_TRIGGER (trigger, rw);
+
+ byte_to_enable = a % 4;
+ c >>= (byte_to_enable * 8);
+
+ p.version = PPC_DEBUG_CURRENT_VERSION;
+ p.trigger_type = trigger;
+ p.addr_mode = PPC_BREAKPOINT_MODE_EXACT;
+ p.condition_mode = booke_linux_fill_cond_mode (byte_to_enable);
+ p.addr = (uint64_t) a;
+ p.addr2 = 0;
+ p.condition_value = (uint64_t) c;
+
+ ALL_LWPS (lp, ptid)
+ booke_remove_point (&p, TIDGET (ptid));
+
+ return 0;
+}
/* Set a watchpoint of type TYPE at address ADDR. */
static int
-ppc_linux_insert_watchpoint (CORE_ADDR addr, int len, int rw)
+ppc_insert_watchpoint (CORE_ADDR addr, int len, int rw)
{
struct lwp_info *lp;
ptid_t ptid;
@@ -1337,19 +2029,19 @@ ppc_linux_insert_watchpoint (CORE_ADDR addr, int len, int rw)
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;
- }
+ {
+ /* 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;
- }
+ {
+ /* 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)
@@ -1378,7 +2070,7 @@ ppc_linux_insert_watchpoint (CORE_ADDR addr, int len, int rw)
}
static int
-ppc_linux_remove_watchpoint (CORE_ADDR addr, int len, int rw)
+ppc_remove_watchpoint (CORE_ADDR addr, int len, int rw)
{
struct lwp_info *lp;
ptid_t ptid;
@@ -1391,10 +2083,70 @@ ppc_linux_remove_watchpoint (CORE_ADDR addr, int len, int rw)
return 0;
}
+static int
+ppc_linux_insert_watchpoint (CORE_ADDR addr, int len,
+ int rw)
+{
+ if (have_ptrace_new_debug_booke)
+ return booke_linux_insert_watchpoint (addr, len, rw);
+
+ return ppc_insert_watchpoint (addr, len, rw);
+}
+
+static int
+ppc_linux_remove_watchpoint (CORE_ADDR addr, int len, int rw)
+{
+ if (have_ptrace_new_debug_booke)
+ return booke_linux_remove_watchpoint (addr, len, rw);
+
+ return ppc_remove_watchpoint (addr, len, rw);
+}
+
+static int
+ppc_linux_insert_mask_watchpoint (CORE_ADDR addr, int len, int rw,
+ CORE_ADDR mask)
+{
+ if (have_ptrace_new_debug_booke)
+ return booke_linux_insert_mask_watchpoint (addr, len, rw, mask);
+
+ return -1;
+}
+
+static int
+ppc_linux_remove_mask_watchpoint (CORE_ADDR addr, int len, int rw,
+ CORE_ADDR mask)
+{
+ if (have_ptrace_new_debug_booke)
+ return booke_linux_remove_mask_watchpoint (addr, len, rw, mask);
+
+ return -1;
+}
+
+static int
+ppc_linux_insert_cond_accel_watchpoint (CORE_ADDR addr, int len,
+ int rw, CORE_ADDR cond)
+{
+ if (have_ptrace_new_debug_booke)
+ return booke_linux_insert_cond_accel_watchpoint (addr, len, rw, cond);
+
+ return -1;
+}
+
+static int
+ppc_linux_remove_cond_accel_watchpoint (CORE_ADDR addr, int len,
+ int rw, CORE_ADDR cond)
+{
+ if (have_ptrace_new_debug_booke)
+ return booke_linux_remove_cond_accel_watchpoint (addr, len, rw, cond);
+
+ return -1;
+}
+
static void
ppc_linux_new_thread (ptid_t ptid)
{
- ptrace (PTRACE_SET_DEBUGREG, TIDGET (ptid), 0, saved_dabr_value);
+ if (!have_ptrace_new_debug_booke)
+ ptrace (PTRACE_SET_DEBUGREG, TIDGET (ptid), 0, saved_dabr_value);
}
static int
@@ -1408,6 +2160,21 @@ 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_new_debug_booke)
+ {
+ struct thread_points *p;
+ /* The index (or slot) of the *point is passed in the si_errno field. */
+ int slot = siginfo_p->si_errno;
+
+ p = booke_find_thread_points_by_tid (TIDGET (inferior_ptid), 0);
+
+ /* If the SLOT position on hw_break vector is not NULL, it means
+ that this specific *point is a hardware breakpoint. So, we
+ should return 0. */
+ if (p && p->hw_breaks[slot])
+ return 0;
+ }
+
*addr_p = (CORE_ADDR) (uintptr_t) siginfo_p->si_addr;
return 1;
}
@@ -1426,7 +2193,10 @@ ppc_linux_watchpoint_addr_within_range (struct target_ops *target,
{
int mask;
- if (ppc_linux_get_hwcap () & PPC_FEATURE_BOOKE)
+ if (have_ptrace_new_debug_booke
+ && 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;
@@ -1437,6 +2207,88 @@ ppc_linux_watchpoint_addr_within_range (struct target_ops *target,
return start <= addr + mask && start + length - 1 >= addr;
}
+static int
+booke_linux_get_watchpoint_cond_accel_addr (struct bp_location *b,
+ CORE_ADDR *addr)
+{
+ return (default_watch_address_if_address_equal_literal_p (b, addr)
+ || default_watch_var_if_var_equal_literal_p (b, addr)
+ || default_watch_var_if_address_equal_literal_p (b, addr)
+ || default_watch_address_if_var_equal_literal_p (b, addr));
+}
+
+static int
+ppc_linux_get_watchpoint_cond_accel_addr (struct bp_location *b,
+ CORE_ADDR *addr)
+{
+ if (have_ptrace_new_debug_booke)
+ return booke_linux_get_watchpoint_cond_accel_addr (b, addr);
+
+ *addr = 0;
+ return 0;
+}
+
+static int
+booke_linux_can_use_watchpoint_cond_accel_p (struct bp_location *b)
+{
+ struct thread_points *p;
+ int tid = TIDGET (inferior_ptid);
+ int cnt = booke_debug_info.num_condition_regs, i;
+ CORE_ADDR tmp_value;
+
+ p = booke_find_thread_points_by_tid (tid, 0);
+
+ if (p)
+ {
+ for (i = 0; i < booke_debug_info.num_data_bps; i++)
+ if (p->hw_watchs[i]
+ && p->hw_watchs[i]->condition_mode != PPC_BREAKPOINT_CONDITION_NONE)
+ cnt--;
+
+ /* There are no available slots now. */
+ if (cnt <= 0)
+ return 0;
+ }
+
+ /* We have to know if the condition associated with the bp_location
+ can be hardware-accelerated. */
+ return (booke_linux_get_watchpoint_cond_accel_addr (b, &tmp_value));
+}
+
+static int
+ppc_linux_can_use_watchpoint_cond_accel_p (struct bp_location *b)
+{
+ if (have_ptrace_new_debug_booke)
+ return booke_linux_can_use_watchpoint_cond_accel_p (b);
+
+ return 0;
+}
+
+static int
+ppc_linux_hw_point_extra_slot_count (enum hw_point_flag flag)
+{
+ /* If this *point is a:
+
+ - Masked hardware watchpoint,
+ - Ranged hardware watchpoint, or
+ - Ranged hardware breakpoint
+
+ then it uses 1 extra slot. */
+ switch (flag)
+ {
+ case HW_POINT_MASKED_WATCH:
+ case HW_POINT_RANGED_WATCH:
+ case HW_POINT_RANGED_BREAK:
+ return 1;
+ default:
+ return 0;
+ }
+
+ /* Otherwise, it is a normal *point and does not use
+ extra slots. */
+ return 0;
+}
+
static void
ppc_linux_store_inferior_registers (struct target_ops *ops,
struct regcache *regcache, int regno)
@@ -1595,6 +2447,20 @@ ppc_linux_read_description (struct target_ops *ops)
perror_with_name (_("Unable to fetch AltiVec registers"));
}
+ /* Check for kernel support for new BOOKE debugging registers. */
+ if (ptrace (PPC_PTRACE_GETHWDBGINFO, tid, 0, &booke_debug_info) >= 0)
+ {
+ have_ptrace_new_debug_booke = 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 new BOOKE registers support. */
+ have_ptrace_new_debug_booke = 0;
+ memset (&booke_debug_info, 0, sizeof (struct ppc_debug_info));
+ }
+
/* Power ISA 2.05 (implemented by Power 6 and newer processors) increases
the FPSCR from 32 bits to 64 bits. Even though Power 7 supports this
ISA version, it doesn't have PPC_FEATURE_ARCH_2_05 set, only
@@ -1643,14 +2509,26 @@ _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_can_use_special_hw_point_p = ppc_linux_can_use_special_hw_point_p;
+ t->to_insert_hw_breakpoint = booke_linux_insert_hw_breakpoint;
+ t->to_remove_hw_breakpoint = booke_linux_remove_hw_breakpoint;
t->to_region_ok_for_hw_watchpoint = ppc_linux_region_ok_for_hw_watchpoint;
+ t->to_get_watchpoint_cond_accel_addr =
+ ppc_linux_get_watchpoint_cond_accel_addr;
+ t->to_can_use_watchpoint_cond_accel_p =
+ ppc_linux_can_use_watchpoint_cond_accel_p;
t->to_insert_watchpoint = ppc_linux_insert_watchpoint;
t->to_remove_watchpoint = ppc_linux_remove_watchpoint;
+ t->to_insert_mask_watchpoint = ppc_linux_insert_mask_watchpoint;
+ t->to_remove_mask_watchpoint = ppc_linux_remove_mask_watchpoint;
+ t->to_insert_cond_accel_watchpoint = ppc_linux_insert_cond_accel_watchpoint;
+ t->to_remove_cond_accel_watchpoint = ppc_linux_remove_cond_accel_watchpoint;
t->to_stopped_by_watchpoint = ppc_linux_stopped_by_watchpoint;
t->to_stopped_data_address = ppc_linux_stopped_data_address;
t->to_watchpoint_addr_within_range = ppc_linux_watchpoint_addr_within_range;
+ t->to_hw_point_extra_slot_count = ppc_linux_hw_point_extra_slot_count;
t->to_read_description = ppc_linux_read_description;
t->to_auxv_parse = ppc_linux_auxv_parse;
diff --git a/gdb/spu-multiarch.c b/gdb/spu-multiarch.c
index b5d2ee6..0f23e07 100644
--- a/gdb/spu-multiarch.c
+++ b/gdb/spu-multiarch.c
@@ -121,7 +121,7 @@ spu_thread_architecture (struct target_ops *ops, ptid_t ptid)
/* Override the to_region_ok_for_hw_watchpoint routine. */
static int
-spu_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
+spu_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len, int is_big_blob)
{
struct target_ops *ops_beneath = find_target_beneath (&spu_ops);
while (ops_beneath && !ops_beneath->to_region_ok_for_hw_watchpoint)
@@ -132,7 +132,8 @@ spu_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
return 0;
if (ops_beneath)
- return ops_beneath->to_region_ok_for_hw_watchpoint (addr, len);
+ return ops_beneath->to_region_ok_for_hw_watchpoint (addr, len,
+ is_big_blob);
return 0;
}
diff --git a/gdb/target.c b/gdb/target.c
index cd1614b..9f4a3fc 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -53,7 +53,7 @@ static void default_terminal_info (char *, int);
static int default_watchpoint_addr_within_range (struct target_ops *,
CORE_ADDR, CORE_ADDR, int);
-static int default_region_ok_for_hw_watchpoint (CORE_ADDR, int);
+static int default_region_ok_for_hw_watchpoint (CORE_ADDR, int, int);
static int nosymbol (char *, CORE_ADDR *);
@@ -131,7 +131,7 @@ static int debug_to_stopped_data_address (struct target_ops *, CORE_ADDR *);
static int debug_to_watchpoint_addr_within_range (struct target_ops *,
CORE_ADDR, CORE_ADDR, int);
-static int debug_to_region_ok_for_hw_watchpoint (CORE_ADDR, int);
+static int debug_to_region_ok_for_hw_watchpoint (CORE_ADDR, int, int);
static void debug_to_terminal_init (void);
@@ -620,16 +620,24 @@ update_current_target (void)
INHERIT (to_insert_breakpoint, t);
INHERIT (to_remove_breakpoint, t);
INHERIT (to_can_use_hw_breakpoint, t);
+ INHERIT (to_can_use_special_hw_point_p, t);
+ INHERIT (to_can_use_watchpoint_cond_accel_p, t);
INHERIT (to_insert_hw_breakpoint, t);
INHERIT (to_remove_hw_breakpoint, t);
INHERIT (to_insert_watchpoint, t);
INHERIT (to_remove_watchpoint, t);
+ INHERIT (to_insert_mask_watchpoint, t);
+ INHERIT (to_remove_mask_watchpoint, t);
+ INHERIT (to_insert_cond_accel_watchpoint, t);
+ INHERIT (to_remove_cond_accel_watchpoint, t);
+ INHERIT (to_get_watchpoint_cond_accel_addr, t);
INHERIT (to_stopped_data_address, t);
INHERIT (to_have_steppable_watchpoint, t);
INHERIT (to_have_continuable_watchpoint, t);
INHERIT (to_stopped_by_watchpoint, t);
INHERIT (to_watchpoint_addr_within_range, t);
INHERIT (to_region_ok_for_hw_watchpoint, t);
+ INHERIT (to_hw_point_extra_slot_count, t);
INHERIT (to_terminal_init, t);
INHERIT (to_terminal_inferior, t);
INHERIT (to_terminal_ours_for_output, t);
@@ -725,6 +733,12 @@ update_current_target (void)
de_fault (to_can_use_hw_breakpoint,
(int (*) (int, int, int))
return_zero);
+ de_fault (to_can_use_special_hw_point_p,
+ (int (*) (enum hw_point_flag))
+ return_zero);
+ de_fault (to_can_use_watchpoint_cond_accel_p,
+ (int (*) (struct bp_location *))
+ return_zero);
de_fault (to_insert_hw_breakpoint,
(int (*) (struct gdbarch *, struct bp_target_info *))
return_minus_one);
@@ -737,6 +751,18 @@ update_current_target (void)
de_fault (to_remove_watchpoint,
(int (*) (CORE_ADDR, int, int))
return_minus_one);
+ de_fault (to_insert_mask_watchpoint,
+ (int (*) (CORE_ADDR, int, int, CORE_ADDR))
+ return_minus_one);
+ de_fault (to_remove_mask_watchpoint,
+ (int (*) (CORE_ADDR, int, int, CORE_ADDR))
+ return_minus_one);
+ de_fault (to_insert_cond_accel_watchpoint,
+ (int (*) (CORE_ADDR, int, int, CORE_ADDR))
+ return_minus_one);
+ de_fault (to_remove_cond_accel_watchpoint,
+ (int (*) (CORE_ADDR, int, int, CORE_ADDR))
+ return_minus_one);
de_fault (to_stopped_by_watchpoint,
(int (*) (void))
return_zero);
@@ -747,6 +773,12 @@ update_current_target (void)
default_watchpoint_addr_within_range);
de_fault (to_region_ok_for_hw_watchpoint,
default_region_ok_for_hw_watchpoint);
+ de_fault (to_hw_point_extra_slot_count,
+ (int (*) (enum hw_point_flag))
+ return_zero);
+ de_fault (to_get_watchpoint_cond_accel_addr,
+ (int (*) (struct bp_location *, CORE_ADDR *))
+ return_zero);
de_fault (to_terminal_init,
(void (*) (void))
target_ignore);
@@ -2598,7 +2630,7 @@ Can't determine the current address space of thread %s\n",
}
static int
-default_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
+default_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len, int is_big_blob)
{
return (len <= gdbarch_ptr_bit (target_gdbarch) / TARGET_CHAR_BIT);
}
@@ -3128,11 +3160,11 @@ debug_to_can_use_hw_breakpoint (int type, int cnt, int from_tty)
}
static int
-debug_to_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
+debug_to_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len, int is_big_blob)
{
CORE_ADDR retval;
- retval = debug_target.to_region_ok_for_hw_watchpoint (addr, len);
+ retval = debug_target.to_region_ok_for_hw_watchpoint (addr, len, is_big_blob);
fprintf_unfiltered (gdb_stdlog,
"target_region_ok_for_hw_watchpoint (%ld, %ld) = 0x%lx\n",
diff --git a/gdb/target.h b/gdb/target.h
index ebe6056..027c380 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -32,6 +32,11 @@ struct bp_target_info;
struct regcache;
struct target_section_table;
+struct bp_location;
+struct breakpoint;
+
+enum hw_point_flag;
+
/* This include file defines the interface between the main part
of the debugger, and the part which is target-specific, or
specific to the communications interface between us and the
@@ -400,17 +405,26 @@ struct target_ops
int (*to_insert_breakpoint) (struct gdbarch *, struct bp_target_info *);
int (*to_remove_breakpoint) (struct gdbarch *, struct bp_target_info *);
int (*to_can_use_hw_breakpoint) (int, int, int);
+ int (*to_can_use_special_hw_point_p) (enum hw_point_flag);
+ int (*to_can_use_watchpoint_cond_accel_p) (struct bp_location *);
int (*to_insert_hw_breakpoint) (struct gdbarch *, struct bp_target_info *);
int (*to_remove_hw_breakpoint) (struct gdbarch *, struct bp_target_info *);
int (*to_remove_watchpoint) (CORE_ADDR, int, int);
int (*to_insert_watchpoint) (CORE_ADDR, int, int);
+ int (*to_insert_mask_watchpoint) (CORE_ADDR, int, int, CORE_ADDR);
+ int (*to_remove_mask_watchpoint) (CORE_ADDR, int, int, CORE_ADDR);
+ int (*to_insert_cond_accel_watchpoint) (CORE_ADDR, int, int, CORE_ADDR);
+ int (*to_remove_cond_accel_watchpoint) (CORE_ADDR, int, int, CORE_ADDR);
int (*to_stopped_by_watchpoint) (void);
int to_have_steppable_watchpoint;
int to_have_continuable_watchpoint;
int (*to_stopped_data_address) (struct target_ops *, CORE_ADDR *);
int (*to_watchpoint_addr_within_range) (struct target_ops *,
CORE_ADDR, CORE_ADDR, int);
- int (*to_region_ok_for_hw_watchpoint) (CORE_ADDR, int);
+ int (*to_get_watchpoint_cond_accel_addr) (struct bp_location *,
+ CORE_ADDR *);
+ int (*to_region_ok_for_hw_watchpoint) (CORE_ADDR, int, int);
+ int (*to_hw_point_extra_slot_count) (enum hw_point_flag);
void (*to_terminal_init) (void);
void (*to_terminal_inferior) (void);
void (*to_terminal_ours_for_output) (void);
@@ -1184,9 +1198,29 @@ extern char *normal_pid_to_str (ptid_t ptid);
#define target_can_use_hardware_watchpoint(TYPE,CNT,OTHERTYPE) \
(*current_target.to_can_use_hw_breakpoint) (TYPE, CNT, OTHERTYPE);
-#define target_region_ok_for_hw_watchpoint(addr, len) \
- (*current_target.to_region_ok_for_hw_watchpoint) (addr, len)
-
+/* Returns non-zero if the target supports the special type of hardware
+ breakpoint/watchpoint represented by FLAG. */
+#ifndef TARGET_CAN_USE_SPECIAL_HW_POINT_P
+#define TARGET_CAN_USE_SPECIAL_HW_POINT_P(flag) \
+ (*current_target.to_can_use_special_hw_point_p) (flag)
+#endif
+
+#ifndef TARGET_REGION_OK_FOR_HW_WATCHPOINT
+#define TARGET_REGION_OK_FOR_HW_WATCHPOINT(addr, len, is_big_blob) \
+ (*current_target.to_region_ok_for_hw_watchpoint) (addr, len, is_big_blob)
+#endif
+
+/* Returns greater than zero if the target supports hardware-accelerated
+ condition. */
+#ifndef TARGET_CAN_USE_WATCHPOINT_COND_ACCEL_P
+#define TARGET_CAN_USE_WATCHPOINT_COND_ACCEL_P(loc) \
+ (*current_target.to_can_use_watchpoint_cond_accel_p) (loc)
+#endif
+
+#ifndef TARGET_GET_WATCHPOINT_COND_ACCEL_ADDR
+#define TARGET_GET_WATCHPOINT_COND_ACCEL_ADDR(loc, addr) \
+ (*current_target.to_get_watchpoint_cond_accel_addr) (loc, addr)
+#endif
/* Set/clear a hardware watchpoint starting at ADDR, for LEN bytes. TYPE is 0
for write, 1 for read, and 2 for read/write accesses. Returns 0 for
@@ -1198,6 +1232,29 @@ extern char *normal_pid_to_str (ptid_t ptid);
#define target_remove_watchpoint(addr, len, type) \
(*current_target.to_remove_watchpoint) (addr, len, type)
+/* Hardware watchpoints with a mask associated. */
+#ifndef target_insert_mask_watchpoint
+#define target_insert_mask_watchpoint(addr, len, type, mask) \
+ (*current_target.to_insert_mask_watchpoint) (addr, len, type, mask)
+#endif
+
+#ifndef target_remove_mask_watchpoint
+#define target_remove_mask_watchpoint(addr, len, type, mask) \
+ (*current_target.to_remove_mask_watchpoint) (addr, len, type, mask)
+#endif
+
+/* Hardware watchpoint with a condition associated (to be
+ hardware-accelerated). */
+#ifndef target_insert_cond_accel_watchpoint
+#define target_insert_cond_accel_watchpoint(addr, len, type, cond) \
+ (*current_target.to_insert_cond_accel_watchpoint) (addr, len, type, cond)
+#endif
+
+#ifndef target_remove_cond_accel_watchpoint
+#define target_remove_cond_accel_watchpoint(addr, len, type, cond) \
+ (*current_target.to_remove_cond_accel_watchpoint) (addr, len, type, cond)
+#endif
+
#define target_insert_hw_breakpoint(gdbarch, bp_tgt) \
(*current_target.to_insert_hw_breakpoint) (gdbarch, bp_tgt)
@@ -1213,6 +1270,11 @@ extern char *normal_pid_to_str (ptid_t ptid);
#define target_watchpoint_addr_within_range(target, addr, start, length) \
(*target.to_watchpoint_addr_within_range) (target, addr, start, length)
+#ifndef target_hw_point_extra_slot_count
+#define target_hw_point_extra_slot_count(flag) \
+ (*current_target.to_hw_point_extra_slot_count) (flag)
+#endif
+
/* Target can execute in reverse? */
#define target_can_execute_reverse \
(current_target.to_can_execute_reverse ? \