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