#include "common-defs.h"
#include "break-common.h"
+#include "nat/linux-nat.h"
#include "aarch64-linux-hw-point.h"
#include <sys/uio.h>
*next_len_p = len;
}
+struct aarch64_dr_update_callback_param
+{
+ int is_watchpoint;
+ unsigned int idx;
+};
+
+/* Callback for iterate_over_lwps. Records the
+ information about the change of one hardware breakpoint/watchpoint
+ setting for the thread LWP.
+ The information is passed in via PTR.
+ N.B. The actual updating of hardware debug registers is not
+ carried out until the moment the thread is resumed. */
+
+static int
+debug_reg_change_callback (struct lwp_info *lwp, void *ptr)
+{
+ struct aarch64_dr_update_callback_param *param_p
+ = (struct aarch64_dr_update_callback_param *) ptr;
+ int tid = ptid_get_lwp (ptid_of_lwp (lwp));
+ int idx = param_p->idx;
+ int is_watchpoint = param_p->is_watchpoint;
+ struct arch_lwp_info *info = lwp_arch_private_info (lwp);
+ dr_changed_t *dr_changed_ptr;
+ dr_changed_t dr_changed;
+
+ if (info == NULL)
+ {
+ info = XCNEW (struct arch_lwp_info);
+ lwp_set_arch_private_info (lwp, info);
+ }
+
+ if (show_debug_regs)
+ {
+ debug_printf ("debug_reg_change_callback: \n\tOn entry:\n");
+ debug_printf ("\ttid%d, dr_changed_bp=0x%s, "
+ "dr_changed_wp=0x%s\n", tid,
+ phex (info->dr_changed_bp, 8),
+ phex (info->dr_changed_wp, 8));
+ }
+
+ dr_changed_ptr = is_watchpoint ? &info->dr_changed_wp
+ : &info->dr_changed_bp;
+ dr_changed = *dr_changed_ptr;
+
+ gdb_assert (idx >= 0
+ && (idx <= (is_watchpoint ? aarch64_num_wp_regs
+ : aarch64_num_bp_regs)));
+
+ /* The actual update is done later just before resuming the lwp,
+ we just mark that one register pair needs updating. */
+ DR_MARK_N_CHANGED (dr_changed, idx);
+ *dr_changed_ptr = dr_changed;
+
+ /* If the lwp isn't stopped, force it to momentarily pause, so
+ we can update its debug registers. */
+ if (!lwp_is_stopped (lwp))
+ linux_stop_lwp (lwp);
+
+ if (show_debug_regs)
+ {
+ debug_printf ("\tOn exit:\n\ttid%d, dr_changed_bp=0x%s, "
+ "dr_changed_wp=0x%s\n", tid,
+ phex (info->dr_changed_bp, 8),
+ phex (info->dr_changed_wp, 8));
+ }
+
+ return 0;
+}
+
+/* Notify each thread that their IDXth breakpoint/watchpoint register
+ pair needs to be updated. The message will be recorded in each
+ thread's arch-specific data area, the actual updating will be done
+ when the thread is resumed. */
+
+static void
+aarch64_notify_debug_reg_change (const struct aarch64_debug_reg_state *state,
+ int is_watchpoint, unsigned int idx)
+{
+ struct aarch64_dr_update_callback_param param;
+ ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (current_lwp_ptid ()));
+
+ param.is_watchpoint = is_watchpoint;
+ param.idx = idx;
+
+ iterate_over_lwps (pid_ptid, debug_reg_change_callback, (void *) ¶m);
+}
+
/* Record the insertion of one breakpoint/watchpoint, as represented
by ADDR and CTRL, in the process' arch-specific data area *STATE. */
i, core_addr_to_string_nz (state->dr_addr_wp[i]),
state->dr_ctrl_wp[i], state->dr_ref_count_wp[i]);
}
+
+/* Get the hardware debug register capacity information from the
+ process represented by TID. */
+
+void
+aarch64_linux_get_debug_reg_capacity (int tid)
+{
+ struct iovec iov;
+ struct user_hwdebug_state dreg_state;
+
+ iov.iov_base = &dreg_state;
+ iov.iov_len = sizeof (dreg_state);
+
+ /* Get hardware watchpoint register info. */
+ if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_HW_WATCH, &iov) == 0
+ && AARCH64_DEBUG_ARCH (dreg_state.dbg_info) == AARCH64_DEBUG_ARCH_V8)
+ {
+ aarch64_num_wp_regs = AARCH64_DEBUG_NUM_SLOTS (dreg_state.dbg_info);
+ if (aarch64_num_wp_regs > AARCH64_HWP_MAX_NUM)
+ {
+ warning (_("Unexpected number of hardware watchpoint registers"
+ " reported by ptrace, got %d, expected %d."),
+ aarch64_num_wp_regs, AARCH64_HWP_MAX_NUM);
+ aarch64_num_wp_regs = AARCH64_HWP_MAX_NUM;
+ }
+ }
+ else
+ {
+ warning (_("Unable to determine the number of hardware watchpoints"
+ " available."));
+ aarch64_num_wp_regs = 0;
+ }
+
+ /* Get hardware breakpoint register info. */
+ if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_HW_BREAK, &iov) == 0
+ && AARCH64_DEBUG_ARCH (dreg_state.dbg_info) == AARCH64_DEBUG_ARCH_V8)
+ {
+ aarch64_num_bp_regs = AARCH64_DEBUG_NUM_SLOTS (dreg_state.dbg_info);
+ if (aarch64_num_bp_regs > AARCH64_HBP_MAX_NUM)
+ {
+ warning (_("Unexpected number of hardware breakpoint registers"
+ " reported by ptrace, got %d, expected %d."),
+ aarch64_num_bp_regs, AARCH64_HBP_MAX_NUM);
+ aarch64_num_bp_regs = AARCH64_HBP_MAX_NUM;
+ }
+ }
+ else
+ {
+ warning (_("Unable to determine the number of hardware breakpoints"
+ " available."));
+ aarch64_num_bp_regs = 0;
+ }
+}
+
+/* Return true if we can watch a memory region that starts address
+ ADDR and whose length is LEN in bytes. */
+
+int
+aarch64_linux_region_ok_for_watchpoint (CORE_ADDR addr, int len)
+{
+ CORE_ADDR aligned_addr;
+
+ /* Can not set watchpoints for zero or negative lengths. */
+ if (len <= 0)
+ return 0;
+
+ /* Must have hardware watchpoint debug register(s). */
+ if (aarch64_num_wp_regs == 0)
+ return 0;
+
+ /* We support unaligned watchpoint address and arbitrary length,
+ as long as the size of the whole watched area after alignment
+ doesn't exceed size of the total area that all watchpoint debug
+ registers can watch cooperatively.
+
+ This is a very relaxed rule, but unfortunately there are
+ limitations, e.g. false-positive hits, due to limited support of
+ hardware debug registers in the kernel. See comment above
+ aarch64_align_watchpoint for more information. */
+
+ aligned_addr = addr & ~(AARCH64_HWP_MAX_LEN_PER_REG - 1);
+ if (aligned_addr + aarch64_num_wp_regs * AARCH64_HWP_MAX_LEN_PER_REG
+ < addr + len)
+ return 0;
+
+ /* All tests passed so we are likely to be able to set the watchpoint.
+ The reason that it is 'likely' rather than 'must' is because
+ we don't check the current usage of the watchpoint registers, and
+ there may not be enough registers available for this watchpoint.
+ Ideally we should check the cached debug register state, however
+ the checking is costly. */
+ return 1;
+}