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