drivers/virtualization: Adding ivshmem driver

This is placed into drivers/virtualization as it does not belong to any
existing subsystem.

This is only the ivshmem-plain variant.

This device is provided by qemu or ACRN, and can be used to share memory
either between the host and the VM or between VMs. Here if zephyr is
used as a VM, it will be able to take advantage of such feature.

Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
This commit is contained in:
Tomasz Bursztyka 2020-10-23 12:01:18 +02:00 committed by Anas Nashif
commit d11f2f184d
7 changed files with 249 additions and 0 deletions

View file

@ -0,0 +1,5 @@
# SPDX-License-Identifier: Apache-2.0
zephyr_library()
zephyr_library_sources_ifdef(CONFIG_IVSHMEM virt_ivshmem.c)

View file

@ -0,0 +1,35 @@
# Virtualization drivers configuration options
# Copyright (c) 2015-2020 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
menuconfig VIRTUALIZATION
bool "Virtualization guests drivers"
help
This contains various drivers meant to support and expose features
when Zephyr is running as a guest in a virtualized or emulated
environment.
if VIRTUALIZATION
config IVSHMEM
bool "Inter-VM shared memory device (ivshmem)"
depends on PCIE
help
This will enable support of qemu's ivshmem device, which is also
present in ACRN hypervizor, and lets VM sharing memory with each
other.
if IVSHMEM
config IVSHMEM_DEV_NAME
string
default "IVSHMEM"
module = IVSHMEM
module-str = ivshmem
source "subsys/logging/Kconfig.template.log_config"
endif # IVSHMEM
endif # VIRTUALIZATION

View file

@ -0,0 +1,133 @@
/*
* Copyright (c) 2020 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define LOG_LEVEL CONFIG_IVSHMEM_LOG_LEVEL
#include <logging/log.h>
LOG_MODULE_REGISTER(ivshmem);
#include <errno.h>
#include <kernel.h>
#include <arch/cpu.h>
#include <soc.h>
#include <device.h>
#include <init.h>
#include <drivers/pcie/pcie.h>
#include <drivers/virtualization/ivshmem.h>
#include "virt_ivshmem.h"
static bool ivshmem_check_on_bdf(pcie_bdf_t bdf)
{
uint32_t data;
data = pcie_conf_read(bdf, PCIE_CONF_ID);
if ((data != PCIE_ID_NONE) &&
(PCIE_ID_TO_VEND(data) == IVSHMEM_VENDOR_ID) &&
(PCIE_ID_TO_DEV(data) == IVSHMEM_DEVICE_ID)) {
return true;
}
return false;
}
/* Ivshmem's BDF is not a static value that we could get from DTS,
* since the same image could run on qemu or ACRN which could set
* a different one. So instead, let's find it at runtime.
*/
static pcie_bdf_t ivshmem_bdf_lookup(void)
{
int bus, dev, func;
for (bus = 0; bus <= MAX_BUS; bus++) {
for (dev = 0; dev <= MAX_DEV; ++dev) {
for (func = 0; func <= MAX_FUNC; ++func) {
pcie_bdf_t bdf = PCIE_BDF(bus, dev, func);
if (ivshmem_check_on_bdf(bdf)) {
return bdf;
}
}
}
}
return 0;
}
static bool ivshmem_configure(const struct device *dev)
{
struct ivshmem *data = dev->data;
struct pcie_mbar mbar_regs, mbar_mem;
if (!pcie_get_mbar(data->bdf, IVSHMEM_PCIE_REG_BAR_IDX, &mbar_regs)) {
LOG_ERR("ivshmem regs bar not found");
return false;
}
pcie_set_cmd(data->bdf, PCIE_CONF_CMDSTAT_MEM, true);
device_map(DEVICE_MMIO_RAM_PTR(dev), mbar_regs.phys_addr,
mbar_regs.size, K_MEM_CACHE_NONE);
if (!pcie_get_mbar(data->bdf, IVSHMEM_PCIE_SHMEM_BAR_IDX, &mbar_mem)) {
LOG_ERR("ivshmem mem bar not found");
return false;
}
data->size = mbar_mem.size;
z_phys_map((uint8_t **)&data->shmem,
mbar_mem.phys_addr, data->size,
K_MEM_CACHE_WB | K_MEM_PERM_RW);
LOG_DBG("ivshmem configured:");
LOG_DBG("- Registers at 0x%lx (mapped to 0x%lx)",
mbar_regs.phys_addr, DEVICE_MMIO_GET(dev));
LOG_DBG("- Shared memory of %lu bytes at 0x%lx (mapped to 0x%lx)",
data->size, mbar_mem.phys_addr, data->shmem);
return true;
}
static size_t ivshmem_api_get_mem(const struct device *dev,
uintptr_t *memmap)
{
struct ivshmem *data = dev->data;
*memmap = data->shmem;
return data->size;
}
static const struct ivshmem_driver_api ivshmem_api = {
.get_mem = ivshmem_api_get_mem,
};
static int ivshmem_init(const struct device *dev)
{
struct ivshmem *data = dev->data;
data->bdf = ivshmem_bdf_lookup();
if (data->bdf == 0) {
LOG_WRN("ivshmem device not found");
return -ENOTSUP;
}
LOG_DBG("ivshmem found at bdf 0x%x", data->bdf);
if (!ivshmem_configure(dev)) {
return -EIO;
}
return 0;
}
static struct ivshmem ivshmem_data;
DEVICE_DEFINE(ivshmem, CONFIG_IVSHMEM_DEV_NAME,
ivshmem_init, device_pm_control_nop, &ivshmem_data, NULL,
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &ivshmem_api);

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2020 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_DRIVERS_VIRTUALIZATION_VIRT_IVSHMEM_H_
#define ZEPHYR_DRIVERS_VIRTUALIZATION_VIRT_IVSHMEM_H_
#define IVSHMEM_VENDOR_ID 0x1AF4
#define IVSHMEM_DEVICE_ID 0x1110
#define IVSHMEM_PCIE_REG_BAR_IDX 0
#define IVSHMEM_PCIE_SHMEM_BAR_IDX 1
#define MAX_BUS (0xFFFFFFFF & PCIE_BDF_BUS_MASK)
#define MAX_DEV (0xFFFFFFFF & PCIE_BDF_DEV_MASK)
#define MAX_FUNC (0xFFFFFFFF & PCIE_BDF_FUNC_MASK)
struct ivshmem {
DEVICE_MMIO_RAM;
pcie_bdf_t bdf;
uintptr_t shmem;
size_t size;
};
#endif /* ZEPHYR_DRIVERS_VIRTUALIZATION_VIRT_IVSHMEM_H_ */