Blame view
virt/kvm/iommu.c
7.1 KB
62c476c7c KVM: Device Assig... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/* * 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 * Copyright IBM Corporation, 2008 |
221d059d1 KVM: Update Red H... |
19 20 |
* Copyright 2010 Red Hat, Inc. and/or its affiliates. * |
62c476c7c KVM: Device Assig... |
21 22 23 24 25 26 27 28 29 |
* Author: Allen M. Kay <allen.m.kay@intel.com> * Author: Weidong Han <weidong.han@intel.com> * Author: Ben-Ami Yassour <benami@il.ibm.com> */ #include <linux/list.h> #include <linux/kvm_host.h> #include <linux/pci.h> #include <linux/dmar.h> |
19de40a84 KVM: change KVM t... |
30 |
#include <linux/iommu.h> |
62c476c7c KVM: Device Assig... |
31 32 33 34 35 |
#include <linux/intel-iommu.h> static int kvm_iommu_unmap_memslots(struct kvm *kvm); static void kvm_iommu_put_pages(struct kvm *kvm, gfn_t base_gfn, unsigned long npages); |
fcd95807f kvm: Change kvm_i... |
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
static pfn_t kvm_pin_pages(struct kvm *kvm, struct kvm_memory_slot *slot, gfn_t gfn, unsigned long size) { gfn_t end_gfn; pfn_t pfn; pfn = gfn_to_pfn_memslot(kvm, slot, gfn); end_gfn = gfn + (size >> PAGE_SHIFT); gfn += 1; if (is_error_pfn(pfn)) return pfn; while (gfn < end_gfn) gfn_to_pfn_memslot(kvm, slot, gfn++); return pfn; } |
3ad26d813 KVM: use gfn_to_p... |
54 |
int kvm_iommu_map_pages(struct kvm *kvm, struct kvm_memory_slot *slot) |
62c476c7c KVM: Device Assig... |
55 |
{ |
fcd95807f kvm: Change kvm_i... |
56 |
gfn_t gfn, end_gfn; |
62c476c7c KVM: Device Assig... |
57 |
pfn_t pfn; |
fcd95807f kvm: Change kvm_i... |
58 |
int r = 0; |
19de40a84 KVM: change KVM t... |
59 |
struct iommu_domain *domain = kvm->arch.iommu_domain; |
522c68c44 KVM: Enable snoop... |
60 |
int flags; |
62c476c7c KVM: Device Assig... |
61 62 63 64 |
/* check if iommu exists and in use */ if (!domain) return 0; |
fcd95807f kvm: Change kvm_i... |
65 66 |
gfn = slot->base_gfn; end_gfn = gfn + slot->npages; |
522c68c44 KVM: Enable snoop... |
67 68 69 |
flags = IOMMU_READ | IOMMU_WRITE; if (kvm->arch.iommu_flags & KVM_IOMMU_CACHE_COHERENCY) flags |= IOMMU_CACHE; |
fcd95807f kvm: Change kvm_i... |
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
while (gfn < end_gfn) { unsigned long page_size; /* Check if already mapped */ if (iommu_iova_to_phys(domain, gfn_to_gpa(gfn))) { gfn += 1; continue; } /* Get the page size we could use to map */ page_size = kvm_host_page_size(kvm, gfn); /* Make sure the page_size does not exceed the memslot */ while ((gfn + (page_size >> PAGE_SHIFT)) > end_gfn) page_size >>= 1; /* Make sure gfn is aligned to the page size we want to map */ while ((gfn << PAGE_SHIFT) & (page_size - 1)) page_size >>= 1; /* * Pin all pages we are about to map in memory. This is * important because we unmap and unpin in 4kb steps later. */ pfn = kvm_pin_pages(kvm, slot, gfn, page_size); if (is_error_pfn(pfn)) { gfn += 1; |
62c476c7c KVM: Device Assig... |
98 |
continue; |
fcd95807f kvm: Change kvm_i... |
99 |
} |
62c476c7c KVM: Device Assig... |
100 |
|
fcd95807f kvm: Change kvm_i... |
101 102 103 |
/* Map into IO address space */ r = iommu_map(domain, gfn_to_gpa(gfn), pfn_to_hpa(pfn), get_order(page_size), flags); |
e5fcfc821 KVM: Device Assig... |
104 |
if (r) { |
260782bcf KVM: use the new ... |
105 |
printk(KERN_ERR "kvm_iommu_map_address:" |
5689cc53f KVM: Use u64 for ... |
106 107 |
"iommu failed to map pfn=%llx ", pfn); |
62c476c7c KVM: Device Assig... |
108 109 |
goto unmap_pages; } |
fcd95807f kvm: Change kvm_i... |
110 111 |
gfn += page_size >> PAGE_SHIFT; |
62c476c7c KVM: Device Assig... |
112 |
} |
fcd95807f kvm: Change kvm_i... |
113 |
|
62c476c7c KVM: Device Assig... |
114 115 116 |
return 0; unmap_pages: |
fcd95807f kvm: Change kvm_i... |
117 |
kvm_iommu_put_pages(kvm, slot->base_gfn, gfn); |
62c476c7c KVM: Device Assig... |
118 119 120 121 122 |
return r; } static int kvm_iommu_map_memslots(struct kvm *kvm) { |
95c87e2b4 KVM: Fix IOMMU me... |
123 |
int i, idx, r = 0; |
46a26bf55 KVM: modify memsl... |
124 |
struct kvm_memslots *slots; |
62c476c7c KVM: Device Assig... |
125 |
|
95c87e2b4 KVM: Fix IOMMU me... |
126 |
idx = srcu_read_lock(&kvm->srcu); |
90d83dc3d KVM: use the corr... |
127 |
slots = kvm_memslots(kvm); |
46a26bf55 KVM: modify memsl... |
128 129 |
for (i = 0; i < slots->nmemslots; i++) { |
3ad26d813 KVM: use gfn_to_p... |
130 |
r = kvm_iommu_map_pages(kvm, &slots->memslots[i]); |
62c476c7c KVM: Device Assig... |
131 132 133 |
if (r) break; } |
95c87e2b4 KVM: Fix IOMMU me... |
134 |
srcu_read_unlock(&kvm->srcu, idx); |
682edb4c0 KVM: Fix assigned... |
135 |
|
62c476c7c KVM: Device Assig... |
136 137 |
return r; } |
260782bcf KVM: use the new ... |
138 139 |
int kvm_assign_device(struct kvm *kvm, struct kvm_assigned_dev_kernel *assigned_dev) |
62c476c7c KVM: Device Assig... |
140 141 |
{ struct pci_dev *pdev = NULL; |
19de40a84 KVM: change KVM t... |
142 |
struct iommu_domain *domain = kvm->arch.iommu_domain; |
522c68c44 KVM: Enable snoop... |
143 |
int r, last_flags; |
62c476c7c KVM: Device Assig... |
144 |
|
260782bcf KVM: use the new ... |
145 146 147 148 149 150 |
/* check if iommu exists and in use */ if (!domain) return 0; pdev = assigned_dev->dev; if (pdev == NULL) |
62c476c7c KVM: Device Assig... |
151 |
return -ENODEV; |
260782bcf KVM: use the new ... |
152 |
|
19de40a84 KVM: change KVM t... |
153 |
r = iommu_attach_device(domain, &pdev->dev); |
260782bcf KVM: use the new ... |
154 |
if (r) { |
ab9f4ecbb KVM: enable PCI m... |
155 156 |
printk(KERN_ERR "assign device %x:%x:%x.%x failed", pci_domain_nr(pdev->bus), |
260782bcf KVM: use the new ... |
157 158 159 160 |
pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); return r; |
62c476c7c KVM: Device Assig... |
161 |
} |
522c68c44 KVM: Enable snoop... |
162 163 164 165 166 167 168 169 170 171 172 173 174 |
last_flags = kvm->arch.iommu_flags; if (iommu_domain_has_cap(kvm->arch.iommu_domain, IOMMU_CAP_CACHE_COHERENCY)) kvm->arch.iommu_flags |= KVM_IOMMU_CACHE_COHERENCY; /* Check if need to update IOMMU page table for guest memory */ if ((last_flags ^ kvm->arch.iommu_flags) == KVM_IOMMU_CACHE_COHERENCY) { kvm_iommu_unmap_memslots(kvm); r = kvm_iommu_map_memslots(kvm); if (r) goto out_unmap; } |
ab9f4ecbb KVM: enable PCI m... |
175 176 177 |
printk(KERN_DEBUG "assign device %x:%x:%x.%x ", assigned_dev->host_segnr, |
260782bcf KVM: use the new ... |
178 179 180 |
assigned_dev->host_busnr, PCI_SLOT(assigned_dev->host_devfn), PCI_FUNC(assigned_dev->host_devfn)); |
62c476c7c KVM: Device Assig... |
181 |
|
260782bcf KVM: use the new ... |
182 |
return 0; |
522c68c44 KVM: Enable snoop... |
183 184 185 |
out_unmap: kvm_iommu_unmap_memslots(kvm); return r; |
260782bcf KVM: use the new ... |
186 |
} |
62c476c7c KVM: Device Assig... |
187 |
|
0a9203567 KVM: support devi... |
188 189 190 |
int kvm_deassign_device(struct kvm *kvm, struct kvm_assigned_dev_kernel *assigned_dev) { |
19de40a84 KVM: change KVM t... |
191 |
struct iommu_domain *domain = kvm->arch.iommu_domain; |
0a9203567 KVM: support devi... |
192 193 194 195 196 197 198 199 200 |
struct pci_dev *pdev = NULL; /* check if iommu exists and in use */ if (!domain) return 0; pdev = assigned_dev->dev; if (pdev == NULL) return -ENODEV; |
19de40a84 KVM: change KVM t... |
201 |
iommu_detach_device(domain, &pdev->dev); |
0a9203567 KVM: support devi... |
202 |
|
ab9f4ecbb KVM: enable PCI m... |
203 204 205 |
printk(KERN_DEBUG "deassign device %x:%x:%x.%x ", assigned_dev->host_segnr, |
0a9203567 KVM: support devi... |
206 207 208 209 210 211 |
assigned_dev->host_busnr, PCI_SLOT(assigned_dev->host_devfn), PCI_FUNC(assigned_dev->host_devfn)); return 0; } |
260782bcf KVM: use the new ... |
212 213 214 |
int kvm_iommu_map_guest(struct kvm *kvm) { int r; |
19de40a84 KVM: change KVM t... |
215 216 217 |
if (!iommu_found()) { printk(KERN_ERR "%s: iommu not found ", __func__); |
62c476c7c KVM: Device Assig... |
218 219 |
return -ENODEV; } |
19de40a84 KVM: change KVM t... |
220 221 |
kvm->arch.iommu_domain = iommu_domain_alloc(); if (!kvm->arch.iommu_domain) |
260782bcf KVM: use the new ... |
222 |
return -ENOMEM; |
62c476c7c KVM: Device Assig... |
223 224 225 226 |
r = kvm_iommu_map_memslots(kvm); if (r) goto out_unmap; |
62c476c7c KVM: Device Assig... |
227 228 229 230 231 232 |
return 0; out_unmap: kvm_iommu_unmap_memslots(kvm); return r; } |
fcd95807f kvm: Change kvm_i... |
233 234 235 236 237 238 239 |
static void kvm_unpin_pages(struct kvm *kvm, pfn_t pfn, unsigned long npages) { unsigned long i; for (i = 0; i < npages; ++i) kvm_release_pfn_clean(pfn + i); } |
62c476c7c KVM: Device Assig... |
240 |
static void kvm_iommu_put_pages(struct kvm *kvm, |
260782bcf KVM: use the new ... |
241 |
gfn_t base_gfn, unsigned long npages) |
62c476c7c KVM: Device Assig... |
242 |
{ |
fcd95807f kvm: Change kvm_i... |
243 244 |
struct iommu_domain *domain; gfn_t end_gfn, gfn; |
62c476c7c KVM: Device Assig... |
245 |
pfn_t pfn; |
260782bcf KVM: use the new ... |
246 |
u64 phys; |
fcd95807f kvm: Change kvm_i... |
247 248 249 |
domain = kvm->arch.iommu_domain; end_gfn = base_gfn + npages; gfn = base_gfn; |
260782bcf KVM: use the new ... |
250 251 252 |
/* check if iommu exists and in use */ if (!domain) return; |
62c476c7c KVM: Device Assig... |
253 |
|
fcd95807f kvm: Change kvm_i... |
254 255 256 257 258 |
while (gfn < end_gfn) { unsigned long unmap_pages; int order; /* Get physical address */ |
19de40a84 KVM: change KVM t... |
259 |
phys = iommu_iova_to_phys(domain, gfn_to_gpa(gfn)); |
fcd95807f kvm: Change kvm_i... |
260 261 262 |
pfn = phys >> PAGE_SHIFT; /* Unmap address from IO address space */ |
05b782ab9 KVM: Fix order pa... |
263 |
order = iommu_unmap(domain, gfn_to_gpa(gfn), 0); |
fcd95807f kvm: Change kvm_i... |
264 |
unmap_pages = 1ULL << order; |
260782bcf KVM: use the new ... |
265 |
|
fcd95807f kvm: Change kvm_i... |
266 267 268 269 270 |
/* Unpin all pages we just unmapped to not leak any memory */ kvm_unpin_pages(kvm, pfn, unmap_pages); gfn += unmap_pages; } |
62c476c7c KVM: Device Assig... |
271 272 273 274 |
} static int kvm_iommu_unmap_memslots(struct kvm *kvm) { |
95c87e2b4 KVM: Fix IOMMU me... |
275 |
int i, idx; |
46a26bf55 KVM: modify memsl... |
276 |
struct kvm_memslots *slots; |
95c87e2b4 KVM: Fix IOMMU me... |
277 |
idx = srcu_read_lock(&kvm->srcu); |
90d83dc3d KVM: use the corr... |
278 |
slots = kvm_memslots(kvm); |
682edb4c0 KVM: Fix assigned... |
279 |
|
46a26bf55 KVM: modify memsl... |
280 281 282 |
for (i = 0; i < slots->nmemslots; i++) { kvm_iommu_put_pages(kvm, slots->memslots[i].base_gfn, slots->memslots[i].npages); |
62c476c7c KVM: Device Assig... |
283 |
} |
95c87e2b4 KVM: Fix IOMMU me... |
284 |
srcu_read_unlock(&kvm->srcu, idx); |
62c476c7c KVM: Device Assig... |
285 286 287 288 289 290 |
return 0; } int kvm_iommu_unmap_guest(struct kvm *kvm) { |
19de40a84 KVM: change KVM t... |
291 |
struct iommu_domain *domain = kvm->arch.iommu_domain; |
62c476c7c KVM: Device Assig... |
292 293 294 295 |
/* check if iommu exists and in use */ if (!domain) return 0; |
62c476c7c KVM: Device Assig... |
296 |
kvm_iommu_unmap_memslots(kvm); |
19de40a84 KVM: change KVM t... |
297 |
iommu_domain_free(domain); |
62c476c7c KVM: Device Assig... |
298 299 |
return 0; } |