【问题标题】:How to create out-of-tree QEMU devices?如何创建树外 QEMU 设备?
【发布时间】:2017-10-30 11:43:57
【问题描述】:

想到了两种可能的机制:

  • IPC 类似于现有的 QMP 和 QAPI
  • QEMU 加载包含模型的共享库插件

所需的功能(当然所有可能通过 C API,但不一定是 IPC API):

  • 注入中断
  • 为注册访问注册回调
  • 修改主存

为什么我想要这个:

  • 将 QEMU 用作子模块并保持其源代码不变
  • 其他优势仅适用于 IPC 方法:
    • 用我想要的任何语言编写模型
    • 为我的设备使用非 GPL 许可

我知道树内设备,如 How to add a new device in QEMU source code? 所述,这是传统的做事方式。

到目前为止我发现了什么:

我能找到的最接近的工作代码是:https://github.com/texane/vpcie,它在两侧序列化 PCI,并通过 QEMU 的 TCP API 发送它。但这更加低效和侵入性,因为它需要在访客和主机上进行额外设置。

【问题讨论】:

    标签: qemu


    【解决方案1】:

    这创建了树形 PCI 设备,它只是在 lspci 中显示设备.. 它将简化更快的 PCI 驱动程序实施,因为它将充当模块, 我们可以将其扩展为具有与 QEMU 的 edu-pci 类似的功能吗?

    https://github.com/alokprasad/pci-hacking/blob/master/ksrc/virtual_pcinet/virtual_pci.c

    /*
     */
    
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/sysfs.h>
    #include <linux/fs.h>
    #include <linux/kobject.h>
    #include <linux/device.h>
    #include <linux/proc_fs.h>
    #include <linux/types.h>
    #include <linux/pci.h>
    #include <linux/version.h>
    #include<linux/kernel.h>
    
    
    #define PCI_VENDOR_ID_XTREME        0x15b3
    #define PCI_DEVICE_ID_XTREME_VNIC   0x1450
    
    static struct pci_bus *vbus;
    static struct pci_sysdata *sysdata;
    
    static DEFINE_PCI_DEVICE_TABLE( vpci_dev_table) = {
        {PCI_DEVICE(PCI_VENDOR_ID_XTREME, PCI_DEVICE_ID_XTREME_VNIC)},
        {0}
    };
    
    MODULE_DEVICE_TABLE(pci,  vpci_dev_table);
    
    int  vpci_read(struct pci_bus *bus, unsigned int devfn, int where,
             int size, u32 *val)
    {
        switch (where) {
        case PCI_VENDOR_ID:
            *val = PCI_VENDOR_ID_XTREME | PCI_DEVICE_ID_XTREME_VNIC << 16;
            /* our id */
            break;
        case PCI_COMMAND:
            *val = 0;
            break;
        case PCI_HEADER_TYPE:
            *val = PCI_HEADER_TYPE_NORMAL;
            break;
        case PCI_STATUS:
            *val = 0;
            break;
        case PCI_CLASS_REVISION:
            *val = (4 << 24) | (0 << 16) | 1;
            /* network class, ethernet controller, revision 1 */ /*2 or 4*/
            break;
        case PCI_INTERRUPT_PIN:
            *val = 0;
            break;
        case PCI_SUBSYSTEM_VENDOR_ID:
            *val = 0;
            break;
        case PCI_SUBSYSTEM_ID:
            *val = 0;
            break;
        default:
            *val = 0;
            /* sensible default */
        }
        return 0;
    }
    
    int  vpci_write(struct pci_bus *bus, unsigned int devfn, int where,
              int size, u32 val)
    {
        switch (where) {
        case PCI_BASE_ADDRESS_0:
        case PCI_BASE_ADDRESS_1:
        case PCI_BASE_ADDRESS_2:
        case PCI_BASE_ADDRESS_3:
        case PCI_BASE_ADDRESS_4:
        case PCI_BASE_ADDRESS_5:
            break;
        }
        return 0;
    }
    
    struct pci_ops  vpci_ops = {
        .read =  vpci_read,
        .write =  vpci_write
    };
    
    
    void  vpci_remove_vnic()
    {
        struct pci_dev *pcidev = NULL;
        if (vbus == NULL)
            return;
        pci_remove_bus_device(pcidev);
        pci_dev_put(pcidev);
    }
    EXPORT_SYMBOL( vpci_remove_vnic);
    
    void  vpci_vdev_remove(struct pci_dev *dev)
    {
    }
    
    static struct pci_driver  vpci_vdev_driver = {
        .name = "Xtreme-Virtual-NIC1",
        .id_table =  vpci_dev_table,
        .remove =  vpci_vdev_remove
    };
    
    int  vpci_bus_init(void)
    {
        struct pci_dev *pcidev = NULL;
        sysdata = kzalloc(sizeof(void *), GFP_KERNEL);
            vbus = pci_scan_bus_parented(NULL, 2, & vpci_ops, sysdata);
            //vbus = pci_create_root_bus(NULL,i,& vpci_ops, sysdata,NULL);
            //if (vbus != NULL)
                //break;
            memset(sysdata, 0, sizeof(void *));
        if (vbus == NULL) {
            kfree(sysdata);
            return -EINVAL;
        }
        if (pci_register_driver(& vpci_vdev_driver) < 0) {
            pci_remove_bus(vbus);
            vbus = NULL;
            return -EINVAL;
        }
        pcidev = pci_scan_single_device(vbus, 0);
    
            if (pcidev == NULL)
                    return 0;
            else
                    pci_dev_get(pcidev);
    
        pci_bus_add_devices(vbus);
    
        return 0;
    }
    
    void  vpci_bus_remove(void)
    {
        if (vbus) {
            pci_unregister_driver(&vpci_vdev_driver);
            device_unregister(vbus->bridge);
            pci_remove_bus(vbus);
            kfree(sysdata);
            vbus = NULL;
        }
    }
    
    
    static int __init pci_init(void)
    {
        printk( "module loaded");
         vpci_bus_init();
        return 0;
    }
    
    static void __exit pci_exit(void)
    {
            printk(KERN_ALERT "unregister PCI Device\n");
            pci_unregister_driver(&vpci_vdev_driver);
    }
    
    
    module_init(pci_init);
    module_exit(pci_exit);
    MODULE_LICENSE("GPL");
    

    【讨论】:

      【解决方案2】:

      据我所知,至少有一个 QEMU 分支为 QEMU 提供共享库插件...但它是 QEMU 4.0 的一个分支。

      https://github.com/cromulencellc/qemu-shoggoth

      虽然没有记录,但可以使用此 fork 构建树插件。

      【讨论】:

        【解决方案3】:

        2019 年 11 月 11 日,QEMU 的主要贡献者 Peter Maydell,commented on another Stack Overflow question

        设备插件特别不在菜单中,因为上游不希望为人们提供一个很好的简单机制来使用树外非 GPL/闭源设备。

        因此,QEMU 开发人员似乎在那个时候反对这个想法。值得学习 QEMU 插件系统,尽管它在任何情况下都可能对相关应用程序派上用场:How to count the number of guest instructions QEMU executed from the beginning to the end of a run?

        这是一种耻辱。想象一下,如果 Linux 内核没有内核模块接口!我建议 QEMU 公开这个接口,但不要让它变得稳定,这样它就不会给开发人员带来负担,而且这样做的好处是合并的人不会有痛苦的 rebase。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-09-14
          • 2019-06-19
          • 2015-10-08
          • 2013-09-01
          • 2019-11-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多