Python
Linux
PCI

PCI ボードをデバイスドライバを用いずに python から操作する

これは何か

PCI ボードをデバイスドライバに頼らず python を使って操作してみます。

環境、必要なもの

背景知識

手順

1. 機器の情報を収集する

lspci を使って、設定ヘッダの情報を確認

$ lspci -v -nn
...
...
05:0c.0 Unassigned class [ff00]: Interface Corp Device [1147:0c5d] (rev 03)
    Subsystem: Interface Corp Device [1147:0001]
    Flags: slow devsel, IRQ 19
    I/O ports at 2000 [size=32]
    I/O ports at 2048 [size=4]

05:0d.0 Unassigned class [ff00]: Interface Corp Device [1147:0c8e] (rev 01)
    Subsystem: Interface Corp Device [1147:2090]
    Flags: slow devsel, IRQ 16
    Memory at 90502000 (32-bit, non-prefetchable) [size=64]
    Memory at 90501000 (32-bit, non-prefetchable) [size=64]
    Memory at 90503000 (32-bit, non-prefetchable) [size=32]
...
...
  • オプション

    • -v : 通信方式、アドレス、を含む詳細を表示
    • -nn : ベンダ ID、装置 ID と共に、名称も表示
  • 見方

    • 05:0c.0 に接続された Interface Corp (ID 0x1147) の装置 ID 0x0c5d です
      • 2 つの PCI I/O port を持っています
        • アドレス 0x2000 から 32 byte
        • アドレス 0x2048 から 4 byte
    • 05:0d.0 に接続された Interface Corp (ID 0x1147) の装置 ID 0x0c8e です
      • 3 つの PCI Memory port を持っています
        • アドレス 0x90502000 から 64 byte
        • アドレス 0x90501000 から 64 byte
        • アドレス 0x90503000 から 32 byte

2. PCI I/O port へ接続する

$ sudo ipython

PCI I/O port へのアクセスのために、ルート権限が必要です。

>>> import portio

# level を 3 に設定し、I/O ポートへのアクセスを許可します
>>> portio.iopl(3)   

# 0x2000 から 1 byte を読み出します
>>> portio.inb(0x2000)
254
  • 注意点:
    • inb や、outb に渡す port を間違えると、誤った機器に命令を送りつけてしまい、危険です。

3. PCI メモリ port へ接続する

$ sudo ipython

/dev/mem へのアクセスのために、ルート権限が必要です。

>>> import os
>>> import mmap

# メモリを読込専用で開きます
>>> f = os.open('/dev/mem', os.O_RDONLY)

# mmap でマッピングします
>>> m = mmap.mmap(f, 32, prot=mmap.PROT_READ, offset=0x90503000)

# 7 byte 目を読んでみます
>>> m.seek(0x07)
>>> m.read(1)
b'\x00'
  • 注意点:
    • mmap の offset に渡すアドレス値を間違えると、他のプロセスなどが使用中のメモリ領域へアクセスしてしまい、危険です。

pypci モジュール

  • 上記の手順だと、煩雑だし、アドレスを間違える危険性があるので、そこらへんをうまいことしてくれるモジュールを作りました。
  • PCI I/O port, PCI メモリ port の違いによらず、統一した API で操作できます。
  • pypci モジュール
    • インストール : pip install pypci

使い方

>>> import pypci

# PCI 設定情報を収集します。
# ベンダー ID、装置 ID を使って絞り込みます。
>>> board = pypci.lspci(vendor=0x1147, device=3214)

# port に関する情報は、Base Address Register に書き込まれています。
>>> board[0].bar
[BaseAddressRegister(type='mem', addr=2421170176, size=64),
 BaseAddressRegister(type='mem', addr=2421166080, size=64),
 BaseAddressRegister(type='mem', addr=2421174272, size=32)]

# BaseAddressRegister オブジェクトを使って通信します
>>> bar2 = board[0].bar[2]


# 読み込みの例
>>> pypci.read(bar2, 0x0c, 4)
b'\x00\x00\x00\x0c'

# アドレス範囲を超えると、エラーです
>>> pypci.read(bar2, 0x1d, 4)
BadAccessError: PCI addr space is 0x90503000-0x9050301f while tried to access 0x9050301d-0x90503020.


# 書き込みの例
>>> pypci.write(bar[2], 0x04, b'\x01')

>>> data = struct.pack('<I', 1234567)
>>> pypci.write(bar[2], 0x00, data)