diff --git a/MacHyperVSupport/Controller/HyperV.hpp b/MacHyperVSupport/Controller/HyperV.hpp index 8b4b493..216b255 100644 --- a/MacHyperVSupport/Controller/HyperV.hpp +++ b/MacHyperVSupport/Controller/HyperV.hpp @@ -363,11 +363,6 @@ static inline int sync_test_and_clear_bit(long nr, volatile void *addr) return oldbit; } -template -constexpr size_t ARRAY_SIZE(const T (&array)[N]) { - return N; -} - // // Pages are assumed to be 4KB // diff --git a/MacHyperVSupport/Keyboard/HyperVKeyboard.cpp b/MacHyperVSupport/Keyboard/HyperVKeyboard.cpp index 320e229..cc94d88 100644 --- a/MacHyperVSupport/Keyboard/HyperVKeyboard.cpp +++ b/MacHyperVSupport/Keyboard/HyperVKeyboard.cpp @@ -51,7 +51,7 @@ bool HyperVKeyboard::start(IOService *provider) { } // - // Open VMBus channel. + // Open VMBus channel and connect to keyboard. // status = _hvDevice->openVMBusChannel(kHyperVKeyboardRingBufferSize, kHyperVKeyboardRingBufferSize); if (status != kIOReturnSuccess) { diff --git a/MacHyperVSupport/PCIBridge/HyperVPCIBridgePrivate.cpp b/MacHyperVSupport/PCIBridge/HyperVPCIBridgePrivate.cpp index 30af177..634b4aa 100644 --- a/MacHyperVSupport/PCIBridge/HyperVPCIBridgePrivate.cpp +++ b/MacHyperVSupport/PCIBridge/HyperVPCIBridgePrivate.cpp @@ -120,7 +120,7 @@ bool HyperVPCIBridge::negotiateProtocolVersion() { pktVersion.header.type = kHyperVPCIBridgeMessageTypeQueryProtocolVersion; // Attempt to find newest version host can support. - for (int i = 0; i < ARRAY_SIZE(usableVersions); i++) { + for (int i = 0; i < arrsize(usableVersions); i++) { pktVersion.version = usableVersions[i]; HVDBGLOG("Attempting to use version 0x%X", pktVersion.version); diff --git a/MacHyperVSupport/Storage/HyperVStorage.cpp b/MacHyperVSupport/Storage/HyperVStorage.cpp index 28ac2ac..39554e8 100644 --- a/MacHyperVSupport/Storage/HyperVStorage.cpp +++ b/MacHyperVSupport/Storage/HyperVStorage.cpp @@ -9,158 +9,114 @@ OSDefineMetaClassAndStructors(HyperVStorage, super); -// -// Hyper-V storage protocol list. -// -static const HyperVStorageProtocol storageProtocols[] = { - { - kHyperVStorageVersionWin10, - kHyperVStoragePostWin7SenseBufferSize, - 0 - }, - { - kHyperVStorageVersionWin8_1, - kHyperVStoragePostWin7SenseBufferSize, - 0 - }, - { - kHyperVStorageVersionWin8, - kHyperVStoragePostWin7SenseBufferSize, - 0 - }, - { - kHyperVStorageVersionWin7, - kHyperVStoragePreWin8SenseBufferSize, - sizeof (HyperVStorageSCSIRequestWin8Extension) - }, - { - kHyperVStorageVersionWin2008, - kHyperVStoragePreWin8SenseBufferSize, - sizeof (HyperVStorageSCSIRequestWin8Extension) - }, -}; - bool HyperVStorage::InitializeController() { - HyperVStoragePacket packet; - - if (HVCheckOffArg()) { - return false; - } - + bool result = false; + IOReturn status; + // // Get parent VMBus device object. // _hvDevice = OSDynamicCast(HyperVVMBusDevice, getProvider()); - if (_hvDevice == NULL) { + if (_hvDevice == nullptr) { + HVSYSLOG("Provider is not HyperVVMBusDevice"); return false; } _hvDevice->retain(); + HVCheckDebugArgs(); - - HVDBGLOG("Initializing Hyper-V Synthetic Storage controller"); - + HVDBGLOG("Initializing Hyper-V Synthetic Storage"); + + if (HVCheckOffArg()) { + HVSYSLOG("Disabling Hyper-V Synthetic Storage due to boot arg"); + OSSafeReleaseNULL(_hvDevice); + return false; + } + // // Assume we are on an older host and take off the Windows 8 extensions by default. // - packetSizeDelta = sizeof (HyperVStorageSCSIRequestWin8Extension); - - // - // Configure interrupt. - // macOS 10.4 always configures the interrupt in the superclass, do - // not configure the interrupt ourselves in that case. - // - _hvDevice->installPacketActions(this, OSMemberFunctionCast(HyperVVMBusDevice::PacketReadyAction, this, &HyperVStorage::handlePacket), OSMemberFunctionCast(HyperVVMBusDevice::WakePacketAction, this, &HyperVStorage::wakePacketHandler), PAGE_SIZE, getKernelVersion() >= KernelVersion::Leopard); + _packetSizeDelta = sizeof (HyperVStorageSCSIRequestWin8Extension); + + do { + // + // Install packet handler. + // macOS 10.4 always configures the interrupt in the superclass, do + // not configure the interrupt ourselves in that case. + // + status = _hvDevice->installPacketActions(this, OSMemberFunctionCast(HyperVVMBusDevice::PacketReadyAction, this, &HyperVStorage::handlePacket), + OSMemberFunctionCast(HyperVVMBusDevice::WakePacketAction, this, &HyperVStorage::wakePacketHandler), + PAGE_SIZE, getKernelVersion() >= KernelVersion::Leopard); + if (status != kIOReturnSuccess) { + HVSYSLOG("Failed to install packet handler with status 0x%X", status); + break; + } + #if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_5 - if (getKernelVersion() < KernelVersion::Leopard) { - EnableInterrupt(); - } + if (getKernelVersion() < KernelVersion::Leopard) { + EnableInterrupt(); + } #endif - - // - // Configure the channel. - // - if (_hvDevice->openVMBusChannel(kHyperVStorageRingBufferSize, kHyperVStorageRingBufferSize) != kIOReturnSuccess) { - return false; - } - - // - // Begin controller initialization. - // - bzero(&packet, sizeof (packet)); - packet.operation = kHyperVStoragePacketOperationBeginInitialization; - if (sendStorageCommand(&packet, true) != kIOReturnSuccess) { - return false; - } - - // - // Negotiate protocol version. - // - for (UInt32 i = 0; i < ARRAY_SIZE(storageProtocols); i++) { - bzero(&packet, sizeof (packet)); - packet.operation = kHyperVStoragePacketOperationQueryProtocolVersion; - packet.protocolVersion.majorMinor = storageProtocols[i].protocolVersion; - packet.protocolVersion.revision = 0; // Revision is zero for non-Windows. - - if (sendStorageCommand(&packet, false) != kIOReturnSuccess) { - return false; + + // + // Open VMBus channel and connect to storage. + // + status = _hvDevice->openVMBusChannel(kHyperVStorageRingBufferSize, kHyperVStorageRingBufferSize); + if (status != kIOReturnSuccess) { + HVSYSLOG("Failed to open VMBus channel with status 0x%X", status); + break; + } + + status = connectStorage(); + if (status != kIOReturnSuccess) { + HVSYSLOG("Failed to connect to storage device with status 0x%X", status); + break; + } + + // + // Initialize segments used for DMA. + // + _segs64 = (IODMACommand::Segment64*) IOMalloc(sizeof (IODMACommand::Segment64) * _maxPageSegments); + if (_segs64 == nullptr) { + HVSYSLOG("Failed to initialize segments"); + break; } // - // A success means this protocol version is acceptable. + // Populate HBA properties and create disk enumeration thread. // - if (packet.status == 0) { - protocolVersion = storageProtocols[i].protocolVersion; - senseBufferSize = storageProtocols[i].senseBufferSize; - packetSizeDelta = storageProtocols[i].packetSizeDelta; - HVDBGLOG("SCSI protocol version: 0x%X, sense buffer size: %u", protocolVersion, senseBufferSize); + setHBAInfo(); + _scanSCSIDiskThread = thread_call_allocate(OSMemberFunctionCast(thread_call_func_t, this, &HyperVStorage::scanSCSIDisks), this); + if (_scanSCSIDiskThread == nullptr) { + HVSYSLOG("Failed to create disk enumeration thread"); break; } - } - - if (packet.status != 0) { - return false; - } - - // - // Query controller properties. - // - bzero(&packet, sizeof (packet)); - packet.operation = kHyperVStoragePacketOperationQueryProperties; - if (sendStorageCommand(&packet, true) != kIOReturnSuccess) { - return false; - } - - subChannelsSupported = packet.storageChannelProperties.flags & kHyperVStorageFlagSupportsMultiChannel; - maxSubChannels = packet.storageChannelProperties.maxChannelCount; - maxTransferBytes = packet.storageChannelProperties.maxTransferBytes; - maxPageSegments = maxTransferBytes / PAGE_SIZE; - HVDBGLOG("Multi channel supported: %s, max sub channels: %u, max transfer bytes: %u (%u segments)", - subChannelsSupported ? "yes" : "no", maxSubChannels, maxTransferBytes, maxPageSegments); - - // - // Complete initialization. - // - bzero(&packet, sizeof (packet)); - packet.operation = kHyperVStoragePacketOperationEndInitialization; - if (sendStorageCommand(&packet, true) != kIOReturnSuccess) { - return false; - } - - segs64 = (IODMACommand::Segment64*) IOMalloc(sizeof (IODMACommand::Segment64) * maxPageSegments); - // - // Populate HBA properties. - // - setHBAInfo(); - - scanSCSIDiskThread = thread_call_allocate(OSMemberFunctionCast(thread_call_func_t, this, &HyperVStorage::scanSCSIDisks), this); - - HVSYSLOG("Initialized Hyper-V Synthetic Storage controller"); - return true; + result = true; + HVDBGLOG("Initialized Hyper-V Synthetic Storage"); + } while (false); + + if (!result) { + TerminateController(); + } + return result; } void HyperVStorage::TerminateController() { - HVDBGLOG("Controller is terminated"); + HVDBGLOG("Stopping Hyper-V Synthetic Storage"); + + if (_scanSCSIDiskThread != nullptr) { + thread_call_free(_scanSCSIDiskThread); + } + + if (_hvDevice != nullptr) { + _hvDevice->closeVMBusChannel(); + _hvDevice->uninstallPacketActions(); + OSSafeReleaseNULL(_hvDevice); + } + + if (_segs64 != nullptr) { + IOFree(_segs64, sizeof (IODMACommand::Segment64) * _maxPageSegments); + } } bool HyperVStorage::StartController() { @@ -219,7 +175,7 @@ UInt32 HyperVStorage::ReportMaximumTaskCount() { UInt32 HyperVStorage::ReportHBASpecificTaskDataSize() { HVDBGLOG("start"); - return sizeof (VMBusPacketMultiPageBuffer) + (sizeof (UInt64) * maxPageSegments); //32 * 4096; + return sizeof (VMBusPacketMultiPageBuffer) + (sizeof (UInt64) * _maxPageSegments); //32 * 4096; } UInt32 HyperVStorage::ReportHBASpecificDeviceDataSize() { @@ -242,7 +198,7 @@ bool HyperVStorage::InitializeDMASpecification(IODMACommand *command) { // Hyper-V requires page-sized segments due to its use of page numbers. // return command->initWithSpecification(kIODMACommandOutputHost64, kHyperVStorageSegmentBits, kHyperVStorageSegmentSize, - IODMACommand::kMapped, maxTransferBytes, kHyperVStorageSegmentSize); + IODMACommand::kMapped, _maxTransferBytes, kHyperVStorageSegmentSize); } SCSILogicalUnitNumber HyperVStorage::ReportHBAHighestLogicalUnitNumber() { @@ -305,7 +261,7 @@ SCSIServiceResponse HyperVStorage::ProcessParallelTask(SCSIParallelTaskIdentifie packet.scsiRequest.targetID = 0; packet.scsiRequest.lun = GetTargetIdentifier(parallelRequest); - packet.scsiRequest.senseInfoLength = senseBufferSize; + packet.scsiRequest.senseInfoLength = _senseBufferSize; packet.scsiRequest.win8Extension.srbFlags |= 0x00000008; packet.scsiRequest.length = sizeof (packet.scsiRequest); // TODO @@ -358,7 +314,7 @@ SCSIServiceResponse HyperVStorage::ProcessParallelTask(SCSIParallelTaskIdentifie } packet.scsiRequest.dataTransferLength = (UInt32) GetRequestedDataTransferCount(parallelRequest); - status = _hvDevice->writeGPADirectMultiPagePacket(&packet, sizeof (packet) - packetSizeDelta, true, + status = _hvDevice->writeGPADirectMultiPagePacket(&packet, sizeof (packet) - _packetSizeDelta, true, pagePacket, pagePacketLength, nullptr, 0, (UInt64)parallelRequest); if (status != kIOReturnSuccess) { @@ -366,7 +322,7 @@ SCSIServiceResponse HyperVStorage::ProcessParallelTask(SCSIParallelTaskIdentifie return kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; } } else { - status = _hvDevice->writeInbandPacketWithTransactionId(&packet, sizeof (packet) - packetSizeDelta, (UInt64)parallelRequest, true); + status = _hvDevice->writeInbandPacketWithTransactionId(&packet, sizeof (packet) - _packetSizeDelta, (UInt64)parallelRequest, true); if (status != kIOReturnSuccess) { HVDBGLOG("Failed to send non-data SCSI packet with status 0x%X", status); return kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; @@ -379,38 +335,38 @@ SCSIServiceResponse HyperVStorage::ProcessParallelTask(SCSIParallelTaskIdentifie void HyperVStorage::ReportHBAConstraints(OSDictionary *constraints) { OSNumber *osNumber; - + // // Populate HBA requirements and limitations. // - osNumber = OSNumber::withNumber(maxPageSegments, 32); - if (osNumber != NULL) { + osNumber = OSNumber::withNumber(_maxPageSegments, 32); + if (osNumber != nullptr) { constraints->setObject(kIOMaximumSegmentCountReadKey, osNumber); constraints->setObject(kIOMaximumSegmentCountWriteKey, osNumber); osNumber->release(); } osNumber = OSNumber::withNumber(kHyperVStorageSegmentSize, 32); - if (osNumber != NULL) { + if (osNumber != nullptr) { constraints->setObject(kIOMaximumSegmentByteCountReadKey, osNumber); constraints->setObject(kIOMaximumSegmentByteCountWriteKey, osNumber); osNumber->release(); } - + osNumber = OSNumber::withNumber(kHyperVStorageSegmentAlignment, 64); - if (osNumber != NULL) { + if (osNumber != nullptr) { constraints->setObject(kIOMinimumHBADataAlignmentMaskKey, osNumber); osNumber->release(); } - + osNumber = OSNumber::withNumber(kHyperVStorageSegmentBits, 32); - if (osNumber != NULL) { + if (osNumber != nullptr) { constraints->setObject(kIOMaximumSegmentAddressableBitCountKey, osNumber); osNumber->release(); } - + osNumber = OSNumber::withNumber(4, 32); - if (osNumber != NULL) { + if (osNumber != nullptr) { constraints->setObject(kIOMinimumSegmentAlignmentByteCountKey, osNumber); osNumber->release(); } diff --git a/MacHyperVSupport/Storage/HyperVStorage.hpp b/MacHyperVSupport/Storage/HyperVStorage.hpp index 13cf45c..1ee9765 100644 --- a/MacHyperVSupport/Storage/HyperVStorage.hpp +++ b/MacHyperVSupport/Storage/HyperVStorage.hpp @@ -28,32 +28,44 @@ class HyperVStorage : public IOSCSIParallelInterfaceController { private: HyperVVMBusDevice *_hvDevice = nullptr; - - UInt32 protocolVersion; - UInt32 senseBufferSize; - UInt32 packetSizeDelta; - - bool subChannelsSupported; - UInt16 maxSubChannels; - UInt32 maxTransferBytes; - UInt32 maxPageSegments; - IODMACommand::Segment64 *segs64; + // + // Storage protocol. + // + UInt32 _protocolVersion = 0; + UInt32 _senseBufferSize = 0; + UInt32 _packetSizeDelta = 0; + bool _subChannelsSupported = false; + UInt16 _maxSubChannels = 0; + UInt32 _maxTransferBytes = 0; + UInt32 _maxPageSegments = 0; + + // + // Segments for DMA transfers. + // + IODMACommand::Segment64 *_segs64 = nullptr; + + // + // Thread for disk enumeration. + // + thread_call_t _scanSCSIDiskThread = nullptr; + + // + // Packets and I/O. + // bool wakePacketHandler(VMBusPacketHeader *pktHeader, UInt32 pktHeaderLength, UInt8 *pktData, UInt32 pktDataLength); void handlePacket(VMBusPacketHeader *pktHeader, UInt32 pktHeaderLength, UInt8 *pktData, UInt32 pktDataLength); void handleIOCompletion(UInt64 transactionId, HyperVStoragePacket *packet); - IOReturn sendStorageCommand(HyperVStoragePacket *packet, bool checkCompletion); IOReturn prepareDataTransfer(SCSIParallelTaskIdentifier parallelRequest, VMBusPacketMultiPageBuffer **pagePacket, UInt32 *pagePacketLength); void completeDataTransfer(SCSIParallelTaskIdentifier parallelRequest, HyperVStoragePacket *packet); - - void setHBAInfo(); - + // - // Disk enumeration. + // Disk enumeration and misc. // - thread_call_t scanSCSIDiskThread; + void setHBAInfo(); + IOReturn connectStorage(); bool checkSCSIDiskPresent(UInt8 diskId); void startDiskEnumeration(); void scanSCSIDisks(); @@ -62,40 +74,37 @@ class HyperVStorage : public IOSCSIParallelInterfaceController { // // IOSCSIParallelInterfaceController overrides. // - virtual bool InitializeController() APPLE_KEXT_OVERRIDE; - virtual void TerminateController() APPLE_KEXT_OVERRIDE; - virtual bool StartController() APPLE_KEXT_OVERRIDE; - virtual void StopController() APPLE_KEXT_OVERRIDE; - virtual bool DoesHBAPerformDeviceManagement() APPLE_KEXT_OVERRIDE; - virtual void HandleInterruptRequest() APPLE_KEXT_OVERRIDE; - virtual SCSIInitiatorIdentifier ReportInitiatorIdentifier() APPLE_KEXT_OVERRIDE; - virtual SCSIDeviceIdentifier ReportHighestSupportedDeviceID() APPLE_KEXT_OVERRIDE; - virtual UInt32 ReportMaximumTaskCount() APPLE_KEXT_OVERRIDE; - virtual UInt32 ReportHBASpecificTaskDataSize() APPLE_KEXT_OVERRIDE; - virtual UInt32 ReportHBASpecificDeviceDataSize() APPLE_KEXT_OVERRIDE; - - virtual IOInterruptEventSource *CreateDeviceInterrupt(IOInterruptEventSource::Action action, - IOFilterInterruptEventSource::Filter filter, - IOService *provider) APPLE_KEXT_OVERRIDE; - - virtual bool InitializeDMASpecification(IODMACommand *command) APPLE_KEXT_OVERRIDE; - + bool InitializeController() APPLE_KEXT_OVERRIDE; + void TerminateController() APPLE_KEXT_OVERRIDE; + bool StartController() APPLE_KEXT_OVERRIDE; + void StopController() APPLE_KEXT_OVERRIDE; + bool DoesHBAPerformDeviceManagement() APPLE_KEXT_OVERRIDE; + void HandleInterruptRequest() APPLE_KEXT_OVERRIDE; + SCSIInitiatorIdentifier ReportInitiatorIdentifier() APPLE_KEXT_OVERRIDE; + SCSIDeviceIdentifier ReportHighestSupportedDeviceID() APPLE_KEXT_OVERRIDE; + UInt32 ReportMaximumTaskCount() APPLE_KEXT_OVERRIDE; + UInt32 ReportHBASpecificTaskDataSize() APPLE_KEXT_OVERRIDE; + UInt32 ReportHBASpecificDeviceDataSize() APPLE_KEXT_OVERRIDE; + IOInterruptEventSource *CreateDeviceInterrupt(IOInterruptEventSource::Action action, + IOFilterInterruptEventSource::Filter filter, + IOService *provider) APPLE_KEXT_OVERRIDE; + bool InitializeDMASpecification(IODMACommand *command) APPLE_KEXT_OVERRIDE; + public: // // IOSCSIParallelInterfaceController overrides. // - virtual bool DoesHBASupportSCSIParallelFeature(SCSIParallelFeature theFeature) APPLE_KEXT_OVERRIDE; - virtual bool InitializeTargetForID(SCSITargetIdentifier targetID) APPLE_KEXT_OVERRIDE; - virtual SCSILogicalUnitNumber ReportHBAHighestLogicalUnitNumber() APPLE_KEXT_OVERRIDE; - virtual SCSIServiceResponse AbortTaskRequest(SCSITargetIdentifier theT, SCSILogicalUnitNumber theL, SCSITaggedTaskIdentifier theQ) APPLE_KEXT_OVERRIDE; - virtual SCSIServiceResponse AbortTaskSetRequest(SCSITargetIdentifier theT, SCSILogicalUnitNumber theL) APPLE_KEXT_OVERRIDE; - virtual SCSIServiceResponse ClearACARequest(SCSITargetIdentifier theT, SCSILogicalUnitNumber theL) APPLE_KEXT_OVERRIDE; - virtual SCSIServiceResponse ClearTaskSetRequest(SCSITargetIdentifier theT, SCSILogicalUnitNumber theL) APPLE_KEXT_OVERRIDE; - virtual SCSIServiceResponse LogicalUnitResetRequest(SCSITargetIdentifier theT, SCSILogicalUnitNumber theL) APPLE_KEXT_OVERRIDE; - virtual SCSIServiceResponse TargetResetRequest(SCSITargetIdentifier theT) APPLE_KEXT_OVERRIDE; - virtual SCSIServiceResponse ProcessParallelTask(SCSIParallelTaskIdentifier parallelRequest) APPLE_KEXT_OVERRIDE; - - virtual void ReportHBAConstraints(OSDictionary *constraints) APPLE_KEXT_OVERRIDE; + bool DoesHBASupportSCSIParallelFeature(SCSIParallelFeature theFeature) APPLE_KEXT_OVERRIDE; + bool InitializeTargetForID(SCSITargetIdentifier targetID) APPLE_KEXT_OVERRIDE; + SCSILogicalUnitNumber ReportHBAHighestLogicalUnitNumber() APPLE_KEXT_OVERRIDE; + SCSIServiceResponse AbortTaskRequest(SCSITargetIdentifier theT, SCSILogicalUnitNumber theL, SCSITaggedTaskIdentifier theQ) APPLE_KEXT_OVERRIDE; + SCSIServiceResponse AbortTaskSetRequest(SCSITargetIdentifier theT, SCSILogicalUnitNumber theL) APPLE_KEXT_OVERRIDE; + SCSIServiceResponse ClearACARequest(SCSITargetIdentifier theT, SCSILogicalUnitNumber theL) APPLE_KEXT_OVERRIDE; + SCSIServiceResponse ClearTaskSetRequest(SCSITargetIdentifier theT, SCSILogicalUnitNumber theL) APPLE_KEXT_OVERRIDE; + SCSIServiceResponse LogicalUnitResetRequest(SCSITargetIdentifier theT, SCSILogicalUnitNumber theL) APPLE_KEXT_OVERRIDE; + SCSIServiceResponse TargetResetRequest(SCSITargetIdentifier theT) APPLE_KEXT_OVERRIDE; + SCSIServiceResponse ProcessParallelTask(SCSIParallelTaskIdentifier parallelRequest) APPLE_KEXT_OVERRIDE; + void ReportHBAConstraints(OSDictionary *constraints) APPLE_KEXT_OVERRIDE; }; #endif diff --git a/MacHyperVSupport/Storage/HyperVStoragePrivate.cpp b/MacHyperVSupport/Storage/HyperVStoragePrivate.cpp index 6425ece..bb5dca4 100644 --- a/MacHyperVSupport/Storage/HyperVStoragePrivate.cpp +++ b/MacHyperVSupport/Storage/HyperVStoragePrivate.cpp @@ -7,6 +7,37 @@ #include "HyperVStorage.hpp" +// +// Hyper-V storage protocol list. +// +static const HyperVStorageProtocol storageProtocols[] = { + { + kHyperVStorageVersionWin10, + kHyperVStoragePostWin7SenseBufferSize, + 0 + }, + { + kHyperVStorageVersionWin8_1, + kHyperVStoragePostWin7SenseBufferSize, + 0 + }, + { + kHyperVStorageVersionWin8, + kHyperVStoragePostWin7SenseBufferSize, + 0 + }, + { + kHyperVStorageVersionWin7, + kHyperVStoragePreWin8SenseBufferSize, + sizeof (HyperVStorageSCSIRequestWin8Extension) + }, + { + kHyperVStorageVersionWin2008, + kHyperVStoragePreWin8SenseBufferSize, + sizeof (HyperVStorageSCSIRequestWin8Extension) + }, +}; + bool HyperVStorage::wakePacketHandler(VMBusPacketHeader *pktHeader, UInt32 pktHeaderLength, UInt8 *pktData, UInt32 pktDataLength) { return true; } @@ -63,7 +94,7 @@ IOReturn HyperVStorage::sendStorageCommand(HyperVStoragePacket *packet, bool che // // Send packet and get response. // - IOReturn status = _hvDevice->writeInbandPacket(packet, sizeof (HyperVStoragePacket) - packetSizeDelta, true, packet, sizeof (HyperVStoragePacket)); + IOReturn status = _hvDevice->writeInbandPacket(packet, sizeof (HyperVStoragePacket) - _packetSizeDelta, true, packet, sizeof (HyperVStoragePacket)); if (status != kIOReturnSuccess) { return status; } @@ -83,7 +114,7 @@ IOReturn HyperVStorage::sendStorageCommand(HyperVStoragePacket *packet, bool che IOReturn HyperVStorage::prepareDataTransfer(SCSIParallelTaskIdentifier parallelRequest, VMBusPacketMultiPageBuffer **pagePacket, UInt32 *pagePacketLength) { IOReturn status; UInt64 offsetSeg = 0; - UInt32 numSegs = maxPageSegments; + UInt32 numSegs = _maxPageSegments; UInt64 dataLength = GetRequestedDataTransferCount(parallelRequest); IODMACommand *dmaCommand = GetDMACommand(parallelRequest); @@ -107,7 +138,7 @@ IOReturn HyperVStorage::prepareDataTransfer(SCSIParallelTaskIdentifier parallelR return status; } - status = dmaCommand->gen64IOVMSegments(&offsetSeg, segs64, &numSegs); + status = dmaCommand->gen64IOVMSegments(&offsetSeg, _segs64, &numSegs); if (status != kIOReturnSuccess) { HVSYSLOG("Failed to generate segments for buffer of %u bytes", dataLength, status); dmaCommand->complete(); @@ -122,12 +153,12 @@ IOReturn HyperVStorage::prepareDataTransfer(SCSIParallelTaskIdentifier parallelR for (UInt32 i = 0; i < numSegs; i++) { if (i != 0 && i != (numSegs - 1)) { - if (segs64[i].fLength != PAGE_SIZE && segs64[i].fLength != 0) { - panic("Invalid segment %u: 0x%llX %llu bytes", (unsigned int) i, segs64[i].fIOVMAddr, segs64[i].fLength); + if (_segs64[i].fLength != PAGE_SIZE && _segs64[i].fLength != 0) { + panic("Invalid segment %u: 0x%llX %llu bytes", (unsigned int) i, _segs64[i].fIOVMAddr, _segs64[i].fLength); } } - (*pagePacket)->range.pfns[i] = segs64[i].fIOVMAddr >> PAGE_SHIFT; + (*pagePacket)->range.pfns[i] = _segs64[i].fIOVMAddr >> PAGE_SHIFT; } *pagePacketLength = sizeof (**pagePacket) + (sizeof (UInt64) * numSegs); @@ -165,8 +196,8 @@ void HyperVStorage::setHBAInfo() { // Populate protocol version. // snprintf(verString, sizeof (verString), "%u.%u", - (unsigned int) HYPERV_STORAGE_PROTCOL_VERSION_MAJOR(protocolVersion), - (unsigned int) HYPERV_STORAGE_PROTCOL_VERSION_MINOR(protocolVersion)); + (unsigned int) HYPERV_STORAGE_PROTCOL_VERSION_MAJOR(_protocolVersion), + (unsigned int) HYPERV_STORAGE_PROTCOL_VERSION_MINOR(_protocolVersion)); propString = OSString::withCString(verString); if (propString != nullptr) { SetHBAProperty(kIOPropertyProductRevisionLevelKey, propString); @@ -174,6 +205,84 @@ void HyperVStorage::setHBAInfo() { } } +IOReturn HyperVStorage::connectStorage() { + IOReturn status; + HyperVStoragePacket storPkt; + + // + // Begin controller initialization. + // + bzero(&storPkt, sizeof (storPkt)); + storPkt.operation = kHyperVStoragePacketOperationBeginInitialization; + status = sendStorageCommand(&storPkt, true); + if (status != kIOReturnSuccess) { + HVSYSLOG("Failed to send begin initialization command with status 0x%X", status); + return status; + } + + // + // Negotiate protocol version. + // + for (UInt32 i = 0; i < arrsize(storageProtocols); i++) { + bzero(&storPkt, sizeof (storPkt)); + storPkt.operation = kHyperVStoragePacketOperationQueryProtocolVersion; + storPkt.protocolVersion.majorMinor = storageProtocols[i].protocolVersion; + storPkt.protocolVersion.revision = 0; // Revision is zero for non-Windows. + + status = sendStorageCommand(&storPkt, false); + if (status != kIOReturnSuccess) { + HVSYSLOG("Failed to send query protocol command with status 0x%X", status); + return status; + } + + // + // A success means this protocol version is acceptable. + // + if (storPkt.status == 0) { + _protocolVersion = storageProtocols[i].protocolVersion; + _senseBufferSize = storageProtocols[i].senseBufferSize; + _packetSizeDelta = storageProtocols[i].packetSizeDelta; + HVDBGLOG("SCSI protocol version: 0x%X, sense buffer size: %u", _protocolVersion, _senseBufferSize); + break; + } + } + if (storPkt.status != 0) { + HVSYSLOG("Query protocol command return error status 0x%X", storPkt.status); + return kIOReturnIOError; + } + + // + // Query controller properties. + // + bzero(&storPkt, sizeof (storPkt)); + storPkt.operation = kHyperVStoragePacketOperationQueryProperties; + status = sendStorageCommand(&storPkt, true); + if (status != kIOReturnSuccess) { + HVSYSLOG("Failed to send query properties command with status 0x%X", status); + return status; + } + + _subChannelsSupported = storPkt.storageChannelProperties.flags & kHyperVStorageFlagSupportsMultiChannel; + _maxSubChannels = storPkt.storageChannelProperties.maxChannelCount; + _maxTransferBytes = storPkt.storageChannelProperties.maxTransferBytes; + _maxPageSegments = _maxTransferBytes / PAGE_SIZE; + HVDBGLOG("Multi channel supported: %s, max sub channels: %u, max transfer bytes: %u (%u segments)", + _subChannelsSupported ? "yes" : "no", _maxSubChannels, _maxTransferBytes, _maxPageSegments); + + // + // Complete initialization. + // + bzero(&storPkt, sizeof (storPkt)); + storPkt.operation = kHyperVStoragePacketOperationEndInitialization; + status = sendStorageCommand(&storPkt, true); + if (status != kIOReturnSuccess) { + HVSYSLOG("Failed to send end initialization command with status 0x%X", status); + return status; + } + + return kIOReturnSuccess; +} + bool HyperVStorage::checkSCSIDiskPresent(UInt8 diskId) { IOReturn status; HyperVStoragePacket packet = { }; @@ -188,7 +297,7 @@ bool HyperVStorage::checkSCSIDiskPresent(UInt8 diskId) { packet.scsiRequest.lun = diskId; packet.scsiRequest.win8Extension.srbFlags |= 0x00000008; packet.scsiRequest.length = sizeof (packet.scsiRequest); - packet.scsiRequest.senseInfoLength = senseBufferSize; + packet.scsiRequest.senseInfoLength = _senseBufferSize; packet.scsiRequest.dataIn = kHyperVStorageSCSIRequestTypeUnknown; // @@ -205,7 +314,7 @@ bool HyperVStorage::checkSCSIDiskPresent(UInt8 diskId) { // // Send SCSI packet and check result to see if disk is present. // - status = _hvDevice->writeInbandPacket(&packet, sizeof (packet) - packetSizeDelta, true, &packet, sizeof (packet)); + status = _hvDevice->writeInbandPacket(&packet, sizeof (packet) - _packetSizeDelta, true, &packet, sizeof (packet)); if (status != kIOReturnSuccess) { HVDBGLOG("Failed to send TEST UNIT READY SCSI packet with status 0x%X", status); return false; @@ -220,7 +329,7 @@ void HyperVStorage::startDiskEnumeration() { // Begin disk enumeration on separate thread. // HVDBGLOG("Starting disk enumeration thread"); - thread_call_enter(scanSCSIDiskThread); + thread_call_enter(_scanSCSIDiskThread); } void HyperVStorage::scanSCSIDisks() {