Google is committed to advancing racial equity for Black communities. See how.

Zircon's Platform Bus

Introduction

The term platform bus refers to a specific Zircon driver with source code located at //fuchsia/src/devices/bus/drivers/platform/. However this term also refers to the framework that manages the lower level drivers in Zircon. In this document, platform bus driver refers to a specific driver and platform bus refers to the general framework.

The platform bus as a whole contains several types of drivers:

  • The platform bus driver, which manages the platform bus. This is a generic driver with no hardware specific functionality. The platform bus driver is started automatically by the devmgr when the system boots.

  • The board driver, which is the first driver loaded by the platform bus driver. The board driver contains all the platform specific information needed by the platform bus and controls what other drivers will be loaded by the platform bus. On arm64 platforms, the platform bus driver uses information from the bootloader or boot shim to bind the correct board driver for the platform it is running on. On x86 platforms, the platform bus driver always loads the x86 board driver and creates platform devices based on information from ACPI.

  • The platform device drivers are the foundations for the higher level drivers in Zircon. These drivers provide the lowest level of support for a particular feature, like USB, eMMC or NAND storage, etc., with higher level drivers loading on top of that.

  • The protocol implementation drivers are drivers that provide protocols that are needed by the board driver. One common example of this is the GPIO driver, which is often needed by the board driver for pin-muxing. In the past, the platform bus used to also proxy these drivers' protocols to platform devices, but now we use composite devices instead. Over time, we will likely phase out the use of protocol implementation drivers in the platform bus and replace it with a new approach that does not require blocking to wait for drivers to load.

  • Finally, the platform proxy driver a companion to the platform bus driver that loads into the platform device devhosts. This driver supports proxying the platform device protocol and other resource protocols from the platform device driver to the platform bus driver and protocol implementation drivers. This is needed because the platform device drivers run in a different devhost process than the platform bus driver and the protocol implementation drivers.

Zircon Platform Bus diagram Source: https://goto.google.com/zircon-platform-bus-diagram

Platform Bus Initialization

The platform bus driver is started automatically by the devmgr at boot. Since the platform bus driver is a generic driver that contains no information about the platform it is running on, it first loads the board driver, which handles platform specific logic. To determine which board driver to load, platform bus driver reads the ZBI_TYPE_PLATFORM_ID record from the ZBI data passed from the bootloader. It then adds a device with protocol ZX_PROTOCOL_PBUS with the BIND_PLATFORM_DEV_VID and BIND_PLATFORM_DEV_PID binding variables set to the vid and did from the platform data record. The correct board driver will bind to this device and continue the platform bus initialization process. On x86 platforms, the x86 board driver is loaded automatically.

The board driver uses the platform bus protocol to communicate with the platform bus driver. After it does its own initialization, the board driver then uses the ProtocolDeviceAdd() call in the platform bus protocol to load protocol implementation drivers. After the protocol implementation driver loads, it must register its protocol with the platform bus driver by calling the platform bus RegisterProtocol() API. ProtocolDeviceAdd() will block until the driver calls RegisterProtocol(), so the board driver must call RegisterProtocol() from one of its own threads rather than a devmgr callback like Bind().

After the protocol devices are added, the board driver will call the DeviceAdd() call in the platform bus protocol to create platform devices, which will result in platform device drivers loading each in its own devhost. After the platform devices are created, the platform bus initialization is complete.

Composite Platform Devices

The platform bus also supports adding platform devices to be used as components in composite devices. The platform bus CompositeDeviceAdd() call adds a composite device, with the zeroth component being a platform device described by the provided PBusDev struct. The binding rules for the remaining components are provided by the components parameter. The coresident_device_index is used to specify which devhost the composite device should be created in. A value of UINT32_MAX will result in a new devhost being created for the composite device, while a value of 1 through n will add the composite device to the devhost of one of the other components. Passing 0 is not allowed, since we do not want the composite device to be added to the platform bus driver's devhost.

The internals of composite platform devices are a bit different than the non-composite case. Instead of using the platform proxy driver, the devmgr component and component proxy drivers proxy the platform device protocol instead. For example, in the diagram above we have a composite device for an audio driver with a platform device as its first component and an I2C channel as its second. The audio driver is started in a new devhost, and the devmgr component and component proxy drivers are responsible for proxying the PDEV and I2C protocols to the audio driver.

Platform Device Protocol

The platform device protocol (ZX_PROTOCOL_PDEV) is the main protocol provided by the platform bus to platform device drivers. This protocol provides access to resources like MMIO ranges, interrupts, BTIs, and SMC ranges to the platform device driver. Rather than requesting MMIOs and interrupts by physical addresses or IRQ numbers, these resource are requested by a zero-based index. This allows us to have platform device drivers for particular IP that works across multiple platforms, since the knowledge of the exact MMIO addresses and interrupt numbers do not need to be known by the driver. Instead, the board driver configures the MMIO addresses and IRQ numbers in the PbusDev struct passed via AddDevice().

The platform device protocol is also available to protocol implementation drivers. For example, a GPIO driver may use the platform device protocol to access its MMIO and interrupts. This allows protocol implementation drivers to be shared among different SOC variants, where the functionality may be identical but the MMIO addresses and interrupt numbers may be different.

Platform Bus Protocol

The platform bus protocol (ZX_PROTOCOL_PBUS) is used by board drivers and protocol implementation drivers to communicate with the platform bus driver. It is only available to drivers running in the platform bus's devhost (in particular, it is not accessible to platform device drivers). The purpose of this protocol is for the board driver to load protocol implementation drivers and to start platform device drivers. It is also used by protocol implementation drivers to register their protocols with the platform bus so their protocols can be made available to platform device drivers.