Reordering things a bit for clarity:
If a higher-scored driver's probe() returns NULL, does the nub reliably fall through to the next candidate, including a family driver?
Returning "NULL" from probe essentially means "not it". That driver is completely removed from the matching process and totally ignored. Note that our codeless DEXT/KEXT architecture actually relies on this behavior, as what that driver actually does is modify the provider (using the configuration embedded in its matching dictionary), then fail probe (so it's out of the "running").
However, the larger issue is that we always probe all candidates, as probe is how we identify which drivers are "worth" trying to start(). The value returned by individual candidates is irrelevant.
Is there a case where a failed probe leaves the nub unmatched rather than retrying lower-scored candidates?
Did you mean start()? Generally speaking, we'll go down the list of candidates until we find one where start succeeds, and I'd expect that process to normally "run to completion". However, one thing to be careful of here is the nuance of what happens AFTER start(). For example, if a driver succeeds start() and then unloads itself, then that could leave the nub sitting "unmatched".
When two personalities match on IOPCIPrimaryMatch for the same vendor/device ID, is IOProbeScore the only tiebreaker?
So, first of all, collisions like this rarely happen in practice. Much of our documentation is written as if matching involved a large range of choices, but, in practice, the behavior looks a lot more like:
-
Most common -> There's one driver, typically either a system class driver for a broad class of hardware or a vendor-specific driver for something we don't recognize.
-
Less common -> There are two drivers, typically the system class driver and a vendor-specific driver (for #1).
-
Very rare -> There are more than two drivers.
That's ESPECIALLY true "post-probe". I've seen cases where multiple drivers passively matched the same device in most cases that was because additional validation was happening in the probe that meant that only one of them was actually going to work.
Note that this means our documentation tends to exaggerate the actual role of "IOProbeScore“ - it doesn't matter how many people voted for you when only one person is running for office.
I've seen suggestions that which kext collection a personality lives in (boot vs. auxiliary) also influences the outcome, and I'd like to know whether that's genuinely part of the matching algorithm or an artifact of load ordering.
So, there are two issues here:
-
The primary factor is that the KEXT collection obviously affects when you’re eligible for loading, so a KEXT in the boot cache can/will load in cases where there auxiliary won't.
-
The loading process is deterministic, so in a "complete" collision (meaning, the two drivers are IDENTICAL candidates) the same driver will always win. That does mean things like kext collection can affect ordering.
Note that #2 is an implementation detail, not reliable behavior. Theoretically we could randomize the list and I'm sure that the specifics of ordering have changed over time.
However, in practice none of that really matters as the actual answer is to simply set up your match then "juice" your probe score high enough that you reliably win.
Are there properties on an IOPCIDevice nub that gate matching independently of score? IOPCITunnelCompatible clearly does something like this for tunneled devices, which suggests the general mechanism exists — is there a documented, per-function form of it?
The details of matching are actually determined by the individual family, but the general pattern is some form of prioritization based on keys, with probe score then being the final tie breaker. Related to that point:
Happy to be pointed at headers or open-source IOPCIFamily if the answers are best read from source;
...IOPCIFamily is open source and largely "complete", including its DEXT implementation and everything related to matching.
If an unmatched nub is genuinely inert from the device's point of view, blocking driver attachment is a complete solution. If IOPCIFamily is causing driving power state transitions on it, then a device that fails during D3 entry or exit will still take the system down, and I need a different approach.
You'll need to review the source to confirm the exact details, but my expectation is that it's basically inert. The power state transitions might be on exception, however, as I think we'd need to transition the hardware in order to sleep and an unmatched nub can't/shouldn't prevent sleep.
Finally — is any of this reachable from DriverKit, or does a per-device matching override necessarily mean a kext?
I have a forum post on this here, but the summary is that DriverKit and KEXT matching are actually the same process, as DEXT provides an IOKitPersonalities dictionary that describes what they want loaded in the kernel with some additional keys that then connect that kernel driver to the DEXTs user space process. The primary imitation here is that DEXTs don’t really load at boot, which can mean you get different configuration if a device is plugged in a boot vs. hot plugged.
Finally, returning to here:
The scenario I'm designing around: an NVMe controller that is degraded but still enumerable. It responds to config space reads and completes some admin commands, but intermittently times out — in the worst case on Identify — which surfaces as a kernel panic rather than a recoverable error. For test and triage purposes I want the ability to leave such a device physically installed while preventing the storage stack from binding to it, scoped to that one function rather than to NVMe generally.
The main issue here is that if the device is attached when the machine boots, then I believe the standard system driver could load, which might bypass DriverKit. Once the machine is booted, I think DriverKit would work fine for this. Note that a big question here is what you actually want to "ship" and how "friendly" you want this to be. For something like this, there's a big difference between a personal use product you're using under controlled conditions and a "consumer" product you'd expect random people to install and use.
__
Kevin Elliott
DTS Engineer, CoreOS/Hardware