15566a3e3SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause 25566a3e3SBruce Richardson * Copyright(c) 2010-2014 Intel Corporation 36c3169a3SBruce Richardson */ 46c3169a3SBruce Richardson #include <stdint.h> 56c3169a3SBruce Richardson 6742bde12SBruce Richardson #ifdef RTE_EXEC_ENV_LINUX 7c52afa68SYuanhan Liu #include <dirent.h> 8c52afa68SYuanhan Liu #include <fcntl.h> 9c52afa68SYuanhan Liu #endif 10c52afa68SYuanhan Liu 11631d4ee4SSantosh Shukla #include <rte_io.h> 122b0e39c1SGaetan Rivet #include <rte_bus.h> 13631d4ee4SSantosh Shukla 146c3169a3SBruce Richardson #include "virtio_pci.h" 156c3169a3SBruce Richardson #include "virtio_logs.h" 16d5bbeefcSYuanhan Liu #include "virtqueue.h" 176c3169a3SBruce Richardson 186ba1f63bSYuanhan Liu /* 196ba1f63bSYuanhan Liu * Following macros are derived from linux/pci_regs.h, however, 206ba1f63bSYuanhan Liu * we can't simply include that header here, as there is no such 216ba1f63bSYuanhan Liu * file for non-Linux platform. 226ba1f63bSYuanhan Liu */ 236ba1f63bSYuanhan Liu #define PCI_CAPABILITY_LIST 0x34 246ba1f63bSYuanhan Liu #define PCI_CAP_ID_VNDR 0x09 25554b6d3eSJianfeng Tan #define PCI_CAP_ID_MSIX 0x11 266ba1f63bSYuanhan Liu 27b8f04520SDavid Marchand /* 28b8f04520SDavid Marchand * The remaining space is defined by each driver as the per-driver 29b8f04520SDavid Marchand * configuration space. 30b8f04520SDavid Marchand */ 31fe19d49cSZhiyong Yang #define VIRTIO_PCI_CONFIG(hw) \ 32fe19d49cSZhiyong Yang (((hw)->use_msix == VIRTIO_MSIX_ENABLED) ? 24 : 20) 33b86af7b1SYuanhan Liu 34595454c5SJianfeng Tan static inline int 35595454c5SJianfeng Tan check_vq_phys_addr_ok(struct virtqueue *vq) 36595454c5SJianfeng Tan { 37595454c5SJianfeng Tan /* Virtio PCI device VIRTIO_PCI_QUEUE_PF register is 32bit, 38595454c5SJianfeng Tan * and only accepts 32 bit page frame number. 39595454c5SJianfeng Tan * Check if the allocated physical memory exceeds 16TB. 40595454c5SJianfeng Tan */ 41595454c5SJianfeng Tan if ((vq->vq_ring_mem + vq->vq_ring_size - 1) >> 42595454c5SJianfeng Tan (VIRTIO_PCI_QUEUE_ADDR_SHIFT + 32)) { 43595454c5SJianfeng Tan PMD_INIT_LOG(ERR, "vring address shouldn't be above 16TB!"); 44595454c5SJianfeng Tan return 0; 45595454c5SJianfeng Tan } 46595454c5SJianfeng Tan 47595454c5SJianfeng Tan return 1; 48595454c5SJianfeng Tan } 49595454c5SJianfeng Tan 507793d293SMaxime Coquelin #define PCI_MSIX_ENABLE 0x8000 517793d293SMaxime Coquelin 527793d293SMaxime Coquelin static enum virtio_msix_status 537793d293SMaxime Coquelin vtpci_msix_detect(struct rte_pci_device *dev) 547793d293SMaxime Coquelin { 557793d293SMaxime Coquelin uint8_t pos; 567793d293SMaxime Coquelin int ret; 577793d293SMaxime Coquelin 587793d293SMaxime Coquelin ret = rte_pci_read_config(dev, &pos, 1, PCI_CAPABILITY_LIST); 597793d293SMaxime Coquelin if (ret != 1) { 607793d293SMaxime Coquelin PMD_INIT_LOG(DEBUG, 617793d293SMaxime Coquelin "failed to read pci capability list, ret %d", ret); 627793d293SMaxime Coquelin return VIRTIO_MSIX_NONE; 637793d293SMaxime Coquelin } 647793d293SMaxime Coquelin 657793d293SMaxime Coquelin while (pos) { 667793d293SMaxime Coquelin uint8_t cap[2]; 677793d293SMaxime Coquelin 687793d293SMaxime Coquelin ret = rte_pci_read_config(dev, cap, sizeof(cap), pos); 697793d293SMaxime Coquelin if (ret != sizeof(cap)) { 707793d293SMaxime Coquelin PMD_INIT_LOG(DEBUG, 717793d293SMaxime Coquelin "failed to read pci cap at pos: %x ret %d", 727793d293SMaxime Coquelin pos, ret); 737793d293SMaxime Coquelin break; 747793d293SMaxime Coquelin } 757793d293SMaxime Coquelin 767793d293SMaxime Coquelin if (cap[0] == PCI_CAP_ID_MSIX) { 777793d293SMaxime Coquelin uint16_t flags; 787793d293SMaxime Coquelin 797793d293SMaxime Coquelin ret = rte_pci_read_config(dev, &flags, sizeof(flags), 807793d293SMaxime Coquelin pos + sizeof(cap)); 817793d293SMaxime Coquelin if (ret != sizeof(flags)) { 827793d293SMaxime Coquelin PMD_INIT_LOG(DEBUG, 837793d293SMaxime Coquelin "failed to read pci cap at pos:" 847793d293SMaxime Coquelin " %x ret %d", pos + 2, ret); 857793d293SMaxime Coquelin break; 867793d293SMaxime Coquelin } 877793d293SMaxime Coquelin 887793d293SMaxime Coquelin if (flags & PCI_MSIX_ENABLE) 897793d293SMaxime Coquelin return VIRTIO_MSIX_ENABLED; 907793d293SMaxime Coquelin else 917793d293SMaxime Coquelin return VIRTIO_MSIX_DISABLED; 927793d293SMaxime Coquelin } 937793d293SMaxime Coquelin 947793d293SMaxime Coquelin pos = cap[1]; 957793d293SMaxime Coquelin } 967793d293SMaxime Coquelin 977793d293SMaxime Coquelin return VIRTIO_MSIX_NONE; 987793d293SMaxime Coquelin } 997793d293SMaxime Coquelin 100281ccccbSDavid Marchand /* 101281ccccbSDavid Marchand * Since we are in legacy mode: 102281ccccbSDavid Marchand * http://ozlabs.org/~rusty/virtio-spec/virtio-0.9.5.pdf 103281ccccbSDavid Marchand * 104281ccccbSDavid Marchand * "Note that this is possible because while the virtio header is PCI (i.e. 105281ccccbSDavid Marchand * little) endian, the device-specific region is encoded in the native endian of 106281ccccbSDavid Marchand * the guest (where such distinction is applicable)." 107281ccccbSDavid Marchand * 108281ccccbSDavid Marchand * For powerpc which supports both, qemu supposes that cpu is big endian and 109281ccccbSDavid Marchand * enforces this for the virtio-net stuff. 110281ccccbSDavid Marchand */ 111d5bbeefcSYuanhan Liu static void 112d5bbeefcSYuanhan Liu legacy_read_dev_config(struct virtio_hw *hw, size_t offset, 1136c3169a3SBruce Richardson void *dst, int length) 1146c3169a3SBruce Richardson { 115281ccccbSDavid Marchand #ifdef RTE_ARCH_PPC_64 116281ccccbSDavid Marchand int size; 117281ccccbSDavid Marchand 118281ccccbSDavid Marchand while (length > 0) { 119281ccccbSDavid Marchand if (length >= 4) { 120281ccccbSDavid Marchand size = 4; 1213dcfe039SThomas Monjalon rte_pci_ioport_read(VTPCI_IO(hw), dst, size, 122281ccccbSDavid Marchand VIRTIO_PCI_CONFIG(hw) + offset); 123281ccccbSDavid Marchand *(uint32_t *)dst = rte_be_to_cpu_32(*(uint32_t *)dst); 124281ccccbSDavid Marchand } else if (length >= 2) { 125281ccccbSDavid Marchand size = 2; 1263dcfe039SThomas Monjalon rte_pci_ioport_read(VTPCI_IO(hw), dst, size, 127281ccccbSDavid Marchand VIRTIO_PCI_CONFIG(hw) + offset); 128281ccccbSDavid Marchand *(uint16_t *)dst = rte_be_to_cpu_16(*(uint16_t *)dst); 129281ccccbSDavid Marchand } else { 130281ccccbSDavid Marchand size = 1; 1313dcfe039SThomas Monjalon rte_pci_ioport_read(VTPCI_IO(hw), dst, size, 132281ccccbSDavid Marchand VIRTIO_PCI_CONFIG(hw) + offset); 133281ccccbSDavid Marchand } 134281ccccbSDavid Marchand 135281ccccbSDavid Marchand dst = (char *)dst + size; 136281ccccbSDavid Marchand offset += size; 137281ccccbSDavid Marchand length -= size; 138281ccccbSDavid Marchand } 139281ccccbSDavid Marchand #else 1403dcfe039SThomas Monjalon rte_pci_ioport_read(VTPCI_IO(hw), dst, length, 141b8f04520SDavid Marchand VIRTIO_PCI_CONFIG(hw) + offset); 142281ccccbSDavid Marchand #endif 1436c3169a3SBruce Richardson } 1446c3169a3SBruce Richardson 145d5bbeefcSYuanhan Liu static void 146d5bbeefcSYuanhan Liu legacy_write_dev_config(struct virtio_hw *hw, size_t offset, 147d5bbeefcSYuanhan Liu const void *src, int length) 1486c3169a3SBruce Richardson { 149281ccccbSDavid Marchand #ifdef RTE_ARCH_PPC_64 150281ccccbSDavid Marchand union { 151281ccccbSDavid Marchand uint32_t u32; 152281ccccbSDavid Marchand uint16_t u16; 153281ccccbSDavid Marchand } tmp; 154281ccccbSDavid Marchand int size; 155281ccccbSDavid Marchand 156281ccccbSDavid Marchand while (length > 0) { 157281ccccbSDavid Marchand if (length >= 4) { 158281ccccbSDavid Marchand size = 4; 159281ccccbSDavid Marchand tmp.u32 = rte_cpu_to_be_32(*(const uint32_t *)src); 1603dcfe039SThomas Monjalon rte_pci_ioport_write(VTPCI_IO(hw), &tmp.u32, size, 161281ccccbSDavid Marchand VIRTIO_PCI_CONFIG(hw) + offset); 162281ccccbSDavid Marchand } else if (length >= 2) { 163281ccccbSDavid Marchand size = 2; 164281ccccbSDavid Marchand tmp.u16 = rte_cpu_to_be_16(*(const uint16_t *)src); 1653dcfe039SThomas Monjalon rte_pci_ioport_write(VTPCI_IO(hw), &tmp.u16, size, 166281ccccbSDavid Marchand VIRTIO_PCI_CONFIG(hw) + offset); 167281ccccbSDavid Marchand } else { 168281ccccbSDavid Marchand size = 1; 1693dcfe039SThomas Monjalon rte_pci_ioport_write(VTPCI_IO(hw), src, size, 170281ccccbSDavid Marchand VIRTIO_PCI_CONFIG(hw) + offset); 171281ccccbSDavid Marchand } 172281ccccbSDavid Marchand 173281ccccbSDavid Marchand src = (const char *)src + size; 174281ccccbSDavid Marchand offset += size; 175281ccccbSDavid Marchand length -= size; 176281ccccbSDavid Marchand } 177281ccccbSDavid Marchand #else 1783dcfe039SThomas Monjalon rte_pci_ioport_write(VTPCI_IO(hw), src, length, 179b8f04520SDavid Marchand VIRTIO_PCI_CONFIG(hw) + offset); 180281ccccbSDavid Marchand #endif 1816c3169a3SBruce Richardson } 1826c3169a3SBruce Richardson 1833891f233SYuanhan Liu static uint64_t 184d5bbeefcSYuanhan Liu legacy_get_features(struct virtio_hw *hw) 185d5bbeefcSYuanhan Liu { 18636ea36efSYuanhan Liu uint32_t dst; 187b8f04520SDavid Marchand 1883dcfe039SThomas Monjalon rte_pci_ioport_read(VTPCI_IO(hw), &dst, 4, VIRTIO_PCI_HOST_FEATURES); 189b8f04520SDavid Marchand return dst; 190d5bbeefcSYuanhan Liu } 191d5bbeefcSYuanhan Liu 192d5bbeefcSYuanhan Liu static void 1933891f233SYuanhan Liu legacy_set_features(struct virtio_hw *hw, uint64_t features) 194d5bbeefcSYuanhan Liu { 1953891f233SYuanhan Liu if ((features >> 32) != 0) { 1963891f233SYuanhan Liu PMD_DRV_LOG(ERR, 1973891f233SYuanhan Liu "only 32 bit features are allowed for legacy virtio!"); 1983891f233SYuanhan Liu return; 1993891f233SYuanhan Liu } 2003dcfe039SThomas Monjalon rte_pci_ioport_write(VTPCI_IO(hw), &features, 4, 201b8f04520SDavid Marchand VIRTIO_PCI_GUEST_FEATURES); 202d5bbeefcSYuanhan Liu } 203d5bbeefcSYuanhan Liu 204cbb135b3SMaxime Coquelin static int 205cbb135b3SMaxime Coquelin legacy_features_ok(struct virtio_hw *hw __rte_unused) 206cbb135b3SMaxime Coquelin { 207cbb135b3SMaxime Coquelin return 0; 208cbb135b3SMaxime Coquelin } 209cbb135b3SMaxime Coquelin 210d5bbeefcSYuanhan Liu static uint8_t 211d5bbeefcSYuanhan Liu legacy_get_status(struct virtio_hw *hw) 212d5bbeefcSYuanhan Liu { 213b8f04520SDavid Marchand uint8_t dst; 214b8f04520SDavid Marchand 2153dcfe039SThomas Monjalon rte_pci_ioport_read(VTPCI_IO(hw), &dst, 1, VIRTIO_PCI_STATUS); 216b8f04520SDavid Marchand return dst; 217d5bbeefcSYuanhan Liu } 218d5bbeefcSYuanhan Liu 219d5bbeefcSYuanhan Liu static void 220d5bbeefcSYuanhan Liu legacy_set_status(struct virtio_hw *hw, uint8_t status) 221d5bbeefcSYuanhan Liu { 2223dcfe039SThomas Monjalon rte_pci_ioport_write(VTPCI_IO(hw), &status, 1, VIRTIO_PCI_STATUS); 223d5bbeefcSYuanhan Liu } 224d5bbeefcSYuanhan Liu 225d5bbeefcSYuanhan Liu static uint8_t 226d5bbeefcSYuanhan Liu legacy_get_isr(struct virtio_hw *hw) 227d5bbeefcSYuanhan Liu { 228b8f04520SDavid Marchand uint8_t dst; 229b8f04520SDavid Marchand 2303dcfe039SThomas Monjalon rte_pci_ioport_read(VTPCI_IO(hw), &dst, 1, VIRTIO_PCI_ISR); 231b8f04520SDavid Marchand return dst; 232d5bbeefcSYuanhan Liu } 233d5bbeefcSYuanhan Liu 234d5bbeefcSYuanhan Liu /* Enable one vector (0) for Link State Intrerrupt */ 235d5bbeefcSYuanhan Liu static uint16_t 236d5bbeefcSYuanhan Liu legacy_set_config_irq(struct virtio_hw *hw, uint16_t vec) 237d5bbeefcSYuanhan Liu { 238b8f04520SDavid Marchand uint16_t dst; 239b8f04520SDavid Marchand 2403dcfe039SThomas Monjalon rte_pci_ioport_write(VTPCI_IO(hw), &vec, 2, VIRTIO_MSI_CONFIG_VECTOR); 2413dcfe039SThomas Monjalon rte_pci_ioport_read(VTPCI_IO(hw), &dst, 2, VIRTIO_MSI_CONFIG_VECTOR); 242b8f04520SDavid Marchand return dst; 243d5bbeefcSYuanhan Liu } 244d5bbeefcSYuanhan Liu 245d5bbeefcSYuanhan Liu static uint16_t 246c49526acSJianfeng Tan legacy_set_queue_irq(struct virtio_hw *hw, struct virtqueue *vq, uint16_t vec) 247c49526acSJianfeng Tan { 248c49526acSJianfeng Tan uint16_t dst; 249c49526acSJianfeng Tan 2503dcfe039SThomas Monjalon rte_pci_ioport_write(VTPCI_IO(hw), &vq->vq_queue_index, 2, 251c49526acSJianfeng Tan VIRTIO_PCI_QUEUE_SEL); 2523dcfe039SThomas Monjalon rte_pci_ioport_write(VTPCI_IO(hw), &vec, 2, VIRTIO_MSI_QUEUE_VECTOR); 2533dcfe039SThomas Monjalon rte_pci_ioport_read(VTPCI_IO(hw), &dst, 2, VIRTIO_MSI_QUEUE_VECTOR); 254c49526acSJianfeng Tan return dst; 255c49526acSJianfeng Tan } 256c49526acSJianfeng Tan 257c49526acSJianfeng Tan static uint16_t 258d5bbeefcSYuanhan Liu legacy_get_queue_num(struct virtio_hw *hw, uint16_t queue_id) 259d5bbeefcSYuanhan Liu { 260b8f04520SDavid Marchand uint16_t dst; 261b8f04520SDavid Marchand 2623dcfe039SThomas Monjalon rte_pci_ioport_write(VTPCI_IO(hw), &queue_id, 2, VIRTIO_PCI_QUEUE_SEL); 2633dcfe039SThomas Monjalon rte_pci_ioport_read(VTPCI_IO(hw), &dst, 2, VIRTIO_PCI_QUEUE_NUM); 264b8f04520SDavid Marchand return dst; 265d5bbeefcSYuanhan Liu } 266d5bbeefcSYuanhan Liu 267595454c5SJianfeng Tan static int 268d5bbeefcSYuanhan Liu legacy_setup_queue(struct virtio_hw *hw, struct virtqueue *vq) 269d5bbeefcSYuanhan Liu { 270b8f04520SDavid Marchand uint32_t src; 271d5bbeefcSYuanhan Liu 272595454c5SJianfeng Tan if (!check_vq_phys_addr_ok(vq)) 273595454c5SJianfeng Tan return -1; 274595454c5SJianfeng Tan 2753dcfe039SThomas Monjalon rte_pci_ioport_write(VTPCI_IO(hw), &vq->vq_queue_index, 2, 276b8f04520SDavid Marchand VIRTIO_PCI_QUEUE_SEL); 27701ad44fdSHuawei Xie src = vq->vq_ring_mem >> VIRTIO_PCI_QUEUE_ADDR_SHIFT; 2783dcfe039SThomas Monjalon rte_pci_ioport_write(VTPCI_IO(hw), &src, 4, VIRTIO_PCI_QUEUE_PFN); 279595454c5SJianfeng Tan 280595454c5SJianfeng Tan return 0; 281d5bbeefcSYuanhan Liu } 282d5bbeefcSYuanhan Liu 283d5bbeefcSYuanhan Liu static void 284d5bbeefcSYuanhan Liu legacy_del_queue(struct virtio_hw *hw, struct virtqueue *vq) 285d5bbeefcSYuanhan Liu { 286b8f04520SDavid Marchand uint32_t src = 0; 287d5bbeefcSYuanhan Liu 2883dcfe039SThomas Monjalon rte_pci_ioport_write(VTPCI_IO(hw), &vq->vq_queue_index, 2, 289b8f04520SDavid Marchand VIRTIO_PCI_QUEUE_SEL); 2903dcfe039SThomas Monjalon rte_pci_ioport_write(VTPCI_IO(hw), &src, 4, VIRTIO_PCI_QUEUE_PFN); 291d5bbeefcSYuanhan Liu } 292d5bbeefcSYuanhan Liu 293d5bbeefcSYuanhan Liu static void 294d5bbeefcSYuanhan Liu legacy_notify_queue(struct virtio_hw *hw, struct virtqueue *vq) 295d5bbeefcSYuanhan Liu { 2963dcfe039SThomas Monjalon rte_pci_ioport_write(VTPCI_IO(hw), &vq->vq_queue_index, 2, 297b8f04520SDavid Marchand VIRTIO_PCI_QUEUE_NOTIFY); 298d5bbeefcSYuanhan Liu } 299d5bbeefcSYuanhan Liu 3007793d293SMaxime Coquelin static void 3017793d293SMaxime Coquelin legacy_intr_detect(struct virtio_hw *hw) 3027793d293SMaxime Coquelin { 3037793d293SMaxime Coquelin hw->use_msix = vtpci_msix_detect(VTPCI_DEV(hw)); 3047793d293SMaxime Coquelin } 3057793d293SMaxime Coquelin 306f12908c8SMaxime Coquelin static int 307f12908c8SMaxime Coquelin legacy_dev_close(struct virtio_hw *hw) 308f12908c8SMaxime Coquelin { 309f12908c8SMaxime Coquelin struct virtio_pci_dev *dev = virtio_pci_get_dev(hw); 310f12908c8SMaxime Coquelin 311f12908c8SMaxime Coquelin rte_pci_unmap_device(dev->pci_dev); 312f12908c8SMaxime Coquelin rte_pci_ioport_unmap(VTPCI_IO(hw)); 313f12908c8SMaxime Coquelin 314f12908c8SMaxime Coquelin return 0; 315f12908c8SMaxime Coquelin } 316f12908c8SMaxime Coquelin 3176d890f8aSYuanhan Liu const struct virtio_pci_ops legacy_ops = { 318d5bbeefcSYuanhan Liu .read_dev_cfg = legacy_read_dev_config, 319d5bbeefcSYuanhan Liu .write_dev_cfg = legacy_write_dev_config, 320d5bbeefcSYuanhan Liu .get_status = legacy_get_status, 321d5bbeefcSYuanhan Liu .set_status = legacy_set_status, 322d5bbeefcSYuanhan Liu .get_features = legacy_get_features, 323d5bbeefcSYuanhan Liu .set_features = legacy_set_features, 324cbb135b3SMaxime Coquelin .features_ok = legacy_features_ok, 325d5bbeefcSYuanhan Liu .get_isr = legacy_get_isr, 326d5bbeefcSYuanhan Liu .set_config_irq = legacy_set_config_irq, 327c49526acSJianfeng Tan .set_queue_irq = legacy_set_queue_irq, 328d5bbeefcSYuanhan Liu .get_queue_num = legacy_get_queue_num, 329d5bbeefcSYuanhan Liu .setup_queue = legacy_setup_queue, 330d5bbeefcSYuanhan Liu .del_queue = legacy_del_queue, 331d5bbeefcSYuanhan Liu .notify_queue = legacy_notify_queue, 3327793d293SMaxime Coquelin .intr_detect = legacy_intr_detect, 333f12908c8SMaxime Coquelin .dev_close = legacy_dev_close, 334d5bbeefcSYuanhan Liu }; 335d5bbeefcSYuanhan Liu 3366ba1f63bSYuanhan Liu static inline void 3376ba1f63bSYuanhan Liu io_write64_twopart(uint64_t val, uint32_t *lo, uint32_t *hi) 3386ba1f63bSYuanhan Liu { 339631d4ee4SSantosh Shukla rte_write32(val & ((1ULL << 32) - 1), lo); 340631d4ee4SSantosh Shukla rte_write32(val >> 32, hi); 3416ba1f63bSYuanhan Liu } 3426ba1f63bSYuanhan Liu 3436ba1f63bSYuanhan Liu static void 3446ba1f63bSYuanhan Liu modern_read_dev_config(struct virtio_hw *hw, size_t offset, 3456ba1f63bSYuanhan Liu void *dst, int length) 3466ba1f63bSYuanhan Liu { 347*266ece29SMaxime Coquelin struct virtio_pci_dev *dev = virtio_pci_get_dev(hw); 3486ba1f63bSYuanhan Liu int i; 3496ba1f63bSYuanhan Liu uint8_t *p; 3506ba1f63bSYuanhan Liu uint8_t old_gen, new_gen; 3516ba1f63bSYuanhan Liu 3526ba1f63bSYuanhan Liu do { 353*266ece29SMaxime Coquelin old_gen = rte_read8(&dev->common_cfg->config_generation); 3546ba1f63bSYuanhan Liu 3556ba1f63bSYuanhan Liu p = dst; 3566ba1f63bSYuanhan Liu for (i = 0; i < length; i++) 357*266ece29SMaxime Coquelin *p++ = rte_read8((uint8_t *)dev->dev_cfg + offset + i); 3586ba1f63bSYuanhan Liu 359*266ece29SMaxime Coquelin new_gen = rte_read8(&dev->common_cfg->config_generation); 3606ba1f63bSYuanhan Liu } while (old_gen != new_gen); 3616ba1f63bSYuanhan Liu } 3626ba1f63bSYuanhan Liu 3636ba1f63bSYuanhan Liu static void 3646ba1f63bSYuanhan Liu modern_write_dev_config(struct virtio_hw *hw, size_t offset, 3656ba1f63bSYuanhan Liu const void *src, int length) 3666ba1f63bSYuanhan Liu { 367*266ece29SMaxime Coquelin struct virtio_pci_dev *dev = virtio_pci_get_dev(hw); 3686ba1f63bSYuanhan Liu int i; 3696ba1f63bSYuanhan Liu const uint8_t *p = src; 3706ba1f63bSYuanhan Liu 3716ba1f63bSYuanhan Liu for (i = 0; i < length; i++) 372*266ece29SMaxime Coquelin rte_write8((*p++), (((uint8_t *)dev->dev_cfg) + offset + i)); 3736ba1f63bSYuanhan Liu } 3746ba1f63bSYuanhan Liu 3756ba1f63bSYuanhan Liu static uint64_t 3766ba1f63bSYuanhan Liu modern_get_features(struct virtio_hw *hw) 3776ba1f63bSYuanhan Liu { 378*266ece29SMaxime Coquelin struct virtio_pci_dev *dev = virtio_pci_get_dev(hw); 3796ba1f63bSYuanhan Liu uint32_t features_lo, features_hi; 3806ba1f63bSYuanhan Liu 381*266ece29SMaxime Coquelin rte_write32(0, &dev->common_cfg->device_feature_select); 382*266ece29SMaxime Coquelin features_lo = rte_read32(&dev->common_cfg->device_feature); 3836ba1f63bSYuanhan Liu 384*266ece29SMaxime Coquelin rte_write32(1, &dev->common_cfg->device_feature_select); 385*266ece29SMaxime Coquelin features_hi = rte_read32(&dev->common_cfg->device_feature); 3866ba1f63bSYuanhan Liu 3876ba1f63bSYuanhan Liu return ((uint64_t)features_hi << 32) | features_lo; 3886ba1f63bSYuanhan Liu } 3896ba1f63bSYuanhan Liu 3906ba1f63bSYuanhan Liu static void 3916ba1f63bSYuanhan Liu modern_set_features(struct virtio_hw *hw, uint64_t features) 3926ba1f63bSYuanhan Liu { 393*266ece29SMaxime Coquelin struct virtio_pci_dev *dev = virtio_pci_get_dev(hw); 3946ba1f63bSYuanhan Liu 395*266ece29SMaxime Coquelin rte_write32(0, &dev->common_cfg->guest_feature_select); 396*266ece29SMaxime Coquelin rte_write32(features & ((1ULL << 32) - 1), 397*266ece29SMaxime Coquelin &dev->common_cfg->guest_feature); 398*266ece29SMaxime Coquelin 399*266ece29SMaxime Coquelin rte_write32(1, &dev->common_cfg->guest_feature_select); 400631d4ee4SSantosh Shukla rte_write32(features >> 32, 401*266ece29SMaxime Coquelin &dev->common_cfg->guest_feature); 4026ba1f63bSYuanhan Liu } 4036ba1f63bSYuanhan Liu 404cbb135b3SMaxime Coquelin static int 405cbb135b3SMaxime Coquelin modern_features_ok(struct virtio_hw *hw) 406cbb135b3SMaxime Coquelin { 407cbb135b3SMaxime Coquelin if (!vtpci_with_feature(hw, VIRTIO_F_VERSION_1)) { 408cbb135b3SMaxime Coquelin PMD_INIT_LOG(ERR, "Version 1+ required with modern devices\n"); 409cbb135b3SMaxime Coquelin return -1; 410cbb135b3SMaxime Coquelin } 411cbb135b3SMaxime Coquelin 412cbb135b3SMaxime Coquelin return 0; 413cbb135b3SMaxime Coquelin } 414cbb135b3SMaxime Coquelin 4156ba1f63bSYuanhan Liu static uint8_t 4166ba1f63bSYuanhan Liu modern_get_status(struct virtio_hw *hw) 4176ba1f63bSYuanhan Liu { 418*266ece29SMaxime Coquelin struct virtio_pci_dev *dev = virtio_pci_get_dev(hw); 419*266ece29SMaxime Coquelin 420*266ece29SMaxime Coquelin return rte_read8(&dev->common_cfg->device_status); 4216ba1f63bSYuanhan Liu } 4226ba1f63bSYuanhan Liu 4236ba1f63bSYuanhan Liu static void 4246ba1f63bSYuanhan Liu modern_set_status(struct virtio_hw *hw, uint8_t status) 4256ba1f63bSYuanhan Liu { 426*266ece29SMaxime Coquelin struct virtio_pci_dev *dev = virtio_pci_get_dev(hw); 427*266ece29SMaxime Coquelin 428*266ece29SMaxime Coquelin rte_write8(status, &dev->common_cfg->device_status); 4296ba1f63bSYuanhan Liu } 4306ba1f63bSYuanhan Liu 4316ba1f63bSYuanhan Liu static uint8_t 4326ba1f63bSYuanhan Liu modern_get_isr(struct virtio_hw *hw) 4336ba1f63bSYuanhan Liu { 434*266ece29SMaxime Coquelin struct virtio_pci_dev *dev = virtio_pci_get_dev(hw); 435*266ece29SMaxime Coquelin 436*266ece29SMaxime Coquelin return rte_read8(dev->isr); 4376ba1f63bSYuanhan Liu } 4386ba1f63bSYuanhan Liu 4396ba1f63bSYuanhan Liu static uint16_t 4406ba1f63bSYuanhan Liu modern_set_config_irq(struct virtio_hw *hw, uint16_t vec) 4416ba1f63bSYuanhan Liu { 442*266ece29SMaxime Coquelin struct virtio_pci_dev *dev = virtio_pci_get_dev(hw); 443*266ece29SMaxime Coquelin 444*266ece29SMaxime Coquelin rte_write16(vec, &dev->common_cfg->msix_config); 445*266ece29SMaxime Coquelin return rte_read16(&dev->common_cfg->msix_config); 4466ba1f63bSYuanhan Liu } 4476ba1f63bSYuanhan Liu 4486ba1f63bSYuanhan Liu static uint16_t 449c49526acSJianfeng Tan modern_set_queue_irq(struct virtio_hw *hw, struct virtqueue *vq, uint16_t vec) 450c49526acSJianfeng Tan { 451*266ece29SMaxime Coquelin struct virtio_pci_dev *dev = virtio_pci_get_dev(hw); 452*266ece29SMaxime Coquelin 453*266ece29SMaxime Coquelin rte_write16(vq->vq_queue_index, &dev->common_cfg->queue_select); 454*266ece29SMaxime Coquelin rte_write16(vec, &dev->common_cfg->queue_msix_vector); 455*266ece29SMaxime Coquelin return rte_read16(&dev->common_cfg->queue_msix_vector); 456c49526acSJianfeng Tan } 457c49526acSJianfeng Tan 458c49526acSJianfeng Tan static uint16_t 4596ba1f63bSYuanhan Liu modern_get_queue_num(struct virtio_hw *hw, uint16_t queue_id) 4606ba1f63bSYuanhan Liu { 461*266ece29SMaxime Coquelin struct virtio_pci_dev *dev = virtio_pci_get_dev(hw); 462*266ece29SMaxime Coquelin 463*266ece29SMaxime Coquelin rte_write16(queue_id, &dev->common_cfg->queue_select); 464*266ece29SMaxime Coquelin return rte_read16(&dev->common_cfg->queue_size); 4656ba1f63bSYuanhan Liu } 4666ba1f63bSYuanhan Liu 467595454c5SJianfeng Tan static int 4686ba1f63bSYuanhan Liu modern_setup_queue(struct virtio_hw *hw, struct virtqueue *vq) 4696ba1f63bSYuanhan Liu { 470*266ece29SMaxime Coquelin struct virtio_pci_dev *dev = virtio_pci_get_dev(hw); 4716ba1f63bSYuanhan Liu uint64_t desc_addr, avail_addr, used_addr; 4726ba1f63bSYuanhan Liu uint16_t notify_off; 4736ba1f63bSYuanhan Liu 474595454c5SJianfeng Tan if (!check_vq_phys_addr_ok(vq)) 475595454c5SJianfeng Tan return -1; 476595454c5SJianfeng Tan 47701ad44fdSHuawei Xie desc_addr = vq->vq_ring_mem; 4786ba1f63bSYuanhan Liu avail_addr = desc_addr + vq->vq_nentries * sizeof(struct vring_desc); 4796ba1f63bSYuanhan Liu used_addr = RTE_ALIGN_CEIL(avail_addr + offsetof(struct vring_avail, 4806ba1f63bSYuanhan Liu ring[vq->vq_nentries]), 4816ba1f63bSYuanhan Liu VIRTIO_PCI_VRING_ALIGN); 4826ba1f63bSYuanhan Liu 483*266ece29SMaxime Coquelin rte_write16(vq->vq_queue_index, &dev->common_cfg->queue_select); 4846ba1f63bSYuanhan Liu 485*266ece29SMaxime Coquelin io_write64_twopart(desc_addr, &dev->common_cfg->queue_desc_lo, 486*266ece29SMaxime Coquelin &dev->common_cfg->queue_desc_hi); 487*266ece29SMaxime Coquelin io_write64_twopart(avail_addr, &dev->common_cfg->queue_avail_lo, 488*266ece29SMaxime Coquelin &dev->common_cfg->queue_avail_hi); 489*266ece29SMaxime Coquelin io_write64_twopart(used_addr, &dev->common_cfg->queue_used_lo, 490*266ece29SMaxime Coquelin &dev->common_cfg->queue_used_hi); 4916ba1f63bSYuanhan Liu 492*266ece29SMaxime Coquelin notify_off = rte_read16(&dev->common_cfg->queue_notify_off); 493*266ece29SMaxime Coquelin vq->notify_addr = (void *)((uint8_t *)dev->notify_base + 494*266ece29SMaxime Coquelin notify_off * dev->notify_off_multiplier); 4956ba1f63bSYuanhan Liu 496*266ece29SMaxime Coquelin rte_write16(1, &dev->common_cfg->queue_enable); 4976ba1f63bSYuanhan Liu 4986ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "queue %u addresses:", vq->vq_queue_index); 4996ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "\t desc_addr: %" PRIx64, desc_addr); 5006ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "\t aval_addr: %" PRIx64, avail_addr); 5016ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "\t used_addr: %" PRIx64, used_addr); 5026ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "\t notify addr: %p (notify offset: %u)", 5036ba1f63bSYuanhan Liu vq->notify_addr, notify_off); 504595454c5SJianfeng Tan 505595454c5SJianfeng Tan return 0; 5066ba1f63bSYuanhan Liu } 5076ba1f63bSYuanhan Liu 5086ba1f63bSYuanhan Liu static void 5096ba1f63bSYuanhan Liu modern_del_queue(struct virtio_hw *hw, struct virtqueue *vq) 5106ba1f63bSYuanhan Liu { 511*266ece29SMaxime Coquelin struct virtio_pci_dev *dev = virtio_pci_get_dev(hw); 5126ba1f63bSYuanhan Liu 513*266ece29SMaxime Coquelin rte_write16(vq->vq_queue_index, &dev->common_cfg->queue_select); 5146ba1f63bSYuanhan Liu 515*266ece29SMaxime Coquelin io_write64_twopart(0, &dev->common_cfg->queue_desc_lo, 516*266ece29SMaxime Coquelin &dev->common_cfg->queue_desc_hi); 517*266ece29SMaxime Coquelin io_write64_twopart(0, &dev->common_cfg->queue_avail_lo, 518*266ece29SMaxime Coquelin &dev->common_cfg->queue_avail_hi); 519*266ece29SMaxime Coquelin io_write64_twopart(0, &dev->common_cfg->queue_used_lo, 520*266ece29SMaxime Coquelin &dev->common_cfg->queue_used_hi); 521*266ece29SMaxime Coquelin 522*266ece29SMaxime Coquelin rte_write16(0, &dev->common_cfg->queue_enable); 5236ba1f63bSYuanhan Liu } 5246ba1f63bSYuanhan Liu 5256ba1f63bSYuanhan Liu static void 5267e72f3ecSCheng Jiang modern_notify_queue(struct virtio_hw *hw, struct virtqueue *vq) 5276ba1f63bSYuanhan Liu { 5287e72f3ecSCheng Jiang uint32_t notify_data; 5297e72f3ecSCheng Jiang 5307e72f3ecSCheng Jiang if (!vtpci_with_feature(hw, VIRTIO_F_NOTIFICATION_DATA)) { 531518208f3SXiao Wang rte_write16(vq->vq_queue_index, vq->notify_addr); 5327e72f3ecSCheng Jiang return; 5337e72f3ecSCheng Jiang } 5347e72f3ecSCheng Jiang 5357e72f3ecSCheng Jiang if (vtpci_with_feature(hw, VIRTIO_F_RING_PACKED)) { 5367e72f3ecSCheng Jiang /* 5377e72f3ecSCheng Jiang * Bit[0:15]: vq queue index 5387e72f3ecSCheng Jiang * Bit[16:30]: avail index 5397e72f3ecSCheng Jiang * Bit[31]: avail wrap counter 5407e72f3ecSCheng Jiang */ 5417e72f3ecSCheng Jiang notify_data = ((uint32_t)(!!(vq->vq_packed.cached_flags & 5427e72f3ecSCheng Jiang VRING_PACKED_DESC_F_AVAIL)) << 31) | 5437e72f3ecSCheng Jiang ((uint32_t)vq->vq_avail_idx << 16) | 5447e72f3ecSCheng Jiang vq->vq_queue_index; 5457e72f3ecSCheng Jiang } else { 5467e72f3ecSCheng Jiang /* 5477e72f3ecSCheng Jiang * Bit[0:15]: vq queue index 5487e72f3ecSCheng Jiang * Bit[16:31]: avail index 5497e72f3ecSCheng Jiang */ 5507e72f3ecSCheng Jiang notify_data = ((uint32_t)vq->vq_avail_idx << 16) | 5517e72f3ecSCheng Jiang vq->vq_queue_index; 5527e72f3ecSCheng Jiang } 5537e72f3ecSCheng Jiang rte_write32(notify_data, vq->notify_addr); 5546ba1f63bSYuanhan Liu } 5556ba1f63bSYuanhan Liu 5567793d293SMaxime Coquelin 5577793d293SMaxime Coquelin 5587793d293SMaxime Coquelin static void 5597793d293SMaxime Coquelin modern_intr_detect(struct virtio_hw *hw) 5607793d293SMaxime Coquelin { 5617793d293SMaxime Coquelin hw->use_msix = vtpci_msix_detect(VTPCI_DEV(hw)); 5627793d293SMaxime Coquelin } 5637793d293SMaxime Coquelin 564f12908c8SMaxime Coquelin static int 565f12908c8SMaxime Coquelin modern_dev_close(struct virtio_hw *hw) 566f12908c8SMaxime Coquelin { 567f12908c8SMaxime Coquelin struct virtio_pci_dev *dev = virtio_pci_get_dev(hw); 568f12908c8SMaxime Coquelin 569f12908c8SMaxime Coquelin rte_pci_unmap_device(dev->pci_dev); 570f12908c8SMaxime Coquelin 571f12908c8SMaxime Coquelin return 0; 572f12908c8SMaxime Coquelin } 573f12908c8SMaxime Coquelin 5746d890f8aSYuanhan Liu const struct virtio_pci_ops modern_ops = { 5756ba1f63bSYuanhan Liu .read_dev_cfg = modern_read_dev_config, 5766ba1f63bSYuanhan Liu .write_dev_cfg = modern_write_dev_config, 5776ba1f63bSYuanhan Liu .get_status = modern_get_status, 5786ba1f63bSYuanhan Liu .set_status = modern_set_status, 5796ba1f63bSYuanhan Liu .get_features = modern_get_features, 5806ba1f63bSYuanhan Liu .set_features = modern_set_features, 581cbb135b3SMaxime Coquelin .features_ok = modern_features_ok, 5826ba1f63bSYuanhan Liu .get_isr = modern_get_isr, 5836ba1f63bSYuanhan Liu .set_config_irq = modern_set_config_irq, 584c49526acSJianfeng Tan .set_queue_irq = modern_set_queue_irq, 5856ba1f63bSYuanhan Liu .get_queue_num = modern_get_queue_num, 5866ba1f63bSYuanhan Liu .setup_queue = modern_setup_queue, 5876ba1f63bSYuanhan Liu .del_queue = modern_del_queue, 5886ba1f63bSYuanhan Liu .notify_queue = modern_notify_queue, 5897793d293SMaxime Coquelin .intr_detect = modern_intr_detect, 590f12908c8SMaxime Coquelin .dev_close = modern_dev_close, 5916ba1f63bSYuanhan Liu }; 5926ba1f63bSYuanhan Liu 5936ba1f63bSYuanhan Liu 594d5bbeefcSYuanhan Liu void 595d5bbeefcSYuanhan Liu vtpci_read_dev_config(struct virtio_hw *hw, size_t offset, 596d5bbeefcSYuanhan Liu void *dst, int length) 597d5bbeefcSYuanhan Liu { 598553f4593SYuanhan Liu VTPCI_OPS(hw)->read_dev_cfg(hw, offset, dst, length); 599d5bbeefcSYuanhan Liu } 600d5bbeefcSYuanhan Liu 601d5bbeefcSYuanhan Liu void 602d5bbeefcSYuanhan Liu vtpci_write_dev_config(struct virtio_hw *hw, size_t offset, 603d5bbeefcSYuanhan Liu const void *src, int length) 604d5bbeefcSYuanhan Liu { 605553f4593SYuanhan Liu VTPCI_OPS(hw)->write_dev_cfg(hw, offset, src, length); 606d5bbeefcSYuanhan Liu } 607d5bbeefcSYuanhan Liu 6083891f233SYuanhan Liu uint64_t 6093891f233SYuanhan Liu vtpci_negotiate_features(struct virtio_hw *hw, uint64_t host_features) 6106c3169a3SBruce Richardson { 6113891f233SYuanhan Liu uint64_t features; 612d5bbeefcSYuanhan Liu 6136c3169a3SBruce Richardson /* 6146c3169a3SBruce Richardson * Limit negotiated features to what the driver, virtqueue, and 6156c3169a3SBruce Richardson * host all support. 6166c3169a3SBruce Richardson */ 6176c3169a3SBruce Richardson features = host_features & hw->guest_features; 618553f4593SYuanhan Liu VTPCI_OPS(hw)->set_features(hw, features); 6196c3169a3SBruce Richardson 6206c3169a3SBruce Richardson return features; 6216c3169a3SBruce Richardson } 6226c3169a3SBruce Richardson 6236c3169a3SBruce Richardson void 6246c3169a3SBruce Richardson vtpci_reset(struct virtio_hw *hw) 6256c3169a3SBruce Richardson { 626553f4593SYuanhan Liu VTPCI_OPS(hw)->set_status(hw, VIRTIO_CONFIG_STATUS_RESET); 627d5bbeefcSYuanhan Liu /* flush status write */ 628553f4593SYuanhan Liu VTPCI_OPS(hw)->get_status(hw); 6296c3169a3SBruce Richardson } 6306c3169a3SBruce Richardson 6316c3169a3SBruce Richardson void 6326c3169a3SBruce Richardson vtpci_reinit_complete(struct virtio_hw *hw) 6336c3169a3SBruce Richardson { 6346c3169a3SBruce Richardson vtpci_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER_OK); 6356c3169a3SBruce Richardson } 6366c3169a3SBruce Richardson 6376c3169a3SBruce Richardson void 6386c3169a3SBruce Richardson vtpci_set_status(struct virtio_hw *hw, uint8_t status) 6396c3169a3SBruce Richardson { 6406c3169a3SBruce Richardson if (status != VIRTIO_CONFIG_STATUS_RESET) 641553f4593SYuanhan Liu status |= VTPCI_OPS(hw)->get_status(hw); 6426c3169a3SBruce Richardson 643553f4593SYuanhan Liu VTPCI_OPS(hw)->set_status(hw, status); 6446c3169a3SBruce Richardson } 6456c3169a3SBruce Richardson 6466c3169a3SBruce Richardson uint8_t 6476ba1f63bSYuanhan Liu vtpci_get_status(struct virtio_hw *hw) 6486ba1f63bSYuanhan Liu { 649553f4593SYuanhan Liu return VTPCI_OPS(hw)->get_status(hw); 6506ba1f63bSYuanhan Liu } 6516ba1f63bSYuanhan Liu 6526ba1f63bSYuanhan Liu uint8_t 6536c3169a3SBruce Richardson vtpci_isr(struct virtio_hw *hw) 6546c3169a3SBruce Richardson { 655553f4593SYuanhan Liu return VTPCI_OPS(hw)->get_isr(hw); 6566c3169a3SBruce Richardson } 6576c3169a3SBruce Richardson 6586ba1f63bSYuanhan Liu static void * 6596ba1f63bSYuanhan Liu get_cfg_addr(struct rte_pci_device *dev, struct virtio_pci_cap *cap) 6606ba1f63bSYuanhan Liu { 6616ba1f63bSYuanhan Liu uint8_t bar = cap->bar; 6626ba1f63bSYuanhan Liu uint32_t length = cap->length; 6636ba1f63bSYuanhan Liu uint32_t offset = cap->offset; 6646ba1f63bSYuanhan Liu uint8_t *base; 6656ba1f63bSYuanhan Liu 6660373ab9bSZhiyong Yang if (bar >= PCI_MAX_RESOURCE) { 6676ba1f63bSYuanhan Liu PMD_INIT_LOG(ERR, "invalid bar: %u", bar); 6686ba1f63bSYuanhan Liu return NULL; 6696ba1f63bSYuanhan Liu } 6706ba1f63bSYuanhan Liu 6716ba1f63bSYuanhan Liu if (offset + length < offset) { 6726ba1f63bSYuanhan Liu PMD_INIT_LOG(ERR, "offset(%u) + length(%u) overflows", 6736ba1f63bSYuanhan Liu offset, length); 6746ba1f63bSYuanhan Liu return NULL; 6756ba1f63bSYuanhan Liu } 6766ba1f63bSYuanhan Liu 6776ba1f63bSYuanhan Liu if (offset + length > dev->mem_resource[bar].len) { 6786ba1f63bSYuanhan Liu PMD_INIT_LOG(ERR, 6796ba1f63bSYuanhan Liu "invalid cap: overflows bar space: %u > %" PRIu64, 6806ba1f63bSYuanhan Liu offset + length, dev->mem_resource[bar].len); 6816ba1f63bSYuanhan Liu return NULL; 6826ba1f63bSYuanhan Liu } 6836ba1f63bSYuanhan Liu 6846ba1f63bSYuanhan Liu base = dev->mem_resource[bar].addr; 6856ba1f63bSYuanhan Liu if (base == NULL) { 6866ba1f63bSYuanhan Liu PMD_INIT_LOG(ERR, "bar %u base addr is NULL", bar); 6876ba1f63bSYuanhan Liu return NULL; 6886ba1f63bSYuanhan Liu } 6896ba1f63bSYuanhan Liu 6906ba1f63bSYuanhan Liu return base + offset; 6916ba1f63bSYuanhan Liu } 6926ba1f63bSYuanhan Liu 6936ba1f63bSYuanhan Liu static int 694*266ece29SMaxime Coquelin virtio_read_caps(struct rte_pci_device *pci_dev, struct virtio_hw *hw) 6956ba1f63bSYuanhan Liu { 696*266ece29SMaxime Coquelin struct virtio_pci_dev *dev = virtio_pci_get_dev(hw); 6976ba1f63bSYuanhan Liu uint8_t pos; 6986ba1f63bSYuanhan Liu struct virtio_pci_cap cap; 6996ba1f63bSYuanhan Liu int ret; 7006ba1f63bSYuanhan Liu 701*266ece29SMaxime Coquelin if (rte_pci_map_device(pci_dev)) { 7026ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "failed to map pci device!"); 7036ba1f63bSYuanhan Liu return -1; 7046ba1f63bSYuanhan Liu } 7056ba1f63bSYuanhan Liu 706*266ece29SMaxime Coquelin ret = rte_pci_read_config(pci_dev, &pos, 1, PCI_CAPABILITY_LIST); 70749bb1f7aSBrian Russell if (ret != 1) { 70849bb1f7aSBrian Russell PMD_INIT_LOG(DEBUG, 70949bb1f7aSBrian Russell "failed to read pci capability list, ret %d", ret); 7106ba1f63bSYuanhan Liu return -1; 7116ba1f63bSYuanhan Liu } 7126ba1f63bSYuanhan Liu 7136ba1f63bSYuanhan Liu while (pos) { 714*266ece29SMaxime Coquelin ret = rte_pci_read_config(pci_dev, &cap, 2, pos); 71549bb1f7aSBrian Russell if (ret != 2) { 71649bb1f7aSBrian Russell PMD_INIT_LOG(DEBUG, 71749bb1f7aSBrian Russell "failed to read pci cap at pos: %x ret %d", 71849bb1f7aSBrian Russell pos, ret); 7196ba1f63bSYuanhan Liu break; 7206ba1f63bSYuanhan Liu } 7216ba1f63bSYuanhan Liu 722cb482cb3SJianfeng Tan if (cap.cap_vndr == PCI_CAP_ID_MSIX) { 723cb482cb3SJianfeng Tan /* Transitional devices would also have this capability, 724cb482cb3SJianfeng Tan * that's why we also check if msix is enabled. 725cb482cb3SJianfeng Tan * 1st byte is cap ID; 2nd byte is the position of next 726cb482cb3SJianfeng Tan * cap; next two bytes are the flags. 727cb482cb3SJianfeng Tan */ 72849bb1f7aSBrian Russell uint16_t flags; 72949bb1f7aSBrian Russell 730*266ece29SMaxime Coquelin ret = rte_pci_read_config(pci_dev, &flags, sizeof(flags), 73149bb1f7aSBrian Russell pos + 2); 73249bb1f7aSBrian Russell if (ret != sizeof(flags)) { 73349bb1f7aSBrian Russell PMD_INIT_LOG(DEBUG, 73449bb1f7aSBrian Russell "failed to read pci cap at pos:" 73549bb1f7aSBrian Russell " %x ret %d", pos + 2, ret); 73649bb1f7aSBrian Russell break; 73749bb1f7aSBrian Russell } 738cb482cb3SJianfeng Tan 739cb482cb3SJianfeng Tan if (flags & PCI_MSIX_ENABLE) 740fe19d49cSZhiyong Yang hw->use_msix = VIRTIO_MSIX_ENABLED; 741fe19d49cSZhiyong Yang else 742fe19d49cSZhiyong Yang hw->use_msix = VIRTIO_MSIX_DISABLED; 743cb482cb3SJianfeng Tan } 744554b6d3eSJianfeng Tan 7456ba1f63bSYuanhan Liu if (cap.cap_vndr != PCI_CAP_ID_VNDR) { 7466ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, 7476ba1f63bSYuanhan Liu "[%2x] skipping non VNDR cap id: %02x", 7486ba1f63bSYuanhan Liu pos, cap.cap_vndr); 7496ba1f63bSYuanhan Liu goto next; 7506ba1f63bSYuanhan Liu } 7516ba1f63bSYuanhan Liu 752*266ece29SMaxime Coquelin ret = rte_pci_read_config(pci_dev, &cap, sizeof(cap), pos); 75349bb1f7aSBrian Russell if (ret != sizeof(cap)) { 75449bb1f7aSBrian Russell PMD_INIT_LOG(DEBUG, 75549bb1f7aSBrian Russell "failed to read pci cap at pos: %x ret %d", 75649bb1f7aSBrian Russell pos, ret); 75749bb1f7aSBrian Russell break; 75849bb1f7aSBrian Russell } 75949bb1f7aSBrian Russell 7606ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, 7616ba1f63bSYuanhan Liu "[%2x] cfg type: %u, bar: %u, offset: %04x, len: %u", 7626ba1f63bSYuanhan Liu pos, cap.cfg_type, cap.bar, cap.offset, cap.length); 7636ba1f63bSYuanhan Liu 7646ba1f63bSYuanhan Liu switch (cap.cfg_type) { 7656ba1f63bSYuanhan Liu case VIRTIO_PCI_CAP_COMMON_CFG: 766*266ece29SMaxime Coquelin dev->common_cfg = get_cfg_addr(pci_dev, &cap); 7676ba1f63bSYuanhan Liu break; 7686ba1f63bSYuanhan Liu case VIRTIO_PCI_CAP_NOTIFY_CFG: 769*266ece29SMaxime Coquelin ret = rte_pci_read_config(pci_dev, 770*266ece29SMaxime Coquelin &dev->notify_off_multiplier, 7716ba1f63bSYuanhan Liu 4, pos + sizeof(cap)); 772ecfae151STiwei Bie if (ret != 4) 773ecfae151STiwei Bie PMD_INIT_LOG(DEBUG, 774ecfae151STiwei Bie "failed to read notify_off_multiplier, ret %d", 775ecfae151STiwei Bie ret); 776ecfae151STiwei Bie else 777*266ece29SMaxime Coquelin dev->notify_base = get_cfg_addr(pci_dev, &cap); 7786ba1f63bSYuanhan Liu break; 7796ba1f63bSYuanhan Liu case VIRTIO_PCI_CAP_DEVICE_CFG: 780*266ece29SMaxime Coquelin dev->dev_cfg = get_cfg_addr(pci_dev, &cap); 7816ba1f63bSYuanhan Liu break; 7826ba1f63bSYuanhan Liu case VIRTIO_PCI_CAP_ISR_CFG: 783*266ece29SMaxime Coquelin dev->isr = get_cfg_addr(pci_dev, &cap); 7846ba1f63bSYuanhan Liu break; 7856ba1f63bSYuanhan Liu } 7866ba1f63bSYuanhan Liu 7876ba1f63bSYuanhan Liu next: 7886ba1f63bSYuanhan Liu pos = cap.cap_next; 7896ba1f63bSYuanhan Liu } 7906ba1f63bSYuanhan Liu 791*266ece29SMaxime Coquelin if (dev->common_cfg == NULL || dev->notify_base == NULL || 792*266ece29SMaxime Coquelin dev->dev_cfg == NULL || dev->isr == NULL) { 7936ba1f63bSYuanhan Liu PMD_INIT_LOG(INFO, "no modern virtio pci device found."); 7946ba1f63bSYuanhan Liu return -1; 7956ba1f63bSYuanhan Liu } 7966ba1f63bSYuanhan Liu 7976ba1f63bSYuanhan Liu PMD_INIT_LOG(INFO, "found modern virtio pci device."); 7986ba1f63bSYuanhan Liu 799*266ece29SMaxime Coquelin PMD_INIT_LOG(DEBUG, "common cfg mapped at: %p", dev->common_cfg); 800*266ece29SMaxime Coquelin PMD_INIT_LOG(DEBUG, "device cfg mapped at: %p", dev->dev_cfg); 801*266ece29SMaxime Coquelin PMD_INIT_LOG(DEBUG, "isr cfg mapped at: %p", dev->isr); 8026ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "notify base: %p, notify off multiplier: %u", 803*266ece29SMaxime Coquelin dev->notify_base, dev->notify_off_multiplier); 8046ba1f63bSYuanhan Liu 8056ba1f63bSYuanhan Liu return 0; 8066ba1f63bSYuanhan Liu } 8076ba1f63bSYuanhan Liu 808ac5e1d83SHuawei Xie /* 809ac5e1d83SHuawei Xie * Return -1: 810ac5e1d83SHuawei Xie * if there is error mapping with VFIO/UIO. 811ac5e1d83SHuawei Xie * if port map error when driver type is KDRV_NONE. 812a65a34a8SStephen Hemminger * if marked as allowed but driver type is KDRV_UNKNOWN. 813ac5e1d83SHuawei Xie * Return 1 if kernel driver is managing the device. 814ac5e1d83SHuawei Xie * Return 0 on success. 815ac5e1d83SHuawei Xie */ 816d5bbeefcSYuanhan Liu int 8171ac79346SMaxime Coquelin vtpci_init(struct rte_pci_device *pci_dev, struct virtio_pci_dev *dev) 818d5bbeefcSYuanhan Liu { 8191ac79346SMaxime Coquelin struct virtio_hw *hw = &dev->hw; 8201ac79346SMaxime Coquelin 821f305ecbbSMaxime Coquelin RTE_BUILD_BUG_ON(offsetof(struct virtio_pci_dev, hw) != 0); 822f305ecbbSMaxime Coquelin 823f12908c8SMaxime Coquelin dev->pci_dev = pci_dev; 824f12908c8SMaxime Coquelin 8256ba1f63bSYuanhan Liu /* 8266ba1f63bSYuanhan Liu * Try if we can succeed reading virtio pci caps, which exists 8276ba1f63bSYuanhan Liu * only on modern pci device. If failed, we fallback to legacy 8286ba1f63bSYuanhan Liu * virtio handling. 8296ba1f63bSYuanhan Liu */ 8301ac79346SMaxime Coquelin if (virtio_read_caps(pci_dev, hw) == 0) { 8316ba1f63bSYuanhan Liu PMD_INIT_LOG(INFO, "modern virtio pci detected."); 832553f4593SYuanhan Liu virtio_hw_internal[hw->port_id].vtpci_ops = &modern_ops; 8331ac79346SMaxime Coquelin dev->modern = true; 8347793d293SMaxime Coquelin goto msix_detect; 8356ba1f63bSYuanhan Liu } 8366ba1f63bSYuanhan Liu 8376ba1f63bSYuanhan Liu PMD_INIT_LOG(INFO, "trying with legacy virtio pci."); 8381ac79346SMaxime Coquelin if (rte_pci_ioport_map(pci_dev, 0, VTPCI_IO(hw)) < 0) { 8391ac79346SMaxime Coquelin rte_pci_unmap_device(pci_dev); 8401ac79346SMaxime Coquelin if (pci_dev->kdrv == RTE_PCI_KDRV_UNKNOWN && 8411ac79346SMaxime Coquelin (!pci_dev->device.devargs || 8421ac79346SMaxime Coquelin pci_dev->device.devargs->bus != 8432b0e39c1SGaetan Rivet rte_bus_find_by_name("pci"))) { 844ac5e1d83SHuawei Xie PMD_INIT_LOG(INFO, 845ac5e1d83SHuawei Xie "skip kernel managed virtio device."); 846ac5e1d83SHuawei Xie return 1; 847ac5e1d83SHuawei Xie } 848c52afa68SYuanhan Liu return -1; 849ac5e1d83SHuawei Xie } 8506ba1f63bSYuanhan Liu 851553f4593SYuanhan Liu virtio_hw_internal[hw->port_id].vtpci_ops = &legacy_ops; 8521ac79346SMaxime Coquelin dev->modern = false; 853c52afa68SYuanhan Liu 8547793d293SMaxime Coquelin msix_detect: 8557793d293SMaxime Coquelin VTPCI_OPS(hw)->intr_detect(hw); 8567793d293SMaxime Coquelin 857d5bbeefcSYuanhan Liu return 0; 8586c3169a3SBruce Richardson } 859fe19d49cSZhiyong Yang 860