Copy wininet to branch
[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 STDCALL
2072 MmpCreateSection(PVOID ObjectBody,
2073 PVOID Parent,
2074 PWSTR RemainingPath,
2075 POBJECT_ATTRIBUTES ObjectAttributes)
2076 {
2077 DPRINT("MmpCreateSection(ObjectBody %x, Parent %x, RemainingPath %S)\n",
2078 ObjectBody, Parent, RemainingPath);
2079
2080 if (RemainingPath == NULL)
2081 {
2082 return(STATUS_SUCCESS);
2083 }
2084
2085 if (wcschr(RemainingPath+1, L'\\') != NULL)
2086 {
2087 return(STATUS_UNSUCCESSFUL);
2088 }
2089 return(STATUS_SUCCESS);
2090 }
2091
2092 NTSTATUS INIT_FUNCTION
2093 MmCreatePhysicalMemorySection(VOID)
2094 {
2095 PSECTION_OBJECT PhysSection;
2096 NTSTATUS Status;
2097 OBJECT_ATTRIBUTES Obj;
2098 UNICODE_STRING Name = ROS_STRING_INITIALIZER(L"\\Device\\PhysicalMemory");
2099 LARGE_INTEGER SectionSize;
2100
2101 /*
2102 * Create the section mapping physical memory
2103 */
2104 SectionSize.QuadPart = 0xFFFFFFFF;
2105 InitializeObjectAttributes(&Obj,
2106 &Name,
2107 0,
2108 NULL,
2109 NULL);
2110 Status = MmCreateSection(&PhysSection,
2111 SECTION_ALL_ACCESS,
2112 &Obj,
2113 &SectionSize,
2114 PAGE_EXECUTE_READWRITE,
2115 0,
2116 NULL,
2117 NULL);
2118 if (!NT_SUCCESS(Status))
2119 {
2120 DbgPrint("Failed to create PhysicalMemory section\n");
2121 KEBUGCHECK(0);
2122 }
2123 PhysSection->AllocationAttributes |= SEC_PHYSICALMEMORY;
2124
2125 return(STATUS_SUCCESS);
2126 }
2127
2128 NTSTATUS INIT_FUNCTION
2129 MmInitSectionImplementation(VOID)
2130 {
2131 MmSectionObjectType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE));
2132
2133 RtlInitUnicodeString(&MmSectionObjectType->TypeName, L"Section");
2134
2135 MmSectionObjectType->Tag = TAG('S', 'E', 'C', 'T');
2136 MmSectionObjectType->TotalObjects = 0;
2137 MmSectionObjectType->TotalHandles = 0;
2138 MmSectionObjectType->PeakObjects = 0;
2139 MmSectionObjectType->PeakHandles = 0;
2140 MmSectionObjectType->PagedPoolCharge = 0;
2141 MmSectionObjectType->NonpagedPoolCharge = sizeof(SECTION_OBJECT);
2142 MmSectionObjectType->Mapping = &MmpSectionMapping;
2143 MmSectionObjectType->Dump = NULL;
2144 MmSectionObjectType->Open = NULL;
2145 MmSectionObjectType->Close = MmpCloseSection;
2146 MmSectionObjectType->Delete = MmpDeleteSection;
2147 MmSectionObjectType->Parse = NULL;
2148 MmSectionObjectType->Security = NULL;
2149 MmSectionObjectType->QueryName = NULL;
2150 MmSectionObjectType->OkayToClose = NULL;
2151 MmSectionObjectType->Create = MmpCreateSection;
2152 MmSectionObjectType->DuplicationNotify = NULL;
2153
2154 /*
2155 * NOTE: Do not register the section object type here because
2156 * the object manager it not initialized yet!
2157 * The section object type will be created in ObInit().
2158 */
2159 ObpCreateTypeObject(MmSectionObjectType);
2160
2161 return(STATUS_SUCCESS);
2162 }
2163
2164 NTSTATUS
2165 MmCreatePageFileSection(PSECTION_OBJECT *SectionObject,
2166 ACCESS_MASK DesiredAccess,
2167 POBJECT_ATTRIBUTES ObjectAttributes,
2168 PLARGE_INTEGER UMaximumSize,
2169 ULONG SectionPageProtection,
2170 ULONG AllocationAttributes)
2171 /*
2172 * Create a section which is backed by the pagefile
2173 */
2174 {
2175 LARGE_INTEGER MaximumSize;
2176 PSECTION_OBJECT Section;
2177 PMM_SECTION_SEGMENT Segment;
2178 NTSTATUS Status;
2179
2180 if (UMaximumSize == NULL)
2181 {
2182 return(STATUS_UNSUCCESSFUL);
2183 }
2184 MaximumSize = *UMaximumSize;
2185
2186 /*
2187 * Create the section
2188 */
2189 Status = ObCreateObject(ExGetPreviousMode(),
2190 MmSectionObjectType,
2191 ObjectAttributes,
2192 ExGetPreviousMode(),
2193 NULL,
2194 sizeof(SECTION_OBJECT),
2195 0,
2196 0,
2197 (PVOID*)(PVOID)&Section);
2198 if (!NT_SUCCESS(Status))
2199 {
2200 return(Status);
2201 }
2202
2203 /*
2204 * Initialize it
2205 */
2206 Section->SectionPageProtection = SectionPageProtection;
2207 Section->AllocationAttributes = AllocationAttributes;
2208 Section->Segment = NULL;
2209 InitializeListHead(&Section->ViewListHead);
2210 KeInitializeSpinLock(&Section->ViewListLock);
2211 Section->FileObject = NULL;
2212 Section->MaximumSize = MaximumSize;
2213 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2214 TAG_MM_SECTION_SEGMENT);
2215 if (Segment == NULL)
2216 {
2217 ObDereferenceObject(Section);
2218 return(STATUS_NO_MEMORY);
2219 }
2220 Section->Segment = Segment;
2221 Segment->ReferenceCount = 1;
2222 ExInitializeFastMutex(&Segment->Lock);
2223 Segment->FileOffset = 0;
2224 Segment->Protection = SectionPageProtection;
2225 Segment->RawLength = MaximumSize.u.LowPart;
2226 Segment->Length = PAGE_ROUND_UP(MaximumSize.u.LowPart);
2227 Segment->Flags = MM_PAGEFILE_SEGMENT;
2228 Segment->WriteCopy = FALSE;
2229 RtlZeroMemory(&Segment->PageDirectory, sizeof(SECTION_PAGE_DIRECTORY));
2230 Segment->VirtualAddress = 0;
2231 Segment->Characteristics = 0;
2232 *SectionObject = Section;
2233 return(STATUS_SUCCESS);
2234 }
2235
2236
2237 NTSTATUS
2238 MmCreateDataFileSection(PSECTION_OBJECT *SectionObject,
2239 ACCESS_MASK DesiredAccess,
2240 POBJECT_ATTRIBUTES ObjectAttributes,
2241 PLARGE_INTEGER UMaximumSize,
2242 ULONG SectionPageProtection,
2243 ULONG AllocationAttributes,
2244 HANDLE FileHandle)
2245 /*
2246 * Create a section backed by a data file
2247 */
2248 {
2249 PSECTION_OBJECT Section;
2250 NTSTATUS Status;
2251 LARGE_INTEGER MaximumSize;
2252 PFILE_OBJECT FileObject;
2253 PMM_SECTION_SEGMENT Segment;
2254 ULONG FileAccess;
2255 IO_STATUS_BLOCK Iosb;
2256 LARGE_INTEGER Offset;
2257 CHAR Buffer;
2258 FILE_STANDARD_INFORMATION FileInfo;
2259
2260 /*
2261 * Create the section
2262 */
2263 Status = ObCreateObject(ExGetPreviousMode(),
2264 MmSectionObjectType,
2265 ObjectAttributes,
2266 ExGetPreviousMode(),
2267 NULL,
2268 sizeof(SECTION_OBJECT),
2269 0,
2270 0,
2271 (PVOID*)(PVOID)&Section);
2272 if (!NT_SUCCESS(Status))
2273 {
2274 return(Status);
2275 }
2276
2277 /*
2278 * Initialize it
2279 */
2280 Section->SectionPageProtection = SectionPageProtection;
2281 Section->AllocationAttributes = AllocationAttributes;
2282 Section->Segment = NULL;
2283 InitializeListHead(&Section->ViewListHead);
2284 KeInitializeSpinLock(&Section->ViewListLock);
2285
2286 /*
2287 * Check file access required
2288 */
2289 if (SectionPageProtection & PAGE_READWRITE ||
2290 SectionPageProtection & PAGE_EXECUTE_READWRITE)
2291 {
2292 FileAccess = FILE_READ_DATA | FILE_WRITE_DATA;
2293 }
2294 else
2295 {
2296 FileAccess = FILE_READ_DATA;
2297 }
2298
2299 /*
2300 * Reference the file handle
2301 */
2302 Status = ObReferenceObjectByHandle(FileHandle,
2303 FileAccess,
2304 IoFileObjectType,
2305 UserMode,
2306 (PVOID*)(PVOID)&FileObject,
2307 NULL);
2308 if (!NT_SUCCESS(Status))
2309 {
2310 ObDereferenceObject(Section);
2311 return(Status);
2312 }
2313
2314 /*
2315 * FIXME: This is propably not entirely correct. We can't look into
2316 * the standard FCB header because it might not be initialized yet
2317 * (as in case of the EXT2FS driver by Manoj Paul Joseph where the
2318 * standard file information is filled on first request).
2319 */
2320 Status = NtQueryInformationFile(FileHandle,
2321 &Iosb,
2322 &FileInfo,
2323 sizeof(FILE_STANDARD_INFORMATION),
2324 FileStandardInformation);
2325 if (!NT_SUCCESS(Status))
2326 {
2327 ObDereferenceObject(Section);
2328 ObDereferenceObject(FileObject);
2329 return Status;
2330 }
2331
2332 /*
2333 * FIXME: Revise this once a locking order for file size changes is
2334 * decided
2335 */
2336 if (UMaximumSize != NULL)
2337 {
2338 MaximumSize = *UMaximumSize;
2339 }
2340 else
2341 {
2342 MaximumSize = FileInfo.EndOfFile;
2343 /* Mapping zero-sized files isn't allowed. */
2344 if (MaximumSize.QuadPart == 0)
2345 {
2346 ObDereferenceObject(Section);
2347 ObDereferenceObject(FileObject);
2348 return STATUS_FILE_INVALID;
2349 }
2350 }
2351
2352 if (MaximumSize.QuadPart > FileInfo.EndOfFile.QuadPart)
2353 {
2354 Status = NtSetInformationFile(FileHandle,
2355 &Iosb,
2356 &MaximumSize,
2357 sizeof(LARGE_INTEGER),
2358 FileAllocationInformation);
2359 if (!NT_SUCCESS(Status))
2360 {
2361 ObDereferenceObject(Section);
2362 ObDereferenceObject(FileObject);
2363 return(STATUS_SECTION_NOT_EXTENDED);
2364 }
2365 }
2366
2367 if (FileObject->SectionObjectPointer == NULL ||
2368 FileObject->SectionObjectPointer->SharedCacheMap == NULL)
2369 {
2370 /*
2371 * Read a bit so caching is initiated for the file object.
2372 * This is only needed because MiReadPage currently cannot
2373 * handle non-cached streams.
2374 */
2375 Offset.QuadPart = 0;
2376 Status = ZwReadFile(FileHandle,
2377 NULL,
2378 NULL,
2379 NULL,
2380 &Iosb,
2381 &Buffer,
2382 sizeof (Buffer),
2383 &Offset,
2384 0);
2385 if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE))
2386 {
2387 ObDereferenceObject(Section);
2388 ObDereferenceObject(FileObject);
2389 return(Status);
2390 }
2391 if (FileObject->SectionObjectPointer == NULL ||
2392 FileObject->SectionObjectPointer->SharedCacheMap == NULL)
2393 {
2394 /* FIXME: handle this situation */
2395 ObDereferenceObject(Section);
2396 ObDereferenceObject(FileObject);
2397 return STATUS_INVALID_PARAMETER;
2398 }
2399 }
2400
2401 /*
2402 * Lock the file
2403 */
2404 Status = MmspWaitForFileLock(FileObject);
2405 if (Status != STATUS_SUCCESS)
2406 {
2407 ObDereferenceObject(Section);
2408 ObDereferenceObject(FileObject);
2409 return(Status);
2410 }
2411
2412 /*
2413 * If this file hasn't been mapped as a data file before then allocate a
2414 * section segment to describe the data file mapping
2415 */
2416 if (FileObject->SectionObjectPointer->DataSectionObject == NULL)
2417 {
2418 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2419 TAG_MM_SECTION_SEGMENT);
2420 if (Segment == NULL)
2421 {
2422 KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2423 ObDereferenceObject(Section);
2424 ObDereferenceObject(FileObject);
2425 return(STATUS_NO_MEMORY);
2426 }
2427 Section->Segment = Segment;
2428 Segment->ReferenceCount = 1;
2429 ExInitializeFastMutex(&Segment->Lock);
2430 /*
2431 * Set the lock before assigning the segment to the file object
2432 */
2433 ExAcquireFastMutex(&Segment->Lock);
2434 FileObject->SectionObjectPointer->DataSectionObject = (PVOID)Segment;
2435
2436 Segment->FileOffset = 0;
2437 Segment->Protection = SectionPageProtection;
2438 Segment->Flags = MM_DATAFILE_SEGMENT;
2439 Segment->Characteristics = 0;
2440 Segment->WriteCopy = FALSE;
2441 if (AllocationAttributes & SEC_RESERVE)
2442 {
2443 Segment->Length = Segment->RawLength = 0;
2444 }
2445 else
2446 {
2447 Segment->RawLength = MaximumSize.u.LowPart;
2448 Segment->Length = PAGE_ROUND_UP(Segment->RawLength);
2449 }
2450 Segment->VirtualAddress = 0;
2451 RtlZeroMemory(&Segment->PageDirectory, sizeof(SECTION_PAGE_DIRECTORY));
2452 }
2453 else
2454 {
2455 /*
2456 * If the file is already mapped as a data file then we may need
2457 * to extend it
2458 */
2459 Segment =
2460 (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->
2461 DataSectionObject;
2462 Section->Segment = Segment;
2463 InterlockedIncrementUL(&Segment->ReferenceCount);
2464 MmLockSectionSegment(Segment);
2465
2466 if (MaximumSize.u.LowPart > Segment->RawLength &&
2467 !(AllocationAttributes & SEC_RESERVE))
2468 {
2469 Segment->RawLength = MaximumSize.u.LowPart;
2470 Segment->Length = PAGE_ROUND_UP(Segment->RawLength);
2471 }
2472 }
2473 MmUnlockSectionSegment(Segment);
2474 Section->FileObject = FileObject;
2475 Section->MaximumSize = MaximumSize;
2476 CcRosReferenceCache(FileObject);
2477 KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2478 *SectionObject = Section;
2479 return(STATUS_SUCCESS);
2480 }
2481
2482 /*
2483 TODO: not that great (declaring loaders statically, having to declare all of
2484 them, having to keep them extern, etc.), will fix in the future
2485 */
2486 extern NTSTATUS NTAPI PeFmtCreateSection
2487 (
2488 IN CONST VOID * FileHeader,
2489 IN SIZE_T FileHeaderSize,
2490 IN PVOID File,
2491 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2492 OUT PULONG Flags,
2493 IN PEXEFMT_CB_READ_FILE ReadFileCb,
2494 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2495 );
2496
2497 extern NTSTATUS NTAPI ElfFmtCreateSection
2498 (
2499 IN CONST VOID * FileHeader,
2500 IN SIZE_T FileHeaderSize,
2501 IN PVOID File,
2502 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2503 OUT PULONG Flags,
2504 IN PEXEFMT_CB_READ_FILE ReadFileCb,
2505 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2506 );
2507
2508 /* TODO: this is a standard DDK/PSDK macro */
2509 #ifndef RTL_NUMBER_OF
2510 #define RTL_NUMBER_OF(ARR_) (sizeof(ARR_) / sizeof((ARR_)[0]))
2511 #endif
2512
2513 static PEXEFMT_LOADER ExeFmtpLoaders[] =
2514 {
2515 PeFmtCreateSection,
2516 ElfFmtCreateSection
2517 };
2518
2519 static
2520 PMM_SECTION_SEGMENT
2521 NTAPI
2522 ExeFmtpAllocateSegments(IN ULONG NrSegments)
2523 {
2524 SIZE_T SizeOfSegments;
2525 PMM_SECTION_SEGMENT Segments;
2526
2527 /* TODO: check for integer overflow */
2528 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * NrSegments;
2529
2530 Segments = ExAllocatePoolWithTag(NonPagedPool,
2531 SizeOfSegments,
2532 TAG_MM_SECTION_SEGMENT);
2533
2534 if(Segments)
2535 RtlZeroMemory(Segments, SizeOfSegments);
2536
2537 return Segments;
2538 }
2539
2540 static
2541 NTSTATUS
2542 NTAPI
2543 ExeFmtpReadFile(IN PVOID File,
2544 IN PLARGE_INTEGER Offset,
2545 IN ULONG Length,
2546 OUT PVOID * Data,
2547 OUT PVOID * AllocBase,
2548 OUT PULONG ReadSize)
2549 {
2550 NTSTATUS Status;
2551 LARGE_INTEGER FileOffset;
2552 ULONG AdjustOffset;
2553 ULONG OffsetAdjustment;
2554 ULONG BufferSize;
2555 ULONG UsedSize;
2556 PVOID Buffer;
2557
2558 ASSERT_IRQL_LESS(DISPATCH_LEVEL);
2559
2560 if(Length == 0)
2561 {
2562 KEBUGCHECK(STATUS_INVALID_PARAMETER_4);
2563 }
2564
2565 FileOffset = *Offset;
2566
2567 /* Negative/special offset: it cannot be used in this context */
2568 if(FileOffset.u.HighPart < 0)
2569 {
2570 KEBUGCHECK(STATUS_INVALID_PARAMETER_5);
2571 }
2572
2573 ASSERT(PAGE_SIZE <= MAXULONG);
2574 AdjustOffset = PAGE_ROUND_DOWN(FileOffset.u.LowPart);
2575 OffsetAdjustment = FileOffset.u.LowPart - AdjustOffset;
2576 FileOffset.u.LowPart = AdjustOffset;
2577
2578 BufferSize = Length + OffsetAdjustment;
2579 BufferSize = PAGE_ROUND_UP(BufferSize);
2580
2581 /*
2582 * It's ok to use paged pool, because this is a temporary buffer only used in
2583 * the loading of executables. The assumption is that MmCreateSection is
2584 * always called at low IRQLs and that these buffers don't survive a brief
2585 * initialization phase
2586 */
2587 Buffer = ExAllocatePoolWithTag(PagedPool,
2588 BufferSize,
2589 TAG('M', 'm', 'X', 'r'));
2590
2591 UsedSize = 0;
2592
2593 #if 0
2594 Status = MmspPageRead(File,
2595 Buffer,
2596 BufferSize,
2597 &FileOffset,
2598 &UsedSize);
2599 #else
2600 /*
2601 * FIXME: if we don't use ZwReadFile, caching is not enabled for the file and
2602 * nothing will work. But using ZwReadFile is wrong, and using its side effects
2603 * to initialize internal state is even worse. Our cache manager is in need of
2604 * professional help
2605 */
2606 {
2607 IO_STATUS_BLOCK Iosb;
2608
2609 Status = ZwReadFile(File,
2610 NULL,
2611 NULL,
2612 NULL,
2613 &Iosb,
2614 Buffer,
2615 BufferSize,
2616 &FileOffset,
2617 NULL);
2618
2619 if(NT_SUCCESS(Status))
2620 {
2621 UsedSize = Iosb.Information;
2622 }
2623 }
2624 #endif
2625
2626 if(NT_SUCCESS(Status) && UsedSize < OffsetAdjustment)
2627 {
2628 Status = STATUS_IN_PAGE_ERROR;
2629 ASSERT(!NT_SUCCESS(Status));
2630 }
2631
2632 if(NT_SUCCESS(Status))
2633 {
2634 *Data = (PVOID)((ULONG_PTR)Buffer + OffsetAdjustment);
2635 *AllocBase = Buffer;
2636 *ReadSize = UsedSize - OffsetAdjustment;
2637 }
2638 else
2639 {
2640 ExFreePool(Buffer);
2641 }
2642
2643 return Status;
2644 }
2645
2646 #ifdef NASSERT
2647 # define MmspAssertSegmentsSorted(OBJ_) ((void)0)
2648 # define MmspAssertSegmentsNoOverlap(OBJ_) ((void)0)
2649 # define MmspAssertSegmentsPageAligned(OBJ_) ((void)0)
2650 #else
2651 static
2652 VOID
2653 NTAPI
2654 MmspAssertSegmentsSorted(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2655 {
2656 ULONG i;
2657
2658 for( i = 1; i < ImageSectionObject->NrSegments; ++ i )
2659 {
2660 ASSERT(ImageSectionObject->Segments[i].VirtualAddress >=
2661 ImageSectionObject->Segments[i - 1].VirtualAddress);
2662 }
2663 }
2664
2665 static
2666 VOID
2667 NTAPI
2668 MmspAssertSegmentsNoOverlap(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2669 {
2670 ULONG i;
2671
2672 MmspAssertSegmentsSorted(ImageSectionObject);
2673
2674 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2675 {
2676 ASSERT(ImageSectionObject->Segments[i].Length > 0);
2677
2678 if(i > 0)
2679 {
2680 ASSERT(ImageSectionObject->Segments[i].VirtualAddress >=
2681 (ImageSectionObject->Segments[i - 1].VirtualAddress +
2682 ImageSectionObject->Segments[i - 1].Length));
2683 }
2684 }
2685 }
2686
2687 static
2688 VOID
2689 NTAPI
2690 MmspAssertSegmentsPageAligned(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2691 {
2692 ULONG i;
2693
2694 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2695 {
2696 ASSERT((ImageSectionObject->Segments[i].VirtualAddress % PAGE_SIZE) == 0);
2697 ASSERT((ImageSectionObject->Segments[i].Length % PAGE_SIZE) == 0);
2698 }
2699 }
2700 #endif
2701
2702 static
2703 int
2704 __cdecl
2705 MmspCompareSegments(const void * x,
2706 const void * y)
2707 {
2708 PMM_SECTION_SEGMENT Segment1 = (PMM_SECTION_SEGMENT)x;
2709 PMM_SECTION_SEGMENT Segment2 = (PMM_SECTION_SEGMENT)y;
2710
2711 return
2712 (Segment1->VirtualAddress - Segment2->VirtualAddress) >>
2713 ((sizeof(ULONG_PTR) - sizeof(int)) * 8);
2714 }
2715
2716 /*
2717 * Ensures an image section's segments are sorted in memory
2718 */
2719 static
2720 VOID
2721 NTAPI
2722 MmspSortSegments(IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2723 IN ULONG Flags)
2724 {
2725 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_SORTED)
2726 {
2727 MmspAssertSegmentsSorted(ImageSectionObject);
2728 }
2729 else
2730 {
2731 qsort(ImageSectionObject->Segments,
2732 ImageSectionObject->NrSegments,
2733 sizeof(ImageSectionObject->Segments[0]),
2734 MmspCompareSegments);
2735 }
2736 }
2737
2738
2739 /*
2740 * Ensures an image section's segments don't overlap in memory and don't have
2741 * gaps and don't have a null size. We let them map to overlapping file regions,
2742 * though - that's not necessarily an error
2743 */
2744 static
2745 BOOLEAN
2746 NTAPI
2747 MmspCheckSegmentBounds
2748 (
2749 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2750 IN ULONG Flags
2751 )
2752 {
2753 ULONG i;
2754
2755 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_NO_OVERLAP)
2756 {
2757 MmspAssertSegmentsNoOverlap(ImageSectionObject);
2758 return TRUE;
2759 }
2760
2761 ASSERT(ImageSectionObject->NrSegments >= 1);
2762
2763 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2764 {
2765 if(ImageSectionObject->Segments[i].Length == 0)
2766 {
2767 return FALSE;
2768 }
2769
2770 if(i > 0)
2771 {
2772 /*
2773 * TODO: relax the limitation on gaps. For example, gaps smaller than a
2774 * page could be OK (Windows seems to be OK with them), and larger gaps
2775 * could lead to image sections spanning several discontiguous regions
2776 * (NtMapViewOfSection could then refuse to map them, and they could
2777 * e.g. only be allowed as parameters to NtCreateProcess, like on UNIX)
2778 */
2779 if ((ImageSectionObject->Segments[i - 1].VirtualAddress +
2780 ImageSectionObject->Segments[i - 1].Length) !=
2781 ImageSectionObject->Segments[i].VirtualAddress)
2782 {
2783 return FALSE;
2784 }
2785 }
2786 }
2787
2788 return TRUE;
2789 }
2790
2791 /*
2792 * Merges and pads an image section's segments until they all are page-aligned
2793 * and have a size that is a multiple of the page size
2794 */
2795 static
2796 BOOLEAN
2797 NTAPI
2798 MmspPageAlignSegments
2799 (
2800 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2801 IN ULONG Flags
2802 )
2803 {
2804 ULONG i;
2805 ULONG LastSegment;
2806 BOOLEAN Initialized;
2807
2808 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED)
2809 {
2810 MmspAssertSegmentsPageAligned(ImageSectionObject);
2811 return TRUE;
2812 }
2813
2814 Initialized = FALSE;
2815 LastSegment = 0;
2816
2817 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2818 {
2819 PMM_SECTION_SEGMENT EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2820
2821 /*
2822 * The first segment requires special handling
2823 */
2824 if (i == 0)
2825 {
2826 ULONG_PTR VirtualAddress;
2827 ULONG_PTR VirtualOffset;
2828
2829 VirtualAddress = EffectiveSegment->VirtualAddress;
2830
2831 /* Round down the virtual address to the nearest page */
2832 EffectiveSegment->VirtualAddress = PAGE_ROUND_DOWN(VirtualAddress);
2833
2834 /* Round up the virtual size to the nearest page */
2835 EffectiveSegment->Length = PAGE_ROUND_UP(VirtualAddress + EffectiveSegment->Length) -
2836 EffectiveSegment->VirtualAddress;
2837
2838 /* Adjust the raw address and size */
2839 VirtualOffset = VirtualAddress - EffectiveSegment->VirtualAddress;
2840
2841 if (EffectiveSegment->FileOffset < VirtualOffset)
2842 {
2843 return FALSE;
2844 }
2845
2846 /*
2847 * Garbage in, garbage out: unaligned base addresses make the file
2848 * offset point in curious and odd places, but that's what we were
2849 * asked for
2850 */
2851 EffectiveSegment->FileOffset -= VirtualOffset;
2852 EffectiveSegment->RawLength += VirtualOffset;
2853 }
2854 else
2855 {
2856 PMM_SECTION_SEGMENT Segment = &ImageSectionObject->Segments[i];
2857 ULONG_PTR EndOfEffectiveSegment;
2858
2859 EndOfEffectiveSegment = EffectiveSegment->VirtualAddress + EffectiveSegment->Length;
2860 ASSERT((EndOfEffectiveSegment % PAGE_SIZE) == 0);
2861
2862 /*
2863 * The current segment begins exactly where the current effective
2864 * segment ended, therefore beginning a new effective segment
2865 */
2866 if (EndOfEffectiveSegment == Segment->VirtualAddress)
2867 {
2868 LastSegment ++;
2869 ASSERT(LastSegment <= i);
2870 ASSERT(LastSegment < ImageSectionObject->NrSegments);
2871
2872 EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2873
2874 /*
2875 * Copy the current segment. If necessary, the effective segment
2876 * will be expanded later
2877 */
2878 *EffectiveSegment = *Segment;
2879
2880 /*
2881 * Page-align the virtual size. We know for sure the virtual address
2882 * already is
2883 */
2884 ASSERT((EffectiveSegment->VirtualAddress % PAGE_SIZE) == 0);
2885 EffectiveSegment->Length = PAGE_ROUND_UP(EffectiveSegment->Length);
2886 }
2887 /*
2888 * The current segment is still part of the current effective segment:
2889 * extend the effective segment to reflect this
2890 */
2891 else if (EndOfEffectiveSegment > Segment->VirtualAddress)
2892 {
2893 static const ULONG FlagsToProtection[16] =
2894 {
2895 PAGE_NOACCESS,
2896 PAGE_READONLY,
2897 PAGE_READWRITE,
2898 PAGE_READWRITE,
2899 PAGE_EXECUTE_READ,
2900 PAGE_EXECUTE_READ,
2901 PAGE_EXECUTE_READWRITE,
2902 PAGE_EXECUTE_READWRITE,
2903 PAGE_WRITECOPY,
2904 PAGE_WRITECOPY,
2905 PAGE_WRITECOPY,
2906 PAGE_WRITECOPY,
2907 PAGE_EXECUTE_WRITECOPY,
2908 PAGE_EXECUTE_WRITECOPY,
2909 PAGE_EXECUTE_WRITECOPY,
2910 PAGE_EXECUTE_WRITECOPY
2911 };
2912
2913 unsigned ProtectionFlags;
2914
2915 /*
2916 * Extend the file size
2917 */
2918
2919 /* Unaligned segments must be contiguous within the file */
2920 if (Segment->FileOffset != (EffectiveSegment->FileOffset +
2921 EffectiveSegment->RawLength))
2922 {
2923 return FALSE;
2924 }
2925
2926 EffectiveSegment->RawLength += Segment->RawLength;
2927
2928 /*
2929 * Extend the virtual size
2930 */
2931 ASSERT(PAGE_ROUND_UP(Segment->VirtualAddress + Segment->Length) > EndOfEffectiveSegment);
2932
2933 EffectiveSegment->Length = PAGE_ROUND_UP(Segment->VirtualAddress + Segment->Length) -
2934 EffectiveSegment->VirtualAddress;
2935
2936 /*
2937 * Merge the protection
2938 */
2939 EffectiveSegment->Protection |= Segment->Protection;
2940
2941 /* Clean up redundance */
2942 ProtectionFlags = 0;
2943
2944 if(EffectiveSegment->Protection & PAGE_IS_READABLE)
2945 ProtectionFlags |= 1 << 0;
2946
2947 if(EffectiveSegment->Protection & PAGE_IS_WRITABLE)
2948 ProtectionFlags |= 1 << 1;
2949
2950 if(EffectiveSegment->Protection & PAGE_IS_EXECUTABLE)
2951 ProtectionFlags |= 1 << 2;
2952
2953 if(EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
2954 ProtectionFlags |= 1 << 3;
2955
2956 ASSERT(ProtectionFlags < 16);
2957 EffectiveSegment->Protection = FlagsToProtection[ProtectionFlags];
2958
2959 /* If a segment was required to be shared and cannot, fail */
2960 if(!(Segment->Protection & PAGE_IS_WRITECOPY) &&
2961 EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
2962 {
2963 return FALSE;
2964 }
2965 }
2966 /*
2967 * We assume no holes between segments at this point
2968 */
2969 else
2970 {
2971 ASSERT(FALSE);
2972 }
2973 }
2974 }
2975
2976 return TRUE;
2977 }
2978
2979 NTSTATUS
2980 ExeFmtpCreateImageSection(HANDLE FileHandle,
2981 PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2982 {
2983 LARGE_INTEGER Offset;
2984 PVOID FileHeader;
2985 PVOID FileHeaderBuffer;
2986 ULONG FileHeaderSize;
2987 ULONG Flags;
2988 ULONG OldNrSegments;
2989 NTSTATUS Status;
2990 ULONG i;
2991
2992 /*
2993 * Read the beginning of the file (2 pages). Should be enough to contain
2994 * all (or most) of the headers
2995 */
2996 Offset.QuadPart = 0;
2997
2998 /* FIXME: use FileObject instead of FileHandle */
2999 Status = ExeFmtpReadFile (FileHandle,
3000 &Offset,
3001 PAGE_SIZE * 2,
3002 &FileHeader,
3003 &FileHeaderBuffer,
3004 &FileHeaderSize);
3005
3006 if (!NT_SUCCESS(Status))
3007 return Status;
3008
3009 if (FileHeaderSize == 0)
3010 {
3011 ExFreePool(FileHeaderBuffer);
3012 return STATUS_UNSUCCESSFUL;
3013 }
3014
3015 /*
3016 * Look for a loader that can handle this executable
3017 */
3018 for (i = 0; i < RTL_NUMBER_OF(ExeFmtpLoaders); ++ i)
3019 {
3020 RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject));
3021 Flags = 0;
3022
3023 /* FIXME: use FileObject instead of FileHandle */
3024 Status = ExeFmtpLoaders[i](FileHeader,
3025 FileHeaderSize,
3026 FileHandle,
3027 ImageSectionObject,
3028 &Flags,
3029 ExeFmtpReadFile,
3030 ExeFmtpAllocateSegments);
3031
3032 if (!NT_SUCCESS(Status))
3033 {
3034 if (ImageSectionObject->Segments)
3035 {
3036 ExFreePool(ImageSectionObject->Segments);
3037 ImageSectionObject->Segments = NULL;
3038 }
3039 }
3040
3041 if (Status != STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
3042 break;
3043 }
3044
3045 ExFreePool(FileHeaderBuffer);
3046
3047 /*
3048 * No loader handled the format
3049 */
3050 if (Status == STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
3051 {
3052 Status = STATUS_INVALID_IMAGE_FORMAT;
3053 ASSERT(!NT_SUCCESS(Status));
3054 }
3055
3056 if (!NT_SUCCESS(Status))
3057 return Status;
3058
3059 ASSERT(ImageSectionObject->Segments != NULL);
3060
3061 /*
3062 * Some defaults
3063 */
3064 /* FIXME? are these values platform-dependent? */
3065 if(ImageSectionObject->StackReserve == 0)
3066 ImageSectionObject->StackReserve = 0x40000;
3067
3068 if(ImageSectionObject->StackCommit == 0)
3069 ImageSectionObject->StackCommit = 0x1000;
3070
3071 if(ImageSectionObject->ImageBase == 0)
3072 {
3073 if(ImageSectionObject->ImageCharacteristics & IMAGE_FILE_DLL)
3074 ImageSectionObject->ImageBase = 0x10000000;
3075 else
3076 ImageSectionObject->ImageBase = 0x00400000;
3077 }
3078
3079 /*
3080 * And now the fun part: fixing the segments
3081 */
3082
3083 /* Sort them by virtual address */
3084 MmspSortSegments(ImageSectionObject, Flags);
3085
3086 /* Ensure they don't overlap in memory */
3087 if (!MmspCheckSegmentBounds(ImageSectionObject, Flags))
3088 return STATUS_INVALID_IMAGE_FORMAT;
3089
3090 /* Ensure they are aligned */
3091 OldNrSegments = ImageSectionObject->NrSegments;
3092
3093 if (!MmspPageAlignSegments(ImageSectionObject, Flags))
3094 return STATUS_INVALID_IMAGE_FORMAT;
3095
3096 /* Trim them if the alignment phase merged some of them */
3097 if (ImageSectionObject->NrSegments < OldNrSegments)
3098 {
3099 PMM_SECTION_SEGMENT Segments;
3100 SIZE_T SizeOfSegments;
3101
3102 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * ImageSectionObject->NrSegments;
3103
3104 Segments = ExAllocatePoolWithTag(NonPagedPool,
3105 SizeOfSegments,
3106 TAG_MM_SECTION_SEGMENT);
3107
3108 if (Segments == NULL)
3109 return STATUS_INSUFFICIENT_RESOURCES;
3110
3111 RtlCopyMemory(Segments, ImageSectionObject->Segments, SizeOfSegments);
3112 ExFreePool(ImageSectionObject->Segments);
3113 ImageSectionObject->Segments = Segments;
3114 }
3115
3116 /* And finish their initialization */
3117 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3118 {
3119 ExInitializeFastMutex(&ImageSectionObject->Segments[i].Lock);
3120 ImageSectionObject->Segments[i].ReferenceCount = 1;
3121
3122 RtlZeroMemory(&ImageSectionObject->Segments[i].PageDirectory,
3123 sizeof(ImageSectionObject->Segments[i].PageDirectory));
3124 }
3125
3126 ASSERT(NT_SUCCESS(Status));
3127 return Status;
3128 }
3129
3130 NTSTATUS
3131 MmCreateImageSection(PSECTION_OBJECT *SectionObject,
3132 ACCESS_MASK DesiredAccess,
3133 POBJECT_ATTRIBUTES ObjectAttributes,
3134 PLARGE_INTEGER UMaximumSize,
3135 ULONG SectionPageProtection,
3136 ULONG AllocationAttributes,
3137 HANDLE FileHandle)
3138 {
3139 PSECTION_OBJECT Section;
3140 NTSTATUS Status;
3141 PFILE_OBJECT FileObject;
3142 PMM_SECTION_SEGMENT SectionSegments;
3143 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3144 ULONG i;
3145 ULONG FileAccess = 0;
3146
3147 /*
3148 * Specifying a maximum size is meaningless for an image section
3149 */
3150 if (UMaximumSize != NULL)
3151 {
3152 return(STATUS_INVALID_PARAMETER_4);
3153 }
3154
3155 /*
3156 * Check file access required
3157 */
3158 if (SectionPageProtection & PAGE_READWRITE ||
3159 SectionPageProtection & PAGE_EXECUTE_READWRITE)
3160 {
3161 FileAccess = FILE_READ_DATA | FILE_WRITE_DATA;
3162 }
3163 else
3164 {
3165 FileAccess = FILE_READ_DATA;
3166 }
3167
3168 /*
3169 * Reference the file handle
3170 */
3171 Status = ObReferenceObjectByHandle(FileHandle,
3172 FileAccess,
3173 IoFileObjectType,
3174 UserMode,
3175 (PVOID*)(PVOID)&FileObject,
3176 NULL);
3177 if (!NT_SUCCESS(Status))
3178 {
3179 return Status;
3180 }
3181
3182 /*
3183 * Create the section
3184 */
3185 Status = ObCreateObject (ExGetPreviousMode(),
3186 MmSectionObjectType,
3187 ObjectAttributes,
3188 ExGetPreviousMode(),
3189 NULL,
3190 sizeof(SECTION_OBJECT),
3191 0,
3192 0,
3193 (PVOID*)(PVOID)&Section);
3194 if (!NT_SUCCESS(Status))
3195 {
3196 ObDereferenceObject(FileObject);
3197 return(Status);
3198 }
3199
3200 /*
3201 * Initialize it
3202 */
3203 Section->SectionPageProtection = SectionPageProtection;
3204 Section->AllocationAttributes = AllocationAttributes;
3205 InitializeListHead(&Section->ViewListHead);
3206 KeInitializeSpinLock(&Section->ViewListLock);
3207
3208 /*
3209 * Initialized caching for this file object if previously caching
3210 * was initialized for the same on disk file
3211 */
3212 Status = CcTryToInitializeFileCache(FileObject);
3213
3214 if (!NT_SUCCESS(Status) || FileObject->SectionObjectPointer->ImageSectionObject == NULL)
3215 {
3216 NTSTATUS StatusExeFmt;
3217
3218 ImageSectionObject = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_IMAGE_SECTION_OBJECT), TAG_MM_SECTION_SEGMENT);
3219 if (ImageSectionObject == NULL)
3220 {
3221 ObDereferenceObject(FileObject);
3222 ObDereferenceObject(Section);
3223 return(STATUS_NO_MEMORY);
3224 }
3225
3226 StatusExeFmt = ExeFmtpCreateImageSection(FileHandle, ImageSectionObject);
3227
3228 if (!NT_SUCCESS(StatusExeFmt))
3229 {
3230 if(ImageSectionObject->Segments != NULL)
3231 ExFreePool(ImageSectionObject->Segments);
3232
3233 ExFreePool(ImageSectionObject);
3234 ObDereferenceObject(Section);
3235 ObDereferenceObject(FileObject);
3236 return(StatusExeFmt);
3237 }
3238
3239 Section->ImageSection = ImageSectionObject;
3240 ASSERT(ImageSectionObject->Segments);
3241
3242 /*
3243 * Lock the file
3244 */
3245 Status = MmspWaitForFileLock(FileObject);
3246 if (!NT_SUCCESS(Status))
3247 {
3248 ExFreePool(ImageSectionObject->Segments);
3249 ExFreePool(ImageSectionObject);
3250 ObDereferenceObject(Section);
3251 ObDereferenceObject(FileObject);
3252 return(Status);
3253 }
3254
3255 if (NULL != InterlockedCompareExchangePointer(&FileObject->SectionObjectPointer->ImageSectionObject,
3256 ImageSectionObject, NULL))
3257 {
3258 /*
3259 * An other thread has initialized the some image in the background
3260 */
3261 ExFreePool(ImageSectionObject->Segments);
3262 ExFreePool(ImageSectionObject);
3263 ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3264 Section->ImageSection = ImageSectionObject;
3265 SectionSegments = ImageSectionObject->Segments;
3266
3267 for (i = 0; i < ImageSectionObject->NrSegments; i++)
3268 {
3269 InterlockedIncrementUL(&SectionSegments[i].ReferenceCount);
3270 }
3271 }
3272
3273 Status = StatusExeFmt;
3274 }
3275 else
3276 {
3277 /*
3278 * Lock the file
3279 */
3280 Status = MmspWaitForFileLock(FileObject);
3281 if (Status != STATUS_SUCCESS)
3282 {
3283 ObDereferenceObject(Section);
3284 ObDereferenceObject(FileObject);
3285 return(Status);
3286 }
3287
3288 ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3289 Section->ImageSection = ImageSectionObject;
3290 SectionSegments = ImageSectionObject->Segments;
3291
3292 /*
3293 * Otherwise just reference all the section segments
3294 */
3295 for (i = 0; i < ImageSectionObject->NrSegments; i++)
3296 {
3297 InterlockedIncrementUL(&SectionSegments[i].ReferenceCount);
3298 }
3299
3300 Status = STATUS_SUCCESS;
3301 }
3302 Section->FileObject = FileObject;
3303 CcRosReferenceCache(FileObject);
3304 KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3305 *SectionObject = Section;
3306 return(Status);
3307 }
3308
3309 /*
3310 * @implemented
3311 */
3312 NTSTATUS STDCALL
3313 NtCreateSection (OUT PHANDLE SectionHandle,
3314 IN ACCESS_MASK DesiredAccess,
3315 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
3316 IN PLARGE_INTEGER MaximumSize OPTIONAL,
3317 IN ULONG SectionPageProtection OPTIONAL,
3318 IN ULONG AllocationAttributes,
3319 IN HANDLE FileHandle OPTIONAL)
3320 {
3321 LARGE_INTEGER SafeMaximumSize;
3322 PSECTION_OBJECT SectionObject;
3323 KPROCESSOR_MODE PreviousMode;
3324 NTSTATUS Status = STATUS_SUCCESS;
3325
3326 PreviousMode = ExGetPreviousMode();
3327
3328 if(MaximumSize != NULL && PreviousMode != KernelMode)
3329 {
3330 _SEH_TRY
3331 {
3332 ProbeForRead(MaximumSize,
3333 sizeof(LARGE_INTEGER),
3334 sizeof(ULONG));
3335 /* make a copy on the stack */
3336 SafeMaximumSize = *MaximumSize;
3337 MaximumSize = &SafeMaximumSize;
3338 }
3339 _SEH_HANDLE
3340 {
3341 Status = _SEH_GetExceptionCode();
3342 }
3343 _SEH_END;
3344
3345 if(!NT_SUCCESS(Status))
3346 {
3347 return Status;
3348 }
3349 }
3350
3351 /*
3352 * Check the protection
3353 */
3354 if ((SectionPageProtection & PAGE_FLAGS_VALID_FROM_USER_MODE) !=
3355 SectionPageProtection)
3356 {
3357 return(STATUS_INVALID_PAGE_PROTECTION);
3358 }
3359
3360 Status = MmCreateSection(&SectionObject,
3361 DesiredAccess,
3362 ObjectAttributes,
3363 MaximumSize,
3364 SectionPageProtection,
3365 AllocationAttributes,
3366 FileHandle,
3367 NULL);
3368
3369 if (NT_SUCCESS(Status))
3370 {
3371 Status = ObInsertObject ((PVOID)SectionObject,
3372 NULL,
3373 DesiredAccess,
3374 0,
3375 NULL,
3376 SectionHandle);
3377 ObDereferenceObject(SectionObject);
3378 }
3379
3380 return Status;
3381 }
3382
3383
3384 /**********************************************************************
3385 * NAME
3386 * NtOpenSection
3387 *
3388 * DESCRIPTION
3389 *
3390 * ARGUMENTS
3391 * SectionHandle
3392 *
3393 * DesiredAccess
3394 *
3395 * ObjectAttributes
3396 *
3397 * RETURN VALUE
3398 *
3399 * REVISIONS
3400 */
3401 NTSTATUS STDCALL
3402 NtOpenSection(PHANDLE SectionHandle,
3403 ACCESS_MASK DesiredAccess,
3404 POBJECT_ATTRIBUTES ObjectAttributes)
3405 {
3406 HANDLE hSection;
3407 KPROCESSOR_MODE PreviousMode;
3408 NTSTATUS Status = STATUS_SUCCESS;
3409
3410 PreviousMode = ExGetPreviousMode();
3411
3412 if(PreviousMode != KernelMode)
3413 {
3414 _SEH_TRY
3415 {
3416 ProbeForWrite(SectionHandle,
3417 sizeof(HANDLE),
3418 sizeof(ULONG));
3419 }
3420 _SEH_HANDLE
3421 {
3422 Status = _SEH_GetExceptionCode();
3423 }
3424 _SEH_END;
3425
3426 if(!NT_SUCCESS(Status))
3427 {
3428 return Status;
3429 }
3430 }
3431
3432 Status = ObOpenObjectByName(ObjectAttributes,
3433 MmSectionObjectType,
3434 NULL,
3435 PreviousMode,
3436 DesiredAccess,
3437 NULL,
3438 &hSection);
3439
3440 if(NT_SUCCESS(Status))
3441 {
3442 _SEH_TRY
3443 {
3444 *SectionHandle = hSection;
3445 }
3446 _SEH_HANDLE
3447 {
3448 Status = _SEH_GetExceptionCode();
3449 }
3450 _SEH_END;
3451 }
3452
3453 return(Status);
3454 }
3455
3456 NTSTATUS STATIC
3457 MmMapViewOfSegment(PEPROCESS Process,
3458 PMADDRESS_SPACE AddressSpace,
3459 PSECTION_OBJECT Section,
3460 PMM_SECTION_SEGMENT Segment,
3461 PVOID* BaseAddress,
3462 ULONG ViewSize,
3463 ULONG Protect,
3464 ULONG ViewOffset,
3465 BOOL TopDown)
3466 {
3467 PMEMORY_AREA MArea;
3468 NTSTATUS Status;
3469 KIRQL oldIrql;
3470 PHYSICAL_ADDRESS BoundaryAddressMultiple;
3471
3472 BoundaryAddressMultiple.QuadPart = 0;
3473
3474 Status = MmCreateMemoryArea(Process,
3475 AddressSpace,
3476 MEMORY_AREA_SECTION_VIEW,
3477 BaseAddress,
3478 ViewSize,
3479 Protect,
3480 &MArea,
3481 FALSE,
3482 TopDown,
3483 BoundaryAddressMultiple);
3484 if (!NT_SUCCESS(Status))
3485 {
3486 DPRINT1("Mapping between 0x%.8X and 0x%.8X failed (%X).\n",
3487 (*BaseAddress), (char*)(*BaseAddress) + ViewSize, Status);
3488 return(Status);
3489 }
3490
3491 KeAcquireSpinLock(&Section->ViewListLock, &oldIrql);
3492 InsertTailList(&Section->ViewListHead,
3493 &MArea->Data.SectionData.ViewListEntry);
3494 KeReleaseSpinLock(&Section->ViewListLock, oldIrql);
3495
3496 ObReferenceObjectByPointer((PVOID)Section,
3497 SECTION_MAP_READ,
3498 NULL,
3499 ExGetPreviousMode());
3500 MArea->Data.SectionData.Segment = Segment;
3501 MArea->Data.SectionData.Section = Section;
3502 MArea->Data.SectionData.ViewOffset = ViewOffset;
3503 MArea->Data.SectionData.WriteCopyView = FALSE;
3504 MmInitialiseRegion(&MArea->Data.SectionData.RegionListHead,
3505 ViewSize, 0, Protect);
3506
3507 return(STATUS_SUCCESS);
3508 }
3509
3510
3511 /**********************************************************************
3512 * NAME EXPORTED
3513 * NtMapViewOfSection
3514 *
3515 * DESCRIPTION
3516 * Maps a view of a section into the virtual address space of a
3517 * process.
3518 *
3519 * ARGUMENTS
3520 * SectionHandle
3521 * Handle of the section.
3522 *
3523 * ProcessHandle
3524 * Handle of the process.
3525 *
3526 * BaseAddress
3527 * Desired base address (or NULL) on entry;
3528 * Actual base address of the view on exit.
3529 *
3530 * ZeroBits
3531 * Number of high order address bits that must be zero.
3532 *
3533 * CommitSize
3534 * Size in bytes of the initially committed section of
3535 * the view.
3536 *
3537 * SectionOffset
3538 * Offset in bytes from the beginning of the section
3539 * to the beginning of the view.
3540 *
3541 * ViewSize
3542 * Desired length of map (or zero to map all) on entry
3543 * Actual length mapped on exit.
3544 *
3545 * InheritDisposition
3546 * Specified how the view is to be shared with
3547 * child processes.
3548 *
3549 * AllocateType
3550 * Type of allocation for the pages.
3551 *
3552 * Protect
3553 * Protection for the committed region of the view.
3554 *
3555 * RETURN VALUE
3556 * Status.
3557 *
3558 * @implemented
3559 */
3560 NTSTATUS STDCALL
3561 NtMapViewOfSection(IN HANDLE SectionHandle,
3562 IN HANDLE ProcessHandle,
3563 IN OUT PVOID* BaseAddress OPTIONAL,
3564 IN ULONG ZeroBits OPTIONAL,
3565 IN ULONG CommitSize,
3566 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
3567 IN OUT PULONG ViewSize,
3568 IN SECTION_INHERIT InheritDisposition,
3569 IN ULONG AllocationType OPTIONAL,
3570 IN ULONG Protect)
3571 {
3572 PVOID SafeBaseAddress;
3573 LARGE_INTEGER SafeSectionOffset;
3574 ULONG SafeViewSize;
3575 PSECTION_OBJECT Section;
3576 PEPROCESS Process;
3577 KPROCESSOR_MODE PreviousMode;
3578 PMADDRESS_SPACE AddressSpace;
3579 NTSTATUS Status = STATUS_SUCCESS;
3580
3581 PreviousMode = ExGetPreviousMode();
3582
3583 if(PreviousMode != KernelMode)
3584 {
3585 SafeBaseAddress = NULL;
3586 SafeSectionOffset.QuadPart = 0;
3587 SafeViewSize = 0;
3588
3589 _SEH_TRY
3590 {
3591 if(BaseAddress != NULL)
3592 {
3593 ProbeForWrite(BaseAddress,
3594 sizeof(PVOID),
3595 sizeof(ULONG));
3596 SafeBaseAddress = *BaseAddress;
3597 }
3598 if(SectionOffset != NULL)
3599 {
3600 ProbeForWrite(SectionOffset,
3601 sizeof(LARGE_INTEGER),
3602 sizeof(ULONG));
3603 SafeSectionOffset = *SectionOffset;
3604 }
3605 ProbeForWrite(ViewSize,
3606 sizeof(ULONG),
3607 sizeof(ULONG));
3608 SafeViewSize = *ViewSize;
3609 }
3610 _SEH_HANDLE
3611 {
3612 Status = _SEH_GetExceptionCode();
3613 }
3614 _SEH_END;
3615
3616 if(!NT_SUCCESS(Status))
3617 {
3618 return Status;
3619 }
3620 }
3621 else
3622 {
3623 SafeBaseAddress = (BaseAddress != NULL ? *BaseAddress : NULL);
3624 SafeSectionOffset.QuadPart = (SectionOffset != NULL ? SectionOffset->QuadPart : 0);
3625 SafeViewSize = (ViewSize != NULL ? *ViewSize : 0);
3626 }
3627
3628 Status = ObReferenceObjectByHandle(ProcessHandle,
3629 PROCESS_VM_OPERATION,
3630 PsProcessType,
3631 PreviousMode,
3632 (PVOID*)(PVOID)&Process,
3633 NULL);
3634 if (!NT_SUCCESS(Status))
3635 {
3636 return(Status);
3637 }
3638
3639 AddressSpace = &Process->AddressSpace;
3640
3641 Status = ObReferenceObjectByHandle(SectionHandle,
3642 SECTION_MAP_READ,
3643 MmSectionObjectType,
3644 PreviousMode,
3645 (PVOID*)(PVOID)&Section,
3646 NULL);
3647 if (!(NT_SUCCESS(Status)))
3648 {
3649 DPRINT("ObReference failed rc=%x\n",Status);
3650 ObDereferenceObject(Process);
3651 return(Status);
3652 }
3653
3654 Status = MmMapViewOfSection(Section,
3655 Process,
3656 (BaseAddress != NULL ? &SafeBaseAddress : NULL),
3657 ZeroBits,
3658 CommitSize,
3659 (SectionOffset != NULL ? &SafeSectionOffset : NULL),
3660 (ViewSize != NULL ? &SafeViewSize : NULL),
3661 InheritDisposition,
3662 AllocationType,
3663 Protect);
3664
3665 ObDereferenceObject(Section);
3666 ObDereferenceObject(Process);
3667
3668 if(NT_SUCCESS(Status))
3669 {
3670 /* copy parameters back to the caller */
3671 _SEH_TRY
3672 {
3673 if(BaseAddress != NULL)
3674 {
3675 *BaseAddress = SafeBaseAddress;
3676 }
3677 if(SectionOffset != NULL)
3678 {
3679 *SectionOffset = SafeSectionOffset;
3680 }
3681 if(ViewSize != NULL)
3682 {
3683 *ViewSize = SafeViewSize;
3684 }
3685 }
3686 _SEH_HANDLE
3687 {
3688 Status = _SEH_GetExceptionCode();
3689 }
3690 _SEH_END;
3691 }
3692
3693 return(Status);
3694 }
3695
3696 VOID STATIC
3697 MmFreeSectionPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
3698 PFN_TYPE Page, SWAPENTRY SwapEntry, BOOLEAN Dirty)
3699 {
3700 PMEMORY_AREA MArea;
3701 ULONG Entry;
3702 PFILE_OBJECT FileObject;
3703 PBCB Bcb;
3704 ULONG Offset;
3705 SWAPENTRY SavedSwapEntry;
3706 PMM_PAGEOP PageOp;
3707 NTSTATUS Status;
3708 PSECTION_OBJECT Section;
3709 PMM_SECTION_SEGMENT Segment;
3710
3711 MArea = (PMEMORY_AREA)Context;
3712
3713 Address = (PVOID)PAGE_ROUND_DOWN(Address);
3714
3715 Offset = ((ULONG_PTR)Address - (ULONG_PTR)MArea->StartingAddress) +
3716 MemoryArea->Data.SectionData.ViewOffset;
3717
3718 Section = MArea->Data.SectionData.Section;
3719 Segment = MArea->Data.SectionData.Segment;
3720
3721 PageOp = MmCheckForPageOp(MArea, NULL, NULL, Segment, Offset);
3722
3723 while (PageOp)
3724 {
3725 MmUnlockSectionSegment(Segment);
3726 MmUnlockAddressSpace(&MArea->Process->AddressSpace);
3727
3728 Status = MmspWaitForPageOpCompletionEvent(PageOp);
3729 if (Status != STATUS_SUCCESS)
3730 {
3731 DPRINT1("Failed to wait for page op, status = %x\n", Status);
3732 KEBUGCHECK(0);
3733 }
3734
3735 MmLockAddressSpace(&MArea->Process->AddressSpace);
3736 MmLockSectionSegment(Segment);
3737 MmspCompleteAndReleasePageOp(PageOp);
3738 PageOp = MmCheckForPageOp(MArea, NULL, NULL, Segment, Offset);
3739 }
3740
3741 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
3742
3743 /*
3744 * For a dirty, datafile, non-private page mark it as dirty in the
3745 * cache manager.
3746 */
3747 if (Segment->Flags & MM_DATAFILE_SEGMENT)
3748 {
3749 if (Page == PFN_FROM_SSE(Entry) && Dirty)
3750 {
3751 FileObject = MemoryArea->Data.SectionData.Section->FileObject;
3752 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
3753 CcRosMarkDirtyCacheSegment(Bcb, Offset);
3754 ASSERT(SwapEntry == 0);
3755 }
3756 }
3757
3758 if (SwapEntry != 0)
3759 {
3760 /*
3761 * Sanity check
3762 */
3763 if (Segment->Flags & MM_PAGEFILE_SEGMENT)
3764 {
3765 DPRINT1("Found a swap entry for a page in a pagefile section.\n");
3766 KEBUGCHECK(0);
3767 }
3768 MmFreeSwapPage(SwapEntry);
3769 }
3770 else if (Page != 0)
3771 {
3772 if (IS_SWAP_FROM_SSE(Entry) ||
3773 Page != PFN_FROM_SSE(Entry))
3774 {
3775 /*
3776 * Sanity check
3777 */
3778 if (Segment->Flags & MM_PAGEFILE_SEGMENT)
3779 {
3780 DPRINT1("Found a private page in a pagefile section.\n");
3781 KEBUGCHECK(0);
3782 }
3783 /*
3784 * Just dereference private pages
3785 */
3786 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
3787 if (SavedSwapEntry != 0)
3788 {
3789 MmFreeSwapPage(SavedSwapEntry);
3790 MmSetSavedSwapEntryPage(Page, 0);
3791 }
3792 MmDeleteRmap(Page, MArea->Process, Address);
3793 MmReleasePageMemoryConsumer(MC_USER, Page);
3794 }
3795 else
3796 {
3797 MmDeleteRmap(Page, MArea->Process, Address);
3798 MmUnsharePageEntrySectionSegment(Section, Segment, Offset, Dirty, FALSE);
3799 }
3800 }
3801 }
3802
3803 NTSTATUS
3804 MmUnmapViewOfSegment(PMADDRESS_SPACE AddressSpace,
3805 PVOID BaseAddress)
3806 {
3807 NTSTATUS Status;
3808 PMEMORY_AREA MemoryArea;
3809 PSECTION_OBJECT Section;
3810 PMM_SECTION_SEGMENT Segment;
3811 KIRQL oldIrql;
3812 PLIST_ENTRY CurrentEntry;
3813 PMM_REGION CurrentRegion;
3814 PLIST_ENTRY RegionListHead;
3815
3816 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace,
3817 BaseAddress);
3818 if (MemoryArea == NULL)
3819 {
3820 return(STATUS_UNSUCCESSFUL);
3821 }
3822
3823 MemoryArea->DeleteInProgress = TRUE;
3824 Section = MemoryArea->Data.SectionData.Section;
3825 Segment = MemoryArea->Data.SectionData.Segment;
3826
3827 MmLockSectionSegment(Segment);
3828 KeAcquireSpinLock(&Section->ViewListLock, &oldIrql);
3829 RemoveEntryList(&MemoryArea->Data.SectionData.ViewListEntry);
3830 KeReleaseSpinLock(&Section->ViewListLock, oldIrql);
3831
3832 RegionListHead = &MemoryArea->Data.SectionData.RegionListHead;
3833 while (!IsListEmpty(RegionListHead))
3834 {
3835 CurrentEntry = RemoveHeadList(RegionListHead);
3836 CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION, RegionListEntry);
3837 ExFreePool(CurrentRegion);
3838 }
3839
3840 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
3841 {
3842 Status = MmFreeMemoryArea(AddressSpace,
3843 MemoryArea,
3844 NULL,
3845 NULL);
3846 }
3847 else
3848 {
3849 Status = MmFreeMemoryArea(AddressSpace,
3850 MemoryArea,
3851 MmFreeSectionPage,
3852 MemoryArea);
3853 }
3854 MmUnlockSectionSegment(Segment);
3855 ObDereferenceObject(Section);
3856 return(STATUS_SUCCESS);
3857 }
3858
3859 /*
3860 * @implemented
3861 */
3862 NTSTATUS STDCALL
3863 MmUnmapViewOfSection(PEPROCESS Process,
3864 PVOID BaseAddress)
3865 {
3866 NTSTATUS Status;
3867 PMEMORY_AREA MemoryArea;
3868 PMADDRESS_SPACE AddressSpace;
3869 PSECTION_OBJECT Section;
3870
3871 DPRINT("Opening memory area Process %x BaseAddress %x\n",
3872 Process, BaseAddress);
3873
3874 ASSERT(Process);
3875
3876 AddressSpace = &Process->AddressSpace;
3877 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace,
3878 BaseAddress);
3879 if (MemoryArea == NULL ||
3880 MemoryArea->Type != MEMORY_AREA_SECTION_VIEW ||
3881 MemoryArea->DeleteInProgress)
3882 {
3883 return STATUS_NOT_MAPPED_VIEW;
3884 }
3885
3886 Section = MemoryArea->Data.SectionData.Section;
3887
3888 if (Section->AllocationAttributes & SEC_IMAGE)
3889 {
3890 ULONG i;
3891 ULONG NrSegments;
3892 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3893 PMM_SECTION_SEGMENT SectionSegments;
3894 PVOID ImageBaseAddress = 0;
3895 PMM_SECTION_SEGMENT Segment;
3896
3897 Segment = MemoryArea->Data.SectionData.Segment;
3898 ImageSectionObject = Section->ImageSection;
3899 SectionSegments = ImageSectionObject->Segments;
3900 NrSegments = ImageSectionObject->NrSegments;
3901
3902 /* Search for the current segment within the section segments
3903 * and calculate the image base address */
3904 for (i = 0; i < NrSegments; i++)
3905 {
3906 if (!(SectionSegments[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
3907 {
3908 if (Segment == &SectionSegments[i])
3909 {
3910 ImageBaseAddress = (char*)BaseAddress - (ULONG_PTR)SectionSegments[i].VirtualAddress;
3911 break;
3912 }
3913 }
3914 }
3915 if (i >= NrSegments)
3916 {
3917 KEBUGCHECK(0);
3918 }
3919
3920 for (i = 0; i < NrSegments; i++)
3921 {
3922 if (!(SectionSegments[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
3923 {
3924 PVOID SBaseAddress = (PVOID)
3925 ((char*)ImageBaseAddress + (ULONG_PTR)SectionSegments[i].VirtualAddress);
3926
3927 Status = MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
3928 }
3929 }
3930 }
3931 else
3932 {
3933 Status = MmUnmapViewOfSegment(AddressSpace, BaseAddress);
3934 }
3935 return(STATUS_SUCCESS);
3936 }
3937
3938 /**********************************************************************
3939 * NAME EXPORTED
3940 * NtUnmapViewOfSection
3941 *
3942 * DESCRIPTION
3943 *
3944 * ARGUMENTS
3945 * ProcessHandle
3946 *
3947 * BaseAddress
3948 *
3949 * RETURN VALUE
3950 * Status.
3951 *
3952 * REVISIONS
3953 */
3954 NTSTATUS STDCALL
3955 NtUnmapViewOfSection (HANDLE ProcessHandle,
3956 PVOID BaseAddress)
3957 {
3958 PEPROCESS Process;
3959 KPROCESSOR_MODE PreviousMode;
3960 NTSTATUS Status;
3961
3962 DPRINT("NtUnmapViewOfSection(ProcessHandle %x, BaseAddress %x)\n",
3963 ProcessHandle, BaseAddress);
3964
3965 PreviousMode = ExGetPreviousMode();
3966
3967 DPRINT("Referencing process\n");
3968 Status = ObReferenceObjectByHandle(ProcessHandle,
3969 PROCESS_VM_OPERATION,
3970 PsProcessType,
3971 PreviousMode,
3972 (PVOID*)(PVOID)&Process,
3973 NULL);
3974 if (!NT_SUCCESS(Status))
3975 {
3976 DPRINT("ObReferenceObjectByHandle failed (Status %x)\n", Status);
3977 return(Status);
3978 }
3979
3980 MmLockAddressSpace(&Process->AddressSpace);
3981 Status = MmUnmapViewOfSection(Process, BaseAddress);
3982 MmUnlockAddressSpace(&Process->AddressSpace);
3983
3984 ObDereferenceObject(Process);
3985
3986 return Status;
3987 }
3988
3989
3990 /**
3991 * Queries the information of a section object.
3992 *
3993 * @param SectionHandle
3994 * Handle to the section object. It must be opened with SECTION_QUERY
3995 * access.
3996 * @param SectionInformationClass
3997 * Index to a certain information structure. Can be either
3998 * SectionBasicInformation or SectionImageInformation. The latter
3999 * is valid only for sections that were created with the SEC_IMAGE
4000 * flag.
4001 * @param SectionInformation
4002 * Caller supplies storage for resulting information.
4003 * @param Length
4004 * Size of the supplied storage.
4005 * @param ResultLength
4006 * Data written.
4007 *
4008 * @return Status.
4009 *
4010 * @implemented
4011 */
4012 NTSTATUS STDCALL
4013 NtQuerySection(IN HANDLE SectionHandle,
4014 IN SECTION_INFORMATION_CLASS SectionInformationClass,
4015 OUT PVOID SectionInformation,
4016 IN ULONG SectionInformationLength,
4017 OUT PULONG ResultLength OPTIONAL)
4018 {
4019 PSECTION_OBJECT Section;
4020 KPROCESSOR_MODE PreviousMode;
4021 NTSTATUS Status = STATUS_SUCCESS;
4022
4023 PreviousMode = ExGetPreviousMode();
4024
4025 DefaultQueryInfoBufferCheck(SectionInformationClass,
4026 ExSectionInfoClass,
4027 SectionInformation,
4028 SectionInformationLength,
4029 ResultLength,
4030 PreviousMode,
4031 &Status);
4032
4033 if(!NT_SUCCESS(Status))
4034 {
4035 DPRINT1("NtQuerySection() failed, Status: 0x%x\n", Status);
4036 return Status;
4037 }
4038
4039 Status = ObReferenceObjectByHandle(SectionHandle,
4040 SECTION_QUERY,
4041 MmSectionObjectType,
4042 PreviousMode,
4043 (PVOID*)(PVOID)&Section,
4044 NULL);
4045 if (NT_SUCCESS(Status))
4046 {
4047 switch (SectionInformationClass)
4048 {
4049 case SectionBasicInformation:
4050 {
4051 PSECTION_BASIC_INFORMATION Sbi = (PSECTION_BASIC_INFORMATION)SectionInformation;
4052
4053 _SEH_TRY
4054 {
4055 Sbi->Attributes = Section->AllocationAttributes;
4056 if (Section->AllocationAttributes & SEC_IMAGE)
4057 {
4058 Sbi->BaseAddress = 0;
4059 Sbi->Size.QuadPart = 0;
4060 }
4061 else
4062 {
4063 Sbi->BaseAddress = (PVOID)Section->Segment->VirtualAddress;
4064 Sbi->Size.QuadPart = Section->Segment->Length;
4065 }
4066
4067 if (ResultLength != NULL)
4068 {
4069 *ResultLength = sizeof(SECTION_BASIC_INFORMATION);
4070 }
4071 Status = STATUS_SUCCESS;
4072 }
4073 _SEH_HANDLE
4074 {
4075 Status = _SEH_GetExceptionCode();
4076 }
4077 _SEH_END;
4078
4079 break;
4080 }
4081
4082 case SectionImageInformation:
4083 {
4084 PSECTION_IMAGE_INFORMATION Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation;
4085
4086 _SEH_TRY
4087 {
4088 memset(Sii, 0, sizeof(SECTION_IMAGE_INFORMATION));
4089 if (Section->AllocationAttributes & SEC_IMAGE)
4090 {
4091 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
4092 ImageSectionObject = Section->ImageSection;
4093
4094 Sii->EntryPoint = ImageSectionObject->EntryPoint;
4095 Sii->StackReserve = ImageSectionObject->StackReserve;
4096 Sii->StackCommit = ImageSectionObject->StackCommit;
4097 Sii->Subsystem = ImageSectionObject->Subsystem;
4098 Sii->MinorSubsystemVersion = ImageSectionObject->MinorSubsystemVersion;
4099 Sii->MajorSubsystemVersion = ImageSectionObject->MajorSubsystemVersion;
4100 Sii->Characteristics = ImageSectionObject->ImageCharacteristics;
4101 Sii->ImageNumber = ImageSectionObject->Machine;
4102 Sii->Executable = ImageSectionObject->Executable;
4103 }
4104
4105 if (ResultLength != NULL)
4106 {
4107 *ResultLength = sizeof(SECTION_IMAGE_INFORMATION);
4108 }
4109 Status = STATUS_SUCCESS;
4110 }
4111 _SEH_HANDLE
4112 {
4113 Status = _SEH_GetExceptionCode();
4114 }
4115 _SEH_END;
4116
4117 break;
4118 }
4119 }
4120
4121 ObDereferenceObject(Section);
4122 }
4123
4124 return(Status);
4125 }
4126
4127
4128 /**
4129 * Extends size of file backed section.
4130 *
4131 * @param SectionHandle
4132 * Handle to the section object. It must be opened with
4133 * SECTION_EXTEND_SIZE access.
4134 * @param NewMaximumSize
4135 * New maximum size of the section in bytes.
4136 *
4137 * @return Status.
4138 *
4139 * @todo Move the actual code to internal function MmExtendSection.
4140 * @unimplemented
4141 */
4142 NTSTATUS STDCALL
4143 NtExtendSection(IN HANDLE SectionHandle,
4144 IN PLARGE_INTEGER NewMaximumSize)
4145 {
4146 LARGE_INTEGER SafeNewMaximumSize;
4147 PSECTION_OBJECT Section;
4148 KPROCESSOR_MODE PreviousMode;
4149 NTSTATUS Status = STATUS_SUCCESS;
4150
4151 PreviousMode = ExGetPreviousMode();
4152
4153 if(PreviousMode != KernelMode)
4154 {
4155 _SEH_TRY
4156 {
4157 ProbeForRead(NewMaximumSize,
4158 sizeof(LARGE_INTEGER),
4159 sizeof(ULONG));
4160 /* make a copy on the stack */
4161 SafeNewMaximumSize = *NewMaximumSize;
4162 NewMaximumSize = &SafeNewMaximumSize;
4163 }
4164 _SEH_HANDLE
4165 {
4166 Status = _SEH_GetExceptionCode();
4167 }
4168 _SEH_END;
4169
4170 if(!NT_SUCCESS(Status))
4171 {
4172 return Status;
4173 }
4174 }
4175
4176 Status = ObReferenceObjectByHandle(SectionHandle,
4177 SECTION_EXTEND_SIZE,
4178 MmSectionObjectType,
4179 PreviousMode,
4180 (PVOID*)&Section,
4181 NULL);
4182 if (!NT_SUCCESS(Status))
4183 {
4184 return Status;
4185 }
4186
4187 if (!(Section->AllocationAttributes & SEC_FILE))
4188 {
4189 ObfDereferenceObject(Section);
4190 return STATUS_INVALID_PARAMETER;
4191 }
4192
4193 /*
4194 * - Acquire file extneding resource.
4195 * - Check if we're not resizing the section below it's actual size!
4196 * - Extend segments if needed.
4197 * - Set file information (FileAllocationInformation) to the new size.
4198 * - Release file extending resource.
4199 */
4200
4201 ObDereferenceObject(Section);
4202
4203 return STATUS_NOT_IMPLEMENTED;
4204 }
4205
4206
4207 /**********************************************************************
4208 * NAME INTERNAL
4209 * MmAllocateSection@4
4210 *
4211 * DESCRIPTION
4212 *
4213 * ARGUMENTS
4214 * Length
4215 *
4216 * RETURN VALUE
4217 *
4218 * NOTE
4219 * Code taken from ntoskrnl/mm/special.c.
4220 *
4221 * REVISIONS
4222 */
4223 PVOID STDCALL
4224 MmAllocateSection (IN ULONG Length, PVOID BaseAddress)
4225 {
4226 PVOID Result;
4227 MEMORY_AREA* marea;
4228 NTSTATUS Status;
4229 ULONG i;
4230 PMADDRESS_SPACE AddressSpace;
4231 PHYSICAL_ADDRESS BoundaryAddressMultiple;
4232
4233 DPRINT("MmAllocateSection(Length %x)\n",Length);
4234
4235 BoundaryAddressMultiple.QuadPart = 0;
4236
4237 AddressSpace = MmGetKernelAddressSpace();
4238 Result = BaseAddress;
4239 MmLockAddressSpace(AddressSpace);
4240 Status = MmCreateMemoryArea (NULL,
4241 AddressSpace,
4242 MEMORY_AREA_SYSTEM,
4243 &Result,
4244 Length,
4245 0,
4246 &marea,
4247 FALSE,
4248 FALSE,
4249 BoundaryAddressMultiple);
4250 MmUnlockAddressSpace(AddressSpace);
4251
4252 if (!NT_SUCCESS(Status))
4253 {
4254 return (NULL);
4255 }
4256 DPRINT("Result %p\n",Result);
4257 for (i = 0; i < PAGE_ROUND_UP(Length) / PAGE_SIZE; i++)
4258 {
4259 PFN_TYPE Page;
4260
4261 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &Page);
4262 if (!NT_SUCCESS(Status))
4263 {
4264 DbgPrint("Unable to allocate page\n");
4265 KEBUGCHECK(0);
4266 }
4267 Status = MmCreateVirtualMapping (NULL,
4268 (PVOID)((ULONG_PTR)Result + (i * PAGE_SIZE)),
4269 PAGE_READWRITE,
4270 &Page,
4271 1);
4272 if (!NT_SUCCESS(Status))
4273 {
4274 DbgPrint("Unable to create virtual mapping\n");
4275 KEBUGCHECK(0);
4276 }
4277 }
4278 return ((PVOID)Result);
4279 }
4280
4281
4282 /**********************************************************************
4283 * NAME EXPORTED
4284 * MmMapViewOfSection
4285 *
4286 * DESCRIPTION
4287 * Maps a view of a section into the virtual address space of a
4288 * process.
4289 *
4290 * ARGUMENTS
4291 * Section
4292 * Pointer to the section object.
4293 *
4294 * ProcessHandle
4295 * Pointer to the process.
4296 *
4297 * BaseAddress
4298 * Desired base address (or NULL) on entry;
4299 * Actual base address of the view on exit.
4300 *
4301 * ZeroBits
4302 * Number of high order address bits that must be zero.
4303 *
4304 * CommitSize
4305 * Size in bytes of the initially committed section of
4306 * the view.
4307 *
4308 * SectionOffset
4309 * Offset in bytes from the beginning of the section
4310 * to the beginning of the view.
4311 *
4312 * ViewSize
4313 * Desired length of map (or zero to map all) on entry
4314 * Actual length mapped on exit.
4315 *
4316 * InheritDisposition
4317 * Specified how the view is to be shared with
4318 * child processes.
4319 *
4320 * AllocationType
4321 * Type of allocation for the pages.
4322 *
4323 * Protect
4324 * Protection for the committed region of the view.
4325 *
4326 * RETURN VALUE
4327 * Status.
4328 *
4329 * @implemented
4330 */
4331 NTSTATUS STDCALL
4332 MmMapViewOfSection(IN PVOID SectionObject,
4333 IN PEPROCESS Process,
4334 IN OUT PVOID *BaseAddress,
4335 IN ULONG ZeroBits,
4336 IN ULONG CommitSize,
4337 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
4338 IN OUT PULONG ViewSize,
4339 IN SECTION_INHERIT InheritDisposition,
4340 IN ULONG AllocationType,
4341 IN ULONG Protect)
4342 {
4343 PSECTION_OBJECT Section;
4344 PMADDRESS_SPACE AddressSpace;
4345 ULONG ViewOffset;
4346 NTSTATUS Status = STATUS_SUCCESS;
4347
4348 ASSERT(Process);
4349
4350 Section = (PSECTION_OBJECT)SectionObject;
4351 AddressSpace = &Process->AddressSpace;
4352
4353 MmLockAddressSpace(AddressSpace);
4354
4355 if (Section->AllocationAttributes & SEC_IMAGE)
4356 {
4357 ULONG i;
4358 ULONG NrSegments;
4359 ULONG_PTR ImageBase;
4360 ULONG ImageSize;
4361 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
4362 PMM_SECTION_SEGMENT SectionSegments;
4363
4364 ImageSectionObject = Section->ImageSection;
4365 SectionSegments = ImageSectionObject->Segments;
4366 NrSegments = ImageSectionObject->NrSegments;
4367
4368
4369 ImageBase = (ULONG_PTR)*BaseAddress;
4370 if (ImageBase == 0)
4371 {
4372 ImageBase = ImageSectionObject->ImageBase;
4373 }
4374
4375 ImageSize = 0;
4376 for (i = 0; i < NrSegments; i++)
4377 {
4378 if (!(SectionSegments[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
4379 {
4380 ULONG_PTR MaxExtent;
4381 MaxExtent = (ULONG_PTR)SectionSegments[i].VirtualAddress +
4382 SectionSegments[i].Length;
4383 ImageSize = max(ImageSize, MaxExtent);
4384 }
4385 }
4386
4387 /* Check there is enough space to map the section at that point. */
4388 if (MmLocateMemoryAreaByRegion(AddressSpace, (PVOID)ImageBase,
4389 PAGE_ROUND_UP(ImageSize)) != NULL)
4390 {
4391 /* Fail if the user requested a fixed base address. */
4392 if ((*BaseAddress) != NULL)
4393 {
4394 MmUnlockAddressSpace(AddressSpace);
4395 return(STATUS_UNSUCCESSFUL);
4396 }
4397 /* Otherwise find a gap to map the image. */
4398 ImageBase = (ULONG_PTR)MmFindGap(AddressSpace, PAGE_ROUND_UP(ImageSize), PAGE_SIZE, FALSE);
4399 if (ImageBase == 0)
4400 {
4401 MmUnlockAddressSpace(AddressSpace);
4402 return(STATUS_UNSUCCESSFUL);
4403 }
4404 }
4405
4406 for (i = 0; i < NrSegments; i++)
4407 {
4408 if (!(SectionSegments[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
4409 {
4410 PVOID SBaseAddress = (PVOID)
4411 ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].VirtualAddress);
4412 MmLockSectionSegment(&SectionSegments[i]);
4413 Status = MmMapViewOfSegment(Process,
4414 AddressSpace,
4415 Section,
4416 &SectionSegments[i],
4417 &SBaseAddress,
4418 SectionSegments[i].Length,
4419 SectionSegments[i].Protection,
4420 0,
4421 FALSE);
4422 MmUnlockSectionSegment(&SectionSegments[i]);
4423 if (!NT_SUCCESS(Status))
4424 {
4425 MmUnlockAddressSpace(AddressSpace);
4426 return(Status);
4427 }
4428 }
4429 }
4430
4431 *BaseAddress = (PVOID)ImageBase;
4432 }
4433 else
4434 {
4435 if (ViewSize == NULL)
4436 {
4437 /* Following this pointer would lead to us to the dark side */
4438 /* What to do? Bugcheck? Return status? Do the mambo? */
4439 KEBUGCHECK(MEMORY_MANAGEMENT);
4440 }
4441
4442 if (SectionOffset == NULL)
4443 {
4444 ViewOffset = 0;
4445 }
4446 else
4447 {
4448 ViewOffset = SectionOffset->u.LowPart;
4449 }
4450
4451 if ((ViewOffset % PAGE_SIZE) != 0)
4452 {
4453 MmUnlockAddressSpace(AddressSpace);
4454 return(STATUS_MAPPED_ALIGNMENT);
4455 }
4456
4457 if ((*ViewSize) == 0)
4458 {
4459 (*ViewSize) = Section->MaximumSize.u.LowPart - ViewOffset;
4460 }
4461 else if (((*ViewSize)+ViewOffset) > Section->MaximumSize.u.LowPart)
4462 {
4463 (*ViewSize) = Section->MaximumSize.u.LowPart - ViewOffset;
4464 }
4465
4466 MmLockSectionSegment(Section->Segment);
4467 Status = MmMapViewOfSegment(Process,
4468 AddressSpace,
4469 Section,
4470 Section->Segment,
4471 BaseAddress,
4472 *ViewSize,
4473 Protect,
4474 ViewOffset,
4475 (AllocationType & MEM_TOP_DOWN));
4476 MmUnlockSectionSegment(Section->Segment);
4477 if (!NT_SUCCESS(Status))
4478 {
4479 MmUnlockAddressSpace(AddressSpace);
4480 return(Status);
4481 }
4482 }
4483
4484 MmUnlockAddressSpace(AddressSpace);
4485
4486 return(STATUS_SUCCESS);
4487 }
4488
4489 /*
4490 * @unimplemented
4491 */
4492 BOOLEAN STDCALL
4493 MmCanFileBeTruncated (IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
4494 IN PLARGE_INTEGER NewFileSize)
4495 {
4496 UNIMPLEMENTED;
4497 return (FALSE);
4498 }
4499
4500
4501 /*
4502 * @unimplemented
4503 */
4504 BOOLEAN STDCALL
4505 MmDisableModifiedWriteOfSection (DWORD Unknown0)
4506 {
4507 UNIMPLEMENTED;
4508 return (FALSE);
4509 }
4510
4511 /*
4512 * @implemented
4513 */
4514 BOOLEAN STDCALL
4515 MmFlushImageSection (IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
4516 IN MMFLUSH_TYPE FlushType)
4517 {
4518 switch(FlushType)
4519 {
4520 case MmFlushForDelete:
4521 if (SectionObjectPointer->ImageSectionObject ||
4522 SectionObjectPointer->DataSectionObject)
4523 {
4524 return FALSE;
4525 }
4526 CcRosSetRemoveOnClose(SectionObjectPointer);
4527 return TRUE;
4528 case MmFlushForWrite:
4529 break;
4530 }
4531 return FALSE;
4532 }
4533
4534 /*
4535 * @unimplemented
4536 */
4537 BOOLEAN STDCALL
4538 MmForceSectionClosed (
4539 IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
4540 IN BOOLEAN DelayClose)
4541 {
4542 UNIMPLEMENTED;
4543 return (FALSE);
4544 }
4545
4546
4547 /*
4548 * @implemented
4549 */
4550 NTSTATUS STDCALL
4551 MmMapViewInSystemSpace (IN PVOID SectionObject,
4552 OUT PVOID * MappedBase,
4553 IN OUT PULONG ViewSize)
4554 {
4555 PSECTION_OBJECT Section;
4556 PMADDRESS_SPACE AddressSpace;
4557 NTSTATUS Status;
4558
4559 DPRINT("MmMapViewInSystemSpace() called\n");
4560
4561 Section = (PSECTION_OBJECT)SectionObject;
4562 AddressSpace = MmGetKernelAddressSpace();
4563
4564 MmLockAddressSpace(AddressSpace);
4565
4566
4567 if ((*ViewSize) == 0)
4568 {
4569 (*ViewSize) = Section->MaximumSize.u.LowPart;
4570 }
4571 else if ((*ViewSize) > Section->MaximumSize.u.LowPart)
4572 {
4573 (*ViewSize) = Section->MaximumSize.u.LowPart;
4574 }
4575
4576 MmLockSectionSegment(Section->Segment);
4577
4578
4579 Status = MmMapViewOfSegment(NULL,
4580 AddressSpace,
4581 Section,
4582 Section->Segment,
4583 MappedBase,
4584 *ViewSize,
4585 PAGE_READWRITE,
4586 0,
4587 FALSE);
4588
4589 MmUnlockSectionSegment(Section->Segment);
4590 MmUnlockAddressSpace(AddressSpace);
4591
4592 return Status;
4593 }
4594
4595 /*
4596 * @unimplemented
4597 */
4598 NTSTATUS
4599 STDCALL
4600 MmMapViewInSessionSpace (
4601 IN PVOID Section,
4602 OUT PVOID *MappedBase,
4603 IN OUT PSIZE_T ViewSize
4604 )
4605 {
4606 UNIMPLEMENTED;
4607 return STATUS_NOT_IMPLEMENTED;
4608 }
4609
4610
4611 /*
4612 * @implemented
4613 */
4614 NTSTATUS STDCALL
4615 MmUnmapViewInSystemSpace (IN PVOID MappedBase)
4616 {
4617 PMADDRESS_SPACE AddressSpace;
4618 NTSTATUS Status;
4619
4620 DPRINT("MmUnmapViewInSystemSpace() called\n");
4621
4622 AddressSpace = MmGetKernelAddressSpace();
4623
4624 Status = MmUnmapViewOfSegment(AddressSpace, MappedBase);
4625
4626 return Status;
4627 }
4628
4629 /*
4630 * @unimplemented
4631 */
4632 NTSTATUS
4633 STDCALL
4634 MmUnmapViewInSessionSpace (
4635 IN PVOID MappedBase
4636 )
4637 {
4638 UNIMPLEMENTED;
4639 return STATUS_NOT_IMPLEMENTED;
4640 }
4641
4642 /*
4643 * @unimplemented
4644 */
4645 NTSTATUS STDCALL
4646 MmSetBankedSection (DWORD Unknown0,
4647 DWORD Unknown1,
4648 DWORD Unknown2,
4649 DWORD Unknown3,
4650 DWORD Unknown4,
4651 DWORD Unknown5)
4652 {
4653 UNIMPLEMENTED;
4654 return (STATUS_NOT_IMPLEMENTED);
4655 }
4656
4657
4658 /**********************************************************************
4659 * NAME EXPORTED
4660 * MmCreateSection@
4661 *
4662 * DESCRIPTION
4663 * Creates a section object.
4664 *
4665 * ARGUMENTS
4666 * SectionObject (OUT)
4667 * Caller supplied storage for the resulting pointer
4668 * to a SECTION_OBJECT instance;
4669 *
4670 * DesiredAccess
4671 * Specifies the desired access to the section can be a
4672 * combination of:
4673 * STANDARD_RIGHTS_REQUIRED |
4674 * SECTION_QUERY |
4675 * SECTION_MAP_WRITE |
4676 * SECTION_MAP_READ |
4677 * SECTION_MAP_EXECUTE
4678 *
4679 * ObjectAttributes [OPTIONAL]
4680 * Initialized attributes for the object can be used
4681 * to create a named section;
4682 *
4683 * MaximumSize
4684 * Maximizes the size of the memory section. Must be
4685 * non-NULL for a page-file backed section.
4686 * If value specified for a mapped file and the file is
4687 * not large enough, file will be extended.
4688 *
4689 * SectionPageProtection
4690 * Can be a combination of:
4691 * PAGE_READONLY |
4692 * PAGE_READWRITE |
4693 * PAGE_WRITEONLY |
4694 * PAGE_WRITECOPY
4695 *
4696 * AllocationAttributes
4697 * Can be a combination of:
4698 * SEC_IMAGE |
4699 * SEC_RESERVE
4700 *
4701 * FileHandle
4702 * Handle to a file to create a section mapped to a file
4703 * instead of a memory backed section;
4704 *
4705 * File
4706 * Unknown.
4707 *
4708 * RETURN VALUE
4709 * Status.
4710 *
4711 * @implemented
4712 */
4713 NTSTATUS STDCALL
4714 MmCreateSection (OUT PSECTION_OBJECT * SectionObject,
4715 IN ACCESS_MASK DesiredAccess,
4716 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
4717 IN PLARGE_INTEGER MaximumSize,
4718 IN ULONG SectionPageProtection,
4719 IN ULONG AllocationAttributes,
4720 IN HANDLE FileHandle OPTIONAL,
4721 IN PFILE_OBJECT File OPTIONAL)
4722 {
4723 if (AllocationAttributes & SEC_IMAGE)
4724 {
4725 return(MmCreateImageSection(SectionObject,
4726 DesiredAccess,
4727 ObjectAttributes,
4728 MaximumSize,
4729 SectionPageProtection,
4730 AllocationAttributes,
4731 FileHandle));
4732 }
4733
4734 if (FileHandle != NULL)
4735 {
4736 return(MmCreateDataFileSection(SectionObject,
4737 DesiredAccess,
4738 ObjectAttributes,
4739 MaximumSize,
4740 SectionPageProtection,
4741 AllocationAttributes,
4742 FileHandle));
4743 }
4744
4745 return(MmCreatePageFileSection(SectionObject,
4746 DesiredAccess,
4747 ObjectAttributes,
4748 MaximumSize,
4749 SectionPageProtection,
4750 AllocationAttributes));
4751 }
4752
4753 /* EOF */