#include "aarch64-tdep.h"
#include "aarch64-linux-tdep.h"
#include "aarch32-linux-nat.h"
+#include "nat/aarch64-linux.h"
+#include "nat/aarch64-linux-hw-point.h"
#include "elf/external.h"
#include "elf/common.h"
-#include <sys/ptrace.h>
+#include "nat/gdb_ptrace.h"
#include <sys/utsname.h>
#include <asm/ptrace.h>
#define TRAP_HWBKPT 0x0004
#endif
-/* On GNU/Linux, threads are implemented as pseudo-processes, in which
- case we may be tracing more than one process at a time. In that
- case, inferior_ptid will contain the main process ID and the
- individual thread (process) ID. get_thread_id () is used to get
- the thread id if it's available, and the process id otherwise. */
-
-static int
-get_thread_id (ptid_t ptid)
-{
- int tid = ptid_get_lwp (ptid);
-
- if (0 == tid)
- tid = ptid_get_pid (ptid);
- return tid;
-}
-
-/* Macro definitions, data structures, and code for the hardware
- breakpoint and hardware watchpoint support follow. We use the
- following abbreviations throughout the code:
-
- hw - hardware
- bp - breakpoint
- wp - watchpoint */
-
-/* Maximum number of hardware breakpoint and watchpoint registers.
- Neither of these values may exceed the width of dr_changed_t
- measured in bits. */
-
-#define AARCH64_HBP_MAX_NUM 16
-#define AARCH64_HWP_MAX_NUM 16
-
-/* Alignment requirement in bytes for addresses written to
- hardware breakpoint and watchpoint value registers.
-
- A ptrace call attempting to set an address that does not meet the
- alignment criteria will fail. Limited support has been provided in
- this port for unaligned watchpoints, such that from a GDB user
- perspective, an unaligned watchpoint may be requested.
-
- This is achieved by minimally enlarging the watched area to meet the
- alignment requirement, and if necessary, splitting the watchpoint
- over several hardware watchpoint registers. */
-
-#define AARCH64_HBP_ALIGNMENT 4
-#define AARCH64_HWP_ALIGNMENT 8
-
-/* The maximum length of a memory region that can be watched by one
- hardware watchpoint register. */
-
-#define AARCH64_HWP_MAX_LEN_PER_REG 8
-
-/* ptrace hardware breakpoint resource info is formatted as follows:
-
- 31 24 16 8 0
- +---------------+--------------+---------------+---------------+
- | RESERVED | RESERVED | DEBUG_ARCH | NUM_SLOTS |
- +---------------+--------------+---------------+---------------+ */
-
-
-/* Macros to extract fields from the hardware debug information word. */
-#define AARCH64_DEBUG_NUM_SLOTS(x) ((x) & 0xff)
-#define AARCH64_DEBUG_ARCH(x) (((x) >> 8) & 0xff)
-
-/* Macro for the expected version of the ARMv8-A debug architecture. */
-#define AARCH64_DEBUG_ARCH_V8 0x6
-
-/* Number of hardware breakpoints/watchpoints the target supports.
- They are initialized with values obtained via the ptrace calls
- with NT_ARM_HW_BREAK and NT_ARM_HW_WATCH respectively. */
-
-static int aarch64_num_bp_regs;
-static int aarch64_num_wp_regs;
-
-/* Each bit of a variable of this type is used to indicate whether a
- hardware breakpoint or watchpoint setting has been changed since
- the last update.
-
- Bit N corresponds to the Nth hardware breakpoint or watchpoint
- setting which is managed in aarch64_debug_reg_state, where N is
- valid between 0 and the total number of the hardware breakpoint or
- watchpoint debug registers minus 1.
-
- When bit N is 1, the corresponding breakpoint or watchpoint setting
- has changed, and therefore the corresponding hardware debug
- register needs to be updated via the ptrace interface.
-
- In the per-thread arch-specific data area, we define two such
- variables for per-thread hardware breakpoint and watchpoint
- settings respectively.
-
- This type is part of the mechanism which helps reduce the number of
- ptrace calls to the kernel, i.e. avoid asking the kernel to write
- to the debug registers with unchanged values. */
-
-typedef ULONGEST dr_changed_t;
-
-/* Set each of the lower M bits of X to 1; assert X is wide enough. */
-
-#define DR_MARK_ALL_CHANGED(x, m) \
- do \
- { \
- gdb_assert (sizeof ((x)) * 8 >= (m)); \
- (x) = (((dr_changed_t)1 << (m)) - 1); \
- } while (0)
-
-#define DR_MARK_N_CHANGED(x, n) \
- do \
- { \
- (x) |= ((dr_changed_t)1 << (n)); \
- } while (0)
-
-#define DR_CLEAR_CHANGED(x) \
- do \
- { \
- (x) = 0; \
- } while (0)
-
-#define DR_HAS_CHANGED(x) ((x) != 0)
-#define DR_N_HAS_CHANGED(x, n) ((x) & ((dr_changed_t)1 << (n)))
-
-/* Structure for managing the hardware breakpoint/watchpoint resources.
- DR_ADDR_* stores the address, DR_CTRL_* stores the control register
- content, and DR_REF_COUNT_* counts the numbers of references to the
- corresponding bp/wp, by which way the limited hardware resources
- are not wasted on duplicated bp/wp settings (though so far gdb has
- done a good job by not sending duplicated bp/wp requests). */
-
-struct aarch64_debug_reg_state
-{
- /* hardware breakpoint */
- CORE_ADDR dr_addr_bp[AARCH64_HBP_MAX_NUM];
- unsigned int dr_ctrl_bp[AARCH64_HBP_MAX_NUM];
- unsigned int dr_ref_count_bp[AARCH64_HBP_MAX_NUM];
-
- /* hardware watchpoint */
- CORE_ADDR dr_addr_wp[AARCH64_HWP_MAX_NUM];
- unsigned int dr_ctrl_wp[AARCH64_HWP_MAX_NUM];
- unsigned int dr_ref_count_wp[AARCH64_HWP_MAX_NUM];
-};
-
/* Per-process data. We don't bind this to a per-inferior registry
because of targets like x86 GNU/Linux that need to keep track of
processes that aren't bound to any inferior (e.g., fork children,
{
struct aarch64_process_info *proc;
- proc = xcalloc (1, sizeof (*proc));
+ proc = XCNEW (struct aarch64_process_info);
proc->pid = pid;
proc->next = aarch64_process_list;
/* Get debug registers state for process PID. */
-static struct aarch64_debug_reg_state *
+struct aarch64_debug_reg_state *
aarch64_get_debug_reg_state (pid_t pid)
{
return &aarch64_process_info_get (pid)->state;
}
-/* Per-thread arch-specific data we want to keep. */
-
-struct arch_lwp_info
-{
- /* When bit N is 1, it indicates the Nth hardware breakpoint or
- watchpoint register pair needs to be updated when the thread is
- resumed; see aarch64_linux_prepare_to_resume. */
- dr_changed_t dr_changed_bp;
- dr_changed_t dr_changed_wp;
-};
-
-/* Call ptrace to set the thread TID's hardware breakpoint/watchpoint
- registers with data from *STATE. */
-
-static void
-aarch64_linux_set_debug_regs (const struct aarch64_debug_reg_state *state,
- int tid, int watchpoint)
-{
- int i, count;
- struct iovec iov;
- struct user_hwdebug_state regs;
- const CORE_ADDR *addr;
- const unsigned int *ctrl;
-
- memset (®s, 0, sizeof (regs));
- iov.iov_base = ®s;
- count = watchpoint ? aarch64_num_wp_regs : aarch64_num_bp_regs;
- addr = watchpoint ? state->dr_addr_wp : state->dr_addr_bp;
- ctrl = watchpoint ? state->dr_ctrl_wp : state->dr_ctrl_bp;
- if (count == 0)
- return;
- iov.iov_len = (offsetof (struct user_hwdebug_state, dbg_regs[count - 1])
- + sizeof (regs.dbg_regs [count - 1]));
-
- for (i = 0; i < count; i++)
- {
- regs.dbg_regs[i].addr = addr[i];
- regs.dbg_regs[i].ctrl = ctrl[i];
- }
-
- if (ptrace (PTRACE_SETREGSET, tid,
- watchpoint ? NT_ARM_HW_WATCH : NT_ARM_HW_BREAK,
- (void *) &iov))
- error (_("Unexpected error setting hardware debug registers"));
-}
-
-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 pid = get_thread_id (lwp->ptid);
- int idx = param_p->idx;
- int is_watchpoint = param_p->is_watchpoint;
- struct arch_lwp_info *info = lwp->arch_private;
- dr_changed_t *dr_changed_ptr;
- dr_changed_t dr_changed;
-
- if (info == NULL)
- info = lwp->arch_private = XCNEW (struct arch_lwp_info);
-
- if (show_debug_regs)
- {
- fprintf_unfiltered (gdb_stdlog,
- "debug_reg_change_callback: \n\tOn entry:\n");
- fprintf_unfiltered (gdb_stdlog,
- "\tpid%d, dr_changed_bp=0x%s, "
- "dr_changed_wp=0x%s\n",
- pid, 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->stopped)
- linux_stop_lwp (lwp);
-
- if (show_debug_regs)
- {
- fprintf_unfiltered (gdb_stdlog,
- "\tOn exit:\n\tpid%d, dr_changed_bp=0x%s, "
- "dr_changed_wp=0x%s\n",
- pid, phex (info->dr_changed_bp, 8),
- phex (info->dr_changed_wp, 8));
- }
-
- /* Continue the iteration. */
- 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 (inferior_ptid));
-
- param.is_watchpoint = is_watchpoint;
- param.idx = idx;
-
- iterate_over_lwps (pid_ptid, debug_reg_change_callback, (void *) ¶m);
-}
-
-/* Print the values of the cached breakpoint/watchpoint registers. */
-
-static void
-aarch64_show_debug_reg_state (struct aarch64_debug_reg_state *state,
- const char *func, CORE_ADDR addr,
- int len, int type)
-{
- int i;
-
- debug_printf ("%s", func);
- if (addr || len)
- debug_printf (" (addr=0x%08lx, len=%d, type=%s)",
- (unsigned long) addr, len,
- type == hw_write ? "hw-write-watchpoint"
- : (type == hw_read ? "hw-read-watchpoint"
- : (type == hw_access ? "hw-access-watchpoint"
- : (type == hw_execute ? "hw-breakpoint"
- : "??unknown??"))));
- debug_printf (":\n");
-
- debug_printf ("\tBREAKPOINTs:\n");
- for (i = 0; i < aarch64_num_bp_regs; i++)
- debug_printf ("\tBP%d: addr=0x%08lx, ctrl=0x%08x, ref.count=%d\n",
- i, state->dr_addr_bp[i],
- state->dr_ctrl_bp[i], state->dr_ref_count_bp[i]);
-
- debug_printf ("\tWATCHPOINTs:\n");
- for (i = 0; i < aarch64_num_wp_regs; i++)
- debug_printf ("\tWP%d: addr=0x%08lx, ctrl=0x%08x, ref.count=%d\n",
- i, state->dr_addr_wp[i],
- state->dr_ctrl_wp[i], state->dr_ref_count_wp[i]);
-}
-
/* Fill GDB's register array with the general-purpose register values
from the current thread. */
and arm. */
gdb_static_assert (sizeof (regs) >= 18 * 4);
- tid = get_thread_id (inferior_ptid);
+ tid = ptid_get_lwp (inferior_ptid);
iovec.iov_base = ®s;
if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
/* Make sure REGS can hold all registers contents on both aarch64
and arm. */
gdb_static_assert (sizeof (regs) >= 18 * 4);
- tid = get_thread_id (inferior_ptid);
+ tid = ptid_get_lwp (inferior_ptid);
iovec.iov_base = ®s;
if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
and arm. */
gdb_static_assert (sizeof regs >= VFP_REGS_SIZE);
- tid = get_thread_id (inferior_ptid);
+ tid = ptid_get_lwp (inferior_ptid);
iovec.iov_base = ®s;
/* Make sure REGS can hold all VFP registers contents on both aarch64
and arm. */
gdb_static_assert (sizeof regs >= VFP_REGS_SIZE);
- tid = get_thread_id (inferior_ptid);
+ tid = ptid_get_lwp (inferior_ptid);
iovec.iov_base = ®s;
AARCH64_LINUX_SIZEOF_FPREGSET);
}
-/* Called when resuming a thread.
- The hardware debug registers are updated when there is any change. */
-
-static void
-aarch64_linux_prepare_to_resume (struct lwp_info *lwp)
-{
- struct arch_lwp_info *info = lwp->arch_private;
-
- /* NULL means this is the main thread still going through the shell,
- or, no watchpoint has been set yet. In that case, there's
- nothing to do. */
- if (info == NULL)
- return;
-
- if (DR_HAS_CHANGED (info->dr_changed_bp)
- || DR_HAS_CHANGED (info->dr_changed_wp))
- {
- int tid = ptid_get_lwp (lwp->ptid);
- struct aarch64_debug_reg_state *state
- = aarch64_get_debug_reg_state (ptid_get_pid (lwp->ptid));
-
- if (show_debug_regs)
- fprintf_unfiltered (gdb_stdlog, "prepare_to_resume thread %d\n", tid);
-
- /* Watchpoints. */
- if (DR_HAS_CHANGED (info->dr_changed_wp))
- {
- aarch64_linux_set_debug_regs (state, tid, 1);
- DR_CLEAR_CHANGED (info->dr_changed_wp);
- }
-
- /* Breakpoints. */
- if (DR_HAS_CHANGED (info->dr_changed_bp))
- {
- aarch64_linux_set_debug_regs (state, tid, 0);
- DR_CLEAR_CHANGED (info->dr_changed_bp);
- }
- }
-}
-
-static void
-aarch64_linux_new_thread (struct lwp_info *lp)
-{
- struct arch_lwp_info *info = XCNEW (struct arch_lwp_info);
-
- /* Mark that all the hardware breakpoint/watchpoint register pairs
- for this thread need to be initialized. */
- DR_MARK_ALL_CHANGED (info->dr_changed_bp, aarch64_num_bp_regs);
- DR_MARK_ALL_CHANGED (info->dr_changed_wp, aarch64_num_wp_regs);
-
- lp->arch_private = info;
-}
-
/* linux_nat_new_fork hook. */
static void
}
\f
-/* Get the hardware debug register capacity information from the
- inferior represented by PTID. */
-
-static void
-aarch64_linux_get_debug_reg_capacity (ptid_t ptid)
-{
- int tid;
- struct iovec iov;
- struct user_hwdebug_state dreg_state;
-
- tid = ptid_get_pid (ptid);
- 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;
- }
-}
-
static void (*super_post_startup_inferior) (struct target_ops *self,
ptid_t ptid);
ptid_t ptid)
{
aarch64_forget_process (ptid_get_pid (ptid));
- aarch64_linux_get_debug_reg_capacity (ptid);
+ aarch64_linux_get_debug_reg_capacity (ptid_get_pid (ptid));
super_post_startup_inferior (self, ptid);
}
return tdesc_aarch64;
}
-/* Given the (potentially unaligned) watchpoint address in ADDR and
- length in LEN, return the aligned address and aligned length in
- *ALIGNED_ADDR_P and *ALIGNED_LEN_P, respectively. The returned
- aligned address and length will be valid values to write to the
- hardware watchpoint value and control registers.
-
- The given watchpoint may get truncated if more than one hardware
- register is needed to cover the watched region. *NEXT_ADDR_P
- and *NEXT_LEN_P, if non-NULL, will return the address and length
- of the remaining part of the watchpoint (which can be processed
- by calling this routine again to generate another aligned address
- and length pair.
+/* Convert a native/host siginfo object, into/from the siginfo in the
+ layout of the inferiors' architecture. Returns true if any
+ conversion was done; false otherwise. If DIRECTION is 1, then copy
+ from INF to NATIVE. If DIRECTION is 0, copy from NATIVE to
+ INF. */
- See the comment above the function of the same name in
- gdbserver/linux-aarch64-low.c for more information. */
-
-static void
-aarch64_align_watchpoint (CORE_ADDR addr, int len, CORE_ADDR *aligned_addr_p,
- int *aligned_len_p, CORE_ADDR *next_addr_p,
- int *next_len_p)
+static int
+aarch64_linux_siginfo_fixup (siginfo_t *native, gdb_byte *inf, int direction)
{
- int aligned_len;
- unsigned int offset;
- CORE_ADDR aligned_addr;
- const unsigned int alignment = AARCH64_HWP_ALIGNMENT;
- const unsigned int max_wp_len = AARCH64_HWP_MAX_LEN_PER_REG;
-
- /* As assumed by the algorithm. */
- gdb_assert (alignment == max_wp_len);
+ struct gdbarch *gdbarch = get_frame_arch (get_current_frame ());
- if (len <= 0)
- return;
-
- /* Address to be put into the hardware watchpoint value register
- must be aligned. */
- offset = addr & (alignment - 1);
- aligned_addr = addr - offset;
-
- gdb_assert (offset >= 0 && offset < alignment);
- gdb_assert (aligned_addr >= 0 && aligned_addr <= addr);
- gdb_assert (offset + len > 0);
-
- if (offset + len >= max_wp_len)
- {
- /* Need more than one watchpoint registers; truncate it at the
- alignment boundary. */
- aligned_len = max_wp_len;
- len -= (max_wp_len - offset);
- addr += (max_wp_len - offset);
- gdb_assert ((addr & (alignment - 1)) == 0);
- }
- else
+ /* Is the inferior 32-bit? If so, then do fixup the siginfo
+ object. */
+ if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
{
- /* Find the smallest valid length that is large enough to
- accommodate this watchpoint. */
- static const unsigned char
- aligned_len_array[AARCH64_HWP_MAX_LEN_PER_REG] =
- { 1, 2, 4, 4, 8, 8, 8, 8 };
-
- aligned_len = aligned_len_array[offset + len - 1];
- addr += len;
- len = 0;
+ if (direction == 0)
+ aarch64_compat_siginfo_from_siginfo ((struct compat_siginfo *) inf,
+ native);
+ else
+ aarch64_siginfo_from_compat_siginfo (native,
+ (struct compat_siginfo *) inf);
+
+ return 1;
}
- if (aligned_addr_p)
- *aligned_addr_p = aligned_addr;
- if (aligned_len_p)
- *aligned_len_p = aligned_len;
- if (next_addr_p)
- *next_addr_p = addr;
- if (next_len_p)
- *next_len_p = len;
+ return 0;
}
/* Returns the number of hardware watchpoints of type TYPE that we can
bp_read_watchpoint, bp_write_watchpoint, or bp_hardware_breakpoint.
CNT is the number of such watchpoints used so far (including this
one). OTHERTYPE is non-zero if other types of watchpoints are
- currently enabled.
-
- We always return 1 here because we don't have enough information
- about possible overlap of addresses that they want to watch. As an
- extreme example, consider the case where all the watchpoints watch
- the same address and the same region length: then we can handle a
- virtually unlimited number of watchpoints, due to debug register
- sharing implemented via reference counts. */
+ currently enabled. */
static int
aarch64_linux_can_use_hw_breakpoint (struct target_ops *self,
- int type, int cnt, int othertype)
-{
- return 1;
-}
-
-/* ptrace expects control registers to be formatted as follows:
-
- 31 13 5 3 1 0
- +--------------------------------+----------+------+------+----+
- | RESERVED (SBZ) | LENGTH | TYPE | PRIV | EN |
- +--------------------------------+----------+------+------+----+
-
- The TYPE field is ignored for breakpoints. */
-
-#define DR_CONTROL_ENABLED(ctrl) (((ctrl) & 0x1) == 1)
-#define DR_CONTROL_LENGTH(ctrl) (((ctrl) >> 5) & 0xff)
-
-/* Utility function that returns the length in bytes of a watchpoint
- according to the content of a hardware debug control register CTRL.
- Note that the kernel currently only supports the following Byte
- Address Select (BAS) values: 0x1, 0x3, 0xf and 0xff, which means
- that for a hardware watchpoint, its valid length can only be 1
- byte, 2 bytes, 4 bytes or 8 bytes. */
-
-static inline unsigned int
-aarch64_watchpoint_length (unsigned int ctrl)
-{
- switch (DR_CONTROL_LENGTH (ctrl))
- {
- case 0x01:
- return 1;
- case 0x03:
- return 2;
- case 0x0f:
- return 4;
- case 0xff:
- return 8;
- default:
- return 0;
- }
-}
-
-/* Given the hardware breakpoint or watchpoint type TYPE and its
- length LEN, return the expected encoding for a hardware
- breakpoint/watchpoint control register. */
-
-static unsigned int
-aarch64_point_encode_ctrl_reg (int type, int len)
-{
- unsigned int ctrl, ttype;
-
- /* type */
- switch (type)
- {
- case hw_write:
- ttype = 2;
- break;
- case hw_read:
- ttype = 1;
- break;
- case hw_access:
- ttype = 3;
- break;
- case hw_execute:
- ttype = 0;
- break;
- default:
- perror_with_name (_("Unrecognized breakpoint/watchpoint type"));
- }
- ctrl = ttype << 3;
-
- /* length bitmask */
- ctrl |= ((1 << len) - 1) << 5;
- /* enabled at el0 */
- ctrl |= (2 << 1) | 1;
-
- return ctrl;
-}
-
-/* Addresses to be written to the hardware breakpoint and watchpoint
- value registers need to be aligned; the alignment is 4-byte and
- 8-type respectively. Linux kernel rejects any non-aligned address
- it receives from the related ptrace call. Furthermore, the kernel
- currently only supports the following Byte Address Select (BAS)
- values: 0x1, 0x3, 0xf and 0xff, which means that for a hardware
- watchpoint to be accepted by the kernel (via ptrace call), its
- valid length can only be 1 byte, 2 bytes, 4 bytes or 8 bytes.
- Despite these limitations, the unaligned watchpoint is supported in
- this port.
-
- Return 0 for any non-compliant ADDR and/or LEN; return 1 otherwise. */
-
-static int
-aarch64_point_is_aligned (int is_watchpoint, CORE_ADDR addr, int len)
-{
- unsigned int alignment = is_watchpoint ? AARCH64_HWP_ALIGNMENT
- : AARCH64_HBP_ALIGNMENT;
-
- if (addr & (alignment - 1))
- return 0;
-
- if (len != 8 && len != 4 && len != 2 && len != 1)
- return 0;
-
- return 1;
-}
-
-/* Record the insertion of one breakpoint/watchpoint, as represented
- by ADDR and CTRL, in the cached debug register state area *STATE. */
-
-static int
-aarch64_dr_state_insert_one_point (struct aarch64_debug_reg_state *state,
- enum target_hw_bp_type type, CORE_ADDR addr,
- int len)
+ enum bptype type,
+ int cnt, int othertype)
{
- int i, idx, num_regs, is_watchpoint;
- unsigned int ctrl, *dr_ctrl_p, *dr_ref_count;
- CORE_ADDR *dr_addr_p;
-
- /* Set up state pointers. */
- is_watchpoint = (type != hw_execute);
- gdb_assert (aarch64_point_is_aligned (is_watchpoint, addr, len));
- if (is_watchpoint)
+ if (type == bp_hardware_watchpoint || type == bp_read_watchpoint
+ || type == bp_access_watchpoint || type == bp_watchpoint)
{
- num_regs = aarch64_num_wp_regs;
- dr_addr_p = state->dr_addr_wp;
- dr_ctrl_p = state->dr_ctrl_wp;
- dr_ref_count = state->dr_ref_count_wp;
+ if (aarch64_num_wp_regs == 0)
+ return 0;
}
- else
- {
- num_regs = aarch64_num_bp_regs;
- dr_addr_p = state->dr_addr_bp;
- dr_ctrl_p = state->dr_ctrl_bp;
- dr_ref_count = state->dr_ref_count_bp;
- }
-
- ctrl = aarch64_point_encode_ctrl_reg (type, len);
-
- /* Find an existing or free register in our cache. */
- idx = -1;
- for (i = 0; i < num_regs; ++i)
+ else if (type == bp_hardware_breakpoint)
{
- if ((dr_ctrl_p[i] & 1) == 0)
- {
- gdb_assert (dr_ref_count[i] == 0);
- idx = i;
- /* no break; continue hunting for an existing one. */
- }
- else if (dr_addr_p[i] == addr && dr_ctrl_p[i] == ctrl)
- {
- gdb_assert (dr_ref_count[i] != 0);
- idx = i;
- break;
- }
- }
-
- /* No space. */
- if (idx == -1)
- return -1;
-
- /* Update our cache. */
- if ((dr_ctrl_p[idx] & 1) == 0)
- {
- /* new entry */
- dr_addr_p[idx] = addr;
- dr_ctrl_p[idx] = ctrl;
- dr_ref_count[idx] = 1;
- /* Notify the change. */
- aarch64_notify_debug_reg_change (state, is_watchpoint, idx);
+ if (aarch64_num_bp_regs == 0)
+ return 0;
}
else
- {
- /* existing entry */
- dr_ref_count[idx]++;
- }
-
- return 0;
-}
-
-/* Record the removal of one breakpoint/watchpoint, as represented by
- ADDR and CTRL, in the cached debug register state area *STATE. */
-
-static int
-aarch64_dr_state_remove_one_point (struct aarch64_debug_reg_state *state,
- enum target_hw_bp_type type, CORE_ADDR addr,
- int len)
-{
- int i, num_regs, is_watchpoint;
- unsigned int ctrl, *dr_ctrl_p, *dr_ref_count;
- CORE_ADDR *dr_addr_p;
-
- /* Set up state pointers. */
- is_watchpoint = (type != hw_execute);
- gdb_assert (aarch64_point_is_aligned (is_watchpoint, addr, len));
- if (is_watchpoint)
- {
- num_regs = aarch64_num_wp_regs;
- dr_addr_p = state->dr_addr_wp;
- dr_ctrl_p = state->dr_ctrl_wp;
- dr_ref_count = state->dr_ref_count_wp;
- }
- else
- {
- num_regs = aarch64_num_bp_regs;
- dr_addr_p = state->dr_addr_bp;
- dr_ctrl_p = state->dr_ctrl_bp;
- dr_ref_count = state->dr_ref_count_bp;
- }
-
- ctrl = aarch64_point_encode_ctrl_reg (type, len);
-
- /* Find the entry that matches the ADDR and CTRL. */
- for (i = 0; i < num_regs; ++i)
- if (dr_addr_p[i] == addr && dr_ctrl_p[i] == ctrl)
- {
- gdb_assert (dr_ref_count[i] != 0);
- break;
- }
-
- /* Not found. */
- if (i == num_regs)
- return -1;
-
- /* Clear our cache. */
- if (--dr_ref_count[i] == 0)
- {
- /* Clear the enable bit. */
- ctrl &= ~1;
- dr_addr_p[i] = 0;
- dr_ctrl_p[i] = ctrl;
- /* Notify the change. */
- aarch64_notify_debug_reg_change (state, is_watchpoint, i);
- }
-
- return 0;
-}
-
-/* Implement insertion and removal of a single breakpoint. */
-
-static int
-aarch64_handle_breakpoint (enum target_hw_bp_type type, CORE_ADDR addr,
- int len, int is_insert,
- struct aarch64_debug_reg_state *state)
-{
- /* The hardware breakpoint on AArch64 should always be 4-byte
- aligned. */
- if (!aarch64_point_is_aligned (0 /* is_watchpoint */ , addr, len))
- return -1;
-
- if (is_insert)
- return aarch64_dr_state_insert_one_point (state, type, addr, len);
- else
- return aarch64_dr_state_remove_one_point (state, type, addr, len);
+ gdb_assert_not_reached ("unexpected breakpoint type");
+
+ /* We always return 1 here because we don't have enough information
+ about possible overlap of addresses that they want to watch. As an
+ extreme example, consider the case where all the watchpoints watch
+ the same address and the same region length: then we can handle a
+ virtually unlimited number of watchpoints, due to debug register
+ sharing implemented via reference counts. */
+ return 1;
}
/* Insert a hardware-assisted breakpoint at BP_TGT->reqstd_address.
return ret;
}
-/* This is essentially the same as aarch64_handle_breakpoint, apart
- from that it is an aligned watchpoint to be handled. */
-
-static int
-aarch64_handle_aligned_watchpoint (enum target_hw_bp_type type, CORE_ADDR addr,
- int len, int is_insert,
- struct aarch64_debug_reg_state *state)
-{
- if (is_insert)
- return aarch64_dr_state_insert_one_point (state, type, addr, len);
- else
- return aarch64_dr_state_remove_one_point (state, type, addr, len);
-}
-
-/* Insert/remove unaligned watchpoint by calling
- aarch64_align_watchpoint repeatedly until the whole watched region,
- as represented by ADDR and LEN, has been properly aligned and ready
- to be written to one or more hardware watchpoint registers.
- IS_INSERT indicates whether this is an insertion or a deletion.
- Return 0 if succeed. */
-
-static int
-aarch64_handle_unaligned_watchpoint (int type, CORE_ADDR addr, int len,
- int is_insert,
- struct aarch64_debug_reg_state *state)
-{
- while (len > 0)
- {
- CORE_ADDR aligned_addr;
- int aligned_len, ret;
-
- aarch64_align_watchpoint (addr, len, &aligned_addr, &aligned_len,
- &addr, &len);
-
- if (is_insert)
- ret = aarch64_dr_state_insert_one_point (state, type, aligned_addr,
- aligned_len);
- else
- ret = aarch64_dr_state_remove_one_point (state, type, aligned_addr,
- aligned_len);
-
- if (show_debug_regs)
- debug_printf (
-"handle_unaligned_watchpoint: is_insert: %d\n"
-" aligned_addr: 0x%08lx, aligned_len: %d\n"
-" next_addr: 0x%08lx, next_len: %d\n",
- is_insert, aligned_addr, aligned_len, addr, len);
-
- if (ret != 0)
- return ret;
- }
-
- return 0;
-}
-
-/* Implements insertion and removal of a single watchpoint. */
-
-static int
-aarch64_handle_watchpoint (int type, CORE_ADDR addr, int len, int is_insert,
- struct aarch64_debug_reg_state *state)
-{
- if (aarch64_point_is_aligned (1 /* is_watchpoint */ , addr, len))
- return aarch64_handle_aligned_watchpoint (type, addr, len, is_insert,
- state);
- else
- return aarch64_handle_unaligned_watchpoint (type, addr, len, is_insert,
- state);
-}
-
/* Implement the "to_insert_watchpoint" target_ops method.
Insert a watchpoint to watch a memory region which starts at
static int
aarch64_linux_insert_watchpoint (struct target_ops *self,
- CORE_ADDR addr, int len, int type,
+ CORE_ADDR addr, int len,
+ enum target_hw_bp_type type,
struct expression *cond)
{
int ret;
static int
aarch64_linux_remove_watchpoint (struct target_ops *self,
- CORE_ADDR addr, int len, int type,
+ CORE_ADDR addr, int len,
+ enum target_hw_bp_type type,
struct expression *cond)
{
int ret;
aarch64_linux_region_ok_for_hw_watchpoint (struct target_ops *self,
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;
+ return aarch64_linux_region_ok_for_watchpoint (addr, len);
}
/* Implement the "to_stopped_data_address" target_ops method. */
return start <= addr && start + length - 1 >= addr;
}
+/* Implement the "to_can_do_single_step" target_ops method. */
+
+static int
+aarch64_linux_can_do_single_step (struct target_ops *target)
+{
+ return 1;
+}
+
/* Define AArch64 maintenance commands. */
static void
t->to_stopped_data_address = aarch64_linux_stopped_data_address;
t->to_watchpoint_addr_within_range =
aarch64_linux_watchpoint_addr_within_range;
+ t->to_can_do_single_step = aarch64_linux_can_do_single_step;
/* Override the GNU/Linux inferior startup hook. */
super_post_startup_inferior = t->to_post_startup_inferior;
linux_nat_set_new_fork (t, aarch64_linux_new_fork);
linux_nat_set_forget_process (t, aarch64_forget_process);
linux_nat_set_prepare_to_resume (t, aarch64_linux_prepare_to_resume);
+
+ /* Add our siginfo layout converter. */
+ linux_nat_set_siginfo_fixup (t, aarch64_linux_siginfo_fixup);
}