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