12a22d05dSAlexander Graf /* 22a22d05dSAlexander Graf * EFI application disk support 32a22d05dSAlexander Graf * 42a22d05dSAlexander Graf * Copyright (c) 2016 Alexander Graf 52a22d05dSAlexander Graf * 62a22d05dSAlexander Graf * SPDX-License-Identifier: GPL-2.0+ 72a22d05dSAlexander Graf */ 82a22d05dSAlexander Graf 92a22d05dSAlexander Graf #include <common.h> 106dd9faf8SSimon Glass #include <blk.h> 11487d756fSSimon Glass #include <dm.h> 122a22d05dSAlexander Graf #include <efi_loader.h> 132a22d05dSAlexander Graf #include <inttypes.h> 142a22d05dSAlexander Graf #include <part.h> 152a22d05dSAlexander Graf #include <malloc.h> 162a22d05dSAlexander Graf 172a22d05dSAlexander Graf static const efi_guid_t efi_block_io_guid = BLOCK_IO_GUID; 182a22d05dSAlexander Graf 192a22d05dSAlexander Graf struct efi_disk_obj { 202a22d05dSAlexander Graf /* Generic EFI object parent class data */ 212a22d05dSAlexander Graf struct efi_object parent; 222a22d05dSAlexander Graf /* EFI Interface callback struct for block I/O */ 232a22d05dSAlexander Graf struct efi_block_io ops; 242a22d05dSAlexander Graf /* U-Boot ifname for block device */ 252a22d05dSAlexander Graf const char *ifname; 262a22d05dSAlexander Graf /* U-Boot dev_index for block device */ 272a22d05dSAlexander Graf int dev_index; 282a22d05dSAlexander Graf /* EFI Interface Media descriptor struct, referenced by ops */ 292a22d05dSAlexander Graf struct efi_block_io_media media; 302a22d05dSAlexander Graf /* EFI device path to this block device */ 31*a0955e7eSRob Clark struct efi_device_path *dp; 32*a0955e7eSRob Clark /* partition # */ 33*a0955e7eSRob Clark unsigned int part; 348c3df0bfSAlexander Graf /* Offset into disk for simple partitions */ 358c3df0bfSAlexander Graf lbaint_t offset; 36f9d334bdSAlexander Graf /* Internal block device */ 37*a0955e7eSRob Clark struct blk_desc *desc; 382a22d05dSAlexander Graf }; 392a22d05dSAlexander Graf 402a22d05dSAlexander Graf static efi_status_t EFIAPI efi_disk_reset(struct efi_block_io *this, 412a22d05dSAlexander Graf char extended_verification) 422a22d05dSAlexander Graf { 432a22d05dSAlexander Graf EFI_ENTRY("%p, %x", this, extended_verification); 442a22d05dSAlexander Graf return EFI_EXIT(EFI_DEVICE_ERROR); 452a22d05dSAlexander Graf } 462a22d05dSAlexander Graf 472a22d05dSAlexander Graf enum efi_disk_direction { 482a22d05dSAlexander Graf EFI_DISK_READ, 492a22d05dSAlexander Graf EFI_DISK_WRITE, 502a22d05dSAlexander Graf }; 512a22d05dSAlexander Graf 522a22d05dSAlexander Graf static efi_status_t EFIAPI efi_disk_rw_blocks(struct efi_block_io *this, 532a22d05dSAlexander Graf u32 media_id, u64 lba, unsigned long buffer_size, 542a22d05dSAlexander Graf void *buffer, enum efi_disk_direction direction) 552a22d05dSAlexander Graf { 562a22d05dSAlexander Graf struct efi_disk_obj *diskobj; 572a22d05dSAlexander Graf struct blk_desc *desc; 582a22d05dSAlexander Graf int blksz; 592a22d05dSAlexander Graf int blocks; 602a22d05dSAlexander Graf unsigned long n; 612a22d05dSAlexander Graf 622a22d05dSAlexander Graf diskobj = container_of(this, struct efi_disk_obj, ops); 63f9d334bdSAlexander Graf desc = (struct blk_desc *) diskobj->desc; 642a22d05dSAlexander Graf blksz = desc->blksz; 652a22d05dSAlexander Graf blocks = buffer_size / blksz; 668c3df0bfSAlexander Graf lba += diskobj->offset; 672a22d05dSAlexander Graf 68edcef3baSAlexander Graf debug("EFI: %s:%d blocks=%x lba=%"PRIx64" blksz=%x dir=%d\n", __func__, 692a22d05dSAlexander Graf __LINE__, blocks, lba, blksz, direction); 702a22d05dSAlexander Graf 712a22d05dSAlexander Graf /* We only support full block access */ 722a22d05dSAlexander Graf if (buffer_size & (blksz - 1)) 733304990bSRob Clark return EFI_DEVICE_ERROR; 742a22d05dSAlexander Graf 752a22d05dSAlexander Graf if (direction == EFI_DISK_READ) 76487d756fSSimon Glass n = blk_dread(desc, lba, blocks, buffer); 772a22d05dSAlexander Graf else 78487d756fSSimon Glass n = blk_dwrite(desc, lba, blocks, buffer); 792a22d05dSAlexander Graf 802a22d05dSAlexander Graf /* We don't do interrupts, so check for timers cooperatively */ 812a22d05dSAlexander Graf efi_timer_check(); 822a22d05dSAlexander Graf 83edcef3baSAlexander Graf debug("EFI: %s:%d n=%lx blocks=%x\n", __func__, __LINE__, n, blocks); 84edcef3baSAlexander Graf 852a22d05dSAlexander Graf if (n != blocks) 863304990bSRob Clark return EFI_DEVICE_ERROR; 872a22d05dSAlexander Graf 883304990bSRob Clark return EFI_SUCCESS; 892a22d05dSAlexander Graf } 902a22d05dSAlexander Graf 91e275458cSSimon Glass static efi_status_t EFIAPI efi_disk_read_blocks(struct efi_block_io *this, 922a22d05dSAlexander Graf u32 media_id, u64 lba, unsigned long buffer_size, 932a22d05dSAlexander Graf void *buffer) 942a22d05dSAlexander Graf { 9551735ae0SAlexander Graf void *real_buffer = buffer; 9651735ae0SAlexander Graf efi_status_t r; 9751735ae0SAlexander Graf 9851735ae0SAlexander Graf #ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER 9951735ae0SAlexander Graf if (buffer_size > EFI_LOADER_BOUNCE_BUFFER_SIZE) { 10051735ae0SAlexander Graf r = efi_disk_read_blocks(this, media_id, lba, 10151735ae0SAlexander Graf EFI_LOADER_BOUNCE_BUFFER_SIZE, buffer); 10251735ae0SAlexander Graf if (r != EFI_SUCCESS) 10351735ae0SAlexander Graf return r; 10451735ae0SAlexander Graf return efi_disk_read_blocks(this, media_id, lba + 10551735ae0SAlexander Graf EFI_LOADER_BOUNCE_BUFFER_SIZE / this->media->block_size, 10651735ae0SAlexander Graf buffer_size - EFI_LOADER_BOUNCE_BUFFER_SIZE, 10751735ae0SAlexander Graf buffer + EFI_LOADER_BOUNCE_BUFFER_SIZE); 10851735ae0SAlexander Graf } 10951735ae0SAlexander Graf 11051735ae0SAlexander Graf real_buffer = efi_bounce_buffer; 11151735ae0SAlexander Graf #endif 11251735ae0SAlexander Graf 11351735ae0SAlexander Graf EFI_ENTRY("%p, %x, %"PRIx64", %lx, %p", this, media_id, lba, 11451735ae0SAlexander Graf buffer_size, buffer); 11551735ae0SAlexander Graf 11651735ae0SAlexander Graf r = efi_disk_rw_blocks(this, media_id, lba, buffer_size, real_buffer, 1172a22d05dSAlexander Graf EFI_DISK_READ); 11851735ae0SAlexander Graf 11951735ae0SAlexander Graf /* Copy from bounce buffer to real buffer if necessary */ 12051735ae0SAlexander Graf if ((r == EFI_SUCCESS) && (real_buffer != buffer)) 12151735ae0SAlexander Graf memcpy(buffer, real_buffer, buffer_size); 12251735ae0SAlexander Graf 12351735ae0SAlexander Graf return EFI_EXIT(r); 1242a22d05dSAlexander Graf } 1252a22d05dSAlexander Graf 126e275458cSSimon Glass static efi_status_t EFIAPI efi_disk_write_blocks(struct efi_block_io *this, 1272a22d05dSAlexander Graf u32 media_id, u64 lba, unsigned long buffer_size, 1282a22d05dSAlexander Graf void *buffer) 1292a22d05dSAlexander Graf { 13051735ae0SAlexander Graf void *real_buffer = buffer; 13151735ae0SAlexander Graf efi_status_t r; 13251735ae0SAlexander Graf 13351735ae0SAlexander Graf #ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER 13451735ae0SAlexander Graf if (buffer_size > EFI_LOADER_BOUNCE_BUFFER_SIZE) { 13551735ae0SAlexander Graf r = efi_disk_write_blocks(this, media_id, lba, 13651735ae0SAlexander Graf EFI_LOADER_BOUNCE_BUFFER_SIZE, buffer); 13751735ae0SAlexander Graf if (r != EFI_SUCCESS) 13851735ae0SAlexander Graf return r; 13951735ae0SAlexander Graf return efi_disk_write_blocks(this, media_id, lba + 14051735ae0SAlexander Graf EFI_LOADER_BOUNCE_BUFFER_SIZE / this->media->block_size, 14151735ae0SAlexander Graf buffer_size - EFI_LOADER_BOUNCE_BUFFER_SIZE, 14251735ae0SAlexander Graf buffer + EFI_LOADER_BOUNCE_BUFFER_SIZE); 14351735ae0SAlexander Graf } 14451735ae0SAlexander Graf 14551735ae0SAlexander Graf real_buffer = efi_bounce_buffer; 14651735ae0SAlexander Graf #endif 14751735ae0SAlexander Graf 14851735ae0SAlexander Graf EFI_ENTRY("%p, %x, %"PRIx64", %lx, %p", this, media_id, lba, 14951735ae0SAlexander Graf buffer_size, buffer); 15051735ae0SAlexander Graf 15151735ae0SAlexander Graf /* Populate bounce buffer if necessary */ 15251735ae0SAlexander Graf if (real_buffer != buffer) 15351735ae0SAlexander Graf memcpy(real_buffer, buffer, buffer_size); 15451735ae0SAlexander Graf 15551735ae0SAlexander Graf r = efi_disk_rw_blocks(this, media_id, lba, buffer_size, real_buffer, 1562a22d05dSAlexander Graf EFI_DISK_WRITE); 15751735ae0SAlexander Graf 15851735ae0SAlexander Graf return EFI_EXIT(r); 1592a22d05dSAlexander Graf } 1602a22d05dSAlexander Graf 1612a22d05dSAlexander Graf static efi_status_t EFIAPI efi_disk_flush_blocks(struct efi_block_io *this) 1622a22d05dSAlexander Graf { 1632a22d05dSAlexander Graf /* We always write synchronously */ 1642a22d05dSAlexander Graf EFI_ENTRY("%p", this); 1652a22d05dSAlexander Graf return EFI_EXIT(EFI_SUCCESS); 1662a22d05dSAlexander Graf } 1672a22d05dSAlexander Graf 1682a22d05dSAlexander Graf static const struct efi_block_io block_io_disk_template = { 1692a22d05dSAlexander Graf .reset = &efi_disk_reset, 1702a22d05dSAlexander Graf .read_blocks = &efi_disk_read_blocks, 1712a22d05dSAlexander Graf .write_blocks = &efi_disk_write_blocks, 1722a22d05dSAlexander Graf .flush_blocks = &efi_disk_flush_blocks, 1732a22d05dSAlexander Graf }; 1742a22d05dSAlexander Graf 175487d756fSSimon Glass static void efi_disk_add_dev(const char *name, 176487d756fSSimon Glass const char *if_typename, 177*a0955e7eSRob Clark struct blk_desc *desc, 1784a12a97cSAlexander Graf int dev_index, 179*a0955e7eSRob Clark lbaint_t offset, 180*a0955e7eSRob Clark unsigned int part) 1814a12a97cSAlexander Graf { 1824a12a97cSAlexander Graf struct efi_disk_obj *diskobj; 1834a12a97cSAlexander Graf 1840812d1a0SAlexander Graf /* Don't add empty devices */ 1850812d1a0SAlexander Graf if (!desc->lba) 1860812d1a0SAlexander Graf return; 1870812d1a0SAlexander Graf 188*a0955e7eSRob Clark diskobj = calloc(1, sizeof(*diskobj)); 1894a12a97cSAlexander Graf 1904a12a97cSAlexander Graf /* Fill in object data */ 191*a0955e7eSRob Clark diskobj->dp = efi_dp_from_part(desc, part); 192*a0955e7eSRob Clark diskobj->part = part; 1934a12a97cSAlexander Graf diskobj->parent.protocols[0].guid = &efi_block_io_guid; 194b5349f74Sxypron.glpk@gmx.de diskobj->parent.protocols[0].protocol_interface = &diskobj->ops; 1954a12a97cSAlexander Graf diskobj->parent.protocols[1].guid = &efi_guid_device_path; 196*a0955e7eSRob Clark diskobj->parent.protocols[1].protocol_interface = diskobj->dp; 1974a12a97cSAlexander Graf diskobj->parent.handle = diskobj; 1984a12a97cSAlexander Graf diskobj->ops = block_io_disk_template; 199487d756fSSimon Glass diskobj->ifname = if_typename; 2004a12a97cSAlexander Graf diskobj->dev_index = dev_index; 2018c3df0bfSAlexander Graf diskobj->offset = offset; 202f9d334bdSAlexander Graf diskobj->desc = desc; 2034a12a97cSAlexander Graf 2044a12a97cSAlexander Graf /* Fill in EFI IO Media info (for read/write callbacks) */ 2054a12a97cSAlexander Graf diskobj->media.removable_media = desc->removable; 2064a12a97cSAlexander Graf diskobj->media.media_present = 1; 2074a12a97cSAlexander Graf diskobj->media.block_size = desc->blksz; 2084a12a97cSAlexander Graf diskobj->media.io_align = desc->blksz; 2090812d1a0SAlexander Graf diskobj->media.last_block = desc->lba - offset; 2104a12a97cSAlexander Graf diskobj->ops.media = &diskobj->media; 2114a12a97cSAlexander Graf 2124a12a97cSAlexander Graf /* Hook up to the device list */ 2134a12a97cSAlexander Graf list_add_tail(&diskobj->parent.link, &efi_obj_list); 2144a12a97cSAlexander Graf } 2154a12a97cSAlexander Graf 2168c3df0bfSAlexander Graf static int efi_disk_create_eltorito(struct blk_desc *desc, 217487d756fSSimon Glass const char *if_typename, 218f9d334bdSAlexander Graf int diskid, 219f9d334bdSAlexander Graf const char *pdevname) 2208c3df0bfSAlexander Graf { 2218c3df0bfSAlexander Graf int disks = 0; 2221acc0087SPatrick Delaunay #if CONFIG_IS_ENABLED(ISO_PARTITION) 223ecbe1a07SAlexander Graf char devname[32] = { 0 }; /* dp->str is u16[32] long */ 2248c3df0bfSAlexander Graf disk_partition_t info; 2258c3df0bfSAlexander Graf int part = 1; 2268c3df0bfSAlexander Graf 2278c3df0bfSAlexander Graf if (desc->part_type != PART_TYPE_ISO) 2288c3df0bfSAlexander Graf return 0; 2298c3df0bfSAlexander Graf 230*a0955e7eSRob Clark /* and devices for each partition: */ 2318c3df0bfSAlexander Graf while (!part_get_info(desc, part, &info)) { 232f9d334bdSAlexander Graf snprintf(devname, sizeof(devname), "%s:%d", pdevname, 233f9d334bdSAlexander Graf part); 234487d756fSSimon Glass efi_disk_add_dev(devname, if_typename, desc, diskid, 235*a0955e7eSRob Clark info.start, part); 2368c3df0bfSAlexander Graf part++; 2378c3df0bfSAlexander Graf disks++; 2388c3df0bfSAlexander Graf } 239*a0955e7eSRob Clark 240*a0955e7eSRob Clark /* ... and add block device: */ 241*a0955e7eSRob Clark efi_disk_add_dev(devname, if_typename, desc, diskid, 0, 0); 2428c3df0bfSAlexander Graf #endif 2438c3df0bfSAlexander Graf 2448c3df0bfSAlexander Graf return disks; 2458c3df0bfSAlexander Graf } 2468c3df0bfSAlexander Graf 2472a22d05dSAlexander Graf /* 2482a22d05dSAlexander Graf * U-Boot doesn't have a list of all online disk devices. So when running our 2492a22d05dSAlexander Graf * EFI payload, we scan through all of the potentially available ones and 2502a22d05dSAlexander Graf * store them in our object pool. 2512a22d05dSAlexander Graf * 252487d756fSSimon Glass * TODO(sjg@chromium.org): Actually with CONFIG_BLK, U-Boot does have this. 253487d756fSSimon Glass * Consider converting the code to look up devices as needed. The EFI device 254487d756fSSimon Glass * could be a child of the UCLASS_BLK block device, perhaps. 255487d756fSSimon Glass * 2562a22d05dSAlexander Graf * This gets called from do_bootefi_exec(). 2572a22d05dSAlexander Graf */ 2582a22d05dSAlexander Graf int efi_disk_register(void) 2592a22d05dSAlexander Graf { 2602a22d05dSAlexander Graf int disks = 0; 261487d756fSSimon Glass #ifdef CONFIG_BLK 262487d756fSSimon Glass struct udevice *dev; 263487d756fSSimon Glass 26470bfcdc6Sxypron.glpk@gmx.de for (uclass_first_device_check(UCLASS_BLK, &dev); 265487d756fSSimon Glass dev; 26670bfcdc6Sxypron.glpk@gmx.de uclass_next_device_check(&dev)) { 267487d756fSSimon Glass struct blk_desc *desc = dev_get_uclass_platdata(dev); 268487d756fSSimon Glass const char *if_typename = dev->driver->name; 269*a0955e7eSRob Clark disk_partition_t info; 270*a0955e7eSRob Clark int part = 1; 271487d756fSSimon Glass 272487d756fSSimon Glass printf("Scanning disk %s...\n", dev->name); 273*a0955e7eSRob Clark 274*a0955e7eSRob Clark /* add devices for each partition: */ 275*a0955e7eSRob Clark while (!part_get_info(desc, part, &info)) { 276*a0955e7eSRob Clark efi_disk_add_dev(dev->name, if_typename, desc, 277*a0955e7eSRob Clark desc->devnum, 0, part); 278*a0955e7eSRob Clark part++; 279*a0955e7eSRob Clark } 280*a0955e7eSRob Clark 281*a0955e7eSRob Clark /* ... and add block device: */ 282*a0955e7eSRob Clark efi_disk_add_dev(dev->name, if_typename, desc, 283*a0955e7eSRob Clark desc->devnum, 0, 0); 284*a0955e7eSRob Clark 285487d756fSSimon Glass disks++; 286487d756fSSimon Glass 287487d756fSSimon Glass /* 288487d756fSSimon Glass * El Torito images show up as block devices in an EFI world, 289487d756fSSimon Glass * so let's create them here 290487d756fSSimon Glass */ 291487d756fSSimon Glass disks += efi_disk_create_eltorito(desc, if_typename, 292f9d334bdSAlexander Graf desc->devnum, dev->name); 293487d756fSSimon Glass } 294487d756fSSimon Glass #else 295487d756fSSimon Glass int i, if_type; 2962a22d05dSAlexander Graf 2972a22d05dSAlexander Graf /* Search for all available disk devices */ 2986dd9faf8SSimon Glass for (if_type = 0; if_type < IF_TYPE_COUNT; if_type++) { 299487d756fSSimon Glass const struct blk_driver *cur_drvr; 300487d756fSSimon Glass const char *if_typename; 301487d756fSSimon Glass 3026dd9faf8SSimon Glass cur_drvr = blk_driver_lookup_type(if_type); 3036dd9faf8SSimon Glass if (!cur_drvr) 3046dd9faf8SSimon Glass continue; 3056dd9faf8SSimon Glass 306487d756fSSimon Glass if_typename = cur_drvr->if_typename; 307487d756fSSimon Glass printf("Scanning disks on %s...\n", if_typename); 3082a22d05dSAlexander Graf for (i = 0; i < 4; i++) { 3092a22d05dSAlexander Graf struct blk_desc *desc; 310ecbe1a07SAlexander Graf char devname[32] = { 0 }; /* dp->str is u16[32] long */ 3112a22d05dSAlexander Graf 3126dd9faf8SSimon Glass desc = blk_get_devnum_by_type(if_type, i); 3132a22d05dSAlexander Graf if (!desc) 3142a22d05dSAlexander Graf continue; 3152a22d05dSAlexander Graf if (desc->type == DEV_TYPE_UNKNOWN) 3162a22d05dSAlexander Graf continue; 3172a22d05dSAlexander Graf 3182a22d05dSAlexander Graf snprintf(devname, sizeof(devname), "%s%d", 319487d756fSSimon Glass if_typename, i); 320*a0955e7eSRob Clark efi_disk_add_dev(devname, if_typename, desc, i, 0, 0); 3212a22d05dSAlexander Graf disks++; 3228c3df0bfSAlexander Graf 3238c3df0bfSAlexander Graf /* 3248c3df0bfSAlexander Graf * El Torito images show up as block devices 3258c3df0bfSAlexander Graf * in an EFI world, so let's create them here 3268c3df0bfSAlexander Graf */ 327f9d334bdSAlexander Graf disks += efi_disk_create_eltorito(desc, if_typename, 328f9d334bdSAlexander Graf i, devname); 3292a22d05dSAlexander Graf } 3302a22d05dSAlexander Graf } 331487d756fSSimon Glass #endif 3322a22d05dSAlexander Graf printf("Found %d disks\n", disks); 3332a22d05dSAlexander Graf 3342a22d05dSAlexander Graf return 0; 3352a22d05dSAlexander Graf } 336