Use Intel CPU flags to determine target supported features.

Summary:
This patch uses the instruction CPUID to verify that FXSAVE, XSAVE, AVX
and MPX are supported by the target hardware. In case the HW supports XSAVE,
and at least one of the extended register sets, it further checks if the
target software has the kernel support for such features, by verifying that
their XSAVE part is correctly managed.

Differential Revision: https://reviews.llvm.org/D24559

llvm-svn: 281507
This commit is contained in:
Valentina Giusti 2016-09-14 17:27:48 +00:00
parent e8e0f5cac6
commit 5f957b54c8
3 changed files with 183 additions and 117 deletions

View File

@ -20,6 +20,8 @@
#include "Plugins/Process/Utility/RegisterContextLinux_i386.h"
#include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h"
#include <cpuid.h>
using namespace lldb_private;
using namespace lldb_private::process_linux;
@ -218,6 +220,23 @@ static const RegisterSet g_reg_sets_x86_64[k_num_register_sets] = {
#define NT_PRXFPREG 0x46e62b7f
#endif
// ----------------------------------------------------------------------------
// Required MPX define.
// ----------------------------------------------------------------------------
// Support MPX extensions also if compiled with compiler without MPX support.
#ifndef bit_MPX
#define bit_MPX 0x4000
#endif
// ----------------------------------------------------------------------------
// XCR0 extended register sets masks.
// ----------------------------------------------------------------------------
#define mask_XSTATE_AVX (1ULL << 2)
#define mask_XSTATE_BNDREGS (1ULL << 3)
#define mask_XSTATE_BNDCFG (1ULL << 4)
#define mask_XSTATE_MPX (mask_XSTATE_BNDREGS | mask_XSTATE_BNDCFG)
NativeRegisterContextLinux *
NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(
const ArchSpec &target_arch, NativeThreadProtocol &native_thread,
@ -249,7 +268,7 @@ NativeRegisterContextLinux_x86_64::NativeRegisterContextLinux_x86_64(
uint32_t concrete_frame_idx)
: NativeRegisterContextLinux(native_thread, concrete_frame_idx,
CreateRegisterInfoInterface(target_arch)),
m_fpr_type(eFPRTypeNotValid), m_fpr(), m_iovec(), m_ymm_set(),
m_xstate_type(eXStateTypeNotValid), m_fpr(), m_iovec(), m_ymm_set(),
m_mpx_set(), m_reg_info(), m_gpr_x86_64() {
// Set up data about ranges of valid registers.
switch (target_arch.GetMachine()) {
@ -379,7 +398,7 @@ Error NativeRegisterContextLinux_x86_64::ReadRegister(
return error;
}
if (IsFPR(reg, GetFPRType())) {
if (IsFPR(reg) || IsAVX(reg) || IsMPX(reg)) {
error = ReadFPR();
if (error.Fail())
return error;
@ -428,7 +447,7 @@ Error NativeRegisterContextLinux_x86_64::ReadRegister(
reg_info->byte_size, byte_order);
if (reg >= m_reg_info.first_ymm && reg <= m_reg_info.last_ymm) {
// Concatenate ymm using the register halves in xmm.bytes and ymmh.bytes
if (GetFPRType() == eFPRTypeXSAVE && CopyXSTATEtoYMM(reg, byte_order))
if (CopyXSTATEtoYMM(reg, byte_order))
reg_value.SetBytes(m_ymm_set.ymm[reg - m_reg_info.first_ymm].bytes,
reg_info->byte_size, byte_order);
else {
@ -437,7 +456,7 @@ Error NativeRegisterContextLinux_x86_64::ReadRegister(
}
}
if (reg >= m_reg_info.first_mpxr && reg <= m_reg_info.last_mpxr) {
if (GetFPRType() == eFPRTypeXSAVE && CopyXSTATEtoMPX(reg))
if (CopyXSTATEtoMPX(reg))
reg_value.SetBytes(m_mpx_set.mpxr[reg - m_reg_info.first_mpxr].bytes,
reg_info->byte_size, byte_order);
else {
@ -446,7 +465,7 @@ Error NativeRegisterContextLinux_x86_64::ReadRegister(
}
}
if (reg >= m_reg_info.first_mpxc && reg <= m_reg_info.last_mpxc) {
if (GetFPRType() == eFPRTypeXSAVE && CopyXSTATEtoMPX(reg))
if (CopyXSTATEtoMPX(reg))
reg_value.SetBytes(m_mpx_set.mpxc[reg - m_reg_info.first_mpxc].bytes,
reg_info->byte_size, byte_order);
else {
@ -517,7 +536,7 @@ Error NativeRegisterContextLinux_x86_64::WriteRegister(
if (IsGPR(reg_index))
return WriteRegisterRaw(reg_index, reg_value);
if (IsFPR(reg_index, GetFPRType())) {
if (IsFPR(reg_index) || IsAVX(reg_index) || IsMPX(reg_index)) {
if (reg_info->encoding == lldb::eEncodingVector) {
if (reg_index >= m_reg_info.first_st && reg_index <= m_reg_info.last_st)
::memcpy(
@ -536,9 +555,6 @@ Error NativeRegisterContextLinux_x86_64::WriteRegister(
if (reg_index >= m_reg_info.first_ymm &&
reg_index <= m_reg_info.last_ymm) {
if (GetFPRType() != eFPRTypeXSAVE)
return Error("target processor does not support AVX");
// Store ymm register content, and split into the register halves in
// xmm.bytes and ymmh.bytes
::memcpy(m_ymm_set.ymm[reg_index - m_reg_info.first_ymm].bytes,
@ -549,9 +565,6 @@ Error NativeRegisterContextLinux_x86_64::WriteRegister(
if (reg_index >= m_reg_info.first_mpxr &&
reg_index <= m_reg_info.last_mpxr) {
if (GetFPRType() != eFPRTypeXSAVE)
return Error("target processor does not support MPX");
::memcpy(m_mpx_set.mpxr[reg_index - m_reg_info.first_mpxr].bytes,
reg_value.GetBytes(), reg_value.GetByteSize());
if (!CopyMPXtoXSTATE(reg_index))
@ -560,9 +573,6 @@ Error NativeRegisterContextLinux_x86_64::WriteRegister(
if (reg_index >= m_reg_info.first_mpxc &&
reg_index <= m_reg_info.last_mpxc) {
if (GetFPRType() != eFPRTypeXSAVE)
return Error("target processor does not support MPX");
::memcpy(m_mpx_set.mpxc[reg_index - m_reg_info.first_mpxc].bytes,
reg_value.GetBytes(), reg_value.GetByteSize());
if (!CopyMPXtoXSTATE(reg_index))
@ -654,31 +664,37 @@ Error NativeRegisterContextLinux_x86_64::ReadAllRegisterValues(
::memcpy(dst, &m_gpr_x86_64, GetRegisterInfoInterface().GetGPRSize());
dst += GetRegisterInfoInterface().GetGPRSize();
if (GetFPRType() == eFPRTypeFXSAVE)
if (GetXStateType() == eXStateTypeFXSAVE)
::memcpy(dst, &m_fpr.xstate.fxsave, sizeof(m_fpr.xstate.fxsave));
else if (GetFPRType() == eFPRTypeXSAVE) {
else if (GetXStateType() == eXStateTypeXSAVE) {
lldb::ByteOrder byte_order = GetByteOrder();
// Assemble the YMM register content from the register halves.
for (uint32_t reg = m_reg_info.first_ymm; reg <= m_reg_info.last_ymm;
++reg) {
if (!CopyXSTATEtoYMM(reg, byte_order)) {
error.SetErrorStringWithFormat("NativeRegisterContextLinux_x86_64::%s "
"CopyXSTATEtoYMM() failed for reg num "
"%" PRIu32,
__FUNCTION__, reg);
return error;
if (IsCPUFeatureAvailable(avx)) {
// Assemble the YMM register content from the register halves.
for (uint32_t reg = m_reg_info.first_ymm; reg <= m_reg_info.last_ymm;
++reg) {
if (!CopyXSTATEtoYMM(reg, byte_order)) {
error.SetErrorStringWithFormat(
"NativeRegisterContextLinux_x86_64::%s "
"CopyXSTATEtoYMM() failed for reg num "
"%" PRIu32,
__FUNCTION__, reg);
return error;
}
}
}
for (uint32_t reg = m_reg_info.first_mpxr; reg <= m_reg_info.last_mpxc;
++reg) {
if (!CopyXSTATEtoMPX(reg)) {
error.SetErrorStringWithFormat("NativeRegisterContextLinux_x86_64::%s "
"CopyXSTATEtoMPX() failed for reg num "
"%" PRIu32,
__FUNCTION__, reg);
return error;
if (IsCPUFeatureAvailable(mpx)) {
for (uint32_t reg = m_reg_info.first_mpxr; reg <= m_reg_info.last_mpxc;
++reg) {
if (!CopyXSTATEtoMPX(reg)) {
error.SetErrorStringWithFormat(
"NativeRegisterContextLinux_x86_64::%s "
"CopyXSTATEtoMPX() failed for reg num "
"%" PRIu32,
__FUNCTION__, reg);
return error;
}
}
}
// Copy the extended register state including the assembled ymm registers.
@ -740,38 +756,44 @@ Error NativeRegisterContextLinux_x86_64::WriteAllRegisterValues(
return error;
src += GetRegisterInfoInterface().GetGPRSize();
if (GetFPRType() == eFPRTypeFXSAVE)
if (GetXStateType() == eXStateTypeFXSAVE)
::memcpy(&m_fpr.xstate.fxsave, src, sizeof(m_fpr.xstate.fxsave));
else if (GetFPRType() == eFPRTypeXSAVE)
else if (GetXStateType() == eXStateTypeXSAVE)
::memcpy(&m_fpr.xstate.xsave, src, sizeof(m_fpr.xstate.xsave));
error = WriteFPR();
if (error.Fail())
return error;
if (GetFPRType() == eFPRTypeXSAVE) {
if (GetXStateType() == eXStateTypeXSAVE) {
lldb::ByteOrder byte_order = GetByteOrder();
// Parse the YMM register content from the register halves.
for (uint32_t reg = m_reg_info.first_ymm; reg <= m_reg_info.last_ymm;
++reg) {
if (!CopyYMMtoXSTATE(reg, byte_order)) {
error.SetErrorStringWithFormat("NativeRegisterContextLinux_x86_64::%s "
"CopyYMMtoXSTATE() failed for reg num "
"%" PRIu32,
__FUNCTION__, reg);
return error;
if (IsCPUFeatureAvailable(avx)) {
// Parse the YMM register content from the register halves.
for (uint32_t reg = m_reg_info.first_ymm; reg <= m_reg_info.last_ymm;
++reg) {
if (!CopyYMMtoXSTATE(reg, byte_order)) {
error.SetErrorStringWithFormat(
"NativeRegisterContextLinux_x86_64::%s "
"CopyYMMtoXSTATE() failed for reg num "
"%" PRIu32,
__FUNCTION__, reg);
return error;
}
}
}
for (uint32_t reg = m_reg_info.first_mpxr; reg <= m_reg_info.last_mpxc;
++reg) {
if (!CopyMPXtoXSTATE(reg)) {
error.SetErrorStringWithFormat("NativeRegisterContextLinux_x86_64::%s "
"CopyMPXtoXSTATE() failed for reg num "
"%" PRIu32,
__FUNCTION__, reg);
return error;
if (IsCPUFeatureAvailable(mpx)) {
for (uint32_t reg = m_reg_info.first_mpxr; reg <= m_reg_info.last_mpxc;
++reg) {
if (!CopyMPXtoXSTATE(reg)) {
error.SetErrorStringWithFormat(
"NativeRegisterContextLinux_x86_64::%s "
"CopyMPXtoXSTATE() failed for reg num "
"%" PRIu32,
__FUNCTION__, reg);
return error;
}
}
}
}
@ -779,16 +801,75 @@ Error NativeRegisterContextLinux_x86_64::WriteAllRegisterValues(
return error;
}
bool NativeRegisterContextLinux_x86_64::HasFXSAVE() const {
unsigned int rax, rbx, rcx, rdx;
// Check if FXSAVE is enabled.
if (!__get_cpuid(1, &rax, &rbx, &rcx, &rdx))
return false;
if ((rdx & bit_FXSAVE) == bit_FXSAVE) {
m_xstate_type = eXStateTypeFXSAVE;
if (const_cast<NativeRegisterContextLinux_x86_64 *>(this)->ReadFPR().Fail())
return false;
return true;
}
return false;
}
bool NativeRegisterContextLinux_x86_64::HasXSAVE() const {
unsigned int rax, rbx, rcx, rdx;
// Check if XSAVE is enabled.
if (!__get_cpuid(1, &rax, &rbx, &rcx, &rdx))
return false;
if ((rcx & bit_OSXSAVE) == bit_OSXSAVE) {
m_xstate_type = eXStateTypeXSAVE;
if (const_cast<NativeRegisterContextLinux_x86_64 *>(this)->ReadFPR().Fail())
return false;
return true;
}
return false;
}
bool NativeRegisterContextLinux_x86_64::IsCPUFeatureAvailable(
RegSet feature_code) const {
unsigned int rax, rbx, rcx, rdx;
// Check if XSAVE is enabled.
if (!HasXSAVE())
return false;
__get_cpuid(1, &rax, &rbx, &rcx, &rdx);
switch (feature_code) {
case avx: // Check if CPU has AVX and if there is kernel support, by reading in the XCR0 area of XSAVE.
if (((rcx & bit_AVX) != 0) && ((m_fpr.xstate.xsave.i387.xcr0 & mask_XSTATE_AVX) == mask_XSTATE_AVX))
return true;
case mpx: // Check if CPU has MPX and if there is kernel support, by reading in the XCR0 area of XSAVE.
if (__get_cpuid_max(0, NULL) > 7) {
__cpuid_count(7, 0, rax, rbx, rcx, rdx);
if (((rbx & bit_MPX) != 0) && ((m_fpr.xstate.xsave.i387.xcr0 & mask_XSTATE_MPX) == mask_XSTATE_MPX))
return true;
}
default:
return false;
}
}
bool NativeRegisterContextLinux_x86_64::IsRegisterSetAvailable(
uint32_t set_index) const {
// Note: Extended register sets are assumed to be at the end of g_reg_sets.
uint32_t num_sets = k_num_register_sets - k_num_extended_register_sets;
if (GetFPRType() == eFPRTypeXSAVE) {
// AVX is the first extended register set.
num_sets += 2;
switch (set_index) {
case gpr:
case fpu:
return (set_index < num_sets);
case avx:
return IsCPUFeatureAvailable(avx);
case mpx:
return IsCPUFeatureAvailable(mpx);
default:
return false;
}
return (set_index < num_sets);
}
bool NativeRegisterContextLinux_x86_64::IsGPR(uint32_t reg_index) const {
@ -796,32 +877,15 @@ bool NativeRegisterContextLinux_x86_64::IsGPR(uint32_t reg_index) const {
return reg_index <= m_reg_info.last_gpr;
}
NativeRegisterContextLinux_x86_64::FPRType
NativeRegisterContextLinux_x86_64::GetFPRType() const {
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
if (m_fpr_type == eFPRTypeNotValid) {
// TODO: Use assembly to call cpuid on the inferior and query ebx or ecx.
// Try and see if AVX register retrieval works.
m_fpr_type = eFPRTypeXSAVE;
if (const_cast<NativeRegisterContextLinux_x86_64 *>(this)
->ReadFPR()
.Fail()) {
// Fall back to general floating point with no AVX support.
m_fpr_type = eFPRTypeFXSAVE;
// Check if FXSAVE area can be read.
if (const_cast<NativeRegisterContextLinux_x86_64 *>(this)
->ReadFPR()
.Fail()) {
if (log)
log->Printf("NativeRegisterContextLinux_x86_64::%s ptrace APIs "
"failed to read XSAVE/FXSAVE area",
__FUNCTION__);
}
}
NativeRegisterContextLinux_x86_64::XStateType
NativeRegisterContextLinux_x86_64::GetXStateType() const {
if (m_xstate_type == eXStateTypeNotValid) {
if (HasXSAVE())
m_xstate_type = eXStateTypeXSAVE;
else if (HasFXSAVE())
m_xstate_type = eXStateTypeFXSAVE;
}
return m_fpr_type;
return m_xstate_type;
}
bool NativeRegisterContextLinux_x86_64::IsFPR(uint32_t reg_index) const {
@ -829,21 +893,12 @@ bool NativeRegisterContextLinux_x86_64::IsFPR(uint32_t reg_index) const {
reg_index <= m_reg_info.last_fpr);
}
bool NativeRegisterContextLinux_x86_64::IsFPR(uint32_t reg_index,
FPRType fpr_type) const {
bool generic_fpr = IsFPR(reg_index);
if (fpr_type == eFPRTypeXSAVE)
return generic_fpr || IsAVX(reg_index) || IsMPX(reg_index);
return generic_fpr;
}
Error NativeRegisterContextLinux_x86_64::WriteFPR() {
const FPRType fpr_type = GetFPRType();
const XStateType fpr_type = GetXStateType();
const lldb_private::ArchSpec &target_arch =
GetRegisterInfoInterface().GetTargetArchitecture();
switch (fpr_type) {
case FPRType::eFPRTypeFXSAVE:
case XStateType::eXStateTypeFXSAVE:
// For 32-bit inferiors on x86_32/x86_64 architectures,
// FXSAVE area can be written using PTRACE_SETREGSET ptrace api
// For 64-bit inferiors on x86_64 architectures,
@ -858,7 +913,7 @@ Error NativeRegisterContextLinux_x86_64::WriteFPR() {
assert(false && "Unhandled target architecture.");
break;
}
case FPRType::eFPRTypeXSAVE:
case XStateType::eXStateTypeXSAVE:
return WriteRegisterSet(&m_iovec, sizeof(m_fpr.xstate.xsave),
NT_X86_XSTATE);
default:
@ -867,6 +922,8 @@ Error NativeRegisterContextLinux_x86_64::WriteFPR() {
}
bool NativeRegisterContextLinux_x86_64::IsAVX(uint32_t reg_index) const {
if (!IsCPUFeatureAvailable(avx))
return false;
return (m_reg_info.first_ymm <= reg_index &&
reg_index <= m_reg_info.last_ymm);
}
@ -926,11 +983,11 @@ bool NativeRegisterContextLinux_x86_64::CopyYMMtoXSTATE(
}
void *NativeRegisterContextLinux_x86_64::GetFPRBuffer() {
const FPRType fpr_type = GetFPRType();
switch (fpr_type) {
case FPRType::eFPRTypeFXSAVE:
const XStateType xstate_type = GetXStateType();
switch (xstate_type) {
case XStateType::eXStateTypeFXSAVE:
return &m_fpr.xstate.fxsave;
case FPRType::eFPRTypeXSAVE:
case XStateType::eXStateTypeXSAVE:
return &m_iovec;
default:
return nullptr;
@ -938,11 +995,11 @@ void *NativeRegisterContextLinux_x86_64::GetFPRBuffer() {
}
size_t NativeRegisterContextLinux_x86_64::GetFPRSize() {
const FPRType fpr_type = GetFPRType();
switch (fpr_type) {
case FPRType::eFPRTypeFXSAVE:
const XStateType xstate_type = GetXStateType();
switch (xstate_type) {
case XStateType::eXStateTypeFXSAVE:
return sizeof(m_fpr.xstate.fxsave);
case FPRType::eFPRTypeXSAVE:
case XStateType::eXStateTypeXSAVE:
return sizeof(m_iovec);
default:
return 0;
@ -950,11 +1007,11 @@ size_t NativeRegisterContextLinux_x86_64::GetFPRSize() {
}
Error NativeRegisterContextLinux_x86_64::ReadFPR() {
const FPRType fpr_type = GetFPRType();
const XStateType xstate_type = GetXStateType();
const lldb_private::ArchSpec &target_arch =
GetRegisterInfoInterface().GetTargetArchitecture();
switch (fpr_type) {
case FPRType::eFPRTypeFXSAVE:
switch (xstate_type) {
case XStateType::eXStateTypeFXSAVE:
// For 32-bit inferiors on x86_32/x86_64 architectures,
// FXSAVE area can be read using PTRACE_GETREGSET ptrace api
// For 64-bit inferiors on x86_64 architectures,
@ -968,7 +1025,7 @@ Error NativeRegisterContextLinux_x86_64::ReadFPR() {
assert(false && "Unhandled target architecture.");
break;
}
case FPRType::eFPRTypeXSAVE:
case XStateType::eXStateTypeXSAVE:
return ReadRegisterSet(&m_iovec, sizeof(m_fpr.xstate.xsave), NT_X86_XSTATE);
default:
return Error("Unrecognized FPR type");
@ -976,8 +1033,10 @@ Error NativeRegisterContextLinux_x86_64::ReadFPR() {
}
bool NativeRegisterContextLinux_x86_64::IsMPX(uint32_t reg_index) const {
return (m_reg_info.first_mpxr <= reg_index &&
reg_index <= m_reg_info.last_mpxc);
if (!IsCPUFeatureAvailable(mpx))
return false;
return (m_reg_info.first_mpxr <= reg_index &&
reg_index <= m_reg_info.last_mpxc);
}
bool NativeRegisterContextLinux_x86_64::CopyXSTATEtoMPX(uint32_t reg) {

View File

@ -77,7 +77,8 @@ protected:
private:
// Private member types.
enum FPRType { eFPRTypeNotValid = 0, eFPRTypeFXSAVE, eFPRTypeXSAVE };
enum XStateType { eXStateTypeNotValid = 0, eXStateTypeFXSAVE, eXStateTypeXSAVE };
enum RegSet { gpr, fpu, avx, mpx };
// Info about register ranges.
struct RegInfo {
@ -106,8 +107,8 @@ private:
};
// Private member variables.
mutable FPRType m_fpr_type;
FPR m_fpr;
mutable XStateType m_xstate_type;
FPR m_fpr; // Extended States Area, named FPR for historical reasons.
IOVEC m_iovec;
YMM m_ymm_set;
MPX m_mpx_set;
@ -116,16 +117,20 @@ private:
uint32_t m_fctrl_offset_in_userarea;
// Private member methods.
bool HasFXSAVE() const;
bool HasXSAVE() const;
bool IsCPUFeatureAvailable(RegSet feature_code) const;
bool IsRegisterSetAvailable(uint32_t set_index) const;
bool IsGPR(uint32_t reg_index) const;
FPRType GetFPRType() const;
XStateType GetXStateType() const;
bool IsFPR(uint32_t reg_index) const;
bool IsFPR(uint32_t reg_index, FPRType fpr_type) const;
bool CopyXSTATEtoYMM(uint32_t reg_index, lldb::ByteOrder byte_order);
bool CopyYMMtoXSTATE(uint32_t reg, lldb::ByteOrder byte_order);

View File

@ -277,7 +277,9 @@ struct FXSAVE {
uint32_t mxcsrmask; // MXCSR Mask
MMSReg stmm[8]; // 8*16 bytes for each FP-reg = 128 bytes
XMMReg xmm[16]; // 16*16 bytes for each XMM-reg = 256 bytes
uint32_t padding[24];
uint8_t padding1[48];
uint64_t xcr0;
uint8_t padding2[40];
};
//---------------------------------------------------------------------------