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
_MiAddressToPte(PVOID Address)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
ValidateMapping(_In_ PVOID BaseAddress,_In_ ULONG TotalPtes,_In_ ULONG PoolTag,_In_ ULONG ValidPtes,_In_ PPFN_NUMBER Pfns)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
TestMap(_In_ PVOID Mapping,_In_ ULONG TotalPtes,_In_ ULONG PoolTag)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
START_TEST(MmReservedMapping)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