Fuchsia のデバイスドライバ概要
本記事では、Fuchsia のデバイスドライバ(ARM プラットフォーム)について簡単に紹介します。
公式ドキュメント
デバイスドライバの種類
Fuchsia では大半のデバイスドライバはユーザー空間で動作します。
-
Kernel drivers
カーネル空間で動作するごく一部のドライバ -
Non-Kernel drivers
ユーザー空間で動作する大多数のドライバ-
Board Driver
ボード固有のハードウェア情報(デバイスの Memory Mapped I/O アドレス、IRQ など)を記載したドライバ
-
Board Driver
Kernel drivers
カーネル起動に必要な最低限のドライバ(だと思われる)。
-
- coresight
高機能なデバッグとトレースを実現するためのハードウェア
— 進化する Cortex-M シリーズ搭載マイコンのデバッグ - hdcp
デジタルコンテンツの不正コピーを防ぐことを目的とした著作権保護技術
— HDCP とは何ですか? - hw_rng
ハードウェア乱数生成器 - hw_watchdog
ウォッチドッグタイマー - interrupt
Generic Interrupt Controller(GIC)、割り込みコントローラ - iommu
IOMMU (Input/Output Memory Management Unit、IOMMU) とは DMA 可能な I/O バスと主記憶装置を接続するメモリ管理ユニット (MMU) である。
— IOMMU - Wikipedia - pcie
PCI Express(ピーシーアイエクスプレス)は、2002 年に PCI-SIG(英語版)によって策定された、I/O シリアルインタフェース、拡張バスの一種である。
— PCI Express - Wikipedia - pdev
おそらく、Platform DEVice。hw_watchdog、interrupt、psci、uart のスタブ(だと思われる) - psci
Power State Coordination Interface - timer
タイマ - uart
デバッグ用 UART。Zircon カーネルが起動すると、シリアル通信でシェルが使用できる - udisplay
不明。フレームバッファ関係
- coresight
-
zircon/system/ulib/uart/include/lib/uart/
physboot
ブートローダ(U-Boot から起動されるブートローダ。圧縮 Zircon カーネルを展開する)が使用する UART ドライバ。
Zircon カーネルも(zircon/kernel/dev/uart/
を使わないで)こちらを使用している模様
Kernel drivers に関係するボード固有の情報
Zircon カーネル内には、ボード固有の情報(Memory Mapped I/O アドレス、IRQ など)は一切含まれません。
これらの情報は、ブートローダ(U-Boot)内に記述し、起動時に SDRAM を通じて Zircon カーネルに渡します。
詳細はこちらの記事を参照ください。
U-Boot でボード固有の情報を記述
static zbi_mem_range_t mem_config[] = {
{
.type = ZBI_MEM_RANGE_RAM,
.length = 0x80000000,
},
{
.type = ZBI_MEM_RANGE_PERIPHERAL,
.paddr = 0xfe000000,
.length = 0x02000000,
},
{
.type = ZBI_MEM_RANGE_RESERVED,
.paddr = 0x07400000,
.length = 0x00100000,
},
{
.type = ZBI_MEM_RANGE_RESERVED,
.paddr = 0x05000000,
.length = 0x02300000,
},
};
...
zircon_append_boot_item(zbi, ZBI_TYPE_MEM_CONFIG, 0, &mem_config, sizeof(mem_config));
...
zircon_append_boot_item()
で、SDRAM 上の ZBI(Zircon Boot Image)に情報を追加します。
static const dcfg_simple_t uart_driver = {
.mmio_phys = 0xff803000,
.irq = 225,
};
...
zircon_append_boot_item(zbi, ZBI_TYPE_KERNEL_DRIVER, KDRV_AMLOGIC_UART, &uart_driver,
sizeof(uart_driver));
...
デバッグ用 UART のメモリマップドアドレスと IRQ を追加します。
static const dcfg_arm_gicv2_driver_t gicv2_driver = {
.mmio_phys = 0xffc00000,
.gicd_offset = 0x1000,
.gicc_offset = 0x2000,
.gich_offset = 0x4000,
.gicv_offset = 0x6000,
.ipi_base = 5,
};
...
zircon_append_boot_item(zbi, ZBI_TYPE_KERNEL_DRIVER, KDRV_ARM_GIC_V2, &gicv2_driver,
sizeof(gicv2_driver));
...
割り込みコントローラ情報を追加します。
static const dcfg_arm_generic_timer_driver_t timer_driver = {
.irq_phys = 30,
};
...
zircon_append_boot_item(zbi, ZBI_TYPE_KERNEL_DRIVER, KDRV_ARM_GENERIC_TIMER, &timer_driver,
sizeof(timer_driver));
...
タイマの IRQ を追加します。
static const zbi_platform_id_t platform_id = {
.vid = PDEV_VID_KHADAS,
.pid = PDEV_PID_VIM3,
.board_name = "vim3",
};
...
zircon_append_boot_item(zbi, ZBI_TYPE_PLATFORM_ID, 0, &platform_id, sizeof(platform_id));
...
プラットフォーム ID(ボードの識別子)を追加します。
この情報をもとに、ユーザー空間でどのデバイスドライバを使うかが決定されます。
Non-Kernel drivers
Non-Kernel(ユーザー空間の)デバイスドライバは共有ライブラリ(Dynamic Shared Library)となります。
ドライバホストプロセスがデバイスドライバ共有ライブラリをロードして使用します。
ドライバの種別ごとにプロセスが生成されます。
例)I2C ドライバホストプロセスが、VIM3 ボード用の I2C デバイスドライバをロードして使用する。
Non-Kernel drivers に関係するボード固有設定
次節で後述します。
デバイスの検出とドライバのロード
どうやってデバイスを検出するか?
x86_64
PCI コントローラがバスに接続されたデバイスを検出できます。
ARM
デバイス情報(ベンダ ID VID、プロダクト ID PID、Memory Mapped I/O アドレス、IRQ など)をコード中に記述します。
Linux では Device Tree に記述する内容です。
static pbus_dev_t gpio_dev = []() {
pbus_dev_t dev = {};
dev.name = "gpio";
dev.vid = PDEV_VID_AMLOGIC;
dev.pid = PDEV_PID_AMLOGIC_A311D;
dev.did = PDEV_DID_AMLOGIC_GPIO;
dev.mmio_list = gpio_mmios;
dev.mmio_count = std::size(gpio_mmios);
dev.irq_list = gpio_irqs;
dev.irq_count = std::size(gpio_irqs);
dev.metadata_list = gpio_metadata;
dev.metadata_count = std::size(gpio_metadata);
return dev;
}();
...
zx_status_t Vim3::GpioInit() {
zx_status_t status = pbus_.ProtocolDeviceAdd(ZX_PROTOCOL_GPIO_IMPL, &gpio_dev);
...
これは、VIM3 ボードに GPIO デバイスがあることを登録する処理です。
デバイス固有の情報(VID、PID、デバイス ID DID、mmio アドレス、IRQ など)を登録します。
この VIM3 ボードドライバ /driver/vim3.so
は、ブートファイルシステム(Linux の initrd 相当)に含まれます。
どうやってデバイスに対応するドライバを見つけるか?
デバイスは VID、PID、DID を持ちます(x86_64 ではデバイスから取得、ARM ではコード中に記載)。
デバイスドライバのディレクトリに、対応する(バインドする)デバイスの VID、PID、DID を記載するファイル(.bind)を用意します。
using fuchsia.amlogic.platform;
using fuchsia.platform;
fuchsia.BIND_PROTOCOL == fuchsia.platform.BIND_PROTOCOL.DEVICE;
fuchsia.BIND_PLATFORM_DEV_VID == fuchsia.amlogic.platform.BIND_PLATFORM_DEV_VID.AMLOGIC;
accept fuchsia.BIND_PLATFORM_DEV_PID {
fuchsia.amlogic.platform.BIND_PLATFORM_DEV_PID.A113,
fuchsia.amlogic.platform.BIND_PLATFORM_DEV_PID.S905D2,
fuchsia.amlogic.platform.BIND_PLATFORM_DEV_PID.T931,
fuchsia.amlogic.platform.BIND_PLATFORM_DEV_PID.A311D,
}
fuchsia.BIND_PLATFORM_DEV_DID == fuchsia.amlogic.platform.BIND_PLATFORM_DEV_DID.GPIO;
これは VIM3 ボード(Amlogic 社の SoC 搭載)の、GPIO デバイスドライバのバインドファイルです。
このバインドファイルをもとに、デバイスを検出した場合に、対応するデバイスドライバの初期化コード(zx_driver_ops_t.bind
)を実行するコードが自動で生成されます。
これはドライバマネージャープロセスが実行します。
ボードのデバイス検出、ドライババインドの流れ
- Zircon カーネルは、U-Boot からプラットフォーム ID(VID、PID)を取得します。
- ドライバマネージャーは、このプラットフォーム ID にバインドする(バインドファイル情報に一致する)ボードドライバをロードします。
- ボードドライバには、ボードに搭載されたデバイスの情報が記載されており、それをシステムに登録します。
- ドライバマネージャーは登録されたデバイスにバインドするデバイスドライバをロードします。
VIM3 の GPIO デバイス例で説明します。
- U-Boot から、プラットフォーム ID(PDEV_VID_KHADAS 0x04、PDEV_PID_VIM3 0x03)を取得します。
- VIM3 ボードドライバ(vim3.so)のバインドファイル(src/devices/board/drivers/vim3/vim3.bind)
fuchsia.khadas.platform.BIND_PLATFORM_DEV_VID.KHADAS 0x04
、fuchsia.khadas.platform.BIND_PLATFORM_DEV_PID.VIM3 0x03
がプラットフォーム ID に一致するのでロードされます。 - VIM3 ボードドライバが GPIO デバイス(PDEV_VID_AMLOGIC 0x05、PDEV_PID_AMLOGIC_A311D 0x06、PDEV_DID_AMLOGIC_GPIO 0x01)を登録します。
- Amlogic GPIO デバイスドライバ(aml-axg-gpio.so)のバインドファイル(src/devices/gpio/drivers/aml-axg-gpio/aml-axg-gpio.bind)
fuchsia.amlogic.platform.BIND_PLATFORM_DEV_VID.AMLOGIC 0x05
、fuchsia.amlogic.platform.BIND_PLATFORM_DEV_PID.A311D 0x06
、fuchsia.amlogic.platform.BIND_PLATFORM_DEV_DID.GPIO 0x01
が GPIO デバイス情報に一致するのでロードされます。
デバイス情報の VID、PID 等はzircon/system/ulib/ddk-platform-defs/include/lib/ddk/platform-defs.h で定義されます。
バインドファイルの VID、PID 等はsrc/devices/bind/fuchsia.amlogic.platform/fuchsia.amlogic.platform.bindで定義されます。
ボードを移植する際は、これらのファイルに定義を追加する必要があります。
考察
ボードに依存する情報(U-Boot 内、ボードドライバ内)とデバイスドライバ(ユーザー空間共有ライブラリ)の両方が、カーネルから切り離されているのがわかります。
これにより、スマートフォンなどのメーカーを通さずに、Google が自らカーネルをアップデートして公開できると思います。
備考
Non-Kernel Drivers の種類
- platform bus driver (src/devices/bus/drivers/)
- ドライバを管理するフレームワーク
- デバイスドライバツリーのルート?
- x86_64 の場合、PCI
- arm64 の場合、Platform
- board driver
- ボード固有のハードウェア情報(デバイスの Memory Mapped I/O アドレス、IRQ など)を記載したドライバ
- VIM3 のボードドライバ src/devices/board/drivers/vim3/
- platform device drivers
- いわゆるデバイスドライバ
- platform bus driver とは別プロセスで動作する
- プロトコル(Ethernet など)を実装したドライバと同じプロセスで動作する
- 性能とセキュリティのトレードオフ
- セキュリティを追求したら、デバイスドライバごとに別プロセスがよい
- 性能を追求したら、すべて同じプロセスがよい
- VIM3 の USB PHY デバイスドライバ src/devices/usb/drivers/aml-usb-phy-v2/
- VIM3 の Ethernet デバイスドライバ src/connectivity/ethernet/drivers/aml-ethernet-s912/
- composite
- protocol implementation drivers
- platform bus driver と同一プロセスで動作するデバイスドライバ
- VIM3 の GPIO デバイスドライバ src/devices/gpio/drivers/aml-axg-gpio/
- VIM3 の Clock デバイスドライバ src/devices/clock/drivers/amlogic-clk/
- 上記の platform device drivers はそれ単体ではユーザーに機能を提供できない(例:Ethernet デバイスドライバ)。さらに上位のドライバが必要(例:Ethernet プロトコルドライバなど)
- GPIO や、Clock はそれ単体で機能を提供できる
- 自身でプロトコルまで実装している、という意味で
protocol implementation
なのか?
- 自身でプロトコルまで実装している、という意味で
- 別の文脈で
protocol implementation
が出てくるので混同しないように注意- VIM3 の USB PHY デバイスドライバは UsbPhyProtocol クラス(インターフェース相当)を継承して実装します
- このコメントで
USB PHY protocol implementation
とあります。これとprotocol implementation drivers
は(無関係ではないが)同一の意味ではないと思います
- platform proxy driver
- ドライバホストプロセス間で通信するためのドライバ
- その他のドライバ
- プラットフォームデバイス(ボード上のデバイス)に依存しないドライバ
- プロトコルなど
デバイスの種類
デバイス種類 | デバイスドライバ種類 | 備考 |
---|---|---|
Isolated | platform device driver | a |
Protocol | protocol implementation driver | a |
Fragment | platform device driver | a |
Isolated
ddk::DeviceAdd()
Protocol
ddk::ProtocolDeviceAdd()
Fragment
ddk::CompositeDeviceAdd()
?
Discussion