LLDB mainline
NativeRegisterContextLinux_s390x.cpp
Go to the documentation of this file.
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
14#include "lldb/Host/HostInfo.h"
16#include "lldb/Utility/Log.h"
18#include "lldb/Utility/Status.h"
19#include <sys/ptrace.h>
20#include <sys/uio.h>
21
22using namespace lldb_private;
23using namespace lldb_private::process_linux;
24
25// Private namespace.
26
27namespace {
28// s390x 64-bit general purpose registers.
29static const uint32_t g_gpr_regnums_s390x[] = {
39 LLDB_INVALID_REGNUM // register sets need to end with this flag
40};
41static_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.
47static const uint32_t g_fpu_regnums_s390x[] = {
53 LLDB_INVALID_REGNUM // register sets need to end with this flag
54};
55static_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.
61static const uint32_t g_linux_regnums_s390x[] = {
63 LLDB_INVALID_REGNUM // register sets need to end with this flag
64};
65static_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.
72enum { k_num_register_sets = 3 };
73
74// Register sets for s390x 64-bit.
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},
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
92std::unique_ptr<NativeRegisterContextLinux>
93NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(
94 const ArchSpec &target_arch, NativeThreadLinux &native_thread) {
95 return std::make_unique<NativeRegisterContextLinux_s390x>(target_arch,
96 native_thread);
97}
98
99llvm::Expected<ArchSpec>
100NativeRegisterContextLinux::DetermineArchitecture(lldb::tid_t tid) {
101 return HostInfo::GetArchitecture();
102}
103
104// NativeRegisterContextLinux_s390x members.
105
107CreateRegisterInfoInterface(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
113NativeRegisterContextLinux_s390x::NativeRegisterContextLinux_s390x(
114 const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
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
137uint32_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
147uint32_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
157const RegisterSet *
158NativeRegisterContextLinux_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
173bool NativeRegisterContextLinux_s390x::IsRegisterSetAvailable(
174 uint32_t set_index) const {
175 return set_index < k_num_register_sets;
176}
177
178bool 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
184bool 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
189Status
190NativeRegisterContextLinux_s390x::ReadRegister(const RegisterInfo *reg_info,
191 RegisterValue &reg_value) {
192 if (!reg_info)
193 return Status::FromErrorString("reg_info NULL");
194
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 "
199 "read directly",
200 reg_info->name);
201
202 if (IsGPR(reg)) {
203 Status error = ReadGPR();
204 if (error.Fail())
205 return error;
206
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) {
210 case 4:
211 reg_value.SetUInt32(*(uint32_t *)src);
212 break;
213 case 8:
214 reg_value.SetUInt64(*(uint64_t *)src);
215 break;
216 default:
217 assert(false && "Unhandled data size.");
218 return Status::FromErrorStringWithFormat("unhandled byte size: %" PRIu32,
219 reg_info->byte_size);
220 }
221 return Status();
222 }
223
224 if (IsFPR(reg)) {
225 Status error = ReadFPR();
226 if (error.Fail())
227 return error;
228
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) {
233 case 4:
234 reg_value.SetUInt32(*(uint32_t *)src);
235 break;
236 case 8:
237 reg_value.SetUInt64(*(uint64_t *)src);
238 break;
239 default:
240 assert(false && "Unhandled data size.");
241 return Status::FromErrorStringWithFormat("unhandled byte size: %" PRIu32,
242 reg_info->byte_size);
243 }
244 return Status();
245 }
246
247 if (reg == lldb_last_break_s390x) {
248 uint64_t last_break;
249 Status error = DoReadRegisterSet(NT_S390_LAST_BREAK, &last_break, 8);
250 if (error.Fail())
251 return error;
252
253 reg_value.SetUInt64(last_break);
254 return Status();
255 }
256
257 if (reg == lldb_system_call_s390x) {
258 uint32_t system_call;
259 Status error = DoReadRegisterSet(NT_S390_SYSTEM_CALL, &system_call, 4);
260 if (error.Fail())
261 return error;
262
263 reg_value.SetUInt32(system_call);
264 return Status();
265 }
266
267 return Status::FromErrorString("failed - register wasn't recognized");
268}
269
270Status NativeRegisterContextLinux_s390x::WriteRegister(
271 const RegisterInfo *reg_info, const RegisterValue &reg_value) {
272 if (!reg_info)
273 return Status::FromErrorString("reg_info NULL");
274
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 "
279 "write directly",
280 reg_info->name);
281
282 if (IsGPR(reg)) {
283 Status error = ReadGPR();
284 if (error.Fail())
285 return error;
286
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) {
290 case 4:
291 *(uint32_t *)dst = reg_value.GetAsUInt32();
292 break;
293 case 8:
294 *(uint64_t *)dst = reg_value.GetAsUInt64();
295 break;
296 default:
297 assert(false && "Unhandled data size.");
298 return Status::FromErrorStringWithFormat("unhandled byte size: %" PRIu32,
299 reg_info->byte_size);
300 }
301 return WriteGPR();
302 }
303
304 if (IsFPR(reg)) {
305 Status error = ReadFPR();
306 if (error.Fail())
307 return error;
308
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) {
313 case 4:
314 *(uint32_t *)dst = reg_value.GetAsUInt32();
315 break;
316 case 8:
317 *(uint64_t *)dst = reg_value.GetAsUInt64();
318 break;
319 default:
320 assert(false && "Unhandled data size.");
321 return Status::FromErrorStringWithFormat("unhandled byte size: %" PRIu32,
322 reg_info->byte_size);
323 }
324 return WriteFPR();
325 }
326
327 if (reg == lldb_last_break_s390x) {
328 return Status::FromErrorString("The last break address is read-only");
329 }
330
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);
334 }
335
336 return Status::FromErrorString("failed - register wasn't recognized");
337}
338
339Status NativeRegisterContextLinux_s390x::ReadAllRegisterValues(
342
343 data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0));
344 uint8_t *dst = data_sp->GetBytes();
345 error = ReadGPR();
346 if (error.Fail())
347 return error;
348 memcpy(dst, GetGPRBuffer(), GetGPRSize());
349 dst += GetGPRSize();
350
351 error = ReadFPR();
352 if (error.Fail())
353 return error;
354 memcpy(dst, GetFPRBuffer(), GetFPRSize());
355 dst += GetFPRSize();
356
357 // Ignore errors if the regset is unsupported (happens on older kernels).
358 DoReadRegisterSet(NT_S390_SYSTEM_CALL, dst, 4);
359 dst += 4;
360
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);
367
368 return error;
369}
370
371Status NativeRegisterContextLinux_s390x::WriteAllRegisterValues(
372 const lldb::DataBufferSP &data_sp) {
374
375 if (!data_sp) {
376 error = Status::FromErrorStringWithFormat(
377 "NativeRegisterContextLinux_s390x::%s invalid data_sp provided",
378 __FUNCTION__);
379 return error;
380 }
381
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());
387 return error;
388 }
389
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 "
395 "pointer",
396 __FUNCTION__);
397 return error;
398 }
399
400 memcpy(GetGPRBuffer(), src, GetGPRSize());
401 src += GetGPRSize();
402 error = WriteGPR();
403 if (error.Fail())
404 return error;
405
406 memcpy(GetFPRBuffer(), src, GetFPRSize());
407 src += GetFPRSize();
408 error = WriteFPR();
409 if (error.Fail())
410 return error;
411
412 // Ignore errors if the regset is unsupported (happens on older kernels).
413 DoWriteRegisterSet(NT_S390_SYSTEM_CALL, src, 4);
414 src += 4;
415
416 return error;
417}
418
419Status NativeRegisterContextLinux_s390x::DoReadRegisterValue(
420 uint32_t offset, const char *reg_name, uint32_t size,
421 RegisterValue &value) {
422 return Status::FromErrorString("DoReadRegisterValue unsupported");
423}
424
425Status NativeRegisterContextLinux_s390x::DoWriteRegisterValue(
426 uint32_t offset, const char *reg_name, const RegisterValue &value) {
427 return Status::FromErrorString("DoWriteRegisterValue unsupported");
428}
429
430Status NativeRegisterContextLinux_s390x::PeekUserArea(uint32_t offset,
431 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_PEEKUSR_AREA,
439 m_thread.GetID(), &parea);
440}
441
442Status NativeRegisterContextLinux_s390x::PokeUserArea(uint32_t offset,
443 const void *buf,
444 size_t buf_size) {
445 ptrace_area parea;
446 parea.len = buf_size;
447 parea.process_addr = (addr_t)buf;
448 parea.kernel_addr = offset;
449
450 return NativeProcessLinux::PtraceWrapper(PTRACE_POKEUSR_AREA,
451 m_thread.GetID(), &parea);
452}
453
454Status NativeRegisterContextLinux_s390x::ReadGPR() {
455 return PeekUserArea(offsetof(user_regs_struct, psw), GetGPRBuffer(),
456 GetGPRSize());
457}
458
459Status NativeRegisterContextLinux_s390x::WriteGPR() {
460 return PokeUserArea(offsetof(user_regs_struct, psw), GetGPRBuffer(),
461 GetGPRSize());
462}
463
464Status NativeRegisterContextLinux_s390x::ReadFPR() {
465 return PeekUserArea(offsetof(user_regs_struct, fp_regs), GetGPRBuffer(),
466 GetGPRSize());
467}
468
469Status NativeRegisterContextLinux_s390x::WriteFPR() {
470 return PokeUserArea(offsetof(user_regs_struct, fp_regs), GetGPRBuffer(),
471 GetGPRSize());
472}
473
474Status NativeRegisterContextLinux_s390x::DoReadRegisterSet(uint32_t regset,
475 void *buf,
476 size_t buf_size) {
477 struct iovec iov;
478 iov.iov_base = buf;
479 iov.iov_len = buf_size;
480
481 return ReadRegisterSet(&iov, buf_size, regset);
482}
483
484Status NativeRegisterContextLinux_s390x::DoWriteRegisterSet(uint32_t regset,
485 const void *buf,
486 size_t buf_size) {
487 struct iovec iov;
488 iov.iov_base = const_cast<void *>(buf);
489 iov.iov_len = buf_size;
490
491 return WriteRegisterSet(&iov, buf_size, regset);
492}
493
494Status NativeRegisterContextLinux_s390x::IsWatchpointHit(uint32_t wp_index,
495 bool &is_hit) {
496 per_lowcore_bits per_lowcore;
497
498 if (wp_index >= NumSupportedHardwareWatchpoints())
499 return Status::FromErrorString("Watchpoint index out of range");
500
501 if (m_watchpoint_addr == LLDB_INVALID_ADDRESS) {
502 is_hit = false;
503 return Status();
504 }
505
506 Status error = PeekUserArea(offsetof(user_regs_struct, per_info.lowcore),
507 &per_lowcore, sizeof(per_lowcore));
508 if (error.Fail()) {
509 is_hit = false;
510 return error;
511 }
512
513 is_hit = (per_lowcore.perc_storage_alteration == 1 &&
514 per_lowcore.perc_store_real_address == 0);
515
516 if (is_hit) {
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));
521 }
522
523 return Status();
524}
525
526Status 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) {
530 bool is_hit;
531 Status error = IsWatchpointHit(wp_index, is_hit);
532 if (error.Fail()) {
533 wp_index = LLDB_INVALID_INDEX32;
534 return error;
535 } else if (is_hit) {
536 return error;
537 }
538 }
539 wp_index = LLDB_INVALID_INDEX32;
540 return Status();
541}
542
543Status NativeRegisterContextLinux_s390x::IsWatchpointVacant(uint32_t wp_index,
544 bool &is_vacant) {
545 if (wp_index >= NumSupportedHardwareWatchpoints())
546 return Status::FromErrorString("Watchpoint index out of range");
547
548 is_vacant = m_watchpoint_addr == LLDB_INVALID_ADDRESS;
549
550 return Status();
551}
552
553bool NativeRegisterContextLinux_s390x::ClearHardwareWatchpoint(
554 uint32_t wp_index) {
555 per_struct per_info;
556
557 if (wp_index >= NumSupportedHardwareWatchpoints())
558 return false;
559
560 Status error = PeekUserArea(offsetof(user_regs_struct, per_info), &per_info,
561 sizeof(per_info));
562 if (error.Fail())
563 return false;
564
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;
569
570 error = PokeUserArea(offsetof(user_regs_struct, per_info), &per_info,
571 sizeof(per_info));
572 if (error.Fail())
573 return false;
574
575 m_watchpoint_addr = LLDB_INVALID_ADDRESS;
576 return true;
577}
578
579Status NativeRegisterContextLinux_s390x::ClearAllHardwareWatchpoints() {
580 if (ClearHardwareWatchpoint(0))
581 return Status();
582 return Status::FromErrorString("Clearing all hardware watchpoints failed.");
583}
584
585uint32_t NativeRegisterContextLinux_s390x::SetHardwareWatchpoint(
586 lldb::addr_t addr, size_t size, uint32_t watch_flags) {
587 per_struct per_info;
588
589 if (watch_flags != 0x1)
591
592 if (m_watchpoint_addr != LLDB_INVALID_ADDRESS)
594
595 Status error = PeekUserArea(offsetof(user_regs_struct, per_info), &per_info,
596 sizeof(per_info));
597 if (error.Fail())
599
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;
604
605 error = PokeUserArea(offsetof(user_regs_struct, per_info), &per_info,
606 sizeof(per_info));
607 if (error.Fail())
609
610 m_watchpoint_addr = addr;
611 return 0;
612}
613
615NativeRegisterContextLinux_s390x::GetWatchpointAddress(uint32_t wp_index) {
616 if (wp_index >= NumSupportedHardwareWatchpoints())
618 return m_watchpoint_addr;
619}
620
621uint32_t NativeRegisterContextLinux_s390x::NumSupportedHardwareWatchpoints() {
622 return 1;
623}
624
625#endif // defined(__s390x__) && defined(__linux__)
static llvm::raw_ostream & error(Stream &strm)
#define REG_CONTEXT_SIZE
constexpr size_t k_num_register_sets
static const uint32_t g_gpr_regnums_s390x[]
static const uint32_t g_fpu_regnums_s390x[]
static const RegisterSet g_reg_sets_s390x[k_num_register_sets]
An architecture specification class.
Definition: ArchSpec.h:31
llvm::Triple::ArchType GetMachine() const
Returns a machine family for the current architecture.
Definition: ArchSpec.cpp:701
A subclass of DataBuffer that stores a data buffer on the heap.
RegisterInfo interface to patch RegisterInfo structure for archs.
void SetUInt64(uint64_t uint, Type t=eTypeUInt64)
uint64_t GetAsUInt64(uint64_t fail_value=UINT64_MAX, bool *success_ptr=nullptr) const
uint32_t GetAsUInt32(uint32_t fail_value=UINT32_MAX, bool *success_ptr=nullptr) const
void SetUInt32(uint32_t uint, Type t=eTypeUInt32)
An error handling class.
Definition: Status.h:118
#define LLDB_INVALID_INDEX32
Definition: lldb-defines.h:83
#define LLDB_INVALID_ADDRESS
Definition: lldb-defines.h:82
#define LLDB_INVALID_REGNUM
Definition: lldb-defines.h:87
A class that represents a running process on the host machine.
std::shared_ptr< lldb_private::DataBuffer > DataBufferSP
Definition: lldb-forward.h:336
std::shared_ptr< lldb_private::WritableDataBuffer > WritableDataBufferSP
Definition: lldb-forward.h:337
uint64_t addr_t
Definition: lldb-types.h:80
uint64_t tid_t
Definition: lldb-types.h:84
@ eRegisterKindLLDB
lldb's internal register numbers
Every register is described in detail including its name, alternate name (optional),...
uint32_t byte_offset
The byte offset in the register context data where this register's value is found.
uint32_t byte_size
Size in bytes of the register.
uint32_t kinds[lldb::kNumRegisterKinds]
Holds all of the various register numbers for all register kinds.
const char * name
Name of this register, can't be NULL.
Registers are grouped into register sets.
size_t num_registers
The number of registers in REGISTERS array below.