/* PPC GNU/Linux native support.
- Copyright (C) 1988-2020 Free Software Foundation, Inc.
+ Copyright (C) 1988-2022 Free Software Foundation, Inc.
This file is part of GDB.
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
-#include "observable.h"
#include "frame.h"
#include "inferior.h"
#include "gdbthread.h"
#include "nat/gdb_ptrace.h"
#include "nat/linux-ptrace.h"
#include "inf-ptrace.h"
+#include <algorithm>
+#include <unordered_map>
+#include <list>
/* Prototypes for supply_gregset etc. */
#include "gregset.h"
#include "arch/ppc-linux-tdesc.h"
#include "nat/ppc-linux.h"
#include "linux-tdep.h"
+#include "expop.h"
/* Similarly for the hardware watchpoint support. These requests are used
when the PowerPC HWDEBUG ptrace interface is not available. */
struct ppc_debug_info
{
- uint32_t version; /* Only version 1 exists to date. */
- uint32_t num_instruction_bps;
- uint32_t num_data_bps;
- uint32_t num_condition_regs;
- uint32_t data_bp_alignment;
- uint32_t sizeof_condition; /* size of the DVC register. */
- uint64_t features;
+ uint32_t version; /* Only version 1 exists to date. */
+ uint32_t num_instruction_bps;
+ uint32_t num_data_bps;
+ uint32_t num_condition_regs;
+ uint32_t data_bp_alignment;
+ uint32_t sizeof_condition; /* size of the DVC register. */
+ uint64_t features;
};
/* Features will have bits indicating whether there is support for: */
struct ppc_hw_breakpoint
{
- uint32_t version; /* currently, version must be 1 */
- uint32_t trigger_type; /* only some combinations allowed */
- uint32_t addr_mode; /* address match mode */
- uint32_t condition_mode; /* break/watchpoint condition flags */
- uint64_t addr; /* break/watchpoint address */
- uint64_t addr2; /* range end or mask */
- uint64_t condition_value; /* contents of the DVC register */
+ uint32_t version; /* currently, version must be 1 */
+ uint32_t trigger_type; /* only some combinations allowed */
+ uint32_t addr_mode; /* address match mode */
+ uint32_t condition_mode; /* break/watchpoint condition flags */
+ uint64_t addr; /* break/watchpoint address */
+ uint64_t addr2; /* range end or mask */
+ uint64_t condition_value; /* contents of the DVC register */
};
/* Trigger type. */
#define PPC_BREAKPOINT_CONDITION_BE_ALL 0x00ff0000
#define PPC_BREAKPOINT_CONDITION_BE_SHIFT 16
#define PPC_BREAKPOINT_CONDITION_BE(n) \
- (1<<((n)+PPC_BREAKPOINT_CONDITION_BE_SHIFT))
+ (1<<((n)+PPC_BREAKPOINT_CONDITION_BE_SHIFT))
#endif /* PPC_PTRACE_GETHWDBGINFO */
/* Feature defined on Linux kernel v3.9: DAWR interface, that enables wider
#define PPC_DEBUG_FEATURE_DATA_BP_DAWR 0x10
#endif /* PPC_DEBUG_FEATURE_DATA_BP_DAWR */
+/* Feature defined on Linux kernel v5.1: Second watchpoint support. */
+#ifndef PPC_DEBUG_FEATURE_DATA_BP_ARCH_31
+#define PPC_DEBUG_FEATURE_DATA_BP_ARCH_31 0x20
+#endif /* PPC_DEBUG_FEATURE_DATA_BP_ARCH_31 */
+
+/* The version of the PowerPC HWDEBUG kernel interface that we will use, if
+ available. */
+#define PPC_DEBUG_CURRENT_VERSION 1
+
/* Similarly for the general-purpose (gp0 -- gp31)
and floating-point registers (fp0 -- fp31). */
#ifndef PTRACE_GETREGS
/* This is the layout of the POWER7 VSX registers and the way they overlap
with the existing FPR and VMX registers.
- VSR doubleword 0 VSR doubleword 1
- ----------------------------------------------------------------
+ VSR doubleword 0 VSR doubleword 1
+ ----------------------------------------------------------------
VSR[0] | FPR[0] | |
- ----------------------------------------------------------------
+ ----------------------------------------------------------------
VSR[1] | FPR[1] | |
- ----------------------------------------------------------------
- | ... | |
- | ... | |
- ----------------------------------------------------------------
+ ----------------------------------------------------------------
+ | ... | |
+ | ... | |
+ ----------------------------------------------------------------
VSR[30] | FPR[30] | |
- ----------------------------------------------------------------
+ ----------------------------------------------------------------
VSR[31] | FPR[31] | |
- ----------------------------------------------------------------
+ ----------------------------------------------------------------
VSR[32] | VR[0] |
- ----------------------------------------------------------------
+ ----------------------------------------------------------------
VSR[33] | VR[1] |
- ----------------------------------------------------------------
- | ... |
- | ... |
- ----------------------------------------------------------------
+ ----------------------------------------------------------------
+ | ... |
+ | ... |
+ ----------------------------------------------------------------
VSR[62] | VR[30] |
- ----------------------------------------------------------------
+ ----------------------------------------------------------------
VSR[63] | VR[31] |
- ----------------------------------------------------------------
+ ----------------------------------------------------------------
VSX has 64 128bit registers. The first 32 registers overlap with
the FP registers (doubleword 0) and hence extend them with additional
them and gotten an error. */
int have_ptrace_getsetfpregs = 1;
+/* Private arch info associated with each thread lwp_info object, used
+ for debug register handling. */
+
+struct arch_lwp_info
+{
+ /* When true, indicates that the debug registers installed in the
+ thread no longer correspond to the watchpoints and breakpoints
+ requested by GDB. */
+ bool debug_regs_stale;
+
+ /* We need a back-reference to the PTID of the thread so that we can
+ cleanup the debug register state of the thread in
+ low_delete_thread. */
+ ptid_t lwp_ptid;
+};
+
+/* Class used to detect which set of ptrace requests that
+ ppc_linux_nat_target will use to install and remove hardware
+ breakpoints and watchpoints.
+
+ The interface is only detected once, testing the ptrace calls. The
+ result can indicate that no interface is available.
+
+ The Linux kernel provides two different sets of ptrace requests to
+ handle hardware watchpoints and breakpoints for Power:
+
+ - PPC_PTRACE_GETHWDBGINFO, PPC_PTRACE_SETHWDEBUG, and
+ PPC_PTRACE_DELHWDEBUG.
+
+ Or
+
+ - PTRACE_SET_DEBUGREG and PTRACE_GET_DEBUGREG
+
+ The first set is the more flexible one and allows setting watchpoints
+ with a variable watched region length and, for BookE processors,
+ multiple types of debug registers (e.g. hardware breakpoints and
+ hardware-assisted conditions for watchpoints). The second one only
+ allows setting one debug register, a watchpoint, so we only use it if
+ the first one is not available. */
+
+class ppc_linux_dreg_interface
+{
+public:
+
+ ppc_linux_dreg_interface ()
+ : m_interface (), m_hwdebug_info ()
+ {
+ };
+
+ DISABLE_COPY_AND_ASSIGN (ppc_linux_dreg_interface);
+
+ /* One and only one of these three functions returns true, indicating
+ whether the corresponding interface is the one we detected. The
+ interface must already have been detected as a precontidion. */
+
+ bool hwdebug_p ()
+ {
+ gdb_assert (detected_p ());
+ return *m_interface == HWDEBUG;
+ }
+
+ bool debugreg_p ()
+ {
+ gdb_assert (detected_p ());
+ return *m_interface == DEBUGREG;
+ }
+
+ bool unavailable_p ()
+ {
+ gdb_assert (detected_p ());
+ return *m_interface == UNAVAILABLE;
+ }
+
+ /* Returns the debug register capabilities of the target. Should only
+ be called if the interface is HWDEBUG. */
+ const struct ppc_debug_info &hwdebug_info ()
+ {
+ gdb_assert (hwdebug_p ());
+
+ return m_hwdebug_info;
+ }
+
+ /* Returns true if the interface has already been detected. This is
+ useful for cases when we know there is no work to be done if the
+ interface hasn't been detected yet. */
+ bool detected_p ()
+ {
+ return m_interface.has_value ();
+ }
+
+ /* Detect the available interface, if any, if it hasn't been detected
+ before, using PTID for the necessary ptrace calls. */
+
+ void detect (const ptid_t &ptid)
+ {
+ if (m_interface.has_value ())
+ return;
+
+ gdb_assert (ptid.lwp_p ());
+
+ bool no_features = false;
+
+ if (ptrace (PPC_PTRACE_GETHWDBGINFO, ptid.lwp (), 0, &m_hwdebug_info)
+ >= 0)
+ {
+ /* If there are no advertised features, we don't use the
+ HWDEBUG interface and try the DEBUGREG interface instead.
+ It shouldn't be necessary to do this, however, when the
+ kernel is configured without CONFIG_HW_BREAKPOINTS (selected
+ by CONFIG_PERF_EVENTS), there is a bug that causes
+ watchpoints installed with the HWDEBUG interface not to
+ trigger. When this is the case, features will be zero,
+ which we use as an indicator to fall back to the DEBUGREG
+ interface. */
+ if (m_hwdebug_info.features != 0)
+ {
+ m_interface.emplace (HWDEBUG);
+ return;
+ }
+ else
+ no_features = true;
+ }
+
+ /* EIO indicates that the request is invalid, so we try DEBUGREG
+ next. Technically, it can also indicate other failures, but we
+ can't differentiate those.
+
+ Other errors could happen for various reasons. We could get an
+ ESRCH if the traced thread was killed by a signal. Trying to
+ detect the interface with another thread in the future would be
+ complicated, as callers would have to handle an "unknown
+ interface" case. It's also unclear if raising an exception
+ here would be safe.
+
+ Other errors, such as ENODEV, could be more permanent and cause
+ a failure for any thread.
+
+ For simplicity, with all errors other than EIO, we set the
+ interface to UNAVAILABLE and don't try DEBUGREG. If DEBUGREG
+ fails too, we'll also set the interface to UNAVAILABLE. It's
+ unlikely that trying the DEBUGREG interface with this same thread
+ would work, for errors other than EIO. This means that these
+ errors will cause hardware watchpoints and breakpoints to become
+ unavailable throughout a GDB session. */
+
+ if (no_features || errno == EIO)
+ {
+ unsigned long wp;
+
+ if (ptrace (PTRACE_GET_DEBUGREG, ptid.lwp (), 0, &wp) >= 0)
+ {
+ m_interface.emplace (DEBUGREG);
+ return;
+ }
+ }
+
+ if (errno != EIO)
+ warning (_("Error when detecting the debug register interface. "
+ "Debug registers will be unavailable."));
+
+ m_interface.emplace (UNAVAILABLE);
+ return;
+ }
+
+private:
+
+ /* HWDEBUG represents the set of calls PPC_PTRACE_GETHWDBGINFO,
+ PPC_PTRACE_SETHWDEBUG and PPC_PTRACE_DELHWDEBUG.
+
+ DEBUGREG represents the set of calls PTRACE_SET_DEBUGREG and
+ PTRACE_GET_DEBUGREG.
+
+ UNAVAILABLE can indicate that the kernel doesn't support any of the
+ two sets of requests or that there was an error when we tried to
+ detect wich interface is available. */
+
+ enum debug_reg_interface
+ {
+ UNAVAILABLE,
+ HWDEBUG,
+ DEBUGREG
+ };
+
+ /* The interface option. Initialized if has_value () returns true. */
+ gdb::optional<enum debug_reg_interface> m_interface;
+
+ /* The info returned by the kernel with PPC_PTRACE_GETHWDBGINFO. Only
+ valid if we determined that the interface is HWDEBUG. */
+ struct ppc_debug_info m_hwdebug_info;
+};
+
+/* Per-process information. This includes the hardware watchpoints and
+ breakpoints that GDB requested to this target. */
+
+struct ppc_linux_process_info
+{
+ /* The list of hardware watchpoints and breakpoints that GDB requested
+ for this process.
+
+ Only used when the interface is HWDEBUG. */
+ std::list<struct ppc_hw_breakpoint> requested_hw_bps;
+
+ /* The watchpoint value that GDB requested for this process.
+
+ Only used when the interface is DEBUGREG. */
+ gdb::optional<long> requested_wp_val;
+};
+
struct ppc_linux_nat_target final : public linux_nat_target
{
/* Add our register access methods. */
int remove_mask_watchpoint (CORE_ADDR, CORE_ADDR, enum target_hw_bp_type)
override;
- bool stopped_by_watchpoint () override;
-
- bool stopped_data_address (CORE_ADDR *) override;
-
bool watchpoint_addr_within_range (CORE_ADDR, CORE_ADDR, int) override;
bool can_accel_watchpoint_condition (CORE_ADDR, int, int, struct expression *)
override;
/* Override linux_nat_target low methods. */
+ bool low_stopped_by_watchpoint () override;
+
+ bool low_stopped_data_address (CORE_ADDR *) override;
+
void low_new_thread (struct lwp_info *lp) override;
+
+ void low_delete_thread (arch_lwp_info *) override;
+
+ void low_new_fork (struct lwp_info *, pid_t) override;
+
+ void low_new_clone (struct lwp_info *, pid_t) override;
+
+ void low_forget_process (pid_t pid) override;
+
+ void low_prepare_to_resume (struct lwp_info *) override;
+
+private:
+
+ void copy_thread_dreg_state (const ptid_t &parent_ptid,
+ const ptid_t &child_ptid);
+
+ void mark_thread_stale (struct lwp_info *lp);
+
+ void mark_debug_registers_changed (pid_t pid);
+
+ void register_hw_breakpoint (pid_t pid,
+ const struct ppc_hw_breakpoint &bp);
+
+ void clear_hw_breakpoint (pid_t pid,
+ const struct ppc_hw_breakpoint &a);
+
+ void register_wp (pid_t pid, long wp_value);
+
+ void clear_wp (pid_t pid);
+
+ bool can_use_watchpoint_cond_accel (void);
+
+ void calculate_dvc (CORE_ADDR addr, int len,
+ CORE_ADDR data_value,
+ uint32_t *condition_mode,
+ uint64_t *condition_value);
+
+ int check_condition (CORE_ADDR watch_addr,
+ struct expression *cond,
+ CORE_ADDR *data_value, int *len);
+
+ int num_memory_accesses (const std::vector<value_ref_ptr> &chain);
+
+ int get_trigger_type (enum target_hw_bp_type type);
+
+ void create_watchpoint_request (struct ppc_hw_breakpoint *p,
+ CORE_ADDR addr,
+ int len,
+ enum target_hw_bp_type type,
+ struct expression *cond,
+ int insert);
+
+ bool hwdebug_point_cmp (const struct ppc_hw_breakpoint &a,
+ const struct ppc_hw_breakpoint &b);
+
+ void init_arch_lwp_info (struct lwp_info *lp);
+
+ arch_lwp_info *get_arch_lwp_info (struct lwp_info *lp);
+
+ /* The ptrace interface we'll use to install hardware watchpoints and
+ breakpoints (debug registers). */
+ ppc_linux_dreg_interface m_dreg_interface;
+
+ /* A map from pids to structs containing info specific to each
+ process. */
+ std::unordered_map<pid_t, ppc_linux_process_info> m_process_info;
+
+ /* Callable object to hash ptids by their lwp number. */
+ struct ptid_hash
+ {
+ std::size_t operator() (const ptid_t &ptid) const
+ {
+ return std::hash<long>{} (ptid.lwp ());
+ }
+ };
+
+ /* A map from ptid_t objects to a list of pairs of slots and hardware
+ breakpoint objects. This keeps track of which hardware breakpoints
+ and watchpoints were last installed in each slot of each thread.
+
+ Only used when the interface is HWDEBUG. */
+ std::unordered_map <ptid_t,
+ std::list<std::pair<long, ppc_hw_breakpoint>>,
+ ptid_hash> m_installed_hw_bps;
};
static ppc_linux_nat_target the_ppc_linux_nat_target;
ppc_register_u_addr (struct gdbarch *gdbarch, int regno)
{
int u_addr = -1;
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ ppc_gdbarch_tdep *tdep = (ppc_gdbarch_tdep *) gdbarch_tdep (gdbarch);
/* NOTE: cagney/2003-11-25: This is the word size used by the ptrace
interface, and not the wordsize of the program's ABI. */
int wordsize = sizeof (long);
if (ret < 0)
{
if (errno == EIO)
- {
- have_ptrace_getvrregs = 0;
- return;
- }
+ {
+ have_ptrace_getvrregs = 0;
+ return;
+ }
perror_with_name (_("Unable to fetch AltiVec registers"));
}
if (have_ptrace_getsetevrregs)
{
if (ptrace (PTRACE_GETEVRREGS, tid, 0, evrregset) >= 0)
- return;
+ return;
else
- {
- /* EIO means that the PTRACE_GETEVRREGS request isn't supported;
- we just return zeros. */
- if (errno == EIO)
- have_ptrace_getsetevrregs = 0;
- else
- /* Anything else needs to be reported. */
- perror_with_name (_("Unable to fetch SPE registers"));
- }
+ {
+ /* EIO means that the PTRACE_GETEVRREGS request isn't supported;
+ we just return zeros. */
+ if (errno == EIO)
+ have_ptrace_getsetevrregs = 0;
+ else
+ /* Anything else needs to be reported. */
+ perror_with_name (_("Unable to fetch SPE registers"));
+ }
}
memset (evrregset, 0, sizeof (*evrregset));
fetch_spe_register (struct regcache *regcache, int tid, int regno)
{
struct gdbarch *gdbarch = regcache->arch ();
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ ppc_gdbarch_tdep *tdep = (ppc_gdbarch_tdep *) gdbarch_tdep (gdbarch);
struct gdb_evrregset_t evrregs;
gdb_assert (sizeof (evrregs.evr[0])
- == register_size (gdbarch, tdep->ppc_ev0_upper_regnum));
+ == register_size (gdbarch, tdep->ppc_ev0_upper_regnum));
gdb_assert (sizeof (evrregs.acc)
- == register_size (gdbarch, tdep->ppc_acc_regnum));
+ == register_size (gdbarch, tdep->ppc_acc_regnum));
gdb_assert (sizeof (evrregs.spefscr)
- == register_size (gdbarch, tdep->ppc_spefscr_regnum));
+ == register_size (gdbarch, tdep->ppc_spefscr_regnum));
get_spe_registers (tid, &evrregs);
int i;
for (i = 0; i < ppc_num_gprs; i++)
- regcache->raw_supply (tdep->ppc_ev0_upper_regnum + i, &evrregs.evr[i]);
+ regcache->raw_supply (tdep->ppc_ev0_upper_regnum + i, &evrregs.evr[i]);
}
else if (tdep->ppc_ev0_upper_regnum <= regno
- && regno < tdep->ppc_ev0_upper_regnum + ppc_num_gprs)
+ && regno < tdep->ppc_ev0_upper_regnum + ppc_num_gprs)
regcache->raw_supply (regno,
&evrregs.evr[regno - tdep->ppc_ev0_upper_regnum]);
fetch_register (struct regcache *regcache, int tid, int regno)
{
struct gdbarch *gdbarch = regcache->arch ();
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ ppc_gdbarch_tdep *tdep = (ppc_gdbarch_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;
if (altivec_register_p (gdbarch, regno))
{
/* If this is the first time through, or if it is not the first
- time through, and we have confirmed that there is kernel
- support for such a ptrace request, then go and fetch the
- register. */
+ time through, and we have confirmed that there is kernel
+ support for such a ptrace request, then go and fetch the
+ register. */
if (have_ptrace_getvrregs)
{
- fetch_altivec_registers (regcache, tid, regno);
- return;
+ fetch_altivec_registers (regcache, tid, regno);
+ return;
}
/* If we have discovered that there is no ptrace support for
- AltiVec registers, fall through and return zeroes, because
- regaddr will be -1 in this case. */
+ AltiVec registers, fall through and return zeroes, because
+ regaddr will be -1 in this case. */
}
else if (vsx_register_p (gdbarch, regno))
{
regaddr += sizeof (long);
if (errno != 0)
{
- char message[128];
+ char message[128];
xsnprintf (message, sizeof (message), "reading register %s (#%d)",
gdbarch_register_name (gdbarch, regno), regno);
perror_with_name (message);
if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_LITTLE)
{
/* Little-endian values are always found at the left end of the
- bytes transferred. */
+ bytes transferred. */
regcache->raw_supply (regno, buf);
}
else if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG)
{
/* Big-endian values are found at the right end of the bytes
- transferred. */
+ transferred. */
size_t padding = (bytes_transferred - register_size (gdbarch, regno));
regcache->raw_supply (regno, buf + padding);
}
else
internal_error (__FILE__, __LINE__,
- _("fetch_register: unexpected byte order: %d"),
- gdbarch_byte_order (gdbarch));
+ _("fetch_register: unexpected byte order: %d"),
+ gdbarch_byte_order (gdbarch));
}
/* This function actually issues the request to ptrace, telling
if (ptrace (PTRACE_GETREGS, tid, 0, (void *) &gregset) < 0)
{
if (errno == EIO)
- {
- have_ptrace_getsetregs = 0;
- return 0;
- }
+ {
+ have_ptrace_getsetregs = 0;
+ return 0;
+ }
perror_with_name (_("Couldn't get general-purpose registers."));
}
fetch_gp_regs (struct regcache *regcache, int tid)
{
struct gdbarch *gdbarch = regcache->arch ();
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ ppc_gdbarch_tdep *tdep = (ppc_gdbarch_tdep *) gdbarch_tdep (gdbarch);
int i;
if (have_ptrace_getsetregs)
if (ptrace (PTRACE_GETFPREGS, tid, 0, (void *) &fpregs) < 0)
{
if (errno == EIO)
- {
- have_ptrace_getsetfpregs = 0;
- return 0;
- }
+ {
+ have_ptrace_getsetfpregs = 0;
+ return 0;
+ }
perror_with_name (_("Couldn't get floating-point registers."));
}
fetch_fp_regs (struct regcache *regcache, int tid)
{
struct gdbarch *gdbarch = regcache->arch ();
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ ppc_gdbarch_tdep *tdep = (ppc_gdbarch_tdep *) gdbarch_tdep (gdbarch);
int i;
if (have_ptrace_getsetfpregs)
fetch_ppc_registers (struct regcache *regcache, int tid)
{
struct gdbarch *gdbarch = regcache->arch ();
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ ppc_gdbarch_tdep *tdep = (ppc_gdbarch_tdep *) gdbarch_tdep (gdbarch);
fetch_gp_regs (regcache, tid);
if (tdep->ppc_fp0_regnum >= 0)
if (ret < 0)
{
if (errno == EIO)
- {
- have_ptrace_getvrregs = 0;
- return;
- }
+ {
+ have_ptrace_getvrregs = 0;
+ return;
+ }
perror_with_name (_("Unable to fetch AltiVec registers"));
}
if (have_ptrace_getsetevrregs)
{
if (ptrace (PTRACE_SETEVRREGS, tid, 0, evrregset) >= 0)
- return;
+ return;
else
- {
- /* EIO means that the PTRACE_SETEVRREGS request isn't
- supported; we fail silently, and don't try the call
- again. */
- if (errno == EIO)
- have_ptrace_getsetevrregs = 0;
- else
- /* Anything else needs to be reported. */
- perror_with_name (_("Unable to set SPE registers"));
- }
+ {
+ /* EIO means that the PTRACE_SETEVRREGS request isn't
+ supported; we fail silently, and don't try the call
+ again. */
+ if (errno == EIO)
+ have_ptrace_getsetevrregs = 0;
+ else
+ /* Anything else needs to be reported. */
+ perror_with_name (_("Unable to set SPE registers"));
+ }
}
}
store_spe_register (const struct regcache *regcache, int tid, int regno)
{
struct gdbarch *gdbarch = regcache->arch ();
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ ppc_gdbarch_tdep *tdep = (ppc_gdbarch_tdep *) gdbarch_tdep (gdbarch);
struct gdb_evrregset_t evrregs;
gdb_assert (sizeof (evrregs.evr[0])
- == register_size (gdbarch, tdep->ppc_ev0_upper_regnum));
+ == register_size (gdbarch, tdep->ppc_ev0_upper_regnum));
gdb_assert (sizeof (evrregs.acc)
- == register_size (gdbarch, tdep->ppc_acc_regnum));
+ == register_size (gdbarch, tdep->ppc_acc_regnum));
gdb_assert (sizeof (evrregs.spefscr)
- == register_size (gdbarch, tdep->ppc_spefscr_regnum));
+ == register_size (gdbarch, tdep->ppc_spefscr_regnum));
if (regno == -1)
/* Since we're going to write out every register, the code below
&evrregs.evr[i]);
}
else if (tdep->ppc_ev0_upper_regnum <= regno
- && regno < tdep->ppc_ev0_upper_regnum + ppc_num_gprs)
+ && regno < tdep->ppc_ev0_upper_regnum + ppc_num_gprs)
regcache->raw_collect (regno,
&evrregs.evr[regno - tdep->ppc_ev0_upper_regnum]);
store_register (const struct regcache *regcache, int tid, int regno)
{
struct gdbarch *gdbarch = regcache->arch ();
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ ppc_gdbarch_tdep *tdep = (ppc_gdbarch_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 i;
regaddr += sizeof (long);
if (errno == EIO
- && (regno == tdep->ppc_fpscr_regnum
+ && (regno == tdep->ppc_fpscr_regnum
|| regno == PPC_ORIG_R3_REGNUM
|| regno == PPC_TRAP_REGNUM))
{
if (errno != 0)
{
- char message[128];
+ char message[128];
xsnprintf (message, sizeof (message), "writing register %s (#%d)",
gdbarch_register_name (gdbarch, regno), regno);
perror_with_name (message);
if (ptrace (PTRACE_GETREGS, tid, 0, (void *) &gregset) < 0)
{
if (errno == EIO)
- {
- have_ptrace_getsetregs = 0;
- return 0;
- }
+ {
+ have_ptrace_getsetregs = 0;
+ return 0;
+ }
perror_with_name (_("Couldn't get general-purpose registers."));
}
if (ptrace (PTRACE_SETREGS, tid, 0, (void *) &gregset) < 0)
{
if (errno == EIO)
- {
- have_ptrace_getsetregs = 0;
- return 0;
- }
+ {
+ have_ptrace_getsetregs = 0;
+ return 0;
+ }
perror_with_name (_("Couldn't set general-purpose registers."));
}
store_gp_regs (const struct regcache *regcache, int tid, int regno)
{
struct gdbarch *gdbarch = regcache->arch ();
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ ppc_gdbarch_tdep *tdep = (ppc_gdbarch_tdep *) gdbarch_tdep (gdbarch);
int i;
if (have_ptrace_getsetregs)
if (ptrace (PTRACE_GETFPREGS, tid, 0, (void *) &fpregs) < 0)
{
if (errno == EIO)
- {
- have_ptrace_getsetfpregs = 0;
- return 0;
- }
+ {
+ have_ptrace_getsetfpregs = 0;
+ return 0;
+ }
perror_with_name (_("Couldn't get floating-point registers."));
}
if (ptrace (PTRACE_SETFPREGS, tid, 0, (void *) &fpregs) < 0)
{
if (errno == EIO)
- {
- have_ptrace_getsetfpregs = 0;
- return 0;
- }
+ {
+ have_ptrace_getsetfpregs = 0;
+ return 0;
+ }
perror_with_name (_("Couldn't set floating-point registers."));
}
store_fp_regs (const struct regcache *regcache, int tid, int regno)
{
struct gdbarch *gdbarch = regcache->arch ();
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ ppc_gdbarch_tdep *tdep = (ppc_gdbarch_tdep *) gdbarch_tdep (gdbarch);
int i;
if (have_ptrace_getsetfpregs)
store_ppc_registers (const struct regcache *regcache, int tid)
{
struct gdbarch *gdbarch = regcache->arch ();
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ ppc_gdbarch_tdep *tdep = (ppc_gdbarch_tdep *) gdbarch_tdep (gdbarch);
store_gp_regs (regcache, tid, -1);
if (tdep->ppc_fp0_regnum >= 0)
function to fail most of the time, so we ignore them. */
}
-/* The cached DABR value, to install in new threads.
- This variable is used when the PowerPC HWDEBUG ptrace
- interface is not available. */
-static long saved_dabr_value;
+void
+ppc_linux_nat_target::store_registers (struct regcache *regcache, int regno)
+{
+ pid_t tid = get_ptrace_pid (regcache->ptid ());
-/* Global structure that will store information about the available
- features provided by the PowerPC HWDEBUG ptrace interface. */
-static struct ppc_debug_info hwdebug_info;
+ if (regno >= 0)
+ store_register (regcache, tid, regno);
+ else
+ store_ppc_registers (regcache, tid);
+}
-/* Global variable that holds the maximum number of slots that the
- kernel will use. This is only used when PowerPC HWDEBUG ptrace interface
- is available. */
-static size_t max_slots_number = 0;
+/* Functions for transferring registers between a gregset_t or fpregset_t
+ (see sys/ucontext.h) and gdb's regcache. The word size is that used
+ by the ptrace interface, not the current program's ABI. Eg. if a
+ powerpc64-linux gdb is being used to debug a powerpc32-linux app, we
+ read or write 64-bit gregsets. This is to suit the host libthread_db. */
-struct hw_break_tuple
+void
+supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregsetp)
{
- long slot;
- struct ppc_hw_breakpoint *hw_break;
-};
+ const struct regset *regset = ppc_linux_gregset (sizeof (long));
-/* This is an internal vector created to store information about *points
- inserted for each thread. This is used when PowerPC HWDEBUG ptrace
- interface is available. */
-struct thread_points
- {
- /* The TID to which this *point relates. */
- int tid;
- /* Information about the *point, such as its address, type, etc.
-
- Each element inside this vector corresponds to a hardware
- breakpoint or watchpoint in the thread represented by TID. The maximum
- size of these vector is MAX_SLOTS_NUMBER. If the hw_break element of
- the tuple is NULL, then the position in the vector is free. */
- struct hw_break_tuple *hw_breaks;
- };
+ ppc_supply_gregset (regset, regcache, -1, gregsetp, sizeof (*gregsetp));
+}
-static std::vector<thread_points *> ppc_threads;
+void
+fill_gregset (const struct regcache *regcache,
+ gdb_gregset_t *gregsetp, int regno)
+{
+ const struct regset *regset = ppc_linux_gregset (sizeof (long));
-/* The version of the PowerPC HWDEBUG kernel interface that we will use, if
- available. */
-#define PPC_DEBUG_CURRENT_VERSION 1
+ if (regno == -1)
+ memset (gregsetp, 0, sizeof (*gregsetp));
+ ppc_collect_gregset (regset, regcache, regno, gregsetp, sizeof (*gregsetp));
+}
-/* Returns non-zero if we support the PowerPC HWDEBUG ptrace interface. */
-static int
-have_ptrace_hwdebug_interface (void)
+void
+supply_fpregset (struct regcache *regcache, const gdb_fpregset_t * fpregsetp)
{
- static int have_ptrace_hwdebug_interface = -1;
-
- if (have_ptrace_hwdebug_interface == -1)
- {
- int tid;
+ const struct regset *regset = ppc_linux_fpregset ();
- tid = inferior_ptid.lwp ();
- if (tid == 0)
- tid = inferior_ptid.pid ();
+ ppc_supply_fpregset (regset, regcache, -1,
+ fpregsetp, sizeof (*fpregsetp));
+}
- /* Check for kernel support for PowerPC HWDEBUG ptrace interface. */
- if (ptrace (PPC_PTRACE_GETHWDBGINFO, tid, 0, &hwdebug_info) >= 0)
- {
- /* Check whether PowerPC HWDEBUG ptrace interface is functional and
- provides any supported feature. */
- if (hwdebug_info.features != 0)
- {
- have_ptrace_hwdebug_interface = 1;
- max_slots_number = hwdebug_info.num_instruction_bps
- + hwdebug_info.num_data_bps
- + hwdebug_info.num_condition_regs;
- return have_ptrace_hwdebug_interface;
- }
- }
- /* Old school interface and no PowerPC HWDEBUG ptrace support. */
- have_ptrace_hwdebug_interface = 0;
- memset (&hwdebug_info, 0, sizeof (struct ppc_debug_info));
- }
+void
+fill_fpregset (const struct regcache *regcache,
+ gdb_fpregset_t *fpregsetp, int regno)
+{
+ const struct regset *regset = ppc_linux_fpregset ();
- return have_ptrace_hwdebug_interface;
+ ppc_collect_fpregset (regset, regcache, regno,
+ fpregsetp, sizeof (*fpregsetp));
}
int
-ppc_linux_nat_target::can_use_hw_breakpoint (enum bptype type, int cnt, int ot)
+ppc_linux_nat_target::auxv_parse (gdb_byte **readptr,
+ gdb_byte *endptr, CORE_ADDR *typep,
+ CORE_ADDR *valp)
{
- int total_hw_wp, total_hw_bp;
+ int tid = inferior_ptid.lwp ();
+ if (tid == 0)
+ tid = inferior_ptid.pid ();
- if (have_ptrace_hwdebug_interface ())
- {
- /* When PowerPC HWDEBUG ptrace interface is available, the number of
- available hardware watchpoints and breakpoints is stored at the
- hwdebug_info struct. */
- total_hw_bp = hwdebug_info.num_instruction_bps;
- total_hw_wp = hwdebug_info.num_data_bps;
- }
- else
- {
- /* When we do not have PowerPC HWDEBUG ptrace interface, we should
- consider having 1 hardware watchpoint and no hardware breakpoints. */
- total_hw_bp = 0;
- total_hw_wp = 1;
- }
+ int sizeof_auxv_field = ppc_linux_target_wordsize (tid);
- if (type == bp_hardware_watchpoint || type == bp_read_watchpoint
- || type == bp_access_watchpoint || type == bp_watchpoint)
- {
- if (cnt + ot > total_hw_wp)
- return -1;
- }
- else if (type == bp_hardware_breakpoint)
- {
- if (total_hw_bp == 0)
- {
- /* No hardware breakpoint support. */
- return 0;
- }
- if (cnt > total_hw_bp)
- return -1;
- }
+ enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ());
+ gdb_byte *ptr = *readptr;
- if (!have_ptrace_hwdebug_interface ())
- {
- int tid;
- ptid_t ptid = inferior_ptid;
+ if (endptr == ptr)
+ 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 = ptid.lwp ();
- if (tid == 0)
- tid = ptid.pid ();
+ if (endptr - ptr < sizeof_auxv_field * 2)
+ return -1;
- if (ptrace (PTRACE_SET_DEBUGREG, tid, 0, 0) == -1)
- return 0;
- }
+ *typep = extract_unsigned_integer (ptr, sizeof_auxv_field, byte_order);
+ ptr += sizeof_auxv_field;
+ *valp = extract_unsigned_integer (ptr, sizeof_auxv_field, byte_order);
+ ptr += sizeof_auxv_field;
+ *readptr = ptr;
return 1;
}
-int
-ppc_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
+const struct target_desc *
+ppc_linux_nat_target::read_description ()
{
- /* Handle sub-8-byte quantities. */
- if (len <= 0)
- return 0;
+ int tid = inferior_ptid.pid ();
- /* The PowerPC HWDEBUG ptrace interface tells if there are alignment
- restrictions for watchpoints in the processors. In that case, we use that
- information to determine the hardcoded watchable region for
- watchpoints. */
- if (have_ptrace_hwdebug_interface ())
+ if (have_ptrace_getsetevrregs)
{
- int region_size;
- /* Embedded DAC-based processors, like the PowerPC 440 have ranged
- watchpoints and can watch any access within an arbitrary memory
- region. This is useful to watch arrays and structs, for instance. It
- takes two hardware watchpoints though. */
- if (len > 1
- && hwdebug_info.features & PPC_DEBUG_FEATURE_DATA_BP_RANGE
- && linux_get_hwcap (current_top_target ()) & PPC_FEATURE_BOOKE)
- return 2;
- /* Check if the processor provides DAWR interface. */
- if (hwdebug_info.features & PPC_DEBUG_FEATURE_DATA_BP_DAWR)
- /* DAWR interface allows to watch up to 512 byte wide ranges which
- can't cross a 512 byte boundary. */
- region_size = 512;
- else
- region_size = hwdebug_info.data_bp_alignment;
- /* Server processors provide one hardware watchpoint and addr+len should
- fall in the watchable region provided by the ptrace interface. */
- if (region_size
- && (addr + len > (addr & ~(region_size - 1)) + region_size))
- return 0;
- }
- /* addr+len must fall in the 8 byte watchable region for DABR-based
- processors (i.e., server processors). Without the new PowerPC HWDEBUG
- ptrace interface, DAC-based processors (i.e., embedded processors) will
- use addresses aligned to 4-bytes due to the way the read/write flags are
- passed in the old ptrace interface. */
- else if (((linux_get_hwcap (current_top_target ()) & PPC_FEATURE_BOOKE)
- && (addr + len) > (addr & ~3) + 4)
- || (addr + len) > (addr & ~7) + 8)
- return 0;
+ struct gdb_evrregset_t evrregset;
- return 1;
-}
+ if (ptrace (PTRACE_GETEVRREGS, tid, 0, &evrregset) >= 0)
+ return tdesc_powerpc_e500l;
-/* This function compares two ppc_hw_breakpoint structs field-by-field. */
-static int
-hwdebug_point_cmp (struct ppc_hw_breakpoint *a, struct ppc_hw_breakpoint *b)
-{
- return (a->trigger_type == b->trigger_type
- && a->addr_mode == b->addr_mode
- && a->condition_mode == b->condition_mode
- && a->addr == b->addr
- && a->addr2 == b->addr2
- && a->condition_value == b->condition_value);
-}
+ /* EIO means that the PTRACE_GETEVRREGS request isn't supported.
+ Anything else needs to be reported. */
+ else if (errno != EIO)
+ perror_with_name (_("Unable to fetch SPE registers"));
+ }
-/* This function can be used to retrieve a thread_points by the TID of the
- related process/thread. If nothing has been found, and ALLOC_NEW is 0,
- it returns NULL. If ALLOC_NEW is non-zero, a new thread_points for the
- provided TID will be created and returned. */
-static struct thread_points *
-hwdebug_find_thread_points_by_tid (int tid, int alloc_new)
-{
- for (thread_points *t : ppc_threads)
+ struct ppc_linux_features features = ppc_linux_no_features;
+
+ features.wordsize = ppc_linux_target_wordsize (tid);
+
+ CORE_ADDR hwcap = linux_get_hwcap (current_inferior ()->top_target ());
+ CORE_ADDR hwcap2 = linux_get_hwcap2 (current_inferior ()->top_target ());
+
+ if (have_ptrace_getsetvsxregs
+ && (hwcap & PPC_FEATURE_HAS_VSX))
+ {
+ gdb_vsxregset_t vsxregset;
+
+ if (ptrace (PTRACE_GETVSXREGS, tid, 0, &vsxregset) >= 0)
+ features.vsx = true;
+
+ /* EIO means that the PTRACE_GETVSXREGS request isn't supported.
+ Anything else needs to be reported. */
+ else if (errno != EIO)
+ perror_with_name (_("Unable to fetch VSX registers"));
+ }
+
+ if (have_ptrace_getvrregs
+ && (hwcap & PPC_FEATURE_HAS_ALTIVEC))
{
- if (t->tid == tid)
- return t;
+ gdb_vrregset_t vrregset;
+
+ if (ptrace (PTRACE_GETVRREGS, tid, 0, &vrregset) >= 0)
+ features.altivec = true;
+
+ /* EIO means that the PTRACE_GETVRREGS request isn't supported.
+ Anything else needs to be reported. */
+ else if (errno != EIO)
+ perror_with_name (_("Unable to fetch AltiVec registers"));
}
- struct thread_points *t = NULL;
+ features.isa205 = ppc_linux_has_isa205 (hwcap);
- /* Do we need to allocate a new point_item
- if the wanted one does not exist? */
- if (alloc_new)
+ 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))
{
- t = XNEW (struct thread_points);
- t->hw_breaks = XCNEWVEC (struct hw_break_tuple, max_slots_number);
- t->tid = tid;
- ppc_threads.push_back (t);
+ features.ppr_dscr = true;
+ if ((hwcap2 & PPC_FEATURE2_ARCH_2_07)
+ && (hwcap2 & PPC_FEATURE2_TAR)
+ && (hwcap2 & PPC_FEATURE2_EBB)
+ && check_regset (tid, NT_PPC_TAR, PPC_LINUX_SIZEOF_TARREGSET)
+ && check_regset (tid, NT_PPC_EBB, PPC_LINUX_SIZEOF_EBBREGSET)
+ && check_regset (tid, NT_PPC_PMU, PPC_LINUX_SIZEOF_PMUREGSET))
+ {
+ features.isa207 = true;
+ if ((hwcap2 & PPC_FEATURE2_HTM)
+ && check_regset (tid, NT_PPC_TM_SPR,
+ PPC_LINUX_SIZEOF_TM_SPRREGSET))
+ features.htm = true;
+ }
}
- return t;
+ return ppc_linux_match_description (features);
}
-/* This function is a generic wrapper that is responsible for inserting a
- *point (i.e., calling `ptrace' in order to issue the request to the
- kernel) and registering it internally in GDB. */
-static void
-hwdebug_insert_point (struct ppc_hw_breakpoint *b, int tid)
+/* Routines for installing hardware watchpoints and breakpoints. When
+ GDB requests a hardware watchpoint or breakpoint to be installed, we
+ register the request for the pid of inferior_ptid in a map with one
+ entry per process. We then issue a stop request to all the threads of
+ this process, and mark a per-thread flag indicating that their debug
+ registers should be updated. Right before they are next resumed, we
+ remove all previously installed debug registers and install all the
+ ones GDB requested. We then update a map with one entry per thread
+ that keeps track of what debug registers were last installed in each
+ thread.
+
+ We use this second map to remove installed registers before installing
+ the ones requested by GDB, and to copy the debug register state after
+ a thread clones or forks, since depending on the kernel configuration,
+ debug registers can be inherited. */
+
+/* Check if we support and have enough resources to install a hardware
+ watchpoint or breakpoint. See the description in target.h. */
+
+int
+ppc_linux_nat_target::can_use_hw_breakpoint (enum bptype type, int cnt,
+ int ot)
{
- int i;
- long slot;
- gdb::unique_xmalloc_ptr<ppc_hw_breakpoint> p (XDUP (ppc_hw_breakpoint, b));
- struct hw_break_tuple *hw_breaks;
- struct thread_points *t;
+ int total_hw_wp, total_hw_bp;
- errno = 0;
- slot = ptrace (PPC_PTRACE_SETHWDEBUG, tid, 0, p.get ());
- if (slot < 0)
- perror_with_name (_("Unexpected error setting breakpoint or watchpoint"));
+ m_dreg_interface.detect (inferior_ptid);
- /* Everything went fine, so we have to register this *point. */
- t = hwdebug_find_thread_points_by_tid (tid, 1);
- gdb_assert (t != NULL);
- hw_breaks = t->hw_breaks;
+ if (m_dreg_interface.unavailable_p ())
+ return 0;
- /* Find a free element in the hw_breaks vector. */
- for (i = 0; i < max_slots_number; i++)
+ if (m_dreg_interface.hwdebug_p ())
{
- if (hw_breaks[i].hw_break == NULL)
- {
- hw_breaks[i].slot = slot;
- hw_breaks[i].hw_break = p.release ();
- break;
- }
+ /* When PowerPC HWDEBUG ptrace interface is available, the number of
+ available hardware watchpoints and breakpoints is stored at the
+ hwdebug_info struct. */
+ total_hw_bp = m_dreg_interface.hwdebug_info ().num_instruction_bps;
+ total_hw_wp = m_dreg_interface.hwdebug_info ().num_data_bps;
+ }
+ else
+ {
+ gdb_assert (m_dreg_interface.debugreg_p ());
+
+ /* With the DEBUGREG ptrace interface, we should consider having 1
+ hardware watchpoint and no hardware breakpoints. */
+ total_hw_bp = 0;
+ total_hw_wp = 1;
+ }
+
+ if (type == bp_hardware_watchpoint || type == bp_read_watchpoint
+ || type == bp_access_watchpoint || type == bp_watchpoint)
+ {
+ if (total_hw_wp == 0)
+ return 0;
+ else if (cnt + ot > total_hw_wp)
+ return -1;
+ else
+ return 1;
+ }
+ else if (type == bp_hardware_breakpoint)
+ {
+ if (total_hw_bp == 0)
+ return 0;
+ else if (cnt > total_hw_bp)
+ return -1;
+ else
+ return 1;
}
- gdb_assert (i != max_slots_number);
+ return 0;
}
-/* This function is a generic wrapper that is responsible for removing a
- *point (i.e., calling `ptrace' in order to issue the request to the
- kernel), and unregistering it internally at GDB. */
-static void
-hwdebug_remove_point (struct ppc_hw_breakpoint *b, int tid)
+/* Returns 1 if we can watch LEN bytes at address ADDR, 0 otherwise. */
+
+int
+ppc_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
{
- int i;
- struct hw_break_tuple *hw_breaks;
- struct thread_points *t;
+ /* Handle sub-8-byte quantities. */
+ if (len <= 0)
+ return 0;
- t = hwdebug_find_thread_points_by_tid (tid, 0);
- gdb_assert (t != NULL);
- hw_breaks = t->hw_breaks;
+ m_dreg_interface.detect (inferior_ptid);
+
+ if (m_dreg_interface.unavailable_p ())
+ return 0;
+
+ /* The PowerPC HWDEBUG ptrace interface tells if there are alignment
+ restrictions for watchpoints in the processors. In that case, we use that
+ information to determine the hardcoded watchable region for
+ watchpoints. */
+ if (m_dreg_interface.hwdebug_p ())
+ {
+ const struct ppc_debug_info &hwdebug_info = (m_dreg_interface
+ .hwdebug_info ());
+ int region_size = hwdebug_info.data_bp_alignment;
+ int region_align = region_size;
+
+ /* Embedded DAC-based processors, like the PowerPC 440 have ranged
+ watchpoints and can watch any access within an arbitrary memory
+ region. This is useful to watch arrays and structs, for instance. It
+ takes two hardware watchpoints though. */
+ if (len > 1
+ && hwdebug_info.features & PPC_DEBUG_FEATURE_DATA_BP_RANGE
+ && (linux_get_hwcap (current_inferior ()->top_target ())
+ & PPC_FEATURE_BOOKE))
+ return 2;
+ /* Check if the processor provides DAWR interface. */
+ if (hwdebug_info.features & PPC_DEBUG_FEATURE_DATA_BP_DAWR)
+ {
+ /* DAWR interface allows to watch up to 512 byte wide ranges. */
+ region_size = 512;
+ /* DAWR interface allows to watch up to 512 byte wide ranges which
+ can't cross a 512 byte bondary on machines that doesn't have a
+ second DAWR (P9 or less). */
+ if (!(hwdebug_info.features & PPC_DEBUG_FEATURE_DATA_BP_ARCH_31))
+ region_align = 512;
+ }
+ /* Server processors provide one hardware watchpoint and addr+len should
+ fall in the watchable region provided by the ptrace interface. */
+ if (region_align
+ && (addr + len > (addr & ~(region_align - 1)) + region_size))
+ return 0;
+ }
+ /* addr+len must fall in the 8 byte watchable region for DABR-based
+ processors (i.e., server processors). Without the new PowerPC HWDEBUG
+ ptrace interface, DAC-based processors (i.e., embedded processors) will
+ use addresses aligned to 4-bytes due to the way the read/write flags are
+ passed in the old ptrace interface. */
+ else
+ {
+ gdb_assert (m_dreg_interface.debugreg_p ());
- for (i = 0; i < max_slots_number; i++)
- if (hw_breaks[i].hw_break && hwdebug_point_cmp (hw_breaks[i].hw_break, b))
- break;
+ if (((linux_get_hwcap (current_inferior ()->top_target ())
+ & PPC_FEATURE_BOOKE)
+ && (addr + len) > (addr & ~3) + 4)
+ || (addr + len) > (addr & ~7) + 8)
+ return 0;
+ }
- gdb_assert (i != max_slots_number);
+ return 1;
+}
- /* We have to ignore ENOENT errors because the kernel implements hardware
- breakpoints/watchpoints as "one-shot", that is, they are automatically
- deleted when hit. */
- errno = 0;
- if (ptrace (PPC_PTRACE_DELHWDEBUG, tid, 0, hw_breaks[i].slot) < 0)
- if (errno != ENOENT)
- perror_with_name (_("Unexpected error deleting "
- "breakpoint or watchpoint"));
+/* This function compares two ppc_hw_breakpoint structs
+ field-by-field. */
- xfree (hw_breaks[i].hw_break);
- hw_breaks[i].hw_break = NULL;
+bool
+ppc_linux_nat_target::hwdebug_point_cmp (const struct ppc_hw_breakpoint &a,
+ const struct ppc_hw_breakpoint &b)
+{
+ return (a.trigger_type == b.trigger_type
+ && a.addr_mode == b.addr_mode
+ && a.condition_mode == b.condition_mode
+ && a.addr == b.addr
+ && a.addr2 == b.addr2
+ && a.condition_value == b.condition_value);
}
/* Return the number of registers needed for a ranged breakpoint. */
int
ppc_linux_nat_target::ranged_break_num_registers ()
{
- return ((have_ptrace_hwdebug_interface ()
- && hwdebug_info.features & PPC_DEBUG_FEATURE_INSN_BP_RANGE)?
+ m_dreg_interface.detect (inferior_ptid);
+
+ return ((m_dreg_interface.hwdebug_p ()
+ && (m_dreg_interface.hwdebug_info ().features
+ & PPC_DEBUG_FEATURE_INSN_BP_RANGE))?
2 : -1);
}
-/* Insert the hardware breakpoint described by BP_TGT. Returns 0 for
- success, 1 if hardware breakpoints are not supported or -1 for failure. */
+/* Register the hardware breakpoint described by BP_TGT, to be inserted
+ when the threads of inferior_ptid are resumed. Returns 0 for success,
+ or -1 if the HWDEBUG interface that we need for hardware breakpoints
+ is not available. */
int
ppc_linux_nat_target::insert_hw_breakpoint (struct gdbarch *gdbarch,
struct bp_target_info *bp_tgt)
{
- struct lwp_info *lp;
struct ppc_hw_breakpoint p;
- if (!have_ptrace_hwdebug_interface ())
+ m_dreg_interface.detect (inferior_ptid);
+
+ if (!m_dreg_interface.hwdebug_p ())
return -1;
p.version = PPC_DEBUG_CURRENT_VERSION;
p.addr2 = 0;
}
- ALL_LWPS (lp)
- hwdebug_insert_point (&p, lp->ptid.lwp ());
+ register_hw_breakpoint (inferior_ptid.pid (), p);
return 0;
}
+/* Clear a registration for the hardware breakpoint given by type BP_TGT.
+ It will be removed from the threads of inferior_ptid when they are
+ next resumed. Returns 0 for success, or -1 if the HWDEBUG interface
+ that we need for hardware breakpoints is not available. */
+
int
ppc_linux_nat_target::remove_hw_breakpoint (struct gdbarch *gdbarch,
struct bp_target_info *bp_tgt)
{
- struct lwp_info *lp;
struct ppc_hw_breakpoint p;
- if (!have_ptrace_hwdebug_interface ())
+ m_dreg_interface.detect (inferior_ptid);
+
+ if (!m_dreg_interface.hwdebug_p ())
return -1;
p.version = PPC_DEBUG_CURRENT_VERSION;
p.addr2 = 0;
}
- ALL_LWPS (lp)
- hwdebug_remove_point (&p, lp->ptid.lwp ());
+ clear_hw_breakpoint (inferior_ptid.pid (), p);
return 0;
}
-static int
-get_trigger_type (enum target_hw_bp_type type)
+/* Return the trigger value to set in a ppc_hw_breakpoint object for a
+ given hardware watchpoint TYPE. We assume type is not hw_execute. */
+
+int
+ppc_linux_nat_target::get_trigger_type (enum target_hw_bp_type type)
{
int t;
return t;
}
-/* Insert a new masked watchpoint at ADDR using the mask MASK.
- RW may be hw_read for a read watchpoint, hw_write for a write watchpoint
- or hw_access for an access watchpoint. Returns 0 on success and throws
- an error on failure. */
+/* Register a new masked watchpoint at ADDR using the mask MASK, to be
+ inserted when the threads of inferior_ptid are resumed. RW may be
+ hw_read for a read watchpoint, hw_write for a write watchpoint or
+ hw_access for an access watchpoint. */
int
ppc_linux_nat_target::insert_mask_watchpoint (CORE_ADDR addr, CORE_ADDR mask,
target_hw_bp_type rw)
{
- struct lwp_info *lp;
struct ppc_hw_breakpoint p;
- gdb_assert (have_ptrace_hwdebug_interface ());
+ gdb_assert (m_dreg_interface.hwdebug_p ());
p.version = PPC_DEBUG_CURRENT_VERSION;
p.trigger_type = get_trigger_type (rw);
p.addr2 = mask;
p.condition_value = 0;
- ALL_LWPS (lp)
- hwdebug_insert_point (&p, lp->ptid.lwp ());
+ register_hw_breakpoint (inferior_ptid.pid (), p);
return 0;
}
-/* Remove a masked watchpoint at ADDR with the mask MASK.
- RW may be hw_read for a read watchpoint, hw_write for a write watchpoint
- or hw_access for an access watchpoint. Returns 0 on success and throws
- an error on failure. */
+/* Clear a registration for a masked watchpoint at ADDR with the mask
+ MASK. It will be removed from the threads of inferior_ptid when they
+ are next resumed. RW may be hw_read for a read watchpoint, hw_write
+ for a write watchpoint or hw_access for an access watchpoint. */
int
ppc_linux_nat_target::remove_mask_watchpoint (CORE_ADDR addr, CORE_ADDR mask,
target_hw_bp_type rw)
{
- struct lwp_info *lp;
struct ppc_hw_breakpoint p;
- gdb_assert (have_ptrace_hwdebug_interface ());
+ gdb_assert (m_dreg_interface.hwdebug_p ());
p.version = PPC_DEBUG_CURRENT_VERSION;
p.trigger_type = get_trigger_type (rw);
p.addr2 = mask;
p.condition_value = 0;
- ALL_LWPS (lp)
- hwdebug_remove_point (&p, lp->ptid.lwp ());
+ clear_hw_breakpoint (inferior_ptid.pid (), p);
return 0;
}
-/* Check whether we have at least one free DVC register. */
-static int
-can_use_watchpoint_cond_accel (void)
+/* Check whether we have at least one free DVC register for the threads
+ of the pid of inferior_ptid. */
+
+bool
+ppc_linux_nat_target::can_use_watchpoint_cond_accel (void)
{
- struct thread_points *p;
- int tid = inferior_ptid.lwp ();
- int cnt = hwdebug_info.num_condition_regs, i;
+ m_dreg_interface.detect (inferior_ptid);
- if (!have_ptrace_hwdebug_interface () || cnt == 0)
- return 0;
+ if (!m_dreg_interface.hwdebug_p ())
+ return false;
- p = hwdebug_find_thread_points_by_tid (tid, 0);
+ int cnt = m_dreg_interface.hwdebug_info ().num_condition_regs;
- if (p)
- {
- for (i = 0; i < max_slots_number; i++)
- if (p->hw_breaks[i].hw_break != NULL
- && (p->hw_breaks[i].hw_break->condition_mode
- != PPC_BREAKPOINT_CONDITION_NONE))
- cnt--;
+ if (cnt == 0)
+ return false;
- /* There are no available slots now. */
- if (cnt <= 0)
- return 0;
- }
+ auto process_it = m_process_info.find (inferior_ptid.pid ());
- return 1;
+ /* No breakpoints or watchpoints have been requested for this process,
+ we have at least one free DVC register. */
+ if (process_it == m_process_info.end ())
+ return true;
+
+ for (const ppc_hw_breakpoint &bp : process_it->second.requested_hw_bps)
+ if (bp.condition_mode != PPC_BREAKPOINT_CONDITION_NONE)
+ cnt--;
+
+ if (cnt <= 0)
+ return false;
+
+ return true;
}
/* Calculate the enable bits and the contents of the Data Value Compare
On exit, CONDITION_MODE will hold the enable bits for the DVC, and
CONDITION_VALUE will hold the value which should be put in the
DVC register. */
-static void
-calculate_dvc (CORE_ADDR addr, int len, CORE_ADDR data_value,
- uint32_t *condition_mode, uint64_t *condition_value)
+
+void
+ppc_linux_nat_target::calculate_dvc (CORE_ADDR addr, int len,
+ CORE_ADDR data_value,
+ uint32_t *condition_mode,
+ uint64_t *condition_value)
{
+ const struct ppc_debug_info &hwdebug_info = (m_dreg_interface.
+ hwdebug_info ());
+
int i, num_byte_enable, align_offset, num_bytes_off_dvc,
rightmost_enabled_byte;
CORE_ADDR addr_end_data, addr_end_dvc;
Returns -1 if there's any register access involved, or if there are
other kinds of values which are not acceptable in a condition
expression (e.g., lval_computed or lval_internalvar). */
-static int
-num_memory_accesses (const std::vector<value_ref_ptr> &chain)
+
+int
+ppc_linux_nat_target::num_memory_accesses (const std::vector<value_ref_ptr>
+ &chain)
{
int found_memory_cnt = 0;
If the function returns 1, DATA_VALUE will contain the constant against
which the watch value should be compared and LEN will contain the size
of the constant. */
-static int
-check_condition (CORE_ADDR watch_addr, struct expression *cond,
- CORE_ADDR *data_value, int *len)
+
+int
+ppc_linux_nat_target::check_condition (CORE_ADDR watch_addr,
+ struct expression *cond,
+ CORE_ADDR *data_value, int *len)
{
- int pc = 1, num_accesses_left, num_accesses_right;
+ int num_accesses_left, num_accesses_right;
struct value *left_val, *right_val;
std::vector<value_ref_ptr> left_chain, right_chain;
- if (cond->elts[0].opcode != BINOP_EQUAL)
+ expr::equal_operation *eqop
+ = dynamic_cast<expr::equal_operation *> (cond->op.get ());
+ if (eqop == nullptr)
return 0;
+ expr::operation *lhs = eqop->get_lhs ();
+ expr::operation *rhs = eqop->get_rhs ();
- fetch_subexp_value (cond, &pc, &left_val, NULL, &left_chain, 0);
+ fetch_subexp_value (cond, lhs, &left_val, NULL, &left_chain, false);
num_accesses_left = num_memory_accesses (left_chain);
if (left_val == NULL || num_accesses_left < 0)
return 0;
- fetch_subexp_value (cond, &pc, &right_val, NULL, &right_chain, 0);
+ fetch_subexp_value (cond, rhs, &right_val, NULL, &right_chain, false);
num_accesses_right = num_memory_accesses (right_chain);
if (right_val == NULL || num_accesses_right < 0)
return 1;
}
-/* Return non-zero if the target is capable of using hardware to evaluate
- the condition expression, thus only triggering the watchpoint when it is
+/* Return true if the target is capable of using hardware to evaluate the
+ condition expression, thus only triggering the watchpoint when it is
true. */
+
bool
-ppc_linux_nat_target::can_accel_watchpoint_condition (CORE_ADDR addr, int len,
- int rw,
+ppc_linux_nat_target::can_accel_watchpoint_condition (CORE_ADDR addr,
+ int len, int rw,
struct expression *cond)
{
CORE_ADDR data_value;
- return (have_ptrace_hwdebug_interface ()
- && hwdebug_info.num_condition_regs > 0
+ m_dreg_interface.detect (inferior_ptid);
+
+ return (m_dreg_interface.hwdebug_p ()
+ && (m_dreg_interface.hwdebug_info ().num_condition_regs > 0)
&& check_condition (addr, cond, &data_value, &len));
}
evaluated by hardware. INSERT tells if we are creating a request for
inserting or removing the watchpoint. */
-static void
-create_watchpoint_request (struct ppc_hw_breakpoint *p, CORE_ADDR addr,
- int len, enum target_hw_bp_type type,
- struct expression *cond, int insert)
+void
+ppc_linux_nat_target::create_watchpoint_request (struct ppc_hw_breakpoint *p,
+ CORE_ADDR addr, int len,
+ enum target_hw_bp_type type,
+ struct expression *cond,
+ int insert)
{
+ const struct ppc_debug_info &hwdebug_info = (m_dreg_interface
+ .hwdebug_info ());
+
if (len == 1
|| !(hwdebug_info.features & PPC_DEBUG_FEATURE_DATA_BP_RANGE))
{
p->addr = (uint64_t) addr;
}
+/* Register a watchpoint, to be inserted when the threads of the group of
+ inferior_ptid are next resumed. Returns 0 on success, and -1 if there
+ is no ptrace interface available to install the watchpoint. */
+
int
ppc_linux_nat_target::insert_watchpoint (CORE_ADDR addr, int len,
enum target_hw_bp_type type,
struct expression *cond)
{
- struct lwp_info *lp;
- int ret = -1;
+ m_dreg_interface.detect (inferior_ptid);
- if (have_ptrace_hwdebug_interface ())
+ if (m_dreg_interface.unavailable_p ())
+ return -1;
+
+ if (m_dreg_interface.hwdebug_p ())
{
struct ppc_hw_breakpoint p;
create_watchpoint_request (&p, addr, len, type, cond, 1);
- ALL_LWPS (lp)
- hwdebug_insert_point (&p, lp->ptid.lwp ());
-
- ret = 0;
+ register_hw_breakpoint (inferior_ptid.pid (), p);
}
else
{
- long dabr_value;
+ gdb_assert (m_dreg_interface.debugreg_p ());
+
+ long wp_value;
long read_mode, write_mode;
- if (linux_get_hwcap (current_top_target ()) & PPC_FEATURE_BOOKE)
+ if (linux_get_hwcap (current_inferior ()->top_target ())
+ & PPC_FEATURE_BOOKE)
{
/* PowerPC 440 requires only the read/write flags to be passed
to the kernel. */
write_mode = 6;
}
- dabr_value = addr & ~(read_mode | write_mode);
+ wp_value = addr & ~(read_mode | write_mode);
switch (type)
{
case hw_read:
/* Set read and translate bits. */
- dabr_value |= read_mode;
+ wp_value |= read_mode;
break;
case hw_write:
/* Set write and translate bits. */
- dabr_value |= write_mode;
+ wp_value |= write_mode;
break;
case hw_access:
/* Set read, write and translate bits. */
- dabr_value |= read_mode | write_mode;
+ wp_value |= read_mode | write_mode;
break;
}
- saved_dabr_value = dabr_value;
-
- ALL_LWPS (lp)
- if (ptrace (PTRACE_SET_DEBUGREG, lp->ptid.lwp (), 0,
- saved_dabr_value) < 0)
- return -1;
-
- ret = 0;
+ register_wp (inferior_ptid.pid (), wp_value);
}
- return ret;
+ return 0;
}
+/* Clear a registration for a hardware watchpoint. It will be removed
+ from the threads of the group of inferior_ptid when they are next
+ resumed. */
+
int
ppc_linux_nat_target::remove_watchpoint (CORE_ADDR addr, int len,
enum target_hw_bp_type type,
struct expression *cond)
{
- struct lwp_info *lp;
- int ret = -1;
+ gdb_assert (!m_dreg_interface.unavailable_p ());
- if (have_ptrace_hwdebug_interface ())
+ if (m_dreg_interface.hwdebug_p ())
{
struct ppc_hw_breakpoint p;
create_watchpoint_request (&p, addr, len, type, cond, 0);
- ALL_LWPS (lp)
- hwdebug_remove_point (&p, lp->ptid.lwp ());
-
- ret = 0;
+ clear_hw_breakpoint (inferior_ptid.pid (), p);
}
else
{
- saved_dabr_value = 0;
- ALL_LWPS (lp)
- if (ptrace (PTRACE_SET_DEBUGREG, lp->ptid.lwp (), 0,
- saved_dabr_value) < 0)
- return -1;
+ gdb_assert (m_dreg_interface.debugreg_p ());
- ret = 0;
+ clear_wp (inferior_ptid.pid ());
}
- return ret;
+ return 0;
}
+/* Clean up the per-process info associated with PID. When using the
+ HWDEBUG interface, we also erase the per-thread state of installed
+ debug registers for all the threads that belong to the group of PID.
+
+ Usually the thread state is cleaned up by low_delete_thread. We also
+ do it here because low_new_thread is not called for the initial LWP,
+ so low_delete_thread won't be able to clean up this state. */
+
void
-ppc_linux_nat_target::low_new_thread (struct lwp_info *lp)
+ppc_linux_nat_target::low_forget_process (pid_t pid)
{
- int tid = lp->ptid.lwp ();
+ if ((!m_dreg_interface.detected_p ())
+ || (m_dreg_interface.unavailable_p ()))
+ return;
- if (have_ptrace_hwdebug_interface ())
+ ptid_t pid_ptid (pid, 0, 0);
+
+ m_process_info.erase (pid);
+
+ if (m_dreg_interface.hwdebug_p ())
{
- int i;
- struct thread_points *p;
- struct hw_break_tuple *hw_breaks;
+ for (auto it = m_installed_hw_bps.begin ();
+ it != m_installed_hw_bps.end ();)
+ {
+ if (it->first.matches (pid_ptid))
+ it = m_installed_hw_bps.erase (it);
+ else
+ it++;
+ }
+ }
+}
- if (ppc_threads.empty ())
- return;
+/* Copy the per-process state associated with the pid of PARENT to the
+ sate of CHILD_PID. GDB expects that a forked process will have the
+ same hardware breakpoints and watchpoints as the parent.
- /* Get a list of breakpoints from any thread. */
- p = ppc_threads.back ();
- hw_breaks = p->hw_breaks;
+ If we're using the HWDEBUG interface, also copy the thread debug
+ register state for the ptid of PARENT to the state for CHILD_PID.
- /* Copy that thread's breakpoints and watchpoints to the new thread. */
- for (i = 0; i < max_slots_number; i++)
- if (hw_breaks[i].hw_break)
- {
- /* Older kernels did not make new threads inherit their parent
- thread's debug state, so we always clear the slot and replicate
- the debug state ourselves, ensuring compatibility with all
- kernels. */
+ Like for clone events, we assume the kernel will copy the debug
+ registers from the parent thread to the child. The
+ low_prepare_to_resume function is made to work even if it doesn't.
- /* The ppc debug resource accounting is done through "slots".
- Ask the kernel the deallocate this specific *point's slot. */
- ptrace (PPC_PTRACE_DELHWDEBUG, tid, 0, hw_breaks[i].slot);
+ We copy the thread state here and not in low_new_thread since we don't
+ have the pid of the parent in low_new_thread. Even if we did,
+ low_new_thread might not be called immediately after the fork event is
+ detected. For instance, with the checkpointing system (see
+ linux-fork.c), the thread won't be added until GDB decides to switch
+ to a new checkpointed process. At that point, the debug register
+ state of the parent thread is unlikely to correspond to the state it
+ had at the point when it forked. */
- hwdebug_insert_point (hw_breaks[i].hw_break, tid);
- }
+void
+ppc_linux_nat_target::low_new_fork (struct lwp_info *parent,
+ pid_t child_pid)
+{
+ if ((!m_dreg_interface.detected_p ())
+ || (m_dreg_interface.unavailable_p ()))
+ return;
+
+ auto process_it = m_process_info.find (parent->ptid.pid ());
+
+ if (process_it != m_process_info.end ())
+ m_process_info[child_pid] = m_process_info[parent->ptid.pid ()];
+
+ if (m_dreg_interface.hwdebug_p ())
+ {
+ ptid_t child_ptid (child_pid, child_pid, 0);
+
+ copy_thread_dreg_state (parent->ptid, child_ptid);
}
- else
- ptrace (PTRACE_SET_DEBUGREG, tid, 0, saved_dabr_value);
}
-static void
-ppc_linux_thread_exit (struct thread_info *tp, int silent)
+/* Copy the thread debug register state from the PARENT thread to the the
+ state for CHILD_LWP, if we're using the HWDEBUG interface. We assume
+ the kernel copies the debug registers from one thread to another after
+ a clone event. The low_prepare_to_resume function is made to work
+ even if it doesn't. */
+
+void
+ppc_linux_nat_target::low_new_clone (struct lwp_info *parent,
+ pid_t child_lwp)
{
- int i;
- int tid = tp->ptid.lwp ();
- struct hw_break_tuple *hw_breaks;
- struct thread_points *t = NULL;
+ if ((!m_dreg_interface.detected_p ())
+ || (m_dreg_interface.unavailable_p ()))
+ return;
+
+ if (m_dreg_interface.hwdebug_p ())
+ {
+ ptid_t child_ptid (parent->ptid.pid (), child_lwp, 0);
+
+ copy_thread_dreg_state (parent->ptid, child_ptid);
+ }
+}
+
+/* Initialize the arch-specific thread state for LP so that it contains
+ the ptid for lp, so that we can use it in low_delete_thread. Mark the
+ new thread LP as stale so that we update its debug registers before
+ resuming it. This is not called for the initial thread. */
+
+void
+ppc_linux_nat_target::low_new_thread (struct lwp_info *lp)
+{
+ init_arch_lwp_info (lp);
+
+ mark_thread_stale (lp);
+}
+
+/* Delete the per-thread debug register stale flag. */
+
+void
+ppc_linux_nat_target::low_delete_thread (struct arch_lwp_info
+ *lp_arch_info)
+{
+ if (lp_arch_info != NULL)
+ {
+ if (m_dreg_interface.detected_p ()
+ && m_dreg_interface.hwdebug_p ())
+ m_installed_hw_bps.erase (lp_arch_info->lwp_ptid);
+
+ xfree (lp_arch_info);
+ }
+}
+
+/* Install or delete debug registers in thread LP so that it matches what
+ GDB requested before it is resumed. */
+
+void
+ppc_linux_nat_target::low_prepare_to_resume (struct lwp_info *lp)
+{
+ if ((!m_dreg_interface.detected_p ())
+ || (m_dreg_interface.unavailable_p ()))
+ return;
+
+ /* We have to re-install or clear the debug registers if we set the
+ stale flag.
+
+ In addition, some kernels configurations can disable a hardware
+ watchpoint after it is hit. Usually, GDB will remove and re-install
+ a hardware watchpoint when the thread stops if "breakpoint
+ always-inserted" is off, or to single-step a watchpoint. But so
+ that we don't rely on this behavior, if we stop due to a hardware
+ breakpoint or watchpoint, we also refresh our debug registers. */
- if (!have_ptrace_hwdebug_interface ())
+ arch_lwp_info *lp_arch_info = get_arch_lwp_info (lp);
+
+ bool stale_dregs = (lp->stop_reason == TARGET_STOPPED_BY_WATCHPOINT
+ || lp->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT
+ || lp_arch_info->debug_regs_stale);
+
+ if (!stale_dregs)
return;
- for (i = 0; i < ppc_threads.size (); i++)
+ gdb_assert (lp->ptid.lwp_p ());
+
+ auto process_it = m_process_info.find (lp->ptid.pid ());
+
+ if (m_dreg_interface.hwdebug_p ())
{
- if (ppc_threads[i]->tid == tid)
+ /* First, delete any hardware watchpoint or breakpoint installed in
+ the inferior and update the thread state. */
+ auto installed_it = m_installed_hw_bps.find (lp->ptid);
+
+ if (installed_it != m_installed_hw_bps.end ())
{
- t = ppc_threads[i];
- break;
+ auto &bp_list = installed_it->second;
+
+ for (auto bp_it = bp_list.begin (); bp_it != bp_list.end ();)
+ {
+ /* We ignore ENOENT to account for various possible kernel
+ behaviors, e.g. the kernel might or might not copy debug
+ registers across forks and clones, and we always copy
+ the debug register state when fork and clone events are
+ detected. */
+ if (ptrace (PPC_PTRACE_DELHWDEBUG, lp->ptid.lwp (), 0,
+ bp_it->first) < 0)
+ if (errno != ENOENT)
+ perror_with_name (_("Error deleting hardware "
+ "breakpoint or watchpoint"));
+
+ /* We erase the entries one at a time after successfuly
+ removing the corresponding slot form the thread so that
+ if we throw an exception above in a future iteration the
+ map remains consistent. */
+ bp_it = bp_list.erase (bp_it);
+ }
+
+ gdb_assert (bp_list.empty ());
}
- }
- if (t == NULL)
- return;
+ /* Now we install all the requested hardware breakpoints and
+ watchpoints and update the thread state. */
- unordered_remove (ppc_threads, i);
+ if (process_it != m_process_info.end ())
+ {
+ auto &bp_list = m_installed_hw_bps[lp->ptid];
- hw_breaks = t->hw_breaks;
+ for (ppc_hw_breakpoint bp
+ : process_it->second.requested_hw_bps)
+ {
+ long slot = ptrace (PPC_PTRACE_SETHWDEBUG, lp->ptid.lwp (),
+ 0, &bp);
- for (i = 0; i < max_slots_number; i++)
- if (hw_breaks[i].hw_break)
- xfree (hw_breaks[i].hw_break);
+ if (slot < 0)
+ perror_with_name (_("Error setting hardware "
+ "breakpoint or watchpoint"));
- xfree (t->hw_breaks);
- xfree (t);
+ /* Keep track of which slots we installed in this
+ thread. */
+ bp_list.emplace (bp_list.begin (), slot, bp);
+ }
+ }
+ }
+ else
+ {
+ gdb_assert (m_dreg_interface.debugreg_p ());
+
+ /* Passing 0 to PTRACE_SET_DEBUGREG will clear the watchpoint. We
+ always clear the watchpoint instead of just overwriting it, in
+ case there is a request for a new watchpoint, because on some
+ older kernel versions and configurations simply overwriting the
+ watchpoint after it was hit would not re-enable it. */
+ if (ptrace (PTRACE_SET_DEBUGREG, lp->ptid.lwp (), 0, 0) < 0)
+ perror_with_name (_("Error clearing hardware watchpoint"));
+
+ /* GDB requested a watchpoint to be installed. */
+ if (process_it != m_process_info.end ()
+ && process_it->second.requested_wp_val.has_value ())
+ {
+ long wp = *(process_it->second.requested_wp_val);
+
+ if (ptrace (PTRACE_SET_DEBUGREG, lp->ptid.lwp (), 0, wp) < 0)
+ perror_with_name (_("Error setting hardware watchpoint"));
+ }
+ }
+
+ lp_arch_info->debug_regs_stale = false;
}
+/* Return true if INFERIOR_PTID is known to have been stopped by a
+ hardware watchpoint, false otherwise. If true is returned, write the
+ address that the kernel reported as causing the SIGTRAP in ADDR_P. */
+
bool
-ppc_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p)
+ppc_linux_nat_target::low_stopped_data_address (CORE_ADDR *addr_p)
{
siginfo_t siginfo;
|| (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
return false;
- if (have_ptrace_hwdebug_interface ())
+ gdb_assert (!m_dreg_interface.unavailable_p ());
+
+ /* Check if this signal corresponds to a hardware breakpoint. We only
+ need to check this if we're using the HWDEBUG interface, since the
+ DEBUGREG interface only allows setting one hardware watchpoint. */
+ if (m_dreg_interface.hwdebug_p ())
{
- int i;
- struct thread_points *t;
- struct hw_break_tuple *hw_breaks;
- /* The index (or slot) of the *point is passed in the si_errno field. */
+ /* The index (or slot) of the *point is passed in the si_errno
+ field. Currently, this is only the case if the kernel was
+ configured with CONFIG_PPC_ADV_DEBUG_REGS. If not, we assume
+ the kernel will set si_errno to a value that doesn't correspond
+ to any real slot. */
int slot = siginfo.si_errno;
- t = hwdebug_find_thread_points_by_tid (inferior_ptid.lwp (), 0);
+ auto installed_it = m_installed_hw_bps.find (inferior_ptid);
- /* Find out if this *point is a hardware breakpoint.
- If so, we should return 0. */
- if (t)
- {
- hw_breaks = t->hw_breaks;
- for (i = 0; i < max_slots_number; i++)
- if (hw_breaks[i].hw_break && hw_breaks[i].slot == slot
- && hw_breaks[i].hw_break->trigger_type
- == PPC_BREAKPOINT_TRIGGER_EXECUTE)
- return false;
- }
+ /* We must have installed slots for the thread if it got a
+ TRAP_HWBKPT signal. */
+ gdb_assert (installed_it != m_installed_hw_bps.end ());
+
+ for (const auto & slot_bp_pair : installed_it->second)
+ if (slot_bp_pair.first == slot
+ && (slot_bp_pair.second.trigger_type
+ == PPC_BREAKPOINT_TRIGGER_EXECUTE))
+ return false;
}
*addr_p = (CORE_ADDR) (uintptr_t) siginfo.si_addr;
return true;
}
+/* Return true if INFERIOR_PTID is known to have been stopped by a
+ hardware watchpoint, false otherwise. */
+
bool
-ppc_linux_nat_target::stopped_by_watchpoint ()
+ppc_linux_nat_target::low_stopped_by_watchpoint ()
{
CORE_ADDR addr;
- return stopped_data_address (&addr);
+ return low_stopped_data_address (&addr);
}
bool
CORE_ADDR start,
int length)
{
+ gdb_assert (!m_dreg_interface.unavailable_p ());
+
int mask;
- if (have_ptrace_hwdebug_interface ()
- && linux_get_hwcap (current_top_target ()) & PPC_FEATURE_BOOKE)
+ if (m_dreg_interface.hwdebug_p ()
+ && (linux_get_hwcap (current_inferior ()->top_target ())
+ & PPC_FEATURE_BOOKE))
return start <= addr && start + length >= addr;
- else if (linux_get_hwcap (current_top_target ()) & PPC_FEATURE_BOOKE)
+ else if (linux_get_hwcap (current_inferior ()->top_target ())
+ & PPC_FEATURE_BOOKE)
mask = 3;
else
mask = 7;
/* Return the number of registers needed for a masked hardware watchpoint. */
int
-ppc_linux_nat_target::masked_watch_num_registers (CORE_ADDR addr, CORE_ADDR mask)
+ppc_linux_nat_target::masked_watch_num_registers (CORE_ADDR addr,
+ CORE_ADDR mask)
{
- if (!have_ptrace_hwdebug_interface ()
- || (hwdebug_info.features & PPC_DEBUG_FEATURE_DATA_BP_MASK) == 0)
+ m_dreg_interface.detect (inferior_ptid);
+
+ if (!m_dreg_interface.hwdebug_p ()
+ || (m_dreg_interface.hwdebug_info ().features
+ & PPC_DEBUG_FEATURE_DATA_BP_MASK) == 0)
return -1;
else if ((mask & 0xC0000000) != 0xC0000000)
{
return 2;
}
+/* Copy the per-thread debug register state, if any, from thread
+ PARENT_PTID to thread CHILD_PTID, if the debug register being used is
+ HWDEBUG. */
+
void
-ppc_linux_nat_target::store_registers (struct regcache *regcache, int regno)
+ppc_linux_nat_target::copy_thread_dreg_state (const ptid_t &parent_ptid,
+ const ptid_t &child_ptid)
{
- pid_t tid = get_ptrace_pid (regcache->ptid ());
+ gdb_assert (m_dreg_interface.hwdebug_p ());
- if (regno >= 0)
- store_register (regcache, tid, regno);
- else
- store_ppc_registers (regcache, tid);
+ auto installed_it = m_installed_hw_bps.find (parent_ptid);
+
+ if (installed_it != m_installed_hw_bps.end ())
+ m_installed_hw_bps[child_ptid] = m_installed_hw_bps[parent_ptid];
}
-/* Functions for transferring registers between a gregset_t or fpregset_t
- (see sys/ucontext.h) and gdb's regcache. The word size is that used
- by the ptrace interface, not the current program's ABI. Eg. if a
- powerpc64-linux gdb is being used to debug a powerpc32-linux app, we
- read or write 64-bit gregsets. This is to suit the host libthread_db. */
+/* Mark the debug register stale flag for the new thread, if we have
+ already detected which debug register interface we use. */
void
-supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregsetp)
+ppc_linux_nat_target::mark_thread_stale (struct lwp_info *lp)
{
- const struct regset *regset = ppc_linux_gregset (sizeof (long));
-
- ppc_supply_gregset (regset, regcache, -1, gregsetp, sizeof (*gregsetp));
-}
+ if ((!m_dreg_interface.detected_p ())
+ || (m_dreg_interface.unavailable_p ()))
+ return;
-void
-fill_gregset (const struct regcache *regcache,
- gdb_gregset_t *gregsetp, int regno)
-{
- const struct regset *regset = ppc_linux_gregset (sizeof (long));
+ arch_lwp_info *lp_arch_info = get_arch_lwp_info (lp);
- if (regno == -1)
- memset (gregsetp, 0, sizeof (*gregsetp));
- ppc_collect_gregset (regset, regcache, regno, gregsetp, sizeof (*gregsetp));
+ lp_arch_info->debug_regs_stale = true;
}
-void
-supply_fpregset (struct regcache *regcache, const gdb_fpregset_t * fpregsetp)
-{
- const struct regset *regset = ppc_linux_fpregset ();
+/* Mark all the threads of the group of PID as stale with respect to
+ debug registers and issue a stop request to each such thread that
+ isn't already stopped. */
- ppc_supply_fpregset (regset, regcache, -1,
- fpregsetp, sizeof (*fpregsetp));
+void
+ppc_linux_nat_target::mark_debug_registers_changed (pid_t pid)
+{
+ /* We do this in two passes to make sure all threads are marked even if
+ we get an exception when stopping one of them. */
+
+ iterate_over_lwps (ptid_t (pid),
+ [this] (struct lwp_info *lp) -> int {
+ this->mark_thread_stale (lp);
+ return 0;
+ });
+
+ iterate_over_lwps (ptid_t (pid),
+ [] (struct lwp_info *lp) -> int {
+ if (!lwp_is_stopped (lp))
+ linux_stop_lwp (lp);
+ return 0;
+ });
}
+/* Register a hardware breakpoint or watchpoint BP for the pid PID, then
+ mark the stale flag for all threads of the group of PID, and issue a
+ stop request for them. The breakpoint or watchpoint will be installed
+ the next time each thread is resumed. Should only be used if the
+ debug register interface is HWDEBUG. */
+
void
-fill_fpregset (const struct regcache *regcache,
- gdb_fpregset_t *fpregsetp, int regno)
+ppc_linux_nat_target::register_hw_breakpoint (pid_t pid,
+ const struct
+ ppc_hw_breakpoint &bp)
{
- const struct regset *regset = ppc_linux_fpregset ();
+ gdb_assert (m_dreg_interface.hwdebug_p ());
- ppc_collect_fpregset (regset, regcache, regno,
- fpregsetp, sizeof (*fpregsetp));
+ m_process_info[pid].requested_hw_bps.push_back (bp);
+
+ mark_debug_registers_changed (pid);
}
-int
-ppc_linux_nat_target::auxv_parse (gdb_byte **readptr,
- gdb_byte *endptr, CORE_ADDR *typep,
- CORE_ADDR *valp)
+/* Clear a registration for a hardware breakpoint or watchpoint BP for
+ the pid PID, then mark the stale flag for all threads of the group of
+ PID, and issue a stop request for them. The breakpoint or watchpoint
+ will be removed the next time each thread is resumed. Should only be
+ used if the debug register interface is HWDEBUG. */
+
+void
+ppc_linux_nat_target::clear_hw_breakpoint (pid_t pid,
+ const struct ppc_hw_breakpoint &bp)
{
- int tid = inferior_ptid.lwp ();
- if (tid == 0)
- tid = inferior_ptid.pid ();
+ gdb_assert (m_dreg_interface.hwdebug_p ());
- int sizeof_auxv_field = ppc_linux_target_wordsize (tid);
+ auto process_it = m_process_info.find (pid);
- enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ());
- gdb_byte *ptr = *readptr;
+ gdb_assert (process_it != m_process_info.end ());
- if (endptr == ptr)
- return 0;
+ auto bp_it = std::find_if (process_it->second.requested_hw_bps.begin (),
+ process_it->second.requested_hw_bps.end (),
+ [&bp, this]
+ (const struct ppc_hw_breakpoint &curr)
+ { return hwdebug_point_cmp (bp, curr); }
+ );
- if (endptr - ptr < sizeof_auxv_field * 2)
- return -1;
+ /* If GDB is removing a watchpoint, it must have been inserted. */
+ gdb_assert (bp_it != process_it->second.requested_hw_bps.end ());
- *typep = extract_unsigned_integer (ptr, sizeof_auxv_field, byte_order);
- ptr += sizeof_auxv_field;
- *valp = extract_unsigned_integer (ptr, sizeof_auxv_field, byte_order);
- ptr += sizeof_auxv_field;
+ process_it->second.requested_hw_bps.erase (bp_it);
- *readptr = ptr;
- return 1;
+ mark_debug_registers_changed (pid);
}
-const struct target_desc *
-ppc_linux_nat_target::read_description ()
+/* Register the hardware watchpoint value WP_VALUE for the pid PID,
+ then mark the stale flag for all threads of the group of PID, and
+ issue a stop request for them. The breakpoint or watchpoint will be
+ installed the next time each thread is resumed. Should only be used
+ if the debug register interface is DEBUGREG. */
+
+void
+ppc_linux_nat_target::register_wp (pid_t pid, long wp_value)
{
- int tid = inferior_ptid.lwp ();
- if (tid == 0)
- tid = inferior_ptid.pid ();
+ gdb_assert (m_dreg_interface.debugreg_p ());
- if (have_ptrace_getsetevrregs)
- {
- struct gdb_evrregset_t evrregset;
+ /* Our other functions should have told GDB that we only have one
+ hardware watchpoint with this interface. */
+ gdb_assert (!m_process_info[pid].requested_wp_val.has_value ());
- if (ptrace (PTRACE_GETEVRREGS, tid, 0, &evrregset) >= 0)
- return tdesc_powerpc_e500l;
+ m_process_info[pid].requested_wp_val.emplace (wp_value);
- /* EIO means that the PTRACE_GETEVRREGS request isn't supported.
- Anything else needs to be reported. */
- else if (errno != EIO)
- perror_with_name (_("Unable to fetch SPE registers"));
- }
+ mark_debug_registers_changed (pid);
+}
- struct ppc_linux_features features = ppc_linux_no_features;
+/* Clear the hardware watchpoint registration for the pid PID, then mark
+ the stale flag for all threads of the group of PID, and issue a stop
+ request for them. The breakpoint or watchpoint will be installed the
+ next time each thread is resumed. Should only be used if the debug
+ register interface is DEBUGREG. */
- features.wordsize = ppc_linux_target_wordsize (tid);
+void
+ppc_linux_nat_target::clear_wp (pid_t pid)
+{
+ gdb_assert (m_dreg_interface.debugreg_p ());
- CORE_ADDR hwcap = linux_get_hwcap (current_top_target ());
- CORE_ADDR hwcap2 = linux_get_hwcap2 (current_top_target ());
+ auto process_it = m_process_info.find (pid);
- if (have_ptrace_getsetvsxregs
- && (hwcap & PPC_FEATURE_HAS_VSX))
- {
- gdb_vsxregset_t vsxregset;
+ gdb_assert (process_it != m_process_info.end ());
+ gdb_assert (process_it->second.requested_wp_val.has_value ());
- if (ptrace (PTRACE_GETVSXREGS, tid, 0, &vsxregset) >= 0)
- features.vsx = true;
-
- /* EIO means that the PTRACE_GETVSXREGS request isn't supported.
- Anything else needs to be reported. */
- else if (errno != EIO)
- perror_with_name (_("Unable to fetch VSX registers"));
- }
+ process_it->second.requested_wp_val.reset ();
- if (have_ptrace_getvrregs
- && (hwcap & PPC_FEATURE_HAS_ALTIVEC))
- {
- gdb_vrregset_t vrregset;
+ mark_debug_registers_changed (pid);
+}
- if (ptrace (PTRACE_GETVRREGS, tid, 0, &vrregset) >= 0)
- features.altivec = true;
+/* Initialize the arch-specific thread state for LWP, if it not already
+ created. */
- /* EIO means that the PTRACE_GETVRREGS request isn't supported.
- Anything else needs to be reported. */
- else if (errno != EIO)
- perror_with_name (_("Unable to fetch AltiVec registers"));
+void
+ppc_linux_nat_target::init_arch_lwp_info (struct lwp_info *lp)
+{
+ if (lwp_arch_private_info (lp) == NULL)
+ {
+ lwp_set_arch_private_info (lp, XCNEW (struct arch_lwp_info));
+ lwp_arch_private_info (lp)->debug_regs_stale = false;
+ lwp_arch_private_info (lp)->lwp_ptid = lp->ptid;
}
+}
- features.isa205 = ppc_linux_has_isa205 (hwcap);
+/* Get the arch-specific thread state for LWP, creating it if
+ necessary. */
- 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;
- if ((hwcap2 & PPC_FEATURE2_ARCH_2_07)
- && (hwcap2 & PPC_FEATURE2_TAR)
- && (hwcap2 & PPC_FEATURE2_EBB)
- && check_regset (tid, NT_PPC_TAR, PPC_LINUX_SIZEOF_TARREGSET)
- && check_regset (tid, NT_PPC_EBB, PPC_LINUX_SIZEOF_EBBREGSET)
- && check_regset (tid, NT_PPC_PMU, PPC_LINUX_SIZEOF_PMUREGSET))
- {
- features.isa207 = true;
- if ((hwcap2 & PPC_FEATURE2_HTM)
- && check_regset (tid, NT_PPC_TM_SPR,
- PPC_LINUX_SIZEOF_TM_SPRREGSET))
- features.htm = true;
- }
- }
+arch_lwp_info *
+ppc_linux_nat_target::get_arch_lwp_info (struct lwp_info *lp)
+{
+ init_arch_lwp_info (lp);
- return ppc_linux_match_description (features);
+ return lwp_arch_private_info (lp);
}
void _initialize_ppc_linux_nat ();
{
linux_target = &the_ppc_linux_nat_target;
- gdb::observers::thread_exit.attach (ppc_linux_thread_exit);
-
/* Register the target. */
add_inf_child_target (linux_target);
}