Hi Everybody,
Follow Communicating between a DriverKit extension and a client app to migrate our kext to dext. The dext might have been loaded successfully by using the command systemextensionsctl list, the dext is loaded and enabled.
% sectl list
1 extension(s)
--- com.apple.system_extension.driver_extension
enabled active teamID bundleID (version) name [state]
* * K3TDMD9Y6B com.accusys.scsidriver (1.0/1) com.accusys.scsidriver [activated enabled]
We try to use the CppUserClient.cpp to communicate with the dext, but can not get the dext service. The debug message as below:
Failed opening service with error: 0xe00002c7.
Here is the part of CppUserClient.cpp
static const char* dextIdentifier = "com.accusys.scsidriver";
kern_return_t ret = kIOReturnSuccess;
io_iterator_t iterator = IO_OBJECT_NULL;
io_service_t service = IO_OBJECT_NULL;
ret = IOServiceGetMatchingServices(kIOMasterPortDefault,
IOServiceMatching("IOUserServer"), &iterator);
printf("dextIdentifier = %s\n", dextIdentifier);
printf("IOServiceNameMatching return = %d\n", ret);
if (ret != kIOReturnSuccess)
{
printf("Unable to find service for identifier with error: 0x%08x.\n", ret);
PrintErrorDetails(ret);
}
printf("Searching for dext service...\n");
while ((service = IOIteratorNext(iterator)) != IO_OBJECT_NULL)
{
// Open a connection to this user client as a server to that client, and store the instance in "service"
ret = IOServiceOpen(service, mach_task_self_, kIOHIDServerConnectType, &connection);
if (ret == kIOReturnSuccess)
{
printf("\tOpened service.\n");
break;
}
else
{
printf("\tFailed opening service with error: 0x%08x.\n", ret);
}
IOObjectRelease(service);
}
IOObjectRelease(iterator);
if (service == IO_OBJECT_NULL)
{
printf("Failed to match to device.\n");
return EXIT_FAILURE;
}
The console output message is
dextIdentifier = com.accusys.scsidriver
IOServiceNameMatching return = 0
Searching for dext service...
Failed opening service with error: 0xe00002c7.
Failed opening service with error: 0xe00002c7.
Failed opening service with error: 0xe00002c7.
Failed opening service with error: 0xe00002c7.
Failed to match to device.
Here is the log show message
fredapp start UserInitializeController
pcitest: fredapp pci vendorID: 14d6 deviceID: 626f
fredapp nnnnnew configuaration read32 0x10 info: 1
fredapp nnnnnew configuaration read32 0x14 info: 80100004
fredapp new 128 before enable busmaster ReqMSGport_info 0x00000040 : fffff
pcitest: fredapp 131 pci ConfigurationRead16 busmaster value 0
pcitest: fredapp 134 Enable BusMaster and IO space done......
locate 139 fredapp MemoryWrite32 function done......
fredapp new 143 after enable busmaster ReqMSGport_info 0x00000040 : 0
fredapp newwww before GetBARInfo memoryIndex1 info is: 5
fredapp GetBARInfo 1 kernel return status is: 0
fredapp GetBARInfo memoryIndex1 info is: 0
fredapp GetBARInfo barSize1 info is: 262144
fredapp GetBARInfo barType1 info is: 0
fredapp GetBARInfo result0 info is: 3758097136
fredapp GetBARInfo memoryIndex0 info is: 0
fredapp GetBARInfo barSize0 info is: 0
fredapp GetBARInfo barType0 info is: 0
pcitest: newwww fredapp againnnn test ReqMSGport info: 8
fredapp Start MPIO_Init_Prepare
fredapp end MPIO_Init_Prepare.
fredapp call 741 Total_memory_size: 1bb900
fredapp Start MemoryAllocationForAME_Module.
fredapp IOBufferMemoryDescriptor create return status info is kIOReturnSuc
fredapp IOBufferMemoryDescriptor virtualAddressSegment address info: 0x1
fredapp virtualAddressSegment length info: 0x00080000
fredapp end IOBufferMemoryDescriptor Create.
fredapp dmaSpecification maxAddressBits: 0x00000000
fredapp dmaSpecification maxAddressBits: 0x00000027
fredapp IODMACommand create return status info is kIOReturnSuccess
fredapp end IODMACommand Create.
fredapp PrepareForDMA return status info is kIOReturnSuccess
fredapp PrepareForDMA return status info is 0x00000000
fredapp Allocate memory PrepareforDMA return flags info: 0x00000003
fredapp Allocate memory PrepareforDMA return segmentsCount info: 0x00000
fredapp Allocate memory PrepareforDMA return physicalAddressSegment info:
fredapp IOBufferMemoryDescriptor virtualAddressSegment address info-2: 0
fredapp verify data success
init() - Finished.
fredapp start UserGetDMASpecification
fredapp end UserGetDMASpecification
fredapp start UserMapHBAData
fredapp end UserMapHBAData
fredapp start UserStartController
fredapp interruptType info is 0x00010000
fredapp PCI Dext interrupt final value return status info is 0x00000000
Any suggestions?
Best Regards,
Charles
Let me start by jumping back to here:
We try to use the CppUserClient.cpp to communicate with the dext, but can not get the dext service. The debug message as below:
Failed opening service with error: 0xe00002c7.
I posted some IORegistry matching code in this post, but the code I actually want to you to grab is my "KE_PrintIOObject" function:
void KE_PrintIOObject(io_service_t myObj)
{
if(myObj != 0)
{
NSString* className = CFBridgingRelease(IOObjectCopyClass(myObj));
NSLog(@"Class Name = %@", className);
CFMutableDictionaryRef propDictionary = NULL;
if(IORegistryEntryCreateCFProperties(myObj, &propDictionary, CFAllocatorGetDefault(), 0) == kIOReturnSuccess)
{
CFShow(propDictionary);
CFRelease(propDictionary);
}
}
}
From past experience, it's very easy to get "disoriented" in the IORegistry and end up interacting with a completely different object than the one you're expecting. The code above makes it easy check that you're actually working with the object you think you are.
Related to that point, the other critical tool here "IORegistryExplorer.app", since it will let you see/find the object you're looking for and then working on matching from there. If you're not familiar with that app, you can find it "Additional Tools for Xcode" from our "More Downloads" section.
May I politely ask if you have a basic and functional Xcode project for both the dext and client sides that I could use as a reference?
As it happens, this is one area that actually has a good sample app. Take a look at "Communicating between a DriverKit extension and a client app". The service matching code is in the file "UserClientCommunication.c".
Moving to your code, this matching dictionary is wrong:
ret = IOServiceGetMatchingServices(kIOMasterPortDefault,
IOServiceMatching("IOUserServer"), &iterator);
You're matching against all IOUserServer objects, most/all of which will not implement the relevant user client and you're also matching against the wrong class for "your" object. This is the matching code from the sample above:
// If you don't know what value to use here, it should be identical to the IOUserClass value in your IOKitPersonalities.
// You can double check by searching with the `ioreg` command in your terminal.
// It will be of type "IOUserService" not "IOUserServer"
static const char* dextIdentifier = "NullDriver";
...
CFMutableDictionaryRef matchingDictionary = IOServiceNameMatching(dextIdentifier);
if (matchingDictionary == NULL)
{
fprintf(stderr, "Failed to initialize matchingDictionary.\n");
UserClientTeardown();
return false;
}
Expanding slightly on what was said here:
The value kIOPCIDeviceConnectType is just a number, the third parameter to IOServiceOpen called by your user-space client code. That number appears as the first parameter to NewUserClient_Impl in your dext. If your dext is your own, you can use any value you like, or ignore it entirely.
The "type" argument to IOServiceOpen is there to provide a convenient API hook for KEXTs that are implementing particularly complex UserClient, as it allows the same driver to return different user clients to the requesting client. Note that, for example, the same HID driver can open "kIOHIDServerConnectType", "kIOHIDParamConnectType", and "kIOHIDServerConnectType". This situation is NOT common in 3rd party drivers, in which case the correct value is "0", which your DEXT always returning the same value.
This is my first time working on dext development, I always ask Copilot or ChatGPT for help, but their answers are mostly non-functional code, which is truly frustrating.
Unfortunately, this is a very difficult development area to jump into and one I don't really have any simple answer to. Driver development has never been easy and DriverKit has not improved that situation. The one suggestion I would offer here it to take a bit of time to become familiar with IOKit. Fundamentally, much of DriverKit design is either directly copied or predetermined by IOKit, so understanding IOKit can help you understand DriverKit.
More to the point, IOKit original documentation is quite good, particular when it comes to explaining the broader architecture and design. In terms of specific starting points, "IOKit Device Driver Design Guidelines" was the original "entry point" document, which then point to "IOKit Fundamentals" (KEXT APIs) and "Accessing Hardware From Applications" (user space).
Note that, as far as I can tell, basically everything IOKit related in "Accessing Hardware From Applications" is effectively current, accurate documentation. We've add GCD callback support and a few other convenience methods, but otherwise the API is completely unchanged since it was introduced in macOS 10.0.
__
Kevin Elliott
DTS Engineer, CoreOS/Hardware