uboot/lib/efi_selftest/efi_selftest_loadimage.c
<<
>>
Prefs
   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
  26static struct efi_boot_services *boottime;
  27static efi_handle_t handle_image;
  28static efi_handle_t handle_volume;
  29
  30static const efi_guid_t guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID;
  31static const efi_guid_t guid_simple_file_system_protocol =
  32                EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
  33static const efi_guid_t guid_file_info = EFI_FILE_INFO_GUID;
  34static const efi_guid_t guid_file_system_info = EFI_FILE_SYSTEM_INFO_GUID;
  35
  36/* One 8 byte block of the compressed disk image */
  37struct line {
  38        size_t addr;
  39        char *line;
  40};
  41
  42/* Compressed file image */
  43struct compressed_file_image {
  44        size_t length;
  45        struct line lines[];
  46};
  47
  48/* File info including file name */
  49struct file_info {
  50        struct efi_file_info info;
  51        u16 file_name[sizeof(FILE_NAME)];
  52};
  53
  54/* File system info including volume name */
  55struct file_system_info {
  56        struct efi_file_system_info info;
  57        u16 file_name[sizeof(VOLUME_NAME)];
  58};
  59
  60/* Compressed file image */
  61static struct compressed_file_image img = EFI_ST_DISK_IMG;
  62
  63/* Pointer to decompressed file image */
  64static u8 *image;
  65
  66/* File info */
  67static 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 */
  76struct efi_file_info *file_info = &priv_file_info.info;
  77
  78/* Volume device path */
  79static 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 */
 100static 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 */
 129static 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 */
 141static struct efi_file_system_info *file_system_info =
 142        &priv_file_system_info.info;
 143
 144/* Forward definitions of file and file system functions */
 145static efi_status_t EFIAPI open_volume
 146        (struct efi_simple_file_system_protocol *this,
 147         struct efi_file_handle **root);
 148
 149static 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
 154static efi_status_t EFIAPI close(struct efi_file_handle *this);
 155
 156static efi_status_t EFIAPI delete(struct efi_file_handle *this);
 157
 158static efi_status_t EFIAPI read
 159        (struct efi_file_handle *this, efi_uintn_t *buffer_size, void *buffer);
 160
 161static efi_status_t EFIAPI write
 162        (struct efi_file_handle *this, efi_uintn_t *buffer_size, void *buffer);
 163
 164static efi_status_t EFIAPI getpos(struct efi_file_handle *this, u64 *pos);
 165
 166static efi_status_t EFIAPI setpos(struct efi_file_handle *this, u64 pos);
 167
 168static 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
 172static 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
 176static efi_status_t EFIAPI flush(struct efi_file_handle *this);
 177
 178/* Internal information about status of file system */
 179static 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 */
 191static 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 */
 206static 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 */
 221struct efi_simple_file_system_protocol file_system = {
 222        .rev = 0x00010000,
 223        .open_volume = open_volume,
 224};
 225
 226static 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
 239static 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
 254static 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
 266static 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
 274static 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
 291static 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
 300static 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
 310static 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
 320static 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 (memcmp(info_type, &guid_file_info, sizeof(efi_guid_t)))
 326                        return EFI_INVALID_PARAMETER;
 327                if (*buffer_size >= sizeof(struct file_info)) {
 328                        boottime->copy_mem(buffer, file_info,
 329                                           sizeof(struct file_info));
 330                } else {
 331                        *buffer_size = sizeof(struct file_info);
 332                        return EFI_BUFFER_TOO_SMALL;
 333                }
 334        } else if (this == &volume) {
 335                if (memcmp(info_type, &guid_file_system_info,
 336                           sizeof(efi_guid_t)))
 337                        return EFI_INVALID_PARAMETER;
 338                if (*buffer_size >= sizeof(struct file_system_info)) {
 339                        boottime->copy_mem(buffer, file_system_info,
 340                                           sizeof(struct file_system_info));
 341                } else {
 342                        *buffer_size = sizeof(struct file_system_info);
 343                        return EFI_BUFFER_TOO_SMALL;
 344                }
 345        } else {
 346                return EFI_INVALID_PARAMETER;
 347        }
 348        return EFI_SUCCESS;
 349}
 350
 351static efi_status_t EFIAPI setinfo
 352        (struct efi_file_handle *this, const efi_guid_t *info_type,
 353         efi_uintn_t buffer_size, void *buffer)
 354{
 355        if (this != &file)
 356                return EFI_INVALID_PARAMETER;
 357
 358        return EFI_UNSUPPORTED;
 359}
 360
 361static efi_status_t EFIAPI flush(struct efi_file_handle *this)
 362{
 363        if (this != &file)
 364                return EFI_INVALID_PARAMETER;
 365
 366        return EFI_UNSUPPORTED;
 367}
 368
 369/*
 370 * Decompress the disk image.
 371 *
 372 * @image       decompressed disk image
 373 * @return      status code
 374 */
 375static efi_status_t decompress(u8 **image)
 376{
 377        u8 *buf;
 378        size_t i;
 379        size_t addr;
 380        size_t len;
 381        efi_status_t ret;
 382
 383        ret = boottime->allocate_pool(EFI_LOADER_DATA, img.length,
 384                                      (void **)&buf);
 385        if (ret != EFI_SUCCESS) {
 386                efi_st_error("Out of memory\n");
 387                return ret;
 388        }
 389        boottime->set_mem(buf, img.length, 0);
 390
 391        for (i = 0; ; ++i) {
 392                if (!img.lines[i].line)
 393                        break;
 394                addr = img.lines[i].addr;
 395                len = COMPRESSED_DISK_IMAGE_BLOCK_SIZE;
 396                if (addr + len > img.length)
 397                        len = img.length - addr;
 398                boottime->copy_mem(buf + addr, img.lines[i].line, len);
 399        }
 400        *image = buf;
 401        priv.file_size = img.length;
 402        file_info->file_size = img.length;
 403        return ret;
 404}
 405
 406/*
 407 * Setup unit test.
 408 *
 409 * Decompress application image and provide a handle for the in memory block
 410 * device.
 411 *
 412 * @handle:     handle of the loaded image
 413 * @systable:   system table
 414 * @return:     EFI_ST_SUCCESS for success
 415 */
 416static int setup(const efi_handle_t handle,
 417                 const struct efi_system_table *systable)
 418{
 419        efi_status_t ret;
 420
 421        handle_image = handle;
 422        boottime = systable->boottime;
 423
 424        /* Load the application image into memory */
 425        decompress(&image);
 426
 427        ret = boottime->install_protocol_interface
 428                (&handle_volume, &guid_device_path, EFI_NATIVE_INTERFACE,
 429                 &dp_volume);
 430        if (ret != EFI_SUCCESS) {
 431                efi_st_error("Failed to install device path\n");
 432                return EFI_ST_FAILURE;
 433        }
 434        ret = boottime->install_protocol_interface
 435                (&handle_volume, &guid_simple_file_system_protocol,
 436                 EFI_NATIVE_INTERFACE, &file_system);
 437        if (ret != EFI_SUCCESS) {
 438                efi_st_error("Failed to install simple file system protocol\n");
 439                return EFI_ST_FAILURE;
 440        }
 441
 442        return EFI_ST_SUCCESS;
 443}
 444
 445/*
 446 * Tear down unit test.
 447 *
 448 * Uninstall protocols and free memory.
 449 *
 450 * @return:     EFI_ST_SUCCESS for success
 451 */
 452static int teardown(void)
 453{
 454        efi_status_t ret = EFI_ST_SUCCESS;
 455
 456        if (handle_volume) {
 457                ret = boottime->uninstall_protocol_interface
 458                        (handle_volume, &guid_simple_file_system_protocol,
 459                         &file_system);
 460                if (ret != EFI_SUCCESS) {
 461                        efi_st_error
 462                                ("Failed to uninstall simple file system protocol\n");
 463                        return EFI_ST_FAILURE;
 464                }
 465                ret = boottime->uninstall_protocol_interface
 466                        (handle_volume, &guid_device_path, &dp_volume);
 467                if (ret != EFI_SUCCESS) {
 468                        efi_st_error
 469                                ("Failed to uninstall device path protocol\n");
 470                        return EFI_ST_FAILURE;
 471                }
 472        }
 473
 474        if (image) {
 475                ret = boottime->free_pool(image);
 476                if (ret != EFI_SUCCESS) {
 477                        efi_st_error("Failed to free image\n");
 478                        return EFI_ST_FAILURE;
 479                }
 480        }
 481        return ret;
 482}
 483
 484/*
 485 * Execute unit test.
 486 *
 487 * Load and start the application image.
 488 *
 489 * @return:     EFI_ST_SUCCESS for success
 490 */
 491static int execute(void)
 492{
 493        efi_status_t ret;
 494        efi_handle_t handle;
 495
 496        ret = boottime->load_image(false, handle_image, &dp_file.vendor.dp,
 497                                   NULL, 0, &handle);
 498        if (ret != EFI_SUCCESS) {
 499                efi_st_error("Failed to load image\n");
 500                return EFI_ST_FAILURE;
 501        }
 502        ret = boottime->start_image(handle, NULL, NULL);
 503        if (ret != EFI_UNSUPPORTED) {
 504                efi_st_error("Wrong return value from application\n");
 505                return EFI_ST_FAILURE;
 506        }
 507
 508        if (priv.file_open_count) {
 509                efi_st_error("File open count = %d, expected 0\n",
 510                             priv.file_open_count);
 511                return EFI_ST_FAILURE;
 512        }
 513        if (priv.volume_open_count) {
 514                efi_st_error("Volume open count = %d, expected 0\n",
 515                             priv.volume_open_count);
 516                return EFI_ST_FAILURE;
 517        }
 518
 519        return EFI_ST_SUCCESS;
 520}
 521
 522EFI_UNIT_TEST(loadimage) = {
 523        .name = "load image from file",
 524        .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
 525        .setup = setup,
 526        .execute = execute,
 527        .teardown = teardown,
 528};
 529