PageRenderTime 53ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/tools/arch/x86/kcpuid/kcpuid.c

https://github.com/kvaneesh/linux
C | 657 lines | 469 code | 112 blank | 76 comment | 96 complexity | 0559313165eea29ab89df2cb61341c33 MD5 | raw file
  1. // SPDX-License-Identifier: GPL-2.0
  2. #define _GNU_SOURCE
  3. #include <stdio.h>
  4. #include <stdbool.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <getopt.h>
  8. #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
  9. typedef unsigned int u32;
  10. typedef unsigned long long u64;
  11. char *def_csv = "/usr/share/misc/cpuid.csv";
  12. char *user_csv;
  13. /* Cover both single-bit flag and multiple-bits fields */
  14. struct bits_desc {
  15. /* start and end bits */
  16. int start, end;
  17. /* 0 or 1 for 1-bit flag */
  18. int value;
  19. char simp[32];
  20. char detail[256];
  21. };
  22. /* descriptor info for eax/ebx/ecx/edx */
  23. struct reg_desc {
  24. /* number of valid entries */
  25. int nr;
  26. struct bits_desc descs[32];
  27. };
  28. enum {
  29. R_EAX = 0,
  30. R_EBX,
  31. R_ECX,
  32. R_EDX,
  33. NR_REGS
  34. };
  35. struct subleaf {
  36. u32 index;
  37. u32 sub;
  38. u32 eax, ebx, ecx, edx;
  39. struct reg_desc info[NR_REGS];
  40. };
  41. /* Represent one leaf (basic or extended) */
  42. struct cpuid_func {
  43. /*
  44. * Array of subleafs for this func, if there is no subleafs
  45. * then the leafs[0] is the main leaf
  46. */
  47. struct subleaf *leafs;
  48. int nr;
  49. };
  50. struct cpuid_range {
  51. /* array of main leafs */
  52. struct cpuid_func *funcs;
  53. /* number of valid leafs */
  54. int nr;
  55. bool is_ext;
  56. };
  57. /*
  58. * basic: basic functions range: [0... ]
  59. * ext: extended functions range: [0x80000000... ]
  60. */
  61. struct cpuid_range *leafs_basic, *leafs_ext;
  62. static int num_leafs;
  63. static bool is_amd;
  64. static bool show_details;
  65. static bool show_raw;
  66. static bool show_flags_only = true;
  67. static u32 user_index = 0xFFFFFFFF;
  68. static u32 user_sub = 0xFFFFFFFF;
  69. static int flines;
  70. static inline void cpuid(u32 *eax, u32 *ebx, u32 *ecx, u32 *edx)
  71. {
  72. /* ecx is often an input as well as an output. */
  73. asm volatile("cpuid"
  74. : "=a" (*eax),
  75. "=b" (*ebx),
  76. "=c" (*ecx),
  77. "=d" (*edx)
  78. : "0" (*eax), "2" (*ecx));
  79. }
  80. static inline bool has_subleafs(u32 f)
  81. {
  82. if (f == 0x7 || f == 0xd)
  83. return true;
  84. if (is_amd) {
  85. if (f == 0x8000001d)
  86. return true;
  87. return false;
  88. }
  89. switch (f) {
  90. case 0x4:
  91. case 0xb:
  92. case 0xf:
  93. case 0x10:
  94. case 0x14:
  95. case 0x18:
  96. case 0x1f:
  97. return true;
  98. default:
  99. return false;
  100. }
  101. }
  102. static void leaf_print_raw(struct subleaf *leaf)
  103. {
  104. if (has_subleafs(leaf->index)) {
  105. if (leaf->sub == 0)
  106. printf("0x%08x: subleafs:\n", leaf->index);
  107. printf(" %2d: EAX=0x%08x, EBX=0x%08x, ECX=0x%08x, EDX=0x%08x\n",
  108. leaf->sub, leaf->eax, leaf->ebx, leaf->ecx, leaf->edx);
  109. } else {
  110. printf("0x%08x: EAX=0x%08x, EBX=0x%08x, ECX=0x%08x, EDX=0x%08x\n",
  111. leaf->index, leaf->eax, leaf->ebx, leaf->ecx, leaf->edx);
  112. }
  113. }
  114. /* Return true is the input eax/ebx/ecx/edx are all zero */
  115. static bool cpuid_store(struct cpuid_range *range, u32 f, int subleaf,
  116. u32 a, u32 b, u32 c, u32 d)
  117. {
  118. struct cpuid_func *func;
  119. struct subleaf *leaf;
  120. int s = 0;
  121. if (a == 0 && b == 0 && c == 0 && d == 0)
  122. return true;
  123. /*
  124. * Cut off vendor-prefix from CPUID function as we're using it as an
  125. * index into ->funcs.
  126. */
  127. func = &range->funcs[f & 0xffff];
  128. if (!func->leafs) {
  129. func->leafs = malloc(sizeof(struct subleaf));
  130. if (!func->leafs)
  131. perror("malloc func leaf");
  132. func->nr = 1;
  133. } else {
  134. s = func->nr;
  135. func->leafs = realloc(func->leafs, (s + 1) * sizeof(*leaf));
  136. if (!func->leafs)
  137. perror("realloc f->leafs");
  138. func->nr++;
  139. }
  140. leaf = &func->leafs[s];
  141. leaf->index = f;
  142. leaf->sub = subleaf;
  143. leaf->eax = a;
  144. leaf->ebx = b;
  145. leaf->ecx = c;
  146. leaf->edx = d;
  147. return false;
  148. }
  149. static void raw_dump_range(struct cpuid_range *range)
  150. {
  151. u32 f;
  152. int i;
  153. printf("%s Leafs :\n", range->is_ext ? "Extended" : "Basic");
  154. printf("================\n");
  155. for (f = 0; (int)f < range->nr; f++) {
  156. struct cpuid_func *func = &range->funcs[f];
  157. u32 index = f;
  158. if (range->is_ext)
  159. index += 0x80000000;
  160. /* Skip leaf without valid items */
  161. if (!func->nr)
  162. continue;
  163. /* First item is the main leaf, followed by all subleafs */
  164. for (i = 0; i < func->nr; i++)
  165. leaf_print_raw(&func->leafs[i]);
  166. }
  167. }
  168. #define MAX_SUBLEAF_NUM 32
  169. struct cpuid_range *setup_cpuid_range(u32 input_eax)
  170. {
  171. u32 max_func, idx_func;
  172. int subleaf;
  173. struct cpuid_range *range;
  174. u32 eax, ebx, ecx, edx;
  175. u32 f = input_eax;
  176. int max_subleaf;
  177. bool allzero;
  178. eax = input_eax;
  179. ebx = ecx = edx = 0;
  180. cpuid(&eax, &ebx, &ecx, &edx);
  181. max_func = eax;
  182. idx_func = (max_func & 0xffff) + 1;
  183. range = malloc(sizeof(struct cpuid_range));
  184. if (!range)
  185. perror("malloc range");
  186. if (input_eax & 0x80000000)
  187. range->is_ext = true;
  188. else
  189. range->is_ext = false;
  190. range->funcs = malloc(sizeof(struct cpuid_func) * idx_func);
  191. if (!range->funcs)
  192. perror("malloc range->funcs");
  193. range->nr = idx_func;
  194. memset(range->funcs, 0, sizeof(struct cpuid_func) * idx_func);
  195. for (; f <= max_func; f++) {
  196. eax = f;
  197. subleaf = ecx = 0;
  198. cpuid(&eax, &ebx, &ecx, &edx);
  199. allzero = cpuid_store(range, f, subleaf, eax, ebx, ecx, edx);
  200. if (allzero)
  201. continue;
  202. num_leafs++;
  203. if (!has_subleafs(f))
  204. continue;
  205. max_subleaf = MAX_SUBLEAF_NUM;
  206. /*
  207. * Some can provide the exact number of subleafs,
  208. * others have to be tried (0xf)
  209. */
  210. if (f == 0x7 || f == 0x14 || f == 0x17 || f == 0x18)
  211. max_subleaf = (eax & 0xff) + 1;
  212. if (f == 0xb)
  213. max_subleaf = 2;
  214. for (subleaf = 1; subleaf < max_subleaf; subleaf++) {
  215. eax = f;
  216. ecx = subleaf;
  217. cpuid(&eax, &ebx, &ecx, &edx);
  218. allzero = cpuid_store(range, f, subleaf,
  219. eax, ebx, ecx, edx);
  220. if (allzero)
  221. continue;
  222. num_leafs++;
  223. }
  224. }
  225. return range;
  226. }
  227. /*
  228. * The basic row format for cpuid.csv is
  229. * LEAF,SUBLEAF,register_name,bits,short name,long description
  230. *
  231. * like:
  232. * 0, 0, EAX, 31:0, max_basic_leafs, Max input value for supported subleafs
  233. * 1, 0, ECX, 0, sse3, Streaming SIMD Extensions 3(SSE3)
  234. */
  235. static int parse_line(char *line)
  236. {
  237. char *str;
  238. int i;
  239. struct cpuid_range *range;
  240. struct cpuid_func *func;
  241. struct subleaf *leaf;
  242. u32 index;
  243. u32 sub;
  244. char buffer[512];
  245. char *buf;
  246. /*
  247. * Tokens:
  248. * 1. leaf
  249. * 2. subleaf
  250. * 3. register
  251. * 4. bits
  252. * 5. short name
  253. * 6. long detail
  254. */
  255. char *tokens[6];
  256. struct reg_desc *reg;
  257. struct bits_desc *bdesc;
  258. int reg_index;
  259. char *start, *end;
  260. /* Skip comments and NULL line */
  261. if (line[0] == '#' || line[0] == '\n')
  262. return 0;
  263. strncpy(buffer, line, 511);
  264. buffer[511] = 0;
  265. str = buffer;
  266. for (i = 0; i < 5; i++) {
  267. tokens[i] = strtok(str, ",");
  268. if (!tokens[i])
  269. goto err_exit;
  270. str = NULL;
  271. }
  272. tokens[5] = strtok(str, "\n");
  273. if (!tokens[5])
  274. goto err_exit;
  275. /* index/main-leaf */
  276. index = strtoull(tokens[0], NULL, 0);
  277. if (index & 0x80000000)
  278. range = leafs_ext;
  279. else
  280. range = leafs_basic;
  281. index &= 0x7FFFFFFF;
  282. /* Skip line parsing for non-existing indexes */
  283. if ((int)index >= range->nr)
  284. return -1;
  285. func = &range->funcs[index];
  286. /* Return if the index has no valid item on this platform */
  287. if (!func->nr)
  288. return 0;
  289. /* subleaf */
  290. sub = strtoul(tokens[1], NULL, 0);
  291. if ((int)sub > func->nr)
  292. return -1;
  293. leaf = &func->leafs[sub];
  294. buf = tokens[2];
  295. if (strcasestr(buf, "EAX"))
  296. reg_index = R_EAX;
  297. else if (strcasestr(buf, "EBX"))
  298. reg_index = R_EBX;
  299. else if (strcasestr(buf, "ECX"))
  300. reg_index = R_ECX;
  301. else if (strcasestr(buf, "EDX"))
  302. reg_index = R_EDX;
  303. else
  304. goto err_exit;
  305. reg = &leaf->info[reg_index];
  306. bdesc = &reg->descs[reg->nr++];
  307. /* bit flag or bits field */
  308. buf = tokens[3];
  309. end = strtok(buf, ":");
  310. bdesc->end = strtoul(end, NULL, 0);
  311. bdesc->start = bdesc->end;
  312. /* start != NULL means it is bit fields */
  313. start = strtok(NULL, ":");
  314. if (start)
  315. bdesc->start = strtoul(start, NULL, 0);
  316. strcpy(bdesc->simp, tokens[4]);
  317. strcpy(bdesc->detail, tokens[5]);
  318. return 0;
  319. err_exit:
  320. printf("Warning: wrong line format:\n");
  321. printf("\tline[%d]: %s\n", flines, line);
  322. return -1;
  323. }
  324. /* Parse csv file, and construct the array of all leafs and subleafs */
  325. static void parse_text(void)
  326. {
  327. FILE *file;
  328. char *filename, *line = NULL;
  329. size_t len = 0;
  330. int ret;
  331. if (show_raw)
  332. return;
  333. filename = user_csv ? user_csv : def_csv;
  334. file = fopen(filename, "r");
  335. if (!file) {
  336. /* Fallback to a csv in the same dir */
  337. file = fopen("./cpuid.csv", "r");
  338. }
  339. if (!file) {
  340. printf("Fail to open '%s'\n", filename);
  341. return;
  342. }
  343. while (1) {
  344. ret = getline(&line, &len, file);
  345. flines++;
  346. if (ret > 0)
  347. parse_line(line);
  348. if (feof(file))
  349. break;
  350. }
  351. fclose(file);
  352. }
  353. /* Decode every eax/ebx/ecx/edx */
  354. static void decode_bits(u32 value, struct reg_desc *rdesc)
  355. {
  356. struct bits_desc *bdesc;
  357. int start, end, i;
  358. u32 mask;
  359. for (i = 0; i < rdesc->nr; i++) {
  360. bdesc = &rdesc->descs[i];
  361. start = bdesc->start;
  362. end = bdesc->end;
  363. if (start == end) {
  364. /* single bit flag */
  365. if (value & (1 << start))
  366. printf("\t%-20s %s%s\n",
  367. bdesc->simp,
  368. show_details ? "-" : "",
  369. show_details ? bdesc->detail : ""
  370. );
  371. } else {
  372. /* bit fields */
  373. if (show_flags_only)
  374. continue;
  375. mask = ((u64)1 << (end - start + 1)) - 1;
  376. printf("\t%-20s\t: 0x%-8x\t%s%s\n",
  377. bdesc->simp,
  378. (value >> start) & mask,
  379. show_details ? "-" : "",
  380. show_details ? bdesc->detail : ""
  381. );
  382. }
  383. }
  384. }
  385. static void show_leaf(struct subleaf *leaf)
  386. {
  387. if (!leaf)
  388. return;
  389. if (show_raw)
  390. leaf_print_raw(leaf);
  391. decode_bits(leaf->eax, &leaf->info[R_EAX]);
  392. decode_bits(leaf->ebx, &leaf->info[R_EBX]);
  393. decode_bits(leaf->ecx, &leaf->info[R_ECX]);
  394. decode_bits(leaf->edx, &leaf->info[R_EDX]);
  395. }
  396. static void show_func(struct cpuid_func *func)
  397. {
  398. int i;
  399. if (!func)
  400. return;
  401. for (i = 0; i < func->nr; i++)
  402. show_leaf(&func->leafs[i]);
  403. }
  404. static void show_range(struct cpuid_range *range)
  405. {
  406. int i;
  407. for (i = 0; i < range->nr; i++)
  408. show_func(&range->funcs[i]);
  409. }
  410. static inline struct cpuid_func *index_to_func(u32 index)
  411. {
  412. struct cpuid_range *range;
  413. range = (index & 0x80000000) ? leafs_ext : leafs_basic;
  414. index &= 0x7FFFFFFF;
  415. if (((index & 0xFFFF) + 1) > (u32)range->nr) {
  416. printf("ERR: invalid input index (0x%x)\n", index);
  417. return NULL;
  418. }
  419. return &range->funcs[index];
  420. }
  421. static void show_info(void)
  422. {
  423. struct cpuid_func *func;
  424. if (show_raw) {
  425. /* Show all of the raw output of 'cpuid' instr */
  426. raw_dump_range(leafs_basic);
  427. raw_dump_range(leafs_ext);
  428. return;
  429. }
  430. if (user_index != 0xFFFFFFFF) {
  431. /* Only show specific leaf/subleaf info */
  432. func = index_to_func(user_index);
  433. if (!func)
  434. return;
  435. /* Dump the raw data also */
  436. show_raw = true;
  437. if (user_sub != 0xFFFFFFFF) {
  438. if (user_sub + 1 <= (u32)func->nr) {
  439. show_leaf(&func->leafs[user_sub]);
  440. return;
  441. }
  442. printf("ERR: invalid input subleaf (0x%x)\n", user_sub);
  443. }
  444. show_func(func);
  445. return;
  446. }
  447. printf("CPU features:\n=============\n\n");
  448. show_range(leafs_basic);
  449. show_range(leafs_ext);
  450. }
  451. static void setup_platform_cpuid(void)
  452. {
  453. u32 eax, ebx, ecx, edx;
  454. /* Check vendor */
  455. eax = ebx = ecx = edx = 0;
  456. cpuid(&eax, &ebx, &ecx, &edx);
  457. /* "htuA" */
  458. if (ebx == 0x68747541)
  459. is_amd = true;
  460. /* Setup leafs for the basic and extended range */
  461. leafs_basic = setup_cpuid_range(0x0);
  462. leafs_ext = setup_cpuid_range(0x80000000);
  463. }
  464. static void usage(void)
  465. {
  466. printf("kcpuid [-abdfhr] [-l leaf] [-s subleaf]\n"
  467. "\t-a|--all Show both bit flags and complex bit fields info\n"
  468. "\t-b|--bitflags Show boolean flags only\n"
  469. "\t-d|--detail Show details of the flag/fields (default)\n"
  470. "\t-f|--flags Specify the cpuid csv file\n"
  471. "\t-h|--help Show usage info\n"
  472. "\t-l|--leaf=index Specify the leaf you want to check\n"
  473. "\t-r|--raw Show raw cpuid data\n"
  474. "\t-s|--subleaf=sub Specify the subleaf you want to check\n"
  475. );
  476. }
  477. static struct option opts[] = {
  478. { "all", no_argument, NULL, 'a' }, /* show both bit flags and fields */
  479. { "bitflags", no_argument, NULL, 'b' }, /* only show bit flags, default on */
  480. { "detail", no_argument, NULL, 'd' }, /* show detail descriptions */
  481. { "file", required_argument, NULL, 'f' }, /* use user's cpuid file */
  482. { "help", no_argument, NULL, 'h'}, /* show usage */
  483. { "leaf", required_argument, NULL, 'l'}, /* only check a specific leaf */
  484. { "raw", no_argument, NULL, 'r'}, /* show raw CPUID leaf data */
  485. { "subleaf", required_argument, NULL, 's'}, /* check a specific subleaf */
  486. { NULL, 0, NULL, 0 }
  487. };
  488. static int parse_options(int argc, char *argv[])
  489. {
  490. int c;
  491. while ((c = getopt_long(argc, argv, "abdf:hl:rs:",
  492. opts, NULL)) != -1)
  493. switch (c) {
  494. case 'a':
  495. show_flags_only = false;
  496. break;
  497. case 'b':
  498. show_flags_only = true;
  499. break;
  500. case 'd':
  501. show_details = true;
  502. break;
  503. case 'f':
  504. user_csv = optarg;
  505. break;
  506. case 'h':
  507. usage();
  508. exit(1);
  509. break;
  510. case 'l':
  511. /* main leaf */
  512. user_index = strtoul(optarg, NULL, 0);
  513. break;
  514. case 'r':
  515. show_raw = true;
  516. break;
  517. case 's':
  518. /* subleaf */
  519. user_sub = strtoul(optarg, NULL, 0);
  520. break;
  521. default:
  522. printf("%s: Invalid option '%c'\n", argv[0], optopt);
  523. return -1;
  524. }
  525. return 0;
  526. }
  527. /*
  528. * Do 4 things in turn:
  529. * 1. Parse user options
  530. * 2. Parse and store all the CPUID leaf data supported on this platform
  531. * 2. Parse the csv file, while skipping leafs which are not available
  532. * on this platform
  533. * 3. Print leafs info based on user options
  534. */
  535. int main(int argc, char *argv[])
  536. {
  537. if (parse_options(argc, argv))
  538. return -1;
  539. /* Setup the cpuid leafs of current platform */
  540. setup_platform_cpuid();
  541. /* Read and parse the 'cpuid.csv' */
  542. parse_text();
  543. show_info();
  544. return 0;
  545. }