/root/src/xen/xen/arch/x86/x86_64/mmconfig_64.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * mmconfig.c - Low-level direct PCI config space access via MMCONFIG |
3 | | * |
4 | | * This is an 64bit optimized version that always keeps the full mmconfig |
5 | | * space mapped. This allows lockless config space operation. |
6 | | * |
7 | | * copied from Linux |
8 | | */ |
9 | | |
10 | | #include <xen/init.h> |
11 | | #include <xen/mm.h> |
12 | | #include <xen/acpi.h> |
13 | | #include <xen/xmalloc.h> |
14 | | #include <xen/pci.h> |
15 | | #include <xen/pci_regs.h> |
16 | | #include <xen/iommu.h> |
17 | | #include <xen/rangeset.h> |
18 | | |
19 | | #include "mmconfig.h" |
20 | | |
21 | | /* Static virtual mapping of the MMCONFIG aperture */ |
22 | | struct mmcfg_virt { |
23 | | struct acpi_mcfg_allocation *cfg; |
24 | | char __iomem *virt; |
25 | | }; |
26 | | static struct mmcfg_virt *pci_mmcfg_virt; |
27 | | static unsigned int mmcfg_pci_segment_shift; |
28 | | |
29 | | static char __iomem *get_virt(unsigned int seg, unsigned int *bus) |
30 | 2 | { |
31 | 2 | struct acpi_mcfg_allocation *cfg; |
32 | 2 | int cfg_num; |
33 | 2 | |
34 | 2 | for (cfg_num = 0; cfg_num < pci_mmcfg_config_num; cfg_num++) { |
35 | 2 | cfg = pci_mmcfg_virt[cfg_num].cfg; |
36 | 2 | if (cfg->pci_segment == seg && |
37 | 2 | (cfg->start_bus_number <= *bus) && |
38 | 2 | (cfg->end_bus_number >= *bus)) { |
39 | 2 | *bus -= cfg->start_bus_number; |
40 | 2 | return pci_mmcfg_virt[cfg_num].virt; |
41 | 2 | } |
42 | 2 | } |
43 | 2 | |
44 | 2 | /* Fall back to type 0 */ |
45 | 0 | return NULL; |
46 | 2 | } |
47 | | |
48 | | static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn) |
49 | 2 | { |
50 | 2 | char __iomem *addr; |
51 | 2 | |
52 | 2 | addr = get_virt(seg, &bus); |
53 | 2 | if (!addr) |
54 | 0 | return NULL; |
55 | 2 | return addr + ((bus << 20) | (devfn << 12)); |
56 | 2 | } |
57 | | |
58 | | int pci_mmcfg_read(unsigned int seg, unsigned int bus, |
59 | | unsigned int devfn, int reg, int len, u32 *value) |
60 | 2 | { |
61 | 2 | char __iomem *addr; |
62 | 2 | |
63 | 2 | /* Why do we have this when nobody checks it. How about a BUG()!? -AK */ |
64 | 2 | if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) { |
65 | 0 | err: *value = -1; |
66 | 0 | return -EINVAL; |
67 | 0 | } |
68 | 2 | |
69 | 2 | addr = pci_dev_base(seg, bus, devfn); |
70 | 2 | if (!addr) |
71 | 0 | goto err; |
72 | 2 | |
73 | 2 | switch (len) { |
74 | 0 | case 1: |
75 | 0 | *value = mmio_config_readb(addr + reg); |
76 | 0 | break; |
77 | 0 | case 2: |
78 | 0 | *value = mmio_config_readw(addr + reg); |
79 | 0 | break; |
80 | 2 | case 4: |
81 | 2 | *value = mmio_config_readl(addr + reg); |
82 | 2 | break; |
83 | 2 | } |
84 | 2 | |
85 | 2 | return 0; |
86 | 2 | } |
87 | | |
88 | | int pci_mmcfg_write(unsigned int seg, unsigned int bus, |
89 | | unsigned int devfn, int reg, int len, u32 value) |
90 | 0 | { |
91 | 0 | char __iomem *addr; |
92 | 0 |
|
93 | 0 | /* Why do we have this when nobody checks it. How about a BUG()!? -AK */ |
94 | 0 | if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) |
95 | 0 | return -EINVAL; |
96 | 0 |
|
97 | 0 | addr = pci_dev_base(seg, bus, devfn); |
98 | 0 | if (!addr) |
99 | 0 | return -EINVAL; |
100 | 0 |
|
101 | 0 | switch (len) { |
102 | 0 | case 1: |
103 | 0 | mmio_config_writeb(addr + reg, value); |
104 | 0 | break; |
105 | 0 | case 2: |
106 | 0 | mmio_config_writew(addr + reg, value); |
107 | 0 | break; |
108 | 0 | case 4: |
109 | 0 | mmio_config_writel(addr + reg, value); |
110 | 0 | break; |
111 | 0 | } |
112 | 0 |
|
113 | 0 | return 0; |
114 | 0 | } |
115 | | |
116 | | static void __iomem *mcfg_ioremap(const struct acpi_mcfg_allocation *cfg, |
117 | | unsigned long idx, unsigned int prot) |
118 | 1 | { |
119 | 1 | unsigned long virt, size; |
120 | 1 | |
121 | 1 | virt = PCI_MCFG_VIRT_START + (idx << mmcfg_pci_segment_shift) + |
122 | 1 | (cfg->start_bus_number << 20); |
123 | 1 | size = (cfg->end_bus_number - cfg->start_bus_number + 1) << 20; |
124 | 1 | if (virt + size < virt || virt + size > PCI_MCFG_VIRT_END) |
125 | 0 | return NULL; |
126 | 1 | |
127 | 1 | if (map_pages_to_xen(virt, |
128 | 1 | (cfg->address >> PAGE_SHIFT) + |
129 | 1 | (cfg->start_bus_number << (20 - PAGE_SHIFT)), |
130 | 1 | size >> PAGE_SHIFT, prot)) |
131 | 0 | return NULL; |
132 | 1 | |
133 | 1 | return (void __iomem *) virt; |
134 | 1 | } |
135 | | |
136 | | int pci_mmcfg_arch_enable(unsigned int idx) |
137 | 1 | { |
138 | 1 | const typeof(pci_mmcfg_config[0]) *cfg = pci_mmcfg_virt[idx].cfg; |
139 | 1 | unsigned long start_mfn, end_mfn; |
140 | 1 | |
141 | 1 | if (pci_mmcfg_virt[idx].virt) |
142 | 0 | return 0; |
143 | 1 | pci_mmcfg_virt[idx].virt = mcfg_ioremap(cfg, idx, PAGE_HYPERVISOR_UC); |
144 | 1 | if (!pci_mmcfg_virt[idx].virt) { |
145 | 0 | printk(KERN_ERR "PCI: Cannot map MCFG aperture for segment %04x\n", |
146 | 0 | cfg->pci_segment); |
147 | 0 | return -ENOMEM; |
148 | 0 | } |
149 | 1 | printk(KERN_INFO "PCI: Using MCFG for segment %04x bus %02x-%02x\n", |
150 | 1 | cfg->pci_segment, cfg->start_bus_number, cfg->end_bus_number); |
151 | 1 | |
152 | 1 | start_mfn = PFN_DOWN(cfg->address) + PCI_BDF(cfg->start_bus_number, 0, 0); |
153 | 1 | end_mfn = PFN_DOWN(cfg->address) + PCI_BDF(cfg->end_bus_number, ~0, ~0); |
154 | 1 | if ( rangeset_add_range(mmio_ro_ranges, start_mfn, end_mfn) ) |
155 | 0 | printk(XENLOG_ERR |
156 | 0 | "%04x:%02x-%02x: could not mark MCFG (mfns %lx-%lx) read-only\n", |
157 | 0 | cfg->pci_segment, cfg->start_bus_number, cfg->end_bus_number, |
158 | 0 | start_mfn, end_mfn); |
159 | 1 | |
160 | 1 | return 0; |
161 | 1 | } |
162 | | |
163 | | void pci_mmcfg_arch_disable(unsigned int idx) |
164 | 0 | { |
165 | 0 | const typeof(pci_mmcfg_config[0]) *cfg = pci_mmcfg_virt[idx].cfg; |
166 | 0 |
|
167 | 0 | pci_mmcfg_virt[idx].virt = NULL; |
168 | 0 | /* |
169 | 0 | * Don't use destroy_xen_mappings() here, or make sure that at least |
170 | 0 | * the necessary L4 entries get populated (so that they get properly |
171 | 0 | * propagated to guest domains' page tables). |
172 | 0 | */ |
173 | 0 | mcfg_ioremap(cfg, idx, 0); |
174 | 0 | printk(KERN_WARNING "PCI: Not using MCFG for segment %04x bus %02x-%02x\n", |
175 | 0 | cfg->pci_segment, cfg->start_bus_number, cfg->end_bus_number); |
176 | 0 | } |
177 | | |
178 | | bool_t pci_mmcfg_decode(unsigned long mfn, unsigned int *seg, |
179 | | unsigned int *bdf) |
180 | 0 | { |
181 | 0 | unsigned int idx; |
182 | 0 |
|
183 | 0 | for (idx = 0; idx < pci_mmcfg_config_num; ++idx) { |
184 | 0 | const struct acpi_mcfg_allocation *cfg = pci_mmcfg_virt[idx].cfg; |
185 | 0 |
|
186 | 0 | if (pci_mmcfg_virt[idx].virt && |
187 | 0 | mfn >= PFN_DOWN(cfg->address) + PCI_BDF(cfg->start_bus_number, |
188 | 0 | 0, 0) && |
189 | 0 | mfn <= PFN_DOWN(cfg->address) + PCI_BDF(cfg->end_bus_number, |
190 | 0 | ~0, ~0)) { |
191 | 0 | *seg = cfg->pci_segment; |
192 | 0 | *bdf = mfn - PFN_DOWN(cfg->address); |
193 | 0 | return 1; |
194 | 0 | } |
195 | 0 | } |
196 | 0 |
|
197 | 0 | return 0; |
198 | 0 | } |
199 | | |
200 | | bool_t pci_ro_mmcfg_decode(unsigned long mfn, unsigned int *seg, |
201 | | unsigned int *bdf) |
202 | 0 | { |
203 | 0 | const unsigned long *ro_map; |
204 | 0 |
|
205 | 0 | return pci_mmcfg_decode(mfn, seg, bdf) && |
206 | 0 | ((ro_map = pci_get_ro_map(*seg)) == NULL || |
207 | 0 | !test_bit(*bdf, ro_map)); |
208 | 0 | } |
209 | | |
210 | | int __init pci_mmcfg_arch_init(void) |
211 | 1 | { |
212 | 1 | int i; |
213 | 1 | |
214 | 1 | if (pci_mmcfg_virt) |
215 | 0 | return 0; |
216 | 1 | |
217 | 1 | pci_mmcfg_virt = xzalloc_array(struct mmcfg_virt, pci_mmcfg_config_num); |
218 | 1 | if (pci_mmcfg_virt == NULL) { |
219 | 0 | printk(KERN_ERR "PCI: Can not allocate memory for mmconfig structures\n"); |
220 | 0 | pci_mmcfg_config_num = 0; |
221 | 0 | return 0; |
222 | 0 | } |
223 | 1 | |
224 | 2 | for (i = 0; i < pci_mmcfg_config_num; ++i) { |
225 | 1 | pci_mmcfg_virt[i].cfg = &pci_mmcfg_config[i]; |
226 | 9 | while (pci_mmcfg_config[i].end_bus_number >> mmcfg_pci_segment_shift) |
227 | 8 | ++mmcfg_pci_segment_shift; |
228 | 1 | } |
229 | 1 | mmcfg_pci_segment_shift += 20; |
230 | 1 | return 1; |
231 | 1 | } |