Skip to content

Commit

Permalink
Storage: Cleanup and implement proper termination
Browse files Browse the repository at this point in the history
  • Loading branch information
Goldfish64 committed Oct 2, 2022
1 parent c719c5a commit a5d906f
Show file tree
Hide file tree
Showing 6 changed files with 276 additions and 207 deletions.
5 changes: 0 additions & 5 deletions MacHyperVSupport/Controller/HyperV.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -363,11 +363,6 @@ static inline int sync_test_and_clear_bit(long nr, volatile void *addr)
return oldbit;
}

template <class T, size_t N>
constexpr size_t ARRAY_SIZE(const T (&array)[N]) {
return N;
}

//
// Pages are assumed to be 4KB
//
Expand Down
2 changes: 1 addition & 1 deletion MacHyperVSupport/Keyboard/HyperVKeyboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ bool HyperVKeyboard::start(IOService *provider) {
}

//
// Open VMBus channel.
// Open VMBus channel and connect to keyboard.
//
status = _hvDevice->openVMBusChannel(kHyperVKeyboardRingBufferSize, kHyperVKeyboardRingBufferSize);
if (status != kIOReturnSuccess) {
Expand Down
2 changes: 1 addition & 1 deletion MacHyperVSupport/PCIBridge/HyperVPCIBridgePrivate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ bool HyperVPCIBridge::negotiateProtocolVersion() {
pktVersion.header.type = kHyperVPCIBridgeMessageTypeQueryProtocolVersion;

// Attempt to find newest version host can support.
for (int i = 0; i < ARRAY_SIZE(usableVersions); i++) {
for (int i = 0; i < arrsize(usableVersions); i++) {
pktVersion.version = usableVersions[i];
HVDBGLOG("Attempting to use version 0x%X", pktVersion.version);

Expand Down
242 changes: 99 additions & 143 deletions MacHyperVSupport/Storage/HyperVStorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,158 +9,114 @@

OSDefineMetaClassAndStructors(HyperVStorage, super);

//
// Hyper-V storage protocol list.
//
static const HyperVStorageProtocol storageProtocols[] = {
{
kHyperVStorageVersionWin10,
kHyperVStoragePostWin7SenseBufferSize,
0
},
{
kHyperVStorageVersionWin8_1,
kHyperVStoragePostWin7SenseBufferSize,
0
},
{
kHyperVStorageVersionWin8,
kHyperVStoragePostWin7SenseBufferSize,
0
},
{
kHyperVStorageVersionWin7,
kHyperVStoragePreWin8SenseBufferSize,
sizeof (HyperVStorageSCSIRequestWin8Extension)
},
{
kHyperVStorageVersionWin2008,
kHyperVStoragePreWin8SenseBufferSize,
sizeof (HyperVStorageSCSIRequestWin8Extension)
},
};

bool HyperVStorage::InitializeController() {
HyperVStoragePacket packet;

if (HVCheckOffArg()) {
return false;
}

bool result = false;
IOReturn status;

//
// Get parent VMBus device object.
//
_hvDevice = OSDynamicCast(HyperVVMBusDevice, getProvider());
if (_hvDevice == NULL) {
if (_hvDevice == nullptr) {
HVSYSLOG("Provider is not HyperVVMBusDevice");
return false;
}
_hvDevice->retain();

HVCheckDebugArgs();

HVDBGLOG("Initializing Hyper-V Synthetic Storage controller");

HVDBGLOG("Initializing Hyper-V Synthetic Storage");

if (HVCheckOffArg()) {
HVSYSLOG("Disabling Hyper-V Synthetic Storage due to boot arg");
OSSafeReleaseNULL(_hvDevice);
return false;
}

//
// Assume we are on an older host and take off the Windows 8 extensions by default.
//
packetSizeDelta = sizeof (HyperVStorageSCSIRequestWin8Extension);

//
// Configure interrupt.
// macOS 10.4 always configures the interrupt in the superclass, do
// not configure the interrupt ourselves in that case.
//
_hvDevice->installPacketActions(this, OSMemberFunctionCast(HyperVVMBusDevice::PacketReadyAction, this, &HyperVStorage::handlePacket), OSMemberFunctionCast(HyperVVMBusDevice::WakePacketAction, this, &HyperVStorage::wakePacketHandler), PAGE_SIZE, getKernelVersion() >= KernelVersion::Leopard);
_packetSizeDelta = sizeof (HyperVStorageSCSIRequestWin8Extension);

do {
//
// Install packet handler.
// macOS 10.4 always configures the interrupt in the superclass, do
// not configure the interrupt ourselves in that case.
//
status = _hvDevice->installPacketActions(this, OSMemberFunctionCast(HyperVVMBusDevice::PacketReadyAction, this, &HyperVStorage::handlePacket),
OSMemberFunctionCast(HyperVVMBusDevice::WakePacketAction, this, &HyperVStorage::wakePacketHandler),
PAGE_SIZE, getKernelVersion() >= KernelVersion::Leopard);
if (status != kIOReturnSuccess) {
HVSYSLOG("Failed to install packet handler with status 0x%X", status);
break;
}

#if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_5
if (getKernelVersion() < KernelVersion::Leopard) {
EnableInterrupt();
}
if (getKernelVersion() < KernelVersion::Leopard) {
EnableInterrupt();
}
#endif

//
// Configure the channel.
//
if (_hvDevice->openVMBusChannel(kHyperVStorageRingBufferSize, kHyperVStorageRingBufferSize) != kIOReturnSuccess) {
return false;
}

//
// Begin controller initialization.
//
bzero(&packet, sizeof (packet));
packet.operation = kHyperVStoragePacketOperationBeginInitialization;
if (sendStorageCommand(&packet, true) != kIOReturnSuccess) {
return false;
}

//
// Negotiate protocol version.
//
for (UInt32 i = 0; i < ARRAY_SIZE(storageProtocols); i++) {
bzero(&packet, sizeof (packet));
packet.operation = kHyperVStoragePacketOperationQueryProtocolVersion;
packet.protocolVersion.majorMinor = storageProtocols[i].protocolVersion;
packet.protocolVersion.revision = 0; // Revision is zero for non-Windows.

if (sendStorageCommand(&packet, false) != kIOReturnSuccess) {
return false;

//
// Open VMBus channel and connect to storage.
//
status = _hvDevice->openVMBusChannel(kHyperVStorageRingBufferSize, kHyperVStorageRingBufferSize);
if (status != kIOReturnSuccess) {
HVSYSLOG("Failed to open VMBus channel with status 0x%X", status);
break;
}

status = connectStorage();
if (status != kIOReturnSuccess) {
HVSYSLOG("Failed to connect to storage device with status 0x%X", status);
break;
}

//
// Initialize segments used for DMA.
//
_segs64 = (IODMACommand::Segment64*) IOMalloc(sizeof (IODMACommand::Segment64) * _maxPageSegments);
if (_segs64 == nullptr) {
HVSYSLOG("Failed to initialize segments");
break;
}

//
// A success means this protocol version is acceptable.
// Populate HBA properties and create disk enumeration thread.
//
if (packet.status == 0) {
protocolVersion = storageProtocols[i].protocolVersion;
senseBufferSize = storageProtocols[i].senseBufferSize;
packetSizeDelta = storageProtocols[i].packetSizeDelta;
HVDBGLOG("SCSI protocol version: 0x%X, sense buffer size: %u", protocolVersion, senseBufferSize);
setHBAInfo();
_scanSCSIDiskThread = thread_call_allocate(OSMemberFunctionCast(thread_call_func_t, this, &HyperVStorage::scanSCSIDisks), this);
if (_scanSCSIDiskThread == nullptr) {
HVSYSLOG("Failed to create disk enumeration thread");
break;
}
}

if (packet.status != 0) {
return false;
}

//
// Query controller properties.
//
bzero(&packet, sizeof (packet));
packet.operation = kHyperVStoragePacketOperationQueryProperties;
if (sendStorageCommand(&packet, true) != kIOReturnSuccess) {
return false;
}

subChannelsSupported = packet.storageChannelProperties.flags & kHyperVStorageFlagSupportsMultiChannel;
maxSubChannels = packet.storageChannelProperties.maxChannelCount;
maxTransferBytes = packet.storageChannelProperties.maxTransferBytes;
maxPageSegments = maxTransferBytes / PAGE_SIZE;
HVDBGLOG("Multi channel supported: %s, max sub channels: %u, max transfer bytes: %u (%u segments)",
subChannelsSupported ? "yes" : "no", maxSubChannels, maxTransferBytes, maxPageSegments);

//
// Complete initialization.
//
bzero(&packet, sizeof (packet));
packet.operation = kHyperVStoragePacketOperationEndInitialization;
if (sendStorageCommand(&packet, true) != kIOReturnSuccess) {
return false;
}

segs64 = (IODMACommand::Segment64*) IOMalloc(sizeof (IODMACommand::Segment64) * maxPageSegments);

//
// Populate HBA properties.
//
setHBAInfo();

scanSCSIDiskThread = thread_call_allocate(OSMemberFunctionCast(thread_call_func_t, this, &HyperVStorage::scanSCSIDisks), this);

HVSYSLOG("Initialized Hyper-V Synthetic Storage controller");
return true;
result = true;
HVDBGLOG("Initialized Hyper-V Synthetic Storage");
} while (false);

if (!result) {
TerminateController();
}
return result;
}

void HyperVStorage::TerminateController() {
HVDBGLOG("Controller is terminated");
HVDBGLOG("Stopping Hyper-V Synthetic Storage");

if (_scanSCSIDiskThread != nullptr) {
thread_call_free(_scanSCSIDiskThread);
}

if (_hvDevice != nullptr) {
_hvDevice->closeVMBusChannel();
_hvDevice->uninstallPacketActions();
OSSafeReleaseNULL(_hvDevice);
}

if (_segs64 != nullptr) {
IOFree(_segs64, sizeof (IODMACommand::Segment64) * _maxPageSegments);
}
}

bool HyperVStorage::StartController() {
Expand Down Expand Up @@ -219,7 +175,7 @@ UInt32 HyperVStorage::ReportMaximumTaskCount() {

UInt32 HyperVStorage::ReportHBASpecificTaskDataSize() {
HVDBGLOG("start");
return sizeof (VMBusPacketMultiPageBuffer) + (sizeof (UInt64) * maxPageSegments); //32 * 4096;
return sizeof (VMBusPacketMultiPageBuffer) + (sizeof (UInt64) * _maxPageSegments); //32 * 4096;
}

UInt32 HyperVStorage::ReportHBASpecificDeviceDataSize() {
Expand All @@ -242,7 +198,7 @@ bool HyperVStorage::InitializeDMASpecification(IODMACommand *command) {
// Hyper-V requires page-sized segments due to its use of page numbers.
//
return command->initWithSpecification(kIODMACommandOutputHost64, kHyperVStorageSegmentBits, kHyperVStorageSegmentSize,
IODMACommand::kMapped, maxTransferBytes, kHyperVStorageSegmentSize);
IODMACommand::kMapped, _maxTransferBytes, kHyperVStorageSegmentSize);
}

SCSILogicalUnitNumber HyperVStorage::ReportHBAHighestLogicalUnitNumber() {
Expand Down Expand Up @@ -305,7 +261,7 @@ SCSIServiceResponse HyperVStorage::ProcessParallelTask(SCSIParallelTaskIdentifie

packet.scsiRequest.targetID = 0;
packet.scsiRequest.lun = GetTargetIdentifier(parallelRequest);
packet.scsiRequest.senseInfoLength = senseBufferSize;
packet.scsiRequest.senseInfoLength = _senseBufferSize;
packet.scsiRequest.win8Extension.srbFlags |= 0x00000008;
packet.scsiRequest.length = sizeof (packet.scsiRequest); // TODO

Expand Down Expand Up @@ -358,15 +314,15 @@ SCSIServiceResponse HyperVStorage::ProcessParallelTask(SCSIParallelTaskIdentifie
}

packet.scsiRequest.dataTransferLength = (UInt32) GetRequestedDataTransferCount(parallelRequest);
status = _hvDevice->writeGPADirectMultiPagePacket(&packet, sizeof (packet) - packetSizeDelta, true,
status = _hvDevice->writeGPADirectMultiPagePacket(&packet, sizeof (packet) - _packetSizeDelta, true,
pagePacket, pagePacketLength, nullptr, 0,
(UInt64)parallelRequest);
if (status != kIOReturnSuccess) {
HVDBGLOG("Failed to send data SCSI packet with status 0x%X", status);
return kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
}
} else {
status = _hvDevice->writeInbandPacketWithTransactionId(&packet, sizeof (packet) - packetSizeDelta, (UInt64)parallelRequest, true);
status = _hvDevice->writeInbandPacketWithTransactionId(&packet, sizeof (packet) - _packetSizeDelta, (UInt64)parallelRequest, true);
if (status != kIOReturnSuccess) {
HVDBGLOG("Failed to send non-data SCSI packet with status 0x%X", status);
return kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
Expand All @@ -379,38 +335,38 @@ SCSIServiceResponse HyperVStorage::ProcessParallelTask(SCSIParallelTaskIdentifie

void HyperVStorage::ReportHBAConstraints(OSDictionary *constraints) {
OSNumber *osNumber;

//
// Populate HBA requirements and limitations.
//
osNumber = OSNumber::withNumber(maxPageSegments, 32);
if (osNumber != NULL) {
osNumber = OSNumber::withNumber(_maxPageSegments, 32);
if (osNumber != nullptr) {
constraints->setObject(kIOMaximumSegmentCountReadKey, osNumber);
constraints->setObject(kIOMaximumSegmentCountWriteKey, osNumber);
osNumber->release();
}

osNumber = OSNumber::withNumber(kHyperVStorageSegmentSize, 32);
if (osNumber != NULL) {
if (osNumber != nullptr) {
constraints->setObject(kIOMaximumSegmentByteCountReadKey, osNumber);
constraints->setObject(kIOMaximumSegmentByteCountWriteKey, osNumber);
osNumber->release();
}

osNumber = OSNumber::withNumber(kHyperVStorageSegmentAlignment, 64);
if (osNumber != NULL) {
if (osNumber != nullptr) {
constraints->setObject(kIOMinimumHBADataAlignmentMaskKey, osNumber);
osNumber->release();
}

osNumber = OSNumber::withNumber(kHyperVStorageSegmentBits, 32);
if (osNumber != NULL) {
if (osNumber != nullptr) {
constraints->setObject(kIOMaximumSegmentAddressableBitCountKey, osNumber);
osNumber->release();
}

osNumber = OSNumber::withNumber(4, 32);
if (osNumber != NULL) {
if (osNumber != nullptr) {
constraints->setObject(kIOMinimumSegmentAlignmentByteCountKey, osNumber);
osNumber->release();
}
Expand Down
Loading

0 comments on commit a5d906f

Please sign in to comment.