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>
CreateHostNativeRegisterContextLinux(const ArchSpec & target_arch,NativeThreadLinux & native_thread)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 // NativeRegisterContextLinux_s390x members.
100
101 static RegisterInfoInterface *
CreateRegisterInfoInterface(const ArchSpec & target_arch)102 CreateRegisterInfoInterface(const ArchSpec &target_arch) {
103 assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) &&
104 "Register setting path assumes this is a 64-bit host");
105 return new RegisterContextLinux_s390x(target_arch);
106 }
107
NativeRegisterContextLinux_s390x(const ArchSpec & target_arch,NativeThreadProtocol & native_thread)108 NativeRegisterContextLinux_s390x::NativeRegisterContextLinux_s390x(
109 const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
110 : NativeRegisterContextRegisterInfo(
111 native_thread, CreateRegisterInfoInterface(target_arch)),
112 NativeRegisterContextLinux(native_thread) {
113 // Set up data about ranges of valid registers.
114 switch (target_arch.GetMachine()) {
115 case llvm::Triple::systemz:
116 m_reg_info.num_registers = k_num_registers_s390x;
117 m_reg_info.num_gpr_registers = k_num_gpr_registers_s390x;
118 m_reg_info.num_fpr_registers = k_num_fpr_registers_s390x;
119 m_reg_info.last_gpr = k_last_gpr_s390x;
120 m_reg_info.first_fpr = k_first_fpr_s390x;
121 m_reg_info.last_fpr = k_last_fpr_s390x;
122 break;
123 default:
124 assert(false && "Unhandled target architecture.");
125 break;
126 }
127
128 // Clear out the watchpoint state.
129 m_watchpoint_addr = LLDB_INVALID_ADDRESS;
130 }
131
GetRegisterSetCount() const132 uint32_t NativeRegisterContextLinux_s390x::GetRegisterSetCount() const {
133 uint32_t sets = 0;
134 for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) {
135 if (IsRegisterSetAvailable(set_index))
136 ++sets;
137 }
138
139 return sets;
140 }
141
GetUserRegisterCount() const142 uint32_t NativeRegisterContextLinux_s390x::GetUserRegisterCount() const {
143 uint32_t count = 0;
144 for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) {
145 const RegisterSet *set = GetRegisterSet(set_index);
146 if (set)
147 count += set->num_registers;
148 }
149 return count;
150 }
151
152 const RegisterSet *
GetRegisterSet(uint32_t set_index) const153 NativeRegisterContextLinux_s390x::GetRegisterSet(uint32_t set_index) const {
154 if (!IsRegisterSetAvailable(set_index))
155 return nullptr;
156
157 switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) {
158 case llvm::Triple::systemz:
159 return &g_reg_sets_s390x[set_index];
160 default:
161 assert(false && "Unhandled target architecture.");
162 return nullptr;
163 }
164
165 return nullptr;
166 }
167
IsRegisterSetAvailable(uint32_t set_index) const168 bool NativeRegisterContextLinux_s390x::IsRegisterSetAvailable(
169 uint32_t set_index) const {
170 return set_index < k_num_register_sets;
171 }
172
IsGPR(uint32_t reg_index) const173 bool NativeRegisterContextLinux_s390x::IsGPR(uint32_t reg_index) const {
174 // GPRs come first. "orig_r2" counts as GPR since it is part of the GPR
175 // register area.
176 return reg_index <= m_reg_info.last_gpr || reg_index == lldb_orig_r2_s390x;
177 }
178
IsFPR(uint32_t reg_index) const179 bool NativeRegisterContextLinux_s390x::IsFPR(uint32_t reg_index) const {
180 return (m_reg_info.first_fpr <= reg_index &&
181 reg_index <= m_reg_info.last_fpr);
182 }
183
184 Status
ReadRegister(const RegisterInfo * reg_info,RegisterValue & reg_value)185 NativeRegisterContextLinux_s390x::ReadRegister(const RegisterInfo *reg_info,
186 RegisterValue ®_value) {
187 if (!reg_info)
188 return Status("reg_info NULL");
189
190 const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
191 if (reg == LLDB_INVALID_REGNUM)
192 return Status("register \"%s\" is an internal-only lldb register, cannot "
193 "read directly",
194 reg_info->name);
195
196 if (IsGPR(reg)) {
197 Status error = ReadGPR();
198 if (error.Fail())
199 return error;
200
201 uint8_t *src = (uint8_t *)&m_regs + reg_info->byte_offset;
202 assert(reg_info->byte_offset + reg_info->byte_size <= sizeof(m_regs));
203 switch (reg_info->byte_size) {
204 case 4:
205 reg_value.SetUInt32(*(uint32_t *)src);
206 break;
207 case 8:
208 reg_value.SetUInt64(*(uint64_t *)src);
209 break;
210 default:
211 assert(false && "Unhandled data size.");
212 return Status("unhandled byte size: %" PRIu32, reg_info->byte_size);
213 }
214 return Status();
215 }
216
217 if (IsFPR(reg)) {
218 Status error = ReadFPR();
219 if (error.Fail())
220 return error;
221
222 // byte_offset is just the offset within FPR, not the whole user area.
223 uint8_t *src = (uint8_t *)&m_fp_regs + reg_info->byte_offset;
224 assert(reg_info->byte_offset + reg_info->byte_size <= sizeof(m_fp_regs));
225 switch (reg_info->byte_size) {
226 case 4:
227 reg_value.SetUInt32(*(uint32_t *)src);
228 break;
229 case 8:
230 reg_value.SetUInt64(*(uint64_t *)src);
231 break;
232 default:
233 assert(false && "Unhandled data size.");
234 return Status("unhandled byte size: %" PRIu32, reg_info->byte_size);
235 }
236 return Status();
237 }
238
239 if (reg == lldb_last_break_s390x) {
240 uint64_t last_break;
241 Status error = DoReadRegisterSet(NT_S390_LAST_BREAK, &last_break, 8);
242 if (error.Fail())
243 return error;
244
245 reg_value.SetUInt64(last_break);
246 return Status();
247 }
248
249 if (reg == lldb_system_call_s390x) {
250 uint32_t system_call;
251 Status error = DoReadRegisterSet(NT_S390_SYSTEM_CALL, &system_call, 4);
252 if (error.Fail())
253 return error;
254
255 reg_value.SetUInt32(system_call);
256 return Status();
257 }
258
259 return Status("failed - register wasn't recognized");
260 }
261
WriteRegister(const RegisterInfo * reg_info,const RegisterValue & reg_value)262 Status NativeRegisterContextLinux_s390x::WriteRegister(
263 const RegisterInfo *reg_info, const RegisterValue ®_value) {
264 if (!reg_info)
265 return Status("reg_info NULL");
266
267 const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
268 if (reg == LLDB_INVALID_REGNUM)
269 return Status("register \"%s\" is an internal-only lldb register, cannot "
270 "write directly",
271 reg_info->name);
272
273 if (IsGPR(reg)) {
274 Status error = ReadGPR();
275 if (error.Fail())
276 return error;
277
278 uint8_t *dst = (uint8_t *)&m_regs + reg_info->byte_offset;
279 assert(reg_info->byte_offset + reg_info->byte_size <= sizeof(m_regs));
280 switch (reg_info->byte_size) {
281 case 4:
282 *(uint32_t *)dst = reg_value.GetAsUInt32();
283 break;
284 case 8:
285 *(uint64_t *)dst = reg_value.GetAsUInt64();
286 break;
287 default:
288 assert(false && "Unhandled data size.");
289 return Status("unhandled byte size: %" PRIu32, reg_info->byte_size);
290 }
291 return WriteGPR();
292 }
293
294 if (IsFPR(reg)) {
295 Status error = ReadFPR();
296 if (error.Fail())
297 return error;
298
299 // byte_offset is just the offset within fp_regs, not the whole user area.
300 uint8_t *dst = (uint8_t *)&m_fp_regs + reg_info->byte_offset;
301 assert(reg_info->byte_offset + reg_info->byte_size <= sizeof(m_fp_regs));
302 switch (reg_info->byte_size) {
303 case 4:
304 *(uint32_t *)dst = reg_value.GetAsUInt32();
305 break;
306 case 8:
307 *(uint64_t *)dst = reg_value.GetAsUInt64();
308 break;
309 default:
310 assert(false && "Unhandled data size.");
311 return Status("unhandled byte size: %" PRIu32, reg_info->byte_size);
312 }
313 return WriteFPR();
314 }
315
316 if (reg == lldb_last_break_s390x) {
317 return Status("The last break address is read-only");
318 }
319
320 if (reg == lldb_system_call_s390x) {
321 uint32_t system_call = reg_value.GetAsUInt32();
322 return DoWriteRegisterSet(NT_S390_SYSTEM_CALL, &system_call, 4);
323 }
324
325 return Status("failed - register wasn't recognized");
326 }
327
ReadAllRegisterValues(lldb::WritableDataBufferSP & data_sp)328 Status NativeRegisterContextLinux_s390x::ReadAllRegisterValues(
329 lldb::WritableDataBufferSP &data_sp) {
330 Status error;
331
332 data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0));
333 uint8_t *dst = data_sp->GetBytes();
334 error = ReadGPR();
335 if (error.Fail())
336 return error;
337 memcpy(dst, GetGPRBuffer(), GetGPRSize());
338 dst += GetGPRSize();
339
340 error = ReadFPR();
341 if (error.Fail())
342 return error;
343 memcpy(dst, GetFPRBuffer(), GetFPRSize());
344 dst += GetFPRSize();
345
346 // Ignore errors if the regset is unsupported (happens on older kernels).
347 DoReadRegisterSet(NT_S390_SYSTEM_CALL, dst, 4);
348 dst += 4;
349
350 // To enable inferior function calls while the process is stopped in an
351 // interrupted system call, we need to clear the system call flag. It will be
352 // restored to its original value by WriteAllRegisterValues. Again we ignore
353 // error if the regset is unsupported.
354 uint32_t system_call = 0;
355 DoWriteRegisterSet(NT_S390_SYSTEM_CALL, &system_call, 4);
356
357 return error;
358 }
359
WriteAllRegisterValues(const lldb::DataBufferSP & data_sp)360 Status NativeRegisterContextLinux_s390x::WriteAllRegisterValues(
361 const lldb::DataBufferSP &data_sp) {
362 Status error;
363
364 if (!data_sp) {
365 error.SetErrorStringWithFormat(
366 "NativeRegisterContextLinux_s390x::%s invalid data_sp provided",
367 __FUNCTION__);
368 return error;
369 }
370
371 if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) {
372 error.SetErrorStringWithFormat(
373 "NativeRegisterContextLinux_s390x::%s data_sp contained mismatched "
374 "data size, expected %" PRIu64 ", actual %" PRIu64,
375 __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize());
376 return error;
377 }
378
379 const uint8_t *src = data_sp->GetBytes();
380 if (src == nullptr) {
381 error.SetErrorStringWithFormat("NativeRegisterContextLinux_s390x::%s "
382 "DataBuffer::GetBytes() returned a null "
383 "pointer",
384 __FUNCTION__);
385 return error;
386 }
387
388 memcpy(GetGPRBuffer(), src, GetGPRSize());
389 src += GetGPRSize();
390 error = WriteGPR();
391 if (error.Fail())
392 return error;
393
394 memcpy(GetFPRBuffer(), src, GetFPRSize());
395 src += GetFPRSize();
396 error = WriteFPR();
397 if (error.Fail())
398 return error;
399
400 // Ignore errors if the regset is unsupported (happens on older kernels).
401 DoWriteRegisterSet(NT_S390_SYSTEM_CALL, src, 4);
402 src += 4;
403
404 return error;
405 }
406
DoReadRegisterValue(uint32_t offset,const char * reg_name,uint32_t size,RegisterValue & value)407 Status NativeRegisterContextLinux_s390x::DoReadRegisterValue(
408 uint32_t offset, const char *reg_name, uint32_t size,
409 RegisterValue &value) {
410 return Status("DoReadRegisterValue unsupported");
411 }
412
DoWriteRegisterValue(uint32_t offset,const char * reg_name,const RegisterValue & value)413 Status NativeRegisterContextLinux_s390x::DoWriteRegisterValue(
414 uint32_t offset, const char *reg_name, const RegisterValue &value) {
415 return Status("DoWriteRegisterValue unsupported");
416 }
417
PeekUserArea(uint32_t offset,void * buf,size_t buf_size)418 Status NativeRegisterContextLinux_s390x::PeekUserArea(uint32_t offset,
419 void *buf,
420 size_t buf_size) {
421 ptrace_area parea;
422 parea.len = buf_size;
423 parea.process_addr = (addr_t)buf;
424 parea.kernel_addr = offset;
425
426 return NativeProcessLinux::PtraceWrapper(PTRACE_PEEKUSR_AREA,
427 m_thread.GetID(), &parea);
428 }
429
PokeUserArea(uint32_t offset,const void * buf,size_t buf_size)430 Status NativeRegisterContextLinux_s390x::PokeUserArea(uint32_t offset,
431 const void *buf,
432 size_t buf_size) {
433 ptrace_area parea;
434 parea.len = buf_size;
435 parea.process_addr = (addr_t)buf;
436 parea.kernel_addr = offset;
437
438 return NativeProcessLinux::PtraceWrapper(PTRACE_POKEUSR_AREA,
439 m_thread.GetID(), &parea);
440 }
441
ReadGPR()442 Status NativeRegisterContextLinux_s390x::ReadGPR() {
443 return PeekUserArea(offsetof(user_regs_struct, psw), GetGPRBuffer(),
444 GetGPRSize());
445 }
446
WriteGPR()447 Status NativeRegisterContextLinux_s390x::WriteGPR() {
448 return PokeUserArea(offsetof(user_regs_struct, psw), GetGPRBuffer(),
449 GetGPRSize());
450 }
451
ReadFPR()452 Status NativeRegisterContextLinux_s390x::ReadFPR() {
453 return PeekUserArea(offsetof(user_regs_struct, fp_regs), GetGPRBuffer(),
454 GetGPRSize());
455 }
456
WriteFPR()457 Status NativeRegisterContextLinux_s390x::WriteFPR() {
458 return PokeUserArea(offsetof(user_regs_struct, fp_regs), GetGPRBuffer(),
459 GetGPRSize());
460 }
461
DoReadRegisterSet(uint32_t regset,void * buf,size_t buf_size)462 Status NativeRegisterContextLinux_s390x::DoReadRegisterSet(uint32_t regset,
463 void *buf,
464 size_t buf_size) {
465 struct iovec iov;
466 iov.iov_base = buf;
467 iov.iov_len = buf_size;
468
469 return ReadRegisterSet(&iov, buf_size, regset);
470 }
471
DoWriteRegisterSet(uint32_t regset,const void * buf,size_t buf_size)472 Status NativeRegisterContextLinux_s390x::DoWriteRegisterSet(uint32_t regset,
473 const void *buf,
474 size_t buf_size) {
475 struct iovec iov;
476 iov.iov_base = const_cast<void *>(buf);
477 iov.iov_len = buf_size;
478
479 return WriteRegisterSet(&iov, buf_size, regset);
480 }
481
IsWatchpointHit(uint32_t wp_index,bool & is_hit)482 Status NativeRegisterContextLinux_s390x::IsWatchpointHit(uint32_t wp_index,
483 bool &is_hit) {
484 per_lowcore_bits per_lowcore;
485
486 if (wp_index >= NumSupportedHardwareWatchpoints())
487 return Status("Watchpoint index out of range");
488
489 if (m_watchpoint_addr == LLDB_INVALID_ADDRESS) {
490 is_hit = false;
491 return Status();
492 }
493
494 Status error = PeekUserArea(offsetof(user_regs_struct, per_info.lowcore),
495 &per_lowcore, sizeof(per_lowcore));
496 if (error.Fail()) {
497 is_hit = false;
498 return error;
499 }
500
501 is_hit = (per_lowcore.perc_storage_alteration == 1 &&
502 per_lowcore.perc_store_real_address == 0);
503
504 if (is_hit) {
505 // Do not report this watchpoint again.
506 memset(&per_lowcore, 0, sizeof(per_lowcore));
507 PokeUserArea(offsetof(user_regs_struct, per_info.lowcore), &per_lowcore,
508 sizeof(per_lowcore));
509 }
510
511 return Status();
512 }
513
GetWatchpointHitIndex(uint32_t & wp_index,lldb::addr_t trap_addr)514 Status NativeRegisterContextLinux_s390x::GetWatchpointHitIndex(
515 uint32_t &wp_index, lldb::addr_t trap_addr) {
516 uint32_t num_hw_wps = NumSupportedHardwareWatchpoints();
517 for (wp_index = 0; wp_index < num_hw_wps; ++wp_index) {
518 bool is_hit;
519 Status error = IsWatchpointHit(wp_index, is_hit);
520 if (error.Fail()) {
521 wp_index = LLDB_INVALID_INDEX32;
522 return error;
523 } else if (is_hit) {
524 return error;
525 }
526 }
527 wp_index = LLDB_INVALID_INDEX32;
528 return Status();
529 }
530
IsWatchpointVacant(uint32_t wp_index,bool & is_vacant)531 Status NativeRegisterContextLinux_s390x::IsWatchpointVacant(uint32_t wp_index,
532 bool &is_vacant) {
533 if (wp_index >= NumSupportedHardwareWatchpoints())
534 return Status("Watchpoint index out of range");
535
536 is_vacant = m_watchpoint_addr == LLDB_INVALID_ADDRESS;
537
538 return Status();
539 }
540
ClearHardwareWatchpoint(uint32_t wp_index)541 bool NativeRegisterContextLinux_s390x::ClearHardwareWatchpoint(
542 uint32_t wp_index) {
543 per_struct per_info;
544
545 if (wp_index >= NumSupportedHardwareWatchpoints())
546 return false;
547
548 Status error = PeekUserArea(offsetof(user_regs_struct, per_info), &per_info,
549 sizeof(per_info));
550 if (error.Fail())
551 return false;
552
553 per_info.control_regs.bits.em_storage_alteration = 0;
554 per_info.control_regs.bits.storage_alt_space_ctl = 0;
555 per_info.starting_addr = 0;
556 per_info.ending_addr = 0;
557
558 error = PokeUserArea(offsetof(user_regs_struct, per_info), &per_info,
559 sizeof(per_info));
560 if (error.Fail())
561 return false;
562
563 m_watchpoint_addr = LLDB_INVALID_ADDRESS;
564 return true;
565 }
566
ClearAllHardwareWatchpoints()567 Status NativeRegisterContextLinux_s390x::ClearAllHardwareWatchpoints() {
568 if (ClearHardwareWatchpoint(0))
569 return Status();
570 return Status("Clearing all hardware watchpoints failed.");
571 }
572
SetHardwareWatchpoint(lldb::addr_t addr,size_t size,uint32_t watch_flags)573 uint32_t NativeRegisterContextLinux_s390x::SetHardwareWatchpoint(
574 lldb::addr_t addr, size_t size, uint32_t watch_flags) {
575 per_struct per_info;
576
577 if (watch_flags != 0x1)
578 return LLDB_INVALID_INDEX32;
579
580 if (m_watchpoint_addr != LLDB_INVALID_ADDRESS)
581 return LLDB_INVALID_INDEX32;
582
583 Status error = PeekUserArea(offsetof(user_regs_struct, per_info), &per_info,
584 sizeof(per_info));
585 if (error.Fail())
586 return LLDB_INVALID_INDEX32;
587
588 per_info.control_regs.bits.em_storage_alteration = 1;
589 per_info.control_regs.bits.storage_alt_space_ctl = 1;
590 per_info.starting_addr = addr;
591 per_info.ending_addr = addr + size - 1;
592
593 error = PokeUserArea(offsetof(user_regs_struct, per_info), &per_info,
594 sizeof(per_info));
595 if (error.Fail())
596 return LLDB_INVALID_INDEX32;
597
598 m_watchpoint_addr = addr;
599 return 0;
600 }
601
602 lldb::addr_t
GetWatchpointAddress(uint32_t wp_index)603 NativeRegisterContextLinux_s390x::GetWatchpointAddress(uint32_t wp_index) {
604 if (wp_index >= NumSupportedHardwareWatchpoints())
605 return LLDB_INVALID_ADDRESS;
606 return m_watchpoint_addr;
607 }
608
NumSupportedHardwareWatchpoints()609 uint32_t NativeRegisterContextLinux_s390x::NumSupportedHardwareWatchpoints() {
610 return 1;
611 }
612
613 #endif // defined(__s390x__) && defined(__linux__)
614