]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - usr.sbin/bhyve/pci_passthru.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / usr.sbin / bhyve / pci_passthru.c
1 /*-
2  * Copyright (c) 2011 NetApp, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/types.h>
34 #include <sys/pciio.h>
35 #include <sys/ioctl.h>
36
37 #include <dev/io/iodev.h>
38 #include <dev/pci/pcireg.h>
39
40 #include <machine/iodev.h>
41
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <unistd.h>
48
49 #include <machine/vmm.h>
50 #include <vmmapi.h>
51 #include "pci_emul.h"
52 #include "mem.h"
53
54 #ifndef _PATH_DEVPCI
55 #define _PATH_DEVPCI    "/dev/pci"
56 #endif
57
58 #ifndef _PATH_DEVIO
59 #define _PATH_DEVIO     "/dev/io"
60 #endif
61
62 #define LEGACY_SUPPORT  1
63
64 #define MSIX_TABLE_COUNT(ctrl) (((ctrl) & PCIM_MSIXCTRL_TABLE_SIZE) + 1)
65 #define MSIX_CAPLEN 12
66
67 static int pcifd = -1;
68 static int iofd = -1;
69
70 struct passthru_softc {
71         struct pci_devinst *psc_pi;
72         struct pcibar psc_bar[PCI_BARMAX + 1];
73         struct {
74                 int             capoff;
75                 int             msgctrl;
76                 int             emulated;
77         } psc_msi;
78         struct {
79                 int             capoff;
80         } psc_msix;
81         struct pcisel psc_sel;
82 };
83
84 static int
85 msi_caplen(int msgctrl)
86 {
87         int len;
88         
89         len = 10;               /* minimum length of msi capability */
90
91         if (msgctrl & PCIM_MSICTRL_64BIT)
92                 len += 4;
93
94 #if 0
95         /*
96          * Ignore the 'mask' and 'pending' bits in the MSI capability.
97          * We'll let the guest manipulate them directly.
98          */
99         if (msgctrl & PCIM_MSICTRL_VECTOR)
100                 len += 10;
101 #endif
102
103         return (len);
104 }
105
106 static uint32_t
107 read_config(const struct pcisel *sel, long reg, int width)
108 {
109         struct pci_io pi;
110
111         bzero(&pi, sizeof(pi));
112         pi.pi_sel = *sel;
113         pi.pi_reg = reg;
114         pi.pi_width = width;
115
116         if (ioctl(pcifd, PCIOCREAD, &pi) < 0)
117                 return (0);                             /* XXX */
118         else
119                 return (pi.pi_data);
120 }
121
122 static void
123 write_config(const struct pcisel *sel, long reg, int width, uint32_t data)
124 {
125         struct pci_io pi;
126
127         bzero(&pi, sizeof(pi));
128         pi.pi_sel = *sel;
129         pi.pi_reg = reg;
130         pi.pi_width = width;
131         pi.pi_data = data;
132
133         (void)ioctl(pcifd, PCIOCWRITE, &pi);            /* XXX */
134 }
135
136 #ifdef LEGACY_SUPPORT
137 static int
138 passthru_add_msicap(struct pci_devinst *pi, int msgnum, int nextptr)
139 {
140         int capoff, i;
141         struct msicap msicap;
142         u_char *capdata;
143
144         pci_populate_msicap(&msicap, msgnum, nextptr);
145
146         /*
147          * XXX
148          * Copy the msi capability structure in the last 16 bytes of the
149          * config space. This is wrong because it could shadow something
150          * useful to the device.
151          */
152         capoff = 256 - roundup(sizeof(msicap), 4);
153         capdata = (u_char *)&msicap;
154         for (i = 0; i < sizeof(msicap); i++)
155                 pci_set_cfgdata8(pi, capoff + i, capdata[i]);
156
157         return (capoff);
158 }
159 #endif  /* LEGACY_SUPPORT */
160
161 static int
162 cfginitmsi(struct passthru_softc *sc)
163 {
164         int i, ptr, capptr, cap, sts, caplen, table_size;
165         uint32_t u32;
166         struct pcisel sel;
167         struct pci_devinst *pi;
168         struct msixcap msixcap;
169         uint32_t *msixcap_ptr;
170
171         pi = sc->psc_pi;
172         sel = sc->psc_sel;
173
174         /*
175          * Parse the capabilities and cache the location of the MSI
176          * and MSI-X capabilities.
177          */
178         sts = read_config(&sel, PCIR_STATUS, 2);
179         if (sts & PCIM_STATUS_CAPPRESENT) {
180                 ptr = read_config(&sel, PCIR_CAP_PTR, 1);
181                 while (ptr != 0 && ptr != 0xff) {
182                         cap = read_config(&sel, ptr + PCICAP_ID, 1);
183                         if (cap == PCIY_MSI) {
184                                 /*
185                                  * Copy the MSI capability into the config
186                                  * space of the emulated pci device
187                                  */
188                                 sc->psc_msi.capoff = ptr;
189                                 sc->psc_msi.msgctrl = read_config(&sel,
190                                                                   ptr + 2, 2);
191                                 sc->psc_msi.emulated = 0;
192                                 caplen = msi_caplen(sc->psc_msi.msgctrl);
193                                 capptr = ptr;
194                                 while (caplen > 0) {
195                                         u32 = read_config(&sel, capptr, 4);
196                                         pci_set_cfgdata32(pi, capptr, u32);
197                                         caplen -= 4;
198                                         capptr += 4;
199                                 }
200                         } else if (cap == PCIY_MSIX) {
201                                 /*
202                                  * Copy the MSI-X capability 
203                                  */
204                                 sc->psc_msix.capoff = ptr;
205                                 caplen = 12;
206                                 msixcap_ptr = (uint32_t*) &msixcap;
207                                 capptr = ptr;
208                                 while (caplen > 0) {
209                                         u32 = read_config(&sel, capptr, 4);
210                                         *msixcap_ptr = u32;
211                                         pci_set_cfgdata32(pi, capptr, u32);
212                                         caplen -= 4;
213                                         capptr += 4;
214                                         msixcap_ptr++;
215                                 }
216                         }
217                         ptr = read_config(&sel, ptr + PCICAP_NEXTPTR, 1);
218                 }
219         }
220
221         if (sc->psc_msix.capoff != 0) {
222                 pi->pi_msix.pba_bar =
223                     msixcap.pba_info & PCIM_MSIX_BIR_MASK;
224                 pi->pi_msix.pba_offset =
225                     msixcap.pba_info & ~PCIM_MSIX_BIR_MASK;
226                 pi->pi_msix.table_bar =
227                     msixcap.table_info & PCIM_MSIX_BIR_MASK;
228                 pi->pi_msix.table_offset =
229                     msixcap.table_info & ~PCIM_MSIX_BIR_MASK;
230                 pi->pi_msix.table_count = MSIX_TABLE_COUNT(msixcap.msgctrl);
231                 pi->pi_msix.pba_size = PBA_SIZE(pi->pi_msix.table_count);
232
233                 /* Allocate the emulated MSI-X table array */
234                 table_size = pi->pi_msix.table_count * MSIX_TABLE_ENTRY_SIZE;
235                 pi->pi_msix.table = calloc(1, table_size);
236
237                 /* Mask all table entries */
238                 for (i = 0; i < pi->pi_msix.table_count; i++) {
239                         pi->pi_msix.table[i].vector_control |=
240                                                 PCIM_MSIX_VCTRL_MASK;
241                 }
242         }
243
244 #ifdef LEGACY_SUPPORT
245         /*
246          * If the passthrough device does not support MSI then craft a
247          * MSI capability for it. We link the new MSI capability at the
248          * head of the list of capabilities.
249          */
250         if ((sts & PCIM_STATUS_CAPPRESENT) != 0 && sc->psc_msi.capoff == 0) {
251                 int origptr, msiptr;
252                 origptr = read_config(&sel, PCIR_CAP_PTR, 1);
253                 msiptr = passthru_add_msicap(pi, 1, origptr);
254                 sc->psc_msi.capoff = msiptr;
255                 sc->psc_msi.msgctrl = pci_get_cfgdata16(pi, msiptr + 2);
256                 sc->psc_msi.emulated = 1;
257                 pci_set_cfgdata8(pi, PCIR_CAP_PTR, msiptr);
258         }
259 #endif
260
261         /* Make sure one of the capabilities is present */
262         if (sc->psc_msi.capoff == 0 && sc->psc_msix.capoff == 0) 
263                 return (-1);
264         else
265                 return (0);
266 }
267
268 static uint64_t
269 msix_table_read(struct passthru_softc *sc, uint64_t offset, int size)
270 {
271         struct pci_devinst *pi;
272         struct msix_table_entry *entry;
273         uint8_t *src8;
274         uint16_t *src16;
275         uint32_t *src32;
276         uint64_t *src64;
277         uint64_t data;
278         size_t entry_offset;
279         int index;
280
281         pi = sc->psc_pi;
282         if (offset < pi->pi_msix.table_offset)
283                 return (-1);
284
285         offset -= pi->pi_msix.table_offset;
286         index = offset / MSIX_TABLE_ENTRY_SIZE;
287         if (index >= pi->pi_msix.table_count)
288                 return (-1);
289
290         entry = &pi->pi_msix.table[index];
291         entry_offset = offset % MSIX_TABLE_ENTRY_SIZE;
292
293         switch(size) {
294         case 1:
295                 src8 = (uint8_t *)((void *)entry + entry_offset);
296                 data = *src8;
297                 break;
298         case 2:
299                 src16 = (uint16_t *)((void *)entry + entry_offset);
300                 data = *src16;
301                 break;
302         case 4:
303                 src32 = (uint32_t *)((void *)entry + entry_offset);
304                 data = *src32;
305                 break;
306         case 8:
307                 src64 = (uint64_t *)((void *)entry + entry_offset);
308                 data = *src64;
309                 break;
310         default:
311                 return (-1);
312         }
313
314         return (data);
315 }
316
317 static void
318 msix_table_write(struct vmctx *ctx, int vcpu, struct passthru_softc *sc,
319                  uint64_t offset, int size, uint64_t data)
320 {
321         struct pci_devinst *pi;
322         struct msix_table_entry *entry;
323         uint32_t *dest;
324         size_t entry_offset;
325         uint32_t vector_control;
326         int error, index;
327
328         pi = sc->psc_pi;
329         if (offset < pi->pi_msix.table_offset)
330                 return;
331
332         offset -= pi->pi_msix.table_offset;
333         index = offset / MSIX_TABLE_ENTRY_SIZE;
334         if (index >= pi->pi_msix.table_count)
335                 return;
336
337         entry = &pi->pi_msix.table[index];
338         entry_offset = offset % MSIX_TABLE_ENTRY_SIZE;
339
340         /* Only 4 byte naturally-aligned writes are supported */
341         assert(size == 4);
342         assert(entry_offset % 4 == 0);
343
344         vector_control = entry->vector_control;
345         dest = (uint32_t *)((void *)entry + entry_offset);
346         *dest = data;
347         /* If MSI-X hasn't been enabled, do nothing */
348         if (pi->pi_msix.enabled) {
349                 /* If the entry is masked, don't set it up */
350                 if ((entry->vector_control & PCIM_MSIX_VCTRL_MASK) == 0 ||
351                     (vector_control & PCIM_MSIX_VCTRL_MASK) == 0) {
352                         error = vm_setup_pptdev_msix(ctx, vcpu,
353                             sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, 
354                             sc->psc_sel.pc_func, index, entry->addr,
355                             entry->msg_data, entry->vector_control);
356                 }
357         }
358 }
359
360 static int
361 init_msix_table(struct vmctx *ctx, struct passthru_softc *sc, uint64_t base)
362 {
363         int b, s, f;
364         int error, idx;
365         size_t len, remaining;
366         uint32_t table_size, table_offset;
367         uint32_t pba_size, pba_offset;
368         vm_paddr_t start;
369         struct pci_devinst *pi = sc->psc_pi;
370
371         assert(pci_msix_table_bar(pi) >= 0 && pci_msix_pba_bar(pi) >= 0);
372
373         b = sc->psc_sel.pc_bus;
374         s = sc->psc_sel.pc_dev;
375         f = sc->psc_sel.pc_func;
376
377         /* 
378          * If the MSI-X table BAR maps memory intended for
379          * other uses, it is at least assured that the table 
380          * either resides in its own page within the region, 
381          * or it resides in a page shared with only the PBA.
382          */
383         table_offset = rounddown2(pi->pi_msix.table_offset, 4096);
384
385         table_size = pi->pi_msix.table_offset - table_offset;
386         table_size += pi->pi_msix.table_count * MSIX_TABLE_ENTRY_SIZE;
387         table_size = roundup2(table_size, 4096);
388
389         if (pi->pi_msix.pba_bar == pi->pi_msix.table_bar) {
390                 pba_offset = pi->pi_msix.pba_offset;
391                 pba_size = pi->pi_msix.pba_size;
392                 if (pba_offset >= table_offset + table_size ||
393                     table_offset >= pba_offset + pba_size) {
394                         /*
395                          * The PBA can reside in the same BAR as the MSI-x
396                          * tables as long as it does not overlap with any
397                          * naturally aligned page occupied by the tables.
398                          */
399                 } else {
400                         /* Need to also emulate the PBA, not supported yet */
401                         printf("Unsupported MSI-X configuration: %d/%d/%d\n",
402                             b, s, f);
403                         return (-1);
404                 }
405         }
406
407         idx = pi->pi_msix.table_bar;
408         start = pi->pi_bar[idx].addr;
409         remaining = pi->pi_bar[idx].size;
410
411         /* Map everything before the MSI-X table */
412         if (table_offset > 0) {
413                 len = table_offset;
414                 error = vm_map_pptdev_mmio(ctx, b, s, f, start, len, base);
415                 if (error)
416                         return (error);
417
418                 base += len;
419                 start += len;
420                 remaining -= len;
421         }
422
423         /* Skip the MSI-X table */
424         base += table_size;
425         start += table_size;
426         remaining -= table_size;
427
428         /* Map everything beyond the end of the MSI-X table */
429         if (remaining > 0) {
430                 len = remaining;
431                 error = vm_map_pptdev_mmio(ctx, b, s, f, start, len, base);
432                 if (error)
433                         return (error);
434         }
435
436         return (0);
437 }
438
439 static int
440 cfginitbar(struct vmctx *ctx, struct passthru_softc *sc)
441 {
442         int i, error;
443         struct pci_devinst *pi;
444         struct pci_bar_io bar;
445         enum pcibar_type bartype;
446         uint64_t base, size;
447
448         pi = sc->psc_pi;
449
450         /*
451          * Initialize BAR registers
452          */
453         for (i = 0; i <= PCI_BARMAX; i++) {
454                 bzero(&bar, sizeof(bar));
455                 bar.pbi_sel = sc->psc_sel;
456                 bar.pbi_reg = PCIR_BAR(i);
457
458                 if (ioctl(pcifd, PCIOCGETBAR, &bar) < 0)
459                         continue;
460
461                 if (PCI_BAR_IO(bar.pbi_base)) {
462                         bartype = PCIBAR_IO;
463                         base = bar.pbi_base & PCIM_BAR_IO_BASE;
464                 } else {
465                         switch (bar.pbi_base & PCIM_BAR_MEM_TYPE) {
466                         case PCIM_BAR_MEM_64:
467                                 bartype = PCIBAR_MEM64;
468                                 break;
469                         default:
470                                 bartype = PCIBAR_MEM32;
471                                 break;
472                         }
473                         base = bar.pbi_base & PCIM_BAR_MEM_BASE;
474                 }
475                 size = bar.pbi_length;
476
477                 if (bartype != PCIBAR_IO) {
478                         if (((base | size) & PAGE_MASK) != 0) {
479                                 printf("passthru device %d/%d/%d BAR %d: "
480                                     "base %#lx or size %#lx not page aligned\n",
481                                     sc->psc_sel.pc_bus, sc->psc_sel.pc_dev,
482                                     sc->psc_sel.pc_func, i, base, size);
483                                 return (-1);
484                         }
485                 }
486
487                 /* Cache information about the "real" BAR */
488                 sc->psc_bar[i].type = bartype;
489                 sc->psc_bar[i].size = size;
490                 sc->psc_bar[i].addr = base;
491
492                 /* Allocate the BAR in the guest I/O or MMIO space */
493                 error = pci_emul_alloc_pbar(pi, i, base, bartype, size);
494                 if (error)
495                         return (-1);
496
497                 /* The MSI-X table needs special handling */
498                 if (i == pci_msix_table_bar(pi)) {
499                         error = init_msix_table(ctx, sc, base);
500                         if (error) 
501                                 return (-1);
502                 } else if (bartype != PCIBAR_IO) {
503                         /* Map the physical BAR in the guest MMIO space */
504                         error = vm_map_pptdev_mmio(ctx, sc->psc_sel.pc_bus,
505                                 sc->psc_sel.pc_dev, sc->psc_sel.pc_func,
506                                 pi->pi_bar[i].addr, pi->pi_bar[i].size, base);
507                         if (error)
508                                 return (-1);
509                 }
510
511                 /*
512                  * 64-bit BAR takes up two slots so skip the next one.
513                  */
514                 if (bartype == PCIBAR_MEM64) {
515                         i++;
516                         assert(i <= PCI_BARMAX);
517                         sc->psc_bar[i].type = PCIBAR_MEMHI64;
518                 }
519         }
520         return (0);
521 }
522
523 static int
524 cfginit(struct vmctx *ctx, struct pci_devinst *pi, int bus, int slot, int func)
525 {
526         int error;
527         struct passthru_softc *sc;
528
529         error = 1;
530         sc = pi->pi_arg;
531
532         bzero(&sc->psc_sel, sizeof(struct pcisel));
533         sc->psc_sel.pc_bus = bus;
534         sc->psc_sel.pc_dev = slot;
535         sc->psc_sel.pc_func = func;
536
537         if (cfginitmsi(sc) != 0)
538                 goto done;
539
540         if (cfginitbar(ctx, sc) != 0)
541                 goto done;
542
543         error = 0;                              /* success */
544 done:
545         return (error);
546 }
547
548 static int
549 passthru_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
550 {
551         int bus, slot, func, error;
552         struct passthru_softc *sc;
553
554         sc = NULL;
555         error = 1;
556
557         if (pcifd < 0) {
558                 pcifd = open(_PATH_DEVPCI, O_RDWR, 0);
559                 if (pcifd < 0)
560                         goto done;
561         }
562
563         if (iofd < 0) {
564                 iofd = open(_PATH_DEVIO, O_RDWR, 0);
565                 if (iofd < 0)
566                         goto done;
567         }
568
569         if (opts == NULL ||
570             sscanf(opts, "%d/%d/%d", &bus, &slot, &func) != 3)
571                 goto done;
572
573         if (vm_assign_pptdev(ctx, bus, slot, func) != 0)
574                 goto done;
575
576         sc = calloc(1, sizeof(struct passthru_softc));
577
578         pi->pi_arg = sc;
579         sc->psc_pi = pi;
580
581         /* initialize config space */
582         if ((error = cfginit(ctx, pi, bus, slot, func)) != 0)
583                 goto done;
584         
585         error = 0;              /* success */
586 done:
587         if (error) {
588                 free(sc);
589                 vm_unassign_pptdev(ctx, bus, slot, func);
590         }
591         return (error);
592 }
593
594 static int
595 bar_access(int coff)
596 {
597         if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1))
598                 return (1);
599         else
600                 return (0);
601 }
602
603 static int
604 msicap_access(struct passthru_softc *sc, int coff)
605 {
606         int caplen;
607
608         if (sc->psc_msi.capoff == 0)
609                 return (0);
610
611         caplen = msi_caplen(sc->psc_msi.msgctrl);
612
613         if (coff >= sc->psc_msi.capoff && coff < sc->psc_msi.capoff + caplen)
614                 return (1);
615         else
616                 return (0);
617 }
618
619 static int 
620 msixcap_access(struct passthru_softc *sc, int coff)
621 {
622         if (sc->psc_msix.capoff == 0) 
623                 return (0);
624
625         return (coff >= sc->psc_msix.capoff && 
626                 coff < sc->psc_msix.capoff + MSIX_CAPLEN);
627 }
628
629 static int
630 passthru_cfgread(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
631                  int coff, int bytes, uint32_t *rv)
632 {
633         struct passthru_softc *sc;
634
635         sc = pi->pi_arg;
636
637         /*
638          * PCI BARs and MSI capability is emulated.
639          */
640         if (bar_access(coff) || msicap_access(sc, coff))
641                 return (-1);
642
643 #ifdef LEGACY_SUPPORT
644         /*
645          * Emulate PCIR_CAP_PTR if this device does not support MSI capability
646          * natively.
647          */
648         if (sc->psc_msi.emulated) {
649                 if (coff >= PCIR_CAP_PTR && coff < PCIR_CAP_PTR + 4)
650                         return (-1);
651         }
652 #endif
653
654         /* Everything else just read from the device's config space */
655         *rv = read_config(&sc->psc_sel, coff, bytes);
656
657         return (0);
658 }
659
660 static int
661 passthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
662                   int coff, int bytes, uint32_t val)
663 {
664         int error, msix_table_entries, i;
665         struct passthru_softc *sc;
666
667         sc = pi->pi_arg;
668
669         /*
670          * PCI BARs are emulated
671          */
672         if (bar_access(coff))
673                 return (-1);
674
675         /*
676          * MSI capability is emulated
677          */
678         if (msicap_access(sc, coff)) {
679                 msicap_cfgwrite(pi, sc->psc_msi.capoff, coff, bytes, val);
680
681                 error = vm_setup_pptdev_msi(ctx, vcpu, sc->psc_sel.pc_bus,
682                         sc->psc_sel.pc_dev, sc->psc_sel.pc_func,
683                         pi->pi_msi.addr, pi->pi_msi.msg_data,
684                         pi->pi_msi.maxmsgnum);
685                 if (error != 0) {
686                         printf("vm_setup_pptdev_msi error %d\r\n", errno);
687                         exit(1);
688                 }
689                 return (0);
690         }
691
692         if (msixcap_access(sc, coff)) {
693                 msixcap_cfgwrite(pi, sc->psc_msix.capoff, coff, bytes, val);
694                 if (pi->pi_msix.enabled) {
695                         msix_table_entries = pi->pi_msix.table_count;
696                         for (i = 0; i < msix_table_entries; i++) {
697                                 error = vm_setup_pptdev_msix(ctx, vcpu,
698                                     sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, 
699                                     sc->psc_sel.pc_func, i, 
700                                     pi->pi_msix.table[i].addr,
701                                     pi->pi_msix.table[i].msg_data,
702                                     pi->pi_msix.table[i].vector_control);
703                 
704                                 if (error) {
705                                         printf("vm_setup_pptdev_msix error "
706                                             "%d\r\n", errno);
707                                         exit(1);        
708                                 }
709                         }
710                 }
711                 return (0);
712         }
713
714 #ifdef LEGACY_SUPPORT
715         /*
716          * If this device does not support MSI natively then we cannot let
717          * the guest disable legacy interrupts from the device. It is the
718          * legacy interrupt that is triggering the virtual MSI to the guest.
719          */
720         if (sc->psc_msi.emulated && pci_msi_enabled(pi)) {
721                 if (coff == PCIR_COMMAND && bytes == 2)
722                         val &= ~PCIM_CMD_INTxDIS;
723         }
724 #endif
725
726         write_config(&sc->psc_sel, coff, bytes, val);
727
728         return (0);
729 }
730
731 static void
732 passthru_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
733                uint64_t offset, int size, uint64_t value)
734 {
735         struct passthru_softc *sc;
736         struct iodev_pio_req pio;
737
738         sc = pi->pi_arg;
739
740         if (baridx == pci_msix_table_bar(pi)) {
741                 msix_table_write(ctx, vcpu, sc, offset, size, value);
742         } else {
743                 assert(pi->pi_bar[baridx].type == PCIBAR_IO);
744                 bzero(&pio, sizeof(struct iodev_pio_req));
745                 pio.access = IODEV_PIO_WRITE;
746                 pio.port = sc->psc_bar[baridx].addr + offset;
747                 pio.width = size;
748                 pio.val = value;
749                 
750                 (void)ioctl(iofd, IODEV_PIO, &pio);
751         }
752 }
753
754 static uint64_t
755 passthru_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
756               uint64_t offset, int size)
757 {
758         struct passthru_softc *sc;
759         struct iodev_pio_req pio;
760         uint64_t val;
761
762         sc = pi->pi_arg;
763
764         if (baridx == pci_msix_table_bar(pi)) {
765                 val = msix_table_read(sc, offset, size);
766         } else {
767                 assert(pi->pi_bar[baridx].type == PCIBAR_IO);
768                 bzero(&pio, sizeof(struct iodev_pio_req));
769                 pio.access = IODEV_PIO_READ;
770                 pio.port = sc->psc_bar[baridx].addr + offset;
771                 pio.width = size;
772                 pio.val = 0;
773
774                 (void)ioctl(iofd, IODEV_PIO, &pio);
775
776                 val = pio.val;
777         }
778
779         return (val);
780 }
781
782 struct pci_devemu passthru = {
783         .pe_emu         = "passthru",
784         .pe_init        = passthru_init,
785         .pe_cfgwrite    = passthru_cfgwrite,
786         .pe_cfgread     = passthru_cfgread,
787         .pe_barwrite    = passthru_write,
788         .pe_barread     = passthru_read,
789 };
790 PCI_EMUL_SET(passthru);