d0eec8fc8c7b44a296cf3caf5bad41892cc473f0
[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 /*
1282 * Delete the old entry.
1283 */
1284 MmDeleteVirtualMapping(AddressSpace->Process, Address, FALSE, NULL, NULL);
1285
1286 /*
1287 * Set the PTE to point to the new page
1288 */
1289 MmLockAddressSpace(AddressSpace);
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 MmInsertRmap(NewPage, AddressSpace->Process, PAddress);
1302 if (!NT_SUCCESS(Status))
1303 {
1304 DbgPrint("Unable to create virtual mapping\n");
1305 KEBUGCHECK(0);
1306 }
1307 if (Locked)
1308 {
1309 MmLockPage(NewPage);
1310 MmUnlockPage(OldPage);
1311 }
1312
1313 /*
1314 * Unshare the old page.
1315 */
1316 MmDeleteRmap(OldPage, 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 MmDeleteVirtualMapping(Process,
1336 Address,
1337 FALSE,
1338 &WasDirty,
1339 &Page);
1340 if (WasDirty)
1341 {
1342 PageOutContext->WasDirty = TRUE;
1343 }
1344 if (!PageOutContext->Private)
1345 {
1346 MmUnsharePageEntrySectionSegment(PageOutContext->Section,
1347 PageOutContext->Segment,
1348 PageOutContext->Offset,
1349 PageOutContext->WasDirty,
1350 TRUE);
1351 }
1352 else
1353 {
1354 MmReleasePageMemoryConsumer(MC_USER, Page);
1355 }
1356
1357 DPRINT("PhysicalAddress %I64x, Address %x\n", Page, Address);
1358 }
1359
1360 NTSTATUS
1361 NTAPI
1362 MmPageOutSectionView(PMADDRESS_SPACE AddressSpace,
1363 MEMORY_AREA* MemoryArea,
1364 PVOID Address,
1365 PMM_PAGEOP PageOp)
1366 {
1367 PFN_TYPE Page;
1368 MM_SECTION_PAGEOUT_CONTEXT Context;
1369 SWAPENTRY SwapEntry;
1370 ULONG Entry;
1371 ULONG FileOffset;
1372 NTSTATUS Status;
1373 PFILE_OBJECT FileObject;
1374 PBCB Bcb = NULL;
1375 BOOLEAN DirectMapped;
1376 BOOLEAN IsImageSection;
1377
1378 Address = (PVOID)PAGE_ROUND_DOWN(Address);
1379
1380 /*
1381 * Get the segment and section.
1382 */
1383 Context.Segment = MemoryArea->Data.SectionData.Segment;
1384 Context.Section = MemoryArea->Data.SectionData.Section;
1385
1386 Context.Offset = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress
1387 + MemoryArea->Data.SectionData.ViewOffset;
1388 FileOffset = Context.Offset + Context.Segment->FileOffset;
1389
1390 IsImageSection = Context.Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
1391
1392 FileObject = Context.Section->FileObject;
1393 DirectMapped = FALSE;
1394 if (FileObject != NULL &&
1395 !(Context.Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
1396 {
1397 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
1398
1399 /*
1400 * If the file system is letting us go directly to the cache and the
1401 * memory area was mapped at an offset in the file which is page aligned
1402 * then note this is a direct mapped page.
1403 */
1404 if ((FileOffset % PAGE_SIZE) == 0 &&
1405 (Context.Offset + PAGE_SIZE <= Context.Segment->RawLength || !IsImageSection))
1406 {
1407 DirectMapped = TRUE;
1408 }
1409 }
1410
1411
1412 /*
1413 * This should never happen since mappings of physical memory are never
1414 * placed in the rmap lists.
1415 */
1416 if (Context.Section->AllocationAttributes & SEC_PHYSICALMEMORY)
1417 {
1418 DPRINT1("Trying to page out from physical memory section address 0x%X "
1419 "process %d\n", Address,
1420 AddressSpace->Process ? AddressSpace->Process->UniqueProcessId : 0);
1421 KEBUGCHECK(0);
1422 }
1423
1424 /*
1425 * Get the section segment entry and the physical address.
1426 */
1427 Entry = MmGetPageEntrySectionSegment(Context.Segment, Context.Offset);
1428 if (!MmIsPagePresent(AddressSpace->Process, Address))
1429 {
1430 DPRINT1("Trying to page out not-present page at (%d,0x%.8X).\n",
1431 AddressSpace->Process ? AddressSpace->Process->UniqueProcessId : 0, Address);
1432 KEBUGCHECK(0);
1433 }
1434 Page = MmGetPfnForProcess(AddressSpace->Process, Address);
1435 SwapEntry = MmGetSavedSwapEntryPage(Page);
1436
1437 /*
1438 * Prepare the context structure for the rmap delete call.
1439 */
1440 Context.WasDirty = FALSE;
1441 if (Context.Segment->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
1442 IS_SWAP_FROM_SSE(Entry) ||
1443 PFN_FROM_SSE(Entry) != Page)
1444 {
1445 Context.Private = TRUE;
1446 }
1447 else
1448 {
1449 Context.Private = FALSE;
1450 }
1451
1452 /*
1453 * Take an additional reference to the page or the cache segment.
1454 */
1455 if (DirectMapped && !Context.Private)
1456 {
1457 if(!MiIsPageFromCache(MemoryArea, Context.Offset))
1458 {
1459 DPRINT1("Direct mapped non private page is not associated with the cache.\n");
1460 KEBUGCHECK(0);
1461 }
1462 }
1463 else
1464 {
1465 MmReferencePage(Page);
1466 }
1467
1468 MmDeleteAllRmaps(Page, (PVOID)&Context, MmPageOutDeleteMapping);
1469
1470 /*
1471 * If this wasn't a private page then we should have reduced the entry to
1472 * zero by deleting all the rmaps.
1473 */
1474 if (!Context.Private && MmGetPageEntrySectionSegment(Context.Segment, Context.Offset) != 0)
1475 {
1476 if (!(Context.Segment->Flags & MM_PAGEFILE_SEGMENT) &&
1477 !(Context.Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
1478 {
1479 KEBUGCHECK(0);
1480 }
1481 }
1482
1483 /*
1484 * If the page wasn't dirty then we can just free it as for a readonly page.
1485 * Since we unmapped all the mappings above we know it will not suddenly
1486 * become dirty.
1487 * If the page is from a pagefile section and has no swap entry,
1488 * we can't free the page at this point.
1489 */
1490 SwapEntry = MmGetSavedSwapEntryPage(Page);
1491 if (Context.Segment->Flags & MM_PAGEFILE_SEGMENT)
1492 {
1493 if (Context.Private)
1494 {
1495 DPRINT1("Found a %s private page (address %x) in a pagefile segment.\n",
1496 Context.WasDirty ? "dirty" : "clean", Address);
1497 KEBUGCHECK(0);
1498 }
1499 if (!Context.WasDirty && SwapEntry != 0)
1500 {
1501 MmSetSavedSwapEntryPage(Page, 0);
1502 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, MAKE_SWAP_SSE(SwapEntry));
1503 MmReleasePageMemoryConsumer(MC_USER, Page);
1504 PageOp->Status = STATUS_SUCCESS;
1505 MmspCompleteAndReleasePageOp(PageOp);
1506 return(STATUS_SUCCESS);
1507 }
1508 }
1509 else if (Context.Segment->Characteristics & IMAGE_SCN_MEM_SHARED)
1510 {
1511 if (Context.Private)
1512 {
1513 DPRINT1("Found a %s private page (address %x) in a shared section segment.\n",
1514 Context.WasDirty ? "dirty" : "clean", Address);
1515 KEBUGCHECK(0);
1516 }
1517 if (!Context.WasDirty || SwapEntry != 0)
1518 {
1519 MmSetSavedSwapEntryPage(Page, 0);
1520 if (SwapEntry != 0)
1521 {
1522 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, MAKE_SWAP_SSE(SwapEntry));
1523 }
1524 MmReleasePageMemoryConsumer(MC_USER, Page);
1525 PageOp->Status = STATUS_SUCCESS;
1526 MmspCompleteAndReleasePageOp(PageOp);
1527 return(STATUS_SUCCESS);
1528 }
1529 }
1530 else if (!Context.Private && DirectMapped)
1531 {
1532 if (SwapEntry != 0)
1533 {
1534 DPRINT1("Found a swapentry for a non private and direct mapped page (address %x)\n",
1535 Address);
1536 KEBUGCHECK(0);
1537 }
1538 Status = CcRosUnmapCacheSegment(Bcb, FileOffset, FALSE);
1539 if (!NT_SUCCESS(Status))
1540 {
1541 DPRINT1("CCRosUnmapCacheSegment failed, status = %x\n", Status);
1542 KEBUGCHECK(0);
1543 }
1544 PageOp->Status = STATUS_SUCCESS;
1545 MmspCompleteAndReleasePageOp(PageOp);
1546 return(STATUS_SUCCESS);
1547 }
1548 else if (!Context.WasDirty && !DirectMapped && !Context.Private)
1549 {
1550 if (SwapEntry != 0)
1551 {
1552 DPRINT1("Found a swap entry for a non dirty, non private and not direct mapped page (address %x)\n",
1553 Address);
1554 KEBUGCHECK(0);
1555 }
1556 MmReleasePageMemoryConsumer(MC_USER, Page);
1557 PageOp->Status = STATUS_SUCCESS;
1558 MmspCompleteAndReleasePageOp(PageOp);
1559 return(STATUS_SUCCESS);
1560 }
1561 else if (!Context.WasDirty && Context.Private && SwapEntry != 0)
1562 {
1563 MmSetSavedSwapEntryPage(Page, 0);
1564 Status = MmCreatePageFileMapping(AddressSpace->Process,
1565 Address,
1566 SwapEntry);
1567 if (!NT_SUCCESS(Status))
1568 {
1569 KEBUGCHECK(0);
1570 }
1571 MmReleasePageMemoryConsumer(MC_USER, Page);
1572 PageOp->Status = STATUS_SUCCESS;
1573 MmspCompleteAndReleasePageOp(PageOp);
1574 return(STATUS_SUCCESS);
1575 }
1576
1577 /*
1578 * If necessary, allocate an entry in the paging file for this page
1579 */
1580 if (SwapEntry == 0)
1581 {
1582 SwapEntry = MmAllocSwapPage();
1583 if (SwapEntry == 0)
1584 {
1585 MmShowOutOfSpaceMessagePagingFile();
1586
1587 /*
1588 * For private pages restore the old mappings.
1589 */
1590 if (Context.Private)
1591 {
1592 Status = MmCreateVirtualMapping(AddressSpace->Process,
1593 Address,
1594 MemoryArea->Attributes,
1595 &Page,
1596 1);
1597 MmSetDirtyPage(AddressSpace->Process, Address);
1598 MmInsertRmap(Page,
1599 AddressSpace->Process,
1600 Address);
1601 }
1602 else
1603 {
1604 /*
1605 * For non-private pages if the page wasn't direct mapped then
1606 * set it back into the section segment entry so we don't loose
1607 * our copy. Otherwise it will be handled by the cache manager.
1608 */
1609 Status = MmCreateVirtualMapping(AddressSpace->Process,
1610 Address,
1611 MemoryArea->Attributes,
1612 &Page,
1613 1);
1614 MmSetDirtyPage(AddressSpace->Process, Address);
1615 MmInsertRmap(Page,
1616 AddressSpace->Process,
1617 Address);
1618 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1619 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, Entry);
1620 }
1621 PageOp->Status = STATUS_UNSUCCESSFUL;
1622 MmspCompleteAndReleasePageOp(PageOp);
1623 return(STATUS_PAGEFILE_QUOTA);
1624 }
1625 }
1626
1627 /*
1628 * Write the page to the pagefile
1629 */
1630 Status = MmWriteToSwapPage(SwapEntry, Page);
1631 if (!NT_SUCCESS(Status))
1632 {
1633 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
1634 Status);
1635 /*
1636 * As above: undo our actions.
1637 * FIXME: Also free the swap page.
1638 */
1639 if (Context.Private)
1640 {
1641 Status = MmCreateVirtualMapping(AddressSpace->Process,
1642 Address,
1643 MemoryArea->Attributes,
1644 &Page,
1645 1);
1646 MmSetDirtyPage(AddressSpace->Process, Address);
1647 MmInsertRmap(Page,
1648 AddressSpace->Process,
1649 Address);
1650 }
1651 else
1652 {
1653 Status = MmCreateVirtualMapping(AddressSpace->Process,
1654 Address,
1655 MemoryArea->Attributes,
1656 &Page,
1657 1);
1658 MmSetDirtyPage(AddressSpace->Process, Address);
1659 MmInsertRmap(Page,
1660 AddressSpace->Process,
1661 Address);
1662 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1663 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, Entry);
1664 }
1665 PageOp->Status = STATUS_UNSUCCESSFUL;
1666 MmspCompleteAndReleasePageOp(PageOp);
1667 return(STATUS_UNSUCCESSFUL);
1668 }
1669
1670 /*
1671 * Otherwise we have succeeded.
1672 */
1673 DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT);
1674 MmSetSavedSwapEntryPage(Page, 0);
1675 if (Context.Segment->Flags & MM_PAGEFILE_SEGMENT ||
1676 Context.Segment->Characteristics & IMAGE_SCN_MEM_SHARED)
1677 {
1678 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, MAKE_SWAP_SSE(SwapEntry));
1679 }
1680 else
1681 {
1682 MmReleasePageMemoryConsumer(MC_USER, Page);
1683 }
1684
1685 if (Context.Private)
1686 {
1687 Status = MmCreatePageFileMapping(AddressSpace->Process,
1688 Address,
1689 SwapEntry);
1690 if (!NT_SUCCESS(Status))
1691 {
1692 KEBUGCHECK(0);
1693 }
1694 }
1695 else
1696 {
1697 Entry = MAKE_SWAP_SSE(SwapEntry);
1698 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, Entry);
1699 }
1700
1701 PageOp->Status = STATUS_SUCCESS;
1702 MmspCompleteAndReleasePageOp(PageOp);
1703 return(STATUS_SUCCESS);
1704 }
1705
1706 NTSTATUS
1707 NTAPI
1708 MmWritePageSectionView(PMADDRESS_SPACE AddressSpace,
1709 PMEMORY_AREA MemoryArea,
1710 PVOID Address,
1711 PMM_PAGEOP PageOp)
1712 {
1713 ULONG Offset;
1714 PSECTION_OBJECT Section;
1715 PMM_SECTION_SEGMENT Segment;
1716 PFN_TYPE Page;
1717 SWAPENTRY SwapEntry;
1718 ULONG Entry;
1719 BOOLEAN Private;
1720 NTSTATUS Status;
1721 PFILE_OBJECT FileObject;
1722 PBCB Bcb = NULL;
1723 BOOLEAN DirectMapped;
1724 BOOLEAN IsImageSection;
1725
1726 Address = (PVOID)PAGE_ROUND_DOWN(Address);
1727
1728 Offset = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress
1729 + MemoryArea->Data.SectionData.ViewOffset;
1730
1731 /*
1732 * Get the segment and section.
1733 */
1734 Segment = MemoryArea->Data.SectionData.Segment;
1735 Section = MemoryArea->Data.SectionData.Section;
1736 IsImageSection = Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
1737
1738 FileObject = Section->FileObject;
1739 DirectMapped = FALSE;
1740 if (FileObject != NULL &&
1741 !(Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
1742 {
1743 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
1744
1745 /*
1746 * If the file system is letting us go directly to the cache and the
1747 * memory area was mapped at an offset in the file which is page aligned
1748 * then note this is a direct mapped page.
1749 */
1750 if ((Offset + MemoryArea->Data.SectionData.ViewOffset % PAGE_SIZE) == 0 &&
1751 (Offset + PAGE_SIZE <= Segment->RawLength || !IsImageSection))
1752 {
1753 DirectMapped = TRUE;
1754 }
1755 }
1756
1757 /*
1758 * This should never happen since mappings of physical memory are never
1759 * placed in the rmap lists.
1760 */
1761 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
1762 {
1763 DPRINT1("Trying to write back page from physical memory mapped at %X "
1764 "process %d\n", Address,
1765 AddressSpace->Process ? AddressSpace->Process->UniqueProcessId : 0);
1766 KEBUGCHECK(0);
1767 }
1768
1769 /*
1770 * Get the section segment entry and the physical address.
1771 */
1772 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
1773 if (!MmIsPagePresent(AddressSpace->Process, Address))
1774 {
1775 DPRINT1("Trying to page out not-present page at (%d,0x%.8X).\n",
1776 AddressSpace->Process ? AddressSpace->Process->UniqueProcessId : 0, Address);
1777 KEBUGCHECK(0);
1778 }
1779 Page = MmGetPfnForProcess(AddressSpace->Process, Address);
1780 SwapEntry = MmGetSavedSwapEntryPage(Page);
1781
1782 /*
1783 * Check for a private (COWed) page.
1784 */
1785 if (Segment->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
1786 IS_SWAP_FROM_SSE(Entry) ||
1787 PFN_FROM_SSE(Entry) != Page)
1788 {
1789 Private = TRUE;
1790 }
1791 else
1792 {
1793 Private = FALSE;
1794 }
1795
1796 /*
1797 * Speculatively set all mappings of the page to clean.
1798 */
1799 MmSetCleanAllRmaps(Page);
1800
1801 /*
1802 * If this page was direct mapped from the cache then the cache manager
1803 * will take care of writing it back to disk.
1804 */
1805 if (DirectMapped && !Private)
1806 {
1807 ASSERT(SwapEntry == 0);
1808 CcRosMarkDirtyCacheSegment(Bcb, Offset + Segment->FileOffset);
1809 PageOp->Status = STATUS_SUCCESS;
1810 MmspCompleteAndReleasePageOp(PageOp);
1811 return(STATUS_SUCCESS);
1812 }
1813
1814 /*
1815 * If necessary, allocate an entry in the paging file for this page
1816 */
1817 if (SwapEntry == 0)
1818 {
1819 SwapEntry = MmAllocSwapPage();
1820 if (SwapEntry == 0)
1821 {
1822 MmSetDirtyAllRmaps(Page);
1823 PageOp->Status = STATUS_UNSUCCESSFUL;
1824 MmspCompleteAndReleasePageOp(PageOp);
1825 return(STATUS_PAGEFILE_QUOTA);
1826 }
1827 MmSetSavedSwapEntryPage(Page, SwapEntry);
1828 }
1829
1830 /*
1831 * Write the page to the pagefile
1832 */
1833 Status = MmWriteToSwapPage(SwapEntry, Page);
1834 if (!NT_SUCCESS(Status))
1835 {
1836 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
1837 Status);
1838 MmSetDirtyAllRmaps(Page);
1839 PageOp->Status = STATUS_UNSUCCESSFUL;
1840 MmspCompleteAndReleasePageOp(PageOp);
1841 return(STATUS_UNSUCCESSFUL);
1842 }
1843
1844 /*
1845 * Otherwise we have succeeded.
1846 */
1847 DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT);
1848 PageOp->Status = STATUS_SUCCESS;
1849 MmspCompleteAndReleasePageOp(PageOp);
1850 return(STATUS_SUCCESS);
1851 }
1852
1853 VOID STATIC
1854 MmAlterViewAttributes(PMADDRESS_SPACE AddressSpace,
1855 PVOID BaseAddress,
1856 ULONG RegionSize,
1857 ULONG OldType,
1858 ULONG OldProtect,
1859 ULONG NewType,
1860 ULONG NewProtect)
1861 {
1862 PMEMORY_AREA MemoryArea;
1863 PMM_SECTION_SEGMENT Segment;
1864 BOOL DoCOW = FALSE;
1865 ULONG i;
1866
1867 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
1868 Segment = MemoryArea->Data.SectionData.Segment;
1869
1870 if ((Segment->WriteCopy || MemoryArea->Data.SectionData.WriteCopyView) &&
1871 (NewProtect == PAGE_READWRITE || NewProtect == PAGE_EXECUTE_READWRITE))
1872 {
1873 DoCOW = TRUE;
1874 }
1875
1876 if (OldProtect != NewProtect)
1877 {
1878 for (i = 0; i < PAGE_ROUND_UP(RegionSize) / PAGE_SIZE; i++)
1879 {
1880 PVOID Address = (char*)BaseAddress + (i * PAGE_SIZE);
1881 ULONG Protect = NewProtect;
1882
1883 /*
1884 * If we doing COW for this segment then check if the page is
1885 * already private.
1886 */
1887 if (DoCOW && MmIsPagePresent(AddressSpace->Process, Address))
1888 {
1889 ULONG Offset;
1890 ULONG Entry;
1891 PFN_TYPE Page;
1892
1893 Offset = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress
1894 + MemoryArea->Data.SectionData.ViewOffset;
1895 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
1896 Page = MmGetPfnForProcess(AddressSpace->Process, Address);
1897
1898 Protect = PAGE_READONLY;
1899 if (Segment->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
1900 IS_SWAP_FROM_SSE(Entry) ||
1901 PFN_FROM_SSE(Entry) != Page)
1902 {
1903 Protect = NewProtect;
1904 }
1905 }
1906
1907 if (MmIsPagePresent(AddressSpace->Process, Address))
1908 {
1909 MmSetPageProtect(AddressSpace->Process, Address,
1910 Protect);
1911 }
1912 }
1913 }
1914 }
1915
1916 NTSTATUS
1917 NTAPI
1918 MmProtectSectionView(PMADDRESS_SPACE AddressSpace,
1919 PMEMORY_AREA MemoryArea,
1920 PVOID BaseAddress,
1921 ULONG Length,
1922 ULONG Protect,
1923 PULONG OldProtect)
1924 {
1925 PMM_REGION Region;
1926 NTSTATUS Status;
1927 ULONG_PTR MaxLength;
1928
1929 MaxLength = (ULONG_PTR)MemoryArea->EndingAddress - (ULONG_PTR)BaseAddress;
1930 if (Length > MaxLength)
1931 Length = MaxLength;
1932
1933 Region = MmFindRegion(MemoryArea->StartingAddress,
1934 &MemoryArea->Data.SectionData.RegionListHead,
1935 BaseAddress, NULL);
1936 *OldProtect = Region->Protect;
1937 Status = MmAlterRegion(AddressSpace, MemoryArea->StartingAddress,
1938 &MemoryArea->Data.SectionData.RegionListHead,
1939 BaseAddress, Length, Region->Type, Protect,
1940 MmAlterViewAttributes);
1941
1942 return(Status);
1943 }
1944
1945 NTSTATUS STDCALL
1946 MmQuerySectionView(PMEMORY_AREA MemoryArea,
1947 PVOID Address,
1948 PMEMORY_BASIC_INFORMATION Info,
1949 PULONG ResultLength)
1950 {
1951 PMM_REGION Region;
1952 PVOID RegionBaseAddress;
1953 PSECTION_OBJECT Section;
1954 PMM_SECTION_SEGMENT Segment;
1955
1956 Region = MmFindRegion((PVOID)MemoryArea->StartingAddress,
1957 &MemoryArea->Data.SectionData.RegionListHead,
1958 Address, &RegionBaseAddress);
1959 if (Region == NULL)
1960 {
1961 return STATUS_UNSUCCESSFUL;
1962 }
1963
1964 Section = MemoryArea->Data.SectionData.Section;
1965 if (Section->AllocationAttributes & SEC_IMAGE)
1966 {
1967 Segment = MemoryArea->Data.SectionData.Segment;
1968 Info->AllocationBase = (PBYTE)MemoryArea->StartingAddress - Segment->VirtualAddress;
1969 Info->Type = MEM_IMAGE;
1970 }
1971 else
1972 {
1973 Info->AllocationBase = MemoryArea->StartingAddress;
1974 Info->Type = MEM_MAPPED;
1975 }
1976 Info->BaseAddress = RegionBaseAddress;
1977 Info->AllocationProtect = MemoryArea->Attributes;
1978 Info->RegionSize = PAGE_ROUND_UP((ULONG_PTR)MemoryArea->EndingAddress -
1979 (ULONG_PTR)MemoryArea->StartingAddress);
1980 Info->State = MEM_COMMIT;
1981 Info->Protect = Region->Protect;
1982
1983 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
1984 return(STATUS_SUCCESS);
1985 }
1986
1987 VOID
1988 NTAPI
1989 MmpFreePageFileSegment(PMM_SECTION_SEGMENT Segment)
1990 {
1991 ULONG Length;
1992 ULONG Offset;
1993 ULONG Entry;
1994 ULONG SavedSwapEntry;
1995 PFN_TYPE Page;
1996
1997 Page = 0;
1998
1999 Length = PAGE_ROUND_UP(Segment->Length);
2000 for (Offset = 0; Offset < Length; Offset += PAGE_SIZE)
2001 {
2002 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
2003 if (Entry)
2004 {
2005 if (IS_SWAP_FROM_SSE(Entry))
2006 {
2007 MmFreeSwapPage(SWAPENTRY_FROM_SSE(Entry));
2008 }
2009 else
2010 {
2011 Page = PFN_FROM_SSE(Entry);
2012 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
2013 if (SavedSwapEntry != 0)
2014 {
2015 MmSetSavedSwapEntryPage(Page, 0);
2016 MmFreeSwapPage(SavedSwapEntry);
2017 }
2018 MmReleasePageMemoryConsumer(MC_USER, Page);
2019 }
2020 MmSetPageEntrySectionSegment(Segment, Offset, 0);
2021 }
2022 }
2023 }
2024
2025 VOID STDCALL
2026 MmpDeleteSection(PVOID ObjectBody)
2027 {
2028 PSECTION_OBJECT Section = (PSECTION_OBJECT)ObjectBody;
2029
2030 DPRINT("MmpDeleteSection(ObjectBody %x)\n", ObjectBody);
2031 if (Section->AllocationAttributes & SEC_IMAGE)
2032 {
2033 ULONG i;
2034 ULONG NrSegments;
2035 ULONG RefCount;
2036 PMM_SECTION_SEGMENT SectionSegments;
2037
2038 /*
2039 * NOTE: Section->ImageSection can be NULL for short time
2040 * during the section creating. If we fail for some reason
2041 * until the image section is properly initialized we shouldn't
2042 * process further here.
2043 */
2044 if (Section->ImageSection == NULL)
2045 return;
2046
2047 SectionSegments = Section->ImageSection->Segments;
2048 NrSegments = Section->ImageSection->NrSegments;
2049
2050 for (i = 0; i < NrSegments; i++)
2051 {
2052 if (SectionSegments[i].Characteristics & IMAGE_SCN_MEM_SHARED)
2053 {
2054 MmLockSectionSegment(&SectionSegments[i]);
2055 }
2056 RefCount = InterlockedDecrementUL(&SectionSegments[i].ReferenceCount);
2057 if (SectionSegments[i].Characteristics & IMAGE_SCN_MEM_SHARED)
2058 {
2059 if (RefCount == 0)
2060 {
2061 MmpFreePageFileSegment(&SectionSegments[i]);
2062 }
2063 MmUnlockSectionSegment(&SectionSegments[i]);
2064 }
2065 }
2066 }
2067 else
2068 {
2069 /*
2070 * NOTE: Section->Segment can be NULL for short time
2071 * during the section creating.
2072 */
2073 if (Section->Segment == NULL)
2074 return;
2075
2076 if (Section->Segment->Flags & MM_PAGEFILE_SEGMENT)
2077 {
2078 MmpFreePageFileSegment(Section->Segment);
2079 MmFreePageTablesSectionSegment(Section->Segment);
2080 ExFreePool(Section->Segment);
2081 Section->Segment = NULL;
2082 }
2083 else
2084 {
2085 InterlockedDecrementUL(&Section->Segment->ReferenceCount);
2086 }
2087 }
2088 if (Section->FileObject != NULL)
2089 {
2090 CcRosDereferenceCache(Section->FileObject);
2091 ObDereferenceObject(Section->FileObject);
2092 Section->FileObject = NULL;
2093 }
2094 }
2095
2096 VOID STDCALL
2097 MmpCloseSection(PVOID ObjectBody,
2098 ULONG HandleCount)
2099 {
2100 DPRINT("MmpCloseSection(OB %x, HC %d) RC %d\n",
2101 ObjectBody, HandleCount, ObGetObjectPointerCount(ObjectBody));
2102 }
2103
2104 NTSTATUS
2105 INIT_FUNCTION
2106 NTAPI
2107 MmCreatePhysicalMemorySection(VOID)
2108 {
2109 PSECTION_OBJECT PhysSection;
2110 NTSTATUS Status;
2111 OBJECT_ATTRIBUTES Obj;
2112 UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory");
2113 LARGE_INTEGER SectionSize;
2114
2115 /*
2116 * Create the section mapping physical memory
2117 */
2118 SectionSize.QuadPart = 0xFFFFFFFF;
2119 InitializeObjectAttributes(&Obj,
2120 &Name,
2121 OBJ_PERMANENT,
2122 NULL,
2123 NULL);
2124 Status = MmCreateSection(&PhysSection,
2125 SECTION_ALL_ACCESS,
2126 &Obj,
2127 &SectionSize,
2128 PAGE_EXECUTE_READWRITE,
2129 0,
2130 NULL,
2131 NULL);
2132 if (!NT_SUCCESS(Status))
2133 {
2134 DbgPrint("Failed to create PhysicalMemory section\n");
2135 KEBUGCHECK(0);
2136 }
2137 Status = ObInsertObject(PhysSection,
2138 NULL,
2139 SECTION_ALL_ACCESS,
2140 0,
2141 NULL,
2142 NULL);
2143 if (!NT_SUCCESS(Status))
2144 {
2145 ObDereferenceObject(PhysSection);
2146 }
2147 PhysSection->AllocationAttributes |= SEC_PHYSICALMEMORY;
2148 PhysSection->Segment->Flags &= ~MM_PAGEFILE_SEGMENT;
2149
2150 return(STATUS_SUCCESS);
2151 }
2152
2153 NTSTATUS
2154 INIT_FUNCTION
2155 NTAPI
2156 MmInitSectionImplementation(VOID)
2157 {
2158 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
2159 UNICODE_STRING Name;
2160
2161 DPRINT("Creating Section Object Type\n");
2162
2163 /* Initialize the Section object type */
2164 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
2165 RtlInitUnicodeString(&Name, L"Section");
2166 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
2167 ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(SECTION_OBJECT);
2168 ObjectTypeInitializer.PoolType = PagedPool;
2169 ObjectTypeInitializer.UseDefaultObject = TRUE;
2170 ObjectTypeInitializer.GenericMapping = MmpSectionMapping;
2171 ObjectTypeInitializer.DeleteProcedure = MmpDeleteSection;
2172 ObjectTypeInitializer.CloseProcedure = MmpCloseSection;
2173 ObpCreateTypeObject(&ObjectTypeInitializer, &Name, &MmSectionObjectType);
2174
2175 return(STATUS_SUCCESS);
2176 }
2177
2178 NTSTATUS
2179 NTAPI
2180 MmCreatePageFileSection(PSECTION_OBJECT *SectionObject,
2181 ACCESS_MASK DesiredAccess,
2182 POBJECT_ATTRIBUTES ObjectAttributes,
2183 PLARGE_INTEGER UMaximumSize,
2184 ULONG SectionPageProtection,
2185 ULONG AllocationAttributes)
2186 /*
2187 * Create a section which is backed by the pagefile
2188 */
2189 {
2190 LARGE_INTEGER MaximumSize;
2191 PSECTION_OBJECT Section;
2192 PMM_SECTION_SEGMENT Segment;
2193 NTSTATUS Status;
2194
2195 if (UMaximumSize == NULL)
2196 {
2197 return(STATUS_UNSUCCESSFUL);
2198 }
2199 MaximumSize = *UMaximumSize;
2200
2201 /*
2202 * Create the section
2203 */
2204 Status = ObCreateObject(ExGetPreviousMode(),
2205 MmSectionObjectType,
2206 ObjectAttributes,
2207 ExGetPreviousMode(),
2208 NULL,
2209 sizeof(SECTION_OBJECT),
2210 0,
2211 0,
2212 (PVOID*)(PVOID)&Section);
2213 if (!NT_SUCCESS(Status))
2214 {
2215 return(Status);
2216 }
2217
2218 /*
2219 * Initialize it
2220 */
2221 Section->SectionPageProtection = SectionPageProtection;
2222 Section->AllocationAttributes = AllocationAttributes;
2223 Section->Segment = NULL;
2224 Section->FileObject = NULL;
2225 Section->MaximumSize = MaximumSize;
2226 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2227 TAG_MM_SECTION_SEGMENT);
2228 if (Segment == NULL)
2229 {
2230 ObDereferenceObject(Section);
2231 return(STATUS_NO_MEMORY);
2232 }
2233 Section->Segment = Segment;
2234 Segment->ReferenceCount = 1;
2235 ExInitializeFastMutex(&Segment->Lock);
2236 Segment->FileOffset = 0;
2237 Segment->Protection = SectionPageProtection;
2238 Segment->RawLength = MaximumSize.u.LowPart;
2239 Segment->Length = PAGE_ROUND_UP(MaximumSize.u.LowPart);
2240 Segment->Flags = MM_PAGEFILE_SEGMENT;
2241 Segment->WriteCopy = FALSE;
2242 RtlZeroMemory(&Segment->PageDirectory, sizeof(SECTION_PAGE_DIRECTORY));
2243 Segment->VirtualAddress = 0;
2244 Segment->Characteristics = 0;
2245 *SectionObject = Section;
2246 return(STATUS_SUCCESS);
2247 }
2248
2249
2250 NTSTATUS
2251 NTAPI
2252 MmCreateDataFileSection(PSECTION_OBJECT *SectionObject,
2253 ACCESS_MASK DesiredAccess,
2254 POBJECT_ATTRIBUTES ObjectAttributes,
2255 PLARGE_INTEGER UMaximumSize,
2256 ULONG SectionPageProtection,
2257 ULONG AllocationAttributes,
2258 HANDLE FileHandle)
2259 /*
2260 * Create a section backed by a data file
2261 */
2262 {
2263 PSECTION_OBJECT Section;
2264 NTSTATUS Status;
2265 LARGE_INTEGER MaximumSize;
2266 PFILE_OBJECT FileObject;
2267 PMM_SECTION_SEGMENT Segment;
2268 ULONG FileAccess;
2269 IO_STATUS_BLOCK Iosb;
2270 LARGE_INTEGER Offset;
2271 CHAR Buffer;
2272 FILE_STANDARD_INFORMATION FileInfo;
2273
2274 /*
2275 * Create the section
2276 */
2277 Status = ObCreateObject(ExGetPreviousMode(),
2278 MmSectionObjectType,
2279 ObjectAttributes,
2280 ExGetPreviousMode(),
2281 NULL,
2282 sizeof(SECTION_OBJECT),
2283 0,
2284 0,
2285 (PVOID*)(PVOID)&Section);
2286 if (!NT_SUCCESS(Status))
2287 {
2288 return(Status);
2289 }
2290 /*
2291 * Initialize it
2292 */
2293 Section->SectionPageProtection = SectionPageProtection;
2294 Section->AllocationAttributes = AllocationAttributes;
2295 Section->Segment = NULL;
2296
2297 /*
2298 * Check file access required
2299 */
2300 if (SectionPageProtection & PAGE_READWRITE ||
2301 SectionPageProtection & PAGE_EXECUTE_READWRITE)
2302 {
2303 FileAccess = FILE_READ_DATA | FILE_WRITE_DATA;
2304 }
2305 else
2306 {
2307 FileAccess = FILE_READ_DATA;
2308 }
2309
2310 /*
2311 * Reference the file handle
2312 */
2313 Status = ObReferenceObjectByHandle(FileHandle,
2314 FileAccess,
2315 IoFileObjectType,
2316 UserMode,
2317 (PVOID*)(PVOID)&FileObject,
2318 NULL);
2319 if (!NT_SUCCESS(Status))
2320 {
2321 ObDereferenceObject(Section);
2322 return(Status);
2323 }
2324
2325 /*
2326 * FIXME: This is propably not entirely correct. We can't look into
2327 * the standard FCB header because it might not be initialized yet
2328 * (as in case of the EXT2FS driver by Manoj Paul Joseph where the
2329 * standard file information is filled on first request).
2330 */
2331 Status = IoQueryFileInformation(FileObject,
2332 FileStandardInformation,
2333 sizeof(FILE_STANDARD_INFORMATION),
2334 &FileInfo,
2335 &Iosb.Information);
2336 if (!NT_SUCCESS(Status))
2337 {
2338 ObDereferenceObject(Section);
2339 ObDereferenceObject(FileObject);
2340 return Status;
2341 }
2342
2343 /*
2344 * FIXME: Revise this once a locking order for file size changes is
2345 * decided
2346 */
2347 if (UMaximumSize != NULL)
2348 {
2349 MaximumSize = *UMaximumSize;
2350 }
2351 else
2352 {
2353 MaximumSize = FileInfo.EndOfFile;
2354 /* Mapping zero-sized files isn't allowed. */
2355 if (MaximumSize.QuadPart == 0)
2356 {
2357 ObDereferenceObject(Section);
2358 ObDereferenceObject(FileObject);
2359 return STATUS_FILE_INVALID;
2360 }
2361 }
2362
2363 if (MaximumSize.QuadPart > FileInfo.EndOfFile.QuadPart)
2364 {
2365 Status = IoSetInformation(FileObject,
2366 FileAllocationInformation,
2367 sizeof(LARGE_INTEGER),
2368 &MaximumSize);
2369 if (!NT_SUCCESS(Status))
2370 {
2371 ObDereferenceObject(Section);
2372 ObDereferenceObject(FileObject);
2373 return(STATUS_SECTION_NOT_EXTENDED);
2374 }
2375 }
2376
2377 if (FileObject->SectionObjectPointer == NULL ||
2378 FileObject->SectionObjectPointer->SharedCacheMap == NULL)
2379 {
2380 /*
2381 * Read a bit so caching is initiated for the file object.
2382 * This is only needed because MiReadPage currently cannot
2383 * handle non-cached streams.
2384 */
2385 Offset.QuadPart = 0;
2386 Status = ZwReadFile(FileHandle,
2387 NULL,
2388 NULL,
2389 NULL,
2390 &Iosb,
2391 &Buffer,
2392 sizeof (Buffer),
2393 &Offset,
2394 0);
2395 if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE))
2396 {
2397 ObDereferenceObject(Section);
2398 ObDereferenceObject(FileObject);
2399 return(Status);
2400 }
2401 if (FileObject->SectionObjectPointer == NULL ||
2402 FileObject->SectionObjectPointer->SharedCacheMap == NULL)
2403 {
2404 /* FIXME: handle this situation */
2405 ObDereferenceObject(Section);
2406 ObDereferenceObject(FileObject);
2407 return STATUS_INVALID_PARAMETER;
2408 }
2409 }
2410
2411 /*
2412 * Lock the file
2413 */
2414 Status = MmspWaitForFileLock(FileObject);
2415 if (Status != STATUS_SUCCESS)
2416 {
2417 ObDereferenceObject(Section);
2418 ObDereferenceObject(FileObject);
2419 return(Status);
2420 }
2421
2422 /*
2423 * If this file hasn't been mapped as a data file before then allocate a
2424 * section segment to describe the data file mapping
2425 */
2426 if (FileObject->SectionObjectPointer->DataSectionObject == NULL)
2427 {
2428 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2429 TAG_MM_SECTION_SEGMENT);
2430 if (Segment == NULL)
2431 {
2432 KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2433 ObDereferenceObject(Section);
2434 ObDereferenceObject(FileObject);
2435 return(STATUS_NO_MEMORY);
2436 }
2437 Section->Segment = Segment;
2438 Segment->ReferenceCount = 1;
2439 ExInitializeFastMutex(&Segment->Lock);
2440 /*
2441 * Set the lock before assigning the segment to the file object
2442 */
2443 ExAcquireFastMutex(&Segment->Lock);
2444 FileObject->SectionObjectPointer->DataSectionObject = (PVOID)Segment;
2445
2446 Segment->FileOffset = 0;
2447 Segment->Protection = SectionPageProtection;
2448 Segment->Flags = MM_DATAFILE_SEGMENT;
2449 Segment->Characteristics = 0;
2450 Segment->WriteCopy = FALSE;
2451 if (AllocationAttributes & SEC_RESERVE)
2452 {
2453 Segment->Length = Segment->RawLength = 0;
2454 }
2455 else
2456 {
2457 Segment->RawLength = MaximumSize.u.LowPart;
2458 Segment->Length = PAGE_ROUND_UP(Segment->RawLength);
2459 }
2460 Segment->VirtualAddress = 0;
2461 RtlZeroMemory(&Segment->PageDirectory, sizeof(SECTION_PAGE_DIRECTORY));
2462 }
2463 else
2464 {
2465 /*
2466 * If the file is already mapped as a data file then we may need
2467 * to extend it
2468 */
2469 Segment =
2470 (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->
2471 DataSectionObject;
2472 Section->Segment = Segment;
2473 InterlockedIncrementUL(&Segment->ReferenceCount);
2474 MmLockSectionSegment(Segment);
2475
2476 if (MaximumSize.u.LowPart > Segment->RawLength &&
2477 !(AllocationAttributes & SEC_RESERVE))
2478 {
2479 Segment->RawLength = MaximumSize.u.LowPart;
2480 Segment->Length = PAGE_ROUND_UP(Segment->RawLength);
2481 }
2482 }
2483 MmUnlockSectionSegment(Segment);
2484 Section->FileObject = FileObject;
2485 Section->MaximumSize = MaximumSize;
2486 CcRosReferenceCache(FileObject);
2487 KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2488 *SectionObject = Section;
2489 return(STATUS_SUCCESS);
2490 }
2491
2492 /*
2493 TODO: not that great (declaring loaders statically, having to declare all of
2494 them, having to keep them extern, etc.), will fix in the future
2495 */
2496 extern NTSTATUS NTAPI PeFmtCreateSection
2497 (
2498 IN CONST VOID * FileHeader,
2499 IN SIZE_T FileHeaderSize,
2500 IN PVOID File,
2501 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2502 OUT PULONG Flags,
2503 IN PEXEFMT_CB_READ_FILE ReadFileCb,
2504 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2505 );
2506
2507 extern NTSTATUS NTAPI ElfFmtCreateSection
2508 (
2509 IN CONST VOID * FileHeader,
2510 IN SIZE_T FileHeaderSize,
2511 IN PVOID File,
2512 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2513 OUT PULONG Flags,
2514 IN PEXEFMT_CB_READ_FILE ReadFileCb,
2515 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2516 );
2517
2518 /* TODO: this is a standard DDK/PSDK macro */
2519 #ifndef RTL_NUMBER_OF
2520 #define RTL_NUMBER_OF(ARR_) (sizeof(ARR_) / sizeof((ARR_)[0]))
2521 #endif
2522
2523 static PEXEFMT_LOADER ExeFmtpLoaders[] =
2524 {
2525 PeFmtCreateSection,
2526 ElfFmtCreateSection
2527 };
2528
2529 static
2530 PMM_SECTION_SEGMENT
2531 NTAPI
2532 ExeFmtpAllocateSegments(IN ULONG NrSegments)
2533 {
2534 SIZE_T SizeOfSegments;
2535 PMM_SECTION_SEGMENT Segments;
2536
2537 /* TODO: check for integer overflow */
2538 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * NrSegments;
2539
2540 Segments = ExAllocatePoolWithTag(NonPagedPool,
2541 SizeOfSegments,
2542 TAG_MM_SECTION_SEGMENT);
2543
2544 if(Segments)
2545 RtlZeroMemory(Segments, SizeOfSegments);
2546
2547 return Segments;
2548 }
2549
2550 static
2551 NTSTATUS
2552 NTAPI
2553 ExeFmtpReadFile(IN PVOID File,
2554 IN PLARGE_INTEGER Offset,
2555 IN ULONG Length,
2556 OUT PVOID * Data,
2557 OUT PVOID * AllocBase,
2558 OUT PULONG ReadSize)
2559 {
2560 NTSTATUS Status;
2561 LARGE_INTEGER FileOffset;
2562 ULONG AdjustOffset;
2563 ULONG OffsetAdjustment;
2564 ULONG BufferSize;
2565 ULONG UsedSize;
2566 PVOID Buffer;
2567
2568 ASSERT_IRQL_LESS(DISPATCH_LEVEL);
2569
2570 if(Length == 0)
2571 {
2572 KEBUGCHECK(STATUS_INVALID_PARAMETER_4);
2573 }
2574
2575 FileOffset = *Offset;
2576
2577 /* Negative/special offset: it cannot be used in this context */
2578 if(FileOffset.u.HighPart < 0)
2579 {
2580 KEBUGCHECK(STATUS_INVALID_PARAMETER_5);
2581 }
2582
2583 AdjustOffset = PAGE_ROUND_DOWN(FileOffset.u.LowPart);
2584 OffsetAdjustment = FileOffset.u.LowPart - AdjustOffset;
2585 FileOffset.u.LowPart = AdjustOffset;
2586
2587 BufferSize = Length + OffsetAdjustment;
2588 BufferSize = PAGE_ROUND_UP(BufferSize);
2589
2590 /*
2591 * It's ok to use paged pool, because this is a temporary buffer only used in
2592 * the loading of executables. The assumption is that MmCreateSection is
2593 * always called at low IRQLs and that these buffers don't survive a brief
2594 * initialization phase
2595 */
2596 Buffer = ExAllocatePoolWithTag(PagedPool,
2597 BufferSize,
2598 TAG('M', 'm', 'X', 'r'));
2599
2600 UsedSize = 0;
2601
2602 #if 0
2603 Status = MmspPageRead(File,
2604 Buffer,
2605 BufferSize,
2606 &FileOffset,
2607 &UsedSize);
2608 #else
2609 /*
2610 * FIXME: if we don't use ZwReadFile, caching is not enabled for the file and
2611 * nothing will work. But using ZwReadFile is wrong, and using its side effects
2612 * to initialize internal state is even worse. Our cache manager is in need of
2613 * professional help
2614 */
2615 {
2616 IO_STATUS_BLOCK Iosb;
2617
2618 Status = ZwReadFile(File,
2619 NULL,
2620 NULL,
2621 NULL,
2622 &Iosb,
2623 Buffer,
2624 BufferSize,
2625 &FileOffset,
2626 NULL);
2627
2628 if(NT_SUCCESS(Status))
2629 {
2630 UsedSize = Iosb.Information;
2631 }
2632 }
2633 #endif
2634
2635 if(NT_SUCCESS(Status) && UsedSize < OffsetAdjustment)
2636 {
2637 Status = STATUS_IN_PAGE_ERROR;
2638 ASSERT(!NT_SUCCESS(Status));
2639 }
2640
2641 if(NT_SUCCESS(Status))
2642 {
2643 *Data = (PVOID)((ULONG_PTR)Buffer + OffsetAdjustment);
2644 *AllocBase = Buffer;
2645 *ReadSize = UsedSize - OffsetAdjustment;
2646 }
2647 else
2648 {
2649 ExFreePool(Buffer);
2650 }
2651
2652 return Status;
2653 }
2654
2655 #ifdef NASSERT
2656 # define MmspAssertSegmentsSorted(OBJ_) ((void)0)
2657 # define MmspAssertSegmentsNoOverlap(OBJ_) ((void)0)
2658 # define MmspAssertSegmentsPageAligned(OBJ_) ((void)0)
2659 #else
2660 static
2661 VOID
2662 NTAPI
2663 MmspAssertSegmentsSorted(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2664 {
2665 ULONG i;
2666
2667 for( i = 1; i < ImageSectionObject->NrSegments; ++ i )
2668 {
2669 ASSERT(ImageSectionObject->Segments[i].VirtualAddress >=
2670 ImageSectionObject->Segments[i - 1].VirtualAddress);
2671 }
2672 }
2673
2674 static
2675 VOID
2676 NTAPI
2677 MmspAssertSegmentsNoOverlap(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2678 {
2679 ULONG i;
2680
2681 MmspAssertSegmentsSorted(ImageSectionObject);
2682
2683 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2684 {
2685 ASSERT(ImageSectionObject->Segments[i].Length > 0);
2686
2687 if(i > 0)
2688 {
2689 ASSERT(ImageSectionObject->Segments[i].VirtualAddress >=
2690 (ImageSectionObject->Segments[i - 1].VirtualAddress +
2691 ImageSectionObject->Segments[i - 1].Length));
2692 }
2693 }
2694 }
2695
2696 static
2697 VOID
2698 NTAPI
2699 MmspAssertSegmentsPageAligned(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2700 {
2701 ULONG i;
2702
2703 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2704 {
2705 ASSERT((ImageSectionObject->Segments[i].VirtualAddress % PAGE_SIZE) == 0);
2706 ASSERT((ImageSectionObject->Segments[i].Length % PAGE_SIZE) == 0);
2707 }
2708 }
2709 #endif
2710
2711 static
2712 int
2713 __cdecl
2714 MmspCompareSegments(const void * x,
2715 const void * y)
2716 {
2717 PMM_SECTION_SEGMENT Segment1 = (PMM_SECTION_SEGMENT)x;
2718 PMM_SECTION_SEGMENT Segment2 = (PMM_SECTION_SEGMENT)y;
2719
2720 return
2721 (Segment1->VirtualAddress - Segment2->VirtualAddress) >>
2722 ((sizeof(ULONG_PTR) - sizeof(int)) * 8);
2723 }
2724
2725 /*
2726 * Ensures an image section's segments are sorted in memory
2727 */
2728 static
2729 VOID
2730 NTAPI
2731 MmspSortSegments(IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2732 IN ULONG Flags)
2733 {
2734 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_SORTED)
2735 {
2736 MmspAssertSegmentsSorted(ImageSectionObject);
2737 }
2738 else
2739 {
2740 qsort(ImageSectionObject->Segments,
2741 ImageSectionObject->NrSegments,
2742 sizeof(ImageSectionObject->Segments[0]),
2743 MmspCompareSegments);
2744 }
2745 }
2746
2747
2748 /*
2749 * Ensures an image section's segments don't overlap in memory and don't have
2750 * gaps and don't have a null size. We let them map to overlapping file regions,
2751 * though - that's not necessarily an error
2752 */
2753 static
2754 BOOLEAN
2755 NTAPI
2756 MmspCheckSegmentBounds
2757 (
2758 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2759 IN ULONG Flags
2760 )
2761 {
2762 ULONG i;
2763
2764 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_NO_OVERLAP)
2765 {
2766 MmspAssertSegmentsNoOverlap(ImageSectionObject);
2767 return TRUE;
2768 }
2769
2770 ASSERT(ImageSectionObject->NrSegments >= 1);
2771
2772 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2773 {
2774 if(ImageSectionObject->Segments[i].Length == 0)
2775 {
2776 return FALSE;
2777 }
2778
2779 if(i > 0)
2780 {
2781 /*
2782 * TODO: relax the limitation on gaps. For example, gaps smaller than a
2783 * page could be OK (Windows seems to be OK with them), and larger gaps
2784 * could lead to image sections spanning several discontiguous regions
2785 * (NtMapViewOfSection could then refuse to map them, and they could
2786 * e.g. only be allowed as parameters to NtCreateProcess, like on UNIX)
2787 */
2788 if ((ImageSectionObject->Segments[i - 1].VirtualAddress +
2789 ImageSectionObject->Segments[i - 1].Length) !=
2790 ImageSectionObject->Segments[i].VirtualAddress)
2791 {
2792 return FALSE;
2793 }
2794 }
2795 }
2796
2797 return TRUE;
2798 }
2799
2800 /*
2801 * Merges and pads an image section's segments until they all are page-aligned
2802 * and have a size that is a multiple of the page size
2803 */
2804 static
2805 BOOLEAN
2806 NTAPI
2807 MmspPageAlignSegments
2808 (
2809 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2810 IN ULONG Flags
2811 )
2812 {
2813 ULONG i;
2814 ULONG LastSegment;
2815 BOOLEAN Initialized;
2816
2817 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED)
2818 {
2819 MmspAssertSegmentsPageAligned(ImageSectionObject);
2820 return TRUE;
2821 }
2822
2823 Initialized = FALSE;
2824 LastSegment = 0;
2825
2826 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2827 {
2828 PMM_SECTION_SEGMENT EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2829
2830 /*
2831 * The first segment requires special handling
2832 */
2833 if (i == 0)
2834 {
2835 ULONG_PTR VirtualAddress;
2836 ULONG_PTR VirtualOffset;
2837
2838 VirtualAddress = EffectiveSegment->VirtualAddress;
2839
2840 /* Round down the virtual address to the nearest page */
2841 EffectiveSegment->VirtualAddress = PAGE_ROUND_DOWN(VirtualAddress);
2842
2843 /* Round up the virtual size to the nearest page */
2844 EffectiveSegment->Length = PAGE_ROUND_UP(VirtualAddress + EffectiveSegment->Length) -
2845 EffectiveSegment->VirtualAddress;
2846
2847 /* Adjust the raw address and size */
2848 VirtualOffset = VirtualAddress - EffectiveSegment->VirtualAddress;
2849
2850 if (EffectiveSegment->FileOffset < VirtualOffset)
2851 {
2852 return FALSE;
2853 }
2854
2855 /*
2856 * Garbage in, garbage out: unaligned base addresses make the file
2857 * offset point in curious and odd places, but that's what we were
2858 * asked for
2859 */
2860 EffectiveSegment->FileOffset -= VirtualOffset;
2861 EffectiveSegment->RawLength += VirtualOffset;
2862 }
2863 else
2864 {
2865 PMM_SECTION_SEGMENT Segment = &ImageSectionObject->Segments[i];
2866 ULONG_PTR EndOfEffectiveSegment;
2867
2868 EndOfEffectiveSegment = EffectiveSegment->VirtualAddress + EffectiveSegment->Length;
2869 ASSERT((EndOfEffectiveSegment % PAGE_SIZE) == 0);
2870
2871 /*
2872 * The current segment begins exactly where the current effective
2873 * segment ended, therefore beginning a new effective segment
2874 */
2875 if (EndOfEffectiveSegment == Segment->VirtualAddress)
2876 {
2877 LastSegment ++;
2878 ASSERT(LastSegment <= i);
2879 ASSERT(LastSegment < ImageSectionObject->NrSegments);
2880
2881 EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2882
2883 /*
2884 * Copy the current segment. If necessary, the effective segment
2885 * will be expanded later
2886 */
2887 *EffectiveSegment = *Segment;
2888
2889 /*
2890 * Page-align the virtual size. We know for sure the virtual address
2891 * already is
2892 */
2893 ASSERT((EffectiveSegment->VirtualAddress % PAGE_SIZE) == 0);
2894 EffectiveSegment->Length = PAGE_ROUND_UP(EffectiveSegment->Length);
2895 }
2896 /*
2897 * The current segment is still part of the current effective segment:
2898 * extend the effective segment to reflect this
2899 */
2900 else if (EndOfEffectiveSegment > Segment->VirtualAddress)
2901 {
2902 static const ULONG FlagsToProtection[16] =
2903 {
2904 PAGE_NOACCESS,
2905 PAGE_READONLY,
2906 PAGE_READWRITE,
2907 PAGE_READWRITE,
2908 PAGE_EXECUTE_READ,
2909 PAGE_EXECUTE_READ,
2910 PAGE_EXECUTE_READWRITE,
2911 PAGE_EXECUTE_READWRITE,
2912 PAGE_WRITECOPY,
2913 PAGE_WRITECOPY,
2914 PAGE_WRITECOPY,
2915 PAGE_WRITECOPY,
2916 PAGE_EXECUTE_WRITECOPY,
2917 PAGE_EXECUTE_WRITECOPY,
2918 PAGE_EXECUTE_WRITECOPY,
2919 PAGE_EXECUTE_WRITECOPY
2920 };
2921
2922 unsigned ProtectionFlags;
2923
2924 /*
2925 * Extend the file size
2926 */
2927
2928 /* Unaligned segments must be contiguous within the file */
2929 if (Segment->FileOffset != (EffectiveSegment->FileOffset +
2930 EffectiveSegment->RawLength))
2931 {
2932 return FALSE;
2933 }
2934
2935 EffectiveSegment->RawLength += Segment->RawLength;
2936
2937 /*
2938 * Extend the virtual size
2939 */
2940 ASSERT(PAGE_ROUND_UP(Segment->VirtualAddress + Segment->Length) > EndOfEffectiveSegment);
2941
2942 EffectiveSegment->Length = PAGE_ROUND_UP(Segment->VirtualAddress + Segment->Length) -
2943 EffectiveSegment->VirtualAddress;
2944
2945 /*
2946 * Merge the protection
2947 */
2948 EffectiveSegment->Protection |= Segment->Protection;
2949
2950 /* Clean up redundance */
2951 ProtectionFlags = 0;
2952
2953 if(EffectiveSegment->Protection & PAGE_IS_READABLE)
2954 ProtectionFlags |= 1 << 0;
2955
2956 if(EffectiveSegment->Protection & PAGE_IS_WRITABLE)
2957 ProtectionFlags |= 1 << 1;
2958
2959 if(EffectiveSegment->Protection & PAGE_IS_EXECUTABLE)
2960 ProtectionFlags |= 1 << 2;
2961
2962 if(EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
2963 ProtectionFlags |= 1 << 3;
2964
2965 ASSERT(ProtectionFlags < 16);
2966 EffectiveSegment->Protection = FlagsToProtection[ProtectionFlags];
2967
2968 /* If a segment was required to be shared and cannot, fail */
2969 if(!(Segment->Protection & PAGE_IS_WRITECOPY) &&
2970 EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
2971 {
2972 return FALSE;
2973 }
2974 }
2975 /*
2976 * We assume no holes between segments at this point
2977 */
2978 else
2979 {
2980 ASSERT(FALSE);
2981 }
2982 }
2983 }
2984
2985 return TRUE;
2986 }
2987
2988 NTSTATUS
2989 ExeFmtpCreateImageSection(HANDLE FileHandle,
2990 PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2991 {
2992 LARGE_INTEGER Offset;
2993 PVOID FileHeader;
2994 PVOID FileHeaderBuffer;
2995 ULONG FileHeaderSize;
2996 ULONG Flags;
2997 ULONG OldNrSegments;
2998 NTSTATUS Status;
2999 ULONG i;
3000
3001 /*
3002 * Read the beginning of the file (2 pages). Should be enough to contain
3003 * all (or most) of the headers
3004 */
3005 Offset.QuadPart = 0;
3006
3007 /* FIXME: use FileObject instead of FileHandle */
3008 Status = ExeFmtpReadFile (FileHandle,
3009 &Offset,
3010 PAGE_SIZE * 2,
3011 &FileHeader,
3012 &FileHeaderBuffer,
3013 &FileHeaderSize);
3014
3015 if (!NT_SUCCESS(Status))
3016 return Status;
3017
3018 if (FileHeaderSize == 0)
3019 {
3020 ExFreePool(FileHeaderBuffer);
3021 return STATUS_UNSUCCESSFUL;
3022 }
3023
3024 /*
3025 * Look for a loader that can handle this executable
3026 */
3027 for (i = 0; i < RTL_NUMBER_OF(ExeFmtpLoaders); ++ i)
3028 {
3029 RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject));
3030 Flags = 0;
3031
3032 /* FIXME: use FileObject instead of FileHandle */
3033 Status = ExeFmtpLoaders[i](FileHeader,
3034 FileHeaderSize,
3035 FileHandle,
3036 ImageSectionObject,
3037 &Flags,
3038 ExeFmtpReadFile,
3039 ExeFmtpAllocateSegments);
3040
3041 if (!NT_SUCCESS(Status))
3042 {
3043 if (ImageSectionObject->Segments)
3044 {
3045 ExFreePool(ImageSectionObject->Segments);
3046 ImageSectionObject->Segments = NULL;
3047 }
3048 }
3049
3050 if (Status != STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
3051 break;
3052 }
3053
3054 ExFreePool(FileHeaderBuffer);
3055
3056 /*
3057 * No loader handled the format
3058 */
3059 if (Status == STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
3060 {
3061 Status = STATUS_INVALID_IMAGE_NOT_MZ;
3062 ASSERT(!NT_SUCCESS(Status));
3063 }
3064
3065 if (!NT_SUCCESS(Status))
3066 return Status;
3067
3068 ASSERT(ImageSectionObject->Segments != NULL);
3069
3070 /*
3071 * Some defaults
3072 */
3073 /* FIXME? are these values platform-dependent? */
3074 if(ImageSectionObject->StackReserve == 0)
3075 ImageSectionObject->StackReserve = 0x40000;
3076
3077 if(ImageSectionObject->StackCommit == 0)
3078 ImageSectionObject->StackCommit = 0x1000;
3079
3080 if(ImageSectionObject->ImageBase == 0)
3081 {
3082 if(ImageSectionObject->ImageCharacteristics & IMAGE_FILE_DLL)
3083 ImageSectionObject->ImageBase = 0x10000000;
3084 else
3085 ImageSectionObject->ImageBase = 0x00400000;
3086 }
3087
3088 /*
3089 * And now the fun part: fixing the segments
3090 */
3091
3092 /* Sort them by virtual address */
3093 MmspSortSegments(ImageSectionObject, Flags);
3094
3095 /* Ensure they don't overlap in memory */
3096 if (!MmspCheckSegmentBounds(ImageSectionObject, Flags))
3097 return STATUS_INVALID_IMAGE_FORMAT;
3098
3099 /* Ensure they are aligned */
3100 OldNrSegments = ImageSectionObject->NrSegments;
3101
3102 if (!MmspPageAlignSegments(ImageSectionObject, Flags))
3103 return STATUS_INVALID_IMAGE_FORMAT;
3104
3105 /* Trim them if the alignment phase merged some of them */
3106 if (ImageSectionObject->NrSegments < OldNrSegments)
3107 {
3108 PMM_SECTION_SEGMENT Segments;
3109 SIZE_T SizeOfSegments;
3110
3111 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * ImageSectionObject->NrSegments;
3112
3113 Segments = ExAllocatePoolWithTag(PagedPool,
3114 SizeOfSegments,
3115 TAG_MM_SECTION_SEGMENT);
3116
3117 if (Segments == NULL)
3118 return STATUS_INSUFFICIENT_RESOURCES;
3119
3120 RtlCopyMemory(Segments, ImageSectionObject->Segments, SizeOfSegments);
3121 ExFreePool(ImageSectionObject->Segments);
3122 ImageSectionObject->Segments = Segments;
3123 }
3124
3125 /* And finish their initialization */
3126 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3127 {
3128 ExInitializeFastMutex(&ImageSectionObject->Segments[i].Lock);
3129 ImageSectionObject->Segments[i].ReferenceCount = 1;
3130
3131 RtlZeroMemory(&ImageSectionObject->Segments[i].PageDirectory,
3132 sizeof(ImageSectionObject->Segments[i].PageDirectory));
3133 }
3134
3135 ASSERT(NT_SUCCESS(Status));
3136 return Status;
3137 }
3138
3139 NTSTATUS
3140 MmCreateImageSection(PSECTION_OBJECT *SectionObject,
3141 ACCESS_MASK DesiredAccess,
3142 POBJECT_ATTRIBUTES ObjectAttributes,
3143 PLARGE_INTEGER UMaximumSize,
3144 ULONG SectionPageProtection,
3145 ULONG AllocationAttributes,
3146 HANDLE FileHandle)
3147 {
3148 PSECTION_OBJECT Section;
3149 NTSTATUS Status;
3150 PFILE_OBJECT FileObject;
3151 PMM_SECTION_SEGMENT SectionSegments;
3152 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3153 ULONG i;
3154 ULONG FileAccess = 0;
3155
3156 /*
3157 * Specifying a maximum size is meaningless for an image section
3158 */
3159 if (UMaximumSize != NULL)
3160 {
3161 return(STATUS_INVALID_PARAMETER_4);
3162 }
3163
3164 /*
3165 * Check file access required
3166 */
3167 if (SectionPageProtection & PAGE_READWRITE ||
3168 SectionPageProtection & PAGE_EXECUTE_READWRITE)
3169 {
3170 FileAccess = FILE_READ_DATA | FILE_WRITE_DATA;
3171 }
3172 else
3173 {
3174 FileAccess = FILE_READ_DATA;
3175 }
3176
3177 /*
3178 * Reference the file handle
3179 */
3180 Status = ObReferenceObjectByHandle(FileHandle,
3181 FileAccess,
3182 IoFileObjectType,
3183 UserMode,
3184 (PVOID*)(PVOID)&FileObject,
3185 NULL);
3186
3187 if (!NT_SUCCESS(Status))
3188 {
3189 return Status;
3190 }
3191
3192 /*
3193 * Create the section
3194 */
3195 Status = ObCreateObject (ExGetPreviousMode(),
3196 MmSectionObjectType,
3197 ObjectAttributes,
3198 ExGetPreviousMode(),
3199 NULL,
3200 sizeof(SECTION_OBJECT),
3201 0,
3202 0,
3203 (PVOID*)(PVOID)&Section);
3204 if (!NT_SUCCESS(Status))
3205 {
3206 ObDereferenceObject(FileObject);
3207 return(Status);
3208 }
3209
3210 /*
3211 * Initialize it
3212 */
3213 Section->SectionPageProtection = SectionPageProtection;
3214 Section->AllocationAttributes = AllocationAttributes;
3215
3216 /*
3217 * Initialized caching for this file object if previously caching
3218 * was initialized for the same on disk file
3219 */
3220 Status = CcTryToInitializeFileCache(FileObject);
3221
3222 if (!NT_SUCCESS(Status) || FileObject->SectionObjectPointer->ImageSectionObject == NULL)
3223 {
3224 NTSTATUS StatusExeFmt;
3225
3226 ImageSectionObject = ExAllocatePoolWithTag(PagedPool, sizeof(MM_IMAGE_SECTION_OBJECT), TAG_MM_SECTION_SEGMENT);
3227 if (ImageSectionObject == NULL)
3228 {
3229 ObDereferenceObject(FileObject);
3230 ObDereferenceObject(Section);
3231 return(STATUS_NO_MEMORY);
3232 }
3233
3234 RtlZeroMemory(ImageSectionObject, sizeof(MM_IMAGE_SECTION_OBJECT));
3235
3236 StatusExeFmt = ExeFmtpCreateImageSection(FileHandle, ImageSectionObject);
3237
3238 if (!NT_SUCCESS(StatusExeFmt))
3239 {
3240 if(ImageSectionObject->Segments != NULL)
3241 ExFreePool(ImageSectionObject->Segments);
3242
3243 ExFreePool(ImageSectionObject);
3244 ObDereferenceObject(Section);
3245 ObDereferenceObject(FileObject);
3246 return(StatusExeFmt);
3247 }
3248
3249 Section->ImageSection = ImageSectionObject;
3250 ASSERT(ImageSectionObject->Segments);
3251
3252 /*
3253 * Lock the file
3254 */
3255 Status = MmspWaitForFileLock(FileObject);
3256 if (!NT_SUCCESS(Status))
3257 {
3258 ExFreePool(ImageSectionObject->Segments);
3259 ExFreePool(ImageSectionObject);
3260 ObDereferenceObject(Section);
3261 ObDereferenceObject(FileObject);
3262 return(Status);
3263 }
3264
3265 if (NULL != InterlockedCompareExchangePointer(&FileObject->SectionObjectPointer->ImageSectionObject,
3266 ImageSectionObject, NULL))
3267 {
3268 /*
3269 * An other thread has initialized the some image in the background
3270 */
3271 ExFreePool(ImageSectionObject->Segments);
3272 ExFreePool(ImageSectionObject);
3273 ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3274 Section->ImageSection = ImageSectionObject;
3275 SectionSegments = ImageSectionObject->Segments;
3276
3277 for (i = 0; i < ImageSectionObject->NrSegments; i++)
3278 {
3279 InterlockedIncrementUL(&SectionSegments[i].ReferenceCount);
3280 }
3281 }
3282
3283 Status = StatusExeFmt;
3284 }
3285 else
3286 {
3287 /*
3288 * Lock the file
3289 */
3290 Status = MmspWaitForFileLock(FileObject);
3291 if (Status != STATUS_SUCCESS)
3292 {
3293 ObDereferenceObject(Section);
3294 ObDereferenceObject(FileObject);
3295 return(Status);
3296 }
3297
3298 ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3299 Section->ImageSection = ImageSectionObject;
3300 SectionSegments = ImageSectionObject->Segments;
3301
3302 /*
3303 * Otherwise just reference all the section segments
3304 */
3305 for (i = 0; i < ImageSectionObject->NrSegments; i++)
3306 {
3307 InterlockedIncrementUL(&SectionSegments[i].ReferenceCount);
3308 }
3309
3310 Status = STATUS_SUCCESS;
3311 }
3312 Section->FileObject = FileObject;
3313 CcRosReferenceCache(FileObject);
3314 KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3315 *SectionObject = Section;
3316 return(Status);
3317 }
3318
3319 /*
3320 * @implemented
3321 */
3322 NTSTATUS STDCALL
3323 NtCreateSection (OUT PHANDLE SectionHandle,
3324 IN ACCESS_MASK DesiredAccess,
3325 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
3326 IN PLARGE_INTEGER MaximumSize OPTIONAL,
3327 IN ULONG SectionPageProtection OPTIONAL,
3328 IN ULONG AllocationAttributes,
3329 IN HANDLE FileHandle OPTIONAL)
3330 {
3331 LARGE_INTEGER SafeMaximumSize;
3332 PSECTION_OBJECT SectionObject;
3333 KPROCESSOR_MODE PreviousMode;
3334 NTSTATUS Status = STATUS_SUCCESS;
3335
3336 PreviousMode = ExGetPreviousMode();
3337
3338 if(MaximumSize != NULL && PreviousMode != KernelMode)
3339 {
3340 _SEH_TRY
3341 {
3342 /* make a copy on the stack */
3343 SafeMaximumSize = ProbeForReadLargeInteger(MaximumSize);
3344 MaximumSize = &SafeMaximumSize;
3345 }
3346 _SEH_HANDLE
3347 {
3348 Status = _SEH_GetExceptionCode();
3349 }
3350 _SEH_END;
3351
3352 if(!NT_SUCCESS(Status))
3353 {
3354 return Status;
3355 }
3356 }
3357
3358 /*
3359 * Check the protection
3360 */
3361 if ((SectionPageProtection & PAGE_FLAGS_VALID_FROM_USER_MODE) !=
3362 SectionPageProtection)
3363 {
3364 return(STATUS_INVALID_PAGE_PROTECTION);
3365 }
3366
3367 Status = MmCreateSection(&SectionObject,
3368 DesiredAccess,
3369 ObjectAttributes,
3370 MaximumSize,
3371 SectionPageProtection,
3372 AllocationAttributes,
3373 FileHandle,
3374 NULL);
3375
3376 if (NT_SUCCESS(Status))
3377 {
3378 Status = ObInsertObject ((PVOID)SectionObject,
3379 NULL,
3380 DesiredAccess,
3381 0,
3382 NULL,
3383 SectionHandle);
3384 ObDereferenceObject(SectionObject);
3385 }
3386
3387 return Status;
3388 }
3389
3390
3391 /**********************************************************************
3392 * NAME
3393 * NtOpenSection
3394 *
3395 * DESCRIPTION
3396 *
3397 * ARGUMENTS
3398 * SectionHandle
3399 *
3400 * DesiredAccess
3401 *
3402 * ObjectAttributes
3403 *
3404 * RETURN VALUE
3405 *
3406 * REVISIONS
3407 */
3408 NTSTATUS STDCALL
3409 NtOpenSection(PHANDLE SectionHandle,
3410 ACCESS_MASK DesiredAccess,
3411 POBJECT_ATTRIBUTES ObjectAttributes)
3412 {
3413 HANDLE hSection;
3414 KPROCESSOR_MODE PreviousMode;
3415 NTSTATUS Status = STATUS_SUCCESS;
3416
3417 PreviousMode = ExGetPreviousMode();
3418
3419 if(PreviousMode != KernelMode)
3420 {
3421 _SEH_TRY
3422 {
3423 ProbeForWriteHandle(SectionHandle);
3424 }
3425 _SEH_HANDLE
3426 {
3427 Status = _SEH_GetExceptionCode();
3428 }
3429 _SEH_END;
3430
3431 if(!NT_SUCCESS(Status))
3432 {
3433 return Status;
3434 }
3435 }
3436
3437 Status = ObOpenObjectByName(ObjectAttributes,
3438 MmSectionObjectType,
3439 NULL,
3440 PreviousMode,
3441 DesiredAccess,
3442 NULL,
3443 &hSection);
3444
3445 if(NT_SUCCESS(Status))
3446 {
3447 _SEH_TRY
3448 {
3449 *SectionHandle = hSection;
3450 }
3451 _SEH_HANDLE
3452 {
3453 Status = _SEH_GetExceptionCode();
3454 }
3455 _SEH_END;
3456 }
3457
3458 return(Status);
3459 }
3460
3461 NTSTATUS STATIC
3462 MmMapViewOfSegment(PEPROCESS Process,
3463 PMADDRESS_SPACE AddressSpace,
3464 PSECTION_OBJECT Section,
3465 PMM_SECTION_SEGMENT Segment,
3466 PVOID* BaseAddress,
3467 ULONG ViewSize,
3468 ULONG Protect,
3469 ULONG ViewOffset,
3470 BOOL TopDown)
3471 {
3472 PMEMORY_AREA MArea;
3473 NTSTATUS Status;
3474 PHYSICAL_ADDRESS BoundaryAddressMultiple;
3475
3476 BoundaryAddressMultiple.QuadPart = 0;
3477
3478 Status = MmCreateMemoryArea(Process,
3479 AddressSpace,
3480 MEMORY_AREA_SECTION_VIEW,
3481 BaseAddress,
3482 ViewSize,
3483 Protect,
3484 &MArea,
3485 FALSE,
3486 TopDown,
3487 BoundaryAddressMultiple);
3488 if (!NT_SUCCESS(Status))
3489 {
3490 DPRINT1("Mapping between 0x%.8X and 0x%.8X failed (%X).\n",
3491 (*BaseAddress), (char*)(*BaseAddress) + ViewSize, Status);
3492 return(Status);
3493 }
3494
3495
3496 ObReferenceObjectByPointer((PVOID)Section,
3497 SECTION_MAP_READ,
3498 NULL,
3499 ExGetPreviousMode());
3500 MArea->Data.SectionData.Segment = Segment;
3501 MArea->Data.SectionData.Section = Section;
3502 MArea->Data.SectionData.ViewOffset = ViewOffset;
3503 MArea->Data.SectionData.WriteCopyView = FALSE;
3504 MmInitialiseRegion(&MArea->Data.SectionData.RegionListHead,
3505 ViewSize, 0, Protect);
3506
3507 return(STATUS_SUCCESS);
3508 }
3509
3510
3511 /**********************************************************************
3512 * NAME EXPORTED
3513 * NtMapViewOfSection
3514 *
3515 * DESCRIPTION
3516 * Maps a view of a section into the virtual address space of a
3517 * process.
3518 *
3519 * ARGUMENTS
3520 * SectionHandle
3521 * Handle of the section.
3522 *
3523 * ProcessHandle
3524 * Handle of the process.
3525 *
3526 * BaseAddress
3527 * Desired base address (or NULL) on entry;
3528 * Actual base address of the view on exit.
3529 *
3530 * ZeroBits
3531 * Number of high order address bits that must be zero.
3532 *
3533 * CommitSize
3534 * Size in bytes of the initially committed section of
3535 * the view.
3536 *
3537 * SectionOffset
3538 * Offset in bytes from the beginning of the section
3539 * to the beginning of the view.
3540 *
3541 * ViewSize
3542 * Desired length of map (or zero to map all) on entry
3543 * Actual length mapped on exit.
3544 *
3545 * InheritDisposition
3546 * Specified how the view is to be shared with
3547 * child processes.
3548 *
3549 * AllocateType
3550 * Type of allocation for the pages.
3551 *
3552 * Protect
3553 * Protection for the committed region of the view.
3554 *
3555 * RETURN VALUE
3556 * Status.
3557 *
3558 * @implemented
3559 */
3560 NTSTATUS STDCALL
3561 NtMapViewOfSection(IN HANDLE SectionHandle,
3562 IN HANDLE ProcessHandle,
3563 IN OUT PVOID* BaseAddress OPTIONAL,
3564 IN ULONG ZeroBits OPTIONAL,
3565 IN ULONG CommitSize,
3566 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
3567 IN OUT PULONG ViewSize,
3568 IN SECTION_INHERIT InheritDisposition,
3569 IN ULONG AllocationType OPTIONAL,
3570 IN ULONG Protect)
3571 {
3572 PVOID SafeBaseAddress;
3573 LARGE_INTEGER SafeSectionOffset;
3574 ULONG SafeViewSize;
3575 PSECTION_OBJECT Section;
3576 PEPROCESS Process;
3577 KPROCESSOR_MODE PreviousMode;
3578 PMADDRESS_SPACE AddressSpace;
3579 NTSTATUS Status = STATUS_SUCCESS;
3580
3581 PreviousMode = ExGetPreviousMode();
3582
3583 if(PreviousMode != KernelMode)
3584 {
3585 SafeBaseAddress = NULL;
3586 SafeSectionOffset.QuadPart = 0;
3587 SafeViewSize = 0;
3588
3589 _SEH_TRY
3590 {
3591 if(BaseAddress != NULL)
3592 {
3593 ProbeForWritePointer(BaseAddress);
3594 SafeBaseAddress = *BaseAddress;
3595 }
3596 if(SectionOffset != NULL)
3597 {
3598 ProbeForWriteLargeInteger(SectionOffset);
3599 SafeSectionOffset = *SectionOffset;
3600 }
3601 ProbeForWriteUlong(ViewSize);
3602 SafeViewSize = *ViewSize;
3603 }
3604 _SEH_HANDLE
3605 {
3606 Status = _SEH_GetExceptionCode();
3607 }
3608 _SEH_END;
3609
3610 if(!NT_SUCCESS(Status))
3611 {
3612 return Status;
3613 }
3614 }
3615 else
3616 {
3617 SafeBaseAddress = (BaseAddress != NULL ? *BaseAddress : NULL);
3618 SafeSectionOffset.QuadPart = (SectionOffset != NULL ? SectionOffset->QuadPart : 0);
3619 SafeViewSize = (ViewSize != NULL ? *ViewSize : 0);
3620 }
3621
3622 Status = ObReferenceObjectByHandle(ProcessHandle,
3623 PROCESS_VM_OPERATION,
3624 PsProcessType,
3625 PreviousMode,
3626 (PVOID*)(PVOID)&Process,
3627 NULL);
3628 if (!NT_SUCCESS(Status))
3629 {
3630 return(Status);
3631 }
3632
3633 AddressSpace = &Process->AddressSpace;
3634
3635 Status = ObReferenceObjectByHandle(SectionHandle,
3636 SECTION_MAP_READ,
3637 MmSectionObjectType,
3638 PreviousMode,
3639 (PVOID*)(PVOID)&Section,
3640 NULL);
3641 if (!(NT_SUCCESS(Status)))
3642 {
3643 DPRINT("ObReference failed rc=%x\n",Status);
3644 ObDereferenceObject(Process);
3645 return(Status);
3646 }
3647
3648 Status = MmMapViewOfSection(Section,
3649 Process,
3650 (BaseAddress != NULL ? &SafeBaseAddress : NULL),
3651 ZeroBits,
3652 CommitSize,
3653 (SectionOffset != NULL ? &SafeSectionOffset : NULL),
3654 (ViewSize != NULL ? &SafeViewSize : NULL),
3655 InheritDisposition,
3656 AllocationType,
3657 Protect);
3658
3659 ObDereferenceObject(Section);
3660 ObDereferenceObject(Process);
3661
3662 if(NT_SUCCESS(Status))
3663 {
3664 /* copy parameters back to the caller */
3665 _SEH_TRY
3666 {
3667 if(BaseAddress != NULL)
3668 {
3669 *BaseAddress = SafeBaseAddress;
3670 }
3671 if(SectionOffset != NULL)
3672 {
3673 *SectionOffset = SafeSectionOffset;
3674 }
3675 if(ViewSize != NULL)
3676 {
3677 *ViewSize = SafeViewSize;
3678 }
3679 }
3680 _SEH_HANDLE
3681 {
3682 Status = _SEH_GetExceptionCode();
3683 }
3684 _SEH_END;
3685 }
3686
3687 return(Status);
3688 }
3689
3690 VOID STATIC
3691 MmFreeSectionPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
3692 PFN_TYPE Page, SWAPENTRY SwapEntry, BOOLEAN Dirty)
3693 {
3694 ULONG Entry;
3695 PFILE_OBJECT FileObject;
3696 PBCB Bcb;
3697 ULONG Offset;
3698 SWAPENTRY SavedSwapEntry;
3699 PMM_PAGEOP PageOp;
3700 NTSTATUS Status;
3701 PSECTION_OBJECT Section;
3702 PMM_SECTION_SEGMENT Segment;
3703 PMADDRESS_SPACE AddressSpace;
3704
3705 AddressSpace = (PMADDRESS_SPACE)Context;
3706
3707 Address = (PVOID)PAGE_ROUND_DOWN(Address);
3708
3709 Offset = ((ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress) +
3710 MemoryArea->Data.SectionData.ViewOffset;
3711
3712 Section = MemoryArea->Data.SectionData.Section;
3713 Segment = MemoryArea->Data.SectionData.Segment;
3714
3715 PageOp = MmCheckForPageOp(MemoryArea, NULL, NULL, Segment, Offset);
3716
3717 while (PageOp)
3718 {
3719 MmUnlockSectionSegment(Segment);
3720 MmUnlockAddressSpace(AddressSpace);
3721
3722 Status = MmspWaitForPageOpCompletionEvent(PageOp);
3723 if (Status != STATUS_SUCCESS)
3724 {
3725 DPRINT1("Failed to wait for page op, status = %x\n", Status);
3726 KEBUGCHECK(0);
3727 }
3728
3729 MmLockAddressSpace(AddressSpace);
3730 MmLockSectionSegment(Segment);
3731 MmspCompleteAndReleasePageOp(PageOp);
3732 PageOp = MmCheckForPageOp(MemoryArea, NULL, NULL, Segment, Offset);
3733 }
3734
3735 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
3736
3737 /*
3738 * For a dirty, datafile, non-private page mark it as dirty in the
3739 * cache manager.
3740 */
3741 if (Segment->Flags & MM_DATAFILE_SEGMENT)
3742 {
3743 if (Page == PFN_FROM_SSE(Entry) && Dirty)
3744 {
3745 FileObject = MemoryArea->Data.SectionData.Section->FileObject;
3746 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
3747 CcRosMarkDirtyCacheSegment(Bcb, Offset + Segment->FileOffset);
3748 ASSERT(SwapEntry == 0);
3749 }
3750 }
3751
3752 if (SwapEntry != 0)
3753 {
3754 /*
3755 * Sanity check
3756 */
3757 if (Segment->Flags & MM_PAGEFILE_SEGMENT)
3758 {
3759 DPRINT1("Found a swap entry for a page in a pagefile section.\n");
3760 KEBUGCHECK(0);
3761 }
3762 MmFreeSwapPage(SwapEntry);
3763 }
3764 else if (Page != 0)
3765 {
3766 if (IS_SWAP_FROM_SSE(Entry) ||
3767 Page != PFN_FROM_SSE(Entry))
3768 {
3769 /*
3770 * Sanity check
3771 */
3772 if (Segment->Flags & MM_PAGEFILE_SEGMENT)
3773 {
3774 DPRINT1("Found a private page in a pagefile section.\n");
3775 KEBUGCHECK(0);
3776 }
3777 /*
3778 * Just dereference private pages
3779 */
3780 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
3781 if (SavedSwapEntry != 0)
3782 {
3783 MmFreeSwapPage(SavedSwapEntry);
3784 MmSetSavedSwapEntryPage(Page, 0);
3785 }
3786 MmDeleteRmap(Page, AddressSpace->Process, Address);
3787 MmReleasePageMemoryConsumer(MC_USER, Page);
3788 }
3789 else
3790 {
3791 MmDeleteRmap(Page, AddressSpace->Process, Address);
3792 MmUnsharePageEntrySectionSegment(Section, Segment, Offset, Dirty, FALSE);
3793 }
3794 }
3795 }
3796
3797 NTSTATUS
3798 MmUnmapViewOfSegment(PMADDRESS_SPACE AddressSpace,
3799 PVOID BaseAddress)
3800 {
3801 NTSTATUS Status;
3802 PMEMORY_AREA MemoryArea;
3803 PSECTION_OBJECT Section;
3804 PMM_SECTION_SEGMENT Segment;
3805 PLIST_ENTRY CurrentEntry;
3806 PMM_REGION CurrentRegion;
3807 PLIST_ENTRY RegionListHead;
3808
3809 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace,
3810 BaseAddress);
3811 if (MemoryArea == NULL)
3812 {
3813 return(STATUS_UNSUCCESSFUL);
3814 }
3815
3816 MemoryArea->DeleteInProgress = TRUE;
3817 Section = MemoryArea->Data.SectionData.Section;
3818 Segment = MemoryArea->Data.SectionData.Segment;
3819
3820 MmLockSectionSegment(Segment);
3821
3822 RegionListHead = &MemoryArea->Data.SectionData.RegionListHead;
3823 while (!IsListEmpty(RegionListHead))
3824 {
3825 CurrentEntry = RemoveHeadList(RegionListHead);
3826 CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION, RegionListEntry);
3827 ExFreePool(CurrentRegion);
3828 }
3829
3830 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
3831 {
3832 Status = MmFreeMemoryArea(AddressSpace,
3833 MemoryArea,
3834 NULL,
3835 NULL);
3836 }
3837 else
3838 {
3839 Status = MmFreeMemoryArea(AddressSpace,
3840 MemoryArea,
3841 MmFreeSectionPage,
3842 AddressSpace);
3843 }
3844 MmUnlockSectionSegment(Segment);
3845 ObDereferenceObject(Section);
3846 return(STATUS_SUCCESS);
3847 }
3848
3849 /*
3850 * @implemented
3851 */
3852 NTSTATUS STDCALL
3853 MmUnmapViewOfSection(PEPROCESS Process,
3854 PVOID BaseAddress)
3855 {
3856 NTSTATUS Status;
3857 PMEMORY_AREA MemoryArea;
3858 PMADDRESS_SPACE AddressSpace;
3859 PSECTION_OBJECT Section;
3860
3861 DPRINT("Opening memory area Process %x BaseAddress %x\n",
3862 Process, BaseAddress);
3863
3864 ASSERT(Process);
3865
3866 AddressSpace = &Process->AddressSpace;
3867 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace,
3868 BaseAddress);
3869 if (MemoryArea == NULL ||
3870 MemoryArea->Type != MEMORY_AREA_SECTION_VIEW ||
3871 MemoryArea->DeleteInProgress)
3872 {
3873 return STATUS_NOT_MAPPED_VIEW;
3874 }
3875
3876 Section = MemoryArea->Data.SectionData.Section;
3877
3878 if (Section->AllocationAttributes & SEC_IMAGE)
3879 {
3880 ULONG i;
3881 ULONG NrSegments;
3882 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3883 PMM_SECTION_SEGMENT SectionSegments;
3884 PVOID ImageBaseAddress = 0;
3885 PMM_SECTION_SEGMENT Segment;
3886
3887 Segment = MemoryArea->Data.SectionData.Segment;
3888 ImageSectionObject = Section->ImageSection;
3889 SectionSegments = ImageSectionObject->Segments;
3890 NrSegments = ImageSectionObject->NrSegments;
3891
3892 /* Search for the current segment within the section segments
3893 * and calculate the image base address */
3894 for (i = 0; i < NrSegments; i++)
3895 {
3896 if (!(SectionSegments[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
3897 {
3898 if (Segment == &SectionSegments[i])
3899 {
3900 ImageBaseAddress = (char*)BaseAddress - (ULONG_PTR)SectionSegments[i].VirtualAddress;
3901 break;
3902 }
3903 }
3904 }
3905 if (i >= NrSegments)
3906 {
3907 KEBUGCHECK(0);
3908 }
3909
3910 for (i = 0; i < NrSegments; i++)
3911 {
3912 if (!(SectionSegments[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
3913 {
3914 PVOID SBaseAddress = (PVOID)
3915 ((char*)ImageBaseAddress + (ULONG_PTR)SectionSegments[i].VirtualAddress);
3916
3917 Status = MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
3918 }
3919 }
3920 }
3921 else
3922 {
3923 Status = MmUnmapViewOfSegment(AddressSpace, BaseAddress);
3924 }
3925 return(STATUS_SUCCESS);
3926 }
3927
3928 /**********************************************************************
3929 * NAME EXPORTED
3930 * NtUnmapViewOfSection
3931 *
3932 * DESCRIPTION
3933 *
3934 * ARGUMENTS
3935 * ProcessHandle
3936 *
3937 * BaseAddress
3938 *
3939 * RETURN VALUE
3940 * Status.
3941 *
3942 * REVISIONS
3943 */
3944 NTSTATUS STDCALL
3945 NtUnmapViewOfSection (HANDLE ProcessHandle,
3946 PVOID BaseAddress)
3947 {
3948 PEPROCESS Process;
3949 KPROCESSOR_MODE PreviousMode;
3950 NTSTATUS Status;
3951
3952 DPRINT("NtUnmapViewOfSection(ProcessHandle %x, BaseAddress %x)\n",
3953 ProcessHandle, BaseAddress);
3954
3955 PreviousMode = ExGetPreviousMode();
3956
3957 DPRINT("Referencing process\n");
3958 Status = ObReferenceObjectByHandle(ProcessHandle,
3959 PROCESS_VM_OPERATION,
3960 PsProcessType,
3961 PreviousMode,
3962 (PVOID*)(PVOID)&Process,
3963 NULL);
3964 if (!NT_SUCCESS(Status))
3965 {
3966 DPRINT("ObReferenceObjectByHandle failed (Status %x)\n", Status);
3967 return(Status);
3968 }
3969
3970 MmLockAddressSpace(&Process->AddressSpace);
3971 Status = MmUnmapViewOfSection(Process, BaseAddress);
3972 MmUnlockAddressSpace(&Process->AddressSpace);
3973
3974 ObDereferenceObject(Process);
3975
3976 return Status;
3977 }
3978
3979
3980 /**
3981 * Queries the information of a section object.
3982 *
3983 * @param SectionHandle
3984 * Handle to the section object. It must be opened with SECTION_QUERY
3985 * access.
3986 * @param SectionInformationClass
3987 * Index to a certain information structure. Can be either
3988 * SectionBasicInformation or SectionImageInformation. The latter
3989 * is valid only for sections that were created with the SEC_IMAGE
3990 * flag.
3991 * @param SectionInformation
3992 * Caller supplies storage for resulting information.
3993 * @param Length
3994 * Size of the supplied storage.
3995 * @param ResultLength
3996 * Data written.
3997 *
3998 * @return Status.
3999 *
4000 * @implemented
4001 */
4002 NTSTATUS STDCALL
4003 NtQuerySection(IN HANDLE SectionHandle,
4004 IN SECTION_INFORMATION_CLASS SectionInformationClass,
4005 OUT PVOID SectionInformation,
4006 IN ULONG SectionInformationLength,
4007 OUT PULONG ResultLength OPTIONAL)
4008 {
4009 PSECTION_OBJECT Section;
4010 KPROCESSOR_MODE PreviousMode;