16c3169a3SBruce Richardson /*- 26c3169a3SBruce Richardson * BSD LICENSE 36c3169a3SBruce Richardson * 46c3169a3SBruce Richardson * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. 56c3169a3SBruce Richardson * All rights reserved. 66c3169a3SBruce Richardson * 76c3169a3SBruce Richardson * Redistribution and use in source and binary forms, with or without 86c3169a3SBruce Richardson * modification, are permitted provided that the following conditions 96c3169a3SBruce Richardson * are met: 106c3169a3SBruce Richardson * 116c3169a3SBruce Richardson * * Redistributions of source code must retain the above copyright 126c3169a3SBruce Richardson * notice, this list of conditions and the following disclaimer. 136c3169a3SBruce Richardson * * Redistributions in binary form must reproduce the above copyright 146c3169a3SBruce Richardson * notice, this list of conditions and the following disclaimer in 156c3169a3SBruce Richardson * the documentation and/or other materials provided with the 166c3169a3SBruce Richardson * distribution. 176c3169a3SBruce Richardson * * Neither the name of Intel Corporation nor the names of its 186c3169a3SBruce Richardson * contributors may be used to endorse or promote products derived 196c3169a3SBruce Richardson * from this software without specific prior written permission. 206c3169a3SBruce Richardson * 216c3169a3SBruce Richardson * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 226c3169a3SBruce Richardson * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 236c3169a3SBruce Richardson * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 246c3169a3SBruce Richardson * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 256c3169a3SBruce Richardson * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 266c3169a3SBruce Richardson * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 276c3169a3SBruce Richardson * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 286c3169a3SBruce Richardson * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 296c3169a3SBruce Richardson * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 306c3169a3SBruce Richardson * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 316c3169a3SBruce Richardson * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 326c3169a3SBruce Richardson */ 336c3169a3SBruce Richardson #include <stdint.h> 346c3169a3SBruce Richardson 35c52afa68SYuanhan Liu #ifdef RTE_EXEC_ENV_LINUXAPP 36c52afa68SYuanhan Liu #include <dirent.h> 37c52afa68SYuanhan Liu #include <fcntl.h> 38c52afa68SYuanhan Liu #endif 39c52afa68SYuanhan Liu 406c3169a3SBruce Richardson #include "virtio_pci.h" 416c3169a3SBruce Richardson #include "virtio_logs.h" 42d5bbeefcSYuanhan Liu #include "virtqueue.h" 436c3169a3SBruce Richardson 446ba1f63bSYuanhan Liu /* 456ba1f63bSYuanhan Liu * Following macros are derived from linux/pci_regs.h, however, 466ba1f63bSYuanhan Liu * we can't simply include that header here, as there is no such 476ba1f63bSYuanhan Liu * file for non-Linux platform. 486ba1f63bSYuanhan Liu */ 496ba1f63bSYuanhan Liu #define PCI_CAPABILITY_LIST 0x34 506ba1f63bSYuanhan Liu #define PCI_CAP_ID_VNDR 0x09 516ba1f63bSYuanhan Liu 52b8f04520SDavid Marchand /* 53b8f04520SDavid Marchand * The remaining space is defined by each driver as the per-driver 54b8f04520SDavid Marchand * configuration space. 55b8f04520SDavid Marchand */ 56b8f04520SDavid Marchand #define VIRTIO_PCI_CONFIG(hw) (((hw)->use_msix) ? 24 : 20) 57b86af7b1SYuanhan Liu 58d5bbeefcSYuanhan Liu static void 59d5bbeefcSYuanhan Liu legacy_read_dev_config(struct virtio_hw *hw, size_t offset, 606c3169a3SBruce Richardson void *dst, int length) 616c3169a3SBruce Richardson { 62b8f04520SDavid Marchand rte_eal_pci_ioport_read(&hw->io, dst, length, 63b8f04520SDavid Marchand VIRTIO_PCI_CONFIG(hw) + offset); 646c3169a3SBruce Richardson } 656c3169a3SBruce Richardson 66d5bbeefcSYuanhan Liu static void 67d5bbeefcSYuanhan Liu legacy_write_dev_config(struct virtio_hw *hw, size_t offset, 68d5bbeefcSYuanhan Liu const void *src, int length) 696c3169a3SBruce Richardson { 70b8f04520SDavid Marchand rte_eal_pci_ioport_write(&hw->io, src, length, 71b8f04520SDavid Marchand VIRTIO_PCI_CONFIG(hw) + offset); 726c3169a3SBruce Richardson } 736c3169a3SBruce Richardson 743891f233SYuanhan Liu static uint64_t 75d5bbeefcSYuanhan Liu legacy_get_features(struct virtio_hw *hw) 76d5bbeefcSYuanhan Liu { 7736ea36efSYuanhan Liu uint32_t dst; 78b8f04520SDavid Marchand 79b8f04520SDavid Marchand rte_eal_pci_ioport_read(&hw->io, &dst, 4, VIRTIO_PCI_HOST_FEATURES); 80b8f04520SDavid Marchand return dst; 81d5bbeefcSYuanhan Liu } 82d5bbeefcSYuanhan Liu 83d5bbeefcSYuanhan Liu static void 843891f233SYuanhan Liu legacy_set_features(struct virtio_hw *hw, uint64_t features) 85d5bbeefcSYuanhan Liu { 863891f233SYuanhan Liu if ((features >> 32) != 0) { 873891f233SYuanhan Liu PMD_DRV_LOG(ERR, 883891f233SYuanhan Liu "only 32 bit features are allowed for legacy virtio!"); 893891f233SYuanhan Liu return; 903891f233SYuanhan Liu } 91b8f04520SDavid Marchand rte_eal_pci_ioport_write(&hw->io, &features, 4, 92b8f04520SDavid Marchand VIRTIO_PCI_GUEST_FEATURES); 93d5bbeefcSYuanhan Liu } 94d5bbeefcSYuanhan Liu 95d5bbeefcSYuanhan Liu static uint8_t 96d5bbeefcSYuanhan Liu legacy_get_status(struct virtio_hw *hw) 97d5bbeefcSYuanhan Liu { 98b8f04520SDavid Marchand uint8_t dst; 99b8f04520SDavid Marchand 100b8f04520SDavid Marchand rte_eal_pci_ioport_read(&hw->io, &dst, 1, VIRTIO_PCI_STATUS); 101b8f04520SDavid Marchand return dst; 102d5bbeefcSYuanhan Liu } 103d5bbeefcSYuanhan Liu 104d5bbeefcSYuanhan Liu static void 105d5bbeefcSYuanhan Liu legacy_set_status(struct virtio_hw *hw, uint8_t status) 106d5bbeefcSYuanhan Liu { 107b8f04520SDavid Marchand rte_eal_pci_ioport_write(&hw->io, &status, 1, VIRTIO_PCI_STATUS); 108d5bbeefcSYuanhan Liu } 109d5bbeefcSYuanhan Liu 110d5bbeefcSYuanhan Liu static void 111d5bbeefcSYuanhan Liu legacy_reset(struct virtio_hw *hw) 112d5bbeefcSYuanhan Liu { 113d5bbeefcSYuanhan Liu legacy_set_status(hw, VIRTIO_CONFIG_STATUS_RESET); 114d5bbeefcSYuanhan Liu } 115d5bbeefcSYuanhan Liu 116d5bbeefcSYuanhan Liu static uint8_t 117d5bbeefcSYuanhan Liu legacy_get_isr(struct virtio_hw *hw) 118d5bbeefcSYuanhan Liu { 119b8f04520SDavid Marchand uint8_t dst; 120b8f04520SDavid Marchand 121b8f04520SDavid Marchand rte_eal_pci_ioport_read(&hw->io, &dst, 1, VIRTIO_PCI_ISR); 122b8f04520SDavid Marchand return dst; 123d5bbeefcSYuanhan Liu } 124d5bbeefcSYuanhan Liu 125d5bbeefcSYuanhan Liu /* Enable one vector (0) for Link State Intrerrupt */ 126d5bbeefcSYuanhan Liu static uint16_t 127d5bbeefcSYuanhan Liu legacy_set_config_irq(struct virtio_hw *hw, uint16_t vec) 128d5bbeefcSYuanhan Liu { 129b8f04520SDavid Marchand uint16_t dst; 130b8f04520SDavid Marchand 131b8f04520SDavid Marchand rte_eal_pci_ioport_write(&hw->io, &vec, 2, VIRTIO_MSI_CONFIG_VECTOR); 132b8f04520SDavid Marchand rte_eal_pci_ioport_read(&hw->io, &dst, 2, VIRTIO_MSI_CONFIG_VECTOR); 133b8f04520SDavid Marchand return dst; 134d5bbeefcSYuanhan Liu } 135d5bbeefcSYuanhan Liu 136d5bbeefcSYuanhan Liu static uint16_t 137d5bbeefcSYuanhan Liu legacy_get_queue_num(struct virtio_hw *hw, uint16_t queue_id) 138d5bbeefcSYuanhan Liu { 139b8f04520SDavid Marchand uint16_t dst; 140b8f04520SDavid Marchand 141b8f04520SDavid Marchand rte_eal_pci_ioport_write(&hw->io, &queue_id, 2, VIRTIO_PCI_QUEUE_SEL); 142b8f04520SDavid Marchand rte_eal_pci_ioport_read(&hw->io, &dst, 2, VIRTIO_PCI_QUEUE_NUM); 143b8f04520SDavid Marchand return dst; 144d5bbeefcSYuanhan Liu } 145d5bbeefcSYuanhan Liu 146d5bbeefcSYuanhan Liu static void 147d5bbeefcSYuanhan Liu legacy_setup_queue(struct virtio_hw *hw, struct virtqueue *vq) 148d5bbeefcSYuanhan Liu { 149b8f04520SDavid Marchand uint32_t src; 150d5bbeefcSYuanhan Liu 151b8f04520SDavid Marchand rte_eal_pci_ioport_write(&hw->io, &vq->vq_queue_index, 2, 152b8f04520SDavid Marchand VIRTIO_PCI_QUEUE_SEL); 153b8f04520SDavid Marchand src = vq->mz->phys_addr >> VIRTIO_PCI_QUEUE_ADDR_SHIFT; 154b8f04520SDavid Marchand rte_eal_pci_ioport_write(&hw->io, &src, 4, VIRTIO_PCI_QUEUE_PFN); 155d5bbeefcSYuanhan Liu } 156d5bbeefcSYuanhan Liu 157d5bbeefcSYuanhan Liu static void 158d5bbeefcSYuanhan Liu legacy_del_queue(struct virtio_hw *hw, struct virtqueue *vq) 159d5bbeefcSYuanhan Liu { 160b8f04520SDavid Marchand uint32_t src = 0; 161d5bbeefcSYuanhan Liu 162b8f04520SDavid Marchand rte_eal_pci_ioport_write(&hw->io, &vq->vq_queue_index, 2, 163b8f04520SDavid Marchand VIRTIO_PCI_QUEUE_SEL); 164b8f04520SDavid Marchand rte_eal_pci_ioport_write(&hw->io, &src, 4, VIRTIO_PCI_QUEUE_PFN); 165d5bbeefcSYuanhan Liu } 166d5bbeefcSYuanhan Liu 167d5bbeefcSYuanhan Liu static void 168d5bbeefcSYuanhan Liu legacy_notify_queue(struct virtio_hw *hw, struct virtqueue *vq) 169d5bbeefcSYuanhan Liu { 170b8f04520SDavid Marchand rte_eal_pci_ioport_write(&hw->io, &vq->vq_queue_index, 2, 171b8f04520SDavid Marchand VIRTIO_PCI_QUEUE_NOTIFY); 172d5bbeefcSYuanhan Liu } 173d5bbeefcSYuanhan Liu 174c52afa68SYuanhan Liu #ifdef RTE_EXEC_ENV_LINUXAPP 175c52afa68SYuanhan Liu static int 176c52afa68SYuanhan Liu legacy_virtio_has_msix(const struct rte_pci_addr *loc) 177c52afa68SYuanhan Liu { 178c52afa68SYuanhan Liu DIR *d; 179c52afa68SYuanhan Liu char dirname[PATH_MAX]; 180c52afa68SYuanhan Liu 181c52afa68SYuanhan Liu snprintf(dirname, sizeof(dirname), 182c52afa68SYuanhan Liu SYSFS_PCI_DEVICES "/" PCI_PRI_FMT "/msi_irqs", 183c52afa68SYuanhan Liu loc->domain, loc->bus, loc->devid, loc->function); 184c52afa68SYuanhan Liu 185c52afa68SYuanhan Liu d = opendir(dirname); 186c52afa68SYuanhan Liu if (d) 187c52afa68SYuanhan Liu closedir(d); 188c52afa68SYuanhan Liu 189693f715dSHuawei Xie return d != NULL; 190c52afa68SYuanhan Liu } 191c52afa68SYuanhan Liu #else 192c52afa68SYuanhan Liu static int 19325294cd3SDavid Marchand legacy_virtio_has_msix(const struct rte_pci_addr *loc __rte_unused) 194c52afa68SYuanhan Liu { 195c52afa68SYuanhan Liu /* nic_uio does not enable interrupts, return 0 (false). */ 196c52afa68SYuanhan Liu return 0; 197c52afa68SYuanhan Liu } 198b8f04520SDavid Marchand #endif 199c52afa68SYuanhan Liu 200c52afa68SYuanhan Liu static int 201b8f04520SDavid Marchand legacy_virtio_resource_init(struct rte_pci_device *pci_dev, 202*62a785a6SJianfeng Tan struct virtio_hw *hw, uint32_t *dev_flags) 203c52afa68SYuanhan Liu { 204b8f04520SDavid Marchand if (rte_eal_pci_ioport_map(pci_dev, 0, &hw->io) < 0) 205b8f04520SDavid Marchand return -1; 206b8f04520SDavid Marchand 207b8f04520SDavid Marchand if (pci_dev->intr_handle.type != RTE_INTR_HANDLE_UNKNOWN) 208*62a785a6SJianfeng Tan *dev_flags |= RTE_ETH_DEV_INTR_LSC; 209b8f04520SDavid Marchand else 210*62a785a6SJianfeng Tan *dev_flags &= ~RTE_ETH_DEV_INTR_LSC; 211b8f04520SDavid Marchand 212c52afa68SYuanhan Liu return 0; 213c52afa68SYuanhan Liu } 214d5bbeefcSYuanhan Liu 215d5bbeefcSYuanhan Liu static const struct virtio_pci_ops legacy_ops = { 216d5bbeefcSYuanhan Liu .read_dev_cfg = legacy_read_dev_config, 217d5bbeefcSYuanhan Liu .write_dev_cfg = legacy_write_dev_config, 218d5bbeefcSYuanhan Liu .reset = legacy_reset, 219d5bbeefcSYuanhan Liu .get_status = legacy_get_status, 220d5bbeefcSYuanhan Liu .set_status = legacy_set_status, 221d5bbeefcSYuanhan Liu .get_features = legacy_get_features, 222d5bbeefcSYuanhan Liu .set_features = legacy_set_features, 223d5bbeefcSYuanhan Liu .get_isr = legacy_get_isr, 224d5bbeefcSYuanhan Liu .set_config_irq = legacy_set_config_irq, 225d5bbeefcSYuanhan Liu .get_queue_num = legacy_get_queue_num, 226d5bbeefcSYuanhan Liu .setup_queue = legacy_setup_queue, 227d5bbeefcSYuanhan Liu .del_queue = legacy_del_queue, 228d5bbeefcSYuanhan Liu .notify_queue = legacy_notify_queue, 229d5bbeefcSYuanhan Liu }; 230d5bbeefcSYuanhan Liu 231d5bbeefcSYuanhan Liu 2326ba1f63bSYuanhan Liu static inline uint8_t 2336ba1f63bSYuanhan Liu io_read8(uint8_t *addr) 2346ba1f63bSYuanhan Liu { 2356ba1f63bSYuanhan Liu return *(volatile uint8_t *)addr; 2366ba1f63bSYuanhan Liu } 2376ba1f63bSYuanhan Liu 2386ba1f63bSYuanhan Liu static inline void 2396ba1f63bSYuanhan Liu io_write8(uint8_t val, uint8_t *addr) 2406ba1f63bSYuanhan Liu { 2416ba1f63bSYuanhan Liu *(volatile uint8_t *)addr = val; 2426ba1f63bSYuanhan Liu } 2436ba1f63bSYuanhan Liu 2446ba1f63bSYuanhan Liu static inline uint16_t 2456ba1f63bSYuanhan Liu io_read16(uint16_t *addr) 2466ba1f63bSYuanhan Liu { 2476ba1f63bSYuanhan Liu return *(volatile uint16_t *)addr; 2486ba1f63bSYuanhan Liu } 2496ba1f63bSYuanhan Liu 2506ba1f63bSYuanhan Liu static inline void 2516ba1f63bSYuanhan Liu io_write16(uint16_t val, uint16_t *addr) 2526ba1f63bSYuanhan Liu { 2536ba1f63bSYuanhan Liu *(volatile uint16_t *)addr = val; 2546ba1f63bSYuanhan Liu } 2556ba1f63bSYuanhan Liu 2566ba1f63bSYuanhan Liu static inline uint32_t 2576ba1f63bSYuanhan Liu io_read32(uint32_t *addr) 2586ba1f63bSYuanhan Liu { 2596ba1f63bSYuanhan Liu return *(volatile uint32_t *)addr; 2606ba1f63bSYuanhan Liu } 2616ba1f63bSYuanhan Liu 2626ba1f63bSYuanhan Liu static inline void 2636ba1f63bSYuanhan Liu io_write32(uint32_t val, uint32_t *addr) 2646ba1f63bSYuanhan Liu { 2656ba1f63bSYuanhan Liu *(volatile uint32_t *)addr = val; 2666ba1f63bSYuanhan Liu } 2676ba1f63bSYuanhan Liu 2686ba1f63bSYuanhan Liu static inline void 2696ba1f63bSYuanhan Liu io_write64_twopart(uint64_t val, uint32_t *lo, uint32_t *hi) 2706ba1f63bSYuanhan Liu { 2716ba1f63bSYuanhan Liu io_write32(val & ((1ULL << 32) - 1), lo); 2726ba1f63bSYuanhan Liu io_write32(val >> 32, hi); 2736ba1f63bSYuanhan Liu } 2746ba1f63bSYuanhan Liu 2756ba1f63bSYuanhan Liu static void 2766ba1f63bSYuanhan Liu modern_read_dev_config(struct virtio_hw *hw, size_t offset, 2776ba1f63bSYuanhan Liu void *dst, int length) 2786ba1f63bSYuanhan Liu { 2796ba1f63bSYuanhan Liu int i; 2806ba1f63bSYuanhan Liu uint8_t *p; 2816ba1f63bSYuanhan Liu uint8_t old_gen, new_gen; 2826ba1f63bSYuanhan Liu 2836ba1f63bSYuanhan Liu do { 2846ba1f63bSYuanhan Liu old_gen = io_read8(&hw->common_cfg->config_generation); 2856ba1f63bSYuanhan Liu 2866ba1f63bSYuanhan Liu p = dst; 2876ba1f63bSYuanhan Liu for (i = 0; i < length; i++) 2886ba1f63bSYuanhan Liu *p++ = io_read8((uint8_t *)hw->dev_cfg + offset + i); 2896ba1f63bSYuanhan Liu 2906ba1f63bSYuanhan Liu new_gen = io_read8(&hw->common_cfg->config_generation); 2916ba1f63bSYuanhan Liu } while (old_gen != new_gen); 2926ba1f63bSYuanhan Liu } 2936ba1f63bSYuanhan Liu 2946ba1f63bSYuanhan Liu static void 2956ba1f63bSYuanhan Liu modern_write_dev_config(struct virtio_hw *hw, size_t offset, 2966ba1f63bSYuanhan Liu const void *src, int length) 2976ba1f63bSYuanhan Liu { 2986ba1f63bSYuanhan Liu int i; 2996ba1f63bSYuanhan Liu const uint8_t *p = src; 3006ba1f63bSYuanhan Liu 3016ba1f63bSYuanhan Liu for (i = 0; i < length; i++) 3026ba1f63bSYuanhan Liu io_write8(*p++, (uint8_t *)hw->dev_cfg + offset + i); 3036ba1f63bSYuanhan Liu } 3046ba1f63bSYuanhan Liu 3056ba1f63bSYuanhan Liu static uint64_t 3066ba1f63bSYuanhan Liu modern_get_features(struct virtio_hw *hw) 3076ba1f63bSYuanhan Liu { 3086ba1f63bSYuanhan Liu uint32_t features_lo, features_hi; 3096ba1f63bSYuanhan Liu 3106ba1f63bSYuanhan Liu io_write32(0, &hw->common_cfg->device_feature_select); 3116ba1f63bSYuanhan Liu features_lo = io_read32(&hw->common_cfg->device_feature); 3126ba1f63bSYuanhan Liu 3136ba1f63bSYuanhan Liu io_write32(1, &hw->common_cfg->device_feature_select); 3146ba1f63bSYuanhan Liu features_hi = io_read32(&hw->common_cfg->device_feature); 3156ba1f63bSYuanhan Liu 3166ba1f63bSYuanhan Liu return ((uint64_t)features_hi << 32) | features_lo; 3176ba1f63bSYuanhan Liu } 3186ba1f63bSYuanhan Liu 3196ba1f63bSYuanhan Liu static void 3206ba1f63bSYuanhan Liu modern_set_features(struct virtio_hw *hw, uint64_t features) 3216ba1f63bSYuanhan Liu { 3226ba1f63bSYuanhan Liu io_write32(0, &hw->common_cfg->guest_feature_select); 3236ba1f63bSYuanhan Liu io_write32(features & ((1ULL << 32) - 1), 3246ba1f63bSYuanhan Liu &hw->common_cfg->guest_feature); 3256ba1f63bSYuanhan Liu 3266ba1f63bSYuanhan Liu io_write32(1, &hw->common_cfg->guest_feature_select); 3276ba1f63bSYuanhan Liu io_write32(features >> 32, 3286ba1f63bSYuanhan Liu &hw->common_cfg->guest_feature); 3296ba1f63bSYuanhan Liu } 3306ba1f63bSYuanhan Liu 3316ba1f63bSYuanhan Liu static uint8_t 3326ba1f63bSYuanhan Liu modern_get_status(struct virtio_hw *hw) 3336ba1f63bSYuanhan Liu { 3346ba1f63bSYuanhan Liu return io_read8(&hw->common_cfg->device_status); 3356ba1f63bSYuanhan Liu } 3366ba1f63bSYuanhan Liu 3376ba1f63bSYuanhan Liu static void 3386ba1f63bSYuanhan Liu modern_set_status(struct virtio_hw *hw, uint8_t status) 3396ba1f63bSYuanhan Liu { 3406ba1f63bSYuanhan Liu io_write8(status, &hw->common_cfg->device_status); 3416ba1f63bSYuanhan Liu } 3426ba1f63bSYuanhan Liu 3436ba1f63bSYuanhan Liu static void 3446ba1f63bSYuanhan Liu modern_reset(struct virtio_hw *hw) 3456ba1f63bSYuanhan Liu { 3466ba1f63bSYuanhan Liu modern_set_status(hw, VIRTIO_CONFIG_STATUS_RESET); 3476ba1f63bSYuanhan Liu modern_get_status(hw); 3486ba1f63bSYuanhan Liu } 3496ba1f63bSYuanhan Liu 3506ba1f63bSYuanhan Liu static uint8_t 3516ba1f63bSYuanhan Liu modern_get_isr(struct virtio_hw *hw) 3526ba1f63bSYuanhan Liu { 3536ba1f63bSYuanhan Liu return io_read8(hw->isr); 3546ba1f63bSYuanhan Liu } 3556ba1f63bSYuanhan Liu 3566ba1f63bSYuanhan Liu static uint16_t 3576ba1f63bSYuanhan Liu modern_set_config_irq(struct virtio_hw *hw, uint16_t vec) 3586ba1f63bSYuanhan Liu { 3596ba1f63bSYuanhan Liu io_write16(vec, &hw->common_cfg->msix_config); 3606ba1f63bSYuanhan Liu return io_read16(&hw->common_cfg->msix_config); 3616ba1f63bSYuanhan Liu } 3626ba1f63bSYuanhan Liu 3636ba1f63bSYuanhan Liu static uint16_t 3646ba1f63bSYuanhan Liu modern_get_queue_num(struct virtio_hw *hw, uint16_t queue_id) 3656ba1f63bSYuanhan Liu { 3666ba1f63bSYuanhan Liu io_write16(queue_id, &hw->common_cfg->queue_select); 3676ba1f63bSYuanhan Liu return io_read16(&hw->common_cfg->queue_size); 3686ba1f63bSYuanhan Liu } 3696ba1f63bSYuanhan Liu 3706ba1f63bSYuanhan Liu static void 3716ba1f63bSYuanhan Liu modern_setup_queue(struct virtio_hw *hw, struct virtqueue *vq) 3726ba1f63bSYuanhan Liu { 3736ba1f63bSYuanhan Liu uint64_t desc_addr, avail_addr, used_addr; 3746ba1f63bSYuanhan Liu uint16_t notify_off; 3756ba1f63bSYuanhan Liu 3766ba1f63bSYuanhan Liu desc_addr = vq->mz->phys_addr; 3776ba1f63bSYuanhan Liu avail_addr = desc_addr + vq->vq_nentries * sizeof(struct vring_desc); 3786ba1f63bSYuanhan Liu used_addr = RTE_ALIGN_CEIL(avail_addr + offsetof(struct vring_avail, 3796ba1f63bSYuanhan Liu ring[vq->vq_nentries]), 3806ba1f63bSYuanhan Liu VIRTIO_PCI_VRING_ALIGN); 3816ba1f63bSYuanhan Liu 3826ba1f63bSYuanhan Liu io_write16(vq->vq_queue_index, &hw->common_cfg->queue_select); 3836ba1f63bSYuanhan Liu 3846ba1f63bSYuanhan Liu io_write64_twopart(desc_addr, &hw->common_cfg->queue_desc_lo, 3856ba1f63bSYuanhan Liu &hw->common_cfg->queue_desc_hi); 3866ba1f63bSYuanhan Liu io_write64_twopart(avail_addr, &hw->common_cfg->queue_avail_lo, 3876ba1f63bSYuanhan Liu &hw->common_cfg->queue_avail_hi); 3886ba1f63bSYuanhan Liu io_write64_twopart(used_addr, &hw->common_cfg->queue_used_lo, 3896ba1f63bSYuanhan Liu &hw->common_cfg->queue_used_hi); 3906ba1f63bSYuanhan Liu 3916ba1f63bSYuanhan Liu notify_off = io_read16(&hw->common_cfg->queue_notify_off); 3926ba1f63bSYuanhan Liu vq->notify_addr = (void *)((uint8_t *)hw->notify_base + 3936ba1f63bSYuanhan Liu notify_off * hw->notify_off_multiplier); 3946ba1f63bSYuanhan Liu 3956ba1f63bSYuanhan Liu io_write16(1, &hw->common_cfg->queue_enable); 3966ba1f63bSYuanhan Liu 3976ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "queue %u addresses:", vq->vq_queue_index); 3986ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "\t desc_addr: %" PRIx64, desc_addr); 3996ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "\t aval_addr: %" PRIx64, avail_addr); 4006ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "\t used_addr: %" PRIx64, used_addr); 4016ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "\t notify addr: %p (notify offset: %u)", 4026ba1f63bSYuanhan Liu vq->notify_addr, notify_off); 4036ba1f63bSYuanhan Liu } 4046ba1f63bSYuanhan Liu 4056ba1f63bSYuanhan Liu static void 4066ba1f63bSYuanhan Liu modern_del_queue(struct virtio_hw *hw, struct virtqueue *vq) 4076ba1f63bSYuanhan Liu { 4086ba1f63bSYuanhan Liu io_write16(vq->vq_queue_index, &hw->common_cfg->queue_select); 4096ba1f63bSYuanhan Liu 4106ba1f63bSYuanhan Liu io_write64_twopart(0, &hw->common_cfg->queue_desc_lo, 4116ba1f63bSYuanhan Liu &hw->common_cfg->queue_desc_hi); 4126ba1f63bSYuanhan Liu io_write64_twopart(0, &hw->common_cfg->queue_avail_lo, 4136ba1f63bSYuanhan Liu &hw->common_cfg->queue_avail_hi); 4146ba1f63bSYuanhan Liu io_write64_twopart(0, &hw->common_cfg->queue_used_lo, 4156ba1f63bSYuanhan Liu &hw->common_cfg->queue_used_hi); 4166ba1f63bSYuanhan Liu 4176ba1f63bSYuanhan Liu io_write16(0, &hw->common_cfg->queue_enable); 4186ba1f63bSYuanhan Liu } 4196ba1f63bSYuanhan Liu 4206ba1f63bSYuanhan Liu static void 4216ba1f63bSYuanhan Liu modern_notify_queue(struct virtio_hw *hw __rte_unused, struct virtqueue *vq) 4226ba1f63bSYuanhan Liu { 4236ba1f63bSYuanhan Liu io_write16(1, vq->notify_addr); 4246ba1f63bSYuanhan Liu } 4256ba1f63bSYuanhan Liu 4266ba1f63bSYuanhan Liu static const struct virtio_pci_ops modern_ops = { 4276ba1f63bSYuanhan Liu .read_dev_cfg = modern_read_dev_config, 4286ba1f63bSYuanhan Liu .write_dev_cfg = modern_write_dev_config, 4296ba1f63bSYuanhan Liu .reset = modern_reset, 4306ba1f63bSYuanhan Liu .get_status = modern_get_status, 4316ba1f63bSYuanhan Liu .set_status = modern_set_status, 4326ba1f63bSYuanhan Liu .get_features = modern_get_features, 4336ba1f63bSYuanhan Liu .set_features = modern_set_features, 4346ba1f63bSYuanhan Liu .get_isr = modern_get_isr, 4356ba1f63bSYuanhan Liu .set_config_irq = modern_set_config_irq, 4366ba1f63bSYuanhan Liu .get_queue_num = modern_get_queue_num, 4376ba1f63bSYuanhan Liu .setup_queue = modern_setup_queue, 4386ba1f63bSYuanhan Liu .del_queue = modern_del_queue, 4396ba1f63bSYuanhan Liu .notify_queue = modern_notify_queue, 4406ba1f63bSYuanhan Liu }; 4416ba1f63bSYuanhan Liu 4426ba1f63bSYuanhan Liu 443d5bbeefcSYuanhan Liu void 444d5bbeefcSYuanhan Liu vtpci_read_dev_config(struct virtio_hw *hw, size_t offset, 445d5bbeefcSYuanhan Liu void *dst, int length) 446d5bbeefcSYuanhan Liu { 447d5bbeefcSYuanhan Liu hw->vtpci_ops->read_dev_cfg(hw, offset, dst, length); 448d5bbeefcSYuanhan Liu } 449d5bbeefcSYuanhan Liu 450d5bbeefcSYuanhan Liu void 451d5bbeefcSYuanhan Liu vtpci_write_dev_config(struct virtio_hw *hw, size_t offset, 452d5bbeefcSYuanhan Liu const void *src, int length) 453d5bbeefcSYuanhan Liu { 454d5bbeefcSYuanhan Liu hw->vtpci_ops->write_dev_cfg(hw, offset, src, length); 455d5bbeefcSYuanhan Liu } 456d5bbeefcSYuanhan Liu 4573891f233SYuanhan Liu uint64_t 4583891f233SYuanhan Liu vtpci_negotiate_features(struct virtio_hw *hw, uint64_t host_features) 4596c3169a3SBruce Richardson { 4603891f233SYuanhan Liu uint64_t features; 461d5bbeefcSYuanhan Liu 4626c3169a3SBruce Richardson /* 4636c3169a3SBruce Richardson * Limit negotiated features to what the driver, virtqueue, and 4646c3169a3SBruce Richardson * host all support. 4656c3169a3SBruce Richardson */ 4666c3169a3SBruce Richardson features = host_features & hw->guest_features; 467d5bbeefcSYuanhan Liu hw->vtpci_ops->set_features(hw, features); 4686c3169a3SBruce Richardson 4696c3169a3SBruce Richardson return features; 4706c3169a3SBruce Richardson } 4716c3169a3SBruce Richardson 4726c3169a3SBruce Richardson void 4736c3169a3SBruce Richardson vtpci_reset(struct virtio_hw *hw) 4746c3169a3SBruce Richardson { 475d5bbeefcSYuanhan Liu hw->vtpci_ops->set_status(hw, VIRTIO_CONFIG_STATUS_RESET); 476d5bbeefcSYuanhan Liu /* flush status write */ 477d5bbeefcSYuanhan Liu hw->vtpci_ops->get_status(hw); 4786c3169a3SBruce Richardson } 4796c3169a3SBruce Richardson 4806c3169a3SBruce Richardson void 4816c3169a3SBruce Richardson vtpci_reinit_complete(struct virtio_hw *hw) 4826c3169a3SBruce Richardson { 4836c3169a3SBruce Richardson vtpci_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER_OK); 4846c3169a3SBruce Richardson } 4856c3169a3SBruce Richardson 4866c3169a3SBruce Richardson void 4876c3169a3SBruce Richardson vtpci_set_status(struct virtio_hw *hw, uint8_t status) 4886c3169a3SBruce Richardson { 4896c3169a3SBruce Richardson if (status != VIRTIO_CONFIG_STATUS_RESET) 490d5bbeefcSYuanhan Liu status |= hw->vtpci_ops->get_status(hw); 4916c3169a3SBruce Richardson 492d5bbeefcSYuanhan Liu hw->vtpci_ops->set_status(hw, status); 4936c3169a3SBruce Richardson } 4946c3169a3SBruce Richardson 4956c3169a3SBruce Richardson uint8_t 4966ba1f63bSYuanhan Liu vtpci_get_status(struct virtio_hw *hw) 4976ba1f63bSYuanhan Liu { 4986ba1f63bSYuanhan Liu return hw->vtpci_ops->get_status(hw); 4996ba1f63bSYuanhan Liu } 5006ba1f63bSYuanhan Liu 5016ba1f63bSYuanhan Liu uint8_t 5026c3169a3SBruce Richardson vtpci_isr(struct virtio_hw *hw) 5036c3169a3SBruce Richardson { 504d5bbeefcSYuanhan Liu return hw->vtpci_ops->get_isr(hw); 5056c3169a3SBruce Richardson } 5066c3169a3SBruce Richardson 5076c3169a3SBruce Richardson 5086c3169a3SBruce Richardson /* Enable one vector (0) for Link State Intrerrupt */ 5096c3169a3SBruce Richardson uint16_t 5106c3169a3SBruce Richardson vtpci_irq_config(struct virtio_hw *hw, uint16_t vec) 5116c3169a3SBruce Richardson { 512d5bbeefcSYuanhan Liu return hw->vtpci_ops->set_config_irq(hw, vec); 513d5bbeefcSYuanhan Liu } 514d5bbeefcSYuanhan Liu 5156ba1f63bSYuanhan Liu static void * 5166ba1f63bSYuanhan Liu get_cfg_addr(struct rte_pci_device *dev, struct virtio_pci_cap *cap) 5176ba1f63bSYuanhan Liu { 5186ba1f63bSYuanhan Liu uint8_t bar = cap->bar; 5196ba1f63bSYuanhan Liu uint32_t length = cap->length; 5206ba1f63bSYuanhan Liu uint32_t offset = cap->offset; 5216ba1f63bSYuanhan Liu uint8_t *base; 5226ba1f63bSYuanhan Liu 5236ba1f63bSYuanhan Liu if (bar > 5) { 5246ba1f63bSYuanhan Liu PMD_INIT_LOG(ERR, "invalid bar: %u", bar); 5256ba1f63bSYuanhan Liu return NULL; 5266ba1f63bSYuanhan Liu } 5276ba1f63bSYuanhan Liu 5286ba1f63bSYuanhan Liu if (offset + length < offset) { 5296ba1f63bSYuanhan Liu PMD_INIT_LOG(ERR, "offset(%u) + length(%u) overflows", 5306ba1f63bSYuanhan Liu offset, length); 5316ba1f63bSYuanhan Liu return NULL; 5326ba1f63bSYuanhan Liu } 5336ba1f63bSYuanhan Liu 5346ba1f63bSYuanhan Liu if (offset + length > dev->mem_resource[bar].len) { 5356ba1f63bSYuanhan Liu PMD_INIT_LOG(ERR, 5366ba1f63bSYuanhan Liu "invalid cap: overflows bar space: %u > %" PRIu64, 5376ba1f63bSYuanhan Liu offset + length, dev->mem_resource[bar].len); 5386ba1f63bSYuanhan Liu return NULL; 5396ba1f63bSYuanhan Liu } 5406ba1f63bSYuanhan Liu 5416ba1f63bSYuanhan Liu base = dev->mem_resource[bar].addr; 5426ba1f63bSYuanhan Liu if (base == NULL) { 5436ba1f63bSYuanhan Liu PMD_INIT_LOG(ERR, "bar %u base addr is NULL", bar); 5446ba1f63bSYuanhan Liu return NULL; 5456ba1f63bSYuanhan Liu } 5466ba1f63bSYuanhan Liu 5476ba1f63bSYuanhan Liu return base + offset; 5486ba1f63bSYuanhan Liu } 5496ba1f63bSYuanhan Liu 5506ba1f63bSYuanhan Liu static int 5516ba1f63bSYuanhan Liu virtio_read_caps(struct rte_pci_device *dev, struct virtio_hw *hw) 5526ba1f63bSYuanhan Liu { 5536ba1f63bSYuanhan Liu uint8_t pos; 5546ba1f63bSYuanhan Liu struct virtio_pci_cap cap; 5556ba1f63bSYuanhan Liu int ret; 5566ba1f63bSYuanhan Liu 5577a66c72dSDavid Marchand if (rte_eal_pci_map_device(dev)) { 5586ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "failed to map pci device!"); 5596ba1f63bSYuanhan Liu return -1; 5606ba1f63bSYuanhan Liu } 5616ba1f63bSYuanhan Liu 5626ba1f63bSYuanhan Liu ret = rte_eal_pci_read_config(dev, &pos, 1, PCI_CAPABILITY_LIST); 5636ba1f63bSYuanhan Liu if (ret < 0) { 5646ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "failed to read pci capability list"); 5656ba1f63bSYuanhan Liu return -1; 5666ba1f63bSYuanhan Liu } 5676ba1f63bSYuanhan Liu 5686ba1f63bSYuanhan Liu while (pos) { 5696ba1f63bSYuanhan Liu ret = rte_eal_pci_read_config(dev, &cap, sizeof(cap), pos); 5706ba1f63bSYuanhan Liu if (ret < 0) { 5716ba1f63bSYuanhan Liu PMD_INIT_LOG(ERR, 5726ba1f63bSYuanhan Liu "failed to read pci cap at pos: %x", pos); 5736ba1f63bSYuanhan Liu break; 5746ba1f63bSYuanhan Liu } 5756ba1f63bSYuanhan Liu 5766ba1f63bSYuanhan Liu if (cap.cap_vndr != PCI_CAP_ID_VNDR) { 5776ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, 5786ba1f63bSYuanhan Liu "[%2x] skipping non VNDR cap id: %02x", 5796ba1f63bSYuanhan Liu pos, cap.cap_vndr); 5806ba1f63bSYuanhan Liu goto next; 5816ba1f63bSYuanhan Liu } 5826ba1f63bSYuanhan Liu 5836ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, 5846ba1f63bSYuanhan Liu "[%2x] cfg type: %u, bar: %u, offset: %04x, len: %u", 5856ba1f63bSYuanhan Liu pos, cap.cfg_type, cap.bar, cap.offset, cap.length); 5866ba1f63bSYuanhan Liu 5876ba1f63bSYuanhan Liu switch (cap.cfg_type) { 5886ba1f63bSYuanhan Liu case VIRTIO_PCI_CAP_COMMON_CFG: 5896ba1f63bSYuanhan Liu hw->common_cfg = get_cfg_addr(dev, &cap); 5906ba1f63bSYuanhan Liu break; 5916ba1f63bSYuanhan Liu case VIRTIO_PCI_CAP_NOTIFY_CFG: 5926ba1f63bSYuanhan Liu rte_eal_pci_read_config(dev, &hw->notify_off_multiplier, 5936ba1f63bSYuanhan Liu 4, pos + sizeof(cap)); 5946ba1f63bSYuanhan Liu hw->notify_base = get_cfg_addr(dev, &cap); 5956ba1f63bSYuanhan Liu break; 5966ba1f63bSYuanhan Liu case VIRTIO_PCI_CAP_DEVICE_CFG: 5976ba1f63bSYuanhan Liu hw->dev_cfg = get_cfg_addr(dev, &cap); 5986ba1f63bSYuanhan Liu break; 5996ba1f63bSYuanhan Liu case VIRTIO_PCI_CAP_ISR_CFG: 6006ba1f63bSYuanhan Liu hw->isr = get_cfg_addr(dev, &cap); 6016ba1f63bSYuanhan Liu break; 6026ba1f63bSYuanhan Liu } 6036ba1f63bSYuanhan Liu 6046ba1f63bSYuanhan Liu next: 6056ba1f63bSYuanhan Liu pos = cap.cap_next; 6066ba1f63bSYuanhan Liu } 6076ba1f63bSYuanhan Liu 6086ba1f63bSYuanhan Liu if (hw->common_cfg == NULL || hw->notify_base == NULL || 6096ba1f63bSYuanhan Liu hw->dev_cfg == NULL || hw->isr == NULL) { 6106ba1f63bSYuanhan Liu PMD_INIT_LOG(INFO, "no modern virtio pci device found."); 6116ba1f63bSYuanhan Liu return -1; 6126ba1f63bSYuanhan Liu } 6136ba1f63bSYuanhan Liu 6146ba1f63bSYuanhan Liu PMD_INIT_LOG(INFO, "found modern virtio pci device."); 6156ba1f63bSYuanhan Liu 6166ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "common cfg mapped at: %p", hw->common_cfg); 6176ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "device cfg mapped at: %p", hw->dev_cfg); 6186ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "isr cfg mapped at: %p", hw->isr); 6196ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "notify base: %p, notify off multiplier: %u", 6206ba1f63bSYuanhan Liu hw->notify_base, hw->notify_off_multiplier); 6216ba1f63bSYuanhan Liu 6226ba1f63bSYuanhan Liu return 0; 6236ba1f63bSYuanhan Liu } 6246ba1f63bSYuanhan Liu 625ac5e1d83SHuawei Xie /* 626ac5e1d83SHuawei Xie * Return -1: 627ac5e1d83SHuawei Xie * if there is error mapping with VFIO/UIO. 628ac5e1d83SHuawei Xie * if port map error when driver type is KDRV_NONE. 629ac5e1d83SHuawei Xie * Return 1 if kernel driver is managing the device. 630ac5e1d83SHuawei Xie * Return 0 on success. 631ac5e1d83SHuawei Xie */ 632d5bbeefcSYuanhan Liu int 633*62a785a6SJianfeng Tan vtpci_init(struct rte_pci_device *dev, struct virtio_hw *hw, 634*62a785a6SJianfeng Tan uint32_t *dev_flags) 635d5bbeefcSYuanhan Liu { 6366ba1f63bSYuanhan Liu hw->dev = dev; 637d5bbeefcSYuanhan Liu 6386ba1f63bSYuanhan Liu /* 6396ba1f63bSYuanhan Liu * Try if we can succeed reading virtio pci caps, which exists 6406ba1f63bSYuanhan Liu * only on modern pci device. If failed, we fallback to legacy 6416ba1f63bSYuanhan Liu * virtio handling. 6426ba1f63bSYuanhan Liu */ 6436ba1f63bSYuanhan Liu if (virtio_read_caps(dev, hw) == 0) { 6446ba1f63bSYuanhan Liu PMD_INIT_LOG(INFO, "modern virtio pci detected."); 6456ba1f63bSYuanhan Liu hw->vtpci_ops = &modern_ops; 6466ba1f63bSYuanhan Liu hw->modern = 1; 647*62a785a6SJianfeng Tan *dev_flags |= RTE_ETH_DEV_INTR_LSC; 6486ba1f63bSYuanhan Liu return 0; 6496ba1f63bSYuanhan Liu } 6506ba1f63bSYuanhan Liu 6516ba1f63bSYuanhan Liu PMD_INIT_LOG(INFO, "trying with legacy virtio pci."); 652*62a785a6SJianfeng Tan if (legacy_virtio_resource_init(dev, hw, dev_flags) < 0) { 653ac5e1d83SHuawei Xie if (dev->kdrv == RTE_KDRV_UNKNOWN && 654ac5e1d83SHuawei Xie dev->devargs->type != RTE_DEVTYPE_WHITELISTED_PCI) { 655ac5e1d83SHuawei Xie PMD_INIT_LOG(INFO, 656ac5e1d83SHuawei Xie "skip kernel managed virtio device."); 657ac5e1d83SHuawei Xie return 1; 658ac5e1d83SHuawei Xie } 659c52afa68SYuanhan Liu return -1; 660ac5e1d83SHuawei Xie } 6616ba1f63bSYuanhan Liu 6626ba1f63bSYuanhan Liu hw->vtpci_ops = &legacy_ops; 663c52afa68SYuanhan Liu hw->use_msix = legacy_virtio_has_msix(&dev->addr); 6646ba1f63bSYuanhan Liu hw->modern = 0; 665c52afa68SYuanhan Liu 666d5bbeefcSYuanhan Liu return 0; 6676c3169a3SBruce Richardson } 668