Coverage Report

Created: 2017-10-25 09:10

/root/src/xen/xen/drivers/passthrough/vtd/x86/ats.c
Line
Count
Source (jump to first uncovered line)
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
33
struct acpi_drhd_unit * find_ats_dev_drhd(struct iommu *iommu)
34
4.56M
{
35
4.56M
    struct acpi_drhd_unit *drhd;
36
4.56M
    list_for_each_entry ( drhd, &ats_dev_drhd_units, list )
37
0
    {
38
0
        if ( drhd->iommu == iommu )
39
0
            return drhd;
40
0
    }
41
4.56M
    return NULL;
42
4.56M
}
43
44
int ats_device(const struct pci_dev *pdev, const struct acpi_drhd_unit *drhd)
45
25
{
46
25
    struct acpi_drhd_unit *ats_drhd;
47
25
    int pos;
48
25
49
25
    if ( !ats_enabled || !iommu_qinval )
50
25
        return 0;
51
25
52
0
    if ( !ecap_queued_inval(drhd->iommu->ecap) ||
53
0
         !ecap_dev_iotlb(drhd->iommu->ecap) )
54
0
        return 0;
55
0
56
0
    if ( !acpi_find_matched_atsr_unit(pdev) )
57
0
        return 0;
58
0
59
0
    ats_drhd = find_ats_dev_drhd(drhd->iommu);
60
0
    pos = pci_find_ext_capability(pdev->seg, pdev->bus, pdev->devfn,
61
0
                                  PCI_EXT_CAP_ID_ATS);
62
0
63
0
    if ( pos && (ats_drhd == NULL) )
64
0
    {
65
0
        ats_drhd = xmalloc(struct acpi_drhd_unit);
66
0
        if ( !ats_drhd )
67
0
            return -ENOMEM;
68
0
        *ats_drhd = *drhd;
69
0
        list_add_tail(&ats_drhd->list, &ats_dev_drhd_units);
70
0
    }
71
0
    return pos;
72
0
}
73
74
static int device_in_domain(const struct iommu *iommu,
75
                            const struct pci_dev *pdev, u16 did)
76
0
{
77
0
    struct root_entry *root_entry = NULL;
78
0
    struct context_entry *ctxt_entry = NULL;
79
0
    int tt, found = 0;
80
0
81
0
    root_entry = (struct root_entry *) map_vtd_domain_page(iommu->root_maddr);
82
0
    if ( !root_entry || !root_present(root_entry[pdev->bus]) )
83
0
        goto out;
84
0
85
0
    ctxt_entry = (struct context_entry *)
86
0
                 map_vtd_domain_page(root_entry[pdev->bus].val);
87
0
88
0
    if ( ctxt_entry == NULL )
89
0
        goto out;
90
0
91
0
    if ( context_domain_id(ctxt_entry[pdev->devfn]) != did )
92
0
        goto out;
93
0
94
0
    tt = context_translation_type(ctxt_entry[pdev->devfn]);
95
0
    if ( tt != CONTEXT_TT_DEV_IOTLB )
96
0
        goto out;
97
0
98
0
    found = 1;
99
0
out:
100
0
    if ( root_entry )
101
0
        unmap_vtd_domain_page(root_entry);
102
0
103
0
    if ( ctxt_entry )
104
0
        unmap_vtd_domain_page(ctxt_entry);
105
0
106
0
    return found;
107
0
}
108
109
int dev_invalidate_iotlb(struct iommu *iommu, u16 did,
110
    u64 addr, unsigned int size_order, u64 type)
111
0
{
112
0
    struct pci_dev *pdev, *temp;
113
0
    int ret = 0;
114
0
115
0
    if ( !ecap_dev_iotlb(iommu->ecap) )
116
0
        return ret;
117
0
118
0
    list_for_each_entry_safe( pdev, temp, &iommu->ats_devices, ats.list )
119
0
    {
120
0
        bool_t sbit;
121
0
        int rc = 0;
122
0
123
0
        switch ( type )
124
0
        {
125
0
        case DMA_TLB_DSI_FLUSH:
126
0
            if ( !device_in_domain(iommu, pdev, did) )
127
0
                break;
128
0
            /* fall through if DSI condition met */
129
0
        case DMA_TLB_GLOBAL_FLUSH:
130
0
            /* invalidate all translations: sbit=1,bit_63=0,bit[62:12]=1 */
131
0
            sbit = 1;
132
0
            addr = (~0UL << PAGE_SHIFT_4K) & 0x7FFFFFFFFFFFFFFF;
133
0
            rc = qinval_device_iotlb_sync(iommu, pdev, did, sbit, addr);
134
0
            break;
135
0
        case DMA_TLB_PSI_FLUSH:
136
0
            if ( !device_in_domain(iommu, pdev, did) )
137
0
                break;
138
0
139
0
            /* if size <= 4K, set sbit = 0, else set sbit = 1 */
140
0
            sbit = size_order ? 1 : 0;
141
0
142
0
            /* clear lower bits */
143
0
            addr &= ~0UL << PAGE_SHIFT_4K;
144
0
145
0
            /* if sbit == 1, zero out size_order bit and set lower bits to 1 */
146
0
            if ( sbit )
147
0
            {
148
0
                addr &= ~((u64)PAGE_SIZE_4K << (size_order - 1));
149
0
                addr |= (((u64)1 << (size_order - 1)) - 1) << PAGE_SHIFT_4K;
150
0
            }
151
0
152
0
            rc = qinval_device_iotlb_sync(iommu, pdev, did, sbit, addr);
153
0
            break;
154
0
        default:
155
0
            dprintk(XENLOG_WARNING VTDPREFIX, "invalid vt-d flush type\n");
156
0
            return -EOPNOTSUPP;
157
0
        }
158
0
159
0
        if ( !ret )
160
0
            ret = rc;
161
0
    }
162
0
163
0
    return ret;
164
0
}