Novell Home

Leveraging the System Management BIOS Information in NetWare-Based Applications

Articles and Tips: article

K.S. Venkatram
Senior Software Engineer
Novell, Inc.
kvenkatram@novell.com

01 Jul 2002


Thanks to Dana Henriksen, Distinguished Engineer at Novell, for providing technical insight and valuable help in making this feature possible.

This AppNote discusses the leveraging of System Management BIOS information through a NetWare Loadable Module (NLM) program. It outlines the basic procedures necessary to access this information when developing an application and provides relevant code samples. It then showcases the Inventory feature of the ZENworks for Servers 3 product to demonstrate how improvements were achieved in the system component inventory reported on NetWare. It also includes a brief personal reflection on the future of management initiatives on NetWare.


Topics

application development, NetWare APIs, SMBIOS

Products

ZENworks for Servers 3

Audience

network application developers

Level

advanced

Prerequisite Skills

familiarity with network management concepts and NLM programming

Operating System

NetWare

Tools

NetWare SDK

Sample Code

yes

Introduction

In today's software marketplace where management and return on investment (ROI) are important factors to address to win market share, it is vital to tap all possible sources of management information. Most management applications written for the NetWare environment have used the NetWare SDK and Simple Network Management Protocol (SNMP) to achieve the desired results. But another largely unused resource exists in the form of the System Management information found in the BIOS of many computers. The purpose of this AppNote is to make NetWare application developers aware of this resource and show how it can be leveraged to create a whole new breed of management applications.

Using the System Management BIOS (SMBIOS), you can obtain the types, capabilities, operational status, installation date, and other information about system components in a computer. This information can then be used in various ways to help manage computers in an enterprise. The procedures to be followed in accessing the management information from an application are documented in the System Management BIOS Reference Specification.

Leveraging the System Management BIOS

The System Management BIOS v2.3 Reference Specification describe the steps to necessary to probe a computer's BIOS for SMBIOS headers and structures. The procedures outlined in this AppNote are based on the table-based method of accessing BIOS information. (The table convention is supported by SMBIOS v2.2 and later implementations.)

The table convention provides a searchable entry point structure that contains a pointer to the packed SMBIOS structures residing somewhere in 32-bit physical address space. An application can locate the SMBIOS Entry Point structure by searching for an anchor-string "_SM_, specified as four ASCII characters (5F 53 4D 5F)" on paragraph (16-byte) boundaries within the physical memory address range 000F0000h to 000FFFFFh.

Thus, to verify whether a particular machine's BIOS contains SMBIOS information, it is first necessary to check for the SMBIOS structure entry point. The NetWare operating system supports probing the F0000h-FFFFFh memory range by an NLM through the use of 32-bit linear addressing and the recommended ReadPhysicalMemory C-API in the NetWare SDK. (Members of Novell's DeveloperNet program can download the NetWare SDK components free from http://developer.novell.com/ndk. If you are not yet a DeveloperNet member, you can sign up for a free electronic subscription at the same Web site.)

After verifying that the BIOS on the machine contains SMBIOS structures, an application can-with the help of the retrieved pointer-use the memory contents to step through the various SMBIOS structures and retrieve the information contained in the structure fields. To retrieve structure fields that are strings, an application must follow the approach outlined in the specification. Examples are given later in this AppNote; but first, let's find out more about the ReadPhysicalMemory API.

About the ReadPhysicalMemory API

This API does not use the real mode workspace normally expected to read physical memory on a machine. It simply uses the page tables to map into the desired memory location and then copies the source data into the application's target buffer. The API transparently handles any paging problems in reading memory.

The function signature for this API is:

LONG ReadPhysicalMemory ( BYTE *source, BYTE *destination, LONG numberOfUnits, LONG unitLength )

The function parameters are defined as follows:

  • BYTE *source - The source 32-bit physical address. If you need to read physical memory F000:0000, it should be input as 0x000F0000.

  • BYTE *target - The logical address of the application buffer into which you want to read physical memory contents. The application discussed in this AppNote passes the logical address of both local and global application buffers in its usage.

  • LONG numberOfUnits - The number of memory units to be read. This is typically the size of the SMBIOS headers or of the global data buffer into which you want to read all SMBIOS structures after retrieving the memory pointer from the entry point structure.

  • LONG unitLength - The length of the memory units (1=Byte, 2=WORD, 4=DWORD). In our example application, the length value is 1.

There is one completion code (displayed in EAX) for this function: 00000000h, which means the routine failed because of a bad parameter.

Code Listings

This section contains two sample code listings that show how an NLM running on NetWare can perform crucial tasks relating to the SMBIOS. Listing 1 shows the task of identifying whether the BIOS on the host machine conforms to the SMBIOS Reference Specification. Listing 2 shows how to retrieve some of the SMBIOS structure contents once the BIOS is found to be conformant.

Listing 1: Probing Memory for the SMBIOS Structure Entry Point

#define FALSE 0

#define TRUE  1

#define DMI_BUFFER_SIZE 16384



#ifndef UCHAR

   typedef unsigned char UCHAR;

#endif



#ifndef BYTE

   typedef unsigned char BYTE;

#endif



#ifndef BOOL

   typedef int BOOL;

#endif



#ifndef UINT

   typedef unsigned int UINT;

#endif



#ifndef LONG

   typedef unsigned long LONG;

#endif



#ifndef ULONG

   typedef unsigned long ULONG;

#endif



#ifndef WORD

   typedef unsigned short WORD;

#endif



#ifndef DWORD

   typedef unsigned long DWORD;

#endif



typedef struct PnpHdr

{

   DWORD       Signature;

   BYTE        Revision;

   BYTE        Length;

   WORD        Control;

   BYTE        Checksum;

   DWORD       EventNotify;

   WORD        RealEntryOffset;

   WORD        RealCodeSegment;

   WORD        ProtectedEntryOffset;

   DWORD       ProtectedCodeSegBase;

   DWORD       OemDeviceId;

   WORD        RealDataSegment;

   DWORD       ProtectedDataSegBase;

} PnpHdr;



//

// This is the header defined previous to the official release of the

// SMBIOS 2.1 Specification. This header was used by Intel and IBM

// as a private add-on to the DMI BIOS 2.0 Specification.

//

typedef struct DmiStructureTableHeader

{

   BYTE        header[5];                   // _DMI_ 

   BYTE        checksum;                      // checksum of DMI BIOS Header Structure

   WORD        len;                          // Total Length of DMI BIOS Structure Table

   DWORD       biosStructureTableAddress; // 32 bit physical address of beginning

                                 // of byte aligned DMI structure table

   WORD        numStructures;      // Total number of structures within the DMI

                              //  Structure table

   BYTE        dmiBiosRevision;   // Revision of the DMI BIOS Extensions



} DmiStructureTableHeader;



//

// SMBIOS 2.1 Structure Table Entry Point

//

typedef struct SmBiosStructureTableHeader

{

   BYTE        anchorString[4];        // _SM_ 

   BYTE        checksum;               // checksum of DMI BIOS Header Structure

   BYTE        len;                    // Total Length of DMI BIOS Structure Table

   BYTE        majorVersion;           // major version of the spec

   BYTE        minorVersion;           // minor version of the spec

   WORD        maxStrucSize;           // maximum structure size

   BYTE        epRevision;             // EPS revision

   BYTE        reserved[5];            // reserved, must be 0

   DmiStructureTableHeader DmiTable;   // legacy DMI BIOS Table Header

} SmBiosStructureTableHeader;



// 4-byte header that each structure begins with

typedef struct DMIHeader

{

   BYTE      structType;   // Specifies the type of structure.  Types 0 through 127 

                  // (7Fh) are reserved for and defined by this specification.

                  // Types 128 through 256 (80h to FFh) are available for 

                  // system and OEM-specific information.   

   BYTE       structLength;

   WORD       structHandle;

} DMIHeader;



// structure for System Enclosure information

typedef struct DMIType3

{

   DMIHeader    header;

   BYTE          manufacturer;

   BYTE          chassisType;

   BYTE          version;

   BYTE          serialNumber;

   BYTE          assetTagNumber;

   BYTE          bootupState;

   BYTE          powerSupplyState;

   BYTE          thermalState;

   BYTE          securityStatus;

} DMIType3;



// structure for Processor information

typedef struct DMIType4

{

   DMIHeader    header;

   BYTE          socketDesignation;

   BYTE          processorType;

   BYTE          processorFamily;

   BYTE          processorManufacturer;

   QWORD          processorID;

   BYTE          processorVersion;

   BYTE          voltage;

   WORD          externalClock;

   WORD          maxSpeed;

   WORD          currentSpeed;

   BYTE          status;

   BYTE          processorUpgrade;

   WORD          L1CacheHandle;

   WORD          L2CacheHandle;

   WORD          L3CacheHandle;

   BYTE          SerialNumber;

} DMIType4;



// External functions

extern LONG ReadPhysicalMemory(BYTE *source, BYTE *destination, LONG numberOfUnits, 

LONG unitLength);



// Global variables

DmiStructureTableHeader far *gfpDmiHeader;

SmBiosStructureTableHeader far *gfpSmBiosHeader;

PnpHdr    gPnpHeader;

char       gDebug = TRUE ;

unsigned char far *dmiTableBuffer;

unsigned char    dmiBuffer[DMI_BUFFER_SIZE];



// Forward declarations

DWORD GetDMITableInformation (void) ;



int       GetDMITableStructures () ;

char       *getTypeStr (char *buf) ;

char       *getString2 (int strNum, void *buf) ;

char       *getString (int strNum, char *buf) ;

int        getLengthOfStructure (char *p) ;



// System Enclosure 

int       printStructure03 (DMIType3 *buf, BOOL bWrite) ;



// Processor

int       printStructure04 (DMIType4 *buf, BOOL bWrite);

char       *getProcessorType (BYTE b) ;

char       *getProcessorFamily (BYTE b);

char       *getProcessorStatus (BYTE b) ;

char       *getProcessorUpgrade (BYTE b) ;



// This implementation has been developed using available references to

// demonstrate we do not need to reinvent the wheel for NetWare. There would be

// more complexity in the solution if it where to address other operating systems

// such as Windows 9x/NT.



DWORD GetDMITableInformation (void)

{

   UCHAR         byPassed = FALSE ;   

   PnpHdr far    *fpbyPnpHeader;

   DWORD          dwSegment;

   char      *pResBuffer = NULL ;



for (dwSegment = 0x000F0000;

   dwSegment <= 0x00100000;

   dwSegment = dwSegment + 0x0010) 

{

      // Work with 32-bit linear addresses only

      fpbyPnpHeader = (PnpHdr far *) dwSegment ;

      if ( ReadPhysicalMemory( (BYTE *)fpbyPnpHeader, 

            (BYTE *)&gPnpHeader, sizeof(gPnpHeader), 1  ) > 0 ) 

      {

          if ( gPnpHeader.Signature == 0x494D445F )  // _DMI 

         {

         BYTE i;

             BYTE byCkSum = 0;

            BYTE byMyLength;



                 // _DMI_ is validated by a 0 sum of bMyLength bytes.

                 byMyLength = sizeof (DmiStructureTableHeader);

                 for (i = 0, byCkSum = 0; i < byMyLength; i++)

                 {   

                            byCkSum += *(((BYTE far *) &gPnpHeader) + i);

      }



             if (0 == byCkSum)

                 {

               if ( gDebug )   

                  ConsolePrintf ( "(DEBUG): _DMI signature found, passed 

                        checksum. \n" ) ;

                   byPassed = TRUE ;

                   break ;

          }

         else {

             if ( gDebug )   

               ConsolePrintf ( "(DEBUG): The _DMI structures do not 

               checksum to 00h. System Management (SMBIOS) system component

               inventory will not be available." ) ;

         }      

       }

   }

}



if ( byPassed != TRUE ) {

   dwSegment = 0 ;

   goto END_GET ;

}



gfpDmiHeader = (DmiStructureTableHeader far *)dwSegment ;

gfpSmBiosHeader = (SmBiosStructureTableHeader far *)(dwSegment-1);



if (dwSegment) 

{

   if (((*(((char far *) gfpSmBiosHeader) + 0) == '_ ') &&\

         (*(((char far *) gfpSmBiosHeader) + 1) == 'S') &&

         (*(((char far *) gfpSmBiosHeader) + 2) == 'M') &&

         (*(((char far *) gfpSmBiosHeader) + 3) == '_ ')) )

   {

          BYTE i;

          BYTE byCkSum = 0;

          BYTE byMyLength;



      if ( gDebug ) 

            ConsolePrintf ("(DEBUG): SMBIOS %d.%d StructureTable Entry Point 

                  Structure (at %Xh):\n"

                    , (WORD) gfpSmBiosHeader->majorVersion

                         , (WORD) gfpSmBiosHeader->minorVersion

                        , dwSegment - 1);

      byMyLength = sizeof (SmBiosStructureTableHeader);

      for (i = 0, byCkSum = 0; i < byMyLength; i++)

         byCkSum += *(((BYTE far *) gfpSmBiosHeader) + i);



      if ((byCkSum != 0)) 

      {

         if ( gDebug)

                ConsolePrintf ( "(DEBUG): The _SM_ Structure Entry Point 

               Structure does not checksum to 00h. System Management (SMBIOS)

               component inventory scan results might not be accurate." ) ;

         }

      }



      if (gfpDmiHeader->biosStructureTableAddress > 0xFFFFF) {     

         // This implementation does not consider this scenario

         if ( gDebug )

               ConsolePrintf ("(DEBUG: No valid SMBIOS Structure Entry Point 

               structure found in F0000h-FFFFFh range.\n");



         dwSegment = 0;

         goto END_GET ;    

      }

      else

         dmiTableBuffer = 

               (char far *)gfpDmiHeader->biosStructureTableAddress;



   }

   else 

      {

           if ( gDebug )

                   ConsolePrintf ("(DEBUG: No valid SMBIOS Structure Entry Point

            structure found in F0000h- FFFFFh range.\n");

         dwSegment = 0;

      }



END_GET:

   return (dwSegment);

}

Listing 2 - Retrieving the System Management BIOS Structure Entities

This is a continuation and refers to variable definitions from Listing 1.

int GetDMITableStructures ()

{

      BOOL      bSuccess = FALSE ;

      BYTE      byDMIType = 0 ;

      int       iReturn = FALSE ;

      int       curLen, 

            totLen;

      char       *bufPtr;



      if ( ReadPhysicalMemory((BYTE *)dmiTableBuffer, 

               (BYTE *)dmiBuffer, DMI_BUFFER_SIZE - 1, 1) > 0 ) 

      {

            curLen = 0;

            totLen = gfpDmiHeader->len;

            bufPtr = dmiBuffer;



            iReturn = TRUE ;



            while (curLen < totLen && ( iReturn == TRUE ) ) 

               {

               if ( gDebug ) {

                  ConsolePrintf ("(DEBUG): SMBIOS Structure: %s (Type %u).\n",

                        getTypeStr (bufPtr), (BYTE) (*bufPtr));

            }



            switch (*bufPtr) 

            {

               case 3:

                  // System enclosure information

                  iReturn = 

                     printStructure03 ((DMIType3 *) bufPtr, TRUE);

                     break;



               case 4:

                  // Processor information

                        iReturn = 

                  printStructure04 ((DMIType4 *) bufPtr, TRUE);

               break;



            default:

               break;

      }



      curLen += getLengthOfStructure (bufPtr);

            bufPtr += getLengthOfStructure (bufPtr);

      }

   }



END_GET :

   return (iReturn);

}



char *getTypeStr (char *buf)

{

   switch (*buf) {

   case 0:

      return ("BIOS Information");

   case 1:

      return ("System Information");

   case 2:

      return ("Baseboard Information");

   case 3:

      return ("System Enclosure");

   case 4:

      return ("Processor Information");

   case 5:

      return ("Memory Controller Information");

   case 6:

      return ("Memory Module Information");

   case 7:

      return ("Cache Information");

   case 8:

      return ("Port Connector Information");

   case 9:

      return ("System Slots");

   case 10:

      return ("On Board Devices Information");

   case 11:

      return ("OEM Strings");

   case 12:

      return ("System Configuration Options");

   case 13:

      return ("BIOS Language Information");

   case 14:

      return ("Group Associations");

   case 15:

      return ("System Event Log");

   case 16:

      return ("Physical Memory Array");

   case 17:

      return ("Memory Device");

   case 18:

      return ("Memory Error Information");

   case 19:

      return ("Memory Array Mapped Address");

   case 20:

      return ("Memory Device Mapped Address");

   case 21:

      return ("Built-in Pointing Device");

   case 22:

      return ("Portable Battery");

   case 23:

      return ("System Reset");

   case 24:

      return ("Hardware Security");

   case 25:

      return ("System Power Controls");

   case 26:

      return ("Voltage Probe");

   case 27:

      return ("Cooling Device");

   case 28:

      return ("Temperature Probe");

   case 29:

      return ("Electrical Current Probe");

   case 30:

      return ("Out-of-Band Remote Access");

   case 37:

      return ("Memory Channel");

   case 39:

      return ("Power Supply");

   case 127:

      return ("End-of-Table");

   case -128:

   case -127:

   case -126:

   case -125:

      return ("OEM Defined Structure");

   default:

      return ("Unknown Structure Type");

   }

}



char *getString2 (int strNum, void *buf)

{

   return getString (strNum, (char *) buf);

}



char *getString (int strNum, char *buf)

{

   char *strPtr;

   int foundStrNum = 1;



   strPtr = buf + (*(buf + 1));   // go directly to start of string  

                              // space (if any)



   if ((*strPtr) == '\0')

      return "(null string)";      //specific initialization



   if (strNum == 1)

      return strPtr;

   else {

      while (foundStrNum != strNum) {

         while ((*strPtr) != '\0')

            strPtr++;

         strPtr++;

         foundStrNum++;

      }

      return strPtr;

      }

   }

}

int getLengthOfStructure (char *p)

{

   int len;



   len = (WORD) ((DMIHeader *) p)->structLength;



   /*

      * Some BIOSes (e.g. Intel BIOSes and maybe others) do not 

      * terminate the Type 5 Structure with two null bytes; therefore 

      * this special check when calculating the length of a Type 5

      * Structure.

   */

   if ((((DMIHeader *) p)->structType) == 0x05) {

      if ((*((WORD *) (p + len)) == 0x0000))

         return len + 2;

      else

         return len;

   }

   else {

      while (*((WORD *) (p + len)) != 0x0000)

         len++;

      return len + 2;

   }

}



int printStructure03 (DMIType3 *buf, BOOL bWrite)

{

   int      nWrite = TRUE ;

   char      szAssetTag[256];

   char      szSerialNumber[256];



   if (buf->serialNumber) {

      strcpy ( szSerialNumber, 

         getString ((UINT)buf->serialNumber, (char *) buf) );

   }

   else {

      strcpy ( szSerialNumber, "(none)" ) ;



   if ( gDebug )   

      ConsolePrintf ("(DEBUG): Serial Number:%s.\n",szSerialNumber);



   if (buf->assetTagNumber) {

      strcpy ( szAssetTag, getString ((UINT)buf->assetTagNumber, (char *) buf));

   }

   else {

      strcpy ( szAssetTag, "(none)" ) ;

   }



   if ( gDebug )

      ConsolePrintf ("(DEBUG): Asset Tag Number:%s.\n",szAssetTag);   



EXIT_PRINT :

   return nWrite ;            

}



int printStructure04 (DMIType4 *buf, BOOL bWrite)

{

   int      nWrite = TRUE ;



   if ( gDebug ) {             

      ConsolePrintf ("(DEBUG: Processor Type: %02Xh (%s).\n", 

            (UINT) buf->processorType,

         getProcessorType (buf->processorType));

   }



   if ( gDebug ) {   

      ConsolePrintf ("(DEBUG: Processor Family: %02Xh (%s).\n", 

            (UINT) buf->processorFamily,

         getProcessorFamily (buf->processorFamily));

   }



   if ( gDebug )   

      ConsolePrintf ("(DEBUG): Processor ID: %08lXh %08lXh.\n", 

            buf->processorID.l, buf->processorID.h);



   if ( gDebug )   

      ConsolePrintf ("(DEBUG):  Max Speed: %d MHz.\n", 

            buf->maxSpeed);



   if ( gDebug )    

      ConsolePrintf ("(DEBUG:  Current Speed: %d MHz.\n", 

            buf->currentSpeed);



   if ( gDebug ) {   

      ConsolePrintf ("(DEBUG:  Processor Upgrade: %02Xh (%s).\n", 

            (UINT) buf->processorUpgrade,

         getProcessorUpgrade (buf->processorUpgrade));

   }



EXIT_PRINT :

   return nWrite ;            

}



char *getProcessorType (BYTE b)

{

   switch (b) {

   case 1:

      return "Other";

   case 2:

      return "Unknown";

   case 3:

      return "Central Processor";

   case 4:

      return "Math Processor";

   case 5:

      return "DSP Processor";

   case 6:

      return "Video Processor";

   default:

      return "Error: Invalid Type";

   }

}



char *getProcessorFamily (BYTE b)

{

   switch (b) {

   case 1:

      return "Other";

   case 2:

      return "Unknown";

   case 3:

      return "8086";

   case 4:

      return "80286";

   case 5:

      return "80386";

   case 6:

      return "80486";

   case 7:

      return "8087";

   case 8:

      return "80287";

   case 9:

      return "80387";

   case 0x0A:

      return "80487";

   case 0x0B:

      return "Pentium Family";

   case 0x0C:

      return "Pentium Pro";

   case 0x0D:

      return "Pentium II";

   case 0x0E:

      return "Pentium MMX";

   case 0x0F:

      return "Celeron";

   case 0x10:

      return "Pentium II";

   case 0x11:

      return "Pentium III";

   case 0x12:

      return "M1 Family";

   case 0x13:

   case 0x14:

   case 0x15:

   case 0x16:

   case 0x17:

   case 0x18:

      return "Reserved for specific M1 versions";

   case 0x19:

      return "K5 Family";

   case 0x1A:

   case 0x1B:

   case 0x1C:

   case 0x1D:

   case 0x1E:

   case 0x1F:

      return "Reserved for specific K5 versions";

   case 0x20:

      return "PowerPC Family";

   case 0x21:

      return "Power PC 601";

   case 0x22:

      return "Power PC 603";

   case 0x23:

      return "Power PC 603+";

   case 0x24:

      return "Power PC 604";

   case 0x30:

      return "Alpha Family";

   case 0x40:

      return "MIPS Family";

   case 0x50:

      return "SPARC Family";

   case 0x60:

      return "68040 Family";

   case 0x61:

      return "68xxx";

   case 0x62:

      return "68000";

   case 0x63:

      return "68010";

   case 0x64:

      return "68020";

   case 0x65:

      return "68030";

   case 0x70:

      return "Hobbit Family";

   case 0x80:

      return "Weitek";

   case 0x90:

      return "PA-RISC Family";

   case 0xA0:

      return "V30 Family";

   case 0xB0:

      return "Pentium III Xeon";

   case 0xB2:

      return "Pentium IV";

   case 0xB1:

   case 0xB3:

   case 0xB4:

   case 0xB5:

   case 0xB6:

   case 0xB7:

   case 0xB8:

   case 0xB9:

   case 0xBA:

   case 0xBB:

   case 0xBC:

   case 0xBD:

   case 0xBE:

   case 0xBF:

      return "Reserved for specific Pentium processor versions";

   default:

      return "Error: Invalid Family";

   }

}



char *getProcessorStatus (BYTE b)

{

   switch (b) {

   case 0x00:

      return "CPU Socket Unpopulated, Status Unknown";

   case 0x40:

      return "CPU Socket Populated, Status Unknown";

   case 0x01:

      return "CPU Socket Unpopulated, Status Enabled";

   case 0x41:

      return "CPU Socket Populated, Status Enabled";

   case 0x02:

      return "CPU Socket Unpopulated, Status Disabled by User";

   case 0x42:

      return "CPU Socket Populated, Status Disabled by User";

   case 0x03:

      return "CPU Socket Unpopulated, Status Disabled by BIOS";

   case 0x43:

      return "CPU Socket Populated, Status Disabled by BIOS";

   case 0x04:

      return "CPU Socket Unpopulated, Status Idle";

   case 0x44:

      return "CPU Socket Populated, Status Idle";

   case 0x07:

      return "CPU Socket Unpopulated, Status Other";

   case 0x47:

      return "CPU Socket Populated, Status Other";

   default:

      return "Error: Invalid Status";

   }

}

char *getProcessorUpgrade (BYTE b)

{

   switch (b) {

   case 1:

      return "Other";

   case 2:

      return "Unknown";

   case 3:

      return "Daughter Board";

   case 4:

      return "ZIF Socket";

   case 5:

      return "Replaceable Piggy Back";

   case 6:

      return "None";

   case 7:

      return "LIF Socket";

   case 8:

      return "Slot 1";

   case 9:

         return "Slot 2";

   default:

      return "Error: Invalid Type";

   }

}

The source code in the above listings can be used in any generic NLM implementation. For compilation, you need to include the standard ANSII headers; for linking, you need to explicitly import the ReadPhysicalMemory API. You can download the source code from the AppNotes Download page at http://developer.novell.com/ research/downloads.htm.

Example: ZENworks for Servers 3 Inventory

The Inventory feature of the ZENworks for Servers 3 product showcases the leveraging of the SMBIOS information on NetWare to report enhanced system component inventory. Prior to the ZENworks for Servers 3 release, NetWare inventory relied on NetWare SDK and SNMP to report system component inventory. In ZENworks for Servers 3, NetWare inventory is reported with a help of an Inventory agent that executes NLMs on demand to retrieve system component and software inventory information. The Inventory agent is managed by policies and executes according to a predefined schedule and parameters.

In ZENworks for Servers 3 Inventory, the INVAID.NLM module leverages SMBIOS v2.3 and pipes the retrieved information to the set of components responsible for storing this information in an inventory database. The inventory database can subsequently be queried for display, inventory summary, report generation, and so on.

The additions to the scanning capability in ZENworks for Servers 3 include inventory of:

  • BIOS version

  • BIOS release date

  • Manufacturer

The inventory feature also reports system information such as asset tag, product name, serial number, processor information, cache information, system slots information, port information, video adapter name, sound card name, and so on. For more information about ZENworks for Servers 3, visit the product Web page at http://www.novell.com/products/zenworks/servers.

Looking Ahead

As market expectations demand still better management initiatives, making management information more easily accessible on NetWare could be one effective step towards the solution. This is especially true where comparisons between existing management instrumen- tation on NetWare and Microsoft's WBEM initiative called WMI (Windows Management Instrumentation) may hold stake in customers' purchase decisions.

To get your creative coding juices flowing, here's my personal reflection on this front. Suppose you had an NLM read the SMBIOS for the various information and publish it in the NetWare registry under the HKEY_DYN_DATA category-that is, create keys for the various SMBIOS structures and then proceed to embed the contents of each structure under the parent key. As there can be multiple instances of some SMBIOS structures, you'd need to design a suitable hierarchy in registry keys to address this.

By using the NetWare registry, you would gain the ease and definitiveness associated with reading the registry keys or data. For administrators, utilities that display the registry contents (such as the NetWare Remote Manager) would help in management. For third-party developers, the simplicity of reading the registry would be a huge benefit and would save you from having to reinvent the development wheel or understand the intricacies of the SMBIOS Reference Specification.

Sample NetWare Registry Model

Here's a possible model for the System Enclosure (Type 3) and Processor Information (Type 4) SMBIOS structures, with the key hierarchy addressing the presence of multiple instances. If the reference specification indicates that a SMBIOS structure member has a special interpretation, it would be the application's responsibility to map the member's value to an appropriate displayable string. If the SMBIOS structure member is a placeholder such as a memory handle or memory address, the application would ignore attempting to convert such values to displayable strings. The application would also need to preserve units associated with the various values to prevent any loss in data interpretation.

This proposed model does not attempt to publish proprietary OEM- or BIOS vendor-specific information in the NetWare registry, as the application may not be able to guarantee precise data interpretation. If necessary, you could define additional rules to help the application publish other high-demand SMBIOS structures to overcome any loss in data interpretation.

The most common SMBIOS member data types (unsigned character BYTE, unsigned short WORD, and unsigned long DWORD) could easily be converted to null-terminated strings. A possible structure for the NetWare Registry is detailed here:

{

      DWORD l;

      DWORD h;

   } QWORD , conversion to null-terminated string easily achievable.



NetWare Registry
   Dynamic Data (HKEY_DYN_DATA)

      System Enclosure  (Key)

         0   (Key)

            Type ("3")        

            Manufacturer    (appropriate REG_SZ value)     

            ChassisType    (appropriate REG_SZ value)

            Version (appropriate REG_SZ value)

            SerialNumber (appropriate REG_SZ value)

            AssetTagNumber (appropriate REG_SZ value)

            BootupState (appropriate REG_SZ value) 

            PowerSupplyState (appropriate REG_SZ value)

            ThermalState (appropriate REG_SZ value)

            SecurityStatus (appropriate REG_SZ value)



      Processor Information (Key)

         0   (Key)

            Type ("4")        

            SocketDesignation (appropriate REG_SZ value)

            ProcessorType (appropriate REG_SZ value)

            ProcessorFamily (appropriate REG_SZ value)

            ProcessorManufacturer (appropriate REG_SZ value)

            ProcessorID (appropriate REG_SZ value)

            ProcessorVersion (appropriate REG_SZ value)

            Voltage (appropriate REG_SZ value)

            ExternalClock (appropriate REG_SZ value)+

            MaxSpeed (appropriate REG_SZ value)

            CurrentSpeed (appropriate REG_SZ value)

            Status (appropriate REG_SZ value)

            ProcessorUpgrade (appropriate REG_SZ value)

            L1CacheHandle ( "Ignored" REG_SZ value) 

            L2CacheHandle ( "Ignored" REG_SZ value)

            L3CacheHandle ( "Ignored" REG_SZ value)

            SerialNumber (appropriate REG_SZ value)



         1   (Key)

            Type (appropriate REG_SZ value)        

            SocketDesignation (appropriate REG_SZ value)

            ProcessorType (appropriate REG_SZ value)

            ProcessorFamily (appropriate REG_SZ value)

            ProcessorManufacturer (appropriate REG_SZ value)

            ProcessorID (appropriate REG_SZ value)

            ProcessorVersion (appropriate REG_SZ value)

            Voltage (appropriate REG_SZ value)

            ExternalClock (appropriate REG_SZ value)

            MaxSpeed (appropriate REG_SZ value)

            CurrentSpeed (appropriate REG_SZ value)

            Status (appropriate REG_SZ value)

            ProcessorUpgrade (appropriate REG_SZ value)

            L1CacheHandle ( "Ignored" REG_SZ value) 

            L2CacheHandle ( "Ignored" REG_SZ value)

            L3CacheHandle ( "Ignored" REG_SZ value)

            SerialNumber (appropriate REG_SZ value)

Conclusion

Leveraging the System Management information in the BIOS and simplifying its availability will benefit management applications in their multi-faceted tasks. Hopefully this AppNote has given you a basis to work from, as well as a good idea of what is possible in future network management applications.

If you want more information on SMBIOS, refer to the following resources:

  • System Management BIOS v2.3 Reference Specification, co-authored by American Megatrends Inc., Award Software International Inc., Compaq Computer Corporation, Dell Computer Corporation, Hewlett-Packard Company, Intel Corporation, International Business Machines Corporation, Phoenix Technologies Limited, and SystemSoft Corporation

  • "SMBIOS (aka DMIBIOS) Version 2.3.1 (Build 39) Display Utility - Mar 27 2000 for DOS", software made available by IBM, without fee, to assist the user in comparing code with respect to the System Management BIOS Reference specification

* Originally published in Novell AppNotes


Disclaimer

The origin of this information may be internal or external to Novell. While Novell makes all reasonable efforts to verify this information, Novell does not make explicit or implied claims to its validity.

© 2014 Novell