IOPCIFamily matching precedence and runtime behavior for unmatched PCIe functions

I'm working on diagnostic tooling for PCIe storage devices and I've run into a gap in my understanding of how IOKit resolves matching against a single PCIe function, and what the kernel continues to do with a function that nothing claims.

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.

Questions on the matching side:

When two personalities match on IOPCIPrimaryMatch for the same vendor/device ID, is IOProbeScore the only tiebreaker? 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. If a higher-scored driver's probe() returns NULL, does the nub reliably fall through to the next candidate, including a family driver? Is there a case where a failed probe leaves the nub unmatched rather than retrying lower-scored candidates? 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?

Questions on runtime behavior:

If a PCIe function ends up with no driver attached, what does IOPCIFamily continue to do with it? Specifically, does it may issue config space accesses, participate in the IOKit power management tree, transition the function to D3 on system sleep or on idle, and save/restore config space across wake? Related: does an unmatched function may get a DART/VT-d mapping established, and does IOPCIFamily poll or act on link status or AER state for it?

The distinction in 4 and 5 matters a lot for my case. If an unmatched nub is genuinely inert from the device's point of view, blocking driver attachment is a complete solution. If IOPCIFamily is may 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.

Finally — is any of this reachable from DriverKit, or does a per-device matching override necessarily mean a kext? I'd rather build on something supported if a supported path exists.

Happy to be pointed at headers or open-source IOPCIFamily if the answers are best read from source; I'm mainly trying to confirm the intended behavior rather than infer it from observation.

IOPCIFamily matching precedence and runtime behavior for unmatched PCIe functions
 
 
Q