+2000-04-12 Kevin Buettner <kevinb@redhat.com>
+
+ * ia64-linux-nat.c (IA64_PSR_DB, IA64_PSR_DD): Define.
+ (fetch_debug_register, fetch_debug_register_pair,
+ store_debug_register, store_debug_register_pair, is_power_of_2,
+ enable_watchpoints_in_psr, ia64_linux_insert_watchpoint,
+ ia64_linux_remove_watchpoint, ia64_linux_stopped_by_watchpoint):
+ New functions.
+ * config/ia64/nm-linux.h (TARGET_HAS_HARDWARE_WATCHPOINTS,
+ TARGET_CAN_USE_HARDWARE_WATCHPOINT, HAVE_STEPPABLE_WATCHPOINT,
+ STOPPED_BY_WATCHPOINT, target_insert_watchpoint,
+ target_remove_watchpoint): Define.
+ (ia64_linux_stopped_by_watchpoint, ia64_linux_insert_watchpoint,
+ ia64_linux_remove_watchpoint): Declare.
+
2000-04-12 Eli Zaretskii <eliz@is.elta.co.il>
* go32-nat.c (go32_insert_hw_breakpoint): When there are no more
#define PTRACE_ARG3_TYPE long
#define PTRACE_XFER_TYPE long
+/* Hardware watchpoints */
+
+#define TARGET_HAS_HARDWARE_WATCHPOINTS
+
+#define TARGET_CAN_USE_HARDWARE_WATCHPOINT(type, cnt, ot) 1
+
+/* The IA-64 architecture can step over a watch point (without triggering
+ it again) if the "dd" (data debug fault disable) bit in the processor
+ status word is set.
+
+ This PSR bit is set in ia64_linux_stopped_by_watchpoint when the
+ code there has determined that a hardware watchpoint has indeed
+ been hit. The CPU will then be able to execute one instruction
+ without triggering a watchpoint. */
+#define HAVE_STEPPABLE_WATCHPOINT 1
+
+#define STOPPED_BY_WATCHPOINT(W) \
+ ia64_linux_stopped_by_watchpoint (inferior_pid)
+extern CORE_ADDR ia64_linux_stopped_by_watchpoint (int);
+
+#define target_insert_watchpoint(addr, len, type) \
+ ia64_linux_insert_watchpoint (inferior_pid, addr, len, type)
+extern int ia64_linux_insert_watchpoint (int pid, CORE_ADDR addr,
+ int len, int rw);
+
+#define target_remove_watchpoint(addr, len, type) \
+ ia64_linux_remove_watchpoint (inferior_pid, addr, len)
+extern int ia64_linux_remove_watchpoint (int pid, CORE_ADDR addr, int len);
+
#endif /* #ifndef NM_LINUX_H */
}
}
}
+
+#define IA64_PSR_DB (1UL << 24)
+#define IA64_PSR_DD (1UL << 39)
+
+static void
+enable_watchpoints_in_psr (int pid)
+{
+ CORE_ADDR psr;
+
+ psr = read_register_pid (IA64_PSR_REGNUM, pid);
+ if (!(psr & IA64_PSR_DB))
+ {
+ psr |= IA64_PSR_DB; /* Set the db bit - this enables hardware
+ watchpoints and breakpoints. */
+ write_register_pid (IA64_PSR_REGNUM, psr, pid);
+ }
+}
+
+static long
+fetch_debug_register (int pid, int idx)
+{
+ long val;
+ int tid;
+
+ tid = TIDGET(pid);
+ if (tid == 0)
+ tid = pid;
+
+ val = ptrace (PT_READ_U, tid, (PTRACE_ARG3_TYPE) (PT_DBR + 8 * idx), 0);
+
+ return val;
+}
+
+static void
+store_debug_register (int pid, int idx, long val)
+{
+ int tid;
+
+ tid = TIDGET(pid);
+ if (tid == 0)
+ tid = pid;
+
+ (void) ptrace (PT_WRITE_U, tid, (PTRACE_ARG3_TYPE) (PT_DBR + 8 * idx), val);
+}
+
+static void
+fetch_debug_register_pair (int pid, int idx, long *dbr_addr, long *dbr_mask)
+{
+ if (dbr_addr)
+ *dbr_addr = fetch_debug_register (pid, 2 * idx);
+ if (dbr_mask)
+ *dbr_mask = fetch_debug_register (pid, 2 * idx + 1);
+}
+
+static void
+store_debug_register_pair (int pid, int idx, long *dbr_addr, long *dbr_mask)
+{
+ if (dbr_addr)
+ store_debug_register (pid, 2 * idx, *dbr_addr);
+ if (dbr_mask)
+ store_debug_register (pid, 2 * idx + 1, *dbr_mask);
+}
+
+static int
+is_power_of_2 (int val)
+{
+ int i, onecount;
+
+ onecount = 0;
+ for (i = 0; i < 8 * sizeof (val); i++)
+ if (val & (1 << i))
+ onecount++;
+
+ return onecount <= 1;
+}
+
+int
+ia64_linux_insert_watchpoint (int pid, CORE_ADDR addr, int len, int rw)
+{
+ int idx;
+ long dbr_addr, dbr_mask;
+ int max_watchpoints = 4;
+
+ if (len <= 0 || !is_power_of_2 (len))
+ return -1;
+
+ for (idx = 0; idx < max_watchpoints; idx++)
+ {
+ fetch_debug_register_pair (pid, idx, NULL, &dbr_mask);
+ if ((dbr_mask & (0x3UL << 62)) == 0)
+ {
+ /* Exit loop if both r and w bits clear */
+ break;
+ }
+ }
+
+ if (idx == max_watchpoints)
+ return -1;
+
+ dbr_addr = (long) addr;
+ dbr_mask = (~(len - 1) & 0x00ffffffffffffffL); /* construct mask to match */
+ dbr_mask |= 0x0800000000000000L; /* Only match privilege level 3 */
+ switch (rw)
+ {
+ case hw_write:
+ dbr_mask |= (1L << 62); /* Set w bit */
+ break;
+ case hw_read:
+ dbr_mask |= (1L << 63); /* Set r bit */
+ break;
+ case hw_access:
+ dbr_mask |= (3L << 62); /* Set both r and w bits */
+ break;
+ default:
+ return -1;
+ }
+
+ store_debug_register_pair (pid, idx, &dbr_addr, &dbr_mask);
+ enable_watchpoints_in_psr (pid);
+
+ return 0;
+}
+
+int
+ia64_linux_remove_watchpoint (int pid, CORE_ADDR addr, int len)
+{
+ int idx;
+ long dbr_addr, dbr_mask;
+ int max_watchpoints = 4;
+
+ if (len <= 0 || !is_power_of_2 (len))
+ return -1;
+
+ for (idx = 0; idx < max_watchpoints; idx++)
+ {
+ fetch_debug_register_pair (pid, idx, &dbr_addr, &dbr_mask);
+ if ((dbr_mask & (0x3UL << 62)) && addr == (CORE_ADDR) dbr_addr)
+ {
+ dbr_addr = 0;
+ dbr_mask = 0;
+ store_debug_register_pair (pid, idx, &dbr_addr, &dbr_mask);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+CORE_ADDR
+ia64_linux_stopped_by_watchpoint (int pid)
+{
+ CORE_ADDR psr;
+ int tid;
+ struct siginfo siginfo;
+
+ tid = TIDGET(pid);
+ if (tid == 0)
+ tid = pid;
+
+ errno = 0;
+ ptrace (PTRACE_GETSIGINFO, tid, (PTRACE_ARG3_TYPE) 0, &siginfo);
+
+ if (errno != 0 || siginfo.si_code != 4 /* TRAP_HWBKPT */)
+ return 0;
+
+ psr = read_register_pid (IA64_PSR_REGNUM, pid);
+ psr |= IA64_PSR_DD; /* Set the dd bit - this will disable the watchpoint
+ for the next instruction */
+ write_register_pid (IA64_PSR_REGNUM, psr, pid);
+
+ return (CORE_ADDR) siginfo.si_addr;
+}