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