xref: /netbsd-src/external/gpl3/binutils/dist/gprofng/common/cpuid.c (revision cb63e24e8d6aae7ddac1859a9015f48b1d8bd90e)
1*cb63e24eSchristos /* Copyright (C) 2021-2024 Free Software Foundation, Inc.
24f645668Schristos    Contributed by Oracle.
34f645668Schristos 
44f645668Schristos    This file is part of GNU Binutils.
54f645668Schristos 
64f645668Schristos    This program is free software; you can redistribute it and/or modify
74f645668Schristos    it under the terms of the GNU General Public License as published by
84f645668Schristos    the Free Software Foundation; either version 3, or (at your option)
94f645668Schristos    any later version.
104f645668Schristos 
114f645668Schristos    This program is distributed in the hope that it will be useful,
124f645668Schristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
134f645668Schristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
144f645668Schristos    GNU General Public License for more details.
154f645668Schristos 
164f645668Schristos    You should have received a copy of the GNU General Public License
174f645668Schristos    along with this program; if not, write to the Free Software
184f645668Schristos    Foundation, 51 Franklin Street - Fifth Floor, Boston,
194f645668Schristos    MA 02110-1301, USA.  */
204f645668Schristos 
214f645668Schristos #if defined(__i386__) || defined(__x86_64)
224f645668Schristos #include <cpuid.h>  /* GCC-provided */
234f645668Schristos #elif defined(__aarch64__)
244f645668Schristos #define ATTRIBUTE_UNUSED __attribute__((unused))
254f645668Schristos 
264f645668Schristos static inline uint_t __attribute_const__
__get_cpuid(unsigned int op ATTRIBUTE_UNUSED,unsigned int * eax,unsigned int * ebx ATTRIBUTE_UNUSED,unsigned int * ecx ATTRIBUTE_UNUSED,unsigned int * edx ATTRIBUTE_UNUSED)274f645668Schristos __get_cpuid (unsigned int op ATTRIBUTE_UNUSED, unsigned int *eax,
284f645668Schristos 	     unsigned int *ebx ATTRIBUTE_UNUSED,
294f645668Schristos 	     unsigned int *ecx ATTRIBUTE_UNUSED, unsigned int *edx ATTRIBUTE_UNUSED)
304f645668Schristos {
314f645668Schristos   // CPUID bit assignments:
324f645668Schristos   // [31:24] IMPLEMENTER (0x50 - ARM_CPU_IMP_APM)
334f645668Schristos   // [23:20] VARIANT indicates processor revision (0x2 = Revision 2)
344f645668Schristos   // [19:16] Constant (Reads as 0xF)
354f645668Schristos   // [15:04] PARTNO indicates part number (0xC23 = Cortex-M3)
364f645668Schristos   // [03:00] REVISION indicates patch release (0x0 = Patch 0)
374f645668Schristos   //    unsigned long v = 0;
384f645668Schristos   //    __asm volatile ("MRS %[result], MPIDR_EL1" : [result] "=r" (v));
394f645668Schristos   //    Tprintf(DBG_LT0, "cpuid.c:%d read_cpuid_id() MPIDR_EL1=0x%016lx\n", __LINE__, v);
404f645668Schristos   uint_t res = 0;
414f645668Schristos   __asm volatile ("MRS %[result], MIDR_EL1" : [result] "=r" (*eax));
424f645668Schristos   Tprintf (DBG_LT0, "cpuid.c:%d read_cpuid_id() MIDR_EL1=0x%016x\n", __LINE__, *eax);
434f645668Schristos   return res;
444f645668Schristos }
454f645668Schristos #endif
464f645668Schristos 
474f645668Schristos /*
484f645668Schristos  * Various routines to handle identification
494f645668Schristos  * and classification of x86 processors.
504f645668Schristos  */
514f645668Schristos 
524f645668Schristos #define IS_GLOBAL /* externally visible */
534f645668Schristos #define	X86_VENDOR_Intel	0
544f645668Schristos #define	X86_VENDORSTR_Intel	"GenuineIntel"
554f645668Schristos #define	X86_VENDOR_IntelClone	1
564f645668Schristos #define	X86_VENDOR_AMD		2
574f645668Schristos #define	X86_VENDORSTR_AMD	"AuthenticAMD"
584f645668Schristos 
594f645668Schristos #define BITX(u, h, l)       (((u) >> (l)) & ((1LU << ((h) - (l) + 1LU)) - 1LU))
604f645668Schristos #define CPI_FAMILY_XTD(reg) BITX(reg, 27, 20)
614f645668Schristos #define CPI_MODEL_XTD(reg)  BITX(reg, 19, 16)
624f645668Schristos #define CPI_TYPE(reg)       BITX(reg, 13, 12)
634f645668Schristos #define CPI_FAMILY(reg)     BITX(reg, 11, 8)
644f645668Schristos #define CPI_STEP(reg)       BITX(reg, 3, 0)
654f645668Schristos #define CPI_MODEL(reg)      BITX(reg, 7, 4)
664f645668Schristos #define IS_EXTENDED_MODEL_INTEL(model)  ((model) == 0x6 || (model) >= 0xf)
674f645668Schristos 
684f645668Schristos 
694f645668Schristos typedef struct
704f645668Schristos {
714f645668Schristos   unsigned int eax;
724f645668Schristos   unsigned int ebx;
734f645668Schristos   unsigned int ecx;
744f645668Schristos   unsigned int edx;
754f645668Schristos } cpuid_regs_t;
764f645668Schristos 
774f645668Schristos typedef struct
784f645668Schristos {
794f645668Schristos   unsigned int cpi_model;
804f645668Schristos   unsigned int cpi_family;
814f645668Schristos   unsigned int cpi_vendor;        /* enum of cpi_vendorstr */
824f645668Schristos   unsigned int cpi_maxeax;        /* fn 0: %eax */
834f645668Schristos   char cpi_vendorstr[13];         /* fn 0: %ebx:%ecx:%edx */
844f645668Schristos } cpuid_info_t;
854f645668Schristos 
864f645668Schristos 
874f645668Schristos #if defined(__i386__) || defined(__x86_64)
884f645668Schristos static uint_t
cpuid_vendorstr_to_vendorcode(char * vendorstr)894f645668Schristos cpuid_vendorstr_to_vendorcode (char *vendorstr)
904f645668Schristos {
914f645668Schristos   if (strcmp (vendorstr, X86_VENDORSTR_Intel) == 0)
924f645668Schristos     return X86_VENDOR_Intel;
934f645668Schristos   else if (strcmp (vendorstr, X86_VENDORSTR_AMD) == 0)
944f645668Schristos     return X86_VENDOR_AMD;
954f645668Schristos   else
964f645668Schristos     return X86_VENDOR_IntelClone;
974f645668Schristos }
984f645668Schristos 
994f645668Schristos static int
my_cpuid(unsigned int op,cpuid_regs_t * regs)1004f645668Schristos my_cpuid (unsigned int op, cpuid_regs_t *regs)
1014f645668Schristos {
1024f645668Schristos   regs->eax = regs->ebx = regs->ecx = regs->edx = 0;
1034f645668Schristos   int ret = __get_cpuid (op, &regs->eax, &regs->ebx, &regs->ecx, &regs->edx);
1044f645668Schristos   TprintfT (DBG_LT1, "my_cpuid: __get_cpuid(0x%x, 0x%x, 0x%x, 0x%x, 0x%x) returns %d\n",
1054f645668Schristos 	    op, regs->eax, regs->ebx, regs->ecx, regs->edx, ret);
1064f645668Schristos   return ret;
1074f645668Schristos }
1084f645668Schristos #endif
1094f645668Schristos 
1104f645668Schristos static cpuid_info_t *
get_cpuid_info()1114f645668Schristos get_cpuid_info ()
1124f645668Schristos {
1134f645668Schristos   static int cpuid_inited = 0;
1144f645668Schristos   static cpuid_info_t cpuid_info;
1154f645668Schristos   cpuid_info_t *cpi = &cpuid_info;
1164f645668Schristos   if (cpuid_inited)
1174f645668Schristos     return cpi;
1184f645668Schristos   cpuid_inited = 1;
1194f645668Schristos 
1204f645668Schristos #if defined(__aarch64__)
1214f645668Schristos   // CPUID bit assignments:
1224f645668Schristos   // [31:24] IMPLEMENTER (0x50 - ARM_CPU_IMP_APM)
1234f645668Schristos   // [23:20] VARIANT indicates processor revision (0x2 = Revision 2)
1244f645668Schristos   // [19:16] Constant (Reads as 0xF)
1254f645668Schristos   // [15:04] PARTNO indicates part number (0xC23 = Cortex-M3)
1264f645668Schristos   // [03:00] REVISION indicates patch release (0x0 = Patch 0)
1274f645668Schristos   uint_t reg = 0;
1284f645668Schristos   __asm volatile ("MRS %[result], MIDR_EL1" : [result] "=r" (reg));
1294f645668Schristos   cpi->cpi_vendor = reg >> 24;
1304f645668Schristos   cpi->cpi_model = (reg >> 4) & 0xfff;
1314f645668Schristos   switch (cpi->cpi_vendor)
1324f645668Schristos     {
1334f645668Schristos     case ARM_CPU_IMP_APM:
1344f645668Schristos     case ARM_CPU_IMP_ARM:
1354f645668Schristos     case ARM_CPU_IMP_CAVIUM:
1364f645668Schristos     case ARM_CPU_IMP_BRCM:
1374f645668Schristos     case ARM_CPU_IMP_QCOM:
1384f645668Schristos       strncpy (cpi->cpi_vendorstr, AARCH64_VENDORSTR_ARM, sizeof (cpi->cpi_vendorstr));
1394f645668Schristos       break;
1404f645668Schristos     default:
1414f645668Schristos       strncpy (cpi->cpi_vendorstr, "UNKNOWN ARM", sizeof (cpi->cpi_vendorstr));
1424f645668Schristos       break;
1434f645668Schristos     }
1444f645668Schristos   Tprintf (DBG_LT0, "cpuid.c:%d read_cpuid_id() MIDR_EL1==0x%016x cpi_vendor=%d cpi_model=%d\n",
1454f645668Schristos 	   __LINE__, (unsigned int) reg, cpi->cpi_vendor, cpi->cpi_model);
1464f645668Schristos 
1474f645668Schristos #elif defined(__i386__) || defined(__x86_64)
1484f645668Schristos   cpuid_regs_t regs;
1494f645668Schristos   my_cpuid (0, &regs);
1504f645668Schristos   cpi->cpi_maxeax = regs.eax;
1514f645668Schristos   ((uint32_t *) cpi->cpi_vendorstr)[0] = regs.ebx;
1524f645668Schristos   ((uint32_t *) cpi->cpi_vendorstr)[1] = regs.edx;
1534f645668Schristos   ((uint32_t *) cpi->cpi_vendorstr)[2] = regs.ecx;
1544f645668Schristos   cpi->cpi_vendorstr[12] = 0;
1554f645668Schristos   cpi->cpi_vendor = cpuid_vendorstr_to_vendorcode (cpi->cpi_vendorstr);
1564f645668Schristos 
1574f645668Schristos   my_cpuid (1, &regs);
1584f645668Schristos   cpi->cpi_model = CPI_MODEL (regs.eax);
1594f645668Schristos   cpi->cpi_family = CPI_FAMILY (regs.eax);
1604f645668Schristos   if (cpi->cpi_family == 0xf)
1614f645668Schristos     cpi->cpi_family += CPI_FAMILY_XTD (regs.eax);
1624f645668Schristos 
1634f645668Schristos   /*
1644f645668Schristos    * Beware: AMD uses "extended model" iff base *FAMILY* == 0xf.
1654f645668Schristos    * Intel, and presumably everyone else, uses model == 0xf, as
1664f645668Schristos    * one would expect (max value means possible overflow).  Sigh.
1674f645668Schristos    */
1684f645668Schristos   switch (cpi->cpi_vendor)
1694f645668Schristos     {
1704f645668Schristos     case X86_VENDOR_Intel:
1714f645668Schristos       if (IS_EXTENDED_MODEL_INTEL (cpi->cpi_family))
1724f645668Schristos 	cpi->cpi_model += CPI_MODEL_XTD (regs.eax) << 4;
1734f645668Schristos       break;
1744f645668Schristos     case X86_VENDOR_AMD:
1754f645668Schristos       if (CPI_FAMILY (cpi->cpi_family) == 0xf)
1764f645668Schristos 	cpi->cpi_model += CPI_MODEL_XTD (regs.eax) << 4;
1774f645668Schristos       break;
1784f645668Schristos     default:
1794f645668Schristos       if (cpi->cpi_model == 0xf)
1804f645668Schristos 	cpi->cpi_model += CPI_MODEL_XTD (regs.eax) << 4;
1814f645668Schristos       break;
1824f645668Schristos     }
1834f645668Schristos #endif
1844f645668Schristos   return cpi;
1854f645668Schristos }
1864f645668Schristos 
1874f645668Schristos static inline uint_t
cpuid_getvendor()1884f645668Schristos cpuid_getvendor ()
1894f645668Schristos {
1904f645668Schristos   return get_cpuid_info ()->cpi_vendor;
1914f645668Schristos }
1924f645668Schristos 
1934f645668Schristos static inline uint_t
cpuid_getfamily()1944f645668Schristos cpuid_getfamily ()
1954f645668Schristos {
1964f645668Schristos   return get_cpuid_info ()->cpi_family;
1974f645668Schristos }
1984f645668Schristos 
1994f645668Schristos static inline uint_t
cpuid_getmodel()2004f645668Schristos cpuid_getmodel ()
2014f645668Schristos {
2024f645668Schristos   return get_cpuid_info ()->cpi_model;
2034f645668Schristos }
204