[NTOS:MM] Finish MmAllocateMappingAddress and MmFreeMappingAddress and fix test failures. (#7491)

* [NTOS:MM] Fix MmAllocateMappingAddress and MmFreeMappingAddress and their regression test failures.
Follow up of #7260.
This fixes kmtest:MmReservedMapping failures and hang.
Based on mm-implement-mappingaddress.patch by Thomas Faber and some changes by Oleg Dubinskiy.
kmtest:MmReservedMapping revisions and updates to Vista+ method by Timo Kreuzer.

Signed-off-by: Oleg Dubinskiy <oleg.dubinskij30@gmail.com>
Signed-off-by: Timo Kreuzer <timo.kreuzer@reactos.org>

CORE-10147, CORE-14635, CORE-17409, CORE-19318
This commit is contained in:
Doug Lyons 2024-11-18 02:44:51 -06:00 committed by GitHub
parent 31334ebcfe
commit e0759a5e35
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 251 additions and 22 deletions

View file

@ -9,6 +9,8 @@
#include <kmt_test.h> #include <kmt_test.h>
static BOOLEAN g_IsPae; static BOOLEAN g_IsPae;
static ULONG g_OsVersion;
static BOOLEAN g_IsReactOS;
#ifdef _M_IX86 #ifdef _M_IX86
@ -76,7 +78,7 @@ ValidateMapping(
BOOLEAN Valid = TRUE; BOOLEAN Valid = TRUE;
#if defined(_M_IX86) || defined(_M_AMD64) #if defined(_M_IX86) || defined(_M_AMD64)
PUCHAR CurrentAddress; PUCHAR CurrentAddress;
ULONGLONG PteValue; ULONGLONG PteValue, ExpectedValue;
ULONG i; ULONG i;
for (i = 0; i < ValidPtes; i++) for (i = 0; i < ValidPtes; i++)
@ -110,10 +112,26 @@ ValidateMapping(
CurrentAddress, PteValue, PoolTag & ~1); CurrentAddress, PteValue, PoolTag & ~1);
CurrentAddress = (PUCHAR)BaseAddress - 2 * PAGE_SIZE; CurrentAddress = (PUCHAR)BaseAddress - 2 * PAGE_SIZE;
PteValue = GET_PTE_VALUE(CurrentAddress); PteValue = GET_PTE_VALUE(CurrentAddress);
if (g_IsReactOS || g_OsVersion >= 0x0600)
{
/* On ReactOS and on Vista+ the size is stored in
* the NextEntry field of a MMPTE_LIST structure */
#ifdef _M_IX86
ExpectedValue = (TotalPtes + 2) << 12;
#elif defined(_M_AMD64)
ExpectedValue = ((ULONG64)TotalPtes + 2) << 32;
#endif
}
else
{
/* On Windows 2003 the size is shifted by 1 bit only */
ExpectedValue = (TotalPtes + 2) * 2;
}
Valid = Valid && Valid = Valid &&
ok(PteValue == (TotalPtes + 2) * 2, ok(PteValue == ExpectedValue,
"PTE for %p contains 0x%I64x, expected %x\n", "PTE for %p contains 0x%I64x, expected %x\n",
CurrentAddress, PteValue, (TotalPtes + 2) * 2); CurrentAddress, PteValue, ExpectedValue);
#endif #endif
return Valid; return Valid;
@ -281,6 +299,9 @@ START_TEST(MmReservedMapping)
PVOID Mapping; PVOID Mapping;
g_IsPae = ExIsProcessorFeaturePresent(PF_PAE_ENABLED); g_IsPae = ExIsProcessorFeaturePresent(PF_PAE_ENABLED);
g_OsVersion = SharedUserData->NtMajorVersion << 8 | SharedUserData->NtMinorVersion;
g_IsReactOS = *(PULONG)(KI_USER_SHARED_DATA + PAGE_SIZE - sizeof(ULONG)) == 0x8eac705;
ok(g_IsReactOS == 1, "Not reactos\n");
pMmAllocatePagesForMdlEx = KmtGetSystemRoutineAddress(L"MmAllocatePagesForMdlEx"); pMmAllocatePagesForMdlEx = KmtGetSystemRoutineAddress(L"MmAllocatePagesForMdlEx");

View file

@ -1330,6 +1330,9 @@ ExpInitializeExecutive(IN ULONG Cpu,
/* Set the machine type */ /* Set the machine type */
SharedUserData->ImageNumberLow = IMAGE_FILE_MACHINE_NATIVE; SharedUserData->ImageNumberLow = IMAGE_FILE_MACHINE_NATIVE;
SharedUserData->ImageNumberHigh = IMAGE_FILE_MACHINE_NATIVE; SharedUserData->ImageNumberHigh = IMAGE_FILE_MACHINE_NATIVE;
/* ReactOS magic */
*(PULONG)(KI_USER_SHARED_DATA + PAGE_SIZE - sizeof(ULONG)) = 0x8eac705;
} }
VOID VOID

View file

@ -529,7 +529,7 @@ MmAllocatePagesForMdlEx(IN PHYSICAL_ADDRESS LowAddress,
else else
{ {
// //
// Conver to internal caching attribute // Convert to internal caching attribute
// //
CacheAttribute = MiPlatformCacheAttributes[FALSE][CacheType]; CacheAttribute = MiPlatformCacheAttributes[FALSE][CacheType];
} }
@ -1622,29 +1622,224 @@ MmAdvanceMdl(IN PMDL Mdl,
} }
/* /*
* @unimplemented * @implemented
*/ */
PVOID PVOID
NTAPI NTAPI
MmMapLockedPagesWithReservedMapping(IN PVOID MappingAddress, MmMapLockedPagesWithReservedMapping(
IN ULONG PoolTag, _In_ PVOID MappingAddress,
IN PMDL MemoryDescriptorList, _In_ ULONG PoolTag,
IN MEMORY_CACHING_TYPE CacheType) _In_ PMDL Mdl,
_In_ MEMORY_CACHING_TYPE CacheType)
{ {
UNIMPLEMENTED; PPFN_NUMBER MdlPages, LastPage;
return 0; PFN_COUNT PageCount;
BOOLEAN IsIoMapping;
MI_PFN_CACHE_ATTRIBUTE CacheAttribute;
PMMPTE PointerPte;
MMPTE TempPte;
ASSERT(Mdl->ByteCount != 0);
// Get the list of pages and count
MdlPages = MmGetMdlPfnArray(Mdl);
PageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(Mdl),
Mdl->ByteCount);
LastPage = MdlPages + PageCount;
// Sanity checks
ASSERT((Mdl->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA |
MDL_SOURCE_IS_NONPAGED_POOL |
MDL_PARTIAL_HAS_BEEN_MAPPED)) == 0);
ASSERT((Mdl->MdlFlags & (MDL_PAGES_LOCKED | MDL_PARTIAL)) != 0);
// Get the correct cache type
IsIoMapping = (Mdl->MdlFlags & MDL_IO_SPACE) != 0;
CacheAttribute = MiPlatformCacheAttributes[IsIoMapping][CacheType];
// Get the first PTE we reserved
ASSERT(MappingAddress);
PointerPte = MiAddressToPte(MappingAddress) - 2;
ASSERT(!PointerPte[0].u.Hard.Valid &&
!PointerPte[1].u.Hard.Valid);
// Verify that the pool tag matches
TempPte.u.Long = PoolTag;
TempPte.u.Hard.Valid = 0;
if (PointerPte[1].u.Long != TempPte.u.Long)
{
KeBugCheckEx(SYSTEM_PTE_MISUSE,
PTE_MAPPING_ADDRESS_NOT_OWNED, /* Trying to map an address it does not own */
(ULONG_PTR)MappingAddress,
PoolTag,
PointerPte[1].u.Long);
}
// We must have a size, and our helper PTEs must be invalid
if (PointerPte[0].u.List.NextEntry < 3)
{
KeBugCheckEx(SYSTEM_PTE_MISUSE,
PTE_MAPPING_ADDRESS_INVALID, /* Trying to map an invalid address */
(ULONG_PTR)MappingAddress,
PoolTag,
(ULONG_PTR)_ReturnAddress());
}
// If the mapping isn't big enough, fail
if (PointerPte[0].u.List.NextEntry - 2 < PageCount)
{
DPRINT1("Reserved mapping too small. Need %Iu pages, have %Iu\n",
PageCount,
PointerPte[0].u.List.NextEntry - 2);
return NULL;
}
// Skip our two helper PTEs
PointerPte += 2;
// Get the template
TempPte = ValidKernelPte;
switch (CacheAttribute)
{
case MiNonCached:
// Disable caching
MI_PAGE_DISABLE_CACHE(&TempPte);
MI_PAGE_WRITE_THROUGH(&TempPte);
break;
case MiWriteCombined:
// Enable write combining
MI_PAGE_DISABLE_CACHE(&TempPte);
MI_PAGE_WRITE_COMBINED(&TempPte);
break;
default:
// Nothing to do
break;
}
// Loop all PTEs
for (; (MdlPages < LastPage) && (*MdlPages != LIST_HEAD); ++MdlPages)
{
// Write the PTE
TempPte.u.Hard.PageFrameNumber = *MdlPages;
MI_WRITE_VALID_PTE(PointerPte++, TempPte);
}
// Mark it as mapped
ASSERT((Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) == 0);
Mdl->MappedSystemVa = MappingAddress;
Mdl->MdlFlags |= MDL_MAPPED_TO_SYSTEM_VA;
// Check if it was partial
if (Mdl->MdlFlags & MDL_PARTIAL)
{
// Write the appropriate flag here too
Mdl->MdlFlags |= MDL_PARTIAL_HAS_BEEN_MAPPED;
}
// Return the mapped address
return (PVOID)((ULONG_PTR)MappingAddress + Mdl->ByteOffset);
} }
/* /*
* @unimplemented * @implemented
*/ */
VOID VOID
NTAPI NTAPI
MmUnmapReservedMapping(IN PVOID BaseAddress, MmUnmapReservedMapping(
IN ULONG PoolTag, _In_ PVOID BaseAddress,
IN PMDL MemoryDescriptorList) _In_ ULONG PoolTag,
_In_ PMDL Mdl)
{ {
UNIMPLEMENTED; PVOID Base;
PFN_COUNT PageCount, ExtraPageCount;
PPFN_NUMBER MdlPages;
PMMPTE PointerPte;
MMPTE TempPte;
// Sanity check
ASSERT(Mdl->ByteCount != 0);
ASSERT(BaseAddress > MM_HIGHEST_USER_ADDRESS);
// Get base and count information
Base = (PVOID)((ULONG_PTR)Mdl->StartVa + Mdl->ByteOffset);
PageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(Base, Mdl->ByteCount);
// Sanity checks
ASSERT((Mdl->MdlFlags & MDL_PARENT_MAPPED_SYSTEM_VA) == 0);
ASSERT(PageCount != 0);
ASSERT(Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA);
// Get the first PTE we reserved
PointerPte = MiAddressToPte(BaseAddress) - 2;
ASSERT(!PointerPte[0].u.Hard.Valid &&
!PointerPte[1].u.Hard.Valid);
// Verify that the pool tag matches
TempPte.u.Long = PoolTag;
TempPte.u.Hard.Valid = 0;
if (PointerPte[1].u.Long != TempPte.u.Long)
{
KeBugCheckEx(SYSTEM_PTE_MISUSE,
PTE_UNMAPPING_ADDRESS_NOT_OWNED, /* Trying to unmap an address it does not own */
(ULONG_PTR)BaseAddress,
PoolTag,
PointerPte[1].u.Long);
}
// We must have a size
if (PointerPte[0].u.List.NextEntry < 3)
{
KeBugCheckEx(SYSTEM_PTE_MISUSE,
PTE_MAPPING_ADDRESS_EMPTY, /* Mapping apparently empty */
(ULONG_PTR)BaseAddress,
PoolTag,
(ULONG_PTR)_ReturnAddress());
}
// Skip our two helper PTEs
PointerPte += 2;
// This should be a resident system PTE
ASSERT(PointerPte >= MmSystemPtesStart[SystemPteSpace]);
ASSERT(PointerPte <= MmSystemPtesEnd[SystemPteSpace]);
ASSERT(PointerPte->u.Hard.Valid == 1);
// TODO: check the MDL range makes sense with regard to the mapping range
// TODO: check if any of them are already zero
// TODO: check if any outside the MDL range are nonzero
// TODO: find out what to do with extra PTEs
// Check if the caller wants us to free advanced pages
if (Mdl->MdlFlags & MDL_FREE_EXTRA_PTES)
{
// Get the MDL page array
MdlPages = MmGetMdlPfnArray(Mdl);
/* Number of extra pages stored after the PFN array */
ExtraPageCount = MdlPages[PageCount];
// Do the math
PageCount += ExtraPageCount;
PointerPte -= ExtraPageCount;
ASSERT(PointerPte >= MmSystemPtesStart[SystemPteSpace]);
ASSERT(PointerPte <= MmSystemPtesEnd[SystemPteSpace]);
// Get the new base address
BaseAddress = (PVOID)((ULONG_PTR)BaseAddress -
(ExtraPageCount << PAGE_SHIFT));
}
// Zero the PTEs
RtlZeroMemory(PointerPte, PageCount * sizeof(MMPTE));
// Flush the TLB
KeFlushEntireTb(TRUE, TRUE);
// Remove flags
Mdl->MdlFlags &= ~(MDL_MAPPED_TO_SYSTEM_VA |
MDL_PARTIAL_HAS_BEEN_MAPPED |
MDL_FREE_EXTRA_PTES);
} }
/* /*

View file

@ -156,11 +156,17 @@ C_ASSERT(SYSTEM_PD_SIZE == PAGE_SIZE);
// //
// Some internal SYSTEM_PTE_MISUSE bugcheck subcodes // Some internal SYSTEM_PTE_MISUSE bugcheck subcodes
// These names were created by Oleg Dubinskiy and Doug Lyons for ReactOS. For reference, see
// https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/bug-check-0xda--system-pte-misuse
// //
#define PTE_MAPPING_NONE 0x100 #define PTE_MAPPING_NONE 0x100
#define PTE_MAPPING_NOT_OWNED 0x101 #define PTE_MAPPING_NOT_OWNED 0x101
#define PTE_MAPPING_EMPTY 0x102 #define PTE_MAPPING_EMPTY 0x102
#define PTE_MAPPING_RESERVED 0x103 #define PTE_MAPPING_RESERVED 0x103
#define PTE_MAPPING_ADDRESS_NOT_OWNED 0x104
#define PTE_MAPPING_ADDRESS_INVALID 0x105
#define PTE_UNMAPPING_ADDRESS_NOT_OWNED 0x108
#define PTE_MAPPING_ADDRESS_EMPTY 0x109
// //
// Mask for image section page protection // Mask for image section page protection
@ -1002,7 +1008,6 @@ MI_WRITE_INVALID_PTE(IN PMMPTE PointerPte,
{ {
/* Write the invalid PTE */ /* Write the invalid PTE */
ASSERT(InvalidPte.u.Hard.Valid == 0); ASSERT(InvalidPte.u.Hard.Valid == 0);
ASSERT(InvalidPte.u.Long != 0);
*PointerPte = InvalidPte; *PointerPte = InvalidPte;
} }

View file

@ -1580,6 +1580,10 @@ MmAllocateMappingAddress(
PMMPTE PointerPte; PMMPTE PointerPte;
MMPTE TempPte; MMPTE TempPte;
/* Fast exit if PoolTag is NULL */
if (!PoolTag)
return NULL;
/* How many PTEs does the caller want? */ /* How many PTEs does the caller want? */
SizeInPages = BYTES_TO_PAGES(NumberOfBytes); SizeInPages = BYTES_TO_PAGES(NumberOfBytes);
if (SizeInPages == 0) if (SizeInPages == 0)

View file

@ -192,8 +192,9 @@ MiAllocatePagesForMdl(IN PHYSICAL_ADDRESS LowAddress,
KIRQL OldIrql; KIRQL OldIrql;
PMMPFN Pfn1; PMMPFN Pfn1;
INT LookForZeroedPages; INT LookForZeroedPages;
ASSERT(KeGetCurrentIrql() <= APC_LEVEL); ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
DPRINT1("ARM3-DEBUG: Being called with %I64x %I64x %I64x %lx %d %lu\n", LowAddress, HighAddress, SkipBytes, TotalBytes, CacheAttribute, MdlFlags); DPRINT("ARM3-DEBUG: Being called with %I64x %I64x %I64x %lx %d %lu\n", LowAddress, HighAddress, SkipBytes, TotalBytes, CacheAttribute, MdlFlags);
// //
// Convert the low address into a PFN // Convert the low address into a PFN