DriverKit

RSS for tag

Develop device drivers that run in user space using DriverKit.

Posts under DriverKit tag

74 Posts

Post

Replies

Boosts

Views

Activity

[DriverKit SCSI] SCSI probe stalls for Target ID > 0 with IOUserSCSIParallelInterfaceController
Hello everyone, We are migrating a KEXT storage driver to DriverKit. In our KEXT, we use a "one LUN = one Target" model and successfully create multiple targets in a loop during initialization. We are now trying to replicate this architecture in our DEXT. The issue is that only Target 0 is fully probed and mounted. For Target 1, the lifecycle silently stops after the first TEST UNIT READY command is successfully acknowledged. The macOS SCSI layer never sends any subsequent probe commands (like INQUIRY) to this target. The failure sequence for Target 1, observed from our logs (regardless of whether Target 0 is created), is as follows: AsyncCreateTargetForID(1) -> UserInitializeTargetForID(1) (Succeeds) UserProcessParallelTask(Target: 1, Opcode: TUR) (Succeeds) The DEXT correctly acknowledges the TUR command for Target 1 by returning kSCSITaskStatus_CHECK_CONDITION with UNIT ATTENTION in the Sense Data (Succeeds) <-- Breakpoint --> UserProcessParallelTask(Target: 1, Opcode: INQUIRY) (Never happens) Through log comparison, we have confirmed that the DEXT's response to the TUR command for Target 1 is identical to the successful KEXT's response. We have tried creating only Target 1 (skipping Target 0 entirely), but the behavior is exactly the same -> the probe still stalls after the TUR. We initially suspected a race condition caused by consecutive calls to AsyncCreateTargetForID(). We attempted several methods to ensure that targets are created sequentially, such as trying to build a "creation chain" using OSAction completion handlers. However, these attempts were unsuccessful due to various compilation errors and API misunderstandings. In any case, this "race condition" theory was ultimately disproven by our experiment where creating only Target 1 still resulted in failure. We would like to ask two questions: Is our inability to have a Target ID greater than 0 fully probed by macOS a bug in our own code, or could there be another reason we are unaware of? If we do indeed need a "one-after-another" creation mechanism for AsyncCreateTargetForID, what is the correct way to implement a "chained creation" using OSAction completion handlers in DriverKit? Thank you for any help or guidance. Best Regards, Charles
3
0
489
Jan ’26
VM App - PCIDriverKit Entitlement for Thunderbolt
I want to help contribute a feature in a virtual-machine app in macOS that supports PCIe device passthrough over thunderbolt. I have a question about the entitlements. Since I do not represent the GPU vendors, would I be allowed to get a driver signed that matches GPU vendor IDs? Is there such a thing as wildcard entitlement for PCIDriverKit? I don't want end-users to have to disable SIP to be able to use this. Any suggestions/leads? Thank you.
1
0
700
Jan ’26
"Signing certificate" and post-installation assignment fail due to IOPCIPrimaryMatch
I'm having trouble configuring the "IOPCIPrimaryMatch" entitlement. I'm currently developing using "sign to run locally" and have been able to confirm the expected behavior. I was considering signing with "Developer ID Application" for future distribution to customers, but after finding the following forum, I'm now aiming to sign with "Apple Development." https://developer.apple.com/forums/thread/743021 I'm currently having trouble with the IOPCIPrimaryMatch value. The "signing certificate" status in Xcode changes depending on the value, as follows: Successful if the value is as follows: IOPCIPrimaryMatch 0xFFFFFFFF&0x00161916 An error occurs if the value is as follows: IOPCIPrimaryMatch 0xFFFFFFFF&0x00161916 So I tried building and installing using "0xFFFFFFFF&0x00161916", but this time the driver was not assigned to the PCI device. By the way, when I used "sign to run locally", both the installation and assignment were successful with the following: IOPCIPrimaryMatch 0xFFFFFFFF&0x00161916 Could you please tell me the correct way to write this?
13
0
1.7k
Dec ’25
Neither macOS 14.7 "Standard" 'AppleUserHIDEventDriver' Matching Driver Nor Custom HIDDriverKit Driver 'IOUserHIDEventService::dispatchDigitizerTouchEvent' API Work for a HID-standard Digitizer Touch Pad Device
I have been working on a multi-platform multi-touch HID-standard digitizer clickpad device. The device uses Bluetooth Low Energy (BLE) as its connectivity transport and advertises HID over GATT. To date, I have the device working successfully on Windows 11 as a multi-touch, gesture-capable click pad with no custom driver or app on Windows. However, I have been having difficulty getting macOS to recognize and react to it as a HID-standard multi-touch click pad digitizer with either the standard Apple HID driver (AppleUserHIDEventDriver) or with a custom-coded driver extension (DEXT) modeled, based on the DTS stylus example and looking at the IOHIDFamily open source driver(s). The trackpad works with full-gesture support on Windows 11 and the descriptors seem to be compliant with the R23 Accessory Guidelines document, §15. With the standard, matching Apple AppleUserHIDEventDriver HID driver, when enumerating using stock-standard HID mouse descriptors, the device works fine on macOS 14.7 "Sonoma" as a relative pointer device with scroll wheel capability (two finger swipe generates a HID scroll report) and a single button. With the standard, matching Apple AppleUserHIDEventDriver HID driver, when enumerating using stock-standard HID digitizer click/touch pad descriptors (those same descriptors used successfully on Windows 11), the device does nothing. No button, no cursor, no gestures, nothing. Looking at ioreg -filtb, all of the key/value pairs for the driver match look correct. Because, even with the Apple open source IOHIDFamily drivers noted above, we could get little visibility into what might be going wrong, I wrote a custom DriverKit/HIDDriverKit driver extension (DEXT) (as noted above, based on the DTS HID stylus example and the open source IOHIDEventDriver. With that custom driver, I can get a single button click from the click pad to work by dispatching button events to dispatchRelativePointerEvent; however, when parsing, processing, and dispatching HID digitizer touch finger (that is, transducer) events via IOUserHIDEventService::dispatchDigitizerTouchEvent, nothing happens. If I log with: % sudo log stream --info --debug --predicate '(subsystem == "com.apple.iohid")' either using the standard AppleUserHIDEventDriver driver or our custom driver, we can see that our input events are tickling the IOHIDNXEventTranslatorSessionFilter HID event filter, so we know HID events are getting from the device into the macOS HID stack. This was further confirmed with the DTS Bluetooth PacketLogger app. Based on these events flowing in and hitting IOHIDNXEventTranslatorSessionFilter, using the standard AppleUserHIDEventDriver driver or our custom driver, clicks or click pad activity will either wake the display or system from sleep and activity will keep the display or system from going to sleep. In short, whether with the stock driver or our custom driver, HID input reports come in over Bluetooth and get processed successfully; however, nothing happens—no pointer movement or gesture recognition. STEPS TO REPRODUCE For the standard AppleUserHIDEventDriver: Pair the device with macOS 14.7 "Sonoma" using the Bluetooth menu. Confirm that it is paired / bonded / connected in the Bluetooth menu. Attempt to click or move one or more fingers on the touchpad surface. Nothing happens. For the our custom driver: Pair the device with macOS 14.7 "Sonoma" using the Bluetooth menu. Confirm that it is paired / bonded / connected in the Bluetooth menu. Attempt to click or move one or more fingers on the touchpad surface. Clicks are correctly registered. With transducer movement, regardless of the number of fingers, nothing happens.
4
0
1.1k
Dec ’25
DEXT (IOUserSCSIParallelInterfaceController): Direct I/O Succeeds, but Buffered I/O Fails with Data Corruption on Large File Copies
Hi all, We are migrating a SCSI HBA driver from KEXT to DriverKit (DEXT), with our DEXT inheriting from IOUserSCSIParallelInterfaceController. We've encountered a data corruption issue that is reliably reproducible under specific conditions and are hoping for some assistance from the community. Hardware and Driver Configuration: Controller: LSI 3108 DEXT Configuration: We are reporting our hardware limitations to the framework via the UserReportHBAConstraints function, with the following key settings: // UserReportHBAConstraints... addConstraint(kIOMaximumSegmentAddressableBitCountKey, 0x20); // 32-bit addConstraint(kIOMaximumSegmentCountWriteKey, 129); addConstraint(kIOMaximumByteCountWriteKey, 0x80000); // 512KB Observed Behavior: Direct I/O vs. Buffered I/O We've observed that the I/O behavior differs drastically depending on whether it goes through the system file cache: 1. Direct I/O (Bypassing System Cache) -> 100% Successful When we use fio with the direct=1 flag, our read/write and data verification tests pass perfectly for all file sizes, including 20GB+. 2. Buffered I/O (Using System Cache) -> 100% Failure at >128MB Whether we use the standard cp command or fio with the direct=1 option removed to simulate buffered I/O, we observe the exact same, clear failure threshold: Test Results: File sizes ≤ 128MB: Success. Data checksums match perfectly. File sizes ≥ 256MB: Failure. Checksums do not match, and the destination file is corrupted. Evidence of failure reproduced with fio (buffered_integrity_test.fio, with direct=1 removed): fio --size=128M buffered_integrity_test.fio -> Test Succeeded (err=0). fio --size=256M buffered_integrity_test.fio -> Test Failed (err=92), reporting the following error, which proves a data mismatch during the verification phase: verify: bad header ... at file ... offset 1048576, length 1048576 fio: ... error=Illegal byte sequence Our Analysis and Hypothesis The phenomenon of "Direct I/O succeeding while Buffered I/O fails" suggests the problem may be related to the cache synchronization mechanism at the end of the I/O process: Our UserProcessParallelTask_Impl function correctly handles READ and WRITE commands. When cp or fio (buffered) runs, the WRITE commands are successfully written to the LSI 3108 controller's onboard DRAM cache, and success is reported up the stack. At the end of the operation, to ensure data is flushed to disk, the macOS file system issues an fsync, which is ultimately translated into a SYNCHRONIZE CACHE SCSI command (Opcode 0x35 or 0x91) and sent to our UserProcessParallelTask_Impl. We hypothesize that our code may not be correctly identifying or handling this SYNCHRONIZE CACHE opcode. It might be reporting "success" up the stack without actually commanding the hardware to flush its cache to the physical disk. The OS receives this "success" status and assumes the operation is safely complete. In reality, however, the last batch of data remains only in the controller's volatile DRAM cache and is eventually lost. This results in an incomplete or incorrect file tail, and while the file size may be correct, the data checksum will inevitably fail. Summary Our DEXT driver performs correctly when handling Direct I/O but consistently fails with data corruption when handling Buffered I/O for files larger than 128MB. We can reliably reproduce this issue using fio with the direct=1 option removed. The root cause is very likely the improper handling of the SYNCHRONIZE CACHE command within our UserProcessParallelTask. P.S. This issue did not exist in the original KEXT version of the driver. We would appreciate any advice or guidance on this issue. Thank you.
13
0
743
Dec ’25
The total DMA size in DriverKit cannot exceed 2G?
We are developing a DriverKit driver on Apple M1. We use the following code to prepare DMA buffer: IODMACommandSpecification dmaSpecification; bzero(&dmaSpecification, sizeof(dmaSpecification)); dmaSpecification.options = kIODMACommandSpecificationNoOptions; dmaSpecification.maxAddressBits = p_dma_mgr->maxAddressBits; kret = IODMACommand::Create(p_dma_mgr->device, kIODMACommandCreateNoOptions, &dmaSpecification, &impl->dma_cmd ); if (kret != kIOReturnSuccess) { os_log(OS_LOG_DEFAULT, "Error: IODMACommand::Create failed! ret=0x%x\n", kret); impl->user_mem.reset(); IOFree(impl, sizeof(*impl)); return ret; } uint64_t flags = 0; uint32_t segmentsCount = 32; IOAddressSegment segments[32]; kret = impl->dma_cmd->PrepareForDMA(kIODMACommandPrepareForDMANoOptions, impl->user_mem.get(), 0, 0, // 0 for entire memory &flags, &segmentsCount, segments ); if (kret != kIOReturnSuccess) { OSSafeReleaseNULL(impl->dma_cmd); impl->user_mem.reset(); IOFree(impl, sizeof(*impl)); os_log(OS_LOG_DEFAULT, "Error: PrepareForDMA failed! ret=0x%x\n", kret); return kret; } I allocated several 8K BGRA video frames, each with a size of 141557760 bytes, and prepared the DMA according to the method mentioned above. The process was successful when the number of frames was 15 or fewer. However, issues arose when allocating 16 frames: Error: PrepareForDMA failed! ret=0xe00002bd By calculating, I found that the total size of 16 video frames exceeds 2GB. Is there such a limitation in DriverKit that the total DMA size cannot exceed 2GB? Are there any methods that would allow me to bypass this restriction so I can use more video frame buffers?
1
0
232
Dec ’25
Archive Fails: Conflict with Existing KEXT Developer ID Certificate
Hi everyone, We're trying to prepare a DriverKit App for a client test, and we've run into an unavoidable signing conflict that seems to be caused by the Xcode Archive process itself. Background & Environment: Environment: macOS 15.6.1, Xcode 16.4 Our project consists of a main App Target and a DEXT Target. Both the Debug and Release configurations for both targets are set to Xcode's default: Automatically manage signing. Our developer account holds a valid, active Developer ID Application (With Kext) certificate, which we use for signing our legacy KEXT. The Action That Triggers Failure: From this clean state, we execute Product -> Archive. The Archive process fails during the signing validation phase and presents the following three errors, completely halting the process: There is a problem with the request entity - You already have a current Developer ID Application Managed (With Kext) certificate... No profiles for 'com.company.Acxxx.driver' were found... No profiles for 'com.company.Acxxx.app' were found... This error seems to indicate that the Xcode Archive process: Ignores the project's Release configuration (even the default 'Auto' setting). Attempts to automatically create a new, standard Developer ID certificate for us. This action conflicts with the existing (With Kext) certificate in our account, causing the entire Archive process to fail. The "Failed Experiment" to Resolve This: To work around this automation conflict, we tried the solution: configuring a fully manual signing process for the Release configuration to explicitly tell Xcode to use our existing KEXT certificate. Our Steps: We disabled automatic signing for both the App and DEXT targets for the Release configuration and manually assigned the Developer ID Provisioning Profiles created for our Developer ID (With Kext) certificate. The New Problem: After doing this, the Signing Certificate field for the DEXT Target's Signing & Capabilities interface now shows None, accompanied by the misleading warning about needing a DriverKit development profile. The Outcome: This None issue now prevents us from even starting the Archive process, as the project fails to build due to the incorrect signing configuration. We've tried every debugging step — including rebuilding profiles, validating the keychain, and clearing caches — but nothing resolves this None issue. Our Dilemma: State A (Fully Automatic Signing): The Archive process fails due to the KEXT certificate conflict. State B (Manual Release Signing): The project fails to build due to the Signing Certificate: None issue, preventing us from initiating an Archive. For a development team holding a KEXT Developer ID certificate, how should an Xcode project be configured when migrating to DriverKit, so that the Archive process: Does not trigger the flawed automation logic that attempts to create a new certificate? And, does not fall into the Signing Certificate: None configuration trap? Related Forum Threads We've Studied: https://developer.apple.com/forums/thread/781932 https://developer.apple.com/forums/thread/751490 https://developer.apple.com/forums/thread/767152 https://developer.apple.com/forums/thread/721563 Best Reagrds, Charles
2
0
574
Dec ’25
INQUIRY command is ILLEGAL REQUEST
I am developing a DriverKit driver with the goal of sending vendor-specific commands to a USB storage device. I have successfully created the DriverKit driver, and when I connect the USB storage device, it appears correctly in IORegistryExplorer. My driver class inherits from IOUserSCSIPeripheralDeviceType00 in the SCSIPeripheralsDriverKit framework. I also created a UserClient class that inherits from IOUserClient, and from its ExternalMethod I tried sending an INQUIRY command as a basic test to confirm that command transmission works. However, the device returns an ILLEGAL REQUEST (Sense Key 0x5 / ASC 0x20). Could someone advise what I might be doing wrong? Below are the logs output from the driver: 2025-11-14 21:00:43.573730+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] Driver - NewUserClient() - Finished. 2025-11-14 21:00:43.573733+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - Start() 2025-11-14 21:00:43.573807+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - Start() - Finished. 2025-11-14 21:00:43.574249+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - ExternalMethod() called 2025-11-14 21:00:43.574258+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - ----- SCSICmdINQUIRY ----- 2025-11-14 21:00:43.574268+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - command.fRequestedByteCountOfTransfer = 512 2025-11-14 21:00:43.575980+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - SCSICmdINQUIRY() UserSendCDB fCompletionStatus = 0x0 2025-11-14 21:00:43.575988+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - SCSICmdINQUIRY() UserSendCDB fServiceResponse = 0x2 2025-11-14 21:00:43.575990+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - SCSICmdINQUIRY() UserSendCDB fSenseDataValid = 0x1 2025-11-14 21:00:43.575992+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - SCSICmdINQUIRY() UserSendCDB VALID_RESPONSE_CODE = 0x70 2025-11-14 21:00:43.575994+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - SCSICmdINQUIRY() UserSendCDB SENSE_KEY = 0x5 2025-11-14 21:00:43.575996+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - SCSICmdINQUIRY() UserSendCDB ADDITIONAL_SENSE_CODE = 0x20 2025-11-14 21:00:43.575998+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - SCSICmdINQUIRY() UserSendCDB ADDITIONAL_SENSE_CODE_QUALIFIER = 0x0 Here is the UserClient class: class SampleDriverKitUserClient: public IOUserClient { public: virtual bool init(void) override; virtual kern_return_t Start(IOService* provider) override; virtual kern_return_t Stop(IOService* provider) override; virtual void free(void) override; virtual kern_return_t ExternalMethod( uint64_t selector, IOUserClientMethodArguments* arguments, const IOUserClientMethodDispatch* dispatch, OSObject* target, void* reference) override; void SCSICmdINQUIRY(SampleDriverKitDriver *driver) LOCALONLY; }; Here is the part that sends the INQUIRY command: void SampleDriverKitUserClient::SCSICmdINQUIRY(SampleDriverKitDriver *driver) { kern_return_t kr = KERN_SUCCESS; SCSIType00OutParameters command = {}; UInt8 dataBuffer[512] = {0}; SCSI_Sense_Data senseData = {0}; Log("----- SCSICmdINQUIRY -----"); SetCommandCDB(&command.fCommandDescriptorBlock, 0x12, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); command.fLogicalUnitNumber = 0; command.fTimeoutDuration = 10000; // milliseconds command.fRequestedByteCountOfTransfer = sizeof(dataBuffer); Log("command.fRequestedByteCountOfTransfer = %lld", command.fRequestedByteCountOfTransfer); command.fBufferDirection = kIOMemoryDirectionIn; command.fDataTransferDirection = kSCSIDataTransfer_FromTargetToInitiator; command.fDataBufferAddr = reinterpret_cast<uint64_t>(dataBuffer); command.fSenseBufferAddr = reinterpret_cast<uint64_t>(&senseData); command.fSenseLengthRequested = sizeof(senseData); if( driver ) { SCSIType00InParameters response = {}; kr = driver->UserSendCDB(command, &response); if( kr != KERN_SUCCESS ) { Log("SCSICmdINQUIRY() UserSendCDB failed (0x%x)", kr); return; } Log("SCSICmdINQUIRY() UserSendCDB fCompletionStatus = 0x%x", response.fCompletionStatus); Log("SCSICmdINQUIRY() UserSendCDB fServiceResponse = 0x%x", response.fServiceResponse); Log("SCSICmdINQUIRY() UserSendCDB fSenseDataValid = 0x%x", response.fSenseDataValid); Log("SCSICmdINQUIRY() UserSendCDB VALID_RESPONSE_CODE = 0x%x", senseData.VALID_RESPONSE_CODE); Log("SCSICmdINQUIRY() UserSendCDB SENSE_KEY = 0x%x", senseData.SENSE_KEY); Log("SCSICmdINQUIRY() UserSendCDB ADDITIONAL_SENSE_CODE = 0x%x", senseData.ADDITIONAL_SENSE_CODE); Log("SCSICmdINQUIRY() UserSendCDB ADDITIONAL_SENSE_CODE_QUALIFIER = 0x%x", senseData.ADDITIONAL_SENSE_CODE_QUALIFIER); if( response.fServiceResponse == kSCSIServiceResponse_TASK_COMPLETE ) { Log("SCSICmdINQUIRY() UserSendCDB complete success!!"); } for( int i=0; i < 5; i++ ) { Log("data [%04d]=0x%x [%04d]=0x%x [%04d]=0x%x [%04d]=0x%x [%04d]=0x%x [%04d]=0x%x [%04d]=0x%x [%04d]=0x%x", i*8+0, dataBuffer[i*8+0], i*8+1, dataBuffer[i*8+1], i*8+2, dataBuffer[i*8+2], i*8+3, dataBuffer[i*8+3], i*8+4, dataBuffer[i*8+4], i*8+5, dataBuffer[i*8+5], i*8+6, dataBuffer[i*8+6], i*8+7, dataBuffer[i*8+7] ); } char vendorID[9] = {0}; memcpy(vendorID, &dataBuffer[8], 8); Log("vendorID = %s",vendorID); char productID[17] = {0}; memcpy(productID, &dataBuffer[16], 16); Log("productID = %s",productID); } } My environment is: MacBook Pro (M2), macOS 15.6 If anyone has insight into what causes the ILLEGAL REQUEST, or what I am missing when using IOUserSCSIPeripheralDeviceType00 and UserSendCDB, I would greatly appreciate your help. Thank you.
1
0
469
Nov ’25
"How to" for dext distribution
I have a DriverKit system extension (dext) that uses PCIDriverKit. I would like to get the build environment straightened out to successfully distribute the dext and associated software to end users. There are three types of software involved: The Dext-hosting application - this is the application that must be installed to /Applications/, and will perform the registration of the dext. The dext is deployed "within" this application, and can be found in the /Contents/Library/SystemExtensions folder of the app bundle. The dext itself - this is the actual binary system extension, which will be registered by its owning application, and will operate in its own application space independent of the hosting application. Additional applications that communicate with the dext - these are applications which will connect to the dext through user clients, but these applications do not contain the dext themselves. There are multiple locations where settings need to be exactly correct for each type of software to be signed, provisioned, and notarized properly in order to be distributed to users: developer.apple.com - where "identifiers" and "provisioning profiles" are managed. Note that there are differences in access between "Team Agent", "Admin", and "Developer" at this site. Xcode project's Target "Signing & Capabilities" tab - this is where "automatically manage signing" can be selected, as well as team selection, provisioning profile selection, and capabilities can be modified. Xcode project's Target "Build Settings" tab - this is where code signing identity, code signing development team, code signing entitlements file selection, Info.plist options and file selection, and provisioning profile selection. Xcode's Organizer window, which is where you manage archives and select for distribution. In this case, I am interested in "Developer ID" Direct Distribution - I want the software signed with our company's credentials (Team Developer ID) so that users know they can trust the software. Choosing "automatically manage signing" does not work for deployment. The debug versions of software include DriverKit (development) capability (under App ID configuration at developer.apple.com), and this apparently must not be present in distributable provisioning. I believe this means that different provisioning needs to occur between debug and release builds? I have tried many iterations of selections at all the locations, for all three types of binaries, and rather than post everything that does not work, I am asking, "what is supposed to work?"
20
0
2.9k
Nov ’25
Using Adhoc Profile for DriverKit show error: Xcode 14 and later requires a DriverKit development profile enabled for iOS and macOS
We got an app for iPad which has two targets one for the App itself (MainApp target ) and another one for the Driver ( Driver Target ) using DriverKit. The app works fine in Development, but I'm trying to distribute it with adhoc. I've requested the Distribution Entitlement to Apple, after getting it, the App Id for the Driver has the following Capabilities: DriverKit, DriverKit (development), DriverKit USB Transport (development), DriverKit USB Transport - VendorID, In-App Purchase Now in the profile section, I've created a adhoc profile for the Driver AppId (Identifier). Obviously I've also created an Adhoc profile for the Main AppId Finally in the Signing & Capabilities Section I set up the profiles for MainApp target, int the Debug one I set up the Development one and int the Release one I set up the adhoc one. I do the same in the Driver Target, but when I set up the Adhoc one in the Release, I've got a warning: Xcode 14 and later requires a DriverKit development profile enabled for iOS and macOS. Visit the developer website to create or download a DriverKit profile Also interestingly the Signing Certificate section says: None I also set up the Capabilities for the Driver Target: DriverKit USB Transport - VendorID DriverKit USB Transport ( Development ) Inside these capabilities I set up the vendor ID as dictionary The problem is, if I try to Archive the app I will get the previous Warning message as error: Xcode 14 and later requires a DriverKit development profile enabled for iOS and macOS. Visit the developer website to create or download a DriverKit profile. Any idea what I'm missing? Thanks
2
0
1.1k
Nov ’25
DriverKit IOUserSerial Driver
Hello everyone. After a lot of research and some tests from various sources, I have actually built a small SerialDriverKit IOUserSerial driver. Unfortunately, the documentation on the official sites is tight-lipped and very thin. At least I have a running driver instance. Now my request and question: Can anyone give me a tip on how to get the data from the serial client? I have already called IOUserSerial::ConnectQueues(...) in the IOUserSerial::Start() method and I got the IOMemoryDescriptors for interrupt, RX and TX to my driver instance. I tried to get access to the memory in the method IOUserSerial::TxDataAvailable() with IOMemoryDescriptor::CreateMapping(...). Unfortunately, no data is coming in. It's always 0x00. Here is the OS log: kernel: (org.eof.tools.VSPDriver.dext) kernel: (org.eof.tools.VSPDriver.dext) [VSPDriver] init called. kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] constructor called. kernel: (org.eof.tools.VSPDriver.dext) [VSPDriver] start called. kernel: (org.eof.tools.VSPDriver.dext) IOUserSerial::: 40 0x600000da4058 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] Start called. kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] Connect INT/RX/TX buffer. kernel: (org.eof.tools.VSPDriver.dext) IOUserSerial::: 59 0x600000da4058 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] prepare TCP socket. kernel: (org.eof.tools.VSPDriver.dext) [VSPDriver] driver started successfully. kernel: DK: VSPDriver-0x100000753::start(IOUserResources-0x100000116) ok ... ... some client serial setup stuff ... kernel: (IOUserSerial) IOUserSerial::hwResetFIFO: 1076 ==&gt;0 kernel: (IOUserSerial) IOUserSerial::hwResetFIFO: 1076 &lt;== kernel: (IOUserSerial) IOUserSerial::hwResetFIFO: 1076 locklevel = 1 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriver] HwResetFIFO called. kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] HwResetFIFO called. kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] HwResetFIFO: tx=0 rx=1 kernel: (IOUserSerial) IOUserSerial::hwResetFIFO: 1076 ==&gt;0 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriver] TxDataAvailable called. kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable called. kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: address=0x104c22000 length=16384 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: debug TX buffer kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00
20
1
1.6k
Nov ’25
Architectural Performance Difference in External Storage I/O Between Intel and Apple Silicon Macs
Hi everyone, We are in the process of migrating a legacy KEXT for our external multi-disk RAID enclosure to the modern DriverKit framework. During the performance validation of our KEXT, we observed a large and consistent maximum throughput difference between Intel-based Macs and Apple Silicon-based Macs. We would like to share our findings and hope to discuss with others in the community to see if you have had similar experiences that could confirm or correct our understanding. The Observation: A Consistent Performance Gap When using the exact same external RAID hardware (an 8-HDD RAID 5 array), driven by our mature KEXT, we see the following results in high-throughput benchmarks (AJA System Test, large sequential writes): On a 2020 Intel-based Mac: We consistently achieve a throughput of ~2500 MB/s. On modern M-series Macs (from M1 to M4): The throughput is consistently capped at ~1500 MB/s. This performance difference of nearly 40% is significant and is present across the entire Apple Silicon product line. Our Hypothesis: A Shift in Architectural Design Philosophy Since the KEXT and external hardware are identical in both tests, we believe this performance difference is not a bug but a fundamental platform architecture distinction. Our hypothesis is as follows: 1. The Intel Mac Era ("Dedicated Throughput") The Intel-based Macs we tested use a dedicated, discrete Intel Thunderbolt controller chip. This chip has its own dedicated PCIe lanes and resources, and its design appears to be singularly focused on maximizing raw, sustained data throughput for external peripherals. 2. The Apple Silicon Era ("Integrated Efficiency") In contrast, M-series Macs use a deeply integrated I/O controller inside the SoC. This controller must share resources, such as the total unified memory bandwidth and the chip's overall power budget, with all other functional units (CPU, GPU, etc.). We speculate that the design priority for this integrated I/O controller has shifted from "maximizing single-task raw throughput" to "maximizing overall system efficiency, multi-task responsiveness, and low latency." As a result, in a pure, single-task storage benchmark, its performance ceiling may be lower than that of the older, dedicated-chip architecture. Our Question to the Community: Is our understanding correct? Have other developers of high-performance storage drivers or peripherals also observed a similar performance ceiling for external storage on Apple Silicon Macs, when compared to high-end Intel Macs? We believe that understanding this as a deliberate architectural trade-off is crucial for setting realistic performance targets for our DEXT. Our current goal has been adjusted to have our DEXT match the KEXT's ~1500 MB/s on the M-series platform. Any insights, confirmations, or corrections from the community or Apple engineers would be greatly appreciated. Thank you very much! Charles
1
0
356
Nov ’25
[DEXT Migration Issue] IOUserSCSIParallelInterfaceController fails to handle low-level I/O from `diskutil`
Hello everyone, We are migrating our KEXT for a Thunderbolt storage device to a DEXT based on IOUserSCSIParallelInterfaceController. We've run into a fundamental issue where the driver's behavior splits based on the I/O source: high-level I/O from the file system (e.g., Finder, cp) is mostly functional (with a minor ls -al sorting issue for Traditional Chinese filenames), while low-level I/O directly to the block device (e.g., diskutil) fails or acts unreliably. Basic read/write with dd appears to be mostly functional. We suspect that our DEXT is failing to correctly register its full device "personality" with the I/O Kit framework, unlike its KEXT counterpart. As a result, low-level I/O requests with special attributes (like cache synchronization) sent by diskutil are not being handled correctly by the IOUserSCSIParallelInterfaceController framework of our DEXT. Actions Performed & Relevant Logs 1. Discrepancy: diskutil info Shows Different Device Identities for DEXT vs. KEXT For the exact same hardware, the KEXT and DEXT are identified by the system as two different protocols. KEXT Environment: Device Identifier: disk5 Protocol: Fibre Channel Interface ... Disk Size: 66.0 TB Device Block Size: 512 Bytes DEXT Environment: Device Identifier: disk5 Protocol: SCSI SCSI Domain ID: 2 SCSI Target ID: 0 ... Disk Size: 66.0 TB Device Block Size: 512 Bytes 2. Divergent I/O Behavior: Partial Success with Finder/cp vs. Failure with diskutil High-Level I/O (Partially Successful): In the DEXT environment, if we operate on an existing volume (e.g., /Volumes/MyVolume), file copy operations using Finder or cp succeed. Furthermore, the logs we've placed in our single I/O entry point, UserProcessParallelTask_Impl, are triggered. Side Effect: However, running ls -al on such a volume shows an incorrect sorting order for files with Traditional Chinese names (they appear before . and ..). Low-Level I/O (Contradictory Behavior): In the DEXT environment, when we operate directly on the raw block device (/dev/disk5): diskutil partitionDisk ... -> Fails 100% of the time with the error: Error: -69825: Wiping volume data to prevent future accidental probing failed. dd command -> Basic read/write operations appear to work correctly (a write can be immediately followed by a read within the same DEXT session, and the data is correct). 3. Evidence of Cache Synchronization Failure (Non-deterministic Behavior) The success of the dd command is not deterministic. Cross-environment tests prove that its write operations are unreliable: First Test: In the DEXT environment, write a file with random data to /dev/disk5 using dd. Reboot into the KEXT environment. Read the data back from /dev/disk5 using dd. The result is a file filled with all zeros. Conclusion: The write operation only went to the hardware cache, and the data was lost upon reboot. Second Test: In the DEXT environment, write the same random file to /dev/disk5 using dd. Key Variable: Immediately after, still within the DEXT environment, read the data back once for verification. The content is correct! Reboot into the KEXT environment. Read the data back from /dev/disk5. This time, the content is correct! Conclusion: The additional read operation in the second test unintentionally triggered a hardware cache flush. This proves that the dd (in our DEXT) write operation by itself does not guarantee synchronization, making its behavior unreliable. Our Problem Based on the observations above, we have the conclusion: High-Level Path (triggered by Finder/cp): When an I/O request originates from the high-level file system, the framework seems to enter a fully-featured mode. In this mode, all SCSI commands, including READ/WRITE, INQUIRY, and SYNCHRONIZE CACHE, are correctly packaged and dispatched to our UserProcessParallelTask_Impl entry point. Therefore, Finder operations are mostly functional. Low-Level Path (triggered by dd/diskutil): When an I/O request originates from the low-level raw block device layer: The most basic READ/WRITE commands can be dispatched (which is why dd appears to work). However, critical management commands, such as INQUIRY and SYNCHRONIZE CACHE, are not being correctly dispatched or handled. This leads to the incorrect device identification in diskutil info and the failure of diskutil partitionDisk due to its inability to confirm cache synchronization. We would greatly appreciate any guidance, suggestions, or insights on how to resolve this discrepancy. Specifically, what is the recommended approach within DriverKit to ensure that a DEXT based on IOUserSCSIParallelInterfaceController can properly declare its capabilities and handle both high-level and low-level I/O requests uniformly? Thank you. Charles
12
0
626
Nov ’25
How to allocate contiguous memory in DriverKit?
We want to allocate a block of contiguous memory (≤1M) for audio ring DMA usage, but we haven't found any explicit method in the DriverKit documentation for allocating contiguous memory. I'm aware that IOBufferMemoryDescriptor::Create can be used in DriverKit to allocate memory and share it with user space. However, is the allocated memory physically contiguous? Can it guarantee that when I subsequently call PrepareForDMA in IODMACommand, there will be only one segment? Could you please help review this? Thank you!
2
0
280
Oct ’25
How to completely uninstall the old kext driver?
Hi, On macOS 11 and earlier versions, we provided users with the following script to uninstall our kext driver: sudo pkgutil --only-files --files com.magewell.ProCapture | tr '\n' '\0' | xargs -n 1 -0 sudo rm -vf sudo pkgutil --only-dirs --files com.magewell.ProCapture | grep ProCapture[^/]*$ | tr '\n' '\0' | xargs -n 1 -0 sudo rm -rvf sudo pkgutil --forget com.magewell.ProCapture sudo kextcache -system-caches However, this script no longer works on macOS 13 and returns the following error: It looks like you're trying to update the system caches. As of macOS 11, the personality cache is no longer in use for keeping kext matching information up-to-date. For more information, see `man kmutil`. This indicates we can no longer use kextcache -system-caches to clear our driver cache. This creates an issue where even after installing the new dext driver, the dext driver cannot run due to the presence of the old kext driver. We've tried various methods but haven't been able to completely uninstall the old kext driver - after every new system update, the old kext reappears. The specific process is as follows: This is the sequence I followed in my latest test - Device is running macOS 13 Ventura w/ 4247 Pro Capture kext driver installed kmutil inspect | grep -i magewell - this returns references to the kext files in /Library/Extensions, which is expected because I have not yet removed the 4247 kext driver - then I ran the following combination of your removal script and my removal steps: cd / sudo rm -r /Library/Extensions/ProCaptureDriver.kext sudo rm -r /Library/Extensions/ProCaptureEvent.kext sudo rm /System/Volumes/Preboot/*/boot/*/System/Library/Caches/com.apple.kernelcaches/kernelcache.auxkc* sudo pkgutil --only-files --files com.magewell.ProCapture | tr '\n' '\0' | xargs -n 1 -0 sudo rm -vf sudo pkgutil --only-dirs --files com.magewell.ProCapture | grep ProCapture[^/]*$ | tr '\n' '\0' | xargs -n 1 -0 sudo rm -rvf sudo pkgutil --forget com.magewell.ProCapture sudo kextcache --clear-staging sudo kcditto sudo kmutil install --update-preboot sudo shutdown -r now - After this I ran 'kmutil inspect | grep -i magewell' and got no results, which seems good but... - then I ran the upgrade to macOS 15.7 Sequoia - Afterwards I ran 'kmutil inspect | grep -i magewell' and it returned references to the old /Library/Extensions kexts again, although the files no longer exist in /Library/Extensions - I then ran my cleanup process again (slightly different for Sequoia-available commands): sudo rm /System/Volumes/Preboot/*/boot/*/System/Library/Caches/com.apple.kernelcaches/kernelcache.auxkc* sudo kextcache --clear-staging sudo kmutil rebuild sudo kcditto sudo kmutil install --update-preboot sudo shutdown -r now - Then I ran 'kmutil inspect | grep -i magewell' and got no results again - As a next test I ran a minor update to macOS 15.7.1, then ran 'kmutil inspect | grep -i magewell' and the references to the old kexts came back again We have indeed identified a solution to address this issue: kmutil trigger-panic-medic --volume-root /Volumes/<YourVolumeName> However, this method requires booting into Recovery Mode, which is unacceptable for many of our customers. Especially for those who need bulk remote management, having personnel physically operate each machine one by one is simply not feasible. Therefore, is there a method to completely uninstall the kext driver while in normal mode? Thank you!
3
0
364
Oct ’25
Should UserSendCBD work on UAS interfaces?
The device I am trying to develop a firmware updater for is an NVMe drive with a USB4 interface. It can connect in USB4 mode (tunneled NVMe), in USB 3 mode or in USB 2 mode. In USB 2 and USB 3 mode, the device descriptor shows one interface with two alternates. Alternate 0 uses the bulk-only protocol, with one IN and one OUT pipe. Alternate 1 uses the UAS protocol, with two IN and two OUT pipes. I use identical code in my driver to send custom CDBs. I can see using IORegistryExplorer that in USB 2 mode, macOS chooses alternate 0, the bulk-only protocol. My custom CDBs and their accompanying data pay loads are put on the bus, more or less as expected. In USB 3 mode, macOS chooses alternate 1, the UAS protocol. My custom CDB is put on the bus, but no payload data is transferred. Is this expected behavior? If so, is there a way to force the OS to choose alternate 0 even when on USB 3, perhaps with another dext? I'll file a bug about this when Feedback Assistant lets me.
8
0
516
Oct ’25
DEXT receives zero-filled buffer from DMA, despite firmware confirming data write
Hello everyone, I am migrating a KEXT for a SCSI PCI RAID controller (LSI 3108 RoC) to DriverKit (DEXT). While the DEXT loads successfully, I'm facing a DMA issue: an INQUIRY command results in a 0-byte disk because the data buffer received by the DEXT is all zeros, despite our firmware logs confirming that the correct data was prepared and sent. We have gathered detailed forensic evidence and would appreciate any insights from the community. Detailed Trace of a Failing INQUIRY Command: 1, DEXT Dispatches the Command: Our UserProcessParallelTask implementation correctly receives the INQUIRY task. Logs show the requested transfer size is 6 bytes, and the DEXT obtains the IOVA (0x801c0000) to pass to the hardware. DEXT Log: [UserProcessParallelTask_Impl] --- FORENSIC ANALYSIS --- [UserProcessParallelTask_Impl] fBufferIOVMAddr = 0x801c0000 [UserProcessParallelTask_Impl] fRequestedTransferCount = 6 2, Firmware Receives IOVA and Prepares Correct Data: A probe in our firmware confirms that the hardware successfully received the correct IOVA and the 6-byte length requirement. The firmware then prepares the correct 6-byte INQUIRY response in its internal staging buffer. Firmware Logs: -- [FIRMWARE PROBE: INCOMING DMA DUMP] -- Host IOVA (High:Low) = 0x00000000801c0000 DataLength in Header = 6 (0x6) --- [Firmware Outgoing Data Dump from go_inquiry] --- Source Address: 0x228BB800, Length: 6 bytes 0x0000: 00 00 05 12 1F 00 3, Hardware Reports a Successful Transfer, but Data is Lost: After the firmware initiates the DMA write to the Host IOVA, the hardware reports a successful transfer of 6 bytes back to our DEXT. DEXT Completion Log: [AME_Host_Normal_Handler_SCSI_Request] [TaskID: 200] COMPLETING... [AME_Host_Normal_Handler_SCSI_Request] Hardware Transferred = 6 bytes [AME_Host_Normal_Handler_SCSI_Request] - ReplyStatus = SUCCESS (0x0) [AME_Host_Normal_Handler_SCSI_Request] - SCSIStatus = SUCCESS (0x0) The Core Contradiction: Despite the firmware preparing the correct data and the hardware reporting a successful DMA transfer, the fDataBuffer in our DEXT remains filled with zeros. The 6 bytes of data are lost somewhere between the PCIe bus and host memory. This "data-in-firmware, zeros-in-DEXT" phenomenon leads us to believe the issue lies in memory address translation or a system security policy, as our legacy KEXT works perfectly on the same hardware. Compared to a KEXT, are there any known, stricter IOMMU/security policies for a DEXT that could cause this kind of "silent write failure" (even with a correct IOVA)? Alternatively, what is the correct and complete expected workflow in DriverKit for preparing an IOMemoryDescriptor* fDataBuffer (received in UserProcessParallelTask) for a PCI hardware device to use as a DMA write target? Any official documentation, examples, or advice on the IOMemoryDescriptor to PCI Bus Address workflow would be immensely helpful. Thank you. Charles
5
0
479
Oct ’25
DriverKit DEXT Logs Only Once, Subsequent Logs Are Lost Despite Normal Functionality
Hello everyone, I've encountered a very strange and persistent logging issue with my DriverKit DEXT and would appreciate any insights from the community. [Problem Summary] My DriverKit DEXT, along with its companion Swift app, is functionally working perfectly. I can repeatedly call methods in the DEXT from the app (e.g., a Ping-Pong test and a StaticProcessInbandTask call) and receive the correct response every time. However, the os_log messages within my IOUserClient subclass are only successfully recorded for the very first set of interactions. After that, all subsequent logs are completely missing. What's even stranger is that all successfully recorded logs are attributed to the kernel: process, even for purely user-space methods like ExternalMethod. [Development Environment] macOS: 15.7.1 Xcode: 16.4 Hardware: MacBook Pro M1 DEXT Logging Macro (Log.h): #include <os/log.h> #define Log(fmt, ...) \ do { \ os_log(OS_LOG_DEFAULT, "[%{public}s] " fmt, __FUNCTION__, ##__VA_ARGS__); \ } while (0) [Steps to Reproduce & Observed Behavior] The DEXT is successfully loaded via the companion app. I click the "Ping-Pong" button, then the "Process InBand" button in the app. The app's UI log correctly shows that the request was sent and a successful response was received from the DEXT. I repeat step 2 multiple times. Each interaction works flawlessly from the app's perspective. I then use the log show command to export the logs from this period, for example: log show --last 5m | grep "com.accusys.Acxxx.driver" > dext_logs.txt Observed Result (Log Content): In the dext_logs.txt file, I can only see the logs from the very first Ping-Pong and the very first Process InBand call. All subsequent, successful operations leave no trace in the logs. kernel: (com.accusys.Acxxx.driver.dext) [ExternalMethod] // { --- kernel: (com.accusys.Acxxx.driver.dext) [ExternalMethod] // --- } kernel: (com.accusys.Acxxx.driver.dext) [StaticPingPong] // { --- kernel: (com.accusys.Acxxx.driver.dext) [StaticPingPong] // --- } kernel: (com.accusys.Acxxx.driver.dext) [ExternalMethod] // { --- kernel: (com.accusys.Acxxx.driver.dext) [ExternalMethod] // --- } kernel: (com.accusys.Acxxx.driver.dext) [StaticProcessInbandTask] // { --- kernel: (com.accusys.Acxxx.driver.dext) [StaticProcessInbandTask] // --- } <--- END OF FILE (No new logs appear after this point) ---> [Core Questions] Why are logs in IOUserClient subclass only recorded once? Given the DEXT is clearly still running and processing requests, why would os_log calls only succeed in writing to the system log database on the first interaction? Why are all logs attributed to the kernel? Why would logs from 100% user-space code like ExternalMethod and StaticPingPong be attributed to the kernel process? [Solutions Attempted That Did Not Work] I have verified with ps aux that the DEXT process (com.accusys.Acxxx.driver) is running continuously in the background and has not crashed. Attempted to force-restart the logging service with sudo killall logd, but the issue persists. Performed the most thorough reset possible using systemextensionsctl reset followed by a full reboot, then reinstalled the DEXT. The issue remains exactly the same. Thank you for any possible help or suggestions Best, Charles
3
0
242
Oct ’25
CreateMemoryDescriptorFromClient can't write data to user?
We've developed a PCIDriverKit driver for the capture card on macOS and have identified an issue: CreateMemoryDescriptorFromClient can only read data from the user space to the driver, but cannot write data back to the user. typedef struct _MWCAP_EDID_DATA { uint64_t size; uint64_t uaddr; } MWCAP_EDID_DATA; // App size_t xxx::GetEdid(void *buff, size_t size) { MWCAP_EDID_DATA edid; edid.size = size; edid.uaddr = (uint64_t)buff; kr = IOConnectCallStructMethod( connect, kUserGetEdid, &edid, sizeof(MWCAP_EDID_DATA), NULL, NULL ); // kr is 0. But However, the data in the buffer remains unchanged; // it does not reflect the EDID copied from the DEXT. return size; } // Driver MWCAP_EDID_DATA *edid = (MWCAP_EDID_DATA *)input; IOMemoryDescriptor *user_buf_mem = NULL; IOAddressSegment segment; segment.address = edid->uaddr; segment.length = edid->size; // We have verified that the values in edid->uaddr and edid->size are consistent with what was set by the application. ret = CreateMemoryDescriptorFromClient(kIOMemoryDirectionOutIn, 1, &segment, &user_buf_mem); if (ret != kIOReturnSuccess) { os_log(OS_LOG_DEFAULT, "Failed to create memdesc with error: 0x%08x", ret); break; } IOMemoryMap* user_buf_map = nullptr; ret = user_buf_mem->CreateMapping(0, 0, 0, 0, 0, &user_buf_map); if (ret != kIOReturnSuccess) { os_log(OS_LOG_DEFAULT, "Failed to create mapping with error: 0x%08x", ret); OSSafeReleaseNULL(user_buf_mem); break; } // ... fill the user_buf_map with edid data ... // For example: // memcpy(user_buf_map->GetAddress(), source_edid_data, edid->size); // At this point we have also verified the data in user_buf_map->GetAddress(), which matches our expectations. OSSafeReleaseNULL(user_buf_map); OSSafeReleaseNULL(user_buf_mem); Please help take a look, thank you!
1
0
290
Oct ’25
[DriverKit SCSI] SCSI probe stalls for Target ID > 0 with IOUserSCSIParallelInterfaceController
Hello everyone, We are migrating a KEXT storage driver to DriverKit. In our KEXT, we use a "one LUN = one Target" model and successfully create multiple targets in a loop during initialization. We are now trying to replicate this architecture in our DEXT. The issue is that only Target 0 is fully probed and mounted. For Target 1, the lifecycle silently stops after the first TEST UNIT READY command is successfully acknowledged. The macOS SCSI layer never sends any subsequent probe commands (like INQUIRY) to this target. The failure sequence for Target 1, observed from our logs (regardless of whether Target 0 is created), is as follows: AsyncCreateTargetForID(1) -> UserInitializeTargetForID(1) (Succeeds) UserProcessParallelTask(Target: 1, Opcode: TUR) (Succeeds) The DEXT correctly acknowledges the TUR command for Target 1 by returning kSCSITaskStatus_CHECK_CONDITION with UNIT ATTENTION in the Sense Data (Succeeds) <-- Breakpoint --> UserProcessParallelTask(Target: 1, Opcode: INQUIRY) (Never happens) Through log comparison, we have confirmed that the DEXT's response to the TUR command for Target 1 is identical to the successful KEXT's response. We have tried creating only Target 1 (skipping Target 0 entirely), but the behavior is exactly the same -> the probe still stalls after the TUR. We initially suspected a race condition caused by consecutive calls to AsyncCreateTargetForID(). We attempted several methods to ensure that targets are created sequentially, such as trying to build a "creation chain" using OSAction completion handlers. However, these attempts were unsuccessful due to various compilation errors and API misunderstandings. In any case, this "race condition" theory was ultimately disproven by our experiment where creating only Target 1 still resulted in failure. We would like to ask two questions: Is our inability to have a Target ID greater than 0 fully probed by macOS a bug in our own code, or could there be another reason we are unaware of? If we do indeed need a "one-after-another" creation mechanism for AsyncCreateTargetForID, what is the correct way to implement a "chained creation" using OSAction completion handlers in DriverKit? Thank you for any help or guidance. Best Regards, Charles
Replies
3
Boosts
0
Views
489
Activity
Jan ’26
VM App - PCIDriverKit Entitlement for Thunderbolt
I want to help contribute a feature in a virtual-machine app in macOS that supports PCIe device passthrough over thunderbolt. I have a question about the entitlements. Since I do not represent the GPU vendors, would I be allowed to get a driver signed that matches GPU vendor IDs? Is there such a thing as wildcard entitlement for PCIDriverKit? I don't want end-users to have to disable SIP to be able to use this. Any suggestions/leads? Thank you.
Replies
1
Boosts
0
Views
700
Activity
Jan ’26
"Signing certificate" and post-installation assignment fail due to IOPCIPrimaryMatch
I'm having trouble configuring the "IOPCIPrimaryMatch" entitlement. I'm currently developing using "sign to run locally" and have been able to confirm the expected behavior. I was considering signing with "Developer ID Application" for future distribution to customers, but after finding the following forum, I'm now aiming to sign with "Apple Development." https://developer.apple.com/forums/thread/743021 I'm currently having trouble with the IOPCIPrimaryMatch value. The "signing certificate" status in Xcode changes depending on the value, as follows: Successful if the value is as follows: IOPCIPrimaryMatch 0xFFFFFFFF&0x00161916 An error occurs if the value is as follows: IOPCIPrimaryMatch 0xFFFFFFFF&0x00161916 So I tried building and installing using "0xFFFFFFFF&0x00161916", but this time the driver was not assigned to the PCI device. By the way, when I used "sign to run locally", both the installation and assignment were successful with the following: IOPCIPrimaryMatch 0xFFFFFFFF&0x00161916 Could you please tell me the correct way to write this?
Replies
13
Boosts
0
Views
1.7k
Activity
Dec ’25
Neither macOS 14.7 "Standard" 'AppleUserHIDEventDriver' Matching Driver Nor Custom HIDDriverKit Driver 'IOUserHIDEventService::dispatchDigitizerTouchEvent' API Work for a HID-standard Digitizer Touch Pad Device
I have been working on a multi-platform multi-touch HID-standard digitizer clickpad device. The device uses Bluetooth Low Energy (BLE) as its connectivity transport and advertises HID over GATT. To date, I have the device working successfully on Windows 11 as a multi-touch, gesture-capable click pad with no custom driver or app on Windows. However, I have been having difficulty getting macOS to recognize and react to it as a HID-standard multi-touch click pad digitizer with either the standard Apple HID driver (AppleUserHIDEventDriver) or with a custom-coded driver extension (DEXT) modeled, based on the DTS stylus example and looking at the IOHIDFamily open source driver(s). The trackpad works with full-gesture support on Windows 11 and the descriptors seem to be compliant with the R23 Accessory Guidelines document, §15. With the standard, matching Apple AppleUserHIDEventDriver HID driver, when enumerating using stock-standard HID mouse descriptors, the device works fine on macOS 14.7 "Sonoma" as a relative pointer device with scroll wheel capability (two finger swipe generates a HID scroll report) and a single button. With the standard, matching Apple AppleUserHIDEventDriver HID driver, when enumerating using stock-standard HID digitizer click/touch pad descriptors (those same descriptors used successfully on Windows 11), the device does nothing. No button, no cursor, no gestures, nothing. Looking at ioreg -filtb, all of the key/value pairs for the driver match look correct. Because, even with the Apple open source IOHIDFamily drivers noted above, we could get little visibility into what might be going wrong, I wrote a custom DriverKit/HIDDriverKit driver extension (DEXT) (as noted above, based on the DTS HID stylus example and the open source IOHIDEventDriver. With that custom driver, I can get a single button click from the click pad to work by dispatching button events to dispatchRelativePointerEvent; however, when parsing, processing, and dispatching HID digitizer touch finger (that is, transducer) events via IOUserHIDEventService::dispatchDigitizerTouchEvent, nothing happens. If I log with: % sudo log stream --info --debug --predicate '(subsystem == "com.apple.iohid")' either using the standard AppleUserHIDEventDriver driver or our custom driver, we can see that our input events are tickling the IOHIDNXEventTranslatorSessionFilter HID event filter, so we know HID events are getting from the device into the macOS HID stack. This was further confirmed with the DTS Bluetooth PacketLogger app. Based on these events flowing in and hitting IOHIDNXEventTranslatorSessionFilter, using the standard AppleUserHIDEventDriver driver or our custom driver, clicks or click pad activity will either wake the display or system from sleep and activity will keep the display or system from going to sleep. In short, whether with the stock driver or our custom driver, HID input reports come in over Bluetooth and get processed successfully; however, nothing happens—no pointer movement or gesture recognition. STEPS TO REPRODUCE For the standard AppleUserHIDEventDriver: Pair the device with macOS 14.7 "Sonoma" using the Bluetooth menu. Confirm that it is paired / bonded / connected in the Bluetooth menu. Attempt to click or move one or more fingers on the touchpad surface. Nothing happens. For the our custom driver: Pair the device with macOS 14.7 "Sonoma" using the Bluetooth menu. Confirm that it is paired / bonded / connected in the Bluetooth menu. Attempt to click or move one or more fingers on the touchpad surface. Clicks are correctly registered. With transducer movement, regardless of the number of fingers, nothing happens.
Replies
4
Boosts
0
Views
1.1k
Activity
Dec ’25
DEXT (IOUserSCSIParallelInterfaceController): Direct I/O Succeeds, but Buffered I/O Fails with Data Corruption on Large File Copies
Hi all, We are migrating a SCSI HBA driver from KEXT to DriverKit (DEXT), with our DEXT inheriting from IOUserSCSIParallelInterfaceController. We've encountered a data corruption issue that is reliably reproducible under specific conditions and are hoping for some assistance from the community. Hardware and Driver Configuration: Controller: LSI 3108 DEXT Configuration: We are reporting our hardware limitations to the framework via the UserReportHBAConstraints function, with the following key settings: // UserReportHBAConstraints... addConstraint(kIOMaximumSegmentAddressableBitCountKey, 0x20); // 32-bit addConstraint(kIOMaximumSegmentCountWriteKey, 129); addConstraint(kIOMaximumByteCountWriteKey, 0x80000); // 512KB Observed Behavior: Direct I/O vs. Buffered I/O We've observed that the I/O behavior differs drastically depending on whether it goes through the system file cache: 1. Direct I/O (Bypassing System Cache) -> 100% Successful When we use fio with the direct=1 flag, our read/write and data verification tests pass perfectly for all file sizes, including 20GB+. 2. Buffered I/O (Using System Cache) -> 100% Failure at >128MB Whether we use the standard cp command or fio with the direct=1 option removed to simulate buffered I/O, we observe the exact same, clear failure threshold: Test Results: File sizes ≤ 128MB: Success. Data checksums match perfectly. File sizes ≥ 256MB: Failure. Checksums do not match, and the destination file is corrupted. Evidence of failure reproduced with fio (buffered_integrity_test.fio, with direct=1 removed): fio --size=128M buffered_integrity_test.fio -> Test Succeeded (err=0). fio --size=256M buffered_integrity_test.fio -> Test Failed (err=92), reporting the following error, which proves a data mismatch during the verification phase: verify: bad header ... at file ... offset 1048576, length 1048576 fio: ... error=Illegal byte sequence Our Analysis and Hypothesis The phenomenon of "Direct I/O succeeding while Buffered I/O fails" suggests the problem may be related to the cache synchronization mechanism at the end of the I/O process: Our UserProcessParallelTask_Impl function correctly handles READ and WRITE commands. When cp or fio (buffered) runs, the WRITE commands are successfully written to the LSI 3108 controller's onboard DRAM cache, and success is reported up the stack. At the end of the operation, to ensure data is flushed to disk, the macOS file system issues an fsync, which is ultimately translated into a SYNCHRONIZE CACHE SCSI command (Opcode 0x35 or 0x91) and sent to our UserProcessParallelTask_Impl. We hypothesize that our code may not be correctly identifying or handling this SYNCHRONIZE CACHE opcode. It might be reporting "success" up the stack without actually commanding the hardware to flush its cache to the physical disk. The OS receives this "success" status and assumes the operation is safely complete. In reality, however, the last batch of data remains only in the controller's volatile DRAM cache and is eventually lost. This results in an incomplete or incorrect file tail, and while the file size may be correct, the data checksum will inevitably fail. Summary Our DEXT driver performs correctly when handling Direct I/O but consistently fails with data corruption when handling Buffered I/O for files larger than 128MB. We can reliably reproduce this issue using fio with the direct=1 option removed. The root cause is very likely the improper handling of the SYNCHRONIZE CACHE command within our UserProcessParallelTask. P.S. This issue did not exist in the original KEXT version of the driver. We would appreciate any advice or guidance on this issue. Thank you.
Replies
13
Boosts
0
Views
743
Activity
Dec ’25
The total DMA size in DriverKit cannot exceed 2G?
We are developing a DriverKit driver on Apple M1. We use the following code to prepare DMA buffer: IODMACommandSpecification dmaSpecification; bzero(&dmaSpecification, sizeof(dmaSpecification)); dmaSpecification.options = kIODMACommandSpecificationNoOptions; dmaSpecification.maxAddressBits = p_dma_mgr->maxAddressBits; kret = IODMACommand::Create(p_dma_mgr->device, kIODMACommandCreateNoOptions, &dmaSpecification, &impl->dma_cmd ); if (kret != kIOReturnSuccess) { os_log(OS_LOG_DEFAULT, "Error: IODMACommand::Create failed! ret=0x%x\n", kret); impl->user_mem.reset(); IOFree(impl, sizeof(*impl)); return ret; } uint64_t flags = 0; uint32_t segmentsCount = 32; IOAddressSegment segments[32]; kret = impl->dma_cmd->PrepareForDMA(kIODMACommandPrepareForDMANoOptions, impl->user_mem.get(), 0, 0, // 0 for entire memory &flags, &segmentsCount, segments ); if (kret != kIOReturnSuccess) { OSSafeReleaseNULL(impl->dma_cmd); impl->user_mem.reset(); IOFree(impl, sizeof(*impl)); os_log(OS_LOG_DEFAULT, "Error: PrepareForDMA failed! ret=0x%x\n", kret); return kret; } I allocated several 8K BGRA video frames, each with a size of 141557760 bytes, and prepared the DMA according to the method mentioned above. The process was successful when the number of frames was 15 or fewer. However, issues arose when allocating 16 frames: Error: PrepareForDMA failed! ret=0xe00002bd By calculating, I found that the total size of 16 video frames exceeds 2GB. Is there such a limitation in DriverKit that the total DMA size cannot exceed 2GB? Are there any methods that would allow me to bypass this restriction so I can use more video frame buffers?
Replies
1
Boosts
0
Views
232
Activity
Dec ’25
Archive Fails: Conflict with Existing KEXT Developer ID Certificate
Hi everyone, We're trying to prepare a DriverKit App for a client test, and we've run into an unavoidable signing conflict that seems to be caused by the Xcode Archive process itself. Background & Environment: Environment: macOS 15.6.1, Xcode 16.4 Our project consists of a main App Target and a DEXT Target. Both the Debug and Release configurations for both targets are set to Xcode's default: Automatically manage signing. Our developer account holds a valid, active Developer ID Application (With Kext) certificate, which we use for signing our legacy KEXT. The Action That Triggers Failure: From this clean state, we execute Product -> Archive. The Archive process fails during the signing validation phase and presents the following three errors, completely halting the process: There is a problem with the request entity - You already have a current Developer ID Application Managed (With Kext) certificate... No profiles for 'com.company.Acxxx.driver' were found... No profiles for 'com.company.Acxxx.app' were found... This error seems to indicate that the Xcode Archive process: Ignores the project's Release configuration (even the default 'Auto' setting). Attempts to automatically create a new, standard Developer ID certificate for us. This action conflicts with the existing (With Kext) certificate in our account, causing the entire Archive process to fail. The "Failed Experiment" to Resolve This: To work around this automation conflict, we tried the solution: configuring a fully manual signing process for the Release configuration to explicitly tell Xcode to use our existing KEXT certificate. Our Steps: We disabled automatic signing for both the App and DEXT targets for the Release configuration and manually assigned the Developer ID Provisioning Profiles created for our Developer ID (With Kext) certificate. The New Problem: After doing this, the Signing Certificate field for the DEXT Target's Signing & Capabilities interface now shows None, accompanied by the misleading warning about needing a DriverKit development profile. The Outcome: This None issue now prevents us from even starting the Archive process, as the project fails to build due to the incorrect signing configuration. We've tried every debugging step — including rebuilding profiles, validating the keychain, and clearing caches — but nothing resolves this None issue. Our Dilemma: State A (Fully Automatic Signing): The Archive process fails due to the KEXT certificate conflict. State B (Manual Release Signing): The project fails to build due to the Signing Certificate: None issue, preventing us from initiating an Archive. For a development team holding a KEXT Developer ID certificate, how should an Xcode project be configured when migrating to DriverKit, so that the Archive process: Does not trigger the flawed automation logic that attempts to create a new certificate? And, does not fall into the Signing Certificate: None configuration trap? Related Forum Threads We've Studied: https://developer.apple.com/forums/thread/781932 https://developer.apple.com/forums/thread/751490 https://developer.apple.com/forums/thread/767152 https://developer.apple.com/forums/thread/721563 Best Reagrds, Charles
Replies
2
Boosts
0
Views
574
Activity
Dec ’25
INQUIRY command is ILLEGAL REQUEST
I am developing a DriverKit driver with the goal of sending vendor-specific commands to a USB storage device. I have successfully created the DriverKit driver, and when I connect the USB storage device, it appears correctly in IORegistryExplorer. My driver class inherits from IOUserSCSIPeripheralDeviceType00 in the SCSIPeripheralsDriverKit framework. I also created a UserClient class that inherits from IOUserClient, and from its ExternalMethod I tried sending an INQUIRY command as a basic test to confirm that command transmission works. However, the device returns an ILLEGAL REQUEST (Sense Key 0x5 / ASC 0x20). Could someone advise what I might be doing wrong? Below are the logs output from the driver: 2025-11-14 21:00:43.573730+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] Driver - NewUserClient() - Finished. 2025-11-14 21:00:43.573733+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - Start() 2025-11-14 21:00:43.573807+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - Start() - Finished. 2025-11-14 21:00:43.574249+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - ExternalMethod() called 2025-11-14 21:00:43.574258+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - ----- SCSICmdINQUIRY ----- 2025-11-14 21:00:43.574268+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - command.fRequestedByteCountOfTransfer = 512 2025-11-14 21:00:43.575980+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - SCSICmdINQUIRY() UserSendCDB fCompletionStatus = 0x0 2025-11-14 21:00:43.575988+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - SCSICmdINQUIRY() UserSendCDB fServiceResponse = 0x2 2025-11-14 21:00:43.575990+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - SCSICmdINQUIRY() UserSendCDB fSenseDataValid = 0x1 2025-11-14 21:00:43.575992+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - SCSICmdINQUIRY() UserSendCDB VALID_RESPONSE_CODE = 0x70 2025-11-14 21:00:43.575994+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - SCSICmdINQUIRY() UserSendCDB SENSE_KEY = 0x5 2025-11-14 21:00:43.575996+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - SCSICmdINQUIRY() UserSendCDB ADDITIONAL_SENSE_CODE = 0x20 2025-11-14 21:00:43.575998+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - SCSICmdINQUIRY() UserSendCDB ADDITIONAL_SENSE_CODE_QUALIFIER = 0x0 Here is the UserClient class: class SampleDriverKitUserClient: public IOUserClient { public: virtual bool init(void) override; virtual kern_return_t Start(IOService* provider) override; virtual kern_return_t Stop(IOService* provider) override; virtual void free(void) override; virtual kern_return_t ExternalMethod( uint64_t selector, IOUserClientMethodArguments* arguments, const IOUserClientMethodDispatch* dispatch, OSObject* target, void* reference) override; void SCSICmdINQUIRY(SampleDriverKitDriver *driver) LOCALONLY; }; Here is the part that sends the INQUIRY command: void SampleDriverKitUserClient::SCSICmdINQUIRY(SampleDriverKitDriver *driver) { kern_return_t kr = KERN_SUCCESS; SCSIType00OutParameters command = {}; UInt8 dataBuffer[512] = {0}; SCSI_Sense_Data senseData = {0}; Log("----- SCSICmdINQUIRY -----"); SetCommandCDB(&command.fCommandDescriptorBlock, 0x12, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); command.fLogicalUnitNumber = 0; command.fTimeoutDuration = 10000; // milliseconds command.fRequestedByteCountOfTransfer = sizeof(dataBuffer); Log("command.fRequestedByteCountOfTransfer = %lld", command.fRequestedByteCountOfTransfer); command.fBufferDirection = kIOMemoryDirectionIn; command.fDataTransferDirection = kSCSIDataTransfer_FromTargetToInitiator; command.fDataBufferAddr = reinterpret_cast<uint64_t>(dataBuffer); command.fSenseBufferAddr = reinterpret_cast<uint64_t>(&senseData); command.fSenseLengthRequested = sizeof(senseData); if( driver ) { SCSIType00InParameters response = {}; kr = driver->UserSendCDB(command, &response); if( kr != KERN_SUCCESS ) { Log("SCSICmdINQUIRY() UserSendCDB failed (0x%x)", kr); return; } Log("SCSICmdINQUIRY() UserSendCDB fCompletionStatus = 0x%x", response.fCompletionStatus); Log("SCSICmdINQUIRY() UserSendCDB fServiceResponse = 0x%x", response.fServiceResponse); Log("SCSICmdINQUIRY() UserSendCDB fSenseDataValid = 0x%x", response.fSenseDataValid); Log("SCSICmdINQUIRY() UserSendCDB VALID_RESPONSE_CODE = 0x%x", senseData.VALID_RESPONSE_CODE); Log("SCSICmdINQUIRY() UserSendCDB SENSE_KEY = 0x%x", senseData.SENSE_KEY); Log("SCSICmdINQUIRY() UserSendCDB ADDITIONAL_SENSE_CODE = 0x%x", senseData.ADDITIONAL_SENSE_CODE); Log("SCSICmdINQUIRY() UserSendCDB ADDITIONAL_SENSE_CODE_QUALIFIER = 0x%x", senseData.ADDITIONAL_SENSE_CODE_QUALIFIER); if( response.fServiceResponse == kSCSIServiceResponse_TASK_COMPLETE ) { Log("SCSICmdINQUIRY() UserSendCDB complete success!!"); } for( int i=0; i < 5; i++ ) { Log("data [%04d]=0x%x [%04d]=0x%x [%04d]=0x%x [%04d]=0x%x [%04d]=0x%x [%04d]=0x%x [%04d]=0x%x [%04d]=0x%x", i*8+0, dataBuffer[i*8+0], i*8+1, dataBuffer[i*8+1], i*8+2, dataBuffer[i*8+2], i*8+3, dataBuffer[i*8+3], i*8+4, dataBuffer[i*8+4], i*8+5, dataBuffer[i*8+5], i*8+6, dataBuffer[i*8+6], i*8+7, dataBuffer[i*8+7] ); } char vendorID[9] = {0}; memcpy(vendorID, &dataBuffer[8], 8); Log("vendorID = %s",vendorID); char productID[17] = {0}; memcpy(productID, &dataBuffer[16], 16); Log("productID = %s",productID); } } My environment is: MacBook Pro (M2), macOS 15.6 If anyone has insight into what causes the ILLEGAL REQUEST, or what I am missing when using IOUserSCSIPeripheralDeviceType00 and UserSendCDB, I would greatly appreciate your help. Thank you.
Replies
1
Boosts
0
Views
469
Activity
Nov ’25
"How to" for dext distribution
I have a DriverKit system extension (dext) that uses PCIDriverKit. I would like to get the build environment straightened out to successfully distribute the dext and associated software to end users. There are three types of software involved: The Dext-hosting application - this is the application that must be installed to /Applications/, and will perform the registration of the dext. The dext is deployed "within" this application, and can be found in the /Contents/Library/SystemExtensions folder of the app bundle. The dext itself - this is the actual binary system extension, which will be registered by its owning application, and will operate in its own application space independent of the hosting application. Additional applications that communicate with the dext - these are applications which will connect to the dext through user clients, but these applications do not contain the dext themselves. There are multiple locations where settings need to be exactly correct for each type of software to be signed, provisioned, and notarized properly in order to be distributed to users: developer.apple.com - where "identifiers" and "provisioning profiles" are managed. Note that there are differences in access between "Team Agent", "Admin", and "Developer" at this site. Xcode project's Target "Signing & Capabilities" tab - this is where "automatically manage signing" can be selected, as well as team selection, provisioning profile selection, and capabilities can be modified. Xcode project's Target "Build Settings" tab - this is where code signing identity, code signing development team, code signing entitlements file selection, Info.plist options and file selection, and provisioning profile selection. Xcode's Organizer window, which is where you manage archives and select for distribution. In this case, I am interested in "Developer ID" Direct Distribution - I want the software signed with our company's credentials (Team Developer ID) so that users know they can trust the software. Choosing "automatically manage signing" does not work for deployment. The debug versions of software include DriverKit (development) capability (under App ID configuration at developer.apple.com), and this apparently must not be present in distributable provisioning. I believe this means that different provisioning needs to occur between debug and release builds? I have tried many iterations of selections at all the locations, for all three types of binaries, and rather than post everything that does not work, I am asking, "what is supposed to work?"
Replies
20
Boosts
0
Views
2.9k
Activity
Nov ’25
Using Adhoc Profile for DriverKit show error: Xcode 14 and later requires a DriverKit development profile enabled for iOS and macOS
We got an app for iPad which has two targets one for the App itself (MainApp target ) and another one for the Driver ( Driver Target ) using DriverKit. The app works fine in Development, but I'm trying to distribute it with adhoc. I've requested the Distribution Entitlement to Apple, after getting it, the App Id for the Driver has the following Capabilities: DriverKit, DriverKit (development), DriverKit USB Transport (development), DriverKit USB Transport - VendorID, In-App Purchase Now in the profile section, I've created a adhoc profile for the Driver AppId (Identifier). Obviously I've also created an Adhoc profile for the Main AppId Finally in the Signing & Capabilities Section I set up the profiles for MainApp target, int the Debug one I set up the Development one and int the Release one I set up the adhoc one. I do the same in the Driver Target, but when I set up the Adhoc one in the Release, I've got a warning: Xcode 14 and later requires a DriverKit development profile enabled for iOS and macOS. Visit the developer website to create or download a DriverKit profile Also interestingly the Signing Certificate section says: None I also set up the Capabilities for the Driver Target: DriverKit USB Transport - VendorID DriverKit USB Transport ( Development ) Inside these capabilities I set up the vendor ID as dictionary The problem is, if I try to Archive the app I will get the previous Warning message as error: Xcode 14 and later requires a DriverKit development profile enabled for iOS and macOS. Visit the developer website to create or download a DriverKit profile. Any idea what I'm missing? Thanks
Replies
2
Boosts
0
Views
1.1k
Activity
Nov ’25
DriverKit IOUserSerial Driver
Hello everyone. After a lot of research and some tests from various sources, I have actually built a small SerialDriverKit IOUserSerial driver. Unfortunately, the documentation on the official sites is tight-lipped and very thin. At least I have a running driver instance. Now my request and question: Can anyone give me a tip on how to get the data from the serial client? I have already called IOUserSerial::ConnectQueues(...) in the IOUserSerial::Start() method and I got the IOMemoryDescriptors for interrupt, RX and TX to my driver instance. I tried to get access to the memory in the method IOUserSerial::TxDataAvailable() with IOMemoryDescriptor::CreateMapping(...). Unfortunately, no data is coming in. It's always 0x00. Here is the OS log: kernel: (org.eof.tools.VSPDriver.dext) kernel: (org.eof.tools.VSPDriver.dext) [VSPDriver] init called. kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] constructor called. kernel: (org.eof.tools.VSPDriver.dext) [VSPDriver] start called. kernel: (org.eof.tools.VSPDriver.dext) IOUserSerial::: 40 0x600000da4058 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] Start called. kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] Connect INT/RX/TX buffer. kernel: (org.eof.tools.VSPDriver.dext) IOUserSerial::: 59 0x600000da4058 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] prepare TCP socket. kernel: (org.eof.tools.VSPDriver.dext) [VSPDriver] driver started successfully. kernel: DK: VSPDriver-0x100000753::start(IOUserResources-0x100000116) ok ... ... some client serial setup stuff ... kernel: (IOUserSerial) IOUserSerial::hwResetFIFO: 1076 ==&gt;0 kernel: (IOUserSerial) IOUserSerial::hwResetFIFO: 1076 &lt;== kernel: (IOUserSerial) IOUserSerial::hwResetFIFO: 1076 locklevel = 1 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriver] HwResetFIFO called. kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] HwResetFIFO called. kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] HwResetFIFO: tx=0 rx=1 kernel: (IOUserSerial) IOUserSerial::hwResetFIFO: 1076 ==&gt;0 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriver] TxDataAvailable called. kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable called. kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: address=0x104c22000 length=16384 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: debug TX buffer kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00 kernel: (org.eof.tools.VSPDriver.dext) [VSPDriverPrivate] TxDataAvailable: TX&gt; 0x00
Replies
20
Boosts
1
Views
1.6k
Activity
Nov ’25
Architectural Performance Difference in External Storage I/O Between Intel and Apple Silicon Macs
Hi everyone, We are in the process of migrating a legacy KEXT for our external multi-disk RAID enclosure to the modern DriverKit framework. During the performance validation of our KEXT, we observed a large and consistent maximum throughput difference between Intel-based Macs and Apple Silicon-based Macs. We would like to share our findings and hope to discuss with others in the community to see if you have had similar experiences that could confirm or correct our understanding. The Observation: A Consistent Performance Gap When using the exact same external RAID hardware (an 8-HDD RAID 5 array), driven by our mature KEXT, we see the following results in high-throughput benchmarks (AJA System Test, large sequential writes): On a 2020 Intel-based Mac: We consistently achieve a throughput of ~2500 MB/s. On modern M-series Macs (from M1 to M4): The throughput is consistently capped at ~1500 MB/s. This performance difference of nearly 40% is significant and is present across the entire Apple Silicon product line. Our Hypothesis: A Shift in Architectural Design Philosophy Since the KEXT and external hardware are identical in both tests, we believe this performance difference is not a bug but a fundamental platform architecture distinction. Our hypothesis is as follows: 1. The Intel Mac Era ("Dedicated Throughput") The Intel-based Macs we tested use a dedicated, discrete Intel Thunderbolt controller chip. This chip has its own dedicated PCIe lanes and resources, and its design appears to be singularly focused on maximizing raw, sustained data throughput for external peripherals. 2. The Apple Silicon Era ("Integrated Efficiency") In contrast, M-series Macs use a deeply integrated I/O controller inside the SoC. This controller must share resources, such as the total unified memory bandwidth and the chip's overall power budget, with all other functional units (CPU, GPU, etc.). We speculate that the design priority for this integrated I/O controller has shifted from "maximizing single-task raw throughput" to "maximizing overall system efficiency, multi-task responsiveness, and low latency." As a result, in a pure, single-task storage benchmark, its performance ceiling may be lower than that of the older, dedicated-chip architecture. Our Question to the Community: Is our understanding correct? Have other developers of high-performance storage drivers or peripherals also observed a similar performance ceiling for external storage on Apple Silicon Macs, when compared to high-end Intel Macs? We believe that understanding this as a deliberate architectural trade-off is crucial for setting realistic performance targets for our DEXT. Our current goal has been adjusted to have our DEXT match the KEXT's ~1500 MB/s on the M-series platform. Any insights, confirmations, or corrections from the community or Apple engineers would be greatly appreciated. Thank you very much! Charles
Replies
1
Boosts
0
Views
356
Activity
Nov ’25
[DEXT Migration Issue] IOUserSCSIParallelInterfaceController fails to handle low-level I/O from `diskutil`
Hello everyone, We are migrating our KEXT for a Thunderbolt storage device to a DEXT based on IOUserSCSIParallelInterfaceController. We've run into a fundamental issue where the driver's behavior splits based on the I/O source: high-level I/O from the file system (e.g., Finder, cp) is mostly functional (with a minor ls -al sorting issue for Traditional Chinese filenames), while low-level I/O directly to the block device (e.g., diskutil) fails or acts unreliably. Basic read/write with dd appears to be mostly functional. We suspect that our DEXT is failing to correctly register its full device "personality" with the I/O Kit framework, unlike its KEXT counterpart. As a result, low-level I/O requests with special attributes (like cache synchronization) sent by diskutil are not being handled correctly by the IOUserSCSIParallelInterfaceController framework of our DEXT. Actions Performed & Relevant Logs 1. Discrepancy: diskutil info Shows Different Device Identities for DEXT vs. KEXT For the exact same hardware, the KEXT and DEXT are identified by the system as two different protocols. KEXT Environment: Device Identifier: disk5 Protocol: Fibre Channel Interface ... Disk Size: 66.0 TB Device Block Size: 512 Bytes DEXT Environment: Device Identifier: disk5 Protocol: SCSI SCSI Domain ID: 2 SCSI Target ID: 0 ... Disk Size: 66.0 TB Device Block Size: 512 Bytes 2. Divergent I/O Behavior: Partial Success with Finder/cp vs. Failure with diskutil High-Level I/O (Partially Successful): In the DEXT environment, if we operate on an existing volume (e.g., /Volumes/MyVolume), file copy operations using Finder or cp succeed. Furthermore, the logs we've placed in our single I/O entry point, UserProcessParallelTask_Impl, are triggered. Side Effect: However, running ls -al on such a volume shows an incorrect sorting order for files with Traditional Chinese names (they appear before . and ..). Low-Level I/O (Contradictory Behavior): In the DEXT environment, when we operate directly on the raw block device (/dev/disk5): diskutil partitionDisk ... -> Fails 100% of the time with the error: Error: -69825: Wiping volume data to prevent future accidental probing failed. dd command -> Basic read/write operations appear to work correctly (a write can be immediately followed by a read within the same DEXT session, and the data is correct). 3. Evidence of Cache Synchronization Failure (Non-deterministic Behavior) The success of the dd command is not deterministic. Cross-environment tests prove that its write operations are unreliable: First Test: In the DEXT environment, write a file with random data to /dev/disk5 using dd. Reboot into the KEXT environment. Read the data back from /dev/disk5 using dd. The result is a file filled with all zeros. Conclusion: The write operation only went to the hardware cache, and the data was lost upon reboot. Second Test: In the DEXT environment, write the same random file to /dev/disk5 using dd. Key Variable: Immediately after, still within the DEXT environment, read the data back once for verification. The content is correct! Reboot into the KEXT environment. Read the data back from /dev/disk5. This time, the content is correct! Conclusion: The additional read operation in the second test unintentionally triggered a hardware cache flush. This proves that the dd (in our DEXT) write operation by itself does not guarantee synchronization, making its behavior unreliable. Our Problem Based on the observations above, we have the conclusion: High-Level Path (triggered by Finder/cp): When an I/O request originates from the high-level file system, the framework seems to enter a fully-featured mode. In this mode, all SCSI commands, including READ/WRITE, INQUIRY, and SYNCHRONIZE CACHE, are correctly packaged and dispatched to our UserProcessParallelTask_Impl entry point. Therefore, Finder operations are mostly functional. Low-Level Path (triggered by dd/diskutil): When an I/O request originates from the low-level raw block device layer: The most basic READ/WRITE commands can be dispatched (which is why dd appears to work). However, critical management commands, such as INQUIRY and SYNCHRONIZE CACHE, are not being correctly dispatched or handled. This leads to the incorrect device identification in diskutil info and the failure of diskutil partitionDisk due to its inability to confirm cache synchronization. We would greatly appreciate any guidance, suggestions, or insights on how to resolve this discrepancy. Specifically, what is the recommended approach within DriverKit to ensure that a DEXT based on IOUserSCSIParallelInterfaceController can properly declare its capabilities and handle both high-level and low-level I/O requests uniformly? Thank you. Charles
Replies
12
Boosts
0
Views
626
Activity
Nov ’25
How to allocate contiguous memory in DriverKit?
We want to allocate a block of contiguous memory (≤1M) for audio ring DMA usage, but we haven't found any explicit method in the DriverKit documentation for allocating contiguous memory. I'm aware that IOBufferMemoryDescriptor::Create can be used in DriverKit to allocate memory and share it with user space. However, is the allocated memory physically contiguous? Can it guarantee that when I subsequently call PrepareForDMA in IODMACommand, there will be only one segment? Could you please help review this? Thank you!
Replies
2
Boosts
0
Views
280
Activity
Oct ’25
How to completely uninstall the old kext driver?
Hi, On macOS 11 and earlier versions, we provided users with the following script to uninstall our kext driver: sudo pkgutil --only-files --files com.magewell.ProCapture | tr '\n' '\0' | xargs -n 1 -0 sudo rm -vf sudo pkgutil --only-dirs --files com.magewell.ProCapture | grep ProCapture[^/]*$ | tr '\n' '\0' | xargs -n 1 -0 sudo rm -rvf sudo pkgutil --forget com.magewell.ProCapture sudo kextcache -system-caches However, this script no longer works on macOS 13 and returns the following error: It looks like you're trying to update the system caches. As of macOS 11, the personality cache is no longer in use for keeping kext matching information up-to-date. For more information, see `man kmutil`. This indicates we can no longer use kextcache -system-caches to clear our driver cache. This creates an issue where even after installing the new dext driver, the dext driver cannot run due to the presence of the old kext driver. We've tried various methods but haven't been able to completely uninstall the old kext driver - after every new system update, the old kext reappears. The specific process is as follows: This is the sequence I followed in my latest test - Device is running macOS 13 Ventura w/ 4247 Pro Capture kext driver installed kmutil inspect | grep -i magewell - this returns references to the kext files in /Library/Extensions, which is expected because I have not yet removed the 4247 kext driver - then I ran the following combination of your removal script and my removal steps: cd / sudo rm -r /Library/Extensions/ProCaptureDriver.kext sudo rm -r /Library/Extensions/ProCaptureEvent.kext sudo rm /System/Volumes/Preboot/*/boot/*/System/Library/Caches/com.apple.kernelcaches/kernelcache.auxkc* sudo pkgutil --only-files --files com.magewell.ProCapture | tr '\n' '\0' | xargs -n 1 -0 sudo rm -vf sudo pkgutil --only-dirs --files com.magewell.ProCapture | grep ProCapture[^/]*$ | tr '\n' '\0' | xargs -n 1 -0 sudo rm -rvf sudo pkgutil --forget com.magewell.ProCapture sudo kextcache --clear-staging sudo kcditto sudo kmutil install --update-preboot sudo shutdown -r now - After this I ran 'kmutil inspect | grep -i magewell' and got no results, which seems good but... - then I ran the upgrade to macOS 15.7 Sequoia - Afterwards I ran 'kmutil inspect | grep -i magewell' and it returned references to the old /Library/Extensions kexts again, although the files no longer exist in /Library/Extensions - I then ran my cleanup process again (slightly different for Sequoia-available commands): sudo rm /System/Volumes/Preboot/*/boot/*/System/Library/Caches/com.apple.kernelcaches/kernelcache.auxkc* sudo kextcache --clear-staging sudo kmutil rebuild sudo kcditto sudo kmutil install --update-preboot sudo shutdown -r now - Then I ran 'kmutil inspect | grep -i magewell' and got no results again - As a next test I ran a minor update to macOS 15.7.1, then ran 'kmutil inspect | grep -i magewell' and the references to the old kexts came back again We have indeed identified a solution to address this issue: kmutil trigger-panic-medic --volume-root /Volumes/<YourVolumeName> However, this method requires booting into Recovery Mode, which is unacceptable for many of our customers. Especially for those who need bulk remote management, having personnel physically operate each machine one by one is simply not feasible. Therefore, is there a method to completely uninstall the kext driver while in normal mode? Thank you!
Replies
3
Boosts
0
Views
364
Activity
Oct ’25
Should UserSendCBD work on UAS interfaces?
The device I am trying to develop a firmware updater for is an NVMe drive with a USB4 interface. It can connect in USB4 mode (tunneled NVMe), in USB 3 mode or in USB 2 mode. In USB 2 and USB 3 mode, the device descriptor shows one interface with two alternates. Alternate 0 uses the bulk-only protocol, with one IN and one OUT pipe. Alternate 1 uses the UAS protocol, with two IN and two OUT pipes. I use identical code in my driver to send custom CDBs. I can see using IORegistryExplorer that in USB 2 mode, macOS chooses alternate 0, the bulk-only protocol. My custom CDBs and their accompanying data pay loads are put on the bus, more or less as expected. In USB 3 mode, macOS chooses alternate 1, the UAS protocol. My custom CDB is put on the bus, but no payload data is transferred. Is this expected behavior? If so, is there a way to force the OS to choose alternate 0 even when on USB 3, perhaps with another dext? I'll file a bug about this when Feedback Assistant lets me.
Replies
8
Boosts
0
Views
516
Activity
Oct ’25
DEXT receives zero-filled buffer from DMA, despite firmware confirming data write
Hello everyone, I am migrating a KEXT for a SCSI PCI RAID controller (LSI 3108 RoC) to DriverKit (DEXT). While the DEXT loads successfully, I'm facing a DMA issue: an INQUIRY command results in a 0-byte disk because the data buffer received by the DEXT is all zeros, despite our firmware logs confirming that the correct data was prepared and sent. We have gathered detailed forensic evidence and would appreciate any insights from the community. Detailed Trace of a Failing INQUIRY Command: 1, DEXT Dispatches the Command: Our UserProcessParallelTask implementation correctly receives the INQUIRY task. Logs show the requested transfer size is 6 bytes, and the DEXT obtains the IOVA (0x801c0000) to pass to the hardware. DEXT Log: [UserProcessParallelTask_Impl] --- FORENSIC ANALYSIS --- [UserProcessParallelTask_Impl] fBufferIOVMAddr = 0x801c0000 [UserProcessParallelTask_Impl] fRequestedTransferCount = 6 2, Firmware Receives IOVA and Prepares Correct Data: A probe in our firmware confirms that the hardware successfully received the correct IOVA and the 6-byte length requirement. The firmware then prepares the correct 6-byte INQUIRY response in its internal staging buffer. Firmware Logs: -- [FIRMWARE PROBE: INCOMING DMA DUMP] -- Host IOVA (High:Low) = 0x00000000801c0000 DataLength in Header = 6 (0x6) --- [Firmware Outgoing Data Dump from go_inquiry] --- Source Address: 0x228BB800, Length: 6 bytes 0x0000: 00 00 05 12 1F 00 3, Hardware Reports a Successful Transfer, but Data is Lost: After the firmware initiates the DMA write to the Host IOVA, the hardware reports a successful transfer of 6 bytes back to our DEXT. DEXT Completion Log: [AME_Host_Normal_Handler_SCSI_Request] [TaskID: 200] COMPLETING... [AME_Host_Normal_Handler_SCSI_Request] Hardware Transferred = 6 bytes [AME_Host_Normal_Handler_SCSI_Request] - ReplyStatus = SUCCESS (0x0) [AME_Host_Normal_Handler_SCSI_Request] - SCSIStatus = SUCCESS (0x0) The Core Contradiction: Despite the firmware preparing the correct data and the hardware reporting a successful DMA transfer, the fDataBuffer in our DEXT remains filled with zeros. The 6 bytes of data are lost somewhere between the PCIe bus and host memory. This "data-in-firmware, zeros-in-DEXT" phenomenon leads us to believe the issue lies in memory address translation or a system security policy, as our legacy KEXT works perfectly on the same hardware. Compared to a KEXT, are there any known, stricter IOMMU/security policies for a DEXT that could cause this kind of "silent write failure" (even with a correct IOVA)? Alternatively, what is the correct and complete expected workflow in DriverKit for preparing an IOMemoryDescriptor* fDataBuffer (received in UserProcessParallelTask) for a PCI hardware device to use as a DMA write target? Any official documentation, examples, or advice on the IOMemoryDescriptor to PCI Bus Address workflow would be immensely helpful. Thank you. Charles
Replies
5
Boosts
0
Views
479
Activity
Oct ’25
DriverKit DEXT Logs Only Once, Subsequent Logs Are Lost Despite Normal Functionality
Hello everyone, I've encountered a very strange and persistent logging issue with my DriverKit DEXT and would appreciate any insights from the community. [Problem Summary] My DriverKit DEXT, along with its companion Swift app, is functionally working perfectly. I can repeatedly call methods in the DEXT from the app (e.g., a Ping-Pong test and a StaticProcessInbandTask call) and receive the correct response every time. However, the os_log messages within my IOUserClient subclass are only successfully recorded for the very first set of interactions. After that, all subsequent logs are completely missing. What's even stranger is that all successfully recorded logs are attributed to the kernel: process, even for purely user-space methods like ExternalMethod. [Development Environment] macOS: 15.7.1 Xcode: 16.4 Hardware: MacBook Pro M1 DEXT Logging Macro (Log.h): #include <os/log.h> #define Log(fmt, ...) \ do { \ os_log(OS_LOG_DEFAULT, "[%{public}s] " fmt, __FUNCTION__, ##__VA_ARGS__); \ } while (0) [Steps to Reproduce & Observed Behavior] The DEXT is successfully loaded via the companion app. I click the "Ping-Pong" button, then the "Process InBand" button in the app. The app's UI log correctly shows that the request was sent and a successful response was received from the DEXT. I repeat step 2 multiple times. Each interaction works flawlessly from the app's perspective. I then use the log show command to export the logs from this period, for example: log show --last 5m | grep "com.accusys.Acxxx.driver" > dext_logs.txt Observed Result (Log Content): In the dext_logs.txt file, I can only see the logs from the very first Ping-Pong and the very first Process InBand call. All subsequent, successful operations leave no trace in the logs. kernel: (com.accusys.Acxxx.driver.dext) [ExternalMethod] // { --- kernel: (com.accusys.Acxxx.driver.dext) [ExternalMethod] // --- } kernel: (com.accusys.Acxxx.driver.dext) [StaticPingPong] // { --- kernel: (com.accusys.Acxxx.driver.dext) [StaticPingPong] // --- } kernel: (com.accusys.Acxxx.driver.dext) [ExternalMethod] // { --- kernel: (com.accusys.Acxxx.driver.dext) [ExternalMethod] // --- } kernel: (com.accusys.Acxxx.driver.dext) [StaticProcessInbandTask] // { --- kernel: (com.accusys.Acxxx.driver.dext) [StaticProcessInbandTask] // --- } <--- END OF FILE (No new logs appear after this point) ---> [Core Questions] Why are logs in IOUserClient subclass only recorded once? Given the DEXT is clearly still running and processing requests, why would os_log calls only succeed in writing to the system log database on the first interaction? Why are all logs attributed to the kernel? Why would logs from 100% user-space code like ExternalMethod and StaticPingPong be attributed to the kernel process? [Solutions Attempted That Did Not Work] I have verified with ps aux that the DEXT process (com.accusys.Acxxx.driver) is running continuously in the background and has not crashed. Attempted to force-restart the logging service with sudo killall logd, but the issue persists. Performed the most thorough reset possible using systemextensionsctl reset followed by a full reboot, then reinstalled the DEXT. The issue remains exactly the same. Thank you for any possible help or suggestions Best, Charles
Replies
3
Boosts
0
Views
242
Activity
Oct ’25
CreateMemoryDescriptorFromClient can't write data to user?
We've developed a PCIDriverKit driver for the capture card on macOS and have identified an issue: CreateMemoryDescriptorFromClient can only read data from the user space to the driver, but cannot write data back to the user. typedef struct _MWCAP_EDID_DATA { uint64_t size; uint64_t uaddr; } MWCAP_EDID_DATA; // App size_t xxx::GetEdid(void *buff, size_t size) { MWCAP_EDID_DATA edid; edid.size = size; edid.uaddr = (uint64_t)buff; kr = IOConnectCallStructMethod( connect, kUserGetEdid, &edid, sizeof(MWCAP_EDID_DATA), NULL, NULL ); // kr is 0. But However, the data in the buffer remains unchanged; // it does not reflect the EDID copied from the DEXT. return size; } // Driver MWCAP_EDID_DATA *edid = (MWCAP_EDID_DATA *)input; IOMemoryDescriptor *user_buf_mem = NULL; IOAddressSegment segment; segment.address = edid->uaddr; segment.length = edid->size; // We have verified that the values in edid->uaddr and edid->size are consistent with what was set by the application. ret = CreateMemoryDescriptorFromClient(kIOMemoryDirectionOutIn, 1, &segment, &user_buf_mem); if (ret != kIOReturnSuccess) { os_log(OS_LOG_DEFAULT, "Failed to create memdesc with error: 0x%08x", ret); break; } IOMemoryMap* user_buf_map = nullptr; ret = user_buf_mem->CreateMapping(0, 0, 0, 0, 0, &user_buf_map); if (ret != kIOReturnSuccess) { os_log(OS_LOG_DEFAULT, "Failed to create mapping with error: 0x%08x", ret); OSSafeReleaseNULL(user_buf_mem); break; } // ... fill the user_buf_map with edid data ... // For example: // memcpy(user_buf_map->GetAddress(), source_edid_data, edid->size); // At this point we have also verified the data in user_buf_map->GetAddress(), which matches our expectations. OSSafeReleaseNULL(user_buf_map); OSSafeReleaseNULL(user_buf_mem); Please help take a look, thank you!
Replies
1
Boosts
0
Views
290
Activity
Oct ’25
*.ips CrashReport not always available when dext crashes
While developing our driver, we've noticed that the *.ips report that contains the stacktrace of the crash is not always generated. I'm wondering why this report may not get generated, or if there's anything specific to do to guarantee it gets generated.
Replies
1
Boosts
3
Views
270
Activity
Oct ’25