How to be close to DriverKit
Execute systemextensionsctl reset and reboot macOS before you suspect a problem is caused by your code
- Restart macOS before investigating your issue.
Replacing extension from
OSSystemExtensionManager.submitdoes not restart your driverkit userspace process. The most reliable way to restart your userspace process is reboot. - Execute
systemextensionsctl resetbefore investigating your issue. The reset command requires disabling SIP, however it solves various problems.
Most reliable way to upgrade your driver extension
- Execute
systemextensionsctl reset. - Install your driver extension from ExtensionManager. (User approval is always required due to
systemextensionsctl reset.) - Restart your macOS.
Log messages
Using log command to show your driver log messages.
log show --predicate 'sender == "sysextd" or sender CONTAINS "org.pqrs"' --info --debug --last 1hThe result:
Timestamp Thread Type Activity PID TTL
2020-07-26 23:43:10.674449+0900 0x973 Default 0x0 0 0 kernel: (org.pqrs.Karabiner-DriverKit-VirtualHIDDevice) Karabiner-DriverKit-VirtualHIDDeviceRoot 0.12.0 init
2020-07-26 23:43:10.674525+0900 0x973 Default 0x0 0 0 kernel: (org.pqrs.Karabiner-DriverKit-VirtualHIDDevice) Karabiner-DriverKit-VirtualHIDDeviceRoot 0.12.0 Start
2020-07-26 23:44:28.781849+0900 0x974 Default 0x0 0 0 kernel: (org.pqrs.Karabiner-DriverKit-VirtualHIDDevice) Karabiner-DriverKit-VirtualHIDDeviceRoot 0.12.0 NewUserClient type:0
2020-07-26 23:44:28.781895+0900 0x974 Default 0x0 0 0 kernel: (org.pqrs.Karabiner-DriverKit-VirtualHIDDevice) Karabiner-DriverKit-VirtualHIDDeviceUserClient 0.12.0 init
2020-07-26 23:44:28.781940+0900 0x974 Default 0x0 0 0 kernel: (org.pqrs.Karabiner-DriverKit-VirtualHIDDevice) Karabiner-DriverKit-VirtualHIDDeviceUserClient 0.12.0 Start
2020-07-26 23:44:28.781943+0900 0x974 Default 0x0 0 0 kernel: (org.pqrs.Karabiner-DriverKit-VirtualHIDDevice) Karabiner-DriverKit-VirtualHIDDeviceRoot 0.12.0 UserClient is created
2020-07-26 23:44:28.782094+0900 0x974 Default 0x0 0 0 kernel: (org.pqrs.Karabiner-DriverKit-VirtualHIDDevice) Karabiner-DriverKit-VirtualHIDKeyboard 0.12.0 init
2020-07-26 23:44:28.782153+0900 0x974 Default 0x0 0 0 kernel: (org.pqrs.Karabiner-DriverKit-VirtualHIDDevice) Karabiner-DriverKit-VirtualHIDKeyboard 0.12.0 handleStart
2020-07-26 23:44:28.782238+0900 0x974 Default 0x0 0 0 kernel: (org.pqrs.Karabiner-DriverKit-VirtualHIDDevice) Karabiner-DriverKit-VirtualHIDKeyboard 0.12.0 newDeviceDescription
2020-07-26 23:44:28.782264+0900 0x974 Default 0x0 0 0 kernel: (org.pqrs.Karabiner-DriverKit-VirtualHIDDevice) Karabiner-DriverKit-VirtualHIDKeyboard 0.12.0 newReportDescriptor
Inspect installed driver extensions
See db.plist.
plutil -convert xml1 -o - /Library/SystemExtensions/db.plistErrors
EXC_CRASH (Code Signature Invalid)- Reason:
- There are extra entitlements which are not allowed for us:
com.apple.developer.hid.virtual.devicecom.apple.developer.system-extension.redistributable(Bug?)
- Your
dextmust be notarized if you enabled SIP even if the dext is built on the install target machine.
- There are extra entitlements which are not allowed for us:
- Reason:
sysextdis crashed byEXC_BAD_INSTRUCTION (SIGILL)- Reason #1:
sysextdwill be crashed if multiple versions of your driver extension are installed.- Workaround:
systemextensionsctl reset
- Reason #2:
- Your driver extension is crashed in
init()orStart(). Add log messages to investigate the problem.
- Your driver extension is crashed in
- Reason #1:
sysextd: (libswiftCore.dylib) Fatal error: Activate found 2 extensions in active state, ID: xxx- Workaround:
- Execute
systemextensionsctl reset, then install your system extension again.
- Execute
- Workaround:
Build issues
- Error: Xcode requires a provisioning profile which supports DriverKit
- Reason:
- The driverkit entitlements (e.g.,
com.apple.developer.driverkit) requires a proper provisioning profile which you cannot create it unless you gained DriverKit framework capability from Apple.
- The driverkit entitlements (e.g.,
- Workaround:
- If you want to develop driver extension without the capability, build your code without entitlements and inject entitlements at codesigning stage.
See
src/scripts/codesign.shfor details.
- If you want to develop driver extension without the capability, build your code without entitlements and inject entitlements at codesigning stage.
See
- Reason:
OSSystemExtensionError.Code
| Code | Name | Description |
|---|---|---|
| 1 | unknown | An error code that indicates an unknown error occurred. |
| 2 | missingEntitlement | An error code that indicates the system extension lacks a required entitlement. |
| 3 | unsupportedParentBundleLocation | An error code that indicates the extension’s parent app isn’t in a valid location for activation. |
| 4 | extensionNotFound | An error code that indicates the manager can’t find the system extension. |
| 5 | extensionMissingIdentifier | An error code that indicates the extension identifier is missing. |
| 6 | duplicateExtensionIdentifer | An error code that indicates the extension identifier duplicates an existing identifier. |
| 7 | unknownExtensionCategory | An error code that indicates the extension manager can’t recognize the extension’s category identifier. |
| 8 | codeSignatureInvalid | An error code that indicates the extension’s signature is invalid. |
| 9 | validationFailed | An error code that indicates the manager can’t validate the extension. |
| 10 | forbiddenBySystemPolicy | An error code that indicates the system policy prohibits activating the system extension. |
| 11 | requestCanceled | ExtensionsAn error code that indicates the system extension manager request was canceled. |
| 12 | requestSuperseded | An error code that indicates the system extension request failed because the system already has a pending request for the same identifier. |
| 13 | authorizationRequired | An error code that indicates the system was unable to obtain the proper authorization. |
How to register IOUserHIDDevice
We should not override Start method in the subclass of IOUserHIDDevice.
IOUserHIDDevice::Start documentation
So, we cannot call IOService::RegisterService directly at the end of Start method.
And if you call RegisterService at the end of handleStart, your own device will not be matched because the initialization process has not been completed at RegisterService is called.
Thus, the correct way to register IOUserHIDDevice is that we implement newDeviceDescription and return "RegisterService".
OSDictionary* org_pqrs_Karabiner_DriverKit_VirtualHIDKeyboard::newDeviceDescription(void) {
auto dictionary = OSDictionary::withCapacity(12);
...
OSDictionarySetValue(dictionary, "RegisterService", kOSBooleanTrue);
...
return dictionary;
}The RegisterService invokes registerService() at IOHIDDevice::start.
How to communicate with your driver extension from user space
Driver extension
-
Provide your driver extension. (e.g., org_pqrs_Karabiner_DriverKit_VirtualHIDDeviceRoot)
-
Add a subclass of IOUserClient. (e.g., org_pqrs_Karabiner_DriverKit_VirtualHIDDeviceUserClient)
-
Put UserClientProperties into Info.plist.
<key>UserClientProperties</key> <dict> <key>IOClass</key> <string>IOUserUserClient</string> <key>IOUserClass</key> <string>org_pqrs_Karabiner_DriverKit_VirtualHIDDeviceUserClient</string> <!-- <key>IOServiceDEXTEntitlements</key> --> </dict>
-
Implement
org_pqrs_Karabiner_DriverKit_VirtualHIDDeviceRoot::NewUserClientmethod. -
Implement
org_pqrs_Karabiner_DriverKit_VirtualHIDDeviceUserClient::StartandStop.-
You can save
providerargument to ivars atorg_pqrs_Karabiner_DriverKit_VirtualHIDDeviceUserClient::Startif needed as follows.ivars->deviceRoot = OSDynamicCast(org_pqrs_Karabiner_DriverKit_VirtualHIDDeviceRoot, provider);
-
Client
-
Make C++ code.
io_connect_t connect = IO_OBJECT_NULL; auto service = IOServiceGetMatchingService(kIOMainPortDefault, IOServiceNameMatching("org_pqrs_Karabiner_DriverKit_VirtualHIDDeviceRoot")); if (!service) { std::cerr << "IOServiceGetMatchingService error" << std::endl; goto finish; } { pqrs::osx::iokit_return ir = IOServiceOpen(service, mach_task_self(), kIOHIDServerConnectType, &connect); if (!ir) { std::cerr << "IOServiceOpen error: " << ir << std::endl; goto finish; } }
-
Inject entitlements to your 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.userclient-access</key> <array> <string>org.pqrs.Karabiner-DriverKit-VirtualHIDDevice</string> </array> </dict> </plist>
Implement methods
You can connect to your driver extension from your client by above steps.
Implement the actual processing by the following steps.
-
Implement
ExternalMethodmethod your driverkit user client class.kern_return_t org_pqrs_Karabiner_DriverKit_VirtualHIDDeviceUserClient::ExternalMethod(uint64_t selector, IOUserClientMethodArguments* arguments, const IOUserClientMethodDispatch* dispatch, OSObject* target, void* reference) { os_log(OS_LOG_DEFAULT, "ExternalMethod %llu", selector); return kIOReturnSuccess; }
-
Call
IOConnectCallStructMethodfrom your client.IOConnectCallStructMethod(connect, 42, nullptr, 0, nullptr, 0);
The
ExternalMethodwill be called.
Prepare provision profile
You have to be granted the permission of com.apple.developer.driverkit.userclient-access from Apple.
(e.g. src/Client/entitlements.plist)
You have to add a description you want to use userclient-access at the DriverKit Entitlement request to Apple.
https://developer.apple.com/contact/request/system-extension/
You can confirm the entitlements on Apple developer portal after you gained the permission.
