1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <fcntl.h> 27 #include <libdevinfo.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <stropts.h> 32 #include <sys/dkio.h> 33 #include <sys/sunddi.h> 34 #include <sys/types.h> 35 #include <unistd.h> 36 #include <kstat.h> 37 #include <errno.h> 38 #include <devid.h> 39 #include <dirent.h> 40 41 /* included for uscsi */ 42 #include <strings.h> 43 #include <sys/stat.h> 44 #include <sys/scsi/impl/types.h> 45 #include <sys/scsi/impl/uscsi.h> 46 #include <sys/scsi/generic/commands.h> 47 #include <sys/scsi/impl/commands.h> 48 #include <sys/scsi/generic/mode.h> 49 #include <sys/byteorder.h> 50 51 #include "libdiskmgt.h" 52 #include "disks_private.h" 53 54 #define KSTAT_CLASS_DISK "disk" 55 #define KSTAT_CLASS_ERROR "device_error" 56 57 #define SCSIBUFLEN 0xffff 58 59 /* byte get macros */ 60 #define b3(a) (((a)>>24) & 0xFF) 61 #define b2(a) (((a)>>16) & 0xFF) 62 #define b1(a) (((a)>>8) & 0xFF) 63 #define b0(a) (((a)>>0) & 0xFF) 64 65 static char *kstat_err_names[] = { 66 "Soft Errors", 67 "Hard Errors", 68 "Transport Errors", 69 "Media Error", 70 "Device Not Ready", 71 "No Device", 72 "Recoverable", 73 "Illegal Request", 74 "Predictive Failure Analysis", 75 NULL 76 }; 77 78 static char *err_attr_names[] = { 79 DM_NSOFTERRS, 80 DM_NHARDERRS, 81 DM_NTRANSERRS, 82 DM_NMEDIAERRS, 83 DM_NDNRERRS, 84 DM_NNODEVERRS, 85 DM_NRECOVERRS, 86 DM_NILLREQERRS, 87 DM_FAILING, 88 NULL 89 }; 90 91 /* 92 * **************** begin uscsi stuff **************** 93 */ 94 95 #if defined(_BIT_FIELDS_LTOH) 96 #elif defined(_BIT_FIELDS_HTOL) 97 #else 98 #error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined 99 #endif 100 101 struct conf_feature { 102 uchar_t feature[2]; /* common to all */ 103 #if defined(_BIT_FIELDS_LTOH) 104 uchar_t current : 1; 105 uchar_t persist : 1; 106 uchar_t version : 4; 107 uchar_t reserved: 2; 108 #else 109 uchar_t reserved: 2; 110 uchar_t version : 4; 111 uchar_t persist : 1; 112 uchar_t current : 1; 113 #endif /* _BIT_FIELDS_LTOH */ 114 uchar_t len; 115 union features { 116 struct generic { 117 uchar_t data[1]; 118 } gen; 119 uchar_t data[1]; 120 struct profile_list { 121 uchar_t profile[2]; 122 #if defined(_BIT_FIELDS_LTOH) 123 uchar_t current_p : 1; 124 uchar_t reserved1 : 7; 125 #else 126 uchar_t reserved1 : 7; 127 uchar_t current_p : 1; 128 #endif /* _BIT_FIELDS_LTOH */ 129 uchar_t reserved2; 130 } plist[1]; 131 struct core { 132 uchar_t phys[4]; 133 } core; 134 struct morphing { 135 #if defined(_BIT_FIELDS_LTOH) 136 uchar_t async : 1; 137 uchar_t reserved1 : 7; 138 #else 139 uchar_t reserved1 : 7; 140 uchar_t async : 1; 141 #endif /* _BIT_FIELDS_LTOH */ 142 uchar_t reserved[3]; 143 } morphing; 144 struct removable { 145 #if defined(_BIT_FIELDS_LTOH) 146 uchar_t lock : 1; 147 uchar_t resv1 : 1; 148 uchar_t pvnt : 1; 149 uchar_t eject : 1; 150 uchar_t resv2 : 1; 151 uchar_t loading : 3; 152 #else 153 uchar_t loading : 3; 154 uchar_t resv2 : 1; 155 uchar_t eject : 1; 156 uchar_t pvnt : 1; 157 uchar_t resv1 : 1; 158 uchar_t lock : 1; 159 #endif /* _BIT_FIELDS_LTOH */ 160 uchar_t reserved[3]; 161 } removable; 162 struct random_readable { 163 uchar_t lbsize[4]; 164 uchar_t blocking[2]; 165 #if defined(_BIT_FIELDS_LTOH) 166 uchar_t pp : 1; 167 uchar_t reserved1 : 7; 168 #else 169 uchar_t reserved1 : 7; 170 uchar_t pp : 1; 171 #endif /* _BIT_FIELDS_LTOH */ 172 uchar_t reserved; 173 } rread; 174 struct cd_read { 175 #if defined(_BIT_FIELDS_LTOH) 176 uchar_t cdtext : 1; 177 uchar_t c2flag : 1; 178 uchar_t reserved1 : 6; 179 #else 180 uchar_t reserved1 : 6; 181 uchar_t c2flag : 1; 182 uchar_t cdtext : 1; 183 #endif /* _BIT_FIELDS_LTOH */ 184 } cdread; 185 struct cd_audio { 186 #if defined(_BIT_FIELDS_LTOH) 187 uchar_t sv : 1; 188 uchar_t scm : 1; 189 uchar_t scan : 1; 190 uchar_t resv : 5; 191 #else 192 uchar_t resv : 5; 193 uchar_t scan : 1; 194 uchar_t scm : 1; 195 uchar_t sv : 1; 196 #endif /* _BIT_FIELDS_LTOH */ 197 uchar_t reserved; 198 uchar_t numlevels[2]; 199 } audio; 200 struct dvd_css { 201 uchar_t reserved[3]; 202 uchar_t version; 203 } dvdcss; 204 } features; 205 }; 206 207 #define PROF_NON_REMOVABLE 0x0001 208 #define PROF_REMOVABLE 0x0002 209 #define PROF_MAGNETO_OPTICAL 0x0003 210 #define PROF_OPTICAL_WO 0x0004 211 #define PROF_OPTICAL_ASMO 0x0005 212 #define PROF_CDROM 0x0008 213 #define PROF_CDR 0x0009 214 #define PROF_CDRW 0x000a 215 #define PROF_DVDROM 0x0010 216 #define PROF_DVDR 0x0011 217 #define PROF_DVDRAM 0x0012 218 #define PROF_DVDRW_REST 0x0013 219 #define PROF_DVDRW_SEQ 0x0014 220 #define PROF_DVDRW 0x001a 221 #define PROF_DDCD_ROM 0x0020 222 #define PROF_DDCD_R 0x0021 223 #define PROF_DDCD_RW 0x0022 224 #define PROF_NON_CONFORMING 0xffff 225 226 struct get_configuration { 227 uchar_t len[4]; 228 uchar_t reserved[2]; 229 uchar_t curprof[2]; 230 struct conf_feature feature; 231 }; 232 233 struct capabilities { 234 #if defined(_BIT_FIELDS_LTOH) 235 uchar_t pagecode : 6; 236 uchar_t resv1 : 1; 237 uchar_t ps : 1; 238 #else 239 uchar_t ps : 1; 240 uchar_t resv1 : 1; 241 uchar_t pagecode : 6; 242 #endif /* _BIT_FIELDS_LTOH */ 243 uchar_t pagelen; 244 #if defined(_BIT_FIELDS_LTOH) 245 /* read capabilities */ 246 uchar_t cdr_read : 1; 247 uchar_t cdrw_read : 1; 248 uchar_t method2 : 1; 249 uchar_t dvdrom_read : 1; 250 uchar_t dvdr_read : 1; 251 uchar_t dvdram_read : 1; 252 uchar_t resv2 : 2; 253 #else 254 uchar_t resv2 : 2; 255 uchar_t dvdram_read : 1; 256 uchar_t dvdr_read : 1; 257 uchar_t dvdrom_read : 1; 258 uchar_t method2 : 1; 259 uchar_t cdrw_read : 1; 260 uchar_t cdr_read : 1; 261 #endif /* _BIT_FIELDS_LTOH */ 262 #if defined(_BIT_FIELDS_LTOH) 263 /* write capabilities */ 264 uchar_t cdr_write : 1; 265 uchar_t cdrw_write : 1; 266 uchar_t testwrite : 1; 267 uchar_t resv3 : 1; 268 uchar_t dvdr_write : 1; 269 uchar_t dvdram_write : 1; 270 uchar_t resv4 : 2; 271 #else 272 /* write capabilities */ 273 uchar_t resv4 : 2; 274 uchar_t dvdram_write : 1; 275 uchar_t dvdr_write : 1; 276 uchar_t resv3 : 1; 277 uchar_t testwrite : 1; 278 uchar_t cdrw_write : 1; 279 uchar_t cdr_write : 1; 280 #endif /* _BIT_FIELDS_LTOH */ 281 uchar_t misc0; 282 uchar_t misc1; 283 uchar_t misc2; 284 uchar_t misc3; 285 uchar_t obsolete0[2]; 286 uchar_t numvlevels[2]; 287 uchar_t bufsize[2]; 288 uchar_t obsolete1[4]; 289 uchar_t resv5; 290 uchar_t misc4; 291 uchar_t obsolete2; 292 uchar_t copymgt[2]; 293 /* there is more to this page, but nothing we care about */ 294 }; 295 296 struct mode_header_g2 { 297 uchar_t modelen[2]; 298 uchar_t obsolete; 299 uchar_t reserved[3]; 300 uchar_t desclen[2]; 301 }; 302 303 /* 304 * Mode sense/select page header information 305 */ 306 struct scsi_ms_header { 307 struct mode_header mode_header; 308 struct block_descriptor block_descriptor; 309 }; 310 311 #define MODESENSE_PAGE_LEN(p) (((int)((struct mode_page *)p)->length) + \ 312 sizeof (struct mode_page)) 313 314 #define MODE_SENSE_PC_CURRENT (0 << 6) 315 #define MODE_SENSE_PC_DEFAULT (2 << 6) 316 #define MODE_SENSE_PC_SAVED (3 << 6) 317 318 #define MAX_MODE_SENSE_SIZE 255 319 #define IMPOSSIBLE_SCSI_STATUS 0xff 320 321 /* 322 * ********** end of uscsi stuff ************ 323 */ 324 325 static descriptor_t **apply_filter(descriptor_t **drives, int filter[], 326 int *errp); 327 static int check_atapi(int fd); 328 static int conv_drive_type(uint_t drive_type); 329 static uint64_t convnum(uchar_t *nptr, int len); 330 static void fill_command_g1(struct uscsi_cmd *cmd, 331 union scsi_cdb *cdb, caddr_t buff, int blen); 332 static void fill_general_page_cdb_g1(union scsi_cdb *cdb, 333 int command, int lun, uchar_t c0, uchar_t c1); 334 static void fill_mode_page_cdb(union scsi_cdb *cdb, int page); 335 static descriptor_t **get_assoc_alias(disk_t *diskp, int *errp); 336 static descriptor_t **get_assoc_controllers(descriptor_t *dp, int *errp); 337 static descriptor_t **get_assoc_paths(descriptor_t *dp, int *errp); 338 static int get_attrs(disk_t *diskp, int fd, char *opath, 339 nvlist_t *nvp); 340 static int get_cdrom_drvtype(int fd); 341 static int get_disk_kstats(kstat_ctl_t *kc, char *diskname, 342 char *classname, nvlist_t *stats); 343 static void get_drive_type(disk_t *dp, int fd); 344 static int get_err_kstats(kstat_ctl_t *kc, char *diskname, 345 nvlist_t *stats); 346 static int get_io_kstats(kstat_ctl_t *kc, char *diskname, 347 nvlist_t *stats); 348 static int get_kstat_vals(kstat_t *ksp, nvlist_t *stats); 349 static char *get_err_attr_name(char *kstat_name); 350 static int get_rpm(disk_t *dp, int fd); 351 static int get_solidstate(disk_t *dp, int fd); 352 static int update_stat64(nvlist_t *stats, char *attr, 353 uint64_t value); 354 static int update_stat32(nvlist_t *stats, char *attr, 355 uint32_t value); 356 static int uscsi_mode_sense(int fd, int page_code, 357 int page_control, caddr_t page_data, int page_size, 358 struct scsi_ms_header *header); 359 360 descriptor_t ** 361 drive_get_assoc_descriptors(descriptor_t *dp, dm_desc_type_t type, 362 int *errp) 363 { 364 switch (type) { 365 case DM_CONTROLLER: 366 return (get_assoc_controllers(dp, errp)); 367 case DM_PATH: 368 return (get_assoc_paths(dp, errp)); 369 case DM_ALIAS: 370 return (get_assoc_alias(dp->p.disk, errp)); 371 case DM_MEDIA: 372 return (media_get_assocs(dp, errp)); 373 } 374 375 *errp = EINVAL; 376 return (NULL); 377 } 378 379 /* 380 * Get the drive descriptors for the given media/alias/devpath. 381 */ 382 descriptor_t ** 383 drive_get_assocs(descriptor_t *desc, int *errp) 384 { 385 descriptor_t **drives; 386 387 /* at most one drive is associated with these descriptors */ 388 389 drives = (descriptor_t **)calloc(2, sizeof (descriptor_t *)); 390 if (drives == NULL) { 391 *errp = ENOMEM; 392 return (NULL); 393 } 394 395 drives[0] = cache_get_desc(DM_DRIVE, desc->p.disk, NULL, NULL, errp); 396 if (*errp != 0) { 397 cache_free_descriptors(drives); 398 return (NULL); 399 } 400 401 drives[1] = NULL; 402 403 return (drives); 404 } 405 406 nvlist_t * 407 drive_get_attributes(descriptor_t *dp, int *errp) 408 { 409 nvlist_t *attrs = NULL; 410 int fd; 411 char opath[MAXPATHLEN]; 412 413 if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) { 414 *errp = ENOMEM; 415 return (NULL); 416 } 417 418 opath[0] = 0; 419 fd = drive_open_disk(dp->p.disk, opath, sizeof (opath)); 420 421 if ((*errp = get_attrs(dp->p.disk, fd, opath, attrs)) != 0) { 422 nvlist_free(attrs); 423 attrs = NULL; 424 } 425 426 if (fd >= 0) { 427 (void) close(fd); 428 } 429 430 return (attrs); 431 } 432 433 /* 434 * Check if we have the drive in our list, based upon the device id. 435 * We got the device id from the dev tree walk. This is encoded 436 * using devid_str_encode(3DEVID). In order to check the device ids we need 437 * to use the devid_compare(3DEVID) function, so we need to decode the 438 * string representation of the device id. 439 */ 440 descriptor_t * 441 drive_get_descriptor_by_name(char *name, int *errp) 442 { 443 ddi_devid_t devid; 444 descriptor_t **drives; 445 descriptor_t *drive = NULL; 446 int i; 447 448 if (name == NULL || devid_str_decode(name, &devid, NULL) != 0) { 449 *errp = EINVAL; 450 return (NULL); 451 } 452 453 drives = cache_get_descriptors(DM_DRIVE, errp); 454 if (*errp != 0) { 455 devid_free(devid); 456 return (NULL); 457 } 458 459 /* 460 * We have to loop through all of them, freeing the ones we don't 461 * want. Once drive is set, we don't need to compare any more. 462 */ 463 for (i = 0; drives[i]; i++) { 464 if (drive == NULL && drives[i]->p.disk->devid != NULL && 465 devid_compare(devid, drives[i]->p.disk->devid) == 0) { 466 drive = drives[i]; 467 468 } else { 469 /* clean up the unused descriptor */ 470 cache_free_descriptor(drives[i]); 471 } 472 } 473 free(drives); 474 devid_free(devid); 475 476 if (drive == NULL) { 477 *errp = ENODEV; 478 } 479 480 return (drive); 481 } 482 483 descriptor_t ** 484 drive_get_descriptors(int filter[], int *errp) 485 { 486 descriptor_t **drives; 487 488 drives = cache_get_descriptors(DM_DRIVE, errp); 489 if (*errp != 0) { 490 return (NULL); 491 } 492 493 if (filter != NULL && filter[0] != DM_FILTER_END) { 494 descriptor_t **found; 495 found = apply_filter(drives, filter, errp); 496 if (*errp != 0) { 497 drives = NULL; 498 } else { 499 drives = found; 500 } 501 } 502 503 return (drives); 504 } 505 506 char * 507 drive_get_name(descriptor_t *dp) 508 { 509 return (dp->p.disk->device_id); 510 } 511 512 nvlist_t * 513 drive_get_stats(descriptor_t *dp, int stat_type, int *errp) 514 { 515 disk_t *diskp; 516 nvlist_t *stats; 517 518 diskp = dp->p.disk; 519 520 if (nvlist_alloc(&stats, NVATTRS, 0) != 0) { 521 *errp = ENOMEM; 522 return (NULL); 523 } 524 525 if (stat_type == DM_DRV_STAT_PERFORMANCE || 526 stat_type == DM_DRV_STAT_DIAGNOSTIC) { 527 528 alias_t *ap; 529 kstat_ctl_t *kc; 530 531 ap = diskp->aliases; 532 if (ap == NULL || ap->kstat_name == NULL) { 533 nvlist_free(stats); 534 *errp = EACCES; 535 return (NULL); 536 } 537 538 if ((kc = kstat_open()) == NULL) { 539 nvlist_free(stats); 540 *errp = EACCES; 541 return (NULL); 542 } 543 544 while (ap != NULL) { 545 int status; 546 547 if (ap->kstat_name == NULL) { 548 continue; 549 } 550 551 if (stat_type == DM_DRV_STAT_PERFORMANCE) { 552 status = get_io_kstats(kc, ap->kstat_name, stats); 553 } else { 554 status = get_err_kstats(kc, ap->kstat_name, stats); 555 } 556 557 if (status != 0) { 558 nvlist_free(stats); 559 (void) kstat_close(kc); 560 *errp = ENOMEM; 561 return (NULL); 562 } 563 564 ap = ap->next; 565 } 566 567 (void) kstat_close(kc); 568 569 *errp = 0; 570 return (stats); 571 } 572 573 if (stat_type == DM_DRV_STAT_TEMPERATURE) { 574 int fd; 575 576 if ((fd = drive_open_disk(diskp, NULL, 0)) >= 0) { 577 struct dk_temperature temp; 578 579 if (ioctl(fd, DKIOCGTEMPERATURE, &temp) >= 0) { 580 if (nvlist_add_uint32(stats, DM_TEMPERATURE, 581 temp.dkt_cur_temp) != 0) { 582 *errp = ENOMEM; 583 nvlist_free(stats); 584 return (NULL); 585 } 586 } else { 587 *errp = errno; 588 nvlist_free(stats); 589 return (NULL); 590 } 591 (void) close(fd); 592 } else { 593 *errp = errno; 594 nvlist_free(stats); 595 return (NULL); 596 } 597 598 *errp = 0; 599 return (stats); 600 } 601 602 nvlist_free(stats); 603 *errp = EINVAL; 604 return (NULL); 605 } 606 607 int 608 drive_make_descriptors() 609 { 610 int error; 611 disk_t *dp; 612 613 dp = cache_get_disklist(); 614 while (dp != NULL) { 615 cache_load_desc(DM_DRIVE, dp, NULL, NULL, &error); 616 if (error != 0) { 617 return (error); 618 } 619 dp = dp->next; 620 } 621 622 return (0); 623 } 624 625 /* 626 * This function opens the disk generically (any slice). 627 */ 628 int 629 drive_open_disk(disk_t *diskp, char *opath, int len) 630 { 631 /* 632 * Just open the first devpath. 633 */ 634 if (diskp->aliases != NULL && diskp->aliases->devpaths != NULL) { 635 if (opath != NULL) { 636 (void) strlcpy(opath, diskp->aliases->devpaths->devpath, len); 637 } 638 return (open(diskp->aliases->devpaths->devpath, O_RDONLY|O_NDELAY)); 639 } 640 641 return (-1); 642 } 643 644 static descriptor_t ** 645 apply_filter(descriptor_t **drives, int filter[], int *errp) 646 { 647 int i; 648 descriptor_t **found; 649 int cnt; 650 int pos; 651 652 /* count the number of drives in the snapshot */ 653 for (cnt = 0; drives[cnt]; cnt++); 654 655 found = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *)); 656 if (found == NULL) { 657 *errp = ENOMEM; 658 cache_free_descriptors(drives); 659 return (NULL); 660 } 661 662 pos = 0; 663 for (i = 0; drives[i]; i++) { 664 int j; 665 int match; 666 667 /* Make sure the drive type is set */ 668 get_drive_type(drives[i]->p.disk, -1); 669 670 match = 0; 671 for (j = 0; filter[j] != DM_FILTER_END; j++) { 672 if (drives[i]->p.disk->drv_type == filter[j]) { 673 found[pos++] = drives[i]; 674 match = 1; 675 break; 676 } 677 } 678 679 if (!match) { 680 cache_free_descriptor(drives[i]); 681 } 682 } 683 found[pos] = NULL; 684 free(drives); 685 686 *errp = 0; 687 return (found); 688 } 689 690 static int 691 conv_drive_type(uint_t drive_type) 692 { 693 switch (drive_type) { 694 case DK_UNKNOWN: 695 return (DM_DT_UNKNOWN); 696 case DK_MO_ERASABLE: 697 return (DM_DT_MO_ERASABLE); 698 case DK_MO_WRITEONCE: 699 return (DM_DT_MO_WRITEONCE); 700 case DK_AS_MO: 701 return (DM_DT_AS_MO); 702 case DK_CDROM: 703 return (DM_DT_CDROM); 704 case DK_CDR: 705 return (DM_DT_CDR); 706 case DK_CDRW: 707 return (DM_DT_CDRW); 708 case DK_DVDROM: 709 return (DM_DT_DVDROM); 710 case DK_DVDR: 711 return (DM_DT_DVDR); 712 case DK_DVDRAM: 713 return (DM_DT_DVDRAM); 714 case DK_FIXED_DISK: 715 return (DM_DT_FIXED); 716 case DK_FLOPPY: 717 return (DM_DT_FLOPPY); 718 case DK_ZIP: 719 return (DM_DT_ZIP); 720 case DK_JAZ: 721 return (DM_DT_JAZ); 722 default: 723 return (DM_DT_UNKNOWN); 724 } 725 } 726 727 static descriptor_t ** 728 get_assoc_alias(disk_t *diskp, int *errp) 729 { 730 alias_t *aliasp; 731 uint_t cnt; 732 descriptor_t **out_array; 733 int pos; 734 735 *errp = 0; 736 737 aliasp = diskp->aliases; 738 cnt = 0; 739 740 while (aliasp != NULL) { 741 if (aliasp->alias != NULL) { 742 cnt++; 743 } 744 aliasp = aliasp->next; 745 } 746 747 /* set up the new array */ 748 out_array = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t)); 749 if (out_array == NULL) { 750 *errp = ENOMEM; 751 return (NULL); 752 } 753 754 aliasp = diskp->aliases; 755 pos = 0; 756 while (aliasp != NULL) { 757 if (aliasp->alias != NULL) { 758 out_array[pos++] = cache_get_desc(DM_ALIAS, diskp, 759 aliasp->alias, NULL, errp); 760 if (*errp != 0) { 761 cache_free_descriptors(out_array); 762 return (NULL); 763 } 764 } 765 766 aliasp = aliasp->next; 767 } 768 769 out_array[pos] = NULL; 770 771 return (out_array); 772 } 773 774 static descriptor_t ** 775 get_assoc_controllers(descriptor_t *dp, int *errp) 776 { 777 disk_t *diskp; 778 int cnt; 779 descriptor_t **controllers; 780 int i; 781 782 diskp = dp->p.disk; 783 784 /* Count how many we have. */ 785 for (cnt = 0; diskp->controllers[cnt]; cnt++); 786 787 /* make the snapshot */ 788 controllers = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *)); 789 if (controllers == NULL) { 790 *errp = ENOMEM; 791 return (NULL); 792 } 793 794 for (i = 0; diskp->controllers[i]; i++) { 795 controllers[i] = cache_get_desc(DM_CONTROLLER, 796 diskp->controllers[i], NULL, NULL, errp); 797 if (*errp != 0) { 798 cache_free_descriptors(controllers); 799 return (NULL); 800 } 801 } 802 803 controllers[i] = NULL; 804 805 *errp = 0; 806 return (controllers); 807 } 808 809 static descriptor_t ** 810 get_assoc_paths(descriptor_t *dp, int *errp) 811 { 812 path_t **pp; 813 int cnt; 814 descriptor_t **paths; 815 int i; 816 817 pp = dp->p.disk->paths; 818 819 /* Count how many we have. */ 820 cnt = 0; 821 if (pp != NULL) { 822 for (; pp[cnt]; cnt++); 823 } 824 825 /* make the snapshot */ 826 paths = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *)); 827 if (paths == NULL) { 828 *errp = ENOMEM; 829 return (NULL); 830 } 831 832 /* 833 * We fill in the name field of the descriptor with the device_id 834 * when we deal with path descriptors originating from a drive. 835 * In that way we can use the device id within the path code to 836 * lookup the path state for this drive. 837 */ 838 for (i = 0; i < cnt; i++) { 839 paths[i] = cache_get_desc(DM_PATH, pp[i], dp->p.disk->device_id, 840 NULL, errp); 841 if (*errp != 0) { 842 cache_free_descriptors(paths); 843 return (NULL); 844 } 845 } 846 847 paths[i] = NULL; 848 849 *errp = 0; 850 return (paths); 851 } 852 853 static int 854 get_attrs(disk_t *diskp, int fd, char *opath, nvlist_t *attrs) 855 { 856 if (diskp->removable) { 857 struct dk_minfo minfo; 858 859 if (nvlist_add_boolean(attrs, DM_REMOVABLE) != 0) { 860 return (ENOMEM); 861 } 862 863 /* Make sure media is inserted and spun up. */ 864 if (fd >= 0 && media_read_info(fd, &minfo)) { 865 if (nvlist_add_boolean(attrs, DM_LOADED) != 0) { 866 return (ENOMEM); 867 } 868 } 869 870 /* can't tell diff between dead & no media on removable drives */ 871 if (nvlist_add_uint32(attrs, DM_STATUS, DM_DISK_UP) != 0) { 872 return (ENOMEM); 873 } 874 875 get_drive_type(diskp, fd); 876 877 } else { 878 struct dk_minfo minfo; 879 880 /* check if the fixed drive is up or not */ 881 if (fd >= 0 && media_read_info(fd, &minfo)) { 882 if (nvlist_add_uint32(attrs, DM_STATUS, DM_DISK_UP) != 0) { 883 return (ENOMEM); 884 } 885 } else { 886 if (nvlist_add_uint32(attrs, DM_STATUS, DM_DISK_DOWN) != 0) { 887 return (ENOMEM); 888 } 889 } 890 891 get_drive_type(diskp, fd); 892 } 893 894 if (nvlist_add_uint32(attrs, DM_DRVTYPE, diskp->drv_type) != 0) { 895 return (ENOMEM); 896 } 897 898 if (diskp->product_id != NULL) { 899 if (nvlist_add_string(attrs, DM_PRODUCT_ID, diskp->product_id) 900 != 0) { 901 return (ENOMEM); 902 } 903 } 904 if (diskp->vendor_id != NULL) { 905 if (nvlist_add_string(attrs, DM_VENDOR_ID, diskp->vendor_id) != 0) { 906 return (ENOMEM); 907 } 908 } 909 910 if (diskp->sync_speed != -1) { 911 if (nvlist_add_uint32(attrs, DM_SYNC_SPEED, diskp->sync_speed) 912 != 0) { 913 return (ENOMEM); 914 } 915 } 916 917 if (diskp->wide == 1) { 918 if (nvlist_add_boolean(attrs, DM_WIDE) != 0) { 919 return (ENOMEM); 920 } 921 } 922 923 if (diskp->rpm == 0) { 924 diskp->rpm = get_rpm(diskp, fd); 925 } 926 927 if (diskp->rpm > 0) { 928 if (nvlist_add_uint32(attrs, DM_RPM, diskp->rpm) != 0) { 929 return (ENOMEM); 930 } 931 } 932 933 if (diskp->aliases != NULL && diskp->aliases->cluster) { 934 if (nvlist_add_boolean(attrs, DM_CLUSTERED) != 0) { 935 return (ENOMEM); 936 } 937 } 938 939 if (strlen(opath) > 0) { 940 if (nvlist_add_string(attrs, DM_OPATH, opath) != 0) { 941 return (ENOMEM); 942 } 943 } 944 945 if (diskp->solid_state < 0) { 946 diskp->solid_state = get_solidstate(diskp, fd); 947 } 948 949 if (diskp->solid_state > 0) { 950 if (nvlist_add_boolean(attrs, DM_SOLIDSTATE) != 0) { 951 return (ENOMEM); 952 } 953 } 954 955 return (0); 956 } 957 958 static int 959 get_disk_kstats(kstat_ctl_t *kc, char *diskname, char *classname, 960 nvlist_t *stats) 961 { 962 kstat_t *ksp; 963 size_t class_len; 964 int err = 0; 965 966 class_len = strlen(classname); 967 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { 968 if (strncmp(ksp->ks_class, classname, class_len) == 0) { 969 char kstat_name[KSTAT_STRLEN]; 970 char *dname = kstat_name; 971 char *ename = ksp->ks_name; 972 973 /* names are format: "sd0,err" - copy chars up to comma */ 974 while (*ename && *ename != ',') { 975 *dname++ = *ename++; 976 } 977 *dname = NULL; 978 979 if (libdiskmgt_str_eq(diskname, kstat_name)) { 980 (void) kstat_read(kc, ksp, NULL); 981 err = get_kstat_vals(ksp, stats); 982 break; 983 } 984 } 985 } 986 987 return (err); 988 } 989 990 /* 991 * Getting the drive type depends on if the dev tree walk indicated that the 992 * drive was a CD-ROM or not. The kernal lumps all of the removable multi-media 993 * drives (e.g. CD, DVD, MO, etc.) together as CD-ROMS, so we need to use 994 * a uscsi cmd to check the drive type. 995 */ 996 static void 997 get_drive_type(disk_t *dp, int fd) 998 { 999 if (dp->drv_type == DM_DT_UNKNOWN) { 1000 int opened_here = 0; 1001 1002 /* We may have already opened the device. */ 1003 if (fd < 0) { 1004 fd = drive_open_disk(dp, NULL, 0); 1005 opened_here = 1; 1006 } 1007 1008 if (fd >= 0) { 1009 if (dp->cd_rom) { 1010 /* use uscsi to determine drive type */ 1011 dp->drv_type = get_cdrom_drvtype(fd); 1012 1013 /* if uscsi fails, just call it a cd-rom */ 1014 if (dp->drv_type == DM_DT_UNKNOWN) { 1015 dp->drv_type = DM_DT_CDROM; 1016 } 1017 1018 } else { 1019 struct dk_minfo minfo; 1020 1021 if (media_read_info(fd, &minfo)) { 1022 dp->drv_type = conv_drive_type(minfo.dki_media_type); 1023 } 1024 } 1025 1026 if (opened_here) { 1027 (void) close(fd); 1028 } 1029 1030 } else { 1031 /* couldn't open */ 1032 if (dp->cd_rom) { 1033 dp->drv_type = DM_DT_CDROM; 1034 } 1035 } 1036 } 1037 } 1038 1039 static char * 1040 get_err_attr_name(char *kstat_name) 1041 { 1042 int i; 1043 1044 for (i = 0; kstat_err_names[i] != NULL; i++) { 1045 if (libdiskmgt_str_eq(kstat_name, kstat_err_names[i])) { 1046 return (err_attr_names[i]); 1047 } 1048 } 1049 1050 return (NULL); 1051 } 1052 1053 static int 1054 get_err_kstats(kstat_ctl_t *kc, char *diskname, nvlist_t *stats) 1055 { 1056 return (get_disk_kstats(kc, diskname, KSTAT_CLASS_ERROR, stats)); 1057 } 1058 1059 static int 1060 get_io_kstats(kstat_ctl_t *kc, char *diskname, nvlist_t *stats) 1061 { 1062 return (get_disk_kstats(kc, diskname, KSTAT_CLASS_DISK, stats)); 1063 } 1064 1065 static int 1066 get_kstat_vals(kstat_t *ksp, nvlist_t *stats) 1067 { 1068 if (ksp->ks_type == KSTAT_TYPE_IO) { 1069 kstat_io_t *kiop; 1070 1071 kiop = KSTAT_IO_PTR(ksp); 1072 1073 /* see sys/kstat.h kstat_io_t struct for more fields */ 1074 1075 if (update_stat64(stats, DM_NBYTESREAD, kiop->nread) != 0) { 1076 return (ENOMEM); 1077 } 1078 if (update_stat64(stats, DM_NBYTESWRITTEN, kiop->nwritten) != 0) { 1079 return (ENOMEM); 1080 } 1081 if (update_stat64(stats, DM_NREADOPS, kiop->reads) != 0) { 1082 return (ENOMEM); 1083 } 1084 if (update_stat64(stats, DM_NWRITEOPS, kiop->writes) != 0) { 1085 return (ENOMEM); 1086 } 1087 1088 } else if (ksp->ks_type == KSTAT_TYPE_NAMED) { 1089 kstat_named_t *knp; 1090 int i; 1091 1092 knp = KSTAT_NAMED_PTR(ksp); 1093 for (i = 0; i < ksp->ks_ndata; i++) { 1094 char *attr_name; 1095 1096 if (knp[i].name[0] == 0) 1097 continue; 1098 1099 if ((attr_name = get_err_attr_name(knp[i].name)) == NULL) { 1100 continue; 1101 1102 } 1103 1104 switch (knp[i].data_type) { 1105 case KSTAT_DATA_UINT32: 1106 if (update_stat32(stats, attr_name, knp[i].value.ui32) 1107 != 0) { 1108 return (ENOMEM); 1109 } 1110 break; 1111 1112 default: 1113 /* Right now all of the error types are uint32 */ 1114 break; 1115 } 1116 } 1117 } 1118 return (0); 1119 } 1120 1121 static int 1122 update_stat32(nvlist_t *stats, char *attr, uint32_t value) 1123 { 1124 int32_t currval; 1125 1126 if (nvlist_lookup_int32(stats, attr, &currval) == 0) { 1127 value += currval; 1128 } 1129 1130 return (nvlist_add_uint32(stats, attr, value)); 1131 } 1132 1133 /* 1134 * There can be more than one kstat value when we have multi-path drives 1135 * that are not under mpxio (since there is more than one kstat name for 1136 * the drive in this case). So, we may have merge all of the kstat values 1137 * to give an accurate set of stats for the drive. 1138 */ 1139 static int 1140 update_stat64(nvlist_t *stats, char *attr, uint64_t value) 1141 { 1142 int64_t currval; 1143 1144 if (nvlist_lookup_int64(stats, attr, &currval) == 0) { 1145 value += currval; 1146 } 1147 return (nvlist_add_uint64(stats, attr, value)); 1148 } 1149 1150 /* 1151 * uscsi function to get the rpm of the drive 1152 */ 1153 static int 1154 get_rpm(disk_t *dp, int fd) 1155 { 1156 int opened_here = 0; 1157 int rpm = -1; 1158 1159 /* We may have already opened the device. */ 1160 if (fd < 0) { 1161 fd = drive_open_disk(dp, NULL, 0); 1162 opened_here = 1; 1163 } 1164 1165 if (fd >= 0) { 1166 int status; 1167 struct mode_geometry *page4; 1168 struct scsi_ms_header header; 1169 union { 1170 struct mode_geometry page4; 1171 char rawbuf[MAX_MODE_SENSE_SIZE]; 1172 } u_page4; 1173 1174 page4 = &u_page4.page4; 1175 (void) memset(&u_page4, 0, sizeof (u_page4)); 1176 1177 status = uscsi_mode_sense(fd, DAD_MODE_GEOMETRY, 1178 MODE_SENSE_PC_DEFAULT, (caddr_t)page4, MAX_MODE_SENSE_SIZE, 1179 &header); 1180 1181 if (status) { 1182 status = uscsi_mode_sense(fd, DAD_MODE_GEOMETRY, 1183 MODE_SENSE_PC_SAVED, (caddr_t)page4, MAX_MODE_SENSE_SIZE, 1184 &header); 1185 } 1186 1187 if (status) { 1188 status = uscsi_mode_sense(fd, DAD_MODE_GEOMETRY, 1189 MODE_SENSE_PC_CURRENT, (caddr_t)page4, MAX_MODE_SENSE_SIZE, 1190 &header); 1191 } 1192 1193 if (!status) { 1194 #ifdef _LITTLE_ENDIAN 1195 page4->rpm = ntohs(page4->rpm); 1196 #endif /* _LITTLE_ENDIAN */ 1197 1198 rpm = page4->rpm; 1199 } 1200 1201 if (opened_here) { 1202 (void) close(fd); 1203 } 1204 } 1205 1206 return (rpm); 1207 } 1208 1209 static int 1210 get_solidstate(disk_t *dp, int fd) 1211 { 1212 int opened_here = 0; 1213 int solid_state = -1; 1214 1215 /* We may have already opened the device. */ 1216 if (fd < 0) { 1217 fd = drive_open_disk(dp, NULL, 0); 1218 opened_here = 1; 1219 } 1220 1221 if (fd >= 0) { 1222 if (ioctl(fd, DKIOCSOLIDSTATE, &solid_state) < 0) { 1223 solid_state = -1; 1224 } 1225 } 1226 1227 if (opened_here) { 1228 (void) close(fd); 1229 } 1230 1231 return (solid_state); 1232 } 1233 1234 /* 1235 * ******** the rest of this is uscsi stuff for the drv type ******** 1236 */ 1237 1238 /* 1239 * We try a get_configuration uscsi cmd. If that fails, try a 1240 * atapi_capabilities cmd. If both fail then this is an older CD-ROM. 1241 */ 1242 static int 1243 get_cdrom_drvtype(int fd) 1244 { 1245 union scsi_cdb cdb; 1246 struct uscsi_cmd cmd; 1247 uchar_t buff[SCSIBUFLEN]; 1248 1249 fill_general_page_cdb_g1(&cdb, SCMD_GET_CONFIGURATION, 0, 1250 b0(sizeof (buff)), b1(sizeof (buff))); 1251 fill_command_g1(&cmd, &cdb, (caddr_t)buff, sizeof (buff)); 1252 1253 if (ioctl(fd, USCSICMD, &cmd) >= 0) { 1254 struct get_configuration *config; 1255 struct conf_feature *feature; 1256 int flen; 1257 1258 /* The first profile is the preferred one for the drive. */ 1259 config = (struct get_configuration *)buff; 1260 feature = &config->feature; 1261 flen = feature->len / sizeof (struct profile_list); 1262 if (flen > 0) { 1263 int prof_num; 1264 1265 prof_num = (int)convnum(feature->features.plist[0].profile, 2); 1266 1267 if (dm_debug > 1) { 1268 (void) fprintf(stderr, "INFO: uscsi get_configuration %d\n", 1269 prof_num); 1270 } 1271 1272 switch (prof_num) { 1273 case PROF_MAGNETO_OPTICAL: 1274 return (DM_DT_MO_ERASABLE); 1275 case PROF_OPTICAL_WO: 1276 return (DM_DT_MO_WRITEONCE); 1277 case PROF_OPTICAL_ASMO: 1278 return (DM_DT_AS_MO); 1279 case PROF_CDROM: 1280 return (DM_DT_CDROM); 1281 case PROF_CDR: 1282 return (DM_DT_CDR); 1283 case PROF_CDRW: 1284 return (DM_DT_CDRW); 1285 case PROF_DVDROM: 1286 return (DM_DT_DVDROM); 1287 case PROF_DVDRAM: 1288 return (DM_DT_DVDRAM); 1289 case PROF_DVDRW_REST: 1290 return (DM_DT_DVDRW); 1291 case PROF_DVDRW_SEQ: 1292 return (DM_DT_DVDRW); 1293 case PROF_DVDRW: 1294 return (DM_DT_DVDRW); 1295 case PROF_DDCD_ROM: 1296 return (DM_DT_DDCDROM); 1297 case PROF_DDCD_R: 1298 return (DM_DT_DDCDR); 1299 case PROF_DDCD_RW: 1300 return (DM_DT_DDCDRW); 1301 } 1302 } 1303 } 1304 1305 /* see if the atapi capabilities give anything */ 1306 return (check_atapi(fd)); 1307 } 1308 1309 static int 1310 check_atapi(int fd) 1311 { 1312 union scsi_cdb cdb; 1313 struct uscsi_cmd cmd; 1314 uchar_t buff[SCSIBUFLEN]; 1315 1316 fill_mode_page_cdb(&cdb, ATAPI_CAPABILITIES); 1317 fill_command_g1(&cmd, &cdb, (caddr_t)buff, sizeof (buff)); 1318 1319 if (ioctl(fd, USCSICMD, &cmd) >= 0) { 1320 int bdesclen; 1321 struct capabilities *cap; 1322 struct mode_header_g2 *mode; 1323 1324 mode = (struct mode_header_g2 *)buff; 1325 1326 bdesclen = (int)convnum(mode->desclen, 2); 1327 cap = (struct capabilities *) 1328 &buff[sizeof (struct mode_header_g2) + bdesclen]; 1329 1330 if (dm_debug > 1) { 1331 (void) fprintf(stderr, "INFO: uscsi atapi capabilities\n"); 1332 } 1333 1334 /* These are in order of how we want to report the drv type. */ 1335 if (cap->dvdram_write) { 1336 return (DM_DT_DVDRAM); 1337 } 1338 if (cap->dvdr_write) { 1339 return (DM_DT_DVDR); 1340 } 1341 if (cap->dvdrom_read) { 1342 return (DM_DT_DVDROM); 1343 } 1344 if (cap->cdrw_write) { 1345 return (DM_DT_CDRW); 1346 } 1347 if (cap->cdr_write) { 1348 return (DM_DT_CDR); 1349 } 1350 if (cap->cdr_read) { 1351 return (DM_DT_CDROM); 1352 } 1353 } 1354 1355 /* everything failed, so this is an older CD-ROM */ 1356 if (dm_debug > 1) { 1357 (void) fprintf(stderr, "INFO: uscsi failed\n"); 1358 } 1359 1360 return (DM_DT_CDROM); 1361 } 1362 1363 static uint64_t 1364 convnum(uchar_t *nptr, int len) 1365 { 1366 uint64_t value; 1367 1368 for (value = 0; len > 0; len--, nptr++) 1369 value = (value << 8) | *nptr; 1370 return (value); 1371 } 1372 1373 static void 1374 fill_command_g1(struct uscsi_cmd *cmd, union scsi_cdb *cdb, 1375 caddr_t buff, int blen) 1376 { 1377 bzero((caddr_t)cmd, sizeof (struct uscsi_cmd)); 1378 bzero(buff, blen); 1379 1380 cmd->uscsi_cdb = (caddr_t)cdb; 1381 cmd->uscsi_cdblen = CDB_GROUP1; 1382 1383 cmd->uscsi_bufaddr = buff; 1384 cmd->uscsi_buflen = blen; 1385 1386 cmd->uscsi_flags = USCSI_DIAGNOSE|USCSI_ISOLATE|USCSI_READ; 1387 } 1388 1389 static void 1390 fill_general_page_cdb_g1(union scsi_cdb *cdb, int command, int lun, 1391 uchar_t c0, uchar_t c1) 1392 { 1393 bzero((caddr_t)cdb, sizeof (union scsi_cdb)); 1394 cdb->scc_cmd = command; 1395 cdb->scc_lun = lun; 1396 cdb->g1_count0 = c0; /* max length for page */ 1397 cdb->g1_count1 = c1; /* max length for page */ 1398 } 1399 1400 static void 1401 fill_mode_page_cdb(union scsi_cdb *cdb, int page) 1402 { 1403 /* group 1 mode page */ 1404 bzero((caddr_t)cdb, sizeof (union scsi_cdb)); 1405 cdb->scc_cmd = SCMD_MODE_SENSE_G1; 1406 cdb->g1_count0 = 0xff; /* max length for mode page */ 1407 cdb->g1_count1 = 0xff; /* max length for mode page */ 1408 cdb->g1_addr3 = page; 1409 } 1410 1411 static int 1412 uscsi_mode_sense(int fd, int page_code, int page_control, caddr_t page_data, 1413 int page_size, struct scsi_ms_header *header) 1414 { 1415 caddr_t mode_sense_buf; 1416 struct mode_header *hdr; 1417 struct mode_page *pg; 1418 int nbytes; 1419 struct uscsi_cmd ucmd; 1420 union scsi_cdb cdb; 1421 int status; 1422 int maximum; 1423 char rqbuf[255]; 1424 1425 /* 1426 * Allocate a buffer for the mode sense headers 1427 * and mode sense data itself. 1428 */ 1429 nbytes = sizeof (struct block_descriptor) + 1430 sizeof (struct mode_header) + page_size; 1431 nbytes = page_size; 1432 if ((mode_sense_buf = malloc((uint_t)nbytes)) == NULL) { 1433 return (-1); 1434 } 1435 1436 /* 1437 * Build and execute the uscsi ioctl 1438 */ 1439 (void) memset(mode_sense_buf, 0, nbytes); 1440 (void) memset((char *)&ucmd, 0, sizeof (ucmd)); 1441 (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); 1442 1443 cdb.scc_cmd = SCMD_MODE_SENSE; 1444 FORMG0COUNT(&cdb, (uchar_t)nbytes); 1445 cdb.cdb_opaque[2] = page_control | page_code; 1446 ucmd.uscsi_cdb = (caddr_t)&cdb; 1447 ucmd.uscsi_cdblen = CDB_GROUP0; 1448 ucmd.uscsi_bufaddr = mode_sense_buf; 1449 ucmd.uscsi_buflen = nbytes; 1450 1451 ucmd.uscsi_flags |= USCSI_SILENT; 1452 ucmd.uscsi_flags |= USCSI_READ; 1453 ucmd.uscsi_timeout = 30; 1454 ucmd.uscsi_flags |= USCSI_RQENABLE; 1455 if (ucmd.uscsi_rqbuf == NULL) { 1456 ucmd.uscsi_rqbuf = rqbuf; 1457 ucmd.uscsi_rqlen = sizeof (rqbuf); 1458 ucmd.uscsi_rqresid = sizeof (rqbuf); 1459 } 1460 ucmd.uscsi_rqstatus = IMPOSSIBLE_SCSI_STATUS; 1461 1462 status = ioctl(fd, USCSICMD, &ucmd); 1463 1464 if (status || ucmd.uscsi_status != 0) { 1465 free(mode_sense_buf); 1466 return (-1); 1467 } 1468 1469 /* 1470 * Verify that the returned data looks reasonabled, 1471 * find the actual page data, and copy it into the 1472 * user's buffer. Copy the mode_header and block_descriptor 1473 * into the header structure, which can then be used to 1474 * return the same data to the drive when issuing a mode select. 1475 */ 1476 hdr = (struct mode_header *)mode_sense_buf; 1477 (void) memset((caddr_t)header, 0, sizeof (struct scsi_ms_header)); 1478 if (hdr->bdesc_length != sizeof (struct block_descriptor) && 1479 hdr->bdesc_length != 0) { 1480 free(mode_sense_buf); 1481 return (-1); 1482 } 1483 (void) memcpy((caddr_t)header, mode_sense_buf, 1484 (int) (sizeof (struct mode_header) + hdr->bdesc_length)); 1485 pg = (struct mode_page *)((ulong_t)mode_sense_buf + 1486 sizeof (struct mode_header) + hdr->bdesc_length); 1487 if (pg->code != page_code) { 1488 free(mode_sense_buf); 1489 return (-1); 1490 } 1491 1492 /* 1493 * Accept up to "page_size" bytes of mode sense data. 1494 * This allows us to accept both CCS and SCSI-2 1495 * structures, as long as we request the greater 1496 * of the two. 1497 */ 1498 maximum = page_size - sizeof (struct mode_page) - hdr->bdesc_length; 1499 if (((int)pg->length) > maximum) { 1500 free(mode_sense_buf); 1501 return (-1); 1502 } 1503 1504 (void) memcpy(page_data, (caddr_t)pg, MODESENSE_PAGE_LEN(pg)); 1505 1506 free(mode_sense_buf); 1507 return (0); 1508 } 1509