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