Copy msimg32
[reactos.git] / reactos / ntoskrnl / mm / section.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id$
20 *
21 * PROJECT: ReactOS kernel
22 * FILE: ntoskrnl/mm/section.c
23 * PURPOSE: Implements section objects
24 * PROGRAMMER: David Welch (welch@mcmail.com)
25 * UPDATE HISTORY:
26 * Created 22/05/98
27 */
28
29 /* INCLUDES *****************************************************************/
30
31 #include <ntoskrnl.h>
32 #define NDEBUG
33 #include <internal/debug.h>
34
35 #include <reactos/exeformat.h>
36
37 /* TYPES *********************************************************************/
38
39 typedef struct
40 {
41 PSECTION_OBJECT Section;
42 PMM_SECTION_SEGMENT Segment;
43 ULONG Offset;
44 BOOLEAN WasDirty;
45 BOOLEAN Private;
46 }
47 MM_SECTION_PAGEOUT_CONTEXT;
48
49 /* GLOBALS *******************************************************************/
50
51 POBJECT_TYPE EXPORTED MmSectionObjectType = NULL;
52
53 static GENERIC_MAPPING MmpSectionMapping = {
54 STANDARD_RIGHTS_READ | SECTION_MAP_READ | SECTION_QUERY,
55 STANDARD_RIGHTS_WRITE | SECTION_MAP_WRITE,
56 STANDARD_RIGHTS_EXECUTE | SECTION_MAP_EXECUTE,
57 SECTION_ALL_ACCESS};
58
59 #define TAG_MM_SECTION_SEGMENT TAG('M', 'M', 'S', 'S')
60 #define TAG_SECTION_PAGE_TABLE TAG('M', 'S', 'P', 'T')
61
62 #define PAGE_FROM_SSE(E) ((E) & 0xFFFFF000)
63 #define PFN_FROM_SSE(E) ((E) >> PAGE_SHIFT)
64 #define SHARE_COUNT_FROM_SSE(E) (((E) & 0x00000FFE) >> 1)
65 #define IS_SWAP_FROM_SSE(E) ((E) & 0x00000001)
66 #define MAX_SHARE_COUNT 0x7FF
67 #define MAKE_SSE(P, C) ((P) | ((C) << 1))
68 #define SWAPENTRY_FROM_SSE(E) ((E) >> 1)
69 #define MAKE_SWAP_SSE(S) (((S) << 1) | 0x1)
70
71 /* FUNCTIONS *****************************************************************/
72
73 /* Note: Mmsp prefix denotes "Memory Manager Section Private". */
74
75 /*
76 * FUNCTION: Waits in kernel mode up to ten seconds for an MM_PAGEOP event.
77 * ARGUMENTS: PMM_PAGEOP which event we should wait for.
78 * RETURNS: Status of the wait.
79 */
80 static NTSTATUS
81 MmspWaitForPageOpCompletionEvent(PMM_PAGEOP PageOp)
82 {
83 LARGE_INTEGER Timeout;
84 #ifdef __GNUC__ /* TODO: Use other macro to check for suffix to use? */
85
86 Timeout.QuadPart = -100000000LL; // 10 sec
87 #else
88
89 Timeout.QuadPart = -100000000; // 10 sec
90 #endif
91
92 return KeWaitForSingleObject(&PageOp->CompletionEvent, 0, KernelMode, FALSE, &Timeout);
93 }
94
95
96 /*
97 * FUNCTION: Sets the page op completion event and releases the page op.
98 * ARGUMENTS: PMM_PAGEOP.
99 * RETURNS: In shorter time than it takes you to even read this
100 * description, so don't even think about geting a mug of coffee.
101 */
102 static void
103 MmspCompleteAndReleasePageOp(PMM_PAGEOP PageOp)
104 {
105 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
106 MmReleasePageOp(PageOp);
107 }
108
109
110 /*
111 * FUNCTION: Waits in kernel mode indefinitely for a file object lock.
112 * ARGUMENTS: PFILE_OBJECT to wait for.
113 * RETURNS: Status of the wait.
114 */
115 static NTSTATUS
116 MmspWaitForFileLock(PFILE_OBJECT File)
117 {
118 return KeWaitForSingleObject(&File->Lock, 0, KernelMode, FALSE, NULL);
119 }
120
121
122 VOID
123 MmFreePageTablesSectionSegment(PMM_SECTION_SEGMENT Segment)
124 {
125 ULONG i;
126 if (Segment->Length > NR_SECTION_PAGE_TABLES * PAGE_SIZE)
127 {
128 for (i = 0; i < NR_SECTION_PAGE_TABLES; i++)
129 {
130 if (Segment->PageDirectory.PageTables[i] != NULL)
131 {
132 ExFreePool(Segment->PageDirectory.PageTables[i]);
133 }
134 }
135 }
136 }
137
138 VOID
139 MmFreeSectionSegments(PFILE_OBJECT FileObject)
140 {
141 if (FileObject->SectionObjectPointer->ImageSectionObject != NULL)
142 {
143 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
144 PMM_SECTION_SEGMENT SectionSegments;
145 ULONG NrSegments;
146 ULONG i;
147
148 ImageSectionObject = (PMM_IMAGE_SECTION_OBJECT)FileObject->SectionObjectPointer->ImageSectionObject;
149 NrSegments = ImageSectionObject->NrSegments;
150 SectionSegments = ImageSectionObject->Segments;
151 for (i = 0; i < NrSegments; i++)
152 {
153 if (SectionSegments[i].ReferenceCount != 0)
154 {
155 DPRINT1("Image segment %d still referenced (was %d)\n", i,
156 SectionSegments[i].ReferenceCount);
157 KEBUGCHECK(0);
158 }
159 MmFreePageTablesSectionSegment(&SectionSegments[i]);
160 }
161 ExFreePool(ImageSectionObject->Segments);
162 ExFreePool(ImageSectionObject);
163 FileObject->SectionObjectPointer->ImageSectionObject = NULL;
164 }
165 if (FileObject->SectionObjectPointer->DataSectionObject != NULL)
166 {
167 PMM_SECTION_SEGMENT Segment;
168
169 Segment = (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->
170 DataSectionObject;
171
172 if (Segment->ReferenceCount != 0)
173 {
174 DPRINT1("Data segment still referenced\n");
175 KEBUGCHECK(0);
176 }
177 MmFreePageTablesSectionSegment(Segment);
178 ExFreePool(Segment);
179 FileObject->SectionObjectPointer->DataSectionObject = NULL;
180 }
181 }
182
183 VOID
184 MmLockSectionSegment(PMM_SECTION_SEGMENT Segment)
185 {
186 ExAcquireFastMutex(&Segment->Lock);
187 }
188
189 VOID
190 MmUnlockSectionSegment(PMM_SECTION_SEGMENT Segment)
191 {
192 ExReleaseFastMutex(&Segment->Lock);
193 }
194
195 VOID
196 MmSetPageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
197 ULONG Offset,
198 ULONG Entry)
199 {
200 PSECTION_PAGE_TABLE Table;
201 ULONG DirectoryOffset;
202 ULONG TableOffset;
203
204 if (Segment->Length <= NR_SECTION_PAGE_TABLES * PAGE_SIZE)
205 {
206 Table = (PSECTION_PAGE_TABLE)&Segment->PageDirectory;
207 }
208 else
209 {
210 DirectoryOffset = PAGE_TO_SECTION_PAGE_DIRECTORY_OFFSET(Offset);
211 Table = Segment->PageDirectory.PageTables[DirectoryOffset];
212 if (Table == NULL)
213 {
214 Table =
215 Segment->PageDirectory.PageTables[DirectoryOffset] =
216 ExAllocatePoolWithTag(NonPagedPool, sizeof(SECTION_PAGE_TABLE),
217 TAG_SECTION_PAGE_TABLE);
218 if (Table == NULL)
219 {
220 KEBUGCHECK(0);
221 }
222 memset(Table, 0, sizeof(SECTION_PAGE_TABLE));
223 DPRINT("Table %x\n", Table);
224 }
225 }
226 TableOffset = PAGE_TO_SECTION_PAGE_TABLE_OFFSET(Offset);
227 Table->Entry[TableOffset] = Entry;
228 }
229
230
231 ULONG
232 MmGetPageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
233 ULONG Offset)
234 {
235 PSECTION_PAGE_TABLE Table;
236 ULONG Entry;
237 ULONG DirectoryOffset;
238 ULONG TableOffset;
239
240 DPRINT("MmGetPageEntrySection(Offset %x)\n", Offset);
241
242 if (Segment->Length <= NR_SECTION_PAGE_TABLES * PAGE_SIZE)
243 {
244 Table = (PSECTION_PAGE_TABLE)&Segment->PageDirectory;
245 }
246 else
247 {
248 DirectoryOffset = PAGE_TO_SECTION_PAGE_DIRECTORY_OFFSET(Offset);
249 Table = Segment->PageDirectory.PageTables[DirectoryOffset];
250 DPRINT("Table %x\n", Table);
251 if (Table == NULL)
252 {
253 return(0);
254 }
255 }
256 TableOffset = PAGE_TO_SECTION_PAGE_TABLE_OFFSET(Offset);
257 Entry = Table->Entry[TableOffset];
258 return(Entry);
259 }
260
261 VOID
262 MmSharePageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
263 ULONG Offset)
264 {
265 ULONG Entry;
266
267 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
268 if (Entry == 0)
269 {
270 DPRINT1("Entry == 0 for MmSharePageEntrySectionSegment\n");
271 KEBUGCHECK(0);
272 }
273 if (SHARE_COUNT_FROM_SSE(Entry) == MAX_SHARE_COUNT)
274 {
275 DPRINT1("Maximum share count reached\n");
276 KEBUGCHECK(0);
277 }
278 if (IS_SWAP_FROM_SSE(Entry))
279 {
280 KEBUGCHECK(0);
281 }
282 Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) + 1);
283 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
284 }
285
286 BOOLEAN
287 MmUnsharePageEntrySectionSegment(PSECTION_OBJECT Section,
288 PMM_SECTION_SEGMENT Segment,
289 ULONG Offset,
290 BOOLEAN Dirty,
291 BOOLEAN PageOut)
292 {
293 ULONG Entry;
294 BOOLEAN IsDirectMapped = FALSE;
295
296 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
297 if (Entry == 0)
298 {
299 DPRINT1("Entry == 0 for MmUnsharePageEntrySectionSegment\n");
300 KEBUGCHECK(0);
301 }
302 if (SHARE_COUNT_FROM_SSE(Entry) == 0)
303 {
304 DPRINT1("Zero share count for unshare\n");
305 KEBUGCHECK(0);
306 }
307 if (IS_SWAP_FROM_SSE(Entry))
308 {
309 KEBUGCHECK(0);
310 }
311 Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) - 1);
312 /*
313 * If we reducing the share count of this entry to zero then set the entry
314 * to zero and tell the cache the page is no longer mapped.
315 */
316 if (SHARE_COUNT_FROM_SSE(Entry) == 0)
317 {
318 PFILE_OBJECT FileObject;
319 PBCB Bcb;
320 SWAPENTRY SavedSwapEntry;
321 PFN_TYPE Page;
322 BOOLEAN IsImageSection;
323 ULONG FileOffset;
324
325 FileOffset = Offset + Segment->FileOffset;
326
327 IsImageSection = Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
328
329 Page = PFN_FROM_SSE(Entry);
330 FileObject = Section->FileObject;
331 if (FileObject != NULL &&
332 !(Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
333 {
334
335 if ((FileOffset % PAGE_SIZE) == 0 &&
336 (Offset + PAGE_SIZE <= Segment->RawLength || !IsImageSection))
337 {
338 NTSTATUS Status;
339 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
340 IsDirectMapped = TRUE;
341 Status = CcRosUnmapCacheSegment(Bcb, FileOffset, Dirty);
342 if (!NT_SUCCESS(Status))
343 {
344 DPRINT1("CcRosUnmapCacheSegment failed, status = %x\n", Status);
345 KEBUGCHECK(0);
346 }
347 }
348 }
349
350 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
351 if (SavedSwapEntry == 0)
352 {
353 if (!PageOut &&
354 ((Segment->Flags & MM_PAGEFILE_SEGMENT) ||
355 (Segment->Characteristics & IMAGE_SCN_MEM_SHARED)))
356 {
357 /*
358 * FIXME:
359 * Try to page out this page and set the swap entry
360 * within the section segment. There exist no rmap entry
361 * for this page. The pager thread can't page out a
362 * page without a rmap entry.
363 */
364 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
365 }
366 else
367 {
368 MmSetPageEntrySectionSegment(Segment, Offset, 0);
369 if (!IsDirectMapped)
370 {
371 MmReleasePageMemoryConsumer(MC_USER, Page);
372 }
373 }
374 }
375 else
376 {
377 if ((Segment->Flags & MM_PAGEFILE_SEGMENT) ||
378 (Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
379 {
380 if (!PageOut)
381 {
382 if (Dirty)
383 {
384 /*
385 * FIXME:
386 * We hold all locks. Nobody can do something with the current
387 * process and the current segment (also not within an other process).
388 */
389 NTSTATUS Status;
390 Status = MmWriteToSwapPage(SavedSwapEntry, Page);
391 if (!NT_SUCCESS(Status))
392 {
393 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n", Status);
394 KEBUGCHECK(0);
395 }
396 }
397 MmSetPageEntrySectionSegment(Segment, Offset, MAKE_SWAP_SSE(SavedSwapEntry));
398 MmSetSavedSwapEntryPage(Page, 0);
399 }
400 MmReleasePageMemoryConsumer(MC_USER, Page);
401 }
402 else
403 {
404 DPRINT1("Found a swapentry for a non private page in an image or data file sgment\n");
405 KEBUGCHECK(0);
406 }
407 }
408 }
409 else
410 {
411 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
412 }
413 return(SHARE_COUNT_FROM_SSE(Entry) > 0);
414 }
415
416 BOOL MiIsPageFromCache(PMEMORY_AREA MemoryArea,
417 ULONG SegOffset)
418 {
419 if (!(MemoryArea->Data.SectionData.Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
420 {
421 PBCB Bcb;
422 PCACHE_SEGMENT CacheSeg;
423 Bcb = MemoryArea->Data.SectionData.Section->FileObject->SectionObjectPointer->SharedCacheMap;
424 CacheSeg = CcRosLookupCacheSegment(Bcb, SegOffset + MemoryArea->Data.SectionData.Segment->FileOffset);
425 if (CacheSeg)
426 {
427 CcRosReleaseCacheSegment(Bcb, CacheSeg, CacheSeg->Valid, FALSE, TRUE);
428 return TRUE;
429 }
430 }
431 return FALSE;
432 }
433
434 NTSTATUS
435 MiReadPage(PMEMORY_AREA MemoryArea,
436 ULONG SegOffset,
437 PPFN_TYPE Page)
438 /*
439 * FUNCTION: Read a page for a section backed memory area.
440 * PARAMETERS:
441 * MemoryArea - Memory area to read the page for.
442 * Offset - Offset of the page to read.
443 * Page - Variable that receives a page contains the read data.
444 */
445 {
446 ULONG BaseOffset;
447 ULONG FileOffset;
448 PVOID BaseAddress;
449 BOOLEAN UptoDate;
450 PCACHE_SEGMENT CacheSeg;
451 PFILE_OBJECT FileObject;
452 NTSTATUS Status;
453 ULONG RawLength;
454 PBCB Bcb;
455 BOOLEAN IsImageSection;
456 ULONG Length;
457
458 FileObject = MemoryArea->Data.SectionData.Section->FileObject;
459 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
460 RawLength = MemoryArea->Data.SectionData.Segment->RawLength;
461 FileOffset = SegOffset + MemoryArea->Data.SectionData.Segment->FileOffset;
462 IsImageSection = MemoryArea->Data.SectionData.Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
463
464 ASSERT(Bcb);
465
466 DPRINT("%S %x\n", FileObject->FileName.Buffer, FileOffset);
467
468 /*
469 * If the file system is letting us go directly to the cache and the
470 * memory area was mapped at an offset in the file which is page aligned
471 * then get the related cache segment.
472 */
473 if ((FileOffset % PAGE_SIZE) == 0 &&
474 (SegOffset + PAGE_SIZE <= RawLength || !IsImageSection) &&
475 !(MemoryArea->Data.SectionData.Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
476 {
477
478 /*
479 * Get the related cache segment; we use a lower level interface than
480 * filesystems do because it is safe for us to use an offset with a
481 * alignment less than the file system block size.
482 */
483 Status = CcRosGetCacheSegment(Bcb,
484 FileOffset,
485 &BaseOffset,
486 &BaseAddress,
487 &UptoDate,
488 &CacheSeg);
489 if (!NT_SUCCESS(Status))
490 {
491 return(Status);
492 }
493 if (!UptoDate)
494 {
495 /*
496 * If the cache segment isn't up to date then call the file
497 * system to read in the data.
498 */
499 Status = ReadCacheSegment(CacheSeg);
500 if (!NT_SUCCESS(Status))
501 {
502 CcRosReleaseCacheSegment(Bcb, CacheSeg, FALSE, FALSE, FALSE);
503 return Status;
504 }
505 }
506 /*
507 * Retrieve the page from the cache segment that we actually want.
508 */
509 (*Page) = MmGetPhysicalAddress((char*)BaseAddress +
510 FileOffset - BaseOffset).QuadPart >> PAGE_SHIFT;
511
512 CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, FALSE, TRUE);
513 }
514 else
515 {
516 PVOID PageAddr;
517 ULONG CacheSegOffset;
518 /*
519 * Allocate a page, this is rather complicated by the possibility
520 * we might have to move other things out of memory
521 */
522 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, Page);
523 if (!NT_SUCCESS(Status))
524 {
525 return(Status);
526 }
527 Status = CcRosGetCacheSegment(Bcb,
528 FileOffset,
529 &BaseOffset,
530 &BaseAddress,
531 &UptoDate,
532 &CacheSeg);
533 if (!NT_SUCCESS(Status))
534 {
535 return(Status);
536 }
537 if (!UptoDate)
538 {
539 /*
540 * If the cache segment isn't up to date then call the file
541 * system to read in the data.
542 */
543 Status = ReadCacheSegment(CacheSeg);
544 if (!NT_SUCCESS(Status))
545 {
546 CcRosReleaseCacheSegment(Bcb, CacheSeg, FALSE, FALSE, FALSE);
547 return Status;
548 }
549 }
550 PageAddr = ExAllocatePageWithPhysPage(*Page);
551 CacheSegOffset = BaseOffset + CacheSeg->Bcb->CacheSegmentSize - FileOffset;
552 Length = RawLength - SegOffset;
553 if (Length <= CacheSegOffset && Length <= PAGE_SIZE)
554 {
555 memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, Length);
556 }
557 else if (CacheSegOffset >= PAGE_SIZE)
558 {
559 memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, PAGE_SIZE);
560 }
561 else
562 {
563 memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, CacheSegOffset);
564 CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, FALSE, FALSE);
565 Status = CcRosGetCacheSegment(Bcb,
566 FileOffset + CacheSegOffset,
567 &BaseOffset,
568 &BaseAddress,
569 &UptoDate,
570 &CacheSeg);
571 if (!NT_SUCCESS(Status))
572 {
573 ExUnmapPage(PageAddr);
574 return(Status);
575 }
576 if (!UptoDate)
577 {
578 /*
579 * If the cache segment isn't up to date then call the file
580 * system to read in the data.
581 */
582 Status = ReadCacheSegment(CacheSeg);
583 if (!NT_SUCCESS(Status))
584 {
585 CcRosReleaseCacheSegment(Bcb, CacheSeg, FALSE, FALSE, FALSE);
586 ExUnmapPage(PageAddr);
587 return Status;
588 }
589 }
590 if (Length < PAGE_SIZE)
591 {
592 memcpy((char*)PageAddr + CacheSegOffset, BaseAddress, Length - CacheSegOffset);
593 }
594 else
595 {
596 memcpy((char*)PageAddr + CacheSegOffset, BaseAddress, PAGE_SIZE - CacheSegOffset);
597 }
598 }
599 CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, FALSE, FALSE);
600 ExUnmapPage(PageAddr);
601 }
602 return(STATUS_SUCCESS);
603 }
604
605 NTSTATUS
606 MmNotPresentFaultSectionView(PMADDRESS_SPACE AddressSpace,
607 MEMORY_AREA* MemoryArea,
608 PVOID Address,
609 BOOLEAN Locked)
610 {
611 ULONG Offset;
612 PFN_TYPE Page;
613 NTSTATUS Status;
614 PVOID PAddress;
615 PSECTION_OBJECT Section;
616 PMM_SECTION_SEGMENT Segment;
617 ULONG Entry;
618 ULONG Entry1;
619 ULONG Attributes;
620 PMM_PAGEOP PageOp;
621 PMM_REGION Region;
622 BOOL HasSwapEntry;
623
624 /*
625 * There is a window between taking the page fault and locking the
626 * address space when another thread could load the page so we check
627 * that.
628 */
629 if (MmIsPagePresent(AddressSpace->Process, Address))
630 {
631 if (Locked)
632 {
633 MmLockPage(MmGetPfnForProcess(AddressSpace->Process, Address));
634 }
635 return(STATUS_SUCCESS);
636 }
637
638 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
639 Offset = (ULONG_PTR)PAddress - (ULONG_PTR)MemoryArea->StartingAddress;
640
641 Segment = MemoryArea->Data.SectionData.Segment;
642 Section = MemoryArea->Data.SectionData.Section;
643 Region = MmFindRegion(MemoryArea->StartingAddress,
644 &MemoryArea->Data.SectionData.RegionListHead,
645 Address, NULL);
646 /*
647 * Lock the segment
648 */
649 MmLockSectionSegment(Segment);
650
651 /*
652 * Check if this page needs to be mapped COW
653 */
654 if ((Segment->WriteCopy || MemoryArea->Data.SectionData.WriteCopyView) &&
655 (Region->Protect == PAGE_READWRITE ||
656 Region->Protect == PAGE_EXECUTE_READWRITE))
657 {
658 Attributes = Region->Protect == PAGE_READWRITE ? PAGE_READONLY : PAGE_EXECUTE_READ;
659 }
660 else
661 {
662 Attributes = Region->Protect;
663 }
664
665 /*
666 * Get or create a page operation descriptor
667 */
668 PageOp = MmGetPageOp(MemoryArea, 0, 0, Segment, Offset, MM_PAGEOP_PAGEIN, FALSE);
669 if (PageOp == NULL)
670 {
671 DPRINT1("MmGetPageOp failed\n");
672 KEBUGCHECK(0);
673 }
674
675 /*
676 * Check if someone else is already handling this fault, if so wait
677 * for them
678 */
679 if (PageOp->Thread != PsGetCurrentThread())
680 {
681 MmUnlockSectionSegment(Segment);
682 MmUnlockAddressSpace(AddressSpace);
683 Status = MmspWaitForPageOpCompletionEvent(PageOp);
684 /*
685 * Check for various strange conditions
686 */
687 if (Status != STATUS_SUCCESS)
688 {
689 DPRINT1("Failed to wait for page op, status = %x\n", Status);
690 KEBUGCHECK(0);
691 }
692 if (PageOp->Status == STATUS_PENDING)
693 {
694 DPRINT1("Woke for page op before completion\n");
695 KEBUGCHECK(0);
696 }
697 MmLockAddressSpace(AddressSpace);
698 /*
699 * If this wasn't a pagein then restart the operation
700 */
701 if (PageOp->OpType != MM_PAGEOP_PAGEIN)
702 {
703 MmspCompleteAndReleasePageOp(PageOp);
704 DPRINT("Address 0x%.8X\n", Address);
705 return(STATUS_MM_RESTART_OPERATION);
706 }
707
708 /*
709 * If the thread handling this fault has failed then we don't retry
710 */
711 if (!NT_SUCCESS(PageOp->Status))
712 {
713 Status = PageOp->Status;
714 MmspCompleteAndReleasePageOp(PageOp);
715 DPRINT("Address 0x%.8X\n", Address);
716 return(Status);
717 }
718 MmLockSectionSegment(Segment);
719 /*
720 * If the completed fault was for another address space then set the
721 * page in this one.
722 */
723 if (!MmIsPagePresent(AddressSpace->Process, Address))
724 {
725 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
726 HasSwapEntry = MmIsPageSwapEntry(AddressSpace->Process, (PVOID)PAddress);
727
728 if (PAGE_FROM_SSE(Entry) == 0 || HasSwapEntry)
729 {
730 /*
731 * The page was a private page in another or in our address space
732 */
733 MmUnlockSectionSegment(Segment);
734 MmspCompleteAndReleasePageOp(PageOp);
735 return(STATUS_MM_RESTART_OPERATION);
736 }
737
738 Page = PFN_FROM_SSE(Entry);
739
740 MmSharePageEntrySectionSegment(Segment, Offset);
741
742 Status = MmCreateVirtualMapping(MemoryArea->Process,
743 Address,
744 Attributes,
745 &Page,
746 1);
747 if (!NT_SUCCESS(Status))
748 {
749 DbgPrint("Unable to create virtual mapping\n");
750 KEBUGCHECK(0);
751 }
752 MmInsertRmap(Page, MemoryArea->Process, (PVOID)PAddress);
753 }
754 if (Locked)
755 {
756 MmLockPage(Page);
757 }
758 MmUnlockSectionSegment(Segment);
759 PageOp->Status = STATUS_SUCCESS;
760 MmspCompleteAndReleasePageOp(PageOp);
761 DPRINT("Address 0x%.8X\n", Address);
762 return(STATUS_SUCCESS);
763 }
764
765 HasSwapEntry = MmIsPageSwapEntry(AddressSpace->Process, (PVOID)PAddress);
766 if (HasSwapEntry)
767 {
768 /*
769 * Must be private page we have swapped out.
770 */
771 SWAPENTRY SwapEntry;
772
773 /*
774 * Sanity check
775 */
776 if (Segment->Flags & MM_PAGEFILE_SEGMENT)
777 {
778 DPRINT1("Found a swaped out private page in a pagefile section.\n");
779 KEBUGCHECK(0);
780 }
781
782 MmUnlockSectionSegment(Segment);
783 MmDeletePageFileMapping(AddressSpace->Process, (PVOID)PAddress, &SwapEntry);
784
785 MmUnlockAddressSpace(AddressSpace);
786 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
787 if (!NT_SUCCESS(Status))
788 {
789 KEBUGCHECK(0);
790 }
791
792 Status = MmReadFromSwapPage(SwapEntry, Page);
793 if (!NT_SUCCESS(Status))
794 {
795 DPRINT1("MmReadFromSwapPage failed, status = %x\n", Status);
796 KEBUGCHECK(0);
797 }
798 MmLockAddressSpace(AddressSpace);
799 Status = MmCreateVirtualMapping(AddressSpace->Process,
800 Address,
801 Region->Protect,
802 &Page,
803 1);
804 if (!NT_SUCCESS(Status))
805 {
806 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
807 KEBUGCHECK(0);
808 return(Status);
809 }
810
811 /*
812 * Store the swap entry for later use.
813 */
814 MmSetSavedSwapEntryPage(Page, SwapEntry);
815
816 /*
817 * Add the page to the process's working set
818 */
819 MmInsertRmap(Page, AddressSpace->Process, (PVOID)PAddress);
820
821 /*
822 * Finish the operation
823 */
824 if (Locked)
825 {
826 MmLockPage(Page);
827 }
828 PageOp->Status = STATUS_SUCCESS;
829 MmspCompleteAndReleasePageOp(PageOp);
830 DPRINT("Address 0x%.8X\n", Address);
831 return(STATUS_SUCCESS);
832 }
833
834 /*
835 * Satisfying a page fault on a map of /Device/PhysicalMemory is easy
836 */
837 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
838 {
839 MmUnlockSectionSegment(Segment);
840 /*
841 * Just map the desired physical page
842 */
843 Page = (Offset + MemoryArea->Data.SectionData.ViewOffset) >> PAGE_SHIFT;
844 Status = MmCreateVirtualMapping(AddressSpace->Process,
845 Address,
846 Region->Protect,
847 &Page,
848 1);
849 if (!NT_SUCCESS(Status))
850 {
851 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
852 KEBUGCHECK(0);
853 return(Status);
854 }
855 /*
856 * Don't add an rmap entry since the page mapped could be for
857 * anything.
858 */
859 if (Locked)
860 {
861 MmLockPage(Page);
862 }
863
864 /*
865 * Cleanup and release locks
866 */
867 PageOp->Status = STATUS_SUCCESS;
868 MmspCompleteAndReleasePageOp(PageOp);
869 DPRINT("Address 0x%.8X\n", Address);
870 return(STATUS_SUCCESS);
871 }
872
873 /*
874 * Map anonymous memory for BSS sections
875 */
876 if (Segment->Characteristics & IMAGE_SCN_LNK_OTHER)
877 {
878 MmUnlockSectionSegment(Segment);
879 Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page);
880 if (!NT_SUCCESS(Status))
881 {
882 MmUnlockAddressSpace(AddressSpace);
883 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
884 MmLockAddressSpace(AddressSpace);
885 }
886 if (!NT_SUCCESS(Status))
887 {
888 KEBUGCHECK(0);
889 }
890 Status = MmCreateVirtualMapping(AddressSpace->Process,
891 Address,
892 Region->Protect,
893 &Page,
894 1);
895 if (!NT_SUCCESS(Status))
896 {
897 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
898 KEBUGCHECK(0);
899 return(Status);
900 }
901 MmInsertRmap(Page, AddressSpace->Process, (PVOID)PAddress);
902 if (Locked)
903 {
904 MmLockPage(Page);
905 }
906
907 /*
908 * Cleanup and release locks
909 */
910 PageOp->Status = STATUS_SUCCESS;
911 MmspCompleteAndReleasePageOp(PageOp);
912 DPRINT("Address 0x%.8X\n", Address);
913 return(STATUS_SUCCESS);
914 }
915
916 /*
917 * Get the entry corresponding to the offset within the section
918 */
919 Offset += MemoryArea->Data.SectionData.ViewOffset;
920 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
921
922 if (Entry == 0)
923 {
924 /*
925 * If the entry is zero (and it can't change because we have
926 * locked the segment) then we need to load the page.
927 */
928
929 /*
930 * Release all our locks and read in the page from disk
931 */
932 MmUnlockSectionSegment(Segment);
933 MmUnlockAddressSpace(AddressSpace);
934
935 if ((Segment->Flags & MM_PAGEFILE_SEGMENT) ||
936 (Offset >= PAGE_ROUND_UP(Segment->RawLength) && Section->AllocationAttributes & SEC_IMAGE))
937 {
938 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
939 if (!NT_SUCCESS(Status))
940 {
941 DPRINT1("MmRequestPageMemoryConsumer failed (Status %x)\n", Status);
942 }
943 }
944 else
945 {
946 Status = MiReadPage(MemoryArea, Offset, &Page);
947 if (!NT_SUCCESS(Status))
948 {
949 DPRINT1("MiReadPage failed (Status %x)\n", Status);
950 }
951 }
952 if (!NT_SUCCESS(Status))
953 {
954 /*
955 * FIXME: What do we know in this case?
956 */
957 /*
958 * Cleanup and release locks
959 */
960 MmLockAddressSpace(AddressSpace);
961 PageOp->Status = Status;
962 MmspCompleteAndReleasePageOp(PageOp);
963 DPRINT("Address 0x%.8X\n", Address);
964 return(Status);
965 }
966 /*
967 * Relock the address space and segment
968 */
969 MmLockAddressSpace(AddressSpace);
970 MmLockSectionSegment(Segment);
971
972 /*
973 * Check the entry. No one should change the status of a page
974 * that has a pending page-in.
975 */
976 Entry1 = MmGetPageEntrySectionSegment(Segment, Offset);
977 if (Entry != Entry1)
978 {
979 DbgPrint("Someone changed ppte entry while we slept\n");
980 KEBUGCHECK(0);
981 }
982
983 /*
984 * Mark the offset within the section as having valid, in-memory
985 * data
986 */
987 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
988 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
989 MmUnlockSectionSegment(Segment);
990
991 Status = MmCreateVirtualMapping(AddressSpace->Process,
992 Address,
993 Attributes,
994 &Page,
995 1);
996 if (!NT_SUCCESS(Status))
997 {
998 DbgPrint("Unable to create virtual mapping\n");
999 KEBUGCHECK(0);
1000 }
1001 MmInsertRmap(Page, AddressSpace->Process, (PVOID)PAddress);
1002
1003 if (Locked)
1004 {
1005 MmLockPage(Page);
1006 }
1007 PageOp->Status = STATUS_SUCCESS;
1008 MmspCompleteAndReleasePageOp(PageOp);
1009 DPRINT("Address 0x%.8X\n", Address);
1010 return(STATUS_SUCCESS);
1011 }
1012 else if (IS_SWAP_FROM_SSE(Entry))
1013 {
1014 SWAPENTRY SwapEntry;
1015
1016 SwapEntry = SWAPENTRY_FROM_SSE(Entry);
1017
1018 /*
1019 * Release all our locks and read in the page from disk
1020 */
1021 MmUnlockSectionSegment(Segment);
1022
1023 MmUnlockAddressSpace(AddressSpace);
1024
1025 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1026 if (!NT_SUCCESS(Status))
1027 {
1028 KEBUGCHECK(0);
1029 }
1030
1031 Status = MmReadFromSwapPage(SwapEntry, Page);
1032 if (!NT_SUCCESS(Status))
1033 {
1034 KEBUGCHECK(0);
1035 }
1036
1037 /*
1038 * Relock the address space and segment
1039 */
1040 MmLockAddressSpace(AddressSpace);
1041 MmLockSectionSegment(Segment);
1042
1043 /*
1044 * Check the entry. No one should change the status of a page
1045 * that has a pending page-in.
1046 */
1047 Entry1 = MmGetPageEntrySectionSegment(Segment, Offset);
1048 if (Entry != Entry1)
1049 {
1050 DbgPrint("Someone changed ppte entry while we slept\n");
1051 KEBUGCHECK(0);
1052 }
1053
1054 /*
1055 * Mark the offset within the section as having valid, in-memory
1056 * data
1057 */
1058 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1059 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1060 MmUnlockSectionSegment(Segment);
1061
1062 /*
1063 * Save the swap entry.
1064 */
1065 MmSetSavedSwapEntryPage(Page, SwapEntry);
1066 Status = MmCreateVirtualMapping(AddressSpace->Process,
1067 Address,
1068 Region->Protect,
1069 &Page,
1070 1);
1071 if (!NT_SUCCESS(Status))
1072 {
1073 DbgPrint("Unable to create virtual mapping\n");
1074 KEBUGCHECK(0);
1075 }
1076 MmInsertRmap(Page, AddressSpace->Process, (PVOID)PAddress);
1077 if (Locked)
1078 {
1079 MmLockPage(Page);
1080 }
1081 PageOp->Status = STATUS_SUCCESS;
1082 MmspCompleteAndReleasePageOp(PageOp);
1083 DPRINT("Address 0x%.8X\n", Address);
1084 return(STATUS_SUCCESS);
1085 }
1086 else
1087 {
1088 /*
1089 * If the section offset is already in-memory and valid then just
1090 * take another reference to the page
1091 */
1092
1093 Page = PFN_FROM_SSE(Entry);
1094
1095 MmSharePageEntrySectionSegment(Segment, Offset);
1096 MmUnlockSectionSegment(Segment);
1097
1098 Status = MmCreateVirtualMapping(AddressSpace->Process,
1099 Address,
1100 Attributes,
1101 &Page,
1102 1);
1103 if (!NT_SUCCESS(Status))
1104 {
1105 DbgPrint("Unable to create virtual mapping\n");
1106 KEBUGCHECK(0);
1107 }
1108 MmInsertRmap(Page, AddressSpace->Process, (PVOID)PAddress);
1109 if (Locked)
1110 {
1111 MmLockPage(Page);
1112 }
1113 PageOp->Status = STATUS_SUCCESS;
1114 MmspCompleteAndReleasePageOp(PageOp);
1115 DPRINT("Address 0x%.8X\n", Address);
1116 return(STATUS_SUCCESS);
1117 }
1118 }
1119
1120 NTSTATUS
1121 MmAccessFaultSectionView(PMADDRESS_SPACE AddressSpace,
1122 MEMORY_AREA* MemoryArea,
1123 PVOID Address,
1124 BOOLEAN Locked)
1125 {
1126 PMM_SECTION_SEGMENT Segment;
1127 PSECTION_OBJECT Section;
1128 PFN_TYPE OldPage;
1129 PFN_TYPE NewPage;
1130 PVOID NewAddress;
1131 NTSTATUS Status;
1132 PVOID PAddress;
1133 ULONG Offset;
1134 PMM_PAGEOP PageOp;
1135 PMM_REGION Region;
1136 ULONG Entry;
1137
1138 /*
1139 * Check if the page has been paged out or has already been set readwrite
1140 */
1141 if (!MmIsPagePresent(AddressSpace->Process, Address) ||
1142 MmGetPageProtect(AddressSpace->Process, Address) & PAGE_READWRITE)
1143 {
1144 DPRINT("Address 0x%.8X\n", Address);
1145 return(STATUS_SUCCESS);
1146 }
1147
1148 /*
1149 * Find the offset of the page
1150 */
1151 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1152 Offset = (ULONG_PTR)PAddress - (ULONG_PTR)MemoryArea->StartingAddress;
1153
1154 Segment = MemoryArea->Data.SectionData.Segment;
1155 Section = MemoryArea->Data.SectionData.Section;
1156 Region = MmFindRegion(MemoryArea->StartingAddress,
1157 &MemoryArea->Data.SectionData.RegionListHead,
1158 Address, NULL);
1159 /*
1160 * Lock the segment
1161 */
1162 MmLockSectionSegment(Segment);
1163
1164 OldPage = MmGetPfnForProcess(NULL, Address);
1165 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
1166
1167 MmUnlockSectionSegment(Segment);
1168
1169 /*
1170 * Check if we are doing COW
1171 */
1172 if (!((Segment->WriteCopy || MemoryArea->Data.SectionData.WriteCopyView) &&
1173 (Region->Protect == PAGE_READWRITE ||
1174 Region->Protect == PAGE_EXECUTE_READWRITE)))
1175 {
1176 DPRINT("Address 0x%.8X\n", Address);
1177 return(STATUS_UNSUCCESSFUL);
1178 }
1179
1180 if (IS_SWAP_FROM_SSE(Entry) ||
1181 PFN_FROM_SSE(Entry) != OldPage)
1182 {
1183 /* This is a private page. We must only change the page protection. */
1184 MmSetPageProtect(AddressSpace->Process, PAddress, Region->Protect);
1185 return(STATUS_SUCCESS);
1186 }
1187
1188 /*
1189 * Get or create a pageop
1190 */
1191 PageOp = MmGetPageOp(MemoryArea, 0, 0, Segment, Offset,
1192 MM_PAGEOP_ACCESSFAULT, FALSE);
1193 if (PageOp == NULL)
1194 {
1195 DPRINT1("MmGetPageOp failed\n");
1196 KEBUGCHECK(0);
1197 }
1198
1199 /*
1200 * Wait for any other operations to complete
1201 */
1202 if (PageOp->Thread != PsGetCurrentThread())
1203 {
1204 MmUnlockAddressSpace(AddressSpace);
1205 Status = MmspWaitForPageOpCompletionEvent(PageOp);
1206 /*
1207 * Check for various strange conditions
1208 */
1209 if (Status == STATUS_TIMEOUT)
1210 {
1211 DPRINT1("Failed to wait for page op, status = %x\n", Status);
1212 KEBUGCHECK(0);
1213 }
1214 if (PageOp->Status == STATUS_PENDING)
1215 {
1216 DPRINT1("Woke for page op before completion\n");
1217 KEBUGCHECK(0);
1218 }
1219 /*
1220 * Restart the operation
1221 */
1222 MmLockAddressSpace(AddressSpace);
1223 MmspCompleteAndReleasePageOp(PageOp);
1224 DPRINT("Address 0x%.8X\n", Address);
1225 return(STATUS_MM_RESTART_OPERATION);
1226 }
1227
1228 /*
1229 * Release locks now we have the pageop
1230 */
1231 MmUnlockAddressSpace(AddressSpace);
1232
1233 /*
1234 * Allocate a page
1235 */
1236 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &NewPage);
1237 if (!NT_SUCCESS(Status))
1238 {
1239 KEBUGCHECK(0);
1240 }
1241
1242 /*
1243 * Copy the old page
1244 */
1245
1246 NewAddress = ExAllocatePageWithPhysPage(NewPage);
1247 memcpy(NewAddress, PAddress, PAGE_SIZE);
1248 ExUnmapPage(NewAddress);
1249
1250 /*
1251 * Delete the old entry.
1252 */
1253 MmDeleteVirtualMapping(AddressSpace->Process, Address, FALSE, NULL, NULL);
1254
1255 /*
1256 * Set the PTE to point to the new page
1257 */
1258 MmLockAddressSpace(AddressSpace);
1259 Status = MmCreateVirtualMapping(AddressSpace->Process,
1260 Address,
1261 Region->Protect,
1262 &NewPage,
1263 1);
1264 if (!NT_SUCCESS(Status))
1265 {
1266 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
1267 KEBUGCHECK(0);
1268 return(Status);
1269 }
1270 MmInsertRmap(NewPage, AddressSpace->Process, PAddress);
1271 if (!NT_SUCCESS(Status))
1272 {
1273 DbgPrint("Unable to create virtual mapping\n");
1274 KEBUGCHECK(0);
1275 }
1276 if (Locked)
1277 {
1278 MmLockPage(NewPage);
1279 MmUnlockPage(OldPage);
1280 }
1281
1282 /*
1283 * Unshare the old page.
1284 */
1285 MmDeleteRmap(OldPage, AddressSpace->Process, PAddress);
1286 MmLockSectionSegment(Segment);
1287 MmUnsharePageEntrySectionSegment(Section, Segment, Offset, FALSE, FALSE);
1288 MmUnlockSectionSegment(Segment);
1289
1290 PageOp->Status = STATUS_SUCCESS;
1291 MmspCompleteAndReleasePageOp(PageOp);
1292 DPRINT("Address 0x%.8X\n", Address);
1293 return(STATUS_SUCCESS);
1294 }
1295
1296 VOID
1297 MmPageOutDeleteMapping(PVOID Context, PEPROCESS Process, PVOID Address)
1298 {
1299 MM_SECTION_PAGEOUT_CONTEXT* PageOutContext;
1300 BOOL WasDirty;
1301 PFN_TYPE Page;
1302
1303 PageOutContext = (MM_SECTION_PAGEOUT_CONTEXT*)Context;
1304 MmDeleteVirtualMapping(Process,
1305 Address,
1306 FALSE,
1307 &WasDirty,
1308 &Page);
1309 if (WasDirty)
1310 {
1311 PageOutContext->WasDirty = TRUE;
1312 }
1313 if (!PageOutContext->Private)
1314 {
1315 MmUnsharePageEntrySectionSegment(PageOutContext->Section,
1316 PageOutContext->Segment,
1317 PageOutContext->Offset,
1318 PageOutContext->WasDirty,
1319 TRUE);
1320 }
1321 else
1322 {
1323 MmReleasePageMemoryConsumer(MC_USER, Page);
1324 }
1325
1326 DPRINT("PhysicalAddress %I64x, Address %x\n", Page, Address);
1327 }
1328
1329 NTSTATUS
1330 MmPageOutSectionView(PMADDRESS_SPACE AddressSpace,
1331 MEMORY_AREA* MemoryArea,
1332 PVOID Address,
1333 PMM_PAGEOP PageOp)
1334 {
1335 PFN_TYPE Page;
1336 MM_SECTION_PAGEOUT_CONTEXT Context;
1337 SWAPENTRY SwapEntry;
1338 ULONG Entry;
1339 ULONG FileOffset;
1340 NTSTATUS Status;
1341 PFILE_OBJECT FileObject;
1342 PBCB Bcb = NULL;
1343 BOOLEAN DirectMapped;
1344 BOOLEAN IsImageSection;
1345
1346 Address = (PVOID)PAGE_ROUND_DOWN(Address);
1347
1348 /*
1349 * Get the segment and section.
1350 */
1351 Context.Segment = MemoryArea->Data.SectionData.Segment;
1352 Context.Section = MemoryArea->Data.SectionData.Section;
1353
1354 Context.Offset = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress;
1355 FileOffset = Context.Offset + Context.Segment->FileOffset;
1356
1357 IsImageSection = Context.Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
1358
1359 FileObject = Context.Section->FileObject;
1360 DirectMapped = FALSE;
1361 if (FileObject != NULL &&
1362 !(Context.Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
1363 {
1364 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
1365
1366 /*
1367 * If the file system is letting us go directly to the cache and the
1368 * memory area was mapped at an offset in the file which is page aligned
1369 * then note this is a direct mapped page.
1370 */
1371 if ((FileOffset % PAGE_SIZE) == 0 &&
1372 (Context.Offset + PAGE_SIZE <= Context.Segment->RawLength || !IsImageSection))
1373 {
1374 DirectMapped = TRUE;
1375 }
1376 }
1377
1378
1379 /*
1380 * This should never happen since mappings of physical memory are never
1381 * placed in the rmap lists.
1382 */
1383 if (Context.Section->AllocationAttributes & SEC_PHYSICALMEMORY)
1384 {
1385 DPRINT1("Trying to page out from physical memory section address 0x%X "
1386 "process %d\n", Address,
1387 AddressSpace->Process ? AddressSpace->Process->UniqueProcessId : 0);
1388 KEBUGCHECK(0);
1389 }
1390
1391 /*
1392 * Get the section segment entry and the physical address.
1393 */
1394 Entry = MmGetPageEntrySectionSegment(Context.Segment, Context.Offset);
1395 if (!MmIsPagePresent(AddressSpace->Process, Address))
1396 {
1397 DPRINT1("Trying to page out not-present page at (%d,0x%.8X).\n",
1398 AddressSpace->Process ? AddressSpace->Process->UniqueProcessId : 0, Address);
1399 KEBUGCHECK(0);
1400 }
1401 Page = MmGetPfnForProcess(AddressSpace->Process, Address);
1402 SwapEntry = MmGetSavedSwapEntryPage(Page);
1403
1404 /*
1405 * Prepare the context structure for the rmap delete call.
1406 */
1407 Context.WasDirty = FALSE;
1408 if (Context.Segment->Characteristics & IMAGE_SCN_LNK_OTHER ||
1409 IS_SWAP_FROM_SSE(Entry) ||
1410 PFN_FROM_SSE(Entry) != Page)
1411 {
1412 Context.Private = TRUE;
1413 }
1414 else
1415 {
1416 Context.Private = FALSE;
1417 }
1418
1419 /*
1420 * Take an additional reference to the page or the cache segment.
1421 */
1422 if (DirectMapped && !Context.Private)
1423 {
1424 if(!MiIsPageFromCache(MemoryArea, Context.Offset))
1425 {
1426 DPRINT1("Direct mapped non private page is not associated with the cache.\n");
1427 KEBUGCHECK(0);
1428 }
1429 }
1430 else
1431 {
1432 MmReferencePage(Page);
1433 }
1434
1435 MmDeleteAllRmaps(Page, (PVOID)&Context, MmPageOutDeleteMapping);
1436
1437 /*
1438 * If this wasn't a private page then we should have reduced the entry to
1439 * zero by deleting all the rmaps.
1440 */
1441 if (!Context.Private && MmGetPageEntrySectionSegment(Context.Segment, Context.Offset) != 0)
1442 {
1443 if (!(Context.Segment->Flags & MM_PAGEFILE_SEGMENT) &&
1444 !(Context.Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
1445 {
1446 KEBUGCHECK(0);
1447 }
1448 }
1449
1450 /*
1451 * If the page wasn't dirty then we can just free it as for a readonly page.
1452 * Since we unmapped all the mappings above we know it will not suddenly
1453 * become dirty.
1454 * If the page is from a pagefile section and has no swap entry,
1455 * we can't free the page at this point.
1456 */
1457 SwapEntry = MmGetSavedSwapEntryPage(Page);
1458 if (Context.Segment->Flags & MM_PAGEFILE_SEGMENT)
1459 {
1460 if (Context.Private)
1461 {
1462 DPRINT1("Found a %s private page (address %x) in a pagefile segment.\n",
1463 Context.WasDirty ? "dirty" : "clean", Address);
1464 KEBUGCHECK(0);
1465 }
1466 if (!Context.WasDirty && SwapEntry != 0)
1467 {
1468 MmSetSavedSwapEntryPage(Page, 0);
1469 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, MAKE_SWAP_SSE(SwapEntry));
1470 MmReleasePageMemoryConsumer(MC_USER, Page);
1471 PageOp->Status = STATUS_SUCCESS;
1472 MmspCompleteAndReleasePageOp(PageOp);
1473 return(STATUS_SUCCESS);
1474 }
1475 }
1476 else if (Context.Segment->Characteristics & IMAGE_SCN_MEM_SHARED)
1477 {
1478 if (Context.Private)
1479 {
1480 DPRINT1("Found a %s private page (address %x) in a shared section segment.\n",
1481 Context.WasDirty ? "dirty" : "clean", Address);
1482 KEBUGCHECK(0);
1483 }
1484 if (!Context.WasDirty || SwapEntry != 0)
1485 {
1486 MmSetSavedSwapEntryPage(Page, 0);
1487 if (SwapEntry != 0)
1488 {
1489 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, MAKE_SWAP_SSE(SwapEntry));
1490 }
1491 MmReleasePageMemoryConsumer(MC_USER, Page);
1492 PageOp->Status = STATUS_SUCCESS;
1493 MmspCompleteAndReleasePageOp(PageOp);
1494 return(STATUS_SUCCESS);
1495 }
1496 }
1497 else if (!Context.Private && DirectMapped)
1498 {
1499 if (SwapEntry != 0)
1500 {
1501 DPRINT1("Found a swapentry for a non private and direct mapped page (address %x)\n",
1502 Address);
1503 KEBUGCHECK(0);
1504 }
1505 Status = CcRosUnmapCacheSegment(Bcb, FileOffset, FALSE);
1506 if (!NT_SUCCESS(Status))
1507 {
1508 DPRINT1("CCRosUnmapCacheSegment failed, status = %x\n", Status);
1509 KEBUGCHECK(0);
1510 }
1511 PageOp->Status = STATUS_SUCCESS;
1512 MmspCompleteAndReleasePageOp(PageOp);
1513 return(STATUS_SUCCESS);
1514 }
1515 else if (!Context.WasDirty && !DirectMapped && !Context.Private)
1516 {
1517 if (SwapEntry != 0)
1518 {
1519 DPRINT1("Found a swap entry for a non dirty, non private and not direct mapped page (address %x)\n",
1520 Address);
1521 KEBUGCHECK(0);
1522 }
1523 MmReleasePageMemoryConsumer(MC_USER, Page);
1524 PageOp->Status = STATUS_SUCCESS;
1525 MmspCompleteAndReleasePageOp(PageOp);
1526 return(STATUS_SUCCESS);
1527 }
1528 else if (!Context.WasDirty && Context.Private && SwapEntry != 0)
1529 {
1530 MmSetSavedSwapEntryPage(Page, 0);
1531 Status = MmCreatePageFileMapping(AddressSpace->Process,
1532 Address,
1533 SwapEntry);
1534 if (!NT_SUCCESS(Status))
1535 {
1536 KEBUGCHECK(0);
1537 }
1538 MmReleasePageMemoryConsumer(MC_USER, Page);
1539 PageOp->Status = STATUS_SUCCESS;
1540 MmspCompleteAndReleasePageOp(PageOp);
1541 return(STATUS_SUCCESS);
1542 }
1543
1544 /*
1545 * If necessary, allocate an entry in the paging file for this page
1546 */
1547 if (SwapEntry == 0)
1548 {
1549 SwapEntry = MmAllocSwapPage();
1550 if (SwapEntry == 0)
1551 {
1552 MmShowOutOfSpaceMessagePagingFile();
1553
1554 /*
1555 * For private pages restore the old mappings.
1556 */
1557 if (Context.Private)
1558 {
1559 Status = MmCreateVirtualMapping(MemoryArea->Process,
1560 Address,
1561 MemoryArea->Attributes,
1562 &Page,
1563 1);
1564 MmSetDirtyPage(MemoryArea->Process, Address);
1565 MmInsertRmap(Page,
1566 MemoryArea->Process,
1567 Address);
1568 }
1569 else
1570 {
1571 /*
1572 * For non-private pages if the page wasn't direct mapped then
1573 * set it back into the section segment entry so we don't loose
1574 * our copy. Otherwise it will be handled by the cache manager.
1575 */
1576 Status = MmCreateVirtualMapping(MemoryArea->Process,
1577 Address,
1578 MemoryArea->Attributes,
1579 &Page,
1580 1);
1581 MmSetDirtyPage(MemoryArea->Process, Address);
1582 MmInsertRmap(Page,
1583 MemoryArea->Process,
1584 Address);
1585 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1586 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, Entry);
1587 }
1588 PageOp->Status = STATUS_UNSUCCESSFUL;
1589 MmspCompleteAndReleasePageOp(PageOp);
1590 return(STATUS_PAGEFILE_QUOTA);
1591 }
1592 }
1593
1594 /*
1595 * Write the page to the pagefile
1596 */
1597 Status = MmWriteToSwapPage(SwapEntry, Page);
1598 if (!NT_SUCCESS(Status))
1599 {
1600 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
1601 Status);
1602 /*
1603 * As above: undo our actions.
1604 * FIXME: Also free the swap page.
1605 */
1606 if (Context.Private)
1607 {
1608 Status = MmCreateVirtualMapping(MemoryArea->Process,
1609 Address,
1610 MemoryArea->Attributes,
1611 &Page,
1612 1);
1613 MmSetDirtyPage(MemoryArea->Process, Address);
1614 MmInsertRmap(Page,
1615 MemoryArea->Process,
1616 Address);
1617 }
1618 else
1619 {
1620 Status = MmCreateVirtualMapping(MemoryArea->Process,
1621 Address,
1622 MemoryArea->Attributes,
1623 &Page,
1624 1);
1625 MmSetDirtyPage(MemoryArea->Process, Address);
1626 MmInsertRmap(Page,
1627 MemoryArea->Process,
1628 Address);
1629 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1630 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, Entry);
1631 }
1632 PageOp->Status = STATUS_UNSUCCESSFUL;
1633 MmspCompleteAndReleasePageOp(PageOp);
1634 return(STATUS_UNSUCCESSFUL);
1635 }
1636
1637 /*
1638 * Otherwise we have succeeded.
1639 */
1640 DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT);
1641 MmSetSavedSwapEntryPage(Page, 0);
1642 if (Context.Segment->Flags & MM_PAGEFILE_SEGMENT ||
1643 Context.Segment->Characteristics & IMAGE_SCN_MEM_SHARED)
1644 {
1645 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, MAKE_SWAP_SSE(SwapEntry));
1646 }
1647 else
1648 {
1649 MmReleasePageMemoryConsumer(MC_USER, Page);
1650 }
1651
1652 if (Context.Private)
1653 {
1654 Status = MmCreatePageFileMapping(MemoryArea->Process,
1655 Address,
1656 SwapEntry);
1657 if (!NT_SUCCESS(Status))
1658 {
1659 KEBUGCHECK(0);
1660 }
1661 }
1662 else
1663 {
1664 Entry = MAKE_SWAP_SSE(SwapEntry);
1665 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, Entry);
1666 }
1667
1668 PageOp->Status = STATUS_SUCCESS;
1669 MmspCompleteAndReleasePageOp(PageOp);
1670 return(STATUS_SUCCESS);
1671 }
1672
1673 NTSTATUS
1674 MmWritePageSectionView(PMADDRESS_SPACE AddressSpace,
1675 PMEMORY_AREA MemoryArea,
1676 PVOID Address,
1677 PMM_PAGEOP PageOp)
1678 {
1679 ULONG Offset;
1680 PSECTION_OBJECT Section;
1681 PMM_SECTION_SEGMENT Segment;
1682 PFN_TYPE Page;
1683 SWAPENTRY SwapEntry;
1684 ULONG Entry;
1685 BOOLEAN Private;
1686 NTSTATUS Status;
1687 PFILE_OBJECT FileObject;
1688 PBCB Bcb = NULL;
1689 BOOLEAN DirectMapped;
1690 BOOLEAN IsImageSection;
1691
1692 Address = (PVOID)PAGE_ROUND_DOWN(Address);
1693
1694 Offset = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress;
1695
1696 /*
1697 * Get the segment and section.
1698 */
1699 Segment = MemoryArea->Data.SectionData.Segment;
1700 Section = MemoryArea->Data.SectionData.Section;
1701 IsImageSection = Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
1702
1703 FileObject = Section->FileObject;
1704 DirectMapped = FALSE;
1705 if (FileObject != NULL &&
1706 !(Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
1707 {
1708 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
1709
1710 /*
1711 * If the file system is letting us go directly to the cache and the
1712 * memory area was mapped at an offset in the file which is page aligned
1713 * then note this is a direct mapped page.
1714 */
1715 if ((Offset + MemoryArea->Data.SectionData.ViewOffset % PAGE_SIZE) == 0 &&
1716 (Offset + PAGE_SIZE <= Segment->RawLength || !IsImageSection))
1717 {
1718 DirectMapped = TRUE;
1719 }
1720 }
1721
1722 /*
1723 * This should never happen since mappings of physical memory are never
1724 * placed in the rmap lists.
1725 */
1726 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
1727 {
1728 DPRINT1("Trying to write back page from physical memory mapped at %X "
1729 "process %d\n", Address,
1730 AddressSpace->Process ? AddressSpace->Process->UniqueProcessId : 0);
1731 KEBUGCHECK(0);
1732 }
1733
1734 /*
1735 * Get the section segment entry and the physical address.
1736 */
1737 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
1738 if (!MmIsPagePresent(AddressSpace->Process, Address))
1739 {
1740 DPRINT1("Trying to page out not-present page at (%d,0x%.8X).\n",
1741 AddressSpace->Process ? AddressSpace->Process->UniqueProcessId : 0, Address);
1742 KEBUGCHECK(0);
1743 }
1744 Page = MmGetPfnForProcess(AddressSpace->Process, Address);
1745 SwapEntry = MmGetSavedSwapEntryPage(Page);
1746
1747 /*
1748 * Check for a private (COWed) page.
1749 */
1750 if (Segment->Characteristics & IMAGE_SCN_LNK_OTHER ||
1751 IS_SWAP_FROM_SSE(Entry) ||
1752 PFN_FROM_SSE(Entry) != Page)
1753 {
1754 Private = TRUE;
1755 }
1756 else
1757 {
1758 Private = FALSE;
1759 }
1760
1761 /*
1762 * Speculatively set all mappings of the page to clean.
1763 */
1764 MmSetCleanAllRmaps(Page);
1765
1766 /*
1767 * If this page was direct mapped from the cache then the cache manager
1768 * will take care of writing it back to disk.
1769 */
1770 if (DirectMapped && !Private)
1771 {
1772 ASSERT(SwapEntry == 0);
1773 CcRosMarkDirtyCacheSegment(Bcb, Offset + MemoryArea->Data.SectionData.ViewOffset);
1774 PageOp->Status = STATUS_SUCCESS;
1775 MmspCompleteAndReleasePageOp(PageOp);
1776 return(STATUS_SUCCESS);
1777 }
1778
1779 /*
1780 * If necessary, allocate an entry in the paging file for this page
1781 */
1782 if (SwapEntry == 0)
1783 {
1784 SwapEntry = MmAllocSwapPage();
1785 if (SwapEntry == 0)
1786 {
1787 MmSetDirtyAllRmaps(Page);
1788 PageOp->Status = STATUS_UNSUCCESSFUL;
1789 MmspCompleteAndReleasePageOp(PageOp);
1790 return(STATUS_PAGEFILE_QUOTA);
1791 }
1792 MmSetSavedSwapEntryPage(Page, SwapEntry);
1793 }
1794
1795 /*
1796 * Write the page to the pagefile
1797 */
1798 Status = MmWriteToSwapPage(SwapEntry, Page);
1799 if (!NT_SUCCESS(Status))
1800 {
1801 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
1802 Status);
1803 MmSetDirtyAllRmaps(Page);
1804 PageOp->Status = STATUS_UNSUCCESSFUL;
1805 MmspCompleteAndReleasePageOp(PageOp);
1806 return(STATUS_UNSUCCESSFUL);
1807 }
1808
1809 /*
1810 * Otherwise we have succeeded.
1811 */
1812 DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT);
1813 PageOp->Status = STATUS_SUCCESS;
1814 MmspCompleteAndReleasePageOp(PageOp);
1815 return(STATUS_SUCCESS);
1816 }
1817
1818 VOID STATIC
1819 MmAlterViewAttributes(PMADDRESS_SPACE AddressSpace,
1820 PVOID BaseAddress,
1821 ULONG RegionSize,
1822 ULONG OldType,
1823 ULONG OldProtect,
1824 ULONG NewType,
1825 ULONG NewProtect)
1826 {
1827 PMEMORY_AREA MemoryArea;
1828 PMM_SECTION_SEGMENT Segment;
1829 BOOL DoCOW = FALSE;
1830 ULONG i;
1831
1832 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
1833 Segment = MemoryArea->Data.SectionData.Segment;
1834
1835 if ((Segment->WriteCopy || MemoryArea->Data.SectionData.WriteCopyView) &&
1836 (NewProtect == PAGE_READWRITE || NewProtect == PAGE_EXECUTE_READWRITE))
1837 {
1838 DoCOW = TRUE;
1839 }
1840
1841 if (OldProtect != NewProtect)
1842 {
1843 for (i = 0; i < PAGE_ROUND_UP(RegionSize) / PAGE_SIZE; i++)
1844 {
1845 PVOID Address = (char*)BaseAddress + (i * PAGE_SIZE);
1846 ULONG Protect = NewProtect;
1847
1848 /*
1849 * If we doing COW for this segment then check if the page is
1850 * already private.
1851 */
1852 if (DoCOW && MmIsPagePresent(AddressSpace->Process, Address))
1853 {
1854 ULONG Offset;
1855 ULONG Entry;
1856 PFN_TYPE Page;
1857
1858 Offset = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress;
1859 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
1860 Page = MmGetPfnForProcess(AddressSpace->Process, Address);
1861
1862 Protect = PAGE_READONLY;
1863 if (Segment->Characteristics & IMAGE_SCN_LNK_OTHER ||
1864 IS_SWAP_FROM_SSE(Entry) ||
1865 PFN_FROM_SSE(Entry) != Page)
1866 {
1867 Protect = NewProtect;
1868 }
1869 }
1870
1871 if (MmIsPagePresent(AddressSpace->Process, Address))
1872 {
1873 MmSetPageProtect(AddressSpace->Process, Address,
1874 Protect);
1875 }
1876 }
1877 }
1878 }
1879
1880 NTSTATUS
1881 MmProtectSectionView(PMADDRESS_SPACE AddressSpace,
1882 PMEMORY_AREA MemoryArea,
1883 PVOID BaseAddress,
1884 ULONG Length,
1885 ULONG Protect,
1886 PULONG OldProtect)
1887 {
1888 PMM_REGION Region;
1889 NTSTATUS Status;
1890 ULONG_PTR MaxLength;
1891
1892 MaxLength = (ULONG_PTR)MemoryArea->EndingAddress - (ULONG_PTR)BaseAddress;
1893 if (Length > MaxLength)
1894 Length = MaxLength;
1895
1896 Region = MmFindRegion(MemoryArea->StartingAddress,
1897 &MemoryArea->Data.SectionData.RegionListHead,
1898 BaseAddress, NULL);
1899 *OldProtect = Region->Protect;
1900 Status = MmAlterRegion(AddressSpace, MemoryArea->StartingAddress,
1901 &MemoryArea->Data.SectionData.RegionListHead,
1902 BaseAddress, Length, Region->Type, Protect,
1903 MmAlterViewAttributes);
1904
1905 return(Status);
1906 }
1907
1908 NTSTATUS STDCALL
1909 MmQuerySectionView(PMEMORY_AREA MemoryArea,
1910 PVOID Address,
1911 PMEMORY_BASIC_INFORMATION Info,
1912 PULONG ResultLength)
1913 {
1914 PMM_REGION Region;
1915 PVOID RegionBaseAddress;
1916 PSECTION_OBJECT Section;
1917 PLIST_ENTRY CurrentEntry;
1918 PMEMORY_AREA CurrentMArea;
1919 KIRQL oldIrql;
1920
1921 Region = MmFindRegion((PVOID)MemoryArea->StartingAddress,
1922 &MemoryArea->Data.SectionData.RegionListHead,
1923 Address, &RegionBaseAddress);
1924 if (Region == NULL)
1925 {
1926 return STATUS_UNSUCCESSFUL;
1927 }
1928 Section = MemoryArea->Data.SectionData.Section;
1929 if (Section->AllocationAttributes & SEC_IMAGE)
1930 {
1931 KeAcquireSpinLock(&Section->ViewListLock, &oldIrql);
1932 CurrentEntry = Section->ViewListHead.Flink;
1933 Info->AllocationBase = NULL;
1934 while (CurrentEntry != &Section->ViewListHead)
1935 {
1936 CurrentMArea = CONTAINING_RECORD(CurrentEntry, MEMORY_AREA, Data.SectionData.ViewListEntry);
1937 CurrentEntry = CurrentEntry->Flink;
1938 if (Info->AllocationBase == NULL)
1939 {
1940 Info->AllocationBase = CurrentMArea->StartingAddress;
1941 }
1942 else if (CurrentMArea->StartingAddress < Info->AllocationBase)
1943 {
1944 Info->AllocationBase = CurrentMArea->StartingAddress;
1945 }
1946 }
1947 KeReleaseSpinLock(&Section->ViewListLock, oldIrql);
1948 Info->BaseAddress = RegionBaseAddress;
1949 Info->AllocationProtect = MemoryArea->Attributes;
1950 Info->Type = MEM_IMAGE;
1951 }
1952 else
1953 {
1954 Info->BaseAddress = RegionBaseAddress;
1955 Info->AllocationBase = MemoryArea->StartingAddress;
1956 Info->AllocationProtect = MemoryArea->Attributes;
1957 Info->Type = MEM_MAPPED;
1958 }
1959 Info->RegionSize = PAGE_ROUND_UP((ULONG_PTR)MemoryArea->EndingAddress -
1960 (ULONG_PTR)MemoryArea->StartingAddress);
1961 Info->State = MEM_COMMIT;
1962 Info->Protect = Region->Protect;
1963
1964 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
1965 return(STATUS_SUCCESS);
1966 }
1967
1968 VOID
1969 MmpFreePageFileSegment(PMM_SECTION_SEGMENT Segment)
1970 {
1971 ULONG Length;
1972 ULONG Offset;
1973 ULONG Entry;
1974 ULONG SavedSwapEntry;
1975 PFN_TYPE Page;
1976
1977 Page = 0;
1978
1979 Length = PAGE_ROUND_UP(Segment->Length);
1980 for (Offset = 0; Offset < Length; Offset += PAGE_SIZE)
1981 {
1982 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
1983 if (Entry)
1984 {
1985 if (IS_SWAP_FROM_SSE(Entry))
1986 {
1987 MmFreeSwapPage(SWAPENTRY_FROM_SSE(Entry));
1988 }
1989 else
1990 {
1991 Page = PFN_FROM_SSE(Entry);
1992 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
1993 if (SavedSwapEntry != 0)
1994 {
1995 MmSetSavedSwapEntryPage(Page, 0);
1996 MmFreeSwapPage(SavedSwapEntry);
1997 }
1998 MmReleasePageMemoryConsumer(MC_USER, Page);
1999 }
2000 MmSetPageEntrySectionSegment(Segment, Offset, 0);
2001 }
2002 }
2003 }
2004
2005 VOID STDCALL
2006 MmpDeleteSection(PVOID ObjectBody)
2007 {
2008 PSECTION_OBJECT Section = (PSECTION_OBJECT)ObjectBody;
2009
2010 DPRINT("MmpDeleteSection(ObjectBody %x)\n", ObjectBody);
2011 if (Section->AllocationAttributes & SEC_IMAGE)
2012 {
2013 ULONG i;
2014 ULONG NrSegments;
2015 ULONG RefCount;
2016 PMM_SECTION_SEGMENT SectionSegments;
2017
2018 /*
2019 * NOTE: Section->ImageSection can be NULL for short time
2020 * during the section creating. If we fail for some reason
2021 * until the image section is properly initialized we shouldn't
2022 * process further here.
2023 */
2024 if (Section->ImageSection == NULL)
2025 return;
2026
2027 SectionSegments = Section->ImageSection->Segments;
2028 NrSegments = Section->ImageSection->NrSegments;
2029
2030 for (i = 0; i < NrSegments; i++)
2031 {
2032 if (SectionSegments[i].Characteristics & IMAGE_SCN_MEM_SHARED)
2033 {
2034 MmLockSectionSegment(&SectionSegments[i]);
2035 }
2036 RefCount = InterlockedDecrementUL(&SectionSegments[i].ReferenceCount);
2037 if (SectionSegments[i].Characteristics & IMAGE_SCN_MEM_SHARED)
2038 {
2039 if (RefCount == 0)
2040 {
2041 MmpFreePageFileSegment(&SectionSegments[i]);
2042 }
2043 MmUnlockSectionSegment(&SectionSegments[i]);
2044 }
2045 }
2046 }
2047 else
2048 {
2049 /*
2050 * NOTE: Section->Segment can be NULL for short time
2051 * during the section creating.
2052 */
2053 if (Section->Segment == NULL)
2054 return;
2055
2056 if (Section->Segment->Flags & MM_PAGEFILE_SEGMENT)
2057 {
2058 MmpFreePageFileSegment(Section->Segment);
2059 MmFreePageTablesSectionSegment(Section->Segment);
2060 ExFreePool(Section->Segment);
2061 Section->Segment = NULL;
2062 }
2063 else
2064 {
2065 InterlockedDecrementUL(&Section->Segment->ReferenceCount);
2066 }
2067 }
2068 if (Section->FileObject != NULL)
2069 {
2070 CcRosDereferenceCache(Section->FileObject);
2071 ObDereferenceObject(Section->FileObject);
2072 Section->FileObject = NULL;
2073 }
2074 }
2075
2076 VOID STDCALL
2077 MmpCloseSection(PVOID ObjectBody,
2078 ULONG HandleCount)
2079 {
2080 DPRINT("MmpCloseSection(OB %x, HC %d) RC %d\n",
2081 ObjectBody, HandleCount, ObGetObjectPointerCount(ObjectBody));
2082 }
2083
2084 NTSTATUS STDCALL
2085 MmpCreateSection(PVOID ObjectBody,
2086 PVOID Parent,
2087 PWSTR RemainingPath,
2088 POBJECT_ATTRIBUTES ObjectAttributes)
2089 {
2090 DPRINT("MmpCreateSection(ObjectBody %x, Parent %x, RemainingPath %S)\n",
2091 ObjectBody, Parent, RemainingPath);
2092
2093 if (RemainingPath == NULL)
2094 {
2095 return(STATUS_SUCCESS);
2096 }
2097
2098 if (wcschr(RemainingPath+1, L'\\') != NULL)
2099 {
2100 return(STATUS_UNSUCCESSFUL);
2101 }
2102 return(STATUS_SUCCESS);
2103 }
2104
2105 NTSTATUS INIT_FUNCTION
2106 MmCreatePhysicalMemorySection(VOID)
2107 {
2108 PSECTION_OBJECT PhysSection;
2109 NTSTATUS Status;
2110 OBJECT_ATTRIBUTES Obj;
2111 UNICODE_STRING Name = ROS_STRING_INITIALIZER(L"\\Device\\PhysicalMemory");
2112 LARGE_INTEGER SectionSize;
2113
2114 /*
2115 * Create the section mapping physical memory
2116 */
2117 SectionSize.QuadPart = 0xFFFFFFFF;
2118 InitializeObjectAttributes(&Obj,
2119 &Name,
2120 0,
2121 NULL,
2122 NULL);
2123 Status = MmCreateSection(&PhysSection,
2124 SECTION_ALL_ACCESS,
2125 &Obj,
2126 &SectionSize,
2127 PAGE_EXECUTE_READWRITE,
2128 0,
2129 NULL,
2130 NULL);
2131 if (!NT_SUCCESS(Status))
2132 {
2133 DbgPrint("Failed to create PhysicalMemory section\n");
2134 KEBUGCHECK(0);
2135 }
2136 PhysSection->AllocationAttributes |= SEC_PHYSICALMEMORY;
2137
2138 return(STATUS_SUCCESS);
2139 }
2140
2141 NTSTATUS INIT_FUNCTION
2142 MmInitSectionImplementation(VOID)
2143 {
2144 MmSectionObjectType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE));
2145
2146 RtlRosInitUnicodeStringFromLiteral(&MmSectionObjectType->TypeName, L"Section");
2147
2148 MmSectionObjectType->Tag = TAG('S', 'E', 'C', 'T');
2149 MmSectionObjectType->TotalObjects = 0;
2150 MmSectionObjectType->TotalHandles = 0;
2151 MmSectionObjectType->MaxObjects = ULONG_MAX;
2152 MmSectionObjectType->MaxHandles = ULONG_MAX;
2153 MmSectionObjectType->PagedPoolCharge = 0;
2154 MmSectionObjectType->NonpagedPoolCharge = sizeof(SECTION_OBJECT);
2155 MmSectionObjectType->Mapping = &MmpSectionMapping;
2156 MmSectionObjectType->Dump = NULL;
2157 MmSectionObjectType->Open = NULL;
2158 MmSectionObjectType->Close = MmpCloseSection;
2159 MmSectionObjectType->Delete = MmpDeleteSection;
2160 MmSectionObjectType->Parse = NULL;
2161 MmSectionObjectType->Security = NULL;
2162 MmSectionObjectType->QueryName = NULL;
2163 MmSectionObjectType->OkayToClose = NULL;
2164 MmSectionObjectType->Create = MmpCreateSection;
2165 MmSectionObjectType->DuplicationNotify = NULL;
2166
2167 /*
2168 * NOTE: Do not register the section object type here because
2169 * the object manager it not initialized yet!
2170 * The section object type will be created in ObInit().
2171 */
2172 ObpCreateTypeObject(MmSectionObjectType);
2173
2174 return(STATUS_SUCCESS);
2175 }
2176
2177 NTSTATUS
2178 MmCreatePageFileSection(PSECTION_OBJECT *SectionObject,
2179 ACCESS_MASK DesiredAccess,
2180 POBJECT_ATTRIBUTES ObjectAttributes,
2181 PLARGE_INTEGER UMaximumSize,
2182 ULONG SectionPageProtection,
2183 ULONG AllocationAttributes)
2184 /*
2185 * Create a section which is backed by the pagefile
2186 */
2187 {
2188 LARGE_INTEGER MaximumSize;
2189 PSECTION_OBJECT Section;
2190 PMM_SECTION_SEGMENT Segment;
2191 NTSTATUS Status;
2192
2193 if (UMaximumSize == NULL)
2194 {
2195 return(STATUS_UNSUCCESSFUL);
2196 }
2197 MaximumSize = *UMaximumSize;
2198
2199 /*
2200 * Create the section
2201 */
2202 Status = ObCreateObject(ExGetPreviousMode(),
2203 MmSectionObjectType,
2204 ObjectAttributes,
2205 ExGetPreviousMode(),
2206 NULL,
2207 sizeof(SECTION_OBJECT),
2208 0,
2209 0,
2210 (PVOID*)(PVOID)&Section);
2211 if (!NT_SUCCESS(Status))
2212 {
2213 return(Status);
2214 }
2215
2216 /*
2217 * Initialize it
2218 */
2219 Section->SectionPageProtection = SectionPageProtection;
2220 Section->AllocationAttributes = AllocationAttributes;
2221 Section->Segment = NULL;
2222 InitializeListHead(&Section->ViewListHead);
2223 KeInitializeSpinLock(&Section->ViewListLock);
2224 Section->FileObject = NULL;
2225 Section->MaximumSize = MaximumSize;
2226 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2227 TAG_MM_SECTION_SEGMENT);
2228 if (Segment == NULL)
2229 {
2230 ObDereferenceObject(Section);
2231 return(STATUS_NO_MEMORY);
2232 }
2233 Section->Segment = Segment;
2234 Segment->ReferenceCount = 1;
2235 ExInitializeFastMutex(&Segment->Lock);
2236 Segment->FileOffset = 0;
2237 Segment->Protection = SectionPageProtection;
2238 Segment->RawLength = MaximumSize.u.LowPart;
2239 Segment->Length = PAGE_ROUND_UP(MaximumSize.u.LowPart);
2240 Segment->Flags = MM_PAGEFILE_SEGMENT;
2241 Segment->WriteCopy = FALSE;
2242 RtlZeroMemory(&Segment->PageDirectory, sizeof(SECTION_PAGE_DIRECTORY));
2243 Segment->VirtualAddress = 0;
2244 Segment->Characteristics = 0;
2245 *SectionObject = Section;
2246 return(STATUS_SUCCESS);
2247 }
2248
2249
2250 NTSTATUS
2251 MmCreateDataFileSection(PSECTION_OBJECT *SectionObject,
2252 ACCESS_MASK DesiredAccess,
2253 POBJECT_ATTRIBUTES ObjectAttributes,
2254 PLARGE_INTEGER UMaximumSize,
2255 ULONG SectionPageProtection,
2256 ULONG AllocationAttributes,
2257 HANDLE FileHandle)
2258 /*
2259 * Create a section backed by a data file
2260 */
2261 {
2262 PSECTION_OBJECT Section;
2263 NTSTATUS Status;
2264 LARGE_INTEGER MaximumSize;
2265 PFILE_OBJECT FileObject;
2266 PMM_SECTION_SEGMENT Segment;
2267 ULONG FileAccess;
2268 IO_STATUS_BLOCK Iosb;
2269 LARGE_INTEGER Offset;
2270 CHAR Buffer;
2271 FILE_STANDARD_INFORMATION FileInfo;
2272
2273 /*
2274 * Create the section
2275 */
2276 Status = ObCreateObject(ExGetPreviousMode(),
2277 MmSectionObjectType,
2278 ObjectAttributes,
2279 ExGetPreviousMode(),
2280 NULL,
2281 sizeof(SECTION_OBJECT),
2282 0,
2283 0,
2284 (PVOID*)(PVOID)&Section);
2285 if (!NT_SUCCESS(Status))
2286 {
2287 return(Status);
2288 }
2289
2290 /*
2291 * Initialize it
2292 */
2293 Section->SectionPageProtection = SectionPageProtection;
2294 Section->AllocationAttributes = AllocationAttributes;
2295 Section->Segment = NULL;
2296 InitializeListHead(&Section->ViewListHead);
2297 KeInitializeSpinLock(&Section->ViewListLock);
2298
2299 /*
2300 * Check file access required
2301 */
2302 if (SectionPageProtection & PAGE_READWRITE ||
2303 SectionPageProtection & PAGE_EXECUTE_READWRITE)
2304 {
2305 FileAccess = FILE_READ_DATA | FILE_WRITE_DATA;
2306 }
2307 else
2308 {
2309 FileAccess = FILE_READ_DATA;
2310 }
2311
2312 /*
2313 * Reference the file handle
2314 */
2315 Status = ObReferenceObjectByHandle(FileHandle,
2316 FileAccess,
2317 IoFileObjectType,
2318 UserMode,
2319 (PVOID*)(PVOID)&FileObject,
2320 NULL);
2321 if (!NT_SUCCESS(Status))
2322 {
2323 ObDereferenceObject(Section);
2324 return(Status);
2325 }
2326
2327 /*
2328 * FIXME: This is propably not entirely correct. We can't look into
2329 * the standard FCB header because it might not be initialized yet
2330 * (as in case of the EXT2FS driver by Manoj Paul Joseph where the
2331 * standard file information is filled on first request).
2332 */
2333 Status = NtQueryInformationFile(FileHandle,
2334 &Iosb,
2335 &FileInfo,
2336 sizeof(FILE_STANDARD_INFORMATION),
2337 FileStandardInformation);
2338 if (!NT_SUCCESS(Status))
2339 {
2340 ObDereferenceObject(Section);
2341 ObDereferenceObject(FileObject);
2342 return Status;
2343 }
2344
2345 /*
2346 * FIXME: Revise this once a locking order for file size changes is
2347 * decided
2348 */
2349 if (UMaximumSize != NULL)
2350 {
2351 MaximumSize = *UMaximumSize;
2352 }
2353 else
2354 {
2355 MaximumSize = FileInfo.EndOfFile;
2356 /* Mapping zero-sized files isn't allowed. */
2357 if (MaximumSize.QuadPart == 0)
2358 {
2359 ObDereferenceObject(Section);
2360 ObDereferenceObject(FileObject);
2361 return STATUS_FILE_INVALID;
2362 }
2363 }
2364
2365 if (MaximumSize.QuadPart > FileInfo.EndOfFile.QuadPart)
2366 {
2367 Status = NtSetInformationFile(FileHandle,
2368 &Iosb,
2369 &MaximumSize,
2370 sizeof(LARGE_INTEGER),
2371 FileAllocationInformation);
2372 if (!NT_SUCCESS(Status))
2373 {
2374 ObDereferenceObject(Section);
2375 ObDereferenceObject(FileObject);
2376 return(STATUS_SECTION_NOT_EXTENDED);
2377 }
2378 }
2379
2380 if (FileObject->SectionObjectPointer == NULL ||
2381 FileObject->SectionObjectPointer->SharedCacheMap == NULL)
2382 {
2383 /*
2384 * Read a bit so caching is initiated for the file object.
2385 * This is only needed because MiReadPage currently cannot
2386 * handle non-cached streams.
2387 */
2388 Offset.QuadPart = 0;
2389 Status = ZwReadFile(FileHandle,
2390 NULL,
2391 NULL,
2392 NULL,
2393 &Iosb,
2394 &Buffer,
2395 sizeof (Buffer),
2396 &Offset,
2397 0);
2398 if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE))
2399 {
2400 ObDereferenceObject(Section);
2401 ObDereferenceObject(FileObject);
2402 return(Status);
2403 }
2404 if (FileObject->SectionObjectPointer == NULL ||
2405 FileObject->SectionObjectPointer->SharedCacheMap == NULL)
2406 {
2407 /* FIXME: handle this situation */
2408 ObDereferenceObject(Section);
2409 ObDereferenceObject(FileObject);
2410 return STATUS_INVALID_PARAMETER;
2411 }
2412 }
2413
2414 /*
2415 * Lock the file
2416 */
2417 Status = MmspWaitForFileLock(FileObject);
2418 if (Status != STATUS_SUCCESS)
2419 {
2420 ObDereferenceObject(Section);
2421 ObDereferenceObject(FileObject);
2422 return(Status);
2423 }
2424
2425 /*
2426 * If this file hasn't been mapped as a data file before then allocate a
2427 * section segment to describe the data file mapping
2428 */
2429 if (FileObject->SectionObjectPointer->DataSectionObject == NULL)
2430 {
2431 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2432 TAG_MM_SECTION_SEGMENT);
2433 if (Segment == NULL)
2434 {
2435 KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2436 ObDereferenceObject(Section);
2437 ObDereferenceObject(FileObject);
2438 return(STATUS_NO_MEMORY);
2439 }
2440 Section->Segment = Segment;
2441 Segment->ReferenceCount = 1;
2442 ExInitializeFastMutex(&Segment->Lock);
2443 /*
2444 * Set the lock before assigning the segment to the file object
2445 */
2446 ExAcquireFastMutex(&Segment->Lock);
2447 FileObject->SectionObjectPointer->DataSectionObject = (PVOID)Segment;
2448
2449 Segment->FileOffset = 0;
2450 Segment->Protection = SectionPageProtection;
2451 Segment->Flags = MM_DATAFILE_SEGMENT;
2452 Segment->Characteristics = 0;
2453 Segment->WriteCopy = FALSE;
2454 if (AllocationAttributes & SEC_RESERVE)
2455 {
2456 Segment->Length = Segment->RawLength = 0;
2457 }
2458 else
2459 {
2460 Segment->RawLength = MaximumSize.u.LowPart;
2461 Segment->Length = PAGE_ROUND_UP(Segment->RawLength);
2462 }
2463 Segment->VirtualAddress = 0;
2464 RtlZeroMemory(&Segment->PageDirectory, sizeof(SECTION_PAGE_DIRECTORY));
2465 }
2466 else
2467 {
2468 /*
2469 * If the file is already mapped as a data file then we may need
2470 * to extend it
2471 */
2472 Segment =
2473 (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->
2474 DataSectionObject;
2475 Section->Segment = Segment;
2476 InterlockedIncrementUL(&Segment->ReferenceCount);
2477 MmLockSectionSegment(Segment);
2478
2479 if (MaximumSize.u.LowPart > Segment->RawLength &&
2480 !(AllocationAttributes & SEC_RESERVE))
2481 {
2482 Segment->RawLength = MaximumSize.u.LowPart;
2483 Segment->Length = PAGE_ROUND_UP(Segment->RawLength);
2484 }
2485 }
2486 MmUnlockSectionSegment(Segment);
2487 Section->FileObject = FileObject;
2488 Section->MaximumSize = MaximumSize;
2489 CcRosReferenceCache(FileObject);
2490 KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2491 *SectionObject = Section;
2492 return(STATUS_SUCCESS);
2493 }
2494
2495 /*
2496 TODO: not that great (declaring loaders statically, having to declare all of
2497 them, having to keep them extern, etc.), will fix in the future
2498 */
2499 extern NTSTATUS NTAPI PeFmtCreateSection
2500 (
2501 IN CONST VOID * FileHeader,
2502 IN SIZE_T FileHeaderSize,
2503 IN PVOID File,
2504 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2505 OUT PULONG Flags,
2506 IN PEXEFMT_CB_READ_FILE ReadFileCb,
2507 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2508 );
2509
2510 extern NTSTATUS NTAPI ElfFmtCreateSection
2511 (
2512 IN CONST VOID * FileHeader,
2513 IN SIZE_T FileHeaderSize,
2514 IN PVOID File,
2515 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2516 OUT PULONG Flags,
2517 IN PEXEFMT_CB_READ_FILE ReadFileCb,
2518 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2519 );
2520
2521 /* TODO: this is a standard DDK/PSDK macro */
2522 #ifndef RTL_NUMBER_OF
2523 #define RTL_NUMBER_OF(ARR_) (sizeof(ARR_) / sizeof((ARR_)[0]))
2524 #endif
2525
2526 static PEXEFMT_LOADER ExeFmtpLoaders[] =
2527 {
2528 PeFmtCreateSection,
2529 ElfFmtCreateSection
2530 };
2531
2532 static
2533 PMM_SECTION_SEGMENT
2534 NTAPI
2535 ExeFmtpAllocateSegments(IN ULONG NrSegments)
2536 {
2537 SIZE_T SizeOfSegments;
2538 PMM_SECTION_SEGMENT Segments;
2539
2540 /* TODO: check for integer overflow */
2541 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * NrSegments;
2542
2543 Segments = ExAllocatePoolWithTag(NonPagedPool,
2544 SizeOfSegments,
2545 TAG_MM_SECTION_SEGMENT);
2546
2547 if(Segments)
2548 RtlZeroMemory(Segments, SizeOfSegments);
2549
2550 return Segments;
2551 }
2552
2553 static
2554 NTSTATUS
2555 NTAPI
2556 ExeFmtpReadFile(IN PVOID File,
2557 IN PLARGE_INTEGER Offset,
2558 IN ULONG Length,
2559 OUT PVOID * Data,
2560 OUT PVOID * AllocBase,
2561 OUT PULONG ReadSize)
2562 {
2563 NTSTATUS Status;
2564 LARGE_INTEGER FileOffset;
2565 ULONG AdjustOffset;
2566 ULONG OffsetAdjustment;
2567 ULONG BufferSize;
2568 ULONG UsedSize;
2569 PVOID Buffer;
2570
2571 ASSERT_IRQL_LESS(DISPATCH_LEVEL);
2572
2573 if(Length == 0)
2574 {
2575 KEBUGCHECK(STATUS_INVALID_PARAMETER_4);
2576 }
2577
2578 FileOffset = *Offset;
2579
2580 /* Negative/special offset: it cannot be used in this context */
2581 if(FileOffset.u.HighPart < 0)
2582 {
2583 KEBUGCHECK(STATUS_INVALID_PARAMETER_5);
2584 }
2585
2586 ASSERT(PAGE_SIZE <= MAXULONG);
2587 AdjustOffset = PAGE_ROUND_DOWN(FileOffset.u.LowPart);
2588 OffsetAdjustment = FileOffset.u.LowPart - AdjustOffset;
2589 FileOffset.u.LowPart = AdjustOffset;
2590
2591 BufferSize = Length + OffsetAdjustment;
2592 BufferSize = PAGE_ROUND_UP(BufferSize);
2593
2594 /*
2595 * It's ok to use paged pool, because this is a temporary buffer only used in
2596 * the loading of executables. The assumption is that MmCreateSection is
2597 * always called at low IRQLs and that these buffers don't survive a brief
2598 * initialization phase
2599 */
2600 Buffer = ExAllocatePoolWithTag(PagedPool,
2601 BufferSize,
2602 TAG('M', 'm', 'X', 'r'));
2603
2604 UsedSize = 0;
2605
2606 #if 0
2607 Status = MmspPageRead(File,
2608 Buffer,
2609 BufferSize,
2610 &FileOffset,
2611 &UsedSize);
2612 #else
2613 /*
2614 * FIXME: if we don't use ZwReadFile, caching is not enabled for the file and
2615 * nothing will work. But using ZwReadFile is wrong, and using its side effects
2616 * to initialize internal state is even worse. Our cache manager is in need of
2617 * professional help
2618 */
2619 {
2620 IO_STATUS_BLOCK Iosb;
2621
2622 Status = ZwReadFile(File,
2623 NULL,
2624 NULL,
2625 NULL,
2626 &Iosb,
2627 Buffer,
2628 BufferSize,
2629 &FileOffset,
2630 NULL);
2631
2632 if(NT_SUCCESS(Status))
2633 {
2634 UsedSize = Iosb.Information;
2635 }
2636 }
2637 #endif
2638
2639 if(NT_SUCCESS(Status) && UsedSize < OffsetAdjustment)
2640 {
2641 Status = STATUS_IN_PAGE_ERROR;
2642 ASSERT(!NT_SUCCESS(Status));
2643 }
2644
2645 if(NT_SUCCESS(Status))
2646 {
2647 *Data = (PVOID)((ULONG_PTR)Buffer + OffsetAdjustment);
2648 *AllocBase = Buffer;
2649 *ReadSize = UsedSize - OffsetAdjustment;
2650 }
2651 else
2652 {
2653 ExFreePool(Buffer);
2654 }
2655
2656 return Status;
2657 }
2658
2659 #ifdef NASSERT
2660 # define MmspAssertSegmentsSorted(OBJ_) ((void)0)
2661 # define MmspAssertSegmentsNoOverlap(OBJ_) ((void)0)
2662 # define MmspAssertSegmentsPageAligned(OBJ_) ((void)0)
2663 #else
2664 static
2665 VOID
2666 NTAPI
2667 MmspAssertSegmentsSorted(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2668 {
2669 ULONG i;
2670
2671 for( i = 1; i < ImageSectionObject->NrSegments; ++ i )
2672 {
2673 ASSERT(ImageSectionObject->Segments[i].VirtualAddress >=
2674 ImageSectionObject->Segments[i - 1].VirtualAddress);
2675 }
2676 }
2677
2678 static
2679 VOID
2680 NTAPI
2681 MmspAssertSegmentsNoOverlap(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2682 {
2683 ULONG i;
2684
2685 MmspAssertSegmentsSorted(ImageSectionObject);
2686
2687 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2688 {
2689 ASSERT(ImageSectionObject->Segments[i].Length > 0);
2690
2691 if(i > 0)
2692 {
2693 ASSERT(ImageSectionObject->Segments[i].VirtualAddress >=
2694 (ImageSectionObject->Segments[i - 1].VirtualAddress +
2695 ImageSectionObject->Segments[i - 1].Length));
2696 }
2697 }
2698 }
2699
2700 static
2701 VOID
2702 NTAPI
2703 MmspAssertSegmentsPageAligned(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2704 {
2705 ULONG i;
2706
2707 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2708 {
2709 ASSERT((ImageSectionObject->Segments[i].VirtualAddress % PAGE_SIZE) == 0);
2710 ASSERT((ImageSectionObject->Segments[i].Length % PAGE_SIZE) == 0);
2711 }
2712 }
2713 #endif
2714
2715 static
2716 int
2717 __cdecl
2718 MmspCompareSegments(const void * x,
2719 const void * y)
2720 {
2721 PMM_SECTION_SEGMENT Segment1 = (PMM_SECTION_SEGMENT)x;
2722 PMM_SECTION_SEGMENT Segment2 = (PMM_SECTION_SEGMENT)y;
2723
2724 return
2725 (Segment1->VirtualAddress - Segment2->VirtualAddress) >>
2726 ((sizeof(ULONG_PTR) - sizeof(int)) * 8);
2727 }
2728
2729 /*
2730 * Ensures an image section's segments are sorted in memory
2731 */
2732 static
2733 VOID
2734 NTAPI
2735 MmspSortSegments(IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2736 IN ULONG Flags)
2737 {
2738 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_SORTED)
2739 {
2740 MmspAssertSegmentsSorted(ImageSectionObject);
2741 }
2742 else
2743 {
2744 qsort(ImageSectionObject->Segments,
2745 ImageSectionObject->NrSegments,
2746 sizeof(ImageSectionObject->Segments[0]),
2747 MmspCompareSegments);
2748 }
2749 }
2750
2751
2752 /*
2753 * Ensures an image section's segments don't overlap in memory and don't have
2754 * gaps and don't have a null size. We let them map to overlapping file regions,
2755 * though - that's not necessarily an error
2756 */
2757 static
2758 BOOLEAN
2759 NTAPI
2760 MmspCheckSegmentBounds
2761 (
2762 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2763 IN ULONG Flags
2764 )
2765 {
2766 ULONG i;
2767
2768 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_NO_OVERLAP)
2769 {
2770 MmspAssertSegmentsNoOverlap(ImageSectionObject);
2771 return TRUE;
2772 }
2773
2774 ASSERT(ImageSectionObject->NrSegments >= 1);
2775
2776 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2777 {
2778 if(ImageSectionObject->Segments[i].Length == 0)
2779 {
2780 return FALSE;
2781 }
2782
2783 if(i > 0)
2784 {
2785 /*
2786 * TODO: relax the limitation on gaps. For example, gaps smaller than a
2787 * page could be OK (Windows seems to be OK with them), and larger gaps
2788 * could lead to image sections spanning several discontiguous regions
2789 * (NtMapViewOfSection could then refuse to map them, and they could
2790 * e.g. only be allowed as parameters to NtCreateProcess, like on UNIX)
2791 */
2792 if ((ImageSectionObject->Segments[i - 1].VirtualAddress +
2793 ImageSectionObject->Segments[i - 1].Length) !=
2794 ImageSectionObject->Segments[i].VirtualAddress)
2795 {
2796 return FALSE;
2797 }
2798 }
2799 }
2800
2801 return TRUE;
2802 }
2803
2804 /*
2805 * Merges and pads an image section's segments until they all are page-aligned
2806 * and have a size that is a multiple of the page size
2807 */
2808 static
2809 BOOLEAN
2810 NTAPI
2811 MmspPageAlignSegments
2812 (
2813 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2814 IN ULONG Flags
2815 )
2816 {
2817 ULONG i;
2818 ULONG LastSegment;
2819 BOOLEAN Initialized;
2820
2821 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED)
2822 {
2823 MmspAssertSegmentsPageAligned(ImageSectionObject);
2824 return TRUE;
2825 }
2826
2827 Initialized = FALSE;
2828 LastSegment = 0;
2829
2830 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2831 {
2832 PMM_SECTION_SEGMENT EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2833
2834 /*
2835 * The first segment requires special handling
2836 */
2837 if (i == 0)
2838 {
2839 ULONG_PTR VirtualAddress;
2840 ULONG_PTR VirtualOffset;
2841
2842 VirtualAddress = EffectiveSegment->VirtualAddress;
2843
2844 /* Round down the virtual address to the nearest page */
2845 EffectiveSegment->VirtualAddress = PAGE_ROUND_DOWN(VirtualAddress);
2846
2847 /* Round up the virtual size to the nearest page */
2848 EffectiveSegment->Length = PAGE_ROUND_UP(VirtualAddress + EffectiveSegment->Length) -
2849 EffectiveSegment->VirtualAddress;
2850
2851 /* Adjust the raw address and size */
2852 VirtualOffset = VirtualAddress - EffectiveSegment->VirtualAddress;
2853
2854 if (EffectiveSegment->FileOffset < VirtualOffset)
2855 {
2856 return FALSE;
2857 }
2858
2859 /*
2860 * Garbage in, garbage out: unaligned base addresses make the file
2861 * offset point in curious and odd places, but that's what we were
2862 * asked for
2863 */
2864 EffectiveSegment->FileOffset -= VirtualOffset;
2865 EffectiveSegment->RawLength += VirtualOffset;
2866 }
2867 else
2868 {
2869 PMM_SECTION_SEGMENT Segment = &ImageSectionObject->Segments[i];
2870 ULONG_PTR EndOfEffectiveSegment;
2871
2872 EndOfEffectiveSegment = EffectiveSegment->VirtualAddress + EffectiveSegment->Length;
2873 ASSERT((EndOfEffectiveSegment % PAGE_SIZE) == 0);
2874
2875 /*
2876 * The current segment begins exactly where the current effective
2877 * segment ended, therefore beginning a new effective segment
2878 */
2879 if (EndOfEffectiveSegment == Segment->VirtualAddress)
2880 {
2881 LastSegment ++;
2882 ASSERT(LastSegment <= i);
2883 ASSERT(LastSegment < ImageSectionObject->NrSegments);
2884
2885 EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2886
2887 /*
2888 * Copy the current segment. If necessary, the effective segment
2889 * will be expanded later
2890 */
2891 *EffectiveSegment = *Segment;
2892
2893 /*
2894 * Page-align the virtual size. We know for sure the virtual address
2895 * already is
2896 */
2897 ASSERT((EffectiveSegment->VirtualAddress % PAGE_SIZE) == 0);
2898 EffectiveSegment->Length = PAGE_ROUND_UP(EffectiveSegment->Length);
2899 }
2900 /*
2901 * The current segment is still part of the current effective segment:
2902 * extend the effective segment to reflect this
2903 */
2904 else if (EndOfEffectiveSegment > Segment->VirtualAddress)
2905 {
2906 static const ULONG FlagsToProtection[16] =
2907 {
2908 PAGE_NOACCESS,
2909 PAGE_READONLY,
2910 PAGE_READWRITE,
2911 PAGE_READWRITE,
2912 PAGE_EXECUTE_READ,
2913 PAGE_EXECUTE_READ,
2914 PAGE_EXECUTE_READWRITE,
2915 PAGE_EXECUTE_READWRITE,
2916 PAGE_WRITECOPY,
2917 PAGE_WRITECOPY,
2918 PAGE_WRITECOPY,
2919 PAGE_WRITECOPY,
2920 PAGE_EXECUTE_WRITECOPY,
2921 PAGE_EXECUTE_WRITECOPY,
2922 PAGE_EXECUTE_WRITECOPY,
2923 PAGE_EXECUTE_WRITECOPY
2924 };
2925
2926 unsigned ProtectionFlags;
2927
2928 /*
2929 * Extend the file size
2930 */
2931
2932 /* Unaligned segments must be contiguous within the file */
2933 if (Segment->FileOffset != (EffectiveSegment->FileOffset +
2934 EffectiveSegment->RawLength))
2935 {
2936 return FALSE;
2937 }
2938
2939 EffectiveSegment->RawLength += Segment->RawLength;
2940
2941 /*
2942 * Extend the virtual size
2943 */
2944 ASSERT(PAGE_ROUND_UP(Segment->VirtualAddress + Segment->Length) > EndOfEffectiveSegment);
2945
2946 EffectiveSegment->Length = PAGE_ROUND_UP(Segment->VirtualAddress + Segment->Length) -
2947 EffectiveSegment->VirtualAddress;
2948
2949 /*
2950 * Merge the protection
2951 */
2952 EffectiveSegment->Protection |= Segment->Protection;
2953
2954 /* Clean up redundance */
2955 ProtectionFlags = 0;
2956
2957 if(EffectiveSegment->Protection & PAGE_IS_READABLE)
2958 ProtectionFlags |= 1 << 0;
2959
2960 if(EffectiveSegment->Protection & PAGE_IS_WRITABLE)
2961 ProtectionFlags |= 1 << 1;
2962
2963 if(EffectiveSegment->Protection & PAGE_IS_EXECUTABLE)
2964 ProtectionFlags |= 1 << 2;
2965
2966 if(EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
2967 ProtectionFlags |= 1 << 3;
2968
2969 ASSERT(ProtectionFlags < 16);
2970 EffectiveSegment->Protection = FlagsToProtection[ProtectionFlags];
2971
2972 /* If a segment was required to be shared and cannot, fail */
2973 if(!(Segment->Protection & PAGE_IS_WRITECOPY) &&
2974 EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
2975 {
2976 return FALSE;
2977 }
2978 }
2979 /*
2980 * We assume no holes between segments at this point
2981 */
2982 else
2983 {
2984 ASSERT(FALSE);
2985 }
2986 }
2987 }
2988
2989 return TRUE;
2990 }
2991
2992 NTSTATUS
2993 ExeFmtpCreateImageSection(HANDLE FileHandle,
2994 PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2995 {
2996 LARGE_INTEGER Offset;
2997 PVOID FileHeader;
2998 PVOID FileHeaderBuffer;
2999 ULONG FileHeaderSize;
3000 ULONG Flags;
3001 ULONG OldNrSegments;
3002 NTSTATUS Status;
3003 ULONG i;
3004
3005 /*
3006 * Read the beginning of the file (2 pages). Should be enough to contain
3007 * all (or most) of the headers
3008 */
3009 Offset.QuadPart = 0;
3010
3011 /* FIXME: use FileObject instead of FileHandle */
3012 Status = ExeFmtpReadFile (FileHandle,
3013 &Offset,
3014 PAGE_SIZE * 2,
3015 &FileHeader,
3016 &FileHeaderBuffer,
3017 &FileHeaderSize);
3018
3019 if (!NT_SUCCESS(Status))
3020 return Status;
3021
3022 if (FileHeaderSize == 0)
3023 {
3024 ExFreePool(FileHeaderBuffer);
3025 return STATUS_UNSUCCESSFUL;
3026 }
3027
3028 /*
3029 * Look for a loader that can handle this executable
3030 */
3031 for (i = 0; i < RTL_NUMBER_OF(ExeFmtpLoaders); ++ i)
3032 {
3033 RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject));
3034 Flags = 0;
3035
3036 /* FIXME: use FileObject instead of FileHandle */
3037 Status = ExeFmtpLoaders[i](FileHeader,
3038 FileHeaderSize,
3039 FileHandle,
3040 ImageSectionObject,
3041 &Flags,
3042 ExeFmtpReadFile,
3043 ExeFmtpAllocateSegments);
3044
3045 if (!NT_SUCCESS(Status))
3046 {
3047 if (ImageSectionObject->Segments)
3048 {
3049 ExFreePool(ImageSectionObject->Segments);
3050 ImageSectionObject->Segments = NULL;
3051 }
3052 }
3053
3054 if (Status != STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
3055 break;
3056 }
3057
3058 ExFreePool(FileHeaderBuffer);
3059
3060 /*
3061 * No loader handled the format
3062 */
3063 if (Status == STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
3064 {
3065 Status = STATUS_INVALID_IMAGE_FORMAT;
3066 ASSERT(!NT_SUCCESS(Status));
3067 }
3068
3069 if (!NT_SUCCESS(Status))
3070 return Status;
3071
3072 ASSERT(ImageSectionObject->Segments != NULL);
3073
3074 /*
3075 * Some defaults
3076 */
3077 /* FIXME? are these values platform-dependent? */
3078 if(ImageSectionObject->StackReserve == 0)
3079 ImageSectionObject->StackReserve = 0x40000;
3080
3081 if(ImageSectionObject->StackCommit == 0)
3082 ImageSectionObject->StackCommit = 0x1000;
3083
3084 if(ImageSectionObject->ImageBase == 0)
3085 {
3086 if(ImageSectionObject->ImageCharacteristics & IMAGE_FILE_DLL)
3087 ImageSectionObject->ImageBase = 0x10000000;
3088 else
3089 ImageSectionObject->ImageBase = 0x00400000;
3090 }
3091
3092 /*
3093 * And now the fun part: fixing the segments
3094 */
3095
3096 /* Sort them by virtual address */
3097 MmspSortSegments(ImageSectionObject, Flags);
3098
3099 /* Ensure they don't overlap in memory */
3100 if (!MmspCheckSegmentBounds(ImageSectionObject, Flags))
3101 return STATUS_INVALID_IMAGE_FORMAT;
3102
3103 /* Ensure they are aligned */
3104 OldNrSegments = ImageSectionObject->NrSegments;
3105
3106 if (!MmspPageAlignSegments(ImageSectionObject, Flags))
3107 return STATUS_INVALID_IMAGE_FORMAT;
3108
3109 /* Trim them if the alignment phase merged some of them */
3110 if (ImageSectionObject->NrSegments < OldNrSegments)
3111 {
3112 PMM_SECTION_SEGMENT Segments;
3113 SIZE_T SizeOfSegments;
3114
3115 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * ImageSectionObject->NrSegments;
3116
3117 Segments = ExAllocatePoolWithTag(NonPagedPool,
3118 SizeOfSegments,
3119 TAG_MM_SECTION_SEGMENT);
3120
3121 if (Segments == NULL)
3122 return STATUS_INSUFFICIENT_RESOURCES;
3123
3124 RtlCopyMemory(Segments, ImageSectionObject->Segments, SizeOfSegments);
3125 ExFreePool(ImageSectionObject->Segments);
3126 ImageSectionObject->Segments = Segments;
3127 }
3128
3129 /* And finish their initialization */
3130 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3131 {
3132 ExInitializeFastMutex(&ImageSectionObject->Segments[i].Lock);
3133 ImageSectionObject->Segments[i].ReferenceCount = 1;
3134
3135 RtlZeroMemory(&ImageSectionObject->Segments[i].PageDirectory,
3136 sizeof(ImageSectionObject->Segments[i].PageDirectory));
3137 }
3138
3139 ASSERT(NT_SUCCESS(Status));
3140 return Status;
3141 }
3142
3143 NTSTATUS
3144 MmCreateImageSection(PSECTION_OBJECT *SectionObject,
3145 ACCESS_MASK DesiredAccess,
3146 POBJECT_ATTRIBUTES ObjectAttributes,
3147 PLARGE_INTEGER UMaximumSize,
3148 ULONG SectionPageProtection,
3149 ULONG AllocationAttributes,
3150 HANDLE FileHandle)
3151 {
3152 PSECTION_OBJECT Section;
3153 NTSTATUS Status;
3154 PFILE_OBJECT FileObject;
3155 PMM_SECTION_SEGMENT SectionSegments;
3156 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3157 ULONG i;
3158 ULONG FileAccess = 0;
3159
3160 /*
3161 * Specifying a maximum size is meaningless for an image section
3162 */
3163 if (UMaximumSize != NULL)
3164 {
3165 return(STATUS_INVALID_PARAMETER_4);
3166 }
3167
3168 /*
3169 * Check file access required
3170 */
3171 if (SectionPageProtection & PAGE_READWRITE ||
3172 SectionPageProtection & PAGE_EXECUTE_READWRITE)
3173 {
3174 FileAccess = FILE_READ_DATA | FILE_WRITE_DATA;
3175 }
3176 else
3177 {
3178 FileAccess = FILE_READ_DATA;
3179 }
3180
3181 /*
3182 * Reference the file handle
3183 */
3184 Status = ObReferenceObjectByHandle(FileHandle,
3185 FileAccess,
3186 IoFileObjectType,
3187 UserMode,
3188 (PVOID*)(PVOID)&FileObject,
3189 NULL);
3190 if (!NT_SUCCESS(Status))
3191 {
3192 return Status;
3193 }
3194
3195 /*
3196 * Create the section
3197 */
3198 Status = ObCreateObject (ExGetPreviousMode(),
3199 MmSectionObjectType,
3200 ObjectAttributes,
3201 ExGetPreviousMode(),
3202 NULL,
3203 sizeof(SECTION_OBJECT),
3204 0,
3205 0,
3206 (PVOID*)(PVOID)&Section);
3207 if (!NT_SUCCESS(Status))
3208 {
3209 ObDereferenceObject(FileObject);
3210 return(Status);
3211 }
3212
3213 /*
3214 * Initialize it
3215 */
3216 Section->SectionPageProtection = SectionPageProtection;
3217 Section->AllocationAttributes = AllocationAttributes;
3218 InitializeListHead(&Section->ViewListHead);
3219 KeInitializeSpinLock(&Section->ViewListLock);
3220
3221 /*
3222 * Initialized caching for this file object if previously caching
3223 * was initialized for the same on disk file
3224 */
3225 Status = CcTryToInitializeFileCache(FileObject);
3226
3227 if (!NT_SUCCESS(Status) || FileObject->SectionObjectPointer->ImageSectionObject == NULL)
3228 {
3229 NTSTATUS StatusExeFmt;
3230
3231 ImageSectionObject = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_IMAGE_SECTION_OBJECT), TAG_MM_SECTION_SEGMENT);
3232 if (ImageSectionObject == NULL)
3233 {
3234 ObDereferenceObject(FileObject);
3235 ObDereferenceObject(Section);
3236 return(STATUS_NO_MEMORY);
3237 }
3238
3239 StatusExeFmt = ExeFmtpCreateImageSection(FileHandle, ImageSectionObject);
3240
3241 if (!NT_SUCCESS(StatusExeFmt))
3242 {
3243 if(ImageSectionObject->Segments != NULL)
3244 ExFreePool(ImageSectionObject->Segments);
3245
3246 ExFreePool(ImageSectionObject);
3247 ObDereferenceObject(Section);
3248 ObDereferenceObject(FileObject);
3249 return(StatusExeFmt);
3250 }
3251
3252 Section->ImageSection = ImageSectionObject;
3253 ASSERT(ImageSectionObject->Segments);
3254
3255 /*
3256 * Lock the file
3257 */
3258 Status = MmspWaitForFileLock(FileObject);
3259 if (!NT_SUCCESS(Status))
3260 {
3261 ExFreePool(ImageSectionObject->Segments);
3262 ExFreePool(ImageSectionObject);
3263 ObDereferenceObject(Section);
3264 ObDereferenceObject(FileObject);
3265 return(Status);
3266 }
3267
3268 if (0 != InterlockedCompareExchangeUL(&FileObject->SectionObjectPointer->ImageSectionObject,
3269 ImageSectionObject, 0))
3270 {
3271 /*
3272 * An other thread has initialized the some image in the background
3273 */
3274 ExFreePool(ImageSectionObject->Segments);
3275 ExFreePool(ImageSectionObject);
3276 ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3277 Section->ImageSection = ImageSectionObject;
3278 SectionSegments = ImageSectionObject->Segments;
3279
3280 for (i = 0; i < ImageSectionObject->NrSegments; i++)
3281 {
3282 InterlockedIncrementUL(&SectionSegments[i].ReferenceCount);
3283 }
3284 }
3285
3286 Status = StatusExeFmt;
3287 }
3288 else
3289 {
3290 /*
3291 * Lock the file
3292 */
3293 Status = MmspWaitForFileLock(FileObject);
3294 if (Status != STATUS_SUCCESS)
3295 {
3296 ObDereferenceObject(Section);
3297 ObDereferenceObject(FileObject);
3298 return(Status);
3299 }
3300
3301 ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3302 Section->ImageSection = ImageSectionObject;
3303 SectionSegments = ImageSectionObject->Segments;
3304
3305 /*
3306 * Otherwise just reference all the section segments
3307 */
3308 for (i = 0; i < ImageSectionObject->NrSegments; i++)
3309 {
3310 InterlockedIncrementUL(&SectionSegments[i].ReferenceCount);
3311 }
3312
3313 Status = STATUS_SUCCESS;
3314 }
3315 Section->FileObject = FileObject;
3316 CcRosReferenceCache(FileObject);
3317 KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3318 *SectionObject = Section;
3319 return(Status);
3320 }
3321
3322 /*
3323 * @implemented
3324 */
3325 NTSTATUS STDCALL
3326 NtCreateSection (OUT PHANDLE SectionHandle,
3327 IN ACCESS_MASK DesiredAccess,
3328 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
3329 IN PLARGE_INTEGER MaximumSize OPTIONAL,
3330 IN ULONG SectionPageProtection OPTIONAL,
3331 IN ULONG AllocationAttributes,
3332 IN HANDLE FileHandle OPTIONAL)
3333 {
3334 PSECTION_OBJECT SectionObject;
3335 NTSTATUS Status;
3336
3337 /*
3338 * Check the protection
3339 */
3340 if ((SectionPageProtection & PAGE_FLAGS_VALID_FROM_USER_MODE) !=
3341 SectionPageProtection)
3342 {
3343 return(STATUS_INVALID_PAGE_PROTECTION);
3344 }
3345
3346 Status = MmCreateSection(&SectionObject,
3347 DesiredAccess,
3348 ObjectAttributes,
3349 MaximumSize,
3350 SectionPageProtection,
3351 AllocationAttributes,
3352 FileHandle,
3353 NULL);
3354
3355 if (NT_SUCCESS(Status))
3356 {
3357 Status = ObInsertObject ((PVOID)SectionObject,
3358 NULL,
3359 DesiredAccess,
3360 0,
3361 NULL,
3362 SectionHandle);
3363 ObDereferenceObject(SectionObject);
3364 }
3365
3366 return Status;
3367 }
3368
3369
3370 /**********************************************************************
3371 * NAME
3372 * NtOpenSection
3373 *
3374 * DESCRIPTION
3375 *
3376 * ARGUMENTS
3377 * SectionHandle
3378 *
3379 * DesiredAccess
3380 *
3381 * ObjectAttributes
3382 *
3383 * RETURN VALUE
3384 *
3385 * REVISIONS
3386 */
3387 NTSTATUS STDCALL
3388 NtOpenSection(PHANDLE SectionHandle,
3389 ACCESS_MASK DesiredAccess,
3390 POBJECT_ATTRIBUTES ObjectAttributes)
3391 {
3392 NTSTATUS Status;
3393
3394 *SectionHandle = 0;
3395
3396 Status = ObOpenObjectByName(ObjectAttributes,
3397 MmSectionObjectType,
3398 NULL,
3399 UserMode,
3400 DesiredAccess,
3401 NULL,
3402 SectionHandle);
3403
3404 return(Status);
3405 }
3406
3407 NTSTATUS STATIC
3408 MmMapViewOfSegment(PEPROCESS Process,
3409 PMADDRESS_SPACE AddressSpace,
3410 PSECTION_OBJECT Section,
3411 PMM_SECTION_SEGMENT Segment,
3412 PVOID* BaseAddress,
3413 ULONG ViewSize,
3414 ULONG Protect,
3415 ULONG ViewOffset,
3416 BOOL TopDown)
3417 {
3418 PMEMORY_AREA MArea;
3419 NTSTATUS Status;
3420 KIRQL oldIrql;
3421 PHYSICAL_ADDRESS BoundaryAddressMultiple;
3422
3423 BoundaryAddressMultiple.QuadPart = 0;
3424
3425 Status = MmCreateMemoryArea(Process,
3426 AddressSpace,
3427 MEMORY_AREA_SECTION_VIEW,
3428 BaseAddress,
3429 ViewSize,
3430 Protect,
3431 &MArea,
3432 FALSE,
3433 TopDown,
3434 BoundaryAddressMultiple);
3435 if (!NT_SUCCESS(Status))
3436 {
3437 DPRINT1("Mapping between 0x%.8X and 0x%.8X failed (%X).\n",
3438 (*BaseAddress), (char*)(*BaseAddress) + ViewSize, Status);
3439 return(Status);
3440 }
3441
3442 KeAcquireSpinLock(&Section->ViewListLock, &oldIrql);
3443 InsertTailList(&Section->ViewListHead,
3444 &MArea->Data.SectionData.ViewListEntry);
3445 KeReleaseSpinLock(&Section->ViewListLock, oldIrql);
3446
3447 ObReferenceObjectByPointer((PVOID)Section,
3448 SECTION_MAP_READ,
3449 NULL,
3450 ExGetPreviousMode());
3451 MArea->Data.SectionData.Segment = Segment;
3452 MArea->Data.SectionData.Section = Section;
3453 MArea->Data.SectionData.ViewOffset = ViewOffset;
3454 MArea->Data.SectionData.WriteCopyView = FALSE;
3455 MmInitialiseRegion(&MArea->Data.SectionData.RegionListHead,
3456 ViewSize, 0, Protect);
3457
3458 return(STATUS_SUCCESS);
3459 }
3460
3461
3462 /**********************************************************************
3463 * NAME EXPORTED
3464 * NtMapViewOfSection
3465 *
3466 * DESCRIPTION
3467 * Maps a view of a section into the virtual address space of a
3468 * process.
3469 *
3470 * ARGUMENTS
3471 * SectionHandle
3472 * Handle of the section.
3473 *
3474 * ProcessHandle
3475 * Handle of the process.
3476 *
3477 * BaseAddress
3478 * Desired base address (or NULL) on entry;
3479 * Actual base address of the view on exit.
3480 *
3481 * ZeroBits
3482 * Number of high order address bits that must be zero.
3483 *
3484 * CommitSize
3485 * Size in bytes of the initially committed section of
3486 * the view.
3487 *
3488 * SectionOffset
3489 * Offset in bytes from the beginning of the section
3490 * to the beginning of the view.
3491 *
3492 * ViewSize
3493 * Desired length of map (or zero to map all) on entry
3494 * Actual length mapped on exit.
3495 *
3496 * InheritDisposition
3497 * Specified how the view is to be shared with
3498 * child processes.
3499 *
3500 * AllocateType
3501 * Type of allocation for the pages.
3502 *
3503 * Protect
3504 * Protection for the committed region of the view.
3505 *
3506 * RETURN VALUE
3507 * Status.
3508 *
3509 * @implemented
3510 */
3511 NTSTATUS STDCALL
3512 NtMapViewOfSection(HANDLE SectionHandle,
3513 HANDLE ProcessHandle,
3514 PVOID* BaseAddress,
3515 ULONG ZeroBits,
3516 ULONG CommitSize,
3517 PLARGE_INTEGER SectionOffset,
3518 PULONG ViewSize,
3519 SECTION_INHERIT InheritDisposition,
3520 ULONG AllocationType,
3521 ULONG Protect)
3522 {
3523 PSECTION_OBJECT Section;
3524 PEPROCESS Process;
3525 NTSTATUS Status;
3526 PMADDRESS_SPACE AddressSpace;
3527
3528 Status = ObReferenceObjectByHandle(ProcessHandle,
3529 PROCESS_VM_OPERATION,
3530 PsProcessType,
3531 UserMode,
3532 (PVOID*)(PVOID)&Process,
3533 NULL);
3534 if (!NT_SUCCESS(Status))
3535 {
3536 return(Status);
3537 }
3538
3539 AddressSpace = &Process->AddressSpace;
3540
3541 Status = ObReferenceObjectByHandle(SectionHandle,
3542 SECTION_MAP_READ,
3543 MmSectionObjectType,
3544 UserMode,
3545 (PVOID*)(PVOID)&Section,
3546 NULL);
3547 if (!(NT_SUCCESS(Status)))
3548 {
3549 DPRINT("ObReference failed rc=%x\n",Status);
3550 ObDereferenceObject(Process);
3551 return(Status);
3552 }
3553
3554 Status = MmMapViewOfSection(Section,
3555 Process,
3556 BaseAddress,
3557 ZeroBits,
3558 CommitSize,
3559 SectionOffset,
3560 ViewSize,
3561 InheritDisposition,
3562 AllocationType,
3563 Protect);
3564
3565 ObDereferenceObject(Section);
3566 ObDereferenceObject(Process);
3567
3568 return(Status);
3569 }
3570
3571 VOID STATIC
3572 MmFreeSectionPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
3573 PFN_TYPE Page, SWAPENTRY SwapEntry, BOOLEAN Dirty)
3574 {
3575 PMEMORY_AREA MArea;
3576 ULONG Entry;
3577 PFILE_OBJECT FileObject;
3578 PBCB Bcb;
3579 ULONG Offset;
3580 SWAPENTRY SavedSwapEntry;
3581 PMM_PAGEOP PageOp;
3582 NTSTATUS Status;
3583 PSECTION_OBJECT Section;
3584 PMM_SECTION_SEGMENT Segment;
3585
3586 MArea = (PMEMORY_AREA)Context;
3587
3588 Address = (PVOID)PAGE_ROUND_DOWN(Address);
3589
3590 Offset = ((ULONG_PTR)Address - (ULONG_PTR)MArea->StartingAddress) +
3591 MemoryArea->Data.SectionData.ViewOffset;
3592
3593 Section = MArea->Data.SectionData.Section;
3594 Segment = MArea->Data.SectionData.Segment;
3595
3596 PageOp = MmCheckForPageOp(MArea, 0, NULL, Segment, Offset);
3597
3598 while (PageOp)
3599 {
3600 MmUnlockSectionSegment(Segment);
3601 MmUnlockAddressSpace(&MArea->Process->AddressSpace);
3602
3603 Status = MmspWaitForPageOpCompletionEvent(PageOp);
3604 if (Status != STATUS_SUCCESS)
3605 {
3606 DPRINT1("Failed to wait for page op, status = %x\n", Status);
3607 KEBUGCHECK(0);
3608 }
3609
3610 MmLockAddressSpace(&MArea->Process->AddressSpace);
3611 MmLockSectionSegment(Segment);
3612 MmspCompleteAndReleasePageOp(PageOp);
3613 PageOp = MmCheckForPageOp(MArea, 0, NULL, Segment, Offset);
3614 }
3615
3616 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
3617
3618 /*
3619 * For a dirty, datafile, non-private page mark it as dirty in the
3620 * cache manager.
3621 */
3622 if (Segment->Flags & MM_DATAFILE_SEGMENT)
3623 {
3624 if (Page == PFN_FROM_SSE(Entry) && Dirty)
3625 {
3626 FileObject = MemoryArea->Data.SectionData.Section->FileObject;
3627 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
3628 CcRosMarkDirtyCacheSegment(Bcb, Offset);
3629 ASSERT(SwapEntry == 0);
3630 }
3631 }
3632
3633 if (SwapEntry != 0)
3634 {
3635 /*
3636 * Sanity check
3637 */
3638 if (Segment->Flags & MM_PAGEFILE_SEGMENT)
3639 {
3640 DPRINT1("Found a swap entry for a page in a pagefile section.\n");
3641 KEBUGCHECK(0);
3642 }
3643 MmFreeSwapPage(SwapEntry);
3644 }
3645 else if (Page != 0)
3646 {
3647 if (IS_SWAP_FROM_SSE(Entry) ||
3648 Page != PFN_FROM_SSE(Entry))
3649 {
3650 /*
3651 * Sanity check
3652 */
3653 if (Segment->Flags & MM_PAGEFILE_SEGMENT)
3654 {
3655 DPRINT1("Found a private page in a pagefile section.\n");
3656 KEBUGCHECK(0);
3657 }
3658 /*
3659 * Just dereference private pages
3660 */
3661 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
3662 if (SavedSwapEntry != 0)
3663 {
3664 MmFreeSwapPage(SavedSwapEntry);
3665 MmSetSavedSwapEntryPage(Page, 0);
3666 }
3667 MmDeleteRmap(Page, MArea->Process, Address);
3668 MmReleasePageMemoryConsumer(MC_USER, Page);
3669 }
3670 else
3671 {
3672 MmDeleteRmap(Page, MArea->Process, Address);
3673 MmUnsharePageEntrySectionSegment(Section, Segment, Offset, Dirty, FALSE);
3674 }
3675 }
3676 }
3677
3678 NTSTATUS
3679 MmUnmapViewOfSegment(PMADDRESS_SPACE AddressSpace,
3680 PVOID BaseAddress)
3681 {
3682 NTSTATUS Status;
3683 PMEMORY_AREA MemoryArea;
3684 PSECTION_OBJECT Section;
3685 PMM_SECTION_SEGMENT Segment;
3686 KIRQL oldIrql;
3687 PLIST_ENTRY CurrentEntry;
3688 PMM_REGION CurrentRegion;
3689 PLIST_ENTRY RegionListHead;
3690
3691 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace,
3692 BaseAddress);
3693 if (MemoryArea == NULL)
3694 {
3695 return(STATUS_UNSUCCESSFUL);
3696 }
3697
3698 MemoryArea->DeleteInProgress = TRUE;
3699 Section = MemoryArea->Data.SectionData.Section;
3700 Segment = MemoryArea->Data.SectionData.Segment;
3701
3702 MmLockSectionSegment(Segment);
3703 KeAcquireSpinLock(&Section->ViewListLock, &oldIrql);
3704 RemoveEntryList(&MemoryArea->Data.SectionData.ViewListEntry);
3705 KeReleaseSpinLock(&Section->ViewListLock, oldIrql);
3706
3707 RegionListHead = &MemoryArea->Data.SectionData.RegionListHead;
3708 while (!IsListEmpty(RegionListHead))
3709 {
3710 CurrentEntry = RemoveHeadList(RegionListHead);
3711 CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION, RegionListEntry);
3712 ExFreePool(CurrentRegion);
3713 }
3714
3715 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
3716 {
3717 Status = MmFreeMemoryArea(AddressSpace,
3718 MemoryArea,
3719 NULL,
3720 NULL);
3721 }
3722 else
3723 {
3724 Status = MmFreeMemoryArea(AddressSpace,
3725 MemoryArea,
3726 MmFreeSectionPage,
3727 MemoryArea);
3728 }
3729 MmUnlockSectionSegment(Segment);
3730 ObDereferenceObject(Section);
3731 return(STATUS_SUCCESS);
3732 }
3733
3734 /*
3735 * @implemented
3736 */
3737 NTSTATUS STDCALL
3738 MmUnmapViewOfSection(PEPROCESS Process,
3739 PVOID BaseAddress)
3740 {
3741 NTSTATUS Status;
3742 PMEMORY_AREA MemoryArea;
3743 PMADDRESS_SPACE AddressSpace;
3744 PSECTION_OBJECT Section;
3745
3746 DPRINT("Opening memory area Process %x BaseAddress %x\n",
3747 Process, BaseAddress);
3748
3749 ASSERT(Process);
3750
3751 AddressSpace = &Process->AddressSpace;
3752 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace,
3753 BaseAddress);
3754 if (MemoryArea == NULL ||
3755 MemoryArea->Type != MEMORY_AREA_SECTION_VIEW ||
3756 MemoryArea->DeleteInProgress)
3757 {
3758 return STATUS_NOT_MAPPED_VIEW;
3759 }
3760
3761 Section = MemoryArea->Data.SectionData.Section;
3762
3763 if (Section->AllocationAttributes & SEC_IMAGE)
3764 {
3765 ULONG i;
3766 ULONG NrSegments;
3767 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3768 PMM_SECTION_SEGMENT SectionSegments;
3769 PVOID ImageBaseAddress = 0;
3770 PMM_SECTION_SEGMENT Segment;
3771
3772 Segment = MemoryArea->Data.SectionData.Segment;
3773 ImageSectionObject = Section->ImageSection;
3774 SectionSegments = ImageSectionObject->Segments;
3775 NrSegments = ImageSectionObject->NrSegments;
3776
3777 /* Search for the current segment within the section segments
3778 * and calculate the image base address */
3779 for (i = 0; i < NrSegments; i++)
3780 {
3781 if (!(SectionSegments[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
3782 {
3783 if (Segment == &SectionSegments[i])
3784 {
3785 ImageBaseAddress = (char*)BaseAddress - (ULONG_PTR)SectionSegments[i].VirtualAddress;
3786 break;
3787 }
3788 }
3789 }
3790 if (i >= NrSegments)
3791 {
3792 KEBUGCHECK(0);
3793 }
3794
3795 for (i = 0; i < NrSegments; i++)
3796 {
3797 if (!(SectionSegments[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
3798 {
3799 PVOID SBaseAddress = (PVOID)
3800 ((char*)ImageBaseAddress + (ULONG_PTR)SectionSegments[i].VirtualAddress);
3801
3802 Status = MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
3803 }
3804 }
3805 }
3806 else
3807 {
3808 Status = MmUnmapViewOfSegment(AddressSpace, BaseAddress);
3809 }
3810 return(STATUS_SUCCESS);
3811 }
3812
3813 /**********************************************************************
3814 * NAME EXPORTED
3815 * NtUnmapViewOfSection
3816 *
3817 * DESCRIPTION
3818 *
3819 * ARGUMENTS
3820 * ProcessHandle
3821 *
3822 * BaseAddress
3823 *
3824 * RETURN VALUE
3825 * Status.
3826 *
3827 * REVISIONS
3828 */
3829 NTSTATUS STDCALL
3830 NtUnmapViewOfSection (HANDLE ProcessHandle,
3831 PVOID BaseAddress)
3832 {
3833 PEPROCESS Process;
3834 NTSTATUS Status;
3835
3836 DPRINT("NtUnmapViewOfSection(ProcessHandle %x, BaseAddress %x)\n",
3837 ProcessHandle, BaseAddress);
3838
3839 DPRINT("Referencing process\n");
3840 Status = ObReferenceObjectByHandle(ProcessHandle,
3841 PROCESS_VM_OPERATION,
3842 PsProcessType,
3843 UserMode,
3844 (PVOID*)(PVOID)&Process,
3845 NULL);
3846 if (!NT_SUCCESS(Status))
3847 {
3848 DPRINT("ObReferenceObjectByHandle failed (Status %x)\n", Status);
3849 return(Status);
3850 }
3851
3852 MmLockAddressSpace(&Process->AddressSpace);
3853 Status = MmUnmapViewOfSection(Process, BaseAddress);
3854 MmUnlockAddressSpace(&Process->AddressSpace);
3855
3856 ObDereferenceObject(Process);
3857
3858 return Status;
3859 }
3860
3861
3862 /**
3863 * Queries the information of a section object.
3864 *
3865 * @param SectionHandle
3866 * Handle to the section object. It must be opened with SECTION_QUERY
3867 * access.
3868 * @param SectionInformationClass
3869 * Index to a certain information structure. Can be either
3870 * SectionBasicInformation or SectionImageInformation. The latter
3871 * is valid only for sections that were created with the SEC_IMAGE
3872 * flag.
3873 * @param SectionInformation
3874 * Caller supplies storage for resulting information.
3875 * @param Length
3876 * Size of the supplied storage.
3877 * @param ResultLength
3878 * Data written.
3879 *
3880 * @return Status.
3881 *
3882 * @todo Guard by SEH.
3883 * @implemented
3884 */
3885 NTSTATUS STDCALL
3886 NtQuerySection(IN HANDLE SectionHandle,
3887 IN CINT SectionInformationClass,
3888 OUT PVOID SectionInformation,
3889 IN ULONG Length,
3890 OUT PULONG ResultLength)
3891 {
3892 PSECTION_OBJECT Section;
3893 NTSTATUS Status;
3894
3895 Status = ObReferenceObjectByHandle(SectionHandle,
3896 SECTION_QUERY,
3897 MmSectionObjectType,
3898 UserMode,
3899 (PVOID*)(PVOID)&Section,
3900 NULL);
3901 if (!(NT_SUCCESS(Status)))
3902 {
3903 return(Status);
3904 }
3905
3906 switch (SectionInformationClass)
3907 {
3908 case SectionBasicInformation:
3909 {
3910 PSECTION_BASIC_INFORMATION Sbi;
3911
3912 if (Length != sizeof(SECTION_BASIC_INFORMATION))
3913 {
3914 ObDereferenceObject(Section);
3915 return(STATUS_INFO_LENGTH_MISMATCH);
3916 }
3917
3918 Sbi = (PSECTION_BASIC_INFORMATION)SectionInformation;
3919
3920 Sbi->Attributes = Section->AllocationAttributes;
3921 if (Section->AllocationAttributes & SEC_IMAGE)
3922 {
3923 Sbi->BaseAddress = 0;
3924 Sbi->Size.QuadPart = 0;
3925 }
3926 else
3927 {
3928 Sbi->BaseAddress = (PVOID)Section->Segment->VirtualAddress;
3929 Sbi->Size.QuadPart = Section->Segment->Length;
3930 }
3931
3932 *ResultLength = sizeof(SECTION_BASIC_INFORMATION);
3933 Status = STATUS_SUCCESS;
3934 break;
3935 }
3936
3937 case SectionImageInformation:
3938 {
3939 PSECTION_IMAGE_INFORMATION Sii;
3940
3941 if (Length != sizeof(SECTION_IMAGE_INFORMATION))
3942 {
3943 ObDereferenceObject(Section);
3944 return(STATUS_INFO_LENGTH_MISMATCH);
3945 }
3946
3947 Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation;
3948 memset(Sii, 0, sizeof(SECTION_IMAGE_INFORMATION));
3949 if (Section->AllocationAttributes & SEC_IMAGE)
3950 {
3951 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3952 ImageSectionObject = Section->ImageSection;
3953
3954 Sii->EntryPoint = ImageSectionObject->EntryPoint;
3955 Sii->StackReserve = ImageSectionObject->StackReserve;
3956 Sii->StackCommit = ImageSectionObject->StackCommit;
3957 Sii->Subsystem = ImageSectionObject->Subsystem;
3958 Sii->MinorSubsystemVersion = ImageSectionObject->MinorSubsystemVersion;
3959 Sii->MajorSubsystemVersion = ImageSectionObject->MajorSubsystemVersion;
3960 Sii->Characteristics = ImageSectionObject->ImageCharacteristics;
3961 Sii->ImageNumber = ImageSectionObject->Machine;
3962 Sii->Executable = ImageSectionObject->Executable;
3963 }
3964 *ResultLength = sizeof(SECTION_IMAGE_INFORMATION);
3965 Status = STATUS_SUCCESS;
3966 break;
3967 }
3968
3969 default:
3970 *ResultLength = 0;
3971 Status = STATUS_INVALID_INFO_CLASS;
3972 }
3973 ObDereferenceObject(Section);
3974 return(Status);
3975 }
3976
3977
3978 /**
3979 * Extends size of file backed section.
3980 *
3981 * @param SectionHandle
3982 * Handle to the section object. It must be opened with
3983 * SECTION_EXTEND_SIZE access.
3984 * @param NewMaximumSize
3985 * New maximum size of the section in bytes.
3986 *
3987 * @return Status.
3988 *
3989 * @todo Guard by SEH.
3990 * @todo Move the actual code to internal function MmExtendSection.
3991 * @unimplemented
3992 */
3993 NTSTATUS STDCALL
3994 NtExtendSection(IN HANDLE SectionHandle,
3995 IN PLARGE_INTEGER NewMaximumSize)
3996 {
3997 PSECTION_OBJECT Section;
3998 NTSTATUS Status;
3999
4000 Status = ObReferenceObjectByHandle(SectionHandle,
4001 SECTION_EXTEND_SIZE,
4002 MmSectionObjectType,
4003 UserMode,
4004 (PVOID*)&Section,
4005 NULL);
4006 if (!NT_SUCCESS(Status))
4007 {
4008 return Status;
4009 }
4010
4011 if (!(Section->AllocationAttributes & SEC_FILE))
4012 {
4013 ObfDereferenceObject(Section);
4014 return STATUS_INVALID_PARAMETER;
4015 }
4016
4017 /*
4018 * - Acquire file extneding resource.
4019 * - Check if we're not resizing the section below it's actual size!
4020 * - Extend segments if needed.
4021 * - Set file information (FileAllocationInformation) to the new size.
4022 * - Release file extending resource.
4023 */
4024
4025 ObDereferenceObject(Section);
4026
4027 return STATUS_NOT_IMPLEMENTED;
4028 }
4029
4030
4031 /**********************************************************************
4032 * NAME INTERNAL
4033 * MmAllocateSection@4
4034 *
4035 * DESCRIPTION
4036 *
4037 * ARGUMENTS
4038 * Length
4039 *
4040 * RETURN VALUE
4041 *
4042 * NOTE
4043 * Code taken from ntoskrnl/mm/special.c.
4044 *
4045 * REVISIONS
4046 */
4047 PVOID STDCALL
4048 MmAllocateSection (IN ULONG Length, PVOID BaseAddress)
4049 {
4050 PVOID Result;
4051 MEMORY_AREA* marea;
4052 NTSTATUS Status;
4053 ULONG i;
4054 PMADDRESS_SPACE AddressSpace;
4055 PHYSICAL_ADDRESS BoundaryAddressMultiple;
4056
4057 DPRINT("MmAllocateSection(Length %x)\n",Length);
4058
4059 BoundaryAddressMultiple.QuadPart = 0;
4060
4061 AddressSpace = MmGetKernelAddressSpace();
4062 Result = BaseAddress;
4063 MmLockAddressSpace(AddressSpace);
4064 Status = MmCreateMemoryArea (NULL,
4065 AddressSpace,
4066 MEMORY_AREA_SYSTEM,
4067 &Result,
4068 Length,
4069 0,
4070 &marea,
4071 FALSE,
4072 FALSE,
4073 BoundaryAddressMultiple);
4074 MmUnlockAddressSpace(AddressSpace);
4075
4076 if (!NT_SUCCESS(Status))
4077 {
4078 return (NULL);
4079 }
4080 DPRINT("Result %p\n",Result);
4081 for (i = 0; i < PAGE_ROUND_UP(Length) / PAGE_SIZE; i++)
4082 {
4083 PFN_TYPE Page;
4084
4085 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &Page);
4086 if (!NT_SUCCESS(Status))
4087 {
4088 DbgPrint("Unable to allocate page\n");
4089 KEBUGCHECK(0);
4090 }
4091 Status = MmCreateVirtualMapping (NULL,
4092 (PVOID)(Result + (i * PAGE_SIZE)),
4093 PAGE_READWRITE,
4094 &Page,
4095 1);
4096 if (!NT_SUCCESS(Status))
4097 {
4098 DbgPrint("Unable to create virtual mapping\n");
4099 KEBUGCHECK(0);
4100 }
4101 }
4102 return ((PVOID)Result);
4103 }
4104
4105
4106 /**********************************************************************
4107 * NAME EXPORTED
4108 * MmMapViewOfSection
4109 *
4110 * DESCRIPTION
4111 * Maps a view of a section into the virtual address space of a
4112 * process.
4113 *
4114 * ARGUMENTS
4115 * Section
4116 * Pointer to the section object.
4117 *
4118 * ProcessHandle
4119 * Pointer to the process.
4120 *
4121 * BaseAddress
4122 * Desired base address (or NULL) on entry;
4123 * Actual base address of the view on exit.
4124 *
4125 * ZeroBits
4126 * Number of high order address bits that must be zero.
4127 *
4128 * CommitSize
4129 * Size in bytes of the initially committed section of
4130 * the view.
4131 *
4132 * SectionOffset
4133 * Offset in bytes from the beginning of the section
4134 * to the beginning of the view.
4135 *
4136 * ViewSize
4137 * Desired length of map (or zero to map all) on entry
4138 * Actual length mapped on exit.
4139 *
4140 * InheritDisposition
4141 * Specified how the view is to be shared with
4142 * child processes.
4143 *
4144 * AllocationType
4145 * Type of allocation for the pages.
4146 *
4147 * Protect
4148 * Protection for the committed region of the view.
4149 *
4150 * RETURN VALUE
4151 * Status.
4152 *
4153 * @implemented
4154 */
4155 NTSTATUS STDCALL
4156 MmMapViewOfSection(IN PVOID SectionObject,
4157 IN PEPROCESS Process,
4158 IN OUT PVOID *BaseAddress,
4159 IN ULONG ZeroBits,
4160 IN ULONG CommitSize,
4161 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
4162 IN OUT PULONG ViewSize,
4163 IN SECTION_INHERIT InheritDisposition,
4164 IN ULONG AllocationType,
4165 IN ULONG Protect)
4166 {
4167 PSECTION_OBJECT Section;
4168 PMADDRESS_SPACE AddressSpace;
4169 ULONG ViewOffset;
4170 NTSTATUS Status = STATUS_SUCCESS;
4171
4172 ASSERT(Process);
4173
4174 Section = (PSECTION_OBJECT)SectionObject;
4175 AddressSpace = &Process->AddressSpace;
4176
4177 MmLockAddressSpace(AddressSpace);
4178
4179 if (Section->AllocationAttributes & SEC_IMAGE)
4180 {
4181 ULONG i;
4182 ULONG NrSegments;
4183 ULONG_PTR ImageBase;
4184 ULONG ImageSize;
4185 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
4186 PMM_SECTION_SEGMENT SectionSegments;
4187
4188 ImageSectionObject = Section->ImageSection;
4189 SectionSegments = ImageSectionObject->Segments;
4190 NrSegments = ImageSectionObject->NrSegments;
4191
4192
4193 ImageBase = (ULONG_PTR)*BaseAddress;
4194 if (ImageBase == 0)
4195 {
4196 ImageBase = ImageSectionObject->ImageBase;
4197 }
4198
4199 ImageSize = 0;
4200 for (i = 0; i < NrSegments; i++)
4201 {
4202 if (!(SectionSegments[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
4203 {
4204 ULONG MaxExtent;
4205 MaxExtent = (ULONG)((char*)SectionSegments[i].VirtualAddress +
4206 SectionSegments[i].Length);
4207 ImageSize = max(ImageSize, MaxExtent);
4208 }
4209 }
4210
4211 /* Check there is enough space to map the section at that point. */
4212 if (MmLocateMemoryAreaByRegion(AddressSpace, (PVOID)ImageBase,
4213 PAGE_ROUND_UP(ImageSize)) != NULL)
4214 {
4215 /* Fail if the user requested a fixed base address. */
4216 if ((*BaseAddress) != NULL)
4217 {
4218 MmUnlockAddressSpace(AddressSpace);
4219 return(STATUS_UNSUCCESSFUL);
4220 }
4221 /* Otherwise find a gap to map the image. */
4222 ImageBase = (ULONG_PTR)MmFindGap(AddressSpace, PAGE_ROUND_UP(ImageSize), PAGE_SIZE, FALSE);
4223 if (ImageBase == 0)
4224 {
4225 MmUnlockAddressSpace(AddressSpace);
4226 return(STATUS_UNSUCCESSFUL);
4227 }
4228 }
4229
4230 for (i = 0; i < NrSegments; i++)
4231 {
4232 if (!(SectionSegments[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
4233 {
4234 PVOID SBaseAddress = (PVOID)
4235 ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].VirtualAddress);
4236 MmLockSectionSegment(&SectionSegments[i]);
4237 Status = MmMapViewOfSegment(Process,
4238 AddressSpace,
4239 Section,
4240 &SectionSegments[i],
4241 &SBaseAddress,
4242 SectionSegments[i].Length,
4243 SectionSegments[i].Protection,
4244 0,
4245 FALSE);
4246 MmUnlockSectionSegment(&SectionSegments[i]);
4247 if (!NT_SUCCESS(Status))
4248 {
4249 MmUnlockAddressSpace(AddressSpace);
4250 return(Status);
4251 }
4252 }
4253 }
4254
4255 *BaseAddress = (PVOID)ImageBase;
4256 }
4257 else
4258 {
4259 if (ViewSize == NULL)
4260 {
4261 /* Following this pointer would lead to us to the dark side */
4262 /* What to do? Bugcheck? Return status? Do the mambo? */
4263 KEBUGCHECK(MEMORY_MANAGEMENT);
4264 }
4265
4266 if (SectionOffset == NULL)
4267 {
4268 ViewOffset = 0;
4269 }
4270 else
4271 {
4272 ViewOffset = SectionOffset->u.LowPart;
4273 }
4274
4275 if ((ViewOffset % PAGE_SIZE) != 0)
4276 {
4277 MmUnlockAddressSpace(AddressSpace);
4278 return(STATUS_MAPPED_ALIGNMENT);
4279 }
4280
4281 if ((*ViewSize) == 0)
4282 {
4283 (*ViewSize) = Section->MaximumSize.u.LowPart - ViewOffset;
4284 }
4285 else if (((*ViewSize)+ViewOffset) > Section->MaximumSize.u.LowPart)
4286 {
4287 (*ViewSize) = Section->MaximumSize.u.LowPart - ViewOffset;
4288 }
4289
4290 MmLockSectionSegment(Section->Segment);
4291 Status = MmMapViewOfSegment(Process,
4292 AddressSpace,
4293 Section,
4294 Section->Segment,
4295 BaseAddress,
4296 *ViewSize,
4297 Protect,
4298 ViewOffset,
4299 (AllocationType & MEM_TOP_DOWN));
4300 MmUnlockSectionSegment(Section->Segment);
4301 if (!NT_SUCCESS(Status))
4302 {
4303 MmUnlockAddressSpace(AddressSpace);
4304 return(Status);
4305 }
4306 }
4307
4308 MmUnlockAddressSpace(AddressSpace);
4309
4310 return(STATUS_SUCCESS);
4311 }
4312
4313 /*
4314 * @unimplemented
4315 */
4316 BOOLEAN STDCALL
4317 MmCanFileBeTruncated (IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
4318 IN PLARGE_INTEGER NewFileSize)
4319 {
4320 UNIMPLEMENTED;
4321 return (FALSE);
4322 }
4323
4324
4325 /*
4326 * @unimplemented
4327 */
4328 BOOLEAN STDCALL
4329 MmDisableModifiedWriteOfSection (DWORD Unknown0)
4330 {
4331 UNIMPLEMENTED;
4332 return (FALSE);
4333 }
4334
4335 /*
4336 * @implemented
4337 */
4338 BOOLEAN STDCALL
4339 MmFlushImageSection (IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
4340 IN MMFLUSH_TYPE FlushType)
4341 {
4342 switch(FlushType)
4343 {
4344 case MmFlushForDelete:
4345 if (SectionObjectPointer->ImageSectionObject ||
4346 SectionObjectPointer->DataSectionObject)
4347 {
4348 return FALSE;
4349 }
4350 CcRosSetRemoveOnClose(SectionObjectPointer);
4351 return TRUE;
4352 case MmFlushForWrite:
4353 break;
4354 }
4355 return FALSE;
4356 }
4357
4358 /*
4359 * @unimplemented
4360 */
4361 BOOLEAN STDCALL
4362 MmForceSectionClosed (
4363 IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
4364 IN BOOLEAN DelayClose)
4365 {
4366 UNIMPLEMENTED;
4367 return (FALSE);
4368 }
4369
4370
4371 /*
4372 * @implemented
4373 */
4374 NTSTATUS STDCALL
4375 MmMapViewInSystemSpace (IN PVOID SectionObject,
4376 OUT PVOID * MappedBase,
4377 IN OUT PULONG ViewSize)
4378 {
4379 PSECTION_OBJECT Section;
4380 PMADDRESS_SPACE AddressSpace;
4381 NTSTATUS Status;
4382
4383 DPRINT("MmMapViewInSystemSpace() called\n");
4384
4385 Section = (PSECTION_OBJECT)SectionObject;
4386 AddressSpace = MmGetKernelAddressSpace();
4387
4388 MmLockAddressSpace(AddressSpace);
4389
4390
4391 if ((*ViewSize) == 0)
4392 {
4393 (*ViewSize) = Section->MaximumSize.u.LowPart;
4394 }
4395 else if ((*ViewSize) > Section->MaximumSize.u.LowPart)
4396 {
4397 (*ViewSize) = Section->MaximumSize.u.LowPart;
4398 }
4399
4400 MmLockSectionSegment(Section->Segment);
4401
4402
4403 Status = MmMapViewOfSegment(NULL,
4404 AddressSpace,
4405 Section,
4406 Section->Segment,
4407 MappedBase,
4408 *ViewSize,
4409 PAGE_READWRITE,
4410 0,
4411 FALSE);
4412
4413 MmUnlockSectionSegment(Section->Segment);
4414 MmUnlockAddressSpace(AddressSpace);
4415
4416 return Status;
4417 }
4418
4419 /*
4420 * @unimplemented
4421 */
4422 NTSTATUS
4423 STDCALL
4424 MmMapViewInSessionSpace (
4425 IN PVOID Section,
4426 OUT PVOID *MappedBase,
4427 IN OUT PSIZE_T ViewSize
4428 )
4429 {
4430 UNIMPLEMENTED;
4431 return STATUS_NOT_IMPLEMENTED;
4432 }
4433
4434
4435 /*
4436 * @implemented
4437 */
4438 NTSTATUS STDCALL
4439 MmUnmapViewInSystemSpace (IN PVOID MappedBase)
4440 {
4441 PMADDRESS_SPACE AddressSpace;
4442 NTSTATUS Status;
4443
4444 DPRINT("MmUnmapViewInSystemSpace() called\n");
4445
4446 AddressSpace = MmGetKernelAddressSpace();
4447
4448 Status = MmUnmapViewOfSegment(AddressSpace, MappedBase);
4449
4450 return Status;
4451 }
4452
4453 /*
4454 * @unimplemented
4455 */
4456 NTSTATUS
4457 STDCALL
4458 MmUnmapViewInSessionSpace (
4459 IN PVOID MappedBase
4460 )
4461 {
4462 UNIMPLEMENTED;
4463 return STATUS_NOT_IMPLEMENTED;
4464 }
4465
4466 /*
4467 * @unimplemented
4468 */
4469 NTSTATUS STDCALL
4470 MmSetBankedSection (DWORD Unknown0,
4471 DWORD Unknown1,
4472 DWORD Unknown2,
4473 DWORD Unknown3,
4474 DWORD Unknown4,
4475 DWORD Unknown5)
4476 {
4477 UNIMPLEMENTED;
4478 return (STATUS_NOT_IMPLEMENTED);
4479 }
4480
4481
4482 /**********************************************************************
4483 * NAME EXPORTED
4484 * MmCreateSection@
4485 *
4486 * DESCRIPTION
4487 * Creates a section object.
4488 *
4489 * ARGUMENTS
4490 * SectionObject (OUT)
4491 * Caller supplied storage for the resulting pointer
4492 * to a SECTION_OBJECT instance;
4493 *
4494 * DesiredAccess
4495 * Specifies the desired access to the section can be a
4496 * combination of:
4497 * STANDARD_RIGHTS_REQUIRED |
4498 * SECTION_QUERY |
4499 * SECTION_MAP_WRITE |
4500 * SECTION_MAP_READ |
4501 * SECTION_MAP_EXECUTE
4502 *
4503 * ObjectAttributes [OPTIONAL]
4504 * Initialized attributes for the object can be used
4505 * to create a named section;
4506 *
4507 * MaximumSize
4508 * Maximizes the size of the memory section. Must be
4509 * non-NULL for a page-file backed section.
4510 * If value specified for a mapped file and the file is
4511 * not large enough, file will be extended.
4512 *
4513 * SectionPageProtection
4514 * Can be a combination of:
4515 * PAGE_READONLY |
4516 * PAGE_READWRITE |
4517 * PAGE_WRITEONLY |
4518 * PAGE_WRITECOPY
4519 *
4520 * AllocationAttributes
4521 * Can be a combination of:
4522 * SEC_IMAGE |
4523 * SEC_RESERVE
4524 *
4525 * FileHandle
4526 * Handle to a file to create a section mapped to a file
4527 * instead of a memory backed section;
4528 *
4529 * File
4530 * Unknown.
4531 *
4532 * RETURN VALUE
4533 * Status.
4534 *
4535 * @implemented
4536 */
4537 NTSTATUS STDCALL
4538 MmCreateSection (OUT PSECTION_OBJECT * SectionObject,
4539 IN ACCESS_MASK DesiredAccess,
4540 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
4541 IN PLARGE_INTEGER MaximumSize,
4542 IN ULONG SectionPageProtection,
4543 IN ULONG AllocationAttributes,
4544 IN HANDLE FileHandle OPTIONAL,
4545 IN PFILE_OBJECT File OPTIONAL)
4546 {
4547 if (AllocationAttributes & SEC_IMAGE)
4548 {
4549 return(MmCreateImageSection(SectionObject,
4550 DesiredAccess,
4551 ObjectAttributes,
4552 MaximumSize,
4553 SectionPageProtection,
4554 AllocationAttributes,
4555 FileHandle));
4556 }
4557
4558 if (FileHandle != NULL)
4559 {
4560 return(MmCreateDataFileSection(SectionObject,
4561 DesiredAccess,
4562 ObjectAttributes,
4563 MaximumSize,
4564 SectionPageProtection,
4565 AllocationAttributes,
4566 FileHandle));
4567 }
4568
4569 return(MmCreatePageFileSection(SectionObject,
4570 DesiredAccess,
4571 ObjectAttributes,
4572 MaximumSize,
4573 SectionPageProtection,
4574 AllocationAttributes));
4575 }
4576
4577 /* EOF */