Apple provides a number of kernel extensions in source form as part of the Darwin project. More are provided with OS X in binary form. Prior Darwin releases included a subset of the OS X kernel extensions with a license to use them.
Unfortunately, current Darwin releases only consist of source. No binary extensions nor license to use them has been provided with the Darwin 9 release. Some of thse extensions are rather critical to the operation of Darwin and therefore it seems you must have an OS X license in order to run Darwin.
In particular, AppleACPIPlatformExpert.kext is not available in source form. There is an older I386 generic platform expert which might be made to work but for now it's easier to use the binary from OS X. More distressingly, the IOATABlockStorage project and the IOSCSIArchitectureModelFamily project are among those available only in binary form which will be a huge stumbling block until they are either rewritten or Apple decides again to license the binaries for free use.
Eventually we can bootstrap out of this situation and the easiest way to do that is to start by using the existing binaries. Particularly when kernel code is involved it can be a real pain to debug since you have to have two Macs. And since Darwin isn't available anymore for non-Apple hardware, you can't even attempt to repurpose a commodity PC to run Darwin in place of using a second Mac.
And that brings us to VMware. VMware is the holy grail of bootstrapping free Darwin as it allows us to legally run non-free Darwin by doing so on real Macs. Yes, we could simply ignore the license terms, but that wouldn't be very nice.
Unfortunately, xnu needs a little bit of help if it is to run as a VMware guest. The in-kernel stuff is pretty easily fixed (see the xnu page) but beyond the kernel we need some drivers for "hardware" (does that term even make sense in a virtual machine?) which doesn't exist on a real Mac.
Want to run Leopard (Darwin 9) in VMware? Oops.. uhh.. where's the keyboard? With Darwin 8 you merely needed to compile ApplePS2Controller.kext and it would magically work. Now it doesn't. What gives!?
Well, you see ApplePS2Controller.kext as the name implies is intended to work with a legacy PS/2 keyboard/mouse controller. You know, keyboard interrupt 1, mouse interrupt 12? Now of course most hardware still includes something that works like the old i8042 but that's not really the issue. The issue is that the driver for it expects it to claim interrupts 1 and 12. But that's more a function of the i8259 interrupt controller, or rather the cascaded pair of them as found on the PC-AT and compatible systems. Of course, modern systems have no such thing but like the keyboard controller, they pretend to. Unfortunately, once you venture into ACPI land (e.g. by using the ACPI platform expert) you give this up.
This is of course a significant improvement, but how do you make the pre-existing i8042 code work? Enter the power of IOKit. The ApplePS2Controller driver only needs to have a provider that allows it to register with interrupts 1 and 12. As long as it can receive those interrupts, it's happy. But ACPI forms a tree structure and the ACPI Platform Expert sets up a hierarchy in the IORegistry to reflect this. Worse yet, the Plug'n'Play and ACPI specs together specify that the keyboard will have one PNP ID and the mouse a different one. That gets represented as two different nubs in the IORegistry.
Damn.. so how do you make a driver that expects to attach to one IOPlatformDevice nub work when you have two nubs? The answer is that you simply write a nub class which attaches to the other two nubs and thus provides a combined nub. It's a bit tricky since a typical IOKit instance only has one provider but it's not impossible. The trick is that you pick one of the nubs as your primary provider. In this case, we'll pick the keyboard nub. That's going to be provided by the ACPI PE with either PNP0303 or PNP030B as its name. Once you've started on that nub, you can then go find the mouse nub and attach yourself to it.
But then what about interrupts? Each of your providers only provides one interrupt but you need to provide two to your client (the ApplePS2Controller class). Furthermore, you need to provide them such that when it asks for interrupt 1 you give it the interrupt information from your keyboard provider and when it asks for interrupt 12 you give it the interrupt information from your mouse provider. That's not too difficult and the trick there is that at the IORegistry level, interrupt numbers are only indices into an array.
What's perhaps confusing about this to IOKit newbies is that the nub I'm discussing here used to be part of the AppleACPIPlatformExpert.kext. Yes Virginia, extensions can have multiple classes. And often times, one class will be the provider and another class will be a client to that provider. That is exactly the situation until OS X Leopard was released with an AppleACPIPlatformExpert.kext that did not contain the AppleACPIPS2Nub class.
Not a problem. All we need to do is rewrite the AppleACPIPS2Nub class and we're in business. Unfortunately, the IOKit documentation on this is pretty nonexistant so I chose instead to painstakingly reverse engineer the binary. The text above is of course the description of my findings and the source is of course based on this knowledge. That means the source is not a "clean-room" reimplementation of the nub.
Hopefully this kernel extension, unlike the class present in Apple's binary, will not only serve its functional purpose but also illustrate some of the trickier parts of the IOKit.
Without further ado, enjoy.
ApplePS2Controller and friends
Apple provides the source code for the PS/2 controller driver as well as the code for the mouse, keyboard, and trackpad drivers.
When using this code you'll see that the AppleACPIPlatfromExpert will provide the two nubs (IOACPIPlatformDevice instances) which the above AppleACPIPS2Nub class will attach to. That class itself is a nub which ApplePS2Controller will attach to. The ApplePS2Controller class then provides two nubs (ApplePS2KeyboardDevice and ApplePS2MouseDevice) which the drivers in the ApplePS2Keyboard and ApplePS2Mouse kernel extensions will then attach to.
You can view all of this using ioreg -w0. If you want to see how the interrupt controllers/specifiers properties work you may want to add the -l flag.
Note that ApplePS2Controller drives the controller (so it's a driver) but it also provides a nub that the mouse and keyboard drivers can attach to. Therefore, you'll also need the mouse and keyboard drivers.
Now, a possible alternative implementation would be to instead create subclasses of ApplePS2KeyboardDevice and ApplePS2MouseDevice which attach directly and independently to the ACPI nubs instead of to the PS2 controller. On the other hand, it is classically modeled as one device, not two independent devices and doing it this way would prevent common code in ApplePS2Controller from driving the controller as one device.
Browse the source
Another problem for OS X running on non-Apple hardware (virtual or otherwise) is the AppleIntelCPUPowerManagement kernel extension which tends to either panic the machine or spew endless debug messages to the console regarding the HPET and its relationship to the CPU.
In a virtual environment there is really no need for the guest OS to change processor C states. The built-in code which simply issues a halt instruction is sufficient. Furthermore, the virtualizer may wish to implement only enough of the HPET to get past the xnu startup routines without actually making the HPET work.
Therefore I have written the NullCPUPowerManagement extension. What it does is play a couple of tricks with the IOKit service registration process to ensure it takes over the AppleIntelCPUPowerManagement match category on its IOResources provider nub. The trick is that any nub matching on the IOResources nub must have a nub name identical to the value of its IOMatchCategory property. The nub name is by default the value of its IOClass property which must be its C++ class name and ought not to be AppleIntelCPUPowerManagement. Why not? Because the kernel cannot load two C++ classes with the same name. When this occurs, one or the other will win. In the best case, our copy would win. But in the worst case the real AppleIntelCPUPowerManagement wins and thus loads and wreaks havoc which is exactly what we are trying to avoid.
Therefore we use our own C++ class name but then from the init method we retrieve the value of the IOMatchCategory key and call setName to set the nub name to match. Combined with a probe score of 100 we guarantee that our provider (IOResources) will choose us over Apple's kext. Ideally we would implement an active probe method that did some checking to see if the system is capable of using AppleIntelCPUPowerManagement. Unfortunately it's hard to know exactly what conditions must be met. Therefore, this version (r11) always succeeds its probe unless you pass -allowAppleCPUPM on the command-line in which case it will fail its probe which allows AppleIntelCPUPowerManagement to attach.
The option isn't guaranteed to stay around, it's more for debugging purposes so you can keep the NullCPUPowerManagement kext in your Extensions folder while you play around trying to make Apple's CPU PM work. In case you don't get it right you can simply remove the -allowAppleCPUPM from your boot flags and you'll still be able to boot your system.