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