1 | //===-- NativeRegisterContextLinux_s390x.cpp ------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #if defined(__s390x__) && defined(__linux__) |
10 | |
11 | #include "NativeRegisterContextLinux_s390x.h" |
12 | #include "Plugins/Process/Linux/NativeProcessLinux.h" |
13 | #include "Plugins/Process/Utility/RegisterContextLinux_s390x.h" |
14 | #include "lldb/Host/HostInfo.h" |
15 | #include "lldb/Utility/DataBufferHeap.h" |
16 | #include "lldb/Utility/Log.h" |
17 | #include "lldb/Utility/RegisterValue.h" |
18 | #include "lldb/Utility/Status.h" |
19 | #include <sys/ptrace.h> |
20 | #include <sys/uio.h> |
21 | |
22 | using namespace lldb_private; |
23 | using namespace lldb_private::process_linux; |
24 | |
25 | // Private namespace. |
26 | |
27 | namespace { |
28 | // s390x 64-bit general purpose registers. |
29 | static const uint32_t g_gpr_regnums_s390x[] = { |
30 | lldb_r0_s390x, lldb_r1_s390x, lldb_r2_s390x, lldb_r3_s390x, |
31 | lldb_r4_s390x, lldb_r5_s390x, lldb_r6_s390x, lldb_r7_s390x, |
32 | lldb_r8_s390x, lldb_r9_s390x, lldb_r10_s390x, lldb_r11_s390x, |
33 | lldb_r12_s390x, lldb_r13_s390x, lldb_r14_s390x, lldb_r15_s390x, |
34 | lldb_acr0_s390x, lldb_acr1_s390x, lldb_acr2_s390x, lldb_acr3_s390x, |
35 | lldb_acr4_s390x, lldb_acr5_s390x, lldb_acr6_s390x, lldb_acr7_s390x, |
36 | lldb_acr8_s390x, lldb_acr9_s390x, lldb_acr10_s390x, lldb_acr11_s390x, |
37 | lldb_acr12_s390x, lldb_acr13_s390x, lldb_acr14_s390x, lldb_acr15_s390x, |
38 | lldb_pswm_s390x, lldb_pswa_s390x, |
39 | LLDB_INVALID_REGNUM // register sets need to end with this flag |
40 | }; |
41 | static_assert((sizeof(g_gpr_regnums_s390x) / sizeof(g_gpr_regnums_s390x[0])) - |
42 | 1 == |
43 | k_num_gpr_registers_s390x, |
44 | "g_gpr_regnums_s390x has wrong number of register infos" ); |
45 | |
46 | // s390x 64-bit floating point registers. |
47 | static const uint32_t g_fpu_regnums_s390x[] = { |
48 | lldb_f0_s390x, lldb_f1_s390x, lldb_f2_s390x, lldb_f3_s390x, |
49 | lldb_f4_s390x, lldb_f5_s390x, lldb_f6_s390x, lldb_f7_s390x, |
50 | lldb_f8_s390x, lldb_f9_s390x, lldb_f10_s390x, lldb_f11_s390x, |
51 | lldb_f12_s390x, lldb_f13_s390x, lldb_f14_s390x, lldb_f15_s390x, |
52 | lldb_fpc_s390x, |
53 | LLDB_INVALID_REGNUM // register sets need to end with this flag |
54 | }; |
55 | static_assert((sizeof(g_fpu_regnums_s390x) / sizeof(g_fpu_regnums_s390x[0])) - |
56 | 1 == |
57 | k_num_fpr_registers_s390x, |
58 | "g_fpu_regnums_s390x has wrong number of register infos" ); |
59 | |
60 | // s390x Linux operating-system information. |
61 | static const uint32_t g_linux_regnums_s390x[] = { |
62 | lldb_orig_r2_s390x, lldb_last_break_s390x, lldb_system_call_s390x, |
63 | LLDB_INVALID_REGNUM // register sets need to end with this flag |
64 | }; |
65 | static_assert((sizeof(g_linux_regnums_s390x) / |
66 | sizeof(g_linux_regnums_s390x[0])) - |
67 | 1 == |
68 | k_num_linux_registers_s390x, |
69 | "g_linux_regnums_s390x has wrong number of register infos" ); |
70 | |
71 | // Number of register sets provided by this context. |
72 | enum { k_num_register_sets = 3 }; |
73 | |
74 | // Register sets for s390x 64-bit. |
75 | static const RegisterSet g_reg_sets_s390x[k_num_register_sets] = { |
76 | {"General Purpose Registers" , "gpr" , k_num_gpr_registers_s390x, |
77 | g_gpr_regnums_s390x}, |
78 | {"Floating Point Registers" , "fpr" , k_num_fpr_registers_s390x, |
79 | g_fpu_regnums_s390x}, |
80 | {"Linux Operating System Data" , "linux" , k_num_linux_registers_s390x, |
81 | g_linux_regnums_s390x}, |
82 | }; |
83 | } |
84 | |
85 | #define REG_CONTEXT_SIZE (sizeof(s390_regs) + sizeof(s390_fp_regs) + 4) |
86 | |
87 | // Required ptrace defines. |
88 | |
89 | #define NT_S390_LAST_BREAK 0x306 /* s390 breaking event address */ |
90 | #define NT_S390_SYSTEM_CALL 0x307 /* s390 system call restart data */ |
91 | |
92 | std::unique_ptr<NativeRegisterContextLinux> |
93 | NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux( |
94 | const ArchSpec &target_arch, NativeThreadLinux &native_thread) { |
95 | return std::make_unique<NativeRegisterContextLinux_s390x>(target_arch, |
96 | native_thread); |
97 | } |
98 | |
99 | llvm::Expected<ArchSpec> |
100 | NativeRegisterContextLinux::DetermineArchitecture(lldb::tid_t tid) { |
101 | return HostInfo::GetArchitecture(); |
102 | } |
103 | |
104 | // NativeRegisterContextLinux_s390x members. |
105 | |
106 | static RegisterInfoInterface * |
107 | CreateRegisterInfoInterface(const ArchSpec &target_arch) { |
108 | assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) && |
109 | "Register setting path assumes this is a 64-bit host" ); |
110 | return new RegisterContextLinux_s390x(target_arch); |
111 | } |
112 | |
113 | NativeRegisterContextLinux_s390x::NativeRegisterContextLinux_s390x( |
114 | const ArchSpec &target_arch, NativeThreadProtocol &native_thread) |
115 | : NativeRegisterContextRegisterInfo( |
116 | native_thread, CreateRegisterInfoInterface(target_arch)), |
117 | NativeRegisterContextLinux(native_thread) { |
118 | // Set up data about ranges of valid registers. |
119 | switch (target_arch.GetMachine()) { |
120 | case llvm::Triple::systemz: |
121 | m_reg_info.num_registers = k_num_registers_s390x; |
122 | m_reg_info.num_gpr_registers = k_num_gpr_registers_s390x; |
123 | m_reg_info.num_fpr_registers = k_num_fpr_registers_s390x; |
124 | m_reg_info.last_gpr = k_last_gpr_s390x; |
125 | m_reg_info.first_fpr = k_first_fpr_s390x; |
126 | m_reg_info.last_fpr = k_last_fpr_s390x; |
127 | break; |
128 | default: |
129 | assert(false && "Unhandled target architecture." ); |
130 | break; |
131 | } |
132 | |
133 | // Clear out the watchpoint state. |
134 | m_watchpoint_addr = LLDB_INVALID_ADDRESS; |
135 | } |
136 | |
137 | uint32_t NativeRegisterContextLinux_s390x::GetRegisterSetCount() const { |
138 | uint32_t sets = 0; |
139 | for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) { |
140 | if (IsRegisterSetAvailable(set_index)) |
141 | ++sets; |
142 | } |
143 | |
144 | return sets; |
145 | } |
146 | |
147 | uint32_t NativeRegisterContextLinux_s390x::GetUserRegisterCount() const { |
148 | uint32_t count = 0; |
149 | for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) { |
150 | const RegisterSet *set = GetRegisterSet(set_index); |
151 | if (set) |
152 | count += set->num_registers; |
153 | } |
154 | return count; |
155 | } |
156 | |
157 | const RegisterSet * |
158 | NativeRegisterContextLinux_s390x::GetRegisterSet(uint32_t set_index) const { |
159 | if (!IsRegisterSetAvailable(set_index)) |
160 | return nullptr; |
161 | |
162 | switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { |
163 | case llvm::Triple::systemz: |
164 | return &g_reg_sets_s390x[set_index]; |
165 | default: |
166 | assert(false && "Unhandled target architecture." ); |
167 | return nullptr; |
168 | } |
169 | |
170 | return nullptr; |
171 | } |
172 | |
173 | bool NativeRegisterContextLinux_s390x::IsRegisterSetAvailable( |
174 | uint32_t set_index) const { |
175 | return set_index < k_num_register_sets; |
176 | } |
177 | |
178 | bool NativeRegisterContextLinux_s390x::IsGPR(uint32_t reg_index) const { |
179 | // GPRs come first. "orig_r2" counts as GPR since it is part of the GPR |
180 | // register area. |
181 | return reg_index <= m_reg_info.last_gpr || reg_index == lldb_orig_r2_s390x; |
182 | } |
183 | |
184 | bool NativeRegisterContextLinux_s390x::IsFPR(uint32_t reg_index) const { |
185 | return (m_reg_info.first_fpr <= reg_index && |
186 | reg_index <= m_reg_info.last_fpr); |
187 | } |
188 | |
189 | Status |
190 | NativeRegisterContextLinux_s390x::ReadRegister(const RegisterInfo *reg_info, |
191 | RegisterValue ®_value) { |
192 | if (!reg_info) |
193 | return Status("reg_info NULL" ); |
194 | |
195 | const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; |
196 | if (reg == LLDB_INVALID_REGNUM) |
197 | return Status("register \"%s\" is an internal-only lldb register, cannot " |
198 | "read directly" , |
199 | reg_info->name); |
200 | |
201 | if (IsGPR(reg)) { |
202 | Status error = ReadGPR(); |
203 | if (error.Fail()) |
204 | return error; |
205 | |
206 | uint8_t *src = (uint8_t *)&m_regs + reg_info->byte_offset; |
207 | assert(reg_info->byte_offset + reg_info->byte_size <= sizeof(m_regs)); |
208 | switch (reg_info->byte_size) { |
209 | case 4: |
210 | reg_value.SetUInt32(*(uint32_t *)src); |
211 | break; |
212 | case 8: |
213 | reg_value.SetUInt64(*(uint64_t *)src); |
214 | break; |
215 | default: |
216 | assert(false && "Unhandled data size." ); |
217 | return Status("unhandled byte size: %" PRIu32, reg_info->byte_size); |
218 | } |
219 | return Status(); |
220 | } |
221 | |
222 | if (IsFPR(reg)) { |
223 | Status error = ReadFPR(); |
224 | if (error.Fail()) |
225 | return error; |
226 | |
227 | // byte_offset is just the offset within FPR, not the whole user area. |
228 | uint8_t *src = (uint8_t *)&m_fp_regs + reg_info->byte_offset; |
229 | assert(reg_info->byte_offset + reg_info->byte_size <= sizeof(m_fp_regs)); |
230 | switch (reg_info->byte_size) { |
231 | case 4: |
232 | reg_value.SetUInt32(*(uint32_t *)src); |
233 | break; |
234 | case 8: |
235 | reg_value.SetUInt64(*(uint64_t *)src); |
236 | break; |
237 | default: |
238 | assert(false && "Unhandled data size." ); |
239 | return Status("unhandled byte size: %" PRIu32, reg_info->byte_size); |
240 | } |
241 | return Status(); |
242 | } |
243 | |
244 | if (reg == lldb_last_break_s390x) { |
245 | uint64_t last_break; |
246 | Status error = DoReadRegisterSet(NT_S390_LAST_BREAK, &last_break, 8); |
247 | if (error.Fail()) |
248 | return error; |
249 | |
250 | reg_value.SetUInt64(last_break); |
251 | return Status(); |
252 | } |
253 | |
254 | if (reg == lldb_system_call_s390x) { |
255 | uint32_t system_call; |
256 | Status error = DoReadRegisterSet(NT_S390_SYSTEM_CALL, &system_call, 4); |
257 | if (error.Fail()) |
258 | return error; |
259 | |
260 | reg_value.SetUInt32(system_call); |
261 | return Status(); |
262 | } |
263 | |
264 | return Status("failed - register wasn't recognized" ); |
265 | } |
266 | |
267 | Status NativeRegisterContextLinux_s390x::WriteRegister( |
268 | const RegisterInfo *reg_info, const RegisterValue ®_value) { |
269 | if (!reg_info) |
270 | return Status("reg_info NULL" ); |
271 | |
272 | const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; |
273 | if (reg == LLDB_INVALID_REGNUM) |
274 | return Status("register \"%s\" is an internal-only lldb register, cannot " |
275 | "write directly" , |
276 | reg_info->name); |
277 | |
278 | if (IsGPR(reg)) { |
279 | Status error = ReadGPR(); |
280 | if (error.Fail()) |
281 | return error; |
282 | |
283 | uint8_t *dst = (uint8_t *)&m_regs + reg_info->byte_offset; |
284 | assert(reg_info->byte_offset + reg_info->byte_size <= sizeof(m_regs)); |
285 | switch (reg_info->byte_size) { |
286 | case 4: |
287 | *(uint32_t *)dst = reg_value.GetAsUInt32(); |
288 | break; |
289 | case 8: |
290 | *(uint64_t *)dst = reg_value.GetAsUInt64(); |
291 | break; |
292 | default: |
293 | assert(false && "Unhandled data size." ); |
294 | return Status("unhandled byte size: %" PRIu32, reg_info->byte_size); |
295 | } |
296 | return WriteGPR(); |
297 | } |
298 | |
299 | if (IsFPR(reg)) { |
300 | Status error = ReadFPR(); |
301 | if (error.Fail()) |
302 | return error; |
303 | |
304 | // byte_offset is just the offset within fp_regs, not the whole user area. |
305 | uint8_t *dst = (uint8_t *)&m_fp_regs + reg_info->byte_offset; |
306 | assert(reg_info->byte_offset + reg_info->byte_size <= sizeof(m_fp_regs)); |
307 | switch (reg_info->byte_size) { |
308 | case 4: |
309 | *(uint32_t *)dst = reg_value.GetAsUInt32(); |
310 | break; |
311 | case 8: |
312 | *(uint64_t *)dst = reg_value.GetAsUInt64(); |
313 | break; |
314 | default: |
315 | assert(false && "Unhandled data size." ); |
316 | return Status("unhandled byte size: %" PRIu32, reg_info->byte_size); |
317 | } |
318 | return WriteFPR(); |
319 | } |
320 | |
321 | if (reg == lldb_last_break_s390x) { |
322 | return Status("The last break address is read-only" ); |
323 | } |
324 | |
325 | if (reg == lldb_system_call_s390x) { |
326 | uint32_t system_call = reg_value.GetAsUInt32(); |
327 | return DoWriteRegisterSet(NT_S390_SYSTEM_CALL, &system_call, 4); |
328 | } |
329 | |
330 | return Status("failed - register wasn't recognized" ); |
331 | } |
332 | |
333 | Status NativeRegisterContextLinux_s390x::ReadAllRegisterValues( |
334 | lldb::WritableDataBufferSP &data_sp) { |
335 | Status error; |
336 | |
337 | data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0)); |
338 | uint8_t *dst = data_sp->GetBytes(); |
339 | error = ReadGPR(); |
340 | if (error.Fail()) |
341 | return error; |
342 | memcpy(dst, GetGPRBuffer(), GetGPRSize()); |
343 | dst += GetGPRSize(); |
344 | |
345 | error = ReadFPR(); |
346 | if (error.Fail()) |
347 | return error; |
348 | memcpy(dst, GetFPRBuffer(), GetFPRSize()); |
349 | dst += GetFPRSize(); |
350 | |
351 | // Ignore errors if the regset is unsupported (happens on older kernels). |
352 | DoReadRegisterSet(NT_S390_SYSTEM_CALL, dst, 4); |
353 | dst += 4; |
354 | |
355 | // To enable inferior function calls while the process is stopped in an |
356 | // interrupted system call, we need to clear the system call flag. It will be |
357 | // restored to its original value by WriteAllRegisterValues. Again we ignore |
358 | // error if the regset is unsupported. |
359 | uint32_t system_call = 0; |
360 | DoWriteRegisterSet(NT_S390_SYSTEM_CALL, &system_call, 4); |
361 | |
362 | return error; |
363 | } |
364 | |
365 | Status NativeRegisterContextLinux_s390x::WriteAllRegisterValues( |
366 | const lldb::DataBufferSP &data_sp) { |
367 | Status error; |
368 | |
369 | if (!data_sp) { |
370 | error.SetErrorStringWithFormat( |
371 | "NativeRegisterContextLinux_s390x::%s invalid data_sp provided" , |
372 | __FUNCTION__); |
373 | return error; |
374 | } |
375 | |
376 | if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) { |
377 | error.SetErrorStringWithFormat( |
378 | "NativeRegisterContextLinux_s390x::%s data_sp contained mismatched " |
379 | "data size, expected %" PRIu64 ", actual %" PRIu64, |
380 | __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize()); |
381 | return error; |
382 | } |
383 | |
384 | const uint8_t *src = data_sp->GetBytes(); |
385 | if (src == nullptr) { |
386 | error.SetErrorStringWithFormat("NativeRegisterContextLinux_s390x::%s " |
387 | "DataBuffer::GetBytes() returned a null " |
388 | "pointer" , |
389 | __FUNCTION__); |
390 | return error; |
391 | } |
392 | |
393 | memcpy(GetGPRBuffer(), src, GetGPRSize()); |
394 | src += GetGPRSize(); |
395 | error = WriteGPR(); |
396 | if (error.Fail()) |
397 | return error; |
398 | |
399 | memcpy(GetFPRBuffer(), src, GetFPRSize()); |
400 | src += GetFPRSize(); |
401 | error = WriteFPR(); |
402 | if (error.Fail()) |
403 | return error; |
404 | |
405 | // Ignore errors if the regset is unsupported (happens on older kernels). |
406 | DoWriteRegisterSet(NT_S390_SYSTEM_CALL, src, 4); |
407 | src += 4; |
408 | |
409 | return error; |
410 | } |
411 | |
412 | Status NativeRegisterContextLinux_s390x::DoReadRegisterValue( |
413 | uint32_t offset, const char *reg_name, uint32_t size, |
414 | RegisterValue &value) { |
415 | return Status("DoReadRegisterValue unsupported" ); |
416 | } |
417 | |
418 | Status NativeRegisterContextLinux_s390x::DoWriteRegisterValue( |
419 | uint32_t offset, const char *reg_name, const RegisterValue &value) { |
420 | return Status("DoWriteRegisterValue unsupported" ); |
421 | } |
422 | |
423 | Status NativeRegisterContextLinux_s390x::PeekUserArea(uint32_t offset, |
424 | void *buf, |
425 | size_t buf_size) { |
426 | ptrace_area parea; |
427 | parea.len = buf_size; |
428 | parea.process_addr = (addr_t)buf; |
429 | parea.kernel_addr = offset; |
430 | |
431 | return NativeProcessLinux::PtraceWrapper(PTRACE_PEEKUSR_AREA, |
432 | m_thread.GetID(), &parea); |
433 | } |
434 | |
435 | Status NativeRegisterContextLinux_s390x::PokeUserArea(uint32_t offset, |
436 | const void *buf, |
437 | size_t buf_size) { |
438 | ptrace_area parea; |
439 | parea.len = buf_size; |
440 | parea.process_addr = (addr_t)buf; |
441 | parea.kernel_addr = offset; |
442 | |
443 | return NativeProcessLinux::PtraceWrapper(PTRACE_POKEUSR_AREA, |
444 | m_thread.GetID(), &parea); |
445 | } |
446 | |
447 | Status NativeRegisterContextLinux_s390x::ReadGPR() { |
448 | return PeekUserArea(offsetof(user_regs_struct, psw), GetGPRBuffer(), |
449 | GetGPRSize()); |
450 | } |
451 | |
452 | Status NativeRegisterContextLinux_s390x::WriteGPR() { |
453 | return PokeUserArea(offsetof(user_regs_struct, psw), GetGPRBuffer(), |
454 | GetGPRSize()); |
455 | } |
456 | |
457 | Status NativeRegisterContextLinux_s390x::ReadFPR() { |
458 | return PeekUserArea(offsetof(user_regs_struct, fp_regs), GetGPRBuffer(), |
459 | GetGPRSize()); |
460 | } |
461 | |
462 | Status NativeRegisterContextLinux_s390x::WriteFPR() { |
463 | return PokeUserArea(offsetof(user_regs_struct, fp_regs), GetGPRBuffer(), |
464 | GetGPRSize()); |
465 | } |
466 | |
467 | Status NativeRegisterContextLinux_s390x::DoReadRegisterSet(uint32_t regset, |
468 | void *buf, |
469 | size_t buf_size) { |
470 | struct iovec iov; |
471 | iov.iov_base = buf; |
472 | iov.iov_len = buf_size; |
473 | |
474 | return ReadRegisterSet(&iov, buf_size, regset); |
475 | } |
476 | |
477 | Status NativeRegisterContextLinux_s390x::DoWriteRegisterSet(uint32_t regset, |
478 | const void *buf, |
479 | size_t buf_size) { |
480 | struct iovec iov; |
481 | iov.iov_base = const_cast<void *>(buf); |
482 | iov.iov_len = buf_size; |
483 | |
484 | return WriteRegisterSet(&iov, buf_size, regset); |
485 | } |
486 | |
487 | Status NativeRegisterContextLinux_s390x::IsWatchpointHit(uint32_t wp_index, |
488 | bool &is_hit) { |
489 | per_lowcore_bits per_lowcore; |
490 | |
491 | if (wp_index >= NumSupportedHardwareWatchpoints()) |
492 | return Status("Watchpoint index out of range" ); |
493 | |
494 | if (m_watchpoint_addr == LLDB_INVALID_ADDRESS) { |
495 | is_hit = false; |
496 | return Status(); |
497 | } |
498 | |
499 | Status error = PeekUserArea(offsetof(user_regs_struct, per_info.lowcore), |
500 | &per_lowcore, sizeof(per_lowcore)); |
501 | if (error.Fail()) { |
502 | is_hit = false; |
503 | return error; |
504 | } |
505 | |
506 | is_hit = (per_lowcore.perc_storage_alteration == 1 && |
507 | per_lowcore.perc_store_real_address == 0); |
508 | |
509 | if (is_hit) { |
510 | // Do not report this watchpoint again. |
511 | memset(&per_lowcore, 0, sizeof(per_lowcore)); |
512 | PokeUserArea(offsetof(user_regs_struct, per_info.lowcore), &per_lowcore, |
513 | sizeof(per_lowcore)); |
514 | } |
515 | |
516 | return Status(); |
517 | } |
518 | |
519 | Status NativeRegisterContextLinux_s390x::GetWatchpointHitIndex( |
520 | uint32_t &wp_index, lldb::addr_t trap_addr) { |
521 | uint32_t num_hw_wps = NumSupportedHardwareWatchpoints(); |
522 | for (wp_index = 0; wp_index < num_hw_wps; ++wp_index) { |
523 | bool is_hit; |
524 | Status error = IsWatchpointHit(wp_index, is_hit); |
525 | if (error.Fail()) { |
526 | wp_index = LLDB_INVALID_INDEX32; |
527 | return error; |
528 | } else if (is_hit) { |
529 | return error; |
530 | } |
531 | } |
532 | wp_index = LLDB_INVALID_INDEX32; |
533 | return Status(); |
534 | } |
535 | |
536 | Status NativeRegisterContextLinux_s390x::IsWatchpointVacant(uint32_t wp_index, |
537 | bool &is_vacant) { |
538 | if (wp_index >= NumSupportedHardwareWatchpoints()) |
539 | return Status("Watchpoint index out of range" ); |
540 | |
541 | is_vacant = m_watchpoint_addr == LLDB_INVALID_ADDRESS; |
542 | |
543 | return Status(); |
544 | } |
545 | |
546 | bool NativeRegisterContextLinux_s390x::ClearHardwareWatchpoint( |
547 | uint32_t wp_index) { |
548 | per_struct per_info; |
549 | |
550 | if (wp_index >= NumSupportedHardwareWatchpoints()) |
551 | return false; |
552 | |
553 | Status error = PeekUserArea(offsetof(user_regs_struct, per_info), &per_info, |
554 | sizeof(per_info)); |
555 | if (error.Fail()) |
556 | return false; |
557 | |
558 | per_info.control_regs.bits.em_storage_alteration = 0; |
559 | per_info.control_regs.bits.storage_alt_space_ctl = 0; |
560 | per_info.starting_addr = 0; |
561 | per_info.ending_addr = 0; |
562 | |
563 | error = PokeUserArea(offsetof(user_regs_struct, per_info), &per_info, |
564 | sizeof(per_info)); |
565 | if (error.Fail()) |
566 | return false; |
567 | |
568 | m_watchpoint_addr = LLDB_INVALID_ADDRESS; |
569 | return true; |
570 | } |
571 | |
572 | Status NativeRegisterContextLinux_s390x::ClearAllHardwareWatchpoints() { |
573 | if (ClearHardwareWatchpoint(0)) |
574 | return Status(); |
575 | return Status("Clearing all hardware watchpoints failed." ); |
576 | } |
577 | |
578 | uint32_t NativeRegisterContextLinux_s390x::SetHardwareWatchpoint( |
579 | lldb::addr_t addr, size_t size, uint32_t watch_flags) { |
580 | per_struct per_info; |
581 | |
582 | if (watch_flags != 0x1) |
583 | return LLDB_INVALID_INDEX32; |
584 | |
585 | if (m_watchpoint_addr != LLDB_INVALID_ADDRESS) |
586 | return LLDB_INVALID_INDEX32; |
587 | |
588 | Status error = PeekUserArea(offsetof(user_regs_struct, per_info), &per_info, |
589 | sizeof(per_info)); |
590 | if (error.Fail()) |
591 | return LLDB_INVALID_INDEX32; |
592 | |
593 | per_info.control_regs.bits.em_storage_alteration = 1; |
594 | per_info.control_regs.bits.storage_alt_space_ctl = 1; |
595 | per_info.starting_addr = addr; |
596 | per_info.ending_addr = addr + size - 1; |
597 | |
598 | error = PokeUserArea(offsetof(user_regs_struct, per_info), &per_info, |
599 | sizeof(per_info)); |
600 | if (error.Fail()) |
601 | return LLDB_INVALID_INDEX32; |
602 | |
603 | m_watchpoint_addr = addr; |
604 | return 0; |
605 | } |
606 | |
607 | lldb::addr_t |
608 | NativeRegisterContextLinux_s390x::GetWatchpointAddress(uint32_t wp_index) { |
609 | if (wp_index >= NumSupportedHardwareWatchpoints()) |
610 | return LLDB_INVALID_ADDRESS; |
611 | return m_watchpoint_addr; |
612 | } |
613 | |
614 | uint32_t NativeRegisterContextLinux_s390x::NumSupportedHardwareWatchpoints() { |
615 | return 1; |
616 | } |
617 | |
618 | #endif // defined(__s390x__) && defined(__linux__) |
619 | |