# 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.submit` does not restart your driverkit userspace process. The most reliable way to restart your userspace process is reboot. - Execute `systemextensionsctl reset` before investigating your issue. The reset command requires disabling SIP, however it solves various problems. ## Most reliable way to upgrade your driver extension 1. Execute `systemextensionsctl reset`. 2. Install your driver extension from ExtensionManager. (User approval is always required due to `systemextensionsctl reset`.) 3. Restart your macOS. ## Log messages Using `log` command to show your driver log messages. ```shell log show --predicate 'sender == "sysextd" or sender CONTAINS "org.pqrs"' --info --debug --last 1h ``` The result: ```text 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`. ```shell plutil -convert xml1 -o - /Library/SystemExtensions/db.plist ``` ## Errors - `EXC_CRASH (Code Signature Invalid)` - Reason: - There are extra entitlements which are not allowed for us: - `com.apple.developer.hid.virtual.device` - `com.apple.developer.system-extension.redistributable` (Bug?) - Your `dext` must be notarized if you enabled SIP even if the dext is built on the install target machine. - `sysextd` is crashed by `EXC_BAD_INSTRUCTION (SIGILL)` - Reason #1: - `sysextd` will be crashed if multiple versions of your driver extension are installed. - Workaround: - `systemextensionsctl reset` - Reason #2: - Your driver extension is crashed in `init()` or `Start()`. Add log messages to investigate the problem. - `sysextd: (libswiftCore.dylib) Fatal error: Activate found 2 extensions in active state, ID: xxx` - Workaround: - Execute `systemextensionsctl reset`, then install your system extension again. ## 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. - 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.sh` for details. ## 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](https://developer.apple.com/documentation/hiddriverkit/iouserhiddevice/3433772-start) 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"`. ```cpp 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](https://github.com/pqrs-org/Karabiner-DriverKit-VirtualHIDDevice/blob/main/docs/vendor/IOHIDFamily/IOHIDDevice.cpp#L476-L479). --- ## How to communicate with your driver extension from user space ### Driver extension 1. Provide your driver extension. (e.g., org_pqrs_Karabiner_DriverKit_VirtualHIDDeviceRoot) 2. Add a subclass of IOUserClient. (e.g., org_pqrs_Karabiner_DriverKit_VirtualHIDDeviceUserClient) 3. Put UserClientProperties into Info.plist. ```xml UserClientProperties IOClass IOUserUserClient IOUserClass org_pqrs_Karabiner_DriverKit_VirtualHIDDeviceUserClient ``` 4. Implement `org_pqrs_Karabiner_DriverKit_VirtualHIDDeviceRoot::NewUserClient` method. 5. Implement `org_pqrs_Karabiner_DriverKit_VirtualHIDDeviceUserClient::Start` and `Stop`. - You can save `provider` argument to ivars at `org_pqrs_Karabiner_DriverKit_VirtualHIDDeviceUserClient::Start` if needed as follows. ```cpp ivars->deviceRoot = OSDynamicCast(org_pqrs_Karabiner_DriverKit_VirtualHIDDeviceRoot, provider); ``` ### Client 1. Make C++ code. ```cpp 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; } } ``` 2. Inject entitlements to your app. ```xml com.apple.developer.driverkit.userclient-access org.pqrs.Karabiner-DriverKit-VirtualHIDDevice ``` ### Implement methods You can connect to your driver extension from your client by above steps. Implement the actual processing by the following steps. 1. Implement `ExternalMethod` method your driverkit user client class. ```cpp 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; } ``` 2. Call `IOConnectCallStructMethod` from your client. ```cpp IOConnectCallStructMethod(connect, 42, nullptr, 0, nullptr, 0); ``` The `ExternalMethod` will be called.