1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * efi_selftest_variables_runtime
4 *
5 * Copyright (c) 2019 Heinrich Schuchardt <xypron.glpk@gmx.de>
6 *
7 * This unit test checks the runtime services for variables after
8 * ExitBootServices():
9 * GetVariable, GetNextVariableName, SetVariable, QueryVariableInfo.
10 */
11
12 #include <efi_selftest.h>
13 #include <efi_variable.h>
14 #include <u-boot/crc.h>
15
16 #define EFI_ST_MAX_DATA_SIZE 16
17 #define EFI_ST_MAX_VARNAME_SIZE 40
18
19 static struct efi_boot_services *boottime;
20 static struct efi_runtime_services *runtime;
21 static const efi_guid_t guid_vendor0 = EFI_GLOBAL_VARIABLE_GUID;
22 static const efi_guid_t __efi_runtime_data efi_rt_var_guid =
23 U_BOOT_EFI_RT_VAR_FILE_GUID;
24
25 /*
26 * Setup unit test.
27 *
28 * @handle handle of the loaded image
29 * @systable system table
30 */
setup(const efi_handle_t img_handle,const struct efi_system_table * systable)31 static int setup(const efi_handle_t img_handle,
32 const struct efi_system_table *systable)
33 {
34 boottime = systable->boottime;
35 runtime = systable->runtime;
36
37 return EFI_ST_SUCCESS;
38 }
39
40 /**
41 * execute() - execute unit test
42 *
43 * As runtime support is not implmented expect EFI_UNSUPPORTED to be returned.
44 */
execute(void)45 static int execute(void)
46 {
47 efi_status_t ret;
48 efi_uintn_t len, avail, append_len = 17;
49 u32 attr;
50 u8 v[16] = {0x5d, 0xd1, 0x5e, 0x51, 0x5a, 0x05, 0xc7, 0x0c,
51 0x35, 0x4a, 0xae, 0x87, 0xa5, 0xdf, 0x0f, 0x65,};
52 u8 v2[CONFIG_EFI_VAR_BUF_SIZE];
53 u8 data[EFI_ST_MAX_DATA_SIZE];
54 u8 data2[CONFIG_EFI_VAR_BUF_SIZE];
55 u16 varname[EFI_ST_MAX_VARNAME_SIZE];
56 efi_guid_t guid;
57 u64 max_storage, rem_storage, max_size;
58 int test_ret;
59
60 memset(v2, 0x1, sizeof(v2));
61
62 if (IS_ENABLED(CONFIG_EFI_VARIABLE_FILE_STORE)) {
63 test_ret = efi_st_query_variable_common(runtime, EFI_VARIABLE_BOOTSERVICE_ACCESS |
64 EFI_VARIABLE_RUNTIME_ACCESS);
65 if (test_ret != EFI_ST_SUCCESS) {
66 efi_st_error("QueryVariableInfo failed\n");
67 return EFI_ST_FAILURE;
68 }
69 } else {
70 ret = runtime->query_variable_info(EFI_VARIABLE_BOOTSERVICE_ACCESS,
71 &max_storage, &rem_storage,
72 &max_size);
73 if (ret != EFI_UNSUPPORTED) {
74 efi_st_error("QueryVariableInfo failed\n");
75 return EFI_ST_FAILURE;
76 }
77 }
78
79 ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
80 EFI_VARIABLE_BOOTSERVICE_ACCESS |
81 EFI_VARIABLE_RUNTIME_ACCESS,
82 3, v + 4);
83 if (IS_ENABLED(CONFIG_EFI_RT_VOLATILE_STORE)) {
84 efi_uintn_t prev_len, delta;
85 struct efi_var_entry *var;
86 struct efi_var_file *hdr;
87
88 /* At runtime only non-volatile variables may be set. */
89 if (ret != EFI_INVALID_PARAMETER) {
90 efi_st_error("SetVariable failed\n");
91 return EFI_ST_FAILURE;
92 }
93
94 /* runtime atttribute must be set */
95 ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
96 EFI_VARIABLE_BOOTSERVICE_ACCESS |
97 EFI_VARIABLE_NON_VOLATILE,
98 3, v + 4);
99 if (ret != EFI_INVALID_PARAMETER) {
100 efi_st_error("SetVariable failed\n");
101 return EFI_ST_FAILURE;
102 }
103
104 len = sizeof(data);
105 ret = runtime->get_variable(u"RTStorageVolatile",
106 &efi_rt_var_guid,
107 &attr, &len, data);
108 if (ret != EFI_SUCCESS) {
109 efi_st_error("GetVariable failed\n");
110 return EFI_ST_FAILURE;
111 }
112
113 if (len != sizeof(EFI_VAR_FILE_NAME) ||
114 memcmp(data, EFI_VAR_FILE_NAME, sizeof(EFI_VAR_FILE_NAME))) {
115 data[len - 1] = 0;
116 efi_st_error("RTStorageVolatile = %s\n", data);
117 return EFI_ST_FAILURE;
118 }
119
120 len = sizeof(data2);
121 ret = runtime->get_variable(u"VarToFile", &efi_rt_var_guid,
122 &attr, &len, data2);
123 if (ret != EFI_SUCCESS) {
124 efi_st_error("GetVariable failed\n");
125 return EFI_ST_FAILURE;
126 }
127 /*
128 * VarToFile size must change once a variable is inserted
129 * Store it now, we'll use it later
130 */
131 prev_len = len;
132 ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
133 EFI_VARIABLE_BOOTSERVICE_ACCESS |
134 EFI_VARIABLE_RUNTIME_ACCESS |
135 EFI_VARIABLE_NON_VOLATILE,
136 sizeof(v2),
137 v2);
138 /*
139 * This will try to update VarToFile as well and must fail,
140 * without changing or deleting VarToFile
141 */
142 if (ret != EFI_OUT_OF_RESOURCES) {
143 efi_st_error("SetVariable failed\n");
144 return EFI_ST_FAILURE;
145 }
146 len = sizeof(data2);
147 ret = runtime->get_variable(u"VarToFile", &efi_rt_var_guid,
148 &attr, &len, data2);
149 if (ret != EFI_SUCCESS || prev_len != len) {
150 efi_st_error("Get/SetVariable failed\n");
151 return EFI_ST_FAILURE;
152 }
153
154 /* Add an 8byte aligned variable */
155 ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
156 EFI_VARIABLE_BOOTSERVICE_ACCESS |
157 EFI_VARIABLE_RUNTIME_ACCESS |
158 EFI_VARIABLE_NON_VOLATILE,
159 sizeof(v), v);
160 if (ret != EFI_SUCCESS) {
161 efi_st_error("SetVariable failed\n");
162 return EFI_ST_FAILURE;
163 }
164
165 /* Delete it by setting the attrs to 0 */
166 ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
167 0, sizeof(v), v);
168 if (ret != EFI_SUCCESS) {
169 efi_st_error("SetVariable failed\n");
170 return EFI_ST_FAILURE;
171 }
172
173 /* Add it back */
174 ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
175 EFI_VARIABLE_BOOTSERVICE_ACCESS |
176 EFI_VARIABLE_RUNTIME_ACCESS |
177 EFI_VARIABLE_NON_VOLATILE,
178 sizeof(v), v);
179 if (ret != EFI_SUCCESS) {
180 efi_st_error("SetVariable failed\n");
181 return EFI_ST_FAILURE;
182 }
183
184 /* Delete it again by setting the size to 0 */
185 ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
186 EFI_VARIABLE_BOOTSERVICE_ACCESS |
187 EFI_VARIABLE_RUNTIME_ACCESS |
188 EFI_VARIABLE_NON_VOLATILE,
189 0, NULL);
190 if (ret != EFI_SUCCESS) {
191 efi_st_error("SetVariable failed\n");
192 return EFI_ST_FAILURE;
193 }
194
195 /* Delete it again and make sure it's not there */
196 ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
197 EFI_VARIABLE_BOOTSERVICE_ACCESS |
198 EFI_VARIABLE_RUNTIME_ACCESS |
199 EFI_VARIABLE_NON_VOLATILE,
200 0, NULL);
201 if (ret != EFI_NOT_FOUND) {
202 efi_st_error("SetVariable failed\n");
203 return EFI_ST_FAILURE;
204 }
205
206 /*
207 * Add a non-aligned variable
208 * VarToFile updates must include efi_st_var0
209 */
210 ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
211 EFI_VARIABLE_BOOTSERVICE_ACCESS |
212 EFI_VARIABLE_RUNTIME_ACCESS |
213 EFI_VARIABLE_NON_VOLATILE,
214 9, v + 4);
215 if (ret != EFI_SUCCESS) {
216 efi_st_error("SetVariable failed\n");
217 return EFI_ST_FAILURE;
218 }
219 var = efi_var_mem_find(&guid_vendor0, u"efi_st_var0", NULL);
220 if (!var) {
221 efi_st_error("GetVariable failed\n");
222 return EFI_ST_FAILURE;
223 }
224 delta = efi_var_entry_len(var);
225 len = sizeof(data2);
226 ret = runtime->get_variable(u"VarToFile", &efi_rt_var_guid,
227 &attr, &len, data2);
228 if (ret != EFI_SUCCESS || prev_len + delta != len) {
229 efi_st_error("Get/SetVariable failed\n");
230 return EFI_ST_FAILURE;
231 }
232
233 /*
234 * Append on an existing variable must update VarToFile
235 * Our variable entries are 8-byte aligned.
236 * Adding a single byte will fit on the existing space
237 */
238 prev_len = len;
239 avail = efi_var_entry_len(var) -
240 (sizeof(u16) * (u16_strlen(var->name) + 1) + sizeof(*var)) -
241 var->length;
242 if (avail >= append_len)
243 delta = 0;
244 else
245 delta = ALIGN(append_len - avail, 8);
246 ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
247 EFI_VARIABLE_BOOTSERVICE_ACCESS |
248 EFI_VARIABLE_RUNTIME_ACCESS |
249 EFI_VARIABLE_APPEND_WRITE |
250 EFI_VARIABLE_NON_VOLATILE,
251 append_len, v2);
252 if (ret != EFI_SUCCESS) {
253 efi_st_error("SetVariable failed\n");
254 return EFI_ST_FAILURE;
255 }
256 len = sizeof(data2);
257 ret = runtime->get_variable(u"VarToFile", &efi_rt_var_guid,
258 &attr, &len, data2);
259 if (ret != EFI_SUCCESS) {
260 efi_st_error("GetVariable failed\n");
261 return EFI_ST_FAILURE;
262 }
263 if (prev_len + delta != len) {
264 efi_st_error("Unexpected VarToFile size");
265 return EFI_ST_FAILURE;
266 }
267
268 /* Make sure that variable contains a valid file */
269 hdr = (struct efi_var_file *)data2;
270 if (hdr->magic != EFI_VAR_FILE_MAGIC ||
271 len != hdr->length ||
272 hdr->crc32 != crc32(0, (u8 *)((uintptr_t)data2 + sizeof(struct efi_var_file)),
273 len - sizeof(struct efi_var_file))) {
274 efi_st_error("VarToFile invalid header\n");
275 return EFI_ST_FAILURE;
276 }
277
278 /* Variables that are BS, RT and volatile are RO after EBS */
279 ret = runtime->set_variable(u"VarToFile", &efi_rt_var_guid,
280 EFI_VARIABLE_BOOTSERVICE_ACCESS |
281 EFI_VARIABLE_RUNTIME_ACCESS |
282 EFI_VARIABLE_NON_VOLATILE,
283 sizeof(v), v);
284 if (ret != EFI_WRITE_PROTECTED) {
285 efi_st_error("Get/SetVariable failed\n");
286 return EFI_ST_FAILURE;
287 }
288 } else {
289 if (ret != EFI_UNSUPPORTED) {
290 efi_st_error("SetVariable failed\n");
291 return EFI_ST_FAILURE;
292 }
293 }
294 len = EFI_ST_MAX_DATA_SIZE;
295 ret = runtime->get_variable(u"PlatformLangCodes", &guid_vendor0,
296 &attr, &len, data);
297 if (ret != EFI_SUCCESS) {
298 efi_st_error("GetVariable failed\n");
299 return EFI_ST_FAILURE;
300 }
301 memset(&guid, 0, 16);
302 *varname = 0;
303 len = 2 * EFI_ST_MAX_VARNAME_SIZE;
304 ret = runtime->get_next_variable_name(&len, varname, &guid);
305 if (ret != EFI_SUCCESS) {
306 efi_st_error("GetNextVariableName failed\n");
307 return EFI_ST_FAILURE;
308 }
309
310 return EFI_ST_SUCCESS;
311 }
312
313 EFI_UNIT_TEST(variables_run) = {
314 .name = "variables at runtime",
315 .phase = EFI_SETUP_BEFORE_BOOTTIME_EXIT,
316 .setup = setup,
317 .execute = execute,
318 };
319