1 /*
2 * PROJECT: ReactOS API Tests
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Tests for System Firmware functions
5 * COPYRIGHT: Copyright 2018 Stanislav Motylkov
6 * Copyright 2018 Mark Jansen
7 */
8
9 #include "precomp.h"
10
11 #include <pseh/pseh2.h>
12
13 static UINT (WINAPI * pEnumSystemFirmwareTables)(DWORD, PVOID, DWORD);
14 static UINT (WINAPI * pGetSystemFirmwareTable)(DWORD, DWORD, PVOID, DWORD);
15
16 typedef struct ENTRY
17 {
18 DWORD Signature;
19 DWORD ErrInsuff;
20 DWORD ErrSuccess;
21 } ENTRY;
22
23 static UINT
CallNt(IN DWORD FirmwareTableProviderSignature,IN DWORD FirmwareTableID,OUT PVOID pFirmwareTableBuffer,IN DWORD BufferSize,IN SYSTEM_FIRMWARE_TABLE_ACTION Action)24 CallNt(IN DWORD FirmwareTableProviderSignature,
25 IN DWORD FirmwareTableID,
26 OUT PVOID pFirmwareTableBuffer,
27 IN DWORD BufferSize,
28 IN SYSTEM_FIRMWARE_TABLE_ACTION Action)
29 {
30 SYSTEM_FIRMWARE_TABLE_INFORMATION* SysFirmwareInfo;
31 ULONG Result = 0, ReturnedSize;
32 ULONG TotalSize = BufferSize + sizeof(SYSTEM_FIRMWARE_TABLE_INFORMATION);
33 NTSTATUS Status;
34
35 SysFirmwareInfo = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, TotalSize);
36 if (!SysFirmwareInfo)
37 {
38 SetLastError(ERROR_INVALID_PARAMETER);
39 return 0;
40 }
41 _SEH2_TRY
42 {
43 SysFirmwareInfo->ProviderSignature = FirmwareTableProviderSignature;
44 SysFirmwareInfo->TableID = FirmwareTableID;
45 SysFirmwareInfo->Action = Action;
46 SysFirmwareInfo->TableBufferLength = BufferSize;
47
48 Status = NtQuerySystemInformation(SystemFirmwareTableInformation, SysFirmwareInfo, TotalSize, &ReturnedSize);
49
50 if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_TOO_SMALL)
51 Result = SysFirmwareInfo->TableBufferLength;
52
53 if (NT_SUCCESS(Status) && pFirmwareTableBuffer)
54 {
55 memcpy(pFirmwareTableBuffer, SysFirmwareInfo->TableBuffer, SysFirmwareInfo->TableBufferLength);
56 }
57 }
58 _SEH2_FINALLY
59 {
60 RtlFreeHeap(RtlGetProcessHeap(), 0, SysFirmwareInfo);
61 }
62 _SEH2_END;
63
64 SetLastError(RtlNtStatusToDosError(Status));
65 return Result;
66 }
67
68 UINT
69 WINAPI
fEnumSystemFirmwareTables(IN DWORD FirmwareTableProviderSignature,OUT PVOID pFirmwareTableBuffer,IN DWORD BufferSize)70 fEnumSystemFirmwareTables(IN DWORD FirmwareTableProviderSignature,
71 OUT PVOID pFirmwareTableBuffer,
72 IN DWORD BufferSize)
73 {
74 return CallNt(FirmwareTableProviderSignature, 0, pFirmwareTableBuffer, BufferSize, SystemFirmwareTable_Enumerate);
75 }
76
77 UINT
78 WINAPI
fGetSystemFirmwareTable(IN DWORD FirmwareTableProviderSignature,IN DWORD FirmwareTableID,OUT PVOID pFirmwareTableBuffer,IN DWORD BufferSize)79 fGetSystemFirmwareTable(IN DWORD FirmwareTableProviderSignature,
80 IN DWORD FirmwareTableID,
81 OUT PVOID pFirmwareTableBuffer,
82 IN DWORD BufferSize)
83 {
84 return CallNt(FirmwareTableProviderSignature, FirmwareTableID, pFirmwareTableBuffer, BufferSize, SystemFirmwareTable_Get);
85 }
86
87 static
88 VOID
test_EnumBuffer(DWORD Signature,PVOID Buffer,DWORD dwSize,UINT * pTableCount,DWORD * pFirstTableID,DWORD ErrInsuff,DWORD ErrSuccess)89 test_EnumBuffer(
90 DWORD Signature,
91 PVOID Buffer,
92 DWORD dwSize,
93 UINT * pTableCount,
94 DWORD * pFirstTableID,
95 DWORD ErrInsuff,
96 DWORD ErrSuccess
97 )
98 {
99 DWORD dwError;
100 DWORD dwBufferSize;
101 DWORD dwException;
102 UINT uResultSize;
103
104 dwException = Buffer && IsBadWritePtr(Buffer, dwSize) ? STATUS_ACCESS_VIOLATION : STATUS_SUCCESS;
105
106 // Test size = 0
107 if (Buffer && dwException == STATUS_SUCCESS)
108 {
109 FillMemory(Buffer, dwSize, 0xFF);
110 }
111 SetLastError(0xbeeffeed);
112 dwError = GetLastError();
113 dwBufferSize = 0;
114 uResultSize = 0;
115 StartSeh()
116 uResultSize = pEnumSystemFirmwareTables(Signature, Buffer, dwBufferSize);
117 dwError = GetLastError();
118 EndSeh(STATUS_SUCCESS);
119
120 if (uResultSize > 0)
121 {
122 ok(dwError == ErrInsuff,
123 "GetLastError() returned %ld, expected %ld\n",
124 dwError, ErrInsuff);
125 }
126 else
127 {
128 ok(dwError == ErrSuccess,
129 "GetLastError() returned %ld, expected %ld\n",
130 dwError, ErrSuccess);
131 }
132 if (ErrSuccess == ERROR_SUCCESS)
133 {
134 ok(uResultSize % sizeof(DWORD) == 0,
135 "uResultSize is %u, expected %% sizeof(DWORD)\n",
136 uResultSize);
137 }
138 else
139 {
140 ok(uResultSize == 0,
141 "uResultSize is %u, expected == 0\n",
142 uResultSize);
143 }
144 if (Buffer && dwException == STATUS_SUCCESS)
145 {
146 ok(*(BYTE *)Buffer == 0xFF,
147 "Buffer should be clean at offset 0, got %x\n",
148 *(BYTE *)Buffer);
149 }
150
151 // Test size = 2
152 if (Buffer && dwException == STATUS_SUCCESS)
153 {
154 FillMemory(Buffer, dwSize, 0xFF);
155 }
156 SetLastError(0xbeeffeed);
157 dwError = GetLastError();
158 dwBufferSize = 2;
159 uResultSize = 0;
160 StartSeh()
161 uResultSize = pEnumSystemFirmwareTables(Signature, Buffer, dwBufferSize);
162 dwError = GetLastError();
163 EndSeh(STATUS_SUCCESS);
164
165 if (uResultSize > 0)
166 {
167 ok(dwError == ErrInsuff,
168 "GetLastError() returned %ld, expected %ld\n",
169 dwError, ErrInsuff);
170 }
171 else
172 {
173 ok(dwError == ErrSuccess,
174 "GetLastError() returned %ld, expected %ld\n",
175 dwError, ErrSuccess);
176 }
177 if (ErrSuccess == ERROR_SUCCESS)
178 {
179 ok(uResultSize % sizeof(DWORD) == 0,
180 "uResultSize is %u, expected %% sizeof(DWORD)\n",
181 uResultSize);
182 }
183 else
184 {
185 ok(uResultSize == 0,
186 "uResultSize is %u, expected == 0\n",
187 uResultSize);
188 }
189 if (Buffer && dwException == STATUS_SUCCESS)
190 {
191 ok(*(WORD *)Buffer == 0xFFFF,
192 "Buffer should be clean at offset 0, got %x\n",
193 *(WORD *)Buffer);
194 }
195
196 // Test full size
197 if (Buffer && dwException == STATUS_SUCCESS)
198 {
199 FillMemory(Buffer, dwSize, 0xFF);
200 }
201 if (uResultSize > 0)
202 {
203 SetLastError(0xbeeffeed);
204 dwError = GetLastError();
205 dwBufferSize = uResultSize;
206 uResultSize = 0;
207 StartSeh()
208 uResultSize = pEnumSystemFirmwareTables(Signature, Buffer, dwBufferSize);
209 dwError = GetLastError();
210 EndSeh(ErrSuccess == ERROR_SUCCESS ? dwException : STATUS_SUCCESS);
211 // Windows 7: does not throw exception here
212
213 if (dwException == STATUS_SUCCESS || ErrSuccess == ERROR_INVALID_FUNCTION)
214 {
215 ok(dwError == ErrSuccess,
216 "GetLastError() returned %ld, expected %ld\n",
217 dwError, ErrSuccess);
218 if (ErrSuccess == ERROR_SUCCESS)
219 {
220 ok(uResultSize == dwBufferSize,
221 "uResultSize is not equal dwBufferSize, expected %ld\n",
222 dwBufferSize);
223 }
224 else
225 {
226 ok(uResultSize == 0,
227 "uResultSize is %u, expected == 0\n",
228 uResultSize);
229 }
230 }
231 else
232 {
233 // Windows 7: returns ERROR_NOACCESS here
234 ok(dwError == 0xbeeffeed,
235 "GetLastError() returned %ld, expected %u\n",
236 dwError, 0xbeeffeed);
237 // Windows 7: returns correct size here
238 ok(uResultSize == 0,
239 "uResultSize is %u, expected == 0\n",
240 uResultSize);
241 }
242 }
243
244 if (pTableCount && pFirstTableID)
245 {
246 if (uResultSize > 0)
247 {
248 if (Signature == 'RSMB')
249 {
250 // Raw SMBIOS have only one table with ID 0
251 ok(*(DWORD *)Buffer == 0,
252 "Buffer should be filled at offset 0, got %lx\n",
253 *(DWORD *)Buffer);
254 }
255 else
256 {
257 // In other cases ID can be different
258 if (ErrSuccess == ERROR_SUCCESS)
259 {
260 ok(*(DWORD *)Buffer != 0xFFFFFFFF,
261 "Buffer should be filled at offset 0\n");
262 }
263 else
264 {
265 ok(*(DWORD *)Buffer == 0xFFFFFFFF,
266 "Buffer should be clean at offset 0\n");
267 }
268 }
269 }
270 *pTableCount = uResultSize / sizeof(DWORD);
271 *pFirstTableID = *(DWORD *)Buffer;
272 }
273 }
274
275 static
276 VOID
test_GetBuffer(DWORD Signature,DWORD TableID,PVOID Buffer,DWORD dwSize,BOOL TestFakeID,DWORD ErrInsuff,DWORD ErrSuccess)277 test_GetBuffer(
278 DWORD Signature,
279 DWORD TableID,
280 PVOID Buffer,
281 DWORD dwSize,
282 BOOL TestFakeID,
283 DWORD ErrInsuff,
284 DWORD ErrSuccess
285 )
286 {
287 DWORD dwError;
288 DWORD dwBufferSize;
289 DWORD dwException;
290 DWORD dwErrCase;
291 UINT uResultSize;
292
293 dwException = Buffer && IsBadWritePtr(Buffer, dwSize) ? STATUS_ACCESS_VIOLATION : STATUS_SUCCESS;
294 switch (Signature)
295 {
296 case 'ACPI':
297 {
298 dwErrCase = ERROR_NOT_FOUND;
299 break;
300 }
301 case 'FIRM':
302 {
303 dwErrCase = ERROR_INVALID_PARAMETER;
304 break;
305 }
306 default:
307 {
308 dwErrCase = ErrInsuff;
309 }
310 }
311
312 // Test size = 0
313 if (Buffer && dwException == STATUS_SUCCESS)
314 {
315 FillMemory(Buffer, dwSize, 0xFF);
316 }
317 SetLastError(0xbeeffeed);
318 dwError = GetLastError();
319 dwBufferSize = 0;
320 uResultSize = 0;
321 StartSeh()
322 uResultSize = pGetSystemFirmwareTable(Signature, TableID, Buffer, dwBufferSize);
323 dwError = GetLastError();
324 EndSeh(STATUS_SUCCESS);
325
326 ok(dwError == (TestFakeID ? dwErrCase : ErrInsuff),
327 "GetLastError() returned %ld, expected %ld\n",
328 dwError, (TestFakeID ? dwErrCase : ErrInsuff));
329 if (ErrSuccess == ERROR_SUCCESS && (!TestFakeID || dwErrCase == ErrInsuff))
330 {
331 ok(uResultSize > 0,
332 "uResultSize is %u, expected > 0\n",
333 uResultSize);
334 }
335 else
336 {
337 ok(uResultSize == 0,
338 "uResultSize is %u, expected == 0\n",
339 uResultSize);
340 }
341 if (Buffer && dwException == STATUS_SUCCESS)
342 {
343 ok(*(BYTE *)Buffer == 0xFF,
344 "Buffer should be clean at offset 0, got %x\n",
345 *(BYTE *)Buffer);
346 }
347
348 // Test size = 2
349 if (Buffer && dwException == STATUS_SUCCESS)
350 {
351 FillMemory(Buffer, dwSize, 0xFF);
352 }
353 SetLastError(0xbeeffeed);
354 dwError = GetLastError();
355 dwBufferSize = 2;
356 uResultSize = 0;
357 StartSeh()
358 uResultSize = pGetSystemFirmwareTable(Signature, TableID, Buffer, dwBufferSize);
359 dwError = GetLastError();
360 EndSeh(STATUS_SUCCESS);
361
362 ok(dwError == (TestFakeID ? dwErrCase : ErrInsuff),
363 "GetLastError() returned %ld, expected %ld\n",
364 dwError, (TestFakeID ? dwErrCase : ErrInsuff));
365 if (ErrSuccess == ERROR_SUCCESS && (!TestFakeID || dwErrCase == ErrInsuff))
366 {
367 ok(uResultSize > 0,
368 "uResultSize is %u, expected > 0\n",
369 uResultSize);
370 }
371 else
372 {
373 ok(uResultSize == 0,
374 "uResultSize is %u, expected == 0\n",
375 uResultSize);
376 }
377 if (Buffer && dwException == STATUS_SUCCESS)
378 {
379 ok(*(WORD *)Buffer == 0xFFFF,
380 "Buffer should be clean at offset 0, got %x\n",
381 *(WORD *)Buffer);
382 }
383
384 // Test full size
385 if (Buffer && dwException == STATUS_SUCCESS)
386 {
387 FillMemory(Buffer, dwSize, 0xFF);
388 }
389 if (uResultSize == 0)
390 {
391 return;
392 }
393 SetLastError(0xbeeffeed);
394 dwError = GetLastError();
395 dwBufferSize = uResultSize;
396 uResultSize = 0;
397 StartSeh()
398 uResultSize = pGetSystemFirmwareTable(Signature, TableID, Buffer, dwBufferSize);
399 dwError = GetLastError();
400 EndSeh(ErrSuccess == ERROR_SUCCESS ? dwException : STATUS_SUCCESS);
401 // Windows 7: does not throw exception here
402
403 if (dwException == STATUS_SUCCESS || ErrSuccess == ERROR_INVALID_FUNCTION)
404 {
405 ok(dwError == ErrSuccess,
406 "GetLastError() returned %ld, expected %ld\n",
407 dwError, ErrSuccess);
408 if (ErrSuccess == ERROR_SUCCESS)
409 {
410 ok(uResultSize == dwBufferSize,
411 "uResultSize is not equal dwBufferSize, expected %ld\n",
412 dwBufferSize);
413 }
414 else
415 {
416 ok(uResultSize == 0,
417 "uResultSize is %u, expected == 0\n",
418 uResultSize);
419 }
420 }
421 else
422 {
423 // Windows 7: returns ERROR_NOACCESS here
424 ok(dwError == 0xbeeffeed,
425 "GetLastError() returned %ld, expected %u\n",
426 dwError, 0xbeeffeed);
427 // Windows 7: returns correct size here
428 ok(uResultSize == 0,
429 "uResultSize is %u, expected == 0\n",
430 uResultSize);
431 }
432
433 if (Buffer && dwException == STATUS_SUCCESS)
434 {
435 if (ErrSuccess == ERROR_SUCCESS)
436 {
437 ok(*(DWORD *)Buffer != 0xFFFFFFFF,
438 "Buffer should be filled at offset 0\n");
439 }
440 else
441 {
442 ok(*(DWORD *)Buffer == 0xFFFFFFFF,
443 "Buffer should be clean at offset 0\n");
444 }
445 }
446 }
447
448 static
449 VOID
test_Functions()450 test_Functions()
451 {
452 static const ENTRY Entries[] =
453 {
454 { 'ACPI', ERROR_INSUFFICIENT_BUFFER, ERROR_SUCCESS },
455 { 'FIRM', ERROR_INSUFFICIENT_BUFFER, ERROR_SUCCESS },
456 { 'RSMB', ERROR_INSUFFICIENT_BUFFER, ERROR_SUCCESS },
457 /* This entry should be last */
458 { 0xDEAD, ERROR_INVALID_FUNCTION, ERROR_INVALID_FUNCTION },
459 };
460 CHAR Buffer[262144]; // 256 KiB should be enough
461 CHAR Sign[sizeof(DWORD) + 1];
462 UINT TableCount[_countof(Entries)];
463 DWORD FirstTableID[_countof(Entries)];
464 int i;
465
466 // Test EnumSystemFirmwareTables
467 for (i = 0; i < _countof(Entries); i++)
468 {
469 // Test with NULL buffer
470 test_EnumBuffer(Entries[i].Signature, NULL, sizeof(Buffer), NULL, NULL,
471 Entries[i].ErrInsuff, Entries[i].ErrSuccess);
472 // Test with wrong buffer
473 test_EnumBuffer(Entries[i].Signature, (PVOID *)(LONG_PTR)0xbeeffeed, sizeof(Buffer), NULL, NULL,
474 Entries[i].ErrInsuff, Entries[i].ErrSuccess);
475 // Test with correct buffer
476 test_EnumBuffer(Entries[i].Signature, &Buffer, sizeof(Buffer), &TableCount[i], &FirstTableID[i],
477 Entries[i].ErrInsuff, Entries[i].ErrSuccess);
478 }
479
480 // Test GetSystemFirmwareTable
481 for (i = 0; i < _countof(Entries); i++)
482 {
483 // Test with fake ID and NULL buffer
484 test_GetBuffer(Entries[i].Signature, 0xbeeffeed, NULL, sizeof(Buffer),
485 TRUE, Entries[i].ErrInsuff, Entries[i].ErrSuccess);
486 // Test with fake ID and wrong buffer
487 test_GetBuffer(Entries[i].Signature, 0xbeeffeed, (PVOID *)(LONG_PTR)0xbeeffeed, sizeof(Buffer),
488 TRUE, Entries[i].ErrInsuff, Entries[i].ErrSuccess);
489 // Test with fake ID and correct buffer
490 test_GetBuffer(Entries[i].Signature, 0xbeeffeed, &Buffer, sizeof(Buffer),
491 TRUE, Entries[i].ErrInsuff, Entries[i].ErrSuccess);
492 if (TableCount[i] == 0)
493 {
494 if (i < _countof(Entries) - 1)
495 {
496 ZeroMemory(&Sign, sizeof(Sign));
497 *(DWORD *)&Sign = _byteswap_ulong(Entries[i].Signature);
498 skip("No tables for %s found. Skipping\n",
499 Sign);
500 }
501 continue;
502 }
503 // Test with correct ID and NULL buffer
504 test_GetBuffer(Entries[i].Signature, FirstTableID[i], NULL, sizeof(Buffer),
505 FALSE, Entries[i].ErrInsuff, Entries[i].ErrSuccess);
506 // Test with correct ID and wrong buffer
507 test_GetBuffer(Entries[i].Signature, FirstTableID[i], (PVOID *)(LONG_PTR)0xbeeffeed, sizeof(Buffer),
508 FALSE, Entries[i].ErrInsuff, Entries[i].ErrSuccess);
509 // Test with correct ID and correct buffer
510 test_GetBuffer(Entries[i].Signature, FirstTableID[i], &Buffer, sizeof(Buffer),
511 FALSE, Entries[i].ErrInsuff, Entries[i].ErrSuccess);
512 }
513 }
514
START_TEST(SystemFirmware)515 START_TEST(SystemFirmware)
516 {
517 HANDLE hKernel;
518
519 hKernel = GetModuleHandleW(L"kernel32.dll");
520 if (!hKernel)
521 {
522 skip("kernel32.dll module not found. Can't proceed\n");
523 return;
524 }
525
526 pEnumSystemFirmwareTables = (void *)fEnumSystemFirmwareTables;
527 pGetSystemFirmwareTable = (void *)fGetSystemFirmwareTable;
528
529 test_Functions();
530
531 pEnumSystemFirmwareTables = (void *)GetProcAddress(hKernel, "EnumSystemFirmwareTables");
532 pGetSystemFirmwareTable = (void *)GetProcAddress(hKernel, "GetSystemFirmwareTable");
533
534 if (!pEnumSystemFirmwareTables)
535 {
536 skip("EnumSystemFirmwareTables not found. Can't proceed\n");
537 return;
538 }
539 if (!pGetSystemFirmwareTable)
540 {
541 skip("GetSystemFirmwareTable not found. Can't proceed\n");
542 return;
543 }
544 test_Functions();
545 }
546