1 /* $NetBSD: sd.c,v 1.6 2019/01/08 19:41:09 jdolecek Exp $ */ 2 /* 3 * Copyright (c) 2010 KIYOHARA Takashi 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <sys/param.h> 29 #include <sys/types.h> 30 #include <sys/stdint.h> 31 32 #include <lib/libsa/stand.h> 33 #include <lib/libkern/libkern.h> 34 35 #include "boot.h" 36 #include "sdvar.h" 37 38 #ifdef DEBUG 39 #define DPRINTF(x) printf x 40 #else 41 #define DPRINTF(x) 42 #endif 43 44 #define SD_DEFAULT_BLKSIZE 512 45 46 47 struct sd_mode_sense_data { 48 struct scsi_mode_parameter_header_6 header; 49 struct scsi_general_block_descriptor blk_desc; 50 union scsi_disk_pages pages; 51 }; 52 53 static int sd_validate_blksize(int); 54 static uint64_t sd_read_capacity(struct sd_softc *, int *); 55 static int sd_get_simplifiedparms(struct sd_softc *); 56 static int sd_get_capacity(struct sd_softc *); 57 static int sd_get_parms_page4(struct sd_softc *, struct disk_parms *); 58 static int sd_get_parms_page5(struct sd_softc *, struct disk_parms *); 59 static int sd_get_parms(struct sd_softc *); 60 static void sdgetdefaultlabel(struct sd_softc *, struct disklabel *); 61 static int sdgetdisklabel(struct sd_softc *); 62 63 int sdopen(struct open_file *, ...); 64 int sdclose(struct open_file *); 65 int sdstrategy(void *, int, daddr_t, size_t, void *, size_t *); 66 67 68 static int 69 sd_validate_blksize(int len) 70 { 71 72 switch (len) { 73 case 256: 74 case 512: 75 case 1024: 76 case 2048: 77 case 4096: 78 return 1; 79 } 80 return 0; 81 } 82 83 /* 84 * sd_read_capacity: 85 * 86 * Find out from the device what its capacity is. 87 */ 88 static uint64_t 89 sd_read_capacity(struct sd_softc *sd, int *blksize) 90 { 91 union { 92 struct scsipi_read_capacity_10 cmd; 93 struct scsipi_read_capacity_16 cmd16; 94 } cmd; 95 union { 96 struct scsipi_read_capacity_10_data data; 97 struct scsipi_read_capacity_16_data data16; 98 } data; 99 uint64_t rv; 100 101 memset(&cmd, 0, sizeof(cmd)); 102 cmd.cmd.opcode = READ_CAPACITY_10; 103 104 /* 105 * If the command works, interpret the result as a 4 byte 106 * number of blocks 107 */ 108 rv = 0; 109 memset(&data, 0, sizeof(data.data)); 110 if (scsi_command(sd, (void *)&cmd.cmd, sizeof(cmd.cmd), 111 (void *)&data, sizeof(data.data)) != 0) 112 goto out; 113 114 if (_4btol(data.data.addr) != 0xffffffff) { 115 *blksize = _4btol(data.data.length); 116 rv = _4btol(data.data.addr) + 1; 117 goto out; 118 } 119 120 /* 121 * Device is larger than can be reflected by READ CAPACITY (10). 122 * Try READ CAPACITY (16). 123 */ 124 125 memset(&cmd, 0, sizeof(cmd)); 126 cmd.cmd16.opcode = READ_CAPACITY_16; 127 cmd.cmd16.byte2 = SRC16_SERVICE_ACTION; 128 _lto4b(sizeof(data.data16), cmd.cmd16.len); 129 130 memset(&data, 0, sizeof(data.data16)); 131 if (scsi_command(sd, (void *)&cmd.cmd16, sizeof(cmd.cmd16), 132 (void *)&data, sizeof(data.data16)) != 0) 133 goto out; 134 135 *blksize = _4btol(data.data16.length); 136 rv = _8btol(data.data16.addr) + 1; 137 138 out: 139 return rv; 140 } 141 142 static int 143 sd_get_simplifiedparms(struct sd_softc *sd) 144 { 145 struct { 146 struct scsi_mode_parameter_header_6 header; 147 /* no block descriptor */ 148 uint8_t pg_code; /* page code (should be 6) */ 149 uint8_t pg_length; /* page length (should be 11) */ 150 uint8_t wcd; /* bit0: cache disable */ 151 uint8_t lbs[2]; /* logical block size */ 152 uint8_t size[5]; /* number of log. blocks */ 153 uint8_t pp; /* power/performance */ 154 uint8_t flags; 155 uint8_t resvd; 156 } scsipi_sense; 157 struct disk_parms *dp = &sd->sc_params; 158 uint64_t blocks; 159 int error, blksize; 160 161 /* 162 * sd_read_capacity (ie "read capacity") and mode sense page 6 163 * give the same information. Do both for now, and check 164 * for consistency. 165 * XXX probably differs for removable media 166 */ 167 dp->blksize = SD_DEFAULT_BLKSIZE; 168 if ((blocks = sd_read_capacity(sd, &blksize)) == 0) 169 return SDGP_RESULT_OFFLINE; /* XXX? */ 170 171 error = scsi_mode_sense(sd, SMS_DBD, 6, 172 &scsipi_sense.header, sizeof(scsipi_sense)); 173 174 if (error != 0) 175 return SDGP_RESULT_OFFLINE; /* XXX? */ 176 177 dp->blksize = blksize; 178 if (!sd_validate_blksize(dp->blksize)) 179 dp->blksize = _2btol(scsipi_sense.lbs); 180 if (!sd_validate_blksize(dp->blksize)) 181 dp->blksize = SD_DEFAULT_BLKSIZE; 182 183 /* 184 * Create a pseudo-geometry. 185 */ 186 dp->heads = 64; 187 dp->sectors = 32; 188 dp->cyls = blocks / (dp->heads * dp->sectors); 189 dp->disksize = _5btol(scsipi_sense.size); 190 if (dp->disksize <= UINT32_MAX && dp->disksize != blocks) { 191 printf("RBC size: mode sense=%llu, get cap=%llu\n", 192 (unsigned long long)dp->disksize, 193 (unsigned long long)blocks); 194 dp->disksize = blocks; 195 } 196 dp->disksize512 = (dp->disksize * dp->blksize) / DEV_BSIZE; 197 198 return SDGP_RESULT_OK; 199 } 200 201 /* 202 * Get the scsi driver to send a full inquiry to the * device and use the 203 * results to fill out the disk parameter structure. 204 */ 205 static int 206 sd_get_capacity(struct sd_softc *sd) 207 { 208 struct disk_parms *dp = &sd->sc_params; 209 uint64_t blocks; 210 int error, blksize; 211 212 dp->disksize = blocks = sd_read_capacity(sd, &blksize); 213 if (blocks == 0) { 214 struct scsipi_read_format_capacities cmd; 215 struct { 216 struct scsipi_capacity_list_header header; 217 struct scsipi_capacity_descriptor desc; 218 } __packed data; 219 220 memset(&cmd, 0, sizeof(cmd)); 221 memset(&data, 0, sizeof(data)); 222 cmd.opcode = READ_FORMAT_CAPACITIES; 223 _lto2b(sizeof(data), cmd.length); 224 225 error = scsi_command(sd, 226 (void *)&cmd, sizeof(cmd), (void *)&data, sizeof(data)); 227 if (error == EFTYPE) 228 /* Medium Format Corrupted, handle as not formatted */ 229 return SDGP_RESULT_UNFORMATTED; 230 if (error || data.header.length == 0) 231 return SDGP_RESULT_OFFLINE; 232 233 switch (data.desc.byte5 & SCSIPI_CAP_DESC_CODE_MASK) { 234 case SCSIPI_CAP_DESC_CODE_RESERVED: 235 case SCSIPI_CAP_DESC_CODE_FORMATTED: 236 break; 237 238 case SCSIPI_CAP_DESC_CODE_UNFORMATTED: 239 return SDGP_RESULT_UNFORMATTED; 240 241 case SCSIPI_CAP_DESC_CODE_NONE: 242 return SDGP_RESULT_OFFLINE; 243 } 244 245 dp->disksize = blocks = _4btol(data.desc.nblks); 246 if (blocks == 0) 247 return SDGP_RESULT_OFFLINE; /* XXX? */ 248 249 blksize = _3btol(data.desc.blklen); 250 251 } else if (!sd_validate_blksize(blksize)) { 252 struct sd_mode_sense_data scsipi_sense; 253 int bsize; 254 255 memset(&scsipi_sense, 0, sizeof(scsipi_sense)); 256 error = scsi_mode_sense(sd, 0, 0, &scsipi_sense.header, 257 sizeof(struct scsi_mode_parameter_header_6) + 258 sizeof(scsipi_sense.blk_desc)); 259 if (!error) { 260 bsize = scsipi_sense.header.blk_desc_len; 261 262 if (bsize >= 8) 263 blksize = _3btol(scsipi_sense.blk_desc.blklen); 264 } 265 } 266 267 if (!sd_validate_blksize(blksize)) 268 blksize = SD_DEFAULT_BLKSIZE; 269 270 dp->blksize = blksize; 271 dp->disksize512 = (blocks * dp->blksize) / DEV_BSIZE; 272 return 0; 273 } 274 275 static int 276 sd_get_parms_page4(struct sd_softc *sd, struct disk_parms *dp) 277 { 278 struct sd_mode_sense_data scsipi_sense; 279 union scsi_disk_pages *pages; 280 size_t poffset; 281 int byte2, error; 282 283 byte2 = SMS_DBD; 284 again: 285 memset(&scsipi_sense, 0, sizeof(scsipi_sense)); 286 error = scsi_mode_sense(sd, byte2, 4, &scsipi_sense.header, 287 (byte2 ? 0 : sizeof(scsipi_sense.blk_desc)) + 288 sizeof(scsipi_sense.pages.rigid_geometry)); 289 if (error) { 290 if (byte2 == SMS_DBD) { 291 /* No result; try once more with DBD off */ 292 byte2 = 0; 293 goto again; 294 } 295 return error; 296 } 297 298 poffset = sizeof(scsipi_sense.header); 299 poffset += scsipi_sense.header.blk_desc_len; 300 301 if (poffset > sizeof(scsipi_sense) - sizeof(pages->rigid_geometry)) 302 return ERESTART; 303 304 pages = (void *)((u_long)&scsipi_sense + poffset); 305 #if 0 306 { 307 size_t i; 308 u_int8_t *p; 309 310 printf("page 4 sense:"); 311 for (i = sizeof(scsipi_sense), p = (void *)&scsipi_sense; i; 312 i--, p++) 313 printf(" %02x", *p); 314 printf("\n"); 315 printf("page 4 pg_code=%d sense=%p/%p\n", 316 pages->rigid_geometry.pg_code, &scsipi_sense, pages); 317 } 318 #endif 319 320 if ((pages->rigid_geometry.pg_code & PGCODE_MASK) != 4) 321 return ERESTART; 322 323 /* 324 * KLUDGE!! (for zone recorded disks) 325 * give a number of sectors so that sec * trks * cyls 326 * is <= disk_size 327 * can lead to wasted space! THINK ABOUT THIS ! 328 */ 329 dp->heads = pages->rigid_geometry.nheads; 330 dp->cyls = _3btol(pages->rigid_geometry.ncyl); 331 if (dp->heads == 0 || dp->cyls == 0) 332 return ERESTART; 333 dp->sectors = dp->disksize / (dp->heads * dp->cyls); /* XXX */ 334 335 dp->rot_rate = _2btol(pages->rigid_geometry.rpm); 336 if (dp->rot_rate == 0) 337 dp->rot_rate = 3600; 338 339 #if 0 340 printf("page 4 ok\n"); 341 #endif 342 return 0; 343 } 344 345 static int 346 sd_get_parms_page5(struct sd_softc *sd, struct disk_parms *dp) 347 { 348 struct sd_mode_sense_data scsipi_sense; 349 union scsi_disk_pages *pages; 350 size_t poffset; 351 int byte2, error; 352 353 byte2 = SMS_DBD; 354 again: 355 memset(&scsipi_sense, 0, sizeof(scsipi_sense)); 356 error = scsi_mode_sense(sd, 0, 5, &scsipi_sense.header, 357 (byte2 ? 0 : sizeof(scsipi_sense.blk_desc)) + 358 sizeof(scsipi_sense.pages.flex_geometry)); 359 if (error) { 360 if (byte2 == SMS_DBD) { 361 /* No result; try once more with DBD off */ 362 byte2 = 0; 363 goto again; 364 } 365 return error; 366 } 367 368 poffset = sizeof(scsipi_sense.header); 369 poffset += scsipi_sense.header.blk_desc_len; 370 371 if (poffset > sizeof(scsipi_sense) - sizeof(pages->flex_geometry)) 372 return ERESTART; 373 374 pages = (void *)((u_long)&scsipi_sense + poffset); 375 #if 0 376 { 377 size_t i; 378 u_int8_t *p; 379 380 printf("page 5 sense:"); 381 for (i = sizeof(scsipi_sense), p = (void *)&scsipi_sense; i; 382 i--, p++) 383 printf(" %02x", *p); 384 printf("\n"); 385 printf("page 5 pg_code=%d sense=%p/%p\n", 386 pages->flex_geometry.pg_code, &scsipi_sense, pages); 387 } 388 #endif 389 390 if ((pages->flex_geometry.pg_code & PGCODE_MASK) != 5) 391 return ERESTART; 392 393 dp->heads = pages->flex_geometry.nheads; 394 dp->cyls = _2btol(pages->flex_geometry.ncyl); 395 dp->sectors = pages->flex_geometry.ph_sec_tr; 396 if (dp->heads == 0 || dp->cyls == 0 || dp->sectors == 0) 397 return ERESTART; 398 399 dp->rot_rate = _2btol(pages->rigid_geometry.rpm); 400 if (dp->rot_rate == 0) 401 dp->rot_rate = 3600; 402 403 #if 0 404 printf("page 5 ok\n"); 405 #endif 406 return 0; 407 } 408 409 static int 410 sd_get_parms(struct sd_softc *sd) 411 { 412 struct disk_parms *dp = &sd->sc_params; 413 int error; 414 415 /* 416 * If offline, the SDEV_MEDIA_LOADED flag will be 417 * cleared by the caller if necessary. 418 */ 419 if (sd->sc_type == T_SIMPLE_DIRECT) { 420 error = sd_get_simplifiedparms(sd); 421 if (error) 422 return error; 423 goto ok; 424 } 425 426 error = sd_get_capacity(sd); 427 if (error) 428 return error; 429 430 if (sd->sc_type == T_OPTICAL) 431 goto page0; 432 433 if (sd->sc_flags & FLAGS_REMOVABLE) { 434 if (!sd_get_parms_page5(sd, dp) || 435 !sd_get_parms_page4(sd, dp)) 436 goto ok; 437 } else { 438 if (!sd_get_parms_page4(sd, dp) || 439 !sd_get_parms_page5(sd, dp)) 440 goto ok; 441 } 442 443 page0: 444 printf("fabricating a geometry\n"); 445 /* Try calling driver's method for figuring out geometry. */ 446 /* 447 * Use adaptec standard fictitious geometry 448 * this depends on which controller (e.g. 1542C is 449 * different. but we have to put SOMETHING here..) 450 */ 451 dp->heads = 64; 452 dp->sectors = 32; 453 dp->cyls = dp->disksize / (64 * 32); 454 dp->rot_rate = 3600; 455 456 ok: 457 DPRINTF(("disksize = %" PRId64 ", disksize512 = %" PRId64 ".\n", 458 dp->disksize, dp->disksize512)); 459 460 return 0; 461 } 462 463 static void 464 sdgetdefaultlabel(struct sd_softc *sd, struct disklabel *lp) 465 { 466 467 memset(lp, 0, sizeof(struct disklabel)); 468 469 lp->d_secsize = sd->sc_params.blksize; 470 lp->d_ntracks = sd->sc_params.heads; 471 lp->d_nsectors = sd->sc_params.sectors; 472 lp->d_ncylinders = sd->sc_params.cyls; 473 lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; 474 475 lp->d_type = DKTYPE_SCSI; 476 477 strncpy(lp->d_packname, "fictitious", 16); 478 lp->d_secperunit = sd->sc_params.disksize; 479 lp->d_rpm = sd->sc_params.rot_rate; 480 lp->d_interleave = 1; 481 lp->d_flags = (sd->sc_flags & FLAGS_REMOVABLE) ? D_REMOVABLE : 0; 482 483 lp->d_partitions[RAW_PART].p_offset = 0; 484 lp->d_partitions[RAW_PART].p_size = lp->d_secperunit; 485 lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; 486 lp->d_npartitions = RAW_PART + 1; 487 488 lp->d_magic = DISKMAGIC; 489 lp->d_magic2 = DISKMAGIC; 490 lp->d_checksum = dkcksum(lp); 491 } 492 493 /* 494 * Load the label information on the named device. 495 */ 496 static int 497 sdgetdisklabel(struct sd_softc *sd) 498 { 499 struct mbr_sector *mbr; 500 struct mbr_partition *mp; 501 struct disklabel *lp = &sd->sc_label; 502 size_t rsize; 503 int sector, i; 504 char *msg; 505 uint8_t buf[DEV_BSIZE]; 506 507 sdgetdefaultlabel(sd, lp); 508 509 if (lp->d_secpercyl == 0) { 510 lp->d_secpercyl = 100; 511 /* as long as it's not 0 - readdisklabel divides by it (?) */ 512 } 513 514 /* 515 * Find NetBSD Partition in DOS partition table. 516 */ 517 sector = 0; 518 if (sdstrategy(sd, F_READ, MBR_BBSECTOR, DEV_BSIZE, buf, &rsize)) 519 return EOFFSET; 520 521 mbr = (struct mbr_sector *)buf; 522 if (mbr->mbr_magic == htole16(MBR_MAGIC)) { 523 /* 524 * Lookup NetBSD slice. If there is none, go ahead 525 * and try to read the disklabel off sector #0. 526 */ 527 mp = mbr->mbr_parts; 528 for (i = 0; i < MBR_PART_COUNT; i++) { 529 if (mp[i].mbrp_type == MBR_PTYPE_NETBSD) { 530 sector = le32toh(mp[i].mbrp_start); 531 break; 532 } 533 } 534 } 535 536 if (sdstrategy(sd, F_READ, sector + LABELSECTOR, DEV_BSIZE, 537 buf, &rsize)) 538 return EOFFSET; 539 540 msg = getdisklabel((const char *)buf + LABELOFFSET, &sd->sc_label); 541 if (msg) 542 printf("scsi/%d/%d/%d: getdisklabel: %s\n", 543 sd->sc_bus, sd->sc_target, sd->sc_lun, msg); 544 545 /* check partition */ 546 if ((sd->sc_part >= lp->d_npartitions) || 547 (lp->d_partitions[sd->sc_part].p_fstype == FS_UNUSED)) { 548 DPRINTF(("illegal partition\n")); 549 return EPART; 550 } 551 552 DPRINTF(("label info: d_secsize %d, d_nsectors %d, d_ncylinders %d," 553 " d_ntracks %d, d_secpercyl %d\n", 554 sd->sc_label.d_secsize, 555 sd->sc_label.d_nsectors, 556 sd->sc_label.d_ncylinders, 557 sd->sc_label.d_ntracks, 558 sd->sc_label.d_secpercyl)); 559 560 return 0; 561 } 562 563 /* 564 * Open device (read drive parameters and disklabel) 565 */ 566 int 567 sdopen(struct open_file *f, ...) 568 { 569 struct sd_softc *sd; 570 struct scsi_test_unit_ready cmd; 571 struct scsipi_inquiry_data *inqbuf; 572 u_int bus, target, lun, part; 573 int error; 574 char buf[SCSIPI_INQUIRY_LENGTH_SCSI2]; 575 va_list ap; 576 577 va_start(ap, f); 578 bus = va_arg(ap, u_int); 579 target = va_arg(ap, u_int); 580 lun = va_arg(ap, u_int); 581 part = va_arg(ap, u_int); 582 va_end(ap); 583 584 DPRINTF(("sdopen: scsi/%d%d%d/0_%d\n", bus, target, lun, part)); 585 586 sd = alloc(sizeof(struct sd_softc)); 587 if (sd == NULL) 588 return ENOMEM; 589 590 memset(sd, 0, sizeof(struct sd_softc)); 591 592 sd->sc_part = part; 593 sd->sc_lun = lun; 594 sd->sc_target = target; 595 sd->sc_bus = bus; 596 597 if ((error = scsi_inquire(sd, sizeof(buf), buf)) != 0) 598 return error; 599 600 inqbuf = (struct scsipi_inquiry_data *)buf; 601 602 sd->sc_type = inqbuf->device & SID_TYPE; 603 604 /* 605 * Determine the operating mode capabilities of the device. 606 */ 607 if ((inqbuf->version & SID_ANSII) >= 2) { 608 // if ((inqbuf->flags3 & SID_CmdQue) != 0) 609 // sd->sc_cap |= PERIPH_CAP_TQING; 610 if ((inqbuf->flags3 & SID_Sync) != 0) 611 sd->sc_cap |= PERIPH_CAP_SYNC; 612 613 /* SPC-2 */ 614 if ((inqbuf->version & SID_ANSII) >= 3) { 615 /* 616 * Report ST clocking though CAP_WIDExx/CAP_SYNC. 617 * If the device only supports DT, clear these 618 * flags (DT implies SYNC and WIDE) 619 */ 620 switch (inqbuf->flags4 & SID_Clocking) { 621 case SID_CLOCKING_DT_ONLY: 622 sd->sc_cap &= ~PERIPH_CAP_SYNC; 623 break; 624 } 625 } 626 } 627 sd->sc_flags = 628 (inqbuf->dev_qual2 & SID_REMOVABLE) ? FLAGS_REMOVABLE : 0; 629 630 memset(&cmd, 0, sizeof(cmd)); 631 cmd.opcode = SCSI_TEST_UNIT_READY; 632 if ((error = scsi_command(sd, (void *)&cmd, sizeof(cmd), NULL, 0)) != 0) 633 return error; 634 635 if (sd->sc_flags & FLAGS_REMOVABLE) { 636 printf("XXXXX: removable device found. will not support\n"); 637 } 638 if (!(sd->sc_flags & FLAGS_MEDIA_LOADED)) 639 sd->sc_flags |= FLAGS_MEDIA_LOADED; 640 641 if ((error = sd_get_parms(sd)) != 0) 642 return error; 643 644 strncpy(sd->sc_label.d_typename, inqbuf->product, 16); 645 if ((error = sdgetdisklabel(sd)) != 0) 646 return error; 647 648 f->f_devdata = sd; 649 return 0; 650 } 651 652 /* 653 * Close device. 654 */ 655 int 656 sdclose(struct open_file *f) 657 { 658 659 return 0; 660 } 661 662 /* 663 * Read some data. 664 */ 665 int 666 sdstrategy(void *f, int rw, daddr_t dblk, size_t size, void *p, size_t *rsize) 667 { 668 struct sd_softc *sd; 669 struct disklabel *lp; 670 struct partition *pp; 671 struct scsipi_generic *cmdp; 672 struct scsipi_rw_16 cmd16; 673 struct scsipi_rw_10 cmd_big; 674 struct scsi_rw_6 cmd_small; 675 daddr_t blkno; 676 int cmdlen, nsect, i; 677 uint8_t *buf; 678 679 if (size == 0) 680 return 0; 681 682 if (rw != F_READ) 683 return EOPNOTSUPP; 684 685 buf = p; 686 sd = f; 687 lp = &sd->sc_label; 688 pp = &lp->d_partitions[sd->sc_part]; 689 690 if (!(sd->sc_flags & FLAGS_MEDIA_LOADED)) 691 return EIO; 692 693 nsect = howmany(size, lp->d_secsize); 694 blkno = dblk + pp->p_offset; 695 696 for (i = 0; i < nsect; i++, blkno++) { 697 int error; 698 699 /* 700 * Fill out the scsi command. Use the smallest CDB possible 701 * (6-byte, 10-byte, or 16-byte). 702 */ 703 if ((blkno & 0x1fffff) == blkno) { 704 /* 6-byte CDB */ 705 memset(&cmd_small, 0, sizeof(cmd_small)); 706 cmd_small.opcode = SCSI_READ_6_COMMAND; 707 _lto3b(blkno, cmd_small.addr); 708 cmd_small.length = 1; 709 cmdlen = sizeof(cmd_small); 710 cmdp = (struct scsipi_generic *)&cmd_small; 711 } else if ((blkno & 0xffffffff) == blkno) { 712 /* 10-byte CDB */ 713 memset(&cmd_big, 0, sizeof(cmd_big)); 714 cmd_big.opcode = READ_10; 715 _lto4b(blkno, cmd_big.addr); 716 _lto2b(1, cmd_big.length); 717 cmdlen = sizeof(cmd_big); 718 cmdp = (struct scsipi_generic *)&cmd_big; 719 } else { 720 /* 16-byte CDB */ 721 memset(&cmd16, 0, sizeof(cmd16)); 722 cmd16.opcode = READ_16; 723 _lto8b(blkno, cmd16.addr); 724 _lto4b(1, cmd16.length); 725 cmdlen = sizeof(cmd16); 726 cmdp = (struct scsipi_generic *)&cmd16; 727 } 728 729 error = scsi_command(sd, cmdp, cmdlen, buf, lp->d_secsize); 730 if (error) 731 return error; 732 733 buf += lp->d_secsize; 734 } 735 736 *rsize = size; 737 return 0; 738 } 739