ia64/xen-unstable

view tools/ioemu/hw/pass-through.c @ 16875:74a9bfccddba

vt-d: Do FLR of assigned devices with VT-d

Currently there is a pdev_flr() function to do FLR before device
assignment in qemu, but most of devices don't have FLR capability.
What's more, should do FLR before assignment and deassignment for
keeping correct device status. If the device doesn't have FLR
capablility, this patch implemented to enter D3hot and return to D0 to
do FLR. And exposed pdev_flr() in VT-d utils, then it can be invoked
by assignment and deassignment functions.

Signed-off-by: Weidong Han <weidong.han@intel.com>
Signed-off-by: Anthony Xu <anthony.xu@intel.com>
author Keir Fraser <keir.fraser@citrix.com>
date Thu Jan 24 14:39:38 2008 +0000 (2008-01-24)
parents ef83b50fc4a4
children c6eeb71a85cf
line source
1 /*
2 * Copyright (c) 2007, Neocleus Corporation.
3 * Copyright (c) 2007, Intel Corporation.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16 * Place - Suite 330, Boston, MA 02111-1307 USA.
17 *
18 * Alex Novik <alex@neocleus.com>
19 * Allen Kay <allen.m.kay@intel.com>
20 * Guy Zana <guy@neocleus.com>
21 *
22 * This file implements direct PCI assignment to a HVM guest
23 */
25 #include "vl.h"
26 #include "pass-through.h"
27 #include "pci/header.h"
28 #include "pci/pci.h"
30 extern FILE *logfile;
32 static int token_value(char *token)
33 {
34 token = strchr(token, 'x') + 1;
35 return strtol(token, NULL, 16);
36 }
38 static int next_bdf(char **str, int *seg, int *bus, int *dev, int *func)
39 {
40 char *token;
42 if ( !(*str) || !strchr(*str, ',') )
43 return 0;
45 token = *str;
46 *seg = token_value(token);
47 token = strchr(token, ',') + 1;
48 *bus = token_value(token);
49 token = strchr(token, ',') + 1;
50 *dev = token_value(token);
51 token = strchr(token, ',') + 1;
52 *func = token_value(token);
53 token = strchr(token, ',');
54 *str = token ? token + 1 : NULL;
56 return 1;
57 }
59 /* Being called each time a mmio region has been updated */
60 void pt_iomem_map(PCIDevice *d, int i, uint32_t e_phys, uint32_t e_size,
61 int type)
62 {
63 struct pt_dev *assigned_device = (struct pt_dev *)d;
64 uint32_t old_ebase = assigned_device->bases[i].e_physbase;
65 int first_map = ( assigned_device->bases[i].e_size == 0 );
66 int ret = 0;
68 assigned_device->bases[i].e_physbase = e_phys;
69 assigned_device->bases[i].e_size= e_size;
71 PT_LOG("e_phys=%08x maddr=%08x type=%d len=%08x index=%d\n",
72 e_phys, assigned_device->bases[i].access.maddr, type, e_size, i);
74 if ( e_size == 0 )
75 return;
77 if ( !first_map )
78 {
79 /* Remove old mapping */
80 ret = xc_domain_memory_mapping(xc_handle, domid,
81 old_ebase >> XC_PAGE_SHIFT,
82 assigned_device->bases[i].access.maddr >> XC_PAGE_SHIFT,
83 (e_size+XC_PAGE_SIZE-1) >> XC_PAGE_SHIFT,
84 DPCI_REMOVE_MAPPING);
85 if ( ret != 0 )
86 {
87 PT_LOG("Error: remove old mapping failed!\n");
88 return;
89 }
90 }
92 /* Create new mapping */
93 ret = xc_domain_memory_mapping(xc_handle, domid,
94 assigned_device->bases[i].e_physbase >> XC_PAGE_SHIFT,
95 assigned_device->bases[i].access.maddr >> XC_PAGE_SHIFT,
96 (e_size+XC_PAGE_SIZE-1) >> XC_PAGE_SHIFT,
97 DPCI_ADD_MAPPING);
98 if ( ret != 0 )
99 PT_LOG("Error: create new mapping failed!\n");
101 }
103 /* Being called each time a pio region has been updated */
104 void pt_ioport_map(PCIDevice *d, int i,
105 uint32_t e_phys, uint32_t e_size, int type)
106 {
107 struct pt_dev *assigned_device = (struct pt_dev *)d;
108 uint32_t old_ebase = assigned_device->bases[i].e_physbase;
109 int first_map = ( assigned_device->bases[i].e_size == 0 );
110 int ret = 0;
112 assigned_device->bases[i].e_physbase = e_phys;
113 assigned_device->bases[i].e_size= e_size;
115 PT_LOG("e_phys=%04x pio_base=%04x len=%04x index=%d\n",
116 (uint16_t)e_phys, (uint16_t)assigned_device->bases[i].access.pio_base,
117 (uint16_t)e_size, i);
119 if ( e_size == 0 )
120 return;
122 if ( !first_map )
123 {
124 /* Remove old mapping */
125 ret = xc_domain_ioport_mapping(xc_handle, domid, old_ebase,
126 assigned_device->bases[i].access.pio_base, e_size,
127 DPCI_REMOVE_MAPPING);
128 if ( ret != 0 )
129 {
130 PT_LOG("Error: remove old mapping failed!\n");
131 return;
132 }
133 }
135 /* Create new mapping */
136 ret = xc_domain_ioport_mapping(xc_handle, domid, e_phys,
137 assigned_device->bases[i].access.pio_base, e_size,
138 DPCI_ADD_MAPPING);
139 if ( ret != 0 )
140 PT_LOG("Error: create new mapping failed!\n");
142 }
144 static void pt_pci_write_config(PCIDevice *d, uint32_t address, uint32_t val,
145 int len)
146 {
147 struct pt_dev *assigned_device = (struct pt_dev *)d;
148 struct pci_dev *pci_dev = assigned_device->pci_dev;
150 #ifdef PT_DEBUG_PCI_CONFIG_ACCESS
151 PT_LOG("(%x.%x): address=%04x val=0x%08x len=%d\n",
152 (d->devfn >> 3) & 0x1F, (d->devfn & 0x7), address, val, len);
153 #endif
155 /* Pre-write hooking */
156 switch ( address ) {
157 case 0x0C ... 0x3F:
158 pci_default_write_config(d, address, val, len);
159 return;
160 }
162 /* PCI config pass-through */
163 if (address == 0x4) {
164 switch (len){
165 case 1:
166 pci_write_byte(pci_dev, address, val);
167 break;
168 case 2:
169 pci_write_word(pci_dev, address, val);
170 break;
171 case 4:
172 pci_write_long(pci_dev, address, val);
173 break;
174 }
175 }
177 if (address == 0x4) {
178 /* Post-write hooking */
179 pci_default_write_config(d, address, val, len);
180 }
181 }
183 static uint32_t pt_pci_read_config(PCIDevice *d, uint32_t address, int len)
184 {
185 struct pt_dev *assigned_device = (struct pt_dev *)d;
186 struct pci_dev *pci_dev = assigned_device->pci_dev;
187 uint32_t val = 0xFF;
189 /* Pre-hooking */
190 switch ( address ) {
191 case 0x0C ... 0x3F:
192 val = pci_default_read_config(d, address, len);
193 goto exit;
194 }
196 switch ( len ) {
197 case 1:
198 val = pci_read_byte(pci_dev, address);
199 break;
200 case 2:
201 val = pci_read_word(pci_dev, address);
202 break;
203 case 4:
204 val = pci_read_long(pci_dev, address);
205 break;
206 }
208 exit:
210 #ifdef PT_DEBUG_PCI_CONFIG_ACCESS
211 PT_LOG("(%x.%x): address=%04x val=0x%08x len=%d\n",
212 (d->devfn >> 3) & 0x1F, (d->devfn & 0x7), address, val, len);
213 #endif
215 return val;
216 }
218 static int pt_register_regions(struct pt_dev *assigned_device)
219 {
220 int i = 0;
221 uint32_t bar_data = 0;
222 struct pci_dev *pci_dev = assigned_device->pci_dev;
223 PCIDevice *d = &assigned_device->dev;
225 /* Register PIO/MMIO BARs */
226 for ( i = 0; i < PCI_BAR_ENTRIES; i++ )
227 {
228 if ( pci_dev->base_addr[i] )
229 {
230 assigned_device->bases[i].e_physbase = pci_dev->base_addr[i];
231 assigned_device->bases[i].access.u = pci_dev->base_addr[i];
233 /* Register current region */
234 bar_data = *((uint32_t*)(d->config + PCI_BASE_ADDRESS_0) + i);
235 if ( bar_data & PCI_ADDRESS_SPACE_IO )
236 pci_register_io_region((PCIDevice *)assigned_device, i,
237 (uint32_t)pci_dev->size[i], PCI_ADDRESS_SPACE_IO,
238 pt_ioport_map);
239 else if ( bar_data & PCI_ADDRESS_SPACE_MEM_PREFETCH )
240 pci_register_io_region((PCIDevice *)assigned_device, i,
241 (uint32_t)pci_dev->size[i], PCI_ADDRESS_SPACE_MEM_PREFETCH,
242 pt_iomem_map);
243 else
244 pci_register_io_region((PCIDevice *)assigned_device, i,
245 (uint32_t)pci_dev->size[i], PCI_ADDRESS_SPACE_MEM,
246 pt_iomem_map);
248 PT_LOG("IO region registered (size=0x%08x base_addr=0x%08x)\n",
249 (uint32_t)(pci_dev->size[i]),
250 (uint32_t)(pci_dev->base_addr[i]));
251 }
252 }
254 /* Register expansion ROM address */
255 if ( pci_dev->rom_base_addr && pci_dev->rom_size )
256 {
257 assigned_device->bases[PCI_ROM_SLOT].e_physbase =
258 pci_dev->rom_base_addr;
259 assigned_device->bases[PCI_ROM_SLOT].access.maddr =
260 pci_dev->rom_base_addr;
261 pci_register_io_region((PCIDevice *)assigned_device, PCI_ROM_SLOT,
262 pci_dev->rom_size, PCI_ADDRESS_SPACE_MEM_PREFETCH,
263 pt_iomem_map);
265 PT_LOG("Expansion ROM registered (size=0x%08x base_addr=0x%08x)\n",
266 (uint32_t)(pci_dev->rom_size), (uint32_t)(pci_dev->rom_base_addr));
267 }
269 return 0;
270 }
272 struct pt_dev * register_real_device(PCIBus *e_bus,
273 const char *e_dev_name, int e_devfn, uint8_t r_bus, uint8_t r_dev,
274 uint8_t r_func, uint32_t machine_irq, struct pci_access *pci_access)
275 {
276 int rc, i;
277 struct pt_dev *assigned_device = NULL;
278 struct pci_dev *pci_dev;
279 uint8_t e_device, e_intx;
280 struct pci_config_cf8 machine_bdf;
282 PT_LOG("Assigning real physical device %02x:%02x.%x ...\n",
283 r_bus, r_dev, r_func);
285 /* Find real device structure */
286 for (pci_dev = pci_access->devices; pci_dev != NULL;
287 pci_dev = pci_dev->next)
288 {
289 if ((r_bus == pci_dev->bus) && (r_dev == pci_dev->dev)
290 && (r_func == pci_dev->func))
291 break;
292 }
293 if ( pci_dev == NULL )
294 {
295 PT_LOG("Error: couldn't locate device in libpci structures\n");
296 return NULL;
297 }
299 /* Register device */
300 assigned_device = (struct pt_dev *) pci_register_device(e_bus, e_dev_name,
301 sizeof(struct pt_dev), e_devfn,
302 pt_pci_read_config, pt_pci_write_config);
303 if ( assigned_device == NULL )
304 {
305 PT_LOG("Error: couldn't register real device\n");
306 return NULL;
307 }
309 assigned_device->pci_dev = pci_dev;
311 /* Assign device */
312 machine_bdf.reg = 0;
313 machine_bdf.bus = r_bus;
314 machine_bdf.dev = r_dev;
315 machine_bdf.func = r_func;
316 rc = xc_assign_device(xc_handle, domid, machine_bdf.value);
317 if ( rc < 0 )
318 PT_LOG("Error: xc_assign_device error %d\n", rc);
320 /* Initialize virtualized PCI configuration (Extended 256 Bytes) */
321 for ( i = 0; i < PCI_CONFIG_SIZE; i++ )
322 assigned_device->dev.config[i] = pci_read_byte(pci_dev, i);
324 /* Handle real device's MMIO/PIO BARs */
325 pt_register_regions(assigned_device);
327 /* Bind interrupt */
328 e_device = (assigned_device->dev.devfn >> 3) & 0x1f;
329 e_intx = assigned_device->dev.config[0x3d]-1;
331 if ( PT_MACHINE_IRQ_AUTO == machine_irq )
332 machine_irq = pci_dev->irq;
334 /* bind machine_irq to device */
335 if ( 0 != machine_irq )
336 {
337 rc = xc_domain_bind_pt_pci_irq(xc_handle, domid, machine_irq, 0,
338 e_device, e_intx);
339 if ( rc < 0 )
340 {
341 /* TBD: unregister device in case of an error */
342 PT_LOG("Error: Binding of interrupt failed! rc=%d\n", rc);
343 }
344 }
345 else {
346 /* Disable PCI intx assertion (turn on bit10 of devctl) */
347 assigned_device->dev.config[0x05] |= 0x04;
348 pci_write_word(pci_dev, 0x04,
349 *(uint16_t *)(&assigned_device->dev.config[0x04]));
350 }
352 PT_LOG("Real physical device %02x:%02x.%x registered successfuly!\n",
353 r_bus, r_dev, r_func);
355 return assigned_device;
356 }
358 int pt_init(PCIBus *e_bus, char *direct_pci)
359 {
360 int seg, b, d, f;
361 struct pt_dev *pt_dev;
362 struct pci_access *pci_access;
364 /* Initialize libpci */
365 pci_access = pci_alloc();
366 if ( pci_access == NULL )
367 {
368 PT_LOG("pci_access is NULL\n");
369 return -1;
370 }
371 pci_init(pci_access);
372 pci_scan_bus(pci_access);
374 /* Assign given devices to guest */
375 while ( next_bdf(&direct_pci, &seg, &b, &d, &f) )
376 {
377 /* Register real device with the emulated bus */
378 pt_dev = register_real_device(e_bus, "DIRECT PCI", PT_VIRT_DEVFN_AUTO,
379 b, d, f, PT_MACHINE_IRQ_AUTO, pci_access);
380 if ( pt_dev == NULL )
381 {
382 PT_LOG("Error: Registration failed (%02x:%02x.%x)\n", b, d, f);
383 return -1;
384 }
385 }
387 /* Success */
388 return 0;
389 }