/ src / core / cpuid.cc
cpuid.cc
  1  #include "version.h"
  2  #include "config.h"
  3  #include "cpuid.h"
  4  #include <stdio.h>
  5  #include <string.h>
  6  #include <unistd.h>
  7  #include <fcntl.h>
  8  #include <sys/stat.h>
  9  #include <sys/time.h>
 10  #include <cstring>
 11  
 12  __ID("@(#) $Id$");
 13  
 14  #if defined(__i386__) || defined(__alpha__)
 15  
 16  static hwNode *getcache(hwNode & node,
 17  int n = 0)
 18  {
 19    char cachename[10];
 20    hwNode *cache = NULL;
 21  
 22    if (n < 0)
 23      n = 0;
 24  
 25    snprintf(cachename, sizeof(cachename), "cache:%d", n);
 26    cache = node.getChild(string(cachename));
 27  
 28    if (cache)
 29      return cache;
 30  
 31  // "cache:0" is equivalent to "cache" if we only have L1 cache
 32    if ((n == 0) && (node.countChildren(hw::memory) <= 1))
 33      cache = node.getChild(string("cache"));
 34    if (cache)
 35      return cache;
 36    else
 37      return NULL;
 38  }
 39  
 40  
 41  static hwNode *getcpu(hwNode & node,
 42  int n = 0)
 43  {
 44    char cpubusinfo[10];
 45    hwNode *cpu = NULL;
 46  
 47    if (n < 0)
 48      n = 0;
 49  
 50    snprintf(cpubusinfo, sizeof(cpubusinfo), "cpu@%d", n);
 51    cpu = node.findChildByBusInfo(cpubusinfo);
 52  
 53    if (cpu)
 54      return cpu;
 55  
 56    if (n > 0)
 57      return NULL;
 58  
 59    hwNode *core = node.getChild("core");
 60  
 61    if (core)
 62    {
 63      hwNode cpu("cpu", hw::processor);
 64  
 65      cpu.setBusInfo(cpubusinfo);
 66      cpu.addHint("icon", string("cpu"));
 67      cpu.claim();
 68  
 69      return core->addChild(cpu);
 70    }
 71    else
 72      return NULL;
 73  }
 74  #endif                                            // __i386__ || __alpha__
 75  
 76  #ifdef __i386__
 77  
 78  /* %ebx may be the PIC register.  */
 79  #define cpuid_up(in,a,b,c,d)\
 80    __asm__ ("xchgl\t%%ebx, %1\n\t"			\
 81  	   "cpuid\n\t"					\
 82  	   "xchgl\t%%ebx, %1\n\t"			\
 83  	   : "=a" (a), "=r" (b), "=c" (c), "=d" (d)	\
 84  	   : "0" (in))
 85  
 86  static void cpuid(int cpunumber,
 87  unsigned long idx,
 88  unsigned long &eax,
 89  unsigned long &ebx,
 90  unsigned long &ecx,
 91  unsigned long &edx)
 92  {
 93    char cpuname[50];
 94    int fd = -1;
 95    unsigned char buffer[16];
 96  
 97    snprintf(cpuname, sizeof(cpuname), "/dev/cpu/%d/cpuid", cpunumber);
 98    fd = open(cpuname, O_RDONLY);
 99    if (fd >= 0)
100    {
101      lseek(fd, idx, SEEK_CUR);
102      memset(buffer, 0, sizeof(buffer));
103      if(read(fd, buffer, sizeof(buffer)) == sizeof(buffer))
104      {
105        eax = (*(unsigned long *) buffer);
106        ebx = (*(unsigned long *) (buffer + 4));
107        ecx = (*(unsigned long *) (buffer + 8));
108        edx = (*(unsigned long *) (buffer + 12));
109      }
110      close(fd);
111    }
112    else
113      cpuid_up(idx, eax, ebx, ecx, edx);
114  }
115  
116  
117  /* Decode Intel TLB and cache info descriptors */
118  static void decode_intel_tlb(int x,
119  long long &l1cache,
120  long long &l2cache)
121  {
122    x &= 0xff;
123    switch (x)
124    {
125      case 0:
126        break;
127      case 0x1:
128  // Instruction TLB: 4KB pages, 4-way set assoc, 32 entries
129        break;
130      case 0x2:
131  // Instruction TLB: 4MB pages, 4-way set assoc, 2 entries
132        break;
133      case 0x3:
134  // Data TLB: 4KB pages, 4-way set assoc, 64 entries
135        break;
136      case 0x4:
137  // Data TLB: 4MB pages, 4-way set assoc, 8 entries
138        break;
139      case 0x6:
140  // 1st-level instruction cache: 8KB, 4-way set assoc, 32 byte line size
141        l1cache += 8 * 1024;
142        break;
143      case 0x8:
144  // 1st-level instruction cache: 16KB, 4-way set assoc, 32 byte line size
145        l1cache += 16 * 1024;
146        break;
147      case 0xa:
148  // 1st-level data cache: 8KB, 2-way set assoc, 32 byte line size
149        l1cache += 8 * 1024;
150        break;
151      case 0xc:
152  // 1st-level data cache: 16KB, 4-way set assoc, 32 byte line size
153        l1cache += 16 * 1024;
154        break;
155      case 0x40:
156  // No 2nd-level cache, or if 2nd-level cache exists, no 3rd-level cache
157        break;
158      case 0x41:
159  // 2nd-level cache: 128KB, 4-way set assoc, 32 byte line size
160        l2cache = 128 * 1024;
161        break;
162      case 0x42:
163  // 2nd-level cache: 256KB, 4-way set assoc, 32 byte line size
164        l2cache = 256 * 1024;
165        break;
166      case 0x43:
167  // 2nd-level cache: 512KB, 4-way set assoc, 32 byte line size
168        l2cache = 512 * 1024;
169        break;
170      case 0x44:
171  // 2nd-level cache: 1MB, 4-way set assoc, 32 byte line size
172        l2cache = 1024 * 1024;
173        break;
174      case 0x45:
175  // 2nd-level cache: 2MB, 4-way set assoc, 32 byte line size
176        l2cache = 2 * 1024 * 1024;
177        break;
178      case 0x50:
179  // Instruction TLB: 4KB and 2MB or 4MB pages, 64 entries
180        break;
181      case 0x51:
182  // Instruction TLB: 4KB and 2MB or 4MB pages, 128 entries
183        break;
184      case 0x52:
185  // Instruction TLB: 4KB and 2MB or 4MB pages, 256 entries
186        break;
187      case 0x5b:
188  // Data TLB: 4KB and 4MB pages, 64 entries
189        break;
190      case 0x5c:
191  // Data TLB: 4KB and 4MB pages, 128 entries
192        break;
193      case 0x5d:
194  // Data TLB: 4KB and 4MB pages, 256 entries
195        break;
196      case 0x66:
197  // 1st-level data cache: 8KB, 4-way set assoc, 64 byte line size
198        l1cache += 8 * 1024;
199        break;
200      case 0x67:
201  // 1st-level data cache: 16KB, 4-way set assoc, 64 byte line size
202        l1cache += 16 * 1024;
203        break;
204      case 0x68:
205  // 1st-level data cache: 32KB, 4-way set assoc, 64 byte line size
206        l1cache += 32 * 1024;
207        break;
208      case 0x70:
209  // Trace cache: 12K-micro-op, 4-way set assoc
210        break;
211      case 0x71:
212  // Trace cache: 16K-micro-op, 4-way set assoc
213        break;
214      case 0x72:
215  // Trace cache: 32K-micro-op, 4-way set assoc
216        break;
217      case 0x79:
218  // 2nd-level cache: 128KB, 8-way set assoc, sectored, 64 byte line size
219        l2cache += 128 * 1024;
220        break;
221      case 0x7a:
222  // 2nd-level cache: 256KB, 8-way set assoc, sectored, 64 byte line size
223        l2cache += 256 * 1024;
224        break;
225      case 0x7b:
226  // 2nd-level cache: 512KB, 8-way set assoc, sectored, 64 byte line size
227        l2cache += 512 * 1024;
228        break;
229      case 0x7c:
230  // 2nd-level cache: 1MB, 8-way set assoc, sectored, 64 byte line size
231        l2cache += 1024 * 1024;
232        break;
233      case 0x82:
234  // 2nd-level cache: 256KB, 8-way set assoc, 32 byte line size
235        l2cache += 256 * 1024;
236        break;
237      case 0x83:
238  // 2nd-level cache: 512KB, 8-way set assoc 32 byte line size
239        l2cache += 512 * 1024;
240        break;
241      case 0x84:
242  // 2nd-level cache: 1MB, 8-way set assoc, 32 byte line size
243        l2cache += 1024 * 1024;
244        break;
245      case 0x85:
246  // 2nd-level cache: 2MB, 8-way set assoc, 32 byte line size
247        l2cache += 2 * 1024 * 1024;
248        break;
249      default:
250  // unknown TLB/cache descriptor
251        break;
252    }
253  }
254  
255  
256  static bool dointel(unsigned long maxi,
257  hwNode * cpu,
258  int cpunumber = 0)
259  {
260    char buffer[1024];
261    unsigned long signature = 0, flags = 0, bflags = 0, eax = 0, ebx = 0, ecx = 0, edx = 0, unused = 0;
262    int stepping, model, family;
263  
264    if (!cpu)
265      return false;
266  
267    cpu->addHint("logo", string("intel"));
268  
269    if (maxi >= 1)
270    {
271      cpuid(cpunumber, 1, eax, ebx, ecx, edx);
272  
273      signature = eax;
274  
275      stepping = eax & 0xf;
276      model = (eax >> 4) & 0xf;
277      family = (eax >> 8) & 0xf;
278      flags = edx;
279      bflags = ebx;
280  
281      snprintf(buffer, sizeof(buffer), "%d.%d.%d", family, model, stepping);
282      cpu->setVersion(buffer);
283  
284      if(ecx & (1 << 5))
285        cpu->addCapability("vmx", _("CPU virtualization (Vanderpool)"));
286  
287  /* Hyper-Threading Technology */
288      if (flags & (1 << 28))
289      {
290        char buff[20];
291        unsigned int nr_ht = (bflags >> 16) & 0xFF;
292        unsigned int phys_id = (bflags >> 24) & 0xFF;
293  
294        snprintf(buff, sizeof(buff), "%d", phys_id);
295        cpu->setConfig("id", buff);
296  
297        hwNode logicalcpu("logicalcpu", hw::processor);
298        logicalcpu.setDescription(_("Logical CPU"));
299        logicalcpu.addCapability("logical", _("Logical CPU"));
300        logicalcpu.setWidth(cpu->getWidth());
301        logicalcpu.claim();
302        cpu->addCapability("ht", _("HyperThreading"));
303  
304        if(nr_ht>1)
305          for(unsigned int i=0; i< nr_ht; i++)
306        {
307          snprintf(buff, sizeof(buff), "CPU:%d.%d", phys_id, i);
308          logicalcpu.setHandle(buff);
309          logicalcpu.setPhysId(phys_id, i+1);
310          cpu->addChild(logicalcpu);
311          cpu->claim();
312        }
313      }
314  
315    }
316  
317    if (maxi >= 2)
318    {
319  /*
320   * Decode TLB and cache info
321   */
322      int ntlb, i;
323      long long l1cache = 0, l2cache = 0;
324  
325      ntlb = 255;
326      for (i = 0; i < ntlb; i++)
327      {
328        cpuid(cpunumber, 2, eax, ebx, ecx, edx);
329        ntlb = eax & 0xff;
330        decode_intel_tlb(eax >> 8, l1cache, l2cache);
331        decode_intel_tlb(eax >> 16, l1cache, l2cache);
332        decode_intel_tlb(eax >> 24, l1cache, l2cache);
333  
334        if ((ebx & 0x80000000) == 0)
335        {
336          decode_intel_tlb(ebx, l1cache, l2cache);
337          decode_intel_tlb(ebx >> 8, l1cache, l2cache);
338          decode_intel_tlb(ebx >> 16, l1cache, l2cache);
339          decode_intel_tlb(ebx >> 24, l1cache, l2cache);
340        }
341        if ((ecx & 0x80000000) == 0)
342        {
343          decode_intel_tlb(ecx, l1cache, l2cache);
344          decode_intel_tlb(ecx >> 8, l1cache, l2cache);
345          decode_intel_tlb(ecx >> 16, l1cache, l2cache);
346          decode_intel_tlb(ecx >> 24, l1cache, l2cache);
347        }
348        if ((edx & 0x80000000) == 0)
349        {
350          decode_intel_tlb(edx, l1cache, l2cache);
351          decode_intel_tlb(edx >> 8, l1cache, l2cache);
352          decode_intel_tlb(edx >> 16, l1cache, l2cache);
353          decode_intel_tlb(edx >> 24, l1cache, l2cache);
354        }
355      }
356  
357      if (l1cache != 0)
358      {
359        hwNode *l1 = getcache(*cpu, 0);
360        hwNode *l2 = getcache(*cpu, 1);
361  
362        if (l1)
363        {
364          l1->setSize(l1cache);
365          if (l1->getDescription() == "")
366            l1->setDescription(_("L1 cache"));
367        }
368        else
369        {
370          hwNode cache("cache",
371            hw::memory);
372          cache.setSize(l1cache);
373          cache.setDescription(_("L1 cache"));
374  
375          cpu->addChild(cache);
376        }
377  
378        if (l2cache != 0)
379        {
380          if (l2 && (l2cache != 0))
381          {
382            l2->setSize(l2cache);
383            if (l2->getDescription() == "")
384              l2->setDescription(_("L2 cache"));
385          }
386          else
387          {
388            hwNode cache("cache",
389              hw::memory);
390            cache.setSize(l2cache);
391            cache.setDescription(_("L2 cache"));
392  
393            cpu->addChild(cache);
394          }
395        }
396      }
397    }
398  
399    if (maxi >= 3)
400    {
401      cpuid(cpunumber, 3, unused, unused, ecx, edx);
402  
403      snprintf(buffer, sizeof(buffer),
404        "%04lX-%04lX-%04lX-%04lX-%04lX-%04lX",
405        signature >> 16,
406        signature & 0xffff,
407        edx >> 16, edx & 0xffff, ecx >> 16, ecx & 0xffff);
408  
409      cpu->setSerial(buffer);
410    }
411    else
412      cpu->setSerial("");
413  
414    return true;
415  }
416  
417  
418  static bool doamd(unsigned long maxi,
419  hwNode * cpu,
420  int cpunumber = 0)
421  {
422    unsigned long maxei = 0, eax, ebx, ecx, edx;
423    long long l1cache = 0, l2cache = 0;
424    unsigned int family = 0, model = 0, stepping = 0;
425    char buffer[1024];
426  
427    if (maxi < 1)
428      return false;
429  
430    cpu->addHint("logo", string("amd"));
431  
432    cpuid(cpunumber, 1, eax, ebx, ecx, edx);
433    stepping = eax & 0xf;
434    model = (eax >> 4) & 0xf;
435    family = (eax >> 8) & 0xf;
436    snprintf(buffer, sizeof(buffer), "%d.%d.%d", family, model, stepping);
437    cpu->setVersion(buffer);
438  
439    cpuid(cpunumber, 0x80000000, maxei, ebx, ecx, edx);
440  
441    if (maxei >= 0x80000005)
442    {
443      cpuid(cpunumber, 0x80000005, eax, ebx, ecx, edx);
444  
445      l1cache = (ecx >> 24) * 1024;                 // data cache
446      l1cache += (edx >> 24) * 1024;                // instruction cache
447    }
448    if (maxei >= 0x80000006)
449    {
450      cpuid(cpunumber, 0x80000006, eax, ebx, ecx, edx);
451  
452      l2cache = (ecx >> 16) * 1024;
453    }
454  
455    if (l1cache != 0)
456    {
457      hwNode *l1 = cpu->getChild("cache:0");
458      hwNode *l2 = cpu->getChild("cache:1");
459  
460      if (l1)
461        l1->setSize(l1cache);
462      else
463      {
464        hwNode newl1("cache",
465          hw::memory);
466  
467        newl1.setDescription(_("L1 cache"));
468        newl1.setSize(l1cache);
469  
470        cpu->addChild(newl1);
471      }
472      if (l2 && l2cache)
473        l2->setSize(l2cache);
474      else
475      {
476        hwNode newl2("cache",
477          hw::memory);
478  
479        newl2.setDescription(_("L2 cache"));
480        newl2.setSize(l2cache);
481  
482        if (l2cache)
483          cpu->addChild(newl2);
484      }
485    }
486  
487    return true;
488  }
489  
490  
491  static bool docyrix(unsigned long maxi,
492  hwNode * cpu,
493  int cpunumber = 0)
494  {
495    unsigned long eax, ebx, ecx, edx;
496    unsigned int family = 0, model = 0, stepping = 0;
497    char buffer[1024];
498  
499    if (maxi < 1)
500      return false;
501  
502    cpuid(cpunumber, 1, eax, ebx, ecx, edx);
503    stepping = eax & 0xf;
504    model = (eax >> 4) & 0xf;
505    family = (eax >> 8) & 0xf;
506    snprintf(buffer, sizeof(buffer), "%d.%d.%d", family, model, stepping);
507    cpu->setVersion(buffer);
508  
509    return true;
510  }
511  
512  
513  static __inline__ bool flag_is_changeable_p(unsigned int flag)
514  {
515    unsigned int f1, f2;
516    __asm__ volatile ("pushfl\n\t"
517      "pushfl\n\t"
518      "popl %0\n\t"
519      "movl %0,%1\n\t"
520      "xorl %2,%0\n\t"
521      "pushl %0\n\t"
522      "popfl\n\t"
523      "pushfl\n\t" "popl %0\n\t" "popfl\n\t":"=&r" (f1),
524      "=&r"(f2):"ir"(flag));
525    return ((f1 ^ f2) & flag) != 0;
526  }
527  
528  
529  static bool haveCPUID()
530  {
531    return flag_is_changeable_p(0x200000);
532  }
533  
534  
535  /*
536   * Estimate CPU MHz routine by Andrea Arcangeli <andrea@suse.de>
537   * Small changes by David Sterba <sterd9am@ss1000.ms.mff.cuni.cz>
538   *
539   */
540  
541  static __inline__ unsigned long long int rdtsc()
542  {
543    unsigned long long int x;
544    __asm__ volatile (".byte 0x0f, 0x31":"=A" (x));
545    return x;
546  }
547  
548  
549  static float estimate_MHz(int cpunum,
550  long sleeptime = 250000)
551  {
552    struct timezone tz;
553    struct timeval tvstart, tvstop;
554    unsigned long long int cycles[2];               /* gotta be 64 bit */
555    float microseconds;                             /* total time taken */
556    unsigned long eax, ebx, ecx, edx;
557    double freq = 1.0f;
558  
559  /*
560   * Make sure we have a TSC (and hence RDTSC)
561   */
562    cpuid(cpunum, 1, eax, ebx, ecx, edx);
563    if ((edx & (1 << 4)) == 0)
564    {
565      return 0;                                     // can't estimate frequency
566    }
567  
568    memset(&tz, 0, sizeof(tz));
569  
570  /*
571   * get this function in cached memory
572   */
573    gettimeofday(&tvstart, &tz);
574    cycles[0] = rdtsc();
575    gettimeofday(&tvstart, &tz);
576  
577  /*
578   * we don't trust that this is any specific length of time
579   */
580    usleep(sleeptime);
581  
582    gettimeofday(&tvstop, &tz);
583    cycles[1] = rdtsc();
584    gettimeofday(&tvstop, &tz);
585  
586    microseconds = (tvstop.tv_sec - tvstart.tv_sec) * 1000000 +
587      (tvstop.tv_usec - tvstart.tv_usec);
588  
589    return (float) (cycles[1] - cycles[0]) / (microseconds / freq);
590  }
591  
592  
593  static float average_MHz(int cpunum,
594  int tries = 2)
595  {
596    float frequency = 0;
597  
598    for (int i = 1; i <= tries; i++)
599      frequency += estimate_MHz(cpunum, i * 150000);
600  
601    if (tries > 0)
602      return frequency / (float) tries;
603    else
604      return 0;
605  }
606  
607  
608  static long round_MHz(float fMHz)
609  {
610    long MHz = (long)fMHz;
611  
612    if ((MHz % 50) > 15)
613      return ((MHz / 50) * 50) + 50;
614    else
615      return ((MHz / 50) * 50);
616  }
617  
618  
619  bool scan_cpuid(hwNode & n)
620  {
621    unsigned long maxi, ebx, ecx, edx;
622    hwNode *cpu = NULL;
623    int currentcpu = 0;
624  
625    if (!haveCPUID())
626      return false;
627  
628    while ((cpu = getcpu(n, currentcpu)))
629    {
630      cpu->claim(true);                             // claim the cpu and all its children
631      cpuid(currentcpu, 0, maxi, ebx, ecx, edx);
632      maxi &= 0xffff;
633  
634      switch (ebx)
635      {
636        case 0x756e6547:                            /* Intel */
637          dointel(maxi, cpu, currentcpu);
638          break;
639        case 0x68747541:                            /* AMD */
640          doamd(maxi, cpu, currentcpu);
641          break;
642        case 0x69727943:                            /* Cyrix */
643          docyrix(maxi, cpu, currentcpu);
644          break;
645        default:
646          return false;
647      }
648  
649      cpu->claim(true);                             // claim the cpu and all its children
650      if (cpu->getSize() == 0)
651        cpu->setSize((unsigned long long) (1000000uL * round_MHz(average_MHz(currentcpu))));
652  
653      currentcpu++;
654    }
655  
656    return true;
657  }
658  
659  
660  #else
661  
662  #ifdef __alpha__
663  
664  #define BWX (1 << 0)
665  #define FIX (1 << 1)
666  #define CIX (1 << 2)
667  #define MVI (1 << 8)
668  #define PAT (1 << 9)
669  #define PMI (1 << 12)
670  
671  bool scan_cpuid(hwNode & n)
672  {
673    hwNode *cpu = NULL;
674    int currentcpu = 0;
675    unsigned long ver = 0, mask = 0;
676  
677    while (cpu = getcpu(n, currentcpu))
678    {
679      asm("implver %0":"=r"(ver));
680      asm("amask %1, %0": "=r"(mask):"r"(-1));
681  
682      cpu->setVendor("Digital Equipment Corporation");
683      cpu->setProduct("Alpha");
684      cpu->setWidth(64);
685  
686      if ((~mask) & BWX)
687        cpu->addCapability("BWX");
688      if ((~mask) & FIX)
689        cpu->addCapability("FIX");
690      if ((~mask) & CIX)
691        cpu->addCapability("CIX");
692      if ((~mask) & MVI)
693        cpu->addCapability("MVI");
694      if ((~mask) & PAT)
695        cpu->addCapability("PAT");
696      if ((~mask) & PMI)
697        cpu->addCapability("PMI");
698  
699      switch (ver)
700      {
701        case 0:
702          cpu->setVersion("EV4");
703          break;
704        case 1:
705          switch (~mask)
706          {
707            case 0:
708              cpu->setVersion("EV5");
709              break;
710            case BWX:
711              cpu->setVersion("EV56");
712              break;
713            case BWX | MVI:
714              cpu->setVersion("PCA56");
715              break;
716            default:
717              cpu->setVersion("EV5 unknown");
718          }
719          break;
720        case 2:
721          switch (~mask)
722          {
723            case BWX | FIX | MVI | PAT:
724              cpu->setVersion("EV6");
725              break;
726            case BWX | FIX | MVI | PAT | CIX:
727              cpu->setVersion("EV67");
728              break;
729            case BWX | FIX | MVI | PAT | CIX | PMI:
730              cpu->setVersion("EV68");
731              break;
732            default:
733              cpu->setVersion("EV6 unknown");
734          }
735          break;
736        case 3:
737          switch (~mask)
738          {
739            case BWX | FIX | MVI | PAT | CIX | PMI:
740              cpu->setVersion("EV7x");
741              break;
742            default:
743              cpu->setVersion("EV7 unknown");
744          }
745          break;
746      }
747  
748      currentcpu++;
749    }
750  
751    return true;
752  }
753  
754  
755  #else
756  
757  bool scan_cpuid(hwNode & n)
758  {
759    return true;
760  }
761  #endif                                            /* __alpha__ */
762  #endif                                            /* __i386__ */