This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: [PATCH 4/5] aarch64-linux-nat.
- From: Marcus Shawcroft <marcus dot shawcroft at arm dot com>
- To: Joel Brobecker <brobecker at adacore dot com>
- Cc: "gdb-patches at sourceware dot org" <gdb-patches at sourceware dot org>
- Date: Mon, 07 Jan 2013 14:34:24 +0000
- Subject: Re: [PATCH 4/5] aarch64-linux-nat.
- References: <50AD0323.1050306@arm.com> <20121223071017.GN5370@adacore.com>
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 = ®s;
+ 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 *) ®s[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 = ®s;
+ 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 *) ®s[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 = ®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."));
+ return;
+ }
+
+ for (regno = AARCH64_V0_REGNUM; regno <= AARCH64_V31_REGNUM; regno++)
+ regcache_raw_supply (regcache, regno,
+ (char *) ®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);
+}
+
+/* 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 = ®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."));
+ 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 *) ®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);
+
+ 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 = ®s;
+ 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 = ®s;
+ 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 (¤t_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