xref: /illumos-gate/usr/src/cmd/bhyve/common/pci_virtio_scsi_ctl.c (revision f032f69d5cad7fff66bd7478cbf3011ad5dcddce)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2016 Jakub Klama <jceel@FreeBSD.org>.
5  * Copyright (c) 2018 Marcelo Araujo <araujo@FreeBSD.org>.
6  * Copyright (c) 2026 Hans Rosenfeld
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer
14  *    in this position and unchanged.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/param.h>
33 #include <sys/linker_set.h>
34 #include <sys/types.h>
35 #include <sys/uio.h>
36 #include <sys/time.h>
37 #include <sys/queue.h>
38 #include <sys/sbuf.h>
39 
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <stdbool.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include <assert.h>
48 #include <pthread.h>
49 #include <pthread_np.h>
50 
51 #include <cam/scsi/scsi_all.h>
52 #include <cam/scsi/scsi_message.h>
53 #include <cam/ctl/ctl.h>
54 #include <cam/ctl/ctl_io.h>
55 #include <cam/ctl/ctl_backend.h>
56 #include <cam/ctl/ctl_ioctl.h>
57 #include <cam/ctl/ctl_util.h>
58 #include <cam/ctl/ctl_scsi_all.h>
59 #include <camlib.h>
60 
61 #include "bhyverun.h"
62 #include "config.h"
63 #include "debug.h"
64 #include "pci_emul.h"
65 #include "virtio.h"
66 #include "iov.h"
67 #include "pci_virtio_scsi.h"
68 
69 struct vtscsi_ctl_backend {
70 	struct pci_vtscsi_backend	vcb_backend;
71 	int				vcb_iid;
72 };
73 
74 static int vtscsi_ctl_init(struct pci_vtscsi_softc *,
75     struct pci_vtscsi_backend *, nvlist_t *);
76 static int vtscsi_ctl_open(struct pci_vtscsi_softc *, const char *, long);
77 static void vtscsi_ctl_reset(struct pci_vtscsi_softc *);
78 
79 static void *vtscsi_ctl_req_alloc(struct pci_vtscsi_softc *);
80 static void vtscsi_ctl_req_clear(void  *);
81 static void vtscsi_ctl_req_free(void *);
82 
83 static void vtscsi_ctl_tmf_hdl(struct pci_vtscsi_softc *, int,
84     struct pci_vtscsi_ctrl_tmf *);
85 static void vtscsi_ctl_an_hdl(struct pci_vtscsi_softc *, int,
86     struct pci_vtscsi_ctrl_an *);
87 static int vtscsi_ctl_req_hdl(struct pci_vtscsi_softc *, int,
88     struct pci_vtscsi_request *);
89 
90 static int
vtscsi_ctl_count_targets(const char * prefix __unused,const nvlist_t * parent __unused,const char * name __unused,int type,void * arg)91 vtscsi_ctl_count_targets(const char *prefix __unused,
92     const nvlist_t *parent __unused, const char *name __unused, int type,
93     void *arg)
94 {
95 	int *count = arg;
96 
97 	if (type != NV_TYPE_STRING) {
98 		EPRINTLN("invalid target \"%s\" type: not a string", name);
99 		errno = EINVAL;
100 		return (-1);
101 	}
102 
103 	(*count)++;
104 
105 	return (0);
106 }
107 
108 static int
vtscsi_ctl_init(struct pci_vtscsi_softc * sc,struct pci_vtscsi_backend * backend,nvlist_t * nvl)109 vtscsi_ctl_init(struct pci_vtscsi_softc *sc, struct pci_vtscsi_backend *backend,
110     nvlist_t *nvl)
111 {
112 	int count = 0;
113 	int ret = 0;
114 	struct vtscsi_ctl_backend *ctl_backend;
115 	const char *value;
116 
117 	ctl_backend = calloc(1, sizeof(struct vtscsi_ctl_backend));
118 	if (ctl_backend == NULL) {
119 		EPRINTLN("failed to allocate backend data: %s",
120 		    strerror(errno));
121 		return (-1);
122 	}
123 
124 	ctl_backend->vcb_backend = *backend;
125 	sc->vss_backend = &ctl_backend->vcb_backend;
126 
127 	value = get_config_value_node(nvl, "iid");
128 	if (value != NULL)
129 		ctl_backend->vcb_iid = strtoul(value, NULL, 10);
130 
131 	/*
132 	 * Count configured targets. If no targets were configured, use
133 	 * /dev/cam/ctl to remain compatible with previous versions.
134 	 */
135 	nvl = find_relative_config_node(nvl, "target");
136 	if (nvl != NULL)
137 		ret = walk_config_nodes("", nvl, &count,
138 		    vtscsi_ctl_count_targets);
139 
140 	if (ret != 0)
141 		return (ret);
142 
143 	if (count == 0)
144 		set_config_value_node(nvl, "0", "/dev/cam/ctl");
145 
146 	return (0);
147 }
148 
149 static int
vtscsi_ctl_open(struct pci_vtscsi_softc * sc __unused,const char * devname,long target)150 vtscsi_ctl_open(struct pci_vtscsi_softc *sc __unused, const char *devname,
151     long target)
152 {
153 	struct pci_vtscsi_target *tgt = &sc->vss_targets[target];
154 
155 	tgt->vst_fd = open(devname, O_RDWR);
156 	if (tgt->vst_fd < 0)
157 		return (-1);
158 
159 	return (0);
160 }
161 
162 static void
vtscsi_ctl_reset(struct pci_vtscsi_softc * sc __unused)163 vtscsi_ctl_reset(struct pci_vtscsi_softc *sc __unused)
164 {
165 	/*
166 	 * There doesn't seem to be a limit to the maximum number of
167 	 * sectors CTL can transfer in one request.
168 	 */
169 	sc->vss_config.max_sectors = INT32_MAX;
170 }
171 
vtscsi_ctl_req_alloc(struct pci_vtscsi_softc * sc)172 static void *vtscsi_ctl_req_alloc(struct pci_vtscsi_softc *sc)
173 {
174 	struct vtscsi_ctl_backend *ctl = (struct vtscsi_ctl_backend *)sc;
175 	union ctl_io *io = ctl_scsi_alloc_io(ctl->vcb_iid);
176 
177 	if (io != NULL)
178 		ctl_scsi_zero_io(io);
179 
180 	return (io);
181 }
182 
vtscsi_ctl_req_clear(void * io)183 static void vtscsi_ctl_req_clear(void *io)
184 {
185 	ctl_scsi_zero_io(io);
186 }
187 
vtscsi_ctl_req_free(void * io)188 static void vtscsi_ctl_req_free(void *io)
189 {
190 	ctl_scsi_free_io(io);
191 }
192 
193 static void
vtscsi_ctl_tmf_hdl(struct pci_vtscsi_softc * sc,int fd,struct pci_vtscsi_ctrl_tmf * tmf)194 vtscsi_ctl_tmf_hdl(struct pci_vtscsi_softc *sc, int fd,
195     struct pci_vtscsi_ctrl_tmf *tmf)
196 {
197 	struct vtscsi_ctl_backend *ctl;
198 	union ctl_io *io;
199 	int err;
200 
201 	ctl = (struct vtscsi_ctl_backend *)sc->vss_backend;
202 
203 	io = vtscsi_ctl_req_alloc(sc);
204 	if (io == NULL) {
205 		tmf->response = VIRTIO_SCSI_S_FAILURE;
206 		return;
207 	}
208 	vtscsi_ctl_req_clear(io);
209 
210 	io->io_hdr.io_type = CTL_IO_TASK;
211 	io->io_hdr.nexus.initid = ctl->vcb_iid;
212 	io->io_hdr.nexus.targ_lun = pci_vtscsi_get_lun(sc, tmf->lun);
213 	io->taskio.tag_type = CTL_TAG_SIMPLE;
214 	io->taskio.tag_num = tmf->id;
215 	io->io_hdr.flags |= CTL_FLAG_USER_TAG;
216 
217 	switch (tmf->subtype) {
218 	case VIRTIO_SCSI_T_TMF_ABORT_TASK:
219 		io->taskio.task_action = CTL_TASK_ABORT_TASK;
220 		break;
221 
222 	case VIRTIO_SCSI_T_TMF_ABORT_TASK_SET:
223 		io->taskio.task_action = CTL_TASK_ABORT_TASK_SET;
224 		break;
225 
226 	case VIRTIO_SCSI_T_TMF_CLEAR_ACA:
227 		io->taskio.task_action = CTL_TASK_CLEAR_ACA;
228 		break;
229 
230 	case VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET:
231 		io->taskio.task_action = CTL_TASK_CLEAR_TASK_SET;
232 		break;
233 
234 	case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET:
235 		io->taskio.task_action = CTL_TASK_I_T_NEXUS_RESET;
236 		break;
237 
238 	case VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET:
239 		io->taskio.task_action = CTL_TASK_LUN_RESET;
240 		break;
241 
242 	case VIRTIO_SCSI_T_TMF_QUERY_TASK:
243 		io->taskio.task_action = CTL_TASK_QUERY_TASK;
244 		break;
245 
246 	case VIRTIO_SCSI_T_TMF_QUERY_TASK_SET:
247 		io->taskio.task_action = CTL_TASK_QUERY_TASK_SET;
248 		break;
249 	}
250 
251 	if (pci_vtscsi_debug) {
252 		struct sbuf *sb = sbuf_new_auto();
253 		ctl_io_sbuf(io, sb);
254 		sbuf_finish(sb);
255 		DPRINTF("%s", sbuf_data(sb));
256 		sbuf_delete(sb);
257 	}
258 
259 	err = ioctl(fd, CTL_IO, io);
260 	if (err != 0) {
261 		WPRINTF("CTL_IO: err=%d (%s)", errno, strerror(errno));
262 		tmf->response = VIRTIO_SCSI_S_FAILURE;
263 	} else {
264 		tmf->response = io->taskio.task_status;
265 	}
266 	vtscsi_ctl_req_free(io);
267 }
268 
269 static void
vtscsi_ctl_an_hdl(struct pci_vtscsi_softc * sc __unused,int fd __unused,struct pci_vtscsi_ctrl_an * an)270 vtscsi_ctl_an_hdl(struct pci_vtscsi_softc *sc __unused, int fd __unused,
271     struct pci_vtscsi_ctrl_an *an)
272 {
273 	an->response = VIRTIO_SCSI_S_FAILURE;
274 }
275 
276 static int
vtscsi_ctl_req_hdl(struct pci_vtscsi_softc * sc,int fd,struct pci_vtscsi_request * req)277 vtscsi_ctl_req_hdl(struct pci_vtscsi_softc *sc, int fd,
278     struct pci_vtscsi_request *req)
279 {
280 	union ctl_io *io = req->vsr_backend;
281 	void *ext_data_ptr = NULL;
282 	uint32_t ext_data_len = 0, ext_sg_entries = 0;
283 	struct vtscsi_ctl_backend *ctl;
284 	int err, nxferred;
285 
286 	ctl = (struct vtscsi_ctl_backend *)sc->vss_backend;
287 
288 	io->io_hdr.nexus.initid = ctl->vcb_iid;
289 	io->io_hdr.nexus.targ_lun = pci_vtscsi_get_lun(sc,
290 	    req->vsr_cmd_rd->lun);
291 
292 	io->io_hdr.io_type = CTL_IO_SCSI;
293 
294 	if (req->vsr_data_niov_in > 0) {
295 		ext_data_ptr = (void *)req->vsr_data_iov_in;
296 		ext_sg_entries = req->vsr_data_niov_in;
297 		ext_data_len = count_iov(req->vsr_data_iov_in,
298 		    req->vsr_data_niov_in);
299 		io->io_hdr.flags |= CTL_FLAG_DATA_OUT;
300 	} else if (req->vsr_data_niov_out > 0) {
301 		ext_data_ptr = (void *)req->vsr_data_iov_out;
302 		ext_sg_entries = req->vsr_data_niov_out;
303 		ext_data_len = count_iov(req->vsr_data_iov_out,
304 		    req->vsr_data_niov_out);
305 		io->io_hdr.flags |= CTL_FLAG_DATA_IN;
306 	}
307 
308 	io->scsiio.sense_len = sc->vss_config.sense_size;
309 	io->scsiio.tag_num = req->vsr_cmd_rd->id;
310 	io->io_hdr.flags |= CTL_FLAG_USER_TAG;
311 	switch (req->vsr_cmd_rd->task_attr) {
312 	case VIRTIO_SCSI_S_ORDERED:
313 		io->scsiio.tag_type = CTL_TAG_ORDERED;
314 		break;
315 	case VIRTIO_SCSI_S_HEAD:
316 		io->scsiio.tag_type = CTL_TAG_HEAD_OF_QUEUE;
317 		break;
318 	case VIRTIO_SCSI_S_ACA:
319 		io->scsiio.tag_type = CTL_TAG_ACA;
320 		break;
321 	case VIRTIO_SCSI_S_SIMPLE:
322 	default:
323 		io->scsiio.tag_type = CTL_TAG_SIMPLE;
324 		break;
325 	}
326 	io->scsiio.ext_sg_entries = ext_sg_entries;
327 	io->scsiio.ext_data_ptr = ext_data_ptr;
328 	io->scsiio.ext_data_len = ext_data_len;
329 	io->scsiio.ext_data_filled = 0;
330 	io->scsiio.cdb_len = sc->vss_config.cdb_size;
331 	memcpy(io->scsiio.cdb, req->vsr_cmd_rd->cdb, sc->vss_config.cdb_size);
332 
333 	if (pci_vtscsi_debug) {
334 		struct sbuf *sb = sbuf_new_auto();
335 		ctl_io_sbuf(io, sb);
336 		sbuf_finish(sb);
337 		DPRINTF("%s", sbuf_data(sb));
338 		sbuf_delete(sb);
339 	}
340 
341 	err = ioctl(fd, CTL_IO, io);
342 	if (err != 0) {
343 		WPRINTF("CTL_IO: err=%d (%s)", errno, strerror(errno));
344 		req->vsr_cmd_wr->response = VIRTIO_SCSI_S_FAILURE;
345 	} else {
346 		req->vsr_cmd_wr->sense_len =
347 		    MIN(io->scsiio.sense_len, sc->vss_config.sense_size);
348 		req->vsr_cmd_wr->residual = ext_data_len -
349 		    io->scsiio.ext_data_filled;
350 		req->vsr_cmd_wr->status = io->scsiio.scsi_status;
351 		req->vsr_cmd_wr->response = VIRTIO_SCSI_S_OK;
352 		memcpy(&req->vsr_cmd_wr->sense, &io->scsiio.sense_data,
353 		    req->vsr_cmd_wr->sense_len);
354 	}
355 
356 	nxferred = io->scsiio.ext_data_filled;
357 	return (nxferred);
358 }
359 
360 static const struct pci_vtscsi_backend vtscsi_ctl_backend = {
361 	.vsb_name = "ctl",
362 	.vsb_init = vtscsi_ctl_init,
363 	.vsb_open = vtscsi_ctl_open,
364 	.vsb_reset = vtscsi_ctl_reset,
365 
366 	.vsb_req_alloc = vtscsi_ctl_req_alloc,
367 	.vsb_req_clear = vtscsi_ctl_req_clear,
368 	.vsb_req_free = vtscsi_ctl_req_free,
369 
370 	.vsb_tmf_hdl = vtscsi_ctl_tmf_hdl,
371 	.vsb_an_hdl = vtscsi_ctl_an_hdl,
372 	.vsb_req_hdl = vtscsi_ctl_req_hdl
373 };
374 PCI_VTSCSI_BACKEND_SET(vtscsi_ctl_backend);
375