From ab7aa48368664ccee2175bbdd28ddd5d56ad5920 Mon Sep 17 00:00:00 2001 From: GuEe-GUI <2991707448@qq.com> Date: Wed, 22 Jul 2026 22:42:32 +0800 Subject: [PATCH] [dm][ivshmem] Add Inter-VM shared memory for virtualization Signed-off-by: GuEe-GUI <2991707448@qq.com> --- components/drivers/Kconfig | 1 + components/drivers/ivshmem/Kconfig | 10 + components/drivers/ivshmem/SConscript | 18 + components/drivers/ivshmem/ivshmem-qemu.c | 393 ++++++++++++++++++++++ components/drivers/ivshmem/ivshmem.c | 240 +++++++++++++ components/drivers/ivshmem/ivshmem.h | 79 +++++ 6 files changed, 741 insertions(+) create mode 100644 components/drivers/ivshmem/Kconfig create mode 100644 components/drivers/ivshmem/SConscript create mode 100644 components/drivers/ivshmem/ivshmem-qemu.c create mode 100644 components/drivers/ivshmem/ivshmem.c create mode 100644 components/drivers/ivshmem/ivshmem.h diff --git a/components/drivers/Kconfig b/components/drivers/Kconfig index 7ceb0720b49..96586a403e5 100755 --- a/components/drivers/Kconfig +++ b/components/drivers/Kconfig @@ -46,6 +46,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 00000000000..b042022911f --- /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 00000000000..c33709d3495 --- /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 00000000000..9bb90b39e5a --- /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 + +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__) " "; + + rt_kputs("Usage:\n"); + rt_kprintf("%s%s\n", cmd_prefix, "info"); + rt_kprintf("%s%s \n", cmd_prefix, "set-int"); + rt_kprintf("%s%s\n", cmd_prefix, "get-int"); + rt_kprintf("%s%s \n", cmd_prefix, "write"); + rt_kprintf("%s%s \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 00000000000..fc1efa3047c --- /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 +#include + +#define DBG_TAG "rtdm.ivshmem" +#define DBG_LVL DBG_INFO +#include + +#include +#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 00000000000..5b5c9a06b6b --- /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 +#include + +/* 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__ */