uboot/lib/efi_loader/efi_disk.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 *  EFI application disk support
   4 *
   5 *  Copyright (c) 2016 Alexander Graf
   6 */
   7
   8#include <common.h>
   9#include <blk.h>
  10#include <dm.h>
  11#include <efi_loader.h>
  12#include <fs.h>
  13#include <part.h>
  14#include <malloc.h>
  15
  16const efi_guid_t efi_block_io_guid = EFI_BLOCK_IO_PROTOCOL_GUID;
  17
  18/**
  19 * struct efi_disk_obj - EFI disk object
  20 *
  21 * @header:     EFI object header
  22 * @ops:        EFI disk I/O protocol interface
  23 * @ifname:     interface name for block device
  24 * @dev_index:  device index of block device
  25 * @media:      block I/O media information
  26 * @dp:         device path to the block device
  27 * @part:       partition
  28 * @volume:     simple file system protocol of the partition
  29 * @offset:     offset into disk for simple partition
  30 * @desc:       internal block device descriptor
  31 */
  32struct efi_disk_obj {
  33        struct efi_object header;
  34        struct efi_block_io ops;
  35        const char *ifname;
  36        int dev_index;
  37        struct efi_block_io_media media;
  38        struct efi_device_path *dp;
  39        unsigned int part;
  40        struct efi_simple_file_system_protocol *volume;
  41        lbaint_t offset;
  42        struct blk_desc *desc;
  43};
  44
  45/**
  46 * efi_disk_reset() - reset block device
  47 *
  48 * This function implements the Reset service of the EFI_BLOCK_IO_PROTOCOL.
  49 *
  50 * As U-Boot's block devices do not have a reset function simply return
  51 * EFI_SUCCESS.
  52 *
  53 * See the Unified Extensible Firmware Interface (UEFI) specification for
  54 * details.
  55 *
  56 * @this:                       pointer to the BLOCK_IO_PROTOCOL
  57 * @extended_verification:      extended verification
  58 * Return:                      status code
  59 */
  60static efi_status_t EFIAPI efi_disk_reset(struct efi_block_io *this,
  61                        char extended_verification)
  62{
  63        EFI_ENTRY("%p, %x", this, extended_verification);
  64        return EFI_EXIT(EFI_SUCCESS);
  65}
  66
  67enum efi_disk_direction {
  68        EFI_DISK_READ,
  69        EFI_DISK_WRITE,
  70};
  71
  72static efi_status_t efi_disk_rw_blocks(struct efi_block_io *this,
  73                        u32 media_id, u64 lba, unsigned long buffer_size,
  74                        void *buffer, enum efi_disk_direction direction)
  75{
  76        struct efi_disk_obj *diskobj;
  77        struct blk_desc *desc;
  78        int blksz;
  79        int blocks;
  80        unsigned long n;
  81
  82        diskobj = container_of(this, struct efi_disk_obj, ops);
  83        desc = (struct blk_desc *) diskobj->desc;
  84        blksz = desc->blksz;
  85        blocks = buffer_size / blksz;
  86        lba += diskobj->offset;
  87
  88        EFI_PRINT("blocks=%x lba=%llx blksz=%x dir=%d\n",
  89                  blocks, lba, blksz, direction);
  90
  91        /* We only support full block access */
  92        if (buffer_size & (blksz - 1))
  93                return EFI_BAD_BUFFER_SIZE;
  94
  95        if (direction == EFI_DISK_READ)
  96                n = blk_dread(desc, lba, blocks, buffer);
  97        else
  98                n = blk_dwrite(desc, lba, blocks, buffer);
  99
 100        /* We don't do interrupts, so check for timers cooperatively */
 101        efi_timer_check();
 102
 103        EFI_PRINT("n=%lx blocks=%x\n", n, blocks);
 104
 105        if (n != blocks)
 106                return EFI_DEVICE_ERROR;
 107
 108        return EFI_SUCCESS;
 109}
 110
 111static efi_status_t EFIAPI efi_disk_read_blocks(struct efi_block_io *this,
 112                        u32 media_id, u64 lba, efi_uintn_t buffer_size,
 113                        void *buffer)
 114{
 115        void *real_buffer = buffer;
 116        efi_status_t r;
 117
 118        if (!this)
 119                return EFI_INVALID_PARAMETER;
 120        /* TODO: check for media changes */
 121        if (media_id != this->media->media_id)
 122                return EFI_MEDIA_CHANGED;
 123        if (!this->media->media_present)
 124                return EFI_NO_MEDIA;
 125        /* media->io_align is a power of 2 */
 126        if ((uintptr_t)buffer & (this->media->io_align - 1))
 127                return EFI_INVALID_PARAMETER;
 128        if (lba * this->media->block_size + buffer_size >
 129            this->media->last_block * this->media->block_size)
 130                return EFI_INVALID_PARAMETER;
 131
 132#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
 133        if (buffer_size > EFI_LOADER_BOUNCE_BUFFER_SIZE) {
 134                r = efi_disk_read_blocks(this, media_id, lba,
 135                        EFI_LOADER_BOUNCE_BUFFER_SIZE, buffer);
 136                if (r != EFI_SUCCESS)
 137                        return r;
 138                return efi_disk_read_blocks(this, media_id, lba +
 139                        EFI_LOADER_BOUNCE_BUFFER_SIZE / this->media->block_size,
 140                        buffer_size - EFI_LOADER_BOUNCE_BUFFER_SIZE,
 141                        buffer + EFI_LOADER_BOUNCE_BUFFER_SIZE);
 142        }
 143
 144        real_buffer = efi_bounce_buffer;
 145#endif
 146
 147        EFI_ENTRY("%p, %x, %llx, %zx, %p", this, media_id, lba,
 148                  buffer_size, buffer);
 149
 150        r = efi_disk_rw_blocks(this, media_id, lba, buffer_size, real_buffer,
 151                               EFI_DISK_READ);
 152
 153        /* Copy from bounce buffer to real buffer if necessary */
 154        if ((r == EFI_SUCCESS) && (real_buffer != buffer))
 155                memcpy(buffer, real_buffer, buffer_size);
 156
 157        return EFI_EXIT(r);
 158}
 159
 160static efi_status_t EFIAPI efi_disk_write_blocks(struct efi_block_io *this,
 161                        u32 media_id, u64 lba, efi_uintn_t buffer_size,
 162                        void *buffer)
 163{
 164        void *real_buffer = buffer;
 165        efi_status_t r;
 166
 167        if (!this)
 168                return EFI_INVALID_PARAMETER;
 169        if (this->media->read_only)
 170                return EFI_WRITE_PROTECTED;
 171        /* TODO: check for media changes */
 172        if (media_id != this->media->media_id)
 173                return EFI_MEDIA_CHANGED;
 174        if (!this->media->media_present)
 175                return EFI_NO_MEDIA;
 176        /* media->io_align is a power of 2 */
 177        if ((uintptr_t)buffer & (this->media->io_align - 1))
 178                return EFI_INVALID_PARAMETER;
 179        if (lba * this->media->block_size + buffer_size >
 180            this->media->last_block * this->media->block_size)
 181                return EFI_INVALID_PARAMETER;
 182
 183#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
 184        if (buffer_size > EFI_LOADER_BOUNCE_BUFFER_SIZE) {
 185                r = efi_disk_write_blocks(this, media_id, lba,
 186                        EFI_LOADER_BOUNCE_BUFFER_SIZE, buffer);
 187                if (r != EFI_SUCCESS)
 188                        return r;
 189                return efi_disk_write_blocks(this, media_id, lba +
 190                        EFI_LOADER_BOUNCE_BUFFER_SIZE / this->media->block_size,
 191                        buffer_size - EFI_LOADER_BOUNCE_BUFFER_SIZE,
 192                        buffer + EFI_LOADER_BOUNCE_BUFFER_SIZE);
 193        }
 194
 195        real_buffer = efi_bounce_buffer;
 196#endif
 197
 198        EFI_ENTRY("%p, %x, %llx, %zx, %p", this, media_id, lba,
 199                  buffer_size, buffer);
 200
 201        /* Populate bounce buffer if necessary */
 202        if (real_buffer != buffer)
 203                memcpy(real_buffer, buffer, buffer_size);
 204
 205        r = efi_disk_rw_blocks(this, media_id, lba, buffer_size, real_buffer,
 206                               EFI_DISK_WRITE);
 207
 208        return EFI_EXIT(r);
 209}
 210
 211static efi_status_t EFIAPI efi_disk_flush_blocks(struct efi_block_io *this)
 212{
 213        /* We always write synchronously */
 214        EFI_ENTRY("%p", this);
 215        return EFI_EXIT(EFI_SUCCESS);
 216}
 217
 218static const struct efi_block_io block_io_disk_template = {
 219        .reset = &efi_disk_reset,
 220        .read_blocks = &efi_disk_read_blocks,
 221        .write_blocks = &efi_disk_write_blocks,
 222        .flush_blocks = &efi_disk_flush_blocks,
 223};
 224
 225/*
 226 * Get the simple file system protocol for a file device path.
 227 *
 228 * The full path provided is split into device part and into a file
 229 * part. The device part is used to find the handle on which the
 230 * simple file system protocol is installed.
 231 *
 232 * @full_path   device path including device and file
 233 * @return      simple file system protocol
 234 */
 235struct efi_simple_file_system_protocol *
 236efi_fs_from_path(struct efi_device_path *full_path)
 237{
 238        struct efi_object *efiobj;
 239        struct efi_handler *handler;
 240        struct efi_device_path *device_path;
 241        struct efi_device_path *file_path;
 242        efi_status_t ret;
 243
 244        /* Split the path into a device part and a file part */
 245        ret = efi_dp_split_file_path(full_path, &device_path, &file_path);
 246        if (ret != EFI_SUCCESS)
 247                return NULL;
 248        efi_free_pool(file_path);
 249
 250        /* Get the EFI object for the partition */
 251        efiobj = efi_dp_find_obj(device_path, NULL);
 252        efi_free_pool(device_path);
 253        if (!efiobj)
 254                return NULL;
 255
 256        /* Find the simple file system protocol */
 257        ret = efi_search_protocol(efiobj, &efi_simple_file_system_protocol_guid,
 258                                  &handler);
 259        if (ret != EFI_SUCCESS)
 260                return NULL;
 261
 262        /* Return the simple file system protocol for the partition */
 263        return handler->protocol_interface;
 264}
 265
 266/**
 267 * efi_fs_exists() - check if a partition bears a file system
 268 *
 269 * @desc:       block device descriptor
 270 * @part:       partition number
 271 * Return:      1 if a file system exists on the partition
 272 *              0 otherwise
 273 */
 274static int efi_fs_exists(struct blk_desc *desc, int part)
 275{
 276        if (fs_set_blk_dev_with_part(desc, part))
 277                return 0;
 278
 279        if (fs_get_type() == FS_TYPE_ANY)
 280                return 0;
 281
 282        fs_close();
 283
 284        return 1;
 285}
 286
 287/*
 288 * Create a handle for a partition or disk
 289 *
 290 * @parent      parent handle
 291 * @dp_parent   parent device path
 292 * @if_typename interface name for block device
 293 * @desc        internal block device
 294 * @dev_index   device index for block device
 295 * @offset      offset into disk for simple partitions
 296 * @return      disk object
 297 */
 298static efi_status_t efi_disk_add_dev(
 299                                efi_handle_t parent,
 300                                struct efi_device_path *dp_parent,
 301                                const char *if_typename,
 302                                struct blk_desc *desc,
 303                                int dev_index,
 304                                lbaint_t offset,
 305                                unsigned int part,
 306                                struct efi_disk_obj **disk)
 307{
 308        struct efi_disk_obj *diskobj;
 309        efi_status_t ret;
 310
 311        /* Don't add empty devices */
 312        if (!desc->lba)
 313                return EFI_NOT_READY;
 314
 315        diskobj = calloc(1, sizeof(*diskobj));
 316        if (!diskobj)
 317                return EFI_OUT_OF_RESOURCES;
 318
 319        /* Hook up to the device list */
 320        efi_add_handle(&diskobj->header);
 321
 322        /* Fill in object data */
 323        if (part) {
 324                struct efi_device_path *node = efi_dp_part_node(desc, part);
 325
 326                diskobj->dp = efi_dp_append_node(dp_parent, node);
 327                efi_free_pool(node);
 328        } else {
 329                diskobj->dp = efi_dp_from_part(desc, part);
 330        }
 331        diskobj->part = part;
 332        ret = efi_add_protocol(&diskobj->header, &efi_block_io_guid,
 333                               &diskobj->ops);
 334        if (ret != EFI_SUCCESS)
 335                return ret;
 336        ret = efi_add_protocol(&diskobj->header, &efi_guid_device_path,
 337                               diskobj->dp);
 338        if (ret != EFI_SUCCESS)
 339                return ret;
 340        /* partitions or whole disk without partitions */
 341        if ((part || desc->part_type == PART_TYPE_UNKNOWN) &&
 342            efi_fs_exists(desc, part)) {
 343                diskobj->volume = efi_simple_file_system(desc, part,
 344                                                         diskobj->dp);
 345                ret = efi_add_protocol(&diskobj->header,
 346                                       &efi_simple_file_system_protocol_guid,
 347                                       diskobj->volume);
 348                if (ret != EFI_SUCCESS)
 349                        return ret;
 350        }
 351        diskobj->ops = block_io_disk_template;
 352        diskobj->ifname = if_typename;
 353        diskobj->dev_index = dev_index;
 354        diskobj->offset = offset;
 355        diskobj->desc = desc;
 356
 357        /* Fill in EFI IO Media info (for read/write callbacks) */
 358        diskobj->media.removable_media = desc->removable;
 359        diskobj->media.media_present = 1;
 360        /*
 361         * MediaID is just an arbitrary counter.
 362         * We have to change it if the medium is removed or changed.
 363         */
 364        diskobj->media.media_id = 1;
 365        diskobj->media.block_size = desc->blksz;
 366        diskobj->media.io_align = desc->blksz;
 367        diskobj->media.last_block = desc->lba - offset;
 368        if (part != 0)
 369                diskobj->media.logical_partition = 1;
 370        diskobj->ops.media = &diskobj->media;
 371        if (disk)
 372                *disk = diskobj;
 373        return EFI_SUCCESS;
 374}
 375
 376/*
 377 * Create handles and protocols for the partitions of a block device
 378 *
 379 * @parent              handle of the parent disk
 380 * @blk_desc            block device
 381 * @if_typename         interface type
 382 * @diskid              device number
 383 * @pdevname            device name
 384 * @return              number of partitions created
 385 */
 386int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc,
 387                               const char *if_typename, int diskid,
 388                               const char *pdevname)
 389{
 390        int disks = 0;
 391        char devname[32] = { 0 }; /* dp->str is u16[32] long */
 392        disk_partition_t info;
 393        int part;
 394        struct efi_device_path *dp = NULL;
 395        efi_status_t ret;
 396        struct efi_handler *handler;
 397
 398        /* Get the device path of the parent */
 399        ret = efi_search_protocol(parent, &efi_guid_device_path, &handler);
 400        if (ret == EFI_SUCCESS)
 401                dp = handler->protocol_interface;
 402
 403        /* Add devices for each partition */
 404        for (part = 1; part <= MAX_SEARCH_PARTITIONS; part++) {
 405                if (part_get_info(desc, part, &info))
 406                        continue;
 407                snprintf(devname, sizeof(devname), "%s:%d", pdevname,
 408                         part);
 409                ret = efi_disk_add_dev(parent, dp, if_typename, desc, diskid,
 410                                       info.start, part, NULL);
 411                if (ret != EFI_SUCCESS) {
 412                        printf("Adding partition %s failed\n", pdevname);
 413                        continue;
 414                }
 415                disks++;
 416        }
 417
 418        return disks;
 419}
 420
 421/*
 422 * U-Boot doesn't have a list of all online disk devices. So when running our
 423 * EFI payload, we scan through all of the potentially available ones and
 424 * store them in our object pool.
 425 *
 426 * TODO(sjg@chromium.org): Actually with CONFIG_BLK, U-Boot does have this.
 427 * Consider converting the code to look up devices as needed. The EFI device
 428 * could be a child of the UCLASS_BLK block device, perhaps.
 429 *
 430 * This gets called from do_bootefi_exec().
 431 */
 432efi_status_t efi_disk_register(void)
 433{
 434        struct efi_disk_obj *disk;
 435        int disks = 0;
 436        efi_status_t ret;
 437#ifdef CONFIG_BLK
 438        struct udevice *dev;
 439
 440        for (uclass_first_device_check(UCLASS_BLK, &dev); dev;
 441             uclass_next_device_check(&dev)) {
 442                struct blk_desc *desc = dev_get_uclass_platdata(dev);
 443                const char *if_typename = blk_get_if_type_name(desc->if_type);
 444
 445                /* Add block device for the full device */
 446                printf("Scanning disk %s...\n", dev->name);
 447                ret = efi_disk_add_dev(NULL, NULL, if_typename,
 448                                        desc, desc->devnum, 0, 0, &disk);
 449                if (ret == EFI_NOT_READY) {
 450                        printf("Disk %s not ready\n", dev->name);
 451                        continue;
 452                }
 453                if (ret) {
 454                        printf("ERROR: failure to add disk device %s, r = %lu\n",
 455                               dev->name, ret & ~EFI_ERROR_MASK);
 456                        return ret;
 457                }
 458                disks++;
 459
 460                /* Partitions show up as block devices in EFI */
 461                disks += efi_disk_create_partitions(
 462                                        &disk->header, desc, if_typename,
 463                                        desc->devnum, dev->name);
 464        }
 465#else
 466        int i, if_type;
 467
 468        /* Search for all available disk devices */
 469        for (if_type = 0; if_type < IF_TYPE_COUNT; if_type++) {
 470                const struct blk_driver *cur_drvr;
 471                const char *if_typename;
 472
 473                cur_drvr = blk_driver_lookup_type(if_type);
 474                if (!cur_drvr)
 475                        continue;
 476
 477                if_typename = cur_drvr->if_typename;
 478                printf("Scanning disks on %s...\n", if_typename);
 479                for (i = 0; i < 4; i++) {
 480                        struct blk_desc *desc;
 481                        char devname[32] = { 0 }; /* dp->str is u16[32] long */
 482
 483                        desc = blk_get_devnum_by_type(if_type, i);
 484                        if (!desc)
 485                                continue;
 486                        if (desc->type == DEV_TYPE_UNKNOWN)
 487                                continue;
 488
 489                        snprintf(devname, sizeof(devname), "%s%d",
 490                                 if_typename, i);
 491
 492                        /* Add block device for the full device */
 493                        ret = efi_disk_add_dev(NULL, NULL, if_typename, desc,
 494                                               i, 0, 0, &disk);
 495                        if (ret == EFI_NOT_READY) {
 496                                printf("Disk %s not ready\n", devname);
 497                                continue;
 498                        }
 499                        if (ret) {
 500                                printf("ERROR: failure to add disk device %s, r = %lu\n",
 501                                       devname, ret & ~EFI_ERROR_MASK);
 502                                return ret;
 503                        }
 504                        disks++;
 505
 506                        /* Partitions show up as block devices in EFI */
 507                        disks += efi_disk_create_partitions
 508                                                (&disk->header, desc,
 509                                                 if_typename, i, devname);
 510                }
 511        }
 512#endif
 513        printf("Found %d disks\n", disks);
 514
 515        return EFI_SUCCESS;
 516}
 517