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