mirror of
https://github.com/OpenAMP/open-amp.git
synced 2026-09-23 00:04:09 +08:00
virtio-mmio: hypervisor-less mode
Added support for hypervisor-less virtio. - HVP dispatch function table - Semaphore-based configuration synchronization mechanism when in yield-capable context Signed-off-by: Dan Milea <dan.milea@windriver.com>
This commit is contained in:
committed by
Arnaud Pouliquen
parent
a76b28b248
commit
7cad4e88e2
@@ -80,11 +80,17 @@ endif (NOT WITH_VIRTIO_DEVICE AND NOT WITH_VIRTIO_SLAVE)
|
||||
|
||||
option (WITH_VIRTIO_MMIO_DRV "Build with virtio mmio driver support enabled" OFF)
|
||||
option (WITH_VIRTIO_MMIO_DEV "Build with virtio mmio device support enabled" OFF)
|
||||
option (WITH_HVL_VIRTIO_DRV "Build with hypervisor-less virtio (front end) enabled" OFF)
|
||||
|
||||
if (WITH_VIRTIO_MMIO_DRV)
|
||||
add_definitions(-DWITH_VIRTIO_MMIO_DRV)
|
||||
endif (WITH_VIRTIO_MMIO_DRV)
|
||||
|
||||
if (WITH_HVL_VIRTIO_DRV)
|
||||
set (WITH_VIRTIO_MMIO_DRV ON)
|
||||
add_definitions(-DHVL_VIRTIO)
|
||||
endif (WITH_HVL_VIRTIO_DRV)
|
||||
|
||||
if (WITH_VIRTIO_MMIO_DEV)
|
||||
add_definitions(-DWITH_VIRTIO_MMIO_DEV)
|
||||
endif (WITH_VIRTIO_MMIO_DEV)
|
||||
|
||||
@@ -12,7 +12,10 @@
|
||||
#include <metal/device.h>
|
||||
#include <openamp/virtio.h>
|
||||
#include <openamp/virtqueue.h>
|
||||
|
||||
#if defined(HVL_VIRTIO)
|
||||
#include <metal/mutex.h>
|
||||
#include <openamp/virtio_mmio_hvl.h>
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -166,6 +169,26 @@ struct virtio_mmio_device {
|
||||
|
||||
/** Custom user data */
|
||||
void *user_data;
|
||||
|
||||
#if defined(HVL_VIRTIO)
|
||||
/** Hypervisorless virtio mode flag */
|
||||
unsigned int hvl_mode;
|
||||
|
||||
/** Hypervisorless virtio bounce buffer list */
|
||||
struct metal_list bounce_buf_list;
|
||||
|
||||
/** Hypervisorless virtio inter-processor notification routine */
|
||||
virtio_mmio_hvl_ipi_t ipi;
|
||||
|
||||
/** Hypervisorless virtio inter-processor notification routine parameter */
|
||||
void *ipi_param;
|
||||
|
||||
/** Configuration synchronization semaphore usable in yield context */
|
||||
metal_mutex_t cfg_sem;
|
||||
|
||||
/** Hypervisorless virtio dispatch table */
|
||||
struct hvl_dispatch *hvl_func;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Wind River Systems, Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef OPENAMP_VIRTIO_MMIO_HVL_H
|
||||
#define OPENAMP_VIRTIO_MMIO_HVL_H
|
||||
|
||||
#include <openamp/virtqueue.h>
|
||||
#include <openamp/virtio.h>
|
||||
#include <metal/device.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(HVL_VIRTIO)
|
||||
|
||||
/*
|
||||
* To minimize the number of hardware notification channels used, in hypervirsorless virtio
|
||||
* deployments, a single multiplexed source of notifications is used for all the devices.
|
||||
* The callback framework allows individual device drivers, if needed, to register their
|
||||
* own callbacks (i.e. the equivalent of per-device ISRs)
|
||||
*/
|
||||
|
||||
/* Max number of hypervisorless virtio callbacks */
|
||||
#define VIRTIO_MMIO_HVL_CB_MAX 10
|
||||
|
||||
/*
|
||||
* For multi-virtqueue devices in hypervisorless mode, a custom configuration acknowledgment
|
||||
* mechanism is used to process same-register configuration entries like QUEUE_PFN. This is
|
||||
* the ACK value from the physical machine monitor for these configurations.
|
||||
*/
|
||||
#define VIRTIO_MMIO_HVL_CFG_ACK 0xAABBAABB
|
||||
|
||||
/* Hypervisorless virtio callback type */
|
||||
typedef void (*virtio_mmio_hvl_cb_t)(void *);
|
||||
|
||||
/* Hypervisorless virtio inter-processor notification routine type */
|
||||
typedef void (*virtio_mmio_hvl_ipi_t)(void *);
|
||||
|
||||
struct hvl_dispatch {
|
||||
/**
|
||||
* @brief VIRTIO MMIO shared memory pool initialization routine.
|
||||
*
|
||||
* @param mem Pointer to memory.
|
||||
* @param size Size of memory region in bytes.
|
||||
*/
|
||||
void (*hvl_shm_pool_init)(void *mem, size_t size);
|
||||
|
||||
/**
|
||||
* @brief VIRTIO MMIO shared memory pool buffer allocation routine.
|
||||
*
|
||||
* @param size Number of bytes requested.
|
||||
*
|
||||
* @return pointer to allocated memory space in shared memory region.
|
||||
*/
|
||||
void* (*hvl_shm_pool_alloc)(size_t size);
|
||||
|
||||
/**
|
||||
* @brief VIRTIO MMIO shared memory pool buffer deallocation routine.
|
||||
*
|
||||
* @param ptr Pointer to memory space to free.
|
||||
*/
|
||||
void (*hvl_shm_pool_free)(void *ptr);
|
||||
|
||||
/**
|
||||
* @brief Wait for configuration ack for device vdev; OS-specific implementation
|
||||
*
|
||||
* @param vdev Pointer to virtio device structure.
|
||||
* @param usec timeout in microseconds.
|
||||
*/
|
||||
void (*hvl_wait_cfg_event)(struct virtio_device *vdev, uint32_t usec);
|
||||
|
||||
/**
|
||||
* @brief VIRTIO MMIO (hypervisorless mode) inter-processor notification routine
|
||||
*/
|
||||
void (*hvl_ipi)(void *param);
|
||||
};
|
||||
|
||||
struct vq_bounce_buf {
|
||||
struct metal_list node;
|
||||
void *cookie;
|
||||
uint64_t addr;
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief VIRTIO MMIO hypervisorless operation mode initialization
|
||||
*
|
||||
* @param vdev Pointer to struct virtio_device.
|
||||
*/
|
||||
|
||||
void virtio_mmio_hvl_init(struct virtio_device *vdev);
|
||||
|
||||
/**
|
||||
* @brief VIRTIO MMIO shared memory pool initialization routine.
|
||||
*
|
||||
* @param mem Pointer to memory.
|
||||
* @param size Size of memory region in bytes.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Add VIRTIO MMIO (hypervisorless mode) callback.
|
||||
*
|
||||
* @param func Callback function pointer.
|
||||
* @param arg Callback parameter.
|
||||
*
|
||||
* @return int 0 for success.
|
||||
*/
|
||||
|
||||
int virtio_mmio_hvl_cb_set(virtio_mmio_hvl_cb_t func, void *arg);
|
||||
|
||||
/**
|
||||
* @brief VIRTIO MMIO (hypervisorless mode) callback execution routine.
|
||||
*/
|
||||
|
||||
void virtio_mmio_hvl_cb_run(void);
|
||||
|
||||
/**
|
||||
* @brief Add VIRTIO MMIO (hypervisorless mode) shared-memory bounce buffer.
|
||||
*
|
||||
* In hypervisorless mode this routine transparently allocates a bounce buffer
|
||||
* to be enqueued in vring for consumption, copies the data in the original
|
||||
* buffer and saves the original buffer information for dequeueing.
|
||||
* If the buffer is already in the pre-shared memory region, the bouncing
|
||||
* mechanism is not used.
|
||||
*
|
||||
* @param vq Pointer to VirtIO queue control block.
|
||||
* @param cookie Pointer to hold call back data
|
||||
* @param buffer Data buffer
|
||||
* @param len Data buffer length
|
||||
*
|
||||
* @return bounce buffer physical address or METAL_BAD_PHYS
|
||||
*/
|
||||
|
||||
uint64_t virtio_mmio_hvl_add_bounce_buf(struct virtqueue *vq, void *cookie, char *buffer,
|
||||
unsigned int len);
|
||||
|
||||
/**
|
||||
* @brief Get used VIRTIO MMIO (hypervisorless mode) shared-memory bounce buffer.
|
||||
*
|
||||
* In hypervisorless mode this routine transparently copies data from a dequeued
|
||||
* bounce buffer in pre-shared memory to the original buffer location.
|
||||
*
|
||||
* @param vq Pointer to VirtIO queue control block.
|
||||
* @param dp Pointer to vring descriptor.
|
||||
* @param cookie Pointer for callback data
|
||||
*/
|
||||
|
||||
void virtio_mmio_hvl_get_bounce_buf(struct virtqueue *vq, struct vring_desc *dp,
|
||||
void *cookie);
|
||||
|
||||
/**
|
||||
* @brief Wait for configuration ack, i.e. value at offset to match data
|
||||
*
|
||||
* @return int 0 for success
|
||||
*/
|
||||
int virtio_mmio_hvl_wait_cfg(struct virtio_device *vdev, int offset, uint32_t data);
|
||||
|
||||
/**
|
||||
* @brief Set inter-processor notification routine
|
||||
*
|
||||
* @param vdev Pointer to virtio_device structure
|
||||
* @param ipi_func Inter-processor notification routine
|
||||
* @param param Inter-processor notification routine parameter
|
||||
*/
|
||||
void virtio_mmio_hvl_set_ipi(struct virtio_device *vdev, virtio_mmio_hvl_ipi_t ipi_func,
|
||||
void *param);
|
||||
|
||||
#endif /* defined(HVL_VIRTIO) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OPENAMP_VIRTIO_MMIO_HVL_H */
|
||||
+24
-1
@@ -11,7 +11,9 @@
|
||||
#include <metal/atomic.h>
|
||||
#include <metal/log.h>
|
||||
#include <metal/alloc.h>
|
||||
|
||||
#if defined(HVL_VIRTIO)
|
||||
#include <openamp/virtio_mmio_hvl.h>
|
||||
#endif
|
||||
/* Prototype for internal functions. */
|
||||
static void vq_ring_init(struct virtqueue *, void *, int);
|
||||
static void vq_ring_update_avail(struct virtqueue *, uint16_t);
|
||||
@@ -166,6 +168,12 @@ void *virtqueue_get_buffer(struct virtqueue *vq, uint32_t *len, uint16_t *idx)
|
||||
vq_ring_free_chain(vq, desc_idx);
|
||||
|
||||
cookie = vq->vq_descx[desc_idx].cookie;
|
||||
|
||||
#if defined(HVL_VIRTIO)
|
||||
virtio_mmio_hvl_get_bounce_buf(vq, &vq->vq_ring.desc[desc_idx],
|
||||
&vq->vq_descx[desc_idx].cookie);
|
||||
#endif
|
||||
|
||||
vq->vq_descx[desc_idx].cookie = NULL;
|
||||
|
||||
if (idx)
|
||||
@@ -403,6 +411,14 @@ static uint16_t vq_ring_add_buffer(struct virtqueue *vq,
|
||||
struct vring_desc *dp;
|
||||
int i, needed;
|
||||
uint16_t idx;
|
||||
#if defined(HVL_VIRTIO)
|
||||
uint64_t addr = 0;
|
||||
struct vq_desc_extra *dxp = NULL;
|
||||
void *cookie = NULL;
|
||||
|
||||
dxp = &vq->vq_descx[head_idx];
|
||||
cookie = dxp->cookie;
|
||||
#endif
|
||||
|
||||
(void)vq;
|
||||
|
||||
@@ -428,6 +444,13 @@ static uint16_t vq_ring_add_buffer(struct virtqueue *vq,
|
||||
if (i >= readable)
|
||||
dp->flags |= VRING_DESC_F_WRITE;
|
||||
|
||||
#if defined(HVL_VIRTIO)
|
||||
addr = virtio_mmio_hvl_add_bounce_buf(vq, &dxp->cookie, buf_list[i].buf,
|
||||
buf_list[i].len);
|
||||
if (addr != 0) {
|
||||
dp->addr = addr;
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* Instead of flushing the whole desc region, we flush only the
|
||||
* single entry hopefully saving some cycles
|
||||
|
||||
@@ -5,6 +5,10 @@ collect (PROJECT_LIB_SOURCES virtio_net_drv.c)
|
||||
collect (PROJECT_LIB_SOURCES virtio_serial_drv.c)
|
||||
endif (WITH_VIRTIO_MMIO_DRV)
|
||||
|
||||
if (WITH_HVL_VIRTIO_DRV)
|
||||
collect (PROJECT_LIB_SOURCES virtio_mmio_hvl.c)
|
||||
endif (WITH_HVL_VIRTIO_DRV)
|
||||
|
||||
if (WITH_VIRTIO_MMIO_DEV)
|
||||
collect (PROJECT_LIB_SOURCES virtio_mmio_dev.c)
|
||||
endif (WITH_VIRTIO_MMIO_DEV)
|
||||
|
||||
@@ -45,7 +45,17 @@ static inline uint8_t virtio_mmio_read8(struct virtio_device *vdev, int offset)
|
||||
|
||||
static inline void virtio_mmio_set_status(struct virtio_device *vdev, uint8_t status)
|
||||
{
|
||||
#if defined(HVL_VIRTIO)
|
||||
struct virtio_mmio_device *vmdev = metal_container_of(vdev,
|
||||
struct virtio_mmio_device, vdev);
|
||||
#endif
|
||||
|
||||
virtio_mmio_write32(vdev, VIRTIO_MMIO_STATUS, status);
|
||||
#if defined(HVL_VIRTIO)
|
||||
if (vmdev && vmdev->ipi && vmdev->hvl_mode == 1) {
|
||||
vmdev->ipi(vmdev->ipi_param);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint8_t virtio_mmio_get_status(struct virtio_device *vdev)
|
||||
@@ -98,6 +108,10 @@ static void _virtio_mmio_set_features(struct virtio_device *vdev,
|
||||
uint32_t features, int idx)
|
||||
{
|
||||
uint32_t hfeatures;
|
||||
#if defined(HVL_VIRTIO)
|
||||
struct virtio_mmio_device *vmdev = metal_container_of(vdev,
|
||||
struct virtio_mmio_device, vdev);
|
||||
#endif
|
||||
|
||||
/* Writing selection register VIRTIO_MMIO_DEVICE_FEATURES_SEL. In pure AMP
|
||||
* mode this needs to be followed by a synchronization w/ the device
|
||||
@@ -108,6 +122,11 @@ static void _virtio_mmio_set_features(struct virtio_device *vdev,
|
||||
features &= hfeatures;
|
||||
virtio_mmio_write32(vdev, VIRTIO_MMIO_DRIVER_FEATURES, features);
|
||||
vdev->features = features;
|
||||
#if defined(HVL_VIRTIO)
|
||||
if (vmdev && vmdev->ipi && vmdev->hvl_mode == 1) {
|
||||
vmdev->ipi(vmdev->ipi_param);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void virtio_mmio_set_features(struct virtio_device *vdev, uint32_t features)
|
||||
@@ -120,10 +139,19 @@ static void virtio_mmio_reset_device(struct virtio_device *vdev)
|
||||
virtio_mmio_set_status(vdev, 0);
|
||||
}
|
||||
|
||||
static void virtio_mmio_notify(struct virtqueue *vq)
|
||||
static void virtio_mmio_drv_notify(struct virtqueue *vq)
|
||||
{
|
||||
#if defined(HVL_VIRTIO)
|
||||
struct virtio_mmio_device *vmdev = metal_container_of(vq->vq_dev,
|
||||
struct virtio_mmio_device, vdev);
|
||||
#endif
|
||||
/* VIRTIO_F_NOTIFICATION_DATA is not supported for now */
|
||||
virtio_mmio_write32(vq->vq_dev, VIRTIO_MMIO_QUEUE_NOTIFY, vq->vq_queue_index);
|
||||
#if defined(HVL_VIRTIO)
|
||||
if (vmdev && vmdev->ipi && vmdev->hvl_mode == 1) {
|
||||
vmdev->ipi(vmdev->ipi_param);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
const struct virtio_dispatch virtio_mmio_dispatch = {
|
||||
@@ -135,7 +163,7 @@ const struct virtio_dispatch virtio_mmio_dispatch = {
|
||||
.read_config = virtio_mmio_read_config,
|
||||
.write_config = virtio_mmio_write_config,
|
||||
.reset_device = virtio_mmio_reset_device,
|
||||
.notify = virtio_mmio_notify,
|
||||
.notify = virtio_mmio_drv_notify,
|
||||
};
|
||||
|
||||
static int virtio_mmio_get_metal_io(struct virtio_device *vdev, uintptr_t virt_mem_ptr,
|
||||
@@ -200,6 +228,9 @@ int virtio_mmio_device_init(struct virtio_mmio_device *vmdev, uintptr_t virt_mem
|
||||
{
|
||||
struct virtio_device *vdev = &vmdev->vdev;
|
||||
uint32_t magic, version, devid, vendor;
|
||||
#if defined(HVL_VIRTIO)
|
||||
uint64_t shm_base, shm_len;
|
||||
#endif
|
||||
|
||||
vdev->role = vmdev->device_mode;
|
||||
vdev->priv = vmdev;
|
||||
@@ -234,6 +265,25 @@ int virtio_mmio_device_init(struct virtio_mmio_device *vmdev, uintptr_t virt_mem
|
||||
vdev->id.device = devid;
|
||||
vdev->id.vendor = vendor;
|
||||
|
||||
#if defined(HVL_VIRTIO)
|
||||
/* Check shared memory information.
|
||||
* This is just a flag for enabling hypervisorless mode for now.
|
||||
*/
|
||||
shm_base = virtio_mmio_read32(vdev, VIRTIO_MMIO_SHM_BASE_HIGH);
|
||||
shm_base <<= 32;
|
||||
shm_base |= virtio_mmio_read32(vdev, VIRTIO_MMIO_SHM_BASE_LOW);
|
||||
shm_len = virtio_mmio_read32(vdev, VIRTIO_MMIO_SHM_LEN_HIGH);
|
||||
shm_len <<= 32;
|
||||
shm_len |= virtio_mmio_read32(vdev, VIRTIO_MMIO_SHM_LEN_LOW);
|
||||
|
||||
if (shm_base != 0 && shm_len != 0) {
|
||||
vmdev->hvl_mode = 1;
|
||||
virtio_mmio_hvl_cb_set((virtio_mmio_hvl_cb_t)virtio_mmio_isr, vdev);
|
||||
virtio_mmio_hvl_init(vdev);
|
||||
/* Initialize bounce buffer list for hypervisorless virtio */
|
||||
metal_list_init(&vmdev->bounce_buf_list);
|
||||
}
|
||||
#endif
|
||||
virtio_mmio_set_status(vdev, VIRTIO_CONFIG_STATUS_ACK);
|
||||
virtio_mmio_write32(vdev, VIRTIO_MMIO_GUEST_PAGE_SIZE, 4096);
|
||||
|
||||
@@ -327,6 +377,24 @@ struct virtqueue *virtio_mmio_setup_virtqueue(struct virtio_device *vdev,
|
||||
virtio_mmio_write32(vdev, VIRTIO_MMIO_QUEUE_PFN,
|
||||
((uintptr_t)metal_io_virt_to_phys(vq->shm_io,
|
||||
(char *)vq->vq_ring.desc)) / 4096);
|
||||
#if defined(HVL_VIRTIO)
|
||||
if (vmdev && vmdev->ipi && vmdev->hvl_mode == 1) {
|
||||
vmdev->ipi(vmdev->ipi_param);
|
||||
/*
|
||||
* For multi-virtqueue devices in hypervisorless mode, a custom
|
||||
* configuration acknowledgment mechanism is used to process
|
||||
* same-register configuration entries like QUEUE_PFN.
|
||||
*/
|
||||
if (virtio_mmio_hvl_wait_cfg(vdev, VIRTIO_MMIO_QUEUE_PFN,
|
||||
VIRTIO_MMIO_HVL_CFG_ACK) != 0) {
|
||||
metal_log(METAL_LOG_ERROR, "HVL mode: configuration failed\n");
|
||||
return NULL;
|
||||
}
|
||||
virtio_mmio_write32(vdev, VIRTIO_MMIO_QUEUE_PFN,
|
||||
((uintptr_t)metal_io_virt_to_phys(vq->shm_io,
|
||||
(char *)vq->vq_ring.desc)) / 4096);
|
||||
}
|
||||
#endif
|
||||
|
||||
vdev->vrings_info[vdev->vrings_num].vq = vq;
|
||||
vdev->vrings_num++;
|
||||
@@ -338,11 +406,25 @@ struct virtqueue *virtio_mmio_setup_virtqueue(struct virtio_device *vdev,
|
||||
void virtio_mmio_isr(struct virtio_device *vdev)
|
||||
{
|
||||
struct virtio_vring_info *vrings_info = vdev->vrings_info;
|
||||
|
||||
#if defined(HVL_VIRTIO)
|
||||
struct virtio_mmio_device *vmdev = metal_container_of(vdev,
|
||||
struct virtio_mmio_device, vdev);
|
||||
#endif
|
||||
uint32_t isr = virtio_mmio_read32(vdev, VIRTIO_MMIO_INTERRUPT_STATUS);
|
||||
struct virtqueue *vq;
|
||||
unsigned int i;
|
||||
|
||||
#if defined(HVL_VIRTIO)
|
||||
if (vmdev && vmdev->hvl_mode == 1) {
|
||||
uint32_t val;
|
||||
|
||||
val = metal_io_read32(vmdev->cfg_io, VIRTIO_MMIO_QUEUE_PFN);
|
||||
if (val == VIRTIO_MMIO_HVL_CFG_ACK) {
|
||||
metal_mutex_release(&vmdev->cfg_sem);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (isr & VIRTIO_MMIO_INT_VRING) {
|
||||
for (i = 0; i < vdev->vrings_num; i++) {
|
||||
vq = vrings_info[i].vq;
|
||||
@@ -355,6 +437,13 @@ void virtio_mmio_isr(struct virtio_device *vdev)
|
||||
metal_log(METAL_LOG_WARNING, "Unhandled interrupt type: 0x%x\n", isr);
|
||||
|
||||
virtio_mmio_write32(vdev, VIRTIO_MMIO_INTERRUPT_ACK, isr);
|
||||
#if defined(HVL_VIRTIO)
|
||||
if (isr != 0) {
|
||||
if (vmdev && vmdev->ipi && vmdev->hvl_mode == 1) {
|
||||
vmdev->ipi(vmdev->ipi_param);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int virtio_mmio_create_virtqueues(struct virtio_device *vdev, unsigned int flags,
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Wind River Systems, Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#if defined(HVL_VIRTIO)
|
||||
|
||||
#include <openamp/open_amp.h>
|
||||
#include <openamp/virtqueue.h>
|
||||
#include <openamp/virtio.h>
|
||||
#include <openamp/virtio_mmio.h>
|
||||
#include <openamp/virtio_mmio_hvl.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/mutex.h>
|
||||
#include <metal/sleep.h>
|
||||
|
||||
/*
|
||||
* The implementation depends on OS-specific services for memory pool management
|
||||
* routines, blocking wait and IPI.
|
||||
* TODO: Replace them in a future iteration of the hypervisorless virtio feature
|
||||
* set with APIs (to be introduced) in libmetal.
|
||||
*/
|
||||
|
||||
/* hypervisorless virtio callback framework */
|
||||
static virtio_mmio_hvl_cb_t _cb_funcs[VIRTIO_MMIO_HVL_CB_MAX];
|
||||
static void *_cb_args[VIRTIO_MMIO_HVL_CB_MAX];
|
||||
static int _num_cb_funcs;
|
||||
|
||||
/* pre-shared memory */
|
||||
uint32_t __shmem0_pool_start __attribute__((weak));
|
||||
uint32_t __shmem0_end __attribute__((weak));
|
||||
|
||||
void virtio_mmio_hvl_init(struct virtio_device *vdev)
|
||||
{
|
||||
static int init;
|
||||
uint32_t *shmem_pool_mem = NULL;
|
||||
uint32_t shmem_pool_size = 0;
|
||||
struct virtio_mmio_device *vmdev = metal_container_of(vdev,
|
||||
struct virtio_mmio_device, vdev);
|
||||
|
||||
VIRTIO_ASSERT(vmdev->hvl_func->hvl_shm_pool_init,
|
||||
"Invalid HVL dispatch configuration");
|
||||
VIRTIO_ASSERT(vmdev->hvl_func->hvl_shm_pool_alloc,
|
||||
"Invalid HVL dispatch configuration");
|
||||
VIRTIO_ASSERT(vmdev->hvl_func->hvl_shm_pool_free,
|
||||
"Invalid HVL dispatch configuration");
|
||||
VIRTIO_ASSERT(vmdev->hvl_func->hvl_wait_cfg_event,
|
||||
"Invalid HVL dispatch configuration");
|
||||
VIRTIO_ASSERT(vmdev->hvl_func->hvl_ipi,
|
||||
"Invalid HVL dispatch configuration");
|
||||
|
||||
if (!init) {
|
||||
shmem_pool_mem = &__shmem0_pool_start;
|
||||
if (shmem_pool_mem != 0) {
|
||||
shmem_pool_size = ((uintptr_t)&__shmem0_end -
|
||||
(uintptr_t)&__shmem0_pool_start);
|
||||
vmdev->hvl_func->hvl_shm_pool_init(shmem_pool_mem, shmem_pool_size);
|
||||
init = 1;
|
||||
}
|
||||
}
|
||||
if (vdev) {
|
||||
virtio_mmio_hvl_set_ipi(vdev, vmdev->hvl_func->hvl_ipi, NULL);
|
||||
}
|
||||
metal_mutex_init(&vmdev->cfg_sem);
|
||||
metal_mutex_acquire(&vmdev->cfg_sem);
|
||||
}
|
||||
|
||||
int virtio_mmio_hvl_cb_set(virtio_mmio_hvl_cb_t func, void *arg)
|
||||
{
|
||||
if (_num_cb_funcs < VIRTIO_MMIO_HVL_CB_MAX - 1) {
|
||||
_cb_funcs[_num_cb_funcs] = func;
|
||||
_cb_args[_num_cb_funcs] = arg;
|
||||
_num_cb_funcs++;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void virtio_mmio_hvl_cb_run(void)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < _num_cb_funcs; i++) {
|
||||
if (_cb_funcs[i]) {
|
||||
_cb_funcs[i](_cb_args[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int virtio_mmio_hvl_wait_cfg(struct virtio_device *vdev, int offset, uint32_t data)
|
||||
{
|
||||
uint32_t val;
|
||||
struct virtio_mmio_device *vmdev = metal_container_of(vdev,
|
||||
struct virtio_mmio_device, vdev);
|
||||
|
||||
if (!vmdev) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (vmdev->hvl_mode == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
val = metal_io_read32(vmdev->cfg_io, offset);
|
||||
|
||||
if (val == data) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
vmdev->hvl_func->hvl_wait_cfg_event(vdev, 4000000);
|
||||
|
||||
val = metal_io_read32(vmdev->cfg_io, offset);
|
||||
if (val == data) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint64_t virtio_mmio_hvl_add_bounce_buf(struct virtqueue *vq, void *cookie, char *buffer,
|
||||
unsigned int len)
|
||||
{
|
||||
struct vq_bounce_buf *bb = NULL;
|
||||
uint64_t addr = 0;
|
||||
char *buf = NULL;
|
||||
struct virtio_mmio_device *vmdev = NULL;
|
||||
|
||||
if (!vq || !cookie || !buffer || len == 0)
|
||||
return 0;
|
||||
|
||||
vmdev = metal_container_of(vq->vq_dev, struct virtio_mmio_device, vdev);
|
||||
|
||||
if (vmdev && vmdev->hvl_mode == 1) {
|
||||
/* check if buffer is already in pre-shared shared memory */
|
||||
addr = metal_io_virt_to_phys(vq->shm_io, buffer);
|
||||
if (addr != METAL_BAD_PHYS) {
|
||||
return addr;
|
||||
}
|
||||
|
||||
buf = vmdev->hvl_func->hvl_shm_pool_alloc(len);
|
||||
if (buf) {
|
||||
memcpy(buf, buffer, len);
|
||||
addr = metal_io_virt_to_phys(vq->shm_io, buf);
|
||||
bb = vmdev->hvl_func->hvl_shm_pool_alloc(sizeof(struct vq_bounce_buf));
|
||||
if (bb) {
|
||||
bb->addr = (uintptr_t)buffer;
|
||||
bb->len = len;
|
||||
bb->cookie = cookie;
|
||||
metal_list_add_tail(&vmdev->bounce_buf_list, &bb->node);
|
||||
return addr;
|
||||
}
|
||||
vmdev->hvl_func->hvl_shm_pool_free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void virtio_mmio_hvl_get_bounce_buf(struct virtqueue *vq, struct vring_desc *dp, void *cookie)
|
||||
{
|
||||
struct vq_bounce_buf *bb = NULL;
|
||||
struct metal_list *node = NULL;
|
||||
struct virtio_mmio_device *vmdev = NULL;
|
||||
|
||||
VIRTIO_ASSERT(vq, "Invalid virtqueue");
|
||||
VIRTIO_ASSERT(dp, "Invalid vring descriptor");
|
||||
VIRTIO_ASSERT(cookie, "Invalid cookie");
|
||||
|
||||
vmdev = metal_container_of(vq->vq_dev, struct virtio_mmio_device, vdev);
|
||||
|
||||
if (vmdev && vmdev->hvl_mode == 1) {
|
||||
metal_list_for_each(&vmdev->bounce_buf_list, node) {
|
||||
bb = metal_container_of(node, struct vq_bounce_buf, node);
|
||||
if (bb->cookie == cookie) {
|
||||
memcpy((void *)(uintptr_t)bb->addr,
|
||||
(void *)(metal_io_phys_to_virt(vq->shm_io, dp->addr)),
|
||||
bb->len);
|
||||
metal_list_del(node);
|
||||
vmdev->hvl_func->hvl_shm_pool_free(bb);
|
||||
vmdev->hvl_func->hvl_shm_pool_free(
|
||||
(void *)(metal_io_phys_to_virt(vq->shm_io, dp->addr)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void virtio_mmio_hvl_set_ipi(struct virtio_device *vdev, virtio_mmio_hvl_ipi_t ipi_func,
|
||||
void *param)
|
||||
{
|
||||
struct virtio_mmio_device *vmdev = metal_container_of(vdev,
|
||||
struct virtio_mmio_device, vdev);
|
||||
|
||||
vmdev->ipi = ipi_func;
|
||||
vmdev->ipi_param = param;
|
||||
}
|
||||
|
||||
#endif /* defined(HVL_VIRTIO) */
|
||||
|
||||
@@ -134,7 +134,15 @@ void virtio_serial_poll_out(const struct virtio_device *vdev, unsigned char out_
|
||||
}
|
||||
|
||||
if (virtqueue_full(vq)) {
|
||||
#if !defined(HVL_VIRTIO)
|
||||
virtqueue_get_buffer(vq, NULL, NULL);
|
||||
#else
|
||||
void *cookie = virtqueue_get_buffer(vq, NULL, NULL);
|
||||
|
||||
while (!cookie) {
|
||||
cookie = virtqueue_get_buffer(vq, NULL, NULL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (virtqueue_full(vq)) {
|
||||
|
||||
Reference in New Issue
Block a user