From acf7b9e14f07331bd010f673555eb9f1d324e391 Mon Sep 17 00:00:00 2001 From: Kevin Buettner Date: Thu, 13 Apr 2000 02:18:13 +0000 Subject: [PATCH] IA-64 watchpoint support. --- gdb/ChangeLog | 15 ++++ gdb/config/ia64/nm-linux.h | 29 +++++++ gdb/ia64-linux-nat.c | 171 +++++++++++++++++++++++++++++++++++++ 3 files changed, 215 insertions(+) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index f534d3e06a4..196727e60de 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,18 @@ +2000-04-12 Kevin Buettner + + * 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 * go32-nat.c (go32_insert_hw_breakpoint): When there are no more diff --git a/gdb/config/ia64/nm-linux.h b/gdb/config/ia64/nm-linux.h index afb86751a3a..90777251ddd 100644 --- a/gdb/config/ia64/nm-linux.h +++ b/gdb/config/ia64/nm-linux.h @@ -44,4 +44,33 @@ extern int ia64_register_u_addr(int, int); #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 */ diff --git a/gdb/ia64-linux-nat.c b/gdb/ia64-linux-nat.c index 485fa4a0d72..dfaa704f799 100644 --- a/gdb/ia64-linux-nat.c +++ b/gdb/ia64-linux-nat.c @@ -473,3 +473,174 @@ fill_fpregset (fpregsetp, regno) } } } + +#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; +} -- 2.30.2