+/* 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. */
+ void fetch_registers (struct regcache *, int) override;
+ void store_registers (struct regcache *, int) override;
+
+ /* Add our breakpoint/watchpoint methods. */
+ int can_use_hw_breakpoint (enum bptype, int, int) override;
+
+ int insert_hw_breakpoint (struct gdbarch *, struct bp_target_info *)
+ override;
+
+ int remove_hw_breakpoint (struct gdbarch *, struct bp_target_info *)
+ override;
+
+ int region_ok_for_hw_watchpoint (CORE_ADDR, int) override;
+
+ int insert_watchpoint (CORE_ADDR, int, enum target_hw_bp_type,
+ struct expression *) override;
+
+ int remove_watchpoint (CORE_ADDR, int, enum target_hw_bp_type,
+ struct expression *) override;
+
+ int insert_mask_watchpoint (CORE_ADDR, CORE_ADDR, enum target_hw_bp_type)
+ override;
+
+ int remove_mask_watchpoint (CORE_ADDR, CORE_ADDR, enum target_hw_bp_type)
+ override;
+
+ bool watchpoint_addr_within_range (CORE_ADDR, CORE_ADDR, int) override;
+
+ bool can_accel_watchpoint_condition (CORE_ADDR, int, int, struct expression *)
+ override;
+
+ int masked_watch_num_registers (CORE_ADDR, CORE_ADDR) override;
+
+ int ranged_break_num_registers () override;
+
+ const struct target_desc *read_description () override;
+
+ int auxv_parse (gdb_byte **readptr,
+ gdb_byte *endptr, CORE_ADDR *typep, CORE_ADDR *valp)
+ 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;
+