From a5fb8431d9888cbd4b40e6435d4379bf0c86cdec Mon Sep 17 00:00:00 2001
From: Lyonel Vincent <lyonel@ezix.org>
Date: Thu, 20 Oct 2016 00:27:58 +0200
Subject: [PATCH] merge github pull request 23: parse CPU information

FIXME: `dimminfo` magic needs cleansing, ugly code getting uglier
---
 src/core/device-tree.cc | 267 ++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 237 insertions(+), 30 deletions(-)

diff --git a/src/core/device-tree.cc b/src/core/device-tree.cc
index c2b7d15..700dff0 100644
--- a/src/core/device-tree.cc
+++ b/src/core/device-tree.cc
@@ -26,6 +26,8 @@
 #include <string.h>
 #include <unistd.h>
 #include <dirent.h>
+#include <utility>
+#include <map>
 
 __ID("@(#) $Id$");
 
@@ -227,6 +229,42 @@ static string cpubusinfo(int cpu)
 }
 
 
+static void set_cpu(hwNode & cpu, int currentcpu, const string & basepath)
+{
+  cpu.setProduct(get_string(basepath + "/name"));
+  cpu.claim();
+  cpu.setBusInfo(cpubusinfo(currentcpu));
+
+  cpu.setSize(get_u32(basepath + "/clock-frequency"));
+  cpu.setClock(get_u32(basepath + "/bus-frequency"));
+
+  if (exists(basepath + "/altivec"))
+    cpu.addCapability("altivec");
+
+  if (exists(basepath + "/performance-monitor"))
+    cpu.addCapability("performance-monitor");
+}
+
+
+static void fill_cache_info(string cache_type, string cachebase,
+			    hwNode & cache, hwNode & icache)
+{
+  cache.claim();
+  cache.setDescription(cache_type);
+  cache.setSize(get_u32(cachebase + "/d-cache-size"));
+
+  if (exists(cachebase + "/cache-unified"))
+    cache.setDescription(cache.getDescription() + " (unified)");
+  else
+  {
+    icache = cache;
+    cache.setDescription(cache.getDescription() + " (data)");
+    icache.setDescription(icache.getDescription() + " (instruction)");
+    icache.setSize(get_u32(cachebase + "/i-cache-size"));
+  }
+}
+
+
 static void scan_devtree_cpu(hwNode & core)
 {
   struct dirent **namelist;
@@ -254,14 +292,8 @@ static void scan_devtree_cpu(hwNode & core)
         hw::strip(get_string(basepath + "/device_type")) != "cpu")
         break;                                    // oops, not a CPU!
 
-      cpu.setProduct(get_string(basepath + "/name"));
       cpu.setDescription("CPU");
-      cpu.claim();
-      cpu.setBusInfo(cpubusinfo(currentcpu++));
-      cpu.setSize(get_u32(basepath + "/clock-frequency"));
-      cpu.setClock(get_u32(basepath + "/bus-frequency"));
-      if (exists(basepath + "/altivec"))
-        cpu.addCapability("altivec");
+      set_cpu(cpu, currentcpu++, basepath);
 
       version = get_u32(basepath + "/cpu-version");
       if (version != 0)
@@ -273,14 +305,11 @@ static void scan_devtree_cpu(hwNode & core)
         snprintf(buffer, sizeof(buffer), "%lx.%d.%d",
           (version & 0xffff0000) >> 16, major, minor);
         cpu.setVersion(buffer);
-
       }
+
       if (hw::strip(get_string(basepath + "/state")) != "running")
         cpu.disable();
 
-      if (exists(basepath + "/performance-monitor"))
-        cpu.addCapability("performance-monitor");
-
       if (exists(basepath + "/d-cache-size"))
       {
         hwNode cache("cache",
@@ -302,29 +331,19 @@ static void scan_devtree_cpu(hwNode & core)
         {
           hwNode cache("cache",
             hw::memory);
+          hwNode icache("cache",
+            hw::memory);
           string cachebase = basepath + "/" + cachelist[j]->d_name;
 
           if (hw::strip(get_string(cachebase + "/device_type")) != "cache" &&
             hw::strip(get_string(cachebase + "/device_type")) != "l2-cache")
             break;                                // oops, not a cache!
 
-          cache.claim();
-          cache.setDescription("L2 Cache");
-          cache.setSize(get_u32(cachebase + "/d-cache-size"));
-          cache.setClock(get_u32(cachebase + "/clock-frequency"));
+	  cache.setClock(get_u32(cachebase + "/clock-frequency"));
+	  fill_cache_info("L2 Cache", cachebase, cache, icache);
 
-          if (exists(cachebase + "/cache-unified"))
-            cache.setDescription(cache.getDescription() + " (unified)");
-          else
-          {
-            hwNode icache = cache;
-            cache.setDescription(cache.getDescription() + " (data)");
-            icache.setDescription(icache.getDescription() + " (instruction)");
-            icache.setSize(get_u32(cachebase + "/i-cache-size"));
-
-            if (icache.getSize() > 0)
-              cpu.addChild(icache);
-          }
+          if (icache.getSize() > 0)
+            cpu.addChild(icache);
 
           if (cache.getSize() > 0)
             cpu.addChild(cache);
@@ -342,6 +361,191 @@ static void scan_devtree_cpu(hwNode & core)
   }
 }
 
+static void set_cpu_config_threads(hwNode & cpu, const string & basepath)
+{
+  static int threads_per_cpu = 0;
+
+  /* In power systems, there are equal no. of threads per cpu-core */
+  if (threads_per_cpu == 0)
+  {
+    int rc;
+    struct stat sbuf;
+    string p = hw::strip(basepath + string("/ibm,ppc-interrupt-server#s"));
+
+    /*
+     * This file contains as many 32 bit interrupt server numbers, as the
+     * number of threads per CPU (in hexadecimal format). st_size gives size
+     * in bytes of a file. Hence, grouping by 4 bytes, we get the thread
+     * count.
+     */
+    rc = stat(p.c_str(), &sbuf);
+    if (!rc)
+      threads_per_cpu = sbuf.st_size / 4;
+  }
+
+  cpu.setConfig("threads", threads_per_cpu);
+}
+
+
+static void scan_devtree_cpu_power(hwNode & core)
+{
+  int n;
+  int currentcpu = 0;
+  struct dirent **namelist;
+  map <uint32_t, pair<uint32_t, vector <hwNode> > > l2_caches;
+  map <uint32_t, vector <hwNode> > l3_caches;
+
+  pushd(DEVICETREE "/cpus");
+  n = scandir(".", &namelist, selectdir, alphasort);
+  popd();
+  if (n < 0)
+    return;
+
+  /*
+   * 'cpus' node contains CPU, L2 and L3 cache nodes. L1 cache information is
+   * available under CPU node itself. l2-cache (or next-level-cache) property
+   * contains next level cache node phandle/ibm,phanle number.
+   * First pass creates cache nodes and second pass will link cache nodes to
+   * corresponding CPU nodes.
+   */
+  for (int i = 0; i < n; i++)
+  {
+    string product;
+    string basepath = string(DEVICETREE "/cpus/") + string(namelist[i]->d_name);
+    hwNode cache("cache", hw::memory);
+    hwNode icache("cache", hw::memory);
+    vector <hwNode> value;
+
+    if (!exists(basepath + "/device_type"))
+      continue;
+
+    if (hw::strip(get_string(basepath + "/device_type")) != "cache")
+      continue;
+
+    product = hw::strip(get_string(basepath + "/name"));
+
+    if (hw::strip(get_string(basepath + "/status")) != "okay")
+      cache.disable();
+
+    if (product == "l2-cache")
+      fill_cache_info("L2 Cache", basepath, cache, icache);
+    else
+      fill_cache_info("L3 Cache", basepath, cache, icache);
+
+    if (icache.getSize() > 0)
+      value.insert(value.begin(), icache);
+
+    if (cache.getSize() > 0)
+      value.insert(value.begin(), cache);
+
+    if (value.size() > 0)
+    {
+      uint32_t phandle = 0;
+
+      if (exists(basepath + "/phandle"))
+        phandle = get_u32(basepath + "/phandle");
+      else if (exists(basepath + "/ibm,phandle")) // on pSeries LPARs
+        phandle = get_u32(basepath + "/ibm,phandle");
+
+      if (!phandle)
+        continue;
+
+      if (product == "l2-cache")
+      {
+        uint32_t l3_key = 0; // 0 indicating no next level of cache
+
+        if (exists(basepath + "/l2-cache"))
+          l3_key = get_u32(basepath + "/l2-cache");
+        else if (exists(basepath + "/next-level-cache")) //on OpenPOWER systems
+          l3_key = get_u32(basepath + "/next-level-cache");
+
+        pair <uint32_t, vector <hwNode> > p (l3_key, value);
+        l2_caches[phandle] = p;
+      }
+      else if (product == "l3-cache")
+      {
+        l3_caches[phandle] = value;
+      }
+    }
+  } // first pass end
+
+  for (int i = 0; i < n; i++) //second and final pass
+  {
+    uint32_t l2_key = 0;
+    uint32_t version = 0;
+    string basepath = string(DEVICETREE "/cpus/") + string(namelist[i]->d_name);
+    hwNode cpu("cpu", hw::processor);
+
+    if (!exists(basepath + "/device_type"))
+    {
+      free(namelist[i]);
+      continue;
+    }
+
+    if (hw::strip(get_string(basepath + "/device_type")) != "cpu")
+    {
+      free(namelist[i]);
+      continue;
+    }
+
+    cpu.setDescription("CPU");
+    set_cpu(cpu, currentcpu++, basepath);
+
+    version = get_u32(basepath + "/cpu-version");
+    if (version != 0)
+      cpu.setVersion(tostring(version));
+
+    if (hw::strip(get_string(basepath + "/status")) != "okay")
+      cpu.disable();
+
+    set_cpu_config_threads(cpu, basepath);
+
+    if (exists(basepath + "/d-cache-size"))
+    {
+      hwNode cache("cache", hw::memory);
+      hwNode icache("cache", hw::memory);
+
+      fill_cache_info("L1 Cache", basepath, cache, icache);
+
+      if (icache.getSize() > 0)
+        cpu.addChild(icache);
+
+      if (cache.getSize() > 0)
+        cpu.addChild(cache);
+    }
+
+    if (exists(basepath + "/l2-cache"))
+        l2_key = get_u32(basepath + "/l2-cache");
+    else if (exists(basepath + "/next-level-cache"))
+        l2_key = get_u32(basepath + "/next-level-cache");
+
+    if (l2_key != 0)
+    {
+      map <uint32_t, pair <uint32_t, vector <hwNode> > >::
+        const_iterator got = l2_caches.find(l2_key);
+
+      if (!(got == l2_caches.end()))
+        for (uint32_t j = 0; j < (got->second).second.size(); j++)
+          cpu.addChild((got->second).second[j]);
+
+      if ((got->second).first != 0) // we have another level of cache
+      {
+        map <uint32_t, vector <hwNode> >::const_iterator got_l3 =
+          l3_caches.find ((got->second).first);
+
+        if (!(got_l3 == l3_caches.end()))
+          for (uint32_t j = 0; j < (got_l3->second).size(); j++)
+            cpu.addChild((got_l3->second)[j]);
+      }
+    }
+
+    core.addChild(cpu);
+
+    free(namelist[i]);
+  }
+  free(namelist);
+}
+
 void add_memory_bank(string name, string path, hwNode & core)
 {
   struct dirent **dirlist;
@@ -728,7 +932,7 @@ bool scan_device_tree(hwNode & n)
       core->addHint("icon", string("board"));
       scan_devtree_root(*core);
       scan_devtree_memory_powernv(*core);
-      scan_devtree_cpu(*core);
+      scan_devtree_cpu_power(*core);
       n.addCapability("powernv", "Non-virtualized");
       n.addCapability("opal", "OPAL firmware");
     }
@@ -764,7 +968,7 @@ bool scan_device_tree(hwNode & n)
     {
       core->addHint("icon", string("board"));
       scan_devtree_root(*core);
-      scan_devtree_cpu(*core);
+      scan_devtree_cpu_power(*core);
       core->addCapability("qemu", "QEMU virtualization");
       core->addCapability("guest", "Virtualization guest");
     }
@@ -779,7 +983,10 @@ bool scan_device_tree(hwNode & n)
       scan_devtree_root(*core);
       scan_devtree_bootrom(*core);
       scan_devtree_memory(*core);
-      scan_devtree_cpu(*core);
+      if (exists(DEVICETREE "/ibm,lpar-capable"))
+        scan_devtree_cpu_power(*core);
+      else
+        scan_devtree_cpu(*core);
     }
   }
 
