1 /*
2  * PROJECT:     ReactOS API Tests
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Tests for UEFI Firmware functions
5  * COPYRIGHT:   Copyright 2023 Ratin Gao <ratin@knsoft.org>
6  */
7 
8 #include "precomp.h"
9 
10 #include <ndk/psfuncs.h>
11 #include <ndk/setypes.h>
12 #include <ndk/sefuncs.h>
13 #include <ndk/obfuncs.h>
14 
15 #define _A2W(quote) __A2W(quote)
16 #define __A2W(quote) L##quote
17 
18 #define EFI_TEST_GUID_STRING "{8768B7AC-F82F-4120-B093-30DFA27DA3B5}"
19 #define EFI_TEST_VARIABLE_NAME "RosUefiVarTest"
20 
21 #define EFI_DUMMY_NAMESPACE_GUID_STRING "{00000000-0000-0000-0000-000000000000}"
22 #define EFI_DUMMY_VARIABLE_NAME ""
23 
24 typedef enum _FIRMWARE_TYPE
25 {
26     FirmwareTypeUnknown,
27     FirmwareTypeBios,
28     FirmwareTypeUefi,
29     FirmwareTypeMax
30 } FIRMWARE_TYPE, *PFIRMWARE_TYPE;
31 
32 typedef
33 _Success_(return)
34 BOOL
35 WINAPI
36 FN_GetFirmwareType(_Out_ PFIRMWARE_TYPE FirmwareType);
37 
38 static ULONG RandomSeed;
39 static DWORD EfiVariableValue;
40 
test_GetFirmwareType(BOOL bIsUEFI)41 static VOID test_GetFirmwareType(BOOL bIsUEFI)
42 {
43     BOOL bResult;
44     HMODULE hKernel32;
45     FN_GetFirmwareType* pfnGetFirmwareType;
46     FIRMWARE_TYPE FirmwareType = FirmwareTypeUnknown, FirmwareExpect;
47 
48     /* Load functions */
49     hKernel32 = GetModuleHandleW(L"kernel32.dll");
50     if (hKernel32 == NULL)
51     {
52         skip("Module kernel32 not found\n");
53         return;
54     }
55     pfnGetFirmwareType = (FN_GetFirmwareType*)GetProcAddress(hKernel32, "GetFirmwareType");
56     if (pfnGetFirmwareType == NULL)
57     {
58         skip("GetFirmwareType (NT6.2+ API) not found\n");
59         return;
60     }
61 
62     /* Test GetFirmwareType, should return FirmwareTypeBios or FirmwareTypeUefi */
63     bResult = pfnGetFirmwareType(&FirmwareType);
64     ok(bResult,
65        "GetFirmwareType failed with error: 0x%08lX\n",
66        GetLastError());
67 
68     if (!bResult)
69         return;
70 
71     FirmwareExpect = (bIsUEFI ? FirmwareTypeUefi : FirmwareTypeBios);
72     ok(FirmwareType == FirmwareExpect,
73        "FirmwareType is %d, but %d is expected.\n",
74        FirmwareType, FirmwareExpect);
75 }
76 
START_TEST(UEFIFirmware)77 START_TEST(UEFIFirmware)
78 {
79     BOOL bResult, bResultTemp, bIsUEFI;
80     DWORD dwError, dwErrorTemp, dwLength, dwLengthTemp, dwValue;
81     HANDLE hToken;
82     TOKEN_PRIVILEGES Privilege;
83     NTSTATUS Status;
84     ULONG ReturnLength;
85 
86     /*
87      * Check whether this test runs on legacy BIOS-based or UEFI system
88      * by calling GetFirmwareEnvironmentVariable with dummy name and GUID.
89      * It should fail with ERROR_INVALID_FUNCTION on the former and
90      * fail with another error on the latter.
91      */
92     dwLength = GetFirmwareEnvironmentVariableW(_A2W(EFI_DUMMY_VARIABLE_NAME),
93                                                _A2W(EFI_DUMMY_NAMESPACE_GUID_STRING),
94                                                NULL,
95                                                0);
96     dwError = GetLastError();
97     ok(dwLength == 0, "dwLength = %lu, expected 0\n", dwLength);
98 
99     bIsUEFI = (dwLength == 0 && dwError != ERROR_INVALID_FUNCTION);
100     test_GetFirmwareType(bIsUEFI);
101     if (!bIsUEFI)
102     {
103         skip("Skipping tests that require UEFI environment.\n");
104         return;
105     }
106 
107     /* Test ANSI function too */
108     dwLengthTemp = GetFirmwareEnvironmentVariableA(EFI_DUMMY_VARIABLE_NAME,
109                                                    EFI_DUMMY_NAMESPACE_GUID_STRING,
110                                                    NULL,
111                                                    0);
112     dwErrorTemp = GetLastError();
113     ok(dwLengthTemp == dwLength && dwErrorTemp == dwError,
114        "dwLength = %lu, LastError = %lu, expected bResult = %lu, LastError = %lu\n",
115        dwLengthTemp,
116        dwErrorTemp,
117        dwLength,
118        dwError);
119 
120     /* Generate a random variable value to be used in this test */
121     RandomSeed = GetTickCount();
122     EfiVariableValue = RtlRandom(&RandomSeed);
123 
124     /* Try to set firmware variable, should fail with ERROR_PRIVILEGE_NOT_HELD,
125      * because no SE_SYSTEM_ENVIRONMENT_NAME privilege enabled by default. */
126     bResult = SetFirmwareEnvironmentVariableW(_A2W(EFI_TEST_VARIABLE_NAME),
127                                               _A2W(EFI_TEST_GUID_STRING),
128                                               &EfiVariableValue,
129                                               sizeof(EfiVariableValue));
130     dwError = GetLastError();
131     ok(!bResult && dwError == ERROR_PRIVILEGE_NOT_HELD,
132        "bResult = %d, LastError = %lu, expected bResult = 0, LastError = ERROR_PRIVILEGE_NOT_HELD\n",
133        bResult,
134        dwError);
135 
136     /* Test ANSI function too */
137     bResultTemp = SetFirmwareEnvironmentVariableA(EFI_TEST_VARIABLE_NAME,
138                                                   EFI_TEST_GUID_STRING,
139                                                   &EfiVariableValue,
140                                                   sizeof(EfiVariableValue));
141     dwErrorTemp = GetLastError();
142     ok(bResultTemp == bResult && dwErrorTemp == dwError,
143        "bResult = %d, LastError = %lu, expected bResult = %d, LastError = %lu\n",
144        bResultTemp,
145        dwErrorTemp,
146        bResult,
147        dwError);
148 
149     /* Enable SE_SYSTEM_ENVIRONMENT_NAME privilege required by the following tests */
150     bResult = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken);
151     if (!bResult)
152     {
153         skip("OpenProcessToken failed with error: 0x%08lX, aborting.\n", GetLastError());
154         return;
155     }
156     Privilege.PrivilegeCount = 1;
157     Privilege.Privileges[0].Luid = RtlConvertUlongToLuid(SE_SYSTEM_ENVIRONMENT_PRIVILEGE);
158     Privilege.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
159     Status = NtAdjustPrivilegesToken(hToken, FALSE, &Privilege, sizeof(Privilege), NULL, &ReturnLength);
160     if (Status != STATUS_SUCCESS)
161     {
162         skip("NtAdjustPrivilegesToken failed with status: 0x%08lX, aborting.\n", Status);
163         NtClose(hToken);
164         return;
165     }
166 
167     /* Set our test variable to UEFI firmware */
168     bResult = SetFirmwareEnvironmentVariableW(_A2W(EFI_TEST_VARIABLE_NAME),
169                                               _A2W(EFI_TEST_GUID_STRING),
170                                               &EfiVariableValue,
171                                               sizeof(EfiVariableValue));
172     ok(bResult,
173        "SetFirmwareEnvironmentVariableW failed with error: 0x%08lX\n",
174        GetLastError());
175     if (bResult)
176     {
177         /* Get the variable back and verify */
178         dwLength = GetFirmwareEnvironmentVariableW(_A2W(EFI_TEST_VARIABLE_NAME),
179                                                    _A2W(EFI_TEST_GUID_STRING),
180                                                    &dwValue,
181                                                    sizeof(dwValue));
182         ok(dwLength,
183            "GetFirmwareEnvironmentVariableW failed with error: 0x%08lX\n",
184            GetLastError());
185         if (dwLength)
186         {
187             ok(dwLength == sizeof(EfiVariableValue) && dwValue == EfiVariableValue,
188                "Retrieved variable different from what we set, "
189                "dwLength = %lu, dwValue = %lu, expected dwLength = %u, dwValue = %lu",
190                dwLength,
191                dwValue,
192                sizeof(EfiVariableValue),
193                EfiVariableValue);
194         }
195     }
196 
197     /* Change variable value and test ANSI function */
198     EfiVariableValue = RtlRandom(&RandomSeed);
199     bResult = SetFirmwareEnvironmentVariableA(EFI_TEST_VARIABLE_NAME,
200                                               EFI_TEST_GUID_STRING,
201                                               &EfiVariableValue,
202                                               sizeof(EfiVariableValue));
203     ok(bResult,
204        "SetFirmwareEnvironmentVariableA failed with error: 0x%08lX\n",
205        GetLastError());
206     if (bResult)
207     {
208         /* Get the variable back and verify */
209         dwLength = GetFirmwareEnvironmentVariableA(EFI_TEST_VARIABLE_NAME,
210                                                    EFI_TEST_GUID_STRING,
211                                                    &dwValue,
212                                                    sizeof(dwValue));
213         ok(dwLength,
214            "GetFirmwareEnvironmentVariableA failed with error: 0x%08lX\n",
215            GetLastError());
216         if (dwLength)
217         {
218             ok(dwLength == sizeof(EfiVariableValue) && dwValue == EfiVariableValue,
219                "Retrieved variable different from what we set, "
220                "dwLength = %lu, dwValue = %lu, expected dwLength = %u, dwValue = %lu",
221                dwLength,
222                dwValue,
223                sizeof(EfiVariableValue),
224                EfiVariableValue);
225         }
226     }
227 
228     /* Delete the variable */
229     bResult = SetFirmwareEnvironmentVariableW(_A2W(EFI_TEST_VARIABLE_NAME),
230                                               _A2W(EFI_TEST_GUID_STRING),
231                                               NULL,
232                                               0);
233     ok(bResult,
234        "SetFirmwareEnvironmentVariableW failed with error: 0x%08lX\n",
235        GetLastError());
236     if (bResult)
237     {
238         dwLength = GetFirmwareEnvironmentVariableW(_A2W(EFI_TEST_VARIABLE_NAME),
239                                                    _A2W(EFI_TEST_GUID_STRING),
240                                                    &dwValue,
241                                                    sizeof(dwValue));
242         ok(dwLength == 0, "SetFirmwareEnvironmentVariableW didn't delete the variable!\n");
243     }
244 
245     /* Restore variable and test ANSI function */
246     bResult = SetFirmwareEnvironmentVariableW(_A2W(EFI_TEST_VARIABLE_NAME),
247                                               _A2W(EFI_TEST_GUID_STRING),
248                                               &EfiVariableValue,
249                                               sizeof(EfiVariableValue));
250     if (!bResult)
251     {
252         skip("SetFirmwareEnvironmentVariableW failed to restore variable with error: 0x%08lX\n",
253              GetLastError());
254         goto _exit;
255     }
256     bResult = SetFirmwareEnvironmentVariableA(EFI_TEST_VARIABLE_NAME,
257                                               EFI_TEST_GUID_STRING,
258                                               NULL,
259                                               0);
260     ok(bResult,
261        "SetFirmwareEnvironmentVariableA failed with error: 0x%08lX\n",
262        GetLastError());
263     if (bResult)
264     {
265         dwLength = GetFirmwareEnvironmentVariableA(EFI_TEST_VARIABLE_NAME,
266                                                    EFI_TEST_GUID_STRING,
267                                                    &dwValue,
268                                                    sizeof(dwValue));
269         ok(dwLength == 0, "SetFirmwareEnvironmentVariableA didn't delete the variable!\n");
270     }
271 
272 _exit:
273     /* Restore the privilege */
274     Privilege.Privileges[0].Attributes = 0;
275     Status = NtAdjustPrivilegesToken(hToken, FALSE, &Privilege, sizeof(Privilege), NULL, &ReturnLength);
276     ok(Status == STATUS_SUCCESS, "NtAdjustPrivilegesToken failed with status: 0x%08lX\n", Status);
277     NtClose(hToken);
278 }
279