]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/pci_passthru.c
Import libyaml as libbsdyml (private brand name)
[FreeBSD/FreeBSD.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
232                 /* Allocate the emulated MSI-X table array */
233                 table_size = pi->pi_msix.table_count * MSIX_TABLE_ENTRY_SIZE;
234                 pi->pi_msix.table = malloc(table_size);
235                 bzero(pi->pi_msix.table, 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
283         index = offset / MSIX_TABLE_ENTRY_SIZE;
284         if (index >= pi->pi_msix.table_count)
285                 return (-1);
286
287         entry = &pi->pi_msix.table[index];
288         entry_offset = offset % MSIX_TABLE_ENTRY_SIZE;
289
290         switch(size) {
291         case 1:
292                 src8 = (uint8_t *)((void *)entry + entry_offset);
293                 data = *src8;
294                 break;
295         case 2:
296                 src16 = (uint16_t *)((void *)entry + entry_offset);
297                 data = *src16;
298                 break;
299         case 4:
300                 src32 = (uint32_t *)((void *)entry + entry_offset);
301                 data = *src32;
302                 break;
303         case 8:
304                 src64 = (uint64_t *)((void *)entry + entry_offset);
305                 data = *src64;
306                 break;
307         default:
308                 return (-1);
309         }
310
311         return (data);
312 }
313
314 static void
315 msix_table_write(struct vmctx *ctx, int vcpu, struct passthru_softc *sc,
316                  uint64_t offset, int size, uint64_t data)
317 {
318         struct pci_devinst *pi;
319         struct msix_table_entry *entry;
320         uint32_t *dest;
321         size_t entry_offset;
322         uint32_t vector_control;
323         int error, index;
324
325         pi = sc->psc_pi;
326         index = offset / MSIX_TABLE_ENTRY_SIZE;
327         if (index >= pi->pi_msix.table_count)
328                 return;
329
330         entry = &pi->pi_msix.table[index];
331         entry_offset = offset % MSIX_TABLE_ENTRY_SIZE;
332
333         /* Only 4 byte naturally-aligned writes are supported */
334         assert(size == 4);
335         assert(entry_offset % 4 == 0);
336
337         vector_control = entry->vector_control;
338         dest = (uint32_t *)((void *)entry + entry_offset);
339         *dest = data;
340         /* If MSI-X hasn't been enabled, do nothing */
341         if (pi->pi_msix.enabled) {
342                 /* If the entry is masked, don't set it up */
343                 if ((entry->vector_control & PCIM_MSIX_VCTRL_MASK) == 0 ||
344                     (vector_control & PCIM_MSIX_VCTRL_MASK) == 0) {
345                         error = vm_setup_msix(ctx, vcpu, sc->psc_sel.pc_bus,
346                                               sc->psc_sel.pc_dev, 
347                                               sc->psc_sel.pc_func,
348                                               index, entry->msg_data, 
349                                               entry->vector_control,
350                                               entry->addr);
351                 }
352         }
353 }
354
355 static int
356 init_msix_table(struct vmctx *ctx, struct passthru_softc *sc, uint64_t base)
357 {
358         int b, s, f;
359         int error, idx;
360         size_t len, remaining, table_size;
361         vm_paddr_t start;
362         struct pci_devinst *pi = sc->psc_pi;
363
364         assert(pci_msix_table_bar(pi) >= 0 && pci_msix_pba_bar(pi) >= 0);
365
366         b = sc->psc_sel.pc_bus;
367         s = sc->psc_sel.pc_dev;
368         f = sc->psc_sel.pc_func;
369
370         /* 
371          * If the MSI-X table BAR maps memory intended for
372          * other uses, it is at least assured that the table 
373          * either resides in its own page within the region, 
374          * or it resides in a page shared with only the PBA.
375          */
376         if (pi->pi_msix.pba_bar == pi->pi_msix.table_bar && 
377             ((pi->pi_msix.pba_offset - pi->pi_msix.table_offset) < 4096)) {
378                 /* Need to also emulate the PBA, not supported yet */
379                 printf("Unsupported MSI-X configuration: %d/%d/%d\n", b, s, f);
380                 return (-1);
381         }
382
383         /* Compute the MSI-X table size */
384         table_size = pi->pi_msix.table_count * MSIX_TABLE_ENTRY_SIZE;
385         table_size = roundup2(table_size, 4096);
386
387         idx = pi->pi_msix.table_bar;
388         start = pi->pi_bar[idx].addr;
389         remaining = pi->pi_bar[idx].size;
390
391         /* Map everything before the MSI-X table */
392         if (pi->pi_msix.table_offset > 0) {
393                 len = pi->pi_msix.table_offset;
394                 error = vm_map_pptdev_mmio(ctx, b, s, f, start, len, base);
395                 if (error)
396                         return (error);
397
398                 base += len;
399                 start += len;
400                 remaining -= len;
401         }
402
403         /* Skip the MSI-X table */
404         base += table_size;
405         start += table_size;
406         remaining -= table_size;
407
408         /* Map everything beyond the end of the MSI-X table */
409         if (remaining > 0) {
410                 len = remaining;
411                 error = vm_map_pptdev_mmio(ctx, b, s, f, start, len, base);
412                 if (error)
413                         return (error);
414         }
415
416         return (0);
417 }
418
419 static int
420 cfginitbar(struct vmctx *ctx, struct passthru_softc *sc)
421 {
422         int i, error;
423         struct pci_devinst *pi;
424         struct pci_bar_io bar;
425         enum pcibar_type bartype;
426         uint64_t base;
427
428         pi = sc->psc_pi;
429
430         /*
431          * Initialize BAR registers
432          */
433         for (i = 0; i <= PCI_BARMAX; i++) {
434                 bzero(&bar, sizeof(bar));
435                 bar.pbi_sel = sc->psc_sel;
436                 bar.pbi_reg = PCIR_BAR(i);
437
438                 if (ioctl(pcifd, PCIOCGETBAR, &bar) < 0)
439                         continue;
440
441                 if (PCI_BAR_IO(bar.pbi_base)) {
442                         bartype = PCIBAR_IO;
443                         base = bar.pbi_base & PCIM_BAR_IO_BASE;
444                 } else {
445                         switch (bar.pbi_base & PCIM_BAR_MEM_TYPE) {
446                         case PCIM_BAR_MEM_64:
447                                 bartype = PCIBAR_MEM64;
448                                 break;
449                         default:
450                                 bartype = PCIBAR_MEM32;
451                                 break;
452                         }
453                         base = bar.pbi_base & PCIM_BAR_MEM_BASE;
454                 }
455
456                 /* Cache information about the "real" BAR */
457                 sc->psc_bar[i].type = bartype;
458                 sc->psc_bar[i].size = bar.pbi_length;
459                 sc->psc_bar[i].addr = base;
460
461                 /* Allocate the BAR in the guest I/O or MMIO space */
462                 error = pci_emul_alloc_pbar(pi, i, base, bartype,
463                                             bar.pbi_length);
464                 if (error)
465                         return (-1);
466
467                 /* The MSI-X table needs special handling */
468                 if (i == pci_msix_table_bar(pi)) {
469                         error = init_msix_table(ctx, sc, base);
470                         if (error) 
471                                 return (-1);
472                 } else if (bartype != PCIBAR_IO) {
473                         /* Map the physical MMIO space in the guest MMIO space */
474                         error = vm_map_pptdev_mmio(ctx, sc->psc_sel.pc_bus,
475                                 sc->psc_sel.pc_dev, sc->psc_sel.pc_func,
476                                 pi->pi_bar[i].addr, pi->pi_bar[i].size, base);
477                         if (error)
478                                 return (-1);
479                 }
480
481                 /*
482                  * 64-bit BAR takes up two slots so skip the next one.
483                  */
484                 if (bartype == PCIBAR_MEM64) {
485                         i++;
486                         assert(i <= PCI_BARMAX);
487                         sc->psc_bar[i].type = PCIBAR_MEMHI64;
488                 }
489         }
490         return (0);
491 }
492
493 static int
494 cfginit(struct vmctx *ctx, struct pci_devinst *pi, int bus, int slot, int func)
495 {
496         int error;
497         struct passthru_softc *sc;
498
499         error = 1;
500         sc = pi->pi_arg;
501
502         bzero(&sc->psc_sel, sizeof(struct pcisel));
503         sc->psc_sel.pc_bus = bus;
504         sc->psc_sel.pc_dev = slot;
505         sc->psc_sel.pc_func = func;
506
507         if (cfginitmsi(sc) != 0)
508                 goto done;
509
510         if (cfginitbar(ctx, sc) != 0)
511                 goto done;
512
513         error = 0;                              /* success */
514 done:
515         return (error);
516 }
517
518 static int
519 passthru_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
520 {
521         int bus, slot, func, error;
522         struct passthru_softc *sc;
523
524         sc = NULL;
525         error = 1;
526
527         if (pcifd < 0) {
528                 pcifd = open(_PATH_DEVPCI, O_RDWR, 0);
529                 if (pcifd < 0)
530                         goto done;
531         }
532
533         if (iofd < 0) {
534                 iofd = open(_PATH_DEVIO, O_RDWR, 0);
535                 if (iofd < 0)
536                         goto done;
537         }
538
539         if (opts == NULL ||
540             sscanf(opts, "%d/%d/%d", &bus, &slot, &func) != 3)
541                 goto done;
542
543         if (vm_assign_pptdev(ctx, bus, slot, func) != 0)
544                 goto done;
545
546         sc = malloc(sizeof(struct passthru_softc));
547         memset(sc, 0, sizeof(struct passthru_softc));
548
549         pi->pi_arg = sc;
550         sc->psc_pi = pi;
551
552         /* initialize config space */
553         if ((error = cfginit(ctx, pi, bus, slot, func)) != 0)
554                 goto done;
555         
556         error = 0;              /* success */
557 done:
558         if (error) {
559                 free(sc);
560                 vm_unassign_pptdev(ctx, bus, slot, func);
561         }
562         return (error);
563 }
564
565 static int
566 bar_access(int coff)
567 {
568         if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1))
569                 return (1);
570         else
571                 return (0);
572 }
573
574 static int
575 msicap_access(struct passthru_softc *sc, int coff)
576 {
577         int caplen;
578
579         if (sc->psc_msi.capoff == 0)
580                 return (0);
581
582         caplen = msi_caplen(sc->psc_msi.msgctrl);
583
584         if (coff >= sc->psc_msi.capoff && coff < sc->psc_msi.capoff + caplen)
585                 return (1);
586         else
587                 return (0);
588 }
589
590 static int 
591 msixcap_access(struct passthru_softc *sc, int coff)
592 {
593         if (sc->psc_msix.capoff == 0) 
594                 return (0);
595
596         return (coff >= sc->psc_msix.capoff && 
597                 coff < sc->psc_msix.capoff + MSIX_CAPLEN);
598 }
599
600 static int
601 passthru_cfgread(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
602                  int coff, int bytes, uint32_t *rv)
603 {
604         struct passthru_softc *sc;
605
606         sc = pi->pi_arg;
607
608         /*
609          * PCI BARs and MSI capability is emulated.
610          */
611         if (bar_access(coff) || msicap_access(sc, coff))
612                 return (-1);
613
614 #ifdef LEGACY_SUPPORT
615         /*
616          * Emulate PCIR_CAP_PTR if this device does not support MSI capability
617          * natively.
618          */
619         if (sc->psc_msi.emulated) {
620                 if (coff >= PCIR_CAP_PTR && coff < PCIR_CAP_PTR + 4)
621                         return (-1);
622         }
623 #endif
624
625         /* Everything else just read from the device's config space */
626         *rv = read_config(&sc->psc_sel, coff, bytes);
627
628         return (0);
629 }
630
631 static int
632 passthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
633                   int coff, int bytes, uint32_t val)
634 {
635         int error, msix_table_entries, i;
636         struct passthru_softc *sc;
637
638         sc = pi->pi_arg;
639
640         /*
641          * PCI BARs are emulated
642          */
643         if (bar_access(coff))
644                 return (-1);
645
646         /*
647          * MSI capability is emulated
648          */
649         if (msicap_access(sc, coff)) {
650                 msicap_cfgwrite(pi, sc->psc_msi.capoff, coff, bytes, val);
651
652                 error = vm_setup_msi(ctx, vcpu, sc->psc_sel.pc_bus,
653                         sc->psc_sel.pc_dev, sc->psc_sel.pc_func, pi->pi_msi.cpu,
654                         pi->pi_msi.vector, pi->pi_msi.msgnum);
655                 if (error != 0) {
656                         printf("vm_setup_msi returned error %d\r\n", errno);
657                         exit(1);
658                 }
659                 return (0);
660         }
661
662         if (msixcap_access(sc, coff)) {
663                 msixcap_cfgwrite(pi, sc->psc_msix.capoff, coff, bytes, val);
664                 if (pi->pi_msix.enabled) {
665                         msix_table_entries = pi->pi_msix.table_count;
666                         for (i = 0; i < msix_table_entries; i++) {
667                                 error = vm_setup_msix(ctx, vcpu, sc->psc_sel.pc_bus,
668                                                       sc->psc_sel.pc_dev, 
669                                                       sc->psc_sel.pc_func, i, 
670                                                       pi->pi_msix.table[i].msg_data,
671                                                       pi->pi_msix.table[i].vector_control,
672                                                       pi->pi_msix.table[i].addr);
673                 
674                                 if (error) {
675                                         printf("vm_setup_msix returned error %d\r\n", errno);
676                                         exit(1);        
677                                 }
678                         }
679                 }
680                 return (0);
681         }
682
683 #ifdef LEGACY_SUPPORT
684         /*
685          * If this device does not support MSI natively then we cannot let
686          * the guest disable legacy interrupts from the device. It is the
687          * legacy interrupt that is triggering the virtual MSI to the guest.
688          */
689         if (sc->psc_msi.emulated && pci_msi_enabled(pi)) {
690                 if (coff == PCIR_COMMAND && bytes == 2)
691                         val &= ~PCIM_CMD_INTxDIS;
692         }
693 #endif
694
695         write_config(&sc->psc_sel, coff, bytes, val);
696
697         return (0);
698 }
699
700 static void
701 passthru_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
702                uint64_t offset, int size, uint64_t value)
703 {
704         struct passthru_softc *sc;
705         struct iodev_pio_req pio;
706
707         sc = pi->pi_arg;
708
709         if (baridx == pci_msix_table_bar(pi)) {
710                 msix_table_write(ctx, vcpu, sc, offset, size, value);
711         } else {
712                 assert(pi->pi_bar[baridx].type == PCIBAR_IO);
713                 bzero(&pio, sizeof(struct iodev_pio_req));
714                 pio.access = IODEV_PIO_WRITE;
715                 pio.port = sc->psc_bar[baridx].addr + offset;
716                 pio.width = size;
717                 pio.val = value;
718                 
719                 (void)ioctl(iofd, IODEV_PIO, &pio);
720         }
721 }
722
723 static uint64_t
724 passthru_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
725               uint64_t offset, int size)
726 {
727         struct passthru_softc *sc;
728         struct iodev_pio_req pio;
729         uint64_t val;
730
731         sc = pi->pi_arg;
732
733         if (baridx == pci_msix_table_bar(pi)) {
734                 val = msix_table_read(sc, offset, size);
735         } else {
736                 assert(pi->pi_bar[baridx].type == PCIBAR_IO);
737                 bzero(&pio, sizeof(struct iodev_pio_req));
738                 pio.access = IODEV_PIO_READ;
739                 pio.port = sc->psc_bar[baridx].addr + offset;
740                 pio.width = size;
741                 pio.val = 0;
742
743                 (void)ioctl(iofd, IODEV_PIO, &pio);
744
745                 val = pio.val;
746         }
747
748         return (val);
749 }
750
751 struct pci_devemu passthru = {
752         .pe_emu         = "passthru",
753         .pe_init        = passthru_init,
754         .pe_cfgwrite    = passthru_cfgwrite,
755         .pe_cfgread     = passthru_cfgread,
756         .pe_barwrite    = passthru_write,
757         .pe_barread     = passthru_read,
758 };
759 PCI_EMUL_SET(passthru);