xref: /openbsd-src/gnu/llvm/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_s390x.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
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 llvm::Expected<ArchSpec>
DetermineArchitecture(lldb::tid_t tid)100 NativeRegisterContextLinux::DetermineArchitecture(lldb::tid_t tid) {
101   return HostInfo::GetArchitecture();
102 }
103 
104 // NativeRegisterContextLinux_s390x members.
105 
106 static RegisterInfoInterface *
CreateRegisterInfoInterface(const ArchSpec & target_arch)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 
NativeRegisterContextLinux_s390x(const ArchSpec & target_arch,NativeThreadProtocol & native_thread)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 
GetRegisterSetCount() const137 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 
GetUserRegisterCount() const147 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 *
GetRegisterSet(uint32_t set_index) const158 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 
IsRegisterSetAvailable(uint32_t set_index) const173 bool NativeRegisterContextLinux_s390x::IsRegisterSetAvailable(
174     uint32_t set_index) const {
175   return set_index < k_num_register_sets;
176 }
177 
IsGPR(uint32_t reg_index) const178 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 
IsFPR(uint32_t reg_index) const184 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
ReadRegister(const RegisterInfo * reg_info,RegisterValue & reg_value)190 NativeRegisterContextLinux_s390x::ReadRegister(const RegisterInfo *reg_info,
191                                                RegisterValue &reg_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 
WriteRegister(const RegisterInfo * reg_info,const RegisterValue & reg_value)267 Status NativeRegisterContextLinux_s390x::WriteRegister(
268     const RegisterInfo *reg_info, const RegisterValue &reg_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 
ReadAllRegisterValues(lldb::WritableDataBufferSP & data_sp)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 
WriteAllRegisterValues(const lldb::DataBufferSP & data_sp)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 
DoReadRegisterValue(uint32_t offset,const char * reg_name,uint32_t size,RegisterValue & value)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 
DoWriteRegisterValue(uint32_t offset,const char * reg_name,const RegisterValue & value)418 Status NativeRegisterContextLinux_s390x::DoWriteRegisterValue(
419     uint32_t offset, const char *reg_name, const RegisterValue &value) {
420   return Status("DoWriteRegisterValue unsupported");
421 }
422 
PeekUserArea(uint32_t offset,void * buf,size_t buf_size)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 
PokeUserArea(uint32_t offset,const void * buf,size_t buf_size)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 
ReadGPR()447 Status NativeRegisterContextLinux_s390x::ReadGPR() {
448   return PeekUserArea(offsetof(user_regs_struct, psw), GetGPRBuffer(),
449                       GetGPRSize());
450 }
451 
WriteGPR()452 Status NativeRegisterContextLinux_s390x::WriteGPR() {
453   return PokeUserArea(offsetof(user_regs_struct, psw), GetGPRBuffer(),
454                       GetGPRSize());
455 }
456 
ReadFPR()457 Status NativeRegisterContextLinux_s390x::ReadFPR() {
458   return PeekUserArea(offsetof(user_regs_struct, fp_regs), GetGPRBuffer(),
459                       GetGPRSize());
460 }
461 
WriteFPR()462 Status NativeRegisterContextLinux_s390x::WriteFPR() {
463   return PokeUserArea(offsetof(user_regs_struct, fp_regs), GetGPRBuffer(),
464                       GetGPRSize());
465 }
466 
DoReadRegisterSet(uint32_t regset,void * buf,size_t buf_size)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 
DoWriteRegisterSet(uint32_t regset,const void * buf,size_t buf_size)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 
IsWatchpointHit(uint32_t wp_index,bool & is_hit)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 
GetWatchpointHitIndex(uint32_t & wp_index,lldb::addr_t trap_addr)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 
IsWatchpointVacant(uint32_t wp_index,bool & is_vacant)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 
ClearHardwareWatchpoint(uint32_t wp_index)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 
ClearAllHardwareWatchpoints()572 Status NativeRegisterContextLinux_s390x::ClearAllHardwareWatchpoints() {
573   if (ClearHardwareWatchpoint(0))
574     return Status();
575   return Status("Clearing all hardware watchpoints failed.");
576 }
577 
SetHardwareWatchpoint(lldb::addr_t addr,size_t size,uint32_t watch_flags)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
GetWatchpointAddress(uint32_t wp_index)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 
NumSupportedHardwareWatchpoints()614 uint32_t NativeRegisterContextLinux_s390x::NumSupportedHardwareWatchpoints() {
615   return 1;
616 }
617 
618 #endif // defined(__s390x__) && defined(__linux__)
619