1 /*
2 * Copyright (c) 2016-2021 Chuck Tuffli <chuck@tuffli.net>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <fcntl.h>
19 #include <string.h>
20 #include <err.h>
21 #include <errno.h>
22 #include <camlib.h>
23 #include <cam/scsi/scsi_message.h>
24
25 #include "libsmart.h"
26 #include "libsmart_priv.h"
27 #include "libsmart_dev.h"
28
29 /* Provide compatibility for FreeBSD 11.0 */
30 #if (__FreeBSD_version < 1101000)
31
32 struct scsi_log_informational_exceptions {
33 struct scsi_log_param_header hdr;
34 #define SLP_IE_GEN 0x0000
35 uint8_t ie_asc;
36 uint8_t ie_ascq;
37 uint8_t temperature;
38 };
39
40 #endif
41
42 struct fbsd_smart {
43 smart_t common;
44 struct cam_device *camdev;
45 };
46
47 static smart_protocol_e __device_get_proto(struct fbsd_smart *);
48 static bool __device_proto_tunneled(struct fbsd_smart *);
49 static int32_t __device_get_info(struct fbsd_smart *);
50
51 smart_h
device_open(smart_protocol_e protocol,char * devname)52 device_open(smart_protocol_e protocol, char *devname)
53 {
54 struct fbsd_smart *h = NULL;
55
56 h = malloc(sizeof(struct fbsd_smart));
57 if (h == NULL)
58 return NULL;
59
60 memset(h, 0, sizeof(struct fbsd_smart));
61
62 h->common.protocol = SMART_PROTO_MAX;
63 h->camdev = cam_open_device(devname, O_RDWR);
64 if (h->camdev == NULL) {
65 printf("%s: error opening %s - %s\n",
66 __func__, devname,
67 cam_errbuf);
68 free(h);
69 h = NULL;
70 } else {
71 smart_protocol_e proto = __device_get_proto(h);
72
73 if ((protocol == SMART_PROTO_AUTO) ||
74 (protocol == proto)) {
75 h->common.protocol = proto;
76 } else {
77 printf("%s: protocol mismatch %d vs %d\n",
78 __func__, protocol, proto);
79 }
80
81 if (proto == SMART_PROTO_SCSI) {
82 if (__device_proto_tunneled(h)) {
83 h->common.protocol = SMART_PROTO_ATA;
84 h->common.info.tunneled = 1;
85 }
86 }
87
88 __device_get_info(h);
89 }
90
91 return h;
92 }
93
94 void
device_close(smart_h h)95 device_close(smart_h h)
96 {
97 struct fbsd_smart *fsmart = h;
98
99 if (fsmart != NULL) {
100 if (fsmart->camdev != NULL) {
101 cam_close_device(fsmart->camdev);
102 }
103
104 free(fsmart);
105 }
106 }
107
108 static const uint8_t smart_read_data[] = {
109 0xb0, 0xd0, 0x00, 0x4f, 0xc2, 0x00,
110 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
111 };
112
113 static const uint8_t smart_return_status[] = {
114 0xb0, 0xda, 0x00, 0x4f, 0xc2, 0x00,
115 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
116 };
117
118 static int32_t
__device_read_ata(smart_h h,uint32_t page,void * buf,size_t bsize,union ccb * ccb)119 __device_read_ata(smart_h h, uint32_t page, void *buf, size_t bsize, union ccb *ccb)
120 {
121 struct fbsd_smart *fsmart = h;
122 const uint8_t *smart_fis;
123 uint32_t smart_fis_size = 0;
124 uint32_t flags = 0;
125 uint16_t sector_count = 0;
126 uint8_t protocol = 0;
127
128 switch (page) {
129 case PAGE_ID_ATA_SMART_READ_DATA: /* Support SMART READ DATA */
130 smart_fis = smart_read_data;
131 smart_fis_size = sizeof(smart_read_data);
132 flags = CAM_DIR_IN;
133 sector_count = 1;
134 protocol = AP_PROTO_PIO_IN;
135 break;
136 case PAGE_ID_ATA_SMART_RET_STATUS: /* Support SMART RETURN STATUS */
137 smart_fis = smart_return_status;
138 smart_fis_size = sizeof(smart_return_status);
139 /* Command has no data but uses the return status */
140 flags = CAM_DIR_NONE;
141 protocol = AP_PROTO_NON_DATA;
142 bsize = 0;
143 break;
144 default:
145 return EINVAL;
146 }
147
148 if (fsmart->common.info.tunneled) {
149 struct ata_pass_16 *cdb;
150 uint8_t cdb_flags;
151
152 if (bsize > 0) {
153 cdb_flags = AP_FLAG_TDIR_FROM_DEV |
154 AP_FLAG_BYT_BLOK_BLOCKS |
155 AP_FLAG_TLEN_SECT_CNT;
156 } else {
157 cdb_flags = AP_FLAG_CHK_COND |
158 AP_FLAG_TDIR_FROM_DEV |
159 AP_FLAG_BYT_BLOK_BLOCKS;
160 }
161
162 cdb = (struct ata_pass_16 *)ccb->csio.cdb_io.cdb_bytes;
163 memset(cdb, 0, sizeof(*cdb));
164
165 scsi_ata_pass_16(&ccb->csio,
166 /*retries*/ 1,
167 /*cbfcnp*/ NULL,
168 /*flags*/ flags,
169 /*tag_action*/ MSG_SIMPLE_Q_TAG,
170 /*protocol*/ protocol,
171 /*ata_flags*/ cdb_flags,
172 /*features*/ page,
173 /*sector_count*/sector_count,
174 /*lba*/ 0,
175 /*command*/ ATA_SMART_CMD,
176 /*control*/ 0,
177 /*data_ptr*/ buf,
178 /*dxfer_len*/ bsize,
179 /*sense_len*/ SSD_FULL_SIZE,
180 /*timeout*/ 5000
181 );
182 cdb->lba_mid = 0x4f;
183 cdb->lba_high = 0xc2;
184 cdb->device = 0; /* scsi_ata_pass_16() sets this */
185 } else {
186 memcpy(&ccb->ataio.cmd.command, smart_fis, smart_fis_size);
187
188 cam_fill_ataio(&ccb->ataio,
189 /* retries */1,
190 /* cbfcnp */NULL,
191 /* flags */flags,
192 /* tag_action */0,
193 /* data_ptr */buf,
194 /* dxfer_len */bsize,
195 /* timeout */5000);
196 ccb->ataio.cmd.flags |= CAM_ATAIO_NEEDRESULT;
197 ccb->ataio.cmd.control = 0;
198 }
199
200 return 0;
201 }
202
203 static int32_t
__device_read_scsi(smart_h h,uint32_t page,void * buf,size_t bsize,union ccb * ccb)204 __device_read_scsi(__attribute__((unused)) smart_h h, uint32_t page, void *buf, size_t bsize, union ccb *ccb)
205 {
206
207 scsi_log_sense(&ccb->csio,
208 /* retries */1,
209 /* cbfcnp */NULL,
210 /* tag_action */0,
211 /* page_code */SLS_PAGE_CTRL_CUMULATIVE,
212 /* page */page,
213 /* save_pages */0,
214 /* ppc */0,
215 /* paramptr */0,
216 /* param_buf */buf,
217 /* param_len */bsize,
218 /* sense_len */0,
219 /* timeout */5000);
220
221 return 0;
222 }
223
224 static int32_t
__device_read_nvme(smart_h h,uint32_t page,void * buf,size_t bsize,union ccb * ccb)225 __device_read_nvme(__attribute__((unused)) smart_h h, uint32_t page, void *buf, size_t bsize, union ccb *ccb)
226 {
227 struct ccb_nvmeio *nvmeio = &ccb->nvmeio;
228 uint32_t numd = 0; /* number of dwords */
229
230 /*
231 * NVME CAM passthru
232 * 1200000 > version > 1101510 uses nvmeio->cmd.opc
233 * 1200059 > version > 1200038 uses nvmeio->cmd.opc
234 * 1200081 > version > 1200058 uses nvmeio->cmd.opc_fuse
235 * > 1200080 uses nvmeio->cmd.opc
236 * This code doesn't support the brief 'opc_fuse' period.
237 */
238 #if ((__FreeBSD_version > 1200038) || ((__FreeBSD_version > 1101510) && (__FreeBSD_version < 1200000)))
239 switch (page) {
240 case NVME_LOG_HEALTH_INFORMATION:
241 numd = (sizeof(struct nvme_health_information_page) / sizeof(uint32_t));
242 break;
243 default:
244 /* Unsupported log page */
245 return EINVAL;
246 }
247
248 /* Subtract 1 because NUMD is a zero based value */
249 numd--;
250
251 nvmeio->cmd.opc = NVME_OPC_GET_LOG_PAGE;
252 nvmeio->cmd.nsid = NVME_GLOBAL_NAMESPACE_TAG;
253 nvmeio->cmd.cdw10 = page | (numd << 16);
254
255 cam_fill_nvmeadmin(&ccb->nvmeio,
256 /* retries */1,
257 /* cbfcnp */NULL,
258 /* flags */CAM_DIR_IN,
259 /* data_ptr */buf,
260 /* dxfer_len */bsize,
261 /* timeout */5000);
262 #endif
263 return 0;
264 }
265
266 /*
267 * Retrieve the SMART RETURN STATUS
268 *
269 * SMART RETURN STATUS provides the reliability status of the
270 * device and can be used as a high-level indication of health.
271 */
272 static int32_t
__device_status_ata(smart_h h,union ccb * ccb)273 __device_status_ata(smart_h h, union ccb *ccb)
274 {
275 struct fbsd_smart *fsmart = h;
276 uint8_t *buf = NULL;
277 uint32_t page = 0;
278 uint8_t lba_high = 0, lba_mid = 0, device = 0, status = 0;
279
280 if (fsmart->common.info.tunneled) {
281 struct ata_res_pass16 {
282 u_int16_t reserved[5];
283 u_int8_t flags;
284 u_int8_t error;
285 u_int8_t sector_count_exp;
286 u_int8_t sector_count;
287 u_int8_t lba_low_exp;
288 u_int8_t lba_low;
289 u_int8_t lba_mid_exp;
290 u_int8_t lba_mid;
291 u_int8_t lba_high_exp;
292 u_int8_t lba_high;
293 u_int8_t device;
294 u_int8_t status;
295 } *res_pass16 = (struct ata_res_pass16 *)(uintptr_t)
296 &ccb->csio.sense_data;
297
298 buf = ccb->csio.data_ptr;
299 page = ((struct ata_pass_16 *)ccb->csio.cdb_io.cdb_bytes)->features;
300 lba_high = res_pass16->lba_high;
301 lba_mid = res_pass16->lba_mid;
302 device = res_pass16->device;
303 status = res_pass16->status;
304
305 /*
306 * Note that this generates an expected CHECK CONDITION.
307 * Mask it so the outer function doesn't print an error
308 * message.
309 */
310 ccb->ccb_h.status &= ~CAM_STATUS_MASK;
311 ccb->ccb_h.status |= CAM_REQ_CMP;
312 } else {
313 struct ccb_ataio *ataio = (struct ccb_ataio *)&ccb->ataio;
314
315 buf = ataio->data_ptr;
316 page = ataio->cmd.features;
317 lba_high = ataio->res.lba_high;
318 lba_mid = ataio->res.lba_mid;
319 device = ataio->res.device;
320 status = ataio->res.status;
321 }
322
323 switch (page) {
324 case PAGE_ID_ATA_SMART_RET_STATUS:
325 /*
326 * Typically, SMART related log pages return data, but this
327 * command is different in that the data is encoded in the
328 * result registers.
329 *
330 * Handle this in a UNIX-like way by writing a 0 (no errors)
331 * or 1 (threshold exceeded condition) to the output buffer.
332 */
333 dprintf("SMART_RET_STATUS: lba mid=%#x high=%#x device=%#x status=%#x\n",
334 lba_mid,
335 lba_high,
336 device,
337 status);
338 if ((lba_high == 0x2c) && (lba_mid == 0xf4)) {
339 buf[0] = 1;
340 } else if ((lba_high == 0xc2) && (lba_mid == 0x4f)) {
341 buf[0] = 0;
342 } else {
343 /* Ruh-roh ... */
344 buf[0] = 255;
345 }
346 break;
347 default:
348 ;
349 }
350
351 return 0;
352 }
353
354 int32_t
device_read_log(smart_h h,uint32_t page,void * buf,size_t bsize)355 device_read_log(smart_h h, uint32_t page, void *buf, size_t bsize)
356 {
357 struct fbsd_smart *fsmart = h;
358 union ccb *ccb = NULL;
359 int rc = 0;
360
361 if (fsmart == NULL)
362 return EINVAL;
363
364 dprintf("read log page %#x\n", page);
365
366 ccb = cam_getccb(fsmart->camdev);
367 if (ccb == NULL)
368 return ENOMEM;
369
370 CCB_CLEAR_ALL_EXCEPT_HDR(ccb);
371
372 switch (fsmart->common.protocol) {
373 case SMART_PROTO_ATA:
374 rc = __device_read_ata(h, page, buf, bsize, ccb);
375 break;
376 case SMART_PROTO_SCSI:
377 rc = __device_read_scsi(h, page, buf, bsize, ccb);
378 break;
379 case SMART_PROTO_NVME:
380 rc = __device_read_nvme(h, page, buf, bsize, ccb);
381 break;
382 default:
383 warnx("unsupported protocol %d", fsmart->common.protocol);
384 cam_freeccb(ccb);
385 return ENODEV;
386 }
387
388 if (rc) {
389 if (rc == EINVAL)
390 warnx("unsupported page %#x", page);
391
392 return rc;
393 }
394
395 if (((rc = cam_send_ccb(fsmart->camdev, ccb)) < 0)
396 || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
397 if (rc < 0)
398 warn("error sending command");
399 }
400
401 /*
402 * Most commands don't need any post-processing. But then there's
403 * ATA. It's why we can't have nice things :(
404 */
405 switch (fsmart->common.protocol) {
406 case SMART_PROTO_ATA:
407 __device_status_ata(h, ccb);
408 break;
409 default:
410 ;
411 }
412
413 if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
414 cam_error_print(fsmart->camdev, ccb, CAM_ESF_ALL,
415 CAM_EPF_ALL, stderr);
416 }
417
418 cam_freeccb(ccb);
419
420 return 0;
421 }
422
423 /*
424 * The SCSI / ATA Translation (SAT) requires devices to support the ATA
425 * Information VPD Page (T10/2126-D Revision 04). Use the existence of
426 * this page to identify tunneled devices.
427 */
428 static bool
__device_proto_tunneled(struct fbsd_smart * fsmart)429 __device_proto_tunneled(struct fbsd_smart *fsmart)
430 {
431 union ccb *ccb = NULL;
432 struct scsi_vpd_supported_page_list supportedp;
433 uint32_t i;
434 bool is_tunneled = false;
435
436 if (fsmart->common.protocol != SMART_PROTO_SCSI) {
437 return false;
438 }
439
440 ccb = cam_getccb(fsmart->camdev);
441 if (!ccb) {
442 warn("Allocation failure ccb=%p", ccb);
443 goto __device_proto_tunneled_out;
444 }
445
446 scsi_inquiry(&ccb->csio,
447 3, // retries
448 NULL, // callback function
449 MSG_SIMPLE_Q_TAG, // tag action
450 (uint8_t *)&supportedp,
451 sizeof(struct scsi_vpd_supported_page_list),
452 1, // EVPD
453 SVPD_SUPPORTED_PAGE_LIST, // page code
454 SSD_FULL_SIZE, // sense length
455 5000); // timeout
456
457 ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
458
459 if ((cam_send_ccb(fsmart->camdev, ccb) >= 0) &&
460 ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)) {
461 dprintf("Looking for page %#x (total = %u):\n", SVPD_ATA_INFORMATION,
462 supportedp.length);
463 for (i = 0; i < supportedp.length; i++) {
464 dprintf("\t[%u] = %#x\n", i, supportedp.list[i]);
465 if (supportedp.list[i] == SVPD_ATA_INFORMATION) {
466 is_tunneled = true;
467 break;
468 }
469 }
470 }
471
472 cam_freeccb(ccb);
473
474 __device_proto_tunneled_out:
475 return is_tunneled;
476 }
477
478 /**
479 * Retrieve the device protocol type via the transport settings
480 *
481 * @return protocol type or SMART_PROTO_MAX on error
482 */
483 static smart_protocol_e
__device_get_proto(struct fbsd_smart * fsmart)484 __device_get_proto(struct fbsd_smart *fsmart)
485 {
486 smart_protocol_e proto = SMART_PROTO_MAX;
487 union ccb *ccb;
488
489 if (!fsmart || !fsmart->camdev) {
490 warn("Bad handle %p", fsmart);
491 return proto;
492 }
493
494 ccb = cam_getccb(fsmart->camdev);
495 if (ccb != NULL) {
496 CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->cts);
497
498 ccb->ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
499 ccb->cts.type = CTS_TYPE_CURRENT_SETTINGS;
500
501 if (cam_send_ccb(fsmart->camdev, ccb) >= 0) {
502 if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
503 struct ccb_trans_settings *cts = &ccb->cts;
504
505 switch (cts->protocol) {
506 case PROTO_ATA:
507 proto = SMART_PROTO_ATA;
508 break;
509 case PROTO_SCSI:
510 proto = SMART_PROTO_SCSI;
511 break;
512 case PROTO_NVME:
513 proto = SMART_PROTO_NVME;
514 break;
515 default:
516 printf("%s: unknown protocol %d\n",
517 __func__,
518 cts->protocol);
519 }
520 }
521 }
522
523 cam_freeccb(ccb);
524 }
525
526 return proto;
527 }
528
529 static int32_t
__device_info_ata(struct fbsd_smart * fsmart,struct ccb_getdev * cgd)530 __device_info_ata(struct fbsd_smart *fsmart, struct ccb_getdev *cgd)
531 {
532 smart_info_t *sinfo = NULL;
533
534 if (!fsmart || !cgd) {
535 return -1;
536 }
537
538 sinfo = &fsmart->common.info;
539
540 sinfo->supported = cgd->ident_data.support.command1 &
541 ATA_SUPPORT_SMART;
542
543 dprintf("ATA command1 = %#x\n", cgd->ident_data.support.command1);
544
545 cam_strvis((uint8_t *)sinfo->device, cgd->ident_data.model,
546 sizeof(cgd->ident_data.model),
547 sizeof(sinfo->device));
548 cam_strvis((uint8_t *)sinfo->rev, cgd->ident_data.revision,
549 sizeof(cgd->ident_data.revision),
550 sizeof(sinfo->rev));
551 cam_strvis((uint8_t *)sinfo->serial, cgd->ident_data.serial,
552 sizeof(cgd->ident_data.serial),
553 sizeof(sinfo->serial));
554
555 return 0;
556 }
557
558 static int32_t
__device_info_scsi(struct fbsd_smart * fsmart,struct ccb_getdev * cgd)559 __device_info_scsi(struct fbsd_smart *fsmart, struct ccb_getdev *cgd)
560 {
561 smart_info_t *sinfo = NULL;
562 union ccb *ccb = NULL;
563 struct scsi_vpd_unit_serial_number *snum = NULL;
564 struct scsi_log_informational_exceptions ie = {0};
565
566 if (!fsmart || !cgd) {
567 return -1;
568 }
569
570 sinfo = &fsmart->common.info;
571
572 cam_strvis((uint8_t *)sinfo->vendor, (uint8_t *)cgd->inq_data.vendor,
573 sizeof(cgd->inq_data.vendor),
574 sizeof(sinfo->vendor));
575 cam_strvis((uint8_t *)sinfo->device, (uint8_t *)cgd->inq_data.product,
576 sizeof(cgd->inq_data.product),
577 sizeof(sinfo->device));
578 cam_strvis((uint8_t *)sinfo->rev, (uint8_t *)cgd->inq_data.revision,
579 sizeof(cgd->inq_data.revision),
580 sizeof(sinfo->rev));
581
582 ccb = cam_getccb(fsmart->camdev);
583 snum = malloc(sizeof(struct scsi_vpd_unit_serial_number));
584 if (!ccb || !snum) {
585 warn("Allocation failure ccb=%p snum=%p", ccb, snum);
586 goto __device_info_scsi_out;
587 }
588
589 /* Get the serial number */
590 CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
591
592 scsi_inquiry(&ccb->csio,
593 3, // retries
594 NULL, // callback function
595 MSG_SIMPLE_Q_TAG, // tag action
596 (uint8_t *)snum,
597 sizeof(struct scsi_vpd_unit_serial_number),
598 1, // EVPD
599 SVPD_UNIT_SERIAL_NUMBER, // page code
600 SSD_FULL_SIZE, // sense length
601 5000); // timeout
602
603 ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
604
605 if ((cam_send_ccb(fsmart->camdev, ccb) >= 0) &&
606 ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)) {
607 cam_strvis((uint8_t *)sinfo->serial, snum->serial_num,
608 snum->length,
609 sizeof(sinfo->serial));
610 sinfo->serial[sizeof(sinfo->serial) - 1] = '\0';
611 }
612
613 memset(ccb, 0, sizeof(*ccb));
614
615 scsi_log_sense(&ccb->csio,
616 /* retries */1,
617 /* cbfcnp */NULL,
618 /* tag_action */0,
619 /* page_code */SLS_PAGE_CTRL_CUMULATIVE,
620 /* page */SLS_IE_PAGE,
621 /* save_pages */0,
622 /* ppc */0,
623 /* paramptr */0,
624 /* param_buf */(uint8_t *)&ie,
625 /* param_len */sizeof(ie),
626 /* sense_len */0,
627 /* timeout */5000);
628
629 /*
630 * Note: The existance of the Informational Exceptions (IE) log page
631 * appears to be the litmus test for SMART support in SCSI
632 * devices. Confusingly, smartctl will report SMART health
633 * status as 'OK' if the device doesn't support the IE page.
634 * For now, just report the facts.
635 */
636 if ((cam_send_ccb(fsmart->camdev, ccb) >= 0) &&
637 ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)) {
638 if ((ie.hdr.param_len < 4) || ie.ie_asc || ie.ie_ascq) {
639 printf("Log Sense, Informational Exceptions failed "
640 "(length=%u asc=%#x ascq=%#x)\n",
641 ie.hdr.param_len, ie.ie_asc, ie.ie_ascq);
642 } else {
643 sinfo->supported = true;
644 }
645 }
646
647 __device_info_scsi_out:
648 free(snum);
649 if (ccb)
650 cam_freeccb(ccb);
651
652 return 0;
653 }
654
655 static int32_t
__device_info_nvme(struct fbsd_smart * fsmart,struct ccb_getdev * cgd)656 __device_info_nvme(struct fbsd_smart *fsmart, struct ccb_getdev *cgd)
657 {
658 union ccb *ccb;
659 smart_info_t *sinfo = NULL;
660 struct nvme_controller_data cd;
661
662 if (!fsmart || !cgd) {
663 return -1;
664 }
665
666 sinfo = &fsmart->common.info;
667
668 sinfo->supported = true;
669
670 ccb = cam_getccb(fsmart->camdev);
671 if (ccb != NULL) {
672 struct ccb_dev_advinfo *cdai = &ccb->cdai;
673
674 CCB_CLEAR_ALL_EXCEPT_HDR(cdai);
675
676 cdai->ccb_h.func_code = XPT_DEV_ADVINFO;
677 cdai->ccb_h.flags = CAM_DIR_IN;
678 cdai->flags = CDAI_FLAG_NONE;
679 #ifdef CDAI_TYPE_NVME_CNTRL
680 cdai->buftype = CDAI_TYPE_NVME_CNTRL;
681 #else
682 cdai->buftype = 6;
683 #endif
684 cdai->bufsiz = sizeof(struct nvme_controller_data);
685 cdai->buf = (uint8_t *)&cd;
686
687 if (cam_send_ccb(fsmart->camdev, ccb) >= 0) {
688 if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
689 cam_strvis((uint8_t *)sinfo->device, cd.mn,
690 sizeof(cd.mn),
691 sizeof(sinfo->device));
692 cam_strvis((uint8_t *)sinfo->rev, cd.fr,
693 sizeof(cd.fr),
694 sizeof(sinfo->rev));
695 cam_strvis((uint8_t *)sinfo->serial, cd.sn,
696 sizeof(cd.sn),
697 sizeof(sinfo->serial));
698 }
699 }
700
701 cam_freeccb(ccb);
702 }
703
704 return 0;
705 }
706
707 static int32_t
__device_info_tunneled_ata(struct fbsd_smart * fsmart)708 __device_info_tunneled_ata(struct fbsd_smart *fsmart)
709 {
710 struct ata_params ident_data;
711 union ccb *ccb = NULL;
712 int32_t rc = -1;
713
714 ccb = cam_getccb(fsmart->camdev);
715 if (ccb == NULL) {
716 goto __device_info_tunneled_ata_out;
717 }
718
719 memset(&ident_data, 0, sizeof(struct ata_params));
720
721 CCB_CLEAR_ALL_EXCEPT_HDR(ccb);
722
723 scsi_ata_pass_16(&ccb->csio,
724 /*retries*/ 1,
725 /*cbfcnp*/ NULL,
726 /*flags*/ CAM_DIR_IN,
727 /*tag_action*/ MSG_SIMPLE_Q_TAG,
728 /*protocol*/ AP_PROTO_PIO_IN,
729 /*ata_flags*/ AP_FLAG_TLEN_SECT_CNT |
730 AP_FLAG_BYT_BLOK_BLOCKS |
731 AP_FLAG_TDIR_FROM_DEV,
732 /*features*/ 0,
733 /*sector_count*/sizeof(struct ata_params),
734 /*lba*/ 0,
735 /*command*/ ATA_ATA_IDENTIFY,
736 /*control*/ 0,
737 /*data_ptr*/ (uint8_t *)&ident_data,
738 /*dxfer_len*/ sizeof(struct ata_params),
739 /*sense_len*/ SSD_FULL_SIZE,
740 /*timeout*/ 5000
741 );
742
743 rc = cam_send_ccb(fsmart->camdev, ccb);
744 if (rc != 0) {
745 warnx("%s: scsi_ata_pass_16() failed (programmer error?)",
746 __func__);
747 goto __device_info_tunneled_ata_out;
748 }
749
750 fsmart->common.info.supported = ident_data.support.command1 &
751 ATA_SUPPORT_SMART;
752
753 dprintf("ATA command1 = %#x\n", ident_data.support.command1);
754
755 __device_info_tunneled_ata_out:
756 if (ccb) {
757 cam_freeccb(ccb);
758 }
759
760 return rc;
761 }
762
763 /**
764 * Retrieve the device information and use to populate the info structure
765 */
766 static int32_t
__device_get_info(struct fbsd_smart * fsmart)767 __device_get_info(struct fbsd_smart *fsmart)
768 {
769 union ccb *ccb;
770 int32_t rc = -1;
771
772 if (!fsmart || !fsmart->camdev) {
773 warn("Bad handle %p", fsmart);
774 return -1;
775 }
776
777 ccb = cam_getccb(fsmart->camdev);
778 if (ccb != NULL) {
779 struct ccb_getdev *cgd = &ccb->cgd;
780
781 CCB_CLEAR_ALL_EXCEPT_HDR(cgd);
782
783 /*
784 * GDEV_TYPE doesn't support NVMe. What we do get is:
785 * - device (ata/model, scsi/product)
786 * - revision (ata, scsi)
787 * - serial (ata)
788 * - vendor (scsi)
789 * - supported (ata)
790 *
791 * Serial # for all proto via ccb_dev_advinfo (buftype CDAI_TYPE_SERIAL_NUM)
792 */
793 ccb->ccb_h.func_code = XPT_GDEV_TYPE;
794
795 if (cam_send_ccb(fsmart->camdev, ccb) >= 0) {
796 if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
797 switch (cgd->protocol) {
798 case PROTO_ATA:
799 rc = __device_info_ata(fsmart, cgd);
800 break;
801 case PROTO_SCSI:
802 rc = __device_info_scsi(fsmart, cgd);
803 if (!rc && fsmart->common.protocol == SMART_PROTO_ATA) {
804 rc = __device_info_tunneled_ata(fsmart);
805 }
806 break;
807 case PROTO_NVME:
808 rc = __device_info_nvme(fsmart, cgd);
809 break;
810 default:
811 printf("%s: unsupported protocol %d\n",
812 __func__, cgd->protocol);
813 }
814 }
815 }
816
817 cam_freeccb(ccb);
818 }
819
820 return rc;
821 }
822