uboot/lib/efi_selftest/efi_selftest_load_file.c
<<
>>
Prefs
   1// 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};
 476