diff --git a/src/common/args.c b/src/common/args.c index 71318db..ff33b1c 100644 --- a/src/common/args.c +++ b/src/common/args.c @@ -20,6 +20,7 @@ static const char *SYTLES_STR_LIST[] = { struct args_struct { bool debug_flag; bool help_flag; + bool raw_flag; bool verbose_flag; bool version_flag; STYLE style; @@ -30,6 +31,7 @@ const char args_chr[] = { /* [ARG_CHAR_STYLE] = */ 's', /* [ARG_CHAR_COLOR] = */ 'c', /* [ARG_CHAR_HELP] = */ 'h', + /* [ARG_CHAR_RAW] = */ 'r', /* [ARG_CHAR_DEBUG] = */ 'd', /* [ARG_CHAR_VERBOSE] = */ 'v', /* [ARG_CHAR_VERSION] = */ 'V', @@ -39,6 +41,7 @@ const char *args_str[] = { /* [ARG_CHAR_STYLE] = */ "style", /* [ARG_CHAR_COLOR] = */ "color", /* [ARG_CHAR_HELP] = */ "help", + /* [ARG_CHAR_RAW] = */ "raw", /* [ARG_CHAR_DEBUG] = */ "debug", /* [ARG_CHAR_VERBOSE] = */ "verbose", /* [ARG_CHAR_VERSION] = */ "version", @@ -66,6 +69,10 @@ bool show_debug() { return args.debug_flag; } +bool show_raw() { + return args.raw_flag; +} + bool verbose_enabled() { return args.verbose_flag; } @@ -182,8 +189,8 @@ char* build_short_options() { char* str = (char *) malloc(sizeof(char) * (len*2 + 1)); memset(str, 0, sizeof(char) * (len*2 + 1)); - sprintf(str, "%c:%c:%c%c%c%c", - c[ARG_STYLE], c[ARG_COLOR], c[ARG_HELP], + sprintf(str, "%c:%c:%c%c%c%c%c", + c[ARG_STYLE], c[ARG_COLOR], c[ARG_HELP], c[ARG_RAW], c[ARG_DEBUG], c[ARG_VERBOSE], c[ARG_VERSION]); return str; @@ -196,6 +203,7 @@ bool parse_args(int argc, char* argv[]) { bool color_flag = false; args.debug_flag = false; + args.raw_flag = false; args.verbose_flag = false; args.help_flag = false; args.style = STYLE_EMPTY; @@ -205,6 +213,7 @@ bool parse_args(int argc, char* argv[]) { {args_str[ARG_STYLE], required_argument, 0, args_chr[ARG_STYLE] }, {args_str[ARG_COLOR], required_argument, 0, args_chr[ARG_COLOR] }, {args_str[ARG_HELP], no_argument, 0, args_chr[ARG_HELP] }, + {args_str[ARG_RAW], no_argument, 0, args_chr[ARG_RAW] }, {args_str[ARG_DEBUG], no_argument, 0, args_chr[ARG_DEBUG] }, {args_str[ARG_VERBOSE], no_argument, 0, args_chr[ARG_VERBOSE] }, {args_str[ARG_VERSION], no_argument, 0, args_chr[ARG_VERSION] }, @@ -241,6 +250,9 @@ bool parse_args(int argc, char* argv[]) { else if(opt == args_chr[ARG_HELP]) { args.help_flag = true; } + else if(opt == args_chr[ARG_RAW]) { + args.raw_flag = true; + } else if(opt == args_chr[ARG_VERBOSE]) { args.verbose_flag = true; } diff --git a/src/common/args.h b/src/common/args.h index 80dabf7..3614718 100644 --- a/src/common/args.h +++ b/src/common/args.h @@ -30,6 +30,7 @@ enum { ARG_STYLE, ARG_COLOR, ARG_HELP, + ARG_RAW, ARG_DEBUG, ARG_VERBOSE, ARG_VERSION @@ -43,6 +44,7 @@ extern const char *args_str[]; int max_arg_str_length(); bool parse_args(int argc, char* argv[]); bool show_help(); +bool show_raw(); bool show_debug(); bool show_version(); bool verbose_enabled(); diff --git a/src/common/cpu.h b/src/common/cpu.h index 77507f1..b107257 100644 --- a/src/common/cpu.h +++ b/src/common/cpu.h @@ -119,6 +119,8 @@ struct cpuInfo { uint32_t maxLevels; // Max cpuids extended levels uint32_t maxExtendedLevels; + // Topology Extensions (AMD only) + bool topology_extensions; #elif ARCH_ARM // Main ID register uint32_t midr; diff --git a/src/common/main.c b/src/common/main.c index f36dd93..d3a42ca 100644 --- a/src/common/main.c +++ b/src/common/main.c @@ -95,6 +95,17 @@ int main(int argc, char* argv[]) { return EXIT_SUCCESS; } + if(show_raw()) { + #ifdef ARCH_X86 + print_version(); + print_raw(cpu); + return EXIT_SUCCESS; + #else + printErr("raw option is valid only in x86_64"); + return EXIT_FAILURE; + #endif + } + if(print_cpufetch(cpu, get_style(), get_colors())) return EXIT_SUCCESS; else diff --git a/src/x86/apic.h b/src/x86/apic.h index 7e2b907..1b183b4 100644 --- a/src/x86/apic.h +++ b/src/x86/apic.h @@ -17,4 +17,8 @@ struct apic { bool get_topology_from_apic(struct cpuInfo* cpu, struct topology* topo); uint32_t is_smt_enabled_amd(struct topology* topo); +#ifndef __APPLE__ +bool bind_to_cpu(int cpu_id); +#endif + #endif diff --git a/src/x86/cpuid.c b/src/x86/cpuid.c index 4bbba38..1ac93d7 100755 --- a/src/x86/cpuid.c +++ b/src/x86/cpuid.c @@ -310,11 +310,18 @@ struct cpuInfo* get_cpu_info() { printWarn("Can't read cpu name from cpuid (needed extended level is 0x%.8X, max is 0x%.8X)", 0x80000004, cpu->maxExtendedLevels); } + cpu->topology_extensions = false; + if(cpu->cpu_vendor == CPU_VENDOR_AMD && cpu->maxExtendedLevels >= 0x80000001) { + eax = 0x80000001; + cpuid(&eax, &ebx, &ecx, &edx); + cpu->topology_extensions = (ecx >> 22) & 1; + } + cpu->arch = get_cpu_uarch(cpu); cpu->freq = get_frequency_info(cpu); cpu->cach = get_cache_info(cpu); cpu->topo = get_topology_info(cpu, cpu->cach); - + if(cpu->cach == NULL || cpu->topo == NULL) { return NULL; } @@ -322,7 +329,7 @@ struct cpuInfo* get_cpu_info() { } bool get_cache_topology_amd(struct cpuInfo* cpu, struct topology* topo) { - if(cpu->maxExtendedLevels >= 0x8000001D) { + if(cpu->maxExtendedLevels >= 0x8000001D && cpu->topology_extensions) { uint32_t i, eax, ebx, ecx, edx, num_sharing_cache, cache_type, cache_level; i = 0; @@ -379,7 +386,7 @@ bool get_cache_topology_amd(struct cpuInfo* cpu, struct topology* topo) { } while (cache_type > 0); } else { - printWarn("Can't read topology information from cpuid (needed extended level is 0x%.8X, max is 0x%.8X). Guessing cache sizes", 0x8000001D, cpu->maxExtendedLevels); + printWarn("Can't read topology information from cpuid (needed extended level is 0x%.8X, max is 0x%.8X and topology_extensions=%s). Guessing cache topology", 0x8000001D, cpu->maxExtendedLevels, cpu->topology_extensions ? "true" : "false"); topo->cach->L1i->num_caches = topo->physical_cores; topo->cach->L1d->num_caches = topo->physical_cores; @@ -440,13 +447,13 @@ struct topology* get_topology_info(struct cpuInfo* cpu, struct cache* cach) { cpuid(&eax, &ebx, &ecx, &edx); topo->logical_cores = (ecx & 0xFF) + 1; - if (cpu->maxExtendedLevels >= 0x8000001E) { + if (cpu->maxExtendedLevels >= 0x8000001E && cpu->topology_extensions) { eax = 0x8000001E; cpuid(&eax, &ebx, &ecx, &edx); topo->smt_supported = ((ebx >> 8) & 0x03) + 1; } else { - printWarn("Can't read topology information from cpuid (needed extended level is 0x%.8X, max is 0x%.8X)", 0x8000001E, cpu->maxExtendedLevels); + printWarn("Can't read topology information from cpuid (needed extended level is 0x%.8X, max is 0x%.8X and topology_extensions=%s)", 0x8000001E, cpu->maxExtendedLevels, cpu->topology_extensions ? "true" : "false"); topo->smt_supported = 1; } } @@ -610,8 +617,8 @@ struct cache* get_cache_info(struct cpuInfo* cpu) { } else { level = 0x8000001D; - if(cpu->maxExtendedLevels < level) { - printWarn("Can't read cache information from cpuid (needed extended level is 0x%.8X, max is 0x%.8X)", level, cpu->maxExtendedLevels); + if(cpu->maxExtendedLevels < level || !cpu->topology_extensions) { + printWarn("Can't read cache information from cpuid (needed extended level is 0x%.8X, max is 0x%.8X and topology_extensions=%s)", level, cpu->maxExtendedLevels, cpu->topology_extensions ? "true" : "false"); level = 0x80000006; if(cpu->maxExtendedLevels < level) { printWarn("Can't read cache information from cpuid using old method (needed extended level is 0x%.8X, max is 0x%.8X)", level, cpu->maxExtendedLevels); @@ -625,29 +632,6 @@ struct cache* get_cache_info(struct cpuInfo* cpu) { } } - // Sanity checks. If we read values greater than this, they can't be valid ones - // The values were chosen by me - if(cach->L1i->size > 64 * 1024) { - printBug("Invalid L1i size: %dKB", cach->L1i->size/1024); - } - if(cach->L1d->size > 64 * 1024) { - printBug("Invalid L1d size: %dKB", cach->L1d->size/1024); - } - if(cach->L2->exists) { - if(cach->L3->exists && cach->L2->size > 2 * 1048576) { - printBug("Invalid L2 size: %dMB", cach->L2->size/(1048576)); - } - else if(cach->L2->size > 100 * 1048576) { - printBug("Invalid L2 size: %dMB", cach->L2->size/(1048576)); - } - } - if(cach->L3->exists && cach->L3->size > 100 * 1048576) { - printBug("Invalid L3 size: %dMB", cach->L3->size/(1048576)); - } - if(!cach->L2->exists) { - printBug("Could not find L2 cache"); - } - return cach; } @@ -890,14 +874,101 @@ void print_debug(struct cpuInfo* cpu) { cpuid(&eax, &ebx, &ecx, &edx); printf("%s\n", cpu->cpu_name); - if(cpu->hv->present) printf("- Hypervisor: %s\n", cpu->hv->hv_name); + if(cpu->hv->present) { + printf("- Hypervisor: %s\n", cpu->hv->hv_name); + } printf("- Max standard level: 0x%.8X\n", cpu->maxLevels); printf("- Max extended level: 0x%.8X\n", cpu->maxExtendedLevels); + if(cpu->cpu_vendor == CPU_VENDOR_AMD) { + printf("- AMD topology extensions: %d\n", cpu->topology_extensions); + } printf("- CPUID dump: 0x%.8X\n", eax); free_cpuinfo_struct(cpu); } +// TODO: Fix on macOS +// TODO: Query HV and Xeon Phi levels +void print_raw(struct cpuInfo* cpu) { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + printf("%s\n\n", cpu->cpu_name); + printf(" CPUID leaf sub EAX EBX ECX EDX \n"); + printf("--------------------------------------------------------------\n"); + + for(int c=0; c < cpu->topo->total_cores; c++) { + if(!bind_to_cpu(c)) { + printErr("Failed binding to CPU %d", c); + return; + } + + printf("CPU %d:\n", c); + + for(uint32_t reg=0x00000000; reg <= cpu->maxLevels; reg++) { + if(reg == 0x00000004) { + for(uint32_t reg2=0x00000000; reg2 < cpu->cach->max_cache_level; reg2++) { + eax = reg; + ebx = 0; + ecx = reg2; + edx = 0; + + cpuid(&eax, &ebx, &ecx, &edx); + + printf(" 0x%.8X 0x%.2X: 0x%.8X 0x%.8X 0x%.8X 0x%.8X\n", reg, reg2, eax, ebx, ecx, edx); + } + } + else if(reg == 0x0000000B) { + for(uint32_t reg2=0x00000000; reg2 < cpu->topo->smt_supported; reg2++) { + eax = reg; + ebx = 0; + ecx = reg2; + edx = 0; + + cpuid(&eax, &ebx, &ecx, &edx); + + printf(" 0x%.8X 0x%.2X: 0x%.8X 0x%.8X 0x%.8X 0x%.8X\n", reg, reg2, eax, ebx, ecx, edx); + } + } + else { + eax = reg; + ebx = 0; + ecx = 0; + edx = 0; + + cpuid(&eax, &ebx, &ecx, &edx); + + printf(" 0x%.8X 0x%.2X: 0x%.8X 0x%.8X 0x%.8X 0x%.8X\n", reg, 0x00, eax, ebx, ecx, edx); + } + } + for(uint32_t reg=0x80000000; reg <= cpu->maxExtendedLevels; reg++) { + if(reg == 0x8000001D) { + for(uint32_t reg2=0x00000000; reg2 < cpu->cach->max_cache_level; reg2++) { + eax = reg; + ebx = 0; + ecx = reg2; + edx = 0; + + cpuid(&eax, &ebx, &ecx, &edx); + + printf(" 0x%.8X 0x%.2X: 0x%.8X 0x%.8X 0x%.8X 0x%.8X\n", reg, reg2, eax, ebx, ecx, edx); + } + } + else { + eax = reg; + ebx = 0; + ecx = 0; + edx = 0; + + cpuid(&eax, &ebx, &ecx, &edx); + + printf(" 0x%.8X 0x%.2X: 0x%.8X 0x%.8X 0x%.8X 0x%.8X\n", reg, 0x00, eax, ebx, ecx, edx); + } + } + } +} + void free_topo_struct(struct topology* topo) { free(topo->apic->cache_select_mask); free(topo->apic->cache_id_apic); diff --git a/src/x86/cpuid.h b/src/x86/cpuid.h index 4025f64..743ae39 100644 --- a/src/x86/cpuid.h +++ b/src/x86/cpuid.h @@ -15,6 +15,7 @@ char* get_str_topology(struct cpuInfo* cpu, struct topology* topo, bool dual_soc char* get_str_peak_performance(struct cpuInfo* cpu, struct topology* topo, int64_t freq); void print_debug(struct cpuInfo* cpu); +void print_raw(struct cpuInfo* cpu); void free_topo_struct(struct topology* topo);