/*----------------------------------------------------------------------* * mem_if.c * Get SMBIOS Tables and associated data. * This code is 64-bit safe. * Note that for Windows, this should be compiled with /TP (as C++). * * 12/17/07 ARCress - created * 02/26/08 ARCress - decode type 15 log structure * 07/21/08 ARCress - fixed for 64-bit memory model * 08/12/08 ARCress - trim out extra stuff, consolidated *----------------------------------------------------------------------*/ /*----------------------------------------------------------------------* The BSD License Copyright (c) 2002-2008, Intel Corporation Copyright (c) 2009 Kontron America, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: a.. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. b.. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. c.. Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *----------------------------------------------------------------------*/ #ifdef WIN32 #define _WIN32_DCOM #pragma once #include #include #include //deprecated iostream.h #include //ole32.lib #include #include //Wbemcli.h,Wbemidl.h Wbemuuid.lib #elif defined(DOS) #include #include #include #else // Linux or Solaris #include #include #include #include #include #include #include #include #include #ifdef __linux__ #include #endif #endif #if defined(SOLARIS) || defined(BSD) || defined(HPUX) #define EXEC_PAGESIZE 4096 #endif #define DOS_MEM_RANGE_BASE 0xF0000 //starting memory range for smbios tables #define DOS_NUM_BYTES_TO_MAP 0x0FFFE //number bytes to map for smbios tables //smbios defines #define SMBIOS_STRING "_SM_" //server management bios string #define SMBIOS_MAJOR_REV 2 //major rev for smbios spec 2.3 #define SMBIOS_MINOR_REV 1 //minor rev for smbios spce 2.3 #define SMBIOS_STRUCTURE_TYPE_OFFSET 0 //offset in structure for type #define SMBIOS_STRUCTURE_LENGTH_OFFSET 1 //offset in structure for length #define SMBIOS_TABLE_LENGTH_OFFSET 0x5 //offset for length of table #define SMBIOS_MAJOR_REV_OFFSET 0x6 //offset for major rev of smbios #define SMBIOS_MINOR_REV_OFFSET 0x7 //offset for minor rev of smbios #define SMBIOS_TABLE_ENTRY_POINT_OFFSET 0x18 //offset for smbios structure table entry point #define SMBIOS_TABLE_SIZE_OFFSET 0x16 //offset for smbios structure table size #define NIBBLE_SIZE 4 //bit size of a nibble #ifdef WIN32 extern "C" { char fsm_debug = 0; } /*=1 to show smbios debug messages*/ #else char fsm_debug = 0; /*=1 to show smbios debug messages*/ #endif static int s_iTableRev = 0; //static globabl var to store table revision #ifndef WIN32 typedef int BOOL; typedef unsigned long ULONG; typedef unsigned char UCHAR; typedef unsigned short USHORT; BOOL MapPhysicalMemory(ULONG tdStartAddress, ULONG ulSize, ULONG* ptdVirtualAddress); BOOL UnMapPhysicalMemory(ULONG tdVirtualAddress, ULONG ulSize); int OpenIMemoryInterface(void); int CloseIMemoryInterface(void); #endif // Linux, not __WINDOWS__ #ifdef WIN32 /* Windows Memory routines */ static BYTE m_byMajorVersion = 0; static BYTE m_byMinorVersion = 0; static BYTE * m_pbBIOSData = NULL; static DWORD m_dwLen = 0; extern "C" { int getSmBiosTables(UCHAR **ptableAddress) { int bRet = 0; HRESULT hres; IWbemLocator *pLoc = 0; IWbemServices *pSvc = 0; IEnumWbemClassObject* pEnumerator = NULL; // Initialize COM. hres = CoInitializeEx(0, COINIT_MULTITHREADED); if (FAILED(hres)) { return bRet; } // Obtain the initial locator to Windows Management // on a particular host computer. hres = CoCreateInstance( CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pLoc); if (FAILED(hres)) { CoUninitialize(); return bRet; } // Connect to the root\cimv2 namespace with the // current user and obtain pointer pSvc // to make IWbemServices calls. hres = pLoc->ConnectServer( _bstr_t(L"ROOT\\WMI"), // WMI namespace NULL, // User name NULL, // User password 0, // Locale NULL, // Security flags 0, // Authority 0, // Context object &pSvc // IWbemServices proxy ); if (FAILED(hres)) { pLoc->Release(); CoUninitialize(); return bRet; } // Set the IWbemServices proxy so that impersonation // of the user (client) occurs. hres = CoSetProxyBlanket( pSvc, // the proxy to set RPC_C_AUTHN_WINNT, // authentication service RPC_C_AUTHZ_NONE, // authorization service NULL, // Server principal name RPC_C_AUTHN_LEVEL_CALL, // authentication level RPC_C_IMP_LEVEL_IMPERSONATE, // impersonation level NULL, // client identity EOAC_NONE // proxy capabilities ); if (FAILED(hres)) { pSvc->Release(); pLoc->Release(); CoUninitialize(); return bRet; // Program has failed. } hres = pSvc->CreateInstanceEnum( L"MSSMBios_RawSMBiosTables", 0, NULL, &pEnumerator); if (FAILED(hres)) { pSvc->Release(); pLoc->Release(); CoUninitialize(); return bRet; // Program has failed. } else { do { IWbemClassObject* pInstance = NULL; ULONG dwCount = NULL; hres = pEnumerator->Next(WBEM_INFINITE, 1, &pInstance, &dwCount); if (SUCCEEDED(hres)) { VARIANT varBIOSData; VariantInit(&varBIOSData); CIMTYPE type; hres = pInstance->Get(bstr_t("SmbiosMajorVersion"),0,&varBIOSData,&type,NULL); if (FAILED(hres)) { VariantClear(&varBIOSData); } else { m_byMajorVersion = (BYTE)varBIOSData.iVal; VariantInit(&varBIOSData); hres = pInstance->Get(bstr_t("SmbiosMinorVersion"),0,&varBIOSData,&type,NULL); if(FAILED(hres)) { VariantClear(&varBIOSData); } else { m_byMinorVersion = (BYTE)varBIOSData.iVal; s_iTableRev = (m_byMajorVersion << 4) + m_byMinorVersion; VariantInit(&varBIOSData); hres = pInstance->Get(bstr_t("SMBiosData"),0,&varBIOSData,&type,NULL); if(SUCCEEDED(hres)) { if ( ( VT_UI1 | VT_ARRAY ) != varBIOSData.vt ) { } else { SAFEARRAY *parray = NULL; parray = V_ARRAY(&varBIOSData); BYTE* pbData = (BYTE*)parray->pvData; m_dwLen = parray->rgsabound[0].cElements; m_pbBIOSData = (BYTE *)malloc(m_dwLen); memcpy(m_pbBIOSData,pbData,m_dwLen); *ptableAddress = m_pbBIOSData; bRet = m_dwLen; } } VariantClear(&varBIOSData); } } break; } } while (hres == WBEM_S_NO_ERROR); } // Cleanup // ======== pSvc->Release(); pLoc->Release(); CoUninitialize(); return(bRet); } /*end getSmBiosTables*/ int closeSmBios(UCHAR *ptableAddress, ULONG ulSmBiosLen) { int rv = 0; /* memory interface already closed */ free(ptableAddress); return(rv); } } /*end extern C*/ #else /* Linux Memory routines */ #define MEM_DRIVER "/dev/mem" #define FALSE 0 #define TRUE 1 //intialize static handle amd counter static int m_iDriver = 0; static int m_iCount = 0; ////////////////////////////////////////////////////////////////////////////// // OpenIMemoryInterface ////////////////////////////////////////////////////////////////////////////// // Name: OpenIMemoryInterface // Purpose: create handle to driver if first time // Context: static m_iDriver & m_iCount // Returns: int rv (0 = success) // Parameters: none // Notes: /////////////////////////////////////////////////////////////////////////////// int OpenIMemoryInterface(void) { int rv = -1; /* ARM64 does not handle /dev/mem the same. * It exposes SMBIOS at /sys/firmware/dmi/tables/, but because * IO memory is memory mapped, cannot use legacy /dev/mem. */ #ifndef __arm__ #ifndef __aarch64__ //check to see if driver has been previously defined if (!m_iDriver) { //open the driver m_iDriver = open(MEM_DRIVER, O_RDONLY); if (m_iDriver > 0) { //increment instance counter m_iCount++; } else { //couldn't open the driver...so make it zero again m_iDriver = 0; } } else { //handle exists...so inc instance counter. m_iCount++; } if (m_iDriver > 0) rv = 0; #endif #endif return(rv); } /////////////////////////////////////////////////////////////////////////////// // CloseIMemoryInterface // Name: destructor // Purpose: close driver handle and null out field // Context: // Returns: // Parameters: none // Notes: /////////////////////////////////////////////////////////////////////////////// int CloseIMemoryInterface(void) { int rv = 0; //check if an instance has been created by looking at the counter if (!m_iCount) { m_iCount--; //decrement instance counter if (m_iCount == 0) { close(m_iDriver); m_iDriver = 0; //and null it out } } return(rv); } /////////////////////////////////////////////////////////////////////////////// // MapPhysicalMemory /////////////////////////////////////////////////////////////////////////////// // Name: MapPhysicalMemory // Purpose: given the starting address and size, the virtualize // memory is mapped. // Returns: True if success // Parameters: [IN] tdStartAddress = physical address to map // [IN] ulSize = number of bytes to map // [OUT] ptdVirtualAddress = ptr to virtual address /////////////////////////////////////////////////////////////////////////////// BOOL MapPhysicalMemory(ULONG tdStartAddress, ULONG ulSize, ULONG* ptdVirtualAddress) { long tdVirtualAddr = 0; ULONG ulDiff; //check if we have a valid driver handle if (!m_iDriver) return FALSE; //error //check for valid input params if (!tdStartAddress || !ulSize) return FALSE; //error //align the offset to a valid page boundary and adust its length ulDiff = (ULONG)(tdStartAddress % EXEC_PAGESIZE); if (fsm_debug) printf("MapPhys: tdStart=%lx, page=%x, diff=%lx\n", tdStartAddress,EXEC_PAGESIZE,ulDiff); tdStartAddress -= ulDiff; ulSize += ulDiff; tdVirtualAddr = (long)mmap((caddr_t)0, (size_t)ulSize, PROT_READ, MAP_SHARED, m_iDriver, (off_t)tdStartAddress); if (fsm_debug) printf("MapPhys: mmap(tdStart=%lx,size=%lx) = %lx\n", tdStartAddress,ulSize,tdVirtualAddr); if (tdVirtualAddr == -1) return FALSE; *ptdVirtualAddress = tdVirtualAddr + ulDiff; return TRUE; } /////////////////////////////////////////////////////////////////////////////// // UnMapPhysicalMemory /////////////////////////////////////////////////////////////////////////////// // Name: UnMapPhysicalMemory // Purpose: given the virtual address, unmaps it...frees memory. // Returns: True if success // Parameters: [IN] tdVirtualAddress = virtual address to unmap // [IN] ulSize = not needed for windows // Notes: none /////////////////////////////////////////////////////////////////////////////// BOOL UnMapPhysicalMemory(ULONG tdVirtualAddress, ULONG ulSize) { ULONG ulDiff = 0; ulDiff = (ULONG)(tdVirtualAddress % EXEC_PAGESIZE); tdVirtualAddress -= ulDiff; ulSize += ulDiff; if (munmap((void *)tdVirtualAddress,ulSize) != 0) return FALSE; return TRUE; } /////////////////////////////////////////////////////////////////////////////// // getSmBiosTables // Purpose: returns smbios tables pointer // Returns: pointer to the virtual address where SMBIOS tables begins ////////////////////////////////////////////////////////////////////////////// int getSmBiosTables(UCHAR **ptableAddress) { ULONG tdStartAddress = DOS_MEM_RANGE_BASE; ULONG ulSize = DOS_NUM_BYTES_TO_MAP; ULONG ulSmBiosLen = 0; ULONG tdVirtualAddress = 0; int iInc; UCHAR *pSmBios = NULL; UCHAR ucCheckSum = 0; UCHAR ucVal, ucVal1; int rv; if (fsm_debug) printf("getSmBiosTables start\n"); //get interface to Memory rv = OpenIMemoryInterface(); if (fsm_debug) printf("OpenIMemoryInterface rv = %d\n",rv); //map memory into user space if ((rv != 0) || !MapPhysicalMemory(tdStartAddress,ulSize,&tdVirtualAddress)) { #ifdef SOLARIS /* mmap always fails on Solaris, so skip the error message, * unless in debug mode. */ if (fsm_debug) #endif fprintf(stderr, "Cannot map memory.\n"); return ulSmBiosLen; /*==0*/ } //now find the entry point for smbios for(iInc=0; iInc < (long)ulSize; iInc+=sizeof(int) ) { if (!memcmp((UCHAR *)(tdVirtualAddress+iInc),SMBIOS_STRING,sizeof(int))) { //cast entry point to a byte pointer pSmBios = (UCHAR *)(tdVirtualAddress+iInc); break; } } if (pSmBios == NULL) { fprintf(stderr, "Can't find SMBIOS address entry point.\n"); UnMapPhysicalMemory(tdVirtualAddress,ulSize); return ulSmBiosLen; } if (fsm_debug) { printf("Found pSmBios=%p tdV=%lx, inc=%x\n",pSmBios, tdVirtualAddress,iInc); // dump_buf("SMBIOS",pSmBios,64,1); } //now sum up the bytes in the table for checksum checking for(iInc=0; iInc= 18)) { switch(VirtualAddress[i+16] >>6) { case 0x00: *inc = 1; break; /* 1-byte bound*/ case 0x01: *inc = 4; break; /* 4-byte bound*/ case 0x02: *inc = 16; break; /*16-byte bound*/ default: break; /**inc = 1; above*/ } } rv = 0; break; } //Skip this record and go to the end of it. for (j = i+length; j < (int)SMBiosLen; j++) { if (VirtualAddress[j] == 0x00 && VirtualAddress[j+1] == 0x00) { i = j+2; break; } } } closeSmBios(VirtualAddress,SMBiosLen); return rv; } //getIPMI_struct /////////////////////////////////////////////////////////////////////////////// // get_MemDesc ////////////////////////////////////////////////////////////////////////////// // Name: get_MemDesc // Purpose: find Type 17 record to get the Memory Locator Description // Returns: 0 for success, or -1 if not found. // if success, the desc string will have valid data ////////////////////////////////////////////////////////////////////////////// int get_MemDesc(UCHAR array, UCHAR dimm, char *desc, int *psize) { UCHAR *VirtualAddress; ULONG SMBiosLen = 0; int length, j, i; int iarray, idimm; UCHAR Type; int rv = -1; char dimmstr[32]; char bankstr[32]; SMBiosLen = getSmBiosTables( &VirtualAddress); if ((SMBiosLen == 0) || !VirtualAddress) return rv; if (desc == NULL) return(-1); bankstr[0] = 0; dimmstr[0] = 0; if (fsm_debug) printf("get_MemDesc(%d,%d)\n",array,dimm); iarray = 0; idimm = 0; for(i=0; i<(int)SMBiosLen; ) { Type = VirtualAddress[i]; length = (int)VirtualAddress[i+1]; if (Type == 127) //end of table record break; else if (Type == 16) //Found a Memory Array */ { /* usually only one onboard memory array */ if (array == iarray) ; /*match*/ else iarray++; } else if (Type == 17) //Found a Memory Device */ { int bank, k, n, nStr, nBLoc, sz; if (dimm == idimm) { if (fsm_debug) { printf("Memory record %d.%d: ",iarray,idimm); for (j = i; j < (i+length+16); j++) { if (((j-i) % 16) == 0) printf("\n"); printf("%02x ",VirtualAddress[j]); } printf("\n"); } /* * Memory Device record * walk through strings to find Locator */ sz = VirtualAddress[i+12] + (VirtualAddress[i+13] << 8); /*Size*/ bank = VirtualAddress[i+15]; /*Set*/ nStr = VirtualAddress[i+16]; /*Locator string num*/ nBLoc = VirtualAddress[i+17]; /*BankLocator string num*/ if (fsm_debug) printf("bank=%d nStr=%d sz=%x\n",bank,nStr,sz); k = i + length; /*hdr len (usu 0x1B)*/ n = 1; /* string number index */ for (j = k; j < (int)SMBiosLen; j++) { if (VirtualAddress[j] == 0x00 && VirtualAddress[j-1] == 0x00) break; else if (VirtualAddress[j] == 0x00) { if (fsm_debug) printf("str[%d] = %s\n", n,&VirtualAddress[k]); if (n == nBLoc) { /*bank string*/ strcpy(bankstr,(char *)&VirtualAddress[k]); break; /*string number*/ } if (n == nStr) { strcpy(dimmstr,(char *)&VirtualAddress[k]); } n++; k = j + 1; } } if ((desc != NULL) && (j < (int)SMBiosLen)) { sprintf(desc,"%s/%s",bankstr,dimmstr); rv = 0; } else { /* have header, but not string */ char b; if ((idimm % 2) == 0) b = 'A'; else b = 'B'; sprintf(desc,"DIMM%d%c",bank,b); rv = 0; } *psize = sz; break; } else idimm++; /*else go to next dimm*/ } //Skip this record and go to the end of it. for (j = i+length; j < (int)SMBiosLen; j++) { if (VirtualAddress[j] == 0x00 && VirtualAddress[j+1] == 0x00) { i = j+2; break; } } } closeSmBios(VirtualAddress,SMBiosLen); /* fill in a default if error */ if ((rv != 0) && (desc != NULL)) sprintf(desc,"DIMM(%d)",dimm); return rv; } //get_MemDesc /////////////////////////////////////////////////////////////////////////////// // get_BiosVersion ////////////////////////////////////////////////////////////////////////////// // Name: get_BiosVersion // Purpose: finds Type 0 record and gets the BIOS ID string // Returns: -1 if not found and 0 if found. Param contains the string ////////////////////////////////////////////////////////////////////////////// int get_BiosVersion(char *bios_str) { #define BIOS_VERSION 0x05 //Specifies string number of BIOS Ver string UCHAR *VirtualAddress; ULONG SMBiosLen = 0; int recLength, j, i, k = 0, str_num; UCHAR recType; int rv = -1; SMBiosLen = getSmBiosTables( &VirtualAddress); if ((SMBiosLen == 0) || !VirtualAddress) return rv; i=0; while (i<(int)SMBiosLen) { recType = VirtualAddress[i]; recLength = (int)VirtualAddress[i+1]; // end of table record, error if (recType == 127) return -1; else if (recType == 0) { // BIOS Information (Type 0) record found //Set index j to the end of the formated part of record. j = i + recLength; //First grab string number of the BIOS Version string //This non-zero number specifies which string at the end //of the record is the BIOS Version string. str_num = VirtualAddress[i + BIOS_VERSION]; //Now skip over strings at the end of the record till we //get to the BIOS Version string for (str_num--;str_num > 0; str_num--) { //Skipping over one string at a time for (; VirtualAddress[j] != 0x00; j++); j++; //Advance index past EOS. } //Copy the BIOS Version string into buffer from caller. for (; VirtualAddress[j] != 0x00; j++) { bios_str[k++] = VirtualAddress[j]; } bios_str[k] = '\0'; rv = 0; break; /*exit while loop*/ } else { //Not a Type 0, so skip to the end of this structure for (j = i+recLength; j < (int)SMBiosLen; j++) { if (VirtualAddress[j] == 0x00 && VirtualAddress[j+1] == 0x00) { i = j+2; break; } } } } /*end while*/ closeSmBios(VirtualAddress,SMBiosLen); return rv; } int get_ChassisSernum(char *chs_str, char fdbg) { #define CHASSIS_SERNUM 0x07 //Specifies string number of BIOS Ver string UCHAR *VirtualAddress; ULONG SMBiosLen = 0; int recLength, j, i, k = 0, str_num; UCHAR recType; int rv = -1; SMBiosLen = getSmBiosTables( &VirtualAddress); if ((SMBiosLen == 0) || !VirtualAddress) return rv; i=0; while (i<(int)SMBiosLen) { recType = VirtualAddress[i]; recLength = (int)VirtualAddress[i+1]; if (recType == 127) return -1; // end of table record, error else if (recType == 3) { // Chassis Information (Type 3) record found j = i + recLength; str_num = VirtualAddress[i + CHASSIS_SERNUM]; for (str_num--;str_num > 0; str_num--) { for (; VirtualAddress[j] != 0x00; j++); j++; } for (; VirtualAddress[j] != 0x00; j++) { chs_str[k++] = VirtualAddress[j]; } chs_str[k] = '\0'; /* also copy Chassis Manuf */ j = i + recLength; memcpy(&chs_str[k+1],&VirtualAddress[j],20); rv = 0; break; /*exit while loop*/ } else { //Not a Type 0, so skip to the end of this structure for (j = i+recLength; j < (int)SMBiosLen; j++) { if (VirtualAddress[j] == 0x00 && VirtualAddress[j+1] == 0x00) { i = j+2; break; } } } } /*end while*/ closeSmBios(VirtualAddress,SMBiosLen); return rv; } int get_SystemGuid(UCHAR *guid) { UCHAR *VirtualAddress; ULONG SMBiosLen = 0; int recLength, i, j, n, k = 0; UCHAR recType; int rv = -1; SMBiosLen = getSmBiosTables( &VirtualAddress); if ((SMBiosLen == 0) || !VirtualAddress) return rv; i=0; while (i<(int)SMBiosLen) { recType = VirtualAddress[i]; recLength = (int)VirtualAddress[i+1]; // end of table record, error if (recType == 127) return -1; else if (recType == 1) { // System Board Information (Type 1) record found //Set index j to the end of the formated part of record. j = i + recLength; n = i + 8; /*UUID offset 8*/ //Copy the UUID string into buffer from caller. for (k = 0; k < 16; k++) { guid[k] = VirtualAddress[n + k]; } rv = 0; break; /*exit while loop*/ } else { //else skip to the end of this structure for (j = i+recLength; j < (int)SMBiosLen; j++) { if (VirtualAddress[j] == 0x00 && VirtualAddress[j+1] == 0x00) { i = j+2; break; } } } } /*end while*/ closeSmBios(VirtualAddress,SMBiosLen); return rv; } #ifdef WIN32 } #endif #ifdef COMP_BIN int main(int argc, char **argv) { char biosver[80]; UCHAR ifs,ver,sa,inc; int rv; int base; biosver[0] = 0; rv = get_BiosVersion(biosver); printf("get_BiosVersion rv=%d Ver=%s\n",rv,biosver); rv = get_IpmiStruct( &ifs, &ver, &sa, &base, &inc); printf("get_IpmiStruct rv=%d if=%02x ver=%02x sa=%02x base=%x, spacing=%d\n", rv, ifs,ver,sa,base,inc); rv = get_SystemGuid(biosver); printf("get_SystemGuid rv=%d Ver=%s\n",rv,biosver); // exit(rv); return(rv); } #endif /* end mem_if.c */