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