Back to index

lshw  02.16
device-tree.cc
Go to the documentation of this file.
00001 /*
00002  * device-tree.cc
00003  *
00004  * This module parses the OpenFirmware device tree (published under /proc
00005  * by the kernel).
00006  *
00007  *
00008  *
00009  *
00010  */
00011 
00012 #include "version.h"
00013 #include "device-tree.h"
00014 #include "osutils.h"
00015 #include <sys/types.h>
00016 #include <sys/stat.h>
00017 #include <fcntl.h>
00018 #include <stdlib.h>
00019 #include <stdio.h>
00020 #include <string.h>
00021 #include <unistd.h>
00022 #include <dirent.h>
00023 
00024 __ID("@(#) $Id: device-tree.cc 2433 2012-01-10 22:01:30Z lyonel $");
00025 
00026 #define DIMMINFOSIZE 0x80
00027 typedef __uint8_t dimminfo_buf[DIMMINFOSIZE];
00028 
00029 struct dimminfo
00030 {
00031   __uint8_t version3;
00032   char serial[16];
00033   __uint16_t version1, version2;
00034 };
00035 
00036 #define DEVICETREE "/proc/device-tree"
00037 
00038 static unsigned long get_long(const string & path)
00039 {
00040   unsigned long result = 0;
00041   int fd = open(path.c_str(), O_RDONLY);
00042 
00043   if (fd >= 0)
00044   {
00045     if(read(fd, &result, sizeof(result)) != sizeof(result))
00046       result = 0;
00047 
00048     close(fd);
00049   }
00050 
00051   return result;
00052 }
00053 
00054 
00055 static vector < string > get_strings(const string & path,
00056 unsigned int offset = 0)
00057 {
00058   vector < string > result;
00059   char *strings = NULL;
00060   char *curstring = NULL;
00061 
00062   int fd = open(path.c_str(), O_RDONLY);
00063 
00064   if (fd >= 0)
00065   {
00066     struct stat buf;
00067 
00068     if (fstat(fd, &buf) == 0)
00069     {
00070       strings = (char *) malloc(buf.st_size + 1);
00071       if (strings)
00072       {
00073         memset(strings, 0, buf.st_size + 1);
00074         if(read(fd, strings, buf.st_size) == buf.st_size)
00075         {
00076           curstring = strings + offset;
00077 
00078           while (strlen(curstring))
00079           {
00080             result.push_back(string(curstring));
00081             curstring += strlen(curstring) + 1;
00082           }
00083         }
00084 
00085         free(strings);
00086       }
00087     }
00088 
00089     close(fd);
00090   }
00091 
00092   return result;
00093 }
00094 
00095 
00096 static void scan_devtree_root(hwNode & core)
00097 {
00098   core.setClock(get_long(DEVICETREE "/clock-frequency"));
00099 }
00100 
00101 
00102 static void scan_devtree_bootrom(hwNode & core)
00103 {
00104   if (exists(DEVICETREE "/rom/boot-rom"))
00105   {
00106     hwNode bootrom("firmware",
00107       hw::memory);
00108     string upgrade = "";
00109     unsigned long base = 0;
00110     unsigned long size = 0;
00111 
00112     bootrom.setProduct(get_string(DEVICETREE "/rom/boot-rom/model"));
00113     bootrom.setDescription("BootROM");
00114     bootrom.
00115       setVersion(get_string(DEVICETREE "/rom/boot-rom/BootROM-version"));
00116 
00117     if ((upgrade =
00118       get_string(DEVICETREE "/rom/boot-rom/write-characteristic")) != "")
00119     {
00120       bootrom.addCapability("upgrade");
00121       bootrom.addCapability(upgrade);
00122     }
00123 
00124     int fd = open(DEVICETREE "/rom/boot-rom/reg", O_RDONLY);
00125     if (fd >= 0)
00126     {
00127       if(read(fd, &base, sizeof(base)) == sizeof(base))
00128       {
00129         if(read(fd, &size, sizeof(size)) != sizeof(size))
00130           size = 0;
00131       }
00132       else
00133         base = 0;
00134 
00135       bootrom.setPhysId(base);
00136       bootrom.setSize(size);
00137       close(fd);
00138     }
00139 
00140     bootrom.claim();
00141 //bootrom.setLogicalName(DEVICETREE "/rom");
00142     core.addChild(bootrom);
00143   }
00144 
00145   if (exists(DEVICETREE "/openprom"))
00146   {
00147     hwNode openprom("firmware",
00148       hw::memory);
00149 
00150     openprom.setProduct(get_string(DEVICETREE "/openprom/model"));
00151 
00152     if (exists(DEVICETREE "/openprom/supports-bootinfo"))
00153       openprom.addCapability("bootinfo");
00154 
00155 //openprom.setLogicalName(DEVICETREE "/openprom");
00156     openprom.setLogicalName(DEVICETREE);
00157     openprom.claim();
00158     core.addChild(openprom);
00159   }
00160 }
00161 
00162 
00163 static string cpubusinfo(int cpu)
00164 {
00165   char buffer[20];
00166 
00167   snprintf(buffer, sizeof(buffer), "cpu@%d", cpu);
00168 
00169   return string(buffer);
00170 }
00171 
00172 
00173 static void scan_devtree_cpu(hwNode & core)
00174 {
00175   struct dirent **namelist;
00176   int n;
00177   int currentcpu=0;
00178 
00179   pushd(DEVICETREE "/cpus");
00180   n = scandir(".", &namelist, selectdir, alphasort);
00181   popd();
00182   if (n < 0)
00183     return;
00184   else
00185   {
00186     for (int i = 0; i < n; i++)
00187     {
00188       string basepath =
00189         string(DEVICETREE "/cpus/") + string(namelist[i]->d_name);
00190       unsigned long version = 0;
00191       hwNode cpu("cpu",
00192         hw::processor);
00193       struct dirent **cachelist;
00194       int ncache;
00195 
00196       if (hw::strip(get_string(basepath + "/device_type")) != "cpu")
00197         break;                                    // oops, not a CPU!
00198 
00199       cpu.setProduct(get_string(basepath + "/name"));
00200       cpu.setDescription("CPU");
00201       cpu.claim();
00202       cpu.setBusInfo(cpubusinfo(currentcpu++));
00203       cpu.setSize(get_long(basepath + "/clock-frequency"));
00204       cpu.setClock(get_long(basepath + "/bus-frequency"));
00205       if (exists(basepath + "/altivec"))
00206         cpu.addCapability("altivec");
00207 
00208       version = get_long(basepath + "/cpu-version");
00209       if (version != 0)
00210       {
00211         int minor = version & 0x00ff;
00212         int major = (version & 0xff00) >> 8;
00213         char buffer[20];
00214 
00215         snprintf(buffer, sizeof(buffer), "%lx.%d.%d",
00216           (version & 0xffff0000) >> 16, major, minor);
00217         cpu.setVersion(buffer);
00218 
00219       }
00220       if (hw::strip(get_string(basepath + "/state")) != "running")
00221         cpu.disable();
00222 
00223       if (exists(basepath + "/performance-monitor"))
00224         cpu.addCapability("performance-monitor");
00225 
00226       if (exists(basepath + "/d-cache-size"))
00227       {
00228         hwNode cache("cache",
00229           hw::memory);
00230 
00231         cache.setDescription("L1 Cache");
00232         cache.setSize(get_long(basepath + "/d-cache-size"));
00233         if (cache.getSize() > 0)
00234           cpu.addChild(cache);
00235       }
00236 
00237       pushd(basepath);
00238       ncache = scandir(".", &cachelist, selectdir, alphasort);
00239       popd();
00240       if (ncache > 0)
00241       {
00242         for (int j = 0; j < ncache; j++)
00243         {
00244           hwNode cache("cache",
00245             hw::memory);
00246           string cachebase = basepath + "/" + cachelist[j]->d_name;
00247 
00248           if (hw::strip(get_string(cachebase + "/device_type")) != "cache" &&
00249             hw::strip(get_string(cachebase + "/device_type")) != "l2-cache")
00250             break;                                // oops, not a cache!
00251 
00252           cache.setDescription("L2 Cache");
00253           cache.setSize(get_long(cachebase + "/d-cache-size"));
00254           cache.setClock(get_long(cachebase + "/clock-frequency"));
00255 
00256           if (exists(cachebase + "/cache-unified"))
00257             cache.setDescription(cache.getDescription() + " (unified)");
00258           else
00259           {
00260             hwNode icache = cache;
00261             cache.setDescription(cache.getDescription() + " (data)");
00262             icache.setDescription(icache.getDescription() + " (instruction)");
00263             icache.setSize(get_long(cachebase + "/i-cache-size"));
00264 
00265             if (icache.getSize() > 0)
00266               cpu.addChild(icache);
00267           }
00268 
00269           if (cache.getSize() > 0)
00270             cpu.addChild(cache);
00271 
00272           free(cachelist[j]);
00273         }
00274         free(cachelist);
00275       }
00276 
00277       core.addChild(cpu);
00278 
00279       free(namelist[i]);
00280     }
00281     free(namelist);
00282   }
00283 }
00284 
00285 
00286 static void scan_devtree_memory(hwNode & core)
00287 {
00288   int currentmc = -1;                             // current memory controller
00289   hwNode *memory = core.getChild("memory");
00290 
00291   while (true)
00292   {
00293     char buffer[10];
00294     string mcbase;
00295     vector < string > slotnames;
00296     vector < string > dimmtypes;
00297     vector < string > dimmspeeds;
00298     string reg;
00299     string dimminfo;
00300 
00301     snprintf(buffer, sizeof(buffer), "%d", currentmc);
00302     if (currentmc >= 0)
00303       mcbase = string(DEVICETREE "/memory@") + string(buffer);
00304     else
00305       mcbase = string(DEVICETREE "/memory");
00306     slotnames =
00307       get_strings(mcbase + string("/slot-names"), sizeof(unsigned long));
00308     dimmtypes = get_strings(mcbase + string("/dimm-types"));
00309     dimmspeeds = get_strings(mcbase + string("/dimm-speeds"));
00310     reg = mcbase + string("/reg");
00311     dimminfo = mcbase + string("/dimm-info");
00312 
00313     if (slotnames.size() == 0)
00314     {
00315       if (currentmc < 0)
00316       {
00317         currentmc++;
00318         continue;
00319       }
00320       else
00321         break;
00322     }
00323 
00324     if (!memory || (currentmc > 0))
00325     {
00326       memory = core.addChild(hwNode("memory", hw::memory));
00327     }
00328 
00329     if (memory)
00330     {
00331       int fd = open(dimminfo.c_str(), O_RDONLY);
00332       int fd2 = open(reg.c_str(), O_RDONLY);
00333 
00334       if (fd2 >= 0)
00335       {
00336         for (unsigned int i = 0; i < slotnames.size(); i++)
00337         {
00338           unsigned long base = 0;
00339           unsigned long size = 0;
00340           hwNode bank("bank",
00341             hw::memory);
00342 
00343           if(read(fd2, &base, sizeof(base)) == sizeof(base))
00344           {
00345             if(read(fd2, &size, sizeof(size)) != sizeof(size))
00346               size = 0;
00347           }
00348           else
00349             base = 0;
00350 
00351           if (fd >= 0)
00352           {
00353             dimminfo_buf dimminfo;
00354 
00355             if (read(fd, &dimminfo, sizeof(dimminfo)) > 0)
00356             {
00357               if (size > 0)
00358               {
00359                 char dimmversion[20];
00360                 snprintf(dimmversion, sizeof(dimmversion),
00361                   "%02X%02X,%02X %02X,%02X", dimminfo[0x5b],
00362                   dimminfo[0x5c], dimminfo[0x5d], dimminfo[0x5e],
00363                   dimminfo[0x48]);
00364                 bank.setSerial(string((char *) &dimminfo + 0x49, 18));
00365                 bank.setVersion(dimmversion);
00366               }
00367             }
00368           }
00369 
00370           if(size>0)
00371             bank.addHint("icon", string("memory"));
00372           bank.setDescription("Memory bank");
00373           bank.setSlot(slotnames[i]);
00374 //bank.setPhysId(base);
00375           if (i < dimmtypes.size())
00376             bank.setDescription(dimmtypes[i]);
00377           if (i < dimmspeeds.size())
00378             bank.setProduct(hw::strip(dimmspeeds[i]));
00379           bank.setSize(size);
00380           memory->addChild(bank);
00381         }
00382         close(fd2);
00383       }
00384 
00385       if (fd >= 0)
00386         close(fd);
00387       currentmc++;
00388     }
00389     else
00390       break;
00391 
00392     memory = NULL;
00393   }
00394 }
00395 
00396 
00397 struct pmac_mb_def
00398 {
00399   const char *model;
00400   const char *modelname;
00401   const char *icon;
00402 };
00403 
00404 static struct pmac_mb_def pmac_mb_defs[] =
00405 {
00406 /*
00407  * Warning: ordering is important as some models may claim
00408  * * beeing compatible with several types
00409  */
00410   {"AAPL,8500", "PowerMac 8500/8600", ""},
00411   {"AAPL,9500", "PowerMac 9500/9600", ""},
00412   {"AAPL,7200", "PowerMac 7200", ""},
00413   {"AAPL,7300", "PowerMac 7200/7300", ""},
00414   {"AAPL,7500", "PowerMac 7500", ""},
00415   {"AAPL,ShinerESB", "Apple Network Server", ""},
00416   {"AAPL,e407", "Alchemy", ""},
00417   {"AAPL,e411", "Gazelle", ""},
00418   {"AAPL,3400/2400", "PowerBook 3400", "laptop"},
00419   {"AAPL,3500", "PowerBook 3500", "laptop"},
00420   {"AAPL,Gossamer", "PowerMac G3 (Gossamer)", ""},
00421   {"AAPL,PowerMac G3", "PowerMac G3 (Silk)", ""},
00422   {"AAPL,PowerBook1998", "PowerBook Wallstreet", "laptop"},
00423   {"iMac,1", "iMac (first generation)", ""},
00424   {"PowerMac1,1", "Blue & White G3", "powermac"},
00425   {"PowerMac1,2", "PowerMac G4 PCI Graphics", "powermac"},
00426   {"PowerMac2,1", "iMac FireWire", ""},
00427   {"PowerMac2,2", "iMac FireWire", ""},
00428   {"PowerMac3,1", "PowerMac G4 AGP Graphics", "powermac"},
00429   {"PowerMac3,2", "PowerMac G4 AGP Graphics", "powermac"},
00430   {"PowerMac3,3", "PowerMac G4 AGP Graphics", "powermac"},
00431   {"PowerMac3,4", "PowerMac G4 QuickSilver", "powermac"},
00432   {"PowerMac3,5", "PowerMac G4 QuickSilver", "powermac"},
00433   {"PowerMac3,6", "PowerMac G4 Windtunnel", "powermac"},
00434   {"PowerMac4,1", "iMac \"Flower Power\"", ""},
00435   {"PowerMac4,2", "iMac LCD 15\"", ""},
00436   {"PowerMac4,4", "eMac", ""},
00437   {"PowerMac4,5", "iMac LCD 17\"", ""},
00438   {"PowerMac5,1", "PowerMac G4 Cube", ""},
00439   {"PowerMac5,2", "PowerMac G4 Cube", ""},
00440   {"PowerMac6,1", "iMac LCD 17\"", ""},
00441   {"PowerMac7,2", "PowerMac G5", "powermacg5"},
00442   {"PowerMac7,3", "PowerMac G5", "powermacg5"},
00443   {"PowerMac8,1", "iMac G5", ""},
00444   {"PowerMac8,2", "iMac G5", ""},
00445   {"PowerMac10,1", "Mac mini", "mini"},
00446   {"PowerMac10,2", "Mac mini", "mini"},
00447   {"PowerMac11,2", "PowerMac G5", "powermacg5"},
00448   {"PowerMac12,1", "iMac G5", ""},
00449   {"PowerBook1,1", "PowerBook 101 (Lombard)", "laptop"},
00450   {"PowerBook2,1", "iBook (first generation)", "laptop"},
00451   {"PowerBook2,2", "iBook FireWire", "laptop"},
00452   {"PowerBook3,1", "PowerBook Pismo", "laptop"},
00453   {"PowerBook3,2", "PowerBook Titanium", "laptop"},
00454   {"PowerBook3,3", "PowerBook Titanium w/ Gigabit Ethernet", "laptop"},
00455   {"PowerBook3,4", "PowerBook Titanium w/ DVI", "laptop"},
00456   {"PowerBook3,5", "PowerBook Titanium 1GHz", "laptop"},
00457   {"PowerBook4,1", "iBook 12\" (May 2001)", "laptop"},
00458   {"PowerBook4,2", "iBook 2", "laptop"},
00459   {"PowerBook4,3", "iBook 2 rev. 2 (Nov 2002)", "laptop"},
00460   {"PowerBook4,4", "iBook 2 rev. 2", "laptop"},
00461   {"PowerBook5,1", "PowerBook G4 17\"", "laptop"},
00462   {"PowerBook5,2", "PowerBook G4 15\"", "laptop"},
00463   {"PowerBook5,3", "PowerBook G4 17\" 1.33GHz", "laptop"},
00464   {"PowerBook5,4", "PowerBook G4 15\" 1.5/1.33GHz", "laptop"},
00465   {"PowerBook5,5", "PowerBook G4 17\" 1.5GHz", "laptop"},
00466   {"PowerBook5,6", "PowerBook G4 15\" 1.67/1.5GHz", "laptop"},
00467   {"PowerBook5,7", "PowerBook G4 17\" 1.67GHz", "laptop"},
00468   {"PowerBook5,8", "PowerBook G4 15\" double layer SD", "laptop"},
00469   {"PowerBook5,9", "PowerBook G4 17\" double layer SD", "laptop"},
00470   {"PowerBook6,1", "PowerBook G4 12\"", "laptop"},
00471   {"PowerBook6,2", "PowerBook G4 12\" DVI", "laptop"},
00472   {"PowerBook6,3", "iBook G4", "laptop"},
00473   {"PowerBook6,4", "PowerBook G4 12\"", "laptop"},
00474   {"PowerBook6,5", "iBook G4", "laptop"},
00475   {"PowerBook6,7", "iBook G4", "laptop"},
00476   {"PowerBook6,8", "PowerBook G4 12\" 1.5GHz", "laptop"},
00477   {"RackMac1,1", "XServe", ""},
00478   {"RackMac1,2", "XServe rev. 2", ""},
00479   {"RackMac3,1", "XServe G5", ""},
00480 };
00481 
00482 static bool get_apple_model(hwNode & n)
00483 {
00484   string model = n.getProduct();
00485   if (model == "")
00486     return false;
00487 
00488   for (unsigned int i = 0; i < sizeof(pmac_mb_defs) / sizeof(pmac_mb_def);
00489     i++)
00490   if (model == pmac_mb_defs[i].model)
00491   {
00492     n.setProduct(pmac_mb_defs[i].modelname);
00493     n.addHint("icon", string(pmac_mb_defs[i].icon));
00494   }
00495 
00496   return false;
00497 }
00498 
00499 
00500 static void fix_serial_number(hwNode & n)
00501 {
00502   string serial = n.getSerial();
00503 
00504   if(serial.find('\0')==string::npos) return;     // nothing to do
00505 
00506   n.setSerial(hw::strip(serial.substr(13)) + hw::strip(serial.substr(0,13)));
00507 }
00508 
00509 
00510 bool scan_device_tree(hwNode & n)
00511 {
00512   hwNode *core = n.getChild("core");
00513 
00514   if (!exists(DEVICETREE))
00515     return false;
00516 
00517   if (!core)
00518   {
00519     n.addChild(hwNode("core", hw::bus));
00520     core = n.getChild("core");
00521   }
00522 
00523   n.setProduct(get_string(DEVICETREE "/model"));
00524   n.addHint("icon", string("motherboard"));
00525 
00526   n.setSerial(get_string(DEVICETREE "/serial-number"));
00527   if (n.getSerial() == "")
00528     n.setSerial(get_string(DEVICETREE "/system-id"));
00529   fix_serial_number(n);
00530 
00531   n.setVendor(get_string(DEVICETREE "/copyright"));
00532 
00533   get_apple_model(n);
00534 
00535   if (core)
00536   {
00537     core->addHint("icon", string("board"));
00538     scan_devtree_root(*core);
00539     scan_devtree_bootrom(*core);
00540     scan_devtree_memory(*core);
00541     scan_devtree_cpu(*core);
00542     core->addCapability(get_string(DEVICETREE "/compatible"));
00543   }
00544 
00545   return true;
00546 }