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:
Yuan Liu 2020-05-24 20:49:04 +08:00 committed by wenlingz
parent 884e9fc911
commit cb816f7f65
1 changed files with 198 additions and 0 deletions

View File

@ -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);