1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2017 Joyent, Inc.
14 */
15
16 /*
17 * Collection of routines specific to SATA devices and attempting to make them
18 * work.
19 */
20
21 #include <sys/scsi/adapters/smrt/smrt.h>
22
23 /*
24 * This is a buffer size that should easily cover all of the data that we need
25 * to properly determine the buffer allocation.
26 */
27 #define SMRT_SATA_INQ83_LEN 256
28
29 /*
30 * We need to try and determine if a SATA WWN exists on the device. SAT-2
31 * defines that the response to the inquiry page 0x83.
32 */
33 int
smrt_sata_determine_wwn(smrt_t * smrt,PhysDevAddr_t * addr,uint64_t * wwnp,uint16_t timeout)34 smrt_sata_determine_wwn(smrt_t *smrt, PhysDevAddr_t *addr, uint64_t *wwnp,
35 uint16_t timeout)
36 {
37 smrt_command_t *smcm;
38 int r;
39 uint8_t *inq;
40 uint64_t wwn;
41 size_t resid;
42
43 VERIFY3P(wwnp, !=, NULL);
44
45 if ((smcm = smrt_command_alloc(smrt, SMRT_CMDTYPE_INTERNAL,
46 KM_NOSLEEP)) == NULL || smrt_command_attach_internal(smrt, smcm,
47 SMRT_SATA_INQ83_LEN, KM_NOSLEEP) != 0) {
48 if (smcm != NULL) {
49 smrt_command_free(smcm);
50 }
51 return (ENOMEM);
52 }
53
54 smcm->smcm_va_cmd->Header.LUN.PhysDev = *addr;
55 smcm->smcm_va_cmd->Request.CDBLen = CDB_GROUP0;
56 smcm->smcm_va_cmd->Request.Type.Type = CISS_TYPE_CMD;
57 smcm->smcm_va_cmd->Request.Type.Attribute = CISS_ATTR_SIMPLE;
58 smcm->smcm_va_cmd->Request.Type.Direction = CISS_XFER_READ;
59 smcm->smcm_va_cmd->Request.Timeout = LE_16(timeout);
60
61 smcm->smcm_va_cmd->Request.CDB[0] = SCMD_INQUIRY;
62 smcm->smcm_va_cmd->Request.CDB[1] = 1;
63 smcm->smcm_va_cmd->Request.CDB[2] = 0x83;
64 smcm->smcm_va_cmd->Request.CDB[3] = (SMRT_SATA_INQ83_LEN & 0xff00) >> 8;
65 smcm->smcm_va_cmd->Request.CDB[4] = SMRT_SATA_INQ83_LEN & 0x00ff;
66 smcm->smcm_va_cmd->Request.CDB[5] = 0;
67
68 mutex_enter(&smrt->smrt_mutex);
69
70 /*
71 * Send the command to the device.
72 */
73 smcm->smcm_status |= SMRT_CMD_STATUS_POLLED;
74 if ((r = smrt_submit(smrt, smcm)) != 0) {
75 mutex_exit(&smrt->smrt_mutex);
76 smrt_command_free(smcm);
77 return (r);
78 }
79
80 if ((r = smrt_poll_for(smrt, smcm)) != 0) {
81 VERIFY3S(r, ==, ETIMEDOUT);
82 VERIFY0(smcm->smcm_status & SMRT_CMD_STATUS_POLL_COMPLETE);
83
84 /*
85 * The command timed out; abandon it now. Remove the POLLED
86 * flag so that the periodic routine will send an abort to
87 * clean it up next time around.
88 */
89 smcm->smcm_status |= SMRT_CMD_STATUS_ABANDONED;
90 smcm->smcm_status &= ~SMRT_CMD_STATUS_POLLED;
91 mutex_exit(&smrt->smrt_mutex);
92 return (r);
93 }
94
95 if (smcm->smcm_status & SMRT_CMD_STATUS_RESET_SENT) {
96 /*
97 * The controller was reset while we were trying to discover
98 * logical volumes. Report failure.
99 */
100 mutex_exit(&smrt->smrt_mutex);
101 smrt_command_free(smcm);
102 return (EIO);
103 }
104
105 if (smcm->smcm_status & SMRT_CMD_STATUS_ERROR) {
106 ErrorInfo_t *ei = smcm->smcm_va_err;
107
108 if (ei->CommandStatus != CISS_CMD_DATA_UNDERRUN) {
109 dev_err(smrt->smrt_dip, CE_WARN, "physical target "
110 "SATA WWN error: status 0x%x", ei->CommandStatus);
111 mutex_exit(&smrt->smrt_mutex);
112 smrt_command_free(smcm);
113 return (EIO);
114 }
115 resid = ei->ResidualCnt;
116 } else {
117 resid = 0;
118 }
119
120 mutex_exit(&smrt->smrt_mutex);
121
122 /*
123 * We must have at least 12 bytes. The first four bytes are the header,
124 * the next four are for the LUN header, and the last 8 are for the
125 * actual WWN, which according to SAT-2 will always be first.
126 */
127 if (SMRT_SATA_INQ83_LEN - resid < 16) {
128 smrt_command_free(smcm);
129 return (EINVAL);
130 }
131 inq = smcm->smcm_internal->smcmi_va;
132
133 /*
134 * Sanity check we have the right page.
135 */
136 if (inq[1] != 0x83) {
137 smrt_command_free(smcm);
138 return (EINVAL);
139 }
140
141 /*
142 * Check to see if we have a proper Network Address Authority (NAA)
143 * based world wide number for this LUN. It is possible that firmware
144 * interposes on this and constructs a fake world wide number (WWN). If
145 * this is the case, we don't want to actually use it. We need to
146 * verify that the WWN declares the correct naming authority and is of
147 * the proper length.
148 */
149 if ((inq[5] & 0x30) != 0 || (inq[5] & 0x0f) != 3 || inq[7] != 8) {
150 smrt_command_free(smcm);
151 return (ENOTSUP);
152 }
153
154 bcopy(&inq[8], &wwn, sizeof (uint64_t));
155 *wwnp = BE_64(wwn);
156
157 smrt_command_free(smcm);
158
159 return (0);
160 }
161