diff --git a/devicemodel/hw/pci/ivshmem.c b/devicemodel/hw/pci/ivshmem.c new file mode 100644 index 000000000..09cce9e33 --- /dev/null +++ b/devicemodel/hw/pci/ivshmem.c @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +/* + * ACRN Inter-VM Virtualizaiton based on ivshmem-v1 device + * + * +----------+ +-----------------------------------------+ +----------+ + * |Postlaunch| | Service OS | |Postlaunch| + * | VM | | | | VM | + * | | | Interrupt | | | + * |+--------+| |+----------+ Foward +----------+| |+--------+| + * || App || || acrn-dm | +-------+ | acrn-dm || || App || + * || || ||+--------+| |ivshmem| |+--------+|| || || + * |+---+----+| |||ivshmem ||<---+server +--->||ivshmem ||| |+---+----+| + * | | | +-+++ dm || +-------+ || dm +++-+ | | | + * | | | | ||+---+----+| |+----+---+|| | | | | + * |+---+----+| | |+----^-----+ +-----^----+| | |+---+----+| + * ||UIO || | | +---------------+-------------+ | | ||UIO || + * ||driver || | | v | | ||driver || + * |+---+----+| | | +--------+-------+ | | |+---+----+| + * | | | | | | /dev/shm | | | | | | + * | | | | | +--------+-------+ | | | | | + * |+---+----+| | | | | | |+---+----+| + * ||ivshmem || | | +--------+-------+ | | ||ivshmem || + * ||device || | | | Shared Memory | | | ||device || + * |+---+----+| | | +----------------+ | | |+---+----+| + * +----+-----+ | +-----------------------------------------+ | +----+-----+ + * +--------+ +-------+ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pci_core.h" +#include "vmmapi.h" +#include "dm_string.h" +#include "log.h" + +#define IVSHMEM_MMIO_BAR 0 +#define IVSHMEM_MEM_BAR 2 + +#define IVSHMEM_VENDOR_ID 0x1af4 +#define IVSHMEM_DEVICE_ID 0x1110 +#define IVSHMEM_CLASS 0x05 +#define IVSHMEM_REV 0x01 + + +/* IVSHMEM MMIO Registers */ +#define IVSHMEM_REG_SIZE 0x100 +#define IVSHMEM_IRQ_MASK_REG 0x00 +#define IVSHMEM_IRQ_STA_REG 0x04 +#define IVSHMEM_IV_POS_REG 0x08 +#define IVSHMEM_DOORBELL_REG 0x0c +#define IVSHMEM_RESERVED_REG 0x0f + +static void +pci_ivshmem_write(struct vmctx *ctx, int vcpu, struct pci_vdev *dev, + int baridx, uint64_t offset, int size, uint64_t value) +{ + pr_dbg("%s: baridx %d, offset = %lx, value = 0x%lx\n", + __func__, baridx, offset, value); + + if (baridx == IVSHMEM_MMIO_BAR) { + switch (offset) { + /* + * Following registers are used to support + * notification/interrupt in future. + */ + case IVSHMEM_IRQ_MASK_REG: + case IVSHMEM_IRQ_STA_REG: + break; + case IVSHMEM_DOORBELL_REG: + pr_warn("Doorbell capability doesn't support for now, ignore vectors 0x%lx, peer id %lu\n", + value & 0xff, ((value >> 16) & 0xff)); + break; + default: + pr_dbg("%s: invalid device register 0x%lx\n", + __func__, offset); + break; + } + } +} + +uint64_t +pci_ivshmem_read(struct vmctx *ctx, int vcpu, struct pci_vdev *dev, + int baridx, uint64_t offset, int size) +{ + uint64_t val = ~0; + + pr_dbg("%s: baridx %d, offset = 0x%lx, size = 0x%x\n", + __func__, baridx, offset, size); + + if (baridx == IVSHMEM_MMIO_BAR) { + switch (offset) { + /* + * Following registers are used to support + * notification/interrupt in future. + */ + case IVSHMEM_IRQ_MASK_REG: + case IVSHMEM_IRQ_STA_REG: + val = 0; + break; + /* + * If ivshmem device doesn't support interrupt, + * The IVPosition is zero. otherwise, it is Peer ID. + */ + case IVSHMEM_IV_POS_REG: + val = 0; + break; + default: + pr_dbg("%s: invalid device register 0x%lx\n", + __func__, offset); + break; + } + } + + switch (size) { + case 1: + val &= 0xFF; + break; + case 2: + val &= 0xFFFF; + break; + case 4: + val &= 0xFFFFFFFF; + break; + } + + return val; +} + +static int +pci_ivshmem_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) +{ + uint32_t size; + char *tmp, *name, *orig; + + /* ivshmem device usage: "-s N,ivshmem,shm_name,shm_size" */ + tmp = orig = strdup(opts); + if (!orig) { + pr_warn("No memory for strdup\n"); + goto err; + } + name = strsep(&tmp, ","); + if (!name) { + pr_warn("the shared memory size is not set\n"); + goto err; + } + if (dm_strtoui(tmp, &tmp, 10, &size) != 0) { + pr_warn("the shared memory size is incorrect, %s\n", tmp); + goto err; + } + if (size < 4096 || size > 128 * 1024 * 1024 || + (size & (size - 1)) != 0) { + pr_warn("invalid shared memory size %u, the size range is [4K,128M] bytes and value must be a power of 2\n", + size); + goto err; + } + + /* initialize config space */ + pci_set_cfgdata16(dev, PCIR_VENDOR, IVSHMEM_VENDOR_ID); + pci_set_cfgdata16(dev, PCIR_DEVICE, IVSHMEM_DEVICE_ID); + pci_set_cfgdata16(dev, PCIR_REVID, IVSHMEM_REV); + pci_set_cfgdata8(dev, PCIR_CLASS, IVSHMEM_CLASS); + + pci_emul_alloc_bar(dev, IVSHMEM_MMIO_BAR, PCIBAR_MEM32, IVSHMEM_REG_SIZE); + pci_emul_alloc_bar(dev, IVSHMEM_MEM_BAR, PCIBAR_MEM64, size); + + free(orig); + return 0; +err: + if (orig) + free(orig); + return -1; +} + +static void +pci_ivshmem_deinit(struct vmctx *ctx, struct pci_vdev *dev, char *opts) +{ +} + +struct pci_vdev_ops pci_ops_ivshmem = { + .class_name = "ivshmem", + .vdev_init = pci_ivshmem_init, + .vdev_deinit = pci_ivshmem_deinit, + .vdev_barwrite = pci_ivshmem_write, + .vdev_barread = pci_ivshmem_read +}; +DEFINE_PCI_DEVTYPE(pci_ops_ivshmem);