Skip to content

Commit 6c4e70f

Browse files
authored
[lldb][Process] Introduce LoongArch64 hw break/watchpoint support
This patch adds support for setting/clearing hardware watchpoints and breakpoints on LoongArch 64-bit hardware. Refer to the following document for the hw break/watchpoint: https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#control-and-status-registers-related-to-watchpoints Fix Failed Tests: lldb-shell :: Subprocess/clone-follow-child-wp.test lldb-shell :: Subprocess/clone-follow-parent-wp.test lldb-shell :: Subprocess/fork-follow-child-wp.test lldb-shell :: Subprocess/fork-follow-parent-wp.test lldb-shell :: Subprocess/vfork-follow-child-wp.test lldb-shell :: Subprocess/vfork-follow-parent-wp.test lldb-shell :: Watchpoint/ExpressionLanguage.test Depends on: #118043 Reviewed By: SixWeining Pull Request: #118770
1 parent 3de5e8b commit 6c4e70f

7 files changed

+192
-3
lines changed

lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "NativeRegisterContextLinux_loongarch64.h"
1212

1313
#include "lldb/Host/HostInfo.h"
14+
#include "lldb/Host/linux/Ptrace.h"
1415
#include "lldb/Utility/DataBufferHeap.h"
1516
#include "lldb/Utility/Log.h"
1617
#include "lldb/Utility/RegisterValue.h"
@@ -62,6 +63,16 @@ NativeRegisterContextLinux_loongarch64::NativeRegisterContextLinux_loongarch64(
6263
::memset(&m_fpr, 0, sizeof(m_fpr));
6364
::memset(&m_gpr, 0, sizeof(m_gpr));
6465

66+
::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs));
67+
::memset(&m_hbp_regs, 0, sizeof(m_hbp_regs));
68+
69+
// Refer to:
70+
// https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#control-and-status-registers-related-to-watchpoints
71+
// 14 is just a maximum value, query hardware for actual watchpoint count.
72+
m_max_hwp_supported = 14;
73+
m_max_hbp_supported = 14;
74+
m_refresh_hwdebug_info = true;
75+
6576
m_gpr_is_valid = false;
6677
m_fpu_is_valid = false;
6778
}
@@ -337,4 +348,73 @@ NativeRegisterContextLinux_loongarch64::GetExpeditedRegisters(
337348
return expedited_reg_nums;
338349
}
339350

351+
llvm::Error NativeRegisterContextLinux_loongarch64::ReadHardwareDebugInfo() {
352+
if (!m_refresh_hwdebug_info)
353+
return llvm::Error::success();
354+
355+
::pid_t tid = m_thread.GetID();
356+
357+
int regset = NT_LOONGARCH_HW_WATCH;
358+
struct iovec ioVec;
359+
struct user_watch_state dreg_state;
360+
Status error;
361+
362+
ioVec.iov_base = &dreg_state;
363+
ioVec.iov_len = sizeof(dreg_state);
364+
error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, &regset,
365+
&ioVec, ioVec.iov_len);
366+
if (error.Fail())
367+
return error.ToError();
368+
369+
m_max_hwp_supported = dreg_state.dbg_info & 0x3f;
370+
371+
regset = NT_LOONGARCH_HW_BREAK;
372+
error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, &regset,
373+
&ioVec, ioVec.iov_len);
374+
if (error.Fail())
375+
return error.ToError();
376+
377+
m_max_hbp_supported = dreg_state.dbg_info & 0x3f;
378+
379+
m_refresh_hwdebug_info = false;
380+
381+
return llvm::Error::success();
382+
}
383+
384+
llvm::Error NativeRegisterContextLinux_loongarch64::WriteHardwareDebugRegs(
385+
DREGType hwbType) {
386+
struct iovec ioVec;
387+
struct user_watch_state dreg_state;
388+
int regset;
389+
390+
memset(&dreg_state, 0, sizeof(dreg_state));
391+
ioVec.iov_base = &dreg_state;
392+
393+
switch (hwbType) {
394+
case eDREGTypeWATCH:
395+
regset = NT_LOONGARCH_HW_WATCH;
396+
ioVec.iov_len = sizeof(dreg_state.dbg_info) +
397+
(sizeof(dreg_state.dbg_regs[0]) * m_max_hwp_supported);
398+
399+
for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
400+
dreg_state.dbg_regs[i].addr = m_hwp_regs[i].address;
401+
dreg_state.dbg_regs[i].ctrl = m_hwp_regs[i].control;
402+
}
403+
break;
404+
case eDREGTypeBREAK:
405+
regset = NT_LOONGARCH_HW_BREAK;
406+
ioVec.iov_len = sizeof(dreg_state.dbg_info) +
407+
(sizeof(dreg_state.dbg_regs[0]) * m_max_hbp_supported);
408+
409+
for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
410+
dreg_state.dbg_regs[i].addr = m_hbp_regs[i].address;
411+
dreg_state.dbg_regs[i].ctrl = m_hbp_regs[i].control;
412+
}
413+
break;
414+
}
415+
416+
return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(),
417+
&regset, &ioVec, ioVec.iov_len)
418+
.ToError();
419+
}
340420
#endif // defined(__loongarch__) && __loongarch_grlen == 64

lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#define lldb_NativeRegisterContextLinux_loongarch64_h
1313

1414
#include "Plugins/Process/Linux/NativeRegisterContextLinux.h"
15+
#include "Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.h"
1516
#include "Plugins/Process/Utility/RegisterInfoPOSIX_loongarch64.h"
1617

1718
#include <asm/ptrace.h>
@@ -22,7 +23,8 @@ namespace process_linux {
2223
class NativeProcessLinux;
2324

2425
class NativeRegisterContextLinux_loongarch64
25-
: public NativeRegisterContextLinux {
26+
: public NativeRegisterContextLinux,
27+
public NativeRegisterContextDBReg_loongarch {
2628
public:
2729
NativeRegisterContextLinux_loongarch64(
2830
const ArchSpec &target_arch, NativeThreadProtocol &native_thread,
@@ -71,6 +73,7 @@ class NativeRegisterContextLinux_loongarch64
7173
private:
7274
bool m_gpr_is_valid;
7375
bool m_fpu_is_valid;
76+
bool m_refresh_hwdebug_info;
7477

7578
RegisterInfoPOSIX_loongarch64::GPR m_gpr;
7679

@@ -83,6 +86,10 @@ class NativeRegisterContextLinux_loongarch64
8386
uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const;
8487

8588
const RegisterInfoPOSIX_loongarch64 &GetRegisterInfo() const;
89+
90+
llvm::Error ReadHardwareDebugInfo() override;
91+
92+
llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override;
8693
};
8794

8895
} // namespace process_linux

lldb/source/Plugins/Process/Utility/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ add_lldb_library(lldbPluginProcessUtility
1111
NativeProcessSoftwareSingleStep.cpp
1212
NativeRegisterContextDBReg.cpp
1313
NativeRegisterContextDBReg_arm64.cpp
14+
NativeRegisterContextDBReg_loongarch.cpp
1415
NativeRegisterContextDBReg_x86.cpp
1516
NativeRegisterContextRegisterInfo.cpp
1617
NetBSDSignals.cpp
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//===-- NativeRegisterContextDBReg_loongarch.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+
#include "NativeRegisterContextDBReg_loongarch.h"
10+
11+
#include "lldb/Utility/LLDBLog.h"
12+
#include "lldb/Utility/Log.h"
13+
#include "lldb/Utility/RegisterValue.h"
14+
15+
using namespace lldb_private;
16+
17+
uint32_t
18+
NativeRegisterContextDBReg_loongarch::GetWatchpointSize(uint32_t wp_index) {
19+
Log *log = GetLog(LLDBLog::Watchpoints);
20+
LLDB_LOG(log, "wp_index: {0}", wp_index);
21+
22+
switch ((m_hwp_regs[wp_index].control >> 10) & 0x3) {
23+
case 0x0:
24+
return 8;
25+
case 0x1:
26+
return 4;
27+
case 0x2:
28+
return 2;
29+
case 0x3:
30+
return 1;
31+
default:
32+
return 0;
33+
}
34+
}
35+
36+
std::optional<NativeRegisterContextDBReg::WatchpointDetails>
37+
NativeRegisterContextDBReg_loongarch::AdjustWatchpoint(
38+
const WatchpointDetails &details) {
39+
// LoongArch only needs to check the size; it does not need to check the
40+
// address.
41+
size_t size = details.size;
42+
if (size != 1 && size != 2 && size != 4 && size != 8)
43+
return std::nullopt;
44+
45+
return details;
46+
}
47+
48+
uint32_t
49+
NativeRegisterContextDBReg_loongarch::MakeBreakControlValue(size_t size) {
50+
// Return encoded hardware breakpoint control value.
51+
return m_hw_dbg_enable_bit;
52+
}
53+
54+
uint32_t NativeRegisterContextDBReg_loongarch::MakeWatchControlValue(
55+
size_t size, uint32_t watch_flags) {
56+
// Encoding hardware watchpoint control value.
57+
// Size encoded:
58+
// case 1 : 0b11
59+
// case 2 : 0b10
60+
// case 4 : 0b01
61+
// case 8 : 0b00
62+
size_t encoded_size = (3 - llvm::Log2_32(size)) << 10;
63+
64+
return m_hw_dbg_enable_bit | encoded_size | (watch_flags << 8);
65+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===-- NativeRegisterContextDBReg_loongarch.h ------------------*- C++ -*-===//
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+
#ifndef lldb_NativeRegisterContextDBReg_loongarch_h
10+
#define lldb_NativeRegisterContextDBReg_loongarch_h
11+
12+
#include "Plugins/Process/Utility/NativeRegisterContextDBReg.h"
13+
14+
namespace lldb_private {
15+
16+
class NativeRegisterContextDBReg_loongarch : public NativeRegisterContextDBReg {
17+
public:
18+
NativeRegisterContextDBReg_loongarch()
19+
: NativeRegisterContextDBReg(/*enable_bit=*/0x10U) {}
20+
21+
private:
22+
uint32_t GetWatchpointSize(uint32_t wp_index) override;
23+
24+
std::optional<WatchpointDetails>
25+
AdjustWatchpoint(const WatchpointDetails &details) override;
26+
27+
uint32_t MakeBreakControlValue(size_t size) override;
28+
29+
uint32_t MakeWatchControlValue(size_t size, uint32_t watch_flags) override;
30+
};
31+
32+
} // namespace lldb_private
33+
34+
#endif // #ifndef lldb_NativeRegisterContextDBReg_loongarch_h

lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,8 @@ GDBRemoteCommunicationServerCommon::Handle_qHostInfo(
231231
host_arch.GetMachine() == llvm::Triple::aarch64_32 ||
232232
host_arch.GetMachine() == llvm::Triple::aarch64_be ||
233233
host_arch.GetMachine() == llvm::Triple::arm ||
234-
host_arch.GetMachine() == llvm::Triple::armeb || host_arch.IsMIPS())
234+
host_arch.GetMachine() == llvm::Triple::armeb || host_arch.IsMIPS() ||
235+
host_arch.GetTriple().isLoongArch())
235236
response.Printf("watchpoint_exceptions_received:before;");
236237
else
237238
response.Printf("watchpoint_exceptions_received:after;");

lldb/source/Target/Process.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2517,7 +2517,8 @@ bool Process::GetWatchpointReportedAfter() {
25172517
llvm::Triple triple = arch.GetTriple();
25182518

25192519
if (triple.isMIPS() || triple.isPPC64() || triple.isRISCV() ||
2520-
triple.isAArch64() || triple.isArmMClass() || triple.isARM())
2520+
triple.isAArch64() || triple.isArmMClass() || triple.isARM() ||
2521+
triple.isLoongArch())
25212522
reported_after = false;
25222523

25232524
return reported_after;

0 commit comments

Comments
 (0)