1# Copyright (C) 2021-2022 Intel Corporation.
2#
3# SPDX-License-Identifier: BSD-3-Clause
4#
5
6import logging
7import lxml.etree
8import re
9
10from cpuparser import parse_cpuid, get_online_cpu_ids
11from cpuparser.msr import *
12from extractors.helpers import add_child, get_node
13
14level_types = {
15    1: "thread",
16    2: "core",
17    3: "module",
18    4: "tile",
19    5: "die",
20}
21
22def get_parent(processors_node, topo_level, topo_id):
23    n = get_node(processors_node, f"//{topo_level}[@id='{topo_id}']")
24    return n
25
26def get_or_create_parent(processors_node, topo_level, topo_id):
27    n = get_parent(processors_node, topo_level, topo_id)
28    if n is None:
29        n = lxml.etree.Element(topo_level)
30        n.set("id", topo_id)
31        return (n, True)
32    return (n, False)
33
34def extract_model(processors_node, cpu_id, family_id, model_id, core_type, native_model_id):
35    n = get_node(processors_node, f"//model[family_id='{family_id}' and model_id='{model_id}' and core_type='{core_type}' and native_model_id='{native_model_id}']")
36    if n is None:
37        n = add_child(processors_node, "model")
38
39        add_child(n, "family_id", family_id)
40        add_child(n, "model_id", model_id)
41        add_child(n, "core_type", core_type)
42        add_child(n, "native_model_id", native_model_id)
43
44        brandstring = b""
45        for leaf in [0x80000002, 0x80000003, 0x80000004]:
46            leaf_data = parse_cpuid(leaf, 0, cpu_id)
47            brandstring += leaf_data.brandstring
48        n.set("description", re.sub('[^!-~]+', ' ', brandstring.decode()).strip())
49
50        leaves = [(1, 0), (6, 0), (7, 0), (0x80000001, 0), (0x80000007, 0)]
51        for leaf in leaves:
52            leaf_data = parse_cpuid(leaf[0], leaf[1], cpu_id)
53            for cap in leaf_data.capability_bits:
54                if getattr(leaf_data, cap) == 1:
55                    add_child(n, "capability", id=cap)
56
57        msr_regs = [MSR_IA32_MISC_ENABLE, MSR_IA32_FEATURE_CONTROL, MSR_IA32_VMX_BASIC,
58                    MSR_IA32_VMX_PINBASED_CTLS, MSR_IA32_VMX_PROCBASED_CTLS, MSR_IA32_VMX_EXIT_CTLS,
59                    MSR_IA32_VMX_ENTRY_CTLS, MSR_IA32_VMX_MISC, MSR_IA32_VMX_PROCBASED_CTLS2,
60                    MSR_IA32_VMX_EPT_VPID_CAP]
61        for msr_reg in msr_regs:
62            msr_data = msr_reg.rdmsr(cpu_id)
63            for cap in msr_data.capability_bits:
64                if getattr(msr_data, cap) == 1:
65                    add_child(n, "capability", id=cap)
66
67        leaves = [(0, 0), (0x80000008, 0)]
68        for leaf in leaves:
69            leaf_data = parse_cpuid(leaf[0], leaf[1], cpu_id)
70            for cap in leaf_data.attribute_bits:
71                add_child(n, "attribute", str(getattr(leaf_data, cap)), id=cap)
72
73        msr_regs = [MSR_TURBO_RATIO_LIMIT, MSR_TURBO_ACTIVATION_RATIO]
74        for msr_reg in msr_regs:
75            try:
76                msr_data = msr_reg.rdmsr(cpu_id)
77                for attr in msr_data.attribute_bits:
78                    add_child(n, "attribute", str(getattr(msr_data, attr)), id=attr)
79            except IOError:
80                logging.debug(f"No {msr_reg} MSR info for CPU {cpu_id}.")
81
82def extract_topology(processors_node):
83    cpu_ids = get_online_cpu_ids()
84    for cpu_id in cpu_ids:
85        subleaf = 0
86        last_shift = 0
87        last_node = None
88
89        leaf_0 = parse_cpuid(0, 0, cpu_id)
90        if leaf_0.max_leaf >= 0x1f:
91            topo_leaf = 0x1f
92        else:
93            topo_leaf = 0xb
94
95        while True:
96            leaf_topo = parse_cpuid(topo_leaf, subleaf, cpu_id)
97            if leaf_topo.level_type == 0:
98                highest_level = max(level_types.keys())
99                if last_node.tag != level_types[highest_level]:
100                    n, _ = get_or_create_parent(processors_node, level_types[highest_level], "0x0")
101                    n.append(last_node)
102                    last_node = n
103                processors_node.append(last_node)
104                break
105
106            topo_level = level_types[leaf_topo.level_type]
107            topo_id = hex(leaf_topo.x2apic_id >> last_shift)
108            n, created = get_or_create_parent(processors_node, topo_level, topo_id)
109
110            if last_node is None:
111                leaf_1 = parse_cpuid(1, 0, cpu_id)
112                family_id = hex(leaf_1.display_family)
113                model_id = hex(leaf_1.display_model)
114                if leaf_0.max_leaf >= 0x1a:
115                    leaf_1a = parse_cpuid(0x1a, 0, cpu_id)
116                    core_type = leaf_1a.core_type
117                    native_model_id = hex(leaf_1a.native_model_id)
118                else:
119                    core_type = ""
120                    native_model_id = ""
121
122                add_child(n, "cpu_id", text=str(cpu_id))
123                add_child(n, "apic_id", text=hex(leaf_1.initial_apic_id))
124                add_child(n, "x2apic_id", text=hex(leaf_topo.x2apic_id))
125                add_child(n, "family_id", text=family_id)
126                add_child(n, "model_id", text=model_id)
127                add_child(n, "stepping_id", text=hex(leaf_1.stepping))
128                add_child(n, "core_type", text=core_type)
129                add_child(n, "native_model_id", text=native_model_id)
130
131                extract_model(processors_node, cpu_id, family_id, model_id, core_type, native_model_id)
132            else:
133                n.append(last_node)
134
135            if not created:
136                break
137
138            last_node = n
139            last_shift = leaf_topo.num_bit_shift
140            subleaf += 1
141
142def extract_hwp_info(processors_node):
143    if not processors_node.xpath("//capability[@id = 'hwp_supported']"):
144        return
145
146    # SDM Vol3 14.4.2: Additional MSRs associated with HWP may only be accessed after HWP is enabled
147    msr_hwp_en = MSR_IA32_PM_ENABLE()
148    msr_hwp_en.hwp_enable = 1
149    msr_hwp_en.wrmsr(0)
150
151    threads = processors_node.xpath("//thread")
152    for thread in threads:
153        cpu_id = get_node(thread, "cpu_id/text()")
154        msr_regs = [MSR_IA32_HWP_CAPABILITIES,]
155        for msr_reg in msr_regs:
156            msr_data = msr_reg.rdmsr(cpu_id)
157            for attr in msr_data.attribute_bits:
158                add_child(thread, attr, str(getattr(msr_data, attr)))
159
160def extract_psd_info(processors_node):
161    sysnode = '/sys/devices/system/cpu/'
162    threads = processors_node.xpath("//thread")
163    for thread in threads:
164        cpu_id = get_node(thread, "cpu_id/text()")
165        try:
166            with open(sysnode + "cpu{cpu_id}/cpufreq/freqdomain_cpus", 'r') as f_node:
167                freqdomain_cpus = f_node.read()
168        except IOError:
169            logging.info("No _PSD info for cpu {cpu_id}")
170            freqdomain_cpus = cpu_id
171
172        freqdomain_cpus.replace('\n','')
173        add_child(thread, "freqdomain_cpus", freqdomain_cpus)
174
175def extract(args, board_etree):
176    processors_node = get_node(board_etree, "//processors")
177    extract_topology(processors_node)
178    extract_hwp_info(processors_node)
179    extract_psd_info(processors_node)
180