Introduce ACRN Inter-VM Virtualization
Support shared memory based inter-vm communication for ACRN post-launched VMs. The ivshmem mediator emulates one standard PCI device which expose the shared memory region through its BAR2. The shared memory region is allocated through Linux shm mechanism and post-launched VM needs to specify the shm name and size as the acrn-dm parameter, the VMs have same shm name parameter can communicate over the shared memory. For first stage, only support shared memory. Consider to support notification(interrupt) in future. To add a ivshmem device in device model, the usage as below -s N,ivshmem,shm_name,shm_size v2: Implement the ivshmem MMIO registers emulation v3: Refine code style Add ivshmem device usage Refine MMIO register return value v4: Refine comments Use logger interfaces to print logs Tracked-On: #4853 Signed-off-by: Yuan Liu <yuan1.liu@intel.com> Acked-by: Yu Wang <yu1.wang@intel.com>
This commit is contained in:
parent
884e9fc911
commit
cb816f7f65
|
@ -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 <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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);
|
Loading…
Reference in New Issue