1 /*- 2 * Copyright (c) 2010 Marcel Moolenaar 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/disk.h> 31 #include <sys/param.h> 32 #include <sys/time.h> 33 #include <sys/queue.h> 34 #include <stddef.h> 35 #include <stdarg.h> 36 37 #include <bootstrap.h> 38 39 #include <efi.h> 40 #include <efilib.h> 41 #include <efiprot.h> 42 #include <efichar.h> 43 #include <disk.h> 44 45 static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL; 46 47 static int efipart_initfd(void); 48 static int efipart_initcd(void); 49 static int efipart_inithd(void); 50 51 static int efipart_strategy(void *, int, daddr_t, size_t, char *, size_t *); 52 static int efipart_realstrategy(void *, int, daddr_t, size_t, char *, size_t *); 53 54 static int efipart_open(struct open_file *, ...); 55 static int efipart_close(struct open_file *); 56 static int efipart_ioctl(struct open_file *, u_long, void *); 57 58 static int efipart_printfd(int); 59 static int efipart_printcd(int); 60 static int efipart_printhd(int); 61 62 /* EISA PNP ID's for floppy controllers */ 63 #define PNP0604 0x604 64 #define PNP0700 0x700 65 #define PNP0701 0x701 66 67 /* Bounce buffer max size */ 68 #define BIO_BUFFER_SIZE 0x4000 69 70 struct devsw efipart_fddev = { 71 .dv_name = "fd", 72 .dv_type = DEVT_FD, 73 .dv_init = efipart_initfd, 74 .dv_strategy = efipart_strategy, 75 .dv_open = efipart_open, 76 .dv_close = efipart_close, 77 .dv_ioctl = efipart_ioctl, 78 .dv_print = efipart_printfd, 79 .dv_cleanup = NULL 80 }; 81 82 struct devsw efipart_cddev = { 83 .dv_name = "cd", 84 .dv_type = DEVT_CD, 85 .dv_init = efipart_initcd, 86 .dv_strategy = efipart_strategy, 87 .dv_open = efipart_open, 88 .dv_close = efipart_close, 89 .dv_ioctl = efipart_ioctl, 90 .dv_print = efipart_printcd, 91 .dv_cleanup = NULL 92 }; 93 94 struct devsw efipart_hddev = { 95 .dv_name = "disk", 96 .dv_type = DEVT_DISK, 97 .dv_init = efipart_inithd, 98 .dv_strategy = efipart_strategy, 99 .dv_open = efipart_open, 100 .dv_close = efipart_close, 101 .dv_ioctl = efipart_ioctl, 102 .dv_print = efipart_printhd, 103 .dv_cleanup = NULL 104 }; 105 106 static pdinfo_list_t fdinfo = STAILQ_HEAD_INITIALIZER(fdinfo); 107 static pdinfo_list_t cdinfo = STAILQ_HEAD_INITIALIZER(cdinfo); 108 static pdinfo_list_t hdinfo = STAILQ_HEAD_INITIALIZER(hdinfo); 109 110 /* 111 * efipart_inithandles() is used to build up the pdinfo list from 112 * block device handles. Then each devsw init callback is used to 113 * pick items from pdinfo and move to proper device list. 114 * In ideal world, we should end up with empty pdinfo once all 115 * devsw initializers are called. 116 */ 117 static pdinfo_list_t pdinfo = STAILQ_HEAD_INITIALIZER(pdinfo); 118 119 pdinfo_list_t * 120 efiblk_get_pdinfo_list(struct devsw *dev) 121 { 122 if (dev->dv_type == DEVT_DISK) 123 return (&hdinfo); 124 if (dev->dv_type == DEVT_CD) 125 return (&cdinfo); 126 if (dev->dv_type == DEVT_FD) 127 return (&fdinfo); 128 return (NULL); 129 } 130 131 /* XXX this gets called way way too often, investigate */ 132 pdinfo_t * 133 efiblk_get_pdinfo(struct devdesc *dev) 134 { 135 pdinfo_list_t *pdi; 136 pdinfo_t *pd = NULL; 137 138 pdi = efiblk_get_pdinfo_list(dev->d_dev); 139 if (pdi == NULL) 140 return (pd); 141 142 STAILQ_FOREACH(pd, pdi, pd_link) { 143 if (pd->pd_unit == dev->d_unit) 144 return (pd); 145 } 146 return (pd); 147 } 148 149 pdinfo_t * 150 efiblk_get_pdinfo_by_device_path(EFI_DEVICE_PATH *path) 151 { 152 EFI_HANDLE h; 153 EFI_STATUS status; 154 EFI_DEVICE_PATH *devp = path; 155 156 status = BS->LocateDevicePath(&blkio_guid, &devp, &h); 157 if (EFI_ERROR(status)) 158 return (NULL); 159 return (efiblk_get_pdinfo_by_handle(h)); 160 } 161 162 static bool 163 same_handle(pdinfo_t *pd, EFI_HANDLE h) 164 { 165 166 return (pd->pd_handle == h || pd->pd_alias == h); 167 } 168 169 pdinfo_t * 170 efiblk_get_pdinfo_by_handle(EFI_HANDLE h) 171 { 172 pdinfo_t *dp, *pp; 173 174 /* 175 * Check hard disks, then cd, then floppy 176 */ 177 STAILQ_FOREACH(dp, &hdinfo, pd_link) { 178 if (same_handle(dp, h)) 179 return (dp); 180 STAILQ_FOREACH(pp, &dp->pd_part, pd_link) { 181 if (same_handle(pp, h)) 182 return (pp); 183 } 184 } 185 STAILQ_FOREACH(dp, &cdinfo, pd_link) { 186 if (same_handle(dp, h)) 187 return (dp); 188 STAILQ_FOREACH(pp, &dp->pd_part, pd_link) { 189 if (same_handle(pp, h)) 190 return (pp); 191 } 192 } 193 STAILQ_FOREACH(dp, &fdinfo, pd_link) { 194 if (same_handle(dp, h)) 195 return (dp); 196 } 197 return (NULL); 198 } 199 200 static int 201 efiblk_pdinfo_count(pdinfo_list_t *pdi) 202 { 203 pdinfo_t *pd; 204 int i = 0; 205 206 STAILQ_FOREACH(pd, pdi, pd_link) { 207 i++; 208 } 209 return (i); 210 } 211 212 int 213 efipart_inithandles(void) 214 { 215 unsigned i, nin; 216 UINTN sz; 217 EFI_HANDLE *hin; 218 EFI_DEVICE_PATH *devpath; 219 EFI_BLOCK_IO *blkio; 220 EFI_STATUS status; 221 pdinfo_t *pd; 222 223 if (!STAILQ_EMPTY(&pdinfo)) 224 return (0); 225 226 sz = 0; 227 hin = NULL; 228 status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, hin); 229 if (status == EFI_BUFFER_TOO_SMALL) { 230 hin = malloc(sz); 231 status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, 232 hin); 233 if (EFI_ERROR(status)) 234 free(hin); 235 } 236 if (EFI_ERROR(status)) 237 return (efi_status_to_errno(status)); 238 239 nin = sz / sizeof(*hin); 240 #ifdef EFIPART_DEBUG 241 printf("%s: Got %d BLOCK IO MEDIA handle(s)\n", __func__, nin); 242 #endif 243 244 for (i = 0; i < nin; i++) { 245 /* 246 * Get devpath and open protocol. 247 * We should not get errors here 248 */ 249 if ((devpath = efi_lookup_devpath(hin[i])) == NULL) 250 continue; 251 252 status = OpenProtocolByHandle(hin[i], &blkio_guid, 253 (void **)&blkio); 254 if (EFI_ERROR(status)) { 255 printf("error %lu\n", EFI_ERROR_CODE(status)); 256 continue; 257 } 258 259 /* 260 * We assume the block size 512 or greater power of 2. 261 * Also skip devices with block size > 64k (16 is max 262 * ashift supported by zfs). 263 * iPXE is known to insert stub BLOCK IO device with 264 * BlockSize 1. 265 */ 266 if (blkio->Media->BlockSize < 512 || 267 blkio->Media->BlockSize > (1 << 16) || 268 !powerof2(blkio->Media->BlockSize)) { 269 continue; 270 } 271 272 /* Allowed values are 0, 1 and power of 2. */ 273 if (blkio->Media->IoAlign > 1 && 274 !powerof2(blkio->Media->IoAlign)) { 275 continue; 276 } 277 278 /* This is bad. */ 279 if ((pd = calloc(1, sizeof(*pd))) == NULL) { 280 printf("efipart_inithandles: Out of memory.\n"); 281 free(hin); 282 return (ENOMEM); 283 } 284 STAILQ_INIT(&pd->pd_part); 285 286 pd->pd_handle = hin[i]; 287 pd->pd_devpath = devpath; 288 pd->pd_blkio = blkio; 289 STAILQ_INSERT_TAIL(&pdinfo, pd, pd_link); 290 } 291 292 free(hin); 293 return (0); 294 } 295 296 static ACPI_HID_DEVICE_PATH * 297 efipart_floppy(EFI_DEVICE_PATH *node) 298 { 299 ACPI_HID_DEVICE_PATH *acpi; 300 301 if (DevicePathType(node) == ACPI_DEVICE_PATH && 302 DevicePathSubType(node) == ACPI_DP) { 303 acpi = (ACPI_HID_DEVICE_PATH *) node; 304 if (acpi->HID == EISA_PNP_ID(PNP0604) || 305 acpi->HID == EISA_PNP_ID(PNP0700) || 306 acpi->HID == EISA_PNP_ID(PNP0701)) { 307 return (acpi); 308 } 309 } 310 return (NULL); 311 } 312 313 static pdinfo_t * 314 efipart_find_parent(pdinfo_list_t *pdi, EFI_DEVICE_PATH *devpath) 315 { 316 pdinfo_t *pd; 317 318 STAILQ_FOREACH(pd, pdi, pd_link) { 319 if (efi_devpath_is_prefix(pd->pd_devpath, devpath)) 320 return (pd); 321 } 322 return (NULL); 323 } 324 325 static int 326 efipart_initfd(void) 327 { 328 EFI_DEVICE_PATH *node; 329 ACPI_HID_DEVICE_PATH *acpi; 330 pdinfo_t *parent, *fd; 331 332 restart: 333 STAILQ_FOREACH(fd, &pdinfo, pd_link) { 334 if ((node = efi_devpath_last_node(fd->pd_devpath)) == NULL) 335 continue; 336 337 if ((acpi = efipart_floppy(node)) == NULL) 338 continue; 339 340 STAILQ_REMOVE(&pdinfo, fd, pdinfo, pd_link); 341 parent = efipart_find_parent(&pdinfo, fd->pd_devpath); 342 if (parent != NULL) { 343 STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link); 344 parent->pd_alias = fd->pd_handle; 345 parent->pd_unit = acpi->UID; 346 free(fd); 347 fd = parent; 348 } else { 349 fd->pd_unit = acpi->UID; 350 } 351 fd->pd_devsw = &efipart_fddev; 352 STAILQ_INSERT_TAIL(&fdinfo, fd, pd_link); 353 goto restart; 354 } 355 356 bcache_add_dev(efiblk_pdinfo_count(&fdinfo)); 357 return (0); 358 } 359 360 /* 361 * Add or update entries with new handle data. 362 */ 363 static void 364 efipart_cdinfo_add(pdinfo_t *cd) 365 { 366 pdinfo_t *pd, *last; 367 368 STAILQ_FOREACH(pd, &cdinfo, pd_link) { 369 if (efi_devpath_is_prefix(pd->pd_devpath, cd->pd_devpath)) { 370 last = STAILQ_LAST(&pd->pd_part, pdinfo, pd_link); 371 if (last != NULL) 372 cd->pd_unit = last->pd_unit + 1; 373 else 374 cd->pd_unit = 0; 375 cd->pd_parent = pd; 376 cd->pd_devsw = &efipart_cddev; 377 STAILQ_INSERT_TAIL(&pd->pd_part, cd, pd_link); 378 return; 379 } 380 } 381 382 last = STAILQ_LAST(&cdinfo, pdinfo, pd_link); 383 if (last != NULL) 384 cd->pd_unit = last->pd_unit + 1; 385 else 386 cd->pd_unit = 0; 387 388 cd->pd_parent = NULL; 389 cd->pd_devsw = &efipart_cddev; 390 STAILQ_INSERT_TAIL(&cdinfo, cd, pd_link); 391 } 392 393 static bool 394 efipart_testcd(EFI_DEVICE_PATH *node, EFI_BLOCK_IO *blkio) 395 { 396 if (DevicePathType(node) == MEDIA_DEVICE_PATH && 397 DevicePathSubType(node) == MEDIA_CDROM_DP) { 398 return (true); 399 } 400 401 /* cd drive without the media. */ 402 if (blkio->Media->RemovableMedia && 403 !blkio->Media->MediaPresent) { 404 return (true); 405 } 406 407 return (false); 408 } 409 410 static void 411 efipart_updatecd(void) 412 { 413 EFI_DEVICE_PATH *devpath, *node; 414 EFI_STATUS status; 415 pdinfo_t *parent, *cd; 416 417 restart: 418 STAILQ_FOREACH(cd, &pdinfo, pd_link) { 419 if ((node = efi_devpath_last_node(cd->pd_devpath)) == NULL) 420 continue; 421 422 if (efipart_floppy(node) != NULL) 423 continue; 424 425 /* Is parent of this device already registered? */ 426 parent = efipart_find_parent(&cdinfo, cd->pd_devpath); 427 if (parent != NULL) { 428 STAILQ_REMOVE(&pdinfo, cd, pdinfo, pd_link); 429 efipart_cdinfo_add(cd); 430 goto restart; 431 } 432 433 if (!efipart_testcd(node, cd->pd_blkio)) 434 continue; 435 436 /* Find parent and unlink both parent and cd from pdinfo */ 437 STAILQ_REMOVE(&pdinfo, cd, pdinfo, pd_link); 438 parent = efipart_find_parent(&pdinfo, cd->pd_devpath); 439 if (parent != NULL) { 440 STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link); 441 efipart_cdinfo_add(parent); 442 } 443 444 if (parent == NULL) 445 parent = efipart_find_parent(&cdinfo, cd->pd_devpath); 446 447 /* 448 * If we come across a logical partition of subtype CDROM 449 * it doesn't refer to the CD filesystem itself, but rather 450 * to any usable El Torito boot image on it. In this case 451 * we try to find the parent device and add that instead as 452 * that will be the CD filesystem. 453 */ 454 if (DevicePathType(node) == MEDIA_DEVICE_PATH && 455 DevicePathSubType(node) == MEDIA_CDROM_DP && 456 parent == NULL) { 457 parent = calloc(1, sizeof(*parent)); 458 if (parent == NULL) { 459 printf("efipart_updatecd: out of memory\n"); 460 /* this device is lost but try again. */ 461 free(cd); 462 goto restart; 463 } 464 465 devpath = efi_devpath_trim(cd->pd_devpath); 466 if (devpath == NULL) { 467 printf("efipart_updatecd: out of memory\n"); 468 /* this device is lost but try again. */ 469 free(parent); 470 free(cd); 471 goto restart; 472 } 473 parent->pd_devpath = devpath; 474 status = BS->LocateDevicePath(&blkio_guid, 475 &parent->pd_devpath, &parent->pd_handle); 476 free(devpath); 477 if (EFI_ERROR(status)) { 478 printf("efipart_updatecd: error %lu\n", 479 EFI_ERROR_CODE(status)); 480 free(parent); 481 free(cd); 482 goto restart; 483 } 484 parent->pd_devpath = 485 efi_lookup_devpath(parent->pd_handle); 486 efipart_cdinfo_add(parent); 487 } 488 489 efipart_cdinfo_add(cd); 490 goto restart; 491 } 492 } 493 494 static int 495 efipart_initcd(void) 496 { 497 efipart_updatecd(); 498 499 bcache_add_dev(efiblk_pdinfo_count(&cdinfo)); 500 return (0); 501 } 502 503 static void 504 efipart_hdinfo_add(pdinfo_t *hd, HARDDRIVE_DEVICE_PATH *node) 505 { 506 pdinfo_t *pd, *last; 507 508 STAILQ_FOREACH(pd, &hdinfo, pd_link) { 509 if (efi_devpath_is_prefix(pd->pd_devpath, hd->pd_devpath)) { 510 /* Add the partition. */ 511 hd->pd_unit = node->PartitionNumber; 512 hd->pd_parent = pd; 513 hd->pd_devsw = &efipart_hddev; 514 STAILQ_INSERT_TAIL(&pd->pd_part, hd, pd_link); 515 return; 516 } 517 } 518 519 last = STAILQ_LAST(&hdinfo, pdinfo, pd_link); 520 if (last != NULL) 521 hd->pd_unit = last->pd_unit + 1; 522 else 523 hd->pd_unit = 0; 524 525 /* Add the disk. */ 526 hd->pd_devsw = &efipart_hddev; 527 STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link); 528 } 529 530 /* 531 * The MEDIA_FILEPATH_DP has device name. 532 * From U-Boot sources it looks like names are in the form 533 * of typeN:M, where type is interface type, N is disk id 534 * and M is partition id. 535 */ 536 static void 537 efipart_hdinfo_add_filepath(pdinfo_t *hd, FILEPATH_DEVICE_PATH *node) 538 { 539 char *pathname, *p; 540 int len; 541 pdinfo_t *last; 542 543 last = STAILQ_LAST(&hdinfo, pdinfo, pd_link); 544 if (last != NULL) 545 hd->pd_unit = last->pd_unit + 1; 546 else 547 hd->pd_unit = 0; 548 549 /* FILEPATH_DEVICE_PATH has 0 terminated string */ 550 len = ucs2len(node->PathName); 551 if ((pathname = malloc(len + 1)) == NULL) { 552 printf("Failed to add disk, out of memory\n"); 553 free(hd); 554 return; 555 } 556 cpy16to8(node->PathName, pathname, len + 1); 557 p = strchr(pathname, ':'); 558 559 /* 560 * Assume we are receiving handles in order, first disk handle, 561 * then partitions for this disk. If this assumption proves 562 * false, this code would need update. 563 */ 564 if (p == NULL) { /* no colon, add the disk */ 565 hd->pd_devsw = &efipart_hddev; 566 STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link); 567 free(pathname); 568 return; 569 } 570 p++; /* skip the colon */ 571 errno = 0; 572 hd->pd_unit = (int)strtol(p, NULL, 0); 573 if (errno != 0) { 574 printf("Bad unit number for partition \"%s\"\n", pathname); 575 free(pathname); 576 free(hd); 577 return; 578 } 579 580 /* 581 * We should have disk registered, if not, we are receiving 582 * handles out of order, and this code should be reworked 583 * to create "blank" disk for partition, and to find the 584 * disk based on PathName compares. 585 */ 586 if (last == NULL) { 587 printf("BUG: No disk for partition \"%s\"\n", pathname); 588 free(pathname); 589 free(hd); 590 return; 591 } 592 /* Add the partition. */ 593 hd->pd_parent = last; 594 hd->pd_devsw = &efipart_hddev; 595 STAILQ_INSERT_TAIL(&last->pd_part, hd, pd_link); 596 free(pathname); 597 } 598 599 static void 600 efipart_updatehd(void) 601 { 602 EFI_DEVICE_PATH *devpath, *node; 603 EFI_STATUS status; 604 pdinfo_t *parent, *hd; 605 606 restart: 607 STAILQ_FOREACH(hd, &pdinfo, pd_link) { 608 if ((node = efi_devpath_last_node(hd->pd_devpath)) == NULL) 609 continue; 610 611 if (efipart_floppy(node) != NULL) 612 continue; 613 614 if (efipart_testcd(node, hd->pd_blkio)) 615 continue; 616 617 if (DevicePathType(node) == HARDWARE_DEVICE_PATH && 618 (DevicePathSubType(node) == HW_PCI_DP || 619 DevicePathSubType(node) == HW_VENDOR_DP)) { 620 STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link); 621 efipart_hdinfo_add(hd, NULL); 622 goto restart; 623 } 624 625 if (DevicePathType(node) == MEDIA_DEVICE_PATH && 626 DevicePathSubType(node) == MEDIA_FILEPATH_DP) { 627 STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link); 628 efipart_hdinfo_add_filepath(hd, 629 (FILEPATH_DEVICE_PATH *)node); 630 goto restart; 631 } 632 633 STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link); 634 parent = efipart_find_parent(&pdinfo, hd->pd_devpath); 635 if (parent != NULL) { 636 STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link); 637 efipart_hdinfo_add(parent, NULL); 638 } else { 639 parent = efipart_find_parent(&hdinfo, hd->pd_devpath); 640 } 641 642 if (DevicePathType(node) == MEDIA_DEVICE_PATH && 643 DevicePathSubType(node) == MEDIA_HARDDRIVE_DP && 644 parent == NULL) { 645 parent = calloc(1, sizeof(*parent)); 646 if (parent == NULL) { 647 printf("efipart_updatehd: out of memory\n"); 648 /* this device is lost but try again. */ 649 free(hd); 650 goto restart; 651 } 652 653 devpath = efi_devpath_trim(hd->pd_devpath); 654 if (devpath == NULL) { 655 printf("efipart_updatehd: out of memory\n"); 656 /* this device is lost but try again. */ 657 free(parent); 658 free(hd); 659 goto restart; 660 } 661 662 parent->pd_devpath = devpath; 663 status = BS->LocateDevicePath(&blkio_guid, 664 &parent->pd_devpath, &parent->pd_handle); 665 free(devpath); 666 if (EFI_ERROR(status)) { 667 printf("efipart_updatehd: error %lu\n", 668 EFI_ERROR_CODE(status)); 669 free(parent); 670 free(hd); 671 goto restart; 672 } 673 674 parent->pd_devpath = 675 efi_lookup_devpath(&parent->pd_handle); 676 677 efipart_hdinfo_add(parent, NULL); 678 } 679 680 efipart_hdinfo_add(hd, (HARDDRIVE_DEVICE_PATH *)node); 681 goto restart; 682 } 683 } 684 685 static int 686 efipart_inithd(void) 687 { 688 689 efipart_updatehd(); 690 691 bcache_add_dev(efiblk_pdinfo_count(&hdinfo)); 692 return (0); 693 } 694 695 static int 696 efipart_print_common(struct devsw *dev, pdinfo_list_t *pdlist, int verbose) 697 { 698 int ret = 0; 699 EFI_BLOCK_IO *blkio; 700 EFI_STATUS status; 701 EFI_HANDLE h; 702 pdinfo_t *pd; 703 CHAR16 *text; 704 struct disk_devdesc pd_dev; 705 char line[80]; 706 707 if (STAILQ_EMPTY(pdlist)) 708 return (0); 709 710 printf("%s devices:", dev->dv_name); 711 if ((ret = pager_output("\n")) != 0) 712 return (ret); 713 714 STAILQ_FOREACH(pd, pdlist, pd_link) { 715 h = pd->pd_handle; 716 if (verbose) { /* Output the device path. */ 717 text = efi_devpath_name(efi_lookup_devpath(h)); 718 if (text != NULL) { 719 printf(" %S", text); 720 efi_free_devpath_name(text); 721 if ((ret = pager_output("\n")) != 0) 722 break; 723 } 724 } 725 snprintf(line, sizeof(line), 726 " %s%d", dev->dv_name, pd->pd_unit); 727 printf("%s:", line); 728 status = OpenProtocolByHandle(h, &blkio_guid, (void **)&blkio); 729 if (!EFI_ERROR(status)) { 730 printf(" %llu", 731 blkio->Media->LastBlock == 0? 0: 732 (unsigned long long) (blkio->Media->LastBlock + 1)); 733 if (blkio->Media->LastBlock != 0) { 734 printf(" X %u", blkio->Media->BlockSize); 735 } 736 printf(" blocks"); 737 if (blkio->Media->MediaPresent) { 738 if (blkio->Media->RemovableMedia) 739 printf(" (removable)"); 740 } else { 741 printf(" (no media)"); 742 } 743 if ((ret = pager_output("\n")) != 0) 744 break; 745 if (!blkio->Media->MediaPresent) 746 continue; 747 748 pd->pd_blkio = blkio; 749 pd_dev.dd.d_dev = dev; 750 pd_dev.dd.d_unit = pd->pd_unit; 751 pd_dev.d_slice = D_SLICENONE; 752 pd_dev.d_partition = D_PARTNONE; 753 ret = disk_open(&pd_dev, blkio->Media->BlockSize * 754 (blkio->Media->LastBlock + 1), 755 blkio->Media->BlockSize); 756 if (ret == 0) { 757 ret = disk_print(&pd_dev, line, verbose); 758 disk_close(&pd_dev); 759 if (ret != 0) 760 return (ret); 761 } else { 762 /* Do not fail from disk_open() */ 763 ret = 0; 764 } 765 } else { 766 if ((ret = pager_output("\n")) != 0) 767 break; 768 } 769 } 770 return (ret); 771 } 772 773 static int 774 efipart_printfd(int verbose) 775 { 776 return (efipart_print_common(&efipart_fddev, &fdinfo, verbose)); 777 } 778 779 static int 780 efipart_printcd(int verbose) 781 { 782 return (efipart_print_common(&efipart_cddev, &cdinfo, verbose)); 783 } 784 785 static int 786 efipart_printhd(int verbose) 787 { 788 return (efipart_print_common(&efipart_hddev, &hdinfo, verbose)); 789 } 790 791 static int 792 efipart_open(struct open_file *f, ...) 793 { 794 va_list args; 795 struct disk_devdesc *dev; 796 pdinfo_t *pd; 797 EFI_BLOCK_IO *blkio; 798 EFI_STATUS status; 799 800 va_start(args, f); 801 dev = va_arg(args, struct disk_devdesc *); 802 va_end(args); 803 if (dev == NULL) 804 return (EINVAL); 805 806 pd = efiblk_get_pdinfo((struct devdesc *)dev); 807 if (pd == NULL) 808 return (EIO); 809 810 if (pd->pd_blkio == NULL) { 811 status = OpenProtocolByHandle(pd->pd_handle, &blkio_guid, 812 (void **)&pd->pd_blkio); 813 if (EFI_ERROR(status)) 814 return (efi_status_to_errno(status)); 815 } 816 817 blkio = pd->pd_blkio; 818 if (!blkio->Media->MediaPresent) 819 return (EAGAIN); 820 821 pd->pd_open++; 822 if (pd->pd_bcache == NULL) 823 pd->pd_bcache = bcache_allocate(); 824 825 if (dev->dd.d_dev->dv_type == DEVT_DISK) { 826 int rc; 827 828 rc = disk_open(dev, 829 blkio->Media->BlockSize * (blkio->Media->LastBlock + 1), 830 blkio->Media->BlockSize); 831 if (rc != 0) { 832 pd->pd_open--; 833 if (pd->pd_open == 0) { 834 pd->pd_blkio = NULL; 835 bcache_free(pd->pd_bcache); 836 pd->pd_bcache = NULL; 837 } 838 } 839 return (rc); 840 } 841 return (0); 842 } 843 844 static int 845 efipart_close(struct open_file *f) 846 { 847 struct disk_devdesc *dev; 848 pdinfo_t *pd; 849 850 dev = (struct disk_devdesc *)(f->f_devdata); 851 if (dev == NULL) 852 return (EINVAL); 853 854 pd = efiblk_get_pdinfo((struct devdesc *)dev); 855 if (pd == NULL) 856 return (EINVAL); 857 858 pd->pd_open--; 859 if (pd->pd_open == 0) { 860 pd->pd_blkio = NULL; 861 bcache_free(pd->pd_bcache); 862 pd->pd_bcache = NULL; 863 } 864 if (dev->dd.d_dev->dv_type == DEVT_DISK) 865 return (disk_close(dev)); 866 return (0); 867 } 868 869 static int 870 efipart_ioctl(struct open_file *f, u_long cmd, void *data) 871 { 872 struct disk_devdesc *dev; 873 pdinfo_t *pd; 874 int rc; 875 876 dev = (struct disk_devdesc *)(f->f_devdata); 877 if (dev == NULL) 878 return (EINVAL); 879 880 pd = efiblk_get_pdinfo((struct devdesc *)dev); 881 if (pd == NULL) 882 return (EINVAL); 883 884 if (dev->dd.d_dev->dv_type == DEVT_DISK) { 885 rc = disk_ioctl(dev, cmd, data); 886 if (rc != ENOTTY) 887 return (rc); 888 } 889 890 switch (cmd) { 891 case DIOCGSECTORSIZE: 892 *(u_int *)data = pd->pd_blkio->Media->BlockSize; 893 break; 894 case DIOCGMEDIASIZE: 895 *(uint64_t *)data = pd->pd_blkio->Media->BlockSize * 896 (pd->pd_blkio->Media->LastBlock + 1); 897 break; 898 default: 899 return (ENOTTY); 900 } 901 902 return (0); 903 } 904 905 /* 906 * efipart_readwrite() 907 * Internal equivalent of efipart_strategy(), which operates on the 908 * media-native block size. This function expects all I/O requests 909 * to be within the media size and returns an error if such is not 910 * the case. 911 */ 912 static int 913 efipart_readwrite(EFI_BLOCK_IO *blkio, int rw, daddr_t blk, daddr_t nblks, 914 char *buf) 915 { 916 EFI_STATUS status; 917 918 if (blkio == NULL) 919 return (ENXIO); 920 if (blk < 0 || blk > blkio->Media->LastBlock) 921 return (EIO); 922 if ((blk + nblks - 1) > blkio->Media->LastBlock) 923 return (EIO); 924 925 switch (rw & F_MASK) { 926 case F_READ: 927 status = blkio->ReadBlocks(blkio, blkio->Media->MediaId, blk, 928 nblks * blkio->Media->BlockSize, buf); 929 break; 930 case F_WRITE: 931 if (blkio->Media->ReadOnly) 932 return (EROFS); 933 status = blkio->WriteBlocks(blkio, blkio->Media->MediaId, blk, 934 nblks * blkio->Media->BlockSize, buf); 935 break; 936 default: 937 return (ENOSYS); 938 } 939 940 if (EFI_ERROR(status)) { 941 printf("%s: rw=%d, blk=%ju size=%ju status=%lu\n", __func__, rw, 942 blk, nblks, EFI_ERROR_CODE(status)); 943 } 944 return (efi_status_to_errno(status)); 945 } 946 947 static int 948 efipart_strategy(void *devdata, int rw, daddr_t blk, size_t size, 949 char *buf, size_t *rsize) 950 { 951 struct bcache_devdata bcd; 952 struct disk_devdesc *dev; 953 pdinfo_t *pd; 954 955 dev = (struct disk_devdesc *)devdata; 956 if (dev == NULL) 957 return (EINVAL); 958 959 pd = efiblk_get_pdinfo((struct devdesc *)dev); 960 if (pd == NULL) 961 return (EINVAL); 962 963 if (pd->pd_blkio->Media->RemovableMedia && 964 !pd->pd_blkio->Media->MediaPresent) 965 return (ENXIO); 966 967 bcd.dv_strategy = efipart_realstrategy; 968 bcd.dv_devdata = devdata; 969 bcd.dv_cache = pd->pd_bcache; 970 971 if (dev->dd.d_dev->dv_type == DEVT_DISK) { 972 daddr_t offset; 973 974 offset = dev->d_offset * pd->pd_blkio->Media->BlockSize; 975 offset /= 512; 976 return (bcache_strategy(&bcd, rw, blk + offset, 977 size, buf, rsize)); 978 } 979 return (bcache_strategy(&bcd, rw, blk, size, buf, rsize)); 980 } 981 982 static int 983 efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size, 984 char *buf, size_t *rsize) 985 { 986 struct disk_devdesc *dev = (struct disk_devdesc *)devdata; 987 pdinfo_t *pd; 988 EFI_BLOCK_IO *blkio; 989 uint64_t off, disk_blocks, d_offset = 0; 990 char *blkbuf; 991 size_t blkoff, blksz, bio_size; 992 unsigned ioalign; 993 bool need_buf; 994 int rc; 995 uint64_t diskend, readstart; 996 997 if (dev == NULL || blk < 0) 998 return (EINVAL); 999 1000 pd = efiblk_get_pdinfo((struct devdesc *)dev); 1001 if (pd == NULL) 1002 return (EINVAL); 1003 1004 blkio = pd->pd_blkio; 1005 if (blkio == NULL) 1006 return (ENXIO); 1007 1008 if (size == 0 || (size % 512) != 0) 1009 return (EIO); 1010 1011 off = blk * 512; 1012 /* 1013 * Get disk blocks, this value is either for whole disk or for 1014 * partition. 1015 */ 1016 disk_blocks = 0; 1017 if (dev->dd.d_dev->dv_type == DEVT_DISK) { 1018 if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) { 1019 /* DIOCGMEDIASIZE does return bytes. */ 1020 disk_blocks /= blkio->Media->BlockSize; 1021 } 1022 d_offset = dev->d_offset; 1023 } 1024 if (disk_blocks == 0) 1025 disk_blocks = blkio->Media->LastBlock + 1 - d_offset; 1026 1027 /* make sure we don't read past disk end */ 1028 if ((off + size) / blkio->Media->BlockSize > d_offset + disk_blocks) { 1029 diskend = d_offset + disk_blocks; 1030 readstart = off / blkio->Media->BlockSize; 1031 1032 if (diskend <= readstart) { 1033 if (rsize != NULL) 1034 *rsize = 0; 1035 1036 return (EIO); 1037 } 1038 size = diskend - readstart; 1039 size = size * blkio->Media->BlockSize; 1040 } 1041 1042 need_buf = true; 1043 /* Do we need bounce buffer? */ 1044 if ((size % blkio->Media->BlockSize == 0) && 1045 (off % blkio->Media->BlockSize == 0)) 1046 need_buf = false; 1047 1048 /* Do we have IO alignment requirement? */ 1049 ioalign = blkio->Media->IoAlign; 1050 if (ioalign == 0) 1051 ioalign++; 1052 1053 if (ioalign > 1 && (uintptr_t)buf != roundup2((uintptr_t)buf, ioalign)) 1054 need_buf = true; 1055 1056 if (need_buf) { 1057 for (bio_size = BIO_BUFFER_SIZE; bio_size > 0; 1058 bio_size -= blkio->Media->BlockSize) { 1059 blkbuf = memalign(ioalign, bio_size); 1060 if (blkbuf != NULL) 1061 break; 1062 } 1063 } else { 1064 blkbuf = buf; 1065 bio_size = size; 1066 } 1067 1068 if (blkbuf == NULL) 1069 return (ENOMEM); 1070 1071 if (rsize != NULL) 1072 *rsize = size; 1073 1074 rc = 0; 1075 blk = off / blkio->Media->BlockSize; 1076 blkoff = off % blkio->Media->BlockSize; 1077 1078 while (size > 0) { 1079 size_t x = min(size, bio_size); 1080 1081 if (x < blkio->Media->BlockSize) 1082 x = 1; 1083 else 1084 x /= blkio->Media->BlockSize; 1085 1086 switch (rw & F_MASK) { 1087 case F_READ: 1088 blksz = blkio->Media->BlockSize * x - blkoff; 1089 if (size < blksz) 1090 blksz = size; 1091 1092 rc = efipart_readwrite(blkio, rw, blk, x, blkbuf); 1093 if (rc != 0) 1094 goto error; 1095 1096 if (need_buf) 1097 bcopy(blkbuf + blkoff, buf, blksz); 1098 break; 1099 case F_WRITE: 1100 rc = 0; 1101 if (blkoff != 0) { 1102 /* 1103 * We got offset to sector, read 1 sector to 1104 * blkbuf. 1105 */ 1106 x = 1; 1107 blksz = blkio->Media->BlockSize - blkoff; 1108 blksz = min(blksz, size); 1109 rc = efipart_readwrite(blkio, F_READ, blk, x, 1110 blkbuf); 1111 } else if (size < blkio->Media->BlockSize) { 1112 /* 1113 * The remaining block is not full 1114 * sector. Read 1 sector to blkbuf. 1115 */ 1116 x = 1; 1117 blksz = size; 1118 rc = efipart_readwrite(blkio, F_READ, blk, x, 1119 blkbuf); 1120 } else { 1121 /* We can write full sector(s). */ 1122 blksz = blkio->Media->BlockSize * x; 1123 } 1124 1125 if (rc != 0) 1126 goto error; 1127 /* 1128 * Put your Data In, Put your Data out, 1129 * Put your Data In, and shake it all about 1130 */ 1131 if (need_buf) 1132 bcopy(buf, blkbuf + blkoff, blksz); 1133 rc = efipart_readwrite(blkio, F_WRITE, blk, x, blkbuf); 1134 if (rc != 0) 1135 goto error; 1136 break; 1137 default: 1138 /* DO NOTHING */ 1139 rc = EROFS; 1140 goto error; 1141 } 1142 1143 blkoff = 0; 1144 buf += blksz; 1145 size -= blksz; 1146 blk += x; 1147 } 1148 1149 error: 1150 if (rsize != NULL) 1151 *rsize -= size; 1152 1153 if (need_buf) 1154 free(blkbuf); 1155 return (rc); 1156 } 1157