summaryrefslogtreecommitdiff
path: root/components/drivers
diff options
context:
space:
mode:
authorGuEe-GUI <[email protected]>2026-07-22 22:42:32 +0800
committerRbb666 <[email protected]>2026-08-06 18:19:52 +0800
commit955fb76bb03ac6dd9c057b9e0826696a320d7d0f (patch)
treed401318b02c2e457c7304c792237273fa32db48c /components/drivers
parent2f7a2299f61b59d5cf00426dac0d248dce9beb4c (diff)
[dm][ivshmem] Add Inter-VM shared memory for virtualization
Signed-off-by: GuEe-GUI <[email protected]>
Diffstat (limited to 'components/drivers')
-rwxr-xr-xcomponents/drivers/Kconfig1
-rw-r--r--components/drivers/ivshmem/Kconfig10
-rw-r--r--components/drivers/ivshmem/SConscript18
-rw-r--r--components/drivers/ivshmem/ivshmem-qemu.c393
-rw-r--r--components/drivers/ivshmem/ivshmem.c240
-rw-r--r--components/drivers/ivshmem/ivshmem.h79
6 files changed, 741 insertions, 0 deletions
diff --git a/components/drivers/Kconfig b/components/drivers/Kconfig
index f412357349..13b516ac66 100755
--- a/components/drivers/Kconfig
+++ b/components/drivers/Kconfig
@@ -48,6 +48,7 @@ rsource "virtio/Kconfig"
rsource "nvmem/Kconfig"
rsource "dma/Kconfig"
rsource "mfd/Kconfig"
+rsource "ivshmem/Kconfig"
rsource "ofw/Kconfig"
rsource "pci/Kconfig"
rsource "pic/Kconfig"
diff --git a/components/drivers/ivshmem/Kconfig b/components/drivers/ivshmem/Kconfig
new file mode 100644
index 0000000000..b042022911
--- /dev/null
+++ b/components/drivers/ivshmem/Kconfig
@@ -0,0 +1,10 @@
+menuconfig RT_USING_IVSHMEM
+ bool "Using Inter-VM shared memory device drivers"
+ depends on RT_USING_DM
+ depends on RT_USING_PCI
+ default n
+
+config RT_IVSHMEM_QEMU
+ bool "QEMU"
+ depends on RT_USING_IVSHMEM
+ default n
diff --git a/components/drivers/ivshmem/SConscript b/components/drivers/ivshmem/SConscript
new file mode 100644
index 0000000000..c33709d349
--- /dev/null
+++ b/components/drivers/ivshmem/SConscript
@@ -0,0 +1,18 @@
+from building import *
+
+group = []
+
+if not GetDepend(['RT_USING_IVSHMEM']):
+ Return('group')
+
+cwd = GetCurrentDir()
+CPPPATH = [cwd + '/../include']
+
+src = ['ivshmem.c']
+
+if GetDepend(['RT_IVSHMEM_QEMU']):
+ src += ['ivshmem-qemu.c']
+
+group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
+
+Return('group')
diff --git a/components/drivers/ivshmem/ivshmem-qemu.c b/components/drivers/ivshmem/ivshmem-qemu.c
new file mode 100644
index 0000000000..9bb90b39e5
--- /dev/null
+++ b/components/drivers/ivshmem/ivshmem-qemu.c
@@ -0,0 +1,393 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2022-10-27 GuEe-GUI first version
+ */
+
+#include "ivshmem.h"
+
+struct qemu_ivshmem
+{
+ struct ivshmem_device parent;
+
+ rt_bool_t is_msx;
+ rt_uint32_t irq_status;
+ rt_event_t irq_event;
+ struct rt_spinlock rw_lock;
+};
+
+#define raw_to_qemu_ivshmem(raw) rt_container_of(raw, struct qemu_ivshmem, parent)
+
+static rt_bool_t rw_count_fixup(struct ivshmem_device *ivdev, rt_off_t pos, rt_size_t *count)
+{
+ if (pos < ivdev->shmem_size)
+ {
+ if (pos + *count > ivdev->shmem_size)
+ {
+ *count = ivdev->shmem_size - pos;
+ }
+
+ return RT_TRUE;
+ }
+ else
+ {
+ *count = 0;
+ }
+
+ return RT_FALSE;
+}
+
+static rt_ssize_t qemu_ivshmem_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t count)
+{
+ struct ivshmem_device *ivdev = &raw_to_qemu_ivshmem(dev)->parent;
+
+ if (rw_count_fixup(ivdev, pos, &count))
+ {
+ rt_memcpy(buffer, (void *)(ivdev->shmem + pos), count);
+ }
+
+ return count;
+}
+
+static rt_ssize_t qemu_ivshmem_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t count)
+{
+ struct ivshmem_device *ivdev = &raw_to_qemu_ivshmem(dev)->parent;
+
+ if (rw_count_fixup(ivdev, pos, &count))
+ {
+ rt_memcpy((void *)(ivdev->shmem + pos), buffer, count);
+ }
+
+ return count;
+}
+
+static rt_err_t qemu_ivshmem_control(rt_device_t dev, int cmd, void *args)
+{
+ rt_err_t err = RT_EOK;
+ struct qemu_ivshmem *qemu_ivshmem = raw_to_qemu_ivshmem(dev);
+ struct ivshmem_device *ivdev = &qemu_ivshmem->parent;
+
+ if (!args && cmd != RT_DEVICE_CTRL_CLR_INT)
+ {
+ cmd = 0;
+ }
+
+ switch (cmd)
+ {
+ case RT_DEVICE_CTRL_CONFIG:
+ {
+ struct ivshmem_cmd *user_cmd = args;
+
+ if (user_cmd->is_read)
+ {
+ user_cmd->value = HWREG32(ivdev->reg + user_cmd->reg_off);
+ }
+ else
+ {
+ HWREG32(ivdev->reg + user_cmd->reg_off) = user_cmd->value;
+ }
+
+ break;
+ }
+ case RT_DEVICE_CTRL_SET_INT:
+ {
+ rt_ubase_t level = rt_spin_lock_irqsave(&qemu_ivshmem->rw_lock);
+
+ qemu_ivshmem->irq_event = args;
+
+ rt_spin_unlock_irqrestore(&qemu_ivshmem->rw_lock, level);
+
+ break;
+ }
+ case RT_DEVICE_CTRL_CLR_INT:
+ {
+ rt_ubase_t level = rt_spin_lock_irqsave(&qemu_ivshmem->rw_lock);
+
+ qemu_ivshmem->irq_event = RT_NULL;
+
+ rt_spin_unlock_irqrestore(&qemu_ivshmem->rw_lock, level);
+
+ break;
+ }
+ case RT_DEVICE_CTRL_GET_INT:
+ {
+ rt_ubase_t level = rt_spin_lock_irqsave(&qemu_ivshmem->rw_lock);
+
+ *((rt_uint32_t *)args) = qemu_ivshmem->irq_status;
+ /* Only read once */
+ qemu_ivshmem->irq_status = 0;
+
+ rt_spin_unlock_irqrestore(&qemu_ivshmem->rw_lock, level);
+
+ break;
+ }
+ default:
+ err = -RT_EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops qemu_ivshmem_ops =
+{
+ .read = qemu_ivshmem_read,
+ .write = qemu_ivshmem_write,
+ .control = qemu_ivshmem_control
+};
+#endif
+
+static rt_err_t qemu_ivshmem_isr(struct ivshmem_device *ivdev, int irq)
+{
+ struct qemu_ivshmem *qemu_ivshmem = raw_to_qemu_ivshmem(ivdev);
+
+ rt_spin_lock(&qemu_ivshmem->rw_lock);
+
+ if (qemu_ivshmem->is_msx)
+ {
+ qemu_ivshmem->irq_status |= 1 << (irq - qemu_ivshmem->parent.msix_entries[0].irq);
+ }
+ else
+ {
+ qemu_ivshmem->irq_status = RT_UINT32_MAX;
+ }
+
+ if (qemu_ivshmem->irq_event)
+ {
+ rt_event_send(qemu_ivshmem->irq_event, qemu_ivshmem->irq_status);
+ }
+
+ rt_spin_unlock(&qemu_ivshmem->rw_lock);
+
+ return RT_EOK;
+}
+
+static rt_err_t qemu_ivshmem_probe(struct rt_pci_device *pdev)
+{
+ rt_err_t err = RT_EOK;
+ const char *dev_name;
+ struct rt_device *parent;
+ struct qemu_ivshmem *qivdev = rt_calloc(1, sizeof(*qivdev));
+
+ if (!qivdev)
+ {
+ return -RT_ENOMEM;
+ }
+
+ if ((err = ivshmem_pci_probe(pdev, &qivdev->parent)))
+ {
+ goto _fail;
+ }
+
+ parent = &qivdev->parent.parent;
+
+ rt_dm_dev_set_name_auto(parent, "ivshmem");
+ dev_name = rt_dm_dev_get_name(parent);
+
+ qivdev->parent.handle_irq = qemu_ivshmem_isr;
+ qivdev->irq_event = RT_NULL;
+ rt_spin_lock_init(&qivdev->rw_lock);
+
+ /* rt_event_t supports only 32 events */
+ if (!ivshmem_install_msix_vectors(&qivdev->parent, 32, dev_name))
+ {
+ qivdev->is_msx = RT_TRUE;
+ }
+ else
+ {
+ qivdev->is_msx = RT_FALSE;
+ ivshmem_install_intx_vector(&qivdev->parent, dev_name);
+ }
+
+ parent->type = RT_Device_Class_MTD;
+#ifdef RT_USING_DEVICE_OPS
+ parent->ops = &qemu_ivshmem_ops;
+#else
+ parent->read = qemu_ivshmem_read;
+ parent->write = qemu_ivshmem_write;
+ parent->control = qemu_ivshmem_control;
+#endif
+
+ rt_device_register(parent, dev_name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE);
+
+ return RT_EOK;
+
+_fail:
+ rt_free(qivdev);
+
+ return err;
+}
+
+static rt_err_t qemu_ivshmem_remove(struct rt_pci_device *pdev)
+{
+ struct ivshmem_device *ivdev = ivshmem_pci_remove(pdev);
+ struct qemu_ivshmem *qivdev = raw_to_qemu_ivshmem(ivdev);
+
+ rt_device_unregister(&qivdev->parent.parent);
+ rt_free(qivdev);
+
+ return RT_EOK;
+}
+
+static const struct rt_pci_device_id qemu_ivshmem_pci_ids[] =
+{
+ {
+ RT_PCI_DEVICE_ID(PCI_VENDOR_ID_REDHAT_QUMRANET, 0x1110),
+ .class = PCIC_MEMORY << 16,
+ .class_mask = 0xff << 16,
+ },
+ { /* sentinel */ }
+};
+
+static struct rt_pci_driver qemu_ivshmem_driver =
+{
+ .name = "ivshmem-qemu",
+
+ .ids = qemu_ivshmem_pci_ids,
+ .probe = qemu_ivshmem_probe,
+ .remove = qemu_ivshmem_remove,
+};
+RT_PCI_DRIVER_EXPORT(qemu_ivshmem_driver);
+
+#if defined(RT_USING_CONSOLE) && defined(RT_USING_MSH)
+#include <stdlib.h>
+
+static int ivshmem_qemu(int argc, char**argv)
+{
+ rt_err_t err = -RT_EINVAL, res;
+ const char *dev_name, *opt;
+ struct ivshmem_cmd cmd;
+ struct rt_device *ivdev;
+
+ if (argc < 3)
+ {
+ goto _help;
+ }
+
+ dev_name = argv[1];
+ ivdev = rt_device_find(dev_name);
+
+ if (!ivdev)
+ {
+ rt_kprintf("dev '%s' not found\n", dev_name);
+ goto _help;
+ }
+
+ if (rt_device_open(ivdev, 0))
+ {
+ rt_kprintf("dev '%s' open fail\n", dev_name);
+ goto _help;
+ }
+
+ err = RT_EOK;
+ opt = argv[2];
+
+ if (!rt_strcmp(opt, "info"))
+ {
+ cmd.is_read = RT_TRUE;
+ cmd.reg_off = IVSHMEM_IV_POSITION;
+
+ res = rt_device_control(ivdev, RT_DEVICE_CTRL_CONFIG, &cmd);
+
+ if (!res)
+ {
+ rt_kprintf("%s ivposition = %d\n", dev_name, cmd.value);
+ }
+
+ goto _end;
+ }
+
+ if (!rt_strcmp(opt, "set-int") && argc == 5)
+ {
+ cmd.is_read = RT_FALSE;
+ cmd.reg_off = IVSHMEM_DOORBELL;
+ cmd.value = ivshmem_doorbell(atol(argv[3]), atol(argv[4]));
+
+ res = rt_device_control(ivdev, RT_DEVICE_CTRL_CONFIG, &cmd);
+
+ if (!res)
+ {
+ rt_kprintf("%s set doorbell OK\n", dev_name);
+ }
+
+ goto _end;
+ }
+
+ if (!rt_strcmp(opt, "get-int"))
+ {
+ rt_event_t ev = rt_event_create("ivshmem", RT_IPC_FLAG_PRIO);
+
+ if (!ev)
+ {
+ rt_kprintf("create event fail\n");
+ }
+
+ res = rt_device_control(ivdev, RT_DEVICE_CTRL_SET_INT, ev);
+
+ if (!res)
+ {
+ rt_uint32_t irq_status;
+
+ if (!rt_event_recv(ev, RT_UINT32_MAX, RT_EVENT_FLAG_OR, RT_WAITING_FOREVER, &irq_status))
+ {
+ rt_kprintf("irq status = 0x%x\n", irq_status);
+ }
+
+ rt_device_control(ivdev, RT_DEVICE_CTRL_CLR_INT, RT_NULL);
+ }
+
+ rt_event_delete(ev);
+
+ goto _end;
+ }
+
+ if (!rt_strcmp(opt, "write") && argc == 5)
+ {
+ rt_device_write(ivdev, atol(argv[3]), argv[4], rt_strlen(argv[4]));
+
+ goto _end;
+ }
+
+ if (!rt_strcmp(opt, "read") && argc == 5)
+ {
+ long pos = atol(argv[3]);
+ long size = atol(argv[4]);
+
+ while (size --> 0)
+ {
+ rt_uint8_t value;
+ rt_device_read(ivdev, pos, &value, 1);
+
+ rt_kprintf("[%2d] = %02x (%c)\n", pos, value, value);
+
+ ++pos;
+ }
+
+ goto _end;
+ }
+
+ err = -RT_EINVAL;
+
+_help:
+ if (err)
+ {
+ const char *cmd_prefix = "\t " RT_STRINGIFY(__FUNCTION__) " <dev> ";
+
+ rt_kputs("Usage:\n");
+ rt_kprintf("%s%s\n", cmd_prefix, "info");
+ rt_kprintf("%s%s <peer-id> <vector-index>\n", cmd_prefix, "set-int");
+ rt_kprintf("%s%s\n", cmd_prefix, "get-int");
+ rt_kprintf("%s%s <pos> <value>\n", cmd_prefix, "write");
+ rt_kprintf("%s%s <pos> <size>\n", cmd_prefix, "read");
+ }
+
+_end:
+ return err;
+}
+MSH_CMD_EXPORT(ivshmem_qemu, ivshmem qemu device command);
+#endif /* RT_USING_CONSOLE && RT_USING_MSH */
diff --git a/components/drivers/ivshmem/ivshmem.c b/components/drivers/ivshmem/ivshmem.c
new file mode 100644
index 0000000000..fc1efa3047
--- /dev/null
+++ b/components/drivers/ivshmem/ivshmem.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2022-10-27 GuEe-GUI first version
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#define DBG_TAG "rtdm.ivshmem"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include <cpuport.h>
+#include "ivshmem.h"
+
+static void ivshmem_isr(int irqno, void *param)
+{
+ rt_uint32_t status;
+ struct ivshmem_device *ivdev = param;
+
+ status = HWREG32(ivdev->reg + IVSHMEM_INTR_STATUS);
+
+ if ((status && status != 0xffffffff) || ivdev->nvectors)
+ {
+ if (ivdev->handle_irq)
+ {
+ ivdev->handle_irq(ivdev, ivdev->nvectors ? irqno : status);
+ }
+ else
+ {
+ LOG_E("IVSHMEM<%p> have a irq = %d, by no have irq handers", ivdev, irqno);
+ }
+ }
+}
+
+rt_err_t ivshmem_install_msix_vectors(struct ivshmem_device *ivdev, int nvectors, const char *name)
+{
+ rt_err_t err = RT_EOK;
+ char irq_name[RT_NAME_MAX];
+
+ if (!ivdev || !name)
+ {
+ return -RT_EINVAL;
+ }
+
+ if (ivdev->nvectors != 0)
+ {
+ LOG_E("IVSHMEM<%p> '%s' have already install msix", ivdev, name);
+
+ return -RT_ERROR;
+ }
+
+ if (!ivdev->handle_irq)
+ {
+ LOG_E("IVSHMEM<%p> '%s' no have irq handers", ivdev, name);
+
+ return -RT_EINVAL;
+ }
+
+ ivdev->msix_entries = rt_malloc(nvectors * sizeof(*ivdev->msix_entries));
+
+ if (!ivdev->msix_entries)
+ {
+ return -RT_ENOMEM;
+ }
+
+ ivdev->nvectors = nvectors;
+
+ rt_pci_msix_entry_index_linear(ivdev->msix_entries, nvectors);
+
+ if ((err = rt_pci_msix_enable(ivdev->pdev, ivdev->msix_entries, ivdev->nvectors)))
+ {
+ rt_free(ivdev->msix_entries);
+ ivdev->msix_entries = RT_NULL;
+ ivdev->nvectors = 0;
+
+ return err;
+ }
+
+ for (int i = 0; i < nvectors; i++)
+ {
+ int irq = ivdev->msix_entries[i].irq;
+
+ rt_snprintf(irq_name, RT_NAME_MAX, "%s%d", name, irq);
+
+ rt_hw_interrupt_install(irq, ivshmem_isr, ivdev, irq_name);
+ rt_hw_interrupt_umask(irq);
+ }
+
+ return err;
+}
+
+rt_err_t ivshmem_install_intx_vector(struct ivshmem_device *ivdev, const char *name)
+{
+ rt_err_t err = RT_EOK;
+
+ if (!ivdev || !name)
+ {
+ return -RT_EINVAL;
+ }
+
+ if (ivdev->nvectors != 0)
+ {
+ LOG_E("IVSHMEM<%p> '%s' have already install msix", ivdev, name);
+
+ return -RT_ERROR;
+ }
+
+ if (!ivdev->handle_irq)
+ {
+ LOG_E("IVSHMEM<%p> '%s' no have irq handers", ivdev, name);
+
+ return -RT_EINVAL;
+ }
+
+ ivdev->nvectors = 0;
+ ivdev->msix_entries = RT_NULL;
+
+ rt_hw_interrupt_install(ivdev->irq, ivshmem_isr, ivdev, name);
+ rt_pci_irq_unmask(ivdev->pdev);
+
+ return err;
+}
+
+static void ivshmem_free_resource(struct ivshmem_device *ivdev)
+{
+ if (ivdev->pdev)
+ {
+ ivdev->pdev->sysdata = RT_NULL;
+ }
+
+ if (ivdev->reg)
+ {
+ rt_iounmap(ivdev->reg);
+ }
+
+ if (ivdev->shmem)
+ {
+ rt_iounmap(ivdev->shmem);
+ }
+}
+
+rt_err_t ivshmem_pci_probe(struct rt_pci_device *pdev, struct ivshmem_device *ivdev)
+{
+ rt_err_t err = RT_EOK;
+
+ if (!ivdev || !pdev || pdev->irq < 0)
+ {
+ return -RT_EINVAL;
+ }
+
+ rt_memset(ivdev, 0, sizeof(*ivdev));
+
+ ivdev->pdev = pdev;
+ ivdev->irq = pdev->irq;
+
+ pdev->sysdata = ivdev;
+
+ ivdev->reg = rt_pci_iomap(pdev, IVSHMEM_REG_BAR);
+
+ if (!ivdev->reg)
+ {
+ err = -RT_EIO;
+ goto _fail;
+ }
+
+ /* Set all masks to on */
+ HWREG32(ivdev->reg + IVSHMEM_INTR_MASK) = 0xffffffff;
+
+ ivdev->shmem_size = pdev->resource[IVSHMEM_SHARE_MEM_BAR].size;
+ ivdev->shmem = (void *)pdev->resource[IVSHMEM_SHARE_MEM_BAR].base;
+
+ if (!ivdev->shmem_size)
+ {
+ rt_pci_read_config_u32(pdev, PCI_CFG_SHARE_MEM_ADDR_LO, (rt_uint32_t *)&ivdev->shmem);
+ rt_pci_read_config_u32(pdev, PCI_CFG_SHARE_MEM_ADDR_HI, (rt_uint32_t *)&ivdev->shmem + 1);
+ rt_pci_read_config_u32(pdev, PCI_CFG_SHARE_MEM_SIZE_LO, (rt_uint32_t *)&ivdev->shmem_size);
+ rt_pci_read_config_u32(pdev, PCI_CFG_SHARE_MEM_SIZE_HI, (rt_uint32_t *)&ivdev->shmem_size + 1);
+ }
+
+ ivdev->shmem = rt_ioremap(ivdev->shmem, ivdev->shmem_size);
+
+ if (!ivdev->shmem)
+ {
+ err = -RT_ERROR;
+ goto _fail;
+ }
+
+ return RT_EOK;
+
+_fail:
+ ivshmem_free_resource(ivdev);
+
+ return err;
+}
+
+struct ivshmem_device *ivshmem_pci_remove(struct rt_pci_device *pdev)
+{
+ struct ivshmem_device *ivdev = RT_NULL;
+
+ if (!pdev || !pdev->sysdata)
+ {
+ return ivdev;
+ }
+
+ ivdev = pdev->sysdata;
+
+ if (ivdev->msix_entries)
+ {
+ for (int i = 0; i < ivdev->nvectors; ++i)
+ {
+ int irq = ivdev->msix_entries[i].irq;
+
+ rt_hw_interrupt_umask(irq);
+ rt_pic_detach_irq(irq, ivdev);
+ }
+ }
+
+ rt_hw_interrupt_umask(ivdev->irq);
+ rt_pic_detach_irq(ivdev->irq, ivdev);
+
+ if (ivdev->nvectors)
+ {
+ rt_pci_msix_disable(ivdev->pdev);
+ rt_free(ivdev->msix_entries);
+ }
+ else
+ {
+ rt_pci_irq_unmask(ivdev->pdev);
+ }
+
+ ivshmem_free_resource(ivdev);
+
+ return ivdev;
+}
diff --git a/components/drivers/ivshmem/ivshmem.h b/components/drivers/ivshmem/ivshmem.h
new file mode 100644
index 0000000000..5b5c9a06b6
--- /dev/null
+++ b/components/drivers/ivshmem/ivshmem.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2022-10-27 GuEe-GUI first version
+ */
+
+#ifndef __IVSHMEM_H__
+#define __IVSHMEM_H__
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+/* Registers */
+#define IVSHMEM_INTR_MASK 0x00
+#define IVSHMEM_INTR_STATUS 0x04
+#define IVSHMEM_IV_POSITION 0x08
+#define IVSHMEM_DOORBELL 0x0c
+#define IVSHMEM_V2S_ID 0x10
+#define IVSHMEM_V2S_MAX_PEERS 0x14
+#define IVSHMEM_V2S_INT_CTRL 0x18
+#define IVSHMEM_V2S_DBELL 0x1c
+#define IVSHMEM_V2S_STATE 0x20
+#define IVSHMEM_V2S_STATE_TBL 0x24
+
+/* BAR */
+#define IVSHMEM_REG_BAR 0
+#define IVSHMEM_MSIX_BAR 1
+#define IVSHMEM_SHARE_MEM_BAR 2
+
+#define IVSHMEM_MAX_VECTORS 255
+
+/* JailHouse support */
+#define PCI_CFG_SHARE_MEM_ADDR_LO 0x40
+#define PCI_CFG_SHARE_MEM_ADDR_HI 0x44
+#define PCI_CFG_SHARE_MEM_SIZE_LO 0x48
+#define PCI_CFG_SHARE_MEM_SIZE_HI 0x4c
+
+struct ivshmem_cmd
+{
+ rt_bool_t is_read;
+ rt_uint32_t reg_off;
+ rt_uint32_t value;
+};
+
+struct ivshmem_device
+{
+ struct rt_device parent;
+ struct rt_pci_device *pdev;
+
+ void *reg;
+ int irq;
+
+ void *shmem;
+ rt_size_t shmem_size;
+
+ int nvectors;
+ struct rt_pci_msix_entry *msix_entries;
+
+ rt_err_t (*handle_irq)(struct ivshmem_device *, int irq);
+};
+
+#define to_ivshmem(dev) rt_container_of(dev, struct ivshmem_device, parent)
+
+rt_inline rt_uint32_t ivshmem_doorbell(int peer_id, int vector_index)
+{
+ return (rt_uint32_t)((peer_id << 16) | (vector_index & IVSHMEM_MAX_VECTORS));
+}
+
+rt_err_t ivshmem_install_msix_vectors(struct ivshmem_device *ivdev, int nvectors, const char *name);
+rt_err_t ivshmem_install_intx_vector(struct ivshmem_device *ivdev, const char *name);
+
+rt_err_t ivshmem_pci_probe(struct rt_pci_device *pdev, struct ivshmem_device *ivdev);
+struct ivshmem_device *ivshmem_pci_remove(struct rt_pci_device *pdev);
+
+#endif /* __IVSHMEM_H__ */