162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#define _GNU_SOURCE 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <stdio.h> 562306a36Sopenharmony_ci#include <stdbool.h> 662306a36Sopenharmony_ci#include <stdlib.h> 762306a36Sopenharmony_ci#include <string.h> 862306a36Sopenharmony_ci#include <getopt.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_citypedef unsigned int u32; 1362306a36Sopenharmony_citypedef unsigned long long u64; 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cichar *def_csv = "/usr/share/misc/cpuid.csv"; 1662306a36Sopenharmony_cichar *user_csv; 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* Cover both single-bit flag and multiple-bits fields */ 2062306a36Sopenharmony_cistruct bits_desc { 2162306a36Sopenharmony_ci /* start and end bits */ 2262306a36Sopenharmony_ci int start, end; 2362306a36Sopenharmony_ci /* 0 or 1 for 1-bit flag */ 2462306a36Sopenharmony_ci int value; 2562306a36Sopenharmony_ci char simp[32]; 2662306a36Sopenharmony_ci char detail[256]; 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* descriptor info for eax/ebx/ecx/edx */ 3062306a36Sopenharmony_cistruct reg_desc { 3162306a36Sopenharmony_ci /* number of valid entries */ 3262306a36Sopenharmony_ci int nr; 3362306a36Sopenharmony_ci struct bits_desc descs[32]; 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cienum cpuid_reg { 3762306a36Sopenharmony_ci R_EAX = 0, 3862306a36Sopenharmony_ci R_EBX, 3962306a36Sopenharmony_ci R_ECX, 4062306a36Sopenharmony_ci R_EDX, 4162306a36Sopenharmony_ci NR_REGS 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic const char * const reg_names[] = { 4562306a36Sopenharmony_ci "EAX", "EBX", "ECX", "EDX", 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistruct subleaf { 4962306a36Sopenharmony_ci u32 index; 5062306a36Sopenharmony_ci u32 sub; 5162306a36Sopenharmony_ci u32 eax, ebx, ecx, edx; 5262306a36Sopenharmony_ci struct reg_desc info[NR_REGS]; 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* Represent one leaf (basic or extended) */ 5662306a36Sopenharmony_cistruct cpuid_func { 5762306a36Sopenharmony_ci /* 5862306a36Sopenharmony_ci * Array of subleafs for this func, if there is no subleafs 5962306a36Sopenharmony_ci * then the leafs[0] is the main leaf 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ci struct subleaf *leafs; 6262306a36Sopenharmony_ci int nr; 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistruct cpuid_range { 6662306a36Sopenharmony_ci /* array of main leafs */ 6762306a36Sopenharmony_ci struct cpuid_func *funcs; 6862306a36Sopenharmony_ci /* number of valid leafs */ 6962306a36Sopenharmony_ci int nr; 7062306a36Sopenharmony_ci bool is_ext; 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* 7462306a36Sopenharmony_ci * basic: basic functions range: [0... ] 7562306a36Sopenharmony_ci * ext: extended functions range: [0x80000000... ] 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_cistruct cpuid_range *leafs_basic, *leafs_ext; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic int num_leafs; 8062306a36Sopenharmony_cistatic bool is_amd; 8162306a36Sopenharmony_cistatic bool show_details; 8262306a36Sopenharmony_cistatic bool show_raw; 8362306a36Sopenharmony_cistatic bool show_flags_only = true; 8462306a36Sopenharmony_cistatic u32 user_index = 0xFFFFFFFF; 8562306a36Sopenharmony_cistatic u32 user_sub = 0xFFFFFFFF; 8662306a36Sopenharmony_cistatic int flines; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic inline void cpuid(u32 *eax, u32 *ebx, u32 *ecx, u32 *edx) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci /* ecx is often an input as well as an output. */ 9162306a36Sopenharmony_ci asm volatile("cpuid" 9262306a36Sopenharmony_ci : "=a" (*eax), 9362306a36Sopenharmony_ci "=b" (*ebx), 9462306a36Sopenharmony_ci "=c" (*ecx), 9562306a36Sopenharmony_ci "=d" (*edx) 9662306a36Sopenharmony_ci : "0" (*eax), "2" (*ecx)); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic inline bool has_subleafs(u32 f) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci if (f == 0x7 || f == 0xd) 10262306a36Sopenharmony_ci return true; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (is_amd) { 10562306a36Sopenharmony_ci if (f == 0x8000001d) 10662306a36Sopenharmony_ci return true; 10762306a36Sopenharmony_ci return false; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci switch (f) { 11162306a36Sopenharmony_ci case 0x4: 11262306a36Sopenharmony_ci case 0xb: 11362306a36Sopenharmony_ci case 0xf: 11462306a36Sopenharmony_ci case 0x10: 11562306a36Sopenharmony_ci case 0x14: 11662306a36Sopenharmony_ci case 0x18: 11762306a36Sopenharmony_ci case 0x1f: 11862306a36Sopenharmony_ci return true; 11962306a36Sopenharmony_ci default: 12062306a36Sopenharmony_ci return false; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic void leaf_print_raw(struct subleaf *leaf) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci if (has_subleafs(leaf->index)) { 12762306a36Sopenharmony_ci if (leaf->sub == 0) 12862306a36Sopenharmony_ci printf("0x%08x: subleafs:\n", leaf->index); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci printf(" %2d: EAX=0x%08x, EBX=0x%08x, ECX=0x%08x, EDX=0x%08x\n", 13162306a36Sopenharmony_ci leaf->sub, leaf->eax, leaf->ebx, leaf->ecx, leaf->edx); 13262306a36Sopenharmony_ci } else { 13362306a36Sopenharmony_ci printf("0x%08x: EAX=0x%08x, EBX=0x%08x, ECX=0x%08x, EDX=0x%08x\n", 13462306a36Sopenharmony_ci leaf->index, leaf->eax, leaf->ebx, leaf->ecx, leaf->edx); 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci/* Return true is the input eax/ebx/ecx/edx are all zero */ 13962306a36Sopenharmony_cistatic bool cpuid_store(struct cpuid_range *range, u32 f, int subleaf, 14062306a36Sopenharmony_ci u32 a, u32 b, u32 c, u32 d) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct cpuid_func *func; 14362306a36Sopenharmony_ci struct subleaf *leaf; 14462306a36Sopenharmony_ci int s = 0; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (a == 0 && b == 0 && c == 0 && d == 0) 14762306a36Sopenharmony_ci return true; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* 15062306a36Sopenharmony_ci * Cut off vendor-prefix from CPUID function as we're using it as an 15162306a36Sopenharmony_ci * index into ->funcs. 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_ci func = &range->funcs[f & 0xffff]; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (!func->leafs) { 15662306a36Sopenharmony_ci func->leafs = malloc(sizeof(struct subleaf)); 15762306a36Sopenharmony_ci if (!func->leafs) 15862306a36Sopenharmony_ci perror("malloc func leaf"); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci func->nr = 1; 16162306a36Sopenharmony_ci } else { 16262306a36Sopenharmony_ci s = func->nr; 16362306a36Sopenharmony_ci func->leafs = realloc(func->leafs, (s + 1) * sizeof(*leaf)); 16462306a36Sopenharmony_ci if (!func->leafs) 16562306a36Sopenharmony_ci perror("realloc f->leafs"); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci func->nr++; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci leaf = &func->leafs[s]; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci leaf->index = f; 17362306a36Sopenharmony_ci leaf->sub = subleaf; 17462306a36Sopenharmony_ci leaf->eax = a; 17562306a36Sopenharmony_ci leaf->ebx = b; 17662306a36Sopenharmony_ci leaf->ecx = c; 17762306a36Sopenharmony_ci leaf->edx = d; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return false; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic void raw_dump_range(struct cpuid_range *range) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci u32 f; 18562306a36Sopenharmony_ci int i; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci printf("%s Leafs :\n", range->is_ext ? "Extended" : "Basic"); 18862306a36Sopenharmony_ci printf("================\n"); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci for (f = 0; (int)f < range->nr; f++) { 19162306a36Sopenharmony_ci struct cpuid_func *func = &range->funcs[f]; 19262306a36Sopenharmony_ci u32 index = f; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (range->is_ext) 19562306a36Sopenharmony_ci index += 0x80000000; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* Skip leaf without valid items */ 19862306a36Sopenharmony_ci if (!func->nr) 19962306a36Sopenharmony_ci continue; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* First item is the main leaf, followed by all subleafs */ 20262306a36Sopenharmony_ci for (i = 0; i < func->nr; i++) 20362306a36Sopenharmony_ci leaf_print_raw(&func->leafs[i]); 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci#define MAX_SUBLEAF_NUM 32 20862306a36Sopenharmony_cistruct cpuid_range *setup_cpuid_range(u32 input_eax) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci u32 max_func, idx_func; 21162306a36Sopenharmony_ci int subleaf; 21262306a36Sopenharmony_ci struct cpuid_range *range; 21362306a36Sopenharmony_ci u32 eax, ebx, ecx, edx; 21462306a36Sopenharmony_ci u32 f = input_eax; 21562306a36Sopenharmony_ci int max_subleaf; 21662306a36Sopenharmony_ci bool allzero; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci eax = input_eax; 21962306a36Sopenharmony_ci ebx = ecx = edx = 0; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci cpuid(&eax, &ebx, &ecx, &edx); 22262306a36Sopenharmony_ci max_func = eax; 22362306a36Sopenharmony_ci idx_func = (max_func & 0xffff) + 1; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci range = malloc(sizeof(struct cpuid_range)); 22662306a36Sopenharmony_ci if (!range) 22762306a36Sopenharmony_ci perror("malloc range"); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (input_eax & 0x80000000) 23062306a36Sopenharmony_ci range->is_ext = true; 23162306a36Sopenharmony_ci else 23262306a36Sopenharmony_ci range->is_ext = false; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci range->funcs = malloc(sizeof(struct cpuid_func) * idx_func); 23562306a36Sopenharmony_ci if (!range->funcs) 23662306a36Sopenharmony_ci perror("malloc range->funcs"); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci range->nr = idx_func; 23962306a36Sopenharmony_ci memset(range->funcs, 0, sizeof(struct cpuid_func) * idx_func); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci for (; f <= max_func; f++) { 24262306a36Sopenharmony_ci eax = f; 24362306a36Sopenharmony_ci subleaf = ecx = 0; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci cpuid(&eax, &ebx, &ecx, &edx); 24662306a36Sopenharmony_ci allzero = cpuid_store(range, f, subleaf, eax, ebx, ecx, edx); 24762306a36Sopenharmony_ci if (allzero) 24862306a36Sopenharmony_ci continue; 24962306a36Sopenharmony_ci num_leafs++; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (!has_subleafs(f)) 25262306a36Sopenharmony_ci continue; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci max_subleaf = MAX_SUBLEAF_NUM; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* 25762306a36Sopenharmony_ci * Some can provide the exact number of subleafs, 25862306a36Sopenharmony_ci * others have to be tried (0xf) 25962306a36Sopenharmony_ci */ 26062306a36Sopenharmony_ci if (f == 0x7 || f == 0x14 || f == 0x17 || f == 0x18) 26162306a36Sopenharmony_ci max_subleaf = (eax & 0xff) + 1; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (f == 0xb) 26462306a36Sopenharmony_ci max_subleaf = 2; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci for (subleaf = 1; subleaf < max_subleaf; subleaf++) { 26762306a36Sopenharmony_ci eax = f; 26862306a36Sopenharmony_ci ecx = subleaf; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci cpuid(&eax, &ebx, &ecx, &edx); 27162306a36Sopenharmony_ci allzero = cpuid_store(range, f, subleaf, 27262306a36Sopenharmony_ci eax, ebx, ecx, edx); 27362306a36Sopenharmony_ci if (allzero) 27462306a36Sopenharmony_ci continue; 27562306a36Sopenharmony_ci num_leafs++; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci return range; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci/* 28462306a36Sopenharmony_ci * The basic row format for cpuid.csv is 28562306a36Sopenharmony_ci * LEAF,SUBLEAF,register_name,bits,short name,long description 28662306a36Sopenharmony_ci * 28762306a36Sopenharmony_ci * like: 28862306a36Sopenharmony_ci * 0, 0, EAX, 31:0, max_basic_leafs, Max input value for supported subleafs 28962306a36Sopenharmony_ci * 1, 0, ECX, 0, sse3, Streaming SIMD Extensions 3(SSE3) 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_cistatic int parse_line(char *line) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci char *str; 29462306a36Sopenharmony_ci int i; 29562306a36Sopenharmony_ci struct cpuid_range *range; 29662306a36Sopenharmony_ci struct cpuid_func *func; 29762306a36Sopenharmony_ci struct subleaf *leaf; 29862306a36Sopenharmony_ci u32 index; 29962306a36Sopenharmony_ci u32 sub; 30062306a36Sopenharmony_ci char buffer[512]; 30162306a36Sopenharmony_ci char *buf; 30262306a36Sopenharmony_ci /* 30362306a36Sopenharmony_ci * Tokens: 30462306a36Sopenharmony_ci * 1. leaf 30562306a36Sopenharmony_ci * 2. subleaf 30662306a36Sopenharmony_ci * 3. register 30762306a36Sopenharmony_ci * 4. bits 30862306a36Sopenharmony_ci * 5. short name 30962306a36Sopenharmony_ci * 6. long detail 31062306a36Sopenharmony_ci */ 31162306a36Sopenharmony_ci char *tokens[6]; 31262306a36Sopenharmony_ci struct reg_desc *reg; 31362306a36Sopenharmony_ci struct bits_desc *bdesc; 31462306a36Sopenharmony_ci int reg_index; 31562306a36Sopenharmony_ci char *start, *end; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* Skip comments and NULL line */ 31862306a36Sopenharmony_ci if (line[0] == '#' || line[0] == '\n') 31962306a36Sopenharmony_ci return 0; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci strncpy(buffer, line, 511); 32262306a36Sopenharmony_ci buffer[511] = 0; 32362306a36Sopenharmony_ci str = buffer; 32462306a36Sopenharmony_ci for (i = 0; i < 5; i++) { 32562306a36Sopenharmony_ci tokens[i] = strtok(str, ","); 32662306a36Sopenharmony_ci if (!tokens[i]) 32762306a36Sopenharmony_ci goto err_exit; 32862306a36Sopenharmony_ci str = NULL; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci tokens[5] = strtok(str, "\n"); 33162306a36Sopenharmony_ci if (!tokens[5]) 33262306a36Sopenharmony_ci goto err_exit; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* index/main-leaf */ 33562306a36Sopenharmony_ci index = strtoull(tokens[0], NULL, 0); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (index & 0x80000000) 33862306a36Sopenharmony_ci range = leafs_ext; 33962306a36Sopenharmony_ci else 34062306a36Sopenharmony_ci range = leafs_basic; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci index &= 0x7FFFFFFF; 34362306a36Sopenharmony_ci /* Skip line parsing for non-existing indexes */ 34462306a36Sopenharmony_ci if ((int)index >= range->nr) 34562306a36Sopenharmony_ci return -1; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci func = &range->funcs[index]; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* Return if the index has no valid item on this platform */ 35062306a36Sopenharmony_ci if (!func->nr) 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* subleaf */ 35462306a36Sopenharmony_ci sub = strtoul(tokens[1], NULL, 0); 35562306a36Sopenharmony_ci if ((int)sub > func->nr) 35662306a36Sopenharmony_ci return -1; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci leaf = &func->leafs[sub]; 35962306a36Sopenharmony_ci buf = tokens[2]; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (strcasestr(buf, "EAX")) 36262306a36Sopenharmony_ci reg_index = R_EAX; 36362306a36Sopenharmony_ci else if (strcasestr(buf, "EBX")) 36462306a36Sopenharmony_ci reg_index = R_EBX; 36562306a36Sopenharmony_ci else if (strcasestr(buf, "ECX")) 36662306a36Sopenharmony_ci reg_index = R_ECX; 36762306a36Sopenharmony_ci else if (strcasestr(buf, "EDX")) 36862306a36Sopenharmony_ci reg_index = R_EDX; 36962306a36Sopenharmony_ci else 37062306a36Sopenharmony_ci goto err_exit; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci reg = &leaf->info[reg_index]; 37362306a36Sopenharmony_ci bdesc = ®->descs[reg->nr++]; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* bit flag or bits field */ 37662306a36Sopenharmony_ci buf = tokens[3]; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci end = strtok(buf, ":"); 37962306a36Sopenharmony_ci bdesc->end = strtoul(end, NULL, 0); 38062306a36Sopenharmony_ci bdesc->start = bdesc->end; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci /* start != NULL means it is bit fields */ 38362306a36Sopenharmony_ci start = strtok(NULL, ":"); 38462306a36Sopenharmony_ci if (start) 38562306a36Sopenharmony_ci bdesc->start = strtoul(start, NULL, 0); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci strcpy(bdesc->simp, tokens[4]); 38862306a36Sopenharmony_ci strcpy(bdesc->detail, tokens[5]); 38962306a36Sopenharmony_ci return 0; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cierr_exit: 39262306a36Sopenharmony_ci printf("Warning: wrong line format:\n"); 39362306a36Sopenharmony_ci printf("\tline[%d]: %s\n", flines, line); 39462306a36Sopenharmony_ci return -1; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci/* Parse csv file, and construct the array of all leafs and subleafs */ 39862306a36Sopenharmony_cistatic void parse_text(void) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci FILE *file; 40162306a36Sopenharmony_ci char *filename, *line = NULL; 40262306a36Sopenharmony_ci size_t len = 0; 40362306a36Sopenharmony_ci int ret; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (show_raw) 40662306a36Sopenharmony_ci return; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci filename = user_csv ? user_csv : def_csv; 40962306a36Sopenharmony_ci file = fopen(filename, "r"); 41062306a36Sopenharmony_ci if (!file) { 41162306a36Sopenharmony_ci /* Fallback to a csv in the same dir */ 41262306a36Sopenharmony_ci file = fopen("./cpuid.csv", "r"); 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (!file) { 41662306a36Sopenharmony_ci printf("Fail to open '%s'\n", filename); 41762306a36Sopenharmony_ci return; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci while (1) { 42162306a36Sopenharmony_ci ret = getline(&line, &len, file); 42262306a36Sopenharmony_ci flines++; 42362306a36Sopenharmony_ci if (ret > 0) 42462306a36Sopenharmony_ci parse_line(line); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (feof(file)) 42762306a36Sopenharmony_ci break; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci fclose(file); 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci/* Decode every eax/ebx/ecx/edx */ 43562306a36Sopenharmony_cistatic void decode_bits(u32 value, struct reg_desc *rdesc, enum cpuid_reg reg) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci struct bits_desc *bdesc; 43862306a36Sopenharmony_ci int start, end, i; 43962306a36Sopenharmony_ci u32 mask; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (!rdesc->nr) { 44262306a36Sopenharmony_ci if (show_details) 44362306a36Sopenharmony_ci printf("\t %s: 0x%08x\n", reg_names[reg], value); 44462306a36Sopenharmony_ci return; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci for (i = 0; i < rdesc->nr; i++) { 44862306a36Sopenharmony_ci bdesc = &rdesc->descs[i]; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci start = bdesc->start; 45162306a36Sopenharmony_ci end = bdesc->end; 45262306a36Sopenharmony_ci if (start == end) { 45362306a36Sopenharmony_ci /* single bit flag */ 45462306a36Sopenharmony_ci if (value & (1 << start)) 45562306a36Sopenharmony_ci printf("\t%-20s %s%s\n", 45662306a36Sopenharmony_ci bdesc->simp, 45762306a36Sopenharmony_ci show_details ? "-" : "", 45862306a36Sopenharmony_ci show_details ? bdesc->detail : "" 45962306a36Sopenharmony_ci ); 46062306a36Sopenharmony_ci } else { 46162306a36Sopenharmony_ci /* bit fields */ 46262306a36Sopenharmony_ci if (show_flags_only) 46362306a36Sopenharmony_ci continue; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci mask = ((u64)1 << (end - start + 1)) - 1; 46662306a36Sopenharmony_ci printf("\t%-20s\t: 0x%-8x\t%s%s\n", 46762306a36Sopenharmony_ci bdesc->simp, 46862306a36Sopenharmony_ci (value >> start) & mask, 46962306a36Sopenharmony_ci show_details ? "-" : "", 47062306a36Sopenharmony_ci show_details ? bdesc->detail : "" 47162306a36Sopenharmony_ci ); 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic void show_leaf(struct subleaf *leaf) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci if (!leaf) 47962306a36Sopenharmony_ci return; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (show_raw) { 48262306a36Sopenharmony_ci leaf_print_raw(leaf); 48362306a36Sopenharmony_ci } else { 48462306a36Sopenharmony_ci if (show_details) 48562306a36Sopenharmony_ci printf("CPUID_0x%x_ECX[0x%x]:\n", 48662306a36Sopenharmony_ci leaf->index, leaf->sub); 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci decode_bits(leaf->eax, &leaf->info[R_EAX], R_EAX); 49062306a36Sopenharmony_ci decode_bits(leaf->ebx, &leaf->info[R_EBX], R_EBX); 49162306a36Sopenharmony_ci decode_bits(leaf->ecx, &leaf->info[R_ECX], R_ECX); 49262306a36Sopenharmony_ci decode_bits(leaf->edx, &leaf->info[R_EDX], R_EDX); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (!show_raw && show_details) 49562306a36Sopenharmony_ci printf("\n"); 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistatic void show_func(struct cpuid_func *func) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci int i; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (!func) 50362306a36Sopenharmony_ci return; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci for (i = 0; i < func->nr; i++) 50662306a36Sopenharmony_ci show_leaf(&func->leafs[i]); 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic void show_range(struct cpuid_range *range) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci int i; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci for (i = 0; i < range->nr; i++) 51462306a36Sopenharmony_ci show_func(&range->funcs[i]); 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic inline struct cpuid_func *index_to_func(u32 index) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci struct cpuid_range *range; 52062306a36Sopenharmony_ci u32 func_idx; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci range = (index & 0x80000000) ? leafs_ext : leafs_basic; 52362306a36Sopenharmony_ci func_idx = index & 0xffff; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if ((func_idx + 1) > (u32)range->nr) { 52662306a36Sopenharmony_ci printf("ERR: invalid input index (0x%x)\n", index); 52762306a36Sopenharmony_ci return NULL; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci return &range->funcs[func_idx]; 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic void show_info(void) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci struct cpuid_func *func; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (show_raw) { 53762306a36Sopenharmony_ci /* Show all of the raw output of 'cpuid' instr */ 53862306a36Sopenharmony_ci raw_dump_range(leafs_basic); 53962306a36Sopenharmony_ci raw_dump_range(leafs_ext); 54062306a36Sopenharmony_ci return; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (user_index != 0xFFFFFFFF) { 54462306a36Sopenharmony_ci /* Only show specific leaf/subleaf info */ 54562306a36Sopenharmony_ci func = index_to_func(user_index); 54662306a36Sopenharmony_ci if (!func) 54762306a36Sopenharmony_ci return; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* Dump the raw data also */ 55062306a36Sopenharmony_ci show_raw = true; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (user_sub != 0xFFFFFFFF) { 55362306a36Sopenharmony_ci if (user_sub + 1 <= (u32)func->nr) { 55462306a36Sopenharmony_ci show_leaf(&func->leafs[user_sub]); 55562306a36Sopenharmony_ci return; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci printf("ERR: invalid input subleaf (0x%x)\n", user_sub); 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci show_func(func); 56262306a36Sopenharmony_ci return; 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci printf("CPU features:\n=============\n\n"); 56662306a36Sopenharmony_ci show_range(leafs_basic); 56762306a36Sopenharmony_ci show_range(leafs_ext); 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_cistatic void setup_platform_cpuid(void) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci u32 eax, ebx, ecx, edx; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* Check vendor */ 57562306a36Sopenharmony_ci eax = ebx = ecx = edx = 0; 57662306a36Sopenharmony_ci cpuid(&eax, &ebx, &ecx, &edx); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci /* "htuA" */ 57962306a36Sopenharmony_ci if (ebx == 0x68747541) 58062306a36Sopenharmony_ci is_amd = true; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci /* Setup leafs for the basic and extended range */ 58362306a36Sopenharmony_ci leafs_basic = setup_cpuid_range(0x0); 58462306a36Sopenharmony_ci leafs_ext = setup_cpuid_range(0x80000000); 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cistatic void usage(void) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci printf("kcpuid [-abdfhr] [-l leaf] [-s subleaf]\n" 59062306a36Sopenharmony_ci "\t-a|--all Show both bit flags and complex bit fields info\n" 59162306a36Sopenharmony_ci "\t-b|--bitflags Show boolean flags only\n" 59262306a36Sopenharmony_ci "\t-d|--detail Show details of the flag/fields (default)\n" 59362306a36Sopenharmony_ci "\t-f|--flags Specify the cpuid csv file\n" 59462306a36Sopenharmony_ci "\t-h|--help Show usage info\n" 59562306a36Sopenharmony_ci "\t-l|--leaf=index Specify the leaf you want to check\n" 59662306a36Sopenharmony_ci "\t-r|--raw Show raw cpuid data\n" 59762306a36Sopenharmony_ci "\t-s|--subleaf=sub Specify the subleaf you want to check\n" 59862306a36Sopenharmony_ci ); 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic struct option opts[] = { 60262306a36Sopenharmony_ci { "all", no_argument, NULL, 'a' }, /* show both bit flags and fields */ 60362306a36Sopenharmony_ci { "bitflags", no_argument, NULL, 'b' }, /* only show bit flags, default on */ 60462306a36Sopenharmony_ci { "detail", no_argument, NULL, 'd' }, /* show detail descriptions */ 60562306a36Sopenharmony_ci { "file", required_argument, NULL, 'f' }, /* use user's cpuid file */ 60662306a36Sopenharmony_ci { "help", no_argument, NULL, 'h'}, /* show usage */ 60762306a36Sopenharmony_ci { "leaf", required_argument, NULL, 'l'}, /* only check a specific leaf */ 60862306a36Sopenharmony_ci { "raw", no_argument, NULL, 'r'}, /* show raw CPUID leaf data */ 60962306a36Sopenharmony_ci { "subleaf", required_argument, NULL, 's'}, /* check a specific subleaf */ 61062306a36Sopenharmony_ci { NULL, 0, NULL, 0 } 61162306a36Sopenharmony_ci}; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic int parse_options(int argc, char *argv[]) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci int c; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci while ((c = getopt_long(argc, argv, "abdf:hl:rs:", 61862306a36Sopenharmony_ci opts, NULL)) != -1) 61962306a36Sopenharmony_ci switch (c) { 62062306a36Sopenharmony_ci case 'a': 62162306a36Sopenharmony_ci show_flags_only = false; 62262306a36Sopenharmony_ci break; 62362306a36Sopenharmony_ci case 'b': 62462306a36Sopenharmony_ci show_flags_only = true; 62562306a36Sopenharmony_ci break; 62662306a36Sopenharmony_ci case 'd': 62762306a36Sopenharmony_ci show_details = true; 62862306a36Sopenharmony_ci break; 62962306a36Sopenharmony_ci case 'f': 63062306a36Sopenharmony_ci user_csv = optarg; 63162306a36Sopenharmony_ci break; 63262306a36Sopenharmony_ci case 'h': 63362306a36Sopenharmony_ci usage(); 63462306a36Sopenharmony_ci exit(1); 63562306a36Sopenharmony_ci break; 63662306a36Sopenharmony_ci case 'l': 63762306a36Sopenharmony_ci /* main leaf */ 63862306a36Sopenharmony_ci user_index = strtoul(optarg, NULL, 0); 63962306a36Sopenharmony_ci break; 64062306a36Sopenharmony_ci case 'r': 64162306a36Sopenharmony_ci show_raw = true; 64262306a36Sopenharmony_ci break; 64362306a36Sopenharmony_ci case 's': 64462306a36Sopenharmony_ci /* subleaf */ 64562306a36Sopenharmony_ci user_sub = strtoul(optarg, NULL, 0); 64662306a36Sopenharmony_ci break; 64762306a36Sopenharmony_ci default: 64862306a36Sopenharmony_ci printf("%s: Invalid option '%c'\n", argv[0], optopt); 64962306a36Sopenharmony_ci return -1; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci return 0; 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci/* 65662306a36Sopenharmony_ci * Do 4 things in turn: 65762306a36Sopenharmony_ci * 1. Parse user options 65862306a36Sopenharmony_ci * 2. Parse and store all the CPUID leaf data supported on this platform 65962306a36Sopenharmony_ci * 2. Parse the csv file, while skipping leafs which are not available 66062306a36Sopenharmony_ci * on this platform 66162306a36Sopenharmony_ci * 3. Print leafs info based on user options 66262306a36Sopenharmony_ci */ 66362306a36Sopenharmony_ciint main(int argc, char *argv[]) 66462306a36Sopenharmony_ci{ 66562306a36Sopenharmony_ci if (parse_options(argc, argv)) 66662306a36Sopenharmony_ci return -1; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* Setup the cpuid leafs of current platform */ 66962306a36Sopenharmony_ci setup_platform_cpuid(); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci /* Read and parse the 'cpuid.csv' */ 67262306a36Sopenharmony_ci parse_text(); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci show_info(); 67562306a36Sopenharmony_ci return 0; 67662306a36Sopenharmony_ci} 677