/* * ReactOS kernel * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* $Id$ * * PROJECT: ReactOS kernel * FILE: ntoskrnl/mm/section.c * PURPOSE: Implements section objects * PROGRAMMER: David Welch (welch@mcmail.com) * UPDATE HISTORY: * Created 22/05/98 */ /* INCLUDES *****************************************************************/ #include #include #include #include #include #include #include #include #include #define NDEBUG #include /* TYPES *********************************************************************/ typedef struct { PSECTION_OBJECT Section; PMM_SECTION_SEGMENT Segment; LARGE_INTEGER Offset; BOOLEAN WasDirty; BOOLEAN Private; } MM_SECTION_PAGEOUT_CONTEXT; /* GLOBALS *******************************************************************/ POBJECT_TYPE EXPORTED MmSectionObjectType = NULL; 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 TAG_MM_SECTION_SEGMENT TAG('M', 'M', 'S', 'S') #define TAG_SECTION_PAGE_TABLE TAG('M', 'S', 'P', 'T') #define PAGE_FROM_SSE(E) ((E) & 0xFFFFF000) #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) /* FUNCTIONS *****************************************************************/ VOID MmFreePageTablesSectionSegment(PMM_SECTION_SEGMENT Segment) { ULONG i; for (i = 0; i < NR_SECTION_PAGE_TABLES; i++) { if (Segment->PageDirectory.PageTables[i] != NULL) { ExFreePool(Segment->PageDirectory.PageTables[i]); } } } VOID MmFreeSectionSegments(PFILE_OBJECT FileObject) { if (FileObject->SectionObjectPointers->ImageSectionObject != NULL) { PMM_IMAGE_SECTION_OBJECT ImageSectionObject; ULONG i; ImageSectionObject = (PMM_IMAGE_SECTION_OBJECT)FileObject->SectionObjectPointers-> ImageSectionObject; for (i = 0; i < ImageSectionObject->NrSegments; i++) { if (ImageSectionObject->Segments[i].ReferenceCount != 0) { DPRINT1("Image segment %d still referenced (was %d)\n", i, ImageSectionObject->Segments[i].ReferenceCount); KeBugCheck(0); } MmFreePageTablesSectionSegment(&ImageSectionObject->Segments[i]); } ExFreePool(ImageSectionObject); FileObject->SectionObjectPointers->ImageSectionObject = NULL; } if (FileObject->SectionObjectPointers->DataSectionObject != NULL) { PMM_SECTION_SEGMENT Segment; Segment = (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointers-> DataSectionObject; if (Segment->ReferenceCount != 0) { DPRINT1("Data segment still referenced\n"); KeBugCheck(0); } MmFreePageTablesSectionSegment(Segment); ExFreePool(Segment); FileObject->SectionObjectPointers->DataSectionObject = NULL; } } VOID MmLockSection(PSECTION_OBJECT Section) { KeWaitForSingleObject(&Section->Lock, UserRequest, KernelMode, FALSE, NULL); } VOID MmUnlockSection(PSECTION_OBJECT Section) { KeReleaseMutex(&Section->Lock, FALSE); } VOID MmLockSectionSegment(PMM_SECTION_SEGMENT Segment) { KeWaitForSingleObject(&Segment->Lock, UserRequest, KernelMode, FALSE, NULL); } VOID MmUnlockSectionSegment(PMM_SECTION_SEGMENT Segment) { KeReleaseMutex(&Segment->Lock, FALSE); } VOID MmSetPageEntrySectionSegment(PMM_SECTION_SEGMENT Segment, ULONG Offset, ULONG Entry) { PSECTION_PAGE_TABLE Table; ULONG DirectoryOffset; ULONG TableOffset; 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); 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 MmGetPageEntrySectionSegment(PMM_SECTION_SEGMENT Segment, ULONG Offset) { PSECTION_PAGE_TABLE Table; ULONG Entry; ULONG DirectoryOffset; ULONG TableOffset; DPRINT("MmGetPageEntrySection(Offset %x)\n", Offset); 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 MmSharePageEntrySectionSegment(PMM_SECTION_SEGMENT Segment, ULONG Offset) { ULONG Entry; Entry = MmGetPageEntrySectionSegment(Segment, Offset); if (Entry == 0) { DPRINT1("Entry == 0 for MmSharePageEntrySectionSegment\n"); KeBugCheck(0); } if (SHARE_COUNT_FROM_SSE(Entry) == MAX_SHARE_COUNT) { DPRINT1("Maximum share count reached\n"); KeBugCheck(0); } if (IS_SWAP_FROM_SSE(Entry)) { KeBugCheck(0); } Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) + 1); MmSetPageEntrySectionSegment(Segment, Offset, Entry); } BOOLEAN MmUnsharePageEntrySectionSegment(PSECTION_OBJECT Section, PMM_SECTION_SEGMENT Segment, ULONG Offset, BOOLEAN Dirty) { ULONG Entry; Entry = MmGetPageEntrySectionSegment(Segment, Offset); if (Entry == 0) { DPRINT1("Entry == 0 for MmSharePageEntrySectionSegment\n"); KeBugCheck(0); } if (SHARE_COUNT_FROM_SSE(Entry) == 0) { DPRINT1("Zero share count for unshare\n"); KeBugCheck(0); } if (IS_SWAP_FROM_SSE(Entry)) { KeBugCheck(0); } 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; PREACTOS_COMMON_FCB_HEADER Fcb; SWAPENTRY SavedSwapEntry; PHYSICAL_ADDRESS Page; MmSetPageEntrySectionSegment(Segment, Offset, 0); FileObject = Section->FileObject; if (FileObject != NULL) { Fcb = (PREACTOS_COMMON_FCB_HEADER)FileObject->FsContext; if (FileObject->Flags & FO_DIRECT_CACHE_PAGING_READ && (Offset % PAGE_SIZE) == 0) { NTSTATUS Status; Status = CcRosUnmapCacheSegment(Fcb->Bcb, Offset, Dirty); if (!NT_SUCCESS(Status)) { KeBugCheck(0); } } } Page = (PHYSICAL_ADDRESS)(LONGLONG)PAGE_FROM_SSE(Entry); SavedSwapEntry = MmGetSavedSwapEntryPage(Page); if (SavedSwapEntry != 0) { MmFreeSwapPage(SavedSwapEntry); MmSetSavedSwapEntryPage(Page, 0); } } else { MmSetPageEntrySectionSegment(Segment, Offset, Entry); } return(SHARE_COUNT_FROM_SSE(Entry) > 1); } NTSTATUS MiReadPage(PMEMORY_AREA MemoryArea, PLARGE_INTEGER Offset, PHYSICAL_ADDRESS* 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. */ { IO_STATUS_BLOCK IoStatus; PFILE_OBJECT FileObject; PMDL Mdl; NTSTATUS Status; PREACTOS_COMMON_FCB_HEADER Fcb; KEVENT Event; FileObject = MemoryArea->Data.SectionData.Section->FileObject; Fcb = (PREACTOS_COMMON_FCB_HEADER)FileObject->FsContext; /* * 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 (FileObject->Flags & FO_DIRECT_CACHE_PAGING_READ && (Offset->QuadPart % PAGE_SIZE) == 0) { ULONG BaseOffset; PVOID BaseAddress; BOOLEAN UptoDate; PCACHE_SEGMENT CacheSeg; PHYSICAL_ADDRESS Addr; /* * 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(Fcb->Bcb, (ULONG)Offset->QuadPart, &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)) { return Status; } } /* * Retrieve the page from the cache segment that we actually want. */ Addr = MmGetPhysicalAddress(BaseAddress + Offset->QuadPart - BaseOffset); (*Page) = Addr; MmReferencePage((*Page)); CcRosReleaseCacheSegment(Fcb->Bcb, CacheSeg, TRUE, FALSE, TRUE); return(STATUS_SUCCESS); } else { /* * 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); } /* * Create an mdl to hold the page we are going to read data into. */ Mdl = MmCreateMdl(NULL, NULL, PAGE_SIZE); MmBuildMdlFromPages(Mdl, &Page->u.LowPart); /* * Call the FSD to read the page */ KeInitializeEvent(&Event, NotificationEvent, FALSE); Status = IoPageRead(FileObject, Mdl, Offset, &Event, &IoStatus); if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); return(IoStatus.Status); } return(Status); } } NTSTATUS MmNotPresentFaultSectionView(PMADDRESS_SPACE AddressSpace, MEMORY_AREA* MemoryArea, PVOID Address, BOOLEAN Locked) { LARGE_INTEGER Offset; LARGE_INTEGER Page; NTSTATUS Status; ULONG PAddress; PSECTION_OBJECT Section; PMM_SECTION_SEGMENT Segment; ULONG Entry; ULONG Entry1; ULONG Attributes; PMM_PAGEOP PageOp; PMM_REGION Region; /* * 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(NULL, Address)) { if (Locked) { MmLockPage(MmGetPhysicalAddressForProcess(NULL, Address)); } return(STATUS_SUCCESS); } PAddress = (ULONG)PAGE_ROUND_DOWN(((ULONG)Address)); Offset.QuadPart = (PAddress - (ULONG)MemoryArea->BaseAddress) + MemoryArea->Data.SectionData.ViewOffset; /* * Lock the segment */ Segment = MemoryArea->Data.SectionData.Segment; Section = MemoryArea->Data.SectionData.Section; Region = MmFindRegion(MemoryArea->BaseAddress, &MemoryArea->Data.SectionData.RegionListHead, Address, NULL); MmLockSection(Section); 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 = PAGE_READONLY; } else { Attributes = Region->Protect; } /* * Get or create a page operation descriptor */ PageOp = MmGetPageOp(MemoryArea, 0, 0, Segment, Offset.u.LowPart, MM_PAGEOP_PAGEIN); if (PageOp == NULL) { DPRINT1("MmGetPageOp failed\n"); KeBugCheck(0); } /* * Check if someone else is already handling this fault, if so wait * for them */ if (PageOp->Thread != PsGetCurrentThread()) { MmUnlockSectionSegment(Segment); MmUnlockSection(Section); MmUnlockAddressSpace(AddressSpace); Status = KeWaitForSingleObject(&PageOp->CompletionEvent, 0, KernelMode, FALSE, NULL); /* * Check for various strange conditions */ if (Status != STATUS_SUCCESS) { DPRINT1("Failed to wait for page op\n"); KeBugCheck(0); } if (PageOp->Status == STATUS_PENDING) { DPRINT1("Woke for page op before completion\n"); KeBugCheck(0); } /* * If this wasn't a pagein then restart the operation */ if (PageOp->OpType != MM_PAGEOP_PAGEIN) { MmLockAddressSpace(AddressSpace); MmReleasePageOp(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)) { MmLockAddressSpace(AddressSpace); DPRINT("Address 0x%.8X\n", Address); return(PageOp->Status); } MmLockAddressSpace(AddressSpace); MmLockSection(Section); MmLockSectionSegment(Segment); /* * If the completed fault was for another address space then set the * page in this one. */ if (!MmIsPagePresent(NULL, Address)) { Entry = MmGetPageEntrySectionSegment(Segment, Offset.u.LowPart); if (Entry == 0) { MmUnlockSectionSegment(Segment); MmUnlockSection(Section); MmReleasePageOp(PageOp); return(STATUS_MM_RESTART_OPERATION); } Page = (LARGE_INTEGER)(LONGLONG)(PAGE_FROM_SSE(Entry)); MmReferencePage(Page); MmSharePageEntrySectionSegment(Segment, Offset.u.LowPart); Status = MmCreateVirtualMapping(PsGetCurrentProcess(), Address, Attributes, Page, FALSE); if (!NT_SUCCESS(Status)) { DbgPrint("Unable to create virtual mapping\n"); KeBugCheck(0); } MmInsertRmap(Page, PsGetCurrentProcess(), (PVOID)PAGE_ROUND_DOWN(Address)); } if (Locked) { MmLockPage(MmGetPhysicalAddressForProcess(NULL, Address)); } MmUnlockSectionSegment(Segment); MmUnlockSection(Section); MmReleasePageOp(PageOp); DPRINT("Address 0x%.8X\n", Address); return(STATUS_SUCCESS); } /* * Must be private page we have swapped out. */ if (MmIsPageSwapEntry(NULL, (PVOID)PAddress)) { SWAPENTRY SwapEntry; PMDL Mdl; MmUnlockSectionSegment(Segment); MmUnlockSection(Section); MmDeletePageFileMapping(NULL, (PVOID)PAddress, &SwapEntry); Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page); if (!NT_SUCCESS(Status)) { KeBugCheck(0); } Mdl = MmCreateMdl(NULL, NULL, PAGE_SIZE); MmBuildMdlFromPages(Mdl, (PULONG)&Page); Status = MmReadFromSwapPage(SwapEntry, Mdl); if (!NT_SUCCESS(Status)) { KeBugCheck(0); } Status = MmCreateVirtualMapping(PsGetCurrentProcess(), Address, Region->Protect, Page, FALSE); while (Status == STATUS_NO_MEMORY) { MmUnlockAddressSpace(AddressSpace); Status = MmCreateVirtualMapping(PsGetCurrentProcess(), Address, Region->Protect, Page, TRUE); MmLockAddressSpace(AddressSpace); } if (!NT_SUCCESS(Status)) { DPRINT1("MmCreateVirtualMapping failed, not out of memory\n"); KeBugCheck(0); return(Status); } /* * Store the swap entry for later use. */ MmSetSavedSwapEntryPage(Page, SwapEntry); /* * Add the page to the process's working set */ MmInsertRmap(Page, PsGetCurrentProcess(), (PVOID)PAGE_ROUND_DOWN(Address)); /* * Finish the operation */ if (Locked) { MmLockPage(MmGetPhysicalAddressForProcess(NULL, Address)); } PageOp->Status = STATUS_SUCCESS; KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE); MmReleasePageOp(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) { /* * Just map the desired physical page */ Status = MmCreateVirtualMapping(PsGetCurrentProcess(), Address, Region->Protect, Offset, FALSE); /* * Don't add an rmap entry since the page mapped could be for * anything. */ if (Locked) { MmLockPage(MmGetPhysicalAddressForProcess(NULL, Address)); } /* * Cleanup and release locks */ PageOp->Status = STATUS_SUCCESS; KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE); MmReleasePageOp(PageOp); MmUnlockSectionSegment(Segment); MmUnlockSection(Section); DPRINT("Address 0x%.8X\n", Address); return(STATUS_SUCCESS); } /* * Map anonymous memory for BSS sections */ if (Segment->Characteristics & IMAGE_SECTION_CHAR_BSS) { Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page); if (!NT_SUCCESS(Status)) { MmUnlockSectionSegment(Segment); MmUnlockSection(Section); MmUnlockAddressSpace(AddressSpace); MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page); MmLockAddressSpace(AddressSpace); MmLockSection(Section); MmLockSectionSegment(Segment); } Status = MmCreateVirtualMapping(PsGetCurrentProcess(), Address, Region->Protect, Page, FALSE); MmInsertRmap(Page, PsGetCurrentProcess(), (PVOID)PAGE_ROUND_DOWN(Address)); if (Locked) { MmLockPage(MmGetPhysicalAddressForProcess(NULL, Address)); } /* * Cleanup and release locks */ PageOp->Status = STATUS_SUCCESS; KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE); MmReleasePageOp(PageOp); MmUnlockSectionSegment(Segment); MmUnlockSection(Section); DPRINT("Address 0x%.8X\n", Address); return(STATUS_SUCCESS); } /* * Get the entry corresponding to the offset within the section */ Entry = MmGetPageEntrySectionSegment(Segment, Offset.u.LowPart); 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); MmUnlockSection(Section); MmUnlockAddressSpace(AddressSpace); if (Segment->Flags & MM_PAGEFILE_SEGMENT) { Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page); } else { Status = MiReadPage(MemoryArea, &Offset, &Page); } if (!NT_SUCCESS(Status) && Status != STATUS_END_OF_FILE) { /* * FIXME: What do we know in this case? */ DPRINT1("IoPageRead failed (Status %x)\n", Status); /* * Cleanup and release locks */ PageOp->Status = Status; KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE); MmReleasePageOp(PageOp); MmLockAddressSpace(AddressSpace); return(Status); } /* * Relock the address space, section and segment */ MmLockAddressSpace(AddressSpace); MmLockSection(Section); MmLockSectionSegment(Segment); /* * Check the entry. No one should change the status of a page * that has a pending page-in. */ Entry1 = MmGetPageEntrySectionSegment(Segment, Offset.QuadPart); if (Entry != Entry1) { DbgPrint("Someone changed ppte entry while we slept\n"); KeBugCheck(0); } /* * Mark the offset within the section as having valid, in-memory * data */ Entry = Page.u.LowPart; MmSetPageEntrySectionSegment(Segment, Offset.QuadPart, Entry); MmSharePageEntrySectionSegment(Segment, Offset.QuadPart); Status = MmCreateVirtualMapping(PsGetCurrentProcess(), Address, Attributes, Page, FALSE); if (!NT_SUCCESS(Status)) { MmUnlockSectionSegment(Segment); MmUnlockSection(Section); MmUnlockAddressSpace(AddressSpace); Status = MmCreateVirtualMapping(PsGetCurrentProcess(), Address, Attributes, Page, TRUE); if (!NT_SUCCESS(Status)) { KeBugCheck(0); } MmLockAddressSpace(AddressSpace); MmLockSection(Section); MmLockSectionSegment(Segment); } MmInsertRmap(Page, PsGetCurrentProcess(), (PVOID)PAGE_ROUND_DOWN(Address)); if (!NT_SUCCESS(Status)) { DbgPrint("Unable to create virtual mapping\n"); KeBugCheck(0); } if (Locked) { MmLockPage(MmGetPhysicalAddressForProcess(NULL, Address)); } PageOp->Status = STATUS_SUCCESS; KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE); MmReleasePageOp(PageOp); MmUnlockSectionSegment(Segment); MmUnlockSection(Section); DPRINT("MmNotPresentFaultSectionView succeeded\n"); return(STATUS_SUCCESS); } else if (IS_SWAP_FROM_SSE(Entry)) { SWAPENTRY SwapEntry; PMDL Mdl; SwapEntry = SWAPENTRY_FROM_SSE(Entry); /* * Release all our locks and read in the page from disk */ MmUnlockSectionSegment(Segment); MmUnlockSection(Section); MmUnlockAddressSpace(AddressSpace); Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page); if (!NT_SUCCESS(Status)) { KeBugCheck(0); } Mdl = MmCreateMdl(NULL, NULL, PAGE_SIZE); MmBuildMdlFromPages(Mdl, (PULONG)&Page); Status = MmReadFromSwapPage(SwapEntry, Mdl); if (!NT_SUCCESS(Status)) { KeBugCheck(0); } /* * Relock the address space, section and segment */ MmLockAddressSpace(AddressSpace); MmLockSection(Section); MmLockSectionSegment(Segment); /* * Check the entry. No one should change the status of a page * that has a pending page-in. */ Entry1 = MmGetPageEntrySectionSegment(Segment, Offset.QuadPart); if (Entry != Entry1) { DbgPrint("Someone changed ppte entry while we slept\n"); KeBugCheck(0); } /* * Mark the offset within the section as having valid, in-memory * data */ Entry = Page.u.LowPart; MmSetPageEntrySectionSegment(Segment, Offset.QuadPart, Entry); MmSharePageEntrySectionSegment(Segment, Offset.QuadPart); /* * Save the swap entry. */ MmSetSavedSwapEntryPage(Page, SwapEntry); Status = MmCreateVirtualMapping(PsGetCurrentProcess(), Address, Attributes, Page, FALSE); MmInsertRmap(Page, PsGetCurrentProcess(), (PVOID)PAGE_ROUND_DOWN(Address)); if (!NT_SUCCESS(Status)) { DbgPrint("Unable to create virtual mapping\n"); KeBugCheck(0); } if (Locked) { MmLockPage(MmGetPhysicalAddressForProcess(NULL, Address)); } PageOp->Status = STATUS_SUCCESS; KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE); MmReleasePageOp(PageOp); MmUnlockSectionSegment(Segment); MmUnlockSection(Section); DPRINT("MmNotPresentFaultSectionView succeeded\n"); return(STATUS_SUCCESS); } else { /* * If the section offset is already in-memory and valid then just * take another reference to the page */ Page = (LARGE_INTEGER)(LONGLONG)PAGE_FROM_SSE(Entry); MmReferencePage(Page); MmSharePageEntrySectionSegment(Segment, Offset.QuadPart); Status = MmCreateVirtualMapping(PsGetCurrentProcess(), Address, Attributes, Page, FALSE); MmInsertRmap(Page, PsGetCurrentProcess(), (PVOID)PAGE_ROUND_DOWN(Address)); if (!NT_SUCCESS(Status)) { DbgPrint("Unable to create virtual mapping\n"); KeBugCheck(0); } if (Locked) { MmLockPage(MmGetPhysicalAddressForProcess(NULL, Address)); } PageOp->Status = STATUS_SUCCESS; KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE); MmReleasePageOp(PageOp); MmUnlockSectionSegment(Segment); MmUnlockSection(Section); return(STATUS_SUCCESS); } } NTSTATUS MmAccessFaultSectionView(PMADDRESS_SPACE AddressSpace, MEMORY_AREA* MemoryArea, PVOID Address, BOOLEAN Locked) { PMM_SECTION_SEGMENT Segment; PSECTION_OBJECT Section; PHYSICAL_ADDRESS OldPage; PHYSICAL_ADDRESS NewPage; PVOID NewAddress; NTSTATUS Status; ULONG PAddress; LARGE_INTEGER Offset; PMM_PAGEOP PageOp; PMM_REGION Region; /* * Check if the page has been paged out or has already been set readwrite */ if (!MmIsPagePresent(NULL, Address) || MmGetPageProtect(NULL, Address) & PAGE_READWRITE) { return(STATUS_SUCCESS); } /* * Find the offset of the page */ PAddress = (ULONG)PAGE_ROUND_DOWN(((ULONG)Address)); Offset.QuadPart = (PAddress - (ULONG)MemoryArea->BaseAddress) + MemoryArea->Data.SectionData.ViewOffset; /* * Lock the segment */ Segment = MemoryArea->Data.SectionData.Segment; Section = MemoryArea->Data.SectionData.Section; Region = MmFindRegion(MemoryArea->BaseAddress, &MemoryArea->Data.SectionData.RegionListHead, Address, NULL); MmLockSection(Section); MmLockSectionSegment(Segment); /* * Sanity check. */ if (MmGetPageEntrySectionSegment(Segment, Offset.QuadPart) == 0) { DPRINT1("COW fault for page with PESS 0. Address was 0x%.8X\n", Address); } /* * Check if we are doing COW */ if (!((Segment->WriteCopy || MemoryArea->Data.SectionData.WriteCopyView) && (Region->Protect == PAGE_READWRITE || Region->Protect == PAGE_EXECUTE_READWRITE))) { MmUnlockSection(Section); MmUnlockSectionSegment(Segment); return(STATUS_UNSUCCESSFUL); } /* * Get or create a pageop */ PageOp = MmGetPageOp(MemoryArea, 0, 0, Segment, Offset.u.LowPart, MM_PAGEOP_ACCESSFAULT); if (PageOp == NULL) { DPRINT1("MmGetPageOp failed\n"); KeBugCheck(0); } /* * Wait for any other operations to complete */ if (PageOp->Thread != PsGetCurrentThread()) { MmUnlockSectionSegment(Segment); MmUnlockSection(Section); MmUnlockAddressSpace(AddressSpace); Status = KeWaitForSingleObject(&PageOp->CompletionEvent, 0, KernelMode, FALSE, NULL); /* * Check for various strange conditions */ if (Status != STATUS_SUCCESS) { DPRINT1("Failed to wait for page op\n"); KeBugCheck(0); } if (PageOp->Status == STATUS_PENDING) { DPRINT1("Woke for page op before completion\n"); KeBugCheck(0); } /* * Restart the operation */ MmLockAddressSpace(AddressSpace); MmReleasePageOp(PageOp); return(STATUS_MM_RESTART_OPERATION); } /* * Release locks now we have the pageop */ MmUnlockSectionSegment(Segment); MmUnlockSection(Section); MmUnlockAddressSpace(AddressSpace); /* * Allocate a page */ Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &NewPage); /* * Copy the old page */ OldPage = MmGetPhysicalAddressForProcess(NULL, Address); NewAddress = ExAllocatePageWithPhysPage(NewPage); memcpy(NewAddress, (PVOID)PAGE_ROUND_DOWN(Address), PAGE_SIZE); ExUnmapPage(NewAddress); /* * Delete the old entry. */ MmDeleteVirtualMapping(PsGetCurrentProcess(), Address, FALSE, NULL, NULL); /* * Set the PTE to point to the new page */ MmLockAddressSpace(AddressSpace); Status = MmCreateVirtualMapping(PsGetCurrentProcess(), Address, Region->Protect, NewPage, FALSE); MmInsertRmap(NewPage, PsGetCurrentProcess(), (PVOID)PAGE_ROUND_DOWN(Address)); if (!NT_SUCCESS(Status)) { DbgPrint("Unable to create virtual mapping\n"); KeBugCheck(0); } if (Locked) { MmLockPage(MmGetPhysicalAddressForProcess(NULL, Address)); } /* * Unshare the old page. */ MmUnsharePageEntrySectionSegment(Section, Segment, Offset.QuadPart, FALSE); MmDeleteRmap(OldPage, PsGetCurrentProcess(), (PVOID)PAGE_ROUND_DOWN(Address)); MmReleasePageMemoryConsumer(MC_USER, OldPage); PageOp->Status = STATUS_SUCCESS; KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE); MmReleasePageOp(PageOp); return(STATUS_SUCCESS); } VOID MmPageOutDeleteMapping(PVOID Context, PEPROCESS Process, PVOID Address) { MM_SECTION_PAGEOUT_CONTEXT* PageOutContext; BOOL WasDirty; PHYSICAL_ADDRESS PhysicalAddress; PageOutContext = (MM_SECTION_PAGEOUT_CONTEXT*)Context; MmDeleteVirtualMapping(Process, Address, FALSE, &WasDirty, &PhysicalAddress); if (WasDirty) { PageOutContext->WasDirty = TRUE; } if (!PageOutContext->Private) { MmUnsharePageEntrySectionSegment(PageOutContext->Section, PageOutContext->Segment, PageOutContext->Offset.u.LowPart, PageOutContext->WasDirty); } MmReleasePageMemoryConsumer(MC_USER, PhysicalAddress); } NTSTATUS MmPageOutSectionView(PMADDRESS_SPACE AddressSpace, MEMORY_AREA* MemoryArea, PVOID Address, PMM_PAGEOP PageOp) { LARGE_INTEGER Offset; PSECTION_OBJECT Section; PMM_SECTION_SEGMENT Segment; PHYSICAL_ADDRESS PhysicalAddress; MM_SECTION_PAGEOUT_CONTEXT Context; SWAPENTRY SwapEntry; PMDL Mdl; ULONG Entry; BOOLEAN Private; NTSTATUS Status; PFILE_OBJECT FileObject; PREACTOS_COMMON_FCB_HEADER Fcb; BOOLEAN DirectMapped; Address = (PVOID)PAGE_ROUND_DOWN(Address); Offset.QuadPart = (ULONG)(Address - (ULONG)MemoryArea->BaseAddress) + MemoryArea->Data.SectionData.ViewOffset; FileObject = MemoryArea->Data.SectionData.Section->FileObject; DirectMapped = FALSE; if (FileObject != NULL) { Fcb = (PREACTOS_COMMON_FCB_HEADER)FileObject->FsContext; /* * 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 (FileObject->Flags & FO_DIRECT_CACHE_PAGING_READ && (Offset.QuadPart % PAGE_SIZE) == 0) { DirectMapped = TRUE; } } /* * Get the segment and section. */ Segment = MemoryArea->Data.SectionData.Segment; Section = MemoryArea->Data.SectionData.Section; /* * This should never happen since mappings of physical memory are never * placed in the rmap lists. */ if (Section->AllocationAttributes & SEC_PHYSICALMEMORY) { DPRINT1("Trying to page out from physical memory section address 0x%X " "process %d\n", Address, AddressSpace->Process->UniqueProcessId); KeBugCheck(0); } /* * Get the section segment entry and the physical address. */ Entry = MmGetPageEntrySectionSegment(Segment, Offset.QuadPart); if (!MmIsPagePresent(AddressSpace->Process, Address)) { DPRINT1("Trying to page out not-present page at (%d,0x%.8X).\n", AddressSpace->Process->UniqueProcessId, Address); KeBugCheck(0); } PhysicalAddress = MmGetPhysicalAddressForProcess(AddressSpace->Process, Address); SwapEntry = MmGetSavedSwapEntryPage(PhysicalAddress); /* * Prepare the context structure for the rmap delete call. */ Context.Section = Section; Context.Segment = Segment; Context.Offset = Offset; Context.WasDirty = FALSE; if (Segment->Characteristics & IMAGE_SECTION_CHAR_BSS || IS_SWAP_FROM_SSE(Entry) || (LONGLONG)PAGE_FROM_SSE(Entry) != PhysicalAddress.QuadPart) { Context.Private = Private = TRUE; } else { Context.Private = Private = FALSE; } /* * Take an additional reference to the page. */ MmReferencePage(PhysicalAddress); /* * Paging out data mapped read-only is easy. */ if (MemoryArea->Attributes & PAGE_READONLY || MemoryArea->Attributes & PAGE_EXECUTE_READ) { /* * Read-only data should never be in the swapfile. */ if (SwapEntry != 0) { DPRINT1("SwapEntry != 0 was 0x%.8X at address 0x%.8X, " "paddress 0x%.8X\n", SwapEntry, Address, PhysicalAddress); KeBugCheck(0); } /* * Read-only data should never be COWed */ if (Private) { DPRINT1("Had private copy of read-only page.\n"); KeBugCheck(0); } /* * Delete all mappings of this page. */ MmDeleteAllRmaps(PhysicalAddress, (PVOID)&Context, MmPageOutDeleteMapping); if (Context.WasDirty) { KeBugCheck(0); } /* * If this page wasn't direct mapped then we have a private copy so * release back to the system; otherwise the cache manager will have * handled freeing the cache segment which we mapped from. */ if (!DirectMapped) { MmReleasePageMemoryConsumer(MC_USER, PhysicalAddress); } PageOp->Status = STATUS_SUCCESS; KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE); MmReleasePageOp(PageOp); return(STATUS_SUCCESS); } /* * Otherwise we have read-write data. */ MmDeleteAllRmaps(PhysicalAddress, (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 (!Private && MmGetPageEntrySectionSegment(Segment, Offset.QuadPart) != 0) { KeBugCheck(0); } /* * 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 (!Context.WasDirty) { if (!DirectMapped || Private) { MmSetSavedSwapEntryPage(PhysicalAddress, 0); MmReleasePageMemoryConsumer(MC_USER, PhysicalAddress); } if (Private) { if (!(Segment->Characteristics & IMAGE_SECTION_CHAR_BSS) && SwapEntry == 0) { DPRINT1("Private page, non-dirty but not swapped out " "process %d address 0x%.8X\n", AddressSpace->Process->UniqueProcessId, Address); KeBugCheck(0); } else { Status = MmCreatePageFileMapping(AddressSpace->Process, Address, SwapEntry); if (!NT_SUCCESS(Status)) { KeBugCheck(0); } } } PageOp->Status = STATUS_SUCCESS; KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE); MmReleasePageOp(PageOp); return(STATUS_SUCCESS); } /* * If this page was direct mapped from the cache then the cache manager * will already have taken care of writing it back. */ if (DirectMapped && !Private) { assert(SwapEntry == 0); MmReleasePageMemoryConsumer(MC_USER, PhysicalAddress); PageOp->Status = STATUS_SUCCESS; KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE); MmReleasePageOp(PageOp); return(STATUS_SUCCESS); } /* * If necessary, allocate an entry in the paging file for this page */ SwapEntry = MmGetSavedSwapEntryPage(PhysicalAddress); if (SwapEntry == 0) { SwapEntry = MmAllocSwapPage(); if (SwapEntry == 0) { MmShowOutOfSpaceMessagePagingFile(); /* * For private pages restore the old mappings. */ if (Private) { Status = MmCreateVirtualMapping(MemoryArea->Process, Address, MemoryArea->Attributes, PhysicalAddress, FALSE); MmSetDirtyPage(MemoryArea->Process, Address); MmInsertRmap(PhysicalAddress, MemoryArea->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(MemoryArea->Process, Address, MemoryArea->Attributes, PhysicalAddress, FALSE); MmSetDirtyPage(MemoryArea->Process, Address); MmInsertRmap(PhysicalAddress, MemoryArea->Process, Address); MmSetPageEntrySectionSegment(Segment, Offset.QuadPart, PhysicalAddress.u.LowPart); MmSharePageEntrySectionSegment(Segment, Offset.QuadPart); } PageOp->Status = STATUS_UNSUCCESSFUL; KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE); MmReleasePageOp(PageOp); return(STATUS_UNSUCCESSFUL); } } /* * Write the page to the pagefile */ Mdl = MmCreateMdl(NULL, NULL, PAGE_SIZE); MmBuildMdlFromPages(Mdl, (PULONG)&PhysicalAddress); Status = MmWriteToSwapPage(SwapEntry, Mdl); 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. */ if (Private) { Status = MmCreateVirtualMapping(MemoryArea->Process, Address, MemoryArea->Attributes, PhysicalAddress, FALSE); MmSetDirtyPage(MemoryArea->Process, Address); MmInsertRmap(PhysicalAddress, MemoryArea->Process, Address); } else { Status = MmCreateVirtualMapping(MemoryArea->Process, Address, MemoryArea->Attributes, PhysicalAddress, FALSE); MmSetDirtyPage(MemoryArea->Process, Address); MmInsertRmap(PhysicalAddress, MemoryArea->Process, Address); MmSetPageEntrySectionSegment(Segment, Offset.QuadPart, PhysicalAddress.u.LowPart); MmSharePageEntrySectionSegment(Segment, Offset.QuadPart); } PageOp->Status = STATUS_UNSUCCESSFUL; KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE); MmReleasePageOp(PageOp); return(STATUS_UNSUCCESSFUL); } /* * Otherwise we have succeeded. */ DPRINT("MM: Wrote section page 0x%.8X to swap!\n", PhysicalAddress); MmSetSavedSwapEntryPage(PhysicalAddress, 0); MmReleasePageMemoryConsumer(MC_USER, PhysicalAddress); if (Private) { Status = MmCreatePageFileMapping(MemoryArea->Process, Address, SwapEntry); if (!NT_SUCCESS(Status)) { KeBugCheck(0); } } else { Entry = MAKE_SWAP_SSE(SwapEntry); MmSetPageEntrySectionSegment(Segment, Offset.QuadPart, Entry); } PageOp->Status = STATUS_SUCCESS; KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE); MmReleasePageOp(PageOp); return(STATUS_SUCCESS); } NTSTATUS MmWritePageSectionView(PMADDRESS_SPACE AddressSpace, PMEMORY_AREA MemoryArea, PVOID Address, PMM_PAGEOP PageOp) { LARGE_INTEGER Offset; PSECTION_OBJECT Section; PMM_SECTION_SEGMENT Segment; PHYSICAL_ADDRESS PhysicalAddress; SWAPENTRY SwapEntry; PMDL Mdl; ULONG Entry; BOOLEAN Private; NTSTATUS Status; PFILE_OBJECT FileObject; PREACTOS_COMMON_FCB_HEADER Fcb = NULL; BOOLEAN DirectMapped; Address = (PVOID)PAGE_ROUND_DOWN(Address); Offset.QuadPart = (ULONG)(Address - (ULONG)MemoryArea->BaseAddress) + MemoryArea->Data.SectionData.ViewOffset; FileObject = MemoryArea->Data.SectionData.Section->FileObject; DirectMapped = FALSE; if (FileObject != NULL) { Fcb = (PREACTOS_COMMON_FCB_HEADER)FileObject->FsContext; /* * 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 (FileObject->Flags & FO_DIRECT_CACHE_PAGING_READ && (Offset.QuadPart % PAGE_SIZE) == 0) { DirectMapped = TRUE; } } /* * Get the segment and section. */ Segment = MemoryArea->Data.SectionData.Segment; Section = MemoryArea->Data.SectionData.Section; /* * 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, AddressSpace->Process->UniqueProcessId); KeBugCheck(0); } /* * Get the section segment entry and the physical address. */ Entry = MmGetPageEntrySectionSegment(Segment, Offset.QuadPart); if (!MmIsPagePresent(AddressSpace->Process, Address)) { DPRINT1("Trying to page out not-present page at (%d,0x%.8X).\n", AddressSpace->Process->UniqueProcessId, Address); KeBugCheck(0); } PhysicalAddress = MmGetPhysicalAddressForProcess(AddressSpace->Process, Address); SwapEntry = MmGetSavedSwapEntryPage(PhysicalAddress); /* * Check for a private (COWed) page. */ if (Segment->Characteristics & IMAGE_SECTION_CHAR_BSS || IS_SWAP_FROM_SSE(Entry) || (LONGLONG)PAGE_FROM_SSE(Entry) != PhysicalAddress.QuadPart) { Private = TRUE; } else { Private = FALSE; } /* * Speculatively set all mappings of the page to clean. */ MmSetCleanAllRmaps(PhysicalAddress); /* * 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(Fcb->Bcb, Offset.u.LowPart); PageOp->Status = STATUS_SUCCESS; KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE); MmReleasePageOp(PageOp); return(STATUS_SUCCESS); } /* * If necessary, allocate an entry in the paging file for this page */ SwapEntry = MmGetSavedSwapEntryPage(PhysicalAddress); if (SwapEntry == 0) { SwapEntry = MmAllocSwapPage(); if (SwapEntry == 0) { MmSetDirtyAllRmaps(PhysicalAddress); PageOp->Status = STATUS_UNSUCCESSFUL; KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE); MmReleasePageOp(PageOp); return(STATUS_UNSUCCESSFUL); } } /* * Write the page to the pagefile */ Mdl = MmCreateMdl(NULL, NULL, PAGE_SIZE); MmBuildMdlFromPages(Mdl, (PULONG)&PhysicalAddress); Status = MmWriteToSwapPage(SwapEntry, Mdl); if (!NT_SUCCESS(Status)) { DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n", Status); MmSetDirtyAllRmaps(PhysicalAddress); PageOp->Status = STATUS_UNSUCCESSFUL; KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE); MmReleasePageOp(PageOp); return(STATUS_UNSUCCESSFUL); } /* * Otherwise we have succeeded. */ DPRINT("MM: Wrote section page 0x%.8X to swap!\n", PhysicalAddress); MmSetSavedSwapEntryPage(PhysicalAddress, SwapEntry); PageOp->Status = STATUS_SUCCESS; KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE); MmReleasePageOp(PageOp); return(STATUS_SUCCESS); } VOID STATIC MmAlterViewAttributes(PMADDRESS_SPACE AddressSpace, PVOID BaseAddress, ULONG RegionSize, ULONG OldType, ULONG OldProtect, ULONG NewType, ULONG NewProtect) { PMEMORY_AREA MemoryArea; PMM_SECTION_SEGMENT Segment; BOOL DoCOW = FALSE; ULONG i; MemoryArea = MmOpenMemoryAreaByAddress(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 < (RegionSize / PAGE_SIZE); i++) { PVOID Address = 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(AddressSpace->Process, Address)) { LARGE_INTEGER Offset; ULONG Entry; LARGE_INTEGER PhysicalAddress; Offset.QuadPart = (ULONG)(Address - (ULONG)MemoryArea->BaseAddress) + MemoryArea->Data.SectionData.ViewOffset; Entry = MmGetPageEntrySectionSegment(Segment, Offset.QuadPart); PhysicalAddress = MmGetPhysicalAddressForProcess(AddressSpace->Process, Address); Protect = PAGE_READONLY; if ((Segment->Characteristics & IMAGE_SECTION_CHAR_BSS || IS_SWAP_FROM_SSE(Entry) || (LONGLONG)PAGE_FROM_SSE(Entry) != PhysicalAddress.QuadPart)) { Protect = NewProtect; } } if (MmIsPagePresent(AddressSpace->Process, Address)) { MmSetPageProtect(AddressSpace->Process, BaseAddress, Protect); } } } } NTSTATUS MmProtectSectionView(PMADDRESS_SPACE AddressSpace, PMEMORY_AREA MemoryArea, PVOID BaseAddress, ULONG Length, ULONG Protect, PULONG OldProtect) { PMM_REGION Region; NTSTATUS Status; Length = min(Length, MemoryArea->BaseAddress + MemoryArea->Length - BaseAddress); Region = MmFindRegion(MemoryArea->BaseAddress, &MemoryArea->Data.SectionData.RegionListHead, BaseAddress, NULL); *OldProtect = Region->Protect; Status = MmAlterRegion(AddressSpace, MemoryArea->BaseAddress, &MemoryArea->Data.SectionData.RegionListHead, BaseAddress, Length, Region->Type, Protect, MmAlterViewAttributes); return(Status); } NTSTATUS STDCALL MmQuerySectionView(PMEMORY_AREA MemoryArea, PVOID Address, PMEMORY_BASIC_INFORMATION Info, PULONG ResultLength) { PMM_REGION Region; PVOID RegionBaseAddress; Region = MmFindRegion(MemoryArea->BaseAddress, &MemoryArea->Data.SectionData.RegionListHead, Address, &RegionBaseAddress); Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address); Info->AllocationBase = MemoryArea->BaseAddress; Info->AllocationProtect = MemoryArea->Attributes; Info->RegionSize = MemoryArea->Length; Info->State = MEM_COMMIT; Info->Protect = Region->Protect; if (MemoryArea->Data.SectionData.Section->AllocationAttributes & SEC_IMAGE) { Info->Type = MEM_IMAGE; } else { Info->Type = MEM_MAPPED; } return(STATUS_SUCCESS); } VOID STDCALL MmpDeleteSection(PVOID ObjectBody) { PSECTION_OBJECT Section = (PSECTION_OBJECT)ObjectBody; DPRINT("MmpDeleteSection(ObjectBody %x)\n", ObjectBody); if (Section->AllocationAttributes & SEC_IMAGE) { ULONG i; for (i = 0; i < Section->NrSegments; i++) { InterlockedDecrement(&Section->Segments[i].ReferenceCount); } } else { InterlockedDecrement(&Section->Segments->ReferenceCount); } if (Section->FileObject != NULL) { CcRosDereferenceCache(Section->FileObject); ObDereferenceObject(Section->FileObject); Section->FileObject = NULL; } } VOID STDCALL MmpCloseSection(PVOID ObjectBody, ULONG HandleCount) { DPRINT("MmpCloseSection(OB %x, HC %d) RC %d\n", ObjectBody, HandleCount, ObGetObjectPointerCount(ObjectBody)); } NTSTATUS STDCALL MmpCreateSection(PVOID ObjectBody, PVOID Parent, PWSTR RemainingPath, POBJECT_ATTRIBUTES ObjectAttributes) { DPRINT("MmpCreateSection(ObjectBody %x, Parent %x, RemainingPath %S)\n", ObjectBody, Parent, RemainingPath); if (RemainingPath == NULL) { return(STATUS_SUCCESS); } if (wcschr(RemainingPath+1, L'\\') != NULL) { return(STATUS_UNSUCCESSFUL); } return(STATUS_SUCCESS); } NTSTATUS MmCreatePhysicalMemorySection(VOID) { HANDLE PhysSectionH; PSECTION_OBJECT PhysSection; NTSTATUS Status; OBJECT_ATTRIBUTES Obj; UNICODE_STRING Name = UNICODE_STRING_INITIALIZER(L"\\Device\\PhysicalMemory"); LARGE_INTEGER SectionSize; /* * Create the section mapping physical memory */ SectionSize.QuadPart = 0xFFFFFFFF; InitializeObjectAttributes(&Obj, &Name, 0, NULL, NULL); Status = NtCreateSection(&PhysSectionH, SECTION_ALL_ACCESS, &Obj, &SectionSize, PAGE_EXECUTE_READWRITE, 0, NULL); if (!NT_SUCCESS(Status)) { DbgPrint("Failed to create PhysicalMemory section\n"); KeBugCheck(0); } Status = ObReferenceObjectByHandle(PhysSectionH, SECTION_ALL_ACCESS, NULL, KernelMode, (PVOID*)&PhysSection, NULL); if (!NT_SUCCESS(Status)) { DbgPrint("Failed to reference PhysicalMemory section\n"); KeBugCheck(0); } PhysSection->AllocationAttributes |= SEC_PHYSICALMEMORY; ObDereferenceObject((PVOID)PhysSection); return(STATUS_SUCCESS); } NTSTATUS MmInitSectionImplementation(VOID) { MmSectionObjectType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE)); RtlInitUnicodeStringFromLiteral(&MmSectionObjectType->TypeName, L"Section"); MmSectionObjectType->Tag = TAG('S', 'E', 'C', 'T'); MmSectionObjectType->TotalObjects = 0; MmSectionObjectType->TotalHandles = 0; MmSectionObjectType->MaxObjects = ULONG_MAX; MmSectionObjectType->MaxHandles = ULONG_MAX; MmSectionObjectType->PagedPoolCharge = 0; MmSectionObjectType->NonpagedPoolCharge = sizeof(SECTION_OBJECT); MmSectionObjectType->Mapping = &MmpSectionMapping; MmSectionObjectType->Dump = NULL; MmSectionObjectType->Open = NULL; MmSectionObjectType->Close = MmpCloseSection; MmSectionObjectType->Delete = MmpDeleteSection; MmSectionObjectType->Parse = NULL; MmSectionObjectType->Security = NULL; MmSectionObjectType->QueryName = NULL; MmSectionObjectType->OkayToClose = NULL; MmSectionObjectType->Create = MmpCreateSection; MmSectionObjectType->DuplicationNotify = NULL; return(STATUS_SUCCESS); } NTSTATUS MmCreatePageFileSection(PHANDLE SectionHandle, 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; PSECTION_OBJECT Section; PMM_SECTION_SEGMENT Segment; NTSTATUS Status; if (UMaximumSize == NULL) { return(STATUS_UNSUCCESSFUL); } MaximumSize = *UMaximumSize; /* * Check the protection */ if ((SectionPageProtection & PAGE_FLAGS_VALID_FROM_USER_MODE) != SectionPageProtection) { return(STATUS_INVALID_PAGE_PROTECTION); } /* * Create the section */ Status = ObCreateObject(SectionHandle, DesiredAccess, ObjectAttributes, MmSectionObjectType, (PVOID*)&Section); if (!NT_SUCCESS(Status)) { return(Status); } /* * Initialize it */ Section->SectionPageProtection = SectionPageProtection; Section->AllocationAttributes = AllocationAttributes; InitializeListHead(&Section->ViewListHead); KeInitializeSpinLock(&Section->ViewListLock); KeInitializeMutex(&Section->Lock, 0); Section->FileObject = NULL; Section->MaximumSize = MaximumSize; Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT), TAG_MM_SECTION_SEGMENT); if (Segment == NULL) { ZwClose(*SectionHandle); ObDereferenceObject(Section); return(STATUS_NO_MEMORY); } Section->Segments = Segment; Segment->ReferenceCount = 1; KeInitializeMutex(&Segment->Lock, 0); Segment->FileOffset = 0; Segment->Protection = SectionPageProtection; Segment->Attributes = AllocationAttributes; Segment->Length = MaximumSize.u.LowPart; Segment->Flags = MM_PAGEFILE_SEGMENT; Segment->WriteCopy = FALSE; return(STATUS_SUCCESS); } NTSTATUS MmCreateDataFileSection(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PLARGE_INTEGER UMaximumSize, ULONG SectionPageProtection, ULONG AllocationAttributes, HANDLE FileHandle) /* * Create a section backed by a data file */ { PSECTION_OBJECT Section; NTSTATUS Status; LARGE_INTEGER MaximumSize; PFILE_OBJECT FileObject; PMM_SECTION_SEGMENT Segment; ULONG FileAccess; /* * Check the protection */ if ((SectionPageProtection & PAGE_FLAGS_VALID_FROM_USER_MODE) != SectionPageProtection) { return(STATUS_INVALID_PAGE_PROTECTION); } /* * Create the section */ Status = ObCreateObject(SectionHandle, DesiredAccess, ObjectAttributes, MmSectionObjectType, (PVOID*)&Section); if (!NT_SUCCESS(Status)) { return(Status); } /* * Initialize it */ Section->SectionPageProtection = SectionPageProtection; Section->AllocationAttributes = AllocationAttributes; InitializeListHead(&Section->ViewListHead); KeInitializeSpinLock(&Section->ViewListLock); KeInitializeMutex(&Section->Lock, 0); Section->NrSegments = 1; Section->ImageBase = NULL; Section->EntryPoint = NULL; Section->StackReserve = 0; Section->StackCommit = 0; Section->Subsystem = 0; Section->MinorSubsystemVersion = 0; Section->MajorSubsystemVersion = 0; Section->ImageCharacteristics = 0; Section->Machine = 0; Section->Executable = FALSE; /* * 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, UserMode, (PVOID*)&FileObject, NULL); if (!NT_SUCCESS(Status)) { ZwClose(*SectionHandle); ObDereferenceObject(Section); return(Status); } /* * We can't do memory mappings if the file system doesn't support the * standard FCB */ if (!(FileObject->Flags & FO_FCB_IS_VALID)) { ZwClose(*SectionHandle); ObDereferenceObject(Section); ObDereferenceObject(FileObject); return(STATUS_INVALID_FILE_FOR_SECTION); } /* * FIXME: Revise this once a locking order for file size changes is * decided */ if (UMaximumSize != NULL) { MaximumSize = *UMaximumSize; } else { MaximumSize = ((PREACTOS_COMMON_FCB_HEADER)FileObject->FsContext)->FileSize; } if (MaximumSize.QuadPart > ((PREACTOS_COMMON_FCB_HEADER)FileObject->FsContext)->FileSize.QuadPart) { IO_STATUS_BLOCK Iosb; Status = NtSetInformationFile(FileHandle, &Iosb, &MaximumSize, sizeof(LARGE_INTEGER), FileAllocationInformation); if (!NT_SUCCESS(Status)) { ZwClose(*SectionHandle); ObDereferenceObject(Section); ObDereferenceObject(FileObject); return(STATUS_SECTION_NOT_EXTENDED); } } /* * Lock the file */ Status = KeWaitForSingleObject((PVOID)&FileObject->Lock, 0, KernelMode, FALSE, NULL); if (Status != STATUS_SUCCESS) { ZwClose(*SectionHandle); 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->SectionObjectPointers->DataSectionObject == NULL) { Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT), TAG_MM_SECTION_SEGMENT); if (Segment == NULL) { KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE); ZwClose(*SectionHandle); ObDereferenceObject(Section); ObDereferenceObject(FileObject); return(STATUS_NO_MEMORY); } Section->Segments = Segment; Segment->ReferenceCount = 1; KeInitializeMutex(&Segment->Lock, 0); /* * Set the lock before assigning the segment to the file object */ Status = KeWaitForSingleObject((PVOID)&Segment->Lock, 0, KernelMode, FALSE, NULL); if (Status != STATUS_SUCCESS) { KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE); ExFreePool(Segment); ZwClose(*SectionHandle); ObDereferenceObject(Section); ObDereferenceObject(FileObject); return(Status); } FileObject->SectionObjectPointers->DataSectionObject = (PVOID)Segment; Segment->FileOffset = 0; Segment->Protection = 0; Segment->Attributes = 0; Segment->Flags = MM_DATAFILE_SEGMENT; Segment->Characteristics = 0; Segment->WriteCopy = FALSE; if (AllocationAttributes & SEC_RESERVE) { Segment->Length = 0; } else { Segment->Length = MaximumSize.u.LowPart; } Segment->VirtualAddress = NULL; } else { /* * If the file is already mapped as a data file then we may need * to extend it */ Segment = (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointers-> DataSectionObject; Section->Segments = Segment; InterlockedIncrement((PLONG)&Segment->ReferenceCount); Status = KeWaitForSingleObject((PVOID)&Segment->Lock, 0, KernelMode, FALSE, NULL); if (Status != STATUS_SUCCESS) { InterlockedDecrement((PLONG)&Segment->ReferenceCount); KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE); ZwClose(*SectionHandle); ObDereferenceObject(Section); ObDereferenceObject(FileObject); return(Status); } if (MaximumSize.u.LowPart > Segment->Length && !(AllocationAttributes & SEC_RESERVE)) { Segment->Length = MaximumSize.u.LowPart; } } KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE); CcRosReferenceCache(FileObject); Section->FileObject = FileObject; Section->MaximumSize = MaximumSize; KeReleaseMutex(&Segment->Lock, FALSE); ObDereferenceObject(Section); return(STATUS_SUCCESS); } static ULONG SectionCharacteristicsToProtect[16] = { PAGE_NOACCESS, // 0 = NONE PAGE_NOACCESS, // 1 = SHARED PAGE_EXECUTE, // 2 = EXECUTABLE PAGE_EXECUTE, // 3 = EXECUTABLE, SHARED PAGE_READONLY, // 4 = READABLE PAGE_READONLY, // 5 = READABLE, SHARED PAGE_EXECUTE_READ, // 6 = READABLE, EXECUTABLE PAGE_EXECUTE_READ, // 7 = READABLE, EXECUTABLE, SHARED PAGE_READWRITE, // 8 = WRITABLE PAGE_READWRITE, // 9 = WRITABLE, SHARED PAGE_EXECUTE_READWRITE, // 10 = WRITABLE, EXECUTABLE PAGE_EXECUTE_READWRITE, // 11 = WRITABLE, EXECUTABLE, SHARED PAGE_READWRITE, // 12 = WRITABLE, READABLE PAGE_READWRITE, // 13 = WRITABLE, READABLE, SHARED PAGE_EXECUTE_READWRITE, // 14 = WRITABLE, READABLE, EXECUTABLE, PAGE_EXECUTE_READWRITE, // 15 = WRITABLE, READABLE, EXECUTABLE, SHARED }; NTSTATUS MmCreateImageSection(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PLARGE_INTEGER UMaximumSize, ULONG SectionPageProtection, ULONG AllocationAttributes, HANDLE FileHandle) { PSECTION_OBJECT Section; NTSTATUS Status; PFILE_OBJECT FileObject; ULONG FileAccess; IMAGE_DOS_HEADER DosHeader; IO_STATUS_BLOCK Iosb; LARGE_INTEGER Offset; IMAGE_NT_HEADERS PEHeader; PIMAGE_SECTION_HEADER ImageSections; PMM_SECTION_SEGMENT SectionSegments; ULONG NrSegments; PMM_IMAGE_SECTION_OBJECT ImageSectionObject; /* * Check the protection */ if ((SectionPageProtection & PAGE_FLAGS_VALID_FROM_USER_MODE) != SectionPageProtection) { return(STATUS_INVALID_PAGE_PROTECTION); } /* * Specifying a maximum size is meaningless for an image section */ if (UMaximumSize != NULL) { return(STATUS_INVALID_PARAMETER_4); } /* * Read the dos header */ Offset.QuadPart = 0; Status = ZwReadFile(FileHandle, NULL, NULL, NULL, &Iosb, &DosHeader, sizeof(DosHeader), &Offset, 0); if (!NT_SUCCESS(Status)) { return(Status); } if (Iosb.Information != sizeof(DosHeader)) { return(STATUS_INVALID_IMAGE_FORMAT); } /* * Check the DOS signature */ if (DosHeader.e_magic != IMAGE_DOS_SIGNATURE) { return(STATUS_INVALID_IMAGE_FORMAT); } /* * Read the PE header */ Offset.QuadPart = DosHeader.e_lfanew; Status = ZwReadFile(FileHandle, NULL, NULL, NULL, &Iosb, &PEHeader, sizeof(PEHeader), &Offset, 0); if (!NT_SUCCESS(Status)) { return(Status); } if (Iosb.Information != sizeof(PEHeader)) { return(STATUS_INVALID_IMAGE_FORMAT); } /* * Check the signature */ if (PEHeader.Signature != IMAGE_NT_SIGNATURE) { return(STATUS_INVALID_IMAGE_FORMAT); } /* * Read in the section headers */ Offset.QuadPart = DosHeader.e_lfanew + sizeof(PEHeader); ImageSections = ExAllocatePool(NonPagedPool, PEHeader.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER)); Status = ZwReadFile(FileHandle, NULL, NULL, NULL, &Iosb, ImageSections, PEHeader.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER), &Offset, 0); if (!NT_SUCCESS(Status)) { ExFreePool(ImageSections); return(Status); } if (Iosb.Information != (PEHeader.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER))) { ExFreePool(ImageSections); return(STATUS_INVALID_IMAGE_FORMAT); } /* * Create the section */ Status = ObCreateObject(SectionHandle, DesiredAccess, ObjectAttributes, MmSectionObjectType, (PVOID*)&Section); if (!NT_SUCCESS(Status)) { ExFreePool(ImageSections); return(Status); } /* * Initialize it */ Section->SectionPageProtection = SectionPageProtection; Section->AllocationAttributes = AllocationAttributes; InitializeListHead(&Section->ViewListHead); KeInitializeSpinLock(&Section->ViewListLock); KeInitializeMutex(&Section->Lock, 0); Section->NrSegments = PEHeader.FileHeader.NumberOfSections + 1; Section->ImageBase = (PVOID)PEHeader.OptionalHeader.ImageBase; Section->EntryPoint = (PVOID)PEHeader.OptionalHeader.AddressOfEntryPoint; Section->StackReserve = PEHeader.OptionalHeader.SizeOfStackReserve; Section->StackCommit = PEHeader.OptionalHeader.SizeOfStackCommit; Section->Subsystem = PEHeader.OptionalHeader.Subsystem; Section->MinorSubsystemVersion = PEHeader.OptionalHeader.MinorSubsystemVersion; Section->MajorSubsystemVersion = PEHeader.OptionalHeader.MajorSubsystemVersion; Section->ImageCharacteristics = PEHeader.FileHeader.Characteristics; Section->Machine = PEHeader.FileHeader.Machine; Section->Executable = (PEHeader.OptionalHeader.SizeOfCode != 0); /* * 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, UserMode, (PVOID*)&FileObject, NULL); if (!NT_SUCCESS(Status)) { ZwClose(*SectionHandle); ObDereferenceObject(Section); ExFreePool(ImageSections); return(Status); } /* * We can't do memory mappings if the file system doesn't support the * standard FCB */ if (!(FileObject->Flags & FO_FCB_IS_VALID)) { ZwClose(*SectionHandle); ObDereferenceObject(Section); ObDereferenceObject(FileObject); ExFreePool(ImageSections); return(STATUS_INVALID_FILE_FOR_SECTION); } /* * Lock the file */ Status = KeWaitForSingleObject((PVOID)&FileObject->Lock, 0, KernelMode, FALSE, NULL); if (Status != STATUS_SUCCESS) { ZwClose(*SectionHandle); ObDereferenceObject(Section); ObDereferenceObject(FileObject); ExFreePool(ImageSections); return(Status); } /* * If this file hasn't been mapped as a image file before then allocate the * section segments to describe the mapping */ NrSegments = PEHeader.FileHeader.NumberOfSections + 1; if (FileObject->SectionObjectPointers->ImageSectionObject == NULL) { ULONG i; ULONG Size; ULONG Characteristics; Size = sizeof(MM_IMAGE_SECTION_OBJECT) + (sizeof(MM_SECTION_SEGMENT) * NrSegments); ImageSectionObject = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_MM_SECTION_SEGMENT); if (ImageSectionObject == NULL) { KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE); ZwClose(*SectionHandle); ObDereferenceObject(Section); ObDereferenceObject(FileObject); ExFreePool(ImageSections); return(STATUS_NO_MEMORY); } ImageSectionObject->NrSegments = NrSegments; SectionSegments = ImageSectionObject->Segments; Section->Segments = SectionSegments; SectionSegments[0].FileOffset = 0; SectionSegments[0].Characteristics = IMAGE_SECTION_CHAR_DATA; SectionSegments[0].Protection = PAGE_READWRITE; SectionSegments[0].RawLength = PAGE_SIZE; SectionSegments[0].Length = PAGE_SIZE; SectionSegments[0].Flags = 0; SectionSegments[0].ReferenceCount = 1; SectionSegments[0].VirtualAddress = 0; SectionSegments[0].WriteCopy = TRUE; KeInitializeMutex(&SectionSegments[0].Lock, 0); for (i = 1; i < NrSegments; i++) { SectionSegments[i].FileOffset = ImageSections[i-1].PointerToRawData; SectionSegments[i].Characteristics = ImageSections[i-1].Characteristics; /* * Set up the protection and write copy variables. */ Characteristics = ImageSections[i - 1].Characteristics; if ((Characteristics & IMAGE_SECTION_CHAR_READABLE) || (Characteristics & IMAGE_SECTION_CHAR_WRITABLE) || (Characteristics & IMAGE_SECTION_CHAR_EXECUTABLE)) { SectionSegments[i].Protection = SectionCharacteristicsToProtect[Characteristics >> 28]; SectionSegments[i].WriteCopy = !(Characteristics & IMAGE_SECTION_CHAR_SHARED); } else if (Characteristics & IMAGE_SECTION_CHAR_CODE) { SectionSegments[i].Protection = PAGE_EXECUTE_READ; SectionSegments[i].WriteCopy = TRUE; } else if (Characteristics & IMAGE_SECTION_CHAR_DATA) { SectionSegments[i].Protection = PAGE_READWRITE; SectionSegments[i].WriteCopy = TRUE; } else if (Characteristics & IMAGE_SECTION_CHAR_BSS) { SectionSegments[i].Protection = PAGE_READWRITE; SectionSegments[i].WriteCopy = TRUE; } else { SectionSegments[i].Protection = PAGE_NOACCESS; SectionSegments[i].WriteCopy = TRUE; } /* * Set up the attributes. */ if (Characteristics & IMAGE_SECTION_CHAR_CODE) { SectionSegments[i].Attributes = 0; } else if (Characteristics & IMAGE_SECTION_CHAR_DATA) { SectionSegments[i].Attributes = 0; } else if (Characteristics & IMAGE_SECTION_CHAR_BSS) { SectionSegments[i].Attributes = MM_SECTION_SEGMENT_BSS; } else { SectionSegments[i].Attributes = 0; } SectionSegments[i].RawLength = ImageSections[i-1].SizeOfRawData; SectionSegments[i].Length = ImageSections[i-1].Misc.VirtualSize; SectionSegments[i].Flags = 0; SectionSegments[i].ReferenceCount = 1; SectionSegments[i].VirtualAddress = (PVOID)ImageSections[i-1].VirtualAddress; KeInitializeMutex(&SectionSegments[i].Lock, 0); } FileObject->SectionObjectPointers->ImageSectionObject = (PVOID)ImageSectionObject; } else { ULONG i; ImageSectionObject = (PMM_IMAGE_SECTION_OBJECT) FileObject->SectionObjectPointers->ImageSectionObject; SectionSegments = ImageSectionObject->Segments; Section->Segments = SectionSegments; /* * Otherwise just reference all the section segments */ for (i = 0; i < NrSegments; i++) { InterlockedIncrement(&SectionSegments[i].ReferenceCount); } } ExFreePool(ImageSections); KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE); CcRosReferenceCache(FileObject); Section->FileObject = FileObject; ObDereferenceObject(Section); return(STATUS_SUCCESS); } NTSTATUS STDCALL 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) { if (AllocationAttributes & SEC_IMAGE) { return(MmCreateImageSection(SectionHandle, DesiredAccess, ObjectAttributes, MaximumSize, SectionPageProtection, AllocationAttributes, FileHandle)); } else if (FileHandle != NULL) { return(MmCreateDataFileSection(SectionHandle, DesiredAccess, ObjectAttributes, MaximumSize, SectionPageProtection, AllocationAttributes, FileHandle)); } else { return(MmCreatePageFileSection(SectionHandle, DesiredAccess, ObjectAttributes, MaximumSize, SectionPageProtection, AllocationAttributes)); } } /********************************************************************** * NAME * NtOpenSection * * DESCRIPTION * * ARGUMENTS * SectionHandle * * DesiredAccess * * ObjectAttributes * * RETURN VALUE * * REVISIONS * */ NTSTATUS STDCALL NtOpenSection(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes) { NTSTATUS Status; *SectionHandle = 0; Status = ObOpenObjectByName(ObjectAttributes, MmSectionObjectType, NULL, UserMode, DesiredAccess, NULL, SectionHandle); return(Status); } NTSTATUS STATIC MmMapViewOfSegment(PEPROCESS Process, PMADDRESS_SPACE AddressSpace, PSECTION_OBJECT Section, PMM_SECTION_SEGMENT Segment, PVOID* BaseAddress, ULONG ViewSize, ULONG Protect, ULONG ViewOffset) { PMEMORY_AREA MArea; NTSTATUS Status; KIRQL oldIrql; Status = MmCreateMemoryArea(Process, &Process->AddressSpace, MEMORY_AREA_SECTION_VIEW, BaseAddress, ViewSize, Protect, &MArea, FALSE); if (!NT_SUCCESS(Status)) { DPRINT1("Mapping between 0x%.8X and 0x%.8X failed.\n", (*BaseAddress), (*BaseAddress) + ViewSize); return(Status); } KeAcquireSpinLock(&Section->ViewListLock, &oldIrql); InsertTailList(&Section->ViewListHead, &MArea->Data.SectionData.ViewListEntry); KeReleaseSpinLock(&Section->ViewListLock, oldIrql); ObReferenceObjectByPointer((PVOID)Section, SECTION_MAP_READ, NULL, ExGetPreviousMode()); MArea->Data.SectionData.Segment = Segment; MArea->Data.SectionData.Section = Section; MArea->Data.SectionData.ViewOffset = ViewOffset; MArea->Data.SectionData.WriteCopyView = FALSE; MmInitialiseRegion(&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. */ NTSTATUS STDCALL NtMapViewOfSection(HANDLE SectionHandle, HANDLE ProcessHandle, PVOID* BaseAddress, ULONG ZeroBits, ULONG CommitSize, PLARGE_INTEGER SectionOffset, PULONG ViewSize, SECTION_INHERIT InheritDisposition, ULONG AllocationType, ULONG Protect) { PSECTION_OBJECT Section; PEPROCESS Process; NTSTATUS Status; PMADDRESS_SPACE AddressSpace; Status = ObReferenceObjectByHandle(ProcessHandle, PROCESS_VM_OPERATION, PsProcessType, UserMode, (PVOID*)&Process, NULL); if (!NT_SUCCESS(Status)) { return(Status); } AddressSpace = &Process->AddressSpace; Status = ObReferenceObjectByHandle(SectionHandle, SECTION_MAP_READ, MmSectionObjectType, UserMode, (PVOID*)&Section, NULL); if (!(NT_SUCCESS(Status))) { DPRINT("ObReference failed rc=%x\n",Status); ObDereferenceObject(Process); return(Status); } Status = MmMapViewOfSection(Section, Process, BaseAddress, ZeroBits, CommitSize, SectionOffset, ViewSize, InheritDisposition, AllocationType, Protect); ObDereferenceObject(Section); ObDereferenceObject(Process); return(Status); } VOID STATIC MmFreeSectionPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address, PHYSICAL_ADDRESS PhysAddr, SWAPENTRY SwapEntry, BOOLEAN Dirty) { PMEMORY_AREA MArea; ULONG Entry; PFILE_OBJECT FileObject; PREACTOS_COMMON_FCB_HEADER Fcb; ULONG Offset; SWAPENTRY SavedSwapEntry; PMM_PAGEOP PageOp; MArea = (PMEMORY_AREA)Context; Offset = ((ULONG)PAGE_ROUND_DOWN(Address) - (ULONG)MArea->BaseAddress) + MArea->Data.SectionData.ViewOffset; Entry = MmGetPageEntrySectionSegment(MArea->Data.SectionData.Segment, Offset); PageOp = MmCheckForPageOp(MArea, 0, NULL, MArea->Data.SectionData.Segment, Offset); assert(PageOp == NULL); /* * For a dirty, datafile, non-private page mark it as dirty in the * cache manager. */ if (MArea->Data.SectionData.Segment->Flags & MM_DATAFILE_SEGMENT) { if (PhysAddr.QuadPart == PAGE_FROM_SSE(Entry) && Dirty) { FileObject = MemoryArea->Data.SectionData.Section->FileObject; Fcb = (PREACTOS_COMMON_FCB_HEADER)FileObject->FsContext; CcRosMarkDirtyCacheSegment(Fcb->Bcb, Offset); assert(SwapEntry == 0); } } if (SwapEntry != 0) { MmFreeSwapPage(SwapEntry); } else if (PhysAddr.QuadPart != 0) { if (IS_SWAP_FROM_SSE(Entry) || PhysAddr.QuadPart != (PAGE_FROM_SSE(Entry))) { /* * Just dereference private pages */ SavedSwapEntry = MmGetSavedSwapEntryPage(PhysAddr); if (SavedSwapEntry != 0) { MmFreeSwapPage(SavedSwapEntry); MmSetSavedSwapEntryPage(PhysAddr, 0); } MmDeleteRmap(PhysAddr, MArea->Process, Address); MmReleasePageMemoryConsumer(MC_USER, PhysAddr); } else { MmUnsharePageEntrySectionSegment(MArea->Data.SectionData.Section, MArea->Data.SectionData.Segment, Offset, Dirty); MmDeleteRmap(PhysAddr, MArea->Process, Address); MmReleasePageMemoryConsumer(MC_USER, PhysAddr); } } } NTSTATUS STDCALL MmUnmapViewOfSection(PEPROCESS Process, PVOID BaseAddress) { NTSTATUS Status; PMEMORY_AREA MemoryArea; PMADDRESS_SPACE AddressSpace; PSECTION_OBJECT Section; PMM_SECTION_SEGMENT Segment; KIRQL oldIrql; PLIST_ENTRY CurrentEntry; PMM_REGION CurrentRegion; AddressSpace = &Process->AddressSpace; DPRINT("Opening memory area Process %x BaseAddress %x\n", Process, BaseAddress); MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace, BaseAddress); if (MemoryArea == NULL) { return(STATUS_UNSUCCESSFUL); } MemoryArea->DeleteInProgress = TRUE; MmLockSection(MemoryArea->Data.SectionData.Section); MmLockSectionSegment(MemoryArea->Data.SectionData.Segment); Section = MemoryArea->Data.SectionData.Section; Segment = MemoryArea->Data.SectionData.Segment; KeAcquireSpinLock(&Section->ViewListLock, &oldIrql); RemoveEntryList(&MemoryArea->Data.SectionData.ViewListEntry); KeReleaseSpinLock(&Section->ViewListLock, oldIrql); CurrentEntry = MemoryArea->Data.SectionData.RegionListHead.Flink; while (CurrentEntry != &MemoryArea->Data.SectionData.RegionListHead) { CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION, RegionListEntry); CurrentEntry = CurrentEntry->Flink; ExFreePool(CurrentRegion); } if (MemoryArea->Data.SectionData.Section->AllocationAttributes & SEC_PHYSICALMEMORY) { Status = MmFreeMemoryArea(&Process->AddressSpace, BaseAddress, 0, NULL, NULL); } else { Status = MmFreeMemoryArea(&Process->AddressSpace, BaseAddress, 0, MmFreeSectionPage, MemoryArea); } MmUnlockSection(Section); MmUnlockSectionSegment(Segment); ObDereferenceObject(Section); return(STATUS_SUCCESS); } /********************************************************************** * NAME EXPORTED * NtUnmapViewOfSection * * DESCRIPTION * * ARGUMENTS * ProcessHandle * * BaseAddress * * RETURN VALUE * Status. * * REVISIONS * */ NTSTATUS STDCALL NtUnmapViewOfSection (HANDLE ProcessHandle, PVOID BaseAddress) { PEPROCESS Process; NTSTATUS Status; DPRINT("NtUnmapViewOfSection(ProcessHandle %x, BaseAddress %x)\n", ProcessHandle, BaseAddress); DPRINT("Referencing process\n"); Status = ObReferenceObjectByHandle(ProcessHandle, PROCESS_VM_OPERATION, PsProcessType, UserMode, (PVOID*)&Process, NULL); if (!NT_SUCCESS(Status)) { DPRINT("ObReferenceObjectByHandle failed (Status %x)\n", Status); return(Status); } MmLockAddressSpace(&Process->AddressSpace); Status = MmUnmapViewOfSection(Process, BaseAddress); MmUnlockAddressSpace(&Process->AddressSpace); ObDereferenceObject(Process); return Status; } NTSTATUS STDCALL NtQuerySection (IN HANDLE SectionHandle, IN CINT SectionInformationClass, OUT PVOID SectionInformation, IN ULONG Length, OUT PULONG ResultLength) /* * FUNCTION: Queries the information of a section object. * ARGUMENTS: * SectionHandle = Handle to the section link object * SectionInformationClass = Index to a certain information structure * SectionInformation (OUT)= Caller supplies storage for resulting * information * Length = Size of the supplied storage * ResultLength = Data written * RETURNS: Status * */ { PSECTION_OBJECT Section; NTSTATUS Status; Status = ObReferenceObjectByHandle(SectionHandle, SECTION_MAP_READ, MmSectionObjectType, UserMode, (PVOID*)&Section, NULL); if (!(NT_SUCCESS(Status))) { return(Status); } switch (SectionInformationClass) { case SectionBasicInformation: { PSECTION_BASIC_INFORMATION Sbi; if (Length != sizeof(SECTION_BASIC_INFORMATION)) { ObDereferenceObject(Section); return(STATUS_INFO_LENGTH_MISMATCH); } Sbi = (PSECTION_BASIC_INFORMATION)SectionInformation; Sbi->BaseAddress = 0; Sbi->Attributes = 0; Sbi->Size.QuadPart = 0; *ResultLength = sizeof(SECTION_BASIC_INFORMATION); Status = STATUS_SUCCESS; break; } case SectionImageInformation: { PSECTION_IMAGE_INFORMATION Sii; if (Length != sizeof(SECTION_IMAGE_INFORMATION)) { ObDereferenceObject(Section); return(STATUS_INFO_LENGTH_MISMATCH); } Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation; Sii->EntryPoint = Section->EntryPoint; Sii->Unknown1 = 0; Sii->StackReserve = Section->StackReserve; Sii->StackCommit = Section->StackCommit; Sii->Subsystem = Section->Subsystem; Sii->MinorSubsystemVersion = Section->MinorSubsystemVersion; Sii->MajorSubsystemVersion = Section->MajorSubsystemVersion; Sii->Unknown2 = 0; Sii->Characteristics = Section->ImageCharacteristics; Sii->ImageNumber = Section->Machine; Sii->Executable = Section->Executable; Sii->Unknown3 = 0; Sii->Unknown4[0] = 0; Sii->Unknown4[1] = 0; Sii->Unknown4[2] = 0; *ResultLength = sizeof(SECTION_IMAGE_INFORMATION); Status = STATUS_SUCCESS; break; } default: *ResultLength = 0; Status = STATUS_INVALID_INFO_CLASS; } ObDereferenceObject(Section); return(Status); } NTSTATUS STDCALL NtExtendSection(IN HANDLE SectionHandle, IN ULONG NewMaximumSize) { UNIMPLEMENTED; } /********************************************************************** * NAME INTERNAL * MmAllocateSection@4 * * DESCRIPTION * * ARGUMENTS * Length * * RETURN VALUE * * NOTE * Code taken from ntoskrnl/mm/special.c. * * REVISIONS * */ PVOID STDCALL MmAllocateSection (IN ULONG Length) { PVOID Result; MEMORY_AREA* marea; NTSTATUS Status; ULONG i; PMADDRESS_SPACE AddressSpace; DPRINT("MmAllocateSection(Length %x)\n",Length); AddressSpace = MmGetKernelAddressSpace(); Result = NULL; MmLockAddressSpace(AddressSpace); Status = MmCreateMemoryArea (NULL, AddressSpace, MEMORY_AREA_SYSTEM, &Result, Length, 0, &marea, FALSE); if (!NT_SUCCESS(Status)) { MmUnlockAddressSpace(AddressSpace); return (NULL); } MmUnlockAddressSpace(AddressSpace); DPRINT("Result %p\n",Result); for (i = 0; (i <= (Length / PAGE_SIZE)); i++) { PHYSICAL_ADDRESS Page; Status = MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &Page); if (!NT_SUCCESS(Status)) { DbgPrint("Unable to allocate page\n"); KeBugCheck(0); } Status = MmCreateVirtualMapping (NULL, (Result + (i * PAGE_SIZE)), PAGE_READWRITE, Page, TRUE); if (!NT_SUCCESS(Status)) { DbgPrint("Unable to create virtual mapping\n"); KeBugCheck(0); } } return ((PVOID)Result); } /********************************************************************** * 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. */ NTSTATUS STDCALL MmMapViewOfSection(IN PVOID SectionObject, IN PEPROCESS Process, IN OUT PVOID *BaseAddress, IN ULONG ZeroBits, IN ULONG CommitSize, IN OUT PLARGE_INTEGER SectionOffset OPTIONAL, IN OUT PULONG ViewSize, IN SECTION_INHERIT InheritDisposition, IN ULONG AllocationType, IN ULONG Protect) { PSECTION_OBJECT Section; PMADDRESS_SPACE AddressSpace; ULONG ViewOffset; NTSTATUS Status = STATUS_SUCCESS; Section = (PSECTION_OBJECT)SectionObject; AddressSpace = &Process->AddressSpace; MmLockAddressSpace(AddressSpace); MmLockSection(SectionObject); if (Section->AllocationAttributes & SEC_IMAGE) { ULONG i; PVOID ImageBase; ULONG ImageSize; ImageBase = *BaseAddress; if (ImageBase == NULL) { ImageBase = Section->ImageBase; } ImageSize = 0; for (i = 0; i < Section->NrSegments; i++) { if (!(Section->Segments[i].Characteristics & IMAGE_SECTION_NOLOAD)) { ULONG MaxExtent; MaxExtent = (ULONG)(Section->Segments[i].VirtualAddress + Section->Segments[i].Length); ImageSize = max(ImageSize, MaxExtent); } } /* Check there is enough space to map the section at that point. */ if (MmOpenMemoryAreaByRegion(AddressSpace, ImageBase, ImageSize) != NULL) { /* Fail if the user requested a fixed base address. */ if ((*BaseAddress) != NULL) { MmUnlockSection(Section); MmUnlockAddressSpace(AddressSpace); return(Status); } /* Otherwise find a gap to map the image. */ ImageBase = MmFindGap(AddressSpace, ImageSize); if (ImageBase == NULL) { MmUnlockSection(Section); MmUnlockAddressSpace(AddressSpace); return(Status); } } for (i = 0; i < Section->NrSegments; i++) { PVOID SBaseAddress; if (!(Section->Segments[i].Characteristics & IMAGE_SECTION_NOLOAD)) { SBaseAddress = (PVOID) (ImageBase + (ULONG_PTR)Section->Segments[i].VirtualAddress); MmLockSectionSegment(&Section->Segments[i]); Status = MmMapViewOfSegment(Process, &Process->AddressSpace, Section, &Section->Segments[i], &SBaseAddress, Section->Segments[i].Length, Section->Segments[i].Protection, Section->Segments[i].FileOffset); MmUnlockSectionSegment(&Section->Segments[i]); if (!NT_SUCCESS(Status)) { MmUnlockSection(Section); MmUnlockAddressSpace(AddressSpace); return(Status); } } } *BaseAddress = ImageBase; } else { if (SectionOffset == NULL) { ViewOffset = 0; } else { ViewOffset = SectionOffset->u.LowPart; } if ((ViewOffset % PAGE_SIZE) != 0) { MmUnlockSection(Section); 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; } MmLockSectionSegment(Section->Segments); Status = MmMapViewOfSegment(Process, &Process->AddressSpace, Section, Section->Segments, BaseAddress, *ViewSize, Protect, ViewOffset); MmUnlockSectionSegment(Section->Segments); if (!NT_SUCCESS(Status)) { MmUnlockSection(Section); MmUnlockAddressSpace(AddressSpace); return(Status); } } MmUnlockSection(Section); MmUnlockAddressSpace(AddressSpace); return(STATUS_SUCCESS); } BOOLEAN STDCALL MmCanFileBeTruncated (IN PSECTION_OBJECT_POINTERS SectionObjectPointer, IN PLARGE_INTEGER NewFileSize) { UNIMPLEMENTED; return (FALSE); } BOOLEAN STDCALL MmDisableModifiedWriteOfSection (DWORD Unknown0) { UNIMPLEMENTED; return (FALSE); } BOOLEAN STDCALL MmFlushImageSection (IN PSECTION_OBJECT_POINTERS SectionObjectPointer, IN MMFLUSH_TYPE FlushType) { UNIMPLEMENTED; return (FALSE); } BOOLEAN STDCALL MmForceSectionClosed (DWORD Unknown0, DWORD Unknown1) { UNIMPLEMENTED; return (FALSE); } NTSTATUS STDCALL MmMapViewInSystemSpace (IN PVOID Section, OUT PVOID * MappedBase, IN PULONG ViewSize) { UNIMPLEMENTED; return (STATUS_NOT_IMPLEMENTED); } NTSTATUS STDCALL MmUnmapViewInSystemSpace (DWORD Unknown0) { UNIMPLEMENTED; return (STATUS_NOT_IMPLEMENTED); } NTSTATUS STDCALL MmSetBankedSection (DWORD Unknown0, DWORD Unknown1, DWORD Unknown2, DWORD Unknown3, DWORD Unknown4, DWORD Unknown5) { UNIMPLEMENTED; return (STATUS_NOT_IMPLEMENTED); } /********************************************************************** * NAME EXPORTED * MmCreateSection@ * * DESCRIPTION * Creates a section object. * * ARGUMENTS * SectionObjiect (OUT) * Caller supplied storage for the resulting pointer * to a SECTION_BOJECT 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. */ NTSTATUS STDCALL MmCreateSection (OUT PSECTION_OBJECT * SectionObject, 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) { return (STATUS_NOT_IMPLEMENTED); } /* EOF */