Skip to content

Commit 2ba5ff4

Browse files
committed
Storage: Add support for synthetic IDE controller on Gen1 VMs
1 parent ea8656d commit 2ba5ff4

File tree

7 files changed

+81
-40
lines changed

7 files changed

+81
-40
lines changed

Changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ MacHyperVSupport Changelog
22
============================
33
#### v0.9.8
44
- Added constants for macOS 15 support
5+
- Fixed completion flag not being added for storage commands
6+
- Added support for the synthetic IDE controller on Gen1 VMs
57

68
#### v0.9.7
79
- Fixed disks on a passed-in PCI device not being usable

MacHyperVSupport/Info.plist

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,25 @@
195195
<key>IOUserClientClass</key>
196196
<string>HyperVShutdownUserClient</string>
197197
</dict>
198-
<key>HyperVStorage</key>
198+
<key>HyperVStorageIDE</key>
199+
<dict>
200+
<key>CFBundleIdentifier</key>
201+
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
202+
<key>HVType</key>
203+
<string>32412632-86cb-44a2-9b5c-50d1417354f5</string>
204+
<key>IOClass</key>
205+
<string>HyperVStorage</string>
206+
<key>IOProviderClass</key>
207+
<string>HyperVVMBusDevice</string>
208+
<key>Protocol Characteristics</key>
209+
<dict>
210+
<key>Physical Interconnect</key>
211+
<string>ATA</string>
212+
<key>Physical Interconnect Location</key>
213+
<string>Internal</string>
214+
</dict>
215+
</dict>
216+
<key>HyperVStorageSCSI</key>
199217
<dict>
200218
<key>CFBundleIdentifier</key>
201219
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>

MacHyperVSupport/Storage/HyperVStorage.cpp

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,9 @@ bool HyperVStorage::DoesHBASupportSCSIParallelFeature(SCSIParallelFeature theFea
145145

146146
bool HyperVStorage::InitializeTargetForID(SCSITargetIdentifier targetID) {
147147
//
148-
// Ensure target ID is under maximum.
148+
// Ensure target ID is under maximum LUN count.
149149
//
150-
return (targetID < kHyperVStorageMaxTargets);
150+
return (targetID < _maxLuns);
151151
}
152152

153153
void HyperVStorage::HandleInterruptRequest() {
@@ -161,11 +161,12 @@ void HyperVStorage::HandleInterruptRequest() {
161161
}
162162

163163
SCSIInitiatorIdentifier HyperVStorage::ReportInitiatorIdentifier() {
164-
return kHyperVStorageMaxTargets;
164+
return _maxLuns;
165165
}
166166

167167
SCSIDeviceIdentifier HyperVStorage::ReportHighestSupportedDeviceID() {
168-
return kHyperVStorageMaxTargets - 1;
168+
// Each Hyper-V LUN is treated as a separate target under macOS.
169+
return _maxLuns - 1;
169170
}
170171

171172
UInt32 HyperVStorage::ReportMaximumTaskCount() {
@@ -202,7 +203,8 @@ bool HyperVStorage::InitializeDMASpecification(IODMACommand *command) {
202203
}
203204

204205
SCSILogicalUnitNumber HyperVStorage::ReportHBAHighestLogicalUnitNumber() {
205-
return kHyperVStorageMaxLuns - 1;
206+
// Report max LUN of 0 to macOS.
207+
return 0;
206208
}
207209

208210
SCSIServiceResponse HyperVStorage::AbortTaskRequest(SCSITargetIdentifier theT, SCSILogicalUnitNumber theL, SCSITaggedTaskIdentifier theQ) {
@@ -249,21 +251,22 @@ SCSIServiceResponse HyperVStorage::ProcessParallelTask(SCSIParallelTaskIdentifie
249251
}
250252

251253
if (GetLogicalUnitNumber(parallelRequest) != 0) {
252-
HVDBGLOG("LUN is non-zero");
254+
HVSYSLOG("LUN is non-zero");
253255
return kSCSIServiceResponse_FUNCTION_REJECTED;
254256
}
255257

256258
//
257259
// Create SCSI command execution packet.
260+
// Each LUN is represented in macOS as a separate target, use that target ID for the LUN here.
258261
//
259262
packet.operation = kHyperVStoragePacketOperationExecuteSRB;
260263
packet.flags = kHyperVStoragePacketFlagRequestCompletion;
261264

262-
packet.scsiRequest.targetID = 0;
263-
packet.scsiRequest.lun = GetTargetIdentifier(parallelRequest);
264-
packet.scsiRequest.senseInfoLength = _senseBufferSize;
265+
packet.scsiRequest.targetID = _targetId;
266+
packet.scsiRequest.lun = GetTargetIdentifier(parallelRequest);
265267
packet.scsiRequest.win8Extension.srbFlags |= 0x00000008;
266-
packet.scsiRequest.length = sizeof (packet.scsiRequest); // TODO
268+
packet.scsiRequest.length = sizeof (packet.scsiRequest) - _packetSizeDelta;
269+
packet.scsiRequest.senseInfoLength = _senseBufferSize;
267270

268271
//
269272
// Determine data direction flags.
@@ -285,7 +288,7 @@ SCSIServiceResponse HyperVStorage::ProcessParallelTask(SCSIParallelTaskIdentifie
285288
break;
286289

287290
default:
288-
HVDBGLOG("Bad data direction 0x%X", dataDirection);
291+
HVSYSLOG("Bad data direction 0x%X", dataDirection);
289292
return kSCSIServiceResponse_FUNCTION_REJECTED;
290293
}
291294
HVDATADBGLOG("Sending command to LUN %u (direction %X) with request %p", packet.scsiRequest.lun,
@@ -309,7 +312,7 @@ SCSIServiceResponse HyperVStorage::ProcessParallelTask(SCSIParallelTaskIdentifie
309312
if (dataDirection != kSCSIDataTransfer_NoDataTransfer) {
310313
status = prepareDataTransfer(parallelRequest, &pagePacket, &pagePacketLength);
311314
if (status != kIOReturnSuccess) {
312-
HVDBGLOG("Failed to prepare data transfer with status 0x%X", status);
315+
HVSYSLOG("Failed to prepare data transfer with status 0x%X", status);
313316
return kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
314317
}
315318

@@ -318,13 +321,13 @@ SCSIServiceResponse HyperVStorage::ProcessParallelTask(SCSIParallelTaskIdentifie
318321
pagePacket, pagePacketLength, nullptr, 0,
319322
(UInt64)parallelRequest);
320323
if (status != kIOReturnSuccess) {
321-
HVDBGLOG("Failed to send data SCSI packet with status 0x%X", status);
324+
HVSYSLOG("Failed to send data SCSI packet with status 0x%X", status);
322325
return kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
323326
}
324327
} else {
325328
status = _hvDevice->writeInbandPacketWithTransactionId(&packet, sizeof (packet) - _packetSizeDelta, (UInt64)parallelRequest, true);
326329
if (status != kIOReturnSuccess) {
327-
HVDBGLOG("Failed to send non-data SCSI packet with status 0x%X", status);
330+
HVSYSLOG("Failed to send non-data SCSI packet with status 0x%X", status);
328331
return kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
329332
}
330333
}

MacHyperVSupport/Storage/HyperVStorage.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ class HyperVStorage : public IOSCSIParallelInterfaceController {
3535
UInt32 _protocolVersion = 0;
3636
UInt32 _senseBufferSize = 0;
3737
UInt32 _packetSizeDelta = 0;
38+
bool _isIDE = false;
39+
UInt32 _maxLuns = kHyperVStorageMaxLunsSCSI;
40+
UInt8 _targetId = 0;
3841

3942
bool _subChannelsSupported = false;
4043
UInt16 _maxSubChannels = 0;

MacHyperVSupport/Storage/HyperVStoragePrivate.cpp

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ void HyperVStorage::setHBAInfo() {
186186
propString->release();
187187
}
188188

189-
propString = OSString::withCString(kHyperVStorageProduct);
189+
propString = OSString::withCString(_isIDE ? kHyperVStorageProductIDE : kHyperVStorageProductSCSI);
190190
if (propString != nullptr) {
191191
SetHBAProperty(kIOPropertyProductNameKey, propString);
192192
propString->release();
@@ -208,6 +208,18 @@ void HyperVStorage::setHBAInfo() {
208208
IOReturn HyperVStorage::connectStorage() {
209209
IOReturn status;
210210
HyperVStoragePacket storPkt;
211+
212+
//
213+
// Check if we are actually an IDE controller.
214+
// IDE supports one device only, and requires the target to be non-zero when communicating with Hyper-V.
215+
//
216+
_isIDE = (strcmp(_hvDevice->getTypeIdString(), kHyperVStorageGuidIDE)) == 0;
217+
if (_isIDE) {
218+
UInt8 *instanceId = (UInt8*)_hvDevice->getInstanceId();
219+
_maxLuns = kHyperVStorageMaxLunsIDE;
220+
_targetId = (instanceId[5] << 8) | instanceId[4];
221+
HVDBGLOG("Storage controller is IDE, using target ID %u", _targetId);
222+
}
211223

212224
//
213225
// Begin controller initialization.
@@ -285,43 +297,42 @@ IOReturn HyperVStorage::connectStorage() {
285297

286298
bool HyperVStorage::checkSCSIDiskPresent(UInt8 diskId) {
287299
IOReturn status;
288-
HyperVStoragePacket packet = { };
300+
HyperVStoragePacket storPkt = { };
289301

290302
//
291303
// Prepare SCSI request packet and flags.
292304
//
293-
packet.operation = kHyperVStoragePacketOperationExecuteSRB;
294-
packet.flags = kHyperVStoragePacketFlagRequestCompletion;
305+
storPkt.operation = kHyperVStoragePacketOperationExecuteSRB;
295306

296-
packet.scsiRequest.targetID = 0;
297-
packet.scsiRequest.lun = diskId;
298-
packet.scsiRequest.win8Extension.srbFlags |= 0x00000008;
299-
packet.scsiRequest.length = sizeof (packet.scsiRequest);
300-
packet.scsiRequest.senseInfoLength = _senseBufferSize;
301-
packet.scsiRequest.dataIn = kHyperVStorageSCSIRequestTypeUnknown;
307+
storPkt.scsiRequest.targetID = _targetId;
308+
storPkt.scsiRequest.lun = diskId;
309+
storPkt.scsiRequest.win8Extension.srbFlags |= 0x00000008;
310+
storPkt.scsiRequest.length = sizeof (storPkt.scsiRequest) - _packetSizeDelta;
311+
storPkt.scsiRequest.senseInfoLength = _senseBufferSize;
312+
storPkt.scsiRequest.dataIn = kHyperVStorageSCSIRequestTypeUnknown;
302313

303314
//
304315
// Set CDB to TEST UNIT READY command.
305316
//
306-
packet.scsiRequest.cdb[0] = kSCSICmd_TEST_UNIT_READY;
307-
packet.scsiRequest.cdb[1] = 0x00;
308-
packet.scsiRequest.cdb[2] = 0x00;
309-
packet.scsiRequest.cdb[3] = 0x00;
310-
packet.scsiRequest.cdb[4] = 0x00;
311-
packet.scsiRequest.cdb[5] = 0x00;
312-
packet.scsiRequest.cdbLength = 6;
317+
storPkt.scsiRequest.cdb[0] = kSCSICmd_TEST_UNIT_READY;
318+
storPkt.scsiRequest.cdb[1] = 0x00;
319+
storPkt.scsiRequest.cdb[2] = 0x00;
320+
storPkt.scsiRequest.cdb[3] = 0x00;
321+
storPkt.scsiRequest.cdb[4] = 0x00;
322+
storPkt.scsiRequest.cdb[5] = 0x00;
323+
storPkt.scsiRequest.cdbLength = 6;
313324

314325
//
315326
// Send SCSI packet and check result to see if disk is present.
316327
//
317-
status = _hvDevice->writeInbandPacket(&packet, sizeof (packet) - _packetSizeDelta, true, &packet, sizeof (packet));
328+
status = sendStorageCommand(&storPkt, false);
318329
if (status != kIOReturnSuccess) {
319330
HVDBGLOG("Failed to send TEST UNIT READY SCSI packet with status 0x%X", status);
320331
return false;
321332
}
322333

323-
HVDBGLOG("Disk %u status: 0x%X SRB status: 0x%X", diskId, packet.scsiRequest.scsiStatus, packet.scsiRequest.srbStatus);
324-
return packet.scsiRequest.srbStatus == kHyperVSRBStatusSuccess;
334+
HVDBGLOG("Disk %u status: 0x%X SRB status: 0x%X", diskId, storPkt.scsiRequest.scsiStatus, storPkt.scsiRequest.srbStatus);
335+
return storPkt.scsiRequest.srbStatus == kHyperVSRBStatusSuccess;
325336
}
326337

327338
void HyperVStorage::startDiskEnumeration() {
@@ -333,9 +344,9 @@ void HyperVStorage::startDiskEnumeration() {
333344
}
334345

335346
void HyperVStorage::scanSCSIDisks() {
336-
HVDBGLOG("Starting disk scan of %u disks", kHyperVStorageMaxTargets);
347+
HVDBGLOG("Starting disk scan of %u disks", _maxLuns);
337348

338-
for (UInt32 lun = 0; lun < kHyperVStorageMaxTargets; lun++) {
349+
for (UInt32 lun = 0; lun < _maxLuns; lun++) {
339350
if (checkSCSIDiskPresent(lun)) {
340351
if (GetTargetForID(lun) == nullptr) {
341352
HVDBGLOG("Disk %u is newly added", lun);

MacHyperVSupport/Storage/HyperVStorageRegs.hpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,13 @@
1818
#define kHyperVStorageMaxBufferLengthPadding 0x14
1919

2020
#define kHyperVStorageVendor "Microsoft"
21-
#define kHyperVStorageProduct "Hyper-V SCSI Controller"
21+
#define kHyperVStorageProductSCSI "Hyper-V SCSI Controller"
22+
#define kHyperVStorageProductIDE "Hyper-V IDE Controller"
23+
#define kHyperVStorageGuidSCSI "ba6163d9-04a1-4d29-b605-72e2ffb1dc7f"
24+
#define kHyperVStorageGuidIDE "32412632-86cb-44a2-9b5c-50d1417354f5"
2225

23-
#define kHyperVStorageMaxTargets 64
24-
#define kHyperVStorageMaxLuns 1
26+
#define kHyperVStorageMaxLunsSCSI 64
27+
#define kHyperVStorageMaxLunsIDE 1
2528

2629
#define kHyperVStorageSegmentSize PAGE_SIZE
2730
#define kHyperVStorageSegmentAlignment 0xFFFFFFFFFFFFF000ULL

MacHyperVSupport/VMBusDevice/HyperVVMBusDevice.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ class HyperVVMBusDevice : public IOService {
168168
IOReturn freeGPADLBuffer(UInt32 gpadlHandle);
169169
UInt32 getChannelId() { return _channelId; }
170170
uuid_t* getInstanceId() { return &_instanceId; }
171+
char* getTypeIdString() { return _typeId; }
171172

172173
//
173174
// Ring buffer.

0 commit comments

Comments
 (0)