+/* Is the breakpoint control value CONTROL initialized? */
+static int
+arm_hwbp_control_is_initialized (arm_hwbp_control_t control)
+{
+ return control != 0;
+}
+
+/* Change a breakpoint control word so that it is in the disabled state. */
+static arm_hwbp_control_t
+arm_hwbp_control_disable (arm_hwbp_control_t control)
+{
+ return control & ~0x1;
+}
+
+/* Are two break-/watch-points equal? */
+static int
+arm_linux_hw_breakpoint_equal (const struct arm_linux_hw_breakpoint *p1,
+ const struct arm_linux_hw_breakpoint *p2)
+{
+ return p1->address == p2->address && p1->control == p2->control;
+}
+
+/* Convert a raw breakpoint type to an enum arm_hwbp_type. */
+
+static int
+raw_bkpt_type_to_arm_hwbp_type (enum raw_bkpt_type raw_type)
+{
+ switch (raw_type)
+ {
+ case raw_bkpt_type_hw:
+ return arm_hwbp_break;
+ case raw_bkpt_type_write_wp:
+ return arm_hwbp_store;
+ case raw_bkpt_type_read_wp:
+ return arm_hwbp_load;
+ case raw_bkpt_type_access_wp:
+ return arm_hwbp_access;
+ default:
+ gdb_assert_not_reached ("unhandled raw type");
+ }
+}
+
+/* Initialize the hardware breakpoint structure P for a breakpoint or
+ watchpoint at ADDR to LEN. The type of watchpoint is given in TYPE.
+ Returns -1 if TYPE is unsupported, or -2 if the particular combination
+ of ADDR and LEN cannot be implemented. Otherwise, returns 0 if TYPE
+ represents a breakpoint and 1 if type represents a watchpoint. */
+static int
+arm_linux_hw_point_initialize (enum raw_bkpt_type raw_type, CORE_ADDR addr,
+ int len, struct arm_linux_hw_breakpoint *p)
+{
+ arm_hwbp_type hwbp_type;
+ unsigned mask;
+
+ hwbp_type = raw_bkpt_type_to_arm_hwbp_type (raw_type);
+
+ if (hwbp_type == arm_hwbp_break)
+ {
+ /* For breakpoints, the length field encodes the mode. */
+ switch (len)
+ {
+ case 2: /* 16-bit Thumb mode breakpoint */
+ case 3: /* 32-bit Thumb mode breakpoint */
+ mask = 0x3;
+ addr &= ~1;
+ break;
+ case 4: /* 32-bit ARM mode breakpoint */
+ mask = 0xf;
+ addr &= ~3;
+ break;
+ default:
+ /* Unsupported. */
+ return -2;
+ }
+ }
+ else
+ {
+ CORE_ADDR max_wp_length = arm_linux_get_hw_watchpoint_max_length ();
+ CORE_ADDR aligned_addr;
+
+ /* Can not set watchpoints for zero or negative lengths. */
+ if (len <= 0)
+ return -2;
+ /* The current ptrace interface can only handle watchpoints that are a
+ power of 2. */
+ if ((len & (len - 1)) != 0)
+ return -2;
+
+ /* Test that the range [ADDR, ADDR + LEN) fits into the largest address
+ range covered by a watchpoint. */
+ aligned_addr = addr & ~(max_wp_length - 1);
+ if (aligned_addr + max_wp_length < addr + len)
+ return -2;
+
+ mask = (1 << len) - 1;
+ }
+
+ p->address = (unsigned int) addr;
+ p->control = arm_hwbp_control_initialize (mask, hwbp_type, 1);
+
+ return hwbp_type != arm_hwbp_break;
+}
+
+/* Callback to mark a watch-/breakpoint to be updated in all threads of
+ the current process. */
+
+struct update_registers_data
+{
+ int watch;
+ int i;
+};
+
+static int
+update_registers_callback (struct inferior_list_entry *entry, void *arg)
+{
+ struct thread_info *thread = (struct thread_info *) entry;
+ struct lwp_info *lwp = get_thread_lwp (thread);
+ struct update_registers_data *data = (struct update_registers_data *) arg;
+
+ /* Only update the threads of the current process. */
+ if (pid_of (thread) == pid_of (current_thread))
+ {
+ /* The actual update is done later just before resuming the lwp,
+ we just mark that the registers need updating. */
+ if (data->watch)
+ lwp->arch_private->wpts_changed[data->i] = 1;
+ else
+ lwp->arch_private->bpts_changed[data->i] = 1;
+
+ /* If the lwp isn't stopped, force it to momentarily pause, so
+ we can update its breakpoint registers. */
+ if (!lwp->stopped)
+ linux_stop_lwp (lwp);
+ }
+
+ return 0;
+}
+
+static int
+arm_supports_z_point_type (char z_type)
+{
+ switch (z_type)
+ {
+ case Z_PACKET_HW_BP:
+ case Z_PACKET_WRITE_WP:
+ case Z_PACKET_READ_WP:
+ case Z_PACKET_ACCESS_WP:
+ return 1;
+ default:
+ /* Leave the handling of sw breakpoints with the gdb client. */
+ return 0;
+ }
+}
+
+/* Insert hardware break-/watchpoint. */
+static int
+arm_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int len, struct raw_breakpoint *bp)
+{
+ struct process_info *proc = current_process ();
+ struct arm_linux_hw_breakpoint p, *pts;
+ int watch, i, count;
+
+ watch = arm_linux_hw_point_initialize (type, addr, len, &p);
+ if (watch < 0)
+ {
+ /* Unsupported. */
+ return watch == -1 ? 1 : -1;
+ }
+
+ if (watch)
+ {
+ count = arm_linux_get_hw_watchpoint_count ();
+ pts = proc->priv->arch_private->wpts;
+ }
+ else
+ {
+ count = arm_linux_get_hw_breakpoint_count ();
+ pts = proc->priv->arch_private->bpts;
+ }
+
+ for (i = 0; i < count; i++)
+ if (!arm_hwbp_control_is_enabled (pts[i].control))
+ {
+ struct update_registers_data data = { watch, i };
+ pts[i] = p;
+ find_inferior (&all_threads, update_registers_callback, &data);
+ return 0;
+ }
+
+ /* We're out of watchpoints. */
+ return -1;
+}
+
+/* Remove hardware break-/watchpoint. */
+static int
+arm_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int len, struct raw_breakpoint *bp)
+{
+ struct process_info *proc = current_process ();
+ struct arm_linux_hw_breakpoint p, *pts;
+ int watch, i, count;
+
+ watch = arm_linux_hw_point_initialize (type, addr, len, &p);
+ if (watch < 0)
+ {
+ /* Unsupported. */
+ return -1;
+ }
+
+ if (watch)
+ {
+ count = arm_linux_get_hw_watchpoint_count ();
+ pts = proc->priv->arch_private->wpts;
+ }
+ else
+ {
+ count = arm_linux_get_hw_breakpoint_count ();
+ pts = proc->priv->arch_private->bpts;
+ }
+
+ for (i = 0; i < count; i++)
+ if (arm_linux_hw_breakpoint_equal (&p, pts + i))
+ {
+ struct update_registers_data data = { watch, i };
+ pts[i].control = arm_hwbp_control_disable (pts[i].control);
+ find_inferior (&all_threads, update_registers_callback, &data);
+ return 0;
+ }
+
+ /* No watchpoint matched. */
+ return -1;
+}
+
+/* Return whether current thread is stopped due to a watchpoint. */
+static int
+arm_stopped_by_watchpoint (void)
+{
+ struct lwp_info *lwp = get_thread_lwp (current_thread);
+ siginfo_t siginfo;
+
+ /* We must be able to set hardware watchpoints. */
+ if (arm_linux_get_hw_watchpoint_count () == 0)
+ return 0;
+
+ /* Retrieve siginfo. */
+ errno = 0;
+ ptrace (PTRACE_GETSIGINFO, lwpid_of (current_thread), 0, &siginfo);
+ if (errno != 0)
+ return 0;
+
+ /* This must be a hardware breakpoint. */
+ if (siginfo.si_signo != SIGTRAP
+ || (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
+ return 0;
+
+ /* If we are in a positive slot then we're looking at a breakpoint and not
+ a watchpoint. */
+ if (siginfo.si_errno >= 0)
+ return 0;
+
+ /* Cache stopped data address for use by arm_stopped_data_address. */
+ lwp->arch_private->stopped_data_address
+ = (CORE_ADDR) (uintptr_t) siginfo.si_addr;
+
+ return 1;
+}
+
+/* Return data address that triggered watchpoint. Called only if
+ arm_stopped_by_watchpoint returned true. */
+static CORE_ADDR
+arm_stopped_data_address (void)
+{
+ struct lwp_info *lwp = get_thread_lwp (current_thread);
+ return lwp->arch_private->stopped_data_address;
+}
+
+/* Called when a new process is created. */
+static struct arch_process_info *
+arm_new_process (void)
+{
+ struct arch_process_info *info = xcalloc (1, sizeof (*info));
+ return info;
+}
+
+/* Called when a new thread is detected. */
+static struct arch_lwp_info *
+arm_new_thread (void)
+{
+ struct arch_lwp_info *info = xcalloc (1, sizeof (*info));
+ int i;
+
+ for (i = 0; i < MAX_BPTS; i++)
+ info->bpts_changed[i] = 1;
+ for (i = 0; i < MAX_WPTS; i++)
+ info->wpts_changed[i] = 1;
+
+ return info;
+}
+
+/* Called when resuming a thread.
+ If the debug regs have changed, update the thread's copies. */
+static void
+arm_prepare_to_resume (struct lwp_info *lwp)
+{
+ struct thread_info *thread = get_lwp_thread (lwp);
+ int pid = lwpid_of (thread);
+ struct process_info *proc = find_process_pid (pid_of (thread));
+ struct arch_process_info *proc_info = proc->priv->arch_private;
+ struct arch_lwp_info *lwp_info = lwp->arch_private;
+ int i;
+
+ for (i = 0; i < arm_linux_get_hw_breakpoint_count (); i++)
+ if (lwp_info->bpts_changed[i])
+ {
+ errno = 0;
+
+ if (arm_hwbp_control_is_enabled (proc_info->bpts[i].control))
+ if (ptrace (PTRACE_SETHBPREGS, pid,
+ (PTRACE_TYPE_ARG3) ((i << 1) + 1),
+ &proc_info->bpts[i].address) < 0)
+ perror_with_name ("Unexpected error setting breakpoint address");
+
+ if (arm_hwbp_control_is_initialized (proc_info->bpts[i].control))
+ if (ptrace (PTRACE_SETHBPREGS, pid,
+ (PTRACE_TYPE_ARG3) ((i << 1) + 2),
+ &proc_info->bpts[i].control) < 0)
+ perror_with_name ("Unexpected error setting breakpoint");
+
+ lwp_info->bpts_changed[i] = 0;
+ }
+
+ for (i = 0; i < arm_linux_get_hw_watchpoint_count (); i++)
+ if (lwp_info->wpts_changed[i])
+ {
+ errno = 0;
+
+ if (arm_hwbp_control_is_enabled (proc_info->wpts[i].control))
+ if (ptrace (PTRACE_SETHBPREGS, pid,
+ (PTRACE_TYPE_ARG3) -((i << 1) + 1),
+ &proc_info->wpts[i].address) < 0)
+ perror_with_name ("Unexpected error setting watchpoint address");
+
+ if (arm_hwbp_control_is_initialized (proc_info->wpts[i].control))
+ if (ptrace (PTRACE_SETHBPREGS, pid,
+ (PTRACE_TYPE_ARG3) -((i << 1) + 2),
+ &proc_info->wpts[i].control) < 0)
+ perror_with_name ("Unexpected error setting watchpoint");
+
+ lwp_info->wpts_changed[i] = 0;
+ }
+}
+
+
+static int
+arm_get_hwcap (unsigned long *valp)
+{
+ unsigned char *data = alloca (8);
+ int offset = 0;
+
+ while ((*the_target->read_auxv) (offset, data, 8) == 8)
+ {
+ unsigned int *data_p = (unsigned int *)data;
+ if (data_p[0] == AT_HWCAP)
+ {
+ *valp = data_p[1];
+ return 1;
+ }
+
+ offset += 8;
+ }
+
+ *valp = 0;
+ return 0;
+}
+
+static const struct target_desc *
+arm_read_description (void)
+{
+ int pid = lwpid_of (current_thread);
+
+ /* Query hardware watchpoint/breakpoint capabilities. */
+ arm_linux_init_hwbp_cap (pid);
+
+ arm_hwcap = 0;
+ if (arm_get_hwcap (&arm_hwcap) == 0)
+ return tdesc_arm;
+
+ if (arm_hwcap & HWCAP_IWMMXT)
+ return tdesc_arm_with_iwmmxt;
+
+ if (arm_hwcap & HWCAP_VFP)
+ {
+ const struct target_desc *result;
+ char *buf;
+
+ /* NEON implies either no VFP, or VFPv3-D32. We only support
+ it with VFP. */
+ if (arm_hwcap & HWCAP_NEON)
+ result = tdesc_arm_with_neon;
+ else if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3)
+ result = tdesc_arm_with_vfpv3;
+ else
+ result = tdesc_arm_with_vfpv2;
+
+ /* Now make sure that the kernel supports reading these
+ registers. Support was added in 2.6.30. */
+ errno = 0;
+ buf = xmalloc (32 * 8 + 4);
+ if (ptrace (PTRACE_GETVFPREGS, pid, 0, buf) < 0
+ && errno == EIO)
+ {
+ arm_hwcap = 0;
+ result = tdesc_arm;
+ }
+ free (buf);
+
+ return result;
+ }
+
+ /* The default configuration uses legacy FPA registers, probably
+ simulated. */
+ return tdesc_arm;
+}
+
+static void
+arm_arch_setup (void)
+{
+ current_process ()->tdesc = arm_read_description ();
+}
+
+static struct regset_info arm_regsets[] = {
+ { PTRACE_GETREGS, PTRACE_SETREGS, 0, 18 * 4,
+ GENERAL_REGS,
+ arm_fill_gregset, arm_store_gregset },
+ { PTRACE_GETWMMXREGS, PTRACE_SETWMMXREGS, 0, 16 * 8 + 6 * 4,
+ EXTENDED_REGS,
+ arm_fill_wmmxregset, arm_store_wmmxregset },
+ { PTRACE_GETVFPREGS, PTRACE_SETVFPREGS, 0, 32 * 8 + 4,
+ EXTENDED_REGS,
+ arm_fill_vfpregset, arm_store_vfpregset },
+ { 0, 0, 0, -1, -1, NULL, NULL }
+};
+
+static struct regsets_info arm_regsets_info =
+ {
+ arm_regsets, /* regsets */
+ 0, /* num_regsets */
+ NULL, /* disabled_regsets */
+ };
+
+static struct usrregs_info arm_usrregs_info =
+ {
+ arm_num_regs,
+ arm_regmap,
+ };
+
+static struct regs_info regs_info =
+ {
+ NULL, /* regset_bitmap */
+ &arm_usrregs_info,
+ &arm_regsets_info
+ };
+
+static const struct regs_info *
+arm_regs_info (void)
+{
+ return ®s_info;
+}
+
+struct linux_target_ops the_low_target = {
+ arm_arch_setup,
+ arm_regs_info,
+ arm_cannot_fetch_register,
+ arm_cannot_store_register,
+ NULL, /* fetch_register */
+ arm_get_pc,
+ arm_set_pc,
+
+ /* Define an ARM-mode breakpoint; we only set breakpoints in the C
+ library, which is most likely to be ARM. If the kernel supports
+ clone events, we will never insert a breakpoint, so even a Thumb
+ C library will work; so will mixing EABI/non-EABI gdbserver and
+ application. */
+#ifndef __ARM_EABI__
+ (const unsigned char *) &arm_breakpoint,
+#else
+ (const unsigned char *) &arm_eabi_breakpoint,
+#endif
+ arm_breakpoint_len,
+ arm_reinsert_addr,
+ 0,
+ arm_breakpoint_at,
+ arm_supports_z_point_type,
+ arm_insert_point,
+ arm_remove_point,
+ arm_stopped_by_watchpoint,
+ arm_stopped_data_address,
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ arm_new_process,
+ arm_new_thread,
+ arm_prepare_to_resume,
+};
+
+void
+initialize_low_arch (void)
+{
+ /* Initialize the Linux target descriptions. */
+ init_registers_arm ();
+ init_registers_arm_with_iwmmxt ();
+ init_registers_arm_with_vfpv2 ();
+ init_registers_arm_with_vfpv3 ();
+ init_registers_arm_with_neon ();
+
+ initialize_regsets_info (&arm_regsets_info);
+}