Hello @all
I'm develop a DriverKit driver extension and without entitlement checks by OS everything runs fine. But if the entitlements check is enabled in the NVRAM then I get an error due connecting my IOUserClient instance. Which entitlements are really and exactly required for my driver?
My driver contains:
one IOUserClient instance
and multiple IOUserSerial instances
The bundle identifier of the driver ist:
org.eof.tools.VSPDriver
The bundle identifier of the client app
org.eof.tools.VSPInstall
My entire source code is available on GitHub if any one want to dive deep in :)
kernel[0:5107] () [VSPDriver]: NewUserClient called.
kernel[0:5107] () [VSPDriver]: CreateUserClient: create VSP user client from Info.plist.
kernel[0:5107] () [VSPUserClient]: init called.
kernel[0:5107] () [VSPUserClient]: init finished.
kernel[0:5107] () [VSPDriver]: CreateUserClient: check VSPUserClient type.
kernel[0:5107] () [VSPDriver]: CreateUserClient: success.
kernel[0:5107] () [VSPDriver]: NewUserClient finished.
kernel[0:5107] () [VSPUserClient]: Start: called.
kernel[0:5107] () [VSPUserClient]: User client successfully started.
kernel[0:389f] DK: VSPUserClient-0x100001127:UC failed userclient-access check, needed bundle ID org.eof.tools.VSPDriver
kernel[0:389f] DK: VSPUserClient-0x100001127:UC entitlements check failed
kernel[0:5107] () [VSPUserClient]: Stop called.
kernel[0:5107] () [VSPUserClient]: User client successfully removed.
kernel[0:5107] () [VSPUserClient]: free called.
Here my drivers entitlement file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.driverkit</key>
<true/>
<key>com.apple.developer.driverkit.allow-third-party-userclients</key>
<true/>
<key>com.apple.developer.driverkit.family.serial</key>
<true/>
</dict>
</plist>
Here my drivers Info.plist file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2025 by EoF Software Labs</string>
<key>OSBundleUsageDescription</key>
<string>Provide virtual serial port</string>
<key>com.apple.developer.driverkit</key>
<true/>
<key>com.apple.developer.driverkit.allow-any-userclient-access</key>
<true/>
<key>com.apple.developer.driverkit.communicates-with-drivers</key>
<true/>
<key>com.apple.developer.system-extension.redistributable</key>
<true/>
<key>OSBundleLibraries</key>
<dict>
<key>com.apple.iokit.IOSerialFamily</key>
<string>1.0</string>
</dict>
<key>IOKitPersonalities</key>
<dict>
<key>VSPDriver</key>
<dict>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleIdentifierKernel</key>
<string>com.apple.kpi.iokit</string>
<key>IOMatchCategory</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>IOProviderClass</key>
<string>IOUserResources</string>
<key>IOResourceMatch</key>
<string>IOKit</string>
<key>IOProbeScore</key>
<integer>0</integer>
<key>IOClass</key>
<string>IOUserService</string>
<key>IOUserClass</key>
<string>VSPDriver</string>
<key>IOUserServerName</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>UserClientProperties</key>
<dict>
<key>IOClass</key>
<string>IOUserUserClient</string>
<key>IOUserClass</key>
<string>VSPUserClient</string>
</dict>
<key>SerialPortProperties</key>
<dict>
<key>CFBundleIdentifierKernel</key>
<string>com.apple.driver.driverkit.serial</string>
<key>IOProviderClass</key>
<string>IOSerialStreamSync</string>
<key>IOClass</key>
<string>IOUserSerial</string>
<key>IOUserClass</key>
<string>VSPSerialPort</string>
<key>HiddenPort</key>
<false/>
<key>IOTTYBaseName</key>
<string>vsp</string>
<key>IOTTYSuffix</key>
<string>0</string>
</dict>
</dict>
</dict>
</dict>
</plist>
Here the entitlements of the client app
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.driverkit</key>
<true/>
<key>com.apple.developer.driverkit.allow-third-party-userclients</key>
<true/>
<key>com.apple.developer.driverkit.communicates-with-drivers</key>
<true/>
<key>com.apple.developer.shared-with-you</key>
<true/>
<key>com.apple.developer.system-extension.install</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>$(TeamIdentifierPrefix).org.eof.apps</string>
</array>
</dict>
</plist>
Here the Info.plist of the client app:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>com.apple.developer.driverkit</key>
<true/>
<key>com.apple.developer.driverkit.install</key>
<true/>
<key>com.apple.developer.system-extension.install</key>
<true/>
<key>com.apple.developer.system-extension.uninstall</key>
<true/>
<key>com.apple.developer.driverkit.userclient-access</key>
<array>
<string>VSPDriver</string>
</array>
<key>com.apple.private.driverkit.driver-access</key>
<array>
<string>VSPDriver</string>
</array>
<key>com.apple.security.temporary-exception.iokit-user-client-class</key>
<array>
<string>IOUserUserClient</string>
</array>
</dict>
</plist>
DriverKit
RSS for tagDevelop device drivers that run in user space using DriverKit.
Posts under DriverKit tag
75 Posts
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Kevin's Guide to DEXT Signing
The question of "How do I sign a DEXT" comes up a lot, so this post is my attempt to describe both what the issue are and the best current solutions are. So...
The Problems:
When DEXTs were originally introduced, the recommended development signing process required disabling SIP and local signing. There is a newer, much simpler process that's built on Xcode's integrated code-signing support; however, that newer process has not yet been integrated into the documentation library. In addition, while the older flow still works, many of the details it describes are no longer correct due to changes to Xcode and the developer portal.
DriverKit's use of individually customized entitlements is different than the other entitlements on our platform, and Xcode's support for it is somewhat incomplete and buggy. The situation has improved considerably over time, particularly from Xcode 15 and Xcode 16, but there are still issues that are not fully resolved.
To address #1, we introduced "development" entitlement variants of all DriverKit entitlements. These entitlement variants are ONLY available in development-signed builds, but they're available on all paid developer accounts without any special approval. They also allow a DEXT to match against any hardware, greatly simplifying working with development or prototype hardware which may not match the configuration of a final product.
Unfortunately, this also means that DEXT developers will always have at least two entitlement variants (the public development variant and the "private" approved entitlement), which is what then causes the problem I mentioned in #2.
The Automatic Solution:
If you're using Xcode 16 or above, then Xcode's Automatic code sign support will work all DEXT Families, with the exception of distribution signing the PCI and USB Families.
For completeness, here is how that Automatic flow should work:
Change the code signing configuration to "Automatic".
Add the capability using Xcode.
If you've been approved for one of these entitlements, the one oddity you'll see is that adding your approved capability will add both the approved AND the development variant, while deleting either will delete both. This is a visual side effect of #2 above; however, aside from the exception described below, it can be ignored.
Similarly, you can sign distribution builds by creating a build archive and then exporting the build using the standard Xcode flow.
__
Kevin Elliott
DTS Engineer, CoreOS/Hardware
Note: This document is specifically focused on what happens after a DEXT has passed its initial code-signing checks. Code-signing issues are dealt with in other posts.
Preliminary Guidance:
Using and understanding DriverKit basically requires understanding IOKit, something which isn't entirely clear in our documentation. The good news here is that IOKit actually does have fairly good "foundational" documentation in the documentation archive. Here are a few of the documents I'd take a look at:
IOKit Fundamentals
IOKit Device Driver Design Guidelines
Accessing Hardware From Applications
Special mention to QA1075: "Making sense of IOKit error codes",, which I happened to notice today and which documents the IOReturn error format (which is a bit weird on first review).
Those documents do not cover the full DEXT loading process, but they are the foundation of how all of this actually works.
Understanding the IOKitPersonalities Dictionary
The first thing to understand here is that the "IOKitPersonalities" is called that because it is in fact a fully valid "IOKitPersonalities" dictionary. That is, what the system actually uses that dictionary "for" is:
Perform a standard IOKit match and load cycle in the kernel.
The final driver in the kernel then uses the DEXT-specific data to launch and run your DEXT process outside the kernel.
So, working through the critical keys in that dictionary:
"IOProviderClass"-> This is the in-kernel class that your in-kernel driver loads "on top" of. The IOKit documentation and naming convention uses the term "Nub", but the naming convention is not consistent enough that it applies to all cases.
"IOClass"-> This is the in-kernel class that your driver loads on top of. This is where things can become a bit confused, as some families work by:
Routing all activity through the provider reference so that the DEXT-specific class does not matter (PCIDriverKit).
Having the DEXT subclass a specific subclass which corresponds to a specific kernel driver (SCSIPeripheralsDriverKit).
This distinction is described in the documentation, but it's easy to overlook if you don't understand what's going on. However, compare PCIDriverKit:
"When the system loads your custom PCI driver, it passes an IOPCIDevice object as the provider to your driver. Use that object to read and write the configuration and memory of your PCI hardware."
Versus SCSIPeripheralsDriverKit:
Develop your driver by subclassing IOUserSCSIPeripheralDeviceType00 or IOUserSCSIPeripheralDeviceType05, depending on whether your device works with SCSI Block Commands (SBC) or SCSI Multimedia Commands (SMC), respectively. In your subclass, override all methods the framework declares as pure virtual.
The reason these differences exist actually comes from the relationship and interactions between the DEXT families. Case in point, PCIDriverKit doesn't require a specific subclass because it wants SCSIControllerDriverKit DEXTs to be able to directly load "above" it.
Note that the common mistake many developers make is leaving "IOUserService" in place when they should have specified a family-specific subclass (case 2 above). This is an undocumented implementation detail, but if there is a mismatch between your DEXT driver ("IOUserSCSIPeripheralDeviceType00") and your kernel driver ("IOUserService"), you end up trying to call unimplemented kernel methods. When a method is "missing" like that, the codegen system ends up handling that by returning kIOReturnUnsupported.
One special case here is the "IOUserResources" provider. This class is the DEXT equivalent of "IOResources" in the kernel. In both cases, these classes exist as an attachment point for objects which don't otherwise have a provider. It's specifically used by the sample "Communicating between a DriverKit extension and a client app" to allow that sample to load on all hardware but is not something the vast majority of DEXT will use.
Following on from that point, most DEXT should NOT include "IOMatchCategory". Quoting IOKit fundamentals:
"Important: Any driver that declares IOResources as the value of its IOProviderClass key must also include in its personality the IOMatchCategory key and a private match category value. This prevents the driver from matching exclusively on the IOResources nub and thereby preventing other drivers from matching on it. It also prevents the driver from having to compete with all other drivers that need to match on IOResources. The value of the IOMatchCategory property should be identical to the value of the driver's IOClass property, which is the driver’s class name in reverse-DNS notation with underbars instead of dots, such as com_MyCompany_driver_MyDriver."
The critical point here is that including IOMatchCategory does this:
"This prevents the driver from matching exclusively on the IOResources nub and thereby preventing other drivers from matching on it."
The problem here is that this is actually the exceptional case. For a typical DEXT, including IOMatchCategory means that a system driver will load "beside" their DEXT, then open the provider blocking DEXT access and breaking the DEXT.
DEXT Launching
The key point here is that the entire process above is the standard IOKit loading process used by all KEXT. Once that process finishes, what actually happens next is the DEXT-specific part of this process:
IOUserServerName-> This key is the bundle ID of your DEXT, which the system uses to find your DEXT target.
IOUserClass-> This is the name of the class the system instantiates after launching your DEXT. Note that this directly mimics how IOKit loading works.
Keep in mind that the second, DEXT-specific, half of this process is the first point your actual code becomes relevant. Any issue before that point will ONLY be visible through kernel logging or possibly the IORegistry.
__
Kevin Elliott
DTS Engineer, CoreOS/Hardware
This is my first driver and I have had the devil of a time trying to find any information to help me with this. I beg help with this, since I cannot find any tutorials that will get me over this problem.
I am attempting to write a bridging driver for an older UPS that only communicates via RPC-over-USB rather than the HID Power Device class the OS requires. I have written the basic framework for the driver (details below) and am calling OSSystemExtensionRequest.submitRequest with a request object created by OSSystemExtensionRequest.activationRequest, but the didFailWithError callback is called with OSSystemExtensionErrorDomain of a value of 9, which appears to be a general failure to activate the driver. I can find no other information on how to address this issue, but I presume the issue is one of entitlements in either the entitlements file or Info.plist. I will have more code-based details below.
For testing context, I am testing this on a 2021 iMac (M1) running Sequoia 15.7, and this iMac is on MDM, specifically Jamf. I have disabled SIP and set systemextensionsctl developer on, per the instructions here, and I have compiled and am attempting to debug the app using xcode 26.2. The driver itself targets DriverKit 25, as 26 does not appear to be available in xcode despite hints on google that it's out.
For the software, I have a two-target structure in my xcode project, the main Manager app, which is a swift-ui app that both handles installation/activation of the driver and (if that finally manages to work) handles communication from the driver via its UserClient, and the driver which compiles as a dext. Both apps compile and use automated signing attached to our Apple Development team.
I won't delve into the Manager app much, as it runs even though activation fails, except to include its entitlements file in case it proves relevant
<dict>
<key>com.apple.developer.driverkit.communicates-with-drivers</key>
<true/>
<key>com.apple.developer.system-extension.install</key>
<true/>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
</dict>
and the relevant activation code:
func request(_ request: OSSystemExtensionRequest, didFailWithError error: any Error) {
// handling the error, which is always code value 9
}
func activateDriver() {
let request = OSSystemExtensionRequest.activationRequest(forExtensionWithIdentifier: "com.mycompany.driver.bundle.identifier", queue: .main)
request.delegate = self
OSSystemExtensionManager.shared.submitRequest(request)
//...
}
And finally the Manager app has the following capabilities requested for its matching identifier in our Apple Developer Account:
DriverKit Communicates with Drivers
System Extension
On the Driver side, I have two major pieces, the main driver class MyDriver, and UserClient class, StatusUserClient. MyDriver derives from IDriverKit/IOService.iig but (in case this is somehow important) does not have the same name as the project/target name MyBatteryDriver. StatusUserClient derives from DriverKit/IOUserClient.iig. I have os_log(OS_LOG_DEFAULT, "trace messages") code in every method of both classes, including the initializers and Start implementations, and the log entries never seem to show up in Console, so I presume that means the OS never tried to load the driver.
Unless I'm looking in the wrong place?
Because I don't think the driver code is the current issue, I won't go into it unless it becomes necessary. As I mentioned above, I think this is a code signing / entitlements issue, but I don't know how to resolve it.
In our Apple Developer account, the Driver's matching identifier has the following capabilities requested:
DriverKit (development)
DriverKit Allow Any UserClient (development)
DriverKit Family HID Device (development) -- NOTE: this is planned for future use, but not yet implemented by my driver code. Could that be part of the problem?
DriverKit Transport HID (development)
DriverKit USB Transport (development)
DriverKit USB Transport - VendorID -- submitted, no response from Apple yet
HID Virtual Device -- submitted, no response from Apple. yet. This is vestigial from an early plan to build the bridge via shared memory funneling to a virtual HID device. I think I've found a way to do it with one Service, but... not sure yet. Still, that's a problem for tomorrow.
Apparently I've gone over the 7000 character maximum so I will add my entitlements and info.plist contents in a reply.
I am trying to add a few properties to an IOUSBHostDevice but the SetProperties is returning kIOReturnUnsupported. The reason I am trying to modify the IOUSBHostDevice's properties is so we can support a MacBook Air SuperDrive when it is attached to our docking station devices. The MacBook Air SuperDrive needs a high powered port to run and this driver will help the OS realize that our dock can support it.
I see that the documentation for SetProperties says:
The default implementation of this method returns kIOReturnUnsupported. You can override this method and use it to modify the set of properties and values as needed. The changes you make apply only to the current service.
Do I need to override IOUSBHostDevice? This is my current Start implementation (you can also see if in the Xcode project):
kern_return_t
IMPL(MyUserUSBHostDriver, Start)
{
kern_return_t ret = kIOReturnSuccess;
OSDictionary * prop = NULL;
OSDictionary * mergeProperties = NULL;
bool success = true;
os_log(OS_LOG_DEFAULT, "> %s", __FUNCTION__);
os_log(OS_LOG_DEFAULT, "%s:%d", __FUNCTION__, __LINE__);
ret = Start(provider, SUPERDISPATCH);
__Require(kIOReturnSuccess == ret, Exit);
os_log(OS_LOG_DEFAULT, "%s:%d", __FUNCTION__, __LINE__);
ivars->host = OSDynamicCast(IOUSBHostDevice, provider);
__Require_Action(NULL != ivars->host, Exit, ret = kIOReturnNoDevice);
os_log(OS_LOG_DEFAULT, "%s:%d", __FUNCTION__, __LINE__);
ret = ivars->host->Open(this, 0, 0);
__Require(kIOReturnSuccess == ret, Exit);
os_log(OS_LOG_DEFAULT, "%s:%d", __FUNCTION__, __LINE__);
ret = CopyProperties(&prop);
__Require(kIOReturnSuccess == ret, Exit);
__Require_Action(NULL != prop, Exit, ret = kIOReturnError);
os_log(OS_LOG_DEFAULT, "%s:%d", __FUNCTION__, __LINE__);
mergeProperties = OSDynamicCast(OSDictionary, prop->getObject("IOProviderMergeProperties"));
mergeProperties->retain();
__Require_Action(NULL != mergeProperties, Exit, ret = kIOReturnError);
os_log(OS_LOG_DEFAULT, "%s:%d", __FUNCTION__, __LINE__);
OSSafeReleaseNULL(prop);
ret = ivars->host->CopyProperties(&prop);
__Require(kIOReturnSuccess == ret, Exit);
__Require_Action(NULL != prop, Exit, ret = kIOReturnError);
os_log(OS_LOG_DEFAULT, "%s:%d", __FUNCTION__, __LINE__);
os_log(OS_LOG_DEFAULT, "%s : %s", "USB Product Name", ((OSString *) prop->getObject("USB Product Name"))->getCStringNoCopy());
os_log(OS_LOG_DEFAULT, "%s : %s", "USB Vendor Name", ((OSString *) prop->getObject("USB Vendor Name"))->getCStringNoCopy());
os_log(OS_LOG_DEFAULT, "%s:%d", __FUNCTION__, __LINE__);
success = prop->merge(mergeProperties);
__Require_Action(success, Exit, ret = kIOReturnError);
os_log(OS_LOG_DEFAULT, "%s:%d", __FUNCTION__, __LINE__);
ret = ivars->host->SetProperties(prop); // this is no working
__Require(kIOReturnSuccess == ret, Exit);
Exit:
OSSafeReleaseNULL(mergeProperties);
OSSafeReleaseNULL(prop);
os_log(OS_LOG_DEFAULT, "err ref %d", kIOReturnUnsupported);
os_log(OS_LOG_DEFAULT, "< %s %d", __FUNCTION__, ret);
return ret;
}
1. 环境描述 (Environment)
OS: macOS 26.2
Hardware: Apple Silicon (M1/M2/M3)
DriverKit SDK: DriverKit 19.0 / 20.0
Arch: Universal (x86_64, arm64, arm64e)
SIP Status: Enabled (Works perfectly when Disabled)
2. 问题现象 (Problem Description)
在开启 SIP 的环境下,USB 驱动扩展(Dext)能安装,但插入设备时无法连接设备(驱动的Start方法未被调用)。
驱动状态:
MacBook-Pro ~ % systemextensionsctl list
1 extension(s)
--- com.apple.system_extension.driver_extension (Go to 'System Settings > General > Login Items & Extensions > Driver Extensions' to modify these system extension(s))
enabled active teamID bundleID (version) name [state]
* * JK9U78YRLU com.ronganchina.usbapp.MyUserUSBInterfaceDriver (1.3/4) com.ronganchina.usbapp.MyUserUSBInterfaceDriver [activated enabled]
关键日志证据 (Key Logs)
KernelManagerd: Error Domain=NSPOSIXErrorDomain Code=8 "Exec format error"
Syspolicyd: failed to fetch ... /_CodeSignature/CodeRequirements-1 error=-10
AppleSystemPolicy: ASP: Security policy would not allow process
DriverKit Kernel: DK: MyUserUSBInterfaceDriver user server timeout
dext的
embedded.provisionprofile 已包含:
com.apple.developer.driverkit
com.apple.developer.driverkit.transport.usb (idVendor: 11977)
Hello everyone,
We are in the process of migrating a high-performance storage KEXT to DriverKit. During our initial validation phase, we noticed a performance gap between the DEXT and the KEXT, which prompted us to try and optimize our I/O handling process.
Background and Motivation:
Our test hardware is a RAID 0 array of two HDDs. According to AJA System Test, our legacy KEXT achieves a write speed of about 645 MB/s on this hardware, whereas the new DEXT reaches about 565 MB/s. We suspect the primary reason for this performance gap might be that the DEXT, by default, uses a serial work-loop to submit I/O commands, which fails to fully leverage the parallelism of the hardware array.
Therefore, to eliminate this bottleneck and improve performance, we configured a dedicated parallel dispatch queue (MyParallelIOQueue) for the UserProcessParallelTask method.
However, during our implementation attempt, we encountered a critical issue that caused a system-wide crash.
The Operation Causing the Panic:
We configured MyParallelIOQueue using the following combination of methods:
In the .iig file: We appended the QUEUENAME(MyParallelIOQueue) macro after the override keyword of the UserProcessParallelTask method declaration.
In the .cpp file: We manually created a queue with the same name by calling the IODispatchQueue::Create() function within our UserInitializeController method.
The Result:
This results in a macOS kernel panic during the DEXT loading process, forcing the user to perform a hard reboot.
After the reboot, checking with the systemextensionsctl list command reveals the DEXT's status as [activated waiting for user], which indicates that it encountered an unrecoverable, fatal error during its initialization.
Key Code Snippets to Reproduce the Panic:
In .iig file - this was our exact implementation:
class DRV_MAIN_CLASS_NAME: public IOUserSCSIParallelInterfaceController
{
public:
virtual kern_return_t UserProcessParallelTask(...) override
QUEUENAME(MyParallelIOQueue);
};
In .h file:
struct DRV_MAIN_CLASS_NAME_IVars {
// ...
IODispatchQueue* MyParallelIOQueue;
};
In UserInitializeController implementation:
kern_return_t
IMPL(DRV_MAIN_CLASS_NAME, UserInitializeController)
{
// ...
// We also included code to manually create the queue.
kern_return_t ret = IODispatchQueue::Create("MyParallelIOQueue",
kIODispatchQueueReentrant,
0,
&ivars->MyParallelIOQueue);
if (ret != kIOReturnSuccess) {
// ... error handling ...
}
// ...
return kIOReturnSuccess;
}
Our Question:
What is the officially recommended and most stable method for configuring UserProcessParallelTask_Impl() to use a parallel I/O queue?
Clarifying this is crucial for all developers pursuing high-performance storage solutions with DriverKit. Any explanation or guidance would be greatly appreciated.
Best Regards,
Charles
I am developing a macOS virtual audio device using an Audio Server Plug-In (HAL). I want the virtual device to be visible to all applications only when my main app is running, and completely hidden from all apps when the app is closed. The goal is to dynamically control device visibility based on app state without reinstalling the driver.What is the recommended way for the app to notify the HAL plug-in about its running or closed state ? Any guidance on best-practice architecture for this scenario would be appreciated.
I created a custom class that inherits from IOUserSCSIPeripheralDeviceType00 in the DriverKit SCSIPeripheralsDriverKit framework.
When I attempted to send a vendor-specific command to a USB storage device using the UserSendCDB function of this class instance, the function returned the error:
kIOReturnNotPrivileged (iokit_common_err(0x2c1)) // privilege violation
However, when using UserSendCDB in the same way to issue standard SCSI commands such as INQUIRY or Test Unit Ready, no error occurred and the returned sense data was valid.
Why is UserSendCDB able to send standard SCSI commands successfully, but vendor-specific commands return kIOReturnNotPrivileged?
Is there any required entitlement, DriverKit capability, or implementation detail needed to allow vendor-specific CDBs?
Below are the entitlements of my DriverKit extension:
<dict>
<key>com.apple.developer.driverkit.transport.usb</key>
<array>
<dict>
<key>idVendor</key>
<integer>[number of vendorid]</integer>
</dict>
</array>
<key>com.apple.developer.driverkit</key>
<true/>
<key>com.apple.developer.driverkit.allow-any-userclient-access</key>
<true/>
<key>com.apple.developer.driverkit.allow-third-party-userclients</key>
<true/>
<key>com.apple.developer.driverkit.communicates-with-drivers</key>
<true/>
<key>com.apple.developer.driverkit.family.scsicontroller</key>
<true/>
</dict>
If there is any additional configuration or requirement to enable vendor-specific SCSI commands, I would appreciate your guidance.
Environment: macOS15.6 M2 MacBook Pro
Hello!
I have app (macos and iPadOS platforms) with empbedded DEXT. The DEXT executable runs fine on both platforms (ver 26.2).
Trying to execute from iPad App code:
let sysExtWs = OSSystemExtensionsWorkspace.shared
let sysExts = try sysExtWs.systemExtensions(forApplicationWithBundleID: appBudleId)
but always getting OSSystemExtensionError.Code.missingEntitlement error.
Which entitlement am I missing?
Thank You!
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
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.
I was wondering if there's a method on MacOS to have my application hide a hid device such as a game controller and instead have the receiving game/application see my app's virtual controller? Is this possible via DriverKit or some other form of kernel level coding?
On Windows we have a tool known as HidHide that hids a game controller from all other applications. Is it possible to implement such behavior into an app or is that system level?
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?
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.
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.
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?
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
Topic:
Code Signing
SubTopic:
Certificates, Identifiers & Profiles
Tags:
SCSIControllerDriverKit
PCIDriverKit
DriverKit
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.
Topic:
App & System Services
SubTopic:
Drivers
Tags:
SCSIControllerDriverKit
DriverKit
BlockStorageDeviceDriverKit
Hello everyone,
I am migrating a legacy KEXT to a DriverKit (DEXT) architecture. While the DEXT itself is working correctly, I am completely blocked by a code signing issue when trying to establish the UserClient connection from our SwiftUI management app.
Project Goal & Status:
Our DEXT (com.accusys.Acxxx.driver) activates successfully (systemextensionsctl list confirms [activated enabled]).
The core functionality is working (diskutil list shows the corresponding disk device node).
The Core Problem: The userclient-access Signing Error
To allow the app to connect to the DEXT, the com.apple.developer.driverkit.userclient-access entitlement is required in the app's .entitlements file.
However, as soon as this entitlement is added, the build fails.
Both automatic and manual signing fail with the same error:
`Provisioning profile ... doesn't match the entitlements file's value for the ... userclient-access entitlement.`
This build failure prevents the generation of an .app bundle, making it impossible to inspect the final entitlements with codesign.
What We've Confirmed:
The necessary capabilities (like DriverKit Communicates with Drivers) are visible and enabled for our App ID on the developer portal.
The issue persists on a clean system state and on the latest macOS Sequoia 15.7.1.
Our Research and Hypothesis:
We have reviewed the official documentation "Diagnosing issues with entitlements" (TN3125).
According to the documentation, a "doesn't match" error implies a discrepancy between the entitlements file and the provisioning profile.
Given that we have tried both automatic and manual profiles (after enabling the capability online), our hypothesis is that the provisioning profile generation process on Apple's backend is not correctly including the approved userclient-access entitlement into the profile file itself. The build fails because Xcode correctly detects this discrepancy.
Our Questions:
Did we misunderstand a step in the process, or is the issue not with the entitlement request at all? Alternatively, are there any other modifications we can make to successfully connect our App to the DEXT and trigger NewUserClient?
Thank you for any guidance.
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?"