在qemu中增加pci设备并用linux驱动验证

声明本文主要针对x86架构进行说明。

使用的qemu版本是:qemu-kvm-1.2.0-rc2


1)PCI结构简介

每个PCI设备都有一个配置空间和若干个地址空间,按照固定的顺序放置CPI的各种配置参数。关于详细的介绍可以在网上搜索相关的资料。

下面是在busybox下lspci -mk的输出内容说明:

00:00.0 "Class 0600" "8086" "1237" "1af4" "1100"
00:01.0 "Class 0601" "8086" "7000" "1af4" "1100"
00:01.1 "Class 0101" "8086" "7010" "1af4" "1100" "ata_piix"
00:01.3 "Class 0680" "8086" "7113" "1af4" "1100"
00:02.0 "Class 0300" "1013" "00b8" "1af4" "1100"
00:03.0 "Class 0200" "10ec" "8139" "1af4" "1100" "8139cp"
00:04.0 "Class 0604" "1011" "0026" "0000" "0000"
01:00.0 "Class 3542" "1234" 		"5678" 		"6872" 			"8952"
	class_id	vendor_id	device_id	subsystem_vendor_id	subsystem_id

2)qemu的桥

在qemu中桥,总线,设备都会对应一个设备结构。最开始的初始化硬件的函数是pc_init1,在这里调用函数i440fx_init创建一个pci_bus,并且和isa_bus关联起来,(qemu模拟的还是pci-isa桥),然后再基于pci_bus创建一系列的设备。


3)pci设备创建

先看下一个pci设备的结构是怎样的:

static TypeInfo mem_pci_info = {
    .name = "mem_pci",
    .parent = TYPE_PCI_DEVICE,
    .instance_size = sizeof(PCIMEMPCIState),
    .class_init = mem_pci_class_init,          /// pci 设备的初始化函数
};

static void mem_pci_register_types(void)
{
    type_register_static(&mem_pci_info);          /// 注册设备结构
}

在函数mem_pci_class_init里面为PCIDeviceClass的init数据成员赋值mem_pci_init

static int mem_pci_init(PCIDevice *dev)
{
    PCIMEMPCIState *pci = DO_UPCAST(PCIMEMPCIState, pci_dev, dev);
    MEMPCIState *s = &pci->state;

    pci->mem_pci_base 	= (uint32_t)malloc(PCI_MEM_SIZE);                          
    memory_region_init_io(&s->mem, &mem_pci_ops, pci, "mem-pci", PCI_MEM_SIZE);        /// 注册一个MemoryRegion结构体,并分配一个                                                                                   ///MemoryRegionOps数据成员,这样
    pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem);                  ///给pci设备注册一个bar类型是mem。

    return 0;
}

由于本文需要让 linux内核的pci驱动 跟  qemu模拟的pci设备  之间实现通信,注意仅仅是实现数据流的传送。如果操作pci设备空间用qemu提供的api函数 cpu_inb ,那么会导致qemu把通信的数据当作操作设备的命令来执行。所以这里申请了一块内存并用mem_pci_base来指向申请的内存。

当以后需要对这块内存读写操作的时候就可以直接读写这块内存:

static void mem_pci_write(void *opaque, target_phys_addr_t addr,
                             uint64_t value, unsigned int size)
{
    	void  	*pci_mem_addr;
    	int 	temp,region_size;
	byte	buff[8];

	pci_mem_addr	= ((PCIMEMPCIState *)opaque)->mem_pci_base;
	pci_mem_addr	= ((char *)pci_mem_addr) + addr;

	switch (size) {
	    	case 1:
			sprintf(buff,"%02llx",value);
			sscanf(buff,"%x",&temp);
			*((byte*)pci_mem_addr)	= (byte)temp;
			break;
	}
}
具体的qemu端pci设备 mem_pci.c 实现方式如下所示:

/*
 * QEMU memory pci emulation (PCI to ISA bridge)
 *
 */

#include "pci.h"
#include "pc.h"
#include "i8254.h"
#include "pcspk.h"
#include "hw.h"


#define MEM_PCI_VENDOR_ID	0x1234
#define	MEM_PCI_DEVICE_ID	0x5678
#define	MEM_PCI_REVISION_ID	0x73

#define	PCI_MEM_SIZE		0x00000010

typedef struct MEMPCIState {
    MemoryRegion mem;
} MEMPCIState;

typedef struct PCIMEMPCIState {
    PCIDevice pci_dev;
    uint32_t mem_pci_base;
    MEMPCIState state;
} PCIMEMPCIState;

static const VMStateDescription vmstate_mem_pci = {
    .name = "mem_pci",
    .version_id = 0,
    .minimum_version_id = 0,
    .fields = (VMStateField[]) {
        VMSTATE_PCI_DEVICE(pci_dev, PCIMEMPCIState),
        VMSTATE_END_OF_LIST()
    },
};


typedef unsigned char  byte;
typedef unsigned short int uint16;
typedef unsigned int 	uint32;


static void mem_pci_write(void *opaque, target_phys_addr_t addr,
                             uint64_t value, unsigned int size)
{
    	void  	*pci_mem_addr;
    	int 	temp,region_size;
	byte	buff[8];
	
	pci_mem_addr	= ((PCIMEMPCIState *)opaque)->mem_pci_base;
	pci_mem_addr	= ((char *)pci_mem_addr) + addr;
	region_size	= (int)memory_region_size( &((PCIMEMPCIState *)opaque)->state.mem);

	if(addr > region_size)
		return ;
	fprintf(stderr,"%x\n",pci_mem_addr);
	switch (size) {
	    	case 1:
			sprintf(buff,"%02llx",value);
			sscanf(buff,"%x",&temp);
			*((byte*)pci_mem_addr)	= (byte)temp;
			break;
	    	case 2:
			sprintf(buff,"%04llx",value);
			sscanf(buff,"%x",&temp);
			*((uint16*)pci_mem_addr)= (uint16)temp;
			break;
	    	case 4:
			sprintf(buff,"%08llx",value);
			sscanf(buff,"%x",&temp);				
			*((uint32*)pci_mem_addr)= (uint32)temp;
			break;
	}
	fprintf(stderr,"%x\n",temp);
}

static uint64_t mem_pci_read(void *opaque, target_phys_addr_t addr,
                                unsigned int size)
{
	void  	*pci_mem_addr;
	int 	temp,region_size;
	byte	buff[8];
	pci_mem_addr	= ((PCIMEMPCIState *)opaque)->mem_pci_base;
	pci_mem_addr	= ((char *)pci_mem_addr) + addr;
	region_size	= memory_region_size(&((PCIMEMPCIState *)opaque)->state.mem);	

	if(addr > region_size)
		return 0;
	switch (size) {
		case 1:
			temp = *((byte *)pci_mem_addr);
			return ((byte)temp);
		case 2:
			temp = *((uint16 *)pci_mem_addr);
			return ((uint16)temp);
		case 4:
			temp = *((uint32 *)pci_mem_addr);
			return ((uint32)temp);
	}
    	//fprintf(stderr,"%d",temp);
}

static const MemoryRegionOps mem_pci_ops = {
    .read = mem_pci_read,
    .write = mem_pci_write,
    .endianness = DEVICE_LITTLE_ENDIAN,
};


static Property mem_pci_properties[] = {
    DEFINE_PROP_HEX32("membase", PCIMEMPCIState, mem_pci_base, 0xc0000000),
    DEFINE_PROP_END_OF_LIST()
};


static int mem_pci_init(PCIDevice *dev)
{
    PCIMEMPCIState *pci = DO_UPCAST(PCIMEMPCIState, pci_dev, dev);
    MEMPCIState *s = &pci->state;

    pci->mem_pci_base 	= (uint32_t)malloc(PCI_MEM_SIZE);
    memory_region_init_io(&s->mem, &mem_pci_ops, pci, "mem-pci", PCI_MEM_SIZE);
    pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem);

    return 0;
}

static void mem_pci_class_init(ObjectClass *klass, void *data)
{
    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
    DeviceClass *dc = DEVICE_CLASS(klass);

    k->init = mem_pci_init;
    k->vendor_id = MEM_PCI_VENDOR_ID;
    k->device_id = MEM_PCI_DEVICE_ID;
    k->revision  = MEM_PCI_REVISION_ID;

    dc->vmsd = &vmstate_mem_pci;
    dc->props = mem_pci_properties;
}

static TypeInfo mem_pci_info = {
    .name = "mem_pci",
    .parent = TYPE_PCI_DEVICE,
    .instance_size = sizeof(PCIMEMPCIState),
    .class_init = mem_pci_class_init,
};

static void mem_pci_register_types(void)
{
    type_register_static(&mem_pci_info);
}

type_init(mem_pci_register_types)


在文件hw/pc_piix.c 函数pc_init1里面增加创建设备的代码:

    pc_cmos_init(below_4g_mem_size, above_4g_mem_size, boot_device,
                 floppy, idebus[0], idebus[1], rtc_state);

    pci_create_simple_multifunction(pci_bus, -1,true ,"mem_pci");

    if (pci_enabled && usb_enabled) {
        pci_create_simple(pci_bus, piix3_devfn + 2, "piix3-usb-uhci");
    }


把源文件mem_pci.c放在hw目录下,在文件hw/Makefile.objs 增加如下代码:

hw-obj-y                  += mem_pci.o

然后编译。


linux内核一端需要有一个pci驱动来驱动这个我们模拟的pci设备,这里仅仅是一个简单的pci设备驱动,关于其框架不多说了,网上有很多教程。直接给出代码吧:

(注意对比这个驱动代码和上面的设备代码相同的地方)

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>


#define MEM_PCI_VENDOR_ID       0x1234
#define MEM_PCI_DEVICE_ID       0x5678  	
#define MEM_PCI_REVISION_ID     0x73

typedef unsigned char  byte;
typedef unsigned short int uint16;
typedef unsigned int 	uint32;

static struct pci_device_id ids[] = {
    { PCI_DEVICE(MEM_PCI_VENDOR_ID, MEM_PCI_DEVICE_ID), },
    { 0, }
};
MODULE_DEVICE_TABLE(pci, ids);

static unsigned char skel_get_revision(struct pci_dev *dev)
{
    u8 revision;

    pci_read_config_byte(dev, PCI_REVISION_ID, &revision);
    return revision;
}


//return 0 means success
static int probe(struct pci_dev *dev, const struct pci_device_id *id)
{
    	/* Do probing type stuff here.  
    	 * Like calling request_region();
    	 */	
	unsigned char revision_id;
	int bar ;

	if (skel_get_revision(dev) != MEM_PCI_REVISION_ID)
		return 1;

    	pci_enable_device(dev);

	bar	= 1;
	resource_size_t start = pci_resource_start(dev, bar);
	resource_size_t len = pci_resource_len(dev, bar);
	unsigned long flags = pci_resource_flags(dev, bar);

	void __iomem * addressio = pci_iomap(dev,bar,len);
	*(byte *)addressio = 0x57;
	iowrite8(0x89,addressio + 8);
	printk("%x\n",ioread8(addressio + 8));
	printk("%x\n",*(byte *)addressio);

	return 0;
}

static void remove(struct pci_dev *dev)
{
	/* clean up any allocated resources and stuff here.
	* like call release_region();
	*/
	pci_disable_device(dev);
}

static struct pci_driver pci_driver = {
    .name = "mem_pci",
    .id_table = ids,
    .probe = probe,
    .remove = remove,
};

static int __init mem_pci_init(void)
{
    return pci_register_driver(&pci_driver);
}

static void __exit mem_pci_exit(void)
{
    pci_unregister_driver(&pci_driver);
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("gudujian");  

module_init(mem_pci_init);
module_exit(mem_pci_exit);

上面的驱动程序在pci设备的首字节写了一个字符0x57.在第8个字节写了一个字符0x89.并读出来:


上面只是实现了一个简单的字节读写功能,有兴趣的可以参考我以前的文章 http://blog.csdn.net/xsckernel/article/details/8159568 把pci驱动实现成一个字符驱动。


本文部分参考文章:

http://blog.csdn.net/yearn520/article/details/6576875

http://blog.csdn.net/yearn520/article/details/6577988



























  • 4
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值