xref: /openbsd/usr.sbin/vmd/vioscsi.c (revision 65bbee46)
1*65bbee46Sjsg /*	$OpenBSD: vioscsi.c,v 1.25 2024/09/26 01:45:13 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 <stdlib.h>
2995ab188fSccardenas #include <string.h>
3095ab188fSccardenas 
3195ab188fSccardenas #include "vmd.h"
3295ab188fSccardenas #include "vioscsi.h"
3395ab188fSccardenas #include "virtio.h"
3495ab188fSccardenas 
3595ab188fSccardenas extern char *__progname;
3695ab188fSccardenas 
3795ab188fSccardenas static void
vioscsi_prepare_resp(struct virtio_scsi_res_hdr * resp,uint8_t vio_status,uint8_t scsi_status,uint8_t err_flags,uint8_t add_sense_code,uint8_t add_sense_code_qual)3895ab188fSccardenas vioscsi_prepare_resp(struct virtio_scsi_res_hdr *resp, uint8_t vio_status,
3995ab188fSccardenas     uint8_t scsi_status, uint8_t err_flags, uint8_t add_sense_code,
4095ab188fSccardenas     uint8_t add_sense_code_qual)
4195ab188fSccardenas {
4295ab188fSccardenas 	/* Set lower 8 bits of status and response fields */
4395ab188fSccardenas 	resp->response &= 0xFFFFFF00;
4495ab188fSccardenas 	resp->response |= vio_status;
4595ab188fSccardenas 	resp->status &= 0xFFFFFF00;
4695ab188fSccardenas 	resp->status |= scsi_status;
4795ab188fSccardenas 
4895ab188fSccardenas 	resp->sense_len = 0;
4995ab188fSccardenas 
5095ab188fSccardenas 	/* determine if we need to populate the sense field */
5195ab188fSccardenas 	if (scsi_status == SCSI_CHECK) {
5295ab188fSccardenas 		/*
5395ab188fSccardenas 		 * sense data is a 96 byte field.
5495ab188fSccardenas 		 * We only need to use the first 14 bytes
5595ab188fSccardenas 		 * - set the sense_len accordingly
5695ab188fSccardenas 		 * - set error_code to Current Command
5795ab188fSccardenas 		 * ref scsi/scsi_all.h:struct scsi_sense_data
5895ab188fSccardenas 		 */
5995ab188fSccardenas 		memset(resp->sense, 0, VIOSCSI_SENSE_LEN);
6095ab188fSccardenas 		resp->sense_len = RESP_SENSE_LEN;
6195ab188fSccardenas 		resp->sense[0] = SSD_ERRCODE_CURRENT;
6295ab188fSccardenas 		resp->sense[2] = err_flags;
6395ab188fSccardenas 		resp->sense[12] = add_sense_code;
6495ab188fSccardenas 		resp->sense[13] = add_sense_code_qual;
6595ab188fSccardenas 	}
6695ab188fSccardenas }
6795ab188fSccardenas 
6895ab188fSccardenas static struct vring_desc*
vioscsi_next_ring_desc(struct vring_desc * desc,struct vring_desc * cur,uint16_t * idx)6995ab188fSccardenas vioscsi_next_ring_desc(struct vring_desc* desc, struct vring_desc* cur,
7095ab188fSccardenas     uint16_t *idx)
7195ab188fSccardenas {
7295ab188fSccardenas 	*idx = cur->next & VIOSCSI_QUEUE_MASK;
7395ab188fSccardenas 	return &desc[*idx];
7495ab188fSccardenas }
7595ab188fSccardenas 
7695ab188fSccardenas static void
vioscsi_next_ring_item(struct vioscsi_dev * dev,struct vring_avail * avail,struct vring_used * used,struct vring_desc * desc,uint16_t idx)7795ab188fSccardenas vioscsi_next_ring_item(struct vioscsi_dev *dev, struct vring_avail *avail,
7895ab188fSccardenas     struct vring_used *used, struct vring_desc *desc, uint16_t idx)
7995ab188fSccardenas {
8095ab188fSccardenas 	used->ring[used->idx & VIOSCSI_QUEUE_MASK].id = idx;
8195ab188fSccardenas 	used->ring[used->idx & VIOSCSI_QUEUE_MASK].len = desc->len;
820bd10b9fSdv 	__sync_synchronize();
8395ab188fSccardenas 	used->idx++;
8495ab188fSccardenas 
8595ab188fSccardenas 	dev->vq[dev->cfg.queue_notify].last_avail =
8695ab188fSccardenas 	    avail->idx & VIOSCSI_QUEUE_MASK;
8795ab188fSccardenas }
8895ab188fSccardenas 
8995ab188fSccardenas static const char *
vioscsi_op_names(uint8_t type)9095ab188fSccardenas vioscsi_op_names(uint8_t type)
9195ab188fSccardenas {
9295ab188fSccardenas 	switch (type) {
9395ab188fSccardenas 	/* defined in scsi_all.h */
9495ab188fSccardenas 	case TEST_UNIT_READY: return "TEST_UNIT_READY";
9595ab188fSccardenas 	case REQUEST_SENSE: return "REQUEST_SENSE";
9695ab188fSccardenas 	case INQUIRY: return "INQUIRY";
9795ab188fSccardenas 	case MODE_SELECT: return "MODE_SELECT";
9895ab188fSccardenas 	case RESERVE: return "RESERVE";
9995ab188fSccardenas 	case RELEASE: return "RELEASE";
10095ab188fSccardenas 	case MODE_SENSE: return "MODE_SENSE";
10195ab188fSccardenas 	case START_STOP: return "START_STOP";
10295ab188fSccardenas 	case RECEIVE_DIAGNOSTIC: return "RECEIVE_DIAGNOSTIC";
10395ab188fSccardenas 	case SEND_DIAGNOSTIC: return "SEND_DIAGNOSTIC";
10495ab188fSccardenas 	case PREVENT_ALLOW: return "PREVENT_ALLOW";
10595ab188fSccardenas 	case POSITION_TO_ELEMENT: return "POSITION_TO_ELEMENT";
10695ab188fSccardenas 	case WRITE_BUFFER: return "WRITE_BUFFER";
10795ab188fSccardenas 	case READ_BUFFER: return "READ_BUFFER";
10895ab188fSccardenas 	case CHANGE_DEFINITION: return "CHANGE_DEFINITION";
10995ab188fSccardenas 	case MODE_SELECT_BIG: return "MODE_SELECT_BIG";
11095ab188fSccardenas 	case MODE_SENSE_BIG: return "MODE_SENSE_BIG";
11195ab188fSccardenas 	case REPORT_LUNS: return "REPORT_LUNS";
11295ab188fSccardenas 	/* defined in scsi_disk.h */
11395ab188fSccardenas 	case REASSIGN_BLOCKS: return "REASSIGN_BLOCKS";
11495ab188fSccardenas 	case READ_COMMAND: return "READ_COMMAND";
11595ab188fSccardenas 	case WRITE_COMMAND: return "WRITE_COMMAND";
11695ab188fSccardenas 	case READ_CAPACITY: return "READ_CAPACITY";
11795ab188fSccardenas 	case READ_CAPACITY_16: return "READ_CAPACITY_16";
118eccd596dSkrw 	case READ_10: return "READ_10";
119eccd596dSkrw 	case WRITE_10: return "WRITE_10";
12095ab188fSccardenas 	case READ_12: return "READ_12";
12195ab188fSccardenas 	case WRITE_12: return "WRITE_12";
12295ab188fSccardenas 	case READ_16: return "READ_16";
12395ab188fSccardenas 	case WRITE_16: return "WRITE_16";
12495ab188fSccardenas 	case SYNCHRONIZE_CACHE: return "SYNCHRONIZE_CACHE";
12595ab188fSccardenas 	case WRITE_SAME_10: return "WRITE_SAME_10";
12695ab188fSccardenas 	case WRITE_SAME_16: return "WRITE_SAME_16";
12795ab188fSccardenas 	/* defined in cd.h */
12895ab188fSccardenas 	case READ_SUBCHANNEL: return "READ_SUBCHANNEL";
12995ab188fSccardenas 	case READ_TOC: return "READ_TOC";
13095ab188fSccardenas 	case READ_HEADER: return "READ_HEADER";
13195ab188fSccardenas 	case PLAY: return "PLAY";
13295ab188fSccardenas 	case PLAY_MSF: return "PLAY_MSF";
13395ab188fSccardenas 	case PLAY_TRACK: return "PLAY_TRACK";
13495ab188fSccardenas 	case PLAY_TRACK_REL: return "PLAY_TRACK_REL";
13595ab188fSccardenas 	case PAUSE: return "PAUSE";
13695ab188fSccardenas 	case READ_TRACK_INFO: return "READ_TRACK_INFO";
13795ab188fSccardenas 	case CLOSE_TRACK: return "CLOSE_TRACK";
13895ab188fSccardenas 	case BLANK: return "BLANK";
13995ab188fSccardenas 	case PLAY_BIG: return "PLAY_BIG";
14095ab188fSccardenas 	case LOAD_UNLOAD: return "LOAD_UNLOAD";
14195ab188fSccardenas 	case PLAY_TRACK_REL_BIG: return "PLAY_TRACK_REL_BIG";
14295ab188fSccardenas 	case SET_CD_SPEED: return "SET_CD_SPEED";
14395ab188fSccardenas 	/* defined locally */
14495ab188fSccardenas 	case READ_DISC_INFORMATION: return "READ_DISC_INFORMATION";
14595ab188fSccardenas 	case GET_CONFIGURATION: return "GET_CONFIGURATION";
14695ab188fSccardenas 	case MECHANISM_STATUS: return "MECHANISM_STATUS";
14795ab188fSccardenas 	case GET_EVENT_STATUS_NOTIFICATION:
14895ab188fSccardenas 	    return "GET_EVENT_STATUS_NOTIFICATION";
14995ab188fSccardenas 	default: return "UNKNOWN";
15095ab188fSccardenas 	}
15195ab188fSccardenas }
15295ab188fSccardenas 
15395ab188fSccardenas static const char *
vioscsi_reg_name(uint8_t reg)15495ab188fSccardenas vioscsi_reg_name(uint8_t reg)
15595ab188fSccardenas {
15695ab188fSccardenas 	switch (reg) {
15795ab188fSccardenas 	case VIRTIO_CONFIG_DEVICE_FEATURES: return "device feature";
15895ab188fSccardenas 	case VIRTIO_CONFIG_GUEST_FEATURES: return "guest feature";
1590bd10b9fSdv 	case VIRTIO_CONFIG_QUEUE_PFN: return "queue pfn";
16095ab188fSccardenas 	case VIRTIO_CONFIG_QUEUE_SIZE: return "queue size";
16195ab188fSccardenas 	case VIRTIO_CONFIG_QUEUE_SELECT: return "queue select";
16295ab188fSccardenas 	case VIRTIO_CONFIG_QUEUE_NOTIFY: return "queue notify";
16395ab188fSccardenas 	case VIRTIO_CONFIG_DEVICE_STATUS: return "device status";
16495ab188fSccardenas 	case VIRTIO_CONFIG_ISR_STATUS: return "isr status";
16595ab188fSccardenas 	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI: return "num_queues";
16695ab188fSccardenas 	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 4: return "seg_max";
16795ab188fSccardenas 	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 8: return "max_sectors";
16895ab188fSccardenas 	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 12: return "cmd_per_lun";
16995ab188fSccardenas 	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 16: return "event_info_size";
17095ab188fSccardenas 	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 20: return "sense_size";
17195ab188fSccardenas 	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 24: return "cdb_size";
17295ab188fSccardenas 	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 28: return "max_channel";
17395ab188fSccardenas 	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 30: return "max_target";
17495ab188fSccardenas 	case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 32: return "max_lun";
17595ab188fSccardenas 	default: return "unknown";
17695ab188fSccardenas 	}
17795ab188fSccardenas }
17895ab188fSccardenas 
17995ab188fSccardenas static void
vioscsi_free_info(struct ioinfo * info)18095ab188fSccardenas vioscsi_free_info(struct ioinfo *info)
18195ab188fSccardenas {
18295ab188fSccardenas 	if (!info)
18395ab188fSccardenas 		return;
18495ab188fSccardenas 	free(info->buf);
18595ab188fSccardenas 	free(info);
18695ab188fSccardenas }
18795ab188fSccardenas 
18895ab188fSccardenas static struct ioinfo *
vioscsi_start_read(struct vioscsi_dev * dev,off_t block,size_t n_blocks)1890759b25cSdv vioscsi_start_read(struct vioscsi_dev *dev, off_t block, size_t n_blocks)
19095ab188fSccardenas {
19195ab188fSccardenas 	struct ioinfo *info;
19295ab188fSccardenas 
1930759b25cSdv 	/* Limit to 64M for now */
1940759b25cSdv 	if (n_blocks * VIOSCSI_BLOCK_SIZE_CDROM > (1 << 26)) {
1950759b25cSdv 		log_warnx("%s: read size exceeded 64M", __func__);
1960759b25cSdv 		return (NULL);
1970759b25cSdv 	}
1980759b25cSdv 
19995ab188fSccardenas 	info = calloc(1, sizeof(*info));
20095ab188fSccardenas 	if (!info)
20195ab188fSccardenas 		goto nomem;
20295ab188fSccardenas 	info->buf = malloc(n_blocks * VIOSCSI_BLOCK_SIZE_CDROM);
20395ab188fSccardenas 	if (info->buf == NULL)
20495ab188fSccardenas 		goto nomem;
20595ab188fSccardenas 	info->len = n_blocks * VIOSCSI_BLOCK_SIZE_CDROM;
20695ab188fSccardenas 	info->offset = block * VIOSCSI_BLOCK_SIZE_CDROM;
20795ab188fSccardenas 
20895ab188fSccardenas 	return info;
20995ab188fSccardenas 
21095ab188fSccardenas nomem:
21195ab188fSccardenas 	free(info);
212ab0aadc5Sccardenas 	log_warn("malloc error vioscsi read");
21395ab188fSccardenas 	return (NULL);
21495ab188fSccardenas }
21595ab188fSccardenas 
21695ab188fSccardenas static const uint8_t *
vioscsi_finish_read(struct vioscsi_dev * dev,struct ioinfo * info)2174d22b0bdSdv vioscsi_finish_read(struct vioscsi_dev *dev, struct ioinfo *info)
21895ab188fSccardenas {
2194d22b0bdSdv 	struct virtio_backing *f = &dev->file;
2209617633bSccardenas 
2219617633bSccardenas 	if (f->pread(f->p, info->buf, info->len, info->offset) != info->len) {
22295ab188fSccardenas 		log_warn("vioscsi read error");
22395ab188fSccardenas 		return NULL;
22495ab188fSccardenas 	}
22595ab188fSccardenas 
22695ab188fSccardenas 	return info->buf;
22795ab188fSccardenas }
22895ab188fSccardenas 
22928705897Sccardenas static int
vioscsi_handle_tur(struct vioscsi_dev * dev,struct virtio_scsi_req_hdr * req,struct virtio_vq_acct * acct)23028705897Sccardenas vioscsi_handle_tur(struct vioscsi_dev *dev, struct virtio_scsi_req_hdr *req,
23128705897Sccardenas     struct virtio_vq_acct *acct)
23228705897Sccardenas {
23328705897Sccardenas 	int ret = 0;
23428705897Sccardenas 	struct virtio_scsi_res_hdr resp;
23528705897Sccardenas 
23628705897Sccardenas 	memset(&resp, 0, sizeof(resp));
23728705897Sccardenas 	/* Move index for response */
23828705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
23928705897Sccardenas 	    &(acct->resp_idx));
24028705897Sccardenas 
24128705897Sccardenas 	vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
24228705897Sccardenas 
2430759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
24428705897Sccardenas 		log_warnx("%s: unable to write OK resp status data @ 0x%llx",
24528705897Sccardenas 		    __func__, acct->resp_desc->addr);
24628705897Sccardenas 	} else {
24728705897Sccardenas 		ret = 1;
24828705897Sccardenas 		dev->cfg.isr_status = 1;
24928705897Sccardenas 		/* Move ring indexes */
25028705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
25128705897Sccardenas 		    acct->req_desc, acct->req_idx);
25228705897Sccardenas 	}
25328705897Sccardenas 
25428705897Sccardenas 	return (ret);
25528705897Sccardenas }
25628705897Sccardenas 
25728705897Sccardenas static int
vioscsi_handle_inquiry(struct vioscsi_dev * dev,struct virtio_scsi_req_hdr * req,struct virtio_vq_acct * acct)25828705897Sccardenas vioscsi_handle_inquiry(struct vioscsi_dev *dev,
25928705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
26028705897Sccardenas {
26128705897Sccardenas 	int ret = 0;
26228705897Sccardenas 	struct virtio_scsi_res_hdr resp;
26328705897Sccardenas 	struct scsi_inquiry_data *inq_data;
26428705897Sccardenas 
26530dd31d2Sdv #if DEBUG
26630dd31d2Sdv 	struct scsi_inquiry *inq = (struct scsi_inquiry *)(req->cdb);
26730dd31d2Sdv 	log_debug("%s: INQ - EVPD %d PAGE_CODE 0x%08x LEN %d", __func__,
26830dd31d2Sdv 	    inq->flags & SI_EVPD, inq->pagecode, _2btol(inq->length));
26930dd31d2Sdv #endif /* DEBUG */
27030dd31d2Sdv 
27128705897Sccardenas 	memset(&resp, 0, sizeof(resp));
27228705897Sccardenas 	vioscsi_prepare_resp(&resp,
27328705897Sccardenas 	    VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
27428705897Sccardenas 
27528705897Sccardenas 	inq_data = calloc(1, sizeof(struct scsi_inquiry_data));
27628705897Sccardenas 
27728705897Sccardenas 	if (inq_data == NULL) {
27828705897Sccardenas 		log_warnx("%s: cannot alloc inq_data", __func__);
27928705897Sccardenas 		goto inq_out;
28028705897Sccardenas 	}
28128705897Sccardenas 
28228705897Sccardenas 	inq_data->device = T_CDROM;
28328705897Sccardenas 	inq_data->dev_qual2 = SID_REMOVABLE;
28428705897Sccardenas 	/* Leave version zero to say we don't comply */
285a1f20724Skrw 	inq_data->response_format = SID_SCSI2_RESPONSE;
28628705897Sccardenas 	inq_data->additional_length = SID_SCSI2_ALEN;
28728705897Sccardenas 	memcpy(inq_data->vendor, INQUIRY_VENDOR, INQUIRY_VENDOR_LEN);
28828705897Sccardenas 	memcpy(inq_data->product, INQUIRY_PRODUCT, INQUIRY_PRODUCT_LEN);
28928705897Sccardenas 	memcpy(inq_data->revision, INQUIRY_REVISION, INQUIRY_REVISION_LEN);
29028705897Sccardenas 
29128705897Sccardenas 	/* Move index for response */
29228705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
29328705897Sccardenas 	    &(acct->resp_idx));
29428705897Sccardenas 
2951f5e00e0Sreyk 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
29628705897Sccardenas 	    "idx %d req_idx %d global_idx %d", __func__, acct->resp_desc->addr,
29728705897Sccardenas 	    acct->resp_desc->len, acct->resp_idx, acct->req_idx, acct->idx);
29828705897Sccardenas 
2990759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
30028705897Sccardenas 		log_warnx("%s: unable to write OK resp status data @ 0x%llx",
30128705897Sccardenas 		    __func__, acct->resp_desc->addr);
30228705897Sccardenas 		goto free_inq;
30328705897Sccardenas 	}
30428705897Sccardenas 
30528705897Sccardenas 	/* Move index for inquiry_data */
30628705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
30728705897Sccardenas 	    &(acct->resp_idx));
30828705897Sccardenas 
3091f5e00e0Sreyk 	DPRINTF("%s: writing inq_data to 0x%llx size %d at "
31028705897Sccardenas 	    "local idx %d req_idx %d global_idx %d",
31128705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
31228705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
31328705897Sccardenas 
3140759b25cSdv 	if (write_mem(acct->resp_desc->addr, inq_data,
3150759b25cSdv 		sizeof(struct scsi_inquiry_data))) {
31628705897Sccardenas 		log_warnx("%s: unable to write inquiry"
31728705897Sccardenas 		    " response to gpa @ 0x%llx",
31828705897Sccardenas 		    __func__, acct->resp_desc->addr);
31928705897Sccardenas 	} else {
32028705897Sccardenas 		ret = 1;
32128705897Sccardenas 		dev->cfg.isr_status = 1;
32228705897Sccardenas 		/* Move ring indexes */
32328705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
32428705897Sccardenas 		    acct->req_desc, acct->req_idx);
32528705897Sccardenas 	}
32628705897Sccardenas 
32728705897Sccardenas free_inq:
32828705897Sccardenas 	free(inq_data);
32928705897Sccardenas inq_out:
33028705897Sccardenas 	return (ret);
33128705897Sccardenas }
33228705897Sccardenas 
33328705897Sccardenas static int
vioscsi_handle_mode_sense(struct vioscsi_dev * dev,struct virtio_scsi_req_hdr * req,struct virtio_vq_acct * acct)33428705897Sccardenas vioscsi_handle_mode_sense(struct vioscsi_dev *dev,
33528705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
33628705897Sccardenas {
33728705897Sccardenas 	int ret = 0;
33828705897Sccardenas 	struct virtio_scsi_res_hdr resp;
33928705897Sccardenas 	uint8_t mode_page_ctl;
34028705897Sccardenas 	uint8_t mode_page_code;
34128705897Sccardenas 	uint8_t *mode_reply;
3420759b25cSdv 	uint8_t mode_reply_len = 0;
34328705897Sccardenas 	struct scsi_mode_sense *mode_sense;
34428705897Sccardenas 
34528705897Sccardenas 	memset(&resp, 0, sizeof(resp));
34628705897Sccardenas 	mode_sense = (struct scsi_mode_sense *)(req->cdb);
34728705897Sccardenas 	mode_page_ctl = mode_sense->page & SMS_PAGE_CTRL;
34828705897Sccardenas 	mode_page_code = mode_sense->page & SMS_PAGE_CODE;
34928705897Sccardenas 
3502d03c861Sccardenas 	DPRINTF("%s: M_SENSE - DBD %d Page Ctrl 0x%x Code 0x%x Len %u",
35128705897Sccardenas 	    __func__, mode_sense->byte2 & SMS_DBD, mode_page_ctl,
35228705897Sccardenas 	    mode_page_code, mode_sense->length);
35328705897Sccardenas 
35428705897Sccardenas 	if (mode_page_ctl == SMS_PAGE_CTRL_CURRENT &&
35528705897Sccardenas 	    (mode_page_code == ERR_RECOVERY_PAGE ||
35628705897Sccardenas 	    mode_page_code == CDVD_CAPABILITIES_PAGE)) {
35728705897Sccardenas 		/*
35828705897Sccardenas 		 * mode sense header is 4 bytes followed
35928705897Sccardenas 		 * by a variable page
36028705897Sccardenas 		 * ERR_RECOVERY_PAGE is 12 bytes
3610759b25cSdv 		 * CDVD_CAPABILITIES_PAGE is 32 bytes
36228705897Sccardenas 		 */
36328705897Sccardenas 		switch (mode_page_code) {
36428705897Sccardenas 		case ERR_RECOVERY_PAGE:
36528705897Sccardenas 			mode_reply_len = 16;
36628705897Sccardenas 			mode_reply =
36728705897Sccardenas 			    (uint8_t*)calloc(mode_reply_len, sizeof(uint8_t));
36828705897Sccardenas 			if (mode_reply == NULL)
36928705897Sccardenas 				goto mode_sense_out;
37028705897Sccardenas 
37128705897Sccardenas 			/* set the page header */
37228705897Sccardenas 			*mode_reply = mode_reply_len - 1;
37328705897Sccardenas 			*(mode_reply + 1) = MODE_MEDIUM_TYPE_CODE;
37428705897Sccardenas 
37528705897Sccardenas 			/* set the page data, 7.3.2.1 mmc-5 */
37628705897Sccardenas 			*(mode_reply + 4) = MODE_ERR_RECOVERY_PAGE_CODE;
37728705897Sccardenas 			*(mode_reply + 5) = MODE_ERR_RECOVERY_PAGE_LEN;
37828705897Sccardenas 			*(mode_reply + 7) = MODE_READ_RETRY_COUNT;
37928705897Sccardenas 			break;
38028705897Sccardenas 		case CDVD_CAPABILITIES_PAGE:
3810759b25cSdv 			mode_reply_len = 36;
38228705897Sccardenas 			mode_reply =
38328705897Sccardenas 			    (uint8_t*)calloc(mode_reply_len, sizeof(uint8_t));
38428705897Sccardenas 			if (mode_reply == NULL)
38528705897Sccardenas 				goto mode_sense_out;
38628705897Sccardenas 
38728705897Sccardenas 			/* set the page header */
38828705897Sccardenas 			*mode_reply = mode_reply_len - 1;
38928705897Sccardenas 			*(mode_reply + 1) = MODE_MEDIUM_TYPE_CODE;
39028705897Sccardenas 
39128705897Sccardenas 			/* set the page data, 6.3.11 mmc-3 */
39228705897Sccardenas 			*(mode_reply + 4) = MODE_CDVD_CAP_PAGE_CODE;
39328705897Sccardenas 			*(mode_reply + 5) = mode_reply_len - 6;
39428705897Sccardenas 			*(mode_reply + 6) = MODE_CDVD_CAP_READ_CODE;
39528705897Sccardenas 			_lto2b(MODE_CDVD_CAP_NUM_LEVELS, mode_reply + 14);
39628705897Sccardenas 			break;
39728705897Sccardenas 		default:
39828705897Sccardenas 			goto mode_sense_error;
39928705897Sccardenas 			break;
40028705897Sccardenas 		}
40128705897Sccardenas 
40228705897Sccardenas 		/* Move index for response */
40328705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
40428705897Sccardenas 		    acct->req_desc, &(acct->resp_idx));
40528705897Sccardenas 
4061f5e00e0Sreyk 		DPRINTF("%s: writing resp to 0x%llx size %d "
40728705897Sccardenas 		    "at local idx %d req_idx %d global_idx %d",
4080759b25cSdv 		    __func__, acct->resp_desc->addr, mode_reply_len,
40928705897Sccardenas 		    acct->resp_idx, acct->req_idx, acct->idx);
41028705897Sccardenas 
4110759b25cSdv 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
41228705897Sccardenas 			log_warnx("%s: unable to write OK"
41328705897Sccardenas 			    " resp status data @ 0x%llx",
41428705897Sccardenas 			    __func__, acct->resp_desc->addr);
41528705897Sccardenas 			free(mode_reply);
41628705897Sccardenas 			goto mode_sense_out;
41728705897Sccardenas 		}
41828705897Sccardenas 
41928705897Sccardenas 		/* Move index for mode_reply */
42028705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
42128705897Sccardenas 		    acct->resp_desc, &(acct->resp_idx));
42228705897Sccardenas 
4231f5e00e0Sreyk 		DPRINTF("%s: writing mode_reply to 0x%llx "
42428705897Sccardenas 		    "size %d at local idx %d req_idx %d "
42528705897Sccardenas 		    "global_idx %d", __func__, acct->resp_desc->addr,
4260759b25cSdv 		    mode_reply_len, acct->resp_idx, acct->req_idx, acct->idx);
42728705897Sccardenas 
42828705897Sccardenas 		if (write_mem(acct->resp_desc->addr, mode_reply,
4290759b25cSdv 			mode_reply_len)) {
43028705897Sccardenas 			log_warnx("%s: unable to write "
43128705897Sccardenas 			    "mode_reply to gpa @ 0x%llx",
43228705897Sccardenas 			    __func__, acct->resp_desc->addr);
43328705897Sccardenas 			free(mode_reply);
43428705897Sccardenas 			goto mode_sense_out;
43528705897Sccardenas 		}
43628705897Sccardenas 
43728705897Sccardenas 		free(mode_reply);
43828705897Sccardenas 
43928705897Sccardenas 		ret = 1;
44028705897Sccardenas 		dev->cfg.isr_status = 1;
44128705897Sccardenas 		/* Move ring indexes */
44228705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
44328705897Sccardenas 		    acct->req_desc, acct->req_idx);
44428705897Sccardenas 	} else {
44528705897Sccardenas mode_sense_error:
44628705897Sccardenas 		/* send back un-supported */
44728705897Sccardenas 		vioscsi_prepare_resp(&resp,
44828705897Sccardenas 		    VIRTIO_SCSI_S_OK, SCSI_CHECK, SKEY_ILLEGAL_REQUEST,
44928705897Sccardenas 		    SENSE_ILLEGAL_CDB_FIELD, SENSE_DEFAULT_ASCQ);
45028705897Sccardenas 
45128705897Sccardenas 		/* Move index for response */
45228705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
45328705897Sccardenas 		    acct->req_desc, &(acct->resp_idx));
45428705897Sccardenas 
4550759b25cSdv 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
45628705897Sccardenas 			log_warnx("%s: unable to set ERR status data @ 0x%llx",
45728705897Sccardenas 			    __func__, acct->resp_desc->addr);
45828705897Sccardenas 			goto mode_sense_out;
45928705897Sccardenas 		}
46028705897Sccardenas 
46128705897Sccardenas 		ret = 1;
46228705897Sccardenas 		dev->cfg.isr_status = 1;
46328705897Sccardenas 		/* Move ring indexes */
46428705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
46528705897Sccardenas 		    acct->req_desc, acct->req_idx);
46628705897Sccardenas 	}
46728705897Sccardenas mode_sense_out:
46828705897Sccardenas 	return (ret);
46928705897Sccardenas }
47028705897Sccardenas 
47128705897Sccardenas static int
vioscsi_handle_mode_sense_big(struct vioscsi_dev * dev,struct virtio_scsi_req_hdr * req,struct virtio_vq_acct * acct)47228705897Sccardenas vioscsi_handle_mode_sense_big(struct vioscsi_dev *dev,
47328705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
47428705897Sccardenas {
47528705897Sccardenas 	int ret = 0;
47628705897Sccardenas 	struct virtio_scsi_res_hdr resp;
47728705897Sccardenas 	uint8_t mode_page_ctl;
47828705897Sccardenas 	uint8_t mode_page_code;
47928705897Sccardenas 	uint8_t *mode_reply;
4800759b25cSdv 	uint8_t mode_reply_len = 0;
48128705897Sccardenas 	struct scsi_mode_sense_big *mode_sense_10;
48228705897Sccardenas 
48328705897Sccardenas 	memset(&resp, 0, sizeof(resp));
48428705897Sccardenas 	mode_sense_10 = (struct scsi_mode_sense_big *)(req->cdb);
48528705897Sccardenas 	mode_page_ctl = mode_sense_10->page & SMS_PAGE_CTRL;
48628705897Sccardenas 	mode_page_code = mode_sense_10->page & SMS_PAGE_CODE;
48728705897Sccardenas 
4882d03c861Sccardenas 	DPRINTF("%s: M_SENSE_10 - DBD %d Page Ctrl 0x%x Code 0x%x Len %u",
48928705897Sccardenas 	    __func__, mode_sense_10->byte2 & SMS_DBD, mode_page_ctl,
49030dd31d2Sdv 	    mode_page_code, (uint16_t)_2btol(mode_sense_10->length));
49128705897Sccardenas 
49228705897Sccardenas 	if (mode_page_ctl == SMS_PAGE_CTRL_CURRENT &&
49328705897Sccardenas 	    (mode_page_code == ERR_RECOVERY_PAGE ||
49428705897Sccardenas 	    mode_page_code == CDVD_CAPABILITIES_PAGE)) {
49528705897Sccardenas 		/*
49628705897Sccardenas 		 * mode sense header is 8 bytes followed
49728705897Sccardenas 		 * by a variable page
49828705897Sccardenas 		 * ERR_RECOVERY_PAGE is 12 bytes
4990759b25cSdv 		 * CDVD_CAPABILITIES_PAGE is 32 bytes
50028705897Sccardenas 		 */
50128705897Sccardenas 		switch (mode_page_code) {
50228705897Sccardenas 		case ERR_RECOVERY_PAGE:
50328705897Sccardenas 			mode_reply_len = 20;
50428705897Sccardenas 			mode_reply =
50528705897Sccardenas 			    (uint8_t*)calloc(mode_reply_len, sizeof(uint8_t));
50628705897Sccardenas 			if (mode_reply == NULL)
50728705897Sccardenas 				goto mode_sense_big_out;
50828705897Sccardenas 
50928705897Sccardenas 			/* set the page header */
51028705897Sccardenas 			_lto2b(mode_reply_len - 2, mode_reply);
51128705897Sccardenas 			*(mode_reply + 2) = MODE_MEDIUM_TYPE_CODE;
51228705897Sccardenas 
51328705897Sccardenas 			/* set the page data, 7.3.2.1 mmc-5 */
51428705897Sccardenas 			*(mode_reply + 8) = MODE_ERR_RECOVERY_PAGE_CODE;
51528705897Sccardenas 			*(mode_reply + 9) = MODE_ERR_RECOVERY_PAGE_LEN;
51628705897Sccardenas 			*(mode_reply + 11) = MODE_READ_RETRY_COUNT;
51728705897Sccardenas 			break;
51828705897Sccardenas 		case CDVD_CAPABILITIES_PAGE:
5190759b25cSdv 			mode_reply_len = 40;
52028705897Sccardenas 			mode_reply =
52128705897Sccardenas 			    (uint8_t*)calloc(mode_reply_len, sizeof(uint8_t));
52228705897Sccardenas 			if (mode_reply == NULL)
52328705897Sccardenas 				goto mode_sense_big_out;
52428705897Sccardenas 
52528705897Sccardenas 			/* set the page header */
52628705897Sccardenas 			_lto2b(mode_reply_len - 2, mode_reply);
52728705897Sccardenas 			*(mode_reply + 2) = MODE_MEDIUM_TYPE_CODE;
52828705897Sccardenas 
52928705897Sccardenas 			/* set the page data, 6.3.11 mmc-3 */
53028705897Sccardenas 			*(mode_reply + 8) = MODE_CDVD_CAP_PAGE_CODE;
53128705897Sccardenas 			*(mode_reply + 9) = mode_reply_len - 6;
53228705897Sccardenas 			*(mode_reply + 10) = MODE_CDVD_CAP_READ_CODE;
53328705897Sccardenas 			_lto2b(MODE_CDVD_CAP_NUM_LEVELS, mode_reply + 18);
53428705897Sccardenas 			break;
53528705897Sccardenas 		default:
53628705897Sccardenas 			goto mode_sense_big_error;
53728705897Sccardenas 			break;
53828705897Sccardenas 		}
53928705897Sccardenas 
54028705897Sccardenas 		/* Move index for response */
54128705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
54228705897Sccardenas 		    acct->req_desc, &(acct->resp_idx));
54328705897Sccardenas 
5441f5e00e0Sreyk 		DPRINTF("%s: writing resp to 0x%llx size %d "
54528705897Sccardenas 		    "at local idx %d req_idx %d global_idx %d",
54628705897Sccardenas 		    __func__, acct->resp_desc->addr, acct->resp_desc->len,
54728705897Sccardenas 		    acct->resp_idx, acct->req_idx, acct->idx);
54828705897Sccardenas 
5490759b25cSdv 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
55028705897Sccardenas 			log_warnx("%s: unable to write OK"
55128705897Sccardenas 			    " resp status data @ 0x%llx",
55228705897Sccardenas 			    __func__, acct->resp_desc->addr);
55328705897Sccardenas 			free(mode_reply);
55428705897Sccardenas 			goto mode_sense_big_out;
55528705897Sccardenas 		}
55628705897Sccardenas 
55728705897Sccardenas 		/* Move index for mode_reply */
55828705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
55928705897Sccardenas 		    acct->resp_desc, &(acct->resp_idx));
56028705897Sccardenas 
5611f5e00e0Sreyk 		DPRINTF("%s: writing mode_reply to 0x%llx "
56228705897Sccardenas 		    "size %d at local idx %d req_idx %d global_idx %d",
5630759b25cSdv 		    __func__, acct->resp_desc->addr, mode_reply_len,
56428705897Sccardenas 		    acct->resp_idx, acct->req_idx, acct->idx);
56528705897Sccardenas 
56628705897Sccardenas 		if (write_mem(acct->resp_desc->addr, mode_reply,
5670759b25cSdv 			mode_reply_len)) {
56828705897Sccardenas 			log_warnx("%s: unable to write "
56928705897Sccardenas 			    "mode_reply to gpa @ 0x%llx",
57028705897Sccardenas 			    __func__, acct->resp_desc->addr);
57128705897Sccardenas 			free(mode_reply);
57228705897Sccardenas 			goto mode_sense_big_out;
57328705897Sccardenas 		}
57428705897Sccardenas 
57528705897Sccardenas 		free(mode_reply);
57628705897Sccardenas 
57728705897Sccardenas 		ret = 1;
57828705897Sccardenas 		dev->cfg.isr_status = 1;
57928705897Sccardenas 		/* Move ring indexes */
58028705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
58128705897Sccardenas 		    acct->req_desc, acct->req_idx);
58228705897Sccardenas 	} else {
58328705897Sccardenas mode_sense_big_error:
58428705897Sccardenas 		/* send back un-supported */
58528705897Sccardenas 		vioscsi_prepare_resp(&resp,
58628705897Sccardenas 		    VIRTIO_SCSI_S_OK, SCSI_CHECK, SKEY_ILLEGAL_REQUEST,
58728705897Sccardenas 		    SENSE_ILLEGAL_CDB_FIELD, SENSE_DEFAULT_ASCQ);
58828705897Sccardenas 
58928705897Sccardenas 		/* Move index for response */
59028705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
59128705897Sccardenas 		    acct->req_desc, &(acct->resp_idx));
59228705897Sccardenas 
5930759b25cSdv 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
59428705897Sccardenas 			log_warnx("%s: unable to set ERR status data @ 0x%llx",
59528705897Sccardenas 			    __func__, acct->resp_desc->addr);
59628705897Sccardenas 			goto mode_sense_big_out;
59728705897Sccardenas 		}
59828705897Sccardenas 
59928705897Sccardenas 		ret = 1;
60028705897Sccardenas 		dev->cfg.isr_status = 1;
60128705897Sccardenas 		/* Move ring indexes */
60228705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
60328705897Sccardenas 		    acct->req_desc, acct->req_idx);
60428705897Sccardenas 	}
60528705897Sccardenas mode_sense_big_out:
60628705897Sccardenas 	return (ret);
60728705897Sccardenas }
60828705897Sccardenas 
60928705897Sccardenas static int
vioscsi_handle_read_capacity(struct vioscsi_dev * dev,struct virtio_scsi_req_hdr * req,struct virtio_vq_acct * acct)61028705897Sccardenas vioscsi_handle_read_capacity(struct vioscsi_dev *dev,
61128705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
61228705897Sccardenas {
61328705897Sccardenas 	int ret = 0;
61428705897Sccardenas 	struct virtio_scsi_res_hdr resp;
61528705897Sccardenas 	struct scsi_read_cap_data *r_cap_data;
61628705897Sccardenas 
61730dd31d2Sdv #if DEBUG
61830dd31d2Sdv 	struct scsi_read_capacity *r_cap =
61930dd31d2Sdv 	    (struct scsi_read_capacity *)(req->cdb);
62030dd31d2Sdv 	log_debug("%s: %s - Addr 0x%08x", __func__,
62130dd31d2Sdv 	    vioscsi_op_names(r_cap->opcode), _4btol(r_cap->addr));
62230dd31d2Sdv #endif /* DEBUG */
62328705897Sccardenas 
62430dd31d2Sdv 	memset(&resp, 0, sizeof(resp));
62528705897Sccardenas 	vioscsi_prepare_resp(&resp,
62628705897Sccardenas 	    VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
62728705897Sccardenas 
62828705897Sccardenas 	r_cap_data = calloc(1, sizeof(struct scsi_read_cap_data));
62928705897Sccardenas 
63028705897Sccardenas 	if (r_cap_data == NULL) {
63128705897Sccardenas 		log_warnx("%s: cannot alloc r_cap_data", __func__);
63228705897Sccardenas 		goto read_capacity_out;
63328705897Sccardenas 	}
63428705897Sccardenas 
6352d03c861Sccardenas 	DPRINTF("%s: ISO has %lld bytes and %lld blocks",
63628705897Sccardenas 	    __func__, dev->sz, dev->n_blocks);
63728705897Sccardenas 
63828705897Sccardenas 	/*
639a9eba918Sccardenas 	 * determine if num blocks of iso image > UINT32_MAX
64028705897Sccardenas 	 * if it is, set addr to UINT32_MAX (0xffffffff)
64128705897Sccardenas 	 * indicating to hosts that READ_CAPACITY_16 should
64228705897Sccardenas 	 * be called to retrieve the full size
64328705897Sccardenas 	 */
644a9eba918Sccardenas 	if (dev->n_blocks >= UINT32_MAX) {
64528705897Sccardenas 		_lto4b(UINT32_MAX, r_cap_data->addr);
64628705897Sccardenas 		_lto4b(VIOSCSI_BLOCK_SIZE_CDROM, r_cap_data->length);
64728705897Sccardenas 		log_warnx("%s: ISO sz %lld is bigger than "
64828705897Sccardenas 		    "UINT32_MAX %u, all data may not be read",
64928705897Sccardenas 		    __func__, dev->sz, UINT32_MAX);
65028705897Sccardenas 	} else {
65128705897Sccardenas 		_lto4b(dev->n_blocks - 1, r_cap_data->addr);
65228705897Sccardenas 		_lto4b(VIOSCSI_BLOCK_SIZE_CDROM, r_cap_data->length);
65328705897Sccardenas 	}
65428705897Sccardenas 
65528705897Sccardenas 	/* Move index for response */
65628705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
65728705897Sccardenas 	    &(acct->resp_idx));
65828705897Sccardenas 
6591f5e00e0Sreyk 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
66028705897Sccardenas 	    "idx %d req_idx %d global_idx %d",
66128705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
66228705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
66328705897Sccardenas 
6640759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
66528705897Sccardenas 		log_warnx("%s: unable to write OK resp status data @ 0x%llx",
66628705897Sccardenas 		    __func__, acct->resp_desc->addr);
66728705897Sccardenas 		goto free_read_capacity;
66828705897Sccardenas 	}
66928705897Sccardenas 
67028705897Sccardenas 	/* Move index for r_cap_data */
67128705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
67228705897Sccardenas 	    &(acct->resp_idx));
67328705897Sccardenas 
6741f5e00e0Sreyk 	DPRINTF("%s: writing r_cap_data to 0x%llx size %d at "
67528705897Sccardenas 	    "local idx %d req_idx %d global_idx %d",
67628705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
67728705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
67828705897Sccardenas 
67928705897Sccardenas 	if (write_mem(acct->resp_desc->addr, r_cap_data,
6800759b25cSdv 		sizeof(struct scsi_read_cap_data))) {
68128705897Sccardenas 		log_warnx("%s: unable to write read_cap_data"
68228705897Sccardenas 		    " response to gpa @ 0x%llx",
68328705897Sccardenas 		    __func__, acct->resp_desc->addr);
68428705897Sccardenas 	} else {
68528705897Sccardenas 		ret = 1;
68628705897Sccardenas 		dev->cfg.isr_status = 1;
68728705897Sccardenas 		/* Move ring indexes */
68828705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
68928705897Sccardenas 		    acct->req_desc, acct->req_idx);
69028705897Sccardenas 	}
69128705897Sccardenas 
69228705897Sccardenas free_read_capacity:
69328705897Sccardenas 	free(r_cap_data);
69428705897Sccardenas read_capacity_out:
69528705897Sccardenas 	return (ret);
69628705897Sccardenas }
69728705897Sccardenas 
69828705897Sccardenas static int
vioscsi_handle_read_capacity_16(struct vioscsi_dev * dev,struct virtio_scsi_req_hdr * req,struct virtio_vq_acct * acct)69928705897Sccardenas vioscsi_handle_read_capacity_16(struct vioscsi_dev *dev,
70028705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
70128705897Sccardenas {
70228705897Sccardenas 	int ret = 0;
70328705897Sccardenas 	struct virtio_scsi_res_hdr resp;
70428705897Sccardenas 	struct scsi_read_cap_data_16 *r_cap_data_16;
70528705897Sccardenas 
70630dd31d2Sdv #if DEBUG
70730dd31d2Sdv 	struct scsi_read_capacity_16 *r_cap_16 =
70830dd31d2Sdv 	    (struct scsi_read_capacity_16 *)(req->cdb);
70930dd31d2Sdv 	log_debug("%s: %s - Addr 0x%016llx", __func__,
71030dd31d2Sdv 	    vioscsi_op_names(r_cap_16->opcode), _8btol(r_cap_16->addr));
71130dd31d2Sdv #endif /* DEBUG */
71228705897Sccardenas 
71330dd31d2Sdv 	memset(&resp, 0, sizeof(resp));
71428705897Sccardenas 	vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
71528705897Sccardenas 
71628705897Sccardenas 	r_cap_data_16 = calloc(1, sizeof(struct scsi_read_cap_data_16));
71728705897Sccardenas 
71828705897Sccardenas 	if (r_cap_data_16 == NULL) {
71928705897Sccardenas 		log_warnx("%s: cannot alloc r_cap_data_16",
72028705897Sccardenas 		    __func__);
72128705897Sccardenas 		goto read_capacity_16_out;
72228705897Sccardenas 	}
72328705897Sccardenas 
7242d03c861Sccardenas 	DPRINTF("%s: ISO has %lld bytes and %lld blocks", __func__,
72528705897Sccardenas 	    dev->sz, dev->n_blocks);
72628705897Sccardenas 
72728705897Sccardenas 	_lto8b(dev->n_blocks - 1, r_cap_data_16->addr);
72828705897Sccardenas 	_lto4b(VIOSCSI_BLOCK_SIZE_CDROM, r_cap_data_16->length);
72928705897Sccardenas 
73028705897Sccardenas 	/* Move index for response */
73128705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
73228705897Sccardenas 	    &(acct->resp_idx));
73328705897Sccardenas 
7341f5e00e0Sreyk 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
73528705897Sccardenas 	    "idx %d req_idx %d global_idx %d",
73628705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
73728705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
73828705897Sccardenas 
7390759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
74028705897Sccardenas 		log_warnx("%s: unable to write OK resp status "
74128705897Sccardenas 		    "data @ 0x%llx", __func__, acct->resp_desc->addr);
74228705897Sccardenas 		goto free_read_capacity_16;
74328705897Sccardenas 	}
74428705897Sccardenas 
74528705897Sccardenas 	/* Move index for r_cap_data_16 */
74628705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
74728705897Sccardenas 	    &(acct->resp_idx));
74828705897Sccardenas 
7491f5e00e0Sreyk 	DPRINTF("%s: writing r_cap_data_16 to 0x%llx size %d "
75028705897Sccardenas 	    "at local idx %d req_idx %d global_idx %d",
75128705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
75228705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
75328705897Sccardenas 
75428705897Sccardenas 	if (write_mem(acct->resp_desc->addr, r_cap_data_16,
7550759b25cSdv 		sizeof(struct scsi_read_cap_data_16))) {
75628705897Sccardenas 		log_warnx("%s: unable to write read_cap_data_16"
75728705897Sccardenas 		    " response to gpa @ 0x%llx",
75828705897Sccardenas 		    __func__, acct->resp_desc->addr);
75928705897Sccardenas 	} else {
76028705897Sccardenas 		ret = 1;
76128705897Sccardenas 		dev->cfg.isr_status = 1;
76228705897Sccardenas 		/* Move ring indexes */
76328705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
76428705897Sccardenas 		    acct->req_desc, acct->req_idx);
76528705897Sccardenas 	}
76628705897Sccardenas 
76728705897Sccardenas free_read_capacity_16:
76828705897Sccardenas 	free(r_cap_data_16);
76928705897Sccardenas read_capacity_16_out:
77028705897Sccardenas 	return (ret);
77128705897Sccardenas }
77228705897Sccardenas 
77328705897Sccardenas static int
vioscsi_handle_report_luns(struct vioscsi_dev * dev,struct virtio_scsi_req_hdr * req,struct virtio_vq_acct * acct)774845b4456Sccardenas vioscsi_handle_report_luns(struct vioscsi_dev *dev,
775845b4456Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
776845b4456Sccardenas {
777845b4456Sccardenas 	int ret = 0;
778845b4456Sccardenas 	struct virtio_scsi_res_hdr resp;
779845b4456Sccardenas 	uint32_t rpl_length;
780845b4456Sccardenas 	struct scsi_report_luns *rpl;
781845b4456Sccardenas 	struct vioscsi_report_luns_data *reply_rpl;
782845b4456Sccardenas 
783845b4456Sccardenas 	memset(&resp, 0, sizeof(resp));
784845b4456Sccardenas 	rpl = (struct scsi_report_luns *)(req->cdb);
785845b4456Sccardenas 	rpl_length = _4btol(rpl->length);
786845b4456Sccardenas 
7872d03c861Sccardenas 	DPRINTF("%s: REPORT_LUNS Report 0x%x Length %d", __func__,
788845b4456Sccardenas 	    rpl->selectreport, rpl_length);
789845b4456Sccardenas 
790845b4456Sccardenas 	if (rpl_length < RPL_MIN_SIZE) {
7912d03c861Sccardenas 		DPRINTF("%s: RPL_Length %d < %d (RPL_MIN_SIZE)", __func__,
792845b4456Sccardenas 		    rpl_length, RPL_MIN_SIZE);
793845b4456Sccardenas 
794845b4456Sccardenas 		vioscsi_prepare_resp(&resp,
795845b4456Sccardenas 		    VIRTIO_SCSI_S_OK, SCSI_CHECK, SKEY_ILLEGAL_REQUEST,
796845b4456Sccardenas 		    SENSE_ILLEGAL_CDB_FIELD, SENSE_DEFAULT_ASCQ);
797845b4456Sccardenas 
798845b4456Sccardenas 		/* Move index for response */
799845b4456Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
800845b4456Sccardenas 		    acct->req_desc, &(acct->resp_idx));
801845b4456Sccardenas 
8020759b25cSdv 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
803845b4456Sccardenas 			log_warnx("%s: unable to set ERR "
804845b4456Sccardenas 			    "status data @ 0x%llx", __func__,
805845b4456Sccardenas 			    acct->resp_desc->addr);
806845b4456Sccardenas 		} else {
807845b4456Sccardenas 			ret = 1;
808845b4456Sccardenas 			dev->cfg.isr_status = 1;
809845b4456Sccardenas 			/* Move ring indexes */
810845b4456Sccardenas 			vioscsi_next_ring_item(dev, acct->avail, acct->used,
811845b4456Sccardenas 			    acct->req_desc, acct->req_idx);
812845b4456Sccardenas 		}
813845b4456Sccardenas 		goto rpl_out;
814845b4456Sccardenas 
815845b4456Sccardenas 	}
816845b4456Sccardenas 
8170759b25cSdv 	reply_rpl = calloc(1, sizeof(struct vioscsi_report_luns_data));
818845b4456Sccardenas 
819845b4456Sccardenas 	if (reply_rpl == NULL) {
820845b4456Sccardenas 		log_warnx("%s: cannot alloc reply_rpl", __func__);
821845b4456Sccardenas 		goto rpl_out;
822845b4456Sccardenas 	}
823845b4456Sccardenas 
824845b4456Sccardenas 	_lto4b(RPL_SINGLE_LUN, reply_rpl->length);
825845b4456Sccardenas 	memcpy(reply_rpl->lun, req->lun, RPL_SINGLE_LUN);
826845b4456Sccardenas 
827845b4456Sccardenas 	vioscsi_prepare_resp(&resp,
828845b4456Sccardenas 	    VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
829845b4456Sccardenas 
830845b4456Sccardenas 	/* Move index for response */
831845b4456Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
832845b4456Sccardenas 	    &(acct->resp_idx));
833845b4456Sccardenas 
8341f5e00e0Sreyk 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
835845b4456Sccardenas 	    "idx %d req_idx %d global_idx %d", __func__, acct->resp_desc->addr,
836845b4456Sccardenas 	    acct->resp_desc->len, acct->resp_idx, acct->req_idx, acct->idx);
837845b4456Sccardenas 
8380759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
839845b4456Sccardenas 		log_warnx("%s: unable to write OK resp status data @ 0x%llx",
840845b4456Sccardenas 		    __func__, acct->resp_desc->addr);
841845b4456Sccardenas 		goto free_rpl;
842845b4456Sccardenas 	}
843845b4456Sccardenas 
844845b4456Sccardenas 	/* Move index for reply_rpl */
845845b4456Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
846845b4456Sccardenas 	    &(acct->resp_idx));
847845b4456Sccardenas 
8481f5e00e0Sreyk 	DPRINTF("%s: writing reply_rpl to 0x%llx size %d at "
849845b4456Sccardenas 	    "local idx %d req_idx %d global_idx %d",
850845b4456Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
851845b4456Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
852845b4456Sccardenas 
8530759b25cSdv 	if (write_mem(acct->resp_desc->addr, reply_rpl,
8540759b25cSdv 		sizeof(struct vioscsi_report_luns_data))) {
855845b4456Sccardenas 		log_warnx("%s: unable to write reply_rpl"
856845b4456Sccardenas 		    " response to gpa @ 0x%llx",
857845b4456Sccardenas 		    __func__, acct->resp_desc->addr);
858845b4456Sccardenas 	} else {
859845b4456Sccardenas 		ret = 1;
860845b4456Sccardenas 		dev->cfg.isr_status = 1;
861845b4456Sccardenas 		/* Move ring indexes */
862845b4456Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
863845b4456Sccardenas 		    acct->req_desc, acct->req_idx);
864845b4456Sccardenas 	}
865845b4456Sccardenas 
866845b4456Sccardenas free_rpl:
867845b4456Sccardenas 	free(reply_rpl);
868845b4456Sccardenas rpl_out:
869845b4456Sccardenas 	return (ret);
870845b4456Sccardenas }
871845b4456Sccardenas 
872845b4456Sccardenas static int
vioscsi_handle_read_6(struct vioscsi_dev * dev,struct virtio_scsi_req_hdr * req,struct virtio_vq_acct * acct)87328705897Sccardenas vioscsi_handle_read_6(struct vioscsi_dev *dev,
87428705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
87528705897Sccardenas {
87628705897Sccardenas 	int ret = 0;
87728705897Sccardenas 	struct virtio_scsi_res_hdr resp;
87828705897Sccardenas 	const uint8_t *read_buf;
87928705897Sccardenas 	uint32_t read_lba;
88028705897Sccardenas 	struct ioinfo *info;
88128705897Sccardenas 	struct scsi_rw *read_6;
88228705897Sccardenas 
88328705897Sccardenas 	memset(&resp, 0, sizeof(resp));
88428705897Sccardenas 	read_6 = (struct scsi_rw *)(req->cdb);
88528705897Sccardenas 	read_lba = ((read_6->addr[0] & SRW_TOPADDR) << 16 ) |
88628705897Sccardenas 	    (read_6->addr[1] << 8) | read_6->addr[2];
88728705897Sccardenas 
8882d03c861Sccardenas 	DPRINTF("%s: READ Addr 0x%08x Len %d (%d)",
88928705897Sccardenas 	    __func__, read_lba, read_6->length, read_6->length * dev->max_xfer);
89028705897Sccardenas 
89128705897Sccardenas 	/* check if lba is in range */
89228705897Sccardenas 	if (read_lba > dev->n_blocks - 1) {
8932d03c861Sccardenas 		DPRINTF("%s: requested block out of range req: %ud max: %lld",
89428705897Sccardenas 		    __func__, read_lba, dev->n_blocks);
89528705897Sccardenas 
89628705897Sccardenas 		vioscsi_prepare_resp(&resp,
89728705897Sccardenas 		    VIRTIO_SCSI_S_OK, SCSI_CHECK, SKEY_ILLEGAL_REQUEST,
89828705897Sccardenas 		    SENSE_LBA_OUT_OF_RANGE, SENSE_DEFAULT_ASCQ);
89928705897Sccardenas 
90028705897Sccardenas 		/* Move index for response */
90128705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
90228705897Sccardenas 		    acct->req_desc, &(acct->resp_idx));
90328705897Sccardenas 
9040759b25cSdv 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
90528705897Sccardenas 			log_warnx("%s: unable to set ERR "
90628705897Sccardenas 			    "status data @ 0x%llx", __func__,
90728705897Sccardenas 			    acct->resp_desc->addr);
90828705897Sccardenas 		} else {
90928705897Sccardenas 			ret = 1;
91028705897Sccardenas 			dev->cfg.isr_status = 1;
91128705897Sccardenas 			/* Move ring indexes */
91228705897Sccardenas 			vioscsi_next_ring_item(dev, acct->avail, acct->used,
91328705897Sccardenas 			    acct->req_desc, acct->req_idx);
91428705897Sccardenas 		}
91528705897Sccardenas 		goto read_6_out;
91628705897Sccardenas 	}
91728705897Sccardenas 
91828705897Sccardenas 	info = vioscsi_start_read(dev, read_lba, read_6->length);
91928705897Sccardenas 
92028705897Sccardenas 	if (info == NULL) {
92128705897Sccardenas 		log_warnx("%s: cannot alloc for read", __func__);
92228705897Sccardenas 		goto read_6_out;
92328705897Sccardenas 	}
92428705897Sccardenas 
92528705897Sccardenas 	/* read block */
9264d22b0bdSdv 	read_buf = vioscsi_finish_read(dev, info);
92728705897Sccardenas 
92828705897Sccardenas 	if (read_buf == NULL) {
92928705897Sccardenas 		log_warnx("%s: error reading position %ud",
93028705897Sccardenas 		    __func__, read_lba);
93128705897Sccardenas 		vioscsi_prepare_resp(&resp,
93228705897Sccardenas 		    VIRTIO_SCSI_S_OK, SCSI_CHECK, SKEY_MEDIUM_ERROR,
93328705897Sccardenas 		    SENSE_MEDIUM_NOT_PRESENT, SENSE_DEFAULT_ASCQ);
93428705897Sccardenas 
93528705897Sccardenas 		/* Move index for response */
93628705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
93728705897Sccardenas 		    acct->req_desc, &(acct->resp_idx));
93828705897Sccardenas 
9390759b25cSdv 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
94028705897Sccardenas 			log_warnx("%s: unable to set ERR "
94128705897Sccardenas 			    "status data @ 0x%llx", __func__,
94228705897Sccardenas 			    acct->resp_desc->addr);
94328705897Sccardenas 		} else {
94428705897Sccardenas 			ret = 1;
94528705897Sccardenas 			dev->cfg.isr_status = 1;
94628705897Sccardenas 			/* Move ring indexes */
94728705897Sccardenas 			vioscsi_next_ring_item(dev, acct->avail, acct->used,
94828705897Sccardenas 			    acct->req_desc, acct->req_idx);
94928705897Sccardenas 		}
95028705897Sccardenas 
95128705897Sccardenas 		goto free_read_6;
95228705897Sccardenas 	}
95328705897Sccardenas 
95428705897Sccardenas 	vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
95528705897Sccardenas 
95628705897Sccardenas 	/* Move index for response */
95728705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
95828705897Sccardenas 	    &(acct->resp_idx));
95928705897Sccardenas 
9601f5e00e0Sreyk 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
96128705897Sccardenas 	    "idx %d req_idx %d global_idx %d",
96228705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
96328705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
96428705897Sccardenas 
9650759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
96628705897Sccardenas 		log_warnx("%s: unable to write OK resp status "
96728705897Sccardenas 		    "data @ 0x%llx", __func__, acct->resp_desc->addr);
96828705897Sccardenas 		goto free_read_6;
96928705897Sccardenas 	}
97028705897Sccardenas 
97128705897Sccardenas 	/* Move index for read_buf */
97228705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
97328705897Sccardenas 	    &(acct->resp_idx));
97428705897Sccardenas 
9751f5e00e0Sreyk 	DPRINTF("%s: writing read_buf to 0x%llx size %d at "
97628705897Sccardenas 	    "local idx %d req_idx %d global_idx %d",
97728705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
97828705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
97928705897Sccardenas 
9800759b25cSdv 	if (write_mem(acct->resp_desc->addr, read_buf, info->len)) {
98128705897Sccardenas 		log_warnx("%s: unable to write read_buf to gpa @ 0x%llx",
98228705897Sccardenas 		    __func__, acct->resp_desc->addr);
98328705897Sccardenas 	} else {
98428705897Sccardenas 		ret = 1;
98528705897Sccardenas 		dev->cfg.isr_status = 1;
98628705897Sccardenas 		/* Move ring indexes */
98728705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
98828705897Sccardenas 		    acct->req_desc, acct->req_idx);
98928705897Sccardenas 	}
99028705897Sccardenas 
99128705897Sccardenas free_read_6:
99228705897Sccardenas 	vioscsi_free_info(info);
99328705897Sccardenas read_6_out:
99428705897Sccardenas 	return (ret);
99528705897Sccardenas }
99628705897Sccardenas 
99728705897Sccardenas static int
vioscsi_handle_read_10(struct vioscsi_dev * dev,struct virtio_scsi_req_hdr * req,struct virtio_vq_acct * acct)99828705897Sccardenas vioscsi_handle_read_10(struct vioscsi_dev *dev,
99928705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
100028705897Sccardenas {
100128705897Sccardenas 	int ret = 0;
100228705897Sccardenas 	struct virtio_scsi_res_hdr resp;
100328705897Sccardenas 	const uint8_t *read_buf;
100428705897Sccardenas 	uint32_t read_lba;
100528705897Sccardenas 	uint16_t read_10_len;
100628705897Sccardenas 	off_t chunk_offset;
100728705897Sccardenas 	struct ioinfo *info;
1008eccd596dSkrw 	struct scsi_rw_10 *read_10;
10090759b25cSdv 	size_t chunk_len = 0;
101028705897Sccardenas 
101128705897Sccardenas 	memset(&resp, 0, sizeof(resp));
1012eccd596dSkrw 	read_10 = (struct scsi_rw_10 *)(req->cdb);
101328705897Sccardenas 	read_lba = _4btol(read_10->addr);
101428705897Sccardenas 	read_10_len = _2btol(read_10->length);
101528705897Sccardenas 	chunk_offset = 0;
101628705897Sccardenas 
10172d03c861Sccardenas 	DPRINTF("%s: READ_10 Addr 0x%08x Len %d (%d)",
101828705897Sccardenas 	    __func__, read_lba, read_10_len, read_10_len * dev->max_xfer);
101928705897Sccardenas 
102028705897Sccardenas 	/* check if lba is in range */
102128705897Sccardenas 	if (read_lba > dev->n_blocks - 1) {
10222d03c861Sccardenas 		DPRINTF("%s: requested block out of range req: %ud max: %lld",
102328705897Sccardenas 		    __func__, read_lba, dev->n_blocks);
102428705897Sccardenas 
102528705897Sccardenas 		vioscsi_prepare_resp(&resp,
102628705897Sccardenas 		    VIRTIO_SCSI_S_OK, SCSI_CHECK, SKEY_ILLEGAL_REQUEST,
102728705897Sccardenas 		    SENSE_LBA_OUT_OF_RANGE, SENSE_DEFAULT_ASCQ);
102828705897Sccardenas 
102928705897Sccardenas 		/* Move index for response */
103028705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
103128705897Sccardenas 		    acct->req_desc, &(acct->resp_idx));
103228705897Sccardenas 
10330759b25cSdv 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
103428705897Sccardenas 			log_warnx("%s: unable to set ERR status data @ 0x%llx",
103528705897Sccardenas 			    __func__, acct->resp_desc->addr);
103628705897Sccardenas 		} else {
103728705897Sccardenas 			ret = 1;
103828705897Sccardenas 			dev->cfg.isr_status = 1;
103928705897Sccardenas 			/* Move ring indexes */
104028705897Sccardenas 			vioscsi_next_ring_item(dev, acct->avail, acct->used,
104128705897Sccardenas 			    acct->req_desc, acct->req_idx);
104228705897Sccardenas 		}
104328705897Sccardenas 
104428705897Sccardenas 		goto read_10_out;
104528705897Sccardenas 	}
104628705897Sccardenas 
104728705897Sccardenas 	info = vioscsi_start_read(dev, read_lba, read_10_len);
104828705897Sccardenas 
104928705897Sccardenas 	if (info == NULL) {
105028705897Sccardenas 		log_warnx("%s: cannot alloc for read", __func__);
105128705897Sccardenas 		goto read_10_out;
105228705897Sccardenas 	}
105328705897Sccardenas 
105428705897Sccardenas 	/* read block */
10554d22b0bdSdv 	read_buf = vioscsi_finish_read(dev, info);
105628705897Sccardenas 
105728705897Sccardenas 	if (read_buf == NULL) {
105828705897Sccardenas 		log_warnx("%s: error reading position %ud", __func__, read_lba);
105928705897Sccardenas 		vioscsi_prepare_resp(&resp,
106028705897Sccardenas 		    VIRTIO_SCSI_S_OK, SCSI_CHECK, SKEY_MEDIUM_ERROR,
106128705897Sccardenas 		    SENSE_MEDIUM_NOT_PRESENT, SENSE_DEFAULT_ASCQ);
106228705897Sccardenas 
106328705897Sccardenas 		/* Move index for response */
106428705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
106528705897Sccardenas 		    acct->req_desc, &(acct->resp_idx));
106628705897Sccardenas 
10670759b25cSdv 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
106828705897Sccardenas 			log_warnx("%s: unable to set ERR status data @ 0x%llx",
106928705897Sccardenas 			    __func__, acct->resp_desc->addr);
107028705897Sccardenas 		} else {
107128705897Sccardenas 			ret = 1;
107228705897Sccardenas 			dev->cfg.isr_status = 1;
107328705897Sccardenas 			/* Move ring indexes */
107428705897Sccardenas 			vioscsi_next_ring_item(dev, acct->avail, acct->used,
107528705897Sccardenas 			    acct->req_desc, acct->req_idx);
107628705897Sccardenas 		}
107728705897Sccardenas 
107828705897Sccardenas 		goto free_read_10;
107928705897Sccardenas 	}
108028705897Sccardenas 
108128705897Sccardenas 	vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
108228705897Sccardenas 
108328705897Sccardenas 	/* Move index for response */
108428705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
108528705897Sccardenas 	    &(acct->resp_idx));
108628705897Sccardenas 
10871f5e00e0Sreyk 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
108828705897Sccardenas 	    "idx %d req_idx %d global_idx %d",
108928705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
109028705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
109128705897Sccardenas 
10920759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
109328705897Sccardenas 		log_warnx("%s: unable to write OK resp status "
109428705897Sccardenas 		    "data @ 0x%llx", __func__, acct->resp_desc->addr);
109528705897Sccardenas 		goto free_read_10;
109628705897Sccardenas 	}
109728705897Sccardenas 
109828705897Sccardenas 	/*
109928705897Sccardenas 	 * Perform possible chunking of writes of read_buf
110028705897Sccardenas 	 * based on the segment length allocated by the host.
110128705897Sccardenas 	 * At least one write will be performed.
110228705897Sccardenas 	 * If chunk_offset == info->len, no more writes
110328705897Sccardenas 	 */
110428705897Sccardenas 	do {
110528705897Sccardenas 		/* Move index for read_buf */
110628705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
110728705897Sccardenas 		    acct->resp_desc, &(acct->resp_idx));
110828705897Sccardenas 
11091f5e00e0Sreyk 		DPRINTF("%s: writing read_buf to 0x%llx size "
111028705897Sccardenas 		    "%d at local idx %d req_idx %d global_idx %d",
111128705897Sccardenas 		    __func__, acct->resp_desc->addr, acct->resp_desc->len,
111228705897Sccardenas 		    acct->resp_idx, acct->req_idx, acct->idx);
111328705897Sccardenas 
11140759b25cSdv 		/* Check we don't read beyond read_buf boundaries. */
11150759b25cSdv 		if (acct->resp_desc->len > info->len - chunk_offset) {
11160759b25cSdv 			log_warnx("%s: descriptor length beyond read_buf len",
11170759b25cSdv 			    __func__);
11180759b25cSdv 			chunk_len = info->len - chunk_offset;
11190759b25cSdv 		} else
11200759b25cSdv 			chunk_len = acct->resp_desc->len;
11210759b25cSdv 
11220759b25cSdv 		if (write_mem(acct->resp_desc->addr, read_buf + chunk_offset,
11230759b25cSdv 			chunk_len)) {
112428705897Sccardenas 			log_warnx("%s: unable to write read_buf"
112528705897Sccardenas 			    " to gpa @ 0x%llx", __func__,
112628705897Sccardenas 			    acct->resp_desc->addr);
112728705897Sccardenas 			goto free_read_10;
112828705897Sccardenas 		}
112928705897Sccardenas 		chunk_offset += acct->resp_desc->len;
113028705897Sccardenas 	} while (chunk_offset < info->len);
113128705897Sccardenas 
113228705897Sccardenas 	ret = 1;
113328705897Sccardenas 	dev->cfg.isr_status = 1;
113428705897Sccardenas 	/* Move ring indexes */
113528705897Sccardenas 	vioscsi_next_ring_item(dev, acct->avail, acct->used, acct->req_desc,
113628705897Sccardenas 	    acct->req_idx);
113728705897Sccardenas 
113828705897Sccardenas free_read_10:
113928705897Sccardenas 	vioscsi_free_info(info);
114028705897Sccardenas read_10_out:
114128705897Sccardenas 	return (ret);
114228705897Sccardenas }
114328705897Sccardenas 
114428705897Sccardenas static int
vioscsi_handle_prevent_allow(struct vioscsi_dev * dev,struct virtio_scsi_req_hdr * req,struct virtio_vq_acct * acct)114528705897Sccardenas vioscsi_handle_prevent_allow(struct vioscsi_dev *dev,
114628705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
114728705897Sccardenas {
114828705897Sccardenas 	int ret = 0;
114928705897Sccardenas 	struct virtio_scsi_res_hdr resp;
115028705897Sccardenas 
115128705897Sccardenas 	memset(&resp, 0, sizeof(resp));
115228705897Sccardenas 	/* Move index for response */
115328705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
115428705897Sccardenas 	    &(acct->resp_idx));
115528705897Sccardenas 
115628705897Sccardenas 	vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
115728705897Sccardenas 
115828705897Sccardenas 	if (dev->locked) {
11592d03c861Sccardenas 		DPRINTF("%s: unlocking medium", __func__);
116028705897Sccardenas 	} else {
11612d03c861Sccardenas 		DPRINTF("%s: locking medium", __func__);
116228705897Sccardenas 	}
116328705897Sccardenas 
116428705897Sccardenas 	dev->locked = dev->locked ? 0 : 1;
116528705897Sccardenas 
11660759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
116728705897Sccardenas 		log_warnx("%s: unable to write OK resp status data @ 0x%llx",
116828705897Sccardenas 		    __func__, acct->resp_desc->addr);
116928705897Sccardenas 	} else {
117028705897Sccardenas 		ret = 1;
117128705897Sccardenas 		dev->cfg.isr_status = 1;
117228705897Sccardenas 		/* Move ring indexes */
117328705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
117428705897Sccardenas 		    acct->req_desc, acct->req_idx);
117528705897Sccardenas 	}
117628705897Sccardenas 
117728705897Sccardenas 	return (ret);
117828705897Sccardenas }
117928705897Sccardenas 
118028705897Sccardenas static int
vioscsi_handle_mechanism_status(struct vioscsi_dev * dev,struct virtio_scsi_req_hdr * req,struct virtio_vq_acct * acct)118128705897Sccardenas vioscsi_handle_mechanism_status(struct vioscsi_dev *dev,
118228705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
118328705897Sccardenas {
118428705897Sccardenas 	int ret = 0;
118528705897Sccardenas 	struct virtio_scsi_res_hdr resp;
118628705897Sccardenas 	struct scsi_mechanism_status_header *mech_status_header;
118728705897Sccardenas 
118830dd31d2Sdv 	DPRINTF("%s: MECH_STATUS Len %u", __func__,
118930dd31d2Sdv 	    _2btol(((struct scsi_mechanism_status *)(req->cdb))->length));
119028705897Sccardenas 
11910759b25cSdv 	mech_status_header = calloc(1,
11920759b25cSdv 	    sizeof(struct scsi_mechanism_status_header));
119328705897Sccardenas 
119428705897Sccardenas 	if (mech_status_header == NULL)
119528705897Sccardenas 		goto mech_out;
119628705897Sccardenas 
119728705897Sccardenas 	/* return a 0 header since we are not a changer */
119830dd31d2Sdv 	memset(&resp, 0, sizeof(resp));
119928705897Sccardenas 	vioscsi_prepare_resp(&resp,
120028705897Sccardenas 	    VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
120128705897Sccardenas 
120228705897Sccardenas 	/* Move index for response */
120328705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
120428705897Sccardenas 	    acct->req_desc, &(acct->resp_idx));
120528705897Sccardenas 
12060759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
120728705897Sccardenas 		log_warnx("%s: unable to set ERR status data @ 0x%llx",
120828705897Sccardenas 		    __func__, acct->resp_desc->addr);
120928705897Sccardenas 		goto free_mech;
121028705897Sccardenas 	}
121128705897Sccardenas 
121228705897Sccardenas 	/* Move index for mech_status_header */
121328705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
121428705897Sccardenas 	    &(acct->resp_idx));
121528705897Sccardenas 
121628705897Sccardenas 	if (write_mem(acct->resp_desc->addr, mech_status_header,
12170759b25cSdv 		sizeof(struct scsi_mechanism_status_header))) {
121828705897Sccardenas 		log_warnx("%s: unable to write "
121928705897Sccardenas 		    "mech_status_header response to "
122028705897Sccardenas 		    "gpa @ 0x%llx",
122128705897Sccardenas 		    __func__, acct->resp_desc->addr);
122228705897Sccardenas 	} else {
122328705897Sccardenas 		ret = 1;
122428705897Sccardenas 		dev->cfg.isr_status = 1;
122528705897Sccardenas 		/* Move ring indexes */
122628705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
122728705897Sccardenas 		    acct->req_desc, acct->req_idx);
122828705897Sccardenas 	}
122928705897Sccardenas 
123028705897Sccardenas free_mech:
123128705897Sccardenas 	free(mech_status_header);
123228705897Sccardenas mech_out:
123328705897Sccardenas 	return (ret);
123428705897Sccardenas }
123528705897Sccardenas 
123628705897Sccardenas static int
vioscsi_handle_read_toc(struct vioscsi_dev * dev,struct virtio_scsi_req_hdr * req,struct virtio_vq_acct * acct)123728705897Sccardenas vioscsi_handle_read_toc(struct vioscsi_dev *dev,
123828705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
123928705897Sccardenas {
124028705897Sccardenas 	int ret = 0;
124128705897Sccardenas 	struct virtio_scsi_res_hdr resp;
124228705897Sccardenas 	uint16_t toc_data_len;
124328705897Sccardenas 	uint8_t toc_data[TOC_DATA_SIZE];
124428705897Sccardenas 	uint8_t *toc_data_p;
124530dd31d2Sdv 	struct scsi_read_toc *toc = (struct scsi_read_toc *)(req->cdb);
124628705897Sccardenas 
12472d03c861Sccardenas 	DPRINTF("%s: %s - MSF %d Track 0x%02x Addr 0x%04x",
124830dd31d2Sdv 	    __func__, vioscsi_op_names(toc->opcode), ((toc->byte2 >> 1) & 1),
124930dd31d2Sdv 	    toc->from_track, _2btol(toc->data_len));
125028705897Sccardenas 
125128705897Sccardenas 	/* Tracks should be 0, 1, or LEAD_OUT_TRACK, 0xaa */
125228705897Sccardenas 	if (toc->from_track > 1 &&
125328705897Sccardenas 	    toc->from_track != READ_TOC_LEAD_OUT_TRACK) {
125428705897Sccardenas 		/* illegal request */
12552d03c861Sccardenas 		log_warnx("%s: illegal request Track 0x%02x",
125628705897Sccardenas 		    __func__, toc->from_track);
125728705897Sccardenas 
125830dd31d2Sdv 		memset(&resp, 0, sizeof(resp));
125928705897Sccardenas 		vioscsi_prepare_resp(&resp,
126028705897Sccardenas 		    VIRTIO_SCSI_S_OK, SCSI_CHECK, SKEY_ILLEGAL_REQUEST,
126128705897Sccardenas 		    SENSE_ILLEGAL_CDB_FIELD, SENSE_DEFAULT_ASCQ);
126228705897Sccardenas 
126328705897Sccardenas 		/* Move index for response */
126428705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
126528705897Sccardenas 		    acct->req_desc, &(acct->resp_idx));
126628705897Sccardenas 
12670759b25cSdv 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
126828705897Sccardenas 			log_warnx("%s: unable to set ERR status data @ 0x%llx",
126928705897Sccardenas 			    __func__, acct->resp_desc->addr);
127028705897Sccardenas 			goto read_toc_out;
127128705897Sccardenas 		}
127228705897Sccardenas 
127328705897Sccardenas 		ret = 1;
127428705897Sccardenas 		dev->cfg.isr_status = 1;
127528705897Sccardenas 		/* Move ring indexes */
127628705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
127728705897Sccardenas 		    acct->req_desc, acct->req_idx);
127828705897Sccardenas 
127928705897Sccardenas 		goto read_toc_out;
128028705897Sccardenas 	}
128128705897Sccardenas 
128228705897Sccardenas 	/*
128328705897Sccardenas 	 * toc_data is defined as:
128428705897Sccardenas 	 * [0-1]: TOC Data Length, typically 0x1a
128528705897Sccardenas 	 * [2]: First Track, 1
128628705897Sccardenas 	 * [3]: Last Track, 1
128728705897Sccardenas 	 *
128828705897Sccardenas 	 * Track 1 Descriptor
128928705897Sccardenas 	 * [0]: Reserved, 0
129028705897Sccardenas 	 * [1]: ADR,Control, 0x14
129128705897Sccardenas 	 * [2]: Track #, 1
129228705897Sccardenas 	 * [3]: Reserved, 0
129328705897Sccardenas 	 * [4-7]: Track Start Address, LBA
129428705897Sccardenas 	 *
129528705897Sccardenas 	 * Track 0xaa (Lead Out) Descriptor
129628705897Sccardenas 	 * [0]: Reserved, 0
129728705897Sccardenas 	 * [1]: ADR,Control, 0x14
129828705897Sccardenas 	 * [2]: Track #, 0xaa
129928705897Sccardenas 	 * [3]: Reserved, 0
130028705897Sccardenas 	 * [4-7]: Track Start Address, LBA
130128705897Sccardenas 	 */
130230dd31d2Sdv 	memset(toc_data, 0, sizeof(toc_data));
130328705897Sccardenas 	toc_data_p = toc_data + 2;
130428705897Sccardenas 	*toc_data_p++ = READ_TOC_START_TRACK;
130528705897Sccardenas 	*toc_data_p++ = READ_TOC_LAST_TRACK;
130628705897Sccardenas 	if (toc->from_track <= 1) {
130728705897Sccardenas 		/* first track descriptor */
130828705897Sccardenas 		*toc_data_p++ = 0x0;
130928705897Sccardenas 		*toc_data_p++ = READ_TOC_ADR_CTL;
131028705897Sccardenas 		*toc_data_p++ = READ_TOC_START_TRACK;
131128705897Sccardenas 		*toc_data_p++ = 0x0;
131228705897Sccardenas 		/* start addr for first track is 0 */
131328705897Sccardenas 		*toc_data_p++ = 0x0;
131428705897Sccardenas 		*toc_data_p++ = 0x0;
131528705897Sccardenas 		*toc_data_p++ = 0x0;
131628705897Sccardenas 		*toc_data_p++ = 0x0;
131728705897Sccardenas 	}
131828705897Sccardenas 
131928705897Sccardenas 	/* last track descriptor */
132028705897Sccardenas 	*toc_data_p++ = 0x0;
132128705897Sccardenas 	*toc_data_p++ = READ_TOC_ADR_CTL;
132228705897Sccardenas 	*toc_data_p++ = READ_TOC_LEAD_OUT_TRACK;
132328705897Sccardenas 	*toc_data_p++ = 0x0;
132428705897Sccardenas 
132528705897Sccardenas 	_lto4b((uint32_t)dev->n_blocks, toc_data_p);
132628705897Sccardenas 	toc_data_p += 4;
132728705897Sccardenas 
132828705897Sccardenas 	toc_data_len = toc_data_p - toc_data;
132928705897Sccardenas 	_lto2b((uint32_t)toc_data_len - 2, toc_data);
133028705897Sccardenas 
1331253c7ec9Sjsg 	memset(&resp, 0, sizeof(resp));
133228705897Sccardenas 	vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
133328705897Sccardenas 
133428705897Sccardenas 	/* Move index for response */
133528705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
133628705897Sccardenas 	    &(acct->resp_idx));
133728705897Sccardenas 
13381f5e00e0Sreyk 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
133928705897Sccardenas 	    "idx %d req_idx %d global_idx %d",
134028705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
134128705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
134228705897Sccardenas 
13430759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
134428705897Sccardenas 		log_warnx("%s: unable to write OK resp status data @ 0x%llx",
134528705897Sccardenas 		    __func__, acct->resp_desc->addr);
134628705897Sccardenas 		goto read_toc_out;
134728705897Sccardenas 	}
134828705897Sccardenas 
134928705897Sccardenas 	/* Move index for toc descriptor */
135028705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
135128705897Sccardenas 	    &(acct->resp_idx));
135228705897Sccardenas 
13531f5e00e0Sreyk 	DPRINTF("%s: writing toc_data to 0x%llx size %d at "
135428705897Sccardenas 	    "local idx %d req_idx %d global_idx %d",
135528705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
135628705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
135728705897Sccardenas 
13580759b25cSdv 	if (write_mem(acct->resp_desc->addr, toc_data, sizeof(toc_data))) {
135928705897Sccardenas 		log_warnx("%s: unable to write toc descriptor data @ 0x%llx",
136028705897Sccardenas 		    __func__, acct->resp_desc->addr);
136128705897Sccardenas 	} else {
136228705897Sccardenas 		ret = 1;
136328705897Sccardenas 		dev->cfg.isr_status = 1;
136428705897Sccardenas 		/* Move ring indexes */
136528705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
136628705897Sccardenas 		    acct->req_desc, acct->req_idx);
136728705897Sccardenas 	}
136828705897Sccardenas 
136928705897Sccardenas read_toc_out:
137028705897Sccardenas 	return (ret);
137128705897Sccardenas }
137228705897Sccardenas 
137328705897Sccardenas static int
vioscsi_handle_read_disc_info(struct vioscsi_dev * dev,struct virtio_scsi_req_hdr * req,struct virtio_vq_acct * acct)137428705897Sccardenas vioscsi_handle_read_disc_info(struct vioscsi_dev *dev,
137528705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
137628705897Sccardenas {
137728705897Sccardenas 	int ret = 0;
137828705897Sccardenas 	struct virtio_scsi_res_hdr resp;
137928705897Sccardenas 
138030dd31d2Sdv 	DPRINTF("%s: Disc Info %x", __func__,
138130dd31d2Sdv 		((struct scsi_read_disc_information *)(req->cdb))->byte2);
138228705897Sccardenas 
138328705897Sccardenas 	/* send back unsupported */
138430dd31d2Sdv 	memset(&resp, 0, sizeof(resp));
138528705897Sccardenas 	vioscsi_prepare_resp(&resp,
138628705897Sccardenas 	    VIRTIO_SCSI_S_OK, SCSI_CHECK, SKEY_ILLEGAL_REQUEST,
138728705897Sccardenas 	    SENSE_ILLEGAL_CDB_FIELD, SENSE_DEFAULT_ASCQ);
138828705897Sccardenas 
138928705897Sccardenas 	/* Move index for response */
139028705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
139128705897Sccardenas 	    acct->req_desc, &(acct->resp_idx));
139228705897Sccardenas 
13930759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
139428705897Sccardenas 		log_warnx("%s: unable to set ERR status data @ 0x%llx",
139528705897Sccardenas 		    __func__, acct->resp_desc->addr);
139628705897Sccardenas 	} else {
139728705897Sccardenas 		ret = 1;
139828705897Sccardenas 		dev->cfg.isr_status = 1;
139928705897Sccardenas 		/* Move ring indexes */
140028705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
140128705897Sccardenas 		    acct->req_desc, acct->req_idx);
140228705897Sccardenas 	}
140328705897Sccardenas 
140428705897Sccardenas 	return (ret);
140528705897Sccardenas }
140628705897Sccardenas 
140728705897Sccardenas static int
vioscsi_handle_gesn(struct vioscsi_dev * dev,struct virtio_scsi_req_hdr * req,struct virtio_vq_acct * acct)140828705897Sccardenas vioscsi_handle_gesn(struct vioscsi_dev *dev,
140928705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
141028705897Sccardenas {
141128705897Sccardenas 	int ret = 0;
141228705897Sccardenas 	struct virtio_scsi_res_hdr resp;
141328705897Sccardenas 	uint8_t gesn_reply[GESN_SIZE];
141428705897Sccardenas 	struct scsi_gesn *gesn;
141528705897Sccardenas 	struct scsi_gesn_event_header *gesn_event_header;
141628705897Sccardenas 	struct scsi_gesn_power_event *gesn_power_event;
141728705897Sccardenas 
141828705897Sccardenas 	memset(&resp, 0, sizeof(resp));
141928705897Sccardenas 	gesn = (struct scsi_gesn *)(req->cdb);
14202d03c861Sccardenas 	DPRINTF("%s: GESN Method %s", __func__,
142128705897Sccardenas 	    gesn->byte2 ? "Polling" : "Asynchronous");
142228705897Sccardenas 
142328705897Sccardenas 	if (gesn->byte2 == 0) {
142428705897Sccardenas 		/* we don't support asynchronous */
142528705897Sccardenas 		vioscsi_prepare_resp(&resp,
142628705897Sccardenas 		    VIRTIO_SCSI_S_OK, SCSI_CHECK, SKEY_ILLEGAL_REQUEST,
142728705897Sccardenas 		    SENSE_ILLEGAL_CDB_FIELD, SENSE_DEFAULT_ASCQ);
142828705897Sccardenas 
142928705897Sccardenas 		/* Move index for response */
143028705897Sccardenas 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
143128705897Sccardenas 		    acct->req_desc, &(acct->resp_idx));
143228705897Sccardenas 
14330759b25cSdv 		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
143428705897Sccardenas 			log_warnx("%s: unable to set ERR status  data @ 0x%llx",
143528705897Sccardenas 			    __func__, acct->resp_desc->addr);
143628705897Sccardenas 			goto gesn_out;
143728705897Sccardenas 		}
143828705897Sccardenas 
143928705897Sccardenas 		ret = 1;
144028705897Sccardenas 		dev->cfg.isr_status = 1;
144128705897Sccardenas 		/* Move ring indexes */
144228705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
144328705897Sccardenas 		    acct->req_desc, acct->req_idx);
144428705897Sccardenas 
144528705897Sccardenas 		goto gesn_out;
144628705897Sccardenas 	}
144728705897Sccardenas 	memset(gesn_reply, 0, sizeof(gesn_reply));
144828705897Sccardenas 	gesn_event_header = (struct scsi_gesn_event_header *)(gesn_reply);
144928705897Sccardenas 	gesn_power_event = (struct scsi_gesn_power_event *)(gesn_reply + 4);
145028705897Sccardenas 	/* set event header length and notification */
145128705897Sccardenas 	_lto2b(GESN_HEADER_LEN, gesn_event_header->length);
145228705897Sccardenas 	gesn_event_header->notification = GESN_NOTIFY_POWER_MGMT;
145328705897Sccardenas 	gesn_event_header->supported_event = GESN_EVENT_POWER_MGMT;
145428705897Sccardenas 
145528705897Sccardenas 	/* set event descriptor */
145628705897Sccardenas 	gesn_power_event->event_code = GESN_CODE_NOCHG;
145728705897Sccardenas 	if (dev->locked)
145828705897Sccardenas 		gesn_power_event->status = GESN_STATUS_ACTIVE;
145928705897Sccardenas 	else
146028705897Sccardenas 		gesn_power_event->status = GESN_STATUS_IDLE;
146128705897Sccardenas 
146228705897Sccardenas 	/* Move index for response */
146328705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
146428705897Sccardenas 	    &(acct->resp_idx));
146528705897Sccardenas 
14661f5e00e0Sreyk 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
146728705897Sccardenas 	    "idx %d req_idx %d global_idx %d",
146828705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
146928705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
147028705897Sccardenas 
14710759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
147228705897Sccardenas 		log_warnx("%s: unable to write OK resp status "
147328705897Sccardenas 		    "data @ 0x%llx", __func__, acct->resp_desc->addr);
147428705897Sccardenas 		goto gesn_out;
147528705897Sccardenas 	}
147628705897Sccardenas 
147728705897Sccardenas 	/* Move index for gesn_reply */
147828705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
147928705897Sccardenas 	    &(acct->resp_idx));
148028705897Sccardenas 
14811f5e00e0Sreyk 	DPRINTF("%s: writing gesn_reply to 0x%llx size %d at "
148228705897Sccardenas 	    "local idx %d req_idx %d global_idx %d",
148328705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
148428705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
148528705897Sccardenas 
14860759b25cSdv 	if (write_mem(acct->resp_desc->addr, gesn_reply, sizeof(gesn_reply))) {
148728705897Sccardenas 		log_warnx("%s: unable to write gesn_reply"
148828705897Sccardenas 		    " response to gpa @ 0x%llx",
148928705897Sccardenas 		    __func__, acct->resp_desc->addr);
149028705897Sccardenas 	} else {
149128705897Sccardenas 		ret = 1;
149228705897Sccardenas 		dev->cfg.isr_status = 1;
149328705897Sccardenas 		/* Move ring indexes */
149428705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
149528705897Sccardenas 		    acct->req_desc, acct->req_idx);
149628705897Sccardenas 	}
149728705897Sccardenas 
149828705897Sccardenas gesn_out:
149928705897Sccardenas 	return (ret);
150028705897Sccardenas }
150128705897Sccardenas 
150228705897Sccardenas static int
vioscsi_handle_get_config(struct vioscsi_dev * dev,struct virtio_scsi_req_hdr * req,struct virtio_vq_acct * acct)150328705897Sccardenas vioscsi_handle_get_config(struct vioscsi_dev *dev,
150428705897Sccardenas     struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
150528705897Sccardenas {
150628705897Sccardenas 	int ret = 0;
150728705897Sccardenas 	struct virtio_scsi_res_hdr resp;
150828705897Sccardenas 	uint8_t *get_conf_reply;
150928705897Sccardenas 	struct scsi_config_feature_header *config_feature_header;
151028705897Sccardenas 	struct scsi_config_generic_descriptor *config_generic_desc;
151128705897Sccardenas 	struct scsi_config_profile_descriptor *config_profile_desc;
151228705897Sccardenas 	struct scsi_config_core_descriptor *config_core_desc;
151328705897Sccardenas 	struct scsi_config_morphing_descriptor *config_morphing_desc;
151428705897Sccardenas 	struct scsi_config_remove_media_descriptor *config_remove_media_desc;
151528705897Sccardenas 	struct scsi_config_random_read_descriptor *config_random_read_desc;
151628705897Sccardenas 
151730dd31d2Sdv #if DEBUG
151830dd31d2Sdv 	struct scsi_get_configuration *get_configuration =
151930dd31d2Sdv 	    (struct scsi_get_configuration *)(req->cdb);
152030dd31d2Sdv 	log_debug("%s: Conf RT %x Feature %d Len %d", __func__,
152130dd31d2Sdv 	    get_configuration->byte2, _2btol(get_configuration->feature),
152230dd31d2Sdv 	    _2btol(get_configuration->length));
152330dd31d2Sdv #endif /* DEBUG */
152428705897Sccardenas 
152528705897Sccardenas 	get_conf_reply = (uint8_t*)calloc(G_CONFIG_REPLY_SIZE, sizeof(uint8_t));
152628705897Sccardenas 
152728705897Sccardenas 	if (get_conf_reply == NULL)
152828705897Sccardenas 		goto get_config_out;
152928705897Sccardenas 
153028705897Sccardenas 	/*
153128705897Sccardenas 	 * Use MMC-5 6.6 for structure and
153228705897Sccardenas 	 * MMC-5 5.2 to send back:
153328705897Sccardenas 	 * feature header - 8 bytes
153428705897Sccardenas 	 * feature descriptor for profile list - 8 bytes
153528705897Sccardenas 	 * feature descriptor for core feature - 12 bytes
153628705897Sccardenas 	 * feature descriptor for morphing feature - 8 bytes
153728705897Sccardenas 	 * feature descriptor for removable media - 8 bytes
153828705897Sccardenas 	 * feature descriptor for random read feature - 12 bytes
153928705897Sccardenas 	 */
154028705897Sccardenas 
154128705897Sccardenas 	config_feature_header =
154228705897Sccardenas 	    (struct scsi_config_feature_header *)(get_conf_reply);
154328705897Sccardenas 	config_generic_desc =
154428705897Sccardenas 	    (struct scsi_config_generic_descriptor *)(get_conf_reply + 8);
154528705897Sccardenas 	config_profile_desc =
154628705897Sccardenas 	    (struct scsi_config_profile_descriptor *)(get_conf_reply + 12);
154728705897Sccardenas 	config_core_desc =
154828705897Sccardenas 	    (struct scsi_config_core_descriptor *)(get_conf_reply + 16);
154928705897Sccardenas 	config_morphing_desc =
155028705897Sccardenas 	    (struct scsi_config_morphing_descriptor *)(get_conf_reply + 28);
155128705897Sccardenas 	config_remove_media_desc =
155228705897Sccardenas 	    (struct scsi_config_remove_media_descriptor *)(get_conf_reply + 36);
155328705897Sccardenas 	config_random_read_desc =
155428705897Sccardenas 	    (struct scsi_config_random_read_descriptor *)(get_conf_reply + 44);
155528705897Sccardenas 
155628705897Sccardenas 	/* set size to be get_conf_reply - size field */
155728705897Sccardenas 	_lto4b(G_CONFIG_REPLY_SIZE_HEX, config_feature_header->length);
155828705897Sccardenas 	/* set current profile to be non-conforming */
155928705897Sccardenas 	_lto2b(CONFIG_PROFILE_NON_CONFORM,
156028705897Sccardenas 	    config_feature_header->current_profile);
156128705897Sccardenas 
156228705897Sccardenas 	/* fill out profile list feature */
156328705897Sccardenas 	_lto2b(CONFIG_FEATURE_CODE_PROFILE, config_generic_desc->feature_code);
156428705897Sccardenas 	config_generic_desc->byte3 = CONFIG_PROFILELIST_BYTE3;
156528705897Sccardenas 	config_generic_desc->length = CONFIG_PROFILELIST_LENGTH;
156628705897Sccardenas 	/* fill out profile descriptor for NON_COFORM */
156728705897Sccardenas 	_lto2b(CONFIG_PROFILE_NON_CONFORM, config_profile_desc->profile_number);
156828705897Sccardenas 	config_profile_desc->byte3 = CONFIG_PROFILE_BYTE3;
156928705897Sccardenas 
157028705897Sccardenas 	/* fill out core feature */
157128705897Sccardenas 	_lto2b(CONFIG_FEATURE_CODE_CORE, config_core_desc->feature_code);
157228705897Sccardenas 	config_core_desc->byte3 = CONFIG_CORE_BYTE3;
157328705897Sccardenas 	config_core_desc->length = CONFIG_CORE_LENGTH;
157428705897Sccardenas 	_lto4b(CONFIG_CORE_PHY_SCSI, config_core_desc->phy_std);
157528705897Sccardenas 
157628705897Sccardenas 	/* fill out morphing feature */
157728705897Sccardenas 	_lto2b(CONFIG_FEATURE_CODE_MORPHING,
157828705897Sccardenas 	    config_morphing_desc->feature_code);
157928705897Sccardenas 	config_morphing_desc->byte3 = CONFIG_MORPHING_BYTE3;
158028705897Sccardenas 	config_morphing_desc->length = CONFIG_MORPHING_LENGTH;
158128705897Sccardenas 	config_morphing_desc->byte5 = CONFIG_MORPHING_BYTE5;
158228705897Sccardenas 
158328705897Sccardenas 	/* fill out removable media feature */
158428705897Sccardenas 	_lto2b(CONFIG_FEATURE_CODE_REMOVE_MEDIA,
158528705897Sccardenas 	    config_remove_media_desc->feature_code);
158628705897Sccardenas 	config_remove_media_desc->byte3 = CONFIG_REMOVE_MEDIA_BYTE3;
158728705897Sccardenas 	config_remove_media_desc->length = CONFIG_REMOVE_MEDIA_LENGTH;
158828705897Sccardenas 	config_remove_media_desc->byte5 = CONFIG_REMOVE_MEDIA_BYTE5;
158928705897Sccardenas 
159028705897Sccardenas 	/* fill out random read feature */
159128705897Sccardenas 	_lto2b(CONFIG_FEATURE_CODE_RANDOM_READ,
159228705897Sccardenas 	    config_random_read_desc->feature_code);
159328705897Sccardenas 	config_random_read_desc->byte3 = CONFIG_RANDOM_READ_BYTE3;
159428705897Sccardenas 	config_random_read_desc->length = CONFIG_RANDOM_READ_LENGTH;
1595a9eba918Sccardenas 	if (dev->n_blocks >= UINT32_MAX)
159628705897Sccardenas 		_lto4b(UINT32_MAX, config_random_read_desc->block_size);
159728705897Sccardenas 	else
159828705897Sccardenas 		_lto4b(dev->n_blocks - 1, config_random_read_desc->block_size);
159928705897Sccardenas 	_lto2b(CONFIG_RANDOM_READ_BLOCKING_TYPE,
160028705897Sccardenas 	    config_random_read_desc->blocking_type);
160128705897Sccardenas 
160230dd31d2Sdv 	memset(&resp, 0, sizeof(resp));
160328705897Sccardenas 	vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
160428705897Sccardenas 
160528705897Sccardenas 	/* Move index for response */
160628705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
160728705897Sccardenas 	    acct->req_desc, &(acct->resp_idx));
160828705897Sccardenas 
16091f5e00e0Sreyk 	DPRINTF("%s: writing resp to 0x%llx size %d at local "
161028705897Sccardenas 	    "idx %d req_idx %d global_idx %d",
161128705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
161228705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
161328705897Sccardenas 
16140759b25cSdv 	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
161528705897Sccardenas 		log_warnx("%s: unable to set Ok status data @ 0x%llx",
161628705897Sccardenas 		    __func__, acct->resp_desc->addr);
161728705897Sccardenas 		goto free_get_config;
161828705897Sccardenas 	}
161928705897Sccardenas 
162028705897Sccardenas 	/* Move index for get_conf_reply */
162128705897Sccardenas 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
162228705897Sccardenas 	    &(acct->resp_idx));
162328705897Sccardenas 
16241f5e00e0Sreyk 	DPRINTF("%s: writing get_conf_reply to 0x%llx size %d "
162528705897Sccardenas 	    "at local idx %d req_idx %d global_idx %d",
162628705897Sccardenas 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
162728705897Sccardenas 	    acct->resp_idx, acct->req_idx, acct->idx);
162828705897Sccardenas 
162928705897Sccardenas 	if (write_mem(acct->resp_desc->addr, get_conf_reply,
16300759b25cSdv 	    G_CONFIG_REPLY_SIZE)) {
163128705897Sccardenas 		log_warnx("%s: unable to write get_conf_reply"
163228705897Sccardenas 		    " response to gpa @ 0x%llx",
163328705897Sccardenas 		    __func__, acct->resp_desc->addr);
163428705897Sccardenas 	} else {
163528705897Sccardenas 		ret = 1;
163628705897Sccardenas 		dev->cfg.isr_status = 1;
163728705897Sccardenas 		/* Move ring indexes */
163828705897Sccardenas 		vioscsi_next_ring_item(dev, acct->avail, acct->used,
163928705897Sccardenas 		    acct->req_desc, acct->req_idx);
164028705897Sccardenas 	}
164128705897Sccardenas 
164228705897Sccardenas free_get_config:
164328705897Sccardenas 	free(get_conf_reply);
164428705897Sccardenas get_config_out:
164528705897Sccardenas 	return (ret);
164628705897Sccardenas }
164728705897Sccardenas 
164895ab188fSccardenas int
vioscsi_io(int dir,uint16_t reg,uint32_t * data,uint8_t * intr,void * cookie,uint8_t sz)164995ab188fSccardenas vioscsi_io(int dir, uint16_t reg, uint32_t *data, uint8_t *intr,
165095ab188fSccardenas     void *cookie, uint8_t sz)
165195ab188fSccardenas {
165295ab188fSccardenas 	struct vioscsi_dev *dev = (struct vioscsi_dev *)cookie;
165395ab188fSccardenas 
165495ab188fSccardenas 	*intr = 0xFF;
165595ab188fSccardenas 
16562d03c861Sccardenas 	DPRINTF("%s: request %s reg %u, %s sz %u", __func__,
165795ab188fSccardenas 	    dir ? "READ" : "WRITE", reg, vioscsi_reg_name(reg), sz);
165895ab188fSccardenas 
165995ab188fSccardenas 	if (dir == 0) {
166095ab188fSccardenas 		switch (reg) {
166195ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_FEATURES:
166295ab188fSccardenas 		case VIRTIO_CONFIG_QUEUE_SIZE:
166395ab188fSccardenas 		case VIRTIO_CONFIG_ISR_STATUS:
166495ab188fSccardenas 			log_warnx("%s: illegal write %x to %s",
166595ab188fSccardenas 			    __progname, *data, vioscsi_reg_name(reg));
166695ab188fSccardenas 			break;
166795ab188fSccardenas 		case VIRTIO_CONFIG_GUEST_FEATURES:
166895ab188fSccardenas 			dev->cfg.guest_feature = *data;
16692d03c861Sccardenas 			DPRINTF("%s: guest feature set to %u",
167095ab188fSccardenas 			    __func__, dev->cfg.guest_feature);
167195ab188fSccardenas 			break;
16720bd10b9fSdv 		case VIRTIO_CONFIG_QUEUE_PFN:
16730bd10b9fSdv 			dev->cfg.queue_pfn = *data;
167495ab188fSccardenas 			vioscsi_update_qa(dev);
167595ab188fSccardenas 			break;
167695ab188fSccardenas 		case VIRTIO_CONFIG_QUEUE_SELECT:
167795ab188fSccardenas 			dev->cfg.queue_select = *data;
167895ab188fSccardenas 			vioscsi_update_qs(dev);
167995ab188fSccardenas 			break;
168095ab188fSccardenas 		case VIRTIO_CONFIG_QUEUE_NOTIFY:
168195ab188fSccardenas 			dev->cfg.queue_notify = *data;
168295ab188fSccardenas 			if (vioscsi_notifyq(dev))
168395ab188fSccardenas 				*intr = 1;
168495ab188fSccardenas 			break;
168595ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_STATUS:
168695ab188fSccardenas 			dev->cfg.device_status = *data;
16872d03c861Sccardenas 			DPRINTF("%s: device status set to %u",
168895ab188fSccardenas 			    __func__, dev->cfg.device_status);
168995ab188fSccardenas 			if (dev->cfg.device_status == 0) {
1690c5c1249fSreyk 				log_debug("%s: device reset", __func__);
169195ab188fSccardenas 				dev->cfg.guest_feature = 0;
16920bd10b9fSdv 				dev->cfg.queue_pfn = 0;
169395ab188fSccardenas 				vioscsi_update_qa(dev);
169495ab188fSccardenas 				dev->cfg.queue_size = 0;
169595ab188fSccardenas 				vioscsi_update_qs(dev);
169695ab188fSccardenas 				dev->cfg.queue_select = 0;
169795ab188fSccardenas 				dev->cfg.queue_notify = 0;
169895ab188fSccardenas 				dev->cfg.isr_status = 0;
169995ab188fSccardenas 				dev->vq[0].last_avail = 0;
170095ab188fSccardenas 				dev->vq[1].last_avail = 0;
170195ab188fSccardenas 				dev->vq[2].last_avail = 0;
170295ab188fSccardenas 			}
170395ab188fSccardenas 			break;
170495ab188fSccardenas 		default:
170595ab188fSccardenas 			break;
170695ab188fSccardenas 		}
170795ab188fSccardenas 	} else {
170895ab188fSccardenas 		switch (reg) {
170995ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI:
171095ab188fSccardenas 			/* VIRTIO_SCSI_CONFIG_NUM_QUEUES, 32bit */
171195ab188fSccardenas 			if (sz == 4)
171295ab188fSccardenas 				*data = (uint32_t)VIOSCSI_NUM_QUEUES;
171395ab188fSccardenas 			else if (sz == 1) {
171495ab188fSccardenas 				/* read first byte of num_queues */
171595ab188fSccardenas 				*data &= 0xFFFFFF00;
171695ab188fSccardenas 				*data |= (uint32_t)(VIOSCSI_NUM_QUEUES) & 0xFF;
171795ab188fSccardenas 			}
171895ab188fSccardenas 			break;
171995ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 1:
172095ab188fSccardenas 			if (sz == 1) {
172195ab188fSccardenas 				/* read second byte of num_queues */
172295ab188fSccardenas 				*data &= 0xFFFFFF00;
172395ab188fSccardenas 				*data |=
172495ab188fSccardenas 				    (uint32_t)(VIOSCSI_NUM_QUEUES >> 8) & 0xFF;
172595ab188fSccardenas 			}
172695ab188fSccardenas 			break;
172795ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 2:
172895ab188fSccardenas 			if (sz == 1) {
172995ab188fSccardenas 				/* read third byte of num_queues */
173095ab188fSccardenas 				*data &= 0xFFFFFF00;
173195ab188fSccardenas 				*data |=
173295ab188fSccardenas 				    (uint32_t)(VIOSCSI_NUM_QUEUES >> 16) & 0xFF;
173395ab188fSccardenas 			}
173495ab188fSccardenas 			break;
173595ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 3:
173695ab188fSccardenas 			if (sz == 1) {
173795ab188fSccardenas 				/* read fourth byte of num_queues */
173895ab188fSccardenas 				*data &= 0xFFFFFF00;
173995ab188fSccardenas 				*data |=
174095ab188fSccardenas 				    (uint32_t)(VIOSCSI_NUM_QUEUES >> 24) & 0xFF;
174195ab188fSccardenas 			}
174295ab188fSccardenas 			break;
174395ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 4:
174495ab188fSccardenas 			/* VIRTIO_SCSI_CONFIG_SEG_MAX, 32bit */
174595ab188fSccardenas 			if (sz == 4)
174695ab188fSccardenas 				*data = (uint32_t)(VIOSCSI_SEG_MAX);
174795ab188fSccardenas 			else if (sz == 1) {
174895ab188fSccardenas 				/* read first byte of seg_max */
174995ab188fSccardenas 				*data &= 0xFFFFFF00;
175095ab188fSccardenas 				*data |= (uint32_t)(VIOSCSI_SEG_MAX) & 0xFF;
175195ab188fSccardenas 			}
175295ab188fSccardenas 			break;
175395ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 5:
175495ab188fSccardenas 			if (sz == 1) {
175595ab188fSccardenas 				/* read second byte of seg_max */
175695ab188fSccardenas 				*data &= 0xFFFFFF00;
175795ab188fSccardenas 				*data |=
175895ab188fSccardenas 				    (uint32_t)(VIOSCSI_SEG_MAX >> 8) & 0xFF;
175995ab188fSccardenas 			}
176095ab188fSccardenas 			break;
176195ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 6:
176295ab188fSccardenas 			if (sz == 1) {
176395ab188fSccardenas 				/* read third byte of seg_max */
176495ab188fSccardenas 				*data &= 0xFFFFFF00;
176595ab188fSccardenas 				*data |=
176695ab188fSccardenas 				    (uint32_t)(VIOSCSI_SEG_MAX >> 16) & 0xFF;
176795ab188fSccardenas 			}
176895ab188fSccardenas 			break;
176995ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 7:
177095ab188fSccardenas 			if (sz == 1) {
177195ab188fSccardenas 				/* read fourth byte of seg_max */
177295ab188fSccardenas 				*data &= 0xFFFFFF00;
177395ab188fSccardenas 				*data |=
177495ab188fSccardenas 				    (uint32_t)(VIOSCSI_SEG_MAX >> 24) & 0xFF;
177595ab188fSccardenas 			}
177695ab188fSccardenas 			break;
177795ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 8:
177895ab188fSccardenas 			/* VIRTIO_SCSI_CONFIG_MAX_SECTORS, 32bit */
177995ab188fSccardenas 			if (sz == 4)
178095ab188fSccardenas 				*data = (uint32_t)(dev->max_xfer);
178195ab188fSccardenas 			else if (sz == 1) {
178295ab188fSccardenas 				/* read first byte of max_xfer */
178395ab188fSccardenas 				*data &= 0xFFFFFF00;
178495ab188fSccardenas 				*data |= (uint32_t)(dev->max_xfer) & 0xFF;
178595ab188fSccardenas 			}
178695ab188fSccardenas 			break;
178795ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 9:
178895ab188fSccardenas 			if (sz == 1) {
178995ab188fSccardenas 				/* read second byte of max_xfer */
179095ab188fSccardenas 				*data &= 0xFFFFFF00;
179195ab188fSccardenas 				*data |=
179295ab188fSccardenas 				    (uint32_t)(dev->max_xfer >> 8) & 0xFF;
179395ab188fSccardenas 			}
179495ab188fSccardenas 			break;
179595ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 10:
179695ab188fSccardenas 			if (sz == 1) {
179795ab188fSccardenas 				/* read third byte of max_xfer */
179895ab188fSccardenas 				*data &= 0xFFFFFF00;
179995ab188fSccardenas 				*data |=
180095ab188fSccardenas 				    (uint32_t)(dev->max_xfer >> 16) & 0xFF;
180195ab188fSccardenas 			}
180295ab188fSccardenas 			break;
180395ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 11:
180495ab188fSccardenas 			if (sz == 1) {
180595ab188fSccardenas 				/* read fourth byte of max_xfer */
180695ab188fSccardenas 				*data &= 0xFFFFFF00;
180795ab188fSccardenas 				*data |=
180895ab188fSccardenas 				    (uint32_t)(dev->max_xfer >> 24) & 0xFF;
180995ab188fSccardenas 			}
181095ab188fSccardenas 			break;
181195ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 12:
181295ab188fSccardenas 			/* VIRTIO_SCSI_CONFIG_CMD_PER_LUN, 32bit */
181395ab188fSccardenas 			if (sz == 4)
181495ab188fSccardenas 				*data = (uint32_t)(VIOSCSI_CMD_PER_LUN);
181595ab188fSccardenas 			else if (sz == 1) {
181695ab188fSccardenas 				/* read first byte of cmd_per_lun */
181795ab188fSccardenas 				*data &= 0xFFFFFF00;
181895ab188fSccardenas 				*data |= (uint32_t)(VIOSCSI_CMD_PER_LUN) & 0xFF;
181995ab188fSccardenas 			}
182095ab188fSccardenas 			break;
182195ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 13:
182295ab188fSccardenas 			if (sz == 1) {
182395ab188fSccardenas 				/* read second byte of cmd_per_lun */
182495ab188fSccardenas 				*data &= 0xFFFFFF00;
182595ab188fSccardenas 				*data |=
182695ab188fSccardenas 				    (uint32_t)(VIOSCSI_CMD_PER_LUN >> 8) & 0xFF;
182795ab188fSccardenas 			}
182895ab188fSccardenas 			break;
182995ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 14:
183095ab188fSccardenas 			if (sz == 1) {
183195ab188fSccardenas 				/* read third byte of cmd_per_lun */
183295ab188fSccardenas 				*data &= 0xFFFFFF00;
1833d2de69e7Sreyk 				*data |= (uint32_t)(VIOSCSI_CMD_PER_LUN >> 16)
1834d2de69e7Sreyk 				    & 0xFF;
183595ab188fSccardenas 			}
183695ab188fSccardenas 			break;
183795ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 15:
183895ab188fSccardenas 			if (sz == 1) {
183995ab188fSccardenas 				/* read fourth byte of cmd_per_lun */
184095ab188fSccardenas 				*data &= 0xFFFFFF00;
1841d2de69e7Sreyk 				*data |= (uint32_t)(VIOSCSI_CMD_PER_LUN >> 24)
1842d2de69e7Sreyk 				    & 0xFF;
184395ab188fSccardenas 			}
184495ab188fSccardenas 			break;
184595ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 16:
184695ab188fSccardenas 			/* VIRTIO_SCSI_CONFIG_EVENT_INFO_SIZE, 32bit */
184795ab188fSccardenas 			*data = 0x00;
184895ab188fSccardenas 			break;
184995ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 20:
185095ab188fSccardenas 			/* VIRTIO_SCSI_CONFIG_SENSE_SIZE, 32bit */
185195ab188fSccardenas 			if (sz == 4)
185295ab188fSccardenas 				*data = (uint32_t)(VIOSCSI_SENSE_LEN);
185395ab188fSccardenas 			else if (sz == 1) {
185495ab188fSccardenas 				/* read first byte of sense_size */
185595ab188fSccardenas 				*data &= 0xFFFFFF00;
185695ab188fSccardenas 				*data |= (uint32_t)(VIOSCSI_SENSE_LEN) & 0xFF;
185795ab188fSccardenas 			}
185895ab188fSccardenas 			break;
185995ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 21:
186095ab188fSccardenas 			if (sz == 1) {
186195ab188fSccardenas 				/* read second byte of sense_size */
186295ab188fSccardenas 				*data &= 0xFFFFFF00;
186395ab188fSccardenas 				*data |=
186495ab188fSccardenas 				    (uint32_t)(VIOSCSI_SENSE_LEN >> 8) & 0xFF;
186595ab188fSccardenas 			}
186695ab188fSccardenas 			break;
186795ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 22:
186895ab188fSccardenas 			if (sz == 1) {
186995ab188fSccardenas 				/* read third byte of sense_size */
187095ab188fSccardenas 				*data &= 0xFFFFFF00;
187195ab188fSccardenas 				*data |=
187295ab188fSccardenas 				    (uint32_t)(VIOSCSI_SENSE_LEN >> 16) & 0xFF;
187395ab188fSccardenas 			}
187495ab188fSccardenas 			break;
187595ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 23:
187695ab188fSccardenas 			if (sz == 1) {
187795ab188fSccardenas 				/* read fourth byte of sense_size */
187895ab188fSccardenas 				*data &= 0xFFFFFF00;
187995ab188fSccardenas 				*data |=
188095ab188fSccardenas 				    (uint32_t)(VIOSCSI_SENSE_LEN >> 24) & 0xFF;
188195ab188fSccardenas 			}
188295ab188fSccardenas 			break;
188395ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 24:
188495ab188fSccardenas 			/* VIRTIO_SCSI_CONFIG_CDB_SIZE, 32bit */
188595ab188fSccardenas 			if (sz == 4)
188695ab188fSccardenas 				*data = (uint32_t)(VIOSCSI_CDB_LEN);
188795ab188fSccardenas 			else if (sz == 1) {
188895ab188fSccardenas 				/* read first byte of cdb_len */
188995ab188fSccardenas 				*data &= 0xFFFFFF00;
189095ab188fSccardenas 				*data |= (uint32_t)(VIOSCSI_CDB_LEN) & 0xFF;
189195ab188fSccardenas 			}
189295ab188fSccardenas 			break;
189395ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 25:
189495ab188fSccardenas 			if (sz == 1) {
189595ab188fSccardenas 				/* read second byte of cdb_len */
189695ab188fSccardenas 				*data &= 0xFFFFFF00;
189795ab188fSccardenas 				*data |=
189895ab188fSccardenas 				    (uint32_t)(VIOSCSI_CDB_LEN >> 8) & 0xFF;
189995ab188fSccardenas 			}
190095ab188fSccardenas 			break;
190195ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 26:
190295ab188fSccardenas 			if (sz == 1) {
190395ab188fSccardenas 				/* read third byte of cdb_len */
190495ab188fSccardenas 				*data &= 0xFFFFFF00;
190595ab188fSccardenas 				*data |=
190695ab188fSccardenas 				    (uint32_t)(VIOSCSI_CDB_LEN >> 16) & 0xFF;
190795ab188fSccardenas 			}
190895ab188fSccardenas 			break;
190995ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 27:
191095ab188fSccardenas 			if (sz == 1) {
191195ab188fSccardenas 				/* read fourth byte of cdb_len */
191295ab188fSccardenas 				*data &= 0xFFFFFF00;
191395ab188fSccardenas 				*data |=
191495ab188fSccardenas 				    (uint32_t)(VIOSCSI_CDB_LEN >> 24) & 0xFF;
191595ab188fSccardenas 			}
191695ab188fSccardenas 			break;
191795ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 28:
191895ab188fSccardenas 			/* VIRTIO_SCSI_CONFIG_MAX_CHANNEL, 16bit */
1919d2de69e7Sreyk 
1920d2de69e7Sreyk 			/* defined by standard to be zero */
1921d2de69e7Sreyk 			*data &= 0xFFFF0000;
192295ab188fSccardenas 			break;
192395ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 29:
1924d2de69e7Sreyk 			/* defined by standard to be zero */
1925d2de69e7Sreyk 			*data &= 0xFFFF0000;
192695ab188fSccardenas 			break;
192795ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 30:
192895ab188fSccardenas 			/* VIRTIO_SCSI_CONFIG_MAX_TARGET, 16bit */
192995ab188fSccardenas 			if (sz == 2) {
193095ab188fSccardenas 				*data &= 0xFFFF0000;
193195ab188fSccardenas 				*data |=
193295ab188fSccardenas 				    (uint32_t)(VIOSCSI_MAX_TARGET) & 0xFFFF;
193395ab188fSccardenas 			} else if (sz == 1) {
193495ab188fSccardenas 				/* read first byte of max_target */
193595ab188fSccardenas 				*data &= 0xFFFFFF00;
193695ab188fSccardenas 				*data |=
193795ab188fSccardenas 				    (uint32_t)(VIOSCSI_MAX_TARGET) & 0xFF;
193895ab188fSccardenas 			}
193995ab188fSccardenas 			break;
194095ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 31:
194195ab188fSccardenas 			if (sz == 1) {
194295ab188fSccardenas 				/* read second byte of max_target */
194395ab188fSccardenas 				*data &= 0xFFFFFF00;
194495ab188fSccardenas 				*data |=
194595ab188fSccardenas 				    (uint32_t)(VIOSCSI_MAX_TARGET >> 8) & 0xFF;
194695ab188fSccardenas 			}
194795ab188fSccardenas 			break;
194895ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 32:
194995ab188fSccardenas 			/* VIRTIO_SCSI_CONFIG_MAX_LUN, 32bit */
195095ab188fSccardenas 			if (sz == 4)
195195ab188fSccardenas 				*data = (uint32_t)(VIOSCSI_MAX_LUN);
195295ab188fSccardenas 			else if (sz == 1) {
195395ab188fSccardenas 				/* read first byte of max_lun */
195495ab188fSccardenas 				*data &= 0xFFFFFF00;
195595ab188fSccardenas 				*data |= (uint32_t)(VIOSCSI_MAX_LUN) & 0xFF;
195695ab188fSccardenas 			}
195795ab188fSccardenas 			break;
195895ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 33:
195995ab188fSccardenas 			if (sz == 1) {
196095ab188fSccardenas 				/* read second byte of max_lun */
196195ab188fSccardenas 				*data &= 0xFFFFFF00;
196295ab188fSccardenas 				*data |=
196395ab188fSccardenas 				    (uint32_t)(VIOSCSI_MAX_LUN >> 8) & 0xFF;
196495ab188fSccardenas 			}
196595ab188fSccardenas 			break;
196695ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 34:
196795ab188fSccardenas 			if (sz == 1) {
196895ab188fSccardenas 				/* read third byte of max_lun */
196995ab188fSccardenas 				*data &= 0xFFFFFF00;
197095ab188fSccardenas 				*data |=
197195ab188fSccardenas 				    (uint32_t)(VIOSCSI_MAX_LUN >> 16) & 0xFF;
197295ab188fSccardenas 			}
197395ab188fSccardenas 			break;
197495ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 35:
197595ab188fSccardenas 			if (sz == 1) {
197695ab188fSccardenas 				/* read fourth byte of max_lun */
197795ab188fSccardenas 				*data &= 0xFFFFFF00;
197895ab188fSccardenas 				*data |=
197995ab188fSccardenas 				    (uint32_t)(VIOSCSI_MAX_LUN >> 24) & 0xFF;
198095ab188fSccardenas 			}
198195ab188fSccardenas 			break;
198295ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_FEATURES:
198395ab188fSccardenas 			*data = dev->cfg.device_feature;
198495ab188fSccardenas 			break;
198595ab188fSccardenas 		case VIRTIO_CONFIG_GUEST_FEATURES:
198695ab188fSccardenas 			*data = dev->cfg.guest_feature;
198795ab188fSccardenas 			break;
19880bd10b9fSdv 		case VIRTIO_CONFIG_QUEUE_PFN:
19890bd10b9fSdv 			*data = dev->cfg.queue_pfn;
199095ab188fSccardenas 			break;
199195ab188fSccardenas 		case VIRTIO_CONFIG_QUEUE_SIZE:
199295ab188fSccardenas 			if (sz == 4)
199395ab188fSccardenas 				*data = dev->cfg.queue_size;
199495ab188fSccardenas 			else if (sz == 2) {
199595ab188fSccardenas 				*data &= 0xFFFF0000;
199695ab188fSccardenas 				*data |= (uint16_t)dev->cfg.queue_size;
199795ab188fSccardenas 			} else if (sz == 1) {
199895ab188fSccardenas 				*data &= 0xFFFFFF00;
199995ab188fSccardenas 				*data |= (uint8_t)dev->cfg.queue_size;
200095ab188fSccardenas 			}
200195ab188fSccardenas 			break;
200295ab188fSccardenas 		case VIRTIO_CONFIG_QUEUE_SELECT:
200395ab188fSccardenas 			*data = dev->cfg.queue_select;
200495ab188fSccardenas 			break;
200595ab188fSccardenas 		case VIRTIO_CONFIG_QUEUE_NOTIFY:
200695ab188fSccardenas 			*data = dev->cfg.queue_notify;
200795ab188fSccardenas 			break;
200895ab188fSccardenas 		case VIRTIO_CONFIG_DEVICE_STATUS:
200995ab188fSccardenas 			if (sz == 4)
201095ab188fSccardenas 				*data = dev->cfg.device_status;
201195ab188fSccardenas 			else if (sz == 2) {
201295ab188fSccardenas 				*data &= 0xFFFF0000;
201395ab188fSccardenas 				*data |= (uint16_t)dev->cfg.device_status;
201495ab188fSccardenas 			} else if (sz == 1) {
201595ab188fSccardenas 				*data &= 0xFFFFFF00;
201695ab188fSccardenas 				*data |= (uint8_t)dev->cfg.device_status;
201795ab188fSccardenas 			}
201895ab188fSccardenas 			break;
201995ab188fSccardenas 		case VIRTIO_CONFIG_ISR_STATUS:
202095ab188fSccardenas 			*data = dev->cfg.isr_status;
202195ab188fSccardenas 			dev->cfg.isr_status = 0;
202295ab188fSccardenas 			break;
202395ab188fSccardenas 		}
202495ab188fSccardenas 	}
202595ab188fSccardenas 
202695ab188fSccardenas 
202795ab188fSccardenas 	return (0);
202895ab188fSccardenas }
202995ab188fSccardenas 
203095ab188fSccardenas void
vioscsi_update_qs(struct vioscsi_dev * dev)203195ab188fSccardenas vioscsi_update_qs(struct vioscsi_dev *dev)
203295ab188fSccardenas {
20330bd10b9fSdv 	struct virtio_vq_info *vq_info;
20340bd10b9fSdv 
203595ab188fSccardenas 	/* Invalid queue? */
20368e29e26eSjsg 	if (dev->cfg.queue_select >= VIRTIO_MAX_QUEUES) {
203795ab188fSccardenas 		dev->cfg.queue_size = 0;
203895ab188fSccardenas 		return;
203995ab188fSccardenas 	}
204095ab188fSccardenas 
20410bd10b9fSdv 	vq_info = &dev->vq[dev->cfg.queue_select];
20420bd10b9fSdv 
20430bd10b9fSdv 	/* Update queue pfn/size based on queue select */
20440bd10b9fSdv 	dev->cfg.queue_pfn = vq_info->q_gpa >> 12;
20450bd10b9fSdv 	dev->cfg.queue_size = vq_info->qs;
204695ab188fSccardenas }
204795ab188fSccardenas 
204895ab188fSccardenas void
vioscsi_update_qa(struct vioscsi_dev * dev)204995ab188fSccardenas vioscsi_update_qa(struct vioscsi_dev *dev)
205095ab188fSccardenas {
20510bd10b9fSdv 	struct virtio_vq_info *vq_info;
20520bd10b9fSdv 	void *hva = NULL;
20530bd10b9fSdv 
205495ab188fSccardenas 	/* Invalid queue? */
20558e29e26eSjsg 	if (dev->cfg.queue_select >= VIRTIO_MAX_QUEUES)
205695ab188fSccardenas 		return;
205795ab188fSccardenas 
20580bd10b9fSdv 	vq_info = &dev->vq[dev->cfg.queue_select];
20590bd10b9fSdv 	vq_info->q_gpa = (uint64_t)dev->cfg.queue_pfn * VIRTIO_PAGE_SIZE;
20600bd10b9fSdv 
20610bd10b9fSdv 	hva = hvaddr_mem(vq_info->q_gpa, vring_size(VIOSCSI_QUEUE_SIZE));
20620bd10b9fSdv 	if (hva == NULL)
20630bd10b9fSdv 		fatal("vioscsi_update_qa");
20640bd10b9fSdv 	vq_info->q_hva = hva;
206595ab188fSccardenas }
206695ab188fSccardenas 
206795ab188fSccardenas /*
206895ab188fSccardenas  * Process message(s) in the queue(s)
206995ab188fSccardenas  * vioscsi driver will be placing the following in the queue for each iteration
207095ab188fSccardenas  * virtio_scsi_req_hdr with a possible SCSI_DATA_OUT buffer
207195ab188fSccardenas  * along with a virtio_scsi_res_hdr with a possible SCSI_DATA_IN buffer
207295ab188fSccardenas  * for consumption.
207395ab188fSccardenas  *
207495ab188fSccardenas  * Return 1 if an interrupt should be generated (response written)
207595ab188fSccardenas  *        0 otherwise
207695ab188fSccardenas  */
207795ab188fSccardenas int
vioscsi_notifyq(struct vioscsi_dev * dev)207895ab188fSccardenas vioscsi_notifyq(struct vioscsi_dev *dev)
207995ab188fSccardenas {
20800bd10b9fSdv 	int cnt, ret = 0;
208195ab188fSccardenas 	char *vr;
208295ab188fSccardenas 	struct virtio_scsi_req_hdr req;
208395ab188fSccardenas 	struct virtio_scsi_res_hdr resp;
208428705897Sccardenas 	struct virtio_vq_acct acct;
20850bd10b9fSdv 	struct virtio_vq_info *vq_info;
208695ab188fSccardenas 
208795ab188fSccardenas 	ret = 0;
208895ab188fSccardenas 
208995ab188fSccardenas 	/* Invalid queue? */
20908e29e26eSjsg 	if (dev->cfg.queue_notify >= VIRTIO_MAX_QUEUES)
209195ab188fSccardenas 		return (ret);
209295ab188fSccardenas 
20930bd10b9fSdv 	vq_info = &dev->vq[dev->cfg.queue_notify];
20940bd10b9fSdv 	vr = vq_info->q_hva;
20950bd10b9fSdv 	if (vr == NULL)
20960bd10b9fSdv 		fatalx("%s: null vring", __func__);
209795ab188fSccardenas 
209895ab188fSccardenas 	/* Compute offsets in ring of descriptors, avail ring, and used ring */
209928705897Sccardenas 	acct.desc = (struct vring_desc *)(vr);
21000bd10b9fSdv 	acct.avail = (struct vring_avail *)(vr + vq_info->vq_availoffset);
21010bd10b9fSdv 	acct.used = (struct vring_used *)(vr + vq_info->vq_usedoffset);
210295ab188fSccardenas 
21030bd10b9fSdv 	acct.idx = vq_info->last_avail & VIOSCSI_QUEUE_MASK;
210495ab188fSccardenas 
210528705897Sccardenas 	if ((acct.avail->idx & VIOSCSI_QUEUE_MASK) == acct.idx) {
21060bd10b9fSdv 		log_debug("%s - nothing to do?", __func__);
21070bd10b9fSdv 		return (0);
210895ab188fSccardenas 	}
210995ab188fSccardenas 
21103688c158Sdv 	cnt = 0;
211128705897Sccardenas 	while (acct.idx != (acct.avail->idx & VIOSCSI_QUEUE_MASK)) {
211295ab188fSccardenas 
21133688c158Sdv 		/* Guard against infinite descriptor chains */
21143688c158Sdv 		if (++cnt >= VIOSCSI_QUEUE_SIZE) {
21153688c158Sdv 			log_warnx("%s: invalid descriptor table", __func__);
21163688c158Sdv 			goto out;
21173688c158Sdv 		}
21183688c158Sdv 
211928705897Sccardenas 		acct.req_idx = acct.avail->ring[acct.idx] & VIOSCSI_QUEUE_MASK;
212028705897Sccardenas 		acct.req_desc = &(acct.desc[acct.req_idx]);
212195ab188fSccardenas 
212295ab188fSccardenas 		/* Clear resp for next message */
212395ab188fSccardenas 		memset(&resp, 0, sizeof(resp));
212495ab188fSccardenas 
212528705897Sccardenas 		if ((acct.req_desc->flags & VRING_DESC_F_NEXT) == 0) {
212695ab188fSccardenas 			log_warnx("%s: unchained req descriptor received "
212728705897Sccardenas 			    "(idx %d)", __func__, acct.req_idx);
212895ab188fSccardenas 			goto out;
212995ab188fSccardenas 		}
213095ab188fSccardenas 
213195ab188fSccardenas 		/* Read command from descriptor ring */
21320759b25cSdv 		if (read_mem(acct.req_desc->addr, &req, sizeof(req))) {
213395ab188fSccardenas 			log_warnx("%s: command read_mem error @ 0x%llx",
213428705897Sccardenas 			    __func__, acct.req_desc->addr);
213595ab188fSccardenas 			goto out;
213695ab188fSccardenas 		}
213795ab188fSccardenas 
213895ab188fSccardenas 		/*
213995ab188fSccardenas 		 * req.lun is defined by virtio as
214095ab188fSccardenas 		 * lun[0] - Always set to 1
214195ab188fSccardenas 		 * lun[1] - Target, negotiated as VIOSCSI_MAX_TARGET
214295ab188fSccardenas 		 * lun[2-3] - represent single level LUN structure
214395ab188fSccardenas 		 * lun[4-7] - Zero
214495ab188fSccardenas 		 * At this current time, we are only servicing one device per
214595ab188fSccardenas 		 * bus (1:0:X:0).
214695ab188fSccardenas 		 *
214795ab188fSccardenas 		 * Various implementations will attempt to scan all possible
214895ab188fSccardenas 		 * targets (256) looking for devices or scan for all possible
214995ab188fSccardenas 		 * LUNs in a single level.  When Target is greater than
215095ab188fSccardenas 		 * VIOSCSI_MAX_TARGET or when lun[3] is greater than zero,
215195ab188fSccardenas 		 * respond with a BAD_TARGET response.
215295ab188fSccardenas 		 */
215395ab188fSccardenas 		if (req.lun[1] >= VIOSCSI_MAX_TARGET || req.lun[3] > 0) {
21542d03c861Sccardenas 			DPRINTF("%s: Ignore CMD 0x%02x,%s on lun %u:%u:%u:%u",
215595ab188fSccardenas 			    __func__, req.cdb[0], vioscsi_op_names(req.cdb[0]),
215695ab188fSccardenas 			    req.lun[0], req.lun[1], req.lun[2], req.lun[3]);
215795ab188fSccardenas 			/* Move index for response */
215828705897Sccardenas 			acct.resp_desc = vioscsi_next_ring_desc(acct.desc,
215928705897Sccardenas 			    acct.req_desc, &(acct.resp_idx));
216095ab188fSccardenas 
216195ab188fSccardenas 			vioscsi_prepare_resp(&resp,
216295ab188fSccardenas 			    VIRTIO_SCSI_S_BAD_TARGET, SCSI_OK, 0, 0, 0);
216395ab188fSccardenas 
21640759b25cSdv 			if (acct.resp_desc->len > sizeof(resp)) {
21650759b25cSdv 				log_warnx("%s: invalid descriptor length",
21660759b25cSdv 				    __func__);
21670759b25cSdv 				goto out;
21680759b25cSdv 			}
216928705897Sccardenas 			if (write_mem(acct.resp_desc->addr, &resp,
21700759b25cSdv 				sizeof(resp))) {
217195ab188fSccardenas 				log_warnx("%s: unable to write BAD_TARGET"
217295ab188fSccardenas 				    " resp status data @ 0x%llx",
217328705897Sccardenas 				    __func__, acct.resp_desc->addr);
217495ab188fSccardenas 				goto out;
217595ab188fSccardenas 			}
217695ab188fSccardenas 
217795ab188fSccardenas 			ret = 1;
217895ab188fSccardenas 			dev->cfg.isr_status = 1;
21790bd10b9fSdv 
21800bd10b9fSdv 			/* Move ring indexes (updates the used ring index) */
218128705897Sccardenas 			vioscsi_next_ring_item(dev, acct.avail, acct.used,
218228705897Sccardenas 			    acct.req_desc, acct.req_idx);
218395ab188fSccardenas 			goto next_msg;
218495ab188fSccardenas 		}
218595ab188fSccardenas 
21862d03c861Sccardenas 		DPRINTF("%s: Queue %d id 0x%llx lun %u:%u:%u:%u"
218795ab188fSccardenas 		    " cdb OP 0x%02x,%s",
218895ab188fSccardenas 		    __func__, dev->cfg.queue_notify, req.id,
218995ab188fSccardenas 		    req.lun[0], req.lun[1], req.lun[2], req.lun[3],
219095ab188fSccardenas 		    req.cdb[0], vioscsi_op_names(req.cdb[0]));
219195ab188fSccardenas 
219295ab188fSccardenas 		/* opcode is first byte */
219395ab188fSccardenas 		switch (req.cdb[0]) {
219495ab188fSccardenas 		case TEST_UNIT_READY:
219595ab188fSccardenas 		case START_STOP:
219628705897Sccardenas 			ret = vioscsi_handle_tur(dev, &req, &acct);
219795ab188fSccardenas 			break;
219895ab188fSccardenas 		case PREVENT_ALLOW:
219928705897Sccardenas 			ret = vioscsi_handle_prevent_allow(dev, &req, &acct);
220095ab188fSccardenas 			break;
220195ab188fSccardenas 		case READ_TOC:
220228705897Sccardenas 			ret = vioscsi_handle_read_toc(dev, &req, &acct);
220395ab188fSccardenas 			break;
220495ab188fSccardenas 		case READ_CAPACITY:
220528705897Sccardenas 			ret = vioscsi_handle_read_capacity(dev, &req, &acct);
220695ab188fSccardenas 			break;
220795ab188fSccardenas 		case READ_CAPACITY_16:
220828705897Sccardenas 			ret = vioscsi_handle_read_capacity_16(dev, &req, &acct);
220995ab188fSccardenas 			break;
221095ab188fSccardenas 		case READ_COMMAND:
221128705897Sccardenas 			ret = vioscsi_handle_read_6(dev, &req, &acct);
221295ab188fSccardenas 			break;
2213eccd596dSkrw 		case READ_10:
221428705897Sccardenas 			ret = vioscsi_handle_read_10(dev, &req, &acct);
221595ab188fSccardenas 			break;
221695ab188fSccardenas 		case INQUIRY:
221728705897Sccardenas 			ret = vioscsi_handle_inquiry(dev, &req, &acct);
221895ab188fSccardenas 			break;
221995ab188fSccardenas 		case MODE_SENSE:
222028705897Sccardenas 			ret = vioscsi_handle_mode_sense(dev, &req, &acct);
222195ab188fSccardenas 			break;
222295ab188fSccardenas 		case MODE_SENSE_BIG:
222328705897Sccardenas 			ret = vioscsi_handle_mode_sense_big(dev, &req, &acct);
222495ab188fSccardenas 			break;
222595ab188fSccardenas 		case GET_EVENT_STATUS_NOTIFICATION:
222628705897Sccardenas 			ret = vioscsi_handle_gesn(dev, &req, &acct);
222795ab188fSccardenas 			break;
222895ab188fSccardenas 		case READ_DISC_INFORMATION:
222928705897Sccardenas 			ret = vioscsi_handle_read_disc_info(dev, &req, &acct);
223095ab188fSccardenas 			break;
223195ab188fSccardenas 		case GET_CONFIGURATION:
223228705897Sccardenas 			ret = vioscsi_handle_get_config(dev, &req, &acct);
223395ab188fSccardenas 			break;
223495ab188fSccardenas 		case MECHANISM_STATUS:
223528705897Sccardenas 			ret = vioscsi_handle_mechanism_status(dev, &req, &acct);
223695ab188fSccardenas 			break;
2237845b4456Sccardenas 		case REPORT_LUNS:
2238845b4456Sccardenas 			ret = vioscsi_handle_report_luns(dev, &req, &acct);
2239845b4456Sccardenas 			break;
224095ab188fSccardenas 		default:
224195ab188fSccardenas 			log_warnx("%s: unsupported opcode 0x%02x,%s",
224295ab188fSccardenas 			    __func__, req.cdb[0], vioscsi_op_names(req.cdb[0]));
224395ab188fSccardenas 			/* Move ring indexes */
224428705897Sccardenas 			vioscsi_next_ring_item(dev, acct.avail, acct.used,
224528705897Sccardenas 			    acct.req_desc, acct.req_idx);
224695ab188fSccardenas 			break;
224795ab188fSccardenas 		}
224895ab188fSccardenas next_msg:
224995ab188fSccardenas 		/* Increment to the next queue slot */
225028705897Sccardenas 		acct.idx = (acct.idx + 1) & VIOSCSI_QUEUE_MASK;
225195ab188fSccardenas 	}
225295ab188fSccardenas out:
225395ab188fSccardenas 	return (ret);
225495ab188fSccardenas }
2255