drivers: virtualization: Add interface for ivshmem-v2

ivshmem-v2 is primarily used for IPC in the Jailhouse hypervisor

Signed-off-by: Grant Ramsay <gramsay@enphaseenergy.com>
This commit is contained in:
Grant Ramsay 2023-04-30 14:34:35 +12:00 committed by Christopher Friedt
commit 4ed404a27f
5 changed files with 259 additions and 4 deletions

View file

@ -38,6 +38,19 @@ vectors that will be needed.
Note that a tiny shell module can be exposed to test the ivshmem feature by Note that a tiny shell module can be exposed to test the ivshmem feature by
enabling :kconfig:option:`CONFIG_IVSHMEM_SHELL`. 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 API Reference
************* *************

View file

@ -28,10 +28,9 @@ source "subsys/logging/Kconfig.template.log_config"
config IVSHMEM_DOORBELL config IVSHMEM_DOORBELL
bool "Support interrupt based ivshmem (doorbell version)" bool "Support interrupt based ivshmem (doorbell version)"
depends on PCIE_MSI_X && PCIE_MSI_MULTI_VECTOR
help help
This will enable support of ivshmem-doorbell, i.e. the interrupt 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 config IVSHMEM_MSI_X_VECTORS
int "How many notification vectors should be pre-allocated?" int "How many notification vectors should be pre-allocated?"
@ -44,11 +43,12 @@ config IVSHMEM_MSI_X_VECTORS
ivshmem. ivshmem.
config IVSHMEM_INT_PRIORITY config IVSHMEM_INT_PRIORITY
int "Interrupt priority" int "MSI-X interrupt priority"
default 2 default 2
depends on IVSHMEM_DOORBELL depends on IVSHMEM_DOORBELL
help help
Interrupt priority used for the MSI-X generated interrupts. Interrupt priority used for the MSI-X generated interrupts.
INTx interrupt priority is configured in the device tree.
config IVSHMEM_SHELL config IVSHMEM_SHELL
bool "IVshmem shell module" bool "IVshmem shell module"
@ -59,4 +59,11 @@ config IVSHMEM_SHELL
endif # IVSHMEM 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 endif # VIRTUALIZATION

View file

@ -13,9 +13,30 @@
#include <zephyr/drivers/pcie/msi.h> #include <zephyr/drivers/pcie/msi.h>
#endif #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_REG_BAR_IDX 0
#define IVSHMEM_PCIE_MSI_X_BAR_IDX 1
#define IVSHMEM_PCIE_SHMEM_BAR_IDX 2 #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 { struct ivshmem_param {
const struct device *dev; const struct device *dev;
struct k_poll_signal *signal; struct k_poll_signal *signal;
@ -32,6 +53,14 @@ struct ivshmem {
struct ivshmem_param params[CONFIG_IVSHMEM_MSI_X_VECTORS]; struct ivshmem_param params[CONFIG_IVSHMEM_MSI_X_VECTORS];
uint16_t n_vectors; uint16_t n_vectors;
#endif #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 { struct ivshmem_reg {
@ -41,6 +70,26 @@ struct ivshmem_reg {
uint32_t doorbell; 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)) #define IVSHMEM_GEN_DOORBELL(i, v) ((i << 16) | (v & 0xFFFF))
#endif /* ZEPHYR_DRIVERS_VIRTUALIZATION_VIRT_IVSHMEM_H_ */ #endif /* ZEPHYR_DRIVERS_VIRTUALIZATION_VIRT_IVSHMEM_H_ */

View file

@ -6,3 +6,12 @@ description: ivShMem device properties
compatible: "qemu,ivshmem" compatible: "qemu,ivshmem"
include: [base.yaml, pcie-device.yaml] 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.

View file

@ -22,6 +22,9 @@
extern "C" { extern "C" {
#endif #endif
#define IVSHMEM_V2_PROTO_UNDEFINED 0x0000
#define IVSHMEM_V2_PROTO_NET 0x0001
typedef size_t (*ivshmem_get_mem_f)(const struct device *dev, typedef size_t (*ivshmem_get_mem_f)(const struct device *dev,
uintptr_t *memmap); uintptr_t *memmap);
@ -36,12 +39,45 @@ typedef int (*ivshmem_register_handler_f)(const struct device *dev,
struct k_poll_signal *signal, struct k_poll_signal *signal,
uint16_t vector); 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 { __subsystem struct ivshmem_driver_api {
ivshmem_get_mem_f get_mem; ivshmem_get_mem_f get_mem;
ivshmem_get_id_f get_id; ivshmem_get_id_f get_id;
ivshmem_get_vectors_f get_vectors; ivshmem_get_vectors_f get_vectors;
ivshmem_int_peer_f int_peer; ivshmem_int_peer_f int_peer;
ivshmem_register_handler_f register_handler; 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
}; };
/** /**
@ -148,6 +184,147 @@ static inline int z_impl_ivshmem_register_handler(const struct device *dev,
return api->register_handler(dev, signal, vector); 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 #ifdef __cplusplus
} }
#endif #endif