/* * 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 #include #include #define NDEBUG #include #include #if defined (ALLOC_PRAGMA) #pragma alloc_text(INIT, MmCreatePhysicalMemorySection) #pragma alloc_text(INIT, MmInitSectionImplementation) #endif #include "ARM3/miarm.h" #undef MmSetPageEntrySectionSegment #define MmSetPageEntrySectionSegment(S,O,E) do { \ DPRINT("SetPageEntrySectionSegment(old,%p,%x,%x)\n",(S),(O)->LowPart,E); \ _MmSetPageEntrySectionSegment((S),(O),(E),__FILE__,__LINE__); \ } while (0) extern MMSESSION MmSession; NTSTATUS NTAPI MiMapViewInSystemSpace(IN PVOID Section, IN PVOID Session, OUT PVOID *MappedBase, IN OUT PSIZE_T ViewSize); NTSTATUS NTAPI MmCreateArm3Section(OUT PVOID *SectionObject, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN PLARGE_INTEGER InputMaximumSize, IN ULONG SectionPageProtection, IN ULONG AllocationAttributes, IN HANDLE FileHandle OPTIONAL, IN PFILE_OBJECT FileObject OPTIONAL); NTSTATUS NTAPI MmMapViewOfArm3Section(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); // // PeFmtCreateSection depends on the following: // C_ASSERT(EXEFMT_LOAD_HEADER_SIZE >= sizeof(IMAGE_DOS_HEADER)); C_ASSERT(sizeof(IMAGE_NT_HEADERS32) <= sizeof(IMAGE_NT_HEADERS64)); C_ASSERT(TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == TYPE_ALIGNMENT(IMAGE_NT_HEADERS64)); C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader) == RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS64, FileHeader)); C_ASSERT(FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader) == FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader)); C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, Magic)); C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SectionAlignment)); C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, FileAlignment)); C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, Subsystem)); C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, MinorSubsystemVersion)); C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, MajorSubsystemVersion)); C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, AddressOfEntryPoint)); C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SizeOfCode)); C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SizeOfHeaders)); /* TYPES *********************************************************************/ typedef struct { PROS_SECTION_OBJECT Section; PMM_SECTION_SEGMENT Segment; LARGE_INTEGER Offset; BOOLEAN WasDirty; BOOLEAN Private; PEPROCESS CallingProcess; ULONG_PTR SectionEntry; } MM_SECTION_PAGEOUT_CONTEXT; /* GLOBALS *******************************************************************/ POBJECT_TYPE MmSectionObjectType = NULL; ULONG_PTR MmSubsectionBase; 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 */ /* * FIXME? do we really need the WriteCopy field in segments? can't we use * PAGE_WRITECOPY here? */ 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 */ }; extern ULONG MmMakeFileAccess []; ACCESS_MASK NTAPI MiArm3GetCorrectFileAccessMask(IN ACCESS_MASK SectionPageProtection); 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 }; /* FUNCTIONS *****************************************************************/ /* References: [1] Microsoft Corporation, "Microsoft Portable Executable and Common Object File Format Specification", revision 6.0 (February 1999) */ 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) { NTSTATUS nStatus; ULONG cbFileHeaderOffsetSize = 0; ULONG cbSectionHeadersOffset = 0; ULONG cbSectionHeadersSize; ULONG cbSectionHeadersOffsetSize = 0; ULONG cbOptHeaderSize; ULONG cbHeadersSize = 0; ULONG nSectionAlignment; ULONG nFileAlignment; ULONG_PTR ImageBase; const IMAGE_DOS_HEADER * pidhDosHeader; const IMAGE_NT_HEADERS32 * pinhNtHeader; const IMAGE_OPTIONAL_HEADER32 * piohOptHeader; const IMAGE_SECTION_HEADER * pishSectionHeaders; PMM_SECTION_SEGMENT pssSegments; LARGE_INTEGER lnOffset; PVOID pBuffer; SIZE_T nPrevVirtualEndOfSegment = 0; ULONG nFileSizeOfHeaders = 0; ULONG i; ULONG AlignedLength; ASSERT(FileHeader); ASSERT(FileHeaderSize > 0); ASSERT(File); ASSERT(ImageSectionObject); ASSERT(ReadFileCb); ASSERT(AllocateSegmentsCb); ASSERT(Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize)); ASSERT(((UINT_PTR)FileHeader % TYPE_ALIGNMENT(IMAGE_DOS_HEADER)) == 0); #define DIE(ARGS_) { DPRINT ARGS_; goto l_Return; } pBuffer = NULL; pidhDosHeader = FileHeader; /* DOS HEADER */ nStatus = STATUS_ROS_EXEFMT_UNKNOWN_FORMAT; /* image too small to be an MZ executable */ if(FileHeaderSize < sizeof(IMAGE_DOS_HEADER)) DIE(("Too small to be an MZ executable, size is %lu\n", FileHeaderSize)); /* no MZ signature */ if(pidhDosHeader->e_magic != IMAGE_DOS_SIGNATURE) DIE(("No MZ signature found, e_magic is %hX\n", pidhDosHeader->e_magic)); /* NT HEADER */ nStatus = STATUS_INVALID_IMAGE_PROTECT; /* not a Windows executable */ if(pidhDosHeader->e_lfanew <= 0) DIE(("Not a Windows executable, e_lfanew is %d\n", pidhDosHeader->e_lfanew)); if(!Intsafe_AddULong32(&cbFileHeaderOffsetSize, pidhDosHeader->e_lfanew, RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader))) DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew)); if(FileHeaderSize < cbFileHeaderOffsetSize) pinhNtHeader = NULL; else { /* * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize), * and FileHeaderSize >= cbFileHeaderOffsetSize, so this holds true too */ ASSERT(Intsafe_CanOffsetPointer(FileHeader, pidhDosHeader->e_lfanew)); pinhNtHeader = (PVOID)((UINT_PTR)FileHeader + pidhDosHeader->e_lfanew); } /* * the buffer doesn't contain the NT file header, or the alignment is wrong: we * need to read the header from the file */ if(FileHeaderSize < cbFileHeaderOffsetSize || (UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0) { ULONG cbNtHeaderSize; ULONG cbReadSize; PVOID pData; l_ReadHeaderFromFile: cbNtHeaderSize = 0; lnOffset.QuadPart = pidhDosHeader->e_lfanew; /* read the header from the file */ nStatus = ReadFileCb(File, &lnOffset, sizeof(IMAGE_NT_HEADERS64), &pData, &pBuffer, &cbReadSize); if(!NT_SUCCESS(nStatus)) { NTSTATUS ReturnedStatus = nStatus; /* If it attempted to read past the end of the file, it means e_lfanew is invalid */ if (ReturnedStatus == STATUS_END_OF_FILE) nStatus = STATUS_INVALID_IMAGE_PROTECT; DIE(("ReadFile failed, status %08X\n", ReturnedStatus)); } ASSERT(pData); ASSERT(pBuffer); ASSERT(cbReadSize > 0); nStatus = STATUS_INVALID_IMAGE_FORMAT; /* the buffer doesn't contain the file header */ if(cbReadSize < RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader)) DIE(("The file doesn't contain the PE file header\n")); pinhNtHeader = pData; /* object still not aligned: copy it to the beginning of the buffer */ if((UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0) { ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == 0); RtlMoveMemory(pBuffer, pData, cbReadSize); pinhNtHeader = pBuffer; } /* invalid NT header */ nStatus = STATUS_INVALID_IMAGE_PROTECT; if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE) DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature)); nStatus = STATUS_INVALID_IMAGE_FORMAT; if(!Intsafe_AddULong32(&cbNtHeaderSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader))) DIE(("The full NT header is too large\n")); /* the buffer doesn't contain the whole NT header */ if(cbReadSize < cbNtHeaderSize) DIE(("The file doesn't contain the full NT header\n")); } else { ULONG cbOptHeaderOffsetSize = 0; nStatus = STATUS_INVALID_IMAGE_PROTECT; /* don't trust an invalid NT header */ if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE) DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature)); if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader))) DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew)); nStatus = STATUS_INVALID_IMAGE_FORMAT; if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, cbOptHeaderOffsetSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader)) DIE(("The NT header is too large, SizeOfOptionalHeader is %X\n", pinhNtHeader->FileHeader.SizeOfOptionalHeader)); /* the buffer doesn't contain the whole NT header: read it from the file */ if(cbOptHeaderOffsetSize > FileHeaderSize) goto l_ReadHeaderFromFile; } /* read information from the NT header */ piohOptHeader = &pinhNtHeader->OptionalHeader; cbOptHeaderSize = pinhNtHeader->FileHeader.SizeOfOptionalHeader; nStatus = STATUS_INVALID_IMAGE_FORMAT; if(!RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Magic)) DIE(("The optional header doesn't contain the Magic field, SizeOfOptionalHeader is %X\n", cbOptHeaderSize)); /* ASSUME: RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject)); */ switch(piohOptHeader->Magic) { case IMAGE_NT_OPTIONAL_HDR32_MAGIC: #ifdef _WIN64 case IMAGE_NT_OPTIONAL_HDR64_MAGIC: #endif // _WIN64 break; default: DIE(("Unrecognized optional header, Magic is %X\n", piohOptHeader->Magic)); } if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SectionAlignment) && RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, FileAlignment)) { /* See [1], section 3.4.2 */ if(piohOptHeader->SectionAlignment < PAGE_SIZE) { if(piohOptHeader->FileAlignment != piohOptHeader->SectionAlignment) DIE(("Sections aren't page-aligned and the file alignment isn't the same\n")); } else if(piohOptHeader->SectionAlignment < piohOptHeader->FileAlignment) DIE(("The section alignment is smaller than the file alignment\n")); nSectionAlignment = piohOptHeader->SectionAlignment; nFileAlignment = piohOptHeader->FileAlignment; if(!IsPowerOf2(nSectionAlignment) || !IsPowerOf2(nFileAlignment)) DIE(("The section alignment (%u) and file alignment (%u) aren't both powers of 2\n", nSectionAlignment, nFileAlignment)); } else { nSectionAlignment = PAGE_SIZE; nFileAlignment = PAGE_SIZE; } ASSERT(IsPowerOf2(nSectionAlignment)); ASSERT(IsPowerOf2(nFileAlignment)); switch(piohOptHeader->Magic) { /* PE32 */ case IMAGE_NT_OPTIONAL_HDR32_MAGIC: { if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, ImageBase)) ImageBase = piohOptHeader->ImageBase; if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfImage)) ImageSectionObject->ImageInformation.ImageFileSize = piohOptHeader->SizeOfImage; if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackReserve)) ImageSectionObject->ImageInformation.MaximumStackSize = piohOptHeader->SizeOfStackReserve; if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackCommit)) ImageSectionObject->ImageInformation.CommittedStackSize = piohOptHeader->SizeOfStackCommit; if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Subsystem)) { ImageSectionObject->ImageInformation.SubSystemType = piohOptHeader->Subsystem; if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MinorSubsystemVersion) && RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MajorSubsystemVersion)) { ImageSectionObject->ImageInformation.SubSystemMinorVersion = piohOptHeader->MinorSubsystemVersion; ImageSectionObject->ImageInformation.SubSystemMajorVersion = piohOptHeader->MajorSubsystemVersion; } } if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint)) { ImageSectionObject->ImageInformation.TransferAddress = (PVOID) (ImageBase + piohOptHeader->AddressOfEntryPoint); } if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfCode)) ImageSectionObject->ImageInformation.ImageContainsCode = piohOptHeader->SizeOfCode != 0; else ImageSectionObject->ImageInformation.ImageContainsCode = TRUE; if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint)) { if (piohOptHeader->AddressOfEntryPoint == 0) { ImageSectionObject->ImageInformation.ImageContainsCode = FALSE; } } if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, LoaderFlags)) ImageSectionObject->ImageInformation.LoaderFlags = piohOptHeader->LoaderFlags; if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, DllCharacteristics)) { ImageSectionObject->ImageInformation.DllCharacteristics = piohOptHeader->DllCharacteristics; /* * Since we don't really implement SxS yet and LD doesn't supoprt /ALLOWISOLATION:NO, hard-code * this flag here, which will prevent the loader and other code from doing any .manifest or SxS * magic to any binary. * * This will break applications that depend on SxS when running with real Windows Kernel32/SxS/etc * but honestly that's not tested. It will also break them when running no ReactOS once we implement * the SxS support -- at which point, duh, this should be removed. * * But right now, any app depending on SxS is already broken anyway, so this flag only helps. */ ImageSectionObject->ImageInformation.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_NO_ISOLATION; } break; } #ifdef _WIN64 /* PE64 */ case IMAGE_NT_OPTIONAL_HDR64_MAGIC: { const IMAGE_OPTIONAL_HEADER64 * pioh64OptHeader; pioh64OptHeader = (const IMAGE_OPTIONAL_HEADER64 *)piohOptHeader; if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, ImageBase)) { ImageBase = pioh64OptHeader->ImageBase; if(pioh64OptHeader->ImageBase > MAXULONG_PTR) DIE(("ImageBase exceeds the address space\n")); } if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfImage)) { if(pioh64OptHeader->SizeOfImage > MAXULONG_PTR) DIE(("SizeOfImage exceeds the address space\n")); ImageSectionObject->ImageInformation.ImageFileSize = pioh64OptHeader->SizeOfImage; } if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackReserve)) { if(pioh64OptHeader->SizeOfStackReserve > MAXULONG_PTR) DIE(("SizeOfStackReserve exceeds the address space\n")); ImageSectionObject->ImageInformation.MaximumStackSize = (ULONG_PTR) pioh64OptHeader->SizeOfStackReserve; } if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackCommit)) { if(pioh64OptHeader->SizeOfStackCommit > MAXULONG_PTR) DIE(("SizeOfStackCommit exceeds the address space\n")); ImageSectionObject->ImageInformation.CommittedStackSize = (ULONG_PTR) pioh64OptHeader->SizeOfStackCommit; } if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, Subsystem)) { ImageSectionObject->ImageInformation.SubSystemType = pioh64OptHeader->Subsystem; if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, MinorSubsystemVersion) && RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, MajorSubsystemVersion)) { ImageSectionObject->ImageInformation.SubSystemMinorVersion = pioh64OptHeader->MinorSubsystemVersion; ImageSectionObject->ImageInformation.SubSystemMajorVersion = pioh64OptHeader->MajorSubsystemVersion; } } if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, AddressOfEntryPoint)) { ImageSectionObject->ImageInformation.TransferAddress = (PVOID) (ImageBase + pioh64OptHeader->AddressOfEntryPoint); } if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfCode)) ImageSectionObject->ImageInformation.ImageContainsCode = pioh64OptHeader->SizeOfCode != 0; else ImageSectionObject->ImageInformation.ImageContainsCode = TRUE; if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, AddressOfEntryPoint)) { if (pioh64OptHeader->AddressOfEntryPoint == 0) { ImageSectionObject->ImageInformation.ImageContainsCode = FALSE; } } if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, LoaderFlags)) ImageSectionObject->ImageInformation.LoaderFlags = pioh64OptHeader->LoaderFlags; if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, DllCharacteristics)) ImageSectionObject->ImageInformation.DllCharacteristics = pioh64OptHeader->DllCharacteristics; break; } #endif // _WIN64 } /* [1], section 3.4.2 */ if((ULONG_PTR)ImageBase % 0x10000) DIE(("ImageBase is not aligned on a 64KB boundary")); ImageSectionObject->ImageInformation.ImageCharacteristics = pinhNtHeader->FileHeader.Characteristics; ImageSectionObject->ImageInformation.Machine = pinhNtHeader->FileHeader.Machine; ImageSectionObject->ImageInformation.GpValue = 0; ImageSectionObject->ImageInformation.ZeroBits = 0; ImageSectionObject->BasedAddress = (PVOID)ImageBase; /* SECTION HEADERS */ nStatus = STATUS_INVALID_IMAGE_FORMAT; /* see [1], section 3.3 */ if(pinhNtHeader->FileHeader.NumberOfSections > 96) DIE(("Too many sections, NumberOfSections is %u\n", pinhNtHeader->FileHeader.NumberOfSections)); /* * the additional segment is for the file's headers. They need to be present for * the benefit of the dynamic loader (to locate exports, defaults for thread * parameters, resources, etc.) */ ImageSectionObject->NrSegments = pinhNtHeader->FileHeader.NumberOfSections + 1; /* file offset for the section headers */ if(!Intsafe_AddULong32(&cbSectionHeadersOffset, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader))) DIE(("Offset overflow\n")); if(!Intsafe_AddULong32(&cbSectionHeadersOffset, cbSectionHeadersOffset, pinhNtHeader->FileHeader.SizeOfOptionalHeader)) DIE(("Offset overflow\n")); /* size of the section headers */ ASSERT(Intsafe_CanMulULong32(pinhNtHeader->FileHeader.NumberOfSections, sizeof(IMAGE_SECTION_HEADER))); cbSectionHeadersSize = pinhNtHeader->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER); if(!Intsafe_AddULong32(&cbSectionHeadersOffsetSize, cbSectionHeadersOffset, cbSectionHeadersSize)) DIE(("Section headers too large\n")); /* size of the executable's headers */ if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfHeaders)) { // if(!IsAligned(piohOptHeader->SizeOfHeaders, nFileAlignment)) // DIE(("SizeOfHeaders is not aligned\n")); if(cbSectionHeadersSize > piohOptHeader->SizeOfHeaders) DIE(("The section headers overflow SizeOfHeaders\n")); cbHeadersSize = piohOptHeader->SizeOfHeaders; } else if(!AlignUp(&cbHeadersSize, cbSectionHeadersOffsetSize, nFileAlignment)) DIE(("Overflow aligning the size of headers\n")); if(pBuffer) { ExFreePool(pBuffer); pBuffer = NULL; } /* WARNING: pinhNtHeader IS NO LONGER USABLE */ /* WARNING: piohOptHeader IS NO LONGER USABLE */ /* WARNING: pioh64OptHeader IS NO LONGER USABLE */ if(FileHeaderSize < cbSectionHeadersOffsetSize) pishSectionHeaders = NULL; else { /* * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize), * and FileHeaderSize >= cbSectionHeadersOffsetSize, so this holds true too */ ASSERT(Intsafe_CanOffsetPointer(FileHeader, cbSectionHeadersOffset)); pishSectionHeaders = (PVOID)((UINT_PTR)FileHeader + cbSectionHeadersOffset); } /* * the buffer doesn't contain the section headers, or the alignment is wrong: * read the headers from the file */ if(FileHeaderSize < cbSectionHeadersOffsetSize || (UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0) { PVOID pData; ULONG cbReadSize; lnOffset.QuadPart = cbSectionHeadersOffset; /* read the header from the file */ nStatus = ReadFileCb(File, &lnOffset, cbSectionHeadersSize, &pData, &pBuffer, &cbReadSize); if(!NT_SUCCESS(nStatus)) DIE(("ReadFile failed with status %08X\n", nStatus)); ASSERT(pData); ASSERT(pBuffer); ASSERT(cbReadSize > 0); nStatus = STATUS_INVALID_IMAGE_FORMAT; /* the buffer doesn't contain all the section headers */ if(cbReadSize < cbSectionHeadersSize) DIE(("The file doesn't contain all of the section headers\n")); pishSectionHeaders = pData; /* object still not aligned: copy it to the beginning of the buffer */ if((UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0) { ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) == 0); RtlMoveMemory(pBuffer, pData, cbReadSize); pishSectionHeaders = pBuffer; } } /* SEGMENTS */ /* allocate the segments */ nStatus = STATUS_INSUFFICIENT_RESOURCES; ImageSectionObject->Segments = AllocateSegmentsCb(ImageSectionObject->NrSegments); if(ImageSectionObject->Segments == NULL) DIE(("AllocateSegments failed\n")); /* initialize the headers segment */ pssSegments = ImageSectionObject->Segments; // ASSERT(IsAligned(cbHeadersSize, nFileAlignment)); if(!AlignUp(&nFileSizeOfHeaders, cbHeadersSize, nFileAlignment)) DIE(("Cannot align the size of the section headers\n")); nPrevVirtualEndOfSegment = ALIGN_UP_BY(cbHeadersSize, nSectionAlignment); if (nPrevVirtualEndOfSegment < cbHeadersSize) DIE(("Cannot align the size of the section headers\n")); pssSegments[0].Image.FileOffset = 0; pssSegments[0].Protection = PAGE_READONLY; pssSegments[0].Length.QuadPart = nPrevVirtualEndOfSegment; pssSegments[0].RawLength.QuadPart = nFileSizeOfHeaders; pssSegments[0].Image.VirtualAddress = 0; pssSegments[0].Image.Characteristics = 0; pssSegments[0].WriteCopy = TRUE; /* skip the headers segment */ ++ pssSegments; nStatus = STATUS_INVALID_IMAGE_FORMAT; /* convert the executable sections into segments. See also [1], section 4 */ for(i = 0; i < ImageSectionObject->NrSegments - 1; ++ i) { ULONG nCharacteristics; /* validate the alignment */ if(!IsAligned(pishSectionHeaders[i].VirtualAddress, nSectionAlignment)) DIE(("Image.VirtualAddress[%u] is not aligned\n", i)); /* sections must be contiguous, ordered by base address and non-overlapping */ if(pishSectionHeaders[i].VirtualAddress != nPrevVirtualEndOfSegment) DIE(("Memory gap between section %u and the previous\n", i)); /* ignore explicit BSS sections */ if(pishSectionHeaders[i].SizeOfRawData != 0) { /* validate the alignment */ #if 0 /* Yes, this should be a multiple of FileAlignment, but there's * stuff out there that isn't. We can cope with that */ if(!IsAligned(pishSectionHeaders[i].SizeOfRawData, nFileAlignment)) DIE(("SizeOfRawData[%u] is not aligned\n", i)); #endif // if(!IsAligned(pishSectionHeaders[i].PointerToRawData, nFileAlignment)) // DIE(("PointerToRawData[%u] is not aligned\n", i)); /* conversion */ pssSegments[i].Image.FileOffset = pishSectionHeaders[i].PointerToRawData; pssSegments[i].RawLength.QuadPart = pishSectionHeaders[i].SizeOfRawData; } else { ASSERT(pssSegments[i].Image.FileOffset == 0); ASSERT(pssSegments[i].RawLength.QuadPart == 0); } ASSERT(Intsafe_CanAddLong64(pssSegments[i].Image.FileOffset, pssSegments[i].RawLength.QuadPart)); nCharacteristics = pishSectionHeaders[i].Characteristics; /* no explicit protection */ if((nCharacteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) == 0) { if(nCharacteristics & IMAGE_SCN_CNT_CODE) nCharacteristics |= IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ; if(nCharacteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE; if(nCharacteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE; } /* see table above */ pssSegments[i].Protection = SectionCharacteristicsToProtect[nCharacteristics >> 28]; pssSegments[i].WriteCopy = !(nCharacteristics & IMAGE_SCN_MEM_SHARED); if(pishSectionHeaders[i].Misc.VirtualSize == 0 || pishSectionHeaders[i].Misc.VirtualSize < pishSectionHeaders[i].SizeOfRawData) pssSegments[i].Length.QuadPart = pishSectionHeaders[i].SizeOfRawData; else pssSegments[i].Length.QuadPart = pishSectionHeaders[i].Misc.VirtualSize; AlignedLength = ALIGN_UP_BY(pssSegments[i].Length.LowPart, nSectionAlignment); if(AlignedLength < pssSegments[i].Length.LowPart) DIE(("Cannot align the virtual size of section %u\n", i)); pssSegments[i].Length.LowPart = AlignedLength; if(pssSegments[i].Length.QuadPart == 0) DIE(("Virtual size of section %u is null\n", i)); pssSegments[i].Image.VirtualAddress = pishSectionHeaders[i].VirtualAddress; pssSegments[i].Image.Characteristics = pishSectionHeaders[i].Characteristics; /* ensure the memory image is no larger than 4GB */ nPrevVirtualEndOfSegment = (ULONG_PTR)(pssSegments[i].Image.VirtualAddress + pssSegments[i].Length.QuadPart); if (nPrevVirtualEndOfSegment < pssSegments[i].Image.VirtualAddress) DIE(("The image is too large\n")); } if(nSectionAlignment >= PAGE_SIZE) *Flags |= EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED; /* Success */ nStatus = STATUS_SUCCESS;// STATUS_ROS_EXEFMT_LOADED_FORMAT | EXEFMT_LOADED_PE32; l_Return: if(pBuffer) ExFreePool(pBuffer); return nStatus; } /* * FUNCTION: Waits in kernel mode indefinitely for a file object lock. * ARGUMENTS: PFILE_OBJECT to wait for. * RETURNS: Status of the wait. */ NTSTATUS MmspWaitForFileLock(PFILE_OBJECT File) { return STATUS_SUCCESS; //return KeWaitForSingleObject(&File->Lock, 0, KernelMode, FALSE, NULL); } 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 %lu still referenced (was %lu)\n", i, SectionSegments[i].ReferenceCount); KeBugCheck(MEMORY_MANAGEMENT); } MmFreePageTablesSectionSegment(&SectionSegments[i], NULL); } 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, NULL); ExFreePool(Segment); FileObject->SectionObjectPointer->DataSectionObject = NULL; } } VOID NTAPI MmSharePageEntrySectionSegment(PMM_SECTION_SEGMENT Segment, PLARGE_INTEGER Offset) { ULONG_PTR 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, PLARGE_INTEGER Offset, BOOLEAN Dirty, BOOLEAN PageOut, ULONG_PTR *InEntry) { ULONG_PTR Entry = InEntry ? *InEntry : MmGetPageEntrySectionSegment(Segment, Offset); BOOLEAN IsDirectMapped = FALSE; 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 (Seg %p Offset %x Page %x)\n", Segment, Offset->LowPart, PFN_FROM_SSE(Entry)); 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; SWAPENTRY SavedSwapEntry; PFN_NUMBER Page; #ifndef NEWCC PROS_SHARED_CACHE_MAP SharedCacheMap; BOOLEAN IsImageSection; LARGE_INTEGER FileOffset; FileOffset.QuadPart = Offset->QuadPart + Segment->Image.FileOffset; IsImageSection = Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE; #endif Page = PFN_FROM_SSE(Entry); FileObject = Section->FileObject; if (FileObject != NULL && !(Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)) { #ifndef NEWCC if ((FileOffset.QuadPart % PAGE_SIZE) == 0 && (Offset->QuadPart + PAGE_SIZE <= Segment->RawLength.QuadPart || !IsImageSection)) { NTSTATUS Status; SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap; IsDirectMapped = TRUE; #ifndef NEWCC Status = CcRosUnmapVacb(SharedCacheMap, FileOffset.QuadPart, Dirty); #else Status = STATUS_SUCCESS; #endif if (!NT_SUCCESS(Status)) { DPRINT1("CcRosUnmapVacb failed, status = %x\n", Status); KeBugCheck(MEMORY_MANAGEMENT); } } #endif } SavedSwapEntry = MmGetSavedSwapEntryPage(Page); if (SavedSwapEntry == 0) { if (!PageOut && ((Segment->Flags & MM_PAGEFILE_SEGMENT) || (Segment->Image.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); if (InEntry) *InEntry = Entry; MiSetPageEvent(NULL, NULL); } else { MmSetPageEntrySectionSegment(Segment, Offset, 0); if (InEntry) *InEntry = 0; MiSetPageEvent(NULL, NULL); if (!IsDirectMapped) { MmReleasePageMemoryConsumer(MC_USER, Page); } } } else { if ((Segment->Flags & MM_PAGEFILE_SEGMENT) || (Segment->Image.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)); if (InEntry) *InEntry = MAKE_SWAP_SSE(SavedSwapEntry); MmSetSavedSwapEntryPage(Page, 0); MiSetPageEvent(NULL, NULL); } 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 { if (InEntry) *InEntry = Entry; else MmSetPageEntrySectionSegment(Segment, Offset, Entry); } return(SHARE_COUNT_FROM_SSE(Entry) > 0); } BOOLEAN MiIsPageFromCache(PMEMORY_AREA MemoryArea, LONGLONG SegOffset) { #ifndef NEWCC if (!(MemoryArea->Data.SectionData.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)) { PROS_SHARED_CACHE_MAP SharedCacheMap; PROS_VACB Vacb; SharedCacheMap = MemoryArea->Data.SectionData.Section->FileObject->SectionObjectPointer->SharedCacheMap; Vacb = CcRosLookupVacb(SharedCacheMap, SegOffset + MemoryArea->Data.SectionData.Segment->Image.FileOffset); if (Vacb) { CcRosReleaseVacb(SharedCacheMap, Vacb, Vacb->Valid, FALSE, TRUE); return TRUE; } } #endif return FALSE; } NTSTATUS NTAPI MiCopyFromUserPage(PFN_NUMBER DestPage, const VOID *SrcAddress) { PEPROCESS Process; KIRQL Irql; PVOID DestAddress; Process = PsGetCurrentProcess(); DestAddress = MiMapPageInHyperSpace(Process, DestPage, &Irql); if (DestAddress == NULL) { return(STATUS_NO_MEMORY); } ASSERT((ULONG_PTR)DestAddress % PAGE_SIZE == 0); ASSERT((ULONG_PTR)SrcAddress % PAGE_SIZE == 0); RtlCopyMemory(DestAddress, SrcAddress, PAGE_SIZE); MiUnmapPageInHyperSpace(Process, DestAddress, Irql); return(STATUS_SUCCESS); } #ifndef NEWCC NTSTATUS NTAPI MiReadPage(PMEMORY_AREA MemoryArea, LONGLONG SegOffset, PPFN_NUMBER 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. */ { LONGLONG BaseOffset; LONGLONG FileOffset; PVOID BaseAddress; BOOLEAN UptoDate; PROS_VACB Vacb; PFILE_OBJECT FileObject; NTSTATUS Status; LONGLONG RawLength; PROS_SHARED_CACHE_MAP SharedCacheMap; BOOLEAN IsImageSection; LONGLONG Length; FileObject = MemoryArea->Data.SectionData.Section->FileObject; SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap; RawLength = MemoryArea->Data.SectionData.Segment->RawLength.QuadPart; FileOffset = SegOffset + MemoryArea->Data.SectionData.Segment->Image.FileOffset; IsImageSection = MemoryArea->Data.SectionData.Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE; ASSERT(SharedCacheMap); DPRINT("%S %I64x\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 VACB. */ if (((FileOffset % PAGE_SIZE) == 0) && ((SegOffset + PAGE_SIZE <= RawLength) || !IsImageSection) && !(MemoryArea->Data.SectionData.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)) { /* * Get the related VACB; we use a lower level interface than * filesystems do because it is safe for us to use an offset with an * alignment less than the file system block size. */ Status = CcRosGetVacb(SharedCacheMap, FileOffset, &BaseOffset, &BaseAddress, &UptoDate, &Vacb); if (!NT_SUCCESS(Status)) { return(Status); } if (!UptoDate) { /* * If the VACB isn't up to date then call the file * system to read in the data. */ Status = CcReadVirtualAddress(Vacb); if (!NT_SUCCESS(Status)) { CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE); return Status; } } /* Probe the page, since it's PDE might not be synced */ (void)*((volatile char*)BaseAddress + FileOffset - BaseOffset); /* * Retrieve the page from the view that we actually want. */ (*Page) = MmGetPhysicalAddress((char*)BaseAddress + FileOffset - BaseOffset).LowPart >> PAGE_SHIFT; CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, TRUE); } else { PEPROCESS Process; KIRQL Irql; PVOID PageAddr; LONGLONG VacbOffset; /* * Allocate a page, this is rather complicated by the possibility * we might have to move other things out of memory */ MI_SET_USAGE(MI_USAGE_SECTION); MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName); Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, Page); if (!NT_SUCCESS(Status)) { return(Status); } Status = CcRosGetVacb(SharedCacheMap, FileOffset, &BaseOffset, &BaseAddress, &UptoDate, &Vacb); if (!NT_SUCCESS(Status)) { return(Status); } if (!UptoDate) { /* * If the VACB isn't up to date then call the file * system to read in the data. */ Status = CcReadVirtualAddress(Vacb); if (!NT_SUCCESS(Status)) { CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE); return Status; } } Process = PsGetCurrentProcess(); PageAddr = MiMapPageInHyperSpace(Process, *Page, &Irql); VacbOffset = BaseOffset + VACB_MAPPING_GRANULARITY - FileOffset; Length = RawLength - SegOffset; if (Length <= VacbOffset && Length <= PAGE_SIZE) { memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, Length); } else if (VacbOffset >= PAGE_SIZE) { memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, PAGE_SIZE); } else { memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, VacbOffset); MiUnmapPageInHyperSpace(Process, PageAddr, Irql); CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE); Status = CcRosGetVacb(SharedCacheMap, FileOffset + VacbOffset, &BaseOffset, &BaseAddress, &UptoDate, &Vacb); if (!NT_SUCCESS(Status)) { return(Status); } if (!UptoDate) { /* * If the VACB isn't up to date then call the file * system to read in the data. */ Status = CcReadVirtualAddress(Vacb); if (!NT_SUCCESS(Status)) { CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE); return Status; } } PageAddr = MiMapPageInHyperSpace(Process, *Page, &Irql); if (Length < PAGE_SIZE) { memcpy((char*)PageAddr + VacbOffset, BaseAddress, Length - VacbOffset); } else { memcpy((char*)PageAddr + VacbOffset, BaseAddress, PAGE_SIZE - VacbOffset); } } MiUnmapPageInHyperSpace(Process, PageAddr, Irql); CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE); } return(STATUS_SUCCESS); } #else NTSTATUS NTAPI MiReadPage(PMEMORY_AREA MemoryArea, LONGLONG SegOffset, PPFN_NUMBER 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. */ { MM_REQUIRED_RESOURCES Resources; NTSTATUS Status; RtlZeroMemory(&Resources, sizeof(MM_REQUIRED_RESOURCES)); Resources.Context = MemoryArea->Data.SectionData.Section->FileObject; Resources.FileOffset.QuadPart = SegOffset + MemoryArea->Data.SectionData.Segment->Image.FileOffset; Resources.Consumer = MC_USER; Resources.Amount = PAGE_SIZE; DPRINT("%S, offset 0x%x, len 0x%x, page 0x%x\n", ((PFILE_OBJECT)Resources.Context)->FileName.Buffer, Resources.FileOffset.LowPart, Resources.Amount, Resources.Page[0]); Status = MiReadFilePage(MmGetKernelAddressSpace(), MemoryArea, &Resources); *Page = Resources.Page[0]; return Status; } #endif static VOID MmAlterViewAttributes(PMMSUPPORT AddressSpace, PVOID BaseAddress, SIZE_T 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); ASSERT(MemoryArea != NULL); Segment = MemoryArea->Data.SectionData.Segment; MmLockSectionSegment(Segment); if ((Segment->WriteCopy) && (NewProtect == PAGE_READWRITE || NewProtect == PAGE_EXECUTE_READWRITE)) { DoCOW = TRUE; } if (OldProtect != NewProtect) { for (i = 0; i < PAGE_ROUND_UP(RegionSize) / PAGE_SIZE; i++) { SWAPENTRY SwapEntry; PVOID Address = (char*)BaseAddress + (i * PAGE_SIZE); ULONG Protect = NewProtect; /* Wait for a wait entry to disappear */ do { MmGetPageFileMapping(Process, Address, &SwapEntry); if (SwapEntry != MM_WAIT_ENTRY) break; MiWaitForPageEvent(Process, Address); } while (TRUE); /* * If we doing COW for this segment then check if the page is * already private. */ if (DoCOW && MmIsPagePresent(Process, Address)) { LARGE_INTEGER Offset; ULONG_PTR Entry; PFN_NUMBER Page; Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea) + MemoryArea->Data.SectionData.ViewOffset.QuadPart; Entry = MmGetPageEntrySectionSegment(Segment, &Offset); /* * An MM_WAIT_ENTRY is ok in this case... It'll just count as * IS_SWAP_FROM_SSE and we'll do the right thing. */ Page = MmGetPfnForProcess(Process, Address); Protect = PAGE_READONLY; if (IS_SWAP_FROM_SSE(Entry) || PFN_FROM_SSE(Entry) != Page) { Protect = NewProtect; } } if (MmIsPagePresent(Process, Address) || MmIsDisabledPage(Process, Address)) { MmSetPageProtect(Process, Address, Protect); } } } MmUnlockSectionSegment(Segment); } NTSTATUS NTAPI MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace, MEMORY_AREA* MemoryArea, PVOID Address, BOOLEAN Locked) { LARGE_INTEGER Offset; PFN_NUMBER Page; NTSTATUS Status; PROS_SECTION_OBJECT Section; PMM_SECTION_SEGMENT Segment; ULONG_PTR Entry; ULONG_PTR Entry1; ULONG Attributes; PMM_REGION Region; BOOLEAN HasSwapEntry; PVOID PAddress; PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace); SWAPENTRY SwapEntry; /* * 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); } if (MmIsDisabledPage(Process, Address)) { return(STATUS_ACCESS_VIOLATION); } /* * Check for the virtual memory area being deleted. */ if (MemoryArea->DeleteInProgress) { return(STATUS_UNSUCCESSFUL); } PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE); Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea) + MemoryArea->Data.SectionData.ViewOffset.QuadPart; Segment = MemoryArea->Data.SectionData.Segment; Section = MemoryArea->Data.SectionData.Section; Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea), &MemoryArea->Data.SectionData.RegionListHead, Address, NULL); ASSERT(Region != NULL); /* * Lock the segment */ MmLockSectionSegment(Segment); Entry = MmGetPageEntrySectionSegment(Segment, &Offset); /* * Check if this page needs to be mapped COW */ if ((Segment->WriteCopy) && (Region->Protect == PAGE_READWRITE || Region->Protect == PAGE_EXECUTE_READWRITE)) { Attributes = Region->Protect == PAGE_READWRITE ? PAGE_READONLY : PAGE_EXECUTE_READ; } else { Attributes = Region->Protect; } /* * Check if someone else is already handling this fault, if so wait * for them */ if (Entry && MM_IS_WAIT_PTE(Entry)) { MmUnlockSectionSegment(Segment); MmUnlockAddressSpace(AddressSpace); MiWaitForPageEvent(NULL, NULL); MmLockAddressSpace(AddressSpace); DPRINT("Address 0x%p\n", Address); return(STATUS_MM_RESTART_OPERATION); } HasSwapEntry = MmIsPageSwapEntry(Process, Address); /* See if we should use a private page */ if (HasSwapEntry) { SWAPENTRY DummyEntry; /* * Is it a wait entry? */ if (HasSwapEntry) { MmGetPageFileMapping(Process, Address, &SwapEntry); if (SwapEntry == MM_WAIT_ENTRY) { MmUnlockSectionSegment(Segment); MmUnlockAddressSpace(AddressSpace); MiWaitForPageEvent(NULL, NULL); MmLockAddressSpace(AddressSpace); return STATUS_MM_RESTART_OPERATION; } /* * Must be private page we have swapped out. */ /* * Sanity check */ if (Segment->Flags & MM_PAGEFILE_SEGMENT) { DPRINT1("Found a swaped out private page in a pagefile section.\n"); KeBugCheck(MEMORY_MANAGEMENT); } MmDeletePageFileMapping(Process, Address, &SwapEntry); } MmUnlockSectionSegment(Segment); /* Tell everyone else we are serving the fault. */ MmCreatePageFileMapping(Process, Address, MM_WAIT_ENTRY); MmUnlockAddressSpace(AddressSpace); MI_SET_USAGE(MI_USAGE_SECTION); if (Process) MI_SET_PROCESS2(Process->ImageFileName); if (!Process) MI_SET_PROCESS2("Kernel Section"); Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page); if (!NT_SUCCESS(Status)) { KeBugCheck(MEMORY_MANAGEMENT); } if (HasSwapEntry) { Status = MmReadFromSwapPage(SwapEntry, Page); if (!NT_SUCCESS(Status)) { DPRINT1("MmReadFromSwapPage failed, status = %x\n", Status); KeBugCheck(MEMORY_MANAGEMENT); } } MmLockAddressSpace(AddressSpace); MmDeletePageFileMapping(Process, PAddress, &DummyEntry); Status = MmCreateVirtualMapping(Process, PAddress, 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. */ if (HasSwapEntry) MmSetSavedSwapEntryPage(Page, SwapEntry); /* * Add the page to the process's working set */ MmInsertRmap(Page, Process, Address); /* * Finish the operation */ MiSetPageEvent(Process, Address); DPRINT("Address 0x%p\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 = (PFN_NUMBER)(Offset.QuadPart >> PAGE_SHIFT); Status = MmCreateVirtualMappingUnsafe(Process, PAddress, 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 */ MiSetPageEvent(Process, Address); DPRINT("Address 0x%p\n", Address); return(STATUS_SUCCESS); } /* * Get the entry corresponding to the offset within the section */ Entry = MmGetPageEntrySectionSegment(Segment, &Offset); if (Entry == 0) { SWAPENTRY FakeSwapEntry; /* * 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 */ MmSetPageEntrySectionSegment(Segment, &Offset, MAKE_SWAP_SSE(MM_WAIT_ENTRY)); MmUnlockSectionSegment(Segment); MmCreatePageFileMapping(Process, PAddress, MM_WAIT_ENTRY); MmUnlockAddressSpace(AddressSpace); if ((Segment->Flags & MM_PAGEFILE_SEGMENT) || ((Offset.QuadPart >= (LONGLONG)PAGE_ROUND_UP(Segment->RawLength.QuadPart) && (Section->AllocationAttributes & SEC_IMAGE)))) { MI_SET_USAGE(MI_USAGE_SECTION); if (Process) MI_SET_PROCESS2(Process->ImageFileName); if (!Process) MI_SET_PROCESS2("Kernel Section"); Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page); if (!NT_SUCCESS(Status)) { DPRINT1("MmRequestPageMemoryConsumer failed (Status %x)\n", Status); } } else { Status = MiReadPage(MemoryArea, Offset.QuadPart, &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); MiSetPageEvent(Process, Address); DPRINT("Address 0x%p\n", Address); return(Status); } /* Lock both segment and process address space while we proceed. */ MmLockAddressSpace(AddressSpace); MmLockSectionSegment(Segment); MmDeletePageFileMapping(Process, PAddress, &FakeSwapEntry); DPRINT("CreateVirtualMapping Page %x Process %p PAddress %p Attributes %x\n", Page, Process, PAddress, Attributes); Status = MmCreateVirtualMapping(Process, PAddress, Attributes, &Page, 1); if (!NT_SUCCESS(Status)) { DPRINT1("Unable to create virtual mapping\n"); KeBugCheck(MEMORY_MANAGEMENT); } ASSERT(MmIsPagePresent(Process, PAddress)); MmInsertRmap(Page, Process, Address); /* Set this section offset has being backed by our new page. */ Entry = MAKE_SSE(Page << PAGE_SHIFT, 1); MmSetPageEntrySectionSegment(Segment, &Offset, Entry); MmUnlockSectionSegment(Segment); MiSetPageEvent(Process, Address); DPRINT("Address 0x%p\n", Address); return(STATUS_SUCCESS); } else if (IS_SWAP_FROM_SSE(Entry)) { SWAPENTRY SwapEntry; SwapEntry = SWAPENTRY_FROM_SSE(Entry); /* See if a page op is running on this segment. */ if (SwapEntry == MM_WAIT_ENTRY) { MmUnlockSectionSegment(Segment); MmUnlockAddressSpace(AddressSpace); MiWaitForPageEvent(NULL, NULL); MmLockAddressSpace(AddressSpace); return STATUS_MM_RESTART_OPERATION; } /* * Release all our locks and read in the page from disk */ MmUnlockSectionSegment(Segment); MmUnlockAddressSpace(AddressSpace); MI_SET_USAGE(MI_USAGE_SECTION); if (Process) MI_SET_PROCESS2(Process->ImageFileName); if (!Process) MI_SET_PROCESS2("Kernel Section"); 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 (%x vs %x)\n", Entry, Entry1); KeBugCheck(MEMORY_MANAGEMENT); } /* * Save the swap entry. */ MmSetSavedSwapEntryPage(Page, SwapEntry); /* Map the page into the process address space */ Status = MmCreateVirtualMapping(Process, PAddress, Region->Protect, &Page, 1); if (!NT_SUCCESS(Status)) { DPRINT1("Unable to create virtual mapping\n"); KeBugCheck(MEMORY_MANAGEMENT); } MmInsertRmap(Page, Process, Address); /* * 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); MiSetPageEvent(Process, Address); DPRINT("Address 0x%p\n", Address); return(STATUS_SUCCESS); } else { /* We already have a page on this section offset. Map it into the process address space. */ Page = PFN_FROM_SSE(Entry); Status = MmCreateVirtualMapping(Process, PAddress, Attributes, &Page, 1); if (!NT_SUCCESS(Status)) { DPRINT1("Unable to create virtual mapping\n"); KeBugCheck(MEMORY_MANAGEMENT); } MmInsertRmap(Page, Process, Address); /* Take a reference on it */ MmSharePageEntrySectionSegment(Segment, &Offset); MmUnlockSectionSegment(Segment); MiSetPageEvent(Process, Address); DPRINT("Address 0x%p\n", Address); return(STATUS_SUCCESS); } } NTSTATUS NTAPI MmAccessFaultSectionView(PMMSUPPORT AddressSpace, MEMORY_AREA* MemoryArea, PVOID Address) { PMM_SECTION_SEGMENT Segment; PROS_SECTION_OBJECT Section; PFN_NUMBER OldPage; PFN_NUMBER NewPage; NTSTATUS Status; PVOID PAddress; LARGE_INTEGER Offset; PMM_REGION Region; ULONG_PTR Entry; PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace); DPRINT("MmAccessFaultSectionView(%p, %p, %p)\n", AddressSpace, MemoryArea, Address); /* Make sure we have a page mapping for this address. */ Status = MmNotPresentFaultSectionView(AddressSpace, MemoryArea, Address, TRUE); if (!NT_SUCCESS(Status)) { /* This is invalid access ! */ return Status; } /* * Check if the page has already been set readwrite */ if (MmGetPageProtect(Process, Address) & PAGE_READWRITE) { DPRINT("Address 0x%p\n", Address); return(STATUS_SUCCESS); } /* * Find the offset of the page */ PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE); Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea) + MemoryArea->Data.SectionData.ViewOffset.QuadPart; Segment = MemoryArea->Data.SectionData.Segment; Section = MemoryArea->Data.SectionData.Section; Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea), &MemoryArea->Data.SectionData.RegionListHead, Address, NULL); ASSERT(Region != NULL); /* * Check if we are doing COW */ if (!((Segment->WriteCopy) && (Region->Protect == PAGE_READWRITE || Region->Protect == PAGE_EXECUTE_READWRITE))) { DPRINT("Address 0x%p\n", Address); return(STATUS_ACCESS_VIOLATION); } /* Get the page mapping this section offset. */ MmLockSectionSegment(Segment); Entry = MmGetPageEntrySectionSegment(Segment, &Offset); /* Get the current page mapping for the process */ ASSERT(MmIsPagePresent(Process, PAddress)); OldPage = MmGetPfnForProcess(Process, PAddress); ASSERT(OldPage != 0); if (IS_SWAP_FROM_SSE(Entry) || PFN_FROM_SSE(Entry) != OldPage) { MmUnlockSectionSegment(Segment); /* This is a private page. We must only change the page protection. */ MmSetPageProtect(Process, PAddress, Region->Protect); return(STATUS_SUCCESS); } /* * Allocate a page */ MI_SET_USAGE(MI_USAGE_SECTION); if (Process) MI_SET_PROCESS2(Process->ImageFileName); if (!Process) MI_SET_PROCESS2("Kernel Section"); Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &NewPage); if (!NT_SUCCESS(Status)) { KeBugCheck(MEMORY_MANAGEMENT); } /* * Copy the old page */ NT_VERIFY(NT_SUCCESS(MiCopyFromUserPage(NewPage, PAddress))); /* * Unshare the old page. */ DPRINT("Swapping page (Old %x New %x)\n", OldPage, NewPage); MmDeleteVirtualMapping(Process, PAddress, NULL, NULL); MmDeleteRmap(OldPage, Process, PAddress); MmUnsharePageEntrySectionSegment(Section, Segment, &Offset, FALSE, FALSE, NULL); MmUnlockSectionSegment(Segment); /* * Set the PTE to point to the new page */ Status = MmCreateVirtualMapping(Process, PAddress, Region->Protect, &NewPage, 1); if (!NT_SUCCESS(Status)) { DPRINT1("MmCreateVirtualMapping failed, unable to create virtual mapping, not out of memory\n"); KeBugCheck(MEMORY_MANAGEMENT); return(Status); } MmInsertRmap(NewPage, Process, PAddress); MiSetPageEvent(Process, Address); DPRINT("Address 0x%p\n", Address); return(STATUS_SUCCESS); } VOID MmPageOutDeleteMapping(PVOID Context, PEPROCESS Process, PVOID Address) { MM_SECTION_PAGEOUT_CONTEXT* PageOutContext; BOOLEAN WasDirty; PFN_NUMBER Page = 0; PageOutContext = (MM_SECTION_PAGEOUT_CONTEXT*)Context; if (Process) { MmLockAddressSpace(&Process->Vm); } MmDeleteVirtualMapping(Process, Address, &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, &PageOutContext->SectionEntry); MmUnlockSectionSegment(PageOutContext->Segment); } if (Process) { MmUnlockAddressSpace(&Process->Vm); } if (PageOutContext->Private) { MmReleasePageMemoryConsumer(MC_USER, Page); } } NTSTATUS NTAPI MmPageOutSectionView(PMMSUPPORT AddressSpace, MEMORY_AREA* MemoryArea, PVOID Address, ULONG_PTR Entry) { PFN_NUMBER Page; MM_SECTION_PAGEOUT_CONTEXT Context; SWAPENTRY SwapEntry; NTSTATUS Status; #ifndef NEWCC ULONGLONG FileOffset; PFILE_OBJECT FileObject; PROS_SHARED_CACHE_MAP SharedCacheMap = NULL; BOOLEAN IsImageSection; #endif BOOLEAN DirectMapped; 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.SectionEntry = Entry; Context.CallingProcess = Process; Context.Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea) + MemoryArea->Data.SectionData.ViewOffset.QuadPart; DirectMapped = FALSE; MmLockSectionSegment(Context.Segment); #ifndef NEWCC FileOffset = Context.Offset.QuadPart + Context.Segment->Image.FileOffset; IsImageSection = Context.Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE; FileObject = Context.Section->FileObject; if (FileObject != NULL && !(Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)) { SharedCacheMap = 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.QuadPart + PAGE_SIZE <= Context.Segment->RawLength.QuadPart || !IsImageSection)) { DirectMapped = TRUE; } } #endif /* * 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%p " "process %p\n", Address, Process ? Process->UniqueProcessId : 0); KeBugCheck(MEMORY_MANAGEMENT); } /* * Get the section segment entry and the physical address. */ if (!MmIsPagePresent(Process, Address)) { DPRINT1("Trying to page out not-present page at (%p,0x%p).\n", Process ? Process->UniqueProcessId : 0, Address); KeBugCheck(MEMORY_MANAGEMENT); } Page = MmGetPfnForProcess(Process, Address); SwapEntry = MmGetSavedSwapEntryPage(Page); /* * Check the reference count to ensure this page can be paged out */ if (MmGetReferenceCountPage(Page) != 1) { DPRINT("Cannot page out locked section page: 0x%lu (RefCount: %lu)\n", Page, MmGetReferenceCountPage(Page)); MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry); MmUnlockSectionSegment(Context.Segment); return STATUS_UNSUCCESSFUL; } /* * Prepare the context structure for the rmap delete call. */ MmUnlockSectionSegment(Context.Segment); Context.WasDirty = FALSE; if (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 VACB. */ if (DirectMapped && !Context.Private) { if(!MiIsPageFromCache(MemoryArea, Context.Offset.QuadPart)) { DPRINT1("Direct mapped non private page is not associated with the cache.\n"); KeBugCheck(MEMORY_MANAGEMENT); } } else { OldIrql = MiAcquirePfnLock(); MmReferencePage(Page); MiReleasePfnLock(OldIrql); } MmDeleteAllRmaps(Page, (PVOID)&Context, MmPageOutDeleteMapping); /* Since we passed in a surrogate, we'll get back the page entry * state in our context. This is intended to make intermediate * decrements of share count not release the wait entry. */ Entry = Context.SectionEntry; /* * If this wasn't a private page then we should have reduced the entry to * zero by deleting all the rmaps. */ if (!Context.Private && Entry != 0) { if (!(Context.Segment->Flags & MM_PAGEFILE_SEGMENT) && !(Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)) { KeBugCheckEx(MEMORY_MANAGEMENT, Entry, (ULONG_PTR)Process, (ULONG_PTR)Address, 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 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 %p) in a pagefile segment.\n", Context.WasDirty ? "dirty" : "clean", Address); KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0); } if (!Context.WasDirty && SwapEntry != 0) { MmSetSavedSwapEntryPage(Page, 0); MmLockSectionSegment(Context.Segment); MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, MAKE_SWAP_SSE(SwapEntry)); MmUnlockSectionSegment(Context.Segment); MmReleasePageMemoryConsumer(MC_USER, Page); MiSetPageEvent(NULL, NULL); return(STATUS_SUCCESS); } } else if (Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED) { if (Context.Private) { DPRINT1("Found a %s private page (address %p) in a shared section segment.\n", Context.WasDirty ? "dirty" : "clean", Address); KeBugCheckEx(MEMORY_MANAGEMENT, Page, (ULONG_PTR)Process, (ULONG_PTR)Address, 0); } if (!Context.WasDirty || SwapEntry != 0) { MmSetSavedSwapEntryPage(Page, 0); if (SwapEntry != 0) { MmLockSectionSegment(Context.Segment); MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, MAKE_SWAP_SSE(SwapEntry)); MmUnlockSectionSegment(Context.Segment); } MmReleasePageMemoryConsumer(MC_USER, Page); MiSetPageEvent(NULL, NULL); return(STATUS_SUCCESS); } } else if (!Context.Private && DirectMapped) { if (SwapEntry != 0) { DPRINT1("Found a swapentry for a non private and direct mapped page (address %p)\n", Address); KeBugCheckEx(MEMORY_MANAGEMENT, STATUS_UNSUCCESSFUL, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address); } #ifndef NEWCC Status = CcRosUnmapVacb(SharedCacheMap, FileOffset, FALSE); #else Status = STATUS_SUCCESS; #endif #ifndef NEWCC if (!NT_SUCCESS(Status)) { DPRINT1("CcRosUnmapVacb failed, status = %x\n", Status); KeBugCheckEx(MEMORY_MANAGEMENT, Status, (ULONG_PTR)SharedCacheMap, (ULONG_PTR)FileOffset, (ULONG_PTR)Address); } #endif MiSetPageEvent(NULL, NULL); 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 %p)\n", Address); KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, Page, (ULONG_PTR)Process, (ULONG_PTR)Address); } MmReleasePageMemoryConsumer(MC_USER, Page); MiSetPageEvent(NULL, NULL); return(STATUS_SUCCESS); } else if (!Context.WasDirty && Context.Private && SwapEntry != 0) { DPRINT("Not dirty and private and not swapped (%p:%p)\n", Process, Address); MmSetSavedSwapEntryPage(Page, 0); MmLockAddressSpace(AddressSpace); Status = MmCreatePageFileMapping(Process, Address, SwapEntry); MmUnlockAddressSpace(AddressSpace); if (!NT_SUCCESS(Status)) { DPRINT1("Status %x Swapping out %p:%p\n", Status, Process, Address); KeBugCheckEx(MEMORY_MANAGEMENT, Status, (ULONG_PTR)Process, (ULONG_PTR)Address, SwapEntry); } MmReleasePageMemoryConsumer(MC_USER, Page); MiSetPageEvent(NULL, NULL); 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 { ULONG_PTR OldEntry; MmLockSectionSegment(Context.Segment); /* * 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); // If we got here, the previous entry should have been a wait Entry = MAKE_SSE(Page << PAGE_SHIFT, 1); OldEntry = MmGetPageEntrySectionSegment(Context.Segment, &Context.Offset); ASSERT(OldEntry == 0 || OldEntry == MAKE_SWAP_SSE(MM_WAIT_ENTRY)); MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry); MmUnlockSectionSegment(Context.Segment); } MmUnlockAddressSpace(AddressSpace); MiSetPageEvent(NULL, NULL); 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 { MmLockSectionSegment(Context.Segment); 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); MmUnlockSectionSegment(Context.Segment); } MmUnlockAddressSpace(AddressSpace); MiSetPageEvent(NULL, NULL); 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->Image.Characteristics & IMAGE_SCN_MEM_SHARED) { MmLockSectionSegment(Context.Segment); MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, MAKE_SWAP_SSE(SwapEntry)); MmUnlockSectionSegment(Context.Segment); } else { MmReleasePageMemoryConsumer(MC_USER, Page); } if (Context.Private) { MmLockAddressSpace(AddressSpace); MmLockSectionSegment(Context.Segment); Status = MmCreatePageFileMapping(Process, Address, SwapEntry); /* We had placed a wait entry upon entry ... replace it before leaving */ MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry); MmUnlockSectionSegment(Context.Segment); MmUnlockAddressSpace(AddressSpace); if (!NT_SUCCESS(Status)) { DPRINT1("Status %x Creating page file mapping for %p:%p\n", Status, Process, Address); KeBugCheckEx(MEMORY_MANAGEMENT, Status, (ULONG_PTR)Process, (ULONG_PTR)Address, SwapEntry); } } else { MmLockAddressSpace(AddressSpace); MmLockSectionSegment(Context.Segment); Entry = MAKE_SWAP_SSE(SwapEntry); /* We had placed a wait entry upon entry ... replace it before leaving */ MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry); MmUnlockSectionSegment(Context.Segment); MmUnlockAddressSpace(AddressSpace); } MiSetPageEvent(NULL, NULL); return(STATUS_SUCCESS); } NTSTATUS NTAPI MmWritePageSectionView(PMMSUPPORT AddressSpace, PMEMORY_AREA MemoryArea, PVOID Address, ULONG PageEntry) { LARGE_INTEGER Offset; PROS_SECTION_OBJECT Section; PMM_SECTION_SEGMENT Segment; PFN_NUMBER Page; SWAPENTRY SwapEntry; ULONG_PTR Entry; BOOLEAN Private; NTSTATUS Status; PFILE_OBJECT FileObject; #ifndef NEWCC PROS_SHARED_CACHE_MAP SharedCacheMap = NULL; #endif BOOLEAN DirectMapped; BOOLEAN IsImageSection; PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace); Address = (PVOID)PAGE_ROUND_DOWN(Address); Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea) + MemoryArea->Data.SectionData.ViewOffset.QuadPart; /* * 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->Image.Characteristics & IMAGE_SCN_MEM_SHARED)) { #ifndef NEWCC SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap; #endif /* * 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.QuadPart + Segment->Image.FileOffset) % PAGE_SIZE) == 0 && (Offset.QuadPart + PAGE_SIZE <= Segment->RawLength.QuadPart || !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 %p " "process %p\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 (%p,0x%p).\n", Process ? Process->UniqueProcessId : 0, Address); KeBugCheck(MEMORY_MANAGEMENT); } Page = MmGetPfnForProcess(Process, Address); SwapEntry = MmGetSavedSwapEntryPage(Page); /* * Check for a private (COWed) page. */ if (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) { //LARGE_INTEGER SOffset; ASSERT(SwapEntry == 0); //SOffset.QuadPart = Offset.QuadPart + Segment->Image.FileOffset; #ifndef NEWCC CcRosMarkDirtyFile(SharedCacheMap, Offset.QuadPart); #endif MmLockSectionSegment(Segment); MmSetPageEntrySectionSegment(Segment, &Offset, PageEntry); MmUnlockSectionSegment(Segment); MiSetPageEvent(NULL, NULL); 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); MiSetPageEvent(NULL, NULL); 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); MiSetPageEvent(NULL, NULL); return(STATUS_UNSUCCESSFUL); } /* * Otherwise we have succeeded. */ DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT); MiSetPageEvent(NULL, NULL); return(STATUS_SUCCESS); } NTSTATUS NTAPI MmProtectSectionView(PMMSUPPORT AddressSpace, PMEMORY_AREA MemoryArea, PVOID BaseAddress, SIZE_T Length, ULONG Protect, PULONG OldProtect) { PMM_REGION Region; NTSTATUS Status; ULONG_PTR MaxLength; MaxLength = MA_GetEndingAddress(MemoryArea) - (ULONG_PTR)BaseAddress; if (Length > MaxLength) Length = (ULONG)MaxLength; Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea), &MemoryArea->Data.SectionData.RegionListHead, BaseAddress, NULL); ASSERT(Region != NULL); if ((MemoryArea->Flags & SEC_NO_CHANGE) && Region->Protect != Protect) { return STATUS_INVALID_PAGE_PROTECTION; } *OldProtect = Region->Protect; Status = MmAlterRegion(AddressSpace, (PVOID)MA_GetStartingAddress(MemoryArea), &MemoryArea->Data.SectionData.RegionListHead, BaseAddress, Length, Region->Type, Protect, MmAlterViewAttributes); return(Status); } NTSTATUS NTAPI MmQuerySectionView(PMEMORY_AREA MemoryArea, PVOID Address, PMEMORY_BASIC_INFORMATION Info, PSIZE_T ResultLength) { PMM_REGION Region; PVOID RegionBaseAddress; PROS_SECTION_OBJECT Section; PMM_SECTION_SEGMENT Segment; Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea), &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)MA_GetStartingAddress(MemoryArea) - Segment->Image.VirtualAddress; Info->Type = MEM_IMAGE; } else { Info->AllocationBase = (PVOID)MA_GetStartingAddress(MemoryArea); 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; LARGE_INTEGER Offset; ULONG_PTR Entry; SWAPENTRY SavedSwapEntry; PFN_NUMBER Page; Page = 0; MmLockSectionSegment(Segment); Length = PAGE_ROUND_UP(Segment->Length.QuadPart); for (Offset.QuadPart = 0; Offset.QuadPart < Length; Offset.QuadPart += PAGE_SIZE) { Entry = MmGetPageEntrySectionSegment(Segment, &Offset); if (Entry) { MmSetPageEntrySectionSegment(Segment, &Offset, 0); 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); } } } MmUnlockSectionSegment(Segment); } VOID NTAPI MmpDeleteSection(PVOID ObjectBody) { PROS_SECTION_OBJECT Section = (PROS_SECTION_OBJECT)ObjectBody; /* Check if it's an ARM3, or ReactOS section */ if (!MiIsRosSectionObject(Section)) { MiDeleteARM3Section(ObjectBody); return; } DPRINT("MmpDeleteSection(ObjectBody %p)\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].Image.Characteristics & IMAGE_SCN_MEM_SHARED) { MmLockSectionSegment(&SectionSegments[i]); } RefCount = InterlockedDecrementUL(&SectionSegments[i].ReferenceCount); if (SectionSegments[i].Image.Characteristics & IMAGE_SCN_MEM_SHARED) { MmUnlockSectionSegment(&SectionSegments[i]); if (RefCount == 0) { MmpFreePageFileSegment(&SectionSegments[i]); } } } } #ifdef NEWCC else if (Section->Segment && Section->Segment->Flags & MM_DATAFILE_SEGMENT) { ULONG RefCount = 0; PMM_SECTION_SEGMENT Segment = Section->Segment; if (Segment && (RefCount = InterlockedDecrementUL(&Segment->ReferenceCount)) == 0) { DPRINT("Freeing section segment\n"); Section->Segment = NULL; MmFinalizeSegment(Segment); } else { DPRINT("RefCount %d\n", RefCount); } } #endif 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, NULL); ExFreePool(Section->Segment); Section->Segment = NULL; } else { (void)InterlockedDecrementUL(&Section->Segment->ReferenceCount); } } if (Section->FileObject != NULL) { #ifndef NEWCC CcRosDereferenceCache(Section->FileObject); #endif 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 %p, HC %lu)\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 | OBJ_KERNEL_EXCLUSIVE, NULL, NULL); Status = MmCreateSection((PVOID)&PhysSection, SECTION_ALL_ACCESS, &Obj, &SectionSize, PAGE_EXECUTE_READWRITE, SEC_PHYSICALMEMORY, 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 based root */ ASSERT(MmSectionBasedRoot.NumberGenericTableElements == 0); MmSectionBasedRoot.BalancedRoot.u1.Parent = &MmSectionBasedRoot.BalancedRoot; /* 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; ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK; 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) { DPRINT1("MmCreatePageFileSection: (UMaximumSize == NULL)\n"); return(STATUS_INVALID_PARAMETER); } 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)) { DPRINT1("MmCreatePageFileSection: failed to create object (0x%lx)\n", Status); return(Status); } /* * Initialize it */ RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT)); Section->Type = 'SC'; Section->Size = 'TN'; 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); } RtlZeroMemory(Segment, sizeof(MM_SECTION_SEGMENT)); Section->Segment = Segment; Segment->ReferenceCount = 1; ExInitializeFastMutex(&Segment->Lock); Segment->Image.FileOffset = 0; Segment->Protection = SectionPageProtection; Segment->RawLength.QuadPart = MaximumSize.u.LowPart; Segment->Length.QuadPart = PAGE_ROUND_UP(MaximumSize.u.LowPart); Segment->Flags = MM_PAGEFILE_SEGMENT; Segment->WriteCopy = FALSE; Segment->Image.VirtualAddress = 0; Segment->Image.Characteristics = 0; *SectionObject = Section; MiInitializeSectionPageTable(Segment); return(STATUS_SUCCESS); } NTSTATUS NTAPI MmCreateDataFileSection(PROS_SECTION_OBJECT *SectionObject, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PLARGE_INTEGER UMaximumSize, ULONG SectionPageProtection, ULONG AllocationAttributes, PFILE_OBJECT FileObject) /* * Create a section backed by a data file */ { PROS_SECTION_OBJECT Section; NTSTATUS Status; LARGE_INTEGER MaximumSize; PMM_SECTION_SEGMENT Segment; FILE_STANDARD_INFORMATION FileInfo; ULONG Length; /* * Create the section */ Status = ObCreateObject(ExGetPreviousMode(), MmSectionObjectType, ObjectAttributes, ExGetPreviousMode(), NULL, sizeof(ROS_SECTION_OBJECT), 0, 0, (PVOID*)&Section); if (!NT_SUCCESS(Status)) { ObDereferenceObject(FileObject); return(Status); } /* * Initialize it */ RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT)); Section->Type = 'SC'; Section->Size = 'TN'; Section->SectionPageProtection = SectionPageProtection; Section->AllocationAttributes = AllocationAttributes; /* * 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); 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_MAPPED_FILE_SIZE_ZERO; } } if (MaximumSize.QuadPart > FileInfo.EndOfFile.QuadPart) { Status = IoSetInformation(FileObject, FileEndOfFileInformation, 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) { ObDereferenceObject(Section); ObDereferenceObject(FileObject); return STATUS_INVALID_FILE_FOR_SECTION; } /* * 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->Image.FileOffset = 0; Segment->Protection = SectionPageProtection; Segment->Flags = MM_DATAFILE_SEGMENT; Segment->Image.Characteristics = 0; Segment->WriteCopy = (SectionPageProtection & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY)); if (AllocationAttributes & SEC_RESERVE) { Segment->Length.QuadPart = Segment->RawLength.QuadPart = 0; } else { Segment->RawLength.QuadPart = MaximumSize.QuadPart; Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart); } Segment->Image.VirtualAddress = 0; Segment->Locked = TRUE; MiInitializeSectionPageTable(Segment); } 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.QuadPart > Segment->RawLength.QuadPart && !(AllocationAttributes & SEC_RESERVE)) { Segment->RawLength.QuadPart = MaximumSize.QuadPart; Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart); } } MmUnlockSectionSegment(Segment); Section->FileObject = FileObject; Section->MaximumSize = MaximumSize; #ifndef NEWCC CcRosReferenceCache(FileObject); #endif //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 ); 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; PFILE_OBJECT FileObject = File; IO_STATUS_BLOCK Iosb; 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); /* Flush data since we're about to perform a non-cached read */ CcFlushCache(FileObject->SectionObjectPointer, &FileOffset, BufferSize, &Iosb); /* * 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) { return STATUS_INSUFFICIENT_RESOURCES; } UsedSize = 0; Status = MiSimpleRead(FileObject, &FileOffset, Buffer, BufferSize, TRUE, &Iosb); UsedSize = (ULONG)Iosb.Information; 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].Image.VirtualAddress >= ImageSectionObject->Segments[i - 1].Image.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.QuadPart > 0); if(i > 0) { ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >= (ImageSectionObject->Segments[i - 1].Image.VirtualAddress + ImageSectionObject->Segments[i - 1].Length.QuadPart)); } } } static VOID NTAPI MmspAssertSegmentsPageAligned(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject) { ULONG i; for( i = 0; i < ImageSectionObject->NrSegments; ++ i ) { ASSERT((ImageSectionObject->Segments[i].Image.VirtualAddress % PAGE_SIZE) == 0); ASSERT((ImageSectionObject->Segments[i].Length.QuadPart % 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->Image.VirtualAddress - Segment2->Image.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.QuadPart == 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].Image.VirtualAddress + ImageSectionObject->Segments[i - 1].Length.QuadPart) != ImageSectionObject->Segments[i].Image.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; PMM_SECTION_SEGMENT EffectiveSegment; if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED) { MmspAssertSegmentsPageAligned(ImageSectionObject); return TRUE; } 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->Image.VirtualAddress; /* Round down the virtual address to the nearest page */ EffectiveSegment->Image.VirtualAddress = PAGE_ROUND_DOWN(VirtualAddress); /* Round up the virtual size to the nearest page */ EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(VirtualAddress + EffectiveSegment->Length.QuadPart) - EffectiveSegment->Image.VirtualAddress; /* Adjust the raw address and size */ VirtualOffset = VirtualAddress - EffectiveSegment->Image.VirtualAddress; if (EffectiveSegment->Image.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->Image.FileOffset -= VirtualOffset; EffectiveSegment->RawLength.QuadPart += VirtualOffset; } else { PMM_SECTION_SEGMENT Segment = &ImageSectionObject->Segments[i]; ULONG_PTR EndOfEffectiveSegment; EndOfEffectiveSegment = (ULONG_PTR)(EffectiveSegment->Image.VirtualAddress + EffectiveSegment->Length.QuadPart); 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->Image.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->Image.VirtualAddress % PAGE_SIZE) == 0); EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(EffectiveSegment->Length.QuadPart); } /* * The current segment is still part of the current effective segment: * extend the effective segment to reflect this */ else if (EndOfEffectiveSegment > Segment->Image.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->Image.FileOffset != (EffectiveSegment->Image.FileOffset + EffectiveSegment->RawLength.QuadPart)) { return FALSE; } EffectiveSegment->RawLength.QuadPart += Segment->RawLength.QuadPart; /* * Extend the virtual size */ ASSERT(PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) >= EndOfEffectiveSegment); EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) - EffectiveSegment->Image.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(PFILE_OBJECT FileObject, 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; Status = ExeFmtpReadFile (FileObject, &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; Status = ExeFmtpLoaders[i](FileHeader, FileHeaderSize, FileObject, 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->ImageInformation.MaximumStackSize == 0) ImageSectionObject->ImageInformation.MaximumStackSize = 0x40000; if(ImageSectionObject->ImageInformation.CommittedStackSize == 0) ImageSectionObject->ImageInformation.CommittedStackSize = 0x1000; if(ImageSectionObject->BasedAddress == NULL) { if(ImageSectionObject->ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL) ImageSectionObject->BasedAddress = (PVOID)0x10000000; else ImageSectionObject->BasedAddress = (PVOID)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; MiInitializeSectionPageTable(&ImageSectionObject->Segments[i]); } 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, PFILE_OBJECT FileObject) { PROS_SECTION_OBJECT Section; NTSTATUS Status; PMM_SECTION_SEGMENT SectionSegments; PMM_IMAGE_SECTION_OBJECT ImageSectionObject; ULONG i; if (FileObject == NULL) return STATUS_INVALID_FILE_FOR_SECTION; #ifndef NEWCC if (FileObject->SectionObjectPointer->SharedCacheMap == NULL) { DPRINT1("Denying section creation due to missing cache initialization\n"); return STATUS_INVALID_FILE_FOR_SECTION; } #endif /* * 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->Type = 'SC'; Section->Size = 'TN'; Section->SectionPageProtection = SectionPageProtection; Section->AllocationAttributes = AllocationAttributes; if (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(FileObject, ImageSectionObject); if (!NT_SUCCESS(StatusExeFmt)) { if(ImageSectionObject->Segments != NULL) ExFreePool(ImageSectionObject->Segments); /* * If image file is empty, then return that the file is invalid for section */ Status = StatusExeFmt; if (StatusExeFmt == STATUS_END_OF_FILE) { Status = STATUS_INVALID_FILE_FOR_SECTION; } ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT); ObDereferenceObject(Section); ObDereferenceObject(FileObject); return(Status); } 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; #ifndef NEWCC CcRosReferenceCache(FileObject); #endif //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE); *SectionObject = Section; 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; ULONG Granularity; if (Segment->WriteCopy) { /* We have to do this because the not present fault * and access fault handlers depend on the protection * that should be granted AFTER the COW fault takes * place to be in Region->Protect. The not present fault * handler changes this to the correct protection for COW when * mapping the pages into the process's address space. If a COW * fault takes place, the access fault handler sets the page protection * to these values for the newly copied pages */ if (Protect == PAGE_WRITECOPY) Protect = PAGE_READWRITE; else if (Protect == PAGE_EXECUTE_WRITECOPY) Protect = PAGE_EXECUTE_READWRITE; } if (*BaseAddress == NULL) Granularity = MM_ALLOCATION_GRANULARITY; else Granularity = PAGE_SIZE; #ifdef NEWCC if (Segment->Flags & MM_DATAFILE_SEGMENT) { LARGE_INTEGER FileOffset; FileOffset.QuadPart = ViewOffset; ObReferenceObject(Section); return _MiMapViewOfSegment(AddressSpace, Segment, BaseAddress, ViewSize, Protect, &FileOffset, AllocationType, __FILE__, __LINE__); } #endif Status = MmCreateMemoryArea(AddressSpace, MEMORY_AREA_SECTION_VIEW, BaseAddress, ViewSize, Protect, &MArea, AllocationType, Granularity); if (!NT_SUCCESS(Status)) { DPRINT1("Mapping between 0x%p and 0x%p 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.QuadPart = ViewOffset; if (Section->AllocationAttributes & SEC_IMAGE) { MArea->VadNode.u.VadFlags.VadType = VadImageMap; } MmInitializeRegion(&MArea->Data.SectionData.RegionListHead, ViewSize, 0, Protect); return(STATUS_SUCCESS); } static VOID MmFreeSectionPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address, PFN_NUMBER Page, SWAPENTRY SwapEntry, BOOLEAN Dirty) { ULONG_PTR Entry; #ifndef NEWCC PFILE_OBJECT FileObject; PROS_SHARED_CACHE_MAP SharedCacheMap; #endif LARGE_INTEGER Offset; SWAPENTRY SavedSwapEntry; 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.QuadPart = ((ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)) + MemoryArea->Data.SectionData.ViewOffset.QuadPart; Section = MemoryArea->Data.SectionData.Section; Segment = MemoryArea->Data.SectionData.Segment; Entry = MmGetPageEntrySectionSegment(Segment, &Offset); while (Entry && MM_IS_WAIT_PTE(Entry)) { MmUnlockSectionSegment(Segment); MmUnlockAddressSpace(AddressSpace); MiWaitForPageEvent(NULL, NULL); MmLockAddressSpace(AddressSpace); MmLockSectionSegment(Segment); 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) { #ifndef NEWCC FileObject = MemoryArea->Data.SectionData.Section->FileObject; SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap; CcRosMarkDirtyFile(SharedCacheMap, Offset.QuadPart + Segment->Image.FileOffset); #endif 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, NULL); } } } 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); } Section = MemoryArea->Data.SectionData.Section; Segment = MemoryArea->Data.SectionData.Segment; #ifdef NEWCC if (Segment->Flags & MM_DATAFILE_SEGMENT) { MmUnlockAddressSpace(AddressSpace); Status = MmUnmapViewOfCacheSegment(AddressSpace, BaseAddress); MmLockAddressSpace(AddressSpace); return Status; } #endif MemoryArea->DeleteInProgress = TRUE; 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); } NTSTATUS NTAPI MiRosUnmapViewOfSection(IN PEPROCESS Process, IN PVOID BaseAddress, IN BOOLEAN SkipDebuggerNotify) { NTSTATUS Status; PMEMORY_AREA MemoryArea; PMMSUPPORT AddressSpace; PROS_SECTION_OBJECT Section; PVOID ImageBaseAddress = 0; DPRINT("Opening memory area Process %p BaseAddress %p\n", Process, BaseAddress); ASSERT(Process); AddressSpace = Process ? &Process->Vm : MmGetKernelAddressSpace(); MmLockAddressSpace(AddressSpace); MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress); if (MemoryArea == NULL || ((MemoryArea->Type != MEMORY_AREA_SECTION_VIEW) && (MemoryArea->Type != MEMORY_AREA_CACHE)) || MemoryArea->DeleteInProgress) { if (MemoryArea) ASSERT(MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3); MmUnlockAddressSpace(AddressSpace); return STATUS_NOT_MAPPED_VIEW; } Section = MemoryArea->Data.SectionData.Section; if ((Section != NULL) && (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; MemoryArea->DeleteInProgress = TRUE; /* Search for the current segment within the section segments * and calculate the image base address */ for (i = 0; i < NrSegments; i++) { if (Segment == &SectionSegments[i]) { ImageBaseAddress = (char*)BaseAddress - (ULONG_PTR)SectionSegments[i].Image.VirtualAddress; break; } } if (i >= NrSegments) { KeBugCheck(MEMORY_MANAGEMENT); } for (i = 0; i < NrSegments; i++) { PVOID SBaseAddress = (PVOID) ((char*)ImageBaseAddress + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress); Status = MmUnmapViewOfSegment(AddressSpace, SBaseAddress); if (!NT_SUCCESS(Status)) { DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n", SBaseAddress, Process, Status); ASSERT(NT_SUCCESS(Status)); } } } else { Status = MmUnmapViewOfSegment(AddressSpace, BaseAddress); if (!NT_SUCCESS(Status)) { DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n", BaseAddress, Process, Status); ASSERT(NT_SUCCESS(Status)); } } MmUnlockAddressSpace(AddressSpace); /* Notify debugger */ if (ImageBaseAddress && !SkipDebuggerNotify) DbgkUnMapViewOfSection(ImageBaseAddress); return(STATUS_SUCCESS); } /** * 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_opt_ PSIZE_T ResultLength) { PSECTION Section; KPROCESSOR_MODE PreviousMode; NTSTATUS Status; PAGED_CODE(); PreviousMode = ExGetPreviousMode(); if (PreviousMode != KernelMode) { _SEH2_TRY { ProbeForWrite(SectionInformation, SectionInformationLength, __alignof(ULONG)); if (ResultLength != NULL) { ProbeForWrite(ResultLength, sizeof(*ResultLength), __alignof(SIZE_T)); } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; } if (SectionInformationClass == SectionBasicInformation) { if (SectionInformationLength < sizeof(SECTION_BASIC_INFORMATION)) { return STATUS_INFO_LENGTH_MISMATCH; } } else if (SectionInformationClass == SectionImageInformation) { if (SectionInformationLength < sizeof(SECTION_IMAGE_INFORMATION)) { return STATUS_INFO_LENGTH_MISMATCH; } } else { return STATUS_INVALID_INFO_CLASS; } Status = ObReferenceObjectByHandle(SectionHandle, SECTION_QUERY, MmSectionObjectType, PreviousMode, (PVOID*)(PVOID)&Section, NULL); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to reference section: 0x%lx\n", Status); return Status; } if (MiIsRosSectionObject(Section)) { PROS_SECTION_OBJECT RosSection = (PROS_SECTION_OBJECT)Section; switch (SectionInformationClass) { case SectionBasicInformation: { PSECTION_BASIC_INFORMATION Sbi = (PSECTION_BASIC_INFORMATION)SectionInformation; _SEH2_TRY { Sbi->Attributes = RosSection->AllocationAttributes; if (RosSection->AllocationAttributes & SEC_IMAGE) { Sbi->BaseAddress = 0; Sbi->Size.QuadPart = 0; } else { Sbi->BaseAddress = (PVOID)RosSection->Segment->Image.VirtualAddress; Sbi->Size.QuadPart = RosSection->Segment->Length.QuadPart; } 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 { if (RosSection->AllocationAttributes & SEC_IMAGE) { PMM_IMAGE_SECTION_OBJECT ImageSectionObject; ImageSectionObject = RosSection->ImageSection; *Sii = ImageSectionObject->ImageInformation; } if (ResultLength != NULL) { *ResultLength = sizeof(SECTION_IMAGE_INFORMATION); } Status = STATUS_SUCCESS; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; break; } } } else { switch(SectionInformationClass) { case SectionBasicInformation: { SECTION_BASIC_INFORMATION Sbi; Sbi.Size = Section->SizeOfSection; Sbi.BaseAddress = (PVOID)Section->Address.StartingVpn; Sbi.Attributes = 0; if (Section->u.Flags.Image) Sbi.Attributes |= SEC_IMAGE; if (Section->u.Flags.Commit) Sbi.Attributes |= SEC_COMMIT; if (Section->u.Flags.Reserve) Sbi.Attributes |= SEC_RESERVE; if (Section->u.Flags.File) Sbi.Attributes |= SEC_FILE; if (Section->u.Flags.Image) Sbi.Attributes |= SEC_IMAGE; /* FIXME : Complete/test the list of flags passed back from NtCreateSection */ _SEH2_TRY { *((SECTION_BASIC_INFORMATION*)SectionInformation) = Sbi; if (ResultLength) *ResultLength = sizeof(Sbi); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; break; } case SectionImageInformation: { if (!Section->u.Flags.Image) { Status = STATUS_SECTION_NOT_IMAGE; } else { /* Currently not supported */ ASSERT(FALSE); } break; } } } ObDereferenceObject(Section); return(Status); } /********************************************************************** * 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; BOOLEAN NotAtBase = FALSE; if (MiIsRosSectionObject(SectionObject) == FALSE) { DPRINT("Mapping ARM3 section into %s\n", Process->ImageFileName); return MmMapViewOfArm3Section(SectionObject, Process, BaseAddress, ZeroBits, CommitSize, SectionOffset, ViewSize, InheritDisposition, AllocationType, Protect); } ASSERT(Process); if (!Protect || Protect & ~PAGE_FLAGS_VALID_FOR_SECTION) { return STATUS_INVALID_PAGE_PROTECTION; } /* FIXME: We should keep this, but it would break code checking equality */ Protect &= ~PAGE_NOCACHE; 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; SIZE_T 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 = (ULONG_PTR)ImageSectionObject->BasedAddress; } ImageSize = 0; for (i = 0; i < NrSegments; i++) { ULONG_PTR MaxExtent; MaxExtent = (ULONG_PTR)(SectionSegments[i].Image.VirtualAddress + SectionSegments[i].Length.QuadPart); ImageSize = max(ImageSize, MaxExtent); } ImageSectionObject->ImageInformation.ImageFileSize = (ULONG)ImageSize; /* Check for an illegal base address */ if (((ImageBase + ImageSize) > (ULONG_PTR)MmHighestUserAddress) || ((ImageBase + ImageSize) < ImageSize)) { ASSERT(*BaseAddress == NULL); ImageBase = ALIGN_DOWN_BY((ULONG_PTR)MmHighestUserAddress - ImageSize, MM_VIRTMEM_GRANULARITY); NotAtBase = TRUE; } else if (ImageBase != ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY)) { ASSERT(*BaseAddress == NULL); ImageBase = ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY); NotAtBase = TRUE; } /* 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_CONFLICTING_ADDRESSES); } /* Otherwise find a gap to map the image. */ ImageBase = (ULONG_PTR)MmFindGap(AddressSpace, PAGE_ROUND_UP(ImageSize), MM_VIRTMEM_GRANULARITY, FALSE); if (ImageBase == 0) { MmUnlockAddressSpace(AddressSpace); return(STATUS_CONFLICTING_ADDRESSES); } /* Remember that we loaded image at a different base address */ NotAtBase = TRUE; } for (i = 0; i < NrSegments; i++) { PVOID SBaseAddress = (PVOID) ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress); MmLockSectionSegment(&SectionSegments[i]); Status = MmMapViewOfSegment(AddressSpace, Section, &SectionSegments[i], &SBaseAddress, SectionSegments[i].Length.LowPart, SectionSegments[i].Protection, 0, 0); MmUnlockSectionSegment(&SectionSegments[i]); if (!NT_SUCCESS(Status)) { MmUnlockAddressSpace(AddressSpace); return(Status); } } *BaseAddress = (PVOID)ImageBase; *ViewSize = ImageSize; } 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 (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); ASSERT(*BaseAddress == ALIGN_DOWN_POINTER_BY(*BaseAddress, MM_VIRTMEM_GRANULARITY)); if (NotAtBase) Status = STATUS_IMAGE_NOT_AT_BASE; else Status = STATUS_SUCCESS; return Status; } /* * @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) { #ifdef NEWCC CC_FILE_SIZES FileSizes; CcpLock(); if (SectionObjectPointer->SharedCacheMap && (Segment->ReferenceCount > CcpCountCacheSections((PNOCC_CACHE_MAP)SectionObjectPointer->SharedCacheMap))) { CcpUnlock(); /* Check size of file */ if (SectionObjectPointer->SharedCacheMap) { if (!CcGetFileSizes(Segment->FileObject, &FileSizes)) { return FALSE; } if (NewFileSize->QuadPart <= FileSizes.FileSize.QuadPart) { return FALSE; } } } else CcpUnlock(); #else /* Check size of file */ if (SectionObjectPointer->SharedCacheMap) { PROS_SHARED_CACHE_MAP SharedCacheMap = SectionObjectPointer->SharedCacheMap; if (NewFileSize->QuadPart <= SharedCacheMap->FileSize.QuadPart) { return FALSE; } } #endif } 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; } /* * @implemented */ BOOLEAN NTAPI MmFlushImageSection (IN PSECTION_OBJECT_POINTERS SectionObjectPointer, IN MMFLUSH_TYPE FlushType) { BOOLEAN Result = TRUE; #ifdef NEWCC PMM_SECTION_SEGMENT Segment; #endif switch(FlushType) { case MmFlushForDelete: if (SectionObjectPointer->ImageSectionObject || SectionObjectPointer->DataSectionObject) { return FALSE; } #ifndef NEWCC CcRosRemoveIfClosed(SectionObjectPointer); #endif return TRUE; case MmFlushForWrite: { DPRINT("MmFlushImageSection(%d)\n", FlushType); #ifdef NEWCC Segment = (PMM_SECTION_SEGMENT)SectionObjectPointer->DataSectionObject; #endif if (SectionObjectPointer->ImageSectionObject) { DPRINT1("SectionObject has ImageSection\n"); return FALSE; } #ifdef NEWCC CcpLock(); Result = !SectionObjectPointer->SharedCacheMap || (Segment->ReferenceCount == CcpCountCacheSections((PNOCC_CACHE_MAP)SectionObjectPointer->SharedCacheMap)); CcpUnlock(); DPRINT("Result %d\n", Result); #endif return Result; } } 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; PAGED_CODE(); if (MiIsRosSectionObject(SectionObject) == FALSE) { return MiMapViewInSystemSpace(SectionObject, &MmSession, MappedBase, ViewSize); } 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; } NTSTATUS NTAPI MiRosUnmapViewInSystemSpace(IN PVOID MappedBase) { PMMSUPPORT AddressSpace; NTSTATUS Status; DPRINT("MmUnmapViewInSystemSpace() called\n"); AddressSpace = MmGetKernelAddressSpace(); MmLockAddressSpace(AddressSpace); Status = MmUnmapViewOfSegment(AddressSpace, MappedBase); MmUnlockAddressSpace(AddressSpace); return Status; } /********************************************************************** * 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 FileObject OPTIONAL) { NTSTATUS Status; ULONG Protection; PROS_SECTION_OBJECT *SectionObject = (PROS_SECTION_OBJECT *)Section; /* Check if an ARM3 section is being created instead */ if (!(AllocationAttributes & (SEC_IMAGE | SEC_PHYSICALMEMORY))) { if (!(FileObject) && !(FileHandle)) { return MmCreateArm3Section(Section, DesiredAccess, ObjectAttributes, MaximumSize, SectionPageProtection, AllocationAttributes &~ 1, FileHandle, FileObject); } } /* Convert section flag to page flag */ if (AllocationAttributes & SEC_NOCACHE) SectionPageProtection |= PAGE_NOCACHE; /* Check to make sure the protection is correct. Nt* does this already */ Protection = MiMakeProtectionMask(SectionPageProtection); if (Protection == MM_INVALID_PROTECTION) { DPRINT1("Page protection is invalid\n"); return STATUS_INVALID_PAGE_PROTECTION; } /* Check if this is going to be a data or image backed file section */ if ((FileHandle) || (FileObject)) { /* These cannot be mapped with large pages */ if (AllocationAttributes & SEC_LARGE_PAGES) { DPRINT1("Large pages cannot be used with an image mapping\n"); return STATUS_INVALID_PARAMETER_6; } /* Did the caller pass an object? */ if (FileObject) { /* Reference the object directly */ ObReferenceObject(FileObject); } else { /* Reference the file handle to get the object */ Status = ObReferenceObjectByHandle(FileHandle, MmMakeFileAccess[Protection], IoFileObjectType, ExGetPreviousMode(), (PVOID*)&FileObject, NULL); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to get a handle to the FO: %lx\n", Status); return Status; } } } else { /* A handle must be supplied with SEC_IMAGE, as this is the no-handle path */ if (AllocationAttributes & SEC_IMAGE) return STATUS_INVALID_FILE_FOR_SECTION; } #ifndef NEWCC // A hack for initializing caching. // This is needed only in the old case. if (FileHandle) { IO_STATUS_BLOCK Iosb; NTSTATUS Status; CHAR Buffer; LARGE_INTEGER ByteOffset; ByteOffset.QuadPart = 0; Status = ZwReadFile(FileHandle, NULL, NULL, NULL, &Iosb, &Buffer, sizeof(Buffer), &ByteOffset, NULL); if (!NT_SUCCESS(Status) && Status != STATUS_END_OF_FILE) { DPRINT1("CC failure: %lx\n", Status); if (FileObject) ObDereferenceObject(FileObject); return Status; } // Caching is initialized... // Hack of the hack: actually, it might not be initialized if FSD init on effective right and if file is null-size // In such case, force cache by initiating a write IRP if (Status == STATUS_END_OF_FILE && !(AllocationAttributes & SEC_IMAGE) && FileObject != NULL && (FileObject->SectionObjectPointer == NULL || FileObject->SectionObjectPointer->SharedCacheMap == NULL)) { Buffer = 0xdb; Status = ZwWriteFile(FileHandle, NULL, NULL, NULL, &Iosb, &Buffer, sizeof(Buffer), &ByteOffset, NULL); if (NT_SUCCESS(Status)) { LARGE_INTEGER Zero; Zero.QuadPart = 0LL; Status = IoSetInformation(FileObject, FileEndOfFileInformation, sizeof(LARGE_INTEGER), &Zero); ASSERT(NT_SUCCESS(Status)); } } } #endif if (AllocationAttributes & SEC_IMAGE) { Status = MmCreateImageSection(SectionObject, DesiredAccess, ObjectAttributes, MaximumSize, SectionPageProtection, AllocationAttributes, FileObject); } #ifndef NEWCC else if (FileHandle != NULL) { Status = MmCreateDataFileSection(SectionObject, DesiredAccess, ObjectAttributes, MaximumSize, SectionPageProtection, AllocationAttributes, FileObject); } #else else if (FileHandle != NULL || FileObject != NULL) { Status = MmCreateCacheSection(SectionObject, DesiredAccess, ObjectAttributes, MaximumSize, SectionPageProtection, AllocationAttributes, FileObject); } #endif else { if ((AllocationAttributes & SEC_PHYSICALMEMORY) == 0) { DPRINT1("Invalid path: %lx %p %p\n", AllocationAttributes, FileObject, FileHandle); } // ASSERT(AllocationAttributes & SEC_PHYSICALMEMORY); Status = MmCreatePageFileSection(SectionObject, DesiredAccess, ObjectAttributes, MaximumSize, SectionPageProtection, AllocationAttributes); if (FileObject) ObDereferenceObject(FileObject); } return Status; } /* EOF */