1 2 3<!-- 4 (C) Copyright 2018 The Fuchsia Authors. All rights reserved. 5 Use of this source code is governed by a BSD-style license that can be 6 found in the LICENSE file. 7--> 8 9# Getting Started 10 11This document is part of the [Driver Development Kit tutorial](ddk-tutorial.md) documentation. 12 13Writing a device driver is often viewed as a daunting task, fraught with complexities 14and requiring arcane knowledge of little-known kernel secrets. 15 16The goal of this section is to demystify the process; you'll learn everything you 17need to know about how to write device drivers, starting with what they do, how 18they work, and how they fit into the overall system. 19 20## Overview 21 22At the highest level, a device driver's job is to provide a uniform interface to 23a particular device, while hiding details specific to the device's implementation. 24 25Two different ethernet drivers, for example, both allow a client to send packets 26out an interface, using the exact same C language function. 27Each driver is responsible for managing its own hardware in a way that makes the 28client interfaces identical, even though the hardware is different. 29 30Note that the interfaces that are provided by the driver may be "intermediate" — 31that is, they might not necessarily represent the "final" device in the chain. 32 33Consider a PCI-based ethernet device. 34First, a base PCI driver is required that understands how to talk to the PCI bus itself. 35This driver doesn't know anything about ethernet, but it does know 36how to deal with the specific PCI chipset present on the machine. 37 38It enumerates the devices on that bus, collects information 39from the various registers on each device, and provides functions that allow 40its clients (such as the PCI-based ethernet driver) to perform PCI operations 41like allocating an interrupt or a DMA channel. 42 43Thus, this base PCI driver provides services to the ethernet driver, allowing 44the ethernet driver to manage its associated hardware. 45 46At the same time, other devices (such as a video card) could also use the base PCI 47driver in a similar manner to manage their hardware. 48 49## The Zircon model 50 51In order to provide maximum flexibility, drivers in the Zircon world are allowed 52to bind to matching "parent" devices, and publish "children" of their own. 53This hierarchy extends as required: one driver might publish a child, only to have 54another driver consider that child their parent, with the second driver publishing 55its own children, and so on. 56 57In order to understand how this works, let's follow the PCI-based ethernet example. 58 59The system starts by providing a special "PCI root" parent. 60Effectively, it's saying "I know that there's a PCI bus on this system, when you 61find it, bind it *here*." 62 63> The "Advanced Topics" section below has more details about this process. 64 65Drivers are evaluated by the system (a directory is searched), and drivers that 66match are automatically bound. 67 68In this case, a driver that binds to a "PCI root" parent is found, and bound. 69 70This is the base PCI driver. 71It's job is to configure the PCI bus, and enumerate the peripherals on the bus. 72 73The PCI bus has specific conventions for how peripherals are identified: 74a combination of a Vendor ID (**VID**) and Device ID (**DID**) uniquely identifies 75all possible PCI devices. 76During enumeration, these values are read from the peripheral, and new parent 77nodes are published containing the detected VID and DID (and a host of other 78information). 79 80Every time a new device is published, the same process as described above (for 81the initial PCI root device publication) repeats; 82that is, drivers are evaluated by the system, searching for drivers that match 83up with the new parents' characteristics. 84 85Whereas with the PCI root device we were searching for a driver that matched 86a certain kind of functionality (called a "protocol," we'll see this shortly), in 87this case, however, we're searching for drivers that match a different 88protocol, namely one that satisfies the requirements of "is a PCI device and 89has a given VID and DID." 90 91If a suitable driver is found (one that matches the required protocol, VID and 92DID), it's bound to the parent. 93 94As part of binding, we initialize the driver — this involves such operations 95as setting up the card for operation, bringing up the interface(s), and 96publishing a child or children of this device. 97In the case of the PCI ethernet driver, it publishes the "ethernet" interface, 98which conforms to yet another protocol, called the "ethernet implementation" protocol. 99This protocol represents a common protocol that's close to the functions that 100clients use (but is one step removed; we'll come back to this). 101 102### Protocols 103 104We mentioned three protocols above: 105 106* the PCI root protocol (`ZX_PROTOCOL_PCIROOT`), 107* the PCI device protocol (`ZX_PROTOCOL_PCI`), and 108* the ethernet implementation protocol (`ZX_PROTOCOL_ETHERNET_IMPL`). 109 110The names in brackets are the C language constants corresponding to the protocols, for reference. 111 112So what is a protocol? 113 114A protocol is a strict interface definition. 115 116The ethernet driver published an interface that conforms to `ZX_PROTOCOL_ETHERNET_IMPL`. 117This means that it must provide a set of functions defined in a data structure 118(in this case, `ethmac_protocol_ops_t`). 119 120These functions are common to all devices implementing the protocol — for example, 121all ethernet devices must provide a function that queries the MAC address of the 122interface. 123 124Other protocols will of course have different requirements for the functions they 125must provide. 126For example a block device will publish an interface that conforms to the 127"block implementation protocol" (`ZX_PROTOCOL_BLOCK_IMPL`) and 128provide functions defined by `block_protocol_ops_t`. 129This protocol includes a function that returns the size of the device in blocks, 130for example. 131 132We'll examine these protocols in the following chapters. 133 134# Advanced Topics 135 136The above has presented a big picture view of Zircon drivers, with a focus on protocols. 137 138In this section, we'll examine some advanced topics, such as platform dependent 139and platform independent code decoupling, 140the "miscellaneous" protocol, and how protocols and processes are mapped. 141 142## Platform dependent vs platform independent 143 144Above, we mentioned that `ZX_PROTOCOL_ETHERNET_IMPL` was "close to" the functions used 145by the client, but one step removed. 146That's because there's one more protocol, `ZX_PROTOCOL_ETHERNET`, that sits between 147the client and the driver. 148This additional protocol is in place to handle functionality common to all ethernet 149drivers (in order to avoid code duplication). 150Such functionality includes buffer management, status reporting, and administrative 151functions. 152 153This is effectively a "platform dependent" vs "platform independent" decoupling; 154common code exists in the platform independent part (once), and driver-specific code 155is implemented in the platform dependent part. 156 157This architecture is repeated in multiple places. 158With block devices, for example, the hardware driver binds to the bus (e.g., PCI) 159and provides a `ZX_PROTOCOL_BLOCK_IMPL` protocol. 160The platform independent driver binds to `ZX_PROTOCOL_BLOCK_IMPL`, and publishes the 161client-facing protocol, `ZX_PROTOCOL_BLOCK`. 162 163You'll also see this with the display controllers, I<sup>2</sup>C bus, and serial drivers. 164 165## Miscellaneous protocol 166 167In [simple drivers](simple.md), we show the code for several drivers that illustrate 168basic functionality, but don't provide services related to a specific protocol 169(i.e., they are not "ethernet" or "block" devices). 170These drivers are bound to `ZX_PROTOCOL_MISC_PARENT`. 171 172> @@@ More content? 173 174## Process / protocol mapping 175 176In order to keep the discussions above simple, we didn't talk about process separation 177as it relates to the drivers. 178To understand the issues, let's see how other operating systems deal with them, 179and compare that to the Zircon approach. 180 181In a monolithic kernel, such as Linux, many drivers are implemented within the kernel. 182This means that they share the same address space, and effectively live in the same 183"process." 184 185The major problem with this approach is fault isolation / exploitation. 186A bad driver can take out the entire kernel, because it lives in the same address 187space and thus has privileged access to all kernel memory and resources. 188A compromised driver can present a security threat for the same reason. 189 190The other extreme, that is, putting each and every driver service into its own 191process, is used by some microkernel operating systems. 192Its major drawback is that if one driver relies on the services of another driver, 193the kernel must effect at least a context switch operation (if not a data transfer 194as well) between the two driver processes. 195While microkernel operating systems are usually designed to be fast at these 196kinds of operations, performing them at high frequency is undesirable. 197 198The approach taken by Zircon is based on the concept of a device host (**devhost**). 199A devhost is a process that contains a protocol stack — that is, one or 200more protocols that work together. 201The devhost loads drivers from ELF shared libraries (called Dynamic Shared Objects, 202or **DSO**s). 203In the [simple drivers](simple.md) section, we'll see the meta information 204that's contained in the DSO to facilitate the discovery process. 205 206The protocol stack effectively allows the creation of a complete "driver" for 207a device, consisting of platform dependent and platform independent components, 208in a self-contained process container. 209 210For the advanced reader, take a look at the `dm dump` command available from 211the Zircon command line. 212It displays a tree of devices, and shows you the process ID, DSO name, and 213other useful information. 214 215Here's a highly-edited version showing just the PCI ethernet driver parts: 216 217``` 2181. [root] 2192. [sys] 2203. <sys> pid=1416 /boot/driver/bus-acpi.so 2214. [acpi] pid=1416 /boot/driver/bus-acpi.so 2225. [pci] pid=1416 /boot/driver/bus-acpi.so 223 ... 2246. [00:02:00] pid=1416 /boot/driver/bus-pci.so 2257. <00:02:00> pid=2052 /boot/driver/bus-pci.proxy.so 2268. [intel-ethernet] pid=2052 /boot/driver/intel-ethernet.so 2279. [ethernet] pid=2052 /boot/driver/ethernet.so 228``` 229 230From the above, you can see that process ID `1416` (lines 3 through 6) 231is the Advanced Configuration and Power Interface (**ACPI**) driver, implemented 232by the DSO `bus-acpi.so`. 233 234During primary enumeration, the ACPI DSO detected a PCI bus. 235This caused the publication of a parent with `ZX_PROTOCOL_PCI_ROOT` (line 5, 236causing the appearance of the `[pci]` entry), 237which then caused the devhost to load the `bus-pci.so` DSO and bind to it. 238That DSO is the "base PCI driver" to which we've been referring throughout the 239discussions above. 240 241During its binding, the base PCI driver enumerated the PCI bus, and found an ethernet 242card (line 6 detects bus 0, device 2, function 0, shown as `[00:02:00]`). 243(Of course, many other devices were found as well, but we've removed them from 244the above listing for simplicity). 245 246The detection of this device then caused the base PCI driver to publish a new parent 247with `ZX_PROTOCOL_PCI` and the device's VID and DID. 248Additionally, a new devhost (process ID `2052`) was created and loaded with the 249`bus-pci.proxy.so` DSO (line 7). 250This proxy serves as the interface from the new devhost (pid `2052`) to the base PCI 251driver (pid `1416`). 252 253> This is where the decision was made to "sever" the device driver into its own 254> process — the new devhost and the base PCI driver now live in two 255> different processes. 256 257The new devhost `2052` then finds a matching child (the `intel-ethernet.so` 258DSO on line 8; it's considered a match because it has `ZX_PROTOCOL_PCI` and the correct 259VID and DID). 260That DSO publishes a `ZX_PROTOCOL_ETHERNET_IMPL`, which binds to a matching 261child (the `ethernet.so` DSO on line 9; it's considered a match because it has a 262`ZX_PROTOCOL_ETHERNET_IMPL` protocol). 263 264What's not shown by this chain is that the final DSO (`ethernet.so`) publishes 265a `ZX_PROTOCOL_ETHERNET` — that's the piece that clients can use, so of 266course there's no further "device" binding involved. 267 268 269