123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- /* Copyright (C) 2009-2022 Free Software Foundation, Inc.
- Contributed by ARM Ltd.
- This file is part of GDB.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
- #include "gdbsupport/common-defs.h"
- #include "gdbsupport/break-common.h"
- #include "gdbsupport/common-regcache.h"
- #include "nat/linux-nat.h"
- #include "aarch64-linux-hw-point.h"
- #include <sys/uio.h>
- /* The order in which <sys/ptrace.h> and <asm/ptrace.h> are included
- can be important. <sys/ptrace.h> often declares various PTRACE_*
- enums. <asm/ptrace.h> often defines preprocessor constants for
- these very same symbols. When that's the case, build errors will
- result when <asm/ptrace.h> is included before <sys/ptrace.h>. */
- #include <sys/ptrace.h>
- #include <asm/ptrace.h>
- #include <elf.h>
- /* See aarch64-linux-hw-point.h */
- bool kernel_supports_any_contiguous_range = true;
- /* Helper for aarch64_notify_debug_reg_change. Records the
- information about the change of one hardware breakpoint/watchpoint
- setting for the thread LWP.
- N.B. The actual updating of hardware debug registers is not
- carried out until the moment the thread is resumed. */
- static int
- debug_reg_change_callback (struct lwp_info *lwp, int is_watchpoint,
- unsigned int idx)
- {
- int tid = ptid_of_lwp (lwp).lwp ();
- struct arch_lwp_info *info = lwp_arch_private_info (lwp);
- dr_changed_t *dr_changed_ptr;
- dr_changed_t dr_changed;
- if (info == NULL)
- {
- info = XCNEW (struct arch_lwp_info);
- lwp_set_arch_private_info (lwp, info);
- }
- if (show_debug_regs)
- {
- debug_printf ("debug_reg_change_callback: \n\tOn entry:\n");
- debug_printf ("\ttid%d, dr_changed_bp=0x%s, "
- "dr_changed_wp=0x%s\n", tid,
- phex (info->dr_changed_bp, 8),
- phex (info->dr_changed_wp, 8));
- }
- dr_changed_ptr = is_watchpoint ? &info->dr_changed_wp
- : &info->dr_changed_bp;
- dr_changed = *dr_changed_ptr;
- gdb_assert (idx >= 0
- && (idx <= (is_watchpoint ? aarch64_num_wp_regs
- : aarch64_num_bp_regs)));
- /* The actual update is done later just before resuming the lwp,
- we just mark that one register pair needs updating. */
- DR_MARK_N_CHANGED (dr_changed, idx);
- *dr_changed_ptr = dr_changed;
- /* If the lwp isn't stopped, force it to momentarily pause, so
- we can update its debug registers. */
- if (!lwp_is_stopped (lwp))
- linux_stop_lwp (lwp);
- if (show_debug_regs)
- {
- debug_printf ("\tOn exit:\n\ttid%d, dr_changed_bp=0x%s, "
- "dr_changed_wp=0x%s\n", tid,
- phex (info->dr_changed_bp, 8),
- phex (info->dr_changed_wp, 8));
- }
- return 0;
- }
- /* Notify each thread that their IDXth breakpoint/watchpoint register
- pair needs to be updated. The message will be recorded in each
- thread's arch-specific data area, the actual updating will be done
- when the thread is resumed. */
- void
- aarch64_notify_debug_reg_change (ptid_t ptid,
- int is_watchpoint, unsigned int idx)
- {
- ptid_t pid_ptid = ptid_t (ptid.pid ());
- iterate_over_lwps (pid_ptid, [=] (struct lwp_info *info)
- {
- return debug_reg_change_callback (info,
- is_watchpoint,
- idx);
- });
- }
- /* Reconfigure STATE to be compatible with Linux kernels with the PR
- external/20207 bug. This is called when
- KERNEL_SUPPORTS_ANY_CONTIGUOUS_RANGE transitions to false. Note we
- don't try to support combining watchpoints with matching (and thus
- shared) masks, as it's too late when we get here. On buggy
- kernels, GDB will try to first setup the perfect matching ranges,
- which will run out of registers before this function can merge
- them. It doesn't look like worth the effort to improve that, given
- eventually buggy kernels will be phased out. */
- static void
- aarch64_downgrade_regs (struct aarch64_debug_reg_state *state)
- {
- for (int i = 0; i < aarch64_num_wp_regs; ++i)
- if ((state->dr_ctrl_wp[i] & 1) != 0)
- {
- gdb_assert (state->dr_ref_count_wp[i] != 0);
- uint8_t mask_orig = (state->dr_ctrl_wp[i] >> 5) & 0xff;
- gdb_assert (mask_orig != 0);
- static const uint8_t old_valid[] = { 0x01, 0x03, 0x0f, 0xff };
- uint8_t mask = 0;
- for (const uint8_t old_mask : old_valid)
- if (mask_orig <= old_mask)
- {
- mask = old_mask;
- break;
- }
- gdb_assert (mask != 0);
- /* No update needed for this watchpoint? */
- if (mask == mask_orig)
- continue;
- state->dr_ctrl_wp[i] |= mask << 5;
- state->dr_addr_wp[i]
- = align_down (state->dr_addr_wp[i], AARCH64_HWP_ALIGNMENT);
- /* Try to match duplicate entries. */
- for (int j = 0; j < i; ++j)
- if ((state->dr_ctrl_wp[j] & 1) != 0
- && state->dr_addr_wp[j] == state->dr_addr_wp[i]
- && state->dr_addr_orig_wp[j] == state->dr_addr_orig_wp[i]
- && state->dr_ctrl_wp[j] == state->dr_ctrl_wp[i])
- {
- state->dr_ref_count_wp[j] += state->dr_ref_count_wp[i];
- state->dr_ref_count_wp[i] = 0;
- state->dr_addr_wp[i] = 0;
- state->dr_addr_orig_wp[i] = 0;
- state->dr_ctrl_wp[i] &= ~1;
- break;
- }
- aarch64_notify_debug_reg_change (current_lwp_ptid (),
- 1 /* is_watchpoint */, i);
- }
- }
- /* Call ptrace to set the thread TID's hardware breakpoint/watchpoint
- registers with data from *STATE. */
- void
- aarch64_linux_set_debug_regs (struct aarch64_debug_reg_state *state,
- int tid, int watchpoint)
- {
- int i, count;
- struct iovec iov;
- struct user_hwdebug_state regs;
- const CORE_ADDR *addr;
- const unsigned int *ctrl;
- memset (®s, 0, sizeof (regs));
- iov.iov_base = ®s;
- count = watchpoint ? aarch64_num_wp_regs : aarch64_num_bp_regs;
- addr = watchpoint ? state->dr_addr_wp : state->dr_addr_bp;
- ctrl = watchpoint ? state->dr_ctrl_wp : state->dr_ctrl_bp;
- if (count == 0)
- return;
- iov.iov_len = (offsetof (struct user_hwdebug_state, dbg_regs)
- + count * sizeof (regs.dbg_regs[0]));
- for (i = 0; i < count; i++)
- {
- regs.dbg_regs[i].addr = addr[i];
- regs.dbg_regs[i].ctrl = ctrl[i];
- }
- if (ptrace (PTRACE_SETREGSET, tid,
- watchpoint ? NT_ARM_HW_WATCH : NT_ARM_HW_BREAK,
- (void *) &iov))
- {
- /* Handle Linux kernels with the PR external/20207 bug. */
- if (watchpoint && errno == EINVAL
- && kernel_supports_any_contiguous_range)
- {
- kernel_supports_any_contiguous_range = false;
- aarch64_downgrade_regs (state);
- aarch64_linux_set_debug_regs (state, tid, watchpoint);
- return;
- }
- error (_("Unexpected error setting hardware debug registers"));
- }
- }
- /* Return true if debug arch level is compatible for hw watchpoints
- and breakpoints. */
- static bool
- compatible_debug_arch (unsigned int debug_arch)
- {
- if (debug_arch == AARCH64_DEBUG_ARCH_V8)
- return true;
- if (debug_arch == AARCH64_DEBUG_ARCH_V8_1)
- return true;
- if (debug_arch == AARCH64_DEBUG_ARCH_V8_2)
- return true;
- if (debug_arch == AARCH64_DEBUG_ARCH_V8_4)
- return true;
- return false;
- }
- /* Get the hardware debug register capacity information from the
- process represented by TID. */
- void
- aarch64_linux_get_debug_reg_capacity (int tid)
- {
- struct iovec iov;
- struct user_hwdebug_state dreg_state;
- iov.iov_base = &dreg_state;
- iov.iov_len = sizeof (dreg_state);
- /* Get hardware watchpoint register info. */
- if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_HW_WATCH, &iov) == 0
- && compatible_debug_arch (AARCH64_DEBUG_ARCH (dreg_state.dbg_info)))
- {
- aarch64_num_wp_regs = AARCH64_DEBUG_NUM_SLOTS (dreg_state.dbg_info);
- if (aarch64_num_wp_regs > AARCH64_HWP_MAX_NUM)
- {
- warning (_("Unexpected number of hardware watchpoint registers"
- " reported by ptrace, got %d, expected %d."),
- aarch64_num_wp_regs, AARCH64_HWP_MAX_NUM);
- aarch64_num_wp_regs = AARCH64_HWP_MAX_NUM;
- }
- }
- else
- {
- warning (_("Unable to determine the number of hardware watchpoints"
- " available."));
- aarch64_num_wp_regs = 0;
- }
- /* Get hardware breakpoint register info. */
- if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_HW_BREAK, &iov) == 0
- && compatible_debug_arch (AARCH64_DEBUG_ARCH (dreg_state.dbg_info)))
- {
- aarch64_num_bp_regs = AARCH64_DEBUG_NUM_SLOTS (dreg_state.dbg_info);
- if (aarch64_num_bp_regs > AARCH64_HBP_MAX_NUM)
- {
- warning (_("Unexpected number of hardware breakpoint registers"
- " reported by ptrace, got %d, expected %d."),
- aarch64_num_bp_regs, AARCH64_HBP_MAX_NUM);
- aarch64_num_bp_regs = AARCH64_HBP_MAX_NUM;
- }
- }
- else
- {
- warning (_("Unable to determine the number of hardware breakpoints"
- " available."));
- aarch64_num_bp_regs = 0;
- }
- }
|