forked from OSchip/llvm-project
[LLDB] Add ptrace register access for AArch64 SVE registers
This patch adds NativeRegisterContext_arm64 ptrace routines to access AArch64 SVE register set. This patch also adds a test-case to test AArch64 SVE register access and dynamic size configuration capability. Reviewed By: labath Differential Revision: https://reviews.llvm.org/D79699
This commit is contained in:
parent
46fc9a0dfc
commit
567ba6c468
|
@ -21,14 +21,17 @@
|
|||
#include "Plugins/Process/Linux/NativeProcessLinux.h"
|
||||
#include "Plugins/Process/Linux/Procfs.h"
|
||||
#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
|
||||
#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"
|
||||
|
||||
// System includes - They have to be included after framework includes because
|
||||
// they define some macros which collide with variable names in other modules
|
||||
#include <sys/socket.h>
|
||||
// NT_PRSTATUS and NT_FPREGSET definition
|
||||
#include <elf.h>
|
||||
// user_hwdebug_state definition
|
||||
#include <asm/ptrace.h>
|
||||
|
||||
#ifndef NT_ARM_SVE
|
||||
#define NT_ARM_SVE 0x405 /* ARM Scalable Vector Extension */
|
||||
#endif
|
||||
|
||||
#define REG_CONTEXT_SIZE (GetGPRSize() + GetFPRSize())
|
||||
|
||||
|
@ -59,14 +62,21 @@ NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64(
|
|||
::memset(&m_gpr_arm64, 0, sizeof(m_gpr_arm64));
|
||||
::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs));
|
||||
::memset(&m_hbr_regs, 0, sizeof(m_hbr_regs));
|
||||
::memset(&m_sve_header, 0, sizeof(m_sve_header));
|
||||
|
||||
// 16 is just a maximum value, query hardware for actual watchpoint count
|
||||
m_max_hwp_supported = 16;
|
||||
m_max_hbp_supported = 16;
|
||||
|
||||
m_refresh_hwdebug_info = true;
|
||||
|
||||
m_gpr_is_valid = false;
|
||||
m_fpu_is_valid = false;
|
||||
m_sve_buffer_is_valid = false;
|
||||
m_sve_header_is_valid = false;
|
||||
|
||||
// SVE is not enabled until we query user_sve_header
|
||||
m_sve_state = SVEState::Unknown;
|
||||
}
|
||||
|
||||
RegisterInfoPOSIX_arm64 &
|
||||
|
@ -109,28 +119,96 @@ NativeRegisterContextLinux_arm64::ReadRegister(const RegisterInfo *reg_info,
|
|||
|
||||
uint8_t *src;
|
||||
uint32_t offset;
|
||||
uint64_t sve_vg;
|
||||
std::vector<uint8_t> sve_reg_non_live;
|
||||
|
||||
if (IsGPR(reg)) {
|
||||
if (!m_gpr_is_valid) {
|
||||
error = ReadGPR();
|
||||
if (error.Fail())
|
||||
return error;
|
||||
}
|
||||
error = ReadGPR();
|
||||
if (error.Fail())
|
||||
return error;
|
||||
|
||||
offset = reg_info->byte_offset;
|
||||
assert(offset < GetGPRSize());
|
||||
src = (uint8_t *)GetGPRBuffer() + offset;
|
||||
|
||||
} else if (IsFPR(reg)) {
|
||||
if (!m_fpu_is_valid) {
|
||||
|
||||
if (m_sve_state == SVEState::Disabled) {
|
||||
// SVE is disabled take legacy route for FPU register access
|
||||
error = ReadFPR();
|
||||
if (error.Fail())
|
||||
return error;
|
||||
|
||||
offset = CalculateFprOffset(reg_info);
|
||||
assert(offset < GetFPRSize());
|
||||
src = (uint8_t *)GetFPRBuffer() + offset;
|
||||
} else {
|
||||
// SVE enabled, we will read and cache SVE ptrace data
|
||||
error = ReadAllSVE();
|
||||
if (error.Fail())
|
||||
return error;
|
||||
|
||||
// FPSR and FPCR will be located right after Z registers in
|
||||
// SVEState::FPSIMD while in SVEState::Full they will be located at the
|
||||
// end of register data after an alignment correction based on currently
|
||||
// selected vector length.
|
||||
uint32_t sve_reg_num = LLDB_INVALID_REGNUM;
|
||||
if (reg == GetRegisterInfo().GetRegNumFPSR()) {
|
||||
sve_reg_num = reg;
|
||||
if (m_sve_state == SVEState::Full)
|
||||
offset = SVE_PT_SVE_FPSR_OFFSET(sve_vq_from_vl(m_sve_header.vl));
|
||||
else if (m_sve_state == SVEState::FPSIMD)
|
||||
offset = SVE_PT_FPSIMD_OFFSET + (32 * 16);
|
||||
} else if (reg == GetRegisterInfo().GetRegNumFPCR()) {
|
||||
sve_reg_num = reg;
|
||||
if (m_sve_state == SVEState::Full)
|
||||
offset = SVE_PT_SVE_FPCR_OFFSET(sve_vq_from_vl(m_sve_header.vl));
|
||||
else if (m_sve_state == SVEState::FPSIMD)
|
||||
offset = SVE_PT_FPSIMD_OFFSET + (32 * 16) + 4;
|
||||
} else {
|
||||
// Extract SVE Z register value register number for this reg_info
|
||||
if (reg_info->value_regs &&
|
||||
reg_info->value_regs[0] != LLDB_INVALID_REGNUM)
|
||||
sve_reg_num = reg_info->value_regs[0];
|
||||
offset = CalculateSVEOffset(GetRegisterInfoAtIndex(sve_reg_num));
|
||||
}
|
||||
|
||||
offset = CalculateSVEOffset(reg_info);
|
||||
assert(offset < GetSVEBufferSize());
|
||||
src = (uint8_t *)GetSVEBuffer() + offset;
|
||||
}
|
||||
} else if (IsSVE(reg)) {
|
||||
|
||||
if (m_sve_state == SVEState::Disabled || m_sve_state == SVEState::Unknown)
|
||||
return Status("SVE disabled or not supported");
|
||||
|
||||
if (GetRegisterInfo().IsSVERegVG(reg)) {
|
||||
sve_vg = GetSVERegVG();
|
||||
src = (uint8_t *)&sve_vg;
|
||||
} else {
|
||||
// SVE enabled, we will read and cache SVE ptrace data
|
||||
error = ReadAllSVE();
|
||||
if (error.Fail())
|
||||
return error;
|
||||
|
||||
if (m_sve_state == SVEState::FPSIMD) {
|
||||
// In FPSIMD state SVE payload mirrors legacy fpsimd struct and so
|
||||
// just copy 16 bytes of v register to the start of z register. All
|
||||
// other SVE register will be set to zero.
|
||||
sve_reg_non_live.resize(reg_info->byte_size, 0);
|
||||
src = sve_reg_non_live.data();
|
||||
|
||||
if (GetRegisterInfo().IsSVEZReg(reg)) {
|
||||
offset = CalculateSVEOffset(reg_info);
|
||||
assert(offset < GetSVEBufferSize());
|
||||
::memcpy(sve_reg_non_live.data(), (uint8_t *)GetSVEBuffer() + offset,
|
||||
16);
|
||||
}
|
||||
} else {
|
||||
offset = CalculateSVEOffset(reg_info);
|
||||
assert(offset < GetSVEBufferSize());
|
||||
src = (uint8_t *)GetSVEBuffer() + offset;
|
||||
}
|
||||
}
|
||||
offset = CalculateFprOffset(reg_info);
|
||||
assert(offset < GetFPRSize());
|
||||
src = (uint8_t *)GetFPRBuffer() + offset;
|
||||
} else
|
||||
return Status("failed - register wasn't recognized to be a GPR or an FPR, "
|
||||
"write strategy unknown");
|
||||
|
@ -157,37 +235,125 @@ Status NativeRegisterContextLinux_arm64::WriteRegister(
|
|||
|
||||
uint8_t *dst;
|
||||
uint32_t offset;
|
||||
std::vector<uint8_t> sve_reg_non_live;
|
||||
|
||||
if (IsGPR(reg)) {
|
||||
if (!m_gpr_is_valid) {
|
||||
error = ReadGPR();
|
||||
if (error.Fail())
|
||||
return error;
|
||||
}
|
||||
|
||||
offset = reg_info->byte_offset;
|
||||
assert(offset < GetGPRSize());
|
||||
dst = (uint8_t *)GetGPRBuffer() + offset;
|
||||
error = ReadGPR();
|
||||
if (error.Fail())
|
||||
return error;
|
||||
|
||||
assert(reg_info->byte_offset < GetGPRSize());
|
||||
dst = (uint8_t *)GetGPRBuffer() + reg_info->byte_offset;
|
||||
::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
|
||||
|
||||
return WriteGPR();
|
||||
} else if (IsFPR(reg)) {
|
||||
if (!m_fpu_is_valid) {
|
||||
if (m_sve_state == SVEState::Disabled) {
|
||||
// SVE is disabled take legacy route for FPU register access
|
||||
error = ReadFPR();
|
||||
if (error.Fail())
|
||||
return error;
|
||||
|
||||
offset = CalculateFprOffset(reg_info);
|
||||
assert(offset < GetFPRSize());
|
||||
dst = (uint8_t *)GetFPRBuffer() + offset;
|
||||
::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
|
||||
|
||||
return WriteFPR();
|
||||
} else {
|
||||
// SVE enabled, we will read and cache SVE ptrace data
|
||||
error = ReadAllSVE();
|
||||
if (error.Fail())
|
||||
return error;
|
||||
|
||||
// FPSR and FPCR will be located right after Z registers in
|
||||
// SVEState::FPSIMD while in SVEState::Full they will be located at the
|
||||
// end of register data after an alignment correction based on currently
|
||||
// selected vector length.
|
||||
uint32_t sve_reg_num = LLDB_INVALID_REGNUM;
|
||||
if (reg == GetRegisterInfo().GetRegNumFPSR()) {
|
||||
sve_reg_num = reg;
|
||||
if (m_sve_state == SVEState::Full)
|
||||
offset = SVE_PT_SVE_FPSR_OFFSET(sve_vq_from_vl(m_sve_header.vl));
|
||||
else if (m_sve_state == SVEState::FPSIMD)
|
||||
offset = SVE_PT_FPSIMD_OFFSET + (32 * 16);
|
||||
} else if (reg == GetRegisterInfo().GetRegNumFPCR()) {
|
||||
sve_reg_num = reg;
|
||||
if (m_sve_state == SVEState::Full)
|
||||
offset = SVE_PT_SVE_FPCR_OFFSET(sve_vq_from_vl(m_sve_header.vl));
|
||||
else if (m_sve_state == SVEState::FPSIMD)
|
||||
offset = SVE_PT_FPSIMD_OFFSET + (32 * 16) + 4;
|
||||
} else {
|
||||
// Extract SVE Z register value register number for this reg_info
|
||||
if (reg_info->value_regs &&
|
||||
reg_info->value_regs[0] != LLDB_INVALID_REGNUM)
|
||||
sve_reg_num = reg_info->value_regs[0];
|
||||
offset = CalculateSVEOffset(GetRegisterInfoAtIndex(sve_reg_num));
|
||||
}
|
||||
|
||||
offset = CalculateSVEOffset(reg_info);
|
||||
assert(offset < GetSVEBufferSize());
|
||||
dst = (uint8_t *)GetSVEBuffer() + offset;
|
||||
::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
|
||||
return WriteAllSVE();
|
||||
}
|
||||
offset = CalculateFprOffset(reg_info);
|
||||
assert(offset < GetFPRSize());
|
||||
dst = (uint8_t *)GetFPRBuffer() + offset;
|
||||
} else if (IsSVE(reg)) {
|
||||
if (m_sve_state == SVEState::Disabled || m_sve_state == SVEState::Unknown)
|
||||
return Status("SVE disabled or not supported");
|
||||
else {
|
||||
if (GetRegisterInfo().IsSVERegVG(reg))
|
||||
return Status("SVE state change operation not supported");
|
||||
|
||||
::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
|
||||
// Target has SVE enabled, we will read and cache SVE ptrace data
|
||||
error = ReadAllSVE();
|
||||
if (error.Fail())
|
||||
return error;
|
||||
|
||||
return WriteFPR();
|
||||
// If target supports SVE but currently in FPSIMD mode.
|
||||
if (m_sve_state == SVEState::FPSIMD) {
|
||||
// Here we will check if writing this SVE register enables
|
||||
// SVEState::Full
|
||||
bool set_sve_state_full = false;
|
||||
const uint8_t *reg_bytes = (const uint8_t *)reg_value.GetBytes();
|
||||
if (GetRegisterInfo().IsSVEZReg(reg)) {
|
||||
for (uint32_t i = 16; i < reg_info->byte_size; i++) {
|
||||
if (reg_bytes[i]) {
|
||||
set_sve_state_full = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (GetRegisterInfo().IsSVEPReg(reg) ||
|
||||
reg == GetRegisterInfo().GetRegNumSVEFFR()) {
|
||||
for (uint32_t i = 0; i < reg_info->byte_size; i++) {
|
||||
if (reg_bytes[i]) {
|
||||
set_sve_state_full = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!set_sve_state_full && GetRegisterInfo().IsSVEZReg(reg)) {
|
||||
// We are writing a Z register which is zero beyond 16 bytes so copy
|
||||
// first 16 bytes only as SVE payload mirrors legacy fpsimd structure
|
||||
offset = CalculateSVEOffset(reg_info);
|
||||
assert(offset < GetSVEBufferSize());
|
||||
dst = (uint8_t *)GetSVEBuffer() + offset;
|
||||
::memcpy(dst, reg_value.GetBytes(), 16);
|
||||
|
||||
return WriteAllSVE();
|
||||
} else
|
||||
return Status("SVE state change operation not supported");
|
||||
} else {
|
||||
offset = CalculateSVEOffset(reg_info);
|
||||
assert(offset < GetSVEBufferSize());
|
||||
dst = (uint8_t *)GetSVEBuffer() + offset;
|
||||
::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
|
||||
return WriteAllSVE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
return Status("Failed to write register value");
|
||||
}
|
||||
|
||||
Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues(
|
||||
|
@ -195,17 +361,15 @@ Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues(
|
|||
Status error;
|
||||
|
||||
data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0));
|
||||
if (!m_gpr_is_valid) {
|
||||
error = ReadGPR();
|
||||
if (error.Fail())
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!m_fpu_is_valid) {
|
||||
error = ReadFPR();
|
||||
if (error.Fail())
|
||||
return error;
|
||||
}
|
||||
error = ReadGPR();
|
||||
if (error.Fail())
|
||||
return error;
|
||||
|
||||
error = ReadFPR();
|
||||
if (error.Fail())
|
||||
return error;
|
||||
|
||||
uint8_t *dst = data_sp->GetBytes();
|
||||
::memcpy(dst, GetGPRBuffer(), GetGPRSize());
|
||||
dst += GetGPRSize();
|
||||
|
@ -271,6 +435,13 @@ bool NativeRegisterContextLinux_arm64::IsFPR(unsigned reg) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool NativeRegisterContextLinux_arm64::IsSVE(unsigned reg) const {
|
||||
if (GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) ==
|
||||
RegisterInfoPOSIX_arm64::SVERegSet)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t NativeRegisterContextLinux_arm64::NumSupportedHardwareBreakpoints() {
|
||||
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_BREAKPOINTS));
|
||||
|
||||
|
@ -762,8 +933,10 @@ Status NativeRegisterContextLinux_arm64::WriteHardwareDebugRegs(int hwbType) {
|
|||
Status NativeRegisterContextLinux_arm64::ReadGPR() {
|
||||
Status error;
|
||||
|
||||
struct iovec ioVec;
|
||||
if (m_gpr_is_valid)
|
||||
return error;
|
||||
|
||||
struct iovec ioVec;
|
||||
ioVec.iov_base = GetGPRBuffer();
|
||||
ioVec.iov_len = GetGPRSize();
|
||||
|
||||
|
@ -776,21 +949,26 @@ Status NativeRegisterContextLinux_arm64::ReadGPR() {
|
|||
}
|
||||
|
||||
Status NativeRegisterContextLinux_arm64::WriteGPR() {
|
||||
Status error = ReadGPR();
|
||||
if (error.Fail())
|
||||
return error;
|
||||
|
||||
struct iovec ioVec;
|
||||
|
||||
m_gpr_is_valid = false;
|
||||
|
||||
ioVec.iov_base = GetGPRBuffer();
|
||||
ioVec.iov_len = GetGPRSize();
|
||||
|
||||
m_gpr_is_valid = false;
|
||||
|
||||
return WriteRegisterSet(&ioVec, GetGPRSize(), NT_PRSTATUS);
|
||||
}
|
||||
|
||||
Status NativeRegisterContextLinux_arm64::ReadFPR() {
|
||||
Status error;
|
||||
|
||||
struct iovec ioVec;
|
||||
if (m_fpu_is_valid)
|
||||
return error;
|
||||
|
||||
struct iovec ioVec;
|
||||
ioVec.iov_base = GetFPRBuffer();
|
||||
ioVec.iov_len = GetFPRSize();
|
||||
|
||||
|
@ -803,19 +981,122 @@ Status NativeRegisterContextLinux_arm64::ReadFPR() {
|
|||
}
|
||||
|
||||
Status NativeRegisterContextLinux_arm64::WriteFPR() {
|
||||
Status error = ReadFPR();
|
||||
if (error.Fail())
|
||||
return error;
|
||||
|
||||
struct iovec ioVec;
|
||||
|
||||
m_fpu_is_valid = false;
|
||||
|
||||
ioVec.iov_base = GetFPRBuffer();
|
||||
ioVec.iov_len = GetFPRSize();
|
||||
|
||||
m_fpu_is_valid = false;
|
||||
|
||||
return WriteRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET);
|
||||
}
|
||||
|
||||
void NativeRegisterContextLinux_arm64::InvalidateAllRegisters() {
|
||||
m_gpr_is_valid = false;
|
||||
m_fpu_is_valid = false;
|
||||
m_sve_buffer_is_valid = false;
|
||||
m_sve_header_is_valid = false;
|
||||
|
||||
// Update SVE registers in case there is change in configuration.
|
||||
ConfigureRegisterContext();
|
||||
}
|
||||
|
||||
Status NativeRegisterContextLinux_arm64::ReadSVEHeader() {
|
||||
Status error;
|
||||
|
||||
if (m_sve_header_is_valid)
|
||||
return error;
|
||||
|
||||
struct iovec ioVec;
|
||||
ioVec.iov_base = GetSVEHeader();
|
||||
ioVec.iov_len = GetSVEHeaderSize();
|
||||
|
||||
error = ReadRegisterSet(&ioVec, GetSVEHeaderSize(), NT_ARM_SVE);
|
||||
|
||||
m_sve_header_is_valid = true;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
Status NativeRegisterContextLinux_arm64::WriteSVEHeader() {
|
||||
Status error;
|
||||
|
||||
error = ReadSVEHeader();
|
||||
if (error.Fail())
|
||||
return error;
|
||||
|
||||
struct iovec ioVec;
|
||||
ioVec.iov_base = GetSVEHeader();
|
||||
ioVec.iov_len = GetSVEHeaderSize();
|
||||
|
||||
m_sve_buffer_is_valid = false;
|
||||
m_sve_header_is_valid = false;
|
||||
m_fpu_is_valid = false;
|
||||
|
||||
return WriteRegisterSet(&ioVec, GetSVEHeaderSize(), NT_ARM_SVE);
|
||||
}
|
||||
|
||||
Status NativeRegisterContextLinux_arm64::ReadAllSVE() {
|
||||
Status error;
|
||||
|
||||
if (m_sve_buffer_is_valid)
|
||||
return error;
|
||||
|
||||
struct iovec ioVec;
|
||||
ioVec.iov_base = GetSVEBuffer();
|
||||
ioVec.iov_len = GetSVEBufferSize();
|
||||
|
||||
error = ReadRegisterSet(&ioVec, GetSVEBufferSize(), NT_ARM_SVE);
|
||||
|
||||
if (error.Success())
|
||||
m_sve_buffer_is_valid = true;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
Status NativeRegisterContextLinux_arm64::WriteAllSVE() {
|
||||
Status error;
|
||||
|
||||
error = ReadAllSVE();
|
||||
if (error.Fail())
|
||||
return error;
|
||||
|
||||
struct iovec ioVec;
|
||||
|
||||
ioVec.iov_base = GetSVEBuffer();
|
||||
ioVec.iov_len = GetSVEBufferSize();
|
||||
|
||||
m_sve_buffer_is_valid = false;
|
||||
m_sve_header_is_valid = false;
|
||||
m_fpu_is_valid = false;
|
||||
|
||||
return WriteRegisterSet(&ioVec, GetSVEBufferSize(), NT_ARM_SVE);
|
||||
}
|
||||
|
||||
void NativeRegisterContextLinux_arm64::ConfigureRegisterContext() {
|
||||
// Read SVE configuration data and configure register infos.
|
||||
if (!m_sve_header_is_valid && m_sve_state != SVEState::Disabled) {
|
||||
Status error = ReadSVEHeader();
|
||||
if (!error.Success() && m_sve_state == SVEState::Unknown) {
|
||||
m_sve_state = SVEState::Disabled;
|
||||
GetRegisterInfo().ConfigureVectorRegisterInfos(
|
||||
RegisterInfoPOSIX_arm64::eVectorQuadwordAArch64);
|
||||
} else {
|
||||
if ((m_sve_header.flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_FPSIMD)
|
||||
m_sve_state = SVEState::FPSIMD;
|
||||
else if ((m_sve_header.flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_SVE)
|
||||
m_sve_state = SVEState::Full;
|
||||
|
||||
uint32_t vq = RegisterInfoPOSIX_arm64::eVectorQuadwordAArch64SVE;
|
||||
if (sve_vl_valid(m_sve_header.vl))
|
||||
vq = sve_vq_from_vl(m_sve_header.vl);
|
||||
GetRegisterInfo().ConfigureVectorRegisterInfos(vq);
|
||||
m_sve_ptrace_payload.resize(SVE_PT_SIZE(vq, SVE_PT_REGS_SVE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t NativeRegisterContextLinux_arm64::CalculateFprOffset(
|
||||
|
@ -823,4 +1104,27 @@ uint32_t NativeRegisterContextLinux_arm64::CalculateFprOffset(
|
|||
return reg_info->byte_offset - GetGPRSize();
|
||||
}
|
||||
|
||||
uint32_t NativeRegisterContextLinux_arm64::CalculateSVEOffset(
|
||||
const RegisterInfo *reg_info) const {
|
||||
// Start of Z0 data is after GPRs plus 8 bytes of vg register
|
||||
uint32_t sve_reg_offset = LLDB_INVALID_INDEX32;
|
||||
if (m_sve_state == SVEState::FPSIMD) {
|
||||
const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
|
||||
sve_reg_offset =
|
||||
SVE_PT_FPSIMD_OFFSET + (reg - GetRegisterInfo().GetRegNumSVEZ0()) * 16;
|
||||
} else if (m_sve_state == SVEState::Full) {
|
||||
uint32_t sve_z0_offset = GetGPRSize() + 8;
|
||||
sve_reg_offset =
|
||||
SVE_SIG_REGS_OFFSET + reg_info->byte_offset - sve_z0_offset;
|
||||
}
|
||||
return sve_reg_offset;
|
||||
}
|
||||
|
||||
void *NativeRegisterContextLinux_arm64::GetSVEBuffer() {
|
||||
if (m_sve_state == SVEState::FPSIMD)
|
||||
return m_sve_ptrace_payload.data() + SVE_PT_FPSIMD_OFFSET;
|
||||
|
||||
return m_sve_ptrace_payload.data();
|
||||
}
|
||||
|
||||
#endif // defined (__arm64__) || defined (__aarch64__)
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#include "Plugins/Process/Linux/NativeRegisterContextLinux.h"
|
||||
#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"
|
||||
|
||||
#include <asm/ptrace.h>
|
||||
|
||||
namespace lldb_private {
|
||||
namespace process_linux {
|
||||
|
||||
|
@ -97,11 +99,19 @@ protected:
|
|||
private:
|
||||
bool m_gpr_is_valid;
|
||||
bool m_fpu_is_valid;
|
||||
bool m_sve_buffer_is_valid;
|
||||
|
||||
bool m_sve_header_is_valid;
|
||||
|
||||
RegisterInfoPOSIX_arm64::GPR m_gpr_arm64; // 64-bit general purpose registers.
|
||||
|
||||
RegisterInfoPOSIX_arm64::FPU
|
||||
m_fpr; // floating-point registers including extended register sets.
|
||||
|
||||
SVEState m_sve_state;
|
||||
struct user_sve_header m_sve_header;
|
||||
std::vector<uint8_t> m_sve_ptrace_payload;
|
||||
|
||||
// Debug register info for hardware breakpoints and watchpoints management.
|
||||
struct DREG {
|
||||
lldb::addr_t address; // Breakpoint/watchpoint address value.
|
||||
|
@ -123,6 +133,28 @@ private:
|
|||
|
||||
bool IsFPR(unsigned reg) const;
|
||||
|
||||
Status ReadAllSVE();
|
||||
|
||||
Status WriteAllSVE();
|
||||
|
||||
Status ReadSVEHeader();
|
||||
|
||||
Status WriteSVEHeader();
|
||||
|
||||
bool IsSVE(unsigned reg) const;
|
||||
|
||||
uint64_t GetSVERegVG() { return m_sve_header.vl / 8; }
|
||||
|
||||
void SetSVERegVG(uint64_t vg) { m_sve_header.vl = vg * 8; }
|
||||
|
||||
void *GetSVEHeader() { return &m_sve_header; }
|
||||
|
||||
void *GetSVEBuffer();
|
||||
|
||||
size_t GetSVEHeaderSize() { return sizeof(m_sve_header); }
|
||||
|
||||
size_t GetSVEBufferSize() { return m_sve_ptrace_payload.size(); }
|
||||
|
||||
Status ReadHardwareDebugInfo();
|
||||
|
||||
Status WriteHardwareDebugRegs(int hwbType);
|
||||
|
@ -130,6 +162,10 @@ private:
|
|||
uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const;
|
||||
|
||||
RegisterInfoPOSIX_arm64 &GetRegisterInfo() const;
|
||||
|
||||
void ConfigureRegisterContext();
|
||||
|
||||
uint32_t CalculateSVEOffset(const RegisterInfo *reg_info) const;
|
||||
};
|
||||
|
||||
} // namespace process_linux
|
||||
|
|
|
@ -66,7 +66,9 @@ protected:
|
|||
uint32_t GetRegNumSVEZ0() const {
|
||||
return m_register_info_up->GetRegNumSVEZ0();
|
||||
}
|
||||
|
||||
uint32_t GetRegNumSVEFFR() const {
|
||||
return m_register_info_up->GetRegNumSVEFFR();
|
||||
}
|
||||
uint32_t GetRegNumFPCR() const { return m_register_info_up->GetRegNumFPCR(); }
|
||||
uint32_t GetRegNumFPSR() const { return m_register_info_up->GetRegNumFPSR(); }
|
||||
|
||||
|
|
|
@ -336,6 +336,8 @@ bool RegisterInfoPOSIX_arm64::IsSVERegVG(unsigned reg) const {
|
|||
|
||||
uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEZ0() const { return sve_z0; }
|
||||
|
||||
uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEFFR() const { return sve_ffr; }
|
||||
|
||||
uint32_t RegisterInfoPOSIX_arm64::GetRegNumFPCR() const { return fpu_fpcr; }
|
||||
|
||||
uint32_t RegisterInfoPOSIX_arm64::GetRegNumFPSR() const { return fpu_fpsr; }
|
||||
|
|
|
@ -98,6 +98,7 @@ public:
|
|||
bool IsSVERegVG(unsigned reg) const;
|
||||
|
||||
uint32_t GetRegNumSVEZ0() const;
|
||||
uint32_t GetRegNumSVEFFR() const;
|
||||
uint32_t GetRegNumFPCR() const;
|
||||
uint32_t GetRegNumFPSR() const;
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
C_SOURCES := main.c
|
||||
|
||||
CFLAGS_EXTRAS := -march=armv8-a+sve
|
||||
|
||||
include Makefile.rules
|
|
@ -0,0 +1,176 @@
|
|||
"""
|
||||
Test the AArch64 SVE registers.
|
||||
"""
|
||||
|
||||
import lldb
|
||||
from lldbsuite.test.decorators import *
|
||||
from lldbsuite.test.lldbtest import *
|
||||
from lldbsuite.test import lldbutil
|
||||
|
||||
|
||||
class RegisterCommandsTestCase(TestBase):
|
||||
|
||||
def targetHasSVE(self):
|
||||
triple = self.dbg.GetSelectedPlatform().GetTriple()
|
||||
|
||||
# TODO other platforms, please implement this function
|
||||
if not re.match(".*-.*-linux", triple):
|
||||
return False
|
||||
|
||||
# Need to do something different for non-Linux/Android targets
|
||||
cpuinfo_path = self.getBuildArtifact("cpuinfo")
|
||||
if configuration.lldb_platform_name:
|
||||
self.runCmd('platform get-file "/proc/cpuinfo" ' + cpuinfo_path)
|
||||
|
||||
f = open(cpuinfo_path, 'r')
|
||||
cpuinfo = f.read()
|
||||
f.close()
|
||||
return " sve " in cpuinfo
|
||||
|
||||
def check_sve_register_size(self, set, name, expected):
|
||||
reg_value = set.GetChildMemberWithName(name)
|
||||
self.assertTrue(reg_value.IsValid(),
|
||||
'Verify we have a register named "%s"' % (name))
|
||||
self.assertEqual(reg_value.GetByteSize(), expected,
|
||||
'Verify "%s" == %i' % (name, expected))
|
||||
|
||||
mydir = TestBase.compute_mydir(__file__)
|
||||
|
||||
@no_debug_info_test
|
||||
@skipIf(archs=no_match(["aarch64"]))
|
||||
@skipIf(oslist=no_match(['linux']))
|
||||
def test_sve_registers_configuration(self):
|
||||
"""Test AArch64 SVE registers size configuration."""
|
||||
self.build()
|
||||
self.line = line_number('main.c', '// Set a break point here.')
|
||||
|
||||
exe = self.getBuildArtifact("a.out")
|
||||
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
|
||||
|
||||
if not self.targetHasSVE():
|
||||
self.skipTest('SVE registers must be supported.')
|
||||
|
||||
lldbutil.run_break_set_by_file_and_line(
|
||||
self, "main.c", self.line, num_expected_locations=1)
|
||||
self.runCmd("run", RUN_SUCCEEDED)
|
||||
|
||||
self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT,
|
||||
substrs=["stop reason = breakpoint 1."])
|
||||
|
||||
target = self.dbg.GetSelectedTarget()
|
||||
process = target.GetProcess()
|
||||
thread = process.GetThreadAtIndex(0)
|
||||
currentFrame = thread.GetFrameAtIndex(0)
|
||||
|
||||
has_sve = False
|
||||
for registerSet in currentFrame.GetRegisters():
|
||||
if 'Scalable Vector Extension Registers' in registerSet.GetName():
|
||||
has_sve = True
|
||||
|
||||
registerSets = process.GetThreadAtIndex(
|
||||
0).GetFrameAtIndex(0).GetRegisters()
|
||||
|
||||
sve_registers = registerSets.GetValueAtIndex(2)
|
||||
|
||||
vg_reg = sve_registers.GetChildMemberWithName("vg")
|
||||
|
||||
vg_reg_value = sve_registers.GetChildMemberWithName(
|
||||
"vg").GetValueAsUnsigned()
|
||||
|
||||
z_reg_size = vg_reg_value * 8
|
||||
|
||||
p_reg_size = z_reg_size / 8
|
||||
|
||||
for i in range(32):
|
||||
self.check_sve_register_size(
|
||||
sve_registers, 'z%i' % (i), z_reg_size)
|
||||
|
||||
for i in range(16):
|
||||
self.check_sve_register_size(
|
||||
sve_registers, 'p%i' % (i), p_reg_size)
|
||||
|
||||
self.check_sve_register_size(sve_registers, 'ffr', p_reg_size)
|
||||
|
||||
@no_debug_info_test
|
||||
@skipIf(archs=no_match(["aarch64"]))
|
||||
@skipIf(oslist=no_match(['linux']))
|
||||
def test_sve_registers_read_write(self):
|
||||
"""Test AArch64 SVE registers read and write."""
|
||||
self.build()
|
||||
self.line = line_number('main.c', '// Set a break point here.')
|
||||
|
||||
exe = self.getBuildArtifact("a.out")
|
||||
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
|
||||
|
||||
if not self.targetHasSVE():
|
||||
self.skipTest('SVE registers must be supported.')
|
||||
|
||||
lldbutil.run_break_set_by_file_and_line(
|
||||
self, "main.c", self.line, num_expected_locations=1)
|
||||
self.runCmd("run", RUN_SUCCEEDED)
|
||||
|
||||
self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT,
|
||||
substrs=["stop reason = breakpoint 1."])
|
||||
|
||||
target = self.dbg.GetSelectedTarget()
|
||||
process = target.GetProcess()
|
||||
thread = process.GetThreadAtIndex(0)
|
||||
currentFrame = thread.GetFrameAtIndex(0)
|
||||
|
||||
has_sve = False
|
||||
for registerSet in currentFrame.GetRegisters():
|
||||
if 'Scalable Vector Extension Registers' in registerSet.GetName():
|
||||
has_sve = True
|
||||
|
||||
registerSets = process.GetThreadAtIndex(
|
||||
0).GetFrameAtIndex(0).GetRegisters()
|
||||
|
||||
sve_registers = registerSets.GetValueAtIndex(2)
|
||||
|
||||
vg_reg = sve_registers.GetChildMemberWithName("vg")
|
||||
|
||||
vg_reg_value = sve_registers.GetChildMemberWithName(
|
||||
"vg").GetValueAsUnsigned()
|
||||
|
||||
z_reg_size = vg_reg_value * 8
|
||||
|
||||
p_reg_size = int(z_reg_size / 8)
|
||||
|
||||
for i in range(32):
|
||||
z_regs_value = '{' + \
|
||||
' '.join('0x{:02x}'.format(i + 1)
|
||||
for _ in range(z_reg_size)) + '}'
|
||||
self.expect("register read " + 'z%i' %
|
||||
(i), substrs=[z_regs_value])
|
||||
|
||||
p_value_bytes = ['0xff', '0x55', '0x11', '0x01', '0x00']
|
||||
for i in range(16):
|
||||
p_regs_value = '{' + \
|
||||
' '.join(p_value_bytes[i % 5] for _ in range(p_reg_size)) + '}'
|
||||
self.expect("register read " + 'p%i' % (i), substrs=[p_regs_value])
|
||||
|
||||
self.expect("register read ffr", substrs=[p_regs_value])
|
||||
|
||||
z_regs_value = '{' + \
|
||||
' '.join(('0x9d' for _ in range(z_reg_size))) + '}'
|
||||
|
||||
p_regs_value = '{' + \
|
||||
' '.join(('0xee' for _ in range(p_reg_size))) + '}'
|
||||
|
||||
for i in range(32):
|
||||
self.runCmd('register write ' + 'z%i' %
|
||||
(i) + " '" + z_regs_value + "'")
|
||||
|
||||
for i in range(32):
|
||||
self.expect("register read " + 'z%i' % (i), substrs=[z_regs_value])
|
||||
|
||||
for i in range(16):
|
||||
self.runCmd('register write ' + 'p%i' %
|
||||
(i) + " '" + p_regs_value + "'")
|
||||
|
||||
for i in range(16):
|
||||
self.expect("register read " + 'p%i' % (i), substrs=[p_regs_value])
|
||||
|
||||
self.runCmd('register write ' + 'ffr ' + "'" + p_regs_value + "'")
|
||||
|
||||
self.expect("register read " + 'ffr', substrs=[p_regs_value])
|
|
@ -0,0 +1,53 @@
|
|||
int main() {
|
||||
asm volatile("setffr\n\t");
|
||||
asm volatile("ptrue p0.b\n\t");
|
||||
asm volatile("ptrue p1.h\n\t");
|
||||
asm volatile("ptrue p2.s\n\t");
|
||||
asm volatile("ptrue p3.d\n\t");
|
||||
asm volatile("pfalse p4.b\n\t");
|
||||
asm volatile("ptrue p5.b\n\t");
|
||||
asm volatile("ptrue p6.h\n\t");
|
||||
asm volatile("ptrue p7.s\n\t");
|
||||
asm volatile("ptrue p8.d\n\t");
|
||||
asm volatile("pfalse p9.b\n\t");
|
||||
asm volatile("ptrue p10.b\n\t");
|
||||
asm volatile("ptrue p11.h\n\t");
|
||||
asm volatile("ptrue p12.s\n\t");
|
||||
asm volatile("ptrue p13.d\n\t");
|
||||
asm volatile("pfalse p14.b\n\t");
|
||||
asm volatile("ptrue p15.b\n\t");
|
||||
|
||||
asm volatile("cpy z0.b, p0/z, #1\n\t");
|
||||
asm volatile("cpy z1.b, p5/z, #2\n\t");
|
||||
asm volatile("cpy z2.b, p10/z, #3\n\t");
|
||||
asm volatile("cpy z3.b, p15/z, #4\n\t");
|
||||
asm volatile("cpy z4.b, p0/z, #5\n\t");
|
||||
asm volatile("cpy z5.b, p5/z, #6\n\t");
|
||||
asm volatile("cpy z6.b, p10/z, #7\n\t");
|
||||
asm volatile("cpy z7.b, p15/z, #8\n\t");
|
||||
asm volatile("cpy z8.b, p0/z, #9\n\t");
|
||||
asm volatile("cpy z9.b, p5/z, #10\n\t");
|
||||
asm volatile("cpy z10.b, p10/z, #11\n\t");
|
||||
asm volatile("cpy z11.b, p15/z, #12\n\t");
|
||||
asm volatile("cpy z12.b, p0/z, #13\n\t");
|
||||
asm volatile("cpy z13.b, p5/z, #14\n\t");
|
||||
asm volatile("cpy z14.b, p10/z, #15\n\t");
|
||||
asm volatile("cpy z15.b, p15/z, #16\n\t");
|
||||
asm volatile("cpy z16.b, p0/z, #17\n\t");
|
||||
asm volatile("cpy z17.b, p5/z, #18\n\t");
|
||||
asm volatile("cpy z18.b, p10/z, #19\n\t");
|
||||
asm volatile("cpy z19.b, p15/z, #20\n\t");
|
||||
asm volatile("cpy z20.b, p0/z, #21\n\t");
|
||||
asm volatile("cpy z21.b, p5/z, #22\n\t");
|
||||
asm volatile("cpy z22.b, p10/z, #23\n\t");
|
||||
asm volatile("cpy z23.b, p15/z, #24\n\t");
|
||||
asm volatile("cpy z24.b, p0/z, #25\n\t");
|
||||
asm volatile("cpy z25.b, p5/z, #26\n\t");
|
||||
asm volatile("cpy z26.b, p10/z, #27\n\t");
|
||||
asm volatile("cpy z27.b, p15/z, #28\n\t");
|
||||
asm volatile("cpy z28.b, p0/z, #29\n\t");
|
||||
asm volatile("cpy z29.b, p5/z, #30\n\t");
|
||||
asm volatile("cpy z30.b, p10/z, #31\n\t");
|
||||
asm volatile("cpy z31.b, p15/z, #32\n\t");
|
||||
return 0; // Set a break point here.
|
||||
}
|
Loading…
Reference in New Issue