Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create out-of-tree QEMU devices?

Tags:

qemu

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:

  • interrupts: could only find NMI generation with the nmi monitor command
  • IO ports: IO possible with i and o monitor commands, so I'm fine there
  • main memory:
    • the ideal solution would be to map memory to host directly, but that seems hard:
      • http://kvm.vger.kernel.narkive.com/rto1dDqn/sharing-variables-memory-between-host-and-guest
      • https://www.linux-kvm.org/images/e/e8/0.11.Nahanni-CamMacdonell.pdf
      • http://www.fp7-save.eu/papers/SCALCOM2016.pdf
    • memory read is possible through the x and xp monitor commands
    • could not find how to write to memory with monitor commands. But I think the GDB API supports, so it should not be too hard to implement.

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.


Video Answer


1 Answers

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");
like image 121
Alok Prasad Avatar answered Oct 17 '22 02:10

Alok Prasad