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 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 *
26 * Copyright (c) 2017, Joyent, Inc.
27 */
28
29 #include <sys/types.h>
30 #include <sys/scsi/impl/uscsi.h>
31 #include <sys/scsi/generic/commands.h>
32
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 #include <string.h>
37 #include <strings.h>
38 #include <stdio.h>
39 #include <limits.h>
40
41 #include <scsi/libscsi.h>
42 #include "libscsi_impl.h"
43
44 struct uscsi_dev {
45 int fd;
46 char *dev;
47 };
48
49 static void *
uscsi_open(libscsi_hdl_t * hp,const void * target)50 uscsi_open(libscsi_hdl_t *hp, const void *target)
51 {
52 struct uscsi_dev *dp;
53 const char *target_name = (const char *)target;
54
55 if ((dp = libscsi_zalloc(hp, sizeof (struct uscsi_dev))) == NULL)
56 return (NULL);
57
58 if ((dp->dev = libscsi_strdup(hp, target_name)) == NULL) {
59 libscsi_free(hp, dp);
60 return (NULL);
61 }
62
63 if ((dp->fd = open(target_name, O_RDONLY)) < 0) {
64 (void) libscsi_error(hp, ESCSI_BADTARGET, "failed to open %s "
65 "for reading: %s", target_name, strerror(errno));
66 libscsi_free(hp, dp->dev);
67 libscsi_free(hp, dp);
68 return (NULL);
69 }
70
71 return (dp);
72 }
73
74 static void
uscsi_close(libscsi_hdl_t * hp,void * private)75 uscsi_close(libscsi_hdl_t *hp, void *private)
76 {
77 struct uscsi_dev *dp = (struct uscsi_dev *)private;
78
79 if (dp == NULL)
80 return;
81
82 if (dp->fd > 0)
83 (void) close(dp->fd);
84
85 libscsi_free(hp, dp->dev);
86 libscsi_free(hp, dp);
87 }
88
89 static int
xlate_flags(libscsi_hdl_t * hp,uint_t flags,int * uf)90 xlate_flags(libscsi_hdl_t *hp, uint_t flags, int *uf)
91 {
92 uint_t f;
93 int i;
94
95 f = 0;
96
97 for (i = 0; i < sizeof (flags) * 8; i++) {
98 switch (flags & (1 << i)) {
99 case 0:
100 continue;
101 case LIBSCSI_AF_READ:
102 f |= USCSI_READ;
103 break;
104 case LIBSCSI_AF_WRITE:
105 f |= USCSI_WRITE;
106 break;
107 case LIBSCSI_AF_SILENT:
108 f |= USCSI_SILENT;
109 break;
110 case LIBSCSI_AF_DIAGNOSE:
111 f |= USCSI_DIAGNOSE;
112 break;
113 case LIBSCSI_AF_ISOLATE:
114 f |= USCSI_ISOLATE;
115 break;
116 case LIBSCSI_AF_RQSENSE:
117 f |= USCSI_RQENABLE;
118 break;
119 default:
120 return (libscsi_error(hp, ESCSI_BOGUSFLAGS,
121 "flag 0x%x is unknown", 1 << i));
122 }
123 }
124
125 *uf = f;
126
127 return (0);
128 }
129
130 static int
uscsi_exec(libscsi_hdl_t * hp,void * private,libscsi_action_t * ap)131 uscsi_exec(libscsi_hdl_t *hp, void *private, libscsi_action_t *ap)
132 {
133 struct uscsi_dev *dp = (struct uscsi_dev *)private;
134 struct uscsi_cmd cmd;
135 size_t data_a, data_v;
136 uint8_t *cp;
137 uint_t flags;
138
139 bzero(&cmd, sizeof (cmd));
140
141 cp = libscsi_action_get_cdb(ap);
142 if (cp == NULL)
143 return (-1);
144
145 flags = libscsi_action_get_flags(ap);
146 if (xlate_flags(hp, flags, &cmd.uscsi_flags) != 0)
147 return (-1);
148
149 cmd.uscsi_status = (short)-1;
150 cmd.uscsi_timeout = (short)libscsi_action_get_timeout(ap);
151
152 cmd.uscsi_cdb = (caddr_t)cp;
153 cmd.uscsi_cdblen = libscsi_action_get_cdblen(ap);
154 if (cmd.uscsi_cdblen == 0)
155 return (-1);
156
157 if (flags & (LIBSCSI_AF_READ | LIBSCSI_AF_WRITE)) {
158 if (libscsi_action_get_buffer(ap,
159 (uint8_t **)&cmd.uscsi_bufaddr, &data_a, &data_v) != 0)
160 return (-1);
161 if (flags & LIBSCSI_AF_READ)
162 cmd.uscsi_buflen = data_a;
163 else
164 cmd.uscsi_buflen = data_v;
165 }
166 if (flags & LIBSCSI_AF_RQSENSE) {
167 if (libscsi_action_get_sense(ap, (uint8_t **)&cmd.uscsi_rqbuf,
168 &data_a, NULL) != 0)
169 return (-1);
170 if (data_a > UCHAR_MAX)
171 data_a = UCHAR_MAX;
172 cmd.uscsi_rqlen = (uchar_t)data_a;
173 cmd.uscsi_rqstatus = (uchar_t)-1;
174 }
175
176 if (ioctl(dp->fd, USCSICMD, &cmd) < 0) {
177 ASSERT(errno != EFAULT);
178 switch (errno) {
179 case EINVAL:
180 return (libscsi_error(hp, ESCSI_BADCMD, "internal "
181 "uscsi error"));
182 case EPERM:
183 return (libscsi_error(hp, ESCSI_PERM, "insufficient "
184 "privileges "));
185 case EIO:
186 /* Command never executed at all */
187 if (cmd.uscsi_status == (short)-1)
188 return (libscsi_error(hp, ESCSI_IO, "I/O "
189 "error", strerror(errno)));
190 break;
191 default:
192 return (libscsi_error(hp, ESCSI_SYS, "uscsi ioctl "
193 "failed: %s", strerror(errno)));
194 }
195 }
196
197 libscsi_action_set_status(ap, cmd.uscsi_status);
198 if ((flags & LIBSCSI_AF_READ) && libscsi_action_set_datalen(ap,
199 cmd.uscsi_buflen - cmd.uscsi_resid) != 0)
200 return (-1);
201 if ((flags & LIBSCSI_AF_RQSENSE) && libscsi_action_set_senselen(ap,
202 cmd.uscsi_rqlen - cmd.uscsi_rqresid) != 0)
203 return (-1);
204
205 return (0);
206 }
207
208 /*ARGSUSED*/
209 static void
uscsi_target_name(libscsi_hdl_t * hp,void * private,char * buf,size_t len)210 uscsi_target_name(libscsi_hdl_t *hp, void *private, char *buf, size_t len)
211 {
212 struct uscsi_dev *dp = (struct uscsi_dev *)private;
213
214 (void) snprintf(buf, len, "%s", dp->dev);
215 }
216
217 static int
uscsi_max_transfer(libscsi_hdl_t * hp,void * private,size_t * sizep)218 uscsi_max_transfer(libscsi_hdl_t *hp, void *private, size_t *sizep)
219 {
220 uscsi_xfer_t xfer;
221 struct uscsi_dev *dp = (struct uscsi_dev *)private;
222
223 if (ioctl(dp->fd, USCSIMAXXFER, &xfer) < 0) {
224 ASSERT(errno != EFAULT);
225 switch (errno) {
226 case EINVAL:
227 return (libscsi_error(hp, ESCSI_BADCMD, "internal "
228 "uscsi error"));
229 case EPERM:
230 return (libscsi_error(hp, ESCSI_PERM, "insufficient "
231 "privileges "));
232 case ENOTTY:
233 return (libscsi_error(hp, ESCSI_NOTSUP, "max transfer "
234 "request not supported on device"));
235 default:
236 return (libscsi_error(hp, ESCSI_SYS, "uscsi ioctl "
237 "failed: %s", strerror(errno)));
238 }
239 }
240
241 if (xfer > SIZE_MAX)
242 xfer = SIZE_MAX;
243
244 *sizep = (size_t)xfer;
245 return (0);
246 }
247
248 static const libscsi_engine_ops_t uscsi_ops = {
249 .lseo_open = uscsi_open,
250 .lseo_close = uscsi_close,
251 .lseo_exec = uscsi_exec,
252 .lseo_target_name = uscsi_target_name,
253 .lseo_max_transfer = uscsi_max_transfer
254 };
255
256 static const libscsi_engine_t uscsi_engine = {
257 .lse_name = "uscsi",
258 .lse_libversion = LIBSCSI_VERSION,
259 .lse_ops = &uscsi_ops
260 };
261
262 /*ARGSUSED*/
263 const libscsi_engine_t *
libscsi_uscsi_init(libscsi_hdl_t * hp)264 libscsi_uscsi_init(libscsi_hdl_t *hp)
265 {
266 return (&uscsi_engine);
267 }
268