source: trunk/src/kernel32/mmap.cpp

Last change on this file was 21916, checked in by dmik, 14 years ago

Merge branch gcc-kmk to trunk.

File size: 36.2 KB
RevLine 
[10572]1/* $Id: mmap.cpp,v 1.72 2004-04-06 14:20:20 sandervl Exp $ */
[657]2
3/*
[712]4 * Win32 Memory mapped file & view classes
[657]5 *
[9946]6 * Copyright 1999-2003 Sander van Leeuwen (sandervl@xs4all.nl)
[657]7 *
8 * NOTE: Memory mapping DOES NOT work when kernel-mode code causes
9 * a pagefault in the memory mapped object. (exceptions aren't
10 * dispatched to our exception handler until after the kernel mode
11 * call returns (too late))
12 *
13 * NOTE: Are apps allowed to change the protection flags of memory mapped pages?
14 * I'm assuming they aren't for now.
15 *
16 * TODO: Handles returned should be usable by all apis that accept file handles
[678]17 * TODO: Sharing memory mapped files between multiple processes
[2658]18 * TODO: Memory mapped files with views that extend the file (not 100% correct now)
[3609]19 * TODO: Suspend all threads when a page is committed (possible that another thread
20 * accesses the same memory before the page is read from disk
[4946]21 * TODO: File maps for new files (must select an initial size)!
[657]22 *
23 * Project Odin Software License can be found in LICENSE.TXT
24 *
25 */
26#include <os2win.h>
[705]27#include <stdio.h>
[657]28#include <stdlib.h>
29#include <string.h>
[21916]30#include <win/virtual.h>
[8203]31#include <odincrt.h>
[657]32#include <handlemanager.h>
33#include "mmap.h"
[712]34#include "oslibdos.h"
[8877]35#include "oslibmem.h"
[21916]36#include "winimagepeldr.h"
[21472]37#include <custombuild.h>
[9946]38#include "asmutil.h"
[657]39
[4496]40#define DBG_LOCALLOG DBG_mmap
[2802]41#include "dbglocal.h"
42
[21916]43static LPCSTR pszMMapSemName = MEMMAP_CRITSECTION_NAME;
[9946]44
[657]45//******************************************************************************
[8203]46//******************************************************************************
[21472]47void WIN32API SetCustomMMapSemName(LPSTR pszSemName)
[8659]48{
49 pszMMapSemName = pszSemName;
50}
51//******************************************************************************
52//******************************************************************************
[8203]53void InitializeMemMaps()
54{
[10542]55 if(globalmapcritsect.hevLock == 0) {
[8203]56 dprintf(("InitializeMemMaps -> create shared critical section"));
57 }
58 else {
59 dprintf(("InitializeMemMaps -> access shared critical section"));
60 }
[10543]61 DosAccessCriticalSection(&globalmapcritsect, pszMMapSemName);
[8203]62}
63//******************************************************************************
[657]64//******************************************************************************
[10377]65void FinalizeMemMaps()
66{
67 DosDeleteCriticalSection(&globalmapcritsect);
68}
69//******************************************************************************
70//******************************************************************************
[9971]71Win32MemMap::Win32MemMap(HANDLE hfile, ULONG size, ULONG fdwProtect, LPSTR lpszName)
[21472]72 : nrMappings(0), pMapping(NULL), mMapAccess(0), referenced(0),
[10073]73 image(0), pWriteBitmap(NULL), lpszFileName(NULL)
[657]74{
[8203]75 DosEnterCriticalSection(&globalmapcritsect);
[4796]76 next = memmaps;
77 memmaps = this;
[8203]78 DosLeaveCriticalSection(&globalmapcritsect);
[657]79
[9824]80 hMemFile = hOrgMemFile = hfile;
[657]81
[4796]82 mSize = size;
83 mProtFlags = fdwProtect;
[9946]84
[6468]85 mProcessId = GetCurrentProcessId();
[657]86
[4796]87 if(lpszName) {
88 lpszMapName = (char *)_smalloc(strlen(lpszName)+1);
89 strcpy(lpszMapName, lpszName);
90 }
91 else lpszMapName = NULL;
[9826]92 AddRef();
[657]93}
94//******************************************************************************
[1811]95//Map constructor used for executable image maps (only used internally)
[657]96//******************************************************************************
[1811]97Win32MemMap::Win32MemMap(Win32PeLdrImage *pImage, ULONG baseAddress, ULONG size)
[21472]98 : nrMappings(0), pMapping(NULL), mMapAccess(0), referenced(0),
[10073]99 image(0), pWriteBitmap(NULL), lpszFileName(NULL)
[1811]100{
[8203]101 DosEnterCriticalSection(&globalmapcritsect);
[4796]102 next = memmaps;
103 memmaps = this;
[8203]104 DosLeaveCriticalSection(&globalmapcritsect);
[1811]105
[9824]106 hMemFile = hOrgMemFile = -1;
[1811]107
[4796]108 mSize = size;
109 mProtFlags = PAGE_READWRITE;
[6468]110 mProcessId = GetCurrentProcessId();
[1811]111
[4796]112 pMapping = (LPVOID)baseAddress;
[1811]113
[4796]114 image = pImage;
115 lpszMapName= NULL;
[9826]116 AddRef();
[1811]117}
118//******************************************************************************
119//******************************************************************************
[8456]120BOOL Win32MemMap::Init(DWORD aMSize)
[657]121{
[4796]122 mapMutex.enter();
123 if(hMemFile != -1)
124 {
[10073]125 lpszFileName = (char *)_smalloc(MMAP_MAX_FILENAME_LENGTH);
126 if(HMGetFileNameFromHandle(hMemFile, lpszFileName, MMAP_MAX_FILENAME_LENGTH) == FALSE) {
127 return FALSE;
128 }
[3948]129#if 0
[6468]130 if(DuplicateHandle(GetCurrentProcess(), hMemFile, GetCurrentProcess(),
[770]131 &hMemFile, 0, FALSE, DUPLICATE_SAME_ACCESS) == FALSE)
[3948]132#else
[9911]133 if(HMDuplicateHandle(GetCurrentProcess(), hMemFile, GetCurrentProcess(),
134 &hMemFile, 0, FALSE, DUPLICATE_SAME_ACCESS) == FALSE)
[3948]135#endif
[4496]136 {
[4796]137 dprintf(("Win32MemMap::Init: DuplicateHandle failed!"));
138 goto fail;
[4496]139 }
[4796]140 mSize = SetFilePointer(hMemFile, 0, NULL, FILE_BEGIN);
141 mSize = SetFilePointer(hMemFile, 0, NULL, FILE_END);
142 if(mSize == -1) {
143 dprintf(("Win32MemMap::init: SetFilePointer failed to set pos end"));
144 goto fail;
145 }
[8456]146 if (mSize < aMSize)
147 {
148 dprintf(("Win32MemMap::init: file size %d, memory map size %d", mSize, aMSize));
149 //Froloff: Need to check if exist the possibility of file to memory
150 // mapping not from the beginning of file
151 mSize = SetFilePointer(hMemFile, aMSize, NULL, FILE_BEGIN);
152 // Commit filesize changes onto disk
153 SetEndOfFile(hMemFile);
154 }
155#if 0
[4796]156 //SvL: Temporary limitation of size (Warp Server Advanced doesn't allow
[2658]157 // one to reserve more than 450 MB (unless you override the virtual
158 // memory max limit) of continuous memory; (Warp 4 much less))
[4796]159 if(mSize > 64*1024*1024) {
160 mSize = 64*1024*1024;
161 }
[8456]162#endif
[4496]163 }
[10459]164 // Allocate the memory for the map right now
165 if(allocateMap() == FALSE) {
166 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
167 goto fail;
168 }
[712]169
[4796]170 dprintf(("CreateFileMappingA for file %x, prot %x size %d, name %s", hMemFile, mProtFlags, mSize, lpszMapName));
171 mapMutex.leave();
172 return TRUE;
[657]173fail:
[4796]174 mapMutex.leave();
175 return FALSE;
[657]176}
177//******************************************************************************
178//******************************************************************************
179Win32MemMap::~Win32MemMap()
180{
[4796]181 Win32MemMapView::deleteViews(this); //delete all views of our memory mapped file
[1432]182
[9946]183 dprintf(("Win32MemMap dtor: deleting map %x %x", pMapping, mSize));
[1481]184
[4796]185 mapMutex.enter();
[4496]186 if(lpszMapName) {
[4796]187 free(lpszMapName);
[4496]188 }
[10073]189 if(lpszFileName) {
190 free(lpszFileName);
191 }
[4796]192 if(pMapping && !image) {
[10459]193 dprintf(("Free map memory"));
[4796]194 if(lpszMapName) {
195 OSLibDosFreeMem(pMapping);
196 }
197 else VirtualFree(pMapping, 0, MEM_RELEASE);
[1432]198
[4796]199 pMapping = NULL;
200 }
201 if(hMemFile != -1) {
202 dprintf(("Win32MemMap dtor: closing memory file %x", hMemFile));
203 CloseHandle(hMemFile);
204 hMemFile = -1;
205 }
[9946]206 if(pWriteBitmap) free(pWriteBitmap);
207
[4796]208 mapMutex.leave();
[657]209
[8203]210 DosEnterCriticalSection(&globalmapcritsect);
[4796]211 Win32MemMap *map = memmaps;
[657]212
[4796]213 if(map == this) {
214 memmaps = next;
[4496]215 }
[4796]216 else {
217 while(map->next) {
218 if(map->next == this)
219 break;
220 map = map->next;
221 }
222 if(map->next) {
223 map->next = next;
224 }
225 else dprintf(("Win32MemMap::~Win32MemMap: map not found!! (%x)", this));
[4496]226 }
[8203]227 DosLeaveCriticalSection(&globalmapcritsect);
[657]228}
229//******************************************************************************
[9824]230//******************************************************************************
[9946]231int Win32MemMap::Release()
[9824]232{
[5011]233 dprintf(("Win32MemMap::Release %s (%d)", lpszMapName, referenced-1));
234 --referenced;
[9946]235 if(referenced == 0) {
[4496]236 delete this;
[9946]237 return 0;
[4796]238 }
[9946]239 return referenced;
[4496]240}
241//******************************************************************************
[9911]242// Win32MemMap::commitRange
243//
244// Commit a range of pages
245//
246// Parameters:
247//
248// ULONG ulFaultAddr - exception address
249// ULONG ulOffset - offset in memory map
250// BOOL fWriteAccess - TRUE -> write exception
251// FALSE -> read exception
252// int nrpages - number of pages
253//
254// Returns:
255// TRUE - success
256// FALSE - failure
257//
[678]258//******************************************************************************
[9911]259BOOL Win32MemMap::commitRange(ULONG ulFaultAddr, ULONG offset, BOOL fWriteAccess, int nrpages)
[678]260{
[9911]261 LPVOID lpPageFaultAddr = (LPVOID)((ULONG)pMapping + offset);
262 DWORD pageAddr = (DWORD)lpPageFaultAddr & ~0xFFF;
263
264 dprintf(("Win32MemMap::commitRange %x (faultaddr %x)", pageAddr, lpPageFaultAddr));
265
[21472]266 if(fWriteAccess)
[9911]267 {//writes are handled on a per-page basis
[21472]268 for(int i=0;i<nrpages;i++)
[9911]269 {
270 if(commitPage(ulFaultAddr, offset, TRUE, 1) == FALSE) {
271 dprintf(("Win32MemMap::commit: commitPage failed!!"));
272 return FALSE;
273 }
274 ulFaultAddr += PAGE_SIZE;
275 offset += PAGE_SIZE;
276 }
277 return TRUE;
278 }
279 else return commitPage(ulFaultAddr, offset, FALSE, nrpages);
280}
281//******************************************************************************
282// Win32MemMap::commitPage
283//
284// Handle a pagefault for a memory map
285//
286// Parameters:
287//
288// ULONG ulFaultAddr - exception address
289// ULONG ulOffset - offset in memory map
290// BOOL fWriteAccess - TRUE -> write exception
291// FALSE -> read exception
292// int nrpages - number of pages
293//
294// Returns:
295// TRUE - success
296// FALSE - failure
297//
[21472]298// NOTE:
[9911]299// We handle only one pages for write access!
300//
301// REMARKS:
302// We determine whether a page has been modified by checking it's protection flags
303// If the write flag is set, this means commitPage had to enable this due to a pagefault
304// (all pages are readonly until the app tries to write to it)
305//******************************************************************************
306BOOL Win32MemMap::commitPage(ULONG ulFaultAddr, ULONG offset, BOOL fWriteAccess, int nrpages)
307{
[699]308 MEMORY_BASIC_INFORMATION memInfo;
[712]309 LPVOID lpPageFaultAddr = (LPVOID)((ULONG)pMapping + offset);
310 DWORD pageAddr = (DWORD)lpPageFaultAddr & ~0xFFF;
[9946]311 DWORD oldProt, newProt, nrBytesRead;
[2053]312 int i;
[712]313
[678]314// mapMutex.enter();
[770]315
[1811]316 if(image) {
[9946]317 return image->commitPage(pageAddr, fWriteAccess);
[1811]318 }
[678]319
[21472]320 if(fWriteAccess && (mProtFlags & PAGE_WRITECOPY))
[9971]321 {//this is a COW map, call commitGuardPage to handle write faults
322 return commitGuardPage(ulFaultAddr, offset, fWriteAccess);
323 }
324
[712]325 dprintf(("Win32MemMap::commitPage %x (faultaddr %x)", pageAddr, lpPageFaultAddr));
[3782]326
[9946]327 //align at page boundary
328 offset &= ~0xFFF;
[3782]329
[9946]330 //If it's a write access violation and the view is readonly, then fail
331 if(fWriteAccess) {
332 Win32MemMapView *view = Win32MemMapView::findView(ulFaultAddr);
[21472]333 if(view) {
[9946]334 if(!(view->getAccessFlags() & MEMMAP_ACCESS_WRITE)) {
335 dprintf(("Write access for a readonly view!!"));
336 return FALSE;
337 }
338 }
339 else {
340 DebugInt3(); //can't happen
341 return FALSE;
342 }
343 }
344
345 int faultsize = nrpages*PAGE_SIZE;
346
[21472]347 if(fWriteAccess)
[9946]348 {//write access needs special care, so do that on a per page basis
349 dprintf(("Write access -> handle only one page"));
350 faultsize = PAGE_SIZE;
351 }
352
353 offset = pageAddr - (ULONG)pMapping;
354 if(offset + faultsize > mSize) {
355 faultsize = mSize - offset;
356 }
357
[21472]358 while(faultsize)
[9946]359 {
[4496]360 if(VirtualQuery((LPSTR)pageAddr, &memInfo, sizeof(MEMORY_BASIC_INFORMATION)) == 0) {
361 dprintf(("Win32MemMap::commitPage: VirtualQuery (%x,%x) failed for %x", pageAddr, nrpages*PAGE_SIZE));
362 goto fail;
363 }
364 memInfo.RegionSize = min(memInfo.RegionSize, faultsize);
365 //Only changes the state of the pages with the same attribute flags
366 //(returned in memInfo.RegionSize)
367 //If it's smaller than the mNrPages, it simply means one or more of the
[5027]368 //other pages have already been committed
[4496]369 if(!(memInfo.State & MEM_COMMIT))
370 {
371 if(VirtualAlloc((LPVOID)pageAddr, memInfo.RegionSize, MEM_COMMIT, PAGE_READWRITE) == FALSE) {
372 goto fail;
373 }
[9946]374
375 //Part of the memory map has been committed, so now we can change
376 //the protection flags of the aliases (DosSetMem fails for reserved pages)
377 updateViewPages(offset, memInfo.RegionSize, (fWriteAccess) ? PAGEVIEW_VIEW : PAGEVIEW_READONLY);
378
[21472]379 if(hMemFile != -1)
[9946]380 {//now read the page(s) from disk
381 DWORD size;
382
383 offset = pageAddr - (ULONG)pMapping;
384 size = memInfo.RegionSize;
385 if(offset + size > mSize) {
386 dprintf(("Adjusting size from %d to %d", size, mSize - offset));
387 size = mSize - offset;
388 }
389 if(SetFilePointer(hMemFile, offset, NULL, FILE_BEGIN) != offset) {
390 dprintf(("Win32MemMap::commitPage: SetFilePointer failed to set pos to %x", offset));
391 goto fail;
392 }
393 if(ReadFile(hMemFile, (LPSTR)pageAddr, size, &nrBytesRead, NULL) == FALSE) {
394 dprintf(("Win32MemMap::commitPage: ReadFile failed for %x", pageAddr));
395 goto fail;
396 }
397 if(nrBytesRead != size) {
398 dprintf(("Win32MemMap::commitPage: ReadFile didn't read all bytes for %x", pageAddr));
399 goto fail;
400 }
[21472]401 }
402 //We set the protection flags to PAGE_READONLY, unless this pagefault
[9946]403 //was due to a write access
404 //This way we can track dirty pages which need to be flushed to
405 //disk when FlushViewOfFile is called or the map is closed.
[21472]406 if(!fWriteAccess)
[9946]407 {
408 if(VirtualProtect((LPVOID)pageAddr, memInfo.RegionSize, PAGE_READONLY, &oldProt) == FALSE) {
409 dprintf(("VirtualProtect %x %x PAGE_READWRITE failed with %d!!", pageAddr, memInfo.RegionSize, GetLastError()));
410 goto fail;
411 }
[4496]412 }
[21472]413 else
[9946]414 {//make these pages as dirty
415 ULONG startPage = (pageAddr - (ULONG)pMapping) >> PAGE_SHIFT;
416 ULONG nrPages = memInfo.RegionSize >> PAGE_SHIFT;
417
418 if(memInfo.RegionSize & 0xFFF)
419 nrPages++;
420
421 dprintf(("Mark %d page(s) starting at %x as dirty", nrPages, pageAddr));
422 markDirtyPages(startPage, nrPages);
[9971]423
[21472]424 //Write access means that the next time the corresponding COW page
[9971]425 //is touched, we need to reload it. So set the GUARD flags.
426 updateViewPages(offset, memInfo.RegionSize, PAGEVIEW_GUARD);
[5027]427 }
[9946]428 }
[21472]429 else
430 if(fWriteAccess)
[9946]431 {
432 //mark these pages as dirty
433 ULONG startPage = (pageAddr - (ULONG)pMapping) >> PAGE_SHIFT;
434 ULONG nrPages = memInfo.RegionSize >> PAGE_SHIFT;
435
436 if(memInfo.RegionSize & 0xFFF)
437 nrPages++;
438
439 dprintf(("Mark %d page(s) starting at %x as dirty", nrPages, pageAddr));
440 markDirtyPages(startPage, nrPages);
441
442 //and turn on a write access
443 if(VirtualProtect((LPVOID)pageAddr, memInfo.RegionSize, PAGE_READWRITE, &oldProt) == FALSE) {
444 dprintf(("VirtualProtect %x %x PAGE_READWRITE failed with %d!!", pageAddr, memInfo.RegionSize, GetLastError()));
[5027]445 goto fail;
446 }
[9946]447 //Now change all the aliased pages according to their view protection flags
448 updateViewPages(offset, memInfo.RegionSize, PAGEVIEW_VIEW);
[9971]449
[21472]450 //Write access means that the next time the corresponding COW page
[9971]451 //is touched, we need to reload it. So set the GUARD flags.
452 updateViewPages(offset, memInfo.RegionSize, PAGEVIEW_GUARD);
[4496]453 }
454 faultsize -= memInfo.RegionSize;
455 pageAddr += memInfo.RegionSize;
[678]456 }
[3259]457
[678]458// mapMutex.leave();
459 return TRUE;
460fail:
461// mapMutex.leave();
462 return FALSE;
463}
464//******************************************************************************
[9911]465// Win32MemMap::commitGuardPage
466//
[9971]467// Handle a guard page exception for a copy-on-write view (one page only)
[9911]468//
469// Parameters:
470//
471// ULONG ulFaultAddr - exception address
472// ULONG ulOffset - offset in memory map
473// BOOL fWriteAccess - TRUE -> write exception
474// FALSE -> read exception
475//
476// Returns:
477// TRUE - success
478// FALSE - failure
479//
480//******************************************************************************
481BOOL Win32MemMap::commitGuardPage(ULONG ulFaultAddr, ULONG ulOffset, BOOL fWriteAccess)
482{
[9946]483 MEMORY_BASIC_INFORMATION memInfo;
484 BOOL ret;
485 DWORD pageAddr = ulFaultAddr & ~0xFFF;
486 DWORD dwNewProt, dwOldProt;
487
488 dprintf(("Win32MemMap::commitGuardPage %x (faultaddr %x)", pageAddr, ulFaultAddr));
489
490 //align at page boundary
491 ulOffset &= ~0xFFF;
492
[9971]493 //commit a single page in the original mapping (pretend read access)
494 ret = commitPage(ulFaultAddr, ulOffset, FALSE, 1);
495 if(ret == FALSE) {
496 DebugInt3();
497 goto fail;
498 }
499 //clear PAGE_GUARD flag, since this page must now be made accessible
500 dwNewProt = mProtFlags & (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY);
501 if(dwNewProt & PAGE_WRITECOPY) {
502 dwNewProt |= PAGE_GUARD;
503 }
504 dwNewProt &= ~PAGE_GUARD;
505 if(VirtualProtect((LPVOID)pageAddr, PAGE_SIZE, dwNewProt, &dwOldProt) == FALSE) {
506 dprintf(("Win32MemMap::commitGuardPage: VirtualProtect %x 0x1000 %x failed!!", pageAddr, dwNewProt));
507 goto fail;
508 }
509 //copy the data from the original mapping to the COW page
510 memcpy((LPVOID)pageAddr, (char *)pMapping+ulOffset, PAGE_SIZE);
511
[21472]512 if(fWriteAccess)
[9971]513 {//copy on write; mark pages as private
514 DWORD startpage = (ulOffset) >> PAGE_SHIFT;
515
516 dprintf(("Win32MemMap::commitGuardPage: write access -> mark page as private"));
517
518 Win32MemMapView *view = Win32MemMapView::findView(ulFaultAddr);
[21472]519 if(view)
[9971]520 {
521 view->markCOWPages(startpage, 1);
522 }
523 else DebugInt3(); //oh, oh
524 }
[21472]525 else
[9971]526 {//read access; must set the map + all views to READONLY to track write access
527
528 dprintf(("Win32MemMap::commitGuardPage: read access -> set page as READONLY in map + all views"));
529
530 //first the memory map page
531 if(VirtualProtect((LPVOID)pageAddr, PAGE_SIZE, PAGE_READONLY, &dwOldProt) == FALSE) {
532 dprintf(("Win32MemMap::commitGuardPage: VirtualProtect %x 0x1000 %x failed!!", pageAddr, dwNewProt));
533 goto fail;
534 }
535 //now for any views that exist
536 updateViewPages(ulOffset, PAGE_SIZE, PAGEVIEW_READONLY);
537 }
538
[9946]539 return TRUE;
540fail:
541 return FALSE;
[9911]542}
543//******************************************************************************
[9946]544// Win32MemMap::updateViewPages
[9911]545//
[9946]546// Update the page flags of all views
[9911]547//
548// Parameters:
549//
550// ULONG offset - offset in memory map
[9946]551// ULONG size - range size
552// PAGEVIEW flags - page flags
553// PAGEVIEW_READONLY -> set page flags to readonly
554// PAGEVIEW_VIEW -> set page flags to view default
[9971]555// PAGEVIEW_GUARD -> set page flags of COW view to GUARD
[9911]556//
557// Returns:
558// TRUE - success
559// FALSE - failure
560//
561//******************************************************************************
[9946]562BOOL Win32MemMap::updateViewPages(ULONG offset, ULONG size, PAGEVIEW flags)
[9911]563{
[9946]564 Win32MemMapView **views = (Win32MemMapView **)alloca(sizeof(Win32MemMapView*)*nrMappings);
[10572]565 int localmaps;
566
[21472]567 if(views)
[9946]568 {
[10572]569 localmaps = Win32MemMapView::findViews(this, nrMappings, views);
[21472]570 if(localmaps <= nrMappings)
[9946]571 {
[21472]572 for(int i=0;i<localmaps;i++)
[9946]573 {
574 views[i]->changePageFlags(offset, size, flags);
[21472]575 }
[9946]576 }
[10572]577 else {
578 dprintf(("unexpected number of views %d vs %d", localmaps, nrMappings));
579 DebugInt3(); //oh, oh
580 }
[9946]581 }
582 return TRUE;
[9911]583}
584//******************************************************************************
[9946]585// Win32MemMap::invalidatePages
[9824]586//
[9946]587// Invalidate map pages. (called by WriteFile)
[9824]588//
589// Parameters:
590//
[9946]591// ULONG offset - offset in memory map
592// ULONG size - invalid range size
[9824]593//
594// Returns:
595// TRUE - success
596// FALSE - failure
597//
[678]598//******************************************************************************
[9946]599BOOL Win32MemMap::invalidatePages(ULONG offset, ULONG size)
[657]600{
[9946]601 ULONG diff = offset & 0xFFF;
602 BOOL ret;
[9824]603
[9946]604 offset &= ~0xFFF;
605 size += diff;
[657]606
[9946]607 dprintf(("Win32MemMap::invalidatePages %x %x", offset, size));
608 ret = VirtualFree((LPSTR)pMapping + offset, size, MEM_DECOMMIT);
609 if(ret == FALSE) {
610 dprintf(("ERROR: Win32MemMap::invalidatePages: VirtualFree failed!!"));
[4796]611 }
[21472]612 //invalidate all shared COW pages too by setting the GUARD flag
[9971]613 //(which forces a resync the next time the app touches them)
614 return updateViewPages(offset, size, PAGEVIEW_GUARD);
[657]615}
616//******************************************************************************
[9946]617// Win32MemMap::allocateMap
618//
619// Allocate memory for the map if not yet already done.
620//
621// Returns:
622// FALSE - success
623// TRUE - failure
624//
[657]625//******************************************************************************
[9946]626BOOL Win32MemMap::allocateMap()
[657]627{
[4796]628 ULONG fAlloc = 0;
[657]629
[4796]630 fAlloc = MEM_RESERVE;
[657]631
[4796]632 //Memory has already been allocated for executable image maps (only used internally)
[21472]633 if(!pMapping && nrMappings == 0)
[9824]634 {//if not mapped, reserve/commit entire view
[4796]635 //SvL: Always read/write access or else ReadFile will crash once we
636 // start committing pages.
637 // This is most likely an OS/2 bug and doesn't happen in Aurora
[708]638 // when allocating memory with the PAG_ANY bit set. (without this
639 // flag it will also crash)
[9824]640 //NOTE: If this is ever changed, then we must update setProtFlags!!!!
[21472]641
[8121]642 //All named file mappings are shared (files & memory only)
643 if(lpszMapName) {
[4796]644 pMapping = VirtualAllocShared(mSize, fAlloc, PAGE_READWRITE, lpszMapName);
645 }
646 else {
647 pMapping = VirtualAlloc(0, mSize, fAlloc, PAGE_READWRITE);
648 }
649 if(pMapping == NULL) {
[9946]650 dprintf(("Win32MemMap::mapViewOfFile: VirtualAlloc %x %x failed!", mSize, fAlloc));
[4796]651 goto fail;
652 }
653 //Windows NT seems to commit memory for memory maps, regardsless of the SEC_COMMIT flag
654 if((hMemFile == -1 && !image)) {//commit memory
655 VirtualAlloc(pMapping, mSize, MEM_COMMIT, PAGE_READWRITE);
656 }
[9946]657
658 DWORD nrPages = mSize >> PAGE_SHIFT;
659 if(mSize & 0xFFF)
660 nrPages++;
661
[4796]662 if(hMemFile != -1 && (mProtFlags & SEC_COMMIT)) {
[9946]663 commitPage((ULONG)pMapping, 0, FALSE, nrPages);
664 }
665 //Allocate bitmap for all pages to keep track of write access (file maps only)
666 //Necessary for FlushViewOfFile.
667 if(hMemFile != -1) {
668 int sizebitmap = nrPages/8 + 1;
[4796]669
[9946]670 pWriteBitmap = (char *)_smalloc(sizebitmap);
671 if(pWriteBitmap == NULL) {
672 DebugInt3();
673 goto fail;
674 }
675 memset(pWriteBitmap, 0, sizebitmap);
[4796]676 }
[4496]677 }
[9946]678 return TRUE;
679
[21472]680fail:
[9946]681 return FALSE;
682}
683//******************************************************************************
684// Win32MemMap::mapViewOfFile
685//
686// Map the view identified by addr
687//
688// Parameters:
689//
690// ULONG size - size of view
691// ULONG offset - offset in memory map
692// ULONG fdwAccess - access flags
693// FILE_MAP_WRITE, FILE_MAP_READ, FILE_MAP_COPY
694// FILE_MAP_ALL_ACCESS
695//
[21472]696//
[9946]697// Returns:
698// <>NULL - success, view address
699// NULL - failure
700//
701//******************************************************************************
702LPVOID Win32MemMap::mapViewOfFile(ULONG size, ULONG offset, ULONG fdwAccess)
703{
704 DWORD processId = GetCurrentProcessId();
705
706 mapMutex.enter();
707 ULONG memFlags = (mProtFlags & (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY));
708 Win32MemMapView *mapview;
709
710 //@@@PH: if(fdwAccess & ~(FILE_MAP_WRITE|FILE_MAP_READ|FILE_MAP_COPY))
711 // Docs say FILE_MAP_ALL_ACCESS is same as FILE_MAP_WRITE. Doesn't match reality though.
712 if(fdwAccess & ~FILE_MAP_ALL_ACCESS)
713 goto parmfail;
714 if((fdwAccess & FILE_MAP_WRITE) && !(mProtFlags & PAGE_READWRITE))
715 goto parmfail;
[21472]716 // CreateFileMapping docs say that PAGE_WRITECOPY is equivalent to PAGE_READONLY and thus
717 // is okay for FILE_MAP_READ too
718 if((fdwAccess & FILE_MAP_READ) && !(mProtFlags & (PAGE_READWRITE|PAGE_READONLY|PAGE_WRITECOPY)))
[9946]719 goto parmfail;
[21472]720
[9946]721 if (fdwAccess != FILE_MAP_ALL_ACCESS)
722 if((fdwAccess & FILE_MAP_COPY) && !(mProtFlags & PAGE_WRITECOPY))
723 goto parmfail;
724
725 if(offset+size > mSize && (!(fdwAccess & FILE_MAP_WRITE) || hMemFile == -1))
726 goto parmfail;
727
728 //SvL: TODO: Doesn't work for multiple views
729 if(offset+size > mSize) {
730 mSize = offset+size;
731 }
732
[8913]733 mapview = new Win32MemMapView(this, offset, (size == 0) ? (mSize - offset) : size, fdwAccess);
[4796]734 if(mapview == NULL) {
735 goto fail;
[4496]736 }
[4796]737 if(mapview->everythingOk() == FALSE) {
[9946]738 dprintf(("Win32MemMap::mapViewOfFile: !mapview->everythingOk"));
[4796]739 delete mapview;
[4496]740 goto fail;
741 }
[4796]742 mapMutex.leave();
[9946]743 SetLastError(ERROR_SUCCESS);
[4796]744 return mapview->getViewAddr();
[2264]745
[657]746parmfail:
[9946]747 dprintf(("Win32MemMap::mapViewOfFile: invalid parameter (ERROR_ACCESS_DENIED)"));
748 //NT4 SP6 returns ERROR_ACCESS_DENIED for most invalid parameters
749 SetLastError(ERROR_ACCESS_DENIED);
[657]750fail:
[4796]751 mapMutex.leave();
752 return 0;
[657]753}
754//******************************************************************************
[9946]755// Win32MemMap::unmapViewOfFile
756//
757// Unmap the view identified by addr
758//
759// Parameters:
760//
761// LPVOID addr - view address; doesn't need to be the address
762// returned by MapViewOfFile(Ex) (as MSDN clearly says);
763// can be any address within the view range
764//
765// Returns:
766// TRUE - success
767// FALSE - failure
768//
769//******************************************************************************
770BOOL Win32MemMap::unmapViewOfFile(LPVOID addr)
771{
772 Win32MemMapView *view;
773
774 dprintf(("Win32MemMap::unmapViewOfFile %x (nrmaps=%d)", addr, nrMappings));
775 mapMutex.enter();
776
777 if(nrMappings == 0)
778 goto fail;
779
780 view = Win32MemMapView::findView((ULONG)addr);
[21472]781 if(view == NULL)
[9946]782 goto fail;
783
784 delete view;
785
786 mapMutex.leave();
787
788 SetLastError(ERROR_SUCCESS);
789 return TRUE;
790fail:
791 mapMutex.leave();
792 SetLastError(ERROR_INVALID_ADDRESS);
793 return FALSE;
794}
795//******************************************************************************
[699]796//We determine whether a page has been modified by checking it's protection flags
797//If the write flag is set, this means commitPage had to enable this due to a pagefault
[712]798//(all pages are readonly until the app tries to modify the contents of the page)
[699]799//
[657]800//TODO: Are apps allowed to change the protection flags of memory mapped pages?
801// I'm assuming they aren't for now.
802//******************************************************************************
[9911]803BOOL Win32MemMap::flushView(ULONG viewaddr, ULONG offset, ULONG cbFlush)
[657]804{
[9946]805 ULONG nrBytesWritten, size, accessflags, oldProt;
806 Win32MemMapView *view;
807 int i;
[657]808
[9946]809 dprintf(("Win32MemMap::flushView: %x %x", (ULONG)pMapping+offset, cbFlush));
810
[1811]811 if(image) //no flushing for image maps
[9946]812 return TRUE;
[1811]813
[9946]814 if(hMemFile == -1)
815 goto success; //TODO: Return an error here?
[657]816
[9946]817 if(offset > mSize)
818 goto parmfail;
819
[21472]820 if(viewaddr != MMAP_FLUSHVIEW_ALL)
[9946]821 {
822 view = Win32MemMapView::findView(viewaddr);
823 if(nrMappings == 0 || view == NULL) {
824 DebugInt3(); //should never happen
825 goto parmfail;
826 }
827 accessflags = view->getAccessFlags();
828 }
829 else {
830 //force a flush to disk; only those pages marked dirty are flushed anyway
831 accessflags = FILE_MAP_WRITE;
832 }
833 //If the view is readonly or copy on write, then the flush is ignored
834 if(!(accessflags & MEMMAP_ACCESS_WRITE) || (accessflags & MEMMAP_ACCESS_COPYONWRITE))
835 {
836 dprintf(("Readonly or Copy-On-Write memory map -> ignore flush"));
837 //this is not a failure; NT4 SP6 returns success
838 goto success;
839 }
840
[770]841 if(cbFlush == 0)
[9946]842 cbFlush = mSize;
[684]843
[9946]844 if(offset + cbFlush > mSize) {
845 cbFlush -= (offset + cbFlush - mSize);
846 }
[657]847
[9946]848 //Check the write page bitmap for dirty pages and write them to disk
[21472]849 while(cbFlush)
[9946]850 {
851 int startPage = offset >> PAGE_SHIFT;
852 size = PAGE_SIZE;
[657]853
[9946]854 if(isDirtyPage(startPage))
855 {
856 if(size > cbFlush) {
857 size = cbFlush;
858 }
859 dprintf(("Win32MemMap::flushView for offset %x, size %d", offset, size));
[684]860
[9946]861 if(SetFilePointer(hMemFile, offset, NULL, FILE_BEGIN) != offset) {
862 dprintf(("Win32MemMap::flushView: SetFilePointer failed to set pos to %x", offset));
863 goto fail;
864 }
865 if(WriteFile(hMemFile, (LPSTR)((ULONG)pMapping + offset), size, &nrBytesWritten, NULL) == FALSE) {
866 dprintf(("Win32MemMap::flushView: WriteFile failed for %x", (ULONG)pMapping + offset));
867 goto fail;
868 }
869 if(nrBytesWritten != size) {
870 dprintf(("Win32MemMap::flushView: WriteFile didn't write all bytes for %x", (ULONG)pMapping + offset));
871 goto fail;
872 }
873 clearDirtyPages(startPage, 1);
[678]874
[9946]875 //We've just flushed the page to disk, so we need to track future writes
876 //again; Set page to readonly (first memory map, then alias(es))
877 if(VirtualProtect((LPVOID)((ULONG)pMapping + offset), size, PAGE_READONLY, &oldProt) == FALSE) {
878 dprintf(("VirtualProtect %x %x PAGE_READWRITE failed with %d!!", (ULONG)pMapping + offset, size, GetLastError()));
879 goto fail;
880 }
881 updateViewPages(offset, size, PAGEVIEW_READONLY);
882 }
[712]883
[9946]884 if(cbFlush < size)
885 break;
[712]886
[9946]887 cbFlush -= size;
888 offset += size;
[657]889 }
[684]890success:
[9946]891 SetLastError(ERROR_SUCCESS);
[657]892 return TRUE;
[9946]893
[657]894parmfail:
895 SetLastError(ERROR_INVALID_PARAMETER);
896 return FALSE;
897fail:
898 return FALSE;
899}
900//******************************************************************************
901//******************************************************************************
902Win32MemMap *Win32MemMap::findMap(LPSTR lpszName)
903{
[1432]904 if(lpszName == NULL)
[4496]905 return NULL;
[1432]906
[8203]907 DosEnterCriticalSection(&globalmapcritsect);
[657]908 Win32MemMap *map = memmaps;
909
910 if(map != NULL) {
[4496]911 while(map) {
912 if(map->lpszMapName && !strcmp(map->lpszMapName, lpszName))
913 break;
914 map = map->next;
915 }
[657]916 }
[9824]917 if(map) map->AddRef();
918
[8203]919 DosLeaveCriticalSection(&globalmapcritsect);
[1481]920 if(!map) dprintf(("Win32MemMap::findMap: couldn't find map %s", lpszName));
[657]921 return map;
922}
923//******************************************************************************
924//******************************************************************************
[9824]925Win32MemMap *Win32MemMap::findMapByFile(HANDLE hFile)
926{
[10073]927 DWORD processId = GetCurrentProcessId();
928 char szFileName[260];
929
[9824]930 if(hFile == -1)
931 return NULL;
932
[10073]933 if(HMGetFileNameFromHandle(hFile, szFileName, sizeof(szFileName)) == FALSE)
934 return NULL;
935
[9824]936 DosEnterCriticalSection(&globalmapcritsect);
937 Win32MemMap *map = memmaps;
938
[21472]939 if(map != NULL)
[9824]940 {
941 while(map) {
[10073]942 //TODO: we currently don't support sharing file maps between processes
[21472]943 if(map->mProcessId == processId && map->lpszFileName)
[10073]944 {
[21667]945 if(!strcmp(map->lpszFileName, szFileName) && map->getFileHandle() != hFile)
[10073]946 break;
947 }
[9824]948 map = map->next;
949 }
950 }
951 if(map) map->AddRef();
952 DosLeaveCriticalSection(&globalmapcritsect);
[9946]953 if(!map) dprintf2(("Win32MemMap::findMapByFile: couldn't find map with file handle %x", hFile));
[9824]954 return map;
955}
956//******************************************************************************
957//******************************************************************************
[657]958Win32MemMap *Win32MemMap::findMap(ULONG address)
959{
[8203]960 DosEnterCriticalSection(&globalmapcritsect);
[657]961 Win32MemMap *map = memmaps;
962
963 if(map != NULL) {
[4496]964 while(map) {
965 if(map->pMapping && (ULONG)map->pMapping <= address &&
[657]966 (ULONG)map->pMapping + map->mSize > address)
[4496]967 {
968 break;
969 }
970 map = map->next;
971 }
[657]972 }
[9824]973 if(map) map->AddRef();
[8203]974 DosLeaveCriticalSection(&globalmapcritsect);
[657]975 return map;
976}
977//******************************************************************************
978//******************************************************************************
[678]979void Win32MemMap::deleteAll()
980{
[9946]981 Win32MemMap *map, *nextmap;
[6468]982 DWORD processId = GetCurrentProcessId();
[1432]983
984 //delete all maps created by this process
[8203]985 DosEnterCriticalSection(&globalmapcritsect);
[9946]986
987startdeleteviews:
988 map = memmaps;
[1432]989 while(map) {
[9946]990 map->AddRef(); //make sure it doesn't get deleted
[712]991
[9946]992 //delete all views created by this process for this map
993 Win32MemMapView::deleteViews(map);
[712]994
[9946]995 nextmap = map->next;
[4496]996
[9946]997 //map->Release can delete multiple objects (duplicate memory map), so make
998 //sure our nextmap pointer remains valid by increasing the refcount
999 if(nextmap) nextmap->AddRef();
1000 map->Release();
[712]1001
[9946]1002 if(nextmap && nextmap->Release() == 0) {
1003 //oops, nextmap was just deleted and is no longer valid
1004 //can't continue from here, so let's start again
1005 dprintf(("oops, nextmap is invalid -> start again (1)"));
1006 goto startdeleteviews;
1007 }
[712]1008
[9946]1009 map = nextmap;
1010 }
1011startdelete:
1012 map = memmaps;
1013 while(map) {
1014 nextmap = map->next;
[21472]1015 if(map->getProcessId() == processId)
[9946]1016 {
1017 //delete map can delete multiple objects (duplicate memory map), so make
1018 //sure our nextmap pointer remains valid by increasing the refcount
1019 if(nextmap) nextmap->AddRef();
1020 delete map;
[1481]1021
[9946]1022 if(nextmap && nextmap->Release() == 0) {
1023 //oops, nextmap was just deleted and is no longer valid
1024 //can't continue from here, so let's start again
1025 dprintf(("oops, nextmap is invalid -> start again (2)"));
1026 goto startdelete;
1027 }
1028 }
1029 map = nextmap;
[712]1030 }
[8203]1031 DosLeaveCriticalSection(&globalmapcritsect);
[712]1032}
1033//******************************************************************************
[9946]1034// Win32MemMapView::markDirtyPages
[9824]1035//
[9946]1036// Mark pages as dirty (changed) in the write page bitmap
[9824]1037//
1038// Parameters:
1039//
[9946]1040// int startpage - start page
1041// int nrpages - number of pages
[9824]1042//
1043//
1044//******************************************************************************
[9946]1045void Win32MemMap::markDirtyPages(int startpage, int nrpages)
[712]1046{
[9946]1047 if(pWriteBitmap == NULL) return; //can be NULL for non-file mappings
[9824]1048
[9946]1049 for(int i=startpage;i<startpage+nrpages;i++) {
1050 set_bit(i, pWriteBitmap);
[4496]1051 }
[712]1052}
1053//******************************************************************************
[9946]1054// Win32MemMapView::clearDirtyPages
[9824]1055//
[9946]1056// Mark pages as clean in the write page bitmap
[9824]1057//
1058// Parameters:
1059//
[9946]1060// int startpage - start page
1061// int nrpages - number of pages
[9824]1062//
1063//
[712]1064//******************************************************************************
[9946]1065void Win32MemMap::clearDirtyPages(int startpage, int nrpages)
[712]1066{
[9946]1067 if(pWriteBitmap == NULL) return; //can be NULL for non-file mappings
[9824]1068
[9946]1069 for(int i=startpage;i<startpage+nrpages;i++) {
1070 clear_bit(i, pWriteBitmap);
[4496]1071 }
[712]1072}
1073//******************************************************************************
1074//******************************************************************************
Note: See TracBrowser for help on using the repository browser.