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