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