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