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