+ 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)
+{
+ /* Handle sub-8-byte quantities. */
+ 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 ())
+ {
+ /* DAC-based processors (i.e., embedded processors), like the PowerPC 440
+ have ranged watchpoints and can watch any access within an arbitrary
+ memory region. This is useful to watch arrays and structs, for
+ instance. It takes two hardware watchpoints though. */
+ if (len > 1
+ && booke_debug_info.features & PPC_DEBUG_FEATURE_DATA_BP_RANGE)
+ return 2;
+ else 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 (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 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;
+}
+
+/* This function compares two ppc_hw_breakpoint structs field-by-field. */
+static 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;
+}
+
+/* Return the number of registers needed for a ranged breakpoint. */
+
+static int
+ppc_linux_ranged_break_num_registers (struct target_ops *target)
+{
+ return ((have_ptrace_booke_interface ()
+ && booke_debug_info.features & PPC_DEBUG_FEATURE_INSN_BP_RANGE)?
+ 2 : -1);
+}
+
+/* Insert the hardware breakpoint described by BP_TGT. Returns 0 for
+ success, 1 if hardware breakpoints are not supported or -1 for failure. */
+
+static int
+ppc_linux_insert_hw_breakpoint (struct gdbarch *gdbarch,
+ struct bp_target_info *bp_tgt)
+{
+ 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.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
+ p.addr = (uint64_t) bp_tgt->placed_address;
+ p.condition_value = 0;
+
+ if (bp_tgt->length)
+ {
+ p.addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
+
+ /* The breakpoint will trigger if the address of the instruction is
+ within the defined range, as follows: p.addr <= address < p.addr2. */
+ p.addr2 = (uint64_t) bp_tgt->placed_address + bp_tgt->length;
+ }
+ else
+ {
+ p.addr_mode = PPC_BREAKPOINT_MODE_EXACT;
+ p.addr2 = 0;
+ }
+
+ ALL_LWPS (lp)
+ booke_insert_point (&p, TIDGET (lp->ptid));
+
+ return 0;
+}
+
+static int
+ppc_linux_remove_hw_breakpoint (struct gdbarch *gdbarch,
+ struct bp_target_info *bp_tgt)
+{
+ 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.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
+ p.addr = (uint64_t) bp_tgt->placed_address;
+ p.condition_value = 0;
+
+ if (bp_tgt->length)
+ {
+ p.addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
+
+ /* The breakpoint will trigger if the address of the instruction is within
+ the defined range, as follows: p.addr <= address < p.addr2. */
+ p.addr2 = (uint64_t) bp_tgt->placed_address + bp_tgt->length;
+ }
+ else
+ {
+ p.addr_mode = PPC_BREAKPOINT_MODE_EXACT;
+ p.addr2 = 0;
+ }
+
+ ALL_LWPS (lp)
+ booke_remove_point (&p, TIDGET (lp->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;
+}
+
+/* Insert a new masked watchpoint at ADDR using the mask MASK.
+ RW may be hw_read for a read watchpoint, hw_write for a write watchpoint
+ or hw_access for an access watchpoint. Returns 0 on success and throws
+ an error on failure. */
+
+static int
+ppc_linux_insert_mask_watchpoint (struct target_ops *ops, CORE_ADDR addr,
+ CORE_ADDR mask, int rw)
+{
+ struct lwp_info *lp;
+ struct ppc_hw_breakpoint p;
+
+ gdb_assert (have_ptrace_booke_interface ());
+
+ p.version = PPC_DEBUG_CURRENT_VERSION;
+ p.trigger_type = get_trigger_type (rw);
+ p.addr_mode = PPC_BREAKPOINT_MODE_MASK;
+ p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
+ p.addr = addr;
+ p.addr2 = mask;
+ p.condition_value = 0;
+
+ ALL_LWPS (lp)
+ booke_insert_point (&p, TIDGET (lp->ptid));
+
+ return 0;
+}
+
+/* Remove a masked watchpoint at ADDR with the mask MASK.
+ RW may be hw_read for a read watchpoint, hw_write for a write watchpoint
+ or hw_access for an access watchpoint. Returns 0 on success and throws
+ an error on failure. */
+
+static int
+ppc_linux_remove_mask_watchpoint (struct target_ops *ops, CORE_ADDR addr,
+ CORE_ADDR mask, int rw)
+{
+ struct lwp_info *lp;
+ struct ppc_hw_breakpoint p;
+
+ gdb_assert (have_ptrace_booke_interface ());
+
+ p.version = PPC_DEBUG_CURRENT_VERSION;
+ p.trigger_type = get_trigger_type (rw);
+ p.addr_mode = PPC_BREAKPOINT_MODE_MASK;
+ p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
+ p.addr = addr;
+ p.addr2 = mask;
+ p.condition_value = 0;
+
+ ALL_LWPS (lp)
+ booke_remove_point (&p, TIDGET (lp->ptid));
+
+ return 0;
+}
+
+/* Check whether we have at least one free DVC register. */
+static int
+can_use_watchpoint_cond_accel (void)
+{
+ struct thread_points *p;