Post

Replies

Boosts

Views

Activity

Reply to [DEXT Migration Issue] IOUserSCSIParallelInterfaceController fails to handle low-level I/O from `diskutil`
Hi Kevin, We conducted a test in our legacy KEXT environment, and the results indicate a behavioral difference between the KEXT and DriverKit frameworks in handling I/O requests. Test Scenario (KEXT Environment): We executed a 10MB raw I/O write command to an rdisk node: sudo dd if=/dev/zero of=/dev/rdisk7 bs=10m count=1 Observations: This command is a single 10MB write request. Our KEXT logs show that the driver did not receive one 10MB request, but instead received 20 sequential WRITE(10) commands. Upon analysis, we confirmed that the size of each request was 512KB. Evidence 1: DataLength The DataLength for each request in the log is 0x00080000, which is 524,288 bytes (512KB). Evidence 2: CDB Content In the CDB of the first request, the transfer length is 0x0400 blocks (1024 blocks). 1024 blocks * 512 bytes/block = 512KB. The LBAs of subsequent requests were sequential (LBA 0, LBA 1024, LBA 2048, ...). Partial log for reference: [AME_DAS_SCSI_Prepare] --- [KEXT FINAL DUMP] AME_SCSI_REQUEST_DAS packet for BufferID 46 --- [AME_DAS_SCSI_Prepare] Opcode (from CDB[0]): 0x2a (WRITE (10)) [AME_DAS_SCSI_Prepare] CDB (hex): 2a 00 00 00 00 00 00 04 00 00 [AME_DAS_SCSI_Prepare] DataLength: 0x00080000 ... [AME_DAS_SCSI_Prepare] --- [KEXT FINAL DUMP] AME_SCSI_REQUEST_DAS packet for BufferID 47 --- [AME_DAS_SCSI_Prepare] Opcode (from CDB[0]): 0x2a (WRITE (10)) [AME_DAS_SCSI_Prepare] CDB (hex): 2a 00 00 00 04 00 00 04 00 00 [AME_DAS_SCSI_Prepare] DataLength: 0x00080000 Our Conclusion: This test shows that in the KEXT environment, the I/O Kit storage stack splits a large raw I/O request that exceeds an internal limit into multiple smaller chunks (512KB in this case) before sending them to the driver. This is different from the behavior we observed in our DEXT. In the DEXT, after we declared a maxTransferSize of 64KB via UserGetDMASpecification, any rdisk request larger than this limit is rejected by the framework, not split. Our Updated Questions: Is this behavior in DriverKit an intentional design change? Is the request-splitting functionality of I/O Kit no longer a part of the DriverKit framework, with the expectation that DEXT developers should now implement the logic for splitting large raw I/O requests? Thank you for your clarification. Best, Charles
Topic: App & System Services SubTopic: Drivers Tags:
Nov ’25
Reply to [DEXT Migration Issue] IOUserSCSIParallelInterfaceController fails to handle low-level I/O from `diskutil`
Hi Kevin, We've encountered an I/O issue with our storage DEXT and were hoping for your expert insight. Our storage DEXT works correctly when handling I/O requests directed to the buffered device node (/dev/disk5). However, when I/O requests target the raw device node (/dev/rdisk5), the operation fails with an Input/output error if the transfer size is 128KB or larger. In contrast, all transfers of 64KB or smaller succeed consistently. Detailed Debugging Steps & Observations: After a full reboot of macOS to ensure a clean test environment, we performed a series of tests exclusively on the /dev/rdisk5 node. A 64KB transfer succeeds: $ sudo dd if=/dev/zero of=/dev/rdisk5 bs=64k count=1 1+0 records in 1+0 records out 65536 bytes transferred in 0.003461 secs (18935568 bytes/sec) A 128KB transfer consistently fails: $ sudo dd if=/dev/zero of=/dev/rdisk5 bs=128k count=1 dd: /dev/rdisk5: Input/output error 1+0 records in 0+0 records out 0 bytes transferred in 0.000247 secs (0 bytes/sec) Log Analysis: When the 64KB transfer succeeds, our DEXT's core I/O handling logic is triggered, and we can see detailed logs for the WRITE(10) command being prepared, including the CDB content and a reported SGCount of 1. [UserProcessParallelTask_Impl] fRequestedTransferCount = 65536 [UserProcessParallelTask_Impl] CDB = 0x2a 0x00 ... 0x00 0x80 0x00 ... [AME_DAS_SCSI_Prepare] > Opcode (from CDB[0]): 0x2a : WRITE (10) [AME_DAS_SCSI_Prepare] > DataLength: 0x00010000 [AME_DAS_SCSI_Prepare] > SGCount: 1 When the 128KB transfer fails, we see absolutely no logs from our core I/O handling function. The I/O request seemingly disappears before it reaches the core part of our DEXT that prepares the SCSI command. Related Code Implementation: We have implemented the UserGetDMASpecification method in our DEXT to report our hardware's DMA capability limits to the system. The current code, where we explicitly set maxTransferSize to 64KB, is shown below. kern_return_t IMPL (DRV_MAIN_CLASS_NAME, UserGetDMASpecification) { // The maximum size of a single DMA transfer. 64KB is a safe value. *maxTransferSize = 64 * 1024; // Standard 4-byte alignment. *alignment = 4; // We explicitly tell macOS that our hardware can ONLY handle // 32-bit physical addresses. *numAddressBits = 32; *segmentType = kDMAOutputSegmentHost64; Log("Reporting DMA Constraints: maxTransferSize=%llu, alignment=%u, numAddressBits=%u", *maxTransferSize, *alignment, *numAddressBits); return kIOReturnSuccess; } This implementation appears to be consistent with the 64KB threshold behavior we are observing. Our Questions: Based on our test results and the UserGetDMASpecification implementation, what is the expected behavior of the DriverKit framework when an I/O request larger than 64KB is sent to the rdisk node? Is the maxTransferSize reported by UserGetDMASpecification the reason why the I/O request is rejected before reaching our DEXT's core logic? What is the recommended "best practice" within the DriverKit framework for handling raw I/O requests that are larger than maxTransferSize? Should we implement the request splitting logic within the DEXT ourselves, or should I/O Kit automatically handle splitting the request for us as long as we correctly report our limits? Thank you again! Charles
Topic: App & System Services SubTopic: Drivers Tags:
Oct ’25
Reply to DEXT receives zero-filled buffer from DMA, despite firmware confirming data write
We've implemented the override for UserReportHBAConstraints. However, our logs confirm it is never called by the framework. We are confused by the official documentation for UserReportHBAConstraints which states: Subclasses must call this method from the dext before UserInitializeController returns. What does this mean in practice? Are we expected to call this method manually?
Topic: App & System Services SubTopic: Drivers Tags:
Oct ’25
Reply to DEXT receives zero-filled buffer from DMA, despite firmware confirming data write
The correct approach for setting these properties is documented in the SCSIControllerDriverKit.iig file. Instead of calling IOPCIDevice::SetProperties, the intended method is to override the UserReportHBAConstraints virtual function in our DEXT's main class. This also answers the second part of my question: we found that all the necessary property keys (e.g., kIOMaximumSegmentCountReadKey) are officially defined in the <DriverKit/IOKitKeys.h> header, which we were able to include directly. The solution is to: 1 - Declare the override for UserReportHBAConstraints in the .iig file. 2 - #include <DriverKit/IOKitKeys.h> in the .cpp file. 3 - Implement the function to populate the dictionary with the hardware's DMA constraints using the official keys. Best regards, Charles
Topic: App & System Services SubTopic: Drivers Tags:
Oct ’25
Reply to DEXT receives zero-filled buffer from DMA, despite firmware confirming data write
Hello Kevin, Thank you for your analysis. We have reviewed our implementation and can confirm that we are not calling UserGetDataBuffer in our UserProcessParallelTask_Impl. Our DEXT fully adheres to the official recommendation you described, using the framework-provided fBufferIOVMAddr for the DMA operation. Since that was not the issue, we performed a deeper comparison between our working KEXT and the DEXT. We discovered that in its InitializeController() method, the KEXT explicitly declares its DMA capabilities to the I/O Kit storage stack by setting several properties. The code is as follows: // In KEXT's InitializeController() setProperty(kIOMaximumSegmentCountReadKey, (UInt64)MAX_IO_SG, 64); setProperty(kIOMaximumSegmentCountWriteKey, (UInt64)MAX_IO_SG, 64); setProperty(kIOMaximumByteCountReadKey, (UInt64)MAX_IO_LENGTH, 64); setProperty(kIOMaximumByteCountWriteKey, (UInt64)MAX_IO_LENGTH, 64); setProperty(kIOPropertyPhysicalInterconnectLocationKey, kIOPropertyExternalKey); It's possible that the absence of these property settings in our DEXT is what causes the SCSI framework to fail when preparing the IOMMU mapping for the INQUIRY command's buffer, leading to the silent DMA failure. Our question now is what the correct method is for implementing this in DriverKit. We found the IOPCIDevice::SetProperties(OSDictionary *) method, and we have also seen these property keys mentioned in DriverKit-related documentation. However, the key constants themselves (e.g., kIOMaximumSegmentCountReadKey) do not appear to be defined in any available DriverKit SDK header, which prevents our code from compiling. Could you please advise us on the official, recommended way for a DEXT to set these storage-specific properties for the framework? Are we missing a specific header file, or is the expected approach to manually define these key strings in our source code? Thank you again for your valuable assistance. Charles
Topic: App & System Services SubTopic: Drivers Tags:
Oct ’25
Reply to DriverKit DEXT Logs Only Once, Subsequent Logs Are Lost Despite Normal Functionality
Hi Kevin, Thank you for your reply. It gave us a clearer understanding of how the DEXT Log mechanism works. You were right: the logs were indeed always there; our filtering methods were just missing them. Here is a summary of how we solved the "silent log" issue: We tried using sysdiagnose. However, for our specific need to capture IOUserClient interaction logs in real-time, the amount of data in the report was too large to be effectively analyzed. The core of the problem was that our DEXT generates a continuous and massive amount of logs, which drowned out the logs from the UserClient methods. Therefore, we created a new, dedicated logging macro, uLog(), for the exclusive use of our UserClient. Next, we modified Log.h to add a compile-time switch (#ifdef), which allows us to completely compile-out the Log() macro from the core engine at compile time, leaving only the new uLog(). All the noise from the DEXT disappeared, and the logs from ExternalMethod appeared clearly and reliably. We have now switched to using Console.app with a precise filter to observe the real-time log stream. Thank you again for your guidance. Charles
Topic: App & System Services SubTopic: Drivers Tags:
Oct ’25
Reply to Xcode Signing Fails: Provisioning Profile "doesn't match" com.apple.developer.driverkit.userclient-access entitlement
Hi Kevin, Thank you for the suggestions. We have conducted a test of your third proposal regarding the IOKit User Client Class Temporary Exception. Our test procedure was as follows: We started from a known-good baseline commit where our DEXT loads correctly and the/dev/diskX node appears (though it reports 0 bytes, which is a separate issue we are debugging). We made only one change: we updated our App's .entitlements file to include the temporary exception. For absolute clarity, the exact content of the .entitlements file used for this test was: <dict> <key>com.apple.security.temporary-exception.iokit-user-client-class</key> <array> <string>IOUserUserClient</string> </array> <key>com.apple.security.app-sandbox</key> <false/> <key>com.apple.developer.system-extension.install</key> <true/> </dict> After a full clean, build, and reboot, the IOServiceGetMatchingService("DriverKitAcxxx") call still fails at runtime, returning IO_OBJECT_NULL. This result seems to indicate that the temporary exception entitlement is not sufficient to grant our app visibility of the active IOKit service on macOS. Best, Charles
Topic: App & System Services SubTopic: Drivers Tags:
Oct ’25
Reply to Xcode Signing Fails: Provisioning Profile "doesn't match" com.apple.developer.driverkit.userclient-access entitlement
Hi Kevin, Thank you for the detailed response. Here is a summary of our tests based on the solutions you proposed: Regarding the userclient-access Entitlement: We discovered a human error in our initial entitlement request (we had entered the wrong Bundle ID). We have submitted a new, corrected request with the proper DEXT Bundle ID and are currently awaiting its approval. Regarding the Temporary Solutions: To continue development while waiting, we tried your other suggestions, but are still blocked. App Sandbox: We confirm that com.apple.security.app-sandbox is set to false in our app's entitlements. This alone did not solve the issue. "IOKit User Client Class Temporary Exception": We implemented your third suggestion precisely. Our app's .entitlements file for this test was configured exactly as follows: <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>com.apple.security.temporary-exception.iokit-user-client-class</key> <array> <string>IOUserUserClient</string> </array> <key>com.apple.security.app-sandbox</key> <false/> <key>com.apple.developer.system-extension.install</key> <true/> <key>com.apple.security.get-task-allow</key> <true/> </dict> </plist> Result: Failure. Even with this entitlement configuration, the call to IOServiceGetMatchingService("DriverKitAcxxx") still fails at runtime and returns IO_OBJECT_NULL. IOKit Registry Confirms the Service IS Active: Although IOServiceGetMatchingService fails, the ioreg command provides definitive proof that our DEXT service is active, registered, and correctly configured. Here is the output from ioreg -l | grep Acxxx: +-o DriverKitAcxxx <class IOUserSCSIParallelInterfaceController, ... registered, matched, active, ...> | "IOUserClass" = "DriverKitAcxxx" | "IOUserServerName" = "com.accusys.Acxxx.driver" ... | "UserClientProperties" = {"IOClass"="IOUserUserClient","IOUserClass"="DriverKitAcxxxUserClient"} This proves that the DriverKitAcxxx service exists and should be discoverable in the IOKit Registry. Summary & Question: We are in a situation where the IOKit Registry verifiably contains our active service, but our app (even with the temporary exception entitlement) is unable to find it via the standard IOServiceGetMatchingService API. Given this hard evidence from ioreg, are there any other mechanisms or sandbox restrictions that could be preventing our app from "seeing" this active IOKit service? Thank you again for your continued support. Best Regards, Charles
Topic: App & System Services SubTopic: Drivers Tags:
Oct ’25
Reply to Xcode Signing Fails: Provisioning Profile "doesn't match" com.apple.developer.driverkit.userclient-access entitlement
Hi Smith, Following your suggestion, we inspected the .provisionprofile file. We used QuickLook on the latest Xcode-managed profile for our app (com.accusys.Acxxx.app). The Entitlements dictionary inside this profile contains the following: com.apple.developer.driverkit.userclient-access: com.accusys.Acxxx.app It shows that the profile generated by Apple's backend incorrectly contains the App's own bundle ID for the userclient-access value, not our DEXT's bundle ID (com.accusys.Acxxx.driver). This should be a human error on our part during the initial entitlement request. We incorrectly entered our App's bundle ID (.app) into the UserClient Bundle IDs field on the request form, when it should have been our DEXT's bundle ID (.driver). We are now preparing to have our Account Holder submit a new, corrected request to Apple, ensuring that the UserClient Bundle IDs field contains the correct DEXT bundle ID this time. We are hopeful that once this new, correct request is approved, the provisioning issue will be resolved. Best regards, Charles
Topic: App & System Services SubTopic: Drivers Tags:
Oct ’25
Reply to Xcode Signing Fails: Provisioning Profile "doesn't match" com.apple.developer.driverkit.userclient-access entitlement
Hi Smith, To add more context to my previous reply, especially regarding the userclient-access entitlement: The main reason we switched our testing focus to the communicates-with-drivers entitlement is because the userclient-access path is completely blocked at build time for us, exactly as you suspected. Here is a detailed breakdown of what happens when we use userclient-access: Entitlement Configuration: We configure our DriverKitSampleApp.entitlements with the correct, specific bundle ID of our DEXT: <key>com.apple.developer.driverkit.userclient-access</key> <array> <string>com.accusys.Acxxx.driver</string> </array> Immediate Build Failure: As soon as we try to build the app with this entitlement, Xcode's signing process fails instantly. This happens with both "Automatic Signing" and a manually created Provisioning Profile. The Error Message: The error is always the same: Provisioning profile "..." doesn't match the entitlements file's value for the com.apple.developer.driverkit.userclient-access entitlement. The Proof (from QuickLook): Following your advice, we inspected the generated provisioning profile. It confirms that the profile issued by Apple's backend incorrectly sets the value for userclient-access to the app's own bundle ID (...Acxxx.app) instead of the DEXT's (...Acxxx.driver). That is why we are currently focusing our efforts on trying to get the alternative communicates-with-drivers path to work, which unfortunately also seems to be broken at runtime on macOS Sequoia. Charles
Topic: App & System Services SubTopic: Drivers Tags:
Oct ’25
Reply to Xcode Signing Fails: Provisioning Profile "doesn't match" com.apple.developer.driverkit.userclient-access entitlement
Hi Smith, Thank you so much for your excellent suggestions. I've followed your advice and have some definitive results to share. Regarding your first suggestion (Inspecting the Provisioning Profile): You were absolutely right. I inspected the latest Xcode-managed .provisionprofile for my app (com.accusys.Acxxx.app) after a complete cleanup. The profile does not contain the correct value for the userclient-access entitlement. Here is what the profile's Entitlements dictionary contains: com.apple.developer.driverkit.userclient-access: com.accusys.Acxxx.app com.apple.application-identifier: K3TDMD9Y6B.com.accusys.Acxxx.app keychain-access-groups: K3TDMD9Y6B.* As you can see, the value for userclient-access is incorrectly set to the app's own bundle ID, not the DEXT's (com.accusys.Acxxx.driver). Regarding your second suggestion (Using allow-any-userclient-access): I also tried the alternative strategy you proposed, which is a setup we had already been testing. Here is the configuration: App Entitlements (DriverKitSampleApp.entitlements): We use the generic communicates-with-drivers entitlement. <key>com.apple.developer.driverkit.communicates-with-drivers</key><true/> <key>com.apple.security.app-sandbox</key><false/> <key>com.apple.developer.system-extension.install</key><true/> DEXT Entitlements (AcxxxDriver.entitlements): We enable allow-any-userclient-access as you suggested. <key>com.apple.developer.driverkit</key> <true/> <key>com.apple.developer.driverkit.allow-any-userclient-access</key> <true/> <key>com.apple.developer.driverkit.family.scsicontroller</key> <true/> Result: With this configuration, the app builds successfully, but it still fails at runtime. The IOServiceGetMatchingService("DriverKitAcxxx") call fails to find the service, and no logs from NewUserClient() are ever generated on the DEXT side. Conclusion: Your suggestions helped us confirm two critical issues on macOS Sequoia 15.7.1: The provisioning system generates an incorrect userclient-access entitlement. The communicates-with-drivers entitlement is not working as expected, as it fails to grant the app visibility of the IOKit service. Both paths for App-DEXT communication appear to be blocked. We have filed a Feedback report with these findings. Any other workarounds you could suggest would be greatly appreciated. Thanks again for your help!
Topic: App & System Services SubTopic: Drivers Tags:
Oct ’25
Reply to Implementing Hardware Interrupt Handling with InterruptOccurred in DriverKit
Hi all, I followed Modernize PCI and SCSI drivers with DriverKit and the source to implement MSI interrupts. Right now, if I first load KEXT, then completely remove it and load DEXT without rebooting the storage, DEXT interrupts work fine. But if I restart the storage, DEXT interrupts stop working. Here is the implementation for initializing MSI-based Interrupts. // MSI-based interrupt kern_return_t DRV_MAIN_CLASS_NAME::InitializeInterrupt() { // // please refer to https://developer.apple.com/forums/thread/779842 // and // https://github.com/apple-oss-distributions/IOPCIFamily/blob/main/PEX8733/PCIDriverKitPEX8733/PCIDriverKitPEX8733.cpp // Log("InitializeInterrupt() - Start"); kern_return_t result = kIOReturnSuccess; uint64_t interruptType = 0; uint32_t msiInterruptIndex = 0; // Interrupt Intialize And Setup while((result = IOInterruptDispatchSource::GetInterruptType( ivars->pciDevice, msiInterruptIndex, &interruptType)) == kIOReturnSuccess) { if ((interruptType & kIOInterruptTypePCIMessaged) != 0) break; // 0x00010000 msiInterruptIndex++; } Log("MSI index: %u, type: 0x%llx", msiInterruptIndex, interruptType); __Require_Action( result == kIOReturnSuccess, Exit, LogErr("IOInterruptDispatchSource::GetInterruptType result 0x%08x", result)); uint16_t msiControl; ivars->pciDevice->ConfigurationRead16(0x52, &msiControl); Log("MSI Control Register: 0x%04x", msiControl); // uint64_t msiOffset; result = ivars->pciDevice->FindPCICapability(kIOPCICapabilityIDMSI, 0, &msiOffset); if (result == kIOReturnSuccess) { Log("MSI Capability Offset: 0x%llx", msiOffset); } else { LogErr("Failed to find MSI capability, error: 0x%x", result); } uint32_t msiAddr; ivars->pciDevice->ConfigurationRead32(msiOffset + 0x4, &msiAddr); uint32_t msiData; ivars->pciDevice->ConfigurationRead32(msiOffset + 0xC, &msiData); Log("MSI Address: 0x%x, MSI Data: 0x%x", msiAddr, msiData); // Create interrupt queue result = IODispatchQueue::Create("InterruptQueue", kIODispatchQueueReentrant, 0, &ivars->fInterruptQueue); __Require_Action(ivars->fInterruptQueue != nullptr && result == kIOReturnSuccess, Exit, LogErr("Create InterruptQueue fail")); result = SetDispatchQueue("InterruptQueue", ivars->fInterruptQueue); __Require_Action(result == kIOReturnSuccess, Exit, LogErr("SetDispatchQueue() fail 0x%0x", result)); // Create interrupt source result = IOInterruptDispatchSource::Create(ivars->pciDevice, msiInterruptIndex, ivars->fInterruptQueue, &ivars->fInterruptSource); __Require_Action(result == kIOReturnSuccess, Exit, LogErr("IOInterruptDispatchSource fail 0x%08x", result)); result = CreateActionInterruptOccurred(sizeof(void*), &ivars->interruptOccurredAction); __Require_Action(result == kIOReturnSuccess, Exit, LogErr("CreateActionInterruptOccurred fail 0x%08x", result)); result = ivars->fInterruptSource->SetHandler(ivars->interruptOccurredAction); __Require_Action(result == kIOReturnSuccess, Exit, LogErr("SetHandler fail 0x%08x", result)); result = ivars->fInterruptSource->SetEnable(true); __Require_Action(result == kIOReturnSuccess, Exit, LogErr("interruptSource SetEnable Error 0x%08x", result)); Exit: Log("InitializeInterrupt() - End"); return result; } and InterruptOccurred() void IMPL(DRV_MAIN_CLASS_NAME, InterruptOccurred) { Log("InterruptOccurred()"); .... } and the iig file class DRV_MAIN_CLASS_NAME: public IOUserSCSIParallelInterfaceController { public: ... virtual void InterruptOccurred(OSAction* action, uint64_t count, uint64_t time) TYPE(IOInterruptDispatchSource::InterruptOccurred) //PCI Dext interrupt QUEUENAME(InterruptQueue); } Did I forget something in the InitializeInterrupt implementation? Or is there another reason for this issue? Best Regards, Charles
Topic: App & System Services SubTopic: Drivers Tags:
May ’25
Reply to macOS Hang After Implementing UserMapHBAData()
Did "UserDoesHBAPerformDeviceManagement" return "true"? Yes, it returns TRUE, and at the same time sets ivars->bUserDoesHBAPerformDeviceManagement as true. This ensures that TargetForID_Init() waits until UserDoesHBAPerformDeviceManagement() has completed before executing AsyncEventHandler(), which then calls UserCreateTargetForID(). IMPL(DRV_MAIN_CLASS_NAME, UserDoesHBAPerformDeviceManagement) { Log("UserDoesHBAPerformDeviceManagement() - Start"); bool ret = TRUE; ivars->bUserDoesHBAPerformDeviceManagement = true; Log("UserDoesHBAPerformDeviceManagement() - End"); return ret; } If AsyncEventHandler() is not called, then UserCreateTargetForID() will not be invoked by the system. Additionally, TargetForID_Init() is called by AME_Host_RAID_Ready(), that runs inside a while loop in the UserInitializeController(). while (1) { bool sts; sts = AME_Check_RAID_Ready_Status(pAMEData); bool b_device_menagement = ivars->bUserDoesHBAPerformDeviceManagement; if (sts == TRUE && ivars->bUserDoesHBAPerformDeviceManagement) { AME_Host_RAID_Ready(); // will call TargetForID_Init() to invoke AsyncEventHandler() and then call UserCreateTargetForID() ... break; } } I suspect that what's actually going on is that CreateTargetForID is still in flight, which means you tried to reenter it by calling "UserCreateTargetForID". How can I avoid re-entering UserCreateTargetForID() and ensure that CreateTargetForID() has already been executed? Best Regards, Charles
Topic: App & System Services SubTopic: Drivers Tags:
May ’25
Reply to macOS Hang After Implementing UserMapHBAData()
Following is the sequence of current implementation Start() Timer_Init() UserInitializeController() In UserInitializeController(), we loop to check whether the RAID is ready and also watch a flag to see if UserDoesHBAPerformDeviceManagement() has been called, then invoke the TargetForID_Init() to call UserCreateTargetForID() Then we proceed with: Timer_Start() ZtsTimerOccurred() UserGetDMASpecification() UserReportInitiatorIdentifier() // *identifier = 65 UserReportHighestSupportedDeviceID() // *id = 63 UserReportMaximumTaskCount() // *count = 200 UserMapHBAData() // run 200 times UserStartController() UserDoesHBAPerformDeviceManagement() UserInitializeTargetForID() // runs for Target IDs 0 through 63 // Each call is paired with UserProcessParallelTask() TargetForID_Init() // be invoked in a loop process within UserInitializeController() Inside the log of TargetForID_Init() we see: TargetForID_Init() - Start AsyncEventHandler(ivars->fAsyncEventHandler, 0) !!! ERROR : Failed at LUN 0: 0xe00002d8 !!! ... Initialize Total 64 TargetForID, 0 Successfully TargetForID_Init() - End Questions: Does UserCreateTargetForID() have to be called only after UserDoesHBAPerformDeviceManagement()? What exactly does the error message 0xe00002d8 mean in this context? I never explicitly called UserInitializeTargetForID(), so why is it being executed? Looking forward to your reply. Really appreciate it! Best Regards, Charles
Topic: App & System Services SubTopic: Drivers Tags:
May ’25
Reply to macOS Hang After Implementing UserMapHBAData()
I figured out the reason why macOS crashed — it was because I called SetDispatchQueue() before assigning a value to the queue variable. // Create and set dispatch AuxiliaryQueue ret = IODispatchQueue::Create("AuxiliaryQueue", kIODispatchQueueReentrant, // Options (e.g., allow reentrancy) 0, // Priority &ivars->fAuxQueue); if (!ivars->fAuxQueue) LogErr("Create AuxiliaryQueue fail"); __Require((kIOReturnSuccess == ret), Exit); ret = SetDispatchQueue("AuxiliaryQueue", ivars->fAuxQueue); if (kIOReturnSuccess != ret) LogErr("SetDispatchQueue AuxiliaryQueue"); __Require((kIOReturnSuccess == ret), Exit); // Create and set dispatch InterruptQueue ret = IODispatchQueue::Create("InterruptQueue", kIODispatchQueueReentrant, 0, &ivars->fIntQueue); if (!ivars->fIntQueue) LogErr("Create InterruptQueue fail"); __Require((kIOReturnSuccess == ret), Exit); // AME_IO_milliseconds_Delay(2000); // 200ms delay #if 0 // Fail to SetDispatchQueue() ret = SetDispatchQueue("InterruptQueue", ivars->fIntQueue); if (kIOReturnSuccess != ret) LogErr("SetDispatchQueue InterruptQueue"); __Require((kIOReturnSuccess == ret), Exit); #endif However, there's still an issue: the second call to SetDispatchQueue() fails, even if I add a sleep before it. UserInitializeController() - Start !!! ERROR : SetDispatchQueue InterruptQueue !!! !!! ERROR : ret = 0xe00002c2 !!! UserInitializeController() - End Additionally, at first, UserMapHBAData() wasn't working either. The logs showed the following error: UserMapHBAData() - Start !!! ERROR : Buffer allocation failed !!! !!! ERROR : ret = 0xe00002bd !!! UserMapHBAData() - End Eventually, I was able to fix it by experimenting with different parameter values: // ivars->fTaskDataSize = 64; // ivars->fTaskDataSize = 128; // ivars->fTaskDataSize = 512; ivars->fTaskDataSize = 1024; // ivars->fTaskDataSize = 4096; const uint32_t alignment = 4; // const uint32_t alignment = 0; // Pass 0 for no guaranteed alignment. ret = IOBufferMemoryDescriptor::Create(kIOMemoryDirectionOutIn, ivars->fTaskDataSize, alignment, &buffer); if (!buffer) LogErr("Buffer allocation failed!"); Log("ivars->fTaskDataSize = %llu, alignment = %d", ivars->fTaskDataSize, alignment); __Require((kIOReturnSuccess == ret), Exit); As you mentioned, the best documentation is working sample code. If Apple could provide complete, buildable source examples, it would be an immense help, both to Apple and to developers. Lastly, you said: In the case here, I would start by simply getting the I/O ‘pipeline’ working WITHOUT actually doing any I/O. How should I go about doing that, exactly? Best Regards, Charles
Topic: App & System Services SubTopic: Drivers Tags:
May ’25
Reply to [DEXT Migration Issue] IOUserSCSIParallelInterfaceController fails to handle low-level I/O from `diskutil`
Hi Kevin, We conducted a test in our legacy KEXT environment, and the results indicate a behavioral difference between the KEXT and DriverKit frameworks in handling I/O requests. Test Scenario (KEXT Environment): We executed a 10MB raw I/O write command to an rdisk node: sudo dd if=/dev/zero of=/dev/rdisk7 bs=10m count=1 Observations: This command is a single 10MB write request. Our KEXT logs show that the driver did not receive one 10MB request, but instead received 20 sequential WRITE(10) commands. Upon analysis, we confirmed that the size of each request was 512KB. Evidence 1: DataLength The DataLength for each request in the log is 0x00080000, which is 524,288 bytes (512KB). Evidence 2: CDB Content In the CDB of the first request, the transfer length is 0x0400 blocks (1024 blocks). 1024 blocks * 512 bytes/block = 512KB. The LBAs of subsequent requests were sequential (LBA 0, LBA 1024, LBA 2048, ...). Partial log for reference: [AME_DAS_SCSI_Prepare] --- [KEXT FINAL DUMP] AME_SCSI_REQUEST_DAS packet for BufferID 46 --- [AME_DAS_SCSI_Prepare] Opcode (from CDB[0]): 0x2a (WRITE (10)) [AME_DAS_SCSI_Prepare] CDB (hex): 2a 00 00 00 00 00 00 04 00 00 [AME_DAS_SCSI_Prepare] DataLength: 0x00080000 ... [AME_DAS_SCSI_Prepare] --- [KEXT FINAL DUMP] AME_SCSI_REQUEST_DAS packet for BufferID 47 --- [AME_DAS_SCSI_Prepare] Opcode (from CDB[0]): 0x2a (WRITE (10)) [AME_DAS_SCSI_Prepare] CDB (hex): 2a 00 00 00 04 00 00 04 00 00 [AME_DAS_SCSI_Prepare] DataLength: 0x00080000 Our Conclusion: This test shows that in the KEXT environment, the I/O Kit storage stack splits a large raw I/O request that exceeds an internal limit into multiple smaller chunks (512KB in this case) before sending them to the driver. This is different from the behavior we observed in our DEXT. In the DEXT, after we declared a maxTransferSize of 64KB via UserGetDMASpecification, any rdisk request larger than this limit is rejected by the framework, not split. Our Updated Questions: Is this behavior in DriverKit an intentional design change? Is the request-splitting functionality of I/O Kit no longer a part of the DriverKit framework, with the expectation that DEXT developers should now implement the logic for splitting large raw I/O requests? Thank you for your clarification. Best, Charles
Topic: App & System Services SubTopic: Drivers Tags:
Replies
Boosts
Views
Activity
Nov ’25
Reply to [DEXT Migration Issue] IOUserSCSIParallelInterfaceController fails to handle low-level I/O from `diskutil`
Hi Kevin, We've encountered an I/O issue with our storage DEXT and were hoping for your expert insight. Our storage DEXT works correctly when handling I/O requests directed to the buffered device node (/dev/disk5). However, when I/O requests target the raw device node (/dev/rdisk5), the operation fails with an Input/output error if the transfer size is 128KB or larger. In contrast, all transfers of 64KB or smaller succeed consistently. Detailed Debugging Steps & Observations: After a full reboot of macOS to ensure a clean test environment, we performed a series of tests exclusively on the /dev/rdisk5 node. A 64KB transfer succeeds: $ sudo dd if=/dev/zero of=/dev/rdisk5 bs=64k count=1 1+0 records in 1+0 records out 65536 bytes transferred in 0.003461 secs (18935568 bytes/sec) A 128KB transfer consistently fails: $ sudo dd if=/dev/zero of=/dev/rdisk5 bs=128k count=1 dd: /dev/rdisk5: Input/output error 1+0 records in 0+0 records out 0 bytes transferred in 0.000247 secs (0 bytes/sec) Log Analysis: When the 64KB transfer succeeds, our DEXT's core I/O handling logic is triggered, and we can see detailed logs for the WRITE(10) command being prepared, including the CDB content and a reported SGCount of 1. [UserProcessParallelTask_Impl] fRequestedTransferCount = 65536 [UserProcessParallelTask_Impl] CDB = 0x2a 0x00 ... 0x00 0x80 0x00 ... [AME_DAS_SCSI_Prepare] > Opcode (from CDB[0]): 0x2a : WRITE (10) [AME_DAS_SCSI_Prepare] > DataLength: 0x00010000 [AME_DAS_SCSI_Prepare] > SGCount: 1 When the 128KB transfer fails, we see absolutely no logs from our core I/O handling function. The I/O request seemingly disappears before it reaches the core part of our DEXT that prepares the SCSI command. Related Code Implementation: We have implemented the UserGetDMASpecification method in our DEXT to report our hardware's DMA capability limits to the system. The current code, where we explicitly set maxTransferSize to 64KB, is shown below. kern_return_t IMPL (DRV_MAIN_CLASS_NAME, UserGetDMASpecification) { // The maximum size of a single DMA transfer. 64KB is a safe value. *maxTransferSize = 64 * 1024; // Standard 4-byte alignment. *alignment = 4; // We explicitly tell macOS that our hardware can ONLY handle // 32-bit physical addresses. *numAddressBits = 32; *segmentType = kDMAOutputSegmentHost64; Log("Reporting DMA Constraints: maxTransferSize=%llu, alignment=%u, numAddressBits=%u", *maxTransferSize, *alignment, *numAddressBits); return kIOReturnSuccess; } This implementation appears to be consistent with the 64KB threshold behavior we are observing. Our Questions: Based on our test results and the UserGetDMASpecification implementation, what is the expected behavior of the DriverKit framework when an I/O request larger than 64KB is sent to the rdisk node? Is the maxTransferSize reported by UserGetDMASpecification the reason why the I/O request is rejected before reaching our DEXT's core logic? What is the recommended "best practice" within the DriverKit framework for handling raw I/O requests that are larger than maxTransferSize? Should we implement the request splitting logic within the DEXT ourselves, or should I/O Kit automatically handle splitting the request for us as long as we correctly report our limits? Thank you again! Charles
Topic: App & System Services SubTopic: Drivers Tags:
Replies
Boosts
Views
Activity
Oct ’25
Reply to DEXT receives zero-filled buffer from DMA, despite firmware confirming data write
We've implemented the override for UserReportHBAConstraints. However, our logs confirm it is never called by the framework. We are confused by the official documentation for UserReportHBAConstraints which states: Subclasses must call this method from the dext before UserInitializeController returns. What does this mean in practice? Are we expected to call this method manually?
Topic: App & System Services SubTopic: Drivers Tags:
Replies
Boosts
Views
Activity
Oct ’25
Reply to DEXT receives zero-filled buffer from DMA, despite firmware confirming data write
The correct approach for setting these properties is documented in the SCSIControllerDriverKit.iig file. Instead of calling IOPCIDevice::SetProperties, the intended method is to override the UserReportHBAConstraints virtual function in our DEXT's main class. This also answers the second part of my question: we found that all the necessary property keys (e.g., kIOMaximumSegmentCountReadKey) are officially defined in the <DriverKit/IOKitKeys.h> header, which we were able to include directly. The solution is to: 1 - Declare the override for UserReportHBAConstraints in the .iig file. 2 - #include <DriverKit/IOKitKeys.h> in the .cpp file. 3 - Implement the function to populate the dictionary with the hardware's DMA constraints using the official keys. Best regards, Charles
Topic: App & System Services SubTopic: Drivers Tags:
Replies
Boosts
Views
Activity
Oct ’25
Reply to DEXT receives zero-filled buffer from DMA, despite firmware confirming data write
Hello Kevin, Thank you for your analysis. We have reviewed our implementation and can confirm that we are not calling UserGetDataBuffer in our UserProcessParallelTask_Impl. Our DEXT fully adheres to the official recommendation you described, using the framework-provided fBufferIOVMAddr for the DMA operation. Since that was not the issue, we performed a deeper comparison between our working KEXT and the DEXT. We discovered that in its InitializeController() method, the KEXT explicitly declares its DMA capabilities to the I/O Kit storage stack by setting several properties. The code is as follows: // In KEXT's InitializeController() setProperty(kIOMaximumSegmentCountReadKey, (UInt64)MAX_IO_SG, 64); setProperty(kIOMaximumSegmentCountWriteKey, (UInt64)MAX_IO_SG, 64); setProperty(kIOMaximumByteCountReadKey, (UInt64)MAX_IO_LENGTH, 64); setProperty(kIOMaximumByteCountWriteKey, (UInt64)MAX_IO_LENGTH, 64); setProperty(kIOPropertyPhysicalInterconnectLocationKey, kIOPropertyExternalKey); It's possible that the absence of these property settings in our DEXT is what causes the SCSI framework to fail when preparing the IOMMU mapping for the INQUIRY command's buffer, leading to the silent DMA failure. Our question now is what the correct method is for implementing this in DriverKit. We found the IOPCIDevice::SetProperties(OSDictionary *) method, and we have also seen these property keys mentioned in DriverKit-related documentation. However, the key constants themselves (e.g., kIOMaximumSegmentCountReadKey) do not appear to be defined in any available DriverKit SDK header, which prevents our code from compiling. Could you please advise us on the official, recommended way for a DEXT to set these storage-specific properties for the framework? Are we missing a specific header file, or is the expected approach to manually define these key strings in our source code? Thank you again for your valuable assistance. Charles
Topic: App & System Services SubTopic: Drivers Tags:
Replies
Boosts
Views
Activity
Oct ’25
Reply to DriverKit DEXT Logs Only Once, Subsequent Logs Are Lost Despite Normal Functionality
Hi Kevin, Thank you for your reply. It gave us a clearer understanding of how the DEXT Log mechanism works. You were right: the logs were indeed always there; our filtering methods were just missing them. Here is a summary of how we solved the "silent log" issue: We tried using sysdiagnose. However, for our specific need to capture IOUserClient interaction logs in real-time, the amount of data in the report was too large to be effectively analyzed. The core of the problem was that our DEXT generates a continuous and massive amount of logs, which drowned out the logs from the UserClient methods. Therefore, we created a new, dedicated logging macro, uLog(), for the exclusive use of our UserClient. Next, we modified Log.h to add a compile-time switch (#ifdef), which allows us to completely compile-out the Log() macro from the core engine at compile time, leaving only the new uLog(). All the noise from the DEXT disappeared, and the logs from ExternalMethod appeared clearly and reliably. We have now switched to using Console.app with a precise filter to observe the real-time log stream. Thank you again for your guidance. Charles
Topic: App & System Services SubTopic: Drivers Tags:
Replies
Boosts
Views
Activity
Oct ’25
Reply to Xcode Signing Fails: Provisioning Profile "doesn't match" com.apple.developer.driverkit.userclient-access entitlement
Hi Kevin, Thank you for the suggestions. We have conducted a test of your third proposal regarding the IOKit User Client Class Temporary Exception. Our test procedure was as follows: We started from a known-good baseline commit where our DEXT loads correctly and the/dev/diskX node appears (though it reports 0 bytes, which is a separate issue we are debugging). We made only one change: we updated our App's .entitlements file to include the temporary exception. For absolute clarity, the exact content of the .entitlements file used for this test was: <dict> <key>com.apple.security.temporary-exception.iokit-user-client-class</key> <array> <string>IOUserUserClient</string> </array> <key>com.apple.security.app-sandbox</key> <false/> <key>com.apple.developer.system-extension.install</key> <true/> </dict> After a full clean, build, and reboot, the IOServiceGetMatchingService("DriverKitAcxxx") call still fails at runtime, returning IO_OBJECT_NULL. This result seems to indicate that the temporary exception entitlement is not sufficient to grant our app visibility of the active IOKit service on macOS. Best, Charles
Topic: App & System Services SubTopic: Drivers Tags:
Replies
Boosts
Views
Activity
Oct ’25
Reply to Xcode Signing Fails: Provisioning Profile "doesn't match" com.apple.developer.driverkit.userclient-access entitlement
Hi Kevin, Thank you for the detailed response. Here is a summary of our tests based on the solutions you proposed: Regarding the userclient-access Entitlement: We discovered a human error in our initial entitlement request (we had entered the wrong Bundle ID). We have submitted a new, corrected request with the proper DEXT Bundle ID and are currently awaiting its approval. Regarding the Temporary Solutions: To continue development while waiting, we tried your other suggestions, but are still blocked. App Sandbox: We confirm that com.apple.security.app-sandbox is set to false in our app's entitlements. This alone did not solve the issue. "IOKit User Client Class Temporary Exception": We implemented your third suggestion precisely. Our app's .entitlements file for this test was configured exactly as follows: <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>com.apple.security.temporary-exception.iokit-user-client-class</key> <array> <string>IOUserUserClient</string> </array> <key>com.apple.security.app-sandbox</key> <false/> <key>com.apple.developer.system-extension.install</key> <true/> <key>com.apple.security.get-task-allow</key> <true/> </dict> </plist> Result: Failure. Even with this entitlement configuration, the call to IOServiceGetMatchingService("DriverKitAcxxx") still fails at runtime and returns IO_OBJECT_NULL. IOKit Registry Confirms the Service IS Active: Although IOServiceGetMatchingService fails, the ioreg command provides definitive proof that our DEXT service is active, registered, and correctly configured. Here is the output from ioreg -l | grep Acxxx: +-o DriverKitAcxxx <class IOUserSCSIParallelInterfaceController, ... registered, matched, active, ...> | "IOUserClass" = "DriverKitAcxxx" | "IOUserServerName" = "com.accusys.Acxxx.driver" ... | "UserClientProperties" = {"IOClass"="IOUserUserClient","IOUserClass"="DriverKitAcxxxUserClient"} This proves that the DriverKitAcxxx service exists and should be discoverable in the IOKit Registry. Summary & Question: We are in a situation where the IOKit Registry verifiably contains our active service, but our app (even with the temporary exception entitlement) is unable to find it via the standard IOServiceGetMatchingService API. Given this hard evidence from ioreg, are there any other mechanisms or sandbox restrictions that could be preventing our app from "seeing" this active IOKit service? Thank you again for your continued support. Best Regards, Charles
Topic: App & System Services SubTopic: Drivers Tags:
Replies
Boosts
Views
Activity
Oct ’25
Reply to Xcode Signing Fails: Provisioning Profile "doesn't match" com.apple.developer.driverkit.userclient-access entitlement
Hi Smith, Following your suggestion, we inspected the .provisionprofile file. We used QuickLook on the latest Xcode-managed profile for our app (com.accusys.Acxxx.app). The Entitlements dictionary inside this profile contains the following: com.apple.developer.driverkit.userclient-access: com.accusys.Acxxx.app It shows that the profile generated by Apple's backend incorrectly contains the App's own bundle ID for the userclient-access value, not our DEXT's bundle ID (com.accusys.Acxxx.driver). This should be a human error on our part during the initial entitlement request. We incorrectly entered our App's bundle ID (.app) into the UserClient Bundle IDs field on the request form, when it should have been our DEXT's bundle ID (.driver). We are now preparing to have our Account Holder submit a new, corrected request to Apple, ensuring that the UserClient Bundle IDs field contains the correct DEXT bundle ID this time. We are hopeful that once this new, correct request is approved, the provisioning issue will be resolved. Best regards, Charles
Topic: App & System Services SubTopic: Drivers Tags:
Replies
Boosts
Views
Activity
Oct ’25
Reply to Xcode Signing Fails: Provisioning Profile "doesn't match" com.apple.developer.driverkit.userclient-access entitlement
Hi Smith, To add more context to my previous reply, especially regarding the userclient-access entitlement: The main reason we switched our testing focus to the communicates-with-drivers entitlement is because the userclient-access path is completely blocked at build time for us, exactly as you suspected. Here is a detailed breakdown of what happens when we use userclient-access: Entitlement Configuration: We configure our DriverKitSampleApp.entitlements with the correct, specific bundle ID of our DEXT: <key>com.apple.developer.driverkit.userclient-access</key> <array> <string>com.accusys.Acxxx.driver</string> </array> Immediate Build Failure: As soon as we try to build the app with this entitlement, Xcode's signing process fails instantly. This happens with both "Automatic Signing" and a manually created Provisioning Profile. The Error Message: The error is always the same: Provisioning profile "..." doesn't match the entitlements file's value for the com.apple.developer.driverkit.userclient-access entitlement. The Proof (from QuickLook): Following your advice, we inspected the generated provisioning profile. It confirms that the profile issued by Apple's backend incorrectly sets the value for userclient-access to the app's own bundle ID (...Acxxx.app) instead of the DEXT's (...Acxxx.driver). That is why we are currently focusing our efforts on trying to get the alternative communicates-with-drivers path to work, which unfortunately also seems to be broken at runtime on macOS Sequoia. Charles
Topic: App & System Services SubTopic: Drivers Tags:
Replies
Boosts
Views
Activity
Oct ’25
Reply to Xcode Signing Fails: Provisioning Profile "doesn't match" com.apple.developer.driverkit.userclient-access entitlement
Hi Smith, Thank you so much for your excellent suggestions. I've followed your advice and have some definitive results to share. Regarding your first suggestion (Inspecting the Provisioning Profile): You were absolutely right. I inspected the latest Xcode-managed .provisionprofile for my app (com.accusys.Acxxx.app) after a complete cleanup. The profile does not contain the correct value for the userclient-access entitlement. Here is what the profile's Entitlements dictionary contains: com.apple.developer.driverkit.userclient-access: com.accusys.Acxxx.app com.apple.application-identifier: K3TDMD9Y6B.com.accusys.Acxxx.app keychain-access-groups: K3TDMD9Y6B.* As you can see, the value for userclient-access is incorrectly set to the app's own bundle ID, not the DEXT's (com.accusys.Acxxx.driver). Regarding your second suggestion (Using allow-any-userclient-access): I also tried the alternative strategy you proposed, which is a setup we had already been testing. Here is the configuration: App Entitlements (DriverKitSampleApp.entitlements): We use the generic communicates-with-drivers entitlement. <key>com.apple.developer.driverkit.communicates-with-drivers</key><true/> <key>com.apple.security.app-sandbox</key><false/> <key>com.apple.developer.system-extension.install</key><true/> DEXT Entitlements (AcxxxDriver.entitlements): We enable allow-any-userclient-access as you suggested. <key>com.apple.developer.driverkit</key> <true/> <key>com.apple.developer.driverkit.allow-any-userclient-access</key> <true/> <key>com.apple.developer.driverkit.family.scsicontroller</key> <true/> Result: With this configuration, the app builds successfully, but it still fails at runtime. The IOServiceGetMatchingService("DriverKitAcxxx") call fails to find the service, and no logs from NewUserClient() are ever generated on the DEXT side. Conclusion: Your suggestions helped us confirm two critical issues on macOS Sequoia 15.7.1: The provisioning system generates an incorrect userclient-access entitlement. The communicates-with-drivers entitlement is not working as expected, as it fails to grant the app visibility of the IOKit service. Both paths for App-DEXT communication appear to be blocked. We have filed a Feedback report with these findings. Any other workarounds you could suggest would be greatly appreciated. Thanks again for your help!
Topic: App & System Services SubTopic: Drivers Tags:
Replies
Boosts
Views
Activity
Oct ’25
Reply to Implementing Hardware Interrupt Handling with InterruptOccurred in DriverKit
Hi all, I followed Modernize PCI and SCSI drivers with DriverKit and the source to implement MSI interrupts. Right now, if I first load KEXT, then completely remove it and load DEXT without rebooting the storage, DEXT interrupts work fine. But if I restart the storage, DEXT interrupts stop working. Here is the implementation for initializing MSI-based Interrupts. // MSI-based interrupt kern_return_t DRV_MAIN_CLASS_NAME::InitializeInterrupt() { // // please refer to https://developer.apple.com/forums/thread/779842 // and // https://github.com/apple-oss-distributions/IOPCIFamily/blob/main/PEX8733/PCIDriverKitPEX8733/PCIDriverKitPEX8733.cpp // Log("InitializeInterrupt() - Start"); kern_return_t result = kIOReturnSuccess; uint64_t interruptType = 0; uint32_t msiInterruptIndex = 0; // Interrupt Intialize And Setup while((result = IOInterruptDispatchSource::GetInterruptType( ivars->pciDevice, msiInterruptIndex, &interruptType)) == kIOReturnSuccess) { if ((interruptType & kIOInterruptTypePCIMessaged) != 0) break; // 0x00010000 msiInterruptIndex++; } Log("MSI index: %u, type: 0x%llx", msiInterruptIndex, interruptType); __Require_Action( result == kIOReturnSuccess, Exit, LogErr("IOInterruptDispatchSource::GetInterruptType result 0x%08x", result)); uint16_t msiControl; ivars->pciDevice->ConfigurationRead16(0x52, &msiControl); Log("MSI Control Register: 0x%04x", msiControl); // uint64_t msiOffset; result = ivars->pciDevice->FindPCICapability(kIOPCICapabilityIDMSI, 0, &msiOffset); if (result == kIOReturnSuccess) { Log("MSI Capability Offset: 0x%llx", msiOffset); } else { LogErr("Failed to find MSI capability, error: 0x%x", result); } uint32_t msiAddr; ivars->pciDevice->ConfigurationRead32(msiOffset + 0x4, &msiAddr); uint32_t msiData; ivars->pciDevice->ConfigurationRead32(msiOffset + 0xC, &msiData); Log("MSI Address: 0x%x, MSI Data: 0x%x", msiAddr, msiData); // Create interrupt queue result = IODispatchQueue::Create("InterruptQueue", kIODispatchQueueReentrant, 0, &ivars->fInterruptQueue); __Require_Action(ivars->fInterruptQueue != nullptr && result == kIOReturnSuccess, Exit, LogErr("Create InterruptQueue fail")); result = SetDispatchQueue("InterruptQueue", ivars->fInterruptQueue); __Require_Action(result == kIOReturnSuccess, Exit, LogErr("SetDispatchQueue() fail 0x%0x", result)); // Create interrupt source result = IOInterruptDispatchSource::Create(ivars->pciDevice, msiInterruptIndex, ivars->fInterruptQueue, &ivars->fInterruptSource); __Require_Action(result == kIOReturnSuccess, Exit, LogErr("IOInterruptDispatchSource fail 0x%08x", result)); result = CreateActionInterruptOccurred(sizeof(void*), &ivars->interruptOccurredAction); __Require_Action(result == kIOReturnSuccess, Exit, LogErr("CreateActionInterruptOccurred fail 0x%08x", result)); result = ivars->fInterruptSource->SetHandler(ivars->interruptOccurredAction); __Require_Action(result == kIOReturnSuccess, Exit, LogErr("SetHandler fail 0x%08x", result)); result = ivars->fInterruptSource->SetEnable(true); __Require_Action(result == kIOReturnSuccess, Exit, LogErr("interruptSource SetEnable Error 0x%08x", result)); Exit: Log("InitializeInterrupt() - End"); return result; } and InterruptOccurred() void IMPL(DRV_MAIN_CLASS_NAME, InterruptOccurred) { Log("InterruptOccurred()"); .... } and the iig file class DRV_MAIN_CLASS_NAME: public IOUserSCSIParallelInterfaceController { public: ... virtual void InterruptOccurred(OSAction* action, uint64_t count, uint64_t time) TYPE(IOInterruptDispatchSource::InterruptOccurred) //PCI Dext interrupt QUEUENAME(InterruptQueue); } Did I forget something in the InitializeInterrupt implementation? Or is there another reason for this issue? Best Regards, Charles
Topic: App & System Services SubTopic: Drivers Tags:
Replies
Boosts
Views
Activity
May ’25
Reply to macOS Hang After Implementing UserMapHBAData()
Did "UserDoesHBAPerformDeviceManagement" return "true"? Yes, it returns TRUE, and at the same time sets ivars->bUserDoesHBAPerformDeviceManagement as true. This ensures that TargetForID_Init() waits until UserDoesHBAPerformDeviceManagement() has completed before executing AsyncEventHandler(), which then calls UserCreateTargetForID(). IMPL(DRV_MAIN_CLASS_NAME, UserDoesHBAPerformDeviceManagement) { Log("UserDoesHBAPerformDeviceManagement() - Start"); bool ret = TRUE; ivars->bUserDoesHBAPerformDeviceManagement = true; Log("UserDoesHBAPerformDeviceManagement() - End"); return ret; } If AsyncEventHandler() is not called, then UserCreateTargetForID() will not be invoked by the system. Additionally, TargetForID_Init() is called by AME_Host_RAID_Ready(), that runs inside a while loop in the UserInitializeController(). while (1) { bool sts; sts = AME_Check_RAID_Ready_Status(pAMEData); bool b_device_menagement = ivars->bUserDoesHBAPerformDeviceManagement; if (sts == TRUE && ivars->bUserDoesHBAPerformDeviceManagement) { AME_Host_RAID_Ready(); // will call TargetForID_Init() to invoke AsyncEventHandler() and then call UserCreateTargetForID() ... break; } } I suspect that what's actually going on is that CreateTargetForID is still in flight, which means you tried to reenter it by calling "UserCreateTargetForID". How can I avoid re-entering UserCreateTargetForID() and ensure that CreateTargetForID() has already been executed? Best Regards, Charles
Topic: App & System Services SubTopic: Drivers Tags:
Replies
Boosts
Views
Activity
May ’25
Reply to macOS Hang After Implementing UserMapHBAData()
Following is the sequence of current implementation Start() Timer_Init() UserInitializeController() In UserInitializeController(), we loop to check whether the RAID is ready and also watch a flag to see if UserDoesHBAPerformDeviceManagement() has been called, then invoke the TargetForID_Init() to call UserCreateTargetForID() Then we proceed with: Timer_Start() ZtsTimerOccurred() UserGetDMASpecification() UserReportInitiatorIdentifier() // *identifier = 65 UserReportHighestSupportedDeviceID() // *id = 63 UserReportMaximumTaskCount() // *count = 200 UserMapHBAData() // run 200 times UserStartController() UserDoesHBAPerformDeviceManagement() UserInitializeTargetForID() // runs for Target IDs 0 through 63 // Each call is paired with UserProcessParallelTask() TargetForID_Init() // be invoked in a loop process within UserInitializeController() Inside the log of TargetForID_Init() we see: TargetForID_Init() - Start AsyncEventHandler(ivars->fAsyncEventHandler, 0) !!! ERROR : Failed at LUN 0: 0xe00002d8 !!! ... Initialize Total 64 TargetForID, 0 Successfully TargetForID_Init() - End Questions: Does UserCreateTargetForID() have to be called only after UserDoesHBAPerformDeviceManagement()? What exactly does the error message 0xe00002d8 mean in this context? I never explicitly called UserInitializeTargetForID(), so why is it being executed? Looking forward to your reply. Really appreciate it! Best Regards, Charles
Topic: App & System Services SubTopic: Drivers Tags:
Replies
Boosts
Views
Activity
May ’25
Reply to macOS Hang After Implementing UserMapHBAData()
I figured out the reason why macOS crashed — it was because I called SetDispatchQueue() before assigning a value to the queue variable. // Create and set dispatch AuxiliaryQueue ret = IODispatchQueue::Create("AuxiliaryQueue", kIODispatchQueueReentrant, // Options (e.g., allow reentrancy) 0, // Priority &ivars->fAuxQueue); if (!ivars->fAuxQueue) LogErr("Create AuxiliaryQueue fail"); __Require((kIOReturnSuccess == ret), Exit); ret = SetDispatchQueue("AuxiliaryQueue", ivars->fAuxQueue); if (kIOReturnSuccess != ret) LogErr("SetDispatchQueue AuxiliaryQueue"); __Require((kIOReturnSuccess == ret), Exit); // Create and set dispatch InterruptQueue ret = IODispatchQueue::Create("InterruptQueue", kIODispatchQueueReentrant, 0, &ivars->fIntQueue); if (!ivars->fIntQueue) LogErr("Create InterruptQueue fail"); __Require((kIOReturnSuccess == ret), Exit); // AME_IO_milliseconds_Delay(2000); // 200ms delay #if 0 // Fail to SetDispatchQueue() ret = SetDispatchQueue("InterruptQueue", ivars->fIntQueue); if (kIOReturnSuccess != ret) LogErr("SetDispatchQueue InterruptQueue"); __Require((kIOReturnSuccess == ret), Exit); #endif However, there's still an issue: the second call to SetDispatchQueue() fails, even if I add a sleep before it. UserInitializeController() - Start !!! ERROR : SetDispatchQueue InterruptQueue !!! !!! ERROR : ret = 0xe00002c2 !!! UserInitializeController() - End Additionally, at first, UserMapHBAData() wasn't working either. The logs showed the following error: UserMapHBAData() - Start !!! ERROR : Buffer allocation failed !!! !!! ERROR : ret = 0xe00002bd !!! UserMapHBAData() - End Eventually, I was able to fix it by experimenting with different parameter values: // ivars->fTaskDataSize = 64; // ivars->fTaskDataSize = 128; // ivars->fTaskDataSize = 512; ivars->fTaskDataSize = 1024; // ivars->fTaskDataSize = 4096; const uint32_t alignment = 4; // const uint32_t alignment = 0; // Pass 0 for no guaranteed alignment. ret = IOBufferMemoryDescriptor::Create(kIOMemoryDirectionOutIn, ivars->fTaskDataSize, alignment, &buffer); if (!buffer) LogErr("Buffer allocation failed!"); Log("ivars->fTaskDataSize = %llu, alignment = %d", ivars->fTaskDataSize, alignment); __Require((kIOReturnSuccess == ret), Exit); As you mentioned, the best documentation is working sample code. If Apple could provide complete, buildable source examples, it would be an immense help, both to Apple and to developers. Lastly, you said: In the case here, I would start by simply getting the I/O ‘pipeline’ working WITHOUT actually doing any I/O. How should I go about doing that, exactly? Best Regards, Charles
Topic: App & System Services SubTopic: Drivers Tags:
Replies
Boosts
Views
Activity
May ’25