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 }