1 //===-- NativeRegisterContextLinux_s390x.cpp ------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #if defined(__s390x__) && defined(__linux__)
10 
11 #include "NativeRegisterContextLinux_s390x.h"
12 #include "Plugins/Process/Linux/NativeProcessLinux.h"
13 #include "Plugins/Process/Utility/RegisterContextLinux_s390x.h"
14 #include "lldb/Host/HostInfo.h"
15 #include "lldb/Utility/DataBufferHeap.h"
16 #include "lldb/Utility/Log.h"
17 #include "lldb/Utility/RegisterValue.h"
18 #include "lldb/Utility/Status.h"
19 #include <sys/ptrace.h>
20 #include <sys/uio.h>
21 
22 using namespace lldb_private;
23 using namespace lldb_private::process_linux;
24 
25 // Private namespace.
26 
27 namespace {
28 // s390x 64-bit general purpose registers.
29 static const uint32_t g_gpr_regnums_s390x[] = {
30     lldb_r0_s390x,      lldb_r1_s390x,    lldb_r2_s390x,    lldb_r3_s390x,
31     lldb_r4_s390x,      lldb_r5_s390x,    lldb_r6_s390x,    lldb_r7_s390x,
32     lldb_r8_s390x,      lldb_r9_s390x,    lldb_r10_s390x,   lldb_r11_s390x,
33     lldb_r12_s390x,     lldb_r13_s390x,   lldb_r14_s390x,   lldb_r15_s390x,
34     lldb_acr0_s390x,    lldb_acr1_s390x,  lldb_acr2_s390x,  lldb_acr3_s390x,
35     lldb_acr4_s390x,    lldb_acr5_s390x,  lldb_acr6_s390x,  lldb_acr7_s390x,
36     lldb_acr8_s390x,    lldb_acr9_s390x,  lldb_acr10_s390x, lldb_acr11_s390x,
37     lldb_acr12_s390x,   lldb_acr13_s390x, lldb_acr14_s390x, lldb_acr15_s390x,
38     lldb_pswm_s390x,    lldb_pswa_s390x,
39     LLDB_INVALID_REGNUM // register sets need to end with this flag
40 };
41 static_assert((sizeof(g_gpr_regnums_s390x) / sizeof(g_gpr_regnums_s390x[0])) -
42                       1 ==
43                   k_num_gpr_registers_s390x,
44               "g_gpr_regnums_s390x has wrong number of register infos");
45 
46 // s390x 64-bit floating point registers.
47 static const uint32_t g_fpu_regnums_s390x[] = {
48     lldb_f0_s390x,      lldb_f1_s390x,  lldb_f2_s390x,  lldb_f3_s390x,
49     lldb_f4_s390x,      lldb_f5_s390x,  lldb_f6_s390x,  lldb_f7_s390x,
50     lldb_f8_s390x,      lldb_f9_s390x,  lldb_f10_s390x, lldb_f11_s390x,
51     lldb_f12_s390x,     lldb_f13_s390x, lldb_f14_s390x, lldb_f15_s390x,
52     lldb_fpc_s390x,
53     LLDB_INVALID_REGNUM // register sets need to end with this flag
54 };
55 static_assert((sizeof(g_fpu_regnums_s390x) / sizeof(g_fpu_regnums_s390x[0])) -
56                       1 ==
57                   k_num_fpr_registers_s390x,
58               "g_fpu_regnums_s390x has wrong number of register infos");
59 
60 // s390x Linux operating-system information.
61 static const uint32_t g_linux_regnums_s390x[] = {
62     lldb_orig_r2_s390x, lldb_last_break_s390x, lldb_system_call_s390x,
63     LLDB_INVALID_REGNUM // register sets need to end with this flag
64 };
65 static_assert((sizeof(g_linux_regnums_s390x) /
66                sizeof(g_linux_regnums_s390x[0])) -
67                       1 ==
68                   k_num_linux_registers_s390x,
69               "g_linux_regnums_s390x has wrong number of register infos");
70 
71 // Number of register sets provided by this context.
72 enum { k_num_register_sets = 3 };
73 
74 // Register sets for s390x 64-bit.
75 static const RegisterSet g_reg_sets_s390x[k_num_register_sets] = {
76     {"General Purpose Registers", "gpr", k_num_gpr_registers_s390x,
77      g_gpr_regnums_s390x},
78     {"Floating Point Registers", "fpr", k_num_fpr_registers_s390x,
79      g_fpu_regnums_s390x},
80     {"Linux Operating System Data", "linux", k_num_linux_registers_s390x,
81      g_linux_regnums_s390x},
82 };
83 }
84 
85 #define REG_CONTEXT_SIZE (sizeof(s390_regs) + sizeof(s390_fp_regs) + 4)
86 
87 // Required ptrace defines.
88 
89 #define NT_S390_LAST_BREAK 0x306  /* s390 breaking event address */
90 #define NT_S390_SYSTEM_CALL 0x307 /* s390 system call restart data */
91 
92 std::unique_ptr<NativeRegisterContextLinux>
93 NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(
94     const ArchSpec &target_arch, NativeThreadLinux &native_thread) {
95   return std::make_unique<NativeRegisterContextLinux_s390x>(target_arch,
96                                                              native_thread);
97 }
98 
99 // NativeRegisterContextLinux_s390x members.
100 
101 static RegisterInfoInterface *
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 
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 
132 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 
142 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 *
153 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 
168 bool NativeRegisterContextLinux_s390x::IsRegisterSetAvailable(
169     uint32_t set_index) const {
170   return set_index < k_num_register_sets;
171 }
172 
173 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 
179 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
185 NativeRegisterContextLinux_s390x::ReadRegister(const RegisterInfo *reg_info,
186                                                RegisterValue &reg_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 
262 Status NativeRegisterContextLinux_s390x::WriteRegister(
263     const RegisterInfo *reg_info, const RegisterValue &reg_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 
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 
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 
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 
413 Status NativeRegisterContextLinux_s390x::DoWriteRegisterValue(
414     uint32_t offset, const char *reg_name, const RegisterValue &value) {
415   return Status("DoWriteRegisterValue unsupported");
416 }
417 
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 
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 
442 Status NativeRegisterContextLinux_s390x::ReadGPR() {
443   return PeekUserArea(offsetof(user_regs_struct, psw), GetGPRBuffer(),
444                       GetGPRSize());
445 }
446 
447 Status NativeRegisterContextLinux_s390x::WriteGPR() {
448   return PokeUserArea(offsetof(user_regs_struct, psw), GetGPRBuffer(),
449                       GetGPRSize());
450 }
451 
452 Status NativeRegisterContextLinux_s390x::ReadFPR() {
453   return PeekUserArea(offsetof(user_regs_struct, fp_regs), GetGPRBuffer(),
454                       GetGPRSize());
455 }
456 
457 Status NativeRegisterContextLinux_s390x::WriteFPR() {
458   return PokeUserArea(offsetof(user_regs_struct, fp_regs), GetGPRBuffer(),
459                       GetGPRSize());
460 }
461 
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 
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 
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 
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 
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 
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 
567 Status NativeRegisterContextLinux_s390x::ClearAllHardwareWatchpoints() {
568   if (ClearHardwareWatchpoint(0))
569     return Status();
570   return Status("Clearing all hardware watchpoints failed.");
571 }
572 
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
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 
609 uint32_t NativeRegisterContextLinux_s390x::NumSupportedHardwareWatchpoints() {
610   return 1;
611 }
612 
613 #endif // defined(__s390x__) && defined(__linux__)
614