From nobody Wed Nov 5 14:41:40 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1535441499599317.6519098225324; Tue, 28 Aug 2018 00:31:39 -0700 (PDT) Received: from localhost ([::1]:36732 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fuYTP-0004ac-LI for importer@patchew.org; Tue, 28 Aug 2018 03:31:35 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:43825) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fuYRg-0003gj-GE for qemu-devel@nongnu.org; Tue, 28 Aug 2018 03:29:51 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fuYMg-0007iP-DC for qemu-devel@nongnu.org; Tue, 28 Aug 2018 03:24:41 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:35258 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fuYMg-0007dr-4V for qemu-devel@nongnu.org; Tue, 28 Aug 2018 03:24:38 -0400 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 34942805A6D8; Tue, 28 Aug 2018 07:24:36 +0000 (UTC) Received: from localhost.localdomain (unknown [10.35.206.49]) by smtp.corp.redhat.com (Postfix) with ESMTP id 3CE182166B41; Tue, 28 Aug 2018 07:24:33 +0000 (UTC) From: Yoni Bettan To: qemu-devel@nongnu.org Date: Tue, 28 Aug 2018 10:24:26 +0300 Message-Id: <20180828072426.45103-1-ybettan@redhat.com> X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Tue, 28 Aug 2018 07:24:36 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Tue, 28 Aug 2018 07:24:36 +0000 (UTC) for IP:'10.11.54.6' DOMAIN:'int-mx06.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'ybettan@redhat.com' RCPT:'' X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [RFC V1] hw/pci/pci_example.c : Added a new pci device X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Yoni Bettan , ehabkost@redhat.com, "Michael S. Tsirkin" Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RDMRC_1 RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" - this is a simple example of how to write a pci device that supports portio, mmio, irq and dma Signed-off-by: Yoni Bettan --- hw/pci/Makefile.objs | 1 + hw/pci/pci_example.c | 309 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 310 insertions(+) create mode 100644 hw/pci/pci_example.c diff --git a/hw/pci/Makefile.objs b/hw/pci/Makefile.objs index 9f905e6344..e684b72f90 100644 --- a/hw/pci/Makefile.objs +++ b/hw/pci/Makefile.objs @@ -4,6 +4,7 @@ common-obj-$(CONFIG_PCI) +=3D shpc.o common-obj-$(CONFIG_PCI) +=3D slotid_cap.o common-obj-$(CONFIG_PCI) +=3D pci_host.o pcie_host.o common-obj-$(CONFIG_PCI) +=3D pcie.o pcie_aer.o pcie_port.o +common-obj-$(CONFIG_PCI) +=3D pci_example.o =20 common-obj-$(call lnot,$(CONFIG_PCI)) +=3D pci-stub.o common-obj-$(CONFIG_ALL) +=3D pci-stub.o diff --git a/hw/pci/pci_example.c b/hw/pci/pci_example.c new file mode 100644 index 0000000000..326c9b7fa1 --- /dev/null +++ b/hw/pci/pci_example.c @@ -0,0 +1,309 @@ +#include "qemu/osdep.h" +#include "hw/pci/pci.h" + +#define TYPE_PCI_EXAMPLE "pci-example" + +#define PCI_EXAMPLE(obj) \ + OBJECT_CHECK(PCIExampleDevState, (obj), TYPE_PCI_EXAMPLE) + + +#define ERROR -1 +#define BLOCK_SIZE 64 +#define EXAMPLE_MMIO_SIZE BLOCK_SIZE +#define EXAMPLE_PIO_SIZE BLOCK_SIZE +#define DMA_BUFF_SIZE 4096 + +/*------------------------------------------------------------------------= ---*/ +/* PCI Struct = */ +/*------------------------------------------------------------------------= ---*/ + +typedef struct PCIExampleDevState { + /*< private >*/ + PCIDevice parent_obj; + /*< public >*/ + + /* memory region */ + MemoryRegion portio; + MemoryRegion mmio; + MemoryRegion irqio; + MemoryRegion dmaio; + + /* data registers */ + uint64_t memData, ioData, dmaPhisicalBase; + + qemu_irq irq; + /* for the driver to determine if this device caused the interrupt */ + uint64_t threwIrq; + +} PCIExampleDevState; + + +/*------------------------------------------------------------------------= ---*/ +/* Read/Write functions = */ +/*------------------------------------------------------------------------= ---*/ + +/* do nothing because the mmio read is done from DMA buffer */ +static uint64_t pci_example_mmio_read(void *opaque, hwaddr addr, unsigned = size) +{ + return ERROR; +} + +static void +pci_example_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned s= ize) +{ + PCIExampleDevState *pms =3D (PCIExampleDevState *)opaque; + PCIDevice *pciDev =3D (PCIDevice *)opaque; + + if (size !=3D 1) { + return; + } + + /* compute the result */ + pms->memData =3D val * 2; + + /* write the result directly to phisical memory */ + cpu_physical_memory_write(pms->dmaPhisicalBase, &pms->memData, + DMA_BUFF_SIZE); + + /* raise an IRQ to notify DMA has finished */ + pms->threwIrq =3D 1; + pci_irq_assert(pciDev); +} + +static uint64_t pci_example_pio_read(void *opaque, hwaddr addr, unsigned s= ize) +{ + PCIExampleDevState *pms =3D (PCIExampleDevState *)opaque; + + if (size !=3D 1) { + return ERROR; + } + + return pms->ioData; +} + +static void +pci_example_pio_write(void *opaque, hwaddr addr, uint64_t val, unsigned si= ze) +{ + PCIExampleDevState *pms =3D (PCIExampleDevState *)opaque; + PCIDevice *pciDev =3D (PCIDevice *)opaque; + + if (size !=3D 1) { + return; + } + + pms->ioData =3D val * 2; + pms->threwIrq =3D 1; + pci_irq_assert(pciDev); +} + +static uint64_t pci_example_irqio_read(void *opaque, hwaddr addr, unsigned= size) +{ + PCIExampleDevState *pms =3D (PCIExampleDevState *)opaque; + + if (size !=3D 1) { + return ERROR; + } + + return pms->threwIrq; +} + +static void +pci_example_irqio_write(void *opaque, hwaddr addr, uint64_t val, unsigned = size) +{ + PCIExampleDevState *pms =3D (PCIExampleDevState *)opaque; + PCIDevice *pciDev =3D (PCIDevice *)opaque; + + if (size !=3D 1) { + return; + } + + /* give the ability to assert IRQ , we will use it only to deassert IR= Q */ + if (val) { + pci_irq_assert(pciDev); + pms->threwIrq =3D 1; + } else { + pms->threwIrq =3D 0; + pci_irq_deassert(pciDev); + } +} + +/* do nothing because physical DMA buffer addres is onlyt set and don't ne= ed to + * be red */ +static uint64_t +pci_example_dma_base_read(void *opaque, hwaddr addr, unsigned size) +{ + return ERROR; +} + +static void +pci_example_dma_base_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + PCIExampleDevState *pms =3D (PCIExampleDevState *)opaque; + + if (size !=3D 4) { + return; + } + + /* notify the device about the physical address of the DMA buffer that= the + * driver has allocated */ + switch (addr) { + /* lower bytes */ + case(0): + pms->dmaPhisicalBase &=3D 0xffffffff00000000; + break; + + /* upper bytes */ + case(4): + val <<=3D 32; + pms->dmaPhisicalBase &=3D 0x00000000ffffffff; + break; + } + + pms->dmaPhisicalBase |=3D val; +} + +/*------------------------------------------------------------------------= ---*/ +/* PCI region ops = */ +/*------------------------------------------------------------------------= ---*/ + +/* callback called when memory region representing the MMIO space is acces= sed */ +static const MemoryRegionOps pci_example_mmio_ops =3D { + .read =3D pci_example_mmio_read, + .write =3D pci_example_mmio_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .impl =3D { + .min_access_size =3D 1, + .max_access_size =3D 1, + }, +}; + +/* callback called when memory region representing the PIO space is access= ed */ +static const MemoryRegionOps pci_example_pio_ops =3D { + .read =3D pci_example_pio_read, + .write =3D pci_example_pio_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .impl =3D { + .min_access_size =3D 1, + .max_access_size =3D 1, + }, +}; + +/* callback called when memory region representing the IRQ space is access= ed */ +static const MemoryRegionOps pci_example_irqio_ops =3D { + .read =3D pci_example_irqio_read, + .write =3D pci_example_irqio_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .impl =3D { + .min_access_size =3D 1, + .max_access_size =3D 1, + }, +}; + +/* callback called when memory region representing the DMA space is access= ed */ +static const MemoryRegionOps pci_example_dma_ops =3D { + .read =3D pci_example_dma_base_read, + .write =3D pci_example_dma_base_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .impl =3D { + .min_access_size =3D 4, + .max_access_size =3D 4, + }, +}; + +/*------------------------------------------------------------------------= ---*/ +/* PCI functions = */ +/*------------------------------------------------------------------------= ---*/ + +/* this is called when lunching the vm with "-device " */ +static void pci_example_realize(PCIDevice *pciDev, Error **errp) +{ + PCIExampleDevState *d =3D PCI_EXAMPLE(pciDev); + uint8_t *pciCond =3D pciDev->config; + + d->threwIrq =3D 0; + + /* initiallise the memory region of the CPU to the device */ + memory_region_init_io(&d->mmio, OBJECT(d), &pci_example_mmio_ops, d, + "pci-example-mmio", EXAMPLE_MMIO_SIZE); + + memory_region_init_io(&d->portio, OBJECT(d), &pci_example_pio_ops, d, + "pci-example-portio", EXAMPLE_PIO_SIZE); + + memory_region_init_io(&d->irqio, OBJECT(d), &pci_example_irqio_ops, d, + "pci-example-irqio", EXAMPLE_PIO_SIZE); + + memory_region_init_io(&d->dmaio, OBJECT(d), &pci_example_dma_ops, d, + "pci-example-dma-base", EXAMPLE_MMIO_SIZE); + + /* alocate BARs */ + pci_register_bar(pciDev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); + pci_register_bar(pciDev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->portio); + pci_register_bar(pciDev, 2, PCI_BASE_ADDRESS_SPACE_IO, &d->irqio); + pci_register_bar(pciDev, 3, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->dmaio); + + /* give interrupt support. + * a pci device has 4 pin for interrupt, here we use pin A */ + pci_config_set_interrupt_pin(pciCond, 1); +} + + +/* the destructor of pci_example_realize() */ +static void pci_example_uninit(PCIDevice *dev) +{ + /* unregister BARs and other stuff */ +} + + +/* class constructor */ +static void pci_example_class_init(ObjectClass *klass, void *data) +{ + /* sort of dynamic cast */ + DeviceClass *dc =3D DEVICE_CLASS(klass); + PCIDeviceClass *k =3D PCI_DEVICE_CLASS(klass); + + k->realize =3D pci_example_realize; + k->exit =3D pci_example_uninit; + + /* some regular IDs in HEXA */ + k->vendor_id =3D PCI_VENDOR_ID_REDHAT; + k->device_id =3D PCI_DEVICE_ID_REDHAT_TEST; + k->class_id =3D PCI_CLASS_OTHERS; + + /* set the device bitmap category */ + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + k->revision =3D 0x00; + dc->desc =3D "PCI Example Device"; +} + +/*------------------------------------------------------------------------= ---*/ +/* QEMU overhead = */ +/*------------------------------------------------------------------------= ---*/ + + +/* Contains all the informations of the device we are creating. + * class_init will be called when we are defining our device. */ +static const TypeInfo pci_example_info =3D { + .name =3D TYPE_PCI_EXAMPLE, + .parent =3D TYPE_PCI_DEVICE, + .instance_size =3D sizeof(PCIExampleDevState), + .class_init =3D pci_example_class_init, + .interfaces =3D (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, +}; + + +/* function called before the qemu main it will define our device */ +static void pci_example_register_types(void) +{ + type_register_static(&pci_example_info); +} + +/* make qemu register our device at qemu-booting */ +type_init(pci_example_register_types) + + + --=20 2.17.1