chiark / gitweb /
eglibc (2.11.3-4+deb6u3) squeeze-lts; urgency=medium
[eglibc.git] / sysdeps / unix / sysv / linux / i386 / sysconf.c
1 /* Get file-specific information about a file.  Linux version.
2    Copyright (C) 2003, 2004, 2006, 2007, 2009 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <assert.h>
21 #include <stdbool.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <hp-timing.h>
25
26 static long int linux_sysconf (int name);
27
28
29 static long int __attribute__ ((noinline))
30 handle_i486 (int name)
31 {
32   /* The processor only has a unified level 1 cache of 8k.  */
33   switch (name)
34     {
35     case _SC_LEVEL1_ICACHE_SIZE:
36     case _SC_LEVEL1_DCACHE_SIZE:
37       return 8 * 1024;
38
39     case _SC_LEVEL1_ICACHE_ASSOC:
40     case _SC_LEVEL1_DCACHE_ASSOC:
41       // XXX Anybody know this?
42       return 0;
43
44     case _SC_LEVEL1_ICACHE_LINESIZE:
45     case _SC_LEVEL1_DCACHE_LINESIZE:
46       // XXX Anybody know for sure?
47       return 16;
48
49     case _SC_LEVEL2_CACHE_SIZE:
50     case _SC_LEVEL2_CACHE_ASSOC:
51     case _SC_LEVEL2_CACHE_LINESIZE:
52     case _SC_LEVEL3_CACHE_SIZE:
53     case _SC_LEVEL3_CACHE_ASSOC:
54     case _SC_LEVEL3_CACHE_LINESIZE:
55     case _SC_LEVEL4_CACHE_SIZE:
56     case _SC_LEVEL4_CACHE_ASSOC:
57       /* Not available.  */
58       break;
59
60     default:
61       assert (! "cannot happen");
62     }
63
64   return -1;
65 }
66
67
68 static const struct intel_02_cache_info
69 {
70   unsigned char idx;
71   unsigned char assoc;
72   unsigned char linesize;
73   unsigned char rel_name;
74   unsigned int size;
75 } intel_02_known [] =
76   {
77 #define M(sc) ((sc) - _SC_LEVEL1_ICACHE_SIZE)
78     { 0x06,  4, 32, M(_SC_LEVEL1_ICACHE_SIZE),    8192 },
79     { 0x08,  4, 32, M(_SC_LEVEL1_ICACHE_SIZE),   16384 },
80     { 0x09,  4, 32, M(_SC_LEVEL1_ICACHE_SIZE),   32768 },
81     { 0x0a,  2, 32, M(_SC_LEVEL1_DCACHE_SIZE),    8192 },
82     { 0x0c,  4, 32, M(_SC_LEVEL1_DCACHE_SIZE),   16384 },
83     { 0x0d,  4, 64, M(_SC_LEVEL1_DCACHE_SIZE),   16384 },
84     { 0x21,  8, 64, M(_SC_LEVEL2_CACHE_SIZE),   262144 },
85     { 0x22,  4, 64, M(_SC_LEVEL3_CACHE_SIZE),   524288 },
86     { 0x23,  8, 64, M(_SC_LEVEL3_CACHE_SIZE),  1048576 },
87     { 0x25,  8, 64, M(_SC_LEVEL3_CACHE_SIZE),  2097152 },
88     { 0x29,  8, 64, M(_SC_LEVEL3_CACHE_SIZE),  4194304 },
89     { 0x2c,  8, 64, M(_SC_LEVEL1_DCACHE_SIZE),   32768 },
90     { 0x30,  8, 64, M(_SC_LEVEL1_ICACHE_SIZE),   32768 },
91     { 0x39,  4, 64, M(_SC_LEVEL2_CACHE_SIZE),   131072 },
92     { 0x3a,  6, 64, M(_SC_LEVEL2_CACHE_SIZE),   196608 },
93     { 0x3b,  2, 64, M(_SC_LEVEL2_CACHE_SIZE),   131072 },
94     { 0x3c,  4, 64, M(_SC_LEVEL2_CACHE_SIZE),   262144 },
95     { 0x3d,  6, 64, M(_SC_LEVEL2_CACHE_SIZE),   393216 },
96     { 0x3e,  4, 64, M(_SC_LEVEL2_CACHE_SIZE),   524288 },
97     { 0x3f,  2, 64, M(_SC_LEVEL2_CACHE_SIZE),   262144 },
98     { 0x41,  4, 32, M(_SC_LEVEL2_CACHE_SIZE),   131072 },
99     { 0x42,  4, 32, M(_SC_LEVEL2_CACHE_SIZE),   262144 },
100     { 0x43,  4, 32, M(_SC_LEVEL2_CACHE_SIZE),   524288 },
101     { 0x44,  4, 32, M(_SC_LEVEL2_CACHE_SIZE),  1048576 },
102     { 0x45,  4, 32, M(_SC_LEVEL2_CACHE_SIZE),  2097152 },
103     { 0x46,  4, 64, M(_SC_LEVEL3_CACHE_SIZE),  4194304 },
104     { 0x47,  8, 64, M(_SC_LEVEL3_CACHE_SIZE),  8388608 },
105     { 0x48, 12, 64, M(_SC_LEVEL2_CACHE_SIZE),  3145728 },
106     { 0x49, 16, 64, M(_SC_LEVEL2_CACHE_SIZE),  4194304 },
107     { 0x4a, 12, 64, M(_SC_LEVEL3_CACHE_SIZE),  6291456 },
108     { 0x4b, 16, 64, M(_SC_LEVEL3_CACHE_SIZE),  8388608 },
109     { 0x4c, 12, 64, M(_SC_LEVEL3_CACHE_SIZE), 12582912 },
110     { 0x4d, 16, 64, M(_SC_LEVEL3_CACHE_SIZE), 16777216 },
111     { 0x4e, 24, 64, M(_SC_LEVEL2_CACHE_SIZE),  6291456 },
112     { 0x60,  8, 64, M(_SC_LEVEL1_DCACHE_SIZE),   16384 },
113     { 0x66,  4, 64, M(_SC_LEVEL1_DCACHE_SIZE),    8192 },
114     { 0x67,  4, 64, M(_SC_LEVEL1_DCACHE_SIZE),   16384 },
115     { 0x68,  4, 64, M(_SC_LEVEL1_DCACHE_SIZE),   32768 },
116     { 0x78,  8, 64, M(_SC_LEVEL2_CACHE_SIZE),  1048576 },
117     { 0x79,  8, 64, M(_SC_LEVEL2_CACHE_SIZE),   131072 },
118     { 0x7a,  8, 64, M(_SC_LEVEL2_CACHE_SIZE),   262144 },
119     { 0x7b,  8, 64, M(_SC_LEVEL2_CACHE_SIZE),   524288 },
120     { 0x7c,  8, 64, M(_SC_LEVEL2_CACHE_SIZE),  1048576 },
121     { 0x7d,  8, 64, M(_SC_LEVEL2_CACHE_SIZE),  2097152 },
122     { 0x7f,  2, 64, M(_SC_LEVEL2_CACHE_SIZE),   524288 },
123     { 0x82,  8, 32, M(_SC_LEVEL2_CACHE_SIZE),   262144 },
124     { 0x83,  8, 32, M(_SC_LEVEL2_CACHE_SIZE),   524288 },
125     { 0x84,  8, 32, M(_SC_LEVEL2_CACHE_SIZE),  1048576 },
126     { 0x85,  8, 32, M(_SC_LEVEL2_CACHE_SIZE),  2097152 },
127     { 0x86,  4, 64, M(_SC_LEVEL2_CACHE_SIZE),   524288 },
128     { 0x87,  8, 64, M(_SC_LEVEL2_CACHE_SIZE),  1048576 },
129     { 0xd0,  4, 64, M(_SC_LEVEL3_CACHE_SIZE),   524288 },
130     { 0xd1,  4, 64, M(_SC_LEVEL3_CACHE_SIZE),  1048576 },
131     { 0xd2,  4, 64, M(_SC_LEVEL3_CACHE_SIZE),  2097152 },
132     { 0xd6,  8, 64, M(_SC_LEVEL3_CACHE_SIZE),  1048576 },
133     { 0xd7,  8, 64, M(_SC_LEVEL3_CACHE_SIZE),  2097152 },
134     { 0xd8,  8, 64, M(_SC_LEVEL3_CACHE_SIZE),  4194304 },
135     { 0xdc, 12, 64, M(_SC_LEVEL3_CACHE_SIZE),  2097152 },
136     { 0xdd, 12, 64, M(_SC_LEVEL3_CACHE_SIZE),  4194304 },
137     { 0xde, 12, 64, M(_SC_LEVEL3_CACHE_SIZE),  8388608 },
138     { 0xe3, 16, 64, M(_SC_LEVEL3_CACHE_SIZE),  2097152 },
139     { 0xe3, 16, 64, M(_SC_LEVEL3_CACHE_SIZE),  4194304 },
140     { 0xe4, 16, 64, M(_SC_LEVEL3_CACHE_SIZE),  8388608 },
141     { 0xea, 24, 64, M(_SC_LEVEL3_CACHE_SIZE), 12582912 },
142     { 0xeb, 24, 64, M(_SC_LEVEL3_CACHE_SIZE), 18874368 },
143     { 0xec, 24, 64, M(_SC_LEVEL3_CACHE_SIZE), 25165824 },
144   };
145 #define nintel_02_known (sizeof (intel_02_known) / sizeof (intel_02_known[0]))
146
147
148 static int
149 intel_02_known_compare (const void *p1, const void *p2)
150 {
151   const struct intel_02_cache_info *i1;
152   const struct intel_02_cache_info *i2;
153
154   i1 = (const struct intel_02_cache_info *) p1;
155   i2 = (const struct intel_02_cache_info *) p2;
156
157   if (i1->idx == i2->idx)
158     return 0;
159
160   return i1->idx < i2->idx ? -1 : 1;
161 }
162
163
164 static long int
165 __attribute__ ((noinline))
166 intel_check_word (int name, unsigned int value, bool *has_level_2,
167                   bool *no_level_2_or_3)
168 {
169   if ((value & 0x80000000) != 0)
170     /* The register value is reserved.  */
171     return 0;
172
173   /* Fold the name.  The _SC_ constants are always in the order SIZE,
174      ASSOC, LINESIZE.  */
175   int folded_rel_name = (M(name) / 3) * 3;
176
177   while (value != 0)
178     {
179       unsigned int byte = value & 0xff;
180
181       if (byte == 0x40)
182         {
183           *no_level_2_or_3 = true;
184
185           if (folded_rel_name == M(_SC_LEVEL3_CACHE_SIZE))
186             /* No need to look further.  */
187             break;
188         }
189       else
190         {
191           if (byte == 0x49 && folded_rel_name == M(_SC_LEVEL3_CACHE_SIZE))
192             {
193               /* Intel reused this value.  For family 15, model 6 it
194                  specifies the 3rd level cache.  Otherwise the 2nd
195                  level cache.  */
196               unsigned int eax;
197               unsigned int ebx;
198               unsigned int ecx;
199               unsigned int edx;
200               asm volatile ("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1"
201                             : "=a" (eax), "=r" (ebx), "=c" (ecx), "=d" (edx)
202                             : "0" (1));
203
204               unsigned int family = ((eax >> 20) & 0xff) + ((eax >> 8) & 0xf);
205               unsigned int model = ((((eax >>16) & 0xf) << 4)
206                                     + ((eax >> 4) & 0xf));
207               if (family == 15 && model == 6)
208                 {
209                   /* The level 3 cache is encoded for this model like
210                      the level 2 cache is for other models.  Pretend
211                      the caller asked for the level 2 cache.  */
212                   name = (_SC_LEVEL2_CACHE_SIZE
213                           + (name - _SC_LEVEL3_CACHE_SIZE));
214                   folded_rel_name = M(_SC_LEVEL2_CACHE_SIZE);
215                 }
216             }
217
218           struct intel_02_cache_info *found;
219           struct intel_02_cache_info search;
220
221           search.idx = byte;
222           found = bsearch (&search, intel_02_known, nintel_02_known,
223                            sizeof (intel_02_known[0]), intel_02_known_compare);
224           if (found != NULL)
225             {
226               if (found->rel_name == folded_rel_name)
227                 {
228                   unsigned int offset = M(name) - folded_rel_name;
229
230                   if (offset == 0)
231                     /* Cache size.  */
232                     return found->size;
233                   if (offset == 1)
234                     return found->assoc;
235
236                   assert (offset == 2);
237                   return found->linesize;
238                 }
239
240               if (found->rel_name == M(_SC_LEVEL2_CACHE_SIZE))
241                 *has_level_2 = true;
242             }
243         }
244
245       /* Next byte for the next round.  */
246       value >>= 8;
247     }
248
249   /* Nothing found.  */
250   return 0;
251 }
252
253
254 static long int  __attribute__ ((noinline))
255 handle_intel (int name, unsigned int maxidx)
256 {
257   if (maxidx < 2)
258     {
259       // XXX Do such processors exist?  When we know we can fill in some
260       // values.
261       return 0;
262     }
263
264   /* OK, we can use the CPUID instruction to get all info about the
265      caches.  */
266   unsigned int cnt = 0;
267   unsigned int max = 1;
268   long int result = 0;
269   bool no_level_2_or_3 = false;
270   bool has_level_2 = false;
271   while (cnt++ < max)
272     {
273       unsigned int eax;
274       unsigned int ebx;
275       unsigned int ecx;
276       unsigned int edx;
277       asm volatile ("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1"
278                     : "=a" (eax), "=r" (ebx), "=c" (ecx), "=d" (edx)
279                     : "0" (2));
280
281       /* The low byte of EAX in the first round contain the number of
282          rounds we have to make.  At least one, the one we are already
283          doing.  */
284       if (cnt == 1)
285         {
286           max = eax & 0xff;
287           eax &= 0xffffff00;
288         }
289
290       /* Process the individual registers' value.  */
291       result = intel_check_word (name, eax, &has_level_2, &no_level_2_or_3);
292       if (result != 0)
293         return result;
294
295       result = intel_check_word (name, ebx, &has_level_2, &no_level_2_or_3);
296       if (result != 0)
297         return result;
298
299       result = intel_check_word (name, ecx, &has_level_2, &no_level_2_or_3);
300       if (result != 0)
301         return result;
302
303       result = intel_check_word (name, edx, &has_level_2, &no_level_2_or_3);
304       if (result != 0)
305         return result;
306     }
307
308   if (name >= _SC_LEVEL2_CACHE_SIZE && name <= _SC_LEVEL3_CACHE_LINESIZE
309       && no_level_2_or_3)
310     return -1;
311
312   return 0;
313 }
314
315
316 static long int __attribute__ ((noinline))
317 handle_amd (int name)
318 {
319   unsigned int eax;
320   unsigned int ebx;
321   unsigned int ecx;
322   unsigned int edx;
323   asm volatile ("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1"
324                 : "=a" (eax), "=r" (ebx), "=c" (ecx), "=d" (edx)
325                 : "0" (0x80000000));
326
327   if (name >= _SC_LEVEL3_CACHE_SIZE)
328     return 0;
329
330   unsigned int fn = 0x80000005 + (name >= _SC_LEVEL2_CACHE_SIZE);
331   if (eax < fn)
332     return 0;
333
334   asm volatile ("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1"
335                 : "=a" (eax), "=r" (ebx), "=c" (ecx), "=d" (edx)
336                 : "0" (fn));
337
338   if (name < _SC_LEVEL1_DCACHE_SIZE)
339     {
340       name += _SC_LEVEL1_DCACHE_SIZE - _SC_LEVEL1_ICACHE_SIZE;
341       ecx = edx;
342     }
343
344   switch (name)
345     {
346     case _SC_LEVEL1_DCACHE_SIZE:
347       return (ecx >> 14) & 0x3fc00;
348     case _SC_LEVEL1_DCACHE_ASSOC:
349       ecx >>= 16;
350       if ((ecx & 0xff) == 0xff)
351         /* Fully associative.  */
352         return (ecx << 2) & 0x3fc00;
353       return ecx & 0xff;
354     case _SC_LEVEL1_DCACHE_LINESIZE:
355       return ecx & 0xff;
356     case _SC_LEVEL2_CACHE_SIZE:
357       return (ecx & 0xf000) == 0 ? 0 : (ecx >> 6) & 0x3fffc00;
358     case _SC_LEVEL2_CACHE_ASSOC:
359       ecx >>= 12;
360       switch (ecx & 0xf)
361         {
362         case 0:
363         case 1:
364         case 2:
365         case 4:
366           return ecx & 0xf;
367         case 6:
368           return 8;
369         case 8:
370           return 16;
371         case 0xf:
372           return (ecx << 6) & 0x3fffc00;
373         default:
374           return 0;
375         }
376     case _SC_LEVEL2_CACHE_LINESIZE:
377       return (ecx & 0xf000) == 0 ? 0 : ecx & 0xff;
378     default:
379       assert (! "cannot happen");
380     }
381   return -1;
382 }
383
384
385 static int
386 i386_i486_test (void)
387 {
388   int eflags;
389   int ac;
390   asm volatile ("pushfl;\n\t"
391                 "popl %0;\n\t"
392                 "movl $0x240000, %1;\n\t"
393                 "xorl %0, %1;\n\t"
394                 "pushl %1;\n\t"
395                 "popfl;\n\t"
396                 "pushfl;\n\t"
397                 "popl %1;\n\t"
398                 "xorl %0, %1;\n\t"
399                 "pushl %0;\n\t"
400                 "popfl"
401                 : "=r" (eflags), "=r" (ac));
402
403   return ac;
404 }
405
406
407 /* Get the value of the system variable NAME.  */
408 long int
409 __sysconf (int name)
410 {
411   /* All the remainder, except the cache information, is handled in
412      the generic code.  */
413   if (name < _SC_LEVEL1_ICACHE_SIZE || name > _SC_LEVEL4_CACHE_LINESIZE)
414     return linux_sysconf (name);
415
416   /* Recognize i386 and compatible.  These don't have any cache on
417      board.  */
418   int ac = i386_i486_test ();
419
420   if (ac == 0)
421     /* This is an i386.  */
422     // XXX Is this true for all brands?
423     return -1;
424
425   /* Detect i486, the last Intel processor without CPUID.  */
426   if ((ac & (1 << 21)) == 0)
427     {
428       /* No CPUID.  */
429       // XXX Fill in info about other brands.  For now only Intel.
430       return handle_i486 (name);
431     }
432
433   /* Find out what brand of processor.  */
434   unsigned int eax;
435   unsigned int ebx;
436   unsigned int ecx;
437   unsigned int edx;
438   asm volatile ("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1"
439                 : "=a" (eax), "=r" (ebx), "=c" (ecx), "=d" (edx)
440                 : "0" (0));
441
442   /* This spells out "GenuineIntel".  */
443   if (ebx == 0x756e6547 && ecx == 0x6c65746e && edx == 0x49656e69)
444     return handle_intel (name, eax);
445
446   /* This spells out "AuthenticAMD".  */
447   if (ebx == 0x68747541 && ecx == 0x444d4163 && edx == 0x69746e65)
448     return handle_amd (name);
449
450   // XXX Fill in more vendors.
451
452   /* CPU not known, we have no information.  */
453   return 0;
454 }
455
456 /* Now the generic Linux version.  */
457 #undef __sysconf
458 #define __sysconf static linux_sysconf
459 #include "../sysconf.c"