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

Generated on Tue Aug 28 01:55:31 2007 for SMBIOS Library by  doxygen 1.5.2