|
#include <stdint.h> |
|
|
|
#include <cpuinfo.h> |
|
#include <x86/cpuid.h> |
|
#include <cpuinfo/utils.h> |
|
#include <cpuinfo/log.h> |
|
|
|
|
|
enum cache_type { |
|
cache_type_none = 0, |
|
cache_type_data = 1, |
|
cache_type_instruction = 2, |
|
cache_type_unified = 3, |
|
}; |
|
|
|
bool cpuinfo_x86_decode_deterministic_cache_parameters( |
|
struct cpuid_regs regs, |
|
struct cpuinfo_x86_caches* cache, |
|
uint32_t* package_cores_max) |
|
{ |
|
const uint32_t type = regs.eax & UINT32_C(0x1F); |
|
if (type == cache_type_none) { |
|
return false; |
|
} |
|
|
|
|
|
const uint32_t level = (regs.eax >> 5) & UINT32_C(0x7); |
|
|
|
const uint32_t sets = 1 + regs.ecx; |
|
const uint32_t line_size = 1 + (regs.ebx & UINT32_C(0x00000FFF)); |
|
const uint32_t partitions = 1 + ((regs.ebx >> 12) & UINT32_C(0x000003FF)); |
|
const uint32_t associativity = 1 + (regs.ebx >> 22); |
|
|
|
*package_cores_max = 1 + (regs.eax >> 26); |
|
const uint32_t processors = 1 + ((regs.eax >> 14) & UINT32_C(0x00000FFF)); |
|
const uint32_t apic_bits = bit_length(processors); |
|
|
|
uint32_t flags = 0; |
|
if (regs.edx & UINT32_C(0x00000002)) { |
|
flags |= CPUINFO_CACHE_INCLUSIVE; |
|
} |
|
if (regs.edx & UINT32_C(0x00000004)) { |
|
flags |= CPUINFO_CACHE_COMPLEX_INDEXING; |
|
} |
|
switch (level) { |
|
case 1: |
|
switch (type) { |
|
case cache_type_unified: |
|
cache->l1d = cache->l1i = (struct cpuinfo_x86_cache) { |
|
.size = associativity * partitions * line_size * sets, |
|
.associativity = associativity, |
|
.sets = sets, |
|
.partitions = partitions, |
|
.line_size = line_size, |
|
.flags = flags | CPUINFO_CACHE_UNIFIED, |
|
.apic_bits = apic_bits |
|
}; |
|
break; |
|
case cache_type_data: |
|
cache->l1d = (struct cpuinfo_x86_cache) { |
|
.size = associativity * partitions * line_size * sets, |
|
.associativity = associativity, |
|
.sets = sets, |
|
.partitions = partitions, |
|
.line_size = line_size, |
|
.flags = flags, |
|
.apic_bits = apic_bits |
|
}; |
|
break; |
|
case cache_type_instruction: |
|
cache->l1i = (struct cpuinfo_x86_cache) { |
|
.size = associativity * partitions * line_size * sets, |
|
.associativity = associativity, |
|
.sets = sets, |
|
.partitions = partitions, |
|
.line_size = line_size, |
|
.flags = flags, |
|
.apic_bits = apic_bits |
|
}; |
|
break; |
|
} |
|
break; |
|
case 2: |
|
switch (type) { |
|
case cache_type_instruction: |
|
cpuinfo_log_warning("unexpected L2 instruction cache reported in leaf 0x00000004 is ignored"); |
|
break; |
|
case cache_type_unified: |
|
flags |= CPUINFO_CACHE_UNIFIED; |
|
case cache_type_data: |
|
cache->l2 = (struct cpuinfo_x86_cache) { |
|
.size = associativity * partitions * line_size * sets, |
|
.associativity = associativity, |
|
.sets = sets, |
|
.partitions = partitions, |
|
.line_size = line_size, |
|
.flags = flags, |
|
.apic_bits = apic_bits |
|
}; |
|
break; |
|
} |
|
break; |
|
case 3: |
|
switch (type) { |
|
case cache_type_instruction: |
|
cpuinfo_log_warning("unexpected L3 instruction cache reported in leaf 0x00000004 is ignored"); |
|
break; |
|
case cache_type_unified: |
|
flags |= CPUINFO_CACHE_UNIFIED; |
|
case cache_type_data: |
|
cache->l3 = (struct cpuinfo_x86_cache) { |
|
.size = associativity * partitions * line_size * sets, |
|
.associativity = associativity, |
|
.sets = sets, |
|
.partitions = partitions, |
|
.line_size = line_size, |
|
.flags = flags, |
|
.apic_bits = apic_bits |
|
}; |
|
break; |
|
} |
|
break; |
|
case 4: |
|
switch (type) { |
|
case cache_type_instruction: |
|
cpuinfo_log_warning("unexpected L4 instruction cache reported in leaf 0x00000004 is ignored"); |
|
break; |
|
case cache_type_unified: |
|
flags |= CPUINFO_CACHE_UNIFIED; |
|
case cache_type_data: |
|
cache->l4 = (struct cpuinfo_x86_cache) { |
|
.size = associativity * partitions * line_size * sets, |
|
.associativity = associativity, |
|
.sets = sets, |
|
.partitions = partitions, |
|
.line_size = line_size, |
|
.flags = flags, |
|
.apic_bits = apic_bits |
|
}; |
|
break; |
|
} |
|
break; |
|
default: |
|
cpuinfo_log_warning("unexpected L%"PRIu32" cache reported in leaf 0x00000004 is ignored", level); |
|
break; |
|
} |
|
return true; |
|
} |
|
|
|
|
|
bool cpuinfo_x86_decode_cache_properties( |
|
struct cpuid_regs regs, |
|
struct cpuinfo_x86_caches* cache) |
|
{ |
|
const uint32_t type = regs.eax & UINT32_C(0x1F); |
|
if (type == cache_type_none) { |
|
return false; |
|
} |
|
|
|
const uint32_t level = (regs.eax >> 5) & UINT32_C(0x7); |
|
const uint32_t cores = 1 + ((regs.eax >> 14) & UINT32_C(0x00000FFF)); |
|
const uint32_t apic_bits = bit_length(cores); |
|
|
|
const uint32_t sets = 1 + regs.ecx; |
|
const uint32_t line_size = 1 + (regs.ebx & UINT32_C(0x00000FFF)); |
|
const uint32_t partitions = 1 + ((regs.ebx >> 12) & UINT32_C(0x000003FF)); |
|
const uint32_t associativity = 1 + (regs.ebx >> 22); |
|
|
|
uint32_t flags = 0; |
|
if (regs.edx & UINT32_C(0x00000002)) { |
|
flags |= CPUINFO_CACHE_INCLUSIVE; |
|
} |
|
|
|
switch (level) { |
|
case 1: |
|
switch (type) { |
|
case cache_type_unified: |
|
cache->l1d = cache->l1i = (struct cpuinfo_x86_cache) { |
|
.size = associativity * partitions * line_size * sets, |
|
.associativity = associativity, |
|
.sets = sets, |
|
.partitions = partitions, |
|
.line_size = line_size, |
|
.flags = flags | CPUINFO_CACHE_UNIFIED, |
|
.apic_bits = apic_bits |
|
}; |
|
break; |
|
case cache_type_data: |
|
cache->l1d = (struct cpuinfo_x86_cache) { |
|
.size = associativity * partitions * line_size * sets, |
|
.associativity = associativity, |
|
.sets = sets, |
|
.partitions = partitions, |
|
.line_size = line_size, |
|
.flags = flags, |
|
.apic_bits = apic_bits |
|
}; |
|
break; |
|
case cache_type_instruction: |
|
cache->l1i = (struct cpuinfo_x86_cache) { |
|
.size = associativity * partitions * line_size * sets, |
|
.associativity = associativity, |
|
.sets = sets, |
|
.partitions = partitions, |
|
.line_size = line_size, |
|
.flags = flags, |
|
.apic_bits = apic_bits |
|
}; |
|
break; |
|
} |
|
break; |
|
case 2: |
|
switch (type) { |
|
case cache_type_instruction: |
|
cpuinfo_log_warning("unexpected L2 instruction cache reported in leaf 0x8000001D is ignored"); |
|
break; |
|
case cache_type_unified: |
|
flags |= CPUINFO_CACHE_UNIFIED; |
|
case cache_type_data: |
|
cache->l2 = (struct cpuinfo_x86_cache) { |
|
.size = associativity * partitions * line_size * sets, |
|
.associativity = associativity, |
|
.sets = sets, |
|
.partitions = partitions, |
|
.line_size = line_size, |
|
.flags = flags, |
|
.apic_bits = apic_bits |
|
}; |
|
break; |
|
} |
|
break; |
|
case 3: |
|
switch (type) { |
|
case cache_type_instruction: |
|
cpuinfo_log_warning("unexpected L3 instruction cache reported in leaf 0x8000001D is ignored"); |
|
break; |
|
case cache_type_unified: |
|
flags |= CPUINFO_CACHE_UNIFIED; |
|
case cache_type_data: |
|
cache->l3 = (struct cpuinfo_x86_cache) { |
|
.size = associativity * partitions * line_size * sets, |
|
.associativity = associativity, |
|
.sets = sets, |
|
.partitions = partitions, |
|
.line_size = line_size, |
|
.flags = flags, |
|
.apic_bits = apic_bits |
|
}; |
|
break; |
|
} |
|
break; |
|
default: |
|
cpuinfo_log_warning("unexpected L%"PRIu32" cache reported in leaf 0x8000001D is ignored", level); |
|
break; |
|
} |
|
return true; |
|
} |
|
|