140bcdb12SYishai Hadas // SPDX-License-Identifier: GPL-2.0-only 240bcdb12SYishai Hadas /* 340bcdb12SYishai Hadas * Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved 440bcdb12SYishai Hadas */ 540bcdb12SYishai Hadas 640bcdb12SYishai Hadas #include <linux/device.h> 740bcdb12SYishai Hadas #include <linux/module.h> 840bcdb12SYishai Hadas #include <linux/mutex.h> 940bcdb12SYishai Hadas #include <linux/pci.h> 1040bcdb12SYishai Hadas #include <linux/pm_runtime.h> 1140bcdb12SYishai Hadas #include <linux/types.h> 1240bcdb12SYishai Hadas #include <linux/uaccess.h> 1340bcdb12SYishai Hadas #include <linux/vfio.h> 1440bcdb12SYishai Hadas #include <linux/vfio_pci_core.h> 1540bcdb12SYishai Hadas #include <linux/virtio_pci.h> 1640bcdb12SYishai Hadas #include <linux/virtio_net.h> 1740bcdb12SYishai Hadas #include <linux/virtio_pci_admin.h> 1840bcdb12SYishai Hadas 1940bcdb12SYishai Hadas #include "common.h" 2040bcdb12SYishai Hadas 2140bcdb12SYishai Hadas static int 2240bcdb12SYishai Hadas virtiovf_issue_legacy_rw_cmd(struct virtiovf_pci_core_device *virtvdev, 2340bcdb12SYishai Hadas loff_t pos, char __user *buf, 2440bcdb12SYishai Hadas size_t count, bool read) 2540bcdb12SYishai Hadas { 2640bcdb12SYishai Hadas bool msix_enabled = 2740bcdb12SYishai Hadas (virtvdev->core_device.irq_type == VFIO_PCI_MSIX_IRQ_INDEX); 2840bcdb12SYishai Hadas struct pci_dev *pdev = virtvdev->core_device.pdev; 2940bcdb12SYishai Hadas u8 *bar0_buf = virtvdev->bar0_virtual_buf; 3040bcdb12SYishai Hadas bool common; 3140bcdb12SYishai Hadas u8 offset; 3240bcdb12SYishai Hadas int ret; 3340bcdb12SYishai Hadas 3440bcdb12SYishai Hadas common = pos < VIRTIO_PCI_CONFIG_OFF(msix_enabled); 3540bcdb12SYishai Hadas /* offset within the relevant configuration area */ 3640bcdb12SYishai Hadas offset = common ? pos : pos - VIRTIO_PCI_CONFIG_OFF(msix_enabled); 3740bcdb12SYishai Hadas mutex_lock(&virtvdev->bar_mutex); 3840bcdb12SYishai Hadas if (read) { 3940bcdb12SYishai Hadas if (common) 4040bcdb12SYishai Hadas ret = virtio_pci_admin_legacy_common_io_read(pdev, offset, 4140bcdb12SYishai Hadas count, bar0_buf + pos); 4240bcdb12SYishai Hadas else 4340bcdb12SYishai Hadas ret = virtio_pci_admin_legacy_device_io_read(pdev, offset, 4440bcdb12SYishai Hadas count, bar0_buf + pos); 4540bcdb12SYishai Hadas if (ret) 4640bcdb12SYishai Hadas goto out; 4740bcdb12SYishai Hadas if (copy_to_user(buf, bar0_buf + pos, count)) 4840bcdb12SYishai Hadas ret = -EFAULT; 4940bcdb12SYishai Hadas } else { 5040bcdb12SYishai Hadas if (copy_from_user(bar0_buf + pos, buf, count)) { 5140bcdb12SYishai Hadas ret = -EFAULT; 5240bcdb12SYishai Hadas goto out; 5340bcdb12SYishai Hadas } 5440bcdb12SYishai Hadas 5540bcdb12SYishai Hadas if (common) 5640bcdb12SYishai Hadas ret = virtio_pci_admin_legacy_common_io_write(pdev, offset, 5740bcdb12SYishai Hadas count, bar0_buf + pos); 5840bcdb12SYishai Hadas else 5940bcdb12SYishai Hadas ret = virtio_pci_admin_legacy_device_io_write(pdev, offset, 6040bcdb12SYishai Hadas count, bar0_buf + pos); 6140bcdb12SYishai Hadas } 6240bcdb12SYishai Hadas out: 6340bcdb12SYishai Hadas mutex_unlock(&virtvdev->bar_mutex); 6440bcdb12SYishai Hadas return ret; 6540bcdb12SYishai Hadas } 6640bcdb12SYishai Hadas 6740bcdb12SYishai Hadas static int 6840bcdb12SYishai Hadas virtiovf_pci_bar0_rw(struct virtiovf_pci_core_device *virtvdev, 6940bcdb12SYishai Hadas loff_t pos, char __user *buf, 7040bcdb12SYishai Hadas size_t count, bool read) 7140bcdb12SYishai Hadas { 7240bcdb12SYishai Hadas struct vfio_pci_core_device *core_device = &virtvdev->core_device; 7340bcdb12SYishai Hadas struct pci_dev *pdev = core_device->pdev; 7440bcdb12SYishai Hadas u16 queue_notify; 7540bcdb12SYishai Hadas int ret; 7640bcdb12SYishai Hadas 7740bcdb12SYishai Hadas if (!(le16_to_cpu(virtvdev->pci_cmd) & PCI_COMMAND_IO)) 7840bcdb12SYishai Hadas return -EIO; 7940bcdb12SYishai Hadas 8040bcdb12SYishai Hadas if (pos + count > virtvdev->bar0_virtual_buf_size) 8140bcdb12SYishai Hadas return -EINVAL; 8240bcdb12SYishai Hadas 8340bcdb12SYishai Hadas ret = pm_runtime_resume_and_get(&pdev->dev); 8440bcdb12SYishai Hadas if (ret) { 8540bcdb12SYishai Hadas pci_info_ratelimited(pdev, "runtime resume failed %d\n", ret); 8640bcdb12SYishai Hadas return -EIO; 8740bcdb12SYishai Hadas } 8840bcdb12SYishai Hadas 8940bcdb12SYishai Hadas switch (pos) { 9040bcdb12SYishai Hadas case VIRTIO_PCI_QUEUE_NOTIFY: 9140bcdb12SYishai Hadas if (count != sizeof(queue_notify)) { 9240bcdb12SYishai Hadas ret = -EINVAL; 9340bcdb12SYishai Hadas goto end; 9440bcdb12SYishai Hadas } 9540bcdb12SYishai Hadas if (read) { 9640bcdb12SYishai Hadas ret = vfio_pci_core_ioread16(core_device, true, &queue_notify, 9740bcdb12SYishai Hadas virtvdev->notify_addr); 9840bcdb12SYishai Hadas if (ret) 9940bcdb12SYishai Hadas goto end; 10040bcdb12SYishai Hadas if (copy_to_user(buf, &queue_notify, 10140bcdb12SYishai Hadas sizeof(queue_notify))) { 10240bcdb12SYishai Hadas ret = -EFAULT; 10340bcdb12SYishai Hadas goto end; 10440bcdb12SYishai Hadas } 10540bcdb12SYishai Hadas } else { 10640bcdb12SYishai Hadas if (copy_from_user(&queue_notify, buf, count)) { 10740bcdb12SYishai Hadas ret = -EFAULT; 10840bcdb12SYishai Hadas goto end; 10940bcdb12SYishai Hadas } 11040bcdb12SYishai Hadas ret = vfio_pci_core_iowrite16(core_device, true, queue_notify, 11140bcdb12SYishai Hadas virtvdev->notify_addr); 11240bcdb12SYishai Hadas } 11340bcdb12SYishai Hadas break; 11440bcdb12SYishai Hadas default: 11540bcdb12SYishai Hadas ret = virtiovf_issue_legacy_rw_cmd(virtvdev, pos, buf, count, 11640bcdb12SYishai Hadas read); 11740bcdb12SYishai Hadas } 11840bcdb12SYishai Hadas 11940bcdb12SYishai Hadas end: 12040bcdb12SYishai Hadas pm_runtime_put(&pdev->dev); 12140bcdb12SYishai Hadas return ret ? ret : count; 12240bcdb12SYishai Hadas } 12340bcdb12SYishai Hadas 12440bcdb12SYishai Hadas static ssize_t virtiovf_pci_read_config(struct vfio_device *core_vdev, 12540bcdb12SYishai Hadas char __user *buf, size_t count, 12640bcdb12SYishai Hadas loff_t *ppos) 12740bcdb12SYishai Hadas { 12840bcdb12SYishai Hadas struct virtiovf_pci_core_device *virtvdev = container_of( 12940bcdb12SYishai Hadas core_vdev, struct virtiovf_pci_core_device, core_device.vdev); 13040bcdb12SYishai Hadas loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; 13140bcdb12SYishai Hadas size_t register_offset; 13240bcdb12SYishai Hadas loff_t copy_offset; 13340bcdb12SYishai Hadas size_t copy_count; 13440bcdb12SYishai Hadas __le32 val32; 13540bcdb12SYishai Hadas __le16 val16; 13640bcdb12SYishai Hadas u8 val8; 13740bcdb12SYishai Hadas int ret; 13840bcdb12SYishai Hadas 13940bcdb12SYishai Hadas ret = vfio_pci_core_read(core_vdev, buf, count, ppos); 14040bcdb12SYishai Hadas if (ret < 0) 14140bcdb12SYishai Hadas return ret; 14240bcdb12SYishai Hadas 14340bcdb12SYishai Hadas if (vfio_pci_core_range_intersect_range(pos, count, PCI_DEVICE_ID, 14440bcdb12SYishai Hadas sizeof(val16), ©_offset, 14540bcdb12SYishai Hadas ©_count, ®ister_offset)) { 14640bcdb12SYishai Hadas val16 = cpu_to_le16(VIRTIO_TRANS_ID_NET); 14740bcdb12SYishai Hadas if (copy_to_user(buf + copy_offset, (void *)&val16 + register_offset, copy_count)) 14840bcdb12SYishai Hadas return -EFAULT; 14940bcdb12SYishai Hadas } 15040bcdb12SYishai Hadas 15140bcdb12SYishai Hadas if ((le16_to_cpu(virtvdev->pci_cmd) & PCI_COMMAND_IO) && 15240bcdb12SYishai Hadas vfio_pci_core_range_intersect_range(pos, count, PCI_COMMAND, 15340bcdb12SYishai Hadas sizeof(val16), ©_offset, 15440bcdb12SYishai Hadas ©_count, ®ister_offset)) { 15540bcdb12SYishai Hadas if (copy_from_user((void *)&val16 + register_offset, buf + copy_offset, 15640bcdb12SYishai Hadas copy_count)) 15740bcdb12SYishai Hadas return -EFAULT; 15840bcdb12SYishai Hadas val16 |= cpu_to_le16(PCI_COMMAND_IO); 15940bcdb12SYishai Hadas if (copy_to_user(buf + copy_offset, (void *)&val16 + register_offset, 16040bcdb12SYishai Hadas copy_count)) 16140bcdb12SYishai Hadas return -EFAULT; 16240bcdb12SYishai Hadas } 16340bcdb12SYishai Hadas 16440bcdb12SYishai Hadas if (vfio_pci_core_range_intersect_range(pos, count, PCI_REVISION_ID, 16540bcdb12SYishai Hadas sizeof(val8), ©_offset, 16640bcdb12SYishai Hadas ©_count, ®ister_offset)) { 16740bcdb12SYishai Hadas /* Transional needs to have revision 0 */ 16840bcdb12SYishai Hadas val8 = 0; 16940bcdb12SYishai Hadas if (copy_to_user(buf + copy_offset, &val8, copy_count)) 17040bcdb12SYishai Hadas return -EFAULT; 17140bcdb12SYishai Hadas } 17240bcdb12SYishai Hadas 17340bcdb12SYishai Hadas if (vfio_pci_core_range_intersect_range(pos, count, PCI_BASE_ADDRESS_0, 17440bcdb12SYishai Hadas sizeof(val32), ©_offset, 17540bcdb12SYishai Hadas ©_count, ®ister_offset)) { 17640bcdb12SYishai Hadas u32 bar_mask = ~(virtvdev->bar0_virtual_buf_size - 1); 17740bcdb12SYishai Hadas u32 pci_base_addr_0 = le32_to_cpu(virtvdev->pci_base_addr_0); 17840bcdb12SYishai Hadas 17940bcdb12SYishai Hadas val32 = cpu_to_le32((pci_base_addr_0 & bar_mask) | PCI_BASE_ADDRESS_SPACE_IO); 18040bcdb12SYishai Hadas if (copy_to_user(buf + copy_offset, (void *)&val32 + register_offset, copy_count)) 18140bcdb12SYishai Hadas return -EFAULT; 18240bcdb12SYishai Hadas } 18340bcdb12SYishai Hadas 18440bcdb12SYishai Hadas if (vfio_pci_core_range_intersect_range(pos, count, PCI_SUBSYSTEM_ID, 18540bcdb12SYishai Hadas sizeof(val16), ©_offset, 18640bcdb12SYishai Hadas ©_count, ®ister_offset)) { 18740bcdb12SYishai Hadas /* 18840bcdb12SYishai Hadas * Transitional devices use the PCI subsystem device id as 18940bcdb12SYishai Hadas * virtio device id, same as legacy driver always did. 19040bcdb12SYishai Hadas */ 19140bcdb12SYishai Hadas val16 = cpu_to_le16(VIRTIO_ID_NET); 19240bcdb12SYishai Hadas if (copy_to_user(buf + copy_offset, (void *)&val16 + register_offset, 19340bcdb12SYishai Hadas copy_count)) 19440bcdb12SYishai Hadas return -EFAULT; 19540bcdb12SYishai Hadas } 19640bcdb12SYishai Hadas 19740bcdb12SYishai Hadas if (vfio_pci_core_range_intersect_range(pos, count, PCI_SUBSYSTEM_VENDOR_ID, 19840bcdb12SYishai Hadas sizeof(val16), ©_offset, 19940bcdb12SYishai Hadas ©_count, ®ister_offset)) { 20040bcdb12SYishai Hadas val16 = cpu_to_le16(PCI_VENDOR_ID_REDHAT_QUMRANET); 20140bcdb12SYishai Hadas if (copy_to_user(buf + copy_offset, (void *)&val16 + register_offset, 20240bcdb12SYishai Hadas copy_count)) 20340bcdb12SYishai Hadas return -EFAULT; 20440bcdb12SYishai Hadas } 20540bcdb12SYishai Hadas 20640bcdb12SYishai Hadas return count; 20740bcdb12SYishai Hadas } 20840bcdb12SYishai Hadas 20940bcdb12SYishai Hadas ssize_t virtiovf_pci_core_read(struct vfio_device *core_vdev, char __user *buf, 21040bcdb12SYishai Hadas size_t count, loff_t *ppos) 21140bcdb12SYishai Hadas { 21240bcdb12SYishai Hadas struct virtiovf_pci_core_device *virtvdev = container_of( 21340bcdb12SYishai Hadas core_vdev, struct virtiovf_pci_core_device, core_device.vdev); 21440bcdb12SYishai Hadas unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); 21540bcdb12SYishai Hadas loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; 21640bcdb12SYishai Hadas 21740bcdb12SYishai Hadas if (!count) 21840bcdb12SYishai Hadas return 0; 21940bcdb12SYishai Hadas 22040bcdb12SYishai Hadas if (index == VFIO_PCI_CONFIG_REGION_INDEX) 22140bcdb12SYishai Hadas return virtiovf_pci_read_config(core_vdev, buf, count, ppos); 22240bcdb12SYishai Hadas 22340bcdb12SYishai Hadas if (index == VFIO_PCI_BAR0_REGION_INDEX) 22440bcdb12SYishai Hadas return virtiovf_pci_bar0_rw(virtvdev, pos, buf, count, true); 22540bcdb12SYishai Hadas 22640bcdb12SYishai Hadas return vfio_pci_core_read(core_vdev, buf, count, ppos); 22740bcdb12SYishai Hadas } 22840bcdb12SYishai Hadas 22940bcdb12SYishai Hadas static ssize_t virtiovf_pci_write_config(struct vfio_device *core_vdev, 23040bcdb12SYishai Hadas const char __user *buf, size_t count, 23140bcdb12SYishai Hadas loff_t *ppos) 23240bcdb12SYishai Hadas { 23340bcdb12SYishai Hadas struct virtiovf_pci_core_device *virtvdev = container_of( 23440bcdb12SYishai Hadas core_vdev, struct virtiovf_pci_core_device, core_device.vdev); 23540bcdb12SYishai Hadas loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; 23640bcdb12SYishai Hadas size_t register_offset; 23740bcdb12SYishai Hadas loff_t copy_offset; 23840bcdb12SYishai Hadas size_t copy_count; 23940bcdb12SYishai Hadas 24040bcdb12SYishai Hadas if (vfio_pci_core_range_intersect_range(pos, count, PCI_COMMAND, 24140bcdb12SYishai Hadas sizeof(virtvdev->pci_cmd), 24240bcdb12SYishai Hadas ©_offset, ©_count, 24340bcdb12SYishai Hadas ®ister_offset)) { 24440bcdb12SYishai Hadas if (copy_from_user((void *)&virtvdev->pci_cmd + register_offset, 24540bcdb12SYishai Hadas buf + copy_offset, 24640bcdb12SYishai Hadas copy_count)) 24740bcdb12SYishai Hadas return -EFAULT; 24840bcdb12SYishai Hadas } 24940bcdb12SYishai Hadas 25040bcdb12SYishai Hadas if (vfio_pci_core_range_intersect_range(pos, count, PCI_BASE_ADDRESS_0, 25140bcdb12SYishai Hadas sizeof(virtvdev->pci_base_addr_0), 25240bcdb12SYishai Hadas ©_offset, ©_count, 25340bcdb12SYishai Hadas ®ister_offset)) { 25440bcdb12SYishai Hadas if (copy_from_user((void *)&virtvdev->pci_base_addr_0 + register_offset, 25540bcdb12SYishai Hadas buf + copy_offset, 25640bcdb12SYishai Hadas copy_count)) 25740bcdb12SYishai Hadas return -EFAULT; 25840bcdb12SYishai Hadas } 25940bcdb12SYishai Hadas 26040bcdb12SYishai Hadas return vfio_pci_core_write(core_vdev, buf, count, ppos); 26140bcdb12SYishai Hadas } 26240bcdb12SYishai Hadas 26340bcdb12SYishai Hadas ssize_t virtiovf_pci_core_write(struct vfio_device *core_vdev, const char __user *buf, 26440bcdb12SYishai Hadas size_t count, loff_t *ppos) 26540bcdb12SYishai Hadas { 26640bcdb12SYishai Hadas struct virtiovf_pci_core_device *virtvdev = container_of( 26740bcdb12SYishai Hadas core_vdev, struct virtiovf_pci_core_device, core_device.vdev); 26840bcdb12SYishai Hadas unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); 26940bcdb12SYishai Hadas loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; 27040bcdb12SYishai Hadas 27140bcdb12SYishai Hadas if (!count) 27240bcdb12SYishai Hadas return 0; 27340bcdb12SYishai Hadas 27440bcdb12SYishai Hadas if (index == VFIO_PCI_CONFIG_REGION_INDEX) 27540bcdb12SYishai Hadas return virtiovf_pci_write_config(core_vdev, buf, count, ppos); 27640bcdb12SYishai Hadas 27740bcdb12SYishai Hadas if (index == VFIO_PCI_BAR0_REGION_INDEX) 27840bcdb12SYishai Hadas return virtiovf_pci_bar0_rw(virtvdev, pos, (char __user *)buf, count, false); 27940bcdb12SYishai Hadas 28040bcdb12SYishai Hadas return vfio_pci_core_write(core_vdev, buf, count, ppos); 28140bcdb12SYishai Hadas } 28240bcdb12SYishai Hadas 28340bcdb12SYishai Hadas int virtiovf_pci_ioctl_get_region_info(struct vfio_device *core_vdev, 28440bcdb12SYishai Hadas unsigned int cmd, unsigned long arg) 28540bcdb12SYishai Hadas { 28640bcdb12SYishai Hadas struct virtiovf_pci_core_device *virtvdev = container_of( 28740bcdb12SYishai Hadas core_vdev, struct virtiovf_pci_core_device, core_device.vdev); 28840bcdb12SYishai Hadas unsigned long minsz = offsetofend(struct vfio_region_info, offset); 28940bcdb12SYishai Hadas void __user *uarg = (void __user *)arg; 29040bcdb12SYishai Hadas struct vfio_region_info info = {}; 29140bcdb12SYishai Hadas 29240bcdb12SYishai Hadas if (copy_from_user(&info, uarg, minsz)) 29340bcdb12SYishai Hadas return -EFAULT; 29440bcdb12SYishai Hadas 29540bcdb12SYishai Hadas if (info.argsz < minsz) 29640bcdb12SYishai Hadas return -EINVAL; 29740bcdb12SYishai Hadas 29840bcdb12SYishai Hadas switch (info.index) { 29940bcdb12SYishai Hadas case VFIO_PCI_BAR0_REGION_INDEX: 30040bcdb12SYishai Hadas info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index); 30140bcdb12SYishai Hadas info.size = virtvdev->bar0_virtual_buf_size; 30240bcdb12SYishai Hadas info.flags = VFIO_REGION_INFO_FLAG_READ | 30340bcdb12SYishai Hadas VFIO_REGION_INFO_FLAG_WRITE; 30440bcdb12SYishai Hadas return copy_to_user(uarg, &info, minsz) ? -EFAULT : 0; 30540bcdb12SYishai Hadas default: 30640bcdb12SYishai Hadas return vfio_pci_core_ioctl(core_vdev, cmd, arg); 30740bcdb12SYishai Hadas } 30840bcdb12SYishai Hadas } 30940bcdb12SYishai Hadas 31040bcdb12SYishai Hadas long virtiovf_vfio_pci_core_ioctl(struct vfio_device *core_vdev, unsigned int cmd, 31140bcdb12SYishai Hadas unsigned long arg) 31240bcdb12SYishai Hadas { 31340bcdb12SYishai Hadas switch (cmd) { 31440bcdb12SYishai Hadas case VFIO_DEVICE_GET_REGION_INFO: 31540bcdb12SYishai Hadas return virtiovf_pci_ioctl_get_region_info(core_vdev, cmd, arg); 31640bcdb12SYishai Hadas default: 31740bcdb12SYishai Hadas return vfio_pci_core_ioctl(core_vdev, cmd, arg); 31840bcdb12SYishai Hadas } 31940bcdb12SYishai Hadas } 32040bcdb12SYishai Hadas 32140bcdb12SYishai Hadas static int virtiovf_set_notify_addr(struct virtiovf_pci_core_device *virtvdev) 32240bcdb12SYishai Hadas { 32340bcdb12SYishai Hadas struct vfio_pci_core_device *core_device = &virtvdev->core_device; 32440bcdb12SYishai Hadas int ret; 32540bcdb12SYishai Hadas 32640bcdb12SYishai Hadas /* 32740bcdb12SYishai Hadas * Setup the BAR where the 'notify' exists to be used by vfio as well 32840bcdb12SYishai Hadas * This will let us mmap it only once and use it when needed. 32940bcdb12SYishai Hadas */ 33040bcdb12SYishai Hadas ret = vfio_pci_core_setup_barmap(core_device, 33140bcdb12SYishai Hadas virtvdev->notify_bar); 33240bcdb12SYishai Hadas if (ret) 33340bcdb12SYishai Hadas return ret; 33440bcdb12SYishai Hadas 33540bcdb12SYishai Hadas virtvdev->notify_addr = core_device->barmap[virtvdev->notify_bar] + 33640bcdb12SYishai Hadas virtvdev->notify_offset; 33740bcdb12SYishai Hadas return 0; 33840bcdb12SYishai Hadas } 33940bcdb12SYishai Hadas 34040bcdb12SYishai Hadas int virtiovf_open_legacy_io(struct virtiovf_pci_core_device *virtvdev) 34140bcdb12SYishai Hadas { 34240bcdb12SYishai Hadas if (!virtvdev->bar0_virtual_buf) 34340bcdb12SYishai Hadas return 0; 34440bcdb12SYishai Hadas 34540bcdb12SYishai Hadas /* 34640bcdb12SYishai Hadas * Upon close_device() the vfio_pci_core_disable() is called 34740bcdb12SYishai Hadas * and will close all the previous mmaps, so it seems that the 34840bcdb12SYishai Hadas * valid life cycle for the 'notify' addr is per open/close. 34940bcdb12SYishai Hadas */ 35040bcdb12SYishai Hadas return virtiovf_set_notify_addr(virtvdev); 35140bcdb12SYishai Hadas } 35240bcdb12SYishai Hadas 35340bcdb12SYishai Hadas static int virtiovf_get_device_config_size(unsigned short device) 35440bcdb12SYishai Hadas { 35540bcdb12SYishai Hadas /* Network card */ 35640bcdb12SYishai Hadas return offsetofend(struct virtio_net_config, status); 35740bcdb12SYishai Hadas } 35840bcdb12SYishai Hadas 35940bcdb12SYishai Hadas static int virtiovf_read_notify_info(struct virtiovf_pci_core_device *virtvdev) 36040bcdb12SYishai Hadas { 36140bcdb12SYishai Hadas u64 offset; 36240bcdb12SYishai Hadas int ret; 36340bcdb12SYishai Hadas u8 bar; 36440bcdb12SYishai Hadas 36540bcdb12SYishai Hadas ret = virtio_pci_admin_legacy_io_notify_info(virtvdev->core_device.pdev, 36640bcdb12SYishai Hadas VIRTIO_ADMIN_CMD_NOTIFY_INFO_FLAGS_OWNER_MEM, 36740bcdb12SYishai Hadas &bar, &offset); 36840bcdb12SYishai Hadas if (ret) 36940bcdb12SYishai Hadas return ret; 37040bcdb12SYishai Hadas 37140bcdb12SYishai Hadas virtvdev->notify_bar = bar; 37240bcdb12SYishai Hadas virtvdev->notify_offset = offset; 37340bcdb12SYishai Hadas return 0; 37440bcdb12SYishai Hadas } 37540bcdb12SYishai Hadas 37640bcdb12SYishai Hadas static bool virtiovf_bar0_exists(struct pci_dev *pdev) 37740bcdb12SYishai Hadas { 37840bcdb12SYishai Hadas struct resource *res = pdev->resource; 37940bcdb12SYishai Hadas 38040bcdb12SYishai Hadas return res->flags; 38140bcdb12SYishai Hadas } 38240bcdb12SYishai Hadas 38340bcdb12SYishai Hadas bool virtiovf_support_legacy_io(struct pci_dev *pdev) 38440bcdb12SYishai Hadas { 385*384a5301SYishai Hadas /* For now, the legacy IO functionality is supported only for virtio-net */ 386*384a5301SYishai Hadas return pdev->device == 0x1041 && virtio_pci_admin_has_legacy_io(pdev) && 387*384a5301SYishai Hadas !virtiovf_bar0_exists(pdev); 38840bcdb12SYishai Hadas } 38940bcdb12SYishai Hadas 39040bcdb12SYishai Hadas int virtiovf_init_legacy_io(struct virtiovf_pci_core_device *virtvdev) 39140bcdb12SYishai Hadas { 39240bcdb12SYishai Hadas struct pci_dev *pdev = virtvdev->core_device.pdev; 39340bcdb12SYishai Hadas int ret; 39440bcdb12SYishai Hadas 39540bcdb12SYishai Hadas ret = virtiovf_read_notify_info(virtvdev); 39640bcdb12SYishai Hadas if (ret) 39740bcdb12SYishai Hadas return ret; 39840bcdb12SYishai Hadas 39940bcdb12SYishai Hadas virtvdev->bar0_virtual_buf_size = VIRTIO_PCI_CONFIG_OFF(true) + 40040bcdb12SYishai Hadas virtiovf_get_device_config_size(pdev->device); 40140bcdb12SYishai Hadas BUILD_BUG_ON(!is_power_of_2(virtvdev->bar0_virtual_buf_size)); 40240bcdb12SYishai Hadas virtvdev->bar0_virtual_buf = kzalloc(virtvdev->bar0_virtual_buf_size, 40340bcdb12SYishai Hadas GFP_KERNEL); 40440bcdb12SYishai Hadas if (!virtvdev->bar0_virtual_buf) 40540bcdb12SYishai Hadas return -ENOMEM; 40640bcdb12SYishai Hadas mutex_init(&virtvdev->bar_mutex); 40740bcdb12SYishai Hadas return 0; 40840bcdb12SYishai Hadas } 40940bcdb12SYishai Hadas 41040bcdb12SYishai Hadas void virtiovf_release_legacy_io(struct virtiovf_pci_core_device *virtvdev) 41140bcdb12SYishai Hadas { 41240bcdb12SYishai Hadas kfree(virtvdev->bar0_virtual_buf); 41340bcdb12SYishai Hadas } 41440bcdb12SYishai Hadas 41540bcdb12SYishai Hadas void virtiovf_legacy_io_reset_done(struct pci_dev *pdev) 41640bcdb12SYishai Hadas { 41740bcdb12SYishai Hadas struct virtiovf_pci_core_device *virtvdev = dev_get_drvdata(&pdev->dev); 41840bcdb12SYishai Hadas 41940bcdb12SYishai Hadas virtvdev->pci_cmd = 0; 42040bcdb12SYishai Hadas } 421