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