├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SOURCES ├── cpuid_topology.c ├── cpuid_topology.h ├── cpuid_topology_display.c ├── cpuid_topology_file.c ├── cpuid_topology_parsecachetlb.c ├── cpuid_topology_parsecpu.c ├── cpuid_topology_tools.c ├── linux_os_util.c ├── security.md └── win_os_util.c /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, caste, color, religion, or sexual 10 | identity and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the overall 26 | community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or advances of 31 | any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email address, 35 | without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | CommunityCodeOfConduct AT intel DOT com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series of 86 | actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or permanent 93 | ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within the 113 | community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.1, available at 119 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 120 | 121 | Community Impact Guidelines were inspired by 122 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 123 | 124 | For answers to common questions about this code of conduct, see the FAQ at 125 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 126 | [https://www.contributor-covenant.org/translations][translations]. 127 | 128 | [homepage]: https://www.contributor-covenant.org 129 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 130 | [Mozilla CoC]: https://github.com/mozilla/diversity 131 | [FAQ]: https://www.contributor-covenant.org/faq 132 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ### License 4 | 5 | SDM-Processor-Topology-Enumeration is licensed under the terms in [LICENSE](LICENSE). By contributing to the project, you agree to the license and copyright terms therein and release your contribution under these terms. 6 | 7 | ### Sign your work 8 | 9 | Please use the sign-off line at the end of the patch. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch. The rules are pretty simple: if you can certify 10 | the below (from [developercertificate.org](http://developercertificate.org/)): 11 | 12 | ``` 13 | Developer Certificate of Origin 14 | Version 1.1 15 | 16 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 17 | 660 York Street, Suite 102, 18 | San Francisco, CA 94110 USA 19 | 20 | Everyone is permitted to copy and distribute verbatim copies of this 21 | license document, but changing it is not allowed. 22 | 23 | Developer's Certificate of Origin 1.1 24 | 25 | By making a contribution to this project, I certify that: 26 | 27 | (a) The contribution was created in whole or in part by me and I 28 | have the right to submit it under the open source license 29 | indicated in the file; or 30 | 31 | (b) The contribution is based upon previous work that, to the best 32 | of my knowledge, is covered under an appropriate open source 33 | license and I have the right under that license to submit that 34 | work with modifications, whether created in whole or in part 35 | by me, under the same open source license (unless I am 36 | permitted to submit under a different license), as indicated 37 | in the file; or 38 | 39 | (c) The contribution was provided directly to me by some other 40 | person who certified (a), (b) or (c) and I have not modified 41 | it. 42 | 43 | (d) I understand and agree that this project and the contribution 44 | are public and that a record of the contribution (including all 45 | personal information I submit with it, including my sign-off) is 46 | maintained indefinitely and may be redistributed consistent with 47 | this project or the open source license(s) involved. 48 | ``` 49 | 50 | Then you just add a line to every git commit message: 51 | 52 | Signed-off-by: Joe Smith 53 | 54 | Use your real name (sorry, no pseudonyms or anonymous contributions.) 55 | 56 | If you set your `user.name` and `user.email` git configs, you can sign your 57 | commit automatically with `git commit -s`. 58 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD Zero Clause License 2 | 3 | Copyright (C) 2023 Intel Corporation 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. 8 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 9 | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/SDM-Processor-Topology-Enumeration/3855a626c91ba4ec7efa96e13f72bc5659b57bbc/README.md -------------------------------------------------------------------------------- /SOURCES: -------------------------------------------------------------------------------- 1 | TARGETNAME=cpuidtopology 2 | TARGETPATH=obj 3 | TARGETTYPE=PROGRAM 4 | 5 | SOURCES=cpuid_topology.c cpuid_topology_file.c cpuid_topology_display.c cpuid_topology_parsecachetlb.c cpuid_topology_parsecpu.c cpuid_topology_tools.c win_os_util.c 6 | 7 | UMTYPE=console 8 | USE_MSVCRT=1 9 | -------------------------------------------------------------------------------- /cpuid_topology.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 by Intel Corporation 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 12 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include "cpuid_topology.h" 20 | 21 | 22 | /* 23 | * Function Pointer Definition for Handling command line input for a specific command 24 | */ 25 | typedef void (*PFN_DISPATCHFUNC)(unsigned int NumberOfParameters, char **Parameters); 26 | 27 | /* 28 | * Global application data variable 29 | */ 30 | extern GLOBAL_DATA g_GlobalData; 31 | 32 | /* 33 | * Command Line Dispatch Definition 34 | */ 35 | typedef struct _DISPATCH_COMMAND { 36 | char LowerCase; 37 | PFN_DISPATCHFUNC pfnDispatchFunc; 38 | } DISPATCH_COMMAND, *PDISPATCH_COMMAND; 39 | 40 | /* 41 | * Internal Prototypes 42 | */ 43 | void CpuidTopology_DispatchTaskCommand(unsigned int NumberOfParameters, char **Parameters); 44 | void CpuidTopology_DispatchCommand(unsigned int NumberOfParameters, char **Parameters); 45 | void CpuidTopology_DispatchReadFile(unsigned int NumberOfParameters, char **Parameters); 46 | void CpuidTopology_DispatchWriteFile(unsigned int NumberOfParameters, char **Parameters); 47 | void CpuidTopology_InitGlobal(void); 48 | void CpuidTopology_AllTopologyFromCpuid(void); 49 | 50 | 51 | 52 | /* 53 | * Global to contain the dispatch function to command line input. 54 | */ 55 | DISPATCH_COMMAND g_DispatchCommand[4] = { 56 | {'s', CpuidTopology_DispatchWriteFile }, 57 | {'l', CpuidTopology_DispatchReadFile }, 58 | {'c', CpuidTopology_DispatchTaskCommand }, 59 | {0, NULL } 60 | }; 61 | 62 | 63 | /* 64 | * main 65 | * 66 | * Entry Point 67 | * 68 | * Arguments: 69 | * argc - Number of Arguements 70 | * argv - Arguements 71 | * 72 | * Return: 73 | * Zero 74 | */ 75 | int main(int argc, char **argv) 76 | { 77 | if (argc < 2) 78 | { 79 | Display_DisplayParameters(); 80 | } 81 | else 82 | { 83 | CpuidTopology_InitGlobal(); 84 | CpuidTopology_DispatchCommand(argc - 1, &argv[1]); 85 | } 86 | 87 | return 0; 88 | } 89 | 90 | 91 | 92 | /* 93 | * CpuidTopology_DispatchCommand 94 | * 95 | * Dispatch the command line input to the function handler. 96 | * 97 | * Arguments: 98 | * Number of Parameters, Paramter List 99 | * 100 | * Return: 101 | * None 102 | */ 103 | void CpuidTopology_DispatchCommand(unsigned int NumberOfParameters, char **Parameters) 104 | { 105 | unsigned int Index; 106 | BOOL_TYPE CommandDispatched; 107 | 108 | CommandDispatched = BOOL_FALSE; 109 | Index = 0; 110 | 111 | while(CommandDispatched == BOOL_FALSE && g_DispatchCommand[Index].LowerCase != 0) 112 | { 113 | if (g_DispatchCommand[Index].LowerCase == (*Parameters[0] | ((char)0x20))) 114 | { 115 | g_DispatchCommand[Index].pfnDispatchFunc(NumberOfParameters - 1, Parameters + 1); 116 | CommandDispatched = BOOL_TRUE; 117 | } 118 | else 119 | { 120 | Index++; 121 | } 122 | } 123 | 124 | if (CommandDispatched == BOOL_FALSE) 125 | { 126 | Display_DisplayParameters(); 127 | } 128 | } 129 | 130 | 131 | /* 132 | * CpuidTopology_DispatchReadFile 133 | * 134 | * Command line handler to dispatch a Load CPUID from file request. 135 | * 136 | * Arguments: 137 | * Number of Parameters, Paramter List 138 | * 139 | * Return: 140 | * None 141 | */ 142 | void CpuidTopology_DispatchReadFile(unsigned int NumberOfParameters, char **Parameters) 143 | { 144 | if(NumberOfParameters >= 2) 145 | { 146 | if(File_ReadCpuidFromFile(Parameters[0]) == BOOL_TRUE) 147 | { 148 | printf("CPUID loaded from %s\n", Parameters[0]); 149 | CpuidTopology_DispatchTaskCommand(NumberOfParameters - 1, Parameters + 1); 150 | } 151 | else 152 | { 153 | printf("Failed to load CPUID to %s\n\n", Parameters[0]); 154 | Display_DisplayParameters(); 155 | } 156 | 157 | } 158 | else 159 | { 160 | printf("No file name to load CPUID or not command to dispatch afterwards.\n\n"); 161 | Display_DisplayParameters(); 162 | } 163 | } 164 | 165 | 166 | 167 | 168 | /* 169 | * CpuidTopology_DispatchWriteFile 170 | * 171 | * Command line handler to dispatch a request to write CPUID on this machine to a file. 172 | * 173 | * Arguments: 174 | * Number of Parameters, Paramter List 175 | * 176 | * Return: 177 | * None 178 | */ 179 | void CpuidTopology_DispatchWriteFile(unsigned int NumberOfParameters, char **Parameters) 180 | { 181 | 182 | if(NumberOfParameters >= 1) 183 | { 184 | if(File_WriteCpuidToFile(Parameters[0]) != BOOL_FALSE) 185 | { 186 | printf("CPUID saved to %s\n", Parameters[0]); 187 | } 188 | else 189 | { 190 | printf("Failed to save CPUID to %s\n\n", Parameters[0]); 191 | Display_DisplayParameters(); 192 | } 193 | } 194 | else 195 | { 196 | printf("No file name to save CPUID.\n\n"); 197 | Display_DisplayParameters(); 198 | } 199 | 200 | } 201 | 202 | 203 | 204 | 205 | /* 206 | * CpuidTopology_DispatchTaskCommand 207 | * 208 | * Dispatch a specific action request from the command line. 209 | * 210 | * Arguments: 211 | * Number of Parameters, Parameter List 212 | * 213 | * Return: 214 | * None 215 | */ 216 | void CpuidTopology_DispatchTaskCommand(unsigned int NumberOfParameters, char **Parameters) 217 | { 218 | /* 219 | * The Command Reference. 220 | * 221 | * 0 - Display the topology via OS APIs (Not valid with File Load since we do not have this data.) 222 | * 1 - Display the topology via CPUID 223 | * 2 - Display CPUID Leaf values one processor 224 | * 3 - Display CPUID Leaf Values all processors 225 | * 4 - Display APIC ID layout 226 | * 5 - Display TLB Information 227 | * 6 - Display Cache Information 228 | * 229 | */ 230 | 231 | if (NumberOfParameters > 0) 232 | { 233 | 234 | if (*Parameters[0] == '0') 235 | { 236 | if (Tools_IsNative()) 237 | { 238 | printf("The following demonstrates OS-provided topology information to applications.\n"); 239 | printf("It is reccomended for applications to utilize OS APIs where possible rather than direct CPUID manipulation.\n\n"); 240 | Os_DisplayTopology(); 241 | } 242 | else 243 | { 244 | Display_DisplayParameters(); 245 | } 246 | } 247 | else 248 | { 249 | 250 | switch (*Parameters[0]) 251 | { 252 | case '1': 253 | CpuidTopology_AllTopologyFromCpuid(); 254 | break; 255 | 256 | case '2': 257 | Display_DisplayProcessorLeafs(1); 258 | break; 259 | 260 | case '3': 261 | Display_DisplayProcessorLeafs(Tools_GetNumberOfProcessors()); 262 | break; 263 | 264 | case '4': 265 | ParseCpu_ApicIdTopologyLayout(); 266 | break; 267 | 268 | case '5': 269 | ParseTlb_CpuidTlbExample(); 270 | break; 271 | 272 | case '6': 273 | ParseCache_CpuidCacheExample(); 274 | break; 275 | 276 | default: 277 | Display_DisplayParameters(); 278 | } 279 | } 280 | } 281 | else 282 | { 283 | Display_DisplayParameters(); 284 | } 285 | 286 | } 287 | 288 | 289 | 290 | 291 | /* 292 | * CpuidTopology_AllTopologyFromCpuid 293 | * 294 | * Demonstrates displaing all topology from CPUID. 295 | * 296 | * Arguments: 297 | * None 298 | * 299 | * Return: 300 | * None 301 | */ 302 | void CpuidTopology_AllTopologyFromCpuid(void) 303 | { 304 | CPUID_REGISTERS CpuidRegisters; 305 | CPUID_REGISTERS CpuidRegistersTopologyLeaf; 306 | unsigned int FoundTopologyInfo; 307 | 308 | FoundTopologyInfo = 0; 309 | 310 | 311 | Tools_ReadCpuid(0, 0, &CpuidRegisters); 312 | 313 | if (CpuidRegisters.x.Register.Eax >= 0x1F) 314 | { 315 | Tools_ReadCpuid(0x1F, 0, &CpuidRegistersTopologyLeaf); 316 | if (CpuidRegistersTopologyLeaf.x.Register.Ebx != 0) 317 | { 318 | /* 319 | * Two Examples below. 320 | * 321 | * 1. Software that supports 3 Domains. 322 | * 2. Software that supports >3 Domains. 323 | */ 324 | ParseCpu_CpuidThreeDomainExample(0x1F); 325 | ParseCpu_CpuidManyDomainExample(0x1F); 326 | FoundTopologyInfo = 1; 327 | } 328 | } 329 | 330 | if (CpuidRegisters.x.Register.Eax >= 0xB && FoundTopologyInfo == 0) 331 | { 332 | Tools_ReadCpuid(0xB, 0, &CpuidRegistersTopologyLeaf); 333 | if (CpuidRegistersTopologyLeaf.x.Register.Ebx != 0) 334 | { 335 | /* 336 | * Both examples for Leaf Bh will yield the same results. The reason 337 | * for this example is to show that regardless of Leaf 1Fh or Leaf Bh, 338 | * they can both use the new Leaf 1Fh documented algorithm so the code 339 | * can be common. 340 | */ 341 | ParseCpu_CpuidThreeDomainExample(0xB); 342 | ParseCpu_CpuidManyDomainExample(0xB); 343 | 344 | FoundTopologyInfo = 1; 345 | } 346 | } 347 | 348 | if (FoundTopologyInfo == 0) 349 | { 350 | printf("\nUsing legacy CPUID Methods.\n"); 351 | ParseCpu_CpuidLegacyExample(); 352 | } 353 | else 354 | { 355 | printf("\nNOTE: Attempting the legacy CPUID method on this platform for demonstration.\n"); 356 | printf("This method is superceded by Leaf Bh and Leaf 1Fh\n"); 357 | printf("Large platforms may show incorrect information since X2APIC are 32 bit\n"); 358 | printf("and this legacy method is for 8 bit APIC IDs.\n\n"); 359 | ParseCpu_CpuidLegacyExample(); 360 | } 361 | } 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | /* 372 | * CpuidTopology_InitGlobal 373 | * 374 | * Initialize the global structure for this file. 375 | * 376 | * Arguments: 377 | * None 378 | * 379 | * Return: 380 | * None 381 | */ 382 | void CpuidTopology_InitGlobal(void) 383 | { 384 | g_GlobalData.UseNativeCpuid = BOOL_TRUE; 385 | } 386 | 387 | 388 | 389 | -------------------------------------------------------------------------------- /cpuid_topology.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 by Intel Corporation 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 12 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | 17 | #ifndef TOPOLOGY_H__ 18 | #define TOPOLOGY_H__ 19 | 20 | 21 | /* 22 | * The following constants are used to simplify the code and in 23 | * many places is used for static array creation as opposed to dynamically 24 | * at runtime determining the size. 25 | * 26 | */ 27 | #define MAX_PROCESSORS 1024 28 | #define MAX_SIMULATED_LEAFS 0x20 29 | #define MAX_SIMULATED_SUBLEAFS 10 30 | #define MAX_CACHE_PER_LP 10 31 | #define MAX_TLB_PER_LP 25 32 | #define INVALID_CACHE_INDEX ((unsigned int)-1) 33 | #define INVALID_TLB_INDEX ((unsigned int)-1) 34 | 35 | /* 36 | * The maximum number of enumerated domains, since X2APIC is 32 bits there 37 | * really can't be more than 32 domains enumerated. 38 | */ 39 | #define MAXIMUM_DOMAINS 32 40 | 41 | /* 42 | * Constants for cache / tlb size enumerations 43 | */ 44 | #define BYTES_IN_KB (1024) 45 | #define BYTES_IN_MB (1048576) 46 | 47 | 48 | 49 | 50 | /* 51 | * CPUID structure for using the return data of CPUID 52 | * in the application. 53 | */ 54 | typedef struct _CPUID_REGISTERS { 55 | 56 | union { 57 | unsigned int Registers[4]; 58 | struct { 59 | unsigned int Eax; 60 | unsigned int Ebx; 61 | unsigned int Ecx; 62 | unsigned int Edx; 63 | } Register; 64 | } x; 65 | 66 | } CPUID_REGISTERS, *PCPUID_REGISTERS; 67 | 68 | /* 69 | * Create a platform independnet boolean type 70 | */ 71 | typedef enum _BOOL_TYPE { 72 | BOOL_FALSE = 0, 73 | BOOL_TRUE 74 | } BOOL_TYPE, *PBOOL_TYPE; 75 | 76 | 77 | /* 78 | * Caching Structure 79 | */ 80 | typedef struct _CPUID_CACHE_INFO 81 | { 82 | /* 83 | * The Cache Type (Data, Instruction) and then the Level (L1, L2,...) 84 | */ 85 | unsigned int CacheType; 86 | unsigned int CacheLevel; 87 | 88 | /* 89 | * The Cache Id that identifies a particular cache and the 90 | * APIC ID mask used to determine the Cache Id. 91 | */ 92 | unsigned int CacheId; 93 | unsigned int CacheMask; 94 | 95 | /* 96 | * Physical description of the cache. 97 | */ 98 | unsigned int CacheWays; 99 | unsigned int CachePartitions; 100 | unsigned int CacheLineSize; 101 | unsigned int CacheSets; 102 | 103 | unsigned int CacheSizeInBytes; 104 | 105 | /* 106 | * Attributes of the cache. 107 | */ 108 | BOOL_TYPE SelfInitializing; 109 | BOOL_TYPE CacheIsFullyAssociative; 110 | BOOL_TYPE WbinvdFlushsLowerLevelsSharing; 111 | BOOL_TYPE CacheIsInclusive; 112 | BOOL_TYPE CacheIsDirectMapped; 113 | BOOL_TYPE CacheIsComplex; 114 | 115 | /* 116 | * The list of shared APIC IDs sharing this cache. 117 | */ 118 | unsigned int NumberOfLPsSharingThisCache; 119 | unsigned int ListOfApicIDsSharingThisCache[MAX_PROCESSORS]; 120 | 121 | /* 122 | * The CPUID description of this cache. 123 | */ 124 | CPUID_REGISTERS CachedCpuidRegisters; 125 | 126 | } CPUID_CACHE_INFO, *PCPUID_CACHE_INFO; 127 | 128 | 129 | /* 130 | * TLB Structure 131 | */ 132 | typedef struct _CPUID_TLB_INFO 133 | { 134 | /* 135 | * The TLB Type (Data, Instruction) and then the Level (L1, L2,...) 136 | */ 137 | unsigned int TlbType; 138 | unsigned int TlbLevel; 139 | 140 | /* 141 | * The TLB Id that identifies a particular TLB and the 142 | * APIC ID mask used to determine the TLB Id. 143 | */ 144 | unsigned int TlbId; 145 | unsigned int TlbMask; 146 | 147 | /* 148 | * Physical description of the TLB. 149 | */ 150 | unsigned int TlbWays; 151 | unsigned int TlbParitioning; 152 | unsigned int TlbSets; 153 | 154 | /* 155 | * TLB Supported Page Sizes 156 | */ 157 | BOOL_TYPE _4K_PageSizeEntries; 158 | BOOL_TYPE _2MB_PageSizeEntries; 159 | BOOL_TYPE _4MB_PageSizeEntries; 160 | BOOL_TYPE _1GB_PageSizeEntries; 161 | 162 | /* 163 | * Attributes of the TLB. 164 | */ 165 | BOOL_TYPE FullyAssociative; 166 | 167 | /* 168 | * The list of shared APIC IDs sharing this TLB. 169 | */ 170 | unsigned int NumberOfLPsSharingThisTlb; 171 | unsigned int ListOfApicIDsSharingThisTlb[MAX_PROCESSORS]; 172 | 173 | /* 174 | * The CPUID description of this TLB. 175 | */ 176 | CPUID_REGISTERS CachedCpuidRegisters; 177 | 178 | } CPUID_TLB_INFO, *PCPUID_TLB_INFO; 179 | 180 | 181 | 182 | 183 | 184 | 185 | /* 186 | * The enumeration of domain identifiers and these need to each match 187 | * the value as specified by CPUID.1F and CPUID.B documentation. 188 | */ 189 | typedef enum _CPU_DOMAIN { 190 | InvalidDomain = 0, 191 | LogicalProcessorDomain, 192 | CoreDomain, 193 | ModuleDomain, 194 | TileDomain, 195 | DieDomain, 196 | DieGrpDomain 197 | } CPU_DOMAIN, *PCPU_DOMAIN; 198 | 199 | 200 | /* 201 | * Create an enumeration of cache types. 202 | */ 203 | typedef enum _CACHE_TYPE { 204 | CacheType_NoMoreCaches = 0, 205 | CacheType_DataCache, 206 | CacheType_InstructionCache, 207 | CacheType_UnifiedCache 208 | } CACHE_TYPE, *PCACHE_TYPE; 209 | 210 | 211 | /* 212 | * Create an enumeration of tlb types. 213 | */ 214 | typedef enum _TLB_TYPE { 215 | TlbType_InvalidSubleaf = 0, 216 | TlbType_Data, 217 | TlbType_Instruction, 218 | TlbType_Unified, 219 | TlbType_LoadOnly, 220 | TlbType_StoreOnly 221 | } TLB_TYPE, *PTLB_TYPE; 222 | 223 | 224 | 225 | 226 | /* 227 | * This data structure is used to communicate CPU Topology structure 228 | * values and bits as derived from CPUID.B or CPUID.1F. It can also 229 | * be used for legacy as hand coded for the APIs that use it. 230 | */ 231 | 232 | typedef struct _APICID_BIT_LAYOUT_CTX { 233 | 234 | /* 235 | * To support this as legacy APIC, the structure will contain the number 236 | * of bits that represent an APIC ID, which has been 4, 8 and 32(Today). 237 | * 238 | * This code only will set it to 8 or 32. 239 | */ 240 | unsigned int NumberOfApicIdBits; 241 | 242 | /* 243 | * These are a cache of CPUID Topology as returned from CPUID.1F or CPUID.B. 244 | * 245 | * The usage beyond mirroring the values in a simple structure is that these 246 | * values can contain a collapsed version from Unknown Domains to a list of 247 | * all known domains or other number of levels. 248 | * 249 | */ 250 | unsigned int ShiftValues[MAXIMUM_DOMAINS]; 251 | unsigned int ShiftValueDomain[MAXIMUM_DOMAINS]; 252 | 253 | 254 | /* 255 | * This is a domain relative where the index is based on the domain 256 | * level index. The second index determines the relative to the current 257 | * domain mask. The index where both entries are the current domain represents 258 | * a global mask to ID this domain level globally. 259 | * 260 | * The indexes then move to the next higher domian creating a relative mask from the 261 | * current domain relative to the second domain level index. 262 | * 263 | */ 264 | unsigned int DomainRelativeMasks[MAXIMUM_DOMAINS][MAXIMUM_DOMAINS]; 265 | 266 | /* 267 | * The top index in the above matrix that contains the package domain. 268 | */ 269 | unsigned int PackageDomainIndex; 270 | 271 | /* 272 | * This is a string that allows a description to be passed from the parsing function 273 | * to the general display function for context. 274 | */ 275 | char szDescription[256]; 276 | 277 | } APICID_BIT_LAYOUT_CTX, *PAPICID_BIT_LAYOUT_CTX; 278 | 279 | 280 | 281 | /* 282 | * Internal Structures and types 283 | */ 284 | typedef struct _GLOBAL_DATA { 285 | 286 | /* 287 | * This determines if the Virtual CPUID or the Native CPUID should be used. 288 | */ 289 | BOOL_TYPE UseNativeCpuid; 290 | 291 | /* 292 | * The Virtual CPUID simulation values. 293 | */ 294 | unsigned int SimulatedCpuid[MAX_SIMULATED_LEAFS][MAX_SIMULATED_SUBLEAFS][4]; 295 | 296 | /* 297 | * The Virtual CPUID simulation value for CPUID.4; since it is asymmetric. 298 | */ 299 | unsigned int SimulatedCpuidLeaf4[MAX_PROCESSORS][MAX_SIMULATED_SUBLEAFS][4]; 300 | 301 | /* 302 | * The Virtual CPUID simulation value for CPUID.18; since it is asymmetric. 303 | */ 304 | unsigned int SimulatedCpuidLeaf18[MAX_PROCESSORS][MAX_SIMULATED_SUBLEAFS][4]; 305 | 306 | /* 307 | * The list of the Simulated APIC IDs. 308 | */ 309 | unsigned int SimulatedApicIds[MAX_PROCESSORS]; 310 | 311 | /* 312 | * The number of simulated processors . 313 | */ 314 | unsigned int NumberOfSimulatedProcessors; 315 | 316 | /* 317 | * The main execution thread's Processor Affinity Number. 318 | */ 319 | unsigned int CurrentProcessorAffinity; 320 | 321 | 322 | } GLOBAL_DATA, *PGLOBAL_DATA; 323 | 324 | 325 | 326 | /* 327 | * Parse CPUID Topology Information APIs 328 | */ 329 | void ParseCpu_ApicIdTopologyLayout(void); 330 | void ParseCpu_CpuidLegacyExample(void); 331 | void ParseCpu_CpuidThreeDomainExample(unsigned int Leaf); 332 | void ParseCpu_CpuidManyDomainExample(unsigned int Leaf); 333 | 334 | 335 | 336 | /* 337 | * Parse CPUID Cache and Tlbs APIs 338 | */ 339 | void ParseCache_CpuidCacheExample(void); 340 | void ParseTlb_CpuidTlbExample(void); 341 | 342 | /* 343 | * Display APIs 344 | */ 345 | void Display_DisplayParameters(void); 346 | void Display_ApicIdBitLayout(PAPICID_BIT_LAYOUT_CTX pApicidBitLayoutCtx); 347 | void Display_ThreeDomainDisplay(unsigned int Leaf, unsigned int PackageShift, unsigned int LogicalProcessorShift); 348 | void Display_DisplayProcessorLeafs(unsigned int NumberOfProcessors); 349 | void Display_ManyDomainExample(unsigned int Leaf, PAPICID_BIT_LAYOUT_CTX pApicidBitLayoutCtx); 350 | void Display_DisplayProcessorCaches(PCPUID_CACHE_INFO pCacheInfo, unsigned int NumberOfCaches); 351 | void Display_DisplayProcessorTlbs(PCPUID_TLB_INFO pTlbInfo, unsigned int NumberOfTlbs); 352 | 353 | /* 354 | * Common Support Tools and Initialization APIs 355 | */ 356 | void Tools_ReadCpuid(unsigned int Leaf, unsigned int Subleaf, PCPUID_REGISTERS pCpuidRegisters); 357 | unsigned int Tools_CreateTopologyShift(unsigned int count); 358 | void Tools_SetAffinity(unsigned int ProcessorNumber); 359 | unsigned int Tools_GetNumberOfProcessors(void); 360 | BOOL_TYPE Tools_IsNative(void); 361 | BOOL_TYPE Tools_IsDomainKnownEnumeration(unsigned int Domain); 362 | unsigned int Tools_GatherPlatformApicIds(unsigned int *pApicIdArray, unsigned int ArraySize); 363 | 364 | 365 | /* 366 | * File Read/Write CPUID APIs 367 | */ 368 | BOOL_TYPE File_ReadCpuidFromFile(char *pszFileName); 369 | BOOL_TYPE File_WriteCpuidToFile(char *pszFileName); 370 | 371 | /* 372 | * OS-Specific Implementation APIs 373 | */ 374 | void Os_DisplayTopology(void); 375 | void Os_Platform_Read_Cpuid(unsigned int Leaf, unsigned int Subleaf, PCPUID_REGISTERS pCpuidRegisters); 376 | unsigned int Os_GetNumberOfProcessors(void); 377 | void Os_SetAffinity(unsigned int ProcessorNumber); 378 | 379 | 380 | #endif 381 | 382 | 383 | -------------------------------------------------------------------------------- /cpuid_topology_display.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 by Intel Corporation 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 12 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include "cpuid_topology.h" 20 | 21 | /* 22 | * Global application data variable 23 | */ 24 | extern GLOBAL_DATA g_GlobalData; 25 | 26 | 27 | /* 28 | * Internal Display APIs 29 | */ 30 | void Display_Internal_DisplaySubLeafs(unsigned int Leaf); 31 | 32 | 33 | 34 | /* 35 | * Display_DisplayParameters 36 | * 37 | * Display the commandline parameters 38 | * 39 | * Arguments: 40 | * None 41 | * 42 | * Return: 43 | * None 44 | */ 45 | void Display_DisplayParameters(void) 46 | { 47 | printf("Processor Topology Example.\n"); 48 | printf(" Command Line Options:\n\n"); 49 | printf(" H - Display this message\n"); 50 | printf(" S [File] - Saves raw CPUID to a file.\n"); 51 | printf(" L [File] [COMMAND] - Loads raw CPUID from a file and perform a numbered COMMAND.\n"); 52 | printf(" C [COMMAND] - Execute the numbered command from below.\n\n"); 53 | printf(" List of commands\n"); 54 | printf(" 0 - Display the topology via OS APIs (Not valid with File Load)\n"); 55 | printf(" 1 - Display the topology via CPUID\n"); 56 | printf(" 2 - Display CPUID Leaf values one processor\n"); 57 | printf(" 3 - Display CPUID Leaf values all processors\n"); 58 | printf(" 4 - Display APIC ID layout\n"); 59 | printf(" 5 - Display TLB Information\n"); 60 | printf(" 6 - Display Cache Information\n"); 61 | printf("\n"); 62 | } 63 | 64 | 65 | /* 66 | * Display_DisplayProcessorLeafs 67 | * 68 | * Display the raw CPUID leafs for the processors. 69 | * 70 | * Arguments: 71 | * Number of Processors 72 | * 73 | * Return: 74 | * None 75 | */ 76 | void Display_DisplayProcessorLeafs(unsigned int NumberOfProcessors) 77 | { 78 | unsigned int ProcessorIndex; 79 | 80 | printf("Displaying CPUID Leafs 0, 1, 4, 0Bh, 018h, 01Fh if they exist\n"); 81 | 82 | for (ProcessorIndex = 0; ProcessorIndex < NumberOfProcessors; ProcessorIndex++) 83 | { 84 | Tools_SetAffinity(ProcessorIndex); 85 | printf("*******************************\n"); 86 | printf("Processor: %i\n", ProcessorIndex); 87 | Display_Internal_DisplaySubLeafs(0); 88 | Display_Internal_DisplaySubLeafs(1); 89 | Display_Internal_DisplaySubLeafs(4); 90 | Display_Internal_DisplaySubLeafs(0xB); 91 | Display_Internal_DisplaySubLeafs(0x18); 92 | Display_Internal_DisplaySubLeafs(0x1F); 93 | printf("\n"); 94 | } 95 | } 96 | 97 | /* 98 | * Display_Internal_DisplaySubLeafs 99 | * 100 | * This displays the raw CPUID leaf information and subleafs. 101 | * There is special handling to determine how to enumerate subleafs 102 | * on each of the leafs. This is only ment for a few leafs needed 103 | * for topology as called by Display_DisplayProcessorLeafs() 104 | * 105 | * Arguments: 106 | * Leaf Number 107 | * 108 | * Return: 109 | * None 110 | */ 111 | void Display_Internal_DisplaySubLeafs(unsigned int Leaf) 112 | { 113 | CPUID_REGISTERS OriginalCpuidRegisters; 114 | CPUID_REGISTERS CpuidRegisters; 115 | PCPUID_REGISTERS pCpuidReadValues; 116 | unsigned int CurrentSubleaf; 117 | BOOL_TYPE NextSubleaf; 118 | 119 | CurrentSubleaf = 0; 120 | pCpuidReadValues = &OriginalCpuidRegisters; 121 | 122 | Tools_ReadCpuid(0, 0, pCpuidReadValues); 123 | 124 | if (pCpuidReadValues->x.Register.Eax >= Leaf) 125 | { 126 | do { 127 | 128 | Tools_ReadCpuid(Leaf, CurrentSubleaf, pCpuidReadValues); 129 | 130 | printf("Leaf %08x Subleaf %u EAX: %08x EBX; %08x ECX: %08x EDX; %08x\n", Leaf, CurrentSubleaf, pCpuidReadValues->x.Register.Eax, pCpuidReadValues->x.Register.Ebx, pCpuidReadValues->x.Register.Ecx, pCpuidReadValues->x.Register.Edx); 131 | CurrentSubleaf++; 132 | 133 | switch (Leaf) 134 | { 135 | case 4: 136 | NextSubleaf = ((pCpuidReadValues->x.Register.Eax & 0x1F) != 0) ? BOOL_TRUE : BOOL_FALSE; 137 | break; 138 | 139 | case 0x18: 140 | NextSubleaf = (CurrentSubleaf <= OriginalCpuidRegisters.x.Register.Eax) ? BOOL_TRUE : BOOL_FALSE; 141 | break; 142 | 143 | case 0xB: 144 | case 0x1F: 145 | NextSubleaf = (pCpuidReadValues->x.Register.Ebx != 0) ? BOOL_TRUE : BOOL_FALSE; 146 | break; 147 | 148 | default: 149 | NextSubleaf = BOOL_FALSE; 150 | } 151 | 152 | pCpuidReadValues = &CpuidRegisters; 153 | 154 | } while(NextSubleaf); 155 | } 156 | 157 | } 158 | 159 | 160 | /* 161 | * Display_ApicIdBitLayout 162 | * 163 | * This is a general function that will display the APIC ID bit 164 | * layout and a message about it from the calling function. The bit layout 165 | * is encoded into the structure passed in from legacy topolgy, three level topology 166 | * or many levels of topology. 167 | * 168 | * Arguments: 169 | * APIC Bit Layout Context 170 | * 171 | * Return: 172 | * None 173 | */ 174 | void Display_ApicIdBitLayout(PAPICID_BIT_LAYOUT_CTX pApicidBitLayoutCtx) 175 | { 176 | unsigned int LowBit; 177 | unsigned int HighBit; 178 | unsigned int DomainIndex; 179 | unsigned int DomainTextIndex; 180 | const unsigned int UnknownTextIndex = 7; 181 | char *pszTopologyNames[] = { "Invalid", "Logical Processor", "Core", "Module", "Tile", "Die", "DieGrp", "Unknown"}; 182 | 183 | printf("%s\n", pApicidBitLayoutCtx->szDescription); 184 | 185 | LowBit = 0; 186 | 187 | for(DomainIndex = 0; DomainIndex < pApicidBitLayoutCtx->PackageDomainIndex; DomainIndex++) 188 | { 189 | /* 190 | * 191 | * The first index will be logical processor and the second will be Core. We need to handle the case 192 | * where zero is returned. 193 | * 194 | */ 195 | if(pApicidBitLayoutCtx->ShiftValues[DomainIndex] != 0) 196 | { 197 | HighBit = pApicidBitLayoutCtx->ShiftValues[DomainIndex] - 1; 198 | 199 | /* 200 | * We are short cutting this here since domains and indexes are equal, note that this may not be the case always depending on what future domains are being handled. 201 | */ 202 | DomainTextIndex = (Tools_IsDomainKnownEnumeration(pApicidBitLayoutCtx->ShiftValueDomain[DomainIndex]) == BOOL_TRUE) ? pApicidBitLayoutCtx->ShiftValueDomain[DomainIndex] : UnknownTextIndex; 203 | 204 | printf("%20s[%i:%i] (Domain Type Value: %i)\n", pszTopologyNames[DomainTextIndex], HighBit, LowBit, pApicidBitLayoutCtx->ShiftValueDomain[DomainIndex]); 205 | LowBit = pApicidBitLayoutCtx->ShiftValues[DomainIndex]; 206 | } 207 | } 208 | 209 | printf("%20s[%i:%i]\n\n", "Package", pApicidBitLayoutCtx->NumberOfApicIdBits-1, LowBit); 210 | } 211 | 212 | 213 | 214 | /* 215 | * Display_ThreeDomainDisplay 216 | * 217 | * This will specifically display a three domain 218 | * topology using a simple output format. This is for 219 | * easy display. 220 | * 221 | * Arguments: 222 | * Leaf, CoreShift, LogicalProcessorShift 223 | * 224 | * Return: 225 | * None 226 | */ 227 | void Display_ThreeDomainDisplay(unsigned int Leaf, unsigned int PackageShift, unsigned int LogicalProcessorShift) 228 | { 229 | unsigned int ProcessorIndex; 230 | unsigned int PackageMask; 231 | unsigned int LogicalProcessorPackageMask; 232 | unsigned int CorePackageMask; 233 | unsigned int LogicalProcessorMask; 234 | unsigned int ApicIdArray[MAX_PROCESSORS]; 235 | unsigned int NumberOfLogicalProcessors; 236 | 237 | printf("\n**************************\n"); 238 | 239 | if (Leaf == 1) 240 | { 241 | printf("Three Level Topology using CPUID.1/CPUID.4.\n"); 242 | printf("On modern platforms this may not be accurate since these are only 8 bit APIC IDs and they are subject to overflow.\n"); 243 | } 244 | else 245 | { 246 | printf("Topology from CPUID Leaf %Xh\n\n", Leaf); 247 | } 248 | 249 | LogicalProcessorPackageMask = (1<>PackageShift, 267 | (ApicIdArray[ProcessorIndex] & CorePackageMask)>>LogicalProcessorShift, 268 | ApicIdArray[ProcessorIndex] & LogicalProcessorMask); 269 | } 270 | 271 | } 272 | 273 | 274 | 275 | /* 276 | * Display_ManyDomainExample 277 | * 278 | * This API takes in the APIC ID bit layout context and will 279 | * display any level of domain layout. 280 | * 281 | * Arguments: 282 | * Leaf, APIC Bit Layout Context 283 | * 284 | * Return: 285 | * None 286 | */ 287 | void Display_ManyDomainExample(unsigned int Leaf, PAPICID_BIT_LAYOUT_CTX pApicidBitLayoutCtx) 288 | { 289 | char *pszTopologyNames[] = { "Invalid", "Logical Processor", "Core", "Module", "Tile", "Die", "DieGrp" }; /* Assume the input was filtered only to known domains. */ 290 | unsigned int DomainIndex; 291 | unsigned int TopDomainIndex; 292 | unsigned int ProcessorIndex; 293 | unsigned int DomainShift; 294 | unsigned int ApicIdArray[MAX_PROCESSORS]; 295 | unsigned int NumberOfLogicalProcessors; 296 | 297 | printf("***********************************\n"); 298 | printf("CPUID Leaf %i - Parse all known domains\n\n", Leaf); 299 | 300 | for (DomainIndex = 0; DomainIndex < pApicidBitLayoutCtx->PackageDomainIndex; DomainIndex++) 301 | { 302 | if (pApicidBitLayoutCtx->ShiftValues[DomainIndex] != 0) 303 | { 304 | printf(" %20s Mask: 0x%08x\n", pszTopologyNames[pApicidBitLayoutCtx->ShiftValueDomain[DomainIndex]], pApicidBitLayoutCtx->DomainRelativeMasks[DomainIndex][DomainIndex]); 305 | } 306 | } 307 | 308 | printf(" %20s Mask: 0x%08x\n\n", "Package", pApicidBitLayoutCtx->DomainRelativeMasks[DomainIndex][DomainIndex]); 309 | 310 | for (DomainIndex = 0; DomainIndex < pApicidBitLayoutCtx->PackageDomainIndex; DomainIndex++) 311 | { 312 | if (pApicidBitLayoutCtx->ShiftValues[DomainIndex] != 0) 313 | { 314 | for (TopDomainIndex = DomainIndex+1; TopDomainIndex < pApicidBitLayoutCtx->PackageDomainIndex; TopDomainIndex++) 315 | { 316 | printf(" %s Domain ID Mask Relative to %s Domain 0x%08x\n", pszTopologyNames[pApicidBitLayoutCtx->ShiftValueDomain[DomainIndex]], pszTopologyNames[pApicidBitLayoutCtx->ShiftValueDomain[TopDomainIndex]], pApicidBitLayoutCtx->DomainRelativeMasks[DomainIndex][TopDomainIndex]); 317 | } 318 | 319 | printf(" %s Domain ID Mask Relative to Package 0x%08x\n\n", pszTopologyNames[pApicidBitLayoutCtx->ShiftValueDomain[DomainIndex]], pApicidBitLayoutCtx->DomainRelativeMasks[DomainIndex][TopDomainIndex]); 320 | } 321 | } 322 | 323 | printf("\n Enumerating Processors\n"); 324 | 325 | /* 326 | * Optionally could dynamically allocate this based on the number of logical processors. 327 | */ 328 | NumberOfLogicalProcessors = Tools_GatherPlatformApicIds(ApicIdArray, MAX_PROCESSORS); 329 | 330 | for (ProcessorIndex = 0; ProcessorIndex < NumberOfLogicalProcessors; ProcessorIndex++) 331 | { 332 | printf("\n - Processor %i APIC ID(0x%x)\n", ProcessorIndex, ApicIdArray[ProcessorIndex]); 333 | printf(" + Package ID: 0x%08x\n", (pApicidBitLayoutCtx->DomainRelativeMasks[pApicidBitLayoutCtx->PackageDomainIndex][pApicidBitLayoutCtx->PackageDomainIndex] & ApicIdArray[ProcessorIndex])>>pApicidBitLayoutCtx->ShiftValues[pApicidBitLayoutCtx->PackageDomainIndex-1]); 334 | 335 | for (DomainIndex = 0, DomainShift = 0; DomainIndex < pApicidBitLayoutCtx->PackageDomainIndex; DomainIndex++) 336 | { 337 | if (pApicidBitLayoutCtx->ShiftValues[DomainIndex] != 0) 338 | { 339 | for (TopDomainIndex = DomainIndex+1; TopDomainIndex < pApicidBitLayoutCtx->PackageDomainIndex; TopDomainIndex++) 340 | { 341 | printf(" + %s Relative to %s ID: 0x%08x\n", pszTopologyNames[pApicidBitLayoutCtx->ShiftValueDomain[DomainIndex]], pszTopologyNames[pApicidBitLayoutCtx->ShiftValueDomain[TopDomainIndex]], (pApicidBitLayoutCtx->DomainRelativeMasks[DomainIndex][TopDomainIndex] & ApicIdArray[ProcessorIndex])>>DomainShift); 342 | } 343 | 344 | printf(" + %s Relative to Package ID: 0x%08x\n", pszTopologyNames[pApicidBitLayoutCtx->ShiftValueDomain[DomainIndex]], (pApicidBitLayoutCtx->DomainRelativeMasks[DomainIndex][TopDomainIndex] & ApicIdArray[ProcessorIndex])>>DomainShift); 345 | } 346 | DomainShift = pApicidBitLayoutCtx->ShiftValues[DomainIndex]; 347 | } 348 | 349 | } 350 | 351 | 352 | printf("***********************************\n"); 353 | } 354 | 355 | 356 | 357 | 358 | 359 | /* 360 | * Display_DisplayProcessorCaches 361 | * 362 | * Displays the enumerated Caches on the platform, the processors 363 | * associated with them and their details. 364 | * 365 | * Arguments: 366 | * List of Caches, number of Caches 367 | * 368 | * Return: 369 | * None 370 | */ 371 | void Display_DisplayProcessorCaches(PCPUID_CACHE_INFO pCacheInfo, unsigned int NumberOfCaches) 372 | { 373 | unsigned int CacheIndex; 374 | unsigned int CacheProcessorIndex; 375 | char *pszCacheType[] = { "Data Cache", "Instruction Cache", "Unified Cache" }; 376 | 377 | for (CacheIndex = 0; CacheIndex < NumberOfCaches; CacheIndex++) 378 | { 379 | printf("\n*************************************\n"); 380 | printf(" Cache Level: %i\n", pCacheInfo[CacheIndex].CacheLevel); 381 | printf(" Cache Type: %i ", pCacheInfo[CacheIndex].CacheType); 382 | 383 | switch (pCacheInfo[CacheIndex].CacheType) 384 | { 385 | case CacheType_DataCache: 386 | case CacheType_InstructionCache: 387 | case CacheType_UnifiedCache: 388 | printf("(%s)", pszCacheType[pCacheInfo[CacheIndex].CacheType - 1]); 389 | } 390 | 391 | printf("\n CacheId: %i\n", pCacheInfo[CacheIndex].CacheId); 392 | printf(" Cache Mask: 0x%08x\n\n", pCacheInfo[CacheIndex].CacheMask); 393 | 394 | printf(" Processors sharing this cache: %i", pCacheInfo[CacheIndex].NumberOfLPsSharingThisCache); 395 | 396 | for (CacheProcessorIndex = 0; CacheProcessorIndex < pCacheInfo[CacheIndex].NumberOfLPsSharingThisCache; CacheProcessorIndex++) 397 | { 398 | if ((CacheProcessorIndex % 6) == 0) 399 | { 400 | printf("\n "); 401 | } 402 | else 403 | { 404 | printf(", "); 405 | } 406 | 407 | printf("0x%03x", pCacheInfo[CacheIndex].ListOfApicIDsSharingThisCache[CacheProcessorIndex]); 408 | } 409 | 410 | printf("\n\n"); 411 | 412 | printf(" Number of Ways: %i\n Partitions: %i\n Cache Line Size: %i Bytes\n Number of Sets: %i\n Cache Size: %i Bytes, %1.2f Kb, %1.2f MB\n", pCacheInfo[CacheIndex].CacheWays, pCacheInfo[CacheIndex].CachePartitions, 413 | pCacheInfo[CacheIndex].CacheLineSize, pCacheInfo[CacheIndex].CacheSets, pCacheInfo[CacheIndex].CacheSizeInBytes, 414 | (float)pCacheInfo[CacheIndex].CacheSizeInBytes/1024.0, ((float)pCacheInfo[CacheIndex].CacheSizeInBytes/1024.0)/1024.0); 415 | 416 | printf("\n Cache Level is Self Initializing: %s\n", (pCacheInfo[CacheIndex].SelfInitializing == BOOL_FALSE) ? "FALSE" : "TRUE"); 417 | printf(" Cache is Fully Associative: %s\n", (pCacheInfo[CacheIndex].CacheIsFullyAssociative == BOOL_FALSE) ? "FALSE" : "TRUE"); 418 | printf(" Cache is Inclusive: %s\n", (pCacheInfo[CacheIndex].CacheIsInclusive == BOOL_FALSE) ? "FALSE" : "TRUE"); 419 | printf(" Cache is Direct Mapped: %s\n", (pCacheInfo[CacheIndex].CacheIsDirectMapped == BOOL_FALSE) ? "FALSE" : "TRUE"); 420 | printf(" Cache is Complex: %s\n\n", (pCacheInfo[CacheIndex].CacheIsComplex == BOOL_FALSE) ? "FALSE" : "TRUE"); 421 | 422 | printf(" WBINVD will flush lower levels sharing this cache: %s\n", (pCacheInfo[CacheIndex].WbinvdFlushsLowerLevelsSharing == BOOL_FALSE) ? "FALSE" : "TRUE"); 423 | 424 | printf("\n"); 425 | } 426 | } 427 | 428 | 429 | /* 430 | * Display_DisplayProcessorTlbs 431 | * 432 | * Displays the enumerated TLBs on the platform, the processors 433 | * associated with them and their details. 434 | * 435 | * 436 | * Arguments: 437 | * List of TLBs, Number of TLBs 438 | * 439 | * Return: 440 | * None 441 | */ 442 | void Display_DisplayProcessorTlbs(PCPUID_TLB_INFO pTlbInfo, unsigned int NumberOfTlbs) 443 | { 444 | unsigned int TlbIndex; 445 | unsigned int TlbProcessorIndex; 446 | char *pszTlbType[] = { "Data TLB", "Instruction TLB", "Unified TLB", "Load-Only TLB", "Store-Only TLB" }; 447 | 448 | for (TlbIndex = 0; TlbIndex < NumberOfTlbs; TlbIndex++) 449 | { 450 | printf("\n*************************************\n"); 451 | printf(" TLB Level: %i\n", pTlbInfo[TlbIndex].TlbLevel); 452 | printf(" TLB Type: %i ", pTlbInfo[TlbIndex].TlbType); 453 | 454 | switch (pTlbInfo[TlbIndex].TlbType) 455 | { 456 | case TlbType_Data: 457 | case TlbType_Instruction: 458 | case TlbType_Unified: 459 | case TlbType_LoadOnly: 460 | case TlbType_StoreOnly: 461 | printf("(%s)", pszTlbType[pTlbInfo[TlbIndex].TlbType - 1]); 462 | } 463 | 464 | printf("\n TlbId: %i\n", pTlbInfo[TlbIndex].TlbId); 465 | printf(" Tlb Mask: 0x%08x\n\n", pTlbInfo[TlbIndex].TlbMask); 466 | 467 | printf(" Processors sharing this TLB: %i", pTlbInfo[TlbIndex].NumberOfLPsSharingThisTlb); 468 | 469 | for (TlbProcessorIndex = 0; TlbProcessorIndex < pTlbInfo[TlbIndex].NumberOfLPsSharingThisTlb; TlbProcessorIndex++) 470 | { 471 | if ((TlbProcessorIndex % 6) == 0) 472 | { 473 | printf("\n "); 474 | } 475 | else 476 | { 477 | printf(", "); 478 | } 479 | 480 | printf("0x%03x", pTlbInfo[TlbIndex].ListOfApicIDsSharingThisTlb[TlbProcessorIndex]); 481 | } 482 | 483 | printf("\n\n"); 484 | 485 | printf(" Number of Ways: %i\n TLB Paritioning: %i\n Number of Sets: %i\n", pTlbInfo[TlbIndex].TlbWays, pTlbInfo[TlbIndex].TlbParitioning, pTlbInfo[TlbIndex].TlbSets); 486 | 487 | 488 | printf("\n 4K Page Size Entries Supported: %s\n", (pTlbInfo[TlbIndex]._4K_PageSizeEntries == BOOL_FALSE) ? "FALSE" : "TRUE"); 489 | printf(" 2MB Page Size Entries Supported: %s\n", (pTlbInfo[TlbIndex]._2MB_PageSizeEntries == BOOL_FALSE) ? "FALSE" : "TRUE"); 490 | printf(" 4MB Page Size Entries Supported: %s\n", (pTlbInfo[TlbIndex]._4MB_PageSizeEntries == BOOL_FALSE) ? "FALSE" : "TRUE"); 491 | printf(" 1GB Page Size Entries Supported: %s\n", (pTlbInfo[TlbIndex]._1GB_PageSizeEntries == BOOL_FALSE) ? "FALSE" : "TRUE"); 492 | 493 | 494 | printf("\n TLB is Fully Associative: %s\n", (pTlbInfo[TlbIndex].FullyAssociative == BOOL_FALSE) ? "FALSE" : "TRUE"); 495 | 496 | printf("\n"); 497 | } 498 | } 499 | 500 | 501 | 502 | -------------------------------------------------------------------------------- /cpuid_topology_file.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 by Intel Corporation 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 12 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include "cpuid_topology.h" 20 | 21 | 22 | /* 23 | * Global application data variable 24 | */ 25 | extern GLOBAL_DATA g_GlobalData; 26 | 27 | 28 | 29 | /* 30 | * Context structures to maintain state during parsing of the 31 | * CPUID files for Read and write. 32 | */ 33 | typedef struct _FILE_READ_CONTEXT 34 | { 35 | FILE *CpuidFile; 36 | 37 | /* 38 | * Maintains the current leaf state machine for reading subsequent subleafs. 39 | */ 40 | unsigned int CurrentLeaf; 41 | 42 | /* 43 | * Maintains the processor relation index of the current CPUID.4 reads 44 | */ 45 | unsigned int Leaf4Index; 46 | 47 | /* 48 | * Maintains the processor relation index of the current CPUID.18 reads 49 | */ 50 | unsigned int Leaf18Index; 51 | 52 | } FILE_READ_CONTEXT, *PFILE_READ_CONTEXT; 53 | 54 | typedef struct _FILE_WRITE_CONTEXT 55 | { 56 | FILE *CpuidFile; 57 | unsigned int NumberOfProcessors; 58 | 59 | } FILE_WRITE_CONTEXT, *PFILE_WRITE_CONTEXT; 60 | 61 | /* 62 | * Internal Prototypes 63 | */ 64 | BOOL_TYPE File_Internal_DispatchReadLeaf(PFILE_READ_CONTEXT pFileContext, unsigned int LeafNumber); 65 | BOOL_TYPE File_Internal_DispatchReadSubleaf(PFILE_READ_CONTEXT pFileContext, unsigned int SubleafNumber); 66 | BOOL_TYPE File_Internal_DispatchReadApicId(PFILE_READ_CONTEXT pFileContext, unsigned int ApicIdNumber); 67 | BOOL_TYPE File_Internal_WriteLeafToFile(PFILE_WRITE_CONTEXT pFileContext, unsigned int LeafNumber); 68 | BOOL_TYPE File_Internal_WriteApicIdsToFile(PFILE_WRITE_CONTEXT pFileContext); 69 | 70 | 71 | /* 72 | * File_ReadCpuidFromFile 73 | * 74 | * This function will read a file that contains 75 | * CPUID data. This will populate the global 76 | * with the fake CPUID data to be used with the CPUID 77 | * algorithms. 78 | * 79 | * 80 | * Arguments: 81 | * File Name 82 | * 83 | * Return: 84 | * Returns true if successful 85 | */ 86 | BOOL_TYPE File_ReadCpuidFromFile(char *pszFileName) 87 | { 88 | FILE_READ_CONTEXT FileContext; 89 | char Character; 90 | unsigned int FirstData; 91 | BOOL_TYPE FileReadStatus; 92 | 93 | FileReadStatus = BOOL_FALSE; 94 | 95 | memset(&FileContext, 0, sizeof(FILE_READ_CONTEXT)); 96 | 97 | /* 98 | * This is a simple file format for reading in data from a file and creating 99 | * a CPUID thunk from that data. 100 | * 101 | * 102 | * The File Format Legend 103 | * 104 | * The Leaf encoding specifies a line starting with "L" and then a space with a number for that Leaf. 105 | * This is then followed by a new line. The leaf is in decimal value. 106 | * 107 | * L [Leaf Number] 108 | * 109 | * After a leaf encoding, subsequent values following it must be a series of lines starting with "S" to identify 110 | * this is a subleaf. This is then followed by the subleaf number and the register values in decimal. These 111 | * subleafs are then associated with the specified preceeding L directive line. 112 | * 113 | * S [Subleaf Number] [EAX] [EBX] [ECX] [EDX] 114 | * 115 | * This simulation is very simple and only expects one entry for each Leaf except for Leaf 4 and Leaf 18H. 116 | * Each subsequent description of a new Leaf 4 or Leaf 18H will for that leaf associate it with an incremental 117 | * processor number thus creating an association between the list of APIC IDs and that leaf as tied to a specific 118 | * processor. 119 | * 120 | * The APIC IDs for the logical processors are represented by a line that starts with "A" followed by a space and 121 | * then the APIC ID value in decimal. Each subsequent APIC ID will be associated with the next numerical logical processor. 122 | * 123 | * A [APIC ID Value] 124 | * 125 | * These values must be capital. 126 | * 127 | * Leaves which do not enumerate multiple subleafs simply enumerate a single subleaf of 0. 128 | * 129 | * For example: 130 | * L 1 131 | * S 0 1 2 3 4 132 | * A 10 133 | * 134 | * 135 | * This code is simple and does not do any extenisve level of error checking. 136 | * 137 | */ 138 | 139 | FileContext.CpuidFile = fopen(pszFileName, "r"); 140 | 141 | /* 142 | * Always switch to Virtual CPUID; if the file does not contain CPUID information 143 | * then it is invalid anyway. 144 | */ 145 | g_GlobalData.UseNativeCpuid = BOOL_FALSE; 146 | 147 | 148 | if(FileContext.CpuidFile) 149 | { 150 | 151 | while (!feof(FileContext.CpuidFile)) 152 | { 153 | if (fscanf(FileContext.CpuidFile, "%c %i\n", &Character, &FirstData) != 0) 154 | { 155 | switch (Character) 156 | { 157 | case 'L': 158 | FileReadStatus = File_Internal_DispatchReadLeaf(&FileContext, FirstData); 159 | break; 160 | 161 | case 'S': 162 | FileReadStatus = File_Internal_DispatchReadSubleaf(&FileContext, FirstData); 163 | break; 164 | 165 | case 'A': 166 | FileReadStatus = File_Internal_DispatchReadApicId(&FileContext, FirstData); 167 | break; 168 | 169 | default: 170 | FileReadStatus = BOOL_FALSE; 171 | } 172 | 173 | } 174 | } 175 | 176 | fclose(FileContext.CpuidFile); 177 | FileContext.CpuidFile = NULL; 178 | } 179 | 180 | return FileReadStatus; 181 | } 182 | 183 | 184 | /* 185 | * File_Internal_DispatchReadLeaf 186 | * 187 | * This function will update the current leaf number. There is no error checking 188 | * for valid leaf or reusing an already processed leaf number. 189 | * 190 | * Arguments: 191 | * File Context, Leaf Number 192 | * 193 | * Return: 194 | * Returns true if successful 195 | */ 196 | BOOL_TYPE File_Internal_DispatchReadLeaf(PFILE_READ_CONTEXT pFileContext, unsigned int LeafNumber) 197 | { 198 | pFileContext->CurrentLeaf = LeafNumber; 199 | 200 | switch (LeafNumber) 201 | { 202 | case 4: 203 | pFileContext->Leaf4Index++; 204 | break; 205 | 206 | case 0x18: 207 | pFileContext->Leaf18Index++; 208 | break; 209 | } 210 | 211 | return BOOL_TRUE; 212 | } 213 | 214 | 215 | /* 216 | * File_Internal_DispatchReadSubleaf 217 | * 218 | * This function will update the subleaf for the current leaf number. 219 | * 220 | * There is no error checking valid subleaf or rewriting the same subleaf. 221 | * 222 | * Arguments: 223 | * File Context, Subleaf Number 224 | * 225 | * Return: 226 | * Returns true if successful 227 | */ 228 | BOOL_TYPE File_Internal_DispatchReadSubleaf(PFILE_READ_CONTEXT pFileContext, unsigned int SubleafNumber) 229 | { 230 | BOOL_TYPE SubleafSuccess; 231 | unsigned int Eax; 232 | unsigned int Ebx; 233 | unsigned int Ecx; 234 | unsigned int Edx; 235 | 236 | SubleafSuccess = BOOL_FALSE; 237 | 238 | if (fscanf(pFileContext->CpuidFile, "%u %u %u %u", &Eax, &Ebx, &Ecx, &Edx) != 0) 239 | { 240 | SubleafSuccess = BOOL_TRUE; 241 | 242 | switch (pFileContext->CurrentLeaf) 243 | { 244 | case 4: 245 | g_GlobalData.SimulatedCpuidLeaf4[pFileContext->Leaf4Index-1][SubleafNumber][0] = Eax; 246 | g_GlobalData.SimulatedCpuidLeaf4[pFileContext->Leaf4Index-1][SubleafNumber][1] = Ebx; 247 | g_GlobalData.SimulatedCpuidLeaf4[pFileContext->Leaf4Index-1][SubleafNumber][2] = Ecx; 248 | g_GlobalData.SimulatedCpuidLeaf4[pFileContext->Leaf4Index-1][SubleafNumber][3] = Edx; 249 | printf("Proc %i Leaf %08x Subleaf %u EAX: %08x EBX; %08x ECX: %08x EDX; %08x\n", pFileContext->Leaf4Index-1, pFileContext->CurrentLeaf, SubleafNumber, Eax, Ebx, Ecx, Edx); 250 | break; 251 | 252 | case 0x18: 253 | g_GlobalData.SimulatedCpuidLeaf18[pFileContext->Leaf18Index-1][SubleafNumber][0] = Eax; 254 | g_GlobalData.SimulatedCpuidLeaf18[pFileContext->Leaf18Index-1][SubleafNumber][1] = Ebx; 255 | g_GlobalData.SimulatedCpuidLeaf18[pFileContext->Leaf18Index-1][SubleafNumber][2] = Ecx; 256 | g_GlobalData.SimulatedCpuidLeaf18[pFileContext->Leaf18Index-1][SubleafNumber][3] = Edx; 257 | printf("Proc %i Leaf %08x Subleaf %u EAX: %08x EBX; %08x ECX: %08x EDX; %08x\n", pFileContext->Leaf18Index-1, pFileContext->CurrentLeaf, SubleafNumber, Eax, Ebx, Ecx, Edx); 258 | break; 259 | 260 | default: 261 | if (pFileContext->CurrentLeaf < MAX_SIMULATED_LEAFS && SubleafNumber < MAX_SIMULATED_SUBLEAFS) 262 | { 263 | g_GlobalData.SimulatedCpuid[pFileContext->CurrentLeaf][SubleafNumber][0] = Eax; 264 | g_GlobalData.SimulatedCpuid[pFileContext->CurrentLeaf][SubleafNumber][1] = Ebx; 265 | g_GlobalData.SimulatedCpuid[pFileContext->CurrentLeaf][SubleafNumber][2] = Ecx; 266 | g_GlobalData.SimulatedCpuid[pFileContext->CurrentLeaf][SubleafNumber][3] = Edx; 267 | printf("Leaf %08x Subleaf %u EAX: %08x EBX; %08x ECX: %08x EDX; %08x\n", pFileContext->CurrentLeaf, SubleafNumber, Eax, Ebx, Ecx, Edx); 268 | } 269 | else 270 | { 271 | printf("Skipping entry beyond supported maximum leaf/subleafs 0x%x, %i\n", pFileContext->CurrentLeaf, SubleafNumber); 272 | } 273 | } 274 | } 275 | 276 | return SubleafSuccess; 277 | } 278 | 279 | 280 | 281 | /* 282 | * File_Internal_DispatchReadApicId 283 | * 284 | * This function will update the APIC ID of the next processor 285 | * 286 | * There is no error checking, same APIC ID could be submitted twice. 287 | * 288 | * Arguments: 289 | * File Context, APIC ID Number 290 | * 291 | * Return: 292 | * Returns true if successful 293 | */ 294 | BOOL_TYPE File_Internal_DispatchReadApicId(PFILE_READ_CONTEXT pFileContext, unsigned int ApicIdNumber) 295 | { 296 | if (g_GlobalData.NumberOfSimulatedProcessors < MAX_PROCESSORS) 297 | { 298 | g_GlobalData.SimulatedApicIds[g_GlobalData.NumberOfSimulatedProcessors] = ApicIdNumber; 299 | printf("Processor %i - ApicID - %08x\n", g_GlobalData.NumberOfSimulatedProcessors, ApicIdNumber); 300 | g_GlobalData.NumberOfSimulatedProcessors++; 301 | } 302 | else 303 | { 304 | printf("Too many processors in file, skipping APICID %0x\n", ApicIdNumber); 305 | } 306 | 307 | return BOOL_TRUE; 308 | } 309 | 310 | 311 | 312 | 313 | /* 314 | * File_WriteCpuidToFile 315 | * 316 | * Write the CPUID values to a file. 317 | * 318 | * Arguments: 319 | * File Name 320 | * 321 | * Return: 322 | * Returns true if successful 323 | */ 324 | BOOL_TYPE File_WriteCpuidToFile(char *pszFileName) 325 | { 326 | FILE_WRITE_CONTEXT FileWriteContext; 327 | CPUID_REGISTERS CpuidRegisters; 328 | unsigned int MaximumLeaf; 329 | unsigned int Index; 330 | unsigned int ApicId; 331 | BOOL_TYPE FileWritten; 332 | 333 | FileWritten = BOOL_FALSE; 334 | 335 | memset(&FileWriteContext, 0, sizeof(FILE_WRITE_CONTEXT)); 336 | 337 | /* 338 | * This is a simple file format for reading in data from a file and creating 339 | * a CPUID thunk from that data. 340 | * 341 | * 342 | * The File Format Legend 343 | * 344 | * The Leaf encoding specifies a line starting with "L" and then a space with a number for that Leaf. 345 | * This is then followed by a new line. The leaf is in decimal value. 346 | * 347 | * L [Leaf Number] 348 | * 349 | * After a leaf encoding, subsequent values following it must be a series of lines starting with "S" to identify 350 | * this is a subleaf. This is then followed by the subleaf number and the register values in decimal. These 351 | * subleafs are then associated with the specified preceeding L directive line. 352 | * 353 | * S [Subleaf Number] [EAX] [EBX] [ECX] [EDX] 354 | * 355 | * This simulation is very simple and only expects one entry for each Leaf except for Leaf 4 and Leaf 18H. 356 | * Each subsequent description of a new Leaf 4 or Leaf 18H will for that leaf associate it with an incremental 357 | * processor number thus creating an association between the list of APIC IDs and that leaf as tied to a specific 358 | * processor. 359 | * 360 | * The APIC IDs for the logical processors are represented by a line that starts with "A" followed by a space and 361 | * then the APIC ID value in decimal. Each subsequent APIC ID will be associated with the next numerical logical processor. 362 | * 363 | * A [APIC ID Value] 364 | * 365 | * These values must be capital. 366 | * 367 | * Leaves which do not enumerate multiple subleafs simply enumerate a single subleaf of 0. 368 | * 369 | * For example: 370 | * L 1 371 | * S 0 1 2 3 4 372 | * A 10 373 | * 374 | * 375 | * This code is simple and does not do any extenisve level of error checking. 376 | * 377 | */ 378 | FileWriteContext.CpuidFile = fopen(pszFileName, "w"); 379 | 380 | if(FileWriteContext.CpuidFile) 381 | { 382 | FileWritten = BOOL_TRUE; 383 | 384 | Tools_ReadCpuid(0, 0, &CpuidRegisters); 385 | MaximumLeaf = CpuidRegisters.x.Register.Eax; 386 | FileWriteContext.NumberOfProcessors = Tools_GetNumberOfProcessors(); 387 | 388 | 389 | File_Internal_WriteLeafToFile(&FileWriteContext, 0); 390 | 391 | if (MaximumLeaf >= 1) 392 | { 393 | File_Internal_WriteLeafToFile(&FileWriteContext, 1); 394 | } 395 | 396 | if (MaximumLeaf >= 0x4) 397 | { 398 | for (Index = 0; Index < FileWriteContext.NumberOfProcessors; Index++) 399 | { 400 | printf("* Processor %i\n", Index); 401 | Tools_SetAffinity(Index); 402 | File_Internal_WriteLeafToFile(&FileWriteContext, 0x4); 403 | } 404 | } 405 | 406 | if (MaximumLeaf >= 0xB) 407 | { 408 | File_Internal_WriteLeafToFile(&FileWriteContext, 0xB); 409 | } 410 | 411 | if (MaximumLeaf >= 0x18) 412 | { 413 | for (Index = 0; Index < FileWriteContext.NumberOfProcessors; Index++) 414 | { 415 | printf("* Processor %i\n", Index); 416 | Tools_SetAffinity(Index); 417 | File_Internal_WriteLeafToFile(&FileWriteContext, 0x18); 418 | } 419 | } 420 | 421 | if (MaximumLeaf >= 0x1F) 422 | { 423 | File_Internal_WriteLeafToFile(&FileWriteContext, 0x1F); 424 | } 425 | 426 | 427 | for (Index = 0; Index < FileWriteContext.NumberOfProcessors; Index++) 428 | { 429 | Tools_SetAffinity(Index); 430 | 431 | if (CpuidRegisters.x.Register.Eax >= 0xB) 432 | { 433 | Tools_ReadCpuid(0xB, 0, &CpuidRegisters); 434 | ApicId = CpuidRegisters.x.Register.Edx; 435 | } 436 | else 437 | { 438 | Tools_ReadCpuid(1, 0, &CpuidRegisters); 439 | ApicId = (CpuidRegisters.x.Register.Ebx >> 24); 440 | } 441 | fprintf(FileWriteContext.CpuidFile, "A %i\n", ApicId); 442 | printf("A %i\n", ApicId); 443 | } 444 | 445 | fclose(FileWriteContext.CpuidFile); 446 | } 447 | 448 | return FileWritten; 449 | } 450 | 451 | 452 | 453 | /* 454 | * File_Internal_WriteLeafToFile 455 | * 456 | * Write a leaf and subleafs into a file. 457 | * 458 | * Arguments: 459 | * File Context, Leaf Number 460 | * 461 | * Return: 462 | * Return true on success 463 | */ 464 | BOOL_TYPE File_Internal_WriteLeafToFile(PFILE_WRITE_CONTEXT pFileContext, unsigned int LeafNumber) 465 | { 466 | CPUID_REGISTERS OriginalCpuidRegisters; 467 | CPUID_REGISTERS CpuidRegisters; 468 | PCPUID_REGISTERS pCpuidReadValues; 469 | unsigned int CurrentSubleaf; 470 | BOOL_TYPE LeafWritten; 471 | BOOL_TYPE NextSubleaf; 472 | 473 | fprintf(pFileContext->CpuidFile, "L %i\n", LeafNumber); 474 | printf("L %i\n", LeafNumber); 475 | 476 | CurrentSubleaf = 0; 477 | 478 | LeafWritten = BOOL_TRUE; 479 | 480 | pCpuidReadValues = &OriginalCpuidRegisters; 481 | 482 | do { 483 | 484 | Tools_ReadCpuid(LeafNumber, CurrentSubleaf, pCpuidReadValues); 485 | 486 | fprintf(pFileContext->CpuidFile, "S %u %u %u %u %u\n", CurrentSubleaf, pCpuidReadValues->x.Register.Eax, pCpuidReadValues->x.Register.Ebx, pCpuidReadValues->x.Register.Ecx, pCpuidReadValues->x.Register.Edx); 487 | printf("S %u %u %u %u %u\n", CurrentSubleaf, pCpuidReadValues->x.Register.Eax, pCpuidReadValues->x.Register.Ebx, pCpuidReadValues->x.Register.Ecx, pCpuidReadValues->x.Register.Edx); 488 | CurrentSubleaf++; 489 | 490 | switch (LeafNumber) 491 | { 492 | case 4: 493 | NextSubleaf = ((pCpuidReadValues->x.Register.Eax & 0x1F) != 0) ? BOOL_TRUE : BOOL_FALSE; 494 | break; 495 | 496 | case 0x18: 497 | NextSubleaf = (CurrentSubleaf <= OriginalCpuidRegisters.x.Register.Eax) ? BOOL_TRUE : BOOL_FALSE; 498 | break; 499 | 500 | case 0xB: 501 | case 0x1F: 502 | NextSubleaf = (pCpuidReadValues->x.Register.Ebx != 0) ? BOOL_TRUE : BOOL_FALSE; 503 | break; 504 | 505 | default: 506 | NextSubleaf = BOOL_FALSE; 507 | } 508 | 509 | pCpuidReadValues = &CpuidRegisters; 510 | 511 | } while(NextSubleaf); 512 | 513 | return LeafWritten; 514 | } 515 | 516 | 517 | 518 | 519 | -------------------------------------------------------------------------------- /cpuid_topology_parsecachetlb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 by Intel Corporation 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 12 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | 17 | 18 | #include 19 | #include 20 | #include 21 | #include "cpuid_topology.h" 22 | 23 | 24 | /* 25 | * Global application data variable 26 | */ 27 | extern GLOBAL_DATA g_GlobalData; 28 | 29 | 30 | 31 | 32 | 33 | /* 34 | * Internal Prototype APIs 35 | */ 36 | unsigned int ParseCache_Internal_FindMatchingCacheEntry(PCPUID_CACHE_INFO pCacheInfo, unsigned int NumberOfCaches, unsigned int CacheId, PCPUID_REGISTERS pCpuidRegisters); 37 | unsigned int ParseCache_Internal_AddCacheEntry(PCPUID_CACHE_INFO pCacheInfo, unsigned int *pNumberOfCaches, unsigned int CacheId, unsigned int CacheMask, PCPUID_REGISTERS pCpuidRegisters); 38 | unsigned int ParseTlb_Internal_FindMatchingTlbEntry(PCPUID_TLB_INFO pTlbInfo, unsigned int NumberOfTlbs, unsigned int TlbId, PCPUID_REGISTERS pCpuidRegisters); 39 | unsigned int ParseTlb_Internal_AddTlbEntry(PCPUID_TLB_INFO pTlbInfo, unsigned int *pNumberOfTlbs, unsigned int TlbId, unsigned int TlbMask, PCPUID_REGISTERS pCpuidRegisters); 40 | 41 | /* 42 | * ParseCache_CpuidCacheExample 43 | * 44 | * This is an example of how to parse the CPUID Caching Information 45 | * via CPUID Leaf 4. 46 | * 47 | * Arguments: 48 | * None 49 | * 50 | * Return: 51 | * None 52 | */ 53 | void ParseCache_CpuidCacheExample(void) 54 | { 55 | PCPUID_CACHE_INFO pCacheInfo; 56 | unsigned int CacheIndex; 57 | unsigned int NumberOfCaches; 58 | unsigned int SubleafIndex; 59 | CPUID_REGISTERS CpuidRegisters; 60 | unsigned int NumberOfProcessors; 61 | unsigned int ApicIdList[MAX_PROCESSORS]; 62 | unsigned int ProcessorIndex; 63 | unsigned int MaxAddressibleIdSharingCache; 64 | unsigned int CacheShift; 65 | unsigned int CacheMask; 66 | unsigned int CacheId; 67 | CACHE_TYPE CacheType; 68 | 69 | /* 70 | * Note that the SDM reccomends to look at CPUID.4 if CPUID.2 contains 0FFh. 71 | * 72 | * However, if there is no intention of decoding CPUID Leaf 2 descriptors you can 73 | * just check if CPUID.4 exists and has information. Only processors that are likely 74 | * older than ~2005-2006 are likely to not have this Leaf. 75 | * 76 | */ 77 | 78 | Tools_ReadCpuid(0, 0, &CpuidRegisters); 79 | 80 | if (CpuidRegisters.x.Register.Eax >= 4) 81 | { 82 | /* 83 | * This is an example and the simplest thing is dynamically hardcode to 10 caches per logical processor 84 | * for simplicity. 85 | */ 86 | 87 | NumberOfProcessors = Tools_GatherPlatformApicIds(ApicIdList, MAX_PROCESSORS); 88 | NumberOfCaches = 0; 89 | 90 | pCacheInfo = (PCPUID_CACHE_INFO)calloc(NumberOfProcessors*MAX_CACHE_PER_LP, sizeof(CPUID_CACHE_INFO)); 91 | 92 | if (pCacheInfo) 93 | { 94 | 95 | /* 96 | * This will enumerate through every logical processor and check each one's cache. It will then 97 | * determine if that logical processor is expressing a new cache or an existing cache. 98 | * 99 | * This can be done by checking the entire CPUID Subleaf contents and using the generated Cache ID. 100 | * 101 | * Here we cannot assume anything about a subleaf number relationship between the enumerated cache and the 102 | * other logical processors on the system which may be sharing it. 103 | * 104 | * This is going to be done the very long way. There is an assumption that an enumerated cache and logical 105 | * processors that are included in that ID should all have the same cache information reported. However, as 106 | * in the manual, we can verify by ensuring the entire subleaf is identical along with the generated Cache ID. 107 | * 108 | * 109 | */ 110 | 111 | for (ProcessorIndex = 0; ProcessorIndex < NumberOfProcessors; ProcessorIndex++) 112 | { 113 | Tools_SetAffinity(ProcessorIndex); 114 | 115 | SubleafIndex = 0; 116 | Tools_ReadCpuid(4, SubleafIndex, &CpuidRegisters); 117 | CacheType = (CpuidRegisters.x.Register.Eax & 0x1F); 118 | 119 | /* 120 | * Enumerate subleafs until no more caches Cache Type is retuned. 121 | */ 122 | while (CacheType != CacheType_NoMoreCaches) 123 | { 124 | /* 125 | * Compute the Cache ID for this cache; this is called 126 | * 127 | * Maximum Addressible IDs sharing this cache since not all APIC IDs 128 | * may be assigned to a logical processor. 129 | * 130 | */ 131 | MaxAddressibleIdSharingCache = (((CpuidRegisters.x.Register.Eax)>>14) & 0xFFF)+1; 132 | 133 | 134 | /* 135 | * We round to a power of 2 and create a CacheMask that can be used with 136 | * APIC IDs to generate a Cache ID. 137 | * 138 | */ 139 | CacheShift = Tools_CreateTopologyShift(MaxAddressibleIdSharingCache); 140 | CacheMask = ~((1<x.Register.Eax & 0x1F);; /* EAX[4:0] */ 299 | pCacheInfo[NewCacheIndex].CacheLevel = ((pCpuidRegisters->x.Register.Eax>>5) & 0x7); /* EAX[7:5] Cache Level Starts at 1 */ 300 | pCacheInfo[NewCacheIndex].CacheId = CacheId; 301 | pCacheInfo[NewCacheIndex].CacheMask = CacheMask; 302 | 303 | /* 304 | * Populate the cache details and calculate the size of the cache 305 | */ 306 | pCacheInfo[NewCacheIndex].CacheWays = (pCpuidRegisters->x.Register.Ebx>>22) + 1; /* EBX[31:22] + 1 */ 307 | pCacheInfo[NewCacheIndex].CachePartitions = ((pCpuidRegisters->x.Register.Ebx>>12) & 0x3FF) + 1; /* EBX[21:12] + 1 */ 308 | pCacheInfo[NewCacheIndex].CacheLineSize = (pCpuidRegisters->x.Register.Ebx & 0xFFF) + 1; /* EBX[11:0] + 1 */ 309 | pCacheInfo[NewCacheIndex].CacheSets = pCpuidRegisters->x.Register.Ecx + 1; /* ECX + 1 */ 310 | 311 | /* 312 | * The number of Cache Ways is multiplied by the number of Sets. 313 | * 314 | * This is then expanded by the number of partitions and then the cache size to get the complete number of bytes 315 | * for ths size of this cache. 316 | * 317 | */ 318 | pCacheInfo[NewCacheIndex].CacheSizeInBytes = pCacheInfo[NewCacheIndex].CacheWays * pCacheInfo[NewCacheIndex].CacheLineSize * 319 | pCacheInfo[NewCacheIndex].CachePartitions * pCacheInfo[NewCacheIndex].CacheSets; 320 | 321 | /* 322 | * Populate the extra attributes of the cache. 323 | */ 324 | pCacheInfo[NewCacheIndex].SelfInitializing = (pCpuidRegisters->x.Register.Eax & (1<<8)) ? BOOL_TRUE : BOOL_FALSE; 325 | pCacheInfo[NewCacheIndex].CacheIsFullyAssociative = (pCpuidRegisters->x.Register.Eax & (1<<9)) ? BOOL_TRUE : BOOL_FALSE; 326 | pCacheInfo[NewCacheIndex].WbinvdFlushsLowerLevelsSharing = (pCpuidRegisters->x.Register.Edx & 1) ? BOOL_FALSE : BOOL_TRUE; 327 | pCacheInfo[NewCacheIndex].CacheIsInclusive = (pCpuidRegisters->x.Register.Edx & 2) ? BOOL_TRUE : BOOL_FALSE; 328 | pCacheInfo[NewCacheIndex].CacheIsComplex = (pCpuidRegisters->x.Register.Edx & 4) ? BOOL_TRUE : BOOL_FALSE; 329 | pCacheInfo[NewCacheIndex].CacheIsDirectMapped = pCacheInfo[NewCacheIndex].CacheIsComplex ? BOOL_FALSE : BOOL_TRUE; 330 | 331 | pCacheInfo[NewCacheIndex].NumberOfLPsSharingThisCache = 0; 332 | memcpy(&pCacheInfo[NewCacheIndex].CachedCpuidRegisters, pCpuidRegisters, sizeof(CPUID_REGISTERS)); 333 | 334 | return NewCacheIndex; 335 | } 336 | 337 | 338 | 339 | 340 | 341 | /* 342 | * ParseTlb_CpuidTlbExample 343 | * 344 | * This is an example of how to parse the CPUID TLB Information 345 | * via CPUID Leaf 18H 346 | * 347 | * Arguments: 348 | * None 349 | * 350 | * Return: 351 | * None 352 | */ 353 | void ParseTlb_CpuidTlbExample(void) 354 | { 355 | PCPUID_TLB_INFO pTlbInfo; 356 | unsigned int TlbIndex; 357 | unsigned int NumberOfTlbs; 358 | unsigned int SubleafIndex; 359 | unsigned int MaxSubleaf; 360 | CPUID_REGISTERS CpuidRegisters; 361 | unsigned int NumberOfProcessors; 362 | unsigned int ApicIdList[MAX_PROCESSORS]; 363 | unsigned int ProcessorIndex; 364 | unsigned int MaxAddressibleIdSharingTlb; 365 | unsigned int TlbShift; 366 | unsigned int TlbMask; 367 | unsigned int TlbId; 368 | TLB_TYPE TlbType; 369 | 370 | Tools_ReadCpuid(0, 0, &CpuidRegisters); 371 | 372 | /* 373 | * Note that the SDM reccomends to look at CPUID.18 if CPUID.2 contains 0FEh. 374 | * 375 | * However, if there is no intention of decoding CPUID Leaf 2 descriptors you can 376 | * just check if CPUID.18 exists and has information. Over time, all processors will 377 | * transition to returning Leaf 18H. 378 | * 379 | */ 380 | 381 | if (CpuidRegisters.x.Register.Eax >= 0x18) 382 | { 383 | /* 384 | * This is an example and the simplest thing is dynamically hardcode to 10 TLBs per logical processor 385 | * for simplicity. 386 | */ 387 | 388 | NumberOfProcessors = Tools_GatherPlatformApicIds(ApicIdList, MAX_PROCESSORS); 389 | NumberOfTlbs = 0; 390 | 391 | pTlbInfo = (PCPUID_TLB_INFO)calloc(NumberOfProcessors*MAX_TLB_PER_LP, sizeof(CPUID_TLB_INFO)); 392 | 393 | if (pTlbInfo) 394 | { 395 | 396 | /* 397 | * This will enumerate through every logical processor and check each one's TLB. It will then 398 | * determine if that logical processor is expressing a new TLB or an existing TLB. 399 | * 400 | * This can be done by checking the entire CPUID Subleaf contents and using the generated TLB ID. 401 | * 402 | * Here we cannot assume anything about a subleaf number relationship between the enumerated TLB and the 403 | * other logical processors on the system which may be sharing it. 404 | * 405 | * This is going to be done the very long way. There is an assumption that an enumerated TLB and logical 406 | * processors that are included in that ID should all have the same TLB information reported. However, as 407 | * in the manual, we can verify by ensuring the entire subleaf is identical along with the generated TLB ID. 408 | * 409 | * 410 | */ 411 | 412 | for (ProcessorIndex = 0; ProcessorIndex < NumberOfProcessors; ProcessorIndex++) 413 | { 414 | Tools_SetAffinity(ProcessorIndex); 415 | 416 | MaxSubleaf = 0; 417 | 418 | for (SubleafIndex = 0; SubleafIndex <= MaxSubleaf; SubleafIndex++) 419 | { 420 | 421 | Tools_ReadCpuid(0x18, SubleafIndex, &CpuidRegisters); 422 | 423 | if (SubleafIndex == 0) 424 | { 425 | /* 426 | * Subleaf 0 contains data in EBX, ECX, EDX and EAX is the maximum subleaf. 427 | * Subsequent subleafs do not use EAX. 428 | */ 429 | MaxSubleaf = (CpuidRegisters.x.Register.Eax); 430 | } 431 | 432 | /* 433 | * Clear EAX for all subleafs for direct compare to work easier. 434 | */ 435 | CpuidRegisters.x.Register.Eax = 0; 436 | 437 | TlbType = (CpuidRegisters.x.Register.Edx & 0x1F); /* EDX[4:0] */ 438 | 439 | if (TlbType != TlbType_InvalidSubleaf) 440 | { 441 | 442 | /* 443 | * Compute the Tlb ID for this tlb; this is called 444 | * 445 | * Maximum Addressible IDs sharing this translation cache since not all APIC IDs 446 | * may be assigned to a logical processor. 447 | * 448 | */ 449 | MaxAddressibleIdSharingTlb = (((CpuidRegisters.x.Register.Edx)>>14) & 0xFFF)+1; 450 | 451 | 452 | /* 453 | * We round to a power of 2 and create a TlbMask that can be used with 454 | * APIC IDs to generate a Tlb ID. 455 | * 456 | */ 457 | TlbShift = Tools_CreateTopologyShift(MaxAddressibleIdSharingTlb); 458 | TlbMask = ~((1<x.Register.Edx & 0x1F); /* EDX[4:0] */ 598 | pTlbInfo[NewTlbIndex].TlbLevel = ((pCpuidRegisters->x.Register.Edx>>5) & 0x7); /* EDX[7:5] Tlb Level Starts at 1 */ 599 | pTlbInfo[NewTlbIndex].TlbId = TlbId; 600 | pTlbInfo[NewTlbIndex].TlbMask = TlbMask; 601 | 602 | /* 603 | * Supported page sizes EBX[3:0] 604 | */ 605 | pTlbInfo[NewTlbIndex]._4K_PageSizeEntries = (pCpuidRegisters->x.Register.Ebx & 1) ? BOOL_TRUE : BOOL_FALSE; 606 | pTlbInfo[NewTlbIndex]._2MB_PageSizeEntries = (pCpuidRegisters->x.Register.Ebx & 2) ? BOOL_TRUE : BOOL_FALSE; 607 | pTlbInfo[NewTlbIndex]._4MB_PageSizeEntries = (pCpuidRegisters->x.Register.Ebx & 4) ? BOOL_TRUE : BOOL_FALSE; 608 | pTlbInfo[NewTlbIndex]._1GB_PageSizeEntries = (pCpuidRegisters->x.Register.Ebx & 8) ? BOOL_TRUE : BOOL_FALSE; 609 | 610 | /* 611 | * TLB Information 612 | */ 613 | pTlbInfo[NewTlbIndex].TlbParitioning = ((pCpuidRegisters->x.Register.Ebx>>8)&0x7); /* EBX[10:8] */ 614 | pTlbInfo[NewTlbIndex].TlbWays = ((pCpuidRegisters->x.Register.Ebx>>16)&0xFF); /* EBX[31:16] */ 615 | pTlbInfo[NewTlbIndex].TlbSets = (pCpuidRegisters->x.Register.Ecx); /* ECX[31:0] */ 616 | 617 | /* 618 | * TLB Attributes 619 | */ 620 | pTlbInfo[NewTlbIndex].FullyAssociative = (pCpuidRegisters->x.Register.Edx & (1<<8)) ? BOOL_TRUE : BOOL_FALSE; 621 | 622 | 623 | pTlbInfo[NewTlbIndex].NumberOfLPsSharingThisTlb = 0; 624 | memcpy(&pTlbInfo[NewTlbIndex].CachedCpuidRegisters, pCpuidRegisters, sizeof(CPUID_REGISTERS)); 625 | 626 | return NewTlbIndex; 627 | } 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | -------------------------------------------------------------------------------- /cpuid_topology_parsecpu.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 by Intel Corporation 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 12 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | 17 | #include 18 | #include 19 | #include 20 | #include "cpuid_topology.h" 21 | 22 | 23 | /* 24 | * Global application data variable 25 | */ 26 | extern GLOBAL_DATA g_GlobalData; 27 | 28 | 29 | 30 | 31 | 32 | /* 33 | * Internal Prototype APIs 34 | */ 35 | void ParseCpu_Internal_TopologyBitsFromLeaf(unsigned int Leaf); 36 | void ParseCpu_Internal_LegacyTopologyBits(void); 37 | void ParseCpu_Internal_CreateDomainMaskMatrix(PAPICID_BIT_LAYOUT_CTX pApicidBitLayoutCtx); 38 | 39 | 40 | /* 41 | * ParseCpu_CpuidLegacyExample 42 | * 43 | * The legacy example of CPUID.1 and CPUID.4. Software should 44 | * not use this method on modern systems, they should check for 45 | * Leaf 1Fh and then Leaf Bh. 46 | * 47 | * Arguments: 48 | * None 49 | * 50 | * Return: 51 | * None 52 | */ 53 | void ParseCpu_CpuidLegacyExample(void) 54 | { 55 | unsigned int MaximumAddressibleIdsPhysicalPackage; 56 | unsigned int MaximumAddressibleIdsCores; 57 | unsigned int LogicalProcessorsPerCore; 58 | unsigned int LogicalProcessorsPerPackage; 59 | unsigned int PackageShift; 60 | unsigned int LogicalProcessorShift; 61 | CPUID_REGISTERS CpuidRegisters; 62 | 63 | /* MaximumAddressibleIdsPhysicalPackage 64 | * 65 | * CPUID.1.EBX[23:16] 66 | * Maximum number of addressable IDs for logical processors in this physical package 67 | * 68 | * This is the legacy value for determining the package mask and has been superceded by Leaf 0Bh and Leaf 01Fh. 69 | * Since this is a byte, processors are already exceeding 256 addressible IDs either due to topology domains or 70 | * simply having more processors in a package. 71 | * 72 | * 73 | * CPUID.1.EDX[28].HTT 74 | * The Maximum number of addressable IDs for logical processor in this package is valid when set to 1. 75 | * 76 | */ 77 | 78 | MaximumAddressibleIdsPhysicalPackage = 1; 79 | 80 | Tools_ReadCpuid(1, 0, &CpuidRegisters); 81 | 82 | /* 83 | * Determine that CPUID.1.EDX[28].HTT == 1, if this is not set it would be a very old platform. 84 | */ 85 | if (CpuidRegisters.x.Register.Edx & ((unsigned int)1<<28)) 86 | { 87 | 88 | MaximumAddressibleIdsPhysicalPackage = (unsigned int)((CpuidRegisters.x.Register.Ebx>>16)&0xFF); 89 | 90 | /* 91 | * This would be a 20+ year old platform to not support CPUID.4 92 | */ 93 | Tools_ReadCpuid(0, 0, &CpuidRegisters); 94 | 95 | if (CpuidRegisters.x.Register.Eax >= 4) 96 | { 97 | /* MaximumAddressibleIdsCores 98 | * 99 | * CPUID.4.0.EAX[31:26] 100 | * Maximum number of addressable IDs for processor cores in the physical Package 101 | * 102 | * This is the legacy value for determining the core/SMT mask and has been superceded by Leaf 0Bh and Leaf 01Fh. 103 | * Since this is 6 bits, processors are already exceeding this value addressible IDs either due to topology domains or 104 | * simply having more processors in a package. 105 | */ 106 | Tools_ReadCpuid(4, 0, &CpuidRegisters); 107 | MaximumAddressibleIdsCores = (unsigned int)(CpuidRegisters.x.Register.Eax>>26) + 1; 108 | 109 | /* 110 | * Determine the number of LogicalProcessors per core. 111 | */ 112 | LogicalProcessorsPerCore = MaximumAddressibleIdsPhysicalPackage / MaximumAddressibleIdsCores; 113 | LogicalProcessorsPerPackage = MaximumAddressibleIdsPhysicalPackage; 114 | 115 | LogicalProcessorShift = Tools_CreateTopologyShift(LogicalProcessorsPerCore); 116 | PackageShift = Tools_CreateTopologyShift(LogicalProcessorsPerPackage); 117 | } 118 | else 119 | { 120 | /* 121 | * You cannot report Cores here, a Package == Core and so this only reports SMT within a Package. 122 | */ 123 | LogicalProcessorsPerCore = MaximumAddressibleIdsPhysicalPackage; 124 | LogicalProcessorsPerPackage = MaximumAddressibleIdsPhysicalPackage; 125 | 126 | PackageShift = Tools_CreateTopologyShift(MaximumAddressibleIdsPhysicalPackage); 127 | LogicalProcessorShift = PackageShift; 128 | } 129 | } 130 | else 131 | { 132 | /* 133 | * You do not report Cores or SMT here. It's always 1 Logical Processor. 134 | */ 135 | PackageShift = Tools_CreateTopologyShift(MaximumAddressibleIdsPhysicalPackage); 136 | LogicalProcessorShift = PackageShift; 137 | } 138 | 139 | Display_ThreeDomainDisplay(1, PackageShift, LogicalProcessorShift); 140 | } 141 | 142 | 143 | 144 | 145 | /* 146 | * ParseCpu_CpuidThreeDomainExample 147 | * 148 | * Performs the topology enumeration for 3 levels of 149 | * topology (Logical Processor, Core, Package). 150 | * 151 | * The valid leaf input as of today is 0xB or 0x1F. 152 | * 153 | * Arguments: 154 | * Leaf 155 | * 156 | * Return: 157 | * None 158 | */ 159 | void ParseCpu_CpuidThreeDomainExample(unsigned int Leaf) 160 | { 161 | unsigned int LogicalProcessorShift; 162 | unsigned int PackageShift; 163 | unsigned int DomainType; 164 | unsigned int DomainShift; 165 | unsigned int Subleaf; 166 | CPUID_REGISTERS CpuidRegisters; 167 | 168 | LogicalProcessorShift = 0; 169 | PackageShift = 0; 170 | Subleaf = 0; 171 | 172 | Tools_ReadCpuid(Leaf, Subleaf, &CpuidRegisters); 173 | 174 | while (CpuidRegisters.x.Register.Ebx != 0) 175 | { 176 | /* 177 | * CPUID.B or 1F.x.ECX[15:8] = Level Type / Domain Type 178 | */ 179 | DomainType = (CpuidRegisters.x.Register.Ecx>>8) & 0xFF; 180 | 181 | /* 182 | * CPUID.B or 1F.x.EAX[4:0] = Level Shift / Domain Shift 183 | */ 184 | DomainShift = CpuidRegisters.x.Register.Eax & 0x1F; 185 | 186 | /* 187 | * Determine if we have enumerated Logical Processor Domain, this will 188 | * always be CPUID.x.0 so can also do an ordered verification. 189 | * 190 | * Also determine that nothing has an invalid Domain Type. 191 | * 192 | */ 193 | switch(DomainType) 194 | { 195 | case InvalidDomain: 196 | /* Optionally log an error */ 197 | break; 198 | 199 | case LogicalProcessorDomain: 200 | LogicalProcessorShift = DomainShift; 201 | break; 202 | } 203 | 204 | /* 205 | * In three Domain topology, we do not care what the last Domain is. Whatever 206 | * it is this is the mask for the package and it is also the mask for the core 207 | * since we are only recognizing three Domains. It is always the core relationship 208 | * to the package. 209 | * 210 | * It is incorrect to check for core id (2) because then if there was another Domain 211 | * above core id, you would then mistake it as the package identifier. 212 | */ 213 | PackageShift = DomainShift; 214 | 215 | Subleaf++; 216 | Tools_ReadCpuid(Leaf, Subleaf, &CpuidRegisters); 217 | } 218 | 219 | Display_ThreeDomainDisplay(Leaf, PackageShift, LogicalProcessorShift); 220 | } 221 | 222 | 223 | 224 | 225 | /* 226 | * ParseCpu_CpuidManyDomainExample 227 | * 228 | * This algorithm will perform >3 Levels of 229 | * Domains but only for known domains. You could modify this 230 | * to also enumerate unknown domains but current expectations are 231 | * that software is only written to perform actions on known domains. 232 | * 233 | * This will collapse domains to known domains. 234 | * 235 | * Valid input is 0xB or 0x1F; only 0x1F can enumerate 236 | * more than 3 domains of topology. 237 | * 238 | * Arguments: 239 | * Leaf 240 | * 241 | * Return: 242 | * None 243 | */ 244 | void ParseCpu_CpuidManyDomainExample(unsigned int Leaf) 245 | { 246 | CPUID_REGISTERS CpuidRegisters; 247 | unsigned int Subleaf; 248 | unsigned int DomainType; 249 | unsigned int DomainShift; 250 | APICID_BIT_LAYOUT_CTX ApicidBitLayoutCtx; 251 | 252 | memset(&ApicidBitLayoutCtx, 0, sizeof(APICID_BIT_LAYOUT_CTX)); 253 | ApicidBitLayoutCtx.NumberOfApicIdBits = 32; 254 | 255 | Subleaf = 0; 256 | 257 | Tools_ReadCpuid(Leaf, Subleaf, &CpuidRegisters); 258 | 259 | while (CpuidRegisters.x.Register.Ebx != 0) 260 | { 261 | /* 262 | * CPUID.B or 1F.x.ECX[15:8] = Level Type / Domain Type 263 | */ 264 | DomainType = (CpuidRegisters.x.Register.Ecx>>8) & 0xFF; 265 | 266 | /* 267 | * CPUID.B or 1F.x.EAX[4:0] = Level Shift / Domain Shift 268 | */ 269 | DomainShift = CpuidRegisters.x.Register.Eax & 0x1F; 270 | 271 | 272 | /* 273 | * Best to check for known domains explicity since the ones you use 274 | * may not be in sequential ordering. 275 | */ 276 | switch(DomainType) 277 | { 278 | case InvalidDomain: 279 | /* This would be an error, could log it. */ 280 | case LogicalProcessorDomain: 281 | case CoreDomain: 282 | case ModuleDomain: 283 | case TileDomain: 284 | case DieDomain: 285 | case DieGrpDomain: 286 | ApicidBitLayoutCtx.ShiftValues[ApicidBitLayoutCtx.PackageDomainIndex] = DomainShift; 287 | ApicidBitLayoutCtx.ShiftValueDomain[ApicidBitLayoutCtx.PackageDomainIndex] = DomainType; 288 | ApicidBitLayoutCtx.PackageDomainIndex++; 289 | break; 290 | 291 | default: 292 | /* 293 | * First Domain is always Logical Processor, so we will always have a valid previous. 294 | */ 295 | ApicidBitLayoutCtx.ShiftValues[ApicidBitLayoutCtx.PackageDomainIndex-1] = DomainShift; 296 | } 297 | 298 | Subleaf++; 299 | Tools_ReadCpuid(Leaf, Subleaf, &CpuidRegisters); 300 | } 301 | 302 | ParseCpu_Internal_CreateDomainMaskMatrix(&ApicidBitLayoutCtx); 303 | Display_ManyDomainExample(Leaf, &ApicidBitLayoutCtx); 304 | } 305 | 306 | 307 | /* 308 | * ParseCpu_Internal_CreateDomainMaskMatrix 309 | * 310 | * There are many ways to create masks to identify a logical processor or domain. There are masks 311 | * that will allow unquie identity of that domain across the entire system. There are also masks that 312 | * will create an ID for a domain relative to other higher domains below the entire system. This routine 313 | * generates these domains. 314 | * 315 | * Arguments: 316 | * APIC Bit Layout Context 317 | * 318 | * Return: 319 | * None 320 | */ 321 | void ParseCpu_Internal_CreateDomainMaskMatrix(PAPICID_BIT_LAYOUT_CTX pApicidBitLayoutCtx) 322 | { 323 | unsigned int DomainIndex; 324 | unsigned int NextDomainIndex; 325 | unsigned int PreviousBit; 326 | 327 | PreviousBit = 0; 328 | 329 | /* 330 | * Create globally identifiable masks for each domain. 331 | */ 332 | for (DomainIndex = 0; DomainIndex <= pApicidBitLayoutCtx->PackageDomainIndex; DomainIndex++) 333 | { 334 | pApicidBitLayoutCtx->DomainRelativeMasks[DomainIndex][DomainIndex] = ~((1<ShiftValues[DomainIndex]; 336 | } 337 | 338 | /* 339 | * Create a relative identifier for each Domain to another higher level Domain 340 | */ 341 | for (DomainIndex = 0; DomainIndex < pApicidBitLayoutCtx->PackageDomainIndex; DomainIndex++) 342 | { 343 | /* 344 | * Start to create relative IDs to the next level above the current. 345 | * 346 | * A relative ID is taking the global ID mask and removing the previous mask (which is already done) and then removing the mask of 347 | * the higher level domain, so for example: 348 | * 349 | * A global Logical processor mask would be 0xFFFFFFFF since all logical processors are the lowest identifier so the entire APIC ID is needed. 350 | * 351 | * A global Core mask could be: 0xFFFFFFFE meaning the Core ID doesn't include the lower Logical Processor IDs. This will identify the 2 Logical processors as 352 | * a core globally. 353 | * 354 | * A global Package mask could be: 0xFFFFFFF8 Meaning we can identify this package among other packages and this package has 8 logical processors. 355 | * 356 | * 357 | * To then create a Mask to create an ID relative to Package, we would do ~(0xFFFFFFF8) & 0xFFFFFFFE = 0x00000006 Essentially, you remove the ID mask for the upper domain 358 | * from the global mask ID for the core. To create the full ID though you also need to use the low bit's shift value. 359 | * 360 | * (APIC ID & 0x6)>>1 = CORE_ID for the Package. 361 | * 362 | */ 363 | for (NextDomainIndex = DomainIndex+1; NextDomainIndex <= pApicidBitLayoutCtx->PackageDomainIndex; NextDomainIndex++) 364 | { 365 | pApicidBitLayoutCtx->DomainRelativeMasks[DomainIndex][NextDomainIndex] = (~pApicidBitLayoutCtx->DomainRelativeMasks[NextDomainIndex][NextDomainIndex]) & (pApicidBitLayoutCtx->DomainRelativeMasks[DomainIndex][DomainIndex]); 366 | } 367 | } 368 | } 369 | 370 | 371 | 372 | 373 | 374 | /* 375 | * ParseCpu_ApicIdTopologyLayout 376 | * 377 | * Parse the APIC ID Topology Layout in a general 378 | * fashion using the different leafs. 379 | * 380 | * Arguments: 381 | * None 382 | * 383 | * Return: 384 | * None 385 | */ 386 | void ParseCpu_ApicIdTopologyLayout(void) 387 | { 388 | CPUID_REGISTERS CpuidRegisters; 389 | 390 | Tools_ReadCpuid(0, 0, &CpuidRegisters); 391 | 392 | if (CpuidRegisters.x.Register.Eax >= 0x1F) 393 | { 394 | ParseCpu_Internal_TopologyBitsFromLeaf(0x1F); 395 | } 396 | 397 | if (CpuidRegisters.x.Register.Eax >= 0xB) 398 | { 399 | ParseCpu_Internal_TopologyBitsFromLeaf(0xB); 400 | } 401 | 402 | ParseCpu_Internal_LegacyTopologyBits(); 403 | } 404 | 405 | /* 406 | * ParseCpu_Internal_LegacyTopologyBits 407 | * 408 | * This is a legacy example. Software should only use this 409 | * as a fallback if Leaf 1F and Leaf B both do not exist. 410 | * 411 | * 412 | * Arguments: 413 | * None 414 | * 415 | * Return: 416 | * None 417 | */ 418 | void ParseCpu_Internal_LegacyTopologyBits(void) 419 | { 420 | CPUID_REGISTERS CpuidRegisters; 421 | APICID_BIT_LAYOUT_CTX ApicidBitLayoutCtx; 422 | unsigned int MaximumAddressibleIdsPhysicalPackage; 423 | unsigned int MaximumAddressibleIdsCores; 424 | unsigned int PackageShift; 425 | unsigned int LogicalProcessorsPerCore; 426 | unsigned int LogicalProcessorShift; 427 | 428 | memset(&ApicidBitLayoutCtx, 0, sizeof(ApicidBitLayoutCtx)); 429 | 430 | ApicidBitLayoutCtx.ShiftValueDomain[0] = LogicalProcessorDomain; 431 | ApicidBitLayoutCtx.ShiftValueDomain[1] = CoreDomain; 432 | ApicidBitLayoutCtx.PackageDomainIndex = 2; 433 | ApicidBitLayoutCtx.NumberOfApicIdBits = 8; 434 | 435 | MaximumAddressibleIdsPhysicalPackage = 1; 436 | Tools_ReadCpuid(1, 0, &CpuidRegisters); 437 | 438 | 439 | /* 440 | * Determine that CPUID.1.EDX[28].HTT == 1; the meaning of this bit has changed from 441 | * originally being SMT to being about Multi-Core. 442 | */ 443 | if (CpuidRegisters.x.Register.Edx & (1<<28)) 444 | { 445 | MaximumAddressibleIdsPhysicalPackage = (unsigned int)((CpuidRegisters.x.Register.Ebx>>16)&0xFF); 446 | 447 | /* 448 | * This would be a very old platform to not have Leaf 4. 449 | */ 450 | Tools_ReadCpuid(0, 0, &CpuidRegisters); 451 | 452 | if (CpuidRegisters.x.Register.Eax >= 4) 453 | { 454 | 455 | /* MaximumAddressibleIdsCores 456 | * 457 | * CPUID.4.0.EAX[31:26] 458 | * Maximum number of addressable IDs for processor cores in the physical Package 459 | * 460 | * This is the legacy value for determining the core/SMT mask and has been superceded by Leaf 0Bh and Leaf 01Fh. 461 | * Since this is 6 bits, processors are already exceeding this value addressible IDs either due to topology domains or 462 | * simply having more processors in a package. 463 | */ 464 | Tools_ReadCpuid(4, 0, &CpuidRegisters); 465 | MaximumAddressibleIdsCores = (unsigned int)(CpuidRegisters.x.Register.Eax>>26) + 1; 466 | 467 | /* 468 | * Determine the number of LogicalProcessors per core. 469 | */ 470 | LogicalProcessorsPerCore = MaximumAddressibleIdsPhysicalPackage / MaximumAddressibleIdsCores; 471 | 472 | LogicalProcessorShift = Tools_CreateTopologyShift(LogicalProcessorsPerCore); 473 | 474 | PackageShift = Tools_CreateTopologyShift(MaximumAddressibleIdsPhysicalPackage); 475 | 476 | ApicidBitLayoutCtx.ShiftValues[0] = LogicalProcessorShift; 477 | ApicidBitLayoutCtx.ShiftValues[1] = PackageShift; 478 | 479 | strncpy(ApicidBitLayoutCtx.szDescription, "Legacy path using CPUID.1 and CPUID.4 (May not be correct if Leaf B or Leaf 1F exist.)", sizeof(ApicidBitLayoutCtx.szDescription)-1); 480 | } 481 | else 482 | { 483 | /* 484 | * This is no Cores, only logical processors in a package (i.e. SMT) 485 | */ 486 | PackageShift = Tools_CreateTopologyShift(MaximumAddressibleIdsPhysicalPackage); 487 | 488 | ApicidBitLayoutCtx.ShiftValues[0] = PackageShift; 489 | ApicidBitLayoutCtx.PackageDomainIndex = 1; 490 | strncpy(ApicidBitLayoutCtx.szDescription, "Legacy path using CPUID.1 and CPUID.HTT = 1 but no CPUID.4", sizeof(ApicidBitLayoutCtx.szDescription)-1); 491 | } 492 | } 493 | else 494 | { 495 | /* 496 | * Without any enumeration of CPUID existing, then it's just one logical processor per package. 497 | */ 498 | PackageShift = Tools_CreateTopologyShift(MaximumAddressibleIdsPhysicalPackage); 499 | ApicidBitLayoutCtx.ShiftValues[0] = PackageShift; 500 | ApicidBitLayoutCtx.PackageDomainIndex = 1; 501 | strncpy(ApicidBitLayoutCtx.szDescription, "Legacy path where CPUID.HTT = 0", sizeof(ApicidBitLayoutCtx.szDescription)-1); 502 | } 503 | 504 | Display_ApicIdBitLayout(&ApicidBitLayoutCtx); 505 | } 506 | 507 | /* 508 | * ParseCpu_Internal_TopologyBitsFromLeaf 509 | * 510 | * Parse the full topology and display the APIC ID bits. 511 | * this will also deal with unknown levels and first 512 | * display the full APICID layout and then if any 513 | * unknown domains were enumerated it will collapse 514 | * them into the previous known domain. 515 | * 516 | * The input is either 0xB or 0x1F, where 0xB 517 | * will only ever return 3 level topology. 518 | * 519 | * 520 | * Arguments: 521 | * CPUID Leaf 522 | * 523 | * Return: 524 | * None 525 | */ 526 | void ParseCpu_Internal_TopologyBitsFromLeaf(unsigned int Leaf) 527 | { 528 | BOOL_TYPE bFoundUnknownDomain; 529 | unsigned int Subleaf; 530 | unsigned int DomainShift; 531 | unsigned int DomainType; 532 | CPUID_REGISTERS CpuidRegisters; 533 | APICID_BIT_LAYOUT_CTX ApicidBitLayoutCtx; 534 | 535 | memset(&ApicidBitLayoutCtx, 0, sizeof(ApicidBitLayoutCtx)); 536 | 537 | sprintf(ApicidBitLayoutCtx.szDescription, "**** APIC ID Bit Layout CPUID.0x%0x ****\n\n", Leaf); 538 | ApicidBitLayoutCtx.NumberOfApicIdBits = 32; 539 | 540 | Subleaf = 0; 541 | 542 | Tools_ReadCpuid(Leaf, Subleaf, &CpuidRegisters); 543 | 544 | bFoundUnknownDomain = BOOL_FALSE; 545 | 546 | 547 | /* 548 | * In this first example, the unknown domains will not be collapsed but 549 | * shown in the APIC ID layout. If unknown domains are found, then it will 550 | * provide a second example with them collasped. 551 | */ 552 | 553 | while (CpuidRegisters.x.Register.Ebx != 0) 554 | { 555 | /* 556 | * CPUID.B or 1F.x.ECX[15:8] = Level Type / Domain Type 557 | */ 558 | DomainType = (CpuidRegisters.x.Register.Ecx>>8) & 0xFF; 559 | 560 | /* 561 | * CPUID.B or 1F.x.EAX[4:0] = Level Shift / Domain Shift 562 | */ 563 | DomainShift = CpuidRegisters.x.Register.Eax & 0x1F; 564 | 565 | ApicidBitLayoutCtx.ShiftValues[Subleaf] = DomainShift; 566 | ApicidBitLayoutCtx.ShiftValueDomain[Subleaf] = DomainType; 567 | ApicidBitLayoutCtx.PackageDomainIndex++; 568 | 569 | /* 570 | * Best to check for known domains explicity since the ones you use 571 | * may not be in sequential ordering. 572 | */ 573 | switch(DomainType) 574 | { 575 | case InvalidDomain: 576 | /* This would be an error, could log it. */ 577 | case LogicalProcessorDomain: 578 | case CoreDomain: 579 | case ModuleDomain: 580 | case TileDomain: 581 | case DieDomain: 582 | case DieGrpDomain: 583 | break; 584 | 585 | default: 586 | bFoundUnknownDomain = BOOL_TRUE; 587 | } 588 | 589 | Subleaf++; 590 | Tools_ReadCpuid(Leaf, Subleaf, &CpuidRegisters); 591 | } 592 | 593 | Display_ApicIdBitLayout(&ApicidBitLayoutCtx); 594 | 595 | 596 | if (bFoundUnknownDomain) 597 | { 598 | sprintf(ApicidBitLayoutCtx.szDescription, "\nFound Unknown Domains, Consolidated APIC ID\n\n"); 599 | ApicidBitLayoutCtx.PackageDomainIndex = 0; 600 | 601 | Subleaf = 0; 602 | Tools_ReadCpuid(Leaf, Subleaf, &CpuidRegisters); 603 | 604 | bFoundUnknownDomain = BOOL_FALSE; 605 | 606 | /* 607 | * Collaspe unknown domains into known domains and display the known APIC ID layout. 608 | */ 609 | while (CpuidRegisters.x.Register.Ebx != 0) 610 | { 611 | /* 612 | * CPUID.B or 1F.x.ECX[15:8] = Level Type / Domain Type 613 | */ 614 | DomainType = (CpuidRegisters.x.Register.Ecx>>8) & 0xFF; 615 | 616 | /* 617 | * CPUID.B or 1F.x.EAX[4:0] = Level Shift / Domain Shift 618 | */ 619 | DomainShift = CpuidRegisters.x.Register.Eax & 0x1F; 620 | 621 | /* 622 | * Best to check for known domains explicity since the ones you use 623 | * may not be in sequential ordering. 624 | */ 625 | switch(DomainType) 626 | { 627 | case InvalidDomain: 628 | /* This would be an error, could log it. */ 629 | case LogicalProcessorDomain: 630 | case CoreDomain: 631 | case ModuleDomain: 632 | case TileDomain: 633 | case DieDomain: 634 | case DieGrpDomain: 635 | ApicidBitLayoutCtx.ShiftValues[ApicidBitLayoutCtx.PackageDomainIndex] = DomainShift; 636 | ApicidBitLayoutCtx.ShiftValueDomain[ApicidBitLayoutCtx.PackageDomainIndex] = DomainType; 637 | ApicidBitLayoutCtx.PackageDomainIndex++; 638 | break; 639 | 640 | default: 641 | /* 642 | * First Domain is always Logical Processor, so we will always have a valid previous. 643 | * Not doing any error checking here and assumption that hardware reported the correct details. 644 | * If this is reported incorrectly and it is detected then it would be possible to report 645 | * an error, bail out or attempt to work with incorrect information. 646 | */ 647 | ApicidBitLayoutCtx.ShiftValues[ApicidBitLayoutCtx.PackageDomainIndex-1] = DomainShift; 648 | } 649 | 650 | Subleaf++; 651 | Tools_ReadCpuid(Leaf, Subleaf, &CpuidRegisters); 652 | } 653 | 654 | Display_ApicIdBitLayout(&ApicidBitLayoutCtx); 655 | } 656 | } 657 | 658 | -------------------------------------------------------------------------------- /cpuid_topology_tools.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 by Intel Corporation 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 12 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include "cpuid_topology.h" 20 | 21 | 22 | /* 23 | * Global application data variable 24 | */ 25 | GLOBAL_DATA g_GlobalData; 26 | 27 | 28 | /* 29 | * Tools_ReadCpuid 30 | * 31 | * CPUID API used to be able to read native CPUID or be thunked and 32 | * simulate or display other CPUID values from other platforms 33 | * or testing out topology values. 34 | * 35 | * Arguments: 36 | * Leaf, Subleaf, CPUID Data Structure 37 | * 38 | * Return: 39 | * None 40 | */ 41 | void Tools_ReadCpuid(unsigned int Leaf, unsigned int Subleaf, PCPUID_REGISTERS pCpuidRegisters) 42 | { 43 | if (g_GlobalData.UseNativeCpuid) 44 | { 45 | Os_Platform_Read_Cpuid(Leaf, Subleaf, pCpuidRegisters); 46 | } 47 | else 48 | { 49 | memset(pCpuidRegisters, 0, sizeof(CPUID_REGISTERS)); 50 | 51 | if (Leaf < MAX_SIMULATED_LEAFS) 52 | { 53 | if (Subleaf < MAX_SIMULATED_SUBLEAFS) 54 | { 55 | /* 56 | * The file stores a single copy of each CPUID except for CPUID.4 and CPUID.18. Although the others 57 | * have some asymmetric aspects in CPUID.1F and CPUID.B; they are not important to this sample code. However, to 58 | * ensure we reserve asymmetric topology enumeration we save all of CPUID.4 and CPUID.18 values and so we 59 | * have to dispatch those seperately and other leafs we rebuild just the APIC IDs (we do not rebuild EBX in extended 60 | * topology leaf, which can also be asymmetric but it's only for reporting purposes and not used in this sample). 61 | */ 62 | if (Leaf == 0x4) 63 | { 64 | pCpuidRegisters->x.Registers[0] = g_GlobalData.SimulatedCpuidLeaf4[g_GlobalData.CurrentProcessorAffinity][Subleaf][0]; 65 | pCpuidRegisters->x.Registers[1] = g_GlobalData.SimulatedCpuidLeaf4[g_GlobalData.CurrentProcessorAffinity][Subleaf][1]; 66 | pCpuidRegisters->x.Registers[2] = g_GlobalData.SimulatedCpuidLeaf4[g_GlobalData.CurrentProcessorAffinity][Subleaf][2]; 67 | pCpuidRegisters->x.Registers[3] = g_GlobalData.SimulatedCpuidLeaf4[g_GlobalData.CurrentProcessorAffinity][Subleaf][3]; 68 | } 69 | else 70 | { 71 | if (Leaf == 0x18) 72 | { 73 | pCpuidRegisters->x.Registers[0] = g_GlobalData.SimulatedCpuidLeaf18[g_GlobalData.CurrentProcessorAffinity][Subleaf][0]; 74 | pCpuidRegisters->x.Registers[1] = g_GlobalData.SimulatedCpuidLeaf18[g_GlobalData.CurrentProcessorAffinity][Subleaf][1]; 75 | pCpuidRegisters->x.Registers[2] = g_GlobalData.SimulatedCpuidLeaf18[g_GlobalData.CurrentProcessorAffinity][Subleaf][2]; 76 | pCpuidRegisters->x.Registers[3] = g_GlobalData.SimulatedCpuidLeaf18[g_GlobalData.CurrentProcessorAffinity][Subleaf][3]; 77 | } 78 | else 79 | { 80 | pCpuidRegisters->x.Registers[0] = g_GlobalData.SimulatedCpuid[Leaf][Subleaf][0]; 81 | pCpuidRegisters->x.Registers[1] = g_GlobalData.SimulatedCpuid[Leaf][Subleaf][1]; 82 | pCpuidRegisters->x.Registers[2] = g_GlobalData.SimulatedCpuid[Leaf][Subleaf][2]; 83 | pCpuidRegisters->x.Registers[3] = g_GlobalData.SimulatedCpuid[Leaf][Subleaf][3]; 84 | 85 | if ((Leaf == 0xB || Leaf == 0x1F) && pCpuidRegisters->x.Registers[1] != 0) 86 | { 87 | pCpuidRegisters->x.Registers[3] = g_GlobalData.SimulatedApicIds[g_GlobalData.CurrentProcessorAffinity]; 88 | } 89 | 90 | if (Leaf == 0x1) 91 | { 92 | pCpuidRegisters->x.Registers[2] = pCpuidRegisters->x.Registers[2] & (~(0xFF<<24)); 93 | pCpuidRegisters->x.Registers[2] = pCpuidRegisters->x.Registers[2] | (g_GlobalData.SimulatedApicIds[g_GlobalData.CurrentProcessorAffinity]<<24); 94 | } 95 | } 96 | } 97 | } 98 | } 99 | } 100 | } 101 | 102 | 103 | 104 | 105 | 106 | 107 | /* 108 | * Tools_CreateTopologyShift 109 | * 110 | * Creates a power of 2 mask inclusive of the count. 111 | * 112 | * Arguments: 113 | * None 114 | * 115 | * Return: 116 | * None 117 | */ 118 | unsigned int Tools_CreateTopologyShift(unsigned int count) 119 | { 120 | unsigned int Index; 121 | unsigned int Shift; 122 | 123 | Shift = 31; 124 | 125 | count = (count*2) - 1; 126 | 127 | for (Index = (1<>=1, Shift--) 128 | { 129 | if (count & Index) 130 | { 131 | break; 132 | } 133 | } 134 | 135 | return Shift; 136 | } 137 | 138 | 139 | 140 | 141 | /* 142 | * Tools_IsNative 143 | * 144 | * Determines if CPUID is in NATIVE or Virtual Mode 145 | * 146 | * Arguments: 147 | * None 148 | * 149 | * Return: 150 | * TRUE for Native and FALSE for Virtual 151 | */ 152 | BOOL_TYPE Tools_IsNative(void) 153 | { 154 | return g_GlobalData.UseNativeCpuid; 155 | } 156 | 157 | 158 | /* 159 | * Tools_SetAffinity 160 | * 161 | * Sets affinity to the specified processor. 162 | * 163 | * Ignored if processor does not exist. 164 | * 165 | * Arguments: 166 | * Processor to set affinity 167 | * 168 | * Return: 169 | * None 170 | */ 171 | void Tools_SetAffinity(unsigned int ProcessorNumber) 172 | { 173 | if (g_GlobalData.UseNativeCpuid) 174 | { 175 | Os_SetAffinity(ProcessorNumber); 176 | } 177 | else 178 | { 179 | if (ProcessorNumber < g_GlobalData.NumberOfSimulatedProcessors) 180 | { 181 | g_GlobalData.CurrentProcessorAffinity = ProcessorNumber; 182 | } 183 | } 184 | } 185 | 186 | 187 | 188 | 189 | /* 190 | * Tools_GetNumberOfProcessors 191 | * 192 | * Returns the number of processors on this system 193 | * 194 | * Arguments: 195 | * None 196 | * 197 | * Return: 198 | * The number of processors 199 | */ 200 | unsigned int Tools_GetNumberOfProcessors(void) 201 | { 202 | unsigned int NumberOfProcessors; 203 | 204 | if (g_GlobalData.UseNativeCpuid) 205 | { 206 | NumberOfProcessors = Os_GetNumberOfProcessors(); 207 | } 208 | else 209 | { 210 | NumberOfProcessors = g_GlobalData.NumberOfSimulatedProcessors; 211 | } 212 | 213 | return NumberOfProcessors; 214 | } 215 | 216 | 217 | /* 218 | * Tools_IsDomainKnownEnumeration 219 | * 220 | * Determines if the enumeration value is known. 221 | * 222 | * Arguments: 223 | * Domain 224 | * 225 | * Return: 226 | * Returns BOOL_TRUE if the enumeration is a known value. 227 | */ 228 | BOOL_TYPE Tools_IsDomainKnownEnumeration(unsigned int Domain) 229 | { 230 | BOOL_TYPE KnownEnumeration; 231 | 232 | KnownEnumeration = BOOL_FALSE; 233 | 234 | switch (Domain) 235 | { 236 | case InvalidDomain: /* This is a valid known enumeration; code should check directly for invalid. 237 | * This API doesn't mean that unknown domains are invalid so doesn't make sense 238 | * to call it not a known enumeration. 239 | */ 240 | case LogicalProcessorDomain: 241 | case CoreDomain: 242 | case ModuleDomain: 243 | case TileDomain: 244 | case DieDomain: 245 | case DieGrpDomain: 246 | KnownEnumeration = BOOL_TRUE; 247 | break; 248 | } 249 | 250 | return KnownEnumeration; 251 | } 252 | 253 | 254 | /* 255 | * Tools_GatherPlatformApicIds 256 | * 257 | * Creates and returns a cache of the APIC IDs. 258 | * 259 | * 260 | * Arguments: 261 | * APIC ID array, Size of the array 262 | * 263 | * Return: 264 | * Number of APIC IDs populated 265 | */ 266 | unsigned int Tools_GatherPlatformApicIds(unsigned int *pApicIdArray, unsigned int ArraySize) 267 | { 268 | unsigned int NumberOfProcessors; 269 | unsigned int Index; 270 | unsigned int ApicId; 271 | BOOL_TYPE bApicIdFound; 272 | CPUID_REGISTERS CpuidRegisters; 273 | CPUID_REGISTERS CpuidRegistersApicid; 274 | 275 | NumberOfProcessors = Tools_GetNumberOfProcessors(); 276 | 277 | if (NumberOfProcessors > MAX_PROCESSORS) 278 | { 279 | NumberOfProcessors = MAX_PROCESSORS; 280 | } 281 | 282 | /* 283 | * Determine X2APIC ID or fall back to APIC ID. 284 | */ 285 | Tools_ReadCpuid(0, 0, &CpuidRegisters); 286 | 287 | for (Index = 0; Index < NumberOfProcessors && Index < ArraySize; Index++) 288 | { 289 | ApicId = 0; 290 | bApicIdFound = BOOL_FALSE; 291 | 292 | Tools_SetAffinity(Index); 293 | if (CpuidRegisters.x.Register.Eax >= 0x1F) 294 | { 295 | Tools_ReadCpuid(0x1F, 0, &CpuidRegistersApicid); 296 | 297 | if (CpuidRegistersApicid.x.Register.Ebx != 0) 298 | { 299 | bApicIdFound = BOOL_TRUE; 300 | ApicId = CpuidRegistersApicid.x.Register.Edx; 301 | } 302 | 303 | } 304 | 305 | if (bApicIdFound == BOOL_FALSE) 306 | { 307 | if (CpuidRegisters.x.Register.Eax >= 0xB) 308 | { 309 | Tools_ReadCpuid(0xB, 0, &CpuidRegistersApicid); 310 | if (CpuidRegistersApicid.x.Register.Ebx != 0) 311 | { 312 | ApicId = CpuidRegistersApicid.x.Register.Edx; 313 | bApicIdFound = BOOL_TRUE; 314 | } 315 | } 316 | 317 | if (bApicIdFound == BOOL_FALSE) 318 | { 319 | /* 320 | * Fall back to Legacy 8 bit APIC ID. 321 | */ 322 | Tools_ReadCpuid(1, 0, &CpuidRegistersApicid); 323 | ApicId = (CpuidRegistersApicid.x.Register.Ebx >> 24); 324 | bApicIdFound = BOOL_TRUE; 325 | } 326 | } 327 | 328 | pApicIdArray[Index] = ApicId; 329 | } 330 | 331 | return Index; 332 | } 333 | 334 | -------------------------------------------------------------------------------- /linux_os_util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 by Intel Corporation 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 12 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #define _GNU_SOURCE 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "cpuid_topology.h" 23 | 24 | 25 | /* 26 | * Constants, values for local use 27 | */ 28 | #define BYTES_IN_KB (1024) 29 | #define BYTES_IN_MB (1048576) 30 | 31 | 32 | 33 | 34 | /* 35 | * Os_DisplayTopology 36 | * 37 | * Display the topology using OS related information 38 | * 39 | * Arguments: 40 | * None 41 | * 42 | * Return: 43 | * None 44 | */ 45 | void Os_DisplayTopology(void) 46 | { 47 | /* 48 | * One example, applications may parse the output. 49 | */ 50 | printf("*********************************\n"); 51 | printf("**** Linux OS /proc/cpuinfo ****\n\n"); 52 | 53 | system("cat /proc/cpuinfo\n"); 54 | 55 | printf("\n\n"); 56 | } 57 | 58 | 59 | 60 | 61 | /* 62 | * Os_Platform_Read_Cpuid 63 | * 64 | * OS/Compiler Specific implementation of reading CPUID. 65 | * 66 | * Arguments: 67 | * Leaf, Subleaf, CPUID Data Structure 68 | * 69 | * Return: 70 | * None 71 | */ 72 | void Os_Platform_Read_Cpuid(unsigned int Leaf, unsigned int Subleaf, PCPUID_REGISTERS pCpuidRegisters) 73 | { 74 | unsigned int ReturnEax; 75 | unsigned int ReturnEbx; 76 | unsigned int ReturnEcx; 77 | unsigned int ReturnEdx; 78 | 79 | asm ("movl %4, %%eax\n" 80 | "movl %5, %%ecx\n" 81 | "CPUID\n" 82 | "movl %%eax, %0\n" 83 | "movl %%ebx, %1\n" 84 | "movl %%ecx, %2\n" 85 | "movl %%edx, %3\n" 86 | : "=r" (ReturnEax), "=r" (ReturnEbx), "=r" (ReturnEcx), "=r" (ReturnEdx) 87 | : "r" (Leaf), "r" (Subleaf) 88 | : "%eax", "%ebx", "%ecx", "%edx" 89 | ); 90 | 91 | pCpuidRegisters->x.Register.Eax = ReturnEax; 92 | pCpuidRegisters->x.Register.Ebx = ReturnEbx; 93 | pCpuidRegisters->x.Register.Ecx = ReturnEcx; 94 | pCpuidRegisters->x.Register.Edx = ReturnEdx; 95 | } 96 | 97 | /* 98 | * Os_GetNumberOfProcessors 99 | * 100 | * Get the number of processors 101 | * 102 | * Arguments: 103 | * None 104 | * 105 | * Return: 106 | * Number of processors 107 | */ 108 | unsigned int Os_GetNumberOfProcessors(void) 109 | { 110 | unsigned int NumberOfProcessors; 111 | 112 | NumberOfProcessors = (unsigned int)get_nprocs(); 113 | 114 | return NumberOfProcessors; 115 | } 116 | 117 | 118 | /* 119 | * Os_SetAffinity 120 | * 121 | * Set the current thread affinity 122 | * 123 | * Arguments: 124 | * Processor Number 125 | * 126 | * Return: 127 | * None 128 | */ 129 | void Os_SetAffinity(unsigned int ProcessorNumber) 130 | { 131 | cpu_set_t *cpu_set; 132 | unsigned int NumberOfProcessors; 133 | unsigned int SetSize; 134 | 135 | /* 136 | * Get the size of the maximum number of configured processors. 137 | */ 138 | NumberOfProcessors = get_nprocs_conf(); 139 | 140 | /* 141 | * Something larger comes in we will just go with it. 142 | */ 143 | if (ProcessorNumber > NumberOfProcessors) 144 | { 145 | NumberOfProcessors = ProcessorNumber; 146 | } 147 | 148 | cpu_set = CPU_ALLOC(NumberOfProcessors); 149 | 150 | if (cpu_set) 151 | { 152 | SetSize = CPU_ALLOC_SIZE(NumberOfProcessors); 153 | CPU_ZERO_S(SetSize, cpu_set); 154 | CPU_SET_S(ProcessorNumber, SetSize, cpu_set); 155 | 156 | sched_setaffinity(getpid(),SetSize, cpu_set); 157 | 158 | CPU_FREE(cpu_set); 159 | } 160 | } 161 | 162 | 163 | -------------------------------------------------------------------------------- /security.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | Intel is committed to rapidly addressing security vulnerabilities affecting our customers and providing clear guidance on the solution, impact, severity and mitigation. 3 | 4 | ## Reporting a Vulnerability 5 | Please report any security vulnerabilities in this project utilizing the guidelines [here](https://www.intel.com/content/www/us/en/security-center/vulnerability-handling-guidelines.html). 6 | -------------------------------------------------------------------------------- /win_os_util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 by Intel Corporation 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 9 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 11 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 12 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 13 | * PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | 17 | #include 18 | #include 19 | #include 20 | #include "cpuid_topology.h" 21 | 22 | /* 23 | * Constants, values for local use 24 | */ 25 | #define BYTES_IN_KB (1024) 26 | #define BYTES_IN_MB (1048576) 27 | 28 | void __cpuidex(unsigned int *cpuinfo,int function_id,int subfunction_id); 29 | #pragma intrinsic(__cpuidex) 30 | 31 | 32 | #define CACHE_INVALID_INDEX 4 33 | 34 | unsigned char *g_pszCacheTypeString[] = { 35 | "Unified", 36 | "Instruction", 37 | "Data", 38 | "Trace", 39 | "Invalid or Unknown Enumeration" 40 | }; 41 | 42 | /* 43 | * Prototypes 44 | */ 45 | void WinOs_EnumerateAndDisplayTopology(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX pSystemLogicalProcInfoEx, DWORD BufferSize); 46 | void WinOs_DisplayGroupAffinity(WORD GroupCount, GROUP_AFFINITY *pGroupAffinityArray); 47 | unsigned char *WinOs_GetCacheTypeString(PROCESSOR_CACHE_TYPE CacheType); 48 | 49 | /* 50 | * Os_DisplayTopology 51 | * 52 | * Display the OS using Windows APIs. 53 | * 54 | * Arguments: 55 | * None 56 | * 57 | * Return: 58 | * None 59 | */ 60 | void Os_DisplayTopology(void) 61 | { 62 | PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX pSystemLogicalProcInfoEx; 63 | DWORD BufferSize = 0; 64 | 65 | GetLogicalProcessorInformationEx(RelationAll, NULL, &BufferSize); 66 | 67 | pSystemLogicalProcInfoEx = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)malloc(BufferSize); 68 | 69 | if(pSystemLogicalProcInfoEx) 70 | { 71 | printf("********************************************************\n"); 72 | printf("**** Windows OS GetLogicalProcessorInformationEx() ****\n\n"); 73 | 74 | memset(pSystemLogicalProcInfoEx, 0, BufferSize); 75 | 76 | if (GetLogicalProcessorInformationEx(RelationAll, pSystemLogicalProcInfoEx, &BufferSize) != FALSE) 77 | { 78 | WinOs_EnumerateAndDisplayTopology(pSystemLogicalProcInfoEx, BufferSize); 79 | } 80 | else 81 | { 82 | printf("Error; failed to display GetLogicalProcessorInformationEx() GLE: %i\n\n", GetLastError()); 83 | } 84 | 85 | free(pSystemLogicalProcInfoEx); 86 | 87 | printf("\n\n"); 88 | } 89 | else 90 | { 91 | printf("Error; failed to display GetLogicalProcessorInformationEx() GLE: %i\n\n", GetLastError()); 92 | } 93 | } 94 | 95 | 96 | 97 | /* 98 | * WinOs_EnumerateAndDisplayTopology 99 | * 100 | * Itterates through the OS supplied topology information. 101 | * 102 | * Arguments: 103 | * System Logical Processor Buffer, Size of the Buffer 104 | * 105 | * Return: 106 | * None 107 | */ 108 | void WinOs_EnumerateAndDisplayTopology(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX pSystemLogicalProcInfoEx, DWORD BufferSize) 109 | { 110 | PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX ptr; 111 | DWORD Index; 112 | PCACHE_RELATIONSHIP Cache; 113 | PGROUP_AFFINITY pGroupAffinity; 114 | PPROCESSOR_GROUP_INFO pProcessorGroupInfo; 115 | DWORD CurrentSize; 116 | DWORD NumberOfPackages = 0; 117 | WORD GroupCount; 118 | 119 | ptr = pSystemLogicalProcInfoEx; 120 | CurrentSize = 0; 121 | 122 | /* 123 | * Example implementation that displays the data, however for applications it could cache the data and use it for any purpose 124 | */ 125 | 126 | while (CurrentSize < BufferSize) 127 | { 128 | switch (ptr->Relationship) 129 | { 130 | case RelationProcessorCore: 131 | printf(" - Processor Core\n"); 132 | WinOs_DisplayGroupAffinity(ptr->Processor.GroupCount, &ptr->Processor.GroupMask[0]); 133 | printf("+++++\n"); 134 | break; 135 | 136 | case RelationCache: 137 | printf(" - Cache Type: %s\n", WinOs_GetCacheTypeString(ptr->Cache.Type)); 138 | printf(" CacheLevel: L%i\n", ptr->Cache.Level); 139 | printf(" CacheSize: %i Bytes (%i Kilobytes) (%i Megabytes)\n", ptr->Cache.CacheSize, ptr->Cache.CacheSize / BYTES_IN_KB, ptr->Cache.CacheSize / BYTES_IN_MB); 140 | printf(" LineSize: %i\n", ptr->Cache.LineSize); 141 | if (ptr->Cache.Associativity == CACHE_FULLY_ASSOCIATIVE) 142 | { 143 | printf(" Fully Associative\n\n"); 144 | } 145 | else 146 | { 147 | printf(" Associativity: %i\n\n", ptr->Cache.Associativity); 148 | } 149 | printf(" Cache Processor Masks\n"); 150 | 151 | /* 152 | * Newer versions of Windows have added "GroupCount" here where it was 0 and reserved on older Versions, 153 | * Convert it to 1 if it is 0. 154 | */ 155 | GroupCount = *((WORD *)(&ptr->Cache.Reserved[18])); 156 | if (GroupCount == 0) 157 | { 158 | GroupCount = 1; 159 | } 160 | WinOs_DisplayGroupAffinity(GroupCount, &ptr->Cache.GroupMask); 161 | printf("+++++\n"); 162 | break; 163 | 164 | case RelationProcessorPackage: 165 | printf(" - Package: %i\n", NumberOfPackages); 166 | WinOs_DisplayGroupAffinity(ptr->Processor.GroupCount, &ptr->Processor.GroupMask[0]); 167 | NumberOfPackages++; 168 | printf("+++++\n"); 169 | break; 170 | default: 171 | /* 172 | * Silently ignore new enumerations that are unknown. 173 | */ 174 | break; 175 | } 176 | 177 | ptr = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)(((char *)ptr) + ptr->Size); 178 | CurrentSize = (DWORD)(((ULONG64)ptr) - ((ULONG64)pSystemLogicalProcInfoEx)); 179 | } 180 | } 181 | 182 | 183 | 184 | /* 185 | * WinOs_DisplayGroupAffinity 186 | * 187 | * Displays the Group Affinity. 188 | * 189 | * Arguments: 190 | * Number Of Groups, Group Affinity Array 191 | * 192 | * Return: 193 | * None 194 | */ 195 | void WinOs_DisplayGroupAffinity(WORD GroupCount, GROUP_AFFINITY *pGroupAffinityArray) 196 | { 197 | WORD Index; 198 | 199 | for(Index = 0; Index < GroupCount; Index++) 200 | { 201 | printf(" Group: %i, Affinity: 0x%016I64x\n", pGroupAffinityArray[Index].Group, pGroupAffinityArray[Index].Mask); 202 | } 203 | } 204 | 205 | 206 | /* 207 | * WinOs_GetCacheTypeString 208 | * 209 | * Returns a string describing the cache type. 210 | * 211 | * Arguments: 212 | * Cache Type 213 | * 214 | * Return: 215 | * Pointer from global string array of cache type 216 | */ 217 | unsigned char *WinOs_GetCacheTypeString(PROCESSOR_CACHE_TYPE CacheType) 218 | { 219 | if ((CacheType >= CacheUnified && CacheType <= CacheTrace) == 0) 220 | { 221 | CacheType = CACHE_INVALID_INDEX; 222 | } 223 | 224 | return g_pszCacheTypeString[CacheType]; 225 | } 226 | 227 | 228 | /* 229 | * Os_Platform_Read_Cpuid 230 | * 231 | * OS/Compiler Specific intrinsic of reading CPUID. 232 | * 233 | * Arguments: 234 | * Leaf, Subleaf, CPUID Data Structure 235 | * 236 | * Return: 237 | * None 238 | */ 239 | void Os_Platform_Read_Cpuid(unsigned int Leaf, unsigned int Subleaf, PCPUID_REGISTERS pCpuidRegisters) 240 | { 241 | __cpuidex(&pCpuidRegisters->x.Registers[0], Leaf, Subleaf); 242 | } 243 | 244 | /* 245 | * Os_GetNumberOfProcessors 246 | * 247 | * Get the number of processors 248 | * 249 | * Arguments: 250 | * None 251 | * 252 | * Return: 253 | * Number of processors 254 | */ 255 | unsigned int Os_GetNumberOfProcessors(void) 256 | { 257 | unsigned int NumberOfProcessors; 258 | 259 | NumberOfProcessors = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS); 260 | 261 | return NumberOfProcessors; 262 | } 263 | 264 | 265 | /* 266 | * Os_SetAffinity 267 | * 268 | * Set the current thread affinity 269 | * 270 | * Arguments: 271 | * Processor Number 272 | * 273 | * Return: 274 | * None 275 | */ 276 | void Os_SetAffinity(unsigned int ProcessorNumber) 277 | { 278 | GROUP_AFFINITY GroupAffinity; 279 | unsigned short GroupIndex; 280 | unsigned short MaxGroups; 281 | unsigned int NumberOfGroupProcessors; 282 | 283 | /* 284 | * Assume the active groups are going to be contiguous. 285 | */ 286 | MaxGroups = GetActiveProcessorGroupCount(); 287 | 288 | for (GroupIndex = 0; GroupIndex < MaxGroups; GroupIndex++) 289 | { 290 | NumberOfGroupProcessors = GetActiveProcessorCount(GroupIndex); 291 | 292 | if (ProcessorNumber < NumberOfGroupProcessors) 293 | { 294 | memset(&GroupAffinity, 0, sizeof(GroupAffinity)); 295 | GroupAffinity.Group = GroupIndex; 296 | GroupAffinity.Mask = (KAFFINITY)((ULONG64)1<<(ULONG64)ProcessorNumber); 297 | SetThreadGroupAffinity(GetCurrentThread(), &GroupAffinity, NULL); 298 | break; 299 | } 300 | else 301 | { 302 | ProcessorNumber = ProcessorNumber - NumberOfGroupProcessors; 303 | } 304 | } 305 | } 306 | 307 | 308 | --------------------------------------------------------------------------------