/root/src/xen/xen/drivers/vpci/header.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Generic functionality for handling accesses to the PCI header from the |
3 | | * configuration space. |
4 | | * |
5 | | * Copyright (C) 2017 Citrix Systems R&D |
6 | | * |
7 | | * This program is free software; you can redistribute it and/or |
8 | | * modify it under the terms and conditions of the GNU General Public |
9 | | * License, version 2, as published by the Free Software Foundation. |
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 GNU |
14 | | * General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU General Public |
17 | | * License along with this program; If not, see <http://www.gnu.org/licenses/>. |
18 | | */ |
19 | | |
20 | | #include <xen/sched.h> |
21 | | #include <xen/vpci.h> |
22 | | #include <xen/p2m-common.h> |
23 | | #include <xen/softirq.h> |
24 | | |
25 | | #include <asm/event.h> |
26 | | |
27 | | #define MAPPABLE_BAR(x) \ |
28 | 39.2k | ((x)->type == VPCI_BAR_MEM32 || (x)->type == VPCI_BAR_MEM64_LO || \ |
29 | 33.8k | (x)->type == VPCI_BAR_ROM) |
30 | | |
31 | | struct map_data { |
32 | | struct domain *d; |
33 | | bool map; |
34 | | }; |
35 | | |
36 | | static int map_range(unsigned long s, unsigned long e, void *data, |
37 | | unsigned long *c) |
38 | 709 | { |
39 | 709 | const struct map_data *map = data; |
40 | 709 | int rc; |
41 | 709 | |
42 | 709 | for ( ; ; ) |
43 | 8.13k | { |
44 | 8.13k | unsigned long size = e - s + 1; |
45 | 8.13k | |
46 | 8.13k | /* |
47 | 8.13k | * ARM TODOs: |
48 | 8.13k | * - On ARM whether the memory is prefetchable or not should be passed |
49 | 8.13k | * to map_mmio_regions in order to decide which memory attributes |
50 | 8.13k | * should be used. |
51 | 8.13k | * |
52 | 8.13k | * - {un}map_mmio_regions doesn't support preemption, hence the bodge |
53 | 8.13k | * below in order to limit the amount of mappings to 64 pages for |
54 | 8.13k | * each function call. |
55 | 8.13k | */ |
56 | 8.13k | |
57 | 8.13k | #ifdef CONFIG_ARM |
58 | | size = min(64ul, size); |
59 | | #endif |
60 | 8.13k | |
61 | 4.62k | rc = (map->map ? map_mmio_regions : unmap_mmio_regions) |
62 | 8.13k | (map->d, _gfn(s), size, _mfn(s)); |
63 | 8.13k | if ( rc == 0 ) |
64 | 318 | { |
65 | 318 | *c += size; |
66 | 318 | #ifdef CONFIG_ARM |
67 | | rc = -ERESTART; |
68 | | #endif |
69 | 318 | break; |
70 | 318 | } |
71 | 7.82k | if ( rc < 0 ) |
72 | 0 | { |
73 | 0 | printk(XENLOG_G_WARNING |
74 | 0 | "Failed to identity %smap [%" PRI_gfn ", %" PRI_gfn ") for d%d: %d\n", |
75 | 0 | map ? "" : "un", s, e, map->d->domain_id, rc); |
76 | 0 | break; |
77 | 0 | } |
78 | 7.82k | ASSERT(rc < size); |
79 | 7.82k | *c += rc; |
80 | 7.82k | s += rc; |
81 | 7.82k | if ( general_preempt_check() ) |
82 | 1.47k | { |
83 | 1.47k | if ( !is_idle_vcpu(current) ) |
84 | 391 | return -ERESTART; |
85 | 1.47k | |
86 | 1.08k | process_pending_softirqs(); |
87 | 1.08k | } |
88 | 7.82k | } |
89 | 709 | |
90 | 318 | return rc; |
91 | 709 | } |
92 | | |
93 | | static void modify_decoding(const struct pci_dev *pdev, bool map, bool rom) |
94 | 225 | { |
95 | 225 | struct vpci_header *header = &pdev->vpci->header; |
96 | 225 | uint8_t slot = PCI_SLOT(pdev->devfn), func = PCI_FUNC(pdev->devfn); |
97 | 225 | unsigned int i; |
98 | 225 | |
99 | 1.80k | for ( i = 0; i < ARRAY_SIZE(header->bars); i++ ) |
100 | 1.57k | { |
101 | 1.57k | if ( rom && header->bars[i].type == VPCI_BAR_ROM ) |
102 | 0 | { |
103 | 0 | unsigned int rom_pos = (i == 6) ? PCI_ROM_ADDRESS |
104 | 0 | : PCI_ROM_ADDRESS1; |
105 | 0 | uint32_t val = pci_conf_read32(pdev->seg, pdev->bus, slot, func, |
106 | 0 | rom_pos); |
107 | 0 |
|
108 | 0 | header->bars[i].enabled = header->bars[i].rom_enabled = map; |
109 | 0 |
|
110 | 0 | val &= ~PCI_ROM_ADDRESS_ENABLE; |
111 | 0 | val |= map ? PCI_ROM_ADDRESS_ENABLE : 0; |
112 | 0 | pci_conf_write32(pdev->seg, pdev->bus, slot, func, rom_pos, val); |
113 | 0 | break; |
114 | 0 | } |
115 | 1.57k | if ( !rom && (header->bars[i].type != VPCI_BAR_ROM || |
116 | 7 | header->bars[i].rom_enabled) ) |
117 | 1.56k | header->bars[i].enabled = map; |
118 | 1.57k | } |
119 | 225 | |
120 | 225 | if ( !rom ) |
121 | 225 | { |
122 | 225 | uint16_t cmd = pci_conf_read16(pdev->seg, pdev->bus, slot, |
123 | 225 | func, PCI_COMMAND); |
124 | 225 | |
125 | 225 | cmd &= ~PCI_COMMAND_MEMORY; |
126 | 123 | cmd |= map ? PCI_COMMAND_MEMORY : 0; |
127 | 225 | pci_conf_write16(pdev->seg, pdev->bus, slot, func, PCI_COMMAND, |
128 | 225 | cmd); |
129 | 225 | } |
130 | 225 | } |
131 | | |
132 | | bool vpci_process_pending(struct vcpu *v) |
133 | 9.70M | { |
134 | 9.70M | while ( v->vpci.mem ) |
135 | 589 | { |
136 | 589 | struct map_data data = { |
137 | 589 | .d = v->domain, |
138 | 589 | .map = v->vpci.map, |
139 | 589 | }; |
140 | 589 | |
141 | 589 | switch ( rangeset_consume_ranges(v->vpci.mem, map_range, &data) ) |
142 | 589 | { |
143 | 391 | case -ERESTART: |
144 | 391 | return true; |
145 | 198 | case 0: |
146 | 198 | if ( v->vpci.map ) |
147 | 96 | { |
148 | 96 | spin_lock(&v->vpci.pdev->vpci->lock); |
149 | 96 | modify_decoding(v->vpci.pdev, v->vpci.map, v->vpci.rom); |
150 | 96 | spin_unlock(&v->vpci.pdev->vpci->lock); |
151 | 96 | } |
152 | 198 | /* fallthrough. */ |
153 | 198 | case -ENOMEM: |
154 | 198 | /* |
155 | 198 | * Other errors are ignored, hopping that at least some regions |
156 | 198 | * will be mapped and that would be enough for the device to |
157 | 198 | * function. Note that in the unmap case the memory decoding or |
158 | 198 | * ROM enable bit have already been toggled off before attempting |
159 | 198 | * to perform the p2m unmap. |
160 | 198 | */ |
161 | 198 | rangeset_destroy(v->vpci.mem); |
162 | 198 | v->vpci.mem = NULL; |
163 | 198 | break; |
164 | 589 | } |
165 | 589 | } |
166 | 9.70M | |
167 | 9.70M | return false; |
168 | 9.70M | } |
169 | | |
170 | | static void maybe_defer_map(struct domain *d, const struct pci_dev *pdev, |
171 | | struct rangeset *mem, bool map, bool rom) |
172 | 225 | { |
173 | 225 | struct vcpu *curr = current; |
174 | 225 | |
175 | 225 | if ( is_idle_vcpu(curr) ) |
176 | 27 | { |
177 | 27 | struct map_data data = { .d = d, .map = true }; |
178 | 27 | |
179 | 27 | /* |
180 | 27 | * Only used for domain construction in order to map the BARs |
181 | 27 | * of devices with memory decoding enabled. |
182 | 27 | */ |
183 | 27 | ASSERT(map && !rom); |
184 | 27 | rangeset_consume_ranges(mem, map_range, &data); |
185 | 27 | modify_decoding(pdev, true, false); |
186 | 27 | rangeset_destroy(mem); |
187 | 27 | } |
188 | 225 | else |
189 | 198 | { |
190 | 198 | /* |
191 | 198 | * NB: when deferring the {un}map the state of the device should not be |
192 | 198 | * trusted. For example the enable bit is toggled after the device is |
193 | 198 | * mapped. This can lead to parallel mapping operations being started |
194 | 198 | * for the same device if the domain is not well-behaved. |
195 | 198 | * |
196 | 198 | * In any case, the worse that can happen are errors from the {un}map |
197 | 198 | * operations, which will lead to the devices not working properly. |
198 | 198 | */ |
199 | 198 | ASSERT(curr->domain == d); |
200 | 198 | curr->vpci.pdev = pdev; |
201 | 198 | curr->vpci.mem = mem; |
202 | 198 | curr->vpci.map = map; |
203 | 198 | curr->vpci.rom = rom; |
204 | 198 | } |
205 | 225 | } |
206 | | |
207 | | static void modify_bars(const struct pci_dev *pdev, bool map, bool rom) |
208 | 225 | { |
209 | 225 | struct vpci_header *header = &pdev->vpci->header; |
210 | 225 | struct rangeset *mem = rangeset_new(NULL, NULL, 0); |
211 | 225 | const struct pci_dev *tmp; |
212 | 225 | const struct vpci_msix *msix = pdev->vpci->msix; |
213 | 225 | unsigned int i; |
214 | 225 | int rc; |
215 | 225 | |
216 | 225 | if ( !map ) |
217 | 102 | modify_decoding(pdev, false, rom); |
218 | 225 | |
219 | 225 | if ( !mem ) |
220 | 0 | return; |
221 | 225 | |
222 | 225 | /* |
223 | 225 | * Create a rangeset that represents the current device BARs memory region |
224 | 225 | * and compare it against all the currently active BAR memory regions. If |
225 | 225 | * an overlap is found, subtract it from the region to be |
226 | 225 | * mapped/unmapped. |
227 | 225 | * |
228 | 225 | * NB: the rangeset uses inclusive frame numbers. |
229 | 225 | */ |
230 | 225 | |
231 | 225 | /* |
232 | 225 | * First fill the rangeset with all the BARs of this device or with the ROM |
233 | 225 | * BAR only, depending on whether the guest is toggling the memory decode |
234 | 225 | * bit of the command register, or the enable bit of the ROM BAR register. |
235 | 225 | */ |
236 | 1.80k | for ( i = 0; i < ARRAY_SIZE(header->bars); i++ ) |
237 | 1.57k | { |
238 | 1.57k | const struct vpci_bar *bar = &header->bars[i]; |
239 | 1.57k | |
240 | 1.57k | if ( !MAPPABLE_BAR(bar) || |
241 | 1.29k | rom ? bar->type != VPCI_BAR_ROM |
242 | 281 | : (bar->type == VPCI_BAR_ROM && !bar->rom_enabled) ) |
243 | 1.30k | continue; |
244 | 1.57k | |
245 | 274 | rc = rangeset_add_range(mem, PFN_DOWN(bar->addr), |
246 | 274 | PFN_DOWN(bar->addr + bar->size - 1)); |
247 | 274 | if ( rc ) |
248 | 0 | { |
249 | 0 | printk(XENLOG_G_WARNING |
250 | 0 | "Failed to add [%" PRI_gfn ", %" PRI_gfn "): %d\n", |
251 | 0 | PFN_DOWN(bar->addr), PFN_DOWN(bar->addr + bar->size - 1), |
252 | 0 | rc); |
253 | 0 | rangeset_destroy(mem); |
254 | 0 | return; |
255 | 0 | } |
256 | 274 | } |
257 | 225 | |
258 | 225 | /* Remove any MSIX regions if present. */ |
259 | 335 | for ( i = 0; msix && i < ARRAY_SIZE(msix->tables); i++ ) |
260 | 110 | { |
261 | 110 | paddr_t start = VMSIX_TABLE_ADDR(pdev->vpci, i); |
262 | 110 | |
263 | 110 | rc = rangeset_remove_range(mem, PFN_DOWN(start), PFN_DOWN(start + |
264 | 110 | VMSIX_TABLE_SIZE(pdev->vpci, i) - 1)); |
265 | 110 | if ( rc ) |
266 | 0 | { |
267 | 0 | rangeset_destroy(mem); |
268 | 0 | return; |
269 | 0 | } |
270 | 110 | } |
271 | 225 | |
272 | 225 | /* |
273 | 225 | * Check for overlaps with other BARs. Note that only BARs that are |
274 | 225 | * currently mapped (enabled) are checked for overlaps. |
275 | 225 | */ |
276 | 225 | list_for_each_entry(tmp, &pdev->domain->arch.pdev_list, domain_list) |
277 | 111k | for ( i = 0; i < ARRAY_SIZE(tmp->vpci->header.bars); i++ ) |
278 | 97.5k | { |
279 | 97.5k | const struct vpci_bar *bar = &tmp->vpci->header.bars[i]; |
280 | 97.5k | unsigned long start = PFN_DOWN(bar->addr); |
281 | 97.5k | unsigned long end = PFN_DOWN(bar->addr + bar->size - 1); |
282 | 97.5k | |
283 | 97.5k | if ( !bar->enabled || !MAPPABLE_BAR(bar) || |
284 | 5.12k | !rangeset_overlaps_range(mem, start, end) ) |
285 | 97.5k | continue; |
286 | 97.5k | |
287 | 0 | rc = rangeset_remove_range(mem, start, end); |
288 | 0 | if ( rc ) |
289 | 0 | { |
290 | 0 | printk(XENLOG_G_WARNING |
291 | 0 | "Failed to remove [%" PRI_gfn ", %" PRI_gfn "): %d\n", |
292 | 0 | start, end, rc); |
293 | 0 | rangeset_destroy(mem); |
294 | 0 | return; |
295 | 0 | } |
296 | 0 | } |
297 | 225 | |
298 | 225 | maybe_defer_map(pdev->domain, pdev, mem, map, rom); |
299 | 225 | } |
300 | | |
301 | | static void cmd_write(const struct pci_dev *pdev, unsigned int reg, |
302 | | uint32_t cmd, void *data) |
303 | 903 | { |
304 | 903 | uint8_t seg = pdev->seg, bus = pdev->bus; |
305 | 903 | uint8_t slot = PCI_SLOT(pdev->devfn), func = PCI_FUNC(pdev->devfn); |
306 | 903 | uint16_t current_cmd = pci_conf_read16(seg, bus, slot, func, reg); |
307 | 903 | |
308 | 903 | /* |
309 | 903 | * Let Dom0 play with all the bits directly except for the memory |
310 | 903 | * decoding one. |
311 | 903 | */ |
312 | 903 | if ( (cmd ^ current_cmd) & PCI_COMMAND_MEMORY ) |
313 | 198 | modify_bars(pdev, cmd & PCI_COMMAND_MEMORY, false); |
314 | 903 | else |
315 | 705 | pci_conf_write16(seg, bus, slot, func, reg, cmd); |
316 | 903 | } |
317 | | |
318 | | static void bar_write(const struct pci_dev *pdev, unsigned int reg, |
319 | | uint32_t val, void *data) |
320 | 99 | { |
321 | 99 | struct vpci_bar *bar = data; |
322 | 99 | uint8_t seg = pdev->seg, bus = pdev->bus; |
323 | 99 | uint8_t slot = PCI_SLOT(pdev->devfn), func = PCI_FUNC(pdev->devfn); |
324 | 99 | bool hi = false; |
325 | 99 | |
326 | 99 | if ( pci_conf_read16(seg, bus, slot, func, PCI_COMMAND) & |
327 | 99 | PCI_COMMAND_MEMORY ) |
328 | 33 | { |
329 | 33 | gprintk(XENLOG_WARNING, |
330 | 33 | "%04x:%02x:%02x.%u: ignored BAR %lu write with memory decoding enabled\n", |
331 | 33 | seg, bus, slot, func, (pdev->vpci->header.bars - bar) / 4); |
332 | 33 | return; |
333 | 33 | } |
334 | 99 | |
335 | 66 | if ( bar->type == VPCI_BAR_MEM64_HI ) |
336 | 14 | { |
337 | 14 | ASSERT(reg > PCI_BASE_ADDRESS_0); |
338 | 14 | bar--; |
339 | 14 | hi = true; |
340 | 14 | } |
341 | 66 | else |
342 | 52 | val &= PCI_BASE_ADDRESS_MEM_MASK; |
343 | 66 | |
344 | 66 | /* |
345 | 66 | * Update the cached address, so that when memory decoding is enabled |
346 | 66 | * Xen can map the BAR into the guest p2m. |
347 | 66 | */ |
348 | 52 | bar->addr &= ~(0xffffffffull << (hi ? 32 : 0)); |
349 | 52 | bar->addr |= (uint64_t)val << (hi ? 32 : 0); |
350 | 66 | |
351 | 66 | /* Make sure Xen writes back the same value for the BAR RO bits. */ |
352 | 66 | if ( !hi ) |
353 | 52 | { |
354 | 38 | val |= bar->type == VPCI_BAR_MEM32 ? PCI_BASE_ADDRESS_MEM_TYPE_32 |
355 | 14 | : PCI_BASE_ADDRESS_MEM_TYPE_64; |
356 | 50 | val |= bar->prefetchable ? PCI_BASE_ADDRESS_MEM_PREFETCH : 0; |
357 | 52 | } |
358 | 66 | |
359 | 66 | pci_conf_write32(pdev->seg, pdev->bus, PCI_SLOT(pdev->devfn), |
360 | 66 | PCI_FUNC(pdev->devfn), reg, val); |
361 | 66 | } |
362 | | |
363 | | static void rom_write(const struct pci_dev *pdev, unsigned int reg, |
364 | | uint32_t val, void *data) |
365 | 0 | { |
366 | 0 | struct vpci_bar *rom = data; |
367 | 0 | uint8_t seg = pdev->seg, bus = pdev->bus; |
368 | 0 | uint8_t slot = PCI_SLOT(pdev->devfn), func = PCI_FUNC(pdev->devfn); |
369 | 0 | uint16_t cmd = pci_conf_read16(seg, bus, slot, func, PCI_COMMAND); |
370 | 0 | bool new_enabled = val & PCI_ROM_ADDRESS_ENABLE; |
371 | 0 |
|
372 | 0 | if ( (cmd & PCI_COMMAND_MEMORY) && rom->rom_enabled && new_enabled ) |
373 | 0 | { |
374 | 0 | gprintk(XENLOG_WARNING, |
375 | 0 | "%04x:%02x:%02x.%u: ignored ROM BAR write with memory decoding enabled\n", |
376 | 0 | seg, bus, slot, func); |
377 | 0 | return; |
378 | 0 | } |
379 | 0 |
|
380 | 0 | if ( !rom->rom_enabled ) |
381 | 0 | rom->addr = val & PCI_ROM_ADDRESS_MASK; |
382 | 0 |
|
383 | 0 | /* Check if ROM BAR should be mapped/unmapped. */ |
384 | 0 | if ( (cmd & PCI_COMMAND_MEMORY) && rom->rom_enabled != new_enabled ) |
385 | 0 | modify_bars(pdev, new_enabled, true); |
386 | 0 | else |
387 | 0 | { |
388 | 0 | rom->rom_enabled = new_enabled; |
389 | 0 | pci_conf_write32(pdev->seg, pdev->bus, slot, func, reg, val); |
390 | 0 | } |
391 | 0 |
|
392 | 0 | if ( !new_enabled ) |
393 | 0 | rom->addr = val & PCI_ROM_ADDRESS_MASK; |
394 | 0 | } |
395 | | |
396 | | static int init_bars(struct pci_dev *pdev) |
397 | 68 | { |
398 | 68 | uint8_t slot = PCI_SLOT(pdev->devfn), func = PCI_FUNC(pdev->devfn); |
399 | 68 | uint16_t cmd; |
400 | 68 | uint64_t addr, size; |
401 | 68 | unsigned int i, num_bars, rom_reg; |
402 | 68 | struct vpci_header *header = &pdev->vpci->header; |
403 | 68 | struct vpci_bar *bars = header->bars; |
404 | 68 | pci_sbdf_t sbdf = { |
405 | 68 | .seg = pdev->seg, |
406 | 68 | .bus = pdev->bus, |
407 | 68 | .dev = slot, |
408 | 68 | .func = func, |
409 | 68 | }; |
410 | 68 | int rc; |
411 | 68 | |
412 | 68 | switch ( pci_conf_read8(pdev->seg, pdev->bus, slot, func, PCI_HEADER_TYPE) |
413 | 68 | & 0x7f ) |
414 | 68 | { |
415 | 58 | case PCI_HEADER_TYPE_NORMAL: |
416 | 58 | num_bars = 6; |
417 | 58 | rom_reg = PCI_ROM_ADDRESS; |
418 | 58 | break; |
419 | 10 | case PCI_HEADER_TYPE_BRIDGE: |
420 | 10 | num_bars = 2; |
421 | 10 | rom_reg = PCI_ROM_ADDRESS1; |
422 | 10 | break; |
423 | 0 | default: |
424 | 0 | return -EOPNOTSUPP; |
425 | 68 | } |
426 | 68 | |
427 | 68 | /* Setup a handler for the command register. */ |
428 | 68 | rc = vpci_add_register(pdev->vpci, vpci_hw_read16, cmd_write, PCI_COMMAND, |
429 | 68 | 2, header); |
430 | 68 | if ( rc ) |
431 | 0 | return rc; |
432 | 68 | |
433 | 68 | /* Disable memory decoding before sizing. */ |
434 | 68 | cmd = pci_conf_read16(pdev->seg, pdev->bus, slot, func, PCI_COMMAND); |
435 | 68 | if ( cmd & PCI_COMMAND_MEMORY ) |
436 | 27 | pci_conf_write16(pdev->seg, pdev->bus, slot, func, PCI_COMMAND, |
437 | 27 | cmd & ~PCI_COMMAND_MEMORY); |
438 | 68 | |
439 | 436 | for ( i = 0; i < num_bars; i++ ) |
440 | 368 | { |
441 | 368 | uint8_t reg = PCI_BASE_ADDRESS_0 + i * 4; |
442 | 368 | uint32_t val; |
443 | 368 | |
444 | 368 | if ( i && bars[i - 1].type == VPCI_BAR_MEM64_LO ) |
445 | 7 | { |
446 | 7 | bars[i].type = VPCI_BAR_MEM64_HI; |
447 | 7 | rc = vpci_add_register(pdev->vpci, vpci_hw_read32, bar_write, reg, |
448 | 7 | 4, &bars[i]); |
449 | 7 | if ( rc ) |
450 | 0 | { |
451 | 0 | pci_conf_write16(pdev->seg, pdev->bus, slot, func, |
452 | 0 | PCI_COMMAND, cmd); |
453 | 0 | return rc; |
454 | 0 | } |
455 | 7 | |
456 | 7 | continue; |
457 | 7 | } |
458 | 368 | |
459 | 361 | val = pci_conf_read32(pdev->seg, pdev->bus, slot, func, reg); |
460 | 361 | if ( (val & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO ) |
461 | 18 | { |
462 | 18 | bars[i].type = VPCI_BAR_IO; |
463 | 18 | continue; |
464 | 18 | } |
465 | 343 | if ( (val & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == |
466 | 343 | PCI_BASE_ADDRESS_MEM_TYPE_64 ) |
467 | 7 | bars[i].type = VPCI_BAR_MEM64_LO; |
468 | 343 | else |
469 | 336 | bars[i].type = VPCI_BAR_MEM32; |
470 | 343 | |
471 | 343 | rc = pci_size_mem_bar(sbdf, reg, &addr, &size, |
472 | 275 | (i == num_bars - 1) ? PCI_BAR_LAST : 0); |
473 | 343 | if ( rc < 0 ) |
474 | 0 | { |
475 | 0 | pci_conf_write16(pdev->seg, pdev->bus, slot, func, PCI_COMMAND, |
476 | 0 | cmd); |
477 | 0 | return rc; |
478 | 0 | } |
479 | 343 | |
480 | 343 | if ( size == 0 ) |
481 | 317 | { |
482 | 317 | bars[i].type = VPCI_BAR_EMPTY; |
483 | 317 | continue; |
484 | 317 | } |
485 | 343 | |
486 | 26 | bars[i].addr = addr; |
487 | 26 | bars[i].size = size; |
488 | 26 | bars[i].prefetchable = val & PCI_BASE_ADDRESS_MEM_PREFETCH; |
489 | 26 | |
490 | 26 | rc = vpci_add_register(pdev->vpci, vpci_hw_read32, bar_write, reg, 4, |
491 | 26 | &bars[i]); |
492 | 26 | if ( rc ) |
493 | 0 | { |
494 | 0 | pci_conf_write16(pdev->seg, pdev->bus, slot, func, PCI_COMMAND, |
495 | 0 | cmd); |
496 | 0 | return rc; |
497 | 0 | } |
498 | 26 | } |
499 | 68 | |
500 | 68 | /* Check expansion ROM. */ |
501 | 68 | rc = pci_size_mem_bar(sbdf, rom_reg, &addr, &size, PCI_BAR_ROM); |
502 | 68 | if ( rc > 0 && size ) |
503 | 1 | { |
504 | 1 | struct vpci_bar *rom = &header->bars[num_bars]; |
505 | 1 | |
506 | 1 | rom->type = VPCI_BAR_ROM; |
507 | 1 | rom->size = size; |
508 | 1 | rom->addr = addr; |
509 | 1 | rom->rom_enabled = pci_conf_read32(pdev->seg, pdev->bus, slot, func, |
510 | 1 | rom_reg) & PCI_ROM_ADDRESS_ENABLE; |
511 | 1 | |
512 | 1 | rc = vpci_add_register(pdev->vpci, vpci_hw_read32, rom_write, rom_reg, |
513 | 1 | 4, rom); |
514 | 1 | if ( rc ) |
515 | 0 | rom->type = VPCI_BAR_EMPTY; |
516 | 1 | } |
517 | 68 | |
518 | 68 | if ( cmd & PCI_COMMAND_MEMORY ) |
519 | 27 | modify_bars(pdev, true, false); |
520 | 68 | |
521 | 68 | return 0; |
522 | 68 | } |
523 | | REGISTER_VPCI_INIT(init_bars, VPCI_PRIORITY_MIDDLE); |
524 | | |
525 | | /* |
526 | | * Local variables: |
527 | | * mode: C |
528 | | * c-file-style: "BSD" |
529 | | * c-basic-offset: 4 |
530 | | * tab-width: 4 |
531 | | * indent-tabs-mode: nil |
532 | | * End: |
533 | | */ |