1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * efi_selftest_loadimage 4 * 5 * Copyright (c) 2019 Heinrich Schuchardt <xypron.glpk@gmx.de> 6 * 7 * This test checks the LoadImage and StartImage boot service. 8 * 9 * The efi_selftest_miniapp_exit.efi application is loaded via a file device 10 * path and started. 11 */ 12 13 #include <efi_selftest.h> 14 /* Include containing the efi_selftest_miniapp_exit.efi application */ 15 #include "efi_miniapp_file_image_exit.h" 16 17 /* Block size of compressed disk image */ 18 #define COMPRESSED_DISK_IMAGE_BLOCK_SIZE 8 19 20 /* Binary logarithm of the block size */ 21 #define LB_BLOCK_SIZE 9 22 23 #define FILE_NAME L"app.efi" 24 #define VOLUME_NAME L"EfiDisk" 25 26 static struct efi_boot_services *boottime; 27 static efi_handle_t handle_image; 28 static efi_handle_t handle_volume; 29 30 static const efi_guid_t guid_device_path = DEVICE_PATH_GUID; 31 static const efi_guid_t guid_simple_file_system_protocol = 32 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID; 33 static const efi_guid_t guid_file_info = EFI_FILE_INFO_GUID; 34 static const efi_guid_t guid_file_system_info = EFI_FILE_SYSTEM_INFO_GUID; 35 36 /* One 8 byte block of the compressed disk image */ 37 struct line { 38 size_t addr; 39 char *line; 40 }; 41 42 /* Compressed file image */ 43 struct compressed_file_image { 44 size_t length; 45 struct line lines[]; 46 }; 47 48 /* File info including file name */ 49 struct file_info { 50 struct efi_file_info info; 51 u16 file_name[sizeof(FILE_NAME)]; 52 }; 53 54 /* File system info including volume name */ 55 struct file_system_info { 56 struct efi_file_system_info info; 57 u16 file_name[sizeof(VOLUME_NAME)]; 58 }; 59 60 /* Compressed file image */ 61 static struct compressed_file_image img = EFI_ST_DISK_IMG; 62 63 /* Pointer to decompressed file image */ 64 static u8 *image; 65 66 /* File info */ 67 static struct file_info priv_file_info = { 68 { 69 .size = sizeof(struct file_info), 70 .attribute = EFI_FILE_READ_ONLY, 71 }, 72 FILE_NAME, 73 }; 74 75 /* Pointer to file info */ 76 struct efi_file_info *file_info = &priv_file_info.info; 77 78 /* Volume device path */ 79 static struct { 80 struct efi_device_path_vendor vendor; 81 struct efi_device_path end; 82 } __packed dp_volume = { 83 .vendor = { 84 .dp = { 85 .type = DEVICE_PATH_TYPE_HARDWARE_DEVICE, 86 .sub_type = DEVICE_PATH_SUB_TYPE_VENDOR, 87 .length = sizeof(struct efi_device_path_vendor), 88 }, 89 .guid = EFI_GUID(0x4f9a0ebf, 0xa179, 0x88a6, 0x25, 0x68, 90 0x10, 0x72, 0xb1, 0x93, 0x51, 0x71), 91 }, 92 .end = { 93 .type = DEVICE_PATH_TYPE_END, 94 .sub_type = DEVICE_PATH_SUB_TYPE_END, 95 .length = sizeof(struct efi_device_path), 96 } 97 }; 98 99 /* File device path */ 100 static struct { 101 struct efi_device_path_vendor vendor; 102 struct efi_device_path path; 103 u16 file[sizeof(FILE_NAME)]; 104 struct efi_device_path end; 105 } __packed dp_file = { 106 .vendor = { 107 .dp = { 108 .type = DEVICE_PATH_TYPE_HARDWARE_DEVICE, 109 .sub_type = DEVICE_PATH_SUB_TYPE_VENDOR, 110 .length = sizeof(struct efi_device_path_vendor), 111 }, 112 .guid = EFI_GUID(0x4f9a0ebf, 0xa179, 0x88a6, 0x25, 0x68, 113 0x10, 0x72, 0xb1, 0x93, 0x51, 0x71), 114 }, 115 .path = { 116 .type = DEVICE_PATH_TYPE_MEDIA_DEVICE, 117 .sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH, 118 .length = sizeof(struct efi_device_path) + sizeof(dp_file.file), 119 }, 120 .file = FILE_NAME, 121 .end = { 122 .type = DEVICE_PATH_TYPE_END, 123 .sub_type = DEVICE_PATH_SUB_TYPE_END, 124 .length = sizeof(struct efi_device_path), 125 } 126 }; 127 128 /* File system info */ 129 static struct file_system_info priv_file_system_info = { 130 { 131 .size = sizeof(struct file_system_info), 132 .read_only = true, 133 .volume_size = 0x100000, 134 .free_space = 0x0, 135 .block_size = 0x200, 136 }, 137 VOLUME_NAME 138 }; 139 140 /* Pointer to file system info */ 141 static struct efi_file_system_info *file_system_info = 142 &priv_file_system_info.info; 143 144 /* Forward definitions of file and file system functions */ 145 static efi_status_t EFIAPI open_volume 146 (struct efi_simple_file_system_protocol *this, 147 struct efi_file_handle **root); 148 149 static efi_status_t EFIAPI open 150 (struct efi_file_handle *this, 151 struct efi_file_handle **new_handle, 152 u16 *file_name, u64 open_mode, u64 attributes); 153 154 static efi_status_t EFIAPI close(struct efi_file_handle *this); 155 156 static efi_status_t EFIAPI delete(struct efi_file_handle *this); 157 158 static efi_status_t EFIAPI read 159 (struct efi_file_handle *this, efi_uintn_t *buffer_size, void *buffer); 160 161 static efi_status_t EFIAPI write 162 (struct efi_file_handle *this, efi_uintn_t *buffer_size, void *buffer); 163 164 static efi_status_t EFIAPI getpos(struct efi_file_handle *this, u64 *pos); 165 166 static efi_status_t EFIAPI setpos(struct efi_file_handle *this, u64 pos); 167 168 static efi_status_t EFIAPI getinfo 169 (struct efi_file_handle *this, const efi_guid_t *info_type, 170 efi_uintn_t *buffer_size, void *buffer); 171 172 static efi_status_t EFIAPI setinfo 173 (struct efi_file_handle *this, const efi_guid_t *info_type, 174 efi_uintn_t buffer_size, void *buffer); 175 176 static efi_status_t EFIAPI flush(struct efi_file_handle *this); 177 178 /* Internal information about status of file system */ 179 static struct { 180 /* Difference of volume open count minus volume close count */ 181 int volume_open_count; 182 /* Difference of file open count minus file close count */ 183 int file_open_count; 184 /* File size */ 185 u64 file_size; 186 /* Current position in file */ 187 u64 file_pos; 188 } priv; 189 190 /* EFI_FILE_PROTOCOL for file */ 191 static struct efi_file_handle file = { 192 .rev = 0x00010000, 193 .open = open, 194 .close = close, 195 .delete = delete, 196 .read = read, 197 .write = write, 198 .getpos = getpos, 199 .setpos = setpos, 200 .getinfo = getinfo, 201 .setinfo = setinfo, 202 .flush = flush, 203 }; 204 205 /* EFI_FILE_PROTOCOL for root directory */ 206 static struct efi_file_handle volume = { 207 .rev = 0x00010000, 208 .open = open, 209 .close = close, 210 .delete = delete, 211 .read = read, 212 .write = write, 213 .getpos = getpos, 214 .setpos = setpos, 215 .getinfo = getinfo, 216 .setinfo = setinfo, 217 .flush = flush, 218 }; 219 220 /* EFI_SIMPLE_FILE_SYSTEM_PROTOCOL of the block device */ 221 struct efi_simple_file_system_protocol file_system = { 222 .rev = 0x00010000, 223 .open_volume = open_volume, 224 }; 225 226 static efi_status_t EFIAPI open_volume 227 (struct efi_simple_file_system_protocol *this, 228 struct efi_file_handle **root) 229 { 230 if (this != &file_system || !root) 231 return EFI_INVALID_PARAMETER; 232 233 *root = &volume; 234 priv.volume_open_count++; 235 236 return EFI_SUCCESS; 237 } 238 239 static efi_status_t EFIAPI open 240 (struct efi_file_handle *this, 241 struct efi_file_handle **new_handle, 242 u16 *file_name, u64 open_mode, u64 attributes) 243 { 244 if (this != &volume) 245 return EFI_INVALID_PARAMETER; 246 247 *new_handle = &file; 248 priv.file_pos = 0; 249 priv.file_open_count++; 250 251 return EFI_SUCCESS; 252 } 253 254 static efi_status_t EFIAPI close(struct efi_file_handle *this) 255 { 256 if (this == &file) 257 priv.file_open_count--; 258 else if (this == &volume) 259 priv.volume_open_count--; 260 else 261 return EFI_INVALID_PARAMETER; 262 263 return EFI_SUCCESS; 264 } 265 266 static efi_status_t EFIAPI delete(struct efi_file_handle *this) 267 { 268 if (this != &file) 269 return EFI_INVALID_PARAMETER; 270 271 return EFI_UNSUPPORTED; 272 } 273 274 static efi_status_t EFIAPI read 275 (struct efi_file_handle *this, efi_uintn_t *buffer_size, void *buffer) 276 { 277 if (this != &file) 278 return EFI_INVALID_PARAMETER; 279 280 if (priv.file_pos >= img.length) 281 *buffer_size = 0; 282 else if (priv.file_pos + *buffer_size > img.length) 283 *buffer_size = img.length - priv.file_pos; 284 285 boottime->copy_mem(buffer, &image[priv.file_pos], *buffer_size); 286 priv.file_pos += *buffer_size; 287 288 return EFI_SUCCESS; 289 } 290 291 static efi_status_t EFIAPI write 292 (struct efi_file_handle *this, efi_uintn_t *buffer_size, void *buffer) 293 { 294 if (this != &file) 295 return EFI_INVALID_PARAMETER; 296 297 return EFI_UNSUPPORTED; 298 } 299 300 static efi_status_t EFIAPI getpos(struct efi_file_handle *this, u64 *pos) 301 { 302 if (this != &file) 303 return EFI_INVALID_PARAMETER; 304 305 *pos = priv.file_pos; 306 307 return EFI_SUCCESS; 308 } 309 310 static efi_status_t EFIAPI setpos(struct efi_file_handle *this, u64 pos) 311 { 312 if (this != &file) 313 return EFI_INVALID_PARAMETER; 314 315 priv.file_pos = pos; 316 317 return EFI_SUCCESS; 318 } 319 320 static efi_status_t EFIAPI getinfo 321 (struct efi_file_handle *this, const efi_guid_t *info_type, 322 efi_uintn_t *buffer_size, void *buffer) 323 { 324 if (this == &file) { 325 if (efi_st_memcmp(info_type, &guid_file_info, 326 sizeof(efi_guid_t))) 327 return EFI_INVALID_PARAMETER; 328 if (*buffer_size >= sizeof(struct file_info)) { 329 boottime->copy_mem(buffer, file_info, 330 sizeof(struct file_info)); 331 } else { 332 *buffer_size = sizeof(struct file_info); 333 return EFI_BUFFER_TOO_SMALL; 334 } 335 } else if (this == &volume) { 336 if (efi_st_memcmp(info_type, &guid_file_system_info, 337 sizeof(efi_guid_t))) 338 return EFI_INVALID_PARAMETER; 339 if (*buffer_size >= sizeof(struct file_system_info)) { 340 boottime->copy_mem(buffer, file_system_info, 341 sizeof(struct file_system_info)); 342 } else { 343 *buffer_size = sizeof(struct file_system_info); 344 return EFI_BUFFER_TOO_SMALL; 345 } 346 } else { 347 return EFI_INVALID_PARAMETER; 348 } 349 return EFI_SUCCESS; 350 } 351 352 static efi_status_t EFIAPI setinfo 353 (struct efi_file_handle *this, const efi_guid_t *info_type, 354 efi_uintn_t buffer_size, void *buffer) 355 { 356 if (this != &file) 357 return EFI_INVALID_PARAMETER; 358 359 return EFI_UNSUPPORTED; 360 } 361 362 static efi_status_t EFIAPI flush(struct efi_file_handle *this) 363 { 364 if (this != &file) 365 return EFI_INVALID_PARAMETER; 366 367 return EFI_UNSUPPORTED; 368 } 369 370 /* 371 * Decompress the disk image. 372 * 373 * @image decompressed disk image 374 * @return status code 375 */ 376 static efi_status_t decompress(u8 **image) 377 { 378 u8 *buf; 379 size_t i; 380 size_t addr; 381 size_t len; 382 efi_status_t ret; 383 384 ret = boottime->allocate_pool(EFI_LOADER_DATA, img.length, 385 (void **)&buf); 386 if (ret != EFI_SUCCESS) { 387 efi_st_error("Out of memory\n"); 388 return ret; 389 } 390 boottime->set_mem(buf, img.length, 0); 391 392 for (i = 0; ; ++i) { 393 if (!img.lines[i].line) 394 break; 395 addr = img.lines[i].addr; 396 len = COMPRESSED_DISK_IMAGE_BLOCK_SIZE; 397 if (addr + len > img.length) 398 len = img.length - addr; 399 boottime->copy_mem(buf + addr, img.lines[i].line, len); 400 } 401 *image = buf; 402 priv.file_size = img.length; 403 file_info->file_size = img.length; 404 return ret; 405 } 406 407 /* 408 * Setup unit test. 409 * 410 * Decompress application image and provide a handle for the in memory block 411 * device. 412 * 413 * @handle: handle of the loaded image 414 * @systable: system table 415 * @return: EFI_ST_SUCCESS for success 416 */ 417 static int setup(const efi_handle_t handle, 418 const struct efi_system_table *systable) 419 { 420 efi_status_t ret; 421 422 handle_image = handle; 423 boottime = systable->boottime; 424 425 /* Load the application image into memory */ 426 decompress(&image); 427 428 ret = boottime->install_protocol_interface 429 (&handle_volume, &guid_device_path, EFI_NATIVE_INTERFACE, 430 &dp_volume); 431 if (ret != EFI_SUCCESS) { 432 efi_st_error("Failed to install device path\n"); 433 return EFI_ST_FAILURE; 434 } 435 ret = boottime->install_protocol_interface 436 (&handle_volume, &guid_simple_file_system_protocol, 437 EFI_NATIVE_INTERFACE, &file_system); 438 if (ret != EFI_SUCCESS) { 439 efi_st_error("Failed to install simple file system protocol\n"); 440 return EFI_ST_FAILURE; 441 } 442 443 return EFI_ST_SUCCESS; 444 } 445 446 /* 447 * Tear down unit test. 448 * 449 * Uninstall protocols and free memory. 450 * 451 * @return: EFI_ST_SUCCESS for success 452 */ 453 static int teardown(void) 454 { 455 efi_status_t ret = EFI_ST_SUCCESS; 456 457 if (handle_volume) { 458 ret = boottime->uninstall_protocol_interface 459 (handle_volume, &guid_simple_file_system_protocol, 460 &file_system); 461 if (ret != EFI_SUCCESS) { 462 efi_st_error 463 ("Failed to uninstall simple file system protocol\n"); 464 return EFI_ST_FAILURE; 465 } 466 ret = boottime->uninstall_protocol_interface 467 (handle_volume, &guid_device_path, &dp_volume); 468 if (ret != EFI_SUCCESS) { 469 efi_st_error 470 ("Failed to uninstall device path protocol\n"); 471 return EFI_ST_FAILURE; 472 } 473 } 474 475 if (image) { 476 ret = boottime->free_pool(image); 477 if (ret != EFI_SUCCESS) { 478 efi_st_error("Failed to free image\n"); 479 return EFI_ST_FAILURE; 480 } 481 } 482 return ret; 483 } 484 485 /* 486 * Execute unit test. 487 * 488 * Load and start the application image. 489 * 490 * @return: EFI_ST_SUCCESS for success 491 */ 492 static int execute(void) 493 { 494 efi_status_t ret; 495 efi_handle_t handle; 496 497 ret = boottime->load_image(false, handle_image, &dp_file.vendor.dp, 498 NULL, 0, &handle); 499 if (ret != EFI_SUCCESS) { 500 efi_st_error("Failed to load image\n"); 501 return EFI_ST_FAILURE; 502 } 503 ret = boottime->start_image(handle, NULL, NULL); 504 if (ret != EFI_UNSUPPORTED) { 505 efi_st_error("Wrong return value from application\n"); 506 return EFI_ST_FAILURE; 507 } 508 509 if (priv.file_open_count) { 510 efi_st_error("File open count = %d, expected 0\n", 511 priv.file_open_count); 512 return EFI_ST_FAILURE; 513 } 514 if (priv.volume_open_count) { 515 efi_st_error("Volume open count = %d, expected 0\n", 516 priv.volume_open_count); 517 return EFI_ST_FAILURE; 518 } 519 520 return EFI_ST_SUCCESS; 521 } 522 523 EFI_UNIT_TEST(loadimage) = { 524 .name = "load image from file", 525 .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, 526 .setup = setup, 527 .execute = execute, 528 .teardown = teardown, 529 }; 530