1 //===-- NativeRegisterContextLinux_s390x.cpp ------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #if defined(__s390x__) && defined(__linux__)
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>
22 using namespace lldb_private
;
23 using namespace lldb_private::process_linux
;
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
41 static_assert((sizeof(g_gpr_regnums_s390x
) / sizeof(g_gpr_regnums_s390x
[0])) -
43 k_num_gpr_registers_s390x
,
44 "g_gpr_regnums_s390x has wrong number of register infos");
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
,
53 LLDB_INVALID_REGNUM
// register sets need to end with this flag
55 static_assert((sizeof(g_fpu_regnums_s390x
) / sizeof(g_fpu_regnums_s390x
[0])) -
57 k_num_fpr_registers_s390x
,
58 "g_fpu_regnums_s390x has wrong number of register infos");
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
65 static_assert((sizeof(g_linux_regnums_s390x
) /
66 sizeof(g_linux_regnums_s390x
[0])) -
68 k_num_linux_registers_s390x
,
69 "g_linux_regnums_s390x has wrong number of register infos");
71 // Number of register sets provided by this context.
72 enum { k_num_register_sets
= 3 };
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
,
78 {"Floating Point Registers", "fpr", k_num_fpr_registers_s390x
,
80 {"Linux Operating System Data", "linux", k_num_linux_registers_s390x
,
81 g_linux_regnums_s390x
},
85 #define REG_CONTEXT_SIZE (sizeof(s390_regs) + sizeof(s390_fp_regs) + 4)
87 // Required ptrace defines.
89 #define NT_S390_LAST_BREAK 0x306 /* s390 breaking event address */
90 #define NT_S390_SYSTEM_CALL 0x307 /* s390 system call restart data */
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
,
99 llvm::Expected
<ArchSpec
>
100 NativeRegisterContextLinux::DetermineArchitecture(lldb::tid_t tid
) {
101 return HostInfo::GetArchitecture();
104 // NativeRegisterContextLinux_s390x members.
106 static RegisterInfoInterface
*
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
);
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
;
129 assert(false && "Unhandled target architecture.");
133 // Clear out the watchpoint state.
134 m_watchpoint_addr
= LLDB_INVALID_ADDRESS
;
137 uint32_t NativeRegisterContextLinux_s390x::GetRegisterSetCount() const {
139 for (uint32_t set_index
= 0; set_index
< k_num_register_sets
; ++set_index
) {
140 if (IsRegisterSetAvailable(set_index
))
147 uint32_t NativeRegisterContextLinux_s390x::GetUserRegisterCount() const {
149 for (uint32_t set_index
= 0; set_index
< k_num_register_sets
; ++set_index
) {
150 const RegisterSet
*set
= GetRegisterSet(set_index
);
152 count
+= set
->num_registers
;
158 NativeRegisterContextLinux_s390x::GetRegisterSet(uint32_t set_index
) const {
159 if (!IsRegisterSetAvailable(set_index
))
162 switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) {
163 case llvm::Triple::systemz
:
164 return &g_reg_sets_s390x
[set_index
];
166 assert(false && "Unhandled target architecture.");
173 bool NativeRegisterContextLinux_s390x::IsRegisterSetAvailable(
174 uint32_t set_index
) const {
175 return set_index
< k_num_register_sets
;
178 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
181 return reg_index
<= m_reg_info
.last_gpr
|| reg_index
== lldb_orig_r2_s390x
;
184 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
);
190 NativeRegisterContextLinux_s390x::ReadRegister(const RegisterInfo
*reg_info
,
191 RegisterValue
®_value
) {
193 return Status::FromErrorString("reg_info NULL");
195 const uint32_t reg
= reg_info
->kinds
[lldb::eRegisterKindLLDB
];
196 if (reg
== LLDB_INVALID_REGNUM
)
197 return Status::FromErrorStringWithFormat(
198 "register \"%s\" is an internal-only lldb register, cannot "
203 Status error
= ReadGPR();
207 uint8_t *src
= (uint8_t *)&m_regs
+ reg_info
->byte_offset
;
208 assert(reg_info
->byte_offset
+ reg_info
->byte_size
<= sizeof(m_regs
));
209 switch (reg_info
->byte_size
) {
211 reg_value
.SetUInt32(*(uint32_t *)src
);
214 reg_value
.SetUInt64(*(uint64_t *)src
);
217 assert(false && "Unhandled data size.");
218 return Status::FromErrorStringWithFormat("unhandled byte size: %" PRIu32
,
219 reg_info
->byte_size
);
225 Status error
= ReadFPR();
229 // byte_offset is just the offset within FPR, not the whole user area.
230 uint8_t *src
= (uint8_t *)&m_fp_regs
+ reg_info
->byte_offset
;
231 assert(reg_info
->byte_offset
+ reg_info
->byte_size
<= sizeof(m_fp_regs
));
232 switch (reg_info
->byte_size
) {
234 reg_value
.SetUInt32(*(uint32_t *)src
);
237 reg_value
.SetUInt64(*(uint64_t *)src
);
240 assert(false && "Unhandled data size.");
241 return Status::FromErrorStringWithFormat("unhandled byte size: %" PRIu32
,
242 reg_info
->byte_size
);
247 if (reg
== lldb_last_break_s390x
) {
249 Status error
= DoReadRegisterSet(NT_S390_LAST_BREAK
, &last_break
, 8);
253 reg_value
.SetUInt64(last_break
);
257 if (reg
== lldb_system_call_s390x
) {
258 uint32_t system_call
;
259 Status error
= DoReadRegisterSet(NT_S390_SYSTEM_CALL
, &system_call
, 4);
263 reg_value
.SetUInt32(system_call
);
267 return Status::FromErrorString("failed - register wasn't recognized");
270 Status
NativeRegisterContextLinux_s390x::WriteRegister(
271 const RegisterInfo
*reg_info
, const RegisterValue
®_value
) {
273 return Status::FromErrorString("reg_info NULL");
275 const uint32_t reg
= reg_info
->kinds
[lldb::eRegisterKindLLDB
];
276 if (reg
== LLDB_INVALID_REGNUM
)
277 return Status::FromErrorStringWithFormat(
278 "register \"%s\" is an internal-only lldb register, cannot "
283 Status error
= ReadGPR();
287 uint8_t *dst
= (uint8_t *)&m_regs
+ reg_info
->byte_offset
;
288 assert(reg_info
->byte_offset
+ reg_info
->byte_size
<= sizeof(m_regs
));
289 switch (reg_info
->byte_size
) {
291 *(uint32_t *)dst
= reg_value
.GetAsUInt32();
294 *(uint64_t *)dst
= reg_value
.GetAsUInt64();
297 assert(false && "Unhandled data size.");
298 return Status::FromErrorStringWithFormat("unhandled byte size: %" PRIu32
,
299 reg_info
->byte_size
);
305 Status error
= ReadFPR();
309 // byte_offset is just the offset within fp_regs, not the whole user area.
310 uint8_t *dst
= (uint8_t *)&m_fp_regs
+ reg_info
->byte_offset
;
311 assert(reg_info
->byte_offset
+ reg_info
->byte_size
<= sizeof(m_fp_regs
));
312 switch (reg_info
->byte_size
) {
314 *(uint32_t *)dst
= reg_value
.GetAsUInt32();
317 *(uint64_t *)dst
= reg_value
.GetAsUInt64();
320 assert(false && "Unhandled data size.");
321 return Status::FromErrorStringWithFormat("unhandled byte size: %" PRIu32
,
322 reg_info
->byte_size
);
327 if (reg
== lldb_last_break_s390x
) {
328 return Status::FromErrorString("The last break address is read-only");
331 if (reg
== lldb_system_call_s390x
) {
332 uint32_t system_call
= reg_value
.GetAsUInt32();
333 return DoWriteRegisterSet(NT_S390_SYSTEM_CALL
, &system_call
, 4);
336 return Status::FromErrorString("failed - register wasn't recognized");
339 Status
NativeRegisterContextLinux_s390x::ReadAllRegisterValues(
340 lldb::WritableDataBufferSP
&data_sp
) {
343 data_sp
.reset(new DataBufferHeap(REG_CONTEXT_SIZE
, 0));
344 uint8_t *dst
= data_sp
->GetBytes();
348 memcpy(dst
, GetGPRBuffer(), GetGPRSize());
354 memcpy(dst
, GetFPRBuffer(), GetFPRSize());
357 // Ignore errors if the regset is unsupported (happens on older kernels).
358 DoReadRegisterSet(NT_S390_SYSTEM_CALL
, dst
, 4);
361 // To enable inferior function calls while the process is stopped in an
362 // interrupted system call, we need to clear the system call flag. It will be
363 // restored to its original value by WriteAllRegisterValues. Again we ignore
364 // error if the regset is unsupported.
365 uint32_t system_call
= 0;
366 DoWriteRegisterSet(NT_S390_SYSTEM_CALL
, &system_call
, 4);
371 Status
NativeRegisterContextLinux_s390x::WriteAllRegisterValues(
372 const lldb::DataBufferSP
&data_sp
) {
376 error
= Status::FromErrorStringWithFormat(
377 "NativeRegisterContextLinux_s390x::%s invalid data_sp provided",
382 if (data_sp
->GetByteSize() != REG_CONTEXT_SIZE
) {
383 error
= Status::FromErrorStringWithFormat(
384 "NativeRegisterContextLinux_s390x::%s data_sp contained mismatched "
385 "data size, expected %" PRIu64
", actual %" PRIu64
,
386 __FUNCTION__
, REG_CONTEXT_SIZE
, data_sp
->GetByteSize());
390 const uint8_t *src
= data_sp
->GetBytes();
391 if (src
== nullptr) {
392 error
= Status::FromErrorStringWithFormat(
393 "NativeRegisterContextLinux_s390x::%s "
394 "DataBuffer::GetBytes() returned a null "
400 memcpy(GetGPRBuffer(), src
, GetGPRSize());
406 memcpy(GetFPRBuffer(), src
, GetFPRSize());
412 // Ignore errors if the regset is unsupported (happens on older kernels).
413 DoWriteRegisterSet(NT_S390_SYSTEM_CALL
, src
, 4);
419 Status
NativeRegisterContextLinux_s390x::DoReadRegisterValue(
420 uint32_t offset
, const char *reg_name
, uint32_t size
,
421 RegisterValue
&value
) {
422 return Status::FromErrorString("DoReadRegisterValue unsupported");
425 Status
NativeRegisterContextLinux_s390x::DoWriteRegisterValue(
426 uint32_t offset
, const char *reg_name
, const RegisterValue
&value
) {
427 return Status::FromErrorString("DoWriteRegisterValue unsupported");
430 Status
NativeRegisterContextLinux_s390x::PeekUserArea(uint32_t offset
,
434 parea
.len
= buf_size
;
435 parea
.process_addr
= (addr_t
)buf
;
436 parea
.kernel_addr
= offset
;
438 return NativeProcessLinux::PtraceWrapper(PTRACE_PEEKUSR_AREA
,
439 m_thread
.GetID(), &parea
);
442 Status
NativeRegisterContextLinux_s390x::PokeUserArea(uint32_t offset
,
446 parea
.len
= buf_size
;
447 parea
.process_addr
= (addr_t
)buf
;
448 parea
.kernel_addr
= offset
;
450 return NativeProcessLinux::PtraceWrapper(PTRACE_POKEUSR_AREA
,
451 m_thread
.GetID(), &parea
);
454 Status
NativeRegisterContextLinux_s390x::ReadGPR() {
455 return PeekUserArea(offsetof(user_regs_struct
, psw
), GetGPRBuffer(),
459 Status
NativeRegisterContextLinux_s390x::WriteGPR() {
460 return PokeUserArea(offsetof(user_regs_struct
, psw
), GetGPRBuffer(),
464 Status
NativeRegisterContextLinux_s390x::ReadFPR() {
465 return PeekUserArea(offsetof(user_regs_struct
, fp_regs
), GetGPRBuffer(),
469 Status
NativeRegisterContextLinux_s390x::WriteFPR() {
470 return PokeUserArea(offsetof(user_regs_struct
, fp_regs
), GetGPRBuffer(),
474 Status
NativeRegisterContextLinux_s390x::DoReadRegisterSet(uint32_t regset
,
479 iov
.iov_len
= buf_size
;
481 return ReadRegisterSet(&iov
, buf_size
, regset
);
484 Status
NativeRegisterContextLinux_s390x::DoWriteRegisterSet(uint32_t regset
,
488 iov
.iov_base
= const_cast<void *>(buf
);
489 iov
.iov_len
= buf_size
;
491 return WriteRegisterSet(&iov
, buf_size
, regset
);
494 Status
NativeRegisterContextLinux_s390x::IsWatchpointHit(uint32_t wp_index
,
496 per_lowcore_bits per_lowcore
;
498 if (wp_index
>= NumSupportedHardwareWatchpoints())
499 return Status::FromErrorString("Watchpoint index out of range");
501 if (m_watchpoint_addr
== LLDB_INVALID_ADDRESS
) {
506 Status error
= PeekUserArea(offsetof(user_regs_struct
, per_info
.lowcore
),
507 &per_lowcore
, sizeof(per_lowcore
));
513 is_hit
= (per_lowcore
.perc_storage_alteration
== 1 &&
514 per_lowcore
.perc_store_real_address
== 0);
517 // Do not report this watchpoint again.
518 memset(&per_lowcore
, 0, sizeof(per_lowcore
));
519 PokeUserArea(offsetof(user_regs_struct
, per_info
.lowcore
), &per_lowcore
,
520 sizeof(per_lowcore
));
526 Status
NativeRegisterContextLinux_s390x::GetWatchpointHitIndex(
527 uint32_t &wp_index
, lldb::addr_t trap_addr
) {
528 uint32_t num_hw_wps
= NumSupportedHardwareWatchpoints();
529 for (wp_index
= 0; wp_index
< num_hw_wps
; ++wp_index
) {
531 Status error
= IsWatchpointHit(wp_index
, is_hit
);
533 wp_index
= LLDB_INVALID_INDEX32
;
539 wp_index
= LLDB_INVALID_INDEX32
;
543 Status
NativeRegisterContextLinux_s390x::IsWatchpointVacant(uint32_t wp_index
,
545 if (wp_index
>= NumSupportedHardwareWatchpoints())
546 return Status::FromErrorString("Watchpoint index out of range");
548 is_vacant
= m_watchpoint_addr
== LLDB_INVALID_ADDRESS
;
553 bool NativeRegisterContextLinux_s390x::ClearHardwareWatchpoint(
557 if (wp_index
>= NumSupportedHardwareWatchpoints())
560 Status error
= PeekUserArea(offsetof(user_regs_struct
, per_info
), &per_info
,
565 per_info
.control_regs
.bits
.em_storage_alteration
= 0;
566 per_info
.control_regs
.bits
.storage_alt_space_ctl
= 0;
567 per_info
.starting_addr
= 0;
568 per_info
.ending_addr
= 0;
570 error
= PokeUserArea(offsetof(user_regs_struct
, per_info
), &per_info
,
575 m_watchpoint_addr
= LLDB_INVALID_ADDRESS
;
579 Status
NativeRegisterContextLinux_s390x::ClearAllHardwareWatchpoints() {
580 if (ClearHardwareWatchpoint(0))
582 return Status::FromErrorString("Clearing all hardware watchpoints failed.");
585 uint32_t NativeRegisterContextLinux_s390x::SetHardwareWatchpoint(
586 lldb::addr_t addr
, size_t size
, uint32_t watch_flags
) {
589 if (watch_flags
!= 0x1)
590 return LLDB_INVALID_INDEX32
;
592 if (m_watchpoint_addr
!= LLDB_INVALID_ADDRESS
)
593 return LLDB_INVALID_INDEX32
;
595 Status error
= PeekUserArea(offsetof(user_regs_struct
, per_info
), &per_info
,
598 return LLDB_INVALID_INDEX32
;
600 per_info
.control_regs
.bits
.em_storage_alteration
= 1;
601 per_info
.control_regs
.bits
.storage_alt_space_ctl
= 1;
602 per_info
.starting_addr
= addr
;
603 per_info
.ending_addr
= addr
+ size
- 1;
605 error
= PokeUserArea(offsetof(user_regs_struct
, per_info
), &per_info
,
608 return LLDB_INVALID_INDEX32
;
610 m_watchpoint_addr
= addr
;
615 NativeRegisterContextLinux_s390x::GetWatchpointAddress(uint32_t wp_index
) {
616 if (wp_index
>= NumSupportedHardwareWatchpoints())
617 return LLDB_INVALID_ADDRESS
;
618 return m_watchpoint_addr
;
621 uint32_t NativeRegisterContextLinux_s390x::NumSupportedHardwareWatchpoints() {
625 #endif // defined(__s390x__) && defined(__linux__)