1 /*
2  * PROJECT:     ReactOS kernel-mode tests
3  * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4  * PURPOSE:     Kernel-Mode Test Suite Reserved Mapping test
5  * COPYRIGHT:   Copyright 2015,2023 Thomas Faber (thomas.faber@reactos.org)
6  * COPYRIGHT:   Copyright 2015 Pierre Schweitzer (pierre@reactos.org)
7  */
8 
9 #include <kmt_test.h>
10 
11 static BOOLEAN g_IsPae;
12 static ULONG g_OsVersion;
13 static BOOLEAN g_IsReactOS;
14 
15 #ifdef _M_IX86
16 
17 #define IS_PAE() (g_IsPae)
18 
19 #define PTE_BASE    0xC0000000
20 
21 #define MiAddressToPteX86(x) \
22     ((PULONG)(((((ULONG)(x)) >> 12) << 2) + PTE_BASE))
23 #define MiAddressToPtePAE(x) \
24     ((PULONGLONG)(((((ULONG)(x)) >> 12) << 3) + PTE_BASE))
25 
26 #define GET_PTE_VALUE_X86(Addr) (*MiAddressToPteX86(Addr))
27 #define GET_PTE_VALUE_PAE(Addr) (*MiAddressToPtePAE(Addr))
28 #define GET_PTE_VALUE(Addr) (IS_PAE() ? GET_PTE_VALUE_PAE(Addr) : GET_PTE_VALUE_X86(Addr))
29 
30 #define PTE_IS_VALID(PteValue) ((PteValue) & 1)
31 
32 #define PTE_GET_PFN_X86(PteValue) (((PteValue) >> PAGE_SHIFT) & 0x0fffffULL)
33 #define PTE_GET_PFN_PAE(PteValue) (((PteValue) >> PAGE_SHIFT) & 0xffffffULL)
34 #define PTE_GET_PFN(PteValue) (IS_PAE() ? PTE_GET_PFN_PAE(PteValue) : PTE_GET_PFN_X86(PteValue))
35 
36 #elif defined(_M_AMD64)
37 
38 #define PTI_SHIFT  12L
39 #define PTE_BASE    0xFFFFF68000000000ULL
40 PULONGLONG
41 FORCEINLINE
_MiAddressToPte(PVOID Address)42 _MiAddressToPte(PVOID Address)
43 {
44     ULONG64 Offset = (ULONG64)Address >> (PTI_SHIFT - 3);
45     Offset &= 0xFFFFFFFFFULL << 3;
46     return (PULONGLONG)(PTE_BASE + Offset);
47 }
48 #define MiAddressToPte(x) _MiAddressToPte((PVOID)(x))
49 
50 #define GET_PTE_VALUE(Addr) (*_MiAddressToPte((PVOID)(Addr)))
51 #define PTE_IS_VALID(PteValue) ((PteValue) & 1)
52 #define PTE_GET_PFN(PteValue) (((PteValue) >> PAGE_SHIFT) & 0xFffffffffULL)
53 
54 #endif
55 
56 static
57 _Must_inspect_result_
58 _IRQL_requires_max_ (DISPATCH_LEVEL)
59 PMDL
60 (NTAPI
61 *pMmAllocatePagesForMdlEx)(
62     _In_ PHYSICAL_ADDRESS LowAddress,
63     _In_ PHYSICAL_ADDRESS HighAddress,
64     _In_ PHYSICAL_ADDRESS SkipBytes,
65     _In_ SIZE_T TotalBytes,
66     _In_ MEMORY_CACHING_TYPE CacheType,
67     _In_ ULONG Flags);
68 
69 static
70 BOOLEAN
ValidateMapping(_In_ PVOID BaseAddress,_In_ ULONG TotalPtes,_In_ ULONG PoolTag,_In_ ULONG ValidPtes,_In_ PPFN_NUMBER Pfns)71 ValidateMapping(
72     _In_ PVOID BaseAddress,
73     _In_ ULONG TotalPtes,
74     _In_ ULONG PoolTag,
75     _In_ ULONG ValidPtes,
76     _In_ PPFN_NUMBER Pfns)
77 {
78     BOOLEAN Valid = TRUE;
79 #if defined(_M_IX86) || defined(_M_AMD64)
80     PUCHAR CurrentAddress;
81     ULONGLONG PteValue, ExpectedValue;
82     ULONG i;
83 
84     for (i = 0; i < ValidPtes; i++)
85     {
86         CurrentAddress = (PUCHAR)BaseAddress + i * PAGE_SIZE;
87         PteValue = GET_PTE_VALUE(CurrentAddress);
88         Valid = Valid &&
89                 ok(PTE_IS_VALID(PteValue),
90                    "[%lu] PTE for %p is not valid (0x%I64x)\n",
91                    i, CurrentAddress, PteValue);
92 
93         Valid = Valid &&
94                 ok(PTE_GET_PFN(PteValue) == Pfns[i],
95                    "[%lu] PTE for %p has PFN %Ix, expected %Ix\n",
96                    i, CurrentAddress, PTE_GET_PFN(PteValue), Pfns[i]);
97     }
98     for (; i < TotalPtes; i++)
99     {
100         CurrentAddress = (PUCHAR)BaseAddress + i * PAGE_SIZE;
101         PteValue = GET_PTE_VALUE(CurrentAddress);
102         Valid = Valid &&
103                 ok(PteValue == 0,
104                    "[%lu] PTE for %p is nonzero (0x%I64x)\n",
105                    i, CurrentAddress, PteValue);
106     }
107     CurrentAddress = (PUCHAR)BaseAddress - 1 * PAGE_SIZE;
108     PteValue = GET_PTE_VALUE(CurrentAddress);
109     Valid = Valid &&
110             ok(PteValue == (PoolTag & ~1ULL),
111                "PTE for %p contains 0x%I64x, expected %x\n",
112                CurrentAddress, PteValue, PoolTag & ~1);
113     CurrentAddress = (PUCHAR)BaseAddress - 2 * PAGE_SIZE;
114     PteValue = GET_PTE_VALUE(CurrentAddress);
115 
116     if (g_IsReactOS || g_OsVersion >= 0x0600)
117     {
118         /* On ReactOS and on Vista+ the size is stored in
119          * the NextEntry field of a MMPTE_LIST structure */
120 #ifdef _M_IX86
121         ExpectedValue = (TotalPtes + 2) << 12;
122 #elif defined(_M_AMD64)
123         ExpectedValue = ((ULONG64)TotalPtes + 2) << 32;
124 #endif
125     }
126     else
127     {
128         /* On Windows 2003 the size is shifted by 1 bit only */
129         ExpectedValue = (TotalPtes + 2) * 2;
130     }
131     Valid = Valid &&
132             ok(PteValue == ExpectedValue,
133                "PTE for %p contains 0x%I64x, expected %x\n",
134                CurrentAddress, PteValue, ExpectedValue);
135 #endif
136 
137     return Valid;
138 }
139 
140 static
141 VOID
TestMap(_In_ PVOID Mapping,_In_ ULONG TotalPtes,_In_ ULONG PoolTag)142 TestMap(
143     _In_ PVOID Mapping,
144     _In_ ULONG TotalPtes,
145     _In_ ULONG PoolTag)
146 {
147     PMDL Mdl;
148     PHYSICAL_ADDRESS ZeroPhysical;
149     PHYSICAL_ADDRESS MaxPhysical;
150     PVOID BaseAddress;
151     PPFN_NUMBER MdlPages;
152     ULONG i;
153 
154     if (skip(pMmAllocatePagesForMdlEx != NULL, "MmAllocatePagesForMdlEx unavailable\n"))
155     {
156         return;
157     }
158 
159     ZeroPhysical.QuadPart = 0;
160     MaxPhysical.QuadPart = 0xffffffffffffffffLL;
161 
162     /* Create a one-page MDL and map it */
163     Mdl = pMmAllocatePagesForMdlEx(ZeroPhysical,
164                                    MaxPhysical,
165                                    ZeroPhysical,
166                                    PAGE_SIZE,
167                                    MmCached,
168                                    0);
169     if (skip(Mdl != NULL, "No MDL\n"))
170     {
171         return;
172     }
173 
174     MdlPages = (PVOID)(Mdl + 1);
175 
176     BaseAddress = MmMapLockedPagesWithReservedMapping(Mapping,
177                                                       PoolTag,
178                                                       Mdl,
179                                                       MmCached);
180     ok(BaseAddress != NULL, "MmMapLockedPagesWithReservedMapping failed\n");
181     if (!skip(BaseAddress != NULL, "Failed to map MDL\n"))
182     {
183         ok_eq_pointer(BaseAddress, Mapping);
184 
185         ok_bool_true(ValidateMapping(BaseAddress, TotalPtes, PoolTag, 1, MdlPages),
186                      "ValidateMapping returned");
187 
188         KmtStartSeh()
189             *(volatile ULONG *)BaseAddress = 0x01234567;
190         KmtEndSeh(STATUS_SUCCESS);
191 
192         MmUnmapReservedMapping(BaseAddress,
193                                PoolTag,
194                                Mdl);
195 
196         ok_bool_true(ValidateMapping(Mapping, TotalPtes, PoolTag, 0, NULL),
197                      "ValidateMapping returned");
198     }
199 
200     /* Try again but at an unaligned address */
201     BaseAddress = MmMapLockedPagesWithReservedMapping((PUCHAR)Mapping + sizeof(ULONG),
202                                                       PoolTag,
203                                                       Mdl,
204                                                       MmCached);
205     ok(BaseAddress != NULL, "MmMapLockedPagesWithReservedMapping failed\n");
206     if (!skip(BaseAddress != NULL, "Failed to map MDL\n"))
207     {
208         ok_eq_pointer(BaseAddress, (PUCHAR)Mapping + sizeof(ULONG));
209 
210         ok_bool_true(ValidateMapping(BaseAddress, TotalPtes, PoolTag, 1, MdlPages),
211                      "ValidateMapping returned");
212 
213         KmtStartSeh()
214             *(volatile ULONG *)BaseAddress = 0x01234567;
215         KmtEndSeh(STATUS_SUCCESS);
216 
217         MmUnmapReservedMapping(BaseAddress,
218                                PoolTag,
219                                Mdl);
220 
221         ok_bool_true(ValidateMapping(Mapping, TotalPtes, PoolTag, 0, NULL),
222                      "ValidateMapping returned");
223     }
224 
225     MmFreePagesFromMdl(Mdl);
226 
227     /* Map all pages */
228     Mdl = pMmAllocatePagesForMdlEx(ZeroPhysical,
229                                    MaxPhysical,
230                                    ZeroPhysical,
231                                    TotalPtes * PAGE_SIZE,
232                                    MmCached,
233                                    0);
234     if (skip(Mdl != NULL, "No MDL\n"))
235     {
236         return;
237     }
238 
239     MdlPages = (PVOID)(Mdl + 1);
240 
241     BaseAddress = MmMapLockedPagesWithReservedMapping(Mapping,
242                                                       PoolTag,
243                                                       Mdl,
244                                                       MmCached);
245     ok(BaseAddress != NULL, "MmMapLockedPagesWithReservedMapping failed\n");
246     if (!skip(BaseAddress != NULL, "Failed to map MDL\n"))
247     {
248         ok_eq_pointer(BaseAddress, Mapping);
249 
250         ok_bool_true(ValidateMapping(BaseAddress, TotalPtes, PoolTag, TotalPtes, MdlPages),
251                      "ValidateMapping returned");
252 
253         for (i = 0; i < TotalPtes; i++)
254         {
255             KmtStartSeh()
256                 *((volatile ULONG *)BaseAddress + i * PAGE_SIZE / sizeof(ULONG)) = 0x01234567;
257             KmtEndSeh(STATUS_SUCCESS);
258         }
259 
260         MmUnmapReservedMapping(BaseAddress,
261                                PoolTag,
262                                Mdl);
263 
264         ok_bool_true(ValidateMapping(Mapping, TotalPtes, PoolTag, 0, NULL),
265                      "ValidateMapping returned");
266     }
267 
268     MmFreePagesFromMdl(Mdl);
269 
270     /* Try to map more pages than we reserved */
271     Mdl = pMmAllocatePagesForMdlEx(ZeroPhysical,
272                                    MaxPhysical,
273                                    ZeroPhysical,
274                                    (TotalPtes + 1) * PAGE_SIZE,
275                                    MmCached,
276                                    0);
277     if (skip(Mdl != NULL, "No MDL\n"))
278     {
279         return;
280     }
281 
282     BaseAddress = MmMapLockedPagesWithReservedMapping(Mapping,
283                                                       PoolTag,
284                                                       Mdl,
285                                                       MmCached);
286     ok_eq_pointer(BaseAddress, NULL);
287     if (BaseAddress)
288     {
289         MmUnmapReservedMapping(BaseAddress,
290                                PoolTag,
291                                Mdl);
292     }
293 
294     MmFreePagesFromMdl(Mdl);
295 }
296 
START_TEST(MmReservedMapping)297 START_TEST(MmReservedMapping)
298 {
299     PVOID Mapping;
300 
301     g_IsPae = ExIsProcessorFeaturePresent(PF_PAE_ENABLED);
302     g_OsVersion = SharedUserData->NtMajorVersion << 8 | SharedUserData->NtMinorVersion;
303     g_IsReactOS = *(PULONG)(KI_USER_SHARED_DATA + PAGE_SIZE - sizeof(ULONG)) == 0x8eac705;
304     ok(g_IsReactOS == 1, "Not reactos\n");
305 
306     pMmAllocatePagesForMdlEx = KmtGetSystemRoutineAddress(L"MmAllocatePagesForMdlEx");
307 
308     /* one byte - single page */
309     Mapping = MmAllocateMappingAddress(1, 'MRmK');
310     ok(Mapping != NULL, "MmAllocateMappingAddress failed\n");
311     if (!skip(Mapping != NULL, "No mapping\n"))
312     {
313         ok_bool_true(ValidateMapping(Mapping, 1, 'MRmK', 0, NULL),
314                      "ValidateMapping returned");
315 
316         MmFreeMappingAddress(Mapping, 'MRmK');
317     }
318 
319     /* 10 pages */
320     Mapping = MmAllocateMappingAddress(10 * PAGE_SIZE, 'MRmK' & ~1);
321     ok(Mapping != NULL, "MmAllocateMappingAddress failed\n");
322     if (!skip(Mapping != NULL, "No mapping\n"))
323     {
324         ok_bool_true(ValidateMapping(Mapping, 10, 'MRmK', 0, NULL),
325                      "ValidateMapping returned");
326 
327         /* PAGE_FAULT_IN_NONPAGED_AREA can't be caught with SEH */
328         if (0)
329         {
330             (void)*(volatile UCHAR *)Mapping;
331         }
332 
333         TestMap(Mapping, 10, 'MRmK');
334 
335         MmFreeMappingAddress(Mapping, 'MRmK');
336     }
337 
338     /* PoolTag = 0 */
339     Mapping = MmAllocateMappingAddress(1, 0);
340     ok(Mapping == NULL, "MmAllocateMappingAddress failed\n");
341     if (Mapping != NULL)
342     {
343         MmFreeMappingAddress(Mapping, 0);
344     }
345 
346     /* PoolTag = 1 */
347     Mapping = MmAllocateMappingAddress(1, 1);
348     ok(Mapping != NULL, "MmAllocateMappingAddress failed\n");
349     if (Mapping != NULL)
350     {
351         ok_bool_true(ValidateMapping(Mapping, 1, 1, 0, NULL),
352                      "ValidateMapping returned");
353 
354         TestMap(Mapping, 1, 1);
355 
356         MmFreeMappingAddress(Mapping, 1);
357     }
358 
359     /* Free an unaligned address */
360     Mapping = MmAllocateMappingAddress(PAGE_SIZE, 'MRmK');
361     ok(Mapping != NULL, "MmAllocateMappingAddress failed\n");
362     if (Mapping != NULL)
363     {
364         ok_bool_true(ValidateMapping(Mapping, 1, 'MRmK', 0, NULL),
365                      "ValidateMapping returned");
366 
367         TestMap(Mapping, 1, 'MRmK');
368 
369         MmFreeMappingAddress((PUCHAR)Mapping + sizeof(ULONG), 'MRmK');
370     }
371 }
372