Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Intel Meteor Lake #196

Merged
merged 5 commits into from
Apr 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 103 additions & 83 deletions libcpuid/cpuid_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,31 @@ static void cache_instances_t_constructor(struct internal_cache_instances_t* dat
memset(data->htable, 0, sizeof(data->htable));
}

static void type_info_array_t_constructor(struct internal_type_info_array_t* data)
{
data->num = 0;
data->data = NULL;
}

static int16_t cpuid_find_index_system_id(struct system_id_t* system, cpu_purpose_t purpose,
struct internal_type_info_array_t* type_info, int32_t package_id, bool is_apic_supported)
{
int16_t i = 0;

if (is_apic_supported) {
for (i = 0; i < system->num_cpu_types; i++)
if ((system->cpu_types[i].purpose == purpose) && (type_info->data[i].package_id == package_id))
return i;
}
else {
for (i = 0; i < system->num_cpu_types; i++)
if (system->cpu_types[i].purpose == purpose)
return i;
}

return -1;
}

static void cpuid_grow_raw_data_array(struct cpu_raw_data_array_t* raw_array, logical_cpu_t n)
{
logical_cpu_t i;
Expand Down Expand Up @@ -156,6 +181,34 @@ static void cpuid_grow_system_id(struct system_id_t* system, uint8_t n)
system->cpu_types = tmp;
}

static void cpuid_grow_type_info(struct internal_type_info_array_t* type_info, uint8_t n)
{
uint8_t i;
struct internal_type_info_t *tmp = NULL;

if ((n <= 0) || (n < type_info->num)) return;
debugf(3, "Growing internal_type_info_t from %u to %u items\n", type_info->num, n);
tmp = realloc(type_info->data, sizeof(struct internal_type_info_t) * n);
if (tmp == NULL) { /* Memory allocation failure */
cpuid_set_error(ERR_NO_MEM);
return;
}

for (i = type_info->num; i < n; i++) {
core_instances_t_constructor(&tmp[i].core_instances);
cache_instances_t_constructor(&tmp[i].cache_instances);
}
type_info->num = n;
type_info->data = tmp;
}

static void cpuid_free_type_info(struct internal_type_info_array_t* type_info)
{
if (type_info->num <= 0) return;
free(type_info->data);
type_info->num = 0;
}

/* get_total_cpus() system specific code: uses OS routines to determine total number of CPUs */
#ifdef __APPLE__
#include <unistd.h>
Expand Down Expand Up @@ -1244,23 +1297,19 @@ int cpu_identify_all(struct cpu_raw_data_array_t* raw_array, struct system_id_t*
int cur_error = cpuid_set_error(ERR_OK);
int ret_error = cpuid_set_error(ERR_OK);
double smt_divisor;
bool is_new_cpu_type;
bool is_last_item;
bool is_smt_supported;
bool is_apic_supported = true;
uint8_t cpu_type_index = 0;
int32_t num_logical_cpus = 0;
int16_t cpu_type_index = -1;
int32_t cur_package_id = 0;
int32_t prev_package_id = 0;
logical_cpu_t logical_cpu = 0;
cpu_purpose_t purpose;
cpu_affinity_mask_t affinity_mask;
struct cpu_raw_data_array_t my_raw_array;
struct internal_id_info_t id_info;
struct internal_apic_info_t apic_info;
struct internal_core_instances_t cores_type;
struct internal_cache_instances_t caches_type, caches_all;
struct internal_type_info_array_t type_info;
struct internal_cache_instances_t caches_all;

/* Init variables */
if (system == NULL)
return cpuid_set_error(ERR_HANDLE);
if (!raw_array) {
Expand All @@ -1269,16 +1318,13 @@ int cpu_identify_all(struct cpu_raw_data_array_t* raw_array, struct system_id_t*
raw_array = &my_raw_array;
}
system_id_t_constructor(system);
core_instances_t_constructor(&cores_type);
cache_instances_t_constructor(&caches_type);
type_info_array_t_constructor(&type_info);
cache_instances_t_constructor(&caches_all);
if (raw_array->with_affinity)
init_affinity_mask(&affinity_mask);

/* Iterate over all raw */
for (logical_cpu = 0; logical_cpu < raw_array->num_raw; logical_cpu++) {
is_new_cpu_type = false;
is_last_item = (logical_cpu + 1 >= raw_array->num_raw);
debugf(2, "Identifying logical core %u\n", logical_cpu);
/* Get CPU purpose and APIC ID
For hybrid CPUs, the purpose may be different than the previous iteration (e.g. from P-cores to E-cores)
Expand All @@ -1291,82 +1337,59 @@ int cpu_identify_all(struct cpu_raw_data_array_t* raw_array, struct system_id_t*
cur_package_id = apic_info.package_id;
}

/* Put data to system->cpu_types on the first iteration or when purpose is different than previous core */
if ((system->num_cpu_types == 0) || (purpose != system->cpu_types[system->num_cpu_types - 1].purpose) || (cur_package_id != prev_package_id)) {
is_new_cpu_type = true;
/* Put data to system->cpu_types on the first iteration or when purpose is new.
For motherboards with multiple CPUs, we grow the array when package ID is different. */
cpu_type_index = cpuid_find_index_system_id(system, purpose, &type_info, cur_package_id, is_apic_supported);
if (cpu_type_index < 0) {
cpu_type_index = system->num_cpu_types;
cpuid_grow_system_id(system, system->num_cpu_types + 1);
cur_error = cpu_ident_internal(&raw_array->raw[logical_cpu], &system->cpu_types[cpu_type_index], &id_info);
cpuid_grow_type_info(&type_info, type_info.num + 1);
cur_error = cpu_ident_internal(&raw_array->raw[logical_cpu], &system->cpu_types[cpu_type_index], &type_info.data[cpu_type_index].id_info);
type_info.data[cpu_type_index].purpose = purpose;
if (ret_error == ERR_OK)
ret_error = cur_error;
if (is_apic_supported)
type_info.data[cpu_type_index].package_id = cur_package_id;
if (raw_array->with_affinity)
system->cpu_types[cpu_type_index].num_logical_cpus = 0;
}
/* Increment counters only for current purpose */
if (raw_array->with_affinity && ((logical_cpu == 0) || !is_new_cpu_type)) {
set_affinity_mask_bit(logical_cpu, &affinity_mask);
num_logical_cpus++;

/* Increment counters */
if (raw_array->with_affinity) {
set_affinity_mask_bit(logical_cpu, &system->cpu_types[cpu_type_index].affinity_mask);
system->cpu_types[cpu_type_index].num_logical_cpus++;
if (is_apic_supported) {
update_core_instances(&cores_type, &apic_info);
update_cache_instances(&caches_type, &apic_info, &id_info, true);
update_cache_instances(&caches_all, &apic_info, &id_info, false);
update_core_instances(&type_info.data[cpu_type_index].core_instances, &apic_info);
update_cache_instances(&type_info.data[cpu_type_index].cache_instances, &apic_info, &type_info.data[cpu_type_index].id_info, true);
update_cache_instances(&caches_all, &apic_info, &type_info.data[cpu_type_index].id_info, false);
}
}
}

/* Update logical CPU counters, physical CPU counters and cache instance in system->cpu_types
Note: we need to differenciate two events:
- is_new_cpu_type (e.g. purpose was 'efficiency' during previous loop, and now purpose is 'performance')
- is_last_item (i.e. this is the last iteration in raw_array->num_raw)
In some cases, both events can occur during the same iteration, thus we have to update counters twice for the same logical_cpu.
This occurs with single-core CPU type. For instance, Pentacore Lakefield CPU consists of:
- 1 "big" Sunny Cove core
- 4 "little" Tremont cores
On the last iteration, there is no need to reset values for the next purpose.
*/
if (raw_array->with_affinity && (is_last_item || (is_new_cpu_type && (system->num_cpu_types > 1)))) {
enum event_t {
EVENT_NEW_CPU_TYPE = 0,
EVENT_LAST_ITEM = 1
};
const enum event_t first_event = is_new_cpu_type && (system->num_cpu_types > 1) ? EVENT_NEW_CPU_TYPE : EVENT_LAST_ITEM;
const enum event_t last_event = is_last_item ? EVENT_LAST_ITEM : EVENT_NEW_CPU_TYPE;
for (enum event_t event = first_event; event <= last_event; event++) {
switch (event) {
case EVENT_NEW_CPU_TYPE: cpu_type_index = system->num_cpu_types - 2; break;
case EVENT_LAST_ITEM: cpu_type_index = system->num_cpu_types - 1; break;
default: warnf("Warning: event %i in cpu_identify_all() not handled.\n", event); return cpuid_set_error(ERR_NOT_IMP);
}
copy_affinity_mask(&system->cpu_types[cpu_type_index].affinity_mask, &affinity_mask);
if (event != EVENT_LAST_ITEM) {
init_affinity_mask(&affinity_mask);
set_affinity_mask_bit(logical_cpu, &affinity_mask);
}
if (is_apic_supported) {
system->cpu_types[cpu_type_index].num_cores = cores_type.instances;
system->cpu_types[cpu_type_index].l1_instruction_instances = caches_type.instances[L1I];
system->cpu_types[cpu_type_index].l1_data_instances = caches_type.instances[L1D];
system->cpu_types[cpu_type_index].l2_instances = caches_type.instances[L2];
system->cpu_types[cpu_type_index].l3_instances = caches_type.instances[L3];
system->cpu_types[cpu_type_index].l4_instances = caches_type.instances[L4];
if (event != EVENT_LAST_ITEM) {
core_instances_t_constructor(&cores_type);
cache_instances_t_constructor(&caches_type);
update_core_instances(&cores_type, &apic_info);
update_cache_instances(&caches_type, &apic_info, &id_info, true);
update_cache_instances(&caches_all, &apic_info, &id_info, false);
}
}
else {
/* Note: if SMT is disabled by BIOS, smt_divisor will no reflect the current state properly */
is_smt_supported = system->cpu_types[cpu_type_index].num_cores > 0 ? (system->cpu_types[cpu_type_index].num_logical_cpus % system->cpu_types[cpu_type_index].num_cores) == 0 : false;
smt_divisor = is_smt_supported ? system->cpu_types[cpu_type_index].num_logical_cpus / system->cpu_types[cpu_type_index].num_cores : 1.0;
system->cpu_types[cpu_type_index].num_cores = (int32_t) (num_logical_cpus / smt_divisor);
}
/* Save current values in system->cpu_types[cpu_type_index] and reset values for the next purpose */
system->cpu_types[cpu_type_index].num_logical_cpus = num_logical_cpus;
num_logical_cpus = 1;
/* Update counters for all CPU types */
for (cpu_type_index = 0; cpu_type_index < system->num_cpu_types; cpu_type_index++) {
/* Overwrite core and cache counters when information is available per core */
if (raw_array->with_affinity) {
if (is_apic_supported) {
system->cpu_types[cpu_type_index].num_cores = type_info.data[cpu_type_index].core_instances.instances;
system->cpu_types[cpu_type_index].l1_instruction_instances = type_info.data[cpu_type_index].cache_instances.instances[L1I];
system->cpu_types[cpu_type_index].l1_data_instances = type_info.data[cpu_type_index].cache_instances.instances[L1D];
system->cpu_types[cpu_type_index].l2_instances = type_info.data[cpu_type_index].cache_instances.instances[L2];
system->cpu_types[cpu_type_index].l3_instances = type_info.data[cpu_type_index].cache_instances.instances[L3];
system->cpu_types[cpu_type_index].l4_instances = type_info.data[cpu_type_index].cache_instances.instances[L4];
}
else {
/* Note: if SMT is disabled by BIOS, smt_divisor will no reflect the current state properly */
is_smt_supported = system->cpu_types[cpu_type_index].num_cores > 0 ? (system->cpu_types[cpu_type_index].num_logical_cpus % system->cpu_types[cpu_type_index].num_cores) == 0 : false;
smt_divisor = is_smt_supported ? system->cpu_types[cpu_type_index].num_logical_cpus / system->cpu_types[cpu_type_index].num_cores : 1.0;
system->cpu_types[cpu_type_index].num_cores = (int32_t) (system->cpu_types[cpu_type_index].num_logical_cpus / smt_divisor);
}
}
prev_package_id = cur_package_id;

/* Update the total_logical_cpus value for each purpose */
system->cpu_types[cpu_type_index].total_logical_cpus = logical_cpu;
}
cpuid_free_type_info(&type_info);

/* Update the grand total of cache instances */
if (is_apic_supported) {
Expand All @@ -1377,10 +1400,6 @@ int cpu_identify_all(struct cpu_raw_data_array_t* raw_array, struct system_id_t*
system->l4_total_instances = caches_all.instances[L4];
}

/* Update the total_logical_cpus value for each purpose */
for (cpu_type_index = 0; cpu_type_index < system->num_cpu_types; cpu_type_index++)
system->cpu_types[cpu_type_index].total_logical_cpus = logical_cpu;

return ret_error;
}

Expand Down Expand Up @@ -1429,9 +1448,10 @@ const char* cpu_purpose_str(cpu_purpose_t purpose)
{
const struct { cpu_purpose_t purpose; const char* name; }
matchtable[] = {
{ PURPOSE_GENERAL, "general" },
{ PURPOSE_PERFORMANCE, "performance" },
{ PURPOSE_EFFICIENCY, "efficiency" },
{ PURPOSE_GENERAL, "general" },
{ PURPOSE_PERFORMANCE, "performance" },
{ PURPOSE_EFFICIENCY, "efficiency" },
{ PURPOSE_LP_EFFICIENCY, "low-power efficiency" },
};
unsigned i, n = COUNT_OF(matchtable);
if (n != NUM_CPU_PURPOSES) {
Expand Down
9 changes: 5 additions & 4 deletions libcpuid/libcpuid.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,12 @@ typedef enum {
* @brief CPU purpose
*/
typedef enum {
PURPOSE_GENERAL = 0, /*!< general purpose CPU */
PURPOSE_PERFORMANCE, /*!< performance CPU */
PURPOSE_EFFICIENCY, /*!< efficiency CPU */
PURPOSE_GENERAL = 0, /*!< general purpose CPU */
PURPOSE_PERFORMANCE, /*!< performance CPU */
PURPOSE_EFFICIENCY, /*!< efficiency CPU */
PURPOSE_LP_EFFICIENCY, /*!< low-power efficiency CPU */

NUM_CPU_PURPOSES, /*!< Valid CPU purpose ids: 0..NUM_CPU_PURPOSES - 1 */
NUM_CPU_PURPOSES, /*!< Valid CPU purpose ids: 0..NUM_CPU_PURPOSES - 1 */
} cpu_purpose_t;
#define NUM_CPU_PURPOSES NUM_CPU_PURPOSES

Expand Down
14 changes: 14 additions & 0 deletions libcpuid/libcpuid_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,19 @@ struct internal_cache_instances_t {
struct internal_cache_id_t htable[NUM_CACHE_TYPES][CACHES_HTABLE_SIZE];
};

struct internal_type_info_t {
cpu_purpose_t purpose;
int32_t package_id;
struct internal_id_info_t id_info;
struct internal_core_instances_t core_instances;
struct internal_cache_instances_t cache_instances;
};

struct internal_type_info_array_t {
uint8_t num;
struct internal_type_info_t* data;
};

#define LBIT(x) (((long long) 1) << x)

enum _common_bits_t {
Expand Down Expand Up @@ -145,6 +158,7 @@ enum _intel_bits_t {
_MAX_ = LBIT( 25 ),
_J_ = LBIT( 26 ),
_N_ = LBIT( 27 ),
_ULTRA_ = LBIT( 28 ),
};
typedef enum _intel_bits_t intel_bits_t;

Expand Down
48 changes: 45 additions & 3 deletions libcpuid/recog_intel.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ enum _intel_model_t {
_x2xx, /* Xeon Bronze/Silver/Gold/Platinum x2xx */
_x3xx, /* Xeon Bronze/Silver/Gold/Platinum x3xx */
_x4xx, /* Xeon Bronze/Silver/Gold/Platinum/Max x4xx */
_1xx, /* Core Ultra [579] 1xx */
};
typedef enum _intel_model_t intel_model_t;

Expand Down Expand Up @@ -536,6 +537,13 @@ const struct match_entry_t cpudb_intel[] = {
{ 6, 15, -1, -1, 143, -1, -1, -1, NC, XEON_|_GOLD_ , _x4xx, "Sapphire Rapids-SP (Xeon Gold)" },
{ 6, 15, -1, -1, 143, -1, -1, -1, NC, XEON_|_SILVER_ , _x4xx, "Sapphire Rapids-SP (Xeon Silver)" },
{ 6, 15, -1, -1, 143, -1, -1, -1, NC, XEON_|_BRONZE_ , _x4xx, "Sapphire Rapids-SP (Xeon Bronze)" },

/* Meteor Lake CPUs (2023, 1st Core Ultra gen, Intel 4) => https://en.wikichip.org/wiki/intel/microarchitectures/meteor_lake */
{ 6, 10, -1, -1, 170, -1, -1, -1, NC, CORE_|_ULTRA_|_9|_H, _x1xx, "Meteor Lake-H (Core Ultra 9)" },
{ 6, 10, -1, -1, 170, -1, -1, -1, NC, CORE_|_ULTRA_|_7|_H, _x1xx, "Meteor Lake-H (Core Ultra 7)" },
{ 6, 10, -1, -1, 170, -1, -1, -1, NC, CORE_|_ULTRA_|_5|_H, _x1xx, "Meteor Lake-H (Core Ultra 5)" },
{ 6, 10, -1, -1, 170, -1, -1, -1, NC, CORE_|_ULTRA_|_7|_U, _x1xx, "Meteor Lake-U (Core Ultra 7)" },
{ 6, 10, -1, -1, 170, -1, -1, -1, NC, CORE_|_ULTRA_|_5|_U, _x1xx, "Meteor Lake-U (Core Ultra 5)" },
/* F M S EF EM #cores L2$ L3$ BC ModelBits ModelCode Name */


Expand Down Expand Up @@ -795,6 +803,27 @@ static intel_code_and_bits_t get_brand_code_and_bits(struct cpu_id_t* data)
}
}
}
if ((i = match_pattern(bs, "Core(TM) Ultra [579]")) != 0) {
bits |= CORE_ | _ULTRA_;
i--;
switch (bs[i + 15]) {
//case '3': bits |= _3; break;
case '5': bits |= _5; break;
case '7': bits |= _7; break;
case '9': bits |= _9; break;
}
for(i = i + 16; i < n; i++) {
switch (bs[i]) {
case 'H': bits |= _H; break;
//case 'K': bits |= _K; break;
//case 'N': bits |= _N; break;
//case 'P': bits |= _P; break;
//case 'S': bits |= _S; break;
case 'U': bits |= _U; break;
//case 'X': bits |= _X; break;
}
}
}
else if ((i = match_pattern(bs, "Xeon(R) w[3579]")) != 0) {
bits |= XEON_;
i--;
Expand Down Expand Up @@ -917,6 +946,12 @@ static intel_model_t get_model_code(struct cpu_id_t* data)
if ((bs[i] == '1') && (bs[i+1] == '4')) return _14xxx;
return UNKNOWN;
}
else if ((i = match_pattern(bs, "Core(TM) Ultra [579]")) != 0) {
i += 16;
if (i + 3 >= l) return UNKNOWN;
if (bs[i] == '1') return _1xx;
return UNKNOWN;
}
else if ((i = match_pattern(bs, "Xeon(R) [WBSGP]")) != 0) {
i = 0;
if ((i = match_pattern(bs, "Xeon(R) W-")) != 0) i += 10;
Expand Down Expand Up @@ -1110,9 +1145,16 @@ cpu_purpose_t cpuid_identify_purpose_intel(struct cpu_raw_data_t* raw)
if (EXTRACTS_BIT(raw->basic_cpuid[0x7][EDX], 15) == 0x1) {
debugf(3, "Detected Intel CPU hybrid architecture\n");
switch (EXTRACTS_BITS(raw->basic_cpuid[0x1a][EAX], 31, 24)) {
case 0x20: /* Atom */ return PURPOSE_EFFICIENCY;
case 0x40: /* Core */ return PURPOSE_PERFORMANCE;
default: return PURPOSE_GENERAL;
case 0x20: /* Atom */
/* Acccording to Ramyer M. from Intel, LP E-Cores do not have a L3 cache
https://community.intel.com/t5/Processors/Detecting-LP-E-Cores-on-Meteor-Lake-in-software/m-p/1584555/highlight/true#M70732
If sub-leaf 3 is set, it is an E-Cores.
*/
return (EXTRACTS_BITS(raw->intel_fn4[3][EAX], 31, 0)) ? PURPOSE_EFFICIENCY : PURPOSE_LP_EFFICIENCY;
case 0x40: /* Core */
return PURPOSE_PERFORMANCE;
default:
return PURPOSE_GENERAL;
}
}

Expand Down
Loading
Loading