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