[PATCH 4/4][VTD] vt-d specific files in KVM

Kay, Allen M
2008-06-10T00:43:30Z

[-- Attachment #1: Type: text/plain, Size: 155 bytes --]

vt-d specific files in KVM for contructing vt-d page tables and
programming vt-d context entries.

Signed-off-by: Allen M. Kay <allen.m.kay@intel.com>

[-- Attachment #2: kvm_vtd.patch --]
[-- Type: application/octet-stream, Size: 7083 bytes --]

diff --git a/arch/x86/kvm/vtd.c b/arch/x86/kvm/vtd.c
new file mode 100644
index 0000000..634802c
--- /dev/null
+++ b/arch/x86/kvm/vtd.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2006, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Copyright (C) 2006-2008 Intel Corporation
+ * Author: Allen M. Kay <allen.m.kay@intel.com>
+ * Author: Weidong Han <weidong.han@intel.com>
+ */
+
+#include <linux/list.h>
+#include <linux/kvm_host.h>
+#include <linux/pci.h>
+#include <linux/dmar.h>
+#include <linux/intel-iommu.h>
+#include "vtd.h"
+
+int kvm_iommu_map_pages(struct kvm *kvm,
+ gfn_t base_gfn, unsigned long npages)
+{
+ gfn_t gfn = base_gfn;
+ pfn_t pfn;
+ struct page *page;
+ int i, rc;
+
+ if (!kvm->arch.domain)
+ return -EFAULT;
+
+ printk(KERN_DEBUG "kvm_iommu_map_page: gpa = %lx\n",
+ gfn << PAGE_SHIFT);
+ printk(KERN_DEBUG "kvm_iommu_map_page: hpa = %lx\n",
+ gfn_to_pfn(kvm, base_gfn) << PAGE_SHIFT);
+ printk(KERN_DEBUG "kvm_iommu_map_page: size = %lx\n",
+ npages*PAGE_SIZE);
+
+ for (i = 0; i < npages; i++) {
+ pfn = gfn_to_pfn(kvm, gfn);
+ if (pfn_valid(pfn)) {
+ rc = kvm_intel_iommu_page_mapping(kvm->arch.domain,
+ gfn << PAGE_SHIFT, pfn << PAGE_SHIFT,
+ PAGE_SIZE, DMA_PTE_READ | DMA_PTE_WRITE);
+ if (rc) {
+ page = gfn_to_page(kvm, gfn);
+ put_page(page);
+ }
+ } else {
+ printk(KERN_DEBUG "kvm_iommu_map_page:"
+ "invalid pfn=%lx\n", pfn);
+ return 0;
+ }
+ gfn++;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(kvm_iommu_map_pages);
+
+static int kvm_iommu_map_memslots(struct kvm *kvm)
+{
+ int i, rc;
+ for (i = 0; i < kvm->nmemslots; i++) {
+ rc = kvm_iommu_map_pages(kvm, kvm->memslots[i].base_gfn,
+ kvm->memslots[i].npages);
+ if (rc)
+ return rc;
+ }
+ return 0;
+}
+
+static int kvm_iommu_unmap_memslots(struct kvm *kvm);
+int kvm_iommu_map_guest(struct kvm *kvm,
+ struct kvm_pci_passthrough_dev *pci_pt_dev)
+{
+ struct pci_dev *pdev = NULL;
+
+ printk(KERN_DEBUG "kvm_iommu_map_guest: host bdf = %x:%x:%x\n",
+ pci_pt_dev->host.busnr,
+ PCI_SLOT(pci_pt_dev->host.devfn),
+ PCI_FUNC(pci_pt_dev->host.devfn));
+
+ for_each_pci_dev(pdev) {
+ if ((pdev->bus->number == pci_pt_dev->host.busnr) &&
+ (pdev->devfn == pci_pt_dev->host.devfn))
+ goto found;
+ }
+ if (kvm->arch.domain) {
+ kvm_intel_iommu_domain_exit(kvm->arch.domain);
+ kvm->arch.domain = NULL;
+ }
+ return -ENODEV;
+found:
+ kvm->arch.domain = kvm_intel_iommu_domain_alloc(pdev);
+ if (kvm->arch.domain == NULL)
+ printk(KERN_WARN "kvm_iommu_map_guest: domain == NULL\n");
+ else
+ printk(KERN_INFO "kvm_iommu_map_guest: domain = %p\n",
+ kvm->arch.domain);
+ if (kvm_iommu_map_memslots(kvm)) {
+ kvm_iommu_unmap_memslots(kvm);
+ return -EFAULT;
+ }
+ kvm_intel_iommu_context_mapping(kvm->arch.domain, pdev);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(kvm_iommu_map_guest);
+
+static int kvm_iommu_put_pages(struct kvm *kvm,
+ gfn_t base_gfn, unsigned long npages)
+{
+ gfn_t gfn = base_gfn;
+ struct page *page;
+ int i;
+
+ if (!kvm->arch.domain)
+ return -EFAULT;
+
+ printk(KERN_DEBUG "kvm_iommu_put_pages: gpa = %lx\n",
+ gfn << PAGE_SHIFT);
+ printk(KERN_DEBUG "kvm_iommu_put_pages: hpa = %lx\n",
+ gfn_to_pfn(kvm, gfn) << PAGE_SHIFT);
+ printk(KERN_DEBUG "kvm_iommu_put_pages: size = %lx\n",
+ npages*PAGE_SIZE);
+
+ for (i = 0; i < npages; i++) {
+ page = gfn_to_page(kvm, gfn);
+ put_page(page);
+ gfn++;
+ }
+ return 0;
+}
+
+static int kvm_iommu_unmap_memslots(struct kvm *kvm)
+{
+ int i, rc;
+ for (i = 0; i < kvm->nmemslots; i++) {
+ rc = kvm_iommu_put_pages(kvm, kvm->memslots[i].base_gfn,
+ kvm->memslots[i].npages);
+ if (rc)
+ return rc;
+ }
+ return 0;
+}
+
+int kvm_iommu_unmap_guest(struct kvm *kvm)
+{
+ struct dmar_domain *domain;
+ struct kvm_pci_pt_dev_list *entry;
+ struct pci_dev *pdev = NULL;
+
+ if (kvm->arch.domain)
+ return 0;
+
+ list_for_each_entry(entry, &kvm->arch.pci_pt_dev_head, list) {
+ printk(KERN_DEBUG "kvm_iommu_unmap_guest: %x:%x:%x\n",
+ entry->pt_dev.host.busnr,
+ PCI_SLOT(entry->pt_dev.host.devfn),
+ PCI_FUNC(entry->pt_dev.host.devfn));
+
+ for_each_pci_dev(pdev) {
+ if ((pdev->bus->number == entry->pt_dev.host.busnr) &&
+ (pdev->devfn == entry->pt_dev.host.devfn))
+ goto found;
+ }
+ return -ENODEV;
+found:
+ if (pdev == NULL) {
+ printk(KERN_ERR "kvm_iommu_unmap_guest:pdev == NULL\n");
+ return -EFAULT;
+ }
+
+ /* detach kvm dmar domain */
+ kvm_intel_iommu_detach_dev(kvm->arch.domain,
+ pdev->bus->number, pdev->devfn);
+
+ /* now restore back linux iommu domain */
+ domain = kvm_intel_iommu_find_domain(pdev);
+ if (domain)
+ kvm_intel_iommu_context_mapping(domain, pdev);
+ else
+ printk(KERN_DEBUG
+ "kvm_iommu_unmap_guest: domain == NULL\n");
+ }
+ kvm_iommu_unmap_memslots(kvm);
+ kvm_intel_iommu_domain_exit(kvm->arch.domain);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(kvm_iommu_unmap_guest);
diff --git a/arch/x86/kvm/vtd.h b/arch/x86/kvm/vtd.h
new file mode 100644
index 0000000..d03c15c
--- /dev/null
+++ b/arch/x86/kvm/vtd.h
@@ -0,0 +1,36 @@
+#ifndef __VTD_H
+#define __VTD_H
+
+/*
+ * Copyright (c) 2006, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Copyright (C) 2006-2008 Intel Corporation
+ * Author: Allen M. Kay <allen.m.kay@intel.com>
+ * Author: Weidong Han <weidong.han@intel.com>
+ */
+
+#define DEBUG
+
+int kvm_intel_iommu_context_mapping(struct dmar_domain *d,
+ struct pci_dev *pdev);
+int kvm_intel_iommu_page_mapping(struct dmar_domain *domain, dma_addr_t iova,
+ u64 hpa, size_t size, int prot);
+void kvm_intel_iommu_detach_dev(struct dmar_domain *domain, u8 bus, u8 devfn);
+struct dmar_domain *kvm_intel_iommu_domain_alloc(struct pci_dev *pdev);
+void kvm_intel_iommu_domain_exit(struct dmar_domain *domain);
+struct dmar_domain *kvm_intel_iommu_find_domain(struct pci_dev *pdev);
+
+#endif