- Fixed the handling for page file backed sections.
[reactos.git] / reactos / ntoskrnl / mm / section.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id: section.c,v 1.123 2003/07/14 20:14:11 hbirr Exp $
20 *
21 * PROJECT: ReactOS kernel
22 * FILE: ntoskrnl/mm/section.c
23 * PURPOSE: Implements section objects
24 * PROGRAMMER: David Welch (welch@mcmail.com)
25 * UPDATE HISTORY:
26 * Created 22/05/98
27 */
28
29 /* INCLUDES *****************************************************************/
30
31 #include <limits.h>
32 #define NTOS_MODE_KERNEL
33 #include <ntos.h>
34 #include <internal/mm.h>
35 #include <internal/io.h>
36 #include <internal/ps.h>
37 #include <internal/pool.h>
38 #include <internal/cc.h>
39 #include <ddk/ntifs.h>
40 #include <ntos/minmax.h>
41
42 #define NDEBUG
43 #include <internal/debug.h>
44
45 /* TYPES *********************************************************************/
46
47 typedef struct
48 {
49 PSECTION_OBJECT Section;
50 PMM_SECTION_SEGMENT Segment;
51 ULONG Offset;
52 BOOLEAN WasDirty;
53 BOOLEAN Private;
54 } MM_SECTION_PAGEOUT_CONTEXT;
55
56 /* GLOBALS *******************************************************************/
57
58 POBJECT_TYPE EXPORTED MmSectionObjectType = NULL;
59
60 static GENERIC_MAPPING MmpSectionMapping = {
61 STANDARD_RIGHTS_READ | SECTION_MAP_READ | SECTION_QUERY,
62 STANDARD_RIGHTS_WRITE | SECTION_MAP_WRITE,
63 STANDARD_RIGHTS_EXECUTE | SECTION_MAP_EXECUTE,
64 SECTION_ALL_ACCESS};
65
66 #define TAG_MM_SECTION_SEGMENT TAG('M', 'M', 'S', 'S')
67 #define TAG_SECTION_PAGE_TABLE TAG('M', 'S', 'P', 'T')
68
69 #define PAGE_FROM_SSE(E) ((E) & 0xFFFFF000)
70 #define SHARE_COUNT_FROM_SSE(E) (((E) & 0x00000FFE) >> 1)
71 #define IS_SWAP_FROM_SSE(E) ((E) & 0x00000001)
72 #define MAX_SHARE_COUNT 0x7FF
73 #define MAKE_SSE(P, C) ((P) | ((C) << 1))
74 #define SWAPENTRY_FROM_SSE(E) ((E) >> 1)
75 #define MAKE_SWAP_SSE(S) (((S) << 1) | 0x1)
76
77 /* FUNCTIONS *****************************************************************/
78
79 VOID
80 MmFreePageTablesSectionSegment(PMM_SECTION_SEGMENT Segment)
81 {
82 ULONG i;
83 if (Segment->Length > NR_SECTION_PAGE_TABLES * PAGE_SIZE)
84 {
85 for (i = 0; i < NR_SECTION_PAGE_TABLES; i++)
86 {
87 if (Segment->PageDirectory.PageTables[i] != NULL)
88 {
89 ExFreePool(Segment->PageDirectory.PageTables[i]);
90 }
91 }
92 }
93 }
94
95 VOID
96 MmFreeSectionSegments(PFILE_OBJECT FileObject)
97 {
98 if (FileObject->SectionObjectPointer->ImageSectionObject != NULL)
99 {
100 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
101 PMM_SECTION_SEGMENT SectionSegments;
102 ULONG NrSegments;
103 ULONG i;
104
105 ImageSectionObject = (PMM_IMAGE_SECTION_OBJECT)FileObject->SectionObjectPointer->ImageSectionObject;
106 NrSegments = ImageSectionObject->NrSegments;
107 SectionSegments = ImageSectionObject->Segments;
108 for (i = 0; i < NrSegments; i++)
109 {
110 if (SectionSegments[i].ReferenceCount != 0)
111 {
112 DPRINT1("Image segment %d still referenced (was %d)\n", i,
113 SectionSegments[i].ReferenceCount);
114 KeBugCheck(0);
115 }
116 MmFreePageTablesSectionSegment(&SectionSegments[i]);
117 }
118 ExFreePool(ImageSectionObject);
119 FileObject->SectionObjectPointer->ImageSectionObject = NULL;
120 }
121 if (FileObject->SectionObjectPointer->DataSectionObject != NULL)
122 {
123 PMM_SECTION_SEGMENT Segment;
124
125 Segment = (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->
126 DataSectionObject;
127
128 if (Segment->ReferenceCount != 0)
129 {
130 DPRINT1("Data segment still referenced\n");
131 KeBugCheck(0);
132 }
133 MmFreePageTablesSectionSegment(Segment);
134 ExFreePool(Segment);
135 FileObject->SectionObjectPointer->DataSectionObject = NULL;
136 }
137 }
138
139 VOID
140 MmLockSectionSegment(PMM_SECTION_SEGMENT Segment)
141 {
142 ExAcquireFastMutex(&Segment->Lock);
143 }
144
145 VOID
146 MmUnlockSectionSegment(PMM_SECTION_SEGMENT Segment)
147 {
148 ExReleaseFastMutex(&Segment->Lock);
149 }
150
151 VOID
152 MmSetPageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
153 ULONG Offset,
154 ULONG Entry)
155 {
156 PSECTION_PAGE_TABLE Table;
157 ULONG DirectoryOffset;
158 ULONG TableOffset;
159
160 if (Segment->Length <= NR_SECTION_PAGE_TABLES * PAGE_SIZE)
161 {
162 Table = (PSECTION_PAGE_TABLE)&Segment->PageDirectory;
163 }
164 else
165 {
166 DirectoryOffset = PAGE_TO_SECTION_PAGE_DIRECTORY_OFFSET(Offset);
167 Table = Segment->PageDirectory.PageTables[DirectoryOffset];
168 if (Table == NULL)
169 {
170 Table =
171 Segment->PageDirectory.PageTables[DirectoryOffset] =
172 ExAllocatePoolWithTag(NonPagedPool, sizeof(SECTION_PAGE_TABLE),
173 TAG_SECTION_PAGE_TABLE);
174 if (Table == NULL)
175 {
176 KeBugCheck(0);
177 }
178 memset(Table, 0, sizeof(SECTION_PAGE_TABLE));
179 DPRINT("Table %x\n", Table);
180 }
181 }
182 TableOffset = PAGE_TO_SECTION_PAGE_TABLE_OFFSET(Offset);
183 Table->Entry[TableOffset] = Entry;
184 }
185
186
187 ULONG
188 MmGetPageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
189 ULONG Offset)
190 {
191 PSECTION_PAGE_TABLE Table;
192 ULONG Entry;
193 ULONG DirectoryOffset;
194 ULONG TableOffset;
195
196 DPRINT("MmGetPageEntrySection(Offset %x)\n", Offset);
197
198 if (Segment->Length <= NR_SECTION_PAGE_TABLES * PAGE_SIZE)
199 {
200 Table = (PSECTION_PAGE_TABLE)&Segment->PageDirectory;
201 }
202 else
203 {
204 DirectoryOffset = PAGE_TO_SECTION_PAGE_DIRECTORY_OFFSET(Offset);
205 Table = Segment->PageDirectory.PageTables[DirectoryOffset];
206 DPRINT("Table %x\n", Table);
207 if (Table == NULL)
208 {
209 return(0);
210 }
211 }
212 TableOffset = PAGE_TO_SECTION_PAGE_TABLE_OFFSET(Offset);
213 Entry = Table->Entry[TableOffset];
214 return(Entry);
215 }
216
217 VOID
218 MmSharePageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
219 ULONG Offset)
220 {
221 ULONG Entry;
222
223 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
224 if (Entry == 0)
225 {
226 DPRINT1("Entry == 0 for MmSharePageEntrySectionSegment\n");
227 KeBugCheck(0);
228 }
229 if (SHARE_COUNT_FROM_SSE(Entry) == MAX_SHARE_COUNT)
230 {
231 DPRINT1("Maximum share count reached\n");
232 KeBugCheck(0);
233 }
234 if (IS_SWAP_FROM_SSE(Entry))
235 {
236 KeBugCheck(0);
237 }
238 Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) + 1);
239 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
240 }
241
242 BOOLEAN
243 MmUnsharePageEntrySectionSegment(PSECTION_OBJECT Section,
244 PMM_SECTION_SEGMENT Segment,
245 ULONG Offset,
246 BOOLEAN Dirty)
247 {
248 ULONG Entry;
249
250 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
251 if (Entry == 0)
252 {
253 DPRINT1("Entry == 0 for MmSharePageEntrySectionSegment\n");
254 KeBugCheck(0);
255 }
256 if (SHARE_COUNT_FROM_SSE(Entry) == 0)
257 {
258 DPRINT1("Zero share count for unshare\n");
259 KeBugCheck(0);
260 }
261 if (IS_SWAP_FROM_SSE(Entry))
262 {
263 KeBugCheck(0);
264 }
265 Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) - 1);
266 /*
267 * If we reducing the share count of this entry to zero then set the entry
268 * to zero and tell the cache the page is no longer mapped.
269 */
270 if (SHARE_COUNT_FROM_SSE(Entry) == 0)
271 {
272 PFILE_OBJECT FileObject;
273 PBCB Bcb;
274 SWAPENTRY SavedSwapEntry;
275 PHYSICAL_ADDRESS Page;
276 BOOLEAN IsImageSection;
277 ULONG FileOffset;
278
279 FileOffset = Offset + Segment->FileOffset;
280
281 IsImageSection = Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
282
283 Page = (PHYSICAL_ADDRESS)(LONGLONG)PAGE_FROM_SSE(Entry);
284 FileObject = Section->FileObject;
285 if (FileObject != NULL)
286 {
287
288 if (FileObject->Flags & FO_DIRECT_CACHE_PAGING_READ &&
289 (FileOffset % PAGE_SIZE) == 0 &&
290 (Offset + PAGE_SIZE <= Segment->RawLength || !IsImageSection))
291 {
292 NTSTATUS Status;
293 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
294 Status = CcRosUnmapCacheSegment(Bcb, FileOffset, Dirty);
295 if (!NT_SUCCESS(Status))
296 {
297 DPRINT1("CcRosUnmapCacheSegment failed, status = %x\n", Status);
298 KeBugCheck(0);
299 }
300 }
301 }
302
303 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
304 if (SavedSwapEntry == 0)
305 {
306 if (Segment->Flags & MM_PAGEFILE_SEGMENT)
307 {
308 /*
309 * FIXME:
310 * Try to page out this page and set the swap entry
311 * within the section segment. There exist no rmap entry
312 * for this page. The pager thread can't page out a
313 * page without a rmap entry.
314 */
315 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
316 MmReferencePage(Page);
317 }
318 else
319 {
320 MmSetPageEntrySectionSegment(Segment, Offset, 0);
321 }
322 }
323 else
324 {
325 MmSetSavedSwapEntryPage(Page, 0);
326 if (Segment->Flags & MM_PAGEFILE_SEGMENT)
327 {
328 MmSetPageEntrySectionSegment(Segment, Offset, MAKE_SWAP_SSE(SavedSwapEntry));
329 }
330 else
331 {
332 MmSetPageEntrySectionSegment(Segment, Offset, 0);
333 MmFreeSwapPage(SavedSwapEntry);
334 }
335 }
336 }
337 else
338 {
339 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
340 }
341 return(SHARE_COUNT_FROM_SSE(Entry) > 0);
342 }
343
344 BOOL MiIsPageFromCache(PMEMORY_AREA MemoryArea,
345 ULONG SegOffset)
346 {
347 PBCB Bcb;
348 PCACHE_SEGMENT CacheSeg;
349
350 Bcb = MemoryArea->Data.SectionData.Section->FileObject->SectionObjectPointer->SharedCacheMap;
351 CacheSeg = CcRosLookupCacheSegment(Bcb, SegOffset + MemoryArea->Data.SectionData.Segment->FileOffset);
352 if (CacheSeg)
353 {
354 CcRosReleaseCacheSegment(Bcb, CacheSeg, CacheSeg->Valid, FALSE, TRUE);
355 return TRUE;
356 }
357 return FALSE;
358 }
359
360 NTSTATUS
361 MiReadPage(PMEMORY_AREA MemoryArea,
362 ULONG SegOffset,
363 PHYSICAL_ADDRESS* Page)
364 /*
365 * FUNCTION: Read a page for a section backed memory area.
366 * PARAMETERS:
367 * MemoryArea - Memory area to read the page for.
368 * Offset - Offset of the page to read.
369 * Page - Variable that receives a page contains the read data.
370 */
371 {
372 ULONG BaseOffset;
373 ULONG FileOffset;
374 PVOID BaseAddress;
375 BOOLEAN UptoDate;
376 PCACHE_SEGMENT CacheSeg;
377 PFILE_OBJECT FileObject;
378 NTSTATUS Status;
379 ULONG RawLength;
380 PBCB Bcb;
381 BOOLEAN IsImageSection;
382 ULONG Length;
383
384 FileObject = MemoryArea->Data.SectionData.Section->FileObject;
385 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
386 RawLength = MemoryArea->Data.SectionData.Segment->RawLength;
387 FileOffset = SegOffset + MemoryArea->Data.SectionData.Segment->FileOffset;
388 IsImageSection = MemoryArea->Data.SectionData.Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
389
390 assert(Bcb);
391
392 DPRINT("%S %x\n", FileObject->FileName.Buffer, FileOffset);
393
394 /*
395 * If the file system is letting us go directly to the cache and the
396 * memory area was mapped at an offset in the file which is page aligned
397 * then get the related cache segment.
398 */
399 if (FileObject->Flags & FO_DIRECT_CACHE_PAGING_READ &&
400 (FileOffset % PAGE_SIZE) == 0 &&
401 (SegOffset + PAGE_SIZE <= RawLength || !IsImageSection))
402 {
403 PHYSICAL_ADDRESS Addr;
404
405 /*
406 * Get the related cache segment; we use a lower level interface than
407 * filesystems do because it is safe for us to use an offset with a
408 * alignment less than the file system block size.
409 */
410 Status = CcRosGetCacheSegment(Bcb,
411 FileOffset,
412 &BaseOffset,
413 &BaseAddress,
414 &UptoDate,
415 &CacheSeg);
416 if (!NT_SUCCESS(Status))
417 {
418 return(Status);
419 }
420 if (!UptoDate)
421 {
422 /*
423 * If the cache segment isn't up to date then call the file
424 * system to read in the data.
425 */
426 Status = ReadCacheSegment(CacheSeg);
427 if (!NT_SUCCESS(Status))
428 {
429 CcRosReleaseCacheSegment(Bcb, CacheSeg, FALSE, FALSE, FALSE);
430 return Status;
431 }
432 }
433 /*
434 * Retrieve the page from the cache segment that we actually want.
435 */
436 Addr = MmGetPhysicalAddress(BaseAddress +
437 FileOffset - BaseOffset);
438 (*Page) = Addr;
439 MmReferencePage((*Page));
440
441 CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, FALSE, TRUE);
442 }
443 else
444 {
445 PVOID PageAddr;
446 ULONG CacheSegOffset;
447 /*
448 * Allocate a page, this is rather complicated by the possibility
449 * we might have to move other things out of memory
450 */
451 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, Page);
452 if (!NT_SUCCESS(Status))
453 {
454 return(Status);
455 }
456 Status = CcRosGetCacheSegment(Bcb,
457 FileOffset,
458 &BaseOffset,
459 &BaseAddress,
460 &UptoDate,
461 &CacheSeg);
462 if (!NT_SUCCESS(Status))
463 {
464 return(Status);
465 }
466 if (!UptoDate)
467 {
468 /*
469 * If the cache segment isn't up to date then call the file
470 * system to read in the data.
471 */
472 Status = ReadCacheSegment(CacheSeg);
473 if (!NT_SUCCESS(Status))
474 {
475 CcRosReleaseCacheSegment(Bcb, CacheSeg, FALSE, FALSE, FALSE);
476 return Status;
477 }
478 }
479 PageAddr = ExAllocatePageWithPhysPage(*Page);
480 CacheSegOffset = BaseOffset + CacheSeg->Bcb->CacheSegmentSize - FileOffset;
481 Length = RawLength - SegOffset;
482 if (Length <= CacheSegOffset && Length <= PAGE_SIZE)
483 {
484 memcpy(PageAddr, BaseAddress + FileOffset - BaseOffset, Length);
485 }
486 else if (CacheSegOffset >= PAGE_SIZE)
487 {
488 memcpy(PageAddr, BaseAddress + FileOffset - BaseOffset, PAGE_SIZE);
489 }
490 else
491 {
492 memcpy(PageAddr, BaseAddress + FileOffset - BaseOffset, CacheSegOffset);
493 CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, FALSE, FALSE);
494 Status = CcRosGetCacheSegment(Bcb,
495 FileOffset + CacheSegOffset,
496 &BaseOffset,
497 &BaseAddress,
498 &UptoDate,
499 &CacheSeg);
500 if (!NT_SUCCESS(Status))
501 {
502 ExUnmapPage(PageAddr);
503 return(Status);
504 }
505 if (!UptoDate)
506 {
507 /*
508 * If the cache segment isn't up to date then call the file
509 * system to read in the data.
510 */
511 Status = ReadCacheSegment(CacheSeg);
512 if (!NT_SUCCESS(Status))
513 {
514 CcRosReleaseCacheSegment(Bcb, CacheSeg, FALSE, FALSE, FALSE);
515 ExUnmapPage(PageAddr);
516 return Status;
517 }
518 }
519 if (Length < PAGE_SIZE)
520 {
521 memcpy(PageAddr + CacheSegOffset, BaseAddress, Length - CacheSegOffset);
522 }
523 else
524 {
525 memcpy(PageAddr + CacheSegOffset, BaseAddress, PAGE_SIZE - CacheSegOffset);
526 }
527 }
528 CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, FALSE, FALSE);
529 ExUnmapPage(PageAddr);
530 }
531 return(STATUS_SUCCESS);
532 }
533
534 NTSTATUS
535 MmNotPresentFaultSectionView(PMADDRESS_SPACE AddressSpace,
536 MEMORY_AREA* MemoryArea,
537 PVOID Address,
538 BOOLEAN Locked)
539 {
540 ULONG Offset;
541 LARGE_INTEGER Page;
542 NTSTATUS Status;
543 ULONG PAddress;
544 PSECTION_OBJECT Section;
545 PMM_SECTION_SEGMENT Segment;
546 ULONG Entry;
547 ULONG Entry1;
548 ULONG Attributes;
549 PMM_PAGEOP PageOp;
550 PMM_REGION Region;
551 LARGE_INTEGER Timeout;
552
553 /*
554 * There is a window between taking the page fault and locking the
555 * address space when another thread could load the page so we check
556 * that.
557 */
558 if (MmIsPagePresent(AddressSpace->Process, Address))
559 {
560 if (Locked)
561 {
562 MmLockPage(MmGetPhysicalAddressForProcess(AddressSpace->Process, Address));
563 }
564 return(STATUS_SUCCESS);
565 }
566
567 PAddress = (ULONG)PAGE_ROUND_DOWN(((ULONG)Address));
568 Offset = PAddress - (ULONG)MemoryArea->BaseAddress;
569
570 Segment = MemoryArea->Data.SectionData.Segment;
571 Section = MemoryArea->Data.SectionData.Section;
572 Region = MmFindRegion(MemoryArea->BaseAddress,
573 &MemoryArea->Data.SectionData.RegionListHead,
574 Address, NULL);
575 /*
576 * Lock the segment
577 */
578 MmLockSectionSegment(Segment);
579
580 /*
581 * Check if this page needs to be mapped COW
582 */
583 if ((Segment->WriteCopy || MemoryArea->Data.SectionData.WriteCopyView) &&
584 (Region->Protect == PAGE_READWRITE ||
585 Region->Protect == PAGE_EXECUTE_READWRITE))
586 {
587 Attributes = Region->Protect == PAGE_READWRITE ? PAGE_READONLY : PAGE_EXECUTE_READ;
588 }
589 else
590 {
591 Attributes = Region->Protect;
592 }
593
594 /*
595 * Get or create a page operation descriptor
596 */
597 PageOp = MmGetPageOp(MemoryArea, 0, 0, Segment, Offset, MM_PAGEOP_PAGEIN);
598 if (PageOp == NULL)
599 {
600 DPRINT1("MmGetPageOp failed\n");
601 KeBugCheck(0);
602 }
603
604 /*
605 * Check if someone else is already handling this fault, if so wait
606 * for them
607 */
608 if (PageOp->Thread != PsGetCurrentThread())
609 {
610 MmUnlockSectionSegment(Segment);
611 MmUnlockAddressSpace(AddressSpace);
612 Timeout.QuadPart = -100000000LL; // 10 sec
613 Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
614 0,
615 KernelMode,
616 FALSE,
617 &Timeout);
618
619 /*
620 * Check for various strange conditions
621 */
622 if (Status != STATUS_SUCCESS)
623 {
624 DPRINT1("Failed to wait for page op, status = %x\n", Status);
625 KeBugCheck(0);
626 }
627 if (PageOp->Status == STATUS_PENDING)
628 {
629 DPRINT1("Woke for page op before completion\n");
630 KeBugCheck(0);
631 }
632 MmLockAddressSpace(AddressSpace);
633 /*
634 * If this wasn't a pagein then restart the operation
635 */
636 if (PageOp->OpType != MM_PAGEOP_PAGEIN)
637 {
638 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
639 MmReleasePageOp(PageOp);
640 DPRINT("Address 0x%.8X\n", Address);
641 return(STATUS_MM_RESTART_OPERATION);
642 }
643
644 /*
645 * If the thread handling this fault has failed then we don't retry
646 */
647 if (!NT_SUCCESS(PageOp->Status))
648 {
649 Status = PageOp->Status;
650 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
651 MmReleasePageOp(PageOp);
652 DPRINT("Address 0x%.8X\n", Address);
653 return(Status);
654 }
655 MmLockSectionSegment(Segment);
656 /*
657 * If the completed fault was for another address space then set the
658 * page in this one.
659 */
660 if (!MmIsPagePresent(AddressSpace->Process, Address))
661 {
662 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
663 if (Entry == 0)
664 {
665 MmUnlockSectionSegment(Segment);
666 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
667 MmReleasePageOp(PageOp);
668 return(STATUS_MM_RESTART_OPERATION);
669 }
670
671 Page = (LARGE_INTEGER)(LONGLONG)(PAGE_FROM_SSE(Entry));
672 MmReferencePage(Page);
673 MmSharePageEntrySectionSegment(Segment, Offset);
674
675 Status = MmCreateVirtualMapping(MemoryArea->Process,
676 Address,
677 Attributes,
678 Page,
679 FALSE);
680 if (Status == STATUS_NO_MEMORY)
681 {
682 MmUnlockAddressSpace(AddressSpace);
683 Status = MmCreateVirtualMapping(MemoryArea->Process,
684 Address,
685 Attributes,
686 Page,
687 TRUE);
688 MmLockAddressSpace(AddressSpace);
689 }
690
691 if (!NT_SUCCESS(Status))
692 {
693 DbgPrint("Unable to create virtual mapping\n");
694 KeBugCheck(0);
695 }
696 MmInsertRmap(Page, MemoryArea->Process, (PVOID)PAddress);
697 }
698 if (Locked)
699 {
700 MmLockPage(Page);
701 }
702 MmUnlockSectionSegment(Segment);
703 PageOp->Status = STATUS_SUCCESS;
704 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
705 MmReleasePageOp(PageOp);
706 DPRINT("Address 0x%.8X\n", Address);
707 return(STATUS_SUCCESS);
708 }
709
710 /*
711 * Must be private page we have swapped out.
712 */
713 if (MmIsPageSwapEntry(AddressSpace->Process, (PVOID)PAddress))
714 {
715 SWAPENTRY SwapEntry;
716 PMDL Mdl;
717
718 /*
719 * Sanity check
720 */
721 if (Segment->Flags & MM_PAGEFILE_SEGMENT)
722 {
723 DPRINT1("Found a swaped out private page in a pagefile section.\n");
724 KeBugCheck(0);
725 }
726
727 MmUnlockSectionSegment(Segment);
728 MmDeletePageFileMapping(AddressSpace->Process, (PVOID)PAddress, &SwapEntry);
729
730 MmUnlockAddressSpace(AddressSpace);
731 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
732 if (!NT_SUCCESS(Status))
733 {
734 KeBugCheck(0);
735 }
736
737 Mdl = MmCreateMdl(NULL, NULL, PAGE_SIZE);
738 MmBuildMdlFromPages(Mdl, (PULONG)&Page);
739 Status = MmReadFromSwapPage(SwapEntry, Mdl);
740 if (!NT_SUCCESS(Status))
741 {
742 DPRINT1("MmReadFromSwapPage failed, status = %x\n", Status);
743 KeBugCheck(0);
744 }
745 MmLockAddressSpace(AddressSpace);
746 Status = MmCreateVirtualMapping(AddressSpace->Process,
747 Address,
748 Region->Protect,
749 Page,
750 FALSE);
751 if (Status == STATUS_NO_MEMORY)
752 {
753 MmUnlockAddressSpace(AddressSpace);
754 Status = MmCreateVirtualMapping(AddressSpace->Process,
755 Address,
756 Region->Protect,
757 Page,
758 TRUE);
759 MmLockAddressSpace(AddressSpace);
760 }
761 if (!NT_SUCCESS(Status))
762 {
763 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
764 KeBugCheck(0);
765 return(Status);
766 }
767
768 /*
769 * Store the swap entry for later use.
770 */
771 MmSetSavedSwapEntryPage(Page, SwapEntry);
772
773 /*
774 * Add the page to the process's working set
775 */
776 MmInsertRmap(Page, AddressSpace->Process, (PVOID)PAddress);
777
778 /*
779 * Finish the operation
780 */
781 if (Locked)
782 {
783 MmLockPage(MmGetPhysicalAddressForProcess(NULL, Address));
784 }
785 PageOp->Status = STATUS_SUCCESS;
786 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
787 MmReleasePageOp(PageOp);
788 DPRINT("Address 0x%.8X\n", Address);
789 return(STATUS_SUCCESS);
790 }
791
792 /*
793 * Satisfying a page fault on a map of /Device/PhysicalMemory is easy
794 */
795 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
796 {
797 MmUnlockSectionSegment(Segment);
798 /*
799 * Just map the desired physical page
800 */
801 Page.QuadPart = Offset + MemoryArea->Data.SectionData.ViewOffset;
802 Status = MmCreateVirtualMapping(AddressSpace->Process,
803 Address,
804 Region->Protect,
805 Page,
806 FALSE);
807 if (Status == STATUS_NO_MEMORY)
808 {
809 MmUnlockAddressSpace(AddressSpace);
810 Status = MmCreateVirtualMapping(AddressSpace->Process,
811 Address,
812 Region->Protect,
813 Page,
814 TRUE);
815 MmLockAddressSpace(AddressSpace);
816 }
817 if (!NT_SUCCESS(Status))
818 {
819 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
820 KeBugCheck(0);
821 return(Status);
822 }
823 /*
824 * Don't add an rmap entry since the page mapped could be for
825 * anything.
826 */
827 if (Locked)
828 {
829 MmLockPage(Page);
830 }
831
832 /*
833 * Cleanup and release locks
834 */
835 PageOp->Status = STATUS_SUCCESS;
836 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
837 MmReleasePageOp(PageOp);
838 DPRINT("Address 0x%.8X\n", Address);
839 return(STATUS_SUCCESS);
840 }
841
842 /*
843 * Map anonymous memory for BSS sections
844 */
845 if (Segment->Characteristics & IMAGE_SECTION_CHAR_BSS)
846 {
847 MmUnlockSectionSegment(Segment);
848 Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page);
849 if (!NT_SUCCESS(Status))
850 {
851 MmUnlockAddressSpace(AddressSpace);
852 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
853 MmLockAddressSpace(AddressSpace);
854 }
855 if (!NT_SUCCESS(Status))
856 {
857 KeBugCheck(0);
858 }
859 Status = MmCreateVirtualMapping(AddressSpace->Process,
860 Address,
861 Region->Protect,
862 Page,
863 FALSE);
864 if (Status == STATUS_NO_MEMORY)
865 {
866 MmUnlockAddressSpace(AddressSpace);
867 Status = MmCreateVirtualMapping(AddressSpace->Process,
868 Address,
869 Region->Protect,
870 Page,
871 TRUE);
872 MmLockAddressSpace(AddressSpace);
873 }
874
875 if (!NT_SUCCESS(Status))
876 {
877 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
878 KeBugCheck(0);
879 return(Status);
880 }
881 MmInsertRmap(Page, AddressSpace->Process, (PVOID)PAddress);
882 if (Locked)
883 {
884 MmLockPage(Page);
885 }
886
887 /*
888 * Cleanup and release locks
889 */
890 PageOp->Status = STATUS_SUCCESS;
891 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
892 MmReleasePageOp(PageOp);
893 DPRINT("Address 0x%.8X\n", Address);
894 return(STATUS_SUCCESS);
895 }
896
897 /*
898 * Get the entry corresponding to the offset within the section
899 */
900 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
901
902 if (Entry == 0)
903 {
904 /*
905 * If the entry is zero (and it can't change because we have
906 * locked the segment) then we need to load the page.
907 */
908
909 /*
910 * Release all our locks and read in the page from disk
911 */
912 MmUnlockSectionSegment(Segment);
913 MmUnlockAddressSpace(AddressSpace);
914
915 if ((Segment->Flags & MM_PAGEFILE_SEGMENT) ||
916 (Offset > Segment->RawLength && Section->AllocationAttributes & SEC_IMAGE))
917 {
918 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
919 }
920 else
921 {
922 Status = MiReadPage(MemoryArea, Offset, &Page);
923 }
924 if (!NT_SUCCESS(Status) && Status != STATUS_END_OF_FILE)
925 {
926 /*
927 * FIXME: What do we know in this case?
928 */
929 DPRINT1("MiReadPage or MmRequestPageMemoryConsumer failed (Status %x)\n", Status);
930 /*
931 * Cleanup and release locks
932 */
933 MmLockAddressSpace(AddressSpace);
934 PageOp->Status = Status;
935 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
936 MmReleasePageOp(PageOp);
937 DPRINT("Address 0x%.8X\n", Address);
938 return(Status);
939 }
940 /*
941 * Relock the address space and segment
942 */
943 MmLockAddressSpace(AddressSpace);
944 MmLockSectionSegment(Segment);
945
946 /*
947 * Check the entry. No one should change the status of a page
948 * that has a pending page-in.
949 */
950 Entry1 = MmGetPageEntrySectionSegment(Segment, Offset);
951 if (Entry != Entry1)
952 {
953 DbgPrint("Someone changed ppte entry while we slept\n");
954 KeBugCheck(0);
955 }
956
957 /*
958 * Mark the offset within the section as having valid, in-memory
959 * data
960 */
961 Entry = MAKE_SSE(Page.u.LowPart, 1);
962 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
963 MmUnlockSectionSegment(Segment);
964
965 Status = MmCreateVirtualMapping(AddressSpace->Process,
966 Address,
967 Attributes,
968 Page,
969 FALSE);
970 if (Status == STATUS_NO_MEMORY)
971 {
972 MmUnlockAddressSpace(AddressSpace);
973 Status = MmCreateVirtualMapping(AddressSpace->Process,
974 Address,
975 Attributes,
976 Page,
977 TRUE);
978 MmLockAddressSpace(AddressSpace);
979 }
980 if (!NT_SUCCESS(Status))
981 {
982 DbgPrint("Unable to create virtual mapping\n");
983 KeBugCheck(0);
984 }
985 MmInsertRmap(Page, AddressSpace->Process, (PVOID)PAddress);
986
987 if (Locked)
988 {
989 MmLockPage(Page);
990 }
991 PageOp->Status = STATUS_SUCCESS;
992 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
993 MmReleasePageOp(PageOp);
994 DPRINT("Address 0x%.8X\n", Address);
995 return(STATUS_SUCCESS);
996 }
997 else if (IS_SWAP_FROM_SSE(Entry))
998 {
999 SWAPENTRY SwapEntry;
1000 PMDL Mdl;
1001
1002 SwapEntry = SWAPENTRY_FROM_SSE(Entry);
1003
1004 /*
1005 * Release all our locks and read in the page from disk
1006 */
1007 MmUnlockSectionSegment(Segment);
1008
1009 MmUnlockAddressSpace(AddressSpace);
1010
1011 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1012 if (!NT_SUCCESS(Status))
1013 {
1014 KeBugCheck(0);
1015 }
1016
1017 Mdl = MmCreateMdl(NULL, NULL, PAGE_SIZE);
1018 MmBuildMdlFromPages(Mdl, (PULONG)&Page);
1019 Status = MmReadFromSwapPage(SwapEntry, Mdl);
1020 if (!NT_SUCCESS(Status))
1021 {
1022 KeBugCheck(0);
1023 }
1024
1025 /*
1026 * Relock the address space and segment
1027 */
1028 MmLockAddressSpace(AddressSpace);
1029 MmLockSectionSegment(Segment);
1030
1031 /*
1032 * Check the entry. No one should change the status of a page
1033 * that has a pending page-in.
1034 */
1035 Entry1 = MmGetPageEntrySectionSegment(Segment, Offset);
1036 if (Entry != Entry1)
1037 {
1038 DbgPrint("Someone changed ppte entry while we slept\n");
1039 KeBugCheck(0);
1040 }
1041
1042 /*
1043 * Mark the offset within the section as having valid, in-memory
1044 * data
1045 */
1046 Entry = MAKE_SSE(Page.u.LowPart, 1);
1047 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1048 MmUnlockSectionSegment(Segment);
1049
1050 /*
1051 * Save the swap entry.
1052 */
1053 MmSetSavedSwapEntryPage(Page, SwapEntry);
1054 Status = MmCreateVirtualMapping(AddressSpace->Process,
1055 Address,
1056 Region->Protect,
1057 Page,
1058 FALSE);
1059 if (Status == STATUS_NO_MEMORY)
1060 {
1061 MmUnlockAddressSpace(AddressSpace);
1062 Status = MmCreateVirtualMapping(AddressSpace->Process,
1063 Address,
1064 Region->Protect,
1065 Page,
1066 TRUE);
1067 MmLockAddressSpace(AddressSpace);
1068 }
1069 if (!NT_SUCCESS(Status))
1070 {
1071 DbgPrint("Unable to create virtual mapping\n");
1072 KeBugCheck(0);
1073 }
1074 MmInsertRmap(Page, AddressSpace->Process, (PVOID)PAddress);
1075 if (Locked)
1076 {
1077 MmLockPage(Page);
1078 }
1079 PageOp->Status = STATUS_SUCCESS;
1080 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
1081 MmReleasePageOp(PageOp);
1082 DPRINT("Address 0x%.8X\n", Address);
1083 return(STATUS_SUCCESS);
1084 }
1085 else
1086 {
1087 /*
1088 * If the section offset is already in-memory and valid then just
1089 * take another reference to the page
1090 */
1091
1092 Page = (LARGE_INTEGER)(LONGLONG)PAGE_FROM_SSE(Entry);
1093 MmReferencePage(Page);
1094 MmSharePageEntrySectionSegment(Segment, Offset);
1095 MmUnlockSectionSegment(Segment);
1096
1097 Status = MmCreateVirtualMapping(AddressSpace->Process,
1098 Address,
1099 Attributes,
1100 Page,
1101 FALSE);
1102 if (Status == STATUS_NO_MEMORY)
1103 {
1104 MmUnlockAddressSpace(AddressSpace);
1105 Status = MmCreateVirtualMapping(AddressSpace->Process,
1106 Address,
1107 Attributes,
1108 Page,
1109 TRUE);
1110 MmLockAddressSpace(AddressSpace);
1111 }
1112 if (!NT_SUCCESS(Status))
1113 {
1114 DbgPrint("Unable to create virtual mapping\n");
1115 KeBugCheck(0);
1116 }
1117 MmInsertRmap(Page, AddressSpace->Process, (PVOID)PAddress);
1118 if (Locked)
1119 {
1120 MmLockPage(Page);
1121 }
1122 PageOp->Status = STATUS_SUCCESS;
1123 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
1124 MmReleasePageOp(PageOp);
1125 DPRINT("Address 0x%.8X\n", Address);
1126 return(STATUS_SUCCESS);
1127 }
1128 }
1129
1130 NTSTATUS
1131 MmAccessFaultSectionView(PMADDRESS_SPACE AddressSpace,
1132 MEMORY_AREA* MemoryArea,
1133 PVOID Address,
1134 BOOLEAN Locked)
1135 {
1136 PMM_SECTION_SEGMENT Segment;
1137 PSECTION_OBJECT Section;
1138 PHYSICAL_ADDRESS OldPage;
1139 PHYSICAL_ADDRESS NewPage;
1140 PVOID NewAddress;
1141 NTSTATUS Status;
1142 ULONG PAddress;
1143 ULONG Offset;
1144 PMM_PAGEOP PageOp;
1145 PMM_REGION Region;
1146 LARGE_INTEGER Timeout;
1147
1148 /*
1149 * Check if the page has been paged out or has already been set readwrite
1150 */
1151 if (!MmIsPagePresent(AddressSpace->Process, Address) ||
1152 MmGetPageProtect(AddressSpace->Process, Address) & PAGE_READWRITE)
1153 {
1154 DPRINT("Address 0x%.8X\n", Address);
1155 return(STATUS_SUCCESS);
1156 }
1157
1158 /*
1159 * Find the offset of the page
1160 */
1161 PAddress = (ULONG)PAGE_ROUND_DOWN(((ULONG)Address));
1162 Offset = PAddress - (ULONG)MemoryArea->BaseAddress;
1163
1164 Segment = MemoryArea->Data.SectionData.Segment;
1165 Section = MemoryArea->Data.SectionData.Section;
1166 Region = MmFindRegion(MemoryArea->BaseAddress,
1167 &MemoryArea->Data.SectionData.RegionListHead,
1168 Address, NULL);
1169 /*
1170 * Lock the segment
1171 */
1172 MmLockSectionSegment(Segment);
1173
1174 /*
1175 * Sanity check.
1176 */
1177 if (MmGetPageEntrySectionSegment(Segment, Offset) == 0)
1178 {
1179 DPRINT1("COW fault for page with PESS 0. Address was 0x%.8X\n",
1180 Address);
1181 }
1182 MmUnlockSectionSegment(Segment);
1183 /*
1184 * Check if we are doing COW
1185 */
1186 if (!((Segment->WriteCopy || MemoryArea->Data.SectionData.WriteCopyView) &&
1187 (Region->Protect == PAGE_READWRITE ||
1188 Region->Protect == PAGE_EXECUTE_READWRITE)))
1189 {
1190 DPRINT("Address 0x%.8X\n", Address);
1191 return(STATUS_UNSUCCESSFUL);
1192 }
1193
1194 /*
1195 * Get or create a pageop
1196 */
1197 PageOp = MmGetPageOp(MemoryArea, 0, 0, Segment, Offset,
1198 MM_PAGEOP_ACCESSFAULT);
1199 if (PageOp == NULL)
1200 {
1201 DPRINT1("MmGetPageOp failed\n");
1202 KeBugCheck(0);
1203 }
1204
1205 /*
1206 * Wait for any other operations to complete
1207 */
1208 if (PageOp->Thread != PsGetCurrentThread())
1209 {
1210 MmUnlockAddressSpace(AddressSpace);
1211 Timeout.QuadPart = -100000000LL; // 10 sec
1212 Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
1213 0,
1214 KernelMode,
1215 FALSE,
1216 &Timeout);
1217 /*
1218 * Check for various strange conditions
1219 */
1220 if (Status == STATUS_TIMEOUT)
1221 {
1222 DPRINT1("Failed to wait for page op, status = %x\n", Status);
1223 KeBugCheck(0);
1224 }
1225 if (PageOp->Status == STATUS_PENDING)
1226 {
1227 DPRINT1("Woke for page op before completion\n");
1228 KeBugCheck(0);
1229 }
1230 /*
1231 * Restart the operation
1232 */
1233 MmLockAddressSpace(AddressSpace);
1234 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
1235 MmReleasePageOp(PageOp);
1236 DPRINT("Address 0x%.8X\n", Address);
1237 return(STATUS_MM_RESTART_OPERATION);
1238 }
1239
1240 /*
1241 * Release locks now we have the pageop
1242 */
1243 MmUnlockAddressSpace(AddressSpace);
1244
1245 /*
1246 * Allocate a page
1247 */
1248 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &NewPage);
1249 if (!NT_SUCCESS(Status))
1250 {
1251 KeBugCheck(0);
1252 }
1253
1254 /*
1255 * Copy the old page
1256 */
1257 OldPage = MmGetPhysicalAddressForProcess(NULL, Address);
1258
1259 NewAddress = ExAllocatePageWithPhysPage(NewPage);
1260 memcpy(NewAddress, (PVOID)PAddress, PAGE_SIZE);
1261 ExUnmapPage(NewAddress);
1262
1263 /*
1264 * Delete the old entry.
1265 */
1266 MmDeleteVirtualMapping(AddressSpace->Process, Address, FALSE, NULL, NULL);
1267
1268 /*
1269 * Set the PTE to point to the new page
1270 */
1271 MmLockAddressSpace(AddressSpace);
1272 Status = MmCreateVirtualMapping(AddressSpace->Process,
1273 Address,
1274 Region->Protect,
1275 NewPage,
1276 FALSE);
1277 if (Status == STATUS_NO_MEMORY)
1278 {
1279 MmUnlockAddressSpace(AddressSpace);
1280 Status = MmCreateVirtualMapping(AddressSpace->Process,
1281 Address,
1282 Region->Protect,
1283 NewPage,
1284 TRUE);
1285 MmLockAddressSpace(AddressSpace);
1286 }
1287 if (!NT_SUCCESS(Status))
1288 {
1289 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
1290 KeBugCheck(0);
1291 return(Status);
1292 }
1293 MmInsertRmap(NewPage, AddressSpace->Process, (PVOID)PAddress);
1294 if (!NT_SUCCESS(Status))
1295 {
1296 DbgPrint("Unable to create virtual mapping\n");
1297 KeBugCheck(0);
1298 }
1299 if (Locked)
1300 {
1301 MmLockPage(NewPage);
1302 }
1303
1304 /*
1305 * Unshare the old page.
1306 */
1307 MmDeleteRmap(OldPage, AddressSpace->Process, (PVOID)PAddress);
1308 MmLockSectionSegment(Segment);
1309 MmUnsharePageEntrySectionSegment(Section, Segment, Offset, FALSE);
1310 MmUnlockSectionSegment(Segment);
1311 MmReleasePageMemoryConsumer(MC_USER, OldPage);
1312
1313 PageOp->Status = STATUS_SUCCESS;
1314 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
1315 MmReleasePageOp(PageOp);
1316 DPRINT("Address 0x%.8X\n", Address);
1317 return(STATUS_SUCCESS);
1318 }
1319
1320 VOID
1321 MmPageOutDeleteMapping(PVOID Context, PEPROCESS Process, PVOID Address)
1322 {
1323 MM_SECTION_PAGEOUT_CONTEXT* PageOutContext;
1324 BOOL WasDirty;
1325 PHYSICAL_ADDRESS Page;
1326
1327 PageOutContext = (MM_SECTION_PAGEOUT_CONTEXT*)Context;
1328 MmDeleteVirtualMapping(Process,
1329 Address,
1330 FALSE,
1331 &WasDirty,
1332 &Page);
1333 if (WasDirty)
1334 {
1335 PageOutContext->WasDirty = TRUE;
1336 }
1337 if (!PageOutContext->Private)
1338 {
1339 MmUnsharePageEntrySectionSegment(PageOutContext->Section,
1340 PageOutContext->Segment,
1341 PageOutContext->Offset,
1342 PageOutContext->WasDirty);
1343 }
1344 MmReleasePageMemoryConsumer(MC_USER, Page);
1345 DPRINT("PhysicalAddress %I64x, Address %x\n", Page, Address);
1346 }
1347
1348 NTSTATUS
1349 MmPageOutSectionView(PMADDRESS_SPACE AddressSpace,
1350 MEMORY_AREA* MemoryArea,
1351 PVOID Address,
1352 PMM_PAGEOP PageOp)
1353 {
1354 PHYSICAL_ADDRESS PhysicalAddress;
1355 MM_SECTION_PAGEOUT_CONTEXT Context;
1356 SWAPENTRY SwapEntry;
1357 PMDL Mdl;
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)(Address - (ULONG)MemoryArea->BaseAddress);
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 {
1383 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
1384
1385 /*
1386 * If the file system is letting us go directly to the cache and the
1387 * memory area was mapped at an offset in the file which is page aligned
1388 * then note this is a direct mapped page.
1389 */
1390 if (FileObject->Flags & FO_DIRECT_CACHE_PAGING_READ &&
1391 (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 PhysicalAddress =
1422 MmGetPhysicalAddressForProcess(AddressSpace->Process, Address);
1423 SwapEntry = MmGetSavedSwapEntryPage(PhysicalAddress);
1424
1425 /*
1426 * Prepare the context structure for the rmap delete call.
1427 */
1428 Context.WasDirty = FALSE;
1429 if (Context.Segment->Characteristics & IMAGE_SECTION_CHAR_BSS ||
1430 IS_SWAP_FROM_SSE(Entry) ||
1431 (LONGLONG)PAGE_FROM_SSE(Entry) != PhysicalAddress.QuadPart)
1432 {
1433 Context.Private = TRUE;
1434 }
1435 else
1436 {
1437 Context.Private = FALSE;
1438 }
1439
1440 /*
1441 * Paging out data mapped read-only is easy.
1442 */
1443 if (Context.Segment->Protection & (PAGE_READONLY|PAGE_EXECUTE_READ))
1444 {
1445 /*
1446 * Read-only data should never be in the swapfile.
1447 */
1448 if (SwapEntry != 0)
1449 {
1450 DPRINT1("SwapEntry != 0 was 0x%.8X at address 0x%.8X, "
1451 "paddress 0x%.8X\n", SwapEntry, Address,
1452 PhysicalAddress);
1453 KeBugCheck(0);
1454 }
1455
1456 /*
1457 * Read-only data should never be COWed
1458 */
1459 if (Context.Private)
1460 {
1461 DPRINT1("Had private copy of read-only page.\n");
1462 KeBugCheck(0);
1463 }
1464
1465 /*
1466 * Delete all mappings of this page.
1467 */
1468 MmDeleteAllRmaps(PhysicalAddress, (PVOID)&Context,
1469 MmPageOutDeleteMapping);
1470 if (Context.WasDirty)
1471 {
1472 DPRINT1("Had a dirty page of a read-only page.\n");
1473 KeBugCheck(0);
1474 }
1475
1476 PageOp->Status = STATUS_SUCCESS;
1477 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
1478 MmReleasePageOp(PageOp);
1479 return(STATUS_SUCCESS);
1480 }
1481
1482 /*
1483 * Otherwise we have read-write data.
1484 */
1485
1486 /*
1487 * Take an additional reference to the page or the cache segment.
1488 */
1489 if (DirectMapped && !Context.Private)
1490 {
1491 if(!MiIsPageFromCache(MemoryArea, Context.Offset))
1492 {
1493 DPRINT1("Direct mapped non private page is not associated with the cache.\n")
1494 KeBugCheck(0);
1495 }
1496 }
1497 else
1498 {
1499 MmReferencePage(PhysicalAddress);
1500 }
1501
1502 MmDeleteAllRmaps(PhysicalAddress, (PVOID)&Context, MmPageOutDeleteMapping);
1503
1504 /*
1505 * If this wasn't a private page then we should have reduced the entry to
1506 * zero by deleting all the rmaps.
1507 */
1508 if (!Context.Private && MmGetPageEntrySectionSegment(Context.Segment, Context.Offset) != 0)
1509 {
1510 if (!(Context.Segment->Flags & MM_PAGEFILE_SEGMENT))
1511 {
1512 CHECKPOINT1;
1513 KeBugCheck(0);
1514 }
1515 }
1516
1517 /*
1518 * If the page wasn't dirty then we can just free it as for a readonly page.
1519 * Since we unmapped all the mappings above we know it will not suddenly
1520 * become dirty.
1521 * If the page is from a pagefile section and has no swap entry,
1522 * we can't free the page at this point.
1523 */
1524 SwapEntry = MmGetSavedSwapEntryPage(PhysicalAddress);
1525 if (!Context.WasDirty &&
1526 !(SwapEntry == 0 && Context.Segment->Flags & MM_PAGEFILE_SEGMENT))
1527
1528 {
1529 if (Context.Private)
1530 {
1531 MmSetSavedSwapEntryPage(PhysicalAddress, 0);
1532 if (!(Context.Segment->Characteristics & IMAGE_SECTION_CHAR_BSS) &&
1533 SwapEntry == 0)
1534 {
1535 DPRINT1("Private page, non-dirty but not swapped out "
1536 "process %d address 0x%.8X\n",
1537 AddressSpace->Process ? AddressSpace->Process->UniqueProcessId : 0,
1538 Address);
1539 KeBugCheck(0);
1540 }
1541 else
1542 {
1543 Status = MmCreatePageFileMapping(AddressSpace->Process,
1544 Address,
1545 SwapEntry);
1546 if (!NT_SUCCESS(Status))
1547 {
1548 KeBugCheck(0);
1549 }
1550 }
1551 }
1552 if (DirectMapped && !Context.Private)
1553 {
1554 Status = CcRosUnmapCacheSegment(Bcb, FileOffset, FALSE);
1555 if (!NT_SUCCESS(Status))
1556 {
1557 DPRINT1("CCRosUnmapCacheSegment failed, status = %x\n", Status);
1558 KeBugCheck(0);
1559 }
1560 }
1561 else
1562 {
1563 MmReleasePageMemoryConsumer(MC_USER, PhysicalAddress);
1564 }
1565
1566 PageOp->Status = STATUS_SUCCESS;
1567 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
1568 MmReleasePageOp(PageOp);
1569 return(STATUS_SUCCESS);
1570 }
1571
1572 /*
1573 * If this page was direct mapped from the cache then the cache manager
1574 * will already have taken care of writing it back.
1575 */
1576 if (DirectMapped && !Context.Private)
1577 {
1578 assert(SwapEntry == 0);
1579 Status = CcRosUnmapCacheSegment(Bcb, FileOffset, FALSE);
1580 if (!NT_SUCCESS(Status))
1581 {
1582 DPRINT1("CcRosUnmapCacheSegment failed, status = %x\n", Status);
1583 KeBugCheck(0);
1584 }
1585 PageOp->Status = STATUS_SUCCESS;
1586 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
1587 MmReleasePageOp(PageOp);
1588 return(STATUS_SUCCESS);
1589 }
1590
1591 /*
1592 * If necessary, allocate an entry in the paging file for this page
1593 */
1594 if (SwapEntry == 0)
1595 {
1596 SwapEntry = MmAllocSwapPage();
1597 if (SwapEntry == 0)
1598 {
1599 MmShowOutOfSpaceMessagePagingFile();
1600
1601 /*
1602 * For private pages restore the old mappings.
1603 */
1604 if (Context.Private)
1605 {
1606 Status = MmCreateVirtualMapping(MemoryArea->Process,
1607 Address,
1608 MemoryArea->Attributes,
1609 PhysicalAddress,
1610 FALSE);
1611 MmSetDirtyPage(MemoryArea->Process, Address);
1612 MmInsertRmap(PhysicalAddress,
1613 MemoryArea->Process,
1614 Address);
1615 }
1616 else
1617 {
1618 /*
1619 * For non-private pages if the page wasn't direct mapped then
1620 * set it back into the section segment entry so we don't loose
1621 * our copy. Otherwise it will be handled by the cache manager.
1622 */
1623 Status = MmCreateVirtualMapping(MemoryArea->Process,
1624 Address,
1625 MemoryArea->Attributes,
1626 PhysicalAddress,
1627 FALSE);
1628 MmSetDirtyPage(MemoryArea->Process, Address);
1629 MmInsertRmap(PhysicalAddress,
1630 MemoryArea->Process,
1631 Address);
1632 Entry = MAKE_SSE(PhysicalAddress.u.LowPart, 1);
1633 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, Entry);
1634 }
1635 PageOp->Status = STATUS_UNSUCCESSFUL;
1636 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
1637 MmReleasePageOp(PageOp);
1638 return(STATUS_PAGEFILE_QUOTA);
1639 }
1640 }
1641
1642 /*
1643 * Write the page to the pagefile
1644 */
1645 Mdl = MmCreateMdl(NULL, NULL, PAGE_SIZE);
1646 MmBuildMdlFromPages(Mdl, (PULONG)&PhysicalAddress);
1647 Status = MmWriteToSwapPage(SwapEntry, Mdl);
1648 if (!NT_SUCCESS(Status))
1649 {
1650 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
1651 Status);
1652 /*
1653 * As above: undo our actions.
1654 * FIXME: Also free the swap page.
1655 */
1656 if (Context.Private)
1657 {
1658 Status = MmCreateVirtualMapping(MemoryArea->Process,
1659 Address,
1660 MemoryArea->Attributes,
1661 PhysicalAddress,
1662 FALSE);
1663 MmSetDirtyPage(MemoryArea->Process, Address);
1664 MmInsertRmap(PhysicalAddress,
1665 MemoryArea->Process,
1666 Address);
1667 }
1668 else
1669 {
1670 Status = MmCreateVirtualMapping(MemoryArea->Process,
1671 Address,
1672 MemoryArea->Attributes,
1673 PhysicalAddress,
1674 FALSE);
1675 MmSetDirtyPage(MemoryArea->Process, Address);
1676 MmInsertRmap(PhysicalAddress,
1677 MemoryArea->Process,
1678 Address);
1679 Entry = MAKE_SSE(PhysicalAddress.u.LowPart, 1);
1680 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, Entry);
1681 }
1682 PageOp->Status = STATUS_UNSUCCESSFUL;
1683 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
1684 MmReleasePageOp(PageOp);
1685 return(STATUS_UNSUCCESSFUL);
1686 }
1687
1688 /*
1689 * Otherwise we have succeeded.
1690 */
1691 DPRINT("MM: Wrote section page 0x%.8X to swap!\n", PhysicalAddress);
1692 MmSetSavedSwapEntryPage(PhysicalAddress, 0);
1693 MmReleasePageMemoryConsumer(MC_USER, PhysicalAddress);
1694
1695 if (Context.Private)
1696 {
1697 Status = MmCreatePageFileMapping(MemoryArea->Process,
1698 Address,
1699 SwapEntry);
1700 if (!NT_SUCCESS(Status))
1701 {
1702 KeBugCheck(0);
1703 }
1704 }
1705 else
1706 {
1707 Entry = MAKE_SWAP_SSE(SwapEntry);
1708 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, Entry);
1709 }
1710
1711 PageOp->Status = STATUS_SUCCESS;
1712 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
1713 MmReleasePageOp(PageOp);
1714 return(STATUS_SUCCESS);
1715 }
1716
1717 NTSTATUS
1718 MmWritePageSectionView(PMADDRESS_SPACE AddressSpace,
1719 PMEMORY_AREA MemoryArea,
1720 PVOID Address,
1721 PMM_PAGEOP PageOp)
1722 {
1723 ULONG Offset;
1724 PSECTION_OBJECT Section;
1725 PMM_SECTION_SEGMENT Segment;
1726 PHYSICAL_ADDRESS PhysicalAddress;
1727 SWAPENTRY SwapEntry;
1728 PMDL Mdl;
1729 ULONG Entry;
1730 BOOLEAN Private;
1731 NTSTATUS Status;
1732 PFILE_OBJECT FileObject;
1733 PBCB Bcb = NULL;
1734 BOOLEAN DirectMapped;
1735 BOOLEAN IsImageSection;
1736
1737 Address = (PVOID)PAGE_ROUND_DOWN(Address);
1738
1739 Offset = (ULONG)(Address - (ULONG)MemoryArea->BaseAddress);
1740
1741 /*
1742 * Get the segment and section.
1743 */
1744 Segment = MemoryArea->Data.SectionData.Segment;
1745 Section = MemoryArea->Data.SectionData.Section;
1746 IsImageSection = Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
1747
1748 FileObject = Section->FileObject;
1749 DirectMapped = FALSE;
1750 if (FileObject != NULL)
1751 {
1752 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
1753
1754 /*
1755 * If the file system is letting us go directly to the cache and the
1756 * memory area was mapped at an offset in the file which is page aligned
1757 * then note this is a direct mapped page.
1758 */
1759 if (FileObject->Flags & FO_DIRECT_CACHE_PAGING_READ &&
1760 (Offset + MemoryArea->Data.SectionData.ViewOffset % PAGE_SIZE) == 0 &&
1761 (Offset + PAGE_SIZE <= Segment->RawLength || !IsImageSection))
1762 {
1763 DirectMapped = TRUE;
1764 }
1765 }
1766
1767 /*
1768 * This should never happen since mappings of physical memory are never
1769 * placed in the rmap lists.
1770 */
1771 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
1772 {
1773 DPRINT1("Trying to write back page from physical memory mapped at %X "
1774 "process %d\n", Address,
1775 AddressSpace->Process ? AddressSpace->Process->UniqueProcessId : 0);
1776 KeBugCheck(0);
1777 }
1778
1779 /*
1780 * Get the section segment entry and the physical address.
1781 */
1782 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
1783 if (!MmIsPagePresent(AddressSpace->Process, Address))
1784 {
1785 DPRINT1("Trying to page out not-present page at (%d,0x%.8X).\n",
1786 AddressSpace->Process ? AddressSpace->Process->UniqueProcessId : 0, Address);
1787 KeBugCheck(0);
1788 }
1789 PhysicalAddress =
1790 MmGetPhysicalAddressForProcess(AddressSpace->Process, Address);
1791 SwapEntry = MmGetSavedSwapEntryPage(PhysicalAddress);
1792
1793 /*
1794 * Check for a private (COWed) page.
1795 */
1796 if (Segment->Characteristics & IMAGE_SECTION_CHAR_BSS ||
1797 IS_SWAP_FROM_SSE(Entry) ||
1798 (LONGLONG)PAGE_FROM_SSE(Entry) != PhysicalAddress.QuadPart)
1799 {
1800 Private = TRUE;
1801 }
1802 else
1803 {
1804 Private = FALSE;
1805 }
1806
1807 /*
1808 * Speculatively set all mappings of the page to clean.
1809 */
1810 MmSetCleanAllRmaps(PhysicalAddress);
1811
1812 /*
1813 * If this page was direct mapped from the cache then the cache manager
1814 * will take care of writing it back to disk.
1815 */
1816 if (DirectMapped && !Private)
1817 {
1818 assert(SwapEntry == 0);
1819 CcRosMarkDirtyCacheSegment(Bcb, Offset + MemoryArea->Data.SectionData.ViewOffset);
1820 PageOp->Status = STATUS_SUCCESS;
1821 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
1822 MmReleasePageOp(PageOp);
1823 return(STATUS_SUCCESS);
1824 }
1825
1826 /*
1827 * If necessary, allocate an entry in the paging file for this page
1828 */
1829 if (SwapEntry == 0)
1830 {
1831 SwapEntry = MmAllocSwapPage();
1832 if (SwapEntry == 0)
1833 {
1834 MmSetDirtyAllRmaps(PhysicalAddress);
1835 PageOp->Status = STATUS_UNSUCCESSFUL;
1836 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
1837 MmReleasePageOp(PageOp);
1838 return(STATUS_PAGEFILE_QUOTA);
1839 }
1840 }
1841
1842 /*
1843 * Write the page to the pagefile
1844 */
1845 Mdl = MmCreateMdl(NULL, NULL, PAGE_SIZE);
1846 MmBuildMdlFromPages(Mdl, (PULONG)&PhysicalAddress);
1847 Status = MmWriteToSwapPage(SwapEntry, Mdl);
1848 if (!NT_SUCCESS(Status))
1849 {
1850 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
1851 Status);
1852 MmSetDirtyAllRmaps(PhysicalAddress);
1853 PageOp->Status = STATUS_UNSUCCESSFUL;
1854 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
1855 MmReleasePageOp(PageOp);
1856 return(STATUS_UNSUCCESSFUL);
1857 }
1858
1859 /*
1860 * Otherwise we have succeeded.
1861 */
1862 DPRINT("MM: Wrote section page 0x%.8X to swap!\n", PhysicalAddress);
1863 MmSetSavedSwapEntryPage(PhysicalAddress, SwapEntry);
1864 PageOp->Status = STATUS_SUCCESS;
1865 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
1866 MmReleasePageOp(PageOp);
1867 return(STATUS_SUCCESS);
1868 }
1869
1870 VOID STATIC
1871 MmAlterViewAttributes(PMADDRESS_SPACE AddressSpace,
1872 PVOID BaseAddress,
1873 ULONG RegionSize,
1874 ULONG OldType,
1875 ULONG OldProtect,
1876 ULONG NewType,
1877 ULONG NewProtect)
1878 {
1879 PMEMORY_AREA MemoryArea;
1880 PMM_SECTION_SEGMENT Segment;
1881 BOOL DoCOW = FALSE;
1882 ULONG i;
1883
1884 MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace, BaseAddress);
1885 Segment = MemoryArea->Data.SectionData.Segment;
1886
1887 if ((Segment->WriteCopy || MemoryArea->Data.SectionData.WriteCopyView) &&
1888 (NewProtect == PAGE_READWRITE || NewProtect == PAGE_EXECUTE_READWRITE))
1889 {
1890 DoCOW = TRUE;
1891 }
1892
1893 if (OldProtect != NewProtect)
1894 {
1895 for (i = 0; i < PAGE_ROUND_UP(RegionSize) / PAGE_SIZE; i++)
1896 {
1897 PVOID Address = BaseAddress + (i * PAGE_SIZE);
1898 ULONG Protect = NewProtect;
1899
1900 /*
1901 * If we doing COW for this segment then check if the page is
1902 * already private.
1903 */
1904 if (DoCOW && MmIsPagePresent(AddressSpace->Process, Address))
1905 {
1906 ULONG Offset;
1907 ULONG Entry;
1908 LARGE_INTEGER PhysicalAddress;
1909
1910 Offset = (ULONG)Address - (ULONG)MemoryArea->BaseAddress;
1911 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
1912 PhysicalAddress =
1913 MmGetPhysicalAddressForProcess(AddressSpace->Process, Address);
1914
1915 Protect = PAGE_READONLY;
1916 if ((Segment->Characteristics & IMAGE_SECTION_CHAR_BSS ||
1917 IS_SWAP_FROM_SSE(Entry) ||
1918 (LONGLONG)PAGE_FROM_SSE(Entry) != PhysicalAddress.QuadPart))
1919 {
1920 Protect = NewProtect;
1921 }
1922 }
1923
1924 if (MmIsPagePresent(AddressSpace->Process, Address))
1925 {
1926 MmSetPageProtect(AddressSpace->Process, BaseAddress,
1927 Protect);
1928 }
1929 }
1930 }
1931 }
1932
1933 NTSTATUS
1934 MmProtectSectionView(PMADDRESS_SPACE AddressSpace,
1935 PMEMORY_AREA MemoryArea,
1936 PVOID BaseAddress,
1937 ULONG Length,
1938 ULONG Protect,
1939 PULONG OldProtect)
1940 {
1941 PMM_REGION Region;
1942 NTSTATUS Status;
1943
1944 Length =
1945 min(Length, (ULONG) (MemoryArea->BaseAddress + MemoryArea->Length - BaseAddress));
1946 Region = MmFindRegion(MemoryArea->BaseAddress,
1947 &MemoryArea->Data.SectionData.RegionListHead,
1948 BaseAddress, NULL);
1949 *OldProtect = Region->Protect;
1950 Status = MmAlterRegion(AddressSpace, MemoryArea->BaseAddress,
1951 &MemoryArea->Data.SectionData.RegionListHead,
1952 BaseAddress, Length, Region->Type, Protect,
1953 MmAlterViewAttributes);
1954
1955 return(Status);
1956 }
1957
1958 NTSTATUS STDCALL
1959 MmQuerySectionView(PMEMORY_AREA MemoryArea,
1960 PVOID Address,
1961 PMEMORY_BASIC_INFORMATION Info,
1962 PULONG ResultLength)
1963 {
1964 PMM_REGION Region;
1965 PVOID RegionBaseAddress;
1966
1967 Region = MmFindRegion(MemoryArea->BaseAddress,
1968 &MemoryArea->Data.SectionData.RegionListHead,
1969 Address, &RegionBaseAddress);
1970 if (Region == NULL)
1971 {
1972 return STATUS_UNSUCCESSFUL;
1973 }
1974 Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
1975 Info->AllocationBase = MemoryArea->BaseAddress;
1976 Info->AllocationProtect = MemoryArea->Attributes;
1977 Info->RegionSize = MemoryArea->Length;
1978 Info->State = MEM_COMMIT;
1979 Info->Protect = Region->Protect;
1980 if (MemoryArea->Data.SectionData.Section->AllocationAttributes & SEC_IMAGE)
1981 {
1982 Info->Type = MEM_IMAGE;
1983 }
1984 else
1985 {
1986 Info->Type = MEM_MAPPED;
1987 }
1988
1989 return(STATUS_SUCCESS);
1990 }
1991
1992 VOID STDCALL
1993 MmpDeleteSection(PVOID ObjectBody)
1994 {
1995 PSECTION_OBJECT Section = (PSECTION_OBJECT)ObjectBody;
1996
1997 DPRINT("MmpDeleteSection(ObjectBody %x)\n", ObjectBody);
1998 if (Section->AllocationAttributes & SEC_IMAGE)
1999 {
2000 ULONG i;
2001 ULONG NrSegments;
2002 PMM_SECTION_SEGMENT SectionSegments;
2003
2004 SectionSegments = Section->ImageSection->Segments;
2005 NrSegments = Section->ImageSection->NrSegments;
2006
2007 for (i = 0; i < NrSegments; i++)
2008 {
2009 InterlockedDecrement((LONG *)&SectionSegments[i].ReferenceCount);
2010 }
2011 }
2012 else
2013 {
2014 InterlockedDecrement((LONG *)&Section->Segment->ReferenceCount);
2015 }
2016 if (Section->FileObject != NULL)
2017 {
2018 CcRosDereferenceCache(Section->FileObject);
2019 ObDereferenceObject(Section->FileObject);
2020 Section->FileObject = NULL;
2021 }
2022
2023 if (Section->Segment->Flags & MM_PAGEFILE_SEGMENT)
2024 {
2025 ULONG Offset;
2026 ULONG Length;
2027 ULONG Entry;
2028 PMM_SECTION_SEGMENT Segment;
2029
2030 Segment = Section->Segment;
2031 Length = PAGE_ROUND_UP(Segment->Length);
2032
2033 for (Offset = 0; Offset < Length; Offset += PAGE_SIZE)
2034 {
2035 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
2036 if (Entry)
2037 {
2038 if (IS_SWAP_FROM_SSE(Entry))
2039 {
2040 MmFreeSwapPage(SWAPENTRY_FROM_SSE(Entry));
2041 }
2042 else
2043 {
2044 PHYSICAL_ADDRESS Page = (PHYSICAL_ADDRESS)(LONGLONG)PAGE_FROM_SSE(Entry);
2045 MmReleasePageMemoryConsumer(MC_USER, Page);
2046 }
2047 }
2048 }
2049 MmFreePageTablesSectionSegment(Section->Segment);
2050 ExFreePool(Section->Segment);
2051 }
2052 }
2053
2054 VOID STDCALL
2055 MmpCloseSection(PVOID ObjectBody,
2056 ULONG HandleCount)
2057 {
2058 DPRINT("MmpCloseSection(OB %x, HC %d) RC %d\n",
2059 ObjectBody, HandleCount, ObGetObjectPointerCount(ObjectBody));
2060 }
2061
2062 NTSTATUS STDCALL
2063 MmpCreateSection(PVOID ObjectBody,
2064 PVOID Parent,
2065 PWSTR RemainingPath,
2066 POBJECT_ATTRIBUTES ObjectAttributes)
2067 {
2068 DPRINT("MmpCreateSection(ObjectBody %x, Parent %x, RemainingPath %S)\n",
2069 ObjectBody, Parent, RemainingPath);
2070
2071 if (RemainingPath == NULL)
2072 {
2073 return(STATUS_SUCCESS);
2074 }
2075
2076 if (wcschr(RemainingPath+1, L'\\') != NULL)
2077 {
2078 return(STATUS_UNSUCCESSFUL);
2079 }
2080 return(STATUS_SUCCESS);
2081 }
2082
2083 NTSTATUS
2084 MmCreatePhysicalMemorySection(VOID)
2085 {
2086 HANDLE PhysSectionH;
2087 PSECTION_OBJECT PhysSection;
2088 NTSTATUS Status;
2089 OBJECT_ATTRIBUTES Obj;
2090 UNICODE_STRING Name = UNICODE_STRING_INITIALIZER(L"\\Device\\PhysicalMemory");
2091 LARGE_INTEGER SectionSize;
2092
2093 /*
2094 * Create the section mapping physical memory
2095 */
2096 SectionSize.QuadPart = 0xFFFFFFFF;
2097 InitializeObjectAttributes(&Obj,
2098 &Name,
2099 0,
2100 NULL,
2101 NULL);
2102 Status = NtCreateSection(&PhysSectionH,
2103 SECTION_ALL_ACCESS,
2104 &Obj,
2105 &SectionSize,
2106 PAGE_EXECUTE_READWRITE,
2107 0,
2108 NULL);
2109 if (!NT_SUCCESS(Status))
2110 {
2111 DbgPrint("Failed to create PhysicalMemory section\n");
2112 KeBugCheck(0);
2113 }
2114 Status = ObReferenceObjectByHandle(PhysSectionH,
2115 SECTION_ALL_ACCESS,
2116 NULL,
2117 KernelMode,
2118 (PVOID*)&PhysSection,
2119 NULL);
2120 if (!NT_SUCCESS(Status))
2121 {
2122 DbgPrint("Failed to reference PhysicalMemory section\n");
2123 KeBugCheck(0);
2124 }
2125 PhysSection->AllocationAttributes |= SEC_PHYSICALMEMORY;
2126 ObDereferenceObject((PVOID)PhysSection);
2127
2128 return(STATUS_SUCCESS);
2129 }
2130
2131 NTSTATUS
2132 MmInitSectionImplementation(VOID)
2133 {
2134 MmSectionObjectType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE));
2135
2136 RtlInitUnicodeStringFromLiteral(&MmSectionObjectType->TypeName, L"Section");
2137
2138 MmSectionObjectType->Tag = TAG('S', 'E', 'C', 'T');
2139 MmSectionObjectType->TotalObjects = 0;
2140 MmSectionObjectType->TotalHandles = 0;
2141 MmSectionObjectType->MaxObjects = ULONG_MAX;
2142 MmSectionObjectType->MaxHandles = ULONG_MAX;
2143 MmSectionObjectType->PagedPoolCharge = 0;
2144 MmSectionObjectType->NonpagedPoolCharge = sizeof(SECTION_OBJECT);
2145 MmSectionObjectType->Mapping = &MmpSectionMapping;
2146 MmSectionObjectType->Dump = NULL;
2147 MmSectionObjectType->Open = NULL;
2148 MmSectionObjectType->Close = MmpCloseSection;
2149 MmSectionObjectType->Delete = MmpDeleteSection;
2150 MmSectionObjectType->Parse = NULL;
2151 MmSectionObjectType->Security = NULL;
2152 MmSectionObjectType->QueryName = NULL;
2153 MmSectionObjectType->OkayToClose = NULL;
2154 MmSectionObjectType->Create = MmpCreateSection;
2155 MmSectionObjectType->DuplicationNotify = NULL;
2156
2157 return(STATUS_SUCCESS);
2158 }
2159
2160 NTSTATUS
2161 MmCreatePageFileSection(PHANDLE SectionHandle,
2162 ACCESS_MASK DesiredAccess,
2163 POBJECT_ATTRIBUTES ObjectAttributes,
2164 PLARGE_INTEGER UMaximumSize,
2165 ULONG SectionPageProtection,
2166 ULONG AllocationAttributes)
2167 /*
2168 * Create a section which is backed by the pagefile
2169 */
2170 {
2171 LARGE_INTEGER MaximumSize;
2172 PSECTION_OBJECT Section;
2173 PMM_SECTION_SEGMENT Segment;
2174 NTSTATUS Status;
2175
2176 if (UMaximumSize == NULL)
2177 {
2178 return(STATUS_UNSUCCESSFUL);
2179 }
2180 MaximumSize = *UMaximumSize;
2181
2182 /*
2183 * Check the protection
2184 */
2185 if ((SectionPageProtection & PAGE_FLAGS_VALID_FROM_USER_MODE) !=
2186 SectionPageProtection)
2187 {
2188 return(STATUS_INVALID_PAGE_PROTECTION);
2189 }
2190
2191 /*
2192 * Create the section
2193 */
2194 Status = ObRosCreateObject(SectionHandle,
2195 DesiredAccess,
2196 ObjectAttributes,
2197 MmSectionObjectType,
2198 (PVOID*)&Section);
2199 if (!NT_SUCCESS(Status))
2200 {
2201 return(Status);
2202 }
2203
2204 /*
2205 * Initialize it
2206 */
2207 Section->SectionPageProtection = SectionPageProtection;
2208 Section->AllocationAttributes = AllocationAttributes;
2209 InitializeListHead(&Section->ViewListHead);
2210 KeInitializeSpinLock(&Section->ViewListLock);
2211 Section->FileObject = NULL;
2212 Section->MaximumSize = MaximumSize;
2213 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2214 TAG_MM_SECTION_SEGMENT);
2215 if (Segment == NULL)
2216 {
2217 ZwClose(*SectionHandle);
2218 ObDereferenceObject(Section);
2219 return(STATUS_NO_MEMORY);
2220 }
2221 Section->Segment = Segment;
2222 Segment->ReferenceCount = 1;
2223 ExInitializeFastMutex(&Segment->Lock);
2224 Segment->FileOffset = 0;
2225 Segment->Protection = SectionPageProtection;
2226 Segment->Attributes = AllocationAttributes;
2227 Segment->Length = MaximumSize.u.LowPart;
2228 Segment->Flags = MM_PAGEFILE_SEGMENT;
2229 Segment->WriteCopy = FALSE;
2230 ObDereferenceObject(Section);
2231 return(STATUS_SUCCESS);
2232 }
2233
2234
2235 NTSTATUS
2236 MmCreateDataFileSection(PHANDLE SectionHandle,
2237 ACCESS_MASK DesiredAccess,
2238 POBJECT_ATTRIBUTES ObjectAttributes,
2239 PLARGE_INTEGER UMaximumSize,
2240 ULONG SectionPageProtection,
2241 ULONG AllocationAttributes,
2242 HANDLE FileHandle)
2243 /*
2244 * Create a section backed by a data file
2245 */
2246 {
2247 PSECTION_OBJECT Section;
2248 NTSTATUS Status;
2249 LARGE_INTEGER MaximumSize;
2250 PFILE_OBJECT FileObject;
2251 PMM_SECTION_SEGMENT Segment;
2252 ULONG FileAccess;
2253 IO_STATUS_BLOCK Iosb;
2254 LARGE_INTEGER Offset;
2255 CHAR Buffer;
2256
2257 /*
2258 * Check the protection
2259 */
2260 if ((SectionPageProtection & PAGE_FLAGS_VALID_FROM_USER_MODE) !=
2261 SectionPageProtection)
2262 {
2263 return(STATUS_INVALID_PAGE_PROTECTION);
2264 }
2265
2266 /*
2267 * Read a bit so caching is initiated for the file object.
2268 * This is only needed because MiReadPage currently cannot
2269 * handle non-cached streams.
2270 */
2271 Offset.QuadPart = 0;
2272 Status = ZwReadFile(FileHandle,
2273 NULL,
2274 NULL,
2275 NULL,
2276 &Iosb,
2277 &Buffer,
2278 sizeof (Buffer),
2279 &Offset,
2280 0);
2281 if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE))
2282 {
2283 return(Status);
2284 }
2285
2286 /*
2287 * Create the section
2288 */
2289 Status = ObRosCreateObject(SectionHandle,
2290 DesiredAccess,
2291 ObjectAttributes,
2292 MmSectionObjectType,
2293 (PVOID*)&Section);
2294 if (!NT_SUCCESS(Status))
2295 {
2296 return(Status);
2297 }
2298
2299 /*
2300 * Initialize it
2301 */
2302 Section->SectionPageProtection = SectionPageProtection;
2303 Section->AllocationAttributes = AllocationAttributes;
2304 InitializeListHead(&Section->ViewListHead);
2305 KeInitializeSpinLock(&Section->ViewListLock);
2306
2307 /*
2308 * Check file access required
2309 */
2310 if (SectionPageProtection & PAGE_READWRITE ||
2311 SectionPageProtection & PAGE_EXECUTE_READWRITE)
2312 {
2313 FileAccess = FILE_READ_DATA | FILE_WRITE_DATA;
2314 }
2315 else
2316 {
2317 FileAccess = FILE_READ_DATA;
2318 }
2319
2320 /*
2321 * Reference the file handle
2322 */
2323 Status = ObReferenceObjectByHandle(FileHandle,
2324 FileAccess,
2325 IoFileObjectType,
2326 UserMode,
2327 (PVOID*)&FileObject,
2328 NULL);
2329 if (!NT_SUCCESS(Status))
2330 {
2331 ZwClose(*SectionHandle);
2332 ObDereferenceObject(Section);
2333 return(Status);
2334 }
2335
2336 /*
2337 * We can't do memory mappings if the file system doesn't support the
2338 * standard FCB
2339 */
2340 if (!(FileObject->Flags & FO_FCB_IS_VALID))
2341 {
2342 ZwClose(*SectionHandle);
2343 ObDereferenceObject(Section);
2344 ObDereferenceObject(FileObject);
2345 return(STATUS_INVALID_FILE_FOR_SECTION);
2346 }
2347
2348 /*
2349 * FIXME: Revise this once a locking order for file size changes is
2350 * decided
2351 */
2352 if (UMaximumSize != NULL)
2353 {
2354 MaximumSize = *UMaximumSize;
2355 }
2356 else
2357 {
2358 MaximumSize =
2359 ((PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext)->FileSize;
2360 }
2361
2362 if (MaximumSize.QuadPart >
2363 ((PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext)->FileSize.QuadPart)
2364 {
2365 IO_STATUS_BLOCK Iosb;
2366 Status = NtSetInformationFile(FileHandle,
2367 &Iosb,
2368 &MaximumSize,
2369 sizeof(LARGE_INTEGER),
2370 FileAllocationInformation);
2371 if (!NT_SUCCESS(Status))
2372 {
2373 ZwClose(*SectionHandle);
2374 ObDereferenceObject(Section);
2375 ObDereferenceObject(FileObject);
2376 return(STATUS_SECTION_NOT_EXTENDED);
2377 }
2378 }
2379
2380 /*
2381 * Lock the file
2382 */
2383 Status = KeWaitForSingleObject((PVOID)&FileObject->Lock,
2384 0,
2385 KernelMode,
2386 FALSE,
2387 NULL);
2388 if (Status != STATUS_SUCCESS)
2389 {
2390 ZwClose(*SectionHandle);
2391 ObDereferenceObject(Section);
2392 ObDereferenceObject(FileObject);
2393 return(Status);
2394 }
2395
2396 /*
2397 * If this file hasn't been mapped as a data file before then allocate a
2398 * section segment to describe the data file mapping
2399 */
2400 if (FileObject->SectionObjectPointer->DataSectionObject == NULL)
2401 {
2402 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2403 TAG_MM_SECTION_SEGMENT);
2404 if (Segment == NULL)
2405 {
2406 KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2407 ZwClose(*SectionHandle);
2408 ObDereferenceObject(Section);
2409 ObDereferenceObject(FileObject);
2410 return(STATUS_NO_MEMORY);
2411 }
2412 Section->Segment = Segment;
2413 Segment->ReferenceCount = 1;
2414 ExInitializeFastMutex(&Segment->Lock);
2415 /*
2416 * Set the lock before assigning the segment to the file object
2417 */
2418 ExAcquireFastMutex(&Segment->Lock);
2419 FileObject->SectionObjectPointer->DataSectionObject = (PVOID)Segment;
2420
2421 Segment->FileOffset = 0;
2422 Segment->Protection = 0;
2423 Segment->Attributes = 0;
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 = NULL;
2437 }
2438 else
2439 {
2440 /*
2441 * If the file is already mapped as a data file then we may need
2442 * to extend it
2443 */
2444 Segment =
2445 (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->
2446 DataSectionObject;
2447 Section->Segment = Segment;
2448 InterlockedIncrement((PLONG)&Segment->ReferenceCount);
2449 MmLockSectionSegment(Segment);
2450
2451 if (MaximumSize.u.LowPart > Segment->RawLength &&
2452 !(AllocationAttributes & SEC_RESERVE))
2453 {
2454 Segment->RawLength = MaximumSize.u.LowPart;
2455 Segment->Length = PAGE_ROUND_UP(Segment->RawLength);
2456 }
2457 }
2458 MmUnlockSectionSegment(Segment);
2459 Section->FileObject = FileObject;
2460 Section->MaximumSize = MaximumSize;
2461 CcRosReferenceCache(FileObject);
2462 KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2463 ObDereferenceObject(Section);
2464 return(STATUS_SUCCESS);
2465 }
2466
2467 static ULONG SectionCharacteristicsToProtect[16] =
2468 {
2469 PAGE_NOACCESS, // 0 = NONE
2470 PAGE_NOACCESS, // 1 = SHARED
2471 PAGE_EXECUTE, // 2 = EXECUTABLE
2472 PAGE_EXECUTE, // 3 = EXECUTABLE, SHARED
2473 PAGE_READONLY, // 4 = READABLE
2474 PAGE_READONLY, // 5 = READABLE, SHARED
2475 PAGE_EXECUTE_READ, // 6 = READABLE, EXECUTABLE
2476 PAGE_EXECUTE_READ, // 7 = READABLE, EXECUTABLE, SHARED
2477 PAGE_READWRITE, // 8 = WRITABLE
2478 PAGE_READWRITE, // 9 = WRITABLE, SHARED
2479 PAGE_EXECUTE_READWRITE, // 10 = WRITABLE, EXECUTABLE
2480 PAGE_EXECUTE_READWRITE, // 11 = WRITABLE, EXECUTABLE, SHARED
2481 PAGE_READWRITE, // 12 = WRITABLE, READABLE
2482 PAGE_READWRITE, // 13 = WRITABLE, READABLE, SHARED
2483 PAGE_EXECUTE_READWRITE, // 14 = WRITABLE, READABLE, EXECUTABLE,
2484 PAGE_EXECUTE_READWRITE, // 15 = WRITABLE, READABLE, EXECUTABLE, SHARED
2485 };
2486
2487 NTSTATUS
2488 MmCreateImageSection(PHANDLE SectionHandle,
2489 ACCESS_MASK DesiredAccess,
2490 POBJECT_ATTRIBUTES ObjectAttributes,
2491 PLARGE_INTEGER UMaximumSize,
2492 ULONG SectionPageProtection,
2493 ULONG AllocationAttributes,
2494 HANDLE FileHandle)
2495 {
2496 PSECTION_OBJECT Section;
2497 NTSTATUS Status;
2498 PFILE_OBJECT FileObject;
2499 IMAGE_DOS_HEADER DosHeader;
2500 IO_STATUS_BLOCK Iosb;
2501 LARGE_INTEGER Offset;
2502 IMAGE_NT_HEADERS PEHeader;
2503 PIMAGE_SECTION_HEADER ImageSections;
2504 PMM_SECTION_SEGMENT SectionSegments;
2505 ULONG NrSegments;
2506 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
2507 ULONG i;
2508 ULONG Size;
2509 ULONG Characteristics;
2510 ULONG FileAccess = 0;
2511 /*
2512 * Check the protection
2513 */
2514 if ((SectionPageProtection & PAGE_FLAGS_VALID_FROM_USER_MODE) !=
2515 SectionPageProtection)
2516 {
2517 return(STATUS_INVALID_PAGE_PROTECTION);
2518 }
2519
2520 /*
2521 * Specifying a maximum size is meaningless for an image section
2522 */
2523 if (UMaximumSize != NULL)
2524 {
2525 return(STATUS_INVALID_PARAMETER_4);
2526 }
2527
2528 /*
2529 * Reference the file handle
2530 */
2531 Status = ObReferenceObjectByHandle(FileHandle,
2532 FileAccess,
2533 IoFileObjectType,
2534 UserMode,
2535 (PVOID*)&FileObject,
2536 NULL);
2537 if (!NT_SUCCESS(Status))
2538 {
2539 return Status;
2540 }
2541
2542 /*
2543 * Initialized caching for this file object if previously caching
2544 * was initialized for the same on disk file
2545 */
2546 Status = CcTryToInitializeFileCache(FileObject);
2547
2548 if (!NT_SUCCESS(Status) || FileObject->SectionObjectPointer->ImageSectionObject == NULL)
2549 {
2550 /*
2551 * Read the dos header and check the DOS signature
2552 */
2553 Offset.QuadPart = 0;
2554 Status = ZwReadFile(FileHandle,
2555 NULL,
2556 NULL,
2557 NULL,
2558 &Iosb,
2559 &DosHeader,
2560 sizeof(DosHeader),
2561 &Offset,
2562 0);
2563 if (!NT_SUCCESS(Status))
2564 {
2565 ObDereferenceObject(FileObject);
2566 return(Status);
2567 }
2568
2569 /*
2570 * Check the DOS signature
2571 */
2572 if (Iosb.Information != sizeof(DosHeader) ||
2573 DosHeader.e_magic != IMAGE_DOS_SIGNATURE)
2574 {
2575 ObDereferenceObject(FileObject);
2576 return(STATUS_INVALID_IMAGE_FORMAT);
2577 }
2578
2579 /*
2580 * Read the PE header
2581 */
2582 Offset.QuadPart = DosHeader.e_lfanew;
2583 Status = ZwReadFile(FileHandle,
2584 NULL,
2585 NULL,
2586 NULL,
2587 &Iosb,
2588 &PEHeader,
2589 sizeof(PEHeader),
2590 &Offset,
2591 0);
2592 if (!NT_SUCCESS(Status))
2593 {
2594 ObDereferenceObject(FileObject);
2595 return(Status);
2596 }
2597
2598 /*
2599 * Check the signature
2600 */
2601 if (Iosb.Information != sizeof(PEHeader) ||
2602 PEHeader.Signature != IMAGE_NT_SIGNATURE)
2603 {
2604 ObDereferenceObject(FileObject);
2605 return(STATUS_INVALID_IMAGE_FORMAT);
2606 }
2607
2608 /*
2609 * Read in the section headers
2610 */
2611 Offset.QuadPart = DosHeader.e_lfanew + sizeof(PEHeader);
2612 ImageSections = ExAllocatePool(NonPagedPool,
2613 PEHeader.FileHeader.NumberOfSections *
2614 sizeof(IMAGE_SECTION_HEADER));
2615 if (ImageSections == NULL)
2616 {
2617 ObDereferenceObject(FileObject);
2618 return(STATUS_NO_MEMORY);
2619 }
2620
2621 Status = ZwReadFile(FileHandle,
2622 NULL,
2623 NULL,
2624 NULL,
2625 &Iosb,
2626 ImageSections,
2627 PEHeader.FileHeader.NumberOfSections *
2628 sizeof(IMAGE_SECTION_HEADER),
2629 &Offset,
2630 0);
2631 if (!NT_SUCCESS(Status))
2632 {
2633 ObDereferenceObject(FileObject);
2634 ExFreePool(ImageSections);
2635 return(Status);
2636 }
2637 if (Iosb.Information != (PEHeader.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER)))
2638 {
2639 ObDereferenceObject(FileObject);
2640 ExFreePool(ImageSections);
2641 return(STATUS_INVALID_IMAGE_FORMAT);
2642 }
2643
2644 /*
2645 * Create the section
2646 */
2647 Status = ObRosCreateObject(SectionHandle,
2648 DesiredAccess,
2649 ObjectAttributes,
2650 MmSectionObjectType,
2651 (PVOID*)&Section);
2652 if (!NT_SUCCESS(Status))
2653 {
2654 ObDereferenceObject(FileObject);
2655 ExFreePool(ImageSections);
2656 return(Status);
2657 }
2658
2659 /*
2660 * Initialize it
2661 */
2662 Section->SectionPageProtection = SectionPageProtection;
2663 Section->AllocationAttributes = AllocationAttributes;
2664 InitializeListHead(&Section->ViewListHead);
2665 KeInitializeSpinLock(&Section->ViewListLock);
2666
2667 /*
2668 * Check file access required
2669 */
2670 if (SectionPageProtection & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE))
2671 {
2672 FileAccess = FILE_READ_DATA | FILE_WRITE_DATA;
2673 }
2674 else
2675 {
2676 FileAccess = FILE_READ_DATA;
2677 }
2678
2679 /*
2680 * We can't do memory mappings if the file system doesn't support the
2681 * standard FCB
2682 */
2683 if (!(FileObject->Flags & FO_FCB_IS_VALID))
2684 {
2685 ZwClose(*SectionHandle);
2686 ObDereferenceObject(Section);
2687 ObDereferenceObject(FileObject);
2688 ExFreePool(ImageSections);
2689 return(STATUS_INVALID_FILE_FOR_SECTION);
2690 }
2691
2692 /*
2693 * Lock the file
2694 */
2695 Status = KeWaitForSingleObject((PVOID)&FileObject->Lock,
2696 0,
2697 KernelMode,
2698 FALSE,
2699 NULL);
2700 if (Status != STATUS_SUCCESS)
2701 {
2702 ZwClose(*SectionHandle);
2703 ObDereferenceObject(Section);
2704 ObDereferenceObject(FileObject);
2705 ExFreePool(ImageSections);
2706 return(Status);
2707 }
2708
2709 /*
2710 * allocate the section segments to describe the mapping
2711 */
2712 NrSegments = PEHeader.FileHeader.NumberOfSections + 1;
2713 Size = sizeof(MM_IMAGE_SECTION_OBJECT) + sizeof(MM_SECTION_SEGMENT) * NrSegments;
2714 ImageSectionObject = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_MM_SECTION_SEGMENT);
2715 if (ImageSectionObject == NULL)
2716 {
2717 KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2718 ZwClose(*SectionHandle);
2719 ObDereferenceObject(Section);
2720 ObDereferenceObject(FileObject);
2721 ExFreePool(ImageSections);
2722 return(STATUS_NO_MEMORY);
2723 }
2724 Section->ImageSection = ImageSectionObject;
2725 ImageSectionObject->NrSegments = NrSegments;
2726 ImageSectionObject->ImageBase = (PVOID)PEHeader.OptionalHeader.ImageBase;
2727 ImageSectionObject->EntryPoint = (PVOID)PEHeader.OptionalHeader.AddressOfEntryPoint;
2728 ImageSectionObject->StackReserve = PEHeader.OptionalHeader.SizeOfStackReserve;
2729 ImageSectionObject->StackCommit = PEHeader.OptionalHeader.SizeOfStackCommit;
2730 ImageSectionObject->Subsystem = PEHeader.OptionalHeader.Subsystem;
2731 ImageSectionObject->MinorSubsystemVersion = PEHeader.OptionalHeader.MinorSubsystemVersion;
2732 ImageSectionObject->MajorSubsystemVersion = PEHeader.OptionalHeader.MajorSubsystemVersion;
2733 ImageSectionObject->ImageCharacteristics = PEHeader.FileHeader.Characteristics;
2734 ImageSectionObject->Machine = PEHeader.FileHeader.Machine;
2735 ImageSectionObject->Executable = (PEHeader.OptionalHeader.SizeOfCode != 0);
2736
2737 SectionSegments = ImageSectionObject->Segments;
2738 SectionSegments[0].FileOffset = 0;
2739 SectionSegments[0].Characteristics = IMAGE_SECTION_CHAR_DATA;
2740 SectionSegments[0].Protection = PAGE_READONLY;
2741 SectionSegments[0].RawLength = PAGE_SIZE;
2742 SectionSegments[0].Length = PAGE_SIZE;
2743 SectionSegments[0].Flags = 0;
2744 SectionSegments[0].ReferenceCount = 1;
2745 SectionSegments[0].VirtualAddress = 0;
2746 SectionSegments[0].WriteCopy = FALSE;
2747 ExInitializeFastMutex(&SectionSegments[0].Lock);
2748 for (i = 1; i < NrSegments; i++)
2749 {
2750 SectionSegments[i].FileOffset = ImageSections[i-1].PointerToRawData;
2751 SectionSegments[i].Characteristics = ImageSections[i-1].Characteristics;
2752
2753 /*
2754 * Set up the protection and write copy variables.
2755 */
2756 Characteristics = ImageSections[i - 1].Characteristics;
2757 if (Characteristics & (IMAGE_SECTION_CHAR_READABLE|IMAGE_SECTION_CHAR_WRITABLE|IMAGE_SECTION_CHAR_EXECUTABLE))
2758 {
2759 SectionSegments[i].Protection = SectionCharacteristicsToProtect[Characteristics >> 28];
2760 SectionSegments[i].WriteCopy = !(Characteristics & IMAGE_SECTION_CHAR_SHARED);
2761 }
2762 else if (Characteristics & IMAGE_SECTION_CHAR_CODE)
2763 {
2764 SectionSegments[i].Protection = PAGE_EXECUTE_READ;
2765 SectionSegments[i].WriteCopy = TRUE;
2766 }
2767 else if (Characteristics & IMAGE_SECTION_CHAR_DATA)
2768 {
2769 SectionSegments[i].Protection = PAGE_READWRITE;
2770 SectionSegments[i].WriteCopy = TRUE;
2771 }
2772 else if (Characteristics & IMAGE_SECTION_CHAR_BSS)
2773 {
2774 SectionSegments[i].Protection = PAGE_READWRITE;
2775 SectionSegments[i].WriteCopy = TRUE;
2776 }
2777 else
2778 {
2779 SectionSegments[i].Protection = PAGE_NOACCESS;
2780 SectionSegments[i].WriteCopy = TRUE;
2781 }
2782
2783 /*
2784 * Set up the attributes.
2785 */
2786 if (Characteristics & IMAGE_SECTION_CHAR_CODE)
2787 {
2788 SectionSegments[i].Attributes = 0;
2789 }
2790 else if (Characteristics & IMAGE_SECTION_CHAR_DATA)
2791 {
2792 SectionSegments[i].Attributes = 0;
2793 }
2794 else if (Characteristics & IMAGE_SECTION_CHAR_BSS)
2795 {
2796 SectionSegments[i].Attributes = MM_SECTION_SEGMENT_BSS;
2797 }
2798 else
2799 {
2800 SectionSegments[i].Attributes = 0;
2801 }
2802
2803 SectionSegments[i].RawLength = ImageSections[i-1].SizeOfRawData;
2804 SectionSegments[i].Length = ImageSections[i-1].Misc.VirtualSize;
2805 SectionSegments[i].Flags = 0;
2806 SectionSegments[i].ReferenceCount = 1;
2807 SectionSegments[i].VirtualAddress = (PVOID)ImageSections[i-1].VirtualAddress;
2808 ExInitializeFastMutex(&SectionSegments[i].Lock);
2809 }
2810 if (0 != InterlockedCompareExchange((PLONG)&FileObject->SectionObjectPointer->ImageSectionObject,
2811 (LONG)ImageSectionObject, 0))
2812 {
2813 /*
2814 * An other thread has initialized the some image in the background
2815 */
2816 ExFreePool(ImageSectionObject);
2817 ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
2818 Section->ImageSection = ImageSectionObject;
2819 SectionSegments = ImageSectionObject->Segments;
2820
2821 for (i = 0; i < NrSegments; i++)
2822 {
2823 InterlockedIncrement((LONG *)&SectionSegments[i].ReferenceCount);
2824 }
2825 }
2826 ExFreePool(ImageSections);
2827 }
2828 else
2829 {
2830 /*
2831 * Create the section
2832 */
2833 Status = ObRosCreateObject(SectionHandle,
2834 DesiredAccess,
2835 ObjectAttributes,
2836 MmSectionObjectType,
2837 (PVOID*)&Section);
2838 if (!NT_SUCCESS(Status))
2839 {
2840 ObDereferenceObject(FileObject);
2841 return(Status);
2842 }
2843
2844 /*
2845 * Initialize it
2846 */
2847 Section->SectionPageProtection = SectionPageProtection;
2848 Section->AllocationAttributes = AllocationAttributes;
2849 InitializeListHead(&Section->ViewListHead);
2850 KeInitializeSpinLock(&Section->ViewListLock);
2851
2852 /*
2853 * Check file access required
2854 */
2855 if (SectionPageProtection & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE))
2856 {
2857 FileAccess = FILE_READ_DATA | FILE_WRITE_DATA;
2858 }
2859 else
2860 {
2861 FileAccess = FILE_READ_DATA;
2862 }
2863
2864 /*
2865 * Lock the file
2866 */
2867 Status = KeWaitForSingleObject((PVOID)&FileObject->Lock,
2868 0,
2869 KernelMode,
2870 FALSE,
2871 NULL);
2872 if (Status != STATUS_SUCCESS)
2873 {
2874 ZwClose(*SectionHandle);
2875 ObDereferenceObject(Section);
2876 ObDereferenceObject(FileObject);
2877 return(Status);
2878 }
2879
2880 ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
2881 Section->ImageSection = ImageSectionObject;
2882 SectionSegments = ImageSectionObject->Segments;
2883 NrSegments = ImageSectionObject->NrSegments;
2884
2885 /*
2886 * Otherwise just reference all the section segments
2887 */
2888 for (i = 0; i < NrSegments; i++)
2889 {
2890 InterlockedIncrement((LONG *)&SectionSegments[i].ReferenceCount);
2891 }
2892
2893 }
2894 Section->FileObject = FileObject;
2895 CcRosReferenceCache(FileObject);
2896 KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2897 ObDereferenceObject(Section);
2898 return(STATUS_SUCCESS);
2899 }
2900
2901 /*
2902 * @implemented
2903 */
2904 NTSTATUS STDCALL
2905 NtCreateSection (OUT PHANDLE SectionHandle,
2906 IN ACCESS_MASK DesiredAccess,
2907 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
2908 IN PLARGE_INTEGER MaximumSize OPTIONAL,
2909 IN ULONG SectionPageProtection OPTIONAL,
2910 IN ULONG AllocationAttributes,
2911 IN HANDLE FileHandle OPTIONAL)
2912 {
2913 if (AllocationAttributes & SEC_IMAGE)
2914 {
2915 return(MmCreateImageSection(SectionHandle,
2916 DesiredAccess,
2917 ObjectAttributes,
2918 MaximumSize,
2919 SectionPageProtection,
2920 AllocationAttributes,
2921 FileHandle));
2922 }
2923 else if (FileHandle != NULL)
2924 {
2925 return(MmCreateDataFileSection(SectionHandle,
2926 DesiredAccess,
2927 ObjectAttributes,
2928 MaximumSize,
2929 SectionPageProtection,
2930 AllocationAttributes,
2931 FileHandle));
2932 }
2933 else
2934 {
2935 return(MmCreatePageFileSection(SectionHandle,
2936 DesiredAccess,
2937 ObjectAttributes,
2938 MaximumSize,
2939 SectionPageProtection,
2940 AllocationAttributes));
2941 }
2942 }
2943
2944
2945 /**********************************************************************
2946 * NAME
2947 * NtOpenSection
2948 *
2949 * DESCRIPTION
2950 *
2951 * ARGUMENTS
2952 * SectionHandle
2953 *
2954 * DesiredAccess
2955 *
2956 * ObjectAttributes
2957 *
2958 * RETURN VALUE
2959 *
2960 * REVISIONS
2961 */
2962 NTSTATUS STDCALL
2963 NtOpenSection(PHANDLE SectionHandle,
2964 ACCESS_MASK DesiredAccess,
2965 POBJECT_ATTRIBUTES ObjectAttributes)
2966 {
2967 NTSTATUS Status;
2968
2969 *SectionHandle = 0;
2970
2971 Status = ObOpenObjectByName(ObjectAttributes,
2972 MmSectionObjectType,
2973 NULL,
2974 UserMode,
2975 DesiredAccess,
2976 NULL,
2977 SectionHandle);
2978
2979 return(Status);
2980 }
2981
2982 NTSTATUS STATIC
2983 MmMapViewOfSegment(PEPROCESS Process,
2984 PMADDRESS_SPACE AddressSpace,
2985 PSECTION_OBJECT Section,
2986 PMM_SECTION_SEGMENT Segment,
2987 PVOID* BaseAddress,
2988 ULONG ViewSize,
2989 ULONG Protect,
2990 ULONG ViewOffset,
2991 BOOL TopDown)
2992 {
2993 PMEMORY_AREA MArea;
2994 NTSTATUS Status;
2995 KIRQL oldIrql;
2996
2997 Status = MmCreateMemoryArea(Process,
2998 AddressSpace,
2999 MEMORY_AREA_SECTION_VIEW,
3000 BaseAddress,
3001 ViewSize,
3002 Protect,
3003 &MArea,
3004 FALSE,
3005 TopDown);
3006 if (!NT_SUCCESS(Status))
3007 {
3008 DPRINT1("Mapping between 0x%.8X and 0x%.8X failed.\n",
3009 (*BaseAddress), (*BaseAddress) + ViewSize);
3010 return(Status);
3011 }
3012
3013 KeAcquireSpinLock(&Section->ViewListLock, &oldIrql);
3014 InsertTailList(&Section->ViewListHead,
3015 &MArea->Data.SectionData.ViewListEntry);
3016 KeReleaseSpinLock(&Section->ViewListLock, oldIrql);
3017
3018 ObReferenceObjectByPointer((PVOID)Section,
3019 SECTION_MAP_READ,
3020 NULL,
3021 ExGetPreviousMode());
3022 MArea->Data.SectionData.Segment = Segment;
3023 MArea->Data.SectionData.Section = Section;
3024 MArea->Data.SectionData.ViewOffset = ViewOffset;
3025 MArea->Data.SectionData.WriteCopyView = FALSE;
3026 MmInitialiseRegion(&MArea->Data.SectionData.RegionListHead,
3027 ViewSize, 0, Protect);
3028
3029 return(STATUS_SUCCESS);
3030 }
3031
3032
3033 /**********************************************************************
3034 * NAME EXPORTED
3035 * NtMapViewOfSection
3036 *
3037 * DESCRIPTION
3038 * Maps a view of a section into the virtual address space of a
3039 * process.
3040 *
3041 * ARGUMENTS
3042 * SectionHandle
3043 * Handle of the section.
3044 *
3045 * ProcessHandle
3046 * Handle of the process.
3047 *
3048 * BaseAddress
3049 * Desired base address (or NULL) on entry;
3050 * Actual base address of the view on exit.
3051 *
3052 * ZeroBits
3053 * Number of high order address bits that must be zero.
3054 *
3055 * CommitSize
3056 * Size in bytes of the initially committed section of
3057 * the view.
3058 *
3059 * SectionOffset
3060 * Offset in bytes from the beginning of the section
3061 * to the beginning of the view.
3062 *
3063 * ViewSize
3064 * Desired length of map (or zero to map all) on entry
3065 * Actual length mapped on exit.
3066 *
3067 * InheritDisposition
3068 * Specified how the view is to be shared with
3069 * child processes.
3070 *
3071 * AllocateType
3072 * Type of allocation for the pages.
3073 *
3074 * Protect
3075 * Protection for the committed region of the view.
3076 *
3077 * RETURN VALUE
3078 * Status.
3079 *
3080 * @implemented
3081 */
3082 NTSTATUS STDCALL
3083 NtMapViewOfSection(HANDLE SectionHandle,
3084 HANDLE ProcessHandle,
3085 PVOID* BaseAddress,
3086 ULONG ZeroBits,
3087 ULONG CommitSize,
3088 PLARGE_INTEGER SectionOffset,
3089 PULONG ViewSize,
3090 SECTION_INHERIT InheritDisposition,
3091 ULONG AllocationType,
3092 ULONG Protect)
3093 {
3094 PSECTION_OBJECT Section;
3095 PEPROCESS Process;
3096 NTSTATUS Status;
3097 PMADDRESS_SPACE AddressSpace;
3098
3099 Status = ObReferenceObjectByHandle(ProcessHandle,
3100 PROCESS_VM_OPERATION,
3101 PsProcessType,
3102 UserMode,
3103 (PVOID*)&Process,
3104 NULL);
3105 if (!NT_SUCCESS(Status))
3106 {
3107 return(Status);
3108 }
3109
3110 AddressSpace = &Process->AddressSpace;
3111
3112 Status = ObReferenceObjectByHandle(SectionHandle,
3113 SECTION_MAP_READ,
3114 MmSectionObjectType,
3115 UserMode,
3116 (PVOID*)&Section,
3117 NULL);
3118 if (!(NT_SUCCESS(Status)))
3119 {
3120 DPRINT("ObReference failed rc=%x\n",Status);
3121 ObDereferenceObject(Process);
3122 return(Status);
3123 }
3124
3125 Status = MmMapViewOfSection(Section,
3126 Process,
3127 BaseAddress,
3128 ZeroBits,
3129 CommitSize,
3130 SectionOffset,
3131 ViewSize,
3132 InheritDisposition,
3133 AllocationType,
3134 Protect);
3135
3136 ObDereferenceObject(Section);
3137 ObDereferenceObject(Process);
3138
3139 return(Status);
3140 }
3141
3142 VOID STATIC
3143 MmFreeSectionPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
3144 PHYSICAL_ADDRESS PhysAddr, SWAPENTRY SwapEntry,
3145 BOOLEAN Dirty)
3146 {
3147 PMEMORY_AREA MArea;
3148 ULONG Entry;
3149 PFILE_OBJECT FileObject;
3150 PBCB Bcb;
3151 ULONG Offset;
3152 SWAPENTRY SavedSwapEntry;
3153 PMM_PAGEOP PageOp;
3154 LARGE_INTEGER Timeout;
3155 NTSTATUS Status;
3156 PSECTION_OBJECT Section;
3157 PMM_SECTION_SEGMENT Segment;
3158
3159 MArea = (PMEMORY_AREA)Context;
3160
3161 Address = (PVOID)PAGE_ROUND_DOWN(Address);
3162
3163 Offset = ((ULONG)Address - (ULONG)MArea->BaseAddress);
3164
3165 Section = MArea->Data.SectionData.Section;
3166 Segment = MArea->Data.SectionData.Segment;
3167
3168
3169 PageOp = MmCheckForPageOp(MArea, 0, NULL, Segment, Offset);
3170
3171 while (PageOp)
3172 {
3173 MmUnlockSectionSegment(Segment);
3174 MmUnlockAddressSpace(&MArea->Process->AddressSpace);
3175
3176 Timeout.QuadPart = -100000000LL; // 10 sec
3177 Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
3178 0,
3179 KernelMode,
3180 FALSE,
3181 &Timeout);
3182 if (Status != STATUS_SUCCESS)
3183 {
3184 DPRINT1("Failed to wait for page op, status = %x\n", Status);
3185 KeBugCheck(0);
3186 }
3187
3188 MmLockAddressSpace(&MArea->Process->AddressSpace);
3189 MmLockSectionSegment(Segment);
3190 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
3191 MmReleasePageOp(PageOp);
3192 PageOp = MmCheckForPageOp(MArea, 0, NULL, Segment, Offset);
3193 }
3194
3195 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
3196
3197 /*
3198 * For a dirty, datafile, non-private page mark it as dirty in the
3199 * cache manager.
3200 */
3201 if (Segment->Flags & MM_DATAFILE_SEGMENT)
3202 {
3203 if (PhysAddr.QuadPart == PAGE_FROM_SSE(Entry) && Dirty)
3204 {
3205 FileObject = MemoryArea->Data.SectionData.Section->FileObject;
3206 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
3207 CcRosMarkDirtyCacheSegment(Bcb, Offset);
3208 assert(SwapEntry == 0);
3209 }
3210 }
3211
3212 if (SwapEntry != 0)
3213 {
3214 /*
3215 * Sanity check
3216 */
3217 if (Segment->Flags & MM_PAGEFILE_SEGMENT)
3218 {
3219 DPRINT1("Found a swap entry for a page in a pagefile section.\n");
3220 KeBugCheck(0);
3221 }
3222 MmFreeSwapPage(SwapEntry);
3223 }
3224 else if (PhysAddr.QuadPart != 0)
3225 {
3226 if (IS_SWAP_FROM_SSE(Entry) ||
3227 PhysAddr.QuadPart != (PAGE_FROM_SSE(Entry)))
3228 {
3229 /*
3230 * Sanity check
3231 */
3232 if (Segment->Flags & MM_PAGEFILE_SEGMENT)
3233 {
3234 DPRINT1("Found a private page in a pagefile section.\n");
3235 KeBugCheck(0);
3236 }
3237 /*
3238 * Just dereference private pages
3239 */
3240 SavedSwapEntry = MmGetSavedSwapEntryPage(PhysAddr);
3241 if (SavedSwapEntry != 0)
3242 {
3243 MmFreeSwapPage(SavedSwapEntry);
3244 MmSetSavedSwapEntryPage(PhysAddr, 0);
3245 }
3246 MmDeleteRmap(PhysAddr, MArea->Process, Address);
3247 MmReleasePageMemoryConsumer(MC_USER, PhysAddr);
3248 }
3249 else
3250 {
3251 MmDeleteRmap(PhysAddr, MArea->Process, Address);
3252 MmUnsharePageEntrySectionSegment(Section, Segment, Offset, Dirty);
3253 MmReleasePageMemoryConsumer(MC_USER, PhysAddr);
3254 }
3255 }
3256 }
3257
3258 /*
3259 * @implemented
3260 */
3261 NTSTATUS STDCALL
3262 MmUnmapViewOfSection(PEPROCESS Process,
3263 PVOID BaseAddress)
3264 {
3265 NTSTATUS Status;
3266 PMEMORY_AREA MemoryArea;
3267 PMADDRESS_SPACE AddressSpace;
3268 PSECTION_OBJECT Section;
3269 PMM_SECTION_SEGMENT Segment;
3270 KIRQL oldIrql;
3271 PLIST_ENTRY CurrentEntry;
3272 PMM_REGION CurrentRegion;
3273
3274 assert(Process);
3275
3276 AddressSpace = &Process->AddressSpace;
3277
3278 DPRINT("Opening memory area Process %x BaseAddress %x\n",
3279 Process, BaseAddress);
3280 MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
3281 BaseAddress);
3282 if (MemoryArea == NULL)
3283 {
3284 return(STATUS_UNSUCCESSFUL);
3285 }
3286
3287 MemoryArea->DeleteInProgress = TRUE;
3288 Section = MemoryArea->Data.SectionData.Section;
3289 Segment = MemoryArea->Data.SectionData.Segment;
3290
3291 MmLockSectionSegment(Segment);
3292 KeAcquireSpinLock(&Section->ViewListLock, &oldIrql);
3293 RemoveEntryList(&MemoryArea->Data.SectionData.ViewListEntry);
3294 KeReleaseSpinLock(&Section->ViewListLock, oldIrql);
3295
3296 CurrentEntry = MemoryArea->Data.SectionData.RegionListHead.Flink;
3297 while (CurrentEntry != &MemoryArea->Data.SectionData.RegionListHead)
3298 {
3299 CurrentRegion =
3300 CONTAINING_RECORD(CurrentEntry, MM_REGION, RegionListEntry);
3301 CurrentEntry = CurrentEntry->Flink;
3302 ExFreePool(CurrentRegion);
3303 }
3304
3305 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
3306 {
3307 Status = MmFreeMemoryArea(&Process->AddressSpace,
3308 BaseAddress,
3309 0,
3310 NULL,
3311 NULL);
3312 }
3313 else
3314 {
3315 Status = MmFreeMemoryArea(&Process->AddressSpace,
3316 BaseAddress,
3317 0,
3318 MmFreeSectionPage,
3319 MemoryArea);
3320 }
3321 MmUnlockSectionSegment(Segment);
3322 ObDereferenceObject(Section);
3323 return(STATUS_SUCCESS);
3324 }
3325
3326 /**********************************************************************
3327 * NAME EXPORTED
3328 * NtUnmapViewOfSection
3329 *
3330 * DESCRIPTION
3331 *
3332 * ARGUMENTS
3333 * ProcessHandle
3334 *
3335 * BaseAddress
3336 *
3337 * RETURN VALUE
3338 * Status.
3339 *
3340 * REVISIONS
3341 */
3342 NTSTATUS STDCALL
3343 NtUnmapViewOfSection (HANDLE ProcessHandle,
3344 PVOID BaseAddress)
3345 {
3346 PEPROCESS Process;
3347 NTSTATUS Status;
3348
3349 DPRINT("NtUnmapViewOfSection(ProcessHandle %x, BaseAddress %x)\n",
3350 ProcessHandle, BaseAddress);
3351
3352 DPRINT("Referencing process\n");
3353 Status = ObReferenceObjectByHandle(ProcessHandle,
3354 PROCESS_VM_OPERATION,
3355 PsProcessType,
3356 UserMode,
3357 (PVOID*)&Process,
3358 NULL);
3359 if (!NT_SUCCESS(Status))
3360 {
3361 DPRINT("ObReferenceObjectByHandle failed (Status %x)\n", Status);
3362 return(Status);
3363 }
3364
3365 MmLockAddressSpace(&Process->AddressSpace);
3366 Status = MmUnmapViewOfSection(Process, BaseAddress);
3367 MmUnlockAddressSpace(&Process->AddressSpace);
3368
3369 ObDereferenceObject(Process);
3370
3371 return Status;
3372 }
3373
3374
3375 NTSTATUS STDCALL
3376 NtQuerySection (IN HANDLE SectionHandle,
3377 IN CINT SectionInformationClass,
3378 OUT PVOID SectionInformation,
3379 IN ULONG Length,
3380 OUT PULONG ResultLength)
3381 /*
3382 * FUNCTION: Queries the information of a section object.
3383 * ARGUMENTS:
3384 * SectionHandle = Handle to the section link object
3385 * SectionInformationClass = Index to a certain information structure
3386 * SectionInformation (OUT)= Caller supplies storage for resulting
3387 * information
3388 * Length = Size of the supplied storage
3389 * ResultLength = Data written
3390 * RETURNS: Status
3391 *
3392 */
3393 {
3394 PSECTION_OBJECT Section;
3395 NTSTATUS Status;
3396
3397 Status = ObReferenceObjectByHandle(SectionHandle,
3398 SECTION_MAP_READ,
3399 MmSectionObjectType,
3400 UserMode,
3401 (PVOID*)&Section,
3402 NULL);
3403 if (!(NT_SUCCESS(Status)))
3404 {
3405 return(Status);
3406 }
3407
3408 switch (SectionInformationClass)
3409 {
3410 case SectionBasicInformation:
3411 {
3412 PSECTION_BASIC_INFORMATION Sbi;
3413
3414 if (Length != sizeof(SECTION_BASIC_INFORMATION))
3415 {
3416 ObDereferenceObject(Section);
3417 return(STATUS_INFO_LENGTH_MISMATCH);
3418 }
3419
3420 Sbi = (PSECTION_BASIC_INFORMATION)SectionInformation;
3421
3422 Sbi->BaseAddress = 0;
3423 Sbi->Attributes = 0;
3424 Sbi->Size.QuadPart = 0;
3425
3426 *ResultLength = sizeof(SECTION_BASIC_INFORMATION);
3427 Status = STATUS_SUCCESS;
3428 break;
3429 }
3430
3431 case SectionImageInformation:
3432 {
3433 PSECTION_IMAGE_INFORMATION Sii;
3434
3435 if (Length != sizeof(SECTION_IMAGE_INFORMATION))
3436 {
3437 ObDereferenceObject(Section);
3438 return(STATUS_INFO_LENGTH_MISMATCH);
3439 }
3440
3441 Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation;
3442 memset(Sii, 0, sizeof(SECTION_IMAGE_INFORMATION));
3443 if (Section->AllocationAttributes & SEC_IMAGE)
3444 {
3445 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3446 ImageSectionObject = Section->ImageSection;
3447
3448 Sii->EntryPoint = ImageSectionObject->EntryPoint;
3449 Sii->StackReserve = ImageSectionObject->StackReserve;
3450 Sii->StackCommit = ImageSectionObject->StackCommit;
3451 Sii->Subsystem = ImageSectionObject->Subsystem;
3452 Sii->MinorSubsystemVersion = ImageSectionObject->MinorSubsystemVersion;
3453 Sii->MajorSubsystemVersion = ImageSectionObject->MajorSubsystemVersion;
3454 Sii->Characteristics = ImageSectionObject->ImageCharacteristics;
3455 Sii->ImageNumber = ImageSectionObject->Machine;
3456 Sii->Executable = ImageSectionObject->Executable;
3457 }
3458 *ResultLength = sizeof(SECTION_IMAGE_INFORMATION);
3459 Status = STATUS_SUCCESS;
3460 break;
3461 }
3462
3463 default:
3464 *ResultLength = 0;
3465 Status = STATUS_INVALID_INFO_CLASS;
3466 }
3467 ObDereferenceObject(Section);
3468 return(Status);
3469 }
3470
3471
3472 NTSTATUS STDCALL
3473 NtExtendSection(IN HANDLE SectionHandle,
3474 IN ULONG NewMaximumSize)
3475 {
3476 UNIMPLEMENTED;
3477 return(STATUS_NOT_IMPLEMENTED);
3478 }
3479
3480
3481 /**********************************************************************
3482 * NAME INTERNAL
3483 * MmAllocateSection@4
3484 *
3485 * DESCRIPTION
3486 *
3487 * ARGUMENTS
3488 * Length
3489 *
3490 * RETURN VALUE
3491 *
3492 * NOTE
3493 * Code taken from ntoskrnl/mm/special.c.
3494 *
3495 * REVISIONS
3496 */
3497 PVOID STDCALL
3498 MmAllocateSection (IN ULONG Length)
3499 {
3500 PVOID Result;
3501 MEMORY_AREA* marea;
3502 NTSTATUS Status;
3503 ULONG i;
3504 PMADDRESS_SPACE AddressSpace;
3505
3506 DPRINT("MmAllocateSection(Length %x)\n",Length);
3507
3508 AddressSpace = MmGetKernelAddressSpace();
3509 Result = NULL;
3510 MmLockAddressSpace(AddressSpace);
3511 Status = MmCreateMemoryArea (NULL,
3512 AddressSpace,
3513 MEMORY_AREA_SYSTEM,
3514 &Result,
3515 Length,
3516 0,
3517 &marea,
3518 FALSE,
3519 FALSE);
3520 if (!NT_SUCCESS(Status))
3521 {
3522 MmUnlockAddressSpace(AddressSpace);
3523 return (NULL);
3524 }
3525 MmUnlockAddressSpace(AddressSpace);
3526 DPRINT("Result %p\n",Result);
3527 for (i = 0; i < PAGE_ROUND_UP(Length) / PAGE_SIZE; i++)
3528 {
3529 PHYSICAL_ADDRESS Page;
3530
3531 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &Page);
3532 if (!NT_SUCCESS(Status))
3533 {
3534 DbgPrint("Unable to allocate page\n");
3535 KeBugCheck(0);
3536 }
3537 Status = MmCreateVirtualMapping (NULL,
3538 (Result + (i * PAGE_SIZE)),
3539 PAGE_READWRITE,
3540 Page,
3541 TRUE);
3542 if (!NT_SUCCESS(Status))
3543 {
3544 DbgPrint("Unable to create virtual mapping\n");
3545 KeBugCheck(0);
3546 }
3547 }
3548 return ((PVOID)Result);
3549 }
3550
3551
3552 /**********************************************************************
3553 * NAME EXPORTED
3554 * MmMapViewOfSection
3555 *
3556 * DESCRIPTION
3557 * Maps a view of a section into the virtual address space of a
3558 * process.
3559 *
3560 * ARGUMENTS
3561 * Section
3562 * Pointer to the section object.
3563 *
3564 * ProcessHandle
3565 * Pointer to the process.
3566 *
3567 * BaseAddress
3568 * Desired base address (or NULL) on entry;
3569 * Actual base address of the view on exit.
3570 *
3571 * ZeroBits
3572 * Number of high order address bits that must be zero.
3573 *
3574 * CommitSize
3575 * Size in bytes of the initially committed section of
3576 * the view.
3577 *
3578 * SectionOffset
3579 * Offset in bytes from the beginning of the section
3580 * to the beginning of the view.
3581 *
3582 * ViewSize
3583 * Desired length of map (or zero to map all) on entry
3584 * Actual length mapped on exit.
3585 *
3586 * InheritDisposition
3587 * Specified how the view is to be shared with
3588 * child processes.
3589 *
3590 * AllocationType
3591 * Type of allocation for the pages.
3592 *
3593 * Protect
3594 * Protection for the committed region of the view.
3595 *
3596 * RETURN VALUE
3597 * Status.
3598 *
3599 * @implemented
3600 */
3601 NTSTATUS STDCALL
3602 MmMapViewOfSection(IN PVOID SectionObject,
3603 IN PEPROCESS Process,
3604 IN OUT PVOID *BaseAddress,
3605 IN ULONG ZeroBits,
3606 IN ULONG CommitSize,
3607 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
3608 IN OUT PULONG ViewSize,
3609 IN SECTION_INHERIT InheritDisposition,
3610 IN ULONG AllocationType,
3611 IN ULONG Protect)
3612 {
3613 PSECTION_OBJECT Section;
3614 PMADDRESS_SPACE AddressSpace;
3615 ULONG ViewOffset;
3616 NTSTATUS Status = STATUS_SUCCESS;
3617
3618 assert(Process);
3619
3620 Section = (PSECTION_OBJECT)SectionObject;
3621 AddressSpace = &Process->AddressSpace;
3622
3623 MmLockAddressSpace(AddressSpace);
3624
3625 if (Section->AllocationAttributes & SEC_IMAGE)
3626 {
3627 ULONG i;
3628 ULONG NrSegments;
3629 PVOID ImageBase;
3630 ULONG ImageSize;
3631 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3632 PMM_SECTION_SEGMENT SectionSegments;
3633
3634 ImageSectionObject = Section->ImageSection;
3635 SectionSegments = ImageSectionObject->Segments;
3636 NrSegments = ImageSectionObject->NrSegments;
3637
3638
3639 ImageBase = *BaseAddress;
3640 if (ImageBase == NULL)
3641 {
3642 ImageBase = ImageSectionObject->ImageBase;
3643 }
3644
3645 ImageSize = 0;
3646 for (i = 0; i < NrSegments; i++)
3647 {
3648 if (!(SectionSegments[i].Characteristics & IMAGE_SECTION_NOLOAD))
3649 {
3650 ULONG MaxExtent;
3651 MaxExtent = (ULONG)(SectionSegments[i].VirtualAddress +
3652 SectionSegments[i].Length);
3653 ImageSize = max(ImageSize, MaxExtent);
3654 }
3655 }
3656
3657 /* Check there is enough space to map the section at that point. */
3658 if (MmOpenMemoryAreaByRegion(AddressSpace, ImageBase,
3659 PAGE_ROUND_UP(ImageSize)) != NULL)
3660 {
3661 /* Fail if the user requested a fixed base address. */
3662 if ((*BaseAddress) != NULL)
3663 {
3664 MmUnlockAddressSpace(AddressSpace);
3665 return(STATUS_UNSUCCESSFUL);
3666 }
3667 /* Otherwise find a gap to map the image. */
3668 ImageBase = MmFindGap(AddressSpace, PAGE_ROUND_UP(ImageSize), FALSE);
3669 if (ImageBase == NULL)
3670 {
3671 MmUnlockAddressSpace(AddressSpace);
3672 return(STATUS_UNSUCCESSFUL);
3673 }
3674 }
3675
3676 for (i = 0; i < NrSegments; i++)
3677 {
3678 PVOID SBaseAddress;
3679
3680 if (!(SectionSegments[i].Characteristics & IMAGE_SECTION_NOLOAD))
3681 {
3682 SBaseAddress = (PVOID)
3683 (ImageBase + (ULONG_PTR)SectionSegments[i].VirtualAddress);
3684 MmLockSectionSegment(&SectionSegments[i]);
3685 Status = MmMapViewOfSegment(Process,
3686 AddressSpace,
3687 Section,
3688 &SectionSegments[i],
3689 &SBaseAddress,
3690 SectionSegments[i].Length,
3691 SectionSegments[i].Protection,
3692 (ULONG_PTR)SectionSegments[i].VirtualAddress,
3693 FALSE);
3694 MmUnlockSectionSegment(&SectionSegments[i]);
3695 if (!NT_SUCCESS(Status))
3696 {
3697 MmUnlockAddressSpace(AddressSpace);
3698 return(Status);
3699 }
3700 }
3701 }
3702
3703 *BaseAddress = ImageBase;
3704 }
3705 else
3706 {
3707 if (SectionOffset == NULL)
3708 {
3709 ViewOffset = 0;
3710 }
3711 else
3712 {
3713 ViewOffset = SectionOffset->u.LowPart;
3714 }
3715
3716 if ((ViewOffset % PAGE_SIZE) != 0)
3717 {
3718 MmUnlockAddressSpace(AddressSpace);
3719 return(STATUS_MAPPED_ALIGNMENT);
3720 }
3721
3722 if ((*ViewSize) == 0)
3723 {
3724 (*ViewSize) = Section->MaximumSize.u.LowPart - ViewOffset;
3725 }
3726 else if (((*ViewSize)+ViewOffset) > Section->MaximumSize.u.LowPart)
3727 {
3728 (*ViewSize) = Section->MaximumSize.u.LowPart - ViewOffset;
3729 }
3730
3731 MmLockSectionSegment(Section->Segment);
3732 Status = MmMapViewOfSegment(Process,
3733 AddressSpace,
3734 Section,
3735 Section->Segment,
3736 BaseAddress,
3737 *ViewSize,
3738 Protect,
3739 ViewOffset,
3740 (AllocationType & MEM_TOP_DOWN));
3741 MmUnlockSectionSegment(Section->Segment);
3742 if (!NT_SUCCESS(Status))
3743 {
3744 MmUnlockAddressSpace(AddressSpace);
3745 return(Status);
3746 }
3747 }
3748
3749 MmUnlockAddressSpace(AddressSpace);
3750
3751 return(STATUS_SUCCESS);
3752 }
3753
3754 /*
3755 * @unimplemented
3756 */
3757 BOOLEAN STDCALL
3758 MmCanFileBeTruncated (IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
3759 IN PLARGE_INTEGER NewFileSize)
3760 {
3761 UNIMPLEMENTED;
3762 return (FALSE);
3763 }
3764
3765
3766 /*
3767 * @unimplemented
3768 */
3769 BOOLEAN STDCALL
3770 MmDisableModifiedWriteOfSection (DWORD Unknown0)
3771 {
3772 UNIMPLEMENTED;
3773 return (FALSE);
3774 }
3775
3776 /*
3777 * @implemented
3778 */
3779 BOOLEAN STDCALL
3780 MmFlushImageSection (IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
3781 IN MMFLUSH_TYPE FlushType)
3782 {
3783 switch(FlushType)
3784 {
3785 case MmFlushForDelete:
3786 if (SectionObjectPointer->ImageSectionObject ||
3787 SectionObjectPointer->DataSectionObject)
3788 {
3789 return FALSE;
3790 }
3791 CcRosSetRemoveOnClose(SectionObjectPointer);
3792 return TRUE;
3793 case MmFlushForWrite:
3794 break;
3795 }
3796 return FALSE;
3797 }
3798
3799 /*
3800 * @unimplemented
3801 */
3802 BOOLEAN STDCALL
3803 MmForceSectionClosed (DWORD Unknown0,
3804 DWORD Unknown1)
3805 {
3806 UNIMPLEMENTED;
3807 return (FALSE);
3808 }
3809
3810
3811 /*
3812 * @implemented
3813 */
3814 NTSTATUS STDCALL
3815 MmMapViewInSystemSpace (IN PVOID SectionObject,
3816 OUT PVOID * MappedBase,
3817 IN OUT PULONG ViewSize)
3818 {
3819 PSECTION_OBJECT Section;
3820 PMADDRESS_SPACE AddressSpace;
3821 NTSTATUS Status;
3822
3823 DPRINT("MmMapViewInSystemSpace() called\n");
3824
3825 Section = (PSECTION_OBJECT)SectionObject;
3826 AddressSpace = MmGetKernelAddressSpace();
3827
3828 MmLockAddressSpace(AddressSpace);
3829
3830
3831 if ((*ViewSize) == 0)
3832 {
3833 (*ViewSize) = Section->MaximumSize.u.LowPart;
3834 }
3835 else if ((*ViewSize) > Section->MaximumSize.u.LowPart)
3836 {
3837 (*ViewSize) = Section->MaximumSize.u.LowPart;
3838 }
3839
3840 MmLockSectionSegment(Section->Segment);
3841
3842
3843 Status = MmMapViewOfSegment(NULL,
3844 AddressSpace,
3845 Section,
3846 Section->Segment,
3847 MappedBase,
3848 *ViewSize,
3849 PAGE_READWRITE,
3850 0,
3851 FALSE);
3852
3853 MmUnlockSectionSegment(Section->Segment);
3854 MmUnlockAddressSpace(AddressSpace);
3855
3856 return Status;
3857 }
3858
3859
3860 /*
3861 * @implemented
3862 */
3863 NTSTATUS STDCALL
3864 MmUnmapViewInSystemSpace (IN PVOID MappedBase)
3865 {
3866 PMEMORY_AREA MemoryArea;
3867 PMADDRESS_SPACE AddressSpace;
3868 PSECTION_OBJECT Section;
3869 PMM_SECTION_SEGMENT Segment;
3870 KIRQL oldIrql;
3871 PLIST_ENTRY CurrentEntry;
3872 PMM_REGION CurrentRegion;
3873 NTSTATUS Status;
3874
3875 DPRINT("MmUnmapViewInSystemSpace() called\n");
3876
3877 AddressSpace = MmGetKernelAddressSpace();
3878
3879 DPRINT("Opening memory area at base address %x\n",
3880 MappedBase);
3881 MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
3882 MappedBase);
3883 if (MemoryArea == NULL)
3884 {
3885 return STATUS_UNSUCCESSFUL;
3886 }
3887
3888 MemoryArea->DeleteInProgress = TRUE;
3889 Section = MemoryArea->Data.SectionData.Section;
3890 Segment = MemoryArea->Data.SectionData.Segment;
3891
3892 MmLockSectionSegment(Segment);
3893 KeAcquireSpinLock(&Section->ViewListLock, &oldIrql);
3894 RemoveEntryList(&MemoryArea->Data.SectionData.ViewListEntry);
3895 KeReleaseSpinLock(&Section->ViewListLock, oldIrql);
3896
3897 CurrentEntry = MemoryArea->Data.SectionData.RegionListHead.Flink;
3898 while (CurrentEntry != &MemoryArea->Data.SectionData.RegionListHead)
3899 {
3900 CurrentRegion =
3901 CONTAINING_RECORD(CurrentEntry, MM_REGION, RegionListEntry);
3902 CurrentEntry = CurrentEntry->Flink;
3903 ExFreePool(CurrentRegion);
3904 }
3905
3906 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
3907 {
3908 Status = MmFreeMemoryArea(AddressSpace,
3909 MappedBase,
3910 0,
3911 NULL,
3912 NULL);
3913 }
3914 else
3915 {
3916 Status = MmFreeMemoryArea(AddressSpace,
3917 MappedBase,
3918 0,
3919 MmFreeSectionPage,
3920 MemoryArea);
3921 }
3922
3923 MmUnlockSectionSegment(Segment);
3924 ObDereferenceObject(Section);
3925
3926 return(STATUS_SUCCESS);
3927 }
3928
3929
3930 /*
3931 * @unimplemented
3932 */
3933 NTSTATUS STDCALL
3934 MmSetBankedSection (DWORD Unknown0,
3935 DWORD Unknown1,
3936 DWORD Unknown2,
3937 DWORD Unknown3,
3938 DWORD Unknown4,
3939 DWORD Unknown5)
3940 {
3941 UNIMPLEMENTED;
3942 return (STATUS_NOT_IMPLEMENTED);
3943 }
3944
3945
3946 /**********************************************************************
3947 * NAME EXPORTED
3948 * MmCreateSection@
3949 *
3950 * DESCRIPTION
3951 * Creates a section object.
3952 *
3953 * ARGUMENTS
3954 * SectionObjiect (OUT)
3955 * Caller supplied storage for the resulting pointer
3956 * to a SECTION_OBJECT instance;
3957 *
3958 * DesiredAccess
3959 * Specifies the desired access to the section can be a
3960 * combination of:
3961 * STANDARD_RIGHTS_REQUIRED |
3962 * SECTION_QUERY |
3963 * SECTION_MAP_WRITE |
3964 * SECTION_MAP_READ |
3965 * SECTION_MAP_EXECUTE
3966 *
3967 * ObjectAttributes [OPTIONAL]
3968 * Initialized attributes for the object can be used
3969 * to create a named section;
3970 *
3971 * MaximumSize
3972 * Maximizes the size of the memory section. Must be
3973 * non-NULL for a page-file backed section.
3974 * If value specified for a mapped file and the file is
3975 * not large enough, file will be extended.
3976 *
3977 * SectionPageProtection
3978 * Can be a combination of:
3979 * PAGE_READONLY |
3980 * PAGE_READWRITE |
3981 * PAGE_WRITEONLY |
3982 * PAGE_WRITECOPY
3983 *
3984 * AllocationAttributes
3985 * Can be a combination of:
3986 * SEC_IMAGE |
3987 * SEC_RESERVE
3988 *
3989 * FileHandle
3990 * Handle to a file to create a section mapped to a file
3991 * instead of a memory backed section;
3992 *
3993 * File
3994 * Unknown.
3995 *
3996 * RETURN VALUE
3997 * Status.
3998 *
3999 * @unimplemented
4000 */
4001 NTSTATUS STDCALL
4002 MmCreateSection (OUT PSECTION_OBJECT * SectionObject,
4003 IN ACCESS_MASK DesiredAccess,
4004 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
4005 IN PLARGE_INTEGER MaximumSize,
4006 IN ULONG SectionPageProtection,
4007 IN ULONG AllocationAttributes,
4008 IN HANDLE FileHandle OPTIONAL,
4009 IN PFILE_OBJECT File OPTIONAL)
4010 {
4011 return (STATUS_NOT_IMPLEMENTED);
4012 }
4013
4014 /* EOF */