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