From 08f79bb9146a02373d04e82944e8f2597bb6ca32 Mon Sep 17 00:00:00 2001 From: Dr-Noob Date: Mon, 6 Jul 2020 01:16:59 +0200 Subject: [PATCH] Fix compilation in Windows and add support for bind to specific cores. Separate APIC code in other file --- Makefile | 6 +- src/apic.c | 245 ++++++++++++++++++++++++++++++++++++++++++++++++ src/apic.h | 17 ++++ src/cpuid.c | 253 +------------------------------------------------- src/cpuid.h | 11 ++- src/printer.c | 8 ++ 6 files changed, 289 insertions(+), 251 deletions(-) create mode 100644 src/apic.c create mode 100644 src/apic.h diff --git a/Makefile b/Makefile index 8db5ffb..9ac8cb2 100644 --- a/Makefile +++ b/Makefile @@ -4,12 +4,14 @@ CXXFLAGS=-Wall -Wextra -Werror -fstack-protector-all -pedantic -Wno-unused -std= SANITY_FLAGS=-Wfloat-equal -Wshadow -Wpointer-arith -Wstrict-overflow=5 -Wformat=2 SRC_DIR=src/ -SOURCE=$(SRC_DIR)main.c $(SRC_DIR)cpuid.c $(SRC_DIR)cpuid_asm.c $(SRC_DIR)printer.c $(SRC_DIR)args.c $(SRC_DIR)global.c -HEADERS=$(SRC_DIR)cpuid.h $(SRC_DIR)cpuid_asm.h $(SRC_DIR)printer.h $(SRC_DIR)ascii.h $(SRC_DIR)args.h $(SRC_DIR)global.h +SOURCE=$(SRC_DIR)main.c $(SRC_DIR)cpuid.c $(SRC_DIR)apic.c $(SRC_DIR)cpuid_asm.c $(SRC_DIR)printer.c $(SRC_DIR)args.c $(SRC_DIR)global.c +HEADERS=$(SRC_DIR)cpuid.h $(SRC_DIR)apic.h $(SRC_DIR)cpuid_asm.h $(SRC_DIR)printer.h $(SRC_DIR)ascii.h $(SRC_DIR)args.h $(SRC_DIR)global.h ifneq ($(OS),Windows_NT) SOURCE += $(SRC_DIR)udev.c HEADERS += $(SRC_DIR)udev.h +else + SANITY_FLAGS += -Wno-pedantic-ms-format endif OUTPUT=cpufetch diff --git a/src/apic.c b/src/apic.c new file mode 100644 index 0000000..792095c --- /dev/null +++ b/src/apic.c @@ -0,0 +1,245 @@ +#ifdef _WIN32 +#include +#else +#define _GNU_SOURCE +#include +#endif + +#include +#include +#include +#include + +#include "apic.h" +#include "cpuid_asm.h" +#include "global.h" + +/* + * bit_scan_reverse and create_mask code taken from: + * https://software.intel.com/content/www/us/en/develop/articles/intel-64-architecture-processor-topology-enumeration.html + */ +unsigned char bit_scan_reverse(uint32_t* index, uint64_t mask) { + for(uint64_t i = (8 * sizeof(uint64_t)); i > 0; i--) { + if((mask & (1LL << (i-1))) != 0) { + *index = (uint64_t) (i-1); + break; + } + } + return (unsigned char) (mask != 0); +} + +uint32_t create_mask(uint32_t num_entries, uint32_t *mask_width) { + uint32_t i; + uint64_t k; + + // NearestPo2(numEntries) is the nearest power of 2 integer that is not less than numEntries + // The most significant bit of (numEntries * 2 -1) matches the above definition + + k = (uint64_t)(num_entries) * 2 -1; + + if (bit_scan_reverse(&i, k) == 0) { + if (mask_width) *mask_width = 0; + return 0; + } + + if (mask_width) *mask_width = i; + if (i == 31) return (uint32_t ) -1; + + return (1 << i) -1; +} + +uint32_t get_apic_id(bool x2apic_id) { + uint32_t eax = 0; + uint32_t ebx = 0; + uint32_t ecx = 0; + uint32_t edx = 0; + + if(x2apic_id) { + eax = 0x0000000B; + cpuid(&eax, &ebx, &ecx, &edx); + return edx; + } + else { + eax = 0x00000001; + cpuid(&eax, &ebx, &ecx, &edx); + return (ebx >> 24); + } +} + +bool bind_to_cpu(int cpu_id) { + #ifdef _WIN32 + HANDLE process = GetCurrentProcess(); + DWORD_PTR processAffinityMask = 1 << cpu_id; + return SetProcessAffinityMask(process, processAffinityMask); + #else + cpu_set_t currentCPU; + CPU_ZERO(¤tCPU); + CPU_SET(cpu_id, ¤tCPU); + if (sched_setaffinity (0, sizeof(currentCPU), ¤tCPU) == -1) { + perror("sched_setaffinity"); + return false; + } + return true; + #endif +} + +bool fill_topo_masks_apic(struct topology** topo) { + uint32_t eax = 0x00000001; + uint32_t ebx = 0; + uint32_t ecx = 0; + uint32_t edx = 0; + uint32_t core_plus_smt_id_max_cnt; + uint32_t core_id_max_cnt; + uint32_t smt_id_per_core_max_cnt; + uint32_t SMTIDPerCoreMaxCnt; + + cpuid(&eax, &ebx, &ecx, &edx); + + core_plus_smt_id_max_cnt = (ebx >> 16) & 0xFF; + + eax = 0x00000004; + ecx = 0; + cpuid(&eax, &ebx, &ecx, &edx); + + core_id_max_cnt = (eax >> 26) + 1; + smt_id_per_core_max_cnt = core_plus_smt_id_max_cnt / core_id_max_cnt; + + (*topo)->apic->smt_mask = create_mask(smt_id_per_core_max_cnt, &((*topo)->apic->smt_mask_width)); + (*topo)->apic->core_mask = create_mask(core_id_max_cnt,&((*topo)->apic->pkg_mask_shift)); + (*topo)->apic->pkg_mask_shift += (*topo)->apic->smt_mask_width; + (*topo)->apic->core_mask <<= (*topo)->apic->smt_mask_width; + (*topo)->apic->pkg_mask = (-1) ^ ((*topo)->apic->core_mask | (*topo)->apic->smt_mask); + + return true; +} + +bool fill_topo_masks_x2apic(struct topology** topo) { + int32_t level_type; + int32_t level_shift; + + int32_t coreplus_smt_mask; + bool level2 = false; + bool level1 = false; + + uint32_t eax = 0; + uint32_t ebx = 0; + uint32_t ecx = 0; + uint32_t edx = 0; + uint32_t i = 0; + + while(true) { + eax = 0x0000000B; + ecx = i; + cpuid(&eax, &ebx, &ecx, &edx); + if(ebx == 0) break; + + level_type = (ecx >> 8) & 0xFF; + level_shift = eax & 0xFFF; + + switch(level_type) { + case 1: // SMT + (*topo)->apic->smt_mask = ~(0xFFFFFFFF << level_shift); + (*topo)->apic->smt_mask_width = level_shift; + (*topo)->smt_supported = ebx & 0xFFFF; + level1 = true; + break; + case 2: // Core + coreplus_smt_mask = ~(0xFFFFFFFF << level_shift); + (*topo)->apic->pkg_mask_shift = level_shift; + (*topo)->apic->pkg_mask = (-1) ^ coreplus_smt_mask; + level2 = true; + break; + default: + printErr("Found invalid level when querying topology: %d", level_type); + break; + } + + i++; // sublevel to query + } + + if (level1 && level2) { + (*topo)->apic->core_mask = coreplus_smt_mask ^ (*topo)->apic->smt_mask; + } + else if (!level2 && level1) { + (*topo)->apic->core_mask = 0; + (*topo)->apic->pkg_mask_shift = (*topo)->apic->smt_mask_width; + (*topo)->apic->pkg_mask = (-1) ^ (*topo)->apic->smt_mask; + } + else { + printErr("SMT level was not found when querying topology"); + return false; + } + + return true; +} + +bool build_topo_from_apic(uint32_t* apic_pkg, uint32_t* apic_core, uint32_t* apic_smt, struct topology** topo) { + uint32_t sockets[64]; + uint32_t smt[64]; + + memset(sockets, 0, sizeof(uint32_t) * 64); + memset(smt, 0, sizeof(uint32_t) * 64); + + for(int i=0; i < (*topo)->total_cores; i++) { + sockets[apic_pkg[i]] = 1; + smt[apic_smt[i]] = 1; + } + for(int i=0; i < 64; i++) { + if(sockets[i] != 0) + (*topo)->sockets++; + if(smt[i] != 0) + (*topo)->smt_available++; + } + + (*topo)->logical_cores = (*topo)->total_cores / (*topo)->sockets; + (*topo)->physical_cores = (*topo)->logical_cores / (*topo)->smt_available; + + return true; +} + +bool get_topology_from_apic(uint32_t cpuid_max_levels, struct topology** topo) { + uint32_t apic_id; + uint32_t* apic_pkg = malloc(sizeof(uint32_t) * (*topo)->total_cores); + uint32_t* apic_core = malloc(sizeof(uint32_t) * (*topo)->total_cores); + uint32_t* apic_smt = malloc(sizeof(uint32_t) * (*topo)->total_cores); + bool x2apic_id = cpuid_max_levels >= 0x0000000B; + + if(x2apic_id) { + if(!fill_topo_masks_x2apic(topo)) + return false; + } + else { + if(!fill_topo_masks_apic(topo)) + return false; + } + + for(int i=0; i < (*topo)->total_cores; i++) { + if(!bind_to_cpu(i)) { + printErr("Failed binding to CPU %d", i); + return false; + } + apic_id = get_apic_id(x2apic_id); + + apic_pkg[i] = (apic_id & (*topo)->apic->pkg_mask) >> (*topo)->apic->pkg_mask_shift; + apic_core[i] = (apic_id & (*topo)->apic->core_mask) >> (*topo)->apic->smt_mask_width; + apic_smt[i] = apic_id & (*topo)->apic->smt_mask; + } + + /* DEBUG + for(int i=0; i < (*topo)->total_cores; i++) + printf("[%2d] 0x%.8X\n", i, apic_pkg[i]); + printf("\n"); + for(int i=0; i < (*topo)->total_cores; i++) + printf("[%2d] 0x%.8X\n", i, apic_core[i]); + printf("\n"); + for(int i=0; i < (*topo)->total_cores; i++) + printf("[%2d] 0x%.8X\n", i, apic_smt[i]);*/ + + + bool ret = build_topo_from_apic(apic_pkg, apic_core, apic_smt, topo); + + // Assumption: If we cant get smt_available, we assume it is equal to smt_supported... + if(!x2apic_id) (*topo)->smt_supported = (*topo)->smt_available; + + return ret; +} diff --git a/src/apic.h b/src/apic.h new file mode 100644 index 0000000..afbb0dd --- /dev/null +++ b/src/apic.h @@ -0,0 +1,17 @@ +#ifndef __APIC__ +#define __APIC__ + +#include +#include "cpuid.h" + +struct apic { + uint32_t pkg_mask; + uint32_t pkg_mask_shift; + uint32_t core_mask; + uint32_t smt_mask_width; + uint32_t smt_mask; +}; + +bool get_topology_from_apic(uint32_t cpuid_max_levels, struct topology** topo); + +#endif diff --git a/src/cpuid.c b/src/cpuid.c index 4ff301a..8163a5e 100644 --- a/src/cpuid.c +++ b/src/cpuid.c @@ -1,10 +1,8 @@ #ifdef _WIN32 -#include + #include #else -#define _GNU_SOURCE -#include -#include -#include "udev.h" + #include "udev.h" + #include #endif #include @@ -16,6 +14,7 @@ #include "cpuid.h" #include "cpuid_asm.h" #include "global.h" +#include "apic.h" #define VENDOR_INTEL_STRING "GenuineIntel" #define VENDOR_AMD_STRING "AuthenticAMD" @@ -75,24 +74,6 @@ struct frequency { int64_t max; }; -struct apic { - uint32_t pkg_mask; - uint32_t pkg_mask_shift; - uint32_t core_mask; - uint32_t smt_mask_width; - uint32_t smt_mask; -}; - -struct topology { - int64_t total_cores; - uint32_t physical_cores; - uint32_t logical_cores; - uint32_t smt_available; // Number of SMT that is currently enabled - uint32_t smt_supported; // Number of SMT that CPU supports (equal to smt_available if SMT is enabled) - uint32_t sockets; - struct apic* apic; -}; - void init_cpu_info(struct cpuInfo* cpu) { cpu->AVX = false; cpu->AVX2 = false; @@ -281,230 +262,6 @@ struct cpuInfo* get_cpu_info() { return cpu; } -bool bind_to_cpu(int cpu_id) { - #ifdef _WIN32 // TODO - return false; - #else - cpu_set_t currentCPU; - CPU_ZERO(¤tCPU); - CPU_SET(cpu_id, ¤tCPU); - if (sched_setaffinity (0, sizeof(currentCPU), ¤tCPU) == -1) { - perror("sched_setaffinity"); - return false; - } - return true; - #endif -} - -uint32_t get_apic_id(bool x2apic_id) { - uint32_t eax = 0; - uint32_t ebx = 0; - uint32_t ecx = 0; - uint32_t edx = 0; - - if(x2apic_id) { - eax = 0x0000000B; - cpuid(&eax, &ebx, &ecx, &edx); - return edx; - } - else { - eax = 0x00000001; - cpuid(&eax, &ebx, &ecx, &edx); - return (ebx >> 24); - } -} - -bool fill_topo_masks_x2apic(struct topology** topo) { - int32_t level_type; - int32_t level_shift; - - int32_t coreplus_smt_mask; - bool level2 = false; - bool level1 = false; - - uint32_t eax = 0; - uint32_t ebx = 0; - uint32_t ecx = 0; - uint32_t edx = 0; - uint32_t i = 0; - - while(true) { - eax = 0x0000000B; - ecx = i; - cpuid(&eax, &ebx, &ecx, &edx); - if(ebx == 0) break; - - level_type = (ecx >> 8) & 0xFF; - level_shift = eax & 0xFFF; - - switch(level_type) { - case 1: // SMT - (*topo)->apic->smt_mask = ~(0xFFFFFFFF << level_shift); - (*topo)->apic->smt_mask_width = level_shift; - (*topo)->smt_supported = ebx & 0xFFFF; - level1 = true; - break; - case 2: // Core - coreplus_smt_mask = ~(0xFFFFFFFF << level_shift); - (*topo)->apic->pkg_mask_shift = level_shift; - (*topo)->apic->pkg_mask = (-1) ^ coreplus_smt_mask; - level2 = true; - break; - default: - printErr("Found invalid level when querying topology: %d", level_type); - break; - } - - i++; // sublevel to query - } - - if (level1 && level2) { - (*topo)->apic->core_mask = coreplus_smt_mask ^ (*topo)->apic->smt_mask; - } - else if (!level2 && level1) { - (*topo)->apic->core_mask = 0; - (*topo)->apic->pkg_mask_shift = (*topo)->apic->smt_mask_width; - (*topo)->apic->pkg_mask = (-1) ^ (*topo)->apic->smt_mask; - } - else { - printErr("SMT level was not found when querying topology"); - return false; - } - - return true; -} - -unsigned char bit_scan_reverse(uint32_t* index, uint64_t mask) { - for(uint64_t i = (8 * sizeof(uint64_t)); i > 0; i--) { - if((mask & (1LL << (i-1))) != 0) { - *index = (uint64_t) (i-1); - break; - } - } - return (unsigned char)( mask != 0); -} - -uint32_t create_mask(uint32_t num_entries, uint32_t *mask_width) { - uint32_t i; - uint64_t k; - - // NearestPo2(numEntries) is the nearest power of 2 integer that is not less than numEntries - // The most significant bit of (numEntries * 2 -1) matches the above definition - - k = (uint64_t)(num_entries) * 2 -1; - - if (bit_scan_reverse(&i, k) == 0) { - if (mask_width) *mask_width = 0; - return 0; - } - - if (mask_width) *mask_width = i; - if (i == 31) return (uint32_t ) -1; - - return (1 << i) -1; -} - -bool fill_topo_masks_apic(struct topology** topo) { - uint32_t eax = 0x00000001; - uint32_t ebx = 0; - uint32_t ecx = 0; - uint32_t edx = 0; - uint32_t core_plus_smt_id_max_cnt; - uint32_t core_id_max_cnt; - uint32_t smt_id_per_core_max_cnt; - uint32_t SMTIDPerCoreMaxCnt; - - cpuid(&eax, &ebx, &ecx, &edx); - - core_plus_smt_id_max_cnt = (ebx >> 16) & 0xFF; // MAL? - - eax = 0x00000004; - ecx = 0; - cpuid(&eax, &ebx, &ecx, &edx); - - core_id_max_cnt = (eax >> 26) + 1; - smt_id_per_core_max_cnt = core_plus_smt_id_max_cnt / core_id_max_cnt; - - (*topo)->apic->smt_mask = create_mask(smt_id_per_core_max_cnt, &((*topo)->apic->smt_mask_width)); - (*topo)->apic->core_mask = create_mask(core_id_max_cnt,&((*topo)->apic->pkg_mask_shift)); - (*topo)->apic->pkg_mask_shift += (*topo)->apic->smt_mask_width; - (*topo)->apic->core_mask <<= (*topo)->apic->smt_mask_width; - (*topo)->apic->pkg_mask = (-1) ^ ((*topo)->apic->core_mask | (*topo)->apic->smt_mask); - - return true; -} - -bool build_topo_from_apic(uint32_t* apic_pkg, uint32_t* apic_core, uint32_t* apic_smt, struct topology** topo) { - uint32_t sockets[64]; - uint32_t smt[64]; - - memset(sockets, 0, sizeof(uint32_t) * 64); - memset(smt, 0, sizeof(uint32_t) * 64); - - for(int i=0; i < (*topo)->total_cores; i++) { - sockets[apic_pkg[i]] = 1; - smt[apic_smt[i]] = 1; - } - for(int i=0; i < 64; i++) { - if(sockets[i] != 0) - (*topo)->sockets++; - if(smt[i] != 0) - (*topo)->smt_available++; - } - - (*topo)->logical_cores = (*topo)->total_cores / (*topo)->sockets; - (*topo)->physical_cores = (*topo)->logical_cores / (*topo)->smt_available; - - return true; -} - -bool get_topology_from_apic(struct cpuInfo* cpu, struct topology** topo) { - uint32_t apic_id; - uint32_t* apic_pkg = malloc(sizeof(uint32_t) * (*topo)->total_cores); - uint32_t* apic_core = malloc(sizeof(uint32_t) * (*topo)->total_cores); - uint32_t* apic_smt = malloc(sizeof(uint32_t) * (*topo)->total_cores); - bool x2apic_id = cpu->maxLevels >= 0x0000000B; - - if(x2apic_id) { - if(!fill_topo_masks_x2apic(topo)) - return false; - } - else { - if(!fill_topo_masks_apic(topo)) - return false; - } - - for(int i=0; i < (*topo)->total_cores; i++) { - if(!bind_to_cpu(i)) { - printErr("Failed binding to CPU %d", i); - return false; - } - apic_id = get_apic_id(x2apic_id); - - apic_pkg[i] = (apic_id & (*topo)->apic->pkg_mask) >> (*topo)->apic->pkg_mask_shift; - apic_core[i] = (apic_id & (*topo)->apic->core_mask) >> (*topo)->apic->smt_mask_width; - apic_smt[i] = apic_id & (*topo)->apic->smt_mask; - } - - /* DEBUG - for(int i=0; i < (*topo)->total_cores; i++) - printf("[%2d] 0x%.8X\n", i, apic_pkg[i]); - printf("\n"); - for(int i=0; i < (*topo)->total_cores; i++) - printf("[%2d] 0x%.8X\n", i, apic_core[i]); - printf("\n"); - for(int i=0; i < (*topo)->total_cores; i++) - printf("[%2d] 0x%.8X\n", i, apic_smt[i]);*/ - - - bool ret = build_topo_from_apic(apic_pkg, apic_core, apic_smt, topo); - - // Assumption: If we cant get smt_available, we assume it is equal to smt_supported... - if(!x2apic_id) (*topo)->smt_supported = (*topo)->smt_available; - - return ret; -} - // Main reference: https://software.intel.com/content/www/us/en/develop/articles/intel-64-architecture-processor-topology-enumeration.html // Very interesting resource: https://wiki.osdev.org/Detecting_CPU_Topology_(80x86) struct topology* get_topology_info(struct cpuInfo* cpu) { @@ -535,7 +292,7 @@ struct topology* get_topology_info(struct cpuInfo* cpu) { switch(cpu->cpu_vendor) { case VENDOR_INTEL: if (cpu->maxLevels >= 0x00000004) { - get_topology_from_apic(cpu, &topo); + get_topology_from_apic(cpu->maxLevels, &topo); } else { printErr("Can't read topology information from cpuid (needed level is 0x%.8X, max is 0x%.8X)", 0x00000001, cpu->maxLevels); diff --git a/src/cpuid.h b/src/cpuid.h index 71263f0..b7e3006 100644 --- a/src/cpuid.h +++ b/src/cpuid.h @@ -13,7 +13,16 @@ struct cpuInfo; struct frequency; struct cache; -struct topology; + +struct topology { + int64_t total_cores; + uint32_t physical_cores; + uint32_t logical_cores; + uint32_t smt_available; // Number of SMT that is currently enabled + uint32_t smt_supported; // Number of SMT that CPU supports (equal to smt_available if SMT is enabled) + uint32_t sockets; + struct apic* apic; +}; typedef int32_t VENDOR; diff --git a/src/printer.c b/src/printer.c index f8601a8..76d8db6 100644 --- a/src/printer.c +++ b/src/printer.c @@ -147,6 +147,13 @@ struct ascii* set_ascii(VENDOR cpuVendor, STYLE style, struct colors* cs) { } art->ascii_chars[1] = '#'; + #ifdef _WIN32 + strcpy(art->color1_ascii,COL_NONE); + strcpy(art->color2_ascii,COL_NONE); + strcpy(art->color1_text,COL_NONE); + strcpy(art->color2_text,COL_NONE); + art->reset[0] = '\0'; + #else switch(style) { case STYLE_EMPTY: #ifdef _WIN32 @@ -200,6 +207,7 @@ struct ascii* set_ascii(VENDOR cpuVendor, STYLE style, struct colors* cs) { printBug("Found invalid style (%d)",style); return NULL; } + #endif char tmp[NUMBER_OF_LINES*LINE_SIZE]; if(cpuVendor == VENDOR_INTEL) strcpy(tmp, INTEL_ASCII);