hrev45816 adds 3 changesets to branch 'master' old head: f0c63f83d2cfb8bbf5f7ad1079bd8aa6c3c6ec49 new head: e19769d2967f21c79ea8485e3b245d65fe1b1f1a overview: http://cgit.haiku-os.org/haiku/log/?qt=range&q=e19769d+%5Ef0c63f8 ---------------------------------------------------------------------------- 46bfab0: virtio: free fDescriptors on VirtioQueue destruction. 82fda49: Virtio: added a driver with basic support for SCSI devices. * Here is the Qemu command line option for Virtio SCSI devices: -drive if=none,id=hd,file=haiku.image -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd * virtio_scsi.h is copied unchanged from FreeBSD, except for the _PACKED directive. e19769d: virtio_block: replaced __packed with _PACKED [ Jérôme Duval <jerome.duval@xxxxxxxxx> ] ---------------------------------------------------------------------------- 10 files changed, 1224 insertions(+), 1 deletion(-) .../kernel/bus_managers/virtio/VirtioQueue.cpp | 1 + src/add-ons/kernel/busses/scsi/Jamfile | 1 + src/add-ons/kernel/busses/scsi/virtio/Jamfile | 11 + .../busses/scsi/virtio/VirtioSCSIController.cpp | 264 +++++++++++++++ .../busses/scsi/virtio/VirtioSCSIHelper.cpp | 79 +++++ .../busses/scsi/virtio/VirtioSCSIPrivate.h | 148 ++++++++ .../busses/scsi/virtio/VirtioSCSIRequest.cpp | 228 +++++++++++++ .../kernel/busses/scsi/virtio/virtio_scsi.cpp | 339 +++++++++++++++++++ .../kernel/busses/scsi/virtio/virtio_scsi.h | 152 +++++++++ .../disk/virtual/virtio_block/virtio_blk.h | 2 +- ############################################################################ Commit: 46bfab031f9094fef5c0f187b28ba243e2ebd2ec URL: http://cgit.haiku-os.org/haiku/commit/?id=46bfab0 Author: Jérôme Duval <jerome.duval@xxxxxxxxx> Date: Tue Jul 2 20:30:18 2013 UTC virtio: free fDescriptors on VirtioQueue destruction. ---------------------------------------------------------------------------- diff --git a/src/add-ons/kernel/bus_managers/virtio/VirtioQueue.cpp b/src/add-ons/kernel/bus_managers/virtio/VirtioQueue.cpp index 6cff71d..5133348 100644 --- a/src/add-ons/kernel/bus_managers/virtio/VirtioQueue.cpp +++ b/src/add-ons/kernel/bus_managers/virtio/VirtioQueue.cpp @@ -134,6 +134,7 @@ VirtioQueue::VirtioQueue(VirtioDevice* device, uint16 queueNumber, VirtioQueue::~VirtioQueue() { delete_area(fArea); + delete[] fDescriptors; } ############################################################################ Commit: 82fda49e52a6d4fe217de033c350fee60ce56bff URL: http://cgit.haiku-os.org/haiku/commit/?id=82fda49 Author: Jérôme Duval <jerome.duval@xxxxxxxxx> Date: Tue Jul 2 20:33:30 2013 UTC Virtio: added a driver with basic support for SCSI devices. * Here is the Qemu command line option for Virtio SCSI devices: -drive if=none,id=hd,file=haiku.image -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd * virtio_scsi.h is copied unchanged from FreeBSD, except for the _PACKED directive. ---------------------------------------------------------------------------- diff --git a/src/add-ons/kernel/busses/scsi/Jamfile b/src/add-ons/kernel/busses/scsi/Jamfile index 8d80bbc..3b1f30f 100644 --- a/src/add-ons/kernel/busses/scsi/Jamfile +++ b/src/add-ons/kernel/busses/scsi/Jamfile @@ -4,3 +4,4 @@ SubInclude HAIKU_TOP src add-ons kernel busses scsi ahci ; SubInclude HAIKU_TOP src add-ons kernel busses scsi 53c8xx ; SubInclude HAIKU_TOP src add-ons kernel busses scsi buslogic ; SubInclude HAIKU_TOP src add-ons kernel busses scsi usb ; +SubInclude HAIKU_TOP src add-ons kernel busses scsi virtio ; diff --git a/src/add-ons/kernel/busses/scsi/virtio/Jamfile b/src/add-ons/kernel/busses/scsi/virtio/Jamfile new file mode 100644 index 0000000..eba2d24 --- /dev/null +++ b/src/add-ons/kernel/busses/scsi/virtio/Jamfile @@ -0,0 +1,11 @@ +SubDir HAIKU_TOP src add-ons kernel busses scsi virtio ; + +UsePrivateHeaders drivers virtio ; +UsePrivateKernelHeaders ; + +KernelAddon virtio_scsi : + virtio_scsi.cpp + VirtioSCSIController.cpp + VirtioSCSIHelper.cpp + VirtioSCSIRequest.cpp +; diff --git a/src/add-ons/kernel/busses/scsi/virtio/VirtioSCSIController.cpp b/src/add-ons/kernel/busses/scsi/virtio/VirtioSCSIController.cpp new file mode 100644 index 0000000..dcde6c8 --- /dev/null +++ b/src/add-ons/kernel/busses/scsi/virtio/VirtioSCSIController.cpp @@ -0,0 +1,264 @@ +/* + * Copyright 2013, Jérôme Duval, korli@xxxxxxxxxxxxxxxx. + * Distributed under the terms of the MIT License. + */ + + +#include "VirtioSCSIPrivate.h" + +#include <new> +#include <stdlib.h> +#include <string.h> + +#include <util/AutoLock.h> + + +const char * +get_feature_name(uint32 feature) +{ + switch (feature) { + case VIRTIO_SCSI_F_INOUT: + return "in out"; + case VIRTIO_SCSI_F_HOTPLUG: + return "hotplug"; + } + return NULL; +} + + +VirtioSCSIController::VirtioSCSIController(device_node *node) + : + fNode(node), + fVirtio(NULL), + fVirtioDevice(NULL), + fStatus(B_NO_INIT), + fRequest(NULL) +{ + CALLED(); + + B_INITIALIZE_SPINLOCK(&fInterruptLock); + fInterruptCondition.Init(this, "virtio scsi transfer"); + + // get the Virtio device from our parent's parent + device_node *parent = gDeviceManager->get_parent_node(node); + device_node *virtioParent = gDeviceManager->get_parent_node(parent); + gDeviceManager->put_node(parent); + + gDeviceManager->get_driver(virtioParent, (driver_module_info **)&fVirtio, + (void **)&fVirtioDevice); + gDeviceManager->put_node(virtioParent); + + fVirtio->negociate_features(fVirtioDevice, + 0 /*VIRTIO_SCSI_F_HOTPLUG*/, + &fFeatures, &get_feature_name); + + fStatus = fVirtio->read_device_config(fVirtioDevice, 0, &fConfig, + sizeof(struct virtio_scsi_config)); + if (fStatus != B_OK) + return; + + fConfig.sense_size = VIRTIO_SCSI_SENSE_SIZE; + fConfig.cdb_size = VIRTIO_SCSI_CDB_SIZE; + + fVirtio->write_device_config(fVirtioDevice, + offsetof(struct virtio_scsi_config, sense_size), &fConfig.sense_size, + sizeof(fConfig.sense_size)); + fVirtio->write_device_config(fVirtioDevice, + offsetof(struct virtio_scsi_config, cdb_size), &fConfig.sense_size, + sizeof(fConfig.cdb_size)); + + fRequest = new(std::nothrow) VirtioSCSIRequest(true); + if (fRequest == NULL) { + fStatus = B_NO_MEMORY; + return; + } + + ::virtio_queue virtioQueues[3]; + fStatus = fVirtio->alloc_queues(fVirtioDevice, 3, virtioQueues); + if (fStatus != B_OK) { + ERROR("queue allocation failed (%s)\n", strerror(fStatus)); + return; + } + + fControlVirtioQueue = virtioQueues[0]; + fEventVirtioQueue = virtioQueues[1]; + fRequestVirtioQueue = virtioQueues[2]; + + fStatus = fVirtio->setup_interrupt(fVirtioDevice, NULL, NULL); + if (fStatus != B_OK) { + ERROR("interrupt setup failed (%s)\n", strerror(fStatus)); + return; + } + + +} + + +VirtioSCSIController::~VirtioSCSIController() +{ + CALLED(); + delete fRequest; +} + + +status_t +VirtioSCSIController::InitCheck() +{ + return fStatus; +} + + +void +VirtioSCSIController::SetBus(scsi_bus bus) +{ + fBus = bus; +} + + +void +VirtioSCSIController::PathInquiry(scsi_path_inquiry *info) +{ + info->hba_inquiry = SCSI_PI_TAG_ABLE; + info->hba_misc = 0; + info->sim_priv = 0; + info->initiator_id = VIRTIO_SCSI_INITIATOR_ID; + info->hba_queue_size = fConfig.cmd_per_lun != 0 ? fConfig.cmd_per_lun : 1; + memset(info->vuhba_flags, 0, sizeof(info->vuhba_flags)); + + strlcpy(info->sim_vid, "Haiku", SCSI_SIM_ID); + strlcpy(info->hba_vid, "VirtIO", SCSI_HBA_ID); + + strlcpy(info->sim_version, "1.0", SCSI_VERS); + strlcpy(info->hba_version, "1.0", SCSI_VERS); + strlcpy(info->controller_family, "Virtio", SCSI_FAM_ID); + strlcpy(info->controller_type, "Virtio", SCSI_TYPE_ID); +} + + +void +VirtioSCSIController::GetRestrictions(uint8 targetID, bool *isATAPI, + bool *noAutoSense, uint32 *maxBlocks) +{ + *isATAPI = false; + *noAutoSense = true; + *maxBlocks = fConfig.cmd_per_lun; +} + + +uchar +VirtioSCSIController::ResetDevice(uchar targetID, uchar targetLUN) +{ + return SCSI_REQ_CMP; +} + + +status_t +VirtioSCSIController::ExecuteRequest(scsi_ccb *ccb) +{ + status_t result = fRequest->Start(ccb); + if (result != B_OK) + return result; + + if (ccb->cdb[0] == SCSI_OP_REQUEST_SENSE && fRequest->HasSense()) { + TRACE("request sense\n"); + fRequest->RequestSense(); + fRequest->Finish(false); + return B_OK; + } + + if (ccb->target_id > fConfig.max_target) { + ERROR("invalid target device\n"); + fRequest->SetStatus(SCSI_TID_INVALID); + fRequest->Finish(false); + return B_BAD_INDEX; + } + + if (ccb->target_lun > fConfig.max_lun) { + ERROR("invalid lun device\n"); + fRequest->SetStatus(SCSI_LUN_INVALID); + fRequest->Finish(false); + return B_BAD_INDEX; + } + + if (ccb->cdb_length > VIRTIO_SCSI_CDB_SIZE) { + fRequest->SetStatus(SCSI_REQ_INVALID); + fRequest->Finish(false); + return B_BAD_VALUE; + } + + bool isOut = (ccb->flags & SCSI_DIR_MASK) == SCSI_DIR_OUT; + bool isIn = (ccb->flags & SCSI_DIR_MASK) == SCSI_DIR_IN; + + // TODO check feature inout if request is bidirectional + + fRequest->SetTimeout(ccb->timeout > 0 ? ccb->timeout * 1000 * 1000 + : VIRTIO_SCSI_STANDARD_TIMEOUT); + + uint32 inCount = (isIn ? ccb->sg_count : 0) + 1; + uint32 outCount = (isOut ? ccb->sg_count : 0) + 1; + physical_entry entries[inCount + outCount]; + fRequest->FillRequest(inCount, outCount, entries); + + { + InterruptsSpinLocker locker(fInterruptLock); + fExpectsInterrupt = true; + fInterruptCondition.Add(&fInterruptConditionEntry); + } + + fVirtio->queue_request_v(fRequestVirtioQueue, entries, + outCount, inCount, VirtioSCSIController::RequestCallback, this); + + result = fInterruptConditionEntry.Wait(B_RELATIVE_TIMEOUT, + fRequest->Timeout()); + + { + InterruptsSpinLocker locker(fInterruptLock); + fExpectsInterrupt = false; + } + + if (result != B_OK) + return result; + + return fRequest->Finish(false); +} + + +uchar +VirtioSCSIController::AbortRequest(scsi_ccb *request) +{ + return SCSI_REQ_CMP; +} + + +uchar +VirtioSCSIController::TerminateRequest(scsi_ccb *request) +{ + return SCSI_REQ_CMP; +} + + +status_t +VirtioSCSIController::Control(uint8 targetID, uint32 op, void *buffer, + size_t length) +{ + CALLED(); + return B_DEV_INVALID_IOCTL; +} + + +void +VirtioSCSIController::RequestCallback(void* cookie) +{ + CALLED(); + VirtioSCSIController* controller = (VirtioSCSIController*)cookie; + controller->_Interrupt(); +} + + +void +VirtioSCSIController::_Interrupt() +{ + SpinLocker locker(fInterruptLock); + fInterruptCondition.NotifyAll(); +} + diff --git a/src/add-ons/kernel/busses/scsi/virtio/VirtioSCSIHelper.cpp b/src/add-ons/kernel/busses/scsi/virtio/VirtioSCSIHelper.cpp new file mode 100644 index 0000000..a5ece2a --- /dev/null +++ b/src/add-ons/kernel/busses/scsi/virtio/VirtioSCSIHelper.cpp @@ -0,0 +1,79 @@ +/* + * Copyright 2009, Michael Lotz, mmlr@xxxxxxxx. + * Copyright 2004-2007, Axel Dörfler, axeld@xxxxxxxxxxxxxxxx. + * Copyright 2002/03, Thomas Kurschel. All rights reserved. + * + * Distributed under the terms of the MIT License. + */ + +#include "VirtioSCSIPrivate.h" + +#include <string.h> + +#include <vm/vm.h> + + +/*! Copy data between ccb data and buffer + ccb - ccb to copy data from/to + offset - offset of data in ccb + allocation_length- limit of ccb's data buffer according to CDB + buffer - data to copy data from/to + size - number of bytes to copy + to_buffer - true: copy from ccb to buffer + false: copy from buffer to ccb + return: true, if data of ccb was large enough +*/ +bool +copy_sg_data(scsi_ccb *ccb, uint offset, uint allocationLength, + void *buffer, int size, bool toBuffer) +{ + const physical_entry *sgList = ccb->sg_list; + int sgCount = ccb->sg_count; + + // skip unused S/G entries + while (sgCount > 0 && offset >= sgList->size) { + offset -= sgList->size; + ++sgList; + --sgCount; + } + + if (sgCount == 0) + return false; + + // remaining bytes we are allowed to copy from/to ccb + int requestSize = MIN(allocationLength, ccb->data_length) - offset; + + // copy one S/G entry at a time + for (; size > 0 && requestSize > 0 && sgCount > 0; ++sgList, --sgCount) { + size_t bytes; + + bytes = MIN(size, requestSize); + bytes = MIN(bytes, sgList->size); + + if (toBuffer) { + vm_memcpy_from_physical(buffer, sgList->address + offset, bytes, + false); + } else { + vm_memcpy_to_physical(sgList->address + offset, buffer, bytes, + false); + } + + buffer = (char *)buffer + bytes; + size -= bytes; + offset = 0; + } + + return size == 0; +} + + +void +swap_words(void *data, size_t size) +{ + uint16 *word = (uint16 *)data; + size_t count = size / 2; + while (count--) { + *word = B_BENDIAN_TO_HOST_INT16(*word); + word++; + } +} diff --git a/src/add-ons/kernel/busses/scsi/virtio/VirtioSCSIPrivate.h b/src/add-ons/kernel/busses/scsi/virtio/VirtioSCSIPrivate.h new file mode 100644 index 0000000..0dddad1 --- /dev/null +++ b/src/add-ons/kernel/busses/scsi/virtio/VirtioSCSIPrivate.h @@ -0,0 +1,148 @@ +/* + * Copyright 2013, Jérôme Duval, korli@xxxxxxxxxxxxxxxx. + * Distributed under the terms of the MIT License. + */ +#ifndef VIRTIO_SCSI_PRIVATE_H +#define VIRTIO_SCSI_PRIVATE_H + + +#include <bus/SCSI.h> +#include <condition_variable.h> +#include <lock.h> +#include <scsi_cmds.h> +#include <virtio.h> + +#include "virtio_scsi.h" + + +//#define TRACE_VIRTIO_SCSI +#ifdef TRACE_VIRTIO_SCSI +# define TRACE(x...) dprintf("virtio_scsi: " x) +#else +# define TRACE(x...) ; +#endif +#define ERROR(x...) dprintf("\33[33mvirtio_scsi:\33[0m " x) +#define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__) + +extern device_manager_info* gDeviceManager; +extern scsi_for_sim_interface *gSCSI; + +bool copy_sg_data(scsi_ccb *ccb, uint offset, uint allocationLength, + void *buffer, int size, bool toBuffer); +void swap_words(void *data, size_t size); + + +#define VIRTIO_SCSI_STANDARD_TIMEOUT 10 * 1000 * 1000 +#define VIRTIO_SCSI_INITIATOR_ID 7 + + +class VirtioSCSIRequest; + + +class VirtioSCSIController { +public: + VirtioSCSIController(device_node* node); + ~VirtioSCSIController(); + + status_t InitCheck(); + + void SetBus(scsi_bus bus); + scsi_bus Bus() const { return fBus; } + + void PathInquiry(scsi_path_inquiry* info); + void GetRestrictions(uint8 targetID, bool* isATAPI, + bool* noAutoSense, uint32* maxBlocks); + uchar ResetDevice(uchar targetID, uchar targetLUN); + status_t ExecuteRequest(scsi_ccb* request); + uchar AbortRequest(scsi_ccb* request); + uchar TerminateRequest(scsi_ccb* request); + status_t Control(uint8 targetID, uint32 op, + void* buffer, size_t length); + +private: + static void RequestCallback(void *cookie); + void _Interrupt(); + + device_node* fNode; + scsi_bus fBus; + + virtio_device_interface* fVirtio; + virtio_device* fVirtioDevice; + + status_t fStatus; + struct virtio_scsi_config fConfig; + uint32 fFeatures; + ::virtio_queue fControlVirtioQueue; + ::virtio_queue fEventVirtioQueue; + ::virtio_queue fRequestVirtioQueue; + + area_id fArea; + struct virtio_scsi_event* fEvents; + + VirtioSCSIRequest* fRequest; + + spinlock fInterruptLock; + ConditionVariable fInterruptCondition; + ConditionVariableEntry fInterruptConditionEntry; + bool fExpectsInterrupt; + +}; + + +class VirtioSCSIRequest { +public: + VirtioSCSIRequest(bool hasLock); + ~VirtioSCSIRequest(); + + void SetStatus(uint8 status); + uint8 Status() const { return fStatus; } + + void SetTimeout(bigtime_t timeout); + bigtime_t Timeout() const { return fTimeout; } + + bool HasSense() { + return (fResponse->sense_len > 0); } + + void SetIsWrite(bool isWrite); + bool IsWrite() const { return fIsWrite; } + + void SetBytesLeft(uint32 bytesLeft); + size_t* BytesLeft() { return &fBytesLeft; } + + bool HasData() const + { return fCCB->data_length > 0; } + + status_t Finish(bool resubmit); + + // SCSI stuff + status_t Start(scsi_ccb *ccb); + scsi_ccb* CCB() { return fCCB; } + + void RequestSense(); + + void FillRequest(uint32 inCount, uint32 outCount, + physical_entry *entries); + +private: + void _FillSense(scsi_sense *sense); + uchar _ResponseStatus(); + + mutex fLock; + bool fHasLock; + + uint8 fStatus; + + bigtime_t fTimeout; + size_t fBytesLeft; + bool fIsWrite; + scsi_ccb* fCCB; + + // virtio scsi + void* fBuffer; + struct virtio_scsi_cmd_req *fRequest; + struct virtio_scsi_cmd_resp *fResponse; +}; + + +#endif // VIRTIO_SCSI_PRIVATE_H + diff --git a/src/add-ons/kernel/busses/scsi/virtio/VirtioSCSIRequest.cpp b/src/add-ons/kernel/busses/scsi/virtio/VirtioSCSIRequest.cpp new file mode 100644 index 0000000..ab93b17 --- /dev/null +++ b/src/add-ons/kernel/busses/scsi/virtio/VirtioSCSIRequest.cpp @@ -0,0 +1,228 @@ +/* + * Copyright 2013, Jérôme Duval, korli@xxxxxxxxxxxxxxxx. + * Copyright 2009, Michael Lotz, mmlr@xxxxxxxx. + * Distributed under the terms of the MIT License. + */ + + +#include "VirtioSCSIPrivate.h" + +#include <string.h> + + +VirtioSCSIRequest::VirtioSCSIRequest(bool hasLock) + : + fHasLock(hasLock), + fTimeout(0), + fBytesLeft(0), + fIsWrite(false), + fCCB(NULL) +{ + if (hasLock) + mutex_init(&fLock, "virtio scsi request"); + + fBuffer = malloc(sizeof(struct virtio_scsi_cmd_req) + + sizeof(struct virtio_scsi_cmd_resp)); + bzero(fBuffer, sizeof(struct virtio_scsi_cmd_req) + + sizeof(struct virtio_scsi_cmd_resp)); + + fRequest = (struct virtio_scsi_cmd_req *)fBuffer; + fResponse = (struct virtio_scsi_cmd_resp *) + ((addr_t)fBuffer + sizeof(struct virtio_scsi_cmd_req)); + + fResponse->sense_len = 0; +} + + +VirtioSCSIRequest::~VirtioSCSIRequest() +{ + if (fHasLock) + mutex_destroy(&fLock); + + free(fBuffer); +} + + +void +VirtioSCSIRequest::SetStatus(uint8 status) +{ + fStatus = status; +} + + +void +VirtioSCSIRequest::SetTimeout(bigtime_t timeout) +{ + fTimeout = timeout; +} + + +void +VirtioSCSIRequest::SetIsWrite(bool isWrite) +{ + fIsWrite = isWrite; +} + + +void +VirtioSCSIRequest::SetBytesLeft(uint32 bytesLeft) +{ + fBytesLeft = bytesLeft; +} + + +status_t +VirtioSCSIRequest::Start(scsi_ccb *ccb) +{ + CALLED(); + if (mutex_trylock(&fLock) != B_OK) + return B_BUSY; + + fCCB = ccb; + fStatus = SCSI_REQ_CMP; + fCCB->device_status = SCSI_STATUS_GOOD; + fIsWrite = false; + bzero(fResponse, sizeof(struct virtio_scsi_cmd_resp)); + + TRACE("VirtioSCSIRequest::Start() opcode %x tid %x lun %x\n", ccb->cdb[0], + ccb->target_id, ccb->target_lun); + + return B_OK; +} + + +status_t +VirtioSCSIRequest::Finish(bool resubmit) +{ + CALLED(); + fStatus = _ResponseStatus(); + fCCB->data_resid = fResponse->resid; + fCCB->subsys_status = fStatus; + + TRACE("VirtioSCSIRequest::Finish() status 0x%x response 0x%x resid:0x%x" + " sense_len:%x\n", fResponse->status, fResponse->response, + fResponse->resid, fResponse->sense_len); + + if (fCCB->cdb[0] == SCSI_OP_INQUIRY) { + // when the request is an inquiry, don't do anything + } else if (fStatus == SCSI_REQ_CMP && fResponse->status != 0 + && HasSense()) { + // when the request completed and has set sense + // data, report this to the scsi stack by setting + // CHECK CONDITION status + TRACE("setting check condition\n"); + + fCCB->subsys_status = SCSI_REQ_CMP_ERR; + fCCB->device_status = SCSI_STATUS_CHECK_CONDITION; + + // copy sense data if caller requested it + if ((fCCB->flags & SCSI_DIS_AUTOSENSE) == 0) { + size_t senseLength = min_c(sizeof(fCCB->sense), + fResponse->sense_len); + memcpy(fCCB->sense, fResponse->sense, senseLength); + fCCB->sense_resid = sizeof(fCCB->sense) - senseLength; + fCCB->subsys_status |= SCSI_AUTOSNS_VALID; + } + } + + mutex_unlock(&fLock); + + if (resubmit) + gSCSI->resubmit(fCCB); + else + gSCSI->finished(fCCB, 1); + + TRACE("VirtioSCSIRequest::Finish() done\n"); + + return B_OK; +} + + +void +VirtioSCSIRequest::RequestSense() +{ + CALLED(); + // Copy sense data from last request into data buffer of current request. + // The sense data of last request is still present in the current request, + // as it isn't cleared on SCSI_OP_REQUEST_SENSE. + scsi_cmd_request_sense *command = (scsi_cmd_request_sense *)fCCB->cdb; + copy_sg_data(fCCB, 0, command->allocation_length, fResponse->sense, + fResponse->sense_len, false); + + fCCB->data_resid = fCCB->data_length - min_c(min_c(fResponse->sense_len, + command->allocation_length), fCCB->data_length); + fResponse->sense_len = 0; +} + + +void +VirtioSCSIRequest::FillRequest(uint32 inCount, uint32 outCount, + physical_entry *entries) +{ + CALLED(); + fRequest->task_attr = VIRTIO_SCSI_S_SIMPLE; + fRequest->tag = (addr_t)fCCB; + fRequest->lun[0] = 1; + fRequest->lun[1] = fCCB->target_id; + fRequest->lun[2] = 0x40 | ((fCCB->target_lun >> 8) & 0x3f); + fRequest->lun[3] = (fCCB->target_lun >> 8) & 0xff; + + memcpy(fRequest->cdb, fCCB->cdb, min_c(fCCB->cdb_length, + sizeof(fRequest->cdb))); + + get_memory_map(fBuffer, sizeof(struct virtio_scsi_cmd_req) + + sizeof(struct virtio_scsi_cmd_resp), &entries[0], 1); + entries[0].size = sizeof(struct virtio_scsi_cmd_req); + if (outCount > 1) { + memcpy(entries + 1, fCCB->sg_list, fCCB->sg_count + * sizeof(physical_entry)); + } + + entries[outCount].address = entries[0].address + + sizeof(struct virtio_scsi_cmd_req); + entries[outCount].size = sizeof(struct virtio_scsi_cmd_resp); + + if (inCount > 1) { + memcpy(entries + outCount + 1, fCCB->sg_list, fCCB->sg_count + * sizeof(physical_entry)); + } +} + + +uchar +VirtioSCSIRequest::_ResponseStatus() +{ + uchar status; + + switch (fResponse->response) { + case VIRTIO_SCSI_S_OK: + status = SCSI_REQ_CMP; + break; + case VIRTIO_SCSI_S_OVERRUN: + status = SCSI_DATA_RUN_ERR; + break; + case VIRTIO_SCSI_S_ABORTED: + status = SCSI_REQ_ABORTED; + break; + case VIRTIO_SCSI_S_BAD_TARGET: + status = SCSI_TID_INVALID; + break; + case VIRTIO_SCSI_S_RESET: + status = SCSI_SCSI_BUS_RESET; + break; + case VIRTIO_SCSI_S_BUSY: + status = SCSI_SCSI_BUSY; + break; + case VIRTIO_SCSI_S_TRANSPORT_FAILURE: + case VIRTIO_SCSI_S_TARGET_FAILURE: + case VIRTIO_SCSI_S_NEXUS_FAILURE: + status = SCSI_NO_NEXUS; + break; + default: /* VIRTIO_SCSI_S_FAILURE */ + status = SCSI_REQ_CMP_ERR; + break; + } + + return status; +} + diff --git a/src/add-ons/kernel/busses/scsi/virtio/virtio_scsi.cpp b/src/add-ons/kernel/busses/scsi/virtio/virtio_scsi.cpp new file mode 100644 index 0000000..c1b6391 --- /dev/null +++ b/src/add-ons/kernel/busses/scsi/virtio/virtio_scsi.cpp @@ -0,0 +1,339 @@ +/* + * Copyright 2013, Jérôme Duval, korli@xxxxxxxxxxxxxxxx. + * Distributed under the terms of the MIT License. + */ + + +#include "VirtioSCSIPrivate.h" + +#include <new> +#include <stdlib.h> +#include <string.h> + + +#define VIRTIO_SCSI_ID_GENERATOR "virtio_scsi/id" +#define VIRTIO_SCSI_ID_ITEM "virtio_scsi/id" +#define VIRTIO_SCSI_BRIDGE_PRETTY_NAME "Virtio SCSI Bridge" +#define VIRTIO_SCSI_CONTROLLER_PRETTY_NAME "Virtio SCSI Controller" + +#define VIRTIO_SCSI_DEVICE_MODULE_NAME "busses/scsi/virtio_scsi/driver_v1" +#define VIRTIO_SCSI_SIM_MODULE_NAME "busses/scsi/virtio_scsi/sim/driver_v1" + + +device_manager_info *gDeviceManager; +scsi_for_sim_interface *gSCSI; + + +// #pragma mark - SIM module interface + + +static void +set_scsi_bus(scsi_sim_cookie cookie, scsi_bus bus) +{ + VirtioSCSIController *sim = (VirtioSCSIController *)cookie; + sim->SetBus(bus); +} + + +static void +scsi_io(scsi_sim_cookie cookie, scsi_ccb *request) +{ + CALLED(); + VirtioSCSIController *sim = (VirtioSCSIController *)cookie; + if (sim->ExecuteRequest(request) == B_BUSY) + gSCSI->requeue(request, true); +} + + +static uchar +abort_io(scsi_sim_cookie cookie, scsi_ccb *request) +{ + CALLED(); + VirtioSCSIController *sim = (VirtioSCSIController *)cookie; + return sim->AbortRequest(request); +} + + +static uchar +reset_device(scsi_sim_cookie cookie, uchar targetID, uchar targetLUN) +{ + CALLED(); + VirtioSCSIController *sim = (VirtioSCSIController *)cookie; + return sim->ResetDevice(targetID, targetLUN); +} + + +static uchar +terminate_io(scsi_sim_cookie cookie, scsi_ccb *request) +{ + CALLED(); + VirtioSCSIController *sim = (VirtioSCSIController *)cookie; + return sim->TerminateRequest(request); +} + + +static uchar +path_inquiry(scsi_sim_cookie cookie, scsi_path_inquiry *info) +{ + CALLED(); + + VirtioSCSIController *sim = (VirtioSCSIController *)cookie; + if (sim->Bus() == NULL) + return SCSI_NO_HBA; + + sim->PathInquiry(info); + return SCSI_REQ_CMP; +} + + +//! this is called immediately before the SCSI bus manager scans the bus +static uchar +scan_bus(scsi_sim_cookie cookie) +{ + CALLED(); + + return SCSI_REQ_CMP; +} + + +static uchar +reset_bus(scsi_sim_cookie cookie) +{ + CALLED(); + + return SCSI_REQ_CMP; +} + + +/*! Get restrictions of one device + (used for non-SCSI transport protocols and bug fixes) +*/ +static void +get_restrictions(scsi_sim_cookie cookie, uchar targetID, bool *isATAPI, + bool *noAutoSense, uint32 *maxBlocks) +{ + CALLED(); + VirtioSCSIController *sim = (VirtioSCSIController *)cookie; + sim->GetRestrictions(targetID, isATAPI, noAutoSense, maxBlocks); +} + + +static status_t +ioctl(scsi_sim_cookie cookie, uint8 targetID, uint32 op, void *buffer, + size_t length) +{ + CALLED(); + VirtioSCSIController *sim = (VirtioSCSIController *)cookie; + return sim->Control(targetID, op, buffer, length); +} + + +// #pragma mark - + + +static status_t +sim_init_bus(device_node *node, void **_cookie) +{ + CALLED(); + + VirtioSCSIController *controller = new(std::nothrow) + VirtioSCSIController(node); + if (controller == NULL) + return B_NO_MEMORY; + status_t status = controller->InitCheck(); + if (status < B_OK) { + delete controller; + return status; + } + + *_cookie = controller; + return B_OK; +} + + +static void +sim_uninit_bus(void *cookie) +{ + CALLED(); + VirtioSCSIController *controller = (VirtioSCSIController*)cookie; + + delete controller; +} + + +// #pragma mark - + + +static float +virtio_scsi_supports_device(device_node *parent) +{ + const char *bus; + uint16 deviceType; + + // make sure parent is really the Virtio bus manager + if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) + return -1; + + if (strcmp(bus, "virtio")) + return 0.0; + + // check whether it's really a Virtio SCSI Device + if (gDeviceManager->get_attr_uint16(parent, VIRTIO_DEVICE_TYPE_ITEM, + &deviceType, true) != B_OK || deviceType != VIRTIO_DEVICE_ID_SCSI) + return 0.0; + + TRACE("Virtio SCSI device found!\n"); + + return 0.6f; +} + + +static status_t +virtio_scsi_register_device(device_node *parent) +{ + CALLED(); + virtio_device_interface* virtio = NULL; + virtio_device* virtioDevice = NULL; + struct virtio_scsi_config config; + + gDeviceManager->get_driver(parent, (driver_module_info **)&virtio, + (void **)&virtioDevice); + + status_t status = virtio->read_device_config(virtioDevice, 0, &config, + sizeof(struct virtio_scsi_config)); + if (status != B_OK) + return status; + + uint32 max_targets = config.max_target + 1; + uint32 max_blocks = 0x10000; + if (config.max_sectors != 0) + max_blocks = config.max_sectors; + + device_attr attrs[] = { + { SCSI_DEVICE_MAX_TARGET_COUNT, B_UINT32_TYPE, + { ui32: max_targets }}, + { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, + { string: VIRTIO_SCSI_BRIDGE_PRETTY_NAME }}, + + // DMA properties + { B_DMA_MAX_SEGMENT_BLOCKS, B_UINT32_TYPE, { ui32: max_blocks }}, + { B_DMA_MAX_SEGMENT_COUNT, B_UINT32_TYPE, + { ui32: config.seg_max }}, + { NULL } + }; + + return gDeviceManager->register_node(parent, VIRTIO_SCSI_DEVICE_MODULE_NAME, + attrs, NULL, NULL); +} + + +static status_t +virtio_scsi_init_driver(device_node *node, void **_cookie) +{ + CALLED(); + *_cookie = node; + return B_OK; +} + + +static status_t +virtio_scsi_register_child_devices(void *cookie) +{ + CALLED(); + device_node *node = (device_node *)cookie; + + int32 id = gDeviceManager->create_id(VIRTIO_SCSI_ID_GENERATOR); + if (id < 0) + return id; + + device_attr attrs[] = { + { B_DEVICE_FIXED_CHILD, B_STRING_TYPE, + { string: SCSI_FOR_SIM_MODULE_NAME }}, + { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, + { string: VIRTIO_SCSI_CONTROLLER_PRETTY_NAME }}, + { SCSI_DESCRIPTION_CONTROLLER_NAME, B_STRING_TYPE, + { string: VIRTIO_SCSI_DEVICE_MODULE_NAME }}, + { B_DMA_MAX_TRANSFER_BLOCKS, B_UINT32_TYPE, { ui32: 255 }}, + { VIRTIO_SCSI_ID_ITEM, B_UINT32_TYPE, { ui32: (uint32)id }}, + { NULL } + }; + + status_t status = gDeviceManager->register_node(node, + VIRTIO_SCSI_SIM_MODULE_NAME, attrs, NULL, NULL); + if (status < B_OK) + gDeviceManager->free_id(VIRTIO_SCSI_ID_GENERATOR, id); + + return status; +} + + +static status_t +std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + case B_MODULE_UNINIT: + return B_OK; + + default: + return B_ERROR; + } +} + + +static scsi_sim_interface sVirtioSCSISimInterface = { + { + { + VIRTIO_SCSI_SIM_MODULE_NAME, + 0, + std_ops + }, + NULL, // supported devices + NULL, // register node + sim_init_bus, + sim_uninit_bus, + NULL, // register child devices + NULL, // rescan + NULL // bus_removed + }, + set_scsi_bus, + scsi_io, + abort_io, + reset_device, + terminate_io, + path_inquiry, + scan_bus, + reset_bus, + get_restrictions, + ioctl +}; + + +static driver_module_info sVirtioSCSIDevice = { + { + VIRTIO_SCSI_DEVICE_MODULE_NAME, + 0, + std_ops + }, + virtio_scsi_supports_device, + virtio_scsi_register_device, + virtio_scsi_init_driver, + NULL, // uninit_driver, + virtio_scsi_register_child_devices, + NULL, // rescan + NULL, // device_removed +}; + + +module_dependency module_dependencies[] = { + { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&gDeviceManager }, + { SCSI_FOR_SIM_MODULE_NAME, (module_info **)&gSCSI }, + {} +}; + + +module_info *modules[] = { + (module_info *)&sVirtioSCSIDevice, + (module_info *)&sVirtioSCSISimInterface, + NULL +}; diff --git a/src/add-ons/kernel/busses/scsi/virtio/virtio_scsi.h b/src/add-ons/kernel/busses/scsi/virtio/virtio_scsi.h new file mode 100644 index 0000000..b781839 --- /dev/null +++ b/src/add-ons/kernel/busses/scsi/virtio/virtio_scsi.h @@ -0,0 +1,152 @@ +/*- + * This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _VIRTIO_SCSI_H +#define _VIRTIO_SCSI_H + +/* Feature bits */ +#define VIRTIO_SCSI_F_INOUT 0x0001 /* Single request can contain both + * read and write buffers */ +#define VIRTIO_SCSI_F_HOTPLUG 0x0002 /* Host should enable hot plug/unplug + * of new LUNs and targets. + */ + +#define VIRTIO_SCSI_CDB_SIZE 32 +#define VIRTIO_SCSI_SENSE_SIZE 96 + +/* SCSI command request, followed by data-out */ +struct virtio_scsi_cmd_req { + uint8_t lun[8]; /* Logical Unit Number */ + uint64_t tag; /* Command identifier */ + uint8_t task_attr; /* Task attribute */ + uint8_t prio; + uint8_t crn; + uint8_t cdb[VIRTIO_SCSI_CDB_SIZE]; +} _PACKED; + +/* Response, followed by sense data and data-in */ +struct virtio_scsi_cmd_resp { + uint32_t sense_len; /* Sense data length */ + uint32_t resid; /* Residual bytes in data buffer */ + uint16_t status_qualifier; /* Status qualifier */ + uint8_t status; /* Command completion status */ + uint8_t response; /* Response values */ + uint8_t sense[VIRTIO_SCSI_SENSE_SIZE]; +} _PACKED; + +/* Task Management Request */ +struct virtio_scsi_ctrl_tmf_req { + uint32_t type; + uint32_t subtype; + uint8_t lun[8]; + uint64_t tag; +} _PACKED; + +struct virtio_scsi_ctrl_tmf_resp { + uint8_t response; +} _PACKED; + +/* Asynchronous notification query/subscription */ +struct virtio_scsi_ctrl_an_req { + uint32_t type; + uint8_t lun[8]; + uint32_t event_requested; +} _PACKED; + +struct virtio_scsi_ctrl_an_resp { + uint32_t event_actual; + uint8_t response; +} _PACKED; + +struct virtio_scsi_event { + uint32_t event; + uint8_t lun[8]; + uint32_t reason; +} _PACKED; + +struct virtio_scsi_config { + uint32_t num_queues; + uint32_t seg_max; + uint32_t max_sectors; + uint32_t cmd_per_lun; + uint32_t event_info_size; + uint32_t sense_size; + uint32_t cdb_size; + uint16_t max_channel; + uint16_t max_target; + uint32_t max_lun; +} _PACKED; + +/* Response codes */ +#define VIRTIO_SCSI_S_OK 0 +#define VIRTIO_SCSI_S_FUNCTION_COMPLETE 0 +#define VIRTIO_SCSI_S_OVERRUN 1 +#define VIRTIO_SCSI_S_ABORTED 2 +#define VIRTIO_SCSI_S_BAD_TARGET 3 +#define VIRTIO_SCSI_S_RESET 4 +#define VIRTIO_SCSI_S_BUSY 5 +#define VIRTIO_SCSI_S_TRANSPORT_FAILURE 6 +#define VIRTIO_SCSI_S_TARGET_FAILURE 7 +#define VIRTIO_SCSI_S_NEXUS_FAILURE 8 +#define VIRTIO_SCSI_S_FAILURE 9 +#define VIRTIO_SCSI_S_FUNCTION_SUCCEEDED 10 +#define VIRTIO_SCSI_S_FUNCTION_REJECTED 11 +#define VIRTIO_SCSI_S_INCORRECT_LUN 12 + +/* Controlq type codes. */ +#define VIRTIO_SCSI_T_TMF 0 +#define VIRTIO_SCSI_T_AN_QUERY 1 +#define VIRTIO_SCSI_T_AN_SUBSCRIBE 2 + +/* Valid TMF subtypes. */ +#define VIRTIO_SCSI_T_TMF_ABORT_TASK 0 +#define VIRTIO_SCSI_T_TMF_ABORT_TASK_SET 1 +#define VIRTIO_SCSI_T_TMF_CLEAR_ACA 2 +#define VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET 3 +#define VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET 4 +#define VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET 5 +#define VIRTIO_SCSI_T_TMF_QUERY_TASK 6 +#define VIRTIO_SCSI_T_TMF_QUERY_TASK_SET 7 + +/* Events. */ +#define VIRTIO_SCSI_T_EVENTS_MISSED 0x80000000 +#define VIRTIO_SCSI_T_NO_EVENT 0 +#define VIRTIO_SCSI_T_TRANSPORT_RESET 1 +#define VIRTIO_SCSI_T_ASYNC_NOTIFY 2 + +/* Reasons of transport reset event */ +#define VIRTIO_SCSI_EVT_RESET_HARD 0 +#define VIRTIO_SCSI_EVT_RESET_RESCAN 1 +#define VIRTIO_SCSI_EVT_RESET_REMOVED 2 + +#define VIRTIO_SCSI_S_SIMPLE 0 +#define VIRTIO_SCSI_S_ORDERED 1 +#define VIRTIO_SCSI_S_HEAD 2 +#define VIRTIO_SCSI_S_ACA 3 + +#endif /* _VIRTIO_SCSI_H */ ############################################################################ Revision: hrev45816 Commit: e19769d2967f21c79ea8485e3b245d65fe1b1f1a URL: http://cgit.haiku-os.org/haiku/commit/?id=e19769d Author: Jérôme Duval <jerome.duval@xxxxxxxxx> Date: Tue Jul 2 20:41:04 2013 UTC virtio_block: replaced __packed with _PACKED ---------------------------------------------------------------------------- diff --git a/src/add-ons/kernel/drivers/disk/virtual/virtio_block/virtio_blk.h b/src/add-ons/kernel/drivers/disk/virtual/virtio_block/virtio_blk.h index fdac9e5..35acaea 100644 --- a/src/add-ons/kernel/drivers/disk/virtual/virtio_block/virtio_blk.h +++ b/src/add-ons/kernel/drivers/disk/virtual/virtio_block/virtio_blk.h @@ -60,7 +60,7 @@ struct virtio_blk_config { /* block size of device (if VIRTIO_BLK_F_BLK_SIZE) */ uint32_t blk_size; -} __packed; +} _PACKED; /* * Command types