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