From 4ed404a27f760db364ef3f01cd3a6cc92760b2c4 Mon Sep 17 00:00:00 2001 From: Grant Ramsay Date: Sun, 30 Apr 2023 14:34:35 +1200 Subject: [PATCH] drivers: virtualization: Add interface for ivshmem-v2 ivshmem-v2 is primarily used for IPC in the Jailhouse hypervisor Signed-off-by: Grant Ramsay --- doc/services/virtualization/ivshmem.rst | 13 ++ drivers/virtualization/Kconfig | 13 +- drivers/virtualization/virt_ivshmem.h | 49 +++++ dts/bindings/virtualization/qemu,ivshmem.yaml | 9 + .../zephyr/drivers/virtualization/ivshmem.h | 179 +++++++++++++++++- 5 files changed, 259 insertions(+), 4 deletions(-) diff --git a/doc/services/virtualization/ivshmem.rst b/doc/services/virtualization/ivshmem.rst index a3123313769..7da8e05bf5e 100644 --- a/doc/services/virtualization/ivshmem.rst +++ b/doc/services/virtualization/ivshmem.rst @@ -38,6 +38,19 @@ vectors that will be needed. Note that a tiny shell module can be exposed to test the ivshmem feature by enabling :kconfig:option:`CONFIG_IVSHMEM_SHELL`. +ivshmem-v2 +********** + +Zephyr also supports ivshmem-v2: + +https://github.com/siemens/jailhouse/blob/master/Documentation/ivshmem-v2-specification.md + +This is primarily used for IPC in the Jailhouse hypervisor +(e.g. :ref:`eth_ivshmem_sample`). It is also possible to use ivshmem-v2 without +Jailhouse by building the Siemens fork of QEMU, and modifying the QEMU launch flags: + +https://github.com/siemens/qemu/tree/wip/ivshmem2 + API Reference ************* diff --git a/drivers/virtualization/Kconfig b/drivers/virtualization/Kconfig index 815484b4ed5..4f32552f09a 100644 --- a/drivers/virtualization/Kconfig +++ b/drivers/virtualization/Kconfig @@ -28,10 +28,9 @@ source "subsys/logging/Kconfig.template.log_config" config IVSHMEM_DOORBELL bool "Support interrupt based ivshmem (doorbell version)" - depends on PCIE_MSI_X && PCIE_MSI_MULTI_VECTOR help This will enable support of ivshmem-doorbell, i.e. the interrupt - based ivshmem. + based ivshmem. For ivshmem-v2 INTx interrupts are also supported. config IVSHMEM_MSI_X_VECTORS int "How many notification vectors should be pre-allocated?" @@ -44,11 +43,12 @@ config IVSHMEM_MSI_X_VECTORS ivshmem. config IVSHMEM_INT_PRIORITY - int "Interrupt priority" + int "MSI-X interrupt priority" default 2 depends on IVSHMEM_DOORBELL help Interrupt priority used for the MSI-X generated interrupts. + INTx interrupt priority is configured in the device tree. config IVSHMEM_SHELL bool "IVshmem shell module" @@ -59,4 +59,11 @@ config IVSHMEM_SHELL endif # IVSHMEM +config IVSHMEM_V2 + bool "Inter-VM shared memory v2 (ivshmem-v2)" + select IVSHMEM + help + Enable ivshmem-v2 support. + ivshmem-v2 is primarily used for IPC in the Jailhouse hypervisor. + endif # VIRTUALIZATION diff --git a/drivers/virtualization/virt_ivshmem.h b/drivers/virtualization/virt_ivshmem.h index 3d25afad25a..e6439408787 100644 --- a/drivers/virtualization/virt_ivshmem.h +++ b/drivers/virtualization/virt_ivshmem.h @@ -13,9 +13,30 @@ #include #endif +#define PCIE_CONF_CMDSTAT_INTX_DISABLE 0x0400 +#define PCIE_CONF_INTR_PIN(x) (((x) >> 8) & 0xFFu) + +#define IVSHMEM_CFG_ID 0x00 +#define IVSHMEM_CFG_NEXT_CAP 0x01 +#define IVSHMEM_CFG_LENGTH 0x02 +#define IVSHMEM_CFG_PRIV_CNTL 0x03 +#define IVSHMEM_PRIV_CNTL_ONESHOT_INT BIT(0) +#define IVSHMEM_CFG_STATE_TAB_SZ 0x04 +#define IVSHMEM_CFG_RW_SECTION_SZ 0x08 +#define IVSHMEM_CFG_OUTPUT_SECTION_SZ 0x10 +#define IVSHMEM_CFG_ADDRESS 0x18 + +#define IVSHMEM_INT_ENABLE BIT(0) + #define IVSHMEM_PCIE_REG_BAR_IDX 0 +#define IVSHMEM_PCIE_MSI_X_BAR_IDX 1 #define IVSHMEM_PCIE_SHMEM_BAR_IDX 2 +#define PCIE_INTX_PIN_MIN 1 +#define PCIE_INTX_PIN_MAX 4 + +#define INTX_IRQ_UNUSED UINT32_MAX + struct ivshmem_param { const struct device *dev; struct k_poll_signal *signal; @@ -32,6 +53,14 @@ struct ivshmem { struct ivshmem_param params[CONFIG_IVSHMEM_MSI_X_VECTORS]; uint16_t n_vectors; #endif +#ifdef CONFIG_IVSHMEM_V2 + bool ivshmem_v2; + uint32_t max_peers; + size_t rw_section_size; + size_t rw_section_offset; + size_t output_section_size; + size_t output_section_offset; +#endif }; struct ivshmem_reg { @@ -41,6 +70,26 @@ struct ivshmem_reg { uint32_t doorbell; }; +#ifdef CONFIG_IVSHMEM_V2 + +struct ivshmem_v2_reg { + uint32_t id; + uint32_t max_peers; + uint32_t int_control; + uint32_t doorbell; + uint32_t state; +}; + +struct ivshmem_cfg { + struct intx_info { + uint32_t irq; + uint32_t priority; + uint32_t flags; + } intx_info[PCIE_INTX_PIN_MAX]; +}; + +#endif /* CONFIG_IVSHMEM_V2 */ + #define IVSHMEM_GEN_DOORBELL(i, v) ((i << 16) | (v & 0xFFFF)) #endif /* ZEPHYR_DRIVERS_VIRTUALIZATION_VIRT_IVSHMEM_H_ */ diff --git a/dts/bindings/virtualization/qemu,ivshmem.yaml b/dts/bindings/virtualization/qemu,ivshmem.yaml index bd841339f1a..b7ac858d494 100644 --- a/dts/bindings/virtualization/qemu,ivshmem.yaml +++ b/dts/bindings/virtualization/qemu,ivshmem.yaml @@ -6,3 +6,12 @@ description: ivShMem device properties compatible: "qemu,ivshmem" include: [base.yaml, pcie-device.yaml] + +properties: + + ivshmem-v2: + type: boolean + description: | + Use ivshmem-v2. + Primarily used for IPC in the Jailhouse hypervisor. + ivshmem-v2 is not compatible with v1. diff --git a/include/zephyr/drivers/virtualization/ivshmem.h b/include/zephyr/drivers/virtualization/ivshmem.h index ec87f5e23b8..13379e2eacf 100644 --- a/include/zephyr/drivers/virtualization/ivshmem.h +++ b/include/zephyr/drivers/virtualization/ivshmem.h @@ -22,6 +22,9 @@ extern "C" { #endif +#define IVSHMEM_V2_PROTO_UNDEFINED 0x0000 +#define IVSHMEM_V2_PROTO_NET 0x0001 + typedef size_t (*ivshmem_get_mem_f)(const struct device *dev, uintptr_t *memmap); @@ -36,19 +39,52 @@ typedef int (*ivshmem_register_handler_f)(const struct device *dev, struct k_poll_signal *signal, uint16_t vector); +#ifdef CONFIG_IVSHMEM_V2 + +typedef size_t (*ivshmem_get_rw_mem_section_f)(const struct device *dev, + uintptr_t *memmap); + +typedef size_t (*ivshmem_get_output_mem_section_f)(const struct device *dev, + uint32_t peer_id, + uintptr_t *memmap); + +typedef uint32_t (*ivshmem_get_state_f)(const struct device *dev, + uint32_t peer_id); + +typedef int (*ivshmem_set_state_f)(const struct device *dev, + uint32_t state); + +typedef uint32_t (*ivshmem_get_max_peers_f)(const struct device *dev); + +typedef uint16_t (*ivshmem_get_protocol_f)(const struct device *dev); + +typedef int (*ivshmem_enable_interrupts_f)(const struct device *dev, + bool enable); + +#endif /* CONFIG_IVSHMEM_V2 */ + __subsystem struct ivshmem_driver_api { ivshmem_get_mem_f get_mem; ivshmem_get_id_f get_id; ivshmem_get_vectors_f get_vectors; ivshmem_int_peer_f int_peer; ivshmem_register_handler_f register_handler; +#ifdef CONFIG_IVSHMEM_V2 + ivshmem_get_rw_mem_section_f get_rw_mem_section; + ivshmem_get_output_mem_section_f get_output_mem_section; + ivshmem_get_state_f get_state; + ivshmem_set_state_f set_state; + ivshmem_get_max_peers_f get_max_peers; + ivshmem_get_protocol_f get_protocol; + ivshmem_enable_interrupts_f enable_interrupts; +#endif }; /** * @brief Get the inter-VM shared memory * * @param dev Pointer to the device structure for the driver instance - * @param memmap A pointer to fill in with the memory address + * @param memmap A pointer to fill in with the memory address * * @return the size of the memory mapped, or 0 */ @@ -148,6 +184,147 @@ static inline int z_impl_ivshmem_register_handler(const struct device *dev, return api->register_handler(dev, signal, vector); } +#ifdef CONFIG_IVSHMEM_V2 + +/** + * @brief Get the ivshmem read/write section (ivshmem-v2 only) + * + * @param dev Pointer to the device structure for the driver instance + * @param memmap A pointer to fill in with the memory address + * + * @return the size of the memory mapped, or 0 + */ +__syscall size_t ivshmem_get_rw_mem_section(const struct device *dev, + uintptr_t *memmap); + +static inline size_t z_impl_ivshmem_get_rw_mem_section(const struct device *dev, + uintptr_t *memmap) +{ + const struct ivshmem_driver_api *api = + (const struct ivshmem_driver_api *)dev->api; + + return api->get_rw_mem_section(dev, memmap); +} + +/** + * @brief Get the ivshmem output section for a peer (ivshmem-v2 only) + * + * @param dev Pointer to the device structure for the driver instance + * @param peer_id The VM ID whose output memory section to get + * @param memmap A pointer to fill in with the memory address + * + * @return the size of the memory mapped, or 0 + */ +__syscall size_t ivshmem_get_output_mem_section(const struct device *dev, + uint32_t peer_id, + uintptr_t *memmap); + +static inline size_t z_impl_ivshmem_get_output_mem_section(const struct device *dev, + uint32_t peer_id, + uintptr_t *memmap) +{ + const struct ivshmem_driver_api *api = + (const struct ivshmem_driver_api *)dev->api; + + return api->get_output_mem_section(dev, peer_id, memmap); +} + +/** + * @brief Get the state value of a peer (ivshmem-v2 only) + * + * @param dev Pointer to the device structure for the driver instance + * @param peer_id The VM ID whose state to get + * + * @return the state value of the peer + */ +__syscall uint32_t ivshmem_get_state(const struct device *dev, + uint32_t peer_id); + +static inline uint32_t z_impl_ivshmem_get_state(const struct device *dev, + uint32_t peer_id) +{ + const struct ivshmem_driver_api *api = + (const struct ivshmem_driver_api *)dev->api; + + return api->get_state(dev, peer_id); +} + +/** + * @brief Set our state (ivshmem-v2 only) + * + * @param dev Pointer to the device structure for the driver instance + * @param state The state value to set + * + * @return 0 on success, a negative errno otherwise + */ +__syscall int ivshmem_set_state(const struct device *dev, + uint32_t state); + +static inline int z_impl_ivshmem_set_state(const struct device *dev, + uint32_t state) +{ + const struct ivshmem_driver_api *api = + (const struct ivshmem_driver_api *)dev->api; + + return api->set_state(dev, state); +} + +/** + * @brief Get the maximum number of peers supported (ivshmem-v2 only) + * + * @param dev Pointer to the device structure for the driver instance + * + * @return the maximum number of peers supported, or 0 + */ +__syscall uint32_t ivshmem_get_max_peers(const struct device *dev); + +static inline uint32_t z_impl_ivshmem_get_max_peers(const struct device *dev) +{ + const struct ivshmem_driver_api *api = + (const struct ivshmem_driver_api *)dev->api; + + return api->get_max_peers(dev); +} + +/** + * @brief Get the protocol used by this ivshmem instance (ivshmem-v2 only) + * + * @param dev Pointer to the device structure for the driver instance + * + * @return the protocol + */ +__syscall uint16_t ivshmem_get_protocol(const struct device *dev); + +static inline uint16_t z_impl_ivshmem_get_protocol(const struct device *dev) +{ + const struct ivshmem_driver_api *api = + (const struct ivshmem_driver_api *)dev->api; + + return api->get_protocol(dev); +} + +/** + * @brief Set the interrupt enablement for our VM (ivshmem-v2 only) + * + * @param dev Pointer to the device structure for the driver instance + * @param enable True to enable interrupts, false to disable + * + * @return 0 on success, a negative errno otherwise + */ +__syscall int ivshmem_enable_interrupts(const struct device *dev, + bool enable); + +static inline int z_impl_ivshmem_enable_interrupts(const struct device *dev, + bool enable) +{ + const struct ivshmem_driver_api *api = + (const struct ivshmem_driver_api *)dev->api; + + return api->enable_interrupts(dev, enable); +} + +#endif /* CONFIG_IVSHMEM_V2 */ + #ifdef __cplusplus } #endif