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