1# Copyright (C) 2021-2022 Intel Corporation. 2# 3# SPDX-License-Identifier: BSD-3-Clause 4# 5 6import logging 7import lxml.etree 8from extractors.helpers import add_child, get_node 9 10from cpuparser import parse_cpuid 11import cpuparser.msr as msr 12from acpiparser import parse_rtct 13import acpiparser.rtct 14 15known_cbms = { 16 # From 11th Gen Intel(R) Core(TM) Processors Real-Time Tuning Guide, document number 640980-1.4 17 "11th Gen Intel(R) Core(TM) i3-1115GRE": 12, 18 "11th Gen Intel(R) Core(TM) i5-1145GRE": 8, 19 "11th Gen Intel(R) Core(TM) i7-1185GRE": 12, 20} 21 22def infer_l3_cat(cpu_id, processor_model_node, cache_node): 23 # First of all, existence of L3 CAT is indicated by the presence of IA32_L3_MASK_0 at C90H 24 try: 25 ia32_l3_mask_0 = msr.MSR_IA32_L3_MASK_n(0).rdmsr(cpu_id) 26 except IOError: 27 return 28 29 # If L3 CAT does exist, try inferring its parameters: 30 # 31 # - For capacity mask length, detect in an trial-and-error way starting from: 32 # a. the capacity mask length documented in any public real-time tuning guide, if any. 33 # b. or, the number of ways of the L3 cache. 34 # 35 # - For the number of CLOS IDs available, detect by searching the last programmable IA32_L3_MASK_n register within 36 # the C90H - D0FH range which is the architecturally defined MSR space for those registers. 37 # 38 # - For CDP, try setting the enable bit in IA32_L3_QOS_CFG. CDP is available if and only if that MSR is present 39 # and its bit 0 can be set. 40 41 # Initial guess of the capacity mask length 42 capacity_mask_length = int(cache_node.find("ways").text) 43 processor_model = processor_model_node.get("description") 44 for k, v in known_cbms.items(): 45 if processor_model.startswith(k): 46 capacity_mask_length = v 47 break 48 49 # Verify our guess. If the verification fails, decrease by 1 and guess again. 50 while capacity_mask_length > 0: 51 ia32_l3_mask_0.bit_mask = (1 << capacity_mask_length) - 1 52 try: 53 ia32_l3_mask_0.wrmsr() 54 break 55 except IOError: 56 capacity_mask_length = capacity_mask_length - 1 57 continue 58 else: 59 logging.debug("All writes to IA32_L3_MASK_0 failed. Cannot guess the capacity mask length of L3 CAT.") 60 return 61 62 # Binary search of the number of CLOS available 63 known_good = 1 64 known_bad = 129 65 while known_good + 1 < known_bad: 66 mid = (known_good + known_bad) // 2 67 try: 68 msr.MSR_IA32_L3_MASK_n(mid - 1).rdmsr(cpu_id) 69 known_good = mid 70 except IOError: 71 known_bad = mid 72 clos_number = known_good 73 74 # Detect availability of CDP by trying to write the enable bit. 75 try: 76 l3_qos_cfg = msr.MSR_IA32_L3_QOS_CFG.rdmsr(cpu_id) 77 l3_qos_cfg.cdp_enable = 1 78 l3_qos_cfg.wrmsr() 79 has_cdp = True 80 except IOError: 81 has_cdp = False 82 83 cap = add_child(cache_node, "capability", None, id="CAT") 84 add_child(cap, "capacity_mask_length", str(capacity_mask_length)) 85 add_child(cap, "clos_number", str(clos_number)) 86 if has_cdp: 87 add_child(cap, "capability", None, id="CDP") 88 89def extract_topology(args, root_node, caches_node): 90 threads = root_node.xpath("//processors//*[cpu_id]") 91 for thread in threads: 92 subleaf = 0 93 while True: 94 cpu_id = int(get_node(thread, "cpu_id/text()")) 95 leaf_4 = parse_cpuid(4, subleaf, cpu_id) 96 cache_type = leaf_4.cache_type 97 if cache_type == 0: 98 break 99 100 cache_level = leaf_4.cache_level 101 shift_width = leaf_4.max_logical_processors_sharing_cache.bit_length() - 1 102 cache_id = hex(int(get_node(thread, "apic_id/text()"), base=16) >> shift_width) 103 104 n = get_node(caches_node, f"cache[@id='{cache_id}' and @type='{cache_type}' and @level='{cache_level}']") 105 if n is None: 106 n = add_child(caches_node, "cache", None, level=str(cache_level), id=cache_id, type=str(cache_type)) 107 add_child(n, "cache_size", str(leaf_4.cache_size)) 108 add_child(n, "line_size", str(leaf_4.line_size)) 109 add_child(n, "ways", str(leaf_4.ways)) 110 add_child(n, "sets", str(leaf_4.sets)) 111 add_child(n, "partitions", str(leaf_4.partitions)) 112 add_child(n, "self_initializing", str(leaf_4.self_initializing)) 113 add_child(n, "fully_associative", str(leaf_4.fully_associative)) 114 add_child(n, "write_back_invalidate", str(leaf_4.write_back_invalidate)) 115 add_child(n, "cache_inclusiveness", str(leaf_4.cache_inclusiveness)) 116 add_child(n, "complex_cache_indexing", str(leaf_4.complex_cache_indexing)) 117 add_child(n, "processors") 118 119 # Check support of Cache Allocation Technology 120 leaf_10 = parse_cpuid(0x10, 0, cpu_id) 121 if cache_level == 2: 122 leaf_10 = parse_cpuid(0x10, 2, cpu_id) if leaf_10.l2_cache_allocation == 1 else None 123 elif cache_level == 3: 124 leaf_10 = parse_cpuid(0x10, 1, cpu_id) if leaf_10.l3_cache_allocation == 1 else None 125 else: 126 leaf_10 = None 127 if leaf_10 is not None: 128 cap = add_child(n, "capability", None, id="CAT") 129 add_child(cap, "capacity_mask_length", str(leaf_10.capacity_mask_length)) 130 add_child(cap, "clos_number", str(leaf_10.clos_number)) 131 if leaf_10.code_and_data_prioritization == 1: 132 add_child(n, "capability", None, id="CDP") 133 134 # Inform the user if L3 CAT capability is specified manually. 135 if args.add_llc_cat: 136 logging.warning(r"The last level cache (cache ID: {cache_id}) already reports CAT capability. The explicit settings from the command line options are ignored.") 137 elif cache_level == 3: 138 if args.add_llc_cat: 139 # Inject L3 CAT capability specified by the user 140 cap = add_child(n, "capability", None, id="CAT") 141 add_child(cap, "capacity_mask_length", str(args.add_llc_cat.capacity_mask_length)) 142 add_child(cap, "clos_number", str(args.add_llc_cat.clos_number)) 143 if args.add_llc_cat.has_CDP: 144 add_child(cap, "capability", None, id="CDP") 145 else: 146 # Try inferring L3 CAT according to the methods described in section 7.2.3, 11th Gen Intel(R) 147 # Core(TM) Processors Real-Time Tuning Guide (document number: 640980-1.4). 148 family_id = thread.find("family_id").text 149 model_id = thread.find("model_id").text 150 core_type = thread.find("core_type").text 151 native_model_id = thread.find("native_model_id").text 152 processor_model_node = get_node(root_node, f"//processors/model[family_id='{family_id}' and model_id='{model_id}' and core_type='{core_type}' and native_model_id='{native_model_id}']") 153 infer_l3_cat(cpu_id, processor_model_node, n) 154 155 add_child(get_node(n, "processors"), "processor", get_node(thread, "apic_id/text()")) 156 157 subleaf += 1 158 159 def getkey(n): 160 level = int(n.get("level")) 161 id = int(n.get("id"), base=16) 162 type = int(n.get("type")) 163 return (level, id, type) 164 caches_node[:] = sorted(caches_node, key=getkey) 165 166def extract(args, board_etree): 167 root_node = board_etree.getroot() 168 caches_node = get_node(board_etree, "//caches") 169 extract_topology(args, root_node, caches_node) 170