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