LCOV - code coverage report
Current view: directory - src/trusted/validator/x86 - nacl_cpuid.c (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 92 88 95.7 %
Date: 2012-02-16 Functions: 0 0 -

       1                 : /*
       2                 :  * Copyright (c) 2012 The Native Client Authors. All rights reserved.
       3                 :  * Use of this source code is governed by a BSD-style license that can be
       4                 :  * found in the LICENSE file.
       5                 :  */
       6                 : 
       7                 : #include "native_client/src/trusted/validator/x86/nacl_cpuid.h"
       8                 : 
       9                 : /*
      10                 :  * nacl_cpuid.c
      11                 :  * Retrieve and decode CPU model specific feature mask.
      12                 :  */
      13                 : #if NACL_WINDOWS
      14                 : #include <intrin.h>  /* __cpuid intrinsic */
      15                 : #endif  /* NACL_WINDOWS  */
      16                 : 
      17                 : #include <stdio.h>
      18                 : #include <string.h>
      19                 : #include <assert.h>
      20                 : #include <stdlib.h>
      21                 : 
      22                 : #include "native_client/src/include/portability_io.h"
      23                 : 
      24                 : /*
      25                 :  * TODO(bradchen): consolidate to use one debug print mechanism.
      26                 :  */
      27                 : 
      28                 : #define CPUID_EDX_x87        0x00000001  /* x87 FPU support */
      29                 : #define CPUID_EDX_VME        0x00000002  /* Virtual 8086 Mode Enhancement */
      30                 : #define CPUID_EDX_DEB        0x00000004  /* Debugging Extensions */
      31                 : #define CPUID_EDX_PSE        0x00000008  /* Page Size Extensions */
      32                 : #define CPUID_EDX_TSC        0x00000010  /* Time Stamp Counter */
      33                 : #define CPUID_EDX_MSR        0x00000020  /* RDMSR and WRMSR */
      34                 : #define CPUID_EDX_PAE        0x00000040  /* Physical Address Extensions */
      35                 : #define CPUID_EDX_MCE        0x00000080  /* Machine Check Exception */
      36                 : #define CPUID_EDX_CX8        0x00000100  /* CMPXCHG8B Instruction */
      37                 : #define CPUID_EDX_APIC       0x00000200  /* APIC on chip */
      38                 : /* 0x00000400 reserved */
      39                 : #define CPUID_EDX_SEP        0x00000800  /* SYSENTER and SYSEXIT */
      40                 : #define CPUID_EDX_MTRR       0x00001000  /* Memory Type Range Registers */
      41                 : #define CPUID_EDX_PGE        0x00002000  /* PTE Global Bit */
      42                 : #define CPUID_EDX_MCA        0x00004000  /* Machine Check Architecture */
      43                 : #define CPUID_EDX_CMOV       0x00008000  /* CMOV instruction */
      44                 : #define CPUID_EDX_PAT        0x00010000  /* Page Attribute Table */
      45                 : #define CPUID_EDX_PSE36      0x00020000  /* Page Size Extension bis */
      46                 : #define CPUID_EDX_PSN        0x00040000  /* Processor Serial Number */
      47                 : #define CPUID_EDX_CLFLUSH    0x00080000  /* CLFLUSH instruction */
      48                 : /* 0x00100000 reserved */
      49                 : #define CPUID_EDX_DS         0x00200000  /* Debug Store */
      50                 : #define CPUID_EDX_ACPI       0x00400000  /* Thermal Monitor and Clock Ctrl */
      51                 : #define CPUID_EDX_MMX        0x00800000  /* MMX extensions */
      52                 : #define CPUID_EDX_FXSR       0x01000000  /* FXSAVE/FXRSTOR instructions */
      53                 : #define CPUID_EDX_SSE        0x02000000  /* SSE extensions */
      54                 : #define CPUID_EDX_SSE2       0x04000000  /* SSE2 extensions */
      55                 : #define CPUID_EDX_SS         0x08000000  /* self snoop */
      56                 : #define CPUID_EDX_HTT        0x10000000  /* hyper-threading */
      57                 : #define CPUID_EDX_TM         0x20000000  /* Thermal monitor */
      58                 : /* 0x40000000 reserved */
      59                 : #define CPUID_EDX_PBE        0x80000000  /* Pending Break Enable */
      60                 : 
      61                 : #define CPUID_ECX_SSE3       0x00000001  /* SSE3 extensions */
      62                 : /* 0x00000002 reserved */
      63                 : /* 0x00000004 reserved */
      64                 : #define CPUID_ECX_MON        0x00000008  /* MONITOR/MWAIT instructions */
      65                 : #define CPUID_ECX_DSCPL      0x00000010  /* CPL Qualified Debug Store */
      66                 : #define CPUID_ECX_VMX        0x00000020  /* Virtual Machine Extensions */
      67                 : #define CPUID_ECX_SMX        0x00000040  /* Safer Mode Extensions */
      68                 : #define CPUID_ECX_EST        0x00000080  /* Enahcned SpeedStep */
      69                 : #define CPUID_ECX_TM2        0x00000100  /* Thermal Monitor 2 */
      70                 : #define CPUID_ECX_SSSE3      0x00000200  /* SS_S_E3 extensions */
      71                 : #define CPUID_ECX_CXID       0x00000400  /* L1 context ID */
      72                 : /* 0x00000800 reserved */
      73                 : /* 0x00001000 reserved */
      74                 : #define CPUID_ECX_CX16       0x00002000  /* CMPXCHG16B instruction */
      75                 : #define CPUID_ECX_XTPR       0x00004000  /* xTPR update control */
      76                 : #define CPUID_ECX_PDCM       0x00008000  /* Perf/Debug Capability MSR */
      77                 : /* 0x00010000 reserved */
      78                 : /* 0x00020000 reserved */
      79                 : /* 0x00040000 reserved */
      80                 : #define CPUID_ECX_SSE41      0x00080000  /* SSE4.1 extensions */
      81                 : #define CPUID_ECX_SSE42      0x00100000  /* SSE4.2 extensions */
      82                 : /* 0x00200000 reserved */
      83                 : #define CPUID_ECX_MOVBE      0x00400000  /* MOVBE instruction */
      84                 : #define CPUID_ECX_POPCNT     0x00800000  /* POPCNT instruction */
      85                 : /* 0x00100000 reserved */
      86                 : #define CPUID_ECX_AES        0x02000000  /* AES instructions */
      87                 : #define CPUID_ECX_XSAVE      0x04000000  /* XSAVE/XRSTOR/XSETBV/XGETBV */
      88                 : #define CPUID_ECX_OSXSAVE    0x08000000  /* XSAVE et al enabled by OS */
      89                 : #define CPUID_ECX_AVX        0x10000000  /* AVX instructions */
      90                 : /* 0x20000000 reserved */
      91                 : /* 0x40000000 reserved */
      92                 : /* 0x80000000 reserved */
      93                 : 
      94                 : /* AMD-specific masks */
      95                 : #define CPUID_EDX_EMMX       0x00400000
      96                 : #define CPUID_EDX_LM         0x20000000  /* longmode */
      97                 : #define CPUID_EDX_E3DN       0x40000000
      98                 : #define CPUID_EDX_3DN        0x80000000
      99                 : #define CPUID_ECX_SVM        0x00000004
     100                 : #define CPUID_ECX_ABM        0x00000020  /* lzcnt instruction */
     101                 : #define CPUID_ECX_SSE4A      0x00000040
     102                 : #define CPUID_ECX_PRE        0x00000100
     103                 : #define CPUID_ECX_SSE5       0x00000800
     104                 : 
     105                 : typedef enum {
     106                 :   CFReg_EAX_I=0,  /* eax == 1 */
     107                 :   CFReg_EBX_I,    /* eax == 1 */
     108                 :   CFReg_ECX_I,    /* eax == 1 */
     109                 :   CFReg_EDX_I,    /* eax == 1 */
     110                 :   CFReg_EAX_A,    /* eax == 0x80000001 */
     111                 :   CFReg_EBX_A,    /* eax == 0x80000001 */
     112                 :   CFReg_ECX_A,    /* eax == 0x80000001 */
     113                 :   CFReg_EDX_A     /* eax == 0x80000001 */
     114                 : } CPUFeatureReg;
     115                 : 
     116                 : typedef struct cpufeature {
     117                 :   CPUFeatureReg reg;
     118                 :   uint32_t mask;
     119                 :   const char *name;
     120                 : } CPUFeature;
     121                 : 
     122                 : static const CPUFeature CPUFeatureDescriptions[(int)NaClCPUFeature_Max] = {
     123                 :   {CFReg_EDX_I, CPUID_EDX_x87, "x87"},
     124                 :   {CFReg_EDX_I, CPUID_EDX_MMX, "MMX"},
     125                 :   {CFReg_EDX_I, CPUID_EDX_SSE, "SSE"},
     126                 :   {CFReg_EDX_I, CPUID_EDX_SSE2, "SSE2"},
     127                 :   {CFReg_ECX_I, CPUID_ECX_SSE3, "SSE3"},
     128                 :   {CFReg_ECX_I, CPUID_ECX_SSSE3, "SSSE3"},
     129                 :   {CFReg_ECX_I, CPUID_ECX_SSE41, "SSE41"},
     130                 :   {CFReg_ECX_I, CPUID_ECX_SSE42, "SSE42"},
     131                 :   {CFReg_ECX_I, CPUID_ECX_MOVBE, "MOVBE"},
     132                 :   {CFReg_ECX_I, CPUID_ECX_POPCNT, "POPCNT"},
     133                 :   {CFReg_EDX_I, CPUID_EDX_CX8, "CMPXCHG8B"},
     134                 :   {CFReg_ECX_I, CPUID_ECX_CX16, "CMPXCHG16B"},
     135                 :   {CFReg_EDX_I, CPUID_EDX_CMOV, "CMOV"},
     136                 :   {CFReg_ECX_I, CPUID_ECX_MON, "MONITOR/MWAIT"},
     137                 :   {CFReg_EDX_I, CPUID_EDX_FXSR, "FXSAVE/FXRSTOR"},
     138                 :   {CFReg_EDX_I, CPUID_EDX_CLFLUSH, "CLFLUSH"},
     139                 :   {CFReg_EDX_I, CPUID_EDX_MSR, "RDMSR/WRMSR"},
     140                 :   {CFReg_EDX_I, CPUID_EDX_TSC, "RDTSC"},
     141                 :   {CFReg_EDX_I, CPUID_EDX_VME, "VME"},
     142                 :   {CFReg_EDX_I, CPUID_EDX_PSN, "PSN"},
     143                 :   {CFReg_ECX_I, CPUID_ECX_VMX, "VMX"},
     144                 :   {CFReg_ECX_I, CPUID_ECX_OSXSAVE, "OSXSAVE"},
     145                 :   {CFReg_ECX_I, CPUID_ECX_AVX, "AVX"},
     146                 :   {CFReg_EDX_A, CPUID_EDX_3DN, "3DNow"},
     147                 :   {CFReg_EDX_A, CPUID_EDX_EMMX, "EMMX"},
     148                 :   {CFReg_EDX_A, CPUID_EDX_E3DN, "E3DNow"},
     149                 :   {CFReg_ECX_A, CPUID_ECX_ABM, "LZCNT"},
     150                 :   {CFReg_ECX_A, CPUID_ECX_SSE4A, "SSE4A"},
     151                 :   {CFReg_EDX_A, CPUID_EDX_LM, "LongMode"},
     152                 :   {CFReg_ECX_A, CPUID_ECX_SVM, "SVM"},
     153                 : };
     154                 : 
     155                 : #define /* static const int */ kVendorIDLength 13
     156                 : static const char Intel_CPUID0[kVendorIDLength]   = "GenuineIntel";
     157                 : static const char AMD_CPUID0[kVendorIDLength]     = "AuthenticAMD";
     158                 : #ifdef NOTYET
     159                 : static const char UMC_CPUID0[kVendorIDLength]     = "UMC UMC UMC ";
     160                 : static const char Cyrix_CPUID0[kVendorIDLength]   = "CyrixInstead";
     161                 : static const char NexGen_CPUID0[kVendorIDLength]  = "NexGenDriven";
     162                 : static const char Cantaur_CPUID0[kVendorIDLength] = "CentaurHauls";
     163                 : static const char Rise_CPUID0[kVendorIDLength]    = "RiseRiseRise";
     164                 : static const char SiS_CPUID0[kVendorIDLength]     = "SiS SiS SiS ";
     165                 : static const char TM_CPUID0[kVendorIDLength]      = "GenuineTMx86";
     166                 : static const char NSC_CPUID0[kVendorIDLength]     = "Geode by NSC";
     167                 : #endif
     168                 : 
     169              36 : static int asm_HasCPUID() {
     170                 :   volatile int before, after, result;
     171                 : #if NACL_BUILD_SUBARCH == 64
     172                 :   /* Note: If we are running in x86-64, then cpuid must be defined,
     173                 :    * since CPUID dates from DX2 486, and x86-64 was added after this.
     174                 :    */
     175                 :   return 1;
     176                 : /* TODO(bradchen): split into separate Windows, etc., files */
     177                 : #elif defined(__GNUC__)
     178              36 :   __asm__ volatile("pushfl                \n\t" /* save EFLAGS to eax */
     179                 :                    "pop %%eax             \n\t"
     180                 :                    "movl %%eax, %0        \n\t" /* remember EFLAGS in %0 */
     181                 :                    "xor $0x00200000, %%eax\n\t" /* toggle bit 21 */
     182                 :                    "push %%eax            \n\t" /* write eax to EFLAGS */
     183                 :                    "popfl                 \n\t"
     184                 :                    "pushfl                \n\t" /* save EFLAGS to %1 */
     185                 :                    "pop %1                \n\t"
     186                 :                    /*
     187                 :                     * We use "r" constraints here, forcing registers,
     188                 :                     * because a memory reference using the stack
     189                 :                     * pointer wouldn't be safe since we're moving the
     190                 :                     * stack pointer around in between the
     191                 :                     * instructions.  We need to inform the compiler
     192                 :                     * that we're clobbering %eax as a scratch register.
     193                 :                     */
     194                 :                    : "=r" (before), "=r" (after) : : "eax");
     195                 : #elif NACL_WINDOWS
     196                 :   __asm {
     197                 :     pushfd
     198                 :     pop eax
     199                 :     mov before, eax
     200                 :     xor eax, 0x00200000
     201                 :     push eax
     202                 :     popfd
     203                 :     pushfd
     204                 :     pop after
     205                 :   }
     206                 : #else
     207                 : # error Unsupported platform
     208                 : #endif
     209              36 :   result = (before ^ after) & 0x0200000;
     210              36 :   return result;
     211                 : }
     212                 : 
     213             108 : static void asm_CPUID(uint32_t op, volatile uint32_t reg[4]) {
     214                 : #if defined(__GNUC__)
     215                 : #if NACL_BUILD_SUBARCH == 64
     216                 :  __asm__ volatile("push %%rbx       \n\t" /* save %ebx */
     217                 : #else
     218             108 :  __asm__ volatile("pushl %%ebx      \n\t"
     219                 : #endif
     220                 :                    "cpuid            \n\t"
     221                 :                    "movl %%ebx, %1   \n\t"
     222                 :                    /* save what cpuid just put in %ebx */
     223                 : #if NACL_BUILD_SUBARCH == 64
     224                 :                    "pop %%rbx        \n\t"
     225                 : #else
     226                 :                    "popl %%ebx       \n\t" /* restore the old %ebx */
     227                 : #endif
     228                 :                    : "=a"(reg[0]), "=S"(reg[1]), "=c"(reg[2]), "=d"(reg[3])
     229                 :                    : "a"(op)
     230                 :                    : "cc");
     231                 : #elif NACL_WINDOWS
     232                 :   __cpuid((uint32_t*)reg, op);
     233                 : #else
     234                 : # error Unsupported platform
     235                 : #endif
     236             108 : }
     237                 : 
     238              36 : static void CacheCPUVersionID(NaClCPUData* data) {
     239              36 :   uint32_t reg[4] = {0, 0, 0, 0 };
     240              36 :   asm_CPUID(0, reg);
     241              36 :   data->_vidwords[0] = reg[1];
     242              36 :   data->_vidwords[1] = reg[3];
     243              36 :   data->_vidwords[2] = reg[2];
     244              36 :   data->_vidwords[3] = 0;
     245              36 : }
     246                 : 
     247                 : /* Defines the (cached) cpu version id */
     248                 : #define CPUVersionID(data) ((char*) (data)->_vidwords)
     249                 : 
     250                 : 
     251                 : /* Cache feature vector as array of uint32_t [ecx, edx] */
     252              36 : static void CacheCPUFeatureVector(NaClCPUData* data) {
     253                 :   int i;
     254             324 :   for (i = 0; i < kMaxCPUFeatureReg; ++i) {
     255             288 :     data->_featurev[i] = 0;
     256                 :   }
     257              36 :   asm_CPUID(1, data->_featurev);
     258                 :   /* this is for AMD CPUs */
     259              36 :   asm_CPUID(0x80000001, &data->_featurev[CFReg_EAX_A]);
     260                 : #if 0
     261                 :   /* print feature vector */
     262                 :   printf("CPUID:  %08x  %08x  %08x  %08x\n",
     263                 :          data->_featurev[0],
     264                 :          data->_featurev[1],
     265                 :          data->_featurev[2],
     266                 :          data->_featurev[3]);
     267                 :   printf("CPUID:  %08x  %08x  %08x  %08x\n",
     268                 :          data->_featurev[4],
     269                 :          data->_featurev[5],
     270                 :          data->_featurev[6],
     271                 :          data->_featurev[7]);
     272                 : #endif
     273              36 : }
     274                 : 
     275                 : /* CacheGetCPUIDString creates an ASCII string that identfies this CPU's */
     276                 : /* vendor ID, family, model, and stepping, as per the CPUID instruction */
     277              36 : static void CacheGetCPUIDString(NaClCPUData* data) {
     278              36 :   char *cpuversionid = CPUVersionID(data);
     279              36 :   uint32_t *fv = data->_featurev;
     280              36 :   char* wlid = data->_wlid;
     281                 :   /* Subtract 1 in this assert to avoid counting two null characters. */
     282                 :   assert(9 + kVendorIDLength - 1 == kCPUIDStringLength);
     283              36 :   memcpy(wlid, cpuversionid, kVendorIDLength-1);
     284              36 :   SNPRINTF(&(wlid[kVendorIDLength-1]), 9, "%08x", (int)fv[CFReg_EAX_I]);
     285              36 : }
     286                 : 
     287              13 : char *GetCPUIDString(NaClCPUData* data) {
     288              13 :   return data->_wlid;
     289                 : }
     290                 : 
     291                 : /* Returns true if the given feature is defined by the CPUID. */
     292             726 : static int CheckCPUFeature(NaClCPUData* data, NaClCPUFeatureID fid) {
     293             726 :   const CPUFeature *f = &CPUFeatureDescriptions[fid];
     294             726 :   uint32_t *fv = data->_featurev;
     295                 : #if 0
     296                 :   printf("%s: %x (%08x & %08x)\n", f->name, (fv[f->reg] & f->mask),
     297                 :          fv[f->reg], f->mask);
     298                 : #endif
     299             726 :   if (fv[f->reg] & f->mask) {
     300             414 :     return 1;
     301                 :   } else {
     302             312 :     return 0;
     303                 :   }
     304                 : }
     305                 : 
     306                 : uint64_t NaClXGETBV(uint32_t);
     307                 : 
     308                 : /* Cache XCR vector */
     309              36 : static void CacheCPUXCRVector(NaClCPUData* data) {
     310              36 :   if (CheckCPUFeature(data, NaClCPUFeature_OSXSAVE)) {
     311                 :     int i;
     312               0 :     for (i = 0; i < kMaxCPUXCRReg; ++i) {
     313               0 :       data->_xcrv[i] = NaClXGETBV(i);
     314                 :     }
     315                 :   } else {
     316                 :     int i;
     317              72 :     for (i = 0; i < kMaxCPUXCRReg; ++i) {
     318              36 :       data->_xcrv[i] = 0;
     319                 :     }
     320                 :   }
     321              36 : }
     322                 : 
     323                 : /* Check that we have a supported 386 architecture. NOTE:
     324                 :  * As as side effect, the given cpu features is cleared before
     325                 :  * setting the appropriate fields.
     326                 :  */
     327                 : static void CheckNaClArchFeatures(NaClCPUData* data,
     328              23 :                                   nacl_arch_features* features) {
     329              23 :   const size_t kCPUID0Length = 12;
     330                 :   char *cpuversionid;
     331              23 :   memset(features, 0, sizeof(*features));
     332              23 :   if (data->_has_CPUID) features->f_cpuid_supported = 1;
     333              23 :   cpuversionid = CPUVersionID(data);
     334              23 :   if (strncmp(cpuversionid, Intel_CPUID0, kCPUID0Length) == 0) {
     335              23 :     features->f_cpu_supported = 1;
     336               0 :   } else if (strncmp(cpuversionid, AMD_CPUID0, kCPUID0Length) == 0) {
     337               0 :     features->f_cpu_supported = 1;
     338                 :   }
     339              23 : }
     340                 : 
     341              10 : int NaClArchSupported(NaClCPUFeaturesX86 *features) {
     342              10 :   return (features->arch_features.f_cpuid_supported &&
     343                 :           features->arch_features.f_cpu_supported);
     344                 : }
     345                 : 
     346             158 : void NaClClearCPUFeatures(NaClCPUFeaturesX86 *features) {
     347             158 :   memset(features, 0, sizeof(*features));
     348             158 : }
     349                 : 
     350              66 : void NaClSetAllCPUFeatures(NaClCPUFeaturesX86 *features) {
     351                 :   /* Be a little more pedantic than using memset because we don't know exactly
     352                 :    * how the structure is laid out.  If we use memset, fields may be initialized
     353                 :    * to 0xff instead of 1 ... this isn't the end of the world but it can
     354                 :    * create a skew if the structure is hashed, etc.
     355                 :    */
     356                 :   int id;
     357                 :   /* Ensure any padding is zeroed. */
     358              66 :   NaClClearCPUFeatures(features);
     359              66 :   features->arch_features.f_cpuid_supported = 1;
     360              66 :   features->arch_features.f_cpu_supported = 1;
     361            2046 :   for (id = 0; id < NaClCPUFeature_Max; ++id) {
     362            1980 :     NaClSetCPUFeature(features, id, 1);
     363                 :   }
     364              66 : }
     365                 : 
     366                 : void NaClCopyCPUFeatures(NaClCPUFeaturesX86* target,
     367             175 :                          const NaClCPUFeaturesX86* source) {
     368             175 :   memcpy(target, source, sizeof(NaClCPUFeaturesX86));
     369             175 : }
     370                 : 
     371                 : void NaClSetCPUFeature(NaClCPUFeaturesX86 *features, NaClCPUFeatureID id,
     372            2695 :                        int state) {
     373            2695 :   features->data[id] = (char) state;
     374            2695 : }
     375                 : 
     376              18 : const char* NaClGetCPUFeatureName(NaClCPUFeatureID id) {
     377              18 :   return CPUFeatureDescriptions[id].name;
     378                 : }
     379                 : 
     380                 : /* WARNING: This routine and subroutines it uses are not threadsafe.
     381                 :  * However, if races occur, they are short lived, and at worst, will
     382                 :  * result in defining fewer features than are actually supported by
     383                 :  * the hardware. Hence, if a race occurs, the validator may reject
     384                 :  * some features that should not be rejected.
     385                 :  */
     386              23 : static void GetCPUFeatures(NaClCPUData* data, NaClCPUFeaturesX86 *cpuf) {
     387                 :   int id;
     388              23 :   NaClClearCPUFeatures(cpuf);
     389              23 :   CheckNaClArchFeatures(data, &cpuf->arch_features);
     390              23 :   if (!cpuf->arch_features.f_cpuid_supported) return;
     391                 : 
     392             713 :   for (id = 0; id < NaClCPUFeature_Max; ++id) {
     393             690 :     NaClSetCPUFeature(cpuf, id, CheckCPUFeature(data, id));
     394                 :   }
     395                 : 
     396                 :   /*
     397                 :    * If the operating system doesn't maintain the AVX state,
     398                 :    * pretend we don't have the instructions available at all.
     399                 :    */
     400              23 :   if (!(NaClGetCPUFeature(cpuf, NaClCPUFeature_OSXSAVE)
     401                 :         && (data->_xcrv[0] & 6) == 6)) {
     402              23 :     NaClSetCPUFeature(cpuf, NaClCPUFeature_AVX, 0);
     403                 :   }
     404                 : }
     405                 : 
     406              36 : void NaClCPUDataGet(NaClCPUData* data) {
     407              36 :   data->_has_CPUID = asm_HasCPUID();
     408              36 :   CacheCPUVersionID(data);
     409              36 :   CacheCPUFeatureVector(data);
     410              36 :   CacheCPUXCRVector(data);
     411              36 :   CacheGetCPUIDString(data);
     412              36 : }
     413                 : 
     414              23 : void NaClGetCurrentCPUFeatures(NaClCPUFeaturesX86 *cpu_features) {
     415                 :   NaClCPUData cpu_data;
     416              23 :   NaClCPUDataGet(&cpu_data);
     417              23 :   GetCPUFeatures(&cpu_data, cpu_features);
     418              23 : }

Generated by: LCOV version 1.7