linux/tools/power/cpupower/utils/helpers/cpuid.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <stdio.h>
   3#include <errno.h>
   4#include <string.h>
   5#include <unistd.h>
   6#include <stdlib.h>
   7
   8#include "helpers/helpers.h"
   9
  10static const char *cpu_vendor_table[X86_VENDOR_MAX] = {
  11        "Unknown", "GenuineIntel", "AuthenticAMD", "HygonGenuine",
  12};
  13
  14#if defined(__i386__) || defined(__x86_64__)
  15
  16/* from gcc */
  17#include <cpuid.h>
  18
  19/*
  20 * CPUID functions returning a single datum
  21 *
  22 * Define unsigned int cpuid_e[abcd]x(unsigned int op)
  23 */
  24#define cpuid_func(reg)                                 \
  25        unsigned int cpuid_##reg(unsigned int op)       \
  26        {                                               \
  27        unsigned int eax, ebx, ecx, edx;                \
  28        __cpuid(op, eax, ebx, ecx, edx);                \
  29        return reg;                                     \
  30        }
  31cpuid_func(eax);
  32cpuid_func(ebx);
  33cpuid_func(ecx);
  34cpuid_func(edx);
  35
  36#endif /* defined(__i386__) || defined(__x86_64__) */
  37
  38/* get_cpu_info
  39 *
  40 * Extract CPU vendor, family, model, stepping info from /proc/cpuinfo
  41 *
  42 * Returns 0 on success or a negativ error code
  43 *
  44 * TBD: Should there be a cpuid alternative for this if /proc is not mounted?
  45 */
  46int get_cpu_info(struct cpupower_cpu_info *cpu_info)
  47{
  48        FILE *fp;
  49        char value[64];
  50        unsigned int proc, x;
  51        unsigned int unknown = 0xffffff;
  52        unsigned int cpuid_level, ext_cpuid_level;
  53
  54        int ret = -EINVAL;
  55
  56        cpu_info->vendor                = X86_VENDOR_UNKNOWN;
  57        cpu_info->family                = unknown;
  58        cpu_info->model                 = unknown;
  59        cpu_info->stepping              = unknown;
  60        cpu_info->caps                  = 0;
  61
  62        fp = fopen("/proc/cpuinfo", "r");
  63        if (!fp)
  64                return -EIO;
  65
  66        while (!feof(fp)) {
  67                if (!fgets(value, 64, fp))
  68                        continue;
  69                value[63 - 1] = '\0';
  70
  71                if (!strncmp(value, "processor\t: ", 12))
  72                        sscanf(value, "processor\t: %u", &proc);
  73
  74                if (proc != (unsigned int)base_cpu)
  75                        continue;
  76
  77                /* Get CPU vendor */
  78                if (!strncmp(value, "vendor_id", 9)) {
  79                        for (x = 1; x < X86_VENDOR_MAX; x++) {
  80                                if (strstr(value, cpu_vendor_table[x]))
  81                                        cpu_info->vendor = x;
  82                        }
  83                /* Get CPU family, etc. */
  84                } else if (!strncmp(value, "cpu family\t: ", 13)) {
  85                        sscanf(value, "cpu family\t: %u",
  86                               &cpu_info->family);
  87                } else if (!strncmp(value, "model\t\t: ", 9)) {
  88                        sscanf(value, "model\t\t: %u",
  89                               &cpu_info->model);
  90                } else if (!strncmp(value, "stepping\t: ", 10)) {
  91                        sscanf(value, "stepping\t: %u",
  92                               &cpu_info->stepping);
  93
  94                        /* Exit -> all values must have been set */
  95                        if (cpu_info->vendor == X86_VENDOR_UNKNOWN ||
  96                            cpu_info->family == unknown ||
  97                            cpu_info->model == unknown ||
  98                            cpu_info->stepping == unknown) {
  99                                ret = -EINVAL;
 100                                goto out;
 101                        }
 102
 103                        ret = 0;
 104                        goto out;
 105                }
 106        }
 107        ret = -ENODEV;
 108out:
 109        fclose(fp);
 110        /* Get some useful CPU capabilities from cpuid */
 111        if (cpu_info->vendor != X86_VENDOR_AMD &&
 112            cpu_info->vendor != X86_VENDOR_HYGON &&
 113            cpu_info->vendor != X86_VENDOR_INTEL)
 114                return ret;
 115
 116        cpuid_level     = cpuid_eax(0);
 117        ext_cpuid_level = cpuid_eax(0x80000000);
 118
 119        /* Invariant TSC */
 120        if (ext_cpuid_level >= 0x80000007 &&
 121            (cpuid_edx(0x80000007) & (1 << 8)))
 122                cpu_info->caps |= CPUPOWER_CAP_INV_TSC;
 123
 124        /* Aperf/Mperf registers support */
 125        if (cpuid_level >= 6 && (cpuid_ecx(6) & 0x1))
 126                cpu_info->caps |= CPUPOWER_CAP_APERF;
 127
 128        /* AMD or Hygon Boost state enable/disable register */
 129        if (cpu_info->vendor == X86_VENDOR_AMD ||
 130            cpu_info->vendor == X86_VENDOR_HYGON) {
 131                if (ext_cpuid_level >= 0x80000007) {
 132                        if (cpuid_edx(0x80000007) & (1 << 9)) {
 133                                cpu_info->caps |= CPUPOWER_CAP_AMD_CPB;
 134
 135                                if (cpu_info->family >= 0x17)
 136                                        cpu_info->caps |= CPUPOWER_CAP_AMD_CPB_MSR;
 137                        }
 138
 139                        if ((cpuid_edx(0x80000007) & (1 << 7)) &&
 140                            cpu_info->family != 0x14) {
 141                                /* HW pstate was not implemented in family 0x14 */
 142                                cpu_info->caps |= CPUPOWER_CAP_AMD_HW_PSTATE;
 143
 144                                if (cpu_info->family >= 0x17)
 145                                        cpu_info->caps |= CPUPOWER_CAP_AMD_PSTATEDEF;
 146                        }
 147                }
 148
 149                if (ext_cpuid_level >= 0x80000008 &&
 150                    cpuid_ebx(0x80000008) & (1 << 4))
 151                        cpu_info->caps |= CPUPOWER_CAP_AMD_RDPRU;
 152        }
 153
 154        if (cpu_info->vendor == X86_VENDOR_INTEL) {
 155                if (cpuid_level >= 6 &&
 156                    (cpuid_eax(6) & (1 << 1)))
 157                        cpu_info->caps |= CPUPOWER_CAP_INTEL_IDA;
 158        }
 159
 160        if (cpu_info->vendor == X86_VENDOR_INTEL) {
 161                /* Intel's perf-bias MSR support */
 162                if (cpuid_level >= 6 && (cpuid_ecx(6) & (1 << 3)))
 163                        cpu_info->caps |= CPUPOWER_CAP_PERF_BIAS;
 164
 165                /* Intel's Turbo Ratio Limit support */
 166                if (cpu_info->family == 6) {
 167                        switch (cpu_info->model) {
 168                        case 0x1A:      /* Core i7, Xeon 5500 series
 169                                         * Bloomfield, Gainstown NHM-EP
 170                                         */
 171                        case 0x1E:      /* Core i7 and i5 Processor
 172                                         * Clarksfield, Lynnfield, Jasper Forest
 173                                         */
 174                        case 0x1F:      /* Core i7 and i5 Processor - Nehalem */
 175                        case 0x25:      /* Westmere Client
 176                                         * Clarkdale, Arrandale
 177                                         */
 178                        case 0x2C:      /* Westmere EP - Gulftown */
 179                                cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO;
 180                                break;
 181                        case 0x2A:      /* SNB */
 182                        case 0x2D:      /* SNB Xeon */
 183                        case 0x3A:      /* IVB */
 184                        case 0x3E:      /* IVB Xeon */
 185                                cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO;
 186                                cpu_info->caps |= CPUPOWER_CAP_IS_SNB;
 187                                break;
 188                        case 0x2E:      /* Nehalem-EX Xeon - Beckton */
 189                        case 0x2F:      /* Westmere-EX Xeon - Eagleton */
 190                        default:
 191                                break;
 192                        }
 193                }
 194        }
 195
 196        /*      printf("ID: %u - Extid: 0x%x - Caps: 0x%llx\n",
 197                cpuid_level, ext_cpuid_level, cpu_info->caps);
 198        */
 199        return ret;
 200}
 201