uboot/lib/efi_selftest/efi_selftest_block_device.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * efi_selftest_block
   4 *
   5 * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
   6 *
   7 * This test checks the driver for block IO devices.
   8 * A disk image is created in memory.
   9 * A handle is created for the new block IO device.
  10 * The block I/O protocol is installed on the handle.
  11 * ConnectController is used to setup partitions and to install the simple
  12 * file protocol.
  13 * A known file is read from the file system and verified.
  14 */
  15
  16#include <efi_selftest.h>
  17#include "efi_selftest_disk_image.h"
  18#include <asm/cache.h>
  19
  20/* Block size of compressed disk image */
  21#define COMPRESSED_DISK_IMAGE_BLOCK_SIZE 8
  22
  23/* Binary logarithm of the block size */
  24#define LB_BLOCK_SIZE 9
  25
  26static struct efi_boot_services *boottime;
  27
  28static const efi_guid_t block_io_protocol_guid = EFI_BLOCK_IO_PROTOCOL_GUID;
  29static const efi_guid_t guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID;
  30static const efi_guid_t guid_simple_file_system_protocol =
  31                                        EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
  32static const efi_guid_t guid_file_system_info = EFI_FILE_SYSTEM_INFO_GUID;
  33static efi_guid_t guid_vendor =
  34        EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d,
  35                 0x08, 0x72, 0x81, 0x9c, 0x65, 0x0c, 0xb7, 0xb8);
  36
  37static struct efi_device_path *dp;
  38
  39/* One 8 byte block of the compressed disk image */
  40struct line {
  41        size_t addr;
  42        char *line;
  43};
  44
  45/* Compressed disk image */
  46struct compressed_disk_image {
  47        size_t length;
  48        struct line lines[];
  49};
  50
  51static const struct compressed_disk_image img = EFI_ST_DISK_IMG;
  52
  53/* Decompressed disk image */
  54static u8 *image;
  55
  56/*
  57 * Reset service of the block IO protocol.
  58 *
  59 * @this        block IO protocol
  60 * @return      status code
  61 */
  62static efi_status_t EFIAPI reset(
  63                        struct efi_block_io *this,
  64                        char extended_verification)
  65{
  66        return EFI_SUCCESS;
  67}
  68
  69/*
  70 * Read service of the block IO protocol.
  71 *
  72 * @this        block IO protocol
  73 * @media_id    media id
  74 * @lba         start of the read in logical blocks
  75 * @buffer_size number of bytes to read
  76 * @buffer      target buffer
  77 * @return      status code
  78 */
  79static efi_status_t EFIAPI read_blocks(
  80                        struct efi_block_io *this, u32 media_id, u64 lba,
  81                        efi_uintn_t buffer_size, void *buffer)
  82{
  83        u8 *start;
  84
  85        if ((lba << LB_BLOCK_SIZE) + buffer_size > img.length)
  86                return EFI_INVALID_PARAMETER;
  87        start = image + (lba << LB_BLOCK_SIZE);
  88
  89        boottime->copy_mem(buffer, start, buffer_size);
  90
  91        return EFI_SUCCESS;
  92}
  93
  94/*
  95 * Write service of the block IO protocol.
  96 *
  97 * @this        block IO protocol
  98 * @media_id    media id
  99 * @lba         start of the write in logical blocks
 100 * @buffer_size number of bytes to read
 101 * @buffer      source buffer
 102 * @return      status code
 103 */
 104static efi_status_t EFIAPI write_blocks(
 105                        struct efi_block_io *this, u32 media_id, u64 lba,
 106                        efi_uintn_t buffer_size, void *buffer)
 107{
 108        u8 *start;
 109
 110        if ((lba << LB_BLOCK_SIZE) + buffer_size > img.length)
 111                return EFI_INVALID_PARAMETER;
 112        start = image + (lba << LB_BLOCK_SIZE);
 113
 114        boottime->copy_mem(start, buffer, buffer_size);
 115
 116        return EFI_SUCCESS;
 117}
 118
 119/*
 120 * Flush service of the block IO protocol.
 121 *
 122 * @this        block IO protocol
 123 * @return      status code
 124 */
 125static efi_status_t EFIAPI flush_blocks(struct efi_block_io *this)
 126{
 127        return EFI_SUCCESS;
 128}
 129
 130/*
 131 * Decompress the disk image.
 132 *
 133 * @image       decompressed disk image
 134 * @return      status code
 135 */
 136static efi_status_t decompress(u8 **image)
 137{
 138        u8 *buf;
 139        size_t i;
 140        size_t addr;
 141        size_t len;
 142        efi_status_t ret;
 143
 144        ret = boottime->allocate_pool(EFI_LOADER_DATA, img.length,
 145                                      (void **)&buf);
 146        if (ret != EFI_SUCCESS) {
 147                efi_st_error("Out of memory\n");
 148                return ret;
 149        }
 150        boottime->set_mem(buf, img.length, 0);
 151
 152        for (i = 0; ; ++i) {
 153                if (!img.lines[i].line)
 154                        break;
 155                addr = img.lines[i].addr;
 156                len = COMPRESSED_DISK_IMAGE_BLOCK_SIZE;
 157                if (addr + len > img.length)
 158                        len = img.length - addr;
 159                boottime->copy_mem(buf + addr, img.lines[i].line, len);
 160        }
 161        *image = buf;
 162        return ret;
 163}
 164
 165static struct efi_block_io_media media;
 166
 167static struct efi_block_io block_io = {
 168        .media = &media,
 169        .reset = reset,
 170        .read_blocks = read_blocks,
 171        .write_blocks = write_blocks,
 172        .flush_blocks = flush_blocks,
 173};
 174
 175/* Handle for the block IO device */
 176static efi_handle_t disk_handle;
 177
 178/*
 179 * Setup unit test.
 180 *
 181 * @handle:     handle of the loaded image
 182 * @systable:   system table
 183 * @return:     EFI_ST_SUCCESS for success
 184 */
 185static int setup(const efi_handle_t handle,
 186                 const struct efi_system_table *systable)
 187{
 188        efi_status_t ret;
 189        struct efi_device_path_vendor vendor_node;
 190        struct efi_device_path end_node;
 191
 192        boottime = systable->boottime;
 193
 194        decompress(&image);
 195
 196        block_io.media->block_size = 1 << LB_BLOCK_SIZE;
 197        block_io.media->last_block = (img.length >> LB_BLOCK_SIZE) - 1;
 198
 199        ret = boottime->install_protocol_interface(
 200                                &disk_handle, &block_io_protocol_guid,
 201                                EFI_NATIVE_INTERFACE, &block_io);
 202        if (ret != EFI_SUCCESS) {
 203                efi_st_error("Failed to install block I/O protocol\n");
 204                return EFI_ST_FAILURE;
 205        }
 206
 207        ret = boottime->allocate_pool(EFI_LOADER_DATA,
 208                                      sizeof(struct efi_device_path_vendor) +
 209                                      sizeof(struct efi_device_path),
 210                                      (void **)&dp);
 211        if (ret != EFI_SUCCESS) {
 212                efi_st_error("Out of memory\n");
 213                return EFI_ST_FAILURE;
 214        }
 215        vendor_node.dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
 216        vendor_node.dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR;
 217        vendor_node.dp.length = sizeof(struct efi_device_path_vendor);
 218
 219        boottime->copy_mem(&vendor_node.guid, &guid_vendor,
 220                           sizeof(efi_guid_t));
 221        boottime->copy_mem(dp, &vendor_node,
 222                           sizeof(struct efi_device_path_vendor));
 223        end_node.type = DEVICE_PATH_TYPE_END;
 224        end_node.sub_type = DEVICE_PATH_SUB_TYPE_END;
 225        end_node.length = sizeof(struct efi_device_path);
 226
 227        boottime->copy_mem((char *)dp + sizeof(struct efi_device_path_vendor),
 228                           &end_node, sizeof(struct efi_device_path));
 229        ret = boottime->install_protocol_interface(&disk_handle,
 230                                                   &guid_device_path,
 231                                                   EFI_NATIVE_INTERFACE,
 232                                                   dp);
 233        if (ret != EFI_SUCCESS) {
 234                efi_st_error("InstallProtocolInterface failed\n");
 235                return EFI_ST_FAILURE;
 236        }
 237        return EFI_ST_SUCCESS;
 238}
 239
 240/*
 241 * Tear down unit test.
 242 *
 243 * @return:     EFI_ST_SUCCESS for success
 244 */
 245static int teardown(void)
 246{
 247        efi_status_t r = EFI_ST_SUCCESS;
 248
 249        if (disk_handle) {
 250                r = boottime->uninstall_protocol_interface(disk_handle,
 251                                                           &guid_device_path,
 252                                                           dp);
 253                if (r != EFI_SUCCESS) {
 254                        efi_st_error("Uninstall device path failed\n");
 255                        return EFI_ST_FAILURE;
 256                }
 257                r = boottime->uninstall_protocol_interface(
 258                                disk_handle, &block_io_protocol_guid,
 259                                &block_io);
 260                if (r != EFI_SUCCESS) {
 261                        efi_st_error(
 262                                "Failed to uninstall block I/O protocol\n");
 263                        return EFI_ST_FAILURE;
 264                }
 265        }
 266
 267        if (image) {
 268                r = boottime->free_pool(image);
 269                if (r != EFI_SUCCESS) {
 270                        efi_st_error("Failed to free image\n");
 271                        return EFI_ST_FAILURE;
 272                }
 273        }
 274        return r;
 275}
 276
 277/*
 278 * Get length of device path without end tag.
 279 *
 280 * @dp          device path
 281 * @return      length of device path in bytes
 282 */
 283static efi_uintn_t dp_size(struct efi_device_path *dp)
 284{
 285        struct efi_device_path *pos = dp;
 286
 287        while (pos->type != DEVICE_PATH_TYPE_END)
 288                pos = (struct efi_device_path *)((char *)pos + pos->length);
 289        return (char *)pos - (char *)dp;
 290}
 291
 292/*
 293 * Execute unit test.
 294 *
 295 * @return:     EFI_ST_SUCCESS for success
 296 */
 297static int execute(void)
 298{
 299        efi_status_t ret;
 300        efi_uintn_t no_handles, i, len;
 301        efi_handle_t *handles;
 302        efi_handle_t handle_partition = NULL;
 303        struct efi_device_path *dp_partition;
 304        struct efi_block_io *block_io_protocol;
 305        struct efi_simple_file_system_protocol *file_system;
 306        struct efi_file_handle *root, *file;
 307        struct {
 308                struct efi_file_system_info info;
 309                u16 label[12];
 310        } system_info;
 311        efi_uintn_t buf_size;
 312        char buf[16] __aligned(ARCH_DMA_MINALIGN);
 313        u32 part1_size;
 314        u64 pos;
 315
 316        /* Connect controller to virtual disk */
 317        ret = boottime->connect_controller(disk_handle, NULL, NULL, 1);
 318        if (ret != EFI_SUCCESS) {
 319                efi_st_error("Failed to connect controller\n");
 320                return EFI_ST_FAILURE;
 321        }
 322
 323        /* Get the handle for the partition */
 324        ret = boottime->locate_handle_buffer(
 325                                BY_PROTOCOL, &guid_device_path, NULL,
 326                                &no_handles, &handles);
 327        if (ret != EFI_SUCCESS) {
 328                efi_st_error("Failed to locate handles\n");
 329                return EFI_ST_FAILURE;
 330        }
 331        len = dp_size(dp);
 332        for (i = 0; i < no_handles; ++i) {
 333                ret = boottime->open_protocol(handles[i], &guid_device_path,
 334                                              (void **)&dp_partition,
 335                                              NULL, NULL,
 336                                              EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 337                if (ret != EFI_SUCCESS) {
 338                        efi_st_error("Failed to open device path protocol\n");
 339                        return EFI_ST_FAILURE;
 340                }
 341                if (len >= dp_size(dp_partition))
 342                        continue;
 343                if (memcmp(dp, dp_partition, len))
 344                        continue;
 345                handle_partition = handles[i];
 346                break;
 347        }
 348        ret = boottime->free_pool(handles);
 349        if (ret != EFI_SUCCESS) {
 350                efi_st_error("Failed to free pool memory\n");
 351                return EFI_ST_FAILURE;
 352        }
 353        if (!handle_partition) {
 354                efi_st_error("Partition handle not found\n");
 355                return EFI_ST_FAILURE;
 356        }
 357
 358        /* Open the block_io_protocol */
 359        ret = boottime->open_protocol(handle_partition,
 360                                      &block_io_protocol_guid,
 361                                      (void **)&block_io_protocol, NULL, NULL,
 362                                      EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 363        if (ret != EFI_SUCCESS) {
 364                efi_st_error("Failed to open block IO protocol\n");
 365                return EFI_ST_FAILURE;
 366        }
 367        /* Get size of first MBR partition */
 368        memcpy(&part1_size, image + 0x1ca, sizeof(u32));
 369        if (block_io_protocol->media->last_block != part1_size - 1) {
 370                efi_st_error("Last LBA of partition %x, expected %x\n",
 371                             (unsigned int)block_io_protocol->media->last_block,
 372                             part1_size - 1);
 373                return EFI_ST_FAILURE;
 374        }
 375        /* Open the simple file system protocol */
 376        ret = boottime->open_protocol(handle_partition,
 377                                      &guid_simple_file_system_protocol,
 378                                      (void **)&file_system, NULL, NULL,
 379                                      EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 380        if (ret != EFI_SUCCESS) {
 381                efi_st_error("Failed to open simple file system protocol\n");
 382                return EFI_ST_FAILURE;
 383        }
 384
 385        /* Open volume */
 386        ret = file_system->open_volume(file_system, &root);
 387        if (ret != EFI_SUCCESS) {
 388                efi_st_error("Failed to open volume\n");
 389                return EFI_ST_FAILURE;
 390        }
 391        buf_size = sizeof(system_info);
 392        ret = root->getinfo(root, &guid_file_system_info, &buf_size,
 393                            &system_info);
 394        if (ret != EFI_SUCCESS) {
 395                efi_st_error("Failed to get file system info\n");
 396                return EFI_ST_FAILURE;
 397        }
 398        if (system_info.info.block_size != 512) {
 399                efi_st_error("Wrong block size %u, expected 512\n",
 400                             system_info.info.block_size);
 401                return EFI_ST_FAILURE;
 402        }
 403        if (efi_st_strcmp_16_8(system_info.info.volume_label, "U-BOOT TEST")) {
 404                efi_st_todo(
 405                        "Wrong volume label '%ps', expected 'U-BOOT TEST'\n",
 406                        system_info.info.volume_label);
 407        }
 408
 409        /* Read file */
 410        ret = root->open(root, &file, L"hello.txt", EFI_FILE_MODE_READ,
 411                         0);
 412        if (ret != EFI_SUCCESS) {
 413                efi_st_error("Failed to open file\n");
 414                return EFI_ST_FAILURE;
 415        }
 416        ret = file->setpos(file, 1);
 417        if (ret != EFI_SUCCESS) {
 418                efi_st_error("SetPosition failed\n");
 419                return EFI_ST_FAILURE;
 420        }
 421        buf_size = sizeof(buf) - 1;
 422        ret = file->read(file, &buf_size, buf);
 423        if (ret != EFI_SUCCESS) {
 424                efi_st_error("Failed to read file\n");
 425                return EFI_ST_FAILURE;
 426        }
 427        if (buf_size != 12) {
 428                efi_st_error("Wrong number of bytes read: %u\n",
 429                             (unsigned int)buf_size);
 430                return EFI_ST_FAILURE;
 431        }
 432        if (memcmp(buf, "ello world!", 11)) {
 433                efi_st_error("Unexpected file content\n");
 434                return EFI_ST_FAILURE;
 435        }
 436        ret = file->getpos(file, &pos);
 437        if (ret != EFI_SUCCESS) {
 438                efi_st_error("GetPosition failed\n");
 439                return EFI_ST_FAILURE;
 440        }
 441        if (pos != 13) {
 442                efi_st_error("GetPosition returned %u, expected 13\n",
 443                             (unsigned int)pos);
 444                return EFI_ST_FAILURE;
 445        }
 446        ret = file->close(file);
 447        if (ret != EFI_SUCCESS) {
 448                efi_st_error("Failed to close file\n");
 449                return EFI_ST_FAILURE;
 450        }
 451
 452#ifdef CONFIG_FAT_WRITE
 453        /* Write file */
 454        ret = root->open(root, &file, L"u-boot.txt", EFI_FILE_MODE_READ |
 455                         EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0);
 456        if (ret != EFI_SUCCESS) {
 457                efi_st_error("Failed to open file\n");
 458                return EFI_ST_FAILURE;
 459        }
 460        buf_size = 7;
 461        boottime->set_mem(buf, sizeof(buf), 0);
 462        boottime->copy_mem(buf, "U-Boot", buf_size);
 463        ret = file->write(file, &buf_size, buf);
 464        if (ret != EFI_SUCCESS || buf_size != 7) {
 465                efi_st_error("Failed to write file\n");
 466                return EFI_ST_FAILURE;
 467        }
 468        ret = file->getpos(file, &pos);
 469        if (ret != EFI_SUCCESS) {
 470                efi_st_error("GetPosition failed\n");
 471                return EFI_ST_FAILURE;
 472        }
 473        if (pos != 7) {
 474                efi_st_error("GetPosition returned %u, expected 7\n",
 475                             (unsigned int)pos);
 476                return EFI_ST_FAILURE;
 477        }
 478        ret = file->close(file);
 479        if (ret != EFI_SUCCESS) {
 480                efi_st_error("Failed to close file\n");
 481                return EFI_ST_FAILURE;
 482        }
 483
 484        /* Verify file */
 485        boottime->set_mem(buf, sizeof(buf), 0);
 486        ret = root->open(root, &file, L"u-boot.txt", EFI_FILE_MODE_READ,
 487                         0);
 488        if (ret != EFI_SUCCESS) {
 489                efi_st_error("Failed to open file\n");
 490                return EFI_ST_FAILURE;
 491        }
 492        buf_size = sizeof(buf) - 1;
 493        ret = file->read(file, &buf_size, buf);
 494        if (ret != EFI_SUCCESS) {
 495                efi_st_error("Failed to read file\n");
 496                return EFI_ST_FAILURE;
 497        }
 498        if (buf_size != 7) {
 499                efi_st_error("Wrong number of bytes read: %u\n",
 500                             (unsigned int)buf_size);
 501                return EFI_ST_FAILURE;
 502        }
 503        if (memcmp(buf, "U-Boot", 7)) {
 504                efi_st_error("Unexpected file content %s\n", buf);
 505                return EFI_ST_FAILURE;
 506        }
 507        ret = file->close(file);
 508        if (ret != EFI_SUCCESS) {
 509                efi_st_error("Failed to close file\n");
 510                return EFI_ST_FAILURE;
 511        }
 512#else
 513        efi_st_todo("CONFIG_FAT_WRITE is not set\n");
 514#endif /* CONFIG_FAT_WRITE */
 515
 516        /* Close volume */
 517        ret = root->close(root);
 518        if (ret != EFI_SUCCESS) {
 519                efi_st_error("Failed to close volume\n");
 520                return EFI_ST_FAILURE;
 521        }
 522
 523        return EFI_ST_SUCCESS;
 524}
 525
 526EFI_UNIT_TEST(blkdev) = {
 527        .name = "block device",
 528        .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
 529        .setup = setup,
 530        .execute = execute,
 531        .teardown = teardown,
 532};
 533