From patchwork Tue Aug 30 02:27:17 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Michael S. Tsirkin" X-Patchwork-Id: 9304715 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 1B4F26086B for ; Tue, 30 Aug 2016 02:27:41 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0578B28A4F for ; Tue, 30 Aug 2016 02:27:41 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id EE48D28A7A; Tue, 30 Aug 2016 02:27:40 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5C19228A62 for ; Tue, 30 Aug 2016 02:27:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757285AbcH3C1W (ORCPT ); Mon, 29 Aug 2016 22:27:22 -0400 Received: from mx1.redhat.com ([209.132.183.28]:51382 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1757113AbcH3C1U (ORCPT ); Mon, 29 Aug 2016 22:27:20 -0400 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 811AC81F07; Tue, 30 Aug 2016 02:27:19 +0000 (UTC) Received: from redhat.com (vpn-60-237.rdu2.redhat.com [10.10.60.237]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with SMTP id u7U2RHPI021090; Mon, 29 Aug 2016 22:27:17 -0400 Date: Tue, 30 Aug 2016 05:27:17 +0300 From: "Michael S. Tsirkin" To: linux-kernel@vger.kernel.org Cc: Jason Wang , Alex Williamson , Cornelia Huck , virtualization@lists.linux-foundation.org, qemu-devel@nongnu.org, kvm@vger.kernel.org, Julia Lawall , Yongji Xie , Dan Carpenter , Feng Wu , Paolo Bonzini Subject: [PATCH v2 2/2] vfio: add virtio pci quirk Message-ID: <1472523968-9540-3-git-send-email-mst@redhat.com> References: <1472523968-9540-1-git-send-email-mst@redhat.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <1472523968-9540-1-git-send-email-mst@redhat.com> X-Mutt-Fcc: =sent X-Scanned-By: MIMEDefang 2.68 on 10.5.11.24 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.27]); Tue, 30 Aug 2016 02:27:19 +0000 (UTC) Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Modern virtio pci devices can set VIRTIO_F_IOMMU_PLATFORM to signal they are safe to use with an IOMMU. Without this bit, exposing the device to userspace is unsafe, so probe and fail VFIO initialization unless noiommu is enabled. Signed-off-by: Michael S. Tsirkin --- drivers/vfio/pci/vfio_pci_private.h | 1 + drivers/vfio/pci/vfio_pci.c | 14 ++++ drivers/vfio/pci/vfio_pci_virtio.c | 140 ++++++++++++++++++++++++++++++++++++ drivers/vfio/pci/Makefile | 1 + 4 files changed, 156 insertions(+) create mode 100644 drivers/vfio/pci/vfio_pci_virtio.c diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h index 2128de8..2bd5616 100644 --- a/drivers/vfio/pci/vfio_pci_private.h +++ b/drivers/vfio/pci/vfio_pci_private.h @@ -139,4 +139,5 @@ static inline int vfio_pci_igd_init(struct vfio_pci_device *vdev) return -ENODEV; } #endif +extern int vfio_pci_virtio_quirk(struct vfio_pci_device *vdev, bool noiommu); #endif /* VFIO_PCI_PRIVATE_H */ diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index d624a52..e93bf0c 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c @@ -1236,6 +1236,20 @@ static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) return ret; } + if (pdev->vendor == PCI_VENDOR_ID_REDHAT_QUMRANET) { + bool noiommu = vfio_is_noiommu_group_dev(&pdev->dev); + + ret = vfio_pci_virtio_quirk(vdev, noiommu); + if (ret) { + dev_warn(&vdev->pdev->dev, + "Failed to setup Virtio for VFIO\n"); + vfio_del_group_dev(&pdev->dev); + vfio_iommu_group_put(group, &pdev->dev); + kfree(vdev); + return ret; + } + } + if (vfio_pci_is_vga(pdev)) { vga_client_register(pdev, vdev, NULL, vfio_pci_set_vga_decode); vga_set_legacy_decoding(pdev, diff --git a/drivers/vfio/pci/vfio_pci_virtio.c b/drivers/vfio/pci/vfio_pci_virtio.c new file mode 100644 index 0000000..e1ecffd --- /dev/null +++ b/drivers/vfio/pci/vfio_pci_virtio.c @@ -0,0 +1,140 @@ +/* + * VFIO PCI Intel Graphics support + * + * Copyright (C) 2016 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Register a device specific region through which to provide read-only + * access to the Intel IGD opregion. The register defining the opregion + * address is also virtualized to prevent user modification. + */ + +#include +#include +#include +#include +#include +#include + +#include "vfio_pci_private.h" + +/** + * virtio_pci_find_capability - walk capabilities to find device info. + * @dev: the pci device + * @cfg_type: the VIRTIO_PCI_CAP_* value we seek + * + * Returns offset of the capability, or 0. + */ +static inline int virtio_pci_find_capability(struct pci_dev *dev, u8 cfg_type) +{ + int pos; + + for (pos = pci_find_capability(dev, PCI_CAP_ID_VNDR); + pos > 0; + pos = pci_find_next_capability(dev, pos, PCI_CAP_ID_VNDR)) { + u8 type; + pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_cap, + cfg_type), + &type); + + if (type != cfg_type) + continue; + + /* Ignore structures with reserved BAR values */ + if (type != VIRTIO_PCI_CAP_PCI_CFG) { + u8 bar; + + pci_read_config_byte(dev, pos + + offsetof(struct virtio_pci_cap, + bar), + &bar); + if (bar > 0x5) + continue; + } + + return pos; + } + return 0; +} + + +int vfio_pci_virtio_quirk(struct vfio_pci_device *vdev, bool noiommu) +{ + struct pci_dev *dev = vdev->pdev; + int common, cfg; + u32 features; + u32 offset; + u8 bar; + + /* Without an IOMMU, we don't care */ + if (noiommu) + return 0; + + /* Virtio only owns devices >= 0x1000 and <= 0x107f: leave the rest. */ + if (dev->device < 0x1000 || dev->device > 0x107f) + return 0; + + /* Check whether device enforces the IOMMU correctly */ + + /* + * All modern devices must have common and cfg capabilities. We use cfg + * capability for access so that we don't need to worry about resource + * availability. Slow but sure. + * Note that all vendor-specific fields we access are little-endian + * which matches what pci config accessors expect, so they do byteswap + * for us if appropriate. + */ + common = virtio_pci_find_capability(dev, VIRTIO_PCI_CAP_COMMON_CFG); + cfg = virtio_pci_find_capability(dev, VIRTIO_PCI_CAP_PCI_CFG); + if (!cfg || !common) { + dev_warn(&dev->dev, + "Virtio device lacks common or pci cfg.\n"); + return -ENODEV; + } + + pci_read_config_byte(dev, common + offsetof(struct virtio_pci_cap, + bar), + &bar); + pci_read_config_dword(dev, common + offsetof(struct virtio_pci_cap, + offset), + &offset); + + /* Program cfg capability for dword access into common cfg. */ + pci_write_config_byte(dev, cfg + offsetof(struct virtio_pci_cfg_cap, + cap.bar), + bar); + pci_write_config_dword(dev, cfg + offsetof(struct virtio_pci_cfg_cap, + cap.length), + 0x4); + + /* Select features dword that has VIRTIO_F_IOMMU_PLATFORM. */ + pci_write_config_dword(dev, cfg + offsetof(struct virtio_pci_cfg_cap, + cap.offset), + offset + offsetof(struct virtio_pci_common_cfg, + device_feature_select)); + pci_write_config_dword(dev, cfg + offsetof(struct virtio_pci_cfg_cap, + pci_cfg_data), + VIRTIO_F_IOMMU_PLATFORM / 32); + + /* Get the features dword. */ + pci_write_config_dword(dev, cfg + offsetof(struct virtio_pci_cfg_cap, + cap.offset), + offset + offsetof(struct virtio_pci_common_cfg, + device_feature)); + pci_read_config_dword(dev, cfg + offsetof(struct virtio_pci_cfg_cap, + pci_cfg_data), + &features); + + /* Does this device obey the platform's IOMMU? If not it's an error. */ + if (!(features & (0x1 << (VIRTIO_F_IOMMU_PLATFORM % 32)))) { + dev_warn(&dev->dev, + "Virtio device lacks VIRTIO_F_IOMMU_PLATFORM.\n"); + return -ENODEV; + } + + return 0; +} diff --git a/drivers/vfio/pci/Makefile b/drivers/vfio/pci/Makefile index 76d8ec0..e9b20e7 100644 --- a/drivers/vfio/pci/Makefile +++ b/drivers/vfio/pci/Makefile @@ -1,5 +1,6 @@ vfio-pci-y := vfio_pci.o vfio_pci_intrs.o vfio_pci_rdwr.o vfio_pci_config.o +vfio-pci-y += vfio_pci_virtio.o vfio-pci-$(CONFIG_VFIO_PCI_IGD) += vfio_pci_igd.o obj-$(CONFIG_VFIO_PCI) += vfio-pci.o