board_inspector/cpuparser: add CPUID parsers

This patch adds a parser of CPU identification information reported by the
CPUID instruction.

The framework is based on the CPUID parsing facilities in
BITS (https://biosbits.org/), but with the following changes.

1. The CPUID data is fetched by executing the `cpuid` utility, rather than
   executing the `cpuid` instruction. This avoids introducing any
   additional library or Python/C extension and gets a CPUID leaf on all
   physical cores in one shot.

2. Parsers of CPUID leaves 0x10, 0x1A and 0x1F are added. New fields in
   existing leaves are also added.

3. A wrapper function, named `parse_cpuid`, is added as the single API that
   allows other modules to get an arbitrary CPUID leaf or subleaf.

Tracked-On: #5922
Signed-off-by: Junjie Mao <junjie.mao@intel.com>
This commit is contained in:
Junjie Mao 2021-05-03 10:26:58 +08:00 committed by wenlingz
parent 05c738a480
commit 598be99dc2
3 changed files with 1022 additions and 0 deletions

View File

@ -0,0 +1,61 @@
# Copyright (C) 2021 Intel Corporation. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
import cpuparser.cpuids
dispatch_table = {
0x0: cpuparser.cpuids.LEAF_0,
0x1: cpuparser.cpuids.LEAF_1,
0x2: cpuparser.cpuids.LEAF_2,
0x4: cpuparser.cpuids.LEAF_4,
0x5: cpuparser.cpuids.LEAF_5,
0x6: cpuparser.cpuids.LEAF_6,
0x7: cpuparser.cpuids.LEAF_7,
0x9: cpuparser.cpuids.LEAF_9,
0xA: cpuparser.cpuids.LEAF_A,
0xB: cpuparser.cpuids.LEAF_B,
# 0xD: multiple parsers
# 0xF: multiple parsers
# 0x10: multiple parsers
0x1A: cpuparser.cpuids.LEAF_1A,
0x1F: cpuparser.cpuids.LEAF_1F,
0x80000000: cpuparser.cpuids.LEAF_80000000,
0x80000001: cpuparser.cpuids.LEAF_80000001,
0x80000002: cpuparser.cpuids.LEAF_80000002,
0x80000003: cpuparser.cpuids.LEAF_80000003,
0x80000004: cpuparser.cpuids.LEAF_80000004,
0x80000006: cpuparser.cpuids.LEAF_80000006,
0x80000007: cpuparser.cpuids.LEAF_80000007,
0x80000008: cpuparser.cpuids.LEAF_80000008,
}
def parse_cpuid(leaf, subleaf, cpu_id):
if leaf in dispatch_table.keys():
return dispatch_table[leaf].read(cpu_id, subleaf)
elif leaf == 0xD:
if subleaf == 0:
return cpuparser.cpuids.LEAF_D.read(cpu_id, subleaf)
elif subleaf == 1:
return cpuparser.cpuids.LEAF_D_1.read(cpu_id, subleaf)
else:
return cpuparser.cpuids.LEAF_D_n.read(cpu_id, subleaf)
elif leaf == 0xF:
if subleaf == 0:
return cpuparser.cpuids.LEAF_F.read(cpu_id, subleaf)
elif subleaf == 1:
return cpuparser.cpuids.LEAF_F_1.read(cpu_id, subleaf)
else:
return cpuparser.cpuids.LEAF_F_n.read(cpu_id, subleaf)
elif leaf == 0x10:
if subleaf == 0:
return cpuparser.cpuids.LEAF_10.read(cpu_id, subleaf)
elif subleaf == 1 or subleaf == 2:
return cpuparser.cpuids.LEAF_10_1.read(cpu_id, subleaf)
elif subleaf == 3:
return cpuparser.cpuids.LEAF_10_3.read(cpu_id, subleaf)
else:
return None
else:
return None

View File

@ -0,0 +1,752 @@
# Copyright (C) 2021 Intel Corporation. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
"""CPUID register decoding."""
from cpuparser.platformbase import CPUID, cpuidfield
import struct
EAX = 0
EBX = 1
ECX = 2
EDX = 3
class LEAF_0(CPUID):
"""Basic CPUID information including vendor and max supported basic leaf."""
leaf = 0x0
max_leaf = cpuidfield(EAX, 31, 0, doc="Highest value the CPUID recognizes for returning basic processor information")
@property
def vendor(self):
"""Vendor identification string"""
return struct.pack('III', self.regs.ebx, self.regs.edx, self.regs.ecx)
class LEAF_1(CPUID):
"""Basic CPUID Information
Contains version type, family, model, and stepping ID; brand index; CLFLUSH
line size; maximum number of addressable IDs for logical processors in the
physical package; initial APIC ID; and feature information"""
leaf = 0x1
stepping = cpuidfield(EAX, 3, 0, doc="Stepping ID")
model = cpuidfield(EAX, 7, 4, doc="Model")
family = cpuidfield(EAX, 11, 8, doc="Family ID")
processor_type = cpuidfield(EAX, 13, 12, doc="Processor Type")
ext_model = cpuidfield(EAX, 19, 16, doc="Extended Model ID")
ext_family = cpuidfield(EAX, 27, 20, doc="Extended Family ID")
brand_index = cpuidfield(EBX, 7, 0, doc="Brand index")
CLFLUSH_line_size = cpuidfield(EBX, 15, 8, doc="CLFLUSH instruction cache line size (in 8-byte words)")
max_logical_processor_ids = cpuidfield(EBX, 23, 16, doc="The maximum number of addressable IDs for logical processors in the physical package.")
initial_apic_id = cpuidfield(EBX, 31, 24, doc="Initial APIC ID")
sse3 = cpuidfield(ECX, 0, 0)
pclmulqdq = cpuidfield(ECX, 1, 1)
dtes64 = cpuidfield(ECX, 2, 2)
monitor = cpuidfield(ECX, 3, 3)
ds_cpl = cpuidfield(ECX, 4, 4)
vmx = cpuidfield(ECX, 5, 5)
smx = cpuidfield(ECX, 6, 6)
est = cpuidfield(ECX, 7, 7)
tm2 = cpuidfield(ECX, 8, 8)
ssse3 = cpuidfield(ECX, 9, 9)
cnxt_id = cpuidfield(ECX, 10, 10)
sdbg = cpuidfield(ECX, 11, 11)
fma = cpuidfield(ECX, 12, 12)
cmpxchg16b = cpuidfield(ECX, 13, 13)
xtpr = cpuidfield(ECX, 14, 14)
pdcm = cpuidfield(ECX, 15, 15)
pcid = cpuidfield(ECX, 17, 17)
dca = cpuidfield(ECX, 18, 18)
sse4_1 = cpuidfield(ECX, 19, 19)
sse4_2 = cpuidfield(ECX, 20, 20)
x2apic = cpuidfield(ECX, 21, 21)
movbe = cpuidfield(ECX, 22, 22)
popcnt = cpuidfield(ECX, 23, 23)
tsc_deadline = cpuidfield(ECX, 24, 24)
aes = cpuidfield(ECX, 25, 25)
xsave = cpuidfield(ECX, 26, 26)
osxsave = cpuidfield(ECX, 27, 27)
avx = cpuidfield(ECX, 28, 28)
f16c = cpuidfield(ECX, 29, 29)
rdrand = cpuidfield(ECX, 30, 30)
fpu = cpuidfield(EDX, 0, 0)
vme = cpuidfield(EDX, 1, 1)
de = cpuidfield(EDX, 2, 2)
pse = cpuidfield(EDX, 3, 3)
tsc = cpuidfield(EDX, 4, 4)
msr = cpuidfield(EDX, 5, 5)
pae = cpuidfield(EDX, 6, 6)
mce = cpuidfield(EDX, 7, 7)
cx8 = cpuidfield(EDX, 8, 8)
apic = cpuidfield(EDX, 9, 9)
sep = cpuidfield(EDX, 11, 11)
mtrr = cpuidfield(EDX, 12, 12)
pge = cpuidfield(EDX, 13, 13)
mca = cpuidfield(EDX, 14, 14)
cmov = cpuidfield(EDX, 15, 15)
pat = cpuidfield(EDX, 16, 16)
pse36 = cpuidfield(EDX, 17, 17)
psn = cpuidfield(EDX, 18, 18)
clfsh = cpuidfield(EDX, 19, 19)
ds = cpuidfield(EDX, 21, 21)
acpi = cpuidfield(EDX, 22, 22)
mmx = cpuidfield(EDX, 23, 23)
fxsr = cpuidfield(EDX, 24, 24)
sse = cpuidfield(EDX, 25, 25)
sse2 = cpuidfield(EDX, 26, 26)
ss = cpuidfield(EDX, 27, 27)
htt = cpuidfield(EDX, 28, 28)
tm = cpuidfield(EDX, 29, 29)
pbe = cpuidfield(EDX, 31, 31)
@property
def display_family(self):
if self.family == 0xf:
return self.ext_family + self.family
return self.family
@property
def display_model(self):
if self.family == 0xf or self.family == 0x6:
return (self.ext_model << 4) + self.model
return self.model
capability_bits = [
"sse3",
"pclmulqdq",
"dtes64",
"monitor",
"ds_cpl",
"vmx",
"smx",
"est",
"tm2",
"ssse3",
"cnxt_id",
"sdbg",
"fma",
"cmpxchg16b",
"xtpr",
"pdcm",
"pcid",
"dca",
"sse4_1",
"sse4_2",
"x2apic",
"movbe",
"popcnt",
"tsc_deadline",
"aes",
"xsave",
"avx",
"f16c",
"rdrand",
"fpu",
"vme",
"de",
"pse",
"tsc",
"msr",
"pae",
"mce",
"cx8",
"apic",
"sep",
"mtrr",
"pge",
"mca",
"cmov",
"pat",
"pse36",
"psn",
"clfsh",
"ds",
"acpi",
"mmx",
"fxsr",
"sse",
"sse2",
"ss",
"htt",
"tm",
"pbe",
]
class LEAF_2(CPUID):
"""TLB, Cache, and Prefetch Information"""
leaf = 0x2
times_to_run = cpuidfield(EAX, 7, 0, doc="Number of times CPUID must be executed with EAX = 2 to retrieve a complete description of the processor's TLB, Cache, and Prefetch hardware")
class LEAF_4(CPUID):
"""Deterministic cache parameters
Returns encoded data that describes a set of deterministic cache parameters
for the cache level associated in ECX"""
leaf = 0x4
cache_type = cpuidfield(EAX, 4, 0, doc="Cache Type Field")
cache_level = cpuidfield(EAX, 7, 5, doc="Cache Level")
self_initializing = cpuidfield(EAX, 8, 8, doc="Self Initializing Cache Level")
fully_associative = cpuidfield(EAX, 9, 9, doc="Fully Associative Cache")
max_logical_processors_sharing_cache_z = cpuidfield(EAX, 25, 14, doc="Max number of addressable IDs for logical processors sharing this cache (zero based)")
max_cores_sharing_cache_z = cpuidfield(EAX, 31, 26, doc="Max number of addressable IDs for processor cores in the physical package (zero based)")
line_size_z = cpuidfield(EBX, 11, 0, doc="System Coherency Line Size (zero-based)")
partitions_z = cpuidfield(EBX, 21, 12, doc="Physical Line Partitions (zero-based)")
ways_z = cpuidfield(EBX, 31, 22, doc="Ways of associativity (zero-based)")
sets_z = cpuidfield(ECX, 31, 0, doc="Sets (zero-based)")
write_back_invalidate = cpuidfield(EDX, 0, 0, doc="Write-back Invalidate/Invalidate")
cache_inclusiveness = cpuidfield(EDX, 1, 1, doc="Cache Inclusiveness")
complex_cache_indexing = cpuidfield(EDX, 2, 2, doc="Complex Cache indexing")
@property
def max_logical_processors_sharing_cache(self):
"""Maximum number of addressable IDs for logical processors sharing this cache"""
return self.max_logical_processors_sharing_cache_z + 1
@property
def max_cores_sharing_cache(self):
"""Maximum number of addressable IDs for processor cores in the physical pacakge"""
return self.max_cores_sharing_cache_z + 1
@property
def partitions(self):
"""Number of physical line partitions"""
return self.partitions_z + 1
@property
def line_size(self):
"""System Coherency line size"""
return self.line_size_z + 1
@property
def ways(self):
"""Ways of associativity"""
return self.ways_z + 1
@property
def sets(self):
"""Number of sets"""
return self.sets_z + 1
@property
def cache_size(self):
"""Cache size in bytes"""
return self.ways * self.partitions * self.line_size * self.sets
class LEAF_5(CPUID):
"""MONITOR/MWAIT Leaf
Returns information about features available to MONITOR/MWAIT instructions"""
leaf = 0x5
smallest_monitor_line_size = cpuidfield(EAX, 15, 0, doc="Smallest monitor-line size in bytes")
largest_monitor_line_size = cpuidfield(EBX, 15, 0, doc="Largest monitor-line size in bytes")
monitor_mwait_supported = cpuidfield(ECX, 0, 0, doc="Enumeration of MONITOR/MWAIT extensions supported")
interrupt_break_event_supported = cpuidfield(ECX, 1, 1, doc="Supports treating interrupts as break-events for MWAIT, even when interrupts disabled")
c0 = cpuidfield(EDX, 3, 0, doc="Number of C0 sub C-states supported using MWAIT")
c1 = cpuidfield(EDX, 7, 4, doc="Number of C1 sub C-states supported using MWAIT")
c2 = cpuidfield(EDX, 11, 8, doc="Number of C2 sub C-states supported using MWAIT")
c3 = cpuidfield(EDX, 15, 12, doc="Number of C3 sub C-states supported using MWAIT")
c4 = cpuidfield(EDX, 19, 16, doc="Number of C4 sub C-states supported using MWAIT")
class LEAF_6(CPUID):
"""Thermal and Power Management leaf
Returns information about the maximum input values for sub-leaves that contain extended feature flags."""
leaf = 0x6
digital_temperature_sensor_supported = cpuidfield(EAX, 0, 0, doc = "Digital temperature sensor is supported if set")
turbo_boost_available = cpuidfield(EAX, 1, 1, doc = "Intel Turbo Boost technology available")
arat_supported = cpuidfield(EAX, 2, 2, doc = "APIC-Timer-always-running feature is supported if set")
pln_supported = cpuidfield(EAX, 4, 4, doc = "Power limit notification controls are supported if set")
ecmd_supported = cpuidfield(EAX, 5, 5, doc = "Clock modulation duty cycle extension is supported if set")
package_thermal_management_supported = cpuidfield(EAX, 6, 6, doc = "Package thermal management is supported if set")
num_interrupt_thresholds = cpuidfield(EBX, 3, 0, doc="Number of interrupt thresholds in digital thermal sensor")
hardware_coordination_feedback_capability = cpuidfield(ECX, 0, 0, doc="Hardware coordination feedback capability")
performance_energy_bias = cpuidfield(ECX, 3, 3, doc="Performance-energy bias preference support")
class LEAF_7(CPUID):
"""Structured Extended Feature Flags Enumeration Leaf
Returns information about the maximum input value for sub-leaves that contain
extended feature flags"""
leaf = 0x7
max_input_values = cpuidfield(EAX, 31, 0, doc="Reports the maximum input value for supported leaf 7 sub-leaves")
fsgsbase = cpuidfield(EBX, 0, 0, doc="Supports RDFSBASE/RDGSBASE/WRFSBASE/WRGSBASE if 1")
ia32_tsc_adjust_msr = cpuidfield(EBX, 1, 1, doc="IA32_TSC_ADJUST MSR is supported if 1")
sgx = cpuidfield(EBX, 2, 2, doc="Supports Intel® Software Guard Extensions (Intel® SGX Extensions) if 1")
bmi1 = cpuidfield(EBX, 3, 3)
hle = cpuidfield(EBX, 4, 4)
avx2 = cpuidfield(EBX, 5, 5)
fdp_excptn_only = cpuidfield(EBX, 6, 6, doc="x87 FPU Data Pointer updated only on x87 exceptions if 1")
smep = cpuidfield(EBX, 7, 7, doc="Supports Supervisor Mode Execution Protection if 1")
bmi2 = cpuidfield(EBX, 8, 8)
erms = cpuidfield(EBX, 9, 9, doc="Supports Enhanced REP MOVSB/STOSB if 1")
invpcid = cpuidfield(EBX, 10, 10, doc="Supports INVPCID instruction for system software that manages process-context identifiers if 1")
rtm = cpuidfield(EBX, 11, 11)
qm = cpuidfield(EBX, 12, 12, doc="Supports Quality of Service Monitoring capability if 1")
deprecate_fpu = cpuidfield(EBX, 13, 13, doc="Deprecates FPS CS and FPU DS values if 1")
mpx = cpuidfield(EBX, 14, 14, doc="Supports Intel® Memory Protection Extensions if 1")
rdt_a = cpuidfield(EBX, 15, 15, doc="Supports Intel® Resource Director Technology (Intel® RDT) Allocation capability if 1")
avx512f = cpuidfield(EBX, 16, 16)
avx512dq = cpuidfield(EBX, 17, 17)
rdseed = cpuidfield(EBX, 18, 18)
adx = cpuidfield(EBX, 19, 19)
smap = cpuidfield(EBX, 20, 20, doc="Supports Supervisor-Mode Access Prevention (and the CLAC/STAC instructions) if 1")
avx512_ifma = cpuidfield(EBX, 21, 21)
clflushopt = cpuidfield(EBX, 23, 23)
clwb = cpuidfield(EBX, 24, 24)
intel_pt = cpuidfield(EBX, 25, 25)
avx512pf = cpuidfield(EBX, 26, 26)
avx512er = cpuidfield(EBX, 27, 27)
avx512cd = cpuidfield(EBX, 28, 28)
sha = cpuidfield(EBX, 29, 29, doc="Supports Intel® Secure Hash Algorithm Extensions (Intel® SHA Extensions) if 1")
avx512bw = cpuidfield(EBX, 30, 30)
avx512vl = cpuidfield(EBX, 31, 31)
prefetchwt1 = cpuidfield(ECX, 0, 0)
avx512_vbmi = cpuidfield(ECX, 1, 1)
umip = cpuidfield(ECX, 2, 2, doc="Supports user-mode instruction prevention if 1")
pku = cpuidfield(ECX, 3, 3, doc="Supports protection keys for user-mode pages if 1")
ospke = cpuidfield(ECX, 4, 4, doc="If 1, OS has set CR4.PKE to enable protection keys (and the RDPKRU/WRPKRU instructions)")
waitpkg = cpuidfield(ECX, 5, 5)
avx512_vbmi2 = cpuidfield(ECX, 6, 6)
cet_ss = cpuidfield(ECX, 7, 7, doc="Supports CET shadow stack features if 1")
gfni = cpuidfield(ECX, 8, 8)
vaes = cpuidfield(ECX, 9, 9)
vpclmulqdq = cpuidfield(ECX, 10, 10)
avx512_vnni = cpuidfield(ECX, 11, 11)
avx512_bitalg = cpuidfield(ECX, 12, 12)
tme_en = cpuidfield(ECX, 13, 13)
avx512_vpopcntdq = cpuidfield(ECX, 14, 14)
la57 = cpuidfield(ECX, 16, 16, doc="Supports 57-bit linear addresses and five-level paging if 1")
mawau = cpuidfield(ECX, 21, 17, doc="The value of MAWAU used by the BNDLDX and BNDSTX instructions in 64-bit mode")
rdpid = cpuidfield(ECX, 22, 22, doc="RDPID and IA32_TSC_AUX are available if 1")
kl = cpuidfield(ECX, 23, 23, doc="Supports Key Locker if 1")
cldemote = cpuidfield(ECX, 25, 25, doc="Supports cache line demote if 1")
movdiri = cpuidfield(ECX, 27, 27, doc="Supports MOVDIRI if 1")
movdiri64b = cpuidfield(ECX, 28, 28, doc="Supports MOVDIRI64B if 1")
sgx_lc = cpuidfield(ECX, 30, 30, doc="Supports SGX Launch Configuration if 1")
pks = cpuidfield(ECX, 31, 31, doc="Supports protection keys for supervisor-mode pages if 1")
avx512_4vnniw = cpuidfield(EDX, 2, 2)
avx512_4fmaps = cpuidfield(EDX, 3, 3)
fast_short_rep_mov = cpuidfield(EDX, 4, 4)
avx512_vp2intersect = cpuidfield(EDX, 8, 8)
md_clear = cpuidfield(EDX, 10, 10)
hybrid = cpuidfield(EDX, 15, 15, doc="If 1, the processor is identified as a hybrid part")
pconfig = cpuidfield(EDX, 18, 18, doc="Supports PCONFIG if 1")
cet_ibt = cpuidfield(EDX, 20, 20, doc="Supports CET indirect branch tracking features if 1")
ibrs_ibpb = cpuidfield(EDX, 26, 26, doc="Enumerates support for indirect branch restricted speculation (IBRS) and the indirect branch predictor barrier (IBPB)")
stibp = cpuidfield(EDX, 27, 27, doc="Enumerates support for single thread indirect branch predictors (STIBP)")
l1d_flush = cpuidfield(EDX, 28, 28, doc="Enumerates support for L1D_FLUSH")
ia32_arch_capabilities = cpuidfield(EDX, 29, 29, doc="Enumerates support for the IA32_ARCH_CAPABILITIES MSR")
ia32_core_capabilities = cpuidfield(EDX, 30, 30, doc="Enumerates support for the IA32_CORE_CAPABILITIES MSR")
ssbd = cpuidfield(EDX, 31, 31, doc="Enumerates support for Speculative Store Bypass Disable (SSBD)")
capability_bits = [
"fsgsbase",
"ia32_tsc_adjust_msr",
"sgx",
"bmi1",
"hle",
"avx2",
"fdp_excptn_only",
"smep",
"bmi2",
"erms",
"invpcid",
"rtm",
"qm",
"deprecate_fpu",
"mpx",
"rdt_a",
"avx512f",
"avx512dq",
"rdseed",
"adx",
"smap",
"avx512_ifma",
"clflushopt",
"clwb",
"intel_pt",
"avx512pf",
"avx512er",
"avx512cd",
"sha",
"avx512bw",
"avx512vl",
"prefetchwt1",
"avx512_vbmi",
"umip",
"pku",
"waitpkg",
"avx512_vbmi2",
"cet_ss",
"gfni",
"vaes",
"vpclmulqdq",
"avx512_vnni",
"avx512_bitalg",
"tme_en",
"avx512_vpopcntdq",
"la57",
"mawau",
"rdpid",
"kl",
"cldemote",
"movdiri",
"movdiri64b",
"sgx_lc",
"pks",
"avx512_4vnniw",
"avx512_4fmaps",
"fast_short_rep_mov",
"avx512_vp2intersect",
"md_clear",
"hybrid",
"pconfig",
"cet_ibt",
"ibrs_ibpb",
"stibp",
"l1d_flush",
"ia32_arch_capabilities",
"ia32_core_capabilities",
"ssbd",
]
class LEAF_9(CPUID):
"""Direct Cache Access Information leaf
Returns information about Direct Cache Access capabilities"""
leaf = 0x9
platform_dca_cap = cpuidfield(EAX, 31, 0, doc="Value of bits of IA32_PLATFORM_DCA_CAP MSR (address 1F8H)")
class LEAF_A(CPUID):
"""Architectural Performance Monitoring Leaf
Returns information about support for architectural performance monitoring capabilities"""
leaf = 0xA
architectural_performance_monitor_version_id = cpuidfield(EAX, 7, 0, doc="Version ID of architectural performance monitoring")
gp_performance_monitor_counters = cpuidfield(EAX, 15, 8, doc="Number of general-purpose performance monitoring counter per logical processor")
gp_performance_counter_width = cpuidfield(EAX, 23, 16, doc="Bit width of general-purpose, performance monitoring counter")
ebx_bit_vector_length = cpuidfield(EAX, 31, 24, doc="Length of EBX bit vector to enumerate architectural performance monitoring events")
core_cycle_event = cpuidfield(EBX, 0, 0, doc="Core cycle event not available if 1")
instruction_retired_event = cpuidfield(EBX, 1, 1, doc="Instruction retired event not available if 1")
reference_cycles_event = cpuidfield(EBX, 2, 2, doc="Reference cycles event not available if 1")
llc_ref_event = cpuidfield(EBX, 3, 3, doc="Last-level cache reference event not available if 1")
llc_misses_event = cpuidfield(EBX, 4, 4, doc= "Last-level cache misses event not available if 1")
branch_instruction_retired_event = cpuidfield(EBX, 5, 5, doc="Branch instruction retired event not available if 1")
branch_mispredict_retired_event = cpuidfield(EBX, 6, 6, doc="Branch mispredict retired event not available if 1")
ff_performance_counters = cpuidfield(EDX, 4, 0, doc="Number of fixed-function performance counters")
ff_performance_counter_width = cpuidfield(EDX, 12, 5, doc="Bit width of fixed-function performance counters")
class LEAF_B(CPUID):
"""Extended Topology Enumeration Leaf
Returns information about extended topology enumeration data"""
leaf = 0xB
num_bit_shift = cpuidfield(EAX, 4, 0, doc="Number of bits to shift right on x2APID ID to get a unique topology ID of the next level type")
logical_proccessors_at_level = cpuidfield(EBX, 15, 0, doc="Number of logical processors at this level type.")
level_number = cpuidfield(ECX, 7, 0, doc="Level number")
level_type = cpuidfield(ECX, 15, 8, doc="Level type")
x2apic_id = cpuidfield(EDX, 31, 0, doc="x2APIC ID of the current logical processor")
class LEAF_D(CPUID):
"""Processor Extended State Enumeration Main Leaf and Sub-Leaves.
Returns information about the bit-vector representation of all processor
state extensions that are supported in the processor, and storage size
requirements of the XSAVE/XRSTOR area. Output depends on initial value of ECX."""
leaf = 0xD
valid_bits_xcr0_lower = cpuidfield(EAX, 31, 0, doc="Reports the valid bit fields of the lower 32 bits of XCR0. If a bit is 0, the corresponding bit field in XCR0 is reserved")
legacy_x87 = cpuidfield(EAX, 0, 0, doc="legacy x87")
sse_128_bit = cpuidfield(EAX, 1, 1, doc="128-bit SSE")
avx_256_bit = cpuidfield(EAX, 2, 2, doc="256-bit AVX")
max_size_enabled_xcr0 = cpuidfield(EBX, 31, 0, doc="Maximum size (bytes, from the beginning of the XSAVE/XRSTOR save area) required by enabled features in XCR0. May be different than ECX if some features at the end of the XSAVE save area are not enabled.")
max_size_supported_xcr0 = cpuidfield(ECX, 31, 0, doc="Maximum size (bytes, from the beginning of the XSAVE/XRSTOR save area) of the XSAVE/XRSTOR save area required by all supported features in the processor, i.e all the valid bit fields in XCR0.")
valid_bits_xcr0_upper = cpuidfield(EDX, 31, 0, doc="The valid bit fields of the upper 32 bits of XCR0. If a bit is 0, the corresponding bit field in XCR0 is reserved.")
def __getitem__(self, subleaf):
if subleaf == 0:
return self.read(self.cpu_id, subleaf)
elif subleaf == 1:
return LEAF_D_1.read(self.cpu_id, subleaf)
return LEAF_D_n.read(self.cpu_id, subleaf)
class LEAF_D_1(CPUID):
"""Processor Extended State Enumeration Main Leaf and Sub-Leaves.
Returns information about the bit-vector representation of all processor
state extensions that are supported in the processor, and storage size
requirements of the XSAVE/XRSTOR area. Output depends on initial value of ECX."""
leaf = 0xD
xsaveopt = cpuidfield(EAX, 0, 0, doc="XSAVEOPT is available")
class LEAF_D_n(CPUID):
"""Processor Extended State Enumeration Main Leaf and Sub-Leaves.
Returns information about the bit-vector representation of all processor
state extensions that are supported in the processor, and storage size
requirements of the XSAVE/XRSTOR area. Output depends on initial value of ECX."""
leaf = 0xD
size = cpuidfield(EAX, 31, 0, doc="The size in bytes (from the offset specified in EBX) of the save area for an extended state feature associated with a valid sub-leaf index")
offset = cpuidfield(EBX, 31, 0, doc="The offset in bytes of this extended state component's save area from the beginning of the XSAVE/XRSTOR area.")
class LEAF_F(CPUID):
"""Quality of Service Resource Type Enumeration Sub-Leaf and L3 Cache QoS Capability Enumeration Sub-leaf. Depends on value of ECX
Returns Quality of Service (QoS) Enumeration Information."""
leaf = 0xF
def __getitem__(self, subleaf):
if subleaf == 0:
return self.read(self.cpu_id, subleaf)
elif subleaf == 1:
return LEAF_F_1.read(self.cpu_id, subleaf)
return LEAF_F_n.read(self.cpu_id, subleaf)
max_range_rmid_z = cpuidfield(EBX, 31, 0, doc="Maximum range (zero-based) of RMID within this physical processor of all types.")
l3_cache_qos = cpuidfield(EDX, 1, 1, doc="Supports L3 Cache QoS if 1")
@property
def max_range_rmid(self):
"""Maximum range of RMID within this physical processor of all types."""
return self.max_range_rmid_z + 1
class LEAF_F_1(CPUID):
"""Quality of Service Resource Type Enumeration Sub-Leaf and L3 Cache QoS Capability Enumeration Sub-leaf. Depends on value of ECX
Returns L3 Cache QoS Capability Enumeration Information."""
leaf = 0xF
qm_ctr_conversion_factor = cpuidfield(EBX, 31, 0, doc="Conversion factor from reported IA32_QM_CTR value to occupancy metric (bytes).")
l3_occupancy_monitoring = cpuidfield(EDX, 0, 0, doc="Supports L3 occupancy monitoring if 1")
max_range_rmid_z = cpuidfield(ECX, 31, 0, doc="Maximum range (zero-based) of RMID of this resource type")
@property
def max_range_rmid(self):
"""Maximum range of RMID of this resource type"""
return self.max_range_rmid_z + 1
class LEAF_F_n(CPUID):
"""Quality of Service Resource Type Enumeration Sub-Leaf and L3 Cache QoS Capability Enumeration Sub-leaf. Depends on value of ECX
Returns Quality of Service (QoS) Enumeration Information."""
leaf = 0xF
class LEAF_10(CPUID):
"""Intel Resource Director Technology (Intel RDT) Allocation Enumeration Sub-leaf"""
leaf = 0x10
l3_cache_allocation = cpuidfield(EBX, 1, 1, doc="Supports L3 Cache Allocation Technology if 1.")
l2_cache_allocation = cpuidfield(EBX, 2, 2, doc="Supports L2 Cache Allocation Technology if 1.")
memory_bandwidth_allocation = cpuidfield(EBX, 3, 3, doc="Supports Memory Bandwidth Allocation if 1.")
class LEAF_10_1(CPUID):
"""L3/L2 Cache Allocation Technology Enumeration Sub-leaf"""
leaf = 0x10
capacity_mask_length_z = cpuidfield(EAX, 4, 0, doc="Length of the capacity bit mask for the corresponding ResID (zero based).")
isolation_map = cpuidfield(EBX, 31, 0, doc="Bit-granular map of isolation/contention of allocation units.")
code_and_data_prioritization = cpuidfield(ECX, 2, 2, doc="Code and Data Prioritization Technology supported if 1.")
clos_number_z = cpuidfield(EDX, 15, 0, doc="Highest COS number supported for this ResID.")
@property
def capacity_mask_length(self):
return self.capacity_mask_length_z + 1
@property
def clos_number(self):
return self.clos_number_z + 1
class LEAF_10_3(CPUID):
"""Memory Bandwidth Allocation Enumeration Sub-leaf"""
leaf = 0x10
max_throttling_value_z = cpuidfield(EAX, 11, 0, doc="Reports the maximum MBA throttling value supported for the corresponding ResID (zero based).")
linear_response_delay = cpuidfield(ECX, 2, 2, doc="Reports whether the response of the delay values is linear.")
clos_number_z = cpuidfield(EDX, 15, 0, doc="Highest COS number supported for this ResID.")
@property
def max_throttling_value(self):
return self.max_throttling_value_z + 1
@property
def clos_number(self):
return self.clos_number_z + 1
class LEAF_1A(CPUID):
"""Hybrid Information Enumeration Leaf"""
leaf = 0x1A
core_type_id = cpuidfield(EAX, 31, 24, doc="Core type")
native_model_id = cpuidfield(EAX, 23, 0, doc="Native model ID")
core_types = {
0x20: "Atom",
0x40: "Core",
}
@property
def core_type(self):
if self.core_type_id in self.core_types:
return self.core_types[self.core_type_id]
return "Reserved"
class LEAF_1F(LEAF_B):
"""Extened Topology Enumeration Leaf v2"""
leaf = 0x1F
class LEAF_80000000(CPUID):
"""Extended Function CPUID Information"""
leaf = 0x80000000
max_extended_leaf = cpuidfield(EAX, 31, 0, doc="Highest extended function input value understood by CPUID")
class LEAF_80000001(CPUID):
"""Extended Function CPUID Information"""
leaf = 0x80000001
ext_signature_feature_bits = cpuidfield(EAX, 31, 0, doc="Extended processor signature and feature bits")
lahf_sahf_64 = cpuidfield(ECX, 0, 0, doc="LAHF/SAHF available in 64-bit mode")
lzcnt = cpuidfield(ECX, 5, 5)
prefetchw = cpuidfield(ECX, 8, 8)
syscall_sysret_64 = cpuidfield(EDX, 11, 11, doc="SYSCALL/SYSRET available in 64-bit mode")
execute_disable = cpuidfield(EDX, 20, 20, doc="Execute Disable Bit available")
gbyte_pages = cpuidfield(EDX, 26, 26, doc="GByte pages are available if 1")
rdtscp_ia32_tsc_aux = cpuidfield(EDX, 27, 27, doc="RDTSCP and IA32_TSC_AUX are available if 1")
intel_64 = cpuidfield(EDX, 29, 29, doc="Intel(R) 64 Architecture available if 1")
capability_bits = [
"lahf_sahf_64",
"lzcnt",
"prefetchw",
"syscall_sysret_64",
"execute_disable",
"gbyte_pages",
"rdtscp_ia32_tsc_aux",
"intel_64",
]
class LEAF_80000002(CPUID):
"""Extended Function CPUID Information
Processor Brand String"""
leaf = 0x80000002
@property
def brandstring(self):
"""Processor Brand String"""
return struct.pack('IIII', self.regs.eax, self.regs.ebx, self.regs.ecx, self.regs.edx).rstrip(b"\x00")
class LEAF_80000003(CPUID):
"""Extended Function CPUID Information
Processor Brand String Continued"""
leaf = 0x80000003
@property
def brandstring(self):
"""Processor Brand String"""
return struct.pack('IIII', self.regs.eax, self.regs.ebx, self.regs.ecx, self.regs.edx).rstrip(b"\x00")
class LEAF_80000004(CPUID):
"""Extended Function CPUID Information
Processor Brand String Continued"""
leaf = 0x80000004
@property
def brandstring(self):
"""Processor Brand String"""
return struct.pack('IIII', self.regs.eax, self.regs.ebx, self.regs.ecx, self.regs.edx).rstrip(b"\x00")
class LEAF_80000006(CPUID):
"""Extended Function CPUID Information"""
leaf = 0x80000006
cache_line_size = cpuidfield(ECX, 7, 0, doc="Cache Line size in bytes")
l2_associativity = cpuidfield(ECX, 15, 12, doc="L2 Associativity field")
cache_size_k = cpuidfield(ECX, 31, 16, doc="Cache size in 1K units")
class LEAF_80000007(CPUID):
"""Misc Feature Flags"""
leaf = 0x80000007
invariant_tsc = cpuidfield(EDX, 8, 8, doc="Invariant TSC available if 1")
capability_bits = [
"invariant_tsc",
]
class LEAF_80000008(CPUID):
"""Returns linear/physical address size"""
leaf = 0x80000008
physical_address_bits = cpuidfield(EAX, 7, 0, doc="# Physical Address bits")
linear_address_bits = cpuidfield(EAX, 15, 8, doc="# Linear Address bits")

View File

@ -0,0 +1,209 @@
# Copyright (C) 2021 Intel Corporation. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
"""Base classes and infrastructure for CPUID and MSR decoding"""
from __future__ import print_function
import subprocess
import re
import functools
import inspect
import operator
import textwrap
from collections import namedtuple
_wrapper = textwrap.TextWrapper(width=78, initial_indent=' ', subsequent_indent=' ')
regex_hex = "0x[0-9a-f]+"
class cpuid_result(namedtuple('cpuid_result', ['eax', 'ebx', 'ecx', 'edx'])):
__slots__ = ()
def __repr__(self):
return "cpuid_result(eax={eax:#010x}, ebx={ebx:#010x}, ecx={ecx:#010x}, edx={edx:#010x})".format(**self._asdict())
def cpuid(cpu_id, leaf, subleaf):
result = subprocess.run(["cpuid", "-l", str(leaf), "-s", str(subleaf), "-r"], stdout=subprocess.PIPE, check=True)
stdout = result.stdout.decode("ascii").replace("\n", "")
regex = re.compile(f"CPU {cpu_id}:[^:]*: eax=({regex_hex}) ebx=({regex_hex}) ecx=({regex_hex}) edx=({regex_hex})")
m = regex.search(stdout)
if m:
regs = list(map(lambda idx: int(m.group(idx), base=16), range(1, 5)))
else:
regs = [0] * 4
return cpuid_result(*regs)
class CPUID(object):
# Subclasses must define a "leaf" field as part of the class definition.
def __init__(self, regs):
self.regs = regs
@classmethod
def read(cls, cpu_id, subleaf=0):
r = cls(cpuid(cpu_id, cls.leaf, subleaf))
r.cpu_id = cpu_id
r.subleaf = subleaf
return r
# FIXME: This allows getting subleaves, but requires having an instance of
# the class first, which means always reading subleaf 0 and then the
# desired subleaf.
def __getitem__(self, subleaf):
return self.read(self.cpu_id, subleaf)
def __eq__(self, other):
return self.regs == other.regs
def __ne__(self, other):
return self.regs != other.regs
def __str__(self):
T = type(self)
fields = dict((regnum, {}) for regnum in range(len(self.regs._fields)))
properties = list()
for field_name in dir(T):
field = getattr(T, field_name)
if isinstance(field, cpuidfield):
fields[field.reg][field_name] = field
elif isinstance(field, property):
properties.append(field_name)
heading = "CPU ID {:#x} -- ".format(self.cpu_id)
heading += "CPUID (EAX={:#x}".format(self.leaf)
if self.subleaf:
heading += ", ECX={:#x}".format(self.subleaf)
heading += ")"
s = heading + "\n" + "-"*len(heading) + "\n"
doc = inspect.getdoc(self)
if doc:
s += doc + "\n"
def format_range(msb, lsb):
if msb == lsb:
return "[{}]".format(msb)
return "[{}:{}]".format(msb, lsb)
def format_field(msb, lsb, value):
"""Field formatter that special-cases single bits and drops the 0x"""
if msb == lsb:
return str(value)
return "{:#x}".format(value)
for regnum, regname in enumerate(self.regs._fields):
s += "\n"
s1 = " {}={:#010x} ".format(regname, self.regs[regnum])
s += s1
inner = ("\n " + " " * len(s1)).join(
"{}{} {}={}".format(regname, format_range(field.msb, field.lsb), field_name, format_field(field.msb, field.lsb, getattr(self, field_name)))
for field_name, field in sorted(fields[regnum].items(), key=(lambda x: x[1].lsb))
)
if inner:
s += " {}".format(inner)
properties = sorted(set(properties))
if len(properties):
s += "\n Attributes derived from one or more fields:"
for property_name in properties:
s += '\n'
temp = "{}={}".format(property_name, getattr(self, property_name))
s += '\n'.join(_wrapper.wrap(temp))
return s
class cpuidfield(property):
def __init__(self, reg, msb, lsb, doc="Bogus"):
self.reg = reg
self.msb = msb
self.lsb = lsb
max_value = (1 << (msb - lsb + 1)) - 1
field_mask = max_value << lsb
def getter(self):
return (self.regs[reg] & field_mask) >> lsb
super(cpuidfield, self).__init__(getter, doc=doc)
class MSR(object):
# Subclasses must define a "addr" field as part of the class definition.
def __init__(self, value=0):
self.value = value
def __eq__(self, other):
return self.value == other.value
def __ne__(self, other):
return self.value != other.value
@classmethod
def rdmsr(cls, cpu_id):
r = cls(bits.rdmsr(cpu_id, cls.addr))
r.cpu_id = cpu_id
return r
def wrmsr(self, cpu_id=None):
if cpu_id is None:
cpu_id = self.cpu_id
bits.wrmsr(cpu_id, self.addr, self.value)
def __str__(self):
T = type(self)
fields = {}
properties = []
for field_name in dir(T):
field = getattr(T, field_name)
if isinstance(field, msrfield):
fields[field_name] = field
elif isinstance(field, property):
properties.append(field_name)
heading = "CPU ID {:#x} -- ".format(self.cpu_id)
heading += "MSR {:#x}".format(self.addr)
s = heading + "\n" + "-"*len(heading) + "\n"
doc = inspect.getdoc(self)
if doc:
s += doc + "\n\n"
s += "MSR {:#x}".format(self.addr)
if self.value is None:
s += ' value=GPF'
return s
s += ' value={:#x}'.format(self.value)
for field_name, field in sorted(fields.items(), key=(lambda x: x[1].lsb)):
s += '\n'
temp = "[{}:{}] {}={:#x}".format(field.msb, field.lsb, field_name, getattr(self, field_name))
# FIXME: check wrapper, and use a hanging indent to wrap the docstring to len(temp)+1
if field.__doc__:
temp += " " + inspect.getdoc(field)
s += '\n'.join(_wrapper.wrap(temp))
if properties:
s += "\n Attributes derived from one or more fields:"
for property_name in sorted(properties):
s += '\n'
temp = "{}={}".format(property_name, getattr(self, property_name))
# FIXME: check wrapper, get the property documentation string if any, and use a hanging indent to wrap the docstring to len(temp)+1
s += '\n'.join(_wrapper.wrap(temp))
return s
class msrfield(property):
def __init__(self, msb, lsb, doc=None):
self.msb = msb
self.lsb = lsb
max_value = (1 << (msb - lsb + 1)) - 1
field_mask = max_value << lsb
def getter(self):
return (self.value & field_mask) >> lsb
def setter(self, value):
if value > max_value:
if msb == lsb:
field = "[{0}]".format(msb)
else:
field = "[{0}:{1}]".format(msb, lsb)
raise OverflowError("Value {value:#x} too big for MSR {self.addr:#x} field {field}".format(**locals()))
self.value = (self.value & ~field_mask) | (value << lsb)
super(msrfield, self).__init__(getter, setter, doc=doc)