Back to index

glibc  2.9
sysconf.c
Go to the documentation of this file.
00001 /* Get file-specific information about a file.  Linux version.
00002    Copyright (C) 2003, 2004, 2006, 2007 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #include <assert.h>
00021 #include <stdbool.h>
00022 #include <stdlib.h>
00023 #include <unistd.h>
00024 #include <hp-timing.h>
00025 
00026 static long int linux_sysconf (int name);
00027 
00028 
00029 static long int __attribute__ ((noinline))
00030 handle_i486 (int name)
00031 {
00032   /* The processor only has a unified level 1 cache of 8k.  */
00033   switch (name)
00034     {
00035     case _SC_LEVEL1_ICACHE_SIZE:
00036     case _SC_LEVEL1_DCACHE_SIZE:
00037       return 8 * 1024;
00038 
00039     case _SC_LEVEL1_ICACHE_ASSOC:
00040     case _SC_LEVEL1_DCACHE_ASSOC:
00041       // XXX Anybody know this?
00042       return 0;
00043 
00044     case _SC_LEVEL1_ICACHE_LINESIZE:
00045     case _SC_LEVEL1_DCACHE_LINESIZE:
00046       // XXX Anybody know for sure?
00047       return 16;
00048 
00049     case _SC_LEVEL2_CACHE_SIZE:
00050     case _SC_LEVEL2_CACHE_ASSOC:
00051     case _SC_LEVEL2_CACHE_LINESIZE:
00052     case _SC_LEVEL3_CACHE_SIZE:
00053     case _SC_LEVEL3_CACHE_ASSOC:
00054     case _SC_LEVEL3_CACHE_LINESIZE:
00055     case _SC_LEVEL4_CACHE_SIZE:
00056     case _SC_LEVEL4_CACHE_ASSOC:
00057       /* Not available.  */
00058       break;
00059 
00060     default:
00061       assert (! "cannot happen");
00062     }
00063 
00064   return -1;
00065 }
00066 
00067 
00068 static const struct intel_02_cache_info
00069 {
00070   unsigned int idx;
00071   int name;
00072   long int size;
00073   long int assoc;
00074   long int linesize;
00075 } intel_02_known[] =
00076   {
00077     { 0x06, _SC_LEVEL1_ICACHE_SIZE, 8192, 4, 32 },
00078     { 0x08, _SC_LEVEL1_ICACHE_SIZE, 16384, 4, 32 },
00079     { 0x0a, _SC_LEVEL1_DCACHE_SIZE, 8192, 2, 32 },
00080     { 0x0c, _SC_LEVEL1_DCACHE_SIZE, 16384, 4, 32 },
00081     { 0x22, _SC_LEVEL3_CACHE_SIZE, 524288, 4, 64 },
00082     { 0x23, _SC_LEVEL3_CACHE_SIZE, 1048576, 8, 64 },
00083     { 0x25, _SC_LEVEL3_CACHE_SIZE, 2097152, 8, 64 },
00084     { 0x29, _SC_LEVEL3_CACHE_SIZE, 4194304, 8, 64 },
00085     { 0x2c, _SC_LEVEL1_DCACHE_SIZE, 32768, 8, 64 },
00086     { 0x30, _SC_LEVEL1_ICACHE_SIZE, 32768, 8, 64 },
00087     { 0x39, _SC_LEVEL2_CACHE_SIZE, 131072, 4, 64 },
00088     { 0x3a, _SC_LEVEL2_CACHE_SIZE, 196608, 6, 64 },
00089     { 0x3b, _SC_LEVEL2_CACHE_SIZE, 131072, 2, 64 },
00090     { 0x3c, _SC_LEVEL2_CACHE_SIZE, 262144, 4, 64 },
00091     { 0x3d, _SC_LEVEL2_CACHE_SIZE, 393216, 6, 64 },
00092     { 0x3e, _SC_LEVEL2_CACHE_SIZE, 524288, 4, 64 },
00093     { 0x3f, _SC_LEVEL2_CACHE_SIZE, 262144, 2, 64 },
00094     { 0x41, _SC_LEVEL2_CACHE_SIZE, 131072, 4, 32 },
00095     { 0x42, _SC_LEVEL2_CACHE_SIZE, 262144, 4, 32 },
00096     { 0x43, _SC_LEVEL2_CACHE_SIZE, 524288, 4, 32 },
00097     { 0x44, _SC_LEVEL2_CACHE_SIZE, 1048576, 4, 32 },
00098     { 0x45, _SC_LEVEL2_CACHE_SIZE, 2097152, 4, 32 },
00099     { 0x46, _SC_LEVEL3_CACHE_SIZE, 4194304, 4, 64 },
00100     { 0x47, _SC_LEVEL3_CACHE_SIZE, 8388608, 8, 64 },
00101     { 0x48, _SC_LEVEL2_CACHE_SIZE, 3145728, 12, 64 },
00102     { 0x49, _SC_LEVEL2_CACHE_SIZE, 4194304, 16, 64 },
00103     { 0x4a, _SC_LEVEL3_CACHE_SIZE, 6291456, 12, 64 },
00104     { 0x4b, _SC_LEVEL3_CACHE_SIZE, 8388608, 16, 64 },
00105     { 0x4c, _SC_LEVEL3_CACHE_SIZE, 12582912, 12, 64 },
00106     { 0x4d, _SC_LEVEL3_CACHE_SIZE, 16777216, 16, 64 },
00107     { 0x4e, _SC_LEVEL2_CACHE_SIZE, 6291456, 24, 64 },
00108     { 0x60, _SC_LEVEL1_DCACHE_SIZE, 16384, 8, 64 },
00109     { 0x66, _SC_LEVEL1_DCACHE_SIZE, 8192, 4, 64 },
00110     { 0x67, _SC_LEVEL1_DCACHE_SIZE, 16384, 4, 64 },
00111     { 0x68, _SC_LEVEL1_DCACHE_SIZE, 32768, 4, 64 },
00112     { 0x78, _SC_LEVEL2_CACHE_SIZE, 1048576, 8, 64 },
00113     { 0x79, _SC_LEVEL2_CACHE_SIZE, 131072, 8, 64 },
00114     { 0x7a, _SC_LEVEL2_CACHE_SIZE, 262144, 8, 64 },
00115     { 0x7b, _SC_LEVEL2_CACHE_SIZE, 524288, 8, 64 },
00116     { 0x7c, _SC_LEVEL2_CACHE_SIZE, 1048576, 8, 64 },
00117     { 0x7d, _SC_LEVEL2_CACHE_SIZE, 2097152, 8, 64 },
00118     { 0x7f, _SC_LEVEL2_CACHE_SIZE, 524288, 2, 64 },
00119     { 0x82, _SC_LEVEL2_CACHE_SIZE, 262144, 8, 32 },
00120     { 0x83, _SC_LEVEL2_CACHE_SIZE, 524288, 8, 32 },
00121     { 0x84, _SC_LEVEL2_CACHE_SIZE, 1048576, 8, 32 },
00122     { 0x85, _SC_LEVEL2_CACHE_SIZE, 2097152, 8, 32 },
00123     { 0x86, _SC_LEVEL2_CACHE_SIZE, 524288, 4, 64 },
00124     { 0x87, _SC_LEVEL2_CACHE_SIZE, 1048576, 8, 64 },
00125   };
00126 #define nintel_02_known (sizeof (intel_02_known) / sizeof (intel_02_known[0]))
00127 
00128 
00129 static int
00130 intel_02_known_compare (const void *p1, const void *p2)
00131 {
00132   const struct intel_02_cache_info *i1;
00133   const struct intel_02_cache_info *i2;
00134 
00135   i1 = (const struct intel_02_cache_info *) p1;
00136   i2 = (const struct intel_02_cache_info *) p2;
00137 
00138   if (i1->idx == i2->idx)
00139     return 0;
00140 
00141   return i1->idx < i2->idx ? -1 : 1;
00142 }
00143 
00144 
00145 static long int
00146 __attribute__ ((noinline))
00147 intel_check_word (int name, unsigned int value, bool *has_level_2,
00148                 bool *no_level_2_or_3)
00149 {
00150   if ((value & 0x80000000) != 0)
00151     /* The register value is reserved.  */
00152     return 0;
00153 
00154   /* Fold the name.  The _SC_ constants are always in the order SIZE,
00155      ASSOC, LINESIZE.  */
00156   int folded_name = (_SC_LEVEL1_ICACHE_SIZE
00157                    + ((name - _SC_LEVEL1_ICACHE_SIZE) / 3) * 3);
00158 
00159   while (value != 0)
00160     {
00161       unsigned int byte = value & 0xff;
00162 
00163       if (byte == 0x40)
00164        {
00165          *no_level_2_or_3 = true;
00166 
00167          if (folded_name == _SC_LEVEL3_CACHE_SIZE)
00168            /* No need to look further.  */
00169            break;
00170        }
00171       else
00172        {
00173          if (byte == 0x49 && folded_name == _SC_LEVEL3_CACHE_SIZE)
00174            {
00175              /* Intel reused this value.  For family 15, model 6 it
00176                specifies the 3rd level cache.  Otherwise the 2nd
00177                level cache.  */
00178              unsigned int eax;
00179              unsigned int ebx;
00180              unsigned int ecx;
00181              unsigned int edx;
00182              asm volatile ("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1"
00183                          : "=a" (eax), "=r" (ebx), "=c" (ecx), "=d" (edx)
00184                          : "0" (1));
00185 
00186              unsigned int family = ((eax >> 20) & 0xff) + ((eax >> 8) & 0xf);
00187              unsigned int model = ((((eax >>16) & 0xf) << 4)
00188                                 + ((eax >> 4) & 0xf));
00189              if (family == 15 && model == 6)
00190               {
00191                 /* The level 3 cache is encoded for this model like
00192                    the level 2 cache is for other models.  Pretend
00193                    the caller asked for the level 2 cache.  */
00194                 name = (_SC_LEVEL2_CACHE_SIZE
00195                        + (name - _SC_LEVEL3_CACHE_SIZE));
00196                 folded_name = _SC_LEVEL3_CACHE_SIZE;
00197               }
00198            }
00199 
00200          struct intel_02_cache_info *found;
00201          struct intel_02_cache_info search;
00202 
00203          search.idx = byte;
00204          found = bsearch (&search, intel_02_known, nintel_02_known,
00205                         sizeof (intel_02_known[0]), intel_02_known_compare);
00206          if (found != NULL)
00207            {
00208              if (found->name == folded_name)
00209               {
00210                 unsigned int offset = name - folded_name;
00211 
00212                 if (offset == 0)
00213                   /* Cache size.  */
00214                   return found->size;
00215                 if (offset == 1)
00216                   return found->assoc;
00217 
00218                 assert (offset == 2);
00219                 return found->linesize;
00220               }
00221 
00222              if (found->name == _SC_LEVEL2_CACHE_SIZE)
00223               *has_level_2 = true;
00224            }
00225        }
00226 
00227       /* Next byte for the next round.  */
00228       value >>= 8;
00229     }
00230 
00231   /* Nothing found.  */
00232   return 0;
00233 }
00234 
00235 
00236 static long int  __attribute__ ((noinline))
00237 handle_intel (int name, unsigned int maxidx)
00238 {
00239   if (maxidx < 2)
00240     {
00241       // XXX Do such processors exist?  When we know we can fill in some
00242       // values.
00243       return 0;
00244     }
00245 
00246   /* OK, we can use the CPUID instruction to get all info about the
00247      caches.  */
00248   unsigned int cnt = 0;
00249   unsigned int max = 1;
00250   long int result = 0;
00251   bool no_level_2_or_3 = false;
00252   bool has_level_2 = false;
00253   while (cnt++ < max)
00254     {
00255       unsigned int eax;
00256       unsigned int ebx;
00257       unsigned int ecx;
00258       unsigned int edx;
00259       asm volatile ("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1"
00260                   : "=a" (eax), "=r" (ebx), "=c" (ecx), "=d" (edx)
00261                   : "0" (2));
00262 
00263       /* The low byte of EAX in the first round contain the number of
00264         rounds we have to make.  At least one, the one we are already
00265         doing.  */
00266       if (cnt == 1)
00267        {
00268          max = eax & 0xff;
00269          eax &= 0xffffff00;
00270        }
00271 
00272       /* Process the individual registers' value.  */
00273       result = intel_check_word (name, eax, &has_level_2, &no_level_2_or_3);
00274       if (result != 0)
00275        return result;
00276 
00277       result = intel_check_word (name, ebx, &has_level_2, &no_level_2_or_3);
00278       if (result != 0)
00279        return result;
00280 
00281       result = intel_check_word (name, ecx, &has_level_2, &no_level_2_or_3);
00282       if (result != 0)
00283        return result;
00284 
00285       result = intel_check_word (name, edx, &has_level_2, &no_level_2_or_3);
00286       if (result != 0)
00287        return result;
00288     }
00289 
00290   if (name >= _SC_LEVEL2_CACHE_SIZE && name <= _SC_LEVEL3_CACHE_LINESIZE
00291       && no_level_2_or_3)
00292     return -1;
00293 
00294   return 0;
00295 }
00296 
00297 
00298 static long int __attribute__ ((noinline))
00299 handle_amd (int name)
00300 {
00301   unsigned int eax;
00302   unsigned int ebx;
00303   unsigned int ecx;
00304   unsigned int edx;
00305   asm volatile ("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1"
00306               : "=a" (eax), "=r" (ebx), "=c" (ecx), "=d" (edx)
00307               : "0" (0x80000000));
00308 
00309   if (name >= _SC_LEVEL3_CACHE_SIZE)
00310     return 0;
00311 
00312   unsigned int fn = 0x80000005 + (name >= _SC_LEVEL2_CACHE_SIZE);
00313   if (eax < fn)
00314     return 0;
00315 
00316   asm volatile ("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1"
00317               : "=a" (eax), "=r" (ebx), "=c" (ecx), "=d" (edx)
00318               : "0" (fn));
00319 
00320   if (name < _SC_LEVEL1_DCACHE_SIZE)
00321     {
00322       name += _SC_LEVEL1_DCACHE_SIZE - _SC_LEVEL1_ICACHE_SIZE;
00323       ecx = edx;
00324     }
00325 
00326   switch (name)
00327     {
00328     case _SC_LEVEL1_DCACHE_SIZE:
00329       return (ecx >> 14) & 0x3fc00;
00330     case _SC_LEVEL1_DCACHE_ASSOC:
00331       ecx >>= 16;
00332       if ((ecx & 0xff) == 0xff)
00333        /* Fully associative.  */
00334        return (ecx << 2) & 0x3fc00;
00335       return ecx & 0xff;
00336     case _SC_LEVEL1_DCACHE_LINESIZE:
00337       return ecx & 0xff;
00338     case _SC_LEVEL2_CACHE_SIZE:
00339       return (ecx & 0xf000) == 0 ? 0 : (ecx >> 6) & 0x3fffc00;
00340     case _SC_LEVEL2_CACHE_ASSOC:
00341       ecx >>= 12;
00342       switch (ecx & 0xf)
00343         {
00344         case 0:
00345         case 1:
00346         case 2:
00347         case 4:
00348          return ecx & 0xf;
00349        case 6:
00350          return 8;
00351        case 8:
00352          return 16;
00353        case 0xf:
00354          return (ecx << 6) & 0x3fffc00;
00355        default:
00356          return 0;
00357         }
00358     case _SC_LEVEL2_CACHE_LINESIZE:
00359       return (ecx & 0xf000) == 0 ? 0 : ecx & 0xff;
00360     default:
00361       assert (! "cannot happen");
00362     }
00363   return -1;
00364 }
00365 
00366 
00367 static int
00368 i386_i486_test (void)
00369 {
00370   int eflags;
00371   int ac;
00372   asm volatile ("pushfl;\n\t"
00373               "popl %0;\n\t"
00374               "movl $0x240000, %1;\n\t"
00375               "xorl %0, %1;\n\t"
00376               "pushl %1;\n\t"
00377               "popfl;\n\t"
00378               "pushfl;\n\t"
00379               "popl %1;\n\t"
00380               "xorl %0, %1;\n\t"
00381               "pushl %0;\n\t"
00382               "popfl"
00383               : "=r" (eflags), "=r" (ac));
00384 
00385   return ac;
00386 }
00387 
00388 
00389 /* Get the value of the system variable NAME.  */
00390 long int
00391 __sysconf (int name)
00392 {
00393   /* All the remainder, except the cache information, is handled in
00394      the generic code.  */
00395   if (name < _SC_LEVEL1_ICACHE_SIZE || name > _SC_LEVEL4_CACHE_LINESIZE)
00396     return linux_sysconf (name);
00397 
00398   /* Recognize i386 and compatible.  These don't have any cache on
00399      board.  */
00400   int ac = i386_i486_test ();
00401 
00402   if (ac == 0)
00403     /* This is an i386.  */
00404     // XXX Is this true for all brands?
00405     return -1;
00406 
00407   /* Detect i486, the last Intel processor without CPUID.  */
00408   if ((ac & (1 << 21)) == 0)
00409     {
00410       /* No CPUID.  */
00411       // XXX Fill in info about other brands.  For now only Intel.
00412       return handle_i486 (name);
00413     }
00414 
00415   /* Find out what brand of processor.  */
00416   unsigned int eax;
00417   unsigned int ebx;
00418   unsigned int ecx;
00419   unsigned int edx;
00420   asm volatile ("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1"
00421               : "=a" (eax), "=r" (ebx), "=c" (ecx), "=d" (edx)
00422               : "0" (0));
00423 
00424   /* This spells out "GenuineIntel".  */
00425   if (ebx == 0x756e6547 && ecx == 0x6c65746e && edx == 0x49656e69)
00426     return handle_intel (name, eax);
00427 
00428   /* This spells out "AuthenticAMD".  */
00429   if (ebx == 0x68747541 && ecx == 0x444d4163 && edx == 0x69746e65)
00430     return handle_amd (name);
00431 
00432   // XXX Fill in more vendors.
00433 
00434   /* CPU not known, we have no information.  */
00435   return 0;
00436 }
00437 
00438 /* Now the generic Linux version.  */
00439 #undef __sysconf
00440 #define __sysconf static linux_sysconf
00441 #include "../sysconf.c"