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 52b86af7b1SYuanhan Liu #define VIRTIO_PCI_REG_ADDR(hw, reg) \ 53b86af7b1SYuanhan Liu (unsigned short)((hw)->io_base + (reg)) 54b86af7b1SYuanhan Liu 55b86af7b1SYuanhan Liu #define VIRTIO_READ_REG_1(hw, reg) \ 56b86af7b1SYuanhan Liu inb((VIRTIO_PCI_REG_ADDR((hw), (reg)))) 57b86af7b1SYuanhan Liu #define VIRTIO_WRITE_REG_1(hw, reg, value) \ 58b86af7b1SYuanhan Liu outb_p((unsigned char)(value), (VIRTIO_PCI_REG_ADDR((hw), (reg)))) 59b86af7b1SYuanhan Liu 60b86af7b1SYuanhan Liu #define VIRTIO_READ_REG_2(hw, reg) \ 61b86af7b1SYuanhan Liu inw((VIRTIO_PCI_REG_ADDR((hw), (reg)))) 62b86af7b1SYuanhan Liu #define VIRTIO_WRITE_REG_2(hw, reg, value) \ 63b86af7b1SYuanhan Liu outw_p((unsigned short)(value), (VIRTIO_PCI_REG_ADDR((hw), (reg)))) 64b86af7b1SYuanhan Liu 65b86af7b1SYuanhan Liu #define VIRTIO_READ_REG_4(hw, reg) \ 66b86af7b1SYuanhan Liu inl((VIRTIO_PCI_REG_ADDR((hw), (reg)))) 67b86af7b1SYuanhan Liu #define VIRTIO_WRITE_REG_4(hw, reg, value) \ 68b86af7b1SYuanhan Liu outl_p((unsigned int)(value), (VIRTIO_PCI_REG_ADDR((hw), (reg)))) 69b86af7b1SYuanhan Liu 70d5bbeefcSYuanhan Liu static void 71d5bbeefcSYuanhan Liu legacy_read_dev_config(struct virtio_hw *hw, size_t offset, 726c3169a3SBruce Richardson void *dst, int length) 736c3169a3SBruce Richardson { 746c3169a3SBruce Richardson uint64_t off; 756c3169a3SBruce Richardson uint8_t *d; 766c3169a3SBruce Richardson int size; 776c3169a3SBruce Richardson 786c3169a3SBruce Richardson off = VIRTIO_PCI_CONFIG(hw) + offset; 796c3169a3SBruce Richardson for (d = dst; length > 0; d += size, off += size, length -= size) { 806c3169a3SBruce Richardson if (length >= 4) { 816c3169a3SBruce Richardson size = 4; 826c3169a3SBruce Richardson *(uint32_t *)d = VIRTIO_READ_REG_4(hw, off); 836c3169a3SBruce Richardson } else if (length >= 2) { 846c3169a3SBruce Richardson size = 2; 856c3169a3SBruce Richardson *(uint16_t *)d = VIRTIO_READ_REG_2(hw, off); 866c3169a3SBruce Richardson } else { 876c3169a3SBruce Richardson size = 1; 886c3169a3SBruce Richardson *d = VIRTIO_READ_REG_1(hw, off); 896c3169a3SBruce Richardson } 906c3169a3SBruce Richardson } 916c3169a3SBruce Richardson } 926c3169a3SBruce Richardson 93d5bbeefcSYuanhan Liu static void 94d5bbeefcSYuanhan Liu legacy_write_dev_config(struct virtio_hw *hw, size_t offset, 95d5bbeefcSYuanhan Liu const void *src, int length) 966c3169a3SBruce Richardson { 976c3169a3SBruce Richardson uint64_t off; 98d5bbeefcSYuanhan Liu const uint8_t *s; 996c3169a3SBruce Richardson int size; 1006c3169a3SBruce Richardson 1016c3169a3SBruce Richardson off = VIRTIO_PCI_CONFIG(hw) + offset; 1026c3169a3SBruce Richardson for (s = src; length > 0; s += size, off += size, length -= size) { 1036c3169a3SBruce Richardson if (length >= 4) { 1046c3169a3SBruce Richardson size = 4; 105d5bbeefcSYuanhan Liu VIRTIO_WRITE_REG_4(hw, off, *(const uint32_t *)s); 1066c3169a3SBruce Richardson } else if (length >= 2) { 1076c3169a3SBruce Richardson size = 2; 108d5bbeefcSYuanhan Liu VIRTIO_WRITE_REG_2(hw, off, *(const uint16_t *)s); 1096c3169a3SBruce Richardson } else { 1106c3169a3SBruce Richardson size = 1; 1116c3169a3SBruce Richardson VIRTIO_WRITE_REG_1(hw, off, *s); 1126c3169a3SBruce Richardson } 1136c3169a3SBruce Richardson } 1146c3169a3SBruce Richardson } 1156c3169a3SBruce Richardson 1163891f233SYuanhan Liu static uint64_t 117d5bbeefcSYuanhan Liu legacy_get_features(struct virtio_hw *hw) 118d5bbeefcSYuanhan Liu { 119d5bbeefcSYuanhan Liu return VIRTIO_READ_REG_4(hw, VIRTIO_PCI_HOST_FEATURES); 120d5bbeefcSYuanhan Liu } 121d5bbeefcSYuanhan Liu 122d5bbeefcSYuanhan Liu static void 1233891f233SYuanhan Liu legacy_set_features(struct virtio_hw *hw, uint64_t features) 124d5bbeefcSYuanhan Liu { 1253891f233SYuanhan Liu if ((features >> 32) != 0) { 1263891f233SYuanhan Liu PMD_DRV_LOG(ERR, 1273891f233SYuanhan Liu "only 32 bit features are allowed for legacy virtio!"); 1283891f233SYuanhan Liu return; 1293891f233SYuanhan Liu } 130d5bbeefcSYuanhan Liu VIRTIO_WRITE_REG_4(hw, VIRTIO_PCI_GUEST_FEATURES, features); 131d5bbeefcSYuanhan Liu } 132d5bbeefcSYuanhan Liu 133d5bbeefcSYuanhan Liu static uint8_t 134d5bbeefcSYuanhan Liu legacy_get_status(struct virtio_hw *hw) 135d5bbeefcSYuanhan Liu { 136d5bbeefcSYuanhan Liu return VIRTIO_READ_REG_1(hw, VIRTIO_PCI_STATUS); 137d5bbeefcSYuanhan Liu } 138d5bbeefcSYuanhan Liu 139d5bbeefcSYuanhan Liu static void 140d5bbeefcSYuanhan Liu legacy_set_status(struct virtio_hw *hw, uint8_t status) 141d5bbeefcSYuanhan Liu { 142d5bbeefcSYuanhan Liu VIRTIO_WRITE_REG_1(hw, VIRTIO_PCI_STATUS, status); 143d5bbeefcSYuanhan Liu } 144d5bbeefcSYuanhan Liu 145d5bbeefcSYuanhan Liu static void 146d5bbeefcSYuanhan Liu legacy_reset(struct virtio_hw *hw) 147d5bbeefcSYuanhan Liu { 148d5bbeefcSYuanhan Liu legacy_set_status(hw, VIRTIO_CONFIG_STATUS_RESET); 149d5bbeefcSYuanhan Liu } 150d5bbeefcSYuanhan Liu 151d5bbeefcSYuanhan Liu static uint8_t 152d5bbeefcSYuanhan Liu legacy_get_isr(struct virtio_hw *hw) 153d5bbeefcSYuanhan Liu { 154d5bbeefcSYuanhan Liu return VIRTIO_READ_REG_1(hw, VIRTIO_PCI_ISR); 155d5bbeefcSYuanhan Liu } 156d5bbeefcSYuanhan Liu 157d5bbeefcSYuanhan Liu /* Enable one vector (0) for Link State Intrerrupt */ 158d5bbeefcSYuanhan Liu static uint16_t 159d5bbeefcSYuanhan Liu legacy_set_config_irq(struct virtio_hw *hw, uint16_t vec) 160d5bbeefcSYuanhan Liu { 161d5bbeefcSYuanhan Liu VIRTIO_WRITE_REG_2(hw, VIRTIO_MSI_CONFIG_VECTOR, vec); 162d5bbeefcSYuanhan Liu return VIRTIO_READ_REG_2(hw, VIRTIO_MSI_CONFIG_VECTOR); 163d5bbeefcSYuanhan Liu } 164d5bbeefcSYuanhan Liu 165d5bbeefcSYuanhan Liu static uint16_t 166d5bbeefcSYuanhan Liu legacy_get_queue_num(struct virtio_hw *hw, uint16_t queue_id) 167d5bbeefcSYuanhan Liu { 168d5bbeefcSYuanhan Liu VIRTIO_WRITE_REG_2(hw, VIRTIO_PCI_QUEUE_SEL, queue_id); 169d5bbeefcSYuanhan Liu return VIRTIO_READ_REG_2(hw, VIRTIO_PCI_QUEUE_NUM); 170d5bbeefcSYuanhan Liu } 171d5bbeefcSYuanhan Liu 172d5bbeefcSYuanhan Liu static void 173d5bbeefcSYuanhan Liu legacy_setup_queue(struct virtio_hw *hw, struct virtqueue *vq) 174d5bbeefcSYuanhan Liu { 175d5bbeefcSYuanhan Liu VIRTIO_WRITE_REG_2(hw, VIRTIO_PCI_QUEUE_SEL, vq->vq_queue_index); 176d5bbeefcSYuanhan Liu 177d5bbeefcSYuanhan Liu VIRTIO_WRITE_REG_4(hw, VIRTIO_PCI_QUEUE_PFN, 178d5bbeefcSYuanhan Liu vq->mz->phys_addr >> VIRTIO_PCI_QUEUE_ADDR_SHIFT); 179d5bbeefcSYuanhan Liu } 180d5bbeefcSYuanhan Liu 181d5bbeefcSYuanhan Liu static void 182d5bbeefcSYuanhan Liu legacy_del_queue(struct virtio_hw *hw, struct virtqueue *vq) 183d5bbeefcSYuanhan Liu { 184d5bbeefcSYuanhan Liu VIRTIO_WRITE_REG_2(hw, VIRTIO_PCI_QUEUE_SEL, vq->vq_queue_index); 185d5bbeefcSYuanhan Liu 186d5bbeefcSYuanhan Liu VIRTIO_WRITE_REG_4(hw, VIRTIO_PCI_QUEUE_PFN, 0); 187d5bbeefcSYuanhan Liu } 188d5bbeefcSYuanhan Liu 189d5bbeefcSYuanhan Liu static void 190d5bbeefcSYuanhan Liu legacy_notify_queue(struct virtio_hw *hw, struct virtqueue *vq) 191d5bbeefcSYuanhan Liu { 192d5bbeefcSYuanhan Liu VIRTIO_WRITE_REG_2(hw, VIRTIO_PCI_QUEUE_NOTIFY, vq->vq_queue_index); 193d5bbeefcSYuanhan Liu } 194d5bbeefcSYuanhan Liu 195c52afa68SYuanhan Liu #ifdef RTE_EXEC_ENV_LINUXAPP 196c52afa68SYuanhan Liu static int 197c52afa68SYuanhan Liu parse_sysfs_value(const char *filename, unsigned long *val) 198c52afa68SYuanhan Liu { 199c52afa68SYuanhan Liu FILE *f; 200c52afa68SYuanhan Liu char buf[BUFSIZ]; 201c52afa68SYuanhan Liu char *end = NULL; 202c52afa68SYuanhan Liu 203c52afa68SYuanhan Liu f = fopen(filename, "r"); 204c52afa68SYuanhan Liu if (f == NULL) { 205c52afa68SYuanhan Liu PMD_INIT_LOG(ERR, "%s(): cannot open sysfs value %s", 206c52afa68SYuanhan Liu __func__, filename); 207c52afa68SYuanhan Liu return -1; 208c52afa68SYuanhan Liu } 209c52afa68SYuanhan Liu 210c52afa68SYuanhan Liu if (fgets(buf, sizeof(buf), f) == NULL) { 211c52afa68SYuanhan Liu PMD_INIT_LOG(ERR, "%s(): cannot read sysfs value %s", 212c52afa68SYuanhan Liu __func__, filename); 213c52afa68SYuanhan Liu fclose(f); 214c52afa68SYuanhan Liu return -1; 215c52afa68SYuanhan Liu } 216c52afa68SYuanhan Liu *val = strtoul(buf, &end, 0); 217c52afa68SYuanhan Liu if ((buf[0] == '\0') || (end == NULL) || (*end != '\n')) { 218c52afa68SYuanhan Liu PMD_INIT_LOG(ERR, "%s(): cannot parse sysfs value %s", 219c52afa68SYuanhan Liu __func__, filename); 220c52afa68SYuanhan Liu fclose(f); 221c52afa68SYuanhan Liu return -1; 222c52afa68SYuanhan Liu } 223c52afa68SYuanhan Liu fclose(f); 224c52afa68SYuanhan Liu return 0; 225c52afa68SYuanhan Liu } 226c52afa68SYuanhan Liu 227c52afa68SYuanhan Liu static int 228c52afa68SYuanhan Liu get_uio_dev(struct rte_pci_addr *loc, char *buf, unsigned int buflen, 229c52afa68SYuanhan Liu unsigned int *uio_num) 230c52afa68SYuanhan Liu { 231c52afa68SYuanhan Liu struct dirent *e; 232c52afa68SYuanhan Liu DIR *dir; 233c52afa68SYuanhan Liu char dirname[PATH_MAX]; 234c52afa68SYuanhan Liu 235c52afa68SYuanhan Liu /* 236c52afa68SYuanhan Liu * depending on kernel version, uio can be located in uio/uioX 237c52afa68SYuanhan Liu * or uio:uioX 238c52afa68SYuanhan Liu */ 239c52afa68SYuanhan Liu snprintf(dirname, sizeof(dirname), 240c52afa68SYuanhan Liu SYSFS_PCI_DEVICES "/" PCI_PRI_FMT "/uio", 241c52afa68SYuanhan Liu loc->domain, loc->bus, loc->devid, loc->function); 242c52afa68SYuanhan Liu dir = opendir(dirname); 243c52afa68SYuanhan Liu if (dir == NULL) { 244c52afa68SYuanhan Liu /* retry with the parent directory */ 245c52afa68SYuanhan Liu snprintf(dirname, sizeof(dirname), 246c52afa68SYuanhan Liu SYSFS_PCI_DEVICES "/" PCI_PRI_FMT, 247c52afa68SYuanhan Liu loc->domain, loc->bus, loc->devid, loc->function); 248c52afa68SYuanhan Liu dir = opendir(dirname); 249c52afa68SYuanhan Liu 250c52afa68SYuanhan Liu if (dir == NULL) { 251c52afa68SYuanhan Liu PMD_INIT_LOG(ERR, "Cannot opendir %s", dirname); 252c52afa68SYuanhan Liu return -1; 253c52afa68SYuanhan Liu } 254c52afa68SYuanhan Liu } 255c52afa68SYuanhan Liu 256c52afa68SYuanhan Liu /* take the first file starting with "uio" */ 257c52afa68SYuanhan Liu while ((e = readdir(dir)) != NULL) { 258c52afa68SYuanhan Liu /* format could be uio%d ...*/ 259c52afa68SYuanhan Liu int shortprefix_len = sizeof("uio") - 1; 260c52afa68SYuanhan Liu /* ... or uio:uio%d */ 261c52afa68SYuanhan Liu int longprefix_len = sizeof("uio:uio") - 1; 262c52afa68SYuanhan Liu char *endptr; 263c52afa68SYuanhan Liu 264c52afa68SYuanhan Liu if (strncmp(e->d_name, "uio", 3) != 0) 265c52afa68SYuanhan Liu continue; 266c52afa68SYuanhan Liu 267c52afa68SYuanhan Liu /* first try uio%d */ 268c52afa68SYuanhan Liu errno = 0; 269c52afa68SYuanhan Liu *uio_num = strtoull(e->d_name + shortprefix_len, &endptr, 10); 270c52afa68SYuanhan Liu if (errno == 0 && endptr != (e->d_name + shortprefix_len)) { 271c52afa68SYuanhan Liu snprintf(buf, buflen, "%s/uio%u", dirname, *uio_num); 272c52afa68SYuanhan Liu break; 273c52afa68SYuanhan Liu } 274c52afa68SYuanhan Liu 275c52afa68SYuanhan Liu /* then try uio:uio%d */ 276c52afa68SYuanhan Liu errno = 0; 277c52afa68SYuanhan Liu *uio_num = strtoull(e->d_name + longprefix_len, &endptr, 10); 278c52afa68SYuanhan Liu if (errno == 0 && endptr != (e->d_name + longprefix_len)) { 279c52afa68SYuanhan Liu snprintf(buf, buflen, "%s/uio:uio%u", dirname, 280c52afa68SYuanhan Liu *uio_num); 281c52afa68SYuanhan Liu break; 282c52afa68SYuanhan Liu } 283c52afa68SYuanhan Liu } 284c52afa68SYuanhan Liu closedir(dir); 285c52afa68SYuanhan Liu 286c52afa68SYuanhan Liu /* No uio resource found */ 287c52afa68SYuanhan Liu if (e == NULL) { 288c52afa68SYuanhan Liu PMD_INIT_LOG(ERR, "Could not find uio resource"); 289c52afa68SYuanhan Liu return -1; 290c52afa68SYuanhan Liu } 291c52afa68SYuanhan Liu 292c52afa68SYuanhan Liu return 0; 293c52afa68SYuanhan Liu } 294c52afa68SYuanhan Liu 295c52afa68SYuanhan Liu static int 296c52afa68SYuanhan Liu legacy_virtio_has_msix(const struct rte_pci_addr *loc) 297c52afa68SYuanhan Liu { 298c52afa68SYuanhan Liu DIR *d; 299c52afa68SYuanhan Liu char dirname[PATH_MAX]; 300c52afa68SYuanhan Liu 301c52afa68SYuanhan Liu snprintf(dirname, sizeof(dirname), 302c52afa68SYuanhan Liu SYSFS_PCI_DEVICES "/" PCI_PRI_FMT "/msi_irqs", 303c52afa68SYuanhan Liu loc->domain, loc->bus, loc->devid, loc->function); 304c52afa68SYuanhan Liu 305c52afa68SYuanhan Liu d = opendir(dirname); 306c52afa68SYuanhan Liu if (d) 307c52afa68SYuanhan Liu closedir(d); 308c52afa68SYuanhan Liu 309693f715dSHuawei Xie return d != NULL; 310c52afa68SYuanhan Liu } 311c52afa68SYuanhan Liu 312c52afa68SYuanhan Liu /* Extract I/O port numbers from sysfs */ 313c52afa68SYuanhan Liu static int 314c52afa68SYuanhan Liu virtio_resource_init_by_uio(struct rte_pci_device *pci_dev) 315c52afa68SYuanhan Liu { 316c52afa68SYuanhan Liu char dirname[PATH_MAX]; 317c52afa68SYuanhan Liu char filename[PATH_MAX]; 318c52afa68SYuanhan Liu unsigned long start, size; 319c52afa68SYuanhan Liu unsigned int uio_num; 320c52afa68SYuanhan Liu 321c52afa68SYuanhan Liu if (get_uio_dev(&pci_dev->addr, dirname, sizeof(dirname), &uio_num) < 0) 322c52afa68SYuanhan Liu return -1; 323c52afa68SYuanhan Liu 324c52afa68SYuanhan Liu /* get portio size */ 325c52afa68SYuanhan Liu snprintf(filename, sizeof(filename), 326c52afa68SYuanhan Liu "%s/portio/port0/size", dirname); 327c52afa68SYuanhan Liu if (parse_sysfs_value(filename, &size) < 0) { 328c52afa68SYuanhan Liu PMD_INIT_LOG(ERR, "%s(): cannot parse size", 329c52afa68SYuanhan Liu __func__); 330c52afa68SYuanhan Liu return -1; 331c52afa68SYuanhan Liu } 332c52afa68SYuanhan Liu 333c52afa68SYuanhan Liu /* get portio start */ 334c52afa68SYuanhan Liu snprintf(filename, sizeof(filename), 335c52afa68SYuanhan Liu "%s/portio/port0/start", dirname); 336c52afa68SYuanhan Liu if (parse_sysfs_value(filename, &start) < 0) { 337c52afa68SYuanhan Liu PMD_INIT_LOG(ERR, "%s(): cannot parse portio start", 338c52afa68SYuanhan Liu __func__); 339c52afa68SYuanhan Liu return -1; 340c52afa68SYuanhan Liu } 341c52afa68SYuanhan Liu pci_dev->mem_resource[0].addr = (void *)(uintptr_t)start; 342c52afa68SYuanhan Liu pci_dev->mem_resource[0].len = (uint64_t)size; 343c52afa68SYuanhan Liu PMD_INIT_LOG(DEBUG, 344c52afa68SYuanhan Liu "PCI Port IO found start=0x%lx with size=0x%lx", 345c52afa68SYuanhan Liu start, size); 346c52afa68SYuanhan Liu 347c52afa68SYuanhan Liu /* save fd */ 348c52afa68SYuanhan Liu memset(dirname, 0, sizeof(dirname)); 349c52afa68SYuanhan Liu snprintf(dirname, sizeof(dirname), "/dev/uio%u", uio_num); 350c52afa68SYuanhan Liu pci_dev->intr_handle.fd = open(dirname, O_RDWR); 351c52afa68SYuanhan Liu if (pci_dev->intr_handle.fd < 0) { 352c52afa68SYuanhan Liu PMD_INIT_LOG(ERR, "Cannot open %s: %s\n", 353c52afa68SYuanhan Liu dirname, strerror(errno)); 354c52afa68SYuanhan Liu return -1; 355c52afa68SYuanhan Liu } 356c52afa68SYuanhan Liu 357c52afa68SYuanhan Liu pci_dev->intr_handle.type = RTE_INTR_HANDLE_UIO; 358c52afa68SYuanhan Liu pci_dev->driver->drv_flags |= RTE_PCI_DRV_INTR_LSC; 359c52afa68SYuanhan Liu 360c52afa68SYuanhan Liu return 0; 361c52afa68SYuanhan Liu } 362c52afa68SYuanhan Liu 363c52afa68SYuanhan Liu /* Extract port I/O numbers from proc/ioports */ 364c52afa68SYuanhan Liu static int 365c52afa68SYuanhan Liu virtio_resource_init_by_ioports(struct rte_pci_device *pci_dev) 366c52afa68SYuanhan Liu { 367c52afa68SYuanhan Liu uint16_t start, end; 368c52afa68SYuanhan Liu int size; 369c52afa68SYuanhan Liu FILE *fp; 370c52afa68SYuanhan Liu char *line = NULL; 371c52afa68SYuanhan Liu char pci_id[16]; 372c52afa68SYuanhan Liu int found = 0; 373c52afa68SYuanhan Liu size_t linesz; 374c52afa68SYuanhan Liu 375c52afa68SYuanhan Liu snprintf(pci_id, sizeof(pci_id), PCI_PRI_FMT, 376c52afa68SYuanhan Liu pci_dev->addr.domain, 377c52afa68SYuanhan Liu pci_dev->addr.bus, 378c52afa68SYuanhan Liu pci_dev->addr.devid, 379c52afa68SYuanhan Liu pci_dev->addr.function); 380c52afa68SYuanhan Liu 381c52afa68SYuanhan Liu fp = fopen("/proc/ioports", "r"); 382c52afa68SYuanhan Liu if (fp == NULL) { 383c52afa68SYuanhan Liu PMD_INIT_LOG(ERR, "%s(): can't open ioports", __func__); 384c52afa68SYuanhan Liu return -1; 385c52afa68SYuanhan Liu } 386c52afa68SYuanhan Liu 387c52afa68SYuanhan Liu while (getdelim(&line, &linesz, '\n', fp) > 0) { 388c52afa68SYuanhan Liu char *ptr = line; 389c52afa68SYuanhan Liu char *left; 390c52afa68SYuanhan Liu int n; 391c52afa68SYuanhan Liu 392c52afa68SYuanhan Liu n = strcspn(ptr, ":"); 393c52afa68SYuanhan Liu ptr[n] = 0; 394c52afa68SYuanhan Liu left = &ptr[n + 1]; 395c52afa68SYuanhan Liu 396c52afa68SYuanhan Liu while (*left && isspace(*left)) 397c52afa68SYuanhan Liu left++; 398c52afa68SYuanhan Liu 399c52afa68SYuanhan Liu if (!strncmp(left, pci_id, strlen(pci_id))) { 400c52afa68SYuanhan Liu found = 1; 401c52afa68SYuanhan Liu 402c52afa68SYuanhan Liu while (*ptr && isspace(*ptr)) 403c52afa68SYuanhan Liu ptr++; 404c52afa68SYuanhan Liu 405c52afa68SYuanhan Liu sscanf(ptr, "%04hx-%04hx", &start, &end); 406c52afa68SYuanhan Liu size = end - start + 1; 407c52afa68SYuanhan Liu 408c52afa68SYuanhan Liu break; 409c52afa68SYuanhan Liu } 410c52afa68SYuanhan Liu } 411c52afa68SYuanhan Liu 412c52afa68SYuanhan Liu free(line); 413c52afa68SYuanhan Liu fclose(fp); 414c52afa68SYuanhan Liu 415c52afa68SYuanhan Liu if (!found) 416c52afa68SYuanhan Liu return -1; 417c52afa68SYuanhan Liu 418c52afa68SYuanhan Liu pci_dev->mem_resource[0].addr = (void *)(uintptr_t)(uint32_t)start; 419c52afa68SYuanhan Liu pci_dev->mem_resource[0].len = (uint64_t)size; 420c52afa68SYuanhan Liu PMD_INIT_LOG(DEBUG, 421c52afa68SYuanhan Liu "PCI Port IO found start=0x%x with size=0x%x", 422c52afa68SYuanhan Liu start, size); 423c52afa68SYuanhan Liu 424c52afa68SYuanhan Liu /* can't support lsc interrupt without uio */ 425c52afa68SYuanhan Liu pci_dev->driver->drv_flags &= ~RTE_PCI_DRV_INTR_LSC; 426c52afa68SYuanhan Liu 427c52afa68SYuanhan Liu return 0; 428c52afa68SYuanhan Liu } 429c52afa68SYuanhan Liu 430c52afa68SYuanhan Liu /* Extract I/O port numbers from sysfs */ 431c52afa68SYuanhan Liu static int 432c52afa68SYuanhan Liu legacy_virtio_resource_init(struct rte_pci_device *pci_dev) 433c52afa68SYuanhan Liu { 434c52afa68SYuanhan Liu if (virtio_resource_init_by_uio(pci_dev) == 0) 435c52afa68SYuanhan Liu return 0; 436c52afa68SYuanhan Liu else 437c52afa68SYuanhan Liu return virtio_resource_init_by_ioports(pci_dev); 438c52afa68SYuanhan Liu } 439c52afa68SYuanhan Liu 440c52afa68SYuanhan Liu #else 441c52afa68SYuanhan Liu static int 44225294cd3SDavid Marchand legacy_virtio_has_msix(const struct rte_pci_addr *loc __rte_unused) 443c52afa68SYuanhan Liu { 444c52afa68SYuanhan Liu /* nic_uio does not enable interrupts, return 0 (false). */ 445c52afa68SYuanhan Liu return 0; 446c52afa68SYuanhan Liu } 447c52afa68SYuanhan Liu 448c52afa68SYuanhan Liu static int 449c52afa68SYuanhan Liu legacy_virtio_resource_init(struct rte_pci_device *pci_dev __rte_unused) 450c52afa68SYuanhan Liu { 451c52afa68SYuanhan Liu /* no setup required */ 452c52afa68SYuanhan Liu return 0; 453c52afa68SYuanhan Liu } 454c52afa68SYuanhan Liu #endif 455d5bbeefcSYuanhan Liu 456d5bbeefcSYuanhan Liu static const struct virtio_pci_ops legacy_ops = { 457d5bbeefcSYuanhan Liu .read_dev_cfg = legacy_read_dev_config, 458d5bbeefcSYuanhan Liu .write_dev_cfg = legacy_write_dev_config, 459d5bbeefcSYuanhan Liu .reset = legacy_reset, 460d5bbeefcSYuanhan Liu .get_status = legacy_get_status, 461d5bbeefcSYuanhan Liu .set_status = legacy_set_status, 462d5bbeefcSYuanhan Liu .get_features = legacy_get_features, 463d5bbeefcSYuanhan Liu .set_features = legacy_set_features, 464d5bbeefcSYuanhan Liu .get_isr = legacy_get_isr, 465d5bbeefcSYuanhan Liu .set_config_irq = legacy_set_config_irq, 466d5bbeefcSYuanhan Liu .get_queue_num = legacy_get_queue_num, 467d5bbeefcSYuanhan Liu .setup_queue = legacy_setup_queue, 468d5bbeefcSYuanhan Liu .del_queue = legacy_del_queue, 469d5bbeefcSYuanhan Liu .notify_queue = legacy_notify_queue, 470d5bbeefcSYuanhan Liu }; 471d5bbeefcSYuanhan Liu 472d5bbeefcSYuanhan Liu 4736ba1f63bSYuanhan Liu static inline uint8_t 4746ba1f63bSYuanhan Liu io_read8(uint8_t *addr) 4756ba1f63bSYuanhan Liu { 4766ba1f63bSYuanhan Liu return *(volatile uint8_t *)addr; 4776ba1f63bSYuanhan Liu } 4786ba1f63bSYuanhan Liu 4796ba1f63bSYuanhan Liu static inline void 4806ba1f63bSYuanhan Liu io_write8(uint8_t val, uint8_t *addr) 4816ba1f63bSYuanhan Liu { 4826ba1f63bSYuanhan Liu *(volatile uint8_t *)addr = val; 4836ba1f63bSYuanhan Liu } 4846ba1f63bSYuanhan Liu 4856ba1f63bSYuanhan Liu static inline uint16_t 4866ba1f63bSYuanhan Liu io_read16(uint16_t *addr) 4876ba1f63bSYuanhan Liu { 4886ba1f63bSYuanhan Liu return *(volatile uint16_t *)addr; 4896ba1f63bSYuanhan Liu } 4906ba1f63bSYuanhan Liu 4916ba1f63bSYuanhan Liu static inline void 4926ba1f63bSYuanhan Liu io_write16(uint16_t val, uint16_t *addr) 4936ba1f63bSYuanhan Liu { 4946ba1f63bSYuanhan Liu *(volatile uint16_t *)addr = val; 4956ba1f63bSYuanhan Liu } 4966ba1f63bSYuanhan Liu 4976ba1f63bSYuanhan Liu static inline uint32_t 4986ba1f63bSYuanhan Liu io_read32(uint32_t *addr) 4996ba1f63bSYuanhan Liu { 5006ba1f63bSYuanhan Liu return *(volatile uint32_t *)addr; 5016ba1f63bSYuanhan Liu } 5026ba1f63bSYuanhan Liu 5036ba1f63bSYuanhan Liu static inline void 5046ba1f63bSYuanhan Liu io_write32(uint32_t val, uint32_t *addr) 5056ba1f63bSYuanhan Liu { 5066ba1f63bSYuanhan Liu *(volatile uint32_t *)addr = val; 5076ba1f63bSYuanhan Liu } 5086ba1f63bSYuanhan Liu 5096ba1f63bSYuanhan Liu static inline void 5106ba1f63bSYuanhan Liu io_write64_twopart(uint64_t val, uint32_t *lo, uint32_t *hi) 5116ba1f63bSYuanhan Liu { 5126ba1f63bSYuanhan Liu io_write32(val & ((1ULL << 32) - 1), lo); 5136ba1f63bSYuanhan Liu io_write32(val >> 32, hi); 5146ba1f63bSYuanhan Liu } 5156ba1f63bSYuanhan Liu 5166ba1f63bSYuanhan Liu static void 5176ba1f63bSYuanhan Liu modern_read_dev_config(struct virtio_hw *hw, size_t offset, 5186ba1f63bSYuanhan Liu void *dst, int length) 5196ba1f63bSYuanhan Liu { 5206ba1f63bSYuanhan Liu int i; 5216ba1f63bSYuanhan Liu uint8_t *p; 5226ba1f63bSYuanhan Liu uint8_t old_gen, new_gen; 5236ba1f63bSYuanhan Liu 5246ba1f63bSYuanhan Liu do { 5256ba1f63bSYuanhan Liu old_gen = io_read8(&hw->common_cfg->config_generation); 5266ba1f63bSYuanhan Liu 5276ba1f63bSYuanhan Liu p = dst; 5286ba1f63bSYuanhan Liu for (i = 0; i < length; i++) 5296ba1f63bSYuanhan Liu *p++ = io_read8((uint8_t *)hw->dev_cfg + offset + i); 5306ba1f63bSYuanhan Liu 5316ba1f63bSYuanhan Liu new_gen = io_read8(&hw->common_cfg->config_generation); 5326ba1f63bSYuanhan Liu } while (old_gen != new_gen); 5336ba1f63bSYuanhan Liu } 5346ba1f63bSYuanhan Liu 5356ba1f63bSYuanhan Liu static void 5366ba1f63bSYuanhan Liu modern_write_dev_config(struct virtio_hw *hw, size_t offset, 5376ba1f63bSYuanhan Liu const void *src, int length) 5386ba1f63bSYuanhan Liu { 5396ba1f63bSYuanhan Liu int i; 5406ba1f63bSYuanhan Liu const uint8_t *p = src; 5416ba1f63bSYuanhan Liu 5426ba1f63bSYuanhan Liu for (i = 0; i < length; i++) 5436ba1f63bSYuanhan Liu io_write8(*p++, (uint8_t *)hw->dev_cfg + offset + i); 5446ba1f63bSYuanhan Liu } 5456ba1f63bSYuanhan Liu 5466ba1f63bSYuanhan Liu static uint64_t 5476ba1f63bSYuanhan Liu modern_get_features(struct virtio_hw *hw) 5486ba1f63bSYuanhan Liu { 5496ba1f63bSYuanhan Liu uint32_t features_lo, features_hi; 5506ba1f63bSYuanhan Liu 5516ba1f63bSYuanhan Liu io_write32(0, &hw->common_cfg->device_feature_select); 5526ba1f63bSYuanhan Liu features_lo = io_read32(&hw->common_cfg->device_feature); 5536ba1f63bSYuanhan Liu 5546ba1f63bSYuanhan Liu io_write32(1, &hw->common_cfg->device_feature_select); 5556ba1f63bSYuanhan Liu features_hi = io_read32(&hw->common_cfg->device_feature); 5566ba1f63bSYuanhan Liu 5576ba1f63bSYuanhan Liu return ((uint64_t)features_hi << 32) | features_lo; 5586ba1f63bSYuanhan Liu } 5596ba1f63bSYuanhan Liu 5606ba1f63bSYuanhan Liu static void 5616ba1f63bSYuanhan Liu modern_set_features(struct virtio_hw *hw, uint64_t features) 5626ba1f63bSYuanhan Liu { 5636ba1f63bSYuanhan Liu io_write32(0, &hw->common_cfg->guest_feature_select); 5646ba1f63bSYuanhan Liu io_write32(features & ((1ULL << 32) - 1), 5656ba1f63bSYuanhan Liu &hw->common_cfg->guest_feature); 5666ba1f63bSYuanhan Liu 5676ba1f63bSYuanhan Liu io_write32(1, &hw->common_cfg->guest_feature_select); 5686ba1f63bSYuanhan Liu io_write32(features >> 32, 5696ba1f63bSYuanhan Liu &hw->common_cfg->guest_feature); 5706ba1f63bSYuanhan Liu } 5716ba1f63bSYuanhan Liu 5726ba1f63bSYuanhan Liu static uint8_t 5736ba1f63bSYuanhan Liu modern_get_status(struct virtio_hw *hw) 5746ba1f63bSYuanhan Liu { 5756ba1f63bSYuanhan Liu return io_read8(&hw->common_cfg->device_status); 5766ba1f63bSYuanhan Liu } 5776ba1f63bSYuanhan Liu 5786ba1f63bSYuanhan Liu static void 5796ba1f63bSYuanhan Liu modern_set_status(struct virtio_hw *hw, uint8_t status) 5806ba1f63bSYuanhan Liu { 5816ba1f63bSYuanhan Liu io_write8(status, &hw->common_cfg->device_status); 5826ba1f63bSYuanhan Liu } 5836ba1f63bSYuanhan Liu 5846ba1f63bSYuanhan Liu static void 5856ba1f63bSYuanhan Liu modern_reset(struct virtio_hw *hw) 5866ba1f63bSYuanhan Liu { 5876ba1f63bSYuanhan Liu modern_set_status(hw, VIRTIO_CONFIG_STATUS_RESET); 5886ba1f63bSYuanhan Liu modern_get_status(hw); 5896ba1f63bSYuanhan Liu } 5906ba1f63bSYuanhan Liu 5916ba1f63bSYuanhan Liu static uint8_t 5926ba1f63bSYuanhan Liu modern_get_isr(struct virtio_hw *hw) 5936ba1f63bSYuanhan Liu { 5946ba1f63bSYuanhan Liu return io_read8(hw->isr); 5956ba1f63bSYuanhan Liu } 5966ba1f63bSYuanhan Liu 5976ba1f63bSYuanhan Liu static uint16_t 5986ba1f63bSYuanhan Liu modern_set_config_irq(struct virtio_hw *hw, uint16_t vec) 5996ba1f63bSYuanhan Liu { 6006ba1f63bSYuanhan Liu io_write16(vec, &hw->common_cfg->msix_config); 6016ba1f63bSYuanhan Liu return io_read16(&hw->common_cfg->msix_config); 6026ba1f63bSYuanhan Liu } 6036ba1f63bSYuanhan Liu 6046ba1f63bSYuanhan Liu static uint16_t 6056ba1f63bSYuanhan Liu modern_get_queue_num(struct virtio_hw *hw, uint16_t queue_id) 6066ba1f63bSYuanhan Liu { 6076ba1f63bSYuanhan Liu io_write16(queue_id, &hw->common_cfg->queue_select); 6086ba1f63bSYuanhan Liu return io_read16(&hw->common_cfg->queue_size); 6096ba1f63bSYuanhan Liu } 6106ba1f63bSYuanhan Liu 6116ba1f63bSYuanhan Liu static void 6126ba1f63bSYuanhan Liu modern_setup_queue(struct virtio_hw *hw, struct virtqueue *vq) 6136ba1f63bSYuanhan Liu { 6146ba1f63bSYuanhan Liu uint64_t desc_addr, avail_addr, used_addr; 6156ba1f63bSYuanhan Liu uint16_t notify_off; 6166ba1f63bSYuanhan Liu 6176ba1f63bSYuanhan Liu desc_addr = vq->mz->phys_addr; 6186ba1f63bSYuanhan Liu avail_addr = desc_addr + vq->vq_nentries * sizeof(struct vring_desc); 6196ba1f63bSYuanhan Liu used_addr = RTE_ALIGN_CEIL(avail_addr + offsetof(struct vring_avail, 6206ba1f63bSYuanhan Liu ring[vq->vq_nentries]), 6216ba1f63bSYuanhan Liu VIRTIO_PCI_VRING_ALIGN); 6226ba1f63bSYuanhan Liu 6236ba1f63bSYuanhan Liu io_write16(vq->vq_queue_index, &hw->common_cfg->queue_select); 6246ba1f63bSYuanhan Liu 6256ba1f63bSYuanhan Liu io_write64_twopart(desc_addr, &hw->common_cfg->queue_desc_lo, 6266ba1f63bSYuanhan Liu &hw->common_cfg->queue_desc_hi); 6276ba1f63bSYuanhan Liu io_write64_twopart(avail_addr, &hw->common_cfg->queue_avail_lo, 6286ba1f63bSYuanhan Liu &hw->common_cfg->queue_avail_hi); 6296ba1f63bSYuanhan Liu io_write64_twopart(used_addr, &hw->common_cfg->queue_used_lo, 6306ba1f63bSYuanhan Liu &hw->common_cfg->queue_used_hi); 6316ba1f63bSYuanhan Liu 6326ba1f63bSYuanhan Liu notify_off = io_read16(&hw->common_cfg->queue_notify_off); 6336ba1f63bSYuanhan Liu vq->notify_addr = (void *)((uint8_t *)hw->notify_base + 6346ba1f63bSYuanhan Liu notify_off * hw->notify_off_multiplier); 6356ba1f63bSYuanhan Liu 6366ba1f63bSYuanhan Liu io_write16(1, &hw->common_cfg->queue_enable); 6376ba1f63bSYuanhan Liu 6386ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "queue %u addresses:", vq->vq_queue_index); 6396ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "\t desc_addr: %" PRIx64, desc_addr); 6406ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "\t aval_addr: %" PRIx64, avail_addr); 6416ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "\t used_addr: %" PRIx64, used_addr); 6426ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "\t notify addr: %p (notify offset: %u)", 6436ba1f63bSYuanhan Liu vq->notify_addr, notify_off); 6446ba1f63bSYuanhan Liu } 6456ba1f63bSYuanhan Liu 6466ba1f63bSYuanhan Liu static void 6476ba1f63bSYuanhan Liu modern_del_queue(struct virtio_hw *hw, struct virtqueue *vq) 6486ba1f63bSYuanhan Liu { 6496ba1f63bSYuanhan Liu io_write16(vq->vq_queue_index, &hw->common_cfg->queue_select); 6506ba1f63bSYuanhan Liu 6516ba1f63bSYuanhan Liu io_write64_twopart(0, &hw->common_cfg->queue_desc_lo, 6526ba1f63bSYuanhan Liu &hw->common_cfg->queue_desc_hi); 6536ba1f63bSYuanhan Liu io_write64_twopart(0, &hw->common_cfg->queue_avail_lo, 6546ba1f63bSYuanhan Liu &hw->common_cfg->queue_avail_hi); 6556ba1f63bSYuanhan Liu io_write64_twopart(0, &hw->common_cfg->queue_used_lo, 6566ba1f63bSYuanhan Liu &hw->common_cfg->queue_used_hi); 6576ba1f63bSYuanhan Liu 6586ba1f63bSYuanhan Liu io_write16(0, &hw->common_cfg->queue_enable); 6596ba1f63bSYuanhan Liu } 6606ba1f63bSYuanhan Liu 6616ba1f63bSYuanhan Liu static void 6626ba1f63bSYuanhan Liu modern_notify_queue(struct virtio_hw *hw __rte_unused, struct virtqueue *vq) 6636ba1f63bSYuanhan Liu { 6646ba1f63bSYuanhan Liu io_write16(1, vq->notify_addr); 6656ba1f63bSYuanhan Liu } 6666ba1f63bSYuanhan Liu 6676ba1f63bSYuanhan Liu static const struct virtio_pci_ops modern_ops = { 6686ba1f63bSYuanhan Liu .read_dev_cfg = modern_read_dev_config, 6696ba1f63bSYuanhan Liu .write_dev_cfg = modern_write_dev_config, 6706ba1f63bSYuanhan Liu .reset = modern_reset, 6716ba1f63bSYuanhan Liu .get_status = modern_get_status, 6726ba1f63bSYuanhan Liu .set_status = modern_set_status, 6736ba1f63bSYuanhan Liu .get_features = modern_get_features, 6746ba1f63bSYuanhan Liu .set_features = modern_set_features, 6756ba1f63bSYuanhan Liu .get_isr = modern_get_isr, 6766ba1f63bSYuanhan Liu .set_config_irq = modern_set_config_irq, 6776ba1f63bSYuanhan Liu .get_queue_num = modern_get_queue_num, 6786ba1f63bSYuanhan Liu .setup_queue = modern_setup_queue, 6796ba1f63bSYuanhan Liu .del_queue = modern_del_queue, 6806ba1f63bSYuanhan Liu .notify_queue = modern_notify_queue, 6816ba1f63bSYuanhan Liu }; 6826ba1f63bSYuanhan Liu 6836ba1f63bSYuanhan Liu 684d5bbeefcSYuanhan Liu void 685d5bbeefcSYuanhan Liu vtpci_read_dev_config(struct virtio_hw *hw, size_t offset, 686d5bbeefcSYuanhan Liu void *dst, int length) 687d5bbeefcSYuanhan Liu { 688d5bbeefcSYuanhan Liu hw->vtpci_ops->read_dev_cfg(hw, offset, dst, length); 689d5bbeefcSYuanhan Liu } 690d5bbeefcSYuanhan Liu 691d5bbeefcSYuanhan Liu void 692d5bbeefcSYuanhan Liu vtpci_write_dev_config(struct virtio_hw *hw, size_t offset, 693d5bbeefcSYuanhan Liu const void *src, int length) 694d5bbeefcSYuanhan Liu { 695d5bbeefcSYuanhan Liu hw->vtpci_ops->write_dev_cfg(hw, offset, src, length); 696d5bbeefcSYuanhan Liu } 697d5bbeefcSYuanhan Liu 6983891f233SYuanhan Liu uint64_t 6993891f233SYuanhan Liu vtpci_negotiate_features(struct virtio_hw *hw, uint64_t host_features) 7006c3169a3SBruce Richardson { 7013891f233SYuanhan Liu uint64_t features; 702d5bbeefcSYuanhan Liu 7036c3169a3SBruce Richardson /* 7046c3169a3SBruce Richardson * Limit negotiated features to what the driver, virtqueue, and 7056c3169a3SBruce Richardson * host all support. 7066c3169a3SBruce Richardson */ 7076c3169a3SBruce Richardson features = host_features & hw->guest_features; 708d5bbeefcSYuanhan Liu hw->vtpci_ops->set_features(hw, features); 7096c3169a3SBruce Richardson 7106c3169a3SBruce Richardson return features; 7116c3169a3SBruce Richardson } 7126c3169a3SBruce Richardson 7136c3169a3SBruce Richardson void 7146c3169a3SBruce Richardson vtpci_reset(struct virtio_hw *hw) 7156c3169a3SBruce Richardson { 716d5bbeefcSYuanhan Liu hw->vtpci_ops->set_status(hw, VIRTIO_CONFIG_STATUS_RESET); 717d5bbeefcSYuanhan Liu /* flush status write */ 718d5bbeefcSYuanhan Liu hw->vtpci_ops->get_status(hw); 7196c3169a3SBruce Richardson } 7206c3169a3SBruce Richardson 7216c3169a3SBruce Richardson void 7226c3169a3SBruce Richardson vtpci_reinit_complete(struct virtio_hw *hw) 7236c3169a3SBruce Richardson { 7246c3169a3SBruce Richardson vtpci_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER_OK); 7256c3169a3SBruce Richardson } 7266c3169a3SBruce Richardson 7276c3169a3SBruce Richardson void 7286c3169a3SBruce Richardson vtpci_set_status(struct virtio_hw *hw, uint8_t status) 7296c3169a3SBruce Richardson { 7306c3169a3SBruce Richardson if (status != VIRTIO_CONFIG_STATUS_RESET) 731d5bbeefcSYuanhan Liu status |= hw->vtpci_ops->get_status(hw); 7326c3169a3SBruce Richardson 733d5bbeefcSYuanhan Liu hw->vtpci_ops->set_status(hw, status); 7346c3169a3SBruce Richardson } 7356c3169a3SBruce Richardson 7366c3169a3SBruce Richardson uint8_t 7376ba1f63bSYuanhan Liu vtpci_get_status(struct virtio_hw *hw) 7386ba1f63bSYuanhan Liu { 7396ba1f63bSYuanhan Liu return hw->vtpci_ops->get_status(hw); 7406ba1f63bSYuanhan Liu } 7416ba1f63bSYuanhan Liu 7426ba1f63bSYuanhan Liu uint8_t 7436c3169a3SBruce Richardson vtpci_isr(struct virtio_hw *hw) 7446c3169a3SBruce Richardson { 745d5bbeefcSYuanhan Liu return hw->vtpci_ops->get_isr(hw); 7466c3169a3SBruce Richardson } 7476c3169a3SBruce Richardson 7486c3169a3SBruce Richardson 7496c3169a3SBruce Richardson /* Enable one vector (0) for Link State Intrerrupt */ 7506c3169a3SBruce Richardson uint16_t 7516c3169a3SBruce Richardson vtpci_irq_config(struct virtio_hw *hw, uint16_t vec) 7526c3169a3SBruce Richardson { 753d5bbeefcSYuanhan Liu return hw->vtpci_ops->set_config_irq(hw, vec); 754d5bbeefcSYuanhan Liu } 755d5bbeefcSYuanhan Liu 7566ba1f63bSYuanhan Liu static void * 7576ba1f63bSYuanhan Liu get_cfg_addr(struct rte_pci_device *dev, struct virtio_pci_cap *cap) 7586ba1f63bSYuanhan Liu { 7596ba1f63bSYuanhan Liu uint8_t bar = cap->bar; 7606ba1f63bSYuanhan Liu uint32_t length = cap->length; 7616ba1f63bSYuanhan Liu uint32_t offset = cap->offset; 7626ba1f63bSYuanhan Liu uint8_t *base; 7636ba1f63bSYuanhan Liu 7646ba1f63bSYuanhan Liu if (bar > 5) { 7656ba1f63bSYuanhan Liu PMD_INIT_LOG(ERR, "invalid bar: %u", bar); 7666ba1f63bSYuanhan Liu return NULL; 7676ba1f63bSYuanhan Liu } 7686ba1f63bSYuanhan Liu 7696ba1f63bSYuanhan Liu if (offset + length < offset) { 7706ba1f63bSYuanhan Liu PMD_INIT_LOG(ERR, "offset(%u) + length(%u) overflows", 7716ba1f63bSYuanhan Liu offset, length); 7726ba1f63bSYuanhan Liu return NULL; 7736ba1f63bSYuanhan Liu } 7746ba1f63bSYuanhan Liu 7756ba1f63bSYuanhan Liu if (offset + length > dev->mem_resource[bar].len) { 7766ba1f63bSYuanhan Liu PMD_INIT_LOG(ERR, 7776ba1f63bSYuanhan Liu "invalid cap: overflows bar space: %u > %" PRIu64, 7786ba1f63bSYuanhan Liu offset + length, dev->mem_resource[bar].len); 7796ba1f63bSYuanhan Liu return NULL; 7806ba1f63bSYuanhan Liu } 7816ba1f63bSYuanhan Liu 7826ba1f63bSYuanhan Liu base = dev->mem_resource[bar].addr; 7836ba1f63bSYuanhan Liu if (base == NULL) { 7846ba1f63bSYuanhan Liu PMD_INIT_LOG(ERR, "bar %u base addr is NULL", bar); 7856ba1f63bSYuanhan Liu return NULL; 7866ba1f63bSYuanhan Liu } 7876ba1f63bSYuanhan Liu 7886ba1f63bSYuanhan Liu return base + offset; 7896ba1f63bSYuanhan Liu } 7906ba1f63bSYuanhan Liu 7916ba1f63bSYuanhan Liu static int 7926ba1f63bSYuanhan Liu virtio_read_caps(struct rte_pci_device *dev, struct virtio_hw *hw) 7936ba1f63bSYuanhan Liu { 7946ba1f63bSYuanhan Liu uint8_t pos; 7956ba1f63bSYuanhan Liu struct virtio_pci_cap cap; 7966ba1f63bSYuanhan Liu int ret; 7976ba1f63bSYuanhan Liu 798*7a66c72dSDavid Marchand if (rte_eal_pci_map_device(dev)) { 7996ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "failed to map pci device!"); 8006ba1f63bSYuanhan Liu return -1; 8016ba1f63bSYuanhan Liu } 8026ba1f63bSYuanhan Liu 8036ba1f63bSYuanhan Liu ret = rte_eal_pci_read_config(dev, &pos, 1, PCI_CAPABILITY_LIST); 8046ba1f63bSYuanhan Liu if (ret < 0) { 8056ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "failed to read pci capability list"); 8066ba1f63bSYuanhan Liu return -1; 8076ba1f63bSYuanhan Liu } 8086ba1f63bSYuanhan Liu 8096ba1f63bSYuanhan Liu while (pos) { 8106ba1f63bSYuanhan Liu ret = rte_eal_pci_read_config(dev, &cap, sizeof(cap), pos); 8116ba1f63bSYuanhan Liu if (ret < 0) { 8126ba1f63bSYuanhan Liu PMD_INIT_LOG(ERR, 8136ba1f63bSYuanhan Liu "failed to read pci cap at pos: %x", pos); 8146ba1f63bSYuanhan Liu break; 8156ba1f63bSYuanhan Liu } 8166ba1f63bSYuanhan Liu 8176ba1f63bSYuanhan Liu if (cap.cap_vndr != PCI_CAP_ID_VNDR) { 8186ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, 8196ba1f63bSYuanhan Liu "[%2x] skipping non VNDR cap id: %02x", 8206ba1f63bSYuanhan Liu pos, cap.cap_vndr); 8216ba1f63bSYuanhan Liu goto next; 8226ba1f63bSYuanhan Liu } 8236ba1f63bSYuanhan Liu 8246ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, 8256ba1f63bSYuanhan Liu "[%2x] cfg type: %u, bar: %u, offset: %04x, len: %u", 8266ba1f63bSYuanhan Liu pos, cap.cfg_type, cap.bar, cap.offset, cap.length); 8276ba1f63bSYuanhan Liu 8286ba1f63bSYuanhan Liu switch (cap.cfg_type) { 8296ba1f63bSYuanhan Liu case VIRTIO_PCI_CAP_COMMON_CFG: 8306ba1f63bSYuanhan Liu hw->common_cfg = get_cfg_addr(dev, &cap); 8316ba1f63bSYuanhan Liu break; 8326ba1f63bSYuanhan Liu case VIRTIO_PCI_CAP_NOTIFY_CFG: 8336ba1f63bSYuanhan Liu rte_eal_pci_read_config(dev, &hw->notify_off_multiplier, 8346ba1f63bSYuanhan Liu 4, pos + sizeof(cap)); 8356ba1f63bSYuanhan Liu hw->notify_base = get_cfg_addr(dev, &cap); 8366ba1f63bSYuanhan Liu break; 8376ba1f63bSYuanhan Liu case VIRTIO_PCI_CAP_DEVICE_CFG: 8386ba1f63bSYuanhan Liu hw->dev_cfg = get_cfg_addr(dev, &cap); 8396ba1f63bSYuanhan Liu break; 8406ba1f63bSYuanhan Liu case VIRTIO_PCI_CAP_ISR_CFG: 8416ba1f63bSYuanhan Liu hw->isr = get_cfg_addr(dev, &cap); 8426ba1f63bSYuanhan Liu break; 8436ba1f63bSYuanhan Liu } 8446ba1f63bSYuanhan Liu 8456ba1f63bSYuanhan Liu next: 8466ba1f63bSYuanhan Liu pos = cap.cap_next; 8476ba1f63bSYuanhan Liu } 8486ba1f63bSYuanhan Liu 8496ba1f63bSYuanhan Liu if (hw->common_cfg == NULL || hw->notify_base == NULL || 8506ba1f63bSYuanhan Liu hw->dev_cfg == NULL || hw->isr == NULL) { 8516ba1f63bSYuanhan Liu PMD_INIT_LOG(INFO, "no modern virtio pci device found."); 8526ba1f63bSYuanhan Liu return -1; 8536ba1f63bSYuanhan Liu } 8546ba1f63bSYuanhan Liu 8556ba1f63bSYuanhan Liu PMD_INIT_LOG(INFO, "found modern virtio pci device."); 8566ba1f63bSYuanhan Liu 8576ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "common cfg mapped at: %p", hw->common_cfg); 8586ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "device cfg mapped at: %p", hw->dev_cfg); 8596ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "isr cfg mapped at: %p", hw->isr); 8606ba1f63bSYuanhan Liu PMD_INIT_LOG(DEBUG, "notify base: %p, notify off multiplier: %u", 8616ba1f63bSYuanhan Liu hw->notify_base, hw->notify_off_multiplier); 8626ba1f63bSYuanhan Liu 8636ba1f63bSYuanhan Liu return 0; 8646ba1f63bSYuanhan Liu } 8656ba1f63bSYuanhan Liu 866d5bbeefcSYuanhan Liu int 867c52afa68SYuanhan Liu vtpci_init(struct rte_pci_device *dev, struct virtio_hw *hw) 868d5bbeefcSYuanhan Liu { 8696ba1f63bSYuanhan Liu hw->dev = dev; 870d5bbeefcSYuanhan Liu 8716ba1f63bSYuanhan Liu /* 8726ba1f63bSYuanhan Liu * Try if we can succeed reading virtio pci caps, which exists 8736ba1f63bSYuanhan Liu * only on modern pci device. If failed, we fallback to legacy 8746ba1f63bSYuanhan Liu * virtio handling. 8756ba1f63bSYuanhan Liu */ 8766ba1f63bSYuanhan Liu if (virtio_read_caps(dev, hw) == 0) { 8776ba1f63bSYuanhan Liu PMD_INIT_LOG(INFO, "modern virtio pci detected."); 8786ba1f63bSYuanhan Liu hw->vtpci_ops = &modern_ops; 8796ba1f63bSYuanhan Liu hw->modern = 1; 8806ba1f63bSYuanhan Liu dev->driver->drv_flags |= RTE_PCI_DRV_INTR_LSC; 8816ba1f63bSYuanhan Liu return 0; 8826ba1f63bSYuanhan Liu } 8836ba1f63bSYuanhan Liu 8846ba1f63bSYuanhan Liu PMD_INIT_LOG(INFO, "trying with legacy virtio pci."); 885c52afa68SYuanhan Liu if (legacy_virtio_resource_init(dev) < 0) 886c52afa68SYuanhan Liu return -1; 8876ba1f63bSYuanhan Liu 8886ba1f63bSYuanhan Liu hw->vtpci_ops = &legacy_ops; 889c52afa68SYuanhan Liu hw->use_msix = legacy_virtio_has_msix(&dev->addr); 890c52afa68SYuanhan Liu hw->io_base = (uint32_t)(uintptr_t)dev->mem_resource[0].addr; 8916ba1f63bSYuanhan Liu hw->modern = 0; 892c52afa68SYuanhan Liu 893d5bbeefcSYuanhan Liu return 0; 8946c3169a3SBruce Richardson } 895