Gain user-space access to hardware devices and drivers using IOKit.

Posts under IOKit tag

43 Posts
Sort by:

Post

Replies

Boosts

Views

Activity

Sanitize and other commands for External SSD Media
I am trying to implement the Sanitize and Firmware Upgrade commands for an external card connected via the Thunderbolt Interface (4.0) Should we consider writing a Kext based off IOBlockStorageDriver or IOPCI interface. NVMController does not expose anything more than the 3 API.. SmartReadData, getLogData and getIdentifyData. The device is connected only on MacOS.(mini and macbooks) My ioregistry look like : | | | +-o DSB1@1 <class IOPCIDevice, id 0x1000003dc, registered, matched, active, busy 0 (194 ms), retain 14> | | | | +-o IOPP <class IOPCI2PCIBridge, id 0x100000457, registered, matched, active, busy 0 (182 ms), retain 8> | | | | +-o UPS0@0 <class IOPCIDevice, id 0x1000003e0, registered, matched, active, busy 0 (182 ms), retain 17> | | | | +-o IOPP <class IOPCI2PCIBridge, id 0x100000477, registered, matched, active, busy 0 (181 ms), retain 8> | | | | +-o pci-bridge@0 <class IOPCIDevice, id 0x1000003e1, registered, matched, active, busy 0 (181 ms), retain 11> | | | | +-o IOPP <class IOPCI2PCIBridge, id 0x10000047d, registered, matched, active, busy 0 (180 ms), retain 8> | | | | +-o pci1987,5021@0 <class IOPCIDevice, id 0x1000003e2, registered, matched, active, busy 0 (180 ms), retain 12> | | | | +-o IONVMeController <class IONVMeController, id 0x100000486, registered, matched, active, busy 0 (166 ms), retain 11> | | | | +-o IONVMeBlockStorageDevice@1 <class IONVMeBlockStorageDevice, id 0x10000048c, registered, matched, active, busy 0 (166 ms), retain 11> | | | | +-o IOBlockStorageDriver <class IOBlockStorageDriver, id 0x10000048d, registered, matched, active, busy 0 (166 ms), retain 8> | | | | +-o Prograde Digital Media <class IOMedia, id 0x10000048e, registered, matched, active, busy 0 (166 ms), retain 12> | | | | +-o IOMediaBSDClient <class IOMediaBSDClient, id 0x100000490, registered, matched, active, busy 0 (0 ms), retain 6> | | | | +-o IOGUIDPartitionScheme <class IOGUIDPartitionScheme, id 0x100000492, !registered, !matched, active, busy 0 (0 ms), retain 7> | | | | +-o EFI System Partition@1 <class IOMedia, id 0x1000004d7, registered, matched, active, busy 0 (0 ms), retain 10> | | | | | +-o IOMediaBSDClient <class IOMediaBSDClient, id 0x1000004db, registered, matched, active, busy 0 (0 ms), retain 6> | | | | +-o Untitled 2@2 <class IOMedia, id 0x1000004d9, registered, matched, active, busy 0 (0 ms), retain 11> | | | | +-o IOMediaBSDClient <class IOMediaBSDClient, id 0x1000004df, registered, matched, active, busy 0 (0 ms), retain 7>Any pointers on this would be helpful.
0
0
533
Mar ’24
How to properly convert any size of application memory into the kernel space of Driverkit?
Hardware and software configuration MacBook Air M2 2022 16GB, MacOS Ventura 13.2.1 Full description This is a DriverKit that controls PCIE FPGA devices for low-latency data exchange. This driver has been implemented on Iokit, and now it needs to be launched on Driverkit to adapt to newer Macs. Driverkit lacks the IOMemoryDescriptor::withAddressRange(Iokit) function to convert the app's memory of any size to a Descriptor. Currently, we use args->structureOutputDescriptor->CreateMapping to map the Descriptor passed by the application to the kernel layer. // App size_t ***::xxRead(long long addr, size_t size, void * buff){ std::lock_guard<std::mutex> guard(usrLock); kern_return_t kr; uint64_t info[2] = {(uint64_t)addr, (uint64_t)size}; kr = IOConnectCallMethod( connect, kUserReadIO, info, 2, NULL, NULL, NULL, NULL, buff, &size); return size; } // Driverkit const IOUserClientMethodDispatch sMethods[kNumMethods] = { [kUserReadIO] = { (IOUserClientMethodFunction) &SmiPcieUc::sUserReadIo, .checkCompletionExists = false, .checkScalarInputCount = 2, // Read Addr, size .checkStructureInputSize = 0, .checkScalarOutputCount = 0, .checkStructureOutputSize = kIOUserClientVariableStructureSize} // Read Data }; kern_return_t SmiPcieUc::sUserReadIo (OSObject * target, void* reference, IOUserClientMethodArguments* args){ IOMemoryMap * memMap = nullptr; uint32_t * buffKptr = nullptr; kern_return_t rt = 0; if(target == nullptr){ Log("***Err***: sUserReadIo Target is Null!"); return kIOReturnError; } if(args->structureOutputDescriptor){ rt = args->structureOutputDescriptor->CreateMapping(0,0,0,0,0, &memMap); if(rt == kIOReturnSuccess){ buffKptr = reinterpret_cast<uint32_t *>(memMap->GetAddress()); } else { Log("***Err***: sUserReadIo Mapping Failed!"); return kIOReturnNoMemory; } } else { buffKptr = (uint32_t *) args->structureOutput; } rt = ((SmiPcieUc *)target)->UserReadIo((uint64_t *)&args->scalarInput[0], (size_t *)&args->scalarInput[1], buffKptr); OSSafeReleaseNULL(memMap); return rt; } phenomenon When StructureOutputSize is greater than 4096, args>structureOutputDescriptor exists, and when it is less than or equal to 4096, args->structureOutputDescriptor and args->structureOutput are both equal to nullptr, (in IOkit, args->structureOutput is not empty)。 How to properly convert any size of application memory into the kernel space of Driverkit?
1
0
687
Mar ’24
monitor input events
Hello, I need to monitor input events and convert keycode to symbol. E.g. convert kHIDUsage_KeyboardQ to symbol according to used keyboard layout. Are there any API to get current keyboard layout (language) in C++? If I understand correctly, the API TISGetInputSourceProperty() is deprecated. Are there any way to monitor keyboard layout changed? (some system notification in case of keyboard layout change) Are there any way to translate keycode to symbol except UCKeyTranslate() which is part of deprecated Unicode Utilities? Thank you in advance.
1
0
593
Feb ’24
smc keys for M3 pro chip, returning temperature values
Im honestly a bit lost and looking for general pointers. Here is the general flow of my project. I have an Xcode project where I want to return and convert the temperature values accessed from the apple smc and I found a GitHub repo with all the smc key sensors for the M3Pros/Max chips: https://github.com/exelban/stats/issues/1703 basically, I have all these keys stored in an array in obj-c like so: NSArray *smcKeys = @[ @"Tp01", @"Tp05", @"Tp09", @"Tp0D", @"Tp0b", @"Tp0f", @"Tp0j", @"Tp0n",@"Tp0h", @"Tp0L", @"Tp0S", @"Tp0V", @"Tp0z", @"Tp0v", @"Tp17", @"Tp1F", @"Tp1J", @"Tp1p", @"Tp1h", @"Tp1R", ]; I am passing all these keys by passing 'smcKeys' in a regular C code file I have here that is meant to open, close and read the data shown here: #include "smc.h" #include <mach/mach.h> #include <IOKit/IOKitLib.h> #include "smckeys.h" io_connect_t conn; kern_return_t openSMC(void) { kern_return_t result; kern_return_t service; io_iterator_t iterator; service = IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceMatching("AppleSMC"), &iterator); if(service == 0) { printf("error: could not match dictionary"); return 0; } result = IOServiceOpen(service, mach_task_self(), 0, &conn); IOObjectRelease(service); return 0; } kern_return_t closeSMC(void) { return IOServiceClose(conn); } kern_return_t readSMC(char *smcKeys, SMCVal_t *val) { kern_return_t result; uint32_t keyCode = *(uint32_t *)smcKeys; SMCVal_t inputStruct; SMCVal_t outputStruct; inputStruct.datasize = sizeof(SMCVal_t); inputStruct.datatype = 'I' << 24; //a left shift operation. turning the I into an int by shifting the ASCII value 24 bits to the left inputStruct.data[0] = keyCode; result = IOConnectCallStructMethod(conn, 5, &inputStruct, sizeof(SMCVal_t), &outputStruct, (size_t*)&inputStruct.datasize); if (result == kIOReturnSuccess) { if (val -> datasize > 0) { if (val -> datatype == ('f' << 24 | 'l' << 16 | 't' << 8 )) { //bit shifting to from 32bit operation associated with the ASCII charecters'f', 'l', and 't', sets datatype field. double temp = *(double *)val -> data; return temp; } } } return 0.0; } Which I am then then calling the functions from this file in a swift file and converting the values to Fahrenheit but no data is being printed in my console: import IOKit public class getTemperature { public struct SMCVal_t { var datasize: UInt32 var datatype: UInt32 var data: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) } @_silgen_name("openSMC") func openSMC() -> kern_return_t @_silgen_name("closeSMC") func closeSMC() -> kern_return_t @_silgen_name("readSMC") func readSMC(key: UnsafePointer<CChar>?,val: UnsafeMutablePointer<SMCVal_t>) -> kern_return_t func convertAndPrintTempValue(key:UnsafePointer<CChar>?,scale: Character, showTemp: Bool ) -> kern_return_t { let openSM = openSMC() guard openSM == 0 else { print("Failed to open SMC: \(openSM)") return kern_return_t() } let closeSM = closeSMC() guard closeSM == 0 else { print("could not close SMC: \(closeSM)") return IOServiceClose(conn) } func convertAndPrint(val: SMCVal_t) -> Double { if val.datatype == (UInt32("f".utf8.first!) << 24 | UInt32("l".utf8.first!) << 16 | UInt32("t".utf8.first!) << 8) { let extractedTemp = Double(val.data.0) return( extractedTemp * 9.0 / 5.0 + 32.0 ) } return 0.0 } let smcValue = SMCVal_t(datasize: 0, datatype: 0, data: (0,0,0,0,0,0,0,0)) let convertedVal = convertAndPrint(val: smcValue) print("Temperarure:\(convertedVal)F°") return kern_return_t() } } I know this is a lot but I am honestly looking for any tips to fill in any gaps in my knowledge for anyone who's built a similar application meant to extract any sort of data from Mac hardware.
1
0
711
Feb ’24
How to make the device communicate with the iPhone 15 APP using the USB-C interface?
Before iPhone 15, the lighting interfaces required communication with external devices through mfi authentication,But there is no evidence to suggest that the USB-C interface needs to add an MFi authentication chip for authentication detection. Is there a way to use USB-C to detect external devices and communicate with each other in the app now, so that I can create my app and communicate with hardware devices
1
1
833
Jan ’24
Vision Pro - USB Support?
Does the Vision Pro Battery Pack allow data passthrough to the Vision Pro? Specifically, I am wanting to confirm whether the platform supports FIDO2 / YubiKey style devices. If someone has a DevKit, could you try testing a keyboard or thumb drive to see if either of those work. If so, I should have all I need to proceed with my project. Thanks.
0
0
640
Jan ’24
kIOReturnNotPermitted from IOHIDManagerOpen under lldb
I'm writing a C/C++ command line program which, at some point, calls IOHIDManagerOpen. I've added both my program executable and lldb as permitted for input monitoring (as far as I remember, my program was added after showing up a permission prompt, I've added lldb manually later, trying to resolve the problem). My problem is that when I run my program from within lldb in Terminal, the call to IOHIDManagerOpen returns kIOReturnNotPermitted. When I run my program directly in the terminal session (without lldb), this call returns kIOReturnSuccess. Such behaviour means it will be impractical to use lldb for any debugging of this program. What can be done to make lldb session behave the same way, the normal execution works? I'm on: 23.2.0 Darwin Kernel Version 23.2.0: Wed Nov 15 21:55:06 PST 2023; root:xnu-10002.61.3~2/RELEASE_ARM64_T6020 arm64 and: lldb-1500.0.200.58 Apple Swift version 5.9.2 (swiftlang-5.9.2.2.56 clang-1500.1.0.2.5)
2
0
934
Jun ’24
USB DriverKit returning large asynchronous data
Can anyone advise, or give example of, communicating large (>128 byte) incoming buffers from a dext to a user-space app? My specific situation is interrupt reads from a USB device. These return reports which are too large to fit into the asyncData field of an AsyncCompletion call. Apple's CommunicatingBetweenADriverKitExtensionAndAClientApp sample shows examples of returning a "large" struct, but the example is synchronous. The asynchronous example returns data by copying into a IOUserClientAsyncArgumentsArray, which isn't very big. I can allocate a single buffer larger than 4K in user space, and communicate that buffer to my driver as an IOMemoryDescriptor when I set up my async callback. The driver retains the descriptor, maps it into its memory space and can thus write into it when the hardware returns interrupt data. The driver then calls AsyncCompletion, which will cause my user-side callback to be called, so the user side software knows that there's new data available in the previously allocated buffer. That's fine, it works, but there are data race problems - since USB interrupt reads complete whenever the hardware has provided data, incoming completions happen at unpredictable times, so the shared buffer contents could change while the user side code is examining them. Is there an example somewhere of how to deal with this? Can I allocate memory on the driver side on demand, create an IOMemoryDescriptor for it and return that descriptor packed inside the asyncData? If so, how does the driver know when it can relinquish that memory? I have a feeling there's something here I just don't understand...
1
0
659
Jan ’24
IOKit with Swift
I have some Objective C code, that I can not find the equivalent for Swift (many symbols are not present). It just lists all serial devices that match a prefix: + (NSArray*)serialDevicesWithPrefix:(NSString*)prefix { NSMutableArray* modems=[NSMutableArray arrayWithCapacity:10]; kern_return_t kernResult; mach_port_t masterPort ; CFMutableDictionaryRef classesToMatch; io_iterator_t matchingServices; NSString* serialDevice=@"/dev/cu."; serialDevice=[serialDevice stringByAppendingString:prefix]; // Lets find all serial device types ; // Get the port for communications to kernal kernResult = IOMainPort(kIOMainPortDefault, &masterPort) ; if (kernResult == KERN_SUCCESS) { // We got a good result! classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue) ; if (classesToMatch != NULL) { // Now, we need to say we are looking for RS232 type of connections // Each serial device object has a property with key // kIOSerialBSDTypeKey and a value that is one of // kIOSerialBSDAllTypes, kIOSerialBSDModemType, // or kIOSerialBSDRS232Type. You can change the // matching dictionary to find other types of serial // devices by changing the last parameter in the above call // to CFDictionarySetValue. CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDRS232Type)); kernResult = IOServiceGetMatchingServices(masterPort, classesToMatch, &matchingServices); if (kernResult == KERN_SUCCESS) { // We did something! // Matching serveres are the ones that "match" our need, now we have to loop through that and get the names io_object_t modemService; while ((modemService = IOIteratorNext(matchingServices))) { CFTypeRef deviceFilePathAsCFString; // Get the callout device's path (/dev/cu.xxxxx). // The callout device should almost always be // used. You would use the dialin device (/dev/tty.xxxxx) when // monitoring a serial port for // incoming calls, for example, a fax listener. deviceFilePathAsCFString = IORegistryEntryCreateCFProperty(modemService,CFSTR(kIOCalloutDeviceKey),kCFAllocatorDefault,0); if (deviceFilePathAsCFString) { // We got it! NSString* currentModem=(__bridge_transfer NSString *)deviceFilePathAsCFString; if ([currentModem hasPrefix:serialDevice]) { [modems addObject:currentModem]; } } IOObjectRelease(modemService) ; // We need to release it! } // We should release our iterator IOObjectRelease(matchingServices) ; } } } return modems ; } Does anyone have ideas?
2
0
673
Jan ’24
linker error | undefined symbols
I have a C file for accessing the apple smc and I have the corresponding header file with my declarations in it but when I build my Xcode project I get the error: "ld: Undefined symbols: _getTemperature, referenced from: _main in getsmc.o clang: error: linker comm" #include &lt;stdio.h&gt; #include &lt;IOKit/IOKitLib.h&gt; typedef struct { uint32_t datasize; uint32_t datatype; uint8_t data[32]; } SMCVal_t; io_connect_t conn; kern_return_t openSMC(void) { kern_return_t result; kern_return_t service; io_iterator_t iterator; service = IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceMatching("AppleSMC"), &amp;iterator); if(service == 0) { printf("error: could not match dictionary"); return 0; } result = IOServiceOpen(service, mach_task_self(), 0, &amp;conn); IOObjectRelease(service); return 0; } kern_return_t closeSMC(void) { return IOServiceClose(conn); } double getTemperature(char *key); kern_return_t readSMC(char *key, SMCVal_t *val) { kern_return_t result; uint32_t keyCode = *(uint32_t *)key; SMCVal_t inputStruct; SMCVal_t outputStruct; inputStruct.datasize = sizeof(SMCVal_t); inputStruct.datatype = 'I' &lt;&lt; 24; //a left shift operation. turning the I into an int by shifting the ASCII value 24 bits to the left inputStruct.data[0] = keyCode; result = IOConnectCallStructMethod(conn, 5, &amp;inputStruct, sizeof(SMCVal_t), &amp;outputStruct, (size_t*)&amp;inputStruct.datasize); if (result == kIOReturnSuccess) { if (val -&gt; datasize &gt; 0) { if (val -&gt; datatype == ('f' &lt;&lt; 24 | 'l' &lt;&lt; 16 | 't' &lt;&lt; 8 )) { float temp = *(float *)val -&gt; data; return temp; } } } return 0.0; } int main(void) { kern_return_t result; result = openSMC(); if(result == kIOReturnSuccess) { double temp = getTemperature("TC0P"); printf("temp: %.2f\n", temp); result = closeSMC(); } return 0; }
2
0
752
Jan ’24
Help needed with lldb - attach to Mac after a kernel panic
I am trying to debug a kernel panic in our kext. I can attach to the target Mac over ethernet if I: cause an NMI using add an IOPanic call to my kext and cause it to be executed use Dtrace to invoke a panic However if I reproduce the kernel panic which I am investigating, the Mac just restarts. How can I make the Mac wait for me to attach with lldb rather than restarting? My target configuration is: Mac is 2021 M1 Pro 14" MacBook Pro macOS 14.2 (23C64) Network: Apple Thunderbolt 3 <-> Thunderbolt 3 adapter + Apple Thunderbolt 2 to ethernet adapters Boot-args = "debug=0x44 wdt=-1 kdp_match_name=en8" (I have also tried debug=0x104C0C)
2
0
788
Jan ’24
Control HID USB Device from iPad?
I have a custom HID USB device that I can control on Mac with IOKit via the default HID MacOS driver. I am using IOHIDManager to detect it and send reports to it. I would like to extend this capability to iPad but the full IOKit framework is not supported on iOS/iPadOS. I saw that USBDriverKit is now supported on iPads with an M1 chip or newer. But, both MacOS and Windows can operate the device with their generic HID driver. As such, having to create a whole custom driver to interact with an HID device on iOS is really overkill. Would registering it in the MFi Program and operating it with the External Accessory framework be the correct route to take here? Or is there another framework for controlling HID devices on iPad over USB that I am not aware of?
2
0
1.3k
Dec ’23
Temperature was not able to be retrieved
0 I am building a simple macOS menu bar app in swift that displays my machine's CPU temperature and I'm just testing out my function to make sure it can retrieve the temp and display it in the Xcode terminal but instead my error handler messages are triggered indicating an issue retrieving my machines CPU temp data "CPU temp °F could not be retrieved temps couldnot be displayed" import Cocoa import IOKit import IOKit.ps class CPUTempWithServiceMatching { static func getCPUTemp() -> Double? { let dictionaryMatching = IOServiceMatching("AppleSMC") var service = IOServiceGetMatchingService(kIOMainPortDefault, dictionaryMatching) var temp = "0.0" if service != 0 { let key = "TC0P" //thermal zone zero proxy if let result = IORegistryEntryCreateCFProperty(service, key as CFString, kCFAllocatorDefault, 0 ) { temp = (result.takeUnretainedValue() as! NSNumber).doubleValue.description IOObjectRelease(service) if let CPUTemp = Double(temp) { print("CPU Temp: \(CPUTemp) °F") return(CPUTemp) } } print("CPU temp °F could not be retrieved") } return nil } } @main struct program { static func main() { if let cpuTemp = CPUTempWithServiceMatching.getCPUTemp() { print("cpu temp\(cpuTemp) °F") } else { print("temps couldnot be displayed") } } }
1
0
635
Dec ’23
IOServiceOpen Keeps Returning kIOReturnUnsupported
I have a custom HID device that works on Windows totally fine with the default HID driver. The HID device knows how to accept string commands to control it and respond accordingly. I am trying to control it on Mac using IOKit. I am able to get the io_service_t reference to the IOHIDInterface for the device from the I/ORegistery. I use this to instantiate a class to represent the device. But when I try to establish a connection to the service, I keep getting -536870201 which corresponds to kIOReturnUnsupported. I'm not entirely sure what I am doing wrong here and I wasn't able to find anything online that could really help.
1
0
690
Dec ’23
Metal Device Architecture on macOS versions before Sonoma
Hello, The latest version of Metal (on macOS 14) seems to have the instance property for the Metal device architecture. I was curious how this could be achieved on versions of macOS prior to 14 programatically? xcrun metal-arch The above command provides exactly what I need. I was thinking that I could use Metal and IOKit to grab the architecture, but I am lead to believe that there is some kind of mapping in the metal-arch tool and it may not be that easy. Any help would be greatly appreciated. Thanks.
1
0
652
Dec ’23
IOHIDDeviceSetReport only works one time
I'm using IOKit to connect to a custom USB HID device. I'm using XCode 14 and running Swift/SwiftUI. So far I have had great success reading from the device with IOHIDDeviceGetReport and this can be done repeatedly with no issues. However, when I use IOHIDDeviceSetReport, I can only successfully set the report to the device one time correctly but any subsequent call to this function would just end up with an I/O Timeout. Any calls using IOHIDDeviceGetReport still works fine so the USB Device is still functioning correctly, but I couldn't receive any additional IOHIDDeviceSetReport call. If I unplug the USB and plug it in again, I can once again successfully send a command. This is, of course, not very practical for the end user to have to unplug and plug the device in after a single set command, and I don't quite understand what's going on with this. Here's my SetOutputReport function to call the IOHIDDeviceSetReport. The IOHIDDevice must already be connected and opened before calling this function so it's not nil. I don't have this "one shot send command" problem on the PC (Windows 7, 10, 11) or Android (v.11,12,13,14) implementation of this Custom USB HID device. It seems like there's something at the lower level of IOHIDDeviceSetReport on macOS which might be done differently than what's available on the PC or Android. Many searches on the web yielded no useful results. There's an IOHIDDeviceSetReportWithCallback function and it also seems only to work one time as well. private func SetOutputReport(dev: IOHIDDevice? ,reportID: Int, data:[UInt8], reportLength: Int) -&gt; String{ let inputReportID = reportID var buffer = data buffer[0] = UInt8(inputReportID) let bufferPointer = UnsafeMutablePointer&lt;UInt8&gt;.allocate(capacity: buffer.count) bufferPointer.initialize(from: &amp;buffer, count: buffer.count) //print(bufferPointer[0]) let bufferLength: CFIndex = buffer.count var success: IOReturn = kIOReturnError if(dev != nil){ success = IOHIDDeviceSetReport(dev!, kIOHIDReportTypeOutput, CFIndex(buffer[0]), bufferPointer, bufferLength) } print("Set report result \(krToString(success))") return krToString(success) }
1
0
932
Nov ’23