From 598be99dc2e9af3334f900d6af388f4e5493f6ca Mon Sep 17 00:00:00 2001 From: Junjie Mao Date: Mon, 3 May 2021 10:26:58 +0800 Subject: [PATCH] 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 --- .../board_inspector/cpuparser/__init__.py | 61 ++ .../board_inspector/cpuparser/cpuids.py | 752 ++++++++++++++++++ .../board_inspector/cpuparser/platformbase.py | 209 +++++ 3 files changed, 1022 insertions(+) create mode 100644 misc/config_tools/board_inspector/cpuparser/__init__.py create mode 100644 misc/config_tools/board_inspector/cpuparser/cpuids.py create mode 100644 misc/config_tools/board_inspector/cpuparser/platformbase.py diff --git a/misc/config_tools/board_inspector/cpuparser/__init__.py b/misc/config_tools/board_inspector/cpuparser/__init__.py new file mode 100644 index 000000000..708778bb4 --- /dev/null +++ b/misc/config_tools/board_inspector/cpuparser/__init__.py @@ -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 diff --git a/misc/config_tools/board_inspector/cpuparser/cpuids.py b/misc/config_tools/board_inspector/cpuparser/cpuids.py new file mode 100644 index 000000000..5a37aaccc --- /dev/null +++ b/misc/config_tools/board_inspector/cpuparser/cpuids.py @@ -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") diff --git a/misc/config_tools/board_inspector/cpuparser/platformbase.py b/misc/config_tools/board_inspector/cpuparser/platformbase.py new file mode 100644 index 000000000..e7e859330 --- /dev/null +++ b/misc/config_tools/board_inspector/cpuparser/platformbase.py @@ -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)