This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH 4/5] aarch64-linux-nat.


On 23/12/12 07:10, Joel Brobecker wrote:

As discussed in patch #1, the configure.host patch needs to be moved
here.

Done, and patch re-spun incorporating fixes to the remainder of your comments.


Thanks
/Marcus

2013-01-07  Jim MacArthur  <jim.macarthur@arm.com>
            Marcus Shawcroft  <marcus.shawcroft@arm.com>
            Nigel Stephens  <nigel.stephens@arm.com>
            Yufeng Zhang  <yufeng.zhang@arm.com>

        * aarch64-linux-nat.c: New file.
        * config/aarch64/linux.mh: New file.
        * configure.host: Add AArch64.


diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 58803e9..49558a1 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1413,6 +1413,7 @@ force_update:
 MAKEOVERRIDES=
 
 ALLDEPFILES = \
+	aarch64-linux-nat.c \
 	aarch64-linux-tdep.c aarch64-newlib-tdep.c aarch64-tdep.c \
 	aix-thread.c \
 	alpha-nat.c alphabsd-nat.c alpha-linux-nat.c \
diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
new file mode 100644
index 0000000..5f5b30c
--- /dev/null
+++ b/gdb/aarch64-linux-nat.c
@@ -0,0 +1,1003 @@
+/* Native-dependent code for GNU/Linux AArch64.
+
+   Copyright (C) 2011-2013 Free Software Foundation, Inc.
+   Contributed by ARM Ltd.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+
+#include "inferior.h"
+#include "gdbcore.h"
+#include "regcache.h"
+#include "linux-nat.h"
+#include "target-descriptions.h"
+#include "auxv.h"
+#include "aarch64-tdep.h"
+#include "aarch64-linux-tdep.h"
+#include "elf/common.h"
+
+#include <sys/ptrace.h>
+#include <sys/utsname.h>
+
+#include "gregset.h"
+
+#include "features/aarch64.c"
+
+#define TRAP_HWBKPT 0x0004
+
+#define AARCH64_HWP_ALIGNMENT 8
+#define AARCH64_HWP_MAX_LEN_PER_REG 8
+
+/* 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)
+
+#define AARCH64_DEBUG_ARCH_V8 0x6
+
+/* 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 = TIDGET (ptid);
+
+  if (0 == tid)
+    tid = PIDGET (ptid);
+  return tid;
+}
+
+/* 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;
+  elf_gregset_t regs;
+  struct iovec iovec;
+
+  tid = get_thread_id (inferior_ptid);
+
+  iovec.iov_base = &regs;
+  iovec.iov_len = sizeof (regs);
+
+  ret = ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iovec);
+  if (ret < 0)
+    {
+      perror_with_name (_("Unable to fetch general registers."));
+      return;
+    }
+
+  for (regno = AARCH64_X0_REGNUM; regno <= AARCH64_CPSR_REGNUM; regno++)
+    regcache_raw_supply (regcache, regno,
+			 (char *) &regs[regno - AARCH64_X0_REGNUM]);
+}
+
+/* Store to the current thread the valid general-purpose register
+   values in the GDB's register array.  */
+
+static void
+store_gregs_to_thread (const struct regcache *regcache)
+{
+  int ret, regno, tid;
+  elf_gregset_t regs;
+  struct iovec iovec;
+
+  tid = get_thread_id (inferior_ptid);
+
+  iovec.iov_base = &regs;
+  iovec.iov_len = sizeof (regs);
+
+  ret = ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iovec);
+  if (ret < 0)
+    {
+      perror_with_name (_("Unable to fetch general registers."));
+      return;
+    }
+
+  for (regno = AARCH64_X0_REGNUM; regno <= AARCH64_CPSR_REGNUM; regno++)
+    if (REG_VALID == regcache_register_status (regcache, regno))
+      regcache_raw_collect (regcache, regno,
+			    (char *) &regs[regno - AARCH64_X0_REGNUM]);
+
+  ret = ptrace (PTRACE_SETREGSET, tid, NT_PRSTATUS, &iovec);
+  if (ret < 0)
+    {
+      perror_with_name (_("Unable to store general registers."));
+      return;
+    }
+}
+
+/* Fill GDB's register array with the fp/simd register values
+   from the current thread.  */
+
+static void
+fetch_fpregs_from_thread (struct regcache *regcache)
+{
+  int ret, regno, tid;
+  elf_fpregset_t regs;
+  struct iovec iovec;
+
+  tid = get_thread_id (inferior_ptid);
+
+  iovec.iov_base = &regs;
+  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."));
+      return;
+    }
+
+  for (regno = AARCH64_V0_REGNUM; regno <= AARCH64_V31_REGNUM; regno++)
+    regcache_raw_supply (regcache, regno,
+			 (char *) &regs.vregs[regno - AARCH64_V0_REGNUM]);
+
+  regcache_raw_supply (regcache, AARCH64_FPSR_REGNUM, (char *) &regs.fpsr);
+  regcache_raw_supply (regcache, AARCH64_FPCR_REGNUM, (char *) &regs.fpcr);
+}
+
+/* Store to the current thread the valid fp/simd register
+   values in the GDB's register array.  */
+
+static void
+store_fpregs_to_thread (const struct regcache *regcache)
+{
+  int ret, regno, tid;
+  elf_fpregset_t regs;
+  struct iovec iovec;
+
+  tid = get_thread_id (inferior_ptid);
+
+  iovec.iov_base = &regs;
+  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."));
+      return;
+    }
+
+  for (regno = AARCH64_V0_REGNUM; regno <= AARCH64_V31_REGNUM; regno++)
+    if (REG_VALID == regcache_register_status (regcache, regno))
+      regcache_raw_collect (regcache, regno,
+			    (char *) &regs.vregs[regno - AARCH64_V0_REGNUM]);
+
+  if (REG_VALID == regcache_register_status (regcache, AARCH64_FPSR_REGNUM))
+    regcache_raw_collect (regcache, AARCH64_FPSR_REGNUM, (char *) &regs.fpsr);
+  if (REG_VALID == regcache_register_status (regcache, AARCH64_FPCR_REGNUM))
+    regcache_raw_collect (regcache, AARCH64_FPCR_REGNUM, (char *) &regs.fpcr);
+
+  ret = ptrace (PTRACE_SETREGSET, tid, NT_FPREGSET, &iovec);
+  if (ret < 0)
+    {
+      perror_with_name (_("Unable to store FP/SIMD registers."));
+      return;
+    }
+}
+
+/* Implement the "to_fetch_register" target_ops method.  */
+
+static void
+aarch64_linux_fetch_inferior_registers (struct target_ops *ops,
+					struct regcache *regcache,
+					int regno)
+{
+  if (regno == -1)
+    {
+      fetch_gregs_from_thread (regcache);
+      fetch_fpregs_from_thread (regcache);
+    }
+  else if (regno < AARCH64_V0_REGNUM)
+    fetch_gregs_from_thread (regcache);
+  else
+    fetch_fpregs_from_thread (regcache);
+}
+
+/* Implement the "to_store_register" target_ops method.  */
+
+static void
+aarch64_linux_store_inferior_registers (struct target_ops *ops,
+					struct regcache *regcache,
+					int regno)
+{
+  if (regno == -1)
+    {
+      store_gregs_to_thread (regcache);
+      store_fpregs_to_thread (regcache);
+    }
+  else if (regno < AARCH64_V0_REGNUM)
+    store_gregs_to_thread (regcache);
+  else
+    store_fpregs_to_thread (regcache);
+}
+
+/* Fill register REGNO (if it is a general-purpose register) in
+   *GREGSETPS with the value in GDB's register array.  If REGNO is -1,
+   do this for all registers.  */
+
+void
+fill_gregset (const struct regcache *regcache,
+	      gdb_gregset_t *gregsetp, int regno)
+{
+  gdb_byte *gregs_buf = (gdb_byte *) gregsetp;
+  int i;
+
+  for (i = AARCH64_X0_REGNUM; i <= AARCH64_CPSR_REGNUM; i++)
+    if (regno == -1 || regno == i)
+      regcache_raw_collect (regcache, i,
+			    gregs_buf + X_REGISTER_SIZE
+			    * (i - AARCH64_X0_REGNUM));
+}
+
+/* Fill GDB's register array with the general-purpose register values
+   in *GREGSETP.  */
+
+void
+supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregsetp)
+{
+  aarch64_linux_supply_gregset (regcache, (const gdb_byte *) gregsetp);
+}
+
+/* Fill register REGNO (if it is a floating-point register) in
+   *FPREGSETP with the value in GDB's register array.  If REGNO is -1,
+   do this for all registers.  */
+
+void
+fill_fpregset (const struct regcache *regcache,
+	       gdb_fpregset_t *fpregsetp, int regno)
+{
+  gdb_byte *fpregs_buf = (gdb_byte *) fpregsetp;
+  int i;
+
+  for (i = AARCH64_V0_REGNUM; i <= AARCH64_V31_REGNUM; i++)
+    if (regno == -1 || regno == i)
+      regcache_raw_collect (regcache, i,
+			    fpregs_buf + V_REGISTER_SIZE
+			    * (i - AARCH64_V0_REGNUM));
+
+  if (regno == -1 || regno == AARCH64_FPSR_REGNUM)
+    regcache_raw_collect (regcache, AARCH64_FPSR_REGNUM,
+			  fpregs_buf + V_REGISTER_SIZE * 32);
+
+  if (regno == -1 || regno == AARCH64_FPCR_REGNUM)
+    regcache_raw_collect (regcache, AARCH64_FPCR_REGNUM,
+			  fpregs_buf + V_REGISTER_SIZE * 32 + 4);
+}
+
+/* Fill GDB's register array with the floating-point register values
+   in *FPREGSETP.  */
+
+void
+supply_fpregset (struct regcache *regcache, const gdb_fpregset_t *fpregsetp)
+{
+  aarch64_linux_supply_fpregset (regcache, (const gdb_byte *) fpregsetp);
+}
+
+/* Implement the "to_read_description" target_ops method.  */
+
+static const struct target_desc *
+aarch64_linux_read_description (struct target_ops *ops)
+{
+  CORE_ADDR hwcap = 0;
+  const struct target_desc *result = NULL;
+
+  if (target_auxv_search (ops, AT_HWCAP, &hwcap) != 1)
+    return NULL;
+
+  initialize_tdesc_aarch64 ();
+  result = tdesc_aarch64;
+  return result;
+}
+
+/* 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.
+
+   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)
+{
+  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);
+
+  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
+    {
+      /* 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 (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;
+}
+
+/* Enum describing the different types of AArch64 hardware
+   break-/watch-points.  */
+typedef enum
+{
+  aarch64_hwbp_break = 0,
+  aarch64_hwbp_load = 1,
+  aarch64_hwbp_store = 2,
+  aarch64_hwbp_access = 3
+} aarch64_hwbp_type;
+
+/* Type for the Hardware Breakpoint Control register value.  */
+typedef unsigned int aarch64_hwbp_control_t;
+
+/* Structure for the raw values of a pair of hardware debug value
+   and control registers, where the low-level hardware
+   breakpoint/watchpoint is set.  */
+struct aarch64_linux_hw_breakpoint
+{
+  /* Address to break on, or being watched.  */
+  CORE_ADDR address;
+  /* Control register for break-/watch- point.  */
+  aarch64_hwbp_control_t control;
+};
+
+/* Structure for the information of the hardware
+   breakpoint/watchpoint capacity.  */
+struct aarch64_linux_hwbp_cap
+{
+  /* Total number of hardware breakpoint registers.  */
+  int bp_count;
+  /* Total number of hardware watchpoint registers.  */
+  int wp_count;
+  /* Maximum watchpoint length in bytes.  */
+  int max_wp_length;
+};
+
+/* Get the hardware debug register capacity information.  */
+
+static const struct aarch64_linux_hwbp_cap *
+aarch64_linux_get_hwbp_cap (void)
+{
+  /* The info structure we return.  This function is called
+     repeatedly, so we return a pointer to a static variable which
+     caches the result, rather than calling ptrace repeatedly.  */
+  static struct aarch64_linux_hwbp_cap info;
+
+  /* Is INFO in a good state?  -1 means that no attempt has been made
+     to initialize INFO; 0 means an attempt has been made, but it
+     failed; 1 means INFO is in an initialized state.  */
+  static int available = -1;
+
+  if (available == -1)
+    {
+      int tid;
+      struct iovec iov;
+      struct user_hwdebug_state regs;
+
+      available = 0;
+
+      tid = get_thread_id (inferior_ptid);
+      iov.iov_base = &regs;
+      iov.iov_len = sizeof (regs);
+
+      if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_HW_BREAK, (void *)&iov) == 0
+	  && AARCH64_DEBUG_ARCH (regs.dbg_info) == AARCH64_DEBUG_ARCH_V8)
+	info.bp_count = AARCH64_DEBUG_NUM_SLOTS (regs.dbg_info);
+      else
+	return NULL;
+
+      if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_HW_WATCH, (void *)&iov) == 0
+	  && AARCH64_DEBUG_ARCH (regs.dbg_info) == AARCH64_DEBUG_ARCH_V8)
+	info.wp_count = AARCH64_DEBUG_NUM_SLOTS (regs.dbg_info);
+      else
+	return NULL;
+
+      info.max_wp_length = AARCH64_HWP_MAX_LEN_PER_REG;
+      available = 1;
+    }
+
+  return available == 1 ? &info : NULL;
+}
+
+/* How many hardware breakpoints are available?  */
+
+static int
+aarch64_linux_get_hw_breakpoint_count (void)
+{
+  const struct aarch64_linux_hwbp_cap *cap = aarch64_linux_get_hwbp_cap ();
+
+  return cap != NULL ? cap->bp_count : 0;
+}
+
+/* How many hardware watchpoints are available?  */
+
+static int
+aarch64_linux_get_hw_watchpoint_count (void)
+{
+  const struct aarch64_linux_hwbp_cap *cap = aarch64_linux_get_hwbp_cap ();
+
+  return cap != NULL ? cap->wp_count : 0;
+}
+
+/* Implement the "to_can_use_hw_breakpoint" target_ops method.  */
+
+static int
+aarch64_linux_can_use_hw_breakpoint (int type, int cnt, int ot)
+{
+  if (type == bp_hardware_watchpoint || type == bp_read_watchpoint
+      || type == bp_access_watchpoint || type == bp_watchpoint)
+    {
+      if (cnt + ot > aarch64_linux_get_hw_watchpoint_count ())
+	return -1;
+    }
+  else if (type == bp_hardware_breakpoint)
+    {
+      if (cnt > aarch64_linux_get_hw_breakpoint_count ())
+	return -1;
+    }
+  else
+    {
+      gdb_assert_not_reached ("unrecognized breakpoint/watchpoint type");
+      return -1;
+    }
+
+  return 1;
+}
+
+/* Given the BYTE_ADDRESS_SELECT mask, hardware breakpoint/watchpoint
+   type in HWBP_TYPE and the ENABLE bit, calculate and return the value
+   that can be used to initialize a hardware breakpoint/watchpoint
+   control register.  */
+
+static aarch64_hwbp_control_t
+aarch64_hwbp_ctrl_reg_initial_value (unsigned byte_address_select,
+				     aarch64_hwbp_type hwbp_type,
+				     int enable)
+{
+  gdb_assert ((byte_address_select & ~0xffU) == 0);
+  gdb_assert (hwbp_type != aarch64_hwbp_break
+	      || ((byte_address_select & 0xfU) != 0));
+
+  return (byte_address_select << 5) | (hwbp_type << 3) | (3 << 1) | enable;
+}
+
+/* Initialize the hardware breakpoint structure P.  The breakpoint
+   will be enabled, and will point to the placed address of BP_TGT.  */
+
+static void
+aarch64_linux_hw_breakpoint_initialize (struct gdbarch *gdbarch,
+					struct bp_target_info *bp_tgt,
+					struct aarch64_linux_hw_breakpoint *p)
+{
+  unsigned mask = 0xf;
+  CORE_ADDR address = bp_tgt->placed_address;
+
+  p->address = address & ~3;
+  p->control = aarch64_hwbp_ctrl_reg_initial_value (mask, aarch64_hwbp_break,
+						    1);
+}
+
+/* Get the AArch64 hardware breakpoint type from the RW value we're
+   given when asked to set a watchpoint.  */
+
+static aarch64_hwbp_type
+aarch64_linux_get_hwbp_type (int rw)
+{
+  if (rw == hw_read)
+    return aarch64_hwbp_load;
+  else if (rw == hw_write)
+    return aarch64_hwbp_store;
+  else
+    return aarch64_hwbp_access;
+}
+
+/* Initialize the hardware breakpoint structure P for a watchpoint at
+   ADDR to LEN.  The type of watchpoint is given in RW.  */
+
+static void
+aarch64_linux_hw_watchpoint_initialize (CORE_ADDR address, int len, int rw,
+					struct aarch64_linux_hw_breakpoint *p)
+{
+  unsigned mask;
+
+  mask = (1 << len) - 1;
+  p->address = address;
+  p->control =
+    aarch64_hwbp_ctrl_reg_initial_value (mask,
+					 aarch64_linux_get_hwbp_type (rw), 1);
+}
+
+/* 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 ((ctrl >> 5) & 0xff)
+    {
+    case 0x01: return 1; break;
+    case 0x03: return 2; break;
+    case 0x0f: return 4; break;
+    case 0xff: return 8; break;
+    default: return 0;
+    }
+}
+
+typedef struct aarch64_linux_thread_points
+{
+  /* Thread ID.  */
+  int tid;
+  /* Breakpoints for thread.  */
+  struct aarch64_linux_hw_breakpoint *bpts;
+  /* Watchpoint for threads.  */
+  struct aarch64_linux_hw_breakpoint *wpts;
+} *aarch64_linux_thread_points_p;
+DEF_VEC_P (aarch64_linux_thread_points_p);
+
+/* Vector of hardware breakpoints for each thread.  */
+VEC (aarch64_linux_thread_points_p) *aarch64_threads = NULL;
+
+/* Find and return the pointer to the hardware breakpoint/watchpoint cache
+   associated with the thread TID.  Allocate a new cache structure and
+   return its address if no existing one has been found and ALLOC_NEW is
+   non-zero.  */
+
+static struct aarch64_linux_thread_points *
+aarch64_linux_find_breakpoints_by_tid (int tid, int alloc_new)
+{
+  int i;
+  struct aarch64_linux_thread_points *t;
+
+  for (i = 0;
+       VEC_iterate (aarch64_linux_thread_points_p, aarch64_threads, i, t);
+       ++i)
+    {
+      if (t->tid == tid)
+	return t;
+    }
+
+  t = NULL;
+
+  if (alloc_new)
+    {
+      t = xmalloc (sizeof (struct aarch64_linux_thread_points));
+      t->tid = tid;
+      t->bpts = xzalloc (aarch64_linux_get_hw_breakpoint_count ()
+			 * sizeof (struct aarch64_linux_hw_breakpoint));
+      t->wpts = xzalloc (aarch64_linux_get_hw_watchpoint_count ()
+			 * sizeof (struct aarch64_linux_hw_breakpoint));
+      VEC_safe_push (aarch64_linux_thread_points_p, aarch64_threads, t);
+    }
+
+  return t;
+}
+
+/* Given the value of a hardware breakpoing/watchpoint control register
+   in CONTROL, return 1 if hardware breakpoing/watchpoint is enabled;
+   return 0 otherwise.  */
+
+static int
+aarch64_hwbp_control_is_enabled (aarch64_hwbp_control_t control)
+{
+  return control & 0x1;
+}
+
+/* Change a breakpoint control word so that it is in the disabled
+   state.  */
+
+static aarch64_hwbp_control_t
+aarch64_hwbp_control_disable (aarch64_hwbp_control_t control)
+{
+  return control & ~0x1;
+}
+
+/* Are two break-/watch-points equal?  */
+
+static int
+aarch64_linux_hw_breakpoint_equal (const struct aarch64_linux_hw_breakpoint *p1,
+				   const struct aarch64_linux_hw_breakpoint *p2)
+{
+  return p1->address == p2->address && p1->control == p2->control;
+}
+
+/* Call ptrace to set the thread TID's hardware breakpoint/watchpoint
+   registers with data from *PTS.  */
+
+static void
+aarch64_linux_set_hw_debug_regs (const struct aarch64_linux_hw_breakpoint *pts,
+				 int tid, int watchpoint)
+{
+  int i, count;
+  struct iovec iov;
+  struct user_hwdebug_state regs;
+
+  iov.iov_base = &regs;
+  iov.iov_len = sizeof (regs);
+  count = (watchpoint
+	   ? aarch64_linux_get_hw_watchpoint_count ()
+	   : aarch64_linux_get_hw_breakpoint_count ());
+
+  for (i = 0; i < count; i++)
+    {
+      regs.dbg_regs[i].addr = pts[i].address;
+      regs.dbg_regs[i].ctrl = pts[i].control;
+    }
+
+  if (ptrace (PTRACE_SETREGSET, tid,
+	      watchpoint ? NT_ARM_HW_WATCH : NT_ARM_HW_BREAK,
+	      (void *) &iov))
+    perror_with_name (_("Unexpected error setting hardware debug registers"));
+}
+
+/* Remove the hardware breakpoint (WATCHPOINT = 0) or watchpoint
+   (WATCHPOINT = 1) BPT for thread TID.  */
+
+static void
+aarch64_linux_remove_hw_breakpoint1
+  (const struct aarch64_linux_hw_breakpoint *bpt, int tid, int watchpoint)
+{
+  struct aarch64_linux_thread_points *t =
+    aarch64_linux_find_breakpoints_by_tid (tid, 0);
+  gdb_byte count, i;
+  struct aarch64_linux_hw_breakpoint *bpts;
+
+  gdb_assert (t != NULL);
+
+  if (watchpoint)
+    {
+      count = aarch64_linux_get_hw_watchpoint_count ();
+      bpts = t->wpts;
+    }
+  else
+    {
+      count = aarch64_linux_get_hw_breakpoint_count ();
+      bpts = t->bpts;
+    }
+
+  for (i = 0; i < count; ++i)
+    if (aarch64_linux_hw_breakpoint_equal (bpt, bpts + i))
+      {
+	bpts[i].control = aarch64_hwbp_control_disable (bpts[i].control);
+	break;
+      }
+
+  gdb_assert (i != count);
+
+  aarch64_linux_set_hw_debug_regs (watchpoint ? t->wpts : t->bpts,
+				   tid, watchpoint);
+}
+
+/* Remove a hardware breakpoint.  */
+
+static int
+aarch64_linux_remove_hw_breakpoint (struct gdbarch *gdbarch,
+				    struct bp_target_info *bp_tgt)
+{
+  struct lwp_info *lp;
+  struct aarch64_linux_hw_breakpoint p;
+
+  if (aarch64_linux_get_hw_breakpoint_count () == 0)
+    return -1;
+
+  aarch64_linux_hw_breakpoint_initialize (gdbarch, bp_tgt, &p);
+  ALL_LWPS (lp)
+    aarch64_linux_remove_hw_breakpoint1 (&p, TIDGET (lp->ptid), 0);
+
+  return 0;
+}
+
+/* Insert the hardware breakpoint (WATCHPOINT = 0) or watchpoint
+   (WATCHPOINT = 1) BPT for thread TID.  */
+
+static void
+aarch64_linux_insert_hw_breakpoint1
+  (const struct aarch64_linux_hw_breakpoint *bpt, int tid, int watchpoint)
+{
+  struct aarch64_linux_thread_points *t =
+    aarch64_linux_find_breakpoints_by_tid (tid, 1);
+  gdb_byte count, i;
+  struct aarch64_linux_hw_breakpoint *bpts;
+  CORE_ADDR aligned_address = bpt->address & ~(0x3);
+
+  gdb_assert (t != NULL);
+
+  if (watchpoint)
+    {
+      count = aarch64_linux_get_hw_watchpoint_count ();
+      bpts = t->wpts;
+    }
+  else
+    {
+      count = aarch64_linux_get_hw_breakpoint_count ();
+      bpts = t->bpts;
+    }
+
+  for (i = 0; i < count; ++i)
+    if (!aarch64_hwbp_control_is_enabled (bpts[i].control))
+      {
+	memcpy (bpts + i, bpt, sizeof (struct aarch64_linux_hw_breakpoint));
+	break;
+      }
+
+  gdb_assert (i != count);
+
+  aarch64_linux_set_hw_debug_regs (watchpoint ? t->wpts : t->bpts,
+				   tid, watchpoint);
+}
+
+/* Insert a Hardware breakpoint.  */
+
+static int
+aarch64_linux_insert_hw_breakpoint (struct gdbarch *gdbarch,
+				    struct bp_target_info *bp_tgt)
+{
+  struct lwp_info *lp;
+  struct aarch64_linux_hw_breakpoint p;
+
+  if (aarch64_linux_get_hw_breakpoint_count () == 0)
+    return -1;
+
+  aarch64_linux_hw_breakpoint_initialize (gdbarch, bp_tgt, &p);
+  ALL_LWPS (lp)
+    aarch64_linux_insert_hw_breakpoint1 (&p, TIDGET (lp->ptid), 0);
+
+  return 0;
+}
+
+/* Implement the "to_region_ok_for_hw_watchpoint" target_ops method.  */
+
+static int
+aarch64_linux_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
+{
+  const struct aarch64_linux_hwbp_cap *cap = aarch64_linux_get_hwbp_cap ();
+  CORE_ADDR max_wp_length, aligned_addr;
+
+  /* Can not set watchpoints for zero or negative lengths.  */
+  if (len <= 0)
+    return 0;
+
+  /* Need to be able to use the ptrace interface.  */
+  if (cap == NULL || cap->wp_count == 0)
+    return 0;
+
+  /* Test that the range [ADDR, ADDR + LEN) fits into the largest
+     address range covered by a watchpoint.  */
+  max_wp_length = (CORE_ADDR) cap->max_wp_length;
+  aligned_addr = addr & ~(max_wp_length - 1);
+
+  if (aligned_addr + max_wp_length < addr + len)
+    return 0;
+
+  /* The current ptrace interface can only handle watchpoints that
+     are a power of 2.  */
+  if ((len & (len - 1)) != 0)
+    return 0;
+
+  /* All tests passed so we must be able to set a watchpoint.  */
+  return 1;
+}
+
+/* Implement the "to_insert_watchpoint" target_ops method.  */
+
+static int
+aarch64_linux_insert_watchpoint (CORE_ADDR addr, int len, int rw,
+				 struct expression *cond)
+{
+  struct lwp_info *lp;
+  struct aarch64_linux_hw_breakpoint p;
+
+  if (aarch64_linux_get_hw_watchpoint_count () == 0)
+    return -1;
+
+  while (len > 0)
+    {
+      CORE_ADDR aligned_addr;
+      int aligned_len;
+
+      aarch64_align_watchpoint (addr, len, &aligned_addr, &aligned_len,
+				&addr, &len);
+
+      aarch64_linux_hw_watchpoint_initialize (aligned_addr, aligned_len,
+					      rw, &p);
+      ALL_LWPS (lp)
+	aarch64_linux_insert_hw_breakpoint1 (&p, TIDGET (lp->ptid), 1);
+    }
+
+  return 0;
+}
+
+/* Implement the "to_remove_watchpoint" target_ops method.  */
+
+static int
+aarch64_linux_remove_watchpoint (CORE_ADDR addr, int len, int rw,
+				 struct expression *cond)
+{
+  struct lwp_info *lp;
+  struct aarch64_linux_hw_breakpoint p;
+
+  if (aarch64_linux_get_hw_watchpoint_count () == 0)
+    return -1;
+
+  while (len > 0)
+    {
+      CORE_ADDR aligned_addr;
+      int aligned_len;
+
+      aarch64_align_watchpoint (addr, len, &aligned_addr, &aligned_len,
+				&addr, &len);
+      aarch64_linux_hw_watchpoint_initialize (aligned_addr, aligned_len,
+					      rw, &p);
+      ALL_LWPS (lp)
+	aarch64_linux_remove_hw_breakpoint1 (&p, TIDGET (lp->ptid), 1);
+    }
+
+  return 0;
+}
+
+/* Implement the "to_stopped_data_address" target_ops method.  */
+
+static int
+aarch64_linux_stopped_data_address (struct target_ops *target,
+				    CORE_ADDR *addr_p)
+{
+  siginfo_t siginfo;
+  int i, tid;
+  const struct aarch64_linux_thread_points *pts;
+
+  if (!linux_nat_get_siginfo (inferior_ptid, &siginfo))
+    return 0;
+
+  /* This must be a hardware breakpoint.  */
+  if (siginfo.si_signo != SIGTRAP
+      || (siginfo.si_code & 0xffff) != TRAP_HWBKPT)
+    return 0;
+
+  /* We must be able to set hardware watchpoints.  */
+  if (aarch64_linux_get_hw_watchpoint_count () == 0)
+    return 0;
+
+  /* Check if the address matches any watched address.  */
+  tid = get_thread_id (inferior_ptid);
+  if ((pts = aarch64_linux_find_breakpoints_by_tid (tid, 0)) == NULL)
+    return 0;
+  for (i = 0; i < aarch64_linux_get_hw_watchpoint_count (); ++i)
+    {
+      const unsigned len = aarch64_watchpoint_length (pts->wpts[i].control);
+      const CORE_ADDR addr_trap = (CORE_ADDR) siginfo.si_addr;
+      const CORE_ADDR addr_watch = pts->wpts[i].address;
+
+      if (aarch64_hwbp_control_is_enabled (pts->wpts[i].control)
+	  && addr_trap >= addr_watch
+	  && addr_trap < addr_watch + len)
+	{
+	  *addr_p = addr_trap;
+	  return 1;
+	}
+    }
+
+  return 0;
+}
+
+/* Implement the "to_stopped_by_watchpoint" target_ops method.  */
+
+static int
+aarch64_linux_stopped_by_watchpoint (void)
+{
+  CORE_ADDR addr;
+
+  return aarch64_linux_stopped_data_address (&current_target, &addr);
+}
+
+/* Implement the "to_watchpoint_addr_within_range" target_ops method.  */
+
+static int
+aarch64_linux_watchpoint_addr_within_range (struct target_ops *target,
+					    CORE_ADDR addr,
+					    CORE_ADDR start, int length)
+{
+  return start <= addr && start + length - 1 >= addr;
+}
+
+/* -Wmissing-prototypes.  */
+void _initialize_aarch64_linux_nat (void);
+
+void
+_initialize_aarch64_linux_nat (void)
+{
+  struct target_ops *t;
+
+  /* Fill in the generic GNU/Linux methods.  */
+  t = linux_target ();
+
+  /* Add our register access methods.  */
+  t->to_fetch_registers = aarch64_linux_fetch_inferior_registers;
+  t->to_store_registers = aarch64_linux_store_inferior_registers;
+
+  t->to_read_description = aarch64_linux_read_description;
+
+  t->to_can_use_hw_breakpoint = aarch64_linux_can_use_hw_breakpoint;
+  t->to_insert_hw_breakpoint = aarch64_linux_insert_hw_breakpoint;
+  t->to_remove_hw_breakpoint = aarch64_linux_remove_hw_breakpoint;
+  t->to_region_ok_for_hw_watchpoint =
+    aarch64_linux_region_ok_for_hw_watchpoint;
+  t->to_insert_watchpoint = aarch64_linux_insert_watchpoint;
+  t->to_remove_watchpoint = aarch64_linux_remove_watchpoint;
+  t->to_stopped_by_watchpoint = aarch64_linux_stopped_by_watchpoint;
+  t->to_stopped_data_address = aarch64_linux_stopped_data_address;
+  t->to_watchpoint_addr_within_range =
+    aarch64_linux_watchpoint_addr_within_range;
+
+  /* Register the target.  */
+  linux_nat_add_target (t);
+}
diff --git a/gdb/config/aarch64/linux.mh b/gdb/config/aarch64/linux.mh
new file mode 100644
index 0000000..bc119c3
--- /dev/null
+++ b/gdb/config/aarch64/linux.mh
@@ -0,0 +1,9 @@
+# Host: AArch64 based machine running GNU/Linux
+
+NAT_FILE= config/nm-linux.h
+NATDEPFILES= inf-ptrace.o fork-child.o aarch64-linux-nat.o \
+	proc-service.o linux-thread-db.o linux-nat.o linux-fork.o \
+	linux-procfs.o linux-ptrace.o linux-osdata.o
+NAT_CDEPS = $(srcdir)/proc-service.list
+
+LOADLIBES= -ldl $(RDYNAMIC)
diff --git a/gdb/configure.host b/gdb/configure.host
index 7dc35e1..c5a7a3e 100644
--- a/gdb/configure.host
+++ b/gdb/configure.host
@@ -39,6 +39,7 @@ esac
 
 case "${host_cpu}" in
 
+aarch64*)		gdb_host_cpu=aarch64 ;;
 alpha*)			gdb_host_cpu=alpha ;;
 arm*)			gdb_host_cpu=arm ;;
 hppa*)			gdb_host_cpu=pa ;;
@@ -64,6 +65,8 @@ case "${host}" in
 
 *-*-darwin*)		gdb_host=darwin ;;
 
+aarch64*-*-linux*)	gdb_host=linux ;;
+
 alpha*-*-osf[3456789]*)	gdb_host=alpha-osf3 ;;
 alpha*-*-linux*)	gdb_host=alpha-linux ;;
 alpha*-*-freebsd* | alpha*-*-kfreebsd*-gnu)
-- 
1.7.9.5

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]