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
19static struct efi_boot_services *boottime;
20static struct efi_runtime_services *runtime;
21static const efi_guid_t guid_vendor0 = EFI_GLOBAL_VARIABLE_GUID;
22static 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 */
31static 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 */
45static 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
313EFI_UNIT_TEST(variables_run) = {
314	.name = "variables at runtime",
315	.phase = EFI_SETUP_BEFORE_BOOTTIME_EXIT,
316	.setup = setup,
317	.execute = execute,
318};
319