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
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
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
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
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
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
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 
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