1*af732203SDimitry Andric //===-- NativeRegisterContextDBReg_x86.cpp --------------------------------===//
2*af732203SDimitry Andric //
3*af732203SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*af732203SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*af732203SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*af732203SDimitry Andric //
7*af732203SDimitry Andric //===----------------------------------------------------------------------===//
8*af732203SDimitry Andric
9*af732203SDimitry Andric #include "NativeRegisterContextDBReg_x86.h"
10*af732203SDimitry Andric
11*af732203SDimitry Andric #include "lldb/Utility/Log.h"
12*af732203SDimitry Andric #include "lldb/Utility/RegisterValue.h"
13*af732203SDimitry Andric
14*af732203SDimitry Andric #include "Plugins/Process/Utility/lldb-x86-register-enums.h"
15*af732203SDimitry Andric
16*af732203SDimitry Andric using namespace lldb_private;
17*af732203SDimitry Andric
18*af732203SDimitry Andric // Returns mask/value for status bit of wp_index in DR6
GetStatusBit(uint32_t wp_index)19*af732203SDimitry Andric static inline uint64_t GetStatusBit(uint32_t wp_index) {
20*af732203SDimitry Andric // DR6: ...BBBB
21*af732203SDimitry Andric // 3210 <- status bits for bp./wp. i; 1 if hit
22*af732203SDimitry Andric return 1 << wp_index;
23*af732203SDimitry Andric }
24*af732203SDimitry Andric
25*af732203SDimitry Andric // Returns mask/value for global enable bit of wp_index in DR7
GetEnableBit(uint32_t wp_index)26*af732203SDimitry Andric static inline uint64_t GetEnableBit(uint32_t wp_index) {
27*af732203SDimitry Andric // DR7: ...GLGLGLGL
28*af732203SDimitry Andric // 33221100 <- global/local enable for bp./wp.; 1 if enabled
29*af732203SDimitry Andric // we use global bits because NetBSD kernel does not preserve local
30*af732203SDimitry Andric // bits reliably; Linux seems fine with either
31*af732203SDimitry Andric return 1 << (2 * wp_index + 1);
32*af732203SDimitry Andric }
33*af732203SDimitry Andric
34*af732203SDimitry Andric // Returns mask for both enable bits of wp_index in DR7
GetBothEnableBitMask(uint32_t wp_index)35*af732203SDimitry Andric static inline uint64_t GetBothEnableBitMask(uint32_t wp_index) {
36*af732203SDimitry Andric // DR7: ...GLGLGLGL
37*af732203SDimitry Andric // 33221100 <- global/local enable for bp./wp.; 1 if enabled
38*af732203SDimitry Andric return 3 << (2 * wp_index + 1);
39*af732203SDimitry Andric }
40*af732203SDimitry Andric
41*af732203SDimitry Andric // Returns value for type bits of wp_index in DR7
GetWatchTypeBits(uint32_t watch_flags,uint32_t wp_index)42*af732203SDimitry Andric static inline uint64_t GetWatchTypeBits(uint32_t watch_flags,
43*af732203SDimitry Andric uint32_t wp_index) {
44*af732203SDimitry Andric // DR7:
45*af732203SDimitry Andric // bit: 3322222222221111...
46*af732203SDimitry Andric // 1098765432109876...
47*af732203SDimitry Andric // val: SSTTSSTTSSTTSSTT...
48*af732203SDimitry Andric // wp.: 3333222211110000...
49*af732203SDimitry Andric //
50*af732203SDimitry Andric // where T - type is 01 for write, 11 for r/w
51*af732203SDimitry Andric return watch_flags << (16 + 4 * wp_index);
52*af732203SDimitry Andric }
53*af732203SDimitry Andric
54*af732203SDimitry Andric // Returns value for size bits of wp_index in DR7
GetWatchSizeBits(uint32_t size,uint32_t wp_index)55*af732203SDimitry Andric static inline uint64_t GetWatchSizeBits(uint32_t size, uint32_t wp_index) {
56*af732203SDimitry Andric // DR7:
57*af732203SDimitry Andric // bit: 3322222222221111...
58*af732203SDimitry Andric // 1098765432109876...
59*af732203SDimitry Andric // val: SSTTSSTTSSTTSSTT...
60*af732203SDimitry Andric // wp.: 3333222211110000...
61*af732203SDimitry Andric //
62*af732203SDimitry Andric // where S - size is:
63*af732203SDimitry Andric // 00 for 1 byte
64*af732203SDimitry Andric // 01 for 2 bytes
65*af732203SDimitry Andric // 10 for 8 bytes
66*af732203SDimitry Andric // 11 for 4 bytes
67*af732203SDimitry Andric return (size == 8 ? 0x2 : size - 1) << (18 + 4 * wp_index);
68*af732203SDimitry Andric }
69*af732203SDimitry Andric
70*af732203SDimitry Andric // Returns bitmask for all bits controlling wp_index in DR7
GetWatchControlBitmask(uint32_t wp_index)71*af732203SDimitry Andric static inline uint64_t GetWatchControlBitmask(uint32_t wp_index) {
72*af732203SDimitry Andric // DR7:
73*af732203SDimitry Andric // bit: 33222222222211111111110000000000
74*af732203SDimitry Andric // 10987654321098765432109876543210
75*af732203SDimitry Andric // val: SSTTSSTTSSTTSSTTxxxxxxGLGLGLGLGL
76*af732203SDimitry Andric // wp.: 3333222211110000xxxxxxEE33221100
77*af732203SDimitry Andric return GetBothEnableBitMask(wp_index) | (0xF << (16 + 4 * wp_index));
78*af732203SDimitry Andric }
79*af732203SDimitry Andric
80*af732203SDimitry Andric // Bit mask for control bits regarding all watchpoints.
81*af732203SDimitry Andric static constexpr uint64_t watchpoint_all_control_bit_mask = 0xFFFF00FF;
82*af732203SDimitry Andric
GetDR(int num) const83*af732203SDimitry Andric const RegisterInfo *NativeRegisterContextDBReg_x86::GetDR(int num) const {
84*af732203SDimitry Andric assert(num >= 0 && num <= 7);
85*af732203SDimitry Andric switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) {
86*af732203SDimitry Andric case llvm::Triple::x86:
87*af732203SDimitry Andric return GetRegisterInfoAtIndex(lldb_dr0_i386 + num);
88*af732203SDimitry Andric case llvm::Triple::x86_64:
89*af732203SDimitry Andric return GetRegisterInfoAtIndex(lldb_dr0_x86_64 + num);
90*af732203SDimitry Andric default:
91*af732203SDimitry Andric llvm_unreachable("Unhandled target architecture.");
92*af732203SDimitry Andric }
93*af732203SDimitry Andric }
94*af732203SDimitry Andric
IsWatchpointHit(uint32_t wp_index,bool & is_hit)95*af732203SDimitry Andric Status NativeRegisterContextDBReg_x86::IsWatchpointHit(uint32_t wp_index,
96*af732203SDimitry Andric bool &is_hit) {
97*af732203SDimitry Andric if (wp_index >= NumSupportedHardwareWatchpoints())
98*af732203SDimitry Andric return Status("Watchpoint index out of range");
99*af732203SDimitry Andric
100*af732203SDimitry Andric RegisterValue dr6;
101*af732203SDimitry Andric Status error = ReadRegister(GetDR(6), dr6);
102*af732203SDimitry Andric if (error.Fail())
103*af732203SDimitry Andric is_hit = false;
104*af732203SDimitry Andric else
105*af732203SDimitry Andric is_hit = dr6.GetAsUInt64() & GetStatusBit(wp_index);
106*af732203SDimitry Andric
107*af732203SDimitry Andric return error;
108*af732203SDimitry Andric }
109*af732203SDimitry Andric
110*af732203SDimitry Andric Status
GetWatchpointHitIndex(uint32_t & wp_index,lldb::addr_t trap_addr)111*af732203SDimitry Andric NativeRegisterContextDBReg_x86::GetWatchpointHitIndex(uint32_t &wp_index,
112*af732203SDimitry Andric lldb::addr_t trap_addr) {
113*af732203SDimitry Andric uint32_t num_hw_wps = NumSupportedHardwareWatchpoints();
114*af732203SDimitry Andric for (wp_index = 0; wp_index < num_hw_wps; ++wp_index) {
115*af732203SDimitry Andric bool is_hit;
116*af732203SDimitry Andric Status error = IsWatchpointHit(wp_index, is_hit);
117*af732203SDimitry Andric if (error.Fail()) {
118*af732203SDimitry Andric wp_index = LLDB_INVALID_INDEX32;
119*af732203SDimitry Andric return error;
120*af732203SDimitry Andric } else if (is_hit) {
121*af732203SDimitry Andric return error;
122*af732203SDimitry Andric }
123*af732203SDimitry Andric }
124*af732203SDimitry Andric wp_index = LLDB_INVALID_INDEX32;
125*af732203SDimitry Andric return Status();
126*af732203SDimitry Andric }
127*af732203SDimitry Andric
IsWatchpointVacant(uint32_t wp_index,bool & is_vacant)128*af732203SDimitry Andric Status NativeRegisterContextDBReg_x86::IsWatchpointVacant(uint32_t wp_index,
129*af732203SDimitry Andric bool &is_vacant) {
130*af732203SDimitry Andric if (wp_index >= NumSupportedHardwareWatchpoints())
131*af732203SDimitry Andric return Status("Watchpoint index out of range");
132*af732203SDimitry Andric
133*af732203SDimitry Andric RegisterValue dr7;
134*af732203SDimitry Andric Status error = ReadRegister(GetDR(7), dr7);
135*af732203SDimitry Andric if (error.Fail())
136*af732203SDimitry Andric is_vacant = false;
137*af732203SDimitry Andric else
138*af732203SDimitry Andric is_vacant = !(dr7.GetAsUInt64() & GetEnableBit(wp_index));
139*af732203SDimitry Andric
140*af732203SDimitry Andric return error;
141*af732203SDimitry Andric }
142*af732203SDimitry Andric
SetHardwareWatchpointWithIndex(lldb::addr_t addr,size_t size,uint32_t watch_flags,uint32_t wp_index)143*af732203SDimitry Andric Status NativeRegisterContextDBReg_x86::SetHardwareWatchpointWithIndex(
144*af732203SDimitry Andric lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) {
145*af732203SDimitry Andric
146*af732203SDimitry Andric if (wp_index >= NumSupportedHardwareWatchpoints())
147*af732203SDimitry Andric return Status("Watchpoint index out of range");
148*af732203SDimitry Andric
149*af732203SDimitry Andric // Read only watchpoints aren't supported on x86_64. Fall back to read/write
150*af732203SDimitry Andric // waitchpoints instead.
151*af732203SDimitry Andric // TODO: Add logic to detect when a write happens and ignore that watchpoint
152*af732203SDimitry Andric // hit.
153*af732203SDimitry Andric if (watch_flags == 2)
154*af732203SDimitry Andric watch_flags = 3;
155*af732203SDimitry Andric
156*af732203SDimitry Andric if (watch_flags != 1 && watch_flags != 3)
157*af732203SDimitry Andric return Status("Invalid read/write bits for watchpoint");
158*af732203SDimitry Andric if (size != 1 && size != 2 && size != 4 && size != 8)
159*af732203SDimitry Andric return Status("Invalid size for watchpoint");
160*af732203SDimitry Andric
161*af732203SDimitry Andric bool is_vacant;
162*af732203SDimitry Andric Status error = IsWatchpointVacant(wp_index, is_vacant);
163*af732203SDimitry Andric if (error.Fail())
164*af732203SDimitry Andric return error;
165*af732203SDimitry Andric if (!is_vacant)
166*af732203SDimitry Andric return Status("Watchpoint index not vacant");
167*af732203SDimitry Andric
168*af732203SDimitry Andric RegisterValue dr7, drN;
169*af732203SDimitry Andric error = ReadRegister(GetDR(7), dr7);
170*af732203SDimitry Andric if (error.Fail())
171*af732203SDimitry Andric return error;
172*af732203SDimitry Andric error = ReadRegister(GetDR(wp_index), drN);
173*af732203SDimitry Andric if (error.Fail())
174*af732203SDimitry Andric return error;
175*af732203SDimitry Andric
176*af732203SDimitry Andric uint64_t control_bits = dr7.GetAsUInt64() & ~GetWatchControlBitmask(wp_index);
177*af732203SDimitry Andric control_bits |= GetEnableBit(wp_index) |
178*af732203SDimitry Andric GetWatchTypeBits(watch_flags, wp_index) |
179*af732203SDimitry Andric GetWatchSizeBits(size, wp_index);
180*af732203SDimitry Andric
181*af732203SDimitry Andric // Clear dr6 if address or bits changed (i.e. we're not reenabling the same
182*af732203SDimitry Andric // watchpoint). This can not be done when clearing watchpoints since
183*af732203SDimitry Andric // the gdb-remote protocol repeatedly clears and readds watchpoints on all
184*af732203SDimitry Andric // program threads, effectively clearing pending events on NetBSD.
185*af732203SDimitry Andric // NB: enable bits in dr7 are always 0 here since we're (re)adding it
186*af732203SDimitry Andric if (drN.GetAsUInt64() != addr ||
187*af732203SDimitry Andric (dr7.GetAsUInt64() & GetWatchControlBitmask(wp_index)) !=
188*af732203SDimitry Andric (GetWatchTypeBits(watch_flags, wp_index) |
189*af732203SDimitry Andric GetWatchSizeBits(size, wp_index))) {
190*af732203SDimitry Andric ClearWatchpointHit(wp_index);
191*af732203SDimitry Andric
192*af732203SDimitry Andric // We skip update to drN if neither address nor mode changed.
193*af732203SDimitry Andric error = WriteRegister(GetDR(wp_index), RegisterValue(addr));
194*af732203SDimitry Andric if (error.Fail())
195*af732203SDimitry Andric return error;
196*af732203SDimitry Andric }
197*af732203SDimitry Andric
198*af732203SDimitry Andric error = WriteRegister(GetDR(7), RegisterValue(control_bits));
199*af732203SDimitry Andric if (error.Fail())
200*af732203SDimitry Andric return error;
201*af732203SDimitry Andric
202*af732203SDimitry Andric return error;
203*af732203SDimitry Andric }
204*af732203SDimitry Andric
ClearHardwareWatchpoint(uint32_t wp_index)205*af732203SDimitry Andric bool NativeRegisterContextDBReg_x86::ClearHardwareWatchpoint(
206*af732203SDimitry Andric uint32_t wp_index) {
207*af732203SDimitry Andric if (wp_index >= NumSupportedHardwareWatchpoints())
208*af732203SDimitry Andric return false;
209*af732203SDimitry Andric
210*af732203SDimitry Andric RegisterValue dr7;
211*af732203SDimitry Andric Status error = ReadRegister(GetDR(7), dr7);
212*af732203SDimitry Andric if (error.Fail())
213*af732203SDimitry Andric return false;
214*af732203SDimitry Andric
215*af732203SDimitry Andric return WriteRegister(GetDR(7), RegisterValue(dr7.GetAsUInt64() &
216*af732203SDimitry Andric ~GetBothEnableBitMask(wp_index)))
217*af732203SDimitry Andric .Success();
218*af732203SDimitry Andric }
219*af732203SDimitry Andric
ClearWatchpointHit(uint32_t wp_index)220*af732203SDimitry Andric Status NativeRegisterContextDBReg_x86::ClearWatchpointHit(uint32_t wp_index) {
221*af732203SDimitry Andric if (wp_index >= NumSupportedHardwareWatchpoints())
222*af732203SDimitry Andric return Status("Watchpoint index out of range");
223*af732203SDimitry Andric
224*af732203SDimitry Andric RegisterValue dr6;
225*af732203SDimitry Andric Status error = ReadRegister(GetDR(6), dr6);
226*af732203SDimitry Andric if (error.Fail())
227*af732203SDimitry Andric return error;
228*af732203SDimitry Andric
229*af732203SDimitry Andric return WriteRegister(
230*af732203SDimitry Andric GetDR(6), RegisterValue(dr6.GetAsUInt64() & ~GetStatusBit(wp_index)));
231*af732203SDimitry Andric }
232*af732203SDimitry Andric
ClearAllHardwareWatchpoints()233*af732203SDimitry Andric Status NativeRegisterContextDBReg_x86::ClearAllHardwareWatchpoints() {
234*af732203SDimitry Andric RegisterValue dr7;
235*af732203SDimitry Andric Status error = ReadRegister(GetDR(7), dr7);
236*af732203SDimitry Andric if (error.Fail())
237*af732203SDimitry Andric return error;
238*af732203SDimitry Andric return WriteRegister(
239*af732203SDimitry Andric GetDR(7),
240*af732203SDimitry Andric RegisterValue(dr7.GetAsUInt64() & ~watchpoint_all_control_bit_mask));
241*af732203SDimitry Andric }
242*af732203SDimitry Andric
SetHardwareWatchpoint(lldb::addr_t addr,size_t size,uint32_t watch_flags)243*af732203SDimitry Andric uint32_t NativeRegisterContextDBReg_x86::SetHardwareWatchpoint(
244*af732203SDimitry Andric lldb::addr_t addr, size_t size, uint32_t watch_flags) {
245*af732203SDimitry Andric Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
246*af732203SDimitry Andric const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
247*af732203SDimitry Andric for (uint32_t wp_index = 0; wp_index < num_hw_watchpoints; ++wp_index) {
248*af732203SDimitry Andric bool is_vacant;
249*af732203SDimitry Andric Status error = IsWatchpointVacant(wp_index, is_vacant);
250*af732203SDimitry Andric if (is_vacant) {
251*af732203SDimitry Andric error = SetHardwareWatchpointWithIndex(addr, size, watch_flags, wp_index);
252*af732203SDimitry Andric if (error.Success())
253*af732203SDimitry Andric return wp_index;
254*af732203SDimitry Andric }
255*af732203SDimitry Andric if (error.Fail() && log) {
256*af732203SDimitry Andric LLDB_LOGF(log, "NativeRegisterContextDBReg_x86::%s Error: %s",
257*af732203SDimitry Andric __FUNCTION__, error.AsCString());
258*af732203SDimitry Andric }
259*af732203SDimitry Andric }
260*af732203SDimitry Andric return LLDB_INVALID_INDEX32;
261*af732203SDimitry Andric }
262*af732203SDimitry Andric
263*af732203SDimitry Andric lldb::addr_t
GetWatchpointAddress(uint32_t wp_index)264*af732203SDimitry Andric NativeRegisterContextDBReg_x86::GetWatchpointAddress(uint32_t wp_index) {
265*af732203SDimitry Andric if (wp_index >= NumSupportedHardwareWatchpoints())
266*af732203SDimitry Andric return LLDB_INVALID_ADDRESS;
267*af732203SDimitry Andric RegisterValue drN;
268*af732203SDimitry Andric if (ReadRegister(GetDR(wp_index), drN).Fail())
269*af732203SDimitry Andric return LLDB_INVALID_ADDRESS;
270*af732203SDimitry Andric return drN.GetAsUInt64();
271*af732203SDimitry Andric }
272*af732203SDimitry Andric
NumSupportedHardwareWatchpoints()273*af732203SDimitry Andric uint32_t NativeRegisterContextDBReg_x86::NumSupportedHardwareWatchpoints() {
274*af732203SDimitry Andric // Available debug address registers: dr0, dr1, dr2, dr3
275*af732203SDimitry Andric return 4;
276*af732203SDimitry Andric }
277