1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * efi_selftest_variables
4  *
5  * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
6  *
7  * This unit test checks the runtime services for variables:
8  * GetVariable, GetNextVariableName, SetVariable, QueryVariableInfo.
9  */
10 
11 #include <efi_selftest.h>
12 
13 #define EFI_ST_MAX_DATA_SIZE 16
14 #define EFI_ST_MAX_VARNAME_SIZE 80
15 
16 static struct efi_boot_services *boottime;
17 static struct efi_runtime_services *runtime;
18 static const efi_guid_t guid_vendor0 =
19 	EFI_GUID(0x67029eb5, 0x0af2, 0xf6b1,
20 		 0xda, 0x53, 0xfc, 0xb5, 0x66, 0xdd, 0x1c, 0xe6);
21 static const efi_guid_t guid_vendor1 =
22 	EFI_GUID(0xff629290, 0x1fc1, 0xd73f,
23 		 0x8f, 0xb1, 0x32, 0xf9, 0x0c, 0xa0, 0x42, 0xea);
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 unit test.
42  */
execute(void)43 static int execute(void)
44 {
45 	efi_status_t ret;
46 	efi_uintn_t len;
47 	u32 attr;
48 	u8 v[16] = {0x5d, 0xd1, 0x5e, 0x51, 0x5a, 0x05, 0xc7, 0x0c,
49 		    0x35, 0x4a, 0xae, 0x87, 0xa5, 0xdf, 0x0f, 0x65,};
50 	u8 data[EFI_ST_MAX_DATA_SIZE];
51 	u16 varname[EFI_ST_MAX_VARNAME_SIZE];
52 	int flag;
53 	efi_guid_t guid;
54 	int test_ret;
55 
56 	test_ret = efi_st_query_variable_common(runtime,
57 						EFI_VARIABLE_BOOTSERVICE_ACCESS);
58 	if (test_ret != EFI_ST_SUCCESS) {
59 		efi_st_error("QueryVariableInfo failed\n");
60 		return EFI_ST_FAILURE;
61 	}
62 	/* Set variable 0 */
63 	ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
64 				    EFI_VARIABLE_BOOTSERVICE_ACCESS,
65 				    3, v + 4);
66 	if (ret != EFI_SUCCESS) {
67 		efi_st_error("SetVariable failed\n");
68 		return EFI_ST_FAILURE;
69 	}
70 	data[3] = 0xff;
71 	len = 3;
72 	ret = runtime->get_variable(u"efi_st_var0", &guid_vendor0,
73 				    &attr, &len, data);
74 	if (ret != EFI_SUCCESS) {
75 		efi_st_error("GetVariable failed\n");
76 		return EFI_ST_FAILURE;
77 	}
78 	if (memcmp(data, v + 4, 3)) {
79 		efi_st_error("GetVariable returned wrong value\n");
80 		return EFI_ST_FAILURE;
81 	}
82 	if (data[3] != 0xff) {
83 		efi_st_error("GetVariable wrote past the end of the buffer\n");
84 		return EFI_ST_FAILURE;
85 	}
86 	/* Set variable 1 */
87 	ret = runtime->set_variable(u"efi_st_var1", &guid_vendor1,
88 				    EFI_VARIABLE_BOOTSERVICE_ACCESS,
89 				    8, v);
90 	if (ret != EFI_SUCCESS) {
91 		efi_st_error("SetVariable failed\n");
92 		return EFI_ST_FAILURE;
93 	}
94 	len = EFI_ST_MAX_DATA_SIZE;
95 	ret = runtime->get_variable(u"efi_st_var1", &guid_vendor1,
96 				    &attr, &len, data);
97 	if (ret != EFI_SUCCESS) {
98 		efi_st_error("GetVariable failed\n");
99 		return EFI_ST_FAILURE;
100 	}
101 	if (len != 8) {
102 		efi_st_error("GetVariable returned wrong length %u\n",
103 			     (unsigned int)len);
104 		return EFI_ST_FAILURE;
105 	}
106 	if (memcmp(data, v, 8)) {
107 		efi_st_error("GetVariable returned wrong value\n");
108 		return EFI_ST_FAILURE;
109 	}
110 	/* Append variable 1 */
111 	ret = runtime->set_variable(u"efi_st_var1", &guid_vendor1,
112 				    EFI_VARIABLE_BOOTSERVICE_ACCESS |
113 				    EFI_VARIABLE_APPEND_WRITE,
114 				    7, v + 8);
115 	if (ret != EFI_SUCCESS) {
116 		efi_st_error("SetVariable(APPEND_WRITE) failed\n");
117 		return EFI_ST_FAILURE;
118 	}
119 	len = EFI_ST_MAX_DATA_SIZE;
120 	ret = runtime->get_variable(u"efi_st_var1", &guid_vendor1,
121 				    &attr, &len, data);
122 	if (ret != EFI_SUCCESS) {
123 		efi_st_error("GetVariable failed\n");
124 		return EFI_ST_FAILURE;
125 	}
126 	if (len != 15)
127 		efi_st_todo("GetVariable returned wrong length %u\n",
128 			    (unsigned int)len);
129 	if (memcmp(data, v, len))
130 		efi_st_todo("GetVariable returned wrong value\n");
131 
132 	/* Append variable 2, write to non-existent variable with datasize=0 */
133 	ret = runtime->set_variable(u"efi_none", &guid_vendor1,
134 				    EFI_VARIABLE_BOOTSERVICE_ACCESS |
135 				    EFI_VARIABLE_APPEND_WRITE,
136 				    0, v);
137 	if (ret != EFI_SUCCESS) {
138 		efi_st_error(
139 			"SetVariable(APPEND_WRITE) with size 0 to non-existent variable returns wrong code\n");
140 		return EFI_ST_FAILURE;
141 	}
142 	len = EFI_ST_MAX_DATA_SIZE;
143 	ret = runtime->get_variable(u"efi_none", &guid_vendor1,
144 				    &attr, &len, data);
145 	if (ret != EFI_NOT_FOUND) {
146 		efi_st_error("Variable must not be created\n");
147 		return EFI_ST_FAILURE;
148 	}
149 	/* Append variable 2, write to non-existent variable with valid data size*/
150 	ret = runtime->set_variable(u"efi_none", &guid_vendor1,
151 				    EFI_VARIABLE_BOOTSERVICE_ACCESS |
152 				    EFI_VARIABLE_APPEND_WRITE,
153 				    15, v);
154 	if (ret != EFI_SUCCESS) {
155 		efi_st_error("SetVariable(APPEND_WRITE) with valid size and data to non-existent variable must be succcessful\n");
156 		return EFI_ST_FAILURE;
157 	}
158 	len = EFI_ST_MAX_DATA_SIZE;
159 	ret = runtime->get_variable(u"efi_none", &guid_vendor1,
160 				    &attr, &len, data);
161 	if (ret != EFI_SUCCESS) {
162 		efi_st_error("GetVariable failed\n");
163 		return EFI_ST_FAILURE;
164 	}
165 	if (len != 15)
166 		efi_st_todo("GetVariable returned wrong length %u\n",
167 			    (unsigned int)len);
168 	if (memcmp(data, v, len))
169 		efi_st_todo("GetVariable returned wrong value\n");
170 	/* Delete variable efi_none */
171 	ret = runtime->set_variable(u"efi_none", &guid_vendor1,
172 				    0, 0, NULL);
173 	if (ret != EFI_SUCCESS) {
174 		efi_st_error("SetVariable failed\n");
175 		return EFI_ST_FAILURE;
176 	}
177 	len = EFI_ST_MAX_DATA_SIZE;
178 	ret = runtime->get_variable(u"efi_none", &guid_vendor1,
179 				    &attr, &len, data);
180 	if (ret != EFI_NOT_FOUND) {
181 		efi_st_error("Variable was not deleted\n");
182 		return EFI_ST_FAILURE;
183 	}
184 	/* Enumerate variables */
185 
186 	ret = runtime->get_next_variable_name(NULL, u"efi_st_var1", &guid);
187 	if (ret != EFI_INVALID_PARAMETER) {
188 		efi_st_error("GetNextVariableName missing parameter check\n");
189 		return EFI_ST_FAILURE;
190 	}
191 
192 	len = 24;
193 	ret = runtime->get_next_variable_name(&len, NULL, &guid);
194 	if (ret != EFI_INVALID_PARAMETER) {
195 		efi_st_error("GetNextVariableName missing parameter check\n");
196 		return EFI_ST_FAILURE;
197 	}
198 
199 	len = 24;
200 	ret = runtime->get_next_variable_name(&len, u"efi_st_var1", NULL);
201 	if (ret != EFI_INVALID_PARAMETER) {
202 		efi_st_error("GetNextVariableName missing parameter check\n");
203 		return EFI_ST_FAILURE;
204 	}
205 
206 	len = 1;
207 	ret = runtime->get_next_variable_name(&len, u"", &guid);
208 	if (ret != EFI_INVALID_PARAMETER) {
209 		efi_st_error("GetNextVariableName missing parameter check\n");
210 		return EFI_ST_FAILURE;
211 	}
212 
213 	len = 16;
214 	ret = runtime->get_next_variable_name(&len, u"efi_st_var1", &guid);
215 	if (ret != EFI_INVALID_PARAMETER) {
216 		efi_st_error("GetNextVariableName missing parameter check\n");
217 		return EFI_ST_FAILURE;
218 	}
219 
220 	boottime->set_mem(&guid, 16, 0);
221 	*varname = 0;
222 	flag = 0;
223 	for (;;) {
224 		len = EFI_ST_MAX_VARNAME_SIZE;
225 		ret = runtime->get_next_variable_name(&len, varname, &guid);
226 		if (ret == EFI_NOT_FOUND)
227 			break;
228 		if (ret != EFI_SUCCESS) {
229 			efi_st_error("GetNextVariableName failed (%u)\n",
230 				     (unsigned int)ret);
231 			return EFI_ST_FAILURE;
232 		}
233 		if (!memcmp(&guid, &guid_vendor0, sizeof(efi_guid_t)) &&
234 		    !efi_st_strcmp_16_8(varname, "efi_st_var0")) {
235 			flag |= 1;
236 			if (len != 24) {
237 				efi_st_error("GetNextVariableName report wrong length %u, expected 24\n",
238 					     (unsigned int)len);
239 				return EFI_ST_FAILURE;
240 			}
241 		}
242 		if (!memcmp(&guid, &guid_vendor1, sizeof(efi_guid_t)) &&
243 		    !efi_st_strcmp_16_8(varname, "efi_st_var1"))
244 			flag |= 2;
245 	}
246 	if (flag != 3) {
247 		efi_st_error(
248 			"GetNextVariableName did not return all variables\n");
249 		return EFI_ST_FAILURE;
250 	}
251 	/* Delete variable 1 */
252 	ret = runtime->set_variable(u"efi_st_var1", &guid_vendor1,
253 				    0, 0, NULL);
254 	if (ret != EFI_SUCCESS) {
255 		efi_st_error("SetVariable failed\n");
256 		return EFI_ST_FAILURE;
257 	}
258 	len = EFI_ST_MAX_DATA_SIZE;
259 	ret = runtime->get_variable(u"efi_st_var1", &guid_vendor1,
260 				    &attr, &len, data);
261 	if (ret != EFI_NOT_FOUND) {
262 		efi_st_error("Variable was not deleted\n");
263 		return EFI_ST_FAILURE;
264 	}
265 	/* Delete variable 0 */
266 	ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
267 				    0, 0, NULL);
268 	if (ret != EFI_SUCCESS) {
269 		efi_st_error("SetVariable failed\n");
270 		return EFI_ST_FAILURE;
271 	}
272 	len = EFI_ST_MAX_DATA_SIZE;
273 	ret = runtime->get_variable(u"efi_st_var0", &guid_vendor0,
274 				    &attr, &len, data);
275 	if (ret != EFI_NOT_FOUND) {
276 		efi_st_error("Variable was not deleted\n");
277 		return EFI_ST_FAILURE;
278 	}
279 
280 	return EFI_ST_SUCCESS;
281 }
282 
283 EFI_UNIT_TEST(variables) = {
284 	.name = "variables",
285 	.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
286 	.setup = setup,
287 	.execute = execute,
288 };
289