/* * Copyright 2008 Veselin Georgiev, * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @page cpuid_tool * @date 2008-11-19 * @author Veselin Georgiev * @brief Command line interface to libcpuid * * @details * This file is provides a direct interface to libcpuid. See the usage() * function (or just run the program with the `--help' switch) for a short * command line options reference. * * This file has several purposes: * * 1. When started with no arguments, the program outputs the RAW and decoded * CPU data to files (`raw.txt' and `report.txt', respectively) - this is * intended to be a dumb, doubleclicky tool for non-developer * users, that can provide debug info about unrecognized processors to * libcpuid developers. * 2. When operated from the terminal with the `--report' option, it is a * generic CPU-info utility. * 3. Can be used in shell scripts, e.g. to get the name of the CPU, cache * sizes, features, with query options like `--cache', `--brandstr', etc. * 4. Finally, it serves to self-document libcpiud itself :) */ #include #include #include #include "libcpuid.h" /* Globals: */ char raw_data_file[256] = ""; char out_file[256] = ""; typedef enum { NEED_CPUID_PRESENT, NEED_ARCHITECTURE, NEED_FEATURE_LEVEL, NEED_PURPOSE, NEED_VENDOR_STR, NEED_VENDOR_ID, NEED_BRAND_STRING, NEED_FAMILY, NEED_MODEL, NEED_STEPPING, NEED_EXT_FAMILY, NEED_EXT_MODEL, NEED_IMPLEMENTER, NEED_VARIANT, NEED_PART_NUM, NEED_REVISION, NEED_NUM_CORES, NEED_NUM_LOGICAL, NEED_TOTAL_CPUS, NEED_AFFI_MASK, NEED_L1D_SIZE, NEED_L1I_SIZE, NEED_L2_SIZE, NEED_L3_SIZE, NEED_L4_SIZE, NEED_L1D_ASSOC, NEED_L1I_ASSOC, NEED_L2_ASSOC, NEED_L3_ASSOC, NEED_L4_ASSOC, NEED_L1D_CACHELINE, NEED_L1I_CACHELINE, NEED_L2_CACHELINE, NEED_L3_CACHELINE, NEED_L4_CACHELINE, NEED_L1D_INSTANCES, NEED_L1I_INSTANCES, NEED_L2_INSTANCES, NEED_L3_INSTANCES, NEED_L4_INSTANCES, NEED_CODENAME, NEED_FEATURES, NEED_CLOCK, NEED_CLOCK_OS, NEED_CLOCK_RDTSC, NEED_CLOCK_IC, NEED_RDMSR, NEED_RDMSR_RAW, NEED_SSE_UNIT_SIZE, } output_data_switch; int need_input = 0, need_output = 0, need_quiet = 0, need_report = 0, need_clockreport = 0, need_timed_clockreport = 0, verbose_level = 0, need_version = 0, need_cpulist = 0, need_sgx = 0, need_hypervisor = 0, need_identify = 0; #define MAX_REQUESTS 64 int num_requests = 0; output_data_switch requests[MAX_REQUESTS]; FILE *fout; const struct { output_data_switch sw; const char* synopsis; int ident_required; } matchtable[] = { { NEED_CPUID_PRESENT, "--cpuid" , 0}, { NEED_ARCHITECTURE , "--architecture" , 1}, { NEED_FEATURE_LEVEL, "--feature-level", 1}, { NEED_PURPOSE , "--purpose" , 1}, { NEED_VENDOR_STR , "--vendorstr" , 1}, { NEED_VENDOR_ID , "--vendorid" , 1}, { NEED_BRAND_STRING , "--brandstr" , 1}, { NEED_FAMILY , "--family" , 1}, { NEED_MODEL , "--model" , 1}, { NEED_STEPPING , "--stepping" , 1}, { NEED_EXT_FAMILY , "--extfamily" , 1}, { NEED_EXT_MODEL , "--extmodel" , 1}, { NEED_IMPLEMENTER , "--implementer" , 1}, { NEED_VARIANT , "--variant" , 1}, { NEED_PART_NUM , "--part-num" , 1}, { NEED_REVISION , "--revision" , 1}, { NEED_NUM_CORES , "--cores" , 1}, { NEED_NUM_LOGICAL , "--logical" , 1}, { NEED_TOTAL_CPUS , "--total-cpus" , 1}, { NEED_AFFI_MASK , "--affi-mask" , 1}, { NEED_L1D_SIZE , "--l1d-cache" , 1}, { NEED_L1I_SIZE , "--l1i-cache" , 1}, { NEED_L2_SIZE , "--cache" , 1}, { NEED_L2_SIZE , "--l2-cache" , 1}, { NEED_L3_SIZE , "--l3-cache" , 1}, { NEED_L4_SIZE , "--l4-cache" , 1}, { NEED_L1D_ASSOC , "--l1d-assoc" , 1}, { NEED_L1I_ASSOC , "--l1i-assoc" , 1}, { NEED_L2_ASSOC , "--l2-assoc" , 1}, { NEED_L3_ASSOC , "--l3-assoc" , 1}, { NEED_L4_ASSOC , "--l4-assoc" , 1}, { NEED_L1D_CACHELINE, "--l1d-cacheline", 1}, { NEED_L1I_CACHELINE, "--l1i-cacheline", 1}, { NEED_L2_CACHELINE , "--l2-cacheline" , 1}, { NEED_L3_CACHELINE , "--l3-cacheline" , 1}, { NEED_L4_CACHELINE , "--l4-cacheline" , 1}, { NEED_L1D_INSTANCES, "--l1d-instances", 1}, { NEED_L1I_INSTANCES, "--l1i-instances", 1}, { NEED_L2_INSTANCES , "--l2-instances" , 1}, { NEED_L3_INSTANCES , "--l3-instances" , 1}, { NEED_L4_INSTANCES , "--l4-instances" , 1}, { NEED_CODENAME , "--codename" , 1}, { NEED_FEATURES , "--flags" , 1}, { NEED_CLOCK , "--clock" , 0}, { NEED_CLOCK_OS , "--clock-os" , 0}, { NEED_CLOCK_RDTSC , "--clock-rdtsc" , 1}, { NEED_CLOCK_IC , "--clock-ic" , 1}, { NEED_RDMSR , "--rdmsr" , 0}, { NEED_RDMSR_RAW , "--rdmsr-raw" , 0}, { NEED_SSE_UNIT_SIZE, "--sse-size" , 1}, }; const int sz_match = (sizeof(matchtable) / sizeof(matchtable[0])); /* functions */ static void usage(void) { int line_fill, l, i; printf("Usage: cpuid_tool [options]\n\n"); printf("Options:\n"); printf(" -h, --help - Show this help\n"); printf(" --load= - Load raw CPUID data from file\n"); printf(" --save= - Acquire raw CPUID data and write it to file\n"); printf(" --report, --all - Report all decoded CPU info (w/o clock)\n"); printf(" --clock - in conjunction to --report: print CPU clock as well\n"); printf(" --clock-rdtsc - same as --clock, but use RDTSC for clock detection\n"); printf(" --cpulist - list all known CPUs\n"); printf(" --sgx - list SGX leaf data, if SGX is supported.\n"); printf(" --hypervisor - print hypervisor vendor if detected.\n"); printf(" --quiet - disable warnings\n"); printf(" --outfile= - redirect all output to this file, instead of stdout\n"); printf(" --verbose, -v - be extra verbose (more keys increase verbosiness level)\n"); printf(" --version - print library version\n"); printf("\n"); printf("Query switches (generate 1 line of output per switch; in order of appearance):"); line_fill = 80; for (i = 0; i < sz_match; i++) { l = (int) strlen(matchtable[i].synopsis); if (line_fill + l > 76) { line_fill = 2; printf("\n "); } printf("%s", matchtable[i].synopsis); if (i < sz_match - 1) { line_fill += l + 2; printf(", "); } } printf("\n\n"); printf("If `-' is used for , then stdin/stdout will be used instead of files.\n"); printf("When no options are present, the program behaves as if it was invoked with\n"); printf(" cpuid_tool \"--save=raw.txt --outfile=report.txt --report --verbose\"\n"); } static int parse_cmdline(int argc, char** argv) { #define xerror(msg)\ fprintf(stderr, "Error: %s\n\n", msg); \ fprintf(stderr, "Use -h to get a list of supported options\n"); \ return -1; int i, j, recog, num_vs; if (argc == 1) { /* Default command line options */ need_output = 1; strcpy(raw_data_file, "raw.txt"); strcpy(out_file, "report.txt"); need_report = 1; verbose_level = 1; return 1; } for (i = 1; i < argc; i++) { char *arg = argv[i]; recog = 0; if (!strcmp(arg, "-h") || !strcmp(arg, "--help")) { usage(); return 0; } if (!strncmp(arg, "--load=", 7)) { if (need_input) { xerror("Too many `--load' options!"); } if (need_output) { xerror("Cannot have both `--load' and `--save' options!"); } if (strlen(arg) <= 7) { xerror("--load: bad file specification!"); } need_input = 1; strcpy(raw_data_file, arg + 7); recog = 1; } if (!strncmp(arg, "--save=", 7)) { if (need_output) { xerror("Too many `--save' options!"); } if (need_input) { xerror("Cannot have both `--load' and `--save' options!"); } if (strlen(arg) <= 7) { xerror("--save: bad file specification!"); } need_output = 1; strcpy(raw_data_file, arg + 7); recog = 1; } if (!strncmp(arg, "--outfile=", 10)) { if (strlen(arg) <= 10) { xerror("--output: bad file specification!"); } strcpy(out_file, arg + 10); recog = 1; } if (!strcmp(arg, "--report") || !strcmp(arg, "--all")) { need_report = 1; recog = 1; } if (!strcmp(arg, "--clock")) { need_clockreport = 1; recog = 1; } if (!strcmp(arg, "--clock-rdtsc")) { need_clockreport = 1; need_timed_clockreport = 1; recog = 1; } if (!strcmp(arg, "--quiet")) { need_quiet = 1; recog = 1; } if (!strcmp(arg, "--verbose")) { verbose_level++; recog = 1; } if (!strcmp(arg, "--version")) { need_version = 1; recog = 1; } if (!strcmp(arg, "--cpulist")) { need_cpulist = 1; recog = 1; } if (!strcmp(arg, "--sgx")) { need_sgx = 1; need_identify = 1; recog = 1; } if (!strcmp(arg, "--hypervisor")) { need_hypervisor = 1; need_identify = 1; recog = 1; } if (arg[0] == '-' && arg[1] == 'v') { num_vs = 1; while (arg[num_vs] == 'v') num_vs++; if (arg[num_vs] == '\0') { verbose_level += num_vs-1; recog = 1; } } for (j = 0; j < sz_match; j++) if (!strcmp(arg, matchtable[j].synopsis)) { if (num_requests >= MAX_REQUESTS) { xerror("Too many requests!"); } requests[num_requests++] = matchtable[j].sw; recog = 1; break; } if (!recog) { fprintf(stderr, "Unrecognized option: `%s'\n\n", arg); fprintf(stderr, "Use -h to get a list of supported options\n"); return -1; } } return 1; } static void close_out(void) { fclose(fout); } static int check_need_raw_data(void) { int i, j; if (need_output || need_report || need_identify) return 1; for (i = 0; i < num_requests; i++) { for (j = 0; j < sz_match; j++) if (requests[i] == matchtable[j].sw && matchtable[j].ident_required) return 1; } return 0; } static void print_info(output_data_switch query, struct cpu_id_t* data) { int i, value; struct msr_driver_t* handle; switch (query) { case NEED_CPUID_PRESENT: fprintf(fout, "%d\n", cpuid_present()); break; case NEED_ARCHITECTURE: fprintf(fout, "%s\n", cpu_architecture_str(data->architecture)); break; case NEED_FEATURE_LEVEL: fprintf(fout, "%s\n", cpu_feature_level_str(data->feature_level)); break; case NEED_PURPOSE: fprintf(fout, "%s\n", cpu_purpose_str(data->purpose)); break; case NEED_VENDOR_STR: fprintf(fout, "%s\n", data->vendor_str); break; case NEED_VENDOR_ID: fprintf(fout, "%d\n", data->vendor); break; case NEED_BRAND_STRING: fprintf(fout, "%s\n", data->brand_str); break; case NEED_FAMILY: fprintf(fout, "%d\n", data->x86.family); break; case NEED_MODEL: fprintf(fout, "%d\n", data->x86.model); break; case NEED_STEPPING: fprintf(fout, "%d\n", data->x86.stepping); break; case NEED_EXT_FAMILY: fprintf(fout, "%d\n", data->x86.ext_family); break; case NEED_EXT_MODEL: fprintf(fout, "%d\n", data->x86.ext_model); break; case NEED_IMPLEMENTER: fprintf(fout, "%d\n", data->arm.implementer); break; case NEED_VARIANT: fprintf(fout, "%d\n", data->arm.variant); break; case NEED_PART_NUM: fprintf(fout, "%d\n", data->arm.part_num); break; case NEED_REVISION: fprintf(fout, "%d\n", data->arm.revision); break; case NEED_NUM_CORES: fprintf(fout, "%d\n", data->num_cores); break; case NEED_NUM_LOGICAL: fprintf(fout, "%d\n", data->num_logical_cpus); break; case NEED_TOTAL_CPUS: fprintf(fout, "%d\n", cpuid_get_total_cpus()); break; case NEED_AFFI_MASK: fprintf(fout, "0x%s\n", affinity_mask_str(&data->affinity_mask)); break; case NEED_L1D_SIZE: fprintf(fout, "%d\n", data->l1_data_cache); break; case NEED_L1I_SIZE: fprintf(fout, "%d\n", data->l1_instruction_cache); break; case NEED_L2_SIZE: fprintf(fout, "%d\n", data->l2_cache); break; case NEED_L3_SIZE: fprintf(fout, "%d\n", data->l3_cache); break; case NEED_L4_SIZE: fprintf(fout, "%d\n", data->l4_cache); break; case NEED_L1D_ASSOC: fprintf(fout, "%d\n", data->l1_data_assoc); break; case NEED_L1I_ASSOC: fprintf(fout, "%d\n", data->l1_instruction_assoc); break; case NEED_L2_ASSOC: fprintf(fout, "%d\n", data->l2_assoc); break; case NEED_L3_ASSOC: fprintf(fout, "%d\n", data->l3_assoc); break; case NEED_L4_ASSOC: fprintf(fout, "%d\n", data->l4_assoc); break; case NEED_L1D_CACHELINE: fprintf(fout, "%d\n", data->l1_data_cacheline); break; case NEED_L1I_CACHELINE: fprintf(fout, "%d\n", data->l1_instruction_cacheline); break; case NEED_L2_CACHELINE: fprintf(fout, "%d\n", data->l2_cacheline); break; case NEED_L3_CACHELINE: fprintf(fout, "%d\n", data->l3_cacheline); break; case NEED_L4_CACHELINE: fprintf(fout, "%d\n", data->l4_cacheline); break; case NEED_L1D_INSTANCES: fprintf(fout, "%d\n", data->l1_data_instances); break; case NEED_L1I_INSTANCES: fprintf(fout, "%d\n", data->l1_instruction_instances); break; case NEED_L2_INSTANCES: fprintf(fout, "%d\n", data->l2_instances); break; case NEED_L3_INSTANCES: fprintf(fout, "%d\n", data->l3_instances); break; case NEED_L4_INSTANCES: fprintf(fout, "%d\n", data->l4_instances); break; case NEED_CODENAME: fprintf(fout, "%s\n", data->cpu_codename); break; case NEED_FEATURES: { for (i = 0; i < NUM_CPU_FEATURES; i++) if (data->flags[i]) fprintf(fout, " %s", cpu_feature_str(i)); fprintf(fout, "\n"); break; } case NEED_CLOCK: fprintf(fout, "%d\n", cpu_clock()); break; case NEED_CLOCK_OS: fprintf(fout, "%d\n", cpu_clock_by_os()); break; case NEED_CLOCK_RDTSC: fprintf(fout, "%d\n", cpu_clock_measure(400, 1)); break; case NEED_CLOCK_IC: fprintf(fout, "%d\n", cpu_clock_by_ic(25, 16)); break; case NEED_RDMSR: { if ((handle = cpu_msr_driver_open()) == NULL) { fprintf(fout, "Cannot open MSR driver: %s\n", cpuid_error()); } else { if ((value = cpu_msrinfo(handle, INFO_MPERF)) != CPU_INVALID_VALUE) fprintf(fout, " MSR.mperf : %d MHz\n", value); if ((value = cpu_msrinfo(handle, INFO_APERF)) != CPU_INVALID_VALUE) fprintf(fout, " MSR.aperf : %d MHz\n", value); if ((value = cpu_msrinfo(handle, INFO_MIN_MULTIPLIER)) != CPU_INVALID_VALUE) fprintf(fout, " min. multi.: %.2lf\n", value / 100.0); if ((value = cpu_msrinfo(handle, INFO_CUR_MULTIPLIER)) != CPU_INVALID_VALUE) fprintf(fout, " cur. multi.: %.2lf\n", value / 100.0); if ((value = cpu_msrinfo(handle, INFO_MAX_MULTIPLIER)) != CPU_INVALID_VALUE) fprintf(fout, " max. multi.: %.2lf\n", value / 100.0); if ((value = cpu_msrinfo(handle, INFO_TEMPERATURE)) != CPU_INVALID_VALUE) fprintf(fout, " temperature: %d degrees Celsius\n", value); if ((value = cpu_msrinfo(handle, INFO_THROTTLING)) != CPU_INVALID_VALUE) fprintf(fout, " throttling : %s\n", value ? "yes" : "no"); if ((value = cpu_msrinfo(handle, INFO_VOLTAGE)) != CPU_INVALID_VALUE) fprintf(fout, " core volt. : %.2lf Volts\n", value / 100.0); if ((value = cpu_msrinfo(handle, INFO_BUS_CLOCK)) != CPU_INVALID_VALUE) fprintf(fout, " bus clock : %.2lf MHz\n", value / 100.0); cpu_msr_driver_close(handle); } break; } case NEED_RDMSR_RAW: { if ((handle = cpu_msr_driver_open()) == NULL) { fprintf(fout, "Cannot open MSR driver: %s\n", cpuid_error()); } else { msr_serialize_raw_data(handle, ""); cpu_msr_driver_close(handle); } break; } case NEED_SSE_UNIT_SIZE: { fprintf(fout, "%d (%s)\n", data->x86.sse_size, data->detection_hints[CPU_HINT_SSE_SIZE_AUTH] ? "authoritative" : "non-authoritative"); break; } default: fprintf(fout, "How did you get here?!?\n"); break; } } static void print_cpulist(void) { int i, j; struct cpu_list_t list; const struct { const char *name; cpu_vendor_t vendor; } cpu_vendors[] = { { "Intel", VENDOR_INTEL }, { "AMD/Hygon", VENDOR_AMD }, { "Cyrix", VENDOR_CYRIX }, { "NexGen", VENDOR_NEXGEN }, { "Transmeta", VENDOR_TRANSMETA }, { "UMC", VENDOR_UMC }, { "Centaur/VIA", VENDOR_CENTAUR }, { "Rise", VENDOR_RISE }, { "SiS", VENDOR_SIS }, { "NSC", VENDOR_NSC }, { "ARM", VENDOR_ARM }, { "Broadcom", VENDOR_BROADCOM }, { "Cavium", VENDOR_CAVIUM }, { "DEC", VENDOR_DEC }, { "FUJITSU", VENDOR_FUJITSU }, { "HiSilicon", VENDOR_HISILICON }, { "Infineon", VENDOR_INFINEON }, { "Motorola/Freescale", VENDOR_FREESCALE }, { "NVIDIA" , VENDOR_NVIDIA }, { "APM", VENDOR_APM }, { "Qualcomm", VENDOR_QUALCOMM }, { "Samsung", VENDOR_SAMSUNG }, { "Marvell" , VENDOR_MARVELL }, { "Apple", VENDOR_APPLE }, { "Faraday", VENDOR_FARADAY }, { "Microsoft" , VENDOR_MICROSOFT }, { "Phytium", VENDOR_PHYTIUM }, { "Ampere" , VENDOR_AMPERE }, }; for (i = 0; i < sizeof(cpu_vendors)/sizeof(cpu_vendors[0]); i++) { fprintf(fout, "-----%s-----\n", cpu_vendors[i].name); cpuid_get_cpu_list(cpu_vendors[i].vendor, &list); for (j = 0; j < list.num_entries; j++) fprintf(fout, "%s\n", list.names[j]); cpuid_free_cpu_list(&list); } } static void print_sgx_data(const struct cpu_raw_data_t* raw, const struct cpu_id_t* data) { int i; fprintf(fout, "SGX: %d (%s)\n", data->x86.sgx.present, data->x86.sgx.present ? "present" : "absent"); if (data->x86.sgx.present) { fprintf(fout, "SGX max enclave size (32-bit): 2^%d\n", data->x86.sgx.max_enclave_32bit); fprintf(fout, "SGX max enclave size (64-bit): 2^%d\n", data->x86.sgx.max_enclave_64bit); fprintf(fout, "SGX1 extensions : %d (%s)\n", data->x86.sgx.flags[INTEL_SGX1], data->x86.sgx.flags[INTEL_SGX1] ? "present" : "absent"); fprintf(fout, "SGX2 extensions : %d (%s)\n", data->x86.sgx.flags[INTEL_SGX2], data->x86.sgx.flags[INTEL_SGX2] ? "present" : "absent"); fprintf(fout, "SGX MISCSELECT : %08x\n", data->x86.sgx.misc_select); fprintf(fout, "SGX SECS.ATTRIBUTES mask : %016llx\n", (unsigned long long) data->x86.sgx.secs_attributes); fprintf(fout, "SGX SECS.XSAVE feature mask : %016llx\n", (unsigned long long) data->x86.sgx.secs_xfrm); fprintf(fout, "SGX EPC sections count : %d\n", data->x86.sgx.num_epc_sections); for (i = 0; i < data->x86.sgx.num_epc_sections; i++) { struct cpu_epc_t epc = cpuid_get_epc(i, raw); fprintf(fout, " SGX EPC section #%-8d: start = %llx, size = %llu\n", i, (unsigned long long) epc.start_addr, (unsigned long long) epc.length); } } } static void print_hypervisor(struct cpu_raw_data_t* raw, struct cpu_id_t* data) { int i; const char *hypervisor_str = NULL; const hypervisor_vendor_t hypervisor = cpuid_get_hypervisor(raw, data); const struct { const char *name; hypervisor_vendor_t hypervisor; } hypervisors_vendors[NUM_HYPERVISOR_VENDORS] = { { "none", HYPERVISOR_NONE }, { "FreeBSD bhyve", HYPERVISOR_BHYVE }, { "Microsoft Hyper-V ", HYPERVISOR_HYPERV }, { "KVM", HYPERVISOR_KVM }, { "Parallels", HYPERVISOR_PARALLELS }, { "QEMU", HYPERVISOR_QEMU }, { "VirtualBox", HYPERVISOR_VIRTUALBOX }, { "VMware", HYPERVISOR_VMWARE }, { "Xen", HYPERVISOR_XEN }, }; for (i = 0; i < NUM_HYPERVISOR_VENDORS; i++) if (hypervisors_vendors[i].hypervisor == hypervisor) { hypervisor_str = hypervisors_vendors[i].name; break; } fprintf(fout, "Hypervisor vendor: %s\n", (hypervisor_str == NULL) ? "unknown" : hypervisor_str); if (hypervisor == HYPERVISOR_NONE) fprintf(fout, "Caution: no hypervisor detected from CPUID bits, but a hypervisor may be hidden.\n" "Refer to https://github.com/anrieff/libcpuid/issues/90#issuecomment-296568713\n"); } int main(int argc, char** argv) { int parseres = parse_cmdline(argc, argv); int i, readres, writeres; int only_clock_queries; uint8_t cpu_type_index; struct cpu_raw_data_array_t raw_array = { .with_affinity = false, .num_raw = 0, .raw = NULL }; struct system_id_t data = { .num_cpu_types = 0 }; if (parseres != 1) return parseres; /* In quiet mode, disable libcpuid warning messages: */ if (need_quiet) cpuid_set_warn_function(NULL); cpuid_set_verbosiness_level(verbose_level); /* Redirect output, if necessary: */ if (strcmp(out_file, "") && strcmp(out_file, "-")) { fout = fopen(out_file, "wt"); if (!fout) { if (!need_quiet) fprintf(stderr, "Cannot open `%s' for writing!\n", out_file); return -1; } atexit(close_out); } else { fout = stdout; } /* If requested, print library version: */ if (need_version) fprintf(fout, "%s\n", cpuid_lib_version()); if (need_input) { /* We have a request to input raw CPUID data from file: */ if (!strcmp(raw_data_file, "-")) /* Input from stdin */ readres = cpuid_deserialize_all_raw_data(&raw_array, ""); else /* Input from file */ readres = cpuid_deserialize_all_raw_data(&raw_array, raw_data_file); if (readres < 0) { if (!need_quiet) { fprintf(stderr, "Cannot deserialize raw data from "); if (!strcmp(raw_data_file, "-")) fprintf(stderr, "stdin\n"); else fprintf(stderr, "file `%s'\n", raw_data_file); /* Print the error message */ fprintf(stderr, "Error: %s\n", cpuid_error()); } return -1; } } else { if (check_need_raw_data()) { /* Try to obtain raw CPUID data from the CPU: */ readres = cpuid_get_all_raw_data(&raw_array); if (readres < 0) { if (!need_quiet) { fprintf(stderr, "Cannot obtain raw CPU data!\n"); fprintf(stderr, "Error: %s\n", cpuid_error()); } return -1; } } } /* Need to dump raw CPUID data to file: */ if (need_output) { if (!strcmp(raw_data_file, "-")) /* Serialize to stdout */ writeres = cpuid_serialize_all_raw_data(&raw_array, ""); else /* Serialize to file */ writeres = cpuid_serialize_all_raw_data(&raw_array, raw_data_file); if (writeres < 0) { if (!need_quiet) { fprintf(stderr, "Cannot serialize raw data to "); if (!strcmp(raw_data_file, "-")) fprintf(stderr, "stdout\n"); else fprintf(stderr, "file `%s'\n", raw_data_file); /* Print the error message */ fprintf(stderr, "Error: %s\n", cpuid_error()); } return -1; } } if (need_report) { if (verbose_level >= 1) { printf("Writing decoded CPU report to `%s'\n", out_file); } /* * Try CPU identification * (this fill the `data' structure with decoded CPU features) */ if (cpu_identify_all(&raw_array, &data) < 0) fprintf(fout, "Error identifying the CPU: %s\n", cpuid_error()); else if (data.num_cpu_types == 0) fprintf(fout, "The cpu_types array is empty: there is nothing to report\n"); else { /* Write a thorough report of cpu_id_t structure to output (usually stdout) */ fprintf(fout, "CPUID is present\n"); /* OK, now write what we have in `data'...: */ for (cpu_type_index = 0; cpu_type_index < data.num_cpu_types; cpu_type_index++) { fprintf(fout, "CPU Info for type #%d:\n------------------\n", cpu_type_index); fprintf(fout, " arch : %s\n", cpu_architecture_str(data.cpu_types[cpu_type_index].architecture)); fprintf(fout, " feat_level : %s\n", cpu_feature_level_str(data.cpu_types[cpu_type_index].feature_level)); fprintf(fout, " purpose : %s\n", cpu_purpose_str(data.cpu_types[cpu_type_index].purpose)); fprintf(fout, " vendor_str : `%s'\n", data.cpu_types[cpu_type_index].vendor_str); fprintf(fout, " vendor id : %d\n", (int) data.cpu_types[cpu_type_index].vendor); fprintf(fout, " brand_str : `%s'\n", data.cpu_types[cpu_type_index].brand_str); if (data.cpu_types[cpu_type_index].architecture == ARCHITECTURE_X86) { fprintf(fout, " family : %d (%02Xh)\n", data.cpu_types[cpu_type_index].x86.family, data.cpu_types[cpu_type_index].x86.family); fprintf(fout, " model : %d (%02Xh)\n", data.cpu_types[cpu_type_index].x86.model, data.cpu_types[cpu_type_index].x86.model); fprintf(fout, " stepping : %d (%02Xh)\n", data.cpu_types[cpu_type_index].x86.stepping, data.cpu_types[cpu_type_index].x86.stepping); fprintf(fout, " ext_family : %d (%02Xh)\n", data.cpu_types[cpu_type_index].x86.ext_family, data.cpu_types[cpu_type_index].x86.ext_family); fprintf(fout, " ext_model : %d (%02Xh)\n", data.cpu_types[cpu_type_index].x86.ext_model, data.cpu_types[cpu_type_index].x86.ext_model); } else if (data.cpu_types[cpu_type_index].architecture == ARCHITECTURE_ARM) { fprintf(fout, " implementer: %d (%02Xh)\n", data.cpu_types[cpu_type_index].arm.implementer, data.cpu_types[cpu_type_index].arm.implementer); fprintf(fout, " variant : %d (%02Xh)\n", data.cpu_types[cpu_type_index].arm.variant, data.cpu_types[cpu_type_index].arm.variant); fprintf(fout, " part_num : %d (%02Xh)\n", data.cpu_types[cpu_type_index].arm.part_num, data.cpu_types[cpu_type_index].arm.part_num); fprintf(fout, " revision : %d (%02Xh)\n", data.cpu_types[cpu_type_index].arm.revision, data.cpu_types[cpu_type_index].arm.revision); } fprintf(fout, " num_cores : %d\n", data.cpu_types[cpu_type_index].num_cores); fprintf(fout, " num_logical: %d\n", data.cpu_types[cpu_type_index].num_logical_cpus); fprintf(fout, " tot_logical: %d\n", data.cpu_types[cpu_type_index].total_logical_cpus); fprintf(fout, " affi_mask : 0x%s\n", affinity_mask_str(&data.cpu_types[cpu_type_index].affinity_mask)); if (data.cpu_types[cpu_type_index].architecture == ARCHITECTURE_X86) { fprintf(fout, " L1 D cache : %d KB\n", data.cpu_types[cpu_type_index].l1_data_cache); fprintf(fout, " L1 I cache : %d KB\n", data.cpu_types[cpu_type_index].l1_instruction_cache); fprintf(fout, " L2 cache : %d KB\n", data.cpu_types[cpu_type_index].l2_cache); fprintf(fout, " L3 cache : %d KB\n", data.cpu_types[cpu_type_index].l3_cache); fprintf(fout, " L4 cache : %d KB\n", data.cpu_types[cpu_type_index].l4_cache); fprintf(fout, " L1D assoc. : %d-way\n", data.cpu_types[cpu_type_index].l1_data_assoc); fprintf(fout, " L1I assoc. : %d-way\n", data.cpu_types[cpu_type_index].l1_instruction_assoc); fprintf(fout, " L2 assoc. : %d-way\n", data.cpu_types[cpu_type_index].l2_assoc); fprintf(fout, " L3 assoc. : %d-way\n", data.cpu_types[cpu_type_index].l3_assoc); fprintf(fout, " L4 assoc. : %d-way\n", data.cpu_types[cpu_type_index].l4_assoc); fprintf(fout, " L1D line sz: %d bytes\n", data.cpu_types[cpu_type_index].l1_data_cacheline); fprintf(fout, " L1I line sz: %d bytes\n", data.cpu_types[cpu_type_index].l1_instruction_cacheline); fprintf(fout, " L2 line sz : %d bytes\n", data.cpu_types[cpu_type_index].l2_cacheline); fprintf(fout, " L3 line sz : %d bytes\n", data.cpu_types[cpu_type_index].l3_cacheline); fprintf(fout, " L4 line sz : %d bytes\n", data.cpu_types[cpu_type_index].l4_cacheline); fprintf(fout, " L1D inst. : %d\n", data.cpu_types[cpu_type_index].l1_data_instances); fprintf(fout, " L1I inst. : %d\n", data.cpu_types[cpu_type_index].l1_instruction_instances); fprintf(fout, " L2 inst. : %d\n", data.cpu_types[cpu_type_index].l2_instances); fprintf(fout, " L3 inst. : %d\n", data.cpu_types[cpu_type_index].l3_instances); fprintf(fout, " L4 inst. : %d\n", data.cpu_types[cpu_type_index].l4_instances); fprintf(fout, " SSE units : %d bits (%s)\n", data.cpu_types[cpu_type_index].x86.sse_size, data.cpu_types[cpu_type_index].detection_hints[CPU_HINT_SSE_SIZE_AUTH] ? "authoritative" : "non-authoritative"); } fprintf(fout, " code name : `%s'\n", data.cpu_types[cpu_type_index].cpu_codename); fprintf(fout, " features :"); /* * Here we enumerate all CPU feature bits, and when a feature * is present output its name: */ for (i = 0; i < NUM_CPU_FEATURES; i++) if (data.cpu_types[cpu_type_index].flags[i]) fprintf(fout, " %s", cpu_feature_str(i)); fprintf(fout, "\n"); } } /* Is CPU clock info requested? */ if (need_clockreport) { if (need_timed_clockreport) { /* Here we use the RDTSC-based routine */ fprintf(fout, " cpu clock : %d MHz\n", cpu_clock_measure(400, 1)); } else { /* Here we use the OS-provided info */ fprintf(fout, " cpu clock : %d MHz\n", cpu_clock()); } } } /* * Check if we have any queries to process. * We have to handle the case when `--clock' or `--clock-rdtsc' options * are present. * If in report mode, this will generate spurious output after the * report, if not handled explicitly. */ only_clock_queries = 1; for (i = 0; i < num_requests; i++) if (requests[i] != NEED_CLOCK && requests[i] != NEED_CLOCK_RDTSC) { only_clock_queries = 0; break; } /* OK, process all queries. */ if (((!need_report || !only_clock_queries) && num_requests > 0) || need_identify) { /* Identify the CPU. Make it do cpuid_get_all_raw_data() itself */ if (check_need_raw_data() && cpu_identify_all(&raw_array, &data) < 0) { if (!need_quiet) fprintf(stderr, "Error identifying the CPU: %s\n", cpuid_error()); return -1; } for (cpu_type_index = 0; cpu_type_index < data.num_cpu_types; cpu_type_index++) { if (raw_array.with_affinity && (cpu_type_index > 0)) fprintf(fout, "--------------------------------------------------------------------------------\n"); for (i = 0; i < num_requests; i++) print_info(requests[i], &data.cpu_types[cpu_type_index]); } } if (need_cpulist) { print_cpulist(); } if (need_sgx) { print_sgx_data(&raw_array.raw[0], &data.cpu_types[0]); } if (need_hypervisor) { print_hypervisor(&raw_array.raw[0], &data.cpu_types[0]); } cpuid_free_raw_data_array(&raw_array); cpuid_free_system_id(&data); return 0; }