diff options
Diffstat (limited to 'gdb/ppc-linux-nat.c')
-rw-r--r-- | gdb/ppc-linux-nat.c | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/gdb/ppc-linux-nat.c b/gdb/ppc-linux-nat.c index f96fba4..27c73c2 100644 --- a/gdb/ppc-linux-nat.c +++ b/gdb/ppc-linux-nat.c @@ -31,6 +31,7 @@ #include <signal.h> #include <sys/user.h> #include <sys/ioctl.h> +#include <sys/uio.h> #include "gdb_wait.h" #include <fcntl.h> #include <sys/procfs.h> @@ -528,10 +529,83 @@ fetch_spe_register (struct regcache *regcache, int tid, int regno) regcache->raw_supply (tdep->ppc_spefscr_regnum, &evrregs.spefscr); } +/* Use ptrace to fetch all registers from the register set with note + type REGSET_ID, size REGSIZE, and layout described by REGSET, from + process/thread TID and supply their values to REGCACHE. If ptrace + returns ENODATA to indicate the regset is unavailable, mark the + registers as unavailable in REGCACHE. */ + +static void +fetch_regset (struct regcache *regcache, int tid, + int regset_id, int regsetsize, const struct regset *regset) +{ + void *buf = alloca (regsetsize); + struct iovec iov; + + iov.iov_base = buf; + iov.iov_len = regsetsize; + + if (ptrace (PTRACE_GETREGSET, tid, regset_id, &iov) < 0) + { + if (errno == ENODATA) + regset->supply_regset (regset, regcache, -1, NULL, regsetsize); + else + perror_with_name (_("Couldn't get register set")); + } + else + regset->supply_regset (regset, regcache, -1, buf, regsetsize); +} + +/* Use ptrace to store register REGNUM of the regset with note type + REGSET_ID, size REGSETSIZE, and layout described by REGSET, from + REGCACHE back to process/thread TID. If REGNUM is -1 all registers + in the set are collected and stored. */ + +static void +store_regset (const struct regcache *regcache, int tid, int regnum, + int regset_id, int regsetsize, const struct regset *regset) +{ + void *buf = alloca (regsetsize); + struct iovec iov; + + iov.iov_base = buf; + iov.iov_len = regsetsize; + + /* Make sure that the buffer that will be stored has up to date values + for the registers that won't be collected. */ + if (ptrace (PTRACE_GETREGSET, tid, regset_id, &iov) < 0) + perror_with_name (_("Couldn't get register set")); + + regset->collect_regset (regset, regcache, regnum, buf, regsetsize); + + if (ptrace (PTRACE_SETREGSET, tid, regset_id, &iov) < 0) + perror_with_name (_("Couldn't set register set")); +} + +/* Check whether the kernel provides a register set with number + REGSET_ID of size REGSETSIZE for process/thread TID. */ + +static bool +check_regset (int tid, int regset_id, int regsetsize) +{ + void *buf = alloca (regsetsize); + struct iovec iov; + + iov.iov_base = buf; + iov.iov_len = regsetsize; + + if (ptrace (PTRACE_GETREGSET, tid, regset_id, &iov) >= 0 + || errno == ENODATA) + return true; + else + return false; +} + static void fetch_register (struct regcache *regcache, int tid, int regno) { struct gdbarch *gdbarch = regcache->arch (); + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); /* This isn't really an address. But ptrace thinks of it as one. */ CORE_ADDR regaddr = ppc_register_u_addr (gdbarch, regno); int bytes_transferred; @@ -565,6 +639,24 @@ fetch_register (struct regcache *regcache, int tid, int regno) fetch_spe_register (regcache, tid, regno); return; } + else if (regno == PPC_DSCR_REGNUM) + { + gdb_assert (tdep->ppc_dscr_regnum != -1); + + fetch_regset (regcache, tid, NT_PPC_DSCR, + PPC_LINUX_SIZEOF_DSCRREGSET, + &ppc32_linux_dscrregset); + return; + } + else if (regno == PPC_PPR_REGNUM) + { + gdb_assert (tdep->ppc_ppr_regnum != -1); + + fetch_regset (regcache, tid, NT_PPC_PPR, + PPC_LINUX_SIZEOF_PPRREGSET, + &ppc32_linux_pprregset); + return; + } if (regaddr == -1) { @@ -758,6 +850,14 @@ fetch_ppc_registers (struct regcache *regcache, int tid) fetch_vsx_registers (regcache, tid, -1); if (tdep->ppc_ev0_upper_regnum >= 0) fetch_spe_register (regcache, tid, -1); + if (tdep->ppc_ppr_regnum != -1) + fetch_regset (regcache, tid, NT_PPC_PPR, + PPC_LINUX_SIZEOF_PPRREGSET, + &ppc32_linux_pprregset); + if (tdep->ppc_dscr_regnum != -1) + fetch_regset (regcache, tid, NT_PPC_DSCR, + PPC_LINUX_SIZEOF_DSCRREGSET, + &ppc32_linux_dscrregset); } /* Fetch registers from the child process. Fetch all registers if @@ -938,6 +1038,24 @@ store_register (const struct regcache *regcache, int tid, int regno) store_spe_register (regcache, tid, regno); return; } + else if (regno == PPC_DSCR_REGNUM) + { + gdb_assert (tdep->ppc_dscr_regnum != -1); + + store_regset (regcache, tid, regno, NT_PPC_DSCR, + PPC_LINUX_SIZEOF_DSCRREGSET, + &ppc32_linux_dscrregset); + return; + } + else if (regno == PPC_PPR_REGNUM) + { + gdb_assert (tdep->ppc_ppr_regnum != -1); + + store_regset (regcache, tid, regno, NT_PPC_PPR, + PPC_LINUX_SIZEOF_PPRREGSET, + &ppc32_linux_pprregset); + return; + } if (regaddr == -1) return; @@ -1149,6 +1267,14 @@ store_ppc_registers (const struct regcache *regcache, int tid) store_vsx_registers (regcache, tid, -1); if (tdep->ppc_ev0_upper_regnum >= 0) store_spe_register (regcache, tid, -1); + if (tdep->ppc_ppr_regnum != -1) + store_regset (regcache, tid, -1, NT_PPC_PPR, + PPC_LINUX_SIZEOF_PPRREGSET, + &ppc32_linux_pprregset); + if (tdep->ppc_dscr_regnum != -1) + store_regset (regcache, tid, -1, NT_PPC_DSCR, + PPC_LINUX_SIZEOF_DSCRREGSET, + &ppc32_linux_dscrregset); } /* Fetch the AT_HWCAP entry from the aux vector. */ @@ -1163,6 +1289,19 @@ ppc_linux_get_hwcap (void) return field; } +/* Fetch the AT_HWCAP2 entry from the aux vector. */ + +static CORE_ADDR +ppc_linux_get_hwcap2 (void) +{ + CORE_ADDR field; + + if (target_auxv_search (current_top_target (), AT_HWCAP2, &field) != 1) + return 0; + + return field; +} + /* The cached DABR value, to install in new threads. This variable is used when the PowerPC HWDEBUG ptrace interface is not available. */ @@ -2222,6 +2361,7 @@ ppc_linux_nat_target::read_description () features.wordsize = ppc_linux_target_wordsize (tid); CORE_ADDR hwcap = ppc_linux_get_hwcap (); + CORE_ADDR hwcap2 = ppc_linux_get_hwcap2 (); if (have_ptrace_getsetvsxregs && (hwcap & PPC_FEATURE_HAS_VSX)) @@ -2256,6 +2396,11 @@ ppc_linux_nat_target::read_description () features.isa205 = ppc_linux_has_isa205 (hwcap); + if ((hwcap2 & PPC_FEATURE2_DSCR) + && check_regset (tid, NT_PPC_PPR, PPC_LINUX_SIZEOF_PPRREGSET) + && check_regset (tid, NT_PPC_DSCR, PPC_LINUX_SIZEOF_DSCRREGSET)) + features.ppr_dscr = true; + return ppc_linux_match_description (features); } |