1 /*
2  * Copyright (c) 2006, Intel Corporation.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms and conditions of the GNU General Public License,
6  * version 2, as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11  * more details.
12  *
13  * You should have received a copy of the GNU General Public License along with
14  * this program; If not, see <http://www.gnu.org/licenses/>.
15  *
16  * Author: Allen Kay <allen.m.kay@intel.com>
17  */
18 
19 #include <xen/sched.h>
20 #include <xen/iommu.h>
21 #include <xen/time.h>
22 #include <xen/pci.h>
23 #include <xen/pci_regs.h>
24 #include <asm/msi.h>
25 #include "../iommu.h"
26 #include "../dmar.h"
27 #include "../vtd.h"
28 #include "../extern.h"
29 #include "../../ats.h"
30 
31 static LIST_HEAD(ats_dev_drhd_units);
32 
find_ats_dev_drhd(struct iommu * iommu)33 struct acpi_drhd_unit * find_ats_dev_drhd(struct iommu *iommu)
34 {
35     struct acpi_drhd_unit *drhd;
36     list_for_each_entry ( drhd, &ats_dev_drhd_units, list )
37     {
38         if ( drhd->iommu == iommu )
39             return drhd;
40     }
41     return NULL;
42 }
43 
ats_device(const struct pci_dev * pdev,const struct acpi_drhd_unit * drhd)44 int ats_device(const struct pci_dev *pdev, const struct acpi_drhd_unit *drhd)
45 {
46     struct acpi_drhd_unit *ats_drhd;
47     int pos;
48 
49     if ( !ats_enabled || !iommu_qinval )
50         return 0;
51 
52     if ( !ecap_queued_inval(drhd->iommu->ecap) ||
53          !ecap_dev_iotlb(drhd->iommu->ecap) )
54         return 0;
55 
56     if ( !acpi_find_matched_atsr_unit(pdev) )
57         return 0;
58 
59     ats_drhd = find_ats_dev_drhd(drhd->iommu);
60     pos = pci_find_ext_capability(pdev->seg, pdev->bus, pdev->devfn,
61                                   PCI_EXT_CAP_ID_ATS);
62 
63     if ( pos && (ats_drhd == NULL) )
64     {
65         ats_drhd = xmalloc(struct acpi_drhd_unit);
66         if ( !ats_drhd )
67             return -ENOMEM;
68         *ats_drhd = *drhd;
69         list_add_tail(&ats_drhd->list, &ats_dev_drhd_units);
70     }
71     return pos;
72 }
73 
device_in_domain(const struct iommu * iommu,const struct pci_dev * pdev,u16 did)74 static int device_in_domain(const struct iommu *iommu,
75                             const struct pci_dev *pdev, u16 did)
76 {
77     struct root_entry *root_entry = NULL;
78     struct context_entry *ctxt_entry = NULL;
79     int tt, found = 0;
80 
81     root_entry = (struct root_entry *) map_vtd_domain_page(iommu->root_maddr);
82     if ( !root_entry || !root_present(root_entry[pdev->bus]) )
83         goto out;
84 
85     ctxt_entry = (struct context_entry *)
86                  map_vtd_domain_page(root_entry[pdev->bus].val);
87 
88     if ( ctxt_entry == NULL )
89         goto out;
90 
91     if ( context_domain_id(ctxt_entry[pdev->devfn]) != did )
92         goto out;
93 
94     tt = context_translation_type(ctxt_entry[pdev->devfn]);
95     if ( tt != CONTEXT_TT_DEV_IOTLB )
96         goto out;
97 
98     found = 1;
99 out:
100     if ( root_entry )
101         unmap_vtd_domain_page(root_entry);
102 
103     if ( ctxt_entry )
104         unmap_vtd_domain_page(ctxt_entry);
105 
106     return found;
107 }
108 
dev_invalidate_iotlb(struct iommu * iommu,u16 did,u64 addr,unsigned int size_order,u64 type)109 int dev_invalidate_iotlb(struct iommu *iommu, u16 did,
110     u64 addr, unsigned int size_order, u64 type)
111 {
112     struct pci_dev *pdev, *temp;
113     int ret = 0;
114 
115     if ( !ecap_dev_iotlb(iommu->ecap) )
116         return ret;
117 
118     list_for_each_entry_safe( pdev, temp, &iommu->ats_devices, ats.list )
119     {
120         bool_t sbit;
121         int rc = 0;
122 
123         switch ( type )
124         {
125         case DMA_TLB_DSI_FLUSH:
126             if ( !device_in_domain(iommu, pdev, did) )
127                 break;
128             /* fall through if DSI condition met */
129         case DMA_TLB_GLOBAL_FLUSH:
130             /* invalidate all translations: sbit=1,bit_63=0,bit[62:12]=1 */
131             sbit = 1;
132             addr = (~0UL << PAGE_SHIFT_4K) & 0x7FFFFFFFFFFFFFFF;
133             rc = qinval_device_iotlb_sync(iommu, pdev, did, sbit, addr);
134             break;
135         case DMA_TLB_PSI_FLUSH:
136             if ( !device_in_domain(iommu, pdev, did) )
137                 break;
138 
139             /* if size <= 4K, set sbit = 0, else set sbit = 1 */
140             sbit = size_order ? 1 : 0;
141 
142             /* clear lower bits */
143             addr &= ~0UL << PAGE_SHIFT_4K;
144 
145             /* if sbit == 1, zero out size_order bit and set lower bits to 1 */
146             if ( sbit )
147             {
148                 addr &= ~((u64)PAGE_SIZE_4K << (size_order - 1));
149                 addr |= (((u64)1 << (size_order - 1)) - 1) << PAGE_SHIFT_4K;
150             }
151 
152             rc = qinval_device_iotlb_sync(iommu, pdev, did, sbit, addr);
153             break;
154         default:
155             dprintk(XENLOG_WARNING VTDPREFIX, "invalid vt-d flush type\n");
156             return -EOPNOTSUPP;
157         }
158 
159         if ( !ret )
160             ret = rc;
161     }
162 
163     return ret;
164 }
165