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