From 0fb421b1888188800371b6b3dac47f686b0d687b Mon Sep 17 00:00:00 2001 From: John Davis Date: Sat, 18 Sep 2021 11:00:07 -0500 Subject: [PATCH] Initial networking support (#2) --- MacHyperVSupport.xcodeproj/project.pbxproj | 8 + MacHyperVSupport/Info.plist | 4 +- .../IntegrationComponents/HyperVHeartbeat.cpp | 43 +- .../IntegrationComponents/HyperVHeartbeat.hpp | 8 +- .../IntegrationComponents/HyperVICService.cpp | 12 +- .../IntegrationComponents/HyperVICService.hpp | 3 +- .../IntegrationComponents/HyperVShutdown.cpp | 52 +- .../IntegrationComponents/HyperVShutdown.hpp | 8 +- MacHyperVSupport/Keyboard/HyperVKeyboard.cpp | 97 ++-- MacHyperVSupport/Keyboard/HyperVKeyboard.hpp | 3 +- .../Keyboard/HyperVKeyboardRegs.hpp | 10 + MacHyperVSupport/Mouse/HyperVMouse.cpp | 32 +- MacHyperVSupport/Mouse/HyperVMouse.hpp | 12 +- MacHyperVSupport/Mouse/HyperVMousePrivate.cpp | 138 ++--- MacHyperVSupport/Mouse/HyperVMouseRegs.hpp | 8 +- MacHyperVSupport/Network/HyperVNetwork.cpp | 43 +- MacHyperVSupport/Network/HyperVNetwork.hpp | 107 +++- .../Network/HyperVNetworkPrivate.cpp | 305 +++++++++++ .../Network/HyperVNetworkRNDIS.cpp | 293 +++++++++++ .../Network/HyperVNetworkRegs.hpp | 486 +++++++++++++++++- MacHyperVSupport/Storage/HyperVStorage.cpp | 30 +- MacHyperVSupport/Storage/HyperVStorage.hpp | 3 +- .../Storage/HyperVStoragePrivate.cpp | 81 ++- .../VMBusController/HyperVVMBusController.hpp | 9 +- MacHyperVSupport/VMBusController/SynIC.cpp | 44 +- MacHyperVSupport/VMBusController/VMBus.cpp | 7 +- MacHyperVSupport/VMBusController/VMBus.hpp | 40 ++ .../VMBusController/VMBusChannel.cpp | 53 +- .../VMBusController/VMBusDriver.hpp | 2 +- .../VMBusDevice/HyperVVMBusDevice.cpp | 277 +++++++++- .../VMBusDevice/HyperVVMBusDevice.hpp | 82 ++- .../VMBusDevice/HyperVVMBusDeviceInternal.hpp | 3 + .../VMBusDevice/HyperVVMBusDevicePrivate.cpp | 355 +++++++------ 33 files changed, 2156 insertions(+), 502 deletions(-) create mode 100644 MacHyperVSupport/Network/HyperVNetworkPrivate.cpp create mode 100644 MacHyperVSupport/Network/HyperVNetworkRNDIS.cpp diff --git a/MacHyperVSupport.xcodeproj/project.pbxproj b/MacHyperVSupport.xcodeproj/project.pbxproj index 6e71ffc..4626307 100644 --- a/MacHyperVSupport.xcodeproj/project.pbxproj +++ b/MacHyperVSupport.xcodeproj/project.pbxproj @@ -31,6 +31,8 @@ 419B88C5263F1F85005A9977 /* VMBus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 419B88C4263F1F85005A9977 /* VMBus.cpp */; }; 41B41BDD26C74B4C00926A0D /* HyperVNetwork.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41B41BDB26C74B4C00926A0D /* HyperVNetwork.cpp */; }; 41B41BDE26C74B4C00926A0D /* HyperVNetwork.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 41B41BDC26C74B4C00926A0D /* HyperVNetwork.hpp */; }; + 41B41BE426C84A9F00926A0D /* HyperVNetworkPrivate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41B41BE326C84A9F00926A0D /* HyperVNetworkPrivate.cpp */; }; + 41B41BE726CDC42D00926A0D /* HyperVNetworkRNDIS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41B41BE626CDC42D00926A0D /* HyperVNetworkRNDIS.cpp */; }; 41BE411A263EDFAF0018C52B /* HyperVVMBusController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41BE4118263EDFAF0018C52B /* HyperVVMBusController.cpp */; }; 41BE411B263EDFAF0018C52B /* HyperVVMBusController.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 41BE4119263EDFAF0018C52B /* HyperVVMBusController.hpp */; }; 41E2EC78263F894300BBE18F /* SynIC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41E2EC77263F894300BBE18F /* SynIC.cpp */; }; @@ -114,6 +116,8 @@ 41B41BDB26C74B4C00926A0D /* HyperVNetwork.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = HyperVNetwork.cpp; sourceTree = ""; }; 41B41BDC26C74B4C00926A0D /* HyperVNetwork.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = HyperVNetwork.hpp; sourceTree = ""; }; 41B41BE126C80DEC00926A0D /* HyperVNetworkRegs.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = HyperVNetworkRegs.hpp; sourceTree = ""; }; + 41B41BE326C84A9F00926A0D /* HyperVNetworkPrivate.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = HyperVNetworkPrivate.cpp; sourceTree = ""; }; + 41B41BE626CDC42D00926A0D /* HyperVNetworkRNDIS.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = HyperVNetworkRNDIS.cpp; sourceTree = ""; }; 41BE4104263EDE380018C52B /* MacHyperVSupport.kext */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MacHyperVSupport.kext; sourceTree = BUILT_PRODUCTS_DIR; }; 41BE410B263EDE380018C52B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 41BE4113263EDEA10018C52B /* libkmod.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libkmod.a; path = MacKernelSDK/Library/x86_64/libkmod.a; sourceTree = ""; }; @@ -243,6 +247,8 @@ 41B41BDB26C74B4C00926A0D /* HyperVNetwork.cpp */, 41B41BDC26C74B4C00926A0D /* HyperVNetwork.hpp */, 41B41BE126C80DEC00926A0D /* HyperVNetworkRegs.hpp */, + 41B41BE626CDC42D00926A0D /* HyperVNetworkRNDIS.cpp */, + 41B41BE326C84A9F00926A0D /* HyperVNetworkPrivate.cpp */, ); path = Network; sourceTree = ""; @@ -572,8 +578,10 @@ 41F2E44B2666F37B00CE26CE /* HyperVPCIProvider.cpp in Sources */, 418F843D2648BA38003F8520 /* HyperVStorage.cpp in Sources */, 41F2E42D2665B64D00CE26CE /* HyperVPlatformProvider.cpp in Sources */, + 41B41BE426C84A9F00926A0D /* HyperVNetworkPrivate.cpp in Sources */, 41225F572644D98500574E86 /* HyperVHeartbeat.cpp in Sources */, 41B41BDD26C74B4C00926A0D /* HyperVNetwork.cpp in Sources */, + 41B41BE726CDC42D00926A0D /* HyperVNetworkRNDIS.cpp in Sources */, 41078473264603F1005894D4 /* VMBusChannel.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/MacHyperVSupport/Info.plist b/MacHyperVSupport/Info.plist index 92e704d..d80b067 100644 --- a/MacHyperVSupport/Info.plist +++ b/MacHyperVSupport/Info.plist @@ -64,7 +64,7 @@ HyperVMouse - AbsoluteAxisBoundsRemovalPercentage + AbsoluteAxisBoundsRemovalPercentage 0 CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) @@ -195,6 +195,8 @@ 1.0.0d1 com.apple.iokit.IOHIDFamily 1.1 + com.apple.iokit.IONetworkingFamily + 1.5.0 com.apple.iokit.IOPCIFamily 1.0.0b1 com.apple.iokit.IOSCSIParallelFamily diff --git a/MacHyperVSupport/IntegrationComponents/HyperVHeartbeat.cpp b/MacHyperVSupport/IntegrationComponents/HyperVHeartbeat.cpp index 7caa450..dd34096 100644 --- a/MacHyperVSupport/IntegrationComponents/HyperVHeartbeat.cpp +++ b/MacHyperVSupport/IntegrationComponents/HyperVHeartbeat.cpp @@ -21,26 +21,29 @@ bool HyperVHeartbeat::start(IOService *provider) { return super::start(provider); } -void HyperVHeartbeat::processMessage() { +bool HyperVHeartbeat::processMessage() { VMBusICMessageHeartbeat heartbeatMsg; - - HyperVVMBusDeviceRequest request = { 0 }; - request.responseData = &heartbeatMsg; - request.responseDataLength = sizeof (heartbeatMsg); - + // // Ignore errors and the acknowledgement interrupt (no data to read). // - if (hvDevice->doRequest(&request) != kIOReturnSuccess || request.responseDataLength == 0) { - return; + UInt32 pktDataLength; + if (!hvDevice->nextInbandPacketAvailable(&pktDataLength) || pktDataLength > sizeof (heartbeatMsg)) { + return false; + } + + // + // Read and parse inbound inband packet. + // + if (hvDevice->readInbandCompletionPacket(&heartbeatMsg, sizeof (heartbeatMsg), NULL) != kIOReturnSuccess) { + return false; } - switch (heartbeatMsg.header.type) { case kVMBusICMessageTypeNegotiate: firstHeartbeatReceived = false; createNegotiationResponse(&heartbeatMsg.negotiate, 3, 3); break; - + case kVMBusICMessageTypeHeartbeat: // // Increment sequence. @@ -48,27 +51,23 @@ void HyperVHeartbeat::processMessage() { // //DBGLOG("Got heartbeat, seq = %u", heartbeatMsg.heartbeat.sequence); heartbeatMsg.heartbeat.sequence++; - + if (!firstHeartbeatReceived) { firstHeartbeatReceived = true; SYSLOG("Initialized Hyper-V Heartbeat"); } break; - + default: DBGLOG("Unknown heartbeat message type %u", heartbeatMsg.header.type); heartbeatMsg.header.status = kHyperVStatusFail; break; } - + + // + // Send response back to Hyper-V. The packet size will always be the same as the original inbound one. + // heartbeatMsg.header.flags = kVMBusICFlagTransaction | kVMBusICFlagResponse; - - UInt32 sendLength = request.responseDataLength; - - request = { 0 }; - request.sendData = &heartbeatMsg; - request.sendDataLength = sendLength; - request.sendPacketType = kVMBusPacketTypeDataInband; - - hvDevice->doRequest(&request); + hvDevice->writeInbandPacket(&heartbeatMsg, pktDataLength, false); + return true; } diff --git a/MacHyperVSupport/IntegrationComponents/HyperVHeartbeat.hpp b/MacHyperVSupport/IntegrationComponents/HyperVHeartbeat.hpp index 63db694..a7cce79 100644 --- a/MacHyperVSupport/IntegrationComponents/HyperVHeartbeat.hpp +++ b/MacHyperVSupport/IntegrationComponents/HyperVHeartbeat.hpp @@ -12,13 +12,13 @@ class HyperVHeartbeat : public HyperVICService { OSDeclareDefaultStructors(HyperVHeartbeat); - + private: bool firstHeartbeatReceived; - + protected: - void processMessage() APPLE_KEXT_OVERRIDE; - + bool processMessage() APPLE_KEXT_OVERRIDE; + public: // // IOService overrides. diff --git a/MacHyperVSupport/IntegrationComponents/HyperVICService.cpp b/MacHyperVSupport/IntegrationComponents/HyperVICService.cpp index a100205..1eaada5 100644 --- a/MacHyperVSupport/IntegrationComponents/HyperVICService.cpp +++ b/MacHyperVSupport/IntegrationComponents/HyperVICService.cpp @@ -29,11 +29,17 @@ bool HyperVICService::start(IOService *provider) { } hvDevice->retain(); + // + // Configure interrupt. + // + interruptSource = IOInterruptEventSource::interruptEventSource(this, OSMemberFunctionCast(IOInterruptEventAction, this, &HyperVICService::handleInterrupt), provider, 0); + getWorkLoop()->addEventSource(interruptSource); + interruptSource->enable(); + // // Configure the channel. // - if (!hvDevice->openChannel(kHyperVICBufferSize, kHyperVICBufferSize, - this, OSMemberFunctionCast(IOInterruptEventAction, this, &HyperVICService::handleInterrupt))) { + if (!hvDevice->openChannel(kHyperVICBufferSize, kHyperVICBufferSize)) { super::stop(provider); return false; } @@ -98,5 +104,5 @@ bool HyperVICService::createNegotiationResponse(VMBusICMessageNegotiate *negMsg, } void HyperVICService::handleInterrupt(OSObject *owner, IOInterruptEventSource *sender, int count) { - processMessage(); + while (processMessage()); } diff --git a/MacHyperVSupport/IntegrationComponents/HyperVICService.hpp b/MacHyperVSupport/IntegrationComponents/HyperVICService.hpp index d6660ed..0b82c04 100644 --- a/MacHyperVSupport/IntegrationComponents/HyperVICService.hpp +++ b/MacHyperVSupport/IntegrationComponents/HyperVICService.hpp @@ -21,11 +21,12 @@ class HyperVICService : public IOService { private: void handleInterrupt(OSObject *owner, IOInterruptEventSource *sender, int count); + IOInterruptEventSource *interruptSource; protected: HyperVVMBusDevice *hvDevice; - virtual void processMessage() = 0; + virtual bool processMessage() = 0; bool createNegotiationResponse(VMBusICMessageNegotiate *negMsg, UInt32 fwVersion, UInt32 msgVersion); diff --git a/MacHyperVSupport/IntegrationComponents/HyperVShutdown.cpp b/MacHyperVSupport/IntegrationComponents/HyperVShutdown.cpp index 84310a5..2eb3798 100644 --- a/MacHyperVSupport/IntegrationComponents/HyperVShutdown.cpp +++ b/MacHyperVSupport/IntegrationComponents/HyperVShutdown.cpp @@ -19,62 +19,64 @@ bool HyperVShutdown::start(IOService *provider) { if (!super::start(provider)) { return false; } - + SYSLOG("Initialized Hyper-V Guest Shutdown"); return true; } -void HyperVShutdown::processMessage() { +bool HyperVShutdown::processMessage() { VMBusICMessageShutdown shutdownMsg; - - HyperVVMBusDeviceRequest request = { 0 }; - request.responseData = &shutdownMsg; - request.responseDataLength = sizeof (shutdownMsg); - + // // Ignore errors and the acknowledgement interrupt (no data to read). // - if (hvDevice->doRequest(&request) != kIOReturnSuccess || request.responseDataLength == 0) { - return; + UInt32 pktDataLength; + if (!hvDevice->nextInbandPacketAvailable(&pktDataLength) || pktDataLength > sizeof (shutdownMsg)) { + return false; + } + + // + // Read and parse inbound inband packet. + // + if (hvDevice->readInbandCompletionPacket(&shutdownMsg, sizeof (shutdownMsg), NULL) != kIOReturnSuccess) { + return false; } - + bool doShutdown = false; - switch (shutdownMsg.header.type) { case kVMBusICMessageTypeNegotiate: createNegotiationResponse(&shutdownMsg.negotiate, 3, 3); break; - + case kVMBusICMessageTypeShutdown: doShutdown = handleShutdown(&shutdownMsg.shutdown); break; - + default: DBGLOG("Unknown shutdown message type %u", shutdownMsg.header.type); shutdownMsg.header.status = kHyperVStatusFail; break; } - + + // + // Send response back to Hyper-V. The packet size will always be the same as the original inbound one. + // shutdownMsg.header.flags = kVMBusICFlagTransaction | kVMBusICFlagResponse; - - UInt32 sendLength = request.responseDataLength; - - request = { 0 }; - request.sendData = &shutdownMsg; - request.sendDataLength = sendLength; - request.sendPacketType = kVMBusPacketTypeDataInband; - - hvDevice->doRequest(&request); + hvDevice->writeInbandPacket(&shutdownMsg, pktDataLength, false); + // + // Shutdown machine if requested. This should not return. + // if (doShutdown) { SYSLOG("Shutting down system"); HyperVPlatformProvider::getInstance()->shutdownSystem(); } + return true; } bool HyperVShutdown::handleShutdown(VMBusICMessageShutdownData *shutdownData) { DBGLOG("Shutdown request received: flags 0x%X, reason 0x%X", shutdownData->flags, shutdownData->reason); - + // // Report back to Hyper-V if we can shutdown system. // @@ -83,7 +85,7 @@ bool HyperVShutdown::handleShutdown(VMBusICMessageShutdownData *shutdownData) { if (provider != NULL) { result = provider->canShutdownSystem(); } - + shutdownData->header.status = result ? kHyperVStatusSuccess : kHyperVStatusFail; if (!result) { SYSLOG("Platform does not support shutdown"); diff --git a/MacHyperVSupport/IntegrationComponents/HyperVShutdown.hpp b/MacHyperVSupport/IntegrationComponents/HyperVShutdown.hpp index 2e04e21..77452e8 100644 --- a/MacHyperVSupport/IntegrationComponents/HyperVShutdown.hpp +++ b/MacHyperVSupport/IntegrationComponents/HyperVShutdown.hpp @@ -12,13 +12,13 @@ class HyperVShutdown : public HyperVICService { OSDeclareDefaultStructors(HyperVShutdown); - + private: bool handleShutdown(VMBusICMessageShutdownData *shutdownData); - + protected: - void processMessage() APPLE_KEXT_OVERRIDE; - + bool processMessage() APPLE_KEXT_OVERRIDE; + public: // // IOService overrides. diff --git a/MacHyperVSupport/Keyboard/HyperVKeyboard.cpp b/MacHyperVSupport/Keyboard/HyperVKeyboard.cpp index d7bd72a..9456f4e 100644 --- a/MacHyperVSupport/Keyboard/HyperVKeyboard.cpp +++ b/MacHyperVSupport/Keyboard/HyperVKeyboard.cpp @@ -27,10 +27,21 @@ bool HyperVKeyboard::start(IOService *provider) { } hvDevice->retain(); + // + // Configure interrupt. + // + interruptSource = + IOInterruptEventSource::interruptEventSource(this, OSMemberFunctionCast(IOInterruptEventAction, this, &HyperVKeyboard::handleInterrupt), provider, 0); + getWorkLoop()->addEventSource(interruptSource); + interruptSource->enable(); + // // Configure the channel. // - if (!hvDevice->openChannel(kHyperVKeyboardRingBufferSize, kHyperVKeyboardRingBufferSize, this, OSMemberFunctionCast(IOInterruptEventAction, this, &HyperVKeyboard::handleInterrupt))) { + if (!hvDevice->openChannel(kHyperVKeyboardRingBufferSize, kHyperVKeyboardRingBufferSize)) { + interruptSource->disable(); + getWorkLoop()->removeEventSource(interruptSource); + OSSafeReleaseNULL(interruptSource); super::stop(provider); return false; } @@ -48,14 +59,7 @@ bool HyperVKeyboard::connectKeyboard() { requestMsg.header.type = kHyperVKeyboardMessageTypeProtocolRequest; requestMsg.versionRequested = kHyperVKeyboardVersion; - HyperVVMBusDeviceRequest request; - request.sendData = &requestMsg; - request.responseRequired = true; - request.sendDataLength = sizeof (requestMsg); - request.responseData = NULL; - request.sendPacketType = kVMBusPacketTypeDataInband; - - hvDevice->doRequest(&request); + hvDevice->writeInbandPacket(&requestMsg, sizeof (requestMsg), true); return true; } @@ -75,40 +79,55 @@ inline UInt32 getKeyCode(HyperVKeyboardMessageKeystroke *keyEvent) { } void HyperVKeyboard::handleInterrupt(OSObject *owner, IOInterruptEventSource *sender, int count) { - UInt8 data[1024]; - HyperVKeyboardMessageProtocolResponse *responseMsg = (HyperVKeyboardMessageProtocolResponse*)data; - HyperVKeyboardMessageKeystroke *eventMsg = (HyperVKeyboardMessageKeystroke*)data; - - HyperVVMBusDeviceRequest request = { 0 }; - request.responseData = responseMsg; + UInt8 data128[128]; + do { - request.responseDataLength = sizeof(data); - - hvDevice->doRequest(&request); - if (request.responseDataLength == 0) { - return; + // + // Check for available inband packets. + // Large packets will be allocated as needed. + // + HyperVKeyboardMessage *message; + UInt32 pktDataLength; + if (!hvDevice->nextInbandPacketAvailable(&pktDataLength)) { + break; } - switch (responseMsg->header.type) { - case kHyperVKeyboardMessageTypeProtocolResponse: - DBGLOG("Keyboard protocol status %u %u", responseMsg->header.type, responseMsg->status); - break; - - case kHyperVKeyboardMessageTypeEvent: - UInt64 time; - clock_get_uptime(&time); - - dispatchKeyboardEvent(getKeyCode(eventMsg), !eventMsg->isBreak, *(AbsoluteTime*)&time); - break; - - default: - break; + + if (pktDataLength <= sizeof (data128)) { + message = (HyperVKeyboardMessage*)data128; + } else { + DBGLOG("Allocating large packet of %u bytes", pktDataLength); + message = (HyperVKeyboardMessage*)IOMalloc(pktDataLength); } - - } while (true); - - - + // + // Read next packet. + // + if (hvDevice->readInbandCompletionPacket((void *)message, pktDataLength, NULL) == kIOReturnSuccess) { + switch (message->header.type) { + case kHyperVKeyboardMessageTypeProtocolResponse: + DBGLOG("Keyboard protocol status %u %u", message->protocolResponse.header.type, message->protocolResponse.status); + break; + + case kHyperVKeyboardMessageTypeEvent: + UInt64 time; + clock_get_uptime(&time); + + dispatchKeyboardEvent(getKeyCode(&message->keystroke), !message->keystroke.isBreak, *(AbsoluteTime*)&time); + break; + + default: + DBGLOG("Unknown message type %u, size %u", message->header.type, pktDataLength); + break; + } + } + + // + // Free allocated packet if needed. + // + if (pktDataLength > sizeof (data128)) { + IOFree(message, pktDataLength); + } + } while (true); } const unsigned char * HyperVKeyboard::defaultKeymapOfLength(UInt32 * length) diff --git a/MacHyperVSupport/Keyboard/HyperVKeyboard.hpp b/MacHyperVSupport/Keyboard/HyperVKeyboard.hpp index a77f717..4477f5b 100644 --- a/MacHyperVSupport/Keyboard/HyperVKeyboard.hpp +++ b/MacHyperVSupport/Keyboard/HyperVKeyboard.hpp @@ -23,7 +23,8 @@ class HyperVKeyboard : public IOHIKeyboard { OSDeclareDefaultStructors(HyperVKeyboard); private: - HyperVVMBusDevice *hvDevice; + HyperVVMBusDevice *hvDevice; + IOInterruptEventSource *interruptSource; void handleInterrupt(OSObject *owner, IOInterruptEventSource *sender, int count); bool connectKeyboard(); diff --git a/MacHyperVSupport/Keyboard/HyperVKeyboardRegs.hpp b/MacHyperVSupport/Keyboard/HyperVKeyboardRegs.hpp index c53cd2d..2d0b116 100644 --- a/MacHyperVSupport/Keyboard/HyperVKeyboardRegs.hpp +++ b/MacHyperVSupport/Keyboard/HyperVKeyboardRegs.hpp @@ -63,4 +63,14 @@ typedef struct __attribute__((packed)) { UInt32 reserved2 : 28; } HyperVKeyboardMessageKeystroke; +// +// All messages. +// +typedef union __attribute__((packed)) { + HyperVKeyboardMessageHeader header; + HyperVKeyboardMessageProtocolRequest protocolRequest; + HyperVKeyboardMessageProtocolResponse protocolResponse; + HyperVKeyboardMessageKeystroke keystroke; +} HyperVKeyboardMessage; + #endif diff --git a/MacHyperVSupport/Mouse/HyperVMouse.cpp b/MacHyperVSupport/Mouse/HyperVMouse.cpp index a79b9a0..09c4dc8 100644 --- a/MacHyperVSupport/Mouse/HyperVMouse.cpp +++ b/MacHyperVSupport/Mouse/HyperVMouse.cpp @@ -13,14 +13,14 @@ bool HyperVMouse::handleStart(IOService *provider) { if (!super::handleStart(provider)) { return false; } - + // // HIDDefaultBehavior needs to be set to Mouse for the device to // get exposed as a mouse to userspace. // DBGLOG("Initializing Hyper-V Synthetic Mouse"); setProperty("HIDDefaultBehavior", "Mouse"); - + // // Get parent VMBus device object. // @@ -30,45 +30,52 @@ bool HyperVMouse::handleStart(IOService *provider) { } hvDevice->retain(); + // + // Configure interrupt. + // + interruptSource = + IOInterruptEventSource::interruptEventSource(this, OSMemberFunctionCast(IOInterruptEventAction, this, &HyperVMouse::handleInterrupt), provider, 0); + getWorkLoop()->addEventSource(interruptSource); + interruptSource->enable(); + // // Configure the channel. // - if (!hvDevice->openChannel(kHyperVMouseRingBufferSize, kHyperVMouseRingBufferSize, - this, OSMemberFunctionCast(IOInterruptEventAction, this, &HyperVMouse::handleInterrupt))) { + if (!hvDevice->openChannel(kHyperVMouseRingBufferSize, kHyperVMouseRingBufferSize)) { return false; } - + if (!setupMouse()) { SYSLOG("Failed to set up device"); return false; } - + for (int i = 0; i < kHyperVMouseInitTimeout; i++) { if (hidDescriptorValid) { DBGLOG("Device info packet is now valid"); break; } - + IODelay(10); } - + if (hidDescriptorValid) { SYSLOG("Initialized Hyper-V Synthetic Mouse"); } else { SYSLOG("Timed out getting device info"); } - + return hidDescriptorValid; } void HyperVMouse::handleStop(IOService *provider) { DBGLOG("Hyper-V Mouse is stopping"); - + if (hidDescriptor != NULL) { IOFree(hidDescriptor, hidDescriptorLength); hidDescriptor = NULL; } - + // // Close channel and remove interrupt sources. // @@ -76,7 +83,7 @@ void HyperVMouse::handleStop(IOService *provider) { hvDevice->closeChannel(); hvDevice->release(); } - + super::handleStop(provider); } @@ -114,4 +121,3 @@ IOReturn HyperVMouse::newReportDescriptor(IOMemoryDescriptor **descriptor) const *descriptor = bufferDesc; return kIOReturnSuccess; } - diff --git a/MacHyperVSupport/Mouse/HyperVMouse.hpp b/MacHyperVSupport/Mouse/HyperVMouse.hpp index a5f6224..1cdb23c 100644 --- a/MacHyperVSupport/Mouse/HyperVMouse.hpp +++ b/MacHyperVSupport/Mouse/HyperVMouse.hpp @@ -26,6 +26,7 @@ class HyperVMouse : public IOHIDDevice { // Parent VMBus device. // HyperVVMBusDevice *hvDevice; + IOInterruptEventSource *interruptSource; // // HID structures. @@ -34,20 +35,21 @@ class HyperVMouse : public IOHIDDevice { void *hidDescriptor; size_t hidDescriptorLength; bool hidDescriptorValid; - + void handleInterrupt(OSObject *owner, IOInterruptEventSource *sender, int count); - + bool setupMouse(); + void handleProtocolResponse(HyperVMouseMessageProtocolResponse *response, UInt64 transactionId); void handleDeviceInfo(HyperVMouseMessageInitialDeviceInfo *deviceInfo); void handleInputReport(HyperVMouseMessageInputReport *inputReport); - + protected: // // IOHIDDevice overrides. // virtual bool handleStart(IOService *provider) APPLE_KEXT_OVERRIDE; virtual void handleStop(IOService *provider) APPLE_KEXT_OVERRIDE; - + public: // // IOHIDDevice overrides. @@ -58,7 +60,7 @@ class HyperVMouse : public IOHIDDevice { virtual OSNumber *newVendorIDNumber() const APPLE_KEXT_OVERRIDE; virtual OSNumber *newProductIDNumber() const APPLE_KEXT_OVERRIDE; virtual OSNumber *newVersionNumber() const APPLE_KEXT_OVERRIDE; - + virtual IOReturn newReportDescriptor(IOMemoryDescriptor **descriptor) const APPLE_KEXT_OVERRIDE; }; diff --git a/MacHyperVSupport/Mouse/HyperVMousePrivate.cpp b/MacHyperVSupport/Mouse/HyperVMousePrivate.cpp index 238244d..3f0e062 100644 --- a/MacHyperVSupport/Mouse/HyperVMousePrivate.cpp +++ b/MacHyperVSupport/Mouse/HyperVMousePrivate.cpp @@ -8,42 +8,57 @@ #include "HyperVMouse.hpp" void HyperVMouse::handleInterrupt(OSObject *owner, IOInterruptEventSource *sender, int count) { - HyperVVMBusDeviceRequest request = { 0 }; - UInt8 data[256]; - - // - // Very large packets should never occur, - // but we do not want to hold up the packet chain. - // - HyperVMousePipeIncomingMessage *message = (HyperVMousePipeIncomingMessage*)data; - request.responseData = data; - request.ignoreLargePackets = true; - + UInt8 data128[128]; + do { // - // Get next packet. + // Check for available inband packets. + // Large packets will be allocated as needed. + // + HyperVMousePipeIncomingMessage *message; + UInt32 pktDataLength; + if (!hvDevice->nextInbandPacketAvailable(&pktDataLength)) { + break; + } + + if (pktDataLength <= sizeof (data128)) { + message = (HyperVMousePipeIncomingMessage*)data128; + } else { + DBGLOG("Allocating large packet of %u bytes", pktDataLength); + message = (HyperVMousePipeIncomingMessage*)IOMalloc(pktDataLength); + } + + // + // Read next packet. // - request.responseDataLength = sizeof (data); - IOReturn status = hvDevice->doRequest(&request); - if (status != kIOReturnSuccess || request.responseDataLength == 0) { + UInt64 transactionId; + if (hvDevice->readInbandCompletionPacket((void *)message, pktDataLength, &transactionId) == kIOReturnSuccess) { // TODO: Handle other failures - return; + switch (message->header.type) { + case kHyperVMouseMessageTypeProtocolResponse: + handleProtocolResponse(&message->response, transactionId); + break; + + case kHyperVMouseMessageTypeInitialDeviceInfo: + handleDeviceInfo(&message->deviceInfo); + break; + + case kHyperVMouseMessageTypeInputReport: + handleInputReport(&message->inputReport); + break; + + default: + DBGLOG("Unknown message type %u, size %u", message->header.type, message->header.size); + break; + } } - - switch (message->header.type) { - case kHyperVMouseMessageTypeInitialDeviceInfo: - handleDeviceInfo(&message->deviceInfo); - break; - - case kHyperVMouseMessageTypeInputReport: - handleInputReport(&message->inputReport); - break; - - default: - DBGLOG("Unknown message type %u, size %u", message->header.type, message->header.size); - break; + + // + // Free allocated packet if needed. + // + if (pktDataLength > sizeof (data128)) { + IOFree(message, pktDataLength); } - } while (true); } @@ -52,35 +67,40 @@ bool HyperVMouse::setupMouse() { // Device info is invalid. // hidDescriptorValid = false; - + // // Send mouse request. // - HyperVVMBusDeviceRequest request = { 0 }; - HyperVMousePipeMessage message; - HyperVMousePipeIncomingMessage respMessage; - + HyperVMousePipeMessage message; + HyperVMouseMessageProtocolResponse protoResponse; + message.type = kHyperVPipeMessageTypeData; message.size = sizeof (HyperVMouseMessageProtocolRequest); - + message.request.header.type = kHyperVMouseMessageTypeProtocolRequest; message.request.header.size = sizeof (UInt32); message.request.versionRequested = kHyperVMouseVersion; - - request.sendData = &message; - request.sendDataLength = sizeof (message); - request.sendPacketType = kVMBusPacketTypeDataInband; - request.responseRequired = true; - request.responseData = &respMessage; - request.responseDataLength = sizeof (respMessage); - + DBGLOG("Sending mouse protocol request"); - if (hvDevice->doRequest(&request) != kIOReturnSuccess) { + if (hvDevice->writeInbandPacket(&message, sizeof (message), true, &protoResponse, sizeof (protoResponse)) != kIOReturnSuccess) { return false; } - - DBGLOG("Got mouse protocol response of %u", respMessage.response.status); - return respMessage.response.status != 0; + + DBGLOG("Got mouse protocol response of %u, version 0x%X", protoResponse.status, protoResponse.versionRequested); + return protoResponse.status != 0; +} + +void HyperVMouse::handleProtocolResponse(HyperVMouseMessageProtocolResponse *response, UInt64 transactionId) { + void *responseBuffer; + UInt32 responseLength; + + if (hvDevice->getPendingTransaction(transactionId, &responseBuffer, &responseLength)) { + if (sizeof (*response) > responseLength) { + return; + } + memcpy(responseBuffer, response, responseLength); + hvDevice->wakeTransaction(transactionId); + } } void HyperVMouse::handleDeviceInfo(HyperVMouseMessageInitialDeviceInfo *deviceInfo) { @@ -88,43 +108,37 @@ void HyperVMouse::handleDeviceInfo(HyperVMouseMessageInitialDeviceInfo *deviceIn deviceInfo->info.size < sizeof(deviceInfo->info)) { return; } - + memcpy(&mouseInfo, &deviceInfo->info, sizeof (mouseInfo)); DBGLOG("Hyper-V Mouse ID %04X:%04X, version 0x%X", mouseInfo.vendor, mouseInfo.product, mouseInfo.version); - + // // Store HID descriptor. // hidDescriptorLength = deviceInfo->hidDescriptor.hidDescriptorLength; DBGLOG("HID descriptor is %u bytes", hidDescriptorLength); - + hidDescriptor = IOMalloc(hidDescriptorLength); if (hidDescriptor == NULL) { return; } memcpy(hidDescriptor, deviceInfo->hidDescriptorData, hidDescriptorLength); hidDescriptorValid = true; - + // // Send device info ack message. // - HyperVVMBusDeviceRequest request = { 0 }; - HyperVMousePipeMessage message; - + HyperVMousePipeMessage message; message.type = kHyperVPipeMessageTypeData; message.size = sizeof (HyperVMouseMessageInitialDeviceInfoAck); - + message.deviceInfoAck.header.type = kHyperVMouseMessageTypeInitialDeviceInfoAck; message.deviceInfoAck.header.size = sizeof (HyperVMouseMessageInitialDeviceInfoAck) - sizeof (HyperVMouseMessageHeader); message.deviceInfoAck.reserved = 0; - - request.sendData = &message; - request.sendDataLength = sizeof (message); - request.sendPacketType = kVMBusPacketTypeDataInband; - + DBGLOG("Sending device info ack"); - hvDevice->doRequest(&request); + hvDevice->writeInbandPacket(&message, sizeof (message), false); } void HyperVMouse::handleInputReport(HyperVMouseMessageInputReport *inputReport) { diff --git a/MacHyperVSupport/Mouse/HyperVMouseRegs.hpp b/MacHyperVSupport/Mouse/HyperVMouseRegs.hpp index 186b5eb..3d22e15 100644 --- a/MacHyperVSupport/Mouse/HyperVMouseRegs.hpp +++ b/MacHyperVSupport/Mouse/HyperVMouseRegs.hpp @@ -78,7 +78,7 @@ typedef struct __attribute__((packed)) { // Protocol response typedef struct __attribute__((packed)) { HyperVMouseMessageHeader header; - + UInt32 versionRequested; UInt8 status; UInt8 reserved[3]; @@ -87,7 +87,7 @@ typedef struct __attribute__((packed)) { // Device info typedef struct __attribute__((packed)) { HyperVMouseMessageHeader header; - + HyperVMouseDeviceInfo info; HyperVHIDDescriptor hidDescriptor; UInt8 hidDescriptorData[]; @@ -96,14 +96,14 @@ typedef struct __attribute__((packed)) { // Device info ack typedef struct __attribute__((packed)) { HyperVMouseMessageHeader header; - + UInt8 reserved; } HyperVMouseMessageInitialDeviceInfoAck; // Input report typedef struct __attribute__((packed)) { HyperVMouseMessageHeader header; - + UInt8 data[]; } HyperVMouseMessageInputReport; diff --git a/MacHyperVSupport/Network/HyperVNetwork.cpp b/MacHyperVSupport/Network/HyperVNetwork.cpp index dd457af..90495fb 100644 --- a/MacHyperVSupport/Network/HyperVNetwork.cpp +++ b/MacHyperVSupport/Network/HyperVNetwork.cpp @@ -6,7 +6,6 @@ // #include "HyperVNetwork.hpp" -#include "HyperVNetworkRegs.hpp" OSDefineMetaClassAndStructors(HyperVNetwork, super); @@ -27,18 +26,54 @@ bool HyperVNetwork::start(IOService *provider) { } hvDevice->retain(); + // + // Configure interrupt. + // + interruptSource = + IOInterruptEventSource::interruptEventSource(this, OSMemberFunctionCast(IOInterruptEventAction, this, &HyperVNetwork::handleInterrupt), provider, 0); + getWorkLoop()->addEventSource(interruptSource); + interruptSource->enable(); + // // Configure the channel. // - if (!hvDevice->openChannel(kHyperVNetworkRingBufferSize, kHyperVNetworkRingBufferSize, this, OSMemberFunctionCast(IOInterruptEventAction, this, &HyperVNetwork::handleInterrupt))) { + if (!hvDevice->openChannel(kHyperVNetworkRingBufferSize, kHyperVNetworkRingBufferSize, kHyperVNetworkMaximumTransId)) { super::stop(provider); return false; } + rndisLock = IOLockAlloc(); + + connectNetwork(); + createMediumDictionary(); + + // + // Attach network interface. + // + if (!attachInterface((IONetworkInterface **)ðInterface, false)) { + return false; + } + ethInterface->registerService(); + SYSLOG("Initialized Hyper-V Synthetic Networking"); return true; } -void HyperVNetwork::handleInterrupt(OSObject *owner, IOInterruptEventSource *sender, int count) { - DBGLOG("Interrupt"); +IOReturn HyperVNetwork::getHardwareAddress(IOEthernetAddress *addrP) { + *addrP = ethAddress; + return kIOReturnSuccess; +} + +UInt32 HyperVNetwork::outputPacket(mbuf_t m, void *param) { + return sendRNDISDataPacket(m) ? kIOReturnSuccess : kIOReturnIOError; +} + +IOReturn HyperVNetwork::enable(IONetworkInterface *interface) { + isEnabled = true; + return kIOReturnSuccess; +} + +IOReturn HyperVNetwork::disable(IONetworkInterface *interface) { + isEnabled = false; + return kIOReturnSuccess; } diff --git a/MacHyperVSupport/Network/HyperVNetwork.hpp b/MacHyperVSupport/Network/HyperVNetwork.hpp index 82e3744..590ed72 100644 --- a/MacHyperVSupport/Network/HyperVNetwork.hpp +++ b/MacHyperVSupport/Network/HyperVNetwork.hpp @@ -8,14 +8,43 @@ #ifndef HyperVNetwork_hpp #define HyperVNetwork_hpp +#include +#include +#include +#include +#include + #include "HyperVVMBusDevice.hpp" +#include "HyperVNetworkRegs.hpp" + +extern "C" { +#include +} -#define super IOService +#define super IOEthernetController #define SYSLOG(str, ...) SYSLOG_PRINT("HyperVNetwork", str, ## __VA_ARGS__) -#define DBGLOG(str, ...) DBGLOG_PRINT("HyperVNetwork", str, ## __VA_ARGS__) +#define DBGLOG(str, ...) \ + if (this->debugEnabled) DBGLOG_PRINT("HyperVNetwork", str, ## __VA_ARGS__) + +#define MBit 1000000 + +#define kHyperVNetworkMaximumTransId 0xFFFFFFFF +#define kHyperVNetworkSendTransIdBits 0xFA00000000000000 -class HyperVNetwork : public IOService { +typedef struct HyperVNetworkRNDISRequest { + HyperVNetworkRNDISMessage message; + UInt8 messageOverflow[PAGE_SIZE]; + + HyperVNetworkRNDISRequest *next; + IOLock *lock; + bool isSleeping; + + IOBufferMemoryDescriptor *memDescriptor; + mach_vm_address_t messagePhysicalAddress; +} HyperVNetworkRNDISRequest; + +class HyperVNetwork : public IOEthernetController { OSDeclareDefaultStructors(HyperVNetwork); private: @@ -23,14 +52,86 @@ class HyperVNetwork : public IOService { // Parent VMBus device. // HyperVVMBusDevice *hvDevice; + IOInterruptEventSource *interruptSource; + bool debugEnabled = false; + + bool isEnabled = false; + + HyperVNetworkProtocolVersion netVersion; + UInt32 receiveBufferSize; + UInt32 receiveGpadlHandle; + UInt8 *receiveBuffer; + + UInt32 sendBufferSize; + UInt32 sendGpadlHandle; + UInt8 *sendBuffer; + UInt32 sendSectionSize; + UInt32 sendSectionCount; + UInt64 *sendIndexMap; + size_t sendIndexMapSize; + + IOLock *rndisLock = NULL; + UInt32 rndisTransId = 0; + + HyperVNetworkRNDISRequest *rndisRequests; + + IOEthernetInterface *ethInterface; + IOEthernetAddress ethAddress; + + bool isLinkUp = false; + OSDictionary *mediumDict; + UInt32 currentMediumIndex; void handleInterrupt(OSObject *owner, IOInterruptEventSource *sender, int count); + + bool negotiateProtocol(HyperVNetworkProtocolVersion protocolVersion); + bool initBuffers(); + bool connectNetwork(); + + void handleRNDISRanges(VMBusPacketTransferPages *pktPages, UInt32 headerSize, UInt32 pktSize); + void handleCompletion(); + + bool processRNDISPacket(UInt8 *data, UInt32 dataLength); + void processIncoming(UInt8 *data, UInt32 dataLength); + + // + // RNDIS + // + UInt32 getNextSendIndex(); + void releaseSendIndex(UInt32 sendIndex); + HyperVNetworkRNDISRequest *allocateRNDISRequest(); + void freeRNDISRequest(HyperVNetworkRNDISRequest *rndisRequest); + UInt32 getNextRNDISTransId(); + bool sendRNDISRequest(HyperVNetworkRNDISRequest *rndisRequest, bool waitResponse = false); + bool sendRNDISDataPacket(mbuf_t packet); + + bool initializeRNDIS(); + bool queryRNDISOID(HyperVNetworkRNDISOID oid, void *value, UInt32 *valueSize); + + // + // Private + // + void addNetworkMedium(UInt32 index, UInt32 type, UInt32 speed); + void createMediumDictionary(); + bool readMACAddress(); + void updateLinkState(HyperVNetworkRNDISMessageIndicateStatus *indicateStatus); + public: // // IOService overrides. // virtual bool start(IOService *provider) APPLE_KEXT_OVERRIDE; + + // + // IOEthernetController overrides. + // + IOReturn getHardwareAddress(IOEthernetAddress *addrP) APPLE_KEXT_OVERRIDE; + + UInt32 outputPacket(mbuf_t m, void *param) APPLE_KEXT_OVERRIDE; + + virtual IOReturn enable(IONetworkInterface *interface) APPLE_KEXT_OVERRIDE; + virtual IOReturn disable(IONetworkInterface *interface) APPLE_KEXT_OVERRIDE; }; #endif /* HyperVNetwork_hpp */ diff --git a/MacHyperVSupport/Network/HyperVNetworkPrivate.cpp b/MacHyperVSupport/Network/HyperVNetworkPrivate.cpp new file mode 100644 index 0000000..b346049 --- /dev/null +++ b/MacHyperVSupport/Network/HyperVNetworkPrivate.cpp @@ -0,0 +1,305 @@ +// +// HyperVNetworkPrivate.cpp +// Hyper-V network driver +// +// Copyright © 2021 Goldfish64. All rights reserved. +// + +#include "HyperVNetwork.hpp" + +void HyperVNetwork::handleInterrupt(OSObject *owner, IOInterruptEventSource *sender, int count) { + VMBusPacketType type; + UInt32 headersize; + UInt32 totalsize; + + void *responseBuffer; + UInt32 responseLength; + + HyperVNetworkMessage *pktComp; + + while (true) { + if (!hvDevice->nextPacketAvailable(&type, &headersize, &totalsize)) { + // DBGLOG("last one"); + break; + } + + UInt8 *buf = (UInt8*)IOMalloc(totalsize); + hvDevice->readRawPacket((void*)buf, totalsize); + + switch (type) { + case kVMBusPacketTypeDataInband: + break; + case kVMBusPacketTypeDataUsingTransferPages: + handleRNDISRanges((VMBusPacketTransferPages*) buf, headersize, totalsize); + break; + + case kVMBusPacketTypeCompletion: + + if (hvDevice->getPendingTransaction(((VMBusPacketHeader*)buf)->transactionId, &responseBuffer, &responseLength)) { + memcpy(responseBuffer, (UInt8*)buf + headersize, responseLength); + hvDevice->wakeTransaction(((VMBusPacketHeader*)buf)->transactionId); + } else { + pktComp = (HyperVNetworkMessage*) (buf + headersize); + DBGLOG("pkt completion status %X %X", pktComp->messageType, pktComp->v1.sendRNDISPacketComplete.status); + + if (pktComp->messageType == kHyperVNetworkMessageTypeV1SendRNDISPacketComplete) { + releaseSendIndex((UInt32)(((VMBusPacketHeader*)buf)->transactionId & ~kHyperVNetworkSendTransIdBits)); + } + } + break; + default: + break; + } + + IOFree(buf, totalsize); + } +} + +void HyperVNetwork::handleRNDISRanges(VMBusPacketTransferPages *pktPages, UInt32 headerSize, UInt32 pktSize) { + HyperVNetworkMessage *netMsg = (HyperVNetworkMessage*) ((UInt8*)pktPages + headerSize); + + // + // Ensure packet is valid. + // + if (netMsg->messageType != kHyperVNetworkMessageTypeV1SendRNDISPacket || + pktPages->transferPagesetId != kHyperVNetworkReceiveBufferID) { + SYSLOG("Invalid message of type 0x%X and pageset ID of 0x%X received", netMsg->messageType, pktPages->transferPagesetId); + return; + } + DBGLOG("Received %u RNDIS ranges, range[0] count = %u, offset = 0x%X", pktPages->rangeCount, pktPages->ranges[0].count, pktPages->ranges[0].offset); + + // + // Process each range which contains a packet. + // + for (int i = 0; i < pktPages->rangeCount; i++) { + UInt8 *data = receiveBuffer + pktPages->ranges[i].offset; + UInt32 dataLength = pktPages->ranges[i].count; + + DBGLOG("Got range of %u bytes at 0x%X", dataLength, pktPages->ranges[i].offset); + processRNDISPacket(data, dataLength); + } + + HyperVNetworkMessage netMsg2; + memset(&netMsg2, 0, sizeof (netMsg2)); + netMsg2.messageType = kHyperVNetworkMessageTypeV1SendRNDISPacketComplete; + netMsg2.v1.sendRNDISPacketComplete.status = kHyperVNetworkMessageStatusSuccess; + + hvDevice->writeCompletionPacketWithTransactionId(&netMsg2, sizeof (netMsg2), pktPages->header.transactionId, false); +} + +bool HyperVNetwork::negotiateProtocol(HyperVNetworkProtocolVersion protocolVersion) { + // Send requested version to Hyper-V. + HyperVNetworkMessage netMsg; + memset(&netMsg, 0, sizeof (netMsg)); + netMsg.messageType = kHyperVNetworkMessageTypeInit; + netMsg.init.initVersion.maxProtocolVersion = protocolVersion; + netMsg.init.initVersion.minProtocolVersion = protocolVersion; + + if (hvDevice->writeInbandPacket(&netMsg, sizeof (netMsg), true, &netMsg, sizeof (netMsg)) != kIOReturnSuccess) { + SYSLOG("failed to send protocol negotiation message"); + return false; + } + + if (netMsg.init.initComplete.status != kHyperVNetworkMessageStatusSuccess) { + SYSLOG("error returned from Hyper-V: 0x%X", netMsg.init.initComplete.status); + } + + DBGLOG("Can use protocol 0x%X, max MDL length %u", + netMsg.init.initComplete.negotiatedProtocolVersion, netMsg.init.initComplete.maxMdlChainLength); + return true; +} + +bool HyperVNetwork::initBuffers() { + + // Allocate receive and send buffers. + if (!hvDevice->createGpadlBuffer(receiveBufferSize, &receiveGpadlHandle, (void**)&receiveBuffer)) { + SYSLOG("Failed to create GPADL for receive buffer"); + return false; + } + if (!hvDevice->createGpadlBuffer(sendBufferSize, &sendGpadlHandle, (void**)&sendBuffer)) { + SYSLOG("Failed to create GPADL for send buffer"); + return false; + } + DBGLOG("Receive GPADL: 0x%X, send GPADL: 0x%X", receiveGpadlHandle, sendGpadlHandle); + + // Send receive buffer GPADL handle to Hyper-V. + HyperVNetworkMessage netMsg; + memset(&netMsg, 0, sizeof (netMsg)); + netMsg.messageType = kHyperVNetworkMessageTypeV1SendReceiveBuffer; + netMsg.v1.sendReceiveBuffer.gpadlHandle = receiveGpadlHandle; + netMsg.v1.sendReceiveBuffer.id = kHyperVNetworkReceiveBufferID; + + if (hvDevice->writeInbandPacket(&netMsg, sizeof (netMsg), true, &netMsg, sizeof (netMsg)) != kIOReturnSuccess) { + SYSLOG("Failed to send receive buffer configuration message"); + return false; + } + + if (netMsg.v1.sendReceiveBufferComplete.status != kHyperVNetworkMessageStatusSuccess) { + SYSLOG("Failed to configure receive buffer: 0x%X", netMsg.v1.sendReceiveBufferComplete.status); + return false; + } + DBGLOG("Receive buffer configured with %u sections", netMsg.v1.sendReceiveBufferComplete.numSections); + + // Linux driver only allows 1 section. + if (netMsg.v1.sendReceiveBufferComplete.numSections != 1 || netMsg.v1.sendReceiveBufferComplete.sections[0].offset != 0) { + SYSLOG("Invalid receive buffer sections"); + return false; + } + + // Send send buffer GPADL handle to Hyper-V. + memset(&netMsg, 0, sizeof (netMsg)); + netMsg.messageType = kHyperVNetworkMessageTypeV1SendSendBuffer; + netMsg.v1.sendSendBuffer.gpadlHandle = sendGpadlHandle; + netMsg.v1.sendSendBuffer.id = kHyperVNetworkSendBufferID; + + if (hvDevice->writeInbandPacket(&netMsg, sizeof (netMsg), true, &netMsg, sizeof (netMsg)) != kIOReturnSuccess) { + SYSLOG("Failed to send send buffer configuration message"); + return false; + } + + if (netMsg.v1.sendSendBufferComplete.status != kHyperVNetworkMessageStatusSuccess) { + SYSLOG("Failed to configure send buffer: 0x%X", netMsg.v1.sendSendBufferComplete.status); + return false; + } + sendSectionSize = netMsg.v1.sendSendBufferComplete.sectionSize; + sendSectionCount = sendBufferSize / sendSectionSize; + sendIndexMapSize = (sendSectionCount + sizeof (UInt64) - 1) / sizeof (UInt64); + sendIndexMap = (UInt64*)IOMalloc(sendIndexMapSize); + memset(sendIndexMap, 0, sendIndexMapSize); + DBGLOG("send index map size %u", sendIndexMapSize); + + DBGLOG("Send buffer configured with section size of %u bytes and %u sections", sendSectionSize, sendSectionCount); + + return true; +} + +bool HyperVNetwork::connectNetwork() { + DBGLOG("start"); + + // Negotiate max protocol version with Hyper-V. + negotiateProtocol(kHyperVNetworkProtocolVersion1); + netVersion = kHyperVNetworkProtocolVersion1; + + // Send NDIS version. + UInt32 ndisVersion = netVersion > kHyperVNetworkProtocolVersion4 ? + kHyperVNetworkNDISVersion6001E : kHyperVNetworkNDISVersion60001; + + HyperVNetworkMessage netMsg; + memset(&netMsg, 0, sizeof (netMsg)); + netMsg.messageType = kHyperVNetworkMessageTypeV1SendNDISVersion; + netMsg.v1.sendNDISVersion.major = (ndisVersion & 0xFFFF0000) >> 16; + netMsg.v1.sendNDISVersion.minor = ndisVersion & 0x0000FFFF; + + if (hvDevice->writeInbandPacket(&netMsg, sizeof (netMsg), false) != kIOReturnSuccess) { + SYSLOG("failed to send NDIS version"); + return false; + } + + receiveBufferSize = netVersion > kHyperVNetworkProtocolVersion2 ? + kHyperVNetworkReceiveBufferSize : kHyperVNetworkReceiveBufferSizeLegacy; + sendBufferSize = kHyperVNetworkSendBufferSize; + initBuffers(); + + initializeRNDIS(); + + readMACAddress(); + updateLinkState(NULL); + + return true; +} + +void HyperVNetwork::addNetworkMedium(UInt32 index, UInt32 type, UInt32 speed) { + IONetworkMedium *medium = IONetworkMedium::medium(type, speed * MBit, 0, index); + if (medium != NULL) { + IONetworkMedium::addMedium(mediumDict, medium); + medium->release(); + } +} + +void HyperVNetwork::createMediumDictionary() { + // + // Create medium dictionary with all possible speeds. + // + mediumDict = OSDictionary::withCapacity(1); + + addNetworkMedium(0, kIOMediumEthernetAuto, 0); + + publishMediumDictionary(mediumDict); +} + +bool HyperVNetwork::readMACAddress() { + UInt32 macSize = sizeof (ethAddress.bytes); + if (!queryRNDISOID(kHyperVNetworkRNDISOIDEthernetPermanentAddress, (void *)ethAddress.bytes, &macSize)) { + SYSLOG("Failed to get MAC address"); + return false; + } + + DBGLOG("MAC address is %02X:%02X:%02X:%02X:%02X:%02X", + ethAddress.bytes[0], ethAddress.bytes[1], ethAddress.bytes[2], + ethAddress.bytes[3], ethAddress.bytes[4], ethAddress.bytes[5]); + return true; +} + +void HyperVNetwork::updateLinkState(HyperVNetworkRNDISMessageIndicateStatus *indicateStatus) { + // + // Pull initial link state from OID. + // + if (indicateStatus == NULL) { + HyperVNetworkRNDISLinkState linkState; + UInt32 linkStateSize = sizeof (linkState); + if (!queryRNDISOID(kHyperVNetworkRNDISOIDGeneralMediaConnectStatus, &linkState, &linkStateSize)) { + SYSLOG("Failed to get link state"); + return; + } + + DBGLOG("Link state is initially %s", linkState == kHyperVNetworkRNDISLinkStateConnected ? "up" : "down"); + isLinkUp = linkState == kHyperVNetworkRNDISLinkStateConnected; + if (isLinkUp) { + setLinkStatus(kIONetworkLinkValid | kIONetworkLinkActive, 0); + } else { + setLinkStatus(kIONetworkLinkValid, 0); + } + return; + } + + // + // Handle media and link speed changes. + // + DBGLOG("Indicate status of 0x%X, buffer off 0x%X of %u bytes received", + indicateStatus->status, indicateStatus->statusBufferOffset, indicateStatus->statusBufferLength); + switch (indicateStatus->status) { + case kHyperVNetworkRNDISStatusLinkSpeedChange: + DBGLOG("Link has changed speeds"); + break; + + case kHyperVNetworkRNDISStatusMediaConnect: + if (!isLinkUp) { + DBGLOG("Link is coming up"); + setLinkStatus(kIONetworkLinkValid | kIONetworkLinkActive, 0); + isLinkUp = true; + } + break; + + case kHyperVNetworkRNDISStatusMediaDisconnect: + if (isLinkUp) { + DBGLOG("Link is going down"); + setLinkStatus(kIONetworkLinkValid, 0); + isLinkUp = false; + } + break; + + case kHyperVNetworkRNDISStatusNetworkChange: + if (isLinkUp) { + // + // Do a link up and down to force a refresh in the OS. + // + DBGLOG("Link has changed networks"); + setLinkStatus(kIONetworkLinkValid, 0); + setLinkStatus(kIONetworkLinkValid | kIONetworkLinkActive, 0); + } + break; + + default: + break; + } +} diff --git a/MacHyperVSupport/Network/HyperVNetworkRNDIS.cpp b/MacHyperVSupport/Network/HyperVNetworkRNDIS.cpp new file mode 100644 index 0000000..1509430 --- /dev/null +++ b/MacHyperVSupport/Network/HyperVNetworkRNDIS.cpp @@ -0,0 +1,293 @@ +// +// HyperVNetworkPrivate.cpp +// Hyper-V network driver +// +// Copyright © 2021 Goldfish64. All rights reserved. +// + +#include "HyperVNetwork.hpp" + +bool HyperVNetwork::processRNDISPacket(UInt8 *data, UInt32 dataLength) { + HyperVNetworkRNDISMessage *rndisPkt = (HyperVNetworkRNDISMessage*)data; + + DBGLOG("New RNDIS packet of type 0x%X and %u bytes", rndisPkt->msgType, rndisPkt->msgLength); + + HyperVNetworkRNDISRequest *reqCurr = rndisRequests; + HyperVNetworkRNDISRequest *reqPrev = NULL; + + switch (rndisPkt->msgType) { + case kHyperVNetworkRNDISMessageTypeInitComplete: + case kHyperVNetworkRNDISMessageTypeQueryComplete: + case kHyperVNetworkRNDISMessageTypeSetComplete: + case kHyperVNetworkRNDISMessageTypeResetComplete: + + + while (reqCurr != NULL) { + DBGLOG("checking %u", reqCurr->message.initComplete.requestId); + if (reqCurr->message.initComplete.requestId == rndisPkt->initComplete.requestId) { + // + // Copy response data. + // + memcpy(&reqCurr->message, rndisPkt, dataLength); + + // + // Remove from linked list. + // + IOLockLock(rndisLock); + if (reqPrev == NULL) { + rndisRequests = reqCurr->next; + } else { + reqPrev->next = reqCurr->next; + } + IOLockUnlock(rndisLock); + + // + // Wakeup sleeping thread. + // + IOLockLock(reqCurr->lock); + reqCurr->isSleeping = false; + IOLockUnlock(reqCurr->lock); + IOLockWakeup(reqCurr->lock, &reqCurr->isSleeping, true); + + return true; + } + + reqPrev = reqCurr; + reqCurr = reqCurr->next; + } + break; + + case kHyperVNetworkRNDISMessageTypePacket: + if (isEnabled) + processIncoming(data, dataLength); + break; + + case kHyperVNetworkRNDISMessageTypeIndicate: + updateLinkState(&rndisPkt->indicateStatus); + break; + + default: + break; + } + + return true; +} + +void HyperVNetwork::processIncoming(UInt8 *data, UInt32 dataLength) { + HyperVNetworkRNDISMessage *rndisPkt = (HyperVNetworkRNDISMessage*)data; + UInt8 *pktData = data + 8 + rndisPkt->dataPacket.dataOffset; + + mbuf_t newPacket = allocatePacket(rndisPkt->dataPacket.dataLength); + memcpy(mbuf_data(newPacket), pktData, rndisPkt->dataPacket.dataLength); + + ethInterface->inputPacket(newPacket, rndisPkt->dataPacket.dataLength); +} + +UInt32 HyperVNetwork::getNextSendIndex() { + for (UInt32 i = 0; i < sendSectionCount; i++) { + DBGLOG("idx %u %X", i, sendIndexMap[i / 8]); + if ((sendIndexMap[i / 8] & (1 << (i % 8))) == 0) { + sendIndexMap[i / 8] |= (1 << (i % 8)); + return i; + } + } + + return kHyperVNetworkRNDISSendSectionIndexInvalid; +} + +void HyperVNetwork::releaseSendIndex(UInt32 sendIndex) { + sendIndexMap[sendIndex / 8] &= ~(1 << (sendIndex % 8)); +} + +HyperVNetworkRNDISRequest* HyperVNetwork::allocateRNDISRequest() { + HyperVNetworkRNDISRequest *rndisRequest; + IOBufferMemoryDescriptor *bufDesc; + IOLock *lock; + + // + // Allocate lock for waiting. + // + lock = IOLockAlloc(); + if (lock == NULL) { + SYSLOG("Failed to allocate lock for RNDIS request"); + return NULL; + } + + // + // Create DMA buffer with required specifications and get physical address. + // + bufDesc = IOBufferMemoryDescriptor::inTaskWithPhysicalMask(kernel_task, + kIODirectionInOut | kIOMemoryPhysicallyContiguous | kIOMapInhibitCache | kIOMemoryMapperNone, + sizeof (HyperVNetworkRNDISRequest), 0xFFFFFFFFFFFFF000ULL); + if (bufDesc == NULL) { + SYSLOG("Failed to allocate buffer memory for RNDIS request"); + IOLockFree(lock); + return NULL; + } + bufDesc->prepare(); + + rndisRequest = (HyperVNetworkRNDISRequest*)bufDesc->getBytesNoCopy(); + memset(rndisRequest, 0, sizeof (HyperVNetworkRNDISRequest)); + + rndisRequest->lock = lock; + rndisRequest->isSleeping = false; + rndisRequest->memDescriptor = bufDesc; + rndisRequest->messagePhysicalAddress = bufDesc->getPhysicalAddress(); + DBGLOG("Mapped RNDIS request buffer 0x%llX to phys 0x%llX", rndisRequest, rndisRequest->messagePhysicalAddress); + + return rndisRequest; +} + +void HyperVNetwork::freeRNDISRequest(HyperVNetworkRNDISRequest *rndisRequest) { + IOLockFree(rndisRequest->lock); + rndisRequest->memDescriptor->complete(); + rndisRequest->memDescriptor->release(); +} + +UInt32 HyperVNetwork::getNextRNDISTransId() { + IOLockLock(rndisLock); + UInt32 value = rndisTransId; + rndisTransId++; + IOLockUnlock(rndisLock); + return value; +} + +bool HyperVNetwork::sendRNDISRequest(HyperVNetworkRNDISRequest *rndisRequest, bool waitResponse) { + // + // Create page buffer set. + // + VMBusSinglePageBuffer pageBuffer; + pageBuffer.length = rndisRequest->message.msgLength; + pageBuffer.offset = 0; + pageBuffer.pfn = rndisRequest->messagePhysicalAddress >> PAGE_SHIFT; + + // + // Create packet for sending the RNDIS request. + // + HyperVNetworkMessage netMsg; + memset(&netMsg, 0, sizeof (netMsg)); + netMsg.messageType = kHyperVNetworkMessageTypeV1SendRNDISPacket; + netMsg.v1.sendRNDISPacket.channelType = kHyperVNetworkRNDISChannelTypeControl; + netMsg.v1.sendRNDISPacket.sendBufferSectionIndex = -1; + netMsg.v1.sendRNDISPacket.sendBufferSectionSize = 0; + + rndisRequest->isSleeping = true; + rndisRequest->message.initRequest.requestId = getNextRNDISTransId(); + + // + // Add to linked list. + // + IOLockLock(rndisLock); + if (rndisRequests == NULL) + rndisRequests = rndisRequest; + else + rndisRequests->next = rndisRequest; + IOLockUnlock(rndisLock); + + hvDevice->writeGPADirectSinglePagePacket(&netMsg, sizeof (netMsg), true, &pageBuffer, 1, &netMsg, sizeof (netMsg)); + + IOLockLock(rndisRequest->lock); + while (rndisRequest->isSleeping) { + IOLockSleep(rndisRequest->lock, &rndisRequest->isSleeping, THREAD_INTERRUPTIBLE); + } + IOLockUnlock(rndisRequest->lock); + + DBGLOG("woke"); + + return true; +} + +bool HyperVNetwork::sendRNDISDataPacket(mbuf_t packet) { + size_t packetLength = mbuf_pkthdr_len(packet); + + UInt32 sendIndex = getNextSendIndex(); + UInt8 *rndisBuffer = sendBuffer + (sendSectionSize * sendIndex); + HyperVNetworkRNDISMessage *rndisMsg = (HyperVNetworkRNDISMessage*)rndisBuffer; + memset(rndisMsg, 0, sizeof (HyperVNetworkRNDISMessage)); + + rndisMsg->msgType = kHyperVNetworkRNDISMessageTypePacket; + rndisMsg->dataPacket.dataOffset = sizeof (HyperVNetworkRNDISMessageDataPacket); + rndisMsg->dataPacket.dataLength = (UInt32)packetLength; + rndisMsg->msgLength = sizeof (HyperVNetworkRNDISMessageDataPacket) + 8 + rndisMsg->dataPacket.dataLength; + + rndisBuffer += rndisMsg->dataPacket.dataOffset + 8; + for (mbuf_t pktCurrent = packet; pktCurrent != NULL; pktCurrent = mbuf_next(pktCurrent)) { + size_t pktCurrentLength = mbuf_len(pktCurrent); + memcpy(rndisBuffer, mbuf_data(pktCurrent), pktCurrentLength); + rndisBuffer += pktCurrentLength; + } + freePacket(packet); + + // + // Create packet for sending the RNDIS data packet. + // + HyperVNetworkMessage netMsg; + memset(&netMsg, 0, sizeof (netMsg)); + netMsg.messageType = kHyperVNetworkMessageTypeV1SendRNDISPacket; + netMsg.v1.sendRNDISPacket.channelType = kHyperVNetworkRNDISChannelTypeData; + netMsg.v1.sendRNDISPacket.sendBufferSectionIndex = sendIndex; + netMsg.v1.sendRNDISPacket.sendBufferSectionSize = rndisMsg->msgLength; + + DBGLOG("Packet at index %u, size %u bytes", sendIndex, rndisMsg->msgLength); + hvDevice->writeInbandPacketWithTransactionId(&netMsg, sizeof (netMsg), sendIndex | kHyperVNetworkSendTransIdBits, true); + + return true; +} + +bool HyperVNetwork::initializeRNDIS() { + HyperVNetworkRNDISRequest *rndisRequest = allocateRNDISRequest(); + rndisRequest->message.msgType = kHyperVNetworkRNDISMessageTypeInit; + rndisRequest->message.msgLength = sizeof(HyperVNetworkRNDISMessageInitializeRequest) + 8; + + rndisRequest->message.initRequest.majorVersion = kHyperVNetworkRNDISVersionMajor; + rndisRequest->message.initRequest.minorVersion = kHyperVNetworkRNDISVersionMinor; + rndisRequest->message.initRequest.maxTransferSize = kHyperVNetworkRNDISMaxTransferSize; + + bool result = sendRNDISRequest(rndisRequest); + if (result) { + DBGLOG("RNDIS initializated with status 0x%X, max packets per msg %u, max transfer size 0x%X, packet alignment 0x%X", + rndisRequest->message.initComplete.status, rndisRequest->message.initComplete.maxPacketsPerMessage, + rndisRequest->message.initComplete.maxTransferSize, rndisRequest->message.initComplete.packetAlignmentFactor); + result = rndisRequest->message.initComplete.status == kHyperVNetworkRNDISStatusSuccess; + } else { + SYSLOG("Failed to send RNDIS initialization request"); + } + + freeRNDISRequest(rndisRequest); + return result; +} + +bool HyperVNetwork::queryRNDISOID(HyperVNetworkRNDISOID oid, void *value, UInt32 *valueSize) { + if (value == NULL || valueSize == NULL) { + return false; + } + + HyperVNetworkRNDISRequest *rndisRequest = allocateRNDISRequest(); + rndisRequest->message.msgType = kHyperVNetworkRNDISMessageTypeQuery; + rndisRequest->message.msgLength = sizeof(HyperVNetworkRNDISMessageQueryRequest) + 8; + + rndisRequest->message.queryRequest.oid = oid; + rndisRequest->message.queryRequest.infoBufferOffset = sizeof(HyperVNetworkRNDISMessageQueryRequest); + rndisRequest->message.queryRequest.infoBufferLength = 0; + rndisRequest->message.queryRequest.deviceVcHandle = 0; + + DBGLOG("OID query 0x%X request offset 0x%X, length 0x%X", oid, + rndisRequest->message.queryRequest.infoBufferOffset, rndisRequest->message.queryRequest.infoBufferLength); + + bool result = sendRNDISRequest(rndisRequest); + if (result) { + DBGLOG("OID query 0x%X response status 0x%X, offset 0x%X, length 0x%X", oid, + rndisRequest->message.queryComplete.status, + rndisRequest->message.queryComplete.infoBufferOffset, rndisRequest->message.queryComplete.infoBufferLength); + + memcpy(value, (UInt8*)(&rndisRequest->message.queryComplete) + rndisRequest->message.queryComplete.infoBufferOffset, rndisRequest->message.queryComplete.infoBufferLength); + *valueSize = rndisRequest->message.queryComplete.infoBufferLength; + } else { + SYSLOG("Failed to send OID 0x%X query", oid); + } + + freeRNDISRequest(rndisRequest); + return result; +} + + diff --git a/MacHyperVSupport/Network/HyperVNetworkRegs.hpp b/MacHyperVSupport/Network/HyperVNetworkRegs.hpp index 80579a7..365ffed 100644 --- a/MacHyperVSupport/Network/HyperVNetworkRegs.hpp +++ b/MacHyperVSupport/Network/HyperVNetworkRegs.hpp @@ -8,6 +8,490 @@ #ifndef HyperVNetworkRegs_h #define HyperVNetworkRegs_h -#define kHyperVNetworkRingBufferSize (128 * PAGE_SIZE) +#define kHyperVNetworkRingBufferSize (16 * PAGE_SIZE) + +#define kHyperVNetworkNDISVersion60001 0x00060001 +#define kHyperVNetworkNDISVersion6001E 0x0006001E + +#define kHyperVNetworkReceiveBufferSize (1024 * 1024 * 16) +#define kHyperVNetworkReceiveBufferSizeLegacy (1024 * 1024 * 15) +#define kHyperVNetworkSendBufferSize (1024 * 1024 * 15) + +// +// Protocol versions. +// +typedef enum : UInt32 { + kHyperVNetworkProtocolVersion1 = 0x00002, + kHyperVNetworkProtocolVersion2 = 0x30002, + kHyperVNetworkProtocolVersion4 = 0x40000, + kHyperVNetworkProtocolVersion5 = 0x50000, + kHyperVNetworkProtocolVersion6 = 0x60000, + kHyperVNetworkProtocolVersion61 = 0x60001 +} HyperVNetworkProtocolVersion; + +// +// Network message types. +// +typedef enum : UInt32 { + kHyperVNetworkMessageTypeNone = 0, + + // Initialization. + kHyperVNetworkMessageTypeInit = 1, + kHyperVNetworkMessageTypeInitComplete = 2, + + // Protocol version 1. + kHyperVNetworkMessageTypeV1SendNDISVersion = 100, + kHyperVNetworkMessageTypeV1SendReceiveBuffer, + kHyperVNetworkMessageTypeV1SendReceiveBufferComplete, + kHyperVNetworkMessageTypeV1RevokeReceiveBuffer, + kHyperVNetworkMessageTypeV1SendSendBuffer, + kHyperVNetworkMessageTypeV1SendSendBufferComplete, + kHyperVNetworkMessageTypeV1RevokeSendBuffer, + kHyperVNetworkMessageTypeV1SendRNDISPacket, + kHyperVNetworkMessageTypeV1SendRNDISPacketComplete +} HyperVNetworkMessageType; + +// +// Network status codes. +// +typedef enum : UInt32 { + kHyperVNetworkMessageStatusNone = 0, + kHyperVNetworkMessageStatusSuccess, + kHyperVNetworkMessageStatusFailure, + kHyperVNetworkMessageStatusProtocolTooNew, + kHyperVNetworkMessageStatusProtocolTooOld, + kHyperVNetworkMessageStatusInvalidRNDISPacket, + kHyperVNetworkMessageStatusBusy, + kHyperVNetworkMessageStatusProtocolUnsupported, + kHyperVNetworkMessageStatusMaximum +} HyperVNetworkMessageStatus; + +// +// Initialization (all versions) +// + +// +// Initialization version message. +// This message is used during intial setup right after opening the VMBus channel. +// +typedef struct __attribute__((packed)) { + HyperVNetworkProtocolVersion minProtocolVersion; + HyperVNetworkProtocolVersion maxProtocolVersion; +} HyperVNetworkMessageInitVersion; + +// +// Initialization complete message. +// This is the response message from Hyper-V used during intial setup right +// after opening the VMBus channel. +// +typedef struct __attribute__((packed)) { + HyperVNetworkProtocolVersion negotiatedProtocolVersion; + UInt32 maxMdlChainLength; + HyperVNetworkMessageStatus status; +} HyperVNetworkMessageInitComplete; + +// +// Initialization message. +// +typedef union __attribute__((packed)) { + HyperVNetworkMessageInitVersion initVersion; + HyperVNetworkMessageInitComplete initComplete; +} HyperVNetworkMessageInit; + +// +// Protocol version 1 +// + +// +// Send NDIS version to Hyper-V. +// +typedef struct __attribute__((packed)) { + UInt32 major; + UInt32 minor; +} HyperVNetworkV1MessageSendNDISVersion; + +// +// Send receive buffer to Hyper-V using an allocated GPADL. +// +typedef struct __attribute__((packed)) { + UInt32 gpadlHandle; + UInt16 id; +} HyperVNetworkV1MessageSendReceiveBuffer; + +typedef struct __attribute__((packed)) { + UInt32 offset; + UInt32 subAllocSize; + UInt32 numSubAllocs; + UInt32 endOffset; +} HyperVNetworkV1MessageReceiveBufferSection; + +// +// Completion response message from Hyper-V after sending a receive buffer. +// This message will be sent before Hyper-V begins using the receive buffer. +// +typedef struct __attribute__((packed)) { + HyperVNetworkMessageStatus status; + UInt32 numSections; + + // + // A receive buffer is split into two portions, a large section and a small section. + // These are then suballocated by a certain size. + // + // For example, the following break up of the receive buffer has 6 + // large suballocations and 10 small suballocations. + // + // + // + // | Large Section | | Small Section | + // ------------------------------------------------------------ + // | | | | | | | | | | | | | | | | | | + // | | + // LargeOffset SmallOffset + HyperVNetworkV1MessageReceiveBufferSection sections[1]; +} HyperVNetworkV1MessageSendReceiveBufferComplete; + +// +// Message sent to Hyper-V revoking the receive buffer. +// +typedef struct { + UInt16 id; +} HyperVNetworkV1MessageRevokeReceiveBuffer; + +// +// Send send buffer to Hyper-V using an allocated GPADL. +// +typedef struct __attribute__((packed)) { + UInt32 gpadlHandle; + UInt16 id; +} HyperVNetworkV1MessageSendSendBuffer; + +// +// Completion response message from Hyper-V after sending a send buffer. +// This message will be sent before Hyper-V begins using the send buffer. +// +typedef struct __attribute__((packed)) { + HyperVNetworkMessageStatus status; + + // VM can select the size of the send buffer. + UInt32 sectionSize; +} HyperVNetworkV1MessageSendSendBufferComplete; + +// +// Message sent to Hyper-V revoking the send buffer. +// +typedef struct { + UInt16 id; +} HyperVNetworkV1MessageRevokeSendBuffer; + +typedef enum : UInt32 { + kHyperVNetworkRNDISChannelTypeData = 0, + kHyperVNetworkRNDISChannelTypeControl = 1 +} HyperVNetworkRNDISChannelType; + +#define kHyperVNetworkRNDISSendSectionIndexInvalid (-1) + +// +// Message used to send an RNDIS packet to the other end of the channel. +// This message is used by both Hyper-V and the VM. +// +typedef struct __attribute__((packed)) { + // + // Specified by RNDIS. RNDIS uses 0 for DATA, and 1 for CONTROL. + // Ethernet packets are typically data, all other non-Ethernet packets are control. + // + HyperVNetworkRNDISChannelType channelType; + + // + // Used to specify what data is to be sent from the send buffer. + // 0xFFFFFFFF means the send buffer is not being used and data was sent via another method. + // + UInt32 sendBufferSectionIndex; + UInt32 sendBufferSectionSize; +} HyperVNetworkV1MessageSendRNDISPacket; + +// +// Response message used after an RNDIS packet is received at the other end of the channel. +// This message is used by both Hyper-V and the VM. +// +typedef struct __attribute__((packed)) { + HyperVNetworkMessageStatus status; +} HyperVNetworkV1MessageSendRNDISPacketComplete; + +// +// Protocol version 1 messages. +// +typedef union __attribute__((packed)) { + HyperVNetworkV1MessageSendNDISVersion sendNDISVersion; + HyperVNetworkV1MessageSendReceiveBuffer sendReceiveBuffer; + HyperVNetworkV1MessageSendReceiveBufferComplete sendReceiveBufferComplete; + HyperVNetworkV1MessageRevokeReceiveBuffer revokeReceiveBuffer; + HyperVNetworkV1MessageSendSendBuffer sendSendBuffer; + HyperVNetworkV1MessageSendSendBufferComplete sendSendBufferComplete; + HyperVNetworkV1MessageSendRNDISPacket sendRNDISPacket; + HyperVNetworkV1MessageSendRNDISPacketComplete sendRNDISPacketComplete; +} HyperVNetworkV1Message; + +// +// Main message structure. +// +typedef struct __attribute__((packed)) { + HyperVNetworkMessageType messageType; + union { + HyperVNetworkMessageInit init; + HyperVNetworkV1Message v1; + } __attribute__((packed)); + UInt8 padd[sizeof (HyperVNetworkMessageInit)]; // TODO: required for now for some reason, otherwise Hyper-V rejects message +} HyperVNetworkMessage; + +// +// RNDIS messages. +// + +#define kHyperVNetworkReceiveBufferID 0xCAFE +#define kHyperVNetworkSendBufferID 0x0000 + +#define kHyperVNetworkRNDISVersionMajor 0x0001 +#define kHyperVNetworkRNDISVersionMinor 0x0000 +#define kHyperVNetworkRNDISMaxTransferSize 0x4000 + +#define kHyperVNetworkRNDISMessageTypeCompletion 0x80000000 + +typedef enum : UInt32 { + kHyperVNetworkRNDISStatusSuccess = 0x0, + kHyperVNetworkRNDISStatusMediaConnect = 0x4001000B, + kHyperVNetworkRNDISStatusMediaDisconnect = 0x4001000C, + kHyperVNetworkRNDISStatusLinkSpeedChange = 0x40010013, + kHyperVNetworkRNDISStatusNetworkChange = 0x40010018 +} HyperVNetworkRNDISStatus; + +typedef enum : UInt32 { + kHyperVNetworkRNDISMessageTypePacket = 0x1, + kHyperVNetworkRNDISMessageTypeInit = 0x2, + kHyperVNetworkRNDISMessageTypeInitComplete = (kHyperVNetworkRNDISMessageTypeInit | kHyperVNetworkRNDISMessageTypeCompletion), + kHyperVNetworkRNDISMessageTypeHalt = 0x3, + kHyperVNetworkRNDISMessageTypeQuery = 0x4, + kHyperVNetworkRNDISMessageTypeQueryComplete = (kHyperVNetworkRNDISMessageTypeQuery | kHyperVNetworkRNDISMessageTypeCompletion), + kHyperVNetworkRNDISMessageTypeSet = 0x5, + kHyperVNetworkRNDISMessageTypeSetComplete = (kHyperVNetworkRNDISMessageTypeSet | kHyperVNetworkRNDISMessageTypeCompletion), + kHyperVNetworkRNDISMessageTypeReset = 0x6, + kHyperVNetworkRNDISMessageTypeResetComplete = (kHyperVNetworkRNDISMessageTypeReset | kHyperVNetworkRNDISMessageTypeCompletion), + kHyperVNetworkRNDISMessageTypeIndicate = 0x7, + kHyperVNetworkRNDISMessageTypeKeepalive = 0x8, + kHyperVNetworkRNDISMessageTypeKeepaliveComplete = (kHyperVNetworkRNDISMessageTypeKeepalive | kHyperVNetworkRNDISMessageTypeCompletion) +} HyperVNetworkRNDISMessageType; + +// +// Data packet message. +// This message is used for Ethernet frames. Offsets are from +// the beginning of the packet header. +// +typedef struct { + UInt32 dataOffset; + UInt32 dataLength; + UInt32 oobDataOffset; + UInt32 oobDataLength; + UInt32 oobNumDataElements; + UInt32 perPacketInfoOffset; + UInt32 perPacketInfoLength; + UInt32 vcHandle; + UInt32 reserved; +} HyperVNetworkRNDISMessageDataPacket; + +// +// Initialization message. +// +typedef struct { + UInt32 requestId; + UInt32 majorVersion; + UInt32 minorVersion; + UInt32 maxTransferSize; +} HyperVNetworkRNDISMessageInitializeRequest; + +// +// Initialization complete message. +// +typedef struct { + UInt32 requestId; + HyperVNetworkRNDISStatus status; + UInt32 majorVersion; + UInt32 minorVersion; + UInt32 devFlags; + UInt32 medium; + UInt32 maxPacketsPerMessage; + UInt32 maxTransferSize; + UInt32 packetAlignmentFactor; + UInt32 afListOffset; + UInt32 afListSize; +} HyperVNetworkRNDISMessageInitializeComplete; + +// +// OID definitions. +// +typedef enum : UInt32 { + // Required general OIDs. + kHyperVNetworkRNDISOIDGeneralSupportedList = 0x10101, + kHyperVNetworkRNDISOIDGeneralHardwareStatus = 0x10102, + kHyperVNetworkRNDISOIDGeneralMediaSupported = 0x10103, + kHyperVNetworkRNDISOIDGeneralMediaInUse = 0x10104, + kHyperVNetworkRNDISOIDGeneralMaximumLookahead = 0x10105, + kHyperVNetworkRNDISOIDGeneralMaximumFrameSize = 0x10106, + kHyperVNetworkRNDISOIDGeneralLinkSpeed = 0x10107, + kHyperVNetworkRNDISOIDGeneralTransmitBufferSpace = 0x10108, + kHyperVNetworkRNDISOIDGeneralReceiveBufferSpace = 0x10109, + kHyperVNetworkRNDISOIDGeneralTransmitBlockSize = 0x1010A, + kHyperVNetworkRNDISOIDGeneralReceiveBlockSize = 0x1010B, + kHyperVNetworkRNDISOIDGeneralVendorId = 0x1010C, + kHyperVNetworkRNDISOIDGeneralVendorDescription = 0x1010D, + kHyperVNetworkRNDISOIDGeneralCurrentPacketFilter = 0x1010E, + kHyperVNetworkRNDISOIDGeneralCurrentLookahead = 0x1010F, + kHyperVNetworkRNDISOIDGeneralDriverVersion = 0x10110, + kHyperVNetworkRNDISOIDGeneralMaximumTotalSize = 0x10111, + kHyperVNetworkRNDISOIDGeneralProtocolOptions = 0x10112, + kHyperVNetworkRNDISOIDGeneralMACOptions = 0x10113, + kHyperVNetworkRNDISOIDGeneralMediaConnectStatus = 0x10114, + kHyperVNetworkRNDISOIDGeneralMaximumSendPackets = 0x10115, + kHyperVNetworkRNDISOIDGeneralVendorDriverVersion = 0x10116, + kHyperVNetworkRNDISOIDGeneralSupportedGUIDs = 0x10117, + kHyperVNetworkRNDISOIDGeneralNetworkLayerAddresses = 0x10118, + kHyperVNetworkRNDISOIDGeneralTransportHeaderOffset = 0x10119, + kHyperVNetworkRNDISOIDGeneralPhysicalMedium = 0x10202, + kHyperVNetworkRNDISOIDGeneralMachineName = 0x1021A, + kHyperVNetworkRNDISOIDGeneralRNDISConfigParameter = 0x1021B, + kHyperVNetworkRNDISOIDGeneralVLANId = 0x1021C, + + // Optional general OIDs. + kHyperVNetworkRNDISOIDGeneralMediaCapabilities = 0x10201, + + // Required statistics OIDs. + kHyperVNetworkRNDISOIDGeneralTransmitOk = 0x20101, + kHyperVNetworkRNDISOIDGeneralReceiveOk = 0x20102, + kHyperVNetworkRNDISOIDGeneralTransmitError = 0x20103, + kHyperVNetworkRNDISOIDGeneralReceiveError = 0x20104, + kHyperVNetworkRNDISOIDGeneralReceiveNoBuffer = 0x20105, + + // 802.3 Ethernet OIDs. + kHyperVNetworkRNDISOIDEthernetPermanentAddress = 0x1010101, + kHyperVNetworkRNDISOIDEthernetCurrentAddress = 0x1010102, + kHyperVNetworkRNDISOIDEthernetMulticastList = 0x1010103, + kHyperVNetworkRNDISOIDEthernetMaximumListSize = 0x1010104, + kHyperVNetworkRNDISOIDEthernetMACOptions = 0x1010105, + kHyperVNetworkRNDISOIDEthernetReceiveErrorAlignment = 0x1020101, + kHyperVNetworkRNDISOIDEthernetTransmitOneCollision = 0x1020102, + kHyperVNetworkRNDISOIDEthernetTransmitMoreCollisions = 0x1020103, + kHyperVNetworkRNDISOIDEthernetTransmitDeferred = 0x1020201, + kHyperVNetworkRNDISOIDEthernetTransmitMaxCollisions = 0x1020202, + kHyperVNetworkRNDISOIDEthernetReceiveOverrun = 0x1020203, + kHyperVNetworkRNDISOIDEthernetTransmitUnderrun = 0x1020204, + kHyperVNetworkRNDISOIDEthernetTransmitHeartbeatFailure = 0x1020205, + kHyperVNetworkRNDISOIDEthernetTransmitTimesCRSLost = 0x1020206, + kHyperVNetworkRNDISOIDEthernetTransmitLateCollision = 0x1020207 +} HyperVNetworkRNDISOID; + +typedef enum : UInt32 { + kHyperVNetworkRNDISLinkStateConnected, + kHyperVNetworkRNDISLinkStateDisconnted +} HyperVNetworkRNDISLinkState; + +// +// Query request message. +// +typedef struct { + UInt32 requestId; + HyperVNetworkRNDISOID oid; + UInt32 infoBufferLength; + UInt32 infoBufferOffset; + UInt32 deviceVcHandle; +} HyperVNetworkRNDISMessageQueryRequest; + +// +// Query complete message. +// +typedef struct { + UInt32 requestId; + HyperVNetworkRNDISStatus status; + UInt32 infoBufferLength; + UInt32 infoBufferOffset; +} HyperVNetworkRNDISMessageQueryComplete; + +// +// Set request message. +// +typedef struct { + UInt32 requestId; + HyperVNetworkRNDISOID oid; + UInt32 infoBufferLength; + UInt32 infoBufferOffset; + UInt32 deviceVcHandle; +} HyperVNetworkRNDISMessageSetRequest; + +// +// Set complete message. +// +typedef struct { + UInt32 requestId; + HyperVNetworkRNDISStatus status; +} HyperVNetworkRNDISMessageSetComplete; + +// +// Reset request message. +// +typedef struct { + UInt32 reserved; +} HyperVNetworkRNDISMessageResetRequest; + +// +// Reset request message. +// +typedef struct { + HyperVNetworkRNDISStatus status; + UInt32 addressingReset; +} HyperVNetworkRNDISMessageResetComplete; + +// +// Indicate status message. +// +typedef struct { + HyperVNetworkRNDISStatus status; + UInt32 statusBufferLength; + UInt32 statusBufferOffset; +} HyperVNetworkRNDISMessageIndicateStatus; + +// +// Keep alive request message. +// +typedef struct { + UInt32 requestId; +} HyperVNetworkRNDISMessageKeepaliveRequest; + +// +// Keep alive complete message. +// +typedef struct { + UInt32 requestId; + HyperVNetworkRNDISStatus status; +} HyperVNetworkRNDISMessageKeepaliveComplete; + +// +// Main message structure. +// +typedef struct { + HyperVNetworkRNDISMessageType msgType; + UInt32 msgLength; + + union { + HyperVNetworkRNDISMessageDataPacket dataPacket; + HyperVNetworkRNDISMessageInitializeRequest initRequest; + HyperVNetworkRNDISMessageInitializeComplete initComplete; + HyperVNetworkRNDISMessageQueryRequest queryRequest; + HyperVNetworkRNDISMessageQueryComplete queryComplete; + HyperVNetworkRNDISMessageSetRequest setRequest; + HyperVNetworkRNDISMessageSetComplete setComplete; + HyperVNetworkRNDISMessageResetRequest resetRequest; + HyperVNetworkRNDISMessageResetComplete resetComplete; + HyperVNetworkRNDISMessageIndicateStatus indicateStatus; + HyperVNetworkRNDISMessageKeepaliveRequest keepaliveRequest; + HyperVNetworkRNDISMessageKeepaliveComplete keepaliveComplete; + }; +} HyperVNetworkRNDISMessage; #endif /* HyperVNetworkRegs_h */ diff --git a/MacHyperVSupport/Storage/HyperVStorage.cpp b/MacHyperVSupport/Storage/HyperVStorage.cpp index e1d072e..cc0a895 100644 --- a/MacHyperVSupport/Storage/HyperVStorage.cpp +++ b/MacHyperVSupport/Storage/HyperVStorage.cpp @@ -58,10 +58,18 @@ bool HyperVStorage::InitializeController() { // packetSizeDelta = sizeof (HyperVStorageSCSIRequestWin8Extension); + // + // Configure interrupt. + // + interruptSource = + IOInterruptEventSource::interruptEventSource(this, OSMemberFunctionCast(IOInterruptEventAction, this, &HyperVStorage::handleInterrupt), getProvider(), 0); + getProvider()->getWorkLoop()->addEventSource(interruptSource); + interruptSource->enable(); + // // Configure the channel. // - if (!hvDevice->openChannel(kHyperVStorageRingBufferSize, kHyperVStorageRingBufferSize, this, OSMemberFunctionCast(IOInterruptEventAction, this, &HyperVStorage::handleInterrupt))) { + if (!hvDevice->openChannel(kHyperVStorageRingBufferSize, kHyperVStorageRingBufferSize)) { return false; } @@ -327,27 +335,23 @@ SCSIServiceResponse HyperVStorage::ProcessParallelTask(SCSIParallelTaskIdentifie packet.flags = kHyperVStoragePacketFlagRequestCompletion; currentTask = parallelRequest; - - - HyperVVMBusDeviceRequest request; - request.sendData = &packet; - request.sendDataLength = sizeof (packet) - packetSizeDelta; - request.sendPacketType = dataDirection != kSCSIDataTransfer_NoDataTransfer ? kVMBusPacketTypeDataUsingGPADirect : kVMBusPacketTypeDataInband; - request.responseRequired = true; - request.responseData = NULL; - request.multiPageBuffer = NULL; + if (dataDirection != kSCSIDataTransfer_NoDataTransfer) { - if (!prepareDataTransfer(parallelRequest, &request)) { + VMBusPacketMultiPageBuffer *pagePacket; + UInt32 pagePacketLength; + if (!prepareDataTransfer(parallelRequest, &pagePacket, &pagePacketLength)) { return kSCSIServiceResponse_FUNCTION_REJECTED; } UInt64 lengthPhys = GetRequestedDataTransferCount(parallelRequest); packet.scsiRequest.dataTransferLength = (UInt32) lengthPhys; + + hvDevice->writeGPADirectMultiPagePacket(&packet, sizeof (packet) - packetSizeDelta, true, pagePacket, pagePacketLength); + } else { + hvDevice->writeInbandPacket(&packet, sizeof (packet) - packetSizeDelta, true); } - - hvDevice->doRequest(&request); currentTask = parallelRequest; /*if (dataDirection != kSCSIDataTransfer_NoDataTransfer && request.mbpArray != NULL) { diff --git a/MacHyperVSupport/Storage/HyperVStorage.hpp b/MacHyperVSupport/Storage/HyperVStorage.hpp index 95a371a..22a5192 100644 --- a/MacHyperVSupport/Storage/HyperVStorage.hpp +++ b/MacHyperVSupport/Storage/HyperVStorage.hpp @@ -30,6 +30,7 @@ class HyperVStorage : public IOSCSIParallelInterfaceController { private: HyperVVMBusDevice *hvDevice; + IOInterruptEventSource *interruptSource; UInt32 protocolVersion; UInt32 senseBufferSize; @@ -62,7 +63,7 @@ class HyperVStorage : public IOSCSIParallelInterfaceController { void setHBAInfo(); void completeIO(HyperVStoragePacket *packet); - bool prepareDataTransfer(SCSIParallelTaskIdentifier parallelRequest, HyperVVMBusDeviceRequest *request); + bool prepareDataTransfer(SCSIParallelTaskIdentifier parallelRequest, VMBusPacketMultiPageBuffer **pagePacket, UInt32 *pagePacketLength); void completeDataTransfer(SCSIParallelTaskIdentifier parallelRequest, HyperVStoragePacket *packet); protected: diff --git a/MacHyperVSupport/Storage/HyperVStoragePrivate.cpp b/MacHyperVSupport/Storage/HyperVStoragePrivate.cpp index ea2c3ad..c89a6d8 100644 --- a/MacHyperVSupport/Storage/HyperVStoragePrivate.cpp +++ b/MacHyperVSupport/Storage/HyperVStoragePrivate.cpp @@ -8,22 +8,10 @@ #include "HyperVStorage.hpp" IOReturn HyperVStorage::executeCommand(HyperVStoragePacket *packet, bool checkCompletion) { - // - // Create request for inband data with a direct response. - // - HyperVVMBusDeviceRequest request; - request.sendData = packet; - request.sendDataLength = sizeof (*packet) - packetSizeDelta; - request.sendPacketType = kVMBusPacketTypeDataInband; - request.responseRequired = true; - - request.responseData = packet; - request.responseDataLength = sizeof (*packet); - // // Send packet and get response. // - IOReturn status = hvDevice->doRequest(&request); + IOReturn status = hvDevice->writeInbandPacket(packet, sizeof (HyperVStoragePacket) - packetSizeDelta, true, packet, sizeof (HyperVStoragePacket)); if (status != kIOReturnSuccess) { return status; } @@ -41,32 +29,41 @@ IOReturn HyperVStorage::executeCommand(HyperVStoragePacket *packet, bool checkCo } void HyperVStorage::handleInterrupt(OSObject *owner, IOInterruptEventSource *sender, int count) { - // DBGLOG("Interrupt!"); - HyperVStoragePacket packet; + + VMBusPacketType type; + UInt32 headersize; + UInt32 totalsize; - HyperVVMBusDeviceRequest request; - request.sendData = NULL; - request.responseData = &packet; - request.responseDataLength = sizeof (packet); - - // UInt32 dataSize = sizeof(packet); - - // hvDevice->readPacket(&packet, &dataSize); - hvDevice->doRequest(&request); + void *responseBuffer; + UInt32 responseLength; - switch (packet.operation) { - case kHyperVStoragePacketOperationCompleteIO: - completeIO(&packet); - break; - - case kHyperVStoragePacketOperationEnumerateBus: - case kHyperVStoragePacketOperationRemoveDevice: - panic("SCSI device hotplug is not supported\n"); - break; - - default: + while (true) { + if (!hvDevice->nextPacketAvailable(&type, &headersize, &totalsize)) { break; + } + + UInt64 transactionId; + hvDevice->readInbandCompletionPacket(&packet, sizeof (packet), &transactionId); + + switch (packet.operation) { + case kHyperVStoragePacketOperationCompleteIO: + if (hvDevice->getPendingTransaction(transactionId, &responseBuffer, &responseLength)) { + memcpy(responseBuffer, &packet, sizeof (packet)); + hvDevice->wakeTransaction(transactionId); + } else { + completeIO(&packet); + } + break; + + case kHyperVStoragePacketOperationEnumerateBus: + case kHyperVStoragePacketOperationRemoveDevice: + panic("SCSI device hotplug is not supported\n"); + break; + + default: + break; + } } } @@ -120,13 +117,13 @@ void HyperVStorage::completeIO(HyperVStoragePacket *packet) { } } -bool HyperVStorage::prepareDataTransfer(SCSIParallelTaskIdentifier parallelRequest, HyperVVMBusDeviceRequest *request) { +bool HyperVStorage::prepareDataTransfer(SCSIParallelTaskIdentifier parallelRequest, VMBusPacketMultiPageBuffer **pagePacket, UInt32 *pagePacketLength) { // // Get DMA command and page buffer for this task. // IODMACommand *dmaCmd = GetDMACommand(parallelRequest); - request->multiPageBuffer = (VMBusPacketMultiPageBuffer*) GetHBADataPointer(parallelRequest); - if (dmaCmd == NULL || request->multiPageBuffer == NULL) { + *pagePacket = (VMBusPacketMultiPageBuffer*) GetHBADataPointer(parallelRequest); + if (dmaCmd == NULL || *pagePacket == NULL) { return false; } @@ -153,8 +150,8 @@ bool HyperVStorage::prepareDataTransfer(SCSIParallelTaskIdentifier parallelReque // // Populate PFNs in page buffer. // - request->multiPageBuffer->range.length = (UInt32) bufferLength; - request->multiPageBuffer->range.offset = 0; + (*pagePacket)->range.length = (UInt32) bufferLength; + (*pagePacket)->range.offset = 0; for (int i = 0; i < numSegs; i++) { if (i != 0 && i != (numSegs - 1)) { @@ -163,10 +160,10 @@ bool HyperVStorage::prepareDataTransfer(SCSIParallelTaskIdentifier parallelReque } } - request->multiPageBuffer->range.pfns[i] = segs64[i].fIOVMAddr >> PAGE_SHIFT; + (*pagePacket)->range.pfns[i] = segs64[i].fIOVMAddr >> PAGE_SHIFT; } - request->multiPageBufferLength = sizeof (VMBusPacketMultiPageBuffer) + (sizeof (UInt64) * numSegs); + *pagePacketLength = sizeof (VMBusPacketMultiPageBuffer) + (sizeof (UInt64) * numSegs); return true; } diff --git a/MacHyperVSupport/VMBusController/HyperVVMBusController.hpp b/MacHyperVSupport/VMBusController/HyperVVMBusController.hpp index b0b7865..a70f457 100644 --- a/MacHyperVSupport/VMBusController/HyperVVMBusController.hpp +++ b/MacHyperVSupport/VMBusController/HyperVVMBusController.hpp @@ -86,9 +86,10 @@ class HyperVVMBusController : public IOInterruptController { HyperVCPUData cpuData; + bool useLegacyEventFlags = false; HyperVDMABuffer vmbusEventFlags; - UInt8 *vmbusRxEventFlags; - UInt8 *vmbusTxEventFlags; + HyperVEventFlags *vmbusRxEventFlags; + HyperVEventFlags *vmbusTxEventFlags; HyperVDMABuffer vmbusMnf1; HyperVDMABuffer vmbusMnf2; @@ -160,7 +161,7 @@ class HyperVVMBusController : public IOInterruptController { // // Private VMBus channel management. // - bool configureVMBusChannelGpadl(VMBusChannel *channel); + bool configureVMBusChannelGpadl(VMBusChannel *channel, HyperVDMABuffer *buffer, UInt32 *gpadlHandle); bool configureVMBusChannel(VMBusChannel *channel); public: @@ -183,6 +184,8 @@ class HyperVVMBusController : public IOInterruptController { void signalVMBusChannel(UInt32 channelId); void closeVMBusChannel(UInt32 channelId); void freeVMBusChannel(UInt32 channelId); + + bool initVMBusChannelGpadl(UInt32 channelId, UInt32 bufferSize, UInt32 *gpadlHandle, void **buffer); }; #endif diff --git a/MacHyperVSupport/VMBusController/SynIC.cpp b/MacHyperVSupport/VMBusController/SynIC.cpp index f9cf800..6cc3f62 100644 --- a/MacHyperVSupport/VMBusController/SynIC.cpp +++ b/MacHyperVSupport/VMBusController/SynIC.cpp @@ -301,18 +301,40 @@ void HyperVVMBusController::handleSynICInterrupt(OSObject *target, void *refCon, // // Handle channel event flags. // - for (UInt32 i = 1; i <= vmbusChannelHighest; i++) { - if ((cpuData.perCPUData[cpuIndex].eventFlags[kVMBusInterruptMessage].flags[VMBUS_CHANNEL_EVENT_INDEX(i)] & VMBUS_CHANNEL_EVENT_MASK(i)) == 0 || - vmbusChannels[i].status != kVMBusChannelStatusOpen) { - continue; + // On Windows 8 and above, each channel has its own bit in the global event flags. + // On Windows Server 2008 R2 and older, the RX event flags needs to be checked in a similar fashion. + // + if (useLegacyEventFlags && cpuData.perCPUData[cpuIndex].eventFlags[kVMBusInterruptMessage].flags[0] & 0x1) { + cpuData.perCPUData[cpuIndex].eventFlags[kVMBusInterruptMessage].flags[0] &= ~0x1; + for (UInt32 i = 1; i <= vmbusChannelHighest; i++) { + if ((vmbusRxEventFlags->flags[VMBUS_CHANNEL_EVENT_INDEX(i)] & VMBUS_CHANNEL_EVENT_MASK(i)) == 0 || + vmbusChannels[i].status != kVMBusChannelStatusOpen) { + continue; + } + + // + // Clear event flag and trigger handler. + // + vmbusRxEventFlags->flags[VMBUS_CHANNEL_EVENT_INDEX(i)] &= ~VMBUS_CHANNEL_EVENT_MASK(i); + if (vectors[i].handler != NULL) { + vectors[i].handler(vectors[i].target, vectors[i].refCon, vectors[i].nub, vectors[i].source); + } } - - // - // Clear event flag and trigger handler. - // - cpuData.perCPUData[cpuIndex].eventFlags[kVMBusInterruptMessage].flags[VMBUS_CHANNEL_EVENT_INDEX(i)] &= ~VMBUS_CHANNEL_EVENT_MASK(i); - if (vectors[i].handler != NULL) { - vectors[i].handler(vectors[i].target, vectors[i].refCon, vectors[i].nub, vectors[i].source); + + } else { + for (UInt32 i = 1; i <= vmbusChannelHighest; i++) { + if ((cpuData.perCPUData[cpuIndex].eventFlags[kVMBusInterruptMessage].flags[VMBUS_CHANNEL_EVENT_INDEX(i)] & VMBUS_CHANNEL_EVENT_MASK(i)) == 0 || + vmbusChannels[i].status != kVMBusChannelStatusOpen) { + continue; + } + + // + // Clear event flag and trigger handler. + // + cpuData.perCPUData[cpuIndex].eventFlags[kVMBusInterruptMessage].flags[VMBUS_CHANNEL_EVENT_INDEX(i)] &= ~VMBUS_CHANNEL_EVENT_MASK(i); + if (vectors[i].handler != NULL) { + vectors[i].handler(vectors[i].target, vectors[i].refCon, vectors[i].nub, vectors[i].source); + } } } diff --git a/MacHyperVSupport/VMBusController/VMBus.cpp b/MacHyperVSupport/VMBusController/VMBus.cpp index 1450f85..f12cfe6 100644 --- a/MacHyperVSupport/VMBusController/VMBus.cpp +++ b/MacHyperVSupport/VMBusController/VMBus.cpp @@ -42,8 +42,11 @@ bool HyperVVMBusController::allocateVMBusBuffers() { allocateDmaBuffer(&vmbusMnf1, PAGE_SIZE); allocateDmaBuffer(&vmbusMnf2, PAGE_SIZE); - vmbusRxEventFlags = (UInt8*)vmbusEventFlags.buffer; - vmbusTxEventFlags = (UInt8*)vmbusEventFlags.buffer + PAGE_SIZE / 2; + // + // Event flag bits primarily used on Windows Server 2008 R2 and older. + // + vmbusRxEventFlags = (HyperVEventFlags*)vmbusEventFlags.buffer; + vmbusTxEventFlags = (HyperVEventFlags*)((UInt8*)vmbusEventFlags.buffer + PAGE_SIZE / 2); return true; } diff --git a/MacHyperVSupport/VMBusController/VMBus.hpp b/MacHyperVSupport/VMBusController/VMBus.hpp index a55f850..b66f905 100644 --- a/MacHyperVSupport/VMBusController/VMBus.hpp +++ b/MacHyperVSupport/VMBusController/VMBus.hpp @@ -386,6 +386,46 @@ typedef struct __attribute__((packed)) { UInt64 transactionId; } VMBusPacketHeader; +typedef struct __attribute__((packed)) { + UInt32 count; + UInt32 offset; +} VMBusTransferPageRange; + +typedef struct __attribute__((packed)) { + VMBusPacketHeader header; + + UInt16 transferPagesetId; + UInt8 senderOwnsSets; + UInt8 reserved; + UInt32 rangeCount; + VMBusTransferPageRange ranges[1]; +} VMBusPacketTransferPages; + +#define kVMBusMaxPageBufferCount 32 + +// +// Single page buffer. +// +typedef struct __attribute__((packed)) { + UInt32 length; + UInt32 offset; + UInt64 pfn; +} VMBusSinglePageBuffer; + +typedef struct __attribute__((packed)) { + VMBusPacketHeader header; + + UInt32 reserved; + // + // Number of single pages. + // + UInt32 rangeCount; + VMBusSinglePageBuffer ranges[kVMBusMaxPageBufferCount]; +} VMBusPacketSinglePageBuffer; + +// +// Multiple page buffer. +// typedef struct __attribute__((packed)) { UInt32 length; UInt32 offset; diff --git a/MacHyperVSupport/VMBusController/VMBusChannel.cpp b/MacHyperVSupport/VMBusController/VMBusChannel.cpp index bc52d00..de4cb32 100644 --- a/MacHyperVSupport/VMBusController/VMBusChannel.cpp +++ b/MacHyperVSupport/VMBusController/VMBusChannel.cpp @@ -8,7 +8,7 @@ #include "HyperVVMBusController.hpp" #include "HyperVVMBusInternal.hpp" -bool HyperVVMBusController::configureVMBusChannelGpadl(VMBusChannel *channel) { +bool HyperVVMBusController::configureVMBusChannelGpadl(VMBusChannel *channel, HyperVDMABuffer *buffer, UInt32 *gpadlHandle) { UInt32 channelId = channel->offerMessage.channelId; // @@ -19,20 +19,20 @@ bool HyperVVMBusController::configureVMBusChannelGpadl(VMBusChannel *channel) { return false; } - channel->gpadlHandle = nextGpadlHandle; + *gpadlHandle = nextGpadlHandle; nextGpadlHandle++; IOSimpleLockUnlock(nextGpadlHandleLock); // // Maximum number of pages allowed is 8190 (8192 - 2 for TX and RX headers). // - UInt32 pageCount = (UInt32)(channel->dataBuffer.size >> PAGE_SHIFT); + UInt32 pageCount = (UInt32)(buffer->size >> PAGE_SHIFT); if (pageCount > kHyperVMaxGpadlPages) { SYSLOG("%u is above the maximum supported number of GPADL pages"); return false; } - DBGLOG("Configuring GPADL handle 0x%X for channel %u of %llu pages", channel->gpadlHandle, channelId, pageCount); + DBGLOG("Configuring GPADL handle 0x%X for channel %u of %llu pages", *gpadlHandle, channelId, pageCount); // // For larger GPADL requests, a GPADL header and one or more GPADL body messages are required. @@ -62,13 +62,13 @@ bool HyperVVMBusController::configureVMBusChannelGpadl(VMBusChannel *channel) { // gpadlHeader->header.type = kVMBusChannelMessageTypeGPADLHeader; gpadlHeader->channelId = channelId; - gpadlHeader->gpadl = channel->gpadlHandle; + gpadlHeader->gpadl = *gpadlHandle; gpadlHeader->rangeCount = kHyperVGpadlRangeCount; gpadlHeader->rangeBufferLength = sizeof (HyperVGPARange) + pageCount * sizeof (UInt64); // Max page count is 8190 gpadlHeader->range[0].byteOffset = 0; - gpadlHeader->range[0].byteCount = (UInt32)channel->dataBuffer.size; + gpadlHeader->range[0].byteCount = (UInt32)buffer->size; - UInt64 physPageIndex = channel->dataBuffer.physAddr >> PAGE_SHIFT; + UInt64 physPageIndex = buffer->physAddr >> PAGE_SHIFT; for (UInt32 i = 0; i < pageHeaderCount; i++) { gpadlHeader->range[0].pfnArray[i] = physPageIndex; physPageIndex++; @@ -106,7 +106,7 @@ bool HyperVVMBusController::configureVMBusChannelGpadl(VMBusChannel *channel) { memset(gpadlBody, 0, messageSize); gpadlBody->header.type = kVMBusChannelMessageTypeGPADLBody; - gpadlBody->gpadl = channel->gpadlHandle; + gpadlBody->gpadl = *gpadlHandle; for (UInt32 i = 0; i < pagesBodyCount; i++) { gpadlBody->pfn[i] = physPageIndex; physPageIndex++; @@ -160,7 +160,7 @@ bool HyperVVMBusController::configureVMBusChannelGpadl(VMBusChannel *channel) { // gpadlHeader->header.type = kVMBusChannelMessageTypeGPADLHeader; gpadlHeader->channelId = channelId; - gpadlHeader->gpadl = channel->gpadlHandle; + gpadlHeader->gpadl = *gpadlHandle; gpadlHeader->rangeCount = kHyperVGpadlRangeCount; gpadlHeader->rangeBufferLength = sizeof (HyperVGPARange) + pageCount * sizeof (UInt64); gpadlHeader->range[0].byteOffset = 0; @@ -190,13 +190,6 @@ bool HyperVVMBusController::configureVMBusChannelGpadl(VMBusChannel *channel) { return false; } } - - // - // Configure TX and RX buffer pointers. - // - channel->txBuffer = (VMBusRingBuffer*) channel->dataBuffer.buffer; - channel->rxBuffer = (VMBusRingBuffer*) (((UInt8*)channel->dataBuffer.buffer) + PAGE_SIZE * channel->rxPageIndex); - channel->status = kVMBusChannelStatusGpadlConfigured; return true; } @@ -207,7 +200,7 @@ bool HyperVVMBusController::configureVMBusChannel(VMBusChannel *channel) { openMsg.header.type = kVMBusChannelMessageTypeChannelOpen; openMsg.openId = channel->offerMessage.channelId; openMsg.channelId = channel->offerMessage.channelId; - openMsg.ringBufferGpadlHandle = channel->gpadlHandle; + openMsg.ringBufferGpadlHandle = channel->dataGpadlHandle; openMsg.downstreamRingBufferPageOffset = channel->rxPageIndex; openMsg.targetCpu = 0; @@ -263,10 +256,17 @@ bool HyperVVMBusController::initVMBusChannel(UInt32 channelId, UInt32 txBufferSi // // Configure GPADL for channel. // - if (!configureVMBusChannelGpadl(channel)) { + if (!configureVMBusChannelGpadl(channel, &channel->dataBuffer, &channel->dataGpadlHandle)) { return false; } + // + // Configure TX and RX buffer pointers. + // + channel->txBuffer = (VMBusRingBuffer*) channel->dataBuffer.buffer; + channel->rxBuffer = (VMBusRingBuffer*) (((UInt8*)channel->dataBuffer.buffer) + PAGE_SIZE * channel->rxPageIndex); + channel->status = kVMBusChannelStatusGpadlConfigured; + *txBuffer = channel->txBuffer; *rxBuffer = channel->rxBuffer; @@ -293,7 +293,9 @@ void HyperVVMBusController::signalVMBusChannel(UInt32 channelId) { // // Set bit for channel. // -// vmbusTxEventFlags[VMBUS_CHANNEL_EVENT_INDEX(channelId)] |= VMBUS_CHANNEL_EVENT_MASK(channelId); + if (useLegacyEventFlags) { + vmbusTxEventFlags->flags[VMBUS_CHANNEL_EVENT_INDEX(channelId)] |= VMBUS_CHANNEL_EVENT_MASK(channelId); + } // // Signal event for specified connection. @@ -335,7 +337,7 @@ void HyperVVMBusController::closeVMBusChannel(UInt32 channelId) { gpadlTeardownMsg.header.type = kVMBusChannelMessageTypeGPADLTeardown; gpadlTeardownMsg.header.reserved = 0; gpadlTeardownMsg.channelId = channelId; - gpadlTeardownMsg.gpadl = channel->gpadlHandle; + gpadlTeardownMsg.gpadl = channel->dataGpadlHandle; VMBusChannelMessageGPADLTeardownResponse gpadlTeardownResponseMsg; result = sendVMBusMessage((VMBusChannelMessage*) &gpadlTeardownMsg, @@ -377,3 +379,14 @@ void HyperVVMBusController::freeVMBusChannel(UInt32 channelId) { DBGLOG("Channel %u is now freed", channelId); channel->status = kVMBusChannelStatusNotPresent; } + +bool HyperVVMBusController::initVMBusChannelGpadl(UInt32 channelId, UInt32 bufferSize, UInt32 *gpadlHandle, void **buffer) { + VMBusChannel *channel = &vmbusChannels[channelId]; + + HyperVDMABuffer buf; + allocateDmaBuffer(&buf, bufferSize); + + configureVMBusChannelGpadl(channel, &buf, gpadlHandle); + *buffer = buf.buffer; + return true; +} diff --git a/MacHyperVSupport/VMBusController/VMBusDriver.hpp b/MacHyperVSupport/VMBusController/VMBusDriver.hpp index 70c57a9..72cd2e7 100644 --- a/MacHyperVSupport/VMBusController/VMBusDriver.hpp +++ b/MacHyperVSupport/VMBusController/VMBusDriver.hpp @@ -51,7 +51,7 @@ typedef struct { // // Unique GPADL handle for this channel. // - UInt32 gpadlHandle; + UInt32 dataGpadlHandle; HyperVDMABuffer dataBuffer; HyperVDMABuffer eventBuffer; diff --git a/MacHyperVSupport/VMBusDevice/HyperVVMBusDevice.cpp b/MacHyperVSupport/VMBusDevice/HyperVVMBusDevice.cpp index eea305a..f30d35b 100644 --- a/MacHyperVSupport/VMBusDevice/HyperVVMBusDevice.cpp +++ b/MacHyperVSupport/VMBusDevice/HyperVVMBusDevice.cpp @@ -36,6 +36,19 @@ bool HyperVVMBusDevice::attach(IOService *provider) { snprintf(channelLocation, sizeof (channelLocation), "%x", channelId); setLocation(channelLocation); + // + // built-in required for some devices, like networking. + // + UInt8 builtInBytes = 0; + OSData *builtInData = OSData::withBytes(&builtInBytes, sizeof (builtInBytes)); + if (builtInData != NULL) { + setProperty("built-in", builtInData); + builtInData->release(); + } + + vmbusRequestsLock = IOLockAlloc(); + vmbusTransLock = IOLockAlloc(); + return true; } @@ -48,10 +61,13 @@ void HyperVVMBusDevice::detach(IOService *provider) { } vmbusProvider->freeVMBusChannel(channelId); + IOLockFree(vmbusRequestsLock); + IOLockFree(vmbusTransLock); + super::detach(provider); } -bool HyperVVMBusDevice::openChannel(UInt32 txSize, UInt32 rxSize, OSObject *owner, IOInterruptEventAction intAction) { +bool HyperVVMBusDevice::openChannel(UInt32 txSize, UInt32 rxSize, UInt64 maxAutoTransId) { if (channelIsOpen) { return true; } @@ -60,31 +76,22 @@ bool HyperVVMBusDevice::openChannel(UInt32 txSize, UInt32 rxSize, OSObject *owne txBufferSize = txSize; rxBufferSize = rxSize; - if (!setupInterrupt()) { + if (!setupCommandGate()) { return false; } - if (owner != NULL && intAction != NULL) { - childInterruptSource = IOInterruptEventSource::interruptEventSource(owner, intAction); - if (childInterruptSource == NULL) { - return kIOReturnError; - } - - workLoop->addEventSource(childInterruptSource); - childInterruptSource->enable(); - } - // // Open channel. // + vmbusMaxAutoTransId = maxAutoTransId; if (!vmbusProvider->initVMBusChannel(channelId, txBufferSize, &txBuffer, rxBufferSize, &rxBuffer)) { - teardownInterrupt(); + teardownCommandGate(); return false; } if (!vmbusProvider->openVMBusChannel(channelId)) { vmbusProvider->closeVMBusChannel(channelId); - teardownInterrupt(); + teardownCommandGate(); return false; } @@ -98,10 +105,246 @@ void HyperVVMBusDevice::closeChannel() { // Close channel and stop interrupts. // vmbusProvider->closeVMBusChannel(channelId); - teardownInterrupt(); + teardownCommandGate(); channelIsOpen = false; } -IOReturn HyperVVMBusDevice::doRequest(HyperVVMBusDeviceRequest *request) { - return commandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &HyperVVMBusDevice::doRequestGated), request); +bool HyperVVMBusDevice::createGpadlBuffer(UInt32 bufferSize, UInt32 *gpadlHandle, void **buffer) { + return vmbusProvider->initVMBusChannelGpadl(channelId, bufferSize, gpadlHandle, buffer); +} + +bool HyperVVMBusDevice::nextPacketAvailable(VMBusPacketType *type, UInt32 *packetHeaderLength, UInt32 *packetTotalLength) { + return commandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &HyperVVMBusDevice::nextPacketAvailableGated), + type, packetHeaderLength, packetTotalLength) == kIOReturnSuccess; +} + +bool HyperVVMBusDevice::nextInbandPacketAvailable(UInt32 *packetDataLength) { + VMBusPacketType pktType; + UInt32 pktHeaderLength; + UInt32 pktTotalLength; + + if (packetDataLength == NULL) { + return false; + } + + bool result = commandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &HyperVVMBusDevice::nextPacketAvailableGated), + &pktType, &pktHeaderLength, &pktTotalLength) == kIOReturnSuccess; + + if (result) { + if (pktType == kVMBusPacketTypeDataInband) { + *packetDataLength = pktTotalLength - pktHeaderLength; + } else { + result = false; + } + } + return result; +} + +UInt64 HyperVVMBusDevice::getNextTransId() { + IOLockLock(vmbusTransLock); + UInt64 value = vmbusTransId; + vmbusTransId++; + if (vmbusTransId > vmbusMaxAutoTransId) { + vmbusTransId = 0; + } + IOLockUnlock(vmbusTransLock); + return value; +} + +IOReturn HyperVVMBusDevice::readRawPacket(void *buffer, UInt32 bufferLength) { + return commandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &HyperVVMBusDevice::readRawPacketGated), + NULL, NULL, buffer, &bufferLength); +} + +IOReturn HyperVVMBusDevice::readInbandCompletionPacket(void *buffer, UInt32 bufferLength, UInt64 *transactionId) { + VMBusPacketHeader pktHeader; + UInt32 pktHeaderSize = sizeof (pktHeader); + + IOReturn status = commandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &HyperVVMBusDevice::readRawPacketGated), + &pktHeader, &pktHeaderSize, buffer, &bufferLength); + if (status == kIOReturnSuccess) { + if (pktHeader.type != kVMBusPacketTypeDataInband && pktHeader.type != kVMBusPacketTypeCompletion) { + MSGDBG("INBAND COMP attempted to read non-inband or non-completion packet"); + return kIOReturnUnsupported; + } + + if (transactionId != NULL) { + *transactionId = pktHeader.transactionId; + } + } + return status; +} + +IOReturn HyperVVMBusDevice::writeRawPacket(void *buffer, UInt32 bufferLength) { + return commandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &HyperVVMBusDevice::writeRawPacketGated), + NULL, NULL, buffer, &bufferLength); +} + +IOReturn HyperVVMBusDevice::writeInbandPacket(void *buffer, UInt32 bufferLength, bool responseRequired, + void *responseBuffer, UInt32 responseBufferLength) { + return writeInbandPacketWithTransactionId(buffer, bufferLength, getNextTransId(), responseRequired, responseBuffer, responseBufferLength); +} + +IOReturn HyperVVMBusDevice::writeInbandPacketWithTransactionId(void *buffer, UInt32 bufferLength, UInt64 transactionId, + bool responseRequired, void *responseBuffer, UInt32 responseBufferLength) { + return writePacketInternal(buffer, bufferLength, kVMBusPacketTypeDataInband, transactionId, responseRequired, responseBuffer, responseBufferLength); +} + +IOReturn HyperVVMBusDevice::writeGPADirectSinglePagePacket(void *buffer, UInt32 bufferLength, bool responseRequired, + VMBusSinglePageBuffer pageBuffers[], UInt32 pageBufferCount, + void *responseBuffer, UInt32 responseBufferLength) { + if (pageBufferCount > kVMBusMaxPageBufferCount) { + return kIOReturnNoResources; + } + + // + // Create packet for single page buffers. + // + UInt64 transactionId = getNextTransId(); + VMBusPacketSinglePageBuffer pagePacket; + UInt32 pagePacketLength = sizeof (VMBusPacketSinglePageBuffer) - + ((kVMBusMaxPageBufferCount - pageBufferCount) * sizeof (VMBusSinglePageBuffer)); + + pagePacket.header.type = kVMBusPacketTypeDataUsingGPADirect; + pagePacket.header.headerLength = pagePacketLength >> kVMBusPacketSizeShift; + pagePacket.header.totalLength = (pagePacketLength + bufferLength) >> kVMBusPacketSizeShift; + pagePacket.header.flags = responseRequired ? kVMBusPacketResponseRequired : 0; + pagePacket.header.transactionId = transactionId; + + pagePacket.reserved = 0; + pagePacket.rangeCount = pageBufferCount; + for (int i = 0; i < pagePacket.rangeCount; i++) { + pagePacket.ranges[i].length = pageBuffers[i].length; + pagePacket.ranges[i].offset = pageBuffers[i].offset; + pagePacket.ranges[i].pfn = pageBuffers[i].pfn; + } + + MSGDBG("SP Packet type %u, flags %u, trans %llu, header length %u, total length %u, page count %u", + pagePacket.header.type, pagePacket.header.flags, pagePacket.header.transactionId, + pagePacket.header.headerLength, pagePacket.header.totalLength, pageBufferCount); + + HyperVVMBusDeviceRequest req; + if (responseBuffer != NULL) { + req.isSleeping = true; + req.lock = IOLockAlloc(); + req.responseData = responseBuffer; + req.responseDataLength = responseBufferLength; + req.transactionId = transactionId; + addPacketRequest(&req); + } + + IOReturn status = commandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &HyperVVMBusDevice::writeRawPacketGated), + &pagePacket, &pagePacketLength, buffer, &bufferLength); + + if (responseBuffer != NULL) { + if (status == kIOReturnSuccess) { + sleepPacketRequest(&req); + } else { + wakeTransaction(transactionId); + } + } + return status; +} + +IOReturn HyperVVMBusDevice::writeGPADirectMultiPagePacket(void *buffer, UInt32 bufferLength, bool responseRequired, + VMBusPacketMultiPageBuffer *pagePacket, UInt32 pagePacketLength, + void *responseBuffer, UInt32 responseBufferLength) { + // + // For multi-page buffers, the packet header itself is passed to this function. + // Ensure general header fields are set. + // + UInt64 transactionId = getNextTransId(); + + pagePacket->header.type = kVMBusPacketTypeDataUsingGPADirect; + pagePacket->header.headerLength = pagePacketLength >> kVMBusPacketSizeShift; + pagePacket->header.totalLength = (pagePacketLength + bufferLength) >> kVMBusPacketSizeShift; + pagePacket->header.flags = responseRequired ? kVMBusPacketResponseRequired : 0; + pagePacket->header.transactionId = transactionId; + + pagePacket->reserved = 0; + pagePacket->rangeCount = 1; + + MSGDBG("MP Packet type %u, flags %u, trans %llu, header length %u, total length %u", + pagePacket->header.type, pagePacket->header.flags, pagePacket->header.transactionId, + pagePacket->header.headerLength, pagePacket->header.totalLength); + + HyperVVMBusDeviceRequest req; + if (responseBuffer != NULL) { + req.isSleeping = true; + req.lock = IOLockAlloc(); + req.responseData = responseBuffer; + req.responseDataLength = responseBufferLength; + req.transactionId = transactionId; + addPacketRequest(&req); + } + + IOReturn status = commandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &HyperVVMBusDevice::writeRawPacketGated), + pagePacket, &pagePacketLength, buffer, &bufferLength); + + if (responseBuffer != NULL) { + if (status == kIOReturnSuccess) { + sleepPacketRequest(&req); + } else { + wakeTransaction(transactionId); + } + } + return status; +} + +IOReturn HyperVVMBusDevice::writeCompletionPacketWithTransactionId(void *buffer, UInt32 bufferLength, UInt64 transactionId, bool responseRequired) { + return writePacketInternal(buffer, bufferLength, kVMBusPacketTypeCompletion, transactionId, responseRequired, NULL, 0); +} + +bool HyperVVMBusDevice::getPendingTransaction(UInt64 transactionId, void **buffer, UInt32 *bufferLength) { + IOLockLock(vmbusRequestsLock); + + HyperVVMBusDeviceRequest *current = vmbusRequests; + while (current != NULL) { + if (current->transactionId == transactionId) { + MSGDBG("Found transaction %u", transactionId); + + *buffer = current->responseData; + *bufferLength = current->responseDataLength; + IOLockUnlock(vmbusRequestsLock); + return true; + } + current = current->next; + } + + IOLockUnlock(vmbusRequestsLock); + return false; +} + +void HyperVVMBusDevice::wakeTransaction(UInt64 transactionId) { + IOLockLock(vmbusRequestsLock); + + HyperVVMBusDeviceRequest *current = vmbusRequests; + HyperVVMBusDeviceRequest *previous = NULL; + while (current != NULL) { + if (current->transactionId == transactionId) { + MSGDBG("Waking transaction %u", transactionId); + + // + // Remove from linked list. + // + if (previous != NULL) { + previous->next = current->next; + } else { + vmbusRequests = current->next; + } + IOLockUnlock(vmbusRequestsLock); + + // + // Wake sleeping thread. + // + IOLockLock(current->lock); + current->isSleeping = false; + IOLockUnlock(current->lock); + IOLockWakeup(current->lock, ¤t->isSleeping, true); + return; + } + previous = current; + current = current->next; + } + IOLockUnlock(vmbusRequestsLock); } diff --git a/MacHyperVSupport/VMBusDevice/HyperVVMBusDevice.hpp b/MacHyperVSupport/VMBusDevice/HyperVVMBusDevice.hpp index 22c8d8c..3abcec2 100644 --- a/MacHyperVSupport/VMBusDevice/HyperVVMBusDevice.hpp +++ b/MacHyperVSupport/VMBusDevice/HyperVVMBusDevice.hpp @@ -20,20 +20,14 @@ #define kHyperVVMBusDeviceChannelInstanceKey "HVInstance" #define kHyperVVMBusDeviceChannelIDKey "HVChannel" -typedef struct { - void *sendData; - UInt32 sendDataLength; - VMBusPacketType sendPacketType; - UInt64 transactionId; - bool responseRequired; +typedef struct HyperVVMBusDeviceRequest { + HyperVVMBusDeviceRequest *next; + IOLock *lock; + bool isSleeping; - VMBusPacketMultiPageBuffer *multiPageBuffer; - UInt32 multiPageBufferLength; - - void *responseData; - UInt32 responseDataLength; - - bool ignoreLargePackets; + UInt64 transactionId; + void *responseData; + UInt32 responseDataLength; } HyperVVMBusDeviceRequest; class HyperVVMBusDevice : public IOService { @@ -47,26 +41,40 @@ class HyperVVMBusDevice : public IOService { IOWorkLoop *workLoop; IOCommandGate *commandGate; bool commandLock; - bool commandSleeping; - IOInterruptEventSource *interruptSource; - IOInterruptEventSource *childInterruptSource; VMBusRingBuffer *txBuffer; UInt32 txBufferSize; VMBusRingBuffer *rxBuffer; UInt32 rxBufferSize; + + HyperVVMBusDeviceRequest *vmbusRequests = NULL; + IOLock *vmbusRequestsLock; + UInt64 vmbusTransId = 0; + UInt64 vmbusMaxAutoTransId = UINT64_MAX; + IOLock *vmbusTransLock; + + bool debugPackets = false; + + bool setupCommandGate(); + void teardownCommandGate(); - bool setupInterrupt(); - void teardownInterrupt(); - void handleInterrupt(OSObject *owner, IOInterruptEventSource *sender, int count); + IOReturn writePacketInternal(void *buffer, UInt32 bufferLength, VMBusPacketType packetType, UInt64 transactionId, + bool responseRequired, void *responseBuffer, UInt32 responseBufferLength); - IOReturn doRequestGated(HyperVVMBusDeviceRequest *request); + IOReturn nextPacketAvailableGated(VMBusPacketType *type, UInt32 *packetHeaderLength, UInt32 *packetTotalLength); + IOReturn readRawPacketGated(void *header, UInt32 *headerLength, void *buffer, UInt32 *bufferLength); + IOReturn writeRawPacketGated(void *header, UInt32 *headerLength, void *buffer, UInt32 *bufferLength); + IOReturn writeInbandPacketGated(void *buffer, UInt32 *bufferLength, bool *responseRequired, UInt64 *transactionId); UInt32 copyPacketDataFromRingBuffer(UInt32 readIndex, UInt32 readLength, void *data, UInt32 dataLength); + UInt32 seekPacketDataFromRingBuffer(UInt32 readIndex, UInt32 readLength); UInt32 copyPacketDataToRingBuffer(UInt32 writeIndex, void *data, UInt32 length); UInt32 zeroPacketDataToRingBuffer(UInt32 writeIndex, UInt32 length); + void addPacketRequest(HyperVVMBusDeviceRequest *vmbusRequest); + void sleepPacketRequest(HyperVVMBusDeviceRequest *vmbusRequest); + inline UInt32 getAvailableTxSpace() { return (txBuffer->writeIndex >= txBuffer->readIndex) ? (txBufferSize - (txBuffer->writeIndex - txBuffer->readIndex)) : @@ -84,10 +92,40 @@ class HyperVVMBusDevice : public IOService { // // General functions. // - bool openChannel(UInt32 txSize, UInt32 rxSize, OSObject *owner = NULL, IOInterruptEventAction intAction = NULL); + void setDebugMessagePrinting(bool enabled) { debugPackets = enabled; } + bool openChannel(UInt32 txSize, UInt32 rxSize, UInt64 maxAutoTransId = UINT64_MAX); void closeChannel(); + bool createGpadlBuffer(UInt32 bufferSize, UInt32 *gpadlHandle, void **buffer); - IOReturn doRequest(HyperVVMBusDeviceRequest *request); + + // + // Messages. + // + bool nextPacketAvailable(VMBusPacketType *type, UInt32 *packetHeaderLength, UInt32 *packetTotalLength); + bool nextInbandPacketAvailable(UInt32 *packetDataLength); + UInt64 getNextTransId(); + + IOReturn readRawPacket(void *buffer, UInt32 bufferLength); + IOReturn readInbandCompletionPacket(void *buffer, UInt32 bufferLength, UInt64 *transactionId = NULL); + + IOReturn writeRawPacket(void *buffer, UInt32 bufferLength); + IOReturn writeInbandPacket(void *buffer, UInt32 bufferLength, bool responseRequired, + void *responseBuffer = NULL, UInt32 responseBufferLength = 0); + IOReturn writeInbandPacketWithTransactionId(void *buffer, UInt32 bufferLength, UInt64 transactionId, bool responseRequired, + void *responseBuffer = NULL, UInt32 responseBufferLength = 0); + IOReturn writeGPADirectSinglePagePacket(void *buffer, UInt32 bufferLength, bool responseRequired, + VMBusSinglePageBuffer pageBuffers[], UInt32 pageBufferCount, + void *responseBuffer = NULL, UInt32 responseBufferLength = 0); + IOReturn writeGPADirectMultiPagePacket(void *buffer, UInt32 bufferLength, bool responseRequired, + VMBusPacketMultiPageBuffer *pagePacket, UInt32 pagePacketLength, + void *responseBuffer = NULL, UInt32 responseBufferLength = 0); + IOReturn writeCompletionPacketWithTransactionId(void *buffer, UInt32 bufferLength, UInt64 transactionId, bool responseRequired); + + + bool getPendingTransaction(UInt64 transactionId, void **buffer, UInt32 *bufferLength); + void wakeTransaction(UInt64 transactionId); + + }; #endif diff --git a/MacHyperVSupport/VMBusDevice/HyperVVMBusDeviceInternal.hpp b/MacHyperVSupport/VMBusDevice/HyperVVMBusDeviceInternal.hpp index 5d716e9..20d1f6e 100644 --- a/MacHyperVSupport/VMBusDevice/HyperVVMBusDeviceInternal.hpp +++ b/MacHyperVSupport/VMBusDevice/HyperVVMBusDeviceInternal.hpp @@ -13,4 +13,7 @@ #define SYSLOG(str, ...) SYSLOG_PRINT("HyperVVMBusDevice", str, ## __VA_ARGS__) #define DBGLOG(str, ...) DBGLOG_PRINT("HyperVVMBusDevice", str, ## __VA_ARGS__) +#define MSGDBG(str, ...) \ + if (this->debugPackets) DBGLOG_PRINT("HyperVVMBusDevice", str, ## __VA_ARGS__) + #endif diff --git a/MacHyperVSupport/VMBusDevice/HyperVVMBusDevicePrivate.cpp b/MacHyperVSupport/VMBusDevice/HyperVVMBusDevicePrivate.cpp index 07e6ab9..f672bf0 100644 --- a/MacHyperVSupport/VMBusDevice/HyperVVMBusDevicePrivate.cpp +++ b/MacHyperVSupport/VMBusDevice/HyperVVMBusDevicePrivate.cpp @@ -8,7 +8,7 @@ #include "HyperVVMBusDevice.hpp" #include "HyperVVMBusDeviceInternal.hpp" -bool HyperVVMBusDevice::setupInterrupt() { +bool HyperVVMBusDevice::setupCommandGate() { bool initialized = false; do { @@ -22,214 +22,187 @@ bool HyperVVMBusDevice::setupInterrupt() { break; } workLoop->addEventSource(commandGate); - - interruptSource = IOInterruptEventSource::interruptEventSource(this, - OSMemberFunctionCast(IOInterruptEventAction, this, &HyperVVMBusDevice::handleInterrupt), - this, 0); - if (interruptSource == NULL) { - workLoop->removeEventSource(commandGate); - break; - } - interruptSource->enable(); - workLoop->addEventSource(interruptSource); initialized = true; } while (false); if (!initialized) { - OSSafeReleaseNULL(interruptSource); OSSafeReleaseNULL(commandGate); OSSafeReleaseNULL(workLoop); - - return false; + + commandGate = NULL; + workLoop = NULL; } - return true; + return initialized; } -void HyperVVMBusDevice::teardownInterrupt() { - if (childInterruptSource != NULL) { - childInterruptSource->disable(); - workLoop->removeEventSource(childInterruptSource); - childInterruptSource = NULL; - } - - if (interruptSource == NULL) { - return; - } - - interruptSource->disable(); - workLoop->removeEventSource(interruptSource); +void HyperVVMBusDevice::teardownCommandGate() { workLoop->removeEventSource(commandGate); - - interruptSource->release(); + commandGate->release(); workLoop->release(); - - interruptSource = NULL; + commandGate = NULL; workLoop = NULL; } -void HyperVVMBusDevice::handleInterrupt(OSObject *owner, IOInterruptEventSource *sender, int count) { + +IOReturn HyperVVMBusDevice::writePacketInternal(void *buffer, UInt32 bufferLength, VMBusPacketType packetType, UInt64 transactionId, + bool responseRequired, void *responseBuffer, UInt32 responseBufferLength) { + // + // Create inband packet header. + // Sizes are represented as 8 byte units. // - // If there is a command in progress, handle that. + VMBusPacketHeader pktHeader; + pktHeader.type = packetType; + pktHeader.flags = responseRequired ? kVMBusPacketResponseRequired : 0; + pktHeader.transactionId = transactionId; + + UInt32 pktHeaderLength = sizeof (pktHeader); + UInt32 pktTotalLength = pktHeaderLength + bufferLength; + + pktHeader.headerLength = pktHeaderLength >> kVMBusPacketSizeShift; + pktHeader.totalLength = pktTotalLength >> kVMBusPacketSizeShift; + // - if (commandSleeping) { - commandSleeping = false; - commandGate->commandWakeup(&commandLock); + // Copy header, data, padding, and index to this packet. + // + MSGDBG("Packet type %u, flags %u, trans %llu, header length %u, total length %u", + pktHeader.type, pktHeader.flags, pktHeader.transactionId, + pktHeaderLength, pktTotalLength); + + HyperVVMBusDeviceRequest req; + if (responseBuffer != NULL) { + req.isSleeping = true; + req.lock = IOLockAlloc(); + req.responseData = responseBuffer; + req.responseDataLength = responseBufferLength; + req.transactionId = transactionId; + addPacketRequest(&req); + } - } else { - if (childInterruptSource != NULL) { - childInterruptSource->interruptOccurred(0, 0, 0); + IOReturn status = commandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &HyperVVMBusDevice::writeRawPacketGated), + &pktHeader, &pktHeaderLength, buffer, &bufferLength); + + if (responseBuffer != NULL) { + if (status == kIOReturnSuccess) { + sleepPacketRequest(&req); + } else { + wakeTransaction(transactionId); } } + return status; } -IOReturn HyperVVMBusDevice::doRequestGated(HyperVVMBusDeviceRequest *request) { +IOReturn HyperVVMBusDevice::nextPacketAvailableGated(VMBusPacketType *type, UInt32 *packetHeaderLength, UInt32 *packetTotalLength) { // - // If there is data to send, send it first. + // No data to read. // - if (request->sendData != NULL && request->sendDataLength != 0) { - // - // Create packet header. - // Sizes are represented as 8 byte units. - // - VMBusPacketHeader *pktHeader; - VMBusPacketHeader pktHeaderStack; - UInt32 pktHeaderLength; - - // - // In-band packets have a fixed length. - // GPA direct packets and other buffer packets have a variable length. - // - if (request->sendPacketType == kVMBusPacketTypeDataUsingGPADirect) { - if (request->multiPageBuffer != NULL) { - pktHeader = (VMBusPacketHeader*) request->multiPageBuffer; - pktHeaderLength = request->multiPageBufferLength; - - request->multiPageBuffer->reserved = 0; - request->multiPageBuffer->rangeCount = 1; - } else { - return kIOReturnBadArgument; - } - - } else { - pktHeader = &pktHeaderStack; - pktHeaderLength = sizeof (pktHeaderStack); - } - pktHeader->type = request->sendPacketType; - pktHeader->flags = request->responseRequired ? kVMBusPacketResponseRequired : 0; - pktHeader->transactionId = request->transactionId; - - UInt32 pktTotalLength = pktHeaderLength + request->sendDataLength; - UInt32 pktTotalLengthAligned = HV_PACKETALIGN(pktTotalLength); - - UInt32 writeIndexOld = txBuffer->writeIndex; - UInt32 writeIndexNew = writeIndexOld; - UInt64 writeIndexShifted = ((UInt64)writeIndexOld) << 32; - - // - // Ensure there is space for the packet. - // - // We cannot end up with read index == write index after the write, as that would indicate an empty buffer. - // - if (getAvailableTxSpace() <= pktTotalLengthAligned) { - SYSLOG("Packet is too large for buffer (TXR: %X, TXW: %X)", txBuffer->readIndex, txBuffer->writeIndex); - return kIOReturnNoResources; - } - - pktHeader->headerLength = pktHeaderLength >> kVMBusPacketSizeShift; - pktHeader->totalLength = pktTotalLength >> kVMBusPacketSizeShift; - - // - // Copy header, data, padding, and index to this packet. - // - // DBGLOG("Packet type %u, flags %u, header length %u, total length %u, pad %u", - // pktHeader->type, pktHeader->flags, pktHeaderLength, pktTotalLength, pktTotalLengthAligned - pktTotalLength); - writeIndexNew = copyPacketDataToRingBuffer(writeIndexNew, pktHeader, pktHeaderLength); - writeIndexNew = copyPacketDataToRingBuffer(writeIndexNew, request->sendData, request->sendDataLength); - writeIndexNew = zeroPacketDataToRingBuffer(writeIndexNew, pktTotalLengthAligned - pktTotalLength); - writeIndexNew = copyPacketDataToRingBuffer(writeIndexNew, &writeIndexShifted, sizeof (writeIndexShifted)); - // DBGLOG("TX read index %X, new TX write index %X", txBuffer->readIndex, writeIndexNew); - - // - // Update write index and notify host if needed. - // - //DBGLOG("TX imask %X rx imask %X, channel ID %u", txBuffer->interruptMask, rxBuffer->interruptMask, channelId); - txBuffer->writeIndex = writeIndexNew; - if (txBuffer->interruptMask == 0) { - vmbusProvider->signalVMBusChannel(channelId); - } - //DBGLOG("TX read index %X, new TX write index %X", txBuffer->readIndex, txBuffer->writeIndex); - - // - // Wait for a response if a response is expected. - // - if (request->responseData != NULL) { - commandSleeping = true; - commandGate->commandSleep(&commandLock); - DBGLOG("Waking up for response"); - } + if (rxBuffer->readIndex == rxBuffer->writeIndex) { + return kIOReturnNotFound; } + VMBusPacketHeader pktHeader; + copyPacketDataFromRingBuffer(rxBuffer->readIndex, sizeof (VMBusPacketHeader), &pktHeader, sizeof (VMBusPacketHeader)); + MSGDBG("Packet type %X, header size %X, total size %X", + pktHeader.type, pktHeader.headerLength << kVMBusPacketSizeShift, pktHeader.totalLength << kVMBusPacketSizeShift); + + if (type != NULL) { + *type = pktHeader.type; + } + if (packetHeaderLength != NULL) { + *packetHeaderLength = pktHeader.headerLength << kVMBusPacketSizeShift; + } + if (packetTotalLength != NULL) { + *packetTotalLength = pktHeader.totalLength << kVMBusPacketSizeShift; + } + + return kIOReturnSuccess; +} + +IOReturn HyperVVMBusDevice::readRawPacketGated(void *header, UInt32 *headerLength, void *buffer, UInt32 *bufferLength) { // - // Process response. + // No data to read. // - if (request->responseData != NULL) { - // - // No data to read. - // - if (rxBuffer->readIndex == rxBuffer->writeIndex) { - request->responseDataLength = 0; - return kIOReturnSuccess; - } - - // - // Read packet header. - // - UInt32 readIndexNew = rxBuffer->readIndex; - VMBusPacketHeader pktHeader; - - readIndexNew = copyPacketDataFromRingBuffer(readIndexNew, sizeof (VMBusPacketHeader), &pktHeader, sizeof (VMBusPacketHeader)); - - UInt32 packetHeaderLength = pktHeader.headerLength << kVMBusPacketSizeShift; - UInt32 packetTotalLength = pktHeader.totalLength << kVMBusPacketSizeShift; - UInt32 packetDataLength = packetTotalLength - packetHeaderLength; - //DBGLOG("Packet type %u, flags %u, trans %u, header length %u, total length %u", pktHeader.type, pktHeader.flags, pktHeader.transactionId, packetHeaderLength, packetTotalLength); - //DBGLOG("RX read index %X, RX write index %X", rxBuffer->readIndex, rxBuffer->writeIndex); - - UInt32 actualReadLength = packetDataLength; - if (request->responseDataLength < packetDataLength) { - DBGLOG("Buffer too small, %u < %u", request->responseDataLength, packetDataLength); - - if (!request->ignoreLargePackets) { - request->responseDataLength = packetDataLength; - return kIOReturnMessageTooLarge; - } else { - actualReadLength = request->responseDataLength; - } - } + if (rxBuffer->readIndex == rxBuffer->writeIndex) { + return kIOReturnNotFound; + } + + // + // Read packet header. + // + VMBusPacketHeader pktHeader; + copyPacketDataFromRingBuffer(rxBuffer->readIndex, sizeof (VMBusPacketHeader), &pktHeader, sizeof (VMBusPacketHeader)); - readIndexNew = copyPacketDataFromRingBuffer(readIndexNew, packetDataLength, request->responseData, actualReadLength); - request->responseDataLength = actualReadLength; - - UInt64 readIndexShifted; - readIndexNew = copyPacketDataFromRingBuffer(readIndexNew, sizeof (readIndexShifted), &readIndexShifted, sizeof (readIndexShifted)); - - rxBuffer->readIndex = readIndexNew; - // DBGLOG("New RX read index %X, RX write index %X", rxBuffer->readIndex, rxBuffer->writeIndex); - - // - // If there is more data to be read, and we returned from an interrupt, raise child interrupt. - // - if (request->sendData != NULL && rxBuffer->readIndex != rxBuffer->writeIndex) { - if (childInterruptSource != NULL) { - childInterruptSource->interruptOccurred(0, 0, 0); - } - } + UInt32 packetTotalLength = pktHeader.totalLength << kVMBusPacketSizeShift; + MSGDBG("RAW packet type %u, flags %u, trans %llu, header length %u, total length %u", pktHeader.type, pktHeader.flags, + pktHeader.transactionId, pktHeader.headerLength << kVMBusPacketSizeShift, packetTotalLength); + MSGDBG("RAW old RX read index %X, RX write index %X", rxBuffer->readIndex, rxBuffer->writeIndex); + + UInt32 packetDataLength = headerLength != NULL ? packetTotalLength - *headerLength : packetTotalLength; + if (*bufferLength < packetDataLength) { + MSGDBG("RAW buffer too small, %u < %u", *bufferLength, packetDataLength); + return kIOReturnNoResources; + } + + // + // Read raw packet. + // + UInt32 readIndexNew = rxBuffer->readIndex; + if (headerLength != NULL) { + readIndexNew = copyPacketDataFromRingBuffer(readIndexNew, *headerLength, header, *headerLength); + } + readIndexNew = copyPacketDataFromRingBuffer(readIndexNew, packetDataLength, buffer, packetDataLength); + + UInt64 readIndexShifted; + readIndexNew = copyPacketDataFromRingBuffer(readIndexNew, sizeof (readIndexShifted), &readIndexShifted, sizeof (readIndexShifted)); + + rxBuffer->readIndex = readIndexNew; + MSGDBG("RAW new RX read index %X, RX write index %X", rxBuffer->readIndex, rxBuffer->writeIndex); + return kIOReturnSuccess; +} + +IOReturn HyperVVMBusDevice::writeRawPacketGated(void *header, UInt32 *headerLength, void *buffer, UInt32 *bufferLength) { + UInt32 pktHeaderLength = headerLength != NULL ? *headerLength : 0; + UInt32 pktTotalLength = pktHeaderLength + *bufferLength; + UInt32 pktTotalLengthAligned = HV_PACKETALIGN(pktTotalLength); + + UInt32 writeIndexOld = txBuffer->writeIndex; + UInt32 writeIndexNew = writeIndexOld; + UInt64 writeIndexShifted = ((UInt64)writeIndexOld) << 32; + + // + // Ensure there is space for the packet. + // + // We cannot end up with read index == write index after the write, as that would indicate an empty buffer. + // + if (getAvailableTxSpace() <= pktTotalLengthAligned) { + SYSLOG("RAW packet is too large for buffer (TXR: %X, TXW: %X)", txBuffer->readIndex, txBuffer->writeIndex); + return kIOReturnNoResources; } + // + // Copy header, data, padding, and index to this packet. + // + MSGDBG("RAW packet header length %u, total length %u, pad %u", pktHeaderLength, pktTotalLength, pktTotalLengthAligned - pktTotalLength); + if (header != NULL && headerLength != NULL) { + writeIndexNew = copyPacketDataToRingBuffer(writeIndexNew, header, *headerLength); + } + writeIndexNew = copyPacketDataToRingBuffer(writeIndexNew, buffer, *bufferLength); + writeIndexNew = zeroPacketDataToRingBuffer(writeIndexNew, pktTotalLengthAligned - pktTotalLength); + writeIndexNew = copyPacketDataToRingBuffer(writeIndexNew, &writeIndexShifted, sizeof (writeIndexShifted)); + MSGDBG("RAW TX read index %X, new TX write index %X", txBuffer->readIndex, writeIndexNew); + + // + // Update write index and notify Hyper-V if needed. + // + MSGDBG("RAW TX imask %X, RX imask %X, channel ID %u", txBuffer->interruptMask, rxBuffer->interruptMask, channelId); + txBuffer->writeIndex = writeIndexNew; + if (txBuffer->interruptMask == 0) { + vmbusProvider->signalVMBusChannel(channelId); + } + MSGDBG("RAW TX read index %X, new TX write index %X", txBuffer->readIndex, txBuffer->writeIndex); return kIOReturnSuccess; } @@ -239,7 +212,7 @@ UInt32 HyperVVMBusDevice::copyPacketDataFromRingBuffer(UInt32 readIndex, UInt32 // if (dataLength > rxBufferSize - readIndex) { UInt32 fragmentLength = rxBufferSize - readIndex; - DBGLOG("RX wraparound by %u bytes", fragmentLength); + MSGDBG("RX wraparound by %u bytes", fragmentLength); memcpy(data, &rxBuffer->buffer[readIndex], fragmentLength); memcpy((UInt8*) data + fragmentLength, rxBuffer->buffer, dataLength - fragmentLength); } else { @@ -249,13 +222,17 @@ UInt32 HyperVVMBusDevice::copyPacketDataFromRingBuffer(UInt32 readIndex, UInt32 return (readIndex + readLength) % rxBufferSize; } +UInt32 HyperVVMBusDevice::seekPacketDataFromRingBuffer(UInt32 readIndex, UInt32 readLength) { + return (readIndex + readLength) % rxBufferSize; +} + UInt32 HyperVVMBusDevice::copyPacketDataToRingBuffer(UInt32 writeIndex, void *data, UInt32 length) { // // Check for wraparound. // if (length > txBufferSize - writeIndex) { UInt32 fragmentLength = txBufferSize - writeIndex; - DBGLOG("TX wraparound by %u bytes", fragmentLength); + MSGDBG("TX wraparound by %u bytes", fragmentLength); memcpy(&txBuffer->buffer[writeIndex], data, fragmentLength); memcpy(txBuffer->buffer, (UInt8*) data + fragmentLength, length - fragmentLength); } else { @@ -271,7 +248,7 @@ UInt32 HyperVVMBusDevice::zeroPacketDataToRingBuffer(UInt32 writeIndex, UInt32 l // if (length > txBufferSize - writeIndex) { UInt32 fragmentLength = txBufferSize - writeIndex; - DBGLOG("TX wraparound by %u bytes", fragmentLength); + MSGDBG("TX wraparound by %u bytes", fragmentLength); memset(&txBuffer->buffer[writeIndex], 0, fragmentLength); memset(txBuffer->buffer, 0, length - fragmentLength); } else { @@ -280,3 +257,25 @@ UInt32 HyperVVMBusDevice::zeroPacketDataToRingBuffer(UInt32 writeIndex, UInt32 l return (writeIndex + length) % txBufferSize; } + +void HyperVVMBusDevice::addPacketRequest(HyperVVMBusDeviceRequest *vmbusRequest) { + IOLockLock(vmbusRequestsLock); + if (vmbusRequests == NULL) { + vmbusRequests = vmbusRequest; + vmbusRequests->next = NULL; + } else { + vmbusRequest->next = vmbusRequests; + vmbusRequests = vmbusRequest; + } + IOLockUnlock(vmbusRequestsLock); +} + +void HyperVVMBusDevice::sleepPacketRequest(HyperVVMBusDeviceRequest *vmbusRequest) { + MSGDBG("Sleeping transaction %u", vmbusRequest->transactionId); + IOLockLock(vmbusRequest->lock); + while (vmbusRequest->isSleeping) { + IOLockSleep(vmbusRequest->lock, &vmbusRequest->isSleeping, THREAD_INTERRUPTIBLE); + } + IOLockUnlock(vmbusRequest->lock); + MSGDBG("Woken transaction %u after sleep", vmbusRequest->transactionId); +}