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