1 /*
2  * Copyright (C) 2007 Advanced Micro Devices, Inc.
3  * Author: Leo Duran <leo.duran@amd.com>
4  * Author: Wei Wang <wei.wang2@amd.com> - adapted to xen
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <xen/errno.h>
21 #include <xen/acpi.h>
22 #include <xen/iommu.h>
23 #include <xen/pci.h>
24 #include <xen/pci_regs.h>
25 #include <asm/amd-iommu.h>
26 #include <asm/hvm/svm/amd-iommu-proto.h>
27 
get_iommu_msi_capabilities(u16 seg,u8 bus,u8 dev,u8 func,struct amd_iommu * iommu)28 static int __init get_iommu_msi_capabilities(
29     u16 seg, u8 bus, u8 dev, u8 func, struct amd_iommu *iommu)
30 {
31     int pos;
32 
33     pos = pci_find_cap_offset(seg, bus, dev, func, PCI_CAP_ID_MSI);
34 
35     if ( !pos )
36         return -ENODEV;
37 
38     AMD_IOMMU_DEBUG("Found MSI capability block at %#x\n", pos);
39 
40     iommu->msi.msi_attrib.type = PCI_CAP_ID_MSI;
41     iommu->msi.msi_attrib.pos = pos;
42     iommu->msi.msi_attrib.is_64 = 1;
43     return 0;
44 }
45 
get_iommu_capabilities(u16 seg,u8 bus,u8 dev,u8 func,u16 cap_ptr,struct amd_iommu * iommu)46 static int __init get_iommu_capabilities(
47     u16 seg, u8 bus, u8 dev, u8 func, u16 cap_ptr, struct amd_iommu *iommu)
48 {
49     u8 type;
50 
51     iommu->cap.header = pci_conf_read32(seg, bus, dev, func, cap_ptr);
52     type = get_field_from_reg_u32(iommu->cap.header, PCI_CAP_TYPE_MASK,
53                                   PCI_CAP_TYPE_SHIFT);
54 
55     if ( type != PCI_CAP_TYPE_IOMMU )
56         return -ENODEV;
57 
58     return 0;
59 }
60 
get_iommu_features(struct amd_iommu * iommu)61 void __init get_iommu_features(struct amd_iommu *iommu)
62 {
63     u32 low, high;
64     int i = 0 ;
65     static const char *__initdata feature_str[] = {
66         "- Prefetch Pages Command",
67         "- Peripheral Page Service Request",
68         "- X2APIC Supported",
69         "- NX bit Supported",
70         "- Guest Translation",
71         "- Reserved bit [5]",
72         "- Invalidate All Command",
73         "- Guest APIC supported",
74         "- Hardware Error Registers",
75         "- Performance Counters",
76         NULL
77     };
78 
79     ASSERT( iommu->mmio_base );
80 
81     if ( !iommu_has_cap(iommu, PCI_CAP_EFRSUP_SHIFT) )
82     {
83         iommu->features = 0;
84         return;
85     }
86 
87     low = readl(iommu->mmio_base + IOMMU_EXT_FEATURE_MMIO_OFFSET);
88     high = readl(iommu->mmio_base + IOMMU_EXT_FEATURE_MMIO_OFFSET + 4);
89 
90     iommu->features = ((u64)high << 32) | low;
91 
92     printk("AMD-Vi: IOMMU Extended Features:\n");
93 
94     while ( feature_str[i] )
95     {
96         if ( amd_iommu_has_feature(iommu, i) )
97             printk( " %s\n", feature_str[i]);
98         i++;
99     }
100 }
101 
amd_iommu_detect_one_acpi(const struct acpi_ivrs_hardware * ivhd_block)102 int __init amd_iommu_detect_one_acpi(
103     const struct acpi_ivrs_hardware *ivhd_block)
104 {
105     struct amd_iommu *iommu;
106     u8 bus, dev, func;
107     int rt = 0;
108 
109     if ( ivhd_block->header.length < sizeof(*ivhd_block) )
110     {
111         AMD_IOMMU_DEBUG("Invalid IVHD Block Length!\n");
112         return -ENODEV;
113     }
114 
115     if ( !ivhd_block->header.device_id ||
116         !ivhd_block->capability_offset || !ivhd_block->base_address)
117     {
118         AMD_IOMMU_DEBUG("Invalid IVHD Block!\n");
119         return -ENODEV;
120     }
121 
122     iommu = xzalloc(struct amd_iommu);
123     if ( !iommu )
124     {
125         AMD_IOMMU_DEBUG("Error allocating amd_iommu\n");
126         return -ENOMEM;
127     }
128 
129     spin_lock_init(&iommu->lock);
130     INIT_LIST_HEAD(&iommu->ats_devices);
131 
132     iommu->seg = ivhd_block->pci_segment_group;
133     iommu->bdf = ivhd_block->header.device_id;
134     iommu->cap_offset = ivhd_block->capability_offset;
135     iommu->mmio_base_phys = ivhd_block->base_address;
136 
137     /* override IOMMU HT flags */
138     iommu->ht_flags = ivhd_block->header.flags;
139 
140     bus = PCI_BUS(iommu->bdf);
141     dev = PCI_SLOT(iommu->bdf);
142     func = PCI_FUNC(iommu->bdf);
143 
144     rt = get_iommu_capabilities(iommu->seg, bus, dev, func,
145                                 iommu->cap_offset, iommu);
146     if ( rt )
147         goto out;
148 
149     rt = get_iommu_msi_capabilities(iommu->seg, bus, dev, func, iommu);
150     if ( rt )
151         goto out;
152 
153     rt = pci_ro_device(iommu->seg, bus, PCI_DEVFN(dev, func));
154     if ( rt )
155         printk(XENLOG_ERR
156                "Could not mark config space of %04x:%02x:%02x.%u read-only (%d)\n",
157                iommu->seg, bus, dev, func, rt);
158 
159     list_add_tail(&iommu->list, &amd_iommu_head);
160     rt = 0;
161 
162  out:
163     if ( rt )
164         xfree(iommu);
165 
166     return rt;
167 }
168