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