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 u"app.efi"
  24#define VOLUME_NAME u"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 efi_st_open_volume
 146        (struct efi_simple_file_system_protocol *this,
 147         struct efi_file_handle **root);
 148
 149static efi_status_t EFIAPI efi_st_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 efi_st_close(struct efi_file_handle *this);
 155
 156static efi_status_t EFIAPI efi_st_delete(struct efi_file_handle *this);
 157
 158static efi_status_t EFIAPI efi_st_read
 159        (struct efi_file_handle *this, efi_uintn_t *buffer_size, void *buffer);
 160
 161static efi_status_t EFIAPI efi_st_write
 162        (struct efi_file_handle *this, efi_uintn_t *buffer_size, void *buffer);
 163
 164static efi_status_t EFIAPI efi_st_getpos(struct efi_file_handle *this,
 165                                         u64 *pos);
 166
 167static efi_status_t EFIAPI efi_st_setpos(struct efi_file_handle *this, u64 pos);
 168
 169static efi_status_t EFIAPI efi_st_getinfo
 170        (struct efi_file_handle *this, const efi_guid_t *info_type,
 171         efi_uintn_t *buffer_size, void *buffer);
 172
 173static efi_status_t EFIAPI efi_st_setinfo
 174        (struct efi_file_handle *this, const efi_guid_t *info_type,
 175         efi_uintn_t buffer_size, void *buffer);
 176
 177static efi_status_t EFIAPI efi_st_flush(struct efi_file_handle *this);
 178
 179/* Internal information about status of file system */
 180static struct {
 181        /* Difference of volume open count minus volume close count */
 182        int volume_open_count;
 183        /* Difference of file open count minus file close count */
 184        int file_open_count;
 185        /* File size */
 186        u64 file_size;
 187        /* Current position in file */
 188        u64 file_pos;
 189} priv;
 190
 191/* EFI_FILE_PROTOCOL for file */
 192static struct efi_file_handle file = {
 193        .rev = 0x00010000,
 194        .open = efi_st_open,
 195        .close = efi_st_close,
 196        .delete = efi_st_delete,
 197        .read = efi_st_read,
 198        .write = efi_st_write,
 199        .getpos = efi_st_getpos,
 200        .setpos = efi_st_setpos,
 201        .getinfo = efi_st_getinfo,
 202        .setinfo = efi_st_setinfo,
 203        .flush = efi_st_flush,
 204};
 205
 206/* EFI_FILE_PROTOCOL for root directory */
 207static struct efi_file_handle volume = {
 208        .rev = 0x00010000,
 209        .open = efi_st_open,
 210        .close = efi_st_close,
 211        .delete = efi_st_delete,
 212        .read = efi_st_read,
 213        .write = efi_st_write,
 214        .getpos = efi_st_getpos,
 215        .setpos = efi_st_setpos,
 216        .getinfo = efi_st_getinfo,
 217        .setinfo = efi_st_setinfo,
 218        .flush = efi_st_flush,
 219};
 220
 221/* EFI_SIMPLE_FILE_SYSTEM_PROTOCOL of the block device */
 222struct efi_simple_file_system_protocol file_system = {
 223        .rev = 0x00010000,
 224        .open_volume = efi_st_open_volume,
 225};
 226
 227static efi_status_t EFIAPI efi_st_open_volume
 228        (struct efi_simple_file_system_protocol *this,
 229         struct efi_file_handle **root)
 230{
 231        if (this != &file_system || !root)
 232                return EFI_INVALID_PARAMETER;
 233
 234        *root = &volume;
 235        priv.volume_open_count++;
 236
 237        return EFI_SUCCESS;
 238}
 239
 240static efi_status_t EFIAPI efi_st_open
 241        (struct efi_file_handle *this,
 242         struct efi_file_handle **new_handle,
 243         u16 *file_name, u64 open_mode, u64 attributes)
 244{
 245        if (this != &volume)
 246                return EFI_INVALID_PARAMETER;
 247
 248        *new_handle = &file;
 249        priv.file_pos = 0;
 250        priv.file_open_count++;
 251
 252        return EFI_SUCCESS;
 253}
 254
 255static efi_status_t EFIAPI efi_st_close(struct efi_file_handle *this)
 256{
 257        if (this == &file)
 258                priv.file_open_count--;
 259        else if (this == &volume)
 260                priv.volume_open_count--;
 261        else
 262                return EFI_INVALID_PARAMETER;
 263
 264        return EFI_SUCCESS;
 265}
 266
 267static efi_status_t EFIAPI efi_st_delete(struct efi_file_handle *this)
 268{
 269        if (this != &file)
 270                return EFI_INVALID_PARAMETER;
 271
 272        return EFI_UNSUPPORTED;
 273}
 274
 275static efi_status_t EFIAPI efi_st_read
 276        (struct efi_file_handle *this, efi_uintn_t *buffer_size, void *buffer)
 277{
 278        if (this != &file)
 279                return EFI_INVALID_PARAMETER;
 280
 281        if (priv.file_pos >= img.length)
 282                *buffer_size = 0;
 283        else if (priv.file_pos + *buffer_size > img.length)
 284                *buffer_size = img.length - priv.file_pos;
 285
 286        boottime->copy_mem(buffer, &image[priv.file_pos], *buffer_size);
 287        priv.file_pos += *buffer_size;
 288
 289        return EFI_SUCCESS;
 290}
 291
 292static efi_status_t EFIAPI efi_st_write
 293        (struct efi_file_handle *this, efi_uintn_t *buffer_size, void *buffer)
 294{
 295        if (this != &file)
 296                return EFI_INVALID_PARAMETER;
 297
 298        return EFI_UNSUPPORTED;
 299}
 300
 301static efi_status_t EFIAPI efi_st_getpos(struct efi_file_handle *this, u64 *pos)
 302{
 303        if (this != &file)
 304                return EFI_INVALID_PARAMETER;
 305
 306        *pos = priv.file_pos;
 307
 308        return EFI_SUCCESS;
 309}
 310
 311static efi_status_t EFIAPI efi_st_setpos(struct efi_file_handle *this, u64 pos)
 312{
 313        if (this != &file)
 314                return EFI_INVALID_PARAMETER;
 315
 316        priv.file_pos = pos;
 317
 318        return EFI_SUCCESS;
 319}
 320
 321static efi_status_t EFIAPI efi_st_getinfo
 322        (struct efi_file_handle *this, const efi_guid_t *info_type,
 323         efi_uintn_t *buffer_size, void *buffer)
 324{
 325        if (this == &file) {
 326                if (memcmp(info_type, &guid_file_info, 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 (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
 352static efi_status_t EFIAPI efi_st_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
 362static efi_status_t EFIAPI efi_st_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 */
 376static 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 */
 417static 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 */
 453static 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 */
 492static 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
 523EFI_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