How to create out-of-tree QEMU devices?
Asked Answered
S

3

6

Two possible mechanisms come to mind:

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

Required capabilities (of course all possible through the C API, but not necessarily IPC APIs):

  • inject interrupts
  • register callbacks for register access
  • modify main memory

Why I want this:

  • use QEMU as a submodule and leave its source untouched
  • additional advantages only present for IPC methods:
    • write the models in any language I want
    • use a non-GPL license for my device

I'm aware of in-tree devices as explained at: How to add a new device in QEMU source code? which are the traditional way of doing things.

What I've found so far:

The closest working piece of code I could find was: https://github.com/texane/vpcie , which serializes PCI on both sides, and sends it through QEMU's TCP API. But this is more inefficient and intrusive, as it requires extra setup on both guest and host.

Shippee answered 30/5, 2017 at 6:14 Comment(0)
P
3

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");
Parfitt answered 1/11, 2017 at 12:37 Comment(0)
S
1

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.

Shippee answered 30/4, 2020 at 19:28 Comment(0)
B
1

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.

Burack answered 1/9, 2020 at 17:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.