1 /* 2 * EFI application disk support 3 * 4 * Copyright (c) 2016 Alexander Graf 5 * 6 * SPDX-License-Identifier: GPL-2.0+ 7 */ 8 9 #include <common.h> 10 #include <blk.h> 11 #include <dm.h> 12 #include <efi_loader.h> 13 #include <inttypes.h> 14 #include <part.h> 15 #include <malloc.h> 16 17 static const efi_guid_t efi_block_io_guid = BLOCK_IO_GUID; 18 19 struct efi_disk_obj { 20 /* Generic EFI object parent class data */ 21 struct efi_object parent; 22 /* EFI Interface callback struct for block I/O */ 23 struct efi_block_io ops; 24 /* U-Boot ifname for block device */ 25 const char *ifname; 26 /* U-Boot dev_index for block device */ 27 int dev_index; 28 /* EFI Interface Media descriptor struct, referenced by ops */ 29 struct efi_block_io_media media; 30 /* EFI device path to this block device */ 31 struct efi_device_path_file_path *dp; 32 /* Offset into disk for simple partitions */ 33 lbaint_t offset; 34 }; 35 36 static efi_status_t efi_disk_open_block(void *handle, efi_guid_t *protocol, 37 void **protocol_interface, void *agent_handle, 38 void *controller_handle, uint32_t attributes) 39 { 40 struct efi_disk_obj *diskobj = handle; 41 42 *protocol_interface = &diskobj->ops; 43 44 return EFI_SUCCESS; 45 } 46 47 static efi_status_t efi_disk_open_dp(void *handle, efi_guid_t *protocol, 48 void **protocol_interface, void *agent_handle, 49 void *controller_handle, uint32_t attributes) 50 { 51 struct efi_disk_obj *diskobj = handle; 52 53 *protocol_interface = diskobj->dp; 54 55 return EFI_SUCCESS; 56 } 57 58 static efi_status_t EFIAPI efi_disk_reset(struct efi_block_io *this, 59 char extended_verification) 60 { 61 EFI_ENTRY("%p, %x", this, extended_verification); 62 return EFI_EXIT(EFI_DEVICE_ERROR); 63 } 64 65 enum efi_disk_direction { 66 EFI_DISK_READ, 67 EFI_DISK_WRITE, 68 }; 69 70 static efi_status_t EFIAPI efi_disk_rw_blocks(struct efi_block_io *this, 71 u32 media_id, u64 lba, unsigned long buffer_size, 72 void *buffer, enum efi_disk_direction direction) 73 { 74 struct efi_disk_obj *diskobj; 75 struct blk_desc *desc; 76 int blksz; 77 int blocks; 78 unsigned long n; 79 80 EFI_ENTRY("%p, %x, %"PRIx64", %lx, %p", this, media_id, lba, 81 buffer_size, buffer); 82 83 diskobj = container_of(this, struct efi_disk_obj, ops); 84 if (!(desc = blk_get_dev(diskobj->ifname, diskobj->dev_index))) 85 return EFI_EXIT(EFI_DEVICE_ERROR); 86 blksz = desc->blksz; 87 blocks = buffer_size / blksz; 88 lba += diskobj->offset; 89 90 #ifdef DEBUG_EFI 91 printf("EFI: %s:%d blocks=%x lba=%"PRIx64" blksz=%x dir=%d\n", __func__, 92 __LINE__, blocks, lba, blksz, direction); 93 #endif 94 95 /* We only support full block access */ 96 if (buffer_size & (blksz - 1)) 97 return EFI_EXIT(EFI_DEVICE_ERROR); 98 99 if (direction == EFI_DISK_READ) 100 n = blk_dread(desc, lba, blocks, buffer); 101 else 102 n = blk_dwrite(desc, lba, blocks, buffer); 103 104 /* We don't do interrupts, so check for timers cooperatively */ 105 efi_timer_check(); 106 107 #ifdef DEBUG_EFI 108 printf("EFI: %s:%d n=%lx blocks=%x\n", __func__, __LINE__, n, blocks); 109 #endif 110 if (n != blocks) 111 return EFI_EXIT(EFI_DEVICE_ERROR); 112 113 return EFI_EXIT(EFI_SUCCESS); 114 } 115 116 static efi_status_t efi_disk_read_blocks(struct efi_block_io *this, 117 u32 media_id, u64 lba, unsigned long buffer_size, 118 void *buffer) 119 { 120 return efi_disk_rw_blocks(this, media_id, lba, buffer_size, buffer, 121 EFI_DISK_READ); 122 } 123 124 static efi_status_t efi_disk_write_blocks(struct efi_block_io *this, 125 u32 media_id, u64 lba, unsigned long buffer_size, 126 void *buffer) 127 { 128 return efi_disk_rw_blocks(this, media_id, lba, buffer_size, buffer, 129 EFI_DISK_WRITE); 130 } 131 132 static efi_status_t EFIAPI efi_disk_flush_blocks(struct efi_block_io *this) 133 { 134 /* We always write synchronously */ 135 EFI_ENTRY("%p", this); 136 return EFI_EXIT(EFI_SUCCESS); 137 } 138 139 static const struct efi_block_io block_io_disk_template = { 140 .reset = &efi_disk_reset, 141 .read_blocks = &efi_disk_read_blocks, 142 .write_blocks = &efi_disk_write_blocks, 143 .flush_blocks = &efi_disk_flush_blocks, 144 }; 145 146 static void efi_disk_add_dev(const char *name, 147 const char *if_typename, 148 const struct blk_desc *desc, 149 int dev_index, 150 lbaint_t offset) 151 { 152 struct efi_disk_obj *diskobj; 153 struct efi_device_path_file_path *dp; 154 int objlen = sizeof(*diskobj) + (sizeof(*dp) * 2); 155 156 diskobj = calloc(1, objlen); 157 158 /* Fill in object data */ 159 diskobj->parent.protocols[0].guid = &efi_block_io_guid; 160 diskobj->parent.protocols[0].open = efi_disk_open_block; 161 diskobj->parent.protocols[1].guid = &efi_guid_device_path; 162 diskobj->parent.protocols[1].open = efi_disk_open_dp; 163 diskobj->parent.handle = diskobj; 164 diskobj->ops = block_io_disk_template; 165 diskobj->ifname = if_typename; 166 diskobj->dev_index = dev_index; 167 diskobj->offset = offset; 168 169 /* Fill in EFI IO Media info (for read/write callbacks) */ 170 diskobj->media.removable_media = desc->removable; 171 diskobj->media.media_present = 1; 172 diskobj->media.block_size = desc->blksz; 173 diskobj->media.io_align = desc->blksz; 174 diskobj->media.last_block = desc->lba; 175 diskobj->ops.media = &diskobj->media; 176 177 /* Fill in device path */ 178 dp = (void*)&diskobj[1]; 179 diskobj->dp = dp; 180 dp[0].dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE; 181 dp[0].dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH; 182 dp[0].dp.length = sizeof(*dp); 183 ascii2unicode(dp[0].str, name); 184 185 dp[1].dp.type = DEVICE_PATH_TYPE_END; 186 dp[1].dp.sub_type = DEVICE_PATH_SUB_TYPE_END; 187 dp[1].dp.length = sizeof(*dp); 188 189 /* Hook up to the device list */ 190 list_add_tail(&diskobj->parent.link, &efi_obj_list); 191 } 192 193 static int efi_disk_create_eltorito(struct blk_desc *desc, 194 const char *if_typename, 195 int diskid) 196 { 197 int disks = 0; 198 #ifdef CONFIG_ISO_PARTITION 199 char devname[32] = { 0 }; /* dp->str is u16[32] long */ 200 disk_partition_t info; 201 int part = 1; 202 203 if (desc->part_type != PART_TYPE_ISO) 204 return 0; 205 206 while (!part_get_info(desc, part, &info)) { 207 snprintf(devname, sizeof(devname), "%s%d:%d", if_typename, 208 diskid, part); 209 efi_disk_add_dev(devname, if_typename, desc, diskid, 210 info.start); 211 part++; 212 disks++; 213 } 214 #endif 215 216 return disks; 217 } 218 219 /* 220 * U-Boot doesn't have a list of all online disk devices. So when running our 221 * EFI payload, we scan through all of the potentially available ones and 222 * store them in our object pool. 223 * 224 * TODO(sjg@chromium.org): Actually with CONFIG_BLK, U-Boot does have this. 225 * Consider converting the code to look up devices as needed. The EFI device 226 * could be a child of the UCLASS_BLK block device, perhaps. 227 * 228 * This gets called from do_bootefi_exec(). 229 */ 230 int efi_disk_register(void) 231 { 232 int disks = 0; 233 #ifdef CONFIG_BLK 234 struct udevice *dev; 235 236 for (uclass_first_device(UCLASS_BLK, &dev); 237 dev; 238 uclass_next_device(&dev)) { 239 struct blk_desc *desc = dev_get_uclass_platdata(dev); 240 const char *if_typename = dev->driver->name; 241 242 printf("Scanning disk %s...\n", dev->name); 243 efi_disk_add_dev(dev->name, if_typename, desc, desc->devnum, 0); 244 disks++; 245 246 /* 247 * El Torito images show up as block devices in an EFI world, 248 * so let's create them here 249 */ 250 disks += efi_disk_create_eltorito(desc, if_typename, 251 desc->devnum); 252 } 253 #else 254 int i, if_type; 255 256 /* Search for all available disk devices */ 257 for (if_type = 0; if_type < IF_TYPE_COUNT; if_type++) { 258 const struct blk_driver *cur_drvr; 259 const char *if_typename; 260 261 cur_drvr = blk_driver_lookup_type(if_type); 262 if (!cur_drvr) 263 continue; 264 265 if_typename = cur_drvr->if_typename; 266 printf("Scanning disks on %s...\n", if_typename); 267 for (i = 0; i < 4; i++) { 268 struct blk_desc *desc; 269 char devname[32] = { 0 }; /* dp->str is u16[32] long */ 270 271 desc = blk_get_devnum_by_type(if_type, i); 272 if (!desc) 273 continue; 274 if (desc->type == DEV_TYPE_UNKNOWN) 275 continue; 276 277 snprintf(devname, sizeof(devname), "%s%d", 278 if_typename, i); 279 efi_disk_add_dev(devname, if_typename, desc, i, 0); 280 disks++; 281 282 /* 283 * El Torito images show up as block devices 284 * in an EFI world, so let's create them here 285 */ 286 disks += efi_disk_create_eltorito(desc, if_typename, i); 287 } 288 } 289 #endif 290 printf("Found %d disks\n", disks); 291 292 return 0; 293 } 294