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