4 * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * * Neither the name of Intel Corporation nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 #ifdef RTE_EXEC_ENV_LINUXAPP
40 #include "virtio_pci.h"
41 #include "virtio_logs.h"
42 #include "virtqueue.h"
45 legacy_read_dev_config(struct virtio_hw *hw, size_t offset,
46 void *dst, int length)
52 off = VIRTIO_PCI_CONFIG(hw) + offset;
53 for (d = dst; length > 0; d += size, off += size, length -= size) {
56 *(uint32_t *)d = VIRTIO_READ_REG_4(hw, off);
57 } else if (length >= 2) {
59 *(uint16_t *)d = VIRTIO_READ_REG_2(hw, off);
62 *d = VIRTIO_READ_REG_1(hw, off);
68 legacy_write_dev_config(struct virtio_hw *hw, size_t offset,
69 const void *src, int length)
75 off = VIRTIO_PCI_CONFIG(hw) + offset;
76 for (s = src; length > 0; s += size, off += size, length -= size) {
79 VIRTIO_WRITE_REG_4(hw, off, *(const uint32_t *)s);
80 } else if (length >= 2) {
82 VIRTIO_WRITE_REG_2(hw, off, *(const uint16_t *)s);
85 VIRTIO_WRITE_REG_1(hw, off, *s);
91 legacy_get_features(struct virtio_hw *hw)
93 return VIRTIO_READ_REG_4(hw, VIRTIO_PCI_HOST_FEATURES);
97 legacy_set_features(struct virtio_hw *hw, uint64_t features)
99 if ((features >> 32) != 0) {
101 "only 32 bit features are allowed for legacy virtio!");
104 VIRTIO_WRITE_REG_4(hw, VIRTIO_PCI_GUEST_FEATURES, features);
108 legacy_get_status(struct virtio_hw *hw)
110 return VIRTIO_READ_REG_1(hw, VIRTIO_PCI_STATUS);
114 legacy_set_status(struct virtio_hw *hw, uint8_t status)
116 VIRTIO_WRITE_REG_1(hw, VIRTIO_PCI_STATUS, status);
120 legacy_reset(struct virtio_hw *hw)
122 legacy_set_status(hw, VIRTIO_CONFIG_STATUS_RESET);
126 legacy_get_isr(struct virtio_hw *hw)
128 return VIRTIO_READ_REG_1(hw, VIRTIO_PCI_ISR);
131 /* Enable one vector (0) for Link State Intrerrupt */
133 legacy_set_config_irq(struct virtio_hw *hw, uint16_t vec)
135 VIRTIO_WRITE_REG_2(hw, VIRTIO_MSI_CONFIG_VECTOR, vec);
136 return VIRTIO_READ_REG_2(hw, VIRTIO_MSI_CONFIG_VECTOR);
140 legacy_get_queue_num(struct virtio_hw *hw, uint16_t queue_id)
142 VIRTIO_WRITE_REG_2(hw, VIRTIO_PCI_QUEUE_SEL, queue_id);
143 return VIRTIO_READ_REG_2(hw, VIRTIO_PCI_QUEUE_NUM);
147 legacy_setup_queue(struct virtio_hw *hw, struct virtqueue *vq)
149 VIRTIO_WRITE_REG_2(hw, VIRTIO_PCI_QUEUE_SEL, vq->vq_queue_index);
151 VIRTIO_WRITE_REG_4(hw, VIRTIO_PCI_QUEUE_PFN,
152 vq->mz->phys_addr >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
156 legacy_del_queue(struct virtio_hw *hw, struct virtqueue *vq)
158 VIRTIO_WRITE_REG_2(hw, VIRTIO_PCI_QUEUE_SEL, vq->vq_queue_index);
160 VIRTIO_WRITE_REG_4(hw, VIRTIO_PCI_QUEUE_PFN, 0);
164 legacy_notify_queue(struct virtio_hw *hw, struct virtqueue *vq)
166 VIRTIO_WRITE_REG_2(hw, VIRTIO_PCI_QUEUE_NOTIFY, vq->vq_queue_index);
169 #ifdef RTE_EXEC_ENV_LINUXAPP
171 parse_sysfs_value(const char *filename, unsigned long *val)
177 f = fopen(filename, "r");
179 PMD_INIT_LOG(ERR, "%s(): cannot open sysfs value %s",
184 if (fgets(buf, sizeof(buf), f) == NULL) {
185 PMD_INIT_LOG(ERR, "%s(): cannot read sysfs value %s",
190 *val = strtoul(buf, &end, 0);
191 if ((buf[0] == '\0') || (end == NULL) || (*end != '\n')) {
192 PMD_INIT_LOG(ERR, "%s(): cannot parse sysfs value %s",
202 get_uio_dev(struct rte_pci_addr *loc, char *buf, unsigned int buflen,
203 unsigned int *uio_num)
207 char dirname[PATH_MAX];
210 * depending on kernel version, uio can be located in uio/uioX
213 snprintf(dirname, sizeof(dirname),
214 SYSFS_PCI_DEVICES "/" PCI_PRI_FMT "/uio",
215 loc->domain, loc->bus, loc->devid, loc->function);
216 dir = opendir(dirname);
218 /* retry with the parent directory */
219 snprintf(dirname, sizeof(dirname),
220 SYSFS_PCI_DEVICES "/" PCI_PRI_FMT,
221 loc->domain, loc->bus, loc->devid, loc->function);
222 dir = opendir(dirname);
225 PMD_INIT_LOG(ERR, "Cannot opendir %s", dirname);
230 /* take the first file starting with "uio" */
231 while ((e = readdir(dir)) != NULL) {
232 /* format could be uio%d ...*/
233 int shortprefix_len = sizeof("uio") - 1;
234 /* ... or uio:uio%d */
235 int longprefix_len = sizeof("uio:uio") - 1;
238 if (strncmp(e->d_name, "uio", 3) != 0)
241 /* first try uio%d */
243 *uio_num = strtoull(e->d_name + shortprefix_len, &endptr, 10);
244 if (errno == 0 && endptr != (e->d_name + shortprefix_len)) {
245 snprintf(buf, buflen, "%s/uio%u", dirname, *uio_num);
249 /* then try uio:uio%d */
251 *uio_num = strtoull(e->d_name + longprefix_len, &endptr, 10);
252 if (errno == 0 && endptr != (e->d_name + longprefix_len)) {
253 snprintf(buf, buflen, "%s/uio:uio%u", dirname,
260 /* No uio resource found */
262 PMD_INIT_LOG(ERR, "Could not find uio resource");
270 legacy_virtio_has_msix(const struct rte_pci_addr *loc)
273 char dirname[PATH_MAX];
275 snprintf(dirname, sizeof(dirname),
276 SYSFS_PCI_DEVICES "/" PCI_PRI_FMT "/msi_irqs",
277 loc->domain, loc->bus, loc->devid, loc->function);
279 d = opendir(dirname);
286 /* Extract I/O port numbers from sysfs */
288 virtio_resource_init_by_uio(struct rte_pci_device *pci_dev)
290 char dirname[PATH_MAX];
291 char filename[PATH_MAX];
292 unsigned long start, size;
293 unsigned int uio_num;
295 if (get_uio_dev(&pci_dev->addr, dirname, sizeof(dirname), &uio_num) < 0)
298 /* get portio size */
299 snprintf(filename, sizeof(filename),
300 "%s/portio/port0/size", dirname);
301 if (parse_sysfs_value(filename, &size) < 0) {
302 PMD_INIT_LOG(ERR, "%s(): cannot parse size",
307 /* get portio start */
308 snprintf(filename, sizeof(filename),
309 "%s/portio/port0/start", dirname);
310 if (parse_sysfs_value(filename, &start) < 0) {
311 PMD_INIT_LOG(ERR, "%s(): cannot parse portio start",
315 pci_dev->mem_resource[0].addr = (void *)(uintptr_t)start;
316 pci_dev->mem_resource[0].len = (uint64_t)size;
318 "PCI Port IO found start=0x%lx with size=0x%lx",
322 memset(dirname, 0, sizeof(dirname));
323 snprintf(dirname, sizeof(dirname), "/dev/uio%u", uio_num);
324 pci_dev->intr_handle.fd = open(dirname, O_RDWR);
325 if (pci_dev->intr_handle.fd < 0) {
326 PMD_INIT_LOG(ERR, "Cannot open %s: %s\n",
327 dirname, strerror(errno));
331 pci_dev->intr_handle.type = RTE_INTR_HANDLE_UIO;
332 pci_dev->driver->drv_flags |= RTE_PCI_DRV_INTR_LSC;
337 /* Extract port I/O numbers from proc/ioports */
339 virtio_resource_init_by_ioports(struct rte_pci_device *pci_dev)
349 snprintf(pci_id, sizeof(pci_id), PCI_PRI_FMT,
350 pci_dev->addr.domain,
353 pci_dev->addr.function);
355 fp = fopen("/proc/ioports", "r");
357 PMD_INIT_LOG(ERR, "%s(): can't open ioports", __func__);
361 while (getdelim(&line, &linesz, '\n', fp) > 0) {
366 n = strcspn(ptr, ":");
370 while (*left && isspace(*left))
373 if (!strncmp(left, pci_id, strlen(pci_id))) {
376 while (*ptr && isspace(*ptr))
379 sscanf(ptr, "%04hx-%04hx", &start, &end);
380 size = end - start + 1;
392 pci_dev->mem_resource[0].addr = (void *)(uintptr_t)(uint32_t)start;
393 pci_dev->mem_resource[0].len = (uint64_t)size;
395 "PCI Port IO found start=0x%x with size=0x%x",
398 /* can't support lsc interrupt without uio */
399 pci_dev->driver->drv_flags &= ~RTE_PCI_DRV_INTR_LSC;
404 /* Extract I/O port numbers from sysfs */
406 legacy_virtio_resource_init(struct rte_pci_device *pci_dev)
408 if (virtio_resource_init_by_uio(pci_dev) == 0)
411 return virtio_resource_init_by_ioports(pci_dev);
416 legayc_virtio_has_msix(const struct rte_pci_addr *loc __rte_unused)
418 /* nic_uio does not enable interrupts, return 0 (false). */
423 legacy_virtio_resource_init(struct rte_pci_device *pci_dev __rte_unused)
425 /* no setup required */
430 static const struct virtio_pci_ops legacy_ops = {
431 .read_dev_cfg = legacy_read_dev_config,
432 .write_dev_cfg = legacy_write_dev_config,
433 .reset = legacy_reset,
434 .get_status = legacy_get_status,
435 .set_status = legacy_set_status,
436 .get_features = legacy_get_features,
437 .set_features = legacy_set_features,
438 .get_isr = legacy_get_isr,
439 .set_config_irq = legacy_set_config_irq,
440 .get_queue_num = legacy_get_queue_num,
441 .setup_queue = legacy_setup_queue,
442 .del_queue = legacy_del_queue,
443 .notify_queue = legacy_notify_queue,
448 vtpci_read_dev_config(struct virtio_hw *hw, size_t offset,
449 void *dst, int length)
451 hw->vtpci_ops->read_dev_cfg(hw, offset, dst, length);
455 vtpci_write_dev_config(struct virtio_hw *hw, size_t offset,
456 const void *src, int length)
458 hw->vtpci_ops->write_dev_cfg(hw, offset, src, length);
462 vtpci_negotiate_features(struct virtio_hw *hw, uint64_t host_features)
467 * Limit negotiated features to what the driver, virtqueue, and
470 features = host_features & hw->guest_features;
471 hw->vtpci_ops->set_features(hw, features);
477 vtpci_reset(struct virtio_hw *hw)
479 hw->vtpci_ops->set_status(hw, VIRTIO_CONFIG_STATUS_RESET);
480 /* flush status write */
481 hw->vtpci_ops->get_status(hw);
485 vtpci_reinit_complete(struct virtio_hw *hw)
487 vtpci_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER_OK);
491 vtpci_set_status(struct virtio_hw *hw, uint8_t status)
493 if (status != VIRTIO_CONFIG_STATUS_RESET)
494 status |= hw->vtpci_ops->get_status(hw);
496 hw->vtpci_ops->set_status(hw, status);
500 vtpci_isr(struct virtio_hw *hw)
502 return hw->vtpci_ops->get_isr(hw);
506 /* Enable one vector (0) for Link State Intrerrupt */
508 vtpci_irq_config(struct virtio_hw *hw, uint16_t vec)
510 return hw->vtpci_ops->set_config_irq(hw, vec);
514 vtpci_init(struct rte_pci_device *dev, struct virtio_hw *hw)
516 hw->vtpci_ops = &legacy_ops;
518 if (legacy_virtio_resource_init(dev) < 0)
520 hw->use_msix = legacy_virtio_has_msix(&dev->addr);
521 hw->io_base = (uint32_t)(uintptr_t)dev->mem_resource[0].addr;