The patch adds QTest support to vtpci abstraction. With this patch, only modern virtio device will be supported. This implementation will be used by later QTest extension patch of virtio-net PMD.
Signed-off-by: Tetsuya Mukawa <mukawa at igel.co.jp> --- drivers/net/virtio/Makefile | 1 + drivers/net/virtio/virtio_qtest/virtio_qtest_pci.c | 407 +++++++++++++++++++++ drivers/net/virtio/virtio_qtest/virtio_qtest_pci.h | 39 ++ 3 files changed, 447 insertions(+) create mode 100644 drivers/net/virtio/virtio_qtest/virtio_qtest_pci.c create mode 100644 drivers/net/virtio/virtio_qtest/virtio_qtest_pci.h diff --git a/drivers/net/virtio/Makefile b/drivers/net/virtio/Makefile index 0b1ccff..1c86d9d 100644 --- a/drivers/net/virtio/Makefile +++ b/drivers/net/virtio/Makefile @@ -65,6 +65,7 @@ endif ifeq ($(CONFIG_RTE_VIRTIO_QTEST),y) SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_qtest/qtest_utils.c +SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_qtest/virtio_qtest_pci.c endif # this lib depends upon: diff --git a/drivers/net/virtio/virtio_qtest/virtio_qtest_pci.c b/drivers/net/virtio/virtio_qtest/virtio_qtest_pci.c new file mode 100644 index 0000000..d715b13 --- /dev/null +++ b/drivers/net/virtio/virtio_qtest/virtio_qtest_pci.c @@ -0,0 +1,407 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2016 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdint.h> +#include <sys/types.h> +#include <unistd.h> + +#include "../virtio_logs.h" +#include "../virtio_pci.h" +#include "../virtqueue.h" + +#include "qtest_utils.h" +#include "virtio_qtest_pci.h" + +static inline int +check_vq_phys_addr_ok(struct virtqueue *vq) +{ + /* Virtio PCI device VIRTIO_PCI_QUEUE_PF register is 32bit, + * and only accepts 32 bit page frame number. + * Check if the allocated physical memory exceeds 16TB. + */ + if ((vq->vq_ring_mem + vq->vq_ring_size - 1) >> + (VIRTIO_PCI_QUEUE_ADDR_SHIFT + 32)) { + PMD_INIT_LOG(ERR, "vring address shouldn't be above 16TB!"); + return 0; + } + + return 1; +} + +static inline uint8_t +qtest_read8(struct virtio_hw *hw, uint8_t *addr) +{ + return qtest_read(hw->virtio_user_dev, (uint64_t)addr, 'b'); +} + +static inline void +qtest_write8(struct virtio_hw *hw, uint8_t val, uint8_t *addr) +{ + return qtest_write(hw->virtio_user_dev, (uint64_t)addr, val, 'b'); +} + +static inline uint16_t +qtest_read16(struct virtio_hw *hw, uint16_t *addr) +{ + return qtest_read(hw->virtio_user_dev, (uint64_t)addr, 'w'); +} + +static inline void +qtest_write16(struct virtio_hw *hw, uint16_t val, uint16_t *addr) +{ + return qtest_write(hw->virtio_user_dev, (uint64_t)addr, val, 'w'); +} + +static inline uint32_t +qtest_read32(struct virtio_hw *hw, uint32_t *addr) +{ + return qtest_read(hw->virtio_user_dev, (uint64_t)addr, 'l'); +} + +static inline void +qtest_write32(struct virtio_hw *hw, uint32_t val, uint32_t *addr) +{ + return qtest_write(hw->virtio_user_dev, (uint64_t)addr, val, 'l'); +} + +static inline void +qtest_write64_twopart(struct virtio_hw *hw, + uint64_t val, uint32_t *lo, uint32_t *hi) +{ + qtest_write32(hw, val & ((1ULL << 32) - 1), lo); + qtest_write32(hw, val >> 32, hi); +} + +static void +qtest_read_dev_config(struct virtio_hw *hw, size_t offset, + void *dst, int length) +{ + int i; + uint8_t *p; + uint8_t old_gen, new_gen; + + do { + old_gen = qtest_read8(hw, &hw->common_cfg->config_generation); + + p = dst; + for (i = 0; i < length; i++) + *p++ = qtest_read8(hw, (uint8_t *)hw->dev_cfg + offset + i); + + new_gen = qtest_read8(hw, &hw->common_cfg->config_generation); + } while (old_gen != new_gen); +} + +static void +qtest_write_dev_config(struct virtio_hw *hw, size_t offset, + const void *src, int length) +{ + int i; + const uint8_t *p = src; + + for (i = 0; i < length; i++) + qtest_write8(hw, *p++, (uint8_t *)hw->dev_cfg + offset + i); +} + +static uint64_t +qtest_get_features(struct virtio_hw *hw) +{ + uint32_t features_lo, features_hi; + + qtest_write32(hw, 0, &hw->common_cfg->device_feature_select); + features_lo = qtest_read32(hw, &hw->common_cfg->device_feature); + + qtest_write32(hw, 1, &hw->common_cfg->device_feature_select); + features_hi = qtest_read32(hw, &hw->common_cfg->device_feature); + + return ((uint64_t)features_hi << 32) | features_lo; +} + +static void +qtest_set_features(struct virtio_hw *hw, uint64_t features) +{ + qtest_write32(hw, 0, &hw->common_cfg->guest_feature_select); + qtest_write32(hw, features & ((1ULL << 32) - 1), + &hw->common_cfg->guest_feature); + + qtest_write32(hw, 1, &hw->common_cfg->guest_feature_select); + qtest_write32(hw, features >> 32, + &hw->common_cfg->guest_feature); +} + +static uint8_t +qtest_get_status(struct virtio_hw *hw) +{ + return qtest_read8(hw, &hw->common_cfg->device_status); +} + +static void +qtest_set_status(struct virtio_hw *hw, uint8_t status) +{ + qtest_write8(hw, status, &hw->common_cfg->device_status); +} + +static void +qtest_reset(struct virtio_hw *hw) +{ + qtest_set_status(hw, VIRTIO_CONFIG_STATUS_RESET); + qtest_get_status(hw); +} + +static uint8_t +qtest_get_isr(struct virtio_hw *hw) +{ + return qtest_read8(hw, hw->isr); +} + +static uint16_t +qtest_set_config_irq(struct virtio_hw *hw, uint16_t vec) +{ + qtest_write16(hw, vec, &hw->common_cfg->msix_config); + return qtest_read16(hw, &hw->common_cfg->msix_config); +} + +static uint16_t +qtest_get_queue_num(struct virtio_hw *hw, uint16_t queue_id) +{ + qtest_write16(hw, queue_id, &hw->common_cfg->queue_select); + return qtest_read16(hw, &hw->common_cfg->queue_size); +} + +static int +qtest_setup_queue(struct virtio_hw *hw, struct virtqueue *vq) +{ + uint64_t desc_addr, avail_addr, used_addr; + uint16_t notify_off; + + if (!check_vq_phys_addr_ok(vq)) + return -1; + + desc_addr = (uint64_t)vq->mz->addr; + avail_addr = desc_addr + vq->vq_nentries * sizeof(struct vring_desc); + used_addr = RTE_ALIGN_CEIL(avail_addr + offsetof(struct vring_avail, + ring[vq->vq_nentries]), + VIRTIO_PCI_VRING_ALIGN); + + qtest_write16(hw, vq->vq_queue_index, &hw->common_cfg->queue_select); + + qtest_write64_twopart(hw, desc_addr, &hw->common_cfg->queue_desc_lo, + &hw->common_cfg->queue_desc_hi); + qtest_write64_twopart(hw, avail_addr, &hw->common_cfg->queue_avail_lo, + &hw->common_cfg->queue_avail_hi); + qtest_write64_twopart(hw, used_addr, &hw->common_cfg->queue_used_lo, + &hw->common_cfg->queue_used_hi); + + notify_off = qtest_read16(hw, &hw->common_cfg->queue_notify_off); + vq->notify_addr = (void *)((uint8_t *)hw->notify_base + + notify_off * hw->notify_off_multiplier); + + qtest_write16(hw, 1, &hw->common_cfg->queue_enable); + + PMD_INIT_LOG(DEBUG, "queue %u addresses:", vq->vq_queue_index); + PMD_INIT_LOG(DEBUG, "\t desc_addr: %" PRIx64, desc_addr); + PMD_INIT_LOG(DEBUG, "\t aval_addr: %" PRIx64, avail_addr); + PMD_INIT_LOG(DEBUG, "\t used_addr: %" PRIx64, used_addr); + PMD_INIT_LOG(DEBUG, "\t notify addr: %p (notify offset: %u)", + vq->notify_addr, notify_off); + + return 0; +} + +static void +qtest_del_queue(struct virtio_hw *hw, struct virtqueue *vq) +{ + qtest_write16(hw, vq->vq_queue_index, &hw->common_cfg->queue_select); + + qtest_write64_twopart(hw, 0, &hw->common_cfg->queue_desc_lo, + &hw->common_cfg->queue_desc_hi); + qtest_write64_twopart(hw, 0, &hw->common_cfg->queue_avail_lo, + &hw->common_cfg->queue_avail_hi); + qtest_write64_twopart(hw, 0, &hw->common_cfg->queue_used_lo, + &hw->common_cfg->queue_used_hi); + + qtest_write16(hw, 0, &hw->common_cfg->queue_enable); +} + +static void +qtest_notify_queue(struct virtio_hw *hw __rte_unused, struct virtqueue *vq) +{ + qtest_write16(hw, 1, vq->notify_addr); +} + +const struct virtio_pci_ops modern_qtest_ops = { + .read_dev_cfg = qtest_read_dev_config, + .write_dev_cfg = qtest_write_dev_config, + .reset = qtest_reset, + .get_status = qtest_get_status, + .set_status = qtest_set_status, + .get_features = qtest_get_features, + .set_features = qtest_set_features, + .get_isr = qtest_get_isr, + .set_config_irq = qtest_set_config_irq, + .get_queue_num = qtest_get_queue_num, + .setup_queue = qtest_setup_queue, + .del_queue = qtest_del_queue, + .notify_queue = qtest_notify_queue, +}; + +static void * +get_cfg_addr(struct virtio_hw *hw, struct virtio_pci_cap *cap) +{ + uint8_t bar = cap->bar; + uint32_t length = cap->length; + uint32_t offset = cap->offset; + uint8_t *base = NULL; + uint64_t size = 0; + + if (bar > 5) { + PMD_INIT_LOG(ERR, "invalid bar: %u", bar); + return NULL; + } + + if (offset + length < offset) { + PMD_INIT_LOG(ERR, "offset(%u) + length(%u) overflows", + offset, length); + return NULL; + } + + qtest_get_bar_size(hw->virtio_user_dev, "virtio-net", bar, &size); + qtest_get_bar_addr(hw->virtio_user_dev, "virtio-net", bar, + (uint64_t **)&base); + + if (offset + length > size) { + PMD_INIT_LOG(ERR, + "invalid cap: overflows bar space: %u > %" PRIu64, + offset + length, size); + return NULL; + } + + if (base == NULL) { + PMD_INIT_LOG(ERR, "bar %u base addr is NULL", bar); + return NULL; + } + + return base + offset; +} + +static int +virtio_read_caps(struct virtio_hw *hw) +{ + uint8_t pos; + struct virtio_pci_cap cap; + int ret; + + ret = qtest_read_pci_cfg(hw->virtio_user_dev, "virtio-net", + &pos, 1, PCI_CAPABILITY_LIST); + if (ret < 0) { + PMD_INIT_LOG(DEBUG, "failed to read pci capability list"); + return -1; + } + + while (pos) { + ret = qtest_read_pci_cfg(hw->virtio_user_dev, "virtio-net", + &cap, sizeof(cap), pos); + if (ret < 0) { + PMD_INIT_LOG(ERR, + "failed to read pci cap at pos: %x", pos); + break; + } + + if (cap.cap_vndr != PCI_CAP_ID_VNDR) { + PMD_INIT_LOG(DEBUG, + "[%2x] skipping non VNDR cap id: %02x", + pos, cap.cap_vndr); + goto next; + } + + PMD_INIT_LOG(DEBUG, + "[%2x] cfg type: %u, bar: %u, offset: %04x, len: %u", + pos, cap.cfg_type, cap.bar, cap.offset, cap.length); + + switch (cap.cfg_type) { + case VIRTIO_PCI_CAP_COMMON_CFG: + hw->common_cfg = get_cfg_addr(hw, &cap); + break; + case VIRTIO_PCI_CAP_NOTIFY_CFG: + qtest_read_pci_cfg(hw->virtio_user_dev, "virtio-net", + &hw->notify_off_multiplier, + 4, pos + sizeof(cap)); + hw->notify_base = get_cfg_addr(hw, &cap); + break; + case VIRTIO_PCI_CAP_DEVICE_CFG: + hw->dev_cfg = get_cfg_addr(hw, &cap); + break; + case VIRTIO_PCI_CAP_ISR_CFG: + hw->isr = get_cfg_addr(hw, &cap); + break; + } + +next: + pos = cap.cap_next; + } + + if (hw->common_cfg == NULL || hw->notify_base == NULL || + hw->dev_cfg == NULL || hw->isr == NULL) { + PMD_INIT_LOG(INFO, "no modern virtio pci device found."); + return -1; + } + + PMD_INIT_LOG(INFO, "found modern virtio pci device."); + + PMD_INIT_LOG(DEBUG, "common cfg mapped at: %p", hw->common_cfg); + PMD_INIT_LOG(DEBUG, "device cfg mapped at: %p", hw->dev_cfg); + PMD_INIT_LOG(DEBUG, "isr cfg mapped at: %p", hw->isr); + PMD_INIT_LOG(DEBUG, "notify base: %p, notify off multiplier: %u", + hw->notify_base, hw->notify_off_multiplier); + + return 0; +} + +int +qtest_vtpci_init(struct virtio_hw *hw, uint32_t *dev_flags) +{ + /* + * Try if we can succeed reading virtio pci caps, which exists + * only on modern pci device. + */ + if (virtio_read_caps(hw) == 0) { + PMD_INIT_LOG(INFO, "modern virtio pci detected."); + hw->vtpci_ops = &modern_qtest_ops; + hw->modern = 1; + /* So far, we don't support LSC interrupt */ + *dev_flags = 0; + return 0; + } + + PMD_INIT_LOG(INFO, "So far, legacy virtio device isn't supported"); + return -EFAULT; +} diff --git a/drivers/net/virtio/virtio_qtest/virtio_qtest_pci.h b/drivers/net/virtio/virtio_qtest/virtio_qtest_pci.h new file mode 100644 index 0000000..6024e27 --- /dev/null +++ b/drivers/net/virtio/virtio_qtest/virtio_qtest_pci.h @@ -0,0 +1,39 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2016 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _VIRTIO_QTEST_PCI_H +#define _VIRTIO_QTEST_PCI_H + +int qtest_vtpci_init(struct virtio_hw *hw, uint32_t *dev_flags); + +#endif -- 2.7.4