/ src / core / device-tree.cc
device-tree.cc
   1  /*
   2   * device-tree.cc
   3   *
   4   * This module parses the OpenFirmware device tree (published under /proc
   5   * by the kernel).
   6   *
   7   *
   8   *
   9   *
  10   */
  11  
  12  #include <algorithm>
  13  #include <errno.h>
  14  #include "version.h"
  15  #include "device-tree.h"
  16  #include "osutils.h"
  17  #include "jedec.h"
  18  #include <sys/types.h>
  19  #include <sys/stat.h>
  20  #include <arpa/inet.h>
  21  #include <fcntl.h>
  22  #include <stdlib.h>
  23  #include <stdint.h>
  24  #include <stdio.h>
  25  #include <stdint.h>
  26  #include <arpa/inet.h>
  27  #include <string.h>
  28  #include <unistd.h>
  29  #include <dirent.h>
  30  #include <utility>
  31  #include <map>
  32  
  33  __ID("@(#) $Id$");
  34  
  35  #define DIMMINFOSIZE 0x200
  36  typedef uint8_t dimminfo_buf[DIMMINFOSIZE];
  37  
  38  struct dimminfo
  39  {
  40    uint8_t version3;
  41    char serial[16];
  42    uint16_t version1, version2;
  43  };
  44  
  45  #define DEVICETREE "/proc/device-tree"
  46  #define DEVICETREEVPD  "/proc/device-tree/vpd/"
  47  
  48  /*
  49   * Integer properties in device tree are usually represented as a single 
  50   * "u32 cell" which is an unsigned 32-bit big-endian integer.
  51   */
  52  static uint32_t get_u32(const string & path)
  53  {
  54    uint32_t result = 0;
  55    int fd = open(path.c_str(), O_RDONLY);
  56  
  57    if (fd >= 0)
  58    {
  59      if(read(fd, &result, sizeof(result)) != sizeof(result))
  60        result = 0;
  61  
  62      close(fd);
  63    }
  64  
  65    return ntohl(result);
  66  }
  67  
  68  static uint64_t read_int(FILE *f, size_t length = 4)
  69  {
  70    vector < uint8_t > bytes(length);
  71    uint64_t result = 0;
  72    if (fread(bytes.data(), length, 1, f) != 1)
  73      return 0;
  74    for (size_t i = 0; i < length; i ++)
  75    {
  76      result += ((uint64_t) bytes[length - i - 1]) << (i * 8);
  77    }
  78    return result;
  79  }
  80  
  81  /*
  82   * The reg property is an array of (address, size) pairs. The length of the 
  83   * address and size are determined by the #address-cells and #size-cells 
  84   * properties on the parent node, measured in number of "cells" which are 
  85   * 32 bits wide. The address and size are in big-endian byte order.
  86   */
  87  struct reg_entry {
  88    uint64_t address;
  89    uint64_t size;
  90  };
  91  static vector < reg_entry > get_reg_property(const string & node)
  92  {
  93    vector < reg_entry > result;
  94  
  95    uint32_t num_address_cells = 1;
  96    uint32_t num_size_cells = 1;
  97  
  98    if (exists(node + "/../#address-cells"))
  99      num_address_cells = get_u32(node + "/../#address-cells");
 100    if (exists(node + "/../#size-cells"))
 101      num_size_cells = get_u32(node + "/../#size-cells");
 102  
 103    if (num_address_cells > 2 || num_size_cells > 2)
 104      return result;
 105  
 106    FILE *f = fopen((node + "/reg").c_str(), "r");
 107    if (f == NULL)
 108      return result;
 109  
 110    while (true)
 111    {
 112      reg_entry entry = {0};
 113      entry.address = read_int(f, num_address_cells * 4);
 114      if (feof(f))
 115        break;
 116      entry.size = read_int(f, num_size_cells * 4);
 117      if (feof(f))
 118        break;
 119      result.push_back(entry);
 120    }
 121  
 122    fclose(f);
 123    return result;
 124  }
 125  
 126  static vector < string > get_strings(const string & path,
 127  unsigned int offset = 0)
 128  {
 129    vector < string > result;
 130    char *strings = NULL;
 131    char *curstring = NULL;
 132  
 133    int fd = open(path.c_str(), O_RDONLY);
 134  
 135    if (fd >= 0)
 136    {
 137      struct stat buf;
 138  
 139      if (fstat(fd, &buf) == 0)
 140      {
 141        strings = (char *) malloc(buf.st_size + 1);
 142        if (strings)
 143        {
 144          memset(strings, 0, buf.st_size + 1);
 145          if(read(fd, strings, buf.st_size) == buf.st_size)
 146          {
 147            curstring = strings + offset;
 148  
 149            while (strlen(curstring))
 150            {
 151              result.push_back(string(curstring));
 152              curstring += strlen(curstring) + 1;
 153            }
 154          }
 155  
 156          free(strings);
 157        }
 158      }
 159  
 160      close(fd);
 161    }
 162  
 163    return result;
 164  }
 165  
 166  
 167  static void scan_devtree_root(hwNode & core)
 168  {
 169    core.setClock(get_u32(DEVICETREE "/clock-frequency"));
 170  }
 171  
 172  
 173  static void scan_devtree_bootrom(hwNode & core)
 174  {
 175    if (exists(DEVICETREE "/rom/boot-rom"))
 176    {
 177      hwNode bootrom("firmware",
 178        hw::memory);
 179      string upgrade = "";
 180  
 181      bootrom.setProduct(get_string(DEVICETREE "/rom/boot-rom/model"));
 182      bootrom.setDescription("BootROM");
 183      bootrom.
 184        setVersion(get_string(DEVICETREE "/rom/boot-rom/BootROM-version"));
 185  
 186      if ((upgrade =
 187        get_string(DEVICETREE "/rom/boot-rom/write-characteristic")) != "")
 188      {
 189        bootrom.addCapability("upgrade");
 190        bootrom.addCapability(upgrade);
 191      }
 192  
 193      vector < reg_entry > regs = get_reg_property(DEVICETREE "/rom/boot-rom");
 194      if (!regs.empty())
 195      {
 196        bootrom.setPhysId(regs[0].address);
 197        bootrom.setSize(regs[0].size);
 198      }
 199  
 200      bootrom.claim();
 201  //bootrom.setLogicalName(DEVICETREE "/rom");
 202      core.addChild(bootrom);
 203    }
 204  
 205    if (exists(DEVICETREE "/openprom"))
 206    {
 207      hwNode openprom("firmware",
 208        hw::memory);
 209  
 210      if (exists(DEVICETREE "/openprom/ibm,vendor-model"))
 211  	    openprom.setProduct(get_string(DEVICETREE "/openprom/ibm,vendor-model"));
 212      else
 213  	    openprom.setProduct(get_string(DEVICETREE "/openprom/model"));
 214  
 215      if (exists(DEVICETREE "/openprom/supports-bootinfo"))
 216        openprom.addCapability("bootinfo");
 217  
 218  //openprom.setLogicalName(DEVICETREE "/openprom");
 219      openprom.setLogicalName(DEVICETREE);
 220      openprom.claim();
 221      core.addChild(openprom);
 222    }
 223  }
 224  
 225  static hwNode *add_base_opal_node(hwNode & core)
 226  {
 227    vector < string >:: iterator it;
 228    vector < string > compat;
 229    string basepath = DEVICETREE "/ibm,opal";
 230    hwNode opal("firmware");
 231  
 232    if (!exists(basepath))
 233      return NULL;
 234  
 235    pushd(basepath);
 236  
 237    opal.setProduct("OPAL firmware");
 238    opal.setDescription("skiboot");
 239  
 240    compat = get_strings(basepath + "/compatible");
 241    for (it = compat.begin(); it != compat.end(); ++it) {
 242      if (matches(*it, "^ibm,opal-v"))
 243        opal.addCapability((*it).erase(0,4));
 244    }
 245  
 246    if (exists(basepath + "/ipmi/compatible") &&
 247      matches(get_string(basepath + "/ipmi/compatible"), "^ibm,opal-ipmi"))
 248      opal.addCapability("ipmi");
 249  
 250    if (exists(basepath + "/diagnostics/compatible") &&
 251      matches(get_string(basepath + "/diagnostics/compatible"), "^ibm,opal-prd"))
 252      opal.addCapability("prd");
 253  
 254    popd();
 255  
 256    opal.claim();
 257    return core.addChild(opal);
 258  }
 259  
 260  static void scan_devtree_firmware_powernv(hwNode & core)
 261  {
 262    int n;
 263    struct dirent **namelist;
 264  
 265    hwNode *opal = add_base_opal_node(core);
 266  
 267    if (!exists(DEVICETREE "/ibm,firmware-versions"))
 268      return;
 269  
 270    pushd(DEVICETREE "/ibm,firmware-versions");
 271    n = scandir(".", &namelist, selectfile, alphasort);
 272    popd();
 273  
 274    if (n <= 0)
 275      return;
 276  
 277    for (int i = 0; i < n; i++)
 278    {
 279      string sname = string(namelist[i]->d_name);
 280      string fullpath = string(DEVICETREE) + "/ibm,firmware-versions/" + sname;
 281  
 282      if (sname != "linux,phandle" && sname != "name" && sname != "phandle")
 283      {
 284        hwNode fwnode("firmware");
 285        fwnode.setDescription(sname);
 286        fwnode.setVersion(hw::strip(get_string(fullpath)));
 287        fwnode.claim();
 288        if (opal && sname == "skiboot") {
 289          opal->merge(fwnode);
 290          free(namelist[i]);
 291          continue;
 292        }
 293        core.addChild(fwnode);
 294      }
 295      free(namelist[i]);
 296    }
 297  
 298    free(namelist);
 299  }
 300  
 301  static string cpubusinfo(int cpu)
 302  {
 303    char buffer[20];
 304  
 305    snprintf(buffer, sizeof(buffer), "cpu@%d", cpu);
 306  
 307    return string(buffer);
 308  }
 309  
 310  
 311  static void set_cpu(hwNode & cpu, int currentcpu, const string & basepath)
 312  {
 313    cpu.setProduct(get_string(basepath + "/name"));
 314    cpu.claim();
 315    cpu.setBusInfo(cpubusinfo(currentcpu));
 316  
 317    cpu.setSize(get_u32(basepath + "/clock-frequency"));
 318    cpu.setClock(get_u32(basepath + "/bus-frequency"));
 319  
 320    if (exists(basepath + "/altivec"))
 321      cpu.addCapability("altivec");
 322  
 323    if (exists(basepath + "/performance-monitor"))
 324      cpu.addCapability("performance-monitor");
 325  }
 326  
 327  
 328  static void fill_cache_info(string cache_type, string cachebase,
 329  			    hwNode & cache, hwNode & icache)
 330  {
 331    cache.claim();
 332    cache.setDescription(cache_type);
 333    cache.setSize(get_u32(cachebase + "/d-cache-size"));
 334  
 335    if (exists(cachebase + "/cache-unified"))
 336      cache.setDescription(cache.getDescription() + " (unified)");
 337    else
 338    {
 339      icache = cache;
 340      cache.setDescription(cache.getDescription() + " (data)");
 341      icache.setDescription(icache.getDescription() + " (instruction)");
 342      icache.setSize(get_u32(cachebase + "/i-cache-size"));
 343    }
 344  }
 345  
 346  
 347  static void scan_devtree_cpu(hwNode & core)
 348  {
 349    struct dirent **namelist;
 350    int n;
 351    int currentcpu=0;
 352  
 353    pushd(DEVICETREE "/cpus");
 354    n = scandir(".", &namelist, selectdir, alphasort);
 355    popd();
 356    if (n < 0)
 357      return;
 358    else
 359    {
 360      for (int i = 0; i < n; i++)
 361      {
 362        string basepath =
 363          string(DEVICETREE "/cpus/") + string(namelist[i]->d_name);
 364        unsigned long version = 0;
 365        hwNode cpu("cpu",
 366          hw::processor);
 367        struct dirent **cachelist;
 368        int ncache;
 369  
 370        if (exists(basepath + "/device_type") &&
 371          hw::strip(get_string(basepath + "/device_type")) != "cpu")
 372          break;                                    // oops, not a CPU!
 373  
 374        cpu.setDescription("CPU");
 375        set_cpu(cpu, currentcpu++, basepath);
 376  
 377        version = get_u32(basepath + "/cpu-version");
 378        if (version != 0)
 379        {
 380          int minor = version & 0x00ff;
 381          int major = (version & 0xff00) >> 8;
 382          char buffer[20];
 383  
 384          snprintf(buffer, sizeof(buffer), "%lx.%d.%d",
 385            (version & 0xffff0000) >> 16, major, minor);
 386          cpu.setVersion(buffer);
 387        }
 388  
 389        if (hw::strip(get_string(basepath + "/state")) != "running")
 390          cpu.disable();
 391  
 392        if (exists(basepath + "/d-cache-size"))
 393        {
 394          hwNode cache("cache",
 395            hw::memory);
 396  
 397          cache.claim();
 398          cache.setDescription("L1 Cache");
 399          cache.setSize(get_u32(basepath + "/d-cache-size"));
 400          if (cache.getSize() > 0)
 401            cpu.addChild(cache);
 402        }
 403  
 404        pushd(basepath);
 405        ncache = scandir(".", &cachelist, selectdir, alphasort);
 406        popd();
 407        if (ncache > 0)
 408        {
 409          for (int j = 0; j < ncache; j++)
 410          {
 411            hwNode cache("cache",
 412              hw::memory);
 413            hwNode icache("cache",
 414              hw::memory);
 415            string cachebase = basepath + "/" + cachelist[j]->d_name;
 416  
 417            if (hw::strip(get_string(cachebase + "/device_type")) != "cache" &&
 418              hw::strip(get_string(cachebase + "/device_type")) != "l2-cache")
 419              break;                                // oops, not a cache!
 420  
 421  	  cache.setClock(get_u32(cachebase + "/clock-frequency"));
 422  	  fill_cache_info("L2 Cache", cachebase, cache, icache);
 423  
 424            if (icache.getSize() > 0)
 425              cpu.addChild(icache);
 426  
 427            if (cache.getSize() > 0)
 428              cpu.addChild(cache);
 429  
 430            free(cachelist[j]);
 431          }
 432          free(cachelist);
 433        }
 434  
 435        core.addChild(cpu);
 436  
 437        free(namelist[i]);
 438      }
 439      free(namelist);
 440    }
 441  }
 442  
 443  
 444  struct chip_vpd_data
 445  {
 446    string product;
 447    string serial;
 448    string slot;
 449    string vendor;
 450  };
 451  
 452  
 453  static void add_chip_vpd(string path, string name,
 454  			 map <uint32_t, chip_vpd_data *> & vpd)
 455  {
 456    int n;
 457    struct dirent **dirlist;
 458  
 459    pushd(path + name);
 460  
 461    if (name.substr(0, 9) == "processor" && exists("ibm,chip-id"))
 462    {
 463      uint32_t chip_id = get_u32("ibm,chip-id");
 464      chip_vpd_data *data = new chip_vpd_data();
 465  
 466      if (data)
 467      {
 468        if (exists("serial-number"))
 469          data->serial = hw::strip(get_string("serial-number"));
 470  
 471        if (exists("ibm,loc-code"))
 472  	data->slot = hw::strip(get_string("ibm,loc-code"));
 473  
 474        if (exists("part-number"))
 475          data->product = hw::strip(get_string("part-number"));
 476  
 477        if (exists("vendor"))
 478          data->vendor = hw::strip(get_string("vendor"));
 479  
 480        if (exists("fru-number"))
 481          data->product += " FRU# " + hw::strip(get_string("fru-number"));
 482  
 483        vpd.insert(std::pair<uint32_t, chip_vpd_data *>(chip_id, data));
 484      }
 485    }
 486  
 487    n = scandir(".", &dirlist, selectdir, alphasort);
 488    popd();
 489  
 490    if (n <= 0)
 491      return;
 492  
 493    for (int i = 0; i < n; i++)
 494    {
 495      add_chip_vpd(path + name + "/", dirlist[i]->d_name, vpd);
 496      free(dirlist[i]);
 497    }
 498  
 499    free(dirlist);
 500  }
 501  
 502  
 503  static void scan_chip_vpd(map <uint32_t, chip_vpd_data *> & vpd)
 504  {
 505    int n;
 506    struct dirent **namelist;
 507  
 508    if (!exists(DEVICETREEVPD))
 509      return;
 510  
 511    pushd(DEVICETREEVPD);
 512    n = scandir(".", &namelist, selectdir, alphasort);
 513    popd();
 514  
 515    if (n <= 0)
 516      return;
 517  
 518    for (int i = 0; i < n; i++)
 519    {
 520      add_chip_vpd(DEVICETREEVPD, namelist[i]->d_name, vpd);
 521      free(namelist[i]);
 522    }
 523  
 524    free(namelist);
 525  }
 526  
 527  
 528  static void fill_core_vpd(hwNode & cpu, string & basepath,
 529  			  map <uint32_t, chip_vpd_data *> & chip_vpd,
 530  			  map <uint32_t, string> & xscoms)
 531  {
 532    uint32_t chip_id;
 533    chip_vpd_data *data;
 534    string xscom_path;
 535  
 536    if (!exists(basepath + "/ibm,chip-id"))
 537      return;
 538  
 539    chip_id = get_u32(basepath + "/ibm,chip-id");
 540    cpu.setConfig("chip-id", chip_id);
 541  
 542    data = chip_vpd[chip_id];
 543    xscom_path = xscoms[chip_id];
 544  
 545    if (data)
 546    {
 547      cpu.setProduct(data->product);
 548      cpu.setSerial(data->serial);
 549      cpu.setSlot(data->slot);
 550      cpu.setVendor(data->vendor);
 551    }
 552  
 553    if (xscom_path != "")
 554    {
 555      vector <string> board_pieces;
 556  
 557      splitlines(hw::strip(get_string(xscom_path + "/board-info")),
 558  	       board_pieces, ' ');
 559      if (board_pieces.size() > 0)
 560        cpu.setVendor(board_pieces[0]);
 561  
 562      if (exists(xscom_path + "/serial-number"))
 563        cpu.setSerial(hw::strip(get_string(xscom_path + "/serial-number")));
 564  
 565      if (exists(xscom_path + "/ibm,slot-location-code"))
 566        cpu.setSlot(hw::strip(get_string(xscom_path + "/ibm,slot-location-code")));
 567  
 568      if (exists(xscom_path + "/part-number"))
 569        cpu.setProduct(hw::strip(get_string(xscom_path + "/part-number")));
 570    }
 571  }
 572  
 573  static void set_cpu_config_threads(hwNode & cpu, const string & basepath)
 574  {
 575    static int threads_per_cpu = 0;
 576  
 577    /* In power systems, there are equal no. of threads per cpu-core */
 578    if (threads_per_cpu == 0)
 579    {
 580      int rc;
 581      struct stat sbuf;
 582      string p = hw::strip(basepath + string("/ibm,ppc-interrupt-server#s"));
 583  
 584      /*
 585       * This file contains as many 32 bit interrupt server numbers, as the
 586       * number of threads per CPU (in hexadecimal format). st_size gives size
 587       * in bytes of a file. Hence, grouping by 4 bytes, we get the thread
 588       * count.
 589       */
 590      rc = stat(p.c_str(), &sbuf);
 591      if (!rc)
 592        threads_per_cpu = sbuf.st_size / 4;
 593    }
 594  
 595    cpu.setConfig("threads", threads_per_cpu);
 596  }
 597  
 598  
 599  static void scan_xscom_node(map <uint32_t, string> & xscoms)
 600  {
 601    int n;
 602    struct dirent **namelist;
 603  
 604    pushd(DEVICETREE);
 605    n = scandir(".", &namelist, selectdir, alphasort);
 606    popd();
 607  
 608    if (n <= 0)
 609      return;
 610  
 611    for (int i = 0; i < n; i++) {
 612      string sname = string(namelist[i]->d_name);
 613      string fullpath = "";
 614      int chip_id = 0;
 615  
 616      if (sname.substr(0,5) == "xscom") {
 617        fullpath = string(DEVICETREE) + "/" + sname;
 618        chip_id = get_u32(fullpath + "/ibm,chip-id");
 619        xscoms.insert(std::pair<uint32_t, string>(chip_id, fullpath));
 620      }
 621  
 622      free(namelist[i]);
 623    }
 624    free(namelist);
 625  }
 626  
 627  static void scan_devtree_cpu_power(hwNode & core)
 628  {
 629    int n;
 630    int currentcpu = 0;
 631    struct dirent **namelist;
 632    map <uint32_t, pair<uint32_t, vector <hwNode> > > l2_caches;
 633    map <uint32_t, vector <hwNode> > l3_caches;
 634    map <uint32_t, chip_vpd_data *> chip_vpd;
 635    map <uint32_t, string> xscoms;
 636  
 637    pushd(DEVICETREE "/cpus");
 638    n = scandir(".", &namelist, selectdir, alphasort);
 639    popd();
 640    if (n < 0)
 641      return;
 642  
 643    /*
 644     * 'cpus' node contains CPU, L2 and L3 cache nodes. L1 cache information is
 645     * available under CPU node itself. l2-cache (or next-level-cache) property
 646     * contains next level cache node phandle/ibm,phanle number.
 647     * First pass creates cache nodes and second pass will link cache nodes to
 648     * corresponding CPU nodes.
 649     */
 650    for (int i = 0; i < n; i++)
 651    {
 652      string product;
 653      string basepath = string(DEVICETREE "/cpus/") + string(namelist[i]->d_name);
 654      hwNode cache("cache", hw::memory);
 655      hwNode icache("cache", hw::memory);
 656      vector <hwNode> value;
 657  
 658      if (!exists(basepath + "/device_type"))
 659        continue;
 660  
 661      if (hw::strip(get_string(basepath + "/device_type")) != "cache")
 662        continue;
 663  
 664      product = hw::strip(get_string(basepath + "/name"));
 665  
 666      if (hw::strip(get_string(basepath + "/status")) != "okay")
 667      {
 668        cache.disable();
 669        icache.disable();
 670      }
 671  
 672      if (product == "l2-cache")
 673        fill_cache_info("L2 Cache", basepath, cache, icache);
 674      else
 675        fill_cache_info("L3 Cache", basepath, cache, icache);
 676  
 677      if (icache.getSize() > 0)
 678        value.insert(value.begin(), icache);
 679  
 680      if (cache.getSize() > 0)
 681        value.insert(value.begin(), cache);
 682  
 683      if (value.size() > 0)
 684      {
 685        uint32_t phandle = 0;
 686  
 687        if (exists(basepath + "/phandle"))
 688          phandle = get_u32(basepath + "/phandle");
 689        else if (exists(basepath + "/ibm,phandle")) // on pSeries LPARs
 690          phandle = get_u32(basepath + "/ibm,phandle");
 691  
 692        if (!phandle)
 693          continue;
 694  
 695        if (product == "l2-cache")
 696        {
 697          uint32_t l3_key = 0; // 0 indicating no next level of cache
 698  
 699          if (exists(basepath + "/l2-cache"))
 700            l3_key = get_u32(basepath + "/l2-cache");
 701          else if (exists(basepath + "/next-level-cache")) //on OpenPOWER systems
 702            l3_key = get_u32(basepath + "/next-level-cache");
 703  
 704          pair <uint32_t, vector <hwNode> > p (l3_key, value);
 705          l2_caches[phandle] = p;
 706        }
 707        else if (product == "l3-cache")
 708        {
 709          l3_caches[phandle] = value;
 710        }
 711      }
 712    } // first pass end
 713  
 714    /*
 715     * We have chip level VPD information (like part number, slot, etc).
 716     * and this information is same for all cores under chip.
 717     * Fetch chip-level VPD from /vpd node.
 718     */
 719    scan_chip_vpd(chip_vpd);
 720  
 721    // List all xscom nodes under DT
 722    scan_xscom_node(xscoms);
 723  
 724    for (int i = 0; i < n; i++) //second and final pass
 725    {
 726      uint32_t l2_key = 0;
 727      uint32_t version = 0;
 728      uint32_t reg;
 729      string basepath = string(DEVICETREE "/cpus/") + string(namelist[i]->d_name);
 730      hwNode cpu("cpu", hw::processor);
 731  
 732      if (!exists(basepath + "/device_type"))
 733      {
 734        free(namelist[i]);
 735        continue;
 736      }
 737  
 738      if (hw::strip(get_string(basepath + "/device_type")) != "cpu")
 739      {
 740        free(namelist[i]);
 741        continue;
 742      }
 743  
 744      cpu.setDescription("CPU");
 745      cpu.addHint("logo", string("powerpc"));
 746      set_cpu(cpu, currentcpu++, basepath);
 747  
 748      reg = get_u32(basepath + "/reg");
 749      cpu.setPhysId(tostring(reg));
 750  
 751      version = get_u32(basepath + "/cpu-version");
 752      if (version != 0)
 753        cpu.setVersion(tostring(version));
 754  
 755      fill_core_vpd(cpu, basepath, chip_vpd, xscoms);
 756  
 757      if (hw::strip(get_string(basepath + "/status")) != "okay")
 758        cpu.disable();
 759  
 760      set_cpu_config_threads(cpu, basepath);
 761  
 762      if (exists(basepath + "/d-cache-size"))
 763      {
 764        hwNode cache("cache", hw::memory);
 765        hwNode icache("cache", hw::memory);
 766  
 767        fill_cache_info("L1 Cache", basepath, cache, icache);
 768  
 769        if (icache.getSize() > 0)
 770          cpu.addChild(icache);
 771  
 772        if (cache.getSize() > 0)
 773          cpu.addChild(cache);
 774  
 775        if (hw::strip(get_string(basepath + "/status")) != "okay")
 776        {
 777          cache.disable();
 778          icache.disable();
 779        }
 780      }
 781  
 782      if (exists(basepath + "/l2-cache"))
 783          l2_key = get_u32(basepath + "/l2-cache");
 784      else if (exists(basepath + "/next-level-cache"))
 785          l2_key = get_u32(basepath + "/next-level-cache");
 786  
 787      if (l2_key != 0)
 788      {
 789        map <uint32_t, pair <uint32_t, vector <hwNode> > >::
 790          const_iterator got = l2_caches.find(l2_key);
 791  
 792        if (!(got == l2_caches.end()))
 793          for (uint32_t j = 0; j < (got->second).second.size(); j++)
 794            cpu.addChild((got->second).second[j]);
 795  
 796        if ((got->second).first != 0) // we have another level of cache
 797        {
 798          map <uint32_t, vector <hwNode> >::const_iterator got_l3 =
 799            l3_caches.find ((got->second).first);
 800  
 801          if (!(got_l3 == l3_caches.end()))
 802            for (uint32_t j = 0; j < (got_l3->second).size(); j++)
 803              cpu.addChild((got_l3->second)[j]);
 804        }
 805      }
 806  
 807      core.addChild(cpu);
 808  
 809      free(namelist[i]);
 810    }
 811    free(namelist);
 812  
 813    map <uint32_t, chip_vpd_data *>::iterator it;
 814    for (it = chip_vpd.begin(); it != chip_vpd.end(); it++)
 815      delete it->second;
 816  }
 817  
 818  static bool add_memory_bank_mba_dimm(string path,
 819  				     unsigned long serial, hwNode & bank)
 820  {
 821    bool found = false;
 822    int n;
 823    struct dirent **namelist;
 824  
 825    pushd(path);
 826    n = scandir(".", &namelist, selectdir, alphasort);
 827    popd();
 828  
 829    if (n < 0)
 830      return found;
 831  
 832    for (int i = 0; i < n; i++)
 833    {
 834      string sname = string(namelist[i]->d_name);
 835      string fullpath = path + "/" + sname;
 836  
 837      if (found)
 838      {
 839        free(namelist[i]);
 840        continue;
 841      }
 842  
 843      if (sname.substr(0, 13) == "memory-buffer")
 844      {
 845        if (exists(fullpath + "/frequency-mhz"))
 846        {
 847          int hz = get_u32(fullpath + "/frequency-mhz") * 1000000;
 848          bank.setClock(hz);
 849        }
 850        found = add_memory_bank_mba_dimm(fullpath, serial, bank);
 851      } else if (sname.substr(0, 3) == "mba") {
 852        found = add_memory_bank_mba_dimm(fullpath, serial, bank);
 853      } else if ((sname.substr(0, 4) == "dimm") &&
 854  	       (get_u32(fullpath + "/serial-number") == serial)) {
 855        vector < reg_entry > regs = get_reg_property(fullpath);
 856        bank.setSize(regs[0].size);
 857  
 858        bank.setSlot(hw::strip(get_string(fullpath + "/ibm,slot-location-code")));
 859        found = true;
 860      }
 861      free(namelist[i]);
 862    }
 863  
 864    free(namelist);
 865    return found;
 866  }
 867  
 868  
 869  static void add_memory_bank_spd(string path, hwNode & bank)
 870  {
 871    char dimmversion[20];
 872    uint16_t mfg_loc_offset;
 873    uint16_t rev_offset1;
 874    uint16_t rev_offset2;
 875    uint16_t year_offset;
 876    uint16_t week_offset;
 877    uint16_t partno_offset;
 878    uint16_t ver_offset;
 879    uint16_t serial_offset;
 880    uint16_t bus_width_offset;
 881    int fd;
 882    size_t len = 0;
 883    dimminfo_buf dimminfo;
 884  
 885    fd = open(path.c_str(), O_RDONLY);
 886    if (fd < 0)
 887      return;
 888  
 889    if (read(fd, &dimminfo, 0x80) != 0x80)
 890    {
 891      close(fd);
 892      return;
 893    }
 894  
 895    /* Read entire SPD eeprom */
 896    if (dimminfo[2] >= 9) /* DDR3 & DDR4 */
 897    {
 898      uint8_t val = (dimminfo[0] >> 4) & 0x7;
 899      if (val == 1)
 900        len = 256;
 901      else if (val == 2)
 902        len = 512;
 903    } else if (dimminfo[0] < 15) { /* DDR 2 */
 904      len = 1 << dimminfo[1];
 905    }
 906  
 907    if (len > 0x80)
 908      read(fd, &dimminfo[0x80], len - 0x80);
 909  
 910    close(fd);
 911  
 912    if (dimminfo[2] >= 9) {
 913      int rank_offset;
 914      double ns;
 915      char vendor[5];
 916      const char *type, *mod_type;
 917  
 918      rev_offset1 = 0x92;
 919      rev_offset2 = 0x93;
 920      ver_offset = 0x01;
 921  
 922      if (dimminfo[0x2] >= 0xc) {
 923        type = "DDR4";
 924        mfg_loc_offset = 0x142;
 925        year_offset = 0x143;
 926        week_offset = 0x144;
 927        partno_offset = 0x149;
 928        bus_width_offset = 0x0d;
 929        serial_offset = 0x145;
 930        rank_offset = 0xc;
 931  
 932        /*
 933         * There is no other valid values for the medium- and fine- timebase
 934         * other than (125ps, 1ps), so we hard-code those here. The fine
 935         * t_{ckavg}_{min} value is signed. Divide by 2 to get from raw clock
 936         * to expected data rate
 937         */
 938        ns = (((float)dimminfo[0x12] * 0.125) +
 939  	    (((signed char) dimminfo[0x7d]) * 0.001)) / 2;
 940        snprintf(vendor, sizeof(vendor), "%x%x", dimminfo[0x141], dimminfo[0x140]);
 941      } else {
 942        type = "DDR3";
 943        mfg_loc_offset = 0x77;
 944        year_offset = 0x78;
 945        week_offset = 0x79;
 946        partno_offset = 0x80;
 947        serial_offset = 0x7a;
 948        bus_width_offset = 0x08;
 949        rank_offset = 0x7;
 950  
 951        ns = (dimminfo[0xc] / 2) * (dimminfo[0xa] / (float) dimminfo[0xb]);
 952        snprintf(vendor, sizeof(vendor), "%x%x", dimminfo[0x76], dimminfo[0x75]);
 953      }
 954  
 955      /* DDR3 & DDR4 error detection and correction scheme */
 956      switch ((dimminfo[bus_width_offset] >> 3) & 0x3)
 957      {
 958        case 0x00:
 959          bank.setConfig("errordetection", "none");
 960          break;
 961        case 0x01:
 962          bank.addCapability("ecc");
 963          bank.setConfig("errordetection", "ecc");
 964          break;
 965      }
 966  
 967      // Add DIMM rank
 968      bank.setConfig("rank", ((dimminfo[rank_offset] >> 3) & 0x7) + 1);
 969  
 970      bank.setClock(1000000000 / ns);
 971      bank.setVendor(jedec_resolve(vendor));
 972  
 973      char description[100];
 974      switch(dimminfo[0x3])
 975      {
 976        case 0x1:
 977          mod_type = "RDIMM";
 978          break;
 979        case 0x2:
 980          mod_type = "UDIMM";
 981          break;
 982        case 0x3:
 983          mod_type = "SODIMM";
 984          break;
 985        case 0x4:
 986          mod_type = "LRDIMM";
 987          break;
 988        default:
 989          mod_type = "DIMM";
 990      }
 991  
 992      snprintf(description, sizeof(description), "%s %s %d MHz (%0.1fns)",
 993  	     mod_type, type, (int) (1000 / ns), ns);
 994      bank.setDescription(description);
 995    } else {
 996      mfg_loc_offset = 0x48;
 997      rev_offset1 = 0x5b;
 998      rev_offset2 = 0x5c;
 999      year_offset = 0x5d;
1000      week_offset = 0x5e;
1001      partno_offset = 0x49;
1002      ver_offset = 0x3e;
1003      serial_offset = 0x5f;
1004  
1005      switch (dimminfo[0xb] & 0x3) // DDR2 error detection and correction scheme
1006      {
1007        case 0x00:
1008          bank.setConfig("errordetection", "none");
1009          break;
1010        case 0x01:
1011          bank.addCapability("parity");
1012          bank.setConfig("errordetection", "parity");
1013          break;
1014        case 0x02:
1015        case 0x03:
1016          bank.addCapability("ecc");
1017          bank.setConfig("errordetection", "ecc");
1018          break;
1019      }
1020    }
1021  
1022    snprintf(dimmversion, sizeof(dimmversion),
1023      "%02X%02X,%02X %02X,%02X", dimminfo[rev_offset1],
1024      dimminfo[rev_offset2], dimminfo[year_offset], dimminfo[week_offset],
1025      dimminfo[mfg_loc_offset]);
1026    bank.setProduct(string((char *) &dimminfo[partno_offset], 18));
1027    bank.setVersion(dimmversion);
1028  
1029    unsigned long serial = be_long(&dimminfo[serial_offset]);
1030    int version = dimminfo[ver_offset];
1031    char buff[32];
1032  
1033    snprintf(buff, sizeof(buff), "0x%lx", serial);
1034    bank.setSerial(buff);
1035  
1036    add_memory_bank_mba_dimm(DEVICETREE, serial, bank);
1037  
1038    snprintf(buff, sizeof(buff), "spd-%d.%d", (version & 0xF0) >> 4, version & 0x0F);
1039    bank.addCapability(buff);
1040  }
1041  
1042  static void add_memory_bank(string name, string path, hwNode & core)
1043  {
1044    struct dirent **dirlist;
1045    string product;
1046    int n;
1047  
1048    hwNode *memory = core.getChild("memory");
1049    if(!memory)
1050      memory = core.addChild(hwNode("memory", hw::memory));
1051  
1052    pushd(path + "/" + name);
1053    if(name.substr(0, 7) == "ms-dimm" ||
1054       name.substr(0, 18) == "IBM,memory-module@")
1055    {
1056      hwNode bank("bank", hw::memory);
1057      bank.claim(true);
1058      bank.addHint("icon", string("memory"));
1059  
1060      if(exists("serial-number"))
1061        bank.setSerial(hw::strip(get_string("serial-number")));
1062  
1063      product = hw::strip(get_string("part-number"));
1064      if(exists("fru-number"))
1065      {
1066        product += " FRU# " + hw::strip(get_string("fru-number"));
1067      }
1068      if(product != "")
1069        bank.setProduct(hw::strip(product));
1070  
1071      string description = "DIMM";
1072      string package = hw::strip(get_string("ibm,mem-package"));
1073      if (!package.empty())
1074        description = package;
1075      string memtype = hw::strip(get_string("ibm,mem-type"));
1076      if (!memtype.empty())
1077        description += " " + memtype;
1078      if(exists("description"))
1079        description = hw::strip(get_string("description"));
1080      bank.setDescription(description);
1081      if (exists("ibm,chip-id"))
1082        bank.setConfig("chip-id", get_u32("ibm,chip-id"));
1083  
1084      if(exists("ibm,loc-code"))
1085        bank.setSlot(hw::strip(get_string("ibm,loc-code")));
1086      unsigned long size = get_number("size") * 1024 * 1024;
1087      if (exists("ibm,size"))
1088        size = get_u32("ibm,size");
1089      if (size > 0)
1090        bank.setSize(size);
1091  
1092      // Parse Memory SPD data
1093      if (exists("spd"))
1094        add_memory_bank_spd(path + "/" + name + "/spd", bank);
1095  
1096      // Parse Memory SPD data
1097      if (exists("frequency"))
1098        bank.setClock(get_u32("frequency"));
1099  
1100      memory->addChild(bank);
1101    } else if(name.substr(0, 4) == "dimm") {
1102      hwNode bank("bank", hw::memory);
1103      bank.claim(true);
1104      bank.addHint("icon", string("memory"));
1105  
1106      // Parse Memory SPD data
1107      add_memory_bank_spd(path + name + "/spd", bank);
1108  
1109      memory->addChild(bank);
1110    }
1111  
1112    n = scandir(".", &dirlist, selectdir, alphasort);
1113    popd();
1114  
1115    if (n < 0)
1116      return;
1117  
1118    for (int i = 0; i < n; i++)
1119    {
1120      add_memory_bank(dirlist[i]->d_name, path + "/" + name, core);
1121      free(dirlist[i]);
1122    }
1123    free(dirlist);
1124  }
1125  
1126  
1127  static void scan_devtree_memory_powernv(hwNode & core)
1128  {
1129    struct dirent **namelist;
1130    int n;
1131    string path = DEVICETREEVPD;
1132  
1133    pushd(DEVICETREEVPD);
1134    n = scandir(".", &namelist, selectdir, alphasort);
1135    popd();
1136  
1137    if (n < 0)
1138      return;
1139  
1140    for (int i = 0; i < n; i++)
1141    {
1142      add_memory_bank(namelist[i]->d_name, path, core);
1143      free(namelist[i]);
1144    }
1145  
1146    free(namelist);
1147  }
1148  
1149  
1150  // older POWER hardware
1151  static void scan_devtree_memory_ibm(hwNode & core)
1152  {
1153    struct dirent **namelist;
1154    pushd(DEVICETREE);
1155    int n = scandir(".", &namelist, selectdir, alphasort);
1156    popd();
1157  
1158    if (n < 0)
1159      return;
1160  
1161    for (int i = 0; i < n; i++)
1162    {
1163      if (strncmp(namelist[i]->d_name, "memory-controller@", 18) == 0)
1164      {
1165        add_memory_bank(namelist[i]->d_name, DEVICETREE, core);
1166      }
1167      free(namelist[i]);
1168    }
1169    free(namelist);
1170  }
1171  
1172  
1173  // Apple and ARM
1174  static void scan_devtree_memory(hwNode & core)
1175  {
1176    int currentmc = -1;                             // current memory controller
1177    hwNode *memory = core.getChild("memory");
1178  
1179    while (true)
1180    {
1181      char buffer[10];
1182      string mcbase;
1183      vector < string > slotnames;
1184      vector < string > dimmtypes;
1185      vector < string > dimmspeeds;
1186      vector < reg_entry > regs;
1187      string dimminfo;
1188  
1189      snprintf(buffer, sizeof(buffer), "%d", currentmc);
1190      if (currentmc >= 0)
1191        mcbase = string(DEVICETREE "/memory@") + string(buffer);
1192      else
1193        mcbase = string(DEVICETREE "/memory");
1194      slotnames =
1195        get_strings(mcbase + string("/slot-names"), 4);
1196      dimmtypes = get_strings(mcbase + string("/dimm-types"));
1197      dimmspeeds = get_strings(mcbase + string("/dimm-speeds"));
1198      regs = get_reg_property(mcbase);
1199      dimminfo = mcbase + string("/dimm-info");
1200  
1201      if (slotnames.size() == 0)
1202      {
1203        if (currentmc < 0)
1204        {
1205          currentmc++;
1206          continue;
1207        }
1208        else
1209          break;
1210      }
1211  
1212      if (!memory || (currentmc > 0))
1213      {
1214        memory = core.addChild(hwNode("memory", hw::memory));
1215      }
1216  
1217      if (!memory)
1218        break;
1219  
1220      if (regs.size() == slotnames.size())
1221      {
1222        for (unsigned int i = 0; i < slotnames.size(); i++)
1223        {
1224          uint64_t size = regs[i].size;
1225          hwNode bank("bank", hw::memory);
1226  
1227  	// Parse Memory SPD data
1228          add_memory_bank_spd(dimminfo, bank);
1229  
1230          if (size > 0)
1231            bank.addHint("icon", string("memory"));
1232          bank.setDescription("Memory bank");
1233          bank.setSlot(slotnames[i]);
1234          if (i < dimmtypes.size())
1235            bank.setDescription(dimmtypes[i]);
1236          if (i < dimmspeeds.size())
1237            bank.setProduct(hw::strip(dimmspeeds[i]));
1238          bank.setSize(size);
1239          memory->addChild(bank);
1240        }
1241      }
1242      currentmc++;
1243  
1244      memory = NULL;
1245    }
1246  }
1247  
1248  
1249  struct pmac_mb_def
1250  {
1251    const char *model;
1252    const char *modelname;
1253    const char *icon;
1254  };
1255  
1256  static struct pmac_mb_def pmac_mb_defs[] =
1257  {
1258  /*
1259   * Warning: ordering is important as some models may claim
1260   * * beeing compatible with several types
1261   */
1262    {"AAPL,8500", "PowerMac 8500/8600", ""},
1263    {"AAPL,9500", "PowerMac 9500/9600", ""},
1264    {"AAPL,7200", "PowerMac 7200", ""},
1265    {"AAPL,7300", "PowerMac 7200/7300", ""},
1266    {"AAPL,7500", "PowerMac 7500", ""},
1267    {"AAPL,ShinerESB", "Apple Network Server", ""},
1268    {"AAPL,e407", "Alchemy", ""},
1269    {"AAPL,e411", "Gazelle", ""},
1270    {"AAPL,3400/2400", "PowerBook 3400", "laptop"},
1271    {"AAPL,3500", "PowerBook 3500", "laptop"},
1272    {"AAPL,Gossamer", "PowerMac G3 (Gossamer)", ""},
1273    {"AAPL,PowerMac G3", "PowerMac G3 (Silk)", ""},
1274    {"AAPL,PowerBook1998", "PowerBook Wallstreet", "laptop"},
1275    {"iMac,1", "iMac (first generation)", ""},
1276    {"PowerMac1,1", "Blue & White G3", "powermac"},
1277    {"PowerMac1,2", "PowerMac G4 PCI Graphics", "powermac"},
1278    {"PowerMac2,1", "iMac FireWire", ""},
1279    {"PowerMac2,2", "iMac FireWire", ""},
1280    {"PowerMac3,1", "PowerMac G4 AGP Graphics", "powermac"},
1281    {"PowerMac3,2", "PowerMac G4 AGP Graphics", "powermac"},
1282    {"PowerMac3,3", "PowerMac G4 AGP Graphics", "powermac"},
1283    {"PowerMac3,4", "PowerMac G4 QuickSilver", "powermac"},
1284    {"PowerMac3,5", "PowerMac G4 QuickSilver", "powermac"},
1285    {"PowerMac3,6", "PowerMac G4 Windtunnel", "powermac"},
1286    {"PowerMac4,1", "iMac \"Flower Power\"", ""},
1287    {"PowerMac4,2", "iMac LCD 15\"", ""},
1288    {"PowerMac4,4", "eMac", ""},
1289    {"PowerMac4,5", "iMac LCD 17\"", ""},
1290    {"PowerMac5,1", "PowerMac G4 Cube", ""},
1291    {"PowerMac5,2", "PowerMac G4 Cube", ""},
1292    {"PowerMac6,1", "iMac LCD 17\"", ""},
1293    {"PowerMac7,2", "PowerMac G5", "powermacg5"},
1294    {"PowerMac7,3", "PowerMac G5", "powermacg5"},
1295    {"PowerMac8,1", "iMac G5", ""},
1296    {"PowerMac8,2", "iMac G5", ""},
1297    {"PowerMac10,1", "Mac mini", "mini"},
1298    {"PowerMac10,2", "Mac mini", "mini"},
1299    {"PowerMac11,2", "PowerMac G5", "powermacg5"},
1300    {"PowerMac12,1", "iMac G5", ""},
1301    {"PowerBook1,1", "PowerBook 101 (Lombard)", "laptop"},
1302    {"PowerBook2,1", "iBook (first generation)", "laptop"},
1303    {"PowerBook2,2", "iBook FireWire", "laptop"},
1304    {"PowerBook3,1", "PowerBook Pismo", "laptop"},
1305    {"PowerBook3,2", "PowerBook Titanium", "laptop"},
1306    {"PowerBook3,3", "PowerBook Titanium w/ Gigabit Ethernet", "laptop"},
1307    {"PowerBook3,4", "PowerBook Titanium w/ DVI", "laptop"},
1308    {"PowerBook3,5", "PowerBook Titanium 1GHz", "laptop"},
1309    {"PowerBook4,1", "iBook 12\" (May 2001)", "laptop"},
1310    {"PowerBook4,2", "iBook 2", "laptop"},
1311    {"PowerBook4,3", "iBook 2 rev. 2 (Nov 2002)", "laptop"},
1312    {"PowerBook4,4", "iBook 2 rev. 2", "laptop"},
1313    {"PowerBook5,1", "PowerBook G4 17\"", "laptop"},
1314    {"PowerBook5,2", "PowerBook G4 15\"", "laptop"},
1315    {"PowerBook5,3", "PowerBook G4 17\" 1.33GHz", "laptop"},
1316    {"PowerBook5,4", "PowerBook G4 15\" 1.5/1.33GHz", "laptop"},
1317    {"PowerBook5,5", "PowerBook G4 17\" 1.5GHz", "laptop"},
1318    {"PowerBook5,6", "PowerBook G4 15\" 1.67/1.5GHz", "laptop"},
1319    {"PowerBook5,7", "PowerBook G4 17\" 1.67GHz", "laptop"},
1320    {"PowerBook5,8", "PowerBook G4 15\" double layer SD", "laptop"},
1321    {"PowerBook5,9", "PowerBook G4 17\" double layer SD", "laptop"},
1322    {"PowerBook6,1", "PowerBook G4 12\"", "laptop"},
1323    {"PowerBook6,2", "PowerBook G4 12\" DVI", "laptop"},
1324    {"PowerBook6,3", "iBook G4", "laptop"},
1325    {"PowerBook6,4", "PowerBook G4 12\"", "laptop"},
1326    {"PowerBook6,5", "iBook G4", "laptop"},
1327    {"PowerBook6,7", "iBook G4", "laptop"},
1328    {"PowerBook6,8", "PowerBook G4 12\" 1.5GHz", "laptop"},
1329    {"RackMac1,1", "XServe", ""},
1330    {"RackMac1,2", "XServe rev. 2", ""},
1331    {"RackMac3,1", "XServe G5", ""},
1332  };
1333  
1334  static bool get_apple_model(hwNode & n)
1335  {
1336    string model = n.getProduct();
1337    if (model == "")
1338      return false;
1339  
1340    for (unsigned int i = 0; i < sizeof(pmac_mb_defs) / sizeof(pmac_mb_def);
1341      i++)
1342    if (model == pmac_mb_defs[i].model)
1343    {
1344      n.setProduct(pmac_mb_defs[i].modelname);
1345      n.addHint("icon", string(pmac_mb_defs[i].icon));
1346    }
1347  
1348    return false;
1349  }
1350  
1351  struct ibm_model_def {
1352    const char *model;
1353    const char *modelname;
1354    const char *icon;
1355    const char *chassis; // matches DMI chassis types
1356  };
1357  struct ibm_model_def ibm_model_defs[] =
1358  {
1359    {"0200", "BladeCenter QS20", "", "blade"},
1360    {"0792", "BladeCenter QS21", "", "blade"},
1361    {"6778", "BladeCenter JS21", "", "blade"},
1362    {"6779", "BladeCenter JS21", "", "blade"},
1363    {"7047-185", "IntelliStation POWER 185", "towercomputer", "tower"},
1364    {"7988", "BladeCenter JS21", "", "blade"},
1365    {"8202", "Power 720 Express", "", "rackmount"},
1366    {"8205", "Power 740 Express", "", "rackmount"},
1367    {"8231-E1C", "Power 710 Express", "", "rackmount"},
1368    {"8231-E1D", "Power 710 Express", "", "rackmount"},
1369    {"8231-E2C", "Power 730 Express", "", "rackmount"},
1370    {"8231-E2D", "Power 730 Express", "", "rackmount"},
1371    {"8233-E8B", "Power 750 Express", "", "rackmount"},
1372    {"8236-E8C", "Power 755", "", "rackmount"},
1373    {"8286-41A", "Power Systems S814", "", "rackmount"},
1374    {"8286-42A", "Power Systems S824", "", "rackmount"},
1375    {"8406-70Y", "Power PS700", "", "rackmount"},
1376    {"8406-71Y", "BladeCenter PS702", "", "blade"},
1377    {"8842", "BladeCenter JS20", "", "blade"},
1378    {"8844", "BladeCenter JS21", "", "blade"},
1379    {"9111-285", "IntelliStation POWER 285", "towercomputer", "tower"},
1380    {"9112-265", "IntelliStation POWER 265", "towercomputer", "tower"},
1381    {"9114-275", "IntelliStation POWER 275", "towercomputer", "tower"},
1382    {"9123", "eServer OpenPower 710", "", "rackmount"},
1383    {"9124", "eServer OpenPower 720", "", "rackmount"},
1384  };
1385  
1386  static void get_ips_model(hwNode & n)
1387  {
1388    string product = n.getProduct();
1389    if (product.empty())
1390      return;
1391    if (product.compare(0, 4, "IPS,") != 0)
1392      return;
1393  
1394    n.setVendor("IPS");
1395  }
1396  
1397  static void get_ibm_model(hwNode & n)
1398  {
1399    string product = n.getProduct();
1400    if (product.empty())
1401      return;
1402    if (product.compare(0, 4, "IBM,") != 0)
1403      return;
1404  
1405    n.setVendor("IBM");
1406    string machinetype = product.substr(4, 4);
1407    string model = product.substr(4);
1408  
1409    for (size_t i = 0; i < sizeof(ibm_model_defs) / sizeof(ibm_model_def); i ++)
1410    {
1411      if (ibm_model_defs[i].model == machinetype || ibm_model_defs[i].model == model)
1412      {
1413        n.setProduct(n.getProduct() + " (" + ibm_model_defs[i].modelname + ")");
1414        n.addHint("icon", string(ibm_model_defs[i].icon));
1415        n.setConfig("chassis", ibm_model_defs[i].chassis);
1416        return;
1417      }
1418    }
1419  }
1420  
1421  static void fix_serial_number(hwNode & n)
1422  {
1423    string serial = n.getSerial();
1424  
1425    if(serial.find('\0')==string::npos) return;     // nothing to do
1426  
1427    n.setSerial(hw::strip(serial.substr(13)) + hw::strip(serial.substr(0,13)));
1428  }
1429  
1430  
1431  bool scan_device_tree(hwNode & n)
1432  {
1433    hwNode *core = n.getChild("core");
1434  
1435    if (!exists(DEVICETREE))
1436      return false;
1437  
1438    if (!core)
1439    {
1440      n.addChild(hwNode("core", hw::bus));
1441      core = n.getChild("core");
1442    }
1443  
1444    if (exists(DEVICETREE "/ibm,vendor-model"))
1445  	  n.setProduct(get_string(DEVICETREE "/ibm,vendor-model", n.getProduct()));
1446    else
1447  	  n.setProduct(get_string(DEVICETREE "/model", n.getProduct()));
1448  
1449    n.addHint("icon", string("motherboard"));
1450  
1451    n.setSerial(get_string(DEVICETREE "/serial-number", n.getSerial()));
1452    if (n.getSerial() == "")
1453    {
1454  	  if (exists(DEVICETREE "/ibm,vendor-system-id"))
1455  		  n.setSerial(get_string(DEVICETREE "/ibm,vendor-system-id"));
1456  	  else
1457  		  n.setSerial(get_string(DEVICETREE "/system-id"));
1458    }
1459    fix_serial_number(n);
1460  
1461    n.setVendor(get_string(DEVICETREE "/copyright", n.getVendor()));
1462    get_apple_model(n);
1463    get_ips_model(n);
1464    get_ibm_model(n);
1465    if (matches(get_string(DEVICETREE "/compatible"), "^ibm,powernv"))
1466    {
1467      n.setVendor(get_string(DEVICETREE "/vendor", "IBM"));
1468  
1469      if (exists(DEVICETREE "/model-name"))
1470        n.setProduct(n.getProduct() + " (" +
1471  		   hw::strip(get_string(DEVICETREE "/model-name")) + ")");
1472  
1473      n.setDescription("PowerNV");
1474      if (core)
1475      {
1476        core->addHint("icon", string("board"));
1477        scan_devtree_root(*core);
1478        scan_devtree_cpu_power(*core);
1479        scan_devtree_memory_powernv(*core);
1480        scan_devtree_firmware_powernv(*core);
1481        n.addCapability("powernv", "Non-virtualized");
1482        n.addCapability("opal", "OPAL firmware");
1483      }
1484    }
1485    else if(matches(get_string(DEVICETREE "/compatible"), "qemu,pseries"))
1486    {
1487      string product;
1488  
1489      if ( exists(DEVICETREE "/host-serial") )
1490        n.setSerial(get_string(DEVICETREE "/host-serial"));
1491  
1492      if ( exists( DEVICETREE "/vm,uuid") )
1493        n.setConfig("uuid", get_string(DEVICETREE "/vm,uuid"));
1494  
1495      n.setVendor(get_string(DEVICETREE "/vendor", "IBM"));
1496  
1497      if ( exists(DEVICETREE "/hypervisor/compatible") ) {
1498        product = get_string(DEVICETREE "/hypervisor/compatible");
1499        product = product.substr(0, product.size()-1);
1500      }
1501  
1502      if ( exists(DEVICETREE "/host-model") ) {
1503        product += " Model# ";
1504        product += get_string(DEVICETREE "/host-model");
1505      }
1506  
1507      if (product != "")
1508        n.setProduct(product);
1509  
1510      n.setDescription("pSeries Guest");
1511  
1512      if (core)
1513      {
1514        core->addHint("icon", string("board"));
1515        scan_devtree_root(*core);
1516        scan_devtree_cpu_power(*core);
1517        core->addCapability("qemu", "QEMU virtualization");
1518        core->addCapability("guest", "Virtualization guest");
1519      }
1520    }
1521    else
1522    {
1523      if (core)
1524      {
1525        core->addHint("icon", string("board"));
1526        scan_devtree_root(*core);
1527        scan_devtree_bootrom(*core);
1528        if (exists(DEVICETREE "/ibm,lpar-capable")) {
1529          n.setDescription("pSeries LPAR");
1530          if (exists( DEVICETREE "/ibm,partition-uuid"))
1531            n.setConfig("uuid", get_string(DEVICETREE "/ibm,partition-uuid"));
1532          scan_devtree_cpu_power(*core);
1533        }
1534        else {
1535          if (exists(DEVICETREE "/cpus"))
1536            scan_devtree_cpu(*core);
1537        }
1538        scan_devtree_memory(*core);
1539        scan_devtree_memory_ibm(*core);
1540      }
1541    }
1542  
1543    return true;
1544  }
1545  
1546  void add_device_tree_info(hwNode & n, string sysfs_path)
1547  {
1548    string of_node = sysfs_path + "/of_node";
1549    string val;
1550  
1551    if (!exists(of_node))
1552      return;
1553  
1554    /* read location / slot data */
1555    val = hw::strip(get_string(of_node + "/ibm,loc-code", ""));
1556    if (val == "")
1557      val = hw::strip(get_string(of_node + "/ibm,slot-location-code", ""));
1558    if (val == "")
1559      val = hw::strip(get_string(of_node + "/ibm,slot-label"));
1560  
1561    if (val != "")
1562      n.setSlot(val);
1563  }