QEMU建模之设备创建总体流程

本文详细介绍了QEMU中设备创建的过程,以中断控制器openpic为例,从main函数之前的type_init开始,通过module_init函数执行typeInfo到typeImpl的转换,再到main函数中调用module_call_init,最后在特定单板中利用qdev_create函数创建设备,完成设备初始化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(这里的设备创建以中断控制器即openpic为例)

1.main函数之前执行type_init

1> 在vl.c文件的main函数执行前会先执行 module_init的函数。因为该函数使用GNC中attribute属性constructor即构造函数属性,该属性的设置使得该函数在main函数之前被执行。函数原型如下:其路径为G:\qemu-3.1.0-rc0\hw\intc\openpic.c

static void openpic_register_types(void)
{
    type_register_static(&openpic_info);
}

type_init(openpic_register_types)

2> type_init函数的宏定义为G:\qemu-3.1.0-rc0\include\qemu\module.h

#define block_init(function) module_init(function, MODULE_INIT_BLOCK)
#define opts_init(function) module_init(function, MODULE_INIT_OPTS)
#define type_init(function) module_init(function, MODULE_INIT_QOM)
#define trace_init(function) module_init(function, MODULE_INIT_TRACE)


#define module_init(function, type)                                         \
static void __attribute__((constructor)) do_qemu_init_ ## function(void)    \
{                                                                           \
    register_module_init(function, type);                                   \
}

3> register_module_init函数原型在G:\qemu-3.1.0-rc0\include\qemu\module.c文件中

void register_module_init(void (*fn)(void), module_init_type type)
{
    ModuleEntry *e;
    ModuleTypeList *l;

    e = g_malloc0(sizeof(*e));
    e->init = fn;
    e->type = type;

    l = find_type(type);

    QTAILQ_INSERT_TAIL(l, e, node);
}

该函数首先动态分配指向ModuleEntry类型的结点,使该节点中的init函数指向openpic这个设备的openpic_register_types函数,其类型为MODULE_INIT_QOM,再将该节点插入链表中。具体执行过程在G:\qemu-3.1.0-rc0\include\qemu\queue.h文件中,执行过程 如下:

#define QTAILQ_INSERT_TAIL(head, elm, field) do {  \//这是一个双向链表
        (elm)->field.tqe_next = NULL;              \//使插入结点的下一节点指向空
        (elm)->field.tqe_prev = (head)->tqh_last;  \//使插入结点的前一结点指向原链表的尾结点
        *(head)->tqh_last = (elm);                 \//使链表的尾结点的下一节点指针指向指向这个
                                                     新插入的结点
        (head)->tqh_last = &(elm)->field.tqe_next; \//链表的尾指针指向新插入结点
} while (/*CONSTCOND*/0)

现在已经将将其存储到链表中了。

2.main函数中,调用module_call_init函数,完成TypeInfo到TypeImpl的转换

1> main函数中调用module_call_init函数,路径为G:\qemu\qemu-3.1.0-rc0\vl.c

 module_call_init(MODULE_INIT_QOM);

2> 执行moduleEntry节点中存储的init函数,路径为G:\qemu-3.1.0-rc0\util\module.c

void module_call_init(module_init_type type)
{
    ModuleTypeList *l;
    ModuleEntry *e;

    l = find_type(type);                      //首先找到该类型的链表的头指针

    QTAILQ_FOREACH(e, l, node) {              //执行所存储的结点的init函数,在openpic中对应
                                              //openpic_register_types函数
        e->init();
    }
}

3> openpic_register_types函数的执行,函数所在文件为G:\qemu-3.1.0-rc0\hw\intc\openpic.c

//首先执行

static void openpic_register_types(void)
{
    type_register_static(&openpic_info);
}
//函数调用
TypeImpl *type_register_static(const TypeInfo *info)
{
    return type_register(info);
}
//函数调用
TypeImpl *type_register(const TypeInfo *info)
{
    assert(info->parent);
    return type_register_internal(info);
}
//函数调用
static TypeImpl *type_register_internal(const TypeInfo *info)
{
    TypeImpl *ti;
    ti = type_new(info);

    type_table_add(ti);       //将这个typeimpl添加到hash表中
    return ti;
}
//函数调用
static TypeImpl *type_new(const TypeInfo *info)
{
    TypeImpl *ti = g_malloc0(sizeof(*ti)); 
### 创建和配置QEMU中的PCI设备QEMU创建和配置PCI设备涉及几个关键步骤,这些步骤确保了设备能够被正确识别并与虚拟机交互。 #### 初始化PCI总线 为了使PCI设备能够在虚拟环境中正常运行,首先需要初始化PCI总线。这一过程通常由`pc_init1()`函数启动,在此期间会调用`i440fx_init()`来建立一个名为`pci_bus`的对象,并将其与ISA总线连接形成PCI-ISA桥[^3]。 ```c PCIBus *i440fx_init(const char *device_name, int devfn, MemoryRegion **pic_state) { PCIDevice *d; d = pci_create_simple(pci_find_rootbus(), devfn, device_name); ... } ``` #### 添加PCI设备到总线上 一旦有了可用的PCI总线实例,就可以通过定义特定类型的PCI设备并将其挂载至该总线上实现扩展功能。这一步骤可以通过提供相应的内存区域操作(`MemoryRegionOps`)以及设置必要的回调机制完成,使得新加入的硬件组件可以响应来自客户操作系统发出的各种请求[^1]。 ```c static void my_pci_device_realize(PCIDevice *dev, Error **errp) { MyState *s = MY_DEVICE(dev); memory_region_init_io(&s->mmio, OBJECT(s), &my_ops, s, "my-device", SIZE); pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio); } static const VMStateDescription vmstate_my_device = { .name = "my-device", .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { /* ... */ VMSTATE_END_OF_LIST() } }; static Property my_properties[] = { DEFINE_PROP_UINT8("foo", MyState, foo, 0), DEFINE_PROP_END_OF_LIST(), }; static void my_pci_device_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); k->realize = my_pci_device_realize; set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->vmsd = &vmstate_my_device; dc->props = my_properties; } static TypeInfo my_pci_device_info = { .name = TYPE_MY_DEVICE, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(MyState), .class_init = my_pci_device_class_init, }; ``` 上述代码片段展示了如何构建自定义PCI设备类及其属性描述符;同时指定了当此类对象实例化时应执行的实际逻辑——即分配资源地址范围、注册中断处理程序等重要细节。 #### 配置选项传递给QEMU命令行 最后,为了让用户可以在启动虚拟机之前指定想要加载哪些额外的外设模块,还需要支持从外部传入参数的方式来进行灵活定制。例如: ```bash qemu-system-x86_64 \ -m 2G \ -cpu host \ -M pc-q35-7.2 \ -device my-pci-device,id=my_dev,bus=pcie.0,addr=0x9 \ -drive file=disk.img,if=virtio \ ... ``` 在此示例中,`-device` 参数允许使用者轻松地向目标平台添加新的PCI装置,并可通过附加的关键字进一步调整其行为模式或位置信息[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值