DebugServer2
Loading...
Searching...
No Matches
HardwareWatchpoint.h
1//
2// Copyright (c) 2014-present, Facebook, Inc.
3// All rights reserved.
4//
5// This source code is licensed under the University of Illinois/NCSA Open
6// Source License found in the LICENSE file in the root directory of this
7// source tree. An additional grant of patent rights can be found in the
8// PATENTS file in the same directory.
9//
10
11#pragma once
12
13#include "DebugServer2/Core/BreakpointManager.h"
14#include "DebugServer2/Core/ErrorCodes.h"
15#include "DebugServer2/Types.h"
16
17namespace ds2 {
18
19//
20// DBGWVRn_EL1/DBGWCRn_EL1 (or Wvr[]/Wcr[] on Windows) bit layout, shared by
21// every OS-specific HardwareBreakpointManager implementation for ARM64. The
22// encoding mirrors LLDB's own client-side implementation for the same
23// hardware (NativeRegisterContextDBReg_arm64.cpp /
24// NativeRegisterContextLinux_arm64dbreg.cpp), which is the most authoritative
25// available cross-check for the exact encoding short of the ARMv8-A
26// Architecture Reference Manual itself:
27//
28// bit 0 : E (enable)
29// bits 2:1 : PAC (privilege access control; 0b10, EL0-only, is what
30// LLDB programs and is what we mirror here)
31// bits 4:3 : LSC (load/store control: 01 for load/read, 10 for
32// store/write, 11 for either)
33// bits 12:5 : BAS (byte address select, one bit per watched byte
34// within the 8-byte-aligned doubleword pointed at by
35// the value register)
36//
37
38constexpr uint32_t kARM64WatchpointEnable = 1u << 0;
39constexpr uint32_t kARM64WatchpointPAC = 0x2u << 1;
40constexpr uint32_t kARM64WatchpointLSCShift = 3;
41constexpr uint32_t kARM64WatchpointBASShift = 5;
42
43// Computes the DBGWVRn_EL1/DBGWCRn_EL1 (or Wvr[]/Wcr[] on Windows) pair that
44// implements a watchpoint over [address, address + size). `address`/`size`
45// are validated (size in {1, 2, 4, 8}, and the range fits within a single
46// 8-byte watchpoint granule) by HardwareBreakpointManager::isValid() before
47// this is ever reached.
48inline ErrorCode ComputeARM64WatchpointControl(Address const &address,
49 size_t size,
50 BreakpointManager::Mode mode,
51 uint64_t &wvr, uint32_t &wcr) {
52 uint32_t lsc;
53 switch (static_cast<int>(mode)) {
54 case BreakpointManager::kModeWrite:
55 lsc = 0x2;
56 break;
57 case BreakpointManager::kModeRead:
58 lsc = 0x1;
59 break;
60 case BreakpointManager::kModeRead | BreakpointManager::kModeWrite:
61 lsc = 0x3;
62 break;
63 default:
64 return kErrorInvalidArgument;
65 }
66
67 uint64_t addr = address;
68 unsigned offset = static_cast<unsigned>(addr & 0x7);
69 unsigned span = offset + static_cast<unsigned>(size);
70 if (span > 8) {
71 // Does not fit in a single hardware watchpoint slot.
72 return kErrorInvalidArgument;
73 }
74
75 // BAS only needs to be a contiguous run of bits within the 8-byte
76 // granule; the hardware does not require that run to itself be aligned
77 // to its own size (this matches the Linux kernel's own arm64
78 // hw_breakpoint validation, which simply shifts the size-based mask by
79 // the raw offset). So the exact requested range can always be expressed
80 // directly: e.g. a 2-byte watchpoint at the granule's byte 1 is BAS 0x06
81 // (bytes 1-2), not a widened 4-byte range starting at byte 0.
82 uint32_t bas = static_cast<uint32_t>((1u << size) - 1) << offset;
83
84 wvr = addr & ~static_cast<uint64_t>(0x7);
85 wcr = kARM64WatchpointEnable | kARM64WatchpointPAC |
86 (lsc << kARM64WatchpointLSCShift) | (bas << kARM64WatchpointBASShift);
87 return kSuccess;
88}
89
90} // namespace ds2