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> 11*487d756fSSimon 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 */ 312a22d05dSAlexander Graf struct efi_device_path_file_path *dp; 328c3df0bfSAlexander Graf /* Offset into disk for simple partitions */ 338c3df0bfSAlexander Graf lbaint_t offset; 342a22d05dSAlexander Graf }; 352a22d05dSAlexander Graf 362a22d05dSAlexander Graf static efi_status_t efi_disk_open_block(void *handle, efi_guid_t *protocol, 372a22d05dSAlexander Graf void **protocol_interface, void *agent_handle, 382a22d05dSAlexander Graf void *controller_handle, uint32_t attributes) 392a22d05dSAlexander Graf { 402a22d05dSAlexander Graf struct efi_disk_obj *diskobj = handle; 412a22d05dSAlexander Graf 422a22d05dSAlexander Graf *protocol_interface = &diskobj->ops; 432a22d05dSAlexander Graf 442a22d05dSAlexander Graf return EFI_SUCCESS; 452a22d05dSAlexander Graf } 462a22d05dSAlexander Graf 472a22d05dSAlexander Graf static efi_status_t efi_disk_open_dp(void *handle, efi_guid_t *protocol, 482a22d05dSAlexander Graf void **protocol_interface, void *agent_handle, 492a22d05dSAlexander Graf void *controller_handle, uint32_t attributes) 502a22d05dSAlexander Graf { 512a22d05dSAlexander Graf struct efi_disk_obj *diskobj = handle; 522a22d05dSAlexander Graf 532a22d05dSAlexander Graf *protocol_interface = diskobj->dp; 542a22d05dSAlexander Graf 552a22d05dSAlexander Graf return EFI_SUCCESS; 562a22d05dSAlexander Graf } 572a22d05dSAlexander Graf 582a22d05dSAlexander Graf static efi_status_t EFIAPI efi_disk_reset(struct efi_block_io *this, 592a22d05dSAlexander Graf char extended_verification) 602a22d05dSAlexander Graf { 612a22d05dSAlexander Graf EFI_ENTRY("%p, %x", this, extended_verification); 622a22d05dSAlexander Graf return EFI_EXIT(EFI_DEVICE_ERROR); 632a22d05dSAlexander Graf } 642a22d05dSAlexander Graf 652a22d05dSAlexander Graf enum efi_disk_direction { 662a22d05dSAlexander Graf EFI_DISK_READ, 672a22d05dSAlexander Graf EFI_DISK_WRITE, 682a22d05dSAlexander Graf }; 692a22d05dSAlexander Graf 702a22d05dSAlexander Graf static efi_status_t EFIAPI efi_disk_rw_blocks(struct efi_block_io *this, 712a22d05dSAlexander Graf u32 media_id, u64 lba, unsigned long buffer_size, 722a22d05dSAlexander Graf void *buffer, enum efi_disk_direction direction) 732a22d05dSAlexander Graf { 742a22d05dSAlexander Graf struct efi_disk_obj *diskobj; 752a22d05dSAlexander Graf struct blk_desc *desc; 762a22d05dSAlexander Graf int blksz; 772a22d05dSAlexander Graf int blocks; 782a22d05dSAlexander Graf unsigned long n; 792a22d05dSAlexander Graf 802a22d05dSAlexander Graf EFI_ENTRY("%p, %x, %"PRIx64", %lx, %p", this, media_id, lba, 812a22d05dSAlexander Graf buffer_size, buffer); 822a22d05dSAlexander Graf 832a22d05dSAlexander Graf diskobj = container_of(this, struct efi_disk_obj, ops); 842a22d05dSAlexander Graf if (!(desc = blk_get_dev(diskobj->ifname, diskobj->dev_index))) 852a22d05dSAlexander Graf return EFI_EXIT(EFI_DEVICE_ERROR); 862a22d05dSAlexander Graf blksz = desc->blksz; 872a22d05dSAlexander Graf blocks = buffer_size / blksz; 888c3df0bfSAlexander Graf lba += diskobj->offset; 892a22d05dSAlexander Graf 902a22d05dSAlexander Graf #ifdef DEBUG_EFI 912a22d05dSAlexander Graf printf("EFI: %s:%d blocks=%x lba=%"PRIx64" blksz=%x dir=%d\n", __func__, 922a22d05dSAlexander Graf __LINE__, blocks, lba, blksz, direction); 932a22d05dSAlexander Graf #endif 942a22d05dSAlexander Graf 952a22d05dSAlexander Graf /* We only support full block access */ 962a22d05dSAlexander Graf if (buffer_size & (blksz - 1)) 972a22d05dSAlexander Graf return EFI_EXIT(EFI_DEVICE_ERROR); 982a22d05dSAlexander Graf 992a22d05dSAlexander Graf if (direction == EFI_DISK_READ) 100*487d756fSSimon Glass n = blk_dread(desc, lba, blocks, buffer); 1012a22d05dSAlexander Graf else 102*487d756fSSimon Glass n = blk_dwrite(desc, lba, blocks, buffer); 1032a22d05dSAlexander Graf 1042a22d05dSAlexander Graf /* We don't do interrupts, so check for timers cooperatively */ 1052a22d05dSAlexander Graf efi_timer_check(); 1062a22d05dSAlexander Graf 1072a22d05dSAlexander Graf #ifdef DEBUG_EFI 1082a22d05dSAlexander Graf printf("EFI: %s:%d n=%lx blocks=%x\n", __func__, __LINE__, n, blocks); 1092a22d05dSAlexander Graf #endif 1102a22d05dSAlexander Graf if (n != blocks) 1112a22d05dSAlexander Graf return EFI_EXIT(EFI_DEVICE_ERROR); 1122a22d05dSAlexander Graf 1132a22d05dSAlexander Graf return EFI_EXIT(EFI_SUCCESS); 1142a22d05dSAlexander Graf } 1152a22d05dSAlexander Graf 1162a22d05dSAlexander Graf static efi_status_t efi_disk_read_blocks(struct efi_block_io *this, 1172a22d05dSAlexander Graf u32 media_id, u64 lba, unsigned long buffer_size, 1182a22d05dSAlexander Graf void *buffer) 1192a22d05dSAlexander Graf { 1202a22d05dSAlexander Graf return efi_disk_rw_blocks(this, media_id, lba, buffer_size, buffer, 1212a22d05dSAlexander Graf EFI_DISK_READ); 1222a22d05dSAlexander Graf } 1232a22d05dSAlexander Graf 1242a22d05dSAlexander Graf static efi_status_t efi_disk_write_blocks(struct efi_block_io *this, 1252a22d05dSAlexander Graf u32 media_id, u64 lba, unsigned long buffer_size, 1262a22d05dSAlexander Graf void *buffer) 1272a22d05dSAlexander Graf { 1282a22d05dSAlexander Graf return efi_disk_rw_blocks(this, media_id, lba, buffer_size, buffer, 1292a22d05dSAlexander Graf EFI_DISK_WRITE); 1302a22d05dSAlexander Graf } 1312a22d05dSAlexander Graf 1322a22d05dSAlexander Graf static efi_status_t EFIAPI efi_disk_flush_blocks(struct efi_block_io *this) 1332a22d05dSAlexander Graf { 1342a22d05dSAlexander Graf /* We always write synchronously */ 1352a22d05dSAlexander Graf EFI_ENTRY("%p", this); 1362a22d05dSAlexander Graf return EFI_EXIT(EFI_SUCCESS); 1372a22d05dSAlexander Graf } 1382a22d05dSAlexander Graf 1392a22d05dSAlexander Graf static const struct efi_block_io block_io_disk_template = { 1402a22d05dSAlexander Graf .reset = &efi_disk_reset, 1412a22d05dSAlexander Graf .read_blocks = &efi_disk_read_blocks, 1422a22d05dSAlexander Graf .write_blocks = &efi_disk_write_blocks, 1432a22d05dSAlexander Graf .flush_blocks = &efi_disk_flush_blocks, 1442a22d05dSAlexander Graf }; 1452a22d05dSAlexander Graf 146*487d756fSSimon Glass static void efi_disk_add_dev(const char *name, 147*487d756fSSimon Glass const char *if_typename, 1484a12a97cSAlexander Graf const struct blk_desc *desc, 1494a12a97cSAlexander Graf int dev_index, 1504a12a97cSAlexander Graf lbaint_t offset) 1514a12a97cSAlexander Graf { 1524a12a97cSAlexander Graf struct efi_disk_obj *diskobj; 1534a12a97cSAlexander Graf struct efi_device_path_file_path *dp; 1544a12a97cSAlexander Graf int objlen = sizeof(*diskobj) + (sizeof(*dp) * 2); 1554a12a97cSAlexander Graf 1564a12a97cSAlexander Graf diskobj = calloc(1, objlen); 1574a12a97cSAlexander Graf 1584a12a97cSAlexander Graf /* Fill in object data */ 1594a12a97cSAlexander Graf diskobj->parent.protocols[0].guid = &efi_block_io_guid; 1604a12a97cSAlexander Graf diskobj->parent.protocols[0].open = efi_disk_open_block; 1614a12a97cSAlexander Graf diskobj->parent.protocols[1].guid = &efi_guid_device_path; 1624a12a97cSAlexander Graf diskobj->parent.protocols[1].open = efi_disk_open_dp; 1634a12a97cSAlexander Graf diskobj->parent.handle = diskobj; 1644a12a97cSAlexander Graf diskobj->ops = block_io_disk_template; 165*487d756fSSimon Glass diskobj->ifname = if_typename; 1664a12a97cSAlexander Graf diskobj->dev_index = dev_index; 1678c3df0bfSAlexander Graf diskobj->offset = offset; 1684a12a97cSAlexander Graf 1694a12a97cSAlexander Graf /* Fill in EFI IO Media info (for read/write callbacks) */ 1704a12a97cSAlexander Graf diskobj->media.removable_media = desc->removable; 1714a12a97cSAlexander Graf diskobj->media.media_present = 1; 1724a12a97cSAlexander Graf diskobj->media.block_size = desc->blksz; 1734a12a97cSAlexander Graf diskobj->media.io_align = desc->blksz; 1744a12a97cSAlexander Graf diskobj->media.last_block = desc->lba; 1754a12a97cSAlexander Graf diskobj->ops.media = &diskobj->media; 1764a12a97cSAlexander Graf 1774a12a97cSAlexander Graf /* Fill in device path */ 1784a12a97cSAlexander Graf dp = (void*)&diskobj[1]; 1794a12a97cSAlexander Graf diskobj->dp = dp; 1804a12a97cSAlexander Graf dp[0].dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE; 1814a12a97cSAlexander Graf dp[0].dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH; 1824a12a97cSAlexander Graf dp[0].dp.length = sizeof(*dp); 1834a12a97cSAlexander Graf ascii2unicode(dp[0].str, name); 1844a12a97cSAlexander Graf 1854a12a97cSAlexander Graf dp[1].dp.type = DEVICE_PATH_TYPE_END; 1864a12a97cSAlexander Graf dp[1].dp.sub_type = DEVICE_PATH_SUB_TYPE_END; 1874a12a97cSAlexander Graf dp[1].dp.length = sizeof(*dp); 1884a12a97cSAlexander Graf 1894a12a97cSAlexander Graf /* Hook up to the device list */ 1904a12a97cSAlexander Graf list_add_tail(&diskobj->parent.link, &efi_obj_list); 1914a12a97cSAlexander Graf } 1924a12a97cSAlexander Graf 1938c3df0bfSAlexander Graf static int efi_disk_create_eltorito(struct blk_desc *desc, 194*487d756fSSimon Glass const char *if_typename, 1958c3df0bfSAlexander Graf int diskid) 1968c3df0bfSAlexander Graf { 1978c3df0bfSAlexander Graf int disks = 0; 1988c3df0bfSAlexander Graf #ifdef CONFIG_ISO_PARTITION 199ecbe1a07SAlexander Graf char devname[32] = { 0 }; /* dp->str is u16[32] long */ 2008c3df0bfSAlexander Graf disk_partition_t info; 2018c3df0bfSAlexander Graf int part = 1; 2028c3df0bfSAlexander Graf 2038c3df0bfSAlexander Graf if (desc->part_type != PART_TYPE_ISO) 2048c3df0bfSAlexander Graf return 0; 2058c3df0bfSAlexander Graf 2068c3df0bfSAlexander Graf while (!part_get_info(desc, part, &info)) { 207*487d756fSSimon Glass snprintf(devname, sizeof(devname), "%s%d:%d", if_typename, 208*487d756fSSimon Glass diskid, part); 209*487d756fSSimon Glass efi_disk_add_dev(devname, if_typename, desc, diskid, 210*487d756fSSimon Glass info.start); 2118c3df0bfSAlexander Graf part++; 2128c3df0bfSAlexander Graf disks++; 2138c3df0bfSAlexander Graf } 2148c3df0bfSAlexander Graf #endif 2158c3df0bfSAlexander Graf 2168c3df0bfSAlexander Graf return disks; 2178c3df0bfSAlexander Graf } 2188c3df0bfSAlexander Graf 2192a22d05dSAlexander Graf /* 2202a22d05dSAlexander Graf * U-Boot doesn't have a list of all online disk devices. So when running our 2212a22d05dSAlexander Graf * EFI payload, we scan through all of the potentially available ones and 2222a22d05dSAlexander Graf * store them in our object pool. 2232a22d05dSAlexander Graf * 224*487d756fSSimon Glass * TODO(sjg@chromium.org): Actually with CONFIG_BLK, U-Boot does have this. 225*487d756fSSimon Glass * Consider converting the code to look up devices as needed. The EFI device 226*487d756fSSimon Glass * could be a child of the UCLASS_BLK block device, perhaps. 227*487d756fSSimon Glass * 2282a22d05dSAlexander Graf * This gets called from do_bootefi_exec(). 2292a22d05dSAlexander Graf */ 2302a22d05dSAlexander Graf int efi_disk_register(void) 2312a22d05dSAlexander Graf { 2322a22d05dSAlexander Graf int disks = 0; 233*487d756fSSimon Glass #ifdef CONFIG_BLK 234*487d756fSSimon Glass struct udevice *dev; 235*487d756fSSimon Glass 236*487d756fSSimon Glass for (uclass_first_device(UCLASS_BLK, &dev); 237*487d756fSSimon Glass dev; 238*487d756fSSimon Glass uclass_next_device(&dev)) { 239*487d756fSSimon Glass struct blk_desc *desc = dev_get_uclass_platdata(dev); 240*487d756fSSimon Glass const char *if_typename = dev->driver->name; 241*487d756fSSimon Glass 242*487d756fSSimon Glass printf("Scanning disk %s...\n", dev->name); 243*487d756fSSimon Glass efi_disk_add_dev(dev->name, if_typename, desc, desc->devnum, 0); 244*487d756fSSimon Glass disks++; 245*487d756fSSimon Glass 246*487d756fSSimon Glass /* 247*487d756fSSimon Glass * El Torito images show up as block devices in an EFI world, 248*487d756fSSimon Glass * so let's create them here 249*487d756fSSimon Glass */ 250*487d756fSSimon Glass disks += efi_disk_create_eltorito(desc, if_typename, 251*487d756fSSimon Glass desc->devnum); 252*487d756fSSimon Glass } 253*487d756fSSimon Glass #else 254*487d756fSSimon Glass int i, if_type; 2552a22d05dSAlexander Graf 2562a22d05dSAlexander Graf /* Search for all available disk devices */ 2576dd9faf8SSimon Glass for (if_type = 0; if_type < IF_TYPE_COUNT; if_type++) { 258*487d756fSSimon Glass const struct blk_driver *cur_drvr; 259*487d756fSSimon Glass const char *if_typename; 260*487d756fSSimon Glass 2616dd9faf8SSimon Glass cur_drvr = blk_driver_lookup_type(if_type); 2626dd9faf8SSimon Glass if (!cur_drvr) 2636dd9faf8SSimon Glass continue; 2646dd9faf8SSimon Glass 265*487d756fSSimon Glass if_typename = cur_drvr->if_typename; 266*487d756fSSimon Glass printf("Scanning disks on %s...\n", if_typename); 2672a22d05dSAlexander Graf for (i = 0; i < 4; i++) { 2682a22d05dSAlexander Graf struct blk_desc *desc; 269ecbe1a07SAlexander Graf char devname[32] = { 0 }; /* dp->str is u16[32] long */ 2702a22d05dSAlexander Graf 2716dd9faf8SSimon Glass desc = blk_get_devnum_by_type(if_type, i); 2722a22d05dSAlexander Graf if (!desc) 2732a22d05dSAlexander Graf continue; 2742a22d05dSAlexander Graf if (desc->type == DEV_TYPE_UNKNOWN) 2752a22d05dSAlexander Graf continue; 2762a22d05dSAlexander Graf 2772a22d05dSAlexander Graf snprintf(devname, sizeof(devname), "%s%d", 278*487d756fSSimon Glass if_typename, i); 279*487d756fSSimon Glass efi_disk_add_dev(devname, if_typename, desc, i, 0); 2802a22d05dSAlexander Graf disks++; 2818c3df0bfSAlexander Graf 2828c3df0bfSAlexander Graf /* 2838c3df0bfSAlexander Graf * El Torito images show up as block devices 2848c3df0bfSAlexander Graf * in an EFI world, so let's create them here 2858c3df0bfSAlexander Graf */ 286*487d756fSSimon Glass disks += efi_disk_create_eltorito(desc, if_typename, i); 2872a22d05dSAlexander Graf } 2882a22d05dSAlexander Graf } 289*487d756fSSimon Glass #endif 2902a22d05dSAlexander Graf printf("Found %d disks\n", disks); 2912a22d05dSAlexander Graf 2922a22d05dSAlexander Graf return 0; 2932a22d05dSAlexander Graf } 294