source: trunk/src/kernel32/mmapview.cpp@ 21916

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

Merge branch gcc-kmk to trunk.

File size: 16.3 KB
Line 
1/* $Id: mmapview.cpp,v 1.4 2003-12-29 12:29:20 sandervl Exp $ */
2
3/*
4 * Win32 Memory mapped file & view classes
5 *
6 * Copyright 1999-2003 Sander van Leeuwen (sandervl@xs4all.nl)
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
17 * TODO: Sharing memory mapped files between multiple processes
18 * TODO: Memory mapped files with views that extend the file (not 100% correct now)
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
21 * TODO: File maps for new files (must select an initial size)!
22 *
23 * Project Odin Software License can be found in LICENSE.TXT
24 *
25 */
26#include <os2win.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <win/virtual.h>
31#include <odincrt.h>
32#include <handlemanager.h>
33#include "mmap.h"
34#include "oslibdos.h"
35#include "oslibmem.h"
36#include "winimagepeldr.h"
37#include <custombuild.h>
38
39#define DBG_LOCALLOG DBG_mmapview
40#include "dbglocal.h"
41
42Win32MemMapView *Win32MemMapView::mapviews = NULL;
43
44//******************************************************************************
45// Class Win32MemMapView
46//
47// Memory map view
48//
49// View parent = memory map that contains the original memory map
50// View owner = duplicate memory map that created this view (can be NULL)
51//
52//******************************************************************************
53Win32MemMapView::Win32MemMapView(Win32MemMap *map, ULONG offset, ULONG size,
54 ULONG fdwAccess, Win32MemMap *owner)
55{
56 LPVOID viewaddr = (LPVOID)((ULONG)map->getMappingAddr()+offset);
57 ULONG accessAttr = 0;
58
59 errorState = 0;
60 mParentMap = map;
61 mOwnerMap = NULL;
62 pCOWBitmap = NULL;
63 mSize = size;
64 mOffset = offset;
65 mProcessId = GetCurrentProcessId();
66 pShareViewAddr = NULL;
67
68 switch(fdwAccess) {
69 case FILE_MAP_READ:
70 accessAttr = PAG_READ;
71 mfAccess = MEMMAP_ACCESS_READ;
72 break;
73 case FILE_MAP_ALL_ACCESS:
74 case FILE_MAP_WRITE:
75 case FILE_MAP_WRITE|FILE_MAP_READ:
76 accessAttr = (PAG_READ|PAG_WRITE);
77 mfAccess = MEMMAP_ACCESS_READ | MEMMAP_ACCESS_WRITE;
78 break;
79 case FILE_MAP_COPY:
80 accessAttr = (PAG_READ|PAG_WRITE);
81 mfAccess = MEMMAP_ACCESS_READ | MEMMAP_ACCESS_WRITE | MEMMAP_ACCESS_COPYONWRITE;
82 break;
83 }
84 //Named file mappings from other processes are always shared;
85 //map into our address space
86 if(map->getMemName() != NULL && map->getProcessId() != mProcessId)
87 {
88 //shared memory map, so map it into our address space
89 if(OSLibDosGetNamedSharedMem((LPVOID *)&viewaddr, map->getMemName()) != OSLIB_NOERROR)
90 {
91 dprintf(("new OSLibDosGetNamedSharedMem FAILED"));
92 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
93 errorState = 1;
94 return;
95 }
96 pShareViewAddr = viewaddr;
97 viewaddr = (LPVOID)((char *)viewaddr + mOffset);
98 }
99
100 //view == memory mapping for executable images (only used internally)
101 if(map->getImage()) {
102 pMapView = map->getMappingAddr();
103 pMapView = (LPVOID)((char *)pMapView + mOffset);
104 }
105 else {
106 if(mfAccess & MEMMAP_ACCESS_COPYONWRITE)
107 {
108 //A copy on write view is a private copy of the memory map
109 //The pages reflect the state of the original map until they are
110 //modified.
111 //We use guard pages to track access to the COW view.
112 pMapView = VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE|PAGE_GUARD);
113 if(pMapView == NULL) {
114 dprintf(("VirtualAlloc FAILED"));
115 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
116 errorState = 1;
117 return;
118 }
119 }
120 else
121 if(OSLibDosAliasMem(viewaddr, size, &pMapView, accessAttr) != OSLIB_NOERROR) {
122 dprintf(("new OSLibDosAliasMem FAILED"));
123 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
124 errorState = 1;
125 return;
126 }
127 }
128 //Allocate bitmap for all pages of a COW view to track which pages are
129 //shared and which are private (copy on write -> private page)
130 if(fdwAccess == FILE_MAP_COPY)
131 {
132 DWORD nrPages = mSize >> PAGE_SHIFT;
133 if(mSize & 0xFFF)
134 nrPages++;
135
136 int sizebitmap = nrPages/8 + 1;
137
138 pCOWBitmap = (char *)malloc(sizebitmap);
139 if(pCOWBitmap) {
140 memset(pCOWBitmap, 0, sizebitmap);
141 }
142 else DebugInt3();
143 }
144
145 dprintf(("Win32MemMapView::Win32MemMapView: created %x (alias for %x), size %d", pMapView, viewaddr, size));
146 mParentMap->AddRef();
147 mParentMap->AddView();
148
149 DosEnterCriticalSection(&globalmapcritsect);
150 Win32MemMapView *tmpview = mapviews;
151
152 if(tmpview == NULL || tmpview->getViewAddr() > pMapView) {
153 next = mapviews;
154 mapviews = this;
155 }
156 else {
157 while(tmpview->next) {
158 if(tmpview->next->getViewAddr() > pMapView) {
159 break;
160 }
161 tmpview = tmpview->next;
162 }
163 next = tmpview->next;
164 tmpview->next = this;
165 }
166 DosLeaveCriticalSection(&globalmapcritsect);
167
168 if(owner) {
169 mOwnerMap = owner;
170 mOwnerMap->AddRef();
171 mOwnerMap->AddView();
172 }
173}
174//******************************************************************************
175//******************************************************************************
176Win32MemMapView::~Win32MemMapView()
177{
178 if(errorState != 0)
179 return;
180
181 dprintf(("Win32MemMapView dtor: deleting view %x %x", mOffset, mSize));
182
183 if(mfAccess & MEMMAP_ACCESS_WRITE)
184 mParentMap->flushView(MMAP_FLUSHVIEW_ALL, mOffset, mSize);
185
186 //Don't free memory for executable image map views (only used internally)
187 if(!mParentMap->getImage())
188 OSLibDosFreeMem(pMapView);
189
190 if(pShareViewAddr) {
191 OSLibDosFreeMem(pShareViewAddr);
192 }
193 if(pCOWBitmap) free(pCOWBitmap);
194
195 DosEnterCriticalSection(&globalmapcritsect);
196 Win32MemMapView *view = mapviews;
197
198 if(view == this) {
199 mapviews = next;
200 }
201 else {
202 while(view->next) {
203 if(view->next == this)
204 break;
205 view = view->next;
206 }
207 if(view->next) {
208 view->next = next;
209 }
210 else dprintf(("Win32MemMapView::~Win32MemMapView: map not found!! (%x)", this));
211 }
212 DosLeaveCriticalSection(&globalmapcritsect);
213
214 mParentMap->RemoveView();
215 mParentMap->Release();
216 if(mOwnerMap) {
217 mOwnerMap->RemoveView();
218 mOwnerMap->Release();
219 }
220}
221//******************************************************************************
222// Win32MemMapView::markCOWPages
223//
224// Mark pages as private in the COW page bitmap
225//
226// Parameters:
227//
228// int startpage - start page
229// int nrpages - number of pages
230//
231//
232//******************************************************************************
233void Win32MemMapView::markCOWPages(int startpage, int nrpages)
234{
235 int viewpagestart, nrviewpages;
236
237 if(pCOWBitmap == NULL) {
238 //not a COW view; ignore
239 return;
240 }
241 //check if this page is part of our view
242 viewpagestart = mOffset >> PAGE_SHIFT;
243 nrviewpages = mSize >> PAGE_SHIFT;
244 if(mSize & 0xFFF)
245 nrviewpages++;
246
247 if(startpage < viewpagestart || startpage >= viewpagestart+nrviewpages) {
248 return; //outside this view
249 }
250 if(startpage + nrpages > viewpagestart + nrviewpages) {
251 nrpages -= ((startpage + nrpages) - (viewpagestart + nrviewpages));
252 }
253
254 for(int i=startpage;i<startpage+nrpages;i++) {
255 set_bit(i, pCOWBitmap);
256 }
257}
258//******************************************************************************
259// Win32MemMapView::changePageFlags
260//
261// Change the protection flags of our alias. Called when a range of pages has
262// been committed.
263//
264// Parameters:
265//
266// ULONG offset - offset in memory map (page aligned!)
267// ULONG size - size of committed page range
268// PAGEVIEW flags - page flags
269// PAGEVIEW_READONLY -> set page flags to readonly
270// PAGEVIEW_VIEW -> set page flags to view default
271// PAGEVIEW_GUARD -> set page flags of COW view to GUARD
272//
273// Returns:
274// TRUE - success
275// FALSE - failure
276//
277//******************************************************************************
278BOOL Win32MemMapView::changePageFlags(ULONG offset, ULONG size, PAGEVIEW flags)
279{
280 ULONG accessAttr = 0, rc;
281
282 //offset must be page aligned
283 if(offset & 0xFFF) {
284 DebugInt3();
285 return FALSE;
286 }
287
288 if( ( (mfAccess & MEMMAP_ACCESS_COPYONWRITE) && (flags != PAGEVIEW_GUARD) ) ||
289 ( (flags == PAGEVIEW_GUARD) && !(mfAccess & MEMMAP_ACCESS_COPYONWRITE) ) )
290 {
291 //PAGEVIEW_GUARD only applies to COW views
292 //PAGEVIEW_VIEW/READONLY does not apply to COW views
293 return TRUE;
294 }
295 if(mOffset + mSize <= offset || mOffset >= offset + size) {
296 return TRUE; //not part of this view
297 }
298 if(offset < mOffset) {
299 size -= mOffset - offset;
300 offset = mOffset;
301 }
302 if(mOffset + mSize < offset + size) {
303 size -= ((offset + size) - (mOffset + mSize));
304 }
305
306 if(flags == PAGEVIEW_READONLY) {
307 accessAttr = PAG_READ;
308 }
309 else
310 {//use view attributes
311 if(mfAccess & MEMMAP_ACCESS_READ) {
312 accessAttr |= PAG_READ;
313 }
314 if(mfAccess & MEMMAP_ACCESS_WRITE) {
315 accessAttr |= PAG_WRITE;
316 }
317 if(flags == PAGEVIEW_GUARD) {
318 accessAttr |= PAG_GUARD;
319 }
320 }
321
322 if(flags == PAGEVIEW_GUARD || (mfAccess & MEMMAP_ACCESS_COPYONWRITE))
323 {
324 DWORD startpage = (offset - mOffset) >> PAGE_SHIFT;
325 DWORD nrPages = size >> PAGE_SHIFT;
326 if(size & 0xFFF)
327 nrPages++;
328
329 //COW views need special treatment. If we are told to change any flags
330 //of the COW pages, then only the shared pages must be changed.
331 //So check each page if it is still shared.
332 for(int i=startpage;i<startpage+nrPages;i++)
333 {
334 if(!isCOWPage(i))
335 {//page is still shared, so set the guard flag
336 rc = OSLibDosSetMem((char *)pMapView+(offset - mOffset), PAGE_SIZE, accessAttr);
337 if(rc) {
338 dprintf(("Win32MemMapView::changePageFlags: OSLibDosSetMem %x %x %x failed with %d", (char *)pMapView+(offset - mOffset), size, accessAttr, rc));
339 return FALSE;
340 }
341 }
342 offset += PAGE_SIZE;
343 }
344 }
345 else {
346 rc = OSLibDosSetMem((char *)pMapView+(offset - mOffset), size, accessAttr);
347 if(rc) {
348 dprintf(("Win32MemMapView::changePageFlags: OSLibDosSetMem %x %x %x failed with %d", (char *)pMapView+(offset - mOffset), size, accessAttr, rc));
349 return FALSE;
350 }
351 }
352 return TRUE;
353}
354//******************************************************************************
355//******************************************************************************
356int Win32MemMapView::findViews(Win32MemMap *map, int nrViews,
357 Win32MemMapView *viewarray[])
358{
359 int i=0;
360
361 DosEnterCriticalSection(&globalmapcritsect);
362 Win32MemMapView *view = mapviews, *nextview;
363
364 if(view != NULL)
365 {
366 while(view && i < nrViews)
367 {
368 if(view->getParentMap() == map)
369 {
370 viewarray[i] = view;
371 i++;
372 }
373 view = view->next;
374 }
375 }
376 DosLeaveCriticalSection(&globalmapcritsect);
377 return i;
378}
379//******************************************************************************
380//******************************************************************************
381void Win32MemMapView::deleteViews(Win32MemMap *map)
382{
383 DosEnterCriticalSection(&globalmapcritsect);
384 Win32MemMapView *view = mapviews, *nextview;
385
386 if(view != NULL)
387 {
388 while(view)
389 {
390 nextview = view->next;
391 if(view->getParentMap() == map)
392 {
393 DosLeaveCriticalSection(&globalmapcritsect);
394 delete view;
395 DosEnterCriticalSection(&globalmapcritsect);
396 }
397 view = nextview;
398 }
399 }
400 DosLeaveCriticalSection(&globalmapcritsect);
401}
402//******************************************************************************
403//******************************************************************************
404// Win32MemMap::findMapByView
405//
406// Find the map of the view that contains the specified starting address
407// and has the specified access type
408//
409// Parameters:
410//
411// ULONG address - view address
412// ULONG *offset - address of ULONG that receives the offset
413// in the returned memory map
414// ULONG accessType - access type:
415// MEMMAP_ACCESS_READ
416// MEMMAP_ACCESS_WRITE
417// MEMMAP_ACCESS_EXECUTE
418//
419// Returns:
420// <> NULL - success, address of parent map object
421// NULL - failure
422//
423//******************************************************************************
424//******************************************************************************
425Win32MemMap *Win32MemMapView::findMapByView(ULONG address,
426 ULONG *offset,
427 ULONG accessType)
428{
429 Win32MemMap *map = NULL;
430 ULONG ulOffset;
431
432 if(mapviews == NULL) return NULL;
433
434 DosEnterCriticalSection(&globalmapcritsect);
435 Win32MemMapView *view = mapviews;
436 ULONG ulViewAddr;
437
438 if(!offset) offset = &ulOffset;
439
440 *offset = 0;
441
442 if(view != NULL)
443 {
444 do
445 {
446 ulViewAddr = (ULONG)view->getViewAddr();
447
448 // if ulViewAddr is > address, we've exhausted
449 // the sorted list already and can abort search.
450 if(ulViewAddr <= address)
451 {
452 if(ulViewAddr + view->getSize() > address &&
453 view->getAccessFlags() >= accessType)
454 {
455 *offset = view->getOffset() + (address - ulViewAddr);
456 goto success;
457 }
458
459 // Not found yet, continue search with next map
460 view = view->next;
461 }
462 else
463 {
464 // list is exhausted, abort loop
465 view = NULL;
466 }
467 }
468 while(view);
469
470 //failure if we get here
471 view = NULL;
472 }
473success:
474#ifdef DEBUG
475 if(view && !view->getParentMap()->isImageMap())
476 dprintf(("findMapByView %x %x -> %x off %x",
477 address,
478 accessType,
479 view->getViewAddr(),
480 *offset));
481#endif
482
483 if(view) {
484 //first look at the owner (duplicate map), then the real parent
485 map = view->getOwnerMap();
486 if(!map) map = view->getParentMap();
487
488 if(map) map->AddRef();
489 }
490
491 DosLeaveCriticalSection(&globalmapcritsect);
492
493 return map;
494}
495//******************************************************************************
496// Win32MemMap::findView
497//
498// Find the view that contains the specified starting address
499//
500// Parameters:
501//
502// LPVOID address - view address
503//
504// Returns:
505// <> NULL - success, address view object
506// NULL - failure
507//
508//******************************************************************************
509Win32MemMapView *Win32MemMapView::findView(ULONG address)
510{
511 ULONG ulViewAddr;
512
513 DosEnterCriticalSection(&globalmapcritsect);
514 Win32MemMapView *view = mapviews;
515
516 if(view != NULL) {
517 while(view) {
518 ulViewAddr = (ULONG)view->getViewAddr();
519 if(ulViewAddr <= address && ulViewAddr + view->getSize() > address)
520 {
521 break;
522 }
523 view = view->next;
524 }
525 }
526 DosLeaveCriticalSection(&globalmapcritsect);
527 return view;
528}
529//******************************************************************************
530//******************************************************************************
Note: See TracBrowser for help on using the repository browser.