Debug a driver when it fails to load

This guide provides best practices for debugging a new driver when it fails to load in your Fuchsia system.

Check if your driver is currently running in the system

In Fuchsia, a driver gets loaded in the system when the driver is matched to a node that represents a hardware or virtual device. Once matched and loaded, the driver starts running and provides services to other components in the system.

To see if a driver is loaded (that is, currently running) in your Fuchsia system, run the following command:

ffx driver list --loaded

This command prints the list of drivers loaded in the system, for example:

$ ffx driver list --loaded
fuchsia-boot:///#meta/bus-pci.cm
fuchsia-boot:///#meta/fvm.cm
fuchsia-boot:///#meta/hid.cm
fuchsia-boot:///#meta/intel-rtc.cm
fuchsia-boot:///#meta/netdevice-migration.cm
fuchsia-boot:///#meta/network-device.cm
fuchsia-boot:///#meta/pc-ps2.cm
fuchsia-boot:///#meta/platform-bus-x86.cm
fuchsia-boot:///#meta/platform-bus.cm
fuchsia-boot:///#meta/ramdisk.cm
fuchsia-boot:///#meta/sysmem.cm
fuchsia-boot:///#meta/virtio_block.cm
fuchsia-boot:///#meta/virtio_ethernet.cm
fuchsia-pkg://fuchsia.com/virtual_audio#meta/virtual_audio_driver.cm
fuchsia-pkg://bazel.pkg.component/qemu_edu#meta/qemu_edu.cm
fuchsia-boot:///#meta/block.core.cm
fuchsia-boot:///#meta/intel-i2c-dfv2.cm

Alternatively, if you know the exact driver component, you can use the ffx component show command to view the state of the component, for example:

$ ffx component show intel-i2c-dfv2
               Moniker: /bootstrap/boot-drivers:root.sys.platform.pt.pci.00_15_2.composite
                   URL: fuchsia-boot:///#meta/intel-i2c-dfv2.cm
                  Type: CML dynamic component
       Component State: Resolved
 Incoming Capabilities: fuchsia.boot.Items
                        fuchsia.driver.compat.Service
                        fuchsia.logger.LogSink
                        pkg
  Exposed Capabilities: diagnostics
                        fuchsia.driver.compat.Service
       Execution State: Running
          Start reason: Instance is in a single_run collection
 Outgoing Capabilities: fuchsia.driver.compat.Service

If you discover that your driver is not loaded in the system, see the next section for the steps you can take to debug the issue.

Best practices for debugging

During the initial phase of writing a new Fuchsia driver, you’d first want to make sure that your new component is recognized as a driver in your Fuchsia system. Once the component is listed as a driver in your Fuchsia system, you can start working on writing bind rules so that your driver can bind to a specific node that represents your target device in the system. When you have the driver bound to your target node (thus, the driver is successfully loaded in the system), you can then move on to the next phrase of development, which is to start implementing features for the driver.

When debugging a driver that fails to load in your Fuchsia system, consider the following steps:

  1. Check the component manifest.
  2. Register your driver with the driver framework.
  3. Verify that the bind rules are correct.

1. Check the component manifest

For a Fuchsia system to view a component as a driver, the component’s runner field in the component manifest (.cml) must be set to driver, for example:

    program: {
        runner: 'driver',
        ...
    }

2. Register your driver with the driver framework

Next, confirm that your component is listed as a driver in your Fuchsia system.

For a component to be recognized as a driver, you need to explicitly register the component as a driver in your Fuchsia system. If a component does not appear as a driver in the system, it means that the driver framework is not aware of the component, therefore such a component will never be considered for driver binding. So having your new component appear as a driver in your Fuchsia system should be the first milestone when writing a new driver.

To register a component as a driver in your Fuchsia system, do the following:

  1. Upload the Fuchsia package (that contains the driver component) to your Fuchsia package server.

  2. Register the component as a driver in the system:

    ffx driver register <URL>

    Replace URL with the component URL from your Fuchsia package server, for example:

    ffx driver register fuchsia-pkg://fuchsia.com/my_example#meta/my_new_driver.cm
    
  3. View the list of drivers currently registered (but not necessarily running) in the system:

    ffx driver list

    This command prints output similar to the following:

    $ ffx driver list
    fuchsia-boot:///#meta/block.core.cm
    fuchsia-boot:///#meta/bus-pci.cm
    fuchsia-boot:///#meta/fvm.cm
    fuchsia-boot:///#meta/hid-input-report.cm
    fuchsia-boot:///#meta/hid.cm
    fuchsia-boot:///#meta/intel-rtc.cm
    fuchsia-boot:///#meta/netdevice-migration.cm
    fuchsia-boot:///#meta/network-device.cm
    fuchsia-boot:///#meta/pc-ps2.cm
    fuchsia-boot:///#meta/platform-bus-x86.cm
    fuchsia-boot:///#meta/platform-bus.cm
    fuchsia-boot:///#meta/ramdisk.cm
    fuchsia-boot:///#meta/sysmem.cm
    fuchsia-boot:///#meta/virtio_block.cm
    fuchsia-boot:///#meta/virtio_ethernet.cm
    fuchsia-boot:///#meta/zxcrypt.cm
    fuchsia-pkg://fuchsia.com/virtual_audio#meta/virtual_audio_driver.cm
    fuchsia-pkg://fuchsia.com/my_example#meta/my_new_driver.cm
    

    Verify that your new driver component appears in this list.

3. Verify that the bind rules are correct

At last, start examining the bind rules of your driver.

A driver’s bind rules determine which nodes it can bind to in a Fuchsia system. The driver framework loads drivers only when they match the node properties of specific nodes in the system. If your driver is registered in the system, but is not loaded (therefore is not running), then check the driver’s bind rules and verify that they are correctly written to match the bind properties of the target node in your Fuchsia system.

To view all nodes and their node properties in your Fuchsia system, run the following command:

ffx driver list-devices -v

This command prints output similar to the following:

$ ffx driver list-devices -v
...
Name : I2C2
Moniker : root.sys.platform.pt.acpi.I2C2
Driver : None
6 Properties
[ 1/ 6] : Key fuchsia.BIND_ACPI_ID Value 0x000034
[ 2/ 6] : Key fuchsia.BIND_PCI_TOPO Value 0x0000aa
[ 3/ 6] : Key fuchsia.BIND_ACPI_BUS_TYPE Value 0x000001
[ 4/ 6] : Key "fuchsia.hardware.acpi.Device" Value true
[ 5/ 6] : Key fuchsia.BIND_PROTOCOL Value 0x00001e
[ 6/ 6] : Key "fuchsia.platform.DRIVER_FRAMEWORK_VERSION" Value 0x000002
...
Name : 00_15_2
Moniker : root.sys.platform.pt.pci.00_15_2
Driver : None
9 Properties
[ 1/ 9] : Key fuchsia.BIND_PROTOCOL Value 0x00001f
[ 2/ 9] : Key fuchsia.BIND_PCI_VID Value 0x008086
[ 3/ 9] : Key fuchsia.BIND_PCI_DID Value 0x009d62
[ 4/ 9] : Key fuchsia.BIND_PCI_CLASS Value 0x000011
[ 5/ 9] : Key fuchsia.BIND_PCI_SUBCLASS Value 0x000080
[ 6/ 9] : Key fuchsia.BIND_PCI_INTERFACE Value 0x000000
[ 7/ 9] : Key fuchsia.BIND_PCI_REVISION Value 0x000021
[ 8/ 9] : Key fuchsia.BIND_PCI_TOPO Value 0x0000aa
[ 9/ 9] : Key "fuchsia.platform.DRIVER_FRAMEWORK_VERSION" Value 0x000002
...

When debugging bind rules, a recommended practice is to visually examine the output from this command to ensure that your Fuchsia system contains nodes with the right node properties. Also, keep in mind that a node is allowed to have only one driver bound to it. So you want to make sure that the target node in your Fuchsia system does not have a driver already bound to it.

The example output above shows a PCI node and an ACPI node, which the intel-i2c driver can bind to. You may write the driver’s bind rules against these two nodes in the following way:

primary node "pci" {
    fuchsia.driver.framework.dfv2 == true;

    fuchsia.BIND_PROTOCOL == fuchsia.pci.BIND_PROTOCOL.DEVICE;
    fuchsia.BIND_PCI_VID == fuchsia.pci.BIND_PCI_VID.INTEL;
    accept fuchsia.BIND_PCI_DID {
        // For now we only add the DID for the touchpad.
        fuchsia.intel.platform.pci.BIND_PCI_DID.SUNRISE_POINT_SERIALIO_I2C2,
    }
}

node "acpi" {
    fuchsia.driver.framework.dfv2 == true;

    fuchsia.BIND_ACPI_ID == 0x000034;
    fuchsia.BIND_PCI_TOPO == 0x0000aa;
    fuchsia.BIND_ACPI_BUS_TYPE == 0x000001;
}

For the ACPI node, visually verify that the ACPI values specified in the bind rules match the values the ACPI node properties (shown in the output of the ffx command above). And for the PCI node, examine the PCI bind library directly to check if the values defined in the library match the values of the PCI node's properties. (For more information on these two approaches, see Write bind rules for a driver.)

Appendices

Error: driver lifecycle not found

After registering a component as a driver in your Fuchsia system, you may see an error message similar to the following in the device logs (ffx log):

Failed to start driver; driver lifecycle not found url=<DRIVER_URL>

If you run into this error, make sure that the FUCHSIA_DRIVER_EXPORT() macro is added at the end of your driver component’s source code, for example:

// Register driver hooks with the framework
FUCHSIA_DRIVER_EXPORT(qemu_edu::QemuEduDriver);

For more information on this macro, see Implement driver hooks in the Driver codelab.