Can't get started with kext development in OS X
Asked Answered
M

1

6

I'm trying to do Apple's "Kernel Extension" Tutorial. I've created a project file, created the Info.plist, built it, used kextlibs to learn the dependencies, added them to the Info.plist, rebuilt, copied to /tmp, used kextutil to test it. Everything fine so far. When I try loading the kext, though, my start/stop/probe functions never seem to get called. My IOLog messages don't appear in /var/log/system.log.

In fact, system.log does not show anything. kernel.log does after I activated kext logging with sysctl -w debug.kextlog=0x0007780E. kernel.log says: "Kext ch.digorydoo.driver.XinputDevice successfully resolved dependencies." Then: "Flushing nonloaded kexts and other unused data." Maybe my class immediately gets flushed?

My class appears at the end of kextstat, but with 0 references. The same with ioclasscount. In ioreg, my class does not appear.

I've set IOProviderClass to IOResources, so it should always have a reference, right? I've properly set IOMatchCategory.

Any help?!!?! Thanks a lot!

EDIT: Here's my Info.plist:

<?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>CFBundleDevelopmentRegion</key>
    <string>English</string>
    <key>CFBundleExecutable</key>
    <string>${EXECUTABLE_NAME}</string>
    <key>CFBundleName</key>
    <string>${PRODUCT_NAME}</string>
    <key>CFBundleIconFile</key>
    <string></string>
    <key>CFBundleIdentifier</key>
    <string>ch.digorydoo.driver.${PRODUCT_NAME:rfc1034identifier}</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundlePackageType</key>
    <string>KEXT</string>
    <key>CFBundleSignature</key>
    <string>????</string>
    <key>CFBundleVersion</key>
    <string>1.0.0</string>
    <key>IOKitPersonalities</key>
    <dict>
        <key>Generic Xinput Gamepad</key>
        <dict>
            <key>CFBundleIdentifier</key>
            <string>ch.digorydoo.driver.${PRODUCT_NAME:rfc1034identifier}</string>
            <key>IOProviderClass</key>
            <string>IOResources</string>
            <key>IOMatchCategory</key>
            <string>ch_digorydoo_driver_XinputDevice</string>
            <key>IOClass</key>
            <string>ch_digorydoo_driver_XinputDevice</string>
            <key>IOKitDebug</key>
            <integer>65535</integer>
        </dict>
    </dict>
    <key>OSBundleLibraries</key>
    <dict>
        <key>com.apple.kpi.iokit</key>
        <string>10.8</string>
        <key>com.apple.kpi.libkern</key>
        <string>10.8</string>
        <key>com.apple.kpi.mach</key>
        <string>10.8</string>
    </dict>
</dict>
</plist>
Murchison answered 5/1, 2013 at 6:10 Comment(7)
If IOProviderClass and IOMatchCategory are set, then you should be able to see the instance in IORegistryExplorer. Are you checking that too? Note that you shouldn't need to restart the machine to debug/develop this.Kirimia
Thanks for your reply. I've just added my Info.plist to the post. As you can see, there's both a IOProviderClass and IOMatchCategory. The class does not show in IORegistryExplorer.Murchison
Still confusing - If nobody requested your class why there would be a reference to it ?Jonellejones
Well, according to the tutorial, this is special about IOResources. It's a virtual driver with no device. Later, I'm going to use IOUSBDevice as the provider class, but first I must get this tutorial working...Murchison
Update: I now changed IOResources to IOUSBDevice in the plist above, removed IOMatchCategory and added an IOProbeScore of 0. When I plug in a new USB device, my kext should be instantiated, right? But it does not happen...Murchison
Have you added some debug output to your class's init() method? Maybe that's returning false, which would mean that probe and start are never called.Shatter
Yeah, my init() first calls super::init, stores the result in a bool, calls IOLog, then results the bool value. The log entry does not show up. Actually, I've tried doing something nasty in start() to force a kernel panic, but it doesn't happen, so I'm sure it never gets called. All the same, the verbose output of kextutil claims that it calls start()... Weird!Murchison
M
1

Still no clue why my simple KEXT is not started. Here's the output from kextutil:

$ kextutil -t -n XinputDevice.kext
No kernel file specified; using running kernel for linking.
Notice: XinputDevice.kext has debug properties set.
XinputDevice.kext appears to be loadable (including linkage for on-disk libraries).

Note that when I load the KEXT, it says 'loadable NOT including linkage', but then all the same says it loaded it:

$ kextutil -v 4 XinputDevice.kext 
Kext library architecture set to i386.
Kext library recording diagnostics for: validation authentication dependencies warnings.
Notice: XinputDevice.kext has debug properties set.
XinputDevice.kext appears to be loadable (not including linkage for on-disk libraries).
Loading XinputDevice.kext.
Reading load info for all kexts.
Reading loaded kext info from kernel.
Adding /private/tmp/XinputDevice.kext to mkext.
/private/tmp/XinputDevice.kext added 29260-byte noncompressed executable to mkext.
Created mkext for architecture i386 containing 1 kexts.
Loading XinputDevice.kext.
(kernel) Received request from user space to load kext ch.digorydoo.driver.XinputDevice.
(kernel) Recorded kext ch.digorydoo.driver.XinputDevice as a candidate for inclusion in prelinked kernel.
(kernel) Loading kext ch.digorydoo.driver.XinputDevice.
(kernel) Allocated link buffer for kext ch.digorydoo.driver.XinputDevice at 0x4d1e6000 (8192 bytes).
(kernel) Kext ch.digorydoo.driver.XinputDevice executable loaded; 2 pages at 0x4d1e6000 (load tag 114).
(kernel) Kext ch.digorydoo.driver.XinputDevice calling module start function.  ####
(kernel) Kext ch.digorydoo.driver.XinputDevice registered class ch_digorydoo_driver_XinputDevice.
(kernel) Kext ch.digorydoo.driver.XinputDevice has IOService subclass ch_digorydoo_driver_XinputDevice; enabling autounload.
(kernel) Kext ch.digorydoo.driver.XinputDevice is now started.
(kernel) Kext ch.digorydoo.driver.XinputDevice sending 1 personality to the IOCatalogue and starting matching.
(kernel) Kext ch.digorydoo.driver.XinputDevice loaded.
Successfully loaded XinputDevice.kext.
XinputDevice.kext successfully loaded (or already loaded).

On the line above that I marked with a ####, it says the start function was called. But that's not true, since my start() calls IOLog, which should write a message in /var/log/system.log, which but it doesn't.

Here's what shows up in /var/log/kernel.log:

Jan  5 15:20:00 karaboudjan3 kernel[0]: Kext ch.digorydoo.driver.XinputDevice, v1.0 registered and available for loading.
Jan  5 15:20:00 karaboudjan3 kernel[0]: Kext ch.digorydoo.driver.XinputDevice resolving dependencies.
Jan  5 15:20:00 karaboudjan3 kernel[0]: Kext ch.digorydoo.driver.XinputDevice added dependency com.apple.kpi.mach.
Jan  5 15:20:00 karaboudjan3 kernel[0]: Kext ch.digorydoo.driver.XinputDevice added dependency com.apple.kpi.iokit.
Jan  5 15:20:00 karaboudjan3 kernel[0]: Kext ch.digorydoo.driver.XinputDevice added dependency com.apple.kpi.libkern.
Jan  5 15:20:00 karaboudjan3 kernel[0]: Kext ch.digorydoo.driver.XinputDevice successfully resolved dependencies.
Jan  5 15:20:00 karaboudjan3 kernel[0]: Flushing nonloaded kexts and other unused data.

In fact, the class is loaded, but no instance is created:

$ ioclasscount | grep digory
ch_digorydoo_driver_XinputDevice = 0

$ kextstat | grep digory
  116    0 0x106f000  0x2000     0x1000     ch.digorydoo.driver.XinputDevice (1.0.0) <5 4 3>

The class does not show up in the I/O registry:

$ ioreg | grep Xinput
$ ioreg | grep digory

No instance means no call to init or start, hence no IOLog. If no one uses the class, it clearly does not show up in the registry of live objects.

My conclusion is that the tutorial must be wrong! The class is never instantiated even though its provider class is IOResources!

I tried replacing IOResources with IOUSBDevice. Since I'm not providing any idProduct or idVendor, any USB device should match. Unless I'm mistaken, the daemon listening to USB devices should instantiate my class and call probe() to see if this is the right driver for the device.

Unfortunately, I still didn't get any log messages in system.log!! Which daemon is responsible for USB devices? Maybe there's a driver that somehow "eats" the newly plugged device before my KEXT gets a chance? How can I track down which driver the daemon chooses?

Any hints?!?

Murchison answered 5/1, 2013 at 14:37 Comment(3)
Ha, it works, I can't believe it! :D All I had to do is set IOUSBDevice as the IOProviderClass, define a IOProbeScore (integer!) of 0, and define idProduct and idVendor (both integers!) and set them to a particular USB device. Documentation is wrong again, my IOLog messages don't appear in system.log, but they do in kernel.log!! Wow, that took me almost 10hrs... Stoopid!Murchison
Could you post what you ended up with?Oid
I think the reference you might need is Finding USB Devices and Interfaces. Table 1-2 shows which combinations of keys can be used together. You can also see what kinds of probe scores are likely to result from those key combinations in Tips on USB driver matching for Mac OS X.Abba

© 2022 - 2024 — McMap. All rights reserved.