Back to index

lshw  02.16
smp.cc
Go to the documentation of this file.
00001 /*
00002  * smp.cc
00003  *
00004  *
00005  */
00006 
00007 #include "version.h"
00008 #include "smp.h"
00009 #include <sys/types.h>
00010 #include <sys/stat.h>
00011 #include <fcntl.h>
00012 #include <string.h>
00013 #include <unistd.h>
00014 
00015 #include "osutils.h"
00016 
00017 __ID("@(#) $Id: smp.cc 1897 2007-10-02 13:29:47Z lyonel $");
00018 
00019 #if defined(__i386__)
00020 
00021 typedef unsigned long vm_offset_t;
00022 
00023 /* MP Floating Pointer Structure */
00024 typedef struct MPFPS {
00025         char    signature[4];
00026         uint32_t             pap;
00027         uint8_t      length;
00028         uint8_t      spec_rev;
00029         uint8_t      checksum;
00030         uint8_t      mpfb1;
00031         uint8_t      mpfb2;
00032         uint8_t      mpfb3;
00033         uint8_t      mpfb4;
00034         uint8_t      mpfb5;
00035 } mpfps_t;
00036 
00037 /* MP Configuration Table Header */
00038 typedef struct MPCTH {
00039     char        signature[ 4 ];
00040     u_short     base_table_length;
00041     uint8_t      spec_rev;
00042     uint8_t      checksum;
00043     char        oem_id[ 8 ];
00044     char        product_id[ 12 ];
00045     void*       oem_table_pointer;
00046     u_short     oem_table_size;
00047     u_short     entry_count;
00048     void*       apic_address;
00049     u_short     extended_table_length;
00050     uint8_t      extended_table_checksum;
00051     uint8_t      reserved;
00052 } mpcth_t;
00053 
00054 typedef struct PROCENTRY {
00055     uint8_t      type;
00056     uint8_t      apicID;
00057     uint8_t      apicVersion;
00058     uint8_t      cpuFlags;
00059     uint32_t      cpuSignature;
00060     uint32_t      featureFlags;
00061     uint32_t      reserved1;
00062     uint32_t      reserved2;
00063 } ProcEntry;
00064 
00065 /* EBDA is @ 40:0e in real-mode terms */
00066 #define EBDA_POINTER                    0x040e            /* location of EBDA pointer */
00067 
00068 /* CMOS 'top of mem' is @ 40:13 in real-mode terms */
00069 #define TOPOFMEM_POINTER                0x0413            /* BIOS: base memory size */
00070 
00071 #define DEFAULT_TOPOFMEM                0xa0000
00072 
00073 #define BIOS_BASE                          0xf0000
00074 #define BIOS_BASE2                        0xe0000
00075 #define BIOS_SIZE                          0x10000
00076 #define ONE_KBYTE                          1024
00077 
00078 #define GROPE_AREA1                      0x80000
00079 #define GROPE_AREA2                      0x90000
00080 #define GROPE_SIZE                        0x10000
00081 
00082 #define PROCENTRY_FLAG_EN          0x01
00083 #define PROCENTRY_FLAG_BP          0x02
00084 #define IOAPICENTRY_FLAG_EN      0x01
00085 
00086 #define MAXPNSTR                                132
00087 
00088 static int pfd = -1; 
00089 
00090 static bool seekEntry(vm_offset_t addr)
00091 {
00092   return(lseek(pfd, (off_t)addr, SEEK_SET) >= 0);
00093 }
00094 
00095 static bool readEntry(void* entry, int size)
00096 {
00097   return (read(pfd, entry, size) == size);
00098 }
00099 
00100 static int readType( void )
00101 {
00102     uint8_t      type;
00103 
00104     if ( read( pfd, &type, sizeof( type ) ) == sizeof( type ) )
00105       lseek( pfd, -sizeof( type ), SEEK_CUR );
00106 
00107     return (int)type;
00108 }
00109 
00110 
00111 /*
00112  * set PHYSICAL address of MP floating pointer structure
00113  */
00114 #define NEXT(X)         ((X) += 4)
00115 static int apic_probe(vm_offset_t* paddr)
00116 {
00117         unsigned int x;
00118         uint16_t segment;
00119         vm_offset_t target;
00120         unsigned int buffer[BIOS_SIZE];
00121         const char MP_SIG[]="_MP_";
00122 
00123         /* search Extended Bios Data Area, if present */
00124         seekEntry((vm_offset_t)EBDA_POINTER);
00125         readEntry(&segment, 2);
00126         if (segment) {                          /* search EBDA */
00127                 target = (vm_offset_t)segment << 4;
00128                 seekEntry(target);
00129                 readEntry(buffer, ONE_KBYTE);
00130 
00131                 for (x = 0; x < ONE_KBYTE / 4; NEXT(x)) {
00132                         if (!strncmp((char *)&buffer[x], MP_SIG, 4)) {
00133                                 *paddr = (x*4) + target;
00134                                 return 1;
00135                         }
00136                 }
00137         }
00138 
00139         /* read CMOS for real top of mem */
00140         seekEntry((vm_offset_t)TOPOFMEM_POINTER);
00141         readEntry(&segment, 2);
00142         --segment;                                                /* less ONE_KBYTE */
00143         target = segment * 1024;
00144         seekEntry(target);
00145         readEntry(buffer, ONE_KBYTE);
00146 
00147         for (x = 0; x < ONE_KBYTE/4; NEXT(x)) {
00148                 if (!strncmp((char *)&buffer[x], MP_SIG, 4)) {
00149                         *paddr = (x*4) + target;
00150                         return 2;
00151                 }
00152         }
00153         /* we don't necessarily believe CMOS, check base of the last 1K of 640K */
00154         if (target != (DEFAULT_TOPOFMEM - 1024)) {
00155                 target = (DEFAULT_TOPOFMEM - 1024);
00156                 seekEntry(target);
00157                 readEntry(buffer, ONE_KBYTE);
00158 
00159                 for (x = 0; x < ONE_KBYTE/4; NEXT(x)) {
00160                         if (!strncmp((char *)&buffer[x], MP_SIG, 4)) {
00161                                 *paddr = (x*4) + target;
00162                                 return 3;
00163                         }
00164                 }
00165         }
00166 
00167         /* search the BIOS */
00168         seekEntry(BIOS_BASE);
00169         readEntry(buffer, BIOS_SIZE);
00170 
00171         for (x = 0; x < BIOS_SIZE/4; NEXT(x)) {
00172                 if (!strncmp((char *)&buffer[x], MP_SIG, 4)) {
00173                         *paddr = (x*4) + BIOS_BASE;
00174                         return 4;
00175                 }
00176         }
00177 
00178         /* search the extended BIOS */
00179         seekEntry(BIOS_BASE2);
00180         readEntry(buffer, BIOS_SIZE);
00181 
00182         for (x = 0; x < BIOS_SIZE/4; NEXT(x)) {
00183                 if (!strncmp((char *)&buffer[x], MP_SIG, 4)) {
00184                         *paddr = (x*4) + BIOS_BASE2;
00185                         return 4;
00186                 }
00187         }
00188 
00189         /* search additional memory */
00190         target = GROPE_AREA1;
00191         seekEntry(target);
00192         readEntry(buffer, GROPE_SIZE);
00193 
00194         for (x = 0; x < GROPE_SIZE/4; NEXT(x)) {
00195                 if (!strncmp((char *)&buffer[x], MP_SIG, 4)) {
00196                         *paddr = (x*4) + GROPE_AREA1;
00197                         return 5;
00198                 }
00199         }
00200 
00201         target = GROPE_AREA2;
00202         seekEntry(target);
00203         readEntry(buffer, GROPE_SIZE);
00204 
00205         for (x = 0; x < GROPE_SIZE/4; NEXT(x)) {
00206                 if (!strncmp((char *)&buffer[x], MP_SIG, 4)) {
00207                         *paddr = (x*4) + GROPE_AREA2;
00208                         return 6;
00209                 }
00210         }
00211 
00212         *paddr = (vm_offset_t)0;
00213         return 0;
00214 }
00215 
00216 static hwNode *addCPU(hwNode & n, int i, ProcEntry entry)
00217 {
00218   hwNode *cpu = NULL;
00219 
00220   if(!(entry.cpuFlags & PROCENTRY_FLAG_EN))
00221     return NULL;
00222 
00223   cpu = n.findChildByBusInfo("cpu@"+tostring(i));
00224   if(!cpu)
00225   {
00226     hwNode *core = n.getChild("core");
00227 
00228     if (!core)
00229     {
00230       n.addChild(hwNode("core", hw::bus));
00231       core = n.getChild("core");
00232     }
00233     if(!core)
00234       cpu = n.addChild(hwNode("cpu", hw::processor));
00235     else
00236       cpu = core->addChild(hwNode("cpu", hw::processor));
00237 
00238     if(cpu)
00239       cpu->setBusInfo("cpu@"+tostring(i));
00240   }
00241 
00242   if(cpu)
00243   {
00244     cpu->setVersion(tostring((entry.cpuSignature >> 8) & 0x0f) + "." + tostring((entry.cpuSignature >> 4) & 0x0f) + "." + tostring(entry.cpuSignature & 0x0f));
00245 
00246     if(entry.cpuFlags & PROCENTRY_FLAG_EN)
00247       cpu->enable();
00248     if(entry.cpuFlags & PROCENTRY_FLAG_BP)
00249       cpu->addCapability("boot", "boot processor");
00250   }
00251 
00252   return cpu;
00253 }
00254 
00255 static void MPConfigDefault( hwNode & n, int featureByte )
00256 {
00257     switch ( featureByte ) {
00258     case 1:
00259         n.addCapability("ISA");
00260         n.addCapability("APIC:82489DX");
00261         break;
00262     case 2:
00263     case 3:
00264         n.addCapability("EISA");
00265         n.addCapability("APIC:82489DX");
00266         break;
00267     case 4:
00268         n.addCapability("MCA");
00269         n.addCapability("APIC:82489DX");
00270         break;
00271     case 5:
00272         n.addCapability("ISA");
00273         n.addCapability("PCI");
00274         n.addCapability("APIC:integrated");
00275         break;
00276     case 6:
00277         n.addCapability("EISA");
00278         n.addCapability("PCI");
00279         n.addCapability("APIC:integrated");
00280         break;
00281     case 7:
00282         n.addCapability("MCA");
00283         n.addCapability("PCI");
00284         n.addCapability("APIC:integrated");
00285         break;
00286     }
00287 
00288     switch ( featureByte ) {
00289     case 1:
00290     case 2:
00291     case 3:
00292     case 4:
00293         n.setConfig("bus", 1);
00294         break;
00295     case 5:
00296     case 6:
00297     case 7:
00298         n.setConfig("bus", 2);
00299         break;
00300     }
00301 
00302     n.setConfig("cpus", 2);
00303 }
00304 
00305 static void MPConfigTableHeader( hwNode & n, uint32_t pap )
00306 {
00307     mpcth_t   cth;
00308     int              c;
00309     int              entrytype;
00310     int ncpu = 0;
00311     int nbus = 0;
00312     int napic = 0;
00313     int nintr = 0;
00314     ProcEntry   entry;
00315 
00316     if ( !pap )
00317       return;
00318 
00319     /* read in cth structure */
00320     seekEntry( pap );
00321     readEntry( &cth, sizeof( cth ) );
00322 
00323 
00324   if(string(cth.signature, 4) != "PCMP")
00325     return;
00326 
00327   if(n.getVendor() == "")
00328     n.setVendor(hw::strip(string(cth.oem_id, 8)));
00329   if(n.getProduct() == "")
00330     n.setProduct(hw::strip(string(cth.product_id, 12)));
00331 
00332   n.addCapability("smp-1." + tostring(cth.spec_rev), "SMP specification v1."+tostring(cth.spec_rev));
00333 
00334     for (c = cth.entry_count; c; c--) {
00335        entrytype = readType();
00336        switch (entrytype) {
00337        case 0:
00338            readEntry( &entry, sizeof( entry ) );
00339            if(addCPU(n, ncpu, entry))
00340               ncpu++;
00341            break;
00342 
00343        case 1:
00344            //busEntry();
00345            nbus++;
00346            break;
00347 
00348        case 2:
00349            //ioApicEntry();
00350            napic++;
00351            break;
00352 
00353        case 3:
00354            //intEntry();
00355            nintr++;
00356            break;
00357 
00358        case 4:
00359            //intEntry();
00360            nintr++;
00361            break;
00362        }
00363     }
00364 
00365     n.setConfig("cpus", ncpu);
00366 }
00367 
00368 bool issmp(hwNode & n)
00369 {
00370   vm_offset_t paddr;
00371   mpfps_t mpfps;
00372 
00373   if((pfd = open("/dev/mem", O_RDONLY)) < 0)
00374     return false;
00375 
00376   if (apic_probe(&paddr) <= 0)
00377     return false;
00378 
00379   /* read in mpfps structure*/
00380   seekEntry(paddr);
00381   readEntry(&mpfps, sizeof(mpfps_t));
00382 
00383   if(string(mpfps.signature, 4) != "_MP_")
00384     return false;
00385 
00386   /* check whether a pre-defined MP config table exists */
00387   if (mpfps.mpfb1)
00388     MPConfigDefault(n, mpfps.mpfb1);
00389   else
00390   if(mpfps.pap)
00391     MPConfigTableHeader(n, mpfps.pap);
00392 
00393   close(pfd);
00394   return true;
00395 }
00396 
00397 #else
00398 
00399 bool issmp(hwNode & n)
00400 {
00401   return false;
00402 }
00403 
00404 #endif
00405 
00406 bool scan_smp(hwNode & n)
00407 {
00408   if(issmp(n))
00409   {
00410     n.addCapability("smp", "Symmetric Multi-Processing");
00411     return true;
00412   }
00413   else
00414     return false;
00415 }