/* * Copyright (C) 1998-2005 ReactOS Team (and the authors from the programmers section) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * PROJECT: ReactOS kernel * FILE: ntoskrnl/mm/section.c * PURPOSE: Implements section objects * * PROGRAMMERS: Rex Jolliff * David Welch * Eric Kohl * Emanuele Aliberti * Eugene Ingerman * Casper Hornstrup * KJK::Hyperion * Guido de Jong * Ge van Geldorp * Royce Mitchell III * Filip Navara * Aleksey Bragin * Jason Filby * Thomas Weidenmueller * Gunnar Andre' Dalsnes * Mike Nordell * Alex Ionescu * Gregor Anich * Steven Edwards * Herve Poussineau */ /* INCLUDES *****************************************************************/ #include #define NDEBUG #include #include #if defined (ALLOC_PRAGMA) #pragma alloc_text(INIT, MmCreatePhysicalMemorySection) #pragma alloc_text(INIT, MmInitSectionImplementation) #endif /* TYPES *********************************************************************/ typedef struct { PROS_SECTION_OBJECT Section; PMM_SECTION_SEGMENT Segment; ULONG Offset; BOOLEAN WasDirty; BOOLEAN Private; } MM_SECTION_PAGEOUT_CONTEXT; /* GLOBALS *******************************************************************/ POBJECT_TYPE MmSectionObjectType = NULL; ULONG_PTR MmSubsectionBase; static GENERIC_MAPPING MmpSectionMapping = { STANDARD_RIGHTS_READ | SECTION_MAP_READ | SECTION_QUERY, STANDARD_RIGHTS_WRITE | SECTION_MAP_WRITE, STANDARD_RIGHTS_EXECUTE | SECTION_MAP_EXECUTE, SECTION_ALL_ACCESS}; #define PAGE_FROM_SSE(E) ((E) & 0xFFFFF000) #define PFN_FROM_SSE(E) ((E) >> PAGE_SHIFT) #define SHARE_COUNT_FROM_SSE(E) (((E) & 0x00000FFE) >> 1) #define IS_SWAP_FROM_SSE(E) ((E) & 0x00000001) #define MAX_SHARE_COUNT 0x7FF #define MAKE_SSE(P, C) ((P) | ((C) << 1)) #define SWAPENTRY_FROM_SSE(E) ((E) >> 1) #define MAKE_SWAP_SSE(S) (((S) << 1) | 0x1) static const INFORMATION_CLASS_INFO ExSectionInfoClass[] = { ICI_SQ_SAME( sizeof(SECTION_BASIC_INFORMATION), sizeof(ULONG), ICIF_QUERY ), /* SectionBasicInformation */ ICI_SQ_SAME( sizeof(SECTION_IMAGE_INFORMATION), sizeof(ULONG), ICIF_QUERY ), /* SectionImageInformation */ }; /* FUNCTIONS *****************************************************************/ PFILE_OBJECT NTAPI MmGetFileObjectForSection(IN PROS_SECTION_OBJECT Section) { PAGED_CODE(); ASSERT(Section); /* Return the file object */ return Section->FileObject; // Section->ControlArea->FileObject on NT } NTSTATUS NTAPI MmGetFileNameForSection(IN PROS_SECTION_OBJECT Section, OUT POBJECT_NAME_INFORMATION *ModuleName) { POBJECT_NAME_INFORMATION ObjectNameInfo; NTSTATUS Status; ULONG ReturnLength; /* Make sure it's an image section */ *ModuleName = NULL; if (!(Section->AllocationAttributes & SEC_IMAGE)) { /* It's not, fail */ return STATUS_SECTION_NOT_IMAGE; } /* Allocate memory for our structure */ ObjectNameInfo = ExAllocatePoolWithTag(PagedPool, 1024, ' mM'); if (!ObjectNameInfo) return STATUS_NO_MEMORY; /* Query the name */ Status = ObQueryNameString(Section->FileObject, ObjectNameInfo, 1024, &ReturnLength); if (!NT_SUCCESS(Status)) { /* Failed, free memory */ ExFreePoolWithTag(ObjectNameInfo, ' mM'); return Status; } /* Success */ *ModuleName = ObjectNameInfo; return STATUS_SUCCESS; } NTSTATUS NTAPI MmGetFileNameForAddress(IN PVOID Address, OUT PUNICODE_STRING ModuleName) { PROS_SECTION_OBJECT Section; PMEMORY_AREA MemoryArea; PMMSUPPORT AddressSpace; POBJECT_NAME_INFORMATION ModuleNameInformation; NTSTATUS Status = STATUS_ADDRESS_NOT_ASSOCIATED; /* Get the MM_AVL_TABLE from EPROCESS */ if (Address >= MmSystemRangeStart) { AddressSpace = MmGetKernelAddressSpace(); } else { AddressSpace = &PsGetCurrentProcess()->Vm; } /* Lock address space */ MmLockAddressSpace(AddressSpace); /* Locate the memory area for the process by address */ MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address); /* Make sure it's a section view type */ if ((MemoryArea != NULL) && (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)) { /* Get the section pointer to the SECTION_OBJECT */ Section = MemoryArea->Data.SectionData.Section; /* Unlock address space */ MmUnlockAddressSpace(AddressSpace); /* Get the filename of the section */ Status = MmGetFileNameForSection(Section,&ModuleNameInformation); if (NT_SUCCESS(Status)) { /* Init modulename */ RtlCreateUnicodeString(ModuleName, ModuleNameInformation->Name.Buffer); /* Free temp taged buffer from MmGetFileNameForSection() */ ExFreePoolWithTag(ModuleNameInformation, ' mM'); DPRINT("Found ModuleName %S by address %p\n", ModuleName->Buffer,Address); } } else { /* Unlock address space */ MmUnlockAddressSpace(AddressSpace); } return Status; } /* Note: Mmsp prefix denotes "Memory Manager Section Private". */ /* * FUNCTION: Waits in kernel mode up to ten seconds for an MM_PAGEOP event. * ARGUMENTS: PMM_PAGEOP which event we should wait for. * RETURNS: Status of the wait. */ static NTSTATUS MmspWaitForPageOpCompletionEvent(PMM_PAGEOP PageOp) { LARGE_INTEGER Timeout; #ifdef __GNUC__ /* TODO: Use other macro to check for suffix to use? */ Timeout.QuadPart = -100000000LL; // 10 sec #else Timeout.QuadPart = -100000000; // 10 sec #endif return KeWaitForSingleObject(&PageOp->CompletionEvent, 0, KernelMode, FALSE, &Timeout); } /* * FUNCTION: Sets the page op completion event and releases the page op. * ARGUMENTS: PMM_PAGEOP. * RETURNS: In shorter time than it takes you to even read this * description, so don't even think about geting a mug of coffee. */ static void MmspCompleteAndReleasePageOp(PMM_PAGEOP PageOp) { KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE); MmReleasePageOp(PageOp); } /* * FUNCTION: Waits in kernel mode indefinitely for a file object lock. * ARGUMENTS: PFILE_OBJECT to wait for. * RETURNS: Status of the wait. */ static NTSTATUS MmspWaitForFileLock(PFILE_OBJECT File) { return STATUS_SUCCESS; //return KeWaitForSingleObject(&File->Lock, 0, KernelMode, FALSE, NULL); } VOID MmFreePageTablesSectionSegment(PMM_SECTION_SEGMENT Segment) { ULONG i; if (Segment->Length > NR_SECTION_PAGE_TABLES * PAGE_SIZE) { for (i = 0; i < NR_SECTION_PAGE_TABLES; i++) { if (Segment->PageDirectory.PageTables[i] != NULL) { ExFreePool(Segment->PageDirectory.PageTables[i]); } } } } VOID NTAPI MmFreeSectionSegments(PFILE_OBJECT FileObject) { if (FileObject->SectionObjectPointer->ImageSectionObject != NULL) { PMM_IMAGE_SECTION_OBJECT ImageSectionObject; PMM_SECTION_SEGMENT SectionSegments; ULONG NrSegments; ULONG i; ImageSectionObject = (PMM_IMAGE_SECTION_OBJECT)FileObject->SectionObjectPointer->ImageSectionObject; NrSegments = ImageSectionObject->NrSegments; SectionSegments = ImageSectionObject->Segments; for (i = 0; i < NrSegments; i++) { if (SectionSegments[i].ReferenceCount != 0) { DPRINT1("Image segment %d still referenced (was %d)\n", i, SectionSegments[i].ReferenceCount); KeBugCheck(MEMORY_MANAGEMENT); } MmFreePageTablesSectionSegment(&SectionSegments[i]); } ExFreePool(ImageSectionObject->Segments); ExFreePool(ImageSectionObject); FileObject->SectionObjectPointer->ImageSectionObject = NULL; } if (FileObject->SectionObjectPointer->DataSectionObject != NULL) { PMM_SECTION_SEGMENT Segment; Segment = (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer-> DataSectionObject; if (Segment->ReferenceCount != 0) { DPRINT1("Data segment still referenced\n"); KeBugCheck(MEMORY_MANAGEMENT); } MmFreePageTablesSectionSegment(Segment); ExFreePool(Segment); FileObject->SectionObjectPointer->DataSectionObject = NULL; } } VOID NTAPI MmLockSectionSegment(PMM_SECTION_SEGMENT Segment) { ExAcquireFastMutex(&Segment->Lock); } VOID NTAPI MmUnlockSectionSegment(PMM_SECTION_SEGMENT Segment) { ExReleaseFastMutex(&Segment->Lock); } VOID NTAPI MmSetPageEntrySectionSegment(PMM_SECTION_SEGMENT Segment, ULONG Offset, ULONG Entry) { PSECTION_PAGE_TABLE Table; ULONG DirectoryOffset; ULONG TableOffset; if (Segment->Length <= NR_SECTION_PAGE_TABLES * PAGE_SIZE) { Table = (PSECTION_PAGE_TABLE)&Segment->PageDirectory; } else { DirectoryOffset = PAGE_TO_SECTION_PAGE_DIRECTORY_OFFSET(Offset); Table = Segment->PageDirectory.PageTables[DirectoryOffset]; if (Table == NULL) { Table = Segment->PageDirectory.PageTables[DirectoryOffset] = ExAllocatePoolWithTag(NonPagedPool, sizeof(SECTION_PAGE_TABLE), TAG_SECTION_PAGE_TABLE); if (Table == NULL) { KeBugCheck(MEMORY_MANAGEMENT); } memset(Table, 0, sizeof(SECTION_PAGE_TABLE)); DPRINT("Table %x\n", Table); } } TableOffset = PAGE_TO_SECTION_PAGE_TABLE_OFFSET(Offset); Table->Entry[TableOffset] = Entry; } ULONG NTAPI MmGetPageEntrySectionSegment(PMM_SECTION_SEGMENT Segment, ULONG Offset) { PSECTION_PAGE_TABLE Table; ULONG Entry; ULONG DirectoryOffset; ULONG TableOffset; DPRINT("MmGetPageEntrySection(Segment %x, Offset %x)\n", Segment, Offset); if (Segment->Length <= NR_SECTION_PAGE_TABLES * PAGE_SIZE) { Table = (PSECTION_PAGE_TABLE)&Segment->PageDirectory; } else { DirectoryOffset = PAGE_TO_SECTION_PAGE_DIRECTORY_OFFSET(Offset); Table = Segment->PageDirectory.PageTables[DirectoryOffset]; DPRINT("Table %x\n", Table); if (Table == NULL) { return(0); } } TableOffset = PAGE_TO_SECTION_PAGE_TABLE_OFFSET(Offset); Entry = Table->Entry[TableOffset]; return(Entry); } VOID NTAPI MmSharePageEntrySectionSegment(PMM_SECTION_SEGMENT Segment, ULONG Offset) { ULONG Entry; Entry = MmGetPageEntrySectionSegment(Segment, Offset); if (Entry == 0) { DPRINT1("Entry == 0 for MmSharePageEntrySectionSegment\n"); KeBugCheck(MEMORY_MANAGEMENT); } if (SHARE_COUNT_FROM_SSE(Entry) == MAX_SHARE_COUNT) { DPRINT1("Maximum share count reached\n"); KeBugCheck(MEMORY_MANAGEMENT); } if (IS_SWAP_FROM_SSE(Entry)) { KeBugCheck(MEMORY_MANAGEMENT); } Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) + 1); MmSetPageEntrySectionSegment(Segment, Offset, Entry); } BOOLEAN NTAPI MmUnsharePageEntrySectionSegment(PROS_SECTION_OBJECT Section, PMM_SECTION_SEGMENT Segment, ULONG Offset, BOOLEAN Dirty, BOOLEAN PageOut) { ULONG Entry; BOOLEAN IsDirectMapped = FALSE; Entry = MmGetPageEntrySectionSegment(Segment, Offset); if (Entry == 0) { DPRINT1("Entry == 0 for MmUnsharePageEntrySectionSegment\n"); KeBugCheck(MEMORY_MANAGEMENT); } if (SHARE_COUNT_FROM_SSE(Entry) == 0) { DPRINT1("Zero share count for unshare\n"); KeBugCheck(MEMORY_MANAGEMENT); } if (IS_SWAP_FROM_SSE(Entry)) { KeBugCheck(MEMORY_MANAGEMENT); } Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) - 1); /* * If we reducing the share count of this entry to zero then set the entry * to zero and tell the cache the page is no longer mapped. */ if (SHARE_COUNT_FROM_SSE(Entry) == 0) { PFILE_OBJECT FileObject; PBCB Bcb; SWAPENTRY SavedSwapEntry; PFN_TYPE Page; BOOLEAN IsImageSection; ULONG FileOffset; FileOffset = Offset + Segment->FileOffset; IsImageSection = Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE; Page = PFN_FROM_SSE(Entry); FileObject = Section->FileObject; if (FileObject != NULL && !(Segment->Characteristics & IMAGE_SCN_MEM_SHARED)) { if ((FileOffset % PAGE_SIZE) == 0 && (Offset + PAGE_SIZE <= Segment->RawLength || !IsImageSection)) { NTSTATUS Status; Bcb = FileObject->SectionObjectPointer->SharedCacheMap; IsDirectMapped = TRUE; Status = CcRosUnmapCacheSegment(Bcb, FileOffset, Dirty); if (!NT_SUCCESS(Status)) { DPRINT1("CcRosUnmapCacheSegment failed, status = %x\n", Status); KeBugCheck(MEMORY_MANAGEMENT); } } } SavedSwapEntry = MmGetSavedSwapEntryPage(Page); if (SavedSwapEntry == 0) { if (!PageOut && ((Segment->Flags & MM_PAGEFILE_SEGMENT) || (Segment->Characteristics & IMAGE_SCN_MEM_SHARED))) { /* * FIXME: * Try to page out this page and set the swap entry * within the section segment. There exist no rmap entry * for this page. The pager thread can't page out a * page without a rmap entry. */ MmSetPageEntrySectionSegment(Segment, Offset, Entry); } else { MmSetPageEntrySectionSegment(Segment, Offset, 0); if (!IsDirectMapped) { MmReleasePageMemoryConsumer(MC_USER, Page); } } } else { if ((Segment->Flags & MM_PAGEFILE_SEGMENT) || (Segment->Characteristics & IMAGE_SCN_MEM_SHARED)) { if (!PageOut) { if (Dirty) { /* * FIXME: * We hold all locks. Nobody can do something with the current * process and the current segment (also not within an other process). */ NTSTATUS Status; Status = MmWriteToSwapPage(SavedSwapEntry, Page); if (!NT_SUCCESS(Status)) { DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n", Status); KeBugCheck(MEMORY_MANAGEMENT); } } MmSetPageEntrySectionSegment(Segment, Offset, MAKE_SWAP_SSE(SavedSwapEntry)); MmSetSavedSwapEntryPage(Page, 0); } MmReleasePageMemoryConsumer(MC_USER, Page); } else { DPRINT1("Found a swapentry for a non private page in an image or data file sgment\n"); KeBugCheck(MEMORY_MANAGEMENT); } } } else { MmSetPageEntrySectionSegment(Segment, Offset, Entry); } return(SHARE_COUNT_FROM_SSE(Entry) > 0); } BOOLEAN MiIsPageFromCache(PMEMORY_AREA MemoryArea, ULONG SegOffset) { if (!(MemoryArea->Data.SectionData.Segment->Characteristics & IMAGE_SCN_MEM_SHARED)) { PBCB Bcb; PCACHE_SEGMENT CacheSeg; Bcb = MemoryArea->Data.SectionData.Section->FileObject->SectionObjectPointer->SharedCacheMap; CacheSeg = CcRosLookupCacheSegment(Bcb, SegOffset + MemoryArea->Data.SectionData.Segment->FileOffset); if (CacheSeg) { CcRosReleaseCacheSegment(Bcb, CacheSeg, CacheSeg->Valid, FALSE, TRUE); return TRUE; } } return FALSE; } NTSTATUS NTAPI MiCopyFromUserPage(PFN_TYPE DestPage, PVOID SourceAddress) { PEPROCESS Process; KIRQL Irql; PVOID TempAddress; Process = PsGetCurrentProcess(); TempAddress = MiMapPageInHyperSpace(Process, DestPage, &Irql); if (TempAddress == NULL) { return(STATUS_NO_MEMORY); } memcpy(TempAddress, SourceAddress, PAGE_SIZE); MiUnmapPageInHyperSpace(Process, TempAddress, Irql); return(STATUS_SUCCESS); } NTSTATUS NTAPI MiReadPage(PMEMORY_AREA MemoryArea, ULONG SegOffset, PPFN_TYPE Page) /* * FUNCTION: Read a page for a section backed memory area. * PARAMETERS: * MemoryArea - Memory area to read the page for. * Offset - Offset of the page to read. * Page - Variable that receives a page contains the read data. */ { ULONG BaseOffset; ULONG FileOffset; PVOID BaseAddress; BOOLEAN UptoDate; PCACHE_SEGMENT CacheSeg; PFILE_OBJECT FileObject; NTSTATUS Status; ULONG RawLength; PBCB Bcb; BOOLEAN IsImageSection; ULONG Length; FileObject = MemoryArea->Data.SectionData.Section->FileObject; Bcb = FileObject->SectionObjectPointer->SharedCacheMap; RawLength = MemoryArea->Data.SectionData.Segment->RawLength; FileOffset = SegOffset + MemoryArea->Data.SectionData.Segment->FileOffset; IsImageSection = MemoryArea->Data.SectionData.Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE; ASSERT(Bcb); DPRINT("%S %x\n", FileObject->FileName.Buffer, FileOffset); /* * If the file system is letting us go directly to the cache and the * memory area was mapped at an offset in the file which is page aligned * then get the related cache segment. */ if ((FileOffset % PAGE_SIZE) == 0 && (SegOffset + PAGE_SIZE <= RawLength || !IsImageSection) && !(MemoryArea->Data.SectionData.Segment->Characteristics & IMAGE_SCN_MEM_SHARED)) { /* * Get the related cache segment; we use a lower level interface than * filesystems do because it is safe for us to use an offset with a * alignment less than the file system block size. */ Status = CcRosGetCacheSegment(Bcb, FileOffset, &BaseOffset, &BaseAddress, &UptoDate, &CacheSeg); if (!NT_SUCCESS(Status)) { return(Status); } if (!UptoDate) { /* * If the cache segment isn't up to date then call the file * system to read in the data. */ Status = ReadCacheSegment(CacheSeg); if (!NT_SUCCESS(Status)) { CcRosReleaseCacheSegment(Bcb, CacheSeg, FALSE, FALSE, FALSE); return Status; } } /* * Retrieve the page from the cache segment that we actually want. */ (*Page) = MmGetPhysicalAddress((char*)BaseAddress + FileOffset - BaseOffset).LowPart >> PAGE_SHIFT; CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, FALSE, TRUE); } else { PEPROCESS Process; KIRQL Irql; PVOID PageAddr; ULONG CacheSegOffset; /* * Allocate a page, this is rather complicated by the possibility * we might have to move other things out of memory */ Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, Page); if (!NT_SUCCESS(Status)) { return(Status); } Status = CcRosGetCacheSegment(Bcb, FileOffset, &BaseOffset, &BaseAddress, &UptoDate, &CacheSeg); if (!NT_SUCCESS(Status)) { return(Status); } if (!UptoDate) { /* * If the cache segment isn't up to date then call the file * system to read in the data. */ Status = ReadCacheSegment(CacheSeg); if (!NT_SUCCESS(Status)) { CcRosReleaseCacheSegment(Bcb, CacheSeg, FALSE, FALSE, FALSE); return Status; } } Process = PsGetCurrentProcess(); PageAddr = MiMapPageInHyperSpace(Process, *Page, &Irql); CacheSegOffset = BaseOffset + CacheSeg->Bcb->CacheSegmentSize - FileOffset; Length = RawLength - SegOffset; if (Length <= CacheSegOffset && Length <= PAGE_SIZE) { memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, Length); } else if (CacheSegOffset >= PAGE_SIZE) { memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, PAGE_SIZE); } else { memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, CacheSegOffset); MiUnmapPageInHyperSpace(Process, PageAddr, Irql); CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, FALSE, FALSE); Status = CcRosGetCacheSegment(Bcb, FileOffset + CacheSegOffset, &BaseOffset, &BaseAddress, &UptoDate, &CacheSeg); if (!NT_SUCCESS(Status)) { return(Status); } if (!UptoDate) { /* * If the cache segment isn't up to date then call the file * system to read in the data. */ Status = ReadCacheSegment(CacheSeg); if (!NT_SUCCESS(Status)) { CcRosReleaseCacheSegment(Bcb, CacheSeg, FALSE, FALSE, FALSE); return Status; } } PageAddr = MiMapPageInHyperSpace(Process, *Page, &Irql); if (Length < PAGE_SIZE) { memcpy((char*)PageAddr + CacheSegOffset, BaseAddress, Length - CacheSegOffset); } else { memcpy((char*)PageAddr + CacheSegOffset, BaseAddress, PAGE_SIZE - CacheSegOffset); } } MiUnmapPageInHyperSpace(Process, PageAddr, Irql); CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, FALSE, FALSE); } return(STATUS_SUCCESS); } NTSTATUS NTAPI MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace, MEMORY_AREA* MemoryArea, PVOID Address, BOOLEAN Locked) { ULONG Offset; PFN_TYPE Page; NTSTATUS Status; PVOID PAddress; PROS_SECTION_OBJECT Section; PMM_SECTION_SEGMENT Segment; ULONG Entry; ULONG Entry1; ULONG Attributes; PMM_PAGEOP PageOp; PMM_REGION Region; BOOLEAN HasSwapEntry; PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace); /* * There is a window between taking the page fault and locking the * address space when another thread could load the page so we check * that. */ if (MmIsPagePresent(Process, Address)) { return(STATUS_SUCCESS); } PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE); Offset = (ULONG_PTR)PAddress - (ULONG_PTR)MemoryArea->StartingAddress + MemoryArea->Data.SectionData.ViewOffset; Segment = MemoryArea->Data.SectionData.Segment; Section = MemoryArea->Data.SectionData.Section; Region = MmFindRegion(MemoryArea->StartingAddress, &MemoryArea->Data.SectionData.RegionListHead, Address, NULL); /* * Lock the segment */ MmLockSectionSegment(Segment); /* * Check if this page needs to be mapped COW */ if ((Segment->WriteCopy || MemoryArea->Data.SectionData.WriteCopyView) && (Region->Protect == PAGE_READWRITE || Region->Protect == PAGE_EXECUTE_READWRITE)) { Attributes = Region->Protect == PAGE_READWRITE ? PAGE_READONLY : PAGE_EXECUTE_READ; } else { Attributes = Region->Protect; } /* * Get or create a page operation descriptor */ PageOp = MmGetPageOp(MemoryArea, NULL, 0, Segment, Offset, MM_PAGEOP_PAGEIN, FALSE); if (PageOp == NULL) { DPRINT1("MmGetPageOp failed\n"); KeBugCheck(MEMORY_MANAGEMENT); } /* * Check if someone else is already handling this fault, if so wait * for them */ if (PageOp->Thread != PsGetCurrentThread()) { MmUnlockSectionSegment(Segment); MmUnlockAddressSpace(AddressSpace); Status = MmspWaitForPageOpCompletionEvent(PageOp); /* * Check for various strange conditions */ if (Status != STATUS_SUCCESS) { DPRINT1("Failed to wait for page op, status = %x\n", Status); KeBugCheck(MEMORY_MANAGEMENT); } if (PageOp->Status == STATUS_PENDING) { DPRINT1("Woke for page op before completion\n"); KeBugCheck(MEMORY_MANAGEMENT); } MmLockAddressSpace(AddressSpace); /* * If this wasn't a pagein then restart the operation */ if (PageOp->OpType != MM_PAGEOP_PAGEIN) { MmspCompleteAndReleasePageOp(PageOp); DPRINT("Address 0x%.8X\n", Address); return(STATUS_MM_RESTART_OPERATION); } /* * If the thread handling this fault has failed then we don't retry */ if (!NT_SUCCESS(PageOp->Status)) { Status = PageOp->Status; MmspCompleteAndReleasePageOp(PageOp); DPRINT("Address 0x%.8X\n", Address); return(Status); } MmLockSectionSegment(Segment); /* * If the completed fault was for another address space then set the * page in this one. */ if (!MmIsPagePresent(Process, Address)) { Entry = MmGetPageEntrySectionSegment(Segment, Offset); HasSwapEntry = MmIsPageSwapEntry(Process, (PVOID)PAddress); if (PAGE_FROM_SSE(Entry) == 0 || HasSwapEntry) { /* * The page was a private page in another or in our address space */ MmUnlockSectionSegment(Segment); MmspCompleteAndReleasePageOp(PageOp); return(STATUS_MM_RESTART_OPERATION); } Page = PFN_FROM_SSE(Entry); MmSharePageEntrySectionSegment(Segment, Offset); /* FIXME: Should we call MmCreateVirtualMappingUnsafe if * (Section->AllocationAttributes & SEC_PHYSICALMEMORY) is true? */ Status = MmCreateVirtualMapping(Process, Address, Attributes, &Page, 1); if (!NT_SUCCESS(Status)) { DPRINT1("Unable to create virtual mapping\n"); KeBugCheck(MEMORY_MANAGEMENT); } MmInsertRmap(Page, Process, (PVOID)PAddress); } MmUnlockSectionSegment(Segment); PageOp->Status = STATUS_SUCCESS; MmspCompleteAndReleasePageOp(PageOp); DPRINT("Address 0x%.8X\n", Address); return(STATUS_SUCCESS); } HasSwapEntry = MmIsPageSwapEntry(Process, (PVOID)PAddress); if (HasSwapEntry) { /* * Must be private page we have swapped out. */ SWAPENTRY SwapEntry; /* * Sanity check */ if (Segment->Flags & MM_PAGEFILE_SEGMENT) { DPRINT1("Found a swaped out private page in a pagefile section.\n"); KeBugCheck(MEMORY_MANAGEMENT); } MmUnlockSectionSegment(Segment); MmDeletePageFileMapping(Process, (PVOID)PAddress, &SwapEntry); MmUnlockAddressSpace(AddressSpace); Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page); if (!NT_SUCCESS(Status)) { KeBugCheck(MEMORY_MANAGEMENT); } Status = MmReadFromSwapPage(SwapEntry, Page); if (!NT_SUCCESS(Status)) { DPRINT1("MmReadFromSwapPage failed, status = %x\n", Status); KeBugCheck(MEMORY_MANAGEMENT); } MmLockAddressSpace(AddressSpace); Status = MmCreateVirtualMapping(Process, Address, Region->Protect, &Page, 1); if (!NT_SUCCESS(Status)) { DPRINT("MmCreateVirtualMapping failed, not out of memory\n"); KeBugCheck(MEMORY_MANAGEMENT); return(Status); } /* * Store the swap entry for later use. */ MmSetSavedSwapEntryPage(Page, SwapEntry); /* * Add the page to the process's working set */ MmInsertRmap(Page, Process, (PVOID)PAddress); /* * Finish the operation */ PageOp->Status = STATUS_SUCCESS; MmspCompleteAndReleasePageOp(PageOp); DPRINT("Address 0x%.8X\n", Address); return(STATUS_SUCCESS); } /* * Satisfying a page fault on a map of /Device/PhysicalMemory is easy */ if (Section->AllocationAttributes & SEC_PHYSICALMEMORY) { MmUnlockSectionSegment(Segment); /* * Just map the desired physical page */ Page = Offset >> PAGE_SHIFT; Status = MmCreateVirtualMappingUnsafe(Process, Address, Region->Protect, &Page, 1); if (!NT_SUCCESS(Status)) { DPRINT("MmCreateVirtualMappingUnsafe failed, not out of memory\n"); KeBugCheck(MEMORY_MANAGEMENT); return(Status); } /* * Cleanup and release locks */ PageOp->Status = STATUS_SUCCESS; MmspCompleteAndReleasePageOp(PageOp); DPRINT("Address 0x%.8X\n", Address); return(STATUS_SUCCESS); } /* * Map anonymous memory for BSS sections */ if (Segment->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) { MmUnlockSectionSegment(Segment); Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page); if (!NT_SUCCESS(Status)) { MmUnlockAddressSpace(AddressSpace); Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page); MmLockAddressSpace(AddressSpace); } if (!NT_SUCCESS(Status)) { KeBugCheck(MEMORY_MANAGEMENT); } Status = MmCreateVirtualMapping(Process, Address, Region->Protect, &Page, 1); if (!NT_SUCCESS(Status)) { DPRINT("MmCreateVirtualMapping failed, not out of memory\n"); KeBugCheck(MEMORY_MANAGEMENT); return(Status); } MmInsertRmap(Page, Process, (PVOID)PAddress); /* * Cleanup and release locks */ PageOp->Status = STATUS_SUCCESS; MmspCompleteAndReleasePageOp(PageOp); DPRINT("Address 0x%.8X\n", Address); return(STATUS_SUCCESS); } /* * Get the entry corresponding to the offset within the section */ Entry = MmGetPageEntrySectionSegment(Segment, Offset); if (Entry == 0) { /* * If the entry is zero (and it can't change because we have * locked the segment) then we need to load the page. */ /* * Release all our locks and read in the page from disk */ MmUnlockSectionSegment(Segment); MmUnlockAddressSpace(AddressSpace); if ((Segment->Flags & MM_PAGEFILE_SEGMENT) || (Offset >= PAGE_ROUND_UP(Segment->RawLength) && Section->AllocationAttributes & SEC_IMAGE)) { Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page); if (!NT_SUCCESS(Status)) { DPRINT1("MmRequestPageMemoryConsumer failed (Status %x)\n", Status); } } else { Status = MiReadPage(MemoryArea, Offset, &Page); if (!NT_SUCCESS(Status)) { DPRINT1("MiReadPage failed (Status %x)\n", Status); } } if (!NT_SUCCESS(Status)) { /* * FIXME: What do we know in this case? */ /* * Cleanup and release locks */ MmLockAddressSpace(AddressSpace); PageOp->Status = Status; MmspCompleteAndReleasePageOp(PageOp); DPRINT("Address 0x%.8X\n", Address); return(Status); } /* * Relock the address space and segment */ MmLockAddressSpace(AddressSpace); MmLockSectionSegment(Segment); /* * Check the entry. No one should change the status of a page * that has a pending page-in. */ Entry1 = MmGetPageEntrySectionSegment(Segment, Offset); if (Entry != Entry1) { DPRINT1("Someone changed ppte entry while we slept\n"); KeBugCheck(MEMORY_MANAGEMENT); } /* * Mark the offset within the section as having valid, in-memory * data */ Entry = MAKE_SSE(Page << PAGE_SHIFT, 1); MmSetPageEntrySectionSegment(Segment, Offset, Entry); MmUnlockSectionSegment(Segment); Status = MmCreateVirtualMapping(Process, Address, Attributes, &Page, 1); if (!NT_SUCCESS(Status)) { DPRINT1("Unable to create virtual mapping\n"); KeBugCheck(MEMORY_MANAGEMENT); } MmInsertRmap(Page, Process, (PVOID)PAddress); PageOp->Status = STATUS_SUCCESS; MmspCompleteAndReleasePageOp(PageOp); DPRINT("Address 0x%.8X\n", Address); return(STATUS_SUCCESS); } else if (IS_SWAP_FROM_SSE(Entry)) { SWAPENTRY SwapEntry; SwapEntry = SWAPENTRY_FROM_SSE(Entry); /* * Release all our locks and read in the page from disk */ MmUnlockSectionSegment(Segment); MmUnlockAddressSpace(AddressSpace); Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page); if (!NT_SUCCESS(Status)) { KeBugCheck(MEMORY_MANAGEMENT); } Status = MmReadFromSwapPage(SwapEntry, Page); if (!NT_SUCCESS(Status)) { KeBugCheck(MEMORY_MANAGEMENT); } /* * Relock the address space and segment */ MmLockAddressSpace(AddressSpace); MmLockSectionSegment(Segment); /* * Check the entry. No one should change the status of a page * that has a pending page-in. */ Entry1 = MmGetPageEntrySectionSegment(Segment, Offset); if (Entry != Entry1) { DPRINT1("Someone changed ppte entry while we slept\n"); KeBugCheck(MEMORY_MANAGEMENT); } /* * Mark the offset within the section as having valid, in-memory * data */ Entry = MAKE_SSE(Page << PAGE_SHIFT, 1); MmSetPageEntrySectionSegment(Segment, Offset, Entry); MmUnlockSectionSegment(Segment); /* * Save the swap entry. */ MmSetSavedSwapEntryPage(Page, SwapEntry); Status = MmCreateVirtualMapping(Process, Address, Region->Protect, &Page, 1); if (!NT_SUCCESS(Status)) { DPRINT1("Unable to create virtual mapping\n"); KeBugCheck(MEMORY_MANAGEMENT); } MmInsertRmap(Page, Process, (PVOID)PAddress); PageOp->Status = STATUS_SUCCESS; MmspCompleteAndReleasePageOp(PageOp); DPRINT("Address 0x%.8X\n", Address); return(STATUS_SUCCESS); } else { /* * If the section offset is already in-memory and valid then just * take another reference to the page */ Page = PFN_FROM_SSE(Entry); MmSharePageEntrySectionSegment(Segment, Offset); MmUnlockSectionSegment(Segment); Status = MmCreateVirtualMapping(Process, Address, Attributes, &Page, 1); if (!NT_SUCCESS(Status)) { DPRINT1("Unable to create virtual mapping\n"); KeBugCheck(MEMORY_MANAGEMENT); } MmInsertRmap(Page, Process, (PVOID)PAddress); PageOp->Status = STATUS_SUCCESS; MmspCompleteAndReleasePageOp(PageOp); DPRINT("Address 0x%.8X\n", Address); return(STATUS_SUCCESS); } } NTSTATUS NTAPI MmAccessFaultSectionView(PMMSUPPORT AddressSpace, MEMORY_AREA* MemoryArea, PVOID Address, BOOLEAN Locked) { PMM_SECTION_SEGMENT Segment; PROS_SECTION_OBJECT Section; PFN_TYPE OldPage; PFN_TYPE NewPage; NTSTATUS Status; PVOID PAddress; ULONG Offset; PMM_PAGEOP PageOp; PMM_REGION Region; ULONG Entry; PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace); DPRINT("MmAccessFaultSectionView(%x, %x, %x, %x)\n", AddressSpace, MemoryArea, Address, Locked); /* * Check if the page has been paged out or has already been set readwrite */ if (!MmIsPagePresent(Process, Address) || MmGetPageProtect(Process, Address) & PAGE_READWRITE) { DPRINT("Address 0x%.8X\n", Address); return(STATUS_SUCCESS); } /* * Find the offset of the page */ PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE); Offset = (ULONG_PTR)PAddress - (ULONG_PTR)MemoryArea->StartingAddress + MemoryArea->Data.SectionData.ViewOffset; Segment = MemoryArea->Data.SectionData.Segment; Section = MemoryArea->Data.SectionData.Section; Region = MmFindRegion(MemoryArea->StartingAddress, &MemoryArea->Data.SectionData.RegionListHead, Address, NULL); /* * Lock the segment */ MmLockSectionSegment(Segment); OldPage = MmGetPfnForProcess(NULL, Address); Entry = MmGetPageEntrySectionSegment(Segment, Offset); MmUnlockSectionSegment(Segment); /* * Check if we are doing COW */ if (!((Segment->WriteCopy || MemoryArea->Data.SectionData.WriteCopyView) && (Region->Protect == PAGE_READWRITE || Region->Protect == PAGE_EXECUTE_READWRITE))) { DPRINT("Address 0x%.8X\n", Address); return(STATUS_ACCESS_VIOLATION); } if (IS_SWAP_FROM_SSE(Entry) || PFN_FROM_SSE(Entry) != OldPage) { /* This is a private page. We must only change the page protection. */ MmSetPageProtect(Process, PAddress, Region->Protect); return(STATUS_SUCCESS); } /* * Get or create a pageop */ PageOp = MmGetPageOp(MemoryArea, NULL, 0, Segment, Offset, MM_PAGEOP_ACCESSFAULT, FALSE); if (PageOp == NULL) { DPRINT1("MmGetPageOp failed\n"); KeBugCheck(MEMORY_MANAGEMENT); } /* * Wait for any other operations to complete */ if (PageOp->Thread != PsGetCurrentThread()) { MmUnlockAddressSpace(AddressSpace); Status = MmspWaitForPageOpCompletionEvent(PageOp); /* * Check for various strange conditions */ if (Status == STATUS_TIMEOUT) { DPRINT1("Failed to wait for page op, status = %x\n", Status); KeBugCheck(MEMORY_MANAGEMENT); } if (PageOp->Status == STATUS_PENDING) { DPRINT1("Woke for page op before completion\n"); KeBugCheck(MEMORY_MANAGEMENT); } /* * Restart the operation */ MmLockAddressSpace(AddressSpace); MmspCompleteAndReleasePageOp(PageOp); DPRINT("Address 0x%.8X\n", Address); return(STATUS_MM_RESTART_OPERATION); } /* * Release locks now we have the pageop */ MmUnlockAddressSpace(AddressSpace); /* * Allocate a page */ Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &NewPage); if (!NT_SUCCESS(Status)) { KeBugCheck(MEMORY_MANAGEMENT); } /* * Copy the old page */ MiCopyFromUserPage(NewPage, PAddress); MmLockAddressSpace(AddressSpace); /* * Delete the old entry. */ MmDeleteVirtualMapping(Process, Address, FALSE, NULL, NULL); /* * Set the PTE to point to the new page */ Status = MmCreateVirtualMapping(Process, Address, Region->Protect, &NewPage, 1); if (!NT_SUCCESS(Status)) { DPRINT("MmCreateVirtualMapping failed, not out of memory\n"); KeBugCheck(MEMORY_MANAGEMENT); return(Status); } if (!NT_SUCCESS(Status)) { DPRINT1("Unable to create virtual mapping\n"); KeBugCheck(MEMORY_MANAGEMENT); } /* * Unshare the old page. */ MmDeleteRmap(OldPage, Process, PAddress); MmInsertRmap(NewPage, Process, PAddress); MmLockSectionSegment(Segment); MmUnsharePageEntrySectionSegment(Section, Segment, Offset, FALSE, FALSE); MmUnlockSectionSegment(Segment); PageOp->Status = STATUS_SUCCESS; MmspCompleteAndReleasePageOp(PageOp); DPRINT("Address 0x%.8X\n", Address); return(STATUS_SUCCESS); } VOID MmPageOutDeleteMapping(PVOID Context, PEPROCESS Process, PVOID Address) { MM_SECTION_PAGEOUT_CONTEXT* PageOutContext; BOOLEAN WasDirty; PFN_TYPE Page; PageOutContext = (MM_SECTION_PAGEOUT_CONTEXT*)Context; if (Process) { MmLockAddressSpace(&Process->Vm); } MmDeleteVirtualMapping(Process, Address, FALSE, &WasDirty, &Page); if (WasDirty) { PageOutContext->WasDirty = TRUE; } if (!PageOutContext->Private) { MmLockSectionSegment(PageOutContext->Segment); MmUnsharePageEntrySectionSegment((PROS_SECTION_OBJECT)PageOutContext->Section, PageOutContext->Segment, PageOutContext->Offset, PageOutContext->WasDirty, TRUE); MmUnlockSectionSegment(PageOutContext->Segment); } if (Process) { MmUnlockAddressSpace(&Process->Vm); } if (PageOutContext->Private) { MmReleasePageMemoryConsumer(MC_USER, Page); } DPRINT("PhysicalAddress %x, Address %x\n", Page << PAGE_SHIFT, Address); } NTSTATUS NTAPI MmPageOutSectionView(PMMSUPPORT AddressSpace, MEMORY_AREA* MemoryArea, PVOID Address, PMM_PAGEOP PageOp) { PFN_TYPE Page; MM_SECTION_PAGEOUT_CONTEXT Context; SWAPENTRY SwapEntry; ULONG Entry; ULONG FileOffset; NTSTATUS Status; PFILE_OBJECT FileObject; PBCB Bcb = NULL; BOOLEAN DirectMapped; BOOLEAN IsImageSection; PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace); KIRQL OldIrql; Address = (PVOID)PAGE_ROUND_DOWN(Address); /* * Get the segment and section. */ Context.Segment = MemoryArea->Data.SectionData.Segment; Context.Section = MemoryArea->Data.SectionData.Section; Context.Offset = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress + MemoryArea->Data.SectionData.ViewOffset; FileOffset = Context.Offset + Context.Segment->FileOffset; IsImageSection = Context.Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE; FileObject = Context.Section->FileObject; DirectMapped = FALSE; if (FileObject != NULL && !(Context.Segment->Characteristics & IMAGE_SCN_MEM_SHARED)) { Bcb = FileObject->SectionObjectPointer->SharedCacheMap; /* * If the file system is letting us go directly to the cache and the * memory area was mapped at an offset in the file which is page aligned * then note this is a direct mapped page. */ if ((FileOffset % PAGE_SIZE) == 0 && (Context.Offset + PAGE_SIZE <= Context.Segment->RawLength || !IsImageSection)) { DirectMapped = TRUE; } } /* * This should never happen since mappings of physical memory are never * placed in the rmap lists. */ if (Context.Section->AllocationAttributes & SEC_PHYSICALMEMORY) { DPRINT1("Trying to page out from physical memory section address 0x%X " "process %d\n", Address, Process ? Process->UniqueProcessId : 0); KeBugCheck(MEMORY_MANAGEMENT); } /* * Get the section segment entry and the physical address. */ Entry = MmGetPageEntrySectionSegment(Context.Segment, Context.Offset); if (!MmIsPagePresent(Process, Address)) { DPRINT1("Trying to page out not-present page at (%d,0x%.8X).\n", Process ? Process->UniqueProcessId : 0, Address); KeBugCheck(MEMORY_MANAGEMENT); } Page = MmGetPfnForProcess(Process, Address); SwapEntry = MmGetSavedSwapEntryPage(Page); /* * Prepare the context structure for the rmap delete call. */ Context.WasDirty = FALSE; if (Context.Segment->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA || IS_SWAP_FROM_SSE(Entry) || PFN_FROM_SSE(Entry) != Page) { Context.Private = TRUE; } else { Context.Private = FALSE; } /* * Take an additional reference to the page or the cache segment. */ if (DirectMapped && !Context.Private) { if(!MiIsPageFromCache(MemoryArea, Context.Offset)) { DPRINT1("Direct mapped non private page is not associated with the cache.\n"); KeBugCheck(MEMORY_MANAGEMENT); } } else { OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); MmReferencePage(Page); KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); } MmDeleteAllRmaps(Page, (PVOID)&Context, MmPageOutDeleteMapping); /* * If this wasn't a private page then we should have reduced the entry to * zero by deleting all the rmaps. */ if (!Context.Private && MmGetPageEntrySectionSegment(Context.Segment, Context.Offset) != 0) { if (!(Context.Segment->Flags & MM_PAGEFILE_SEGMENT) && !(Context.Segment->Characteristics & IMAGE_SCN_MEM_SHARED)) { KeBugCheck(MEMORY_MANAGEMENT); } } /* * If the page wasn't dirty then we can just free it as for a readonly page. * Since we unmapped all the mappings above we know it will not suddenly * become dirty. * If the page is from a pagefile section and has no swap entry, * we can't free the page at this point. */ SwapEntry = MmGetSavedSwapEntryPage(Page); if (Context.Segment->Flags & MM_PAGEFILE_SEGMENT) { if (Context.Private) { DPRINT1("Found a %s private page (address %x) in a pagefile segment.\n", Context.WasDirty ? "dirty" : "clean", Address); KeBugCheck(MEMORY_MANAGEMENT); } if (!Context.WasDirty && SwapEntry != 0) { MmSetSavedSwapEntryPage(Page, 0); MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, MAKE_SWAP_SSE(SwapEntry)); MmReleasePageMemoryConsumer(MC_USER, Page); PageOp->Status = STATUS_SUCCESS; MmspCompleteAndReleasePageOp(PageOp); return(STATUS_SUCCESS); } } else if (Context.Segment->Characteristics & IMAGE_SCN_MEM_SHARED) { if (Context.Private) { DPRINT1("Found a %s private page (address %x) in a shared section segment.\n", Context.WasDirty ? "dirty" : "clean", Address); KeBugCheck(MEMORY_MANAGEMENT); } if (!Context.WasDirty || SwapEntry != 0) { MmSetSavedSwapEntryPage(Page, 0); if (SwapEntry != 0) { MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, MAKE_SWAP_SSE(SwapEntry)); } MmReleasePageMemoryConsumer(MC_USER, Page); PageOp->Status = STATUS_SUCCESS; MmspCompleteAndReleasePageOp(PageOp); return(STATUS_SUCCESS); } } else if (!Context.Private && DirectMapped) { if (SwapEntry != 0) { DPRINT1("Found a swapentry for a non private and direct mapped page (address %x)\n", Address); KeBugCheck(MEMORY_MANAGEMENT); } Status = CcRosUnmapCacheSegment(Bcb, FileOffset, FALSE); if (!NT_SUCCESS(Status)) { DPRINT1("CCRosUnmapCacheSegment failed, status = %x\n", Status); KeBugCheck(MEMORY_MANAGEMENT); } PageOp->Status = STATUS_SUCCESS; MmspCompleteAndReleasePageOp(PageOp); return(STATUS_SUCCESS); } else if (!Context.WasDirty && !DirectMapped && !Context.Private) { if (SwapEntry != 0) { DPRINT1("Found a swap entry for a non dirty, non private and not direct mapped page (address %x)\n", Address); KeBugCheck(MEMORY_MANAGEMENT); } MmReleasePageMemoryConsumer(MC_USER, Page); PageOp->Status = STATUS_SUCCESS; MmspCompleteAndReleasePageOp(PageOp); return(STATUS_SUCCESS); } else if (!Context.WasDirty && Context.Private && SwapEntry != 0) { MmSetSavedSwapEntryPage(Page, 0); MmLockAddressSpace(AddressSpace); Status = MmCreatePageFileMapping(Process, Address, SwapEntry); MmUnlockAddressSpace(AddressSpace); if (!NT_SUCCESS(Status)) { KeBugCheck(MEMORY_MANAGEMENT); } MmReleasePageMemoryConsumer(MC_USER, Page); PageOp->Status = STATUS_SUCCESS; MmspCompleteAndReleasePageOp(PageOp); return(STATUS_SUCCESS); } /* * If necessary, allocate an entry in the paging file for this page */ if (SwapEntry == 0) { SwapEntry = MmAllocSwapPage(); if (SwapEntry == 0) { MmShowOutOfSpaceMessagePagingFile(); MmLockAddressSpace(AddressSpace); /* * For private pages restore the old mappings. */ if (Context.Private) { Status = MmCreateVirtualMapping(Process, Address, MemoryArea->Protect, &Page, 1); MmSetDirtyPage(Process, Address); MmInsertRmap(Page, Process, Address); } else { /* * For non-private pages if the page wasn't direct mapped then * set it back into the section segment entry so we don't loose * our copy. Otherwise it will be handled by the cache manager. */ Status = MmCreateVirtualMapping(Process, Address, MemoryArea->Protect, &Page, 1); MmSetDirtyPage(Process, Address); MmInsertRmap(Page, Process, Address); Entry = MAKE_SSE(Page << PAGE_SHIFT, 1); MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, Entry); } MmUnlockAddressSpace(AddressSpace); PageOp->Status = STATUS_UNSUCCESSFUL; MmspCompleteAndReleasePageOp(PageOp); return(STATUS_PAGEFILE_QUOTA); } } /* * Write the page to the pagefile */ Status = MmWriteToSwapPage(SwapEntry, Page); if (!NT_SUCCESS(Status)) { DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n", Status); /* * As above: undo our actions. * FIXME: Also free the swap page. */ MmLockAddressSpace(AddressSpace); if (Context.Private) { Status = MmCreateVirtualMapping(Process, Address, MemoryArea->Protect, &Page, 1); MmSetDirtyPage(Process, Address); MmInsertRmap(Page, Process, Address); } else { Status = MmCreateVirtualMapping(Process, Address, MemoryArea->Protect, &Page, 1); MmSetDirtyPage(Process, Address); MmInsertRmap(Page, Process, Address); Entry = MAKE_SSE(Page << PAGE_SHIFT, 1); MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, Entry); } MmUnlockAddressSpace(AddressSpace); PageOp->Status = STATUS_UNSUCCESSFUL; MmspCompleteAndReleasePageOp(PageOp); return(STATUS_UNSUCCESSFUL); } /* * Otherwise we have succeeded. */ DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT); MmSetSavedSwapEntryPage(Page, 0); if (Context.Segment->Flags & MM_PAGEFILE_SEGMENT || Context.Segment->Characteristics & IMAGE_SCN_MEM_SHARED) { MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, MAKE_SWAP_SSE(SwapEntry)); } else { MmReleasePageMemoryConsumer(MC_USER, Page); } if (Context.Private) { MmLockAddressSpace(AddressSpace); Status = MmCreatePageFileMapping(Process, Address, SwapEntry); MmUnlockAddressSpace(AddressSpace); if (!NT_SUCCESS(Status)) { KeBugCheck(MEMORY_MANAGEMENT); } } else { Entry = MAKE_SWAP_SSE(SwapEntry); MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, Entry); } PageOp->Status = STATUS_SUCCESS; MmspCompleteAndReleasePageOp(PageOp); return(STATUS_SUCCESS); } NTSTATUS NTAPI MmWritePageSectionView(PMMSUPPORT AddressSpace, PMEMORY_AREA MemoryArea, PVOID Address, PMM_PAGEOP PageOp) { ULONG Offset; PROS_SECTION_OBJECT Section; PMM_SECTION_SEGMENT Segment; PFN_TYPE Page; SWAPENTRY SwapEntry; ULONG Entry; BOOLEAN Private; NTSTATUS Status; PFILE_OBJECT FileObject; PBCB Bcb = NULL; BOOLEAN DirectMapped; BOOLEAN IsImageSection; PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace); Address = (PVOID)PAGE_ROUND_DOWN(Address); Offset = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress + MemoryArea->Data.SectionData.ViewOffset; /* * Get the segment and section. */ Segment = MemoryArea->Data.SectionData.Segment; Section = MemoryArea->Data.SectionData.Section; IsImageSection = Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE; FileObject = Section->FileObject; DirectMapped = FALSE; if (FileObject != NULL && !(Segment->Characteristics & IMAGE_SCN_MEM_SHARED)) { Bcb = FileObject->SectionObjectPointer->SharedCacheMap; /* * If the file system is letting us go directly to the cache and the * memory area was mapped at an offset in the file which is page aligned * then note this is a direct mapped page. */ if (((Offset + Segment->FileOffset) % PAGE_SIZE) == 0 && (Offset + PAGE_SIZE <= Segment->RawLength || !IsImageSection)) { DirectMapped = TRUE; } } /* * This should never happen since mappings of physical memory are never * placed in the rmap lists. */ if (Section->AllocationAttributes & SEC_PHYSICALMEMORY) { DPRINT1("Trying to write back page from physical memory mapped at %X " "process %d\n", Address, Process ? Process->UniqueProcessId : 0); KeBugCheck(MEMORY_MANAGEMENT); } /* * Get the section segment entry and the physical address. */ Entry = MmGetPageEntrySectionSegment(Segment, Offset); if (!MmIsPagePresent(Process, Address)) { DPRINT1("Trying to page out not-present page at (%d,0x%.8X).\n", Process ? Process->UniqueProcessId : 0, Address); KeBugCheck(MEMORY_MANAGEMENT); } Page = MmGetPfnForProcess(Process, Address); SwapEntry = MmGetSavedSwapEntryPage(Page); /* * Check for a private (COWed) page. */ if (Segment->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA || IS_SWAP_FROM_SSE(Entry) || PFN_FROM_SSE(Entry) != Page) { Private = TRUE; } else { Private = FALSE; } /* * Speculatively set all mappings of the page to clean. */ MmSetCleanAllRmaps(Page); /* * If this page was direct mapped from the cache then the cache manager * will take care of writing it back to disk. */ if (DirectMapped && !Private) { ASSERT(SwapEntry == 0); CcRosMarkDirtyCacheSegment(Bcb, Offset + Segment->FileOffset); PageOp->Status = STATUS_SUCCESS; MmspCompleteAndReleasePageOp(PageOp); return(STATUS_SUCCESS); } /* * If necessary, allocate an entry in the paging file for this page */ if (SwapEntry == 0) { SwapEntry = MmAllocSwapPage(); if (SwapEntry == 0) { MmSetDirtyAllRmaps(Page); PageOp->Status = STATUS_UNSUCCESSFUL; MmspCompleteAndReleasePageOp(PageOp); return(STATUS_PAGEFILE_QUOTA); } MmSetSavedSwapEntryPage(Page, SwapEntry); } /* * Write the page to the pagefile */ Status = MmWriteToSwapPage(SwapEntry, Page); if (!NT_SUCCESS(Status)) { DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n", Status); MmSetDirtyAllRmaps(Page); PageOp->Status = STATUS_UNSUCCESSFUL; MmspCompleteAndReleasePageOp(PageOp); return(STATUS_UNSUCCESSFUL); } /* * Otherwise we have succeeded. */ DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT); PageOp->Status = STATUS_SUCCESS; MmspCompleteAndReleasePageOp(PageOp); return(STATUS_SUCCESS); } static VOID MmAlterViewAttributes(PMMSUPPORT AddressSpace, PVOID BaseAddress, ULONG RegionSize, ULONG OldType, ULONG OldProtect, ULONG NewType, ULONG NewProtect) { PMEMORY_AREA MemoryArea; PMM_SECTION_SEGMENT Segment; BOOLEAN DoCOW = FALSE; ULONG i; PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace); MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress); Segment = MemoryArea->Data.SectionData.Segment; if ((Segment->WriteCopy || MemoryArea->Data.SectionData.WriteCopyView) && (NewProtect == PAGE_READWRITE || NewProtect == PAGE_EXECUTE_READWRITE)) { DoCOW = TRUE; } if (OldProtect != NewProtect) { for (i = 0; i < PAGE_ROUND_UP(RegionSize) / PAGE_SIZE; i++) { PVOID Address = (char*)BaseAddress + (i * PAGE_SIZE); ULONG Protect = NewProtect; /* * If we doing COW for this segment then check if the page is * already private. */ if (DoCOW && MmIsPagePresent(Process, Address)) { ULONG Offset; ULONG Entry; PFN_TYPE Page; Offset = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress + MemoryArea->Data.SectionData.ViewOffset; Entry = MmGetPageEntrySectionSegment(Segment, Offset); Page = MmGetPfnForProcess(Process, Address); Protect = PAGE_READONLY; if (Segment->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA || IS_SWAP_FROM_SSE(Entry) || PFN_FROM_SSE(Entry) != Page) { Protect = NewProtect; } } if (MmIsPagePresent(Process, Address)) { MmSetPageProtect(Process, Address, Protect); } } } } NTSTATUS NTAPI MmProtectSectionView(PMMSUPPORT AddressSpace, PMEMORY_AREA MemoryArea, PVOID BaseAddress, ULONG Length, ULONG Protect, PULONG OldProtect) { PMM_REGION Region; NTSTATUS Status; ULONG_PTR MaxLength; MaxLength = (ULONG_PTR)MemoryArea->EndingAddress - (ULONG_PTR)BaseAddress; if (Length > MaxLength) Length = MaxLength; Region = MmFindRegion(MemoryArea->StartingAddress, &MemoryArea->Data.SectionData.RegionListHead, BaseAddress, NULL); if ((MemoryArea->Flags & SEC_NO_CHANGE) && Region->Protect != Protect) { return STATUS_INVALID_PAGE_PROTECTION; } *OldProtect = Region->Protect; Status = MmAlterRegion(AddressSpace, MemoryArea->StartingAddress, &MemoryArea->Data.SectionData.RegionListHead, BaseAddress, Length, Region->Type, Protect, MmAlterViewAttributes); return(Status); } NTSTATUS NTAPI MmQuerySectionView(PMEMORY_AREA MemoryArea, PVOID Address, PMEMORY_BASIC_INFORMATION Info, PULONG ResultLength) { PMM_REGION Region; PVOID RegionBaseAddress; PROS_SECTION_OBJECT Section; PMM_SECTION_SEGMENT Segment; Region = MmFindRegion((PVOID)MemoryArea->StartingAddress, &MemoryArea->Data.SectionData.RegionListHead, Address, &RegionBaseAddress); if (Region == NULL) { return STATUS_UNSUCCESSFUL; } Section = MemoryArea->Data.SectionData.Section; if (Section->AllocationAttributes & SEC_IMAGE) { Segment = MemoryArea->Data.SectionData.Segment; Info->AllocationBase = (PUCHAR)MemoryArea->StartingAddress - Segment->VirtualAddress; Info->Type = MEM_IMAGE; } else { Info->AllocationBase = MemoryArea->StartingAddress; Info->Type = MEM_MAPPED; } Info->BaseAddress = RegionBaseAddress; Info->AllocationProtect = MemoryArea->Protect; Info->RegionSize = Region->Length; Info->State = MEM_COMMIT; Info->Protect = Region->Protect; *ResultLength = sizeof(MEMORY_BASIC_INFORMATION); return(STATUS_SUCCESS); } VOID NTAPI MmpFreePageFileSegment(PMM_SECTION_SEGMENT Segment) { ULONG Length; ULONG Offset; ULONG Entry; ULONG SavedSwapEntry; PFN_TYPE Page; Page = 0; Length = PAGE_ROUND_UP(Segment->Length); for (Offset = 0; Offset < Length; Offset += PAGE_SIZE) { Entry = MmGetPageEntrySectionSegment(Segment, Offset); if (Entry) { if (IS_SWAP_FROM_SSE(Entry)) { MmFreeSwapPage(SWAPENTRY_FROM_SSE(Entry)); } else { Page = PFN_FROM_SSE(Entry); SavedSwapEntry = MmGetSavedSwapEntryPage(Page); if (SavedSwapEntry != 0) { MmSetSavedSwapEntryPage(Page, 0); MmFreeSwapPage(SavedSwapEntry); } MmReleasePageMemoryConsumer(MC_USER, Page); } MmSetPageEntrySectionSegment(Segment, Offset, 0); } } } VOID NTAPI MmpDeleteSection(PVOID ObjectBody) { PROS_SECTION_OBJECT Section = (PROS_SECTION_OBJECT)ObjectBody; DPRINT("MmpDeleteSection(ObjectBody %x)\n", ObjectBody); if (Section->AllocationAttributes & SEC_IMAGE) { ULONG i; ULONG NrSegments; ULONG RefCount; PMM_SECTION_SEGMENT SectionSegments; /* * NOTE: Section->ImageSection can be NULL for short time * during the section creating. If we fail for some reason * until the image section is properly initialized we shouldn't * process further here. */ if (Section->ImageSection == NULL) return; SectionSegments = Section->ImageSection->Segments; NrSegments = Section->ImageSection->NrSegments; for (i = 0; i < NrSegments; i++) { if (SectionSegments[i].Characteristics & IMAGE_SCN_MEM_SHARED) { MmLockSectionSegment(&SectionSegments[i]); } RefCount = InterlockedDecrementUL(&SectionSegments[i].ReferenceCount); if (SectionSegments[i].Characteristics & IMAGE_SCN_MEM_SHARED) { if (RefCount == 0) { MmpFreePageFileSegment(&SectionSegments[i]); } MmUnlockSectionSegment(&SectionSegments[i]); } } } else { /* * NOTE: Section->Segment can be NULL for short time * during the section creating. */ if (Section->Segment == NULL) return; if (Section->Segment->Flags & MM_PAGEFILE_SEGMENT) { MmpFreePageFileSegment(Section->Segment); MmFreePageTablesSectionSegment(Section->Segment); ExFreePool(Section->Segment); Section->Segment = NULL; } else { (void)InterlockedDecrementUL(&Section->Segment->ReferenceCount); } } if (Section->FileObject != NULL) { CcRosDereferenceCache(Section->FileObject); ObDereferenceObject(Section->FileObject); Section->FileObject = NULL; } } VOID NTAPI MmpCloseSection(IN PEPROCESS Process OPTIONAL, IN PVOID Object, IN ACCESS_MASK GrantedAccess, IN ULONG ProcessHandleCount, IN ULONG SystemHandleCount) { DPRINT("MmpCloseSection(OB %x, HC %d)\n", Object, ProcessHandleCount); } NTSTATUS INIT_FUNCTION NTAPI MmCreatePhysicalMemorySection(VOID) { PROS_SECTION_OBJECT PhysSection; NTSTATUS Status; OBJECT_ATTRIBUTES Obj; UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory"); LARGE_INTEGER SectionSize; HANDLE Handle; /* * Create the section mapping physical memory */ SectionSize.QuadPart = 0xFFFFFFFF; InitializeObjectAttributes(&Obj, &Name, OBJ_PERMANENT, NULL, NULL); Status = MmCreateSection((PVOID)&PhysSection, SECTION_ALL_ACCESS, &Obj, &SectionSize, PAGE_EXECUTE_READWRITE, 0, NULL, NULL); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to create PhysicalMemory section\n"); KeBugCheck(MEMORY_MANAGEMENT); } Status = ObInsertObject(PhysSection, NULL, SECTION_ALL_ACCESS, 0, NULL, &Handle); if (!NT_SUCCESS(Status)) { ObDereferenceObject(PhysSection); } ObCloseHandle(Handle, KernelMode); PhysSection->AllocationAttributes |= SEC_PHYSICALMEMORY; PhysSection->Segment->Flags &= ~MM_PAGEFILE_SEGMENT; return(STATUS_SUCCESS); } NTSTATUS INIT_FUNCTION NTAPI MmInitSectionImplementation(VOID) { OBJECT_TYPE_INITIALIZER ObjectTypeInitializer; UNICODE_STRING Name; DPRINT("Creating Section Object Type\n"); /* Initialize the Section object type */ RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer)); RtlInitUnicodeString(&Name, L"Section"); ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer); ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(ROS_SECTION_OBJECT); ObjectTypeInitializer.PoolType = PagedPool; ObjectTypeInitializer.UseDefaultObject = TRUE; ObjectTypeInitializer.GenericMapping = MmpSectionMapping; ObjectTypeInitializer.DeleteProcedure = MmpDeleteSection; ObjectTypeInitializer.CloseProcedure = MmpCloseSection; ObjectTypeInitializer.ValidAccessMask = SECTION_ALL_ACCESS; ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &MmSectionObjectType); MmCreatePhysicalMemorySection(); return(STATUS_SUCCESS); } NTSTATUS NTAPI MmCreatePageFileSection(PROS_SECTION_OBJECT *SectionObject, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PLARGE_INTEGER UMaximumSize, ULONG SectionPageProtection, ULONG AllocationAttributes) /* * Create a section which is backed by the pagefile */ { LARGE_INTEGER MaximumSize; PROS_SECTION_OBJECT Section; PMM_SECTION_SEGMENT Segment; NTSTATUS Status; if (UMaximumSize == NULL) { return(STATUS_UNSUCCESSFUL); } MaximumSize = *UMaximumSize; /* * Create the section */ Status = ObCreateObject(ExGetPreviousMode(), MmSectionObjectType, ObjectAttributes, ExGetPreviousMode(), NULL, sizeof(ROS_SECTION_OBJECT), 0, 0, (PVOID*)(PVOID)&Section); if (!NT_SUCCESS(Status)) { return(Status); } /* * Initialize it */ RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT)); Section->SectionPageProtection = SectionPageProtection; Section->AllocationAttributes = AllocationAttributes; Section->MaximumSize = MaximumSize; Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT), TAG_MM_SECTION_SEGMENT); if (Segment == NULL) { ObDereferenceObject(Section); return(STATUS_NO_MEMORY); } Section->Segment = Segment; Segment->ReferenceCount = 1; ExInitializeFastMutex(&Segment->Lock); Segment->FileOffset = 0; Segment->Protection = SectionPageProtection; Segment->RawLength = MaximumSize.u.LowPart; Segment->Length = PAGE_ROUND_UP(MaximumSize.u.LowPart); Segment->Flags = MM_PAGEFILE_SEGMENT; Segment->WriteCopy = FALSE; RtlZeroMemory(&Segment->PageDirectory, sizeof(SECTION_PAGE_DIRECTORY)); Segment->VirtualAddress = 0; Segment->Characteristics = 0; *SectionObject = Section; return(STATUS_SUCCESS); } NTSTATUS NTAPI MmCreateDataFileSection(PROS_SECTION_OBJECT *SectionObject, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PLARGE_INTEGER UMaximumSize, ULONG SectionPageProtection, ULONG AllocationAttributes, HANDLE FileHandle) /* * Create a section backed by a data file */ { PROS_SECTION_OBJECT Section; NTSTATUS Status; LARGE_INTEGER MaximumSize; PFILE_OBJECT FileObject; PMM_SECTION_SEGMENT Segment; ULONG FileAccess; IO_STATUS_BLOCK Iosb; LARGE_INTEGER Offset; CHAR Buffer; FILE_STANDARD_INFORMATION FileInfo; ULONG Length; /* * Create the section */ Status = ObCreateObject(ExGetPreviousMode(), MmSectionObjectType, ObjectAttributes, ExGetPreviousMode(), NULL, sizeof(ROS_SECTION_OBJECT), 0, 0, (PVOID*)(PVOID)&Section); if (!NT_SUCCESS(Status)) { return(Status); } /* * Initialize it */ RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT)); Section->SectionPageProtection = SectionPageProtection; Section->AllocationAttributes = AllocationAttributes; /* * Check file access required */ if (SectionPageProtection & PAGE_READWRITE || SectionPageProtection & PAGE_EXECUTE_READWRITE) { FileAccess = FILE_READ_DATA | FILE_WRITE_DATA; } else { FileAccess = FILE_READ_DATA; } /* * Reference the file handle */ Status = ObReferenceObjectByHandle(FileHandle, FileAccess, IoFileObjectType, ExGetPreviousMode(), (PVOID*)(PVOID)&FileObject, NULL); if (!NT_SUCCESS(Status)) { ObDereferenceObject(Section); return(Status); } /* * FIXME: This is propably not entirely correct. We can't look into * the standard FCB header because it might not be initialized yet * (as in case of the EXT2FS driver by Manoj Paul Joseph where the * standard file information is filled on first request). */ Status = IoQueryFileInformation(FileObject, FileStandardInformation, sizeof(FILE_STANDARD_INFORMATION), &FileInfo, &Length); Iosb.Information = Length; if (!NT_SUCCESS(Status)) { ObDereferenceObject(Section); ObDereferenceObject(FileObject); return Status; } /* * FIXME: Revise this once a locking order for file size changes is * decided */ if ((UMaximumSize != NULL) && (UMaximumSize->QuadPart != 0)) { MaximumSize = *UMaximumSize; } else { MaximumSize = FileInfo.EndOfFile; /* Mapping zero-sized files isn't allowed. */ if (MaximumSize.QuadPart == 0) { ObDereferenceObject(Section); ObDereferenceObject(FileObject); return STATUS_FILE_INVALID; } } if (MaximumSize.QuadPart > FileInfo.EndOfFile.QuadPart) { Status = IoSetInformation(FileObject, FileAllocationInformation, sizeof(LARGE_INTEGER), &MaximumSize); if (!NT_SUCCESS(Status)) { ObDereferenceObject(Section); ObDereferenceObject(FileObject); return(STATUS_SECTION_NOT_EXTENDED); } } if (FileObject->SectionObjectPointer == NULL || FileObject->SectionObjectPointer->SharedCacheMap == NULL) { /* * Read a bit so caching is initiated for the file object. * This is only needed because MiReadPage currently cannot * handle non-cached streams. */ Offset.QuadPart = 0; Status = ZwReadFile(FileHandle, NULL, NULL, NULL, &Iosb, &Buffer, sizeof (Buffer), &Offset, 0); if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE)) { ObDereferenceObject(Section); ObDereferenceObject(FileObject); return(Status); } if (FileObject->SectionObjectPointer == NULL || FileObject->SectionObjectPointer->SharedCacheMap == NULL) { /* FIXME: handle this situation */ ObDereferenceObject(Section); ObDereferenceObject(FileObject); return STATUS_INVALID_PARAMETER; } } /* * Lock the file */ Status = MmspWaitForFileLock(FileObject); if (Status != STATUS_SUCCESS) { ObDereferenceObject(Section); ObDereferenceObject(FileObject); return(Status); } /* * If this file hasn't been mapped as a data file before then allocate a * section segment to describe the data file mapping */ if (FileObject->SectionObjectPointer->DataSectionObject == NULL) { Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT), TAG_MM_SECTION_SEGMENT); if (Segment == NULL) { //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE); ObDereferenceObject(Section); ObDereferenceObject(FileObject); return(STATUS_NO_MEMORY); } Section->Segment = Segment; Segment->ReferenceCount = 1; ExInitializeFastMutex(&Segment->Lock); /* * Set the lock before assigning the segment to the file object */ ExAcquireFastMutex(&Segment->Lock); FileObject->SectionObjectPointer->DataSectionObject = (PVOID)Segment; Segment->FileOffset = 0; Segment->Protection = SectionPageProtection; Segment->Flags = MM_DATAFILE_SEGMENT; Segment->Characteristics = 0; Segment->WriteCopy = FALSE; if (AllocationAttributes & SEC_RESERVE) { Segment->Length = Segment->RawLength = 0; } else { Segment->RawLength = MaximumSize.u.LowPart; Segment->Length = PAGE_ROUND_UP(Segment->RawLength); } Segment->VirtualAddress = 0; RtlZeroMemory(&Segment->PageDirectory, sizeof(SECTION_PAGE_DIRECTORY)); } else { /* * If the file is already mapped as a data file then we may need * to extend it */ Segment = (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer-> DataSectionObject; Section->Segment = Segment; (void)InterlockedIncrementUL(&Segment->ReferenceCount); MmLockSectionSegment(Segment); if (MaximumSize.u.LowPart > Segment->RawLength && !(AllocationAttributes & SEC_RESERVE)) { Segment->RawLength = MaximumSize.u.LowPart; Segment->Length = PAGE_ROUND_UP(Segment->RawLength); } } MmUnlockSectionSegment(Segment); Section->FileObject = FileObject; Section->MaximumSize = MaximumSize; CcRosReferenceCache(FileObject); //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE); *SectionObject = Section; return(STATUS_SUCCESS); } /* TODO: not that great (declaring loaders statically, having to declare all of them, having to keep them extern, etc.), will fix in the future */ extern NTSTATUS NTAPI PeFmtCreateSection ( IN CONST VOID * FileHeader, IN SIZE_T FileHeaderSize, IN PVOID File, OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject, OUT PULONG Flags, IN PEXEFMT_CB_READ_FILE ReadFileCb, IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb ); extern NTSTATUS NTAPI ElfFmtCreateSection ( IN CONST VOID * FileHeader, IN SIZE_T FileHeaderSize, IN PVOID File, OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject, OUT PULONG Flags, IN PEXEFMT_CB_READ_FILE ReadFileCb, IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb ); /* TODO: this is a standard DDK/PSDK macro */ #ifndef RTL_NUMBER_OF #define RTL_NUMBER_OF(ARR_) (sizeof(ARR_) / sizeof((ARR_)[0])) #endif static PEXEFMT_LOADER ExeFmtpLoaders[] = { PeFmtCreateSection, #ifdef __ELF ElfFmtCreateSection #endif }; static PMM_SECTION_SEGMENT NTAPI ExeFmtpAllocateSegments(IN ULONG NrSegments) { SIZE_T SizeOfSegments; PMM_SECTION_SEGMENT Segments; /* TODO: check for integer overflow */ SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * NrSegments; Segments = ExAllocatePoolWithTag(NonPagedPool, SizeOfSegments, TAG_MM_SECTION_SEGMENT); if(Segments) RtlZeroMemory(Segments, SizeOfSegments); return Segments; } static NTSTATUS NTAPI ExeFmtpReadFile(IN PVOID File, IN PLARGE_INTEGER Offset, IN ULONG Length, OUT PVOID * Data, OUT PVOID * AllocBase, OUT PULONG ReadSize) { NTSTATUS Status; LARGE_INTEGER FileOffset; ULONG AdjustOffset; ULONG OffsetAdjustment; ULONG BufferSize; ULONG UsedSize; PVOID Buffer; ASSERT_IRQL_LESS(DISPATCH_LEVEL); if(Length == 0) { KeBugCheck(MEMORY_MANAGEMENT); } FileOffset = *Offset; /* Negative/special offset: it cannot be used in this context */ if(FileOffset.u.HighPart < 0) { KeBugCheck(MEMORY_MANAGEMENT); } AdjustOffset = PAGE_ROUND_DOWN(FileOffset.u.LowPart); OffsetAdjustment = FileOffset.u.LowPart - AdjustOffset; FileOffset.u.LowPart = AdjustOffset; BufferSize = Length + OffsetAdjustment; BufferSize = PAGE_ROUND_UP(BufferSize); /* * It's ok to use paged pool, because this is a temporary buffer only used in * the loading of executables. The assumption is that MmCreateSection is * always called at low IRQLs and that these buffers don't survive a brief * initialization phase */ Buffer = ExAllocatePoolWithTag(PagedPool, BufferSize, 'rXmM'); if (!Buffer) { KeBugCheck(MEMORY_MANAGEMENT); } UsedSize = 0; #if 0 Status = MmspPageRead(File, Buffer, BufferSize, &FileOffset, &UsedSize); #else /* * FIXME: if we don't use ZwReadFile, caching is not enabled for the file and * nothing will work. But using ZwReadFile is wrong, and using its side effects * to initialize internal state is even worse. Our cache manager is in need of * professional help */ { IO_STATUS_BLOCK Iosb; Status = ZwReadFile(File, NULL, NULL, NULL, &Iosb, Buffer, BufferSize, &FileOffset, NULL); if(NT_SUCCESS(Status)) { UsedSize = Iosb.Information; } } #endif if(NT_SUCCESS(Status) && UsedSize < OffsetAdjustment) { Status = STATUS_IN_PAGE_ERROR; ASSERT(!NT_SUCCESS(Status)); } if(NT_SUCCESS(Status)) { *Data = (PVOID)((ULONG_PTR)Buffer + OffsetAdjustment); *AllocBase = Buffer; *ReadSize = UsedSize - OffsetAdjustment; } else { ExFreePoolWithTag(Buffer, 'rXmM'); } return Status; } #ifdef NASSERT # define MmspAssertSegmentsSorted(OBJ_) ((void)0) # define MmspAssertSegmentsNoOverlap(OBJ_) ((void)0) # define MmspAssertSegmentsPageAligned(OBJ_) ((void)0) #else static VOID NTAPI MmspAssertSegmentsSorted(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject) { ULONG i; for( i = 1; i < ImageSectionObject->NrSegments; ++ i ) { ASSERT(ImageSectionObject->Segments[i].VirtualAddress >= ImageSectionObject->Segments[i - 1].VirtualAddress); } } static VOID NTAPI MmspAssertSegmentsNoOverlap(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject) { ULONG i; MmspAssertSegmentsSorted(ImageSectionObject); for( i = 0; i < ImageSectionObject->NrSegments; ++ i ) { ASSERT(ImageSectionObject->Segments[i].Length > 0); if(i > 0) { ASSERT(ImageSectionObject->Segments[i].VirtualAddress >= (ImageSectionObject->Segments[i - 1].VirtualAddress + ImageSectionObject->Segments[i - 1].Length)); } } } static VOID NTAPI MmspAssertSegmentsPageAligned(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject) { ULONG i; for( i = 0; i < ImageSectionObject->NrSegments; ++ i ) { ASSERT((ImageSectionObject->Segments[i].VirtualAddress % PAGE_SIZE) == 0); ASSERT((ImageSectionObject->Segments[i].Length % PAGE_SIZE) == 0); } } #endif static int __cdecl MmspCompareSegments(const void * x, const void * y) { const MM_SECTION_SEGMENT *Segment1 = (const MM_SECTION_SEGMENT *)x; const MM_SECTION_SEGMENT *Segment2 = (const MM_SECTION_SEGMENT *)y; return (Segment1->VirtualAddress - Segment2->VirtualAddress) >> ((sizeof(ULONG_PTR) - sizeof(int)) * 8); } /* * Ensures an image section's segments are sorted in memory */ static VOID NTAPI MmspSortSegments(IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject, IN ULONG Flags) { if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_SORTED) { MmspAssertSegmentsSorted(ImageSectionObject); } else { qsort(ImageSectionObject->Segments, ImageSectionObject->NrSegments, sizeof(ImageSectionObject->Segments[0]), MmspCompareSegments); } } /* * Ensures an image section's segments don't overlap in memory and don't have * gaps and don't have a null size. We let them map to overlapping file regions, * though - that's not necessarily an error */ static BOOLEAN NTAPI MmspCheckSegmentBounds ( IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject, IN ULONG Flags ) { ULONG i; if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_NO_OVERLAP) { MmspAssertSegmentsNoOverlap(ImageSectionObject); return TRUE; } ASSERT(ImageSectionObject->NrSegments >= 1); for ( i = 0; i < ImageSectionObject->NrSegments; ++ i ) { if(ImageSectionObject->Segments[i].Length == 0) { return FALSE; } if(i > 0) { /* * TODO: relax the limitation on gaps. For example, gaps smaller than a * page could be OK (Windows seems to be OK with them), and larger gaps * could lead to image sections spanning several discontiguous regions * (NtMapViewOfSection could then refuse to map them, and they could * e.g. only be allowed as parameters to NtCreateProcess, like on UNIX) */ if ((ImageSectionObject->Segments[i - 1].VirtualAddress + ImageSectionObject->Segments[i - 1].Length) != ImageSectionObject->Segments[i].VirtualAddress) { return FALSE; } } } return TRUE; } /* * Merges and pads an image section's segments until they all are page-aligned * and have a size that is a multiple of the page size */ static BOOLEAN NTAPI MmspPageAlignSegments ( IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject, IN ULONG Flags ) { ULONG i; ULONG LastSegment; BOOLEAN Initialized; PMM_SECTION_SEGMENT EffectiveSegment; if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED) { MmspAssertSegmentsPageAligned(ImageSectionObject); return TRUE; } Initialized = FALSE; LastSegment = 0; EffectiveSegment = &ImageSectionObject->Segments[LastSegment]; for ( i = 0; i < ImageSectionObject->NrSegments; ++ i ) { /* * The first segment requires special handling */ if (i == 0) { ULONG_PTR VirtualAddress; ULONG_PTR VirtualOffset; VirtualAddress = EffectiveSegment->VirtualAddress; /* Round down the virtual address to the nearest page */ EffectiveSegment->VirtualAddress = PAGE_ROUND_DOWN(VirtualAddress); /* Round up the virtual size to the nearest page */ EffectiveSegment->Length = PAGE_ROUND_UP(VirtualAddress + EffectiveSegment->Length) - EffectiveSegment->VirtualAddress; /* Adjust the raw address and size */ VirtualOffset = VirtualAddress - EffectiveSegment->VirtualAddress; if (EffectiveSegment->FileOffset < VirtualOffset) { return FALSE; } /* * Garbage in, garbage out: unaligned base addresses make the file * offset point in curious and odd places, but that's what we were * asked for */ EffectiveSegment->FileOffset -= VirtualOffset; EffectiveSegment->RawLength += VirtualOffset; } else { PMM_SECTION_SEGMENT Segment = &ImageSectionObject->Segments[i]; ULONG_PTR EndOfEffectiveSegment; EndOfEffectiveSegment = EffectiveSegment->VirtualAddress + EffectiveSegment->Length; ASSERT((EndOfEffectiveSegment % PAGE_SIZE) == 0); /* * The current segment begins exactly where the current effective * segment ended, therefore beginning a new effective segment */ if (EndOfEffectiveSegment == Segment->VirtualAddress) { LastSegment ++; ASSERT(LastSegment <= i); ASSERT(LastSegment < ImageSectionObject->NrSegments); EffectiveSegment = &ImageSectionObject->Segments[LastSegment]; if (LastSegment != i) { /* * Copy the current segment. If necessary, the effective segment * will be expanded later */ *EffectiveSegment = *Segment; } /* * Page-align the virtual size. We know for sure the virtual address * already is */ ASSERT((EffectiveSegment->VirtualAddress % PAGE_SIZE) == 0); EffectiveSegment->Length = PAGE_ROUND_UP(EffectiveSegment->Length); } /* * The current segment is still part of the current effective segment: * extend the effective segment to reflect this */ else if (EndOfEffectiveSegment > Segment->VirtualAddress) { static const ULONG FlagsToProtection[16] = { PAGE_NOACCESS, PAGE_READONLY, PAGE_READWRITE, PAGE_READWRITE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_READWRITE, PAGE_WRITECOPY, PAGE_WRITECOPY, PAGE_WRITECOPY, PAGE_WRITECOPY, PAGE_EXECUTE_WRITECOPY, PAGE_EXECUTE_WRITECOPY, PAGE_EXECUTE_WRITECOPY, PAGE_EXECUTE_WRITECOPY }; unsigned ProtectionFlags; /* * Extend the file size */ /* Unaligned segments must be contiguous within the file */ if (Segment->FileOffset != (EffectiveSegment->FileOffset + EffectiveSegment->RawLength)) { return FALSE; } EffectiveSegment->RawLength += Segment->RawLength; /* * Extend the virtual size */ ASSERT(PAGE_ROUND_UP(Segment->VirtualAddress + Segment->Length) >= EndOfEffectiveSegment); EffectiveSegment->Length = PAGE_ROUND_UP(Segment->VirtualAddress + Segment->Length) - EffectiveSegment->VirtualAddress; /* * Merge the protection */ EffectiveSegment->Protection |= Segment->Protection; /* Clean up redundance */ ProtectionFlags = 0; if(EffectiveSegment->Protection & PAGE_IS_READABLE) ProtectionFlags |= 1 << 0; if(EffectiveSegment->Protection & PAGE_IS_WRITABLE) ProtectionFlags |= 1 << 1; if(EffectiveSegment->Protection & PAGE_IS_EXECUTABLE) ProtectionFlags |= 1 << 2; if(EffectiveSegment->Protection & PAGE_IS_WRITECOPY) ProtectionFlags |= 1 << 3; ASSERT(ProtectionFlags < 16); EffectiveSegment->Protection = FlagsToProtection[ProtectionFlags]; /* If a segment was required to be shared and cannot, fail */ if(!(Segment->Protection & PAGE_IS_WRITECOPY) && EffectiveSegment->Protection & PAGE_IS_WRITECOPY) { return FALSE; } } /* * We assume no holes between segments at this point */ else { KeBugCheck(MEMORY_MANAGEMENT); } } } ImageSectionObject->NrSegments = LastSegment + 1; return TRUE; } NTSTATUS ExeFmtpCreateImageSection(HANDLE FileHandle, PMM_IMAGE_SECTION_OBJECT ImageSectionObject) { LARGE_INTEGER Offset; PVOID FileHeader; PVOID FileHeaderBuffer; ULONG FileHeaderSize; ULONG Flags; ULONG OldNrSegments; NTSTATUS Status; ULONG i; /* * Read the beginning of the file (2 pages). Should be enough to contain * all (or most) of the headers */ Offset.QuadPart = 0; /* FIXME: use FileObject instead of FileHandle */ Status = ExeFmtpReadFile (FileHandle, &Offset, PAGE_SIZE * 2, &FileHeader, &FileHeaderBuffer, &FileHeaderSize); if (!NT_SUCCESS(Status)) return Status; if (FileHeaderSize == 0) { ExFreePool(FileHeaderBuffer); return STATUS_UNSUCCESSFUL; } /* * Look for a loader that can handle this executable */ for (i = 0; i < RTL_NUMBER_OF(ExeFmtpLoaders); ++ i) { RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject)); Flags = 0; /* FIXME: use FileObject instead of FileHandle */ Status = ExeFmtpLoaders[i](FileHeader, FileHeaderSize, FileHandle, ImageSectionObject, &Flags, ExeFmtpReadFile, ExeFmtpAllocateSegments); if (!NT_SUCCESS(Status)) { if (ImageSectionObject->Segments) { ExFreePool(ImageSectionObject->Segments); ImageSectionObject->Segments = NULL; } } if (Status != STATUS_ROS_EXEFMT_UNKNOWN_FORMAT) break; } ExFreePoolWithTag(FileHeaderBuffer, 'rXmM'); /* * No loader handled the format */ if (Status == STATUS_ROS_EXEFMT_UNKNOWN_FORMAT) { Status = STATUS_INVALID_IMAGE_NOT_MZ; ASSERT(!NT_SUCCESS(Status)); } if (!NT_SUCCESS(Status)) return Status; ASSERT(ImageSectionObject->Segments != NULL); /* * Some defaults */ /* FIXME? are these values platform-dependent? */ if(ImageSectionObject->StackReserve == 0) ImageSectionObject->StackReserve = 0x40000; if(ImageSectionObject->StackCommit == 0) ImageSectionObject->StackCommit = 0x1000; if(ImageSectionObject->ImageBase == 0) { if(ImageSectionObject->ImageCharacteristics & IMAGE_FILE_DLL) ImageSectionObject->ImageBase = 0x10000000; else ImageSectionObject->ImageBase = 0x00400000; } /* * And now the fun part: fixing the segments */ /* Sort them by virtual address */ MmspSortSegments(ImageSectionObject, Flags); /* Ensure they don't overlap in memory */ if (!MmspCheckSegmentBounds(ImageSectionObject, Flags)) return STATUS_INVALID_IMAGE_FORMAT; /* Ensure they are aligned */ OldNrSegments = ImageSectionObject->NrSegments; if (!MmspPageAlignSegments(ImageSectionObject, Flags)) return STATUS_INVALID_IMAGE_FORMAT; /* Trim them if the alignment phase merged some of them */ if (ImageSectionObject->NrSegments < OldNrSegments) { PMM_SECTION_SEGMENT Segments; SIZE_T SizeOfSegments; SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * ImageSectionObject->NrSegments; Segments = ExAllocatePoolWithTag(PagedPool, SizeOfSegments, TAG_MM_SECTION_SEGMENT); if (Segments == NULL) return STATUS_INSUFFICIENT_RESOURCES; RtlCopyMemory(Segments, ImageSectionObject->Segments, SizeOfSegments); ExFreePool(ImageSectionObject->Segments); ImageSectionObject->Segments = Segments; } /* And finish their initialization */ for ( i = 0; i < ImageSectionObject->NrSegments; ++ i ) { ExInitializeFastMutex(&ImageSectionObject->Segments[i].Lock); ImageSectionObject->Segments[i].ReferenceCount = 1; RtlZeroMemory(&ImageSectionObject->Segments[i].PageDirectory, sizeof(ImageSectionObject->Segments[i].PageDirectory)); } ASSERT(NT_SUCCESS(Status)); return Status; } NTSTATUS MmCreateImageSection(PROS_SECTION_OBJECT *SectionObject, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PLARGE_INTEGER UMaximumSize, ULONG SectionPageProtection, ULONG AllocationAttributes, HANDLE FileHandle) { PROS_SECTION_OBJECT Section; NTSTATUS Status; PFILE_OBJECT FileObject; PMM_SECTION_SEGMENT SectionSegments; PMM_IMAGE_SECTION_OBJECT ImageSectionObject; ULONG i; ULONG FileAccess = 0; /* * Specifying a maximum size is meaningless for an image section */ if (UMaximumSize != NULL) { return(STATUS_INVALID_PARAMETER_4); } /* * Check file access required */ if (SectionPageProtection & PAGE_READWRITE || SectionPageProtection & PAGE_EXECUTE_READWRITE) { FileAccess = FILE_READ_DATA | FILE_WRITE_DATA; } else { FileAccess = FILE_READ_DATA; } /* * Reference the file handle */ Status = ObReferenceObjectByHandle(FileHandle, FileAccess, IoFileObjectType, ExGetPreviousMode(), (PVOID*)(PVOID)&FileObject, NULL); if (!NT_SUCCESS(Status)) { return Status; } /* * Create the section */ Status = ObCreateObject (ExGetPreviousMode(), MmSectionObjectType, ObjectAttributes, ExGetPreviousMode(), NULL, sizeof(ROS_SECTION_OBJECT), 0, 0, (PVOID*)(PVOID)&Section); if (!NT_SUCCESS(Status)) { ObDereferenceObject(FileObject); return(Status); } /* * Initialize it */ RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT)); Section->SectionPageProtection = SectionPageProtection; Section->AllocationAttributes = AllocationAttributes; /* * Initialized caching for this file object if previously caching * was initialized for the same on disk file */ Status = CcTryToInitializeFileCache(FileObject); if (!NT_SUCCESS(Status) || FileObject->SectionObjectPointer->ImageSectionObject == NULL) { NTSTATUS StatusExeFmt; ImageSectionObject = ExAllocatePoolWithTag(PagedPool, sizeof(MM_IMAGE_SECTION_OBJECT), TAG_MM_SECTION_SEGMENT); if (ImageSectionObject == NULL) { ObDereferenceObject(FileObject); ObDereferenceObject(Section); return(STATUS_NO_MEMORY); } RtlZeroMemory(ImageSectionObject, sizeof(MM_IMAGE_SECTION_OBJECT)); StatusExeFmt = ExeFmtpCreateImageSection(FileHandle, ImageSectionObject); if (!NT_SUCCESS(StatusExeFmt)) { if(ImageSectionObject->Segments != NULL) ExFreePool(ImageSectionObject->Segments); ExFreePool(ImageSectionObject); ObDereferenceObject(Section); ObDereferenceObject(FileObject); return(StatusExeFmt); } Section->ImageSection = ImageSectionObject; ASSERT(ImageSectionObject->Segments); /* * Lock the file */ Status = MmspWaitForFileLock(FileObject); if (!NT_SUCCESS(Status)) { ExFreePool(ImageSectionObject->Segments); ExFreePool(ImageSectionObject); ObDereferenceObject(Section); ObDereferenceObject(FileObject); return(Status); } if (NULL != InterlockedCompareExchangePointer(&FileObject->SectionObjectPointer->ImageSectionObject, ImageSectionObject, NULL)) { /* * An other thread has initialized the same image in the background */ ExFreePool(ImageSectionObject->Segments); ExFreePool(ImageSectionObject); ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject; Section->ImageSection = ImageSectionObject; SectionSegments = ImageSectionObject->Segments; for (i = 0; i < ImageSectionObject->NrSegments; i++) { (void)InterlockedIncrementUL(&SectionSegments[i].ReferenceCount); } } Status = StatusExeFmt; } else { /* * Lock the file */ Status = MmspWaitForFileLock(FileObject); if (Status != STATUS_SUCCESS) { ObDereferenceObject(Section); ObDereferenceObject(FileObject); return(Status); } ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject; Section->ImageSection = ImageSectionObject; SectionSegments = ImageSectionObject->Segments; /* * Otherwise just reference all the section segments */ for (i = 0; i < ImageSectionObject->NrSegments; i++) { (void)InterlockedIncrementUL(&SectionSegments[i].ReferenceCount); } Status = STATUS_SUCCESS; } Section->FileObject = FileObject; CcRosReferenceCache(FileObject); //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE); *SectionObject = Section; return(Status); } /* * @implemented */ NTSTATUS NTAPI NtCreateSection (OUT PHANDLE SectionHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN PLARGE_INTEGER MaximumSize OPTIONAL, IN ULONG SectionPageProtection OPTIONAL, IN ULONG AllocationAttributes, IN HANDLE FileHandle OPTIONAL) { LARGE_INTEGER SafeMaximumSize; PVOID SectionObject; KPROCESSOR_MODE PreviousMode; NTSTATUS Status; PreviousMode = ExGetPreviousMode(); if(PreviousMode != KernelMode) { _SEH2_TRY { if (MaximumSize != NULL) { /* make a copy on the stack */ SafeMaximumSize = ProbeForReadLargeInteger(MaximumSize); MaximumSize = &SafeMaximumSize; } ProbeForWriteHandle(SectionHandle); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Return the exception code */ _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; } Status = MmCreateSection(&SectionObject, DesiredAccess, ObjectAttributes, MaximumSize, SectionPageProtection, AllocationAttributes, FileHandle, NULL); if (NT_SUCCESS(Status)) { Status = ObInsertObject ((PVOID)SectionObject, NULL, DesiredAccess, 0, NULL, SectionHandle); } return Status; } /********************************************************************** * NAME * NtOpenSection * * DESCRIPTION * * ARGUMENTS * SectionHandle * * DesiredAccess * * ObjectAttributes * * RETURN VALUE * * REVISIONS */ NTSTATUS NTAPI NtOpenSection(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) { HANDLE hSection; KPROCESSOR_MODE PreviousMode; NTSTATUS Status; PreviousMode = ExGetPreviousMode(); if(PreviousMode != KernelMode) { _SEH2_TRY { ProbeForWriteHandle(SectionHandle); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Return the exception code */ _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; } Status = ObOpenObjectByName(ObjectAttributes, MmSectionObjectType, PreviousMode, NULL, DesiredAccess, NULL, &hSection); if(NT_SUCCESS(Status)) { _SEH2_TRY { *SectionHandle = hSection; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; } return(Status); } static NTSTATUS MmMapViewOfSegment(PMMSUPPORT AddressSpace, PROS_SECTION_OBJECT Section, PMM_SECTION_SEGMENT Segment, PVOID* BaseAddress, SIZE_T ViewSize, ULONG Protect, ULONG ViewOffset, ULONG AllocationType) { PMEMORY_AREA MArea; NTSTATUS Status; PHYSICAL_ADDRESS BoundaryAddressMultiple; BoundaryAddressMultiple.QuadPart = 0; Status = MmCreateMemoryArea(AddressSpace, MEMORY_AREA_SECTION_VIEW, BaseAddress, ViewSize, Protect, &MArea, FALSE, AllocationType, BoundaryAddressMultiple); if (!NT_SUCCESS(Status)) { DPRINT1("Mapping between 0x%.8X and 0x%.8X failed (%X).\n", (*BaseAddress), (char*)(*BaseAddress) + ViewSize, Status); return(Status); } ObReferenceObject((PVOID)Section); MArea->Data.SectionData.Segment = Segment; MArea->Data.SectionData.Section = Section; MArea->Data.SectionData.ViewOffset = ViewOffset; MArea->Data.SectionData.WriteCopyView = FALSE; MmInitializeRegion(&MArea->Data.SectionData.RegionListHead, ViewSize, 0, Protect); return(STATUS_SUCCESS); } /********************************************************************** * NAME EXPORTED * NtMapViewOfSection * * DESCRIPTION * Maps a view of a section into the virtual address space of a * process. * * ARGUMENTS * SectionHandle * Handle of the section. * * ProcessHandle * Handle of the process. * * BaseAddress * Desired base address (or NULL) on entry; * Actual base address of the view on exit. * * ZeroBits * Number of high order address bits that must be zero. * * CommitSize * Size in bytes of the initially committed section of * the view. * * SectionOffset * Offset in bytes from the beginning of the section * to the beginning of the view. * * ViewSize * Desired length of map (or zero to map all) on entry * Actual length mapped on exit. * * InheritDisposition * Specified how the view is to be shared with * child processes. * * AllocateType * Type of allocation for the pages. * * Protect * Protection for the committed region of the view. * * RETURN VALUE * Status. * * @implemented */ NTSTATUS NTAPI NtMapViewOfSection(IN HANDLE SectionHandle, IN HANDLE ProcessHandle, IN OUT PVOID* BaseAddress OPTIONAL, IN ULONG_PTR ZeroBits OPTIONAL, IN SIZE_T CommitSize, IN OUT PLARGE_INTEGER SectionOffset OPTIONAL, IN OUT PSIZE_T ViewSize, IN SECTION_INHERIT InheritDisposition, IN ULONG AllocationType OPTIONAL, IN ULONG Protect) { PVOID SafeBaseAddress; LARGE_INTEGER SafeSectionOffset; SIZE_T SafeViewSize; PROS_SECTION_OBJECT Section; PEPROCESS Process; KPROCESSOR_MODE PreviousMode; PMMSUPPORT AddressSpace; NTSTATUS Status; ULONG tmpProtect; ACCESS_MASK DesiredAccess; /* * Check the protection */ if (Protect & ~PAGE_FLAGS_VALID_FROM_USER_MODE) { return STATUS_INVALID_PARAMETER_10; } tmpProtect = Protect & ~(PAGE_GUARD|PAGE_NOCACHE); if (tmpProtect != PAGE_NOACCESS && tmpProtect != PAGE_READONLY && tmpProtect != PAGE_READWRITE && tmpProtect != PAGE_WRITECOPY && tmpProtect != PAGE_EXECUTE && tmpProtect != PAGE_EXECUTE_READ && tmpProtect != PAGE_EXECUTE_READWRITE && tmpProtect != PAGE_EXECUTE_WRITECOPY) { return STATUS_INVALID_PAGE_PROTECTION; } PreviousMode = ExGetPreviousMode(); if(PreviousMode != KernelMode) { SafeBaseAddress = NULL; SafeSectionOffset.QuadPart = 0; SafeViewSize = 0; _SEH2_TRY { if(BaseAddress != NULL) { ProbeForWritePointer(BaseAddress); SafeBaseAddress = *BaseAddress; } if(SectionOffset != NULL) { ProbeForWriteLargeInteger(SectionOffset); SafeSectionOffset = *SectionOffset; } ProbeForWriteSize_t(ViewSize); SafeViewSize = *ViewSize; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Return the exception code */ _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; } else { SafeBaseAddress = (BaseAddress != NULL ? *BaseAddress : NULL); SafeSectionOffset.QuadPart = (SectionOffset != NULL ? SectionOffset->QuadPart : 0); SafeViewSize = (ViewSize != NULL ? *ViewSize : 0); } SafeSectionOffset.LowPart = PAGE_ROUND_DOWN(SafeSectionOffset.LowPart); Status = ObReferenceObjectByHandle(ProcessHandle, PROCESS_VM_OPERATION, PsProcessType, PreviousMode, (PVOID*)(PVOID)&Process, NULL); if (!NT_SUCCESS(Status)) { return(Status); } AddressSpace = &Process->Vm; /* Convert NT Protection Attr to Access Mask */ if (Protect == PAGE_READONLY) { DesiredAccess = SECTION_MAP_READ; } else if (Protect == PAGE_READWRITE) { DesiredAccess = SECTION_MAP_WRITE; } else if (Protect == PAGE_WRITECOPY) { DesiredAccess = SECTION_QUERY; } /* FIXME: Handle other Protection Attributes. For now keep previous behavior */ else { DesiredAccess = SECTION_MAP_READ; } Status = ObReferenceObjectByHandle(SectionHandle, DesiredAccess, MmSectionObjectType, PreviousMode, (PVOID*)(PVOID)&Section, NULL); if (!(NT_SUCCESS(Status))) { DPRINT("ObReference failed rc=%x\n",Status); ObDereferenceObject(Process); return(Status); } Status = MmMapViewOfSection(Section, (PEPROCESS)Process, (BaseAddress != NULL ? &SafeBaseAddress : NULL), ZeroBits, CommitSize, (SectionOffset != NULL ? &SafeSectionOffset : NULL), (ViewSize != NULL ? &SafeViewSize : NULL), InheritDisposition, AllocationType, Protect); /* Check if this is an image for the current process */ if ((Section->AllocationAttributes & SEC_IMAGE) && (Process == PsGetCurrentProcess()) && (Status != STATUS_IMAGE_NOT_AT_BASE)) { /* Notify the debugger */ DbgkMapViewOfSection(Section, SafeBaseAddress, SafeSectionOffset.LowPart, SafeViewSize); } ObDereferenceObject(Section); ObDereferenceObject(Process); if(NT_SUCCESS(Status)) { /* copy parameters back to the caller */ _SEH2_TRY { if(BaseAddress != NULL) { *BaseAddress = SafeBaseAddress; } if(SectionOffset != NULL) { *SectionOffset = SafeSectionOffset; } if(ViewSize != NULL) { *ViewSize = SafeViewSize; } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; } return(Status); } static VOID MmFreeSectionPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address, PFN_TYPE Page, SWAPENTRY SwapEntry, BOOLEAN Dirty) { ULONG Entry; PFILE_OBJECT FileObject; PBCB Bcb; ULONG Offset; SWAPENTRY SavedSwapEntry; PMM_PAGEOP PageOp; NTSTATUS Status; PROS_SECTION_OBJECT Section; PMM_SECTION_SEGMENT Segment; PMMSUPPORT AddressSpace; PEPROCESS Process; AddressSpace = (PMMSUPPORT)Context; Process = MmGetAddressSpaceOwner(AddressSpace); Address = (PVOID)PAGE_ROUND_DOWN(Address); Offset = ((ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress) + MemoryArea->Data.SectionData.ViewOffset; Section = MemoryArea->Data.SectionData.Section; Segment = MemoryArea->Data.SectionData.Segment; PageOp = MmCheckForPageOp(MemoryArea, NULL, NULL, Segment, Offset); while (PageOp) { MmUnlockSectionSegment(Segment); MmUnlockAddressSpace(AddressSpace); Status = MmspWaitForPageOpCompletionEvent(PageOp); if (Status != STATUS_SUCCESS) { DPRINT1("Failed to wait for page op, status = %x\n", Status); KeBugCheck(MEMORY_MANAGEMENT); } MmLockAddressSpace(AddressSpace); MmLockSectionSegment(Segment); MmspCompleteAndReleasePageOp(PageOp); PageOp = MmCheckForPageOp(MemoryArea, NULL, NULL, Segment, Offset); } Entry = MmGetPageEntrySectionSegment(Segment, Offset); /* * For a dirty, datafile, non-private page mark it as dirty in the * cache manager. */ if (Segment->Flags & MM_DATAFILE_SEGMENT) { if (Page == PFN_FROM_SSE(Entry) && Dirty) { FileObject = MemoryArea->Data.SectionData.Section->FileObject; Bcb = FileObject->SectionObjectPointer->SharedCacheMap; CcRosMarkDirtyCacheSegment(Bcb, Offset + Segment->FileOffset); ASSERT(SwapEntry == 0); } } if (SwapEntry != 0) { /* * Sanity check */ if (Segment->Flags & MM_PAGEFILE_SEGMENT) { DPRINT1("Found a swap entry for a page in a pagefile section.\n"); KeBugCheck(MEMORY_MANAGEMENT); } MmFreeSwapPage(SwapEntry); } else if (Page != 0) { if (IS_SWAP_FROM_SSE(Entry) || Page != PFN_FROM_SSE(Entry)) { /* * Sanity check */ if (Segment->Flags & MM_PAGEFILE_SEGMENT) { DPRINT1("Found a private page in a pagefile section.\n"); KeBugCheck(MEMORY_MANAGEMENT); } /* * Just dereference private pages */ SavedSwapEntry = MmGetSavedSwapEntryPage(Page); if (SavedSwapEntry != 0) { MmFreeSwapPage(SavedSwapEntry); MmSetSavedSwapEntryPage(Page, 0); } MmDeleteRmap(Page, Process, Address); MmReleasePageMemoryConsumer(MC_USER, Page); } else { MmDeleteRmap(Page, Process, Address); MmUnsharePageEntrySectionSegment(Section, Segment, Offset, Dirty, FALSE); } } } static NTSTATUS MmUnmapViewOfSegment(PMMSUPPORT AddressSpace, PVOID BaseAddress) { NTSTATUS Status; PMEMORY_AREA MemoryArea; PROS_SECTION_OBJECT Section; PMM_SECTION_SEGMENT Segment; PLIST_ENTRY CurrentEntry; PMM_REGION CurrentRegion; PLIST_ENTRY RegionListHead; MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress); if (MemoryArea == NULL) { return(STATUS_UNSUCCESSFUL); } MemoryArea->DeleteInProgress = TRUE; Section = MemoryArea->Data.SectionData.Section; Segment = MemoryArea->Data.SectionData.Segment; MmLockSectionSegment(Segment); RegionListHead = &MemoryArea->Data.SectionData.RegionListHead; while (!IsListEmpty(RegionListHead)) { CurrentEntry = RemoveHeadList(RegionListHead); CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION, RegionListEntry); ExFreePoolWithTag(CurrentRegion, TAG_MM_REGION); } if (Section->AllocationAttributes & SEC_PHYSICALMEMORY) { Status = MmFreeMemoryArea(AddressSpace, MemoryArea, NULL, NULL); } else { Status = MmFreeMemoryArea(AddressSpace, MemoryArea, MmFreeSectionPage, AddressSpace); } MmUnlockSectionSegment(Segment); ObDereferenceObject(Section); return(STATUS_SUCCESS); } /* * @implemented */ NTSTATUS NTAPI MmUnmapViewOfSection(PEPROCESS Process, PVOID BaseAddress) { NTSTATUS Status; PMEMORY_AREA MemoryArea; PMMSUPPORT AddressSpace; PROS_SECTION_OBJECT Section; PMM_PAGEOP PageOp; ULONG_PTR Offset; PVOID ImageBaseAddress = 0; DPRINT("Opening memory area Process %x BaseAddress %x\n", Process, BaseAddress); ASSERT(Process); AddressSpace = &Process->Vm; MmLockAddressSpace(AddressSpace); MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress); if (MemoryArea == NULL || MemoryArea->Type != MEMORY_AREA_SECTION_VIEW || MemoryArea->DeleteInProgress) { MmUnlockAddressSpace(AddressSpace); return STATUS_NOT_MAPPED_VIEW; } MemoryArea->DeleteInProgress = TRUE; while (MemoryArea->PageOpCount) { Offset = PAGE_ROUND_UP((ULONG_PTR)MemoryArea->EndingAddress - (ULONG_PTR)MemoryArea->StartingAddress); while (Offset) { Offset -= PAGE_SIZE; PageOp = MmCheckForPageOp(MemoryArea, NULL, NULL, MemoryArea->Data.SectionData.Segment, Offset + MemoryArea->Data.SectionData.ViewOffset); if (PageOp) { MmUnlockAddressSpace(AddressSpace); Status = MmspWaitForPageOpCompletionEvent(PageOp); if (Status != STATUS_SUCCESS) { DPRINT1("Failed to wait for page op, status = %x\n", Status); KeBugCheck(MEMORY_MANAGEMENT); } MmLockAddressSpace(AddressSpace); MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress); if (MemoryArea == NULL || MemoryArea->Type != MEMORY_AREA_SECTION_VIEW) { MmUnlockAddressSpace(AddressSpace); return STATUS_NOT_MAPPED_VIEW; } break; } } } Section = MemoryArea->Data.SectionData.Section; if (Section->AllocationAttributes & SEC_IMAGE) { ULONG i; ULONG NrSegments; PMM_IMAGE_SECTION_OBJECT ImageSectionObject; PMM_SECTION_SEGMENT SectionSegments; PMM_SECTION_SEGMENT Segment; Segment = MemoryArea->Data.SectionData.Segment; ImageSectionObject = Section->ImageSection; SectionSegments = ImageSectionObject->Segments; NrSegments = ImageSectionObject->NrSegments; /* Search for the current segment within the section segments * and calculate the image base address */ for (i = 0; i < NrSegments; i++) { if (!(SectionSegments[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD)) { if (Segment == &SectionSegments[i]) { ImageBaseAddress = (char*)BaseAddress - (ULONG_PTR)SectionSegments[i].VirtualAddress; break; } } } if (i >= NrSegments) { KeBugCheck(MEMORY_MANAGEMENT); } for (i = 0; i < NrSegments; i++) { if (!(SectionSegments[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD)) { PVOID SBaseAddress = (PVOID) ((char*)ImageBaseAddress + (ULONG_PTR)SectionSegments[i].VirtualAddress); Status = MmUnmapViewOfSegment(AddressSpace, SBaseAddress); } } } else { Status = MmUnmapViewOfSegment(AddressSpace, BaseAddress); } MmUnlockAddressSpace(AddressSpace); /* Notify debugger */ if (ImageBaseAddress) DbgkUnMapViewOfSection(ImageBaseAddress); return(STATUS_SUCCESS); } /********************************************************************** * NAME EXPORTED * NtUnmapViewOfSection * * DESCRIPTION * * ARGUMENTS * ProcessHandle * * BaseAddress * * RETURN VALUE * Status. * * REVISIONS */ NTSTATUS NTAPI NtUnmapViewOfSection (HANDLE ProcessHandle, PVOID BaseAddress) { PEPROCESS Process; KPROCESSOR_MODE PreviousMode; NTSTATUS Status; DPRINT("NtUnmapViewOfSection(ProcessHandle %x, BaseAddress %x)\n", ProcessHandle, BaseAddress); PreviousMode = ExGetPreviousMode(); DPRINT("Referencing process\n"); Status = ObReferenceObjectByHandle(ProcessHandle, PROCESS_VM_OPERATION, PsProcessType, PreviousMode, (PVOID*)(PVOID)&Process, NULL); if (!NT_SUCCESS(Status)) { DPRINT("ObReferenceObjectByHandle failed (Status %x)\n", Status); return(Status); } Status = MmUnmapViewOfSection(Process, BaseAddress); ObDereferenceObject(Process); return Status; } /** * Queries the information of a section object. * * @param SectionHandle * Handle to the section object. It must be opened with SECTION_QUERY * access. * @param SectionInformationClass * Index to a certain information structure. Can be either * SectionBasicInformation or SectionImageInformation. The latter * is valid only for sections that were created with the SEC_IMAGE * flag. * @param SectionInformation * Caller supplies storage for resulting information. * @param Length * Size of the supplied storage. * @param ResultLength * Data written. * * @return Status. * * @implemented */ NTSTATUS NTAPI NtQuerySection(IN HANDLE SectionHandle, IN SECTION_INFORMATION_CLASS SectionInformationClass, OUT PVOID SectionInformation, IN SIZE_T SectionInformationLength, OUT PSIZE_T ResultLength OPTIONAL) { PROS_SECTION_OBJECT Section; KPROCESSOR_MODE PreviousMode; NTSTATUS Status; PAGED_CODE(); PreviousMode = ExGetPreviousMode(); Status = DefaultQueryInfoBufferCheck(SectionInformationClass, ExSectionInfoClass, sizeof(ExSectionInfoClass) / sizeof(ExSectionInfoClass[0]), SectionInformation, SectionInformationLength, NULL, ResultLength, PreviousMode); if(!NT_SUCCESS(Status)) { DPRINT1("NtQuerySection() failed, Status: 0x%x\n", Status); return Status; } Status = ObReferenceObjectByHandle(SectionHandle, SECTION_QUERY, MmSectionObjectType, PreviousMode, (PVOID*)(PVOID)&Section, NULL); if (NT_SUCCESS(Status)) { switch (SectionInformationClass) { case SectionBasicInformation: { PSECTION_BASIC_INFORMATION Sbi = (PSECTION_BASIC_INFORMATION)SectionInformation; _SEH2_TRY { Sbi->Attributes = Section->AllocationAttributes; if (Section->AllocationAttributes & SEC_IMAGE) { Sbi->BaseAddress = 0; Sbi->Size.QuadPart = 0; } else { Sbi->BaseAddress = (PVOID)Section->Segment->VirtualAddress; Sbi->Size.QuadPart = Section->Segment->Length; } if (ResultLength != NULL) { *ResultLength = sizeof(SECTION_BASIC_INFORMATION); } Status = STATUS_SUCCESS; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; break; } case SectionImageInformation: { PSECTION_IMAGE_INFORMATION Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation; _SEH2_TRY { memset(Sii, 0, sizeof(SECTION_IMAGE_INFORMATION)); if (Section->AllocationAttributes & SEC_IMAGE) { PMM_IMAGE_SECTION_OBJECT ImageSectionObject; ImageSectionObject = Section->ImageSection; Sii->TransferAddress = (PVOID)ImageSectionObject->EntryPoint; Sii->MaximumStackSize = ImageSectionObject->StackReserve; Sii->CommittedStackSize = ImageSectionObject->StackCommit; Sii->SubSystemType = ImageSectionObject->Subsystem; Sii->SubSystemMinorVersion = ImageSectionObject->MinorSubsystemVersion; Sii->SubSystemMajorVersion = ImageSectionObject->MajorSubsystemVersion; Sii->ImageCharacteristics = ImageSectionObject->ImageCharacteristics; Sii->Machine = ImageSectionObject->Machine; Sii->ImageContainsCode = ImageSectionObject->Executable; } if (ResultLength != NULL) { *ResultLength = sizeof(SECTION_IMAGE_INFORMATION); } Status = STATUS_SUCCESS; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; break; } } ObDereferenceObject(Section); } return(Status); } /** * Extends size of file backed section. * * @param SectionHandle * Handle to the section object. It must be opened with * SECTION_EXTEND_SIZE access. * @param NewMaximumSize * New maximum size of the section in bytes. * * @return Status. * * @todo Move the actual code to internal function MmExtendSection. * @unimplemented */ NTSTATUS NTAPI NtExtendSection(IN HANDLE SectionHandle, IN PLARGE_INTEGER NewMaximumSize) { LARGE_INTEGER SafeNewMaximumSize; PROS_SECTION_OBJECT Section; KPROCESSOR_MODE PreviousMode; NTSTATUS Status; PreviousMode = ExGetPreviousMode(); if(PreviousMode != KernelMode) { _SEH2_TRY { /* make a copy on the stack */ SafeNewMaximumSize = ProbeForReadLargeInteger(NewMaximumSize); NewMaximumSize = &SafeNewMaximumSize; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Return the exception code */ _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; } Status = ObReferenceObjectByHandle(SectionHandle, SECTION_EXTEND_SIZE, MmSectionObjectType, PreviousMode, (PVOID*)&Section, NULL); if (!NT_SUCCESS(Status)) { return Status; } if (!(Section->AllocationAttributes & SEC_FILE)) { ObDereferenceObject(Section); return STATUS_INVALID_PARAMETER; } /* * - Acquire file extneding resource. * - Check if we're not resizing the section below it's actual size! * - Extend segments if needed. * - Set file information (FileAllocationInformation) to the new size. * - Release file extending resource. */ ObDereferenceObject(Section); return STATUS_NOT_IMPLEMENTED; } /********************************************************************** * NAME EXPORTED * MmMapViewOfSection * * DESCRIPTION * Maps a view of a section into the virtual address space of a * process. * * ARGUMENTS * Section * Pointer to the section object. * * ProcessHandle * Pointer to the process. * * BaseAddress * Desired base address (or NULL) on entry; * Actual base address of the view on exit. * * ZeroBits * Number of high order address bits that must be zero. * * CommitSize * Size in bytes of the initially committed section of * the view. * * SectionOffset * Offset in bytes from the beginning of the section * to the beginning of the view. * * ViewSize * Desired length of map (or zero to map all) on entry * Actual length mapped on exit. * * InheritDisposition * Specified how the view is to be shared with * child processes. * * AllocationType * Type of allocation for the pages. * * Protect * Protection for the committed region of the view. * * RETURN VALUE * Status. * * @implemented */ NTSTATUS NTAPI MmMapViewOfSection(IN PVOID SectionObject, IN PEPROCESS Process, IN OUT PVOID *BaseAddress, IN ULONG_PTR ZeroBits, IN SIZE_T CommitSize, IN OUT PLARGE_INTEGER SectionOffset OPTIONAL, IN OUT PSIZE_T ViewSize, IN SECTION_INHERIT InheritDisposition, IN ULONG AllocationType, IN ULONG Protect) { PROS_SECTION_OBJECT Section; PMMSUPPORT AddressSpace; ULONG ViewOffset; NTSTATUS Status = STATUS_SUCCESS; ASSERT(Process); if (!Protect || Protect & ~PAGE_FLAGS_VALID_FOR_SECTION) { return STATUS_INVALID_PAGE_PROTECTION; } Section = (PROS_SECTION_OBJECT)SectionObject; AddressSpace = &Process->Vm; AllocationType |= (Section->AllocationAttributes & SEC_NO_CHANGE); MmLockAddressSpace(AddressSpace); if (Section->AllocationAttributes & SEC_IMAGE) { ULONG i; ULONG NrSegments; ULONG_PTR ImageBase; ULONG ImageSize; PMM_IMAGE_SECTION_OBJECT ImageSectionObject; PMM_SECTION_SEGMENT SectionSegments; ImageSectionObject = Section->ImageSection; SectionSegments = ImageSectionObject->Segments; NrSegments = ImageSectionObject->NrSegments; ImageBase = (ULONG_PTR)*BaseAddress; if (ImageBase == 0) { ImageBase = ImageSectionObject->ImageBase; } ImageSize = 0; for (i = 0; i < NrSegments; i++) { if (!(SectionSegments[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD)) { ULONG_PTR MaxExtent; MaxExtent = (ULONG_PTR)SectionSegments[i].VirtualAddress + SectionSegments[i].Length; ImageSize = max(ImageSize, MaxExtent); } } ImageSectionObject->ImageSize = ImageSize; /* Check there is enough space to map the section at that point. */ if (MmLocateMemoryAreaByRegion(AddressSpace, (PVOID)ImageBase, PAGE_ROUND_UP(ImageSize)) != NULL) { /* Fail if the user requested a fixed base address. */ if ((*BaseAddress) != NULL) { MmUnlockAddressSpace(AddressSpace); return(STATUS_UNSUCCESSFUL); } /* Otherwise find a gap to map the image. */ ImageBase = (ULONG_PTR)MmFindGap(AddressSpace, PAGE_ROUND_UP(ImageSize), PAGE_SIZE, FALSE); if (ImageBase == 0) { MmUnlockAddressSpace(AddressSpace); return(STATUS_UNSUCCESSFUL); } } for (i = 0; i < NrSegments; i++) { if (!(SectionSegments[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD)) { PVOID SBaseAddress = (PVOID) ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].VirtualAddress); MmLockSectionSegment(&SectionSegments[i]); Status = MmMapViewOfSegment(AddressSpace, Section, &SectionSegments[i], &SBaseAddress, SectionSegments[i].Length, SectionSegments[i].Protection, 0, 0); MmUnlockSectionSegment(&SectionSegments[i]); if (!NT_SUCCESS(Status)) { MmUnlockAddressSpace(AddressSpace); return(Status); } } } *BaseAddress = (PVOID)ImageBase; } else { /* check for write access */ if ((Protect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE)) && !(Section->SectionPageProtection & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE))) { MmUnlockAddressSpace(AddressSpace); return STATUS_SECTION_PROTECTION; } /* check for read access */ if ((Protect & (PAGE_READONLY|PAGE_WRITECOPY|PAGE_EXECUTE_READ|PAGE_EXECUTE_WRITECOPY)) && !(Section->SectionPageProtection & (PAGE_READONLY|PAGE_READWRITE|PAGE_WRITECOPY|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY))) { MmUnlockAddressSpace(AddressSpace); return STATUS_SECTION_PROTECTION; } /* check for execute access */ if ((Protect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)) && !(Section->SectionPageProtection & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY))) { MmUnlockAddressSpace(AddressSpace); return STATUS_SECTION_PROTECTION; } if (ViewSize == NULL) { /* Following this pointer would lead to us to the dark side */ /* What to do? Bugcheck? Return status? Do the mambo? */ KeBugCheck(MEMORY_MANAGEMENT); } if (SectionOffset == NULL) { ViewOffset = 0; } else { ViewOffset = SectionOffset->u.LowPart; } if ((ViewOffset % PAGE_SIZE) != 0) { MmUnlockAddressSpace(AddressSpace); return(STATUS_MAPPED_ALIGNMENT); } if ((*ViewSize) == 0) { (*ViewSize) = Section->MaximumSize.u.LowPart - ViewOffset; } else if (((*ViewSize)+ViewOffset) > Section->MaximumSize.u.LowPart) { (*ViewSize) = Section->MaximumSize.u.LowPart - ViewOffset; } *ViewSize = PAGE_ROUND_UP(*ViewSize); MmLockSectionSegment(Section->Segment); Status = MmMapViewOfSegment(AddressSpace, Section, Section->Segment, BaseAddress, *ViewSize, Protect, ViewOffset, AllocationType & (MEM_TOP_DOWN|SEC_NO_CHANGE)); MmUnlockSectionSegment(Section->Segment); if (!NT_SUCCESS(Status)) { MmUnlockAddressSpace(AddressSpace); return(Status); } } MmUnlockAddressSpace(AddressSpace); return(STATUS_SUCCESS); } /* * @unimplemented */ BOOLEAN NTAPI MmCanFileBeTruncated (IN PSECTION_OBJECT_POINTERS SectionObjectPointer, IN PLARGE_INTEGER NewFileSize) { /* Check whether an ImageSectionObject exists */ if (SectionObjectPointer->ImageSectionObject != NULL) { DPRINT1("ERROR: File can't be truncated because it has an image section\n"); return FALSE; } if (SectionObjectPointer->DataSectionObject != NULL) { PMM_SECTION_SEGMENT Segment; Segment = (PMM_SECTION_SEGMENT)SectionObjectPointer-> DataSectionObject; if (Segment->ReferenceCount != 0) { /* Check size of file */ if (SectionObjectPointer->SharedCacheMap) { PBCB Bcb = SectionObjectPointer->SharedCacheMap; if (NewFileSize->QuadPart <= Bcb->FileSize.QuadPart) { return FALSE; } } } else { /* Something must gone wrong * how can we have a Section but no * reference? */ DPRINT("ERROR: DataSectionObject without reference!\n"); } } DPRINT("FIXME: didn't check for outstanding write probes\n"); return TRUE; } /* * @unimplemented */ BOOLEAN NTAPI MmDisableModifiedWriteOfSection (ULONG Unknown0) { UNIMPLEMENTED; return (FALSE); } /* * @implemented */ BOOLEAN NTAPI MmFlushImageSection (IN PSECTION_OBJECT_POINTERS SectionObjectPointer, IN MMFLUSH_TYPE FlushType) { switch(FlushType) { case MmFlushForDelete: if (SectionObjectPointer->ImageSectionObject || SectionObjectPointer->DataSectionObject) { return FALSE; } CcRosSetRemoveOnClose(SectionObjectPointer); return TRUE; case MmFlushForWrite: break; } return FALSE; } /* * @unimplemented */ BOOLEAN NTAPI MmForceSectionClosed ( IN PSECTION_OBJECT_POINTERS SectionObjectPointer, IN BOOLEAN DelayClose) { UNIMPLEMENTED; return (FALSE); } /* * @implemented */ NTSTATUS NTAPI MmMapViewInSystemSpace (IN PVOID SectionObject, OUT PVOID * MappedBase, IN OUT PSIZE_T ViewSize) { PROS_SECTION_OBJECT Section; PMMSUPPORT AddressSpace; NTSTATUS Status; DPRINT("MmMapViewInSystemSpace() called\n"); Section = (PROS_SECTION_OBJECT)SectionObject; AddressSpace = MmGetKernelAddressSpace(); MmLockAddressSpace(AddressSpace); if ((*ViewSize) == 0) { (*ViewSize) = Section->MaximumSize.u.LowPart; } else if ((*ViewSize) > Section->MaximumSize.u.LowPart) { (*ViewSize) = Section->MaximumSize.u.LowPart; } MmLockSectionSegment(Section->Segment); Status = MmMapViewOfSegment(AddressSpace, Section, Section->Segment, MappedBase, *ViewSize, PAGE_READWRITE, 0, 0); MmUnlockSectionSegment(Section->Segment); MmUnlockAddressSpace(AddressSpace); return Status; } /* * @unimplemented */ NTSTATUS NTAPI MmMapViewInSessionSpace ( IN PVOID Section, OUT PVOID *MappedBase, IN OUT PSIZE_T ViewSize ) { UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED; } /* * @implemented */ NTSTATUS NTAPI MmUnmapViewInSystemSpace (IN PVOID MappedBase) { PMMSUPPORT AddressSpace; NTSTATUS Status; DPRINT("MmUnmapViewInSystemSpace() called\n"); AddressSpace = MmGetKernelAddressSpace(); Status = MmUnmapViewOfSegment(AddressSpace, MappedBase); return Status; } /* * @unimplemented */ NTSTATUS NTAPI MmUnmapViewInSessionSpace ( IN PVOID MappedBase ) { UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED; } /********************************************************************** * NAME EXPORTED * MmCreateSection@ * * DESCRIPTION * Creates a section object. * * ARGUMENTS * SectionObject (OUT) * Caller supplied storage for the resulting pointer * to a SECTION_OBJECT instance; * * DesiredAccess * Specifies the desired access to the section can be a * combination of: * STANDARD_RIGHTS_REQUIRED | * SECTION_QUERY | * SECTION_MAP_WRITE | * SECTION_MAP_READ | * SECTION_MAP_EXECUTE * * ObjectAttributes [OPTIONAL] * Initialized attributes for the object can be used * to create a named section; * * MaximumSize * Maximizes the size of the memory section. Must be * non-NULL for a page-file backed section. * If value specified for a mapped file and the file is * not large enough, file will be extended. * * SectionPageProtection * Can be a combination of: * PAGE_READONLY | * PAGE_READWRITE | * PAGE_WRITEONLY | * PAGE_WRITECOPY * * AllocationAttributes * Can be a combination of: * SEC_IMAGE | * SEC_RESERVE * * FileHandle * Handle to a file to create a section mapped to a file * instead of a memory backed section; * * File * Unknown. * * RETURN VALUE * Status. * * @implemented */ NTSTATUS NTAPI MmCreateSection (OUT PVOID * Section, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN PLARGE_INTEGER MaximumSize, IN ULONG SectionPageProtection, IN ULONG AllocationAttributes, IN HANDLE FileHandle OPTIONAL, IN PFILE_OBJECT File OPTIONAL) { ULONG Protection; PROS_SECTION_OBJECT *SectionObject = (PROS_SECTION_OBJECT *)Section; /* * Check the protection */ Protection = SectionPageProtection & ~(PAGE_GUARD|PAGE_NOCACHE); if (Protection != PAGE_READONLY && Protection != PAGE_READWRITE && Protection != PAGE_WRITECOPY && Protection != PAGE_EXECUTE && Protection != PAGE_EXECUTE_READ && Protection != PAGE_EXECUTE_READWRITE && Protection != PAGE_EXECUTE_WRITECOPY) { return STATUS_INVALID_PAGE_PROTECTION; } if (AllocationAttributes & SEC_IMAGE) { return(MmCreateImageSection(SectionObject, DesiredAccess, ObjectAttributes, MaximumSize, SectionPageProtection, AllocationAttributes, FileHandle)); } if (FileHandle != NULL) { return(MmCreateDataFileSection(SectionObject, DesiredAccess, ObjectAttributes, MaximumSize, SectionPageProtection, AllocationAttributes, FileHandle)); } return(MmCreatePageFileSection(SectionObject, DesiredAccess, ObjectAttributes, MaximumSize, SectionPageProtection, AllocationAttributes)); } NTSTATUS NTAPI NtAreMappedFilesTheSame(IN PVOID File1MappedAsAnImage, IN PVOID File2MappedAsFile) { UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED; } /* EOF */