ia64/xen-unstable

view tools/ioemu/hw/pass-through.c @ 16391:5a72a99be911

hvm passthru: Uses of XC_PAGE_MASK should be XC_PAGE_SIZE-1.
Signed-off-by: Weidong Han <weidong.han@intel.com>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Nov 16 17:09:53 2007 +0000 (2007-11-16)
parents 4f1363491a77
children ef83b50fc4a4
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 uint8_t find_cap_offset(struct pci_dev *pci_dev, uint8_t cap)
60 {
61 int id;
62 int max_cap = 48;
63 int pos = PCI_CAPABILITY_LIST;
64 int status;
66 status = pci_read_byte(pci_dev, PCI_STATUS);
67 if ( (status & PCI_STATUS_CAP_LIST) == 0 )
68 return 0;
70 while ( max_cap-- )
71 {
72 pos = pci_read_byte(pci_dev, pos);
73 if ( pos < 0x40 )
74 break;
76 pos &= ~3;
77 id = pci_read_byte(pci_dev, pos + PCI_CAP_LIST_ID);
79 if ( id == 0xff )
80 break;
81 if ( id == cap )
82 return pos;
84 pos += PCI_CAP_LIST_NEXT;
85 }
86 return 0;
87 }
89 void pdev_flr(struct pci_dev *pci_dev)
90 {
91 int pos;
92 int dev_cap;
93 int dev_status;
95 pos = find_cap_offset(pci_dev, PCI_CAP_ID_EXP);
96 if ( pos )
97 {
98 dev_cap = pci_read_long(pci_dev, pos + PCI_EXP_DEVCAP);
99 if ( dev_cap & PCI_EXP_DEVCAP_FLR )
100 {
101 pci_write_word(pci_dev, pos + PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_FLR);
102 do {
103 dev_status = pci_read_long(pci_dev, pos + PCI_EXP_DEVSTA);
104 } while (dev_status & PCI_EXP_DEVSTA_TRPND);
105 }
106 }
107 }
109 /* Being called each time a mmio region has been updated */
110 void pt_iomem_map(PCIDevice *d, int i, uint32_t e_phys, uint32_t e_size,
111 int type)
112 {
113 struct pt_dev *assigned_device = (struct pt_dev *)d;
114 uint32_t old_ebase = assigned_device->bases[i].e_physbase;
115 int first_map = ( assigned_device->bases[i].e_size == 0 );
116 int ret = 0;
118 assigned_device->bases[i].e_physbase = e_phys;
119 assigned_device->bases[i].e_size= e_size;
121 PT_LOG("e_phys=%08x maddr=%08x type=%d len=%08x index=%d\n",
122 e_phys, assigned_device->bases[i].access.maddr, type, e_size, i);
124 if ( e_size == 0 )
125 return;
127 if ( !first_map )
128 {
129 /* Remove old mapping */
130 ret = xc_domain_memory_mapping(xc_handle, domid,
131 old_ebase >> XC_PAGE_SHIFT,
132 assigned_device->bases[i].access.maddr >> XC_PAGE_SHIFT,
133 (e_size+XC_PAGE_SIZE-1) >> XC_PAGE_SHIFT,
134 DPCI_REMOVE_MAPPING);
135 if ( ret != 0 )
136 {
137 PT_LOG("Error: remove old mapping failed!\n");
138 return;
139 }
140 }
142 /* Create new mapping */
143 ret = xc_domain_memory_mapping(xc_handle, domid,
144 assigned_device->bases[i].e_physbase >> XC_PAGE_SHIFT,
145 assigned_device->bases[i].access.maddr >> XC_PAGE_SHIFT,
146 (e_size+XC_PAGE_SIZE-1) >> XC_PAGE_SHIFT,
147 DPCI_ADD_MAPPING);
148 if ( ret != 0 )
149 PT_LOG("Error: create new mapping failed!\n");
151 }
153 /* Being called each time a pio region has been updated */
154 void pt_ioport_map(PCIDevice *d, int i,
155 uint32_t e_phys, uint32_t e_size, int type)
156 {
157 struct pt_dev *assigned_device = (struct pt_dev *)d;
158 uint32_t old_ebase = assigned_device->bases[i].e_physbase;
159 int first_map = ( assigned_device->bases[i].e_size == 0 );
160 int ret = 0;
162 assigned_device->bases[i].e_physbase = e_phys;
163 assigned_device->bases[i].e_size= e_size;
165 PT_LOG("e_phys=%04x pio_base=%04x len=%04x index=%d\n",
166 (uint16_t)e_phys, (uint16_t)assigned_device->bases[i].access.pio_base,
167 (uint16_t)e_size, i);
169 if ( e_size == 0 )
170 return;
172 if ( !first_map )
173 {
174 /* Remove old mapping */
175 ret = xc_domain_ioport_mapping(xc_handle, domid, old_ebase,
176 assigned_device->bases[i].access.pio_base, e_size,
177 DPCI_REMOVE_MAPPING);
178 if ( ret != 0 )
179 {
180 PT_LOG("Error: remove old mapping failed!\n");
181 return;
182 }
183 }
185 /* Create new mapping */
186 ret = xc_domain_ioport_mapping(xc_handle, domid, e_phys,
187 assigned_device->bases[i].access.pio_base, e_size,
188 DPCI_ADD_MAPPING);
189 if ( ret != 0 )
190 PT_LOG("Error: create new mapping failed!\n");
192 }
194 static void pt_pci_write_config(PCIDevice *d, uint32_t address, uint32_t val,
195 int len)
196 {
197 struct pt_dev *assigned_device = (struct pt_dev *)d;
198 struct pci_dev *pci_dev = assigned_device->pci_dev;
200 #ifdef PT_DEBUG_PCI_CONFIG_ACCESS
201 PT_LOG("(%x.%x): address=%04x val=0x%08x len=%d\n",
202 (d->devfn >> 3) & 0x1F, (d->devfn & 0x7), address, val, len);
203 #endif
205 /* Pre-write hooking */
206 switch ( address ) {
207 case 0x0C ... 0x3F:
208 pci_default_write_config(d, address, val, len);
209 return;
210 }
212 /* PCI config pass-through */
213 if (address == 0x4) {
214 switch (len){
215 case 1:
216 pci_write_byte(pci_dev, address, val);
217 break;
218 case 2:
219 pci_write_word(pci_dev, address, val);
220 break;
221 case 4:
222 pci_write_long(pci_dev, address, val);
223 break;
224 }
225 }
227 if (address == 0x4) {
228 /* Post-write hooking */
229 pci_default_write_config(d, address, val, len);
230 }
231 }
233 static uint32_t pt_pci_read_config(PCIDevice *d, uint32_t address, int len)
234 {
235 struct pt_dev *assigned_device = (struct pt_dev *)d;
236 struct pci_dev *pci_dev = assigned_device->pci_dev;
237 uint32_t val = 0xFF;
239 /* Pre-hooking */
240 switch ( address ) {
241 case 0x0C ... 0x3F:
242 val = pci_default_read_config(d, address, len);
243 goto exit;
244 }
246 switch ( len ) {
247 case 1:
248 val = pci_read_byte(pci_dev, address);
249 break;
250 case 2:
251 val = pci_read_word(pci_dev, address);
252 break;
253 case 4:
254 val = pci_read_long(pci_dev, address);
255 break;
256 }
258 exit:
260 #ifdef PT_DEBUG_PCI_CONFIG_ACCESS
261 PT_LOG("(%x.%x): address=%04x val=0x%08x len=%d\n",
262 (d->devfn >> 3) & 0x1F, (d->devfn & 0x7), address, val, len);
263 #endif
265 return val;
266 }
268 static int pt_register_regions(struct pt_dev *assigned_device)
269 {
270 int i = 0;
271 uint32_t bar_data = 0;
272 struct pci_dev *pci_dev = assigned_device->pci_dev;
273 PCIDevice *d = &assigned_device->dev;
275 /* Register PIO/MMIO BARs */
276 for ( i=0; i < PCI_BAR_ENTRIES; i++ )
277 {
278 if ( pci_dev->base_addr[i] )
279 {
280 assigned_device->bases[i].e_physbase = pci_dev->base_addr[i];
281 assigned_device->bases[i].access.u = pci_dev->base_addr[i];
283 /* Register current region */
284 bar_data = *((uint32_t*)(d->config + PCI_BASE_ADDRESS_0) + i);
285 if ( bar_data & PCI_ADDRESS_SPACE_IO )
286 pci_register_io_region((PCIDevice *)assigned_device, i,
287 (uint32_t)pci_dev->size[i], PCI_ADDRESS_SPACE_IO,
288 pt_ioport_map);
289 else if ( bar_data & PCI_ADDRESS_SPACE_MEM_PREFETCH )
290 pci_register_io_region((PCIDevice *)assigned_device, i,
291 (uint32_t)pci_dev->size[i], PCI_ADDRESS_SPACE_MEM_PREFETCH,
292 pt_iomem_map);
293 else
294 pci_register_io_region((PCIDevice *)assigned_device, i,
295 (uint32_t)pci_dev->size[i], PCI_ADDRESS_SPACE_MEM,
296 pt_iomem_map);
298 PT_LOG("IO region registered (size=0x%08x base_addr=0x%08x)\n",
299 (uint32_t)(pci_dev->size[i]),
300 (uint32_t)(pci_dev->base_addr[i]));
301 }
302 }
304 /* Register expansion ROM address */
305 if ( pci_dev->rom_base_addr && pci_dev->rom_size )
306 {
307 assigned_device->bases[PCI_ROM_SLOT].e_physbase =
308 pci_dev->rom_base_addr;
309 assigned_device->bases[PCI_ROM_SLOT].access.maddr =
310 pci_dev->rom_base_addr;
311 pci_register_io_region((PCIDevice *)assigned_device, PCI_ROM_SLOT,
312 pci_dev->rom_size, PCI_ADDRESS_SPACE_MEM_PREFETCH,
313 pt_iomem_map);
315 PT_LOG("Expansion ROM registered (size=0x%08x base_addr=0x%08x)\n",
316 (uint32_t)(pci_dev->rom_size), (uint32_t)(pci_dev->rom_base_addr));
317 }
319 return 0;
320 }
322 struct pt_dev * register_real_device(PCIBus *e_bus,
323 const char *e_dev_name, int e_devfn, uint8_t r_bus, uint8_t r_dev,
324 uint8_t r_func, uint32_t machine_irq, struct pci_access *pci_access)
325 {
326 int rc, i;
327 struct pt_dev *assigned_device = NULL;
328 struct pci_dev *pci_dev;
329 uint8_t e_device, e_intx;
331 PT_LOG("Assigning real physical device %02x:%02x.%x ...\n",
332 r_bus, r_dev, r_func);
334 /* Find real device structure */
335 for (pci_dev = pci_access->devices; pci_dev != NULL;
336 pci_dev = pci_dev->next)
337 {
338 if ((r_bus == pci_dev->bus) && (r_dev == pci_dev->dev)
339 && (r_func == pci_dev->func))
340 break;
341 }
342 if ( pci_dev == NULL )
343 {
344 PT_LOG("Error: couldn't locate device in libpci structures\n");
345 return NULL;
346 }
348 /* Register device */
349 assigned_device = (struct pt_dev *) pci_register_device(e_bus, e_dev_name,
350 sizeof(struct pt_dev), e_devfn,
351 pt_pci_read_config, pt_pci_write_config);
352 if ( assigned_device == NULL )
353 {
354 PT_LOG("Error: couldn't register real device\n");
355 return NULL;
356 }
358 assigned_device->pci_dev = pci_dev;
360 /* Issue PCIe FLR */
361 pdev_flr(pci_dev);
363 /* Initialize virtualized PCI configuration (Extended 256 Bytes) */
364 for ( i = 0; i < PCI_CONFIG_SIZE; i++ )
365 assigned_device->dev.config[i] = pci_read_byte(pci_dev, i);
367 /* Handle real device's MMIO/PIO BARs */
368 pt_register_regions(assigned_device);
370 /* Bind interrupt */
371 e_device = (assigned_device->dev.devfn >> 3) & 0x1f;
372 e_intx = assigned_device->dev.config[0x3d]-1;
374 if ( PT_MACHINE_IRQ_AUTO == machine_irq )
375 machine_irq = pci_dev->irq;
377 /* bind machine_irq to device */
378 if ( 0 != machine_irq )
379 {
380 rc = xc_domain_bind_pt_pci_irq(xc_handle, domid, machine_irq, 0,
381 e_device, e_intx);
382 if ( rc < 0 )
383 {
384 /* TBD: unregister device in case of an error */
385 PT_LOG("Error: Binding of interrupt failed! rc=%d\n", rc);
386 }
387 }
388 else {
389 /* Disable PCI intx assertion (turn on bit10 of devctl) */
390 assigned_device->dev.config[0x05] |= 0x04;
391 pci_write_word(pci_dev, 0x04,
392 *(uint16_t *)(&assigned_device->dev.config[0x04]));
393 }
395 PT_LOG("Real physical device %02x:%02x.%x registered successfuly!\n",
396 r_bus, r_dev, r_func);
398 return assigned_device;
399 }
401 int pt_init(PCIBus *e_bus, char *direct_pci)
402 {
403 int seg, b, d, f;
404 struct pt_dev *pt_dev;
405 struct pci_access *pci_access;
407 /* Initialize libpci */
408 pci_access = pci_alloc();
409 if ( pci_access == NULL )
410 {
411 PT_LOG("pci_access is NULL\n");
412 return -1;
413 }
414 pci_init(pci_access);
415 pci_scan_bus(pci_access);
417 /* Assign given devices to guest */
418 while ( next_bdf(&direct_pci, &seg, &b, &d, &f) )
419 {
420 /* Register real device with the emulated bus */
421 pt_dev = register_real_device(e_bus, "DIRECT PCI", PT_VIRT_DEVFN_AUTO,
422 b, d, f, PT_MACHINE_IRQ_AUTO, pci_access);
423 if ( pt_dev == NULL )
424 {
425 PT_LOG("Error: Registration failed (%02x:%02x.%x)\n", b, d, f);
426 return -1;
427 }
428 }
430 /* Success */
431 return 0;
432 }