Home | History | Annotate | Line # | Download | only in common
cpuid.c revision 1.1.1.1.2.1
      1  1.1.1.1.2.1  perseant /* Copyright (C) 2021-2024 Free Software Foundation, Inc.
      2          1.1  christos    Contributed by Oracle.
      3          1.1  christos 
      4          1.1  christos    This file is part of GNU Binutils.
      5          1.1  christos 
      6          1.1  christos    This program is free software; you can redistribute it and/or modify
      7          1.1  christos    it under the terms of the GNU General Public License as published by
      8          1.1  christos    the Free Software Foundation; either version 3, or (at your option)
      9          1.1  christos    any later version.
     10          1.1  christos 
     11          1.1  christos    This program is distributed in the hope that it will be useful,
     12          1.1  christos    but WITHOUT ANY WARRANTY; without even the implied warranty of
     13          1.1  christos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14          1.1  christos    GNU General Public License for more details.
     15          1.1  christos 
     16          1.1  christos    You should have received a copy of the GNU General Public License
     17          1.1  christos    along with this program; if not, write to the Free Software
     18          1.1  christos    Foundation, 51 Franklin Street - Fifth Floor, Boston,
     19          1.1  christos    MA 02110-1301, USA.  */
     20          1.1  christos 
     21          1.1  christos #if defined(__i386__) || defined(__x86_64)
     22          1.1  christos #include <cpuid.h>  /* GCC-provided */
     23          1.1  christos #elif defined(__aarch64__)
     24          1.1  christos #define ATTRIBUTE_UNUSED __attribute__((unused))
     25          1.1  christos 
     26          1.1  christos static inline uint_t __attribute_const__
     27          1.1  christos __get_cpuid (unsigned int op ATTRIBUTE_UNUSED, unsigned int *eax,
     28          1.1  christos 	     unsigned int *ebx ATTRIBUTE_UNUSED,
     29          1.1  christos 	     unsigned int *ecx ATTRIBUTE_UNUSED, unsigned int *edx ATTRIBUTE_UNUSED)
     30          1.1  christos {
     31          1.1  christos   // CPUID bit assignments:
     32          1.1  christos   // [31:24] IMPLEMENTER (0x50 - ARM_CPU_IMP_APM)
     33          1.1  christos   // [23:20] VARIANT indicates processor revision (0x2 = Revision 2)
     34          1.1  christos   // [19:16] Constant (Reads as 0xF)
     35          1.1  christos   // [15:04] PARTNO indicates part number (0xC23 = Cortex-M3)
     36          1.1  christos   // [03:00] REVISION indicates patch release (0x0 = Patch 0)
     37          1.1  christos   //    unsigned long v = 0;
     38          1.1  christos   //    __asm volatile ("MRS %[result], MPIDR_EL1" : [result] "=r" (v));
     39          1.1  christos   //    Tprintf(DBG_LT0, "cpuid.c:%d read_cpuid_id() MPIDR_EL1=0x%016lx\n", __LINE__, v);
     40          1.1  christos   uint_t res = 0;
     41          1.1  christos   __asm volatile ("MRS %[result], MIDR_EL1" : [result] "=r" (*eax));
     42          1.1  christos   Tprintf (DBG_LT0, "cpuid.c:%d read_cpuid_id() MIDR_EL1=0x%016x\n", __LINE__, *eax);
     43          1.1  christos   return res;
     44          1.1  christos }
     45          1.1  christos #endif
     46          1.1  christos 
     47          1.1  christos /*
     48          1.1  christos  * Various routines to handle identification
     49          1.1  christos  * and classification of x86 processors.
     50          1.1  christos  */
     51          1.1  christos 
     52          1.1  christos #define IS_GLOBAL /* externally visible */
     53          1.1  christos #define	X86_VENDOR_Intel	0
     54          1.1  christos #define	X86_VENDORSTR_Intel	"GenuineIntel"
     55          1.1  christos #define	X86_VENDOR_IntelClone	1
     56          1.1  christos #define	X86_VENDOR_AMD		2
     57          1.1  christos #define	X86_VENDORSTR_AMD	"AuthenticAMD"
     58          1.1  christos 
     59          1.1  christos #define BITX(u, h, l)       (((u) >> (l)) & ((1LU << ((h) - (l) + 1LU)) - 1LU))
     60          1.1  christos #define CPI_FAMILY_XTD(reg) BITX(reg, 27, 20)
     61          1.1  christos #define CPI_MODEL_XTD(reg)  BITX(reg, 19, 16)
     62          1.1  christos #define CPI_TYPE(reg)       BITX(reg, 13, 12)
     63          1.1  christos #define CPI_FAMILY(reg)     BITX(reg, 11, 8)
     64          1.1  christos #define CPI_STEP(reg)       BITX(reg, 3, 0)
     65          1.1  christos #define CPI_MODEL(reg)      BITX(reg, 7, 4)
     66          1.1  christos #define IS_EXTENDED_MODEL_INTEL(model)  ((model) == 0x6 || (model) >= 0xf)
     67          1.1  christos 
     68          1.1  christos 
     69          1.1  christos typedef struct
     70          1.1  christos {
     71          1.1  christos   unsigned int eax;
     72          1.1  christos   unsigned int ebx;
     73          1.1  christos   unsigned int ecx;
     74          1.1  christos   unsigned int edx;
     75          1.1  christos } cpuid_regs_t;
     76          1.1  christos 
     77          1.1  christos typedef struct
     78          1.1  christos {
     79          1.1  christos   unsigned int cpi_model;
     80          1.1  christos   unsigned int cpi_family;
     81          1.1  christos   unsigned int cpi_vendor;        /* enum of cpi_vendorstr */
     82          1.1  christos   unsigned int cpi_maxeax;        /* fn 0: %eax */
     83          1.1  christos   char cpi_vendorstr[13];         /* fn 0: %ebx:%ecx:%edx */
     84          1.1  christos } cpuid_info_t;
     85          1.1  christos 
     86          1.1  christos 
     87          1.1  christos #if defined(__i386__) || defined(__x86_64)
     88          1.1  christos static uint_t
     89          1.1  christos cpuid_vendorstr_to_vendorcode (char *vendorstr)
     90          1.1  christos {
     91          1.1  christos   if (strcmp (vendorstr, X86_VENDORSTR_Intel) == 0)
     92          1.1  christos     return X86_VENDOR_Intel;
     93          1.1  christos   else if (strcmp (vendorstr, X86_VENDORSTR_AMD) == 0)
     94          1.1  christos     return X86_VENDOR_AMD;
     95          1.1  christos   else
     96          1.1  christos     return X86_VENDOR_IntelClone;
     97          1.1  christos }
     98          1.1  christos 
     99          1.1  christos static int
    100          1.1  christos my_cpuid (unsigned int op, cpuid_regs_t *regs)
    101          1.1  christos {
    102          1.1  christos   regs->eax = regs->ebx = regs->ecx = regs->edx = 0;
    103          1.1  christos   int ret = __get_cpuid (op, &regs->eax, &regs->ebx, &regs->ecx, &regs->edx);
    104          1.1  christos   TprintfT (DBG_LT1, "my_cpuid: __get_cpuid(0x%x, 0x%x, 0x%x, 0x%x, 0x%x) returns %d\n",
    105          1.1  christos 	    op, regs->eax, regs->ebx, regs->ecx, regs->edx, ret);
    106          1.1  christos   return ret;
    107          1.1  christos }
    108          1.1  christos #endif
    109          1.1  christos 
    110          1.1  christos static cpuid_info_t *
    111          1.1  christos get_cpuid_info ()
    112          1.1  christos {
    113          1.1  christos   static int cpuid_inited = 0;
    114          1.1  christos   static cpuid_info_t cpuid_info;
    115          1.1  christos   cpuid_info_t *cpi = &cpuid_info;
    116          1.1  christos   if (cpuid_inited)
    117          1.1  christos     return cpi;
    118          1.1  christos   cpuid_inited = 1;
    119          1.1  christos 
    120          1.1  christos #if defined(__aarch64__)
    121          1.1  christos   // CPUID bit assignments:
    122          1.1  christos   // [31:24] IMPLEMENTER (0x50 - ARM_CPU_IMP_APM)
    123          1.1  christos   // [23:20] VARIANT indicates processor revision (0x2 = Revision 2)
    124          1.1  christos   // [19:16] Constant (Reads as 0xF)
    125          1.1  christos   // [15:04] PARTNO indicates part number (0xC23 = Cortex-M3)
    126          1.1  christos   // [03:00] REVISION indicates patch release (0x0 = Patch 0)
    127          1.1  christos   uint_t reg = 0;
    128          1.1  christos   __asm volatile ("MRS %[result], MIDR_EL1" : [result] "=r" (reg));
    129          1.1  christos   cpi->cpi_vendor = reg >> 24;
    130          1.1  christos   cpi->cpi_model = (reg >> 4) & 0xfff;
    131          1.1  christos   switch (cpi->cpi_vendor)
    132          1.1  christos     {
    133          1.1  christos     case ARM_CPU_IMP_APM:
    134          1.1  christos     case ARM_CPU_IMP_ARM:
    135          1.1  christos     case ARM_CPU_IMP_CAVIUM:
    136          1.1  christos     case ARM_CPU_IMP_BRCM:
    137          1.1  christos     case ARM_CPU_IMP_QCOM:
    138          1.1  christos       strncpy (cpi->cpi_vendorstr, AARCH64_VENDORSTR_ARM, sizeof (cpi->cpi_vendorstr));
    139          1.1  christos       break;
    140          1.1  christos     default:
    141          1.1  christos       strncpy (cpi->cpi_vendorstr, "UNKNOWN ARM", sizeof (cpi->cpi_vendorstr));
    142          1.1  christos       break;
    143          1.1  christos     }
    144          1.1  christos   Tprintf (DBG_LT0, "cpuid.c:%d read_cpuid_id() MIDR_EL1==0x%016x cpi_vendor=%d cpi_model=%d\n",
    145          1.1  christos 	   __LINE__, (unsigned int) reg, cpi->cpi_vendor, cpi->cpi_model);
    146          1.1  christos 
    147          1.1  christos #elif defined(__i386__) || defined(__x86_64)
    148          1.1  christos   cpuid_regs_t regs;
    149          1.1  christos   my_cpuid (0, &regs);
    150          1.1  christos   cpi->cpi_maxeax = regs.eax;
    151          1.1  christos   ((uint32_t *) cpi->cpi_vendorstr)[0] = regs.ebx;
    152          1.1  christos   ((uint32_t *) cpi->cpi_vendorstr)[1] = regs.edx;
    153          1.1  christos   ((uint32_t *) cpi->cpi_vendorstr)[2] = regs.ecx;
    154          1.1  christos   cpi->cpi_vendorstr[12] = 0;
    155          1.1  christos   cpi->cpi_vendor = cpuid_vendorstr_to_vendorcode (cpi->cpi_vendorstr);
    156          1.1  christos 
    157          1.1  christos   my_cpuid (1, &regs);
    158          1.1  christos   cpi->cpi_model = CPI_MODEL (regs.eax);
    159          1.1  christos   cpi->cpi_family = CPI_FAMILY (regs.eax);
    160          1.1  christos   if (cpi->cpi_family == 0xf)
    161          1.1  christos     cpi->cpi_family += CPI_FAMILY_XTD (regs.eax);
    162          1.1  christos 
    163          1.1  christos   /*
    164          1.1  christos    * Beware: AMD uses "extended model" iff base *FAMILY* == 0xf.
    165          1.1  christos    * Intel, and presumably everyone else, uses model == 0xf, as
    166          1.1  christos    * one would expect (max value means possible overflow).  Sigh.
    167          1.1  christos    */
    168          1.1  christos   switch (cpi->cpi_vendor)
    169          1.1  christos     {
    170          1.1  christos     case X86_VENDOR_Intel:
    171          1.1  christos       if (IS_EXTENDED_MODEL_INTEL (cpi->cpi_family))
    172          1.1  christos 	cpi->cpi_model += CPI_MODEL_XTD (regs.eax) << 4;
    173          1.1  christos       break;
    174          1.1  christos     case X86_VENDOR_AMD:
    175          1.1  christos       if (CPI_FAMILY (cpi->cpi_family) == 0xf)
    176          1.1  christos 	cpi->cpi_model += CPI_MODEL_XTD (regs.eax) << 4;
    177          1.1  christos       break;
    178          1.1  christos     default:
    179          1.1  christos       if (cpi->cpi_model == 0xf)
    180          1.1  christos 	cpi->cpi_model += CPI_MODEL_XTD (regs.eax) << 4;
    181          1.1  christos       break;
    182          1.1  christos     }
    183          1.1  christos #endif
    184          1.1  christos   return cpi;
    185          1.1  christos }
    186          1.1  christos 
    187          1.1  christos static inline uint_t
    188          1.1  christos cpuid_getvendor ()
    189          1.1  christos {
    190          1.1  christos   return get_cpuid_info ()->cpi_vendor;
    191          1.1  christos }
    192          1.1  christos 
    193          1.1  christos static inline uint_t
    194          1.1  christos cpuid_getfamily ()
    195          1.1  christos {
    196          1.1  christos   return get_cpuid_info ()->cpi_family;
    197          1.1  christos }
    198          1.1  christos 
    199          1.1  christos static inline uint_t
    200          1.1  christos cpuid_getmodel ()
    201          1.1  christos {
    202          1.1  christos   return get_cpuid_info ()->cpi_model;
    203          1.1  christos }
    204