From 6c4e70fcbbb62f38a5aab085634de5faaa5cf729 Mon Sep 17 00:00:00 2001 From: wanglei Date: Fri, 13 Dec 2024 10:06:55 +0800 Subject: [PATCH] [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: https://github.com/llvm/llvm-project/pull/118770 --- ...NativeRegisterContextLinux_loongarch64.cpp | 80 +++++++++++++++++++ .../NativeRegisterContextLinux_loongarch64.h | 9 ++- .../Plugins/Process/Utility/CMakeLists.txt | 1 + .../NativeRegisterContextDBReg_loongarch.cpp | 65 +++++++++++++++ .../NativeRegisterContextDBReg_loongarch.h | 34 ++++++++ .../GDBRemoteCommunicationServerCommon.cpp | 3 +- lldb/source/Target/Process.cpp | 3 +- 7 files changed, 192 insertions(+), 3 deletions(-) create mode 100644 lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.cpp create mode 100644 lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.h diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.cpp index f4d1bb297049da6..9ffc8ada920cb89 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.cpp @@ -11,6 +11,7 @@ #include "NativeRegisterContextLinux_loongarch64.h" #include "lldb/Host/HostInfo.h" +#include "lldb/Host/linux/Ptrace.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/RegisterValue.h" @@ -62,6 +63,16 @@ NativeRegisterContextLinux_loongarch64::NativeRegisterContextLinux_loongarch64( ::memset(&m_fpr, 0, sizeof(m_fpr)); ::memset(&m_gpr, 0, sizeof(m_gpr)); + ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs)); + ::memset(&m_hbp_regs, 0, sizeof(m_hbp_regs)); + + // Refer to: + // https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#control-and-status-registers-related-to-watchpoints + // 14 is just a maximum value, query hardware for actual watchpoint count. + m_max_hwp_supported = 14; + m_max_hbp_supported = 14; + m_refresh_hwdebug_info = true; + m_gpr_is_valid = false; m_fpu_is_valid = false; } @@ -337,4 +348,73 @@ NativeRegisterContextLinux_loongarch64::GetExpeditedRegisters( return expedited_reg_nums; } +llvm::Error NativeRegisterContextLinux_loongarch64::ReadHardwareDebugInfo() { + if (!m_refresh_hwdebug_info) + return llvm::Error::success(); + + ::pid_t tid = m_thread.GetID(); + + int regset = NT_LOONGARCH_HW_WATCH; + struct iovec ioVec; + struct user_watch_state dreg_state; + Status error; + + ioVec.iov_base = &dreg_state; + ioVec.iov_len = sizeof(dreg_state); + error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, + &ioVec, ioVec.iov_len); + if (error.Fail()) + return error.ToError(); + + m_max_hwp_supported = dreg_state.dbg_info & 0x3f; + + regset = NT_LOONGARCH_HW_BREAK; + error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, + &ioVec, ioVec.iov_len); + if (error.Fail()) + return error.ToError(); + + m_max_hbp_supported = dreg_state.dbg_info & 0x3f; + + m_refresh_hwdebug_info = false; + + return llvm::Error::success(); +} + +llvm::Error NativeRegisterContextLinux_loongarch64::WriteHardwareDebugRegs( + DREGType hwbType) { + struct iovec ioVec; + struct user_watch_state dreg_state; + int regset; + + memset(&dreg_state, 0, sizeof(dreg_state)); + ioVec.iov_base = &dreg_state; + + switch (hwbType) { + case eDREGTypeWATCH: + regset = NT_LOONGARCH_HW_WATCH; + ioVec.iov_len = sizeof(dreg_state.dbg_info) + + (sizeof(dreg_state.dbg_regs[0]) * m_max_hwp_supported); + + for (uint32_t i = 0; i < m_max_hwp_supported; i++) { + dreg_state.dbg_regs[i].addr = m_hwp_regs[i].address; + dreg_state.dbg_regs[i].ctrl = m_hwp_regs[i].control; + } + break; + case eDREGTypeBREAK: + regset = NT_LOONGARCH_HW_BREAK; + ioVec.iov_len = sizeof(dreg_state.dbg_info) + + (sizeof(dreg_state.dbg_regs[0]) * m_max_hbp_supported); + + for (uint32_t i = 0; i < m_max_hbp_supported; i++) { + dreg_state.dbg_regs[i].addr = m_hbp_regs[i].address; + dreg_state.dbg_regs[i].ctrl = m_hbp_regs[i].control; + } + break; + } + + return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), + ®set, &ioVec, ioVec.iov_len) + .ToError(); +} #endif // defined(__loongarch__) && __loongarch_grlen == 64 diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.h index 0a6084ff4206dbc..633b26fa970de1e 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.h +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.h @@ -12,6 +12,7 @@ #define lldb_NativeRegisterContextLinux_loongarch64_h #include "Plugins/Process/Linux/NativeRegisterContextLinux.h" +#include "Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.h" #include "Plugins/Process/Utility/RegisterInfoPOSIX_loongarch64.h" #include @@ -22,7 +23,8 @@ namespace process_linux { class NativeProcessLinux; class NativeRegisterContextLinux_loongarch64 - : public NativeRegisterContextLinux { + : public NativeRegisterContextLinux, + public NativeRegisterContextDBReg_loongarch { public: NativeRegisterContextLinux_loongarch64( const ArchSpec &target_arch, NativeThreadProtocol &native_thread, @@ -71,6 +73,7 @@ class NativeRegisterContextLinux_loongarch64 private: bool m_gpr_is_valid; bool m_fpu_is_valid; + bool m_refresh_hwdebug_info; RegisterInfoPOSIX_loongarch64::GPR m_gpr; @@ -83,6 +86,10 @@ class NativeRegisterContextLinux_loongarch64 uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const; const RegisterInfoPOSIX_loongarch64 &GetRegisterInfo() const; + + llvm::Error ReadHardwareDebugInfo() override; + + llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override; }; } // namespace process_linux diff --git a/lldb/source/Plugins/Process/Utility/CMakeLists.txt b/lldb/source/Plugins/Process/Utility/CMakeLists.txt index 0526c95503175ed..0e1a5069d4409e3 100644 --- a/lldb/source/Plugins/Process/Utility/CMakeLists.txt +++ b/lldb/source/Plugins/Process/Utility/CMakeLists.txt @@ -11,6 +11,7 @@ add_lldb_library(lldbPluginProcessUtility NativeProcessSoftwareSingleStep.cpp NativeRegisterContextDBReg.cpp NativeRegisterContextDBReg_arm64.cpp + NativeRegisterContextDBReg_loongarch.cpp NativeRegisterContextDBReg_x86.cpp NativeRegisterContextRegisterInfo.cpp NetBSDSignals.cpp diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.cpp b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.cpp new file mode 100644 index 000000000000000..b3b5e6b4d4139b6 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.cpp @@ -0,0 +1,65 @@ +//===-- NativeRegisterContextDBReg_loongarch.cpp --------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "NativeRegisterContextDBReg_loongarch.h" + +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" + +using namespace lldb_private; + +uint32_t +NativeRegisterContextDBReg_loongarch::GetWatchpointSize(uint32_t wp_index) { + Log *log = GetLog(LLDBLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + switch ((m_hwp_regs[wp_index].control >> 10) & 0x3) { + case 0x0: + return 8; + case 0x1: + return 4; + case 0x2: + return 2; + case 0x3: + return 1; + default: + return 0; + } +} + +std::optional +NativeRegisterContextDBReg_loongarch::AdjustWatchpoint( + const WatchpointDetails &details) { + // LoongArch only needs to check the size; it does not need to check the + // address. + size_t size = details.size; + if (size != 1 && size != 2 && size != 4 && size != 8) + return std::nullopt; + + return details; +} + +uint32_t +NativeRegisterContextDBReg_loongarch::MakeBreakControlValue(size_t size) { + // Return encoded hardware breakpoint control value. + return m_hw_dbg_enable_bit; +} + +uint32_t NativeRegisterContextDBReg_loongarch::MakeWatchControlValue( + size_t size, uint32_t watch_flags) { + // Encoding hardware watchpoint control value. + // Size encoded: + // case 1 : 0b11 + // case 2 : 0b10 + // case 4 : 0b01 + // case 8 : 0b00 + size_t encoded_size = (3 - llvm::Log2_32(size)) << 10; + + return m_hw_dbg_enable_bit | encoded_size | (watch_flags << 8); +} diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.h b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.h new file mode 100644 index 000000000000000..19c5e4cdea2635a --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.h @@ -0,0 +1,34 @@ +//===-- NativeRegisterContextDBReg_loongarch.h ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_NativeRegisterContextDBReg_loongarch_h +#define lldb_NativeRegisterContextDBReg_loongarch_h + +#include "Plugins/Process/Utility/NativeRegisterContextDBReg.h" + +namespace lldb_private { + +class NativeRegisterContextDBReg_loongarch : public NativeRegisterContextDBReg { +public: + NativeRegisterContextDBReg_loongarch() + : NativeRegisterContextDBReg(/*enable_bit=*/0x10U) {} + +private: + uint32_t GetWatchpointSize(uint32_t wp_index) override; + + std::optional + AdjustWatchpoint(const WatchpointDetails &details) override; + + uint32_t MakeBreakControlValue(size_t size) override; + + uint32_t MakeWatchControlValue(size_t size, uint32_t watch_flags) override; +}; + +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextDBReg_loongarch_h diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp index 324db3db7eb4c76..c2fe05cad566ef4 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp @@ -231,7 +231,8 @@ GDBRemoteCommunicationServerCommon::Handle_qHostInfo( host_arch.GetMachine() == llvm::Triple::aarch64_32 || host_arch.GetMachine() == llvm::Triple::aarch64_be || host_arch.GetMachine() == llvm::Triple::arm || - host_arch.GetMachine() == llvm::Triple::armeb || host_arch.IsMIPS()) + host_arch.GetMachine() == llvm::Triple::armeb || host_arch.IsMIPS() || + host_arch.GetTriple().isLoongArch()) response.Printf("watchpoint_exceptions_received:before;"); else response.Printf("watchpoint_exceptions_received:after;"); diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index db33525978a16aa..68485a40a3fcce2 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -2517,7 +2517,8 @@ bool Process::GetWatchpointReportedAfter() { llvm::Triple triple = arch.GetTriple(); if (triple.isMIPS() || triple.isPPC64() || triple.isRISCV() || - triple.isAArch64() || triple.isArmMClass() || triple.isARM()) + triple.isAArch64() || triple.isArmMClass() || triple.isARM() || + triple.isLoongArch()) reported_after = false; return reported_after;