Hi Kevin,
I'm afraid I have to disagree with one detail. Please give me two seconds to enjoy the moment.
You know, the Big Five for Life: getting married, having children, planting a tree, building a house and correcting an Apple DTS engineer. :-)
All right, moment over. Seriously, though: thank you for revisiting this so openly, investigating the alternatives and trying to move the underlying issues forward internally.
Your revised overall conclusion appears to be correct: SCSIControllerDriverKit is viable for this use case, at least on the system we have now tested.
One detail may need clarification, though: the descriptor API path.
In the public DriverKit 25.5 headers, UserMapHBAData does not return a memory descriptor. Its only output is the unique controller task identifier:
UserMapHBAData(
uint32_t *uniqueTaskID
);
The descriptor itself is retrieved later through:
UserGetDataBuffer(
targetID,
controllerTaskID,
&buffer
);
when called from UserProcessParallelTask, using the request's fControllerTaskIdentifier.
Perhaps this is the mechanism you meant. UserMapHBAData establishes the task identity, and UserGetDataBuffer subsequently retrieves the task's IOBufferMemoryDescriptor.
As we are still testing, we would like to keep the conclusions narrow and clearly distinguish between what we have measured and what remains open.
Hardware: Intel Mac
macOS: 26.5.2
Xcode: 26.5
DriverKit SDK: 25.5
IOClass:
IOUserSCSIParallelInterfaceController
IOProviderClass:
IOUserResources
There is no PCI or Thunderbolt provider. The signed DEXT does not request the PCI transport entitlement.
The extension was installed and activated successfully. The controller became registered, matched and active.
The following lifecycle and reporting steps completed:
UserInitializeController
UserReportHBAConstraints
UserStartController
UserInitializeTargetForID
The framework then submitted real SCSI commands through UserProcessParallelTask.
Inside those callbacks:
UserGetDataBuffer
-> kIOReturnSuccess
-> non-null IOBufferMemoryDescriptor
GetAddressRange
-> kIOReturnSuccess
-> nonzero DEXT-local address
-> expected transfer length
We initially performed a descriptor-only probe without touching the buffer. That succeeded.
We then performed a strictly bounded standard INQUIRY write using the address returned by GetAddressRange.
The observed sequence was:
INQUIRY allocation length 6
-> wrote 6 bytes
-> completed GOOD
INQUIRY allocation length 36
-> wrote 36 bytes
-> completed GOOD
macOS consumed the initial six bytes and subsequently requested the complete 36-byte INQUIRY response.
The resulting IORegistry properties contained the exact data written by the DEXT:
Vendor: AVIONTEX
Product: iSCSI4NAS VIRT
Revision: 0001
The native storage stack then advanced through:
IOSCSIParallelInterfaceDevice
IOSCSITargetDevice
IOSCSIHierarchicalLogicalUnit
IOSCSIPeripheralDeviceType00
IOBlockStorageServices
IOBlockStorageDriver
It is now issuing subsequent discovery commands, including READ CAPACITY(10).
There were no panics and no DEXT crashes. The DriverKit crash counter remained at zero.
For this specific configuration, we have therefore demonstrated:
IOUserResources
-> IOUserSCSIParallelInterfaceController
-> UserProcessParallelTask
-> UserGetDataBuffer
-> IOBufferMemoryDescriptor
-> GetAddressRange
-> bounded byte write
-> successful SCSI completion
-> data consumed and interpreted by the macOS storage stack
This confirms your revised conclusion that the SCSI path is possible today.
It also shows that the original fBufferIOVMAddr limitation does not prevent the DEXT from accessing the request data, because UserGetDataBuffer provides a separate descriptor-based path. We still do not use or dereference fBufferIOVMAddr.
Your warning about the single-page DMA limitation remains open.
Our successful transfers were only:
6 bytes
36 bytes
These results therefore neither confirm nor disprove a single-page limitation. We have not yet tested a cross-page request, Scatter/Gather I/O or real READ/WRITE commands.
Could you clarify the exact configuration Apple currently recommends to enforce the single-page restriction?
In particular, should a virtual HBA use:
maximum segment count read = 1
maximum segment count write = 1
maximum segment byte count read = runtime system page size
maximum segment byte count write = runtime system page size
maxTransferSize = runtime system page size
Should any additional alignment constraint be applied?
And should "one page" always use the runtime system page size rather than a fixed 4096-byte value, particularly when validating the same implementation across Intel and Apple Silicon?
We also agree with your assessment of BlockStorageDeviceDriverKit.
Its current DoAsyncReadWrite interface exposes only the DMA address and does not provide an equivalent to UserGetDataBuffer. We will file the two requested Feedback Assistant reports:
- A descriptor-only read/write path for
BlockStorageDeviceDriverKit - A documented descriptor-only, multi-page I/O path for
SCSIControllerDriverKit
We will post both feedback numbers here once they have been submitted.
We will also retain the FSKit/raw-DiskImage design as a fallback and potential performance comparison. For now, however, the SCSI path has progressed far enough that we would like to finish validating it before changing architectures.
For completeness, the results above currently apply only to:
Intel
macOS 26.5.2
DriverKit 25.5
development signing
Developer Mode
SIP disabled
The following remain separate validation steps:
Apple Silicon
SIP-enabled standard security
distribution provisioning
multi-page I/O
real READ/WRITE traffic
long-running stability
This is an early status report, not a final result. We will keep testing and keep you posted as we go, including the feedback numbers once both reports are filed.
One more thing, and I mean it. When we started, we did not see the SCSI option at all. In hindsight we were probably too fixated on BlockStorageDeviceDriverKit, because from where we sit that is the more logical family for this product. It took both the entitlement process and your pointers to send us back to the SCSI DEXT and look at it properly.
So thank you again. Your correction and the pointer back toward the SCSI path appear to have saved the architecture.