#include "gdbcmd.h"
#include "aarch64-tdep.h"
#include "aarch64-linux-tdep.h"
+#include "aarch32-linux-nat.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>
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,
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;
thread's arch-specific data area, the actual updating will be done
when the thread is resumed. */
-static void
+void
aarch64_notify_debug_reg_change (const struct aarch64_debug_reg_state *state,
int is_watchpoint, unsigned int 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;
-
- fprintf_unfiltered (gdb_stdlog, "%s", func);
- if (addr || len)
- fprintf_unfiltered (gdb_stdlog, " (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??"))));
- fprintf_unfiltered (gdb_stdlog, ":\n");
-
- fprintf_unfiltered (gdb_stdlog, "\tBREAKPOINTs:\n");
- for (i = 0; i < aarch64_num_bp_regs; i++)
- fprintf_unfiltered (gdb_stdlog,
- "\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]);
-
- fprintf_unfiltered (gdb_stdlog, "\tWATCHPOINTs:\n");
- for (i = 0; i < aarch64_num_wp_regs; i++)
- fprintf_unfiltered (gdb_stdlog,
- "\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. */
static void
fetch_gregs_from_thread (struct regcache *regcache)
{
- int ret, regno, tid;
+ int ret, tid;
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
elf_gregset_t regs;
struct iovec iovec;
+ /* 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);
iovec.iov_base = ®s;
- iovec.iov_len = sizeof (regs);
+ if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+ iovec.iov_len = 18 * 4;
+ else
+ iovec.iov_len = sizeof (regs);
ret = ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iovec);
if (ret < 0)
perror_with_name (_("Unable to fetch general registers."));
- for (regno = AARCH64_X0_REGNUM; regno <= AARCH64_CPSR_REGNUM; regno++)
- regcache_raw_supply (regcache, regno,
- (char *) ®s[regno - AARCH64_X0_REGNUM]);
+ if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+ aarch32_gp_regcache_supply (regcache, (uint32_t *) regs, 1);
+ else
+ {
+ int regno;
+
+ for (regno = AARCH64_X0_REGNUM; regno <= AARCH64_CPSR_REGNUM; regno++)
+ regcache_raw_supply (regcache, regno, ®s[regno - AARCH64_X0_REGNUM]);
+ }
}
/* Store to the current thread the valid general-purpose register
static void
store_gregs_to_thread (const struct regcache *regcache)
{
- int ret, regno, tid;
+ int ret, tid;
elf_gregset_t regs;
struct iovec iovec;
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ /* 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);
iovec.iov_base = ®s;
- iovec.iov_len = sizeof (regs);
+ if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+ iovec.iov_len = 18 * 4;
+ else
+ iovec.iov_len = sizeof (regs);
ret = ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iovec);
if (ret < 0)
perror_with_name (_("Unable to fetch general registers."));
- for (regno = AARCH64_X0_REGNUM; regno <= AARCH64_CPSR_REGNUM; regno++)
- if (REG_VALID == regcache_register_status (regcache, regno))
- regcache_raw_collect (regcache, regno,
- (char *) ®s[regno - AARCH64_X0_REGNUM]);
+ if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+ aarch32_gp_regcache_collect (regcache, (uint32_t *) regs, 1);
+ else
+ {
+ int regno;
+
+ for (regno = AARCH64_X0_REGNUM; regno <= AARCH64_CPSR_REGNUM; regno++)
+ if (REG_VALID == regcache_register_status (regcache, regno))
+ regcache_raw_collect (regcache, regno,
+ ®s[regno - AARCH64_X0_REGNUM]);
+ }
ret = ptrace (PTRACE_SETREGSET, tid, NT_PRSTATUS, &iovec);
if (ret < 0)
static void
fetch_fpregs_from_thread (struct regcache *regcache)
{
- int ret, regno, tid;
+ int ret, tid;
elf_fpregset_t regs;
struct iovec iovec;
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+
+ /* 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);
iovec.iov_base = ®s;
- iovec.iov_len = sizeof (regs);
- ret = ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, &iovec);
- if (ret < 0)
- perror_with_name (_("Unable to fetch FP/SIMD registers."));
+ if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+ {
+ iovec.iov_len = VFP_REGS_SIZE;
+
+ ret = ptrace (PTRACE_GETREGSET, tid, NT_ARM_VFP, &iovec);
+ if (ret < 0)
+ perror_with_name (_("Unable to fetch VFP registers."));
+
+ aarch32_vfp_regcache_supply (regcache, (gdb_byte *) ®s, 32);
+ }
+ else
+ {
+ int regno;
+
+ iovec.iov_len = sizeof (regs);
+
+ ret = ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, &iovec);
+ if (ret < 0)
+ perror_with_name (_("Unable to fetch vFP/SIMD registers."));
- for (regno = AARCH64_V0_REGNUM; regno <= AARCH64_V31_REGNUM; regno++)
- regcache_raw_supply (regcache, regno,
- (char *) ®s.vregs[regno - AARCH64_V0_REGNUM]);
+ for (regno = AARCH64_V0_REGNUM; regno <= AARCH64_V31_REGNUM; regno++)
+ regcache_raw_supply (regcache, regno,
+ ®s.vregs[regno - AARCH64_V0_REGNUM]);
- regcache_raw_supply (regcache, AARCH64_FPSR_REGNUM, (char *) ®s.fpsr);
- regcache_raw_supply (regcache, AARCH64_FPCR_REGNUM, (char *) ®s.fpcr);
+ regcache_raw_supply (regcache, AARCH64_FPSR_REGNUM, ®s.fpsr);
+ regcache_raw_supply (regcache, AARCH64_FPCR_REGNUM, ®s.fpcr);
+ }
}
/* Store to the current thread the valid fp/simd register
static void
store_fpregs_to_thread (const struct regcache *regcache)
{
- int ret, regno, tid;
+ int ret, tid;
elf_fpregset_t regs;
struct iovec iovec;
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ /* 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);
iovec.iov_base = ®s;
- iovec.iov_len = sizeof (regs);
- ret = ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, &iovec);
- if (ret < 0)
- perror_with_name (_("Unable to fetch FP/SIMD registers."));
+ if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+ {
+ iovec.iov_len = VFP_REGS_SIZE;
+
+ ret = ptrace (PTRACE_GETREGSET, tid, NT_ARM_VFP, &iovec);
+ if (ret < 0)
+ perror_with_name (_("Unable to fetch VFP registers."));
- for (regno = AARCH64_V0_REGNUM; regno <= AARCH64_V31_REGNUM; regno++)
- if (REG_VALID == regcache_register_status (regcache, regno))
- regcache_raw_collect (regcache, regno,
- (char *) ®s.vregs[regno - AARCH64_V0_REGNUM]);
+ aarch32_vfp_regcache_collect (regcache, (gdb_byte *) ®s, 32);
+ }
+ else
+ {
+ int regno;
- if (REG_VALID == regcache_register_status (regcache, AARCH64_FPSR_REGNUM))
- regcache_raw_collect (regcache, AARCH64_FPSR_REGNUM, (char *) ®s.fpsr);
- if (REG_VALID == regcache_register_status (regcache, AARCH64_FPCR_REGNUM))
- regcache_raw_collect (regcache, AARCH64_FPCR_REGNUM, (char *) ®s.fpcr);
+ iovec.iov_len = sizeof (regs);
- ret = ptrace (PTRACE_SETREGSET, tid, NT_FPREGSET, &iovec);
- if (ret < 0)
- perror_with_name (_("Unable to store FP/SIMD registers."));
+ ret = ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, &iovec);
+ if (ret < 0)
+ perror_with_name (_("Unable to fetch FP/SIMD registers."));
+
+ for (regno = AARCH64_V0_REGNUM; regno <= AARCH64_V31_REGNUM; regno++)
+ if (REG_VALID == regcache_register_status (regcache, regno))
+ regcache_raw_collect (regcache, regno,
+ (char *) ®s.vregs[regno - AARCH64_V0_REGNUM]);
+
+ if (REG_VALID == regcache_register_status (regcache, AARCH64_FPSR_REGNUM))
+ regcache_raw_collect (regcache, AARCH64_FPSR_REGNUM,
+ (char *) ®s.fpsr);
+ if (REG_VALID == regcache_register_status (regcache, AARCH64_FPCR_REGNUM))
+ regcache_raw_collect (regcache, AARCH64_FPCR_REGNUM,
+ (char *) ®s.fpcr);
+ }
+
+ if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+ {
+ ret = ptrace (PTRACE_SETREGSET, tid, NT_ARM_VFP, &iovec);
+ if (ret < 0)
+ perror_with_name (_("Unable to store VFP registers."));
+ }
+ else
+ {
+ ret = ptrace (PTRACE_SETREGSET, tid, NT_FPREGSET, &iovec);
+ if (ret < 0)
+ perror_with_name (_("Unable to store FP/SIMD registers."));
+ }
}
/* Implement the "to_fetch_register" target_ops method. */
}
\f
-/* Get the hardware debug register capacity information. */
-
-static void
-aarch64_linux_get_debug_reg_capacity (void)
-{
- int tid;
- struct iovec iov;
- struct user_hwdebug_state dreg_state;
-
- tid = get_thread_id (inferior_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 ();
+ aarch64_linux_get_debug_reg_capacity (ptid_get_pid (ptid));
super_post_startup_inferior (self, ptid);
}
+extern struct target_desc *tdesc_arm_with_vfpv3;
+extern struct target_desc *tdesc_arm_with_neon;
+
/* Implement the "to_read_description" target_ops method. */
static const struct target_desc *
aarch64_linux_read_description (struct target_ops *ops)
{
- 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.
+ CORE_ADDR at_phent;
- See the comment above the function of the same name in
- gdbserver/linux-aarch64-low.c for more information. */
+ if (target_auxv_search (ops, AT_PHENT, &at_phent) == 1)
+ {
+ if (at_phent == sizeof (Elf64_External_Phdr))
+ return tdesc_aarch64;
+ else
+ {
+ CORE_ADDR arm_hwcap = 0;
-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)
-{
- 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;
+ if (target_auxv_search (ops, AT_HWCAP, &arm_hwcap) != 1)
+ return ops->beneath->to_read_description (ops->beneath);
- /* As assumed by the algorithm. */
- gdb_assert (alignment == max_wp_len);
+#ifndef COMPAT_HWCAP_VFP
+#define COMPAT_HWCAP_VFP (1 << 6)
+#endif
+#ifndef COMPAT_HWCAP_NEON
+#define COMPAT_HWCAP_NEON (1 << 12)
+#endif
+#ifndef COMPAT_HWCAP_VFPv3
+#define COMPAT_HWCAP_VFPv3 (1 << 13)
+#endif
- if (len <= 0)
- return;
+ if (arm_hwcap & COMPAT_HWCAP_VFP)
+ {
+ char *buf;
+ const struct target_desc *result = NULL;
- /* Address to be put into the hardware watchpoint value register
- must be aligned. */
- offset = addr & (alignment - 1);
- aligned_addr = addr - offset;
+ if (arm_hwcap & COMPAT_HWCAP_NEON)
+ result = tdesc_arm_with_neon;
+ else if (arm_hwcap & COMPAT_HWCAP_VFPv3)
+ result = tdesc_arm_with_vfpv3;
- gdb_assert (offset >= 0 && offset < alignment);
- gdb_assert (aligned_addr >= 0 && aligned_addr <= addr);
- gdb_assert (offset + len > 0);
+ return result;
+ }
- 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
- {
- /* 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;
+ return NULL;
+ }
}
- 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 tdesc_aarch64;
}
/* 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,
- int type, CORE_ADDR addr, int len)
-{
- 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)
- {
- 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 an existing or free register in our cache. */
- idx = -1;
- for (i = 0; i < num_regs; ++i)
- {
- 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);
- }
- 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,
- int 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)
+ 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
+ else if (type == bp_hardware_breakpoint)
{
- 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;
+ if (aarch64_num_bp_regs == 0)
+ return 0;
}
-
- 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 (int 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;
-
- state = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
-
- 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.
int ret;
CORE_ADDR addr = bp_tgt->placed_address = bp_tgt->reqstd_address;
const int len = 4;
- const int type = hw_execute;
+ const enum target_hw_bp_type type = hw_execute;
+ struct aarch64_debug_reg_state *state
+ = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
if (show_debug_regs)
fprintf_unfiltered
"insert_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n",
(unsigned long) addr, len);
- ret = aarch64_handle_breakpoint (type, addr, len, 1 /* is_insert */);
+ ret = aarch64_handle_breakpoint (type, addr, len, 1 /* is_insert */, state);
if (show_debug_regs)
{
- struct aarch64_debug_reg_state *state
- = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
-
aarch64_show_debug_reg_state (state,
"insert_hw_breakpoint", addr, len, type);
}
int ret;
CORE_ADDR addr = bp_tgt->placed_address;
const int len = 4;
- const int type = hw_execute;
+ const enum target_hw_bp_type type = hw_execute;
+ struct aarch64_debug_reg_state *state
+ = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
if (show_debug_regs)
fprintf_unfiltered
(gdb_stdlog, "remove_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n",
(unsigned long) addr, len);
- ret = aarch64_handle_breakpoint (type, addr, len, 0 /* is_insert */);
+ ret = aarch64_handle_breakpoint (type, addr, len, 0 /* is_insert */, state);
if (show_debug_regs)
{
- struct aarch64_debug_reg_state *state
- = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
-
aarch64_show_debug_reg_state (state,
"remove_hw_watchpoint", addr, len, type);
}
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 (int type, CORE_ADDR addr, int len,
- int is_insert)
-{
- struct aarch64_debug_reg_state *state
- = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
-
- 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
- = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
-
- 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)
- fprintf_unfiltered (gdb_stdlog,
-"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)
-{
- if (aarch64_point_is_aligned (1 /* is_watchpoint */ , addr, len))
- return aarch64_handle_aligned_watchpoint (type, addr, len, is_insert);
- else
- return aarch64_handle_unaligned_watchpoint (type, addr, len, is_insert);
-}
-
/* Implement the "to_insert_watchpoint" target_ops method.
Insert a watchpoint to watch a memory region which starts at
struct expression *cond)
{
int ret;
+ struct aarch64_debug_reg_state *state
+ = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
if (show_debug_regs)
fprintf_unfiltered (gdb_stdlog,
gdb_assert (type != hw_execute);
- ret = aarch64_handle_watchpoint (type, addr, len, 1 /* is_insert */);
+ ret = aarch64_handle_watchpoint (type, addr, len, 1 /* is_insert */, state);
if (show_debug_regs)
{
- struct aarch64_debug_reg_state *state
- = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
-
aarch64_show_debug_reg_state (state,
"insert_watchpoint", addr, len, type);
}
struct expression *cond)
{
int ret;
+ struct aarch64_debug_reg_state *state
+ = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
if (show_debug_regs)
fprintf_unfiltered (gdb_stdlog,
gdb_assert (type != hw_execute);
- ret = aarch64_handle_watchpoint (type, addr, len, 0 /* is_insert */);
+ ret = aarch64_handle_watchpoint (type, addr, len, 0 /* is_insert */, state);
if (show_debug_regs)
{
- struct aarch64_debug_reg_state *state
- = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
-
aarch64_show_debug_reg_state (state,
"remove_watchpoint", addr, len, type);
}