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