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
Drivers
RSS for tagUnderstand the role of drivers in bridging the gap between software and hardware, ensuring smooth hardware functionality.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Hi Team ,
I want to create a system where i can mirror the iPhone screen connected through USB and control it from the web browser.
can anyone help me ?
Thanks ,
Mukta
Topic:
App & System Services
SubTopic:
Drivers
Hello Everyone,
I am trying to develop a DriverKit for RAID system, using PCIDriverKit & SCSIControllerDriverKit framework. The driver can detect the Vendor ID and Device ID. But before communicating to the RAID system, I would like to simulate a virtual Volume using a memory block to talk with macOS.
In the UserInitializeController(), I allocated a 512K memory for a IOBufferMemoryDescriptor* volumeBuffer, but fail to use Map() to map memory for volumeBuffer.
result = ivars->volumeBuffer->Map(
0, // Options: Use default
0, // Offset: Start of the buffer
ivars->volumeSize, // Length: Must not exceed buffer size
0, // Flags: Use default
nullptr, // Address space: Default address space
&mappedAddress // Output parameter
);
Log("Memory mapped completed at address: 0x%llx", mappedAddress); // this line never run
The Log for Map completed never run, just restart to run the Start() and makes this Driver re-run again and again, in the end, the driver eat out macOS's memory and system halt.
Are the parameters for Map() error? or I should not put this code in UserInitializeController()?
Any help is appreciated!
Thanks in advance.
Charles
Hi,
our CoreAudio server plugin supports different clock sources. A switch might result in a change of the selectable sample rates (and other settings). On a clock source switch the plugin reconfigures the set of available kAudioStreamPropertyAvailablePhysicalFormats and announces the change via AudioServerPlugInHostInterface::PropertiesChanged(). However at least the Audio MIDI Setup seems to ignore to update it's UI. The changes are first reflected after selecting another device and re-selecting the device of interest. (Latest macOS, M4 macMini)
Is this a bug? Or is our CoreAudio server plugin required to indicate the change in the list of available audio formats differently?
Thanks!
VirtIO provides macOS VM users on Intel with integrations like Shared Folders, Shared Clipboard or Drag and Drop files.
After updating VM to macOS 15.4, the VirtIO is no longer available, and we see that the functionality listed above doesn't work.
Please fix it.
Topic:
App & System Services
SubTopic:
Drivers
How can I get the Testflight invitation code
Topic:
App & System Services
SubTopic:
Drivers
"Bu beta şu anda yeni testçileri kabul etmiyor"
Lütfen bu hatayı düzeltin
My back camera freezes after first frame is displayed on the screen. Issue is present in all the apps that use the back camera. Same issue with front camera, however front camera works with Facetime and WhatsApp but not the native camera app.
Hello everyone,
I'm developing a CarPlay app and am trying to test it with the dock on the right side of the screen, as is standard for right-hand drive vehicles like those in Japan.
Currently, the CarPlay Simulator always displays the dock on the left, and I can't find an option to change its position. This is important for ensuring a proper user experience for my target market.
Has anyone figured out how to configure the simulator for RHD layouts? Any guidance on how to move the dock to the right would be greatly appreciated.
Thanks in advance for your help!
I have a USB-device with three interfaces:
Vendor-Specific Bulk
CDC Serial Control
CDC Serial Data
To configure the vendor specific bulk endpoints I need to send vendor specific control requests to endpoint 0. I'm using libusb for this task. As long as the interfaces for the CDC serial port are present I get an access error when trying to send vendor control requests.
If I disable these CDC interfaces I can send vendor control request without any problems:
Is this by design or ist there any possibility to send vendor control requests to the USB device while a CDC driver is active?
Topic:
App & System Services
SubTopic:
Drivers
在Developer app中现在注册按钮时置灰等,无法点击,我发送的联系邮件也没有得到回应,有什么办法解决吗
Topic:
App & System Services
SubTopic:
Drivers
I'm trying to implement a virtual serial port driver for my ham radio projects which require emulating some serial port devices and I need to have a "backend" to translate the commands received by the virtual serial port into some network-based communications. I think the best way to do that is to subclass IOUserSerial? Based on the available docs on this class (https://developer.apple.com/documentation/serialdriverkit/iouserserial), I've done the basic implementation below. When the driver gets loaded, I can see sth like tty.serial-1000008DD in /dev and I can use picocom to do I/O on the virtual serial port. And I see TxDataAvailable() gets called every time I type a character in picocom.
The problems are however, firstly, when TxDataAvailable() is called, the TX buffer is all-zero so although the driver knows there is some incoming data received from picocom, it cannot actually see the data in neither Tx/Rx buffers.
Secondly, I couldn't figure out how to notify the system that there are data available for sending back to picocom. I call RxDataAvailable(), but nothing appears on picocom, and RxFreeSpaceAvailable() never gets called back. So I think I must be doing something wrong somewhere. Really appreciate it if anyone could point out how should I fix it, many thanks!
VirtualSerialPortDriver.cpp:
constexpr int bufferSize = 2048;
using SerialPortInterface = driverkit::serial::SerialPortInterface;
struct VirtualSerialPortDriver_IVars
{
IOBufferMemoryDescriptor *ifmd, *rxq, *txq;
SerialPortInterface *interface;
uint64_t rx_buf, tx_buf;
bool dtr, rts;
};
bool VirtualSerialPortDriver::init()
{
bool result = false;
result = super::init();
if (result != true)
{
goto Exit;
}
ivars = IONewZero(VirtualSerialPortDriver_IVars, 1);
if (ivars == nullptr)
{
goto Exit;
}
kern_return_t ret;
ret = ivars->rxq->Create(kIOMemoryDirectionInOut, bufferSize, 0, &ivars->rxq);
if (ret != kIOReturnSuccess) {
goto Exit;
}
ret = ivars->txq->Create(kIOMemoryDirectionInOut, bufferSize, 0, &ivars->txq);
if (ret != kIOReturnSuccess) {
goto Exit;
}
IOAddressSegment ioaddrseg;
ivars->rxq->GetAddressRange(&ioaddrseg);
ivars->rx_buf = ioaddrseg.address;
ivars->txq->GetAddressRange(&ioaddrseg);
ivars->tx_buf = ioaddrseg.address;
return true;
Exit:
return false;
}
kern_return_t
IMPL(VirtualSerialPortDriver, HwActivate)
{
kern_return_t ret;
ret = HwActivate(SUPERDISPATCH);
if (ret != kIOReturnSuccess) {
goto Exit;
}
// Loopback, set CTS to RTS, set DSR and DCD to DTR
ret = SetModemStatus(ivars->rts, ivars->dtr, false, ivars->dtr);
if (ret != kIOReturnSuccess) {
goto Exit;
}
Exit:
return ret;
}
kern_return_t
IMPL(VirtualSerialPortDriver, HwDeactivate)
{
kern_return_t ret;
ret = HwDeactivate(SUPERDISPATCH);
if (ret != kIOReturnSuccess) {
goto Exit;
}
Exit:
return ret;
}
kern_return_t
IMPL(VirtualSerialPortDriver, Start)
{
kern_return_t ret;
ret = Start(provider, SUPERDISPATCH);
if (ret != kIOReturnSuccess) {
return ret;
}
IOMemoryDescriptor *rxq_, *txq_;
ret = ConnectQueues(&ivars->ifmd, &rxq_, &txq_, ivars->rxq, ivars->txq, 0, 0, 11, 11);
if (ret != kIOReturnSuccess) {
return ret;
}
IOAddressSegment ioaddrseg;
ivars->ifmd->GetAddressRange(&ioaddrseg);
ivars->interface = reinterpret_cast<SerialPortInterface*>(ioaddrseg.address);
SerialPortInterface &intf = *ivars->interface;
ret = RegisterService();
if (ret != kIOReturnSuccess) {
goto Exit;
}
TxFreeSpaceAvailable();
Exit:
return ret;
}
void
IMPL(VirtualSerialPortDriver, TxDataAvailable)
{
SerialPortInterface &intf = *ivars->interface;
// Loopback
// FIXME consider wrapped case
size_t tx_buf_sz = intf.txPI - intf.txCI;
void *src = reinterpret_cast<void *>(ivars->tx_buf + intf.txCI);
// char src[] = "Hello, World!";
void *dest = reinterpret_cast<void *>(ivars->rx_buf + intf.rxPI);
memcpy(dest, src, tx_buf_sz);
intf.rxPI += tx_buf_sz;
RxDataAvailable();
intf.txCI = intf.txPI;
TxFreeSpaceAvailable();
Log("[TX Buf]: %{public}s", reinterpret_cast<char *>(ivars->tx_buf));
Log("[RX Buf]: %{public}s", reinterpret_cast<char *>(ivars->rx_buf));
// dmesg confirms both buffers are all-zero
Log("[TX] txPI: %d, txCI: %d, rxPI: %d, rxCI: %d, txqoffset: %d, rxqoffset: %d, txlogsz: %d, rxlogsz: %d",
intf.txPI, intf.txCI, intf.rxPI, intf.rxCI, intf.txqoffset, intf.rxqoffset, intf.txqlogsz, intf.rxqlogsz);
}
void
IMPL(VirtualSerialPortDriver, RxFreeSpaceAvailable)
{
Log("RxFreeSpaceAvailable() called!");
}
kern_return_t IMPL(VirtualSerialPortDriver,HwResetFIFO){
Log("HwResetFIFO() called with tx: %d, rx: %d!", tx, rx);
kern_return_t ret = kIOReturnSuccess;
return ret;
}
kern_return_t IMPL(VirtualSerialPortDriver,HwSendBreak){
Log("HwSendBreak() called!");
kern_return_t ret = kIOReturnSuccess;
return ret;
}
kern_return_t IMPL(VirtualSerialPortDriver,HwProgramUART){
Log("HwProgramUART() called, BaudRate: %u, nD: %d, nS: %d, P: %d!", baudRate, nDataBits, nHalfStopBits, parity);
kern_return_t ret = kIOReturnSuccess;
return ret;
}
kern_return_t IMPL(VirtualSerialPortDriver,HwProgramBaudRate){
Log("HwProgramBaudRate() called, BaudRate = %d!", baudRate);
kern_return_t ret = kIOReturnSuccess;
return ret;
}
kern_return_t IMPL(VirtualSerialPortDriver,HwProgramMCR){
Log("HwProgramMCR() called, DTR: %d, RTS: %d!", dtr, rts);
ivars->dtr = dtr;
ivars->rts = rts;
kern_return_t ret = kIOReturnSuccess;
Exit:
return ret;
}
kern_return_t IMPL(VirtualSerialPortDriver, HwGetModemStatus){
*cts = ivars->rts;
*dsr = ivars->dtr;
*ri = false;
*dcd = ivars->dtr;
Log("HwGetModemStatus() called, returning CTS=%d, DSR=%d, RI=%d, DCD=%d!", *cts, *dsr, *ri, *dcd);
kern_return_t ret = kIOReturnSuccess;
return ret;
}
kern_return_t IMPL(VirtualSerialPortDriver,HwProgramLatencyTimer){
Log("HwProgramLatencyTimer() called!");
kern_return_t ret = kIOReturnSuccess;
return ret;
}
kern_return_t IMPL(VirtualSerialPortDriver,HwProgramFlowControl){
Log("HwProgramFlowControl() called! arg: %u, xon: %d, xoff: %d", arg, xon, xoff);
kern_return_t ret = kIOReturnSuccess;
Exit:
return ret;
}
I would like to write a driver that supports our custom USB-C connected device, which provides a serial port interface. USBSerialDriverKit looks like the solution I need. Unfortunately, without a decent sample, I'm not sure how to accomplish this. The DriverKit documentation does a good job of telling me what APIs exist but it is very light on semantic information and details about how to use all of these API elements. A function call with five unexplained parameters just is that useful to me.
Does anyone have or know of a resource that can help me figure out how to get started?
Hi there. I inadvertently deleted the Passwords app. The App Store is telling me restrictions are enabled so I can’t reinstall from the cloud. Not sure where to go from here. Help.
Topic:
App & System Services
SubTopic:
Drivers
I have USB DriverKit driver. When I use the log command below to get log, there is logs from my driver on my own M-series MacBook where the driver is built using developer account.
log stream | grep CompanyName
But on other mac like (M-series) Mac Mini, there is no log captured from driver though the driver is communicating with the machine correctly. The only log captured are from MacOS regarding CompanyName driver status/unload/load. The MacOS is Sonoma 14.7.2 and 14.7.3.
Please advise on how to get log from driver since writing to files is not allowed in DriverKit. I need logs to troubleshoot on Mac Mini.
Thanks.
I'm trying to iterate through a USB device but the iterator is always empty or contains only the matched interface:
Single interface in Iterator
This happens when my driver matches against the interface. Because I need to use 2 interfaces (control and cdc), I try to open the IOUSBHostDevice (copied from the interface) and iterate through the rest, but I only get the interface my dext matched with.
Empty Iterator
I decided to match against USB communication devices, thinking things would be different. However, this time the interface iterator is completely empty (provider is IOUSBHostDevice).
Here's a snippet of my code before iterating with IOUSBHostDevice->CopyInterface():
// teardown the configured interfaces.
result = device->SetConfiguration(ivars->Config, true);
__Require_noErr_Action(result, _failure_Out,
ELOG("IOUSBHostDevice::SetConfiguration failed 0x%x", result));
// open usb device
result = device->Open(this, 0, 0);
__Require_noErr_Action(result, _failure_Out,
ELOG("Failed to open IOUSBHostDevice"));
// Get interface iterator
result = device->CreateInterfaceIterator(&iterRef);
__Require_noErr_Action(result, _failure_Out,
ELOG("IOUSBHostDevice::CreateInterfaceIterator failed failed: 0x%x", result));
Hello Everyone,
I am trying to create a Fake SCSI target based on SCSIControllerDriverKit.framework and inherent from IOUserSCSIParallelInterfaceController, here is the code
kern_return_t IMPL(DRV_MAIN_CLASS_NAME, Start)
{
...
// Programmatically create a null SCSI Target
SCSIDeviceIdentifier nullTargetID = 0; // Example target ID, adjust as needed
ret = UserCreateTargetForID(nullTargetID, nullptr);
if (ret != kIOReturnSuccess) {
Log("Failed to create Null SCSI Target for ID %llu", nullTargetID);
return ret;
}
...
}
According the document UserCreateTargetForID, after creating a TargetID successfully, the framework will call the UserInitializeTargetForID()
The document said:
As part of the UserCreateTargetForID call, the kernel calls several APIs like UserInitializeTargetForID which run on the default dispatch queue of the dext.
But after UserCreateTargetForID created, why the UserInitializeTargetForID() not be invoked automatically?
Here is the part of log show
init() - Start
init() - End
Start() - Start
Start() - try 1 times
UserCreateTargetForID() - Start
Allocating resources for Target ID 0
UserCreateTargetForID() - End
Start() - Finished.
UserInitializeController() - Start
- PCI vendorID: 0x14d6, deviceID: 0x626f.
- BAR0: 0x1, BAR1: 0x200004.
- GetBARInfo() - BAR1 - MemoryIndex: 0, Size: 262144, Type: 0.
UserInitializeController() - End
UserStartController() - Start
- msiInterruptIndex : 0x00000000
- interruptType info is 0x00010000
- PCI Dext interrupt final value, return status info is 0x00000000
UserStartController() - End
Any assistance would be greatly appreciated!
Thank you in advance for your support.
Best regards, Charles
Hello Everyone,
I am working on migrating a KEXT to DriverKit but am struggling to resolve a specific issue. The code in question is simple, but I haven't been able to find a solution.
void AME_IO_milliseconds_Delay(AME_U32 Delay)
{
Log("AME_IO_milliseconds_Delay()");
IOSleep(Delay);
return;
}
//delay for a number of microseconds
void AME_IO_microseconds_Delay(AME_U32 Delay)
{
Log("AME_IO_microseconds_Delay()");
IODelay(Delay);
return;
}
I've sought help from Copilot and ChatGPT, but their suggestions haven't worked. Any guidance on how to implement this functionality in DriverKit would be greatly appreciated.
Thank you for your time and assistance.
Best regards, Charles
I'm working on a project to allow HID input from macOS to a connected iOS device. Are we prohibited from matching to a connected iPhone with DriverKit? I see the attribute kCDCDoNotMatchThisDevice for my iPhone is YES when looking at the IO registry and my dext does not initialize
When my virtual CoreAudio server plugins propagates a change to it´s device name the CoreAudio system does not seem to reflect the change.
My user mode application subscribes to the property change and receives the change though.
I also alternatively submitted a kAudioObjectPropertyName change with the same effect.
Is this possible at all and what needs to be done then?
Are there restrictions about which properties can be successfully changed and are reflected by the system?
Any hint is highly appreciated!
Thanks
When plugging in my matched USB device I see the logs below. It seems the kernelmanagerd process is sandboxed and can't write out the reason my Dext failed to load. Is there somewhere else I can look for this info?
default 11:03:22.175152-0700 kernelmanagerd Received kext load notification: me.keithg.MyUserUSBInterfaceDriver
default 11:03:22.177637-0700 kernel 1 duplicate report for Sandbox: icdd(2124) allow file-read-data /Library/Image Capture/Devices
error 11:03:22.177681-0700 kernel Sandbox: kernelmanagerd(545) deny(1) file-write-create /private/var/folders/zz/zyxvpxvq6csfxvn_n0000000000000/T/com.apple.kernelmanagerd/TemporaryItems
com.apple.libcoreservices error 11:03:22.177711-0700 kernelmanagerd mkdir: path=/var/folders/zz/zyxvpxvq6csfxvn_n0000000000000/T/com.apple.kernelmanagerd/TemporaryItems/ mode= -rwx------: [1: Operation not permitted]
error 11:03:22.179361-0700 kernel Sandbox: kernelmanagerd(545) deny(1) file-write-create /private/var/db/loadedkextmt.plist.sb-5a00fc77-LNttZF
com.apple.libcoreservices error 11:03:22.177755-0700 kernelmanagerd _dirhelper_relative_internal: error for path <private>: [1: Operation not permitted]
com.apple.accessories default 11:03:22.177674-0700 WindowServer Sending analytics event... (eventName: com.apple.ioport.transport.USB.published)
error 11:03:22.179913-0700 kernelmanagerd Failed to write extension load report plist.