mirror of
https://github.com/anrieff/libcpuid
synced 2024-12-16 16:35:45 +00:00
Merge pull request #22 from X0rg/pull_request
Some new features for function cpu_msrinfo()
This commit is contained in:
commit
08615d76fe
5 changed files with 191 additions and 13 deletions
|
@ -239,10 +239,10 @@ static void load_features_common(struct cpu_raw_data_t* raw, struct cpu_id_t* da
|
|||
}
|
||||
}
|
||||
|
||||
static int cpuid_basic_identify(struct cpu_raw_data_t* raw, struct cpu_id_t* data)
|
||||
static cpu_vendor_t cpuid_vendor_identify(const uint32_t *raw_vendor, char *vendor_str)
|
||||
{
|
||||
int i, j, basic, xmodel, xfamily, ext;
|
||||
char brandstr[64] = {0};
|
||||
int i;
|
||||
cpu_vendor_t vendor = VENDOR_UNKNOWN;
|
||||
const struct { cpu_vendor_t vendor; char match[16]; }
|
||||
matchtable[NUM_CPU_VENDORS] = {
|
||||
/* source: http://www.sandpile.org/ia32/cpuid.htm */
|
||||
|
@ -258,17 +258,26 @@ static int cpuid_basic_identify(struct cpu_raw_data_t* raw, struct cpu_id_t* dat
|
|||
{ VENDOR_NSC , "Geode by NSC" },
|
||||
};
|
||||
|
||||
memcpy(data->vendor_str + 0, &raw->basic_cpuid[0][1], 4);
|
||||
memcpy(data->vendor_str + 4, &raw->basic_cpuid[0][3], 4);
|
||||
memcpy(data->vendor_str + 8, &raw->basic_cpuid[0][2], 4);
|
||||
data->vendor_str[12] = 0;
|
||||
memcpy(vendor_str + 0, &raw_vendor[1], 4);
|
||||
memcpy(vendor_str + 4, &raw_vendor[3], 4);
|
||||
memcpy(vendor_str + 8, &raw_vendor[2], 4);
|
||||
vendor_str[12] = 0;
|
||||
|
||||
/* Determine vendor: */
|
||||
data->vendor = VENDOR_UNKNOWN;
|
||||
for (i = 0; i < NUM_CPU_VENDORS; i++)
|
||||
if (!strcmp(data->vendor_str, matchtable[i].match)) {
|
||||
data->vendor = matchtable[i].vendor;
|
||||
if (!strcmp(vendor_str, matchtable[i].match)) {
|
||||
vendor = matchtable[i].vendor;
|
||||
break;
|
||||
}
|
||||
return vendor;
|
||||
}
|
||||
|
||||
static int cpuid_basic_identify(struct cpu_raw_data_t* raw, struct cpu_id_t* data)
|
||||
{
|
||||
int i, j, basic, xmodel, xfamily, ext;
|
||||
char brandstr[64] = {0};
|
||||
data->vendor = cpuid_vendor_identify(raw->basic_cpuid[0], data->vendor_str);
|
||||
|
||||
if (data->vendor == VENDOR_UNKNOWN)
|
||||
return set_error(ERR_CPU_UNKN);
|
||||
basic = raw->basic_cpuid[0][0];
|
||||
|
@ -638,6 +647,23 @@ void cpuid_set_verbosiness_level(int level)
|
|||
_current_verboselevel = level;
|
||||
}
|
||||
|
||||
cpu_vendor_t cpuid_get_vendor(void)
|
||||
{
|
||||
static cpu_vendor_t vendor = VENDOR_UNKNOWN;
|
||||
uint32_t raw_vendor[4];
|
||||
char vendor_str[VENDOR_STR_MAX];
|
||||
|
||||
if(vendor == VENDOR_UNKNOWN) {
|
||||
if (!cpuid_present())
|
||||
set_error(ERR_NO_CPUID);
|
||||
else {
|
||||
cpu_exec_cpuid(0, raw_vendor);
|
||||
vendor = cpuid_vendor_identify(raw_vendor, vendor_str);
|
||||
}
|
||||
}
|
||||
return vendor;
|
||||
}
|
||||
|
||||
void cpuid_get_cpu_list(cpu_vendor_t vendor, struct cpu_list_t* list)
|
||||
{
|
||||
switch (vendor) {
|
||||
|
|
|
@ -28,3 +28,4 @@ cpu_msrinfo @24
|
|||
cpu_msr_driver_close @25
|
||||
cpu_clock_by_ic @26
|
||||
cpuid_get_total_cpus @27
|
||||
cpu_msr_driver_open_core @28
|
||||
|
|
|
@ -390,6 +390,8 @@ typedef enum {
|
|||
ERR_EXTRACT = -11, /*!< "Cannot extract RDMSR driver (read only media?)" */
|
||||
ERR_HANDLE = -12, /*!< "Bad handle" */
|
||||
ERR_INVMSR = -13, /*!< "Invalid MSR" */
|
||||
ERR_INVCNB = -14, /*!< "Invalid core number" */
|
||||
ERR_HANDLE_R = -15, /*!< "Error on handle read" */
|
||||
} cpu_error_t;
|
||||
|
||||
/**
|
||||
|
@ -752,6 +754,14 @@ libcpuid_warn_fn_t cpuid_set_warn_function(libcpuid_warn_fn_t warn_fun);
|
|||
void cpuid_set_verbosiness_level(int level);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Obtains the CPU vendor from CPUID from the current CPU
|
||||
* @note The result is cached.
|
||||
* @returns VENDOR_UNKNOWN if failed, otherwise the CPU vendor type.
|
||||
* @see cpu_vendor_t
|
||||
*/
|
||||
cpu_vendor_t cpuid_get_vendor(void);
|
||||
|
||||
/**
|
||||
* @brief a structure that holds a list of processor names
|
||||
*/
|
||||
|
@ -804,6 +814,21 @@ void cpuid_free_cpu_list(struct cpu_list_t* list);
|
|||
struct msr_driver_t;
|
||||
struct msr_driver_t* cpu_msr_driver_open(void);
|
||||
|
||||
/**
|
||||
* @brief Similar to \ref cpu_msr_driver_open, but accept one parameter
|
||||
*
|
||||
* This function works on Linux only
|
||||
*
|
||||
* @param core_num specify the core number for MSR.
|
||||
* The first core number is 0.
|
||||
* The last core number is \ref cpuid_get_total_cpus - 1.
|
||||
*
|
||||
* @returns a handle to the driver on success, and NULL on error.
|
||||
* The error message can be obtained by calling \ref cpuid_error.
|
||||
* @see cpu_error_t
|
||||
*/
|
||||
struct msr_driver_t* cpu_msr_driver_open_core(int core_num);
|
||||
|
||||
/**
|
||||
* @brief Reads a Model-Specific Register (MSR)
|
||||
*
|
||||
|
@ -846,6 +871,10 @@ typedef enum {
|
|||
INFO_TEMPERATURE, /*!< The current core temperature in Celsius */
|
||||
INFO_THROTTLING, /*!< 1 if the current logical processor is
|
||||
throttling. 0 if it is running normally. */
|
||||
INFO_VOLTAGE, /*!< The current core voltage in Volt,
|
||||
multiplied by 100. */
|
||||
INFO_BCLK, /*!< The BCLK (base clock) in MHz,
|
||||
multiplied by 100. */
|
||||
} cpu_msrinfo_request_t;
|
||||
|
||||
/**
|
||||
|
|
|
@ -25,3 +25,4 @@ cpu_msrinfo
|
|||
cpu_msr_driver_close
|
||||
cpu_clock_by_ic
|
||||
cpuid_get_total_cpus
|
||||
cpu_msr_driver_open_core
|
||||
|
|
125
libcpuid/rdmsr.c
125
libcpuid/rdmsr.c
|
@ -69,12 +69,24 @@ struct msr_driver_t { int fd; };
|
|||
static int rdmsr_supported(void);
|
||||
struct msr_driver_t* cpu_msr_driver_open(void)
|
||||
{
|
||||
return cpu_msr_driver_open_core(0);
|
||||
}
|
||||
|
||||
struct msr_driver_t* cpu_msr_driver_open_core(int core_num)
|
||||
{
|
||||
char msr[32];
|
||||
struct msr_driver_t* handle;
|
||||
if(core_num < 0 && cpuid_get_total_cpus() <= core_num)
|
||||
{
|
||||
set_error(ERR_INVCNB);
|
||||
return NULL;
|
||||
}
|
||||
if (!rdmsr_supported()) {
|
||||
set_error(ERR_NO_RDMSR);
|
||||
return NULL;
|
||||
}
|
||||
int fd = open("/dev/cpu/0/msr", O_RDONLY);
|
||||
sprintf(msr, "/dev/cpu/%i/msr", core_num);
|
||||
int fd = open(msr, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
if (errno == EIO) {
|
||||
set_error(ERR_NO_RDMSR);
|
||||
|
@ -384,10 +396,65 @@ static int perfmsr_measure(struct msr_driver_t* handle, int msr)
|
|||
}
|
||||
|
||||
#ifndef MSRINFO_DEFINED
|
||||
|
||||
#define IA32_THERM_STATUS 0x19C
|
||||
#define IA32_TEMPERATURE_TARGET 0x1a2
|
||||
#define IA32_PACKAGE_THERM_STATUS 0x1b1
|
||||
#define MSR_PERF_STATUS 0x198
|
||||
#define MSR_TURBO_RATIO_LIMIT 429
|
||||
#define PLATFORM_INFO_MSR 206
|
||||
#define PLATFORM_INFO_MSR_low 8
|
||||
#define PLATFORM_INFO_MSR_high 15
|
||||
|
||||
static int get_bits_value(unsigned long val, int highbit, int lowbit)
|
||||
{
|
||||
unsigned long data = val;
|
||||
int bits = highbit - lowbit + 1;
|
||||
if(bits < 64){
|
||||
data >>= lowbit;
|
||||
data &= (1ULL<<bits) - 1;
|
||||
}
|
||||
return(data);
|
||||
}
|
||||
|
||||
static uint64_t cpu_rdmsr_range(struct msr_driver_t* handle, uint32_t reg, unsigned int highbit,
|
||||
unsigned int lowbit, int* error_indx)
|
||||
{
|
||||
uint64_t data;
|
||||
int bits;
|
||||
*error_indx =0;
|
||||
|
||||
if (pread (handle->fd, &data, sizeof data, reg) != sizeof data)
|
||||
{
|
||||
*error_indx = 1;
|
||||
return set_error(ERR_HANDLE_R);
|
||||
}
|
||||
|
||||
bits = highbit - lowbit + 1;
|
||||
if (bits < 64)
|
||||
{
|
||||
/* Show only part of register */
|
||||
data >>= lowbit;
|
||||
data &= (1ULL << bits) - 1;
|
||||
}
|
||||
|
||||
/* Make sure we get sign correct */
|
||||
if (data & (1ULL << (bits - 1)))
|
||||
{
|
||||
data &= ~(1ULL << (bits - 1));
|
||||
data = -data;
|
||||
}
|
||||
|
||||
*error_indx = 0;
|
||||
return (data);
|
||||
}
|
||||
|
||||
int cpu_msrinfo(struct msr_driver_t* handle, cpu_msrinfo_request_t which)
|
||||
{
|
||||
uint64_t r;
|
||||
int err;
|
||||
int err, error_indx, cur_clock;
|
||||
static int max_clock = 0, multiplier = 0;
|
||||
static double bclk = 0.0;
|
||||
|
||||
if (handle == NULL)
|
||||
return set_error(ERR_HANDLE);
|
||||
|
@ -398,18 +465,72 @@ int cpu_msrinfo(struct msr_driver_t* handle, cpu_msrinfo_request_t which)
|
|||
return perfmsr_measure(handle, 0xe8);
|
||||
case INFO_CUR_MULTIPLIER:
|
||||
{
|
||||
if(cpuid_get_vendor() == VENDOR_INTEL)
|
||||
{
|
||||
if(!bclk)
|
||||
bclk = (double) cpu_msrinfo(handle, INFO_BCLK) / 100;
|
||||
if(bclk > 0)
|
||||
{
|
||||
cur_clock = cpu_clock_by_ic(10, 4);
|
||||
if(cur_clock > 0)
|
||||
return (int) (cur_clock / bclk * 100);
|
||||
}
|
||||
}
|
||||
err = cpu_rdmsr(handle, 0x2a, &r);
|
||||
if (err) return CPU_INVALID_VALUE;
|
||||
return (int) ((r>>22) & 0x1f) * 100;
|
||||
}
|
||||
case INFO_MAX_MULTIPLIER:
|
||||
{
|
||||
if(cpuid_get_vendor() == VENDOR_INTEL)
|
||||
{
|
||||
if(!multiplier)
|
||||
multiplier = (int) cpu_rdmsr_range(handle, PLATFORM_INFO_MSR, PLATFORM_INFO_MSR_high, PLATFORM_INFO_MSR_low, &error_indx);
|
||||
if(multiplier > 0)
|
||||
return multiplier * 100;
|
||||
}
|
||||
err = cpu_rdmsr(handle, 0x198, &r);
|
||||
if (err) return CPU_INVALID_VALUE;
|
||||
return (int) ((r >> 40) & 0x1f) * 100;
|
||||
}
|
||||
case INFO_TEMPERATURE:
|
||||
if(cpuid_get_vendor() == VENDOR_INTEL)
|
||||
{
|
||||
// https://github.com/ajaiantilal/i7z/blob/5023138d7c35c4667c938b853e5ea89737334e92/helper_functions.c#L59
|
||||
unsigned long val = cpu_rdmsr_range(handle, IA32_THERM_STATUS, 63, 0, &error_indx);
|
||||
int digital_readout = get_bits_value(val, 23, 16);
|
||||
int thermal_status = get_bits_value(val, 32, 31);
|
||||
val = cpu_rdmsr_range(handle, IA32_TEMPERATURE_TARGET, 63, 0, &error_indx);
|
||||
int PROCHOT_temp = get_bits_value(val, 23, 16);
|
||||
|
||||
// These bits are thermal status : 1 if supported, 0 else
|
||||
if(thermal_status)
|
||||
return(PROCHOT_temp - digital_readout); // Temperature is prochot - digital readout
|
||||
}
|
||||
return CPU_INVALID_VALUE;
|
||||
case INFO_THROTTLING:
|
||||
return CPU_INVALID_VALUE;
|
||||
case INFO_VOLTAGE:
|
||||
{
|
||||
if(cpuid_get_vendor() == VENDOR_INTEL)
|
||||
{
|
||||
unsigned long val = cpu_rdmsr_range(handle, MSR_PERF_STATUS, 47, 32, &error_indx);
|
||||
double ret = (double) val / (1 << 13);
|
||||
return (ret > 0) ? ret * 100 : CPU_INVALID_VALUE;
|
||||
}
|
||||
return CPU_INVALID_VALUE;
|
||||
}
|
||||
case INFO_BCLK:
|
||||
{
|
||||
if(!max_clock)
|
||||
max_clock = cpu_clock_measure(100, 1); // Return the non-Turbo clock
|
||||
if(!multiplier)
|
||||
multiplier = cpu_msrinfo(handle, INFO_MAX_MULTIPLIER) / 100;
|
||||
if(max_clock > 0 && multiplier > 0)
|
||||
return (int) ((double) max_clock / multiplier * 100);
|
||||
|
||||
return CPU_INVALID_VALUE;
|
||||
}
|
||||
default:
|
||||
return CPU_INVALID_VALUE;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue