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