diff options
author | Wu Zhou <woodzltc@cn.ibm.com> | 2006-02-08 05:41:06 +0000 |
---|---|---|
committer | Wu Zhou <woodzltc@cn.ibm.com> | 2006-02-08 05:41:06 +0000 |
commit | e0d24f8d6e485843de9fac1ba21412491e07be7b (patch) | |
tree | d7c85f32289f7427f9d05d1bc0edd424c028d3cf /gdb/ppc-linux-nat.c | |
parent | 0d2a63896314177cbf183995e4a4907a4723688b (diff) | |
download | gdb-e0d24f8d6e485843de9fac1ba21412491e07be7b.zip gdb-e0d24f8d6e485843de9fac1ba21412491e07be7b.tar.gz gdb-e0d24f8d6e485843de9fac1ba21412491e07be7b.tar.bz2 |
* ppc-linux-nat.c (PTRACE_GET_DEBUGREG, PTRACE_SET_DEBUGREG,
PTRACE_GETSIGINFO): Define.
(last_stopped_data_address): New.
(ppc_linux_check_watch_resources): New function.
(ppc_linux_region_ok_for_hw_watchpoint): New function.
(ppc_linux_insert_watchpoint): New function.
(ppc_linux_remove_watchpoint): New function.
(ppc_linux_stopped_data_address): New function.
(ppc_linux_stopped_by_watchpoint): New function.
(_initialize_ppc_linux_nat): Set the above hardware watchpoint
related target vectors.
* rs6000-tdep.c (rs6000_gdbarch_init): Set PPC architectures
to have nonsteppable watchpoint.
* target.c (default_region_ok_for_hw_watchpoint,
debug_to_region_ok_for_hw_watchpoint): New prototypes.
(update_current_target): Inherit to_region_ok_for_hw_watchpoint
and set default to_region_ok_for_hw_watchpoint.
(default_region_ok_for_hw_watchpoint): New function.
(debug_to_region_ok_for_hw_watchpoint): New function.
(setup_target_debug): Set to_region_ok_for_hw_watchpoint of
debug_target.
* target.h (struct target_ops): Add a new target vector
to_region_ok_for_hw_watchpoint.
(TARGET_REGION_OK_FOR_HW_WATCHPOINT): Define this if it is not
defined anyplace else.
Diffstat (limited to 'gdb/ppc-linux-nat.c')
-rw-r--r-- | gdb/ppc-linux-nat.c | 138 |
1 files changed, 137 insertions, 1 deletions
diff --git a/gdb/ppc-linux-nat.c b/gdb/ppc-linux-nat.c index c8c9376..b52eca2 100644 --- a/gdb/ppc-linux-nat.c +++ b/gdb/ppc-linux-nat.c @@ -81,6 +81,16 @@ #define PTRACE_SETEVRREGS 21 #endif +/* Similarly for the hardware watchpoint support. */ +#ifndef PTRACE_GET_DEBUGREG +#define PTRACE_GET_DEBUGREG 25 +#endif +#ifndef PTRACE_SET_DEBUGREG +#define PTRACE_SET_DEBUGREG 26 +#endif +#ifndef PTRACE_GETSIGINFO +#define PTRACE_GETSIGINFO 0x4202 +#endif /* This oddity is because the Linux kernel defines elf_vrregset_t as an array of 33 16 bytes long elements. I.e. it leaves out vrsave. @@ -146,6 +156,7 @@ struct gdb_evrregset_t error. */ int have_ptrace_getvrregs = 1; +static CORE_ADDR last_stopped_data_address = 0; /* Non-zero if our kernel may support the PTRACE_GETEVRREGS and PTRACE_SETEVRREGS requests, for reading and writing the SPE @@ -153,7 +164,6 @@ int have_ptrace_getvrregs = 1; error. */ int have_ptrace_getsetevrregs = 1; - int kernel_u_size (void) { @@ -777,6 +787,124 @@ store_ppc_registers (int tid) store_spe_register (tid, -1); } +static int +ppc_linux_check_watch_resources (int type, int cnt, int ot) +{ + int tid; + ptid_t ptid = inferior_ptid; + + /* DABR (data address breakpoint register) is optional for PPC variants. + Some variants have one DABR, others have none. So CNT can't be larger + than 1. */ + if (cnt > 1) + return 0; + + /* We need to know whether ptrace supports PTRACE_SET_DEBUGREG and whether + the target has DABR. If either answer is no, the ptrace call will + return -1. Fail in that case. */ + tid = TIDGET (ptid); + if (tid == 0) + tid = PIDGET (ptid); + + if (ptrace (PTRACE_SET_DEBUGREG, tid, 0, 0) == -1) + return 0; + return 1; +} + +static int +ppc_linux_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len) +{ + /* Handle sub-8-byte quantities. */ + if (len <= 0) + return 0; + + /* addr+len must fall in the 8 byte watchable region. */ + if ((addr + len) > (addr & ~7) + 8) + return 0; + + return 1; +} + +/* Set a watchpoint of type TYPE at address ADDR. */ +static long +ppc_linux_insert_watchpoint (CORE_ADDR addr, int len, int rw) +{ + int tid; + long dabr_value; + ptid_t ptid = inferior_ptid; + + dabr_value = addr & ~7; + switch (rw) + { + case hw_read: + /* Set read and translate bits. */ + dabr_value |= 5; + break; + case hw_write: + /* Set write and translate bits. */ + dabr_value |= 6; + break; + case hw_access: + /* Set read, write and translate bits. */ + dabr_value |= 7; + break; + } + + tid = TIDGET (ptid); + if (tid == 0) + tid = PIDGET (ptid); + + return ptrace (PTRACE_SET_DEBUGREG, tid, 0, dabr_value); +} + +static long +ppc_linux_remove_watchpoint (CORE_ADDR addr, int len) +{ + int tid; + ptid_t ptid = inferior_ptid; + + tid = TIDGET (ptid); + if (tid == 0) + tid = PIDGET (ptid); + + return ptrace (PTRACE_SET_DEBUGREG, tid, 0, 0); +} + +static int +ppc_linux_stopped_data_address (struct target_ops *target, CORE_ADDR *addr_p) +{ + if (last_stopped_data_address) + { + *addr_p = last_stopped_data_address; + last_stopped_data_address = 0; + return 1; + } + return 0; +} + +static int +ppc_linux_stopped_by_watchpoint (void) +{ + int tid; + struct siginfo siginfo; + ptid_t ptid = inferior_ptid; + CORE_ADDR *addr_p; + + tid = TIDGET(ptid); + if (tid == 0) + tid = PIDGET (ptid); + + errno = 0; + ptrace (PTRACE_GETSIGINFO, tid, (PTRACE_TYPE_ARG3) 0, &siginfo); + + if (errno != 0 || siginfo.si_signo != SIGTRAP || + (siginfo.si_code & 0xffff) != 0x0004) + return 0; + + last_stopped_data_address = (CORE_ADDR) siginfo.si_addr; + return 1; +} + static void ppc_linux_store_inferior_registers (int regno) { @@ -900,6 +1028,14 @@ _initialize_ppc_linux_nat (void) t->to_fetch_registers = ppc_linux_fetch_inferior_registers; t->to_store_registers = ppc_linux_store_inferior_registers; + /* Add our watchpoint methods. */ + t->to_can_use_hw_breakpoint = ppc_linux_check_watch_resources; + t->to_region_ok_for_hw_watchpoint = ppc_linux_region_ok_for_hw_watchpoint; + t->to_insert_watchpoint = ppc_linux_insert_watchpoint; + t->to_remove_watchpoint = ppc_linux_remove_watchpoint; + t->to_stopped_by_watchpoint = ppc_linux_stopped_by_watchpoint; + t->to_stopped_data_address = ppc_linux_stopped_data_address; + /* Register the target. */ add_target (t); } |