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