blob: 4473e7c36ed2ba7f0993601313e64dcc3607a93a [file] [log] [blame]
Heinrich Schuchardtee3d1e02020-12-08 15:55:01 +01001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * efi_selftest_load_file
4 *
5 * Copyright (c) 2020 Heinrich Schuchardt <xypron.glpk@gmx.de>
6 *
7 * This test checks the handling of the LOAD_FILE and the LOAD_FILE2 protocol
8 * by the LoadImage() service.
9 */
10
11#include <efi_selftest.h>
12/* Include containing the miniapp.efi application */
13#include "efi_miniapp_file_image_exit.h"
14
15/* Block size of compressed disk image */
16#define COMPRESSED_DISK_IMAGE_BLOCK_SIZE 8
17
18/* Binary logarithm of the block size */
19#define LB_BLOCK_SIZE 9
20
21#define GUID_VENDOR \
22 EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d, \
23 0x08, 0x72, 0x81, 0x9c, 0x65, 0xfc, 0xbb, 0xd1)
24
25#define GUID_VENDOR2 \
26 EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d, \
27 0x08, 0x72, 0x81, 0x9c, 0x65, 0xfc, 0xbb, 0xd2)
28
29#define FILE_NAME_SIZE 16
30
31static const efi_guid_t efi_st_guid_load_file_protocol =
32 EFI_LOAD_FILE_PROTOCOL_GUID;
33static const efi_guid_t efi_st_guid_load_file2_protocol =
34 EFI_LOAD_FILE2_PROTOCOL_GUID;
35static const efi_guid_t efi_st_guid_device_path =
36 EFI_DEVICE_PATH_PROTOCOL_GUID;
37
38static efi_handle_t image_handle;
39static struct efi_boot_services *boottime;
40static efi_handle_t handle_lf;
41static efi_handle_t handle_lf2;
42
43/* One 8 byte block of the compressed disk image */
44struct line {
45 size_t addr;
46 char *line;
47};
48
49/* Compressed file image */
50struct compressed_file_image {
51 size_t length;
52 struct line lines[];
53};
54
55static struct compressed_file_image img = EFI_ST_DISK_IMG;
56
57static int load_file_call_count;
58static int load_file2_call_count;
59
60/* Decompressed file image */
61static u8 *image;
62
63static struct {
64 struct efi_device_path_vendor v;
65 struct efi_device_path d;
66} dp_lf_prot = {
67 {
68 {
69 DEVICE_PATH_TYPE_HARDWARE_DEVICE,
70 DEVICE_PATH_SUB_TYPE_VENDOR,
71 sizeof(struct efi_device_path_vendor),
72 },
73 GUID_VENDOR,
74 },
75 {
76 DEVICE_PATH_TYPE_END,
77 DEVICE_PATH_SUB_TYPE_END,
78 sizeof(struct efi_device_path),
79 },
80};
81
82static struct {
83 struct efi_device_path_vendor v;
84 struct efi_device_path_file_path f;
85 u16 file_name[FILE_NAME_SIZE];
86 struct efi_device_path e;
87} dp_lf_file = {
88 {
89 {
90 DEVICE_PATH_TYPE_HARDWARE_DEVICE,
91 DEVICE_PATH_SUB_TYPE_VENDOR,
92 sizeof(struct efi_device_path_vendor),
93 },
94 GUID_VENDOR,
95 },
96 {
97 {
98 DEVICE_PATH_TYPE_MEDIA_DEVICE,
99 DEVICE_PATH_SUB_TYPE_FILE_PATH,
100 sizeof(struct efi_device_path_file_path) +
101 FILE_NAME_SIZE * sizeof(u16),
102 }
103 },
104 L"\\lf.efi",
105 {
106 DEVICE_PATH_TYPE_END,
107 DEVICE_PATH_SUB_TYPE_END,
108 sizeof(struct efi_device_path),
109 },
110};
111
112struct efi_device_path *dp_lf_file_remainder = &dp_lf_file.f.dp;
113
114static struct {
115 struct efi_device_path_vendor v;
116 struct efi_device_path d;
117} dp_lf2_prot = {
118 {
119 {
120 DEVICE_PATH_TYPE_HARDWARE_DEVICE,
121 DEVICE_PATH_SUB_TYPE_VENDOR,
122 sizeof(struct efi_device_path_vendor),
123 },
124 GUID_VENDOR2,
125 },
126 {
127 DEVICE_PATH_TYPE_END,
128 DEVICE_PATH_SUB_TYPE_END,
129 sizeof(struct efi_device_path),
130 },
131};
132
133static struct {
134 struct efi_device_path_vendor v;
135 struct efi_device_path_file_path f;
136 u16 file_name[FILE_NAME_SIZE];
137 struct efi_device_path e;
138} dp_lf2_file = {
139 {
140 {
141 DEVICE_PATH_TYPE_HARDWARE_DEVICE,
142 DEVICE_PATH_SUB_TYPE_VENDOR,
143 sizeof(struct efi_device_path_vendor),
144 },
145 GUID_VENDOR2,
146 },
147 {
148 {
149 DEVICE_PATH_TYPE_MEDIA_DEVICE,
150 DEVICE_PATH_SUB_TYPE_FILE_PATH,
151 sizeof(struct efi_device_path_file_path) +
152 FILE_NAME_SIZE * sizeof(u16),
153 }
154 },
155 L"\\lf2.efi",
156 {
157 DEVICE_PATH_TYPE_END,
158 DEVICE_PATH_SUB_TYPE_END,
159 sizeof(struct efi_device_path),
160 },
161};
162
163struct efi_device_path *dp_lf2_file_remainder = &dp_lf2_file.f.dp;
164
165/*
166 * Decompress the disk image.
167 *
168 * @image decompressed disk image
169 * @return status code
170 */
171static efi_status_t decompress(u8 **image)
172{
173 u8 *buf;
174 size_t i;
175 size_t addr;
176 size_t len;
177 efi_status_t ret;
178
179 ret = boottime->allocate_pool(EFI_LOADER_DATA, img.length,
180 (void **)&buf);
181 if (ret != EFI_SUCCESS) {
182 efi_st_error("Out of memory\n");
183 return ret;
184 }
185 boottime->set_mem(buf, img.length, 0);
186
187 for (i = 0; ; ++i) {
188 if (!img.lines[i].line)
189 break;
190 addr = img.lines[i].addr;
191 len = COMPRESSED_DISK_IMAGE_BLOCK_SIZE;
192 if (addr + len > img.length)
193 len = img.length - addr;
194 boottime->copy_mem(buf + addr, img.lines[i].line, len);
195 }
196 *image = buf;
197 return ret;
198}
199
200/*
201 * load_file() - LoadFile() service of a EFI_LOAD_FILE_PROTOCOL
202 *
203 * @this: instance of EFI_LOAD_FILE_PROTOCOL
204 * @file_path: remaining device path
205 * @boot_policy: true if called by boot manager
206 * @buffer_size: (required) buffer size
207 * @buffer: buffer to which the file is to be loaded
208 */
209efi_status_t EFIAPI load_file(struct efi_load_file_protocol *this,
210 struct efi_device_path *file_path,
211 bool boot_policy,
212 efi_uintn_t *buffer_size,
213 void *buffer)
214{
215 ++load_file_call_count;
216 if (memcmp(file_path, dp_lf_file_remainder,
217 sizeof(struct efi_device_path_file_path) +
218 FILE_NAME_SIZE * sizeof(u16) +
219 sizeof(struct efi_device_path))) {
220 efi_st_error("Wrong remaining device path\n");
221 return EFI_NOT_FOUND;
222 }
223 if (this->load_file != load_file) {
224 efi_st_error("wrong this\n");
225 return EFI_INVALID_PARAMETER;
226 }
227 if (*buffer_size < img.length) {
228 *buffer_size = img.length;
229 return EFI_BUFFER_TOO_SMALL;
230 }
231 memcpy(buffer, image, img.length);
232 *buffer_size = img.length;
233 return EFI_SUCCESS;
234}
235
236
237/*
238 * load_file2() - LoadFile() service of a EFI_LOAD_FILE2_PROTOCOL
239 *
240 * @this: instance of EFI_LOAD_FILE2_PROTOCOL
241 * @file_path: remaining device path
242 * @boot_policy: true if called by boot manager
243 * @buffer_size: (required) buffer size
244 * @buffer: buffer to which the file is to be loaded
245 */
246efi_status_t EFIAPI load_file2(struct efi_load_file_protocol *this,
247 struct efi_device_path *file_path,
248 bool boot_policy,
249 efi_uintn_t *buffer_size,
250 void *buffer)
251{
252 ++load_file2_call_count;
253 if (memcmp(file_path, dp_lf2_file_remainder,
254 sizeof(struct efi_device_path_file_path) +
255 FILE_NAME_SIZE * sizeof(u16) +
256 sizeof(struct efi_device_path))) {
257 efi_st_error("Wrong remaining device path\n");
258 return EFI_NOT_FOUND;
259 }
260 if (this->load_file != load_file2) {
261 efi_st_error("wrong this\n");
262 return EFI_INVALID_PARAMETER;
263 }
264 if (boot_policy) {
265 efi_st_error("LOAD_FILE2 called with boot_policy = true");
266 return EFI_INVALID_PARAMETER;
267 }
268 if (*buffer_size < img.length) {
269 *buffer_size = img.length;
270 return EFI_BUFFER_TOO_SMALL;
271 }
272 memcpy(buffer, image, img.length);
273 *buffer_size = img.length;
274 return EFI_SUCCESS;
275}
276
277static struct efi_load_file_protocol lf_prot = {load_file};
278static struct efi_load_file_protocol lf2_prot = {load_file2};
279
280/*
281 * Setup unit test.
282 *
283 * Install an EFI_LOAD_FILE_PROTOCOL and an EFI_LOAD_FILE2_PROTOCOL.
284 *
285 * @handle: handle of the loaded image
286 * @systable: system table
287 * @return: EFI_ST_SUCCESS for success
288 */
289static int efi_st_load_file_setup(const efi_handle_t handle,
290 const struct efi_system_table *systable)
291{
292 efi_status_t ret;
293
294 image_handle = handle;
295 boottime = systable->boottime;
296
297 /* Load the application image into memory */
298 decompress(&image);
299
300 ret = boottime->install_multiple_protocol_interfaces(
301 &handle_lf,
302 &efi_st_guid_device_path,
303 &dp_lf_prot,
304 &efi_st_guid_load_file_protocol,
305 &lf_prot,
306 NULL);
307 if (ret != EFI_SUCCESS) {
308 efi_st_error("InstallMultipleProtocolInterfaces failed\n");
309 return EFI_ST_FAILURE;
310 }
311 ret = boottime->install_multiple_protocol_interfaces(
312 &handle_lf2,
313 &efi_st_guid_device_path,
314 &dp_lf2_prot,
315 &efi_st_guid_load_file2_protocol,
316 &lf2_prot,
317 NULL);
318 if (ret != EFI_SUCCESS) {
319 efi_st_error("InstallMultipleProtocolInterfaces failed\n");
320 return EFI_ST_FAILURE;
321 }
322
323 return EFI_ST_SUCCESS;
324}
325
326/*
327 * Tear down unit test.
328 *
329 * @return: EFI_ST_SUCCESS for success
330 */
331static int efi_st_load_file_teardown(void)
332{
333 efi_status_t ret = EFI_ST_SUCCESS;
334
335 if (handle_lf) {
336 ret = boottime->uninstall_multiple_protocol_interfaces(
337 handle_lf,
338 &efi_st_guid_device_path,
339 &dp_lf_prot,
340 &efi_st_guid_load_file_protocol,
341 &lf_prot,
342 NULL);
343 if (ret != EFI_SUCCESS) {
344 efi_st_error(
345 "UninstallMultipleProtocolInterfaces failed\n");
346 return EFI_ST_FAILURE;
347 }
348 }
349 if (handle_lf2) {
350 ret = boottime->uninstall_multiple_protocol_interfaces(
351 handle_lf2,
352 &efi_st_guid_device_path,
353 &dp_lf2_prot,
354 &efi_st_guid_load_file2_protocol,
355 &lf2_prot,
356 NULL);
357 if (ret != EFI_SUCCESS) {
358 efi_st_error(
359 "UninstallMultipleProtocolInterfaces failed\n");
360 return EFI_ST_FAILURE;
361 }
362 }
363
364 if (image) {
365 ret = boottime->free_pool(image);
366 if (ret != EFI_SUCCESS) {
367 efi_st_error("Failed to free image\n");
368 return EFI_ST_FAILURE;
369 }
370 }
371 return ret;
372}
373
374/*
375 * Execute unit test.
376 *
377 * Try loading an image via the EFI_LOAD_FILE_PROTOCOL and the
378 * EFI_LOAD_FILE2_PROTOCOL. Finally execute the image.
379 *
380 * @return: EFI_ST_SUCCESS for success
381 */
382static int efi_st_load_file_execute(void)
383{
384 efi_status_t ret;
385 efi_handle_t handle;
386 efi_uintn_t exit_data_size = 0;
387 u16 *exit_data = NULL;
388 u16 expected_text[] = EFI_ST_SUCCESS_STR;
389
390 load_file_call_count = 0;
391 load_file2_call_count = 0;
392 handle = NULL;
393 ret = boottime->load_image(true, image_handle, &dp_lf_file.v.dp, NULL,
394 0, &handle);
395 if (ret != EFI_SUCCESS) {
396 efi_st_error("Failed to load image\n");
397 return EFI_ST_FAILURE;
398 }
399 if (load_file2_call_count || !load_file_call_count) {
400 efi_st_error("Wrong image loaded\n");
401 return EFI_ST_FAILURE;
402 }
403 ret = boottime->unload_image(handle);
404 if (ret != EFI_SUCCESS) {
405 efi_st_error("Failed to unload image\n");
406 return EFI_ST_FAILURE;
407 }
408
409 load_file_call_count = 0;
410 load_file2_call_count = 0;
411 handle = NULL;
412 ret = boottime->load_image(false, image_handle, &dp_lf_file.v.dp, NULL,
413 0, &handle);
414 if (ret != EFI_SUCCESS) {
415 efi_st_error("Failed to load image\n");
416 return EFI_ST_FAILURE;
417 }
418 if (load_file2_call_count || !load_file_call_count) {
419 efi_st_error("Wrong image loaded\n");
420 return EFI_ST_FAILURE;
421 }
422 ret = boottime->unload_image(handle);
423 if (ret != EFI_SUCCESS) {
424 efi_st_error("Failed to unload image\n");
425 return EFI_ST_FAILURE;
426 }
427
428 ret = boottime->load_image(true, image_handle, &dp_lf2_file.v.dp, NULL,
429 0, &handle);
430 if (ret != EFI_NOT_FOUND) {
431 efi_st_error(
432 "Boot manager should not use LOAD_FILE2_PROTOCOL\n");
433 return EFI_ST_FAILURE;
434 }
435
436 load_file_call_count = 0;
437 load_file2_call_count = 0;
438 handle = NULL;
439 ret = boottime->load_image(false, image_handle, &dp_lf2_file.v.dp, NULL,
440 0, &handle);
441 if (ret != EFI_SUCCESS) {
442 efi_st_error("Failed to load image\n");
443 return EFI_ST_FAILURE;
444 }
445 if (!load_file2_call_count || load_file_call_count) {
446 efi_st_error("Wrong image loaded\n");
447 return EFI_ST_FAILURE;
448 }
449
450 ret = boottime->start_image(handle, &exit_data_size, &exit_data);
451 if (ret != EFI_UNSUPPORTED) {
452 efi_st_error("Wrong return value from application\n");
453 return EFI_ST_FAILURE;
454 }
455 if (!exit_data || exit_data_size != sizeof(expected_text) ||
456 memcmp(exit_data, expected_text, sizeof(expected_text))) {
457 efi_st_error("Incorrect exit data\n");
458 return EFI_ST_FAILURE;
459 }
460 ret = boottime->free_pool(exit_data);
461 if (ret != EFI_SUCCESS) {
462 efi_st_error("Failed to free exit data\n");
463 return EFI_ST_FAILURE;
464 }
465
466 return EFI_ST_SUCCESS;
467}
468
469EFI_UNIT_TEST(load_file_protocol) = {
470 .name = "load file protocol",
471 .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
472 .setup = efi_st_load_file_setup,
473 .execute = efi_st_load_file_execute,
474 .teardown = efi_st_load_file_teardown,
475};