Author: sir_richard
Date: Wed Apr 21 16:06:01 2010
New Revision: 46977
URL:
http://svn.reactos.org/svn/reactos?rev=46977&view=rev
Log:
[NTOS]: Enable MmPageEntireDriver by implementing MiSetPagingOfDriver.
[NTOS]: Call MiEnablePagingOfDriver from MmLoadSystemImage and implement it. All the work
is done other than actually enabling paging, which requires system working set support.
[NTOS]: Implement MiWriteProtectSystemImage and MiComputeDriverProtection. All the work is
done other than actually setting the bits on the pages, since I wanted to avoid too many
changes.
[NTOS]: MmCheckSystemImage returns STATUS_INVALID_IMAGE_PROTECT, not
STATUS_INVALID_IMAGE_FORMAT, so the branch in MmLoadSystemImage needs to check for the
correct status code.
[NTOS]: Support FLG_SHOW_LDR_SNAPS for the kernel loader.
Modified:
trunk/reactos/ntoskrnl/mm/sysldr.c
Modified: trunk/reactos/ntoskrnl/mm/sysldr.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/mm/sysldr.c?rev=4…
==============================================================================
--- trunk/reactos/ntoskrnl/mm/sysldr.c [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/mm/sysldr.c [iso-8859-1] Wed Apr 21 16:06:01 2010
@@ -39,10 +39,10 @@
ULONG_PTR PsNtosImageBase;
KMUTANT MmSystemLoadLock;
+PFN_NUMBER MmTotalSystemDriverPages;
+
PVOID MmUnloadedDrivers;
PVOID MmLastUnloadedDrivers;
-PVOID MmTriageActionTaken;
-PVOID KernelVerifier;
BOOLEAN MmMakeLowMemory;
BOOLEAN MmEnforceWriteProtection = TRUE;
@@ -1468,6 +1468,374 @@
return TRUE;
}
+ULONG
+NTAPI
+MiComputeDriverProtection(IN BOOLEAN SessionSpace,
+ IN ULONG SectionProtection)
+{
+ ULONG Protection = MM_ZERO_ACCESS;
+
+ /* Check if the caller gave anything */
+ if (SectionProtection)
+ {
+ /* Always turn on execute access */
+ SectionProtection |= IMAGE_SCN_MEM_EXECUTE;
+
+ /* Check if the registry setting is on or not */
+ if (!MmEnforceWriteProtection)
+ {
+ /* Turn on write access too */
+ SectionProtection |= (IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE);
+ }
+ }
+
+ /* Convert to internal PTE flags */
+ if (SectionProtection & IMAGE_SCN_MEM_EXECUTE) Protection |= MM_EXECUTE;
+ if (SectionProtection & IMAGE_SCN_MEM_READ) Protection |= MM_READONLY;
+
+ /* Check for write access */
+ if (SectionProtection & IMAGE_SCN_MEM_WRITE)
+ {
+ /* Session space is not supported */
+ if (SessionSpace)
+ {
+ DPRINT1("Session drivers not supported\n");
+ ASSERT(SessionSpace == FALSE);
+ }
+ else
+ {
+ /* Convert to internal PTE flag */
+ Protection = (Protection & MM_EXECUTE) ? MM_EXECUTE_READWRITE :
MM_READWRITE;
+ }
+ }
+
+ /* If there's no access at all by now, convert to internal no access flag */
+ if (Protection == MM_ZERO_ACCESS) Protection = MM_NOACCESS;
+
+ /* Return the computed PTE protection */
+ return Protection;
+}
+
+VOID
+NTAPI
+MiSetSystemCodeProtection(IN PMMPTE FirstPte,
+ IN PMMPTE LastPte,
+ IN ULONG ProtectionMask)
+{
+ /* I'm afraid to introduce regressions at the moment... */
+ return;
+}
+
+VOID
+NTAPI
+MiWriteProtectSystemImage(IN PVOID ImageBase)
+{
+ PIMAGE_NT_HEADERS NtHeaders;
+ PIMAGE_SECTION_HEADER Section;
+ PFN_NUMBER DriverPages;
+ ULONG CurrentProtection, SectionProtection, CombinedProtection, ProtectionMask;
+ ULONG Sections, Size;
+ ULONG_PTR BaseAddress, CurrentAddress;
+ PMMPTE PointerPte, StartPte, LastPte, CurrentPte, ComboPte = NULL;
+ ULONG CurrentMask, CombinedMask = 0;
+ PAGED_CODE();
+
+ /* No need to write protect physical memory-backed drivers (large pages) */
+ if (MI_IS_PHYSICAL_ADDRESS(ImageBase)) return;
+
+ /* Get the image headers */
+ NtHeaders = RtlImageNtHeader(ImageBase);
+ if (!NtHeaders) return;
+
+ /* Check if this is a session driver or not */
+ if (!MI_IS_SESSION_ADDRESS(ImageBase))
+ {
+ /* Don't touch NT4 drivers */
+ if (NtHeaders->OptionalHeader.MajorOperatingSystemVersion < 5) return;
+ if (NtHeaders->OptionalHeader.MajorImageVersion < 5) return;
+ }
+ else
+ {
+ /* Not supported */
+ DPRINT1("Session drivers not supported\n");
+ ASSERT(FALSE);
+ }
+
+ /* These are the only protection masks we care about */
+ ProtectionMask = IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE;
+
+ /* Calculate the number of pages this driver is occupying */
+ DriverPages = BYTES_TO_PAGES(NtHeaders->OptionalHeader.SizeOfImage);
+
+ /* Get the number of sections and the first section header */
+ Sections = NtHeaders->FileHeader.NumberOfSections;
+ ASSERT(Sections != 0);
+ Section = IMAGE_FIRST_SECTION(NtHeaders);
+
+ /* Loop all the sections */
+ CurrentAddress = (ULONG_PTR)ImageBase;
+ while (Sections)
+ {
+ /* Get the section size */
+ Size = max(Section->SizeOfRawData, Section->Misc.VirtualSize);
+
+ /* Get its virtual address */
+ BaseAddress = (ULONG_PTR)ImageBase + Section->VirtualAddress;
+ if (BaseAddress < CurrentAddress)
+ {
+ /* Windows doesn't like these */
+ DPRINT1("Badly linked image!\n");
+ return;
+ }
+
+ /* Remember the current address */
+ CurrentAddress = BaseAddress + Size - 1;
+
+ /* Next */
+ Sections--;
+ Section++;
+ }
+
+ /* Get the number of sections and the first section header */
+ Sections = NtHeaders->FileHeader.NumberOfSections;
+ ASSERT(Sections != 0);
+ Section = IMAGE_FIRST_SECTION(NtHeaders);
+
+ /* Set the address at the end to initialize the loop */
+ CurrentAddress = (ULONG_PTR)Section + Sections - 1;
+ CurrentProtection = IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ;
+
+ /* Set the PTE points for the image, and loop its sections */
+ StartPte = MiAddressToPte(ImageBase);
+ LastPte = StartPte + DriverPages;
+ while (Sections)
+ {
+ /* Get the section size */
+ Size = max(Section->SizeOfRawData, Section->Misc.VirtualSize);
+
+ /* Get its virtual address and PTE */
+ BaseAddress = (ULONG_PTR)ImageBase + Section->VirtualAddress;
+ PointerPte = MiAddressToPte(BaseAddress);
+
+ /* Check if we were already protecting a run, and found a new run */
+ if ((ComboPte) && (PointerPte > ComboPte))
+ {
+ /* Compute protection */
+ CombinedMask = MiComputeDriverProtection(FALSE, CombinedProtection);
+
+ /* Set it */
+ MiSetSystemCodeProtection(ComboPte, ComboPte, CombinedMask);
+
+ /* Check for overlap */
+ if (ComboPte == StartPte) StartPte++;
+
+ /* One done, reset variables */
+ ComboPte = NULL;
+ CombinedProtection = 0;
+ }
+
+ /* Break out when needed */
+ if (PointerPte >= LastPte) break;
+
+ /* Get the requested protection from the image header */
+ SectionProtection = Section->Characteristics & ProtectionMask;
+ if (SectionProtection == CurrentProtection)
+ {
+ /* Same protection, so merge the request */
+ CurrentAddress = BaseAddress + Size - 1;
+ continue;
+ }
+
+ /* This is now a new section, so close up the old one */
+ CurrentPte = MiAddressToPte(CurrentAddress);
+
+ /* Check for overlap */
+ if (CurrentPte == PointerPte)
+ {
+ /* Skip the last PTE, since it overlaps with us */
+ CurrentPte--;
+
+ /* And set the PTE we will merge with */
+ ASSERT((ComboPte == NULL) || (ComboPte == PointerPte));
+ ComboPte = PointerPte;
+
+ /* Get the most flexible protection by merging both */
+ CombinedMask |= (SectionProtection | CurrentProtection);
+ }
+
+ /* Loop any PTEs left */
+ if (CurrentPte >= StartPte)
+ {
+ /* Sanity check */
+ ASSERT(StartPte < LastPte);
+
+ /* Make sure we don't overflow past the last PTE in the driver */
+ if (CurrentPte >= LastPte) CurrentPte = LastPte - 1;
+ ASSERT(CurrentPte >= StartPte);
+
+ /* Compute the protection and set it */
+ CurrentMask = MiComputeDriverProtection(FALSE, CurrentProtection);
+ MiSetSystemCodeProtection(StartPte, CurrentPte, CurrentMask);
+ }
+
+ /* Set new state */
+ StartPte = PointerPte;
+ CurrentAddress = BaseAddress + Size - 1;
+ CurrentProtection = SectionProtection;
+
+ /* Next */
+ Sections--;
+ Section++;
+ }
+
+ /* Is there a leftover section to merge? */
+ if (ComboPte)
+ {
+ /* Compute and set the protection */
+ CombinedMask = MiComputeDriverProtection(FALSE, CombinedProtection);
+ MiSetSystemCodeProtection(ComboPte, ComboPte, CombinedMask);
+
+ /* Handle overlap */
+ if (ComboPte == StartPte) StartPte++;
+ }
+
+ /* Finally, handle the last section */
+ CurrentPte = MiPteToAddress(CurrentAddress);
+ if ((StartPte < LastPte) && (CurrentPte >= StartPte))
+ {
+ /* Handle overlap */
+ if (CurrentPte >= LastPte) CurrentPte = LastPte - 1;
+ ASSERT(CurrentPte >= StartPte);
+
+ /* Compute and set the protection */
+ CurrentMask = MiComputeDriverProtection(FALSE, CurrentProtection);
+ MiSetSystemCodeProtection(StartPte, CurrentPte, CurrentMask);
+ }
+}
+
+VOID
+NTAPI
+MiSetPagingOfDriver(IN PMMPTE PointerPte,
+ IN PMMPTE LastPte)
+{
+ PVOID ImageBase;
+ PETHREAD CurrentThread;
+ PFN_NUMBER PageCount = 0, PageFrameIndex;
+ PMMPFN Pfn1;
+ PAGED_CODE();
+
+ /* Get the driver's base address */
+ ImageBase = MiPteToAddress(PointerPte);
+ ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(ImageBase) == FALSE);
+
+ /* If this is a large page, it's stuck in physical memory */
+ if (MI_IS_PHYSICAL_ADDRESS(ImageBase)) return;
+
+ /* We should lock the system working set -- we don't have one yet, so just be
consistent */
+ CurrentThread = PsGetCurrentThread();
+ KeEnterGuardedRegion();
+ ASSERT((CurrentThread->OwnsSystemWorkingSetExclusive == 0) &&
+ (CurrentThread->OwnsSystemWorkingSetShared == 0));
+ CurrentThread->OwnsSystemWorkingSetExclusive = 1;
+
+ /* Loop the PTEs */
+ while (PointerPte <= LastPte)
+ {
+ /* Check for valid PTE */
+ if (PointerPte->u.Hard.Valid == 1)
+ {
+ PageFrameIndex = PFN_FROM_PTE(PointerPte);
+ Pfn1 = MiGetPfnEntry(PageFrameIndex);
+ ASSERT(Pfn1->u2.ShareCount == 1);
+
+ /* No working sets in ReactOS yet */
+ PageCount++;
+ }
+
+ ImageBase = (PVOID)((ULONG_PTR)ImageBase + PAGE_SIZE);
+ PointerPte++;
+ }
+
+ /* Release the working set "lock" */
+ ASSERT(KeAreAllApcsDisabled() == TRUE);
+ CurrentThread->OwnsSystemWorkingSetExclusive = 0;
+ KeLeaveGuardedRegion();
+
+ /* Do we have any driver pages? */
+ if (PageCount)
+ {
+ /* Update counters */
+ InterlockedExchangeAdd((PLONG)&MmTotalSystemDriverPages, PageCount);
+ }
+}
+
+VOID
+NTAPI
+MiEnablePagingOfDriver(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
+{
+ ULONG_PTR ImageBase;
+ PIMAGE_NT_HEADERS NtHeaders;
+ ULONG Sections, Alignment, Size;
+ PIMAGE_SECTION_HEADER Section;
+ PMMPTE PointerPte = NULL, LastPte = NULL;
+ if (MmDisablePagingExecutive) return;
+
+ /* Get the driver base address and its NT header */
+ ImageBase = (ULONG_PTR)LdrEntry->DllBase;
+ NtHeaders = RtlImageNtHeader((PVOID)ImageBase);
+ if (!NtHeaders) return;
+
+ /* Get the sections and their alignment */
+ Sections = NtHeaders->FileHeader.NumberOfSections;
+ Alignment = NtHeaders->OptionalHeader.SectionAlignment - 1;
+
+ /* Loop each section */
+ Section = IMAGE_FIRST_SECTION(NtHeaders);
+ while (Sections)
+ {
+ /* Find PAGE or .edata */
+ if ((*(PULONG)Section->Name == 'EGAP') ||
+ (*(PULONG)Section->Name == 'ade.'))
+ {
+ /* Had we already done some work? */
+ if (!PointerPte)
+ {
+ /* Nope, setup the first PTE address */
+ PointerPte = MiAddressToPte(ROUND_TO_PAGES(ImageBase +
+ Section->
+ VirtualAddress));
+ }
+
+ /* Compute the size */
+ Size = max(Section->SizeOfRawData, Section->Misc.VirtualSize);
+
+ /* Find the last PTE that maps this section */
+ LastPte = MiAddressToPte(ImageBase +
+ Section->VirtualAddress +
+ Alignment +
+ Size -
+ PAGE_SIZE);
+ }
+ else
+ {
+ /* Had we found a section before? */
+ if (PointerPte)
+ {
+ /* Mark it as pageable */
+ MiSetPagingOfDriver(PointerPte, LastPte);
+ PointerPte = NULL;
+ }
+ }
+
+ /* Keep searching */
+ Sections--;
+ Section++;
+ }
+
+ /* Handle the straggler */
+ if (PointerPte) MiSetPagingOfDriver(PointerPte, LastPte);
+}
+
BOOLEAN
NTAPI
MmVerifyImageIsOkForMpUse(IN PVOID BaseAddress)
@@ -1590,7 +1958,7 @@
UNICODE_STRING BaseName, BaseDirectory, PrefixName, UnicodeTemp;
PLDR_DATA_TABLE_ENTRY LdrEntry = NULL;
ULONG EntrySize, DriverSize;
- PLOAD_IMPORTS LoadedImports = (PVOID)-2;
+ PLOAD_IMPORTS LoadedImports = MM_SYSLDR_NO_IMPORTS;
PCHAR MissingApiName, Buffer;
PWCHAR MissingDriverName;
HANDLE SectionHandle;
@@ -1614,7 +1982,7 @@
}
/* Allocate a buffer we'll use for names */
- Buffer = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH, TAG_LDR_WSTR);
+ Buffer = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH, 'nLmM');
if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES;
/* Check for a separator */
@@ -1658,6 +2026,14 @@
/* Check if we already have a name, use it instead */
if (LoadedName) BaseName = *LoadedName;
+
+ /* Check for loader snap debugging */
+ if (NtGlobalFlag & FLG_SHOW_LDR_SNAPS)
+ {
+ /* Print out standard string */
+ DPRINT1("MM:SYSLDR Loading %wZ (%wZ) %s\n",
+ &PrefixName, &BaseName, Flags ? "in session space" :
"");
+ }
/* Acquire the load lock */
LoaderScan:
@@ -1753,7 +2129,7 @@
Status = MmCheckSystemImage(FileHandle, FALSE);
if ((Status == STATUS_IMAGE_CHECKSUM_MISMATCH) ||
(Status == STATUS_IMAGE_MP_UP_MISMATCH) ||
- (Status == STATUS_INVALID_IMAGE_FORMAT))
+ (Status == STATUS_INVALID_IMAGE_PROTECT))
{
/* Fail loading */
goto Quickie;
@@ -1832,16 +2208,22 @@
/* Check for success */
if (NT_SUCCESS(Status))
{
- /* FIXME: Support large pages for drivers */
+ #if 0
+ /* Support large pages for drivers */
+ MiUseLargeDriverPage(DriverSize / PAGE_SIZE,
+ &ModuleLoadBase,
+ &BaseName,
+ TRUE);
+ #endif
}
/* Dereference the section */
ObDereferenceObject(Section);
Section = NULL;
}
-
- /* Get the NT Header */
- NtHeader = RtlImageNtHeader(ModuleLoadBase);
+
+ /* Check for failure of the load earlier */
+ if (!NT_SUCCESS(Status)) goto Quickie;
/* Relocate the driver */
Status = LdrRelocateImageWithBias(ModuleLoadBase,
@@ -1851,6 +2233,10 @@
STATUS_CONFLICTING_ADDRESSES,
STATUS_INVALID_IMAGE_FORMAT);
if (!NT_SUCCESS(Status)) goto Quickie;
+
+
+ /* Get the NT Header */
+ NtHeader = RtlImageNtHeader(ModuleLoadBase);
/* Calculate the size we'll need for the entry and allocate it */
EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
@@ -1959,9 +2345,10 @@
LdrEntry->Flags &= ~LDRP_LOAD_IN_PROGRESS;
LdrEntry->LoadedImports = LoadedImports;
- /* FIXME: Apply driver verifier */
-
- /* FIXME: Write-protect the system image */
+ /* FIXME: Call driver verifier's loader function */
+
+ /* Write-protect the system image */
+ MiWriteProtectSystemImage(LdrEntry->DllBase);
/* Check if notifications are enabled */
if (PsImageNotifyEnabled)
@@ -2015,17 +2402,15 @@
LdrEntry->Flags |= LDRP_DEBUG_SYMBOLS_LOADED;
}
- /* FIXME: Page the driver */
+ /* Page the driver */
ASSERT(Section == NULL);
+ MiEnablePagingOfDriver(LdrEntry);
/* Return pointers */
*ModuleObject = LdrEntry;
*ImageBaseAddress = LdrEntry->DllBase;
Quickie:
- /* If we have a file handle, close it */
- if (FileHandle) ZwClose(FileHandle);
-
/* Check if we have the lock acquired */
if (LockOwned)
{
@@ -2035,6 +2420,9 @@
LockOwned = FALSE;
}
+ /* If we have a file handle, close it */
+ if (FileHandle) ZwClose(FileHandle);
+
/* Check if we had a prefix */
if (NamePrefix) ExFreePool(PrefixName.Buffer);
@@ -2077,6 +2465,8 @@
/* Return the entry */
return FoundEntry;
}
+
+/* PUBLIC FUNCTIONS ***********************************************************/
/*
* @implemented
@@ -2106,11 +2496,11 @@
/* Get the PTE range for the whole driver image */
StartPte = MiAddressToPte((ULONG_PTR)LdrEntry->DllBase);
EndPte = MiAddressToPte((ULONG_PTR)LdrEntry->DllBase + LdrEntry->SizeOfImage);
-#if 0
+
/* Enable paging for the PTE range */
ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(AddressWithinSection) == FALSE);
MiSetPagingOfDriver(StartPte, EndPte);
-#endif
+
/* Return the base address */
return LdrEntry->DllBase;
}
@@ -2124,6 +2514,7 @@
{
UNIMPLEMENTED;
}
+
/*
* @implemented
*/