Note: Access SMBIOS in Kernel of Windows

因為老闆說要給部門做一個簡單的talk, 鑑於目前同事多數是 Junior level, 而公司從事的還是x86為主的硬體生意, 所以決定中秋過完的部門週會上, 簡單介紹一下SMBIOS 的來由與存取方法, 讀資料過程還是有新的收穫, 學到如何kernel 使用wmi

開始做一些研究, 找到一些舊的資料, 基本上SMBIOS 真的開始全面導入是 v2.3.1之後, Win95 就開始使用PNPBIOS 界面提供 SMBIOS的支援, 但是直到 1999年Microsoft 才開始加入 Design for Windows Logo 認證裡面(from PC 99 Design guide), 不過強制需求應該是從Windows XP SP2/Windows 2003 Server SP1 開始引入了mssmbios.sys, 在WinHEC 2005有Release了一個文件 smbios.doc

該文件解釋了mssmbios.sys的一些行為,  基本上是基於WMI機制等於內部做了MOF宣告, 然後會去parsing smbios table資料, 並且存在 registry path 下"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Mssmbios\Data"

因為基於WMI, 所以它提供XP SP2與之後版本的user/kernel mode 的 wmi 存取, 為了更簡便使用user mode 還透過增加GetSystemFirmwareTable() API (from Windows 2003 server sp1), 而kernel mode 則從Vista 開始 多了 AuxKlibGetSystemFirmwareTable

kernel wmi 則是透過 IoWmiOpenBlock(), IoWMIQueryAllData(), “SMBIOS_DATA_GUID" 去取得, 這還要看一下 driver load order, 否則貿然在kernel driver使用應該會出問題

廣告

Note: SMBIOS/ACPI 觀察

這兩天因為要跟同事介紹 SMBIOS 所以重新把之前寫的code 拿出來測試一下, 公司配給我一台Asus Laptop 發現一件有趣的事情, ACPI 仍然可以在E/F Segment 內被找到 RSDP, 但是 SMBIOS 的 ESP 卻已經移掉, 這件事情讓我感覺蠻奇怪的

所以就查了一下SMBIOS v3.0規格內寫的是 Non-UEFI system 還是需要 在F Segment 內提供ESP呀, ACPI 猜想也是, 搞不太懂 ASUS 為啥一個拔掉一個還留著, 理論上為了最大相容性, 應該是都留著會比較好, 雖然他是UEFI only 的設計了, 但是我看過 WINCE一個詭異的設計是透過UEFI bootloader 去支援APIC mode 就是只在開機階段透過UEFI bootloader 去parsing ACPI table 然後自己切 Interrupt mode 到APIC mode其他部份保持WINCE本來的樣子

Note: Device Instance Order of PCI/PCI-e

遇到需要客制化NIC 是指定 NIC1,  NIC2的客制需求, 但是有些人的想法竟然是進OS再去改設定改變 Device Order, 這真是讓我覺的不能接受, 不能接受的原因不是因為這樣掉到我頭上, 而是這樣會變成生產安裝需要多一道程序,這代表工時與成本增加, 最重要是客戶是否使用會有困擾, 不太可能PC出貨後, 都不重新安裝OS, 那是否客戶也需要記得如果改變特定的Device Instance Order, 按客戶提案需求也不見得吻合

直覺這樣的需求應該是透過ACPI 來設定, 但是許久沒做BIOS工作後, 看了ACPI Spec. 沒看到可以做的方法, 跟老闆討論說Dell 有這樣改, 查了一下果然Dell 有於2010年提案並於同年12月正式成為 PCI Firmware Specification Revision 3.1 (need member) 的一部分,

看了一下 Linux 關於這個功能的實現, 發現早於2008年SMBIOS 2.6.0 就有Type 41 可以做這個功能

如果沒有這兩個機制, 變成就必須HW設計時注意PCI的BUS順序了

Note: new version DumpSMBIOS for Win7/Win8 , use GetSystemFirmwareTable() API

多年前, 我剛開始學習BIOS 的相關資訊時, 曾經寫過一個DumpSMBIOS, 程式基本是建立在Legacy BIOS 下面的設定, 所以是從 0xE000 ~ 0xF000 Segments 中去查找相關資訊,

最近因為工作需求, 需要讀取SMBIOS Table 內的相關資訊, 而執行環境又限定在Win7 跟Win 8之上, 所以採用了 GetSystemFirmwareTable() 去讀取, 因為如果是XP SP2 (32bits) 則只能透過WMI介面去讀取了

而GetSystemFiremwareTable MSDN 沒有太多的資訊說明(要自己看memory dump資料判斷), 所以寫了一個測試用的code 放在 github 上, 有需要的人歡迎自取! 基本上是把之前寫的code 稍微改一下就可以用了, 主要是增加 Unicode 的支援, 跟為了相容 VC2013 的型態檢查

下面的資訊是程式在 Win7 32bit/VisualBox 4.3 執行的結果, 也在實機上測試過 Win7/Win8 執行 x64

———————————————————————————————————————————————————–

We need prepare 457 bytes for recevie SMBIOS/DMI Table
SMBIOS version:2.5
DMI Revision:25
Total length: 449
DMI at address aa390
-=======================================================-
========== BIOS information ==========
Vendor: innotek GmbH
Version: VirtualBox
BIOS Starting Segment: 0xE000
Release Date: 12/01/2006
Image Size: 128K
-=======================================================-
========== System information ==========
Manufacturer: innotek GmbH
Product Name: VirtualBox
Version: 1.2
Serial Number: 0
UUID: 0A04CA8F-8B0E-45BD-AB29-F975CACECE29
SKU Number: Null String
Family: Virtual Machine
-=======================================================-
========== Base Board information ==========
Length: 0xF
Manufacturer: Oracle Corporation
Product Name: VirtualBox
Version: 1.2
Serial Number: 0
Asset Tag Number: Null String
Location in Chassis: Null String
-=======================================================-
========== System Enclosure information ==========
Length: 0xD
Manufacturer: Oracle Corporation
Version: Null String
Serial Number: Null String
Asset Tag Number: Null String
-=======================================================-
========== OEM String ==========
OEM String: vboxRev_96996
-=======================================================-
========== BIOS information ==========
Vendor: Null String
Version:
BIOS Starting Segment: 0x6E00
Release Date: i
Image Size: 14080K
System BIOS version: 0.9
EC Firmware version: 0.0

Note: Dump some SMBIOS information.

Update:
on XP and lasted version Windows,
You can try https://github.com/KunYi/DumpSMBIOS

字串定址也搞定, 剛剛看了新的 spec.  ver2.6 才發現原本 PNP BIOS的介面已經不被建議(將被捨棄), 以後都採用這種Table/Structure 的結構方式取得系統相關資訊!, 但是Entry Point Structure 在 UEFI系統上有對應的 SMBIOS_GUID 去取得, 而不一定是 F0000h ~ FFFFFh 這個64K 的segment裡了!, 不過我猜應該目前都會在, 維持相容性很重要的!

Update : the program source code & VC2005 project can download from DumpSMBIOS Source

The program use SysInfo driver from CrystalCPUID

// DumpSMBIOS.cpp : Defines the entry point for the console application.
//

#include “stdafx.h"
#include “.SysInfoISysInfo.h"
#include “.SysInfoItemID.h"

typedef ISysInfo* (*_CreateSysInfo) (DWORD);
typedef void (*_DestroySysInfo) (ISysInfo*);
typedef ULONG    (*_MemReadBlock) (ULONG address, UCHAR* data, ULONG count, ULONG unitsize);
_MemReadBlock pfMemReadBlock = NULL;

#pragma pack(push)
#pragma pack(1)

typedef struct {
UCHAR    Signature[4];
UCHAR    Chksum;
UCHAR    Length;
UCHAR    VerMajor;
UCHAR    VerMinor;
UINT16    MaxSize;
UCHAR    EPSRevision;
UCHAR    Formatter[5];
UCHAR    DMISignature[5];
UCHAR    DMIChksum;
UINT16    StructLength;
ULONG    StructAddress;
UINT16    NumStruct;
UCHAR    BCDRevision;
} SMBIOS_EPS, *PSMBIOS_EPS;

// ref. section 3.1.2 in spec.
typedef struct {
UCHAR    Type;
UCHAR    Length;
UINT16    Handle;
} SMBIOS_STRUCT_HEADER, *PSMBIOS_STRUCT_HEADER;

typedef struct _TYPE_0_ {
SMBIOS_STRUCT_HEADER    Header;
UCHAR    Vendor;
UCHAR    Version;
UINT16    StartingAddrSeg;
UCHAR    ReleaseDate;
UCHAR    ROMSize;
ULONG64 Characteristics;
UCHAR    Extension[2]; // spec. 2.3
UCHAR    MajorRelease;
UCHAR    MinorRelease;
UCHAR    ECFirmwareMajor;
UCHAR    ECFirmwareMinor;
} BIOSInfo, *PBIOSInfo;

typedef struct _TYPE_1_ {
SMBIOS_STRUCT_HEADER    Header;
UCHAR    Manufacturer;
UCHAR    ProductName;
UCHAR    Version;
UCHAR    SN;
UCHAR    UUID[16];
UCHAR    WakeUpType;
UCHAR    SKUNumber;
UCHAR    Family;
} SystemInfo, *PSystemInfo;

typedef struct _TYPE_2_ {
SMBIOS_STRUCT_HEADER    Header;
UCHAR    Manufacturer;
UCHAR    Product;
UCHAR    Version;
UCHAR    SN;
UCHAR    AssetTag;

} BoardInfo, *PBoardInfo;

#pragma pack(pop)

UCHAR* toString(const UCHAR* src, int len)
{
static UCHAR buff[256];
memcpy(buff, src, len);
buff[len] = 0;
return buff;
}

void printSMBIOSEPS(SMBIOS_EPS& eps, ULONG addr)
{
printf(“SMBIOS Signature: %s, at 0x%Xn", toString(eps.Signature, 4), addr);
printf(“Checksum: 0x%Xn", eps.Chksum);
printf(“Length: %d(0x%X)n", eps.Length, eps.Length);
printf(“Version: %d.%dn", eps.VerMajor, eps.VerMinor);
printf(“Maximum Structure Size: %d(0x%X)n", eps.MaxSize, eps.MaxSize);
printf(“Entry Point Structure Revision: %dn", eps.EPSRevision);
printf(“Formatter Area: 0x%02X,0x%02X,0x%02X,0x%02X,0x%02Xn",
eps.Formatter[0],eps.Formatter[1],eps.Formatter[2],
eps.Formatter[3],eps.Formatter[4]);
printf(“Intermediate anchor string: %sn", toString(eps.DMISignature, 5));
printf(“Intermediate Checksum: 0x%Xn", eps.DMIChksum);
printf(“Structure Table Length: %dn", eps.StructLength);
printf(“Structure Table Address: 0x%08Xn", eps.StructAddress);
printf(“Number of SMBIOS Structures: %dn", eps.NumStruct);
printf(“BCD Revision: 0x%02Xn", eps.BCDRevision);
}

bool LoadSysInfo(HMODULE &hSysInfoLib, ISysInfo* pISysInfo)
{
hSysInfoLib = LoadLibrary(_T(“SysInfo.dll"));

if (NULL != hSysInfoLib)
{
_CreateSysInfo pCreateSysInfo = (_CreateSysInfo)GetProcAddress(hSysInfoLib, “CreateSysInfo");

if (NULL != pCreateSysInfo)
{
pISysInfo = pCreateSysInfo(MODE_PCI); // request access memory spaces, MODE_PCI
}
else
{
printf(“ERR: Failed in GetProcAddress(“CreateSysInfo")n");
exit(-1);
return false;
}
}
else
{
printf(“Can’t find Sysinfo.dlln");
exit(-1);
return false;
}
return true;
}

bool UnloadSysInfo(HMODULE hSysInfoLib, ISysInfo* pISysInfo)
{
_DestroySysInfo pDestroySysInfo = (_DestroySysInfo)GetProcAddress(hSysInfoLib, “DestroySysInfo");
if (pDestroySysInfo)
{
pDestroySysInfo(pISysInfo);
pISysInfo = NULL;
}
else
{
printf(“ERR: Failed in GetProcAddress(“DestroySysInfo")n");
exit(-1);
return false;
}

FreeLibrary(hSysInfoLib);
hSysInfoLib = NULL;
return true;
}

bool FindSMBIOS(PSMBIOS_EPS& eps, UCHAR* buff, UINT size)
{
UCHAR *p = buff;

for (UINT i = 0; i < size; i+=16)
{
if (0 == memcmp(p, “_SM_", 4))
{
PSMBIOS_EPS psm = (PSMBIOS_EPS)p;

if (0 == memcmp(psm->DMISignature, “_DMI_",5))
{
UCHAR chk = 0;

// verify checksum
for(UINT i=0; i < psm->Length; i++)
{
chk+=*(p+i);
}

if (0 == chk)
{
eps = psm;
return true;
}
}
}
p+=16;
}
return false;
}

const UCHAR* LocateString(UCHAR* str, UINT i)
{
static const UCHAR strNull[] = “Null String";
if (0 == i || 0 == *str) return strNull;

while(–i)
{
str += strlen((char*)str) + 1;
}
return str;
}

UCHAR* toPointString(void* p)
{
return (UCHAR*)p+((PSMBIOS_STRUCT_HEADER)p)->Length;
}

bool ProcBIOSInfo(void* p)
{
PBIOSInfo pBIOSinfo = (PBIOSInfo)p;
UCHAR *str = toPointString(p);

printf(“=============== BIOS information =================n");
printf(“Vendor: %sn", LocateString(str, pBIOSinfo->Vendor));
printf(“Version: %sn", LocateString(str, pBIOSinfo->Version));
printf(“BIOS Starting Segment: 0x%Xn", pBIOSinfo->StartingAddrSeg);
printf(“Release Date: %sn", LocateString(str, pBIOSinfo->ReleaseDate));
printf(“Image Size: %dKn", (pBIOSinfo->ROMSize+1)*64);
if (pBIOSinfo->Header.Length > 0x14)
{   // for spec v2.4 and later
printf(“System BIOS version: %d.%dn", pBIOSinfo->MajorRelease, pBIOSinfo->MinorRelease);
printf(“EC Firmware version: %d.%dn", pBIOSinfo->ECFirmwareMajor, pBIOSinfo->ECFirmwareMinor);
}
return true;
}

bool ProcSysInfo(void* p)
{
PSystemInfo pSysinfo = (PSystemInfo)p;
UCHAR *str = toPointString(p);

printf(“============= System information =============n");
printf(“Manufacturer: %sn", LocateString(str, pSysinfo->Manufacturer));
printf(“Product Name: %sn", LocateString(str, pSysinfo->ProductName));
printf(“Version: %sn", LocateString(str, pSysinfo->Version));
printf(“Serial Number: %sn", LocateString(str, pSysinfo->SN));
// for v2.1 and later
printf(“UUID: %02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02Xn",
pSysinfo->UUID[0], pSysinfo->UUID[1], pSysinfo->UUID[2], pSysinfo->UUID[3],
pSysinfo->UUID[4], pSysinfo->UUID[5], pSysinfo->UUID[6], pSysinfo->UUID[7],
pSysinfo->UUID[8], pSysinfo->UUID[9], pSysinfo->UUID[10], pSysinfo->UUID[11],
pSysinfo->UUID[12], pSysinfo->UUID[13], pSysinfo->UUID[14], pSysinfo->UUID[15]);

if (pSysinfo->Header.Length > 0x19)
{
// fileds for spec. 2.4
printf(“SKU Number: %sn", LocateString(str, pSysinfo->SKUNumber));
printf(“Family: %sn", LocateString(str, pSysinfo->Family));
}
return true;
}

bool ProcBoardInfo(void* p)
{
PBoardInfo pBoardInfo = (PBoardInfo)p;
UCHAR *str = toPointString(p);

printf(“============= Base Board information =============n");
printf(“Manufacturer: %sn", LocateString(str, pBoardInfo->Manufacturer));
printf(“Product Name: %sn", LocateString(str, pBoardInfo->Product));
printf(“Version: %sn", LocateString(str, pBoardInfo->Version));
printf(“Serial Number: %sn", LocateString(str, pBoardInfo->SN));
printf(“Asset Tag Number: %sn", LocateString(str, pBoardInfo->AssetTag));
return true;
}

bool ProcOEMString(void* p)
{
PSMBIOS_STRUCT_HEADER pHdr = (PSMBIOS_STRUCT_HEADER)p;
UCHAR *str = toPointString(p);
printf(“============== OEM String =================n");
printf(“OEM String: %sn", LocateString(str,*(((UCHAR*)p)+4) ));

return true;
}

bool DispatchStructType(PSMBIOS_STRUCT_HEADER hdr)
{
typedef struct {
UCHAR    t;
bool (*Proc)(void* p);
} TPFUNC;

const TPFUNC    tpfunc[] = {
{    0, ProcBIOSInfo        },
{   1, ProcSysInfo        },
{   2, ProcBoardInfo    },
{    11, ProcOEMString    },
};

for (UINT i = 0; i < sizeof(tpfunc)/sizeof(TPFUNC); i++)
{
if (tpfunc[i].t == hdr->Type)
{
printf(“-==============================================-n");
tpfunc[i].Proc((void*)hdr);
return true;
}
}
return false;
}

void DumpSMBIOSStruct(ULONG Addr,UINT Len, UINT Num)
{
UCHAR *pData;
UCHAR *p = NULL;
pData = (UCHAR*)malloc(Len);
if (NULL != pData)
{
pfMemReadBlock(Addr, pData, Len, 1);
p = pData;
}
PSMBIOS_STRUCT_HEADER pHeader;
for (UINT n = 0; n < Num; n++) {
pHeader = (PSMBIOS_STRUCT_HEADER)p;

DispatchStructType(pHeader);

UCHAR *nt = p + pHeader->Length; // point to struct end
while (0 != (*nt | *(nt+1))) nt++;
nt+=2;
if (nt > pData+Len) break;
p = nt;
}

free(pData);
}

int _tmain(int argc, _TCHAR* argv[])
{
ISysInfo* pISysInfo = NULL;
HMODULE hSysInfoLib = NULL;
const ULONG MEM_RANGE = 64*1024; // 0xF0000 ~ 0xFFFFF, 64K
const ULONG MEM_START = 0xF0000;
UCHAR buff[MEM_RANGE];
PSMBIOS_EPS pSMBIOS = NULL;

if (false == LoadSysInfo(hSysInfoLib, pISysInfo))
return -1;

pfMemReadBlock = (_MemReadBlock) GetProcAddress(hSysInfoLib, “_MemReadBlock");
if (pfMemReadBlock)
{
pfMemReadBlock(MEM_START, buff, MEM_RANGE, 1);
FindSMBIOS(pSMBIOS, buff, MEM_RANGE);
}
else
{
printf(“ERR: failed in GetProcAddress(“_MemReadBlock")n");
}

if (pSMBIOS)
{
printf(“Find out the SMBIOS Entry Point Structure n");
printSMBIOSEPS(*pSMBIOS, ((UCHAR*)pSMBIOS – buff) + 0xF0000);
DumpSMBIOSStruct(pSMBIOS->StructAddress, pSMBIOS->StructLength, pSMBIOS->NumStruct);
}
UnloadSysInfo(hSysInfoLib, pISysInfo);
return 0;
}

Result is run the program on Lenovo X60

Find out the SMBIOS Entry Point Structure
SMBIOS Signature: _SM_, at 0xF6730
Checksum: 0x27
Length: 31(0x1F)
Version: 2.4
Maximum Structure Size: 86(0x56)
Entry Point Structure Revision: 0
Formatter Area: 0x00,0x00,0x00,0x00,0x00
Intermediate anchor string: _DMI_
Intermediate Checksum: 0x3E
Structure Table Length: 2241
Structure Table Address: 0x000E0010
Number of SMBIOS Structures: 67
BCD Revision: 0x00
-==============================================-
=============== BIOS information =================
Vendor: LENOVO
Version: 7BETD7WW (2.18 )
BIOS Starting Segment: 0xDC00
Release Date: 11/20/2008
Image Size: 2048K
System BIOS version: 2.24
EC Firmware version: 1.19
-==============================================-
============= System information =============
Manufacturer: LENOVO
Product Name: 1706B69
Version: ThinkPad X60
Serial Number: LVN1422
UUID: XXXXXXXX-XXXX-XXXX-C3117DD70E8C
SKU Number: Null String
Family: ThinkPad X60
-==============================================-
============= Base Board information =============
Manufacturer: LENOVO
Product Name: 1706B69
Version: Not Available
Serial Number: XXXXXXXXX
Asset Tag Number:
-==============================================-
============== OEM String =================
Count:1
OEM String: IBM ThinkPad Embedded Controller -[7BHT40WW-1.13    ]-

Note: Dump SMBIOS Entry Pointer Structure

Update:  on XP and lasted version Windows

You can try  https://github.com/KunYi/DumpSMBIOS
// DumpSMBIOS.cpp : Defines the entry point for the console
application.
//

#include “stdafx.h"
#include “.SysInfoISysInfo.h"
#include
“.SysInfoItemID.h"

typedef ISysInfo* (*_CreateSysInfo) (DWORD);
typedef void
(*_DestroySysInfo) (ISysInfo*);
typedef ULONG    (*_MemReadBlock) (ULONG
address, UCHAR* data, ULONG count, ULONG unitsize);
_MemReadBlock
pfMemReadBlock = NULL;

#pragma pack(push)
#pragma pack(1)

typedef struct {
UCHAR    Signature[4];
UCHAR    Chksum;

UCHAR    Length;
UCHAR    VerMajor;
UCHAR    VerMinor;

UINT16    MaxSize;
UCHAR    EPSRevision;
UCHAR
Formatter[5];
UCHAR    DMISignature[5];
UCHAR    DMIChksum;

UINT16    StructLength;
ULONG    StructAddress;
UINT16
NumStruct;
UCHAR    BCDRevision;
} SMBIOS_EPS, *PSMBIOS_EPS;

// ref. section 3.1.2 in spec.
typedef struct {
UCHAR
Type;
UCHAR    Length;
UINT16    Handle;
}
SMBIOS_STRUCT_HEADER, *PSMBIOS_STRUCT_HEADER;

typedef struct _TYPE_0_ {
SMBIOS_STRUCT_HEADER    Header;

UCHAR    Vendor;
UCHAR    Version;
UINT16    StartingAddrSeg;
}
BIOSInfo, *PBIOSInfo;

#pragma pack(pop)

UCHAR* toString(const UCHAR* src, int len)
{
static UCHAR
buff[256];
memcpy(buff, src, len);
buff[len] = 0;
return
buff;
}

void printSMBIOSEPS(SMBIOS_EPS& eps, ULONG addr)
{

printf(“SMBIOS Signature: %s, at 0x%Xn", toString(eps.Signature, 4),
addr);
printf(“Checksum: 0x%Xn", eps.Chksum);
printf(“Length:
%d(0x%X)n", eps.Length, eps.Length);
printf(“Version: %d.%dn",
eps.VerMajor, eps.VerMinor);
printf(“Maximum Structure Size: %d(0x%X)n",
eps.MaxSize, eps.MaxSize);
printf(“Entry Point Structure Revision: %dn",
eps.EPSRevision);
printf(“Formatter Area:
0x%02X,0x%02X,0x%02X,0x%02X,0x%02Xn",

eps.Formatter[0],eps.Formatter[1],eps.Formatter[2],

eps.Formatter[3],eps.Formatter[4]);
printf(“Intermediate anchor string:
%sn", toString(eps.DMISignature, 5));
printf(“Intermediate Checksum:
0x%Xn", eps.DMIChksum);
printf(“Structure Table Length: %dn",
eps.StructLength);
printf(“Structure Table Address: 0x%08Xn",
eps.StructAddress);
printf(“Number of SMBIOS Structures: %dn",
eps.NumStruct);
printf(“BCD Revision: 0x%02Xn", eps.BCDRevision);
}

bool LoadSysInfo(HMODULE &hSysInfoLib, ISysInfo* pISysInfo)
{

hSysInfoLib = LoadLibrary(_T(“SysInfo.dll"));

if (NULL != hSysInfoLib)
{
_CreateSysInfo
pCreateSysInfo = (_CreateSysInfo)GetProcAddress(hSysInfoLib, “CreateSysInfo");

if (NULL != pCreateSysInfo)
{
pISysInfo =
pCreateSysInfo(MODE_PCI); // request access memory spaces, MODE_PCI

}
else
{
printf(“ERR: Failed in
GetProcAddress(“CreateSysInfo")n");
exit(-1);

return false;
}
}
else
{

printf(“Can’t find Sysinfo.dlln");
exit(-1);
return
false;
}
return true;
}

bool UnloadSysInfo(HMODULE hSysInfoLib, ISysInfo* pISysInfo)
{

_DestroySysInfo pDestroySysInfo = (_DestroySysInfo)GetProcAddress(hSysInfoLib,
“DestroySysInfo");
if (pDestroySysInfo)
{

pDestroySysInfo(pISysInfo);
pISysInfo = NULL;
}

else
{
printf(“ERR: Failed in
GetProcAddress(“DestroySysInfo")n");
exit(-1);
return
false;
}

FreeLibrary(hSysInfoLib);
hSysInfoLib = NULL;
return
true;
}

bool FindSMBIOS(PSMBIOS_EPS& eps, UCHAR* buff, UINT size)
{

UCHAR *p = buff;

for (UINT i = 0; i < size; i+=16)
{
if (0 ==
memcmp(p, “_SM_", 4))
{
PSMBIOS_EPS psm =
(PSMBIOS_EPS)p;

if (0 == memcmp(psm->DMISignature, “_DMI_",5))

{
UCHAR chk = 0;

// verify checksum
for(UINT i=0; i <
psm->Length; i++)
{

chk+=*(p+i);
}

if (0 == chk)
{
eps
= psm;
return true;
}

}
}
p+=16;
}
return false;
}

void DumpSMBIOSStruct(ULONG Addr,UINT Len, UINT Num)
{
UCHAR
*pData;
UCHAR *p = NULL;
pData = (UCHAR*)malloc(Len);
if
(NULL != pData)
{
pfMemReadBlock(Addr, pData, Len,
1);
p = pData;
}
PSMBIOS_STRUCT_HEADER
pHeader;
for (UINT i = 0; i < Num; i++) {
pHeader =
(PSMBIOS_STRUCT_HEADER)p;
switch(pHeader->Type) {
case
0: {
PBIOSInfo pBIOSinfo = (PBIOSInfo)p;

printf(“BIOS Starting Segment: 0x%X",
pBIOSinfo->StartingAddrSeg);
}

break;
case 1:
break;
}
UCHAR
*nt = p + pHeader->Length; // point to struct end
while (0 != (*nt
| *(nt+1))) nt++;
nt+=2;
if (nt > pData+Len) break; //
over?
p = nt;
}

free(pData);
}

int _tmain(int argc, _TCHAR* argv[])
{
ISysInfo* pISysInfo =
NULL;
HMODULE hSysInfoLib = NULL;
const ULONG MEM_RANGE = 64*1024;
// 0xF0000 ~ 0xFFFFF, 64K
const ULONG MEM_START = 0xF0000;
UCHAR
buff[MEM_RANGE];
PSMBIOS_EPS pSMBIOS = NULL;

if (false == LoadSysInfo(hSysInfoLib, pISysInfo))
return -1;

pfMemReadBlock = (_MemReadBlock) GetProcAddress(hSysInfoLib,
“_MemReadBlock");
if (pfMemReadBlock)
{

pfMemReadBlock(MEM_START, buff, MEM_RANGE, 1);
FindSMBIOS(pSMBIOS,
buff, MEM_RANGE);
}
else
{
printf(“ERR: failed
in GetProcAddress(“_MemReadBlock")n");
}

if (pSMBIOS)
{
printf(“Find out the SMBIOS Entry Point
Structure n");
printSMBIOSEPS(*pSMBIOS, ((UCHAR*)pSMBIOS – buff) +
0xF0000);
DumpSMBIOSStruct(pSMBIOS->StructAddress,
pSMBIOS->StructLength, pSMBIOS->NumStruct);
}

UnloadSysInfo(hSysInfoLib, pISysInfo);
return 0;
}

Result is run the program on Lenovo X60

Find out the SMBIOS Entry Point Structure
SMBIOS Signature: _SM_, at
0xF6730
Checksum: 0x27
Length: 31(0x1F)
Version: 2.4
Maximum
Structure Size: 86(0x56)
Entry Point Structure Revision: 0
Formatter Area:
0x00,0x00,0x00,0x00,0x00
Intermediate anchor string: _DMI_
Intermediate
Checksum: 0x3E
Structure Table Length: 2241
Structure Table Address:
0x000E0010
Number of SMBIOS Structures: 67
BCD Revision: 0x00

這個執行結果SMBIOS Entry Point Structure 在 0F6730h, 而一共有 67個數據體, 頭一個在 0E0010h
的位址

解析 SMBIOS 數據體麻煩的地方是, 在數據體後面還有可能接數個字串(C string). 所以不能直接使用數據體 本身的
length來跳到下一個數據體. 看了 Spec. 的Example 還是有點模糊.需要在花點時間想想怎樣Dump才好!

把數據體與數據體定位搞定了!