/ src / core / cpuinfo.cc
cpuinfo.cc
  1  #include "version.h"
  2  #include "cpuinfo.h"
  3  #include "osutils.h"
  4  #include <sys/types.h>
  5  #include <sys/stat.h>
  6  #include <fcntl.h>
  7  #include <unistd.h>
  8  #include <stdio.h>
  9  #include <stdlib.h>
 10  #include <vector>
 11  
 12  __ID("@(#) $Id$");
 13  
 14  static int currentcpu = 0;
 15  
 16  static inline bool is_system_ppc_ibm(hwNode & node)
 17  {
 18    string desc = node.getDescription();
 19  
 20    return (desc == "PowerNV" || desc == "pSeries Guest" || desc == "pSeries LPAR");
 21  }
 22  
 23  static hwNode *getcpu(hwNode & node,
 24  int n = 0)
 25  {
 26    char cpubusinfo[15];
 27    hwNode *cpu = NULL;
 28  
 29    if (n < 0)
 30      n = 0;
 31  
 32    snprintf(cpubusinfo, sizeof(cpubusinfo), "cpu@%d", n);
 33    cpu = node.findChildByBusInfo(cpubusinfo);
 34  
 35    if (cpu)
 36    {
 37      cpu->addHint("icon", string("cpu"));
 38      cpu->claim(true);                             // claim the cpu and all its children
 39      if (!is_system_ppc_ibm(node))
 40        cpu->enable();                                // enable it
 41  
 42      return cpu;
 43    }
 44  
 45    /*
 46     * device-tree.cc creates all CPU nodes on Power Systems.
 47     * Hence do not create new CPU nodes here.
 48     */
 49    if (is_system_ppc_ibm(node))
 50      return NULL;
 51  
 52    hwNode *core = node.getChild("core");
 53  
 54    if (core)
 55    {
 56      hwNode newcpu("cpu", hw::processor);
 57  
 58      newcpu.setBusInfo(cpubusinfo);
 59      newcpu.claim();
 60      return core->addChild(newcpu);
 61    }
 62    else
 63      return NULL;
 64  }
 65  
 66  
 67  static void cpuinfo_ppc_ibm(hwNode & node,
 68  			    const string & description, const string & version)
 69  {
 70    hwNode *cpu = getcpu(node, currentcpu);
 71  
 72    while (cpu)
 73    {
 74      cpu->setDescription(description);
 75      cpu->setVersion(version);
 76  
 77      cpu = getcpu(node, ++currentcpu);
 78    }
 79  }
 80  
 81  static void cpuinfo_ppc(hwNode & node,
 82  string id,
 83  string value)
 84  {
 85    if (id == "processor")
 86      currentcpu++;
 87  
 88    hwNode *cpu = getcpu(node, currentcpu);
 89  
 90    if (cpu)
 91    {
 92      cpu->addHint("logo", string("powerpc"));
 93      cpu->claim(true);
 94      if (id == "revision")
 95        cpu->setVersion(value);
 96      if (id == "cpu")
 97        cpu->setProduct(value);
 98      if (id == "clock")
 99      {
100        double frequency = 0.0;
101  
102        frequency = atof(value.c_str());
103        cpu->setSize((unsigned long long) (frequency * 1E6));
104      }
105    }
106  }
107  
108  static vector <string> s390x_features;
109  static string s390x_vendor;
110  static void cpuinfo_s390x(hwNode & node,
111  string id,
112  string value)
113  {
114    if (id == "features")
115      {
116        while (value.length() > 0)
117          {
118            size_t pos = value.find(' ');
119            string capability = (pos==string::npos)?value:value.substr(0, pos);
120            s390x_features.push_back(capability);
121            if (pos == string::npos)
122              value = "";
123            else
124              value = hw::strip(value.substr(pos));
125          }
126      }
127  
128    if (id == "vendor_id")
129      s390x_vendor = value;
130  
131    if (matches(id, "^processor"))
132      currentcpu++;
133  
134    hwNode *cpu = getcpu(node, currentcpu);
135    if (cpu)
136      {
137        cpu->addHint("logo", string("s390x"));
138        cpu->claim(true);
139        cpu->setVendor(s390x_vendor);
140  
141        for(size_t i=0; i < s390x_features.size(); i++)
142          cpu->addCapability(s390x_features[i]);
143        /* many thanks to Martin Schwidefsky for communicating the descriptions
144           of the feature flags
145        */
146        cpu->describeCapability("esan3", "ESA new instructions 3 (N3)");
147        cpu->describeCapability("zarch", "x/Architecture (64-bit) mode)");
148        cpu->describeCapability("stfle", "store facility list extended instruction");
149        cpu->describeCapability("msa", "message security assist facility");
150        cpu->describeCapability("ldisp", "long displacement facility");
151        cpu->describeCapability("eimm", "extended immediate facility");
152        cpu->describeCapability("dfp", "decimal floating point facility");
153        cpu->describeCapability("edat", "enhanced DAT facility");
154        cpu->describeCapability("etf3eh", "ETF3 enhancement facility");
155        cpu->describeCapability("highgprs", "support for 64-bit registers for 31-bit programs");
156        cpu->describeCapability("te", "transactional/constraint transactional execution facilities");
157      }
158  }
159  
160  static void cpuinfo_arm(hwNode & node,
161                          string id,
162                          string value)
163  {
164  
165    if (id.substr(0, string("processor").size())=="processor")
166      currentcpu++;
167  
168    hwNode *cpu = getcpu(node, currentcpu);
169    if (cpu)
170      {
171        cpu->addHint("logo", string("arm"));
172        if (id == "model name" && node.getDescription() == "")
173          node.setDescription(value);
174        cpu->claim(true);
175        if (id == "Features")
176          {
177            while (value.length() > 0)
178              {
179                size_t pos = value.find(' ');
180                string capability = (pos==string::npos)?value:value.substr(0, pos);
181                cpu->addCapability(capability);
182                if (pos == string::npos)
183                  value = "";
184                else
185                  value = hw::strip(value.substr(pos));
186              }
187          }
188        /* With help from:
189           http://infocenter.arm.com/help/index.jsp
190           http://unix.stackexchange.com/questions/43539/what-do-the-flags-in-proc-cpuinfo-mean
191           http://en.wikipedia.org/wiki/ARM_architecture
192        */
193        cpu->describeCapability("swp", "Swap instruction");
194        cpu->describeCapability("swpb", "Swap Byte instruction");
195        cpu->describeCapability("half", "Unknown");
196        cpu->describeCapability("thumb", "Thumb instruction set");
197        cpu->describeCapability("26bit", "26-bit Model");
198        cpu->describeCapability("fastmult", "Fast Multiplication");
199        cpu->describeCapability("fpa", "Floating point accelerator");
200        cpu->describeCapability("vfp", "VFP (vector floating point instructions)");
201        cpu->describeCapability("vfpv3", "VFP version 3");
202        cpu->describeCapability("vfpv3d16", "VFP version 3 with 16 64-bit FPU registers");
203        cpu->describeCapability("vfpv4", "VFP version 4");
204        cpu->describeCapability("vfpd32", "Unknown");
205        cpu->describeCapability("edsp", "DSP extensions");
206        cpu->describeCapability("java", "Jazelle (Java bytecode accelerator)");
207        cpu->describeCapability("thumbee", "Thumb Execution Environment");
208        cpu->describeCapability("neon", "NEON aka MPE - Media Processing Engine");
209        cpu->describeCapability("tls", "TLS register");
210        cpu->describeCapability("iwmmxt", "SIMD instructions");
211        cpu->describeCapability("crunch", "MaverickCrunch coprocessor");
212        cpu->describeCapability("idiva", "SDIV and UDIV hardware division in ARM mode");
213        cpu->describeCapability("idivt", "SDIV and UDIV hardware division in Thumb mode");
214        cpu->describeCapability("lpae", "Large Physical Address Extension architecture");
215        cpu->describeCapability("evtstrm", "Unknown");
216      }
217  }
218  
219  static vector <string> aarch64_features;
220  static string aarch64_processor_name;
221  static void cpuinfo_aarch64(hwNode & node,
222                          string id,
223                          string value)
224  {
225  
226    /*
227     * If we already have CPU info extracted from SMBIOS, ignore /proc/cpuinfo 
228     * entirely. This is because the kernel's /proc/cpuinfo output on aarch64 
229     * does not distinguish between cores and physical processors (every core is 
230     * treated as a separate processor) so we may end up creating too many CPU 
231     * nodes.
232     */
233    if (getcpu(node)->getHandle().compare(0, 4, "DMI:") == 0)
234      return;
235  
236    if (id.substr(0, string("processor").size())=="processor")
237      currentcpu++;
238  
239    if (id.substr(0, string("Processor").size())=="Processor")
240      aarch64_processor_name = value;
241  
242    if (id == "Features")
243      {
244        hwNode *cpu = getcpu(node, currentcpu);
245        if (cpu)
246          {
247            cpu->addHint("logo", string("aarch64"));
248            if (node.getDescription() == "")
249              node.setDescription(aarch64_processor_name);
250            cpu->claim(true);
251  
252            while (value.length() > 0)
253              {
254                size_t pos = value.find(' ');
255                string capability = (pos==string::npos)?value:value.substr(0, pos);
256                aarch64_features.push_back(capability);
257                if (pos == string::npos)
258                  value = "";
259                else
260                  value = hw::strip(value.substr(pos));
261              }
262  
263            for(size_t i=0; i < aarch64_features.size(); i++)
264              {
265                cpu->addCapability(aarch64_features[i]);
266                cpu->describeCapability("fp", "Floating point instructions");
267                cpu->describeCapability("asimd", "Advanced SIMD");
268                cpu->describeCapability("evtstrm", "Event stream");
269                cpu->describeCapability("aes", "AES instructions");
270                cpu->describeCapability("pmull", "PMULL instruction");
271                cpu->describeCapability("sha1", "SHA1 instructions");
272                cpu->describeCapability("sha2", "SHA2 instructions");
273                cpu->describeCapability("crc32", "CRC extension");
274              }
275          }
276      }
277  }
278  
279  static void cpuinfo_ia64(hwNode & node,
280  string id,
281  string value)
282  {
283  
284    if (id == "processor")
285      currentcpu++;
286  
287    hwNode *cpu = getcpu(node, currentcpu);
288  
289    if (cpu)
290    {
291      cpu->claim(true);
292  
293      if (id == "cpu number")
294      {
295        int physicalcpu = 0;
296  
297        physicalcpu = atoi(value.c_str());
298  
299        if (physicalcpu != currentcpu)
300        {
301          cpu->addCapability("emulated");
302          cpu->addCapability("hyperthreading");
303        }
304      }
305  
306      if (id == "vendor")
307      {
308        if (value == "GenuineIntel")
309          value = "Intel Corp.";
310        cpu->setVendor(value);
311      }
312  
313      if (id == "revision")
314        cpu->setVersion(value);
315  
316      if (id == "family")
317        cpu->setProduct(value);
318  
319      if (id == "cpu MHz" && cpu->getSize() == 0)
320      {
321        double frequency = 0.0;
322  
323        frequency = atof(value.c_str());
324        cpu->setSize((unsigned long long) (frequency * 1E6));
325      }
326    }
327  }
328  
329  static void cpuinfo_hppa(hwNode & node,
330  string id,
331  string value)
332  {
333    if (id == "processor")
334      currentcpu++;
335  
336    hwNode *cpu = getcpu(node, currentcpu);
337  
338    if (id == "model" && node.getProduct() == "")
339      node.setProduct(value);
340    if (id == "model name" && node.getDescription() == "")
341      node.setDescription(value);
342    if (id == "software id" && node.getSerial() == "")
343      node.setSerial(value);
344  
345    if (cpu)
346    {
347      cpu->claim(true);
348  
349      if (id == "cpu family" && cpu->getVersion() == "")
350        cpu->setVersion(value);
351      if (id == "cpu" && cpu->getProduct() == "")
352        cpu->setProduct(value);
353      if (id == "cpu MHz" && cpu->getSize() == 0)
354      {
355        double frequency = 0.0;
356  
357        frequency = atof(value.c_str());
358        cpu->setSize((unsigned long long) (frequency * 1E6));
359      }
360    }
361  }
362  
363  static void cpuinfo_alpha(hwNode & node,
364  string id,
365  string value)
366  {
367    static int cpusdetected = 0;
368    static int cpusactive = 0;
369    unsigned long long frequency = 0;
370    int i;
371  
372    hwNode *cpu = getcpu(node, 0);
373  
374    if (id == "platform string" && node.getProduct() == "")
375      node.setProduct(value);
376    if (id == "system serial number" && node.getSerial() == "")
377      node.setSerial(value);
378    if (id == "system type")
379      node.setVersion(node.getVersion() + " " + value);
380    if (id == "system variation")
381      node.setVersion(node.getVersion() + " " + value);
382    if (id == "system revision")
383      node.setVersion(node.getVersion() + " " + value);
384  
385    if (id == "cpus detected")
386      cpusdetected = atoi(value.c_str());
387    if (id == "cpus active")
388      cpusactive = atoi(value.c_str());
389    if (id == "cycle frequency [Hz]")
390      frequency = atoll(value.c_str());
391  
392    if (cpu)
393    {
394      cpu->claim(true);
395  
396      if (frequency)
397        cpu->setSize(frequency);
398    }
399  
400    for (i = 1; i < cpusdetected; i++)
401    {
402      hwNode *mycpu = getcpu(node, i);
403  
404      if (mycpu)
405      {
406        mycpu->disable();
407  
408        if (cpu)
409          mycpu->setSize(cpu->getSize());
410      }
411    }
412    for (i = 1; i < cpusactive; i++)
413    {
414      hwNode *mycpu = getcpu(node, i);
415  
416      if (mycpu)
417        mycpu->enable();
418    }
419  }
420  
421  static void cpuinfo_x86(hwNode & node,
422  string id,
423  string value)
424  {
425    static int siblings = -1;
426  
427    if(currentcpu < 0) siblings = -1;
428  
429    if ((siblings<0) && (id == "siblings"))
430    {
431      siblings = atoi(value.c_str());
432      siblings--;
433    }
434  
435    if (id == "processor")
436    {
437      siblings--;
438  
439      if(siblings >= 0)
440        return;
441      else
442        currentcpu++;
443    }
444  
445    hwNode *cpu = getcpu(node, currentcpu);
446  
447    if (cpu)
448    {
449      hw::value family, model, stepping;
450  
451  // x86 CPUs are assumed to be 32 bits per default
452      if(cpu->getWidth()==0) cpu->setWidth(32);
453  
454      cpu->claim(true);
455      if (id == "vendor_id")
456      {
457        if (value == "AuthenticAMD")
458          value = "Advanced Micro Devices [AMD]";
459        if (value == "HygonGenuine")
460          value = "Hygon";
461        if (value == "GenuineIntel")
462          value = "Intel Corp.";
463        cpu->setVendor(value);
464      }
465      if (id == "model name")
466        cpu->setProduct(value);
467      if (id == "microcode")
468        cpu->setConfig(id, stoll(value, NULL, 0));
469      if (id == "cpu family")
470        cpu->addHint(id, stoll(value, NULL, 0));
471      if (id == "model")
472        cpu->addHint(id, stoll(value, NULL, 0));
473      if (id == "stepping")
474        cpu->addHint(id, stoll(value, NULL, 0));
475  
476      family = cpu->getHint("cpu family");
477      model = cpu->getHint("model");
478      stepping = cpu->getHint("stepping");
479      if(family.defined() && model.defined() && stepping.defined())
480              cpu->setVersion(tostring(family.asInteger())+"."+tostring(model.asInteger())+"."+tostring(stepping.asInteger()));
481  
482  //if ((id == "cpu MHz") && (cpu->getSize() == 0))
483  //{
484  //cpu->setSize((long long) (1000000L * atof(value.c_str())));
485  //}
486      if (id == "Physical processor ID")
487        cpu->setSerial(value);
488      if ((id == "fdiv_bug") && (value == "yes"))
489        cpu->addCapability("fdiv_bug");
490      if ((id == "hlt_bug") && (value == "yes"))
491        cpu->addCapability("hlt_bug");
492      if ((id == "f00f_bug") && (value == "yes"))
493        cpu->addCapability("f00f_bug");
494      if ((id == "coma_bug") && (value == "yes"))
495        cpu->addCapability("coma_bug");
496      if ((id == "fpu") && (value == "yes"))
497        cpu->addCapability("fpu");
498      if ((id == "wp") && (value == "yes"))
499        cpu->addCapability("wp");
500      if ((id == "fpu_exception") && (value == "yes"))
501        cpu->addCapability("fpu_exception", "FPU exceptions reporting");
502      if (id == "flags")
503        while (value.length() > 0)
504      {
505        size_t pos = value.find(' ');
506        string capability = (pos==string::npos)?value:value.substr(0, pos);
507  
508        if(capability == "lm") capability = "x86-64";
509  
510        cpu->addCapability(capability);
511  
512        if (pos == string::npos)
513          value = "";
514        else
515          value = hw::strip(value.substr(pos));
516      }
517  
518      cpu->describeCapability("fpu", "mathematical co-processor");
519      cpu->describeCapability("vme", "virtual mode extensions");
520      cpu->describeCapability("de", "debugging extensions");
521      cpu->describeCapability("pse", "page size extensions");
522      cpu->describeCapability("tsc", "time stamp counter");
523      cpu->describeCapability("msr", "model-specific registers");
524      cpu->describeCapability("mce", "machine check exceptions");
525      cpu->describeCapability("cx8", "compare and exchange 8-byte");
526      cpu->describeCapability("apic", "on-chip advanced programmable interrupt controller (APIC)");
527      cpu->describeCapability("sep", "fast system calls");
528      cpu->describeCapability("mtrr", "memory type range registers");
529      cpu->describeCapability("pge", "page global enable");
530      cpu->describeCapability("mca", "machine check architecture");
531      cpu->describeCapability("cmov", "conditional move instruction");
532      cpu->describeCapability("pat", "page attribute table");
533      cpu->describeCapability("pse36", "36-bit page size extensions");
534      cpu->describeCapability("pn", "processor serial number");
535      cpu->describeCapability("psn", "processor serial number");
536      cpu->describeCapability("clflush", "CLFLUSH instruction");
537      cpu->describeCapability("dts", "debug trace and EMON store MSRs");
538      cpu->describeCapability("acpi", "thermal control (ACPI)");
539      cpu->describeCapability("fxsr", "fast floating point save/restore");
540      cpu->describeCapability("sse", "streaming SIMD extensions (SSE)");
541      cpu->describeCapability("sse2", "streaming SIMD extensions (SSE2)");
542      cpu->describeCapability("ss", "self-snoop");
543      cpu->describeCapability("tm", "thermal interrupt and status");
544      cpu->describeCapability("ia64", "IA-64 (64-bit Intel CPU)");
545      cpu->describeCapability("pbe", "pending break event");
546      cpu->describeCapability("syscall", "fast system calls");
547      cpu->describeCapability("mp", "multi-processor capable");
548      cpu->describeCapability("nx", "no-execute bit (NX)");
549      cpu->describeCapability("mmxext", "multimedia extensions (MMXExt)");
550      cpu->describeCapability("3dnowext", "multimedia extensions (3DNow!Ext)");
551      cpu->describeCapability("3dnow", "multimedia extensions (3DNow!)");
552      cpu->describeCapability("recovery", "CPU in recovery mode");
553      cpu->describeCapability("longrun", "LongRun Dynamic Power/Thermal Management");
554      cpu->describeCapability("lrti", "LongRun Table Interface");
555      cpu->describeCapability("cxmmx", "multimedia extensions (Cyrix MMX)");
556      cpu->describeCapability("k6_mtrr", "AMD K6 MTRRs");
557      cpu->describeCapability("cyrix_arr", "Cyrix ARRs (= MTRRs)");
558      cpu->describeCapability("centaur_mcr", "Centaur MCRs (= MTRRs)");
559      cpu->describeCapability("pni", "SSE-3");
560      cpu->describeCapability("monitor", "MONITOR/MWAIT support");
561  //cpu->describeCapability("ds_cpl", "");
562  //cpu->describeCapability("est", "");
563  //cpu->describeCapability("tm2", "");
564  //cpu->describeCapability("cid", "");
565  //cpu->describeCapability("xtpr", "");
566      cpu->describeCapability("rng", "random number generator");
567      cpu->describeCapability("rng_en", "random number generator (enhanced)");
568      cpu->describeCapability("ace", "advanced cryptography engine");
569      cpu->describeCapability("ace_en", "advanced cryptography engine (enhanced)");
570      cpu->describeCapability("ht", "HyperThreading");
571      cpu->describeCapability("lm", "64bits extensions (x86-64)");
572      cpu->describeCapability("x86-64", "64bits extensions (x86-64)");
573      cpu->describeCapability("mmx", "multimedia extensions (MMX)");
574      cpu->describeCapability("pae", "4GB+ memory addressing (Physical Address Extension)");
575  
576      if(cpu->isCapable("ia64") || cpu->isCapable("lm") || cpu->isCapable("x86-64"))
577        cpu->setWidth(64);
578  
579      if(node.getWidth()==0) node.setWidth(cpu->getWidth());
580    }
581  }
582  
583  bool scan_cpuinfo(hwNode & n)
584  {
585    hwNode *core = n.getChild("core");
586    int cpuinfo = open("/proc/cpuinfo", O_RDONLY);
587  
588    if (cpuinfo < 0)
589      return false;
590  
591    if (!core)
592    {
593      n.addChild(hwNode("core", hw::bus));
594      core = n.getChild("core");
595    }
596  
597    if (core)
598    {
599      char buffer[1024];
600      ssize_t count;
601      string cpuinfo_str = "";
602      string description = "", version = "";
603      string plat = platform();
604  
605      while ((count = read(cpuinfo, buffer, sizeof(buffer))) > 0)
606      {
607        cpuinfo_str += string(buffer, count);
608      }
609      close(cpuinfo);
610  
611      vector < string > cpuinfo_lines;
612      splitlines(cpuinfo_str, cpuinfo_lines);
613      cpuinfo_str = "";                             // free memory
614      currentcpu = -1;
615  
616      for (unsigned int i = 0; i < cpuinfo_lines.size(); i++)
617      {
618        string id = "";
619        string value = "";
620        size_t pos = 0;
621  
622        pos = cpuinfo_lines[i].find(':');
623  
624        if (pos != string::npos)
625        {
626          id = hw::strip(cpuinfo_lines[i].substr(0, pos));
627          value = hw::strip(cpuinfo_lines[i].substr(pos + 1));
628  
629          if (plat == "ppc" || plat == "ppc64" || plat == "ppc64le")
630          {
631            // All cores have same product name and version on power systems
632            if (is_system_ppc_ibm(n))
633              {
634                if (id == "cpu")
635                  description = value;
636                if (id == "revision")
637                  version = value;
638  
639                if (description != "" && version != "")
640                {
641                  cpuinfo_ppc_ibm(n, description, version);
642                  break;
643                }
644              }
645            else
646              cpuinfo_ppc(n, id, value);
647          }
648          else if (plat == "hppa")
649          {
650            cpuinfo_hppa(n, id, value);
651          }
652          else if (plat == "alpha")
653          {
654            cpuinfo_alpha(n, id, value);
655          }
656          else if (plat == "ia64")
657          {
658            cpuinfo_ia64(n, id, value);
659          }
660          else if (plat == "s390" || plat == "s390x")
661          {
662            cpuinfo_s390x(n, id, value);
663          }
664          else if (plat.compare(0, 3, "arm") == 0)
665          {
666            cpuinfo_arm(n, id, value);
667          }
668          else if (plat == "aarch64")
669          {
670            cpuinfo_aarch64(n, id, value);
671          }
672          else
673          {
674            cpuinfo_x86(n, id, value);
675          }
676        }
677      }
678    }
679    else
680    {
681      close(cpuinfo);
682      return false;
683    }
684  
685    hwNode *cpu = getcpu(n, 0);
686    if(cpu && (n.getWidth()==0))
687      n.setWidth(cpu->getWidth());
688  
689    return true;
690  }