How to create out-of-tree QEMU devices?

前端 未结 3 923
离开以前
离开以前 2020-12-11 19:15

Two possible mechanisms come to mind:

  • IPC like the existing QMP and QAPI
  • QEMU loads a shared library plugin that contains the model

Req

相关标签:
3条回答
  • 2020-12-11 19:31

    This create out of tree PCI device , it just display device in lspci.. It will ease faster PCI driver implementation as it will act as module, can we extend this to to have similar functionality as edu-pci of QEMU.?

    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");
    
    0 讨论(0)
  • 2020-12-11 19:34

    There is at least one fork of QEMU I'm aware of that offers shared library plugins for QEMU... but it's a fork of QEMU 4.0.

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

    It is possible to build out of tree plugins with this fork, though it's not documented.

    0 讨论(0)
  • 2020-12-11 19:40

    On Nov 11 2019 Peter Maydell, a major QEMU contributor, commented on another Stack Overflow question that:

    Device plugins are specifically off the menu, because upstream does not want to provide a nice easy mechanism for people to use to have out-of-tree non-GPL/closed-source devices.

    So it seems that QEMU devs oppose this idea at that point in time. It is worth learning about the QEMU plugin system though which might come handy for related applications in any case: How to count the number of guest instructions QEMU executed from the beginning to the end of a run?

    This is a shame. Imagine if the Linux kernel didn't have a kernel module interface! I suggest QEMU expose this interface, but just don't make it stable, so that it won't impose a developer burden, and which gives the upside that those who merge won't have as painful rebases.

    0 讨论(0)
提交回复
热议问题