Main Page | Namespace List | Class Hierarchy | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

Memory_Windows.cpp

Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  * vim:expandtab:autoindent:tabstop=4:shiftwidth=4:filetype=c:cindent:textwidth=0:
00003  *
00004  * Copyright (C) 2005 Dell Inc.
00005  *  by Michael Brown <Michael_E_Brown@dell.com>
00006  * Licensed under the Open Software License version 2.1
00007  *
00008  * Alternatively, you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published
00010  * by the Free Software Foundation; either version 2 of the License,
00011  * or (at your option) any later version.
00012 
00013  * This program is distributed in the hope that it will be useful, but
00014  * WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00016  * See the GNU General Public License for more details.
00017  */
00018 
00019 #define LIBSMBIOS_SOURCE
00020 #include "MemoryImpl.h"
00021 #include "miniddk.h"
00022 
00023 // include this last
00024 #include "smbios/message.h"
00025 
00026 using namespace std;
00027 
00028 namespace memory
00029 {
00030     MemoryFactoryImpl::MemoryFactoryImpl()
00031     {
00032         setParameter("memFile", "\\Device\\PhysicalMemory");
00033     }
00034 
00035 
00036     // Non Member Functions
00037     NtOpenSectionPtr NtOpenSection = NULL;
00038     NtClosePtr NtClose = NULL;
00039     NtMapViewOfSectionPtr NtMapViewOfSection = NULL;
00040     NtUnmapViewOfSectionPtr NtUnmapViewOfSection = NULL;
00041     RtlInitUnicodeStringPtr RtlInitUnicodeString = NULL;
00042     ZwSystemDebugControlPtr ZwSystemDebugControl = NULL;
00043 
00044     EnumSystemFirmwareTablesPtr EnumSystemFirmwareTables = NULL;
00045     GetSystemFirmwareTablePtr GetSystemFirmwareTable = NULL;
00046     u8 * CBlockBuffer = NULL;
00047     u8 * EBlockBuffer = NULL;
00048 
00049     int LoadNtdllFuncs(void)
00050     {
00051         HMODULE hNtdll;
00052         HMODULE hKerneldll;
00053 
00054         hNtdll = GetModuleHandle(L"ntdll.dll");
00055         hKerneldll = GetModuleHandle( L"kernel32.dll" );
00056         if (!(hNtdll && hKerneldll))
00057             return FALSE;
00058 
00059         // should be available across all systems no matter our priv level
00060         NtOpenSection        = (NtOpenSectionPtr) GetProcAddress(hNtdll, "NtOpenSection");
00061         NtClose              = (NtClosePtr) GetProcAddress(hNtdll, "NtClose");
00062         NtMapViewOfSection   = (NtMapViewOfSectionPtr) GetProcAddress(hNtdll, "NtMapViewOfSection");
00063         NtUnmapViewOfSection = (NtUnmapViewOfSectionPtr) GetProcAddress(hNtdll, "NtUnmapViewOfSection");
00064         RtlInitUnicodeString = (RtlInitUnicodeStringPtr) GetProcAddress(hNtdll, "RtlInitUnicodeString");
00065         ZwSystemDebugControl = (ZwSystemDebugControlPtr) GetProcAddress(hNtdll, "ZwSystemDebugControl");
00066 
00067         // Donot return false since these APIs are only available on W2K3 SP1 and higher.
00068         // returning FALSE will break libsmbios on W2K and W2K3( no SP )
00069         EnumSystemFirmwareTables = (EnumSystemFirmwareTablesPtr) GetProcAddress(hKerneldll, "EnumSystemFirmwareTables");
00070         GetSystemFirmwareTable = (GetSystemFirmwareTablePtr) GetProcAddress(hKerneldll, "GetSystemFirmwareTable");
00071 
00072         return TRUE;
00073     }
00074 
00075     // MEB: fatal issue
00076     // MEB: pass in filename.  use getParameterString("memfile") as the
00077     // parameter. makes it possible to unit test against file.
00078     HANDLE OpenMemAccess(void)
00079     {
00080         UNICODE_STRING usDevmem;
00081         OBJECT_ATTRIBUTES oaAttrs;
00082         NTSTATUS status;
00083         HANDLE hPhysMem = NULL;
00084 
00085         RtlInitUnicodeString(&usDevmem, L"\\device\\physicalmemory");
00086         InitializeObjectAttributes(&oaAttrs, &usDevmem, OBJ_CASE_INSENSITIVE, NULL, NULL);
00087         status = NtOpenSection(&hPhysMem, SECTION_MAP_READ, &oaAttrs);
00088 
00089         if (!NT_SUCCESS(status))
00090         {
00091             hPhysMem = NULL;
00092         }
00093 
00094         return hPhysMem;
00095     }
00096 
00097     int CloseMemAccess(HANDLE hPhysMem)
00098     {
00099         NTSTATUS status;
00100 
00101         status = NtClose(hPhysMem);
00102 
00103         if (!NT_SUCCESS(status))
00104         {
00105             return FALSE;
00106         }
00107 
00108         return TRUE;
00109     }
00110 
00111     int MapMem(HANDLE hPhysMem, PVOID pBaseAddr, PDWORD pPhysAddr, PDWORD pSize)
00112     {
00113         NTSTATUS status;
00114         PHYSICAL_ADDRESS paAddr;
00115 
00116         * (DWORD *) pBaseAddr = (DWORD) NULL;
00117         paAddr.HighPart = 0;
00118         paAddr.LowPart = *pPhysAddr;
00119         status = NtMapViewOfSection(hPhysMem, NtCurrentProcess(), (PVOID *) pBaseAddr, 0L,
00120                                     *pSize, &paAddr, pSize, ViewShare, 0, PAGE_READONLY);
00121 
00122         if (!NT_SUCCESS(status))
00123         {
00124             hPhysMem = NULL;
00125             return FALSE;
00126         }
00127 
00128         *pPhysAddr = paAddr.LowPart;
00129         return TRUE;
00130     }
00131 
00132     int UnMapMem(PVOID pBaseAddr)
00133     {
00134         NTSTATUS status;
00135 
00136         status = NtUnmapViewOfSection(NtCurrentProcess(), pBaseAddr);
00137 
00138         if (!NT_SUCCESS(status))
00139         {
00140             return FALSE;
00141         }
00142 
00143         return TRUE;
00144     }
00145 
00146     static BOOL setPrivilege(LPCTSTR privilegeName, BOOL enable)
00147     {
00148         HANDLE              hToken;
00149         HANDLE              hCurrentProcess;
00150         DWORD               err;
00151         TOKEN_PRIVILEGES    tkprivs;
00152 
00153         // Get a token for this process.
00154         hCurrentProcess = GetCurrentProcess();
00155         if(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
00156         {
00157             // Get the LUID for the security privilege.
00158             LookupPrivilegeValue(NULL, privilegeName, &tkprivs.Privileges[0].Luid);
00159             tkprivs.PrivilegeCount = 1;  // one privilege to set
00160             tkprivs.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
00161 
00162             // Get the security privilege for this process.
00163             AdjustTokenPrivileges(hToken, FALSE, &tkprivs, 0, (PTOKEN_PRIVILEGES)NULL, NULL);
00164         }
00165         // get the error returned by AdjustTokenPrivileges() or OpenProcessToken()
00166         err = GetLastError();
00167 
00168         return err == ERROR_SUCCESS;
00169     }
00170 
00171     void readPhysicalMemoryMap( HANDLE hPhysMem, u8 *buffer, u64 offset, unsigned int length)
00172     {
00173         unsigned int totalBytes = 0;
00174         unsigned int originalSize = length;
00175         unsigned int pageSize = length;
00176 
00177         if(!hPhysMem)
00178         {
00179             throw AccessErrorImpl( _("Handle to physical memory was not set or could not be opened.") );
00180         }
00181 
00182         if(0 == buffer)
00183         {
00184             throw AccessErrorImpl( _("Error accessing buffer.") );
00185         }
00186 
00187         while(totalBytes != originalSize )
00188         {
00189             DWORD BaseAddr;
00190             DWORD PhysAddr = static_cast<DWORD>(offset);
00191 
00192             // OK, first get the page that has the requested offset.  This
00193             // function will round-down physaddr to the nearest page boundary,
00194             // and the pageSize will change to the size of the page
00195             if (!MapMem(hPhysMem, (PVOID) &BaseAddr, &PhysAddr, reinterpret_cast<PDWORD>(&pageSize)))
00196             {
00197                 throw AccessErrorImpl( _("Error mapping physical memory.") );
00198             }
00199 
00200             //Find the index into the page based on requested offset
00201             unsigned int index =  static_cast<DWORD>(offset) - PhysAddr;
00202 
00203             // Only continue to copy if the index is within the pageSize and
00204             // we still need bytes
00205             while( index < pageSize && totalBytes != originalSize)
00206             {
00207                 u64 tmp = BaseAddr + index; // extra tmp to satisfy vc++ /w4
00208                 buffer[totalBytes] = *reinterpret_cast<u8 *>(tmp);
00209                 index++;
00210                 totalBytes++;
00211             }
00212 
00213             u64 tmp = BaseAddr; // extra tmp to satisfy vc++ /w4
00214             if (!UnMapMem(reinterpret_cast<PVOID>(tmp)))
00215             {
00216                 throw AccessErrorImpl( _("Error unmapping physical memory."));
00217             }
00218 
00219             // If the inner while loop exited due to the end of the page, we
00220             // need to update the offset and the remaining size and map
00221             // another page
00222             offset = PhysAddr + index;
00223             pageSize = originalSize-totalBytes;
00224         }
00225     }
00226 
00227     void readPhysicalMemoryDebugSysctl( u8 *buffer, u64 offset, unsigned int length)
00228     {
00229         MEM_STRUCT mem;
00230         NTSTATUS status;
00231         ULONG bytesReturned;
00232 
00233         memset(&mem, 0, sizeof(mem));
00234         mem.Addr = (DWORD)offset;
00235         mem.pBuf = buffer;
00236         mem.NumBytes = (DWORD)length;
00237 
00238         status = ZwSystemDebugControl(DebugSysReadPhysicalMemory,
00239                                       &mem,
00240                                       sizeof(mem),
00241                                       0,
00242                                       0,
00243                                       &bytesReturned);
00244         if (! NT_SUCCESS(status) )
00245         {
00246             throw AccessErrorImpl( _("Could not use Debug Sysctl to read physical memory."));
00247         }
00248     }
00249 
00250 
00251     // breaks for requests that span boundaries
00252     // breaks for requests of mem at offset > 1M
00253     void enumSystemFirmwareTables( u8 *buffer, u64 offset, unsigned int length)
00254     {
00255         // The following code is added for faster access. Otherewise, it takes libsmbios
00256         // approx 3-5minutes to get Service tag etc.
00257         if( offset >= 0xC0000 && offset < 0xDFFFF && CBlockBuffer != NULL )
00258         {
00259             memset( buffer, 0, length );
00260             memcpy( buffer, &CBlockBuffer[(DWORD)offset - 0xC0000], length );
00261             return;
00262         }
00263 
00264         // The following code is added for faster access. Otherewise, it takes libsmbios
00265         // approx 3-5minutes to get Service tag etc.
00266         if( offset >= 0xE0000 && offset < 0xFFFFF && EBlockBuffer != NULL ) // optimization
00267         {
00268             memset( buffer, 0, length );
00269             memcpy( buffer, &EBlockBuffer[(DWORD)offset - 0xE0000], length );
00270             return;
00271         }
00272 
00273         DWORD iSignature = 0x46000000 | 0x00490000 | 0x00005200 | 0x0000004D ; //FIRM
00274 
00275         // pass NULL to EnumSystemFirmwareTables. This will return the size needed.
00276         unsigned int iBufferSizeNeeded = EnumSystemFirmwareTables( iSignature, NULL, 0 );
00277         if( iBufferSizeNeeded > 0 )
00278         {
00279             DWORD * FirmwareTableEnumBuffer = new DWORD[iBufferSizeNeeded];
00280             ULONG i=0;
00281             for( i = 0; i < iBufferSizeNeeded; i++ )  // zero out the buffer. Otherwise, the function sometimes fails.
00282             {
00283                 FirmwareTableEnumBuffer[i] = 0;
00284             }
00285 
00286             EnumSystemFirmwareTables( iSignature, FirmwareTableEnumBuffer, iBufferSizeNeeded );
00287             DWORD FirmwareTableNameToUse = 0;
00288             for( i = 0; i < iBufferSizeNeeded; i++ )
00289             {
00290                 if( FirmwareTableEnumBuffer[i] > 0 && FirmwareTableEnumBuffer[i] <= offset && offset <= FirmwareTableEnumBuffer[i] + 128 * 1024 )
00291                 {
00292                     FirmwareTableNameToUse = FirmwareTableEnumBuffer[i];
00293                 }
00294             }
00295             delete [] FirmwareTableEnumBuffer;
00296 
00297             if( FirmwareTableNameToUse == 0 )
00298             {
00299                 throw AccessErrorImpl( _("Could not locate a table which can be used.") );
00300             }
00301 
00302 
00303             iBufferSizeNeeded = GetSystemFirmwareTable( iSignature, FirmwareTableNameToUse, NULL, 0 );
00304             if( iBufferSizeNeeded > 0 )
00305             {
00306                 u8 * MemoryAtRequestedOffSet = NULL;
00307                 MemoryAtRequestedOffSet = new u8[iBufferSizeNeeded];
00308                 if( MemoryAtRequestedOffSet != NULL )
00309                 {
00310                     memset( MemoryAtRequestedOffSet, 0, iBufferSizeNeeded );
00311                     memset( buffer, 0, length );
00312                     GetSystemFirmwareTable( iSignature, FirmwareTableNameToUse, MemoryAtRequestedOffSet, iBufferSizeNeeded );
00313                     memcpy( buffer, &MemoryAtRequestedOffSet[(DWORD)offset - FirmwareTableNameToUse], length );
00314                     if( FirmwareTableNameToUse == 0xC0000 )
00315                     {
00316                         CBlockBuffer = MemoryAtRequestedOffSet; // save for later use
00317                     }
00318                     else
00319                         if( FirmwareTableNameToUse == 0xE0000 )
00320                         {
00321                             EBlockBuffer = MemoryAtRequestedOffSet; // save for later use
00322                         }
00323                         else
00324                         {
00325                             delete MemoryAtRequestedOffSet;
00326                         }
00327                 }
00328                 else
00329                 {
00330                     throw AccessErrorImpl( _("Failed to allocate memory for Firmware table.") );
00331                 }
00332             }
00333             else
00334             {
00335                 throw AccessErrorImpl( _("GetSystemFirmwareTable returned 0 for table length.") );
00336             }
00337         }
00338         else
00339         {
00340             throw AccessErrorImpl( _("EnumSystemFirmwareTables returned 0 for table size.") );
00341         }
00342     }
00343 
00344 
00345     MemoryOsSpecific::MemoryOsSpecific( const string )
00346             : IMemory()
00347     {
00348         HANDLE hPhysMem = NULL;
00349 
00350         if (!LoadNtdllFuncs())
00351         {
00352             //printf("Could not find functions in dll!\n");
00353             //TODO change exception
00354             throw AccessErrorImpl( _("Could not load ntdll functions!") );
00355         }
00356 
00357         // DEBUG privs necessary to call systemdebugcontrol
00358         // set here to avoid big delays in mem read loops
00359         setPrivilege(SE_DEBUG_NAME, 1);
00360 
00361         hPhysMem = OpenMemAccess();
00362 
00363         osData = static_cast<HANDLE *>(hPhysMem);
00364     }
00365 
00366     MemoryOsSpecific::~MemoryOsSpecific()
00367     {
00368         HANDLE hPhysMem = static_cast<HANDLE>(osData);
00369 
00370         // NEVER THROW FROM DESTRUCTOR!
00371         if(hPhysMem)
00372         {
00373             CloseMemAccess(hPhysMem); // if this fails, something bad happened,
00374         }                       // but there isn't anything we can do about it.
00375 
00376         delete CBlockBuffer;   // safe to delete NULL
00377         CBlockBuffer = NULL;
00378 
00379         delete EBlockBuffer;   // safe to delete NULL
00380         EBlockBuffer = NULL;
00381     }
00382 
00383     void MemoryOsSpecific::fillBuffer( u8 *buffer, u64 offset, unsigned int length) const
00384     {
00385 
00386         if( EnumSystemFirmwareTables && GetSystemFirmwareTable )
00387         {
00388             // Use the newer W2K3 SP1 APIs if they are available.
00389             // If we first call readPhysicalMemoryMap and readPhysicalMemoryDebugSysctl
00390             // and then call enumSystemFirmwareTables, the new GetSystemFirmwareTable API
00391             // of W2K3 SP1 returns invalid data.
00392             enumSystemFirmwareTables( buffer, offset, length );
00393         }
00394         else
00395         {
00396             HANDLE hPhysMem = static_cast<HANDLE>(osData);
00397             if(hPhysMem)
00398             {
00399                 readPhysicalMemoryMap( hPhysMem, buffer, offset, length );
00400             }
00401             else
00402             {
00403                 readPhysicalMemoryDebugSysctl( buffer, offset, length );
00404             }
00405         }
00406     }
00407 
00408     u8 MemoryOsSpecific::getByte( u64 offset ) const
00409     {
00410         u8 value = 0;
00411         fillBuffer(&value, offset, 1);
00412         return value;
00413     }
00414 
00415     void MemoryOsSpecific::putByte( u64 , u8 ) const
00416     {
00417         throw smbios::NotImplementedImpl( _("writing to physical memory is not implemented on Windows yet.") );
00418     }
00419 }

Generated on Wed Apr 11 16:25:10 2007 for SMBIOS Library by doxygen 1.3.5