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