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