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