Introduction
Anyone who has used a computer since the IBM PC hit the marketplace back in 1981 has at least heard the term BIOS. It stands for Basic Input / Output System and plays a critical role in the bootstrap process of both x86 and x64 machines. The BIOS is firmware code that runs when the computer is first turned on, and is used to identify and initiate the component hardware that will later get used by the operating system and applications.
If you have ever had the opportunity, (or misfortune depending on the circumstances), of browsing through your PC BIOS, you may well have discovered there is a lot of nice-to-know information there. There are all sorts of useful items, such as what sort of expansion slots are in the chassis, which CPU sockets are populated, even the serial number and manufacturer code for a given RAM chip. (There is also some mostly meaningless information for the majority of us, like whether or not the BIOS supports five and a quarter inch Toshiba brand floppy disks formatted for the Japanese character set.)
While not all of the information applies to everyone, certainly some of it is useful to some of us. Less so if we can only get to the information by waiting for our PC to boot up, then banging frantically on the F2 key, while hoping its not the delete key on this model that interrupts the load of the operating system and brings up the BIOS main menu. Which begs the question, why can't we view all the BIOS information after the operating system boots up?
To WMI or Not to WMI
The answer of course, is that you can. But doing so is not as straight-forward as one might think. In the wild-west days of computing, a system level programmer might just map a pointer directly to the physical start address of the BIOS ROM, and read away. But most of us know those days have come and gone under most modern operating system offerings. Unless you are still running Windows 98 getting access to physical memory addresses, especially from within user application space, is a little tricky. (If you are still running Windows 98 you may find some other things a little tricky, like keeping your PC running for more than a handful of hours without the dreaded blue screen of death paying you a visit!)
In an attempt to simplify systems administration, a few years back Microsoft began to offer IT professionals an initiative called WMI, a subset of the more open DMTF initiative. WMI, which stands for Windows Management Instrumentation, attempts to expose information about hardware by abstracting devices to manageable entities which can then be queried and in some cases controlled via simple scripts. While the information provided is easy to get at it is also often missing the level of detail provided in the BIOS. Take for instance the RAM. According to the WMI documentation, all the same detail level about a physical RAM device that is present in the BIOS is present in the WMI representation. However, the results I get back disagree.
WMI:
BankLabel:
Capacity: 1073741824
DataWidth: 64
DeviceLocator: XMM1
FormFactor: 8
InterleaveDataDepth: 0
InterleavePosition: 0
MemoryType: 0
Speed: 800
Tag: Physical Memory 0
TotalWidth: 64
TypeDetail: 128
BIOS:
physical memory array handle:0x37
memory error information handle:0xfffe
total width:64
data width:64
size:1024 megabytes
form factor:dimm
memory type:ddr2
device set:0x01
device locator:XMM1
bank locator:
additional memory details:
synchronous
speed:800 mhz
manufacturer:JEDEC ID:AD 00 00 00 00 00 00 00
serial number:28300000
asset tag:
part number:HYMP512U64CP8-S6
rank:8
While the differences may seem subtle, if you are trying to audit your inventory for recalled RAM chips, having the serial number, manufacturer, and part number are an absolute must. Which brings us back to our original question, how does one get at the system management BIOS information from the operating system?
Look Again!
Fortunately, Microsoft gives us two methods for getting at the raw SMBIOS data block. The first is ironically through WMI itself. For reasons unknown to me, the operating system actually loads all the SMBIOS data on start up, but then chooses only to parse and make available portions of it. So while WMI only directly exposes some of the memory components, in fact, by parsing the same data the WMI abstraction objects load to populate themselves, you can successfully retrieve and display all the data. The key to this is the MSSMBios_RawSMBiosTables
class inside the root\wmi namespace. If you are using Windows XP or 2000, this is the only way to get this data without writing or accessing a driver lurking in the kernel space. For Windows 2003 and above, there is an API call, GetSystemFirmwareTable
, which will retrieve the raw data block.
System Management BIOS Specification
Of course once we have the raw data block itself, there is still the issue of how to parse it. For this you will need to look at the DMTF System Management BIOS Reference Specification. It outlines each and every table from version 1.0 to the current standard (which at the time this document was written is 2.6a). The data structure itself is rather compact, but requires specific knowledge of each table to properly interpret the results. Currently there are forty two defined tables, though only ten of them are required by BIOS vendors to be considered compliant with the specification.
From a macro-view, each table is divided into two sections, the formatted, and the unformatted section. The formatted section consists of a required header, which is defined as 4 bytes.
0x00 TYPE BYTE Specifies the table type of the structure (currently 0 - 41).
0x01 LENGTH BYTE Length of the formatted area including byte 0.
0x02 HANDLE WORD Unique 16-bit handle for correlation to related tables.
After the header, you would read each byte for the specified LENGTH, and match those up against the table in the reference specification for identification. For example, according to the specification, the 4th, 5th, 6th, and 7th bytes of the memory array mapped table (type 19) represent the starting address of the device, to be interpreted as a DWORD. So you can simply pluck those 4 bytes and cast them to an unsigned long, presuming of course you are coding on a little endian processor.
Reading the unformatted section of a table is a little bit trickier. You start by moving to the offset of the table specified by the length parameter. You then can expect each "entry" to be an array of 8-bit ASCII based strings. Each string is null terminated, and the unformatted section of the current table, is separated from the formatted section of the next table, with a double null. This will hopefully be less confusing with an example right out of the DMTF specification.
BIOS_INFO
0x00 TYPE BYTE
0x02 LENGTH BYTE
0x03 HANDLE WORD
0x04 VENDOR BYTE *STRING
0x05 VERSION BYTE *STRING
0x06 START_SEGMENT WORD
0x08 RELEASE_DATE BYTE *STRING
0x09 ROM_SIZE BYTE
0x0A CHARACTERISTICS QWORD
0x12 CHARACTERISTICS2 BYTE
0x14 BIOS_MAJOR BYTE
0x15 BIOS_MINOR BYTE
0x16 FIRMWARE_MAJOR BYTE
0x17 FIRMWARE_MINOR BYTE
Note the three entries marked "string." These will be the indexes into the unformatted section we use for associating the ASCII text strings with the entry in the table. Let's assume our table contains the following data (the comments to the right are only for clarification and not part of the actual data).
0 ;BIOS structure type
0x13 ;length of formatted section
0x1234 ;unique handle
0x01 ;indexes string 1 in the unformatted section
0x02 ;indexes string 2 in the unformatted section
0xE800 ;BIOS starting address
0x03 ;indexes string 3 in the unformatted section
0x01 ;size of BIOS rom
0x1C ;BIOS characteristics
0x0 ;BIOS characteristics extended
'System BIOS Vendor name',0x0 ;first string in table
'BIOS version 01.02C',0x0 ;second string in table
'01/12/2006',0x0 ;last string in table
0x0 ;end of table
(It is worth noting, however, that not all tables have an unformatted section. In that case the LENGTH byte will in fact point to the double nulls which separate the current table from the next table.)
While parsing these tables will for sure make your head spin at first glance, once you get a feel for a few its really not as difficult as it would seem. In truth its mostly tedious and without a doubt the most difficult part of parsing the data from the SMBIOS tables, is simply getting all the table formats into usable header files. Luckily for you, I've already done that in creating my SMBIOS Peek Utility! Drum roll please...
The Source Code
Without further ado I present to you my Smbios Peek Utility. I wrote the application using Visual Studio 2005. It is the most basic of command line programs that simply displays to the console the BIOS information present for the ten mandatory structures for a BIOS to be DMTF compliant. These are based on the May 2nd, 2007 proposed 2.6a specification. The required tables handled are:
BIOS Information Type 0
System Information Type 1
System Enclosure Type 3
Processor Information Type 4
Cache Information Type 7
System Slots Type 9
Physical Memory Array Type 16
Memory Device Type 17
Memory Array Mapped Address Type 19
System Boot Information Type 32
You can download the details for these tables here. I did my best to make the code structure itself straight forward and modular. To this extent I frequently chose readability over elegance. Anyone wishing to implement any of the other 42 smbios tables, should be able to do so by copying one of the existing table .h and .cpp files and simply filling in the required support and display methods as defined by the DMTF specification for that table type.
Using the Program
The program gets run from the command line. It can be invoked with a ''-f'' option followed by a file name which will redirect all the console output to the specified file. For example:
c:\program files\utilities\smbiosp\smbios_p.exe -f "c:\my documents\inventory.txt"
will append all the smbios data gathered silently to the file c:\my documents\inventory.txt. If the file or the directory does not exist, it will be created. If you run the executable without a command line, it simply displays the output on the console. In that case I'd recommend using the |more option to keep the data from scrolling off the display before you have a chance to read it.
Acknowledgments
When I first began this project, I was specifically hunting for the serial number on the RAM chips. I had a number of very frustrating false starts and misinformation. Not to push the Microsoft Kool-Aid, but as often is the case I got my first real clue from the Microsoft News Groups. I owe many thanks to "Ben Voigt", "Jeff Henkels", and the mysterious but dead-on-the-money "m". I am always amazed at the number of people willing to help out a fellow hacker at microsoft.public.win32.programmer.kernel. I also owe a debt of gratitude to "kirants" and his 2006 article appropriately titled: ''SMBIOS Demystified''.
Unfortunately...
After running this utility on several different motherboards, I came across a PC where the BIOS manufacturer was not loading all of the data, even when more in-depth probing of the device EEPROMs themselves show the data is present. This is unfortunate and surprising since in my case at least, the BIOS core was the latest and greatest version from one of the BIOS developers who chair the standard committee that advocates SMBIOS data. In such cases the only way to get the data is to purchase a third party application, or write an SMBUS mini-port client driver. Who knows, I may try my hand at this as well. If so, you can bet I'll be posting a version 2 of this article!