これから実装した機能毎に分けてプログラムの説明をします。
なお、PCIデバイスのベンダID/デバイスIDは適当なもの (0x1234/ 0x1) を使用しています。
また、QEMU仮想デバイス上でprintf を使うとそのままコンソール上に出力されます。
ここでは、仮想デバイスからのprintfには##, デバイスドライバのprintkには**を
先頭で付けるように適当に関数をラップしています。
QEMU仮想デバイスをQEMUに登録する
QEMU自体はC言語で書かれていますが、構造体を利用したオブジェクト指向の
書き方 (QOM : QEMU Objet Model) によってハードウェア等のオブジェクトが記述されています。
これについては include/qom/object.h の説明が参考になります。
あるオブジェクトには対応するオブジェクトクラスが
ただ1つ存在し(クラスもQOMです)、
クラスの初期化がオブジェクトの初期化より前に起こります。
また、継承関係に応じて親オブジェクト/クラスの初期化が
子オブジェクト/クラスの初期化の前に行われます。
クラスには通常各オブジェクトがオーバーライドする(関数ポインタの値を置き換える)
関数ポインタや、そのクラスのデバイスが持つべきデータがあります。
PCIデバイスにはPCIデバイス用の記述の仕方が用意されているので、既存
のコードを参考に(hw/misc/pci-testdev.c など)以下のコードを書きました。
なお、オブジェクトの構造体名やマクロ名、変数名の付け方は他のファイルにならっています。
// test_pci_device.c 抜粋
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "<path_to>/test_pci.h"
#define TEST_PCI_DEVICE_DEBUG
#ifdef TEST_PCI_DEVICE_DEBUG
#define tprintf(fmt, ...) printf("## (%3d) %-20s: " fmt, __LINE__, __func__, ## __VA_ARGS__)
#else
#define tprintf(fmt, ...)
#endif
typedef struct TestPCIState {
PCIDevice parent_obj;
/* ... */
MemoryRegion mmio;
MemoryRegion portio;
} TestPCIState;
#define TYPE_TEST_PCI "test_pci"
#define TEST_PCI(obj) \
OBJECT_CHECK(TestPCIState, (obj), TYPE_TEST_PCI)
static uint64_t
test_pci_mmio_read(void *opaque, hwaddr addr, unsigned size)
{
TestPCIState *s = opaque;
/* 略 */
}
static uint64_t
test_pci_pio_read(void *opaque, hwaddr addr, unsigned size)
{
TestPCIState *s = opaque;
/* 略 */
}
static void
test_pci_mmio_write(void *opaque, hwaddr addr, uint64_t val,
unsigned size)
{
TestPCIState *s = opaque;
/* 略 */
}
static void
test_pci_pio_write(void *opaque, hwaddr addr, uint64_t val,
unsigned size)
{
TestPCIState *s = opaque;
/* 略 */
}
static const MemoryRegionOps test_pci_mmio_ops = {
.read = test_pci_mmio_read,
.write = test_pci_mmio_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.impl = {
.min_access_size = 4,
.max_access_size = 4,
},
};
static const MemoryRegionOps test_pci_pio_ops = {
.read = test_pci_pio_read,
.write = test_pci_pio_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.impl = {
.min_access_size = 1,
.max_access_size = 4,
},
};
static int test_pci_init(PCIDevice *pdev)
{
TestPCIState *s = TEST_PCI(pdev);
uint8_t *pci_conf;
int i;
pci_conf = pdev->config;
pci_conf[PCI_INTERRUPT_PIN] = 1; /* if 0 no interrupt pin */
// register memory mapped io / port mapped io
memory_region_init_io(&s->mmio, OBJECT(s), &test_pci_mmio_ops, s,
"test_pci_mmio", TEST_PCI_MEMSIZE * 2);
memory_region_init_io(&s->portio, OBJECT(s), &test_pci_pio_ops, s,
"test_pci_portio", TEST_PCI_IOSIZE * 2);
// set base address register
pci_register_bar(pdev, BAR_MMIO, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio);
pci_register_bar(pdev, BAR_PIO , PCI_BASE_ADDRESS_SPACE_IO , &s->portio);
tprintf("loaded\n");
return 0;
}
static void
test_pci_uninit(PCIDevice *pdev)
{
TestPCIState *s = TEST_PCI(pdev);
memory_region_destroy(&s->mmio);
memory_region_destroy(&s->portio);
tprintf("unloaded\n");
}
static void
test_pci_reset(TestPCIState *s)
{
tprintf("done reset\n");
}
static void qdev_test_pci_reset(DeviceState *ds)
{
TestPCIState *s = TEST_PCI(ds);
test_pci_reset(s);
}
static void test_pci_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
k->init = test_pci_init;
k->exit = test_pci_uninit;
k->vendor_id = PCI_VENDOR_ID_TEST;
k->device_id = PCI_DEVICE_ID_TEST;
k->revision = 0x00;
k->class_id = PCI_CLASS_OTHERS;
dc->desc = "Test PCI Virtual Device";
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
dc->reset = qdev_test_pci_reset;
}
static const TypeInfo test_pci_info = {
.name = TYPE_TEST_PCI,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(TestPCIState),
.class_init = test_pci_class_init,
};
static void test_pci_register_types(void)
{
type_register_static(&test_pci_info);
}
type_init(test_pci_register_types)
ここで、仮想デバイスのオブジェクトは struct TestPCIState で
親として PCIDevice を持っています。この様にオブジェクトは必ず第一引数に
親の構造体を持ち、そのオブジェクトを親の型にキャストすることで
親のオブジェクトへアクセスすることが出来ます。
例えば、 test_pci_class_init 関数においては 引数のクラスオブジェクトを
PCI_DEVICE_CLASS マクロでキャストすることで、
その親クラス(PCIDeviceClass)にアクセスし、
さらに PCIDeviceClass の親であるObjectClass に、OBJECT_CLASS マクロで
キャストを行ってアクセスしています。
このデバイスのクラスは test_pci_class_init にあるように
PCI_DEVICE_CLASS となっています。
TestPCIState 中の MmeoryRegion 変数がポートI/O, メモリマップドI/O を利用する領域を設定します。ここではポートI/O、メモリマップドI/Oの領域を1つずつ
用意しています。
一番最後にある type_init マクロによりこの仮想ハードウェアが登録されます。
さらに、type_register_static を通して登録されたハードウェア情報(struct test_pci_info)に初期化関数やベンダIDといったものが記述されます。
クラスの初期化(test_pci_class_init)ではベンダIDやデバイスIDなどの設定や、
オブジェクトの初期化関数などを指定しています。
一方オブジェクトの初期化(test_pci_init) においてはまず TEST_PCI(obj)マクロを利用して、
親メンバを持つ TestPCIState オブジェクトのための領域を作成しています
(詳しくは include/qom/object.h を見てください) 。
その後、割り込みの有無と各メモリ領域(MemoryRegion)に対応する
関数の指定を行っています(これらの情報がPCIコンフィギュレーション空間に書かれます)。
なお、MemoryRegion 初期化時に必要となる MemoryRegionOps
のimplメンバのmin_access_size,max_access_size
が実装上のアクセスサイズを規定します。例えばI/O ポートからは
inl/outl命令により4バイト読み込み/書き込みができますが、max_access_size が1で
あった場合は対応するread/write 関数が4回呼ばれることになります。また、
もしmin_access_size が4であるならば1バイトのアクセス関数(inb など)
をドライバが使用した場合でも read/write 関数の第四引数 size が4となります。
また、ここで指定した名前(test_pci_info の name : TYPE_TEST_PCI == "test_pci") が
qemuを起動するときにオプションで指定する名前です(-device test_pci)。
以上のようにソースコードを書きQEMUをコンパイルした後、 -device test_pci オプションを
つけて起動し、lspci コマンドを実行すると設定したベンダID/デバイスIDの
PCIデバイスが Linux から認識されていることが確認できます。
デバイスの登録について
この様に、QEMU の各オブジェクトのソースコードには type_init が書かれています。
type_init は実はマクロでありさらに modle_init マクロに置き換わり、
最終的に __attribute__constructor()__ 属性がつく関数が作られます。
この属性が付く関数は main 関数の実行よりも前に実行されます。
なお、エミュレーションマシン全体を登録する場合は machine_init というマクロ
(同様に module_init に置き換わる) が使われます。
// include/qemu/module.h 中
#define module_init(function, type) \
static void __attribute__((constructor)) do_qemu_init_ ## function(void) \
{ \
register_dso_module_init(function, type); \
}
#else
/* This should not be used directly. Use block_init etc. instead. */
#define module_init(function, type) \
static void __attribute__((constructor)) do_qemu_init_ ## function(void) \
{ \
register_module_init(function, type); \
}
#endif
typedef enum {
MODULE_INIT_BLOCK,
MODULE_INIT_MACHINE,
MODULE_INIT_QAPI,
MODULE_INIT_QOM,
MODULE_INIT_MAX
} module_init_type;
#define block_init(function) module_init(function, MODULE_INIT_BLOCK)
#define machine_init(function) module_init(function, MODULE_INIT_MACHINE)
#define qapi_init(function) module_init(function, MODULE_INIT_QAPI)
#define type_init(function) module_init(function, MODULE_INIT_QOM)
例えば type_init(some_function) に対して do_qemu_init_some_function という名前の関数が作られ、
QEMU のmain関数が実行されるより前に実行されることになります。
その関数は結局register_module_initかregister_dso_module_init(違いはわかりません) を呼び、
最終的にリスト(QTAILQ)に名前と初期化関数が登録されます。
QEMU の main 関数(vl.c の中) の初めの方において module_call_init が呼ばれており、
これが各リストをたどって順番に登録された初期化関数を実行しています。
基本的にそれらの関数は今回の例のように type_register_static などを呼び出して
そのオブジェクトのタイプをハッシュ表に登録しており、この表を
新しいオブジェクトを生成するときに使用しているようです。
デバイスドライバの登録
デバイスドライバはまず自身をカーネルに登録する必要があります。
PCIデバイス、キャラクタデバイス 共に登録には決まったやり方があるので
これに従います。
なおPCIデバイスとしての情報は struct pci_dev,
キャラクタデバイスとしての情報はstruct cdev に格納されます。
// test_pci_driver.c (一部)
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <asm/dma.h>
#include <asm/current.h>
#include <asm/uaccess.h>
#include "test_pci.h"
MODULE_LICENSE("GPL");
// MODULE_AUTHOR("tm");
MODULE_DESCRIPTION("device driver for test pci");
#define DRIVER_TEST_NAME "test_pci"
#define DEVICE_TEST_NAME "test_pci_dev"
#define TEST_PCI_DRIVER_DEBUG
#ifdef TEST_PCI_DRIVER_DEBUG
#define tprintk(fmt, ...) printk(KERN_ALERT "** (%3d) %-20s: " fmt, __LINE__, __func__, ## __VA_ARGS__)
#else
#define tprintk(fmt, ...)
#endif
// max device count (minor number)
static int test_pci_devs_max = 1;
// if test_pci_major = 0 then use dynamic allocation
static unsigned int test_pci_major = 0;
// get parameter from console if needed for test_pci_major when insmod
module_param(test_pci_major, uint, 0);
static unsigned int test_pci_minor = 0; // static allocation
static dev_t test_pci_devt; // MKDEV(test_pci_major, test_pci_minor)
struct test_device_data {
struct pci_dev *pdev;
struct cdev *cdev;
// for PCI pio/mmio
unsigned long pio_base, pio_flags, pio_length;
unsigned long mmio_base, mmio_flags, mmio_length;
char *mmio_addr;
unsigned int pio_memsize;
// for consistent/streaming dma
dma_addr_t cdma_addr, sdma_addr;
int cdma_len, sdma_len;
void *cdma_buffer, *sdma_buffer;
wait_queue_head_t sdma_q;
};
static struct test_device_data *dev_data;
static int test_pci_open(struct inode *inode, struct file *file)
{
file->private_data = NULL;
return 0; // success
}
static int test_pci_close(struct inode *inode, struct file *file)
{
if(file->private_data) {
kfree(file->private_data);
file->private_data = NULL;
}
return 0; // success
}
struct file_operations test_pci_fops =
{
.open = test_pci_open,
.release = test_pci_close,
};
//-----------------------------------------------------------------
// supported pci id type
static struct pci_device_id test_pci_ids[] =
{
{ PCI_DEVICE(PCI_VENDOR_ID_TEST, PCI_DEVICE_ID_TEST) },
{ 0, },
};
// export pci_device_id
MODULE_DEVICE_TABLE(pci, test_pci_ids);
//-----------------------------------------------------------------
// pci initialization function
// enable pci & register character device
static int test_pci_probe (struct pci_dev *pdev,
const struct pci_device_id *id)
{
int err;
char irq;
int alloc_ret = 0;
int cdev_err = 0;
short vendor_id, device_id;
//-----------------------------------------------------------------
// config PCI
// enable pci device
err = pci_enable_device(pdev);
if(err) {
printk(KERN_ERR "can't enable pci device\n");
goto error;
}
tprintk("PCI enabled for %s\n", DRIVER_TEST_NAME);
// request PCI region
// bar 0 ... MMIO
dev_data->mmio_base = pci_resource_start(pdev, BAR_MMIO);
dev_data->mmio_length = pci_resource_len(pdev, BAR_MMIO);
dev_data->mmio_flags = pci_resource_flags(pdev, BAR_MMIO);
tprintk( "mmio_base: %lx, mmio_length: %lx, mmio_flags: %lx\n",
dev_data->mmio_base, dev_data->mmio_length, dev_data->mmio_flags);
dev_data->mmio_addr = ioremap(dev_data->mmio_base, TEST_PCI_MEMSIZE);
if(!(dev_data->mmio_flags & IORESOURCE_MEM)){
printk(KERN_ERR "BAR%d is not for mmio\n", BAR_MMIO);
goto error;
}
err = pci_request_region(pdev, BAR_MMIO, DRIVER_TEST_NAME);
if(err) {
printk(KERN_ERR "%s :error pci_request_region MMIO\n", __func__);
goto error;
}
// bar 1 ... IO port
dev_data->pio_base = pci_resource_start(pdev, BAR_PIO);
dev_data->pio_length = pci_resource_len(pdev, BAR_PIO);
dev_data->pio_flags = pci_resource_flags(pdev, BAR_PIO);
tprintk("pio_base: %lx, pio_length: %lx, pio_flags: %lx\n",
dev_data->pio_base, dev_data->pio_length, dev_data->pio_flags);
if(!(dev_data->pio_flags & IORESOURCE_IO)){
printk(KERN_ERR "BAR%d is not for pio\n", BAR_PIO);
goto error;
}
err = pci_request_region(pdev, BAR_PIO, DRIVER_TEST_NAME);
if(err) {
printk(KERN_ERR "%s :error pci_request_region PIO\n", __func__);
goto error;
}
// show PCI configuration data
// define at include/uapi/linux/pci_regs.h
pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor_id);
pci_read_config_word(pdev, PCI_DEVICE_ID, &device_id);
tprintk("PCI Vendor ID:%x, Device ID:%x\n", vendor_id, device_id);
dev_data->pdev = pdev;
dev_data->pio_memsize = TEST_PIO_DATASIZE;
tprintk("sucess allocate i/o region\n");
//-----------------------------------------------------------------
// config irq
// get irq number
irq = pdev->irq; // same as pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &irq);
tprintk("device irq: %d\n", irq);
err = request_irq(irq, test_pci_handler, 0, DRIVER_TEST_NAME, pdev);
if(err){
printk(KERN_ERR "%s :error request irq %d\n", __func__, irq);
goto error;
}
//-----------------------------------------------------------------
// register character device
// allocate major number
alloc_ret = alloc_chrdev_region(&test_pci_devt, test_pci_minor, test_pci_devs_max, DRIVER_TEST_NAME);
if(alloc_ret) goto error;
test_pci_major = MAJOR(test_pci_devt);
dev_data->cdev = (struct cdev*)kmalloc(sizeof(struct cdev), GFP_KERNEL);
if(!dev_data->cdev) goto error;
cdev_init(dev_data->cdev, &test_pci_fops);
dev_data->cdev->owner = THIS_MODULE;
cdev_err = cdev_add(dev_data->cdev, test_pci_devt, test_pci_devs_max);
if(cdev_err) goto error;
tprintk("%s driver(major %d) installed.\n", DRIVER_TEST_NAME, test_pci_major);
//-----------------------------------------------------------------
// config DMA
err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
if(err) {
printk(KERN_ERR "Cannot set DMA mask\n");
goto error;
}
pci_set_master(pdev);
// allocate consistent DMA
dev_data->cdma_buffer = pci_alloc_consistent(pdev, TEST_CDMA_BUFFER_SIZE, &dev_data->cdma_addr);
if(dev_data->cdma_buffer == NULL) {
printk(KERN_ERR "Cannot allocate consistent DMA buffer\n");
goto error;
}
dev_data->cdma_len = TEST_CDMA_BUFFER_SIZE;
// send consistent DMA info to device
outl(dev_data->cdma_addr, dev_data->pio_base + TEST_SET_CDMA_ADDR);
outl(dev_data->cdma_len, dev_data->pio_base + TEST_SET_CDMA_LEN);
tprintk("cdma_addr : %x\n", dev_data->cdma_addr);
// streaming DMA
dev_data->sdma_buffer = kmalloc(TEST_SDMA_BUFFER_SIZE, GFP_KERNEL);
if(dev_data->sdma_buffer == NULL) {
printk(KERN_ERR "Cannot allocate streaming DMA buffer\n");
goto error;
}
init_waitqueue_head(&(dev_data->sdma_q));
return 0;
error:
tprintk("PCI load error\n");
if(cdev_err == 0) cdev_del(dev_data->cdev);
if(alloc_ret == 0) unregister_chrdev_region(test_pci_devt, test_pci_devs_max);
return -1;
}
static void test_pci_remove(struct pci_dev *pdev)
{
// DMA
pci_free_consistent(dev_data->pdev, TEST_CDMA_BUFFER_SIZE,
dev_data->cdma_buffer, dev_data->cdma_addr);
// character device
cdev_del(dev_data->cdev);
unregister_chrdev_region(test_pci_devt, test_pci_devs_max);
// irq
free_irq(dev_data->pdev->irq, dev_data->pdev);
// memory mapped i/o
iounmap(dev_data->mmio_addr);
/pci
pci_release_region(dev_data->pdev, BAR_MMIO);
pci_release_region(dev_data->pdev, BAR_PIO);
pci_disable_device(dev_data->pdev);
tprintk("%s driver (major %d) unloaded\n", DRIVER_TEST_NAME, test_pci_major);
}
static struct pci_driver test_pci_driver =
{
.name = DRIVER_TEST_NAME,
.id_table = test_pci_ids,
.probe = test_pci_probe,
.remove = test_pci_remove,
};
//-----------------------------------------------------------------
// module init/exit
static int __init test_pci_init(void)
{
dev_data = kmalloc(sizeof(struct test_device_data), GFP_KERNEL);
if(!dev_data) {
printk(KERN_ERR "cannot allocate device data memory\n");
return -1; // should return other value?
}
return pci_register_driver(&test_pci_driver);
}
static void __exit test_pci_exit(void)
{
pci_unregister_driver(&test_pci_driver);
kfree(dev_data);
}
module_init(test_pci_init);
module_exit(test_pci_exit);
ドライバ固有のデータは struct test_device_data として構造体に
まとめてあります。今回はマイナー番号については考えていませんが、
考える場合はマイナー番号毎のデータを配列として確保するなどの必要があります。
プログラムの最後に書かれているように、デバイスドライバには必ずmodule_init とmodule_exit で指定された
初期化時/終了時に実行される関数があります
(ローダブルモジュールの場合は insmod/rmmod 時に実行される関数)。
なお、__init 属性をつけると実行後メモリが開放され、__exit 属性をつけると
モジュールがアンロードされない場合(予めカーネルに組み込まれるとき)
メモリが開放されるようになります。
PCIデバイスの場合はmodule_initで登録した関数から
pci_register_driver を呼び出し、PCIデバイスの登録を行います。
実際の初期化はその関数に渡した構造体(struct pci_driver) のメンバにあるprobe
の関数が行います(ここでは、test_pci_probe)。
またこれとは別に struct pci_device_id の配列にそのドライバが対応する
デバイスIDなどの情報をセット(PCI_DEVICEマクロを使用)し、
MODULE_DEVICE_TABLEにエクスポートします。
1つのドライバが複数のデバイスに対応することが可能です。
これにより、ドライバをロードした時や新たにPCIデバイスを検知した時に対応する
ドライバの初期化関数が自動で実行されます。
PCI probe 関数
probe関数においてはまず pci_enable_device によりPCIデバイスを有効化した後に、
pic_request_region 関数でI/O ポートやメモリマップドI/Oの領域を確保します。
なお、BAR領域の情報はpci_resource_start などの関数から入手できます。
pci_resource_start はその領域が始まるポート番号(I/Oポートの場合)や
物理アドレス(メモリマップドI/Oの場合)を返し、pci_resource_length がその長さ
となります。また、その他のPCI コンフィギュレーション空間の情報は
pci_read_config_byte/word などの関数から読み取れます
(ここでは確認のためにデバイス番号などを取得し、printk していますが
ドライバはこれらの情報を知っているはずです)。
次に割り込みの設定を行います(後述)。
その後PCIデバイスの種類に応じたクラスへ登録します(ここではキャラクタデバイス)。
alloc_chrdev_region で動的にメジャー番号を割り当て、
cdev_init, cdev_add によりデバイスを登録しています。なお、今回は
マイナー番号によるデバイスの区別は行っていません
最後に必要であるならばDMAの設定を行います(後述)。
一方で、module_exit で指定した関数や struct pci_driver の remove 関数では
初期化時に確保した領域の登録解消や開放を行います。
必須ではありませんが初期化の順番とは逆に行うことが一般的なようです。
適切に終了処理を行わないと終了時や、モジュールの再ロード時にカーネルパニック
を起こします。
なお、デバイスドライバにおいてはエラー処理にgoto文を使う
ことは普通だということなので、ここでもその書き方にならっています。
システムコール用の関数は struct file_operations を用いて登録します。
最低限 open/close に対応するメンバ (.open/.release) に関数を用意します。
このモジュールをコンパイル後 insmod/mknod を行いドライバをロードすると、
/proc/iomem, ioports, interrupt などの情報に test_pci の名前が付くことが
確認できます。
ユーザープログラム
デバイスファイルを開くにはopen/close システムコールを利用します。
int open_device(const char* filename)
{
int fd;
fd = open(filename, O_RDWR);
if(fd == -1){
printf("can't open device: %s", filename);
exit(1);
}
printf("success: open device %s\n", filename);
return fd;
}
void close_device(int fd)
{
if(close(fd) != 0){
printf("can't close device");
exit(1);
}
printf("success: close device\n");
}
実行結果
-device test_pci を付けて QEMU 実行開始後に
各種コマンドを実行すると以下のようになります。
# lspci
00:00.0 Class 0600: 8086:1237
00:01.0 Class 0601: 8086:7000
00:01.1 Class 0101: 8086:7010
00:01.3 Class 0680: 8086:7113
00:02.0 Class 0300: 1013:00b8
00:03.0 Class 0200: 8086:100e
00:04.0 Class 00ff: 1234:0001 // 作成したPCIデバイス
# lsmod
Module Size Used by Tainted: G
# insmod /drivers/test_pci_driver.ko
** (315) test_pci_probe : PCI enabled for test_pci
** (323) test_pci_probe : mmio_base: febf1000, mmio_length: 1000, mmio_flags: 40200
** (344) test_pci_probe : pio_base: c000, pio_length: 100, pio_flags: 40101
** (361) test_pci_probe : PCI Vendor ID:1234, Device ID:1
** (367) test_pci_probe : sucess allocate i/o region
** (373) test_pci_probe : device irq: 11
** (399) test_pci_probe : test_pci driver(major 254) installed.
## (155) test_pci_pio_write : addr 116, size 4
## (155) test_pci_pio_write : addr 120, size 4
** (422) test_pci_probe : cdma_addr : 79b7000
# lsmod
Module Size Used by Tainted: G
test_pci_driver 6252 0
# cat /proc/ioports | grep test
c000-c0ff : test_pci
# cat /proc/iomem | grep test
febf1000-febf1fff : test_pci
# cat /proc/interrupts |grep test
11: 3 XT-PIC-XT-PIC test_pci
# cat /proc/devices | grep test
254 test_pci
# mknod /dev/test_pci c 254 0
# rmmod test_pci_driver
** (459) test_pci_remove : test_pci driver (major 254) unloaded
# rm /dev/test_pci
# lsmod
Module Size Used by Tainted: G
#
また、ctrl-a+c でモニタ画面に移動後 info pci
により以下の情報が得られます。
(qemu) info pci
Bus 0, device 0, function 0:
Host bridge: PCI device 8086:1237
id ""
Bus 0, device 1, function 0:
ISA bridge: PCI device 8086:7000
id ""
Bus 0, device 1, function 1:
IDE controller: PCI device 8086:7010
BAR4: I/O at 0xc140 [0xc14f].
id ""
Bus 0, device 1, function 3:
Bridge: PCI device 8086:7113
IRQ 9.
id ""
Bus 0, device 2, function 0:
VGA controller: PCI device 1013:00b8
BAR0: 32 bit prefetchable memory at 0xfc000000 [0xfdffffff].
BAR1: 32 bit memory at 0xfebf0000 [0xfebf0fff].
BAR6: 32 bit memory at 0xffffffffffffffff [0x0000fffe].
id ""
Bus 0, device 3, function 0:
Ethernet controller: PCI device 8086:100e
IRQ 11.
BAR0: 32 bit memory at 0xfebc0000 [0xfebdffff].
BAR1: I/O at 0xc100 [0xc13f].
BAR6: 32 bit memory at 0xffffffffffffffff [0x0003fffe].
id ""
Bus 0, device 4, function 0:
Class 0255: PCI device 1234:0001
IRQ 11.
BAR0: 32 bit memory at 0xfebf1000 [0xfebf1fff].
BAR1: I/O at 0xc000 [0xc0ff].
id ""
lspci コマンドの結果と一致しています。
はじめ: [(1) 作ったもの]
(http://qiita.com/rafilia/items/f7646d12212da2a85bd8)
前: [(3) 開発/動作環境]
(http://qiita.com/rafilia/items/ff02c38be2ab01220fcb)
次: [(5) I/Oポートの使用]
(http://qiita.com/rafilia/items/9e8d62444be472e573c2)