xref: /openbsd/usr.sbin/vmd/vioscsi.c (revision 253c7ec9)
1*253c7ec9Sjsg /*	$OpenBSD: vioscsi.c,v 1.23 2023/04/01 06:39:03 jsg Exp $  */
295ab188fSccardenas 
395ab188fSccardenas /*
495ab188fSccardenas  * Copyright (c) 2017 Carlos Cardenas <ccardenas@openbsd.org>
595ab188fSccardenas  *
695ab188fSccardenas  * Permission to use, copy, modify, and distribute this software for any
795ab188fSccardenas  * purpose with or without fee is hereby granted, provided that the above
895ab188fSccardenas  * copyright notice and this permission notice appear in all copies.
995ab188fSccardenas  *
1095ab188fSccardenas  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1195ab188fSccardenas  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1295ab188fSccardenas  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1395ab188fSccardenas  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1495ab188fSccardenas  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1595ab188fSccardenas  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1695ab188fSccardenas  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1795ab188fSccardenas  */
1895ab188fSccardenas 
1995ab188fSccardenas #include <sys/types.h>
206eb4c859Sdv 
2111b9cb3bSsf #include <dev/pci/virtio_pcireg.h>
2295ab188fSccardenas #include <dev/pv/vioscsireg.h>
2395ab188fSccardenas #include <scsi/scsi_all.h>
2495ab188fSccardenas #include <scsi/scsi_disk.h>
2595ab188fSccardenas #include <scsi/scsiconf.h>
2695ab188fSccardenas #include <scsi/cd.h>
2795ab188fSccardenas 
2895ab188fSccardenas #include <errno.h>
2995ab188fSccardenas #include <stdlib.h>
3095ab188fSccardenas #include <string.h>
3195ab188fSccardenas #include <unistd.h>
3295ab188fSccardenas 
3395ab188fSccardenas #include "vmd.h"
3495ab188fSccardenas #include "vioscsi.h"
3595ab188fSccardenas #include "virtio.h"
3695ab188fSccardenas 
3795ab188fSccardenas extern char *__progname;
3895ab188fSccardenas 
3995ab188fSccardenas static void
4095ab188fSccardenas vioscsi_prepare_resp(struct virtio_scsi_res_hdr *resp, uint8_t vio_status,
4195ab188fSccardenas     uint8_t scsi_status, uint8_t err_flags, uint8_t add_sense_code,
4295ab188fSccardenas     uint8_t add_sense_code_qual)
4395ab188fSccardenas {
4495ab188fSccardenas 	/* Set lower 8 bits of status and response fields */
4595ab188fSccardenas 	resp->response &= 0xFFFFFF00;
4695ab188fSccardenas 	resp->response |= vio_status;
4795ab188fSccardenas 	resp->status &= 0xFFFFFF00;
4895ab188fSccardenas 	resp->status |= scsi_status;
4995ab188fSccardenas 
5095ab188fSccardenas 	resp->sense_len = 0;
5195ab188fSccardenas 
5295ab188fSccardenas 	/* determine if we need to populate the sense field */
5395ab188fSccardenas 	if (scsi_status == SCSI_CHECK) {
5495ab188fSccardenas 		/*
5595ab188fSccardenas 		 * sense data is a 96 byte field.
5695ab188fSccardenas 		 * We only need to use the first 14 bytes
5795ab188fSccardenas 		 * - set the sense_len accordingly
5895ab188fSccardenas 		 * - set error_code to Current Command
5995ab188fSccardenas 		 * ref scsi/scsi_all.h:struct scsi_sense_data
6095ab188fSccardenas 		 */
6195ab188fSccardenas 		memset(resp->sense, 0, VIOSCSI_SENSE_LEN);
6295ab188fSccardenas 		resp->sense_len = RESP_SENSE_LEN;
6395ab188fSccardenas 		resp->sense[0] = SSD_ERRCODE_CURRENT;
6495ab188fSccardenas 		resp->sense[2] = err_flags;
6595ab188fSccardenas 		resp->sense[12] = add_sense_code;
6695ab188fSccardenas 		resp->sense[13] = add_sense_code_qual;
6795ab188fSccardenas 	}
6895ab188fSccardenas }
6995ab188fSccardenas 
7095ab188fSccardenas static struct vring_desc*
7195ab188fSccardenas vioscsi_next_ring_desc(struct vring_desc* desc, struct vring_desc* cur,
7295ab188fSccardenas     uint16_t *idx)
7395ab188fSccardenas {
7495ab188fSccardenas 	*idx = cur->next & VIOSCSI_QUEUE_MASK;
7595ab188fSccardenas 	return &desc[*idx];
7695ab188fSccardenas }
7795ab188fSccardenas 
7895ab188fSccardenas static void
7995ab188fSccardenas vioscsi_next_ring_item(struct vioscsi_dev *dev, struct vring_avail *avail,
8095ab188fSccardenas     struct vring_used *used, struct vring_desc *desc, uint16_t idx)
8195ab188fSccardenas {
8295ab188fSccardenas 	used->ring[used->idx & VIOSCSI_QUEUE_MASK].id = idx;
8395ab188fSccardenas 	used->ring[used->idx & VIOSCSI_QUEUE_MASK].len = desc->len;
840bd10b9fSdv 	__sync_synchronize();
8595ab188fSccardenas 	used->idx++;
8695ab188fSccardenas 
8795ab188fSccardenas 	dev->vq[dev->cfg.queue_notify].last_avail =
8895ab188fSccardenas 	    avail->idx & VIOSCSI_QUEUE_MASK;
8995ab188fSccardenas }
9095ab188fSccardenas 
9195ab188fSccardenas static const char *
9295ab188fSccardenas vioscsi_op_names(uint8_t type)
9395ab188fSccardenas {
9495ab188fSccardenas 	switch (type) {
9595ab188fSccardenas 	/* defined in scsi_all.h */
9695ab188fSccardenas 	case TEST_UNIT_READY: return "TEST_UNIT_READY";
9795ab188fSccardenas 	case REQUEST_SENSE: return "REQUEST_SENSE";
9895ab188fSccardenas 	case INQUIRY: return "INQUIRY";
9995ab188fSccardenas 	case MODE_SELECT: return "MODE_SELECT";
10095ab188fSccardenas 	case RESERVE: return "RESERVE";
10195ab188fSccardenas 	case RELEASE: return "RELEASE";
10295ab188fSccardenas 	case MODE_SENSE: return "MODE_SENSE";
10395ab188fSccardenas 	case START_STOP: return "START_STOP";
10495ab188fSccardenas 	case RECEIVE_DIAGNOSTIC: return "RECEIVE_DIAGNOSTIC";
10595ab188fSccardenas 	case SEND_DIAGNOSTIC: return "SEND_DIAGNOSTIC";
10695ab188fSccardenas 	case PREVENT_ALLOW: return "PREVENT_ALLOW";
10795ab188fSccardenas 	case POSITION_TO_ELEMENT: return "POSITION_TO_ELEMENT";
10895ab188fSccardenas 	case WRITE_BUFFER: return "WRITE_BUFFER";
10995ab188fSccardenas 	case READ_BUFFER: return "READ_BUFFER";
11095ab188fSccardenas 	case CHANGE_DEFINITION: return "CHANGE_DEFINITION";
11195ab188fSccardenas 	case MODE_SELECT_BIG: return "MODE_SELECT_BIG";
11295ab188fSccardenas 	case MODE_SENSE_BIG: return "MODE_SENSE_BIG";
11395ab188fSccardenas 	case REPORT_LUNS: return "REPORT_LUNS";
11495ab188fSccardenas 	/* defined in scsi_disk.h */
11595ab188fSccardenas 	case REASSIGN_BLOCKS: return "REASSIGN_BLOCKS";
11695ab188fSccardenas 	case READ_COMMAND: return "READ_COMMAND";
11795ab188fSccardenas 	case WRITE_COMMAND: return "WRITE_COMMAND";
11895ab188fSccardenas 	case READ_CAPACITY: return "READ_CAPACITY";
11995ab188fSccardenas 	case READ_CAPACITY_16: return "READ_CAPACITY_16";
120eccd596dSkrw 	case READ_10: return "READ_10";
121eccd596dSkrw 	case WRITE_10: return "WRITE_10";
12295ab188fSccardenas 	case READ_12: return "READ_12";
12395ab188fSccardenas 	case WRITE_12: return "WRITE_12";
12495ab188fSccardenas 	case READ_16: return "READ_16";
12595ab188fSccardenas 	case WRITE_16: return "WRITE_16";
12695ab188fSccardenas 	case SYNCHRONIZE_CACHE: return "SYNCHRONIZE_CACHE";
12795ab188fSccardenas 	case WRITE_SAME_10: return "WRITE_SAME_10";
12895ab188fSccardenas 	case WRITE_SAME_16: return "WRITE_SAME_16";
12995ab188fSccardenas 	/* defined in cd.h */
13095ab188fSccardenas 	case READ_SUBCHANNEL: return "READ_SUBCHANNEL";
13195ab188fSccardenas 	case READ_TOC: return "READ_TOC";
13295ab188fSccardenas 	case READ_HEADER: return "READ_HEADER";
13395ab188fSccardenas 	case PLAY: return "PLAY";
13495ab188fSccardenas 	case PLAY_MSF: return "PLAY_MSF";
13595ab188fSccardenas 	case PLAY_TRACK: return "PLAY_TRACK";
13695ab188fSccardenas 	case PLAY_TRACK_REL: return "PLAY_TRACK_REL";
13795ab188fSccardenas 	case PAUSE: return "PAUSE";
13895ab188fSccardenas 	case READ_TRACK_INFO: return "READ_TRACK_INFO";
13995ab188fSccardenas 	case CLOSE_TRACK: return "CLOSE_TRACK";
14095ab188fSccardenas 	case BLANK: return "BLANK";
14195ab188fSccardenas 	case PLAY_BIG: return "PLAY_BIG";
14295ab188fSccardenas 	case LOAD_UNLOAD: return "LOAD_UNLOAD";
14395ab188fSccardenas 	case PLAY_TRACK_REL_BIG: return "PLAY_TRACK_REL_BIG";
14495ab188fSccardenas 	case SET_CD_SPEED: return "SET_CD_SPEED";
14595ab188fSccardenas 	/* defined locally */
14695ab188fSccardenas 	case READ_DISC_INFORMATION: return "READ_DISC_INFORMATION";
14795ab188fSccardenas 	case GET_CONFIGURATION: return "GET_CONFIGURATION";
14895ab188fSccardenas 	case MECHANISM_STATUS: return "MECHANISM_STATUS";
14995ab188fSccardenas 	case GET_EVENT_STATUS_NOTIFICATION:
15095ab188fSccardenas 	    return "GET_EVENT_STATUS_NOTIFICATION";
15195ab188fSccardenas 	default: return "UNKNOWN";
15295ab188fSccardenas 	}
15395ab188fSccardenas }
15495ab188fSccardenas 
15595ab188fSccardenas static const char *
15695ab188fSccardenas vioscsi_reg_name(uint8_t reg)
15795ab188fSccardenas {
15895ab188fSccardenas 	switch (reg) {
15995ab188fSccardenas 	case VIRTIO_CONFIG_DEVICE_FEATURES: return "device feature";
16095ab188fSccardenas 	case VIRTIO_CONFIG_GUEST_FEATURES: return "guest feature";
1610bd10b9fSdv 	case VIRTIO_CONFIG_QUEUE_PFN: return "queue pfn";
16295ab188fSccardenas 	case VIRTIO_CONFIG_QUEUE_SIZE: return "queue size";
16395ab188fSccardenas 	case VIRTIO_CONFIG_QUEUE_SELECT: return "queue select";
16495ab188fSccardenas 	case VIRTIO_CONFIG_QUEUE_NOTIFY: return "queue notify";
16595ab188fSccardenas 	case VIRTIO_CONFIG_DEVICE_STATUS: return "device status";
16695ab188fSccardenas 	case VIRTIO_CONFIG_ISR_STATUS: return "isr status";
16795ab188fSccardenas 	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI: return "num_queues";
16895ab188fSccardenas 	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 4: return "seg_max";
16995ab188fSccardenas 	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 8: return "max_sectors";
17095ab188fSccardenas 	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 12: return "cmd_per_lun";
17195ab188fSccardenas 	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 16: return "event_info_size";
17295ab188fSccardenas 	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 20: return "sense_size";
17395ab188fSccardenas 	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 24: return "cdb_size";
17495ab188fSccardenas 	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 28: return "max_channel";
17595ab188fSccardenas 	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 30: return "max_target";
17695ab188fSccardenas 	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 32: return "max_lun";
17795ab188fSccardenas 	default: return "unknown";
17895ab188fSccardenas 	}
17995ab188fSccardenas }
18095ab188fSccardenas 
18195ab188fSccardenas static void
18295ab188fSccardenas vioscsi_free_info(struct ioinfo *info)
18395ab188fSccardenas {
18495ab188fSccardenas 	if (!info)
18595ab188fSccardenas 		return;
18695ab188fSccardenas 	free(info->buf);
18795ab188fSccardenas 	free(info);
18895ab188fSccardenas }
18995ab188fSccardenas 
19095ab188fSccardenas static struct ioinfo *
1910759b25cSdv vioscsi_start_read(struct vioscsi_dev *dev, off_t block, size_t n_blocks)
19295ab188fSccardenas {
19395ab188fSccardenas 	struct ioinfo *info;
19495ab188fSccardenas 
1950759b25cSdv 	/* Limit to 64M for now */
1960759b25cSdv 	if (n_blocks * VIOSCSI_BLOCK_SIZE_CDROM > (1 << 26)) {
1970759b25cSdv 		log_warnx("%s: read size exceeded 64M", __func__);
1980759b25cSdv 		return (NULL);
1990759b25cSdv 	}
2000759b25cSdv 
20195ab188fSccardenas 	info = calloc(1, sizeof(*info));
20295ab188fSccardenas 	if (!info)
20395ab188fSccardenas 		goto nomem;
20495ab188fSccardenas 	info->buf = malloc(n_blocks * VIOSCSI_BLOCK_SIZE_CDROM);
20595ab188fSccardenas 	if (info->buf == NULL)
20695ab188fSccardenas 		goto nomem;
20795ab188fSccardenas 	info->len = n_blocks * VIOSCSI_BLOCK_SIZE_CDROM;
20895ab188fSccardenas 	info->offset = block * VIOSCSI_BLOCK_SIZE_CDROM;
2099617633bSccardenas 	info->file = &dev->file;
21095ab188fSccardenas 
21195ab188fSccardenas 	return info;
21295ab188fSccardenas 
21395ab188fSccardenas nomem:
21495ab188fSccardenas 	free(info);
215ab0aadc5Sccardenas 	log_warn("malloc error vioscsi read");
21695ab188fSccardenas 	return (NULL);
21795ab188fSccardenas }
21895ab188fSccardenas 
21995ab188fSccardenas static const uint8_t *
22095ab188fSccardenas vioscsi_finish_read(struct ioinfo *info)
22195ab188fSccardenas {
2229617633bSccardenas 	struct virtio_backing *f;
2239617633bSccardenas 
2249617633bSccardenas 	f = info->file;
2259617633bSccardenas 	if (f->pread(f->p, info->buf, info->len, info->offset) != info->len) {
22695ab188fSccardenas 		info->error = errno;
22795ab188fSccardenas 		log_warn("vioscsi read error");
22895ab188fSccardenas 		return NULL;
22995ab188fSccardenas 	}
23095ab188fSccardenas 
23195ab188fSccardenas 	return info->buf;
23295ab188fSccardenas }
23395ab188fSccardenas 
23428705897Sccardenas static int
23528705897Sccardenas vioscsi_handle_tur(struct vioscsi_dev *dev, struct virtio_scsi_req_hdr *req,
23628705897Sccardenas     struct virtio_vq_acct *acct)
23728705897Sccardenas {
23828705897Sccardenas 	int ret = 0;
23928705897Sccardenas 	struct virtio_scsi_res_hdr resp;
24028705897Sccardenas 
24128705897Sccardenas 	memset(&resp, 0, sizeof(resp));
24228705897Sccardenas 	/* Move index for response */
24328705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
24428705897Sccardenas 	    &(acct->resp_idx));
24528705897Sccardenas 
24628705897Sccardenas 	vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
24728705897Sccardenas 
2480759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
24928705897Sccardenas 		log_warnx("%s: unable to write OK resp status data @ 0x%llx",
25028705897Sccardenas 		    __func__, acct->resp_desc->addr);
25128705897Sccardenas 	} else {
25228705897Sccardenas 		ret = 1;
25328705897Sccardenas 		dev->cfg.isr_status = 1;
25428705897Sccardenas 		/* Move ring indexes */
25528705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
25628705897Sccardenas 		    acct->req_desc, acct->req_idx);
25728705897Sccardenas 	}
25828705897Sccardenas 
25928705897Sccardenas 	return (ret);
26028705897Sccardenas }
26128705897Sccardenas 
26228705897Sccardenas static int
26328705897Sccardenas vioscsi_handle_inquiry(struct vioscsi_dev *dev,
26428705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
26528705897Sccardenas {
26628705897Sccardenas 	int ret = 0;
26728705897Sccardenas 	struct virtio_scsi_res_hdr resp;
26828705897Sccardenas 	struct scsi_inquiry_data *inq_data;
26928705897Sccardenas 
27030dd31d2Sdv #if DEBUG
27130dd31d2Sdv 	struct scsi_inquiry *inq = (struct scsi_inquiry *)(req->cdb);
27230dd31d2Sdv 	log_debug("%s: INQ - EVPD %d PAGE_CODE 0x%08x LEN %d", __func__,
27330dd31d2Sdv 	    inq->flags & SI_EVPD, inq->pagecode, _2btol(inq->length));
27430dd31d2Sdv #endif /* DEBUG */
27530dd31d2Sdv 
27628705897Sccardenas 	memset(&resp, 0, sizeof(resp));
27728705897Sccardenas 	vioscsi_prepare_resp(&resp,
27828705897Sccardenas 	    VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
27928705897Sccardenas 
28028705897Sccardenas 	inq_data = calloc(1, sizeof(struct scsi_inquiry_data));
28128705897Sccardenas 
28228705897Sccardenas 	if (inq_data == NULL) {
28328705897Sccardenas 		log_warnx("%s: cannot alloc inq_data", __func__);
28428705897Sccardenas 		goto inq_out;
28528705897Sccardenas 	}
28628705897Sccardenas 
28728705897Sccardenas 	inq_data->device = T_CDROM;
28828705897Sccardenas 	inq_data->dev_qual2 = SID_REMOVABLE;
28928705897Sccardenas 	/* Leave version zero to say we don't comply */
290a1f20724Skrw 	inq_data->response_format = SID_SCSI2_RESPONSE;
29128705897Sccardenas 	inq_data->additional_length = SID_SCSI2_ALEN;
29228705897Sccardenas 	memcpy(inq_data->vendor, INQUIRY_VENDOR, INQUIRY_VENDOR_LEN);
29328705897Sccardenas 	memcpy(inq_data->product, INQUIRY_PRODUCT, INQUIRY_PRODUCT_LEN);
29428705897Sccardenas 	memcpy(inq_data->revision, INQUIRY_REVISION, INQUIRY_REVISION_LEN);
29528705897Sccardenas 
29628705897Sccardenas 	/* Move index for response */
29728705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
29828705897Sccardenas 	    &(acct->resp_idx));
29928705897Sccardenas 
3001f5e00e0Sreyk 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
30128705897Sccardenas 	    "idx %d req_idx %d global_idx %d", __func__, acct->resp_desc->addr,
30228705897Sccardenas 	    acct->resp_desc->len, acct->resp_idx, acct->req_idx, acct->idx);
30328705897Sccardenas 
3040759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
30528705897Sccardenas 		log_warnx("%s: unable to write OK resp status data @ 0x%llx",
30628705897Sccardenas 		    __func__, acct->resp_desc->addr);
30728705897Sccardenas 		goto free_inq;
30828705897Sccardenas 	}
30928705897Sccardenas 
31028705897Sccardenas 	/* Move index for inquiry_data */
31128705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
31228705897Sccardenas 	    &(acct->resp_idx));
31328705897Sccardenas 
3141f5e00e0Sreyk 	DPRINTF("%s: writing inq_data to 0x%llx size %d at "
31528705897Sccardenas 	    "local idx %d req_idx %d global_idx %d",
31628705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
31728705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
31828705897Sccardenas 
3190759b25cSdv 	if (write_mem(acct->resp_desc->addr, inq_data,
3200759b25cSdv 		sizeof(struct scsi_inquiry_data))) {
32128705897Sccardenas 		log_warnx("%s: unable to write inquiry"
32228705897Sccardenas 		    " response to gpa @ 0x%llx",
32328705897Sccardenas 		    __func__, acct->resp_desc->addr);
32428705897Sccardenas 	} else {
32528705897Sccardenas 		ret = 1;
32628705897Sccardenas 		dev->cfg.isr_status = 1;
32728705897Sccardenas 		/* Move ring indexes */
32828705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
32928705897Sccardenas 		    acct->req_desc, acct->req_idx);
33028705897Sccardenas 	}
33128705897Sccardenas 
33228705897Sccardenas free_inq:
33328705897Sccardenas 	free(inq_data);
33428705897Sccardenas inq_out:
33528705897Sccardenas 	return (ret);
33628705897Sccardenas }
33728705897Sccardenas 
33828705897Sccardenas static int
33928705897Sccardenas vioscsi_handle_mode_sense(struct vioscsi_dev *dev,
34028705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
34128705897Sccardenas {
34228705897Sccardenas 	int ret = 0;
34328705897Sccardenas 	struct virtio_scsi_res_hdr resp;
34428705897Sccardenas 	uint8_t mode_page_ctl;
34528705897Sccardenas 	uint8_t mode_page_code;
34628705897Sccardenas 	uint8_t *mode_reply;
3470759b25cSdv 	uint8_t mode_reply_len = 0;
34828705897Sccardenas 	struct scsi_mode_sense *mode_sense;
34928705897Sccardenas 
35028705897Sccardenas 	memset(&resp, 0, sizeof(resp));
35128705897Sccardenas 	mode_sense = (struct scsi_mode_sense *)(req->cdb);
35228705897Sccardenas 	mode_page_ctl = mode_sense->page & SMS_PAGE_CTRL;
35328705897Sccardenas 	mode_page_code = mode_sense->page & SMS_PAGE_CODE;
35428705897Sccardenas 
3552d03c861Sccardenas 	DPRINTF("%s: M_SENSE - DBD %d Page Ctrl 0x%x Code 0x%x Len %u",
35628705897Sccardenas 	    __func__, mode_sense->byte2 & SMS_DBD, mode_page_ctl,
35728705897Sccardenas 	    mode_page_code, mode_sense->length);
35828705897Sccardenas 
35928705897Sccardenas 	if (mode_page_ctl == SMS_PAGE_CTRL_CURRENT &&
36028705897Sccardenas 	    (mode_page_code == ERR_RECOVERY_PAGE ||
36128705897Sccardenas 	    mode_page_code == CDVD_CAPABILITIES_PAGE)) {
36228705897Sccardenas 		/*
36328705897Sccardenas 		 * mode sense header is 4 bytes followed
36428705897Sccardenas 		 * by a variable page
36528705897Sccardenas 		 * ERR_RECOVERY_PAGE is 12 bytes
3660759b25cSdv 		 * CDVD_CAPABILITIES_PAGE is 32 bytes
36728705897Sccardenas 		 */
36828705897Sccardenas 		switch (mode_page_code) {
36928705897Sccardenas 		case ERR_RECOVERY_PAGE:
37028705897Sccardenas 			mode_reply_len = 16;
37128705897Sccardenas 			mode_reply =
37228705897Sccardenas 			    (uint8_t*)calloc(mode_reply_len, sizeof(uint8_t));
37328705897Sccardenas 			if (mode_reply == NULL)
37428705897Sccardenas 				goto mode_sense_out;
37528705897Sccardenas 
37628705897Sccardenas 			/* set the page header */
37728705897Sccardenas 			*mode_reply = mode_reply_len - 1;
37828705897Sccardenas 			*(mode_reply + 1) = MODE_MEDIUM_TYPE_CODE;
37928705897Sccardenas 
38028705897Sccardenas 			/* set the page data, 7.3.2.1 mmc-5 */
38128705897Sccardenas 			*(mode_reply + 4) = MODE_ERR_RECOVERY_PAGE_CODE;
38228705897Sccardenas 			*(mode_reply + 5) = MODE_ERR_RECOVERY_PAGE_LEN;
38328705897Sccardenas 			*(mode_reply + 7) = MODE_READ_RETRY_COUNT;
38428705897Sccardenas 			break;
38528705897Sccardenas 		case CDVD_CAPABILITIES_PAGE:
3860759b25cSdv 			mode_reply_len = 36;
38728705897Sccardenas 			mode_reply =
38828705897Sccardenas 			    (uint8_t*)calloc(mode_reply_len, sizeof(uint8_t));
38928705897Sccardenas 			if (mode_reply == NULL)
39028705897Sccardenas 				goto mode_sense_out;
39128705897Sccardenas 
39228705897Sccardenas 			/* set the page header */
39328705897Sccardenas 			*mode_reply = mode_reply_len - 1;
39428705897Sccardenas 			*(mode_reply + 1) = MODE_MEDIUM_TYPE_CODE;
39528705897Sccardenas 
39628705897Sccardenas 			/* set the page data, 6.3.11 mmc-3 */
39728705897Sccardenas 			*(mode_reply + 4) = MODE_CDVD_CAP_PAGE_CODE;
39828705897Sccardenas 			*(mode_reply + 5) = mode_reply_len - 6;
39928705897Sccardenas 			*(mode_reply + 6) = MODE_CDVD_CAP_READ_CODE;
40028705897Sccardenas 			_lto2b(MODE_CDVD_CAP_NUM_LEVELS, mode_reply + 14);
40128705897Sccardenas 			break;
40228705897Sccardenas 		default:
40328705897Sccardenas 			goto mode_sense_error;
40428705897Sccardenas 			break;
40528705897Sccardenas 		}
40628705897Sccardenas 
40728705897Sccardenas 		/* Move index for response */
40828705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
40928705897Sccardenas 		    acct->req_desc, &(acct->resp_idx));
41028705897Sccardenas 
4111f5e00e0Sreyk 		DPRINTF("%s: writing resp to 0x%llx size %d "
41228705897Sccardenas 		    "at local idx %d req_idx %d global_idx %d",
4130759b25cSdv 		    __func__, acct->resp_desc->addr, mode_reply_len,
41428705897Sccardenas 		    acct->resp_idx, acct->req_idx, acct->idx);
41528705897Sccardenas 
4160759b25cSdv 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
41728705897Sccardenas 			log_warnx("%s: unable to write OK"
41828705897Sccardenas 			    " resp status data @ 0x%llx",
41928705897Sccardenas 			    __func__, acct->resp_desc->addr);
42028705897Sccardenas 			free(mode_reply);
42128705897Sccardenas 			goto mode_sense_out;
42228705897Sccardenas 		}
42328705897Sccardenas 
42428705897Sccardenas 		/* Move index for mode_reply */
42528705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
42628705897Sccardenas 		    acct->resp_desc, &(acct->resp_idx));
42728705897Sccardenas 
4281f5e00e0Sreyk 		DPRINTF("%s: writing mode_reply to 0x%llx "
42928705897Sccardenas 		    "size %d at local idx %d req_idx %d "
43028705897Sccardenas 		    "global_idx %d", __func__, acct->resp_desc->addr,
4310759b25cSdv 		    mode_reply_len, acct->resp_idx, acct->req_idx, acct->idx);
43228705897Sccardenas 
43328705897Sccardenas 		if (write_mem(acct->resp_desc->addr, mode_reply,
4340759b25cSdv 			mode_reply_len)) {
43528705897Sccardenas 			log_warnx("%s: unable to write "
43628705897Sccardenas 			    "mode_reply to gpa @ 0x%llx",
43728705897Sccardenas 			    __func__, acct->resp_desc->addr);
43828705897Sccardenas 			free(mode_reply);
43928705897Sccardenas 			goto mode_sense_out;
44028705897Sccardenas 		}
44128705897Sccardenas 
44228705897Sccardenas 		free(mode_reply);
44328705897Sccardenas 
44428705897Sccardenas 		ret = 1;
44528705897Sccardenas 		dev->cfg.isr_status = 1;
44628705897Sccardenas 		/* Move ring indexes */
44728705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
44828705897Sccardenas 		    acct->req_desc, acct->req_idx);
44928705897Sccardenas 	} else {
45028705897Sccardenas mode_sense_error:
45128705897Sccardenas 		/* send back un-supported */
45228705897Sccardenas 		vioscsi_prepare_resp(&resp,
45328705897Sccardenas 		    VIRTIO_SCSI_S_OK, SCSI_CHECK, SKEY_ILLEGAL_REQUEST,
45428705897Sccardenas 		    SENSE_ILLEGAL_CDB_FIELD, SENSE_DEFAULT_ASCQ);
45528705897Sccardenas 
45628705897Sccardenas 		/* Move index for response */
45728705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
45828705897Sccardenas 		    acct->req_desc, &(acct->resp_idx));
45928705897Sccardenas 
4600759b25cSdv 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
46128705897Sccardenas 			log_warnx("%s: unable to set ERR status data @ 0x%llx",
46228705897Sccardenas 			    __func__, acct->resp_desc->addr);
46328705897Sccardenas 			goto mode_sense_out;
46428705897Sccardenas 		}
46528705897Sccardenas 
46628705897Sccardenas 		ret = 1;
46728705897Sccardenas 		dev->cfg.isr_status = 1;
46828705897Sccardenas 		/* Move ring indexes */
46928705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
47028705897Sccardenas 		    acct->req_desc, acct->req_idx);
47128705897Sccardenas 	}
47228705897Sccardenas mode_sense_out:
47328705897Sccardenas 	return (ret);
47428705897Sccardenas }
47528705897Sccardenas 
47628705897Sccardenas static int
47728705897Sccardenas vioscsi_handle_mode_sense_big(struct vioscsi_dev *dev,
47828705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
47928705897Sccardenas {
48028705897Sccardenas 	int ret = 0;
48128705897Sccardenas 	struct virtio_scsi_res_hdr resp;
48228705897Sccardenas 	uint8_t mode_page_ctl;
48328705897Sccardenas 	uint8_t mode_page_code;
48428705897Sccardenas 	uint8_t *mode_reply;
4850759b25cSdv 	uint8_t mode_reply_len = 0;
48628705897Sccardenas 	struct scsi_mode_sense_big *mode_sense_10;
48728705897Sccardenas 
48828705897Sccardenas 	memset(&resp, 0, sizeof(resp));
48928705897Sccardenas 	mode_sense_10 = (struct scsi_mode_sense_big *)(req->cdb);
49028705897Sccardenas 	mode_page_ctl = mode_sense_10->page & SMS_PAGE_CTRL;
49128705897Sccardenas 	mode_page_code = mode_sense_10->page & SMS_PAGE_CODE;
49228705897Sccardenas 
4932d03c861Sccardenas 	DPRINTF("%s: M_SENSE_10 - DBD %d Page Ctrl 0x%x Code 0x%x Len %u",
49428705897Sccardenas 	    __func__, mode_sense_10->byte2 & SMS_DBD, mode_page_ctl,
49530dd31d2Sdv 	    mode_page_code, (uint16_t)_2btol(mode_sense_10->length));
49628705897Sccardenas 
49728705897Sccardenas 	if (mode_page_ctl == SMS_PAGE_CTRL_CURRENT &&
49828705897Sccardenas 	    (mode_page_code == ERR_RECOVERY_PAGE ||
49928705897Sccardenas 	    mode_page_code == CDVD_CAPABILITIES_PAGE)) {
50028705897Sccardenas 		/*
50128705897Sccardenas 		 * mode sense header is 8 bytes followed
50228705897Sccardenas 		 * by a variable page
50328705897Sccardenas 		 * ERR_RECOVERY_PAGE is 12 bytes
5040759b25cSdv 		 * CDVD_CAPABILITIES_PAGE is 32 bytes
50528705897Sccardenas 		 */
50628705897Sccardenas 		switch (mode_page_code) {
50728705897Sccardenas 		case ERR_RECOVERY_PAGE:
50828705897Sccardenas 			mode_reply_len = 20;
50928705897Sccardenas 			mode_reply =
51028705897Sccardenas 			    (uint8_t*)calloc(mode_reply_len, sizeof(uint8_t));
51128705897Sccardenas 			if (mode_reply == NULL)
51228705897Sccardenas 				goto mode_sense_big_out;
51328705897Sccardenas 
51428705897Sccardenas 			/* set the page header */
51528705897Sccardenas 			_lto2b(mode_reply_len - 2, mode_reply);
51628705897Sccardenas 			*(mode_reply + 2) = MODE_MEDIUM_TYPE_CODE;
51728705897Sccardenas 
51828705897Sccardenas 			/* set the page data, 7.3.2.1 mmc-5 */
51928705897Sccardenas 			*(mode_reply + 8) = MODE_ERR_RECOVERY_PAGE_CODE;
52028705897Sccardenas 			*(mode_reply + 9) = MODE_ERR_RECOVERY_PAGE_LEN;
52128705897Sccardenas 			*(mode_reply + 11) = MODE_READ_RETRY_COUNT;
52228705897Sccardenas 			break;
52328705897Sccardenas 		case CDVD_CAPABILITIES_PAGE:
5240759b25cSdv 			mode_reply_len = 40;
52528705897Sccardenas 			mode_reply =
52628705897Sccardenas 			    (uint8_t*)calloc(mode_reply_len, sizeof(uint8_t));
52728705897Sccardenas 			if (mode_reply == NULL)
52828705897Sccardenas 				goto mode_sense_big_out;
52928705897Sccardenas 
53028705897Sccardenas 			/* set the page header */
53128705897Sccardenas 			_lto2b(mode_reply_len - 2, mode_reply);
53228705897Sccardenas 			*(mode_reply + 2) = MODE_MEDIUM_TYPE_CODE;
53328705897Sccardenas 
53428705897Sccardenas 			/* set the page data, 6.3.11 mmc-3 */
53528705897Sccardenas 			*(mode_reply + 8) = MODE_CDVD_CAP_PAGE_CODE;
53628705897Sccardenas 			*(mode_reply + 9) = mode_reply_len - 6;
53728705897Sccardenas 			*(mode_reply + 10) = MODE_CDVD_CAP_READ_CODE;
53828705897Sccardenas 			_lto2b(MODE_CDVD_CAP_NUM_LEVELS, mode_reply + 18);
53928705897Sccardenas 			break;
54028705897Sccardenas 		default:
54128705897Sccardenas 			goto mode_sense_big_error;
54228705897Sccardenas 			break;
54328705897Sccardenas 		}
54428705897Sccardenas 
54528705897Sccardenas 		/* Move index for response */
54628705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
54728705897Sccardenas 		    acct->req_desc, &(acct->resp_idx));
54828705897Sccardenas 
5491f5e00e0Sreyk 		DPRINTF("%s: writing resp to 0x%llx size %d "
55028705897Sccardenas 		    "at local idx %d req_idx %d global_idx %d",
55128705897Sccardenas 		    __func__, acct->resp_desc->addr, acct->resp_desc->len,
55228705897Sccardenas 		    acct->resp_idx, acct->req_idx, acct->idx);
55328705897Sccardenas 
5540759b25cSdv 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
55528705897Sccardenas 			log_warnx("%s: unable to write OK"
55628705897Sccardenas 			    " resp status data @ 0x%llx",
55728705897Sccardenas 			    __func__, acct->resp_desc->addr);
55828705897Sccardenas 			free(mode_reply);
55928705897Sccardenas 			goto mode_sense_big_out;
56028705897Sccardenas 		}
56128705897Sccardenas 
56228705897Sccardenas 		/* Move index for mode_reply */
56328705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
56428705897Sccardenas 		    acct->resp_desc, &(acct->resp_idx));
56528705897Sccardenas 
5661f5e00e0Sreyk 		DPRINTF("%s: writing mode_reply to 0x%llx "
56728705897Sccardenas 		    "size %d at local idx %d req_idx %d global_idx %d",
5680759b25cSdv 		    __func__, acct->resp_desc->addr, mode_reply_len,
56928705897Sccardenas 		    acct->resp_idx, acct->req_idx, acct->idx);
57028705897Sccardenas 
57128705897Sccardenas 		if (write_mem(acct->resp_desc->addr, mode_reply,
5720759b25cSdv 			mode_reply_len)) {
57328705897Sccardenas 			log_warnx("%s: unable to write "
57428705897Sccardenas 			    "mode_reply to gpa @ 0x%llx",
57528705897Sccardenas 			    __func__, acct->resp_desc->addr);
57628705897Sccardenas 			free(mode_reply);
57728705897Sccardenas 			goto mode_sense_big_out;
57828705897Sccardenas 		}
57928705897Sccardenas 
58028705897Sccardenas 		free(mode_reply);
58128705897Sccardenas 
58228705897Sccardenas 		ret = 1;
58328705897Sccardenas 		dev->cfg.isr_status = 1;
58428705897Sccardenas 		/* Move ring indexes */
58528705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
58628705897Sccardenas 		    acct->req_desc, acct->req_idx);
58728705897Sccardenas 	} else {
58828705897Sccardenas mode_sense_big_error:
58928705897Sccardenas 		/* send back un-supported */
59028705897Sccardenas 		vioscsi_prepare_resp(&resp,
59128705897Sccardenas 		    VIRTIO_SCSI_S_OK, SCSI_CHECK, SKEY_ILLEGAL_REQUEST,
59228705897Sccardenas 		    SENSE_ILLEGAL_CDB_FIELD, SENSE_DEFAULT_ASCQ);
59328705897Sccardenas 
59428705897Sccardenas 		/* Move index for response */
59528705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
59628705897Sccardenas 		    acct->req_desc, &(acct->resp_idx));
59728705897Sccardenas 
5980759b25cSdv 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
59928705897Sccardenas 			log_warnx("%s: unable to set ERR status data @ 0x%llx",
60028705897Sccardenas 			    __func__, acct->resp_desc->addr);
60128705897Sccardenas 			goto mode_sense_big_out;
60228705897Sccardenas 		}
60328705897Sccardenas 
60428705897Sccardenas 		ret = 1;
60528705897Sccardenas 		dev->cfg.isr_status = 1;
60628705897Sccardenas 		/* Move ring indexes */
60728705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
60828705897Sccardenas 		    acct->req_desc, acct->req_idx);
60928705897Sccardenas 	}
61028705897Sccardenas mode_sense_big_out:
61128705897Sccardenas 	return (ret);
61228705897Sccardenas }
61328705897Sccardenas 
61428705897Sccardenas static int
61528705897Sccardenas vioscsi_handle_read_capacity(struct vioscsi_dev *dev,
61628705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
61728705897Sccardenas {
61828705897Sccardenas 	int ret = 0;
61928705897Sccardenas 	struct virtio_scsi_res_hdr resp;
62028705897Sccardenas 	struct scsi_read_cap_data *r_cap_data;
62128705897Sccardenas 
62230dd31d2Sdv #if DEBUG
62330dd31d2Sdv 	struct scsi_read_capacity *r_cap =
62430dd31d2Sdv 	    (struct scsi_read_capacity *)(req->cdb);
62530dd31d2Sdv 	log_debug("%s: %s - Addr 0x%08x", __func__,
62630dd31d2Sdv 	    vioscsi_op_names(r_cap->opcode), _4btol(r_cap->addr));
62730dd31d2Sdv #endif /* DEBUG */
62828705897Sccardenas 
62930dd31d2Sdv 	memset(&resp, 0, sizeof(resp));
63028705897Sccardenas 	vioscsi_prepare_resp(&resp,
63128705897Sccardenas 	    VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
63228705897Sccardenas 
63328705897Sccardenas 	r_cap_data = calloc(1, sizeof(struct scsi_read_cap_data));
63428705897Sccardenas 
63528705897Sccardenas 	if (r_cap_data == NULL) {
63628705897Sccardenas 		log_warnx("%s: cannot alloc r_cap_data", __func__);
63728705897Sccardenas 		goto read_capacity_out;
63828705897Sccardenas 	}
63928705897Sccardenas 
6402d03c861Sccardenas 	DPRINTF("%s: ISO has %lld bytes and %lld blocks",
64128705897Sccardenas 	    __func__, dev->sz, dev->n_blocks);
64228705897Sccardenas 
64328705897Sccardenas 	/*
644a9eba918Sccardenas 	 * determine if num blocks of iso image > UINT32_MAX
64528705897Sccardenas 	 * if it is, set addr to UINT32_MAX (0xffffffff)
64628705897Sccardenas 	 * indicating to hosts that READ_CAPACITY_16 should
64728705897Sccardenas 	 * be called to retrieve the full size
64828705897Sccardenas 	 */
649a9eba918Sccardenas 	if (dev->n_blocks >= UINT32_MAX) {
65028705897Sccardenas 		_lto4b(UINT32_MAX, r_cap_data->addr);
65128705897Sccardenas 		_lto4b(VIOSCSI_BLOCK_SIZE_CDROM, r_cap_data->length);
65228705897Sccardenas 		log_warnx("%s: ISO sz %lld is bigger than "
65328705897Sccardenas 		    "UINT32_MAX %u, all data may not be read",
65428705897Sccardenas 		    __func__, dev->sz, UINT32_MAX);
65528705897Sccardenas 	} else {
65628705897Sccardenas 		_lto4b(dev->n_blocks - 1, r_cap_data->addr);
65728705897Sccardenas 		_lto4b(VIOSCSI_BLOCK_SIZE_CDROM, r_cap_data->length);
65828705897Sccardenas 	}
65928705897Sccardenas 
66028705897Sccardenas 	/* Move index for response */
66128705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
66228705897Sccardenas 	    &(acct->resp_idx));
66328705897Sccardenas 
6641f5e00e0Sreyk 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
66528705897Sccardenas 	    "idx %d req_idx %d global_idx %d",
66628705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
66728705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
66828705897Sccardenas 
6690759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
67028705897Sccardenas 		log_warnx("%s: unable to write OK resp status data @ 0x%llx",
67128705897Sccardenas 		    __func__, acct->resp_desc->addr);
67228705897Sccardenas 		goto free_read_capacity;
67328705897Sccardenas 	}
67428705897Sccardenas 
67528705897Sccardenas 	/* Move index for r_cap_data */
67628705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
67728705897Sccardenas 	    &(acct->resp_idx));
67828705897Sccardenas 
6791f5e00e0Sreyk 	DPRINTF("%s: writing r_cap_data to 0x%llx size %d at "
68028705897Sccardenas 	    "local idx %d req_idx %d global_idx %d",
68128705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
68228705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
68328705897Sccardenas 
68428705897Sccardenas 	if (write_mem(acct->resp_desc->addr, r_cap_data,
6850759b25cSdv 		sizeof(struct scsi_read_cap_data))) {
68628705897Sccardenas 		log_warnx("%s: unable to write read_cap_data"
68728705897Sccardenas 		    " response to gpa @ 0x%llx",
68828705897Sccardenas 		    __func__, acct->resp_desc->addr);
68928705897Sccardenas 	} else {
69028705897Sccardenas 		ret = 1;
69128705897Sccardenas 		dev->cfg.isr_status = 1;
69228705897Sccardenas 		/* Move ring indexes */
69328705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
69428705897Sccardenas 		    acct->req_desc, acct->req_idx);
69528705897Sccardenas 	}
69628705897Sccardenas 
69728705897Sccardenas free_read_capacity:
69828705897Sccardenas 	free(r_cap_data);
69928705897Sccardenas read_capacity_out:
70028705897Sccardenas 	return (ret);
70128705897Sccardenas }
70228705897Sccardenas 
70328705897Sccardenas static int
70428705897Sccardenas vioscsi_handle_read_capacity_16(struct vioscsi_dev *dev,
70528705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
70628705897Sccardenas {
70728705897Sccardenas 	int ret = 0;
70828705897Sccardenas 	struct virtio_scsi_res_hdr resp;
70928705897Sccardenas 	struct scsi_read_cap_data_16 *r_cap_data_16;
71028705897Sccardenas 
71130dd31d2Sdv #if DEBUG
71230dd31d2Sdv 	struct scsi_read_capacity_16 *r_cap_16 =
71330dd31d2Sdv 	    (struct scsi_read_capacity_16 *)(req->cdb);
71430dd31d2Sdv 	log_debug("%s: %s - Addr 0x%016llx", __func__,
71530dd31d2Sdv 	    vioscsi_op_names(r_cap_16->opcode), _8btol(r_cap_16->addr));
71630dd31d2Sdv #endif /* DEBUG */
71728705897Sccardenas 
71830dd31d2Sdv 	memset(&resp, 0, sizeof(resp));
71928705897Sccardenas 	vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
72028705897Sccardenas 
72128705897Sccardenas 	r_cap_data_16 = calloc(1, sizeof(struct scsi_read_cap_data_16));
72228705897Sccardenas 
72328705897Sccardenas 	if (r_cap_data_16 == NULL) {
72428705897Sccardenas 		log_warnx("%s: cannot alloc r_cap_data_16",
72528705897Sccardenas 		    __func__);
72628705897Sccardenas 		goto read_capacity_16_out;
72728705897Sccardenas 	}
72828705897Sccardenas 
7292d03c861Sccardenas 	DPRINTF("%s: ISO has %lld bytes and %lld blocks", __func__,
73028705897Sccardenas 	    dev->sz, dev->n_blocks);
73128705897Sccardenas 
73228705897Sccardenas 	_lto8b(dev->n_blocks - 1, r_cap_data_16->addr);
73328705897Sccardenas 	_lto4b(VIOSCSI_BLOCK_SIZE_CDROM, r_cap_data_16->length);
73428705897Sccardenas 
73528705897Sccardenas 	/* Move index for response */
73628705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
73728705897Sccardenas 	    &(acct->resp_idx));
73828705897Sccardenas 
7391f5e00e0Sreyk 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
74028705897Sccardenas 	    "idx %d req_idx %d global_idx %d",
74128705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
74228705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
74328705897Sccardenas 
7440759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
74528705897Sccardenas 		log_warnx("%s: unable to write OK resp status "
74628705897Sccardenas 		    "data @ 0x%llx", __func__, acct->resp_desc->addr);
74728705897Sccardenas 		goto free_read_capacity_16;
74828705897Sccardenas 	}
74928705897Sccardenas 
75028705897Sccardenas 	/* Move index for r_cap_data_16 */
75128705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
75228705897Sccardenas 	    &(acct->resp_idx));
75328705897Sccardenas 
7541f5e00e0Sreyk 	DPRINTF("%s: writing r_cap_data_16 to 0x%llx size %d "
75528705897Sccardenas 	    "at local idx %d req_idx %d global_idx %d",
75628705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
75728705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
75828705897Sccardenas 
75928705897Sccardenas 	if (write_mem(acct->resp_desc->addr, r_cap_data_16,
7600759b25cSdv 		sizeof(struct scsi_read_cap_data_16))) {
76128705897Sccardenas 		log_warnx("%s: unable to write read_cap_data_16"
76228705897Sccardenas 		    " response to gpa @ 0x%llx",
76328705897Sccardenas 		    __func__, acct->resp_desc->addr);
76428705897Sccardenas 	} else {
76528705897Sccardenas 		ret = 1;
76628705897Sccardenas 		dev->cfg.isr_status = 1;
76728705897Sccardenas 		/* Move ring indexes */
76828705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
76928705897Sccardenas 		    acct->req_desc, acct->req_idx);
77028705897Sccardenas 	}
77128705897Sccardenas 
77228705897Sccardenas free_read_capacity_16:
77328705897Sccardenas 	free(r_cap_data_16);
77428705897Sccardenas read_capacity_16_out:
77528705897Sccardenas 	return (ret);
77628705897Sccardenas }
77728705897Sccardenas 
77828705897Sccardenas static int
779845b4456Sccardenas vioscsi_handle_report_luns(struct vioscsi_dev *dev,
780845b4456Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
781845b4456Sccardenas {
782845b4456Sccardenas 	int ret = 0;
783845b4456Sccardenas 	struct virtio_scsi_res_hdr resp;
784845b4456Sccardenas 	uint32_t rpl_length;
785845b4456Sccardenas 	struct scsi_report_luns *rpl;
786845b4456Sccardenas 	struct vioscsi_report_luns_data *reply_rpl;
787845b4456Sccardenas 
788845b4456Sccardenas 	memset(&resp, 0, sizeof(resp));
789845b4456Sccardenas 	rpl = (struct scsi_report_luns *)(req->cdb);
790845b4456Sccardenas 	rpl_length = _4btol(rpl->length);
791845b4456Sccardenas 
7922d03c861Sccardenas 	DPRINTF("%s: REPORT_LUNS Report 0x%x Length %d", __func__,
793845b4456Sccardenas 	    rpl->selectreport, rpl_length);
794845b4456Sccardenas 
795845b4456Sccardenas 	if (rpl_length < RPL_MIN_SIZE) {
7962d03c861Sccardenas 		DPRINTF("%s: RPL_Length %d < %d (RPL_MIN_SIZE)", __func__,
797845b4456Sccardenas 		    rpl_length, RPL_MIN_SIZE);
798845b4456Sccardenas 
799845b4456Sccardenas 		vioscsi_prepare_resp(&resp,
800845b4456Sccardenas 		    VIRTIO_SCSI_S_OK, SCSI_CHECK, SKEY_ILLEGAL_REQUEST,
801845b4456Sccardenas 		    SENSE_ILLEGAL_CDB_FIELD, SENSE_DEFAULT_ASCQ);
802845b4456Sccardenas 
803845b4456Sccardenas 		/* Move index for response */
804845b4456Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
805845b4456Sccardenas 		    acct->req_desc, &(acct->resp_idx));
806845b4456Sccardenas 
8070759b25cSdv 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
808845b4456Sccardenas 			log_warnx("%s: unable to set ERR "
809845b4456Sccardenas 			    "status data @ 0x%llx", __func__,
810845b4456Sccardenas 			    acct->resp_desc->addr);
811845b4456Sccardenas 		} else {
812845b4456Sccardenas 			ret = 1;
813845b4456Sccardenas 			dev->cfg.isr_status = 1;
814845b4456Sccardenas 			/* Move ring indexes */
815845b4456Sccardenas 			vioscsi_next_ring_item(dev, acct->avail, acct->used,
816845b4456Sccardenas 			    acct->req_desc, acct->req_idx);
817845b4456Sccardenas 		}
818845b4456Sccardenas 		goto rpl_out;
819845b4456Sccardenas 
820845b4456Sccardenas 	}
821845b4456Sccardenas 
8220759b25cSdv 	reply_rpl = calloc(1, sizeof(struct vioscsi_report_luns_data));
823845b4456Sccardenas 
824845b4456Sccardenas 	if (reply_rpl == NULL) {
825845b4456Sccardenas 		log_warnx("%s: cannot alloc reply_rpl", __func__);
826845b4456Sccardenas 		goto rpl_out;
827845b4456Sccardenas 	}
828845b4456Sccardenas 
829845b4456Sccardenas 	_lto4b(RPL_SINGLE_LUN, reply_rpl->length);
830845b4456Sccardenas 	memcpy(reply_rpl->lun, req->lun, RPL_SINGLE_LUN);
831845b4456Sccardenas 
832845b4456Sccardenas 	vioscsi_prepare_resp(&resp,
833845b4456Sccardenas 	    VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
834845b4456Sccardenas 
835845b4456Sccardenas 	/* Move index for response */
836845b4456Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
837845b4456Sccardenas 	    &(acct->resp_idx));
838845b4456Sccardenas 
8391f5e00e0Sreyk 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
840845b4456Sccardenas 	    "idx %d req_idx %d global_idx %d", __func__, acct->resp_desc->addr,
841845b4456Sccardenas 	    acct->resp_desc->len, acct->resp_idx, acct->req_idx, acct->idx);
842845b4456Sccardenas 
8430759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
844845b4456Sccardenas 		log_warnx("%s: unable to write OK resp status data @ 0x%llx",
845845b4456Sccardenas 		    __func__, acct->resp_desc->addr);
846845b4456Sccardenas 		goto free_rpl;
847845b4456Sccardenas 	}
848845b4456Sccardenas 
849845b4456Sccardenas 	/* Move index for reply_rpl */
850845b4456Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
851845b4456Sccardenas 	    &(acct->resp_idx));
852845b4456Sccardenas 
8531f5e00e0Sreyk 	DPRINTF("%s: writing reply_rpl to 0x%llx size %d at "
854845b4456Sccardenas 	    "local idx %d req_idx %d global_idx %d",
855845b4456Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
856845b4456Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
857845b4456Sccardenas 
8580759b25cSdv 	if (write_mem(acct->resp_desc->addr, reply_rpl,
8590759b25cSdv 		sizeof(struct vioscsi_report_luns_data))) {
860845b4456Sccardenas 		log_warnx("%s: unable to write reply_rpl"
861845b4456Sccardenas 		    " response to gpa @ 0x%llx",
862845b4456Sccardenas 		    __func__, acct->resp_desc->addr);
863845b4456Sccardenas 	} else {
864845b4456Sccardenas 		ret = 1;
865845b4456Sccardenas 		dev->cfg.isr_status = 1;
866845b4456Sccardenas 		/* Move ring indexes */
867845b4456Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
868845b4456Sccardenas 		    acct->req_desc, acct->req_idx);
869845b4456Sccardenas 	}
870845b4456Sccardenas 
871845b4456Sccardenas free_rpl:
872845b4456Sccardenas 	free(reply_rpl);
873845b4456Sccardenas rpl_out:
874845b4456Sccardenas 	return (ret);
875845b4456Sccardenas }
876845b4456Sccardenas 
877845b4456Sccardenas static int
87828705897Sccardenas vioscsi_handle_read_6(struct vioscsi_dev *dev,
87928705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
88028705897Sccardenas {
88128705897Sccardenas 	int ret = 0;
88228705897Sccardenas 	struct virtio_scsi_res_hdr resp;
88328705897Sccardenas 	const uint8_t *read_buf;
88428705897Sccardenas 	uint32_t read_lba;
88528705897Sccardenas 	struct ioinfo *info;
88628705897Sccardenas 	struct scsi_rw *read_6;
88728705897Sccardenas 
88828705897Sccardenas 	memset(&resp, 0, sizeof(resp));
88928705897Sccardenas 	read_6 = (struct scsi_rw *)(req->cdb);
89028705897Sccardenas 	read_lba = ((read_6->addr[0] & SRW_TOPADDR) << 16 ) |
89128705897Sccardenas 	    (read_6->addr[1] << 8) | read_6->addr[2];
89228705897Sccardenas 
8932d03c861Sccardenas 	DPRINTF("%s: READ Addr 0x%08x Len %d (%d)",
89428705897Sccardenas 	    __func__, read_lba, read_6->length, read_6->length * dev->max_xfer);
89528705897Sccardenas 
89628705897Sccardenas 	/* check if lba is in range */
89728705897Sccardenas 	if (read_lba > dev->n_blocks - 1) {
8982d03c861Sccardenas 		DPRINTF("%s: requested block out of range req: %ud max: %lld",
89928705897Sccardenas 		    __func__, read_lba, dev->n_blocks);
90028705897Sccardenas 
90128705897Sccardenas 		vioscsi_prepare_resp(&resp,
90228705897Sccardenas 		    VIRTIO_SCSI_S_OK, SCSI_CHECK, SKEY_ILLEGAL_REQUEST,
90328705897Sccardenas 		    SENSE_LBA_OUT_OF_RANGE, SENSE_DEFAULT_ASCQ);
90428705897Sccardenas 
90528705897Sccardenas 		/* Move index for response */
90628705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
90728705897Sccardenas 		    acct->req_desc, &(acct->resp_idx));
90828705897Sccardenas 
9090759b25cSdv 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
91028705897Sccardenas 			log_warnx("%s: unable to set ERR "
91128705897Sccardenas 			    "status data @ 0x%llx", __func__,
91228705897Sccardenas 			    acct->resp_desc->addr);
91328705897Sccardenas 		} else {
91428705897Sccardenas 			ret = 1;
91528705897Sccardenas 			dev->cfg.isr_status = 1;
91628705897Sccardenas 			/* Move ring indexes */
91728705897Sccardenas 			vioscsi_next_ring_item(dev, acct->avail, acct->used,
91828705897Sccardenas 			    acct->req_desc, acct->req_idx);
91928705897Sccardenas 		}
92028705897Sccardenas 		goto read_6_out;
92128705897Sccardenas 	}
92228705897Sccardenas 
92328705897Sccardenas 	info = vioscsi_start_read(dev, read_lba, read_6->length);
92428705897Sccardenas 
92528705897Sccardenas 	if (info == NULL) {
92628705897Sccardenas 		log_warnx("%s: cannot alloc for read", __func__);
92728705897Sccardenas 		goto read_6_out;
92828705897Sccardenas 	}
92928705897Sccardenas 
93028705897Sccardenas 	/* read block */
93128705897Sccardenas 	read_buf = vioscsi_finish_read(info);
93228705897Sccardenas 
93328705897Sccardenas 	if (read_buf == NULL) {
93428705897Sccardenas 		log_warnx("%s: error reading position %ud",
93528705897Sccardenas 		    __func__, read_lba);
93628705897Sccardenas 		vioscsi_prepare_resp(&resp,
93728705897Sccardenas 		    VIRTIO_SCSI_S_OK, SCSI_CHECK, SKEY_MEDIUM_ERROR,
93828705897Sccardenas 		    SENSE_MEDIUM_NOT_PRESENT, SENSE_DEFAULT_ASCQ);
93928705897Sccardenas 
94028705897Sccardenas 		/* Move index for response */
94128705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
94228705897Sccardenas 		    acct->req_desc, &(acct->resp_idx));
94328705897Sccardenas 
9440759b25cSdv 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
94528705897Sccardenas 			log_warnx("%s: unable to set ERR "
94628705897Sccardenas 			    "status data @ 0x%llx", __func__,
94728705897Sccardenas 			    acct->resp_desc->addr);
94828705897Sccardenas 		} else {
94928705897Sccardenas 			ret = 1;
95028705897Sccardenas 			dev->cfg.isr_status = 1;
95128705897Sccardenas 			/* Move ring indexes */
95228705897Sccardenas 			vioscsi_next_ring_item(dev, acct->avail, acct->used,
95328705897Sccardenas 			    acct->req_desc, acct->req_idx);
95428705897Sccardenas 		}
95528705897Sccardenas 
95628705897Sccardenas 		goto free_read_6;
95728705897Sccardenas 	}
95828705897Sccardenas 
95928705897Sccardenas 	vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
96028705897Sccardenas 
96128705897Sccardenas 	/* Move index for response */
96228705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
96328705897Sccardenas 	    &(acct->resp_idx));
96428705897Sccardenas 
9651f5e00e0Sreyk 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
96628705897Sccardenas 	    "idx %d req_idx %d global_idx %d",
96728705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
96828705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
96928705897Sccardenas 
9700759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
97128705897Sccardenas 		log_warnx("%s: unable to write OK resp status "
97228705897Sccardenas 		    "data @ 0x%llx", __func__, acct->resp_desc->addr);
97328705897Sccardenas 		goto free_read_6;
97428705897Sccardenas 	}
97528705897Sccardenas 
97628705897Sccardenas 	/* Move index for read_buf */
97728705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
97828705897Sccardenas 	    &(acct->resp_idx));
97928705897Sccardenas 
9801f5e00e0Sreyk 	DPRINTF("%s: writing read_buf to 0x%llx size %d at "
98128705897Sccardenas 	    "local idx %d req_idx %d global_idx %d",
98228705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
98328705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
98428705897Sccardenas 
9850759b25cSdv 	if (write_mem(acct->resp_desc->addr, read_buf, info->len)) {
98628705897Sccardenas 		log_warnx("%s: unable to write read_buf to gpa @ 0x%llx",
98728705897Sccardenas 		    __func__, acct->resp_desc->addr);
98828705897Sccardenas 	} else {
98928705897Sccardenas 		ret = 1;
99028705897Sccardenas 		dev->cfg.isr_status = 1;
99128705897Sccardenas 		/* Move ring indexes */
99228705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
99328705897Sccardenas 		    acct->req_desc, acct->req_idx);
99428705897Sccardenas 	}
99528705897Sccardenas 
99628705897Sccardenas free_read_6:
99728705897Sccardenas 	vioscsi_free_info(info);
99828705897Sccardenas read_6_out:
99928705897Sccardenas 	return (ret);
100028705897Sccardenas }
100128705897Sccardenas 
100228705897Sccardenas static int
100328705897Sccardenas vioscsi_handle_read_10(struct vioscsi_dev *dev,
100428705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
100528705897Sccardenas {
100628705897Sccardenas 	int ret = 0;
100728705897Sccardenas 	struct virtio_scsi_res_hdr resp;
100828705897Sccardenas 	const uint8_t *read_buf;
100928705897Sccardenas 	uint32_t read_lba;
101028705897Sccardenas 	uint16_t read_10_len;
101128705897Sccardenas 	off_t chunk_offset;
101228705897Sccardenas 	struct ioinfo *info;
1013eccd596dSkrw 	struct scsi_rw_10 *read_10;
10140759b25cSdv 	size_t chunk_len = 0;
101528705897Sccardenas 
101628705897Sccardenas 	memset(&resp, 0, sizeof(resp));
1017eccd596dSkrw 	read_10 = (struct scsi_rw_10 *)(req->cdb);
101828705897Sccardenas 	read_lba = _4btol(read_10->addr);
101928705897Sccardenas 	read_10_len = _2btol(read_10->length);
102028705897Sccardenas 	chunk_offset = 0;
102128705897Sccardenas 
10222d03c861Sccardenas 	DPRINTF("%s: READ_10 Addr 0x%08x Len %d (%d)",
102328705897Sccardenas 	    __func__, read_lba, read_10_len, read_10_len * dev->max_xfer);
102428705897Sccardenas 
102528705897Sccardenas 	/* check if lba is in range */
102628705897Sccardenas 	if (read_lba > dev->n_blocks - 1) {
10272d03c861Sccardenas 		DPRINTF("%s: requested block out of range req: %ud max: %lld",
102828705897Sccardenas 		    __func__, read_lba, dev->n_blocks);
102928705897Sccardenas 
103028705897Sccardenas 		vioscsi_prepare_resp(&resp,
103128705897Sccardenas 		    VIRTIO_SCSI_S_OK, SCSI_CHECK, SKEY_ILLEGAL_REQUEST,
103228705897Sccardenas 		    SENSE_LBA_OUT_OF_RANGE, SENSE_DEFAULT_ASCQ);
103328705897Sccardenas 
103428705897Sccardenas 		/* Move index for response */
103528705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
103628705897Sccardenas 		    acct->req_desc, &(acct->resp_idx));
103728705897Sccardenas 
10380759b25cSdv 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
103928705897Sccardenas 			log_warnx("%s: unable to set ERR status data @ 0x%llx",
104028705897Sccardenas 			    __func__, acct->resp_desc->addr);
104128705897Sccardenas 		} else {
104228705897Sccardenas 			ret = 1;
104328705897Sccardenas 			dev->cfg.isr_status = 1;
104428705897Sccardenas 			/* Move ring indexes */
104528705897Sccardenas 			vioscsi_next_ring_item(dev, acct->avail, acct->used,
104628705897Sccardenas 			    acct->req_desc, acct->req_idx);
104728705897Sccardenas 		}
104828705897Sccardenas 
104928705897Sccardenas 		goto read_10_out;
105028705897Sccardenas 	}
105128705897Sccardenas 
105228705897Sccardenas 	info = vioscsi_start_read(dev, read_lba, read_10_len);
105328705897Sccardenas 
105428705897Sccardenas 	if (info == NULL) {
105528705897Sccardenas 		log_warnx("%s: cannot alloc for read", __func__);
105628705897Sccardenas 		goto read_10_out;
105728705897Sccardenas 	}
105828705897Sccardenas 
105928705897Sccardenas 	/* read block */
106028705897Sccardenas 	read_buf = vioscsi_finish_read(info);
106128705897Sccardenas 
106228705897Sccardenas 	if (read_buf == NULL) {
106328705897Sccardenas 		log_warnx("%s: error reading position %ud", __func__, read_lba);
106428705897Sccardenas 		vioscsi_prepare_resp(&resp,
106528705897Sccardenas 		    VIRTIO_SCSI_S_OK, SCSI_CHECK, SKEY_MEDIUM_ERROR,
106628705897Sccardenas 		    SENSE_MEDIUM_NOT_PRESENT, SENSE_DEFAULT_ASCQ);
106728705897Sccardenas 
106828705897Sccardenas 		/* Move index for response */
106928705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
107028705897Sccardenas 		    acct->req_desc, &(acct->resp_idx));
107128705897Sccardenas 
10720759b25cSdv 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
107328705897Sccardenas 			log_warnx("%s: unable to set ERR status data @ 0x%llx",
107428705897Sccardenas 			    __func__, acct->resp_desc->addr);
107528705897Sccardenas 		} else {
107628705897Sccardenas 			ret = 1;
107728705897Sccardenas 			dev->cfg.isr_status = 1;
107828705897Sccardenas 			/* Move ring indexes */
107928705897Sccardenas 			vioscsi_next_ring_item(dev, acct->avail, acct->used,
108028705897Sccardenas 			    acct->req_desc, acct->req_idx);
108128705897Sccardenas 		}
108228705897Sccardenas 
108328705897Sccardenas 		goto free_read_10;
108428705897Sccardenas 	}
108528705897Sccardenas 
108628705897Sccardenas 	vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
108728705897Sccardenas 
108828705897Sccardenas 	/* Move index for response */
108928705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
109028705897Sccardenas 	    &(acct->resp_idx));
109128705897Sccardenas 
10921f5e00e0Sreyk 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
109328705897Sccardenas 	    "idx %d req_idx %d global_idx %d",
109428705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
109528705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
109628705897Sccardenas 
10970759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
109828705897Sccardenas 		log_warnx("%s: unable to write OK resp status "
109928705897Sccardenas 		    "data @ 0x%llx", __func__, acct->resp_desc->addr);
110028705897Sccardenas 		goto free_read_10;
110128705897Sccardenas 	}
110228705897Sccardenas 
110328705897Sccardenas 	/*
110428705897Sccardenas 	 * Perform possible chunking of writes of read_buf
110528705897Sccardenas 	 * based on the segment length allocated by the host.
110628705897Sccardenas 	 * At least one write will be performed.
110728705897Sccardenas 	 * If chunk_offset == info->len, no more writes
110828705897Sccardenas 	 */
110928705897Sccardenas 	do {
111028705897Sccardenas 		/* Move index for read_buf */
111128705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
111228705897Sccardenas 		    acct->resp_desc, &(acct->resp_idx));
111328705897Sccardenas 
11141f5e00e0Sreyk 		DPRINTF("%s: writing read_buf to 0x%llx size "
111528705897Sccardenas 		    "%d at local idx %d req_idx %d global_idx %d",
111628705897Sccardenas 		    __func__, acct->resp_desc->addr, acct->resp_desc->len,
111728705897Sccardenas 		    acct->resp_idx, acct->req_idx, acct->idx);
111828705897Sccardenas 
11190759b25cSdv 		/* Check we don't read beyond read_buf boundaries. */
11200759b25cSdv 		if (acct->resp_desc->len > info->len - chunk_offset) {
11210759b25cSdv 			log_warnx("%s: descriptor length beyond read_buf len",
11220759b25cSdv 			    __func__);
11230759b25cSdv 			chunk_len = info->len - chunk_offset;
11240759b25cSdv 		} else
11250759b25cSdv 			chunk_len = acct->resp_desc->len;
11260759b25cSdv 
11270759b25cSdv 		if (write_mem(acct->resp_desc->addr, read_buf + chunk_offset,
11280759b25cSdv 			chunk_len)) {
112928705897Sccardenas 			log_warnx("%s: unable to write read_buf"
113028705897Sccardenas 			    " to gpa @ 0x%llx", __func__,
113128705897Sccardenas 			    acct->resp_desc->addr);
113228705897Sccardenas 			goto free_read_10;
113328705897Sccardenas 		}
113428705897Sccardenas 		chunk_offset += acct->resp_desc->len;
113528705897Sccardenas 	} while (chunk_offset < info->len);
113628705897Sccardenas 
113728705897Sccardenas 	ret = 1;
113828705897Sccardenas 	dev->cfg.isr_status = 1;
113928705897Sccardenas 	/* Move ring indexes */
114028705897Sccardenas 	vioscsi_next_ring_item(dev, acct->avail, acct->used, acct->req_desc,
114128705897Sccardenas 	    acct->req_idx);
114228705897Sccardenas 
114328705897Sccardenas free_read_10:
114428705897Sccardenas 	vioscsi_free_info(info);
114528705897Sccardenas read_10_out:
114628705897Sccardenas 	return (ret);
114728705897Sccardenas }
114828705897Sccardenas 
114928705897Sccardenas static int
115028705897Sccardenas vioscsi_handle_prevent_allow(struct vioscsi_dev *dev,
115128705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
115228705897Sccardenas {
115328705897Sccardenas 	int ret = 0;
115428705897Sccardenas 	struct virtio_scsi_res_hdr resp;
115528705897Sccardenas 
115628705897Sccardenas 	memset(&resp, 0, sizeof(resp));
115728705897Sccardenas 	/* Move index for response */
115828705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
115928705897Sccardenas 	    &(acct->resp_idx));
116028705897Sccardenas 
116128705897Sccardenas 	vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
116228705897Sccardenas 
116328705897Sccardenas 	if (dev->locked) {
11642d03c861Sccardenas 		DPRINTF("%s: unlocking medium", __func__);
116528705897Sccardenas 	} else {
11662d03c861Sccardenas 		DPRINTF("%s: locking medium", __func__);
116728705897Sccardenas 	}
116828705897Sccardenas 
116928705897Sccardenas 	dev->locked = dev->locked ? 0 : 1;
117028705897Sccardenas 
11710759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
117228705897Sccardenas 		log_warnx("%s: unable to write OK resp status data @ 0x%llx",
117328705897Sccardenas 		    __func__, acct->resp_desc->addr);
117428705897Sccardenas 	} else {
117528705897Sccardenas 		ret = 1;
117628705897Sccardenas 		dev->cfg.isr_status = 1;
117728705897Sccardenas 		/* Move ring indexes */
117828705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
117928705897Sccardenas 		    acct->req_desc, acct->req_idx);
118028705897Sccardenas 	}
118128705897Sccardenas 
118228705897Sccardenas 	return (ret);
118328705897Sccardenas }
118428705897Sccardenas 
118528705897Sccardenas static int
118628705897Sccardenas vioscsi_handle_mechanism_status(struct vioscsi_dev *dev,
118728705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
118828705897Sccardenas {
118928705897Sccardenas 	int ret = 0;
119028705897Sccardenas 	struct virtio_scsi_res_hdr resp;
119128705897Sccardenas 	struct scsi_mechanism_status_header *mech_status_header;
119228705897Sccardenas 
119330dd31d2Sdv 	DPRINTF("%s: MECH_STATUS Len %u", __func__,
119430dd31d2Sdv 	    _2btol(((struct scsi_mechanism_status *)(req->cdb))->length));
119528705897Sccardenas 
11960759b25cSdv 	mech_status_header = calloc(1,
11970759b25cSdv 	    sizeof(struct scsi_mechanism_status_header));
119828705897Sccardenas 
119928705897Sccardenas 	if (mech_status_header == NULL)
120028705897Sccardenas 		goto mech_out;
120128705897Sccardenas 
120228705897Sccardenas 	/* return a 0 header since we are not a changer */
120330dd31d2Sdv 	memset(&resp, 0, sizeof(resp));
120428705897Sccardenas 	vioscsi_prepare_resp(&resp,
120528705897Sccardenas 	    VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
120628705897Sccardenas 
120728705897Sccardenas 	/* Move index for response */
120828705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
120928705897Sccardenas 	    acct->req_desc, &(acct->resp_idx));
121028705897Sccardenas 
12110759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
121228705897Sccardenas 		log_warnx("%s: unable to set ERR status data @ 0x%llx",
121328705897Sccardenas 		    __func__, acct->resp_desc->addr);
121428705897Sccardenas 		goto free_mech;
121528705897Sccardenas 	}
121628705897Sccardenas 
121728705897Sccardenas 	/* Move index for mech_status_header */
121828705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
121928705897Sccardenas 	    &(acct->resp_idx));
122028705897Sccardenas 
122128705897Sccardenas 	if (write_mem(acct->resp_desc->addr, mech_status_header,
12220759b25cSdv 		sizeof(struct scsi_mechanism_status_header))) {
122328705897Sccardenas 		log_warnx("%s: unable to write "
122428705897Sccardenas 		    "mech_status_header response to "
122528705897Sccardenas 		    "gpa @ 0x%llx",
122628705897Sccardenas 		    __func__, acct->resp_desc->addr);
122728705897Sccardenas 	} else {
122828705897Sccardenas 		ret = 1;
122928705897Sccardenas 		dev->cfg.isr_status = 1;
123028705897Sccardenas 		/* Move ring indexes */
123128705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
123228705897Sccardenas 		    acct->req_desc, acct->req_idx);
123328705897Sccardenas 	}
123428705897Sccardenas 
123528705897Sccardenas free_mech:
123628705897Sccardenas 	free(mech_status_header);
123728705897Sccardenas mech_out:
123828705897Sccardenas 	return (ret);
123928705897Sccardenas }
124028705897Sccardenas 
124128705897Sccardenas static int
124228705897Sccardenas vioscsi_handle_read_toc(struct vioscsi_dev *dev,
124328705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
124428705897Sccardenas {
124528705897Sccardenas 	int ret = 0;
124628705897Sccardenas 	struct virtio_scsi_res_hdr resp;
124728705897Sccardenas 	uint16_t toc_data_len;
124828705897Sccardenas 	uint8_t toc_data[TOC_DATA_SIZE];
124928705897Sccardenas 	uint8_t *toc_data_p;
125030dd31d2Sdv 	struct scsi_read_toc *toc = (struct scsi_read_toc *)(req->cdb);
125128705897Sccardenas 
12522d03c861Sccardenas 	DPRINTF("%s: %s - MSF %d Track 0x%02x Addr 0x%04x",
125330dd31d2Sdv 	    __func__, vioscsi_op_names(toc->opcode), ((toc->byte2 >> 1) & 1),
125430dd31d2Sdv 	    toc->from_track, _2btol(toc->data_len));
125528705897Sccardenas 
125628705897Sccardenas 	/* Tracks should be 0, 1, or LEAD_OUT_TRACK, 0xaa */
125728705897Sccardenas 	if (toc->from_track > 1 &&
125828705897Sccardenas 	    toc->from_track != READ_TOC_LEAD_OUT_TRACK) {
125928705897Sccardenas 		/* illegal request */
12602d03c861Sccardenas 		log_warnx("%s: illegal request Track 0x%02x",
126128705897Sccardenas 		    __func__, toc->from_track);
126228705897Sccardenas 
126330dd31d2Sdv 		memset(&resp, 0, sizeof(resp));
126428705897Sccardenas 		vioscsi_prepare_resp(&resp,
126528705897Sccardenas 		    VIRTIO_SCSI_S_OK, SCSI_CHECK, SKEY_ILLEGAL_REQUEST,
126628705897Sccardenas 		    SENSE_ILLEGAL_CDB_FIELD, SENSE_DEFAULT_ASCQ);
126728705897Sccardenas 
126828705897Sccardenas 		/* Move index for response */
126928705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
127028705897Sccardenas 		    acct->req_desc, &(acct->resp_idx));
127128705897Sccardenas 
12720759b25cSdv 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
127328705897Sccardenas 			log_warnx("%s: unable to set ERR status data @ 0x%llx",
127428705897Sccardenas 			    __func__, acct->resp_desc->addr);
127528705897Sccardenas 			goto read_toc_out;
127628705897Sccardenas 		}
127728705897Sccardenas 
127828705897Sccardenas 		ret = 1;
127928705897Sccardenas 		dev->cfg.isr_status = 1;
128028705897Sccardenas 		/* Move ring indexes */
128128705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
128228705897Sccardenas 		    acct->req_desc, acct->req_idx);
128328705897Sccardenas 
128428705897Sccardenas 		goto read_toc_out;
128528705897Sccardenas 	}
128628705897Sccardenas 
128728705897Sccardenas 	/*
128828705897Sccardenas 	 * toc_data is defined as:
128928705897Sccardenas 	 * [0-1]: TOC Data Length, typically 0x1a
129028705897Sccardenas 	 * [2]: First Track, 1
129128705897Sccardenas 	 * [3]: Last Track, 1
129228705897Sccardenas 	 *
129328705897Sccardenas 	 * Track 1 Descriptor
129428705897Sccardenas 	 * [0]: Reserved, 0
129528705897Sccardenas 	 * [1]: ADR,Control, 0x14
129628705897Sccardenas 	 * [2]: Track #, 1
129728705897Sccardenas 	 * [3]: Reserved, 0
129828705897Sccardenas 	 * [4-7]: Track Start Address, LBA
129928705897Sccardenas 	 *
130028705897Sccardenas 	 * Track 0xaa (Lead Out) Descriptor
130128705897Sccardenas 	 * [0]: Reserved, 0
130228705897Sccardenas 	 * [1]: ADR,Control, 0x14
130328705897Sccardenas 	 * [2]: Track #, 0xaa
130428705897Sccardenas 	 * [3]: Reserved, 0
130528705897Sccardenas 	 * [4-7]: Track Start Address, LBA
130628705897Sccardenas 	 */
130730dd31d2Sdv 	memset(toc_data, 0, sizeof(toc_data));
130828705897Sccardenas 	toc_data_p = toc_data + 2;
130928705897Sccardenas 	*toc_data_p++ = READ_TOC_START_TRACK;
131028705897Sccardenas 	*toc_data_p++ = READ_TOC_LAST_TRACK;
131128705897Sccardenas 	if (toc->from_track <= 1) {
131228705897Sccardenas 		/* first track descriptor */
131328705897Sccardenas 		*toc_data_p++ = 0x0;
131428705897Sccardenas 		*toc_data_p++ = READ_TOC_ADR_CTL;
131528705897Sccardenas 		*toc_data_p++ = READ_TOC_START_TRACK;
131628705897Sccardenas 		*toc_data_p++ = 0x0;
131728705897Sccardenas 		/* start addr for first track is 0 */
131828705897Sccardenas 		*toc_data_p++ = 0x0;
131928705897Sccardenas 		*toc_data_p++ = 0x0;
132028705897Sccardenas 		*toc_data_p++ = 0x0;
132128705897Sccardenas 		*toc_data_p++ = 0x0;
132228705897Sccardenas 	}
132328705897Sccardenas 
132428705897Sccardenas 	/* last track descriptor */
132528705897Sccardenas 	*toc_data_p++ = 0x0;
132628705897Sccardenas 	*toc_data_p++ = READ_TOC_ADR_CTL;
132728705897Sccardenas 	*toc_data_p++ = READ_TOC_LEAD_OUT_TRACK;
132828705897Sccardenas 	*toc_data_p++ = 0x0;
132928705897Sccardenas 
133028705897Sccardenas 	_lto4b((uint32_t)dev->n_blocks, toc_data_p);
133128705897Sccardenas 	toc_data_p += 4;
133228705897Sccardenas 
133328705897Sccardenas 	toc_data_len = toc_data_p - toc_data;
133428705897Sccardenas 	_lto2b((uint32_t)toc_data_len - 2, toc_data);
133528705897Sccardenas 
1336*253c7ec9Sjsg 	memset(&resp, 0, sizeof(resp));
133728705897Sccardenas 	vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
133828705897Sccardenas 
133928705897Sccardenas 	/* Move index for response */
134028705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
134128705897Sccardenas 	    &(acct->resp_idx));
134228705897Sccardenas 
13431f5e00e0Sreyk 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
134428705897Sccardenas 	    "idx %d req_idx %d global_idx %d",
134528705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
134628705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
134728705897Sccardenas 
13480759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
134928705897Sccardenas 		log_warnx("%s: unable to write OK resp status data @ 0x%llx",
135028705897Sccardenas 		    __func__, acct->resp_desc->addr);
135128705897Sccardenas 		goto read_toc_out;
135228705897Sccardenas 	}
135328705897Sccardenas 
135428705897Sccardenas 	/* Move index for toc descriptor */
135528705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
135628705897Sccardenas 	    &(acct->resp_idx));
135728705897Sccardenas 
13581f5e00e0Sreyk 	DPRINTF("%s: writing toc_data to 0x%llx size %d at "
135928705897Sccardenas 	    "local idx %d req_idx %d global_idx %d",
136028705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
136128705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
136228705897Sccardenas 
13630759b25cSdv 	if (write_mem(acct->resp_desc->addr, toc_data, sizeof(toc_data))) {
136428705897Sccardenas 		log_warnx("%s: unable to write toc descriptor data @ 0x%llx",
136528705897Sccardenas 		    __func__, acct->resp_desc->addr);
136628705897Sccardenas 	} else {
136728705897Sccardenas 		ret = 1;
136828705897Sccardenas 		dev->cfg.isr_status = 1;
136928705897Sccardenas 		/* Move ring indexes */
137028705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
137128705897Sccardenas 		    acct->req_desc, acct->req_idx);
137228705897Sccardenas 	}
137328705897Sccardenas 
137428705897Sccardenas read_toc_out:
137528705897Sccardenas 	return (ret);
137628705897Sccardenas }
137728705897Sccardenas 
137828705897Sccardenas static int
137928705897Sccardenas vioscsi_handle_read_disc_info(struct vioscsi_dev *dev,
138028705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
138128705897Sccardenas {
138228705897Sccardenas 	int ret = 0;
138328705897Sccardenas 	struct virtio_scsi_res_hdr resp;
138428705897Sccardenas 
138530dd31d2Sdv 	DPRINTF("%s: Disc Info %x", __func__,
138630dd31d2Sdv 		((struct scsi_read_disc_information *)(req->cdb))->byte2);
138728705897Sccardenas 
138828705897Sccardenas 	/* send back unsupported */
138930dd31d2Sdv 	memset(&resp, 0, sizeof(resp));
139028705897Sccardenas 	vioscsi_prepare_resp(&resp,
139128705897Sccardenas 	    VIRTIO_SCSI_S_OK, SCSI_CHECK, SKEY_ILLEGAL_REQUEST,
139228705897Sccardenas 	    SENSE_ILLEGAL_CDB_FIELD, SENSE_DEFAULT_ASCQ);
139328705897Sccardenas 
139428705897Sccardenas 	/* Move index for response */
139528705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
139628705897Sccardenas 	    acct->req_desc, &(acct->resp_idx));
139728705897Sccardenas 
13980759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
139928705897Sccardenas 		log_warnx("%s: unable to set ERR status data @ 0x%llx",
140028705897Sccardenas 		    __func__, acct->resp_desc->addr);
140128705897Sccardenas 	} else {
140228705897Sccardenas 		ret = 1;
140328705897Sccardenas 		dev->cfg.isr_status = 1;
140428705897Sccardenas 		/* Move ring indexes */
140528705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
140628705897Sccardenas 		    acct->req_desc, acct->req_idx);
140728705897Sccardenas 	}
140828705897Sccardenas 
140928705897Sccardenas 	return (ret);
141028705897Sccardenas }
141128705897Sccardenas 
141228705897Sccardenas static int
141328705897Sccardenas vioscsi_handle_gesn(struct vioscsi_dev *dev,
141428705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
141528705897Sccardenas {
141628705897Sccardenas 	int ret = 0;
141728705897Sccardenas 	struct virtio_scsi_res_hdr resp;
141828705897Sccardenas 	uint8_t gesn_reply[GESN_SIZE];
141928705897Sccardenas 	struct scsi_gesn *gesn;
142028705897Sccardenas 	struct scsi_gesn_event_header *gesn_event_header;
142128705897Sccardenas 	struct scsi_gesn_power_event *gesn_power_event;
142228705897Sccardenas 
142328705897Sccardenas 	memset(&resp, 0, sizeof(resp));
142428705897Sccardenas 	gesn = (struct scsi_gesn *)(req->cdb);
14252d03c861Sccardenas 	DPRINTF("%s: GESN Method %s", __func__,
142628705897Sccardenas 	    gesn->byte2 ? "Polling" : "Asynchronous");
142728705897Sccardenas 
142828705897Sccardenas 	if (gesn->byte2 == 0) {
142928705897Sccardenas 		/* we don't support asynchronous */
143028705897Sccardenas 		vioscsi_prepare_resp(&resp,
143128705897Sccardenas 		    VIRTIO_SCSI_S_OK, SCSI_CHECK, SKEY_ILLEGAL_REQUEST,
143228705897Sccardenas 		    SENSE_ILLEGAL_CDB_FIELD, SENSE_DEFAULT_ASCQ);
143328705897Sccardenas 
143428705897Sccardenas 		/* Move index for response */
143528705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
143628705897Sccardenas 		    acct->req_desc, &(acct->resp_idx));
143728705897Sccardenas 
14380759b25cSdv 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
143928705897Sccardenas 			log_warnx("%s: unable to set ERR status  data @ 0x%llx",
144028705897Sccardenas 			    __func__, acct->resp_desc->addr);
144128705897Sccardenas 			goto gesn_out;
144228705897Sccardenas 		}
144328705897Sccardenas 
144428705897Sccardenas 		ret = 1;
144528705897Sccardenas 		dev->cfg.isr_status = 1;
144628705897Sccardenas 		/* Move ring indexes */
144728705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
144828705897Sccardenas 		    acct->req_desc, acct->req_idx);
144928705897Sccardenas 
145028705897Sccardenas 		goto gesn_out;
145128705897Sccardenas 	}
145228705897Sccardenas 	memset(gesn_reply, 0, sizeof(gesn_reply));
145328705897Sccardenas 	gesn_event_header = (struct scsi_gesn_event_header *)(gesn_reply);
145428705897Sccardenas 	gesn_power_event = (struct scsi_gesn_power_event *)(gesn_reply + 4);
145528705897Sccardenas 	/* set event header length and notification */
145628705897Sccardenas 	_lto2b(GESN_HEADER_LEN, gesn_event_header->length);
145728705897Sccardenas 	gesn_event_header->notification = GESN_NOTIFY_POWER_MGMT;
145828705897Sccardenas 	gesn_event_header->supported_event = GESN_EVENT_POWER_MGMT;
145928705897Sccardenas 
146028705897Sccardenas 	/* set event descriptor */
146128705897Sccardenas 	gesn_power_event->event_code = GESN_CODE_NOCHG;
146228705897Sccardenas 	if (dev->locked)
146328705897Sccardenas 		gesn_power_event->status = GESN_STATUS_ACTIVE;
146428705897Sccardenas 	else
146528705897Sccardenas 		gesn_power_event->status = GESN_STATUS_IDLE;
146628705897Sccardenas 
146728705897Sccardenas 	/* Move index for response */
146828705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
146928705897Sccardenas 	    &(acct->resp_idx));
147028705897Sccardenas 
14711f5e00e0Sreyk 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
147228705897Sccardenas 	    "idx %d req_idx %d global_idx %d",
147328705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
147428705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
147528705897Sccardenas 
14760759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
147728705897Sccardenas 		log_warnx("%s: unable to write OK resp status "
147828705897Sccardenas 		    "data @ 0x%llx", __func__, acct->resp_desc->addr);
147928705897Sccardenas 		goto gesn_out;
148028705897Sccardenas 	}
148128705897Sccardenas 
148228705897Sccardenas 	/* Move index for gesn_reply */
148328705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
148428705897Sccardenas 	    &(acct->resp_idx));
148528705897Sccardenas 
14861f5e00e0Sreyk 	DPRINTF("%s: writing gesn_reply to 0x%llx size %d at "
148728705897Sccardenas 	    "local idx %d req_idx %d global_idx %d",
148828705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
148928705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
149028705897Sccardenas 
14910759b25cSdv 	if (write_mem(acct->resp_desc->addr, gesn_reply, sizeof(gesn_reply))) {
149228705897Sccardenas 		log_warnx("%s: unable to write gesn_reply"
149328705897Sccardenas 		    " response to gpa @ 0x%llx",
149428705897Sccardenas 		    __func__, acct->resp_desc->addr);
149528705897Sccardenas 	} else {
149628705897Sccardenas 		ret = 1;
149728705897Sccardenas 		dev->cfg.isr_status = 1;
149828705897Sccardenas 		/* Move ring indexes */
149928705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
150028705897Sccardenas 		    acct->req_desc, acct->req_idx);
150128705897Sccardenas 	}
150228705897Sccardenas 
150328705897Sccardenas gesn_out:
150428705897Sccardenas 	return (ret);
150528705897Sccardenas }
150628705897Sccardenas 
150728705897Sccardenas static int
150828705897Sccardenas vioscsi_handle_get_config(struct vioscsi_dev *dev,
150928705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
151028705897Sccardenas {
151128705897Sccardenas 	int ret = 0;
151228705897Sccardenas 	struct virtio_scsi_res_hdr resp;
151328705897Sccardenas 	uint8_t *get_conf_reply;
151428705897Sccardenas 	struct scsi_config_feature_header *config_feature_header;
151528705897Sccardenas 	struct scsi_config_generic_descriptor *config_generic_desc;
151628705897Sccardenas 	struct scsi_config_profile_descriptor *config_profile_desc;
151728705897Sccardenas 	struct scsi_config_core_descriptor *config_core_desc;
151828705897Sccardenas 	struct scsi_config_morphing_descriptor *config_morphing_desc;
151928705897Sccardenas 	struct scsi_config_remove_media_descriptor *config_remove_media_desc;
152028705897Sccardenas 	struct scsi_config_random_read_descriptor *config_random_read_desc;
152128705897Sccardenas 
152230dd31d2Sdv #if DEBUG
152330dd31d2Sdv 	struct scsi_get_configuration *get_configuration =
152430dd31d2Sdv 	    (struct scsi_get_configuration *)(req->cdb);
152530dd31d2Sdv 	log_debug("%s: Conf RT %x Feature %d Len %d", __func__,
152630dd31d2Sdv 	    get_configuration->byte2, _2btol(get_configuration->feature),
152730dd31d2Sdv 	    _2btol(get_configuration->length));
152830dd31d2Sdv #endif /* DEBUG */
152928705897Sccardenas 
153028705897Sccardenas 	get_conf_reply = (uint8_t*)calloc(G_CONFIG_REPLY_SIZE, sizeof(uint8_t));
153128705897Sccardenas 
153228705897Sccardenas 	if (get_conf_reply == NULL)
153328705897Sccardenas 		goto get_config_out;
153428705897Sccardenas 
153528705897Sccardenas 	/*
153628705897Sccardenas 	 * Use MMC-5 6.6 for structure and
153728705897Sccardenas 	 * MMC-5 5.2 to send back:
153828705897Sccardenas 	 * feature header - 8 bytes
153928705897Sccardenas 	 * feature descriptor for profile list - 8 bytes
154028705897Sccardenas 	 * feature descriptor for core feature - 12 bytes
154128705897Sccardenas 	 * feature descriptor for morphing feature - 8 bytes
154228705897Sccardenas 	 * feature descriptor for removable media - 8 bytes
154328705897Sccardenas 	 * feature descriptor for random read feature - 12 bytes
154428705897Sccardenas 	 */
154528705897Sccardenas 
154628705897Sccardenas 	config_feature_header =
154728705897Sccardenas 	    (struct scsi_config_feature_header *)(get_conf_reply);
154828705897Sccardenas 	config_generic_desc =
154928705897Sccardenas 	    (struct scsi_config_generic_descriptor *)(get_conf_reply + 8);
155028705897Sccardenas 	config_profile_desc =
155128705897Sccardenas 	    (struct scsi_config_profile_descriptor *)(get_conf_reply + 12);
155228705897Sccardenas 	config_core_desc =
155328705897Sccardenas 	    (struct scsi_config_core_descriptor *)(get_conf_reply + 16);
155428705897Sccardenas 	config_morphing_desc =
155528705897Sccardenas 	    (struct scsi_config_morphing_descriptor *)(get_conf_reply + 28);
155628705897Sccardenas 	config_remove_media_desc =
155728705897Sccardenas 	    (struct scsi_config_remove_media_descriptor *)(get_conf_reply + 36);
155828705897Sccardenas 	config_random_read_desc =
155928705897Sccardenas 	    (struct scsi_config_random_read_descriptor *)(get_conf_reply + 44);
156028705897Sccardenas 
156128705897Sccardenas 	/* set size to be get_conf_reply - size field */
156228705897Sccardenas 	_lto4b(G_CONFIG_REPLY_SIZE_HEX, config_feature_header->length);
156328705897Sccardenas 	/* set current profile to be non-conforming */
156428705897Sccardenas 	_lto2b(CONFIG_PROFILE_NON_CONFORM,
156528705897Sccardenas 	    config_feature_header->current_profile);
156628705897Sccardenas 
156728705897Sccardenas 	/* fill out profile list feature */
156828705897Sccardenas 	_lto2b(CONFIG_FEATURE_CODE_PROFILE, config_generic_desc->feature_code);
156928705897Sccardenas 	config_generic_desc->byte3 = CONFIG_PROFILELIST_BYTE3;
157028705897Sccardenas 	config_generic_desc->length = CONFIG_PROFILELIST_LENGTH;
157128705897Sccardenas 	/* fill out profile descriptor for NON_COFORM */
157228705897Sccardenas 	_lto2b(CONFIG_PROFILE_NON_CONFORM, config_profile_desc->profile_number);
157328705897Sccardenas 	config_profile_desc->byte3 = CONFIG_PROFILE_BYTE3;
157428705897Sccardenas 
157528705897Sccardenas 	/* fill out core feature */
157628705897Sccardenas 	_lto2b(CONFIG_FEATURE_CODE_CORE, config_core_desc->feature_code);
157728705897Sccardenas 	config_core_desc->byte3 = CONFIG_CORE_BYTE3;
157828705897Sccardenas 	config_core_desc->length = CONFIG_CORE_LENGTH;
157928705897Sccardenas 	_lto4b(CONFIG_CORE_PHY_SCSI, config_core_desc->phy_std);
158028705897Sccardenas 
158128705897Sccardenas 	/* fill out morphing feature */
158228705897Sccardenas 	_lto2b(CONFIG_FEATURE_CODE_MORPHING,
158328705897Sccardenas 	    config_morphing_desc->feature_code);
158428705897Sccardenas 	config_morphing_desc->byte3 = CONFIG_MORPHING_BYTE3;
158528705897Sccardenas 	config_morphing_desc->length = CONFIG_MORPHING_LENGTH;
158628705897Sccardenas 	config_morphing_desc->byte5 = CONFIG_MORPHING_BYTE5;
158728705897Sccardenas 
158828705897Sccardenas 	/* fill out removable media feature */
158928705897Sccardenas 	_lto2b(CONFIG_FEATURE_CODE_REMOVE_MEDIA,
159028705897Sccardenas 	    config_remove_media_desc->feature_code);
159128705897Sccardenas 	config_remove_media_desc->byte3 = CONFIG_REMOVE_MEDIA_BYTE3;
159228705897Sccardenas 	config_remove_media_desc->length = CONFIG_REMOVE_MEDIA_LENGTH;
159328705897Sccardenas 	config_remove_media_desc->byte5 = CONFIG_REMOVE_MEDIA_BYTE5;
159428705897Sccardenas 
159528705897Sccardenas 	/* fill out random read feature */
159628705897Sccardenas 	_lto2b(CONFIG_FEATURE_CODE_RANDOM_READ,
159728705897Sccardenas 	    config_random_read_desc->feature_code);
159828705897Sccardenas 	config_random_read_desc->byte3 = CONFIG_RANDOM_READ_BYTE3;
159928705897Sccardenas 	config_random_read_desc->length = CONFIG_RANDOM_READ_LENGTH;
1600a9eba918Sccardenas 	if (dev->n_blocks >= UINT32_MAX)
160128705897Sccardenas 		_lto4b(UINT32_MAX, config_random_read_desc->block_size);
160228705897Sccardenas 	else
160328705897Sccardenas 		_lto4b(dev->n_blocks - 1, config_random_read_desc->block_size);
160428705897Sccardenas 	_lto2b(CONFIG_RANDOM_READ_BLOCKING_TYPE,
160528705897Sccardenas 	    config_random_read_desc->blocking_type);
160628705897Sccardenas 
160730dd31d2Sdv 	memset(&resp, 0, sizeof(resp));
160828705897Sccardenas 	vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
160928705897Sccardenas 
161028705897Sccardenas 	/* Move index for response */
161128705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
161228705897Sccardenas 	    acct->req_desc, &(acct->resp_idx));
161328705897Sccardenas 
16141f5e00e0Sreyk 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
161528705897Sccardenas 	    "idx %d req_idx %d global_idx %d",
161628705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
161728705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
161828705897Sccardenas 
16190759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
162028705897Sccardenas 		log_warnx("%s: unable to set Ok status data @ 0x%llx",
162128705897Sccardenas 		    __func__, acct->resp_desc->addr);
162228705897Sccardenas 		goto free_get_config;
162328705897Sccardenas 	}
162428705897Sccardenas 
162528705897Sccardenas 	/* Move index for get_conf_reply */
162628705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
162728705897Sccardenas 	    &(acct->resp_idx));
162828705897Sccardenas 
16291f5e00e0Sreyk 	DPRINTF("%s: writing get_conf_reply to 0x%llx size %d "
163028705897Sccardenas 	    "at local idx %d req_idx %d global_idx %d",
163128705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
163228705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
163328705897Sccardenas 
163428705897Sccardenas 	if (write_mem(acct->resp_desc->addr, get_conf_reply,
16350759b25cSdv 	    G_CONFIG_REPLY_SIZE)) {
163628705897Sccardenas 		log_warnx("%s: unable to write get_conf_reply"
163728705897Sccardenas 		    " response to gpa @ 0x%llx",
163828705897Sccardenas 		    __func__, acct->resp_desc->addr);
163928705897Sccardenas 	} else {
164028705897Sccardenas 		ret = 1;
164128705897Sccardenas 		dev->cfg.isr_status = 1;
164228705897Sccardenas 		/* Move ring indexes */
164328705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
164428705897Sccardenas 		    acct->req_desc, acct->req_idx);
164528705897Sccardenas 	}
164628705897Sccardenas 
164728705897Sccardenas free_get_config:
164828705897Sccardenas 	free(get_conf_reply);
164928705897Sccardenas get_config_out:
165028705897Sccardenas 	return (ret);
165128705897Sccardenas }
165228705897Sccardenas 
165395ab188fSccardenas int
165495ab188fSccardenas vioscsi_io(int dir, uint16_t reg, uint32_t *data, uint8_t *intr,
165595ab188fSccardenas     void *cookie, uint8_t sz)
165695ab188fSccardenas {
165795ab188fSccardenas 	struct vioscsi_dev *dev = (struct vioscsi_dev *)cookie;
165895ab188fSccardenas 
165995ab188fSccardenas 	*intr = 0xFF;
166095ab188fSccardenas 
16612d03c861Sccardenas 	DPRINTF("%s: request %s reg %u, %s sz %u", __func__,
166295ab188fSccardenas 	    dir ? "READ" : "WRITE", reg, vioscsi_reg_name(reg), sz);
166395ab188fSccardenas 
166495ab188fSccardenas 	if (dir == 0) {
166595ab188fSccardenas 		switch (reg) {
166695ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_FEATURES:
166795ab188fSccardenas 		case VIRTIO_CONFIG_QUEUE_SIZE:
166895ab188fSccardenas 		case VIRTIO_CONFIG_ISR_STATUS:
166995ab188fSccardenas 			log_warnx("%s: illegal write %x to %s",
167095ab188fSccardenas 			    __progname, *data, vioscsi_reg_name(reg));
167195ab188fSccardenas 			break;
167295ab188fSccardenas 		case VIRTIO_CONFIG_GUEST_FEATURES:
167395ab188fSccardenas 			dev->cfg.guest_feature = *data;
16742d03c861Sccardenas 			DPRINTF("%s: guest feature set to %u",
167595ab188fSccardenas 			    __func__, dev->cfg.guest_feature);
167695ab188fSccardenas 			break;
16770bd10b9fSdv 		case VIRTIO_CONFIG_QUEUE_PFN:
16780bd10b9fSdv 			dev->cfg.queue_pfn = *data;
167995ab188fSccardenas 			vioscsi_update_qa(dev);
168095ab188fSccardenas 			break;
168195ab188fSccardenas 		case VIRTIO_CONFIG_QUEUE_SELECT:
168295ab188fSccardenas 			dev->cfg.queue_select = *data;
168395ab188fSccardenas 			vioscsi_update_qs(dev);
168495ab188fSccardenas 			break;
168595ab188fSccardenas 		case VIRTIO_CONFIG_QUEUE_NOTIFY:
168695ab188fSccardenas 			dev->cfg.queue_notify = *data;
168795ab188fSccardenas 			if (vioscsi_notifyq(dev))
168895ab188fSccardenas 				*intr = 1;
168995ab188fSccardenas 			break;
169095ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_STATUS:
169195ab188fSccardenas 			dev->cfg.device_status = *data;
16922d03c861Sccardenas 			DPRINTF("%s: device status set to %u",
169395ab188fSccardenas 			    __func__, dev->cfg.device_status);
169495ab188fSccardenas 			if (dev->cfg.device_status == 0) {
1695c5c1249fSreyk 				log_debug("%s: device reset", __func__);
169695ab188fSccardenas 				dev->cfg.guest_feature = 0;
16970bd10b9fSdv 				dev->cfg.queue_pfn = 0;
169895ab188fSccardenas 				vioscsi_update_qa(dev);
169995ab188fSccardenas 				dev->cfg.queue_size = 0;
170095ab188fSccardenas 				vioscsi_update_qs(dev);
170195ab188fSccardenas 				dev->cfg.queue_select = 0;
170295ab188fSccardenas 				dev->cfg.queue_notify = 0;
170395ab188fSccardenas 				dev->cfg.isr_status = 0;
170495ab188fSccardenas 				dev->vq[0].last_avail = 0;
170595ab188fSccardenas 				dev->vq[1].last_avail = 0;
170695ab188fSccardenas 				dev->vq[2].last_avail = 0;
170795ab188fSccardenas 			}
170895ab188fSccardenas 			break;
170995ab188fSccardenas 		default:
171095ab188fSccardenas 			break;
171195ab188fSccardenas 		}
171295ab188fSccardenas 	} else {
171395ab188fSccardenas 		switch (reg) {
171495ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI:
171595ab188fSccardenas 			/* VIRTIO_SCSI_CONFIG_NUM_QUEUES, 32bit */
171695ab188fSccardenas 			if (sz == 4)
171795ab188fSccardenas 				*data = (uint32_t)VIOSCSI_NUM_QUEUES;
171895ab188fSccardenas 			else if (sz == 1) {
171995ab188fSccardenas 				/* read first byte of num_queues */
172095ab188fSccardenas 				*data &= 0xFFFFFF00;
172195ab188fSccardenas 				*data |= (uint32_t)(VIOSCSI_NUM_QUEUES) & 0xFF;
172295ab188fSccardenas 			}
172395ab188fSccardenas 			break;
172495ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 1:
172595ab188fSccardenas 			if (sz == 1) {
172695ab188fSccardenas 				/* read second byte of num_queues */
172795ab188fSccardenas 				*data &= 0xFFFFFF00;
172895ab188fSccardenas 				*data |=
172995ab188fSccardenas 				    (uint32_t)(VIOSCSI_NUM_QUEUES >> 8) & 0xFF;
173095ab188fSccardenas 			}
173195ab188fSccardenas 			break;
173295ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 2:
173395ab188fSccardenas 			if (sz == 1) {
173495ab188fSccardenas 				/* read third byte of num_queues */
173595ab188fSccardenas 				*data &= 0xFFFFFF00;
173695ab188fSccardenas 				*data |=
173795ab188fSccardenas 				    (uint32_t)(VIOSCSI_NUM_QUEUES >> 16) & 0xFF;
173895ab188fSccardenas 			}
173995ab188fSccardenas 			break;
174095ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 3:
174195ab188fSccardenas 			if (sz == 1) {
174295ab188fSccardenas 				/* read fourth byte of num_queues */
174395ab188fSccardenas 				*data &= 0xFFFFFF00;
174495ab188fSccardenas 				*data |=
174595ab188fSccardenas 				    (uint32_t)(VIOSCSI_NUM_QUEUES >> 24) & 0xFF;
174695ab188fSccardenas 			}
174795ab188fSccardenas 			break;
174895ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 4:
174995ab188fSccardenas 			/* VIRTIO_SCSI_CONFIG_SEG_MAX, 32bit */
175095ab188fSccardenas 			if (sz == 4)
175195ab188fSccardenas 				*data = (uint32_t)(VIOSCSI_SEG_MAX);
175295ab188fSccardenas 			else if (sz == 1) {
175395ab188fSccardenas 				/* read first byte of seg_max */
175495ab188fSccardenas 				*data &= 0xFFFFFF00;
175595ab188fSccardenas 				*data |= (uint32_t)(VIOSCSI_SEG_MAX) & 0xFF;
175695ab188fSccardenas 			}
175795ab188fSccardenas 			break;
175895ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 5:
175995ab188fSccardenas 			if (sz == 1) {
176095ab188fSccardenas 				/* read second byte of seg_max */
176195ab188fSccardenas 				*data &= 0xFFFFFF00;
176295ab188fSccardenas 				*data |=
176395ab188fSccardenas 				    (uint32_t)(VIOSCSI_SEG_MAX >> 8) & 0xFF;
176495ab188fSccardenas 			}
176595ab188fSccardenas 			break;
176695ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 6:
176795ab188fSccardenas 			if (sz == 1) {
176895ab188fSccardenas 				/* read third byte of seg_max */
176995ab188fSccardenas 				*data &= 0xFFFFFF00;
177095ab188fSccardenas 				*data |=
177195ab188fSccardenas 				    (uint32_t)(VIOSCSI_SEG_MAX >> 16) & 0xFF;
177295ab188fSccardenas 			}
177395ab188fSccardenas 			break;
177495ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 7:
177595ab188fSccardenas 			if (sz == 1) {
177695ab188fSccardenas 				/* read fourth byte of seg_max */
177795ab188fSccardenas 				*data &= 0xFFFFFF00;
177895ab188fSccardenas 				*data |=
177995ab188fSccardenas 				    (uint32_t)(VIOSCSI_SEG_MAX >> 24) & 0xFF;
178095ab188fSccardenas 			}
178195ab188fSccardenas 			break;
178295ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 8:
178395ab188fSccardenas 			/* VIRTIO_SCSI_CONFIG_MAX_SECTORS, 32bit */
178495ab188fSccardenas 			if (sz == 4)
178595ab188fSccardenas 				*data = (uint32_t)(dev->max_xfer);
178695ab188fSccardenas 			else if (sz == 1) {
178795ab188fSccardenas 				/* read first byte of max_xfer */
178895ab188fSccardenas 				*data &= 0xFFFFFF00;
178995ab188fSccardenas 				*data |= (uint32_t)(dev->max_xfer) & 0xFF;
179095ab188fSccardenas 			}
179195ab188fSccardenas 			break;
179295ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 9:
179395ab188fSccardenas 			if (sz == 1) {
179495ab188fSccardenas 				/* read second byte of max_xfer */
179595ab188fSccardenas 				*data &= 0xFFFFFF00;
179695ab188fSccardenas 				*data |=
179795ab188fSccardenas 				    (uint32_t)(dev->max_xfer >> 8) & 0xFF;
179895ab188fSccardenas 			}
179995ab188fSccardenas 			break;
180095ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 10:
180195ab188fSccardenas 			if (sz == 1) {
180295ab188fSccardenas 				/* read third byte of max_xfer */
180395ab188fSccardenas 				*data &= 0xFFFFFF00;
180495ab188fSccardenas 				*data |=
180595ab188fSccardenas 				    (uint32_t)(dev->max_xfer >> 16) & 0xFF;
180695ab188fSccardenas 			}
180795ab188fSccardenas 			break;
180895ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 11:
180995ab188fSccardenas 			if (sz == 1) {
181095ab188fSccardenas 				/* read fourth byte of max_xfer */
181195ab188fSccardenas 				*data &= 0xFFFFFF00;
181295ab188fSccardenas 				*data |=
181395ab188fSccardenas 				    (uint32_t)(dev->max_xfer >> 24) & 0xFF;
181495ab188fSccardenas 			}
181595ab188fSccardenas 			break;
181695ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 12:
181795ab188fSccardenas 			/* VIRTIO_SCSI_CONFIG_CMD_PER_LUN, 32bit */
181895ab188fSccardenas 			if (sz == 4)
181995ab188fSccardenas 				*data = (uint32_t)(VIOSCSI_CMD_PER_LUN);
182095ab188fSccardenas 			else if (sz == 1) {
182195ab188fSccardenas 				/* read first byte of cmd_per_lun */
182295ab188fSccardenas 				*data &= 0xFFFFFF00;
182395ab188fSccardenas 				*data |= (uint32_t)(VIOSCSI_CMD_PER_LUN) & 0xFF;
182495ab188fSccardenas 			}
182595ab188fSccardenas 			break;
182695ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 13:
182795ab188fSccardenas 			if (sz == 1) {
182895ab188fSccardenas 				/* read second byte of cmd_per_lun */
182995ab188fSccardenas 				*data &= 0xFFFFFF00;
183095ab188fSccardenas 				*data |=
183195ab188fSccardenas 				    (uint32_t)(VIOSCSI_CMD_PER_LUN >> 8) & 0xFF;
183295ab188fSccardenas 			}
183395ab188fSccardenas 			break;
183495ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 14:
183595ab188fSccardenas 			if (sz == 1) {
183695ab188fSccardenas 				/* read third byte of cmd_per_lun */
183795ab188fSccardenas 				*data &= 0xFFFFFF00;
1838d2de69e7Sreyk 				*data |= (uint32_t)(VIOSCSI_CMD_PER_LUN >> 16)
1839d2de69e7Sreyk 				    & 0xFF;
184095ab188fSccardenas 			}
184195ab188fSccardenas 			break;
184295ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 15:
184395ab188fSccardenas 			if (sz == 1) {
184495ab188fSccardenas 				/* read fourth byte of cmd_per_lun */
184595ab188fSccardenas 				*data &= 0xFFFFFF00;
1846d2de69e7Sreyk 				*data |= (uint32_t)(VIOSCSI_CMD_PER_LUN >> 24)
1847d2de69e7Sreyk 				    & 0xFF;
184895ab188fSccardenas 			}
184995ab188fSccardenas 			break;
185095ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 16:
185195ab188fSccardenas 			/* VIRTIO_SCSI_CONFIG_EVENT_INFO_SIZE, 32bit */
185295ab188fSccardenas 			*data = 0x00;
185395ab188fSccardenas 			break;
185495ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 20:
185595ab188fSccardenas 			/* VIRTIO_SCSI_CONFIG_SENSE_SIZE, 32bit */
185695ab188fSccardenas 			if (sz == 4)
185795ab188fSccardenas 				*data = (uint32_t)(VIOSCSI_SENSE_LEN);
185895ab188fSccardenas 			else if (sz == 1) {
185995ab188fSccardenas 				/* read first byte of sense_size */
186095ab188fSccardenas 				*data &= 0xFFFFFF00;
186195ab188fSccardenas 				*data |= (uint32_t)(VIOSCSI_SENSE_LEN) & 0xFF;
186295ab188fSccardenas 			}
186395ab188fSccardenas 			break;
186495ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 21:
186595ab188fSccardenas 			if (sz == 1) {
186695ab188fSccardenas 				/* read second byte of sense_size */
186795ab188fSccardenas 				*data &= 0xFFFFFF00;
186895ab188fSccardenas 				*data |=
186995ab188fSccardenas 				    (uint32_t)(VIOSCSI_SENSE_LEN >> 8) & 0xFF;
187095ab188fSccardenas 			}
187195ab188fSccardenas 			break;
187295ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 22:
187395ab188fSccardenas 			if (sz == 1) {
187495ab188fSccardenas 				/* read third byte of sense_size */
187595ab188fSccardenas 				*data &= 0xFFFFFF00;
187695ab188fSccardenas 				*data |=
187795ab188fSccardenas 				    (uint32_t)(VIOSCSI_SENSE_LEN >> 16) & 0xFF;
187895ab188fSccardenas 			}
187995ab188fSccardenas 			break;
188095ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 23:
188195ab188fSccardenas 			if (sz == 1) {
188295ab188fSccardenas 				/* read fourth byte of sense_size */
188395ab188fSccardenas 				*data &= 0xFFFFFF00;
188495ab188fSccardenas 				*data |=
188595ab188fSccardenas 				    (uint32_t)(VIOSCSI_SENSE_LEN >> 24) & 0xFF;
188695ab188fSccardenas 			}
188795ab188fSccardenas 			break;
188895ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 24:
188995ab188fSccardenas 			/* VIRTIO_SCSI_CONFIG_CDB_SIZE, 32bit */
189095ab188fSccardenas 			if (sz == 4)
189195ab188fSccardenas 				*data = (uint32_t)(VIOSCSI_CDB_LEN);
189295ab188fSccardenas 			else if (sz == 1) {
189395ab188fSccardenas 				/* read first byte of cdb_len */
189495ab188fSccardenas 				*data &= 0xFFFFFF00;
189595ab188fSccardenas 				*data |= (uint32_t)(VIOSCSI_CDB_LEN) & 0xFF;
189695ab188fSccardenas 			}
189795ab188fSccardenas 			break;
189895ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 25:
189995ab188fSccardenas 			if (sz == 1) {
190095ab188fSccardenas 				/* read second byte of cdb_len */
190195ab188fSccardenas 				*data &= 0xFFFFFF00;
190295ab188fSccardenas 				*data |=
190395ab188fSccardenas 				    (uint32_t)(VIOSCSI_CDB_LEN >> 8) & 0xFF;
190495ab188fSccardenas 			}
190595ab188fSccardenas 			break;
190695ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 26:
190795ab188fSccardenas 			if (sz == 1) {
190895ab188fSccardenas 				/* read third byte of cdb_len */
190995ab188fSccardenas 				*data &= 0xFFFFFF00;
191095ab188fSccardenas 				*data |=
191195ab188fSccardenas 				    (uint32_t)(VIOSCSI_CDB_LEN >> 16) & 0xFF;
191295ab188fSccardenas 			}
191395ab188fSccardenas 			break;
191495ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 27:
191595ab188fSccardenas 			if (sz == 1) {
191695ab188fSccardenas 				/* read fourth byte of cdb_len */
191795ab188fSccardenas 				*data &= 0xFFFFFF00;
191895ab188fSccardenas 				*data |=
191995ab188fSccardenas 				    (uint32_t)(VIOSCSI_CDB_LEN >> 24) & 0xFF;
192095ab188fSccardenas 			}
192195ab188fSccardenas 			break;
192295ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 28:
192395ab188fSccardenas 			/* VIRTIO_SCSI_CONFIG_MAX_CHANNEL, 16bit */
1924d2de69e7Sreyk 
1925d2de69e7Sreyk 			/* defined by standard to be zero */
1926d2de69e7Sreyk 			*data &= 0xFFFF0000;
192795ab188fSccardenas 			break;
192895ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 29:
1929d2de69e7Sreyk 			/* defined by standard to be zero */
1930d2de69e7Sreyk 			*data &= 0xFFFF0000;
193195ab188fSccardenas 			break;
193295ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 30:
193395ab188fSccardenas 			/* VIRTIO_SCSI_CONFIG_MAX_TARGET, 16bit */
193495ab188fSccardenas 			if (sz == 2) {
193595ab188fSccardenas 				*data &= 0xFFFF0000;
193695ab188fSccardenas 				*data |=
193795ab188fSccardenas 				    (uint32_t)(VIOSCSI_MAX_TARGET) & 0xFFFF;
193895ab188fSccardenas 			} else if (sz == 1) {
193995ab188fSccardenas 				/* read first byte of max_target */
194095ab188fSccardenas 				*data &= 0xFFFFFF00;
194195ab188fSccardenas 				*data |=
194295ab188fSccardenas 				    (uint32_t)(VIOSCSI_MAX_TARGET) & 0xFF;
194395ab188fSccardenas 			}
194495ab188fSccardenas 			break;
194595ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 31:
194695ab188fSccardenas 			if (sz == 1) {
194795ab188fSccardenas 				/* read second byte of max_target */
194895ab188fSccardenas 				*data &= 0xFFFFFF00;
194995ab188fSccardenas 				*data |=
195095ab188fSccardenas 				    (uint32_t)(VIOSCSI_MAX_TARGET >> 8) & 0xFF;
195195ab188fSccardenas 			}
195295ab188fSccardenas 			break;
195395ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 32:
195495ab188fSccardenas 			/* VIRTIO_SCSI_CONFIG_MAX_LUN, 32bit */
195595ab188fSccardenas 			if (sz == 4)
195695ab188fSccardenas 				*data = (uint32_t)(VIOSCSI_MAX_LUN);
195795ab188fSccardenas 			else if (sz == 1) {
195895ab188fSccardenas 				/* read first byte of max_lun */
195995ab188fSccardenas 				*data &= 0xFFFFFF00;
196095ab188fSccardenas 				*data |= (uint32_t)(VIOSCSI_MAX_LUN) & 0xFF;
196195ab188fSccardenas 			}
196295ab188fSccardenas 			break;
196395ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 33:
196495ab188fSccardenas 			if (sz == 1) {
196595ab188fSccardenas 				/* read second byte of max_lun */
196695ab188fSccardenas 				*data &= 0xFFFFFF00;
196795ab188fSccardenas 				*data |=
196895ab188fSccardenas 				    (uint32_t)(VIOSCSI_MAX_LUN >> 8) & 0xFF;
196995ab188fSccardenas 			}
197095ab188fSccardenas 			break;
197195ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 34:
197295ab188fSccardenas 			if (sz == 1) {
197395ab188fSccardenas 				/* read third byte of max_lun */
197495ab188fSccardenas 				*data &= 0xFFFFFF00;
197595ab188fSccardenas 				*data |=
197695ab188fSccardenas 				    (uint32_t)(VIOSCSI_MAX_LUN >> 16) & 0xFF;
197795ab188fSccardenas 			}
197895ab188fSccardenas 			break;
197995ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 35:
198095ab188fSccardenas 			if (sz == 1) {
198195ab188fSccardenas 				/* read fourth byte of max_lun */
198295ab188fSccardenas 				*data &= 0xFFFFFF00;
198395ab188fSccardenas 				*data |=
198495ab188fSccardenas 				    (uint32_t)(VIOSCSI_MAX_LUN >> 24) & 0xFF;
198595ab188fSccardenas 			}
198695ab188fSccardenas 			break;
198795ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_FEATURES:
198895ab188fSccardenas 			*data = dev->cfg.device_feature;
198995ab188fSccardenas 			break;
199095ab188fSccardenas 		case VIRTIO_CONFIG_GUEST_FEATURES:
199195ab188fSccardenas 			*data = dev->cfg.guest_feature;
199295ab188fSccardenas 			break;
19930bd10b9fSdv 		case VIRTIO_CONFIG_QUEUE_PFN:
19940bd10b9fSdv 			*data = dev->cfg.queue_pfn;
199595ab188fSccardenas 			break;
199695ab188fSccardenas 		case VIRTIO_CONFIG_QUEUE_SIZE:
199795ab188fSccardenas 			if (sz == 4)
199895ab188fSccardenas 				*data = dev->cfg.queue_size;
199995ab188fSccardenas 			else if (sz == 2) {
200095ab188fSccardenas 				*data &= 0xFFFF0000;
200195ab188fSccardenas 				*data |= (uint16_t)dev->cfg.queue_size;
200295ab188fSccardenas 			} else if (sz == 1) {
200395ab188fSccardenas 				*data &= 0xFFFFFF00;
200495ab188fSccardenas 				*data |= (uint8_t)dev->cfg.queue_size;
200595ab188fSccardenas 			}
200695ab188fSccardenas 			break;
200795ab188fSccardenas 		case VIRTIO_CONFIG_QUEUE_SELECT:
200895ab188fSccardenas 			*data = dev->cfg.queue_select;
200995ab188fSccardenas 			break;
201095ab188fSccardenas 		case VIRTIO_CONFIG_QUEUE_NOTIFY:
201195ab188fSccardenas 			*data = dev->cfg.queue_notify;
201295ab188fSccardenas 			break;
201395ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_STATUS:
201495ab188fSccardenas 			if (sz == 4)
201595ab188fSccardenas 				*data = dev->cfg.device_status;
201695ab188fSccardenas 			else if (sz == 2) {
201795ab188fSccardenas 				*data &= 0xFFFF0000;
201895ab188fSccardenas 				*data |= (uint16_t)dev->cfg.device_status;
201995ab188fSccardenas 			} else if (sz == 1) {
202095ab188fSccardenas 				*data &= 0xFFFFFF00;
202195ab188fSccardenas 				*data |= (uint8_t)dev->cfg.device_status;
202295ab188fSccardenas 			}
202395ab188fSccardenas 			break;
202495ab188fSccardenas 		case VIRTIO_CONFIG_ISR_STATUS:
202595ab188fSccardenas 			*data = dev->cfg.isr_status;
202695ab188fSccardenas 			dev->cfg.isr_status = 0;
202795ab188fSccardenas 			break;
202895ab188fSccardenas 		}
202995ab188fSccardenas 	}
203095ab188fSccardenas 
203195ab188fSccardenas 
203295ab188fSccardenas 	return (0);
203395ab188fSccardenas }
203495ab188fSccardenas 
203595ab188fSccardenas void
203695ab188fSccardenas vioscsi_update_qs(struct vioscsi_dev *dev)
203795ab188fSccardenas {
20380bd10b9fSdv 	struct virtio_vq_info *vq_info;
20390bd10b9fSdv 
204095ab188fSccardenas 	/* Invalid queue? */
20418e29e26eSjsg 	if (dev->cfg.queue_select >= VIRTIO_MAX_QUEUES) {
204295ab188fSccardenas 		dev->cfg.queue_size = 0;
204395ab188fSccardenas 		return;
204495ab188fSccardenas 	}
204595ab188fSccardenas 
20460bd10b9fSdv 	vq_info = &dev->vq[dev->cfg.queue_select];
20470bd10b9fSdv 
20480bd10b9fSdv 	/* Update queue pfn/size based on queue select */
20490bd10b9fSdv 	dev->cfg.queue_pfn = vq_info->q_gpa >> 12;
20500bd10b9fSdv 	dev->cfg.queue_size = vq_info->qs;
205195ab188fSccardenas }
205295ab188fSccardenas 
205395ab188fSccardenas void
205495ab188fSccardenas vioscsi_update_qa(struct vioscsi_dev *dev)
205595ab188fSccardenas {
20560bd10b9fSdv 	struct virtio_vq_info *vq_info;
20570bd10b9fSdv 	void *hva = NULL;
20580bd10b9fSdv 
205995ab188fSccardenas 	/* Invalid queue? */
20608e29e26eSjsg 	if (dev->cfg.queue_select >= VIRTIO_MAX_QUEUES)
206195ab188fSccardenas 		return;
206295ab188fSccardenas 
20630bd10b9fSdv 	vq_info = &dev->vq[dev->cfg.queue_select];
20640bd10b9fSdv 	vq_info->q_gpa = (uint64_t)dev->cfg.queue_pfn * VIRTIO_PAGE_SIZE;
20650bd10b9fSdv 
20660bd10b9fSdv 	hva = hvaddr_mem(vq_info->q_gpa, vring_size(VIOSCSI_QUEUE_SIZE));
20670bd10b9fSdv 	if (hva == NULL)
20680bd10b9fSdv 		fatal("vioscsi_update_qa");
20690bd10b9fSdv 	vq_info->q_hva = hva;
207095ab188fSccardenas }
207195ab188fSccardenas 
207295ab188fSccardenas /*
207395ab188fSccardenas  * Process message(s) in the queue(s)
207495ab188fSccardenas  * vioscsi driver will be placing the following in the queue for each iteration
207595ab188fSccardenas  * virtio_scsi_req_hdr with a possible SCSI_DATA_OUT buffer
207695ab188fSccardenas  * along with a virtio_scsi_res_hdr with a possible SCSI_DATA_IN buffer
207795ab188fSccardenas  * for consumption.
207895ab188fSccardenas  *
207995ab188fSccardenas  * Return 1 if an interrupt should be generated (response written)
208095ab188fSccardenas  *        0 otherwise
208195ab188fSccardenas  */
208295ab188fSccardenas int
208395ab188fSccardenas vioscsi_notifyq(struct vioscsi_dev *dev)
208495ab188fSccardenas {
20850bd10b9fSdv 	int cnt, ret = 0;
208695ab188fSccardenas 	char *vr;
208795ab188fSccardenas 	struct virtio_scsi_req_hdr req;
208895ab188fSccardenas 	struct virtio_scsi_res_hdr resp;
208928705897Sccardenas 	struct virtio_vq_acct acct;
20900bd10b9fSdv 	struct virtio_vq_info *vq_info;
209195ab188fSccardenas 
209295ab188fSccardenas 	ret = 0;
209395ab188fSccardenas 
209495ab188fSccardenas 	/* Invalid queue? */
20958e29e26eSjsg 	if (dev->cfg.queue_notify >= VIRTIO_MAX_QUEUES)
209695ab188fSccardenas 		return (ret);
209795ab188fSccardenas 
20980bd10b9fSdv 	vq_info = &dev->vq[dev->cfg.queue_notify];
20990bd10b9fSdv 	vr = vq_info->q_hva;
21000bd10b9fSdv 	if (vr == NULL)
21010bd10b9fSdv 		fatalx("%s: null vring", __func__);
210295ab188fSccardenas 
210395ab188fSccardenas 	/* Compute offsets in ring of descriptors, avail ring, and used ring */
210428705897Sccardenas 	acct.desc = (struct vring_desc *)(vr);
21050bd10b9fSdv 	acct.avail = (struct vring_avail *)(vr + vq_info->vq_availoffset);
21060bd10b9fSdv 	acct.used = (struct vring_used *)(vr + vq_info->vq_usedoffset);
210795ab188fSccardenas 
21080bd10b9fSdv 	acct.idx = vq_info->last_avail & VIOSCSI_QUEUE_MASK;
210995ab188fSccardenas 
211028705897Sccardenas 	if ((acct.avail->idx & VIOSCSI_QUEUE_MASK) == acct.idx) {
21110bd10b9fSdv 		log_debug("%s - nothing to do?", __func__);
21120bd10b9fSdv 		return (0);
211395ab188fSccardenas 	}
211495ab188fSccardenas 
21153688c158Sdv 	cnt = 0;
211628705897Sccardenas 	while (acct.idx != (acct.avail->idx & VIOSCSI_QUEUE_MASK)) {
211795ab188fSccardenas 
21183688c158Sdv 		/* Guard against infinite descriptor chains */
21193688c158Sdv 		if (++cnt >= VIOSCSI_QUEUE_SIZE) {
21203688c158Sdv 			log_warnx("%s: invalid descriptor table", __func__);
21213688c158Sdv 			goto out;
21223688c158Sdv 		}
21233688c158Sdv 
212428705897Sccardenas 		acct.req_idx = acct.avail->ring[acct.idx] & VIOSCSI_QUEUE_MASK;
212528705897Sccardenas 		acct.req_desc = &(acct.desc[acct.req_idx]);
212695ab188fSccardenas 
212795ab188fSccardenas 		/* Clear resp for next message */
212895ab188fSccardenas 		memset(&resp, 0, sizeof(resp));
212995ab188fSccardenas 
213028705897Sccardenas 		if ((acct.req_desc->flags & VRING_DESC_F_NEXT) == 0) {
213195ab188fSccardenas 			log_warnx("%s: unchained req descriptor received "
213228705897Sccardenas 			    "(idx %d)", __func__, acct.req_idx);
213395ab188fSccardenas 			goto out;
213495ab188fSccardenas 		}
213595ab188fSccardenas 
213695ab188fSccardenas 		/* Read command from descriptor ring */
21370759b25cSdv 		if (read_mem(acct.req_desc->addr, &req, sizeof(req))) {
213895ab188fSccardenas 			log_warnx("%s: command read_mem error @ 0x%llx",
213928705897Sccardenas 			    __func__, acct.req_desc->addr);
214095ab188fSccardenas 			goto out;
214195ab188fSccardenas 		}
214295ab188fSccardenas 
214395ab188fSccardenas 		/*
214495ab188fSccardenas 		 * req.lun is defined by virtio as
214595ab188fSccardenas 		 * lun[0] - Always set to 1
214695ab188fSccardenas 		 * lun[1] - Target, negotiated as VIOSCSI_MAX_TARGET
214795ab188fSccardenas 		 * lun[2-3] - represent single level LUN structure
214895ab188fSccardenas 		 * lun[4-7] - Zero
214995ab188fSccardenas 		 * At this current time, we are only servicing one device per
215095ab188fSccardenas 		 * bus (1:0:X:0).
215195ab188fSccardenas 		 *
215295ab188fSccardenas 		 * Various implementations will attempt to scan all possible
215395ab188fSccardenas 		 * targets (256) looking for devices or scan for all possible
215495ab188fSccardenas 		 * LUNs in a single level.  When Target is greater than
215595ab188fSccardenas 		 * VIOSCSI_MAX_TARGET or when lun[3] is greater than zero,
215695ab188fSccardenas 		 * respond with a BAD_TARGET response.
215795ab188fSccardenas 		 */
215895ab188fSccardenas 		if (req.lun[1] >= VIOSCSI_MAX_TARGET || req.lun[3] > 0) {
21592d03c861Sccardenas 			DPRINTF("%s: Ignore CMD 0x%02x,%s on lun %u:%u:%u:%u",
216095ab188fSccardenas 			    __func__, req.cdb[0], vioscsi_op_names(req.cdb[0]),
216195ab188fSccardenas 			    req.lun[0], req.lun[1], req.lun[2], req.lun[3]);
216295ab188fSccardenas 			/* Move index for response */
216328705897Sccardenas 			acct.resp_desc = vioscsi_next_ring_desc(acct.desc,
216428705897Sccardenas 			    acct.req_desc, &(acct.resp_idx));
216595ab188fSccardenas 
216695ab188fSccardenas 			vioscsi_prepare_resp(&resp,
216795ab188fSccardenas 			    VIRTIO_SCSI_S_BAD_TARGET, SCSI_OK, 0, 0, 0);
216895ab188fSccardenas 
21690759b25cSdv 			if (acct.resp_desc->len > sizeof(resp)) {
21700759b25cSdv 				log_warnx("%s: invalid descriptor length",
21710759b25cSdv 				    __func__);
21720759b25cSdv 				goto out;
21730759b25cSdv 			}
217428705897Sccardenas 			if (write_mem(acct.resp_desc->addr, &resp,
21750759b25cSdv 				sizeof(resp))) {
217695ab188fSccardenas 				log_warnx("%s: unable to write BAD_TARGET"
217795ab188fSccardenas 				    " resp status data @ 0x%llx",
217828705897Sccardenas 				    __func__, acct.resp_desc->addr);
217995ab188fSccardenas 				goto out;
218095ab188fSccardenas 			}
218195ab188fSccardenas 
218295ab188fSccardenas 			ret = 1;
218395ab188fSccardenas 			dev->cfg.isr_status = 1;
21840bd10b9fSdv 
21850bd10b9fSdv 			/* Move ring indexes (updates the used ring index) */
218628705897Sccardenas 			vioscsi_next_ring_item(dev, acct.avail, acct.used,
218728705897Sccardenas 			    acct.req_desc, acct.req_idx);
218895ab188fSccardenas 			goto next_msg;
218995ab188fSccardenas 		}
219095ab188fSccardenas 
21912d03c861Sccardenas 		DPRINTF("%s: Queue %d id 0x%llx lun %u:%u:%u:%u"
219295ab188fSccardenas 		    " cdb OP 0x%02x,%s",
219395ab188fSccardenas 		    __func__, dev->cfg.queue_notify, req.id,
219495ab188fSccardenas 		    req.lun[0], req.lun[1], req.lun[2], req.lun[3],
219595ab188fSccardenas 		    req.cdb[0], vioscsi_op_names(req.cdb[0]));
219695ab188fSccardenas 
219795ab188fSccardenas 		/* opcode is first byte */
219895ab188fSccardenas 		switch (req.cdb[0]) {
219995ab188fSccardenas 		case TEST_UNIT_READY:
220095ab188fSccardenas 		case START_STOP:
220128705897Sccardenas 			ret = vioscsi_handle_tur(dev, &req, &acct);
220295ab188fSccardenas 			break;
220395ab188fSccardenas 		case PREVENT_ALLOW:
220428705897Sccardenas 			ret = vioscsi_handle_prevent_allow(dev, &req, &acct);
220595ab188fSccardenas 			break;
220695ab188fSccardenas 		case READ_TOC:
220728705897Sccardenas 			ret = vioscsi_handle_read_toc(dev, &req, &acct);
220895ab188fSccardenas 			break;
220995ab188fSccardenas 		case READ_CAPACITY:
221028705897Sccardenas 			ret = vioscsi_handle_read_capacity(dev, &req, &acct);
221195ab188fSccardenas 			break;
221295ab188fSccardenas 		case READ_CAPACITY_16:
221328705897Sccardenas 			ret = vioscsi_handle_read_capacity_16(dev, &req, &acct);
221495ab188fSccardenas 			break;
221595ab188fSccardenas 		case READ_COMMAND:
221628705897Sccardenas 			ret = vioscsi_handle_read_6(dev, &req, &acct);
221795ab188fSccardenas 			break;
2218eccd596dSkrw 		case READ_10:
221928705897Sccardenas 			ret = vioscsi_handle_read_10(dev, &req, &acct);
222095ab188fSccardenas 			break;
222195ab188fSccardenas 		case INQUIRY:
222228705897Sccardenas 			ret = vioscsi_handle_inquiry(dev, &req, &acct);
222395ab188fSccardenas 			break;
222495ab188fSccardenas 		case MODE_SENSE:
222528705897Sccardenas 			ret = vioscsi_handle_mode_sense(dev, &req, &acct);
222695ab188fSccardenas 			break;
222795ab188fSccardenas 		case MODE_SENSE_BIG:
222828705897Sccardenas 			ret = vioscsi_handle_mode_sense_big(dev, &req, &acct);
222995ab188fSccardenas 			break;
223095ab188fSccardenas 		case GET_EVENT_STATUS_NOTIFICATION:
223128705897Sccardenas 			ret = vioscsi_handle_gesn(dev, &req, &acct);
223295ab188fSccardenas 			break;
223395ab188fSccardenas 		case READ_DISC_INFORMATION:
223428705897Sccardenas 			ret = vioscsi_handle_read_disc_info(dev, &req, &acct);
223595ab188fSccardenas 			break;
223695ab188fSccardenas 		case GET_CONFIGURATION:
223728705897Sccardenas 			ret = vioscsi_handle_get_config(dev, &req, &acct);
223895ab188fSccardenas 			break;
223995ab188fSccardenas 		case MECHANISM_STATUS:
224028705897Sccardenas 			ret = vioscsi_handle_mechanism_status(dev, &req, &acct);
224195ab188fSccardenas 			break;
2242845b4456Sccardenas 		case REPORT_LUNS:
2243845b4456Sccardenas 			ret = vioscsi_handle_report_luns(dev, &req, &acct);
2244845b4456Sccardenas 			break;
224595ab188fSccardenas 		default:
224695ab188fSccardenas 			log_warnx("%s: unsupported opcode 0x%02x,%s",
224795ab188fSccardenas 			    __func__, req.cdb[0], vioscsi_op_names(req.cdb[0]));
224895ab188fSccardenas 			/* Move ring indexes */
224928705897Sccardenas 			vioscsi_next_ring_item(dev, acct.avail, acct.used,
225028705897Sccardenas 			    acct.req_desc, acct.req_idx);
225195ab188fSccardenas 			break;
225295ab188fSccardenas 		}
225395ab188fSccardenas next_msg:
225495ab188fSccardenas 		/* Increment to the next queue slot */
225528705897Sccardenas 		acct.idx = (acct.idx + 1) & VIOSCSI_QUEUE_MASK;
225695ab188fSccardenas 	}
225795ab188fSccardenas out:
225895ab188fSccardenas 	return (ret);
225995ab188fSccardenas }
2260