Driver Activation failure error code 9. Maybe Entitlements? Please help

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.

my driver entitlements file originally looked like this:

<dict>
	<key>com.apple.developer.driverkit</key>
	<true/>
	<key>com.apple.developer.driverkit.family.hid.device</key>
	<true/>
	<key>com.apple.developer.driverkit.transport.usb</key>
    <array/>
    <key>com.apple.developer.driverkit.allow-any-userclient-access</key>
    <true/>
</dict>

but I also tried this

<dict>
	<key>com.apple.developer.driverkit</key>
	<true/>
	<key>com.apple.developer.driverkit.transport.usb</key>
    <array>
        <dict>
            <key>idVendor</key>
            <integer>*</integer> <!-- I've also tried hex and decimal values here. Wildcard was a last ditch hope --> 
        </dict>
    </array>
</dict>

to no effect. The driver's Info.plist originally looked like this:

<dict>
    <key>CFBundleShortVersionString</key>
    <string>1.0</string>
    <key>CFBundleVersion</key>
    <string>1</string>
	<key>IOKitPersonalities</key>
	<dict>
		<key>MyBatteryDriver</key>
		<dict>
            <key>CFBundleIdentifier</key>
            <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
			<key>CFBundleIdentifierKernel</key>
			<string>com.apple.kpi.iokit</string>
			<key>IOClass</key>
			<string>IOUserService</string>
			<key>IOMatchCategory</key>
			<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
			<key>IOProviderClass</key>
			<string>IOUserResources</string>
			<key>IOResourceMatch</key>
			<string>IOKit</string>
			<key>IOUserClass</key>
			<string>MyDriver</string>
			<key>IOUserServerName</key>
			<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
            <key>UserClientProperties</key>
            <dict>
                <key>IOClass</key>
                <string>IOUserUserClient</string>
                <key>IOUserClass</key>
                <string>StatusUserClient</string>
            </dict>
            <key>idVendor</key>
            <integer>0x0000</integer>  <!-- showing the format, but not the actual vendor or product ID -->   
            <key>idProduct</key>
            <integer>0x0000</integer>
		</dict>
	</dict>
	<key>OSBundleUsageDescription</key>
	<string>Driver description</string>
    <key>OSBundleUsageDescriptionKey</key>
    <string>This application is trying to install a driver.</string>
</dict>

But I also tried this

<dict>
    <key>CFBundleShortVersionString</key>
    <string>2.0</string>
    <key>CFBundleVersion</key>
    <string>2</string>
    <key>OSBundleUsageDescriptionKey</key>
    <string>This application is trying to install a driver.</string>
    <key>OSBundleUsageDescription</key>
	<string>Driver description</string>
	<key>IOKitPersonalities</key>
	<dict>
		<key>MyBatteryDriver</key>
		<dict>
            <key>CFBundleIdentifier</key>
            <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
			<key>CFBundleIdentifierKernel</key>
			<string>com.apple.kpi.iokit</string>
			<key>IOClass</key>
			<string>IOUserService</string>
			<key>IOMatchCategory</key>
			<string>IOUserService</string>
			<key>IOProviderClass</key>
			<string>IOUserResources</string>
			<key>IOResourceMatch</key>
			<string>IOKit</string>
			<key>IOUserClass</key>
			<string>MyDriver</string>
			<key>IOUserServerName</key>
			<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
            <key>UserClientProperties</key>
            <dict>
                <key>IOClass</key>
                <string>IOUserUserClient</string>
                <key>IOUserClass</key>
                <string>StatusUserClient</string>
            </dict>
            <key>idVendor</key>
            <integer>2222</integer> <!-- again showing the format, which was the hex value converted to decimal -->   
		</dict>
	</dict>
</dict>

to also no effect.

First off, a clarification here:

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.

What's your final goal here? That is, are you:

  • Trying to create a "seamless" bridge to HID and then relying on the system’s support.

OR

  • Direct communication via user client to your own app.

  • In the first case, how "seamless" do you actually want this to be? Do you not want any app "at all"?

The issue here is that you don't actually need to use DriverKit to talk to a USB device. You could also use the USBHost framework, which would let your app just "talk" to the device. Unless you're specifically bridging to HID, then I wouldn't use DriverKit at all.

In addition, if you're planning to bridge to HID but you're also planning to have an app/daemon, then you could also do this by using the USBHost framework to communicate with your device and a virtual HID device to expose that device to the system.

The right choice here really depends on your final product goals. If your goal is just to expose the device via HID, then DriverKit is probably your best choice. However, as your final product "expands", I think the path above makes more and more sense, both for ease of development and general "flexibility". Note that "ease of development" is NOT a minor difference here- DriverKit is not an easy API to work with, not to mention the added development friction inherent in DEXT/KEXT development.

In any case, getting into the details:

For testing context, I am testing this on a 2021 iMac (M1) running Sequoia 15.7, and this iMac is on MDM, specifically Jamf.

Before you do anything else, I would make sure that your MDM configuration doesn't block DEXT loading. Your DEXT isn't properly configured (more on that below), but you don't want to end up wasting a bunch of time because MDM is blocking a valid configuration.

Next:

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.

So, the first step here is to reenable SIP and basically ignore all of the instructions on our website when it comes to DriverKit testing. Technically, I think they still work [1], but the new flow is easier and (mostly) just works "better".

That leads to here:

<dict>
	<key>com.apple.developer.driverkit</key>
	<true/>
	<key>com.apple.developer.driverkit.transport.usb</key>
    <array>
        <dict>
            <key>idVendor</key>
            <integer>*</integer> <!-- I've also tried hex and decimal values here. Wildcard was a last-ditch hope --> 
        </dict>
    </array>
</dict>

This is actually the correct configuration for the development entitlement, as the "*" means "allow this DEXT as an eligible match for any vendor". Note that this is NOT the configuration you'll actually end up shipping, just the configuration you'll use for development. See this post for guidance on production signing.

That leads to here:

As I mentioned above, I think this is a code signing / entitlements issue, but I don't know how to resolve it.

Actually, I don't think that's the case. There are instructions on validating your codesign configuration here, but if your entitlement configuration is to "wrong”, then you'll end up getting a build failure.

Shifting to what is the problem:

The driver's Info.plist originally looked like this:

So, your post gave me a good excuse to do pull together a more thorough run down on DEXT matching, which you can find here. That article has the more detail on what's actually going on, but the specifics are:

(1)
Your IOProviderClass is wrong, you need to be matching against the USB device/interface you actually want to "use".

<key>IOProviderClass</key>
<string>IOUserResources</string>

(2)
You should not be including IOMatchCategory at all. It isn't causing the current failure, but it would cause problems once you actually matched.

<key>IOMatchCategory</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>

(3)
I'm not sure what your final goal here is, but if you're actually planning to use IOUserService, then I think you could just use the USBHost framework instead.

<key>IOClass</key>
<string>IOUserService</string>

In terms of the specific failure, I think what actually causes the failure is the combination of #1 and the inclusion of the "idVendor"/"idProduct" properties. Those are valid properties in for USB matching, but they don't exist on IOUserResources, so your match fails during IOKit matching, so your DEXT never launches.

My suggestion would be that you first remove those two keys, at which point I think your DEXT will start loading (just not against a USB device). That lets you confirm that the basic load process "works", after which you can correct the issues above to match your hardware target.

[1] Strictly speaking, I've never actually used the old flow, as I didn't take over DEXT support until the new flow was live.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Driver Activation failure error code 9. Maybe Entitlements? Please help
 
 
Q