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