[心得] 關於SMBIOS的讀取方法

看板C_and_CPP作者 (今、そこに いる僕)時間14年前 (2009/11/20 11:43), 編輯推噓7(704)
留言11則, 7人參與, 最新討論串1/1
SMBIOS是一組記錄系統的組態資訊的表格所成集合。例如BIOS版號, EC版號, 有什麼插槽 之或是有幾根RAM的資訊。一般來說, BIOS會在開機時把SMBIOS的進入點寫到F000:0000~ FFFF之間的任意位址裡, 我們再寫個AP, 在F000:0000~FFFF之間找到'_SM_'這樣的字串來 確定進入點, 並讀取每張表格。不過, SMBIOS的specification並沒有要求每張表格要塞 在那小小的64K裡, 於是就產生了以下問題: 如果進入點和各張表格都塞在那64K裡, 那簡單。用真實模式的手法硬幹就可以讀到全部 的表格。(在Win32, 則因為DOS VM的關係, 會把前1MB的實體記憶體映射到16 bit行程裡, 所以也沒問題)。但若是各張表格沒有放在那64K裡, 情況就會變得很複雜。真實模式的手 法行不通了, 因為碰不到1MB以上的範圍! 在DOS下, 可以透過保護模式來讀, 但在Win32下 , 雖然身處保護模式, 但因為Virtual Memory的關係, 所以碰不到Physical Memory, 這時 候就只能寫個Driver, 把那塊存有SMBIOS的記憶體Map到行程裡。 只是讀個小小的記憶體要搞得這麼麻煩, 很令人頭痛對吧。不過Windows有提供一個API , 叫GetSystemFirmwareTable, 它可以幫忙讀到SMBIOS, 但它只能用在64bit XP上頭, 所 以還是不方便。另一個方式則是透過WMI, 但是程式要寫一大堆, 也不方便。如果有更直接 的方式就好了。其實, Win32有個Driver叫mssmbios.sys, 它會在開機時把SMBIOS的值偷到 以下路徑: "HKLM\\system\\currentcontrolset\\services\\mssmbios\\data" 所以我們只需要透過讀機碼就可以得到那一堆表格了, 很方便。以下是我寫的一個類別 , 它可以列印Type 0的內容... #pragma comment(lib, "advapi32") #include <windows.h> #include <tchar.h> #include <cstdio> #include <cstdlib> #include <vector> using namespace std; inline TCHAR const *DumpData(PBYTE dat, DWORD len) { static TCHAR ret[65536]=_T(""); TCHAR buf[65536]=_T(""); TCHAR txt[17]=_T(""), hex[53]=_T(" "); DWORD i=0, j=0; buf[0]=_T('\n'); while(i<len) { txt[j]=dat[i]>=30?dat[i]:_T('.'); _stprintf(&hex[j*3], _T("%02X "), (BYTE)dat[i]); hex[(j+1)*3]=_T(' '); j++; if(j==16) { _tcscat(buf, _T("\n")); _tcscat(buf, hex); _tcscat(buf, txt); j=0; } i++; } if(j>0) { _tcsnset(&hex[j*3], _T(' '), 52-j*3); _tcsnset(&txt[j], _T(' '), 16-j); _tcscat(buf, _T("\n")); _tcscat(buf, hex); _tcscat(buf, txt); } _tcscpy(ret, buf); return ret; } class CSMBIOS { public: typedef struct {PBYTE off; DWORD len;} table_t; typedef vector<table_t> tables_t; protected: PBYTE m_pData; tables_t m_idx[0x89]; public: CSMBIOS(void); ~CSMBIOS(); tables_t GetTable(BYTE nType); WORD GetSMVersion(void); BYTE GetDMIRevision(void); }; CSMBIOS::CSMBIOS(void) : m_pData(NULL) { HKEY key=0; DWORD len=0; LONG err=0; err=RegOpenKeyEx(HKEY_LOCAL_MACHINE, "system\\currentcontrolset\\services\\mssmbios\\data", 0, KEY_READ, &key); if(err==ERROR_SUCCESS) { err=RegQueryValueEx(key, "smbiosdata", NULL, NULL, NULL, &len); if(err==ERROR_SUCCESS) { m_pData=(PBYTE)malloc(len); if(m_pData) { err=RegQueryValueEx(key, "smbiosdata", NULL, NULL, m_pData, &len); if(err==ERROR_SUCCESS) { for(PBYTE ptr=m_pData+8;ptr<m_pData+len;) { BYTE type=*ptr; table_t tab={ptr, 0}; if(type>=0x89) break; for(ptr+=ptr[1];ptr<m_pData+len && (*ptr || ptr[1]);ptr++); ptr+=2; tab.len=ptr-tab.off; m_idx[type].push_back(tab); } return; } free(m_pData); RegCloseKey(key); m_pData=NULL; } RegCloseKey(key); } RegCloseKey(key); } } CSMBIOS::~CSMBIOS() { free(m_pData); } CSMBIOS::tables_t CSMBIOS::GetTable(unsigned char nType) { return m_idx[nType]; } WORD CSMBIOS::GetSMVersion(void) { return *(WORD *)(m_pData+1); } BYTE CSMBIOS::GetDMIRevision(void) { return *(BYTE *)(m_pData+3); } class CType0 : public CSMBIOS { public: char const *m_pVendor; char const *m_pBIOSVerStr; WORD m_nBIOSAddr; char const *m_pBIOSDateStr; ULONG m_nROMSize; __int64 m_nBIOSChar; WORD m_nBIOSExtChar; WORD m_nBIOSRev; WORD m_nECRev; CType0(void); }; CType0::CType0(void) : CSMBIOS(), m_pVendor(NULL), m_pBIOSVerStr(NULL), m_nBIOSAddr(0), m_pBIOSDateStr(NULL), m_nROMSize(0), m_nBIOSChar(0), m_nBIOSExtChar(0), m_nBIOSRev(0), m_nECRev(0) { tables_t tabs=GetTable(0); if(!tabs.size()) return; m_pVendor=(char const *)(tabs[0].off+tabs[0].off[1]); m_pBIOSVerStr=m_pVendor+strlen(m_pVendor)+0x1; m_nBIOSAddr=*(PWORD)(tabs[0].off+0x6); m_pBIOSDateStr=m_pBIOSVerStr+strlen(m_pBIOSVerStr)+1; m_nROMSize=*(PWORD)(tabs[0].off+0x9); m_nBIOSChar=*(__int64 *)(tabs[0].off+0xa); WORD ver=GetSMVersion(); ver=MAKEWORD(LOBYTE(ver), HIBYTE(ver)); if(ver>=0x0204) { m_nBIOSExtChar=*(PWORD)(tabs[0].off+0x12); m_nBIOSRev=*(PWORD)(tabs[0].off+0x14); m_nECRev=*(PWORD)(tabs[0].off+0x16); } } int _tmain(int argc, TCHAR *argv[]) { CType0 type0; _tprintf(_T("SMBIOS version:\t%04x\n"), type0.GetSMVersion()); _tprintf(_T("Vendor:\t%s\n"), type0.m_pVendor); _tprintf(_T("BIOS Version:\t%s\n"), type0.m_pBIOSVerStr); _tprintf(_T("BIOS Date:\t%s\n"), type0.m_pBIOSDateStr); _tprintf(_T("BIOS Address:\t%x\n"), type0.m_nBIOSAddr); _tprintf(_T("ROM Size:\t%u\n"), type0.m_nROMSize); _tprintf(_T("BIOS Characeristics:\t%x\n"), type0.m_nBIOSChar); _tprintf(_T("BIOS Extension Characeristics:\t%x\n"), type0.m_nBIOSExtChar); _tprintf(_T("BIOS Reversion:\t%x\n"), type0.m_nBIOSRev); _tprintf(_T("EC Reversion:\t%x\n"), type0.m_nECRev); return 0; } 這樣的程式在網路上到處都有, 重點是怎麼把它寫得很漂亮。我是用個類別包起來, 不知 道大家覺得怎麼樣。 --               裸になって                                                   何が悪い?      -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 114.36.35.211

11/20 11:45, , 1F
雖然沒在用, 但是先推一下分享:)
11/20 11:45, 1F

11/20 11:46, , 2F
if 太多層
11/20 11:46, 2F

11/20 11:47, , 3F
buf[0]=_T('\n'); 之後再 tcscat 是危險的
11/20 11:47, 3F

11/20 11:48, , 4F
data member 都是 public ... 那其實用 struct 就好
11/20 11:48, 4F

11/20 11:49, , 5F
然後 magic number 多了點
11/20 11:49, 5F

11/20 11:50, , 6F
最後是縮排
11/20 11:50, 6F

11/20 12:19, , 7F
樓上比較講究~軔體工程師大多都這樣啦
11/20 12:19, 7F

11/20 12:48, , 8F
推:)
11/20 12:48, 8F

11/20 12:56, , 9F
先推XD
11/20 12:56, 9F

11/20 16:32, , 10F
長知識 推
11/20 16:32, 10F

11/20 23:46, , 11F
推!
11/20 23:46, 11F
文章代碼(AID): #1B1X1GM2 (C_and_CPP)