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