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