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