summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--exception_lists/copyright28
-rw-r--r--exception_lists/cstyle26
-rw-r--r--exception_lists/hdrchk2
-rw-r--r--usr/contrib/freebsd/dev/io/iodev.h44
-rw-r--r--usr/contrib/freebsd/dev/mii/mii.h239
-rw-r--r--usr/contrib/freebsd/dev/usb/controller/xhcireg.h224
-rw-r--r--usr/contrib/freebsd/dev/usb/usb.h801
-rw-r--r--usr/contrib/freebsd/dev/usb/usb_endian.h121
-rw-r--r--usr/contrib/freebsd/dev/usb/usb_freebsd.h101
-rw-r--r--usr/contrib/freebsd/dev/usb/usbdi.h657
-rw-r--r--usr/contrib/freebsd/sys/ata.h392
-rw-r--r--usr/contrib/freebsd/sys/pciio.h146
-rw-r--r--usr/src/cmd/Makefile2
-rw-r--r--usr/src/cmd/bhyve/Makefile.com35
-rw-r--r--usr/src/cmd/bhyve/acpi.c1012
-rw-r--r--usr/src/cmd/bhyve/acpi.h2
-rw-r--r--usr/src/cmd/bhyve/ahci.h24
-rw-r--r--usr/src/cmd/bhyve/atkbdc.c220
-rw-r--r--usr/src/cmd/bhyve/atkbdc.h2
-rw-r--r--usr/src/cmd/bhyve/bhyve_sol_glue.c50
-rw-r--r--usr/src/cmd/bhyve/bhyvegc.c33
-rw-r--r--usr/src/cmd/bhyve/bhyvegc.h4
-rw-r--r--usr/src/cmd/bhyve/bhyverun.c466
-rw-r--r--usr/src/cmd/bhyve/bhyverun.h8
-rw-r--r--usr/src/cmd/bhyve/block_if.c425
-rw-r--r--usr/src/cmd/bhyve/block_if.h10
-rw-r--r--usr/src/cmd/bhyve/bootrom.c111
-rw-r--r--usr/src/cmd/bhyve/bootrom.h38
-rw-r--r--usr/src/cmd/bhyve/console.c39
-rw-r--r--usr/src/cmd/bhyve/console.h9
-rw-r--r--usr/src/cmd/bhyve/consport.c21
-rw-r--r--usr/src/cmd/bhyve/dbgport.c175
-rw-r--r--usr/src/cmd/bhyve/dbgport.h2
-rw-r--r--usr/src/cmd/bhyve/fwctl.c549
-rw-r--r--usr/src/cmd/bhyve/fwctl.h54
-rw-r--r--usr/src/cmd/bhyve/inout.c12
-rw-r--r--usr/src/cmd/bhyve/inout.h2
-rw-r--r--usr/src/cmd/bhyve/ioapic.c4
-rw-r--r--usr/src/cmd/bhyve/ioapic.h4
-rw-r--r--usr/src/cmd/bhyve/mem.c4
-rw-r--r--usr/src/cmd/bhyve/mem.h2
-rw-r--r--usr/src/cmd/bhyve/mevent.c478
-rw-r--r--usr/src/cmd/bhyve/mevent.h51
-rw-r--r--usr/src/cmd/bhyve/mevent_test.c256
-rw-r--r--usr/src/cmd/bhyve/mptbl.c4
-rw-r--r--usr/src/cmd/bhyve/mptbl.h2
-rw-r--r--usr/src/cmd/bhyve/pci_ahci.c1040
-rw-r--r--usr/src/cmd/bhyve/pci_e82545.c2413
-rw-r--r--usr/src/cmd/bhyve/pci_emul.c152
-rw-r--r--usr/src/cmd/bhyve/pci_emul.h9
-rw-r--r--usr/src/cmd/bhyve/pci_fbuf.c424
-rw-r--r--usr/src/cmd/bhyve/pci_hostbridge.c4
-rw-r--r--usr/src/cmd/bhyve/pci_irq.c21
-rw-r--r--usr/src/cmd/bhyve/pci_irq.h4
-rw-r--r--usr/src/cmd/bhyve/pci_lpc.c35
-rw-r--r--usr/src/cmd/bhyve/pci_lpc.h3
-rw-r--r--usr/src/cmd/bhyve/pci_passthru.c932
-rw-r--r--usr/src/cmd/bhyve/pci_uart.c119
-rw-r--r--usr/src/cmd/bhyve/pci_virtio_block.c222
-rw-r--r--usr/src/cmd/bhyve/pci_virtio_net.c669
-rw-r--r--usr/src/cmd/bhyve/pci_virtio_rnd.c203
-rw-r--r--usr/src/cmd/bhyve/pci_virtio_viona.c2
-rw-r--r--usr/src/cmd/bhyve/pci_xhci.c2834
-rw-r--r--usr/src/cmd/bhyve/pci_xhci.h353
-rw-r--r--usr/src/cmd/bhyve/pm.c16
-rw-r--r--usr/src/cmd/bhyve/pmtmr.c217
-rw-r--r--usr/src/cmd/bhyve/post.c4
-rw-r--r--usr/src/cmd/bhyve/ps2kbd.c166
-rw-r--r--usr/src/cmd/bhyve/ps2mouse.c77
-rw-r--r--usr/src/cmd/bhyve/ps2mouse.h4
-rw-r--r--usr/src/cmd/bhyve/rfb.c683
-rw-r--r--usr/src/cmd/bhyve/rfb.h2
-rw-r--r--usr/src/cmd/bhyve/rtc.c313
-rw-r--r--usr/src/cmd/bhyve/rtc.h4
-rw-r--r--usr/src/cmd/bhyve/smbiostbl.c6
-rw-r--r--usr/src/cmd/bhyve/smbiostbl.h2
-rw-r--r--usr/src/cmd/bhyve/sockstream.c86
-rw-r--r--usr/src/cmd/bhyve/sockstream.h33
-rw-r--r--usr/src/cmd/bhyve/spinup_ap.c4
-rw-r--r--usr/src/cmd/bhyve/spinup_ap.h2
-rw-r--r--usr/src/cmd/bhyve/task_switch.c939
-rw-r--r--usr/src/cmd/bhyve/uart_emul.c364
-rw-r--r--usr/src/cmd/bhyve/uart_emul.h2
-rw-r--r--usr/src/cmd/bhyve/usb_emul.c76
-rw-r--r--usr/src/cmd/bhyve/usb_emul.h156
-rw-r--r--usr/src/cmd/bhyve/usb_mouse.c800
-rw-r--r--usr/src/cmd/bhyve/vga.c94
-rw-r--r--usr/src/cmd/bhyve/vga.h2
-rw-r--r--usr/src/cmd/bhyve/virtio.c48
-rw-r--r--usr/src/cmd/bhyve/virtio.h25
-rw-r--r--usr/src/cmd/bhyve/xmsr.c4
-rw-r--r--usr/src/cmd/bhyve/xmsr.h2
-rw-r--r--usr/src/cmd/bhyveload-uefi/Makefile41
-rw-r--r--usr/src/cmd/bhyveload-uefi/Makefile.com51
-rw-r--r--usr/src/cmd/bhyveload-uefi/amd64/Makefile21
-rw-r--r--usr/src/cmd/bhyveload-uefi/bhyveload-uefi.c191
-rw-r--r--usr/src/cmd/bhyveload-uefi/i386/Makefile18
-rw-r--r--usr/src/cmd/mdb/intel/amd64/Makefile3
-rw-r--r--usr/src/compat/freebsd/amd64/machine/atomic.h3
-rw-r--r--usr/src/compat/freebsd/amd64/machine/iodev.h19
-rw-r--r--usr/src/compat/freebsd/net/ethernet.h14
-rw-r--r--usr/src/compat/freebsd/sys/cdefs.h42
-rw-r--r--usr/src/compat/freebsd/sys/limits.h5
-rw-r--r--usr/src/compat/freebsd/sys/param.h3
-rw-r--r--usr/src/compat/freebsd/sys/socket.h23
-rw-r--r--usr/src/compat/freebsd/unistd.h23
106 files changed, 18652 insertions, 2266 deletions
diff --git a/exception_lists/copyright b/exception_lists/copyright
index e2832bfc2c..e5ca6632c4 100644
--- a/exception_lists/copyright
+++ b/exception_lists/copyright
@@ -486,26 +486,36 @@ usr/src/uts/common/sys/scsi/adapters/mpt_sas/mpi/*
usr/src/uts/sparc/nsmb/ioc_check.ref
# bhyve sources
-usr/src/cmd/bhyve/acpi.h
+usr/src/cmd/bhyve/acpi.[ch]
usr/src/cmd/bhyve/ahci.h
usr/src/cmd/bhyve/atkbdc.[ch]
usr/src/cmd/bhyve/bhyvegc.[ch]
usr/src/cmd/bhyve/bhyverun.[ch]
usr/src/cmd/bhyve/block_if.[ch]
+usr/src/cmd/bhyve/bootrom.[ch]
usr/src/cmd/bhyve/console.[ch]
usr/src/cmd/bhyve/consport.c
-usr/src/cmd/bhyve/dbgport.h
+usr/src/cmd/bhyve/dbgport.[ch]
+usr/src/cmd/bhyve/fwctl.[ch]
usr/src/cmd/bhyve/inout.[ch]
usr/src/cmd/bhyve/ioapic.[ch]
usr/src/cmd/bhyve/mem.[ch]
+usr/src/cmd/bhyve/mevent.[ch]
+usr/src/cmd/bhyve/mevent_test.c
usr/src/cmd/bhyve/mptbl.[ch]
usr/src/cmd/bhyve/pci_ahci.c
+usr/src/cmd/bhyve/pci_e82545.c
usr/src/cmd/bhyve/pci_emul.[ch]
+usr/src/cmd/bhyve/pci_fbuf.c
usr/src/cmd/bhyve/pci_hostbridge.c
usr/src/cmd/bhyve/pci_irq.[ch]
usr/src/cmd/bhyve/pci_lpc.[ch]
+usr/src/cmd/bhyve/pci_passthru.c
+usr/src/cmd/bhyve/pci_uart.c
usr/src/cmd/bhyve/pci_virtio_block.c
usr/src/cmd/bhyve/pci_virtio_net.c
+usr/src/cmd/bhyve/pci_virtio_rnd.c
+usr/src/cmd/bhyve/pci_xhci.[ch]
usr/src/cmd/bhyve/pm.c
usr/src/cmd/bhyve/pmtmr.c
usr/src/cmd/bhyve/post.c
@@ -514,8 +524,12 @@ usr/src/cmd/bhyve/ps2mouse.[ch]
usr/src/cmd/bhyve/rfb.[ch]
usr/src/cmd/bhyve/rtc.[ch]
usr/src/cmd/bhyve/smbiostbl.[ch]
+usr/src/cmd/bhyve/sockstream.[ch]
usr/src/cmd/bhyve/spinup_ap.[ch]
+usr/src/cmd/bhyve/task_switch.c
usr/src/cmd/bhyve/uart_emul.[ch]
+usr/src/cmd/bhyve/usb_emul.[ch]
+usr/src/cmd/bhyve/usb_mouse.c
usr/src/cmd/bhyve/vga.[ch]
usr/src/cmd/bhyve/virtio.[ch]
usr/src/cmd/bhyve/xmsr.[ch]
@@ -525,11 +539,13 @@ usr/src/compat/freebsd/*/*.h
usr/src/compat/freebsd/amd64/machine/*.h
usr/contrib/freebsd/*/*.h
usr/contrib/freebsd/*/*/*.h
-usr/contrib/freebsd/lib/libutil/expand_number.c
+usr/contrib/freebsd/lib/libutil/*.c
usr/src/head/bhyve.h
usr/src/lib/libvmmapi/common/vmmapi.[ch]
-usr/src/uts/i86pc/io/vmm/amd/*.c
+usr/src/tools/scripts/gensetdefs.pl
+usr/src/uts/i86pc/io/vmm/amd/*.[chs]
usr/src/uts/i86pc/io/vmm/intel/*.[chs]
+usr/src/uts/i86pc/io/vmm/intel/offsets.in
usr/src/uts/i86pc/io/vmm/io/*.[ch]
usr/src/uts/i86pc/io/vmm/vmm.c
usr/src/uts/i86pc/io/vmm/vmm_host.[ch]
@@ -538,11 +554,11 @@ usr/src/uts/i86pc/io/vmm/vmm_ioport.[ch]
usr/src/uts/i86pc/io/vmm/vmm_ipi.h
usr/src/uts/i86pc/io/vmm/vmm_ktr.h
usr/src/uts/i86pc/io/vmm/vmm_lapic.[ch]
-usr/src/uts/i86pc/io/vmm/vmm_mem.h
+usr/src/uts/i86pc/io/vmm/vmm_mem.[ch]
usr/src/uts/i86pc/io/vmm/vmm_sol_dev.c
usr/src/uts/i86pc/io/vmm/vmm_sol_glue.c
usr/src/uts/i86pc/io/vmm/vmm_sol_mem.c
-usr/src/uts/i86pc/io/vmm/vmm_stat.h
+usr/src/uts/i86pc/io/vmm/vmm_stat.[ch]
usr/src/uts/i86pc/io/vmm/vmm_util.[ch]
usr/src/uts/i86pc/io/vmm/vmx_assym.s
usr/src/uts/i86pc/io/vmm/x86.[ch]
diff --git a/exception_lists/cstyle b/exception_lists/cstyle
index 60712bf30e..c16a9d0bde 100644
--- a/exception_lists/cstyle
+++ b/exception_lists/cstyle
@@ -1390,26 +1390,36 @@ usr/src/uts/intel/sys/acpi/platform/acwin64.h
# bhyve sources
syntax: glob
-usr/src/cmd/bhyve/acpi.h
+usr/src/cmd/bhyve/acpi.[ch]
usr/src/cmd/bhyve/ahci.h
usr/src/cmd/bhyve/atkbdc.[ch]
usr/src/cmd/bhyve/bhyvegc.[ch]
usr/src/cmd/bhyve/bhyverun.[ch]
usr/src/cmd/bhyve/block_if.[ch]
+usr/src/cmd/bhyve/bootrom.[ch]
usr/src/cmd/bhyve/console.[ch]
usr/src/cmd/bhyve/consport.c
-usr/src/cmd/bhyve/dbgport.h
+usr/src/cmd/bhyve/dbgport.[ch]
+usr/src/cmd/bhyve/fwctl.[ch]
usr/src/cmd/bhyve/inout.[ch]
usr/src/cmd/bhyve/ioapic.[ch]
usr/src/cmd/bhyve/mem.[ch]
+usr/src/cmd/bhyve/mevent.[ch]
+usr/src/cmd/bhyve/mevent_test.c
usr/src/cmd/bhyve/mptbl.[ch]
usr/src/cmd/bhyve/pci_ahci.c
+usr/src/cmd/bhyve/pci_e82545.c
usr/src/cmd/bhyve/pci_emul.[ch]
+usr/src/cmd/bhyve/pci_fbuf.c
usr/src/cmd/bhyve/pci_hostbridge.c
usr/src/cmd/bhyve/pci_irq.[ch]
usr/src/cmd/bhyve/pci_lpc.[ch]
+usr/src/cmd/bhyve/pci_passthru.c
+usr/src/cmd/bhyve/pci_uart.c
usr/src/cmd/bhyve/pci_virtio_block.c
usr/src/cmd/bhyve/pci_virtio_net.c
+usr/src/cmd/bhyve/pci_virtio_rnd.c
+usr/src/cmd/bhyve/pci_xhci.[ch]
usr/src/cmd/bhyve/pm.c
usr/src/cmd/bhyve/pmtmr.c
usr/src/cmd/bhyve/post.c
@@ -1418,8 +1428,12 @@ usr/src/cmd/bhyve/ps2mouse.[ch]
usr/src/cmd/bhyve/rfb.[ch]
usr/src/cmd/bhyve/rtc.[ch]
usr/src/cmd/bhyve/smbiostbl.[ch]
+usr/src/cmd/bhyve/sockstream.[ch]
usr/src/cmd/bhyve/spinup_ap.[ch]
+usr/src/cmd/bhyve/task_switch.c
usr/src/cmd/bhyve/uart_emul.[ch]
+usr/src/cmd/bhyve/usb_emul.[ch]
+usr/src/cmd/bhyve/usb_mouse.c
usr/src/cmd/bhyve/vga.[ch]
usr/src/cmd/bhyve/virtio.[ch]
usr/src/cmd/bhyve/xmsr.[ch]
@@ -1430,10 +1444,10 @@ usr/src/compat/freebsd/*/*.h
usr/src/compat/freebsd/amd64/machine/*.h
usr/contrib/freebsd/*/*.h
usr/contrib/freebsd/*/*/*.h
-usr/contrib/freebsd/lib/libutil/expand_number.c
+usr/contrib/freebsd/lib/libutil/*.c
usr/src/head/bhyve.h
usr/src/lib/libvmmapi/common/vmmapi.[ch]
-usr/src/uts/i86pc/io/vmm/amd/*.c
+usr/src/uts/i86pc/io/vmm/amd/*.[ch]
usr/src/uts/i86pc/io/vmm/intel/*.[chs]
usr/src/uts/i86pc/io/vmm/io/*.[ch]
usr/src/uts/i86pc/io/vmm/vmm.c
@@ -1443,9 +1457,9 @@ usr/src/uts/i86pc/io/vmm/vmm_ioport.[ch]
usr/src/uts/i86pc/io/vmm/vmm_ipi.h
usr/src/uts/i86pc/io/vmm/vmm_ktr.h
usr/src/uts/i86pc/io/vmm/vmm_lapic.[ch]
-usr/src/uts/i86pc/io/vmm/vmm_mem.h
+usr/src/uts/i86pc/io/vmm/vmm_mem.[ch]
usr/src/uts/i86pc/io/vmm/vmm_sol_glue.c
-usr/src/uts/i86pc/io/vmm/vmm_stat.h
+usr/src/uts/i86pc/io/vmm/vmm_stat.[ch]
usr/src/uts/i86pc/io/vmm/vmm_util.[ch]
usr/src/uts/i86pc/io/vmm/vmx_assym.s
usr/src/uts/i86pc/io/vmm/x86.[ch]
diff --git a/exception_lists/hdrchk b/exception_lists/hdrchk
index 43ed5c5a10..fdd8307f32 100644
--- a/exception_lists/hdrchk
+++ b/exception_lists/hdrchk
@@ -405,6 +405,7 @@ usr/src/cmd/bhyve/atkbdc.h
usr/src/cmd/bhyve/bhyvegc.h
usr/src/cmd/bhyve/bhyverun.h
usr/src/cmd/bhyve/block_if.h
+usr/src/cmd/bhyve/bootrom.h
usr/src/cmd/bhyve/console.h
usr/src/cmd/bhyve/dbgport.h
usr/src/cmd/bhyve/inout.h
@@ -419,6 +420,7 @@ usr/src/cmd/bhyve/ps2mouse.h
usr/src/cmd/bhyve/rfb.h
usr/src/cmd/bhyve/rtc.h
usr/src/cmd/bhyve/smbiostbl.h
+usr/src/cmd/bhyve/sockstream.h
usr/src/cmd/bhyve/spinup_ap.h
usr/src/cmd/bhyve/uart_emul.h
usr/src/cmd/bhyve/vga.h
diff --git a/usr/contrib/freebsd/dev/io/iodev.h b/usr/contrib/freebsd/dev/io/iodev.h
new file mode 100644
index 0000000000..d040fcccf4
--- /dev/null
+++ b/usr/contrib/freebsd/dev/io/iodev.h
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 2010 Marcel Moolenaar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DEV_IODEV_H_
+#define _DEV_IODEV_H_
+
+#define IODEV_PIO_READ 0
+#define IODEV_PIO_WRITE 1
+
+struct iodev_pio_req {
+ u_int access;
+ u_int port;
+ u_int width;
+ u_int val;
+};
+
+#define IODEV_PIO _IOWR('I', 0, struct iodev_pio_req)
+
+#endif /* _DEV_IODEV_H_ */
diff --git a/usr/contrib/freebsd/dev/mii/mii.h b/usr/contrib/freebsd/dev/mii/mii.h
new file mode 100644
index 0000000000..fa1ec84eaa
--- /dev/null
+++ b/usr/contrib/freebsd/dev/mii/mii.h
@@ -0,0 +1,239 @@
+/* $NetBSD: mii.h,v 1.18 2014/06/16 14:43:22 msaitoh Exp $ */
+
+/*-
+ * Copyright (c) 1997 Manuel Bouyer. All rights reserved.
+ *
+ * Modification to match BSD/OS 3.0 MII interface by Jason R. Thorpe,
+ * Numerical Aerospace Simulation Facility, NASA Ames Research Center.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DEV_MII_MII_H_
+#define _DEV_MII_MII_H_
+
+/*
+ * Registers common to all PHYs.
+ */
+
+#define MII_NPHY 32 /* max # of PHYs per MII */
+
+/*
+ * MII commands, used if a device must drive the MII lines
+ * manually.
+ */
+#define MII_COMMAND_START 0x01
+#define MII_COMMAND_READ 0x02
+#define MII_COMMAND_WRITE 0x01
+#define MII_COMMAND_ACK 0x02
+
+#define MII_BMCR 0x00 /* Basic mode control register (rw) */
+#define BMCR_RESET 0x8000 /* reset */
+#define BMCR_LOOP 0x4000 /* loopback */
+#define BMCR_SPEED0 0x2000 /* speed selection (LSB) */
+#define BMCR_AUTOEN 0x1000 /* autonegotiation enable */
+#define BMCR_PDOWN 0x0800 /* power down */
+#define BMCR_ISO 0x0400 /* isolate */
+#define BMCR_STARTNEG 0x0200 /* restart autonegotiation */
+#define BMCR_FDX 0x0100 /* Set duplex mode */
+#define BMCR_CTEST 0x0080 /* collision test */
+#define BMCR_SPEED1 0x0040 /* speed selection (MSB) */
+
+#define BMCR_S10 0x0000 /* 10 Mb/s */
+#define BMCR_S100 BMCR_SPEED0 /* 100 Mb/s */
+#define BMCR_S1000 BMCR_SPEED1 /* 1000 Mb/s */
+
+#define BMCR_SPEED(x) ((x) & (BMCR_SPEED0|BMCR_SPEED1))
+
+#define MII_BMSR 0x01 /* Basic mode status register (ro) */
+#define BMSR_100T4 0x8000 /* 100 base T4 capable */
+#define BMSR_100TXFDX 0x4000 /* 100 base Tx full duplex capable */
+#define BMSR_100TXHDX 0x2000 /* 100 base Tx half duplex capable */
+#define BMSR_10TFDX 0x1000 /* 10 base T full duplex capable */
+#define BMSR_10THDX 0x0800 /* 10 base T half duplex capable */
+#define BMSR_100T2FDX 0x0400 /* 100 base T2 full duplex capable */
+#define BMSR_100T2HDX 0x0200 /* 100 base T2 half duplex capable */
+#define BMSR_EXTSTAT 0x0100 /* Extended status in register 15 */
+#define BMSR_MFPS 0x0040 /* MII Frame Preamble Suppression */
+#define BMSR_ACOMP 0x0020 /* Autonegotiation complete */
+#define BMSR_RFAULT 0x0010 /* Link partner fault */
+#define BMSR_ANEG 0x0008 /* Autonegotiation capable */
+#define BMSR_LINK 0x0004 /* Link status */
+#define BMSR_JABBER 0x0002 /* Jabber detected */
+#define BMSR_EXTCAP 0x0001 /* Extended capability */
+
+#define BMSR_DEFCAPMASK 0xffffffff
+
+/*
+ * Note that the EXTSTAT bit indicates that there is extended status
+ * info available in register 15, but 802.3 section 22.2.4.3 also
+ * states that all 1000 Mb/s capable PHYs will set this bit to 1.
+ */
+
+#define BMSR_MEDIAMASK (BMSR_100T4|BMSR_100TXFDX|BMSR_100TXHDX| \
+ BMSR_10TFDX|BMSR_10THDX|BMSR_100T2FDX|BMSR_100T2HDX)
+
+/*
+ * Convert BMSR media capabilities to ANAR bits for autonegotiation.
+ * Note the shift chopps off the BMSR_ANEG bit.
+ */
+#define BMSR_MEDIA_TO_ANAR(x) (((x) & BMSR_MEDIAMASK) >> 6)
+
+#define MII_PHYIDR1 0x02 /* ID register 1 (ro) */
+
+#define MII_PHYIDR2 0x03 /* ID register 2 (ro) */
+#define IDR2_OUILSB 0xfc00 /* OUI LSB */
+#define IDR2_MODEL 0x03f0 /* vendor model */
+#define IDR2_REV 0x000f /* vendor revision */
+
+#define MII_ANAR 0x04 /* Autonegotiation advertisement (rw) */
+ /* section 28.2.4.1 and 37.2.6.1 */
+#define ANAR_NP 0x8000 /* Next page (ro) */
+#define ANAR_ACK 0x4000 /* link partner abilities acknowledged (ro) */
+#define ANAR_RF 0x2000 /* remote fault (ro) */
+ /* Annex 28B.2 */
+#define ANAR_FC 0x0400 /* local device supports PAUSE */
+#define ANAR_T4 0x0200 /* local device supports 100bT4 */
+#define ANAR_TX_FD 0x0100 /* local device supports 100bTx FD */
+#define ANAR_TX 0x0080 /* local device supports 100bTx */
+#define ANAR_10_FD 0x0040 /* local device supports 10bT FD */
+#define ANAR_10 0x0020 /* local device supports 10bT */
+#define ANAR_CSMA 0x0001 /* protocol selector CSMA/CD */
+#define ANAR_PAUSE_NONE (0 << 10)
+#define ANAR_PAUSE_SYM (1 << 10)
+#define ANAR_PAUSE_ASYM (2 << 10)
+#define ANAR_PAUSE_TOWARDS (3 << 10)
+
+ /* Annex 28D */
+#define ANAR_X_FD 0x0020 /* local device supports 1000BASE-X FD */
+#define ANAR_X_HD 0x0040 /* local device supports 1000BASE-X HD */
+#define ANAR_X_PAUSE_NONE (0 << 7)
+#define ANAR_X_PAUSE_SYM (1 << 7)
+#define ANAR_X_PAUSE_ASYM (2 << 7)
+#define ANAR_X_PAUSE_TOWARDS (3 << 7)
+
+#define MII_ANLPAR 0x05 /* Autonegotiation lnk partner abilities (rw) */
+ /* section 28.2.4.1 and 37.2.6.1 */
+#define ANLPAR_NP 0x8000 /* Next page (ro) */
+#define ANLPAR_ACK 0x4000 /* link partner accepted ACK (ro) */
+#define ANLPAR_RF 0x2000 /* remote fault (ro) */
+#define ANLPAR_FC 0x0400 /* link partner supports PAUSE */
+#define ANLPAR_T4 0x0200 /* link partner supports 100bT4 */
+#define ANLPAR_TX_FD 0x0100 /* link partner supports 100bTx FD */
+#define ANLPAR_TX 0x0080 /* link partner supports 100bTx */
+#define ANLPAR_10_FD 0x0040 /* link partner supports 10bT FD */
+#define ANLPAR_10 0x0020 /* link partner supports 10bT */
+#define ANLPAR_CSMA 0x0001 /* protocol selector CSMA/CD */
+#define ANLPAR_PAUSE_MASK (3 << 10)
+#define ANLPAR_PAUSE_NONE (0 << 10)
+#define ANLPAR_PAUSE_SYM (1 << 10)
+#define ANLPAR_PAUSE_ASYM (2 << 10)
+#define ANLPAR_PAUSE_TOWARDS (3 << 10)
+
+#define ANLPAR_X_FD 0x0020 /* local device supports 1000BASE-X FD */
+#define ANLPAR_X_HD 0x0040 /* local device supports 1000BASE-X HD */
+#define ANLPAR_X_PAUSE_MASK (3 << 7)
+#define ANLPAR_X_PAUSE_NONE (0 << 7)
+#define ANLPAR_X_PAUSE_SYM (1 << 7)
+#define ANLPAR_X_PAUSE_ASYM (2 << 7)
+#define ANLPAR_X_PAUSE_TOWARDS (3 << 7)
+
+#define MII_ANER 0x06 /* Autonegotiation expansion (ro) */
+ /* section 28.2.4.1 and 37.2.6.1 */
+#define ANER_MLF 0x0010 /* multiple link detection fault */
+#define ANER_LPNP 0x0008 /* link parter next page-able */
+#define ANER_NP 0x0004 /* next page-able */
+#define ANER_PAGE_RX 0x0002 /* Page received */
+#define ANER_LPAN 0x0001 /* link parter autoneg-able */
+
+#define MII_ANNP 0x07 /* Autonegotiation next page */
+ /* section 28.2.4.1 and 37.2.6.1 */
+
+#define MII_ANLPRNP 0x08 /* Autonegotiation link partner rx next page */
+ /* section 32.5.1 and 37.2.6.1 */
+
+ /* This is also the 1000baseT control register */
+#define MII_100T2CR 0x09 /* 100base-T2 control register */
+#define GTCR_TEST_MASK 0xe000 /* see 802.3ab ss. 40.6.1.1.2 */
+#define GTCR_MAN_MS 0x1000 /* enable manual master/slave control */
+#define GTCR_ADV_MS 0x0800 /* 1 = adv. master, 0 = adv. slave */
+#define GTCR_PORT_TYPE 0x0400 /* 1 = DCE, 0 = DTE (NIC) */
+#define GTCR_ADV_1000TFDX 0x0200 /* adv. 1000baseT FDX */
+#define GTCR_ADV_1000THDX 0x0100 /* adv. 1000baseT HDX */
+
+ /* This is also the 1000baseT status register */
+#define MII_100T2SR 0x0a /* 100base-T2 status register */
+#define GTSR_MAN_MS_FLT 0x8000 /* master/slave config fault */
+#define GTSR_MS_RES 0x4000 /* result: 1 = master, 0 = slave */
+#define GTSR_LRS 0x2000 /* local rx status, 1 = ok */
+#define GTSR_RRS 0x1000 /* remote rx status, 1 = ok */
+#define GTSR_LP_1000TFDX 0x0800 /* link partner 1000baseT FDX capable */
+#define GTSR_LP_1000THDX 0x0400 /* link partner 1000baseT HDX capable */
+#define GTSR_LP_ASM_DIR 0x0200 /* link partner asym. pause dir. capable */
+#define GTSR_IDLE_ERR 0x00ff /* IDLE error count */
+
+#define MII_PSECR 0x0b /* PSE control register */
+#define PSECR_PACTLMASK 0x000c /* pair control mask */
+#define PSECR_PSEENMASK 0x0003 /* PSE enable mask */
+#define PSECR_PINOUTB 0x0008 /* PSE pinout Alternative B */
+#define PSECR_PINOUTA 0x0004 /* PSE pinout Alternative A */
+#define PSECR_FOPOWTST 0x0002 /* Force Power Test Mode */
+#define PSECR_PSEEN 0x0001 /* PSE Enabled */
+#define PSECR_PSEDIS 0x0000 /* PSE Disabled */
+
+#define MII_PSESR 0x0c /* PSE status register */
+#define PSESR_PWRDENIED 0x1000 /* Power Denied */
+#define PSESR_VALSIG 0x0800 /* Valid PD signature detected */
+#define PSESR_INVALSIG 0x0400 /* Invalid PD signature detected */
+#define PSESR_SHORTCIRC 0x0200 /* Short circuit condition detected */
+#define PSESR_OVERLOAD 0x0100 /* Overload condition detected */
+#define PSESR_MPSABSENT 0x0080 /* MPS absent condition detected */
+#define PSESR_PDCLMASK 0x0070 /* PD Class mask */
+#define PSESR_STATMASK 0x000e /* PSE Status mask */
+#define PSESR_PAIRCTABL 0x0001 /* PAIR Control Ability */
+#define PSESR_PDCL_4 (4 << 4) /* Class 4 */
+#define PSESR_PDCL_3 (3 << 4) /* Class 3 */
+#define PSESR_PDCL_2 (2 << 4) /* Class 2 */
+#define PSESR_PDCL_1 (1 << 4) /* Class 1 */
+#define PSESR_PDCL_0 (0 << 4) /* Class 0 */
+
+#define MII_MMDACR 0x0d /* MMD access control register */
+#define MMDACR_FUNCMASK 0xc000 /* function */
+#define MMDACR_DADDRMASK 0x001f /* device address */
+#define MMDACR_FN_ADDRESS (0 << 14) /* address */
+#define MMDACR_FN_DATANPI (1 << 14) /* data, no post increment */
+#define MMDACR_FN_DATAPIRW (2 << 14) /* data, post increment on r/w */
+#define MMDACR_FN_DATAPIW (3 << 14) /* data, post increment on wr only */
+
+#define MII_MMDAADR 0x0e /* MMD access address data register */
+
+#define MII_EXTSR 0x0f /* Extended status register */
+#define EXTSR_1000XFDX 0x8000 /* 1000X full-duplex capable */
+#define EXTSR_1000XHDX 0x4000 /* 1000X half-duplex capable */
+#define EXTSR_1000TFDX 0x2000 /* 1000T full-duplex capable */
+#define EXTSR_1000THDX 0x1000 /* 1000T half-duplex capable */
+
+#define EXTSR_MEDIAMASK (EXTSR_1000XFDX|EXTSR_1000XHDX| \
+ EXTSR_1000TFDX|EXTSR_1000THDX)
+
+#endif /* _DEV_MII_MII_H_ */
diff --git a/usr/contrib/freebsd/dev/usb/controller/xhcireg.h b/usr/contrib/freebsd/dev/usb/controller/xhcireg.h
new file mode 100644
index 0000000000..0e588ecba3
--- /dev/null
+++ b/usr/contrib/freebsd/dev/usb/controller/xhcireg.h
@@ -0,0 +1,224 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XHCIREG_H_
+#define _XHCIREG_H_
+
+/* XHCI PCI config registers */
+#define PCI_XHCI_CBMEM 0x10 /* configuration base MEM */
+#define PCI_XHCI_USBREV 0x60 /* RO USB protocol revision */
+#define PCI_USB_REV_3_0 0x30 /* USB 3.0 */
+#define PCI_XHCI_FLADJ 0x61 /* RW frame length adjust */
+
+#define PCI_XHCI_INTEL_XUSB2PR 0xD0 /* Intel USB2 Port Routing */
+#define PCI_XHCI_INTEL_USB2PRM 0xD4 /* Intel USB2 Port Routing Mask */
+#define PCI_XHCI_INTEL_USB3_PSSEN 0xD8 /* Intel USB3 Port SuperSpeed Enable */
+#define PCI_XHCI_INTEL_USB3PRM 0xDC /* Intel USB3 Port Routing Mask */
+
+/* XHCI capability registers */
+#define XHCI_CAPLENGTH 0x00 /* RO capability */
+#define XHCI_RESERVED 0x01 /* Reserved */
+#define XHCI_HCIVERSION 0x02 /* RO Interface version number */
+#define XHCI_HCIVERSION_0_9 0x0090 /* xHCI version 0.9 */
+#define XHCI_HCIVERSION_1_0 0x0100 /* xHCI version 1.0 */
+#define XHCI_HCSPARAMS1 0x04 /* RO structural parameters 1 */
+#define XHCI_HCS1_DEVSLOT_MAX(x)((x) & 0xFF)
+#define XHCI_HCS1_IRQ_MAX(x) (((x) >> 8) & 0x3FF)
+#define XHCI_HCS1_N_PORTS(x) (((x) >> 24) & 0xFF)
+#define XHCI_HCSPARAMS2 0x08 /* RO structural parameters 2 */
+#define XHCI_HCS2_IST(x) ((x) & 0xF)
+#define XHCI_HCS2_ERST_MAX(x) (((x) >> 4) & 0xF)
+#define XHCI_HCS2_SPR(x) (((x) >> 26) & 0x1)
+#define XHCI_HCS2_SPB_MAX(x) ((((x) >> 16) & 0x3E0) | (((x) >> 27) & 0x1F))
+#define XHCI_HCSPARAMS3 0x0C /* RO structural parameters 3 */
+#define XHCI_HCS3_U1_DEL(x) ((x) & 0xFF)
+#define XHCI_HCS3_U2_DEL(x) (((x) >> 16) & 0xFFFF)
+#define XHCI_HCSPARAMS0 0x10 /* RO capability parameters */
+#define XHCI_HCS0_AC64(x) ((x) & 0x1) /* 64-bit capable */
+#define XHCI_HCS0_BNC(x) (((x) >> 1) & 0x1) /* BW negotiation */
+#define XHCI_HCS0_CSZ(x) (((x) >> 2) & 0x1) /* context size */
+#define XHCI_HCS0_PPC(x) (((x) >> 3) & 0x1) /* port power control */
+#define XHCI_HCS0_PIND(x) (((x) >> 4) & 0x1) /* port indicators */
+#define XHCI_HCS0_LHRC(x) (((x) >> 5) & 0x1) /* light HC reset */
+#define XHCI_HCS0_LTC(x) (((x) >> 6) & 0x1) /* latency tolerance msg */
+#define XHCI_HCS0_NSS(x) (((x) >> 7) & 0x1) /* no secondary sid */
+#define XHCI_HCS0_PSA_SZ_MAX(x) (((x) >> 12) & 0xF) /* max pri. stream array size */
+#define XHCI_HCS0_XECP(x) (((x) >> 16) & 0xFFFF) /* extended capabilities pointer */
+#define XHCI_DBOFF 0x14 /* RO doorbell offset */
+#define XHCI_RTSOFF 0x18 /* RO runtime register space offset */
+
+/* XHCI operational registers. Offset given by XHCI_CAPLENGTH register */
+#define XHCI_USBCMD 0x00 /* XHCI command */
+#define XHCI_CMD_RS 0x00000001 /* RW Run/Stop */
+#define XHCI_CMD_HCRST 0x00000002 /* RW Host Controller Reset */
+#define XHCI_CMD_INTE 0x00000004 /* RW Interrupter Enable */
+#define XHCI_CMD_HSEE 0x00000008 /* RW Host System Error Enable */
+#define XHCI_CMD_LHCRST 0x00000080 /* RO/RW Light Host Controller Reset */
+#define XHCI_CMD_CSS 0x00000100 /* RW Controller Save State */
+#define XHCI_CMD_CRS 0x00000200 /* RW Controller Restore State */
+#define XHCI_CMD_EWE 0x00000400 /* RW Enable Wrap Event */
+#define XHCI_CMD_EU3S 0x00000800 /* RW Enable U3 MFINDEX Stop */
+#define XHCI_USBSTS 0x04 /* XHCI status */
+#define XHCI_STS_HCH 0x00000001 /* RO - Host Controller Halted */
+#define XHCI_STS_HSE 0x00000004 /* RW - Host System Error */
+#define XHCI_STS_EINT 0x00000008 /* RW - Event Interrupt */
+#define XHCI_STS_PCD 0x00000010 /* RW - Port Change Detect */
+#define XHCI_STS_SSS 0x00000100 /* RO - Save State Status */
+#define XHCI_STS_RSS 0x00000200 /* RO - Restore State Status */
+#define XHCI_STS_SRE 0x00000400 /* RW - Save/Restore Error */
+#define XHCI_STS_CNR 0x00000800 /* RO - Controller Not Ready */
+#define XHCI_STS_HCE 0x00001000 /* RO - Host Controller Error */
+#define XHCI_PAGESIZE 0x08 /* XHCI page size mask */
+#define XHCI_PAGESIZE_4K 0x00000001 /* 4K Page Size */
+#define XHCI_PAGESIZE_8K 0x00000002 /* 8K Page Size */
+#define XHCI_PAGESIZE_16K 0x00000004 /* 16K Page Size */
+#define XHCI_PAGESIZE_32K 0x00000008 /* 32K Page Size */
+#define XHCI_PAGESIZE_64K 0x00000010 /* 64K Page Size */
+#define XHCI_DNCTRL 0x14 /* XHCI device notification control */
+#define XHCI_DNCTRL_MASK(n) (1U << (n))
+#define XHCI_CRCR_LO 0x18 /* XHCI command ring control */
+#define XHCI_CRCR_LO_RCS 0x00000001 /* RW - consumer cycle state */
+#define XHCI_CRCR_LO_CS 0x00000002 /* RW - command stop */
+#define XHCI_CRCR_LO_CA 0x00000004 /* RW - command abort */
+#define XHCI_CRCR_LO_CRR 0x00000008 /* RW - command ring running */
+#define XHCI_CRCR_LO_MASK 0x0000000F
+#define XHCI_CRCR_HI 0x1C /* XHCI command ring control */
+#define XHCI_DCBAAP_LO 0x30 /* XHCI dev context BA pointer */
+#define XHCI_DCBAAP_HI 0x34 /* XHCI dev context BA pointer */
+#define XHCI_CONFIG 0x38
+#define XHCI_CONFIG_SLOTS_MASK 0x000000FF /* RW - number of device slots enabled */
+
+/* XHCI port status registers */
+#define XHCI_PORTSC(n) (0x3F0 + (0x10 * (n))) /* XHCI port status */
+#define XHCI_PS_CCS 0x00000001 /* RO - current connect status */
+#define XHCI_PS_PED 0x00000002 /* RW - port enabled / disabled */
+#define XHCI_PS_OCA 0x00000008 /* RO - over current active */
+#define XHCI_PS_PR 0x00000010 /* RW - port reset */
+#define XHCI_PS_PLS_GET(x) (((x) >> 5) & 0xF) /* RW - port link state */
+#define XHCI_PS_PLS_SET(x) (((x) & 0xF) << 5) /* RW - port link state */
+#define XHCI_PS_PP 0x00000200 /* RW - port power */
+#define XHCI_PS_SPEED_GET(x) (((x) >> 10) & 0xF) /* RO - port speed */
+#define XHCI_PS_PIC_GET(x) (((x) >> 14) & 0x3) /* RW - port indicator */
+#define XHCI_PS_PIC_SET(x) (((x) & 0x3) << 14) /* RW - port indicator */
+#define XHCI_PS_LWS 0x00010000 /* RW - port link state write strobe */
+#define XHCI_PS_CSC 0x00020000 /* RW - connect status change */
+#define XHCI_PS_PEC 0x00040000 /* RW - port enable/disable change */
+#define XHCI_PS_WRC 0x00080000 /* RW - warm port reset change */
+#define XHCI_PS_OCC 0x00100000 /* RW - over-current change */
+#define XHCI_PS_PRC 0x00200000 /* RW - port reset change */
+#define XHCI_PS_PLC 0x00400000 /* RW - port link state change */
+#define XHCI_PS_CEC 0x00800000 /* RW - config error change */
+#define XHCI_PS_CAS 0x01000000 /* RO - cold attach status */
+#define XHCI_PS_WCE 0x02000000 /* RW - wake on connect enable */
+#define XHCI_PS_WDE 0x04000000 /* RW - wake on disconnect enable */
+#define XHCI_PS_WOE 0x08000000 /* RW - wake on over-current enable */
+#define XHCI_PS_DR 0x40000000 /* RO - device removable */
+#define XHCI_PS_WPR 0x80000000U /* RW - warm port reset */
+#define XHCI_PS_CLEAR 0x80FF01FFU /* command bits */
+
+#define XHCI_PORTPMSC(n) (0x3F4 + (0x10 * (n))) /* XHCI status and control */
+#define XHCI_PM3_U1TO_GET(x) (((x) >> 0) & 0xFF) /* RW - U1 timeout */
+#define XHCI_PM3_U1TO_SET(x) (((x) & 0xFF) << 0) /* RW - U1 timeout */
+#define XHCI_PM3_U2TO_GET(x) (((x) >> 8) & 0xFF) /* RW - U2 timeout */
+#define XHCI_PM3_U2TO_SET(x) (((x) & 0xFF) << 8) /* RW - U2 timeout */
+#define XHCI_PM3_FLA 0x00010000 /* RW - Force Link PM Accept */
+#define XHCI_PM2_L1S_GET(x) (((x) >> 0) & 0x7) /* RO - L1 status */
+#define XHCI_PM2_RWE 0x00000008 /* RW - remote wakup enable */
+#define XHCI_PM2_HIRD_GET(x) (((x) >> 4) & 0xF) /* RW - host initiated resume duration */
+#define XHCI_PM2_HIRD_SET(x) (((x) & 0xF) << 4) /* RW - host initiated resume duration */
+#define XHCI_PM2_L1SLOT_GET(x) (((x) >> 8) & 0xFF) /* RW - L1 device slot */
+#define XHCI_PM2_L1SLOT_SET(x) (((x) & 0xFF) << 8) /* RW - L1 device slot */
+#define XHCI_PM2_HLE 0x00010000 /* RW - hardware LPM enable */
+#define XHCI_PORTLI(n) (0x3F8 + (0x10 * (n))) /* XHCI port link info */
+#define XHCI_PLI3_ERR_GET(x) (((x) >> 0) & 0xFFFF) /* RO - port link errors */
+#define XHCI_PORTRSV(n) (0x3FC + (0x10 * (n))) /* XHCI port reserved */
+
+/* XHCI runtime registers. Offset given by XHCI_CAPLENGTH + XHCI_RTSOFF registers */
+#define XHCI_MFINDEX 0x0000 /* RO - microframe index */
+#define XHCI_MFINDEX_GET(x) ((x) & 0x3FFF)
+#define XHCI_IMAN(n) (0x0020 + (0x20 * (n))) /* XHCI interrupt management */
+#define XHCI_IMAN_INTR_PEND 0x00000001 /* RW - interrupt pending */
+#define XHCI_IMAN_INTR_ENA 0x00000002 /* RW - interrupt enable */
+#define XHCI_IMOD(n) (0x0024 + (0x20 * (n))) /* XHCI interrupt moderation */
+#define XHCI_IMOD_IVAL_GET(x) (((x) >> 0) & 0xFFFF) /* 250ns unit */
+#define XHCI_IMOD_IVAL_SET(x) (((x) & 0xFFFF) << 0) /* 250ns unit */
+#define XHCI_IMOD_ICNT_GET(x) (((x) >> 16) & 0xFFFF) /* 250ns unit */
+#define XHCI_IMOD_ICNT_SET(x) (((x) & 0xFFFF) << 16) /* 250ns unit */
+#define XHCI_IMOD_DEFAULT 0x000001F4U /* 8000 IRQs/second */
+#define XHCI_IMOD_DEFAULT_LP 0x000003F8U /* 4000 IRQs/second - LynxPoint */
+#define XHCI_ERSTSZ(n) (0x0028 + (0x20 * (n))) /* XHCI event ring segment table size */
+#define XHCI_ERSTS_GET(x) ((x) & 0xFFFF)
+#define XHCI_ERSTS_SET(x) ((x) & 0xFFFF)
+#define XHCI_ERSTBA_LO(n) (0x0030 + (0x20 * (n))) /* XHCI event ring segment table BA */
+#define XHCI_ERSTBA_HI(n) (0x0034 + (0x20 * (n))) /* XHCI event ring segment table BA */
+#define XHCI_ERDP_LO(n) (0x0038 + (0x20 * (n))) /* XHCI event ring dequeue pointer */
+#define XHCI_ERDP_LO_SINDEX(x) ((x) & 0x7) /* RO - dequeue segment index */
+#define XHCI_ERDP_LO_BUSY 0x00000008 /* RW - event handler busy */
+#define XHCI_ERDP_HI(n) (0x003C + (0x20 * (n))) /* XHCI event ring dequeue pointer */
+
+/* XHCI doorbell registers. Offset given by XHCI_CAPLENGTH + XHCI_DBOFF registers */
+#define XHCI_DOORBELL(n) (0x0000 + (4 * (n)))
+#define XHCI_DB_TARGET_GET(x) ((x) & 0xFF) /* RW - doorbell target */
+#define XHCI_DB_TARGET_SET(x) ((x) & 0xFF) /* RW - doorbell target */
+#define XHCI_DB_SID_GET(x) (((x) >> 16) & 0xFFFF) /* RW - doorbell stream ID */
+#define XHCI_DB_SID_SET(x) (((x) & 0xFFFF) << 16) /* RW - doorbell stream ID */
+
+/* XHCI legacy support */
+#define XHCI_XECP_ID(x) ((x) & 0xFF)
+#define XHCI_XECP_NEXT(x) (((x) >> 8) & 0xFF)
+#define XHCI_XECP_BIOS_SEM 0x0002
+#define XHCI_XECP_OS_SEM 0x0003
+
+/* XHCI capability ID's */
+#define XHCI_ID_USB_LEGACY 0x0001
+#define XHCI_ID_PROTOCOLS 0x0002
+#define XHCI_ID_POWER_MGMT 0x0003
+#define XHCI_ID_VIRTUALIZATION 0x0004
+#define XHCI_ID_MSG_IRQ 0x0005
+#define XHCI_ID_USB_LOCAL_MEM 0x0006
+
+/* XHCI register R/W wrappers */
+#define XREAD1(sc, what, a) \
+ bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, \
+ (a) + (sc)->sc_##what##_off)
+#define XREAD2(sc, what, a) \
+ bus_space_read_2((sc)->sc_io_tag, (sc)->sc_io_hdl, \
+ (a) + (sc)->sc_##what##_off)
+#define XREAD4(sc, what, a) \
+ bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, \
+ (a) + (sc)->sc_##what##_off)
+#define XWRITE1(sc, what, a, x) \
+ bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, \
+ (a) + (sc)->sc_##what##_off, (x))
+#define XWRITE2(sc, what, a, x) \
+ bus_space_write_2((sc)->sc_io_tag, (sc)->sc_io_hdl, \
+ (a) + (sc)->sc_##what##_off, (x))
+#define XWRITE4(sc, what, a, x) \
+ bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, \
+ (a) + (sc)->sc_##what##_off, (x))
+
+#endif /* _XHCIREG_H_ */
diff --git a/usr/contrib/freebsd/dev/usb/usb.h b/usr/contrib/freebsd/dev/usb/usb.h
new file mode 100644
index 0000000000..bcea2ac8bd
--- /dev/null
+++ b/usr/contrib/freebsd/dev/usb/usb.h
@@ -0,0 +1,801 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
+ * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved.
+ * Copyright (c) 1998 Lennart Augustsson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This file contains standard definitions for the following USB
+ * protocol versions:
+ *
+ * USB v1.0
+ * USB v1.1
+ * USB v2.0
+ * USB v3.0
+ */
+
+#ifndef _USB_STANDARD_H_
+#define _USB_STANDARD_H_
+
+#if defined(_KERNEL)
+#ifndef USB_GLOBAL_INCLUDE_FILE
+#include "opt_usb.h"
+#endif
+
+/* Declare parent SYSCTL USB node. */
+#ifdef SYSCTL_DECL
+SYSCTL_DECL(_hw_usb);
+#endif
+
+#ifndef USB_GLOBAL_INCLUDE_FILE
+#include <sys/malloc.h>
+#endif
+
+MALLOC_DECLARE(M_USB);
+MALLOC_DECLARE(M_USBDEV);
+#endif /* _KERNEL */
+
+#ifndef USB_GLOBAL_INCLUDE_FILE
+#include <dev/usb/usb_endian.h>
+#include <dev/usb/usb_freebsd.h>
+#endif
+
+#define USB_STACK_VERSION 2000 /* 2.0 */
+
+/* Definition of some hardcoded USB constants. */
+
+#define USB_MAX_IPACKET 8 /* initial USB packet size */
+#define USB_EP_MAX (2*16) /* hardcoded */
+#define USB_ROOT_HUB_ADDR 1 /* index */
+#define USB_MIN_DEVICES 2 /* unused + root HUB */
+#define USB_UNCONFIG_INDEX 0xFF /* internal use only */
+#define USB_IFACE_INDEX_ANY 0xFF /* internal use only */
+#define USB_START_ADDR 0 /* default USB device BUS address
+ * after USB bus reset */
+#define USB_CONTROL_ENDPOINT 0 /* default control endpoint */
+
+#define USB_FRAMES_PER_SECOND_FS 1000 /* full speed */
+#define USB_FRAMES_PER_SECOND_HS 8000 /* high speed */
+
+#define USB_FS_BYTES_PER_HS_UFRAME 188 /* bytes */
+#define USB_HS_MICRO_FRAMES_MAX 8 /* units */
+
+#define USB_ISOC_TIME_MAX 128 /* ms */
+
+/*
+ * Minimum time a device needs to be powered down to go through a
+ * power cycle. These values are not in the USB specification.
+ */
+#define USB_POWER_DOWN_TIME 200 /* ms */
+#define USB_PORT_POWER_DOWN_TIME 100 /* ms */
+
+/* Definition of software USB power modes */
+#define USB_POWER_MODE_OFF 0 /* turn off device */
+#define USB_POWER_MODE_ON 1 /* always on */
+#define USB_POWER_MODE_SAVE 2 /* automatic suspend and resume */
+#define USB_POWER_MODE_SUSPEND 3 /* force suspend */
+#define USB_POWER_MODE_RESUME 4 /* force resume */
+
+/* These are the values from the USB specification. */
+#define USB_PORT_RESET_DELAY_SPEC 10 /* ms */
+#define USB_PORT_ROOT_RESET_DELAY_SPEC 50 /* ms */
+#define USB_PORT_RESET_RECOVERY_SPEC 10 /* ms */
+#define USB_PORT_POWERUP_DELAY_SPEC 100 /* ms */
+#define USB_PORT_RESUME_DELAY_SPEC 20 /* ms */
+#define USB_SET_ADDRESS_SETTLE_SPEC 2 /* ms */
+#define USB_RESUME_DELAY_SPEC (20*5) /* ms */
+#define USB_RESUME_WAIT_SPEC 10 /* ms */
+#define USB_RESUME_RECOVERY_SPEC 10 /* ms */
+#define USB_EXTRA_POWER_UP_TIME_SPEC 0 /* ms */
+
+/* Allow for marginal and non-conforming devices. */
+#define USB_PORT_RESET_DELAY 50 /* ms */
+#define USB_PORT_ROOT_RESET_DELAY 200 /* ms */
+#define USB_PORT_RESET_RECOVERY 250 /* ms */
+#define USB_PORT_POWERUP_DELAY 300 /* ms */
+#define USB_PORT_RESUME_DELAY (20*2) /* ms */
+#define USB_SET_ADDRESS_SETTLE 10 /* ms */
+#define USB_RESUME_DELAY (50*5) /* ms */
+#define USB_RESUME_WAIT 50 /* ms */
+#define USB_RESUME_RECOVERY 50 /* ms */
+#define USB_EXTRA_POWER_UP_TIME 20 /* ms */
+
+#define USB_MIN_POWER 100 /* mA */
+#define USB_MAX_POWER 500 /* mA */
+
+#define USB_BUS_RESET_DELAY 100 /* ms */
+
+/*
+ * USB record layout in memory:
+ *
+ * - USB config 0
+ * - USB interfaces
+ * - USB alternative interfaces
+ * - USB endpoints
+ *
+ * - USB config 1
+ * - USB interfaces
+ * - USB alternative interfaces
+ * - USB endpoints
+ */
+
+/* Declaration of USB records */
+
+struct usb_device_request {
+ uByte bmRequestType;
+ uByte bRequest;
+ uWord wValue;
+ uWord wIndex;
+ uWord wLength;
+} __packed;
+typedef struct usb_device_request usb_device_request_t;
+
+#define UT_WRITE 0x00
+#define UT_READ 0x80
+#define UT_STANDARD 0x00
+#define UT_CLASS 0x20
+#define UT_VENDOR 0x40
+#define UT_DEVICE 0x00
+#define UT_INTERFACE 0x01
+#define UT_ENDPOINT 0x02
+#define UT_OTHER 0x03
+
+#define UT_READ_DEVICE (UT_READ | UT_STANDARD | UT_DEVICE)
+#define UT_READ_INTERFACE (UT_READ | UT_STANDARD | UT_INTERFACE)
+#define UT_READ_ENDPOINT (UT_READ | UT_STANDARD | UT_ENDPOINT)
+#define UT_WRITE_DEVICE (UT_WRITE | UT_STANDARD | UT_DEVICE)
+#define UT_WRITE_INTERFACE (UT_WRITE | UT_STANDARD | UT_INTERFACE)
+#define UT_WRITE_ENDPOINT (UT_WRITE | UT_STANDARD | UT_ENDPOINT)
+#define UT_READ_CLASS_DEVICE (UT_READ | UT_CLASS | UT_DEVICE)
+#define UT_READ_CLASS_INTERFACE (UT_READ | UT_CLASS | UT_INTERFACE)
+#define UT_READ_CLASS_OTHER (UT_READ | UT_CLASS | UT_OTHER)
+#define UT_READ_CLASS_ENDPOINT (UT_READ | UT_CLASS | UT_ENDPOINT)
+#define UT_WRITE_CLASS_DEVICE (UT_WRITE | UT_CLASS | UT_DEVICE)
+#define UT_WRITE_CLASS_INTERFACE (UT_WRITE | UT_CLASS | UT_INTERFACE)
+#define UT_WRITE_CLASS_OTHER (UT_WRITE | UT_CLASS | UT_OTHER)
+#define UT_WRITE_CLASS_ENDPOINT (UT_WRITE | UT_CLASS | UT_ENDPOINT)
+#define UT_READ_VENDOR_DEVICE (UT_READ | UT_VENDOR | UT_DEVICE)
+#define UT_READ_VENDOR_INTERFACE (UT_READ | UT_VENDOR | UT_INTERFACE)
+#define UT_READ_VENDOR_OTHER (UT_READ | UT_VENDOR | UT_OTHER)
+#define UT_READ_VENDOR_ENDPOINT (UT_READ | UT_VENDOR | UT_ENDPOINT)
+#define UT_WRITE_VENDOR_DEVICE (UT_WRITE | UT_VENDOR | UT_DEVICE)
+#define UT_WRITE_VENDOR_INTERFACE (UT_WRITE | UT_VENDOR | UT_INTERFACE)
+#define UT_WRITE_VENDOR_OTHER (UT_WRITE | UT_VENDOR | UT_OTHER)
+#define UT_WRITE_VENDOR_ENDPOINT (UT_WRITE | UT_VENDOR | UT_ENDPOINT)
+
+/* Requests */
+#define UR_GET_STATUS 0x00
+#define UR_CLEAR_FEATURE 0x01
+#define UR_SET_FEATURE 0x03
+#define UR_SET_ADDRESS 0x05
+#define UR_GET_DESCRIPTOR 0x06
+#define UDESC_DEVICE 0x01
+#define UDESC_CONFIG 0x02
+#define UDESC_STRING 0x03
+#define USB_LANGUAGE_TABLE 0x00 /* language ID string index */
+#define UDESC_INTERFACE 0x04
+#define UDESC_ENDPOINT 0x05
+#define UDESC_DEVICE_QUALIFIER 0x06
+#define UDESC_OTHER_SPEED_CONFIGURATION 0x07
+#define UDESC_INTERFACE_POWER 0x08
+#define UDESC_OTG 0x09
+#define UDESC_DEBUG 0x0A
+#define UDESC_IFACE_ASSOC 0x0B /* interface association */
+#define UDESC_BOS 0x0F /* binary object store */
+#define UDESC_DEVICE_CAPABILITY 0x10
+#define UDESC_CS_DEVICE 0x21 /* class specific */
+#define UDESC_CS_CONFIG 0x22
+#define UDESC_CS_STRING 0x23
+#define UDESC_CS_INTERFACE 0x24
+#define UDESC_CS_ENDPOINT 0x25
+#define UDESC_HUB 0x29
+#define UDESC_SS_HUB 0x2A /* super speed */
+#define UDESC_ENDPOINT_SS_COMP 0x30 /* super speed */
+#define UR_SET_DESCRIPTOR 0x07
+#define UR_GET_CONFIG 0x08
+#define UR_SET_CONFIG 0x09
+#define UR_GET_INTERFACE 0x0a
+#define UR_SET_INTERFACE 0x0b
+#define UR_SYNCH_FRAME 0x0c
+#define UR_SET_SEL 0x30
+#define UR_ISOCH_DELAY 0x31
+
+/* HUB specific request */
+#define UR_GET_BUS_STATE 0x02
+#define UR_CLEAR_TT_BUFFER 0x08
+#define UR_RESET_TT 0x09
+#define UR_GET_TT_STATE 0x0a
+#define UR_STOP_TT 0x0b
+#define UR_SET_AND_TEST 0x0c /* USB 2.0 only */
+#define UR_SET_HUB_DEPTH 0x0c /* USB 3.0 only */
+#define USB_SS_HUB_DEPTH_MAX 5
+#define UR_GET_PORT_ERR_COUNT 0x0d
+
+/* Feature numbers */
+#define UF_ENDPOINT_HALT 0
+#define UF_DEVICE_REMOTE_WAKEUP 1
+#define UF_TEST_MODE 2
+#define UF_U1_ENABLE 0x30
+#define UF_U2_ENABLE 0x31
+#define UF_LTM_ENABLE 0x32
+
+/* HUB specific features */
+#define UHF_C_HUB_LOCAL_POWER 0
+#define UHF_C_HUB_OVER_CURRENT 1
+#define UHF_PORT_CONNECTION 0
+#define UHF_PORT_ENABLE 1
+#define UHF_PORT_SUSPEND 2
+#define UHF_PORT_OVER_CURRENT 3
+#define UHF_PORT_RESET 4
+#define UHF_PORT_LINK_STATE 5
+#define UHF_PORT_POWER 8
+#define UHF_PORT_LOW_SPEED 9
+#define UHF_PORT_L1 10
+#define UHF_C_PORT_CONNECTION 16
+#define UHF_C_PORT_ENABLE 17
+#define UHF_C_PORT_SUSPEND 18
+#define UHF_C_PORT_OVER_CURRENT 19
+#define UHF_C_PORT_RESET 20
+#define UHF_PORT_TEST 21
+#define UHF_PORT_INDICATOR 22
+#define UHF_C_PORT_L1 23
+
+/* SuperSpeed HUB specific features */
+#define UHF_PORT_U1_TIMEOUT 23
+#define UHF_PORT_U2_TIMEOUT 24
+#define UHF_C_PORT_LINK_STATE 25
+#define UHF_C_PORT_CONFIG_ERROR 26
+#define UHF_PORT_REMOTE_WAKE_MASK 27
+#define UHF_BH_PORT_RESET 28
+#define UHF_C_BH_PORT_RESET 29
+#define UHF_FORCE_LINKPM_ACCEPT 30
+
+struct usb_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+} __packed;
+typedef struct usb_descriptor usb_descriptor_t;
+
+struct usb_device_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord bcdUSB;
+#define UD_USB_2_0 0x0200
+#define UD_USB_3_0 0x0300
+#define UD_IS_USB2(d) ((d)->bcdUSB[1] == 0x02)
+#define UD_IS_USB3(d) ((d)->bcdUSB[1] == 0x03)
+ uByte bDeviceClass;
+ uByte bDeviceSubClass;
+ uByte bDeviceProtocol;
+ uByte bMaxPacketSize;
+ /* The fields below are not part of the initial descriptor. */
+ uWord idVendor;
+ uWord idProduct;
+ uWord bcdDevice;
+ uByte iManufacturer;
+ uByte iProduct;
+ uByte iSerialNumber;
+ uByte bNumConfigurations;
+} __packed;
+typedef struct usb_device_descriptor usb_device_descriptor_t;
+
+/* Binary Device Object Store (BOS) */
+struct usb_bos_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord wTotalLength;
+ uByte bNumDeviceCaps;
+} __packed;
+typedef struct usb_bos_descriptor usb_bos_descriptor_t;
+
+/* Binary Device Object Store Capability */
+struct usb_bos_cap_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDevCapabilityType;
+#define USB_DEVCAP_RESERVED 0x00
+#define USB_DEVCAP_WUSB 0x01
+#define USB_DEVCAP_USB2EXT 0x02
+#define USB_DEVCAP_SUPER_SPEED 0x03
+#define USB_DEVCAP_CONTAINER_ID 0x04
+ /* data ... */
+} __packed;
+typedef struct usb_bos_cap_descriptor usb_bos_cap_descriptor_t;
+
+struct usb_devcap_usb2ext_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDevCapabilityType;
+ uDWord bmAttributes;
+#define USB_V2EXT_LPM (1U << 1)
+#define USB_V2EXT_BESL_SUPPORTED (1U << 2)
+#define USB_V2EXT_BESL_BASELINE_VALID (1U << 3)
+#define USB_V2EXT_BESL_DEEP_VALID (1U << 4)
+#define USB_V2EXT_BESL_BASELINE_GET(x) (((x) >> 8) & 0xF)
+#define USB_V2EXT_BESL_DEEP_GET(x) (((x) >> 12) & 0xF)
+} __packed;
+typedef struct usb_devcap_usb2ext_descriptor usb_devcap_usb2ext_descriptor_t;
+
+struct usb_devcap_ss_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDevCapabilityType;
+ uByte bmAttributes;
+ uWord wSpeedsSupported;
+ uByte bFunctionalitySupport;
+ uByte bU1DevExitLat;
+ uWord wU2DevExitLat;
+} __packed;
+typedef struct usb_devcap_ss_descriptor usb_devcap_ss_descriptor_t;
+
+struct usb_devcap_container_id_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDevCapabilityType;
+ uByte bReserved;
+ uByte bContainerID;
+} __packed;
+typedef struct usb_devcap_container_id_descriptor
+ usb_devcap_container_id_descriptor_t;
+
+/* Device class codes */
+#define UDCLASS_IN_INTERFACE 0x00
+#define UDCLASS_COMM 0x02
+#define UDCLASS_HUB 0x09
+#define UDSUBCLASS_HUB 0x00
+#define UDPROTO_FSHUB 0x00
+#define UDPROTO_HSHUBSTT 0x01
+#define UDPROTO_HSHUBMTT 0x02
+#define UDPROTO_SSHUB 0x03
+#define UDCLASS_DIAGNOSTIC 0xdc
+#define UDCLASS_WIRELESS 0xe0
+#define UDSUBCLASS_RF 0x01
+#define UDPROTO_BLUETOOTH 0x01
+#define UDCLASS_VENDOR 0xff
+
+struct usb_config_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord wTotalLength;
+ uByte bNumInterface;
+ uByte bConfigurationValue;
+#define USB_UNCONFIG_NO 0
+ uByte iConfiguration;
+ uByte bmAttributes;
+#define UC_BUS_POWERED 0x80
+#define UC_SELF_POWERED 0x40
+#define UC_REMOTE_WAKEUP 0x20
+ uByte bMaxPower; /* max current in 2 mA units */
+#define UC_POWER_FACTOR 2
+} __packed;
+typedef struct usb_config_descriptor usb_config_descriptor_t;
+
+struct usb_interface_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bInterfaceNumber;
+ uByte bAlternateSetting;
+ uByte bNumEndpoints;
+ uByte bInterfaceClass;
+ uByte bInterfaceSubClass;
+ uByte bInterfaceProtocol;
+ uByte iInterface;
+} __packed;
+typedef struct usb_interface_descriptor usb_interface_descriptor_t;
+
+struct usb_interface_assoc_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bFirstInterface;
+ uByte bInterfaceCount;
+ uByte bFunctionClass;
+ uByte bFunctionSubClass;
+ uByte bFunctionProtocol;
+ uByte iFunction;
+} __packed;
+typedef struct usb_interface_assoc_descriptor usb_interface_assoc_descriptor_t;
+
+/* Interface class codes */
+#define UICLASS_UNSPEC 0x00
+#define UICLASS_AUDIO 0x01 /* audio */
+#define UISUBCLASS_AUDIOCONTROL 1
+#define UISUBCLASS_AUDIOSTREAM 2
+#define UISUBCLASS_MIDISTREAM 3
+
+#define UICLASS_CDC 0x02 /* communication */
+#define UISUBCLASS_DIRECT_LINE_CONTROL_MODEL 1
+#define UISUBCLASS_ABSTRACT_CONTROL_MODEL 2
+#define UISUBCLASS_TELEPHONE_CONTROL_MODEL 3
+#define UISUBCLASS_MULTICHANNEL_CONTROL_MODEL 4
+#define UISUBCLASS_CAPI_CONTROLMODEL 5
+#define UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL 6
+#define UISUBCLASS_ATM_NETWORKING_CONTROL_MODEL 7
+#define UISUBCLASS_WIRELESS_HANDSET_CM 8
+#define UISUBCLASS_DEVICE_MGMT 9
+#define UISUBCLASS_MOBILE_DIRECT_LINE_MODEL 10
+#define UISUBCLASS_OBEX 11
+#define UISUBCLASS_ETHERNET_EMULATION_MODEL 12
+#define UISUBCLASS_NETWORK_CONTROL_MODEL 13
+
+#define UIPROTO_CDC_NONE 0
+#define UIPROTO_CDC_AT 1
+
+#define UICLASS_HID 0x03
+#define UISUBCLASS_BOOT 1
+#define UIPROTO_BOOT_KEYBOARD 1
+#define UIPROTO_MOUSE 2
+
+#define UICLASS_PHYSICAL 0x05
+#define UICLASS_IMAGE 0x06
+#define UISUBCLASS_SIC 1 /* still image class */
+#define UICLASS_PRINTER 0x07
+#define UISUBCLASS_PRINTER 1
+#define UIPROTO_PRINTER_UNI 1
+#define UIPROTO_PRINTER_BI 2
+#define UIPROTO_PRINTER_1284 3
+
+#define UICLASS_MASS 0x08
+#define UISUBCLASS_RBC 1
+#define UISUBCLASS_SFF8020I 2
+#define UISUBCLASS_QIC157 3
+#define UISUBCLASS_UFI 4
+#define UISUBCLASS_SFF8070I 5
+#define UISUBCLASS_SCSI 6
+#define UIPROTO_MASS_CBI_I 0
+#define UIPROTO_MASS_CBI 1
+#define UIPROTO_MASS_BBB_OLD 2 /* Not in the spec anymore */
+#define UIPROTO_MASS_BBB 80 /* 'P' for the Iomega Zip drive */
+
+#define UICLASS_HUB 0x09
+#define UISUBCLASS_HUB 0
+#define UIPROTO_FSHUB 0
+#define UIPROTO_HSHUBSTT 0 /* Yes, same as previous */
+#define UIPROTO_HSHUBMTT 1
+
+#define UICLASS_CDC_DATA 0x0a
+#define UISUBCLASS_DATA 0x00
+#define UIPROTO_DATA_ISDNBRI 0x30 /* Physical iface */
+#define UIPROTO_DATA_HDLC 0x31 /* HDLC */
+#define UIPROTO_DATA_TRANSPARENT 0x32 /* Transparent */
+#define UIPROTO_DATA_Q921M 0x50 /* Management for Q921 */
+#define UIPROTO_DATA_Q921 0x51 /* Data for Q921 */
+#define UIPROTO_DATA_Q921TM 0x52 /* TEI multiplexer for Q921 */
+#define UIPROTO_DATA_V42BIS 0x90 /* Data compression */
+#define UIPROTO_DATA_Q931 0x91 /* Euro-ISDN */
+#define UIPROTO_DATA_V120 0x92 /* V.24 rate adaption */
+#define UIPROTO_DATA_CAPI 0x93 /* CAPI 2.0 commands */
+#define UIPROTO_DATA_HOST_BASED 0xfd /* Host based driver */
+#define UIPROTO_DATA_PUF 0xfe /* see Prot. Unit Func. Desc. */
+#define UIPROTO_DATA_VENDOR 0xff /* Vendor specific */
+#define UIPROTO_DATA_NCM 0x01 /* Network Control Model */
+
+#define UICLASS_SMARTCARD 0x0b
+#define UICLASS_FIRM_UPD 0x0c
+#define UICLASS_SECURITY 0x0d
+#define UICLASS_DIAGNOSTIC 0xdc
+#define UICLASS_WIRELESS 0xe0
+#define UISUBCLASS_RF 0x01
+#define UIPROTO_BLUETOOTH 0x01
+#define UIPROTO_RNDIS 0x03
+
+#define UICLASS_IAD 0xEF /* Interface Association Descriptor */
+#define UISUBCLASS_SYNC 0x01
+#define UIPROTO_ACTIVESYNC 0x01
+
+#define UICLASS_APPL_SPEC 0xfe
+#define UISUBCLASS_FIRMWARE_DOWNLOAD 1
+#define UISUBCLASS_IRDA 2
+#define UIPROTO_IRDA 0
+
+#define UICLASS_VENDOR 0xff
+#define UISUBCLASS_XBOX360_CONTROLLER 0x5d
+#define UIPROTO_XBOX360_GAMEPAD 0x01
+
+struct usb_endpoint_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bEndpointAddress;
+#define UE_GET_DIR(a) ((a) & 0x80)
+#define UE_SET_DIR(a,d) ((a) | (((d)&1) << 7))
+#define UE_DIR_IN 0x80 /* IN-token endpoint, fixed */
+#define UE_DIR_OUT 0x00 /* OUT-token endpoint, fixed */
+#define UE_DIR_RX 0xfd /* for internal use only! */
+#define UE_DIR_TX 0xfe /* for internal use only! */
+#define UE_DIR_ANY 0xff /* for internal use only! */
+#define UE_ADDR 0x0f
+#define UE_ADDR_ANY 0xff /* for internal use only! */
+#define UE_GET_ADDR(a) ((a) & UE_ADDR)
+ uByte bmAttributes;
+#define UE_XFERTYPE 0x03
+#define UE_CONTROL 0x00
+#define UE_ISOCHRONOUS 0x01
+#define UE_BULK 0x02
+#define UE_INTERRUPT 0x03
+#define UE_BULK_INTR 0xfe /* for internal use only! */
+#define UE_TYPE_ANY 0xff /* for internal use only! */
+#define UE_GET_XFERTYPE(a) ((a) & UE_XFERTYPE)
+#define UE_ISO_TYPE 0x0c
+#define UE_ISO_ASYNC 0x04
+#define UE_ISO_ADAPT 0x08
+#define UE_ISO_SYNC 0x0c
+#define UE_GET_ISO_TYPE(a) ((a) & UE_ISO_TYPE)
+#define UE_ISO_USAGE 0x30
+#define UE_ISO_USAGE_DATA 0x00
+#define UE_ISO_USAGE_FEEDBACK 0x10
+#define UE_ISO_USAGE_IMPLICT_FB 0x20
+#define UE_GET_ISO_USAGE(a) ((a) & UE_ISO_USAGE)
+ uWord wMaxPacketSize;
+#define UE_ZERO_MPS 0xFFFF /* for internal use only */
+ uByte bInterval;
+} __packed;
+typedef struct usb_endpoint_descriptor usb_endpoint_descriptor_t;
+
+struct usb_endpoint_ss_comp_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bMaxBurst;
+ uByte bmAttributes;
+#define UE_GET_BULK_STREAMS(x) ((x) & 0x0F)
+#define UE_GET_SS_ISO_MULT(x) ((x) & 0x03)
+ uWord wBytesPerInterval;
+} __packed;
+typedef struct usb_endpoint_ss_comp_descriptor
+ usb_endpoint_ss_comp_descriptor_t;
+
+struct usb_string_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord bString[126];
+ uByte bUnused;
+} __packed;
+typedef struct usb_string_descriptor usb_string_descriptor_t;
+
+#define USB_MAKE_STRING_DESC(m,name) \
+static const struct { \
+ uByte bLength; \
+ uByte bDescriptorType; \
+ uByte bData[sizeof((uint8_t []){m})]; \
+} __packed name = { \
+ .bLength = sizeof(name), \
+ .bDescriptorType = UDESC_STRING, \
+ .bData = { m }, \
+}
+
+struct usb_string_lang {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bData[2];
+} __packed;
+typedef struct usb_string_lang usb_string_lang_t;
+
+struct usb_hub_descriptor {
+ uByte bDescLength;
+ uByte bDescriptorType;
+ uByte bNbrPorts;
+ uWord wHubCharacteristics;
+#define UHD_PWR 0x0003
+#define UHD_PWR_GANGED 0x0000
+#define UHD_PWR_INDIVIDUAL 0x0001
+#define UHD_PWR_NO_SWITCH 0x0002
+#define UHD_COMPOUND 0x0004
+#define UHD_OC 0x0018
+#define UHD_OC_GLOBAL 0x0000
+#define UHD_OC_INDIVIDUAL 0x0008
+#define UHD_OC_NONE 0x0010
+#define UHD_TT_THINK 0x0060
+#define UHD_TT_THINK_8 0x0000
+#define UHD_TT_THINK_16 0x0020
+#define UHD_TT_THINK_24 0x0040
+#define UHD_TT_THINK_32 0x0060
+#define UHD_PORT_IND 0x0080
+ uByte bPwrOn2PwrGood; /* delay in 2 ms units */
+#define UHD_PWRON_FACTOR 2
+ uByte bHubContrCurrent;
+ uByte DeviceRemovable[32]; /* max 255 ports */
+#define UHD_NOT_REMOV(desc, i) \
+ (((desc)->DeviceRemovable[(i)/8] >> ((i) % 8)) & 1)
+ uByte PortPowerCtrlMask[1]; /* deprecated */
+} __packed;
+typedef struct usb_hub_descriptor usb_hub_descriptor_t;
+
+struct usb_hub_ss_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bNbrPorts;
+ uWord wHubCharacteristics;
+ uByte bPwrOn2PwrGood; /* delay in 2 ms units */
+ uByte bHubContrCurrent;
+ uByte bHubHdrDecLat;
+ uWord wHubDelay;
+ uByte DeviceRemovable[32]; /* max 255 ports */
+} __packed;
+typedef struct usb_hub_ss_descriptor usb_hub_ss_descriptor_t;
+
+/* minimum HUB descriptor (8-ports maximum) */
+struct usb_hub_descriptor_min {
+ uByte bDescLength;
+ uByte bDescriptorType;
+ uByte bNbrPorts;
+ uWord wHubCharacteristics;
+ uByte bPwrOn2PwrGood;
+ uByte bHubContrCurrent;
+ uByte DeviceRemovable[1];
+ uByte PortPowerCtrlMask[1];
+} __packed;
+typedef struct usb_hub_descriptor_min usb_hub_descriptor_min_t;
+
+struct usb_device_qualifier {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord bcdUSB;
+ uByte bDeviceClass;
+ uByte bDeviceSubClass;
+ uByte bDeviceProtocol;
+ uByte bMaxPacketSize0;
+ uByte bNumConfigurations;
+ uByte bReserved;
+} __packed;
+typedef struct usb_device_qualifier usb_device_qualifier_t;
+
+struct usb_otg_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bmAttributes;
+#define UOTG_SRP 0x01
+#define UOTG_HNP 0x02
+} __packed;
+typedef struct usb_otg_descriptor usb_otg_descriptor_t;
+
+/* OTG feature selectors */
+#define UOTG_B_HNP_ENABLE 3
+#define UOTG_A_HNP_SUPPORT 4
+#define UOTG_A_ALT_HNP_SUPPORT 5
+
+struct usb_status {
+ uWord wStatus;
+/* Device status flags */
+#define UDS_SELF_POWERED 0x0001
+#define UDS_REMOTE_WAKEUP 0x0002
+/* Endpoint status flags */
+#define UES_HALT 0x0001
+} __packed;
+typedef struct usb_status usb_status_t;
+
+struct usb_hub_status {
+ uWord wHubStatus;
+#define UHS_LOCAL_POWER 0x0001
+#define UHS_OVER_CURRENT 0x0002
+ uWord wHubChange;
+} __packed;
+typedef struct usb_hub_status usb_hub_status_t;
+
+struct usb_port_status {
+ uWord wPortStatus;
+#define UPS_CURRENT_CONNECT_STATUS 0x0001
+#define UPS_PORT_ENABLED 0x0002
+#define UPS_SUSPEND 0x0004
+#define UPS_OVERCURRENT_INDICATOR 0x0008
+#define UPS_RESET 0x0010
+#define UPS_PORT_L1 0x0020 /* USB 2.0 only */
+/* The link-state bits are valid for Super-Speed USB HUBs */
+#define UPS_PORT_LINK_STATE_GET(x) (((x) >> 5) & 0xF)
+#define UPS_PORT_LINK_STATE_SET(x) (((x) & 0xF) << 5)
+#define UPS_PORT_LS_U0 0x00
+#define UPS_PORT_LS_U1 0x01
+#define UPS_PORT_LS_U2 0x02
+#define UPS_PORT_LS_U3 0x03
+#define UPS_PORT_LS_SS_DIS 0x04
+#define UPS_PORT_LS_RX_DET 0x05
+#define UPS_PORT_LS_SS_INA 0x06
+#define UPS_PORT_LS_POLL 0x07
+#define UPS_PORT_LS_RECOVER 0x08
+#define UPS_PORT_LS_HOT_RST 0x09
+#define UPS_PORT_LS_COMP_MODE 0x0A
+#define UPS_PORT_LS_LOOPBACK 0x0B
+#define UPS_PORT_LS_RESUME 0x0F
+#define UPS_PORT_POWER 0x0100
+#define UPS_PORT_POWER_SS 0x0200 /* super-speed only */
+#define UPS_LOW_SPEED 0x0200
+#define UPS_HIGH_SPEED 0x0400
+#define UPS_OTHER_SPEED 0x0600 /* currently FreeBSD specific */
+#define UPS_PORT_TEST 0x0800
+#define UPS_PORT_INDICATOR 0x1000
+#define UPS_PORT_MODE_DEVICE 0x8000 /* currently FreeBSD specific */
+ uWord wPortChange;
+#define UPS_C_CONNECT_STATUS 0x0001
+#define UPS_C_PORT_ENABLED 0x0002
+#define UPS_C_SUSPEND 0x0004
+#define UPS_C_OVERCURRENT_INDICATOR 0x0008
+#define UPS_C_PORT_RESET 0x0010
+#define UPS_C_PORT_L1 0x0020 /* USB 2.0 only */
+#define UPS_C_BH_PORT_RESET 0x0020 /* USB 3.0 only */
+#define UPS_C_PORT_LINK_STATE 0x0040
+#define UPS_C_PORT_CONFIG_ERROR 0x0080
+} __packed;
+typedef struct usb_port_status usb_port_status_t;
+
+/*
+ * The "USB_SPEED" macros defines all the supported USB speeds.
+ */
+enum usb_dev_speed {
+ USB_SPEED_VARIABLE,
+ USB_SPEED_LOW,
+ USB_SPEED_FULL,
+ USB_SPEED_HIGH,
+ USB_SPEED_SUPER,
+};
+#define USB_SPEED_MAX (USB_SPEED_SUPER+1)
+
+/*
+ * The "USB_REV" macros defines all the supported USB revisions.
+ */
+enum usb_revision {
+ USB_REV_UNKNOWN,
+ USB_REV_PRE_1_0,
+ USB_REV_1_0,
+ USB_REV_1_1,
+ USB_REV_2_0,
+ USB_REV_2_5,
+ USB_REV_3_0
+};
+#define USB_REV_MAX (USB_REV_3_0+1)
+
+/*
+ * Supported host controller modes.
+ */
+enum usb_hc_mode {
+ USB_MODE_HOST, /* initiates transfers */
+ USB_MODE_DEVICE, /* bus transfer target */
+ USB_MODE_DUAL /* can be host or device */
+};
+#define USB_MODE_MAX (USB_MODE_DUAL+1)
+
+/*
+ * The "USB_STATE" enums define all the supported device states.
+ */
+enum usb_dev_state {
+ USB_STATE_DETACHED,
+ USB_STATE_ATTACHED,
+ USB_STATE_POWERED,
+ USB_STATE_ADDRESSED,
+ USB_STATE_CONFIGURED,
+};
+#define USB_STATE_MAX (USB_STATE_CONFIGURED+1)
+
+/*
+ * The "USB_EP_MODE" macros define all the currently supported
+ * endpoint modes.
+ */
+enum usb_ep_mode {
+ USB_EP_MODE_DEFAULT,
+ USB_EP_MODE_STREAMS, /* USB3.0 specific */
+ USB_EP_MODE_HW_MASS_STORAGE,
+ USB_EP_MODE_HW_SERIAL,
+ USB_EP_MODE_HW_ETHERNET_CDC,
+ USB_EP_MODE_HW_ETHERNET_NCM,
+ USB_EP_MODE_MAX
+};
+#endif /* _USB_STANDARD_H_ */
diff --git a/usr/contrib/freebsd/dev/usb/usb_endian.h b/usr/contrib/freebsd/dev/usb/usb_endian.h
new file mode 100644
index 0000000000..0bbcb9bf82
--- /dev/null
+++ b/usr/contrib/freebsd/dev/usb/usb_endian.h
@@ -0,0 +1,121 @@
+/* $FreeBSD$ */
+/*
+ * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _USB_ENDIAN_H_
+#define _USB_ENDIAN_H_
+
+#ifndef USB_GLOBAL_INCLUDE_FILE
+#include <sys/stdint.h>
+#include <sys/endian.h>
+#endif
+
+/*
+ * Declare the basic USB record types. USB records have an alignment
+ * of 1 byte and are always packed.
+ */
+typedef uint8_t uByte;
+typedef uint8_t uWord[2];
+typedef uint8_t uDWord[4];
+typedef uint8_t uQWord[8];
+
+/*
+ * Define a set of macros that can get and set data independent of
+ * CPU endianness and CPU alignment requirements:
+ */
+#define UGETB(w) \
+ ((w)[0])
+
+#define UGETW(w) \
+ ((w)[0] | \
+ (((uint16_t)((w)[1])) << 8))
+
+#define UGETDW(w) \
+ ((w)[0] | \
+ (((uint16_t)((w)[1])) << 8) | \
+ (((uint32_t)((w)[2])) << 16) | \
+ (((uint32_t)((w)[3])) << 24))
+
+#define UGETQW(w) \
+ ((w)[0] | \
+ (((uint16_t)((w)[1])) << 8) | \
+ (((uint32_t)((w)[2])) << 16) | \
+ (((uint32_t)((w)[3])) << 24) | \
+ (((uint64_t)((w)[4])) << 32) | \
+ (((uint64_t)((w)[5])) << 40) | \
+ (((uint64_t)((w)[6])) << 48) | \
+ (((uint64_t)((w)[7])) << 56))
+
+#define USETB(w,v) do { \
+ (w)[0] = (uint8_t)(v); \
+} while (0)
+
+#define USETW(w,v) do { \
+ (w)[0] = (uint8_t)(v); \
+ (w)[1] = (uint8_t)((v) >> 8); \
+} while (0)
+
+#define USETDW(w,v) do { \
+ (w)[0] = (uint8_t)(v); \
+ (w)[1] = (uint8_t)((v) >> 8); \
+ (w)[2] = (uint8_t)((v) >> 16); \
+ (w)[3] = (uint8_t)((v) >> 24); \
+} while (0)
+
+#define USETQW(w,v) do { \
+ (w)[0] = (uint8_t)(v); \
+ (w)[1] = (uint8_t)((v) >> 8); \
+ (w)[2] = (uint8_t)((v) >> 16); \
+ (w)[3] = (uint8_t)((v) >> 24); \
+ (w)[4] = (uint8_t)((v) >> 32); \
+ (w)[5] = (uint8_t)((v) >> 40); \
+ (w)[6] = (uint8_t)((v) >> 48); \
+ (w)[7] = (uint8_t)((v) >> 56); \
+} while (0)
+
+#define USETW2(w,b1,b0) do { \
+ (w)[0] = (uint8_t)(b0); \
+ (w)[1] = (uint8_t)(b1); \
+} while (0)
+
+#define USETW4(w,b3,b2,b1,b0) do { \
+ (w)[0] = (uint8_t)(b0); \
+ (w)[1] = (uint8_t)(b1); \
+ (w)[2] = (uint8_t)(b2); \
+ (w)[3] = (uint8_t)(b3); \
+} while (0)
+
+#define USETW8(w,b7,b6,b5,b4,b3,b2,b1,b0) do { \
+ (w)[0] = (uint8_t)(b0); \
+ (w)[1] = (uint8_t)(b1); \
+ (w)[2] = (uint8_t)(b2); \
+ (w)[3] = (uint8_t)(b3); \
+ (w)[4] = (uint8_t)(b4); \
+ (w)[5] = (uint8_t)(b5); \
+ (w)[6] = (uint8_t)(b6); \
+ (w)[7] = (uint8_t)(b7); \
+} while (0)
+
+#endif /* _USB_ENDIAN_H_ */
diff --git a/usr/contrib/freebsd/dev/usb/usb_freebsd.h b/usr/contrib/freebsd/dev/usb/usb_freebsd.h
new file mode 100644
index 0000000000..3bc9d2c1eb
--- /dev/null
+++ b/usr/contrib/freebsd/dev/usb/usb_freebsd.h
@@ -0,0 +1,101 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Including this file is mandatory for all USB related c-files in the kernel.
+ */
+
+#ifndef _USB_FREEBSD_H_
+#define _USB_FREEBSD_H_
+
+/* Default USB configuration */
+#define USB_HAVE_UGEN 1
+#define USB_HAVE_DEVCTL 1
+#define USB_HAVE_BUSDMA 1
+#define USB_HAVE_COMPAT_LINUX 1
+#define USB_HAVE_USER_IO 1
+#define USB_HAVE_MBUF 1
+#define USB_HAVE_TT_SUPPORT 1
+#define USB_HAVE_POWERD 1
+#define USB_HAVE_MSCTEST 1
+#define USB_HAVE_MSCTEST_DETACH 1
+#define USB_HAVE_PF 1
+#define USB_HAVE_ROOT_MOUNT_HOLD 1
+#define USB_HAVE_ID_SECTION 1
+#define USB_HAVE_PER_BUS_PROCESS 1
+#define USB_HAVE_FIXED_ENDPOINT 0
+#define USB_HAVE_FIXED_IFACE 0
+#define USB_HAVE_FIXED_CONFIG 0
+#define USB_HAVE_FIXED_PORT 0
+#define USB_HAVE_DISABLE_ENUM 1
+
+/* define zero ticks callout value */
+#define USB_CALLOUT_ZERO_TICKS 1
+
+#define USB_TD_GET_PROC(td) (td)->td_proc
+#define USB_PROC_GET_GID(td) (td)->p_pgid
+
+#if (!defined(USB_HOST_ALIGN)) || (USB_HOST_ALIGN <= 0)
+/* Use default value. */
+#undef USB_HOST_ALIGN
+#if defined(__arm__) || defined(__mips__) || defined(__powerpc__)
+#define USB_HOST_ALIGN 32 /* Arm and MIPS need at least this much, if not more */
+#else
+#define USB_HOST_ALIGN 8 /* bytes, must be power of two */
+#endif
+#endif
+/* Sanity check for USB_HOST_ALIGN: Verify power of two. */
+#if ((-USB_HOST_ALIGN) & USB_HOST_ALIGN) != USB_HOST_ALIGN
+#error "USB_HOST_ALIGN is not power of two."
+#endif
+#define USB_FS_ISOC_UFRAME_MAX 4 /* exclusive unit */
+#define USB_BUS_MAX 256 /* units */
+#define USB_MAX_DEVICES 128 /* units */
+#define USB_CONFIG_MAX 65535 /* bytes */
+#define USB_IFACE_MAX 32 /* units */
+#define USB_FIFO_MAX 128 /* units */
+#define USB_MAX_EP_STREAMS 8 /* units */
+#define USB_MAX_EP_UNITS 32 /* units */
+#define USB_MAX_PORTS 255 /* units */
+
+#define USB_MAX_FS_ISOC_FRAMES_PER_XFER (120) /* units */
+#define USB_MAX_HS_ISOC_FRAMES_PER_XFER (8*120) /* units */
+
+#define USB_HUB_MAX_DEPTH 5
+#define USB_EP0_BUFSIZE 1024 /* bytes */
+#define USB_CS_RESET_LIMIT 20 /* failures = 20 * 50 ms = 1sec */
+
+#define USB_MAX_AUTO_QUIRK 8 /* maximum number of dynamic quirks */
+
+typedef uint32_t usb_timeout_t; /* milliseconds */
+typedef uint32_t usb_frlength_t; /* bytes */
+typedef uint32_t usb_frcount_t; /* units */
+typedef uint32_t usb_size_t; /* bytes */
+typedef uint32_t usb_ticks_t; /* system defined */
+typedef uint16_t usb_power_mask_t; /* see "USB_HW_POWER_XXX" */
+typedef uint16_t usb_stream_t; /* stream ID */
+
+#endif /* _USB_FREEBSD_H_ */
diff --git a/usr/contrib/freebsd/dev/usb/usbdi.h b/usr/contrib/freebsd/dev/usb/usbdi.h
new file mode 100644
index 0000000000..202ad89fa7
--- /dev/null
+++ b/usr/contrib/freebsd/dev/usb/usbdi.h
@@ -0,0 +1,657 @@
+/*-
+ * Copyright (c) 2009 Andrew Thompson
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _USB_USBDI_H_
+#define _USB_USBDI_H_
+
+struct usb_fifo;
+struct usb_xfer;
+struct usb_device;
+struct usb_attach_arg;
+struct usb_interface;
+struct usb_endpoint;
+struct usb_page_cache;
+struct usb_page_search;
+struct usb_process;
+struct usb_proc_msg;
+struct usb_mbuf;
+struct usb_fs_privdata;
+struct mbuf;
+
+typedef enum { /* keep in sync with usb_errstr_table */
+ USB_ERR_NORMAL_COMPLETION = 0,
+ USB_ERR_PENDING_REQUESTS, /* 1 */
+ USB_ERR_NOT_STARTED, /* 2 */
+ USB_ERR_INVAL, /* 3 */
+ USB_ERR_NOMEM, /* 4 */
+ USB_ERR_CANCELLED, /* 5 */
+ USB_ERR_BAD_ADDRESS, /* 6 */
+ USB_ERR_BAD_BUFSIZE, /* 7 */
+ USB_ERR_BAD_FLAG, /* 8 */
+ USB_ERR_NO_CALLBACK, /* 9 */
+ USB_ERR_IN_USE, /* 10 */
+ USB_ERR_NO_ADDR, /* 11 */
+ USB_ERR_NO_PIPE, /* 12 */
+ USB_ERR_ZERO_NFRAMES, /* 13 */
+ USB_ERR_ZERO_MAXP, /* 14 */
+ USB_ERR_SET_ADDR_FAILED, /* 15 */
+ USB_ERR_NO_POWER, /* 16 */
+ USB_ERR_TOO_DEEP, /* 17 */
+ USB_ERR_IOERROR, /* 18 */
+ USB_ERR_NOT_CONFIGURED, /* 19 */
+ USB_ERR_TIMEOUT, /* 20 */
+ USB_ERR_SHORT_XFER, /* 21 */
+ USB_ERR_STALLED, /* 22 */
+ USB_ERR_INTERRUPTED, /* 23 */
+ USB_ERR_DMA_LOAD_FAILED, /* 24 */
+ USB_ERR_BAD_CONTEXT, /* 25 */
+ USB_ERR_NO_ROOT_HUB, /* 26 */
+ USB_ERR_NO_INTR_THREAD, /* 27 */
+ USB_ERR_NOT_LOCKED, /* 28 */
+ USB_ERR_MAX
+} usb_error_t;
+
+/*
+ * Flags for transfers
+ */
+#define USB_FORCE_SHORT_XFER 0x0001 /* force a short transmit last */
+#define USB_SHORT_XFER_OK 0x0004 /* allow short reads */
+#define USB_DELAY_STATUS_STAGE 0x0010 /* insert delay before STATUS stage */
+#define USB_USER_DATA_PTR 0x0020 /* internal flag */
+#define USB_MULTI_SHORT_OK 0x0040 /* allow multiple short frames */
+#define USB_MANUAL_STATUS 0x0080 /* manual ctrl status */
+
+#define USB_NO_TIMEOUT 0
+#define USB_DEFAULT_TIMEOUT 5000 /* 5000 ms = 5 seconds */
+
+#if defined(_KERNEL)
+/* typedefs */
+
+typedef void (usb_callback_t)(struct usb_xfer *, usb_error_t);
+typedef void (usb_proc_callback_t)(struct usb_proc_msg *);
+typedef usb_error_t (usb_handle_req_t)(struct usb_device *,
+ struct usb_device_request *, const void **, uint16_t *);
+
+typedef int (usb_fifo_open_t)(struct usb_fifo *fifo, int fflags);
+typedef void (usb_fifo_close_t)(struct usb_fifo *fifo, int fflags);
+typedef int (usb_fifo_ioctl_t)(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags);
+typedef void (usb_fifo_cmd_t)(struct usb_fifo *fifo);
+typedef void (usb_fifo_filter_t)(struct usb_fifo *fifo, struct usb_mbuf *m);
+
+
+/* USB events */
+#ifndef USB_GLOBAL_INCLUDE_FILE
+#include <sys/eventhandler.h>
+#endif
+typedef void (*usb_dev_configured_t)(void *, struct usb_device *,
+ struct usb_attach_arg *);
+EVENTHANDLER_DECLARE(usb_dev_configured, usb_dev_configured_t);
+
+/*
+ * The following macros are used used to convert milliseconds into
+ * HZ. We use 1024 instead of 1000 milliseconds per second to save a
+ * full division.
+ */
+#define USB_MS_HZ 1024
+
+#define USB_MS_TO_TICKS(ms) \
+ (((uint32_t)((((uint32_t)(ms)) * ((uint32_t)(hz))) + USB_MS_HZ - 1)) / USB_MS_HZ)
+
+/*
+ * Common queue structure for USB transfers.
+ */
+struct usb_xfer_queue {
+ TAILQ_HEAD(, usb_xfer) head;
+ struct usb_xfer *curr; /* current USB transfer processed */
+ void (*command) (struct usb_xfer_queue *pq);
+ uint8_t recurse_1:1;
+ uint8_t recurse_2:1;
+ uint8_t recurse_3:1;
+ uint8_t reserved:5;
+};
+
+/*
+ * The following structure defines an USB endpoint
+ * USB endpoint.
+ */
+struct usb_endpoint {
+ /* queue of USB transfers */
+ struct usb_xfer_queue endpoint_q[USB_MAX_EP_STREAMS];
+
+ struct usb_endpoint_descriptor *edesc;
+ struct usb_endpoint_ss_comp_descriptor *ecomp;
+ const struct usb_pipe_methods *methods; /* set by HC driver */
+
+ uint16_t isoc_next;
+
+ uint8_t toggle_next:1; /* next data toggle value */
+ uint8_t is_stalled:1; /* set if endpoint is stalled */
+ uint8_t is_synced:1; /* set if we a synchronised */
+ uint8_t unused:5;
+ uint8_t iface_index; /* not used by "default endpoint" */
+
+ uint8_t refcount_alloc; /* allocation refcount */
+ uint8_t refcount_bw; /* bandwidth refcount */
+#define USB_EP_REF_MAX 0x3f
+
+ /* High-Speed resource allocation (valid if "refcount_bw" > 0) */
+
+ uint8_t usb_smask; /* USB start mask */
+ uint8_t usb_cmask; /* USB complete mask */
+ uint8_t usb_uframe; /* USB microframe */
+
+ /* USB endpoint mode, see USB_EP_MODE_XXX */
+
+ uint8_t ep_mode;
+};
+
+/*
+ * The following structure defines an USB interface.
+ */
+struct usb_interface {
+ struct usb_interface_descriptor *idesc;
+ device_t subdev;
+ uint8_t alt_index;
+ uint8_t parent_iface_index;
+
+ /* Linux compat */
+ struct usb_host_interface *altsetting;
+ struct usb_host_interface *cur_altsetting;
+ struct usb_device *linux_udev;
+ void *bsd_priv_sc; /* device specific information */
+ char *pnpinfo; /* additional PnP-info for this interface */
+ uint8_t num_altsetting; /* number of alternate settings */
+ uint8_t bsd_iface_index;
+};
+
+/*
+ * The following structure defines a set of USB transfer flags.
+ */
+struct usb_xfer_flags {
+ uint8_t force_short_xfer:1; /* force a short transmit transfer
+ * last */
+ uint8_t short_xfer_ok:1; /* allow short receive transfers */
+ uint8_t short_frames_ok:1; /* allow short frames */
+ uint8_t pipe_bof:1; /* block pipe on failure */
+ uint8_t proxy_buffer:1; /* makes buffer size a factor of
+ * "max_frame_size" */
+ uint8_t ext_buffer:1; /* uses external DMA buffer */
+ uint8_t manual_status:1; /* non automatic status stage on
+ * control transfers */
+ uint8_t no_pipe_ok:1; /* set if "USB_ERR_NO_PIPE" error can
+ * be ignored */
+ uint8_t stall_pipe:1; /* set if the endpoint belonging to
+ * this USB transfer should be stalled
+ * before starting this transfer! */
+ uint8_t pre_scale_frames:1; /* "usb_config->frames" is
+ * assumed to give the
+ * buffering time in
+ * milliseconds and is
+ * converted into the nearest
+ * number of frames when the
+ * USB transfer is setup. This
+ * option only has effect for
+ * ISOCHRONOUS transfers.
+ */
+};
+
+/*
+ * The following structure define an USB configuration, that basically
+ * is used when setting up an USB transfer.
+ */
+struct usb_config {
+ usb_callback_t *callback; /* USB transfer callback */
+ usb_frlength_t bufsize; /* total pipe buffer size in bytes */
+ usb_frcount_t frames; /* maximum number of USB frames */
+ usb_timeout_t interval; /* interval in milliseconds */
+#define USB_DEFAULT_INTERVAL 0
+ usb_timeout_t timeout; /* transfer timeout in milliseconds */
+ struct usb_xfer_flags flags; /* transfer flags */
+ usb_stream_t stream_id; /* USB3.0 specific */
+ enum usb_hc_mode usb_mode; /* host or device mode */
+ uint8_t type; /* pipe type */
+ uint8_t endpoint; /* pipe number */
+ uint8_t direction; /* pipe direction */
+ uint8_t ep_index; /* pipe index match to use */
+ uint8_t if_index; /* "ifaces" index to use */
+};
+
+/*
+ * Use these macro when defining USB device ID arrays if you want to
+ * have your driver module automatically loaded in host, device or
+ * both modes respectively:
+ */
+#if USB_HAVE_ID_SECTION
+#define STRUCT_USB_HOST_ID \
+ struct usb_device_id __section("usb_host_id")
+#define STRUCT_USB_DEVICE_ID \
+ struct usb_device_id __section("usb_device_id")
+#define STRUCT_USB_DUAL_ID \
+ struct usb_device_id __section("usb_dual_id")
+#else
+#define STRUCT_USB_HOST_ID \
+ struct usb_device_id
+#define STRUCT_USB_DEVICE_ID \
+ struct usb_device_id
+#define STRUCT_USB_DUAL_ID \
+ struct usb_device_id
+#endif /* USB_HAVE_ID_SECTION */
+
+/*
+ * The following structure is used when looking up an USB driver for
+ * an USB device. It is inspired by the Linux structure called
+ * "usb_device_id".
+ */
+struct usb_device_id {
+
+ /* Select which fields to match against */
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint16_t
+ match_flag_vendor:1,
+ match_flag_product:1,
+ match_flag_dev_lo:1,
+ match_flag_dev_hi:1,
+
+ match_flag_dev_class:1,
+ match_flag_dev_subclass:1,
+ match_flag_dev_protocol:1,
+ match_flag_int_class:1,
+
+ match_flag_int_subclass:1,
+ match_flag_int_protocol:1,
+ match_flag_unused:6;
+#else
+ uint16_t
+ match_flag_unused:6,
+ match_flag_int_protocol:1,
+ match_flag_int_subclass:1,
+
+ match_flag_int_class:1,
+ match_flag_dev_protocol:1,
+ match_flag_dev_subclass:1,
+ match_flag_dev_class:1,
+
+ match_flag_dev_hi:1,
+ match_flag_dev_lo:1,
+ match_flag_product:1,
+ match_flag_vendor:1;
+#endif
+
+ /* Used for product specific matches; the BCD range is inclusive */
+ uint16_t idVendor;
+ uint16_t idProduct;
+ uint16_t bcdDevice_lo;
+ uint16_t bcdDevice_hi;
+
+ /* Used for device class matches */
+ uint8_t bDeviceClass;
+ uint8_t bDeviceSubClass;
+ uint8_t bDeviceProtocol;
+
+ /* Used for interface class matches */
+ uint8_t bInterfaceClass;
+ uint8_t bInterfaceSubClass;
+ uint8_t bInterfaceProtocol;
+
+#if USB_HAVE_COMPAT_LINUX
+ /* which fields to match against */
+ uint16_t match_flags;
+#define USB_DEVICE_ID_MATCH_VENDOR 0x0001
+#define USB_DEVICE_ID_MATCH_PRODUCT 0x0002
+#define USB_DEVICE_ID_MATCH_DEV_LO 0x0004
+#define USB_DEVICE_ID_MATCH_DEV_HI 0x0008
+#define USB_DEVICE_ID_MATCH_DEV_CLASS 0x0010
+#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS 0x0020
+#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL 0x0040
+#define USB_DEVICE_ID_MATCH_INT_CLASS 0x0080
+#define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100
+#define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200
+#endif
+
+ /* Hook for driver specific information */
+ unsigned long driver_info;
+} __aligned(32);
+
+#define USB_STD_PNP_INFO "M16:mask;U16:vendor;U16:product;L16:product;G16:product;" \
+ "U8:devclass;U8:devsubclass;U8:devprotocol;" \
+ "U8:intclass;U8:intsubclass;U8:intprotocol;"
+#define USB_STD_PNP_HOST_INFO USB_STD_PNP_INFO "T:mode=host;"
+#define USB_STD_PNP_DEVICE_INFO USB_STD_PNP_INFO "T:mode=device;"
+#define USB_PNP_HOST_INFO(table) \
+ MODULE_PNP_INFO(USB_STD_PNP_HOST_INFO, usb, table, table, sizeof(table[0]), \
+ sizeof(table) / sizeof(table[0]))
+#define USB_PNP_DEVICE_INFO(table) \
+ MODULE_PNP_INFO(USB_STD_PNP_DEVICE_INFO, usb, table, table, sizeof(table[0]), \
+ sizeof(table) / sizeof(table[0]))
+#define USB_PNP_DUAL_INFO(table) \
+ MODULE_PNP_INFO(USB_STD_PNP_INFO, usb, table, table, sizeof(table[0]), \
+ sizeof(table) / sizeof(table[0]))
+
+/* check that the size of the structure above is correct */
+extern char usb_device_id_assert[(sizeof(struct usb_device_id) == 32) ? 1 : -1];
+
+#define USB_VENDOR(vend) \
+ .match_flag_vendor = 1, .idVendor = (vend)
+
+#define USB_PRODUCT(prod) \
+ .match_flag_product = 1, .idProduct = (prod)
+
+#define USB_VP(vend,prod) \
+ USB_VENDOR(vend), USB_PRODUCT(prod)
+
+#define USB_VPI(vend,prod,info) \
+ USB_VENDOR(vend), USB_PRODUCT(prod), USB_DRIVER_INFO(info)
+
+#define USB_DEV_BCD_GTEQ(lo) /* greater than or equal */ \
+ .match_flag_dev_lo = 1, .bcdDevice_lo = (lo)
+
+#define USB_DEV_BCD_LTEQ(hi) /* less than or equal */ \
+ .match_flag_dev_hi = 1, .bcdDevice_hi = (hi)
+
+#define USB_DEV_CLASS(dc) \
+ .match_flag_dev_class = 1, .bDeviceClass = (dc)
+
+#define USB_DEV_SUBCLASS(dsc) \
+ .match_flag_dev_subclass = 1, .bDeviceSubClass = (dsc)
+
+#define USB_DEV_PROTOCOL(dp) \
+ .match_flag_dev_protocol = 1, .bDeviceProtocol = (dp)
+
+#define USB_IFACE_CLASS(ic) \
+ .match_flag_int_class = 1, .bInterfaceClass = (ic)
+
+#define USB_IFACE_SUBCLASS(isc) \
+ .match_flag_int_subclass = 1, .bInterfaceSubClass = (isc)
+
+#define USB_IFACE_PROTOCOL(ip) \
+ .match_flag_int_protocol = 1, .bInterfaceProtocol = (ip)
+
+#define USB_IF_CSI(class,subclass,info) \
+ USB_IFACE_CLASS(class), USB_IFACE_SUBCLASS(subclass), USB_DRIVER_INFO(info)
+
+#define USB_DRIVER_INFO(n) \
+ .driver_info = (n)
+
+#define USB_GET_DRIVER_INFO(did) \
+ (did)->driver_info
+
+/*
+ * The following structure keeps information that is used to match
+ * against an array of "usb_device_id" elements.
+ */
+struct usbd_lookup_info {
+ uint16_t idVendor;
+ uint16_t idProduct;
+ uint16_t bcdDevice;
+ uint8_t bDeviceClass;
+ uint8_t bDeviceSubClass;
+ uint8_t bDeviceProtocol;
+ uint8_t bInterfaceClass;
+ uint8_t bInterfaceSubClass;
+ uint8_t bInterfaceProtocol;
+ uint8_t bIfaceIndex;
+ uint8_t bIfaceNum;
+ uint8_t bConfigIndex;
+ uint8_t bConfigNum;
+};
+
+/* Structure used by probe and attach */
+
+struct usb_attach_arg {
+ struct usbd_lookup_info info;
+ device_t temp_dev; /* for internal use */
+ unsigned long driver_info; /* for internal use */
+ void *driver_ivar;
+ struct usb_device *device; /* current device */
+ struct usb_interface *iface; /* current interface */
+ enum usb_hc_mode usb_mode; /* host or device mode */
+ uint8_t port;
+ uint8_t dev_state;
+#define UAA_DEV_READY 0
+#define UAA_DEV_DISABLED 1
+#define UAA_DEV_EJECTING 2
+};
+
+/*
+ * The following is a wrapper for the callout structure to ease
+ * porting the code to other platforms.
+ */
+struct usb_callout {
+ struct callout co;
+};
+#define usb_callout_init_mtx(c,m,f) callout_init_mtx(&(c)->co,m,f)
+#define usb_callout_reset(c,t,f,d) callout_reset(&(c)->co,t,f,d)
+#define usb_callout_stop(c) callout_stop(&(c)->co)
+#define usb_callout_drain(c) callout_drain(&(c)->co)
+#define usb_callout_pending(c) callout_pending(&(c)->co)
+
+/* USB transfer states */
+
+#define USB_ST_SETUP 0
+#define USB_ST_TRANSFERRED 1
+#define USB_ST_ERROR 2
+
+/* USB handle request states */
+#define USB_HR_NOT_COMPLETE 0
+#define USB_HR_COMPLETE_OK 1
+#define USB_HR_COMPLETE_ERR 2
+
+/*
+ * The following macro will return the current state of an USB
+ * transfer like defined by the "USB_ST_XXX" enums.
+ */
+#define USB_GET_STATE(xfer) (usbd_xfer_state(xfer))
+
+/*
+ * The following structure defines the USB process message header.
+ */
+struct usb_proc_msg {
+ TAILQ_ENTRY(usb_proc_msg) pm_qentry;
+ usb_proc_callback_t *pm_callback;
+ usb_size_t pm_num;
+};
+
+#define USB_FIFO_TX 0
+#define USB_FIFO_RX 1
+
+/*
+ * Locking note for the following functions. All the
+ * "usb_fifo_cmd_t" and "usb_fifo_filter_t" functions are called
+ * locked. The others are called unlocked.
+ */
+struct usb_fifo_methods {
+ usb_fifo_open_t *f_open;
+ usb_fifo_close_t *f_close;
+ usb_fifo_ioctl_t *f_ioctl;
+ /*
+ * NOTE: The post-ioctl callback is called after the USB reference
+ * gets locked in the IOCTL handler:
+ */
+ usb_fifo_ioctl_t *f_ioctl_post;
+ usb_fifo_cmd_t *f_start_read;
+ usb_fifo_cmd_t *f_stop_read;
+ usb_fifo_cmd_t *f_start_write;
+ usb_fifo_cmd_t *f_stop_write;
+ usb_fifo_filter_t *f_filter_read;
+ usb_fifo_filter_t *f_filter_write;
+ const char *basename[4];
+ const char *postfix[4];
+};
+
+struct usb_fifo_sc {
+ struct usb_fifo *fp[2];
+ struct usb_fs_privdata *dev;
+};
+
+const char *usbd_errstr(usb_error_t error);
+void *usbd_find_descriptor(struct usb_device *udev, void *id,
+ uint8_t iface_index, uint8_t type, uint8_t type_mask,
+ uint8_t subtype, uint8_t subtype_mask);
+struct usb_config_descriptor *usbd_get_config_descriptor(
+ struct usb_device *udev);
+struct usb_device_descriptor *usbd_get_device_descriptor(
+ struct usb_device *udev);
+struct usb_interface *usbd_get_iface(struct usb_device *udev,
+ uint8_t iface_index);
+struct usb_interface_descriptor *usbd_get_interface_descriptor(
+ struct usb_interface *iface);
+struct usb_endpoint *usbd_get_endpoint(struct usb_device *udev, uint8_t iface_index,
+ const struct usb_config *setup);
+struct usb_endpoint *usbd_get_ep_by_addr(struct usb_device *udev, uint8_t ea_val);
+usb_error_t usbd_interface_count(struct usb_device *udev, uint8_t *count);
+enum usb_hc_mode usbd_get_mode(struct usb_device *udev);
+enum usb_dev_speed usbd_get_speed(struct usb_device *udev);
+void device_set_usb_desc(device_t dev);
+void usb_pause_mtx(struct mtx *mtx, int _ticks);
+usb_error_t usbd_set_pnpinfo(struct usb_device *udev,
+ uint8_t iface_index, const char *pnpinfo);
+usb_error_t usbd_add_dynamic_quirk(struct usb_device *udev,
+ uint16_t quirk);
+usb_error_t usbd_set_endpoint_mode(struct usb_device *udev,
+ struct usb_endpoint *ep, uint8_t ep_mode);
+uint8_t usbd_get_endpoint_mode(struct usb_device *udev,
+ struct usb_endpoint *ep);
+
+const struct usb_device_id *usbd_lookup_id_by_info(
+ const struct usb_device_id *id, usb_size_t sizeof_id,
+ const struct usbd_lookup_info *info);
+int usbd_lookup_id_by_uaa(const struct usb_device_id *id,
+ usb_size_t sizeof_id, struct usb_attach_arg *uaa);
+
+usb_error_t usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx,
+ struct usb_device_request *req, void *data, uint16_t flags,
+ uint16_t *actlen, usb_timeout_t timeout);
+#define usbd_do_request(u,m,r,d) \
+ usbd_do_request_flags(u,m,r,d,0,NULL,USB_DEFAULT_TIMEOUT)
+
+uint8_t usbd_clear_stall_callback(struct usb_xfer *xfer1,
+ struct usb_xfer *xfer2);
+uint8_t usbd_get_interface_altindex(struct usb_interface *iface);
+usb_error_t usbd_set_alt_interface_index(struct usb_device *udev,
+ uint8_t iface_index, uint8_t alt_index);
+uint32_t usbd_get_isoc_fps(struct usb_device *udev);
+usb_error_t usbd_transfer_setup(struct usb_device *udev,
+ const uint8_t *ifaces, struct usb_xfer **pxfer,
+ const struct usb_config *setup_start, uint16_t n_setup,
+ void *priv_sc, struct mtx *priv_mtx);
+void usbd_transfer_submit(struct usb_xfer *xfer);
+void usbd_transfer_clear_stall(struct usb_xfer *xfer);
+void usbd_transfer_drain(struct usb_xfer *xfer);
+uint8_t usbd_transfer_pending(struct usb_xfer *xfer);
+void usbd_transfer_start(struct usb_xfer *xfer);
+void usbd_transfer_stop(struct usb_xfer *xfer);
+void usbd_transfer_unsetup(struct usb_xfer **pxfer, uint16_t n_setup);
+void usbd_transfer_poll(struct usb_xfer **ppxfer, uint16_t max);
+void usbd_set_parent_iface(struct usb_device *udev, uint8_t iface_index,
+ uint8_t parent_index);
+uint8_t usbd_get_bus_index(struct usb_device *udev);
+uint8_t usbd_get_device_index(struct usb_device *udev);
+void usbd_set_power_mode(struct usb_device *udev, uint8_t power_mode);
+uint8_t usbd_filter_power_mode(struct usb_device *udev, uint8_t power_mode);
+uint8_t usbd_device_attached(struct usb_device *udev);
+
+usb_frlength_t
+ usbd_xfer_old_frame_length(struct usb_xfer *xfer, usb_frcount_t frindex);
+void usbd_xfer_status(struct usb_xfer *xfer, int *actlen, int *sumlen,
+ int *aframes, int *nframes);
+struct usb_page_cache *usbd_xfer_get_frame(struct usb_xfer *, usb_frcount_t);
+void *usbd_xfer_get_frame_buffer(struct usb_xfer *, usb_frcount_t);
+void *usbd_xfer_softc(struct usb_xfer *xfer);
+void *usbd_xfer_get_priv(struct usb_xfer *xfer);
+void usbd_xfer_set_priv(struct usb_xfer *xfer, void *);
+void usbd_xfer_set_interval(struct usb_xfer *xfer, int);
+uint8_t usbd_xfer_state(struct usb_xfer *xfer);
+void usbd_xfer_set_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex,
+ void *ptr, usb_frlength_t len);
+void usbd_xfer_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex,
+ void **ptr, int *len);
+void usbd_xfer_set_frame_offset(struct usb_xfer *xfer, usb_frlength_t offset,
+ usb_frcount_t frindex);
+usb_frlength_t usbd_xfer_max_len(struct usb_xfer *xfer);
+usb_frlength_t usbd_xfer_max_framelen(struct usb_xfer *xfer);
+usb_frcount_t usbd_xfer_max_frames(struct usb_xfer *xfer);
+uint8_t usbd_xfer_get_fps_shift(struct usb_xfer *xfer);
+usb_frlength_t usbd_xfer_frame_len(struct usb_xfer *xfer,
+ usb_frcount_t frindex);
+void usbd_xfer_set_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex,
+ usb_frlength_t len);
+void usbd_xfer_set_timeout(struct usb_xfer *xfer, int timeout);
+void usbd_xfer_set_frames(struct usb_xfer *xfer, usb_frcount_t n);
+void usbd_xfer_set_stall(struct usb_xfer *xfer);
+int usbd_xfer_is_stalled(struct usb_xfer *xfer);
+void usbd_xfer_set_flag(struct usb_xfer *xfer, int flag);
+void usbd_xfer_clr_flag(struct usb_xfer *xfer, int flag);
+uint16_t usbd_xfer_get_timestamp(struct usb_xfer *xfer);
+uint8_t usbd_xfer_maxp_was_clamped(struct usb_xfer *xfer);
+
+void usbd_copy_in(struct usb_page_cache *cache, usb_frlength_t offset,
+ const void *ptr, usb_frlength_t len);
+int usbd_copy_in_user(struct usb_page_cache *cache, usb_frlength_t offset,
+ const void *ptr, usb_frlength_t len);
+void usbd_copy_out(struct usb_page_cache *cache, usb_frlength_t offset,
+ void *ptr, usb_frlength_t len);
+int usbd_copy_out_user(struct usb_page_cache *cache, usb_frlength_t offset,
+ void *ptr, usb_frlength_t len);
+void usbd_get_page(struct usb_page_cache *pc, usb_frlength_t offset,
+ struct usb_page_search *res);
+void usbd_m_copy_in(struct usb_page_cache *cache, usb_frlength_t dst_offset,
+ struct mbuf *m, usb_size_t src_offset, usb_frlength_t src_len);
+void usbd_frame_zero(struct usb_page_cache *cache, usb_frlength_t offset,
+ usb_frlength_t len);
+void usbd_start_re_enumerate(struct usb_device *udev);
+usb_error_t
+ usbd_start_set_config(struct usb_device *, uint8_t);
+
+int usb_fifo_attach(struct usb_device *udev, void *priv_sc,
+ struct mtx *priv_mtx, struct usb_fifo_methods *pm,
+ struct usb_fifo_sc *f_sc, uint16_t unit, int16_t subunit,
+ uint8_t iface_index, uid_t uid, gid_t gid, int mode);
+void usb_fifo_detach(struct usb_fifo_sc *f_sc);
+int usb_fifo_alloc_buffer(struct usb_fifo *f, uint32_t bufsize,
+ uint16_t nbuf);
+void usb_fifo_free_buffer(struct usb_fifo *f);
+uint32_t usb_fifo_put_bytes_max(struct usb_fifo *fifo);
+void usb_fifo_put_data(struct usb_fifo *fifo, struct usb_page_cache *pc,
+ usb_frlength_t offset, usb_frlength_t len, uint8_t what);
+void usb_fifo_put_data_linear(struct usb_fifo *fifo, void *ptr,
+ usb_size_t len, uint8_t what);
+uint8_t usb_fifo_put_data_buffer(struct usb_fifo *f, void *ptr, usb_size_t len);
+void usb_fifo_put_data_error(struct usb_fifo *fifo);
+uint8_t usb_fifo_get_data(struct usb_fifo *fifo, struct usb_page_cache *pc,
+ usb_frlength_t offset, usb_frlength_t len, usb_frlength_t *actlen,
+ uint8_t what);
+uint8_t usb_fifo_get_data_linear(struct usb_fifo *fifo, void *ptr,
+ usb_size_t len, usb_size_t *actlen, uint8_t what);
+uint8_t usb_fifo_get_data_buffer(struct usb_fifo *f, void **pptr,
+ usb_size_t *plen);
+void usb_fifo_reset(struct usb_fifo *f);
+void usb_fifo_wakeup(struct usb_fifo *f);
+void usb_fifo_get_data_error(struct usb_fifo *fifo);
+void *usb_fifo_softc(struct usb_fifo *fifo);
+void usb_fifo_set_close_zlp(struct usb_fifo *, uint8_t);
+void usb_fifo_set_write_defrag(struct usb_fifo *, uint8_t);
+void usb_fifo_free(struct usb_fifo *f);
+#endif /* _KERNEL */
+#endif /* _USB_USBDI_H_ */
diff --git a/usr/contrib/freebsd/sys/ata.h b/usr/contrib/freebsd/sys/ata.h
index 705460355f..223bd7b3eb 100644
--- a/usr/contrib/freebsd/sys/ata.h
+++ b/usr/contrib/freebsd/sys/ata.h
@@ -23,7 +23,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * $FreeBSD: head/sys/sys/ata.h 264853 2014-04-24 01:28:14Z smh $
+ * $FreeBSD$
*/
#ifndef _SYS_ATA_H_
@@ -105,6 +105,10 @@ struct ata_params {
/*069*/ u_int16_t support3;
#define ATA_SUPPORT_RZAT 0x0020
#define ATA_SUPPORT_DRAT 0x4000
+#define ATA_SUPPORT_ZONE_MASK 0x0003
+#define ATA_SUPPORT_ZONE_NR 0x0000
+#define ATA_SUPPORT_ZONE_HOST_AWARE 0x0001
+#define ATA_SUPPORT_ZONE_DEV_MANAGED 0x0002
u_int16_t reserved70;
/*071*/ u_int16_t rlsovlap; /* rel time (us) for overlap */
/*072*/ u_int16_t rlsservice; /* rel time (us) for service */
@@ -228,7 +232,14 @@ struct ata_params {
#define ATA_SUPPORT_RWLOGDMAEXT 0x0008
#define ATA_SUPPORT_MICROCODE3 0x0010
#define ATA_SUPPORT_FREEFALL 0x0020
+#define ATA_SUPPORT_SENSE_REPORT 0x0040
+#define ATA_SUPPORT_EPC 0x0080
/*120*/ u_int16_t enabled2;
+#define ATA_ENABLED_WRITEREADVERIFY 0x0002
+#define ATA_ENABLED_WRITEUNCORREXT 0x0004
+#define ATA_ENABLED_FREEFALL 0x0020
+#define ATA_ENABLED_SENSE_REPORT 0x0040
+#define ATA_ENABLED_EPC 0x0080
u_int16_t reserved121[6];
/*127*/ u_int16_t removable_status;
/*128*/ u_int16_t security_status;
@@ -252,7 +263,7 @@ struct ata_params {
u_int16_t reserved170[6];
/*176*/ u_int8_t media_serial[60];
/*206*/ u_int16_t sct;
- u_int16_t reserved206[2];
+ u_int16_t reserved207[2];
/*209*/ u_int16_t lsalign;
/*210*/ u_int16_t wrv_sectors_m3_1;
u_int16_t wrv_sectors_m3_2;
@@ -298,8 +309,14 @@ struct ata_params {
#define ATA_MAX_28BIT_LBA 268435455UL
/* ATA Status Register */
-#define ATA_STATUS_ERROR 0x01
-#define ATA_STATUS_DEVICE_FAULT 0x20
+#define ATA_STATUS_ERROR 0x01
+#define ATA_STATUS_SENSE_AVAIL 0x02
+#define ATA_STATUS_ALIGN_ERR 0x04
+#define ATA_STATUS_DATA_REQ 0x08
+#define ATA_STATUS_DEF_WRITE_ERR 0x10
+#define ATA_STATUS_DEVICE_FAULT 0x20
+#define ATA_STATUS_DEVICE_READY 0x40
+#define ATA_STATUS_BUSY 0x80
/* ATA Error Register */
#define ATA_ERROR_ABORT 0x04
@@ -335,6 +352,7 @@ struct ata_params {
#define ATA_UDMA6 0x46
#define ATA_SA150 0x47
#define ATA_SA300 0x48
+#define ATA_SA600 0x49
#define ATA_DMA_MAX 0x4f
@@ -367,13 +385,36 @@ struct ata_params {
#define ATA_WRITE_LOG_EXT 0x3f
#define ATA_READ_VERIFY 0x40
#define ATA_READ_VERIFY48 0x42
+#define ATA_WRITE_UNCORRECTABLE48 0x45 /* write uncorrectable 48bit LBA */
+#define ATA_WU_PSEUDO 0x55 /* pseudo-uncorrectable error */
+#define ATA_WU_FLAGGED 0xaa /* flagged-uncorrectable error */
#define ATA_READ_LOG_DMA_EXT 0x47 /* read log DMA ext - PIO Data-In */
+#define ATA_ZAC_MANAGEMENT_IN 0x4a /* ZAC management in */
+#define ATA_ZM_REPORT_ZONES 0x00 /* report zones */
#define ATA_READ_FPDMA_QUEUED 0x60 /* read DMA NCQ */
#define ATA_WRITE_FPDMA_QUEUED 0x61 /* write DMA NCQ */
+#define ATA_NCQ_NON_DATA 0x63 /* NCQ non-data command */
+#define ATA_ABORT_NCQ_QUEUE 0x00 /* abort NCQ queue */
+#define ATA_DEADLINE_HANDLING 0x01 /* deadline handling */
+#define ATA_SET_FEATURES 0x05 /* set features */
+#define ATA_ZERO_EXT 0x06 /* zero ext */
+#define ATA_NCQ_ZAC_MGMT_OUT 0x07 /* NCQ ZAC mgmt out no data */
#define ATA_SEND_FPDMA_QUEUED 0x64 /* send DMA NCQ */
-#define ATA_RECV_FPDMA_QUEUED 0x65 /* recieve DMA NCQ */
+#define ATA_SFPDMA_DSM 0x00 /* Data set management */
+#define ATA_SFPDMA_DSM_TRIM 0x01 /* Set trim bit in auxiliary */
+#define ATA_SFPDMA_HYBRID_EVICT 0x01 /* Hybrid Evict */
+#define ATA_SFPDMA_WLDMA 0x02 /* Write Log DMA EXT */
+#define ATA_SFPDMA_ZAC_MGMT_OUT 0x03 /* NCQ ZAC mgmt out w/data */
+#define ATA_RECV_FPDMA_QUEUED 0x65 /* receive DMA NCQ */
+#define ATA_RFPDMA_RL_DMA_EXT 0x00 /* Read Log DMA EXT */
+#define ATA_RFPDMA_ZAC_MGMT_IN 0x02 /* NCQ ZAC mgmt in w/data */
#define ATA_SEP_ATTN 0x67 /* SEP request */
#define ATA_SEEK 0x70 /* seek */
+#define ATA_ZAC_MANAGEMENT_OUT 0x9f /* ZAC management out */
+#define ATA_ZM_CLOSE_ZONE 0x01 /* close zone */
+#define ATA_ZM_FINISH_ZONE 0x02 /* finish zone */
+#define ATA_ZM_OPEN_ZONE 0x03 /* open zone */
+#define ATA_ZM_RWP 0x04 /* reset write pointer */
#define ATA_PACKET_CMD 0xa0 /* packet command */
#define ATA_ATAPI_IDENTIFY 0xa1 /* get ATAPI params*/
#define ATA_SERVICE 0xa2 /* service command */
@@ -393,24 +434,36 @@ struct ata_params {
#define ATA_IDLE_CMD 0xe3 /* idle */
#define ATA_READ_BUFFER 0xe4 /* read buffer */
#define ATA_READ_PM 0xe4 /* read portmultiplier */
+#define ATA_CHECK_POWER_MODE 0xe5 /* device power mode */
#define ATA_SLEEP 0xe6 /* sleep */
#define ATA_FLUSHCACHE 0xe7 /* flush cache to disk */
#define ATA_WRITE_PM 0xe8 /* write portmultiplier */
#define ATA_FLUSHCACHE48 0xea /* flush cache to disk */
#define ATA_ATA_IDENTIFY 0xec /* get ATA params */
#define ATA_SETFEATURES 0xef /* features command */
-#define ATA_SF_SETXFER 0x03 /* set transfer mode */
#define ATA_SF_ENAB_WCACHE 0x02 /* enable write cache */
#define ATA_SF_DIS_WCACHE 0x82 /* disable write cache */
+#define ATA_SF_SETXFER 0x03 /* set transfer mode */
+#define ATA_SF_APM 0x05 /* Enable APM feature set */
#define ATA_SF_ENAB_PUIS 0x06 /* enable PUIS */
#define ATA_SF_DIS_PUIS 0x86 /* disable PUIS */
#define ATA_SF_PUIS_SPINUP 0x07 /* PUIS spin-up */
+#define ATA_SF_WRV 0x0b /* Enable Write-Read-Verify */
+#define ATA_SF_DLC 0x0c /* Enable device life control */
+#define ATA_SF_SATA 0x10 /* Enable use of SATA feature */
+#define ATA_SF_FFC 0x41 /* Free-fall Control */
+#define ATA_SF_MHIST 0x43 /* Set Max Host Sect. Times */
+#define ATA_SF_RATE 0x45 /* Set Rate Basis */
+#define ATA_SF_EPC 0x4A /* Extended Power Conditions */
#define ATA_SF_ENAB_RCACHE 0xaa /* enable readahead cache */
#define ATA_SF_DIS_RCACHE 0x55 /* disable readahead cache */
#define ATA_SF_ENAB_RELIRQ 0x5d /* enable release interrupt */
#define ATA_SF_DIS_RELIRQ 0xdd /* disable release interrupt */
#define ATA_SF_ENAB_SRVIRQ 0x5e /* enable service interrupt */
#define ATA_SF_DIS_SRVIRQ 0xde /* disable service interrupt */
+#define ATA_SF_LPSAERC 0x62 /* Long Phys Sect Align ErrRep*/
+#define ATA_SF_DSN 0x63 /* Device Stats Notification */
+#define ATA_CHECK_POWER_MODE 0xe5 /* Check Power Mode */
#define ATA_SECURITY_SET_PASSWORD 0xf1 /* set drive password */
#define ATA_SECURITY_UNLOCK 0xf2 /* unlock drive using passwd */
#define ATA_SECURITY_ERASE_PREPARE 0xf3 /* prepare to erase drive */
@@ -537,6 +590,333 @@ struct atapi_sense {
u_int8_t specific2; /* sense key specific */
} __packed;
+/*
+ * SET FEATURES subcommands
+ */
+
+/*
+ * SET FEATURES command
+ * Extended Power Conditions subcommand -- ATA_SF_EPC (0x4A)
+ * These values go in the LBA 3:0.
+ */
+#define ATA_SF_EPC_RESTORE 0x00 /* Restore Power Condition Settings */
+#define ATA_SF_EPC_GOTO 0x01 /* Go To Power Condition */
+#define ATA_SF_EPC_SET_TIMER 0x02 /* Set Power Condition Timer */
+#define ATA_SF_EPC_SET_STATE 0x03 /* Set Power Condition State */
+#define ATA_SF_EPC_ENABLE 0x04 /* Enable the EPC feature set */
+#define ATA_SF_EPC_DISABLE 0x05 /* Disable the EPC feature set */
+#define ATA_SF_EPC_SET_SOURCE 0x06 /* Set EPC Power Source */
+
+/*
+ * SET FEATURES command
+ * Extended Power Conditions subcommand -- ATA_SF_EPC (0x4A)
+ * Power Condition ID field
+ * These values go in the count register.
+ */
+#define ATA_EPC_STANDBY_Z 0x00 /* Substate of PM2:Standby */
+#define ATA_EPC_STANDBY_Y 0x01 /* Substate of PM2:Standby */
+#define ATA_EPC_IDLE_A 0x81 /* Substate of PM1:Idle */
+#define ATA_EPC_IDLE_B 0x82 /* Substate of PM1:Idle */
+#define ATA_EPC_IDLE_C 0x83 /* Substate of PM1:Idle */
+#define ATA_EPC_ALL 0xff /* All supported power conditions */
+
+/*
+ * SET FEATURES command
+ * Extended Power Conditions subcommand -- ATA_SF_EPC (0x4A)
+ * Restore Power Conditions Settings subcommand
+ * These values go in the LBA register.
+ */
+#define ATA_SF_EPC_RST_DFLT 0x40 /* 1=Rst from Default, 0= from Saved */
+#define ATA_SF_EPC_RST_SAVE 0x10 /* 1=Save on completion */
+
+/*
+ * SET FEATURES command
+ * Extended Power Conditions subcommand -- ATA_SF_EPC (0x4A)
+ * Got To Power Condition subcommand
+ * These values go in the LBA register.
+ */
+#define ATA_SF_EPC_GOTO_DELAY 0x02000000 /* Delayed entry bit */
+#define ATA_SF_EPC_GOTO_HOLD 0x01000000 /* Hold Power Cond bit */
+
+/*
+ * SET FEATURES command
+ * Extended Power Conditions subcommand -- ATA_SF_EPC (0x4A)
+ * Set Power Condition Timer subcommand
+ * These values go in the LBA register.
+ */
+#define ATA_SF_EPC_TIMER_MASK 0x00ffff00 /* Timer field */
+#define ATA_SF_EPC_TIMER_SHIFT 8
+#define ATA_SF_EPC_TIMER_SEC 0x00000080 /* Timer units, 1=sec, 0=.1s */
+#define ATA_SF_EPC_TIMER_EN 0x00000020 /* Enable/disable cond. */
+#define ATA_SF_EPC_TIMER_SAVE 0x00000010 /* Save settings on comp. */
+
+/*
+ * SET FEATURES command
+ * Extended Power Conditions subcommand -- ATA_SF_EPC (0x4A)
+ * Set Power Condition State subcommand
+ * These values go in the LBA register.
+ */
+#define ATA_SF_EPC_SETCON_EN 0x00000020 /* Enable power cond. */
+#define ATA_SF_EPC_SETCON_SAVE 0x00000010 /* Save settings on comp */
+
+/*
+ * SET FEATURES command
+ * Extended Power Conditions subcommand -- ATA_SF_EPC (0x4A)
+ * Set EPC Power Source subcommand
+ * These values go in the count register.
+ */
+#define ATA_SF_EPC_SRC_UNKNOWN 0x0000 /* Unknown source */
+#define ATA_SF_EPC_SRC_BAT 0x0001 /* battery source */
+#define ATA_SF_EPC_SRC_NOT_BAT 0x0002 /* not battery source */
+
+#define ATA_LOG_DIRECTORY 0x00 /* Directory of all logs */
+#define ATA_POWER_COND_LOG 0x08 /* Power Conditions Log */
+#define ATA_PCL_IDLE 0x00 /* Idle Power Conditions Page */
+#define ATA_PCL_STANDBY 0x01 /* Standby Power Conditions Page */
+#define ATA_IDENTIFY_DATA_LOG 0x30 /* Identify Device Data Log */
+#define ATA_IDL_PAGE_LIST 0x00 /* List of supported pages */
+#define ATA_IDL_IDENTIFY_DATA 0x01 /* Copy of Identify Device data */
+#define ATA_IDL_CAPACITY 0x02 /* Capacity */
+#define ATA_IDL_SUP_CAP 0x03 /* Supported Capabilities */
+#define ATA_IDL_CUR_SETTINGS 0x04 /* Current Settings */
+#define ATA_IDL_ATA_STRINGS 0x05 /* ATA Strings */
+#define ATA_IDL_SECURITY 0x06 /* Security */
+#define ATA_IDL_PARALLEL_ATA 0x07 /* Parallel ATA */
+#define ATA_IDL_SERIAL_ATA 0x08 /* Seiral ATA */
+#define ATA_IDL_ZDI 0x09 /* Zoned Device Information */
+
+struct ata_gp_log_dir {
+ uint8_t header[2];
+#define ATA_GP_LOG_DIR_VERSION 0x0001
+ uint8_t num_pages[255*2]; /* Number of log pages at address */
+};
+
+/*
+ * ATA Power Conditions log descriptor
+ */
+struct ata_power_cond_log_desc {
+ uint8_t reserved1;
+ uint8_t flags;
+#define ATA_PCL_COND_SUPPORTED 0x80
+#define ATA_PCL_COND_SAVEABLE 0x40
+#define ATA_PCL_COND_CHANGEABLE 0x20
+#define ATA_PCL_DEFAULT_TIMER_EN 0x10
+#define ATA_PCL_SAVED_TIMER_EN 0x08
+#define ATA_PCL_CURRENT_TIMER_EN 0x04
+#define ATA_PCL_HOLD_PC_NOT_SUP 0x02
+ uint8_t reserved2[2];
+ uint8_t default_timer[4];
+ uint8_t saved_timer[4];
+ uint8_t current_timer[4];
+ uint8_t nom_time_to_active[4];
+ uint8_t min_timer[4];
+ uint8_t max_timer[4];
+ uint8_t num_transitions_to_pc[4];
+ uint8_t hours_in_pc[4];
+ uint8_t reserved3[28];
+};
+
+/*
+ * ATA Power Conditions Log (0x08), Idle power conditions page (0x00)
+ */
+struct ata_power_cond_log_idle {
+ struct ata_power_cond_log_desc idle_a_desc;
+ struct ata_power_cond_log_desc idle_b_desc;
+ struct ata_power_cond_log_desc idle_c_desc;
+ uint8_t reserved[320];
+};
+
+/*
+ * ATA Power Conditions Log (0x08), Standby power conditions page (0x01)
+ */
+struct ata_power_cond_log_standby {
+ uint8_t reserved[384];
+ struct ata_power_cond_log_desc standby_y_desc;
+ struct ata_power_cond_log_desc standby_z_desc;
+};
+
+/*
+ * ATA IDENTIFY DEVICE data log (0x30) page 0x00
+ * List of Supported IDENTIFY DEVICE data pages.
+ */
+struct ata_identify_log_pages {
+ uint8_t header[8];
+#define ATA_IDLOG_REVISION 0x0000000000000001
+ uint8_t entry_count;
+ uint8_t entries[503];
+};
+
+/*
+ * ATA IDENTIFY DEVICE data log (0x30)
+ * Capacity (Page 0x02).
+ */
+struct ata_identify_log_capacity {
+ uint8_t header[8];
+#define ATA_CAP_HEADER_VALID 0x8000000000000000
+#define ATA_CAP_PAGE_NUM_MASK 0x0000000000ff0000
+#define ATA_CAP_PAGE_NUM_SHIFT 16
+#define ATA_CAP_REV_MASK 0x00000000000000ff
+ uint8_t capacity[8];
+#define ATA_CAP_CAPACITY_VALID 0x8000000000000000
+#define ATA_CAP_ACCESSIBLE_CAP 0x0000ffffffffffff
+ uint8_t phys_logical_sect_size[8];
+#define ATA_CAP_PL_VALID 0x8000000000000000
+#define ATA_CAP_LTOP_REL_SUP 0x4000000000000000
+#define ATA_CAP_LOG_SECT_SUP 0x2000000000000000
+#define ATA_CAP_ALIGN_ERR_MASK 0x0000000000300000
+#define ATA_CAP_LTOP_MASK 0x00000000000f0000
+#define ATA_CAP_LOG_SECT_OFF 0x000000000000ffff
+ uint8_t logical_sect_size[8];
+#define ATA_CAP_LOG_SECT_VALID 0x8000000000000000
+#define ATA_CAP_LOG_SECT_SIZE 0x00000000ffffffff
+ uint8_t nominal_buffer_size[8];
+#define ATA_CAP_NOM_BUF_VALID 0x8000000000000000
+#define ATA_CAP_NOM_BUF_SIZE 0x7fffffffffffffff
+ uint8_t reserved[472];
+};
+
+/*
+ * ATA IDENTIFY DEVICE data log (0x30)
+ * Supported Capabilities (Page 0x03).
+ */
+
+struct ata_identify_log_sup_cap {
+ uint8_t header[8];
+#define ATA_SUP_CAP_HEADER_VALID 0x8000000000000000
+#define ATA_SUP_CAP_PAGE_NUM_MASK 0x0000000000ff0000
+#define ATA_SUP_CAP_PAGE_NUM_SHIFT 16
+#define ATA_SUP_CAP_REV_MASK 0x00000000000000ff
+ uint8_t sup_cap[8];
+#define ATA_SUP_CAP_VALID 0x8000000000000000
+#define ATA_SC_SET_SECT_CONFIG_SUP 0x0002000000000000 /* Set Sect Conf*/
+#define ATA_SC_ZERO_EXT_SUP 0x0001000000000000 /* Zero EXT */
+#define ATA_SC_SUCC_NCQ_SENSE_SUP 0x0000800000000000 /* Succ. NCQ Sns */
+#define ATA_SC_DLC_SUP 0x0000400000000000 /* DLC */
+#define ATA_SC_RQSN_DEV_FAULT_SUP 0x0000200000000000 /* Req Sns Dev Flt*/
+#define ATA_SC_DSN_SUP 0x0000100000000000 /* DSN */
+#define ATA_SC_LP_STANDBY_SUP 0x0000080000000000 /* LP Standby */
+#define ATA_SC_SET_EPC_PS_SUP 0x0000040000000000 /* Set EPC PS */
+#define ATA_SC_AMAX_ADDR_SUP 0x0000020000000000 /* AMAX Addr */
+#define ATA_SC_DRAT_SUP 0x0000008000000000 /* DRAT */
+#define ATA_SC_LPS_MISALGN_SUP 0x0000004000000000 /* LPS Misalign */
+#define ATA_SC_RB_DMA_SUP 0x0000001000000000 /* Read Buf DMA */
+#define ATA_SC_WB_DMA_SUP 0x0000000800000000 /* Write Buf DMA */
+#define ATA_SC_DNLD_MC_DMA_SUP 0x0000000200000000 /* DL MCode DMA */
+#define ATA_SC_28BIT_SUP 0x0000000100000000 /* 28-bit */
+#define ATA_SC_RZAT_SUP 0x0000000080000000 /* RZAT */
+#define ATA_SC_NOP_SUP 0x0000000020000000 /* NOP */
+#define ATA_SC_READ_BUFFER_SUP 0x0000000010000000 /* Read Buffer */
+#define ATA_SC_WRITE_BUFFER_SUP 0x0000000008000000 /* Write Buffer */
+#define ATA_SC_READ_LOOK_AHEAD_SUP 0x0000000002000000 /* Read Look-Ahead*/
+#define ATA_SC_VOLATILE_WC_SUP 0x0000000001000000 /* Volatile WC */
+#define ATA_SC_SMART_SUP 0x0000000000800000 /* SMART */
+#define ATA_SC_FLUSH_CACHE_EXT_SUP 0x0000000000400000 /* Flush Cache Ext */
+#define ATA_SC_48BIT_SUP 0x0000000000100000 /* 48-Bit */
+#define ATA_SC_SPINUP_SUP 0x0000000000040000 /* Spin-Up */
+#define ATA_SC_PUIS_SUP 0x0000000000020000 /* PUIS */
+#define ATA_SC_APM_SUP 0x0000000000010000 /* APM */
+#define ATA_SC_DL_MICROCODE_SUP 0x0000000000004000 /* DL Microcode */
+#define ATA_SC_UNLOAD_SUP 0x0000000000002000 /* Unload */
+#define ATA_SC_WRITE_FUA_EXT_SUP 0x0000000000001000 /* Write FUA EXT */
+#define ATA_SC_GPL_SUP 0x0000000000000800 /* GPL */
+#define ATA_SC_STREAMING_SUP 0x0000000000000400 /* Streaming */
+#define ATA_SC_SMART_SELFTEST_SUP 0x0000000000000100 /* SMART self-test */
+#define ATA_SC_SMART_ERR_LOG_SUP 0x0000000000000080 /* SMART Err Log */
+#define ATA_SC_EPC_SUP 0x0000000000000040 /* EPC */
+#define ATA_SC_SENSE_SUP 0x0000000000000020 /* Sense data */
+#define ATA_SC_FREEFALL_SUP 0x0000000000000010 /* Free-Fall */
+#define ATA_SC_DM_MODE3_SUP 0x0000000000000008 /* DM Mode 3 */
+#define ATA_SC_GPL_DMA_SUP 0x0000000000000004 /* GPL DMA */
+#define ATA_SC_WRITE_UNCOR_SUP 0x0000000000000002 /* Write uncorr. */
+#define ATA_SC_WRV_SUP 0x0000000000000001 /* WRV */
+ uint8_t download_code_cap[8];
+#define ATA_DL_CODE_VALID 0x8000000000000000
+#define ATA_DLC_DM_OFFSETS_DEFER_SUP 0x0000000400000000
+#define ATA_DLC_DM_IMMED_SUP 0x0000000200000000
+#define ATA_DLC_DM_OFF_IMMED_SUP 0x0000000100000000
+#define ATA_DLC_DM_MAX_XFER_SIZE_MASK 0x00000000ffff0000
+#define ATA_DLC_DM_MAX_XFER_SIZE_SHIFT 16
+#define ATA_DLC_DM_MIN_XFER_SIZE_MASK 0x000000000000ffff
+ uint8_t nom_media_rotation_rate[8];
+#define ATA_NOM_MEDIA_ROTATION_VALID 0x8000000000000000
+#define ATA_ROTATION_MASK 0x000000000000ffff
+ uint8_t form_factor[8];
+#define ATA_FORM_FACTOR_VALID 0x8000000000000000
+#define ATA_FF_MASK 0x000000000000000f
+#define ATA_FF_NOT_REPORTED 0x0000000000000000 /* Not reported */
+#define ATA_FF_525_IN 0x0000000000000001 /* 5.25 inch */
+#define ATA_FF_35_IN 0x0000000000000002 /* 3.5 inch */
+#define ATA_FF_25_IN 0x0000000000000003 /* 2.5 inch */
+#define ATA_FF_18_IN 0x0000000000000004 /* 1.8 inch */
+#define ATA_FF_LT_18_IN 0x0000000000000005 /* < 1.8 inch */
+#define ATA_FF_MSATA 0x0000000000000006 /* mSATA */
+#define ATA_FF_M2 0x0000000000000007 /* M.2 */
+#define ATA_FF_MICROSSD 0x0000000000000008 /* MicroSSD */
+#define ATA_FF_CFAST 0x0000000000000009 /* CFast */
+ uint8_t wrv_sec_cnt_mode3[8];
+#define ATA_WRV_MODE3_VALID 0x8000000000000000
+#define ATA_WRV_MODE3_COUNT 0x00000000ffffffff
+ uint8_t wrv_sec_cnt_mode2[8];
+#define ATA_WRV_MODE2_VALID 0x8000000000000000
+#define ATA_WRV_MODE2_COUNT 0x00000000ffffffff
+ uint8_t wwn[16];
+ /* XXX KDM need to figure out how to handle 128-bit fields */
+ uint8_t dsm[8];
+#define ATA_DSM_VALID 0x8000000000000000
+#define ATA_LB_MARKUP_SUP 0x000000000000ff00
+#define ATA_TRIM_SUP 0x0000000000000001
+ uint8_t util_per_unit_time[16];
+ /* XXX KDM need to figure out how to handle 128-bit fields */
+ uint8_t util_usage_rate_sup[8];
+#define ATA_UTIL_USAGE_RATE_VALID 0x8000000000000000
+#define ATA_SETTING_RATE_SUP 0x0000000000800000
+#define ATA_SINCE_POWERON_SUP 0x0000000000000100
+#define ATA_POH_RATE_SUP 0x0000000000000010
+#define ATA_DATE_TIME_RATE_SUP 0x0000000000000001
+ uint8_t zoned_cap[8];
+#define ATA_ZONED_VALID 0x8000000000000000
+#define ATA_ZONED_MASK 0x0000000000000003
+ uint8_t sup_zac_cap[8];
+#define ATA_SUP_ZAC_CAP_VALID 0x8000000000000000
+#define ATA_ND_RWP_SUP 0x0000000000000010 /* Reset Write Ptr*/
+#define ATA_ND_FINISH_ZONE_SUP 0x0000000000000008 /* Finish Zone */
+#define ATA_ND_CLOSE_ZONE_SUP 0x0000000000000004 /* Close Zone */
+#define ATA_ND_OPEN_ZONE_SUP 0x0000000000000002 /* Open Zone */
+#define ATA_REPORT_ZONES_SUP 0x0000000000000001 /* Report Zones */
+ uint8_t reserved[392];
+};
+
+/*
+ * ATA Identify Device Data Log Zoned Device Information Page (0x09).
+ * Current as of ZAC r04a, August 25, 2015.
+ */
+struct ata_zoned_info_log {
+ uint8_t header[8];
+#define ATA_ZDI_HEADER_VALID 0x8000000000000000
+#define ATA_ZDI_PAGE_NUM_MASK 0x0000000000ff0000
+#define ATA_ZDI_PAGE_NUM_SHIFT 16
+#define ATA_ZDI_REV_MASK 0x00000000000000ff
+ uint8_t zoned_cap[8];
+#define ATA_ZDI_CAP_VALID 0x8000000000000000
+#define ATA_ZDI_CAP_URSWRZ 0x0000000000000001
+ uint8_t zoned_settings[8];
+#define ATA_ZDI_SETTINGS_VALID 0x8000000000000000
+ uint8_t optimal_seq_zones[8];
+#define ATA_ZDI_OPT_SEQ_VALID 0x8000000000000000
+#define ATA_ZDI_OPT_SEQ_MASK 0x00000000ffffffff
+ uint8_t optimal_nonseq_zones[8];
+#define ATA_ZDI_OPT_NS_VALID 0x8000000000000000
+#define ATA_ZDI_OPT_NS_MASK 0x00000000ffffffff
+ uint8_t max_seq_req_zones[8];
+#define ATA_ZDI_MAX_SEQ_VALID 0x8000000000000000
+#define ATA_ZDI_MAX_SEQ_MASK 0x00000000ffffffff
+ uint8_t version_info[8];
+#define ATA_ZDI_VER_VALID 0x8000000000000000
+#define ATA_ZDI_VER_ZAC_SUP 0x0100000000000000
+#define ATA_ZDI_VER_ZAC_MASK 0x00000000000000ff
+ uint8_t reserved[456];
+};
+
struct ata_ioc_request {
union {
struct {
diff --git a/usr/contrib/freebsd/sys/pciio.h b/usr/contrib/freebsd/sys/pciio.h
new file mode 100644
index 0000000000..d70bfbcf6f
--- /dev/null
+++ b/usr/contrib/freebsd/sys/pciio.h
@@ -0,0 +1,146 @@
+/*-
+ * Copyright (c) 1997, Stefan Esser <se@FreeBSD.ORG>
+ * Copyright (c) 1997, 1998, 1999, Kenneth D. Merry <ken@FreeBSD.ORG>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice unmodified, this list of conditions, and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#ifndef _SYS_PCIIO_H_
+#define _SYS_PCIIO_H_
+
+#include <sys/ioccom.h>
+
+#define PCI_MAXNAMELEN 16
+
+typedef enum {
+ PCI_GETCONF_LAST_DEVICE,
+ PCI_GETCONF_LIST_CHANGED,
+ PCI_GETCONF_MORE_DEVS,
+ PCI_GETCONF_ERROR
+} pci_getconf_status;
+
+typedef enum {
+ PCI_GETCONF_NO_MATCH = 0x0000,
+ PCI_GETCONF_MATCH_DOMAIN = 0x0001,
+ PCI_GETCONF_MATCH_BUS = 0x0002,
+ PCI_GETCONF_MATCH_DEV = 0x0004,
+ PCI_GETCONF_MATCH_FUNC = 0x0008,
+ PCI_GETCONF_MATCH_NAME = 0x0010,
+ PCI_GETCONF_MATCH_UNIT = 0x0020,
+ PCI_GETCONF_MATCH_VENDOR = 0x0040,
+ PCI_GETCONF_MATCH_DEVICE = 0x0080,
+ PCI_GETCONF_MATCH_CLASS = 0x0100
+} pci_getconf_flags;
+
+struct pcisel {
+ u_int32_t pc_domain; /* domain number */
+ u_int8_t pc_bus; /* bus number */
+ u_int8_t pc_dev; /* device on this bus */
+ u_int8_t pc_func; /* function on this device */
+};
+
+struct pci_conf {
+ struct pcisel pc_sel; /* domain+bus+slot+function */
+ u_int8_t pc_hdr; /* PCI header type */
+ u_int16_t pc_subvendor; /* card vendor ID */
+ u_int16_t pc_subdevice; /* card device ID, assigned by
+ card vendor */
+ u_int16_t pc_vendor; /* chip vendor ID */
+ u_int16_t pc_device; /* chip device ID, assigned by
+ chip vendor */
+ u_int8_t pc_class; /* chip PCI class */
+ u_int8_t pc_subclass; /* chip PCI subclass */
+ u_int8_t pc_progif; /* chip PCI programming interface */
+ u_int8_t pc_revid; /* chip revision ID */
+ char pd_name[PCI_MAXNAMELEN + 1]; /* device name */
+ u_long pd_unit; /* device unit number */
+};
+
+struct pci_match_conf {
+ struct pcisel pc_sel; /* domain+bus+slot+function */
+ char pd_name[PCI_MAXNAMELEN + 1]; /* device name */
+ u_long pd_unit; /* Unit number */
+ u_int16_t pc_vendor; /* PCI Vendor ID */
+ u_int16_t pc_device; /* PCI Device ID */
+ u_int8_t pc_class; /* PCI class */
+ pci_getconf_flags flags; /* Matching expression */
+};
+
+struct pci_conf_io {
+ u_int32_t pat_buf_len; /* pattern buffer length */
+ u_int32_t num_patterns; /* number of patterns */
+ struct pci_match_conf *patterns; /* pattern buffer */
+ u_int32_t match_buf_len; /* match buffer length */
+ u_int32_t num_matches; /* number of matches returned */
+ struct pci_conf *matches; /* match buffer */
+ u_int32_t offset; /* offset into device list */
+ u_int32_t generation; /* device list generation */
+ pci_getconf_status status; /* request status */
+};
+
+struct pci_io {
+ struct pcisel pi_sel; /* device to operate on */
+ int pi_reg; /* configuration register to examine */
+ int pi_width; /* width (in bytes) of read or write */
+ u_int32_t pi_data; /* data to write or result of read */
+};
+
+struct pci_bar_io {
+ struct pcisel pbi_sel; /* device to operate on */
+ int pbi_reg; /* starting address of BAR */
+ int pbi_enabled; /* decoding enabled */
+ uint64_t pbi_base; /* current value of BAR */
+ uint64_t pbi_length; /* length of BAR */
+};
+
+struct pci_vpd_element {
+ char pve_keyword[2];
+ uint8_t pve_flags;
+ uint8_t pve_datalen;
+ uint8_t pve_data[0];
+};
+
+#define PVE_FLAG_IDENT 0x01 /* Element is the string identifier */
+#define PVE_FLAG_RW 0x02 /* Element is read/write */
+
+#define PVE_NEXT(pve) \
+ ((struct pci_vpd_element *)((char *)(pve) + \
+ sizeof(struct pci_vpd_element) + (pve)->pve_datalen))
+
+struct pci_list_vpd_io {
+ struct pcisel plvi_sel; /* device to operate on */
+ size_t plvi_len; /* size of the data area */
+ struct pci_vpd_element *plvi_data;
+};
+
+#define PCIOCGETCONF _IOWR('p', 5, struct pci_conf_io)
+#define PCIOCREAD _IOWR('p', 2, struct pci_io)
+#define PCIOCWRITE _IOWR('p', 3, struct pci_io)
+#define PCIOCATTACHED _IOWR('p', 4, struct pci_io)
+#define PCIOCGETBAR _IOWR('p', 6, struct pci_bar_io)
+#define PCIOCLISTVPD _IOWR('p', 7, struct pci_list_vpd_io)
+
+#endif /* !_SYS_PCIIO_H_ */
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index d97da99fd7..c6f19d5369 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -480,6 +480,8 @@ i386_SUBDIRS= \
acpi \
acpihpd \
addbadsec \
+ bhyve \
+ bhyveconsole \
bhyvectl \
biosdev \
diskscan \
diff --git a/usr/src/cmd/bhyve/Makefile.com b/usr/src/cmd/bhyve/Makefile.com
index 4a92b622ab..b80bf910de 100644
--- a/usr/src/cmd/bhyve/Makefile.com
+++ b/usr/src/cmd/bhyve/Makefile.com
@@ -11,37 +11,50 @@
#
# Copyright 2015 Pluribus Networks Inc.
+# Copyright 2017 Joyent, Inc.
#
PROG= bhyve
-SRCS = atkbdc.c \
+SRCS = acpi.c \
+ atkbdc.c \
bhyvegc.c \
bhyverun.c \
block_if.c \
+ bootrom.c \
console.c \
consport.c \
+ dbgport.c \
+ fwctl.c \
inout.c \
ioapic.c \
mem.c \
mptbl.c \
pci_ahci.c \
+ pci_e82545.c \
pci_emul.c \
+ pci_fbuf.c \
pci_hostbridge.c \
pci_irq.c \
pci_lpc.c \
+ pci_passthru.c \
+ pci_uart.c \
pci_virtio_block.c \
pci_virtio_net.c \
- pci_virtio_viona.c \
+ pci_virtio_rnd.c \
+ pci_xhci.c \
pm.c \
- pmtmr.c \
post.c \
ps2kbd.c \
ps2mouse.c \
rfb.c \
rtc.c \
smbiostbl.c \
+ sockstream.c \
+ task_switch.c \
uart_emul.c \
+ usb_emul.c \
+ usb_mouse.c \
vga.c \
virtio.c \
vmm_instruction_emul.c \
@@ -52,18 +65,24 @@ SRCS = atkbdc.c \
OBJS = $(SRCS:.c=.o)
include ../../Makefile.cmd
+include ../../Makefile.ctf
.KEEP_STATE:
-CFLAGS += $(CCVERBOSE) -_gcc=-Wimplicit-function-declaration
-CFLAGS64 += $(CCVERBOSE) -_gcc=-Wimplicit-function-declaration
-CPPFLAGS = -I$(COMPAT)/freebsd -I$(CONTRIB)/freebsd $(CPPFLAGS.master) \
+CFLAGS += $(CCVERBOSE) -_gcc=-Wimplicit-function-declaration -_gcc=-Wno-parentheses
+CFLAGS64 += $(CCVERBOSE) -_gcc=-Wimplicit-function-declaration -_gcc=-Wno-parentheses
+CPPFLAGS = -I$(COMPAT)/freebsd -I$(CONTRIB)/freebsd \
+ -I$(CONTRIB)/freebsd/dev/usb/controller \
+ -I$(CONTRIB)/freebsd/dev/mii \
+ -I$(SRC)/uts/common/io/e1000api \
+ $(CPPFLAGS.master) \
-I$(ROOT)/usr/platform/i86pc/include \
-I$(SRC)/uts/i86pc/io/vmm \
-I$(SRC)/uts/common \
-I$(SRC)/uts/i86pc \
- -I$(SRC)/lib/libdladm/common
-LDLIBS += -lsocket -lnsl -ldlpi -ldladm -lkstat -lmd -luuid -lvmmapi
+ -I$(SRC)/lib/libdladm/common \
+ -DWITHOUT_CAPSICUM
+LDLIBS += -lsocket -lnsl -ldlpi -ldladm -lkstat -lmd -luuid -lvmmapi -lz
POST_PROCESS += ; $(GENSETDEFS) $@
diff --git a/usr/src/cmd/bhyve/acpi.c b/usr/src/cmd/bhyve/acpi.c
new file mode 100644
index 0000000000..52518cb745
--- /dev/null
+++ b/usr/src/cmd/bhyve/acpi.c
@@ -0,0 +1,1012 @@
+/*-
+ * Copyright (c) 2012 NetApp, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * bhyve ACPI table generator.
+ *
+ * Create the minimal set of ACPI tables required to boot FreeBSD (and
+ * hopefully other o/s's) by writing out ASL template files for each of
+ * the tables and the compiling them to AML with the Intel iasl compiler.
+ * The AML files are then read into guest memory.
+ *
+ * The tables are placed in the guest's ROM area just below 1MB physical,
+ * above the MPTable.
+ *
+ * Layout
+ * ------
+ * RSDP -> 0xf2400 (36 bytes fixed)
+ * RSDT -> 0xf2440 (36 bytes + 4*7 table addrs, 4 used)
+ * XSDT -> 0xf2480 (36 bytes + 8*7 table addrs, 4 used)
+ * MADT -> 0xf2500 (depends on #CPUs)
+ * FADT -> 0xf2600 (268 bytes)
+ * HPET -> 0xf2740 (56 bytes)
+ * MCFG -> 0xf2780 (60 bytes)
+ * FACS -> 0xf27C0 (64 bytes)
+ * DSDT -> 0xf2800 (variable - can go up to 0x100000)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+
+#include <paths.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <machine/vmm.h>
+#include <vmmapi.h>
+
+#include "bhyverun.h"
+#include "acpi.h"
+#include "pci_emul.h"
+
+/*
+ * Define the base address of the ACPI tables, and the offsets to
+ * the individual tables
+ */
+#define BHYVE_ACPI_BASE 0xf2400
+#define RSDT_OFFSET 0x040
+#define XSDT_OFFSET 0x080
+#define MADT_OFFSET 0x100
+#define FADT_OFFSET 0x200
+#define HPET_OFFSET 0x340
+#define MCFG_OFFSET 0x380
+#define FACS_OFFSET 0x3C0
+#define DSDT_OFFSET 0x400
+
+#define BHYVE_ASL_TEMPLATE "bhyve.XXXXXXX"
+#define BHYVE_ASL_SUFFIX ".aml"
+#define BHYVE_ASL_COMPILER "/usr/sbin/iasl"
+
+static int basl_keep_temps;
+static int basl_verbose_iasl;
+static int basl_ncpu;
+static uint32_t basl_acpi_base = BHYVE_ACPI_BASE;
+static uint32_t hpet_capabilities;
+
+/*
+ * Contains the full pathname of the template to be passed
+ * to mkstemp/mktemps(3)
+ */
+static char basl_template[MAXPATHLEN];
+static char basl_stemplate[MAXPATHLEN];
+
+/*
+ * State for dsdt_line(), dsdt_indent(), and dsdt_unindent().
+ */
+static FILE *dsdt_fp;
+static int dsdt_indent_level;
+static int dsdt_error;
+
+struct basl_fio {
+ int fd;
+ FILE *fp;
+ char f_name[MAXPATHLEN];
+};
+
+#define EFPRINTF(...) \
+ err = fprintf(__VA_ARGS__); if (err < 0) goto err_exit;
+
+#define EFFLUSH(x) \
+ err = fflush(x); if (err != 0) goto err_exit;
+
+static int
+basl_fwrite_rsdp(FILE *fp)
+{
+ int err;
+
+ err = 0;
+
+ EFPRINTF(fp, "/*\n");
+ EFPRINTF(fp, " * bhyve RSDP template\n");
+ EFPRINTF(fp, " */\n");
+ EFPRINTF(fp, "[0008]\t\tSignature : \"RSD PTR \"\n");
+ EFPRINTF(fp, "[0001]\t\tChecksum : 43\n");
+ EFPRINTF(fp, "[0006]\t\tOem ID : \"BHYVE \"\n");
+ EFPRINTF(fp, "[0001]\t\tRevision : 02\n");
+ EFPRINTF(fp, "[0004]\t\tRSDT Address : %08X\n",
+ basl_acpi_base + RSDT_OFFSET);
+ EFPRINTF(fp, "[0004]\t\tLength : 00000024\n");
+ EFPRINTF(fp, "[0008]\t\tXSDT Address : 00000000%08X\n",
+ basl_acpi_base + XSDT_OFFSET);
+ EFPRINTF(fp, "[0001]\t\tExtended Checksum : 00\n");
+ EFPRINTF(fp, "[0003]\t\tReserved : 000000\n");
+
+ EFFLUSH(fp);
+
+ return (0);
+
+err_exit:
+ return (errno);
+}
+
+static int
+basl_fwrite_rsdt(FILE *fp)
+{
+ int err;
+
+ err = 0;
+
+ EFPRINTF(fp, "/*\n");
+ EFPRINTF(fp, " * bhyve RSDT template\n");
+ EFPRINTF(fp, " */\n");
+ EFPRINTF(fp, "[0004]\t\tSignature : \"RSDT\"\n");
+ EFPRINTF(fp, "[0004]\t\tTable Length : 00000000\n");
+ EFPRINTF(fp, "[0001]\t\tRevision : 01\n");
+ EFPRINTF(fp, "[0001]\t\tChecksum : 00\n");
+ EFPRINTF(fp, "[0006]\t\tOem ID : \"BHYVE \"\n");
+ EFPRINTF(fp, "[0008]\t\tOem Table ID : \"BVRSDT \"\n");
+ EFPRINTF(fp, "[0004]\t\tOem Revision : 00000001\n");
+ /* iasl will fill in the compiler ID/revision fields */
+ EFPRINTF(fp, "[0004]\t\tAsl Compiler ID : \"xxxx\"\n");
+ EFPRINTF(fp, "[0004]\t\tAsl Compiler Revision : 00000000\n");
+ EFPRINTF(fp, "\n");
+
+ /* Add in pointers to the MADT, FADT and HPET */
+ EFPRINTF(fp, "[0004]\t\tACPI Table Address 0 : %08X\n",
+ basl_acpi_base + MADT_OFFSET);
+ EFPRINTF(fp, "[0004]\t\tACPI Table Address 1 : %08X\n",
+ basl_acpi_base + FADT_OFFSET);
+ EFPRINTF(fp, "[0004]\t\tACPI Table Address 2 : %08X\n",
+ basl_acpi_base + HPET_OFFSET);
+ EFPRINTF(fp, "[0004]\t\tACPI Table Address 3 : %08X\n",
+ basl_acpi_base + MCFG_OFFSET);
+
+ EFFLUSH(fp);
+
+ return (0);
+
+err_exit:
+ return (errno);
+}
+
+static int
+basl_fwrite_xsdt(FILE *fp)
+{
+ int err;
+
+ err = 0;
+
+ EFPRINTF(fp, "/*\n");
+ EFPRINTF(fp, " * bhyve XSDT template\n");
+ EFPRINTF(fp, " */\n");
+ EFPRINTF(fp, "[0004]\t\tSignature : \"XSDT\"\n");
+ EFPRINTF(fp, "[0004]\t\tTable Length : 00000000\n");
+ EFPRINTF(fp, "[0001]\t\tRevision : 01\n");
+ EFPRINTF(fp, "[0001]\t\tChecksum : 00\n");
+ EFPRINTF(fp, "[0006]\t\tOem ID : \"BHYVE \"\n");
+ EFPRINTF(fp, "[0008]\t\tOem Table ID : \"BVXSDT \"\n");
+ EFPRINTF(fp, "[0004]\t\tOem Revision : 00000001\n");
+ /* iasl will fill in the compiler ID/revision fields */
+ EFPRINTF(fp, "[0004]\t\tAsl Compiler ID : \"xxxx\"\n");
+ EFPRINTF(fp, "[0004]\t\tAsl Compiler Revision : 00000000\n");
+ EFPRINTF(fp, "\n");
+
+ /* Add in pointers to the MADT, FADT and HPET */
+ EFPRINTF(fp, "[0004]\t\tACPI Table Address 0 : 00000000%08X\n",
+ basl_acpi_base + MADT_OFFSET);
+ EFPRINTF(fp, "[0004]\t\tACPI Table Address 1 : 00000000%08X\n",
+ basl_acpi_base + FADT_OFFSET);
+ EFPRINTF(fp, "[0004]\t\tACPI Table Address 2 : 00000000%08X\n",
+ basl_acpi_base + HPET_OFFSET);
+ EFPRINTF(fp, "[0004]\t\tACPI Table Address 3 : 00000000%08X\n",
+ basl_acpi_base + MCFG_OFFSET);
+
+ EFFLUSH(fp);
+
+ return (0);
+
+err_exit:
+ return (errno);
+}
+
+static int
+basl_fwrite_madt(FILE *fp)
+{
+ int err;
+ int i;
+
+ err = 0;
+
+ EFPRINTF(fp, "/*\n");
+ EFPRINTF(fp, " * bhyve MADT template\n");
+ EFPRINTF(fp, " */\n");
+ EFPRINTF(fp, "[0004]\t\tSignature : \"APIC\"\n");
+ EFPRINTF(fp, "[0004]\t\tTable Length : 00000000\n");
+ EFPRINTF(fp, "[0001]\t\tRevision : 01\n");
+ EFPRINTF(fp, "[0001]\t\tChecksum : 00\n");
+ EFPRINTF(fp, "[0006]\t\tOem ID : \"BHYVE \"\n");
+ EFPRINTF(fp, "[0008]\t\tOem Table ID : \"BVMADT \"\n");
+ EFPRINTF(fp, "[0004]\t\tOem Revision : 00000001\n");
+
+ /* iasl will fill in the compiler ID/revision fields */
+ EFPRINTF(fp, "[0004]\t\tAsl Compiler ID : \"xxxx\"\n");
+ EFPRINTF(fp, "[0004]\t\tAsl Compiler Revision : 00000000\n");
+ EFPRINTF(fp, "\n");
+
+ EFPRINTF(fp, "[0004]\t\tLocal Apic Address : FEE00000\n");
+ EFPRINTF(fp, "[0004]\t\tFlags (decoded below) : 00000001\n");
+ EFPRINTF(fp, "\t\t\tPC-AT Compatibility : 1\n");
+ EFPRINTF(fp, "\n");
+
+ /* Add a Processor Local APIC entry for each CPU */
+ for (i = 0; i < basl_ncpu; i++) {
+ EFPRINTF(fp, "[0001]\t\tSubtable Type : 00\n");
+ EFPRINTF(fp, "[0001]\t\tLength : 08\n");
+ /* iasl expects hex values for the proc and apic id's */
+ EFPRINTF(fp, "[0001]\t\tProcessor ID : %02x\n", i);
+ EFPRINTF(fp, "[0001]\t\tLocal Apic ID : %02x\n", i);
+ EFPRINTF(fp, "[0004]\t\tFlags (decoded below) : 00000001\n");
+ EFPRINTF(fp, "\t\t\tProcessor Enabled : 1\n");
+ EFPRINTF(fp, "\n");
+ }
+
+ /* Always a single IOAPIC entry, with ID 0 */
+ EFPRINTF(fp, "[0001]\t\tSubtable Type : 01\n");
+ EFPRINTF(fp, "[0001]\t\tLength : 0C\n");
+ /* iasl expects a hex value for the i/o apic id */
+ EFPRINTF(fp, "[0001]\t\tI/O Apic ID : %02x\n", 0);
+ EFPRINTF(fp, "[0001]\t\tReserved : 00\n");
+ EFPRINTF(fp, "[0004]\t\tAddress : fec00000\n");
+ EFPRINTF(fp, "[0004]\t\tInterrupt : 00000000\n");
+ EFPRINTF(fp, "\n");
+
+ /* Legacy IRQ0 is connected to pin 2 of the IOAPIC */
+ EFPRINTF(fp, "[0001]\t\tSubtable Type : 02\n");
+ EFPRINTF(fp, "[0001]\t\tLength : 0A\n");
+ EFPRINTF(fp, "[0001]\t\tBus : 00\n");
+ EFPRINTF(fp, "[0001]\t\tSource : 00\n");
+ EFPRINTF(fp, "[0004]\t\tInterrupt : 00000002\n");
+ EFPRINTF(fp, "[0002]\t\tFlags (decoded below) : 0005\n");
+ EFPRINTF(fp, "\t\t\tPolarity : 1\n");
+ EFPRINTF(fp, "\t\t\tTrigger Mode : 1\n");
+ EFPRINTF(fp, "\n");
+
+ EFPRINTF(fp, "[0001]\t\tSubtable Type : 02\n");
+ EFPRINTF(fp, "[0001]\t\tLength : 0A\n");
+ EFPRINTF(fp, "[0001]\t\tBus : 00\n");
+ EFPRINTF(fp, "[0001]\t\tSource : %02X\n", SCI_INT);
+ EFPRINTF(fp, "[0004]\t\tInterrupt : %08X\n", SCI_INT);
+ EFPRINTF(fp, "[0002]\t\tFlags (decoded below) : 0000\n");
+ EFPRINTF(fp, "\t\t\tPolarity : 3\n");
+ EFPRINTF(fp, "\t\t\tTrigger Mode : 3\n");
+ EFPRINTF(fp, "\n");
+
+ /* Local APIC NMI is connected to LINT 1 on all CPUs */
+ EFPRINTF(fp, "[0001]\t\tSubtable Type : 04\n");
+ EFPRINTF(fp, "[0001]\t\tLength : 06\n");
+ EFPRINTF(fp, "[0001]\t\tProcessorId : FF\n");
+ EFPRINTF(fp, "[0002]\t\tFlags (decoded below) : 0005\n");
+ EFPRINTF(fp, "\t\t\tPolarity : 1\n");
+ EFPRINTF(fp, "\t\t\tTrigger Mode : 1\n");
+ EFPRINTF(fp, "[0001]\t\tInterrupt : 01\n");
+ EFPRINTF(fp, "\n");
+
+ EFFLUSH(fp);
+
+ return (0);
+
+err_exit:
+ return (errno);
+}
+
+static int
+basl_fwrite_fadt(FILE *fp)
+{
+ int err;
+
+ err = 0;
+
+ EFPRINTF(fp, "/*\n");
+ EFPRINTF(fp, " * bhyve FADT template\n");
+ EFPRINTF(fp, " */\n");
+ EFPRINTF(fp, "[0004]\t\tSignature : \"FACP\"\n");
+ EFPRINTF(fp, "[0004]\t\tTable Length : 0000010C\n");
+ EFPRINTF(fp, "[0001]\t\tRevision : 05\n");
+ EFPRINTF(fp, "[0001]\t\tChecksum : 00\n");
+ EFPRINTF(fp, "[0006]\t\tOem ID : \"BHYVE \"\n");
+ EFPRINTF(fp, "[0008]\t\tOem Table ID : \"BVFACP \"\n");
+ EFPRINTF(fp, "[0004]\t\tOem Revision : 00000001\n");
+ /* iasl will fill in the compiler ID/revision fields */
+ EFPRINTF(fp, "[0004]\t\tAsl Compiler ID : \"xxxx\"\n");
+ EFPRINTF(fp, "[0004]\t\tAsl Compiler Revision : 00000000\n");
+ EFPRINTF(fp, "\n");
+
+ EFPRINTF(fp, "[0004]\t\tFACS Address : %08X\n",
+ basl_acpi_base + FACS_OFFSET);
+ EFPRINTF(fp, "[0004]\t\tDSDT Address : %08X\n",
+ basl_acpi_base + DSDT_OFFSET);
+ EFPRINTF(fp, "[0001]\t\tModel : 01\n");
+ EFPRINTF(fp, "[0001]\t\tPM Profile : 00 [Unspecified]\n");
+ EFPRINTF(fp, "[0002]\t\tSCI Interrupt : %04X\n",
+ SCI_INT);
+ EFPRINTF(fp, "[0004]\t\tSMI Command Port : %08X\n",
+ SMI_CMD);
+ EFPRINTF(fp, "[0001]\t\tACPI Enable Value : %02X\n",
+ BHYVE_ACPI_ENABLE);
+ EFPRINTF(fp, "[0001]\t\tACPI Disable Value : %02X\n",
+ BHYVE_ACPI_DISABLE);
+ EFPRINTF(fp, "[0001]\t\tS4BIOS Command : 00\n");
+ EFPRINTF(fp, "[0001]\t\tP-State Control : 00\n");
+ EFPRINTF(fp, "[0004]\t\tPM1A Event Block Address : %08X\n",
+ PM1A_EVT_ADDR);
+ EFPRINTF(fp, "[0004]\t\tPM1B Event Block Address : 00000000\n");
+ EFPRINTF(fp, "[0004]\t\tPM1A Control Block Address : %08X\n",
+ PM1A_CNT_ADDR);
+ EFPRINTF(fp, "[0004]\t\tPM1B Control Block Address : 00000000\n");
+ EFPRINTF(fp, "[0004]\t\tPM2 Control Block Address : 00000000\n");
+ EFPRINTF(fp, "[0004]\t\tPM Timer Block Address : %08X\n",
+ IO_PMTMR);
+ EFPRINTF(fp, "[0004]\t\tGPE0 Block Address : 00000000\n");
+ EFPRINTF(fp, "[0004]\t\tGPE1 Block Address : 00000000\n");
+ EFPRINTF(fp, "[0001]\t\tPM1 Event Block Length : 04\n");
+ EFPRINTF(fp, "[0001]\t\tPM1 Control Block Length : 02\n");
+ EFPRINTF(fp, "[0001]\t\tPM2 Control Block Length : 00\n");
+ EFPRINTF(fp, "[0001]\t\tPM Timer Block Length : 04\n");
+ EFPRINTF(fp, "[0001]\t\tGPE0 Block Length : 00\n");
+ EFPRINTF(fp, "[0001]\t\tGPE1 Block Length : 00\n");
+ EFPRINTF(fp, "[0001]\t\tGPE1 Base Offset : 00\n");
+ EFPRINTF(fp, "[0001]\t\t_CST Support : 00\n");
+ EFPRINTF(fp, "[0002]\t\tC2 Latency : 0000\n");
+ EFPRINTF(fp, "[0002]\t\tC3 Latency : 0000\n");
+ EFPRINTF(fp, "[0002]\t\tCPU Cache Size : 0000\n");
+ EFPRINTF(fp, "[0002]\t\tCache Flush Stride : 0000\n");
+ EFPRINTF(fp, "[0001]\t\tDuty Cycle Offset : 00\n");
+ EFPRINTF(fp, "[0001]\t\tDuty Cycle Width : 00\n");
+ EFPRINTF(fp, "[0001]\t\tRTC Day Alarm Index : 00\n");
+ EFPRINTF(fp, "[0001]\t\tRTC Month Alarm Index : 00\n");
+ EFPRINTF(fp, "[0001]\t\tRTC Century Index : 32\n");
+ EFPRINTF(fp, "[0002]\t\tBoot Flags (decoded below) : 0000\n");
+ EFPRINTF(fp, "\t\t\tLegacy Devices Supported (V2) : 0\n");
+ EFPRINTF(fp, "\t\t\t8042 Present on ports 60/64 (V2) : 0\n");
+ EFPRINTF(fp, "\t\t\tVGA Not Present (V4) : 1\n");
+ EFPRINTF(fp, "\t\t\tMSI Not Supported (V4) : 0\n");
+ EFPRINTF(fp, "\t\t\tPCIe ASPM Not Supported (V4) : 1\n");
+ EFPRINTF(fp, "\t\t\tCMOS RTC Not Present (V5) : 0\n");
+ EFPRINTF(fp, "[0001]\t\tReserved : 00\n");
+ EFPRINTF(fp, "[0004]\t\tFlags (decoded below) : 00000000\n");
+ EFPRINTF(fp, "\t\t\tWBINVD instruction is operational (V1) : 1\n");
+ EFPRINTF(fp, "\t\t\tWBINVD flushes all caches (V1) : 0\n");
+ EFPRINTF(fp, "\t\t\tAll CPUs support C1 (V1) : 1\n");
+ EFPRINTF(fp, "\t\t\tC2 works on MP system (V1) : 0\n");
+ EFPRINTF(fp, "\t\t\tControl Method Power Button (V1) : 0\n");
+ EFPRINTF(fp, "\t\t\tControl Method Sleep Button (V1) : 1\n");
+ EFPRINTF(fp, "\t\t\tRTC wake not in fixed reg space (V1) : 0\n");
+ EFPRINTF(fp, "\t\t\tRTC can wake system from S4 (V1) : 0\n");
+ EFPRINTF(fp, "\t\t\t32-bit PM Timer (V1) : 1\n");
+ EFPRINTF(fp, "\t\t\tDocking Supported (V1) : 0\n");
+ EFPRINTF(fp, "\t\t\tReset Register Supported (V2) : 1\n");
+ EFPRINTF(fp, "\t\t\tSealed Case (V3) : 0\n");
+ EFPRINTF(fp, "\t\t\tHeadless - No Video (V3) : 1\n");
+ EFPRINTF(fp, "\t\t\tUse native instr after SLP_TYPx (V3) : 0\n");
+ EFPRINTF(fp, "\t\t\tPCIEXP_WAK Bits Supported (V4) : 0\n");
+ EFPRINTF(fp, "\t\t\tUse Platform Timer (V4) : 0\n");
+ EFPRINTF(fp, "\t\t\tRTC_STS valid on S4 wake (V4) : 0\n");
+ EFPRINTF(fp, "\t\t\tRemote Power-on capable (V4) : 0\n");
+ EFPRINTF(fp, "\t\t\tUse APIC Cluster Model (V4) : 0\n");
+ EFPRINTF(fp, "\t\t\tUse APIC Physical Destination Mode (V4) : 1\n");
+ EFPRINTF(fp, "\t\t\tHardware Reduced (V5) : 0\n");
+ EFPRINTF(fp, "\t\t\tLow Power S0 Idle (V5) : 0\n");
+ EFPRINTF(fp, "\n");
+
+ EFPRINTF(fp,
+ "[0012]\t\tReset Register : [Generic Address Structure]\n");
+ EFPRINTF(fp, "[0001]\t\tSpace ID : 01 [SystemIO]\n");
+ EFPRINTF(fp, "[0001]\t\tBit Width : 08\n");
+ EFPRINTF(fp, "[0001]\t\tBit Offset : 00\n");
+ EFPRINTF(fp, "[0001]\t\tEncoded Access Width : 01 [Byte Access:8]\n");
+ EFPRINTF(fp, "[0008]\t\tAddress : 0000000000000CF9\n");
+ EFPRINTF(fp, "\n");
+
+ EFPRINTF(fp, "[0001]\t\tValue to cause reset : 06\n");
+ EFPRINTF(fp, "[0002]\t\tARM Flags (decoded below): 0000\n");
+ EFPRINTF(fp, "\t\t\tPSCI Compliant : 0\n");
+ EFPRINTF(fp, "\t\t\tMust use HVC for PSCI : 0\n");
+ EFPRINTF(fp, "[0001]\t\tFADT Minor Revision : 01\n");
+ EFPRINTF(fp, "[0008]\t\tFACS Address : 00000000%08X\n",
+ basl_acpi_base + FACS_OFFSET);
+ EFPRINTF(fp, "[0008]\t\tDSDT Address : 00000000%08X\n",
+ basl_acpi_base + DSDT_OFFSET);
+ EFPRINTF(fp,
+ "[0012]\t\tPM1A Event Block : [Generic Address Structure]\n");
+ EFPRINTF(fp, "[0001]\t\tSpace ID : 01 [SystemIO]\n");
+ EFPRINTF(fp, "[0001]\t\tBit Width : 20\n");
+ EFPRINTF(fp, "[0001]\t\tBit Offset : 00\n");
+ EFPRINTF(fp, "[0001]\t\tEncoded Access Width : 02 [Word Access:16]\n");
+ EFPRINTF(fp, "[0008]\t\tAddress : 00000000%08X\n",
+ PM1A_EVT_ADDR);
+ EFPRINTF(fp, "\n");
+
+ EFPRINTF(fp,
+ "[0012]\t\tPM1B Event Block : [Generic Address Structure]\n");
+ EFPRINTF(fp, "[0001]\t\tSpace ID : 01 [SystemIO]\n");
+ EFPRINTF(fp, "[0001]\t\tBit Width : 00\n");
+ EFPRINTF(fp, "[0001]\t\tBit Offset : 00\n");
+ EFPRINTF(fp,
+ "[0001]\t\tEncoded Access Width : 00 [Undefined/Legacy]\n");
+ EFPRINTF(fp, "[0008]\t\tAddress : 0000000000000000\n");
+ EFPRINTF(fp, "\n");
+
+ EFPRINTF(fp,
+ "[0012]\t\tPM1A Control Block : [Generic Address Structure]\n");
+ EFPRINTF(fp, "[0001]\t\tSpace ID : 01 [SystemIO]\n");
+ EFPRINTF(fp, "[0001]\t\tBit Width : 10\n");
+ EFPRINTF(fp, "[0001]\t\tBit Offset : 00\n");
+ EFPRINTF(fp, "[0001]\t\tEncoded Access Width : 02 [Word Access:16]\n");
+ EFPRINTF(fp, "[0008]\t\tAddress : 00000000%08X\n",
+ PM1A_CNT_ADDR);
+ EFPRINTF(fp, "\n");
+
+ EFPRINTF(fp,
+ "[0012]\t\tPM1B Control Block : [Generic Address Structure]\n");
+ EFPRINTF(fp, "[0001]\t\tSpace ID : 01 [SystemIO]\n");
+ EFPRINTF(fp, "[0001]\t\tBit Width : 00\n");
+ EFPRINTF(fp, "[0001]\t\tBit Offset : 00\n");
+ EFPRINTF(fp,
+ "[0001]\t\tEncoded Access Width : 00 [Undefined/Legacy]\n");
+ EFPRINTF(fp, "[0008]\t\tAddress : 0000000000000000\n");
+ EFPRINTF(fp, "\n");
+
+ EFPRINTF(fp,
+ "[0012]\t\tPM2 Control Block : [Generic Address Structure]\n");
+ EFPRINTF(fp, "[0001]\t\tSpace ID : 01 [SystemIO]\n");
+ EFPRINTF(fp, "[0001]\t\tBit Width : 08\n");
+ EFPRINTF(fp, "[0001]\t\tBit Offset : 00\n");
+ EFPRINTF(fp,
+ "[0001]\t\tEncoded Access Width : 00 [Undefined/Legacy]\n");
+ EFPRINTF(fp, "[0008]\t\tAddress : 0000000000000000\n");
+ EFPRINTF(fp, "\n");
+
+ /* Valid for bhyve */
+ EFPRINTF(fp,
+ "[0012]\t\tPM Timer Block : [Generic Address Structure]\n");
+ EFPRINTF(fp, "[0001]\t\tSpace ID : 01 [SystemIO]\n");
+ EFPRINTF(fp, "[0001]\t\tBit Width : 20\n");
+ EFPRINTF(fp, "[0001]\t\tBit Offset : 00\n");
+ EFPRINTF(fp,
+ "[0001]\t\tEncoded Access Width : 03 [DWord Access:32]\n");
+ EFPRINTF(fp, "[0008]\t\tAddress : 00000000%08X\n",
+ IO_PMTMR);
+ EFPRINTF(fp, "\n");
+
+ EFPRINTF(fp, "[0012]\t\tGPE0 Block : [Generic Address Structure]\n");
+ EFPRINTF(fp, "[0001]\t\tSpace ID : 01 [SystemIO]\n");
+ EFPRINTF(fp, "[0001]\t\tBit Width : 00\n");
+ EFPRINTF(fp, "[0001]\t\tBit Offset : 00\n");
+ EFPRINTF(fp, "[0001]\t\tEncoded Access Width : 01 [Byte Access:8]\n");
+ EFPRINTF(fp, "[0008]\t\tAddress : 0000000000000000\n");
+ EFPRINTF(fp, "\n");
+
+ EFPRINTF(fp, "[0012]\t\tGPE1 Block : [Generic Address Structure]\n");
+ EFPRINTF(fp, "[0001]\t\tSpace ID : 01 [SystemIO]\n");
+ EFPRINTF(fp, "[0001]\t\tBit Width : 00\n");
+ EFPRINTF(fp, "[0001]\t\tBit Offset : 00\n");
+ EFPRINTF(fp,
+ "[0001]\t\tEncoded Access Width : 00 [Undefined/Legacy]\n");
+ EFPRINTF(fp, "[0008]\t\tAddress : 0000000000000000\n");
+ EFPRINTF(fp, "\n");
+
+ EFPRINTF(fp,
+ "[0012]\t\tSleep Control Register : [Generic Address Structure]\n");
+ EFPRINTF(fp, "[0001]\t\tSpace ID : 01 [SystemIO]\n");
+ EFPRINTF(fp, "[0001]\t\tBit Width : 08\n");
+ EFPRINTF(fp, "[0001]\t\tBit Offset : 00\n");
+ EFPRINTF(fp, "[0001]\t\tEncoded Access Width : 01 [Byte Access:8]\n");
+ EFPRINTF(fp, "[0008]\t\tAddress : 0000000000000000\n");
+ EFPRINTF(fp, "\n");
+
+ EFPRINTF(fp,
+ "[0012]\t\tSleep Status Register : [Generic Address Structure]\n");
+ EFPRINTF(fp, "[0001]\t\tSpace ID : 01 [SystemIO]\n");
+ EFPRINTF(fp, "[0001]\t\tBit Width : 08\n");
+ EFPRINTF(fp, "[0001]\t\tBit Offset : 00\n");
+ EFPRINTF(fp, "[0001]\t\tEncoded Access Width : 01 [Byte Access:8]\n");
+ EFPRINTF(fp, "[0008]\t\tAddress : 0000000000000000\n");
+
+ EFFLUSH(fp);
+
+ return (0);
+
+err_exit:
+ return (errno);
+}
+
+static int
+basl_fwrite_hpet(FILE *fp)
+{
+ int err;
+
+ err = 0;
+
+ EFPRINTF(fp, "/*\n");
+ EFPRINTF(fp, " * bhyve HPET template\n");
+ EFPRINTF(fp, " */\n");
+ EFPRINTF(fp, "[0004]\t\tSignature : \"HPET\"\n");
+ EFPRINTF(fp, "[0004]\t\tTable Length : 00000000\n");
+ EFPRINTF(fp, "[0001]\t\tRevision : 01\n");
+ EFPRINTF(fp, "[0001]\t\tChecksum : 00\n");
+ EFPRINTF(fp, "[0006]\t\tOem ID : \"BHYVE \"\n");
+ EFPRINTF(fp, "[0008]\t\tOem Table ID : \"BVHPET \"\n");
+ EFPRINTF(fp, "[0004]\t\tOem Revision : 00000001\n");
+
+ /* iasl will fill in the compiler ID/revision fields */
+ EFPRINTF(fp, "[0004]\t\tAsl Compiler ID : \"xxxx\"\n");
+ EFPRINTF(fp, "[0004]\t\tAsl Compiler Revision : 00000000\n");
+ EFPRINTF(fp, "\n");
+
+ EFPRINTF(fp, "[0004]\t\tTimer Block ID : %08X\n", hpet_capabilities);
+ EFPRINTF(fp,
+ "[0012]\t\tTimer Block Register : [Generic Address Structure]\n");
+ EFPRINTF(fp, "[0001]\t\tSpace ID : 00 [SystemMemory]\n");
+ EFPRINTF(fp, "[0001]\t\tBit Width : 00\n");
+ EFPRINTF(fp, "[0001]\t\tBit Offset : 00\n");
+ EFPRINTF(fp,
+ "[0001]\t\tEncoded Access Width : 00 [Undefined/Legacy]\n");
+ EFPRINTF(fp, "[0008]\t\tAddress : 00000000FED00000\n");
+ EFPRINTF(fp, "\n");
+
+ EFPRINTF(fp, "[0001]\t\tHPET Number : 00\n");
+ EFPRINTF(fp, "[0002]\t\tMinimum Clock Ticks : 0000\n");
+ EFPRINTF(fp, "[0004]\t\tFlags (decoded below) : 00000001\n");
+ EFPRINTF(fp, "\t\t\t4K Page Protect : 1\n");
+ EFPRINTF(fp, "\t\t\t64K Page Protect : 0\n");
+ EFPRINTF(fp, "\n");
+
+ EFFLUSH(fp);
+
+ return (0);
+
+err_exit:
+ return (errno);
+}
+
+static int
+basl_fwrite_mcfg(FILE *fp)
+{
+ int err = 0;
+
+ EFPRINTF(fp, "/*\n");
+ EFPRINTF(fp, " * bhyve MCFG template\n");
+ EFPRINTF(fp, " */\n");
+ EFPRINTF(fp, "[0004]\t\tSignature : \"MCFG\"\n");
+ EFPRINTF(fp, "[0004]\t\tTable Length : 00000000\n");
+ EFPRINTF(fp, "[0001]\t\tRevision : 01\n");
+ EFPRINTF(fp, "[0001]\t\tChecksum : 00\n");
+ EFPRINTF(fp, "[0006]\t\tOem ID : \"BHYVE \"\n");
+ EFPRINTF(fp, "[0008]\t\tOem Table ID : \"BVMCFG \"\n");
+ EFPRINTF(fp, "[0004]\t\tOem Revision : 00000001\n");
+
+ /* iasl will fill in the compiler ID/revision fields */
+ EFPRINTF(fp, "[0004]\t\tAsl Compiler ID : \"xxxx\"\n");
+ EFPRINTF(fp, "[0004]\t\tAsl Compiler Revision : 00000000\n");
+ EFPRINTF(fp, "[0008]\t\tReserved : 0\n");
+ EFPRINTF(fp, "\n");
+
+ EFPRINTF(fp, "[0008]\t\tBase Address : %016lX\n", pci_ecfg_base());
+ EFPRINTF(fp, "[0002]\t\tSegment Group: 0000\n");
+ EFPRINTF(fp, "[0001]\t\tStart Bus: 00\n");
+ EFPRINTF(fp, "[0001]\t\tEnd Bus: FF\n");
+ EFPRINTF(fp, "[0004]\t\tReserved : 0\n");
+ EFFLUSH(fp);
+ return (0);
+err_exit:
+ return (errno);
+}
+
+static int
+basl_fwrite_facs(FILE *fp)
+{
+ int err;
+
+ err = 0;
+
+ EFPRINTF(fp, "/*\n");
+ EFPRINTF(fp, " * bhyve FACS template\n");
+ EFPRINTF(fp, " */\n");
+ EFPRINTF(fp, "[0004]\t\tSignature : \"FACS\"\n");
+ EFPRINTF(fp, "[0004]\t\tLength : 00000040\n");
+ EFPRINTF(fp, "[0004]\t\tHardware Signature : 00000000\n");
+ EFPRINTF(fp, "[0004]\t\t32 Firmware Waking Vector : 00000000\n");
+ EFPRINTF(fp, "[0004]\t\tGlobal Lock : 00000000\n");
+ EFPRINTF(fp, "[0004]\t\tFlags (decoded below) : 00000000\n");
+ EFPRINTF(fp, "\t\t\tS4BIOS Support Present : 0\n");
+ EFPRINTF(fp, "\t\t\t64-bit Wake Supported (V2) : 0\n");
+ EFPRINTF(fp,
+ "[0008]\t\t64 Firmware Waking Vector : 0000000000000000\n");
+ EFPRINTF(fp, "[0001]\t\tVersion : 02\n");
+ EFPRINTF(fp, "[0003]\t\tReserved : 000000\n");
+ EFPRINTF(fp, "[0004]\t\tOspmFlags (decoded below) : 00000000\n");
+ EFPRINTF(fp, "\t\t\t64-bit Wake Env Required (V2) : 0\n");
+
+ EFFLUSH(fp);
+
+ return (0);
+
+err_exit:
+ return (errno);
+}
+
+/*
+ * Helper routines for writing to the DSDT from other modules.
+ */
+void
+dsdt_line(const char *fmt, ...)
+{
+ va_list ap;
+ int err = 0;
+
+ if (dsdt_error != 0)
+ return;
+
+ if (strcmp(fmt, "") != 0) {
+ if (dsdt_indent_level != 0)
+ EFPRINTF(dsdt_fp, "%*c", dsdt_indent_level * 2, ' ');
+ va_start(ap, fmt);
+ if (vfprintf(dsdt_fp, fmt, ap) < 0)
+ goto err_exit;
+ va_end(ap);
+ }
+ EFPRINTF(dsdt_fp, "\n");
+ return;
+
+err_exit:
+ dsdt_error = errno;
+}
+
+void
+dsdt_indent(int levels)
+{
+
+ dsdt_indent_level += levels;
+ assert(dsdt_indent_level >= 0);
+}
+
+void
+dsdt_unindent(int levels)
+{
+
+ assert(dsdt_indent_level >= levels);
+ dsdt_indent_level -= levels;
+}
+
+void
+dsdt_fixed_ioport(uint16_t iobase, uint16_t length)
+{
+
+ dsdt_line("IO (Decode16,");
+ dsdt_line(" 0x%04X, // Range Minimum", iobase);
+ dsdt_line(" 0x%04X, // Range Maximum", iobase);
+ dsdt_line(" 0x01, // Alignment");
+ dsdt_line(" 0x%02X, // Length", length);
+ dsdt_line(" )");
+}
+
+void
+dsdt_fixed_irq(uint8_t irq)
+{
+
+ dsdt_line("IRQNoFlags ()");
+ dsdt_line(" {%d}", irq);
+}
+
+void
+dsdt_fixed_mem32(uint32_t base, uint32_t length)
+{
+
+ dsdt_line("Memory32Fixed (ReadWrite,");
+ dsdt_line(" 0x%08X, // Address Base", base);
+ dsdt_line(" 0x%08X, // Address Length", length);
+ dsdt_line(" )");
+}
+
+static int
+basl_fwrite_dsdt(FILE *fp)
+{
+ int err;
+
+ err = 0;
+ dsdt_fp = fp;
+ dsdt_error = 0;
+ dsdt_indent_level = 0;
+
+ dsdt_line("/*");
+ dsdt_line(" * bhyve DSDT template");
+ dsdt_line(" */");
+ dsdt_line("DefinitionBlock (\"bhyve_dsdt.aml\", \"DSDT\", 2,"
+ "\"BHYVE \", \"BVDSDT \", 0x00000001)");
+ dsdt_line("{");
+ dsdt_line(" Name (_S5, Package ()");
+ dsdt_line(" {");
+ dsdt_line(" 0x05,");
+ dsdt_line(" Zero,");
+ dsdt_line(" })");
+
+ pci_write_dsdt();
+
+ dsdt_line("");
+ dsdt_line(" Scope (_SB.PC00)");
+ dsdt_line(" {");
+ dsdt_line(" Device (HPET)");
+ dsdt_line(" {");
+ dsdt_line(" Name (_HID, EISAID(\"PNP0103\"))");
+ dsdt_line(" Name (_UID, 0)");
+ dsdt_line(" Name (_CRS, ResourceTemplate ()");
+ dsdt_line(" {");
+ dsdt_indent(4);
+ dsdt_fixed_mem32(0xFED00000, 0x400);
+ dsdt_unindent(4);
+ dsdt_line(" })");
+ dsdt_line(" }");
+ dsdt_line(" }");
+ dsdt_line("}");
+
+ if (dsdt_error != 0)
+ return (dsdt_error);
+
+ EFFLUSH(fp);
+
+ return (0);
+
+err_exit:
+ return (errno);
+}
+
+static int
+basl_open(struct basl_fio *bf, int suffix)
+{
+ int err;
+
+ err = 0;
+
+ if (suffix) {
+ strlcpy(bf->f_name, basl_stemplate, MAXPATHLEN);
+ bf->fd = mkstemps(bf->f_name, strlen(BHYVE_ASL_SUFFIX));
+ } else {
+ strlcpy(bf->f_name, basl_template, MAXPATHLEN);
+ bf->fd = mkstemp(bf->f_name);
+ }
+
+ if (bf->fd > 0) {
+ bf->fp = fdopen(bf->fd, "w+");
+ if (bf->fp == NULL) {
+ unlink(bf->f_name);
+ close(bf->fd);
+ }
+ } else {
+ err = 1;
+ }
+
+ return (err);
+}
+
+static void
+basl_close(struct basl_fio *bf)
+{
+
+ if (!basl_keep_temps)
+ unlink(bf->f_name);
+ fclose(bf->fp);
+}
+
+static int
+basl_start(struct basl_fio *in, struct basl_fio *out)
+{
+ int err;
+
+ err = basl_open(in, 0);
+ if (!err) {
+ err = basl_open(out, 1);
+ if (err) {
+ basl_close(in);
+ }
+ }
+
+ return (err);
+}
+
+static void
+basl_end(struct basl_fio *in, struct basl_fio *out)
+{
+
+ basl_close(in);
+ basl_close(out);
+}
+
+static int
+basl_load(struct vmctx *ctx, int fd, uint64_t off)
+{
+ struct stat sb;
+ void *gaddr;
+
+ if (fstat(fd, &sb) < 0)
+ return (errno);
+
+ gaddr = paddr_guest2host(ctx, basl_acpi_base + off, sb.st_size);
+ if (gaddr == NULL)
+ return (EFAULT);
+
+ if (read(fd, gaddr, sb.st_size) < 0)
+ return (errno);
+
+ return (0);
+}
+
+static int
+basl_compile(struct vmctx *ctx, int (*fwrite_section)(FILE *), uint64_t offset)
+{
+ struct basl_fio io[2];
+ static char iaslbuf[3*MAXPATHLEN + 10];
+ char *fmt;
+ int err;
+
+ err = basl_start(&io[0], &io[1]);
+ if (!err) {
+ err = (*fwrite_section)(io[0].fp);
+
+ if (!err) {
+ /*
+ * iasl sends the results of the compilation to
+ * stdout. Shut this down by using the shell to
+ * redirect stdout to /dev/null, unless the user
+ * has requested verbose output for debugging
+ * purposes
+ */
+ fmt = basl_verbose_iasl ?
+ "%s -p %s %s" :
+ "/bin/sh -c \"%s -p %s %s\" 1> /dev/null";
+
+ snprintf(iaslbuf, sizeof(iaslbuf),
+ fmt,
+ BHYVE_ASL_COMPILER,
+ io[1].f_name, io[0].f_name);
+ err = system(iaslbuf);
+
+ if (!err) {
+ /*
+ * Copy the aml output file into guest
+ * memory at the specified location
+ */
+ err = basl_load(ctx, io[1].fd, offset);
+ }
+ }
+ basl_end(&io[0], &io[1]);
+ }
+
+ return (err);
+}
+
+static int
+basl_make_templates(void)
+{
+ const char *tmpdir;
+ int err;
+ int len;
+
+ err = 0;
+
+ /*
+ *
+ */
+ if ((tmpdir = getenv("BHYVE_TMPDIR")) == NULL || *tmpdir == '\0' ||
+ (tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0') {
+ tmpdir = _PATH_TMP;
+ }
+
+ len = strlen(tmpdir);
+
+ if ((len + sizeof(BHYVE_ASL_TEMPLATE) + 1) < MAXPATHLEN) {
+ strcpy(basl_template, tmpdir);
+ while (len > 0 && basl_template[len - 1] == '/')
+ len--;
+ basl_template[len] = '/';
+ strcpy(&basl_template[len + 1], BHYVE_ASL_TEMPLATE);
+ } else
+ err = E2BIG;
+
+ if (!err) {
+ /*
+ * len has been intialized (and maybe adjusted) above
+ */
+ if ((len + sizeof(BHYVE_ASL_TEMPLATE) + 1 +
+ sizeof(BHYVE_ASL_SUFFIX)) < MAXPATHLEN) {
+ strcpy(basl_stemplate, tmpdir);
+ basl_stemplate[len] = '/';
+ strcpy(&basl_stemplate[len + 1], BHYVE_ASL_TEMPLATE);
+ len = strlen(basl_stemplate);
+ strcpy(&basl_stemplate[len], BHYVE_ASL_SUFFIX);
+ } else
+ err = E2BIG;
+ }
+
+ return (err);
+}
+
+static struct {
+ int (*wsect)(FILE *fp);
+ uint64_t offset;
+} basl_ftables[] =
+{
+ { basl_fwrite_rsdp, 0},
+ { basl_fwrite_rsdt, RSDT_OFFSET },
+ { basl_fwrite_xsdt, XSDT_OFFSET },
+ { basl_fwrite_madt, MADT_OFFSET },
+ { basl_fwrite_fadt, FADT_OFFSET },
+ { basl_fwrite_hpet, HPET_OFFSET },
+ { basl_fwrite_mcfg, MCFG_OFFSET },
+ { basl_fwrite_facs, FACS_OFFSET },
+ { basl_fwrite_dsdt, DSDT_OFFSET },
+ { NULL }
+};
+
+int
+acpi_build(struct vmctx *ctx, int ncpu)
+{
+ int err;
+ int i;
+
+ basl_ncpu = ncpu;
+
+ err = vm_get_hpet_capabilities(ctx, &hpet_capabilities);
+ if (err != 0)
+ return (err);
+
+ /*
+ * For debug, allow the user to have iasl compiler output sent
+ * to stdout rather than /dev/null
+ */
+ if (getenv("BHYVE_ACPI_VERBOSE_IASL"))
+ basl_verbose_iasl = 1;
+
+ /*
+ * Allow the user to keep the generated ASL files for debugging
+ * instead of deleting them following use
+ */
+ if (getenv("BHYVE_ACPI_KEEPTMPS"))
+ basl_keep_temps = 1;
+
+ i = 0;
+ err = basl_make_templates();
+
+ /*
+ * Run through all the ASL files, compiling them and
+ * copying them into guest memory
+ */
+ while (!err && basl_ftables[i].wsect != NULL) {
+ err = basl_compile(ctx, basl_ftables[i].wsect,
+ basl_ftables[i].offset);
+ i++;
+ }
+
+ return (err);
+}
diff --git a/usr/src/cmd/bhyve/acpi.h b/usr/src/cmd/bhyve/acpi.h
index 477f827286..652164af35 100644
--- a/usr/src/cmd/bhyve/acpi.h
+++ b/usr/src/cmd/bhyve/acpi.h
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/acpi.h 266125 2014-05-15 14:16:55Z jhb $
+ * $FreeBSD$
*/
#ifndef _ACPI_H_
diff --git a/usr/src/cmd/bhyve/ahci.h b/usr/src/cmd/bhyve/ahci.h
index 1cf09adcbf..1fd9f208e8 100644
--- a/usr/src/cmd/bhyve/ahci.h
+++ b/usr/src/cmd/bhyve/ahci.h
@@ -24,7 +24,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/ahci.h 256056 2013-10-04 18:31:38Z grehan $
+ * $FreeBSD$
*/
#ifndef _AHCI_H_
@@ -96,13 +96,14 @@
#define ATA_SS_SPD_NO_SPEED 0x00000000
#define ATA_SS_SPD_GEN1 0x00000010
#define ATA_SS_SPD_GEN2 0x00000020
-#define ATA_SS_SPD_GEN3 0x00000040
+#define ATA_SS_SPD_GEN3 0x00000030
#define ATA_SS_IPM_MASK 0x00000f00
#define ATA_SS_IPM_NO_DEVICE 0x00000000
#define ATA_SS_IPM_ACTIVE 0x00000100
#define ATA_SS_IPM_PARTIAL 0x00000200
#define ATA_SS_IPM_SLUMBER 0x00000600
+#define ATA_SS_IPM_DEVSLEEP 0x00000800
#define ATA_SERROR 14
#define ATA_SE_DATA_CORRECTED 0x00000001
@@ -133,17 +134,19 @@
#define ATA_SC_SPD_NO_SPEED 0x00000000
#define ATA_SC_SPD_SPEED_GEN1 0x00000010
#define ATA_SC_SPD_SPEED_GEN2 0x00000020
-#define ATA_SC_SPD_SPEED_GEN3 0x00000040
+#define ATA_SC_SPD_SPEED_GEN3 0x00000030
#define ATA_SC_IPM_MASK 0x00000f00
#define ATA_SC_IPM_NONE 0x00000000
#define ATA_SC_IPM_DIS_PARTIAL 0x00000100
#define ATA_SC_IPM_DIS_SLUMBER 0x00000200
+#define ATA_SC_IPM_DIS_DEVSLEEP 0x00000400
#define ATA_SACTIVE 16
#define AHCI_MAX_PORTS 32
#define AHCI_MAX_SLOTS 32
+#define AHCI_MAX_IRQS 16
/* SATA AHCI v1.0 register defines */
#define AHCI_CAP 0x00
@@ -208,6 +211,9 @@
#define AHCI_CAP2_BOH 0x00000001
#define AHCI_CAP2_NVMP 0x00000002
#define AHCI_CAP2_APST 0x00000004
+#define AHCI_CAP2_SDS 0x00000008
+#define AHCI_CAP2_SADM 0x00000010
+#define AHCI_CAP2_DESO 0x00000020
#define AHCI_OFFSET 0x100
#define AHCI_STEP 0x80
@@ -265,6 +271,7 @@
#define AHCI_P_CMD_ACTIVE 0x10000000
#define AHCI_P_CMD_PARTIAL 0x20000000
#define AHCI_P_CMD_SLUMBER 0x60000000
+#define AHCI_P_CMD_DEVSLEEP 0x80000000
#define AHCI_P_TFD 0x20
#define AHCI_P_SIG 0x24
@@ -284,6 +291,17 @@
#define AHCI_P_FBS_ADO_SHIFT 12
#define AHCI_P_FBS_DWE 0x000f0000
#define AHCI_P_FBS_DWE_SHIFT 16
+#define AHCI_P_DEVSLP 0x44
+#define AHCI_P_DEVSLP_ADSE 0x00000001
+#define AHCI_P_DEVSLP_DSP 0x00000002
+#define AHCI_P_DEVSLP_DETO 0x000003fc
+#define AHCI_P_DEVSLP_DETO_SHIFT 2
+#define AHCI_P_DEVSLP_MDAT 0x00007c00
+#define AHCI_P_DEVSLP_MDAT_SHIFT 10
+#define AHCI_P_DEVSLP_DITO 0x01ff8000
+#define AHCI_P_DEVSLP_DITO_SHIFT 15
+#define AHCI_P_DEVSLP_DM 0x0e000000
+#define AHCI_P_DEVSLP_DM_SHIFT 25
/* Just to be sure, if building as module. */
#if MAXPHYS < 512 * 1024
diff --git a/usr/src/cmd/bhyve/atkbdc.c b/usr/src/cmd/bhyve/atkbdc.c
index a1501fa31d..8e71b0507c 100644
--- a/usr/src/cmd/bhyve/atkbdc.c
+++ b/usr/src/cmd/bhyve/atkbdc.c
@@ -26,7 +26,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/usr.sbin/bhyve/atkbdc.c 267611 2014-06-18 17:20:02Z neel $");
+__FBSDID("$FreeBSD$");
#include <sys/types.h>
@@ -99,19 +99,21 @@ __FBSDID("$FreeBSD: head/usr.sbin/bhyve/atkbdc.c 267611 2014-06-18 17:20:02Z nee
#define KBDO_AUX_OUTFULL 0x20
#define RAMSZ 32
+#define FIFOSZ 15
+#define CTRL_CMD_FLAG 0x8000
struct kbd_dev {
bool irq_active;
int irq;
- uint8_t buffer;
+ uint8_t buffer[FIFOSZ];
+ int brd, bwr;
+ int bcnt;
};
struct aux_dev {
bool irq_active;
int irq;
-
- uint8_t buffer;
};
struct atkbdc_softc {
@@ -126,6 +128,7 @@ struct atkbdc_softc {
uint8_t ram[RAMSZ]; /* byte0 = controller config */
uint32_t curcmd; /* current command for next byte */
+ uint32_t ctrlbyte;
struct kbd_dev kbd;
struct aux_dev aux;
@@ -134,72 +137,37 @@ struct atkbdc_softc {
static void
atkbdc_assert_kbd_intr(struct atkbdc_softc *sc)
{
- if (!sc->kbd.irq_active &&
- (sc->ram[0] & KBD_ENABLE_KBD_INT) != 0) {
+ if ((sc->ram[0] & KBD_ENABLE_KBD_INT) != 0) {
sc->kbd.irq_active = true;
- vm_isa_assert_irq(sc->ctx, sc->kbd.irq, sc->kbd.irq);
- }
-}
-
-static void
-atkbdc_deassert_kbd_intr(struct atkbdc_softc *sc)
-{
- if (sc->kbd.irq_active) {
- vm_isa_deassert_irq(sc->ctx, sc->kbd.irq, sc->kbd.irq);
- sc->kbd.irq_active = false;
+ vm_isa_pulse_irq(sc->ctx, sc->kbd.irq, sc->kbd.irq);
}
}
static void
atkbdc_assert_aux_intr(struct atkbdc_softc *sc)
{
- if (!sc->aux.irq_active &&
- (sc->ram[0] & KBD_ENABLE_AUX_INT) != 0) {
+ if ((sc->ram[0] & KBD_ENABLE_AUX_INT) != 0) {
sc->aux.irq_active = true;
- vm_isa_assert_irq(sc->ctx, sc->aux.irq, sc->aux.irq);
- }
-}
-
-static void
-atkbdc_deassert_aux_intr(struct atkbdc_softc *sc)
-{
- if (sc->aux.irq_active) {
- vm_isa_deassert_irq(sc->ctx, sc->aux.irq, sc->aux.irq);
- sc->aux.irq_active = false;
+ vm_isa_pulse_irq(sc->ctx, sc->aux.irq, sc->aux.irq);
}
}
-static void
-atkbdc_aux_queue_data(struct atkbdc_softc *sc, uint8_t val)
-{
- assert(pthread_mutex_isowned_np(&sc->mtx));
-
- sc->aux.buffer = val;
- sc->status |= (KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
- sc->outport |= KBDO_AUX_OUTFULL;
- atkbdc_assert_aux_intr(sc);
-}
-
-static void
+static int
atkbdc_kbd_queue_data(struct atkbdc_softc *sc, uint8_t val)
{
assert(pthread_mutex_isowned_np(&sc->mtx));
- sc->kbd.buffer = val;
- sc->status |= KBDS_KBD_BUFFER_FULL;
- sc->outport |= KBDO_KBD_OUTFULL;
- atkbdc_assert_kbd_intr(sc);
-}
-
-static void
-atkbdc_aux_read(struct atkbdc_softc *sc)
-{
- uint8_t val;
-
- assert(pthread_mutex_isowned_np(&sc->mtx));
+ if (sc->kbd.bcnt < FIFOSZ) {
+ sc->kbd.buffer[sc->kbd.bwr] = val;
+ sc->kbd.bwr = (sc->kbd.bwr + 1) % FIFOSZ;
+ sc->kbd.bcnt++;
+ sc->status |= KBDS_KBD_BUFFER_FULL;
+ sc->outport |= KBDO_KBD_OUTFULL;
+ } else {
+ printf("atkbd data buffer full\n");
+ }
- if (ps2mouse_read(sc->ps2mouse_sc, &val) != -1)
- atkbdc_aux_queue_data(sc, val);
+ return (sc->kbd.bcnt < FIFOSZ);
}
static void
@@ -252,21 +220,31 @@ atkbdc_kbd_read(struct atkbdc_softc *sc)
} else {
val = translation[val] | release;
}
-
atkbdc_kbd_queue_data(sc, val);
break;
}
} else {
- if (ps2kbd_read(sc->ps2kbd_sc, &val) != -1)
- atkbdc_kbd_queue_data(sc, val);
+ while (sc->kbd.bcnt < FIFOSZ) {
+ if (ps2kbd_read(sc->ps2kbd_sc, &val) != -1)
+ atkbdc_kbd_queue_data(sc, val);
+ else
+ break;
+ }
}
+
+ if (((sc->ram[0] & KBD_DISABLE_AUX_PORT) ||
+ ps2mouse_fifocnt(sc->ps2mouse_sc) == 0) && sc->kbd.bcnt > 0)
+ atkbdc_assert_kbd_intr(sc);
}
static void
atkbdc_aux_poll(struct atkbdc_softc *sc)
{
- if ((sc->outport & KBDO_AUX_OUTFULL) == 0)
- atkbdc_aux_read(sc);
+ if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0) {
+ sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
+ sc->outport |= KBDO_AUX_OUTFULL;
+ atkbdc_assert_aux_intr(sc);
+ }
}
static void
@@ -274,8 +252,7 @@ atkbdc_kbd_poll(struct atkbdc_softc *sc)
{
assert(pthread_mutex_isowned_np(&sc->mtx));
- if ((sc->outport & KBDO_KBD_OUTFULL) == 0)
- atkbdc_kbd_read(sc);
+ atkbdc_kbd_read(sc);
}
static void
@@ -290,22 +267,35 @@ atkbdc_dequeue_data(struct atkbdc_softc *sc, uint8_t *buf)
{
assert(pthread_mutex_isowned_np(&sc->mtx));
- if (sc->outport & KBDO_AUX_OUTFULL) {
- *buf = sc->aux.buffer;
- sc->status &= ~(KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
- sc->outport &= ~KBDO_AUX_OUTFULL;
- atkbdc_deassert_aux_intr(sc);
+ if (ps2mouse_read(sc->ps2mouse_sc, buf) == 0) {
+ if (ps2mouse_fifocnt(sc->ps2mouse_sc) == 0) {
+ if (sc->kbd.bcnt == 0)
+ sc->status &= ~(KBDS_AUX_BUFFER_FULL |
+ KBDS_KBD_BUFFER_FULL);
+ else
+ sc->status &= ~(KBDS_AUX_BUFFER_FULL);
+ sc->outport &= ~KBDO_AUX_OUTFULL;
+ }
atkbdc_poll(sc);
return;
}
- *buf = sc->kbd.buffer;
- sc->status &= ~KBDS_KBD_BUFFER_FULL;
- sc->outport &= ~KBDO_KBD_OUTFULL;
- atkbdc_deassert_kbd_intr(sc);
+ if (sc->kbd.bcnt > 0) {
+ *buf = sc->kbd.buffer[sc->kbd.brd];
+ sc->kbd.brd = (sc->kbd.brd + 1) % FIFOSZ;
+ sc->kbd.bcnt--;
+ if (sc->kbd.bcnt == 0) {
+ sc->status &= ~KBDS_KBD_BUFFER_FULL;
+ sc->outport &= ~KBDO_KBD_OUTFULL;
+ }
- atkbdc_poll(sc);
+ atkbdc_poll(sc);
+ }
+
+ if (ps2mouse_fifocnt(sc->ps2mouse_sc) == 0 && sc->kbd.bcnt == 0) {
+ sc->status &= ~(KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
+ }
}
static int
@@ -318,19 +308,22 @@ atkbdc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
if (bytes != 1)
return (-1);
-
sc = arg;
retval = 0;
pthread_mutex_lock(&sc->mtx);
if (in) {
sc->curcmd = 0;
- sc->status &= ~KBDS_CTRL_FLAG;
-
- /* read device buffer; includes kbd cmd responses */
- atkbdc_dequeue_data(sc, &buf);
- *eax = buf;
+ if (sc->ctrlbyte != 0) {
+ *eax = sc->ctrlbyte & 0xff;
+ sc->ctrlbyte = 0;
+ } else {
+ /* read device buffer; includes kbd cmd responses */
+ atkbdc_dequeue_data(sc, &buf);
+ *eax = buf;
+ }
+ sc->status &= ~KBDS_CTRL_FLAG;
pthread_mutex_unlock(&sc->mtx);
return (retval);
}
@@ -345,29 +338,22 @@ atkbdc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
if (sc->ram[0] & KBD_SYS_FLAG_BIT)
sc->status |= KBDS_SYS_FLAG;
else
- sc->status &= KBDS_SYS_FLAG;
- if (sc->outport & KBDO_AUX_OUTFULL)
- atkbdc_assert_aux_intr(sc);
- else if (sc->outport & KBDO_KBD_OUTFULL)
- atkbdc_assert_kbd_intr(sc);
+ sc->status &= ~KBDS_SYS_FLAG;
break;
case KBDC_WRITE_OUTPORT:
sc->outport = *eax;
- if (sc->outport & KBDO_AUX_OUTFULL)
- sc->status |= (KBDS_AUX_BUFFER_FULL |
- KBDS_KBD_BUFFER_FULL);
- if (sc->outport & KBDO_KBD_OUTFULL)
- sc->status |= KBDS_KBD_BUFFER_FULL;
break;
case KBDC_WRITE_TO_AUX:
- ps2mouse_write(sc->ps2mouse_sc, *eax);
+ ps2mouse_write(sc->ps2mouse_sc, *eax, 0);
atkbdc_poll(sc);
break;
case KBDC_WRITE_KBD_OUTBUF:
atkbdc_kbd_queue_data(sc, *eax);
break;
case KBDC_WRITE_AUX_OUTBUF:
- atkbdc_aux_queue_data(sc, *eax);
+ ps2mouse_write(sc->ps2mouse_sc, *eax, 1);
+ sc->status |= (KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
+ atkbdc_aux_poll(sc);
break;
default:
/* write to particular RAM byte */
@@ -398,16 +384,12 @@ atkbdc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
return (retval);
}
-
static int
atkbdc_sts_ctl_handler(struct vmctx *ctx, int vcpu, int in, int port,
int bytes, uint32_t *eax, void *arg)
{
struct atkbdc_softc *sc;
-#ifdef __FreeBSD__
- int error;
-#endif
- int retval;
+ int error, retval;
if (bytes != 1)
return (-1);
@@ -424,25 +406,27 @@ atkbdc_sts_ctl_handler(struct vmctx *ctx, int vcpu, int in, int port,
return (retval);
}
+
sc->curcmd = 0;
sc->status |= KBDS_CTRL_FLAG;
+ sc->ctrlbyte = 0;
switch (*eax) {
case KBDC_GET_COMMAND_BYTE:
- atkbdc_kbd_queue_data(sc, sc->ram[0]);
+ sc->ctrlbyte = CTRL_CMD_FLAG | sc->ram[0];
break;
case KBDC_TEST_CTRL:
- atkbdc_kbd_queue_data(sc, 0x55);
+ sc->ctrlbyte = CTRL_CMD_FLAG | 0x55;
break;
case KBDC_TEST_AUX_PORT:
case KBDC_TEST_KBD_PORT:
- atkbdc_kbd_queue_data(sc, 0);
+ sc->ctrlbyte = CTRL_CMD_FLAG | 0;
break;
case KBDC_READ_INPORT:
- atkbdc_kbd_queue_data(sc, 0);
+ sc->ctrlbyte = CTRL_CMD_FLAG | 0;
break;
case KBDC_READ_OUTPORT:
- atkbdc_kbd_queue_data(sc, sc->outport);
+ sc->ctrlbyte = CTRL_CMD_FLAG | sc->outport;
break;
case KBDC_SET_COMMAND_BYTE:
case KBDC_WRITE_OUTPORT:
@@ -455,6 +439,8 @@ atkbdc_sts_ctl_handler(struct vmctx *ctx, int vcpu, int in, int port,
break;
case KBDC_ENABLE_KBD_PORT:
sc->ram[0] &= ~KBD_DISABLE_KBD_PORT;
+ if (sc->kbd.bcnt > 0)
+ sc->status |= KBDS_KBD_BUFFER_FULL;
atkbdc_poll(sc);
break;
case KBDC_WRITE_TO_AUX:
@@ -462,17 +448,19 @@ atkbdc_sts_ctl_handler(struct vmctx *ctx, int vcpu, int in, int port,
break;
case KBDC_DISABLE_AUX_PORT:
sc->ram[0] |= KBD_DISABLE_AUX_PORT;
+ ps2mouse_toggle(sc->ps2mouse_sc, 0);
+ sc->status &= ~(KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
+ sc->outport &= ~KBDS_AUX_BUFFER_FULL;
break;
case KBDC_ENABLE_AUX_PORT:
sc->ram[0] &= ~KBD_DISABLE_AUX_PORT;
+ ps2mouse_toggle(sc->ps2mouse_sc, 1);
+ if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0)
+ sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
break;
case KBDC_RESET: /* Pulse "reset" line */
-#ifdef __FreeBSD__
error = vm_suspend(ctx, VM_SUSPEND_RESET);
assert(error == 0 || errno == EALREADY);
-#else
- exit(0);
-#endif
break;
default:
if (*eax >= 0x21 && *eax <= 0x3f) {
@@ -480,21 +468,38 @@ atkbdc_sts_ctl_handler(struct vmctx *ctx, int vcpu, int in, int port,
int byten;
byten = (*eax - 0x20) & 0x1f;
- atkbdc_kbd_queue_data(sc, sc->ram[byten]);
+ sc->ctrlbyte = CTRL_CMD_FLAG | sc->ram[byten];
}
break;
}
pthread_mutex_unlock(&sc->mtx);
+ if (sc->ctrlbyte != 0) {
+ sc->status |= KBDS_KBD_BUFFER_FULL;
+ sc->status &= ~KBDS_AUX_BUFFER_FULL;
+ atkbdc_assert_kbd_intr(sc);
+ } else if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0 &&
+ (sc->ram[0] & KBD_DISABLE_AUX_PORT) == 0) {
+ sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
+ atkbdc_assert_aux_intr(sc);
+ } else if (sc->kbd.bcnt > 0 && (sc->ram[0] & KBD_DISABLE_KBD_PORT) == 0) {
+ sc->status |= KBDS_KBD_BUFFER_FULL;
+ atkbdc_assert_kbd_intr(sc);
+ }
+
return (retval);
}
void
-atkbdc_event(struct atkbdc_softc *sc)
+atkbdc_event(struct atkbdc_softc *sc, int iskbd)
{
pthread_mutex_lock(&sc->mtx);
- atkbdc_poll(sc);
+
+ if (iskbd)
+ atkbdc_kbd_poll(sc);
+ else
+ atkbdc_aux_poll(sc);
pthread_mutex_unlock(&sc->mtx);
}
@@ -542,7 +547,6 @@ atkbdc_init(struct vmctx *ctx)
sc->ps2mouse_sc = ps2mouse_init(sc);
}
-#ifdef __FreeBSD__
static void
atkbdc_dsdt(void)
{
@@ -576,4 +580,4 @@ atkbdc_dsdt(void)
dsdt_line("}");
}
LPC_DSDT(atkbdc_dsdt);
-#endif
+
diff --git a/usr/src/cmd/bhyve/atkbdc.h b/usr/src/cmd/bhyve/atkbdc.h
index 48b3a8b00c..85c8a7141e 100644
--- a/usr/src/cmd/bhyve/atkbdc.h
+++ b/usr/src/cmd/bhyve/atkbdc.h
@@ -33,6 +33,6 @@ struct atkbdc_softc;
struct vmctx;
void atkbdc_init(struct vmctx *ctx);
-void atkbdc_event(struct atkbdc_softc *sc);
+void atkbdc_event(struct atkbdc_softc *sc, int iskbd);
#endif /* _ATKBDC_H_ */
diff --git a/usr/src/cmd/bhyve/bhyve_sol_glue.c b/usr/src/cmd/bhyve/bhyve_sol_glue.c
index 47bc7a3999..7b24ea7f5d 100644
--- a/usr/src/cmd/bhyve/bhyve_sol_glue.c
+++ b/usr/src/cmd/bhyve/bhyve_sol_glue.c
@@ -37,53 +37,3 @@ cfmakeraw(struct termios *t)
t->c_cc[VMIN] = 1;
t->c_cc[VTIME] = 0;
}
-
-ssize_t
-preadv(int d, const struct iovec *iov, int iovcnt, off_t offset)
-{
- off_t old_offset;
- ssize_t n;
-
- old_offset = lseek(d, (off_t)0, SEEK_CUR);
- if (old_offset == -1)
- return (-1);
-
- offset = lseek(d, offset, SEEK_SET);
- if (offset == -1)
- return (-1);
-
- n = readv(d, iov, iovcnt);
- if (n == -1)
- return (-1);
-
- offset = lseek(d, old_offset, SEEK_SET);
- if (offset == -1)
- return (-1);
-
- return (n);
-}
-
-ssize_t
-pwritev(int d, const struct iovec *iov, int iovcnt, off_t offset)
-{
- off_t old_offset;
- ssize_t n;
-
- old_offset = lseek(d, (off_t)0, SEEK_CUR);
- if (old_offset == -1)
- return (-1);
-
- offset = lseek(d, offset, SEEK_SET);
- if (offset == -1)
- return (-1);
-
- n = writev(d, iov, iovcnt);
- if (n == -1)
- return (-1);
-
- offset = lseek(d, old_offset, SEEK_SET);
- if (offset == -1)
- return (-1);
-
- return (n);
-}
diff --git a/usr/src/cmd/bhyve/bhyvegc.c b/usr/src/cmd/bhyve/bhyvegc.c
index 7a13c4c83f..11cc2b1fb4 100644
--- a/usr/src/cmd/bhyve/bhyvegc.c
+++ b/usr/src/cmd/bhyve/bhyvegc.c
@@ -37,10 +37,11 @@ __FBSDID("$FreeBSD$");
struct bhyvegc {
struct bhyvegc_image *gc_image;
+ int raw;
};
struct bhyvegc *
-bhyvegc_init(int width, int height)
+bhyvegc_init(int width, int height, void *fbaddr)
{
struct bhyvegc *gc;
struct bhyvegc_image *gc_image;
@@ -50,7 +51,13 @@ bhyvegc_init(int width, int height)
gc_image = calloc(1, sizeof(struct bhyvegc_image));
gc_image->width = width;
gc_image->height = height;
- gc_image->data = calloc(width * height, sizeof (uint32_t));
+ if (fbaddr) {
+ gc_image->data = fbaddr;
+ gc->raw = 1;
+ } else {
+ gc_image->data = calloc(width * height, sizeof (uint32_t));
+ gc->raw = 0;
+ }
gc->gc_image = gc_image;
@@ -58,6 +65,15 @@ bhyvegc_init(int width, int height)
}
void
+bhyvegc_set_fbaddr(struct bhyvegc *gc, void *fbaddr)
+{
+ gc->raw = 1;
+ if (gc->gc_image->data && gc->gc_image->data != fbaddr)
+ free(gc->gc_image->data);
+ gc->gc_image->data = fbaddr;
+}
+
+void
bhyvegc_resize(struct bhyvegc *gc, int width, int height)
{
struct bhyvegc_image *gc_image;
@@ -66,13 +82,20 @@ bhyvegc_resize(struct bhyvegc *gc, int width, int height)
gc_image->width = width;
gc_image->height = height;
- gc_image->data = realloc(gc_image->data,
- sizeof (uint32_t) * width * height);
- memset(gc_image->data, 0, width * height * sizeof (uint32_t));
+ if (!gc->raw) {
+ gc_image->data = reallocarray(gc_image->data, width * height,
+ sizeof (uint32_t));
+ if (gc_image->data != NULL)
+ memset(gc_image->data, 0, width * height *
+ sizeof (uint32_t));
+ }
}
struct bhyvegc_image *
bhyvegc_get_image(struct bhyvegc *gc)
{
+ if (gc == NULL)
+ return (NULL);
+
return (gc->gc_image);
}
diff --git a/usr/src/cmd/bhyve/bhyvegc.h b/usr/src/cmd/bhyve/bhyvegc.h
index 19648f98af..fa2ab68d9e 100644
--- a/usr/src/cmd/bhyve/bhyvegc.h
+++ b/usr/src/cmd/bhyve/bhyvegc.h
@@ -32,12 +32,14 @@
struct bhyvegc;
struct bhyvegc_image {
+ int vgamode;
int width;
int height;
uint32_t *data;
};
-struct bhyvegc *bhyvegc_init(int width, int height);
+struct bhyvegc *bhyvegc_init(int width, int height, void *fbaddr);
+void bhyvegc_set_fbaddr(struct bhyvegc *gc, void *fbaddr);
void bhyvegc_resize(struct bhyvegc *gc, int width, int height);
struct bhyvegc_image *bhyvegc_get_image(struct bhyvegc *gc);
diff --git a/usr/src/cmd/bhyve/bhyverun.c b/usr/src/cmd/bhyve/bhyverun.c
index b985a2286e..de8084d0f1 100644
--- a/usr/src/cmd/bhyve/bhyverun.c
+++ b/usr/src/cmd/bhyve/bhyverun.c
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/bhyverun.c 281611 2015-04-16 20:11:49Z neel $
+ * $FreeBSD$
*/
/*
* This file and its contents are supplied under the terms of the
@@ -36,14 +36,19 @@
* http://www.illumos.org/license/CDDL.
*
* Copyright 2015 Pluribus Networks Inc.
+ * Copyright 2017 Joyent, Inc.
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/usr.sbin/bhyve/bhyverun.c 281611 2015-04-16 20:11:49Z neel $");
+__FBSDID("$FreeBSD$");
#include <sys/types.h>
+#ifndef WITHOUT_CAPSICUM
+#include <sys/capsicum.h>
+#endif
#include <sys/mman.h>
#include <sys/time.h>
+#include <sys/cpuset.h>
#include <machine/segments.h>
@@ -51,6 +56,7 @@ __FBSDID("$FreeBSD: head/usr.sbin/bhyve/bhyverun.c 281611 2015-04-16 20:11:49Z n
#include <stdlib.h>
#include <string.h>
#include <err.h>
+#include <errno.h>
#include <libgen.h>
#include <unistd.h>
#include <assert.h>
@@ -58,8 +64,16 @@ __FBSDID("$FreeBSD: head/usr.sbin/bhyve/bhyverun.c 281611 2015-04-16 20:11:49Z n
#include <pthread.h>
#include <pthread_np.h>
#include <sysexits.h>
+#include <stdbool.h>
+#ifndef WITHOUT_CAPSICUM
+#include <nl_types.h>
+#include <termios.h>
+#endif
#include <machine/vmm.h>
+#ifndef WITHOUT_CAPSICUM
+#include <machine/vmm_dev.h>
+#endif
#include <vmmapi.h>
#include "bhyverun.h"
@@ -68,6 +82,7 @@ __FBSDID("$FreeBSD: head/usr.sbin/bhyve/bhyverun.c 281611 2015-04-16 20:11:49Z n
#include "console.h"
#include "inout.h"
#include "dbgport.h"
+#include "fwctl.h"
#include "ioapic.h"
#include "mem.h"
#ifdef __FreeBSD__
@@ -90,6 +105,7 @@ __FBSDID("$FreeBSD: head/usr.sbin/bhyve/bhyverun.c 281611 2015-04-16 20:11:49Z n
#define GB (1024UL * MB)
typedef int (*vmexit_handler_t)(struct vmctx *, struct vm_exit *, int *vcpu);
+extern int vmexit_task_switch(struct vmctx *, struct vm_exit *, int *vcpu);
char *vmname;
@@ -103,9 +119,7 @@ static int x2apic_mode = 0; /* default is xAPIC */
static int strictio;
static int strictmsr = 1;
-#ifdef __FreeBSD__
static int acpi;
-#endif
static char *progname;
static const int BSP = 0;
@@ -125,14 +139,13 @@ static struct vm_exit vmexit[VM_MAXCPU];
struct bhyvestats {
uint64_t vmexit_bogus;
- uint64_t vmexit_bogus_switch;
+ uint64_t vmexit_reqidle;
uint64_t vmexit_hlt;
uint64_t vmexit_pause;
uint64_t vmexit_mtrap;
uint64_t vmexit_inst_emul;
uint64_t cpu_switch_rotate;
uint64_t cpu_switch_direct;
- int io_reset;
} stats;
struct mt_vmm_info {
@@ -141,55 +154,120 @@ struct mt_vmm_info {
int mt_vcpu;
} mt_vmm_info[VM_MAXCPU];
+#ifdef __FreeBSD__
+static cpuset_t *vcpumap[VM_MAXCPU] = { NULL };
+#endif
+
static void
usage(int code)
{
-#ifdef __FreeBSD__
fprintf(stderr,
- "Usage: %s [-aehwAHIPW] [-g <gdb port>] [-s <pci>] [-c vcpus]\n"
- " %*s [-p vcpu:hostcpu] [-m mem] [-l <lpc>] <vm>\n"
+ "Usage: %s [-abehuwxACHIPSWY] [-c vcpus] [-g <gdb port>] [-l <lpc>]\n"
+#ifdef __FreeBSD__
+ " %*s [-m memsize[K|k|M|m|G|g|T|t] [-p vcpu:hostcpu] [-s <pci>] [-U uuid] <vm>\n"
+#else
+ " %*s [-s <pci>] [-U uuid] <vm>\n"
+#endif
" -a: local apic is in xAPIC mode (deprecated)\n"
- " -A: create an ACPI table\n"
- " -g: gdb port\n"
+ " -A: create ACPI tables\n"
" -c: # cpus (default 1)\n"
" -C: include guest memory in core file\n"
- " -p: pin 'vcpu' to 'hostcpu'\n"
- " -H: vmexit from the guest on hlt\n"
- " -P: vmexit from the guest on pause\n"
- " -W: force virtio to use single-vector MSI\n"
" -e: exit on unhandled I/O access\n"
+ " -g: gdb port\n"
" -h: help\n"
- " -s: <slot,driver,configinfo> PCI slot config\n"
- " -l: LPC device configuration\n"
- " -m: memory size in MB\n"
- " -w: ignore unimplemented MSRs\n"
- " -x: local apic is in x2APIC mode\n"
- " -Y: disable MPtable generation\n"
- " -U: uuid\n",
- progname, (int)strlen(progname), "");
-#else
- fprintf(stderr,
- "Usage: %s [-ehwHPW] [-s <pci>] [-c vcpus]\n"
- " %*s [-p vcpu:hostcpu] [-m mem] [-l <lpc>] <vm>\n"
- " -c: # cpus (default 1)\n"
" -H: vmexit from the guest on hlt\n"
+ " -l: LPC device configuration\n"
+ " -m: memory size\n"
+#ifdef __FreeBSD__
+ " -p: pin 'vcpu' to 'hostcpu'\n"
+#endif
" -P: vmexit from the guest on pause\n"
- " -W: force virtio to use single-vector MSI\n"
- " -e: exit on unhandled I/O access\n"
- " -h: help\n"
" -s: <slot,driver,configinfo> PCI slot config\n"
- " -l: LPC device configuration\n"
- " -m: memory size in MB\n"
+ " -S: guest memory cannot be swapped\n"
+ " -u: RTC keeps UTC time\n"
+ " -U: uuid\n"
" -w: ignore unimplemented MSRs\n"
- " -Y: disable MPtable generation\n"
- " -U: uuid\n",
+ " -W: force virtio to use single-vector MSI\n"
+ " -x: local apic is in x2APIC mode\n"
+ " -Y: disable MPtable generation\n",
progname, (int)strlen(progname), "");
-#endif
exit(code);
}
+#ifndef WITHOUT_CAPSICUM
+/*
+ * 11-stable capsicum helpers
+ */
+static void
+bhyve_caph_cache_catpages(void)
+{
+
+ (void)catopen("libc", NL_CAT_LOCALE);
+}
+
+static int
+bhyve_caph_limit_stdoe(void)
+{
+ cap_rights_t rights;
+ unsigned long cmds[] = { TIOCGETA, TIOCGWINSZ };
+ int i, fds[] = { STDOUT_FILENO, STDERR_FILENO };
+
+ cap_rights_init(&rights, CAP_FCNTL, CAP_FSTAT, CAP_IOCTL);
+ cap_rights_set(&rights, CAP_WRITE);
+
+ for (i = 0; i < nitems(fds); i++) {
+ if (cap_rights_limit(fds[i], &rights) < 0 && errno != ENOSYS)
+ return (-1);
+
+ if (cap_ioctls_limit(fds[i], cmds, nitems(cmds)) < 0 && errno != ENOSYS)
+ return (-1);
+
+ if (cap_fcntls_limit(fds[i], CAP_FCNTL_GETFL) < 0 && errno != ENOSYS)
+ return (-1);
+ }
+
+ return (0);
+}
+
+#endif
+
+#ifdef __FreeBSD__
+static int
+pincpu_parse(const char *opt)
+{
+ int vcpu, pcpu;
+
+ if (sscanf(opt, "%d:%d", &vcpu, &pcpu) != 2) {
+ fprintf(stderr, "invalid format: %s\n", opt);
+ return (-1);
+ }
+
+ if (vcpu < 0 || vcpu >= VM_MAXCPU) {
+ fprintf(stderr, "vcpu '%d' outside valid range from 0 to %d\n",
+ vcpu, VM_MAXCPU - 1);
+ return (-1);
+ }
+
+ if (pcpu < 0 || pcpu >= CPU_SETSIZE) {
+ fprintf(stderr, "hostcpu '%d' outside valid range from "
+ "0 to %d\n", pcpu, CPU_SETSIZE - 1);
+ return (-1);
+ }
+
+ if (vcpumap[vcpu] == NULL) {
+ if ((vcpumap[vcpu] = malloc(sizeof(cpuset_t))) == NULL) {
+ perror("malloc");
+ return (-1);
+ }
+ CPU_ZERO(vcpumap[vcpu]);
+ }
+ CPU_SET(pcpu, vcpumap[vcpu]);
+ return (0);
+}
+#endif
+
void
vm_inject_fault(void *arg, int vcpu, int vector, int errcode_valid,
int errcode)
@@ -267,7 +345,8 @@ fbsdrun_addcpu(struct vmctx *ctx, int fromcpu, int newcpu, uint64_t rip)
* with vm_suspend().
*/
error = vm_activate_cpu(ctx, newcpu);
- assert(error == 0);
+ if (error != 0)
+ err(EX_OSERR, "could not activate CPU %d", newcpu);
CPU_SET_ATOMIC(newcpu, &cpumask);
@@ -287,6 +366,19 @@ fbsdrun_addcpu(struct vmctx *ctx, int fromcpu, int newcpu, uint64_t rip)
}
static int
+fbsdrun_deletecpu(struct vmctx *ctx, int vcpu)
+{
+
+ if (!CPU_ISSET(vcpu, &cpumask)) {
+ fprintf(stderr, "Attempting to delete unknown cpu %d\n", vcpu);
+ exit(1);
+ }
+
+ CPU_CLR_ATOMIC(vcpu, &cpumask);
+ return (CPU_EMPTY(&cpumask));
+}
+
+static int
vmexit_handle_notify(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu,
uint32_t eax)
{
@@ -302,14 +394,13 @@ static int
vmexit_inout(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu)
{
int error;
- int bytes, port, in, out, string;
+ int bytes, port, in, out;
int vcpu;
vcpu = *pvcpu;
port = vme->u.inout.port;
bytes = vme->u.inout.bytes;
- string = vme->u.inout.string;
in = vme->u.inout.in;
out = !in;
@@ -380,15 +471,23 @@ vmexit_wrmsr(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu)
static int
vmexit_spinup_ap(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu)
{
- int newcpu;
- int retval = VMEXIT_CONTINUE;
- newcpu = spinup_ap(ctx, *pvcpu,
- vme->u.spinup_ap.vcpu, vme->u.spinup_ap.rip);
+ (void)spinup_ap(ctx, *pvcpu,
+ vme->u.spinup_ap.vcpu, vme->u.spinup_ap.rip);
- return (retval);
+ return (VMEXIT_CONTINUE);
}
+#define DEBUG_EPT_MISCONFIG
+#ifdef DEBUG_EPT_MISCONFIG
+#define EXIT_REASON_EPT_MISCONFIG 49
+#define VMCS_GUEST_PHYSICAL_ADDRESS 0x00002400
+#define VMCS_IDENT(x) ((x) | 0x80000000)
+
+static uint64_t ept_misconfig_gpa, ept_misconfig_pte[4];
+static int ept_misconfig_ptenum;
+#endif
+
static int
vmexit_vmx(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
{
@@ -403,7 +502,35 @@ vmexit_vmx(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
vmexit->u.vmx.exit_qualification);
fprintf(stderr, "\tinst_type\t\t%d\n", vmexit->u.vmx.inst_type);
fprintf(stderr, "\tinst_error\t\t%d\n", vmexit->u.vmx.inst_error);
+#ifdef DEBUG_EPT_MISCONFIG
+ if (vmexit->u.vmx.exit_reason == EXIT_REASON_EPT_MISCONFIG) {
+ vm_get_register(ctx, *pvcpu,
+ VMCS_IDENT(VMCS_GUEST_PHYSICAL_ADDRESS),
+ &ept_misconfig_gpa);
+ vm_get_gpa_pmap(ctx, ept_misconfig_gpa, ept_misconfig_pte,
+ &ept_misconfig_ptenum);
+ fprintf(stderr, "\tEPT misconfiguration:\n");
+ fprintf(stderr, "\t\tGPA: %#lx\n", ept_misconfig_gpa);
+ fprintf(stderr, "\t\tPTE(%d): %#lx %#lx %#lx %#lx\n",
+ ept_misconfig_ptenum, ept_misconfig_pte[0],
+ ept_misconfig_pte[1], ept_misconfig_pte[2],
+ ept_misconfig_pte[3]);
+ }
+#endif /* DEBUG_EPT_MISCONFIG */
+ return (VMEXIT_ABORT);
+}
+static int
+vmexit_svm(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
+{
+
+ fprintf(stderr, "vm exit[%d]\n", *pvcpu);
+ fprintf(stderr, "\treason\t\tSVM\n");
+ fprintf(stderr, "\trip\t\t0x%016lx\n", vmexit->rip);
+ fprintf(stderr, "\tinst_length\t%d\n", vmexit->inst_length);
+ fprintf(stderr, "\texitcode\t%#lx\n", vmexit->u.svm.exitcode);
+ fprintf(stderr, "\texitinfo1\t%#lx\n", vmexit->u.svm.exitinfo1);
+ fprintf(stderr, "\texitinfo2\t%#lx\n", vmexit->u.svm.exitinfo2);
return (VMEXIT_ABORT);
}
@@ -411,12 +538,25 @@ static int
vmexit_bogus(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
{
+ assert(vmexit->inst_length == 0);
+
stats.vmexit_bogus++;
return (VMEXIT_CONTINUE);
}
static int
+vmexit_reqidle(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
+{
+
+ assert(vmexit->inst_length == 0);
+
+ stats.vmexit_reqidle++;
+
+ return (VMEXIT_CONTINUE);
+}
+
+static int
vmexit_hlt(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
{
@@ -443,6 +583,8 @@ static int
vmexit_mtrap(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
{
+ assert(vmexit->inst_length == 0);
+
stats.vmexit_mtrap++;
return (VMEXIT_CONTINUE);
@@ -478,35 +620,79 @@ vmexit_inst_emul(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
return (VMEXIT_CONTINUE);
}
+static pthread_mutex_t resetcpu_mtx = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t resetcpu_cond = PTHREAD_COND_INITIALIZER;
+
+static int
+vmexit_suspend(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
+{
+ enum vm_suspend_how how;
+
+ how = vmexit->u.suspended.how;
+
+ fbsdrun_deletecpu(ctx, *pvcpu);
+
+ if (*pvcpu != BSP) {
+ pthread_mutex_lock(&resetcpu_mtx);
+ pthread_cond_signal(&resetcpu_cond);
+ pthread_mutex_unlock(&resetcpu_mtx);
+ pthread_exit(NULL);
+ }
+
+ pthread_mutex_lock(&resetcpu_mtx);
+ while (!CPU_EMPTY(&cpumask)) {
+ pthread_cond_wait(&resetcpu_cond, &resetcpu_mtx);
+ }
+ pthread_mutex_unlock(&resetcpu_mtx);
+
+ switch (how) {
+ case VM_SUSPEND_RESET:
+ exit(0);
+ case VM_SUSPEND_POWEROFF:
+ exit(1);
+ case VM_SUSPEND_HALT:
+ exit(2);
+ case VM_SUSPEND_TRIPLEFAULT:
+ exit(3);
+ default:
+ fprintf(stderr, "vmexit_suspend: invalid reason %d\n", how);
+ exit(100);
+ }
+ return (0); /* NOTREACHED */
+}
+
static vmexit_handler_t handler[VM_EXITCODE_MAX] = {
[VM_EXITCODE_INOUT] = vmexit_inout,
+ [VM_EXITCODE_INOUT_STR] = vmexit_inout,
[VM_EXITCODE_VMX] = vmexit_vmx,
+ [VM_EXITCODE_SVM] = vmexit_svm,
[VM_EXITCODE_BOGUS] = vmexit_bogus,
+ [VM_EXITCODE_REQIDLE] = vmexit_reqidle,
[VM_EXITCODE_RDMSR] = vmexit_rdmsr,
[VM_EXITCODE_WRMSR] = vmexit_wrmsr,
[VM_EXITCODE_MTRAP] = vmexit_mtrap,
[VM_EXITCODE_INST_EMUL] = vmexit_inst_emul,
[VM_EXITCODE_SPINUP_AP] = vmexit_spinup_ap,
+ [VM_EXITCODE_SUSPENDED] = vmexit_suspend,
+ [VM_EXITCODE_TASK_SWITCH] = vmexit_task_switch,
};
static void
vm_loop(struct vmctx *ctx, int vcpu, uint64_t startrip)
{
-#ifdef __FreeBSD__
- cpuset_t mask;
-#endif
- int error, rc, prevcpu;
+ int error, rc;
enum vm_exitcode exitcode;
+ cpuset_t active_cpus;
#ifdef __FreeBSD__
- if (pincpu >= 0) {
- CPU_ZERO(&mask);
- CPU_SET(pincpu + vcpu, &mask);
+ if (vcpumap[vcpu] != NULL) {
error = pthread_setaffinity_np(pthread_self(),
- sizeof(mask), &mask);
+ sizeof(cpuset_t), vcpumap[vcpu]);
assert(error == 0);
}
#endif
+ error = vm_active_cpus(ctx, &active_cpus);
+ assert(CPU_ISSET(vcpu, &active_cpus));
error = vm_set_register(ctx, vcpu, VM_REG_GUEST_RIP, startrip);
assert(error == 0);
@@ -516,8 +702,6 @@ vm_loop(struct vmctx *ctx, int vcpu, uint64_t startrip)
if (error != 0)
break;
- prevcpu = vcpu;
-
exitcode = vmexit[vcpu].exitcode;
if (exitcode >= VM_EXITCODE_MAX || handler[exitcode] == NULL) {
fprintf(stderr, "vm_loop: unexpected exitcode 0x%x\n",
@@ -525,7 +709,7 @@ vm_loop(struct vmctx *ctx, int vcpu, uint64_t startrip)
exit(1);
}
- rc = (*handler[exitcode])(ctx, &vmexit[vcpu], &vcpu);
+ rc = (*handler[exitcode])(ctx, &vmexit[vcpu], &vcpu);
switch (rc) {
case VMEXIT_CONTINUE:
@@ -602,59 +786,133 @@ fbsdrun_set_capabilities(struct vmctx *ctx, int cpu)
#endif
}
+static struct vmctx *
+do_open(const char *vmname)
+{
+ struct vmctx *ctx;
+ int error;
+ bool reinit, romboot;
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_t rights;
+ const cap_ioctl_t *cmds;
+ size_t ncmds;
+#endif
+
+ reinit = romboot = false;
+
+ if (lpc_bootrom())
+ romboot = true;
+
+ error = vm_create(vmname);
+ if (error) {
+ if (errno == EEXIST) {
+ if (romboot) {
+ reinit = true;
+ } else {
+ /*
+ * The virtual machine has been setup by the
+ * userspace bootloader.
+ */
+ }
+ } else {
+ perror("vm_create");
+ exit(1);
+ }
+ } else {
+ if (!romboot) {
+ /*
+ * If the virtual machine was just created then a
+ * bootrom must be configured to boot it.
+ */
+ fprintf(stderr, "virtual machine cannot be booted\n");
+ exit(1);
+ }
+ }
+
+ ctx = vm_open(vmname);
+ if (ctx == NULL) {
+ perror("vm_open");
+ exit(1);
+ }
+
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_init(&rights, CAP_IOCTL, CAP_MMAP_RW);
+ if (cap_rights_limit(vm_get_device_fd(ctx), &rights) == -1 &&
+ errno != ENOSYS)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+ vm_get_ioctls(&ncmds);
+ cmds = vm_get_ioctls(NULL);
+ if (cmds == NULL)
+ errx(EX_OSERR, "out of memory");
+ if (cap_ioctls_limit(vm_get_device_fd(ctx), cmds, ncmds) == -1 &&
+ errno != ENOSYS)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+ free((cap_ioctl_t *)cmds);
+#endif
+
+ if (reinit) {
+ error = vm_reinit(ctx);
+ if (error) {
+ perror("vm_reinit");
+ exit(1);
+ }
+ }
+ return (ctx);
+}
+
int
main(int argc, char *argv[])
{
- int c, error, gdb_port, rfb_port, err, bvmcons;
- int max_vcpus;
+ int c, error, gdb_port, err, bvmcons;
+ int max_vcpus, mptgen, memflags;
+ int rtc_localtime;
struct vmctx *ctx;
uint64_t rip;
size_t memsize;
+ char *optstr;
bvmcons = 0;
progname = basename(argv[0]);
gdb_port = 0;
- rfb_port = -1;
guest_ncpus = 1;
memsize = 256 * MB;
-
+ mptgen = 1;
+ rtc_localtime = 1;
+ memflags = 0;
#ifdef __FreeBSD__
- while ((c = getopt(argc, argv, "abehwxACHIPWYp:r:g:c:s:m:l:U:")) != -1) {
+ optstr = "abehuwxACHIPSWYp:g:c:s:m:l:U:";
#else
- while ((c = getopt(argc, argv, "abehwxHIPWYr:c:s:m:l:U:")) != -1) {
+ optstr = "abehuwxACHIPSWYg:c:s:m:l:U:";
#endif
+ while ((c = getopt(argc, argv, optstr)) != -1) {
switch (c) {
case 'a':
x2apic_mode = 0;
break;
-#ifdef __FreeBSD__
case 'A':
acpi = 1;
break;
-#endif
case 'b':
bvmcons = 1;
break;
#ifdef __FreeBSD__
case 'p':
- pincpu = atoi(optarg);
+ if (pincpu_parse(optarg) != 0) {
+ errx(EX_USAGE, "invalid vcpu pinning "
+ "configuration '%s'", optarg);
+ }
break;
#endif
- case 'r':
- if (optarg[0] == ':')
- rfb_port = atoi(optarg + 1) + RFB_PORT;
- else
- rfb_port = atoi(optarg);
- break;
case 'c':
guest_ncpus = atoi(optarg);
break;
-#ifdef __FreeBSD__
+ case 'C':
+ memflags |= VM_MEM_F_INCORE;
+ break;
case 'g':
gdb_port = atoi(optarg);
break;
-#endif
case 'l':
if (lpc_device_parse(optarg) != 0) {
errx(EX_USAGE, "invalid lpc device "
@@ -666,6 +924,9 @@ main(int argc, char *argv[])
exit(1);
else
break;
+ case 'S':
+ memflags |= VM_MEM_F_WIRED;
+ break;
case 'm':
error = vm_parse_memsize(optarg, &memsize);
if (error)
@@ -689,15 +950,24 @@ main(int argc, char *argv[])
case 'e':
strictio = 1;
break;
+ case 'u':
+ rtc_localtime = 0;
+ break;
case 'U':
guest_uuid_str = optarg;
break;
+ case 'w':
+ strictmsr = 0;
+ break;
case 'W':
virtio_msix = 0;
break;
case 'x':
x2apic_mode = 1;
break;
+ case 'Y':
+ mptgen = 0;
+ break;
case 'h':
usage(0);
default:
@@ -711,10 +981,10 @@ main(int argc, char *argv[])
usage(1);
vmname = argv[0];
+ ctx = do_open(vmname);
- ctx = vm_open(vmname);
- if (ctx == NULL) {
- perror("vm_open");
+ if (guest_ncpus < 1) {
+ fprintf(stderr, "Invalid guest vCPUs (%d)\n", guest_ncpus);
exit(1);
}
@@ -727,9 +997,10 @@ main(int argc, char *argv[])
fbsdrun_set_capabilities(ctx, BSP);
+ vm_set_memflags(ctx, memflags);
err = vm_setup_memory(ctx, memsize, VM_MMAP_ALL);
if (err) {
- fprintf(stderr, "Unable to setup memory (%d)\n", err);
+ fprintf(stderr, "Unable to setup memory (%d)\n", errno);
exit(1);
}
@@ -745,7 +1016,8 @@ main(int argc, char *argv[])
pci_irq_init(ctx);
ioapic_init(ctx);
- rtc_init(ctx);
+ rtc_init(ctx, rtc_localtime);
+ sci_init(ctx);
/*
* Exit if a device emulation finds an error in it's initilization
@@ -753,18 +1025,23 @@ main(int argc, char *argv[])
if (init_pci(ctx) != 0)
exit(1);
-#ifdef __FreeBSD__
if (gdb_port != 0)
init_dbgport(gdb_port);
-#endif
if (bvmcons)
init_bvmcons();
- console_init();
- vga_init();
- if (rfb_port != -1)
- rfb_init(rfb_port);
+ vga_init(1);
+
+ if (lpc_bootrom()) {
+ if (vm_set_capability(ctx, BSP, VM_CAP_UNRESTRICTED_GUEST, 1)) {
+ fprintf(stderr, "ROM boot failed: unrestricted guest "
+ "capability not available\n");
+ exit(1);
+ }
+ error = vcpu_reset(ctx, BSP);
+ assert(error == 0);
+ }
error = vm_get_register(ctx, BSP, VM_REG_GUEST_RIP, &rip);
assert(error == 0);
@@ -772,22 +1049,39 @@ main(int argc, char *argv[])
/*
* build the guest tables, MP etc.
*/
- mptable_build(ctx, guest_ncpus);
+ if (mptgen) {
+ error = mptable_build(ctx, guest_ncpus);
+ if (error)
+ exit(1);
+ }
error = smbios_build(ctx);
assert(error == 0);
-#ifdef __FreeBSD__
if (acpi) {
error = acpi_build(ctx, guest_ncpus);
assert(error == 0);
}
+ if (lpc_bootrom())
+ fwctl_init();
+
+#ifndef WITHOUT_CAPSICUM
+
+
+ if (bhyve_caph_limit_stdoe() == -1)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+
+ if (cap_enter() == -1 && errno != ENOSYS)
+ errx(EX_OSERR, "cap_enter() failed");
+#endif
+
/*
* Change the proc title to include the VM name.
*/
setproctitle("%s", vmname);
-#else
+
+#ifndef __FreeBSD__
/*
* If applicable, wait for bhyveconsole
*/
diff --git a/usr/src/cmd/bhyve/bhyverun.h b/usr/src/cmd/bhyve/bhyverun.h
index be89314c09..d06b47a09a 100644
--- a/usr/src/cmd/bhyve/bhyverun.h
+++ b/usr/src/cmd/bhyve/bhyverun.h
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/bhyverun.h 277310 2015-01-18 03:08:30Z neel $
+ * $FreeBSD$
*/
/*
* This file and its contents are supplied under the terms of the
@@ -41,12 +41,6 @@
#ifndef _FBSDRUN_H_
#define _FBSDRUN_H_
-#ifndef CTASSERT /* Allow lint to override */
-#define CTASSERT(x) _CTASSERT(x, __LINE__)
-#define _CTASSERT(x, y) __CTASSERT(x, y)
-#define __CTASSERT(x, y) typedef char __assert ## y[(x) ? 1 : -1]
-#endif
-
#define VMEXIT_CONTINUE (0)
#define VMEXIT_ABORT (-1)
diff --git a/usr/src/cmd/bhyve/block_if.c b/usr/src/cmd/bhyve/block_if.c
index 2da946d420..c6f211632f 100644
--- a/usr/src/cmd/bhyve/block_if.c
+++ b/usr/src/cmd/bhyve/block_if.c
@@ -23,20 +23,26 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/block_if.c 274330 2014-11-09 21:08:52Z tychon $
+ * $FreeBSD$
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/usr.sbin/bhyve/block_if.c 274330 2014-11-09 21:08:52Z tychon $");
+__FBSDID("$FreeBSD$");
#include <sys/param.h>
+#ifndef WITHOUT_CAPSICUM
+#include <sys/capsicum.h>
+#endif
#include <sys/queue.h>
#include <sys/errno.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/disk.h>
+#include <sys/limits.h>
+#include <sys/uio.h>
#include <assert.h>
+#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
@@ -44,6 +50,7 @@ __FBSDID("$FreeBSD: head/usr.sbin/bhyve/block_if.c 274330 2014-11-09 21:08:52Z t
#include <pthread.h>
#include <pthread_np.h>
#include <signal.h>
+#include <sysexits.h>
#include <unistd.h>
#include <machine/atomic.h>
@@ -56,16 +63,19 @@ __FBSDID("$FreeBSD: head/usr.sbin/bhyve/block_if.c 274330 2014-11-09 21:08:52Z t
#define BLOCKIF_SIG 0xb109b109
-#define BLOCKIF_MAXREQ 33
+#define BLOCKIF_NUMTHR 8
+#define BLOCKIF_MAXREQ (64 + BLOCKIF_NUMTHR)
enum blockop {
BOP_READ,
BOP_WRITE,
- BOP_FLUSH
+ BOP_FLUSH,
+ BOP_DELETE
};
enum blockstat {
BST_FREE,
+ BST_BLOCK,
BST_PEND,
BST_BUSY,
BST_DONE
@@ -77,24 +87,29 @@ struct blockif_elem {
enum blockop be_op;
enum blockstat be_status;
pthread_t be_tid;
+ off_t be_block;
};
struct blockif_ctxt {
int bc_magic;
int bc_fd;
+ int bc_ischr;
+ int bc_isgeom;
+ int bc_candelete;
int bc_rdonly;
off_t bc_size;
int bc_sectsz;
- pthread_t bc_btid;
+ int bc_psectsz;
+ int bc_psectoff;
+ int bc_closing;
+ pthread_t bc_btid[BLOCKIF_NUMTHR];
pthread_mutex_t bc_mtx;
pthread_cond_t bc_cond;
- int bc_closing;
/* Request elements and free/pending/busy queues */
TAILQ_HEAD(, blockif_elem) bc_freeq;
TAILQ_HEAD(, blockif_elem) bc_pendq;
TAILQ_HEAD(, blockif_elem) bc_busyq;
- u_int bc_req_count;
struct blockif_elem bc_reqs[BLOCKIF_MAXREQ];
};
@@ -113,83 +128,202 @@ static int
blockif_enqueue(struct blockif_ctxt *bc, struct blockif_req *breq,
enum blockop op)
{
- struct blockif_elem *be;
-
- assert(bc->bc_req_count < BLOCKIF_MAXREQ);
+ struct blockif_elem *be, *tbe;
+ off_t off;
+ int i;
be = TAILQ_FIRST(&bc->bc_freeq);
assert(be != NULL);
assert(be->be_status == BST_FREE);
-
TAILQ_REMOVE(&bc->bc_freeq, be, be_link);
- be->be_status = BST_PEND;
be->be_req = breq;
be->be_op = op;
+ switch (op) {
+ case BOP_READ:
+ case BOP_WRITE:
+ case BOP_DELETE:
+ off = breq->br_offset;
+ for (i = 0; i < breq->br_iovcnt; i++)
+ off += breq->br_iov[i].iov_len;
+ break;
+ default:
+ off = OFF_MAX;
+ }
+ be->be_block = off;
+ TAILQ_FOREACH(tbe, &bc->bc_pendq, be_link) {
+ if (tbe->be_block == breq->br_offset)
+ break;
+ }
+ if (tbe == NULL) {
+ TAILQ_FOREACH(tbe, &bc->bc_busyq, be_link) {
+ if (tbe->be_block == breq->br_offset)
+ break;
+ }
+ }
+ if (tbe == NULL)
+ be->be_status = BST_PEND;
+ else
+ be->be_status = BST_BLOCK;
TAILQ_INSERT_TAIL(&bc->bc_pendq, be, be_link);
-
- bc->bc_req_count++;
-
- return (0);
+ return (be->be_status == BST_PEND);
}
static int
-blockif_dequeue(struct blockif_ctxt *bc, struct blockif_elem **bep)
+blockif_dequeue(struct blockif_ctxt *bc, pthread_t t, struct blockif_elem **bep)
{
struct blockif_elem *be;
- if (bc->bc_req_count == 0)
- return (ENOENT);
-
- be = TAILQ_FIRST(&bc->bc_pendq);
- assert(be != NULL);
- assert(be->be_status == BST_PEND);
+ TAILQ_FOREACH(be, &bc->bc_pendq, be_link) {
+ if (be->be_status == BST_PEND)
+ break;
+ assert(be->be_status == BST_BLOCK);
+ }
+ if (be == NULL)
+ return (0);
TAILQ_REMOVE(&bc->bc_pendq, be, be_link);
be->be_status = BST_BUSY;
- be->be_tid = bc->bc_btid;
+ be->be_tid = t;
TAILQ_INSERT_TAIL(&bc->bc_busyq, be, be_link);
-
*bep = be;
-
- return (0);
+ return (1);
}
static void
blockif_complete(struct blockif_ctxt *bc, struct blockif_elem *be)
{
- assert(be->be_status == BST_DONE);
+ struct blockif_elem *tbe;
- TAILQ_REMOVE(&bc->bc_busyq, be, be_link);
+ if (be->be_status == BST_DONE || be->be_status == BST_BUSY)
+ TAILQ_REMOVE(&bc->bc_busyq, be, be_link);
+ else
+ TAILQ_REMOVE(&bc->bc_pendq, be, be_link);
+ TAILQ_FOREACH(tbe, &bc->bc_pendq, be_link) {
+ if (tbe->be_req->br_offset == be->be_block)
+ tbe->be_status = BST_PEND;
+ }
be->be_tid = 0;
be->be_status = BST_FREE;
be->be_req = NULL;
TAILQ_INSERT_TAIL(&bc->bc_freeq, be, be_link);
-
- bc->bc_req_count--;
}
static void
-blockif_proc(struct blockif_ctxt *bc, struct blockif_elem *be)
+blockif_proc(struct blockif_ctxt *bc, struct blockif_elem *be, uint8_t *buf)
{
struct blockif_req *br;
- int err;
+#ifdef __FreeBSD__
+ off_t arg[2];
+#endif
+ ssize_t clen, len, off, boff, voff;
+ int i, err;
br = be->be_req;
+ if (br->br_iovcnt <= 1)
+ buf = NULL;
err = 0;
-
switch (be->be_op) {
case BOP_READ:
- if (preadv(bc->bc_fd, br->br_iov, br->br_iovcnt,
- br->br_offset) < 0)
- err = errno;
+ if (buf == NULL) {
+ if ((len = preadv(bc->bc_fd, br->br_iov, br->br_iovcnt,
+ br->br_offset)) < 0)
+ err = errno;
+ else
+ br->br_resid -= len;
+ break;
+ }
+ i = 0;
+ off = voff = 0;
+ while (br->br_resid > 0) {
+ len = MIN(br->br_resid, MAXPHYS);
+ if (pread(bc->bc_fd, buf, len, br->br_offset +
+ off) < 0) {
+ err = errno;
+ break;
+ }
+ boff = 0;
+ do {
+ clen = MIN(len - boff, br->br_iov[i].iov_len -
+ voff);
+ memcpy(br->br_iov[i].iov_base + voff,
+ buf + boff, clen);
+ if (clen < br->br_iov[i].iov_len - voff)
+ voff += clen;
+ else {
+ i++;
+ voff = 0;
+ }
+ boff += clen;
+ } while (boff < len);
+ off += len;
+ br->br_resid -= len;
+ }
break;
case BOP_WRITE:
- if (bc->bc_rdonly)
+ if (bc->bc_rdonly) {
err = EROFS;
- else if (pwritev(bc->bc_fd, br->br_iov, br->br_iovcnt,
- br->br_offset) < 0)
- err = errno;
+ break;
+ }
+ if (buf == NULL) {
+ if ((len = pwritev(bc->bc_fd, br->br_iov, br->br_iovcnt,
+ br->br_offset)) < 0)
+ err = errno;
+ else
+ br->br_resid -= len;
+ break;
+ }
+ i = 0;
+ off = voff = 0;
+ while (br->br_resid > 0) {
+ len = MIN(br->br_resid, MAXPHYS);
+ boff = 0;
+ do {
+ clen = MIN(len - boff, br->br_iov[i].iov_len -
+ voff);
+ memcpy(buf + boff,
+ br->br_iov[i].iov_base + voff, clen);
+ if (clen < br->br_iov[i].iov_len - voff)
+ voff += clen;
+ else {
+ i++;
+ voff = 0;
+ }
+ boff += clen;
+ } while (boff < len);
+ if (pwrite(bc->bc_fd, buf, len, br->br_offset +
+ off) < 0) {
+ err = errno;
+ break;
+ }
+ off += len;
+ br->br_resid -= len;
+ }
break;
case BOP_FLUSH:
+ if (bc->bc_ischr) {
+#ifdef __FreeBSD__
+ if (ioctl(bc->bc_fd, DIOCGFLUSH))
+ err = errno;
+#endif
+ } else if (fsync(bc->bc_fd))
+ err = errno;
+ break;
+ case BOP_DELETE:
+ if (!bc->bc_candelete)
+ err = EOPNOTSUPP;
+ else if (bc->bc_rdonly)
+ err = EROFS;
+#ifdef __FreeBSD__
+ else if (bc->bc_ischr) {
+ arg[0] = br->br_offset;
+ arg[1] = br->br_resid;
+ if (ioctl(bc->bc_fd, DIOCGDELETE, arg))
+ err = errno;
+ else
+ br->br_resid = 0;
+ }
+#endif
+ else
+ err = EOPNOTSUPP;
break;
default:
err = EINVAL;
@@ -206,28 +340,34 @@ blockif_thr(void *arg)
{
struct blockif_ctxt *bc;
struct blockif_elem *be;
+ pthread_t t;
+ uint8_t *buf;
bc = arg;
+ if (bc->bc_isgeom)
+ buf = malloc(MAXPHYS);
+ else
+ buf = NULL;
+ t = pthread_self();
+ pthread_mutex_lock(&bc->bc_mtx);
for (;;) {
- pthread_mutex_lock(&bc->bc_mtx);
- while (!blockif_dequeue(bc, &be)) {
+ while (blockif_dequeue(bc, t, &be)) {
pthread_mutex_unlock(&bc->bc_mtx);
- blockif_proc(bc, be);
+ blockif_proc(bc, be, buf);
pthread_mutex_lock(&bc->bc_mtx);
blockif_complete(bc, be);
}
- pthread_cond_wait(&bc->bc_cond, &bc->bc_mtx);
- pthread_mutex_unlock(&bc->bc_mtx);
-
- /*
- * Check ctxt status here to see if exit requested
- */
+ /* Check ctxt status here to see if exit requested */
if (bc->bc_closing)
- pthread_exit(NULL);
+ break;
+ pthread_cond_wait(&bc->bc_cond, &bc->bc_mtx);
}
+ pthread_mutex_unlock(&bc->bc_mtx);
- /* Not reached */
+ if (buf)
+ free(buf);
+ pthread_exit(NULL);
return (NULL);
}
@@ -276,15 +416,27 @@ struct blockif_ctxt *
blockif_open(const char *optstr, const char *ident)
{
char tname[MAXCOMLEN + 1];
- char *nopt, *xopts;
+#ifdef __FreeBSD__
+ char name[MAXPATHLEN];
+#endif
+ char *nopt, *xopts, *cp;
struct blockif_ctxt *bc;
struct stat sbuf;
- off_t size;
+#ifdef __FreeBSD__
+ struct diocgattr_arg arg;
+#endif
+ off_t size, psectsz, psectoff;
int extra, fd, i, sectsz;
- int nocache, sync, ro;
+ int nocache, sync, ro, candelete, geom, ssopt, pssopt;
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_t rights;
+ cap_ioctl_t cmds[] = { DIOCGFLUSH, DIOCGDELETE };
+#endif
pthread_once(&blockif_once, blockif_init);
+ fd = -1;
+ ssopt = 0;
nocache = 0;
sync = 0;
ro = 0;
@@ -293,16 +445,25 @@ blockif_open(const char *optstr, const char *ident)
* The first element in the optstring is always a pathname.
* Optional elements follow
*/
- nopt = strdup(optstr);
- for (xopts = strtok(nopt, ",");
- xopts != NULL;
- xopts = strtok(NULL, ",")) {
- if (!strcmp(xopts, "nocache"))
+ nopt = xopts = strdup(optstr);
+ while (xopts != NULL) {
+ cp = strsep(&xopts, ",");
+ if (cp == nopt) /* file or device pathname */
+ continue;
+ else if (!strcmp(cp, "nocache"))
nocache = 1;
- else if (!strcmp(xopts, "sync"))
+ else if (!strcmp(cp, "sync") || !strcmp(cp, "direct"))
sync = 1;
- else if (!strcmp(xopts, "ro"))
+ else if (!strcmp(cp, "ro"))
ro = 1;
+ else if (sscanf(cp, "sectorsize=%d/%d", &ssopt, &pssopt) == 2)
+ ;
+ else if (sscanf(cp, "sectorsize=%d", &ssopt) == 1)
+ pssopt = ssopt;
+ else {
+ fprintf(stderr, "Invalid device option \"%s\"\n", cp);
+ goto err;
+ }
}
extra = 0;
@@ -319,62 +480,124 @@ blockif_open(const char *optstr, const char *ident)
}
if (fd < 0) {
- perror("Could not open backing file");
- return (NULL);
+ warn("Could not open backing file: %s", nopt);
+ goto err;
}
if (fstat(fd, &sbuf) < 0) {
- perror("Could not stat backing file");
- close(fd);
- return (NULL);
+ warn("Could not stat backing file %s", nopt);
+ goto err;
}
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_init(&rights, CAP_FSYNC, CAP_IOCTL, CAP_READ, CAP_SEEK,
+ CAP_WRITE);
+ if (ro)
+ cap_rights_clear(&rights, CAP_FSYNC, CAP_WRITE);
+
+ if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+#endif
+
/*
* Deal with raw devices
*/
size = sbuf.st_size;
sectsz = DEV_BSIZE;
+ psectsz = psectoff = 0;
+ candelete = geom = 0;
#ifdef __FreeBSD__
if (S_ISCHR(sbuf.st_mode)) {
if (ioctl(fd, DIOCGMEDIASIZE, &size) < 0 ||
ioctl(fd, DIOCGSECTORSIZE, &sectsz)) {
perror("Could not fetch dev blk/sector size");
- close(fd);
- return (NULL);
+ goto err;
}
assert(size != 0);
assert(sectsz != 0);
- }
+ if (ioctl(fd, DIOCGSTRIPESIZE, &psectsz) == 0 && psectsz > 0)
+ ioctl(fd, DIOCGSTRIPEOFFSET, &psectoff);
+ strlcpy(arg.name, "GEOM::candelete", sizeof(arg.name));
+ arg.len = sizeof(arg.value.i);
+ if (ioctl(fd, DIOCGATTR, &arg) == 0)
+ candelete = arg.value.i;
+ if (ioctl(fd, DIOCGPROVIDERNAME, name) == 0)
+ geom = 1;
+ } else
+#endif
+ psectsz = sbuf.st_blksize;
+
+#ifndef WITHOUT_CAPSICUM
+ if (cap_ioctls_limit(fd, cmds, nitems(cmds)) == -1 && errno != ENOSYS)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
#endif
+ if (ssopt != 0) {
+ if (!powerof2(ssopt) || !powerof2(pssopt) || ssopt < 512 ||
+ ssopt > pssopt) {
+ fprintf(stderr, "Invalid sector size %d/%d\n",
+ ssopt, pssopt);
+ goto err;
+ }
+
+ /*
+ * Some backend drivers (e.g. cd0, ada0) require that the I/O
+ * size be a multiple of the device's sector size.
+ *
+ * Validate that the emulated sector size complies with this
+ * requirement.
+ */
+ if (S_ISCHR(sbuf.st_mode)) {
+ if (ssopt < sectsz || (ssopt % sectsz) != 0) {
+ fprintf(stderr, "Sector size %d incompatible "
+ "with underlying device sector size %d\n",
+ ssopt, sectsz);
+ goto err;
+ }
+ }
+
+ sectsz = ssopt;
+ psectsz = pssopt;
+ psectoff = 0;
+ }
+
bc = calloc(1, sizeof(struct blockif_ctxt));
if (bc == NULL) {
- close(fd);
- return (NULL);
+ perror("calloc");
+ goto err;
}
bc->bc_magic = BLOCKIF_SIG;
bc->bc_fd = fd;
+ bc->bc_ischr = S_ISCHR(sbuf.st_mode);
+ bc->bc_isgeom = geom;
+ bc->bc_candelete = candelete;
bc->bc_rdonly = ro;
bc->bc_size = size;
bc->bc_sectsz = sectsz;
+ bc->bc_psectsz = psectsz;
+ bc->bc_psectoff = psectoff;
pthread_mutex_init(&bc->bc_mtx, NULL);
pthread_cond_init(&bc->bc_cond, NULL);
TAILQ_INIT(&bc->bc_freeq);
TAILQ_INIT(&bc->bc_pendq);
TAILQ_INIT(&bc->bc_busyq);
- bc->bc_req_count = 0;
for (i = 0; i < BLOCKIF_MAXREQ; i++) {
bc->bc_reqs[i].be_status = BST_FREE;
TAILQ_INSERT_HEAD(&bc->bc_freeq, &bc->bc_reqs[i], be_link);
}
- pthread_create(&bc->bc_btid, NULL, blockif_thr, bc);
-
- snprintf(tname, sizeof(tname), "blk-%s", ident);
- pthread_set_name_np(bc->bc_btid, tname);
+ for (i = 0; i < BLOCKIF_NUMTHR; i++) {
+ pthread_create(&bc->bc_btid[i], NULL, blockif_thr, bc);
+ snprintf(tname, sizeof(tname), "blk-%s-%d", ident, i);
+ pthread_set_name_np(bc->bc_btid[i], tname);
+ }
return (bc);
+err:
+ if (fd >= 0)
+ close(fd);
+ return (NULL);
}
static int
@@ -386,13 +609,13 @@ blockif_request(struct blockif_ctxt *bc, struct blockif_req *breq,
err = 0;
pthread_mutex_lock(&bc->bc_mtx);
- if (bc->bc_req_count < BLOCKIF_MAXREQ) {
+ if (!TAILQ_EMPTY(&bc->bc_freeq)) {
/*
* Enqueue and inform the block i/o thread
* that there is work available
*/
- blockif_enqueue(bc, breq, op);
- pthread_cond_signal(&bc->bc_cond);
+ if (blockif_enqueue(bc, breq, op))
+ pthread_cond_signal(&bc->bc_cond);
} else {
/*
* Callers are not allowed to enqueue more than
@@ -432,6 +655,14 @@ blockif_flush(struct blockif_ctxt *bc, struct blockif_req *breq)
}
int
+blockif_delete(struct blockif_ctxt *bc, struct blockif_req *breq)
+{
+
+ assert(bc->bc_magic == BLOCKIF_SIG);
+ return (blockif_request(bc, breq, BOP_DELETE));
+}
+
+int
blockif_cancel(struct blockif_ctxt *bc, struct blockif_req *breq)
{
struct blockif_elem *be;
@@ -450,11 +681,7 @@ blockif_cancel(struct blockif_ctxt *bc, struct blockif_req *breq)
/*
* Found it.
*/
- TAILQ_REMOVE(&bc->bc_pendq, be, be_link);
- be->be_status = BST_FREE;
- be->be_req = NULL;
- TAILQ_INSERT_TAIL(&bc->bc_freeq, be, be_link);
- bc->bc_req_count--;
+ blockif_complete(bc, be);
pthread_mutex_unlock(&bc->bc_mtx);
return (0);
@@ -515,18 +742,19 @@ int
blockif_close(struct blockif_ctxt *bc)
{
void *jval;
- int err;
-
- err = 0;
+ int i;
assert(bc->bc_magic == BLOCKIF_SIG);
/*
* Stop the block i/o thread
*/
+ pthread_mutex_lock(&bc->bc_mtx);
bc->bc_closing = 1;
- pthread_cond_signal(&bc->bc_cond);
- pthread_join(bc->bc_btid, &jval);
+ pthread_mutex_unlock(&bc->bc_mtx);
+ pthread_cond_broadcast(&bc->bc_cond);
+ for (i = 0; i < BLOCKIF_NUMTHR; i++)
+ pthread_join(bc->bc_btid[i], &jval);
/* XXX Cancel queued i/o's ??? */
@@ -608,6 +836,15 @@ blockif_sectsz(struct blockif_ctxt *bc)
return (bc->bc_sectsz);
}
+void
+blockif_psectsz(struct blockif_ctxt *bc, int *size, int *off)
+{
+
+ assert(bc->bc_magic == BLOCKIF_SIG);
+ *size = bc->bc_psectsz;
+ *off = bc->bc_psectoff;
+}
+
int
blockif_queuesz(struct blockif_ctxt *bc)
{
@@ -623,3 +860,11 @@ blockif_is_ro(struct blockif_ctxt *bc)
assert(bc->bc_magic == BLOCKIF_SIG);
return (bc->bc_rdonly);
}
+
+int
+blockif_candelete(struct blockif_ctxt *bc)
+{
+
+ assert(bc->bc_magic == BLOCKIF_SIG);
+ return (bc->bc_candelete);
+}
diff --git a/usr/src/cmd/bhyve/block_if.h b/usr/src/cmd/bhyve/block_if.h
index 5ef120933c..9e8df4120f 100644
--- a/usr/src/cmd/bhyve/block_if.h
+++ b/usr/src/cmd/bhyve/block_if.h
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/block_if.h 268638 2014-07-15 00:25:54Z grehan $
+ * $FreeBSD$
*/
/*
@@ -40,15 +40,16 @@
#include <sys/unistd.h>
#ifdef __FreeBSD__
-#define BLOCKIF_IOV_MAX 32 /* not practical to be IOV_MAX */
+#define BLOCKIF_IOV_MAX 33 /* not practical to be IOV_MAX */
#else
-#define BLOCKIF_IOV_MAX 16 /* not practical to be IOV_MAX */
+#define BLOCKIF_IOV_MAX 17 /* not practical to be IOV_MAX */
#endif
struct blockif_req {
struct iovec br_iov[BLOCKIF_IOV_MAX];
int br_iovcnt;
off_t br_offset;
+ ssize_t br_resid;
void (*br_callback)(struct blockif_req *req, int err);
void *br_param;
};
@@ -59,11 +60,14 @@ off_t blockif_size(struct blockif_ctxt *bc);
void blockif_chs(struct blockif_ctxt *bc, uint16_t *c, uint8_t *h,
uint8_t *s);
int blockif_sectsz(struct blockif_ctxt *bc);
+void blockif_psectsz(struct blockif_ctxt *bc, int *size, int *off);
int blockif_queuesz(struct blockif_ctxt *bc);
int blockif_is_ro(struct blockif_ctxt *bc);
+int blockif_candelete(struct blockif_ctxt *bc);
int blockif_read(struct blockif_ctxt *bc, struct blockif_req *breq);
int blockif_write(struct blockif_ctxt *bc, struct blockif_req *breq);
int blockif_flush(struct blockif_ctxt *bc, struct blockif_req *breq);
+int blockif_delete(struct blockif_ctxt *bc, struct blockif_req *breq);
int blockif_cancel(struct blockif_ctxt *bc, struct blockif_req *breq);
int blockif_close(struct blockif_ctxt *bc);
diff --git a/usr/src/cmd/bhyve/bootrom.c b/usr/src/cmd/bhyve/bootrom.c
new file mode 100644
index 0000000000..5e4e0e93a2
--- /dev/null
+++ b/usr/src/cmd/bhyve/bootrom.c
@@ -0,0 +1,111 @@
+/*-
+ * Copyright (c) 2015 Neel Natu <neel@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <machine/vmm.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+#include <vmmapi.h>
+#include "bhyverun.h"
+#include "bootrom.h"
+
+#define MAX_BOOTROM_SIZE (16 * 1024 * 1024) /* 16 MB */
+
+int
+bootrom_init(struct vmctx *ctx, const char *romfile)
+{
+ struct stat sbuf;
+ vm_paddr_t gpa;
+ ssize_t rlen;
+ char *ptr;
+ int fd, i, rv, prot;
+
+ rv = -1;
+ fd = open(romfile, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Error opening bootrom \"%s\": %s\n",
+ romfile, strerror(errno));
+ goto done;
+ }
+
+ if (fstat(fd, &sbuf) < 0) {
+ fprintf(stderr, "Could not fstat bootrom file \"%s\": %s\n",
+ romfile, strerror(errno));
+ goto done;
+ }
+
+ /*
+ * Limit bootrom size to 16MB so it doesn't encroach into reserved
+ * MMIO space (e.g. APIC, HPET, MSI).
+ */
+ if (sbuf.st_size > MAX_BOOTROM_SIZE || sbuf.st_size < PAGE_SIZE) {
+ fprintf(stderr, "Invalid bootrom size %ld\n", sbuf.st_size);
+ goto done;
+ }
+
+ if (sbuf.st_size & PAGE_MASK) {
+ fprintf(stderr, "Bootrom size %ld is not a multiple of the "
+ "page size\n", sbuf.st_size);
+ goto done;
+ }
+
+ ptr = vm_create_devmem(ctx, VM_BOOTROM, "bootrom", sbuf.st_size);
+ if (ptr == MAP_FAILED)
+ goto done;
+
+ /* Map the bootrom into the guest address space */
+ prot = PROT_READ | PROT_EXEC;
+ gpa = (1ULL << 32) - sbuf.st_size;
+ if (vm_mmap_memseg(ctx, gpa, VM_BOOTROM, 0, sbuf.st_size, prot) != 0)
+ goto done;
+
+ /* Read 'romfile' into the guest address space */
+ for (i = 0; i < sbuf.st_size / PAGE_SIZE; i++) {
+ rlen = read(fd, ptr + i * PAGE_SIZE, PAGE_SIZE);
+ if (rlen != PAGE_SIZE) {
+ fprintf(stderr, "Incomplete read of page %d of bootrom "
+ "file %s: %ld bytes\n", i, romfile, rlen);
+ goto done;
+ }
+ }
+ rv = 0;
+done:
+ if (fd >= 0)
+ close(fd);
+ return (rv);
+}
diff --git a/usr/src/cmd/bhyve/bootrom.h b/usr/src/cmd/bhyve/bootrom.h
new file mode 100644
index 0000000000..af150d3255
--- /dev/null
+++ b/usr/src/cmd/bhyve/bootrom.h
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2015 Neel Natu <neel@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BOOTROM_H_
+#define _BOOTROM_H_
+
+#include <stdbool.h>
+
+struct vmctx;
+
+int bootrom_init(struct vmctx *ctx, const char *romfile);
+
+#endif
diff --git a/usr/src/cmd/bhyve/console.c b/usr/src/cmd/bhyve/console.c
index a8d07709be..ebb9c921bf 100644
--- a/usr/src/cmd/bhyve/console.c
+++ b/usr/src/cmd/bhyve/console.c
@@ -40,15 +40,23 @@ static struct {
kbd_event_func_t kbd_event_cb;
void *kbd_arg;
+ int kbd_priority;
ptr_event_func_t ptr_event_cb;
void *ptr_arg;
+ int ptr_priority;
} console;
void
-console_init(void)
+console_init(int w, int h, void *fbaddr)
{
- console.gc = bhyvegc_init(640, 400);
+ console.gc = bhyvegc_init(w, h, fbaddr);
+}
+
+void
+console_set_fbaddr(void *fbaddr)
+{
+ bhyvegc_set_fbaddr(console.gc, fbaddr);
}
struct bhyvegc_image *
@@ -71,31 +79,40 @@ console_fb_register(fb_render_func_t render_cb, void *arg)
void
console_refresh(void)
{
- (*console.fb_render_cb)(console.gc, console.fb_arg);
+ if (console.fb_render_cb)
+ (*console.fb_render_cb)(console.gc, console.fb_arg);
}
void
-console_kbd_register(kbd_event_func_t event_cb, void *arg)
+console_kbd_register(kbd_event_func_t event_cb, void *arg, int pri)
{
- console.kbd_event_cb = event_cb;
- console.kbd_arg = arg;
+ if (pri > console.kbd_priority) {
+ console.kbd_event_cb = event_cb;
+ console.kbd_arg = arg;
+ console.kbd_priority = pri;
+ }
}
void
-console_ptr_register(ptr_event_func_t event_cb, void *arg)
+console_ptr_register(ptr_event_func_t event_cb, void *arg, int pri)
{
- console.ptr_event_cb = event_cb;
- console.ptr_arg = arg;
+ if (pri > console.ptr_priority) {
+ console.ptr_event_cb = event_cb;
+ console.ptr_arg = arg;
+ console.ptr_priority = pri;
+ }
}
void
console_key_event(int down, uint32_t keysym)
{
- (*console.kbd_event_cb)(down, keysym, console.kbd_arg);
+ if (console.kbd_event_cb)
+ (*console.kbd_event_cb)(down, keysym, console.kbd_arg);
}
void
console_ptr_event(uint8_t button, int x, int y)
{
- (*console.ptr_event_cb)(button, x, y, console.ptr_arg);
+ if (console.ptr_event_cb)
+ (*console.ptr_event_cb)(button, x, y, console.ptr_arg);
}
diff --git a/usr/src/cmd/bhyve/console.h b/usr/src/cmd/bhyve/console.h
index bffb7c2456..47193938a6 100644
--- a/usr/src/cmd/bhyve/console.h
+++ b/usr/src/cmd/bhyve/console.h
@@ -35,16 +35,19 @@ typedef void (*fb_render_func_t)(struct bhyvegc *gc, void *arg);
typedef void (*kbd_event_func_t)(int down, uint32_t keysym, void *arg);
typedef void (*ptr_event_func_t)(uint8_t mask, int x, int y, void *arg);
-void console_init(void);
+void console_init(int w, int h, void *fbaddr);
+
+void console_set_fbaddr(void *fbaddr);
+
struct bhyvegc_image *console_get_image(void);
void console_fb_register(fb_render_func_t render_cb, void *arg);
void console_refresh(void);
-void console_kbd_register(kbd_event_func_t event_cb, void *arg);
+void console_kbd_register(kbd_event_func_t event_cb, void *arg, int pri);
void console_key_event(int down, uint32_t keysym);
-void console_ptr_register(ptr_event_func_t event_cb, void *arg);
+void console_ptr_register(ptr_event_func_t event_cb, void *arg, int pri);
void console_ptr_event(uint8_t button, int x, int y);
#endif /* _CONSOLE_H_ */
diff --git a/usr/src/cmd/bhyve/consport.c b/usr/src/cmd/bhyve/consport.c
index 90e3d0e5eb..599df62ec3 100644
--- a/usr/src/cmd/bhyve/consport.c
+++ b/usr/src/cmd/bhyve/consport.c
@@ -23,20 +23,26 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/consport.c 264277 2014-04-08 21:02:03Z jhb $
+ * $FreeBSD$
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/usr.sbin/bhyve/consport.c 264277 2014-04-08 21:02:03Z jhb $");
+__FBSDID("$FreeBSD$");
#include <sys/types.h>
+#ifndef WITHOUT_CAPSICUM
+#include <sys/capsicum.h>
+#endif
#include <sys/select.h>
+#include <err.h>
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <stdbool.h>
+#include <sysexits.h>
#include "inout.h"
#include "pci_lpc.h"
@@ -108,6 +114,10 @@ console_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
uint32_t *eax, void *arg)
{
static int opened;
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_t rights;
+ cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ };
+#endif
if (bytes == 2 && in) {
*eax = BVM_CONS_SIG;
@@ -127,6 +137,13 @@ console_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
return (-1);
if (!opened) {
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ, CAP_WRITE);
+ if (cap_rights_limit(STDIN_FILENO, &rights) == -1 && errno != ENOSYS)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+ if (cap_ioctls_limit(STDIN_FILENO, cmds, nitems(cmds)) == -1 && errno != ENOSYS)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+#endif
ttyopen();
opened = 1;
}
diff --git a/usr/src/cmd/bhyve/dbgport.c b/usr/src/cmd/bhyve/dbgport.c
new file mode 100644
index 0000000000..a1fe4b8d9f
--- /dev/null
+++ b/usr/src/cmd/bhyve/dbgport.c
@@ -0,0 +1,175 @@
+/*-
+ * Copyright (c) 2011 NetApp, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#ifndef WITHOUT_CAPSICUM
+#include <sys/capsicum.h>
+#endif
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/uio.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "inout.h"
+#include "dbgport.h"
+#include "pci_lpc.h"
+
+#define BVM_DBG_PORT 0x224
+#define BVM_DBG_SIG ('B' << 8 | 'V')
+
+static int listen_fd, conn_fd;
+
+static struct sockaddr_in sin;
+
+static int
+dbg_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
+ uint32_t *eax, void *arg)
+{
+ int nwritten, nread, printonce;
+ int on = 1;
+ char ch;
+
+ if (bytes == 2 && in) {
+ *eax = BVM_DBG_SIG;
+ return (0);
+ }
+
+ if (bytes != 4)
+ return (-1);
+
+again:
+ printonce = 0;
+ while (conn_fd < 0) {
+ if (!printonce) {
+ printf("Waiting for connection from gdb\r\n");
+ printonce = 1;
+ }
+ conn_fd = accept4(listen_fd, NULL, NULL, SOCK_NONBLOCK);
+ if (conn_fd >= 0) {
+ /* Avoid EPIPE after the client drops off. */
+ (void)setsockopt(conn_fd, SOL_SOCKET, SO_NOSIGPIPE,
+ &on, sizeof(on));
+ /* Improve latency for one byte at a time tranfers. */
+ (void)setsockopt(conn_fd, IPPROTO_TCP, TCP_NODELAY,
+ &on, sizeof(on));
+ } else if (errno != EINTR) {
+ perror("accept");
+ }
+ }
+
+ if (in) {
+ nread = read(conn_fd, &ch, 1);
+ if (nread == -1 && errno == EAGAIN)
+ *eax = -1;
+ else if (nread == 1)
+ *eax = ch;
+ else {
+ close(conn_fd);
+ conn_fd = -1;
+ goto again;
+ }
+ } else {
+ ch = *eax;
+ nwritten = write(conn_fd, &ch, 1);
+ if (nwritten != 1) {
+ close(conn_fd);
+ conn_fd = -1;
+ goto again;
+ }
+ }
+ return (0);
+}
+
+static struct inout_port dbgport = {
+ "bvmdbg",
+ BVM_DBG_PORT,
+ 1,
+ IOPORT_F_INOUT,
+ dbg_handler
+};
+
+SYSRES_IO(BVM_DBG_PORT, 4);
+
+void
+init_dbgport(int sport)
+{
+ int reuse;
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_t rights;
+#endif
+
+ conn_fd = -1;
+
+ if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ perror("socket");
+ exit(1);
+ }
+
+#ifdef __FreeBSD__
+ sin.sin_len = sizeof(sin);
+#endif
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ sin.sin_port = htons(sport);
+
+ reuse = 1;
+ if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuse,
+ sizeof(reuse)) < 0) {
+ perror("setsockopt");
+ exit(1);
+ }
+
+ if (bind(listen_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ perror("bind");
+ exit(1);
+ }
+
+ if (listen(listen_fd, 1) < 0) {
+ perror("listen");
+ exit(1);
+ }
+
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_init(&rights, CAP_ACCEPT, CAP_READ, CAP_WRITE);
+ if (cap_rights_limit(listen_fd, &rights) == -1 && errno != ENOSYS)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+#endif
+
+ register_inout(&dbgport);
+}
diff --git a/usr/src/cmd/bhyve/dbgport.h b/usr/src/cmd/bhyve/dbgport.h
index b95df0bd31..2ddcbf8f80 100644
--- a/usr/src/cmd/bhyve/dbgport.h
+++ b/usr/src/cmd/bhyve/dbgport.h
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/dbgport.h 256156 2013-10-08 16:36:17Z neel $
+ * $FreeBSD$
*/
#ifndef _DBGPORT_H_
diff --git a/usr/src/cmd/bhyve/fwctl.c b/usr/src/cmd/bhyve/fwctl.c
new file mode 100644
index 0000000000..9e90c1ade6
--- /dev/null
+++ b/usr/src/cmd/bhyve/fwctl.c
@@ -0,0 +1,549 @@
+/*-
+ * Copyright (c) 2015 Peter Grehan <grehan@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Guest firmware interface. Uses i/o ports x510/x511 as Qemu does,
+ * but with a request/response messaging protocol.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/uio.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bhyverun.h"
+#include "inout.h"
+#include "fwctl.h"
+
+/*
+ * Messaging protocol base operations
+ */
+#define OP_NULL 1
+#define OP_ECHO 2
+#define OP_GET 3
+#define OP_GET_LEN 4
+#define OP_SET 5
+#define OP_MAX OP_SET
+
+/* I/O ports */
+#define FWCTL_OUT 0x510
+#define FWCTL_IN 0x511
+
+/*
+ * Back-end state-machine
+ */
+enum state {
+ DORMANT,
+ IDENT_WAIT,
+ IDENT_SEND,
+ REQ,
+ RESP
+} be_state = DORMANT;
+
+static uint8_t sig[] = { 'B', 'H', 'Y', 'V' };
+static u_int ident_idx;
+
+struct op_info {
+ int op;
+ int (*op_start)(int len);
+ void (*op_data)(uint32_t data, int len);
+ int (*op_result)(struct iovec **data);
+ void (*op_done)(struct iovec *data);
+};
+static struct op_info *ops[OP_MAX+1];
+
+/* Return 0-padded uint32_t */
+static uint32_t
+fwctl_send_rest(uint32_t *data, size_t len)
+{
+ union {
+ uint8_t c[4];
+ uint32_t w;
+ } u;
+ uint8_t *cdata;
+ int i;
+
+ cdata = (uint8_t *) data;
+ u.w = 0;
+
+ for (i = 0, u.w = 0; i < len; i++)
+ u.c[i] = *cdata++;
+
+ return (u.w);
+}
+
+/*
+ * error op dummy proto - drop all data sent and return an error
+*/
+static int errop_code;
+
+static void
+errop_set(int err)
+{
+
+ errop_code = err;
+}
+
+static int
+errop_start(int len)
+{
+ errop_code = ENOENT;
+
+ /* accept any length */
+ return (errop_code);
+}
+
+static void
+errop_data(uint32_t data, int len)
+{
+
+ /* ignore */
+}
+
+static int
+errop_result(struct iovec **data)
+{
+
+ /* no data to send back; always successful */
+ *data = NULL;
+ return (errop_code);
+}
+
+static void
+errop_done(struct iovec *data)
+{
+
+ /* assert data is NULL */
+}
+
+static struct op_info errop_info = {
+ .op_start = errop_start,
+ .op_data = errop_data,
+ .op_result = errop_result,
+ .op_done = errop_done
+};
+
+/* OID search */
+SET_DECLARE(ctl_set, struct ctl);
+
+CTL_NODE("hw.ncpu", &guest_ncpus, sizeof(guest_ncpus));
+
+static struct ctl *
+ctl_locate(const char *str, int maxlen)
+{
+ struct ctl *cp, **cpp;
+
+ SET_FOREACH(cpp, ctl_set) {
+ cp = *cpp;
+ if (!strncmp(str, cp->c_oid, maxlen))
+ return (cp);
+ }
+
+ return (NULL);
+}
+
+/* uefi-sysctl get-len */
+#define FGET_STRSZ 80
+static struct iovec fget_biov[2];
+static char fget_str[FGET_STRSZ];
+static struct {
+ size_t f_sz;
+ uint32_t f_data[1024];
+} fget_buf;
+static int fget_cnt;
+static size_t fget_size;
+
+static int
+fget_start(int len)
+{
+
+ if (len > FGET_STRSZ)
+ return(E2BIG);
+
+ fget_cnt = 0;
+
+ return (0);
+}
+
+static void
+fget_data(uint32_t data, int len)
+{
+
+ *((uint32_t *) &fget_str[fget_cnt]) = data;
+ fget_cnt += sizeof(uint32_t);
+}
+
+static int
+fget_result(struct iovec **data, int val)
+{
+ struct ctl *cp;
+ int err;
+
+ err = 0;
+
+ /* Locate the OID */
+ cp = ctl_locate(fget_str, fget_cnt);
+ if (cp == NULL) {
+ *data = NULL;
+ err = ENOENT;
+ } else {
+ if (val) {
+ /* For now, copy the len/data into a buffer */
+ memset(&fget_buf, 0, sizeof(fget_buf));
+ fget_buf.f_sz = cp->c_len;
+ memcpy(fget_buf.f_data, cp->c_data, cp->c_len);
+ fget_biov[0].iov_base = (char *)&fget_buf;
+ fget_biov[0].iov_len = sizeof(fget_buf.f_sz) +
+ cp->c_len;
+ } else {
+ fget_size = cp->c_len;
+ fget_biov[0].iov_base = (char *)&fget_size;
+ fget_biov[0].iov_len = sizeof(fget_size);
+ }
+
+ fget_biov[1].iov_base = NULL;
+ fget_biov[1].iov_len = 0;
+ *data = fget_biov;
+ }
+
+ return (err);
+}
+
+static void
+fget_done(struct iovec *data)
+{
+
+ /* nothing needs to be freed */
+}
+
+static int
+fget_len_result(struct iovec **data)
+{
+ return (fget_result(data, 0));
+}
+
+static int
+fget_val_result(struct iovec **data)
+{
+ return (fget_result(data, 1));
+}
+
+static struct op_info fgetlen_info = {
+ .op_start = fget_start,
+ .op_data = fget_data,
+ .op_result = fget_len_result,
+ .op_done = fget_done
+};
+
+static struct op_info fgetval_info = {
+ .op_start = fget_start,
+ .op_data = fget_data,
+ .op_result = fget_val_result,
+ .op_done = fget_done
+};
+
+static struct req_info {
+ int req_error;
+ u_int req_count;
+ uint32_t req_size;
+ uint32_t req_type;
+ uint32_t req_txid;
+ struct op_info *req_op;
+ int resp_error;
+ int resp_count;
+ int resp_size;
+ int resp_off;
+ struct iovec *resp_biov;
+} rinfo;
+
+static void
+fwctl_response_done(void)
+{
+
+ (*rinfo.req_op->op_done)(rinfo.resp_biov);
+
+ /* reinit the req data struct */
+ memset(&rinfo, 0, sizeof(rinfo));
+}
+
+static void
+fwctl_request_done(void)
+{
+
+ rinfo.resp_error = (*rinfo.req_op->op_result)(&rinfo.resp_biov);
+
+ /* XXX only a single vector supported at the moment */
+ rinfo.resp_off = 0;
+ if (rinfo.resp_biov == NULL) {
+ rinfo.resp_size = 0;
+ } else {
+ rinfo.resp_size = rinfo.resp_biov[0].iov_len;
+ }
+}
+
+static int
+fwctl_request_start(void)
+{
+ int err;
+
+ /* Data size doesn't include header */
+ rinfo.req_size -= 12;
+
+ rinfo.req_op = &errop_info;
+ if (rinfo.req_type <= OP_MAX && ops[rinfo.req_type] != NULL)
+ rinfo.req_op = ops[rinfo.req_type];
+
+ err = (*rinfo.req_op->op_start)(rinfo.req_size);
+
+ if (err) {
+ errop_set(err);
+ rinfo.req_op = &errop_info;
+ }
+
+ /* Catch case of zero-length message here */
+ if (rinfo.req_size == 0) {
+ fwctl_request_done();
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+fwctl_request_data(uint32_t value)
+{
+ int remlen;
+
+ /* Make sure remaining size is >= 0 */
+ rinfo.req_size -= sizeof(uint32_t);
+ remlen = MAX(rinfo.req_size, 0);
+
+ (*rinfo.req_op->op_data)(value, remlen);
+
+ if (rinfo.req_size < sizeof(uint32_t)) {
+ fwctl_request_done();
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+fwctl_request(uint32_t value)
+{
+
+ int ret;
+
+ ret = 0;
+
+ switch (rinfo.req_count) {
+ case 0:
+ /* Verify size */
+ if (value < 12) {
+ printf("msg size error");
+ exit(1);
+ }
+ rinfo.req_size = value;
+ rinfo.req_count = 1;
+ break;
+ case 1:
+ rinfo.req_type = value;
+ rinfo.req_count++;
+ break;
+ case 2:
+ rinfo.req_txid = value;
+ rinfo.req_count++;
+ ret = fwctl_request_start();
+ break;
+ default:
+ ret = fwctl_request_data(value);
+ break;
+ }
+
+ return (ret);
+}
+
+static int
+fwctl_response(uint32_t *retval)
+{
+ uint32_t *dp;
+ int remlen;
+
+ switch(rinfo.resp_count) {
+ case 0:
+ /* 4 x u32 header len + data */
+ *retval = 4*sizeof(uint32_t) +
+ roundup(rinfo.resp_size, sizeof(uint32_t));
+ rinfo.resp_count++;
+ break;
+ case 1:
+ *retval = rinfo.req_type;
+ rinfo.resp_count++;
+ break;
+ case 2:
+ *retval = rinfo.req_txid;
+ rinfo.resp_count++;
+ break;
+ case 3:
+ *retval = rinfo.resp_error;
+ rinfo.resp_count++;
+ break;
+ default:
+ remlen = rinfo.resp_size - rinfo.resp_off;
+ dp = (uint32_t *)
+ ((uint8_t *)rinfo.resp_biov->iov_base + rinfo.resp_off);
+ if (remlen >= sizeof(uint32_t)) {
+ *retval = *dp;
+ } else if (remlen > 0) {
+ *retval = fwctl_send_rest(dp, remlen);
+ }
+ rinfo.resp_off += sizeof(uint32_t);
+ break;
+ }
+
+ if (rinfo.resp_count > 3 &&
+ rinfo.resp_size - rinfo.resp_off <= 0) {
+ fwctl_response_done();
+ return (1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * i/o port handling.
+ */
+static uint8_t
+fwctl_inb(void)
+{
+ uint8_t retval;
+
+ retval = 0xff;
+
+ switch (be_state) {
+ case IDENT_SEND:
+ retval = sig[ident_idx++];
+ if (ident_idx >= sizeof(sig))
+ be_state = REQ;
+ break;
+ default:
+ break;
+ }
+
+ return (retval);
+}
+
+static void
+fwctl_outw(uint16_t val)
+{
+ switch (be_state) {
+ case IDENT_WAIT:
+ if (val == 0) {
+ be_state = IDENT_SEND;
+ ident_idx = 0;
+ }
+ break;
+ default:
+ /* ignore */
+ break;
+ }
+}
+
+static uint32_t
+fwctl_inl(void)
+{
+ uint32_t retval;
+
+ switch (be_state) {
+ case RESP:
+ if (fwctl_response(&retval))
+ be_state = REQ;
+ break;
+ default:
+ retval = 0xffffffff;
+ break;
+ }
+
+ return (retval);
+}
+
+static void
+fwctl_outl(uint32_t val)
+{
+
+ switch (be_state) {
+ case REQ:
+ if (fwctl_request(val))
+ be_state = RESP;
+ default:
+ break;
+ }
+
+}
+
+static int
+fwctl_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
+ uint32_t *eax, void *arg)
+{
+
+ if (in) {
+ if (bytes == 1)
+ *eax = fwctl_inb();
+ else if (bytes == 4)
+ *eax = fwctl_inl();
+ else
+ *eax = 0xffff;
+ } else {
+ if (bytes == 2)
+ fwctl_outw(*eax);
+ else if (bytes == 4)
+ fwctl_outl(*eax);
+ }
+
+ return (0);
+}
+INOUT_PORT(fwctl_wreg, FWCTL_OUT, IOPORT_F_INOUT, fwctl_handler);
+INOUT_PORT(fwctl_rreg, FWCTL_IN, IOPORT_F_IN, fwctl_handler);
+
+void
+fwctl_init(void)
+{
+
+ ops[OP_GET_LEN] = &fgetlen_info;
+ ops[OP_GET] = &fgetval_info;
+
+ be_state = IDENT_WAIT;
+}
diff --git a/usr/src/cmd/bhyve/fwctl.h b/usr/src/cmd/bhyve/fwctl.h
new file mode 100644
index 0000000000..f5f8d131ab
--- /dev/null
+++ b/usr/src/cmd/bhyve/fwctl.h
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 2015 Peter Grehan <grehan@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _FWCTL_H_
+#define _FWCTL_H_
+
+#include <sys/linker_set.h>
+
+/*
+ * Linker set api for export of information to guest firmware via
+ * a sysctl-like OID interface
+ */
+struct ctl {
+ const char *c_oid;
+ const void *c_data;
+ const int c_len;
+};
+
+#define CTL_NODE(oid, data, len) \
+ static struct ctl __CONCAT(__ctl, __LINE__) = { \
+ oid, \
+ (data), \
+ (len), \
+ }; \
+ DATA_SET(ctl_set, __CONCAT(__ctl, __LINE__))
+
+void fwctl_init(void);
+
+#endif /* _FWCTL_H_ */
diff --git a/usr/src/cmd/bhyve/inout.c b/usr/src/cmd/bhyve/inout.c
index 510649893a..929bb3c5b8 100644
--- a/usr/src/cmd/bhyve/inout.c
+++ b/usr/src/cmd/bhyve/inout.c
@@ -23,11 +23,11 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/inout.c 277310 2015-01-18 03:08:30Z neel $
+ * $FreeBSD$
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/usr.sbin/bhyve/inout.c 277310 2015-01-18 03:08:30Z neel $");
+__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/linker_set.h>
@@ -107,7 +107,7 @@ emulate_inout(struct vmctx *ctx, int vcpu, struct vm_exit *vmexit, int strict)
uint32_t eax, val;
inout_func_t handler;
void *arg;
- int error, retval;
+ int error, fault, retval;
enum vm_reg_name idxreg;
uint64_t gla, index, iterations, count;
struct vm_inout_str *vis;
@@ -163,11 +163,11 @@ emulate_inout(struct vmctx *ctx, int vcpu, struct vm_exit *vmexit, int strict)
}
error = vm_copy_setup(ctx, vcpu, &vis->paging, gla,
- bytes, prot, iov, nitems(iov));
- if (error == -1) {
+ bytes, prot, iov, nitems(iov), &fault);
+ if (error) {
retval = -1; /* Unrecoverable error */
break;
- } else if (error == 1) {
+ } else if (fault) {
retval = 0; /* Resume guest to handle fault */
break;
}
diff --git a/usr/src/cmd/bhyve/inout.h b/usr/src/cmd/bhyve/inout.h
index 0d4046bd61..362b1221b4 100644
--- a/usr/src/cmd/bhyve/inout.h
+++ b/usr/src/cmd/bhyve/inout.h
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/inout.h 269094 2014-07-25 20:18:35Z neel $
+ * $FreeBSD$
*/
/*
* This file and its contents are supplied under the terms of the
diff --git a/usr/src/cmd/bhyve/ioapic.c b/usr/src/cmd/bhyve/ioapic.c
index 86ff5c6580..0ad69d96a4 100644
--- a/usr/src/cmd/bhyve/ioapic.c
+++ b/usr/src/cmd/bhyve/ioapic.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2014 Advanced Computing Technologies LLC
+ * Copyright (c) 2014 Hudson River Trading LLC
* Written by: John H. Baldwin <jhb@FreeBSD.org>
* All rights reserved.
*
@@ -26,7 +26,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/usr.sbin/bhyve/ioapic.c 261268 2014-01-29 14:56:48Z jhb $");
+__FBSDID("$FreeBSD$");
#include <sys/types.h>
diff --git a/usr/src/cmd/bhyve/ioapic.h b/usr/src/cmd/bhyve/ioapic.h
index 789f90fea9..efdd3c67a4 100644
--- a/usr/src/cmd/bhyve/ioapic.h
+++ b/usr/src/cmd/bhyve/ioapic.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2014 Advanced Computing Technologies LLC
+ * Copyright (c) 2014 Hudson River Trading LLC
* Written by: John H. Baldwin <jhb@FreeBSD.org>
* All rights reserved.
*
@@ -24,7 +24,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/ioapic.h 261268 2014-01-29 14:56:48Z jhb $
+ * $FreeBSD$
*/
#ifndef _IOAPIC_H_
diff --git a/usr/src/cmd/bhyve/mem.c b/usr/src/cmd/bhyve/mem.c
index a153a8e960..2a9f430c82 100644
--- a/usr/src/cmd/bhyve/mem.c
+++ b/usr/src/cmd/bhyve/mem.c
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/mem.c 269700 2014-08-08 03:49:01Z neel $
+ * $FreeBSD$
*/
/*
@@ -33,7 +33,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/usr.sbin/bhyve/mem.c 269700 2014-08-08 03:49:01Z neel $");
+__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/tree.h>
diff --git a/usr/src/cmd/bhyve/mem.h b/usr/src/cmd/bhyve/mem.h
index 09cf56b72e..f671eaedf7 100644
--- a/usr/src/cmd/bhyve/mem.h
+++ b/usr/src/cmd/bhyve/mem.h
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/mem.h 269700 2014-08-08 03:49:01Z neel $
+ * $FreeBSD$
*/
#ifndef _MEM_H_
diff --git a/usr/src/cmd/bhyve/mevent.c b/usr/src/cmd/bhyve/mevent.c
new file mode 100644
index 0000000000..adc047db71
--- /dev/null
+++ b/usr/src/cmd/bhyve/mevent.c
@@ -0,0 +1,478 @@
+/*-
+ * Copyright (c) 2011 NetApp, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Micro event library for FreeBSD, designed for a single i/o thread
+ * using kqueue, and having events be persistent by default.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#ifndef WITHOUT_CAPSICUM
+#include <sys/capsicum.h>
+#endif
+#include <sys/event.h>
+#include <sys/time.h>
+
+#include <pthread.h>
+#include <pthread_np.h>
+
+#include "mevent.h"
+
+#define MEVENT_MAX 64
+
+#define MEV_ADD 1
+#define MEV_ENABLE 2
+#define MEV_DISABLE 3
+#define MEV_DEL_PENDING 4
+
+extern char *vmname;
+
+static pthread_t mevent_tid;
+static int mevent_timid = 43;
+static int mevent_pipefd[2];
+static pthread_mutex_t mevent_lmutex = PTHREAD_MUTEX_INITIALIZER;
+
+struct mevent {
+ void (*me_func)(int, enum ev_type, void *);
+#define me_msecs me_fd
+ int me_fd;
+ int me_timid;
+ enum ev_type me_type;
+ void *me_param;
+ int me_cq;
+ int me_state;
+ int me_closefd;
+ LIST_ENTRY(mevent) me_list;
+};
+
+static LIST_HEAD(listhead, mevent) global_head, change_head;
+
+static void
+mevent_qlock(void)
+{
+ pthread_mutex_lock(&mevent_lmutex);
+}
+
+static void
+mevent_qunlock(void)
+{
+ pthread_mutex_unlock(&mevent_lmutex);
+}
+
+static void
+mevent_pipe_read(int fd, enum ev_type type, void *param)
+{
+ char buf[MEVENT_MAX];
+ int status;
+
+ /*
+ * Drain the pipe read side. The fd is non-blocking so this is
+ * safe to do.
+ */
+ do {
+ status = read(fd, buf, sizeof(buf));
+ } while (status == MEVENT_MAX);
+}
+
+static void
+mevent_notify(void)
+{
+ char c;
+
+ /*
+ * If calling from outside the i/o thread, write a byte on the
+ * pipe to force the i/o thread to exit the blocking kevent call.
+ */
+ if (mevent_pipefd[1] != 0 && pthread_self() != mevent_tid) {
+ write(mevent_pipefd[1], &c, 1);
+ }
+}
+
+static int
+mevent_kq_filter(struct mevent *mevp)
+{
+ int retval;
+
+ retval = 0;
+
+ if (mevp->me_type == EVF_READ)
+ retval = EVFILT_READ;
+
+ if (mevp->me_type == EVF_WRITE)
+ retval = EVFILT_WRITE;
+
+ if (mevp->me_type == EVF_TIMER)
+ retval = EVFILT_TIMER;
+
+ if (mevp->me_type == EVF_SIGNAL)
+ retval = EVFILT_SIGNAL;
+
+ return (retval);
+}
+
+static int
+mevent_kq_flags(struct mevent *mevp)
+{
+ int ret;
+
+ switch (mevp->me_state) {
+ case MEV_ADD:
+ ret = EV_ADD; /* implicitly enabled */
+ break;
+ case MEV_ENABLE:
+ ret = EV_ENABLE;
+ break;
+ case MEV_DISABLE:
+ ret = EV_DISABLE;
+ break;
+ case MEV_DEL_PENDING:
+ ret = EV_DELETE;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ return (ret);
+}
+
+static int
+mevent_kq_fflags(struct mevent *mevp)
+{
+ /* XXX nothing yet, perhaps EV_EOF for reads ? */
+ return (0);
+}
+
+static int
+mevent_build(int mfd, struct kevent *kev)
+{
+ struct mevent *mevp, *tmpp;
+ int i;
+
+ i = 0;
+
+ mevent_qlock();
+
+ LIST_FOREACH_SAFE(mevp, &change_head, me_list, tmpp) {
+ if (mevp->me_closefd) {
+ /*
+ * A close of the file descriptor will remove the
+ * event
+ */
+ close(mevp->me_fd);
+ } else {
+ if (mevp->me_type == EVF_TIMER) {
+ kev[i].ident = mevp->me_timid;
+ kev[i].data = mevp->me_msecs;
+ } else {
+ kev[i].ident = mevp->me_fd;
+ kev[i].data = 0;
+ }
+ kev[i].filter = mevent_kq_filter(mevp);
+ kev[i].flags = mevent_kq_flags(mevp);
+ kev[i].fflags = mevent_kq_fflags(mevp);
+ kev[i].udata = mevp;
+ i++;
+ }
+
+ mevp->me_cq = 0;
+ LIST_REMOVE(mevp, me_list);
+
+ if (mevp->me_state == MEV_DEL_PENDING) {
+ free(mevp);
+ } else {
+ LIST_INSERT_HEAD(&global_head, mevp, me_list);
+ }
+
+ assert(i < MEVENT_MAX);
+ }
+
+ mevent_qunlock();
+
+ return (i);
+}
+
+static void
+mevent_handle(struct kevent *kev, int numev)
+{
+ struct mevent *mevp;
+ int i;
+
+ for (i = 0; i < numev; i++) {
+ mevp = kev[i].udata;
+
+ /* XXX check for EV_ERROR ? */
+
+ (*mevp->me_func)(mevp->me_fd, mevp->me_type, mevp->me_param);
+ }
+}
+
+struct mevent *
+mevent_add(int tfd, enum ev_type type,
+ void (*func)(int, enum ev_type, void *), void *param)
+{
+ struct mevent *lp, *mevp;
+
+ if (tfd < 0 || func == NULL) {
+ return (NULL);
+ }
+
+ mevp = NULL;
+
+ mevent_qlock();
+
+ /*
+ * Verify that the fd/type tuple is not present in any list
+ */
+ LIST_FOREACH(lp, &global_head, me_list) {
+ if (type != EVF_TIMER && lp->me_fd == tfd &&
+ lp->me_type == type) {
+ goto exit;
+ }
+ }
+
+ LIST_FOREACH(lp, &change_head, me_list) {
+ if (type != EVF_TIMER && lp->me_fd == tfd &&
+ lp->me_type == type) {
+ goto exit;
+ }
+ }
+
+ /*
+ * Allocate an entry, populate it, and add it to the change list.
+ */
+ mevp = calloc(1, sizeof(struct mevent));
+ if (mevp == NULL) {
+ goto exit;
+ }
+
+ if (type == EVF_TIMER) {
+ mevp->me_msecs = tfd;
+ mevp->me_timid = mevent_timid++;
+ } else
+ mevp->me_fd = tfd;
+ mevp->me_type = type;
+ mevp->me_func = func;
+ mevp->me_param = param;
+
+ LIST_INSERT_HEAD(&change_head, mevp, me_list);
+ mevp->me_cq = 1;
+ mevp->me_state = MEV_ADD;
+ mevent_notify();
+
+exit:
+ mevent_qunlock();
+
+ return (mevp);
+}
+
+static int
+mevent_update(struct mevent *evp, int newstate)
+{
+ /*
+ * It's not possible to enable/disable a deleted event
+ */
+ if (evp->me_state == MEV_DEL_PENDING)
+ return (EINVAL);
+
+ /*
+ * No update needed if state isn't changing
+ */
+ if (evp->me_state == newstate)
+ return (0);
+
+ mevent_qlock();
+
+ evp->me_state = newstate;
+
+ /*
+ * Place the entry onto the changed list if not already there.
+ */
+ if (evp->me_cq == 0) {
+ evp->me_cq = 1;
+ LIST_REMOVE(evp, me_list);
+ LIST_INSERT_HEAD(&change_head, evp, me_list);
+ mevent_notify();
+ }
+
+ mevent_qunlock();
+
+ return (0);
+}
+
+int
+mevent_enable(struct mevent *evp)
+{
+
+ return (mevent_update(evp, MEV_ENABLE));
+}
+
+int
+mevent_disable(struct mevent *evp)
+{
+
+ return (mevent_update(evp, MEV_DISABLE));
+}
+
+static int
+mevent_delete_event(struct mevent *evp, int closefd)
+{
+ mevent_qlock();
+
+ /*
+ * Place the entry onto the changed list if not already there, and
+ * mark as to be deleted.
+ */
+ if (evp->me_cq == 0) {
+ evp->me_cq = 1;
+ LIST_REMOVE(evp, me_list);
+ LIST_INSERT_HEAD(&change_head, evp, me_list);
+ mevent_notify();
+ }
+ evp->me_state = MEV_DEL_PENDING;
+
+ if (closefd)
+ evp->me_closefd = 1;
+
+ mevent_qunlock();
+
+ return (0);
+}
+
+int
+mevent_delete(struct mevent *evp)
+{
+
+ return (mevent_delete_event(evp, 0));
+}
+
+int
+mevent_delete_close(struct mevent *evp)
+{
+
+ return (mevent_delete_event(evp, 1));
+}
+
+static void
+mevent_set_name(void)
+{
+
+ pthread_set_name_np(mevent_tid, "mevent");
+}
+
+void
+mevent_dispatch(void)
+{
+ struct kevent changelist[MEVENT_MAX];
+ struct kevent eventlist[MEVENT_MAX];
+ struct mevent *pipev;
+ int mfd;
+ int numev;
+ int ret;
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_t rights;
+#endif
+
+ mevent_tid = pthread_self();
+ mevent_set_name();
+
+ mfd = kqueue();
+ assert(mfd > 0);
+
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_init(&rights, CAP_KQUEUE);
+ if (cap_rights_limit(mfd, &rights) == -1 && errno != ENOSYS)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+#endif
+
+ /*
+ * Open the pipe that will be used for other threads to force
+ * the blocking kqueue call to exit by writing to it. Set the
+ * descriptor to non-blocking.
+ */
+ ret = pipe(mevent_pipefd);
+ if (ret < 0) {
+ perror("pipe");
+ exit(0);
+ }
+
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_init(&rights, CAP_EVENT, CAP_READ, CAP_WRITE);
+ if (cap_rights_limit(mevent_pipefd[0], &rights) == -1 && errno != ENOSYS)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+ if (cap_rights_limit(mevent_pipefd[1], &rights) == -1 && errno != ENOSYS)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+#endif
+
+ /*
+ * Add internal event handler for the pipe write fd
+ */
+ pipev = mevent_add(mevent_pipefd[0], EVF_READ, mevent_pipe_read, NULL);
+ assert(pipev != NULL);
+
+ for (;;) {
+ /*
+ * Build changelist if required.
+ * XXX the changelist can be put into the blocking call
+ * to eliminate the extra syscall. Currently better for
+ * debug.
+ */
+ numev = mevent_build(mfd, changelist);
+ if (numev) {
+ ret = kevent(mfd, changelist, numev, NULL, 0, NULL);
+ if (ret == -1) {
+ perror("Error return from kevent change");
+ }
+ }
+
+ /*
+ * Block awaiting events
+ */
+ ret = kevent(mfd, NULL, 0, eventlist, MEVENT_MAX, NULL);
+ if (ret == -1 && errno != EINTR) {
+ perror("Error return from kevent monitor");
+ }
+
+ /*
+ * Handle reported events
+ */
+ mevent_handle(eventlist, ret);
+ }
+}
diff --git a/usr/src/cmd/bhyve/mevent.h b/usr/src/cmd/bhyve/mevent.h
new file mode 100644
index 0000000000..d6a59c61b3
--- /dev/null
+++ b/usr/src/cmd/bhyve/mevent.h
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 2011 NetApp, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MEVENT_H_
+#define _MEVENT_H_
+
+enum ev_type {
+ EVF_READ,
+ EVF_WRITE,
+ EVF_TIMER,
+ EVF_SIGNAL
+};
+
+struct mevent;
+
+struct mevent *mevent_add(int fd, enum ev_type type,
+ void (*func)(int, enum ev_type, void *),
+ void *param);
+int mevent_enable(struct mevent *evp);
+int mevent_disable(struct mevent *evp);
+int mevent_delete(struct mevent *evp);
+int mevent_delete_close(struct mevent *evp);
+
+void mevent_dispatch(void);
+
+#endif /* _MEVENT_H_ */
diff --git a/usr/src/cmd/bhyve/mevent_test.c b/usr/src/cmd/bhyve/mevent_test.c
new file mode 100644
index 0000000000..9c68ff7874
--- /dev/null
+++ b/usr/src/cmd/bhyve/mevent_test.c
@@ -0,0 +1,256 @@
+/*-
+ * Copyright (c) 2011 NetApp, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Test program for the micro event library. Set up a simple TCP echo
+ * service.
+ *
+ * cc mevent_test.c mevent.c -lpthread
+ */
+
+#include <sys/types.h>
+#include <sys/stdint.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <machine/cpufunc.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#include "mevent.h"
+
+#define TEST_PORT 4321
+
+static pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t accept_condvar = PTHREAD_COND_INITIALIZER;
+
+static struct mevent *tevp;
+
+char *vmname = "test vm";
+
+
+#define MEVENT_ECHO
+
+/* Number of timer events to capture */
+#define TEVSZ 4096
+uint64_t tevbuf[TEVSZ];
+
+static void
+timer_print(void)
+{
+ uint64_t min, max, diff, sum, tsc_freq;
+ size_t len;
+ int j;
+
+ min = UINT64_MAX;
+ max = 0;
+ sum = 0;
+
+ len = sizeof(tsc_freq);
+ sysctlbyname("machdep.tsc_freq", &tsc_freq, &len, NULL, 0);
+
+ for (j = 1; j < TEVSZ; j++) {
+ /* Convert a tsc diff into microseconds */
+ diff = (tevbuf[j] - tevbuf[j-1]) * 1000000 / tsc_freq;
+ sum += diff;
+ if (min > diff)
+ min = diff;
+ if (max < diff)
+ max = diff;
+ }
+
+ printf("timers done: usecs, min %ld, max %ld, mean %ld\n", min, max,
+ sum/(TEVSZ - 1));
+}
+
+static void
+timer_callback(int fd, enum ev_type type, void *param)
+{
+ static int i;
+
+ if (i >= TEVSZ)
+ abort();
+
+ tevbuf[i++] = rdtsc();
+
+ if (i == TEVSZ) {
+ mevent_delete(tevp);
+ timer_print();
+ }
+}
+
+
+#ifdef MEVENT_ECHO
+struct esync {
+ pthread_mutex_t e_mt;
+ pthread_cond_t e_cond;
+};
+
+static void
+echoer_callback(int fd, enum ev_type type, void *param)
+{
+ struct esync *sync = param;
+
+ pthread_mutex_lock(&sync->e_mt);
+ pthread_cond_signal(&sync->e_cond);
+ pthread_mutex_unlock(&sync->e_mt);
+}
+
+static void *
+echoer(void *param)
+{
+ struct esync sync;
+ struct mevent *mev;
+ char buf[128];
+ int fd = (int)(uintptr_t) param;
+ int len;
+
+ pthread_mutex_init(&sync.e_mt, NULL);
+ pthread_cond_init(&sync.e_cond, NULL);
+
+ pthread_mutex_lock(&sync.e_mt);
+
+ mev = mevent_add(fd, EVF_READ, echoer_callback, &sync);
+ if (mev == NULL) {
+ printf("Could not allocate echoer event\n");
+ exit(1);
+ }
+
+ while (!pthread_cond_wait(&sync.e_cond, &sync.e_mt)) {
+ len = read(fd, buf, sizeof(buf));
+ if (len > 0) {
+ write(fd, buf, len);
+ write(0, buf, len);
+ } else {
+ break;
+ }
+ }
+
+ mevent_delete_close(mev);
+
+ pthread_mutex_unlock(&sync.e_mt);
+ pthread_mutex_destroy(&sync.e_mt);
+ pthread_cond_destroy(&sync.e_cond);
+
+ return (NULL);
+}
+
+#else
+
+static void *
+echoer(void *param)
+{
+ char buf[128];
+ int fd = (int)(uintptr_t) param;
+ int len;
+
+ while ((len = read(fd, buf, sizeof(buf))) > 0) {
+ write(1, buf, len);
+ }
+
+ return (NULL);
+}
+#endif /* MEVENT_ECHO */
+
+static void
+acceptor_callback(int fd, enum ev_type type, void *param)
+{
+ pthread_mutex_lock(&accept_mutex);
+ pthread_cond_signal(&accept_condvar);
+ pthread_mutex_unlock(&accept_mutex);
+}
+
+static void *
+acceptor(void *param)
+{
+ struct sockaddr_in sin;
+ pthread_t tid;
+ int news;
+ int s;
+ static int first;
+
+ if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ perror("socket");
+ exit(1);
+ }
+
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ sin.sin_port = htons(TEST_PORT);
+
+ if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ perror("bind");
+ exit(1);
+ }
+
+ if (listen(s, 1) < 0) {
+ perror("listen");
+ exit(1);
+ }
+
+ (void) mevent_add(s, EVF_READ, acceptor_callback, NULL);
+
+ pthread_mutex_lock(&accept_mutex);
+
+ while (!pthread_cond_wait(&accept_condvar, &accept_mutex)) {
+ news = accept(s, NULL, NULL);
+ if (news < 0) {
+ perror("accept error");
+ } else {
+ static int first = 1;
+
+ if (first) {
+ /*
+ * Start a timer
+ */
+ first = 0;
+ tevp = mevent_add(1, EVF_TIMER, timer_callback,
+ NULL);
+ }
+
+ printf("incoming connection, spawning thread\n");
+ pthread_create(&tid, NULL, echoer,
+ (void *)(uintptr_t)news);
+ }
+ }
+
+ return (NULL);
+}
+
+main()
+{
+ pthread_t tid;
+
+ pthread_create(&tid, NULL, acceptor, NULL);
+
+ mevent_dispatch();
+}
diff --git a/usr/src/cmd/bhyve/mptbl.c b/usr/src/cmd/bhyve/mptbl.c
index 9d03765c7a..996cf1d24e 100644
--- a/usr/src/cmd/bhyve/mptbl.c
+++ b/usr/src/cmd/bhyve/mptbl.c
@@ -23,11 +23,11 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/mptbl.c 266125 2014-05-15 14:16:55Z jhb $
+ * $FreeBSD$
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/usr.sbin/bhyve/mptbl.c 266125 2014-05-15 14:16:55Z jhb $");
+__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/errno.h>
diff --git a/usr/src/cmd/bhyve/mptbl.h b/usr/src/cmd/bhyve/mptbl.h
index d78ea6da09..e9e1c426ea 100644
--- a/usr/src/cmd/bhyve/mptbl.h
+++ b/usr/src/cmd/bhyve/mptbl.h
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/mptbl.h 257423 2013-10-31 05:44:45Z neel $
+ * $FreeBSD$
*/
#ifndef _MPTBL_H_
diff --git a/usr/src/cmd/bhyve/pci_ahci.c b/usr/src/cmd/bhyve/pci_ahci.c
index 9c8d6d429a..7e1a387963 100644
--- a/usr/src/cmd/bhyve/pci_ahci.c
+++ b/usr/src/cmd/bhyve/pci_ahci.c
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2013 Zhixiang Yu <zcore@freebsd.org>
+ * Copyright (c) 2015-2016 Alexander Motin <mav@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -23,11 +24,11 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/pci_ahci.c 274045 2014-11-03 12:55:31Z tychon $
+ * $FreeBSD$
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/usr.sbin/bhyve/pci_ahci.c 274045 2014-11-03 12:55:31Z tychon $");
+__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/linker_set.h>
@@ -50,13 +51,15 @@ __FBSDID("$FreeBSD: head/usr.sbin/bhyve/pci_ahci.c 274045 2014-11-03 12:55:31Z t
#include <pthread.h>
#include <pthread_np.h>
#include <inttypes.h>
+#include <md5.h>
#include "bhyverun.h"
#include "pci_emul.h"
#include "ahci.h"
#include "block_if.h"
-#define MAX_PORTS 6 /* Intel ICH8 AHCI supports 6 ports */
+#define DEF_PORTS 6 /* Intel ICH8 AHCI supports 6 ports */
+#define MAX_PORTS 32 /* AHCI supports 32 ports */
#define PxSIG_ATA 0x00000101 /* ATA drive */
#define PxSIG_ATAPI 0xeb140101 /* ATAPI drive */
@@ -86,6 +89,7 @@ enum sata_fis_type {
#define READ_TOC 0x43
#define GET_EVENT_STATUS_NOTIFICATION 0x4A
#define MODE_SENSE_10 0x5A
+#define REPORT_LUNS 0xA0
#define READ_12 0xA8
#define READ_CD 0xBE
@@ -122,7 +126,7 @@ struct ahci_ioreq {
uint32_t len;
uint32_t done;
int slot;
- int prdtl;
+ int more;
};
struct ahci_port {
@@ -130,12 +134,17 @@ struct ahci_port {
struct pci_ahci_softc *pr_sc;
uint8_t *cmd_lst;
uint8_t *rfis;
+ char ident[20 + 1];
+ int port;
int atapi;
int reset;
+ int waitforclear;
int mult_sectors;
uint8_t xfermode;
+ uint8_t err_cfis[20];
uint8_t sense_key;
uint8_t asc;
+ u_int ccs;
uint32_t pending;
uint32_t clb;
@@ -200,6 +209,8 @@ struct pci_ahci_softc {
};
#define ahci_ctx(sc) ((sc)->asc_pi->pi_vmctx)
+static void ahci_handle_port(struct ahci_port *p);
+
static inline void lba_to_msf(uint8_t *buf, int lba)
{
lba += 150;
@@ -209,47 +220,95 @@ static inline void lba_to_msf(uint8_t *buf, int lba)
}
/*
- * generate HBA intr depending on whether or not ports within
- * the controller have an interrupt pending.
+ * Generate HBA interrupts on global IS register write.
*/
static void
-ahci_generate_intr(struct pci_ahci_softc *sc)
+ahci_generate_intr(struct pci_ahci_softc *sc, uint32_t mask)
{
- struct pci_devinst *pi;
- int i;
-
- pi = sc->asc_pi;
+ struct pci_devinst *pi = sc->asc_pi;
+ struct ahci_port *p;
+ int i, nmsg;
+ uint32_t mmask;
+ /* Update global IS from PxIS/PxIE. */
for (i = 0; i < sc->ports; i++) {
- struct ahci_port *pr;
- pr = &sc->port[i];
- if (pr->is & pr->ie)
+ p = &sc->port[i];
+ if (p->is & p->ie)
sc->is |= (1 << i);
}
+ DPRINTF("%s(%08x) %08x\n", __func__, mask, sc->is);
+
+ /* If there is nothing enabled -- clear legacy interrupt and exit. */
+ if (sc->is == 0 || (sc->ghc & AHCI_GHC_IE) == 0) {
+ if (sc->lintr) {
+ pci_lintr_deassert(pi);
+ sc->lintr = 0;
+ }
+ return;
+ }
- DPRINTF("%s %x\n", __func__, sc->is);
-
- if (sc->is && (sc->ghc & AHCI_GHC_IE)) {
- if (pci_msi_enabled(pi)) {
- /*
- * Generate an MSI interrupt on every edge
- */
- pci_generate_msi(pi, 0);
- } else if (!sc->lintr) {
- /*
- * Only generate a pin-based interrupt if one wasn't
- * in progress
- */
+ /* If there is anything and no MSI -- assert legacy interrupt. */
+ nmsg = pci_msi_maxmsgnum(pi);
+ if (nmsg == 0) {
+ if (!sc->lintr) {
sc->lintr = 1;
pci_lintr_assert(pi);
}
- } else if (sc->lintr) {
- /*
- * No interrupts: deassert pin-based signal if it had
- * been asserted
- */
- pci_lintr_deassert(pi);
- sc->lintr = 0;
+ return;
+ }
+
+ /* Assert respective MSIs for ports that were touched. */
+ for (i = 0; i < nmsg; i++) {
+ if (sc->ports <= nmsg || i < nmsg - 1)
+ mmask = 1 << i;
+ else
+ mmask = 0xffffffff << i;
+ if (sc->is & mask && mmask & mask)
+ pci_generate_msi(pi, i);
+ }
+}
+
+/*
+ * Generate HBA interrupt on specific port event.
+ */
+static void
+ahci_port_intr(struct ahci_port *p)
+{
+ struct pci_ahci_softc *sc = p->pr_sc;
+ struct pci_devinst *pi = sc->asc_pi;
+ int nmsg;
+
+ DPRINTF("%s(%d) %08x/%08x %08x\n", __func__,
+ p->port, p->is, p->ie, sc->is);
+
+ /* If there is nothing enabled -- we are done. */
+ if ((p->is & p->ie) == 0)
+ return;
+
+ /* In case of non-shared MSI always generate interrupt. */
+ nmsg = pci_msi_maxmsgnum(pi);
+ if (sc->ports <= nmsg || p->port < nmsg - 1) {
+ sc->is |= (1 << p->port);
+ if ((sc->ghc & AHCI_GHC_IE) == 0)
+ return;
+ pci_generate_msi(pi, p->port);
+ return;
+ }
+
+ /* If IS for this port is already set -- do nothing. */
+ if (sc->is & (1 << p->port))
+ return;
+
+ sc->is |= (1 << p->port);
+
+ /* If interrupts are enabled -- generate one. */
+ if ((sc->ghc & AHCI_GHC_IE) == 0)
+ return;
+ if (nmsg > 0) {
+ pci_generate_msi(pi, nmsg - 1);
+ } else if (!sc->lintr) {
+ sc->lintr = 1;
+ pci_lintr_assert(pi);
}
}
@@ -265,26 +324,32 @@ ahci_write_fis(struct ahci_port *p, enum sata_fis_type ft, uint8_t *fis)
case FIS_TYPE_REGD2H:
offset = 0x40;
len = 20;
- irq = AHCI_P_IX_DHR;
+ irq = (fis[1] & (1 << 6)) ? AHCI_P_IX_DHR : 0;
break;
case FIS_TYPE_SETDEVBITS:
offset = 0x58;
len = 8;
- irq = AHCI_P_IX_SDB;
+ irq = (fis[1] & (1 << 6)) ? AHCI_P_IX_SDB : 0;
break;
case FIS_TYPE_PIOSETUP:
offset = 0x20;
len = 20;
- irq = 0;
+ irq = (fis[1] & (1 << 6)) ? AHCI_P_IX_PS : 0;
break;
default:
WPRINTF("unsupported fis type %d\n", ft);
return;
}
+ if (fis[2] & ATA_S_ERROR) {
+ p->waitforclear = 1;
+ irq |= AHCI_P_IX_TFE;
+ }
memcpy(p->rfis + offset, fis, len);
if (irq) {
- p->is |= irq;
- ahci_generate_intr(p->pr_sc);
+ if (~p->is & irq) {
+ p->is |= irq;
+ ahci_port_intr(p);
+ }
}
}
@@ -299,19 +364,29 @@ ahci_write_fis_piosetup(struct ahci_port *p)
}
static void
-ahci_write_fis_sdb(struct ahci_port *p, int slot, uint32_t tfd)
+ahci_write_fis_sdb(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t tfd)
{
uint8_t fis[8];
uint8_t error;
error = (tfd >> 8) & 0xff;
+ tfd &= 0x77;
memset(fis, 0, sizeof(fis));
- fis[0] = error;
- fis[2] = tfd & 0x77;
- *(uint32_t *)(fis + 4) = (1 << slot);
- if (fis[2] & ATA_S_ERROR)
- p->is |= AHCI_P_IX_TFE;
- p->tfd = tfd;
+ fis[0] = FIS_TYPE_SETDEVBITS;
+ fis[1] = (1 << 6);
+ fis[2] = tfd;
+ fis[3] = error;
+ if (fis[2] & ATA_S_ERROR) {
+ p->err_cfis[0] = slot;
+ p->err_cfis[2] = tfd;
+ p->err_cfis[3] = error;
+ memcpy(&p->err_cfis[4], cfis + 4, 16);
+ } else {
+ *(uint32_t *)(fis + 4) = (1 << slot);
+ p->sact &= ~(1 << slot);
+ }
+ p->tfd &= ~0x77;
+ p->tfd |= tfd;
ahci_write_fis(p, FIS_TYPE_SETDEVBITS, fis);
}
@@ -337,15 +412,33 @@ ahci_write_fis_d2h(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t tfd)
fis[11] = cfis[11];
fis[12] = cfis[12];
fis[13] = cfis[13];
- if (fis[2] & ATA_S_ERROR)
- p->is |= AHCI_P_IX_TFE;
- else
+ if (fis[2] & ATA_S_ERROR) {
+ p->err_cfis[0] = 0x80;
+ p->err_cfis[2] = tfd & 0xff;
+ p->err_cfis[3] = error;
+ memcpy(&p->err_cfis[4], cfis + 4, 16);
+ } else
p->ci &= ~(1 << slot);
p->tfd = tfd;
ahci_write_fis(p, FIS_TYPE_REGD2H, fis);
}
static void
+ahci_write_fis_d2h_ncq(struct ahci_port *p, int slot)
+{
+ uint8_t fis[20];
+
+ p->tfd = ATA_S_READY | ATA_S_DSC;
+ memset(fis, 0, sizeof(fis));
+ fis[0] = FIS_TYPE_REGD2H;
+ fis[1] = 0; /* No interrupt */
+ fis[2] = p->tfd; /* Status */
+ fis[3] = 0; /* No error */
+ p->ci &= ~(1 << slot);
+ ahci_write_fis(p, FIS_TYPE_REGD2H, fis);
+}
+
+static void
ahci_write_reset_fis_d2h(struct ahci_port *p)
{
uint8_t fis[20];
@@ -372,9 +465,11 @@ ahci_check_stopped(struct ahci_port *p)
*/
if (!(p->cmd & AHCI_P_CMD_ST)) {
if (p->pending == 0) {
+ p->ccs = 0;
p->cmd &= ~(AHCI_P_CMD_CR | AHCI_P_CMD_CCS_MASK);
p->ci = 0;
p->sact = 0;
+ p->waitforclear = 0;
}
}
}
@@ -385,7 +480,6 @@ ahci_port_stop(struct ahci_port *p)
struct ahci_ioreq *aior;
uint8_t *cfis;
int slot;
- int ncq;
int error;
assert(pthread_mutex_isowned_np(&p->pr_sc->mtx));
@@ -401,11 +495,9 @@ ahci_port_stop(struct ahci_port *p)
slot = aior->slot;
cfis = aior->cfis;
if (cfis[2] == ATA_WRITE_FPDMA_QUEUED ||
- cfis[2] == ATA_READ_FPDMA_QUEUED)
- ncq = 1;
-
- if (ncq)
- p->sact &= ~(1 << slot);
+ cfis[2] == ATA_READ_FPDMA_QUEUED ||
+ cfis[2] == ATA_SEND_FPDMA_QUEUED)
+ p->sact &= ~(1 << slot); /* NCQ */
else
p->ci &= ~(1 << slot);
@@ -431,7 +523,6 @@ ahci_port_stop(struct ahci_port *p)
static void
ahci_port_reset(struct ahci_port *pr)
{
- pr->sctl = 0;
pr->serr = 0;
pr->sact = 0;
pr->xfermode = ATA_UDMA6;
@@ -443,8 +534,11 @@ ahci_port_reset(struct ahci_port *pr)
pr->tfd = 0x7F;
return;
}
- pr->ssts = ATA_SS_DET_PHY_ONLINE | ATA_SS_SPD_GEN2 |
- ATA_SS_IPM_ACTIVE;
+ pr->ssts = ATA_SS_DET_PHY_ONLINE | ATA_SS_IPM_ACTIVE;
+ if (pr->sctl & ATA_SC_SPD_MASK)
+ pr->ssts |= (pr->sctl & ATA_SC_SPD_MASK);
+ else
+ pr->ssts |= ATA_SS_SPD_GEN3;
pr->tfd = (1 << 8) | ATA_S_DSC | ATA_S_DMA;
if (!pr->atapi) {
pr->sig = PxSIG_ATA;
@@ -470,6 +564,10 @@ ahci_reset(struct pci_ahci_softc *sc)
for (i = 0; i < sc->ports; i++) {
sc->port[i].ie = 0;
sc->port[i].is = 0;
+ sc->port[i].cmd = (AHCI_P_CMD_SUD | AHCI_P_CMD_POD);
+ if (sc->port[i].bctx)
+ sc->port[i].cmd |= AHCI_P_CMD_CPS;
+ sc->port[i].sctl = 0;
ahci_port_reset(&sc->port[i]);
}
}
@@ -500,32 +598,87 @@ atapi_string(uint8_t *dest, const char *src, int len)
}
}
+/*
+ * Build up the iovec based on the PRDT, 'done' and 'len'.
+ */
+static void
+ahci_build_iov(struct ahci_port *p, struct ahci_ioreq *aior,
+ struct ahci_prdt_entry *prdt, uint16_t prdtl)
+{
+ struct blockif_req *breq = &aior->io_req;
+ int i, j, skip, todo, left, extra;
+ uint32_t dbcsz;
+
+ /* Copy part of PRDT between 'done' and 'len' bytes into the iov. */
+ skip = aior->done;
+ left = aior->len - aior->done;
+ todo = 0;
+ for (i = 0, j = 0; i < prdtl && j < BLOCKIF_IOV_MAX && left > 0;
+ i++, prdt++) {
+ dbcsz = (prdt->dbc & DBCMASK) + 1;
+ /* Skip already done part of the PRDT */
+ if (dbcsz <= skip) {
+ skip -= dbcsz;
+ continue;
+ }
+ dbcsz -= skip;
+ if (dbcsz > left)
+ dbcsz = left;
+ breq->br_iov[j].iov_base = paddr_guest2host(ahci_ctx(p->pr_sc),
+ prdt->dba + skip, dbcsz);
+ breq->br_iov[j].iov_len = dbcsz;
+ todo += dbcsz;
+ left -= dbcsz;
+ skip = 0;
+ j++;
+ }
+
+ /* If we got limited by IOV length, round I/O down to sector size. */
+ if (j == BLOCKIF_IOV_MAX) {
+ extra = todo % blockif_sectsz(p->bctx);
+ todo -= extra;
+ assert(todo > 0);
+ while (extra > 0) {
+ if (breq->br_iov[j - 1].iov_len > extra) {
+ breq->br_iov[j - 1].iov_len -= extra;
+ break;
+ }
+ extra -= breq->br_iov[j - 1].iov_len;
+ j--;
+ }
+ }
+
+ breq->br_iovcnt = j;
+ breq->br_resid = todo;
+ aior->done += todo;
+ aior->more = (aior->done < aior->len && i < prdtl);
+}
+
static void
-ahci_handle_dma(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done,
- int seek)
+ahci_handle_rw(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done)
{
struct ahci_ioreq *aior;
struct blockif_req *breq;
- struct pci_ahci_softc *sc;
struct ahci_prdt_entry *prdt;
struct ahci_cmd_hdr *hdr;
uint64_t lba;
uint32_t len;
- int i, err, iovcnt, ncq, readop;
+ int err, first, ncq, readop;
- sc = p->pr_sc;
prdt = (struct ahci_prdt_entry *)(cfis + 0x80);
hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
ncq = 0;
readop = 1;
+ first = (done == 0);
- prdt += seek;
- if (cfis[2] == ATA_WRITE_DMA || cfis[2] == ATA_WRITE_DMA48 ||
- cfis[2] == ATA_WRITE_FPDMA_QUEUED)
+ if (cfis[2] == ATA_WRITE || cfis[2] == ATA_WRITE48 ||
+ cfis[2] == ATA_WRITE_MUL || cfis[2] == ATA_WRITE_MUL48 ||
+ cfis[2] == ATA_WRITE_DMA || cfis[2] == ATA_WRITE_DMA48 ||
+ cfis[2] == ATA_WRITE_FPDMA_QUEUED)
readop = 0;
if (cfis[2] == ATA_WRITE_FPDMA_QUEUED ||
- cfis[2] == ATA_READ_FPDMA_QUEUED) {
+ cfis[2] == ATA_READ_FPDMA_QUEUED) {
lba = ((uint64_t)cfis[10] << 40) |
((uint64_t)cfis[9] << 32) |
((uint64_t)cfis[8] << 24) |
@@ -536,7 +689,9 @@ ahci_handle_dma(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done,
if (!len)
len = 65536;
ncq = 1;
- } else if (cfis[2] == ATA_READ_DMA48 || cfis[2] == ATA_WRITE_DMA48) {
+ } else if (cfis[2] == ATA_READ48 || cfis[2] == ATA_WRITE48 ||
+ cfis[2] == ATA_READ_MUL48 || cfis[2] == ATA_WRITE_MUL48 ||
+ cfis[2] == ATA_READ_DMA48 || cfis[2] == ATA_WRITE_DMA48) {
lba = ((uint64_t)cfis[10] << 40) |
((uint64_t)cfis[9] << 32) |
((uint64_t)cfis[8] << 24) |
@@ -556,57 +711,33 @@ ahci_handle_dma(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done,
lba *= blockif_sectsz(p->bctx);
len *= blockif_sectsz(p->bctx);
- /*
- * Pull request off free list
- */
+ /* Pull request off free list */
aior = STAILQ_FIRST(&p->iofhd);
assert(aior != NULL);
STAILQ_REMOVE_HEAD(&p->iofhd, io_flist);
+
aior->cfis = cfis;
aior->slot = slot;
aior->len = len;
aior->done = done;
breq = &aior->io_req;
breq->br_offset = lba + done;
- iovcnt = hdr->prdtl - seek;
- if (iovcnt > BLOCKIF_IOV_MAX) {
- aior->prdtl = iovcnt - BLOCKIF_IOV_MAX;
- iovcnt = BLOCKIF_IOV_MAX;
- } else
- aior->prdtl = 0;
- breq->br_iovcnt = iovcnt;
+ ahci_build_iov(p, aior, prdt, hdr->prdtl);
- /*
- * Mark this command in-flight.
- */
+ /* Mark this command in-flight. */
p->pending |= 1 << slot;
- /*
- * Stuff request onto busy list
- */
+ /* Stuff request onto busy list. */
TAILQ_INSERT_HEAD(&p->iobhd, aior, io_blist);
- /*
- * Build up the iovec based on the prdt
- */
- for (i = 0; i < iovcnt; i++) {
- uint32_t dbcsz;
+ if (ncq && first)
+ ahci_write_fis_d2h_ncq(p, slot);
- dbcsz = (prdt->dbc & DBCMASK) + 1;
- breq->br_iov[i].iov_base = paddr_guest2host(ahci_ctx(sc),
- prdt->dba, dbcsz);
- breq->br_iov[i].iov_len = dbcsz;
- aior->done += dbcsz;
- prdt++;
- }
if (readop)
err = blockif_read(p->bctx, breq);
else
err = blockif_write(p->bctx, breq);
assert(err == 0);
-
- if (ncq)
- p->ci &= ~(1 << slot);
}
static void
@@ -626,7 +757,7 @@ ahci_handle_flush(struct ahci_port *p, int slot, uint8_t *cfis)
aior->slot = slot;
aior->len = 0;
aior->done = 0;
- aior->prdtl = 0;
+ aior->more = 0;
breq = &aior->io_req;
/*
@@ -644,6 +775,120 @@ ahci_handle_flush(struct ahci_port *p, int slot, uint8_t *cfis)
}
static inline void
+read_prdt(struct ahci_port *p, int slot, uint8_t *cfis,
+ void *buf, int size)
+{
+ struct ahci_cmd_hdr *hdr;
+ struct ahci_prdt_entry *prdt;
+ void *to;
+ int i, len;
+
+ hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
+ len = size;
+ to = buf;
+ prdt = (struct ahci_prdt_entry *)(cfis + 0x80);
+ for (i = 0; i < hdr->prdtl && len; i++) {
+ uint8_t *ptr;
+ uint32_t dbcsz;
+ int sublen;
+
+ dbcsz = (prdt->dbc & DBCMASK) + 1;
+ ptr = paddr_guest2host(ahci_ctx(p->pr_sc), prdt->dba, dbcsz);
+ sublen = MIN(len, dbcsz);
+ memcpy(to, ptr, sublen);
+ len -= sublen;
+ to += sublen;
+ prdt++;
+ }
+}
+
+static void
+ahci_handle_dsm_trim(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done)
+{
+ struct ahci_ioreq *aior;
+ struct blockif_req *breq;
+ uint8_t *entry;
+ uint64_t elba;
+ uint32_t len, elen;
+ int err, first, ncq;
+ uint8_t buf[512];
+
+ first = (done == 0);
+ if (cfis[2] == ATA_DATA_SET_MANAGEMENT) {
+ len = (uint16_t)cfis[13] << 8 | cfis[12];
+ len *= 512;
+ ncq = 0;
+ } else { /* ATA_SEND_FPDMA_QUEUED */
+ len = (uint16_t)cfis[11] << 8 | cfis[3];
+ len *= 512;
+ ncq = 1;
+ }
+ read_prdt(p, slot, cfis, buf, sizeof(buf));
+
+next:
+ entry = &buf[done];
+ elba = ((uint64_t)entry[5] << 40) |
+ ((uint64_t)entry[4] << 32) |
+ ((uint64_t)entry[3] << 24) |
+ ((uint64_t)entry[2] << 16) |
+ ((uint64_t)entry[1] << 8) |
+ entry[0];
+ elen = (uint16_t)entry[7] << 8 | entry[6];
+ done += 8;
+ if (elen == 0) {
+ if (done >= len) {
+ if (ncq) {
+ if (first)
+ ahci_write_fis_d2h_ncq(p, slot);
+ ahci_write_fis_sdb(p, slot, cfis,
+ ATA_S_READY | ATA_S_DSC);
+ } else {
+ ahci_write_fis_d2h(p, slot, cfis,
+ ATA_S_READY | ATA_S_DSC);
+ }
+ p->pending &= ~(1 << slot);
+ ahci_check_stopped(p);
+ if (!first)
+ ahci_handle_port(p);
+ return;
+ }
+ goto next;
+ }
+
+ /*
+ * Pull request off free list
+ */
+ aior = STAILQ_FIRST(&p->iofhd);
+ assert(aior != NULL);
+ STAILQ_REMOVE_HEAD(&p->iofhd, io_flist);
+ aior->cfis = cfis;
+ aior->slot = slot;
+ aior->len = len;
+ aior->done = done;
+ aior->more = (len != done);
+
+ breq = &aior->io_req;
+ breq->br_offset = elba * blockif_sectsz(p->bctx);
+ breq->br_resid = elen * blockif_sectsz(p->bctx);
+
+ /*
+ * Mark this command in-flight.
+ */
+ p->pending |= 1 << slot;
+
+ /*
+ * Stuff request onto busy list
+ */
+ TAILQ_INSERT_HEAD(&p->iobhd, aior, io_blist);
+
+ if (ncq && first)
+ ahci_write_fis_d2h_ncq(p, slot);
+
+ err = blockif_delete(p->bctx, breq);
+ assert(err == 0);
+}
+
+static inline void
write_prdt(struct ahci_port *p, int slot, uint8_t *cfis,
void *buf, int size)
{
@@ -663,7 +908,7 @@ write_prdt(struct ahci_port *p, int slot, uint8_t *cfis,
dbcsz = (prdt->dbc & DBCMASK) + 1;
ptr = paddr_guest2host(ahci_ctx(p->pr_sc), prdt->dba, dbcsz);
- sublen = len < dbcsz ? len : dbcsz;
+ sublen = MIN(len, dbcsz);
memcpy(ptr, from, sublen);
len -= sublen;
from += sublen;
@@ -673,88 +918,174 @@ write_prdt(struct ahci_port *p, int slot, uint8_t *cfis,
}
static void
+ahci_checksum(uint8_t *buf, int size)
+{
+ int i;
+ uint8_t sum = 0;
+
+ for (i = 0; i < size - 1; i++)
+ sum += buf[i];
+ buf[size - 1] = 0x100 - sum;
+}
+
+static void
+ahci_handle_read_log(struct ahci_port *p, int slot, uint8_t *cfis)
+{
+ struct ahci_cmd_hdr *hdr;
+ uint32_t buf[128];
+ uint8_t *buf8 = (uint8_t *)buf;
+ uint16_t *buf16 = (uint16_t *)buf;
+
+ hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
+ if (p->atapi || hdr->prdtl == 0 || cfis[5] != 0 ||
+ cfis[9] != 0 || cfis[12] != 1 || cfis[13] != 0) {
+ ahci_write_fis_d2h(p, slot, cfis,
+ (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
+ return;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ if (cfis[4] == 0x00) { /* Log directory */
+ buf16[0x00] = 1; /* Version -- 1 */
+ buf16[0x10] = 1; /* NCQ Command Error Log -- 1 page */
+ buf16[0x13] = 1; /* SATA NCQ Send and Receive Log -- 1 page */
+ } else if (cfis[4] == 0x10) { /* NCQ Command Error Log */
+ memcpy(buf8, p->err_cfis, sizeof(p->err_cfis));
+ ahci_checksum(buf8, sizeof(buf));
+ } else if (cfis[4] == 0x13) { /* SATA NCQ Send and Receive Log */
+ if (blockif_candelete(p->bctx) && !blockif_is_ro(p->bctx)) {
+ buf[0x00] = 1; /* SFQ DSM supported */
+ buf[0x01] = 1; /* SFQ DSM TRIM supported */
+ }
+ } else {
+ ahci_write_fis_d2h(p, slot, cfis,
+ (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
+ return;
+ }
+
+ if (cfis[2] == ATA_READ_LOG_EXT)
+ ahci_write_fis_piosetup(p);
+ write_prdt(p, slot, cfis, (void *)buf, sizeof(buf));
+ ahci_write_fis_d2h(p, slot, cfis, ATA_S_DSC | ATA_S_READY);
+}
+
+static void
handle_identify(struct ahci_port *p, int slot, uint8_t *cfis)
{
struct ahci_cmd_hdr *hdr;
hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
if (p->atapi || hdr->prdtl == 0) {
- p->tfd = (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR;
- p->is |= AHCI_P_IX_TFE;
+ ahci_write_fis_d2h(p, slot, cfis,
+ (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
} else {
uint16_t buf[256];
uint64_t sectors;
+ int sectsz, psectsz, psectoff, candelete, ro;
uint16_t cyl;
uint8_t sech, heads;
- sectors = blockif_size(p->bctx) / blockif_sectsz(p->bctx);
+ ro = blockif_is_ro(p->bctx);
+ candelete = blockif_candelete(p->bctx);
+ sectsz = blockif_sectsz(p->bctx);
+ sectors = blockif_size(p->bctx) / sectsz;
blockif_chs(p->bctx, &cyl, &heads, &sech);
+ blockif_psectsz(p->bctx, &psectsz, &psectoff);
memset(buf, 0, sizeof(buf));
buf[0] = 0x0040;
buf[1] = cyl;
buf[3] = heads;
buf[6] = sech;
- /* TODO emulate different serial? */
- ata_string((uint8_t *)(buf+10), "123456", 20);
+ ata_string((uint8_t *)(buf+10), p->ident, 20);
ata_string((uint8_t *)(buf+23), "001", 8);
ata_string((uint8_t *)(buf+27), "BHYVE SATA DISK", 40);
buf[47] = (0x8000 | 128);
- buf[48] = 0x1;
+ buf[48] = 0;
buf[49] = (1 << 8 | 1 << 9 | 1 << 11);
buf[50] = (1 << 14);
buf[53] = (1 << 1 | 1 << 2);
if (p->mult_sectors)
buf[59] = (0x100 | p->mult_sectors);
- buf[60] = sectors;
- buf[61] = (sectors >> 16);
+ if (sectors <= 0x0fffffff) {
+ buf[60] = sectors;
+ buf[61] = (sectors >> 16);
+ } else {
+ buf[60] = 0xffff;
+ buf[61] = 0x0fff;
+ }
buf[63] = 0x7;
if (p->xfermode & ATA_WDMA0)
buf[63] |= (1 << ((p->xfermode & 7) + 8));
buf[64] = 0x3;
- buf[65] = 100;
- buf[66] = 100;
- buf[67] = 100;
- buf[68] = 100;
+ buf[65] = 120;
+ buf[66] = 120;
+ buf[67] = 120;
+ buf[68] = 120;
+ buf[69] = 0;
buf[75] = 31;
- buf[76] = (1 << 8 | 1 << 2);
- buf[80] = 0x1f0;
+ buf[76] = (ATA_SATA_GEN1 | ATA_SATA_GEN2 | ATA_SATA_GEN3 |
+ ATA_SUPPORT_NCQ);
+ buf[77] = (ATA_SUPPORT_RCVSND_FPDMA_QUEUED |
+ (p->ssts & ATA_SS_SPD_MASK) >> 3);
+ buf[80] = 0x3f0;
buf[81] = 0x28;
- buf[82] = (1 << 5 | 1 << 14);
- buf[83] = (1 << 10 | 1 << 12 | 1 << 13 | 1 << 14);
+ buf[82] = (ATA_SUPPORT_POWERMGT | ATA_SUPPORT_WRITECACHE|
+ ATA_SUPPORT_LOOKAHEAD | ATA_SUPPORT_NOP);
+ buf[83] = (ATA_SUPPORT_ADDRESS48 | ATA_SUPPORT_FLUSHCACHE |
+ ATA_SUPPORT_FLUSHCACHE48 | 1 << 14);
buf[84] = (1 << 14);
- buf[85] = (1 << 5 | 1 << 14);
- buf[86] = (1 << 10 | 1 << 12 | 1 << 13);
+ buf[85] = (ATA_SUPPORT_POWERMGT | ATA_SUPPORT_WRITECACHE|
+ ATA_SUPPORT_LOOKAHEAD | ATA_SUPPORT_NOP);
+ buf[86] = (ATA_SUPPORT_ADDRESS48 | ATA_SUPPORT_FLUSHCACHE |
+ ATA_SUPPORT_FLUSHCACHE48 | 1 << 15);
buf[87] = (1 << 14);
buf[88] = 0x7f;
if (p->xfermode & ATA_UDMA0)
buf[88] |= (1 << ((p->xfermode & 7) + 8));
- buf[93] = (1 | 1 <<14);
buf[100] = sectors;
buf[101] = (sectors >> 16);
buf[102] = (sectors >> 32);
buf[103] = (sectors >> 48);
+ if (candelete && !ro) {
+ buf[69] |= ATA_SUPPORT_RZAT | ATA_SUPPORT_DRAT;
+ buf[105] = 1;
+ buf[169] = ATA_SUPPORT_DSM_TRIM;
+ }
+ buf[106] = 0x4000;
+ buf[209] = 0x4000;
+ if (psectsz > sectsz) {
+ buf[106] |= 0x2000;
+ buf[106] |= ffsl(psectsz / sectsz) - 1;
+ buf[209] |= (psectoff / sectsz);
+ }
+ if (sectsz > 512) {
+ buf[106] |= 0x1000;
+ buf[117] = sectsz / 2;
+ buf[118] = ((sectsz / 2) >> 16);
+ }
+ buf[119] = (ATA_SUPPORT_RWLOGDMAEXT | 1 << 14);
+ buf[120] = (ATA_SUPPORT_RWLOGDMAEXT | 1 << 14);
+ buf[222] = 0x1020;
+ buf[255] = 0x00a5;
+ ahci_checksum((uint8_t *)buf, sizeof(buf));
ahci_write_fis_piosetup(p);
write_prdt(p, slot, cfis, (void *)buf, sizeof(buf));
- p->tfd = ATA_S_DSC | ATA_S_READY;
- p->is |= AHCI_P_IX_DP;
- p->ci &= ~(1 << slot);
+ ahci_write_fis_d2h(p, slot, cfis, ATA_S_DSC | ATA_S_READY);
}
- ahci_generate_intr(p->pr_sc);
}
static void
handle_atapi_identify(struct ahci_port *p, int slot, uint8_t *cfis)
{
if (!p->atapi) {
- p->tfd = (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR;
- p->is |= AHCI_P_IX_TFE;
+ ahci_write_fis_d2h(p, slot, cfis,
+ (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
} else {
uint16_t buf[256];
memset(buf, 0, sizeof(buf));
buf[0] = (2 << 14 | 5 << 8 | 1 << 7 | 2 << 5);
- /* TODO emulate different serial? */
- ata_string((uint8_t *)(buf+10), "123456", 20);
+ ata_string((uint8_t *)(buf+10), p->ident, 20);
ata_string((uint8_t *)(buf+23), "001", 8);
ata_string((uint8_t *)(buf+27), "BHYVE SATA DVD ROM", 40);
buf[49] = (1 << 9 | 1 << 8);
@@ -762,27 +1093,34 @@ handle_atapi_identify(struct ahci_port *p, int slot, uint8_t *cfis)
buf[53] = (1 << 2 | 1 << 1);
buf[62] = 0x3f;
buf[63] = 7;
+ if (p->xfermode & ATA_WDMA0)
+ buf[63] |= (1 << ((p->xfermode & 7) + 8));
buf[64] = 3;
- buf[65] = 100;
- buf[66] = 100;
- buf[67] = 100;
- buf[68] = 100;
- buf[76] = (1 << 2 | 1 << 1);
+ buf[65] = 120;
+ buf[66] = 120;
+ buf[67] = 120;
+ buf[68] = 120;
+ buf[76] = (ATA_SATA_GEN1 | ATA_SATA_GEN2 | ATA_SATA_GEN3);
+ buf[77] = ((p->ssts & ATA_SS_SPD_MASK) >> 3);
buf[78] = (1 << 5);
- buf[80] = (0x1f << 4);
- buf[82] = (1 << 4);
+ buf[80] = 0x3f0;
+ buf[82] = (ATA_SUPPORT_POWERMGT | ATA_SUPPORT_PACKET |
+ ATA_SUPPORT_RESET | ATA_SUPPORT_NOP);
buf[83] = (1 << 14);
buf[84] = (1 << 14);
- buf[85] = (1 << 4);
+ buf[85] = (ATA_SUPPORT_POWERMGT | ATA_SUPPORT_PACKET |
+ ATA_SUPPORT_RESET | ATA_SUPPORT_NOP);
buf[87] = (1 << 14);
- buf[88] = (1 << 14 | 0x7f);
+ buf[88] = 0x7f;
+ if (p->xfermode & ATA_UDMA0)
+ buf[88] |= (1 << ((p->xfermode & 7) + 8));
+ buf[222] = 0x1020;
+ buf[255] = 0x00a5;
+ ahci_checksum((uint8_t *)buf, sizeof(buf));
ahci_write_fis_piosetup(p);
write_prdt(p, slot, cfis, (void *)buf, sizeof(buf));
- p->tfd = ATA_S_DSC | ATA_S_READY;
- p->is |= AHCI_P_IX_DHR;
- p->ci &= ~(1 << slot);
+ ahci_write_fis_d2h(p, slot, cfis, ATA_S_DSC | ATA_S_READY);
}
- ahci_generate_intr(p->pr_sc);
}
static void
@@ -791,22 +1129,41 @@ atapi_inquiry(struct ahci_port *p, int slot, uint8_t *cfis)
uint8_t buf[36];
uint8_t *acmd;
int len;
+ uint32_t tfd;
acmd = cfis + 0x40;
- buf[0] = 0x05;
- buf[1] = 0x80;
- buf[2] = 0x00;
- buf[3] = 0x21;
- buf[4] = 31;
- buf[5] = 0;
- buf[6] = 0;
- buf[7] = 0;
- atapi_string(buf + 8, "BHYVE", 8);
- atapi_string(buf + 16, "BHYVE DVD-ROM", 16);
- atapi_string(buf + 32, "001", 4);
-
- len = sizeof(buf);
+ if (acmd[1] & 1) { /* VPD */
+ if (acmd[2] == 0) { /* Supported VPD pages */
+ buf[0] = 0x05;
+ buf[1] = 0;
+ buf[2] = 0;
+ buf[3] = 1;
+ buf[4] = 0;
+ len = 4 + buf[3];
+ } else {
+ p->sense_key = ATA_SENSE_ILLEGAL_REQUEST;
+ p->asc = 0x24;
+ tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR;
+ cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
+ ahci_write_fis_d2h(p, slot, cfis, tfd);
+ return;
+ }
+ } else {
+ buf[0] = 0x05;
+ buf[1] = 0x80;
+ buf[2] = 0x00;
+ buf[3] = 0x21;
+ buf[4] = 31;
+ buf[5] = 0;
+ buf[6] = 0;
+ buf[7] = 0;
+ atapi_string(buf + 8, "BHYVE", 8);
+ atapi_string(buf + 16, "BHYVE DVD-ROM", 16);
+ atapi_string(buf + 32, "001", 4);
+ len = sizeof(buf);
+ }
+
if (len > acmd[4])
len = acmd[4];
cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
@@ -918,10 +1275,9 @@ atapi_read_toc(struct ahci_port *p, int slot, uint8_t *cfis)
{
int msf, size;
uint64_t sectors;
- uint8_t start_track, *bp, buf[50];
+ uint8_t *bp, buf[50];
msf = (acmd[1] >> 1) & 1;
- start_track = acmd[6];
bp = buf + 2;
*bp++ = 1;
*bp++ = 1;
@@ -1010,25 +1366,34 @@ atapi_read_toc(struct ahci_port *p, int slot, uint8_t *cfis)
}
static void
-atapi_read(struct ahci_port *p, int slot, uint8_t *cfis,
- uint32_t done, int seek)
+atapi_report_luns(struct ahci_port *p, int slot, uint8_t *cfis)
+{
+ uint8_t buf[16];
+
+ memset(buf, 0, sizeof(buf));
+ buf[3] = 8;
+
+ cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
+ write_prdt(p, slot, cfis, buf, sizeof(buf));
+ ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
+}
+
+static void
+atapi_read(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done)
{
struct ahci_ioreq *aior;
struct ahci_cmd_hdr *hdr;
struct ahci_prdt_entry *prdt;
struct blockif_req *breq;
- struct pci_ahci_softc *sc;
uint8_t *acmd;
uint64_t lba;
uint32_t len;
- int i, err, iovcnt;
+ int err;
- sc = p->pr_sc;
acmd = cfis + 0x40;
hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
prdt = (struct ahci_prdt_entry *)(cfis + 0x80);
- prdt += seek;
lba = be32dec(acmd + 2);
if (acmd[0] == READ_10)
len = be16dec(acmd + 7);
@@ -1053,37 +1418,14 @@ atapi_read(struct ahci_port *p, int slot, uint8_t *cfis,
aior->done = done;
breq = &aior->io_req;
breq->br_offset = lba + done;
- iovcnt = hdr->prdtl - seek;
- if (iovcnt > BLOCKIF_IOV_MAX) {
- aior->prdtl = iovcnt - BLOCKIF_IOV_MAX;
- iovcnt = BLOCKIF_IOV_MAX;
- } else
- aior->prdtl = 0;
- breq->br_iovcnt = iovcnt;
+ ahci_build_iov(p, aior, prdt, hdr->prdtl);
- /*
- * Mark this command in-flight.
- */
+ /* Mark this command in-flight. */
p->pending |= 1 << slot;
- /*
- * Stuff request onto busy list
- */
+ /* Stuff request onto busy list. */
TAILQ_INSERT_HEAD(&p->iobhd, aior, io_blist);
- /*
- * Build up the iovec based on the prdt
- */
- for (i = 0; i < iovcnt; i++) {
- uint32_t dbcsz;
-
- dbcsz = (prdt->dbc & DBCMASK) + 1;
- breq->br_iov[i].iov_base = paddr_guest2host(ahci_ctx(sc),
- prdt->dba, dbcsz);
- breq->br_iov[i].iov_len = dbcsz;
- aior->done += dbcsz;
- prdt++;
- }
err = blockif_read(p->bctx, breq);
assert(err == 0);
}
@@ -1278,9 +1620,12 @@ handle_packet_cmd(struct ahci_port *p, int slot, uint8_t *cfis)
case READ_TOC:
atapi_read_toc(p, slot, cfis);
break;
+ case REPORT_LUNS:
+ atapi_report_luns(p, slot, cfis);
+ break;
case READ_10:
case READ_12:
- atapi_read(p, slot, cfis, 0, 0);
+ atapi_read(p, slot, cfis, 0);
break;
case REQUEST_SENSE:
atapi_request_sense(p, slot, cfis);
@@ -1308,6 +1653,7 @@ static void
ahci_handle_cmd(struct ahci_port *p, int slot, uint8_t *cfis)
{
+ p->tfd |= ATA_S_BUSY;
switch (cfis[2]) {
case ATA_ATA_IDENTIFY:
handle_identify(p, slot, cfis);
@@ -1363,28 +1709,68 @@ ahci_handle_cmd(struct ahci_port *p, int slot, uint8_t *cfis)
p->mult_sectors = cfis[12];
p->tfd = ATA_S_DSC | ATA_S_READY;
}
- p->is |= AHCI_P_IX_DP;
- p->ci &= ~(1 << slot);
- ahci_generate_intr(p->pr_sc);
+ ahci_write_fis_d2h(p, slot, cfis, p->tfd);
break;
+ case ATA_READ:
+ case ATA_WRITE:
+ case ATA_READ48:
+ case ATA_WRITE48:
+ case ATA_READ_MUL:
+ case ATA_WRITE_MUL:
+ case ATA_READ_MUL48:
+ case ATA_WRITE_MUL48:
case ATA_READ_DMA:
case ATA_WRITE_DMA:
case ATA_READ_DMA48:
case ATA_WRITE_DMA48:
case ATA_READ_FPDMA_QUEUED:
case ATA_WRITE_FPDMA_QUEUED:
- ahci_handle_dma(p, slot, cfis, 0, 0);
+ ahci_handle_rw(p, slot, cfis, 0);
break;
case ATA_FLUSHCACHE:
case ATA_FLUSHCACHE48:
ahci_handle_flush(p, slot, cfis);
break;
- case ATA_STANDBY_CMD:
+ case ATA_DATA_SET_MANAGEMENT:
+ if (cfis[11] == 0 && cfis[3] == ATA_DSM_TRIM &&
+ cfis[13] == 0 && cfis[12] == 1) {
+ ahci_handle_dsm_trim(p, slot, cfis, 0);
+ break;
+ }
+ ahci_write_fis_d2h(p, slot, cfis,
+ (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
+ break;
+ case ATA_SEND_FPDMA_QUEUED:
+ if ((cfis[13] & 0x1f) == ATA_SFPDMA_DSM &&
+ cfis[17] == 0 && cfis[16] == ATA_DSM_TRIM &&
+ cfis[11] == 0 && cfis[3] == 1) {
+ ahci_handle_dsm_trim(p, slot, cfis, 0);
+ break;
+ }
+ ahci_write_fis_d2h(p, slot, cfis,
+ (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
break;
+ case ATA_READ_LOG_EXT:
+ case ATA_READ_LOG_DMA_EXT:
+ ahci_handle_read_log(p, slot, cfis);
+ break;
+ case ATA_SECURITY_FREEZE_LOCK:
+ case ATA_SMART_CMD:
case ATA_NOP:
+ ahci_write_fis_d2h(p, slot, cfis,
+ (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
+ break;
+ case ATA_CHECK_POWER_MODE:
+ cfis[12] = 0xff; /* always on */
+ ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
+ break;
+ case ATA_STANDBY_CMD:
case ATA_STANDBY_IMMEDIATE:
+ case ATA_IDLE_CMD:
case ATA_IDLE_IMMEDIATE:
case ATA_SLEEP:
+ case ATA_READ_VERIFY:
+ case ATA_READ_VERIFY48:
ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
break;
case ATA_ATAPI_IDENTIFY:
@@ -1392,17 +1778,15 @@ ahci_handle_cmd(struct ahci_port *p, int slot, uint8_t *cfis)
break;
case ATA_PACKET_CMD:
if (!p->atapi) {
- p->tfd = (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR;
- p->is |= AHCI_P_IX_TFE;
- ahci_generate_intr(p->pr_sc);
+ ahci_write_fis_d2h(p, slot, cfis,
+ (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
} else
handle_packet_cmd(p, slot, cfis);
break;
default:
WPRINTF("Unsupported cmd:%02x\n", cfis[2]);
- p->tfd = (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR;
- p->is |= AHCI_P_IX_TFE;
- ahci_generate_intr(p->pr_sc);
+ ahci_write_fis_d2h(p, slot, cfis,
+ (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR);
break;
}
}
@@ -1411,19 +1795,25 @@ static void
ahci_handle_slot(struct ahci_port *p, int slot)
{
struct ahci_cmd_hdr *hdr;
+#ifdef AHCI_DEBUG
struct ahci_prdt_entry *prdt;
+#endif
struct pci_ahci_softc *sc;
uint8_t *cfis;
- int cfl;
+#ifdef AHCI_DEBUG
+ int cfl, i;
+#endif
sc = p->pr_sc;
hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
+#ifdef AHCI_DEBUG
cfl = (hdr->flags & 0x1f) * 4;
+#endif
cfis = paddr_guest2host(ahci_ctx(sc), hdr->ctba,
0x80 + hdr->prdtl * sizeof(struct ahci_prdt_entry));
+#ifdef AHCI_DEBUG
prdt = (struct ahci_prdt_entry *)(cfis + 0x80);
-#ifdef AHCI_DEBUG
DPRINTF("\ncfis:");
for (i = 0; i < cfl; i++) {
if (i % 10 == 0)
@@ -1459,20 +1849,23 @@ ahci_handle_slot(struct ahci_port *p, int slot)
static void
ahci_handle_port(struct ahci_port *p)
{
- int i;
if (!(p->cmd & AHCI_P_CMD_ST))
return;
/*
* Search for any new commands to issue ignoring those that
- * are already in-flight.
+ * are already in-flight. Stop if device is busy or in error.
*/
- for (i = 0; (i < 32) && p->ci; i++) {
- if ((p->ci & (1 << i)) && !(p->pending & (1 << i))) {
+ for (; (p->ci & ~p->pending) != 0; p->ccs = ((p->ccs + 1) & 31)) {
+ if ((p->tfd & (ATA_S_BUSY | ATA_S_DRQ)) != 0)
+ break;
+ if (p->waitforclear)
+ break;
+ if ((p->ci & ~p->pending & (1 << p->ccs)) != 0) {
p->cmd &= ~AHCI_P_CMD_CCS_MASK;
- p->cmd |= i << AHCI_P_CMD_CCS_SHIFT;
- ahci_handle_slot(p, i);
+ p->cmd |= p->ccs << AHCI_P_CMD_CCS_SHIFT;
+ ahci_handle_slot(p, p->ccs);
}
}
}
@@ -1490,22 +1883,26 @@ ata_ioreq_cb(struct blockif_req *br, int err)
struct pci_ahci_softc *sc;
uint32_t tfd;
uint8_t *cfis;
- int pending, slot, ncq;
+ int slot, ncq, dsm;
DPRINTF("%s %d\n", __func__, err);
- ncq = 0;
+ ncq = dsm = 0;
aior = br->br_param;
p = aior->io_pr;
cfis = aior->cfis;
slot = aior->slot;
- pending = aior->prdtl;
sc = p->pr_sc;
hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
if (cfis[2] == ATA_WRITE_FPDMA_QUEUED ||
- cfis[2] == ATA_READ_FPDMA_QUEUED)
+ cfis[2] == ATA_READ_FPDMA_QUEUED ||
+ cfis[2] == ATA_SEND_FPDMA_QUEUED)
ncq = 1;
+ if (cfis[2] == ATA_DATA_SET_MANAGEMENT ||
+ (cfis[2] == ATA_SEND_FPDMA_QUEUED &&
+ (cfis[13] & 0x1f) == ATA_SFPDMA_DSM))
+ dsm = 1;
pthread_mutex_lock(&sc->mtx);
@@ -1519,29 +1916,24 @@ ata_ioreq_cb(struct blockif_req *br, int err)
*/
STAILQ_INSERT_TAIL(&p->iofhd, aior, io_flist);
- if (pending && !err) {
- ahci_handle_dma(p, slot, cfis, aior->done,
- hdr->prdtl - pending);
+ if (!err)
+ hdr->prdbc = aior->done;
+
+ if (!err && aior->more) {
+ if (dsm)
+ ahci_handle_dsm_trim(p, slot, cfis, aior->done);
+ else
+ ahci_handle_rw(p, slot, cfis, aior->done);
goto out;
}
- if (!err && aior->done == aior->len) {
+ if (!err)
tfd = ATA_S_READY | ATA_S_DSC;
- if (ncq)
- hdr->prdbc = 0;
- else
- hdr->prdbc = aior->len;
- } else {
+ else
tfd = (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR;
- hdr->prdbc = 0;
- if (ncq)
- p->serr |= (1 << slot);
- }
-
- if (ncq) {
- p->sact &= ~(1 << slot);
- ahci_write_fis_sdb(p, slot, tfd);
- } else
+ if (ncq)
+ ahci_write_fis_sdb(p, slot, cfis, tfd);
+ else
ahci_write_fis_d2h(p, slot, cfis, tfd);
/*
@@ -1550,6 +1942,7 @@ ata_ioreq_cb(struct blockif_req *br, int err)
p->pending &= ~(1 << slot);
ahci_check_stopped(p);
+ ahci_handle_port(p);
out:
pthread_mutex_unlock(&sc->mtx);
DPRINTF("%s exit\n", __func__);
@@ -1564,7 +1957,7 @@ atapi_ioreq_cb(struct blockif_req *br, int err)
struct pci_ahci_softc *sc;
uint8_t *cfis;
uint32_t tfd;
- int pending, slot;
+ int slot;
DPRINTF("%s %d\n", __func__, err);
@@ -1572,7 +1965,6 @@ atapi_ioreq_cb(struct blockif_req *br, int err)
p = aior->io_pr;
cfis = aior->cfis;
slot = aior->slot;
- pending = aior->prdtl;
sc = p->pr_sc;
hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + aior->slot * AHCI_CL_SIZE);
@@ -1588,21 +1980,21 @@ atapi_ioreq_cb(struct blockif_req *br, int err)
*/
STAILQ_INSERT_TAIL(&p->iofhd, aior, io_flist);
- if (pending && !err) {
- atapi_read(p, slot, cfis, aior->done, hdr->prdtl - pending);
+ if (!err)
+ hdr->prdbc = aior->done;
+
+ if (!err && aior->more) {
+ atapi_read(p, slot, cfis, aior->done);
goto out;
}
- if (!err && aior->done == aior->len) {
+ if (!err) {
tfd = ATA_S_READY | ATA_S_DSC;
- hdr->prdbc = aior->len;
} else {
p->sense_key = ATA_SENSE_ILLEGAL_REQUEST;
p->asc = 0x21;
tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR;
- hdr->prdbc = 0;
}
-
cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
ahci_write_fis_d2h(p, slot, cfis, tfd);
@@ -1612,6 +2004,7 @@ atapi_ioreq_cb(struct blockif_req *br, int err)
p->pending &= ~(1 << slot);
ahci_check_stopped(p);
+ ahci_handle_port(p);
out:
pthread_mutex_unlock(&sc->mtx);
DPRINTF("%s exit\n", __func__);
@@ -1669,15 +2062,23 @@ pci_ahci_port_write(struct pci_ahci_softc *sc, uint64_t offset, uint64_t value)
break;
case AHCI_P_IS:
p->is &= ~value;
+ ahci_port_intr(p);
break;
case AHCI_P_IE:
p->ie = value & 0xFDC000FF;
- ahci_generate_intr(sc);
+ ahci_port_intr(p);
break;
case AHCI_P_CMD:
{
- p->cmd = value;
-
+ p->cmd &= ~(AHCI_P_CMD_ST | AHCI_P_CMD_SUD | AHCI_P_CMD_POD |
+ AHCI_P_CMD_CLO | AHCI_P_CMD_FRE | AHCI_P_CMD_APSTE |
+ AHCI_P_CMD_ATAPI | AHCI_P_CMD_DLAE | AHCI_P_CMD_ALPE |
+ AHCI_P_CMD_ASP | AHCI_P_CMD_ICC_MASK);
+ p->cmd |= (AHCI_P_CMD_ST | AHCI_P_CMD_SUD | AHCI_P_CMD_POD |
+ AHCI_P_CMD_CLO | AHCI_P_CMD_FRE | AHCI_P_CMD_APSTE |
+ AHCI_P_CMD_ATAPI | AHCI_P_CMD_DLAE | AHCI_P_CMD_ALPE |
+ AHCI_P_CMD_ASP | AHCI_P_CMD_ICC_MASK) & value;
+
if (!(value & AHCI_P_CMD_ST)) {
ahci_port_stop(p);
} else {
@@ -1701,10 +2102,14 @@ pci_ahci_port_write(struct pci_ahci_softc *sc, uint64_t offset, uint64_t value)
}
if (value & AHCI_P_CMD_CLO) {
- p->tfd = 0;
+ p->tfd &= ~(ATA_S_BUSY | ATA_S_DRQ);
p->cmd &= ~AHCI_P_CMD_CLO;
}
+ if (value & AHCI_P_CMD_ICC_MASK) {
+ p->cmd &= ~AHCI_P_CMD_ICC_MASK;
+ }
+
ahci_handle_port(p);
break;
}
@@ -1714,10 +2119,10 @@ pci_ahci_port_write(struct pci_ahci_softc *sc, uint64_t offset, uint64_t value)
WPRINTF("pci_ahci_port: read only registers 0x%"PRIx64"\n", offset);
break;
case AHCI_P_SCTL:
+ p->sctl = value;
if (!(p->cmd & AHCI_P_CMD_ST)) {
if (value & ATA_SC_DET_RESET)
ahci_port_reset(p);
- p->sctl = value;
}
break;
case AHCI_P_SERR:
@@ -1751,16 +2156,19 @@ pci_ahci_host_write(struct pci_ahci_softc *sc, uint64_t offset, uint64_t value)
DPRINTF("pci_ahci_host: read only registers 0x%"PRIx64"\n", offset);
break;
case AHCI_GHC:
- if (value & AHCI_GHC_HR)
+ if (value & AHCI_GHC_HR) {
ahci_reset(sc);
- else if (value & AHCI_GHC_IE) {
- sc->ghc |= AHCI_GHC_IE;
- ahci_generate_intr(sc);
+ break;
}
+ if (value & AHCI_GHC_IE)
+ sc->ghc |= AHCI_GHC_IE;
+ else
+ sc->ghc &= ~AHCI_GHC_IE;
+ ahci_generate_intr(sc, 0xffffffff);
break;
case AHCI_IS:
sc->is &= ~value;
- ahci_generate_intr(sc);
+ ahci_generate_intr(sc, value);
break;
default:
break;
@@ -1774,7 +2182,7 @@ pci_ahci_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
struct pci_ahci_softc *sc = pi->pi_arg;
assert(baridx == 5);
- assert(size == 4);
+ assert((offset % 4) == 0 && size == 4);
pthread_mutex_lock(&sc->mtx);
@@ -1863,24 +2271,29 @@ pci_ahci_port_read(struct pci_ahci_softc *sc, uint64_t offset)
static uint64_t
pci_ahci_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
- uint64_t offset, int size)
+ uint64_t regoff, int size)
{
struct pci_ahci_softc *sc = pi->pi_arg;
+ uint64_t offset;
uint32_t value;
assert(baridx == 5);
- assert(size == 4);
+ assert(size == 1 || size == 2 || size == 4);
+ assert((regoff & (size - 1)) == 0);
pthread_mutex_lock(&sc->mtx);
+ offset = regoff & ~0x3; /* round down to a multiple of 4 bytes */
if (offset < AHCI_OFFSET)
value = pci_ahci_host_read(sc, offset);
else if (offset < AHCI_OFFSET + sc->ports * AHCI_STEP)
value = pci_ahci_port_read(sc, offset);
else {
value = 0;
- WPRINTF("pci_ahci: unknown i/o read offset 0x%"PRIx64"\n", offset);
+ WPRINTF("pci_ahci: unknown i/o read offset 0x%"PRIx64"\n",
+ regoff);
}
+ value >>= 8 * (regoff & 0x3);
pthread_mutex_unlock(&sc->mtx);
@@ -1890,18 +2303,16 @@ pci_ahci_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
static int
pci_ahci_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts, int atapi)
{
- char bident[sizeof("XX:X:X")];
+ char bident[sizeof("XX:XX:XX")];
struct blockif_ctxt *bctxt;
struct pci_ahci_softc *sc;
- int ret, slots;
+ int ret, slots, p;
+ MD5_CTX mdctx;
+ u_char digest[16];
+ char *next, *next2;
ret = 0;
- if (opts == NULL) {
- fprintf(stderr, "pci_ahci: backing device required\n");
- return (1);
- }
-
#ifdef AHCI_DEBUG
dbg = fopen("/tmp/log", "w+");
#endif
@@ -1909,48 +2320,84 @@ pci_ahci_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts, int atapi)
sc = calloc(1, sizeof(struct pci_ahci_softc));
pi->pi_arg = sc;
sc->asc_pi = pi;
- sc->ports = MAX_PORTS;
+ pthread_mutex_init(&sc->mtx, NULL);
+ sc->ports = 0;
+ sc->pi = 0;
+ slots = 32;
+
+ for (p = 0; p < MAX_PORTS && opts != NULL; p++, opts = next) {
+ /* Identify and cut off type of present port. */
+ if (strncmp(opts, "hd:", 3) == 0) {
+ atapi = 0;
+ opts += 3;
+ } else if (strncmp(opts, "cd:", 3) == 0) {
+ atapi = 1;
+ opts += 3;
+ }
- /*
- * Only use port 0 for a backing device. All other ports will be
- * marked as unused
- */
- sc->port[0].atapi = atapi;
+ /* Find and cut off the next port options. */
+ next = strstr(opts, ",hd:");
+ next2 = strstr(opts, ",cd:");
+ if (next == NULL || (next2 != NULL && next2 < next))
+ next = next2;
+ if (next != NULL) {
+ next[0] = 0;
+ next++;
+ }
- /*
- * Attempt to open the backing image. Use the PCI
- * slot/func for the identifier string.
- */
- snprintf(bident, sizeof(bident), "%d:%d", pi->pi_slot, pi->pi_func);
- bctxt = blockif_open(opts, bident);
- if (bctxt == NULL) {
- ret = 1;
- goto open_fail;
- }
- sc->port[0].bctx = bctxt;
- sc->port[0].pr_sc = sc;
+ if (opts[0] == 0)
+ continue;
- /*
- * Allocate blockif request structures and add them
- * to the free list
- */
- pci_ahci_ioreq_init(&sc->port[0]);
+ /*
+ * Attempt to open the backing image. Use the PCI slot/func
+ * and the port number for the identifier string.
+ */
+ snprintf(bident, sizeof(bident), "%d:%d:%d", pi->pi_slot,
+ pi->pi_func, p);
+ bctxt = blockif_open(opts, bident);
+ if (bctxt == NULL) {
+ sc->ports = p;
+ ret = 1;
+ goto open_fail;
+ }
+ sc->port[p].bctx = bctxt;
+ sc->port[p].pr_sc = sc;
+ sc->port[p].port = p;
+ sc->port[p].atapi = atapi;
- pthread_mutex_init(&sc->mtx, NULL);
+ /*
+ * Create an identifier for the backing file.
+ * Use parts of the md5 sum of the filename
+ */
+ MD5Init(&mdctx);
+ MD5Update(&mdctx, opts, strlen(opts));
+ MD5Final(digest, &mdctx);
+ sprintf(sc->port[p].ident, "BHYVE-%02X%02X-%02X%02X-%02X%02X",
+ digest[0], digest[1], digest[2], digest[3], digest[4],
+ digest[5]);
+
+ /*
+ * Allocate blockif request structures and add them
+ * to the free list
+ */
+ pci_ahci_ioreq_init(&sc->port[p]);
+
+ sc->pi |= (1 << p);
+ if (sc->port[p].ioqsz < slots)
+ slots = sc->port[p].ioqsz;
+ }
+ sc->ports = p;
/* Intel ICH8 AHCI */
- slots = sc->port[0].ioqsz;
- if (slots > 32)
- slots = 32;
--slots;
+ if (sc->ports < DEF_PORTS)
+ sc->ports = DEF_PORTS;
sc->cap = AHCI_CAP_64BIT | AHCI_CAP_SNCQ | AHCI_CAP_SSNTF |
AHCI_CAP_SMPS | AHCI_CAP_SSS | AHCI_CAP_SALP |
AHCI_CAP_SAL | AHCI_CAP_SCLO | (0x3 << AHCI_CAP_ISS_SHIFT)|
AHCI_CAP_PMD | AHCI_CAP_SSC | AHCI_CAP_PSC |
(slots << AHCI_CAP_NCS_SHIFT) | AHCI_CAP_SXS | (sc->ports - 1);
- /* Only port 0 implemented */
- sc->pi = 1;
sc->vs = 0x10300;
sc->cap2 = AHCI_CAP2_APST;
ahci_reset(sc);
@@ -1960,7 +2407,9 @@ pci_ahci_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts, int atapi)
pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_STORAGE);
pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_STORAGE_SATA);
pci_set_cfgdata8(pi, PCIR_PROGIF, PCIP_STORAGE_SATA_AHCI_1_0);
- pci_emul_add_msicap(pi, 1);
+ p = MIN(sc->ports, 16);
+ p = flsl(p) - ((p & (p - 1)) ? 0 : 1);
+ pci_emul_add_msicap(pi, 1 << p);
pci_emul_alloc_bar(pi, 5, PCIBAR_MEM32,
AHCI_OFFSET + sc->ports * AHCI_STEP);
@@ -1968,7 +2417,10 @@ pci_ahci_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts, int atapi)
open_fail:
if (ret) {
- blockif_close(sc->port[0].bctx);
+ for (p = 0; p < sc->ports; p++) {
+ if (sc->port[p].bctx != NULL)
+ blockif_close(sc->port[p].bctx);
+ }
free(sc);
}
@@ -1992,6 +2444,14 @@ pci_ahci_atapi_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
/*
* Use separate emulation names to distinguish drive and atapi devices
*/
+struct pci_devemu pci_de_ahci = {
+ .pe_emu = "ahci",
+ .pe_init = pci_ahci_hd_init,
+ .pe_barwrite = pci_ahci_write,
+ .pe_barread = pci_ahci_read
+};
+PCI_EMUL_SET(pci_de_ahci);
+
struct pci_devemu pci_de_ahci_hd = {
.pe_emu = "ahci-hd",
.pe_init = pci_ahci_hd_init,
diff --git a/usr/src/cmd/bhyve/pci_e82545.c b/usr/src/cmd/bhyve/pci_e82545.c
new file mode 100644
index 0000000000..121c0fc773
--- /dev/null
+++ b/usr/src/cmd/bhyve/pci_e82545.c
@@ -0,0 +1,2413 @@
+/*
+ * Copyright (c) 2016 Alexander Motin <mav@FreeBSD.org>
+ * Copyright (c) 2015 Peter Grehan <grehan@freebsd.org>
+ * Copyright (c) 2013 Jeremiah Lott, Avere Systems
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#ifndef WITHOUT_CAPSICUM
+#include <sys/capsicum.h>
+#endif
+#include <sys/limits.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#ifndef __FreeBSD__
+#include <sys/filio.h>
+#endif
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <md5.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <pthread_np.h>
+
+#include "e1000_regs.h"
+#include "e1000_defines.h"
+#include "mii.h"
+
+#include "bhyverun.h"
+#include "pci_emul.h"
+#include "mevent.h"
+
+/* Hardware/register definitions XXX: move some to common code. */
+#define E82545_VENDOR_ID_INTEL 0x8086
+#define E82545_DEV_ID_82545EM_COPPER 0x100F
+#define E82545_SUBDEV_ID 0x1008
+
+#define E82545_REVISION_4 4
+
+#define E82545_MDIC_DATA_MASK 0x0000FFFF
+#define E82545_MDIC_OP_MASK 0x0c000000
+#define E82545_MDIC_IE 0x20000000
+
+#define E82545_EECD_FWE_DIS 0x00000010 /* Flash writes disabled */
+#define E82545_EECD_FWE_EN 0x00000020 /* Flash writes enabled */
+#define E82545_EECD_FWE_MASK 0x00000030 /* Flash writes mask */
+
+#define E82545_BAR_REGISTER 0
+#define E82545_BAR_REGISTER_LEN (128*1024)
+#define E82545_BAR_FLASH 1
+#define E82545_BAR_FLASH_LEN (64*1024)
+#define E82545_BAR_IO 2
+#define E82545_BAR_IO_LEN 8
+
+#define E82545_IOADDR 0x00000000
+#define E82545_IODATA 0x00000004
+#define E82545_IO_REGISTER_MAX 0x0001FFFF
+#define E82545_IO_FLASH_BASE 0x00080000
+#define E82545_IO_FLASH_MAX 0x000FFFFF
+
+#define E82545_ARRAY_ENTRY(reg, offset) (reg + (offset<<2))
+#define E82545_RAR_MAX 15
+#define E82545_MTA_MAX 127
+#define E82545_VFTA_MAX 127
+
+/* Slightly modified from the driver versions, hardcoded for 3 opcode bits,
+ * followed by 6 address bits.
+ * TODO: make opcode bits and addr bits configurable?
+ * NVM Commands - Microwire */
+#define E82545_NVM_OPCODE_BITS 3
+#define E82545_NVM_ADDR_BITS 6
+#define E82545_NVM_DATA_BITS 16
+#define E82545_NVM_OPADDR_BITS (E82545_NVM_OPCODE_BITS + E82545_NVM_ADDR_BITS)
+#define E82545_NVM_ADDR_MASK ((1 << E82545_NVM_ADDR_BITS)-1)
+#define E82545_NVM_OPCODE_MASK \
+ (((1 << E82545_NVM_OPCODE_BITS) - 1) << E82545_NVM_ADDR_BITS)
+#define E82545_NVM_OPCODE_READ (0x6 << E82545_NVM_ADDR_BITS) /* read */
+#define E82545_NVM_OPCODE_WRITE (0x5 << E82545_NVM_ADDR_BITS) /* write */
+#define E82545_NVM_OPCODE_ERASE (0x7 << E82545_NVM_ADDR_BITS) /* erase */
+#define E82545_NVM_OPCODE_EWEN (0x4 << E82545_NVM_ADDR_BITS) /* wr-enable */
+
+#define E82545_NVM_EEPROM_SIZE 64 /* 64 * 16-bit values == 128K */
+
+#define E1000_ICR_SRPD 0x00010000
+
+/* This is an arbitrary number. There is no hard limit on the chip. */
+#define I82545_MAX_TXSEGS 64
+
+/* Legacy receive descriptor */
+struct e1000_rx_desc {
+ uint64_t buffer_addr; /* Address of the descriptor's data buffer */
+ uint16_t length; /* Length of data DMAed into data buffer */
+ uint16_t csum; /* Packet checksum */
+ uint8_t status; /* Descriptor status */
+ uint8_t errors; /* Descriptor Errors */
+ uint16_t special;
+};
+
+/* Transmit descriptor types */
+#define E1000_TXD_MASK (E1000_TXD_CMD_DEXT | 0x00F00000)
+#define E1000_TXD_TYP_L (0)
+#define E1000_TXD_TYP_C (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_C)
+#define E1000_TXD_TYP_D (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D)
+
+/* Legacy transmit descriptor */
+struct e1000_tx_desc {
+ uint64_t buffer_addr; /* Address of the descriptor's data buffer */
+ union {
+ uint32_t data;
+ struct {
+ uint16_t length; /* Data buffer length */
+ uint8_t cso; /* Checksum offset */
+ uint8_t cmd; /* Descriptor control */
+ } flags;
+ } lower;
+ union {
+ uint32_t data;
+ struct {
+ uint8_t status; /* Descriptor status */
+ uint8_t css; /* Checksum start */
+ uint16_t special;
+ } fields;
+ } upper;
+};
+
+/* Context descriptor */
+struct e1000_context_desc {
+ union {
+ uint32_t ip_config;
+ struct {
+ uint8_t ipcss; /* IP checksum start */
+ uint8_t ipcso; /* IP checksum offset */
+ uint16_t ipcse; /* IP checksum end */
+ } ip_fields;
+ } lower_setup;
+ union {
+ uint32_t tcp_config;
+ struct {
+ uint8_t tucss; /* TCP checksum start */
+ uint8_t tucso; /* TCP checksum offset */
+ uint16_t tucse; /* TCP checksum end */
+ } tcp_fields;
+ } upper_setup;
+ uint32_t cmd_and_length;
+ union {
+ uint32_t data;
+ struct {
+ uint8_t status; /* Descriptor status */
+ uint8_t hdr_len; /* Header length */
+ uint16_t mss; /* Maximum segment size */
+ } fields;
+ } tcp_seg_setup;
+};
+
+/* Data descriptor */
+struct e1000_data_desc {
+ uint64_t buffer_addr; /* Address of the descriptor's buffer address */
+ union {
+ uint32_t data;
+ struct {
+ uint16_t length; /* Data buffer length */
+ uint8_t typ_len_ext;
+ uint8_t cmd;
+ } flags;
+ } lower;
+ union {
+ uint32_t data;
+ struct {
+ uint8_t status; /* Descriptor status */
+ uint8_t popts; /* Packet Options */
+ uint16_t special;
+ } fields;
+ } upper;
+};
+
+union e1000_tx_udesc {
+ struct e1000_tx_desc td;
+ struct e1000_context_desc cd;
+ struct e1000_data_desc dd;
+};
+
+/* Tx checksum info for a packet. */
+struct ck_info {
+ int ck_valid; /* ck_info is valid */
+ uint8_t ck_start; /* start byte of cksum calcuation */
+ uint8_t ck_off; /* offset of cksum insertion */
+ uint16_t ck_len; /* length of cksum calc: 0 is to packet-end */
+};
+
+/*
+ * Debug printf
+ */
+static int e82545_debug = 0;
+#define DPRINTF(msg,params...) if (e82545_debug) fprintf(stderr, "e82545: " msg, params)
+#define WPRINTF(msg,params...) fprintf(stderr, "e82545: " msg, params)
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#define MAX(a,b) (((a)>(b))?(a):(b))
+
+/* s/w representation of the RAL/RAH regs */
+struct eth_uni {
+ int eu_valid;
+ int eu_addrsel;
+ struct ether_addr eu_eth;
+};
+
+
+struct e82545_softc {
+ struct pci_devinst *esc_pi;
+ struct vmctx *esc_ctx;
+ struct mevent *esc_mevp;
+ struct mevent *esc_mevpitr;
+ pthread_mutex_t esc_mtx;
+ struct ether_addr esc_mac;
+ int esc_tapfd;
+
+ /* General */
+ uint32_t esc_CTRL; /* x0000 device ctl */
+ uint32_t esc_FCAL; /* x0028 flow ctl addr lo */
+ uint32_t esc_FCAH; /* x002C flow ctl addr hi */
+ uint32_t esc_FCT; /* x0030 flow ctl type */
+ uint32_t esc_VET; /* x0038 VLAN eth type */
+ uint32_t esc_FCTTV; /* x0170 flow ctl tx timer */
+ uint32_t esc_LEDCTL; /* x0E00 LED control */
+ uint32_t esc_PBA; /* x1000 pkt buffer allocation */
+
+ /* Interrupt control */
+ int esc_irq_asserted;
+ uint32_t esc_ICR; /* x00C0 cause read/clear */
+ uint32_t esc_ITR; /* x00C4 intr throttling */
+ uint32_t esc_ICS; /* x00C8 cause set */
+ uint32_t esc_IMS; /* x00D0 mask set/read */
+ uint32_t esc_IMC; /* x00D8 mask clear */
+
+ /* Transmit */
+ union e1000_tx_udesc *esc_txdesc;
+ struct e1000_context_desc esc_txctx;
+ pthread_t esc_tx_tid;
+ pthread_cond_t esc_tx_cond;
+ int esc_tx_enabled;
+ int esc_tx_active;
+ uint32_t esc_TXCW; /* x0178 transmit config */
+ uint32_t esc_TCTL; /* x0400 transmit ctl */
+ uint32_t esc_TIPG; /* x0410 inter-packet gap */
+ uint16_t esc_AIT; /* x0458 Adaptive Interframe Throttle */
+ uint64_t esc_tdba; /* verified 64-bit desc table addr */
+ uint32_t esc_TDBAL; /* x3800 desc table addr, low bits */
+ uint32_t esc_TDBAH; /* x3804 desc table addr, hi 32-bits */
+ uint32_t esc_TDLEN; /* x3808 # descriptors in bytes */
+ uint16_t esc_TDH; /* x3810 desc table head idx */
+ uint16_t esc_TDHr; /* internal read version of TDH */
+ uint16_t esc_TDT; /* x3818 desc table tail idx */
+ uint32_t esc_TIDV; /* x3820 intr delay */
+ uint32_t esc_TXDCTL; /* x3828 desc control */
+ uint32_t esc_TADV; /* x382C intr absolute delay */
+
+ /* L2 frame acceptance */
+ struct eth_uni esc_uni[16]; /* 16 x unicast MAC addresses */
+ uint32_t esc_fmcast[128]; /* Multicast filter bit-match */
+ uint32_t esc_fvlan[128]; /* VLAN 4096-bit filter */
+
+ /* Receive */
+ struct e1000_rx_desc *esc_rxdesc;
+ pthread_cond_t esc_rx_cond;
+ int esc_rx_enabled;
+ int esc_rx_active;
+ int esc_rx_loopback;
+ uint32_t esc_RCTL; /* x0100 receive ctl */
+ uint32_t esc_FCRTL; /* x2160 flow cntl thresh, low */
+ uint32_t esc_FCRTH; /* x2168 flow cntl thresh, hi */
+ uint64_t esc_rdba; /* verified 64-bit desc table addr */
+ uint32_t esc_RDBAL; /* x2800 desc table addr, low bits */
+ uint32_t esc_RDBAH; /* x2804 desc table addr, hi 32-bits*/
+ uint32_t esc_RDLEN; /* x2808 #descriptors */
+ uint16_t esc_RDH; /* x2810 desc table head idx */
+ uint16_t esc_RDT; /* x2818 desc table tail idx */
+ uint32_t esc_RDTR; /* x2820 intr delay */
+ uint32_t esc_RXDCTL; /* x2828 desc control */
+ uint32_t esc_RADV; /* x282C intr absolute delay */
+ uint32_t esc_RSRPD; /* x2C00 recv small packet detect */
+ uint32_t esc_RXCSUM; /* x5000 receive cksum ctl */
+
+ /* IO Port register access */
+ uint32_t io_addr;
+
+ /* Shadow copy of MDIC */
+ uint32_t mdi_control;
+ /* Shadow copy of EECD */
+ uint32_t eeprom_control;
+ /* Latest NVM in/out */
+ uint16_t nvm_data;
+ uint16_t nvm_opaddr;
+ /* stats */
+ uint32_t missed_pkt_count; /* dropped for no room in rx queue */
+ uint32_t pkt_rx_by_size[6];
+ uint32_t pkt_tx_by_size[6];
+ uint32_t good_pkt_rx_count;
+ uint32_t bcast_pkt_rx_count;
+ uint32_t mcast_pkt_rx_count;
+ uint32_t good_pkt_tx_count;
+ uint32_t bcast_pkt_tx_count;
+ uint32_t mcast_pkt_tx_count;
+ uint32_t oversize_rx_count;
+ uint32_t tso_tx_count;
+ uint64_t good_octets_rx;
+ uint64_t good_octets_tx;
+ uint64_t missed_octets; /* counts missed and oversized */
+
+ uint8_t nvm_bits:6; /* number of bits remaining in/out */
+ uint8_t nvm_mode:2;
+#define E82545_NVM_MODE_OPADDR 0x0
+#define E82545_NVM_MODE_DATAIN 0x1
+#define E82545_NVM_MODE_DATAOUT 0x2
+ /* EEPROM data */
+ uint16_t eeprom_data[E82545_NVM_EEPROM_SIZE];
+};
+
+static void e82545_reset(struct e82545_softc *sc, int dev);
+static void e82545_rx_enable(struct e82545_softc *sc);
+static void e82545_rx_disable(struct e82545_softc *sc);
+#ifdef __FreeBSD__
+static void e82545_tap_callback(int fd, enum ev_type type, void *param);
+#endif
+static void e82545_tx_start(struct e82545_softc *sc);
+static void e82545_tx_enable(struct e82545_softc *sc);
+static void e82545_tx_disable(struct e82545_softc *sc);
+
+static inline int
+e82545_size_stat_index(uint32_t size)
+{
+ if (size <= 64) {
+ return 0;
+ } else if (size >= 1024) {
+ return 5;
+ } else {
+ /* should be 1-4 */
+ return (ffs(size) - 6);
+ }
+}
+
+static void
+e82545_init_eeprom(struct e82545_softc *sc)
+{
+ uint16_t checksum, i;
+
+ /* mac addr */
+ sc->eeprom_data[NVM_MAC_ADDR] = ((uint16_t)sc->esc_mac.octet[0]) |
+ (((uint16_t)sc->esc_mac.octet[1]) << 8);
+ sc->eeprom_data[NVM_MAC_ADDR+1] = ((uint16_t)sc->esc_mac.octet[2]) |
+ (((uint16_t)sc->esc_mac.octet[3]) << 8);
+ sc->eeprom_data[NVM_MAC_ADDR+2] = ((uint16_t)sc->esc_mac.octet[4]) |
+ (((uint16_t)sc->esc_mac.octet[5]) << 8);
+
+ /* pci ids */
+ sc->eeprom_data[NVM_SUB_DEV_ID] = E82545_SUBDEV_ID;
+ sc->eeprom_data[NVM_SUB_VEN_ID] = E82545_VENDOR_ID_INTEL;
+ sc->eeprom_data[NVM_DEV_ID] = E82545_DEV_ID_82545EM_COPPER;
+ sc->eeprom_data[NVM_VEN_ID] = E82545_VENDOR_ID_INTEL;
+
+ /* fill in the checksum */
+ checksum = 0;
+ for (i = 0; i < NVM_CHECKSUM_REG; i++) {
+ checksum += sc->eeprom_data[i];
+ }
+ checksum = NVM_SUM - checksum;
+ sc->eeprom_data[NVM_CHECKSUM_REG] = checksum;
+ DPRINTF("eeprom checksum: 0x%x\r\n", checksum);
+}
+
+static void
+e82545_write_mdi(struct e82545_softc *sc, uint8_t reg_addr,
+ uint8_t phy_addr, uint32_t data)
+{
+ DPRINTF("Write mdi reg:0x%x phy:0x%x data: 0x%x\r\n", reg_addr, phy_addr, data);
+}
+
+static uint32_t
+e82545_read_mdi(struct e82545_softc *sc, uint8_t reg_addr,
+ uint8_t phy_addr)
+{
+ //DPRINTF("Read mdi reg:0x%x phy:0x%x\r\n", reg_addr, phy_addr);
+ switch (reg_addr) {
+ case PHY_STATUS:
+ return (MII_SR_LINK_STATUS | MII_SR_AUTONEG_CAPS |
+ MII_SR_AUTONEG_COMPLETE);
+ case PHY_AUTONEG_ADV:
+ return NWAY_AR_SELECTOR_FIELD;
+ case PHY_LP_ABILITY:
+ return 0;
+ case PHY_1000T_STATUS:
+ return (SR_1000T_LP_FD_CAPS | SR_1000T_REMOTE_RX_STATUS |
+ SR_1000T_LOCAL_RX_STATUS);
+ case PHY_ID1:
+ return (M88E1011_I_PHY_ID >> 16) & 0xFFFF;
+ case PHY_ID2:
+ return (M88E1011_I_PHY_ID | E82545_REVISION_4) & 0xFFFF;
+ default:
+ DPRINTF("Unknown mdi read reg:0x%x phy:0x%x\r\n", reg_addr, phy_addr);
+ return 0;
+ }
+ /* not reached */
+}
+
+static void
+e82545_eecd_strobe(struct e82545_softc *sc)
+{
+ /* Microwire state machine */
+ /*
+ DPRINTF("eeprom state machine srtobe "
+ "0x%x 0x%x 0x%x 0x%x\r\n",
+ sc->nvm_mode, sc->nvm_bits,
+ sc->nvm_opaddr, sc->nvm_data);*/
+
+ if (sc->nvm_bits == 0) {
+ DPRINTF("eeprom state machine not expecting data! "
+ "0x%x 0x%x 0x%x 0x%x\r\n",
+ sc->nvm_mode, sc->nvm_bits,
+ sc->nvm_opaddr, sc->nvm_data);
+ return;
+ }
+ sc->nvm_bits--;
+ if (sc->nvm_mode == E82545_NVM_MODE_DATAOUT) {
+ /* shifting out */
+ if (sc->nvm_data & 0x8000) {
+ sc->eeprom_control |= E1000_EECD_DO;
+ } else {
+ sc->eeprom_control &= ~E1000_EECD_DO;
+ }
+ sc->nvm_data <<= 1;
+ if (sc->nvm_bits == 0) {
+ /* read done, back to opcode mode. */
+ sc->nvm_opaddr = 0;
+ sc->nvm_mode = E82545_NVM_MODE_OPADDR;
+ sc->nvm_bits = E82545_NVM_OPADDR_BITS;
+ }
+ } else if (sc->nvm_mode == E82545_NVM_MODE_DATAIN) {
+ /* shifting in */
+ sc->nvm_data <<= 1;
+ if (sc->eeprom_control & E1000_EECD_DI) {
+ sc->nvm_data |= 1;
+ }
+ if (sc->nvm_bits == 0) {
+ /* eeprom write */
+ uint16_t op = sc->nvm_opaddr & E82545_NVM_OPCODE_MASK;
+ uint16_t addr = sc->nvm_opaddr & E82545_NVM_ADDR_MASK;
+ if (op != E82545_NVM_OPCODE_WRITE) {
+ DPRINTF("Illegal eeprom write op 0x%x\r\n",
+ sc->nvm_opaddr);
+ } else if (addr >= E82545_NVM_EEPROM_SIZE) {
+ DPRINTF("Illegal eeprom write addr 0x%x\r\n",
+ sc->nvm_opaddr);
+ } else {
+ DPRINTF("eeprom write eeprom[0x%x] = 0x%x\r\n",
+ addr, sc->nvm_data);
+ sc->eeprom_data[addr] = sc->nvm_data;
+ }
+ /* back to opcode mode */
+ sc->nvm_opaddr = 0;
+ sc->nvm_mode = E82545_NVM_MODE_OPADDR;
+ sc->nvm_bits = E82545_NVM_OPADDR_BITS;
+ }
+ } else if (sc->nvm_mode == E82545_NVM_MODE_OPADDR) {
+ sc->nvm_opaddr <<= 1;
+ if (sc->eeprom_control & E1000_EECD_DI) {
+ sc->nvm_opaddr |= 1;
+ }
+ if (sc->nvm_bits == 0) {
+ uint16_t op = sc->nvm_opaddr & E82545_NVM_OPCODE_MASK;
+ switch (op) {
+ case E82545_NVM_OPCODE_EWEN:
+ DPRINTF("eeprom write enable: 0x%x\r\n",
+ sc->nvm_opaddr);
+ /* back to opcode mode */
+ sc->nvm_opaddr = 0;
+ sc->nvm_mode = E82545_NVM_MODE_OPADDR;
+ sc->nvm_bits = E82545_NVM_OPADDR_BITS;
+ break;
+ case E82545_NVM_OPCODE_READ:
+ {
+ uint16_t addr = sc->nvm_opaddr &
+ E82545_NVM_ADDR_MASK;
+ sc->nvm_mode = E82545_NVM_MODE_DATAOUT;
+ sc->nvm_bits = E82545_NVM_DATA_BITS;
+ if (addr < E82545_NVM_EEPROM_SIZE) {
+ sc->nvm_data = sc->eeprom_data[addr];
+ DPRINTF("eeprom read: eeprom[0x%x] = 0x%x\r\n",
+ addr, sc->nvm_data);
+ } else {
+ DPRINTF("eeprom illegal read: 0x%x\r\n",
+ sc->nvm_opaddr);
+ sc->nvm_data = 0;
+ }
+ break;
+ }
+ case E82545_NVM_OPCODE_WRITE:
+ sc->nvm_mode = E82545_NVM_MODE_DATAIN;
+ sc->nvm_bits = E82545_NVM_DATA_BITS;
+ sc->nvm_data = 0;
+ break;
+ default:
+ DPRINTF("eeprom unknown op: 0x%x\r\r",
+ sc->nvm_opaddr);
+ /* back to opcode mode */
+ sc->nvm_opaddr = 0;
+ sc->nvm_mode = E82545_NVM_MODE_OPADDR;
+ sc->nvm_bits = E82545_NVM_OPADDR_BITS;
+ }
+ }
+ } else {
+ DPRINTF("eeprom state machine wrong state! "
+ "0x%x 0x%x 0x%x 0x%x\r\n",
+ sc->nvm_mode, sc->nvm_bits,
+ sc->nvm_opaddr, sc->nvm_data);
+ }
+}
+
+#ifdef __FreeBSD__
+static void
+e82545_itr_callback(int fd, enum ev_type type, void *param)
+{
+ uint32_t new;
+ struct e82545_softc *sc = param;
+
+ pthread_mutex_lock(&sc->esc_mtx);
+ new = sc->esc_ICR & sc->esc_IMS;
+ if (new && !sc->esc_irq_asserted) {
+ DPRINTF("itr callback: lintr assert %x\r\n", new);
+ sc->esc_irq_asserted = 1;
+ pci_lintr_assert(sc->esc_pi);
+ } else {
+ mevent_delete(sc->esc_mevpitr);
+ sc->esc_mevpitr = NULL;
+ }
+ pthread_mutex_unlock(&sc->esc_mtx);
+}
+#endif
+
+static void
+e82545_icr_assert(struct e82545_softc *sc, uint32_t bits)
+{
+ uint32_t new;
+
+ DPRINTF("icr assert: 0x%x\r\n", bits);
+
+ /*
+ * An interrupt is only generated if bits are set that
+ * aren't already in the ICR, these bits are unmasked,
+ * and there isn't an interrupt already pending.
+ */
+ new = bits & ~sc->esc_ICR & sc->esc_IMS;
+ sc->esc_ICR |= bits;
+
+ if (new == 0) {
+ DPRINTF("icr assert: masked %x, ims %x\r\n", new, sc->esc_IMS);
+ } else if (sc->esc_mevpitr != NULL) {
+ DPRINTF("icr assert: throttled %x, ims %x\r\n", new, sc->esc_IMS);
+ } else if (!sc->esc_irq_asserted) {
+ DPRINTF("icr assert: lintr assert %x\r\n", new);
+ sc->esc_irq_asserted = 1;
+ pci_lintr_assert(sc->esc_pi);
+ if (sc->esc_ITR != 0) {
+#ifdef __FreeBSD__
+ sc->esc_mevpitr = mevent_add(
+ (sc->esc_ITR + 3905) / 3906, /* 256ns -> 1ms */
+ EVF_TIMER, e82545_itr_callback, sc);
+#endif
+ }
+ }
+}
+
+static void
+e82545_ims_change(struct e82545_softc *sc, uint32_t bits)
+{
+ uint32_t new;
+
+ /*
+ * Changing the mask may allow previously asserted
+ * but masked interrupt requests to generate an interrupt.
+ */
+ new = bits & sc->esc_ICR & ~sc->esc_IMS;
+ sc->esc_IMS |= bits;
+
+ if (new == 0) {
+ DPRINTF("ims change: masked %x, ims %x\r\n", new, sc->esc_IMS);
+ } else if (sc->esc_mevpitr != NULL) {
+ DPRINTF("ims change: throttled %x, ims %x\r\n", new, sc->esc_IMS);
+ } else if (!sc->esc_irq_asserted) {
+ DPRINTF("ims change: lintr assert %x\n\r", new);
+ sc->esc_irq_asserted = 1;
+ pci_lintr_assert(sc->esc_pi);
+ if (sc->esc_ITR != 0) {
+#ifdef __FreeBSD__
+ sc->esc_mevpitr = mevent_add(
+ (sc->esc_ITR + 3905) / 3906, /* 256ns -> 1ms */
+ EVF_TIMER, e82545_itr_callback, sc);
+#endif
+ }
+ }
+}
+
+static void
+e82545_icr_deassert(struct e82545_softc *sc, uint32_t bits)
+{
+
+ DPRINTF("icr deassert: 0x%x\r\n", bits);
+ sc->esc_ICR &= ~bits;
+
+ /*
+ * If there are no longer any interrupt sources and there
+ * was an asserted interrupt, clear it
+ */
+ if (sc->esc_irq_asserted && !(sc->esc_ICR & sc->esc_IMS)) {
+ DPRINTF("icr deassert: lintr deassert %x\r\n", bits);
+ pci_lintr_deassert(sc->esc_pi);
+ sc->esc_irq_asserted = 0;
+ }
+}
+
+static void
+e82545_intr_write(struct e82545_softc *sc, uint32_t offset, uint32_t value)
+{
+
+ DPRINTF("intr_write: off %x, val %x\n\r", offset, value);
+
+ switch (offset) {
+ case E1000_ICR:
+ e82545_icr_deassert(sc, value);
+ break;
+ case E1000_ITR:
+ sc->esc_ITR = value;
+ break;
+ case E1000_ICS:
+ sc->esc_ICS = value; /* not used: store for debug */
+ e82545_icr_assert(sc, value);
+ break;
+ case E1000_IMS:
+ e82545_ims_change(sc, value);
+ break;
+ case E1000_IMC:
+ sc->esc_IMC = value; /* for debug */
+ sc->esc_IMS &= ~value;
+ // XXX clear interrupts if all ICR bits now masked
+ // and interrupt was pending ?
+ break;
+ default:
+ break;
+ }
+}
+
+static uint32_t
+e82545_intr_read(struct e82545_softc *sc, uint32_t offset)
+{
+ uint32_t retval;
+
+ retval = 0;
+
+ DPRINTF("intr_read: off %x\n\r", offset);
+
+ switch (offset) {
+ case E1000_ICR:
+ retval = sc->esc_ICR;
+ sc->esc_ICR = 0;
+ e82545_icr_deassert(sc, ~0);
+ break;
+ case E1000_ITR:
+ retval = sc->esc_ITR;
+ break;
+ case E1000_ICS:
+ /* write-only register */
+ break;
+ case E1000_IMS:
+ retval = sc->esc_IMS;
+ break;
+ case E1000_IMC:
+ /* write-only register */
+ break;
+ default:
+ break;
+ }
+
+ return (retval);
+}
+
+static void
+e82545_devctl(struct e82545_softc *sc, uint32_t val)
+{
+
+ sc->esc_CTRL = val & ~E1000_CTRL_RST;
+
+ if (val & E1000_CTRL_RST) {
+ DPRINTF("e1k: s/w reset, ctl %x\n", val);
+ e82545_reset(sc, 1);
+ }
+ /* XXX check for phy reset ? */
+}
+
+static void
+e82545_rx_update_rdba(struct e82545_softc *sc)
+{
+
+ /* XXX verify desc base/len within phys mem range */
+ sc->esc_rdba = (uint64_t)sc->esc_RDBAH << 32 |
+ sc->esc_RDBAL;
+
+ /* Cache host mapping of guest descriptor array */
+ sc->esc_rxdesc = paddr_guest2host(sc->esc_ctx,
+ sc->esc_rdba, sc->esc_RDLEN);
+}
+
+static void
+e82545_rx_ctl(struct e82545_softc *sc, uint32_t val)
+{
+ int on;
+
+ on = ((val & E1000_RCTL_EN) == E1000_RCTL_EN);
+
+ /* Save RCTL after stripping reserved bits 31:27,24,21,14,11:10,0 */
+ sc->esc_RCTL = val & ~0xF9204c01;
+
+ DPRINTF("rx_ctl - %s RCTL %x, val %x\n",
+ on ? "on" : "off", sc->esc_RCTL, val);
+
+ /* state change requested */
+ if (on != sc->esc_rx_enabled) {
+ if (on) {
+ /* Catch disallowed/unimplemented settings */
+ //assert(!(val & E1000_RCTL_LBM_TCVR));
+
+ if (sc->esc_RCTL & E1000_RCTL_LBM_TCVR) {
+ sc->esc_rx_loopback = 1;
+ } else {
+ sc->esc_rx_loopback = 0;
+ }
+
+ e82545_rx_update_rdba(sc);
+ e82545_rx_enable(sc);
+ } else {
+ e82545_rx_disable(sc);
+ sc->esc_rx_loopback = 0;
+ sc->esc_rdba = 0;
+ sc->esc_rxdesc = NULL;
+ }
+ }
+}
+
+static void
+e82545_tx_update_tdba(struct e82545_softc *sc)
+{
+
+ /* XXX verify desc base/len within phys mem range */
+ sc->esc_tdba = (uint64_t)sc->esc_TDBAH << 32 | sc->esc_TDBAL;
+
+ /* Cache host mapping of guest descriptor array */
+ sc->esc_txdesc = paddr_guest2host(sc->esc_ctx, sc->esc_tdba,
+ sc->esc_TDLEN);
+}
+
+static void
+e82545_tx_ctl(struct e82545_softc *sc, uint32_t val)
+{
+ int on;
+
+ on = ((val & E1000_TCTL_EN) == E1000_TCTL_EN);
+
+ /* ignore TCTL_EN settings that don't change state */
+ if (on == sc->esc_tx_enabled)
+ return;
+
+ if (on) {
+ e82545_tx_update_tdba(sc);
+ e82545_tx_enable(sc);
+ } else {
+ e82545_tx_disable(sc);
+ sc->esc_tdba = 0;
+ sc->esc_txdesc = NULL;
+ }
+
+ /* Save TCTL value after stripping reserved bits 31:25,23,2,0 */
+ sc->esc_TCTL = val & ~0xFE800005;
+}
+
+int
+e82545_bufsz(uint32_t rctl)
+{
+
+ switch (rctl & (E1000_RCTL_BSEX | E1000_RCTL_SZ_256)) {
+ case (E1000_RCTL_SZ_2048): return (2048);
+ case (E1000_RCTL_SZ_1024): return (1024);
+ case (E1000_RCTL_SZ_512): return (512);
+ case (E1000_RCTL_SZ_256): return (256);
+ case (E1000_RCTL_BSEX|E1000_RCTL_SZ_16384): return (16384);
+ case (E1000_RCTL_BSEX|E1000_RCTL_SZ_8192): return (8192);
+ case (E1000_RCTL_BSEX|E1000_RCTL_SZ_4096): return (4096);
+ }
+ return (256); /* Forbidden value. */
+}
+
+#ifdef __FreeBSD__
+static uint8_t dummybuf[2048];
+
+/* XXX one packet at a time until this is debugged */
+static void
+e82545_tap_callback(int fd, enum ev_type type, void *param)
+{
+ struct e82545_softc *sc = param;
+ struct e1000_rx_desc *rxd;
+ struct iovec vec[64];
+ int left, len, lim, maxpktsz, maxpktdesc, bufsz, i, n, size;
+ uint32_t cause = 0;
+ uint16_t *tp, tag, head;
+
+ pthread_mutex_lock(&sc->esc_mtx);
+ DPRINTF("rx_run: head %x, tail %x\r\n", sc->esc_RDH, sc->esc_RDT);
+
+ if (!sc->esc_rx_enabled || sc->esc_rx_loopback) {
+ DPRINTF("rx disabled (!%d || %d) -- packet(s) dropped\r\n",
+ sc->esc_rx_enabled, sc->esc_rx_loopback);
+ while (read(sc->esc_tapfd, dummybuf, sizeof(dummybuf)) > 0) {
+ }
+ goto done1;
+ }
+ bufsz = e82545_bufsz(sc->esc_RCTL);
+ maxpktsz = (sc->esc_RCTL & E1000_RCTL_LPE) ? 16384 : 1522;
+ maxpktdesc = (maxpktsz + bufsz - 1) / bufsz;
+ size = sc->esc_RDLEN / 16;
+ head = sc->esc_RDH;
+ left = (size + sc->esc_RDT - head) % size;
+ if (left < maxpktdesc) {
+ DPRINTF("rx overflow (%d < %d) -- packet(s) dropped\r\n",
+ left, maxpktdesc);
+ while (read(sc->esc_tapfd, dummybuf, sizeof(dummybuf)) > 0) {
+ }
+ goto done1;
+ }
+
+ sc->esc_rx_active = 1;
+ pthread_mutex_unlock(&sc->esc_mtx);
+
+ for (lim = size / 4; lim > 0 && left >= maxpktdesc; lim -= n) {
+
+ /* Grab rx descriptor pointed to by the head pointer */
+ for (i = 0; i < maxpktdesc; i++) {
+ rxd = &sc->esc_rxdesc[(head + i) % size];
+ vec[i].iov_base = paddr_guest2host(sc->esc_ctx,
+ rxd->buffer_addr, bufsz);
+ vec[i].iov_len = bufsz;
+ }
+ len = readv(sc->esc_tapfd, vec, maxpktdesc);
+ if (len <= 0) {
+ DPRINTF("tap: readv() returned %d\n", len);
+ goto done;
+ }
+
+ /*
+ * Adjust the packet length based on whether the CRC needs
+ * to be stripped or if the packet is less than the minimum
+ * eth packet size.
+ */
+ if (len < ETHER_MIN_LEN - ETHER_CRC_LEN)
+ len = ETHER_MIN_LEN - ETHER_CRC_LEN;
+ if (!(sc->esc_RCTL & E1000_RCTL_SECRC))
+ len += ETHER_CRC_LEN;
+ n = (len + bufsz - 1) / bufsz;
+
+ DPRINTF("packet read %d bytes, %d segs, head %d\r\n",
+ len, n, head);
+
+ /* Apply VLAN filter. */
+ tp = (uint16_t *)vec[0].iov_base + 6;
+ if ((sc->esc_RCTL & E1000_RCTL_VFE) &&
+ (ntohs(tp[0]) == sc->esc_VET)) {
+ tag = ntohs(tp[1]) & 0x0fff;
+ if ((sc->esc_fvlan[tag >> 5] &
+ (1 << (tag & 0x1f))) != 0) {
+ DPRINTF("known VLAN %d\r\n", tag);
+ } else {
+ DPRINTF("unknown VLAN %d\r\n", tag);
+ n = 0;
+ continue;
+ }
+ }
+
+ /* Update all consumed descriptors. */
+ for (i = 0; i < n - 1; i++) {
+ rxd = &sc->esc_rxdesc[(head + i) % size];
+ rxd->length = bufsz;
+ rxd->csum = 0;
+ rxd->errors = 0;
+ rxd->special = 0;
+ rxd->status = E1000_RXD_STAT_DD;
+ }
+ rxd = &sc->esc_rxdesc[(head + i) % size];
+ rxd->length = len % bufsz;
+ rxd->csum = 0;
+ rxd->errors = 0;
+ rxd->special = 0;
+ /* XXX signal no checksum for now */
+ rxd->status = E1000_RXD_STAT_PIF | E1000_RXD_STAT_IXSM |
+ E1000_RXD_STAT_EOP | E1000_RXD_STAT_DD;
+
+ /* Schedule receive interrupts. */
+ if (len <= sc->esc_RSRPD) {
+ cause |= E1000_ICR_SRPD | E1000_ICR_RXT0;
+ } else {
+ /* XXX: RDRT and RADV timers should be here. */
+ cause |= E1000_ICR_RXT0;
+ }
+
+ head = (head + n) % size;
+ left -= n;
+ }
+
+done:
+ pthread_mutex_lock(&sc->esc_mtx);
+ sc->esc_rx_active = 0;
+ if (sc->esc_rx_enabled == 0)
+ pthread_cond_signal(&sc->esc_rx_cond);
+
+ sc->esc_RDH = head;
+ /* Respect E1000_RCTL_RDMTS */
+ left = (size + sc->esc_RDT - head) % size;
+ if (left < (size >> (((sc->esc_RCTL >> 8) & 3) + 1)))
+ cause |= E1000_ICR_RXDMT0;
+ /* Assert all accumulated interrupts. */
+ if (cause != 0)
+ e82545_icr_assert(sc, cause);
+done1:
+ DPRINTF("rx_run done: head %x, tail %x\r\n", sc->esc_RDH, sc->esc_RDT);
+ pthread_mutex_unlock(&sc->esc_mtx);
+}
+#endif
+
+static uint16_t
+e82545_carry(uint32_t sum)
+{
+
+ sum = (sum & 0xFFFF) + (sum >> 16);
+ if (sum > 0xFFFF)
+ sum -= 0xFFFF;
+ return (sum);
+}
+
+static uint16_t
+#ifdef __FreeBSD__
+e82545_buf_checksum(uint8_t *buf, int len)
+#else
+e82545_buf_checksum(caddr_t buf, int len)
+#endif
+{
+ int i;
+ uint32_t sum = 0;
+
+ /* Checksum all the pairs of bytes first... */
+ for (i = 0; i < (len & ~1U); i += 2)
+ sum += *((u_int16_t *)(buf + i));
+
+ /*
+ * If there's a single byte left over, checksum it, too.
+ * Network byte order is big-endian, so the remaining byte is
+ * the high byte.
+ */
+ if (i < len)
+ sum += htons(buf[i] << 8);
+
+ return (e82545_carry(sum));
+}
+
+static uint16_t
+e82545_iov_checksum(struct iovec *iov, int iovcnt, int off, int len)
+{
+ int now, odd;
+ uint32_t sum = 0, s;
+
+ /* Skip completely unneeded vectors. */
+ while (iovcnt > 0 && iov->iov_len <= off && off > 0) {
+ off -= iov->iov_len;
+ iov++;
+ iovcnt--;
+ }
+
+ /* Calculate checksum of requested range. */
+ odd = 0;
+ while (len > 0 && iovcnt > 0) {
+ now = MIN(len, iov->iov_len - off);
+ s = e82545_buf_checksum(iov->iov_base + off, now);
+ sum += odd ? (s << 8) : s;
+ odd ^= (now & 1);
+ len -= now;
+ off = 0;
+ iov++;
+ iovcnt--;
+ }
+
+ return (e82545_carry(sum));
+}
+
+/*
+ * Return the transmit descriptor type.
+ */
+int
+e82545_txdesc_type(uint32_t lower)
+{
+ int type;
+
+ type = 0;
+
+ if (lower & E1000_TXD_CMD_DEXT)
+ type = lower & E1000_TXD_MASK;
+
+ return (type);
+}
+
+static void
+e82545_transmit_checksum(struct iovec *iov, int iovcnt, struct ck_info *ck)
+{
+ uint16_t cksum;
+ int cklen;
+
+ DPRINTF("tx cksum: iovcnt/s/off/len %d/%d/%d/%d\r\n",
+ iovcnt, ck->ck_start, ck->ck_off, ck->ck_len);
+ cklen = ck->ck_len ? ck->ck_len - ck->ck_start + 1 : INT_MAX;
+ cksum = e82545_iov_checksum(iov, iovcnt, ck->ck_start, cklen);
+ *(uint16_t *)((uint8_t *)iov[0].iov_base + ck->ck_off) = ~cksum;
+}
+
+static void
+e82545_transmit_backend(struct e82545_softc *sc, struct iovec *iov, int iovcnt)
+{
+
+ if (sc->esc_tapfd == -1)
+ return;
+
+ (void) writev(sc->esc_tapfd, iov, iovcnt);
+}
+
+static void
+e82545_transmit_done(struct e82545_softc *sc, uint16_t head, uint16_t tail,
+ uint16_t dsize, int *tdwb)
+{
+ union e1000_tx_udesc *dsc;
+
+ for ( ; head != tail; head = (head + 1) % dsize) {
+ dsc = &sc->esc_txdesc[head];
+ if (dsc->td.lower.data & E1000_TXD_CMD_RS) {
+ dsc->td.upper.data |= E1000_TXD_STAT_DD;
+ *tdwb = 1;
+ }
+ }
+}
+
+static int
+e82545_transmit(struct e82545_softc *sc, uint16_t head, uint16_t tail,
+ uint16_t dsize, uint16_t *rhead, int *tdwb)
+{
+#ifdef __FreeBSD__
+ uint8_t *hdr, *hdrp;
+#else
+ caddr_t hdr, hdrp;
+#endif
+ struct iovec iovb[I82545_MAX_TXSEGS + 2];
+ struct iovec tiov[I82545_MAX_TXSEGS + 2];
+ struct e1000_context_desc *cd;
+ struct ck_info ckinfo[2];
+ struct iovec *iov;
+ union e1000_tx_udesc *dsc;
+ int desc, dtype, len, ntype, iovcnt, tlen, hdrlen, vlen, tcp, tso;
+ int mss, paylen, seg, tiovcnt, left, now, nleft, nnow, pv, pvoff;
+ uint32_t tcpsum, tcpseq;
+ uint16_t ipcs, tcpcs, ipid, ohead;
+
+ ckinfo[0].ck_valid = ckinfo[1].ck_valid = 0;
+ iovcnt = 0;
+ tlen = 0;
+ ntype = 0;
+ tso = 0;
+ ohead = head;
+ hdr = NULL;
+
+ /* iovb[0/1] may be used for writable copy of headers. */
+ iov = &iovb[2];
+
+ for (desc = 0; ; desc++, head = (head + 1) % dsize) {
+ if (head == tail) {
+ *rhead = head;
+ return (0);
+ }
+ dsc = &sc->esc_txdesc[head];
+ dtype = e82545_txdesc_type(dsc->td.lower.data);
+
+ if (desc == 0) {
+ switch (dtype) {
+ case E1000_TXD_TYP_C:
+ DPRINTF("tx ctxt desc idx %d: %016jx "
+ "%08x%08x\r\n",
+ head, dsc->td.buffer_addr,
+ dsc->td.upper.data, dsc->td.lower.data);
+ /* Save context and return */
+ sc->esc_txctx = dsc->cd;
+ goto done;
+ case E1000_TXD_TYP_L:
+ DPRINTF("tx legacy desc idx %d: %08x%08x\r\n",
+ head, dsc->td.upper.data, dsc->td.lower.data);
+ /*
+ * legacy cksum start valid in first descriptor
+ */
+ ntype = dtype;
+ ckinfo[0].ck_start = dsc->td.upper.fields.css;
+ break;
+ case E1000_TXD_TYP_D:
+ DPRINTF("tx data desc idx %d: %08x%08x\r\n",
+ head, dsc->td.upper.data, dsc->td.lower.data);
+ ntype = dtype;
+ break;
+ default:
+ break;
+ }
+ } else {
+ /* Descriptor type must be consistent */
+ assert(dtype == ntype);
+ DPRINTF("tx next desc idx %d: %08x%08x\r\n",
+ head, dsc->td.upper.data, dsc->td.lower.data);
+ }
+
+ len = (dtype == E1000_TXD_TYP_L) ? dsc->td.lower.flags.length :
+ dsc->dd.lower.data & 0xFFFFF;
+
+ if (len > 0) {
+ /* Strip checksum supplied by guest. */
+ if ((dsc->td.lower.data & E1000_TXD_CMD_EOP) != 0 &&
+ (dsc->td.lower.data & E1000_TXD_CMD_IFCS) == 0)
+ len -= 2;
+ tlen += len;
+ if (iovcnt < I82545_MAX_TXSEGS) {
+ iov[iovcnt].iov_base = paddr_guest2host(
+ sc->esc_ctx, dsc->td.buffer_addr, len);
+ iov[iovcnt].iov_len = len;
+ }
+ iovcnt++;
+ }
+
+ /*
+ * Pull out info that is valid in the final descriptor
+ * and exit descriptor loop.
+ */
+ if (dsc->td.lower.data & E1000_TXD_CMD_EOP) {
+ if (dtype == E1000_TXD_TYP_L) {
+ if (dsc->td.lower.data & E1000_TXD_CMD_IC) {
+ ckinfo[0].ck_valid = 1;
+ ckinfo[0].ck_off =
+ dsc->td.lower.flags.cso;
+ ckinfo[0].ck_len = 0;
+ }
+ } else {
+ cd = &sc->esc_txctx;
+ if (dsc->dd.lower.data & E1000_TXD_CMD_TSE)
+ tso = 1;
+ if (dsc->dd.upper.fields.popts &
+ E1000_TXD_POPTS_IXSM)
+ ckinfo[0].ck_valid = 1;
+ if (dsc->dd.upper.fields.popts &
+ E1000_TXD_POPTS_IXSM || tso) {
+ ckinfo[0].ck_start =
+ cd->lower_setup.ip_fields.ipcss;
+ ckinfo[0].ck_off =
+ cd->lower_setup.ip_fields.ipcso;
+ ckinfo[0].ck_len =
+ cd->lower_setup.ip_fields.ipcse;
+ }
+ if (dsc->dd.upper.fields.popts &
+ E1000_TXD_POPTS_TXSM)
+ ckinfo[1].ck_valid = 1;
+ if (dsc->dd.upper.fields.popts &
+ E1000_TXD_POPTS_TXSM || tso) {
+ ckinfo[1].ck_start =
+ cd->upper_setup.tcp_fields.tucss;
+ ckinfo[1].ck_off =
+ cd->upper_setup.tcp_fields.tucso;
+ ckinfo[1].ck_len =
+ cd->upper_setup.tcp_fields.tucse;
+ }
+ }
+ break;
+ }
+ }
+
+ if (iovcnt > I82545_MAX_TXSEGS) {
+ WPRINTF("tx too many descriptors (%d > %d) -- dropped\r\n",
+ iovcnt, I82545_MAX_TXSEGS);
+ goto done;
+ }
+
+ hdrlen = vlen = 0;
+ /* Estimate writable space for VLAN header insertion. */
+ if ((sc->esc_CTRL & E1000_CTRL_VME) &&
+ (dsc->td.lower.data & E1000_TXD_CMD_VLE)) {
+ hdrlen = ETHER_ADDR_LEN*2;
+ vlen = ETHER_VLAN_ENCAP_LEN;
+ }
+ if (!tso) {
+ /* Estimate required writable space for checksums. */
+ if (ckinfo[0].ck_valid)
+ hdrlen = MAX(hdrlen, ckinfo[0].ck_off + 2);
+ if (ckinfo[1].ck_valid)
+ hdrlen = MAX(hdrlen, ckinfo[1].ck_off + 2);
+ /* Round up writable space to the first vector. */
+ if (hdrlen != 0 && iov[0].iov_len > hdrlen &&
+ iov[0].iov_len < hdrlen + 100)
+ hdrlen = iov[0].iov_len;
+ } else {
+ /* In case of TSO header length provided by software. */
+ hdrlen = sc->esc_txctx.tcp_seg_setup.fields.hdr_len;
+ }
+
+ /* Allocate, fill and prepend writable header vector. */
+ if (hdrlen != 0) {
+ hdr = __builtin_alloca(hdrlen + vlen);
+ hdr += vlen;
+ for (left = hdrlen, hdrp = hdr; left > 0;
+ left -= now, hdrp += now) {
+ now = MIN(left, iov->iov_len);
+ memcpy(hdrp, iov->iov_base, now);
+ iov->iov_base += now;
+ iov->iov_len -= now;
+ if (iov->iov_len == 0) {
+ iov++;
+ iovcnt--;
+ }
+ }
+ iov--;
+ iovcnt++;
+ iov->iov_base = hdr;
+ iov->iov_len = hdrlen;
+ }
+
+ /* Insert VLAN tag. */
+ if (vlen != 0) {
+ hdr -= ETHER_VLAN_ENCAP_LEN;
+ memmove(hdr, hdr + ETHER_VLAN_ENCAP_LEN, ETHER_ADDR_LEN*2);
+ hdrlen += ETHER_VLAN_ENCAP_LEN;
+ hdr[ETHER_ADDR_LEN*2 + 0] = sc->esc_VET >> 8;
+ hdr[ETHER_ADDR_LEN*2 + 1] = sc->esc_VET & 0xff;
+ hdr[ETHER_ADDR_LEN*2 + 2] = dsc->td.upper.fields.special >> 8;
+ hdr[ETHER_ADDR_LEN*2 + 3] = dsc->td.upper.fields.special & 0xff;
+ iov->iov_base = hdr;
+ iov->iov_len += ETHER_VLAN_ENCAP_LEN;
+ /* Correct checksum offsets after VLAN tag insertion. */
+ ckinfo[0].ck_start += ETHER_VLAN_ENCAP_LEN;
+ ckinfo[0].ck_off += ETHER_VLAN_ENCAP_LEN;
+ if (ckinfo[0].ck_len != 0)
+ ckinfo[0].ck_len += ETHER_VLAN_ENCAP_LEN;
+ ckinfo[1].ck_start += ETHER_VLAN_ENCAP_LEN;
+ ckinfo[1].ck_off += ETHER_VLAN_ENCAP_LEN;
+ if (ckinfo[1].ck_len != 0)
+ ckinfo[1].ck_len += ETHER_VLAN_ENCAP_LEN;
+ }
+
+ /* Simple non-TSO case. */
+ if (!tso) {
+ /* Calculate checksums and transmit. */
+ if (ckinfo[0].ck_valid)
+ e82545_transmit_checksum(iov, iovcnt, &ckinfo[0]);
+ if (ckinfo[1].ck_valid)
+ e82545_transmit_checksum(iov, iovcnt, &ckinfo[1]);
+ e82545_transmit_backend(sc, iov, iovcnt);
+ goto done;
+ }
+
+ /* Doing TSO. */
+ tcp = (sc->esc_txctx.cmd_and_length & E1000_TXD_CMD_TCP) != 0;
+ mss = sc->esc_txctx.tcp_seg_setup.fields.mss;
+ paylen = (sc->esc_txctx.cmd_and_length & 0x000fffff);
+ DPRINTF("tx %s segmentation offload %d+%d/%d bytes %d iovs\r\n",
+ tcp ? "TCP" : "UDP", hdrlen, paylen, mss, iovcnt);
+ ipid = ntohs(*(uint16_t *)&hdr[ckinfo[0].ck_start + 4]);
+ tcpseq = ntohl(*(uint32_t *)&hdr[ckinfo[1].ck_start + 4]);
+ ipcs = *(uint16_t *)&hdr[ckinfo[0].ck_off];
+ tcpcs = 0;
+ if (ckinfo[1].ck_valid) /* Save partial pseudo-header checksum. */
+ tcpcs = *(uint16_t *)&hdr[ckinfo[1].ck_off];
+ pv = 1;
+ pvoff = 0;
+ for (seg = 0, left = paylen; left > 0; seg++, left -= now) {
+ now = MIN(left, mss);
+
+ /* Construct IOVs for the segment. */
+ /* Include whole original header. */
+ tiov[0].iov_base = hdr;
+ tiov[0].iov_len = hdrlen;
+ tiovcnt = 1;
+ /* Include respective part of payload IOV. */
+ for (nleft = now; pv < iovcnt && nleft > 0; nleft -= nnow) {
+ nnow = MIN(nleft, iov[pv].iov_len - pvoff);
+ tiov[tiovcnt].iov_base = iov[pv].iov_base + pvoff;
+ tiov[tiovcnt++].iov_len = nnow;
+ if (pvoff + nnow == iov[pv].iov_len) {
+ pv++;
+ pvoff = 0;
+ } else
+ pvoff += nnow;
+ }
+ DPRINTF("tx segment %d %d+%d bytes %d iovs\r\n",
+ seg, hdrlen, now, tiovcnt);
+
+ /* Update IP header. */
+ if (sc->esc_txctx.cmd_and_length & E1000_TXD_CMD_IP) {
+ /* IPv4 -- set length and ID */
+ *(uint16_t *)&hdr[ckinfo[0].ck_start + 2] =
+ htons(hdrlen - ckinfo[0].ck_start + now);
+ *(uint16_t *)&hdr[ckinfo[0].ck_start + 4] =
+ htons(ipid + seg);
+ } else {
+ /* IPv6 -- set length */
+ *(uint16_t *)&hdr[ckinfo[0].ck_start + 4] =
+ htons(hdrlen - ckinfo[0].ck_start - 40 +
+ now);
+ }
+
+ /* Update pseudo-header checksum. */
+ tcpsum = tcpcs;
+ tcpsum += htons(hdrlen - ckinfo[1].ck_start + now);
+
+ /* Update TCP/UDP headers. */
+ if (tcp) {
+ /* Update sequence number and FIN/PUSH flags. */
+ *(uint32_t *)&hdr[ckinfo[1].ck_start + 4] =
+ htonl(tcpseq + paylen - left);
+ if (now < left) {
+ hdr[ckinfo[1].ck_start + 13] &=
+ ~(TH_FIN | TH_PUSH);
+ }
+ } else {
+ /* Update payload length. */
+ *(uint32_t *)&hdr[ckinfo[1].ck_start + 4] =
+ hdrlen - ckinfo[1].ck_start + now;
+ }
+
+ /* Calculate checksums and transmit. */
+ if (ckinfo[0].ck_valid) {
+ *(uint16_t *)&hdr[ckinfo[0].ck_off] = ipcs;
+ e82545_transmit_checksum(tiov, tiovcnt, &ckinfo[0]);
+ }
+ if (ckinfo[1].ck_valid) {
+ *(uint16_t *)&hdr[ckinfo[1].ck_off] =
+ e82545_carry(tcpsum);
+ e82545_transmit_checksum(tiov, tiovcnt, &ckinfo[1]);
+ }
+ e82545_transmit_backend(sc, tiov, tiovcnt);
+ }
+
+done:
+ head = (head + 1) % dsize;
+ e82545_transmit_done(sc, ohead, head, dsize, tdwb);
+
+ *rhead = head;
+ return (desc + 1);
+}
+
+static void
+e82545_tx_run(struct e82545_softc *sc)
+{
+ uint32_t cause;
+ uint16_t head, rhead, tail, size;
+ int lim, tdwb, sent;
+
+ head = sc->esc_TDH;
+ tail = sc->esc_TDT;
+ size = sc->esc_TDLEN / 16;
+ DPRINTF("tx_run: head %x, rhead %x, tail %x\r\n",
+ sc->esc_TDH, sc->esc_TDHr, sc->esc_TDT);
+
+ pthread_mutex_unlock(&sc->esc_mtx);
+ rhead = head;
+ tdwb = 0;
+ for (lim = size / 4; sc->esc_tx_enabled && lim > 0; lim -= sent) {
+ sent = e82545_transmit(sc, head, tail, size, &rhead, &tdwb);
+ if (sent == 0)
+ break;
+ head = rhead;
+ }
+ pthread_mutex_lock(&sc->esc_mtx);
+
+ sc->esc_TDH = head;
+ sc->esc_TDHr = rhead;
+ cause = 0;
+ if (tdwb)
+ cause |= E1000_ICR_TXDW;
+ if (lim != size / 4 && sc->esc_TDH == sc->esc_TDT)
+ cause |= E1000_ICR_TXQE;
+ if (cause)
+ e82545_icr_assert(sc, cause);
+
+ DPRINTF("tx_run done: head %x, rhead %x, tail %x\r\n",
+ sc->esc_TDH, sc->esc_TDHr, sc->esc_TDT);
+}
+
+static void *
+e82545_tx_thread(void *param)
+{
+ struct e82545_softc *sc = param;
+
+ pthread_mutex_lock(&sc->esc_mtx);
+ for (;;) {
+ while (!sc->esc_tx_enabled || sc->esc_TDHr == sc->esc_TDT) {
+ if (sc->esc_tx_enabled && sc->esc_TDHr != sc->esc_TDT)
+ break;
+ sc->esc_tx_active = 0;
+ if (sc->esc_tx_enabled == 0)
+ pthread_cond_signal(&sc->esc_tx_cond);
+ pthread_cond_wait(&sc->esc_tx_cond, &sc->esc_mtx);
+ }
+ sc->esc_tx_active = 1;
+
+ /* Process some tx descriptors. Lock dropped inside. */
+ e82545_tx_run(sc);
+ }
+#ifndef __FreeBSD__
+ return (NULL);
+#endif
+}
+
+static void
+e82545_tx_start(struct e82545_softc *sc)
+{
+
+ if (sc->esc_tx_active == 0)
+ pthread_cond_signal(&sc->esc_tx_cond);
+}
+
+static void
+e82545_tx_enable(struct e82545_softc *sc)
+{
+
+ sc->esc_tx_enabled = 1;
+}
+
+static void
+e82545_tx_disable(struct e82545_softc *sc)
+{
+
+ sc->esc_tx_enabled = 0;
+ while (sc->esc_tx_active)
+ pthread_cond_wait(&sc->esc_tx_cond, &sc->esc_mtx);
+}
+
+static void
+e82545_rx_enable(struct e82545_softc *sc)
+{
+
+ sc->esc_rx_enabled = 1;
+}
+
+static void
+e82545_rx_disable(struct e82545_softc *sc)
+{
+
+ sc->esc_rx_enabled = 0;
+ while (sc->esc_rx_active)
+ pthread_cond_wait(&sc->esc_rx_cond, &sc->esc_mtx);
+}
+
+static void
+e82545_write_ra(struct e82545_softc *sc, int reg, uint32_t wval)
+{
+ struct eth_uni *eu;
+ int idx;
+
+ idx = reg >> 1;
+ assert(idx < 15);
+
+ eu = &sc->esc_uni[idx];
+
+ if (reg & 0x1) {
+ /* RAH */
+ eu->eu_valid = ((wval & E1000_RAH_AV) == E1000_RAH_AV);
+ eu->eu_addrsel = (wval >> 16) & 0x3;
+ eu->eu_eth.octet[5] = wval >> 8;
+ eu->eu_eth.octet[4] = wval;
+ } else {
+ /* RAL */
+ eu->eu_eth.octet[3] = wval >> 24;
+ eu->eu_eth.octet[2] = wval >> 16;
+ eu->eu_eth.octet[1] = wval >> 8;
+ eu->eu_eth.octet[0] = wval;
+ }
+}
+
+static uint32_t
+e82545_read_ra(struct e82545_softc *sc, int reg)
+{
+ struct eth_uni *eu;
+ uint32_t retval;
+ int idx;
+
+ idx = reg >> 1;
+ assert(idx < 15);
+
+ eu = &sc->esc_uni[idx];
+
+ if (reg & 0x1) {
+ /* RAH */
+ retval = (eu->eu_valid << 31) |
+ (eu->eu_addrsel << 16) |
+ (eu->eu_eth.octet[5] << 8) |
+ eu->eu_eth.octet[4];
+ } else {
+ /* RAL */
+ retval = (eu->eu_eth.octet[3] << 24) |
+ (eu->eu_eth.octet[2] << 16) |
+ (eu->eu_eth.octet[1] << 8) |
+ eu->eu_eth.octet[0];
+ }
+
+ return (retval);
+}
+
+static void
+e82545_write_register(struct e82545_softc *sc, uint32_t offset, uint32_t value)
+{
+ int ridx;
+
+ if (offset & 0x3) {
+ DPRINTF("Unaligned register write offset:0x%x value:0x%x\r\n", offset, value);
+ return;
+ }
+ DPRINTF("Register write: 0x%x value: 0x%x\r\n", offset, value);
+
+ switch (offset) {
+ case E1000_CTRL:
+ case E1000_CTRL_DUP:
+ e82545_devctl(sc, value);
+ break;
+ case E1000_FCAL:
+ sc->esc_FCAL = value;
+ break;
+ case E1000_FCAH:
+ sc->esc_FCAH = value & ~0xFFFF0000;
+ break;
+ case E1000_FCT:
+ sc->esc_FCT = value & ~0xFFFF0000;
+ break;
+ case E1000_VET:
+ sc->esc_VET = value & ~0xFFFF0000;
+ break;
+ case E1000_FCTTV:
+ sc->esc_FCTTV = value & ~0xFFFF0000;
+ break;
+ case E1000_LEDCTL:
+ sc->esc_LEDCTL = value & ~0x30303000;
+ break;
+ case E1000_PBA:
+ sc->esc_PBA = value & 0x0000FF80;
+ break;
+ case E1000_ICR:
+ case E1000_ITR:
+ case E1000_ICS:
+ case E1000_IMS:
+ case E1000_IMC:
+ e82545_intr_write(sc, offset, value);
+ break;
+ case E1000_RCTL:
+ e82545_rx_ctl(sc, value);
+ break;
+ case E1000_FCRTL:
+ sc->esc_FCRTL = value & ~0xFFFF0007;
+ break;
+ case E1000_FCRTH:
+ sc->esc_FCRTH = value & ~0xFFFF0007;
+ break;
+ case E1000_RDBAL(0):
+ sc->esc_RDBAL = value & ~0xF;
+ if (sc->esc_rx_enabled) {
+ /* Apparently legal: update cached address */
+ e82545_rx_update_rdba(sc);
+ }
+ break;
+ case E1000_RDBAH(0):
+ assert(!sc->esc_rx_enabled);
+ sc->esc_RDBAH = value;
+ break;
+ case E1000_RDLEN(0):
+ assert(!sc->esc_rx_enabled);
+ sc->esc_RDLEN = value & ~0xFFF0007F;
+ break;
+ case E1000_RDH(0):
+ /* XXX should only ever be zero ? Range check ? */
+ sc->esc_RDH = value;
+ break;
+ case E1000_RDT(0):
+ /* XXX if this opens up the rx ring, do something ? */
+ sc->esc_RDT = value;
+ break;
+ case E1000_RDTR:
+ /* ignore FPD bit 31 */
+ sc->esc_RDTR = value & ~0xFFFF0000;
+ break;
+ case E1000_RXDCTL(0):
+ sc->esc_RXDCTL = value & ~0xFEC0C0C0;
+ break;
+ case E1000_RADV:
+ sc->esc_RADV = value & ~0xFFFF0000;
+ break;
+ case E1000_RSRPD:
+ sc->esc_RSRPD = value & ~0xFFFFF000;
+ break;
+ case E1000_RXCSUM:
+ sc->esc_RXCSUM = value & ~0xFFFFF800;
+ break;
+ case E1000_TXCW:
+ sc->esc_TXCW = value & ~0x3FFF0000;
+ break;
+ case E1000_TCTL:
+ e82545_tx_ctl(sc, value);
+ break;
+ case E1000_TIPG:
+ sc->esc_TIPG = value;
+ break;
+ case E1000_AIT:
+ sc->esc_AIT = value;
+ break;
+ case E1000_TDBAL(0):
+ sc->esc_TDBAL = value & ~0xF;
+ if (sc->esc_tx_enabled) {
+ /* Apparently legal */
+ e82545_tx_update_tdba(sc);
+ }
+ break;
+ case E1000_TDBAH(0):
+ //assert(!sc->esc_tx_enabled);
+ sc->esc_TDBAH = value;
+ break;
+ case E1000_TDLEN(0):
+ //assert(!sc->esc_tx_enabled);
+ sc->esc_TDLEN = value & ~0xFFF0007F;
+ break;
+ case E1000_TDH(0):
+ //assert(!sc->esc_tx_enabled);
+ /* XXX should only ever be zero ? Range check ? */
+ sc->esc_TDHr = sc->esc_TDH = value;
+ break;
+ case E1000_TDT(0):
+ /* XXX range check ? */
+ sc->esc_TDT = value;
+ if (sc->esc_tx_enabled)
+ e82545_tx_start(sc);
+ break;
+ case E1000_TIDV:
+ sc->esc_TIDV = value & ~0xFFFF0000;
+ break;
+ case E1000_TXDCTL(0):
+ //assert(!sc->esc_tx_enabled);
+ sc->esc_TXDCTL = value & ~0xC0C0C0;
+ break;
+ case E1000_TADV:
+ sc->esc_TADV = value & ~0xFFFF0000;
+ break;
+ case E1000_RAL(0) ... E1000_RAH(15):
+ /* convert to u32 offset */
+ ridx = (offset - E1000_RAL(0)) >> 2;
+ e82545_write_ra(sc, ridx, value);
+ break;
+ case E1000_MTA ... (E1000_MTA + (127*4)):
+ sc->esc_fmcast[(offset - E1000_MTA) >> 2] = value;
+ break;
+ case E1000_VFTA ... (E1000_VFTA + (127*4)):
+ sc->esc_fvlan[(offset - E1000_VFTA) >> 2] = value;
+ break;
+ case E1000_EECD:
+ {
+ //DPRINTF("EECD write 0x%x -> 0x%x\r\n", sc->eeprom_control, value);
+ /* edge triggered low->high */
+ uint32_t eecd_strobe = ((sc->eeprom_control & E1000_EECD_SK) ?
+ 0 : (value & E1000_EECD_SK));
+ uint32_t eecd_mask = (E1000_EECD_SK|E1000_EECD_CS|
+ E1000_EECD_DI|E1000_EECD_REQ);
+ sc->eeprom_control &= ~eecd_mask;
+ sc->eeprom_control |= (value & eecd_mask);
+ /* grant/revoke immediately */
+ if (value & E1000_EECD_REQ) {
+ sc->eeprom_control |= E1000_EECD_GNT;
+ } else {
+ sc->eeprom_control &= ~E1000_EECD_GNT;
+ }
+ if (eecd_strobe && (sc->eeprom_control & E1000_EECD_CS)) {
+ e82545_eecd_strobe(sc);
+ }
+ return;
+ }
+ case E1000_MDIC:
+ {
+ uint8_t reg_addr = (uint8_t)((value & E1000_MDIC_REG_MASK) >>
+ E1000_MDIC_REG_SHIFT);
+ uint8_t phy_addr = (uint8_t)((value & E1000_MDIC_PHY_MASK) >>
+ E1000_MDIC_PHY_SHIFT);
+ sc->mdi_control =
+ (value & ~(E1000_MDIC_ERROR|E1000_MDIC_DEST));
+ if ((value & E1000_MDIC_READY) != 0) {
+ DPRINTF("Incorrect MDIC ready bit: 0x%x\r\n", value);
+ return;
+ }
+ switch (value & E82545_MDIC_OP_MASK) {
+ case E1000_MDIC_OP_READ:
+ sc->mdi_control &= ~E82545_MDIC_DATA_MASK;
+ sc->mdi_control |= e82545_read_mdi(sc, reg_addr, phy_addr);
+ break;
+ case E1000_MDIC_OP_WRITE:
+ e82545_write_mdi(sc, reg_addr, phy_addr,
+ value & E82545_MDIC_DATA_MASK);
+ break;
+ default:
+ DPRINTF("Unknown MDIC op: 0x%x\r\n", value);
+ return;
+ }
+ /* TODO: barrier? */
+ sc->mdi_control |= E1000_MDIC_READY;
+ if (value & E82545_MDIC_IE) {
+ // TODO: generate interrupt
+ }
+ return;
+ }
+ case E1000_MANC:
+ case E1000_STATUS:
+ return;
+ default:
+ DPRINTF("Unknown write register: 0x%x value:%x\r\n", offset, value);
+ return;
+ }
+}
+
+static uint32_t
+e82545_read_register(struct e82545_softc *sc, uint32_t offset)
+{
+ uint32_t retval;
+ int ridx;
+
+ if (offset & 0x3) {
+ DPRINTF("Unaligned register read offset:0x%x\r\n", offset);
+ return 0;
+ }
+
+ DPRINTF("Register read: 0x%x\r\n", offset);
+
+ switch (offset) {
+ case E1000_CTRL:
+ retval = sc->esc_CTRL;
+ break;
+ case E1000_STATUS:
+ retval = E1000_STATUS_FD | E1000_STATUS_LU |
+ E1000_STATUS_SPEED_1000;
+ break;
+ case E1000_FCAL:
+ retval = sc->esc_FCAL;
+ break;
+ case E1000_FCAH:
+ retval = sc->esc_FCAH;
+ break;
+ case E1000_FCT:
+ retval = sc->esc_FCT;
+ break;
+ case E1000_VET:
+ retval = sc->esc_VET;
+ break;
+ case E1000_FCTTV:
+ retval = sc->esc_FCTTV;
+ break;
+ case E1000_LEDCTL:
+ retval = sc->esc_LEDCTL;
+ break;
+ case E1000_PBA:
+ retval = sc->esc_PBA;
+ break;
+ case E1000_ICR:
+ case E1000_ITR:
+ case E1000_ICS:
+ case E1000_IMS:
+ case E1000_IMC:
+ retval = e82545_intr_read(sc, offset);
+ break;
+ case E1000_RCTL:
+ retval = sc->esc_RCTL;
+ break;
+ case E1000_FCRTL:
+ retval = sc->esc_FCRTL;
+ break;
+ case E1000_FCRTH:
+ retval = sc->esc_FCRTH;
+ break;
+ case E1000_RDBAL(0):
+ retval = sc->esc_RDBAL;
+ break;
+ case E1000_RDBAH(0):
+ retval = sc->esc_RDBAH;
+ break;
+ case E1000_RDLEN(0):
+ retval = sc->esc_RDLEN;
+ break;
+ case E1000_RDH(0):
+ retval = sc->esc_RDH;
+ break;
+ case E1000_RDT(0):
+ retval = sc->esc_RDT;
+ break;
+ case E1000_RDTR:
+ retval = sc->esc_RDTR;
+ break;
+ case E1000_RXDCTL(0):
+ retval = sc->esc_RXDCTL;
+ break;
+ case E1000_RADV:
+ retval = sc->esc_RADV;
+ break;
+ case E1000_RSRPD:
+ retval = sc->esc_RSRPD;
+ break;
+ case E1000_RXCSUM:
+ retval = sc->esc_RXCSUM;
+ break;
+ case E1000_TXCW:
+ retval = sc->esc_TXCW;
+ break;
+ case E1000_TCTL:
+ retval = sc->esc_TCTL;
+ break;
+ case E1000_TIPG:
+ retval = sc->esc_TIPG;
+ break;
+ case E1000_AIT:
+ retval = sc->esc_AIT;
+ break;
+ case E1000_TDBAL(0):
+ retval = sc->esc_TDBAL;
+ break;
+ case E1000_TDBAH(0):
+ retval = sc->esc_TDBAH;
+ break;
+ case E1000_TDLEN(0):
+ retval = sc->esc_TDLEN;
+ break;
+ case E1000_TDH(0):
+ retval = sc->esc_TDH;
+ break;
+ case E1000_TDT(0):
+ retval = sc->esc_TDT;
+ break;
+ case E1000_TIDV:
+ retval = sc->esc_TIDV;
+ break;
+ case E1000_TXDCTL(0):
+ retval = sc->esc_TXDCTL;
+ break;
+ case E1000_TADV:
+ retval = sc->esc_TADV;
+ break;
+ case E1000_RAL(0) ... E1000_RAH(15):
+ /* convert to u32 offset */
+ ridx = (offset - E1000_RAL(0)) >> 2;
+ retval = e82545_read_ra(sc, ridx);
+ break;
+ case E1000_MTA ... (E1000_MTA + (127*4)):
+ retval = sc->esc_fmcast[(offset - E1000_MTA) >> 2];
+ break;
+ case E1000_VFTA ... (E1000_VFTA + (127*4)):
+ retval = sc->esc_fvlan[(offset - E1000_VFTA) >> 2];
+ break;
+ case E1000_EECD:
+ //DPRINTF("EECD read %x\r\n", sc->eeprom_control);
+ retval = sc->eeprom_control;
+ break;
+ case E1000_MDIC:
+ retval = sc->mdi_control;
+ break;
+ case E1000_MANC:
+ retval = 0;
+ break;
+ /* stats that we emulate. */
+ case E1000_MPC:
+ retval = sc->missed_pkt_count;
+ break;
+ case E1000_PRC64:
+ retval = sc->pkt_rx_by_size[0];
+ break;
+ case E1000_PRC127:
+ retval = sc->pkt_rx_by_size[1];
+ break;
+ case E1000_PRC255:
+ retval = sc->pkt_rx_by_size[2];
+ break;
+ case E1000_PRC511:
+ retval = sc->pkt_rx_by_size[3];
+ break;
+ case E1000_PRC1023:
+ retval = sc->pkt_rx_by_size[4];
+ break;
+ case E1000_PRC1522:
+ retval = sc->pkt_rx_by_size[5];
+ break;
+ case E1000_GPRC:
+ retval = sc->good_pkt_rx_count;
+ break;
+ case E1000_BPRC:
+ retval = sc->bcast_pkt_rx_count;
+ break;
+ case E1000_MPRC:
+ retval = sc->mcast_pkt_rx_count;
+ break;
+ case E1000_GPTC:
+ case E1000_TPT:
+ retval = sc->good_pkt_tx_count;
+ break;
+ case E1000_GORCL:
+ retval = (uint32_t)sc->good_octets_rx;
+ break;
+ case E1000_GORCH:
+ retval = (uint32_t)(sc->good_octets_rx >> 32);
+ break;
+ case E1000_TOTL:
+ case E1000_GOTCL:
+ retval = (uint32_t)sc->good_octets_tx;
+ break;
+ case E1000_TOTH:
+ case E1000_GOTCH:
+ retval = (uint32_t)(sc->good_octets_tx >> 32);
+ break;
+ case E1000_ROC:
+ retval = sc->oversize_rx_count;
+ break;
+ case E1000_TORL:
+ retval = (uint32_t)(sc->good_octets_rx + sc->missed_octets);
+ break;
+ case E1000_TORH:
+ retval = (uint32_t)((sc->good_octets_rx +
+ sc->missed_octets) >> 32);
+ break;
+ case E1000_TPR:
+ retval = sc->good_pkt_rx_count + sc->missed_pkt_count +
+ sc->oversize_rx_count;
+ break;
+ case E1000_PTC64:
+ retval = sc->pkt_tx_by_size[0];
+ break;
+ case E1000_PTC127:
+ retval = sc->pkt_tx_by_size[1];
+ break;
+ case E1000_PTC255:
+ retval = sc->pkt_tx_by_size[2];
+ break;
+ case E1000_PTC511:
+ retval = sc->pkt_tx_by_size[3];
+ break;
+ case E1000_PTC1023:
+ retval = sc->pkt_tx_by_size[4];
+ break;
+ case E1000_PTC1522:
+ retval = sc->pkt_tx_by_size[5];
+ break;
+ case E1000_MPTC:
+ retval = sc->mcast_pkt_tx_count;
+ break;
+ case E1000_BPTC:
+ retval = sc->bcast_pkt_tx_count;
+ break;
+ case E1000_TSCTC:
+ retval = sc->tso_tx_count;
+ break;
+ /* stats that are always 0. */
+ case E1000_CRCERRS:
+ case E1000_ALGNERRC:
+ case E1000_SYMERRS:
+ case E1000_RXERRC:
+ case E1000_SCC:
+ case E1000_ECOL:
+ case E1000_MCC:
+ case E1000_LATECOL:
+ case E1000_COLC:
+ case E1000_DC:
+ case E1000_TNCRS:
+ case E1000_SEC:
+ case E1000_CEXTERR:
+ case E1000_RLEC:
+ case E1000_XONRXC:
+ case E1000_XONTXC:
+ case E1000_XOFFRXC:
+ case E1000_XOFFTXC:
+ case E1000_FCRUC:
+ case E1000_RNBC:
+ case E1000_RUC:
+ case E1000_RFC:
+ case E1000_RJC:
+ case E1000_MGTPRC:
+ case E1000_MGTPDC:
+ case E1000_MGTPTC:
+ case E1000_TSCTFC:
+ retval = 0;
+ break;
+ default:
+ DPRINTF("Unknown read register: 0x%x\r\n", offset);
+ retval = 0;
+ break;
+ }
+
+ return (retval);
+}
+
+static void
+e82545_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
+ uint64_t offset, int size, uint64_t value)
+{
+ struct e82545_softc *sc;
+
+ //DPRINTF("Write bar:%d offset:0x%lx value:0x%lx size:%d\r\n", baridx, offset, value, size);
+
+ sc = pi->pi_arg;
+
+ pthread_mutex_lock(&sc->esc_mtx);
+
+ switch (baridx) {
+ case E82545_BAR_IO:
+ switch (offset) {
+ case E82545_IOADDR:
+ if (size != 4) {
+ DPRINTF("Wrong io addr write sz:%d value:0x%lx\r\n", size, value);
+ } else
+ sc->io_addr = (uint32_t)value;
+ break;
+ case E82545_IODATA:
+ if (size != 4) {
+ DPRINTF("Wrong io data write size:%d value:0x%lx\r\n", size, value);
+ } else if (sc->io_addr > E82545_IO_REGISTER_MAX) {
+ DPRINTF("Non-register io write addr:0x%x value:0x%lx\r\n", sc->io_addr, value);
+ } else
+ e82545_write_register(sc, sc->io_addr,
+ (uint32_t)value);
+ break;
+ default:
+ DPRINTF("Unknown io bar write offset:0x%lx value:0x%lx size:%d\r\n", offset, value, size);
+ break;
+ }
+ break;
+ case E82545_BAR_REGISTER:
+ if (size != 4) {
+ DPRINTF("Wrong register write size:%d offset:0x%lx value:0x%lx\r\n", size, offset, value);
+ } else
+ e82545_write_register(sc, (uint32_t)offset,
+ (uint32_t)value);
+ break;
+ default:
+ DPRINTF("Unknown write bar:%d off:0x%lx val:0x%lx size:%d\r\n",
+ baridx, offset, value, size);
+ }
+
+ pthread_mutex_unlock(&sc->esc_mtx);
+}
+
+static uint64_t
+e82545_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
+ uint64_t offset, int size)
+{
+ struct e82545_softc *sc;
+ uint64_t retval;
+
+ //DPRINTF("Read bar:%d offset:0x%lx size:%d\r\n", baridx, offset, size);
+ sc = pi->pi_arg;
+ retval = 0;
+
+ pthread_mutex_lock(&sc->esc_mtx);
+
+ switch (baridx) {
+ case E82545_BAR_IO:
+ switch (offset) {
+ case E82545_IOADDR:
+ if (size != 4) {
+ DPRINTF("Wrong io addr read sz:%d\r\n", size);
+ } else
+ retval = sc->io_addr;
+ break;
+ case E82545_IODATA:
+ if (size != 4) {
+ DPRINTF("Wrong io data read sz:%d\r\n", size);
+ }
+ if (sc->io_addr > E82545_IO_REGISTER_MAX) {
+ DPRINTF("Non-register io read addr:0x%x\r\n",
+ sc->io_addr);
+ } else
+ retval = e82545_read_register(sc, sc->io_addr);
+ break;
+ default:
+ DPRINTF("Unknown io bar read offset:0x%lx size:%d\r\n",
+ offset, size);
+ break;
+ }
+ break;
+ case E82545_BAR_REGISTER:
+ if (size != 4) {
+ DPRINTF("Wrong register read size:%d offset:0x%lx\r\n",
+ size, offset);
+ } else
+ retval = e82545_read_register(sc, (uint32_t)offset);
+ break;
+ default:
+ DPRINTF("Unknown read bar:%d offset:0x%lx size:%d\r\n",
+ baridx, offset, size);
+ break;
+ }
+
+ pthread_mutex_unlock(&sc->esc_mtx);
+
+ return (retval);
+}
+
+static void
+e82545_reset(struct e82545_softc *sc, int drvr)
+{
+ int i;
+
+ e82545_rx_disable(sc);
+ e82545_tx_disable(sc);
+
+ /* clear outstanding interrupts */
+ if (sc->esc_irq_asserted)
+ pci_lintr_deassert(sc->esc_pi);
+
+ /* misc */
+ if (!drvr) {
+ sc->esc_FCAL = 0;
+ sc->esc_FCAH = 0;
+ sc->esc_FCT = 0;
+ sc->esc_VET = 0;
+ sc->esc_FCTTV = 0;
+ }
+ sc->esc_LEDCTL = 0x07061302;
+ sc->esc_PBA = 0x00100030;
+
+ /* start nvm in opcode mode. */
+ sc->nvm_opaddr = 0;
+ sc->nvm_mode = E82545_NVM_MODE_OPADDR;
+ sc->nvm_bits = E82545_NVM_OPADDR_BITS;
+ sc->eeprom_control = E1000_EECD_PRES | E82545_EECD_FWE_EN;
+ e82545_init_eeprom(sc);
+
+ /* interrupt */
+ sc->esc_ICR = 0;
+ sc->esc_ITR = 250;
+ sc->esc_ICS = 0;
+ sc->esc_IMS = 0;
+ sc->esc_IMC = 0;
+
+ /* L2 filters */
+ if (!drvr) {
+ memset(sc->esc_fvlan, 0, sizeof(sc->esc_fvlan));
+ memset(sc->esc_fmcast, 0, sizeof(sc->esc_fmcast));
+ memset(sc->esc_uni, 0, sizeof(sc->esc_uni));
+
+ /* XXX not necessary on 82545 ?? */
+ sc->esc_uni[0].eu_valid = 1;
+ memcpy(sc->esc_uni[0].eu_eth.octet, sc->esc_mac.octet,
+ ETHER_ADDR_LEN);
+ } else {
+ /* Clear RAH valid bits */
+ for (i = 0; i < 16; i++)
+ sc->esc_uni[i].eu_valid = 0;
+ }
+
+ /* receive */
+ if (!drvr) {
+ sc->esc_RDBAL = 0;
+ sc->esc_RDBAH = 0;
+ }
+ sc->esc_RCTL = 0;
+ sc->esc_FCRTL = 0;
+ sc->esc_FCRTH = 0;
+ sc->esc_RDLEN = 0;
+ sc->esc_RDH = 0;
+ sc->esc_RDT = 0;
+ sc->esc_RDTR = 0;
+ sc->esc_RXDCTL = (1 << 24) | (1 << 16); /* default GRAN/WTHRESH */
+ sc->esc_RADV = 0;
+ sc->esc_RXCSUM = 0;
+
+ /* transmit */
+ if (!drvr) {
+ sc->esc_TDBAL = 0;
+ sc->esc_TDBAH = 0;
+ sc->esc_TIPG = 0;
+ sc->esc_AIT = 0;
+ sc->esc_TIDV = 0;
+ sc->esc_TADV = 0;
+ }
+ sc->esc_tdba = 0;
+ sc->esc_txdesc = NULL;
+ sc->esc_TXCW = 0;
+ sc->esc_TCTL = 0;
+ sc->esc_TDLEN = 0;
+ sc->esc_TDT = 0;
+ sc->esc_TDHr = sc->esc_TDH = 0;
+ sc->esc_TXDCTL = 0;
+}
+
+static void
+e82545_open_tap(struct e82545_softc *sc, char *opts)
+{
+ char tbuf[80];
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_t rights;
+#endif
+
+ if (opts == NULL) {
+ sc->esc_tapfd = -1;
+ return;
+ }
+
+ strcpy(tbuf, "/dev/");
+ strlcat(tbuf, opts, sizeof(tbuf));
+
+ sc->esc_tapfd = open(tbuf, O_RDWR);
+ if (sc->esc_tapfd == -1) {
+ DPRINTF("unable to open tap device %s\n", opts);
+ exit(1);
+ }
+
+ /*
+ * Set non-blocking and register for read
+ * notifications with the event loop
+ */
+ int opt = 1;
+ if (ioctl(sc->esc_tapfd, FIONBIO, &opt) < 0) {
+ WPRINTF("tap device O_NONBLOCK failed: %d\n", errno);
+ close(sc->esc_tapfd);
+ sc->esc_tapfd = -1;
+ }
+
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_init(&rights, CAP_EVENT, CAP_READ, CAP_WRITE);
+ if (cap_rights_limit(sc->esc_tapfd, &rights) == -1 && errno != ENOSYS)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+#endif
+
+#ifdef __FreeBSD__
+ sc->esc_mevp = mevent_add(sc->esc_tapfd,
+ EVF_READ,
+ e82545_tap_callback,
+ sc);
+ if (sc->esc_mevp == NULL) {
+ DPRINTF("Could not register mevent %d\n", EVF_READ);
+ close(sc->esc_tapfd);
+ sc->esc_tapfd = -1;
+ }
+#endif
+}
+
+static int
+e82545_parsemac(char *mac_str, uint8_t *mac_addr)
+{
+ struct ether_addr *ea;
+ char *tmpstr;
+ char zero_addr[ETHER_ADDR_LEN] = { 0, 0, 0, 0, 0, 0 };
+
+ tmpstr = strsep(&mac_str,"=");
+ if ((mac_str != NULL) && (!strcmp(tmpstr,"mac"))) {
+ ea = ether_aton(mac_str);
+ if (ea == NULL || ETHER_IS_MULTICAST(ea->octet) ||
+ memcmp(ea->octet, zero_addr, ETHER_ADDR_LEN) == 0) {
+ fprintf(stderr, "Invalid MAC %s\n", mac_str);
+ return (1);
+ } else
+ memcpy(mac_addr, ea->octet, ETHER_ADDR_LEN);
+ }
+ return (0);
+}
+
+static int
+e82545_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
+{
+ DPRINTF("Loading with options: %s\r\n", opts);
+
+ MD5_CTX mdctx;
+ unsigned char digest[16];
+ char nstr[80];
+ struct e82545_softc *sc;
+ char *devname;
+ char *vtopts;
+ int mac_provided;
+
+ /* Setup our softc */
+ sc = calloc(1, sizeof(*sc));
+
+ pi->pi_arg = sc;
+ sc->esc_pi = pi;
+ sc->esc_ctx = ctx;
+
+ pthread_mutex_init(&sc->esc_mtx, NULL);
+ pthread_cond_init(&sc->esc_rx_cond, NULL);
+ pthread_cond_init(&sc->esc_tx_cond, NULL);
+ pthread_create(&sc->esc_tx_tid, NULL, e82545_tx_thread, sc);
+ snprintf(nstr, sizeof(nstr), "e82545-%d:%d tx", pi->pi_slot,
+ pi->pi_func);
+ pthread_set_name_np(sc->esc_tx_tid, nstr);
+
+ pci_set_cfgdata16(pi, PCIR_DEVICE, E82545_DEV_ID_82545EM_COPPER);
+ pci_set_cfgdata16(pi, PCIR_VENDOR, E82545_VENDOR_ID_INTEL);
+ pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_NETWORK);
+ pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_NETWORK_ETHERNET);
+ pci_set_cfgdata16(pi, PCIR_SUBDEV_0, E82545_SUBDEV_ID);
+ pci_set_cfgdata16(pi, PCIR_SUBVEND_0, E82545_VENDOR_ID_INTEL);
+
+ pci_set_cfgdata8(pi, PCIR_HDRTYPE, PCIM_HDRTYPE_NORMAL);
+ pci_set_cfgdata8(pi, PCIR_INTPIN, 0x1);
+
+ /* TODO: this card also supports msi, but the freebsd driver for it
+ * does not, so I have not implemented it. */
+ pci_lintr_request(pi);
+
+ pci_emul_alloc_bar(pi, E82545_BAR_REGISTER, PCIBAR_MEM32,
+ E82545_BAR_REGISTER_LEN);
+ pci_emul_alloc_bar(pi, E82545_BAR_FLASH, PCIBAR_MEM32,
+ E82545_BAR_FLASH_LEN);
+ pci_emul_alloc_bar(pi, E82545_BAR_IO, PCIBAR_IO,
+ E82545_BAR_IO_LEN);
+
+ /*
+ * Attempt to open the tap device and read the MAC address
+ * if specified. Copied from virtio-net, slightly modified.
+ */
+ mac_provided = 0;
+ sc->esc_tapfd = -1;
+ if (opts != NULL) {
+ int err;
+
+ devname = vtopts = strdup(opts);
+ (void) strsep(&vtopts, ",");
+
+ if (vtopts != NULL) {
+ err = e82545_parsemac(vtopts, sc->esc_mac.octet);
+ if (err != 0) {
+ free(devname);
+ return (err);
+ }
+ mac_provided = 1;
+ }
+
+ if (strncmp(devname, "tap", 3) == 0 ||
+ strncmp(devname, "vmnet", 5) == 0)
+ e82545_open_tap(sc, devname);
+
+ free(devname);
+ }
+
+ /*
+ * The default MAC address is the standard NetApp OUI of 00-a0-98,
+ * followed by an MD5 of the PCI slot/func number and dev name
+ */
+ if (!mac_provided) {
+ snprintf(nstr, sizeof(nstr), "%d-%d-%s", pi->pi_slot,
+ pi->pi_func, vmname);
+
+ MD5Init(&mdctx);
+ MD5Update(&mdctx, nstr, strlen(nstr));
+ MD5Final(digest, &mdctx);
+
+ sc->esc_mac.octet[0] = 0x00;
+ sc->esc_mac.octet[1] = 0xa0;
+ sc->esc_mac.octet[2] = 0x98;
+ sc->esc_mac.octet[3] = digest[0];
+ sc->esc_mac.octet[4] = digest[1];
+ sc->esc_mac.octet[5] = digest[2];
+ }
+
+ /* H/w initiated reset */
+ e82545_reset(sc, 0);
+
+ return (0);
+}
+
+struct pci_devemu pci_de_e82545 = {
+ .pe_emu = "e1000",
+ .pe_init = e82545_init,
+ .pe_barwrite = e82545_write,
+ .pe_barread = e82545_read
+};
+PCI_EMUL_SET(pci_de_e82545);
+
diff --git a/usr/src/cmd/bhyve/pci_emul.c b/usr/src/cmd/bhyve/pci_emul.c
index 19b3fcbedd..101773b4e5 100644
--- a/usr/src/cmd/bhyve/pci_emul.c
+++ b/usr/src/cmd/bhyve/pci_emul.c
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/pci_emul.c 269700 2014-08-08 03:49:01Z neel $
+ * $FreeBSD$
*/
/*
* This file and its contents are supplied under the terms of the
@@ -40,13 +40,13 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/usr.sbin/bhyve/pci_emul.c 269700 2014-08-08 03:49:01Z neel $");
+__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/linker_set.h>
-#include <sys/errno.h>
#include <ctype.h>
+#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
@@ -72,17 +72,6 @@ __FBSDID("$FreeBSD: head/usr.sbin/bhyve/pci_emul.c 269700 2014-08-08 03:49:01Z n
#define CONF1_ENABLE 0x80000000ul
-#define CFGWRITE(pi,off,val,b) \
-do { \
- if ((b) == 1) { \
- pci_set_cfgdata8((pi),(off),(val)); \
- } else if ((b) == 2) { \
- pci_set_cfgdata16((pi),(off),(val)); \
- } else { \
- pci_set_cfgdata32((pi),(off),(val)); \
- } \
-} while (0)
-
#define MAXBUSES (PCI_BUSMAX + 1)
#define MAXSLOTS (PCI_SLOTMAX + 1)
#define MAXFUNCS (PCI_FUNCMAX + 1)
@@ -137,6 +126,30 @@ static void pci_lintr_update(struct pci_devinst *pi);
static void pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot,
int func, int coff, int bytes, uint32_t *val);
+static __inline void
+CFGWRITE(struct pci_devinst *pi, int coff, uint32_t val, int bytes)
+{
+
+ if (bytes == 1)
+ pci_set_cfgdata8(pi, coff, val);
+ else if (bytes == 2)
+ pci_set_cfgdata16(pi, coff, val);
+ else
+ pci_set_cfgdata32(pi, coff, val);
+}
+
+static __inline uint32_t
+CFGREAD(struct pci_devinst *pi, int coff, int bytes)
+{
+
+ if (bytes == 1)
+ return (pci_get_cfgdata8(pi, coff));
+ else if (bytes == 2)
+ return (pci_get_cfgdata16(pi, coff));
+ else
+ return (pci_get_cfgdata32(pi, coff));
+}
+
/*
* I/O access
*/
@@ -295,7 +308,7 @@ pci_emul_msix_tread(struct pci_devinst *pi, uint64_t offset, int size)
/*
* The PCI standard only allows 4 and 8 byte accesses to the MSI-X
- * table but we also allow 1 byte access to accomodate reads from
+ * table but we also allow 1 byte access to accommodate reads from
* ddb.
*/
if (size != 1 && size != 4 && size != 8)
@@ -762,8 +775,6 @@ pci_populate_msicap(struct msicap *msicap, int msgnum, int nextptr)
{
int mmc;
- CTASSERT(sizeof(struct msicap) == 14);
-
/* Number of msi messages must be a power of 2 between 1 and 32 */
assert((msgnum & (msgnum - 1)) == 0 && msgnum >= 1 && msgnum <= 32);
mmc = ffs(msgnum) - 1;
@@ -788,7 +799,6 @@ static void
pci_populate_msixcap(struct msixcap *msixcap, int msgnum, int barnum,
uint32_t msix_tab_size)
{
- CTASSERT(sizeof(struct msixcap) == 12);
assert(msix_tab_size % 4096 == 0);
@@ -865,10 +875,9 @@ msixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
int bytes, uint32_t val)
{
uint16_t msgctrl, rwmask;
- int off, table_bar;
+ int off;
off = offset - capoff;
- table_bar = pi->pi_msix.table_bar;
/* Message Control Register */
if (off == 2 && bytes == 2) {
rwmask = PCIM_MSIXCTRL_MSIX_ENABLE | PCIM_MSIXCTRL_FUNCTION_MASK;
@@ -940,8 +949,6 @@ pci_emul_add_pciecap(struct pci_devinst *pi, int type)
int err;
struct pciecap pciecap;
- CTASSERT(sizeof(struct pciecap) == 60);
-
if (type != PCIEM_TYPE_ROOT_PORT)
return (-1);
@@ -1189,7 +1196,6 @@ init_pci(struct vmctx *ctx)
return (0);
}
-#ifdef __FreeBSD__
static void
pci_apic_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq,
void *arg)
@@ -1395,7 +1401,6 @@ pci_write_dsdt(void)
dsdt_line("}");
dsdt_unindent(1);
}
-#endif
int
pci_bus_configured(int bus)
@@ -1670,27 +1675,31 @@ pci_emul_hdrtype_fixup(int bus, int slot, int off, int bytes, uint32_t *rv)
}
}
-static uint32_t
-bits_changed(uint32_t old, uint32_t new, uint32_t mask)
-{
-
- return ((old ^ new) & mask);
-}
-
static void
-pci_emul_cmdwrite(struct pci_devinst *pi, uint32_t new, int bytes)
+pci_emul_cmdsts_write(struct pci_devinst *pi, int coff, uint32_t new, int bytes)
{
- int i;
- uint16_t old;
+ int i, rshift;
+ uint32_t cmd, cmd2, changed, old, readonly;
+
+ cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); /* stash old value */
/*
- * The command register is at an offset of 4 bytes and thus the
- * guest could write 1, 2 or 4 bytes starting at this offset.
+ * From PCI Local Bus Specification 3.0 sections 6.2.2 and 6.2.3.
+ *
+ * XXX Bits 8, 11, 12, 13, 14 and 15 in the status register are
+ * 'write 1 to clear'. However these bits are not set to '1' by
+ * any device emulation so it is simpler to treat them as readonly.
*/
+ rshift = (coff & 0x3) * 8;
+ readonly = 0xFFFFF880 >> rshift;
+
+ old = CFGREAD(pi, coff, bytes);
+ new &= ~readonly;
+ new |= (old & readonly);
+ CFGWRITE(pi, coff, new, bytes); /* update config */
- old = pci_get_cfgdata16(pi, PCIR_COMMAND); /* stash old value */
- CFGWRITE(pi, PCIR_COMMAND, new, bytes); /* update config */
- new = pci_get_cfgdata16(pi, PCIR_COMMAND); /* get updated value */
+ cmd2 = pci_get_cfgdata16(pi, PCIR_COMMAND); /* get updated value */
+ changed = cmd ^ cmd2;
/*
* If the MMIO or I/O address space decoding has changed then
@@ -1703,7 +1712,7 @@ pci_emul_cmdwrite(struct pci_devinst *pi, uint32_t new, int bytes)
break;
case PCIBAR_IO:
/* I/O address space decoding changed? */
- if (bits_changed(old, new, PCIM_CMD_PORTEN)) {
+ if (changed & PCIM_CMD_PORTEN) {
if (porten(pi))
register_bar(pi, i);
else
@@ -1713,7 +1722,7 @@ pci_emul_cmdwrite(struct pci_devinst *pi, uint32_t new, int bytes)
case PCIBAR_MEM32:
case PCIBAR_MEM64:
/* MMIO address space decoding changed? */
- if (bits_changed(old, new, PCIM_CMD_MEMEN)) {
+ if (changed & PCIM_CMD_MEMEN) {
if (memen(pi))
register_bar(pi, i);
else
@@ -1740,6 +1749,7 @@ pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func,
struct slotinfo *si;
struct pci_devinst *pi;
struct pci_devemu *pe;
+ int idx, needcfg;
uint64_t addr, mask;
uint64_t bar = 0;
@@ -1785,8 +1795,6 @@ pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func,
* Config read
*/
if (in) {
- int needcfg;
-
/* Let the device emulation override the default handler */
if (pe->pe_cfgread != NULL) {
needcfg = pe->pe_cfgread(ctx, vcpu, pi, coff, bytes,
@@ -1795,14 +1803,8 @@ pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func,
needcfg = 1;
}
- if (needcfg) {
- if (bytes == 1)
- *eax = pci_get_cfgdata8(pi, coff);
- else if (bytes == 2)
- *eax = pci_get_cfgdata16(pi, coff);
- else
- *eax = pci_get_cfgdata32(pi, coff);
- }
+ if (needcfg)
+ *eax = CFGREAD(pi, coff, bytes);
pci_emul_hdrtype_fixup(bus, slot, coff, bytes, eax);
} else {
@@ -1815,7 +1817,6 @@ pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func,
* Special handling for write to BAR registers
*/
if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) {
- int idx;
/*
* Ignore writes to BAR registers that are not
* 4-byte aligned.
@@ -1873,8 +1874,8 @@ pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func,
} else if (pci_emul_iscap(pi, coff)) {
pci_emul_capwrite(pi, coff, bytes, *eax);
- } else if (coff == PCIR_COMMAND) {
- pci_emul_cmdwrite(pi, *eax, bytes);
+ } else if (coff >= PCIR_COMMAND && coff < PCIR_REVID) {
+ pci_emul_cmdsts_write(pi, coff, *eax, bytes);
} else {
CFGWRITE(pi, coff, *eax, bytes);
}
@@ -1947,7 +1948,7 @@ INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata);
#define DMEMSZ 4096
struct pci_emul_dsoftc {
uint8_t ioregs[DIOSZ];
- uint8_t memregs[DMEMSZ];
+ uint8_t memregs[2][DMEMSZ];
};
#define PCI_EMUL_MSI_MSGS 4
@@ -1976,6 +1977,9 @@ pci_emul_dinit(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, DMEMSZ);
assert(error == 0);
+ error = pci_emul_alloc_bar(pi, 2, PCIBAR_MEM32, DMEMSZ);
+ assert(error == 0);
+
return (0);
}
@@ -2015,21 +2019,23 @@ pci_emul_diow(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
}
}
- if (baridx == 1) {
+ if (baridx == 1 || baridx == 2) {
if (offset + size > DMEMSZ) {
printf("diow: memw too large, offset %ld size %d\n",
offset, size);
return;
}
+ i = baridx - 1; /* 'memregs' index */
+
if (size == 1) {
- sc->memregs[offset] = value;
+ sc->memregs[i][offset] = value;
} else if (size == 2) {
- *(uint16_t *)&sc->memregs[offset] = value;
+ *(uint16_t *)&sc->memregs[i][offset] = value;
} else if (size == 4) {
- *(uint32_t *)&sc->memregs[offset] = value;
+ *(uint32_t *)&sc->memregs[i][offset] = value;
} else if (size == 8) {
- *(uint64_t *)&sc->memregs[offset] = value;
+ *(uint64_t *)&sc->memregs[i][offset] = value;
} else {
printf("diow: memw unknown size %d\n", size);
}
@@ -2039,7 +2045,7 @@ pci_emul_diow(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
*/
}
- if (baridx > 1) {
+ if (baridx > 2 || baridx < 0) {
printf("diow: unknown bar idx %d\n", baridx);
}
}
@@ -2049,15 +2055,17 @@ pci_emul_dior(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
uint64_t offset, int size)
{
struct pci_emul_dsoftc *sc = pi->pi_arg;
- uint32_t value = 0;
+ uint32_t value;
+ int i;
+ value = 0;
if (baridx == 0) {
if (offset + size > DIOSZ) {
printf("dior: ior too large, offset %ld size %d\n",
offset, size);
return (0);
}
-
+
if (size == 1) {
value = sc->ioregs[offset];
} else if (size == 2) {
@@ -2068,29 +2076,31 @@ pci_emul_dior(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
printf("dior: ior unknown size %d\n", size);
}
}
-
- if (baridx == 1) {
+
+ if (baridx == 1 || baridx == 2) {
if (offset + size > DMEMSZ) {
printf("dior: memr too large, offset %ld size %d\n",
offset, size);
return (0);
}
-
+
+ i = baridx - 1; /* 'memregs' index */
+
if (size == 1) {
- value = sc->memregs[offset];
+ value = sc->memregs[i][offset];
} else if (size == 2) {
- value = *(uint16_t *) &sc->memregs[offset];
+ value = *(uint16_t *) &sc->memregs[i][offset];
} else if (size == 4) {
- value = *(uint32_t *) &sc->memregs[offset];
+ value = *(uint32_t *) &sc->memregs[i][offset];
} else if (size == 8) {
- value = *(uint64_t *) &sc->memregs[offset];
+ value = *(uint64_t *) &sc->memregs[i][offset];
} else {
printf("dior: ior unknown size %d\n", size);
}
}
- if (baridx > 1) {
+ if (baridx > 2 || baridx < 0) {
printf("dior: unknown bar idx %d\n", baridx);
return (0);
}
diff --git a/usr/src/cmd/bhyve/pci_emul.h b/usr/src/cmd/bhyve/pci_emul.h
index 6af01c4c3c..0fffb19dee 100644
--- a/usr/src/cmd/bhyve/pci_emul.h
+++ b/usr/src/cmd/bhyve/pci_emul.h
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/pci_emul.h 269700 2014-08-08 03:49:01Z neel $
+ * $FreeBSD$
*/
#ifndef _PCI_EMUL_H_
@@ -142,6 +142,8 @@ struct pci_devinst {
int pba_size;
int function_mask;
struct msix_table_entry *table; /* allocated at runtime */
+ void *pba_page;
+ int pba_page_offset;
} pi_msix;
void *pi_arg; /* devemu-private data */
@@ -158,6 +160,7 @@ struct msicap {
uint32_t addrhi;
uint16_t msgdata;
} __packed;
+static_assert(sizeof(struct msicap) == 14, "compile-time assertion failed");
struct msixcap {
uint8_t capid;
@@ -166,6 +169,7 @@ struct msixcap {
uint32_t table_info; /* bar index and offset within it */
uint32_t pba_info; /* bar index and offset within it */
} __packed;
+static_assert(sizeof(struct msixcap) == 12, "compile-time assertion failed");
struct pciecap {
uint8_t capid;
@@ -200,6 +204,7 @@ struct pciecap {
uint16_t slot_control2;
uint16_t slot_status2;
} __packed;
+static_assert(sizeof(struct pciecap) == 60, "compile-time assertion failed");
typedef void (*pci_lintr_cb)(int b, int s, int pin, int pirq_pin,
int ioapic_irq, void *arg);
@@ -225,7 +230,7 @@ int pci_msi_enabled(struct pci_devinst *pi);
int pci_msix_enabled(struct pci_devinst *pi);
int pci_msix_table_bar(struct pci_devinst *pi);
int pci_msix_pba_bar(struct pci_devinst *pi);
-int pci_msi_msgnum(struct pci_devinst *pi);
+int pci_msi_maxmsgnum(struct pci_devinst *pi);
int pci_parse_slot(char *opt);
void pci_populate_msicap(struct msicap *cap, int msgs, int nextptr);
int pci_emul_add_msixcap(struct pci_devinst *pi, int msgnum, int barnum);
diff --git a/usr/src/cmd/bhyve/pci_fbuf.c b/usr/src/cmd/bhyve/pci_fbuf.c
new file mode 100644
index 0000000000..67279d2162
--- /dev/null
+++ b/usr/src/cmd/bhyve/pci_fbuf.c
@@ -0,0 +1,424 @@
+/*-
+ * Copyright (c) 2015 Nahanni Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <machine/vmm.h>
+#include <vmmapi.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <errno.h>
+#include <unistd.h>
+
+#include "bhyvegc.h"
+#include "bhyverun.h"
+#include "console.h"
+#include "inout.h"
+#include "pci_emul.h"
+#include "rfb.h"
+#include "vga.h"
+
+/*
+ * bhyve Framebuffer device emulation.
+ * BAR0 points to the current mode information.
+ * BAR1 is the 32-bit framebuffer address.
+ *
+ * -s <b>,fbuf,wait,vga=on|io|off,rfb=<ip>:port,w=width,h=height
+ */
+
+static int fbuf_debug = 1;
+#define DEBUG_INFO 1
+#define DEBUG_VERBOSE 4
+#define DPRINTF(level, params) if (level <= fbuf_debug) printf params
+
+
+#define KB (1024UL)
+#define MB (1024 * 1024UL)
+
+#define DMEMSZ 128
+
+#define FB_SIZE (16*MB)
+
+#define COLS_MAX 1920
+#define ROWS_MAX 1200
+
+#define COLS_DEFAULT 1024
+#define ROWS_DEFAULT 768
+
+#define COLS_MIN 640
+#define ROWS_MIN 480
+
+struct pci_fbuf_softc {
+ struct pci_devinst *fsc_pi;
+ struct {
+ uint32_t fbsize;
+ uint16_t width;
+ uint16_t height;
+ uint16_t depth;
+ uint16_t refreshrate;
+ uint8_t reserved[116];
+ } __packed memregs;
+
+ /* rfb server */
+ char *rfb_host;
+ int rfb_port;
+ int rfb_wait;
+ int vga_enabled;
+ int vga_full;
+
+ uint32_t fbaddr;
+ char *fb_base;
+ uint16_t gc_width;
+ uint16_t gc_height;
+ void *vgasc;
+ struct bhyvegc_image *gc_image;
+};
+
+static struct pci_fbuf_softc *fbuf_sc;
+
+#define PCI_FBUF_MSI_MSGS 4
+
+static void
+pci_fbuf_usage(char *opt)
+{
+
+ fprintf(stderr, "Invalid fbuf emulation \"%s\"\r\n", opt);
+ fprintf(stderr, "fbuf: {wait,}{vga=on|io|off,}rfb=<ip>:port\r\n");
+}
+
+static void
+pci_fbuf_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
+ int baridx, uint64_t offset, int size, uint64_t value)
+{
+ struct pci_fbuf_softc *sc;
+ uint8_t *p;
+
+ assert(baridx == 0);
+
+ sc = pi->pi_arg;
+
+ DPRINTF(DEBUG_VERBOSE,
+ ("fbuf wr: offset 0x%lx, size: %d, value: 0x%lx\n",
+ offset, size, value));
+
+ if (offset + size > DMEMSZ) {
+ printf("fbuf: write too large, offset %ld size %d\n",
+ offset, size);
+ return;
+ }
+
+ p = (uint8_t *)&sc->memregs + offset;
+
+ switch (size) {
+ case 1:
+ *p = value;
+ break;
+ case 2:
+ *(uint16_t *)p = value;
+ break;
+ case 4:
+ *(uint32_t *)p = value;
+ break;
+ case 8:
+ *(uint64_t *)p = value;
+ break;
+ default:
+ printf("fbuf: write unknown size %d\n", size);
+ break;
+ }
+
+ if (!sc->gc_image->vgamode && sc->memregs.width == 0 &&
+ sc->memregs.height == 0) {
+ DPRINTF(DEBUG_INFO, ("switching to VGA mode\r\n"));
+ sc->gc_image->vgamode = 1;
+ sc->gc_width = 0;
+ sc->gc_height = 0;
+ } else if (sc->gc_image->vgamode && sc->memregs.width != 0 &&
+ sc->memregs.height != 0) {
+ DPRINTF(DEBUG_INFO, ("switching to VESA mode\r\n"));
+ sc->gc_image->vgamode = 0;
+ }
+}
+
+uint64_t
+pci_fbuf_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
+ int baridx, uint64_t offset, int size)
+{
+ struct pci_fbuf_softc *sc;
+ uint8_t *p;
+ uint64_t value;
+
+ assert(baridx == 0);
+
+ sc = pi->pi_arg;
+
+
+ if (offset + size > DMEMSZ) {
+ printf("fbuf: read too large, offset %ld size %d\n",
+ offset, size);
+ return (0);
+ }
+
+ p = (uint8_t *)&sc->memregs + offset;
+ value = 0;
+ switch (size) {
+ case 1:
+ value = *p;
+ break;
+ case 2:
+ value = *(uint16_t *)p;
+ break;
+ case 4:
+ value = *(uint32_t *)p;
+ break;
+ case 8:
+ value = *(uint64_t *)p;
+ break;
+ default:
+ printf("fbuf: read unknown size %d\n", size);
+ break;
+ }
+
+ DPRINTF(DEBUG_VERBOSE,
+ ("fbuf rd: offset 0x%lx, size: %d, value: 0x%lx\n",
+ offset, size, value));
+
+ return (value);
+}
+
+static int
+pci_fbuf_parse_opts(struct pci_fbuf_softc *sc, char *opts)
+{
+ char *uopts, *xopts, *config;
+ char *tmpstr;
+ int ret;
+
+ ret = 0;
+ uopts = strdup(opts);
+ for (xopts = strtok(uopts, ",");
+ xopts != NULL;
+ xopts = strtok(NULL, ",")) {
+ if (strcmp(xopts, "wait") == 0) {
+ sc->rfb_wait = 1;
+ continue;
+ }
+
+ if ((config = strchr(xopts, '=')) == NULL) {
+ pci_fbuf_usage(xopts);
+ ret = -1;
+ goto done;
+ }
+
+ *config++ = '\0';
+
+ DPRINTF(DEBUG_VERBOSE, ("pci_fbuf option %s = %s\r\n",
+ xopts, config));
+
+ if (!strcmp(xopts, "tcp") || !strcmp(xopts, "rfb")) {
+ /* parse host-ip:port */
+ tmpstr = strsep(&config, ":");
+ if (!config)
+ sc->rfb_port = atoi(tmpstr);
+ else {
+ sc->rfb_port = atoi(config);
+ sc->rfb_host = tmpstr;
+ }
+ } else if (!strcmp(xopts, "vga")) {
+ if (!strcmp(config, "off")) {
+ sc->vga_enabled = 0;
+ } else if (!strcmp(config, "io")) {
+ sc->vga_enabled = 1;
+ sc->vga_full = 0;
+ } else if (!strcmp(config, "on")) {
+ sc->vga_enabled = 1;
+ sc->vga_full = 1;
+ } else {
+ pci_fbuf_usage(opts);
+ ret = -1;
+ goto done;
+ }
+ } else if (!strcmp(xopts, "w")) {
+ sc->memregs.width = atoi(config);
+ if (sc->memregs.width > COLS_MAX) {
+ pci_fbuf_usage(xopts);
+ ret = -1;
+ goto done;
+ } else if (sc->memregs.width == 0)
+ sc->memregs.width = 1920;
+ } else if (!strcmp(xopts, "h")) {
+ sc->memregs.height = atoi(config);
+ if (sc->memregs.height > ROWS_MAX) {
+ pci_fbuf_usage(xopts);
+ ret = -1;
+ goto done;
+ } else if (sc->memregs.height == 0)
+ sc->memregs.height = 1080;
+
+ } else {
+ pci_fbuf_usage(xopts);
+ ret = -1;
+ goto done;
+ }
+ }
+
+done:
+ return (ret);
+}
+
+
+extern void vga_render(struct bhyvegc *gc, void *arg);
+
+void
+pci_fbuf_render(struct bhyvegc *gc, void *arg)
+{
+ struct pci_fbuf_softc *sc;
+
+ sc = arg;
+
+ if (sc->vga_full && sc->gc_image->vgamode) {
+ /* TODO: mode switching to vga and vesa should use the special
+ * EFI-bhyve protocol port.
+ */
+ vga_render(gc, sc->vgasc);
+ return;
+ }
+ if (sc->gc_width != sc->memregs.width ||
+ sc->gc_height != sc->memregs.height) {
+ bhyvegc_resize(gc, sc->memregs.width, sc->memregs.height);
+ sc->gc_width = sc->memregs.width;
+ sc->gc_height = sc->memregs.height;
+ }
+
+ return;
+}
+
+static int
+pci_fbuf_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
+{
+ int error, prot;
+ struct pci_fbuf_softc *sc;
+
+ if (fbuf_sc != NULL) {
+ fprintf(stderr, "Only one frame buffer device is allowed.\n");
+ return (-1);
+ }
+
+ sc = calloc(1, sizeof(struct pci_fbuf_softc));
+
+ pi->pi_arg = sc;
+
+ /* initialize config space */
+ pci_set_cfgdata16(pi, PCIR_DEVICE, 0x40FB);
+ pci_set_cfgdata16(pi, PCIR_VENDOR, 0xFB5D);
+ pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_DISPLAY);
+ pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_DISPLAY_VGA);
+
+ error = pci_emul_alloc_bar(pi, 0, PCIBAR_MEM32, DMEMSZ);
+ assert(error == 0);
+
+ error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, FB_SIZE);
+ assert(error == 0);
+
+ error = pci_emul_add_msicap(pi, PCI_FBUF_MSI_MSGS);
+ assert(error == 0);
+
+ sc->fbaddr = pi->pi_bar[1].addr;
+ sc->memregs.fbsize = FB_SIZE;
+ sc->memregs.width = COLS_DEFAULT;
+ sc->memregs.height = ROWS_DEFAULT;
+ sc->memregs.depth = 32;
+
+ sc->vga_enabled = 1;
+ sc->vga_full = 0;
+
+ sc->fsc_pi = pi;
+
+ error = pci_fbuf_parse_opts(sc, opts);
+ if (error != 0)
+ goto done;
+
+ /* XXX until VGA rendering is enabled */
+ if (sc->vga_full != 0) {
+ fprintf(stderr, "pci_fbuf: VGA rendering not enabled");
+ goto done;
+ }
+
+ sc->fb_base = vm_create_devmem(ctx, VM_FRAMEBUFFER, "framebuffer", FB_SIZE);
+ if (sc->fb_base == MAP_FAILED) {
+ error = -1;
+ goto done;
+ }
+ DPRINTF(DEBUG_INFO, ("fbuf frame buffer base: %p [sz %lu]\r\n",
+ sc->fb_base, FB_SIZE));
+
+ /*
+ * Map the framebuffer into the guest address space.
+ * XXX This may fail if the BAR is different than a prior
+ * run. In this case flag the error. This will be fixed
+ * when a change_memseg api is available.
+ */
+ prot = PROT_READ | PROT_WRITE;
+ if (vm_mmap_memseg(ctx, sc->fbaddr, VM_FRAMEBUFFER, 0, FB_SIZE, prot) != 0) {
+ fprintf(stderr, "pci_fbuf: mapseg failed - try deleting VM and restarting\n");
+ error = -1;
+ goto done;
+ }
+
+ console_init(sc->memregs.width, sc->memregs.height, sc->fb_base);
+ console_fb_register(pci_fbuf_render, sc);
+
+ if (sc->vga_enabled)
+ sc->vgasc = vga_init(!sc->vga_full);
+ sc->gc_image = console_get_image();
+
+ fbuf_sc = sc;
+
+ memset((void *)sc->fb_base, 0, FB_SIZE);
+
+ error = rfb_init(sc->rfb_host, sc->rfb_port, sc->rfb_wait);
+done:
+ if (error)
+ free(sc);
+
+ return (error);
+}
+
+struct pci_devemu pci_fbuf = {
+ .pe_emu = "fbuf",
+ .pe_init = pci_fbuf_init,
+ .pe_barwrite = pci_fbuf_write,
+ .pe_barread = pci_fbuf_read
+};
+PCI_EMUL_SET(pci_fbuf);
diff --git a/usr/src/cmd/bhyve/pci_hostbridge.c b/usr/src/cmd/bhyve/pci_hostbridge.c
index 08956d082e..5c9ea28191 100644
--- a/usr/src/cmd/bhyve/pci_hostbridge.c
+++ b/usr/src/cmd/bhyve/pci_hostbridge.c
@@ -23,11 +23,11 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/pci_hostbridge.c 283264 2015-05-21 20:11:52Z tychon $
+ * $FreeBSD$
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/usr.sbin/bhyve/pci_hostbridge.c 283264 2015-05-21 20:11:52Z tychon $");
+__FBSDID("$FreeBSD$");
#include "pci_emul.h"
diff --git a/usr/src/cmd/bhyve/pci_irq.c b/usr/src/cmd/bhyve/pci_irq.c
index 97ee330c65..f22b15cefa 100644
--- a/usr/src/cmd/bhyve/pci_irq.c
+++ b/usr/src/cmd/bhyve/pci_irq.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2014 Advanced Computing Technologies LLC
+ * Copyright (c) 2014 Hudson River Trading LLC
* Written by: John H. Baldwin <jhb@FreeBSD.org>
* All rights reserved.
*
@@ -27,7 +27,7 @@
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/usr.sbin/bhyve/pci_irq.c 266125 2014-05-15 14:16:55Z jhb $");
+__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <machine/vmm.h>
@@ -115,7 +115,7 @@ void
pci_irq_reserve(int irq)
{
- assert(irq < nitems(irq_counts));
+ assert(irq >= 0 && irq < nitems(irq_counts));
assert(pirq_cold);
assert(irq_counts[irq] == 0 || irq_counts[irq] == IRQ_DISABLED);
irq_counts[irq] = IRQ_DISABLED;
@@ -125,10 +125,10 @@ void
pci_irq_use(int irq)
{
- assert(irq < nitems(irq_counts));
+ assert(irq >= 0 && irq < nitems(irq_counts));
assert(pirq_cold);
- if (irq_counts[irq] != IRQ_DISABLED)
- irq_counts[irq]++;
+ assert(irq_counts[irq] != IRQ_DISABLED);
+ irq_counts[irq]++;
}
void
@@ -197,7 +197,7 @@ pirq_alloc_pin(struct vmctx *ctx)
{
int best_count, best_irq, best_pin, irq, pin;
- pirq_cold = 1;
+ pirq_cold = 0;
/* First, find the least-used PIRQ pin. */
best_pin = 0;
@@ -222,7 +222,7 @@ pirq_alloc_pin(struct vmctx *ctx)
best_count = irq_counts[irq];
}
}
- assert(best_irq != 0);
+ assert(best_irq >= 0);
irq_counts[best_irq]++;
pirqs[best_pin].reg = best_irq;
vm_isa_set_irq_trigger(ctx, best_irq, LEVEL_TRIGGER);
@@ -234,16 +234,12 @@ pirq_alloc_pin(struct vmctx *ctx)
int
pirq_irq(int pin)
{
-
- if (pin == -1)
- return (255);
assert(pin > 0 && pin <= nitems(pirqs));
return (pirqs[pin - 1].reg & PIRQ_IRQ);
}
/* XXX: Generate $PIR table. */
-#ifdef __FreeBSD__
static void
pirq_dsdt(void)
{
@@ -348,4 +344,3 @@ pirq_dsdt(void)
free(irq_prs);
}
LPC_DSDT(pirq_dsdt);
-#endif
diff --git a/usr/src/cmd/bhyve/pci_irq.h b/usr/src/cmd/bhyve/pci_irq.h
index 483f12b61e..24f9c99875 100644
--- a/usr/src/cmd/bhyve/pci_irq.h
+++ b/usr/src/cmd/bhyve/pci_irq.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2014 Advanced Computing Technologies LLC
+ * Copyright (c) 2014 Hudson River Trading LLC
* Written by: John H. Baldwin <jhb@FreeBSD.org>
* All rights reserved.
*
@@ -24,7 +24,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/pci_irq.h 266125 2014-05-15 14:16:55Z jhb $
+ * $FreeBSD$
*/
#ifndef __PCI_IRQ_H__
diff --git a/usr/src/cmd/bhyve/pci_lpc.c b/usr/src/cmd/bhyve/pci_lpc.c
index 8c060150dc..2203a00baa 100644
--- a/usr/src/cmd/bhyve/pci_lpc.c
+++ b/usr/src/cmd/bhyve/pci_lpc.c
@@ -24,11 +24,11 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/pci_lpc.c 266933 2014-05-31 23:37:34Z neel $
+ * $FreeBSD$
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/usr.sbin/bhyve/pci_lpc.c 266933 2014-05-31 23:37:34Z neel $");
+__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <machine/vmm.h>
@@ -40,6 +40,7 @@ __FBSDID("$FreeBSD: head/usr.sbin/bhyve/pci_lpc.c 266933 2014-05-31 23:37:34Z ne
#include <vmmapi.h>
#include "acpi.h"
+#include "bootrom.h"
#include "inout.h"
#include "pci_emul.h"
#include "pci_irq.h"
@@ -62,6 +63,8 @@ SYSRES_IO(NMISC_PORT, 1);
static struct pci_devinst *lpc_bridge;
+static const char *romfile;
+
#define LPC_UART_NUM 2
static struct lpc_uart_softc {
struct uart_softc *uart_softc;
@@ -76,7 +79,7 @@ static const char *lpc_uart_names[LPC_UART_NUM] = { "COM1", "COM2" };
/*
* LPC device configuration is in the following form:
* <lpc_device_name>[,<options>]
- * For e.g. "com1,stdio"
+ * For e.g. "com1,stdio" or "bootrom,/var/romfile"
*/
int
lpc_device_parse(const char *opts)
@@ -88,6 +91,11 @@ lpc_device_parse(const char *opts)
str = cpy = strdup(opts);
lpcdev = strsep(&str, ",");
if (lpcdev != NULL) {
+ if (strcasecmp(lpcdev, "bootrom") == 0) {
+ romfile = str;
+ error = 0;
+ goto done;
+ }
for (unit = 0; unit < LPC_UART_NUM; unit++) {
if (strcasecmp(lpcdev, lpc_uart_names[unit]) == 0) {
lpc_uart_softc[unit].opts = str;
@@ -104,6 +112,13 @@ done:
return (error);
}
+const char *
+lpc_bootrom(void)
+{
+
+ return (romfile);
+}
+
static void
lpc_uart_intr_assert(void *arg)
{
@@ -156,13 +171,19 @@ lpc_uart_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
}
static int
-lpc_init(void)
+lpc_init(struct vmctx *ctx)
{
struct lpc_uart_softc *sc;
struct inout_port iop;
const char *name;
int unit, error;
+ if (romfile != NULL) {
+ error = bootrom_init(ctx, romfile);
+ if (error)
+ return (error);
+ }
+
/* COM1 and COM2 */
for (unit = 0; unit < LPC_UART_NUM; unit++) {
sc = &lpc_uart_softc[unit];
@@ -200,7 +221,6 @@ lpc_init(void)
return (0);
}
-#ifdef __FreeBSD__
static void
pci_lpc_write_dsdt(struct pci_devinst *pi)
{
@@ -320,7 +340,6 @@ pci_lpc_uart_dsdt(void)
}
}
LPC_DSDT(pci_lpc_uart_dsdt);
-#endif
static int
pci_lpc_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
@@ -381,7 +400,7 @@ pci_lpc_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
return (-1);
}
- if (lpc_init() != 0)
+ if (lpc_init(ctx) != 0)
return (-1);
/* initialize config space */
@@ -423,9 +442,7 @@ lpc_pirq_routed(void)
struct pci_devemu pci_de_lpc = {
.pe_emu = "lpc",
.pe_init = pci_lpc_init,
-#ifdef __FreeBSD__
.pe_write_dsdt = pci_lpc_write_dsdt,
-#endif
.pe_cfgwrite = pci_lpc_cfgwrite,
.pe_barwrite = pci_lpc_write,
.pe_barread = pci_lpc_read
diff --git a/usr/src/cmd/bhyve/pci_lpc.h b/usr/src/cmd/bhyve/pci_lpc.h
index 4f725b1dd3..431f5cffd1 100644
--- a/usr/src/cmd/bhyve/pci_lpc.h
+++ b/usr/src/cmd/bhyve/pci_lpc.h
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/pci_lpc.h 266125 2014-05-15 14:16:55Z jhb $
+ * $FreeBSD$
*/
#ifndef _LPC_H_
@@ -68,5 +68,6 @@ struct lpc_sysres {
int lpc_device_parse(const char *opt);
char *lpc_pirq_name(int pin);
void lpc_pirq_routed(void);
+const char *lpc_bootrom(void);
#endif
diff --git a/usr/src/cmd/bhyve/pci_passthru.c b/usr/src/cmd/bhyve/pci_passthru.c
new file mode 100644
index 0000000000..f314679d91
--- /dev/null
+++ b/usr/src/cmd/bhyve/pci_passthru.c
@@ -0,0 +1,932 @@
+/*-
+ * Copyright (c) 2011 NetApp, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#ifndef WITHOUT_CAPSICUM
+#include <sys/capsicum.h>
+#endif
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/pciio.h>
+#include <sys/ioctl.h>
+
+#include <dev/io/iodev.h>
+#include <dev/pci/pcireg.h>
+
+#include <machine/iodev.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include <machine/vmm.h>
+#include <vmmapi.h>
+#include "pci_emul.h"
+#include "mem.h"
+
+#ifndef _PATH_DEVPCI
+#define _PATH_DEVPCI "/dev/pci"
+#endif
+
+#ifndef _PATH_DEVIO
+#define _PATH_DEVIO "/dev/io"
+#endif
+
+#ifndef _PATH_MEM
+#define _PATH_MEM "/dev/mem"
+#endif
+
+#define LEGACY_SUPPORT 1
+
+#define MSIX_TABLE_COUNT(ctrl) (((ctrl) & PCIM_MSIXCTRL_TABLE_SIZE) + 1)
+#define MSIX_CAPLEN 12
+
+static int pcifd = -1;
+static int iofd = -1;
+static int memfd = -1;
+
+struct passthru_softc {
+ struct pci_devinst *psc_pi;
+ struct pcibar psc_bar[PCI_BARMAX + 1];
+ struct {
+ int capoff;
+ int msgctrl;
+ int emulated;
+ } psc_msi;
+ struct {
+ int capoff;
+ } psc_msix;
+ struct pcisel psc_sel;
+};
+
+static int
+msi_caplen(int msgctrl)
+{
+ int len;
+
+ len = 10; /* minimum length of msi capability */
+
+ if (msgctrl & PCIM_MSICTRL_64BIT)
+ len += 4;
+
+#if 0
+ /*
+ * Ignore the 'mask' and 'pending' bits in the MSI capability.
+ * We'll let the guest manipulate them directly.
+ */
+ if (msgctrl & PCIM_MSICTRL_VECTOR)
+ len += 10;
+#endif
+
+ return (len);
+}
+
+static uint32_t
+read_config(const struct pcisel *sel, long reg, int width)
+{
+ struct pci_io pi;
+
+ bzero(&pi, sizeof(pi));
+ pi.pi_sel = *sel;
+ pi.pi_reg = reg;
+ pi.pi_width = width;
+
+ if (ioctl(pcifd, PCIOCREAD, &pi) < 0)
+ return (0); /* XXX */
+ else
+ return (pi.pi_data);
+}
+
+static void
+write_config(const struct pcisel *sel, long reg, int width, uint32_t data)
+{
+ struct pci_io pi;
+
+ bzero(&pi, sizeof(pi));
+ pi.pi_sel = *sel;
+ pi.pi_reg = reg;
+ pi.pi_width = width;
+ pi.pi_data = data;
+
+ (void)ioctl(pcifd, PCIOCWRITE, &pi); /* XXX */
+}
+
+#ifdef LEGACY_SUPPORT
+static int
+passthru_add_msicap(struct pci_devinst *pi, int msgnum, int nextptr)
+{
+ int capoff, i;
+ struct msicap msicap;
+ u_char *capdata;
+
+ pci_populate_msicap(&msicap, msgnum, nextptr);
+
+ /*
+ * XXX
+ * Copy the msi capability structure in the last 16 bytes of the
+ * config space. This is wrong because it could shadow something
+ * useful to the device.
+ */
+ capoff = 256 - roundup(sizeof(msicap), 4);
+ capdata = (u_char *)&msicap;
+ for (i = 0; i < sizeof(msicap); i++)
+ pci_set_cfgdata8(pi, capoff + i, capdata[i]);
+
+ return (capoff);
+}
+#endif /* LEGACY_SUPPORT */
+
+static int
+cfginitmsi(struct passthru_softc *sc)
+{
+ int i, ptr, capptr, cap, sts, caplen, table_size;
+ uint32_t u32;
+ struct pcisel sel;
+ struct pci_devinst *pi;
+ struct msixcap msixcap;
+ uint32_t *msixcap_ptr;
+
+ pi = sc->psc_pi;
+ sel = sc->psc_sel;
+
+ /*
+ * Parse the capabilities and cache the location of the MSI
+ * and MSI-X capabilities.
+ */
+ sts = read_config(&sel, PCIR_STATUS, 2);
+ if (sts & PCIM_STATUS_CAPPRESENT) {
+ ptr = read_config(&sel, PCIR_CAP_PTR, 1);
+ while (ptr != 0 && ptr != 0xff) {
+ cap = read_config(&sel, ptr + PCICAP_ID, 1);
+ if (cap == PCIY_MSI) {
+ /*
+ * Copy the MSI capability into the config
+ * space of the emulated pci device
+ */
+ sc->psc_msi.capoff = ptr;
+ sc->psc_msi.msgctrl = read_config(&sel,
+ ptr + 2, 2);
+ sc->psc_msi.emulated = 0;
+ caplen = msi_caplen(sc->psc_msi.msgctrl);
+ capptr = ptr;
+ while (caplen > 0) {
+ u32 = read_config(&sel, capptr, 4);
+ pci_set_cfgdata32(pi, capptr, u32);
+ caplen -= 4;
+ capptr += 4;
+ }
+ } else if (cap == PCIY_MSIX) {
+ /*
+ * Copy the MSI-X capability
+ */
+ sc->psc_msix.capoff = ptr;
+ caplen = 12;
+ msixcap_ptr = (uint32_t*) &msixcap;
+ capptr = ptr;
+ while (caplen > 0) {
+ u32 = read_config(&sel, capptr, 4);
+ *msixcap_ptr = u32;
+ pci_set_cfgdata32(pi, capptr, u32);
+ caplen -= 4;
+ capptr += 4;
+ msixcap_ptr++;
+ }
+ }
+ ptr = read_config(&sel, ptr + PCICAP_NEXTPTR, 1);
+ }
+ }
+
+ if (sc->psc_msix.capoff != 0) {
+ pi->pi_msix.pba_bar =
+ msixcap.pba_info & PCIM_MSIX_BIR_MASK;
+ pi->pi_msix.pba_offset =
+ msixcap.pba_info & ~PCIM_MSIX_BIR_MASK;
+ pi->pi_msix.table_bar =
+ msixcap.table_info & PCIM_MSIX_BIR_MASK;
+ pi->pi_msix.table_offset =
+ msixcap.table_info & ~PCIM_MSIX_BIR_MASK;
+ pi->pi_msix.table_count = MSIX_TABLE_COUNT(msixcap.msgctrl);
+ pi->pi_msix.pba_size = PBA_SIZE(pi->pi_msix.table_count);
+
+ /* Allocate the emulated MSI-X table array */
+ table_size = pi->pi_msix.table_count * MSIX_TABLE_ENTRY_SIZE;
+ pi->pi_msix.table = calloc(1, table_size);
+
+ /* Mask all table entries */
+ for (i = 0; i < pi->pi_msix.table_count; i++) {
+ pi->pi_msix.table[i].vector_control |=
+ PCIM_MSIX_VCTRL_MASK;
+ }
+ }
+
+#ifdef LEGACY_SUPPORT
+ /*
+ * If the passthrough device does not support MSI then craft a
+ * MSI capability for it. We link the new MSI capability at the
+ * head of the list of capabilities.
+ */
+ if ((sts & PCIM_STATUS_CAPPRESENT) != 0 && sc->psc_msi.capoff == 0) {
+ int origptr, msiptr;
+ origptr = read_config(&sel, PCIR_CAP_PTR, 1);
+ msiptr = passthru_add_msicap(pi, 1, origptr);
+ sc->psc_msi.capoff = msiptr;
+ sc->psc_msi.msgctrl = pci_get_cfgdata16(pi, msiptr + 2);
+ sc->psc_msi.emulated = 1;
+ pci_set_cfgdata8(pi, PCIR_CAP_PTR, msiptr);
+ }
+#endif
+
+ /* Make sure one of the capabilities is present */
+ if (sc->psc_msi.capoff == 0 && sc->psc_msix.capoff == 0)
+ return (-1);
+ else
+ return (0);
+}
+
+static uint64_t
+msix_table_read(struct passthru_softc *sc, uint64_t offset, int size)
+{
+ struct pci_devinst *pi;
+ struct msix_table_entry *entry;
+ uint8_t *src8;
+ uint16_t *src16;
+ uint32_t *src32;
+ uint64_t *src64;
+ uint64_t data;
+ size_t entry_offset;
+ int index;
+
+ pi = sc->psc_pi;
+ if (offset >= pi->pi_msix.pba_offset &&
+ offset < pi->pi_msix.pba_offset + pi->pi_msix.pba_size) {
+ switch(size) {
+ case 1:
+ src8 = (uint8_t *)(pi->pi_msix.pba_page + offset -
+ pi->pi_msix.pba_page_offset);
+ data = *src8;
+ break;
+ case 2:
+ src16 = (uint16_t *)(pi->pi_msix.pba_page + offset -
+ pi->pi_msix.pba_page_offset);
+ data = *src16;
+ break;
+ case 4:
+ src32 = (uint32_t *)(pi->pi_msix.pba_page + offset -
+ pi->pi_msix.pba_page_offset);
+ data = *src32;
+ break;
+ case 8:
+ src64 = (uint64_t *)(pi->pi_msix.pba_page + offset -
+ pi->pi_msix.pba_page_offset);
+ data = *src64;
+ break;
+ default:
+ return (-1);
+ }
+ return (data);
+ }
+
+ if (offset < pi->pi_msix.table_offset)
+ return (-1);
+
+ offset -= pi->pi_msix.table_offset;
+ index = offset / MSIX_TABLE_ENTRY_SIZE;
+ if (index >= pi->pi_msix.table_count)
+ return (-1);
+
+ entry = &pi->pi_msix.table[index];
+ entry_offset = offset % MSIX_TABLE_ENTRY_SIZE;
+
+ switch(size) {
+ case 1:
+ src8 = (uint8_t *)((void *)entry + entry_offset);
+ data = *src8;
+ break;
+ case 2:
+ src16 = (uint16_t *)((void *)entry + entry_offset);
+ data = *src16;
+ break;
+ case 4:
+ src32 = (uint32_t *)((void *)entry + entry_offset);
+ data = *src32;
+ break;
+ case 8:
+ src64 = (uint64_t *)((void *)entry + entry_offset);
+ data = *src64;
+ break;
+ default:
+ return (-1);
+ }
+
+ return (data);
+}
+
+static void
+msix_table_write(struct vmctx *ctx, int vcpu, struct passthru_softc *sc,
+ uint64_t offset, int size, uint64_t data)
+{
+ struct pci_devinst *pi;
+ struct msix_table_entry *entry;
+ uint8_t *dest8;
+ uint16_t *dest16;
+ uint32_t *dest32;
+ uint64_t *dest64;
+ size_t entry_offset;
+ uint32_t vector_control;
+ int index;
+
+ pi = sc->psc_pi;
+ if (offset >= pi->pi_msix.pba_offset &&
+ offset < pi->pi_msix.pba_offset + pi->pi_msix.pba_size) {
+ switch(size) {
+ case 1:
+ dest8 = (uint8_t *)(pi->pi_msix.pba_page + offset -
+ pi->pi_msix.pba_page_offset);
+ *dest8 = data;
+ break;
+ case 2:
+ dest16 = (uint16_t *)(pi->pi_msix.pba_page + offset -
+ pi->pi_msix.pba_page_offset);
+ *dest16 = data;
+ break;
+ case 4:
+ dest32 = (uint32_t *)(pi->pi_msix.pba_page + offset -
+ pi->pi_msix.pba_page_offset);
+ *dest32 = data;
+ break;
+ case 8:
+ dest64 = (uint64_t *)(pi->pi_msix.pba_page + offset -
+ pi->pi_msix.pba_page_offset);
+ *dest64 = data;
+ break;
+ default:
+ break;
+ }
+ return;
+ }
+
+ if (offset < pi->pi_msix.table_offset)
+ return;
+
+ offset -= pi->pi_msix.table_offset;
+ index = offset / MSIX_TABLE_ENTRY_SIZE;
+ if (index >= pi->pi_msix.table_count)
+ return;
+
+ entry = &pi->pi_msix.table[index];
+ entry_offset = offset % MSIX_TABLE_ENTRY_SIZE;
+
+ /* Only 4 byte naturally-aligned writes are supported */
+ assert(size == 4);
+ assert(entry_offset % 4 == 0);
+
+ vector_control = entry->vector_control;
+ dest32 = (uint32_t *)((void *)entry + entry_offset);
+ *dest32 = data;
+ /* If MSI-X hasn't been enabled, do nothing */
+ if (pi->pi_msix.enabled) {
+ /* If the entry is masked, don't set it up */
+ if ((entry->vector_control & PCIM_MSIX_VCTRL_MASK) == 0 ||
+ (vector_control & PCIM_MSIX_VCTRL_MASK) == 0) {
+ (void)vm_setup_pptdev_msix(ctx, vcpu,
+ sc->psc_sel.pc_bus, sc->psc_sel.pc_dev,
+ sc->psc_sel.pc_func, index, entry->addr,
+ entry->msg_data, entry->vector_control);
+ }
+ }
+}
+
+static int
+init_msix_table(struct vmctx *ctx, struct passthru_softc *sc, uint64_t base)
+{
+ int b, s, f;
+ int error, idx;
+ size_t len, remaining;
+ uint32_t table_size, table_offset;
+ uint32_t pba_size, pba_offset;
+ vm_paddr_t start;
+ struct pci_devinst *pi = sc->psc_pi;
+
+ assert(pci_msix_table_bar(pi) >= 0 && pci_msix_pba_bar(pi) >= 0);
+
+ b = sc->psc_sel.pc_bus;
+ s = sc->psc_sel.pc_dev;
+ f = sc->psc_sel.pc_func;
+
+ /*
+ * If the MSI-X table BAR maps memory intended for
+ * other uses, it is at least assured that the table
+ * either resides in its own page within the region,
+ * or it resides in a page shared with only the PBA.
+ */
+ table_offset = rounddown2(pi->pi_msix.table_offset, 4096);
+
+ table_size = pi->pi_msix.table_offset - table_offset;
+ table_size += pi->pi_msix.table_count * MSIX_TABLE_ENTRY_SIZE;
+ table_size = roundup2(table_size, 4096);
+
+ idx = pi->pi_msix.table_bar;
+ start = pi->pi_bar[idx].addr;
+ remaining = pi->pi_bar[idx].size;
+
+ if (pi->pi_msix.pba_bar == pi->pi_msix.table_bar) {
+ pba_offset = pi->pi_msix.pba_offset;
+ pba_size = pi->pi_msix.pba_size;
+ if (pba_offset >= table_offset + table_size ||
+ table_offset >= pba_offset + pba_size) {
+ /*
+ * If the PBA does not share a page with the MSI-x
+ * tables, no PBA emulation is required.
+ */
+ pi->pi_msix.pba_page = NULL;
+ pi->pi_msix.pba_page_offset = 0;
+ } else {
+ /*
+ * The PBA overlaps with either the first or last
+ * page of the MSI-X table region. Map the
+ * appropriate page.
+ */
+ if (pba_offset <= table_offset)
+ pi->pi_msix.pba_page_offset = table_offset;
+ else
+ pi->pi_msix.pba_page_offset = table_offset +
+ table_size - 4096;
+ pi->pi_msix.pba_page = mmap(NULL, 4096, PROT_READ |
+ PROT_WRITE, MAP_SHARED, memfd, start +
+ pi->pi_msix.pba_page_offset);
+ if (pi->pi_msix.pba_page == MAP_FAILED) {
+ warn(
+ "Failed to map PBA page for MSI-X on %d/%d/%d",
+ b, s, f);
+ return (-1);
+ }
+ }
+ }
+
+ /* Map everything before the MSI-X table */
+ if (table_offset > 0) {
+ len = table_offset;
+ error = vm_map_pptdev_mmio(ctx, b, s, f, start, len, base);
+ if (error)
+ return (error);
+
+ base += len;
+ start += len;
+ remaining -= len;
+ }
+
+ /* Skip the MSI-X table */
+ base += table_size;
+ start += table_size;
+ remaining -= table_size;
+
+ /* Map everything beyond the end of the MSI-X table */
+ if (remaining > 0) {
+ len = remaining;
+ error = vm_map_pptdev_mmio(ctx, b, s, f, start, len, base);
+ if (error)
+ return (error);
+ }
+
+ return (0);
+}
+
+static int
+cfginitbar(struct vmctx *ctx, struct passthru_softc *sc)
+{
+ int i, error;
+ struct pci_devinst *pi;
+ struct pci_bar_io bar;
+ enum pcibar_type bartype;
+ uint64_t base, size;
+
+ pi = sc->psc_pi;
+
+ /*
+ * Initialize BAR registers
+ */
+ for (i = 0; i <= PCI_BARMAX; i++) {
+ bzero(&bar, sizeof(bar));
+ bar.pbi_sel = sc->psc_sel;
+ bar.pbi_reg = PCIR_BAR(i);
+
+ if (ioctl(pcifd, PCIOCGETBAR, &bar) < 0)
+ continue;
+
+ if (PCI_BAR_IO(bar.pbi_base)) {
+ bartype = PCIBAR_IO;
+ base = bar.pbi_base & PCIM_BAR_IO_BASE;
+ } else {
+ switch (bar.pbi_base & PCIM_BAR_MEM_TYPE) {
+ case PCIM_BAR_MEM_64:
+ bartype = PCIBAR_MEM64;
+ break;
+ default:
+ bartype = PCIBAR_MEM32;
+ break;
+ }
+ base = bar.pbi_base & PCIM_BAR_MEM_BASE;
+ }
+ size = bar.pbi_length;
+
+ if (bartype != PCIBAR_IO) {
+ if (((base | size) & PAGE_MASK) != 0) {
+ warnx("passthru device %d/%d/%d BAR %d: "
+ "base %#lx or size %#lx not page aligned\n",
+ sc->psc_sel.pc_bus, sc->psc_sel.pc_dev,
+ sc->psc_sel.pc_func, i, base, size);
+ return (-1);
+ }
+ }
+
+ /* Cache information about the "real" BAR */
+ sc->psc_bar[i].type = bartype;
+ sc->psc_bar[i].size = size;
+ sc->psc_bar[i].addr = base;
+
+ /* Allocate the BAR in the guest I/O or MMIO space */
+ error = pci_emul_alloc_pbar(pi, i, base, bartype, size);
+ if (error)
+ return (-1);
+
+ /* The MSI-X table needs special handling */
+ if (i == pci_msix_table_bar(pi)) {
+ error = init_msix_table(ctx, sc, base);
+ if (error)
+ return (-1);
+ } else if (bartype != PCIBAR_IO) {
+ /* Map the physical BAR in the guest MMIO space */
+ error = vm_map_pptdev_mmio(ctx, sc->psc_sel.pc_bus,
+ sc->psc_sel.pc_dev, sc->psc_sel.pc_func,
+ pi->pi_bar[i].addr, pi->pi_bar[i].size, base);
+ if (error)
+ return (-1);
+ }
+
+ /*
+ * 64-bit BAR takes up two slots so skip the next one.
+ */
+ if (bartype == PCIBAR_MEM64) {
+ i++;
+ assert(i <= PCI_BARMAX);
+ sc->psc_bar[i].type = PCIBAR_MEMHI64;
+ }
+ }
+ return (0);
+}
+
+static int
+cfginit(struct vmctx *ctx, struct pci_devinst *pi, int bus, int slot, int func)
+{
+ int error;
+ struct passthru_softc *sc;
+
+ error = 1;
+ sc = pi->pi_arg;
+
+ bzero(&sc->psc_sel, sizeof(struct pcisel));
+ sc->psc_sel.pc_bus = bus;
+ sc->psc_sel.pc_dev = slot;
+ sc->psc_sel.pc_func = func;
+
+ if (cfginitmsi(sc) != 0) {
+ warnx("failed to initialize MSI for PCI %d/%d/%d",
+ bus, slot, func);
+ goto done;
+ }
+
+ if (cfginitbar(ctx, sc) != 0) {
+ warnx("failed to initialize BARs for PCI %d/%d/%d",
+ bus, slot, func);
+ goto done;
+ }
+
+ error = 0; /* success */
+done:
+ return (error);
+}
+
+static int
+passthru_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
+{
+ int bus, slot, func, error, memflags;
+ struct passthru_softc *sc;
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_t rights;
+ cap_ioctl_t pci_ioctls[] = { PCIOCREAD, PCIOCWRITE, PCIOCGETBAR };
+ cap_ioctl_t io_ioctls[] = { IODEV_PIO };
+#endif
+
+ sc = NULL;
+ error = 1;
+
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_init(&rights, CAP_IOCTL, CAP_READ, CAP_WRITE);
+#endif
+
+ memflags = vm_get_memflags(ctx);
+ if (!(memflags & VM_MEM_F_WIRED)) {
+ warnx("passthru requires guest memory to be wired");
+ goto done;
+ }
+
+ if (pcifd < 0) {
+ pcifd = open(_PATH_DEVPCI, O_RDWR, 0);
+ if (pcifd < 0) {
+ warn("failed to open %s", _PATH_DEVPCI);
+ goto done;
+ }
+ }
+
+#ifndef WITHOUT_CAPSICUM
+ if (cap_rights_limit(pcifd, &rights) == -1 && errno != ENOSYS)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+ if (cap_ioctls_limit(pcifd, pci_ioctls, nitems(pci_ioctls)) == -1 && errno != ENOSYS)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+#endif
+
+ if (iofd < 0) {
+ iofd = open(_PATH_DEVIO, O_RDWR, 0);
+ if (iofd < 0) {
+ warn("failed to open %s", _PATH_DEVIO);
+ goto done;
+ }
+ }
+
+#ifndef WITHOUT_CAPSICUM
+ if (cap_rights_limit(iofd, &rights) == -1 && errno != ENOSYS)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+ if (cap_ioctls_limit(iofd, io_ioctls, nitems(io_ioctls)) == -1 && errno != ENOSYS)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+#endif
+
+ if (memfd < 0) {
+ memfd = open(_PATH_MEM, O_RDWR, 0);
+ if (memfd < 0) {
+ warn("failed to open %s", _PATH_MEM);
+ goto done;
+ }
+ }
+
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_clear(&rights, CAP_IOCTL);
+ cap_rights_set(&rights, CAP_MMAP_RW);
+ if (cap_rights_limit(memfd, &rights) == -1 && errno != ENOSYS)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+#endif
+
+ if (opts == NULL ||
+ sscanf(opts, "%d/%d/%d", &bus, &slot, &func) != 3) {
+ warnx("invalid passthru options");
+ goto done;
+ }
+
+ if (vm_assign_pptdev(ctx, bus, slot, func) != 0) {
+ warnx("PCI device at %d/%d/%d is not using the ppt(4) driver",
+ bus, slot, func);
+ goto done;
+ }
+
+ sc = calloc(1, sizeof(struct passthru_softc));
+
+ pi->pi_arg = sc;
+ sc->psc_pi = pi;
+
+ /* initialize config space */
+ if ((error = cfginit(ctx, pi, bus, slot, func)) != 0)
+ goto done;
+
+ error = 0; /* success */
+done:
+ if (error) {
+ free(sc);
+ vm_unassign_pptdev(ctx, bus, slot, func);
+ }
+ return (error);
+}
+
+static int
+bar_access(int coff)
+{
+ if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1))
+ return (1);
+ else
+ return (0);
+}
+
+static int
+msicap_access(struct passthru_softc *sc, int coff)
+{
+ int caplen;
+
+ if (sc->psc_msi.capoff == 0)
+ return (0);
+
+ caplen = msi_caplen(sc->psc_msi.msgctrl);
+
+ if (coff >= sc->psc_msi.capoff && coff < sc->psc_msi.capoff + caplen)
+ return (1);
+ else
+ return (0);
+}
+
+static int
+msixcap_access(struct passthru_softc *sc, int coff)
+{
+ if (sc->psc_msix.capoff == 0)
+ return (0);
+
+ return (coff >= sc->psc_msix.capoff &&
+ coff < sc->psc_msix.capoff + MSIX_CAPLEN);
+}
+
+static int
+passthru_cfgread(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
+ int coff, int bytes, uint32_t *rv)
+{
+ struct passthru_softc *sc;
+
+ sc = pi->pi_arg;
+
+ /*
+ * PCI BARs and MSI capability is emulated.
+ */
+ if (bar_access(coff) || msicap_access(sc, coff))
+ return (-1);
+
+#ifdef LEGACY_SUPPORT
+ /*
+ * Emulate PCIR_CAP_PTR if this device does not support MSI capability
+ * natively.
+ */
+ if (sc->psc_msi.emulated) {
+ if (coff >= PCIR_CAP_PTR && coff < PCIR_CAP_PTR + 4)
+ return (-1);
+ }
+#endif
+
+ /* Everything else just read from the device's config space */
+ *rv = read_config(&sc->psc_sel, coff, bytes);
+
+ return (0);
+}
+
+static int
+passthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
+ int coff, int bytes, uint32_t val)
+{
+ int error, msix_table_entries, i;
+ struct passthru_softc *sc;
+
+ sc = pi->pi_arg;
+
+ /*
+ * PCI BARs are emulated
+ */
+ if (bar_access(coff))
+ return (-1);
+
+ /*
+ * MSI capability is emulated
+ */
+ if (msicap_access(sc, coff)) {
+ msicap_cfgwrite(pi, sc->psc_msi.capoff, coff, bytes, val);
+
+ error = vm_setup_pptdev_msi(ctx, vcpu, sc->psc_sel.pc_bus,
+ sc->psc_sel.pc_dev, sc->psc_sel.pc_func,
+ pi->pi_msi.addr, pi->pi_msi.msg_data,
+ pi->pi_msi.maxmsgnum);
+ if (error != 0)
+ err(1, "vm_setup_pptdev_msi");
+ return (0);
+ }
+
+ if (msixcap_access(sc, coff)) {
+ msixcap_cfgwrite(pi, sc->psc_msix.capoff, coff, bytes, val);
+ if (pi->pi_msix.enabled) {
+ msix_table_entries = pi->pi_msix.table_count;
+ for (i = 0; i < msix_table_entries; i++) {
+ error = vm_setup_pptdev_msix(ctx, vcpu,
+ sc->psc_sel.pc_bus, sc->psc_sel.pc_dev,
+ sc->psc_sel.pc_func, i,
+ pi->pi_msix.table[i].addr,
+ pi->pi_msix.table[i].msg_data,
+ pi->pi_msix.table[i].vector_control);
+
+ if (error)
+ err(1, "vm_setup_pptdev_msix");
+ }
+ }
+ return (0);
+ }
+
+#ifdef LEGACY_SUPPORT
+ /*
+ * If this device does not support MSI natively then we cannot let
+ * the guest disable legacy interrupts from the device. It is the
+ * legacy interrupt that is triggering the virtual MSI to the guest.
+ */
+ if (sc->psc_msi.emulated && pci_msi_enabled(pi)) {
+ if (coff == PCIR_COMMAND && bytes == 2)
+ val &= ~PCIM_CMD_INTxDIS;
+ }
+#endif
+
+ write_config(&sc->psc_sel, coff, bytes, val);
+
+ return (0);
+}
+
+static void
+passthru_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
+ uint64_t offset, int size, uint64_t value)
+{
+ struct passthru_softc *sc;
+ struct iodev_pio_req pio;
+
+ sc = pi->pi_arg;
+
+ if (baridx == pci_msix_table_bar(pi)) {
+ msix_table_write(ctx, vcpu, sc, offset, size, value);
+ } else {
+ assert(pi->pi_bar[baridx].type == PCIBAR_IO);
+ bzero(&pio, sizeof(struct iodev_pio_req));
+ pio.access = IODEV_PIO_WRITE;
+ pio.port = sc->psc_bar[baridx].addr + offset;
+ pio.width = size;
+ pio.val = value;
+
+ (void)ioctl(iofd, IODEV_PIO, &pio);
+ }
+}
+
+static uint64_t
+passthru_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
+ uint64_t offset, int size)
+{
+ struct passthru_softc *sc;
+ struct iodev_pio_req pio;
+ uint64_t val;
+
+ sc = pi->pi_arg;
+
+ if (baridx == pci_msix_table_bar(pi)) {
+ val = msix_table_read(sc, offset, size);
+ } else {
+ assert(pi->pi_bar[baridx].type == PCIBAR_IO);
+ bzero(&pio, sizeof(struct iodev_pio_req));
+ pio.access = IODEV_PIO_READ;
+ pio.port = sc->psc_bar[baridx].addr + offset;
+ pio.width = size;
+ pio.val = 0;
+
+ (void)ioctl(iofd, IODEV_PIO, &pio);
+
+ val = pio.val;
+ }
+
+ return (val);
+}
+
+struct pci_devemu passthru = {
+ .pe_emu = "passthru",
+ .pe_init = passthru_init,
+ .pe_cfgwrite = passthru_cfgwrite,
+ .pe_cfgread = passthru_cfgread,
+ .pe_barwrite = passthru_write,
+ .pe_barread = passthru_read,
+};
+PCI_EMUL_SET(passthru);
diff --git a/usr/src/cmd/bhyve/pci_uart.c b/usr/src/cmd/bhyve/pci_uart.c
new file mode 100644
index 0000000000..21b93bfd69
--- /dev/null
+++ b/usr/src/cmd/bhyve/pci_uart.c
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 2012 NetApp, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+#include <stdio.h>
+
+#include "bhyverun.h"
+#include "pci_emul.h"
+#include "uart_emul.h"
+
+/*
+ * Pick a PCI vid/did of a chip with a single uart at
+ * BAR0, that most versions of FreeBSD can understand:
+ * Siig CyberSerial 1-port.
+ */
+#define COM_VENDOR 0x131f
+#define COM_DEV 0x2000
+
+static void
+pci_uart_intr_assert(void *arg)
+{
+ struct pci_devinst *pi = arg;
+
+ pci_lintr_assert(pi);
+}
+
+static void
+pci_uart_intr_deassert(void *arg)
+{
+ struct pci_devinst *pi = arg;
+
+ pci_lintr_deassert(pi);
+}
+
+static void
+pci_uart_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
+ int baridx, uint64_t offset, int size, uint64_t value)
+{
+
+ assert(baridx == 0);
+ assert(size == 1);
+
+ uart_write(pi->pi_arg, offset, value);
+}
+
+uint64_t
+pci_uart_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
+ int baridx, uint64_t offset, int size)
+{
+ uint8_t val;
+
+ assert(baridx == 0);
+ assert(size == 1);
+
+ val = uart_read(pi->pi_arg, offset);
+ return (val);
+}
+
+static int
+pci_uart_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
+{
+ struct uart_softc *sc;
+
+ pci_emul_alloc_bar(pi, 0, PCIBAR_IO, UART_IO_BAR_SIZE);
+ pci_lintr_request(pi);
+
+ /* initialize config space */
+ pci_set_cfgdata16(pi, PCIR_DEVICE, COM_DEV);
+ pci_set_cfgdata16(pi, PCIR_VENDOR, COM_VENDOR);
+ pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_SIMPLECOMM);
+
+ sc = uart_init(pci_uart_intr_assert, pci_uart_intr_deassert, pi);
+ pi->pi_arg = sc;
+
+ if (uart_set_backend(sc, opts) != 0) {
+ fprintf(stderr, "Unable to initialize backend '%s' for "
+ "pci uart at %d:%d\n", opts, pi->pi_slot, pi->pi_func);
+ return (-1);
+ }
+
+ return (0);
+}
+
+struct pci_devemu pci_de_com = {
+ .pe_emu = "uart",
+ .pe_init = pci_uart_init,
+ .pe_barwrite = pci_uart_write,
+ .pe_barread = pci_uart_read
+};
+PCI_EMUL_SET(pci_de_com);
diff --git a/usr/src/cmd/bhyve/pci_virtio_block.c b/usr/src/cmd/bhyve/pci_virtio_block.c
index cba1e26189..abec2d832a 100644
--- a/usr/src/cmd/bhyve/pci_virtio_block.c
+++ b/usr/src/cmd/bhyve/pci_virtio_block.c
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/pci_virtio_block.c 266935 2014-06-01 02:47:09Z neel $
+ * $FreeBSD$
*/
/*
* This file and its contents are supplied under the terms of the
@@ -40,7 +40,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/usr.sbin/bhyve/pci_virtio_block.c 266935 2014-06-01 02:47:09Z neel $");
+__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/linker_set.h>
@@ -64,15 +64,10 @@ __FBSDID("$FreeBSD: head/usr.sbin/bhyve/pci_virtio_block.c 266935 2014-06-01 02:
#include "bhyverun.h"
#include "pci_emul.h"
#include "virtio.h"
+#include "block_if.h"
#define VTBLK_RINGSZ 64
-#ifdef __FreeBSD__
-#define VTBLK_MAXSEGS 32
-#else
-#define VTBLK_MAXSEGS 16
-#endif
-
#define VTBLK_S_OK 0
#define VTBLK_S_IOERR 1
#define VTBLK_S_UNSUPP 2
@@ -81,7 +76,9 @@ __FBSDID("$FreeBSD: head/usr.sbin/bhyve/pci_virtio_block.c 266935 2014-06-01 02:
/* Capability bits */
#define VTBLK_F_SEG_MAX (1 << 2) /* Maximum request segments */
-#define VTBLK_F_BLK_SIZE (1 << 6) /* cfg block size valid */
+#define VTBLK_F_BLK_SIZE (1 << 6) /* cfg block size valid */
+#define VTBLK_F_FLUSH (1 << 9) /* Cache flush support */
+#define VTBLK_F_TOPOLOGY (1 << 10) /* Optimal I/O alignment */
/*
* Host capabilities
@@ -89,6 +86,8 @@ __FBSDID("$FreeBSD: head/usr.sbin/bhyve/pci_virtio_block.c 266935 2014-06-01 02:
#define VTBLK_S_HOSTCAPS \
( VTBLK_F_SEG_MAX | \
VTBLK_F_BLK_SIZE | \
+ VTBLK_F_FLUSH | \
+ VTBLK_F_TOPOLOGY | \
VIRTIO_RING_F_INDIRECT_DESC ) /* indirect descriptors */
/*
@@ -98,11 +97,19 @@ struct vtblk_config {
uint64_t vbc_capacity;
uint32_t vbc_size_max;
uint32_t vbc_seg_max;
- uint16_t vbc_geom_c;
- uint8_t vbc_geom_h;
- uint8_t vbc_geom_s;
+ struct {
+ uint16_t cylinders;
+ uint8_t heads;
+ uint8_t sectors;
+ } vbc_geometry;
uint32_t vbc_blk_size;
- uint32_t vbc_sectors_max;
+ struct {
+ uint8_t physical_block_exp;
+ uint8_t alignment_offset;
+ uint16_t min_io_size;
+ uint32_t opt_io_size;
+ } vbc_topology;
+ uint8_t vbc_writeback;
} __packed;
/*
@@ -111,6 +118,8 @@ struct vtblk_config {
struct virtio_blk_hdr {
#define VBH_OP_READ 0
#define VBH_OP_WRITE 1
+#define VBH_OP_FLUSH 4
+#define VBH_OP_FLUSH_OUT 5
#define VBH_OP_IDENT 8
#define VBH_FLAG_BARRIER 0x80000000 /* OR'ed into vbh_type */
uint32_t vbh_type;
@@ -125,6 +134,13 @@ static int pci_vtblk_debug;
#define DPRINTF(params) if (pci_vtblk_debug) printf params
#define WPRINTF(params) printf params
+struct pci_vtblk_ioreq {
+ struct blockif_req io_req;
+ struct pci_vtblk_softc *io_sc;
+ uint8_t *io_status;
+ uint16_t io_idx;
+};
+
/*
* Per-device softc
*/
@@ -132,9 +148,10 @@ struct pci_vtblk_softc {
struct virtio_softc vbsc_vs;
pthread_mutex_t vsc_mtx;
struct vqueue_info vbsc_vq;
- int vbsc_fd;
- struct vtblk_config vbsc_cfg;
+ struct vtblk_config vbsc_cfg;
+ struct blockif_ctxt *bc;
char vbsc_ident[VTBLK_BLK_ID_BYTES];
+ struct pci_vtblk_ioreq vbsc_ios[VTBLK_RINGSZ];
};
static void pci_vtblk_reset(void *);
@@ -150,6 +167,7 @@ static struct virtio_consts vtblk_vi_consts = {
pci_vtblk_notify, /* device-wide qnotify */
pci_vtblk_cfgread, /* read PCI config */
pci_vtblk_cfgwrite, /* write PCI config */
+ NULL, /* apply negotiated features */
VTBLK_S_HOSTCAPS, /* our capabilities */
};
@@ -163,19 +181,50 @@ pci_vtblk_reset(void *vsc)
}
static void
+pci_vtblk_done_locked(struct pci_vtblk_ioreq *io, int err)
+{
+ struct pci_vtblk_softc *sc = io->io_sc;
+
+ /* convert errno into a virtio block error return */
+ if (err == EOPNOTSUPP || err == ENOSYS)
+ *io->io_status = VTBLK_S_UNSUPP;
+ else if (err != 0)
+ *io->io_status = VTBLK_S_IOERR;
+ else
+ *io->io_status = VTBLK_S_OK;
+
+ /*
+ * Return the descriptor back to the host.
+ * We wrote 1 byte (our status) to host.
+ */
+ vq_relchain(&sc->vbsc_vq, io->io_idx, 1);
+ vq_endchains(&sc->vbsc_vq, 0);
+}
+
+static void
+pci_vtblk_done(struct blockif_req *br, int err)
+{
+ struct pci_vtblk_ioreq *io = br->br_param;
+ struct pci_vtblk_softc *sc = io->io_sc;
+
+ pthread_mutex_lock(&sc->vsc_mtx);
+ pci_vtblk_done_locked(io, err);
+ pthread_mutex_unlock(&sc->vsc_mtx);
+}
+
+static void
pci_vtblk_proc(struct pci_vtblk_softc *sc, struct vqueue_info *vq)
{
struct virtio_blk_hdr *vbh;
- uint8_t *status;
+ struct pci_vtblk_ioreq *io;
int i, n;
int err;
- int iolen;
+ ssize_t iolen;
int writeop, type;
- off_t offset;
- struct iovec iov[VTBLK_MAXSEGS + 2];
- uint16_t flags[VTBLK_MAXSEGS + 2];
+ struct iovec iov[BLOCKIF_IOV_MAX + 2];
+ uint16_t idx, flags[BLOCKIF_IOV_MAX + 2];
- n = vq_getchain(vq, iov, VTBLK_MAXSEGS + 2, flags);
+ n = vq_getchain(vq, &idx, iov, BLOCKIF_IOV_MAX + 2, flags);
/*
* The first descriptor will be the read-only fixed header,
@@ -185,13 +234,16 @@ pci_vtblk_proc(struct pci_vtblk_softc *sc, struct vqueue_info *vq)
* XXX - note - this fails on crash dump, which does a
* VIRTIO_BLK_T_FLUSH with a zero transfer length
*/
- assert(n >= 2 && n <= VTBLK_MAXSEGS + 2);
+ assert(n >= 2 && n <= BLOCKIF_IOV_MAX + 2);
+ io = &sc->vbsc_ios[idx];
assert((flags[0] & VRING_DESC_F_WRITE) == 0);
assert(iov[0].iov_len == sizeof(struct virtio_blk_hdr));
vbh = (struct virtio_blk_hdr *)iov[0].iov_base;
-
- status = (uint8_t *)iov[--n].iov_base;
+ memcpy(&io->io_req.br_iov, &iov[1], sizeof(struct iovec) * (n - 2));
+ io->io_req.br_iovcnt = n - 2;
+ io->io_req.br_offset = vbh->vbh_sector * DEV_BSIZE;
+ io->io_status = (uint8_t *)iov[--n].iov_base;
assert(iov[n].iov_len == 1);
assert(flags[n] & VRING_DESC_F_WRITE);
@@ -203,8 +255,6 @@ pci_vtblk_proc(struct pci_vtblk_softc *sc, struct vqueue_info *vq)
type = vbh->vbh_type & ~VBH_FLAG_BARRIER;
writeop = (type == VBH_OP_WRITE);
- offset = vbh->vbh_sector * DEV_BSIZE;
-
iolen = 0;
for (i = 1; i < n; i++) {
/*
@@ -216,42 +266,36 @@ pci_vtblk_proc(struct pci_vtblk_softc *sc, struct vqueue_info *vq)
assert(((flags[i] & VRING_DESC_F_WRITE) == 0) == writeop);
iolen += iov[i].iov_len;
}
+ io->io_req.br_resid = iolen;
- DPRINTF(("virtio-block: %s op, %d bytes, %d segs, offset %ld\n\r",
- writeop ? "write" : "read/ident", iolen, i - 1, offset));
+ DPRINTF(("virtio-block: %s op, %zd bytes, %d segs, offset %ld\n\r",
+ writeop ? "write" : "read/ident", iolen, i - 1,
+ io->io_req.br_offset));
switch (type) {
+ case VBH_OP_READ:
+ err = blockif_read(sc->bc, &io->io_req);
+ break;
case VBH_OP_WRITE:
- err = pwritev(sc->vbsc_fd, iov + 1, i - 1, offset);
+ err = blockif_write(sc->bc, &io->io_req);
break;
- case VBH_OP_READ:
- err = preadv(sc->vbsc_fd, iov + 1, i - 1, offset);
+ case VBH_OP_FLUSH:
+ case VBH_OP_FLUSH_OUT:
+ err = blockif_flush(sc->bc, &io->io_req);
break;
case VBH_OP_IDENT:
/* Assume a single buffer */
- strlcpy(iov[1].iov_base, sc->vbsc_ident,
+ /* S/n equal to buffer is not zero-terminated. */
+ memset(iov[1].iov_base, 0, iov[1].iov_len);
+ strncpy(iov[1].iov_base, sc->vbsc_ident,
MIN(iov[1].iov_len, sizeof(sc->vbsc_ident)));
- err = 0;
- break;
+ pci_vtblk_done_locked(io, 0);
+ return;
default:
- err = -ENOSYS;
- break;
+ pci_vtblk_done_locked(io, EOPNOTSUPP);
+ return;
}
-
- /* convert errno into a virtio block error return */
- if (err < 0) {
- if (err == -ENOSYS)
- *status = VTBLK_S_UNSUPP;
- else
- *status = VTBLK_S_IOERR;
- } else
- *status = VTBLK_S_OK;
-
- /*
- * Return the descriptor back to the host.
- * We wrote 1 byte (our status) to host.
- */
- vq_relchain(vq, 1);
+ assert(err == 0);
}
static void
@@ -259,22 +303,20 @@ pci_vtblk_notify(void *vsc, struct vqueue_info *vq)
{
struct pci_vtblk_softc *sc = vsc;
- vq_startchains(vq);
while (vq_has_descs(vq))
pci_vtblk_proc(sc, vq);
- vq_endchains(vq, 1); /* Generate interrupt if appropriate. */
}
static int
pci_vtblk_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
{
- struct stat sbuf;
+ char bident[sizeof("XX:X:X")];
+ struct blockif_ctxt *bctxt;
MD5_CTX mdctx;
u_char digest[16];
struct pci_vtblk_softc *sc;
- off_t size;
- int fd;
- int sectsz;
+ off_t size;
+ int i, sectsz, sts, sto;
if (opts == NULL) {
printf("virtio-block: backing device required\n");
@@ -284,40 +326,26 @@ pci_vtblk_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
/*
* The supplied backing file has to exist
*/
- fd = open(opts, O_RDWR);
- if (fd < 0) {
+ snprintf(bident, sizeof(bident), "%d:%d", pi->pi_slot, pi->pi_func);
+ bctxt = blockif_open(opts, bident);
+ if (bctxt == NULL) {
perror("Could not open backing file");
return (1);
}
- if (fstat(fd, &sbuf) < 0) {
- perror("Could not stat backing file");
- close(fd);
- return (1);
- }
-
- /*
- * Deal with raw devices
- */
- size = sbuf.st_size;
- sectsz = DEV_BSIZE;
-#ifdef __FreeBSD__
- if (S_ISCHR(sbuf.st_mode)) {
- if (ioctl(fd, DIOCGMEDIASIZE, &size) < 0 ||
- ioctl(fd, DIOCGSECTORSIZE, &sectsz)) {
- perror("Could not fetch dev blk/sector size");
- close(fd);
- return (1);
- }
- assert(size != 0);
- assert(sectsz != 0);
- }
-#endif
+ size = blockif_size(bctxt);
+ sectsz = blockif_sectsz(bctxt);
+ blockif_psectsz(bctxt, &sts, &sto);
sc = calloc(1, sizeof(struct pci_vtblk_softc));
-
- /* record fd of storage device/file */
- sc->vbsc_fd = fd;
+ sc->bc = bctxt;
+ for (i = 0; i < VTBLK_RINGSZ; i++) {
+ struct pci_vtblk_ioreq *io = &sc->vbsc_ios[i];
+ io->io_req.br_callback = pci_vtblk_done;
+ io->io_req.br_param = io;
+ io->io_sc = sc;
+ io->io_idx = i;
+ }
pthread_mutex_init(&sc->vsc_mtx, NULL);
@@ -340,13 +368,19 @@ pci_vtblk_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
/* setup virtio block config space */
sc->vbsc_cfg.vbc_capacity = size / DEV_BSIZE; /* 512-byte units */
- sc->vbsc_cfg.vbc_seg_max = VTBLK_MAXSEGS;
- sc->vbsc_cfg.vbc_blk_size = sectsz;
sc->vbsc_cfg.vbc_size_max = 0; /* not negotiated */
- sc->vbsc_cfg.vbc_geom_c = 0; /* no geometry */
- sc->vbsc_cfg.vbc_geom_h = 0;
- sc->vbsc_cfg.vbc_geom_s = 0;
- sc->vbsc_cfg.vbc_sectors_max = 0;
+ sc->vbsc_cfg.vbc_seg_max = BLOCKIF_IOV_MAX;
+ sc->vbsc_cfg.vbc_geometry.cylinders = 0; /* no geometry */
+ sc->vbsc_cfg.vbc_geometry.heads = 0;
+ sc->vbsc_cfg.vbc_geometry.sectors = 0;
+ sc->vbsc_cfg.vbc_blk_size = sectsz;
+ sc->vbsc_cfg.vbc_topology.physical_block_exp =
+ (sts > sectsz) ? (ffsll(sts / sectsz) - 1) : 0;
+ sc->vbsc_cfg.vbc_topology.alignment_offset =
+ (sto != 0) ? ((sts - sto) / sectsz) : 0;
+ sc->vbsc_cfg.vbc_topology.min_io_size = 0;
+ sc->vbsc_cfg.vbc_topology.opt_io_size = 0;
+ sc->vbsc_cfg.vbc_writeback = 0;
/*
* Should we move some of this into virtio.c? Could
@@ -357,9 +391,13 @@ pci_vtblk_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR);
pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_STORAGE);
pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_TYPE_BLOCK);
+ pci_set_cfgdata16(pi, PCIR_SUBVEND_0, VIRTIO_VENDOR);
- if (vi_intr_init(&sc->vbsc_vs, 1, fbsdrun_virtio_msix()))
+ if (vi_intr_init(&sc->vbsc_vs, 1, fbsdrun_virtio_msix())) {
+ blockif_close(sc->bc);
+ free(sc);
return (1);
+ }
vi_set_io_bar(&sc->vbsc_vs, 0);
return (0);
}
diff --git a/usr/src/cmd/bhyve/pci_virtio_net.c b/usr/src/cmd/bhyve/pci_virtio_net.c
index 2c63612a8e..72290aafbb 100644
--- a/usr/src/cmd/bhyve/pci_virtio_net.c
+++ b/usr/src/cmd/bhyve/pci_virtio_net.c
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/pci_virtio_net.c 253440 2013-07-17 23:37:33Z grehan $
+ * $FreeBSD$
*/
/*
* This file and its contents are supplied under the terms of the
@@ -40,15 +40,26 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/usr.sbin/bhyve/pci_virtio_net.c 253440 2013-07-17 23:37:33Z grehan $");
+__FBSDID("$FreeBSD$");
#include <sys/param.h>
+#ifndef WITHOUT_CAPSICUM
+#include <sys/capsicum.h>
+#endif
#include <sys/linker_set.h>
#include <sys/select.h>
#include <sys/uio.h>
#include <sys/ioctl.h>
+#include <machine/atomic.h>
#include <net/ethernet.h>
+#ifdef __FreeBSD__
+#ifndef NETMAP_WITH_LIBS
+#define NETMAP_WITH_LIBS
+#endif
+#include <net/netmap_user.h>
+#endif
+#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
@@ -61,21 +72,22 @@ __FBSDID("$FreeBSD: head/usr.sbin/bhyve/pci_virtio_net.c 253440 2013-07-17 23:37
#include <md5.h>
#include <pthread.h>
#include <pthread_np.h>
-#ifndef __FreeBSD__
+#include <sysexits.h>
+#ifndef __FreeBSD__
#include <poll.h>
#include <libdlpi.h>
#endif
#include "bhyverun.h"
#include "pci_emul.h"
-#ifdef __FreeBSD__
+#ifdef __FreeBSD__
#include "mevent.h"
#endif
#include "virtio.h"
#define VTNET_RINGSZ 1024
-#define VTNET_MAXSEGS 32
+#define VTNET_MAXSEGS 256
/*
* Host capabilities. Note that we only offer a few of these.
@@ -102,7 +114,7 @@ __FBSDID("$FreeBSD: head/usr.sbin/bhyve/pci_virtio_net.c 253440 2013-07-17 23:37
#define VTNET_S_HOSTCAPS \
( VIRTIO_NET_F_MAC | VIRTIO_NET_F_MRG_RXBUF | VIRTIO_NET_F_STATUS | \
- VIRTIO_F_NOTIFY_ON_EMPTY)
+ VIRTIO_F_NOTIFY_ON_EMPTY | VIRTIO_RING_F_INDIRECT_DESC)
/*
* PCI config-space "registers"
@@ -156,25 +168,35 @@ struct pci_vtnet_softc {
dlpi_handle_t vsc_dhp;
int vsc_dlpifd;
#endif
+ struct nm_desc *vsc_nmd;
+
int vsc_rx_ready;
volatile int resetting; /* set and checked outside lock */
- uint32_t vsc_features;
+ uint64_t vsc_features; /* negotiated features */
+
struct virtio_net_config vsc_config;
pthread_mutex_t rx_mtx;
int rx_in_progress;
+ int rx_vhdrlen;
+ int rx_merge; /* merged rx bufs in use */
pthread_t tx_tid;
pthread_mutex_t tx_mtx;
pthread_cond_t tx_cond;
int tx_in_progress;
+
+ void (*pci_vtnet_rx)(struct pci_vtnet_softc *sc);
+ void (*pci_vtnet_tx)(struct pci_vtnet_softc *sc, struct iovec *iov,
+ int iovcnt, int len);
};
static void pci_vtnet_reset(void *);
/* static void pci_vtnet_notify(void *, struct vqueue_info *); */
static int pci_vtnet_cfgread(void *, int, int, uint32_t *);
static int pci_vtnet_cfgwrite(void *, int, int, uint32_t);
+static void pci_vtnet_neg_features(void *, uint64_t);
static struct virtio_consts vtnet_vi_consts = {
"vtnet", /* our name */
@@ -184,6 +206,7 @@ static struct virtio_consts vtnet_vi_consts = {
NULL, /* device-wide qnotify -- not used */
pci_vtnet_cfgread, /* read PCI config */
pci_vtnet_cfgwrite, /* write PCI config */
+ pci_vtnet_neg_features, /* apply negotiated features */
VTNET_S_HOSTCAPS, /* our capabilities */
};
@@ -236,6 +259,8 @@ pci_vtnet_reset(void *vsc)
pci_vtnet_rxwait(sc);
sc->vsc_rx_ready = 0;
+ sc->rx_merge = 1;
+ sc->rx_vhdrlen = sizeof(struct virtio_net_rxhdr);
/* now reset rings, MSI-X vectors, and negotiated capabilities */
vi_reset_dev(&sc->vsc_vs);
@@ -246,7 +271,7 @@ pci_vtnet_reset(void *vsc)
/*
* Called to send a buffer chain out to the tap device
*/
-#ifdef __FreeBSD__
+#ifdef __FreeBSD__
static void
pci_vtnet_tap_tx(struct pci_vtnet_softc *sc, struct iovec *iov, int iovcnt,
int len)
@@ -280,9 +305,9 @@ pci_vtnet_tap_tx(struct pci_vtnet_softc *sc, struct iovec *iov, int iovcnt,
iov[i].iov_base, iov[i].iov_len, NULL);
}
}
-#endif
+#endif /* __FreeBSD__ */
-#ifdef __FreeBSD__
+#ifdef __FreeBSD__
/*
* Called when there is read activity on the tap file descriptor.
* Each buffer posted by the guest is assumed to be able to contain
@@ -291,23 +316,43 @@ pci_vtnet_tap_tx(struct pci_vtnet_softc *sc, struct iovec *iov, int iovcnt,
* is no need for it to be per-vtnet or locked.
*/
static uint8_t dummybuf[2048];
-#endif
+#endif /* __FreeBSD__ */
+
+static __inline struct iovec *
+rx_iov_trim(struct iovec *iov, int *niov, int tlen)
+{
+ struct iovec *riov;
+
+ /* XXX short-cut: assume first segment is >= tlen */
+ assert(iov[0].iov_len >= tlen);
+
+ iov[0].iov_len -= tlen;
+ if (iov[0].iov_len == 0) {
+ assert(*niov > 1);
+ *niov -= 1;
+ riov = &iov[1];
+ } else {
+ iov[0].iov_base = (void *)((uintptr_t)iov[0].iov_base + tlen);
+ riov = &iov[0];
+ }
+
+ return (riov);
+}
static void
pci_vtnet_tap_rx(struct pci_vtnet_softc *sc)
{
+ struct iovec iov[VTNET_MAXSEGS], *riov;
struct vqueue_info *vq;
- struct virtio_net_rxhdr *vrx;
- uint8_t *buf;
+ void *vrx;
+ int n;
#ifdef __FreeBSD__
int len;
-#endif
- struct iovec iov[VTNET_MAXSEGS];
-#ifndef __FreeBSD__
+#else
size_t len;
int ret;
#endif
- int total_len = 0;
+ uint16_t idx;
/*
* Should never be called without a valid tap fd
@@ -336,7 +381,6 @@ pci_vtnet_tap_rx(struct pci_vtnet_softc *sc)
* Check for available rx buffers
*/
vq = &sc->vsc_queues[VTNET_RXQ];
- vq_startchains(vq);
if (!vq_has_descs(vq)) {
/*
* Drop the packet and try later. Interrupt on
@@ -353,109 +397,267 @@ pci_vtnet_tap_rx(struct pci_vtnet_softc *sc)
/*
* Get descriptor chain
*/
- if (sc->vsc_vs.vs_negotiated_caps & VIRTIO_NET_F_MRG_RXBUF) {
- assert(vq_getchain(vq, iov, 1, NULL) == 1);
+ n = vq_getchain(vq, &idx, iov, VTNET_MAXSEGS, NULL);
+ assert(n >= 1 && n <= VTNET_MAXSEGS);
- /*
- * Get a pointer to the rx header, and use the
- * data immediately following it for the packet buffer.
- */
- vrx = (struct virtio_net_rxhdr *)iov[0].iov_base;
- buf = (uint8_t *)(vrx + 1);
- total_len = iov[0].iov_len;
+ /*
+ * Get a pointer to the rx header, and use the
+ * data immediately following it for the packet buffer.
+ */
+ vrx = iov[0].iov_base;
+ riov = rx_iov_trim(iov, &n, sc->rx_vhdrlen);
#ifdef __FreeBSD__
- len = read(sc->vsc_tapfd, buf,
- iov[0].iov_len - sizeof(struct virtio_net_rxhdr));
-
- if (len < 0 && errno == EWOULDBLOCK) {
- /*
- * No more packets, but still some avail ring
- * entries. Interrupt if needed/appropriate.
- */
- vq_endchains(vq, 0);
- return;
- }
+ len = readv(sc->vsc_tapfd, riov, n);
#else
- len = iov[0].iov_len - sizeof(struct virtio_net_rxhdr);
- ret = dlpi_recv(sc->vsc_dhp, NULL, NULL, buf,
- &len, 0, NULL);
- if (ret != DLPI_SUCCESS) {
- /*
- * No more packets, but still some avail ring
- * entries. Interrupt if needed/appropriate.
- */
- vq_endchains(vq, 0);
- return;
- }
+ len = riov[0].iov_len;
+ ret = dlpi_recv(sc->vsc_dhp, NULL, NULL,
+ (uint8_t *)riov[0].iov_base, &len, 0, NULL);
+ if (ret != DLPI_SUCCESS) {
+ errno = EWOULDBLOCK;
+ len = 0;
+ }
#endif
- } else {
- int i;
- int num_segs;
- num_segs = vq_getchain(vq, iov,
- VTNET_MAXSEGS, NULL);
- vrx = (struct virtio_net_rxhdr *)iov[0].iov_base;
- total_len = iov[0].iov_len;
- for (i = 1; i < num_segs; i++) {
- buf = (uint8_t *)iov[i].iov_base;
- total_len += iov[i].iov_len;
+ if (len <= 0 && errno == EWOULDBLOCK) {
+ /*
+ * No more packets, but still some avail ring
+ * entries. Interrupt if needed/appropriate.
+ */
+ vq_retchain(vq);
+ vq_endchains(vq, 0);
+ return;
+ }
+
+ /*
+ * The only valid field in the rx packet header is the
+ * number of buffers if merged rx bufs were negotiated.
+ */
+ memset(vrx, 0, sc->rx_vhdrlen);
+
+ if (sc->rx_merge) {
+ struct virtio_net_rxhdr *vrxh;
+
+ vrxh = vrx;
+ vrxh->vrh_bufs = 1;
+ }
+
+ /*
+ * Release this chain and handle more chains.
+ */
+ vq_relchain(vq, idx, len + sc->rx_vhdrlen);
+ } while (vq_has_descs(vq));
+
+ /* Interrupt if needed, including for NOTIFY_ON_EMPTY. */
+ vq_endchains(vq, 1);
+}
+
#ifdef __FreeBSD__
- len = read(sc->vsc_tapfd, buf, iov[i].iov_len);
- if (len < 0 && errno == EWOULDBLOCK) {
- /*
- * No more packets,
- * but still some avail ring entries.
- * Interrupt if needed/appropriate.
- */
- break;
- }
-#else
- len = iov[i].iov_len;
- ret = dlpi_recv(sc->vsc_dhp, NULL, NULL, buf,
- &len, 0, NULL);
- if (ret != DLPI_SUCCESS) {
- /*
- * No more packets,
- * but still some avail ring entries.
- * Interrupt if needed/appropriate.
- */
- total_len = 0;
- break;
- }
-#endif
- }
- if (total_len == 0) {
- vq_endchains(vq, 0);
- return;
- }
+static __inline int
+pci_vtnet_netmap_writev(struct nm_desc *nmd, struct iovec *iov, int iovcnt)
+{
+ int r, i;
+ int len = 0;
+
+ for (r = nmd->cur_tx_ring; ; ) {
+ struct netmap_ring *ring = NETMAP_TXRING(nmd->nifp, r);
+ uint32_t cur, idx;
+ char *buf;
+
+ if (nm_ring_empty(ring)) {
+ r++;
+ if (r > nmd->last_tx_ring)
+ r = nmd->first_tx_ring;
+ if (r == nmd->cur_tx_ring)
+ break;
+ continue;
+ }
+ cur = ring->cur;
+ idx = ring->slot[cur].buf_idx;
+ buf = NETMAP_BUF(ring, idx);
+
+ for (i = 0; i < iovcnt; i++) {
+ if (len + iov[i].iov_len > 2048)
+ break;
+ memcpy(&buf[len], iov[i].iov_base, iov[i].iov_len);
+ len += iov[i].iov_len;
+ }
+ ring->slot[cur].len = len;
+ ring->head = ring->cur = nm_ring_next(ring, cur);
+ nmd->cur_tx_ring = r;
+ ioctl(nmd->fd, NIOCTXSYNC, NULL);
+ break;
+ }
+
+ return (len);
+}
+
+static __inline int
+pci_vtnet_netmap_readv(struct nm_desc *nmd, struct iovec *iov, int iovcnt)
+{
+ int len = 0;
+ int i = 0;
+ int r;
+
+ for (r = nmd->cur_rx_ring; ; ) {
+ struct netmap_ring *ring = NETMAP_RXRING(nmd->nifp, r);
+ uint32_t cur, idx;
+ char *buf;
+ size_t left;
+
+ if (nm_ring_empty(ring)) {
+ r++;
+ if (r > nmd->last_rx_ring)
+ r = nmd->first_rx_ring;
+ if (r == nmd->cur_rx_ring)
+ break;
+ continue;
+ }
+ cur = ring->cur;
+ idx = ring->slot[cur].buf_idx;
+ buf = NETMAP_BUF(ring, idx);
+ left = ring->slot[cur].len;
+
+ for (i = 0; i < iovcnt && left > 0; i++) {
+ if (iov[i].iov_len > left)
+ iov[i].iov_len = left;
+ memcpy(iov[i].iov_base, &buf[len], iov[i].iov_len);
+ len += iov[i].iov_len;
+ left -= iov[i].iov_len;
+ }
+ ring->head = ring->cur = nm_ring_next(ring, cur);
+ nmd->cur_rx_ring = r;
+ ioctl(nmd->fd, NIOCRXSYNC, NULL);
+ break;
+ }
+ for (; i < iovcnt; i++)
+ iov[i].iov_len = 0;
+
+ return (len);
+}
+
+/*
+ * Called to send a buffer chain out to the vale port
+ */
+static void
+pci_vtnet_netmap_tx(struct pci_vtnet_softc *sc, struct iovec *iov, int iovcnt,
+ int len)
+{
+ static char pad[60]; /* all zero bytes */
+
+ if (sc->vsc_nmd == NULL)
+ return;
+
+ /*
+ * If the length is < 60, pad out to that and add the
+ * extra zero'd segment to the iov. It is guaranteed that
+ * there is always an extra iov available by the caller.
+ */
+ if (len < 60) {
+ iov[iovcnt].iov_base = pad;
+ iov[iovcnt].iov_len = 60 - len;
+ iovcnt++;
+ }
+ (void) pci_vtnet_netmap_writev(sc->vsc_nmd, iov, iovcnt);
+}
+
+static void
+pci_vtnet_netmap_rx(struct pci_vtnet_softc *sc)
+{
+ struct iovec iov[VTNET_MAXSEGS], *riov;
+ struct vqueue_info *vq;
+ void *vrx;
+ int len, n;
+ uint16_t idx;
+
+ /*
+ * Should never be called without a valid netmap descriptor
+ */
+ assert(sc->vsc_nmd != NULL);
+
+ /*
+ * But, will be called when the rx ring hasn't yet
+ * been set up or the guest is resetting the device.
+ */
+ if (!sc->vsc_rx_ready || sc->resetting) {
+ /*
+ * Drop the packet and try later.
+ */
+ (void) nm_nextpkt(sc->vsc_nmd, (void *)dummybuf);
+ return;
+ }
+
+ /*
+ * Check for available rx buffers
+ */
+ vq = &sc->vsc_queues[VTNET_RXQ];
+ if (!vq_has_descs(vq)) {
+ /*
+ * Drop the packet and try later. Interrupt on
+ * empty, if that's negotiated.
+ */
+ (void) nm_nextpkt(sc->vsc_nmd, (void *)dummybuf);
+ vq_endchains(vq, 1);
+ return;
+ }
+
+ do {
+ /*
+ * Get descriptor chain.
+ */
+ n = vq_getchain(vq, &idx, iov, VTNET_MAXSEGS, NULL);
+ assert(n >= 1 && n <= VTNET_MAXSEGS);
+
+ /*
+ * Get a pointer to the rx header, and use the
+ * data immediately following it for the packet buffer.
+ */
+ vrx = iov[0].iov_base;
+ riov = rx_iov_trim(iov, &n, sc->rx_vhdrlen);
+
+ len = pci_vtnet_netmap_readv(sc->vsc_nmd, riov, n);
+
+ if (len == 0) {
+ /*
+ * No more packets, but still some avail ring
+ * entries. Interrupt if needed/appropriate.
+ */
+ vq_retchain(vq);
+ vq_endchains(vq, 0);
+ return;
}
/*
* The only valid field in the rx packet header is the
- * number of buffers, which is always 1 without TSO
- * support.
+ * number of buffers if merged rx bufs were negotiated.
*/
- memset(vrx, 0, sizeof(struct virtio_net_rxhdr));
- vrx->vrh_bufs = 1;
+ memset(vrx, 0, sc->rx_vhdrlen);
+
+ if (sc->rx_merge) {
+ struct virtio_net_rxhdr *vrxh;
+
+ vrxh = vrx;
+ vrxh->vrh_bufs = 1;
+ }
/*
* Release this chain and handle more chains.
*/
- vq_relchain(vq, total_len);
+ vq_relchain(vq, idx, len + sc->rx_vhdrlen);
} while (vq_has_descs(vq));
/* Interrupt if needed, including for NOTIFY_ON_EMPTY. */
vq_endchains(vq, 1);
}
+#endif /* __FreeBSD__ */
-#ifdef __FreeBSD__
+#ifdef __FreeBSD__
static void
-pci_vtnet_tap_callback(int fd, enum ev_type type, void *param)
+pci_vtnet_rx_callback(int fd, enum ev_type type, void *param)
{
struct pci_vtnet_softc *sc = param;
pthread_mutex_lock(&sc->rx_mtx);
sc->rx_in_progress = 1;
- pci_vtnet_tap_rx(sc);
+ sc->pci_vtnet_rx(sc);
sc->rx_in_progress = 0;
pthread_mutex_unlock(&sc->rx_mtx);
@@ -478,13 +680,15 @@ pci_vtnet_poll_thread(void *param)
continue;
}
pthread_mutex_lock(&sc->vsc_mtx);
+ sc->rx_in_progress = 1;
pci_vtnet_tap_rx(sc);
+ sc->rx_in_progress = 0;
pthread_mutex_unlock(&sc->vsc_mtx);
}
return (NULL);
}
-#endif
+#endif /* __FreeBSD__ */
static void
pci_vtnet_ping_rxq(void *vsc, struct vqueue_info *vq)
@@ -496,6 +700,7 @@ pci_vtnet_ping_rxq(void *vsc, struct vqueue_info *vq)
*/
if (sc->vsc_rx_ready == 0) {
sc->vsc_rx_ready = 1;
+ vq->vq_used->vu_flags |= VRING_USED_F_NO_NOTIFY;
}
}
@@ -505,13 +710,14 @@ pci_vtnet_proctx(struct pci_vtnet_softc *sc, struct vqueue_info *vq)
struct iovec iov[VTNET_MAXSEGS + 1];
int i, n;
int plen, tlen;
+ uint16_t idx;
/*
* Obtain chain of descriptors. The first one is
* really the header descriptor, so we need to sum
* up two lengths: packet length and transfer length.
*/
- n = vq_getchain(vq, iov, VTNET_MAXSEGS, NULL);
+ n = vq_getchain(vq, &idx, iov, VTNET_MAXSEGS, NULL);
assert(n >= 1 && n <= VTNET_MAXSEGS);
plen = 0;
tlen = iov[0].iov_len;
@@ -521,10 +727,10 @@ pci_vtnet_proctx(struct pci_vtnet_softc *sc, struct vqueue_info *vq)
}
DPRINTF(("virtio: packet send, %d bytes, %d segs\n\r", plen, n));
- pci_vtnet_tap_tx(sc, &iov[1], n - 1, plen);
+ sc->pci_vtnet_tx(sc, &iov[1], n - 1, plen);
/* chain is processed, release it and set tlen */
- vq_relchain(vq, tlen);
+ vq_relchain(vq, idx, tlen);
}
static void
@@ -540,6 +746,7 @@ pci_vtnet_ping_txq(void *vsc, struct vqueue_info *vq)
/* Signal the tx thread for processing */
pthread_mutex_lock(&sc->tx_mtx);
+ vq->vq_used->vu_flags |= VRING_USED_F_NO_NOTIFY;
if (sc->tx_in_progress == 0)
pthread_cond_signal(&sc->tx_cond);
pthread_mutex_unlock(&sc->tx_mtx);
@@ -553,7 +760,7 @@ pci_vtnet_tx_thread(void *param)
{
struct pci_vtnet_softc *sc = param;
struct vqueue_info *vq;
- int have_work, error;
+ int error;
vq = &sc->vsc_queues[VTNET_TXQ];
@@ -567,23 +774,20 @@ pci_vtnet_tx_thread(void *param)
for (;;) {
/* note - tx mutex is locked here */
- do {
- if (sc->resetting)
- have_work = 0;
- else
- have_work = vq_has_descs(vq);
-
- if (!have_work) {
- sc->tx_in_progress = 0;
- error = pthread_cond_wait(&sc->tx_cond,
- &sc->tx_mtx);
- assert(error == 0);
- }
- } while (!have_work);
+ while (sc->resetting || !vq_has_descs(vq)) {
+ vq->vq_used->vu_flags &= ~VRING_USED_F_NO_NOTIFY;
+ mb();
+ if (!sc->resetting && vq_has_descs(vq))
+ break;
+
+ sc->tx_in_progress = 0;
+ error = pthread_cond_wait(&sc->tx_cond, &sc->tx_mtx);
+ assert(error == 0);
+ }
+ vq->vq_used->vu_flags |= VRING_USED_F_NO_NOTIFY;
sc->tx_in_progress = 1;
pthread_mutex_unlock(&sc->tx_mtx);
- vq_startchains(vq);
do {
/*
* Run through entries, placing them into
@@ -603,16 +807,16 @@ pci_vtnet_tx_thread(void *param)
return (NULL);
}
-#ifdef notyet
+#ifdef __FreeBSD__
static void
pci_vtnet_ping_ctlq(void *vsc, struct vqueue_info *vq)
{
DPRINTF(("vtnet: control qnotify!\n\r"));
}
-#endif
+#endif /* __FreeBSD__ */
-#ifdef __FreeBSD__
+#ifdef __FreeBSD__
static int
pci_vtnet_parsemac(char *mac_str, uint8_t *mac_addr)
{
@@ -635,8 +839,126 @@ pci_vtnet_parsemac(char *mac_str, uint8_t *mac_addr)
return (0);
}
+#endif /* __FreeBSD__ */
+
+static void
+pci_vtnet_tap_setup(struct pci_vtnet_softc *sc, char *devname)
+{
+ char tbuf[80];
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_t rights;
+#endif
+#ifndef __FreeBSD__
+ uchar_t physaddr[DLPI_PHYSADDR_MAX];
+ size_t physaddrlen = DLPI_PHYSADDR_MAX;
+ int error;
+#endif
+
+ strcpy(tbuf, "/dev/");
+ strlcat(tbuf, devname, sizeof(tbuf));
+
+ sc->pci_vtnet_rx = pci_vtnet_tap_rx;
+ sc->pci_vtnet_tx = pci_vtnet_tap_tx;
+#ifdef __FreeBSD__
+ sc->vsc_tapfd = open(tbuf, O_RDWR);
+ if (sc->vsc_tapfd == -1) {
+ WPRINTF(("open of tap device %s failed\n", tbuf));
+ return;
+ }
+
+ /*
+ * Set non-blocking and register for read
+ * notifications with the event loop
+ */
+ int opt = 1;
+ if (ioctl(sc->vsc_tapfd, FIONBIO, &opt) < 0) {
+ WPRINTF(("tap device O_NONBLOCK failed\n"));
+ close(sc->vsc_tapfd);
+ sc->vsc_tapfd = -1;
+ }
+
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_init(&rights, CAP_EVENT, CAP_READ, CAP_WRITE);
+ if (cap_rights_limit(sc->vsc_tapfd, &rights) == -1 && errno != ENOSYS)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
#endif
+ sc->vsc_mevp = mevent_add(sc->vsc_tapfd,
+ EVF_READ,
+ pci_vtnet_rx_callback,
+ sc);
+ if (sc->vsc_mevp == NULL) {
+ WPRINTF(("Could not register event\n"));
+ close(sc->vsc_tapfd);
+ sc->vsc_tapfd = -1;
+ }
+#else
+ if (dlpi_open(devname, &sc->vsc_dhp, DLPI_RAW) != DLPI_SUCCESS) {
+ WPRINTF(("open of vnic device %s failed\n", devname));
+ }
+
+ if (dlpi_get_physaddr(sc->vsc_dhp, DL_CURR_PHYS_ADDR, physaddr,
+ &physaddrlen) != DLPI_SUCCESS) {
+ WPRINTF(("read MAC address of vnic device %s failed\n",
+ devname));
+ }
+ if (physaddrlen != ETHERADDRL) {
+ WPRINTF(("bad MAC address len %d on vnic device %s\n",
+ physaddrlen, devname));
+ }
+ memcpy(sc->vsc_config.mac, physaddr, ETHERADDRL);
+
+ if (dlpi_bind(sc->vsc_dhp, DLPI_ANY_SAP, NULL) != DLPI_SUCCESS) {
+ WPRINTF(("bind of vnic device %s failed\n", devname));
+ }
+
+ if (dlpi_promiscon(sc->vsc_dhp, DL_PROMISC_PHYS) != DLPI_SUCCESS) {
+ WPRINTF(("enable promiscous mode(physical) of vnic device %s "
+ "failed\n", devname));
+ }
+ if (dlpi_promiscon(sc->vsc_dhp, DL_PROMISC_SAP) != DLPI_SUCCESS) {
+ WPRINTF(("enable promiscous mode(SAP) of vnic device %s "
+ "failed\n", devname));
+ }
+
+ sc->vsc_dlpifd = dlpi_fd(sc->vsc_dhp);
+
+ if (fcntl(sc->vsc_dlpifd, F_SETFL, O_NONBLOCK) < 0) {
+ WPRINTF(("enable O_NONBLOCK of vnic device %s failed\n",
+ devname));
+ dlpi_close(sc->vsc_dhp);
+ sc->vsc_dlpifd = -1;
+ }
+
+ error = pthread_create(NULL, NULL, pci_vtnet_poll_thread, sc);
+ assert(error == 0);
+#endif
+}
+
+#ifdef __FreeBSD__
+static void
+pci_vtnet_netmap_setup(struct pci_vtnet_softc *sc, char *ifname)
+{
+ sc->pci_vtnet_rx = pci_vtnet_netmap_rx;
+ sc->pci_vtnet_tx = pci_vtnet_netmap_tx;
+
+ sc->vsc_nmd = nm_open(ifname, NULL, 0, 0);
+ if (sc->vsc_nmd == NULL) {
+ WPRINTF(("open of netmap device %s failed\n", ifname));
+ return;
+ }
+
+ sc->vsc_mevp = mevent_add(sc->vsc_nmd->fd,
+ EVF_READ,
+ pci_vtnet_rx_callback,
+ sc);
+ if (sc->vsc_mevp == NULL) {
+ WPRINTF(("Could not register event\n"));
+ nm_close(sc->vsc_nmd);
+ sc->vsc_nmd = NULL;
+ }
+}
+#endif /* __FreeBSD__ */
static int
pci_vtnet_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
@@ -645,10 +967,6 @@ pci_vtnet_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
MD5_CTX mdctx;
unsigned char digest[16];
char nstr[80];
-#else
- uchar_t physaddr[DLPI_PHYSADDR_MAX];
- size_t physaddrlen = DLPI_PHYSADDR_MAX;
- int error;
#endif
char tname[MAXCOMLEN + 1];
struct pci_vtnet_softc *sc;
@@ -658,17 +976,18 @@ pci_vtnet_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
int mac_provided;
int use_msix;
- sc = malloc(sizeof(struct pci_vtnet_softc));
- memset(sc, 0, sizeof(struct pci_vtnet_softc));
+ sc = calloc(1, sizeof(struct pci_vtnet_softc));
pthread_mutex_init(&sc->vsc_mtx, NULL);
vi_softc_linkup(&sc->vsc_vs, &vtnet_vi_consts, sc, pi, sc->vsc_queues);
+ sc->vsc_vs.vs_mtx = &sc->vsc_mtx;
+
sc->vsc_queues[VTNET_RXQ].vq_qsize = VTNET_RINGSZ;
sc->vsc_queues[VTNET_RXQ].vq_notify = pci_vtnet_ping_rxq;
sc->vsc_queues[VTNET_TXQ].vq_qsize = VTNET_RINGSZ;
sc->vsc_queues[VTNET_TXQ].vq_notify = pci_vtnet_ping_txq;
-#ifdef notyet
+#ifdef __FreeBSD__
sc->vsc_queues[VTNET_CTLQ].vq_qsize = VTNET_RINGSZ;
sc->vsc_queues[VTNET_CTLQ].vq_notify = pci_vtnet_ping_ctlq;
#endif
@@ -690,8 +1009,8 @@ pci_vtnet_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
#ifdef __FreeBSD__
sc->vsc_tapfd = -1;
#endif
+ sc->vsc_nmd = NULL;
if (opts != NULL) {
- char tbuf[80];
#ifdef __FreeBSD__
int err;
#endif
@@ -710,72 +1029,15 @@ pci_vtnet_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
}
#endif
- strcpy(tbuf, "/dev/");
- strlcat(tbuf, devname, sizeof(tbuf));
+#ifdef __FreeBSD__
+ if (strncmp(devname, "vale", 4) == 0)
+ pci_vtnet_netmap_setup(sc, devname);
+#endif
+ if (strncmp(devname, "tap", 3) == 0 ||
+ strncmp(devname, "vmnet", 5) == 0)
+ pci_vtnet_tap_setup(sc, devname);
free(devname);
-
-#ifdef __FreeBSD__
- sc->vsc_tapfd = open(tbuf, O_RDWR);
- if (sc->vsc_tapfd == -1) {
- WPRINTF(("open of tap device %s failed\n", tbuf));
- } else {
- /*
- * Set non-blocking and register for read
- * notifications with the event loop
- */
- int opt = 1;
- if (ioctl(sc->vsc_tapfd, FIONBIO, &opt) < 0) {
- WPRINTF(("tap device O_NONBLOCK failed\n"));
- close(sc->vsc_tapfd);
- sc->vsc_tapfd = -1;
- }
-
- sc->vsc_mevp = mevent_add(sc->vsc_tapfd,
- EVF_READ,
- pci_vtnet_tap_callback,
- sc);
- if (sc->vsc_mevp == NULL) {
- WPRINTF(("Could not register event\n"));
- close(sc->vsc_tapfd);
- sc->vsc_tapfd = -1;
- }
- }
-#else
- if (dlpi_open(opts, &sc->vsc_dhp, DLPI_RAW) != DLPI_SUCCESS) {
- WPRINTF(("open of vnic device %s failed\n", opts));
- }
-
- if (dlpi_get_physaddr(sc->vsc_dhp, DL_CURR_PHYS_ADDR, physaddr, &physaddrlen) != DLPI_SUCCESS) {
- WPRINTF(("read MAC address of vnic device %s failed\n", opts));
- }
- if (physaddrlen != ETHERADDRL) {
- WPRINTF(("bad MAC address len %d on vnic device %s\n", physaddrlen, opts));
- }
- memcpy(sc->vsc_config.mac, physaddr, ETHERADDRL);
-
- if (dlpi_bind(sc->vsc_dhp, DLPI_ANY_SAP, NULL) != DLPI_SUCCESS) {
- WPRINTF(("bind of vnic device %s failed\n", opts));
- }
-
- if (dlpi_promiscon(sc->vsc_dhp, DL_PROMISC_PHYS) != DLPI_SUCCESS) {
- WPRINTF(("enable promiscous mode(physical) of vnic device %s failed\n", opts));
- }
- if (dlpi_promiscon(sc->vsc_dhp, DL_PROMISC_SAP) != DLPI_SUCCESS) {
- WPRINTF(("enable promiscous mode(SAP) of vnic device %s failed\n", opts));
- }
-
- sc->vsc_dlpifd = dlpi_fd(sc->vsc_dhp);
-
- if (fcntl(sc->vsc_dlpifd, F_SETFL, O_NONBLOCK) < 0) {
- WPRINTF(("enable O_NONBLOCK of vnic device %s failed\n", opts));
- dlpi_close(sc->vsc_dhp);
- sc->vsc_dlpifd = -1;
- }
-
- error = pthread_create(NULL, NULL, pci_vtnet_poll_thread, sc);
- assert(error == 0);
-#endif
}
#ifdef __FreeBSD__
@@ -805,9 +1067,15 @@ pci_vtnet_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR);
pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_NETWORK);
pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_TYPE_NET);
+ pci_set_cfgdata16(pi, PCIR_SUBVEND_0, VIRTIO_VENDOR);
- /* link always up */
- sc->vsc_config.status = 1;
+ /* Link is up if we managed to open tap device or vale port. */
+#ifdef __FreeBSD__
+ sc->vsc_config.status = (opts == NULL || sc->vsc_tapfd >= 0 ||
+#else
+ sc->vsc_config.status = (opts == NULL || sc->vsc_dlpifd >= 0 ||
+#endif
+ sc->vsc_nmd != NULL);
/* use BAR 1 to map MSI-X table and PBA, if we're using MSI-X */
if (vi_intr_init(&sc->vsc_vs, 1, use_msix))
@@ -818,6 +1086,8 @@ pci_vtnet_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
sc->resetting = 0;
+ sc->rx_merge = 1;
+ sc->rx_vhdrlen = sizeof(struct virtio_net_rxhdr);
sc->rx_in_progress = 0;
pthread_mutex_init(&sc->rx_mtx, NULL);
@@ -850,9 +1120,10 @@ pci_vtnet_cfgwrite(void *vsc, int offset, int size, uint32_t value)
ptr = &sc->vsc_config.mac[offset];
memcpy(ptr, &value, size);
} else {
+ /* silently ignore other writes */
DPRINTF(("vtnet: write to readonly reg %d\n\r", offset));
- return (1);
}
+
return (0);
}
@@ -867,6 +1138,20 @@ pci_vtnet_cfgread(void *vsc, int offset, int size, uint32_t *retval)
return (0);
}
+static void
+pci_vtnet_neg_features(void *vsc, uint64_t negotiated_features)
+{
+ struct pci_vtnet_softc *sc = vsc;
+
+ sc->vsc_features = negotiated_features;
+
+ if (!(sc->vsc_features & VIRTIO_NET_F_MRG_RXBUF)) {
+ sc->rx_merge = 0;
+ /* non-merge rx header is 2 bytes shorter */
+ sc->rx_vhdrlen -= 2;
+ }
+}
+
struct pci_devemu pci_de_vnet = {
.pe_emu = "virtio-net",
.pe_init = pci_vtnet_init,
diff --git a/usr/src/cmd/bhyve/pci_virtio_rnd.c b/usr/src/cmd/bhyve/pci_virtio_rnd.c
new file mode 100644
index 0000000000..4ce749053c
--- /dev/null
+++ b/usr/src/cmd/bhyve/pci_virtio_rnd.c
@@ -0,0 +1,203 @@
+/*-
+ * Copyright (c) 2014 Nahanni Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * virtio entropy device emulation.
+ * Randomness is sourced from /dev/random which does not block
+ * once it has been seeded at bootup.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#ifndef WITHOUT_CAPSICUM
+#include <sys/capsicum.h>
+#endif
+#include <sys/linker_set.h>
+#include <sys/uio.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <pthread.h>
+#include <sysexits.h>
+
+#include "bhyverun.h"
+#include "pci_emul.h"
+#include "virtio.h"
+
+#define VTRND_RINGSZ 64
+
+
+static int pci_vtrnd_debug;
+#define DPRINTF(params) if (pci_vtrnd_debug) printf params
+#define WPRINTF(params) printf params
+
+/*
+ * Per-device softc
+ */
+struct pci_vtrnd_softc {
+ struct virtio_softc vrsc_vs;
+ struct vqueue_info vrsc_vq;
+ pthread_mutex_t vrsc_mtx;
+ uint64_t vrsc_cfg;
+ int vrsc_fd;
+};
+
+static void pci_vtrnd_reset(void *);
+static void pci_vtrnd_notify(void *, struct vqueue_info *);
+
+static struct virtio_consts vtrnd_vi_consts = {
+ "vtrnd", /* our name */
+ 1, /* we support 1 virtqueue */
+ 0, /* config reg size */
+ pci_vtrnd_reset, /* reset */
+ pci_vtrnd_notify, /* device-wide qnotify */
+ NULL, /* read virtio config */
+ NULL, /* write virtio config */
+ NULL, /* apply negotiated features */
+ 0, /* our capabilities */
+};
+
+
+static void
+pci_vtrnd_reset(void *vsc)
+{
+ struct pci_vtrnd_softc *sc;
+
+ sc = vsc;
+
+ DPRINTF(("vtrnd: device reset requested !\n"));
+ vi_reset_dev(&sc->vrsc_vs);
+}
+
+
+static void
+pci_vtrnd_notify(void *vsc, struct vqueue_info *vq)
+{
+ struct iovec iov;
+ struct pci_vtrnd_softc *sc;
+ int len;
+ uint16_t idx;
+
+ sc = vsc;
+
+ if (sc->vrsc_fd < 0) {
+ vq_endchains(vq, 0);
+ return;
+ }
+
+ while (vq_has_descs(vq)) {
+ vq_getchain(vq, &idx, &iov, 1, NULL);
+
+ len = read(sc->vrsc_fd, iov.iov_base, iov.iov_len);
+
+ DPRINTF(("vtrnd: vtrnd_notify(): %d\r\n", len));
+
+ /* Catastrophe if unable to read from /dev/random */
+ assert(len > 0);
+
+ /*
+ * Release this chain and handle more
+ */
+ vq_relchain(vq, idx, len);
+ }
+ vq_endchains(vq, 1); /* Generate interrupt if appropriate. */
+}
+
+
+static int
+pci_vtrnd_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
+{
+ struct pci_vtrnd_softc *sc;
+ int fd;
+ int len;
+ uint8_t v;
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_t rights;
+#endif
+
+ /*
+ * Should always be able to open /dev/random.
+ */
+ fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
+
+ assert(fd >= 0);
+
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_init(&rights, CAP_READ);
+ if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+#endif
+
+ /*
+ * Check that device is seeded and non-blocking.
+ */
+ len = read(fd, &v, sizeof(v));
+ if (len <= 0) {
+ WPRINTF(("vtrnd: /dev/random not ready, read(): %d", len));
+ return (1);
+ }
+
+ sc = calloc(1, sizeof(struct pci_vtrnd_softc));
+
+ vi_softc_linkup(&sc->vrsc_vs, &vtrnd_vi_consts, sc, pi, &sc->vrsc_vq);
+ sc->vrsc_vs.vs_mtx = &sc->vrsc_mtx;
+
+ sc->vrsc_vq.vq_qsize = VTRND_RINGSZ;
+
+ /* keep /dev/random opened while emulating */
+ sc->vrsc_fd = fd;
+
+ /* initialize config space */
+ pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_RANDOM);
+ pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR);
+ pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_CRYPTO);
+ pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_TYPE_ENTROPY);
+ pci_set_cfgdata16(pi, PCIR_SUBVEND_0, VIRTIO_VENDOR);
+
+ if (vi_intr_init(&sc->vrsc_vs, 1, fbsdrun_virtio_msix()))
+ return (1);
+ vi_set_io_bar(&sc->vrsc_vs, 0);
+
+ return (0);
+}
+
+
+struct pci_devemu pci_de_vrnd = {
+ .pe_emu = "virtio-rnd",
+ .pe_init = pci_vtrnd_init,
+ .pe_barwrite = vi_pci_write,
+ .pe_barread = vi_pci_read
+};
+PCI_EMUL_SET(pci_de_vrnd);
diff --git a/usr/src/cmd/bhyve/pci_virtio_viona.c b/usr/src/cmd/bhyve/pci_virtio_viona.c
index ddc9f0546a..e5a5cb584f 100644
--- a/usr/src/cmd/bhyve/pci_virtio_viona.c
+++ b/usr/src/cmd/bhyve/pci_virtio_viona.c
@@ -365,10 +365,12 @@ pci_viona_viona_init(struct vmctx *ctx, struct pci_viona_softc *sc)
vna_create.c_linkid = sc->vsc_linkid;
strlcpy(vna_create.c_vmname, vmname,
sizeof (vna_create.c_vmname));
+#if notyet
vm_get_memory_seg(ctx, 1 * (1024 * 1024UL), &vna_create.c_lomem_size,
NULL);
vm_get_memory_seg(ctx, 4 * (1024 * 1024 * 1024UL),
&vna_create.c_himem_size, NULL);
+#endif
error = ioctl(sc->vsc_vnafd, VNA_IOC_CREATE, &vna_create);
if (error != 0) {
WPRINTF(("ioctl viona create failed %d\n", error));
diff --git a/usr/src/cmd/bhyve/pci_xhci.c b/usr/src/cmd/bhyve/pci_xhci.c
new file mode 100644
index 0000000000..f178468108
--- /dev/null
+++ b/usr/src/cmd/bhyve/pci_xhci.c
@@ -0,0 +1,2834 @@
+/*-
+ * Copyright (c) 2014 Leon Dang <ldang@nahannisys.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ XHCI options:
+ -s <n>,xhci,{devices}
+
+ devices:
+ tablet USB tablet mouse
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/uio.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_freebsd.h>
+#include <xhcireg.h>
+
+#include "bhyverun.h"
+#include "pci_emul.h"
+#include "pci_xhci.h"
+#include "usb_emul.h"
+
+
+static int xhci_debug = 0;
+#define DPRINTF(params) if (xhci_debug) printf params
+#define WPRINTF(params) printf params
+
+
+#define XHCI_NAME "xhci"
+#define XHCI_MAX_DEVS 8 /* 4 USB3 + 4 USB2 devs */
+
+#define XHCI_MAX_SLOTS 64 /* min allowed by Windows drivers */
+
+/*
+ * XHCI data structures can be up to 64k, but limit paddr_guest2host mapping
+ * to 4k to avoid going over the guest physical memory barrier.
+ */
+#define XHCI_PADDR_SZ 4096 /* paddr_guest2host max size */
+
+#define XHCI_ERST_MAX 0 /* max 2^entries event ring seg tbl */
+
+#define XHCI_CAPLEN (4*8) /* offset of op register space */
+#define XHCI_HCCPRAMS2 0x1C /* offset of HCCPARAMS2 register */
+#define XHCI_PORTREGS_START 0x400
+#define XHCI_DOORBELL_MAX 256
+
+#define XHCI_STREAMS_MAX 1 /* 4-15 in XHCI spec */
+
+/* caplength and hci-version registers */
+#define XHCI_SET_CAPLEN(x) ((x) & 0xFF)
+#define XHCI_SET_HCIVERSION(x) (((x) & 0xFFFF) << 16)
+#define XHCI_GET_HCIVERSION(x) (((x) >> 16) & 0xFFFF)
+
+/* hcsparams1 register */
+#define XHCI_SET_HCSP1_MAXSLOTS(x) ((x) & 0xFF)
+#define XHCI_SET_HCSP1_MAXINTR(x) (((x) & 0x7FF) << 8)
+#define XHCI_SET_HCSP1_MAXPORTS(x) (((x) & 0xFF) << 24)
+
+/* hcsparams2 register */
+#define XHCI_SET_HCSP2_IST(x) ((x) & 0x0F)
+#define XHCI_SET_HCSP2_ERSTMAX(x) (((x) & 0x0F) << 4)
+#define XHCI_SET_HCSP2_MAXSCRATCH_HI(x) (((x) & 0x1F) << 21)
+#define XHCI_SET_HCSP2_MAXSCRATCH_LO(x) (((x) & 0x1F) << 27)
+
+/* hcsparams3 register */
+#define XHCI_SET_HCSP3_U1EXITLATENCY(x) ((x) & 0xFF)
+#define XHCI_SET_HCSP3_U2EXITLATENCY(x) (((x) & 0xFFFF) << 16)
+
+/* hccparams1 register */
+#define XHCI_SET_HCCP1_AC64(x) ((x) & 0x01)
+#define XHCI_SET_HCCP1_BNC(x) (((x) & 0x01) << 1)
+#define XHCI_SET_HCCP1_CSZ(x) (((x) & 0x01) << 2)
+#define XHCI_SET_HCCP1_PPC(x) (((x) & 0x01) << 3)
+#define XHCI_SET_HCCP1_PIND(x) (((x) & 0x01) << 4)
+#define XHCI_SET_HCCP1_LHRC(x) (((x) & 0x01) << 5)
+#define XHCI_SET_HCCP1_LTC(x) (((x) & 0x01) << 6)
+#define XHCI_SET_HCCP1_NSS(x) (((x) & 0x01) << 7)
+#define XHCI_SET_HCCP1_PAE(x) (((x) & 0x01) << 8)
+#define XHCI_SET_HCCP1_SPC(x) (((x) & 0x01) << 9)
+#define XHCI_SET_HCCP1_SEC(x) (((x) & 0x01) << 10)
+#define XHCI_SET_HCCP1_CFC(x) (((x) & 0x01) << 11)
+#define XHCI_SET_HCCP1_MAXPSA(x) (((x) & 0x0F) << 12)
+#define XHCI_SET_HCCP1_XECP(x) (((x) & 0xFFFF) << 16)
+
+/* hccparams2 register */
+#define XHCI_SET_HCCP2_U3C(x) ((x) & 0x01)
+#define XHCI_SET_HCCP2_CMC(x) (((x) & 0x01) << 1)
+#define XHCI_SET_HCCP2_FSC(x) (((x) & 0x01) << 2)
+#define XHCI_SET_HCCP2_CTC(x) (((x) & 0x01) << 3)
+#define XHCI_SET_HCCP2_LEC(x) (((x) & 0x01) << 4)
+#define XHCI_SET_HCCP2_CIC(x) (((x) & 0x01) << 5)
+
+/* other registers */
+#define XHCI_SET_DOORBELL(x) ((x) & ~0x03)
+#define XHCI_SET_RTSOFFSET(x) ((x) & ~0x0F)
+
+/* register masks */
+#define XHCI_PS_PLS_MASK (0xF << 5) /* port link state */
+#define XHCI_PS_SPEED_MASK (0xF << 10) /* port speed */
+#define XHCI_PS_PIC_MASK (0x3 << 14) /* port indicator */
+
+/* port register set */
+#define XHCI_PORTREGS_BASE 0x400 /* base offset */
+#define XHCI_PORTREGS_PORT0 0x3F0
+#define XHCI_PORTREGS_SETSZ 0x10 /* size of a set */
+
+#define MASK_64_HI(x) ((x) & ~0xFFFFFFFFULL)
+#define MASK_64_LO(x) ((x) & 0xFFFFFFFFULL)
+
+#define FIELD_REPLACE(a,b,m,s) (((a) & ~((m) << (s))) | \
+ (((b) & (m)) << (s)))
+#define FIELD_COPY(a,b,m,s) (((a) & ~((m) << (s))) | \
+ (((b) & ((m) << (s)))))
+
+struct pci_xhci_trb_ring {
+ uint64_t ringaddr; /* current dequeue guest address */
+ uint32_t ccs; /* consumer cycle state */
+};
+
+/* device endpoint transfer/stream rings */
+struct pci_xhci_dev_ep {
+ union {
+ struct xhci_trb *_epu_tr;
+ struct xhci_stream_ctx *_epu_sctx;
+ } _ep_trbsctx;
+#define ep_tr _ep_trbsctx._epu_tr
+#define ep_sctx _ep_trbsctx._epu_sctx
+
+ union {
+ struct pci_xhci_trb_ring _epu_trb;
+ struct pci_xhci_trb_ring *_epu_sctx_trbs;
+ } _ep_trb_rings;
+#define ep_ringaddr _ep_trb_rings._epu_trb.ringaddr
+#define ep_ccs _ep_trb_rings._epu_trb.ccs
+#define ep_sctx_trbs _ep_trb_rings._epu_sctx_trbs
+
+ struct usb_data_xfer *ep_xfer; /* transfer chain */
+};
+
+/* device context base address array: maps slot->device context */
+struct xhci_dcbaa {
+ uint64_t dcba[USB_MAX_DEVICES+1]; /* xhci_dev_ctx ptrs */
+};
+
+/* port status registers */
+struct pci_xhci_portregs {
+ uint32_t portsc; /* port status and control */
+ uint32_t portpmsc; /* port pwr mgmt status & control */
+ uint32_t portli; /* port link info */
+ uint32_t porthlpmc; /* port hardware LPM control */
+} __packed;
+#define XHCI_PS_SPEED_SET(x) (((x) & 0xF) << 10)
+
+/* xHC operational registers */
+struct pci_xhci_opregs {
+ uint32_t usbcmd; /* usb command */
+ uint32_t usbsts; /* usb status */
+ uint32_t pgsz; /* page size */
+ uint32_t dnctrl; /* device notification control */
+ uint64_t crcr; /* command ring control */
+ uint64_t dcbaap; /* device ctx base addr array ptr */
+ uint32_t config; /* configure */
+
+ /* guest mapped addresses: */
+ struct xhci_trb *cr_p; /* crcr dequeue */
+ struct xhci_dcbaa *dcbaa_p; /* dev ctx array ptr */
+};
+
+/* xHC runtime registers */
+struct pci_xhci_rtsregs {
+ uint32_t mfindex; /* microframe index */
+ struct { /* interrupter register set */
+ uint32_t iman; /* interrupter management */
+ uint32_t imod; /* interrupter moderation */
+ uint32_t erstsz; /* event ring segment table size */
+ uint32_t rsvd;
+ uint64_t erstba; /* event ring seg-tbl base addr */
+ uint64_t erdp; /* event ring dequeue ptr */
+ } intrreg __packed;
+
+ /* guest mapped addresses */
+ struct xhci_event_ring_seg *erstba_p;
+ struct xhci_trb *erst_p; /* event ring segment tbl */
+ int er_deq_seg; /* event ring dequeue segment */
+ int er_enq_idx; /* event ring enqueue index - xHCI */
+ int er_enq_seg; /* event ring enqueue segment */
+ uint32_t er_events_cnt; /* number of events in ER */
+ uint32_t event_pcs; /* producer cycle state flag */
+};
+
+
+struct pci_xhci_softc;
+
+
+/*
+ * USB device emulation container.
+ * This is referenced from usb_hci->hci_sc; 1 pci_xhci_dev_emu for each
+ * emulated device instance.
+ */
+struct pci_xhci_dev_emu {
+ struct pci_xhci_softc *xsc;
+
+ /* XHCI contexts */
+ struct xhci_dev_ctx *dev_ctx;
+ struct pci_xhci_dev_ep eps[XHCI_MAX_ENDPOINTS];
+ int dev_slotstate;
+
+ struct usb_devemu *dev_ue; /* USB emulated dev */
+ void *dev_sc; /* device's softc */
+
+ struct usb_hci hci;
+};
+
+struct pci_xhci_softc {
+ struct pci_devinst *xsc_pi;
+
+ pthread_mutex_t mtx;
+
+ uint32_t caplength; /* caplen & hciversion */
+ uint32_t hcsparams1; /* structural parameters 1 */
+ uint32_t hcsparams2; /* structural parameters 2 */
+ uint32_t hcsparams3; /* structural parameters 3 */
+ uint32_t hccparams1; /* capability parameters 1 */
+ uint32_t dboff; /* doorbell offset */
+ uint32_t rtsoff; /* runtime register space offset */
+ uint32_t hccparams2; /* capability parameters 2 */
+
+ uint32_t regsend; /* end of configuration registers */
+
+ struct pci_xhci_opregs opregs;
+ struct pci_xhci_rtsregs rtsregs;
+
+ struct pci_xhci_portregs *portregs;
+ struct pci_xhci_dev_emu **devices; /* XHCI[port] = device */
+ struct pci_xhci_dev_emu **slots; /* slots assigned from 1 */
+ int ndevices;
+
+ int usb2_port_start;
+ int usb3_port_start;
+};
+
+
+/* portregs and devices arrays are set up to start from idx=1 */
+#define XHCI_PORTREG_PTR(x,n) &(x)->portregs[(n)]
+#define XHCI_DEVINST_PTR(x,n) (x)->devices[(n)]
+#define XHCI_SLOTDEV_PTR(x,n) (x)->slots[(n)]
+
+#define XHCI_HALTED(sc) ((sc)->opregs.usbsts & XHCI_STS_HCH)
+
+#define XHCI_GADDR(sc,a) paddr_guest2host((sc)->xsc_pi->pi_vmctx, \
+ (a), \
+ XHCI_PADDR_SZ - ((a) & (XHCI_PADDR_SZ-1)))
+
+static int xhci_in_use;
+
+/* map USB errors to XHCI */
+static const int xhci_usb_errors[USB_ERR_MAX] = {
+ [USB_ERR_NORMAL_COMPLETION] = XHCI_TRB_ERROR_SUCCESS,
+ [USB_ERR_PENDING_REQUESTS] = XHCI_TRB_ERROR_RESOURCE,
+ [USB_ERR_NOT_STARTED] = XHCI_TRB_ERROR_ENDP_NOT_ON,
+ [USB_ERR_INVAL] = XHCI_TRB_ERROR_INVALID,
+ [USB_ERR_NOMEM] = XHCI_TRB_ERROR_RESOURCE,
+ [USB_ERR_CANCELLED] = XHCI_TRB_ERROR_STOPPED,
+ [USB_ERR_BAD_ADDRESS] = XHCI_TRB_ERROR_PARAMETER,
+ [USB_ERR_BAD_BUFSIZE] = XHCI_TRB_ERROR_PARAMETER,
+ [USB_ERR_BAD_FLAG] = XHCI_TRB_ERROR_PARAMETER,
+ [USB_ERR_NO_CALLBACK] = XHCI_TRB_ERROR_STALL,
+ [USB_ERR_IN_USE] = XHCI_TRB_ERROR_RESOURCE,
+ [USB_ERR_NO_ADDR] = XHCI_TRB_ERROR_RESOURCE,
+ [USB_ERR_NO_PIPE] = XHCI_TRB_ERROR_RESOURCE,
+ [USB_ERR_ZERO_NFRAMES] = XHCI_TRB_ERROR_UNDEFINED,
+ [USB_ERR_ZERO_MAXP] = XHCI_TRB_ERROR_UNDEFINED,
+ [USB_ERR_SET_ADDR_FAILED] = XHCI_TRB_ERROR_RESOURCE,
+ [USB_ERR_NO_POWER] = XHCI_TRB_ERROR_ENDP_NOT_ON,
+ [USB_ERR_TOO_DEEP] = XHCI_TRB_ERROR_RESOURCE,
+ [USB_ERR_IOERROR] = XHCI_TRB_ERROR_TRB,
+ [USB_ERR_NOT_CONFIGURED] = XHCI_TRB_ERROR_ENDP_NOT_ON,
+ [USB_ERR_TIMEOUT] = XHCI_TRB_ERROR_CMD_ABORTED,
+ [USB_ERR_SHORT_XFER] = XHCI_TRB_ERROR_SHORT_PKT,
+ [USB_ERR_STALLED] = XHCI_TRB_ERROR_STALL,
+ [USB_ERR_INTERRUPTED] = XHCI_TRB_ERROR_CMD_ABORTED,
+ [USB_ERR_DMA_LOAD_FAILED] = XHCI_TRB_ERROR_DATA_BUF,
+ [USB_ERR_BAD_CONTEXT] = XHCI_TRB_ERROR_TRB,
+ [USB_ERR_NO_ROOT_HUB] = XHCI_TRB_ERROR_UNDEFINED,
+ [USB_ERR_NO_INTR_THREAD] = XHCI_TRB_ERROR_UNDEFINED,
+ [USB_ERR_NOT_LOCKED] = XHCI_TRB_ERROR_UNDEFINED,
+};
+#define USB_TO_XHCI_ERR(e) ((e) < USB_ERR_MAX ? xhci_usb_errors[(e)] : \
+ XHCI_TRB_ERROR_INVALID)
+
+static int pci_xhci_insert_event(struct pci_xhci_softc *sc,
+ struct xhci_trb *evtrb, int do_intr);
+static void pci_xhci_dump_trb(struct xhci_trb *trb);
+static void pci_xhci_assert_interrupt(struct pci_xhci_softc *sc);
+static void pci_xhci_reset_slot(struct pci_xhci_softc *sc, int slot);
+static void pci_xhci_reset_port(struct pci_xhci_softc *sc, int portn, int warm);
+static void pci_xhci_update_ep_ring(struct pci_xhci_softc *sc,
+ struct pci_xhci_dev_emu *dev, struct pci_xhci_dev_ep *devep,
+ struct xhci_endp_ctx *ep_ctx, uint32_t streamid,
+ uint64_t ringaddr, int ccs);
+
+static void
+pci_xhci_set_evtrb(struct xhci_trb *evtrb, uint64_t port, uint32_t errcode,
+ uint32_t evtype)
+{
+ evtrb->qwTrb0 = port << 24;
+ evtrb->dwTrb2 = XHCI_TRB_2_ERROR_SET(errcode);
+ evtrb->dwTrb3 = XHCI_TRB_3_TYPE_SET(evtype);
+}
+
+
+/* controller reset */
+static void
+pci_xhci_reset(struct pci_xhci_softc *sc)
+{
+ int i;
+
+ sc->rtsregs.er_enq_idx = 0;
+ sc->rtsregs.er_events_cnt = 0;
+ sc->rtsregs.event_pcs = 1;
+
+ for (i = 1; i <= XHCI_MAX_SLOTS; i++) {
+ pci_xhci_reset_slot(sc, i);
+ }
+}
+
+static uint32_t
+pci_xhci_usbcmd_write(struct pci_xhci_softc *sc, uint32_t cmd)
+{
+ int do_intr = 0;
+ int i;
+
+ if (cmd & XHCI_CMD_RS) {
+ do_intr = (sc->opregs.usbcmd & XHCI_CMD_RS) == 0;
+
+ sc->opregs.usbcmd |= XHCI_CMD_RS;
+ sc->opregs.usbsts &= ~XHCI_STS_HCH;
+ sc->opregs.usbsts |= XHCI_STS_PCD;
+
+ /* Queue port change event on controller run from stop */
+ if (do_intr)
+ for (i = 1; i <= XHCI_MAX_DEVS; i++) {
+ struct pci_xhci_dev_emu *dev;
+ struct pci_xhci_portregs *port;
+ struct xhci_trb evtrb;
+
+ if ((dev = XHCI_DEVINST_PTR(sc, i)) == NULL)
+ continue;
+
+ port = XHCI_PORTREG_PTR(sc, i);
+ port->portsc |= XHCI_PS_CSC | XHCI_PS_CCS;
+ port->portsc &= ~XHCI_PS_PLS_MASK;
+
+ /*
+ * XHCI 4.19.3 USB2 RxDetect->Polling,
+ * USB3 Polling->U0
+ */
+ if (dev->dev_ue->ue_usbver == 2)
+ port->portsc |=
+ XHCI_PS_PLS_SET(UPS_PORT_LS_POLL);
+ else
+ port->portsc |=
+ XHCI_PS_PLS_SET(UPS_PORT_LS_U0);
+
+ pci_xhci_set_evtrb(&evtrb, i,
+ XHCI_TRB_ERROR_SUCCESS,
+ XHCI_TRB_EVENT_PORT_STS_CHANGE);
+
+ if (pci_xhci_insert_event(sc, &evtrb, 0) !=
+ XHCI_TRB_ERROR_SUCCESS)
+ break;
+ }
+ } else {
+ sc->opregs.usbcmd &= ~XHCI_CMD_RS;
+ sc->opregs.usbsts |= XHCI_STS_HCH;
+ sc->opregs.usbsts &= ~XHCI_STS_PCD;
+ }
+
+ /* start execution of schedule; stop when set to 0 */
+ cmd |= sc->opregs.usbcmd & XHCI_CMD_RS;
+
+ if (cmd & XHCI_CMD_HCRST) {
+ /* reset controller */
+ pci_xhci_reset(sc);
+ cmd &= ~XHCI_CMD_HCRST;
+ }
+
+ cmd &= ~(XHCI_CMD_CSS | XHCI_CMD_CRS);
+
+ if (do_intr)
+ pci_xhci_assert_interrupt(sc);
+
+ return (cmd);
+}
+
+static void
+pci_xhci_portregs_write(struct pci_xhci_softc *sc, uint64_t offset,
+ uint64_t value)
+{
+ struct xhci_trb evtrb;
+ struct pci_xhci_portregs *p;
+ int port;
+ uint32_t oldpls, newpls;
+
+ if (sc->portregs == NULL)
+ return;
+
+ port = (offset - XHCI_PORTREGS_PORT0) / XHCI_PORTREGS_SETSZ;
+ offset = (offset - XHCI_PORTREGS_PORT0) % XHCI_PORTREGS_SETSZ;
+
+ DPRINTF(("pci_xhci: portregs wr offset 0x%lx, port %u: 0x%lx\r\n",
+ offset, port, value));
+
+ assert(port >= 0);
+
+ if (port > XHCI_MAX_DEVS) {
+ DPRINTF(("pci_xhci: portregs_write port %d > ndevices\r\n",
+ port));
+ return;
+ }
+
+ if (XHCI_DEVINST_PTR(sc, port) == NULL) {
+ DPRINTF(("pci_xhci: portregs_write to unattached port %d\r\n",
+ port));
+ }
+
+ p = XHCI_PORTREG_PTR(sc, port);
+ switch (offset) {
+ case 0:
+ /* port reset or warm reset */
+ if (value & (XHCI_PS_PR | XHCI_PS_WPR)) {
+ pci_xhci_reset_port(sc, port, value & XHCI_PS_WPR);
+ break;
+ }
+
+ if ((p->portsc & XHCI_PS_PP) == 0) {
+ WPRINTF(("pci_xhci: portregs_write to unpowered "
+ "port %d\r\n", port));
+ break;
+ }
+
+ /* Port status and control register */
+ oldpls = XHCI_PS_PLS_GET(p->portsc);
+ newpls = XHCI_PS_PLS_GET(value);
+
+ p->portsc &= XHCI_PS_PED | XHCI_PS_PLS_MASK |
+ XHCI_PS_SPEED_MASK | XHCI_PS_PIC_MASK;
+
+ if (XHCI_DEVINST_PTR(sc, port))
+ p->portsc |= XHCI_PS_CCS;
+
+ p->portsc |= (value &
+ ~(XHCI_PS_OCA |
+ XHCI_PS_PR |
+ XHCI_PS_PED |
+ XHCI_PS_PLS_MASK | /* link state */
+ XHCI_PS_SPEED_MASK |
+ XHCI_PS_PIC_MASK | /* port indicator */
+ XHCI_PS_LWS | XHCI_PS_DR | XHCI_PS_WPR));
+
+ /* clear control bits */
+ p->portsc &= ~(value &
+ (XHCI_PS_CSC |
+ XHCI_PS_PEC |
+ XHCI_PS_WRC |
+ XHCI_PS_OCC |
+ XHCI_PS_PRC |
+ XHCI_PS_PLC |
+ XHCI_PS_CEC |
+ XHCI_PS_CAS));
+
+ /* port disable request; for USB3, don't care */
+ if (value & XHCI_PS_PED)
+ DPRINTF(("Disable port %d request\r\n", port));
+
+ if (!(value & XHCI_PS_LWS))
+ break;
+
+ DPRINTF(("Port new PLS: %d\r\n", newpls));
+ switch (newpls) {
+ case 0: /* U0 */
+ case 3: /* U3 */
+ if (oldpls != newpls) {
+ p->portsc &= ~XHCI_PS_PLS_MASK;
+ p->portsc |= XHCI_PS_PLS_SET(newpls) |
+ XHCI_PS_PLC;
+
+ if (oldpls != 0 && newpls == 0) {
+ pci_xhci_set_evtrb(&evtrb, port,
+ XHCI_TRB_ERROR_SUCCESS,
+ XHCI_TRB_EVENT_PORT_STS_CHANGE);
+
+ pci_xhci_insert_event(sc, &evtrb, 1);
+ }
+ }
+ break;
+
+ default:
+ DPRINTF(("Unhandled change port %d PLS %u\r\n",
+ port, newpls));
+ break;
+ }
+ break;
+ case 4:
+ /* Port power management status and control register */
+ p->portpmsc = value;
+ break;
+ case 8:
+ /* Port link information register */
+ DPRINTF(("pci_xhci attempted write to PORTLI, port %d\r\n",
+ port));
+ break;
+ case 12:
+ /*
+ * Port hardware LPM control register.
+ * For USB3, this register is reserved.
+ */
+ p->porthlpmc = value;
+ break;
+ }
+}
+
+struct xhci_dev_ctx *
+pci_xhci_get_dev_ctx(struct pci_xhci_softc *sc, uint32_t slot)
+{
+ uint64_t devctx_addr;
+ struct xhci_dev_ctx *devctx;
+
+ assert(slot > 0 && slot <= sc->ndevices);
+ assert(sc->opregs.dcbaa_p != NULL);
+
+ devctx_addr = sc->opregs.dcbaa_p->dcba[slot];
+
+ if (devctx_addr == 0) {
+ DPRINTF(("get_dev_ctx devctx_addr == 0\r\n"));
+ return (NULL);
+ }
+
+ DPRINTF(("pci_xhci: get dev ctx, slot %u devctx addr %016lx\r\n",
+ slot, devctx_addr));
+ devctx = XHCI_GADDR(sc, devctx_addr & ~0x3FUL);
+
+ return (devctx);
+}
+
+struct xhci_trb *
+pci_xhci_trb_next(struct pci_xhci_softc *sc, struct xhci_trb *curtrb,
+ uint64_t *guestaddr)
+{
+ struct xhci_trb *next;
+
+ assert(curtrb != NULL);
+
+ if (XHCI_TRB_3_TYPE_GET(curtrb->dwTrb3) == XHCI_TRB_TYPE_LINK) {
+ if (guestaddr)
+ *guestaddr = curtrb->qwTrb0 & ~0xFUL;
+
+ next = XHCI_GADDR(sc, curtrb->qwTrb0 & ~0xFUL);
+ } else {
+ if (guestaddr)
+ *guestaddr += sizeof(struct xhci_trb) & ~0xFUL;
+
+ next = curtrb + 1;
+ }
+
+ return (next);
+}
+
+static void
+pci_xhci_assert_interrupt(struct pci_xhci_softc *sc)
+{
+
+ sc->rtsregs.intrreg.erdp |= XHCI_ERDP_LO_BUSY;
+ sc->rtsregs.intrreg.iman |= XHCI_IMAN_INTR_PEND;
+ sc->opregs.usbsts |= XHCI_STS_EINT;
+
+ /* only trigger interrupt if permitted */
+ if ((sc->opregs.usbcmd & XHCI_CMD_INTE) &&
+ (sc->rtsregs.intrreg.iman & XHCI_IMAN_INTR_ENA)) {
+ if (pci_msi_enabled(sc->xsc_pi))
+ pci_generate_msi(sc->xsc_pi, 0);
+ else
+ pci_lintr_assert(sc->xsc_pi);
+ }
+}
+
+static void
+pci_xhci_deassert_interrupt(struct pci_xhci_softc *sc)
+{
+
+ if (!pci_msi_enabled(sc->xsc_pi))
+ pci_lintr_assert(sc->xsc_pi);
+}
+
+static void
+pci_xhci_init_ep(struct pci_xhci_dev_emu *dev, int epid)
+{
+ struct xhci_dev_ctx *dev_ctx;
+ struct pci_xhci_dev_ep *devep;
+ struct xhci_endp_ctx *ep_ctx;
+ uint32_t pstreams;
+ int i;
+
+ dev_ctx = dev->dev_ctx;
+ ep_ctx = &dev_ctx->ctx_ep[epid];
+ devep = &dev->eps[epid];
+ pstreams = XHCI_EPCTX_0_MAXP_STREAMS_GET(ep_ctx->dwEpCtx0);
+ if (pstreams > 0) {
+ DPRINTF(("init_ep %d with pstreams %d\r\n", epid, pstreams));
+ assert(devep->ep_sctx_trbs == NULL);
+
+ devep->ep_sctx = XHCI_GADDR(dev->xsc, ep_ctx->qwEpCtx2 &
+ XHCI_EPCTX_2_TR_DQ_PTR_MASK);
+ devep->ep_sctx_trbs = calloc(pstreams,
+ sizeof(struct pci_xhci_trb_ring));
+ for (i = 0; i < pstreams; i++) {
+ devep->ep_sctx_trbs[i].ringaddr =
+ devep->ep_sctx[i].qwSctx0 &
+ XHCI_SCTX_0_TR_DQ_PTR_MASK;
+ devep->ep_sctx_trbs[i].ccs =
+ XHCI_SCTX_0_DCS_GET(devep->ep_sctx[i].qwSctx0);
+ }
+ } else {
+ DPRINTF(("init_ep %d with no pstreams\r\n", epid));
+ devep->ep_ringaddr = ep_ctx->qwEpCtx2 &
+ XHCI_EPCTX_2_TR_DQ_PTR_MASK;
+ devep->ep_ccs = XHCI_EPCTX_2_DCS_GET(ep_ctx->qwEpCtx2);
+ devep->ep_tr = XHCI_GADDR(dev->xsc, devep->ep_ringaddr);
+ DPRINTF(("init_ep tr DCS %x\r\n", devep->ep_ccs));
+ }
+
+ if (devep->ep_xfer == NULL) {
+ devep->ep_xfer = malloc(sizeof(struct usb_data_xfer));
+ USB_DATA_XFER_INIT(devep->ep_xfer);
+ }
+}
+
+static void
+pci_xhci_disable_ep(struct pci_xhci_dev_emu *dev, int epid)
+{
+ struct xhci_dev_ctx *dev_ctx;
+ struct pci_xhci_dev_ep *devep;
+ struct xhci_endp_ctx *ep_ctx;
+
+ DPRINTF(("pci_xhci disable_ep %d\r\n", epid));
+
+ dev_ctx = dev->dev_ctx;
+ ep_ctx = &dev_ctx->ctx_ep[epid];
+ ep_ctx->dwEpCtx0 = (ep_ctx->dwEpCtx0 & ~0x7) | XHCI_ST_EPCTX_DISABLED;
+
+ devep = &dev->eps[epid];
+ if (XHCI_EPCTX_0_MAXP_STREAMS_GET(ep_ctx->dwEpCtx0) > 0 &&
+ devep->ep_sctx_trbs != NULL)
+ free(devep->ep_sctx_trbs);
+
+ if (devep->ep_xfer != NULL) {
+ free(devep->ep_xfer);
+ devep->ep_xfer = NULL;
+ }
+
+ memset(devep, 0, sizeof(struct pci_xhci_dev_ep));
+}
+
+
+/* reset device at slot and data structures related to it */
+static void
+pci_xhci_reset_slot(struct pci_xhci_softc *sc, int slot)
+{
+ struct pci_xhci_dev_emu *dev;
+
+ dev = XHCI_SLOTDEV_PTR(sc, slot);
+
+ if (!dev) {
+ DPRINTF(("xhci reset unassigned slot (%d)?\r\n", slot));
+ } else {
+ dev->dev_slotstate = XHCI_ST_DISABLED;
+ }
+
+ /* TODO: reset ring buffer pointers */
+}
+
+static int
+pci_xhci_insert_event(struct pci_xhci_softc *sc, struct xhci_trb *evtrb,
+ int do_intr)
+{
+ struct pci_xhci_rtsregs *rts;
+ uint64_t erdp;
+ int erdp_idx;
+ int err;
+ struct xhci_trb *evtrbptr;
+
+ err = XHCI_TRB_ERROR_SUCCESS;
+
+ rts = &sc->rtsregs;
+
+ erdp = rts->intrreg.erdp & ~0xF;
+ erdp_idx = (erdp - rts->erstba_p[rts->er_deq_seg].qwEvrsTablePtr) /
+ sizeof(struct xhci_trb);
+
+ DPRINTF(("pci_xhci: insert event 0[%lx] 2[%x] 3[%x]\r\n"
+ "\terdp idx %d/seg %d, enq idx %d/seg %d, pcs %u\r\n"
+ "\t(erdp=0x%lx, erst=0x%lx, tblsz=%u, do_intr %d)\r\n",
+ evtrb->qwTrb0, evtrb->dwTrb2, evtrb->dwTrb3,
+ erdp_idx, rts->er_deq_seg, rts->er_enq_idx,
+ rts->er_enq_seg,
+ rts->event_pcs, erdp, rts->erstba_p->qwEvrsTablePtr,
+ rts->erstba_p->dwEvrsTableSize, do_intr));
+
+ evtrbptr = &rts->erst_p[rts->er_enq_idx];
+
+ /* TODO: multi-segment table */
+ if (rts->er_events_cnt >= rts->erstba_p->dwEvrsTableSize) {
+ DPRINTF(("pci_xhci[%d] cannot insert event; ring full\r\n",
+ __LINE__));
+ err = XHCI_TRB_ERROR_EV_RING_FULL;
+ goto done;
+ }
+
+ if (rts->er_events_cnt == rts->erstba_p->dwEvrsTableSize - 1) {
+ struct xhci_trb errev;
+
+ if ((evtrbptr->dwTrb3 & 0x1) == (rts->event_pcs & 0x1)) {
+
+ DPRINTF(("pci_xhci[%d] insert evt err: ring full\r\n",
+ __LINE__));
+
+ errev.qwTrb0 = 0;
+ errev.dwTrb2 = XHCI_TRB_2_ERROR_SET(
+ XHCI_TRB_ERROR_EV_RING_FULL);
+ errev.dwTrb3 = XHCI_TRB_3_TYPE_SET(
+ XHCI_TRB_EVENT_HOST_CTRL) |
+ rts->event_pcs;
+ rts->er_events_cnt++;
+ memcpy(&rts->erst_p[rts->er_enq_idx], &errev,
+ sizeof(struct xhci_trb));
+ rts->er_enq_idx = (rts->er_enq_idx + 1) %
+ rts->erstba_p->dwEvrsTableSize;
+ err = XHCI_TRB_ERROR_EV_RING_FULL;
+ do_intr = 1;
+
+ goto done;
+ }
+ } else {
+ rts->er_events_cnt++;
+ }
+
+ evtrb->dwTrb3 &= ~XHCI_TRB_3_CYCLE_BIT;
+ evtrb->dwTrb3 |= rts->event_pcs;
+
+ memcpy(&rts->erst_p[rts->er_enq_idx], evtrb, sizeof(struct xhci_trb));
+ rts->er_enq_idx = (rts->er_enq_idx + 1) %
+ rts->erstba_p->dwEvrsTableSize;
+
+ if (rts->er_enq_idx == 0)
+ rts->event_pcs ^= 1;
+
+done:
+ if (do_intr)
+ pci_xhci_assert_interrupt(sc);
+
+ return (err);
+}
+
+static uint32_t
+pci_xhci_cmd_enable_slot(struct pci_xhci_softc *sc, uint32_t *slot)
+{
+ struct pci_xhci_dev_emu *dev;
+ uint32_t cmderr;
+ int i;
+
+ cmderr = XHCI_TRB_ERROR_NO_SLOTS;
+ if (sc->portregs != NULL)
+ for (i = 1; i <= XHCI_MAX_SLOTS; i++) {
+ dev = XHCI_SLOTDEV_PTR(sc, i);
+ if (dev && dev->dev_slotstate == XHCI_ST_DISABLED) {
+ *slot = i;
+ dev->dev_slotstate = XHCI_ST_ENABLED;
+ cmderr = XHCI_TRB_ERROR_SUCCESS;
+ dev->hci.hci_address = i;
+ break;
+ }
+ }
+
+ DPRINTF(("pci_xhci enable slot (error=%d) slot %u\r\n",
+ cmderr != XHCI_TRB_ERROR_SUCCESS, *slot));
+
+ return (cmderr);
+}
+
+static uint32_t
+pci_xhci_cmd_disable_slot(struct pci_xhci_softc *sc, uint32_t slot)
+{
+ struct pci_xhci_dev_emu *dev;
+ uint32_t cmderr;
+
+ DPRINTF(("pci_xhci disable slot %u\r\n", slot));
+
+ cmderr = XHCI_TRB_ERROR_NO_SLOTS;
+ if (sc->portregs == NULL)
+ goto done;
+
+ if (slot > sc->ndevices) {
+ cmderr = XHCI_TRB_ERROR_SLOT_NOT_ON;
+ goto done;
+ }
+
+ dev = XHCI_SLOTDEV_PTR(sc, slot);
+ if (dev) {
+ if (dev->dev_slotstate == XHCI_ST_DISABLED) {
+ cmderr = XHCI_TRB_ERROR_SLOT_NOT_ON;
+ } else {
+ dev->dev_slotstate = XHCI_ST_DISABLED;
+ cmderr = XHCI_TRB_ERROR_SUCCESS;
+ /* TODO: reset events and endpoints */
+ }
+ }
+
+done:
+ return (cmderr);
+}
+
+static uint32_t
+pci_xhci_cmd_reset_device(struct pci_xhci_softc *sc, uint32_t slot)
+{
+ struct pci_xhci_dev_emu *dev;
+ struct xhci_dev_ctx *dev_ctx;
+ struct xhci_endp_ctx *ep_ctx;
+ uint32_t cmderr;
+ int i;
+
+ cmderr = XHCI_TRB_ERROR_NO_SLOTS;
+ if (sc->portregs == NULL)
+ goto done;
+
+ DPRINTF(("pci_xhci reset device slot %u\r\n", slot));
+
+ dev = XHCI_SLOTDEV_PTR(sc, slot);
+ if (!dev || dev->dev_slotstate == XHCI_ST_DISABLED)
+ cmderr = XHCI_TRB_ERROR_SLOT_NOT_ON;
+ else {
+ dev->dev_slotstate = XHCI_ST_DEFAULT;
+
+ dev->hci.hci_address = 0;
+ dev_ctx = pci_xhci_get_dev_ctx(sc, slot);
+
+ /* slot state */
+ dev_ctx->ctx_slot.dwSctx3 = FIELD_REPLACE(
+ dev_ctx->ctx_slot.dwSctx3, XHCI_ST_SLCTX_DEFAULT,
+ 0x1F, 27);
+
+ /* number of contexts */
+ dev_ctx->ctx_slot.dwSctx0 = FIELD_REPLACE(
+ dev_ctx->ctx_slot.dwSctx0, 1, 0x1F, 27);
+
+ /* reset all eps other than ep-0 */
+ for (i = 2; i <= 31; i++) {
+ ep_ctx = &dev_ctx->ctx_ep[i];
+ ep_ctx->dwEpCtx0 = FIELD_REPLACE( ep_ctx->dwEpCtx0,
+ XHCI_ST_EPCTX_DISABLED, 0x7, 0);
+ }
+
+ cmderr = XHCI_TRB_ERROR_SUCCESS;
+ }
+
+ pci_xhci_reset_slot(sc, slot);
+
+done:
+ return (cmderr);
+}
+
+static uint32_t
+pci_xhci_cmd_address_device(struct pci_xhci_softc *sc, uint32_t slot,
+ struct xhci_trb *trb)
+{
+ struct pci_xhci_dev_emu *dev;
+ struct xhci_input_dev_ctx *input_ctx;
+ struct xhci_slot_ctx *islot_ctx;
+ struct xhci_dev_ctx *dev_ctx;
+ struct xhci_endp_ctx *ep0_ctx;
+ uint32_t cmderr;
+
+ input_ctx = XHCI_GADDR(sc, trb->qwTrb0 & ~0xFUL);
+ islot_ctx = &input_ctx->ctx_slot;
+ ep0_ctx = &input_ctx->ctx_ep[1];
+
+ cmderr = XHCI_TRB_ERROR_SUCCESS;
+
+ DPRINTF(("pci_xhci: address device, input ctl: D 0x%08x A 0x%08x,\r\n"
+ " slot %08x %08x %08x %08x\r\n"
+ " ep0 %08x %08x %016lx %08x\r\n",
+ input_ctx->ctx_input.dwInCtx0, input_ctx->ctx_input.dwInCtx1,
+ islot_ctx->dwSctx0, islot_ctx->dwSctx1,
+ islot_ctx->dwSctx2, islot_ctx->dwSctx3,
+ ep0_ctx->dwEpCtx0, ep0_ctx->dwEpCtx1, ep0_ctx->qwEpCtx2,
+ ep0_ctx->dwEpCtx4));
+
+ /* when setting address: drop-ctx=0, add-ctx=slot+ep0 */
+ if ((input_ctx->ctx_input.dwInCtx0 != 0) ||
+ (input_ctx->ctx_input.dwInCtx1 & 0x03) != 0x03) {
+ DPRINTF(("pci_xhci: address device, input ctl invalid\r\n"));
+ cmderr = XHCI_TRB_ERROR_TRB;
+ goto done;
+ }
+
+ /* assign address to slot */
+ dev_ctx = pci_xhci_get_dev_ctx(sc, slot);
+
+ DPRINTF(("pci_xhci: address device, dev ctx\r\n"
+ " slot %08x %08x %08x %08x\r\n",
+ dev_ctx->ctx_slot.dwSctx0, dev_ctx->ctx_slot.dwSctx1,
+ dev_ctx->ctx_slot.dwSctx2, dev_ctx->ctx_slot.dwSctx3));
+
+ dev = XHCI_SLOTDEV_PTR(sc, slot);
+ assert(dev != NULL);
+
+ dev->hci.hci_address = slot;
+ dev->dev_ctx = dev_ctx;
+
+ if (dev->dev_ue->ue_reset == NULL ||
+ dev->dev_ue->ue_reset(dev->dev_sc) < 0) {
+ cmderr = XHCI_TRB_ERROR_ENDP_NOT_ON;
+ goto done;
+ }
+
+ memcpy(&dev_ctx->ctx_slot, islot_ctx, sizeof(struct xhci_slot_ctx));
+
+ dev_ctx->ctx_slot.dwSctx3 =
+ XHCI_SCTX_3_SLOT_STATE_SET(XHCI_ST_SLCTX_ADDRESSED) |
+ XHCI_SCTX_3_DEV_ADDR_SET(slot);
+
+ memcpy(&dev_ctx->ctx_ep[1], ep0_ctx, sizeof(struct xhci_endp_ctx));
+ ep0_ctx = &dev_ctx->ctx_ep[1];
+ ep0_ctx->dwEpCtx0 = (ep0_ctx->dwEpCtx0 & ~0x7) |
+ XHCI_EPCTX_0_EPSTATE_SET(XHCI_ST_EPCTX_RUNNING);
+
+ pci_xhci_init_ep(dev, 1);
+
+ dev->dev_slotstate = XHCI_ST_ADDRESSED;
+
+ DPRINTF(("pci_xhci: address device, output ctx\r\n"
+ " slot %08x %08x %08x %08x\r\n"
+ " ep0 %08x %08x %016lx %08x\r\n",
+ dev_ctx->ctx_slot.dwSctx0, dev_ctx->ctx_slot.dwSctx1,
+ dev_ctx->ctx_slot.dwSctx2, dev_ctx->ctx_slot.dwSctx3,
+ ep0_ctx->dwEpCtx0, ep0_ctx->dwEpCtx1, ep0_ctx->qwEpCtx2,
+ ep0_ctx->dwEpCtx4));
+
+done:
+ return (cmderr);
+}
+
+static uint32_t
+pci_xhci_cmd_config_ep(struct pci_xhci_softc *sc, uint32_t slot,
+ struct xhci_trb *trb)
+{
+ struct xhci_input_dev_ctx *input_ctx;
+ struct pci_xhci_dev_emu *dev;
+ struct xhci_dev_ctx *dev_ctx;
+ struct xhci_endp_ctx *ep_ctx, *iep_ctx;
+ uint32_t cmderr;
+ int i;
+
+ cmderr = XHCI_TRB_ERROR_SUCCESS;
+
+ DPRINTF(("pci_xhci config_ep slot %u\r\n", slot));
+
+ dev = XHCI_SLOTDEV_PTR(sc, slot);
+ assert(dev != NULL);
+
+ if ((trb->dwTrb3 & XHCI_TRB_3_DCEP_BIT) != 0) {
+ DPRINTF(("pci_xhci config_ep - deconfigure ep slot %u\r\n",
+ slot));
+ if (dev->dev_ue->ue_stop != NULL)
+ dev->dev_ue->ue_stop(dev->dev_sc);
+
+ dev->dev_slotstate = XHCI_ST_ADDRESSED;
+
+ dev->hci.hci_address = 0;
+ dev_ctx = pci_xhci_get_dev_ctx(sc, slot);
+
+ /* number of contexts */
+ dev_ctx->ctx_slot.dwSctx0 = FIELD_REPLACE(
+ dev_ctx->ctx_slot.dwSctx0, 1, 0x1F, 27);
+
+ /* slot state */
+ dev_ctx->ctx_slot.dwSctx3 = FIELD_REPLACE(
+ dev_ctx->ctx_slot.dwSctx3, XHCI_ST_SLCTX_ADDRESSED,
+ 0x1F, 27);
+
+ /* disable endpoints */
+ for (i = 2; i < 32; i++)
+ pci_xhci_disable_ep(dev, i);
+
+ cmderr = XHCI_TRB_ERROR_SUCCESS;
+
+ goto done;
+ }
+
+ if (dev->dev_slotstate < XHCI_ST_ADDRESSED) {
+ DPRINTF(("pci_xhci: config_ep slotstate x%x != addressed\r\n",
+ dev->dev_slotstate));
+ cmderr = XHCI_TRB_ERROR_SLOT_NOT_ON;
+ goto done;
+ }
+
+ /* In addressed/configured state;
+ * for each drop endpoint ctx flag:
+ * ep->state = DISABLED
+ * for each add endpoint ctx flag:
+ * cp(ep-in, ep-out)
+ * ep->state = RUNNING
+ * for each drop+add endpoint flag:
+ * reset ep resources
+ * cp(ep-in, ep-out)
+ * ep->state = RUNNING
+ * if input->DisabledCtx[2-31] < 30: (at least 1 ep not disabled)
+ * slot->state = configured
+ */
+
+ input_ctx = XHCI_GADDR(sc, trb->qwTrb0 & ~0xFUL);
+ dev_ctx = dev->dev_ctx;
+ DPRINTF(("pci_xhci: config_ep inputctx: D:x%08x A:x%08x 7:x%08x\r\n",
+ input_ctx->ctx_input.dwInCtx0, input_ctx->ctx_input.dwInCtx1,
+ input_ctx->ctx_input.dwInCtx7));
+
+ for (i = 2; i <= 31; i++) {
+ ep_ctx = &dev_ctx->ctx_ep[i];
+
+ if (input_ctx->ctx_input.dwInCtx0 &
+ XHCI_INCTX_0_DROP_MASK(i)) {
+ DPRINTF((" config ep - dropping ep %d\r\n", i));
+ pci_xhci_disable_ep(dev, i);
+ }
+
+ if (input_ctx->ctx_input.dwInCtx1 &
+ XHCI_INCTX_1_ADD_MASK(i)) {
+ iep_ctx = &input_ctx->ctx_ep[i];
+
+ DPRINTF((" enable ep[%d] %08x %08x %016lx %08x\r\n",
+ i, iep_ctx->dwEpCtx0, iep_ctx->dwEpCtx1,
+ iep_ctx->qwEpCtx2, iep_ctx->dwEpCtx4));
+
+ memcpy(ep_ctx, iep_ctx, sizeof(struct xhci_endp_ctx));
+
+ pci_xhci_init_ep(dev, i);
+
+ /* ep state */
+ ep_ctx->dwEpCtx0 = FIELD_REPLACE(
+ ep_ctx->dwEpCtx0, XHCI_ST_EPCTX_RUNNING, 0x7, 0);
+ }
+ }
+
+ /* slot state to configured */
+ dev_ctx->ctx_slot.dwSctx3 = FIELD_REPLACE(
+ dev_ctx->ctx_slot.dwSctx3, XHCI_ST_SLCTX_CONFIGURED, 0x1F, 27);
+ dev_ctx->ctx_slot.dwSctx0 = FIELD_COPY(
+ dev_ctx->ctx_slot.dwSctx0, input_ctx->ctx_slot.dwSctx0, 0x1F, 27);
+ dev->dev_slotstate = XHCI_ST_CONFIGURED;
+
+ DPRINTF(("EP configured; slot %u [0]=0x%08x [1]=0x%08x [2]=0x%08x "
+ "[3]=0x%08x\r\n",
+ slot, dev_ctx->ctx_slot.dwSctx0, dev_ctx->ctx_slot.dwSctx1,
+ dev_ctx->ctx_slot.dwSctx2, dev_ctx->ctx_slot.dwSctx3));
+
+done:
+ return (cmderr);
+}
+
+static uint32_t
+pci_xhci_cmd_reset_ep(struct pci_xhci_softc *sc, uint32_t slot,
+ struct xhci_trb *trb)
+{
+ struct pci_xhci_dev_emu *dev;
+ struct pci_xhci_dev_ep *devep;
+ struct xhci_dev_ctx *dev_ctx;
+ struct xhci_endp_ctx *ep_ctx;
+ uint32_t cmderr, epid;
+ uint32_t type;
+
+ epid = XHCI_TRB_3_EP_GET(trb->dwTrb3);
+
+ DPRINTF(("pci_xhci: reset ep %u: slot %u\r\n", epid, slot));
+
+ cmderr = XHCI_TRB_ERROR_SUCCESS;
+
+ type = XHCI_TRB_3_TYPE_GET(trb->dwTrb3);
+
+ dev = XHCI_SLOTDEV_PTR(sc, slot);
+ assert(dev != NULL);
+
+ if (type == XHCI_TRB_TYPE_STOP_EP &&
+ (trb->dwTrb3 & XHCI_TRB_3_SUSP_EP_BIT) != 0) {
+ /* XXX suspend endpoint for 10ms */
+ }
+
+ if (epid < 1 || epid > 31) {
+ DPRINTF(("pci_xhci: reset ep: invalid epid %u\r\n", epid));
+ cmderr = XHCI_TRB_ERROR_TRB;
+ goto done;
+ }
+
+ devep = &dev->eps[epid];
+ if (devep->ep_xfer != NULL)
+ USB_DATA_XFER_RESET(devep->ep_xfer);
+
+ dev_ctx = dev->dev_ctx;
+ assert(dev_ctx != NULL);
+
+ ep_ctx = &dev_ctx->ctx_ep[epid];
+
+ ep_ctx->dwEpCtx0 = (ep_ctx->dwEpCtx0 & ~0x7) | XHCI_ST_EPCTX_STOPPED;
+
+ if (XHCI_EPCTX_0_MAXP_STREAMS_GET(ep_ctx->dwEpCtx0) == 0)
+ ep_ctx->qwEpCtx2 = devep->ep_ringaddr | devep->ep_ccs;
+
+ DPRINTF(("pci_xhci: reset ep[%u] %08x %08x %016lx %08x\r\n",
+ epid, ep_ctx->dwEpCtx0, ep_ctx->dwEpCtx1, ep_ctx->qwEpCtx2,
+ ep_ctx->dwEpCtx4));
+
+ if (type == XHCI_TRB_TYPE_RESET_EP &&
+ (dev->dev_ue->ue_reset == NULL ||
+ dev->dev_ue->ue_reset(dev->dev_sc) < 0)) {
+ cmderr = XHCI_TRB_ERROR_ENDP_NOT_ON;
+ goto done;
+ }
+
+done:
+ return (cmderr);
+}
+
+
+static uint32_t
+pci_xhci_find_stream(struct pci_xhci_softc *sc, struct xhci_endp_ctx *ep,
+ uint32_t streamid, struct xhci_stream_ctx **osctx)
+{
+ struct xhci_stream_ctx *sctx;
+ uint32_t maxpstreams;
+
+ maxpstreams = XHCI_EPCTX_0_MAXP_STREAMS_GET(ep->dwEpCtx0);
+ if (maxpstreams == 0)
+ return (XHCI_TRB_ERROR_TRB);
+
+ if (maxpstreams > XHCI_STREAMS_MAX)
+ return (XHCI_TRB_ERROR_INVALID_SID);
+
+ if (XHCI_EPCTX_0_LSA_GET(ep->dwEpCtx0) == 0) {
+ DPRINTF(("pci_xhci: find_stream; LSA bit not set\r\n"));
+ return (XHCI_TRB_ERROR_INVALID_SID);
+ }
+
+ /* only support primary stream */
+ if (streamid > maxpstreams)
+ return (XHCI_TRB_ERROR_STREAM_TYPE);
+
+ sctx = XHCI_GADDR(sc, ep->qwEpCtx2 & ~0xFUL) + streamid;
+ if (!XHCI_SCTX_0_SCT_GET(sctx->qwSctx0))
+ return (XHCI_TRB_ERROR_STREAM_TYPE);
+
+ *osctx = sctx;
+
+ return (XHCI_TRB_ERROR_SUCCESS);
+}
+
+
+static uint32_t
+pci_xhci_cmd_set_tr(struct pci_xhci_softc *sc, uint32_t slot,
+ struct xhci_trb *trb)
+{
+ struct pci_xhci_dev_emu *dev;
+ struct pci_xhci_dev_ep *devep;
+ struct xhci_dev_ctx *dev_ctx;
+ struct xhci_endp_ctx *ep_ctx;
+ uint32_t cmderr, epid;
+ uint32_t streamid;
+
+ cmderr = XHCI_TRB_ERROR_SUCCESS;
+
+ dev = XHCI_SLOTDEV_PTR(sc, slot);
+ assert(dev != NULL);
+
+ DPRINTF(("pci_xhci set_tr: new-tr x%016lx, SCT %u DCS %u\r\n"
+ " stream-id %u, slot %u, epid %u, C %u\r\n",
+ (trb->qwTrb0 & ~0xF), (uint32_t)((trb->qwTrb0 >> 1) & 0x7),
+ (uint32_t)(trb->qwTrb0 & 0x1), (trb->dwTrb2 >> 16) & 0xFFFF,
+ XHCI_TRB_3_SLOT_GET(trb->dwTrb3),
+ XHCI_TRB_3_EP_GET(trb->dwTrb3), trb->dwTrb3 & 0x1));
+
+ epid = XHCI_TRB_3_EP_GET(trb->dwTrb3);
+ if (epid < 1 || epid > 31) {
+ DPRINTF(("pci_xhci: set_tr_deq: invalid epid %u\r\n", epid));
+ cmderr = XHCI_TRB_ERROR_TRB;
+ goto done;
+ }
+
+ dev_ctx = dev->dev_ctx;
+ assert(dev_ctx != NULL);
+
+ ep_ctx = &dev_ctx->ctx_ep[epid];
+ devep = &dev->eps[epid];
+
+ switch (XHCI_EPCTX_0_EPSTATE_GET(ep_ctx->dwEpCtx0)) {
+ case XHCI_ST_EPCTX_STOPPED:
+ case XHCI_ST_EPCTX_ERROR:
+ break;
+ default:
+ DPRINTF(("pci_xhci cmd set_tr invalid state %x\r\n",
+ XHCI_EPCTX_0_EPSTATE_GET(ep_ctx->dwEpCtx0)));
+ cmderr = XHCI_TRB_ERROR_CONTEXT_STATE;
+ goto done;
+ }
+
+ streamid = XHCI_TRB_2_STREAM_GET(trb->dwTrb2);
+ if (XHCI_EPCTX_0_MAXP_STREAMS_GET(ep_ctx->dwEpCtx0) > 0) {
+ struct xhci_stream_ctx *sctx;
+
+ sctx = NULL;
+ cmderr = pci_xhci_find_stream(sc, ep_ctx, streamid, &sctx);
+ if (sctx != NULL) {
+ assert(devep->ep_sctx != NULL);
+
+ devep->ep_sctx[streamid].qwSctx0 = trb->qwTrb0;
+ devep->ep_sctx_trbs[streamid].ringaddr =
+ trb->qwTrb0 & ~0xF;
+ devep->ep_sctx_trbs[streamid].ccs =
+ XHCI_EPCTX_2_DCS_GET(trb->qwTrb0);
+ }
+ } else {
+ if (streamid != 0) {
+ DPRINTF(("pci_xhci cmd set_tr streamid %x != 0\r\n",
+ streamid));
+ }
+ ep_ctx->qwEpCtx2 = trb->qwTrb0 & ~0xFUL;
+ devep->ep_ringaddr = ep_ctx->qwEpCtx2 & ~0xFUL;
+ devep->ep_ccs = trb->qwTrb0 & 0x1;
+ devep->ep_tr = XHCI_GADDR(sc, devep->ep_ringaddr);
+
+ DPRINTF(("pci_xhci set_tr first TRB:\r\n"));
+ pci_xhci_dump_trb(devep->ep_tr);
+ }
+ ep_ctx->dwEpCtx0 = (ep_ctx->dwEpCtx0 & ~0x7) | XHCI_ST_EPCTX_STOPPED;
+
+done:
+ return (cmderr);
+}
+
+static uint32_t
+pci_xhci_cmd_eval_ctx(struct pci_xhci_softc *sc, uint32_t slot,
+ struct xhci_trb *trb)
+{
+ struct xhci_input_dev_ctx *input_ctx;
+ struct xhci_slot_ctx *islot_ctx;
+ struct xhci_dev_ctx *dev_ctx;
+ struct xhci_endp_ctx *ep0_ctx;
+ uint32_t cmderr;
+
+ input_ctx = XHCI_GADDR(sc, trb->qwTrb0 & ~0xFUL);
+ islot_ctx = &input_ctx->ctx_slot;
+ ep0_ctx = &input_ctx->ctx_ep[1];
+
+ cmderr = XHCI_TRB_ERROR_SUCCESS;
+ DPRINTF(("pci_xhci: eval ctx, input ctl: D 0x%08x A 0x%08x,\r\n"
+ " slot %08x %08x %08x %08x\r\n"
+ " ep0 %08x %08x %016lx %08x\r\n",
+ input_ctx->ctx_input.dwInCtx0, input_ctx->ctx_input.dwInCtx1,
+ islot_ctx->dwSctx0, islot_ctx->dwSctx1,
+ islot_ctx->dwSctx2, islot_ctx->dwSctx3,
+ ep0_ctx->dwEpCtx0, ep0_ctx->dwEpCtx1, ep0_ctx->qwEpCtx2,
+ ep0_ctx->dwEpCtx4));
+
+ /* this command expects drop-ctx=0 & add-ctx=slot+ep0 */
+ if ((input_ctx->ctx_input.dwInCtx0 != 0) ||
+ (input_ctx->ctx_input.dwInCtx1 & 0x03) == 0) {
+ DPRINTF(("pci_xhci: eval ctx, input ctl invalid\r\n"));
+ cmderr = XHCI_TRB_ERROR_TRB;
+ goto done;
+ }
+
+ /* assign address to slot; in this emulation, slot_id = address */
+ dev_ctx = pci_xhci_get_dev_ctx(sc, slot);
+
+ DPRINTF(("pci_xhci: eval ctx, dev ctx\r\n"
+ " slot %08x %08x %08x %08x\r\n",
+ dev_ctx->ctx_slot.dwSctx0, dev_ctx->ctx_slot.dwSctx1,
+ dev_ctx->ctx_slot.dwSctx2, dev_ctx->ctx_slot.dwSctx3));
+
+ if (input_ctx->ctx_input.dwInCtx1 & 0x01) { /* slot ctx */
+ /* set max exit latency */
+ dev_ctx->ctx_slot.dwSctx1 = FIELD_COPY(
+ dev_ctx->ctx_slot.dwSctx1, input_ctx->ctx_slot.dwSctx1,
+ 0xFFFF, 0);
+
+ /* set interrupter target */
+ dev_ctx->ctx_slot.dwSctx2 = FIELD_COPY(
+ dev_ctx->ctx_slot.dwSctx2, input_ctx->ctx_slot.dwSctx2,
+ 0x3FF, 22);
+ }
+ if (input_ctx->ctx_input.dwInCtx1 & 0x02) { /* control ctx */
+ /* set max packet size */
+ dev_ctx->ctx_ep[1].dwEpCtx1 = FIELD_COPY(
+ dev_ctx->ctx_ep[1].dwEpCtx1, ep0_ctx->dwEpCtx1,
+ 0xFFFF, 16);
+
+ ep0_ctx = &dev_ctx->ctx_ep[1];
+ }
+
+ DPRINTF(("pci_xhci: eval ctx, output ctx\r\n"
+ " slot %08x %08x %08x %08x\r\n"
+ " ep0 %08x %08x %016lx %08x\r\n",
+ dev_ctx->ctx_slot.dwSctx0, dev_ctx->ctx_slot.dwSctx1,
+ dev_ctx->ctx_slot.dwSctx2, dev_ctx->ctx_slot.dwSctx3,
+ ep0_ctx->dwEpCtx0, ep0_ctx->dwEpCtx1, ep0_ctx->qwEpCtx2,
+ ep0_ctx->dwEpCtx4));
+
+done:
+ return (cmderr);
+}
+
+static int
+pci_xhci_complete_commands(struct pci_xhci_softc *sc)
+{
+ struct xhci_trb evtrb;
+ struct xhci_trb *trb;
+ uint64_t crcr;
+ uint32_t ccs; /* cycle state (XHCI 4.9.2) */
+ uint32_t type;
+ uint32_t slot;
+ uint32_t cmderr;
+ int error;
+
+ error = 0;
+ sc->opregs.crcr |= XHCI_CRCR_LO_CRR;
+
+ trb = sc->opregs.cr_p;
+ ccs = sc->opregs.crcr & XHCI_CRCR_LO_RCS;
+ crcr = sc->opregs.crcr & ~0xF;
+
+ while (1) {
+ sc->opregs.cr_p = trb;
+
+ type = XHCI_TRB_3_TYPE_GET(trb->dwTrb3);
+
+ if ((trb->dwTrb3 & XHCI_TRB_3_CYCLE_BIT) !=
+ (ccs & XHCI_TRB_3_CYCLE_BIT))
+ break;
+
+ DPRINTF(("pci_xhci: cmd type 0x%x, Trb0 x%016lx dwTrb2 x%08x"
+ " dwTrb3 x%08x, TRB_CYCLE %u/ccs %u\r\n",
+ type, trb->qwTrb0, trb->dwTrb2, trb->dwTrb3,
+ trb->dwTrb3 & XHCI_TRB_3_CYCLE_BIT, ccs));
+
+ cmderr = XHCI_TRB_ERROR_SUCCESS;
+ evtrb.dwTrb2 = 0;
+ evtrb.dwTrb3 = (ccs & XHCI_TRB_3_CYCLE_BIT) |
+ XHCI_TRB_3_TYPE_SET(XHCI_TRB_EVENT_CMD_COMPLETE);
+ slot = 0;
+
+ switch (type) {
+ case XHCI_TRB_TYPE_LINK: /* 0x06 */
+ if (trb->dwTrb3 & XHCI_TRB_3_TC_BIT)
+ ccs ^= XHCI_CRCR_LO_RCS;
+ break;
+
+ case XHCI_TRB_TYPE_ENABLE_SLOT: /* 0x09 */
+ cmderr = pci_xhci_cmd_enable_slot(sc, &slot);
+ break;
+
+ case XHCI_TRB_TYPE_DISABLE_SLOT: /* 0x0A */
+ slot = XHCI_TRB_3_SLOT_GET(trb->dwTrb3);
+ cmderr = pci_xhci_cmd_disable_slot(sc, slot);
+ break;
+
+ case XHCI_TRB_TYPE_ADDRESS_DEVICE: /* 0x0B */
+ slot = XHCI_TRB_3_SLOT_GET(trb->dwTrb3);
+ cmderr = pci_xhci_cmd_address_device(sc, slot, trb);
+ break;
+
+ case XHCI_TRB_TYPE_CONFIGURE_EP: /* 0x0C */
+ slot = XHCI_TRB_3_SLOT_GET(trb->dwTrb3);
+ cmderr = pci_xhci_cmd_config_ep(sc, slot, trb);
+ break;
+
+ case XHCI_TRB_TYPE_EVALUATE_CTX: /* 0x0D */
+ slot = XHCI_TRB_3_SLOT_GET(trb->dwTrb3);
+ cmderr = pci_xhci_cmd_eval_ctx(sc, slot, trb);
+ break;
+
+ case XHCI_TRB_TYPE_RESET_EP: /* 0x0E */
+ DPRINTF(("Reset Endpoint on slot %d\r\n", slot));
+ slot = XHCI_TRB_3_SLOT_GET(trb->dwTrb3);
+ cmderr = pci_xhci_cmd_reset_ep(sc, slot, trb);
+ break;
+
+ case XHCI_TRB_TYPE_STOP_EP: /* 0x0F */
+ DPRINTF(("Stop Endpoint on slot %d\r\n", slot));
+ slot = XHCI_TRB_3_SLOT_GET(trb->dwTrb3);
+ cmderr = pci_xhci_cmd_reset_ep(sc, slot, trb);
+ break;
+
+ case XHCI_TRB_TYPE_SET_TR_DEQUEUE: /* 0x10 */
+ slot = XHCI_TRB_3_SLOT_GET(trb->dwTrb3);
+ cmderr = pci_xhci_cmd_set_tr(sc, slot, trb);
+ break;
+
+ case XHCI_TRB_TYPE_RESET_DEVICE: /* 0x11 */
+ slot = XHCI_TRB_3_SLOT_GET(trb->dwTrb3);
+ cmderr = pci_xhci_cmd_reset_device(sc, slot);
+ break;
+
+ case XHCI_TRB_TYPE_FORCE_EVENT: /* 0x12 */
+ /* TODO: */
+ break;
+
+ case XHCI_TRB_TYPE_NEGOTIATE_BW: /* 0x13 */
+ break;
+
+ case XHCI_TRB_TYPE_SET_LATENCY_TOL: /* 0x14 */
+ break;
+
+ case XHCI_TRB_TYPE_GET_PORT_BW: /* 0x15 */
+ break;
+
+ case XHCI_TRB_TYPE_FORCE_HEADER: /* 0x16 */
+ break;
+
+ case XHCI_TRB_TYPE_NOOP_CMD: /* 0x17 */
+ break;
+
+ default:
+ DPRINTF(("pci_xhci: unsupported cmd %x\r\n", type));
+ break;
+ }
+
+ if (type != XHCI_TRB_TYPE_LINK) {
+ /*
+ * insert command completion event and assert intr
+ */
+ evtrb.qwTrb0 = crcr;
+ evtrb.dwTrb2 |= XHCI_TRB_2_ERROR_SET(cmderr);
+ evtrb.dwTrb3 |= XHCI_TRB_3_SLOT_SET(slot);
+ DPRINTF(("pci_xhci: command 0x%x result: 0x%x\r\n",
+ type, cmderr));
+ pci_xhci_insert_event(sc, &evtrb, 1);
+ }
+
+ trb = pci_xhci_trb_next(sc, trb, &crcr);
+ }
+
+ sc->opregs.crcr = crcr | (sc->opregs.crcr & XHCI_CRCR_LO_CA) | ccs;
+ sc->opregs.crcr &= ~XHCI_CRCR_LO_CRR;
+ return (error);
+}
+
+static void
+pci_xhci_dump_trb(struct xhci_trb *trb)
+{
+ static const char *trbtypes[] = {
+ "RESERVED",
+ "NORMAL",
+ "SETUP_STAGE",
+ "DATA_STAGE",
+ "STATUS_STAGE",
+ "ISOCH",
+ "LINK",
+ "EVENT_DATA",
+ "NOOP",
+ "ENABLE_SLOT",
+ "DISABLE_SLOT",
+ "ADDRESS_DEVICE",
+ "CONFIGURE_EP",
+ "EVALUATE_CTX",
+ "RESET_EP",
+ "STOP_EP",
+ "SET_TR_DEQUEUE",
+ "RESET_DEVICE",
+ "FORCE_EVENT",
+ "NEGOTIATE_BW",
+ "SET_LATENCY_TOL",
+ "GET_PORT_BW",
+ "FORCE_HEADER",
+ "NOOP_CMD"
+ };
+ uint32_t type;
+
+ type = XHCI_TRB_3_TYPE_GET(trb->dwTrb3);
+ DPRINTF(("pci_xhci: trb[@%p] type x%02x %s 0:x%016lx 2:x%08x 3:x%08x\r\n",
+ trb, type,
+ type <= XHCI_TRB_TYPE_NOOP_CMD ? trbtypes[type] : "INVALID",
+ trb->qwTrb0, trb->dwTrb2, trb->dwTrb3));
+}
+
+static int
+pci_xhci_xfer_complete(struct pci_xhci_softc *sc, struct usb_data_xfer *xfer,
+ uint32_t slot, uint32_t epid, int *do_intr)
+{
+ struct pci_xhci_dev_emu *dev;
+ struct pci_xhci_dev_ep *devep;
+ struct xhci_dev_ctx *dev_ctx;
+ struct xhci_endp_ctx *ep_ctx;
+ struct xhci_trb *trb;
+ struct xhci_trb evtrb;
+ uint32_t trbflags;
+ uint32_t edtla;
+ int i, err;
+
+ dev = XHCI_SLOTDEV_PTR(sc, slot);
+ devep = &dev->eps[epid];
+ dev_ctx = pci_xhci_get_dev_ctx(sc, slot);
+
+ assert(dev_ctx != NULL);
+
+ ep_ctx = &dev_ctx->ctx_ep[epid];
+
+ err = XHCI_TRB_ERROR_SUCCESS;
+ *do_intr = 0;
+ edtla = 0;
+
+ /* go through list of TRBs and insert event(s) */
+ for (i = xfer->head; xfer->ndata > 0; ) {
+ evtrb.qwTrb0 = (uint64_t)xfer->data[i].hci_data;
+ trb = XHCI_GADDR(sc, evtrb.qwTrb0);
+ trbflags = trb->dwTrb3;
+
+ DPRINTF(("pci_xhci: xfer[%d] done?%u:%d trb %x %016lx %x "
+ "(err %d) IOC?%d\r\n",
+ i, xfer->data[i].processed, xfer->data[i].blen,
+ XHCI_TRB_3_TYPE_GET(trbflags), evtrb.qwTrb0,
+ trbflags, err,
+ trb->dwTrb3 & XHCI_TRB_3_IOC_BIT ? 1 : 0));
+
+ if (!xfer->data[i].processed) {
+ xfer->head = i;
+ break;
+ }
+
+ xfer->ndata--;
+ edtla += xfer->data[i].bdone;
+
+ trb->dwTrb3 = (trb->dwTrb3 & ~0x1) | (xfer->data[i].ccs);
+
+ pci_xhci_update_ep_ring(sc, dev, devep, ep_ctx,
+ xfer->data[i].streamid, xfer->data[i].trbnext,
+ xfer->data[i].ccs);
+
+ /* Only interrupt if IOC or short packet */
+ if (!(trb->dwTrb3 & XHCI_TRB_3_IOC_BIT) &&
+ !((err == XHCI_TRB_ERROR_SHORT_PKT) &&
+ (trb->dwTrb3 & XHCI_TRB_3_ISP_BIT))) {
+
+ i = (i + 1) % USB_MAX_XFER_BLOCKS;
+ continue;
+ }
+
+ evtrb.dwTrb2 = XHCI_TRB_2_ERROR_SET(err) |
+ XHCI_TRB_2_REM_SET(xfer->data[i].blen);
+
+ evtrb.dwTrb3 = XHCI_TRB_3_TYPE_SET(XHCI_TRB_EVENT_TRANSFER) |
+ XHCI_TRB_3_SLOT_SET(slot) | XHCI_TRB_3_EP_SET(epid);
+
+ if (XHCI_TRB_3_TYPE_GET(trbflags) == XHCI_TRB_TYPE_EVENT_DATA) {
+ DPRINTF(("pci_xhci EVENT_DATA edtla %u\r\n", edtla));
+ evtrb.qwTrb0 = trb->qwTrb0;
+ evtrb.dwTrb2 = (edtla & 0xFFFFF) |
+ XHCI_TRB_2_ERROR_SET(err);
+ evtrb.dwTrb3 |= XHCI_TRB_3_ED_BIT;
+ edtla = 0;
+ }
+
+ *do_intr = 1;
+
+ err = pci_xhci_insert_event(sc, &evtrb, 0);
+ if (err != XHCI_TRB_ERROR_SUCCESS) {
+ break;
+ }
+
+ i = (i + 1) % USB_MAX_XFER_BLOCKS;
+ }
+
+ return (err);
+}
+
+static void
+pci_xhci_update_ep_ring(struct pci_xhci_softc *sc, struct pci_xhci_dev_emu *dev,
+ struct pci_xhci_dev_ep *devep, struct xhci_endp_ctx *ep_ctx,
+ uint32_t streamid, uint64_t ringaddr, int ccs)
+{
+
+ if (XHCI_EPCTX_0_MAXP_STREAMS_GET(ep_ctx->dwEpCtx0) != 0) {
+ devep->ep_sctx[streamid].qwSctx0 = (ringaddr & ~0xFUL) |
+ (ccs & 0x1);
+
+ devep->ep_sctx_trbs[streamid].ringaddr = ringaddr & ~0xFUL;
+ devep->ep_sctx_trbs[streamid].ccs = ccs & 0x1;
+ ep_ctx->qwEpCtx2 = (ep_ctx->qwEpCtx2 & ~0x1) | (ccs & 0x1);
+
+ DPRINTF(("xhci update ep-ring stream %d, addr %lx\r\n",
+ streamid, devep->ep_sctx[streamid].qwSctx0));
+ } else {
+ devep->ep_ringaddr = ringaddr & ~0xFUL;
+ devep->ep_ccs = ccs & 0x1;
+ devep->ep_tr = XHCI_GADDR(sc, ringaddr & ~0xFUL);
+ ep_ctx->qwEpCtx2 = (ringaddr & ~0xFUL) | (ccs & 0x1);
+
+ DPRINTF(("xhci update ep-ring, addr %lx\r\n",
+ (devep->ep_ringaddr | devep->ep_ccs)));
+ }
+}
+
+/*
+ * Outstanding transfer still in progress (device NAK'd earlier) so retry
+ * the transfer again to see if it succeeds.
+ */
+static int
+pci_xhci_try_usb_xfer(struct pci_xhci_softc *sc,
+ struct pci_xhci_dev_emu *dev, struct pci_xhci_dev_ep *devep,
+ struct xhci_endp_ctx *ep_ctx, uint32_t slot, uint32_t epid)
+{
+ struct usb_data_xfer *xfer;
+ int err;
+ int do_intr;
+
+ ep_ctx->dwEpCtx0 = FIELD_REPLACE(
+ ep_ctx->dwEpCtx0, XHCI_ST_EPCTX_RUNNING, 0x7, 0);
+
+ err = 0;
+ do_intr = 0;
+
+ xfer = devep->ep_xfer;
+ USB_DATA_XFER_LOCK(xfer);
+
+ /* outstanding requests queued up */
+ if (dev->dev_ue->ue_data != NULL) {
+ err = dev->dev_ue->ue_data(dev->dev_sc, xfer,
+ epid & 0x1 ? USB_XFER_IN : USB_XFER_OUT, epid/2);
+ if (err == USB_ERR_CANCELLED) {
+ if (USB_DATA_GET_ERRCODE(&xfer->data[xfer->head]) ==
+ USB_NAK)
+ err = XHCI_TRB_ERROR_SUCCESS;
+ } else {
+ err = pci_xhci_xfer_complete(sc, xfer, slot, epid,
+ &do_intr);
+ if (err == XHCI_TRB_ERROR_SUCCESS && do_intr) {
+ pci_xhci_assert_interrupt(sc);
+ }
+
+
+ /* XXX should not do it if error? */
+ USB_DATA_XFER_RESET(xfer);
+ }
+ }
+
+ USB_DATA_XFER_UNLOCK(xfer);
+
+
+ return (err);
+}
+
+
+static int
+pci_xhci_handle_transfer(struct pci_xhci_softc *sc,
+ struct pci_xhci_dev_emu *dev, struct pci_xhci_dev_ep *devep,
+ struct xhci_endp_ctx *ep_ctx, struct xhci_trb *trb, uint32_t slot,
+ uint32_t epid, uint64_t addr, uint32_t ccs, uint32_t streamid)
+{
+ struct xhci_trb *setup_trb;
+ struct usb_data_xfer *xfer;
+ struct usb_data_xfer_block *xfer_block;
+ uint64_t val;
+ uint32_t trbflags;
+ int do_intr, err;
+ int do_retry;
+
+ ep_ctx->dwEpCtx0 = FIELD_REPLACE(ep_ctx->dwEpCtx0,
+ XHCI_ST_EPCTX_RUNNING, 0x7, 0);
+
+ xfer = devep->ep_xfer;
+ USB_DATA_XFER_LOCK(xfer);
+
+ DPRINTF(("pci_xhci handle_transfer slot %u\r\n", slot));
+
+retry:
+ err = 0;
+ do_retry = 0;
+ do_intr = 0;
+ setup_trb = NULL;
+
+ while (1) {
+ pci_xhci_dump_trb(trb);
+
+ trbflags = trb->dwTrb3;
+
+ if (XHCI_TRB_3_TYPE_GET(trbflags) != XHCI_TRB_TYPE_LINK &&
+ (trbflags & XHCI_TRB_3_CYCLE_BIT) !=
+ (ccs & XHCI_TRB_3_CYCLE_BIT)) {
+ DPRINTF(("Cycle-bit changed trbflags %x, ccs %x\r\n",
+ trbflags & XHCI_TRB_3_CYCLE_BIT, ccs));
+ break;
+ }
+
+ xfer_block = NULL;
+
+ switch (XHCI_TRB_3_TYPE_GET(trbflags)) {
+ case XHCI_TRB_TYPE_LINK:
+ if (trb->dwTrb3 & XHCI_TRB_3_TC_BIT)
+ ccs ^= 0x1;
+
+ xfer_block = usb_data_xfer_append(xfer, NULL, 0,
+ (void *)addr, ccs);
+ xfer_block->processed = 1;
+ break;
+
+ case XHCI_TRB_TYPE_SETUP_STAGE:
+ if ((trbflags & XHCI_TRB_3_IDT_BIT) == 0 ||
+ XHCI_TRB_2_BYTES_GET(trb->dwTrb2) != 8) {
+ DPRINTF(("pci_xhci: invalid setup trb\r\n"));
+ err = XHCI_TRB_ERROR_TRB;
+ goto errout;
+ }
+ setup_trb = trb;
+
+ val = trb->qwTrb0;
+ if (!xfer->ureq)
+ xfer->ureq = malloc(
+ sizeof(struct usb_device_request));
+ memcpy(xfer->ureq, &val,
+ sizeof(struct usb_device_request));
+
+ xfer_block = usb_data_xfer_append(xfer, NULL, 0,
+ (void *)addr, ccs);
+ xfer_block->processed = 1;
+ break;
+
+ case XHCI_TRB_TYPE_NORMAL:
+ case XHCI_TRB_TYPE_ISOCH:
+ if (setup_trb != NULL) {
+ DPRINTF(("pci_xhci: trb not supposed to be in "
+ "ctl scope\r\n"));
+ err = XHCI_TRB_ERROR_TRB;
+ goto errout;
+ }
+ /* fall through */
+
+ case XHCI_TRB_TYPE_DATA_STAGE:
+ xfer_block = usb_data_xfer_append(xfer,
+ (void *)(trbflags & XHCI_TRB_3_IDT_BIT ?
+ &trb->qwTrb0 : XHCI_GADDR(sc, trb->qwTrb0)),
+ trb->dwTrb2 & 0x1FFFF, (void *)addr, ccs);
+ break;
+
+ case XHCI_TRB_TYPE_STATUS_STAGE:
+ xfer_block = usb_data_xfer_append(xfer, NULL, 0,
+ (void *)addr, ccs);
+ break;
+
+ case XHCI_TRB_TYPE_NOOP:
+ xfer_block = usb_data_xfer_append(xfer, NULL, 0,
+ (void *)addr, ccs);
+ xfer_block->processed = 1;
+ break;
+
+ case XHCI_TRB_TYPE_EVENT_DATA:
+ xfer_block = usb_data_xfer_append(xfer, NULL, 0,
+ (void *)addr, ccs);
+ if ((epid > 1) && (trbflags & XHCI_TRB_3_IOC_BIT)) {
+ xfer_block->processed = 1;
+ }
+ break;
+
+ default:
+ DPRINTF(("pci_xhci: handle xfer unexpected trb type "
+ "0x%x\r\n",
+ XHCI_TRB_3_TYPE_GET(trbflags)));
+ err = XHCI_TRB_ERROR_TRB;
+ goto errout;
+ }
+
+ trb = pci_xhci_trb_next(sc, trb, &addr);
+
+ DPRINTF(("pci_xhci: next trb: 0x%lx\r\n", (uint64_t)trb));
+
+ if (xfer_block) {
+ xfer_block->trbnext = addr;
+ xfer_block->streamid = streamid;
+ }
+
+ if (!setup_trb && !(trbflags & XHCI_TRB_3_CHAIN_BIT) &&
+ XHCI_TRB_3_TYPE_GET(trbflags) != XHCI_TRB_TYPE_LINK) {
+ break;
+ }
+
+ /* handle current batch that requires interrupt on complete */
+ if (trbflags & XHCI_TRB_3_IOC_BIT) {
+ DPRINTF(("pci_xhci: trb IOC bit set\r\n"));
+ if (epid == 1)
+ do_retry = 1;
+ break;
+ }
+ }
+
+ DPRINTF(("pci_xhci[%d]: xfer->ndata %u\r\n", __LINE__, xfer->ndata));
+
+ if (epid == 1) {
+ err = USB_ERR_NOT_STARTED;
+ if (dev->dev_ue->ue_request != NULL)
+ err = dev->dev_ue->ue_request(dev->dev_sc, xfer);
+ setup_trb = NULL;
+ } else {
+ /* handle data transfer */
+ pci_xhci_try_usb_xfer(sc, dev, devep, ep_ctx, slot, epid);
+ err = XHCI_TRB_ERROR_SUCCESS;
+ goto errout;
+ }
+
+ err = USB_TO_XHCI_ERR(err);
+ if ((err == XHCI_TRB_ERROR_SUCCESS) ||
+ (err == XHCI_TRB_ERROR_SHORT_PKT)) {
+ err = pci_xhci_xfer_complete(sc, xfer, slot, epid, &do_intr);
+ if (err != XHCI_TRB_ERROR_SUCCESS)
+ do_retry = 0;
+ }
+
+errout:
+ if (err == XHCI_TRB_ERROR_EV_RING_FULL)
+ DPRINTF(("pci_xhci[%d]: event ring full\r\n", __LINE__));
+
+ if (!do_retry)
+ USB_DATA_XFER_UNLOCK(xfer);
+
+ if (do_intr)
+ pci_xhci_assert_interrupt(sc);
+
+ if (do_retry) {
+ USB_DATA_XFER_RESET(xfer);
+ DPRINTF(("pci_xhci[%d]: retry:continuing with next TRBs\r\n",
+ __LINE__));
+ goto retry;
+ }
+
+ if (epid == 1)
+ USB_DATA_XFER_RESET(xfer);
+
+ return (err);
+}
+
+static void
+pci_xhci_device_doorbell(struct pci_xhci_softc *sc, uint32_t slot,
+ uint32_t epid, uint32_t streamid)
+{
+ struct pci_xhci_dev_emu *dev;
+ struct pci_xhci_dev_ep *devep;
+ struct xhci_dev_ctx *dev_ctx;
+ struct xhci_endp_ctx *ep_ctx;
+ struct pci_xhci_trb_ring *sctx_tr;
+ struct xhci_trb *trb;
+ uint64_t ringaddr;
+ uint32_t ccs;
+
+ DPRINTF(("pci_xhci doorbell slot %u epid %u stream %u\r\n",
+ slot, epid, streamid));
+
+ if (slot == 0 || slot > sc->ndevices) {
+ DPRINTF(("pci_xhci: invalid doorbell slot %u\r\n", slot));
+ return;
+ }
+
+ dev = XHCI_SLOTDEV_PTR(sc, slot);
+ devep = &dev->eps[epid];
+ dev_ctx = pci_xhci_get_dev_ctx(sc, slot);
+ if (!dev_ctx) {
+ return;
+ }
+ ep_ctx = &dev_ctx->ctx_ep[epid];
+
+ sctx_tr = NULL;
+
+ DPRINTF(("pci_xhci: device doorbell ep[%u] %08x %08x %016lx %08x\r\n",
+ epid, ep_ctx->dwEpCtx0, ep_ctx->dwEpCtx1, ep_ctx->qwEpCtx2,
+ ep_ctx->dwEpCtx4));
+
+ if (ep_ctx->qwEpCtx2 == 0)
+ return;
+
+ /* handle pending transfers */
+ if (devep->ep_xfer->ndata > 0) {
+ pci_xhci_try_usb_xfer(sc, dev, devep, ep_ctx, slot, epid);
+ return;
+ }
+
+ /* get next trb work item */
+ if (XHCI_EPCTX_0_MAXP_STREAMS_GET(ep_ctx->dwEpCtx0) != 0) {
+ sctx_tr = &devep->ep_sctx_trbs[streamid];
+ ringaddr = sctx_tr->ringaddr;
+ ccs = sctx_tr->ccs;
+ trb = XHCI_GADDR(sc, sctx_tr->ringaddr & ~0xFUL);
+ DPRINTF(("doorbell, stream %u, ccs %lx, trb ccs %x\r\n",
+ streamid, ep_ctx->qwEpCtx2 & XHCI_TRB_3_CYCLE_BIT,
+ trb->dwTrb3 & XHCI_TRB_3_CYCLE_BIT));
+ } else {
+ ringaddr = devep->ep_ringaddr;
+ ccs = devep->ep_ccs;
+ trb = devep->ep_tr;
+ DPRINTF(("doorbell, ccs %lx, trb ccs %x\r\n",
+ ep_ctx->qwEpCtx2 & XHCI_TRB_3_CYCLE_BIT,
+ trb->dwTrb3 & XHCI_TRB_3_CYCLE_BIT));
+ }
+
+ if (XHCI_TRB_3_TYPE_GET(trb->dwTrb3) == 0) {
+ DPRINTF(("pci_xhci: ring %lx trb[%lx] EP %u is RESERVED?\r\n",
+ ep_ctx->qwEpCtx2, devep->ep_ringaddr, epid));
+ return;
+ }
+
+ pci_xhci_handle_transfer(sc, dev, devep, ep_ctx, trb, slot, epid,
+ ringaddr, ccs, streamid);
+}
+
+static void
+pci_xhci_dbregs_write(struct pci_xhci_softc *sc, uint64_t offset,
+ uint64_t value)
+{
+
+ offset = (offset - sc->dboff) / sizeof(uint32_t);
+
+ DPRINTF(("pci_xhci: doorbell write offset 0x%lx: 0x%lx\r\n",
+ offset, value));
+
+ if (XHCI_HALTED(sc)) {
+ DPRINTF(("pci_xhci: controller halted\r\n"));
+ return;
+ }
+
+ if (offset == 0)
+ pci_xhci_complete_commands(sc);
+ else if (sc->portregs != NULL)
+ pci_xhci_device_doorbell(sc, offset,
+ XHCI_DB_TARGET_GET(value), XHCI_DB_SID_GET(value));
+}
+
+static void
+pci_xhci_rtsregs_write(struct pci_xhci_softc *sc, uint64_t offset,
+ uint64_t value)
+{
+ struct pci_xhci_rtsregs *rts;
+
+ offset -= sc->rtsoff;
+
+ if (offset == 0) {
+ DPRINTF(("pci_xhci attempted write to MFINDEX\r\n"));
+ return;
+ }
+
+ DPRINTF(("pci_xhci: runtime regs write offset 0x%lx: 0x%lx\r\n",
+ offset, value));
+
+ offset -= 0x20; /* start of intrreg */
+
+ rts = &sc->rtsregs;
+
+ switch (offset) {
+ case 0x00:
+ if (value & XHCI_IMAN_INTR_PEND)
+ rts->intrreg.iman &= ~XHCI_IMAN_INTR_PEND;
+ rts->intrreg.iman = (value & XHCI_IMAN_INTR_ENA) |
+ (rts->intrreg.iman & XHCI_IMAN_INTR_PEND);
+
+ if (!(value & XHCI_IMAN_INTR_ENA))
+ pci_xhci_deassert_interrupt(sc);
+
+ break;
+
+ case 0x04:
+ rts->intrreg.imod = value;
+ break;
+
+ case 0x08:
+ rts->intrreg.erstsz = value & 0xFFFF;
+ break;
+
+ case 0x10:
+ /* ERSTBA low bits */
+ rts->intrreg.erstba = MASK_64_HI(sc->rtsregs.intrreg.erstba) |
+ (value & ~0x3F);
+ break;
+
+ case 0x14:
+ /* ERSTBA high bits */
+ rts->intrreg.erstba = (value << 32) |
+ MASK_64_LO(sc->rtsregs.intrreg.erstba);
+
+ rts->erstba_p = XHCI_GADDR(sc,
+ sc->rtsregs.intrreg.erstba & ~0x3FUL);
+
+ rts->erst_p = XHCI_GADDR(sc,
+ sc->rtsregs.erstba_p->qwEvrsTablePtr & ~0x3FUL);
+
+ rts->er_enq_idx = 0;
+ rts->er_events_cnt = 0;
+
+ DPRINTF(("pci_xhci: wr erstba erst (%p) ptr 0x%lx, sz %u\r\n",
+ rts->erstba_p,
+ rts->erstba_p->qwEvrsTablePtr,
+ rts->erstba_p->dwEvrsTableSize));
+ break;
+
+ case 0x18:
+ /* ERDP low bits */
+ rts->intrreg.erdp =
+ MASK_64_HI(sc->rtsregs.intrreg.erdp) |
+ (rts->intrreg.erdp & XHCI_ERDP_LO_BUSY) |
+ (value & ~0xF);
+ if (value & XHCI_ERDP_LO_BUSY) {
+ rts->intrreg.erdp &= ~XHCI_ERDP_LO_BUSY;
+ rts->intrreg.iman &= ~XHCI_IMAN_INTR_PEND;
+ }
+
+ rts->er_deq_seg = XHCI_ERDP_LO_SINDEX(value);
+
+ break;
+
+ case 0x1C:
+ /* ERDP high bits */
+ rts->intrreg.erdp = (value << 32) |
+ MASK_64_LO(sc->rtsregs.intrreg.erdp);
+
+ if (rts->er_events_cnt > 0) {
+ uint64_t erdp;
+ uint32_t erdp_i;
+
+ erdp = rts->intrreg.erdp & ~0xF;
+ erdp_i = (erdp - rts->erstba_p->qwEvrsTablePtr) /
+ sizeof(struct xhci_trb);
+
+ if (erdp_i <= rts->er_enq_idx)
+ rts->er_events_cnt = rts->er_enq_idx - erdp_i;
+ else
+ rts->er_events_cnt =
+ rts->erstba_p->dwEvrsTableSize -
+ (erdp_i - rts->er_enq_idx);
+
+ DPRINTF(("pci_xhci: erdp 0x%lx, events cnt %u\r\n",
+ erdp, rts->er_events_cnt));
+ }
+
+ break;
+
+ default:
+ DPRINTF(("pci_xhci attempted write to RTS offset 0x%lx\r\n",
+ offset));
+ break;
+ }
+}
+
+static uint64_t
+pci_xhci_portregs_read(struct pci_xhci_softc *sc, uint64_t offset)
+{
+ int port;
+ uint32_t *p;
+
+ if (sc->portregs == NULL)
+ return (0);
+
+ port = (offset - 0x3F0) / 0x10;
+
+ if (port > XHCI_MAX_DEVS) {
+ DPRINTF(("pci_xhci: portregs_read port %d >= XHCI_MAX_DEVS\r\n",
+ port));
+
+ /* return default value for unused port */
+ return (XHCI_PS_SPEED_SET(3));
+ }
+
+ offset = (offset - 0x3F0) % 0x10;
+
+ p = &sc->portregs[port].portsc;
+ p += offset / sizeof(uint32_t);
+
+ DPRINTF(("pci_xhci: portregs read offset 0x%lx port %u -> 0x%x\r\n",
+ offset, port, *p));
+
+ return (*p);
+}
+
+static void
+pci_xhci_hostop_write(struct pci_xhci_softc *sc, uint64_t offset,
+ uint64_t value)
+{
+ offset -= XHCI_CAPLEN;
+
+ if (offset < 0x400)
+ DPRINTF(("pci_xhci: hostop write offset 0x%lx: 0x%lx\r\n",
+ offset, value));
+
+ switch (offset) {
+ case XHCI_USBCMD:
+ sc->opregs.usbcmd = pci_xhci_usbcmd_write(sc, value & 0x3F0F);
+ break;
+
+ case XHCI_USBSTS:
+ /* clear bits on write */
+ sc->opregs.usbsts &= ~(value &
+ (XHCI_STS_HSE|XHCI_STS_EINT|XHCI_STS_PCD|XHCI_STS_SSS|
+ XHCI_STS_RSS|XHCI_STS_SRE|XHCI_STS_CNR));
+ break;
+
+ case XHCI_PAGESIZE:
+ /* read only */
+ break;
+
+ case XHCI_DNCTRL:
+ sc->opregs.dnctrl = value & 0xFFFF;
+ break;
+
+ case XHCI_CRCR_LO:
+ if (sc->opregs.crcr & XHCI_CRCR_LO_CRR) {
+ sc->opregs.crcr &= ~(XHCI_CRCR_LO_CS|XHCI_CRCR_LO_CA);
+ sc->opregs.crcr |= value &
+ (XHCI_CRCR_LO_CS|XHCI_CRCR_LO_CA);
+ } else {
+ sc->opregs.crcr = MASK_64_HI(sc->opregs.crcr) |
+ (value & (0xFFFFFFC0 | XHCI_CRCR_LO_RCS));
+ }
+ break;
+
+ case XHCI_CRCR_HI:
+ if (!(sc->opregs.crcr & XHCI_CRCR_LO_CRR)) {
+ sc->opregs.crcr = MASK_64_LO(sc->opregs.crcr) |
+ (value << 32);
+
+ sc->opregs.cr_p = XHCI_GADDR(sc,
+ sc->opregs.crcr & ~0xF);
+ }
+
+ if (sc->opregs.crcr & XHCI_CRCR_LO_CS) {
+ /* Stop operation of Command Ring */
+ }
+
+ if (sc->opregs.crcr & XHCI_CRCR_LO_CA) {
+ /* Abort command */
+ }
+
+ break;
+
+ case XHCI_DCBAAP_LO:
+ sc->opregs.dcbaap = MASK_64_HI(sc->opregs.dcbaap) |
+ (value & 0xFFFFFFC0);
+ break;
+
+ case XHCI_DCBAAP_HI:
+ sc->opregs.dcbaap = MASK_64_LO(sc->opregs.dcbaap) |
+ (value << 32);
+ sc->opregs.dcbaa_p = XHCI_GADDR(sc, sc->opregs.dcbaap & ~0x3FUL);
+
+ DPRINTF(("pci_xhci: opregs dcbaap = 0x%lx (vaddr 0x%lx)\r\n",
+ sc->opregs.dcbaap, (uint64_t)sc->opregs.dcbaa_p));
+ break;
+
+ case XHCI_CONFIG:
+ sc->opregs.config = value & 0x03FF;
+ break;
+
+ default:
+ if (offset >= 0x400)
+ pci_xhci_portregs_write(sc, offset, value);
+
+ break;
+ }
+}
+
+
+static void
+pci_xhci_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
+ int baridx, uint64_t offset, int size, uint64_t value)
+{
+ struct pci_xhci_softc *sc;
+
+ sc = pi->pi_arg;
+
+ assert(baridx == 0);
+
+
+ pthread_mutex_lock(&sc->mtx);
+ if (offset < XHCI_CAPLEN) /* read only registers */
+ WPRINTF(("pci_xhci: write RO-CAPs offset %ld\r\n", offset));
+ else if (offset < sc->dboff)
+ pci_xhci_hostop_write(sc, offset, value);
+ else if (offset < sc->rtsoff)
+ pci_xhci_dbregs_write(sc, offset, value);
+ else if (offset < sc->regsend)
+ pci_xhci_rtsregs_write(sc, offset, value);
+ else
+ WPRINTF(("pci_xhci: write invalid offset %ld\r\n", offset));
+
+ pthread_mutex_unlock(&sc->mtx);
+}
+
+static uint64_t
+pci_xhci_hostcap_read(struct pci_xhci_softc *sc, uint64_t offset)
+{
+ uint64_t value;
+
+ switch (offset) {
+ case XHCI_CAPLENGTH: /* 0x00 */
+ value = sc->caplength;
+ break;
+
+ case XHCI_HCSPARAMS1: /* 0x04 */
+ value = sc->hcsparams1;
+ break;
+
+ case XHCI_HCSPARAMS2: /* 0x08 */
+ value = sc->hcsparams2;
+ break;
+
+ case XHCI_HCSPARAMS3: /* 0x0C */
+ value = sc->hcsparams3;
+ break;
+
+ case XHCI_HCSPARAMS0: /* 0x10 */
+ value = sc->hccparams1;
+ break;
+
+ case XHCI_DBOFF: /* 0x14 */
+ value = sc->dboff;
+ break;
+
+ case XHCI_RTSOFF: /* 0x18 */
+ value = sc->rtsoff;
+ break;
+
+ case XHCI_HCCPRAMS2: /* 0x1C */
+ value = sc->hccparams2;
+ break;
+
+ default:
+ value = 0;
+ break;
+ }
+
+ DPRINTF(("pci_xhci: hostcap read offset 0x%lx -> 0x%lx\r\n",
+ offset, value));
+
+ return (value);
+}
+
+static uint64_t
+pci_xhci_hostop_read(struct pci_xhci_softc *sc, uint64_t offset)
+{
+ uint64_t value;
+
+ offset = (offset - XHCI_CAPLEN);
+
+ switch (offset) {
+ case XHCI_USBCMD: /* 0x00 */
+ value = sc->opregs.usbcmd;
+ break;
+
+ case XHCI_USBSTS: /* 0x04 */
+ value = sc->opregs.usbsts;
+ break;
+
+ case XHCI_PAGESIZE: /* 0x08 */
+ value = sc->opregs.pgsz;
+ break;
+
+ case XHCI_DNCTRL: /* 0x14 */
+ value = sc->opregs.dnctrl;
+ break;
+
+ case XHCI_CRCR_LO: /* 0x18 */
+ value = sc->opregs.crcr & XHCI_CRCR_LO_CRR;
+ break;
+
+ case XHCI_CRCR_HI: /* 0x1C */
+ value = 0;
+ break;
+
+ case XHCI_DCBAAP_LO: /* 0x30 */
+ value = sc->opregs.dcbaap & 0xFFFFFFFF;
+ break;
+
+ case XHCI_DCBAAP_HI: /* 0x34 */
+ value = (sc->opregs.dcbaap >> 32) & 0xFFFFFFFF;
+ break;
+
+ case XHCI_CONFIG: /* 0x38 */
+ value = sc->opregs.config;
+ break;
+
+ default:
+ if (offset >= 0x400)
+ value = pci_xhci_portregs_read(sc, offset);
+ else
+ value = 0;
+
+ break;
+ }
+
+ if (offset < 0x400)
+ DPRINTF(("pci_xhci: hostop read offset 0x%lx -> 0x%lx\r\n",
+ offset, value));
+
+ return (value);
+}
+
+static uint64_t
+pci_xhci_dbregs_read(struct pci_xhci_softc *sc, uint64_t offset)
+{
+
+ /* read doorbell always returns 0 */
+ return (0);
+}
+
+static uint64_t
+pci_xhci_rtsregs_read(struct pci_xhci_softc *sc, uint64_t offset)
+{
+ uint32_t value;
+
+ offset -= sc->rtsoff;
+ value = 0;
+
+ if (offset == XHCI_MFINDEX) {
+ value = sc->rtsregs.mfindex;
+ } else if (offset >= 0x20) {
+ int item;
+ uint32_t *p;
+
+ offset -= 0x20;
+ item = offset % 32;
+
+ assert(offset < sizeof(sc->rtsregs.intrreg));
+
+ p = &sc->rtsregs.intrreg.iman;
+ p += item / sizeof(uint32_t);
+ value = *p;
+ }
+
+ DPRINTF(("pci_xhci: rtsregs read offset 0x%lx -> 0x%x\r\n",
+ offset, value));
+
+ return (value);
+}
+
+static uint64_t
+pci_xhci_xecp_read(struct pci_xhci_softc *sc, uint64_t offset)
+{
+ uint32_t value;
+
+ offset -= sc->regsend;
+ value = 0;
+
+ switch (offset) {
+ case 0:
+ /* rev major | rev minor | next-cap | cap-id */
+ value = (0x02 << 24) | (4 << 8) | XHCI_ID_PROTOCOLS;
+ break;
+ case 4:
+ /* name string = "USB" */
+ value = 0x20425355;
+ break;
+ case 8:
+ /* psic | proto-defined | compat # | compat offset */
+ value = ((XHCI_MAX_DEVS/2) << 8) | sc->usb2_port_start;
+ break;
+ case 12:
+ break;
+ case 16:
+ /* rev major | rev minor | next-cap | cap-id */
+ value = (0x03 << 24) | XHCI_ID_PROTOCOLS;
+ break;
+ case 20:
+ /* name string = "USB" */
+ value = 0x20425355;
+ break;
+ case 24:
+ /* psic | proto-defined | compat # | compat offset */
+ value = ((XHCI_MAX_DEVS/2) << 8) | sc->usb3_port_start;
+ break;
+ case 28:
+ break;
+ default:
+ DPRINTF(("pci_xhci: xecp invalid offset 0x%lx\r\n", offset));
+ break;
+ }
+
+ DPRINTF(("pci_xhci: xecp read offset 0x%lx -> 0x%x\r\n",
+ offset, value));
+
+ return (value);
+}
+
+
+static uint64_t
+pci_xhci_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
+ uint64_t offset, int size)
+{
+ struct pci_xhci_softc *sc;
+ uint32_t value;
+
+ sc = pi->pi_arg;
+
+ assert(baridx == 0);
+
+ pthread_mutex_lock(&sc->mtx);
+ if (offset < XHCI_CAPLEN)
+ value = pci_xhci_hostcap_read(sc, offset);
+ else if (offset < sc->dboff)
+ value = pci_xhci_hostop_read(sc, offset);
+ else if (offset < sc->rtsoff)
+ value = pci_xhci_dbregs_read(sc, offset);
+ else if (offset < sc->regsend)
+ value = pci_xhci_rtsregs_read(sc, offset);
+ else if (offset < (sc->regsend + 4*32))
+ value = pci_xhci_xecp_read(sc, offset);
+ else {
+ value = 0;
+ WPRINTF(("pci_xhci: read invalid offset %ld\r\n", offset));
+ }
+
+ pthread_mutex_unlock(&sc->mtx);
+
+ switch (size) {
+ case 1:
+ value &= 0xFF;
+ break;
+ case 2:
+ value &= 0xFFFF;
+ break;
+ case 4:
+ value &= 0xFFFFFFFF;
+ break;
+ }
+
+ return (value);
+}
+
+static void
+pci_xhci_reset_port(struct pci_xhci_softc *sc, int portn, int warm)
+{
+ struct pci_xhci_portregs *port;
+ struct pci_xhci_dev_emu *dev;
+ struct xhci_trb evtrb;
+ int error;
+
+ assert(portn <= XHCI_MAX_DEVS);
+
+ DPRINTF(("xhci reset port %d\r\n", portn));
+
+ port = XHCI_PORTREG_PTR(sc, portn);
+ dev = XHCI_DEVINST_PTR(sc, portn);
+ if (dev) {
+ port->portsc &= ~(XHCI_PS_PLS_MASK | XHCI_PS_PR | XHCI_PS_PRC);
+ port->portsc |= XHCI_PS_PED |
+ XHCI_PS_SPEED_SET(dev->dev_ue->ue_usbspeed);
+
+ if (warm && dev->dev_ue->ue_usbver == 3) {
+ port->portsc |= XHCI_PS_WRC;
+ }
+
+ if ((port->portsc & XHCI_PS_PRC) == 0) {
+ port->portsc |= XHCI_PS_PRC;
+
+ pci_xhci_set_evtrb(&evtrb, portn,
+ XHCI_TRB_ERROR_SUCCESS,
+ XHCI_TRB_EVENT_PORT_STS_CHANGE);
+ error = pci_xhci_insert_event(sc, &evtrb, 1);
+ if (error != XHCI_TRB_ERROR_SUCCESS)
+ DPRINTF(("xhci reset port insert event "
+ "failed\r\n"));
+ }
+ }
+}
+
+static void
+pci_xhci_init_port(struct pci_xhci_softc *sc, int portn)
+{
+ struct pci_xhci_portregs *port;
+ struct pci_xhci_dev_emu *dev;
+
+ port = XHCI_PORTREG_PTR(sc, portn);
+ dev = XHCI_DEVINST_PTR(sc, portn);
+ if (dev) {
+ port->portsc = XHCI_PS_CCS | /* connected */
+ XHCI_PS_PP; /* port power */
+
+ if (dev->dev_ue->ue_usbver == 2) {
+ port->portsc |= XHCI_PS_PLS_SET(UPS_PORT_LS_POLL) |
+ XHCI_PS_SPEED_SET(dev->dev_ue->ue_usbspeed);
+ } else {
+ port->portsc |= XHCI_PS_PLS_SET(UPS_PORT_LS_U0) |
+ XHCI_PS_PED | /* enabled */
+ XHCI_PS_SPEED_SET(dev->dev_ue->ue_usbspeed);
+ }
+
+ DPRINTF(("Init port %d 0x%x\n", portn, port->portsc));
+ } else {
+ port->portsc = XHCI_PS_PLS_SET(UPS_PORT_LS_RX_DET) | XHCI_PS_PP;
+ DPRINTF(("Init empty port %d 0x%x\n", portn, port->portsc));
+ }
+}
+
+static int
+pci_xhci_dev_intr(struct usb_hci *hci, int epctx)
+{
+ struct pci_xhci_dev_emu *dev;
+ struct xhci_dev_ctx *dev_ctx;
+ struct xhci_trb evtrb;
+ struct pci_xhci_softc *sc;
+ struct pci_xhci_portregs *p;
+ struct xhci_endp_ctx *ep_ctx;
+ int error;
+ int dir_in;
+ int epid;
+
+ dir_in = epctx & 0x80;
+ epid = epctx & ~0x80;
+
+ /* HW endpoint contexts are 0-15; convert to epid based on dir */
+ epid = (epid * 2) + (dir_in ? 1 : 0);
+
+ assert(epid >= 1 && epid <= 31);
+
+ dev = hci->hci_sc;
+ sc = dev->xsc;
+
+ /* check if device is ready; OS has to initialise it */
+ if (sc->rtsregs.erstba_p == NULL ||
+ (sc->opregs.usbcmd & XHCI_CMD_RS) == 0 ||
+ dev->dev_ctx == NULL)
+ return (0);
+
+ p = XHCI_PORTREG_PTR(sc, hci->hci_port);
+
+ /* raise event if link U3 (suspended) state */
+ if (XHCI_PS_PLS_GET(p->portsc) == 3) {
+ p->portsc &= ~XHCI_PS_PLS_MASK;
+ p->portsc |= XHCI_PS_PLS_SET(UPS_PORT_LS_RESUME);
+ if ((p->portsc & XHCI_PS_PLC) != 0)
+ return (0);
+
+ p->portsc |= XHCI_PS_PLC;
+
+ pci_xhci_set_evtrb(&evtrb, hci->hci_port,
+ XHCI_TRB_ERROR_SUCCESS, XHCI_TRB_EVENT_PORT_STS_CHANGE);
+ error = pci_xhci_insert_event(sc, &evtrb, 0);
+ if (error != XHCI_TRB_ERROR_SUCCESS)
+ goto done;
+ }
+
+ dev_ctx = dev->dev_ctx;
+ ep_ctx = &dev_ctx->ctx_ep[epid];
+ if ((ep_ctx->dwEpCtx0 & 0x7) == XHCI_ST_EPCTX_DISABLED) {
+ DPRINTF(("xhci device interrupt on disabled endpoint %d\r\n",
+ epid));
+ return (0);
+ }
+
+ DPRINTF(("xhci device interrupt on endpoint %d\r\n", epid));
+
+ pci_xhci_device_doorbell(sc, hci->hci_port, epid, 0);
+
+done:
+ return (error);
+}
+
+static int
+pci_xhci_dev_event(struct usb_hci *hci, enum hci_usbev evid, void *param)
+{
+
+ DPRINTF(("xhci device event port %d\r\n", hci->hci_port));
+ return (0);
+}
+
+
+
+static void
+pci_xhci_device_usage(char *opt)
+{
+
+ fprintf(stderr, "Invalid USB emulation \"%s\"\r\n", opt);
+}
+
+static int
+pci_xhci_parse_opts(struct pci_xhci_softc *sc, char *opts)
+{
+ struct pci_xhci_dev_emu **devices;
+ struct pci_xhci_dev_emu *dev;
+ struct usb_devemu *ue;
+ void *devsc;
+ char *uopt, *xopts, *config;
+ int usb3_port, usb2_port, i;
+
+ usb3_port = sc->usb3_port_start - 1;
+ usb2_port = sc->usb2_port_start - 1;
+ devices = NULL;
+
+ if (opts == NULL)
+ goto portsfinal;
+
+ devices = calloc(XHCI_MAX_DEVS, sizeof(struct pci_xhci_dev_emu *));
+
+ sc->slots = calloc(XHCI_MAX_SLOTS, sizeof(struct pci_xhci_dev_emu *));
+ sc->devices = devices;
+ sc->ndevices = 0;
+
+ uopt = strdup(opts);
+ for (xopts = strtok(uopt, ",");
+ xopts != NULL;
+ xopts = strtok(NULL, ",")) {
+ if (usb2_port == ((sc->usb2_port_start-1) + XHCI_MAX_DEVS/2) ||
+ usb3_port == ((sc->usb3_port_start-1) + XHCI_MAX_DEVS/2)) {
+ WPRINTF(("pci_xhci max number of USB 2 or 3 "
+ "devices reached, max %d\r\n", XHCI_MAX_DEVS/2));
+ usb2_port = usb3_port = -1;
+ goto done;
+ }
+
+ /* device[=<config>] */
+ if ((config = strchr(xopts, '=')) == NULL)
+ config = ""; /* no config */
+ else
+ *config++ = '\0';
+
+ ue = usb_emu_finddev(xopts);
+ if (ue == NULL) {
+ pci_xhci_device_usage(xopts);
+ DPRINTF(("pci_xhci device not found %s\r\n", xopts));
+ usb2_port = usb3_port = -1;
+ goto done;
+ }
+
+ DPRINTF(("pci_xhci adding device %s, opts \"%s\"\r\n",
+ xopts, config));
+
+ dev = calloc(1, sizeof(struct pci_xhci_dev_emu));
+ dev->xsc = sc;
+ dev->hci.hci_sc = dev;
+ dev->hci.hci_intr = pci_xhci_dev_intr;
+ dev->hci.hci_event = pci_xhci_dev_event;
+
+ if (ue->ue_usbver == 2) {
+ dev->hci.hci_port = usb2_port + 1;
+ devices[usb2_port] = dev;
+ usb2_port++;
+ } else {
+ dev->hci.hci_port = usb3_port + 1;
+ devices[usb3_port] = dev;
+ usb3_port++;
+ }
+
+ dev->hci.hci_address = 0;
+ devsc = ue->ue_init(&dev->hci, config);
+ if (devsc == NULL) {
+ pci_xhci_device_usage(xopts);
+ usb2_port = usb3_port = -1;
+ goto done;
+ }
+
+ dev->dev_ue = ue;
+ dev->dev_sc = devsc;
+
+ /* assign slot number to device */
+ sc->slots[sc->ndevices] = dev;
+
+ sc->ndevices++;
+ }
+
+portsfinal:
+ sc->portregs = calloc(XHCI_MAX_DEVS, sizeof(struct pci_xhci_portregs));
+
+ if (sc->ndevices > 0) {
+ /* port and slot numbering start from 1 */
+ sc->devices--;
+ sc->portregs--;
+ sc->slots--;
+
+ for (i = 1; i <= XHCI_MAX_DEVS; i++) {
+ pci_xhci_init_port(sc, i);
+ }
+ } else {
+ WPRINTF(("pci_xhci no USB devices configured\r\n"));
+ sc->ndevices = 1;
+ }
+
+done:
+ if (devices != NULL) {
+ if (usb2_port <= 0 && usb3_port <= 0) {
+ sc->devices = NULL;
+ for (i = 0; devices[i] != NULL; i++)
+ free(devices[i]);
+ sc->ndevices = -1;
+
+ free(devices);
+ }
+ }
+ return (sc->ndevices);
+}
+
+static int
+pci_xhci_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
+{
+ struct pci_xhci_softc *sc;
+ int error;
+
+ if (xhci_in_use) {
+ WPRINTF(("pci_xhci controller already defined\r\n"));
+ return (-1);
+ }
+ xhci_in_use = 1;
+
+ sc = calloc(1, sizeof(struct pci_xhci_softc));
+ pi->pi_arg = sc;
+ sc->xsc_pi = pi;
+
+ sc->usb2_port_start = (XHCI_MAX_DEVS/2) + 1;
+ sc->usb3_port_start = 1;
+
+ /* discover devices */
+ error = pci_xhci_parse_opts(sc, opts);
+ if (error < 0)
+ goto done;
+ else
+ error = 0;
+
+ sc->caplength = XHCI_SET_CAPLEN(XHCI_CAPLEN) |
+ XHCI_SET_HCIVERSION(0x0100);
+ sc->hcsparams1 = XHCI_SET_HCSP1_MAXPORTS(XHCI_MAX_DEVS) |
+ XHCI_SET_HCSP1_MAXINTR(1) | /* interrupters */
+ XHCI_SET_HCSP1_MAXSLOTS(XHCI_MAX_SLOTS);
+ sc->hcsparams2 = XHCI_SET_HCSP2_ERSTMAX(XHCI_ERST_MAX) |
+ XHCI_SET_HCSP2_IST(0x04);
+ sc->hcsparams3 = 0; /* no latency */
+ sc->hccparams1 = XHCI_SET_HCCP1_NSS(1) | /* no 2nd-streams */
+ XHCI_SET_HCCP1_SPC(1) | /* short packet */
+ XHCI_SET_HCCP1_MAXPSA(XHCI_STREAMS_MAX);
+ sc->hccparams2 = XHCI_SET_HCCP2_LEC(1) |
+ XHCI_SET_HCCP2_U3C(1);
+ sc->dboff = XHCI_SET_DOORBELL(XHCI_CAPLEN + XHCI_PORTREGS_START +
+ XHCI_MAX_DEVS * sizeof(struct pci_xhci_portregs));
+
+ /* dboff must be 32-bit aligned */
+ if (sc->dboff & 0x3)
+ sc->dboff = (sc->dboff + 0x3) & ~0x3;
+
+ /* rtsoff must be 32-bytes aligned */
+ sc->rtsoff = XHCI_SET_RTSOFFSET(sc->dboff + (XHCI_MAX_SLOTS+1) * 32);
+ if (sc->rtsoff & 0x1F)
+ sc->rtsoff = (sc->rtsoff + 0x1F) & ~0x1F;
+
+ DPRINTF(("pci_xhci dboff: 0x%x, rtsoff: 0x%x\r\n", sc->dboff,
+ sc->rtsoff));
+
+ sc->opregs.usbsts = XHCI_STS_HCH;
+ sc->opregs.pgsz = XHCI_PAGESIZE_4K;
+
+ pci_xhci_reset(sc);
+
+ sc->regsend = sc->rtsoff + 0x20 + 32; /* only 1 intrpter */
+
+ /*
+ * Set extended capabilities pointer to be after regsend;
+ * value of xecp field is 32-bit offset.
+ */
+ sc->hccparams1 |= XHCI_SET_HCCP1_XECP(sc->regsend/4);
+
+ pci_set_cfgdata16(pi, PCIR_DEVICE, 0x1E31);
+ pci_set_cfgdata16(pi, PCIR_VENDOR, 0x8086);
+ pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_SERIALBUS);
+ pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_SERIALBUS_USB);
+ pci_set_cfgdata8(pi, PCIR_PROGIF,PCIP_SERIALBUS_USB_XHCI);
+ pci_set_cfgdata8(pi, PCI_USBREV, PCI_USB_REV_3_0);
+
+ pci_emul_add_msicap(pi, 1);
+
+ /* regsend + xecp registers */
+ pci_emul_alloc_bar(pi, 0, PCIBAR_MEM32, sc->regsend + 4*32);
+ DPRINTF(("pci_xhci pci_emu_alloc: %d\r\n", sc->regsend + 4*32));
+
+
+ pci_lintr_request(pi);
+
+ pthread_mutex_init(&sc->mtx, NULL);
+
+done:
+ if (error) {
+ free(sc);
+ }
+
+ return (error);
+}
+
+
+
+struct pci_devemu pci_de_xhci = {
+ .pe_emu = "xhci",
+ .pe_init = pci_xhci_init,
+ .pe_barwrite = pci_xhci_write,
+ .pe_barread = pci_xhci_read
+};
+PCI_EMUL_SET(pci_de_xhci);
diff --git a/usr/src/cmd/bhyve/pci_xhci.h b/usr/src/cmd/bhyve/pci_xhci.h
new file mode 100644
index 0000000000..d5f05af5d0
--- /dev/null
+++ b/usr/src/cmd/bhyve/pci_xhci.h
@@ -0,0 +1,353 @@
+/*-
+ * Copyright (c) 2014 Leon Dang <ldang@nahannisys.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _PCI_XHCI_H_
+#define _PCI_XHCI_H_
+
+#define PCI_USBREV 0x60 /* USB protocol revision */
+
+
+enum { /* dsc_slotstate */
+ XHCI_ST_DISABLED,
+ XHCI_ST_ENABLED,
+ XHCI_ST_DEFAULT,
+ XHCI_ST_ADDRESSED,
+ XHCI_ST_CONFIGURED,
+ XHCI_ST_MAX
+};
+
+enum {
+ XHCI_ST_SLCTX_DISABLED,
+ XHCI_ST_SLCTX_DEFAULT,
+ XHCI_ST_SLCTX_ADDRESSED,
+ XHCI_ST_SLCTX_CONFIGURED
+};
+
+enum {
+ XHCI_ST_EPCTX_DISABLED,
+ XHCI_ST_EPCTX_RUNNING,
+ XHCI_ST_EPCTX_HALTED,
+ XHCI_ST_EPCTX_STOPPED,
+ XHCI_ST_EPCTX_ERROR
+};
+
+#define XHCI_MAX_DEVICES MIN(USB_MAX_DEVICES, 128)
+#define XHCI_MAX_ENDPOINTS 32 /* hardcoded - do not change */
+#define XHCI_MAX_SCRATCHPADS 32
+#define XHCI_MAX_EVENTS (16 * 13)
+#define XHCI_MAX_COMMANDS (16 * 1)
+#define XHCI_MAX_RSEG 1
+#define XHCI_MAX_TRANSFERS 4
+#if USB_MAX_EP_STREAMS == 8
+#define XHCI_MAX_STREAMS 8
+#define XHCI_MAX_STREAMS_LOG 3
+#elif USB_MAX_EP_STREAMS == 1
+#define XHCI_MAX_STREAMS 1
+#define XHCI_MAX_STREAMS_LOG 0
+#else
+#error "The USB_MAX_EP_STREAMS value is not supported."
+#endif
+#define XHCI_DEV_CTX_ADDR_ALIGN 64 /* bytes */
+#define XHCI_DEV_CTX_ALIGN 64 /* bytes */
+#define XHCI_INPUT_CTX_ALIGN 64 /* bytes */
+#define XHCI_SLOT_CTX_ALIGN 32 /* bytes */
+#define XHCI_ENDP_CTX_ALIGN 32 /* bytes */
+#define XHCI_STREAM_CTX_ALIGN 16 /* bytes */
+#define XHCI_TRANS_RING_SEG_ALIGN 16 /* bytes */
+#define XHCI_CMD_RING_SEG_ALIGN 64 /* bytes */
+#define XHCI_EVENT_RING_SEG_ALIGN 64 /* bytes */
+#define XHCI_SCRATCH_BUF_ARRAY_ALIGN 64 /* bytes */
+#define XHCI_SCRATCH_BUFFER_ALIGN USB_PAGE_SIZE
+#define XHCI_TRB_ALIGN 16 /* bytes */
+#define XHCI_TD_ALIGN 64 /* bytes */
+#define XHCI_PAGE_SIZE 4096 /* bytes */
+
+struct xhci_slot_ctx {
+ volatile uint32_t dwSctx0;
+#define XHCI_SCTX_0_ROUTE_SET(x) ((x) & 0xFFFFF)
+#define XHCI_SCTX_0_ROUTE_GET(x) ((x) & 0xFFFFF)
+#define XHCI_SCTX_0_SPEED_SET(x) (((x) & 0xF) << 20)
+#define XHCI_SCTX_0_SPEED_GET(x) (((x) >> 20) & 0xF)
+#define XHCI_SCTX_0_MTT_SET(x) (((x) & 0x1) << 25)
+#define XHCI_SCTX_0_MTT_GET(x) (((x) >> 25) & 0x1)
+#define XHCI_SCTX_0_HUB_SET(x) (((x) & 0x1) << 26)
+#define XHCI_SCTX_0_HUB_GET(x) (((x) >> 26) & 0x1)
+#define XHCI_SCTX_0_CTX_NUM_SET(x) (((x) & 0x1F) << 27)
+#define XHCI_SCTX_0_CTX_NUM_GET(x) (((x) >> 27) & 0x1F)
+ volatile uint32_t dwSctx1;
+#define XHCI_SCTX_1_MAX_EL_SET(x) ((x) & 0xFFFF)
+#define XHCI_SCTX_1_MAX_EL_GET(x) ((x) & 0xFFFF)
+#define XHCI_SCTX_1_RH_PORT_SET(x) (((x) & 0xFF) << 16)
+#define XHCI_SCTX_1_RH_PORT_GET(x) (((x) >> 16) & 0xFF)
+#define XHCI_SCTX_1_NUM_PORTS_SET(x) (((x) & 0xFF) << 24)
+#define XHCI_SCTX_1_NUM_PORTS_GET(x) (((x) >> 24) & 0xFF)
+ volatile uint32_t dwSctx2;
+#define XHCI_SCTX_2_TT_HUB_SID_SET(x) ((x) & 0xFF)
+#define XHCI_SCTX_2_TT_HUB_SID_GET(x) ((x) & 0xFF)
+#define XHCI_SCTX_2_TT_PORT_NUM_SET(x) (((x) & 0xFF) << 8)
+#define XHCI_SCTX_2_TT_PORT_NUM_GET(x) (((x) >> 8) & 0xFF)
+#define XHCI_SCTX_2_TT_THINK_TIME_SET(x) (((x) & 0x3) << 16)
+#define XHCI_SCTX_2_TT_THINK_TIME_GET(x) (((x) >> 16) & 0x3)
+#define XHCI_SCTX_2_IRQ_TARGET_SET(x) (((x) & 0x3FF) << 22)
+#define XHCI_SCTX_2_IRQ_TARGET_GET(x) (((x) >> 22) & 0x3FF)
+ volatile uint32_t dwSctx3;
+#define XHCI_SCTX_3_DEV_ADDR_SET(x) ((x) & 0xFF)
+#define XHCI_SCTX_3_DEV_ADDR_GET(x) ((x) & 0xFF)
+#define XHCI_SCTX_3_SLOT_STATE_SET(x) (((x) & 0x1F) << 27)
+#define XHCI_SCTX_3_SLOT_STATE_GET(x) (((x) >> 27) & 0x1F)
+ volatile uint32_t dwSctx4;
+ volatile uint32_t dwSctx5;
+ volatile uint32_t dwSctx6;
+ volatile uint32_t dwSctx7;
+};
+
+struct xhci_endp_ctx {
+ volatile uint32_t dwEpCtx0;
+#define XHCI_EPCTX_0_EPSTATE_SET(x) ((x) & 0x7)
+#define XHCI_EPCTX_0_EPSTATE_GET(x) ((x) & 0x7)
+#define XHCI_EPCTX_0_MULT_SET(x) (((x) & 0x3) << 8)
+#define XHCI_EPCTX_0_MULT_GET(x) (((x) >> 8) & 0x3)
+#define XHCI_EPCTX_0_MAXP_STREAMS_SET(x) (((x) & 0x1F) << 10)
+#define XHCI_EPCTX_0_MAXP_STREAMS_GET(x) (((x) >> 10) & 0x1F)
+#define XHCI_EPCTX_0_LSA_SET(x) (((x) & 0x1) << 15)
+#define XHCI_EPCTX_0_LSA_GET(x) (((x) >> 15) & 0x1)
+#define XHCI_EPCTX_0_IVAL_SET(x) (((x) & 0xFF) << 16)
+#define XHCI_EPCTX_0_IVAL_GET(x) (((x) >> 16) & 0xFF)
+ volatile uint32_t dwEpCtx1;
+#define XHCI_EPCTX_1_CERR_SET(x) (((x) & 0x3) << 1)
+#define XHCI_EPCTX_1_CERR_GET(x) (((x) >> 1) & 0x3)
+#define XHCI_EPCTX_1_EPTYPE_SET(x) (((x) & 0x7) << 3)
+#define XHCI_EPCTX_1_EPTYPE_GET(x) (((x) >> 3) & 0x7)
+#define XHCI_EPCTX_1_HID_SET(x) (((x) & 0x1) << 7)
+#define XHCI_EPCTX_1_HID_GET(x) (((x) >> 7) & 0x1)
+#define XHCI_EPCTX_1_MAXB_SET(x) (((x) & 0xFF) << 8)
+#define XHCI_EPCTX_1_MAXB_GET(x) (((x) >> 8) & 0xFF)
+#define XHCI_EPCTX_1_MAXP_SIZE_SET(x) (((x) & 0xFFFF) << 16)
+#define XHCI_EPCTX_1_MAXP_SIZE_GET(x) (((x) >> 16) & 0xFFFF)
+ volatile uint64_t qwEpCtx2;
+#define XHCI_EPCTX_2_DCS_SET(x) ((x) & 0x1)
+#define XHCI_EPCTX_2_DCS_GET(x) ((x) & 0x1)
+#define XHCI_EPCTX_2_TR_DQ_PTR_MASK 0xFFFFFFFFFFFFFFF0U
+ volatile uint32_t dwEpCtx4;
+#define XHCI_EPCTX_4_AVG_TRB_LEN_SET(x) ((x) & 0xFFFF)
+#define XHCI_EPCTX_4_AVG_TRB_LEN_GET(x) ((x) & 0xFFFF)
+#define XHCI_EPCTX_4_MAX_ESIT_PAYLOAD_SET(x) (((x) & 0xFFFF) << 16)
+#define XHCI_EPCTX_4_MAX_ESIT_PAYLOAD_GET(x) (((x) >> 16) & 0xFFFF)
+ volatile uint32_t dwEpCtx5;
+ volatile uint32_t dwEpCtx6;
+ volatile uint32_t dwEpCtx7;
+};
+
+struct xhci_input_ctx {
+#define XHCI_INCTX_NON_CTRL_MASK 0xFFFFFFFCU
+ volatile uint32_t dwInCtx0;
+#define XHCI_INCTX_0_DROP_MASK(n) (1U << (n))
+ volatile uint32_t dwInCtx1;
+#define XHCI_INCTX_1_ADD_MASK(n) (1U << (n))
+ volatile uint32_t dwInCtx2;
+ volatile uint32_t dwInCtx3;
+ volatile uint32_t dwInCtx4;
+ volatile uint32_t dwInCtx5;
+ volatile uint32_t dwInCtx6;
+ volatile uint32_t dwInCtx7;
+};
+
+struct xhci_input_dev_ctx {
+ struct xhci_input_ctx ctx_input;
+ union {
+ struct xhci_slot_ctx u_slot;
+ struct xhci_endp_ctx u_ep[XHCI_MAX_ENDPOINTS];
+ } ctx_dev_slep;
+};
+
+struct xhci_dev_ctx {
+ union {
+ struct xhci_slot_ctx u_slot;
+ struct xhci_endp_ctx u_ep[XHCI_MAX_ENDPOINTS];
+ } ctx_dev_slep;
+} __aligned(XHCI_DEV_CTX_ALIGN);
+#define ctx_slot ctx_dev_slep.u_slot
+#define ctx_ep ctx_dev_slep.u_ep
+
+struct xhci_stream_ctx {
+ volatile uint64_t qwSctx0;
+#define XHCI_SCTX_0_DCS_GET(x) ((x) & 0x1)
+#define XHCI_SCTX_0_DCS_SET(x) ((x) & 0x1)
+#define XHCI_SCTX_0_SCT_SET(x) (((x) & 0x7) << 1)
+#define XHCI_SCTX_0_SCT_GET(x) (((x) >> 1) & 0x7)
+#define XHCI_SCTX_0_SCT_SEC_TR_RING 0x0
+#define XHCI_SCTX_0_SCT_PRIM_TR_RING 0x1
+#define XHCI_SCTX_0_SCT_PRIM_SSA_8 0x2
+#define XHCI_SCTX_0_SCT_PRIM_SSA_16 0x3
+#define XHCI_SCTX_0_SCT_PRIM_SSA_32 0x4
+#define XHCI_SCTX_0_SCT_PRIM_SSA_64 0x5
+#define XHCI_SCTX_0_SCT_PRIM_SSA_128 0x6
+#define XHCI_SCTX_0_SCT_PRIM_SSA_256 0x7
+#define XHCI_SCTX_0_TR_DQ_PTR_MASK 0xFFFFFFFFFFFFFFF0U
+ volatile uint32_t dwSctx2;
+ volatile uint32_t dwSctx3;
+};
+
+struct xhci_trb {
+ volatile uint64_t qwTrb0;
+#define XHCI_TRB_0_DIR_IN_MASK (0x80ULL << 0)
+#define XHCI_TRB_0_WLENGTH_MASK (0xFFFFULL << 48)
+ volatile uint32_t dwTrb2;
+#define XHCI_TRB_2_ERROR_GET(x) (((x) >> 24) & 0xFF)
+#define XHCI_TRB_2_ERROR_SET(x) (((x) & 0xFF) << 24)
+#define XHCI_TRB_2_TDSZ_GET(x) (((x) >> 17) & 0x1F)
+#define XHCI_TRB_2_TDSZ_SET(x) (((x) & 0x1F) << 17)
+#define XHCI_TRB_2_REM_GET(x) ((x) & 0xFFFFFF)
+#define XHCI_TRB_2_REM_SET(x) ((x) & 0xFFFFFF)
+#define XHCI_TRB_2_BYTES_GET(x) ((x) & 0x1FFFF)
+#define XHCI_TRB_2_BYTES_SET(x) ((x) & 0x1FFFF)
+#define XHCI_TRB_2_IRQ_GET(x) (((x) >> 22) & 0x3FF)
+#define XHCI_TRB_2_IRQ_SET(x) (((x) & 0x3FF) << 22)
+#define XHCI_TRB_2_STREAM_GET(x) (((x) >> 16) & 0xFFFF)
+#define XHCI_TRB_2_STREAM_SET(x) (((x) & 0xFFFF) << 16)
+
+ volatile uint32_t dwTrb3;
+#define XHCI_TRB_3_TYPE_GET(x) (((x) >> 10) & 0x3F)
+#define XHCI_TRB_3_TYPE_SET(x) (((x) & 0x3F) << 10)
+#define XHCI_TRB_3_CYCLE_BIT (1U << 0)
+#define XHCI_TRB_3_TC_BIT (1U << 1) /* command ring only */
+#define XHCI_TRB_3_ENT_BIT (1U << 1) /* transfer ring only */
+#define XHCI_TRB_3_ISP_BIT (1U << 2)
+#define XHCI_TRB_3_ED_BIT (1U << 2)
+#define XHCI_TRB_3_NSNOOP_BIT (1U << 3)
+#define XHCI_TRB_3_CHAIN_BIT (1U << 4)
+#define XHCI_TRB_3_IOC_BIT (1U << 5)
+#define XHCI_TRB_3_IDT_BIT (1U << 6)
+#define XHCI_TRB_3_TBC_GET(x) (((x) >> 7) & 3)
+#define XHCI_TRB_3_TBC_SET(x) (((x) & 3) << 7)
+#define XHCI_TRB_3_BEI_BIT (1U << 9)
+#define XHCI_TRB_3_DCEP_BIT (1U << 9)
+#define XHCI_TRB_3_PRSV_BIT (1U << 9)
+#define XHCI_TRB_3_BSR_BIT (1U << 9)
+#define XHCI_TRB_3_TRT_MASK (3U << 16)
+#define XHCI_TRB_3_TRT_NONE (0U << 16)
+#define XHCI_TRB_3_TRT_OUT (2U << 16)
+#define XHCI_TRB_3_TRT_IN (3U << 16)
+#define XHCI_TRB_3_DIR_IN (1U << 16)
+#define XHCI_TRB_3_TLBPC_GET(x) (((x) >> 16) & 0xF)
+#define XHCI_TRB_3_TLBPC_SET(x) (((x) & 0xF) << 16)
+#define XHCI_TRB_3_EP_GET(x) (((x) >> 16) & 0x1F)
+#define XHCI_TRB_3_EP_SET(x) (((x) & 0x1F) << 16)
+#define XHCI_TRB_3_FRID_GET(x) (((x) >> 20) & 0x7FF)
+#define XHCI_TRB_3_FRID_SET(x) (((x) & 0x7FF) << 20)
+#define XHCI_TRB_3_ISO_SIA_BIT (1U << 31)
+#define XHCI_TRB_3_SUSP_EP_BIT (1U << 23)
+#define XHCI_TRB_3_SLOT_GET(x) (((x) >> 24) & 0xFF)
+#define XHCI_TRB_3_SLOT_SET(x) (((x) & 0xFF) << 24)
+
+/* Commands */
+#define XHCI_TRB_TYPE_RESERVED 0x00
+#define XHCI_TRB_TYPE_NORMAL 0x01
+#define XHCI_TRB_TYPE_SETUP_STAGE 0x02
+#define XHCI_TRB_TYPE_DATA_STAGE 0x03
+#define XHCI_TRB_TYPE_STATUS_STAGE 0x04
+#define XHCI_TRB_TYPE_ISOCH 0x05
+#define XHCI_TRB_TYPE_LINK 0x06
+#define XHCI_TRB_TYPE_EVENT_DATA 0x07
+#define XHCI_TRB_TYPE_NOOP 0x08
+#define XHCI_TRB_TYPE_ENABLE_SLOT 0x09
+#define XHCI_TRB_TYPE_DISABLE_SLOT 0x0A
+#define XHCI_TRB_TYPE_ADDRESS_DEVICE 0x0B
+#define XHCI_TRB_TYPE_CONFIGURE_EP 0x0C
+#define XHCI_TRB_TYPE_EVALUATE_CTX 0x0D
+#define XHCI_TRB_TYPE_RESET_EP 0x0E
+#define XHCI_TRB_TYPE_STOP_EP 0x0F
+#define XHCI_TRB_TYPE_SET_TR_DEQUEUE 0x10
+#define XHCI_TRB_TYPE_RESET_DEVICE 0x11
+#define XHCI_TRB_TYPE_FORCE_EVENT 0x12
+#define XHCI_TRB_TYPE_NEGOTIATE_BW 0x13
+#define XHCI_TRB_TYPE_SET_LATENCY_TOL 0x14
+#define XHCI_TRB_TYPE_GET_PORT_BW 0x15
+#define XHCI_TRB_TYPE_FORCE_HEADER 0x16
+#define XHCI_TRB_TYPE_NOOP_CMD 0x17
+
+/* Events */
+#define XHCI_TRB_EVENT_TRANSFER 0x20
+#define XHCI_TRB_EVENT_CMD_COMPLETE 0x21
+#define XHCI_TRB_EVENT_PORT_STS_CHANGE 0x22
+#define XHCI_TRB_EVENT_BW_REQUEST 0x23
+#define XHCI_TRB_EVENT_DOORBELL 0x24
+#define XHCI_TRB_EVENT_HOST_CTRL 0x25
+#define XHCI_TRB_EVENT_DEVICE_NOTIFY 0x26
+#define XHCI_TRB_EVENT_MFINDEX_WRAP 0x27
+
+/* Error codes */
+#define XHCI_TRB_ERROR_INVALID 0x00
+#define XHCI_TRB_ERROR_SUCCESS 0x01
+#define XHCI_TRB_ERROR_DATA_BUF 0x02
+#define XHCI_TRB_ERROR_BABBLE 0x03
+#define XHCI_TRB_ERROR_XACT 0x04
+#define XHCI_TRB_ERROR_TRB 0x05
+#define XHCI_TRB_ERROR_STALL 0x06
+#define XHCI_TRB_ERROR_RESOURCE 0x07
+#define XHCI_TRB_ERROR_BANDWIDTH 0x08
+#define XHCI_TRB_ERROR_NO_SLOTS 0x09
+#define XHCI_TRB_ERROR_STREAM_TYPE 0x0A
+#define XHCI_TRB_ERROR_SLOT_NOT_ON 0x0B
+#define XHCI_TRB_ERROR_ENDP_NOT_ON 0x0C
+#define XHCI_TRB_ERROR_SHORT_PKT 0x0D
+#define XHCI_TRB_ERROR_RING_UNDERRUN 0x0E
+#define XHCI_TRB_ERROR_RING_OVERRUN 0x0F
+#define XHCI_TRB_ERROR_VF_RING_FULL 0x10
+#define XHCI_TRB_ERROR_PARAMETER 0x11
+#define XHCI_TRB_ERROR_BW_OVERRUN 0x12
+#define XHCI_TRB_ERROR_CONTEXT_STATE 0x13
+#define XHCI_TRB_ERROR_NO_PING_RESP 0x14
+#define XHCI_TRB_ERROR_EV_RING_FULL 0x15
+#define XHCI_TRB_ERROR_INCOMPAT_DEV 0x16
+#define XHCI_TRB_ERROR_MISSED_SERVICE 0x17
+#define XHCI_TRB_ERROR_CMD_RING_STOP 0x18
+#define XHCI_TRB_ERROR_CMD_ABORTED 0x19
+#define XHCI_TRB_ERROR_STOPPED 0x1A
+#define XHCI_TRB_ERROR_LENGTH 0x1B
+#define XHCI_TRB_ERROR_BAD_MELAT 0x1D
+#define XHCI_TRB_ERROR_ISOC_OVERRUN 0x1F
+#define XHCI_TRB_ERROR_EVENT_LOST 0x20
+#define XHCI_TRB_ERROR_UNDEFINED 0x21
+#define XHCI_TRB_ERROR_INVALID_SID 0x22
+#define XHCI_TRB_ERROR_SEC_BW 0x23
+#define XHCI_TRB_ERROR_SPLIT_XACT 0x24
+} __aligned(4);
+
+struct xhci_dev_endpoint_trbs {
+ struct xhci_trb trb[(XHCI_MAX_STREAMS *
+ XHCI_MAX_TRANSFERS) + XHCI_MAX_STREAMS];
+};
+
+struct xhci_event_ring_seg {
+ volatile uint64_t qwEvrsTablePtr;
+ volatile uint32_t dwEvrsTableSize;
+ volatile uint32_t dwEvrsReserved;
+};
+
+#endif /* _PCI_XHCI_H_ */
diff --git a/usr/src/cmd/bhyve/pm.c b/usr/src/cmd/bhyve/pm.c
index 70c4f1fae8..1313fbfb1d 100644
--- a/usr/src/cmd/bhyve/pm.c
+++ b/usr/src/cmd/bhyve/pm.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2013 Advanced Computing Technologies LLC
+ * Copyright (c) 2013 Hudson River Trading LLC
* Written by: John H. Baldwin <jhb@FreeBSD.org>
* All rights reserved.
*
@@ -26,12 +26,13 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/usr.sbin/bhyve/pm.c 266125 2014-05-15 14:16:55Z jhb $");
+__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <machine/vmm.h>
#include <assert.h>
+#include <errno.h>
#include <pthread.h>
#ifndef __FreeBSD__
#include <stdlib.h>
@@ -63,6 +64,8 @@ static int
reset_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
uint32_t *eax, void *arg)
{
+ int error;
+
static uint8_t reset_control;
if (bytes != 1)
@@ -74,12 +77,8 @@ reset_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
/* Treat hard and soft resets the same. */
if (reset_control & 0x4) {
-#ifdef __FreeBSD__
error = vm_suspend(ctx, VM_SUSPEND_RESET);
assert(error == 0 || errno == EALREADY);
-#else
- exit(0);
-#endif
}
}
return (0);
@@ -239,6 +238,7 @@ static int
pm1_control_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
uint32_t *eax, void *arg)
{
+ int error;
if (bytes != 2)
return (-1);
@@ -259,12 +259,8 @@ pm1_control_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
*/
if (*eax & PM1_SLP_EN) {
if ((pm1_control & PM1_SLP_TYP) >> 10 == 5) {
-#ifdef __FreeBSD__
error = vm_suspend(ctx, VM_SUSPEND_POWEROFF);
assert(error == 0 || errno == EALREADY);
-#else
- exit(0);
-#endif
}
}
}
diff --git a/usr/src/cmd/bhyve/pmtmr.c b/usr/src/cmd/bhyve/pmtmr.c
deleted file mode 100644
index ee797d7fff..0000000000
--- a/usr/src/cmd/bhyve/pmtmr.c
+++ /dev/null
@@ -1,217 +0,0 @@
-/*-
- * Copyright (c) 2012 NetApp, Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $FreeBSD: head/usr.sbin/bhyve/pmtmr.c 259998 2013-12-28 04:01:05Z jhb $
- */
-/*
- * This file and its contents are supplied under the terms of the
- * Common Development and Distribution License ("CDDL"), version 1.0.
- * You may only use this file in accordance with the terms of version
- * 1.0 of the CDDL.
- *
- * A full copy of the text of the CDDL should have accompanied this
- * source. A copy of the CDDL is also available via the Internet at
- * http://www.illumos.org/license/CDDL.
- *
- * Copyright 2014 Pluribus Networks Inc.
- * Copyright 2017 Joyent, Inc.
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/usr.sbin/bhyve/pmtmr.c 259998 2013-12-28 04:01:05Z jhb $");
-
-#include <sys/types.h>
-#include <sys/sysctl.h>
-#include <sys/time.h>
-#include <machine/cpufunc.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <assert.h>
-#include <pthread.h>
-#ifndef __FreeBSD__
-#include <kstat.h>
-#endif
-
-#include "acpi.h"
-#include "inout.h"
-
-/*
- * The ACPI Power Management timer is a free-running 24- or 32-bit
- * timer with a frequency of 3.579545MHz
- *
- * This implementation will be 32-bits
- */
-
-#define PMTMR_FREQ 3579545 /* 3.579545MHz */
-
-static pthread_mutex_t pmtmr_mtx;
-static pthread_once_t pmtmr_once = PTHREAD_ONCE_INIT;
-
-static uint64_t pmtmr_old;
-
-static uint64_t pmtmr_tscf;
-static uint64_t pmtmr_tsc_old;
-
-#ifdef __FreeBSD__
-static clockid_t clockid = CLOCK_UPTIME_FAST;
-static struct timespec pmtmr_uptime_old;
-
-#define timespecsub(vvp, uvp) \
- do { \
- (vvp)->tv_sec -= (uvp)->tv_sec; \
- (vvp)->tv_nsec -= (uvp)->tv_nsec; \
- if ((vvp)->tv_nsec < 0) { \
- (vvp)->tv_sec--; \
- (vvp)->tv_nsec += 1000000000; \
- } \
- } while (0)
-
-static uint64_t
-timespec_to_pmtmr(const struct timespec *tsnew, const struct timespec *tsold)
-{
- struct timespec tsdiff;
- int64_t nsecs;
-
- tsdiff = *tsnew;
- timespecsub(&tsdiff, tsold);
- nsecs = tsdiff.tv_sec * 1000000000 + tsdiff.tv_nsec;
- assert(nsecs >= 0);
-
- return (nsecs * PMTMR_FREQ / 1000000000 + pmtmr_old);
-}
-#endif
-
-static uint64_t
-tsc_to_pmtmr(uint64_t tsc_new, uint64_t tsc_old)
-{
-
- return ((tsc_new - tsc_old) * PMTMR_FREQ / pmtmr_tscf + pmtmr_old);
-}
-
-static void
-pmtmr_init(void)
-{
-#ifdef __FreeBSD__
- size_t len;
- int smp_tsc, err;
- struct timespec tsnew, tsold = { 0 };
-
- len = sizeof(smp_tsc);
- err = sysctlbyname("kern.timecounter.smp_tsc", &smp_tsc, &len, NULL, 0);
- assert(err == 0);
-
- if (smp_tsc) {
- len = sizeof(pmtmr_tscf);
- err = sysctlbyname("machdep.tsc_freq", &pmtmr_tscf, &len,
- NULL, 0);
- assert(err == 0);
-
- pmtmr_tsc_old = rdtsc();
- pmtmr_old = tsc_to_pmtmr(pmtmr_tsc_old, 0);
- } else {
- if (getenv("BHYVE_PMTMR_PRECISE") != NULL)
- clockid = CLOCK_UPTIME;
-
- err = clock_gettime(clockid, &tsnew);
- assert(err == 0);
-
- pmtmr_uptime_old = tsnew;
- pmtmr_old = timespec_to_pmtmr(&tsnew, &tsold);
- }
-#else
- kstat_ctl_t *kstat_ctl;
- kstat_t *kstat;
- kstat_named_t *kstat_cpu_freq;
-
- kstat_ctl = kstat_open();
- kstat = kstat_lookup(kstat_ctl, "cpu_info", 0, NULL);
- kstat_read(kstat_ctl, kstat, NULL);
- kstat_cpu_freq = kstat_data_lookup(kstat, "current_clock_Hz");
- pmtmr_tscf = kstat_cpu_freq->value.ul;
- kstat_close(kstat_ctl);
-
- pmtmr_tsc_old = rdtsc();
- pmtmr_old = tsc_to_pmtmr(pmtmr_tsc_old, 0);
-#endif
- pthread_mutex_init(&pmtmr_mtx, NULL);
-}
-
-static uint32_t
-pmtmr_val(void)
-{
-#ifdef __FreeBSD__
- struct timespec tsnew;
-#endif
- uint64_t pmtmr_tsc_new;
- uint64_t pmtmr_new;
-#ifdef __FreeBSD__
- int error;
-#endif
-
- pthread_once(&pmtmr_once, pmtmr_init);
-
- pthread_mutex_lock(&pmtmr_mtx);
-
-#ifdef __FreeBSD__
- if (pmtmr_tscf) {
- pmtmr_tsc_new = rdtsc();
- pmtmr_new = tsc_to_pmtmr(pmtmr_tsc_new, pmtmr_tsc_old);
- pmtmr_tsc_old = pmtmr_tsc_new;
- } else {
- error = clock_gettime(clockid, &tsnew);
- assert(error == 0);
-
- pmtmr_new = timespec_to_pmtmr(&tsnew, &pmtmr_uptime_old);
- pmtmr_uptime_old = tsnew;
- }
-#else
- pmtmr_tsc_new = rdtsc();
- pmtmr_new = tsc_to_pmtmr(pmtmr_tsc_new, pmtmr_tsc_old);
- pmtmr_tsc_old = pmtmr_tsc_new;
-#endif
- pmtmr_old = pmtmr_new;
-
- pthread_mutex_unlock(&pmtmr_mtx);
-
- return (pmtmr_new);
-}
-
-static int
-pmtmr_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
- uint32_t *eax, void *arg)
-{
- assert(in == 1);
-
- if (bytes != 4)
- return (-1);
-
- *eax = pmtmr_val();
-
- return (0);
-}
-
-INOUT_PORT(pmtmr, IO_PMTMR, IOPORT_F_IN, pmtmr_handler);
diff --git a/usr/src/cmd/bhyve/post.c b/usr/src/cmd/bhyve/post.c
index dcb481aac4..5215a0c22d 100644
--- a/usr/src/cmd/bhyve/post.c
+++ b/usr/src/cmd/bhyve/post.c
@@ -23,11 +23,11 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/post.c 260206 2014-01-02 21:26:59Z jhb $
+ * $FreeBSD$
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/usr.sbin/bhyve/post.c 260206 2014-01-02 21:26:59Z jhb $");
+__FBSDID("$FreeBSD$");
#include <sys/types.h>
diff --git a/usr/src/cmd/bhyve/ps2kbd.c b/usr/src/cmd/bhyve/ps2kbd.c
index 1399cde66e..ec3bb9814c 100644
--- a/usr/src/cmd/bhyve/ps2kbd.c
+++ b/usr/src/cmd/bhyve/ps2kbd.c
@@ -93,17 +93,6 @@ fifo_reset(struct ps2kbd_softc *sc)
fifo->size = sizeof(((struct fifo *)0)->buf);
}
-#if notyet
-static int
-fifo_available(struct ps2kbd_softc *sc)
-{
- struct fifo *fifo;
-
- fifo = &sc->fifo;
- return (fifo->num < fifo->size);
-}
-#endif
-
static void
fifo_put(struct ps2kbd_softc *sc, uint8_t val)
{
@@ -168,6 +157,9 @@ ps2kbd_write(struct ps2kbd_softc *sc, uint8_t val)
sc->curcmd = 0;
} else {
switch (val) {
+ case 0x00:
+ fifo_put(sc, PS2KC_ACK);
+ break;
case PS2KC_RESET_DEV:
fifo_reset(sc);
fifo_put(sc, PS2KC_ACK);
@@ -266,6 +258,12 @@ ps2kbd_keysym_queue(struct ps2kbd_softc *sc,
fifo_put(sc, 0xf0);
fifo_put(sc, 0x76);
break;
+ case 0xff50: /* Home */
+ fifo_put(sc, 0xe0);
+ if (!down)
+ fifo_put(sc, 0xf0);
+ fifo_put(sc, 0x6c);
+ break;
case 0xff51: /* Left arrow */
fifo_put(sc, 0xe0);
if (!down)
@@ -290,95 +288,152 @@ ps2kbd_keysym_queue(struct ps2kbd_softc *sc,
fifo_put(sc, 0xf0);
fifo_put(sc, 0x72);
break;
- case 0xffbe: /* F1 */
+ case 0xff55: /* PgUp */
+ fifo_put(sc, 0xe0);
+ if (!down)
+ fifo_put(sc, 0xf0);
+ fifo_put(sc, 0x7d);
+ break;
+ case 0xff56: /* PgDwn */
+ fifo_put(sc, 0xe0);
+ if (!down)
+ fifo_put(sc, 0xf0);
+ fifo_put(sc, 0x7a);
+ break;
+ case 0xff57: /* End */
+ fifo_put(sc, 0xe0);
+ if (!down)
+ fifo_put(sc, 0xf0);
+ fifo_put(sc, 0x69);
+ break;
+ case 0xff63: /* Ins */
+ fifo_put(sc, 0xe0);
+ if (!down)
+ fifo_put(sc, 0xf0);
+ fifo_put(sc, 0x70);
+ break;
+ case 0xff8d: /* Keypad Enter */
+ fifo_put(sc, 0xe0);
if (!down)
fifo_put(sc, 0xf0);
- fifo_put(sc, 0x05);
+ fifo_put(sc, 0x5a);
break;
- case 0xffbf: /* F2 */
+ case 0xffe1: /* Left shift */
if (!down)
fifo_put(sc, 0xf0);
- fifo_put(sc, 0x06);
+ fifo_put(sc, 0x12);
break;
- case 0xffc0: /* F3 */
+ case 0xffe2: /* Right shift */
if (!down)
fifo_put(sc, 0xf0);
- fifo_put(sc, 0x04);
+ fifo_put(sc, 0x59);
break;
- case 0xffc1: /* F4 */
+ case 0xffe3: /* Left control */
if (!down)
fifo_put(sc, 0xf0);
- fifo_put(sc, 0x0c);
+ fifo_put(sc, 0x14);
break;
- case 0xffc2: /* F5 */
+ case 0xffe4: /* Right control */
+ fifo_put(sc, 0xe0);
if (!down)
fifo_put(sc, 0xf0);
- fifo_put(sc, 0x03);
+ fifo_put(sc, 0x14);
break;
- case 0xffc3: /* F6 */
+ case 0xffe7: /* Left meta */
+ /* XXX */
+ break;
+ case 0xffe8: /* Right meta */
+ /* XXX */
+ break;
+ case 0xffe9: /* Left alt */
if (!down)
fifo_put(sc, 0xf0);
- fifo_put(sc, 0x0b);
+ fifo_put(sc, 0x11);
break;
- case 0xffc4: /* F7 */
+ case 0xfe03: /* AltGr */
+ case 0xffea: /* Right alt */
+ fifo_put(sc, 0xe0);
if (!down)
fifo_put(sc, 0xf0);
- fifo_put(sc, 0x83);
+ fifo_put(sc, 0x11);
break;
- case 0xffc5: /* F8 */
+ case 0xffeb: /* Left Windows */
+ fifo_put(sc, 0xe0);
if (!down)
fifo_put(sc, 0xf0);
- fifo_put(sc, 0x0a);
+ fifo_put(sc, 0x1f);
break;
- case 0xffc6: /* F9 */
+ case 0xffec: /* Right Windows */
+ fifo_put(sc, 0xe0);
if (!down)
fifo_put(sc, 0xf0);
- fifo_put(sc, 0x01);
+ fifo_put(sc, 0x27);
break;
- case 0xffc7: /* F10 */
+ case 0xffbe: /* F1 */
if (!down)
fifo_put(sc, 0xf0);
- fifo_put(sc, 0x09);
+ fifo_put(sc, 0x05);
break;
- case 0xffc8: /* F11 */
+ case 0xffbf: /* F2 */
if (!down)
fifo_put(sc, 0xf0);
- fifo_put(sc, 0x78);
+ fifo_put(sc, 0x06);
break;
- case 0xffc9: /* F12 */
+ case 0xffc0: /* F3 */
if (!down)
fifo_put(sc, 0xf0);
- fifo_put(sc, 0x07);
+ fifo_put(sc, 0x04);
break;
- case 0xffe1: /* Left shift */
+ case 0xffc1: /* F4 */
if (!down)
fifo_put(sc, 0xf0);
- fifo_put(sc, 0x12);
+ fifo_put(sc, 0x0C);
break;
- case 0xffe2: /* Right shift */
- /* XXX */
+ case 0xffc2: /* F5 */
+ if (!down)
+ fifo_put(sc, 0xf0);
+ fifo_put(sc, 0x03);
break;
- case 0xffe3: /* Left control */
+ case 0xffc3: /* F6 */
if (!down)
fifo_put(sc, 0xf0);
- fifo_put(sc, 0x14);
+ fifo_put(sc, 0x0B);
break;
- case 0xffe4: /* Right control */
- /* XXX */
+ case 0xffc4: /* F7 */
+ if (!down)
+ fifo_put(sc, 0xf0);
+ fifo_put(sc, 0x83);
break;
- case 0xffe7: /* Left meta */
- /* XXX */
+ case 0xffc5: /* F8 */
+ if (!down)
+ fifo_put(sc, 0xf0);
+ fifo_put(sc, 0x0A);
break;
- case 0xffe8: /* Right meta */
- /* XXX */
+ case 0xffc6: /* F9 */
+ if (!down)
+ fifo_put(sc, 0xf0);
+ fifo_put(sc, 0x01);
break;
- case 0xffe9: /* Left alt */
+ case 0xffc7: /* F10 */
if (!down)
fifo_put(sc, 0xf0);
- fifo_put(sc, 0x11);
+ fifo_put(sc, 0x09);
break;
- case 0xffea: /* Right alt */
- /* XXX */
+ case 0xffc8: /* F11 */
+ if (!down)
+ fifo_put(sc, 0xf0);
+ fifo_put(sc, 0x78);
+ break;
+ case 0xffc9: /* F12 */
+ if (!down)
+ fifo_put(sc, 0xf0);
+ fifo_put(sc, 0x07);
+ break;
+ case 0xffff: /* Del */
+ fifo_put(sc, 0xe0);
+ if (!down)
+ fifo_put(sc, 0xf0);
+ fifo_put(sc, 0x71);
break;
default:
fprintf(stderr, "Unhandled ps2 keyboard keysym 0x%x\n",
@@ -391,17 +446,19 @@ static void
ps2kbd_event(int down, uint32_t keysym, void *arg)
{
struct ps2kbd_softc *sc = arg;
+ int fifo_full;
pthread_mutex_lock(&sc->mtx);
if (!sc->enabled) {
pthread_mutex_unlock(&sc->mtx);
return;
}
-
+ fifo_full = sc->fifo.num == PS2KBD_FIFOSZ;
ps2kbd_keysym_queue(sc, down, keysym);
pthread_mutex_unlock(&sc->mtx);
- atkbdc_event(sc->atkbdc_sc);
+ if (!fifo_full)
+ atkbdc_event(sc->atkbdc_sc, 1);
}
struct ps2kbd_softc *
@@ -414,7 +471,8 @@ ps2kbd_init(struct atkbdc_softc *atkbdc_sc)
fifo_init(sc);
sc->atkbdc_sc = atkbdc_sc;
- console_kbd_register(ps2kbd_event, sc);
+ console_kbd_register(ps2kbd_event, sc, 1);
return (sc);
}
+
diff --git a/usr/src/cmd/bhyve/ps2mouse.c b/usr/src/cmd/bhyve/ps2mouse.c
index e96fbbf411..cea7210e2a 100644
--- a/usr/src/cmd/bhyve/ps2mouse.c
+++ b/usr/src/cmd/bhyve/ps2mouse.c
@@ -62,6 +62,16 @@ __FBSDID("$FreeBSD$");
/* mouse device id */
#define PS2MOUSE_DEV_ID 0x0
+/* mouse data bits */
+#define PS2M_DATA_Y_OFLOW 0x80
+#define PS2M_DATA_X_OFLOW 0x40
+#define PS2M_DATA_Y_SIGN 0x20
+#define PS2M_DATA_X_SIGN 0x10
+#define PS2M_DATA_AONE 0x08
+#define PS2M_DATA_MID_BUTTON 0x04
+#define PS2M_DATA_RIGHT_BUTTON 0x02
+#define PS2M_DATA_LEFT_BUTTON 0x01
+
/* mouse status bits */
#define PS2M_STS_REMOTE_MODE 0x40
#define PS2M_STS_ENABLE_DEV 0x20
@@ -87,6 +97,7 @@ struct ps2mouse_softc {
uint8_t status;
uint8_t resolution;
uint8_t sampling_rate;
+ int ctrlenable;
struct fifo fifo;
uint8_t curcmd; /* current command for next byte */
@@ -168,19 +179,20 @@ movement_get(struct ps2mouse_softc *sc)
assert(pthread_mutex_isowned_np(&sc->mtx));
- val0 = sc->status & (PS2M_STS_LEFT_BUTTON |
- PS2M_STS_RIGHT_BUTTON | PS2M_STS_MID_BUTTON);
+ val0 = PS2M_DATA_AONE;
+ val0 |= sc->status & (PS2M_DATA_LEFT_BUTTON |
+ PS2M_DATA_RIGHT_BUTTON | PS2M_DATA_MID_BUTTON);
if (sc->delta_x >= 0) {
if (sc->delta_x > 255) {
- val0 |= (1 << 6);
+ val0 |= PS2M_DATA_X_OFLOW;
val1 = 255;
} else
val1 = sc->delta_x;
} else {
- val0 |= (1 << 4);
+ val0 |= PS2M_DATA_X_SIGN;
if (sc->delta_x < -255) {
- val0 |= (1 << 6);
+ val0 |= PS2M_DATA_X_OFLOW;
val1 = 255;
} else
val1 = sc->delta_x;
@@ -189,23 +201,25 @@ movement_get(struct ps2mouse_softc *sc)
if (sc->delta_y >= 0) {
if (sc->delta_y > 255) {
- val0 |= (1 << 7);
+ val0 |= PS2M_DATA_Y_OFLOW;
val2 = 255;
} else
val2 = sc->delta_y;
} else {
- val0 |= (1 << 5);
+ val0 |= PS2M_DATA_Y_SIGN;
if (sc->delta_y < -255) {
- val0 |= (1 << 7);
+ val0 |= PS2M_DATA_Y_OFLOW;
val2 = 255;
} else
val2 = sc->delta_y;
}
sc->delta_y = 0;
- fifo_put(sc, val0);
- fifo_put(sc, val1);
- fifo_put(sc, val2);
+ if (sc->fifo.num < (sc->fifo.size - 3)) {
+ fifo_put(sc, val0);
+ fifo_put(sc, val1);
+ fifo_put(sc, val2);
+ }
}
static void
@@ -214,7 +228,7 @@ ps2mouse_reset(struct ps2mouse_softc *sc)
assert(pthread_mutex_isowned_np(&sc->mtx));
fifo_reset(sc);
movement_reset(sc);
- sc->status = 0x8;
+ sc->status = PS2M_STS_ENABLE_DEV;
sc->resolution = 4;
sc->sampling_rate = 100;
@@ -236,10 +250,32 @@ ps2mouse_read(struct ps2mouse_softc *sc, uint8_t *val)
return (retval);
}
+int
+ps2mouse_fifocnt(struct ps2mouse_softc *sc)
+{
+ return (sc->fifo.num);
+}
+
+void
+ps2mouse_toggle(struct ps2mouse_softc *sc, int enable)
+{
+ pthread_mutex_lock(&sc->mtx);
+ if (enable)
+ sc->ctrlenable = 1;
+ else {
+ sc->ctrlenable = 0;
+ sc->fifo.rindex = 0;
+ sc->fifo.windex = 0;
+ sc->fifo.num = 0;
+ }
+ pthread_mutex_unlock(&sc->mtx);
+}
+
void
-ps2mouse_write(struct ps2mouse_softc *sc, uint8_t val)
+ps2mouse_write(struct ps2mouse_softc *sc, uint8_t val, int insert)
{
pthread_mutex_lock(&sc->mtx);
+ fifo_reset(sc);
if (sc->curcmd) {
switch (sc->curcmd) {
case PS2MC_SET_SAMPLING_RATE:
@@ -256,8 +292,14 @@ ps2mouse_write(struct ps2mouse_softc *sc, uint8_t val)
break;
}
sc->curcmd = 0;
+
+ } else if (insert) {
+ fifo_put(sc, val);
} else {
switch (val) {
+ case 0x00:
+ fifo_put(sc, PS2MC_ACK);
+ break;
case PS2MC_RESET_DEV:
ps2mouse_reset(sc);
fifo_put(sc, PS2MC_ACK);
@@ -313,6 +355,7 @@ ps2mouse_write(struct ps2mouse_softc *sc, uint8_t val)
fifo_put(sc, PS2MC_ACK);
break;
default:
+ fifo_put(sc, PS2MC_ACK);
fprintf(stderr, "Unhandled ps2 mouse command "
"0x%02x\n", val);
break;
@@ -338,7 +381,7 @@ ps2mouse_event(uint8_t button, int x, int y, void *arg)
if (button & (1 << 2))
sc->status |= PS2M_STS_RIGHT_BUTTON;
- if ((sc->status & PS2M_STS_ENABLE_DEV) == 0) {
+ if ((sc->status & PS2M_STS_ENABLE_DEV) == 0 || !sc->ctrlenable) {
/* no data reporting */
pthread_mutex_unlock(&sc->mtx);
return;
@@ -347,7 +390,8 @@ ps2mouse_event(uint8_t button, int x, int y, void *arg)
movement_get(sc);
pthread_mutex_unlock(&sc->mtx);
- atkbdc_event(sc->atkbdc_sc);
+ if (sc->fifo.num > 0)
+ atkbdc_event(sc->atkbdc_sc, 0);
}
struct ps2mouse_softc *
@@ -364,8 +408,9 @@ ps2mouse_init(struct atkbdc_softc *atkbdc_sc)
ps2mouse_reset(sc);
pthread_mutex_unlock(&sc->mtx);
- console_ptr_register(ps2mouse_event, sc);
+ console_ptr_register(ps2mouse_event, sc, 1);
return (sc);
}
+
diff --git a/usr/src/cmd/bhyve/ps2mouse.h b/usr/src/cmd/bhyve/ps2mouse.h
index 1a78934b98..10d5698a30 100644
--- a/usr/src/cmd/bhyve/ps2mouse.h
+++ b/usr/src/cmd/bhyve/ps2mouse.h
@@ -34,6 +34,8 @@ struct atkbdc_softc;
struct ps2mouse_softc *ps2mouse_init(struct atkbdc_softc *sc);
int ps2mouse_read(struct ps2mouse_softc *sc, uint8_t *val);
-void ps2mouse_write(struct ps2mouse_softc *sc, uint8_t val);
+void ps2mouse_write(struct ps2mouse_softc *sc, uint8_t val, int insert);
+void ps2mouse_toggle(struct ps2mouse_softc *sc, int enable);
+int ps2mouse_fifocnt(struct ps2mouse_softc *sc);
#endif /* _PS2MOUSE_H_ */
diff --git a/usr/src/cmd/bhyve/rfb.c b/usr/src/cmd/bhyve/rfb.c
index d0ba4b10c9..ae99e045dd 100644
--- a/usr/src/cmd/bhyve/rfb.c
+++ b/usr/src/cmd/bhyve/rfb.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
- * Copyright (c) 2015 Nahanni Systems Inc.
+ * Copyright (c) 2015 Leon Dang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,30 +28,67 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include <sys/param.h>
+#ifndef WITHOUT_CAPSICUM
+#include <sys/capsicum.h>
+#endif
#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <machine/cpufunc.h>
+#include <machine/specialreg.h>
#include <netinet/in.h>
#include <assert.h>
+#include <err.h>
+#include <errno.h>
#include <pthread.h>
+#include <pthread_np.h>
#include <signal.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <sysexits.h>
#include <unistd.h>
+#include <zlib.h>
+
#include "bhyvegc.h"
#include "console.h"
#include "rfb.h"
+#include "sockstream.h"
+
+static int rfb_debug = 0;
+#define DPRINTF(params) if (rfb_debug) printf params
+#define WPRINTF(params) printf params
struct rfb_softc {
int sfd;
pthread_t tid;
+ int cfd;
+
int width, height;
bool enc_raw_ok;
+ bool enc_zlib_ok;
bool enc_resize_ok;
+
+ z_stream zstream;
+ uint8_t *zbuf;
+ int zbuflen;
+
+ int conn_wait;
+ int sending;
+ pthread_mutex_t mtx;
+ pthread_cond_t cond;
+
+ int hw_crc;
+ uint32_t *crc; /* WxH crc cells */
+ uint32_t *crc_tmp; /* buffer to store single crc row */
+ int crc_width, crc_height;
};
struct rfb_pixfmt {
@@ -82,8 +119,16 @@ struct rfb_pixfmt_msg {
};
#define RFB_ENCODING_RAW 0
+#define RFB_ENCODING_ZLIB 6
#define RFB_ENCODING_RESIZE -223
+#define RFB_MAX_WIDTH 2000
+#define RFB_MAX_HEIGHT 1200
+#define RFB_ZLIB_BUFSZ RFB_MAX_WIDTH*RFB_MAX_HEIGHT*4
+
+/* percentage changes to screen before sending the entire screen */
+#define RFB_SEND_ALL_THRESH 25
+
struct rfb_enc_msg {
uint8_t type;
uint8_t pad;
@@ -127,30 +172,36 @@ struct rfb_srvr_rect_hdr {
uint32_t encoding;
};
+struct rfb_cuttext_msg {
+ uint8_t type;
+ uint8_t padding[3];
+ uint32_t length;
+};
+
+
static void
rfb_send_server_init_msg(int cfd)
{
struct bhyvegc_image *gc_image;
struct rfb_srvr_info sinfo;
- int len;
gc_image = console_get_image();
- sinfo.width = ntohs(gc_image->width);
- sinfo.height = ntohs(gc_image->height);
+ sinfo.width = htons(gc_image->width);
+ sinfo.height = htons(gc_image->height);
sinfo.pixfmt.bpp = 32;
sinfo.pixfmt.depth = 32;
sinfo.pixfmt.bigendian = 0;
sinfo.pixfmt.truecolor = 1;
- sinfo.pixfmt.red_max = ntohs(255);
- sinfo.pixfmt.green_max = ntohs(255);
- sinfo.pixfmt.blue_max = ntohs(255);
+ sinfo.pixfmt.red_max = htons(255);
+ sinfo.pixfmt.green_max = htons(255);
+ sinfo.pixfmt.blue_max = htons(255);
sinfo.pixfmt.red_shift = 16;
sinfo.pixfmt.green_shift = 8;
sinfo.pixfmt.blue_shift = 0;
- sinfo.namelen = ntohl(strlen("bhyve"));
- len = write(cfd, &sinfo, sizeof(sinfo));
- len = write(cfd, "bhyve", strlen("bhyve"));
+ sinfo.namelen = htonl(strlen("bhyve"));
+ (void)stream_write(cfd, &sinfo, sizeof(sinfo));
+ (void)stream_write(cfd, "bhyve", strlen("bhyve"));
}
static void
@@ -162,25 +213,24 @@ rfb_send_resize_update_msg(struct rfb_softc *rc, int cfd)
/* Number of rectangles: 1 */
supdt_msg.type = 0;
supdt_msg.pad = 0;
- supdt_msg.numrects = ntohs(1);
- write(cfd, &supdt_msg, sizeof(struct rfb_srvr_updt_msg));
+ supdt_msg.numrects = htons(1);
+ stream_write(cfd, &supdt_msg, sizeof(struct rfb_srvr_updt_msg));
/* Rectangle header */
- srect_hdr.x = ntohs(0);
- srect_hdr.y = ntohs(0);
- srect_hdr.width = ntohs(rc->width);
- srect_hdr.height = ntohs(rc->height);
- srect_hdr.encoding = ntohl(RFB_ENCODING_RESIZE);
- write(cfd, &srect_hdr, sizeof(struct rfb_srvr_rect_hdr));
+ srect_hdr.x = htons(0);
+ srect_hdr.y = htons(0);
+ srect_hdr.width = htons(rc->width);
+ srect_hdr.height = htons(rc->height);
+ srect_hdr.encoding = htonl(RFB_ENCODING_RESIZE);
+ stream_write(cfd, &srect_hdr, sizeof(struct rfb_srvr_rect_hdr));
}
static void
rfb_recv_set_pixfmt_msg(struct rfb_softc *rc, int cfd)
{
struct rfb_pixfmt_msg pixfmt_msg;
- int len;
- len = read(cfd, ((void *)&pixfmt_msg) + 1, sizeof(pixfmt_msg) - 1);
+ (void)stream_read(cfd, ((void *)&pixfmt_msg)+1, sizeof(pixfmt_msg)-1);
}
@@ -188,18 +238,22 @@ static void
rfb_recv_set_encodings_msg(struct rfb_softc *rc, int cfd)
{
struct rfb_enc_msg enc_msg;
- int len, i;
+ int i;
uint32_t encoding;
assert((sizeof(enc_msg) - 1) == 3);
- len = read(cfd, ((void *)&enc_msg) + 1, sizeof(enc_msg) - 1);
+ (void)stream_read(cfd, ((void *)&enc_msg)+1, sizeof(enc_msg)-1);
- for (i = 0; i < ntohs(enc_msg.numencs); i++) {
- len = read(cfd, &encoding, sizeof(encoding));
- switch (ntohl(encoding)) {
+ for (i = 0; i < htons(enc_msg.numencs); i++) {
+ (void)stream_read(cfd, &encoding, sizeof(encoding));
+ switch (htonl(encoding)) {
case RFB_ENCODING_RAW:
rc->enc_raw_ok = true;
break;
+ case RFB_ENCODING_ZLIB:
+ rc->enc_zlib_ok = true;
+ deflateInit(&rc->zstream, Z_BEST_SPEED);
+ break;
case RFB_ENCODING_RESIZE:
rc->enc_resize_ok = true;
break;
@@ -207,90 +261,477 @@ rfb_recv_set_encodings_msg(struct rfb_softc *rc, int cfd)
}
}
-#if notyet
-static void
-rfb_resize_update(struct rfb_softc *rc, int fd)
+/*
+ * Calculate CRC32 using SSE4.2; Intel or AMD Bulldozer+ CPUs only
+ */
+static __inline uint32_t
+fast_crc32(void *buf, int len, uint32_t crcval)
+{
+ uint32_t q = len / sizeof(uint32_t);
+ uint32_t *p = (uint32_t *)buf;
+
+ while (q--) {
+ asm volatile (
+ ".byte 0xf2, 0xf, 0x38, 0xf1, 0xf1;"
+ :"=S" (crcval)
+ :"0" (crcval), "c" (*p)
+ );
+ p++;
+ }
+
+ return (crcval);
+}
+
+
+static int
+rfb_send_rect(struct rfb_softc *rc, int cfd, struct bhyvegc_image *gc,
+ int x, int y, int w, int h)
{
struct rfb_srvr_updt_msg supdt_msg;
struct rfb_srvr_rect_hdr srect_hdr;
+ unsigned long zlen;
+ ssize_t nwrite, total;
+ int err;
+ uint32_t *p;
+ uint8_t *zbufp;
+
+ /*
+ * Send a single rectangle of the given x, y, w h dimensions.
+ */
/* Number of rectangles: 1 */
supdt_msg.type = 0;
supdt_msg.pad = 0;
- supdt_msg.numrects = ntohs(1);
- write(fd, &supdt_msg, sizeof (struct rfb_srvr_updt_msg));
+ supdt_msg.numrects = htons(1);
+ nwrite = stream_write(cfd, &supdt_msg,
+ sizeof(struct rfb_srvr_updt_msg));
+ if (nwrite <= 0)
+ return (nwrite);
+
/* Rectangle header */
- srect_hdr.x = ntohs(0);
- srect_hdr.y = ntohs(0);
- srect_hdr.width = ntohs(rc->width);
- srect_hdr.height = ntohs(rc->height);
- srect_hdr.encoding = ntohl(RFB_ENCODING_RESIZE);
- write(fd, &srect_hdr, sizeof (struct rfb_srvr_rect_hdr));
+ srect_hdr.x = htons(x);
+ srect_hdr.y = htons(y);
+ srect_hdr.width = htons(w);
+ srect_hdr.height = htons(h);
+
+ h = y + h;
+ w *= sizeof(uint32_t);
+ if (rc->enc_zlib_ok) {
+ zbufp = rc->zbuf;
+ rc->zstream.total_in = 0;
+ rc->zstream.total_out = 0;
+ for (p = &gc->data[y * gc->width + x]; y < h; y++) {
+ rc->zstream.next_in = (Bytef *)p;
+ rc->zstream.avail_in = w;
+ rc->zstream.next_out = (Bytef *)zbufp;
+ rc->zstream.avail_out = RFB_ZLIB_BUFSZ + 16 -
+ rc->zstream.total_out;
+ rc->zstream.data_type = Z_BINARY;
+
+ /* Compress with zlib */
+ err = deflate(&rc->zstream, Z_SYNC_FLUSH);
+ if (err != Z_OK) {
+ WPRINTF(("zlib[rect] deflate err: %d\n", err));
+ rc->enc_zlib_ok = false;
+ deflateEnd(&rc->zstream);
+ goto doraw;
+ }
+ zbufp = rc->zbuf + rc->zstream.total_out;
+ p += gc->width;
+ }
+ srect_hdr.encoding = htonl(RFB_ENCODING_ZLIB);
+ nwrite = stream_write(cfd, &srect_hdr,
+ sizeof(struct rfb_srvr_rect_hdr));
+ if (nwrite <= 0)
+ return (nwrite);
+
+ zlen = htonl(rc->zstream.total_out);
+ nwrite = stream_write(cfd, &zlen, sizeof(uint32_t));
+ if (nwrite <= 0)
+ return (nwrite);
+ return (stream_write(cfd, rc->zbuf, rc->zstream.total_out));
+ }
+
+doraw:
+
+ total = 0;
+ zbufp = rc->zbuf;
+ for (p = &gc->data[y * gc->width + x]; y < h; y++) {
+ memcpy(zbufp, p, w);
+ zbufp += w;
+ total += w;
+ p += gc->width;
+ }
+
+ srect_hdr.encoding = htonl(RFB_ENCODING_RAW);
+ nwrite = stream_write(cfd, &srect_hdr,
+ sizeof(struct rfb_srvr_rect_hdr));
+ if (nwrite <= 0)
+ return (nwrite);
+
+ total = stream_write(cfd, rc->zbuf, total);
+
+ return (total);
}
-#endif
-static void
-rfb_recv_update_msg(struct rfb_softc *rc, int cfd)
+static int
+rfb_send_all(struct rfb_softc *rc, int cfd, struct bhyvegc_image *gc)
{
- struct rfb_updt_msg updt_msg;
struct rfb_srvr_updt_msg supdt_msg;
struct rfb_srvr_rect_hdr srect_hdr;
+ ssize_t nwrite;
+ unsigned long zlen;
+ int err;
+
+ /*
+ * Send the whole thing
+ */
+
+ /* Number of rectangles: 1 */
+ supdt_msg.type = 0;
+ supdt_msg.pad = 0;
+ supdt_msg.numrects = htons(1);
+ nwrite = stream_write(cfd, &supdt_msg,
+ sizeof(struct rfb_srvr_updt_msg));
+ if (nwrite <= 0)
+ return (nwrite);
+
+ /* Rectangle header */
+ srect_hdr.x = 0;
+ srect_hdr.y = 0;
+ srect_hdr.width = htons(gc->width);
+ srect_hdr.height = htons(gc->height);
+ if (rc->enc_zlib_ok) {
+ rc->zstream.next_in = (Bytef *)gc->data;
+ rc->zstream.avail_in = gc->width * gc->height *
+ sizeof(uint32_t);
+ rc->zstream.next_out = (Bytef *)rc->zbuf;
+ rc->zstream.avail_out = RFB_ZLIB_BUFSZ + 16;
+ rc->zstream.data_type = Z_BINARY;
+
+ rc->zstream.total_in = 0;
+ rc->zstream.total_out = 0;
+
+ /* Compress with zlib */
+ err = deflate(&rc->zstream, Z_SYNC_FLUSH);
+ if (err != Z_OK) {
+ WPRINTF(("zlib deflate err: %d\n", err));
+ rc->enc_zlib_ok = false;
+ deflateEnd(&rc->zstream);
+ goto doraw;
+ }
+
+ srect_hdr.encoding = htonl(RFB_ENCODING_ZLIB);
+ nwrite = stream_write(cfd, &srect_hdr,
+ sizeof(struct rfb_srvr_rect_hdr));
+ if (nwrite <= 0)
+ return (nwrite);
+
+ zlen = htonl(rc->zstream.total_out);
+ nwrite = stream_write(cfd, &zlen, sizeof(uint32_t));
+ if (nwrite <= 0)
+ return (nwrite);
+ return (stream_write(cfd, rc->zbuf, rc->zstream.total_out));
+ }
+
+doraw:
+ srect_hdr.encoding = htonl(RFB_ENCODING_RAW);
+ nwrite = stream_write(cfd, &srect_hdr,
+ sizeof(struct rfb_srvr_rect_hdr));
+ if (nwrite <= 0)
+ return (nwrite);
+
+ nwrite = stream_write(cfd, gc->data,
+ gc->width * gc->height * sizeof(uint32_t));
+
+ return (nwrite);
+}
+
+#define PIX_PER_CELL 32
+#define PIXCELL_SHIFT 5
+#define PIXCELL_MASK 0x1F
+
+static int
+rfb_send_screen(struct rfb_softc *rc, int cfd, int all)
+{
struct bhyvegc_image *gc_image;
- int len;
+ ssize_t nwrite;
+ int x, y;
+ int celly, cellwidth;
+ int xcells, ycells;
+ int w, h;
+ uint32_t *p;
+ int rem_x, rem_y; /* remainder for resolutions not x32 pixels ratio */
+ int retval;
+ uint32_t *crc_p, *orig_crc;
+ int changes;
+
+ console_refresh();
+ gc_image = console_get_image();
+
+ pthread_mutex_lock(&rc->mtx);
+ if (rc->sending) {
+ pthread_mutex_unlock(&rc->mtx);
+ return (1);
+ }
+ rc->sending = 1;
+ pthread_mutex_unlock(&rc->mtx);
+
+ retval = 0;
+
+ if (all) {
+ retval = rfb_send_all(rc, cfd, gc_image);
+ goto done;
+ }
+
+ /*
+ * Calculate the checksum for each 32x32 cell. Send each that
+ * has changed since the last scan.
+ */
+
+ /* Resolution changed */
+
+ rc->crc_width = gc_image->width;
+ rc->crc_height = gc_image->height;
+
+ w = rc->crc_width;
+ h = rc->crc_height;
+ xcells = howmany(rc->crc_width, PIX_PER_CELL);
+ ycells = howmany(rc->crc_height, PIX_PER_CELL);
+
+ rem_x = w & PIXCELL_MASK;
+
+ rem_y = h & PIXCELL_MASK;
+ if (!rem_y)
+ rem_y = PIX_PER_CELL;
+
+ p = gc_image->data;
- len = read(cfd, ((void *)&updt_msg) + 1 , sizeof(updt_msg) - 1);
+ /*
+ * Go through all cells and calculate crc. If significant number
+ * of changes, then send entire screen.
+ * crc_tmp is dual purpose: to store the new crc and to flag as
+ * a cell that has changed.
+ */
+ crc_p = rc->crc_tmp - xcells;
+ orig_crc = rc->crc - xcells;
+ changes = 0;
+ memset(rc->crc_tmp, 0, sizeof(uint32_t) * xcells * ycells);
+ for (y = 0; y < h; y++) {
+ if ((y & PIXCELL_MASK) == 0) {
+ crc_p += xcells;
+ orig_crc += xcells;
+ }
+
+ for (x = 0; x < xcells; x++) {
+ if (rc->hw_crc)
+ crc_p[x] = fast_crc32(p,
+ PIX_PER_CELL * sizeof(uint32_t),
+ crc_p[x]);
+ else
+ crc_p[x] = (uint32_t)crc32(crc_p[x],
+ (Bytef *)p,
+ PIX_PER_CELL * sizeof(uint32_t));
+
+ p += PIX_PER_CELL;
+
+ /* check for crc delta if last row in cell */
+ if ((y & PIXCELL_MASK) == PIXCELL_MASK || y == (h-1)) {
+ if (orig_crc[x] != crc_p[x]) {
+ orig_crc[x] = crc_p[x];
+ crc_p[x] = 1;
+ changes++;
+ } else {
+ crc_p[x] = 0;
+ }
+ }
+ }
+
+ if (rem_x) {
+ if (rc->hw_crc)
+ crc_p[x] = fast_crc32(p,
+ rem_x * sizeof(uint32_t),
+ crc_p[x]);
+ else
+ crc_p[x] = (uint32_t)crc32(crc_p[x],
+ (Bytef *)p,
+ rem_x * sizeof(uint32_t));
+ p += rem_x;
+
+ if ((y & PIXCELL_MASK) == PIXCELL_MASK || y == (h-1)) {
+ if (orig_crc[x] != crc_p[x]) {
+ orig_crc[x] = crc_p[x];
+ crc_p[x] = 1;
+ changes++;
+ } else {
+ crc_p[x] = 0;
+ }
+ }
+ }
+ }
+
+ /* If number of changes is > THRESH percent, send the whole screen */
+ if (((changes * 100) / (xcells * ycells)) >= RFB_SEND_ALL_THRESH) {
+ retval = rfb_send_all(rc, cfd, gc_image);
+ goto done;
+ }
+
+ /* Go through all cells, and send only changed ones */
+ crc_p = rc->crc_tmp;
+ for (y = 0; y < h; y += PIX_PER_CELL) {
+ /* previous cell's row */
+ celly = (y >> PIXCELL_SHIFT);
+
+ /* Delta check crc to previous set */
+ for (x = 0; x < xcells; x++) {
+ if (*crc_p++ == 0)
+ continue;
+
+ if (x == (xcells - 1) && rem_x > 0)
+ cellwidth = rem_x;
+ else
+ cellwidth = PIX_PER_CELL;
+ nwrite = rfb_send_rect(rc, cfd,
+ gc_image,
+ x * PIX_PER_CELL,
+ celly * PIX_PER_CELL,
+ cellwidth,
+ y + PIX_PER_CELL >= h ? rem_y : PIX_PER_CELL);
+ if (nwrite <= 0) {
+ retval = nwrite;
+ goto done;
+ }
+ }
+ }
+ retval = 1;
+
+done:
+ pthread_mutex_lock(&rc->mtx);
+ rc->sending = 0;
+ pthread_mutex_unlock(&rc->mtx);
+
+ return (retval);
+}
+
+
+static void
+rfb_recv_update_msg(struct rfb_softc *rc, int cfd, int discardonly)
+{
+ struct rfb_updt_msg updt_msg;
+ struct bhyvegc_image *gc_image;
+
+ (void)stream_read(cfd, ((void *)&updt_msg) + 1 , sizeof(updt_msg) - 1);
console_refresh();
gc_image = console_get_image();
- if (rc->width != gc_image->width || rc->height != gc_image->height) {
+ updt_msg.x = htons(updt_msg.x);
+ updt_msg.y = htons(updt_msg.y);
+ updt_msg.width = htons(updt_msg.width);
+ updt_msg.height = htons(updt_msg.height);
+
+ if (updt_msg.width != gc_image->width ||
+ updt_msg.height != gc_image->height) {
rc->width = gc_image->width;
rc->height = gc_image->height;
- rfb_send_resize_update_msg(rc, cfd);
+ if (rc->enc_resize_ok)
+ rfb_send_resize_update_msg(rc, cfd);
}
- /*
- * Send the whole thing
- */
- /* Number of rectangles: 1 */
- supdt_msg.type = 0;
- supdt_msg.pad = 0;
- supdt_msg.numrects = ntohs(1);
- write(cfd, &supdt_msg, sizeof(struct rfb_srvr_updt_msg));
+ if (discardonly)
+ return;
- /* Rectangle header */
- srect_hdr.x = ntohs(0);
- srect_hdr.y = ntohs(0);
- srect_hdr.width = ntohs(gc_image->width);
- srect_hdr.height = ntohs(gc_image->height);
- srect_hdr.encoding = ntohl(0); /* raw */
- write(cfd, &srect_hdr, sizeof(struct rfb_srvr_rect_hdr));
-
- write(cfd, gc_image->data, gc_image->width * gc_image->height *
- sizeof(uint32_t));
+ rfb_send_screen(rc, cfd, 1);
}
static void
rfb_recv_key_msg(struct rfb_softc *rc, int cfd)
{
struct rfb_key_msg key_msg;
- int len;
- len = read(cfd, ((void *)&key_msg) + 1, sizeof(key_msg) - 1);
+ (void)stream_read(cfd, ((void *)&key_msg) + 1, sizeof(key_msg) - 1);
- console_key_event(key_msg.down, ntohl(key_msg.code));
+ console_key_event(key_msg.down, htonl(key_msg.code));
}
static void
rfb_recv_ptr_msg(struct rfb_softc *rc, int cfd)
{
struct rfb_ptr_msg ptr_msg;
+
+ (void)stream_read(cfd, ((void *)&ptr_msg) + 1, sizeof(ptr_msg) - 1);
+
+ console_ptr_event(ptr_msg.button, htons(ptr_msg.x), htons(ptr_msg.y));
+}
+
+static void
+rfb_recv_cuttext_msg(struct rfb_softc *rc, int cfd)
+{
+ struct rfb_cuttext_msg ct_msg;
+ unsigned char buf[32];
int len;
- len = read(cfd, ((void *)&ptr_msg) + 1, sizeof(ptr_msg) - 1);
+ len = stream_read(cfd, ((void *)&ct_msg) + 1, sizeof(ct_msg) - 1);
+ ct_msg.length = htonl(ct_msg.length);
+ while (ct_msg.length > 0) {
+ len = stream_read(cfd, buf, ct_msg.length > sizeof(buf) ?
+ sizeof(buf) : ct_msg.length);
+ ct_msg.length -= len;
+ }
+}
- console_ptr_event(ptr_msg.button, ntohs(ptr_msg.x), ntohs(ptr_msg.y));
+static int64_t
+timeval_delta(struct timeval *prev, struct timeval *now)
+{
+ int64_t n1, n2;
+ n1 = now->tv_sec * 1000000 + now->tv_usec;
+ n2 = prev->tv_sec * 1000000 + prev->tv_usec;
+ return (n1 - n2);
+}
+
+static void *
+rfb_wr_thr(void *arg)
+{
+ struct rfb_softc *rc;
+ fd_set rfds;
+ struct timeval tv;
+ struct timeval prev_tv;
+ int64_t tdiff;
+ int cfd;
+ int err;
+
+ rc = arg;
+ cfd = rc->cfd;
+
+ prev_tv.tv_sec = 0;
+ prev_tv.tv_usec = 0;
+ while (rc->cfd >= 0) {
+ FD_ZERO(&rfds);
+ FD_SET(cfd, &rfds);
+ tv.tv_sec = 0;
+ tv.tv_usec = 10000;
+
+ err = select(cfd+1, &rfds, NULL, NULL, &tv);
+ if (err < 0)
+ return (NULL);
+
+ /* Determine if its time to push screen; ~24hz */
+ gettimeofday(&tv, NULL);
+ tdiff = timeval_delta(&prev_tv, &tv);
+ if (tdiff > 40000) {
+ prev_tv.tv_sec = tv.tv_sec;
+ prev_tv.tv_usec = tv.tv_usec;
+ if (rfb_send_screen(rc, cfd, 0) <= 0) {
+ return (NULL);
+ }
+ } else {
+ /* sleep */
+ usleep(40000 - tdiff);
+ }
+ }
+
+ return (NULL);
}
void
@@ -298,12 +739,14 @@ rfb_handle(struct rfb_softc *rc, int cfd)
{
const char *vbuf = "RFB 003.008\n";
unsigned char buf[80];
- int len;
+ pthread_t tid;
uint32_t sres;
+ int len;
+
+ rc->cfd = cfd;
/* 1a. Send server version */
- printf("server vers write: (%s), %d bytes\n", vbuf, (int) strlen(vbuf));
- write(cfd, vbuf, strlen(vbuf));
+ stream_write(cfd, vbuf, strlen(vbuf));
/* 1b. Read client version */
len = read(cfd, buf, sizeof(buf));
@@ -311,26 +754,37 @@ rfb_handle(struct rfb_softc *rc, int cfd)
/* 2a. Send security type 'none' */
buf[0] = 1;
buf[1] = 1; /* none */
- write(cfd, buf, 2);
+ stream_write(cfd, buf, 2);
+
/* 2b. Read agreed security type */
- len = read(cfd, buf, 1);
+ len = stream_read(cfd, buf, 1);
/* 2c. Write back a status of 0 */
sres = 0;
- write(cfd, &sres, 4);
+ stream_write(cfd, &sres, 4);
/* 3a. Read client shared-flag byte */
- len = read(cfd, buf, 1);
+ len = stream_read(cfd, buf, 1);
/* 4a. Write server-init info */
rfb_send_server_init_msg(cfd);
+ if (!rc->zbuf) {
+ rc->zbuf = malloc(RFB_ZLIB_BUFSZ + 16);
+ assert(rc->zbuf != NULL);
+ }
+
+ rfb_send_screen(rc, cfd, 1);
+
+ pthread_create(&tid, NULL, rfb_wr_thr, rc);
+ pthread_set_name_np(tid, "rfbout");
+
/* Now read in client requests. 1st byte identifies type */
for (;;) {
len = read(cfd, buf, 1);
if (len <= 0) {
- printf("exiting\n");
+ DPRINTF(("rfb client exiting\r\n"));
break;
}
@@ -342,7 +796,7 @@ rfb_handle(struct rfb_softc *rc, int cfd)
rfb_recv_set_encodings_msg(rc, cfd);
break;
case 3:
- rfb_recv_update_msg(rc, cfd);
+ rfb_recv_update_msg(rc, cfd, 1);
break;
case 4:
rfb_recv_key_msg(rc, cfd);
@@ -350,11 +804,19 @@ rfb_handle(struct rfb_softc *rc, int cfd)
case 5:
rfb_recv_ptr_msg(rc, cfd);
break;
+ case 6:
+ rfb_recv_cuttext_msg(rc, cfd);
+ break;
default:
- printf("unknown client code!\n");
- exit(1);
+ WPRINTF(("rfb unknown cli-code %d!\n", buf[0] & 0xff));
+ goto done;
}
}
+done:
+ rc->cfd = -1;
+ pthread_join(tid, NULL);
+ if (rc->enc_zlib_ok)
+ deflateEnd(&rc->zstream);
}
static void *
@@ -375,23 +837,56 @@ rfb_thr(void *arg)
}
for (;;) {
+ rc->enc_raw_ok = false;
+ rc->enc_zlib_ok = false;
+ rc->enc_resize_ok = false;
+
cfd = accept(rc->sfd, NULL, NULL);
+ if (rc->conn_wait) {
+ pthread_mutex_lock(&rc->mtx);
+ pthread_cond_signal(&rc->cond);
+ pthread_mutex_unlock(&rc->mtx);
+ rc->conn_wait = 0;
+ }
rfb_handle(rc, cfd);
+ close(cfd);
}
/* NOTREACHED */
return (NULL);
}
+static int
+sse42_supported(void)
+{
+ u_int cpu_registers[4], ecx;
+
+ do_cpuid(1, cpu_registers);
+
+ ecx = cpu_registers[2];
+
+ return ((ecx & CPUID2_SSE42) != 0);
+}
+
int
-rfb_init(int port)
+rfb_init(char *hostname, int port, int wait)
{
struct rfb_softc *rc;
struct sockaddr_in sin;
int on = 1;
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_t rights;
+#endif
rc = calloc(1, sizeof(struct rfb_softc));
+ rc->crc = calloc(howmany(RFB_MAX_WIDTH * RFB_MAX_HEIGHT, 32),
+ sizeof(uint32_t));
+ rc->crc_tmp = calloc(howmany(RFB_MAX_WIDTH * RFB_MAX_HEIGHT, 32),
+ sizeof(uint32_t));
+ rc->crc_width = RFB_MAX_WIDTH;
+ rc->crc_height = RFB_MAX_HEIGHT;
+
rc->sfd = socket(AF_INET, SOCK_STREAM, 0);
if (rc->sfd < 0) {
perror("socket");
@@ -404,8 +899,12 @@ rfb_init(int port)
sin.sin_len = sizeof(sin);
#endif
sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = htonl(INADDR_ANY);
- sin.sin_port = htons(port);
+ sin.sin_port = port ? htons(port) : htons(5900);
+ if (hostname && strlen(hostname) > 0)
+ inet_pton(AF_INET, hostname, &(sin.sin_addr));
+ else
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
if (bind(rc->sfd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
perror("bind");
return (-1);
@@ -416,7 +915,29 @@ rfb_init(int port)
return (-1);
}
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_init(&rights, CAP_ACCEPT, CAP_EVENT, CAP_READ, CAP_WRITE);
+ if (cap_rights_limit(rc->sfd, &rights) == -1 && errno != ENOSYS)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+#endif
+
+ rc->hw_crc = sse42_supported();
+
+ rc->conn_wait = wait;
+ if (wait) {
+ pthread_mutex_init(&rc->mtx, NULL);
+ pthread_cond_init(&rc->cond, NULL);
+ }
+
pthread_create(&rc->tid, NULL, rfb_thr, rc);
+ pthread_set_name_np(rc->tid, "rfb");
+
+ if (wait) {
+ DPRINTF(("Waiting for rfb client...\n"));
+ pthread_mutex_lock(&rc->mtx);
+ pthread_cond_wait(&rc->cond, &rc->mtx);
+ pthread_mutex_unlock(&rc->mtx);
+ }
return (0);
}
diff --git a/usr/src/cmd/bhyve/rfb.h b/usr/src/cmd/bhyve/rfb.h
index 5504c333ab..b3d3786669 100644
--- a/usr/src/cmd/bhyve/rfb.h
+++ b/usr/src/cmd/bhyve/rfb.h
@@ -31,6 +31,6 @@
#define RFB_PORT 5900
-int rfb_init(int port);
+int rfb_init(char *hostname, int port, int wait);
#endif /* _RFB_H_ */
diff --git a/usr/src/cmd/bhyve/rtc.c b/usr/src/cmd/bhyve/rtc.c
index 5ab78e060f..5c7015400a 100644
--- a/usr/src/cmd/bhyve/rtc.c
+++ b/usr/src/cmd/bhyve/rtc.c
@@ -23,17 +23,14 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/rtc.c 260206 2014-01-02 21:26:59Z jhb $
+ * $FreeBSD$
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/usr.sbin/bhyve/rtc.c 260206 2014-01-02 21:26:59Z jhb $");
+__FBSDID("$FreeBSD$");
#include <sys/types.h>
-#include <sys/time.h>
-#include <stdio.h>
-#include <string.h>
#include <time.h>
#include <assert.h>
@@ -41,47 +38,11 @@ __FBSDID("$FreeBSD: head/usr.sbin/bhyve/rtc.c 260206 2014-01-02 21:26:59Z jhb $"
#include <vmmapi.h>
#include "acpi.h"
-#include "inout.h"
#include "pci_lpc.h"
#include "rtc.h"
-#define IO_RTC 0x70
+#define IO_RTC 0x70
-#define RTC_SEC 0x00 /* seconds */
-#define RTC_SEC_ALARM 0x01
-#define RTC_MIN 0x02
-#define RTC_MIN_ALARM 0x03
-#define RTC_HRS 0x04
-#define RTC_HRS_ALARM 0x05
-#define RTC_WDAY 0x06
-#define RTC_DAY 0x07
-#define RTC_MONTH 0x08
-#define RTC_YEAR 0x09
-#define RTC_CENTURY 0x32 /* current century */
-
-#define RTC_STATUSA 0xA
-#define RTCSA_TUP 0x80 /* time update, don't look now */
-
-#define RTC_STATUSB 0xB
-#define RTCSB_DST 0x01
-#define RTCSB_24HR 0x02
-#define RTCSB_BIN 0x04 /* 0 = BCD, 1 = Binary */
-#define RTCSB_PINTR 0x40 /* 1 = enable periodic clock interrupt */
-#define RTCSB_HALT 0x80 /* stop clock updates */
-
-#define RTC_INTR 0x0c /* status register C (R) interrupt source */
-
-#define RTC_STATUSD 0x0d /* status register D (R) Lost Power */
-#define RTCSD_PWR 0x80 /* clock power OK */
-
-#define RTC_NVRAM_START 0x0e
-#define RTC_NVRAM_END 0x7f
-#define RTC_NVRAM_SZ (128 - RTC_NVRAM_START)
-#define nvoff(x) ((x) - RTC_NVRAM_START)
-
-#define RTC_DIAG 0x0e
-#define RTC_RSTCODE 0x0f
-#define RTC_EQUIPMENT 0x14
#define RTC_LMEM_LSB 0x34
#define RTC_LMEM_MSB 0x35
#define RTC_HMEM_LSB 0x5b
@@ -92,249 +53,30 @@ __FBSDID("$FreeBSD: head/usr.sbin/bhyve/rtc.c 260206 2014-01-02 21:26:59Z jhb $"
#define m_16MB (16*1024*1024)
#define m_4GB (4ULL*1024*1024*1024)
-static int addr;
-
-static uint8_t rtc_nvram[RTC_NVRAM_SZ];
-
-/* XXX initialize these to default values as they would be from BIOS */
-static uint8_t status_a, status_b;
-
-static struct {
- uint8_t hours;
- uint8_t mins;
- uint8_t secs;
-} rtc_alarm;
-
-static u_char const bin2bcd_data[] = {
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
- 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
- 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
- 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
- 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
- 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
- 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
- 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
- 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
- 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99
-};
-#define bin2bcd(bin) (bin2bcd_data[bin])
-
-#define rtcout(val) ((status_b & RTCSB_BIN) ? (val) : bin2bcd((val)))
-
-static void
-timevalfix(struct timeval *t1)
-{
-
- if (t1->tv_usec < 0) {
- t1->tv_sec--;
- t1->tv_usec += 1000000;
- }
- if (t1->tv_usec >= 1000000) {
- t1->tv_sec++;
- t1->tv_usec -= 1000000;
- }
-}
-
-static void
-timevalsub(struct timeval *t1, const struct timeval *t2)
-{
-
- t1->tv_sec -= t2->tv_sec;
- t1->tv_usec -= t2->tv_usec;
- timevalfix(t1);
-}
-
-static int
-rtc_addr_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
- uint32_t *eax, void *arg)
-{
- if (bytes != 1)
- return (-1);
-
- if (in) {
- /* straight read of this register will return 0xFF */
- *eax = 0xff;
- return (0);
- }
-
- switch (*eax & 0x7f) {
- case RTC_SEC:
- case RTC_SEC_ALARM:
- case RTC_MIN:
- case RTC_MIN_ALARM:
- case RTC_HRS:
- case RTC_HRS_ALARM:
- case RTC_WDAY:
- case RTC_DAY:
- case RTC_MONTH:
- case RTC_YEAR:
- case RTC_STATUSA:
- case RTC_STATUSB:
- case RTC_INTR:
- case RTC_STATUSD:
- case RTC_NVRAM_START ... RTC_NVRAM_END:
- break;
- default:
- return (-1);
- }
-
- addr = *eax & 0x7f;
- return (0);
-}
-
-static int
-rtc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
- uint32_t *eax, void *arg)
+/*
+ * Returns the current RTC time as number of seconds since 00:00:00 Jan 1, 1970
+ */
+static time_t
+rtc_time(struct vmctx *ctx, int use_localtime)
{
- int hour;
+ struct tm tm;
time_t t;
- struct timeval cur, delta;
- static struct timeval last;
- static struct tm tm;
-
- if (bytes != 1)
- return (-1);
-
- gettimeofday(&cur, NULL);
-
- /*
- * Increment the cached time only once per second so we can guarantee
- * that the guest has at least one second to read the hour:min:sec
- * separately and still get a coherent view of the time.
- */
- delta = cur;
- timevalsub(&delta, &last);
- if (delta.tv_sec >= 1 && (status_b & RTCSB_HALT) == 0) {
- t = cur.tv_sec;
+ time(&t);
+ if (use_localtime) {
localtime_r(&t, &tm);
- last = cur;
- }
-
- if (in) {
- switch (addr) {
- case RTC_SEC_ALARM:
- *eax = rtc_alarm.secs;
- break;
- case RTC_MIN_ALARM:
- *eax = rtc_alarm.mins;
- break;
- case RTC_HRS_ALARM:
- *eax = rtc_alarm.hours;
- break;
- case RTC_SEC:
- *eax = rtcout(tm.tm_sec);
- return (0);
- case RTC_MIN:
- *eax = rtcout(tm.tm_min);
- return (0);
- case RTC_HRS:
- if (status_b & RTCSB_24HR)
- hour = tm.tm_hour;
- else
- hour = (tm.tm_hour % 12) + 1;
-
- *eax = rtcout(hour);
-
- /*
- * If we are representing time in the 12-hour format
- * then set the MSB to indicate PM.
- */
- if ((status_b & RTCSB_24HR) == 0 && tm.tm_hour >= 12)
- *eax |= 0x80;
-
- return (0);
- case RTC_WDAY:
- *eax = rtcout(tm.tm_wday + 1);
- return (0);
- case RTC_DAY:
- *eax = rtcout(tm.tm_mday);
- return (0);
- case RTC_MONTH:
- *eax = rtcout(tm.tm_mon + 1);
- return (0);
- case RTC_YEAR:
- *eax = rtcout(tm.tm_year % 100);
- return (0);
- case RTC_STATUSA:
- *eax = status_a;
- return (0);
- case RTC_STATUSB:
- *eax = status_b;
- return (0);
- case RTC_INTR:
- *eax = 0;
- return (0);
- case RTC_STATUSD:
- *eax = RTCSD_PWR;
- return (0);
- case RTC_NVRAM_START ... RTC_NVRAM_END:
- *eax = rtc_nvram[addr - RTC_NVRAM_START];
- return (0);
- default:
- return (-1);
- }
- }
-
- switch (addr) {
- case RTC_STATUSA:
- status_a = *eax & ~RTCSA_TUP;
- break;
- case RTC_STATUSB:
- /* XXX not implemented yet XXX */
- if (*eax & RTCSB_PINTR)
- return (-1);
- status_b = *eax;
- break;
- case RTC_STATUSD:
- /* ignore write */
- break;
- case RTC_SEC_ALARM:
- rtc_alarm.secs = *eax;
- break;
- case RTC_MIN_ALARM:
- rtc_alarm.mins = *eax;
- break;
- case RTC_HRS_ALARM:
- rtc_alarm.hours = *eax;
- break;
- case RTC_SEC:
- case RTC_MIN:
- case RTC_HRS:
- case RTC_WDAY:
- case RTC_DAY:
- case RTC_MONTH:
- case RTC_YEAR:
- /*
- * Ignore writes to the time of day registers
- */
- break;
- case RTC_NVRAM_START ... RTC_NVRAM_END:
- rtc_nvram[addr - RTC_NVRAM_START] = *eax;
- break;
- default:
- return (-1);
+ t = timegm(&tm);
}
- return (0);
+ return (t);
}
void
-rtc_init(struct vmctx *ctx)
+rtc_init(struct vmctx *ctx, int use_localtime)
{
- struct timeval cur;
- struct tm tm;
size_t himem;
size_t lomem;
int err;
- err = gettimeofday(&cur, NULL);
- assert(err == 0);
- (void) localtime_r(&cur.tv_sec, &tm);
-
- memset(rtc_nvram, 0, sizeof(rtc_nvram));
-
- rtc_nvram[nvoff(RTC_CENTURY)] = bin2bcd((tm.tm_year + 1900) / 100);
-
/* XXX init diag/reset code/equipment/checksum ? */
/*
@@ -344,19 +86,23 @@ rtc_init(struct vmctx *ctx)
* 0x5b/0x5c/0x5d - 64KB chunks above 4GB
*/
lomem = (vm_get_lowmem_size(ctx) - m_16MB) / m_64KB;
- rtc_nvram[nvoff(RTC_LMEM_LSB)] = lomem;
- rtc_nvram[nvoff(RTC_LMEM_MSB)] = lomem >> 8;
+ err = vm_rtc_write(ctx, RTC_LMEM_LSB, lomem);
+ assert(err == 0);
+ err = vm_rtc_write(ctx, RTC_LMEM_MSB, lomem >> 8);
+ assert(err == 0);
himem = vm_get_highmem_size(ctx) / m_64KB;
- rtc_nvram[nvoff(RTC_HMEM_LSB)] = himem;
- rtc_nvram[nvoff(RTC_HMEM_SB)] = himem >> 8;
- rtc_nvram[nvoff(RTC_HMEM_MSB)] = himem >> 16;
-}
+ err = vm_rtc_write(ctx, RTC_HMEM_LSB, himem);
+ assert(err == 0);
+ err = vm_rtc_write(ctx, RTC_HMEM_SB, himem >> 8);
+ assert(err == 0);
+ err = vm_rtc_write(ctx, RTC_HMEM_MSB, himem >> 16);
+ assert(err == 0);
-INOUT_PORT(rtc, IO_RTC, IOPORT_F_INOUT, rtc_addr_handler);
-INOUT_PORT(rtc, IO_RTC + 1, IOPORT_F_INOUT, rtc_data_handler);
+ err = vm_rtc_settime(ctx, rtc_time(ctx, use_localtime));
+ assert(err == 0);
+}
-#ifdef __FreeBSD__
static void
rtc_dsdt(void)
{
@@ -375,6 +121,9 @@ rtc_dsdt(void)
dsdt_line("}");
}
LPC_DSDT(rtc_dsdt);
-#endif
+/*
+ * Reserve the extended RTC I/O ports although they are not emulated at this
+ * time.
+ */
SYSRES_IO(0x72, 6);
diff --git a/usr/src/cmd/bhyve/rtc.h b/usr/src/cmd/bhyve/rtc.h
index 6406d24c37..5b08ca3463 100644
--- a/usr/src/cmd/bhyve/rtc.h
+++ b/usr/src/cmd/bhyve/rtc.h
@@ -23,12 +23,12 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/rtc.h 253181 2013-07-11 03:54:35Z grehan $
+ * $FreeBSD$
*/
#ifndef _RTC_H_
#define _RTC_H_
-void rtc_init(struct vmctx *ctx);
+void rtc_init(struct vmctx *ctx, int use_localtime);
#endif /* _RTC_H_ */
diff --git a/usr/src/cmd/bhyve/smbiostbl.c b/usr/src/cmd/bhyve/smbiostbl.c
index 7ba0f0dfa0..59a1358bd0 100644
--- a/usr/src/cmd/bhyve/smbiostbl.c
+++ b/usr/src/cmd/bhyve/smbiostbl.c
@@ -25,7 +25,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/usr.sbin/bhyve/smbiostbl.c 272007 2014-09-23 01:17:22Z grehan $");
+__FBSDID("$FreeBSD$");
#include <sys/param.h>
@@ -321,8 +321,8 @@ struct smbios_table_type0 smbios_type0_template = {
const char *smbios_type0_strings[] = {
"BHYVE", /* vendor string */
- __TIME__, /* bios version string */
- __DATE__, /* bios release date string */
+ "1.00", /* bios version string */
+ "03/14/2014", /* bios release date string */
NULL
};
diff --git a/usr/src/cmd/bhyve/smbiostbl.h b/usr/src/cmd/bhyve/smbiostbl.h
index fd7f86be80..e8b3a4fe29 100644
--- a/usr/src/cmd/bhyve/smbiostbl.h
+++ b/usr/src/cmd/bhyve/smbiostbl.h
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/smbiostbl.h 262744 2014-03-04 17:12:06Z tychon $
+ * $FreeBSD$
*/
#ifndef _SMBIOSTBL_H_
diff --git a/usr/src/cmd/bhyve/sockstream.c b/usr/src/cmd/bhyve/sockstream.c
new file mode 100644
index 0000000000..1789206ff3
--- /dev/null
+++ b/usr/src/cmd/bhyve/sockstream.c
@@ -0,0 +1,86 @@
+/*-
+ * Copyright (c) 2015 Nahanni Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <errno.h>
+
+#include "sockstream.h"
+
+ssize_t
+stream_read(int fd, void *buf, ssize_t nbytes)
+{
+ uint8_t *p;
+ ssize_t len = 0;
+ ssize_t n;
+
+ p = buf;
+
+ while (len < nbytes) {
+ n = read(fd, p + len, nbytes - len);
+ if (n == 0)
+ break;
+
+ if (n < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return (n);
+ }
+ len += n;
+ }
+ return (len);
+}
+
+ssize_t
+stream_write(int fd, const void *buf, ssize_t nbytes)
+{
+ const uint8_t *p;
+ ssize_t len = 0;
+ ssize_t n;
+
+ p = buf;
+
+ while (len < nbytes) {
+ n = write(fd, p + len, nbytes - len);
+ if (n == 0)
+ break;
+ if (n < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return (n);
+ }
+ len += n;
+ }
+ return (len);
+}
+
+
diff --git a/usr/src/cmd/bhyve/sockstream.h b/usr/src/cmd/bhyve/sockstream.h
new file mode 100644
index 0000000000..bb0b3b06eb
--- /dev/null
+++ b/usr/src/cmd/bhyve/sockstream.h
@@ -0,0 +1,33 @@
+/*-
+ * Copyright (c) 2015 Nahanni Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+ssize_t stream_read(int fd, void *buf, ssize_t nbytes);
+ssize_t stream_write(int fd, const void *buf, ssize_t nbytes);
diff --git a/usr/src/cmd/bhyve/spinup_ap.c b/usr/src/cmd/bhyve/spinup_ap.c
index e1dd562d3f..c597023aeb 100644
--- a/usr/src/cmd/bhyve/spinup_ap.c
+++ b/usr/src/cmd/bhyve/spinup_ap.c
@@ -23,11 +23,11 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/spinup_ap.c 263432 2014-03-20 18:15:37Z neel $
+ * $FreeBSD$
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/usr.sbin/bhyve/spinup_ap.c 263432 2014-03-20 18:15:37Z neel $");
+__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/types.h>
diff --git a/usr/src/cmd/bhyve/spinup_ap.h b/usr/src/cmd/bhyve/spinup_ap.h
index 090de091ba..2749ee95e4 100644
--- a/usr/src/cmd/bhyve/spinup_ap.h
+++ b/usr/src/cmd/bhyve/spinup_ap.h
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/spinup_ap.h 240912 2012-09-25 02:33:25Z neel $
+ * $FreeBSD$
*/
#ifndef _SPINUP_AP_H_
diff --git a/usr/src/cmd/bhyve/task_switch.c b/usr/src/cmd/bhyve/task_switch.c
new file mode 100644
index 0000000000..6138bcdef8
--- /dev/null
+++ b/usr/src/cmd/bhyve/task_switch.c
@@ -0,0 +1,939 @@
+/*-
+ * Copyright (c) 2014 Neel Natu <neel@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/_iovec.h>
+#include <sys/mman.h>
+
+#include <x86/psl.h>
+#include <x86/segments.h>
+#include <x86/specialreg.h>
+#include <machine/vmm.h>
+#include <machine/vmm_instruction_emul.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <vmmapi.h>
+
+#include "bhyverun.h"
+
+/*
+ * Using 'struct i386tss' is tempting but causes myriad sign extension
+ * issues because all of its fields are defined as signed integers.
+ */
+struct tss32 {
+ uint16_t tss_link;
+ uint16_t rsvd1;
+ uint32_t tss_esp0;
+ uint16_t tss_ss0;
+ uint16_t rsvd2;
+ uint32_t tss_esp1;
+ uint16_t tss_ss1;
+ uint16_t rsvd3;
+ uint32_t tss_esp2;
+ uint16_t tss_ss2;
+ uint16_t rsvd4;
+ uint32_t tss_cr3;
+ uint32_t tss_eip;
+ uint32_t tss_eflags;
+ uint32_t tss_eax;
+ uint32_t tss_ecx;
+ uint32_t tss_edx;
+ uint32_t tss_ebx;
+ uint32_t tss_esp;
+ uint32_t tss_ebp;
+ uint32_t tss_esi;
+ uint32_t tss_edi;
+ uint16_t tss_es;
+ uint16_t rsvd5;
+ uint16_t tss_cs;
+ uint16_t rsvd6;
+ uint16_t tss_ss;
+ uint16_t rsvd7;
+ uint16_t tss_ds;
+ uint16_t rsvd8;
+ uint16_t tss_fs;
+ uint16_t rsvd9;
+ uint16_t tss_gs;
+ uint16_t rsvd10;
+ uint16_t tss_ldt;
+ uint16_t rsvd11;
+ uint16_t tss_trap;
+ uint16_t tss_iomap;
+};
+static_assert(sizeof(struct tss32) == 104, "compile-time assertion failed");
+
+#define SEL_START(sel) (((sel) & ~0x7))
+#define SEL_LIMIT(sel) (((sel) | 0x7))
+#define TSS_BUSY(type) (((type) & 0x2) != 0)
+
+static uint64_t
+GETREG(struct vmctx *ctx, int vcpu, int reg)
+{
+ uint64_t val;
+ int error;
+
+ error = vm_get_register(ctx, vcpu, reg, &val);
+ assert(error == 0);
+ return (val);
+}
+
+static void
+SETREG(struct vmctx *ctx, int vcpu, int reg, uint64_t val)
+{
+ int error;
+
+ error = vm_set_register(ctx, vcpu, reg, val);
+ assert(error == 0);
+}
+
+static struct seg_desc
+usd_to_seg_desc(struct user_segment_descriptor *usd)
+{
+ struct seg_desc seg_desc;
+
+ seg_desc.base = (u_int)USD_GETBASE(usd);
+ if (usd->sd_gran)
+ seg_desc.limit = (u_int)(USD_GETLIMIT(usd) << 12) | 0xfff;
+ else
+ seg_desc.limit = (u_int)USD_GETLIMIT(usd);
+ seg_desc.access = usd->sd_type | usd->sd_dpl << 5 | usd->sd_p << 7;
+ seg_desc.access |= usd->sd_xx << 12;
+ seg_desc.access |= usd->sd_def32 << 14;
+ seg_desc.access |= usd->sd_gran << 15;
+
+ return (seg_desc);
+}
+
+/*
+ * Inject an exception with an error code that is a segment selector.
+ * The format of the error code is described in section 6.13, "Error Code",
+ * Intel SDM volume 3.
+ *
+ * Bit 0 (EXT) denotes whether the exception occurred during delivery
+ * of an external event like an interrupt.
+ *
+ * Bit 1 (IDT) indicates whether the selector points to a gate descriptor
+ * in the IDT.
+ *
+ * Bit 2(GDT/LDT) has the usual interpretation of Table Indicator (TI).
+ */
+static void
+sel_exception(struct vmctx *ctx, int vcpu, int vector, uint16_t sel, int ext)
+{
+ /*
+ * Bit 2 from the selector is retained as-is in the error code.
+ *
+ * Bit 1 can be safely cleared because none of the selectors
+ * encountered during task switch emulation refer to a task
+ * gate in the IDT.
+ *
+ * Bit 0 is set depending on the value of 'ext'.
+ */
+ sel &= ~0x3;
+ if (ext)
+ sel |= 0x1;
+ vm_inject_fault(ctx, vcpu, vector, 1, sel);
+}
+
+/*
+ * Return 0 if the selector 'sel' in within the limits of the GDT/LDT
+ * and non-zero otherwise.
+ */
+static int
+desc_table_limit_check(struct vmctx *ctx, int vcpu, uint16_t sel)
+{
+ uint64_t base;
+ uint32_t limit, access;
+ int error, reg;
+
+ reg = ISLDT(sel) ? VM_REG_GUEST_LDTR : VM_REG_GUEST_GDTR;
+ error = vm_get_desc(ctx, vcpu, reg, &base, &limit, &access);
+ assert(error == 0);
+
+ if (reg == VM_REG_GUEST_LDTR) {
+ if (SEG_DESC_UNUSABLE(access) || !SEG_DESC_PRESENT(access))
+ return (-1);
+ }
+
+ if (limit < SEL_LIMIT(sel))
+ return (-1);
+ else
+ return (0);
+}
+
+/*
+ * Read/write the segment descriptor 'desc' into the GDT/LDT slot referenced
+ * by the selector 'sel'.
+ *
+ * Returns 0 on success.
+ * Returns 1 if an exception was injected into the guest.
+ * Returns -1 otherwise.
+ */
+static int
+desc_table_rw(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging,
+ uint16_t sel, struct user_segment_descriptor *desc, bool doread,
+ int *faultptr)
+{
+ struct iovec iov[2];
+ uint64_t base;
+ uint32_t limit, access;
+ int error, reg;
+
+ reg = ISLDT(sel) ? VM_REG_GUEST_LDTR : VM_REG_GUEST_GDTR;
+ error = vm_get_desc(ctx, vcpu, reg, &base, &limit, &access);
+ assert(error == 0);
+ assert(limit >= SEL_LIMIT(sel));
+
+ error = vm_copy_setup(ctx, vcpu, paging, base + SEL_START(sel),
+ sizeof(*desc), doread ? PROT_READ : PROT_WRITE, iov, nitems(iov),
+ faultptr);
+ if (error || *faultptr)
+ return (error);
+
+ if (doread)
+ vm_copyin(ctx, vcpu, iov, desc, sizeof(*desc));
+ else
+ vm_copyout(ctx, vcpu, desc, iov, sizeof(*desc));
+ return (0);
+}
+
+static int
+desc_table_read(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging,
+ uint16_t sel, struct user_segment_descriptor *desc, int *faultptr)
+{
+ return (desc_table_rw(ctx, vcpu, paging, sel, desc, true, faultptr));
+}
+
+static int
+desc_table_write(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging,
+ uint16_t sel, struct user_segment_descriptor *desc, int *faultptr)
+{
+ return (desc_table_rw(ctx, vcpu, paging, sel, desc, false, faultptr));
+}
+
+/*
+ * Read the TSS descriptor referenced by 'sel' into 'desc'.
+ *
+ * Returns 0 on success.
+ * Returns 1 if an exception was injected into the guest.
+ * Returns -1 otherwise.
+ */
+static int
+read_tss_descriptor(struct vmctx *ctx, int vcpu, struct vm_task_switch *ts,
+ uint16_t sel, struct user_segment_descriptor *desc, int *faultptr)
+{
+ struct vm_guest_paging sup_paging;
+ int error;
+
+ assert(!ISLDT(sel));
+ assert(IDXSEL(sel) != 0);
+
+ /* Fetch the new TSS descriptor */
+ if (desc_table_limit_check(ctx, vcpu, sel)) {
+ if (ts->reason == TSR_IRET)
+ sel_exception(ctx, vcpu, IDT_TS, sel, ts->ext);
+ else
+ sel_exception(ctx, vcpu, IDT_GP, sel, ts->ext);
+ return (1);
+ }
+
+ sup_paging = ts->paging;
+ sup_paging.cpl = 0; /* implicit supervisor mode */
+ error = desc_table_read(ctx, vcpu, &sup_paging, sel, desc, faultptr);
+ return (error);
+}
+
+static bool
+code_desc(int sd_type)
+{
+ /* code descriptor */
+ return ((sd_type & 0x18) == 0x18);
+}
+
+static bool
+stack_desc(int sd_type)
+{
+ /* writable data descriptor */
+ return ((sd_type & 0x1A) == 0x12);
+}
+
+static bool
+data_desc(int sd_type)
+{
+ /* data descriptor or a readable code descriptor */
+ return ((sd_type & 0x18) == 0x10 || (sd_type & 0x1A) == 0x1A);
+}
+
+static bool
+ldt_desc(int sd_type)
+{
+
+ return (sd_type == SDT_SYSLDT);
+}
+
+/*
+ * Validate the descriptor 'seg_desc' associated with 'segment'.
+ */
+static int
+validate_seg_desc(struct vmctx *ctx, int vcpu, struct vm_task_switch *ts,
+ int segment, struct seg_desc *seg_desc, int *faultptr)
+{
+ struct vm_guest_paging sup_paging;
+ struct user_segment_descriptor usd;
+ int error, idtvec;
+ int cpl, dpl, rpl;
+ uint16_t sel, cs;
+ bool ldtseg, codeseg, stackseg, dataseg, conforming;
+
+ ldtseg = codeseg = stackseg = dataseg = false;
+ switch (segment) {
+ case VM_REG_GUEST_LDTR:
+ ldtseg = true;
+ break;
+ case VM_REG_GUEST_CS:
+ codeseg = true;
+ break;
+ case VM_REG_GUEST_SS:
+ stackseg = true;
+ break;
+ case VM_REG_GUEST_DS:
+ case VM_REG_GUEST_ES:
+ case VM_REG_GUEST_FS:
+ case VM_REG_GUEST_GS:
+ dataseg = true;
+ break;
+ default:
+ assert(0);
+ }
+
+ /* Get the segment selector */
+ sel = GETREG(ctx, vcpu, segment);
+
+ /* LDT selector must point into the GDT */
+ if (ldtseg && ISLDT(sel)) {
+ sel_exception(ctx, vcpu, IDT_TS, sel, ts->ext);
+ return (1);
+ }
+
+ /* Descriptor table limit check */
+ if (desc_table_limit_check(ctx, vcpu, sel)) {
+ sel_exception(ctx, vcpu, IDT_TS, sel, ts->ext);
+ return (1);
+ }
+
+ /* NULL selector */
+ if (IDXSEL(sel) == 0) {
+ /* Code and stack segment selectors cannot be NULL */
+ if (codeseg || stackseg) {
+ sel_exception(ctx, vcpu, IDT_TS, sel, ts->ext);
+ return (1);
+ }
+ seg_desc->base = 0;
+ seg_desc->limit = 0;
+ seg_desc->access = 0x10000; /* unusable */
+ return (0);
+ }
+
+ /* Read the descriptor from the GDT/LDT */
+ sup_paging = ts->paging;
+ sup_paging.cpl = 0; /* implicit supervisor mode */
+ error = desc_table_read(ctx, vcpu, &sup_paging, sel, &usd, faultptr);
+ if (error || *faultptr)
+ return (error);
+
+ /* Verify that the descriptor type is compatible with the segment */
+ if ((ldtseg && !ldt_desc(usd.sd_type)) ||
+ (codeseg && !code_desc(usd.sd_type)) ||
+ (dataseg && !data_desc(usd.sd_type)) ||
+ (stackseg && !stack_desc(usd.sd_type))) {
+ sel_exception(ctx, vcpu, IDT_TS, sel, ts->ext);
+ return (1);
+ }
+
+ /* Segment must be marked present */
+ if (!usd.sd_p) {
+ if (ldtseg)
+ idtvec = IDT_TS;
+ else if (stackseg)
+ idtvec = IDT_SS;
+ else
+ idtvec = IDT_NP;
+ sel_exception(ctx, vcpu, idtvec, sel, ts->ext);
+ return (1);
+ }
+
+ cs = GETREG(ctx, vcpu, VM_REG_GUEST_CS);
+ cpl = cs & SEL_RPL_MASK;
+ rpl = sel & SEL_RPL_MASK;
+ dpl = usd.sd_dpl;
+
+ if (stackseg && (rpl != cpl || dpl != cpl)) {
+ sel_exception(ctx, vcpu, IDT_TS, sel, ts->ext);
+ return (1);
+ }
+
+ if (codeseg) {
+ conforming = (usd.sd_type & 0x4) ? true : false;
+ if ((conforming && (cpl < dpl)) ||
+ (!conforming && (cpl != dpl))) {
+ sel_exception(ctx, vcpu, IDT_TS, sel, ts->ext);
+ return (1);
+ }
+ }
+
+ if (dataseg) {
+ /*
+ * A data segment is always non-conforming except when it's
+ * descriptor is a readable, conforming code segment.
+ */
+ if (code_desc(usd.sd_type) && (usd.sd_type & 0x4) != 0)
+ conforming = true;
+ else
+ conforming = false;
+
+ if (!conforming && (rpl > dpl || cpl > dpl)) {
+ sel_exception(ctx, vcpu, IDT_TS, sel, ts->ext);
+ return (1);
+ }
+ }
+ *seg_desc = usd_to_seg_desc(&usd);
+ return (0);
+}
+
+static void
+tss32_save(struct vmctx *ctx, int vcpu, struct vm_task_switch *task_switch,
+ uint32_t eip, struct tss32 *tss, struct iovec *iov)
+{
+
+ /* General purpose registers */
+ tss->tss_eax = GETREG(ctx, vcpu, VM_REG_GUEST_RAX);
+ tss->tss_ecx = GETREG(ctx, vcpu, VM_REG_GUEST_RCX);
+ tss->tss_edx = GETREG(ctx, vcpu, VM_REG_GUEST_RDX);
+ tss->tss_ebx = GETREG(ctx, vcpu, VM_REG_GUEST_RBX);
+ tss->tss_esp = GETREG(ctx, vcpu, VM_REG_GUEST_RSP);
+ tss->tss_ebp = GETREG(ctx, vcpu, VM_REG_GUEST_RBP);
+ tss->tss_esi = GETREG(ctx, vcpu, VM_REG_GUEST_RSI);
+ tss->tss_edi = GETREG(ctx, vcpu, VM_REG_GUEST_RDI);
+
+ /* Segment selectors */
+ tss->tss_es = GETREG(ctx, vcpu, VM_REG_GUEST_ES);
+ tss->tss_cs = GETREG(ctx, vcpu, VM_REG_GUEST_CS);
+ tss->tss_ss = GETREG(ctx, vcpu, VM_REG_GUEST_SS);
+ tss->tss_ds = GETREG(ctx, vcpu, VM_REG_GUEST_DS);
+ tss->tss_fs = GETREG(ctx, vcpu, VM_REG_GUEST_FS);
+ tss->tss_gs = GETREG(ctx, vcpu, VM_REG_GUEST_GS);
+
+ /* eflags and eip */
+ tss->tss_eflags = GETREG(ctx, vcpu, VM_REG_GUEST_RFLAGS);
+ if (task_switch->reason == TSR_IRET)
+ tss->tss_eflags &= ~PSL_NT;
+ tss->tss_eip = eip;
+
+ /* Copy updated old TSS into guest memory */
+ vm_copyout(ctx, vcpu, tss, iov, sizeof(struct tss32));
+}
+
+static void
+update_seg_desc(struct vmctx *ctx, int vcpu, int reg, struct seg_desc *sd)
+{
+ int error;
+
+ error = vm_set_desc(ctx, vcpu, reg, sd->base, sd->limit, sd->access);
+ assert(error == 0);
+}
+
+/*
+ * Update the vcpu registers to reflect the state of the new task.
+ */
+static int
+tss32_restore(struct vmctx *ctx, int vcpu, struct vm_task_switch *ts,
+ uint16_t ot_sel, struct tss32 *tss, struct iovec *iov, int *faultptr)
+{
+ struct seg_desc seg_desc, seg_desc2;
+ uint64_t *pdpte, maxphyaddr, reserved;
+ uint32_t eflags;
+ int error, i;
+ bool nested;
+
+ nested = false;
+ if (ts->reason != TSR_IRET && ts->reason != TSR_JMP) {
+ tss->tss_link = ot_sel;
+ nested = true;
+ }
+
+ eflags = tss->tss_eflags;
+ if (nested)
+ eflags |= PSL_NT;
+
+ /* LDTR */
+ SETREG(ctx, vcpu, VM_REG_GUEST_LDTR, tss->tss_ldt);
+
+ /* PBDR */
+ if (ts->paging.paging_mode != PAGING_MODE_FLAT) {
+ if (ts->paging.paging_mode == PAGING_MODE_PAE) {
+ /*
+ * XXX Assuming 36-bit MAXPHYADDR.
+ */
+ maxphyaddr = (1UL << 36) - 1;
+ pdpte = paddr_guest2host(ctx, tss->tss_cr3 & ~0x1f, 32);
+ for (i = 0; i < 4; i++) {
+ /* Check reserved bits if the PDPTE is valid */
+ if (!(pdpte[i] & 0x1))
+ continue;
+ /*
+ * Bits 2:1, 8:5 and bits above the processor's
+ * maximum physical address are reserved.
+ */
+ reserved = ~maxphyaddr | 0x1E6;
+ if (pdpte[i] & reserved) {
+ vm_inject_gp(ctx, vcpu);
+ return (1);
+ }
+ }
+ SETREG(ctx, vcpu, VM_REG_GUEST_PDPTE0, pdpte[0]);
+ SETREG(ctx, vcpu, VM_REG_GUEST_PDPTE1, pdpte[1]);
+ SETREG(ctx, vcpu, VM_REG_GUEST_PDPTE2, pdpte[2]);
+ SETREG(ctx, vcpu, VM_REG_GUEST_PDPTE3, pdpte[3]);
+ }
+ SETREG(ctx, vcpu, VM_REG_GUEST_CR3, tss->tss_cr3);
+ ts->paging.cr3 = tss->tss_cr3;
+ }
+
+ /* eflags and eip */
+ SETREG(ctx, vcpu, VM_REG_GUEST_RFLAGS, eflags);
+ SETREG(ctx, vcpu, VM_REG_GUEST_RIP, tss->tss_eip);
+
+ /* General purpose registers */
+ SETREG(ctx, vcpu, VM_REG_GUEST_RAX, tss->tss_eax);
+ SETREG(ctx, vcpu, VM_REG_GUEST_RCX, tss->tss_ecx);
+ SETREG(ctx, vcpu, VM_REG_GUEST_RDX, tss->tss_edx);
+ SETREG(ctx, vcpu, VM_REG_GUEST_RBX, tss->tss_ebx);
+ SETREG(ctx, vcpu, VM_REG_GUEST_RSP, tss->tss_esp);
+ SETREG(ctx, vcpu, VM_REG_GUEST_RBP, tss->tss_ebp);
+ SETREG(ctx, vcpu, VM_REG_GUEST_RSI, tss->tss_esi);
+ SETREG(ctx, vcpu, VM_REG_GUEST_RDI, tss->tss_edi);
+
+ /* Segment selectors */
+ SETREG(ctx, vcpu, VM_REG_GUEST_ES, tss->tss_es);
+ SETREG(ctx, vcpu, VM_REG_GUEST_CS, tss->tss_cs);
+ SETREG(ctx, vcpu, VM_REG_GUEST_SS, tss->tss_ss);
+ SETREG(ctx, vcpu, VM_REG_GUEST_DS, tss->tss_ds);
+ SETREG(ctx, vcpu, VM_REG_GUEST_FS, tss->tss_fs);
+ SETREG(ctx, vcpu, VM_REG_GUEST_GS, tss->tss_gs);
+
+ /*
+ * If this is a nested task then write out the new TSS to update
+ * the previous link field.
+ */
+ if (nested)
+ vm_copyout(ctx, vcpu, tss, iov, sizeof(*tss));
+
+ /* Validate segment descriptors */
+ error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_LDTR, &seg_desc,
+ faultptr);
+ if (error || *faultptr)
+ return (error);
+ update_seg_desc(ctx, vcpu, VM_REG_GUEST_LDTR, &seg_desc);
+
+ /*
+ * Section "Checks on Guest Segment Registers", Intel SDM, Vol 3.
+ *
+ * The SS and CS attribute checks on VM-entry are inter-dependent so
+ * we need to make sure that both segments are valid before updating
+ * either of them. This ensures that the VMCS state can pass the
+ * VM-entry checks so the guest can handle any exception injected
+ * during task switch emulation.
+ */
+ error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_CS, &seg_desc,
+ faultptr);
+ if (error || *faultptr)
+ return (error);
+
+ error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_SS, &seg_desc2,
+ faultptr);
+ if (error || *faultptr)
+ return (error);
+ update_seg_desc(ctx, vcpu, VM_REG_GUEST_CS, &seg_desc);
+ update_seg_desc(ctx, vcpu, VM_REG_GUEST_SS, &seg_desc2);
+ ts->paging.cpl = tss->tss_cs & SEL_RPL_MASK;
+
+ error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_DS, &seg_desc,
+ faultptr);
+ if (error || *faultptr)
+ return (error);
+ update_seg_desc(ctx, vcpu, VM_REG_GUEST_DS, &seg_desc);
+
+ error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_ES, &seg_desc,
+ faultptr);
+ if (error || *faultptr)
+ return (error);
+ update_seg_desc(ctx, vcpu, VM_REG_GUEST_ES, &seg_desc);
+
+ error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_FS, &seg_desc,
+ faultptr);
+ if (error || *faultptr)
+ return (error);
+ update_seg_desc(ctx, vcpu, VM_REG_GUEST_FS, &seg_desc);
+
+ error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_GS, &seg_desc,
+ faultptr);
+ if (error || *faultptr)
+ return (error);
+ update_seg_desc(ctx, vcpu, VM_REG_GUEST_GS, &seg_desc);
+
+ return (0);
+}
+
+/*
+ * Push an error code on the stack of the new task. This is needed if the
+ * task switch was triggered by a hardware exception that causes an error
+ * code to be saved (e.g. #PF).
+ */
+static int
+push_errcode(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging,
+ int task_type, uint32_t errcode, int *faultptr)
+{
+ struct iovec iov[2];
+ struct seg_desc seg_desc;
+ int stacksize, bytes, error;
+ uint64_t gla, cr0, rflags;
+ uint32_t esp;
+ uint16_t stacksel;
+
+ *faultptr = 0;
+
+ cr0 = GETREG(ctx, vcpu, VM_REG_GUEST_CR0);
+ rflags = GETREG(ctx, vcpu, VM_REG_GUEST_RFLAGS);
+ stacksel = GETREG(ctx, vcpu, VM_REG_GUEST_SS);
+
+ error = vm_get_desc(ctx, vcpu, VM_REG_GUEST_SS, &seg_desc.base,
+ &seg_desc.limit, &seg_desc.access);
+ assert(error == 0);
+
+ /*
+ * Section "Error Code" in the Intel SDM vol 3: the error code is
+ * pushed on the stack as a doubleword or word (depending on the
+ * default interrupt, trap or task gate size).
+ */
+ if (task_type == SDT_SYS386BSY || task_type == SDT_SYS386TSS)
+ bytes = 4;
+ else
+ bytes = 2;
+
+ /*
+ * PUSH instruction from Intel SDM vol 2: the 'B' flag in the
+ * stack-segment descriptor determines the size of the stack
+ * pointer outside of 64-bit mode.
+ */
+ if (SEG_DESC_DEF32(seg_desc.access))
+ stacksize = 4;
+ else
+ stacksize = 2;
+
+ esp = GETREG(ctx, vcpu, VM_REG_GUEST_RSP);
+ esp -= bytes;
+
+ if (vie_calculate_gla(paging->cpu_mode, VM_REG_GUEST_SS,
+ &seg_desc, esp, bytes, stacksize, PROT_WRITE, &gla)) {
+ sel_exception(ctx, vcpu, IDT_SS, stacksel, 1);
+ *faultptr = 1;
+ return (0);
+ }
+
+ if (vie_alignment_check(paging->cpl, bytes, cr0, rflags, gla)) {
+ vm_inject_ac(ctx, vcpu, 1);
+ *faultptr = 1;
+ return (0);
+ }
+
+ error = vm_copy_setup(ctx, vcpu, paging, gla, bytes, PROT_WRITE,
+ iov, nitems(iov), faultptr);
+ if (error || *faultptr)
+ return (error);
+
+ vm_copyout(ctx, vcpu, &errcode, iov, bytes);
+ SETREG(ctx, vcpu, VM_REG_GUEST_RSP, esp);
+ return (0);
+}
+
+/*
+ * Evaluate return value from helper functions and potentially return to
+ * the VM run loop.
+ */
+#define CHKERR(error,fault) \
+ do { \
+ assert((error == 0) || (error == EFAULT)); \
+ if (error) \
+ return (VMEXIT_ABORT); \
+ else if (fault) \
+ return (VMEXIT_CONTINUE); \
+ } while (0)
+
+int
+vmexit_task_switch(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
+{
+ struct seg_desc nt;
+ struct tss32 oldtss, newtss;
+ struct vm_task_switch *task_switch;
+ struct vm_guest_paging *paging, sup_paging;
+ struct user_segment_descriptor nt_desc, ot_desc;
+ struct iovec nt_iov[2], ot_iov[2];
+ uint64_t cr0, ot_base;
+ uint32_t eip, ot_lim, access;
+ int error, ext, fault, minlimit, nt_type, ot_type, vcpu;
+ enum task_switch_reason reason;
+ uint16_t nt_sel, ot_sel;
+
+ task_switch = &vmexit->u.task_switch;
+ nt_sel = task_switch->tsssel;
+ ext = vmexit->u.task_switch.ext;
+ reason = vmexit->u.task_switch.reason;
+ paging = &vmexit->u.task_switch.paging;
+ vcpu = *pvcpu;
+
+ assert(paging->cpu_mode == CPU_MODE_PROTECTED);
+
+ /*
+ * Calculate the instruction pointer to store in the old TSS.
+ */
+ eip = vmexit->rip + vmexit->inst_length;
+
+ /*
+ * Section 4.6, "Access Rights" in Intel SDM Vol 3.
+ * The following page table accesses are implicitly supervisor mode:
+ * - accesses to GDT or LDT to load segment descriptors
+ * - accesses to the task state segment during task switch
+ */
+ sup_paging = *paging;
+ sup_paging.cpl = 0; /* implicit supervisor mode */
+
+ /* Fetch the new TSS descriptor */
+ error = read_tss_descriptor(ctx, vcpu, task_switch, nt_sel, &nt_desc,
+ &fault);
+ CHKERR(error, fault);
+
+ nt = usd_to_seg_desc(&nt_desc);
+
+ /* Verify the type of the new TSS */
+ nt_type = SEG_DESC_TYPE(nt.access);
+ if (nt_type != SDT_SYS386BSY && nt_type != SDT_SYS386TSS &&
+ nt_type != SDT_SYS286BSY && nt_type != SDT_SYS286TSS) {
+ sel_exception(ctx, vcpu, IDT_TS, nt_sel, ext);
+ goto done;
+ }
+
+ /* TSS descriptor must have present bit set */
+ if (!SEG_DESC_PRESENT(nt.access)) {
+ sel_exception(ctx, vcpu, IDT_NP, nt_sel, ext);
+ goto done;
+ }
+
+ /*
+ * TSS must have a minimum length of 104 bytes for a 32-bit TSS and
+ * 44 bytes for a 16-bit TSS.
+ */
+ if (nt_type == SDT_SYS386BSY || nt_type == SDT_SYS386TSS)
+ minlimit = 104 - 1;
+ else if (nt_type == SDT_SYS286BSY || nt_type == SDT_SYS286TSS)
+ minlimit = 44 - 1;
+ else
+ minlimit = 0;
+
+ assert(minlimit > 0);
+ if (nt.limit < minlimit) {
+ sel_exception(ctx, vcpu, IDT_TS, nt_sel, ext);
+ goto done;
+ }
+
+ /* TSS must be busy if task switch is due to IRET */
+ if (reason == TSR_IRET && !TSS_BUSY(nt_type)) {
+ sel_exception(ctx, vcpu, IDT_TS, nt_sel, ext);
+ goto done;
+ }
+
+ /*
+ * TSS must be available (not busy) if task switch reason is
+ * CALL, JMP, exception or interrupt.
+ */
+ if (reason != TSR_IRET && TSS_BUSY(nt_type)) {
+ sel_exception(ctx, vcpu, IDT_GP, nt_sel, ext);
+ goto done;
+ }
+
+ /* Fetch the new TSS */
+ error = vm_copy_setup(ctx, vcpu, &sup_paging, nt.base, minlimit + 1,
+ PROT_READ | PROT_WRITE, nt_iov, nitems(nt_iov), &fault);
+ CHKERR(error, fault);
+ vm_copyin(ctx, vcpu, nt_iov, &newtss, minlimit + 1);
+
+ /* Get the old TSS selector from the guest's task register */
+ ot_sel = GETREG(ctx, vcpu, VM_REG_GUEST_TR);
+ if (ISLDT(ot_sel) || IDXSEL(ot_sel) == 0) {
+ /*
+ * This might happen if a task switch was attempted without
+ * ever loading the task register with LTR. In this case the
+ * TR would contain the values from power-on:
+ * (sel = 0, base = 0, limit = 0xffff).
+ */
+ sel_exception(ctx, vcpu, IDT_TS, ot_sel, task_switch->ext);
+ goto done;
+ }
+
+ /* Get the old TSS base and limit from the guest's task register */
+ error = vm_get_desc(ctx, vcpu, VM_REG_GUEST_TR, &ot_base, &ot_lim,
+ &access);
+ assert(error == 0);
+ assert(!SEG_DESC_UNUSABLE(access) && SEG_DESC_PRESENT(access));
+ ot_type = SEG_DESC_TYPE(access);
+ assert(ot_type == SDT_SYS386BSY || ot_type == SDT_SYS286BSY);
+
+ /* Fetch the old TSS descriptor */
+ error = read_tss_descriptor(ctx, vcpu, task_switch, ot_sel, &ot_desc,
+ &fault);
+ CHKERR(error, fault);
+
+ /* Get the old TSS */
+ error = vm_copy_setup(ctx, vcpu, &sup_paging, ot_base, minlimit + 1,
+ PROT_READ | PROT_WRITE, ot_iov, nitems(ot_iov), &fault);
+ CHKERR(error, fault);
+ vm_copyin(ctx, vcpu, ot_iov, &oldtss, minlimit + 1);
+
+ /*
+ * Clear the busy bit in the old TSS descriptor if the task switch
+ * due to an IRET or JMP instruction.
+ */
+ if (reason == TSR_IRET || reason == TSR_JMP) {
+ ot_desc.sd_type &= ~0x2;
+ error = desc_table_write(ctx, vcpu, &sup_paging, ot_sel,
+ &ot_desc, &fault);
+ CHKERR(error, fault);
+ }
+
+ if (nt_type == SDT_SYS286BSY || nt_type == SDT_SYS286TSS) {
+ fprintf(stderr, "Task switch to 16-bit TSS not supported\n");
+ return (VMEXIT_ABORT);
+ }
+
+ /* Save processor state in old TSS */
+ tss32_save(ctx, vcpu, task_switch, eip, &oldtss, ot_iov);
+
+ /*
+ * If the task switch was triggered for any reason other than IRET
+ * then set the busy bit in the new TSS descriptor.
+ */
+ if (reason != TSR_IRET) {
+ nt_desc.sd_type |= 0x2;
+ error = desc_table_write(ctx, vcpu, &sup_paging, nt_sel,
+ &nt_desc, &fault);
+ CHKERR(error, fault);
+ }
+
+ /* Update task register to point at the new TSS */
+ SETREG(ctx, vcpu, VM_REG_GUEST_TR, nt_sel);
+
+ /* Update the hidden descriptor state of the task register */
+ nt = usd_to_seg_desc(&nt_desc);
+ update_seg_desc(ctx, vcpu, VM_REG_GUEST_TR, &nt);
+
+ /* Set CR0.TS */
+ cr0 = GETREG(ctx, vcpu, VM_REG_GUEST_CR0);
+ SETREG(ctx, vcpu, VM_REG_GUEST_CR0, cr0 | CR0_TS);
+
+ /*
+ * We are now committed to the task switch. Any exceptions encountered
+ * after this point will be handled in the context of the new task and
+ * the saved instruction pointer will belong to the new task.
+ */
+ error = vm_set_register(ctx, vcpu, VM_REG_GUEST_RIP, newtss.tss_eip);
+ assert(error == 0);
+
+ /* Load processor state from new TSS */
+ error = tss32_restore(ctx, vcpu, task_switch, ot_sel, &newtss, nt_iov,
+ &fault);
+ CHKERR(error, fault);
+
+ /*
+ * Section "Interrupt Tasks" in Intel SDM, Vol 3: if an exception
+ * caused an error code to be generated, this error code is copied
+ * to the stack of the new task.
+ */
+ if (task_switch->errcode_valid) {
+ assert(task_switch->ext);
+ assert(task_switch->reason == TSR_IDT_GATE);
+ error = push_errcode(ctx, vcpu, &task_switch->paging, nt_type,
+ task_switch->errcode, &fault);
+ CHKERR(error, fault);
+ }
+
+ /*
+ * Treatment of virtual-NMI blocking if NMI is delivered through
+ * a task gate.
+ *
+ * Section "Architectural State Before A VM Exit", Intel SDM, Vol3:
+ * If the virtual NMIs VM-execution control is 1, VM entry injects
+ * an NMI, and delivery of the NMI causes a task switch that causes
+ * a VM exit, virtual-NMI blocking is in effect before the VM exit
+ * commences.
+ *
+ * Thus, virtual-NMI blocking is in effect at the time of the task
+ * switch VM exit.
+ */
+
+ /*
+ * Treatment of virtual-NMI unblocking on IRET from NMI handler task.
+ *
+ * Section "Changes to Instruction Behavior in VMX Non-Root Operation"
+ * If "virtual NMIs" control is 1 IRET removes any virtual-NMI blocking.
+ * This unblocking of virtual-NMI occurs even if IRET causes a fault.
+ *
+ * Thus, virtual-NMI blocking is cleared at the time of the task switch
+ * VM exit.
+ */
+
+ /*
+ * If the task switch was triggered by an event delivered through
+ * the IDT then extinguish the pending event from the vcpu's
+ * exitintinfo.
+ */
+ if (task_switch->reason == TSR_IDT_GATE) {
+ error = vm_set_intinfo(ctx, vcpu, 0);
+ assert(error == 0);
+ }
+
+ /*
+ * XXX should inject debug exception if 'T' bit is 1
+ */
+done:
+ return (VMEXIT_CONTINUE);
+}
diff --git a/usr/src/cmd/bhyve/uart_emul.c b/usr/src/cmd/bhyve/uart_emul.c
index f9533ecfae..711e94430f 100644
--- a/usr/src/cmd/bhyve/uart_emul.c
+++ b/usr/src/cmd/bhyve/uart_emul.c
@@ -24,7 +24,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/uart_emul.c 257293 2013-10-29 00:18:11Z neel $
+ * $FreeBSD$
*/
/*
* This file and its contents are supplied under the terms of the
@@ -41,10 +41,13 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/usr.sbin/bhyve/uart_emul.c 257293 2013-10-29 00:18:11Z neel $");
+__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <dev/ic/ns16550.h>
+#ifndef WITHOUT_CAPSICUM
+#include <sys/capsicum.h>
+#endif
#ifndef __FreeBSD__
#include <sys/socket.h>
@@ -53,11 +56,15 @@ __FBSDID("$FreeBSD: head/usr.sbin/bhyve/uart_emul.c 257293 2013-10-29 00:18:11Z
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <stdbool.h>
#include <string.h>
#include <pthread.h>
+#include <sysexits.h>
#ifndef __FreeBSD__
#include <errno.h>
#include <fcntl.h>
@@ -96,6 +103,8 @@ __FBSDID("$FreeBSD: head/usr.sbin/bhyve/uart_emul.c 257293 2013-10-29 00:18:11Z
#define FIFOSZ 16
static bool uart_stdio; /* stdio in use for i/o */
+static struct termios tio_stdio_orig;
+
#ifndef __FreeBSD__
static bool uart_bcons; /* bhyveconsole in use for i/o */
#endif
@@ -119,6 +128,12 @@ struct fifo {
int size; /* size of the fifo */
};
+struct ttyfd {
+ bool opened;
+ int fd; /* tty device file descriptor */
+ struct termios tio_orig, tio_new; /* I/O Terminals */
+};
+
struct uart_softc {
pthread_mutex_t mtx; /* protects all softc elements */
uint8_t data; /* Data register (R/W) */
@@ -134,9 +149,11 @@ struct uart_softc {
uint8_t dlh; /* Baudrate divisor latch MSB */
struct fifo rxfifo;
+#ifdef __FreeBSD__
+ struct mevent *mev;
+#endif
- bool opened;
- bool stdio;
+ struct ttyfd tty;
#ifndef __FreeBSD__
bool bcons;
struct {
@@ -160,63 +177,46 @@ static void uart_tty_drain(struct uart_softc *sc);
static int uart_bcons_drain(struct uart_softc *sc);
#endif
-static struct termios tio_orig, tio_new; /* I/O Terminals */
-
static void
ttyclose(void)
{
- tcsetattr(STDIN_FILENO, TCSANOW, &tio_orig);
+ tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig);
}
static void
-ttyopen(void)
+ttyopen(struct ttyfd *tf)
{
- tcgetattr(STDIN_FILENO, &tio_orig);
+ tcgetattr(tf->fd, &tf->tio_orig);
- tio_new = tio_orig;
- cfmakeraw(&tio_new);
- tcsetattr(STDIN_FILENO, TCSANOW, &tio_new);
+ tf->tio_new = tf->tio_orig;
+ cfmakeraw(&tf->tio_new);
+ tf->tio_new.c_cflag |= CLOCAL;
+ tcsetattr(tf->fd, TCSANOW, &tf->tio_new);
- atexit(ttyclose);
-}
-
-static bool
-tty_char_available(void)
-{
- fd_set rfds;
- struct timeval tv;
-
- FD_ZERO(&rfds);
- FD_SET(STDIN_FILENO, &rfds);
- tv.tv_sec = 0;
- tv.tv_usec = 0;
- if (select(STDIN_FILENO + 1, &rfds, NULL, NULL, &tv) > 0 ) {
- return (true);
- } else {
- return (false);
+ if (tf->fd == STDIN_FILENO) {
+ tio_stdio_orig = tf->tio_orig;
+ atexit(ttyclose);
}
}
static int
-ttyread(void)
+ttyread(struct ttyfd *tf)
{
- char rb;
+ unsigned char rb;
- if (tty_char_available()) {
- read(STDIN_FILENO, &rb, 1);
- return (rb & 0xff);
- } else {
+ if (read(tf->fd, &rb, 1) == 1)
+ return (rb);
+ else
return (-1);
- }
}
static void
-ttywrite(unsigned char wb)
+ttywrite(struct ttyfd *tf, unsigned char wb)
{
- (void)write(STDIN_FILENO, &wb, 1);
+ (void)write(tf->fd, &wb, 1);
}
#ifndef __FreeBSD__
@@ -228,67 +228,156 @@ bconswrite(struct uart_softc *sc, unsigned char wb)
#endif
static void
-fifo_reset(struct fifo *fifo, int size)
+rxfifo_reset(struct uart_softc *sc, int size)
{
+ char flushbuf[32];
+ struct fifo *fifo;
+ ssize_t nread;
+#ifdef __FreeBSD__
+ int error;
+#endif
+
+ fifo = &sc->rxfifo;
bzero(fifo, sizeof(struct fifo));
fifo->size = size;
+
+ if (sc->tty.opened) {
+ /*
+ * Flush any unread input from the tty buffer.
+ */
+ while (1) {
+ nread = read(sc->tty.fd, flushbuf, sizeof(flushbuf));
+ if (nread != sizeof(flushbuf))
+ break;
+ }
+
+#ifdef __FreeBSD__
+ /*
+ * Enable mevent to trigger when new characters are available
+ * on the tty fd.
+ */
+ error = mevent_enable(sc->mev);
+ assert(error == 0);
+#endif
+ }
}
static int
-fifo_putchar(struct fifo *fifo, uint8_t ch)
+rxfifo_available(struct uart_softc *sc)
{
+ struct fifo *fifo;
+
+ fifo = &sc->rxfifo;
+ return (fifo->num < fifo->size);
+}
+
+static int
+rxfifo_putchar(struct uart_softc *sc, uint8_t ch)
+{
+ struct fifo *fifo;
+#ifdef __FreeBSD__
+ int error;
+#endif
+
+ fifo = &sc->rxfifo;
if (fifo->num < fifo->size) {
fifo->buf[fifo->windex] = ch;
fifo->windex = (fifo->windex + 1) % fifo->size;
fifo->num++;
+ if (!rxfifo_available(sc)) {
+ if (sc->tty.opened) {
+#ifdef __FreeBSD__
+ /*
+ * Disable mevent callback if the FIFO is full.
+ */
+ error = mevent_disable(sc->mev);
+ assert(error == 0);
+#endif
+ }
+ }
return (0);
} else
return (-1);
}
static int
-fifo_getchar(struct fifo *fifo)
+rxfifo_getchar(struct uart_softc *sc)
{
- int c;
+ struct fifo *fifo;
+ int c, wasfull;
+#ifdef __FreeBSD__
+ int error;
+#endif
+ wasfull = 0;
+ fifo = &sc->rxfifo;
if (fifo->num > 0) {
+ if (!rxfifo_available(sc))
+ wasfull = 1;
c = fifo->buf[fifo->rindex];
fifo->rindex = (fifo->rindex + 1) % fifo->size;
fifo->num--;
+ if (wasfull) {
+ if (sc->tty.opened) {
+#ifdef __FreeBSD__
+ error = mevent_enable(sc->mev);
+ assert(error == 0);
+#endif
+ }
+ }
return (c);
} else
return (-1);
}
static int
-fifo_numchars(struct fifo *fifo)
+rxfifo_numchars(struct uart_softc *sc)
{
+ struct fifo *fifo = &sc->rxfifo;
return (fifo->num);
}
-static int
-fifo_available(struct fifo *fifo)
-{
-
- return (fifo->num < fifo->size);
-}
-
static void
uart_opentty(struct uart_softc *sc)
{
+ ttyopen(&sc->tty);
#ifdef __FreeBSD__
- struct mevent *mev;
+ sc->mev = mevent_add(sc->tty.fd, EVF_READ, uart_drain, sc);
+ assert(sc->mev != NULL);
#endif
+}
- assert(!sc->opened && sc->stdio);
+static uint8_t
+modem_status(uint8_t mcr)
+{
+ uint8_t msr;
- ttyopen();
-#ifdef __FreeBSD__
- mev = mevent_add(STDIN_FILENO, EVF_READ, uart_drain, sc);
- assert(mev);
-#endif
+ if (mcr & MCR_LOOPBACK) {
+ /*
+ * In the loopback mode certain bits from the MCR are
+ * reflected back into MSR.
+ */
+ msr = 0;
+ if (mcr & MCR_RTS)
+ msr |= MSR_CTS;
+ if (mcr & MCR_DTR)
+ msr |= MSR_DSR;
+ if (mcr & MCR_OUT1)
+ msr |= MSR_RI;
+ if (mcr & MCR_OUT2)
+ msr |= MSR_DCD;
+ } else {
+ /*
+ * Always assert DCD and DSR so tty open doesn't block
+ * even if CLOCAL is turned off.
+ */
+ msr = MSR_DCD | MSR_DSR;
+ }
+ assert((msr & MSR_DELTA_MASK) == 0);
+
+ return (msr);
}
/*
@@ -305,7 +394,7 @@ uart_intr_reason(struct uart_softc *sc)
if ((sc->lsr & LSR_OE) != 0 && (sc->ier & IER_ERLS) != 0)
return (IIR_RLS);
- else if (fifo_numchars(&sc->rxfifo) > 0 && (sc->ier & IER_ERXRDY) != 0)
+ else if (rxfifo_numchars(sc) > 0 && (sc->ier & IER_ERXRDY) != 0)
return (IIR_RXTOUT);
else if (sc->thre_int_pending && (sc->ier & IER_ETXRDY) != 0)
return (IIR_TXRDY);
@@ -323,8 +412,9 @@ uart_reset(struct uart_softc *sc)
divisor = DEFAULT_RCLK / DEFAULT_BAUD / 16;
sc->dll = divisor;
sc->dlh = divisor >> 16;
+ sc->msr = modem_status(sc->mcr);
- fifo_reset(&sc->rxfifo, 1); /* no fifo until enabled by software */
+ rxfifo_reset(sc, 1); /* no fifo until enabled by software */
}
/*
@@ -353,7 +443,7 @@ uart_drain(int fd, enum ev_type ev, void *arg)
sc = arg;
- assert(fd == STDIN_FILENO);
+ assert(fd == sc->tty.fd);
assert(ev == EVF_READ);
/*
@@ -364,11 +454,11 @@ uart_drain(int fd, enum ev_type ev, void *arg)
pthread_mutex_lock(&sc->mtx);
if ((sc->mcr & MCR_LOOPBACK) != 0) {
- (void) ttyread();
+ (void) ttyread(&sc->tty);
} else {
- while (fifo_available(&sc->rxfifo) &&
- ((ch = ttyread()) != -1)) {
- fifo_putchar(&sc->rxfifo, ch);
+ while (rxfifo_available(sc) &&
+ ((ch = ttyread(&sc->tty)) != -1)) {
+ rxfifo_putchar(sc, ch);
}
uart_toggle_intr(sc);
}
@@ -388,11 +478,11 @@ uart_tty_drain(struct uart_softc *sc)
pthread_mutex_lock(&sc->mtx);
if ((sc->mcr & MCR_LOOPBACK) != 0) {
- (void) ttyread();
+ (void) ttyread(&sc->tty);
} else {
- while (fifo_available(&sc->rxfifo) &&
- ((ch = ttyread()) != -1)) {
- fifo_putchar(&sc->rxfifo, ch);
+ while (rxfifo_available(sc) &&
+ ((ch = ttyread(&sc->tty)) != -1)) {
+ rxfifo_putchar(sc, ch);
}
uart_toggle_intr(sc);
}
@@ -431,8 +521,8 @@ uart_bcons_drain(struct uart_softc *sc)
break;
}
- if (fifo_available(&sc->rxfifo)) {
- fifo_putchar(&sc->rxfifo, ch);
+ if (rxfifo_available(sc)) {
+ rxfifo_putchar(sc, ch);
}
}
uart_toggle_intr(sc);
@@ -452,12 +542,6 @@ uart_write(struct uart_softc *sc, int offset, uint8_t value)
pthread_mutex_lock(&sc->mtx);
- /* Open terminal */
- if (!sc->opened && sc->stdio) {
- uart_opentty(sc);
- sc->opened = true;
- }
-
/*
* Take care of the special case DLAB accesses first
*/
@@ -476,10 +560,10 @@ uart_write(struct uart_softc *sc, int offset, uint8_t value)
switch (offset) {
case REG_DATA:
if (sc->mcr & MCR_LOOPBACK) {
- if (fifo_putchar(&sc->rxfifo, value) != 0)
+ if (rxfifo_putchar(sc, value) != 0)
sc->lsr |= LSR_OE;
- } else if (sc->stdio) {
- ttywrite(value);
+ } else if (sc->tty.opened) {
+ ttywrite(&sc->tty, value);
#ifndef __FreeBSD__
} else if (sc->bcons) {
bconswrite(sc, value);
@@ -501,7 +585,7 @@ uart_write(struct uart_softc *sc, int offset, uint8_t value)
*/
if ((sc->fcr & FCR_ENABLE) ^ (value & FCR_ENABLE)) {
fifosz = (value & FCR_ENABLE) ? FIFOSZ : 1;
- fifo_reset(&sc->rxfifo, fifosz);
+ rxfifo_reset(sc, fifosz);
}
/*
@@ -512,7 +596,7 @@ uart_write(struct uart_softc *sc, int offset, uint8_t value)
sc->fcr = 0;
} else {
if ((value & FCR_RCV_RST) != 0)
- fifo_reset(&sc->rxfifo, FIFOSZ);
+ rxfifo_reset(sc, FIFOSZ);
sc->fcr = value &
(FCR_ENABLE | FCR_DMA | FCR_RX_MASK);
@@ -524,22 +608,7 @@ uart_write(struct uart_softc *sc, int offset, uint8_t value)
case REG_MCR:
/* Apply mask so that bits 5-7 are 0 */
sc->mcr = value & 0x1F;
-
- msr = 0;
- if (sc->mcr & MCR_LOOPBACK) {
- /*
- * In the loopback mode certain bits from the
- * MCR are reflected back into MSR
- */
- if (sc->mcr & MCR_RTS)
- msr |= MSR_CTS;
- if (sc->mcr & MCR_DTR)
- msr |= MSR_DSR;
- if (sc->mcr & MCR_OUT1)
- msr |= MSR_RI;
- if (sc->mcr & MCR_OUT2)
- msr |= MSR_DCD;
- }
+ msr = modem_status(sc->mcr);
/*
* Detect if there has been any change between the
@@ -592,12 +661,6 @@ uart_read(struct uart_softc *sc, int offset)
pthread_mutex_lock(&sc->mtx);
- /* Open terminal */
- if (!sc->opened && sc->stdio) {
- uart_opentty(sc);
- sc->opened = true;
- }
-
/*
* Take care of the special case DLAB accesses first
*/
@@ -615,7 +678,7 @@ uart_read(struct uart_softc *sc, int offset)
switch (offset) {
case REG_DATA:
- reg = fifo_getchar(&sc->rxfifo);
+ reg = rxfifo_getchar(sc);
break;
case REG_IER:
reg = sc->ier;
@@ -646,7 +709,7 @@ uart_read(struct uart_softc *sc, int offset)
sc->lsr |= LSR_TEMT | LSR_THRE;
/* Check for new receive data */
- if (fifo_numchars(&sc->rxfifo) > 0)
+ if (rxfifo_numchars(sc) > 0)
sc->lsr |= LSR_RXRDY;
else
sc->lsr &= ~LSR_RXRDY;
@@ -685,7 +748,7 @@ uart_tty_thread(void *param)
struct uart_softc *sc = param;
pollfd_t pollset;
- pollset.fd = STDIN_FILENO;
+ pollset.fd = sc->tty.fd;
pollset.events = POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND;
for (;;) {
@@ -983,8 +1046,7 @@ uart_init(uart_intr_func_t intr_assert, uart_intr_func_t intr_deassert,
{
struct uart_softc *sc;
- sc = malloc(sizeof(struct uart_softc));
- bzero(sc, sizeof(struct uart_softc));
+ sc = calloc(1, sizeof(struct uart_softc));
sc->arg = arg;
sc->intr_assert = intr_assert;
@@ -997,32 +1059,51 @@ uart_init(uart_intr_func_t intr_assert, uart_intr_func_t intr_deassert,
return (sc);
}
+static int
+uart_tty_backend(struct uart_softc *sc, const char *opts)
+{
+ int fd;
+ int retval;
+
+ retval = -1;
+
+ fd = open(opts, O_RDWR | O_NONBLOCK);
+ if (fd > 0 && isatty(fd)) {
+ sc->tty.fd = fd;
+ sc->tty.opened = true;
+ retval = 0;
+ }
+
+ return (retval);
+}
+
int
uart_set_backend(struct uart_softc *sc, const char *opts)
{
#ifndef __FreeBSD__
int error;
#endif
- /*
- * XXX one stdio backend supported at this time.
- */
- if (opts == NULL)
- return (0);
-
-#ifdef __FreeBSD__
- if (strcmp("stdio", opts) == 0 && !uart_stdio) {
- sc->stdio = true;
- uart_stdio = true;
- return (0);
-#else
- if (strcmp("stdio", opts) == 0 && !uart_stdio && !uart_bcons) {
- sc->stdio = true;
- uart_stdio = true;
+ int retval;
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_t rights;
+ cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ };
+ cap_ioctl_t sicmds[] = { TIOCGETA, TIOCGWINSZ };
+#endif
- error = pthread_create(NULL, NULL, uart_tty_thread, sc);
- assert(error == 0);
+ retval = -1;
+ if (opts == NULL)
return (0);
+
+ if (strcmp("stdio", opts) == 0) {
+ if (!uart_stdio && !uart_bcons) {
+ sc->tty.fd = STDIN_FILENO;
+ sc->tty.opened = true;
+ uart_stdio = true;
+ retval = 0;
+ }
+ } else if (uart_tty_backend(sc, opts) == 0) {
+ retval = 0;
} else if (strstr(opts, "bcons") != 0 && !uart_stdio && !uart_bcons) {
sc->bcons = true;
uart_bcons= true;
@@ -1041,7 +1122,36 @@ uart_set_backend(struct uart_softc *sc, const char *opts)
assert(error == 0);
return (0);
+ }
+
+ /* Make the backend file descriptor non-blocking */
+ if (retval == 0)
+ retval = fcntl(sc->tty.fd, F_SETFL, O_NONBLOCK);
+
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ, CAP_WRITE);
+ if (cap_rights_limit(sc->tty.fd, &rights) == -1 && errno != ENOSYS)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+ if (cap_ioctls_limit(sc->tty.fd, cmds, nitems(cmds)) == -1 && errno != ENOSYS)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+ if (!uart_stdio) {
+ cap_rights_init(&rights, CAP_FCNTL, CAP_FSTAT, CAP_IOCTL, CAP_READ);
+ if (cap_rights_limit(STDIN_FILENO, &rights) == -1 && errno != ENOSYS)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+ if (cap_ioctls_limit(STDIN_FILENO, sicmds, nitems(sicmds)) == -1 && errno != ENOSYS)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+ if (cap_fcntls_limit(STDIN_FILENO, CAP_FCNTL_GETFL) == -1 && errno != ENOSYS)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+ }
#endif
- } else
- return (-1);
+
+ if (retval == 0) {
+ uart_opentty(sc);
+#ifndef __FreeBSD__
+ error = pthread_create(NULL, NULL, uart_tty_thread, sc);
+ assert(error == 0);
+#endif
+ }
+
+ return (retval);
}
diff --git a/usr/src/cmd/bhyve/uart_emul.h b/usr/src/cmd/bhyve/uart_emul.h
index ecff957991..993b92e3cf 100644
--- a/usr/src/cmd/bhyve/uart_emul.h
+++ b/usr/src/cmd/bhyve/uart_emul.h
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/uart_emul.h 257293 2013-10-29 00:18:11Z neel $
+ * $FreeBSD$
*/
#ifndef _UART_EMUL_H_
diff --git a/usr/src/cmd/bhyve/usb_emul.c b/usr/src/cmd/bhyve/usb_emul.c
new file mode 100644
index 0000000000..3dc12a5c3c
--- /dev/null
+++ b/usr/src/cmd/bhyve/usb_emul.c
@@ -0,0 +1,76 @@
+/*-
+ * Copyright (c) 2014 Nahanni Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+#include "usb_emul.h"
+
+SET_DECLARE(usb_emu_set, struct usb_devemu);
+
+struct usb_devemu *
+usb_emu_finddev(char *name)
+{
+ struct usb_devemu **udpp, *udp;
+
+ SET_FOREACH(udpp, usb_emu_set) {
+ udp = *udpp;
+ if (!strcmp(udp->ue_emu, name))
+ return (udp);
+ }
+
+ return (NULL);
+}
+
+struct usb_data_xfer_block *
+usb_data_xfer_append(struct usb_data_xfer *xfer, void *buf, int blen,
+ void *hci_data, int ccs)
+{
+ struct usb_data_xfer_block *xb;
+
+ if (xfer->ndata >= USB_MAX_XFER_BLOCKS)
+ return (NULL);
+
+ xb = &xfer->data[xfer->tail];
+ xb->buf = buf;
+ xb->blen = blen;
+ xb->hci_data = hci_data;
+ xb->ccs = ccs;
+ xb->processed = 0;
+ xb->bdone = 0;
+ xfer->ndata++;
+ xfer->tail = (xfer->tail + 1) % USB_MAX_XFER_BLOCKS;
+ return (xb);
+}
diff --git a/usr/src/cmd/bhyve/usb_emul.h b/usr/src/cmd/bhyve/usb_emul.h
new file mode 100644
index 0000000000..69df135466
--- /dev/null
+++ b/usr/src/cmd/bhyve/usb_emul.h
@@ -0,0 +1,156 @@
+/*-
+ * Copyright (c) 2014 Leon Dang <ldang@nahannisys.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _USB_EMUL_H_
+#define _USB_EMUL_H_
+
+#include <stdlib.h>
+#include <sys/linker_set.h>
+#include <pthread.h>
+
+#define USB_MAX_XFER_BLOCKS 8
+
+#define USB_XFER_OUT 0
+#define USB_XFER_IN 1
+
+
+
+struct usb_hci;
+struct usb_device_request;
+struct usb_data_xfer;
+
+/* Device emulation handlers */
+struct usb_devemu {
+ char *ue_emu; /* name of device emulation */
+ int ue_usbver; /* usb version: 2 or 3 */
+ int ue_usbspeed; /* usb device speed */
+
+ /* instance creation */
+ void *(*ue_init)(struct usb_hci *hci, char *opt);
+
+ /* handlers */
+ int (*ue_request)(void *sc, struct usb_data_xfer *xfer);
+ int (*ue_data)(void *sc, struct usb_data_xfer *xfer, int dir,
+ int epctx);
+ int (*ue_reset)(void *sc);
+ int (*ue_remove)(void *sc);
+ int (*ue_stop)(void *sc);
+};
+#define USB_EMUL_SET(x) DATA_SET(usb_emu_set, x);
+
+/*
+ * USB device events to notify HCI when state changes
+ */
+enum hci_usbev {
+ USBDEV_ATTACH,
+ USBDEV_RESET,
+ USBDEV_STOP,
+ USBDEV_REMOVE,
+};
+
+/* usb controller, ie xhci, ehci */
+struct usb_hci {
+ int (*hci_intr)(struct usb_hci *hci, int epctx);
+ int (*hci_event)(struct usb_hci *hci, enum hci_usbev evid,
+ void *param);
+ void *hci_sc; /* private softc for hci */
+
+ /* controller managed fields */
+ int hci_address;
+ int hci_port;
+};
+
+/*
+ * Each xfer block is mapped to the hci transfer block.
+ * On input into the device handler, blen is set to the lenght of buf.
+ * The device handler is to update blen to reflect on the residual size
+ * of the buffer, i.e. len(buf) - len(consumed).
+ */
+struct usb_data_xfer_block {
+ void *buf; /* IN or OUT pointer */
+ int blen; /* in:len(buf), out:len(remaining) */
+ int bdone; /* bytes transferred */
+ uint32_t processed; /* device processed this + errcode */
+ void *hci_data; /* HCI private reference */
+ int ccs;
+ uint32_t streamid;
+ uint64_t trbnext; /* next TRB guest address */
+};
+
+struct usb_data_xfer {
+ struct usb_data_xfer_block data[USB_MAX_XFER_BLOCKS];
+ struct usb_device_request *ureq; /* setup ctl request */
+ int ndata; /* # of data items */
+ int head;
+ int tail;
+ pthread_mutex_t mtx;
+};
+
+enum USB_ERRCODE {
+ USB_ACK,
+ USB_NAK,
+ USB_STALL,
+ USB_NYET,
+ USB_ERR,
+ USB_SHORT
+};
+
+#define USB_DATA_GET_ERRCODE(x) (x)->processed >> 8
+#define USB_DATA_SET_ERRCODE(x,e) do { \
+ (x)->processed = ((x)->processed & 0xFF) | (e << 8); \
+ } while (0)
+
+#define USB_DATA_OK(x,i) ((x)->data[(i)].buf != NULL)
+
+#define USB_DATA_XFER_INIT(x) do { \
+ memset((x), 0, sizeof(*(x))); \
+ pthread_mutex_init(&((x)->mtx), NULL); \
+ } while (0)
+
+#define USB_DATA_XFER_RESET(x) do { \
+ memset((x)->data, 0, sizeof((x)->data)); \
+ (x)->ndata = 0; \
+ (x)->head = (x)->tail = 0; \
+ } while (0)
+
+#define USB_DATA_XFER_LOCK(x) do { \
+ pthread_mutex_lock(&((x)->mtx)); \
+ } while (0)
+
+#define USB_DATA_XFER_UNLOCK(x) do { \
+ pthread_mutex_unlock(&((x)->mtx)); \
+ } while (0)
+
+
+struct usb_devemu *usb_emu_finddev(char *name);
+
+struct usb_data_xfer_block *usb_data_xfer_append(struct usb_data_xfer *xfer,
+ void *buf, int blen, void *hci_data, int ccs);
+
+
+#endif /* _USB_EMUL_H_ */
diff --git a/usr/src/cmd/bhyve/usb_mouse.c b/usr/src/cmd/bhyve/usb_mouse.c
new file mode 100644
index 0000000000..e9fc77ed8a
--- /dev/null
+++ b/usr/src/cmd/bhyve/usb_mouse.c
@@ -0,0 +1,800 @@
+/*-
+ * Copyright (c) 2014 Leon Dang <ldang@nahannisys.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/time.h>
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include "usb_emul.h"
+#include "console.h"
+#include "bhyvegc.h"
+
+static int umouse_debug = 0;
+#define DPRINTF(params) if (umouse_debug) printf params
+#define WPRINTF(params) printf params
+
+/* USB endpoint context (1-15) for reporting mouse data events*/
+#define UMOUSE_INTR_ENDPT 1
+
+#define UMOUSE_REPORT_DESC_TYPE 0x22
+
+#define UMOUSE_GET_REPORT 0x01
+#define UMOUSE_GET_IDLE 0x02
+#define UMOUSE_GET_PROTOCOL 0x03
+#define UMOUSE_SET_REPORT 0x09
+#define UMOUSE_SET_IDLE 0x0A
+#define UMOUSE_SET_PROTOCOL 0x0B
+
+#define HSETW(ptr, val) ptr = { (uint8_t)(val), (uint8_t)((val) >> 8) }
+
+enum {
+ UMSTR_LANG,
+ UMSTR_MANUFACTURER,
+ UMSTR_PRODUCT,
+ UMSTR_SERIAL,
+ UMSTR_CONFIG,
+ UMSTR_MAX
+};
+
+static const char *umouse_desc_strings[] = {
+ "\x04\x09",
+ "BHYVE",
+ "HID Tablet",
+ "01",
+ "HID Tablet Device",
+};
+
+struct umouse_hid_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bcdHID[2];
+ uint8_t bCountryCode;
+ uint8_t bNumDescriptors;
+ uint8_t bReportDescriptorType;
+ uint8_t wItemLength[2];
+} __packed;
+
+struct umouse_config_desc {
+ struct usb_config_descriptor confd;
+ struct usb_interface_descriptor ifcd;
+ struct umouse_hid_descriptor hidd;
+ struct usb_endpoint_descriptor endpd;
+ struct usb_endpoint_ss_comp_descriptor sscompd;
+} __packed;
+
+#define MOUSE_MAX_X 0x8000
+#define MOUSE_MAX_Y 0x8000
+
+static const uint8_t umouse_report_desc[] = {
+ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
+ 0x09, 0x02, /* USAGE (Mouse) */
+ 0xa1, 0x01, /* COLLECTION (Application) */
+ 0x09, 0x01, /* USAGE (Pointer) */
+ 0xa1, 0x00, /* COLLECTION (Physical) */
+ 0x05, 0x09, /* USAGE_PAGE (Button) */
+ 0x19, 0x01, /* USAGE_MINIMUM (Button 1) */
+ 0x29, 0x03, /* USAGE_MAXIMUM (Button 3) */
+ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
+ 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
+ 0x75, 0x01, /* REPORT_SIZE (1) */
+ 0x95, 0x03, /* REPORT_COUNT (3) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs); 3 buttons */
+ 0x75, 0x05, /* REPORT_SIZE (5) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x81, 0x03, /* INPUT (Cnst,Var,Abs); padding */
+ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
+ 0x09, 0x30, /* USAGE (X) */
+ 0x09, 0x31, /* USAGE (Y) */
+ 0x35, 0x00, /* PHYSICAL_MINIMUM (0) */
+ 0x46, 0xff, 0x7f, /* PHYSICAL_MAXIMUM (0x7fff) */
+ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
+ 0x26, 0xff, 0x7f, /* LOGICAL_MAXIMUM (0x7fff) */
+ 0x75, 0x10, /* REPORT_SIZE (16) */
+ 0x95, 0x02, /* REPORT_COUNT (2) */
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */
+ 0x05, 0x01, /* USAGE Page (Generic Desktop) */
+ 0x09, 0x38, /* USAGE (Wheel) */
+ 0x35, 0x00, /* PHYSICAL_MINIMUM (0) */
+ 0x45, 0x00, /* PHYSICAL_MAXIMUM (0) */
+ 0x15, 0x81, /* LOGICAL_MINIMUM (-127) */
+ 0x25, 0x7f, /* LOGICAL_MAXIMUM (127) */
+ 0x75, 0x08, /* REPORT_SIZE (8) */
+ 0x95, 0x01, /* REPORT_COUNT (1) */
+ 0x81, 0x06, /* INPUT (Data,Var,Rel) */
+ 0xc0, /* END_COLLECTION */
+ 0xc0 /* END_COLLECTION */
+};
+
+struct umouse_report {
+ uint8_t buttons; /* bits: 0 left, 1 right, 2 middle */
+ int16_t x; /* x position */
+ int16_t y; /* y position */
+ int8_t z; /* z wheel position */
+} __packed;
+
+
+#define MSETW(ptr, val) ptr = { (uint8_t)(val), (uint8_t)((val) >> 8) }
+
+static struct usb_device_descriptor umouse_dev_desc = {
+ .bLength = sizeof(umouse_dev_desc),
+ .bDescriptorType = UDESC_DEVICE,
+ MSETW(.bcdUSB, UD_USB_3_0),
+ .bMaxPacketSize = 8, /* max packet size */
+ MSETW(.idVendor, 0xFB5D), /* vendor */
+ MSETW(.idProduct, 0x0001), /* product */
+ MSETW(.bcdDevice, 0), /* device version */
+ .iManufacturer = UMSTR_MANUFACTURER,
+ .iProduct = UMSTR_PRODUCT,
+ .iSerialNumber = UMSTR_SERIAL,
+ .bNumConfigurations = 1,
+};
+
+static struct umouse_config_desc umouse_confd = {
+ .confd = {
+ .bLength = sizeof(umouse_confd.confd),
+ .bDescriptorType = UDESC_CONFIG,
+ .wTotalLength[0] = sizeof(umouse_confd),
+ .bNumInterface = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = UMSTR_CONFIG,
+ .bmAttributes = UC_BUS_POWERED | UC_REMOTE_WAKEUP,
+ .bMaxPower = 0,
+ },
+ .ifcd = {
+ .bLength = sizeof(umouse_confd.ifcd),
+ .bDescriptorType = UDESC_INTERFACE,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = UICLASS_HID,
+ .bInterfaceSubClass = UISUBCLASS_BOOT,
+ .bInterfaceProtocol = UIPROTO_MOUSE,
+ },
+ .hidd = {
+ .bLength = sizeof(umouse_confd.hidd),
+ .bDescriptorType = 0x21,
+ .bcdHID = { 0x01, 0x10 },
+ .bCountryCode = 0,
+ .bNumDescriptors = 1,
+ .bReportDescriptorType = UMOUSE_REPORT_DESC_TYPE,
+ .wItemLength = { sizeof(umouse_report_desc), 0 },
+ },
+ .endpd = {
+ .bLength = sizeof(umouse_confd.endpd),
+ .bDescriptorType = UDESC_ENDPOINT,
+ .bEndpointAddress = UE_DIR_IN | UMOUSE_INTR_ENDPT,
+ .bmAttributes = UE_INTERRUPT,
+ .wMaxPacketSize[0] = 8,
+ .bInterval = 0xA,
+ },
+ .sscompd = {
+ .bLength = sizeof(umouse_confd.sscompd),
+ .bDescriptorType = UDESC_ENDPOINT_SS_COMP,
+ .bMaxBurst = 0,
+ .bmAttributes = 0,
+ MSETW(.wBytesPerInterval, 0),
+ },
+};
+
+
+struct umouse_bos_desc {
+ struct usb_bos_descriptor bosd;
+ struct usb_devcap_ss_descriptor usbssd;
+} __packed;
+
+
+struct umouse_bos_desc umouse_bosd = {
+ .bosd = {
+ .bLength = sizeof(umouse_bosd.bosd),
+ .bDescriptorType = UDESC_BOS,
+ HSETW(.wTotalLength, sizeof(umouse_bosd)),
+ .bNumDeviceCaps = 1,
+ },
+ .usbssd = {
+ .bLength = sizeof(umouse_bosd.usbssd),
+ .bDescriptorType = UDESC_DEVICE_CAPABILITY,
+ .bDevCapabilityType = 3,
+ .bmAttributes = 0,
+ HSETW(.wSpeedsSupported, 0x08),
+ .bFunctionalitySupport = 3,
+ .bU1DevExitLat = 0xa, /* dummy - not used */
+ .wU2DevExitLat = { 0x20, 0x00 },
+ }
+};
+
+
+struct umouse_softc {
+ struct usb_hci *hci;
+
+ char *opt;
+
+ struct umouse_report um_report;
+ int newdata;
+ struct {
+ uint8_t idle;
+ uint8_t protocol;
+ uint8_t feature;
+ } hid;
+
+ pthread_mutex_t mtx;
+ pthread_mutex_t ev_mtx;
+ int polling;
+ struct timeval prev_evt;
+};
+
+static void
+umouse_event(uint8_t button, int x, int y, void *arg)
+{
+ struct umouse_softc *sc;
+ struct bhyvegc_image *gc;
+
+ gc = console_get_image();
+ if (gc == NULL) {
+ /* not ready */
+ return;
+ }
+
+ sc = arg;
+
+ pthread_mutex_lock(&sc->mtx);
+
+ sc->um_report.buttons = 0;
+ sc->um_report.z = 0;
+
+ if (button & 0x01)
+ sc->um_report.buttons |= 0x01; /* left */
+ if (button & 0x02)
+ sc->um_report.buttons |= 0x04; /* middle */
+ if (button & 0x04)
+ sc->um_report.buttons |= 0x02; /* right */
+ if (button & 0x8)
+ sc->um_report.z = 1;
+ if (button & 0x10)
+ sc->um_report.z = -1;
+
+ /* scale coords to mouse resolution */
+ sc->um_report.x = MOUSE_MAX_X * x / gc->width;
+ sc->um_report.y = MOUSE_MAX_Y * y / gc->height;
+ sc->newdata = 1;
+ pthread_mutex_unlock(&sc->mtx);
+
+ pthread_mutex_lock(&sc->ev_mtx);
+ sc->hci->hci_intr(sc->hci, UE_DIR_IN | UMOUSE_INTR_ENDPT);
+ pthread_mutex_unlock(&sc->ev_mtx);
+}
+
+static void *
+umouse_init(struct usb_hci *hci, char *opt)
+{
+ struct umouse_softc *sc;
+
+ sc = calloc(1, sizeof(struct umouse_softc));
+ sc->hci = hci;
+
+ sc->hid.protocol = 1; /* REPORT protocol */
+ sc->opt = strdup(opt);
+ pthread_mutex_init(&sc->mtx, NULL);
+ pthread_mutex_init(&sc->ev_mtx, NULL);
+
+ console_ptr_register(umouse_event, sc, 10);
+
+ return (sc);
+}
+
+#define UREQ(x,y) ((x) | ((y) << 8))
+
+static int
+umouse_request(void *scarg, struct usb_data_xfer *xfer)
+{
+ struct umouse_softc *sc;
+ struct usb_data_xfer_block *data;
+ const char *str;
+ uint16_t value;
+ uint16_t index;
+ uint16_t len;
+ uint16_t slen;
+ uint8_t *udata;
+ int err;
+ int i, idx;
+ int eshort;
+
+ sc = scarg;
+
+ data = NULL;
+ udata = NULL;
+ idx = xfer->head;
+ for (i = 0; i < xfer->ndata; i++) {
+ xfer->data[idx].bdone = 0;
+ if (data == NULL && USB_DATA_OK(xfer,i)) {
+ data = &xfer->data[idx];
+ udata = data->buf;
+ }
+
+ xfer->data[idx].processed = 1;
+ idx = (idx + 1) % USB_MAX_XFER_BLOCKS;
+ }
+
+ err = USB_ERR_NORMAL_COMPLETION;
+ eshort = 0;
+
+ if (!xfer->ureq) {
+ DPRINTF(("umouse_request: port %d\r\n", sc->hci->hci_port));
+ goto done;
+ }
+
+ value = UGETW(xfer->ureq->wValue);
+ index = UGETW(xfer->ureq->wIndex);
+ len = UGETW(xfer->ureq->wLength);
+
+ DPRINTF(("umouse_request: port %d, type 0x%x, req 0x%x, val 0x%x, "
+ "idx 0x%x, len %u\r\n",
+ sc->hci->hci_port, xfer->ureq->bmRequestType,
+ xfer->ureq->bRequest, value, index, len));
+
+ switch (UREQ(xfer->ureq->bRequest, xfer->ureq->bmRequestType)) {
+ case UREQ(UR_GET_CONFIG, UT_READ_DEVICE):
+ DPRINTF(("umouse: (UR_GET_CONFIG, UT_READ_DEVICE)\r\n"));
+ if (!data)
+ break;
+
+ *udata = umouse_confd.confd.bConfigurationValue;
+ data->blen = len > 0 ? len - 1 : 0;
+ eshort = data->blen > 0;
+ data->bdone += 1;
+ break;
+
+ case UREQ(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
+ DPRINTF(("umouse: (UR_GET_DESCRIPTOR, UT_READ_DEVICE) val %x\r\n",
+ value >> 8));
+ if (!data)
+ break;
+
+ switch (value >> 8) {
+ case UDESC_DEVICE:
+ DPRINTF(("umouse: (->UDESC_DEVICE) len %u ?= "
+ "sizeof(umouse_dev_desc) %lu\r\n",
+ len, sizeof(umouse_dev_desc)));
+ if ((value & 0xFF) != 0) {
+ err = USB_ERR_IOERROR;
+ goto done;
+ }
+ if (len > sizeof(umouse_dev_desc)) {
+ data->blen = len - sizeof(umouse_dev_desc);
+ len = sizeof(umouse_dev_desc);
+ } else
+ data->blen = 0;
+ memcpy(data->buf, &umouse_dev_desc, len);
+ data->bdone += len;
+ break;
+
+ case UDESC_CONFIG:
+ DPRINTF(("umouse: (->UDESC_CONFIG)\r\n"));
+ if ((value & 0xFF) != 0) {
+ err = USB_ERR_IOERROR;
+ goto done;
+ }
+ if (len > sizeof(umouse_confd)) {
+ data->blen = len - sizeof(umouse_confd);
+ len = sizeof(umouse_confd);
+ } else
+ data->blen = 0;
+
+ memcpy(data->buf, &umouse_confd, len);
+ data->bdone += len;
+ break;
+
+ case UDESC_STRING:
+ DPRINTF(("umouse: (->UDESC_STRING)\r\n"));
+ str = NULL;
+ if ((value & 0xFF) < UMSTR_MAX)
+ str = umouse_desc_strings[value & 0xFF];
+ else
+ goto done;
+
+ if ((value & 0xFF) == UMSTR_LANG) {
+ udata[0] = 4;
+ udata[1] = UDESC_STRING;
+ data->blen = len - 2;
+ len -= 2;
+ data->bdone += 2;
+
+ if (len >= 2) {
+ udata[2] = str[0];
+ udata[3] = str[1];
+ data->blen -= 2;
+ data->bdone += 2;
+ } else
+ data->blen = 0;
+
+ goto done;
+ }
+
+ slen = 2 + strlen(str) * 2;
+ udata[0] = slen;
+ udata[1] = UDESC_STRING;
+
+ if (len > slen) {
+ data->blen = len - slen;
+ len = slen;
+ } else
+ data->blen = 0;
+ for (i = 2; i < len; i += 2) {
+ udata[i] = *str++;
+ udata[i+1] = '\0';
+ }
+ data->bdone += slen;
+
+ break;
+
+ case UDESC_BOS:
+ DPRINTF(("umouse: USB3 BOS\r\n"));
+ if (len > sizeof(umouse_bosd)) {
+ data->blen = len - sizeof(umouse_bosd);
+ len = sizeof(umouse_bosd);
+ } else
+ data->blen = 0;
+ memcpy(udata, &umouse_bosd, len);
+ data->bdone += len;
+ break;
+
+ default:
+ DPRINTF(("umouse: unknown(%d)->ERROR\r\n", value >> 8));
+ err = USB_ERR_IOERROR;
+ goto done;
+ }
+ eshort = data->blen > 0;
+ break;
+
+ case UREQ(UR_GET_DESCRIPTOR, UT_READ_INTERFACE):
+ DPRINTF(("umouse: (UR_GET_DESCRIPTOR, UT_READ_INTERFACE) "
+ "0x%x\r\n", (value >> 8)));
+ if (!data)
+ break;
+
+ switch (value >> 8) {
+ case UMOUSE_REPORT_DESC_TYPE:
+ if (len > sizeof(umouse_report_desc)) {
+ data->blen = len - sizeof(umouse_report_desc);
+ len = sizeof(umouse_report_desc);
+ } else
+ data->blen = 0;
+ memcpy(data->buf, umouse_report_desc, len);
+ data->bdone += len;
+ break;
+ default:
+ DPRINTF(("umouse: IO ERROR\r\n"));
+ err = USB_ERR_IOERROR;
+ goto done;
+ }
+ eshort = data->blen > 0;
+ break;
+
+ case UREQ(UR_GET_INTERFACE, UT_READ_INTERFACE):
+ DPRINTF(("umouse: (UR_GET_INTERFACE, UT_READ_INTERFACE)\r\n"));
+ if (index != 0) {
+ DPRINTF(("umouse get_interface, invalid index %d\r\n",
+ index));
+ err = USB_ERR_IOERROR;
+ goto done;
+ }
+
+ if (!data)
+ break;
+
+ if (len > 0) {
+ *udata = 0;
+ data->blen = len - 1;
+ }
+ eshort = data->blen > 0;
+ data->bdone += 1;
+ break;
+
+ case UREQ(UR_GET_STATUS, UT_READ_DEVICE):
+ DPRINTF(("umouse: (UR_GET_STATUS, UT_READ_DEVICE)\r\n"));
+ if (data != NULL && len > 1) {
+ if (sc->hid.feature == UF_DEVICE_REMOTE_WAKEUP)
+ USETW(udata, UDS_REMOTE_WAKEUP);
+ else
+ USETW(udata, 0);
+ data->blen = len - 2;
+ data->bdone += 2;
+ }
+
+ eshort = data->blen > 0;
+ break;
+
+ case UREQ(UR_GET_STATUS, UT_READ_INTERFACE):
+ case UREQ(UR_GET_STATUS, UT_READ_ENDPOINT):
+ DPRINTF(("umouse: (UR_GET_STATUS, UT_READ_INTERFACE)\r\n"));
+ if (data != NULL && len > 1) {
+ USETW(udata, 0);
+ data->blen = len - 2;
+ data->bdone += 2;
+ }
+ eshort = data->blen > 0;
+ break;
+
+ case UREQ(UR_SET_ADDRESS, UT_WRITE_DEVICE):
+ /* XXX Controller should've handled this */
+ DPRINTF(("umouse set address %u\r\n", value));
+ break;
+
+ case UREQ(UR_SET_CONFIG, UT_WRITE_DEVICE):
+ DPRINTF(("umouse set config %u\r\n", value));
+ break;
+
+ case UREQ(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE):
+ DPRINTF(("umouse set descriptor %u\r\n", value));
+ break;
+
+
+ case UREQ(UR_CLEAR_FEATURE, UT_WRITE_DEVICE):
+ DPRINTF(("umouse: (UR_SET_FEATURE, UT_WRITE_DEVICE) %x\r\n", value));
+ if (value == UF_DEVICE_REMOTE_WAKEUP)
+ sc->hid.feature = 0;
+ break;
+
+ case UREQ(UR_SET_FEATURE, UT_WRITE_DEVICE):
+ DPRINTF(("umouse: (UR_SET_FEATURE, UT_WRITE_DEVICE) %x\r\n", value));
+ if (value == UF_DEVICE_REMOTE_WAKEUP)
+ sc->hid.feature = UF_DEVICE_REMOTE_WAKEUP;
+ break;
+
+ case UREQ(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE):
+ case UREQ(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
+ case UREQ(UR_SET_FEATURE, UT_WRITE_INTERFACE):
+ case UREQ(UR_SET_FEATURE, UT_WRITE_ENDPOINT):
+ DPRINTF(("umouse: (UR_CLEAR_FEATURE, UT_WRITE_INTERFACE)\r\n"));
+ err = USB_ERR_IOERROR;
+ goto done;
+
+ case UREQ(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
+ DPRINTF(("umouse set interface %u\r\n", value));
+ break;
+
+ case UREQ(UR_ISOCH_DELAY, UT_WRITE_DEVICE):
+ DPRINTF(("umouse set isoch delay %u\r\n", value));
+ break;
+
+ case UREQ(UR_SET_SEL, 0):
+ DPRINTF(("umouse set sel\r\n"));
+ break;
+
+ case UREQ(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT):
+ DPRINTF(("umouse synch frame\r\n"));
+ break;
+
+ /* HID device requests */
+
+ case UREQ(UMOUSE_GET_REPORT, UT_READ_CLASS_INTERFACE):
+ DPRINTF(("umouse: (UMOUSE_GET_REPORT, UT_READ_CLASS_INTERFACE) "
+ "0x%x\r\n", (value >> 8)));
+ if (!data)
+ break;
+
+ if ((value >> 8) == 0x01 && len >= sizeof(sc->um_report)) {
+ /* TODO read from backend */
+
+ if (len > sizeof(sc->um_report)) {
+ data->blen = len - sizeof(sc->um_report);
+ len = sizeof(sc->um_report);
+ } else
+ data->blen = 0;
+
+ memcpy(data->buf, &sc->um_report, len);
+ data->bdone += len;
+ } else {
+ err = USB_ERR_IOERROR;
+ goto done;
+ }
+ eshort = data->blen > 0;
+ break;
+
+ case UREQ(UMOUSE_GET_IDLE, UT_READ_CLASS_INTERFACE):
+ if (data != NULL && len > 0) {
+ *udata = sc->hid.idle;
+ data->blen = len - 1;
+ data->bdone += 1;
+ }
+ eshort = data->blen > 0;
+ break;
+
+ case UREQ(UMOUSE_GET_PROTOCOL, UT_READ_CLASS_INTERFACE):
+ if (data != NULL && len > 0) {
+ *udata = sc->hid.protocol;
+ data->blen = len - 1;
+ data->bdone += 1;
+ }
+ eshort = data->blen > 0;
+ break;
+
+ case UREQ(UMOUSE_SET_REPORT, UT_WRITE_CLASS_INTERFACE):
+ DPRINTF(("umouse: (UMOUSE_SET_REPORT, UT_WRITE_CLASS_INTERFACE) ignored\r\n"));
+ break;
+
+ case UREQ(UMOUSE_SET_IDLE, UT_WRITE_CLASS_INTERFACE):
+ sc->hid.idle = UGETW(xfer->ureq->wValue) >> 8;
+ DPRINTF(("umouse: (UMOUSE_SET_IDLE, UT_WRITE_CLASS_INTERFACE) %x\r\n",
+ sc->hid.idle));
+ break;
+
+ case UREQ(UMOUSE_SET_PROTOCOL, UT_WRITE_CLASS_INTERFACE):
+ sc->hid.protocol = UGETW(xfer->ureq->wValue) >> 8;
+ DPRINTF(("umouse: (UR_CLEAR_FEATURE, UT_WRITE_CLASS_INTERFACE) %x\r\n",
+ sc->hid.protocol));
+ break;
+
+ default:
+ DPRINTF(("**** umouse request unhandled\r\n"));
+ err = USB_ERR_IOERROR;
+ break;
+ }
+
+done:
+ if (xfer->ureq && (xfer->ureq->bmRequestType & UT_WRITE) &&
+ (err == USB_ERR_NORMAL_COMPLETION) && (data != NULL))
+ data->blen = 0;
+ else if (eshort)
+ err = USB_ERR_SHORT_XFER;
+
+ DPRINTF(("umouse request error code %d (0=ok), blen %u txlen %u\r\n",
+ err, (data ? data->blen : 0), (data ? data->bdone : 0)));
+
+ return (err);
+}
+
+static int
+umouse_data_handler(void *scarg, struct usb_data_xfer *xfer, int dir,
+ int epctx)
+{
+ struct umouse_softc *sc;
+ struct usb_data_xfer_block *data;
+ uint8_t *udata;
+ int len, i, idx;
+ int err;
+
+ DPRINTF(("umouse handle data - DIR=%s|EP=%d, blen %d\r\n",
+ dir ? "IN" : "OUT", epctx, xfer->data[0].blen));
+
+
+ /* find buffer to add data */
+ udata = NULL;
+ err = USB_ERR_NORMAL_COMPLETION;
+
+ /* handle xfer at first unprocessed item with buffer */
+ data = NULL;
+ idx = xfer->head;
+ for (i = 0; i < xfer->ndata; i++) {
+ data = &xfer->data[idx];
+ if (data->buf != NULL && data->blen != 0) {
+ break;
+ } else {
+ data->processed = 1;
+ data = NULL;
+ }
+ idx = (idx + 1) % USB_MAX_XFER_BLOCKS;
+ }
+ if (!data)
+ goto done;
+
+ udata = data->buf;
+ len = data->blen;
+
+ if (udata == NULL) {
+ DPRINTF(("umouse no buffer provided for input\r\n"));
+ err = USB_ERR_NOMEM;
+ goto done;
+ }
+
+ sc = scarg;
+
+ if (dir) {
+
+ pthread_mutex_lock(&sc->mtx);
+
+ if (!sc->newdata) {
+ err = USB_ERR_CANCELLED;
+ USB_DATA_SET_ERRCODE(&xfer->data[xfer->head], USB_NAK);
+ pthread_mutex_unlock(&sc->mtx);
+ goto done;
+ }
+
+ if (sc->polling) {
+ err = USB_ERR_STALLED;
+ USB_DATA_SET_ERRCODE(data, USB_STALL);
+ pthread_mutex_unlock(&sc->mtx);
+ goto done;
+ }
+ sc->polling = 1;
+
+ if (len > 0) {
+ sc->newdata = 0;
+
+ data->processed = 1;
+ data->bdone += 6;
+ memcpy(udata, &sc->um_report, 6);
+ data->blen = len - 6;
+ if (data->blen > 0)
+ err = USB_ERR_SHORT_XFER;
+ }
+
+ sc->polling = 0;
+ pthread_mutex_unlock(&sc->mtx);
+ } else {
+ USB_DATA_SET_ERRCODE(data, USB_STALL);
+ err = USB_ERR_STALLED;
+ }
+
+done:
+ return (err);
+}
+
+static int
+umouse_reset(void *scarg)
+{
+ struct umouse_softc *sc;
+
+ sc = scarg;
+
+ sc->newdata = 0;
+
+ return (0);
+}
+
+static int
+umouse_remove(void *scarg)
+{
+
+ return (0);
+}
+
+static int
+umouse_stop(void *scarg)
+{
+
+ return (0);
+}
+
+
+struct usb_devemu ue_mouse = {
+ .ue_emu = "tablet",
+ .ue_usbver = 3,
+ .ue_usbspeed = USB_SPEED_HIGH,
+ .ue_init = umouse_init,
+ .ue_request = umouse_request,
+ .ue_data = umouse_data_handler,
+ .ue_reset = umouse_reset,
+ .ue_remove = umouse_remove,
+ .ue_stop = umouse_stop
+};
+USB_EMUL_SET(ue_mouse);
diff --git a/usr/src/cmd/bhyve/vga.c b/usr/src/cmd/bhyve/vga.c
index 4330741042..e279dd8080 100644
--- a/usr/src/cmd/bhyve/vga.c
+++ b/usr/src/cmd/bhyve/vga.c
@@ -24,6 +24,10 @@
* SUCH DAMAGE.
*/
+/*
+ * Copyright 2017 Joyent, Inc.
+ */
+
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
@@ -161,10 +165,10 @@ struct vga_softc {
*/
struct {
uint8_t dac_state;
- int dac_rd_index;
- int dac_rd_subindex;
- int dac_wr_index;
- int dac_wr_subindex;
+ uint8_t dac_rd_index;
+ uint8_t dac_rd_subindex;
+ uint8_t dac_wr_index;
+ uint8_t dac_wr_subindex;
uint8_t dac_palette[3 * 256];
uint32_t dac_palette_rgb[256];
} vga_dac;
@@ -187,8 +191,10 @@ vga_check_size(struct bhyvegc *gc, struct vga_softc *sc)
if (vga_in_reset(sc))
return;
- old_width = sc->gc_width;
- old_height = sc->gc_height;
+ //old_width = sc->gc_width;
+ //old_height = sc->gc_height;
+ old_width = sc->gc_image->width;
+ old_height = sc->gc_image->height;
/*
* Horizontal Display End: For text modes this is the number
@@ -263,7 +269,7 @@ vga_get_text_pixel(struct vga_softc *sc, int x, int y)
offset = 2 * sc->vga_crtc.crtc_start_addr;
offset += (y / 16 * sc->gc_width / dots) * 2 + (x / dots) * 2;
- bit = 7 - (x % dots);
+ bit = 7 - (x % dots > 7 ? 7 : x % dots);
ch = sc->vga_ram[offset + 0 * 64*KB];
attr = sc->vga_ram[offset + 1 * 64*KB];
@@ -291,7 +297,7 @@ vga_get_text_pixel(struct vga_softc *sc, int x, int y)
font = sc->vga_ram[font_offset + 2 * 64*KB];
- if ((bit > 0) && (font & (1 << bit)))
+ if (font & (1 << bit))
idx = sc->vga_atc.atc_palette[attr & 0xf];
else
idx = sc->vga_atc.atc_palette[attr >> 4];
@@ -314,7 +320,7 @@ vga_render_text(struct vga_softc *sc)
}
}
-static void
+void
vga_render(struct bhyvegc *gc, void *arg)
{
struct vga_softc *sc = arg;
@@ -858,6 +864,7 @@ vga_port_in_handler(struct vmctx *ctx, int in, int port, int bytes,
assert(0);
break;
}
+ break;
case DAC_DATA_PORT:
*val = sc->vga_dac.dac_palette[3 * sc->vga_dac.dac_rd_index +
sc->vga_dac.dac_rd_subindex];
@@ -914,15 +921,33 @@ vga_port_in_handler(struct vmctx *ctx, int in, int port, int bytes,
case GEN_INPUT_STS1_MONO_PORT:
case GEN_INPUT_STS1_COLOR_PORT:
sc->vga_atc.atc_flipflop = 0;
+#ifdef __FreeBSD__
+ sc->vga_sts1 = GEN_IS1_VR | GEN_IS1_DE;
+ //sc->vga_sts1 ^= (GEN_IS1_VR | GEN_IS1_DE);
+#else
+ /*
+ * During the bhyve bring-up process, a guest image was failing
+ * to successfully boot. It appeared to be spinning, waiting
+ * for this value to be toggled. Until it can be ruled out
+ * that this is unnecessary (and documentation seems to
+ * indicate that it should be present), the toggle should
+ * remain.
+ */
sc->vga_sts1 ^= (GEN_IS1_VR | GEN_IS1_DE);
+#endif
*val = sc->vga_sts1;
break;
case GEN_FEATURE_CTRL_PORT:
- assert(0);
+ // OpenBSD calls this with bytes = 1
+ //assert(0);
+ *val = 0;
+ break;
+ case 0x3c3:
+ *val = 0;
break;
default:
printf("XXX vga_port_in_handler() unhandled port 0x%x\n", port);
- assert(0);
+ //assert(0);
return (-1);
}
@@ -1095,7 +1120,8 @@ vga_port_out_handler(struct vmctx *ctx, int in, int port, int bytes,
break;
case SEQ_MEMORY_MODE:
sc->vga_seq.seq_mm = val;
- assert((sc->vga_seq.seq_mm & SEQ_MM_C4) == 0);
+ /* Windows queries Chain4 */
+ //assert((sc->vga_seq.seq_mm & SEQ_MM_C4) == 0);
break;
default:
//printf("XXX VGA SEQ: outb 0x%04x, 0x%02x at index %d\n", port, val, sc->vga_seq.seq_index);
@@ -1161,6 +1187,9 @@ vga_port_out_handler(struct vmctx *ctx, int in, int port, int bytes,
sc->vga_gc.gc_mode_oe = (val & GC_MODE_OE) != 0;
sc->vga_gc.gc_mode_rm = (val >> 3) & 0x1;
sc->vga_gc.gc_mode_wm = val & 0x3;
+
+ if (sc->gc_image)
+ sc->gc_image->vgamode = 1;
break;
case GC_MISCELLANEOUS:
sc->vga_gc.gc_misc = val;
@@ -1188,8 +1217,10 @@ vga_port_out_handler(struct vmctx *ctx, int in, int port, int bytes,
case GEN_INPUT_STS1_COLOR_PORT:
/* write to Feature Control Register */
break;
+// case 0x3c3:
+// break;
default:
- printf("XXX vga_port_out_handler() unhandled port 0x%x\n", port);
+ printf("XXX vga_port_out_handler() unhandled port 0x%x, val 0x%x\n", port, val);
//assert(0);
return (-1);
}
@@ -1248,8 +1279,8 @@ vga_port_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
return (error);
}
-int
-vga_init(void)
+void *
+vga_init(int io_only)
{
struct inout_port iop;
struct vga_softc *sc;
@@ -1270,6 +1301,12 @@ vga_init(void)
assert(error == 0);
}
+ sc->gc_image = console_get_image();
+
+ /* only handle io ports; vga graphics is disabled */
+ if (io_only)
+ return(sc);
+
sc->mr.name = "VGA memory";
sc->mr.flags = MEM_F_RW;
sc->mr.base = 640 * KB;
@@ -1282,8 +1319,29 @@ vga_init(void)
sc->vga_ram = malloc(256 * KB);
memset(sc->vga_ram, 0, 256 * KB);
- sc->gc_image = console_get_image();
- console_fb_register(vga_render, sc);
+ {
+ static uint8_t palette[] = {
+ 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a,
+ 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x2a,0x00, 0x2a,0x2a,0x2a,
+ 0x00,0x00,0x15, 0x00,0x00,0x3f, 0x00,0x2a,0x15, 0x00,0x2a,0x3f,
+ 0x2a,0x00,0x15, 0x2a,0x00,0x3f, 0x2a,0x2a,0x15, 0x2a,0x2a,0x3f,
+ };
+ int i;
+
+ memcpy(sc->vga_dac.dac_palette, palette, 16 * 3 * sizeof (uint8_t));
+ for (i = 0; i < 16; i++) {
+ sc->vga_dac.dac_palette_rgb[i] =
+ ((((sc->vga_dac.dac_palette[3*i + 0] << 2) |
+ ((sc->vga_dac.dac_palette[3*i + 0] & 0x1) << 1) |
+ (sc->vga_dac.dac_palette[3*i + 0] & 0x1)) << 16) |
+ (((sc->vga_dac.dac_palette[3*i + 1] << 2) |
+ ((sc->vga_dac.dac_palette[3*i + 1] & 0x1) << 1) |
+ (sc->vga_dac.dac_palette[3*i + 1] & 0x1)) << 8) |
+ (((sc->vga_dac.dac_palette[3*i + 2] << 2) |
+ ((sc->vga_dac.dac_palette[3*i + 2] & 0x1) << 1) |
+ (sc->vga_dac.dac_palette[3*i + 2] & 0x1)) << 0));
+ }
+ }
- return (0);
+ return (sc);
}
diff --git a/usr/src/cmd/bhyve/vga.h b/usr/src/cmd/bhyve/vga.h
index 14637b12b3..4364f1b17a 100644
--- a/usr/src/cmd/bhyve/vga.h
+++ b/usr/src/cmd/bhyve/vga.h
@@ -155,6 +155,6 @@
#define DAC_IDX_WR_PORT 0x3c8
#define DAC_DATA_PORT 0x3c9
-int vga_init(void);
+void *vga_init(int io_only);
#endif /* _VGA_H_ */
diff --git a/usr/src/cmd/bhyve/virtio.c b/usr/src/cmd/bhyve/virtio.c
index c3b11dc439..11b1e627fd 100644
--- a/usr/src/cmd/bhyve/virtio.c
+++ b/usr/src/cmd/bhyve/virtio.c
@@ -25,7 +25,7 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/usr.sbin/bhyve/virtio.c 270326 2014-08-22 13:01:22Z tychon $");
+__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/uio.h>
@@ -97,6 +97,7 @@ vi_reset_dev(struct virtio_softc *vs)
for (vq = vs->vs_queues, i = 0; i < nvq; vq++, i++) {
vq->vq_flags = 0;
vq->vq_last_avail = 0;
+ vq->vq_save_used = 0;
vq->vq_pfn = 0;
vq->vq_msix_idx = VIRTIO_MSI_NO_VECTOR;
}
@@ -147,8 +148,13 @@ vi_intr_init(struct virtio_softc *vs, int barnum, int use_msix)
return (1);
} else
vs->vs_flags &= ~VIRTIO_USE_MSIX;
+
/* Only 1 MSI vector for bhyve */
pci_emul_add_msicap(vs->vs_pi, 1);
+
+ /* Legacy interrupts are mandatory for virtio devices */
+ pci_lintr_request(vs->vs_pi);
+
return (0);
}
@@ -188,6 +194,7 @@ vi_vq_init(struct virtio_softc *vs, uint32_t pfn)
/* Mark queue as allocated, and start at 0 when we use it. */
vq->vq_flags = VQ_ALLOC;
vq->vq_last_avail = 0;
+ vq->vq_save_used = 0;
}
/*
@@ -247,12 +254,12 @@ _vq_record(int i, volatile struct virtio_desc *vd, struct vmctx *ctx,
* that vq_has_descs() does one).
*/
int
-vq_getchain(struct vqueue_info *vq,
+vq_getchain(struct vqueue_info *vq, uint16_t *pidx,
struct iovec *iov, int n_iov, uint16_t *flags)
{
int i;
u_int ndesc, n_indir;
- u_int idx, head, next;
+ u_int idx, next;
volatile struct virtio_desc *vdir, *vindir, *vp;
struct vmctx *ctx;
struct virtio_softc *vs;
@@ -295,8 +302,8 @@ vq_getchain(struct vqueue_info *vq,
* index, but we just abort if the count gets excessive.
*/
ctx = vs->vs_pi->pi_vmctx;
- head = vq->vq_avail->va_ring[idx & (vq->vq_qsize - 1)];
- next = head;
+ *pidx = next = vq->vq_avail->va_ring[idx & (vq->vq_qsize - 1)];
+ vq->vq_last_avail++;
for (i = 0; i < VQ_MAX_DESCRIPTORS; next = vdir->vd_next) {
if (next >= vq->vq_qsize) {
fprintf(stderr,
@@ -309,7 +316,7 @@ vq_getchain(struct vqueue_info *vq,
if ((vdir->vd_flags & VRING_DESC_F_INDIRECT) == 0) {
_vq_record(i, vdir, ctx, iov, n_iov, flags);
i++;
- } else if ((vs->vs_negotiated_caps &
+ } else if ((vs->vs_vc->vc_hv_caps &
VIRTIO_RING_F_INDIRECT_DESC) == 0) {
fprintf(stderr,
"%s: descriptor has forbidden INDIRECT flag, "
@@ -370,16 +377,29 @@ loopy:
}
/*
- * Return the currently-first request chain to the guest, setting
- * its I/O length to the provided value.
+ * Return the currently-first request chain back to the available queue.
+ *
+ * (This chain is the one you handled when you called vq_getchain()
+ * and used its positive return value.)
+ */
+void
+vq_retchain(struct vqueue_info *vq)
+{
+
+ vq->vq_last_avail--;
+}
+
+/*
+ * Return specified request chain to the guest, setting its I/O length
+ * to the provided value.
*
* (This chain is the one you handled when you called vq_getchain()
* and used its positive return value.)
*/
void
-vq_relchain(struct vqueue_info *vq, uint32_t iolen)
+vq_relchain(struct vqueue_info *vq, uint16_t idx, uint32_t iolen)
{
- uint16_t head, uidx, mask;
+ uint16_t uidx, mask;
volatile struct vring_used *vuh;
volatile struct virtio_used *vue;
@@ -395,11 +415,10 @@ vq_relchain(struct vqueue_info *vq, uint32_t iolen)
*/
mask = vq->vq_qsize - 1;
vuh = vq->vq_used;
- head = vq->vq_avail->va_ring[vq->vq_last_avail++ & mask];
uidx = vuh->vu_idx;
vue = &vuh->vu_ring[uidx++ & mask];
- vue->vu_idx = head; /* ie, vue->id = head */
+ vue->vu_idx = idx;
vue->vu_tlen = iolen;
vuh->vu_idx = uidx;
}
@@ -436,8 +455,8 @@ vq_endchains(struct vqueue_info *vq, int used_all_avail)
* entire avail was processed, we need to interrupt always.
*/
vs = vq->vq_vs;
- new_idx = vq->vq_used->vu_idx;
old_idx = vq->vq_save_used;
+ vq->vq_save_used = new_idx = vq->vq_used->vu_idx;
if (used_all_avail &&
(vs->vs_negotiated_caps & VIRTIO_F_NOTIFY_ON_EMPTY))
intr = 1;
@@ -698,6 +717,9 @@ bad:
switch (offset) {
case VTCFG_R_GUESTCAP:
vs->vs_negotiated_caps = value & vc->vc_hv_caps;
+ if (vc->vc_apply_features)
+ (*vc->vc_apply_features)(DEV_SOFTC(vs),
+ vs->vs_negotiated_caps);
break;
case VTCFG_R_PFN:
if (vs->vs_curq >= vc->vc_nvq)
diff --git a/usr/src/cmd/bhyve/virtio.h b/usr/src/cmd/bhyve/virtio.h
index 1a2ebe8118..28eecc7978 100644
--- a/usr/src/cmd/bhyve/virtio.h
+++ b/usr/src/cmd/bhyve/virtio.h
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/virtio.h 268276 2014-07-05 02:38:53Z grehan $
+ * $FreeBSD$
*/
#ifndef _VIRTIO_H_
@@ -209,7 +209,7 @@ struct vring_used {
#define VIRTIO_VENDOR 0x1AF4
#define VIRTIO_DEV_NET 0x1000
#define VIRTIO_DEV_BLOCK 0x1001
-#define VIRTIO_DEV_RANDOM 0x1002
+#define VIRTIO_DEV_RANDOM 0x1005
/*
* PCI config space constants.
@@ -352,6 +352,8 @@ struct virtio_consts {
/* called to read config regs */
int (*vc_cfgwrite)(void *, int, int, uint32_t);
/* called to write config regs */
+ void (*vc_apply_features)(void *, uint64_t);
+ /* called to apply negotiated features */
uint64_t vc_hv_caps; /* hypervisor-provided capabilities */
};
@@ -423,20 +425,6 @@ vq_has_descs(struct vqueue_info *vq)
}
/*
- * Called by virtio driver as it starts processing chains. Each
- * completed chain (obtained from vq_getchain()) is released by
- * calling vq_relchain(), then when all are done, vq_endchains()
- * can tell if / how-many chains were processed and know whether
- * and how to generate an interrupt.
- */
-static inline void
-vq_startchains(struct vqueue_info *vq)
-{
-
- vq->vq_save_used = vq->vq_used->vu_idx;
-}
-
-/*
* Deliver an interrupt to guest on the given virtual queue
* (if possible, or a generic MSI interrupt if not using MSI-X).
*/
@@ -463,9 +451,10 @@ int vi_intr_init(struct virtio_softc *vs, int barnum, int use_msix);
void vi_reset_dev(struct virtio_softc *);
void vi_set_io_bar(struct virtio_softc *, int);
-int vq_getchain(struct vqueue_info *vq,
+int vq_getchain(struct vqueue_info *vq, uint16_t *pidx,
struct iovec *iov, int n_iov, uint16_t *flags);
-void vq_relchain(struct vqueue_info *vq, uint32_t iolen);
+void vq_retchain(struct vqueue_info *vq);
+void vq_relchain(struct vqueue_info *vq, uint16_t idx, uint32_t iolen);
void vq_endchains(struct vqueue_info *vq, int used_all_avail);
uint64_t vi_pci_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
diff --git a/usr/src/cmd/bhyve/xmsr.c b/usr/src/cmd/bhyve/xmsr.c
index 0c097251e0..26b8e49e20 100644
--- a/usr/src/cmd/bhyve/xmsr.c
+++ b/usr/src/cmd/bhyve/xmsr.c
@@ -23,11 +23,11 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/xmsr.c 279227 2015-02-24 05:15:40Z neel $
+ * $FreeBSD$
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/usr.sbin/bhyve/xmsr.c 279227 2015-02-24 05:15:40Z neel $");
+__FBSDID("$FreeBSD$");
#include <sys/types.h>
diff --git a/usr/src/cmd/bhyve/xmsr.h b/usr/src/cmd/bhyve/xmsr.h
index ac3c147442..bcf65b7127 100644
--- a/usr/src/cmd/bhyve/xmsr.h
+++ b/usr/src/cmd/bhyve/xmsr.h
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: head/usr.sbin/bhyve/xmsr.h 271888 2014-09-20 02:35:21Z neel $
+ * $FreeBSD$
*/
#ifndef _XMSR_H_
diff --git a/usr/src/cmd/bhyveload-uefi/Makefile b/usr/src/cmd/bhyveload-uefi/Makefile
deleted file mode 100644
index bbcbacf32f..0000000000
--- a/usr/src/cmd/bhyveload-uefi/Makefile
+++ /dev/null
@@ -1,41 +0,0 @@
-#
-# This file and its contents are supplied under the terms of the
-# Common Development and Distribution License ("CDDL"), version 1.0.
-# You may only use this file in accordance with the terms of version
-# 1.0 of the CDDL.
-#
-# A full copy of the text of the CDDL should have accompanied this
-# source. A copy of the CDDL is also available via the Internet at
-# http://www.illumos.org/license/CDDL.
-#
-
-#
-# Copyright 2013 Pluribus Networks Inc.
-#
-
-PROG = bhyveload-uefi
-
-include ../Makefile.cmd
-
-$(BUILD64)SUBDIRS += $(MACH64)
-
-all := TARGET = all
-install := TARGET = install
-clean := TARGET = clean
-clobber := TARGET = clobber
-lint := TARGET = lint
-
-.KEEP_STATE:
-
-all clean clobber lint: $(SUBDIRS)
-
-install: $(SUBDIRS)
- -$(RM) $(ROOTUSRSBINPROG)
- -$(LN) $(ISAEXEC) $(ROOTUSRSBINPROG)
-
-$(SUBDIRS): FRC
- @cd $@; pwd; $(MAKE) CW_NO_SHADOW=true __GNUC= $(TARGET)
-
-FRC:
-
-include ../Makefile.targ
diff --git a/usr/src/cmd/bhyveload-uefi/Makefile.com b/usr/src/cmd/bhyveload-uefi/Makefile.com
deleted file mode 100644
index 244a29a454..0000000000
--- a/usr/src/cmd/bhyveload-uefi/Makefile.com
+++ /dev/null
@@ -1,51 +0,0 @@
-#
-# This file and its contents are supplied under the terms of the
-# Common Development and Distribution License ("CDDL"), version 1.0.
-# You may only use this file in accordance with the terms of version
-# 1.0 of the CDDL.
-#
-# A full copy of the text of the CDDL should have accompanied this
-# source. A copy of the CDDL is also available via the Internet at
-# http://www.illumos.org/license/CDDL.
-#
-
-#
-# Copyright 2013 Pluribus Networks Inc.
-#
-
-PROG= bhyveload-uefi
-
-SRCS = ../bhyveload-uefi.c expand_number.c
-OBJS = bhyveload-uefi.o expand_number.o
-
-include ../../Makefile.cmd
-
-.KEEP_STATE:
-
-CFLAGS += $(CCVERBOSE)
-CPPFLAGS = -I$(COMPAT)/freebsd -I$(CONTRIB)/freebsd $(CPPFLAGS.master) \
- -I$(ROOT)/usr/platform/i86pc/include
-LDLIBS += -lvmmapi
-
-all: $(PROG)
-
-$(PROG): $(OBJS)
- $(LINK.c) -o $@ $(OBJS) $(LDFLAGS) $(LDLIBS)
- $(POST_PROCESS)
-
-install: all $(ROOTUSRSBINPROG)
-
-clean:
- $(RM) $(OBJS)
-
-lint: lint_SRCS
-
-include ../../Makefile.targ
-
-%.o: ../%.c
- $(COMPILE.c) $<
- $(POST_PROCESS_O)
-
-%.o: $(CONTRIB)/freebsd/lib/libutil/%.c
- $(COMPILE.c) $<
- $(POST_PROCESS_O)
diff --git a/usr/src/cmd/bhyveload-uefi/amd64/Makefile b/usr/src/cmd/bhyveload-uefi/amd64/Makefile
deleted file mode 100644
index b602c50d05..0000000000
--- a/usr/src/cmd/bhyveload-uefi/amd64/Makefile
+++ /dev/null
@@ -1,21 +0,0 @@
-#
-# This file and its contents are supplied under the terms of the
-# Common Development and Distribution License ("CDDL"), version 1.0.
-# You may only use this file in accordance with the terms of version
-# 1.0 of the CDDL.
-#
-# A full copy of the text of the CDDL should have accompanied this
-# source. A copy of the CDDL is also available via the Internet at
-# http://www.illumos.org/license/CDDL.
-#
-
-#
-# Copyright 2013 Pluribus Networks Inc.
-#
-
-include ../Makefile.com
-include ../../Makefile.cmd.64
-
-CPPFLAGS += -I$(COMPAT)/freebsd/amd64 -I$(CONTRIB)/freebsd/amd64
-
-install: all $(ROOTUSRSBINPROG64)
diff --git a/usr/src/cmd/bhyveload-uefi/bhyveload-uefi.c b/usr/src/cmd/bhyveload-uefi/bhyveload-uefi.c
deleted file mode 100644
index f3bc6630f6..0000000000
--- a/usr/src/cmd/bhyveload-uefi/bhyveload-uefi.c
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * This file and its contents are supplied under the terms of the
- * Common Development and Distribution License ("CDDL"), version 1.0.
- * You may only use this file in accordance with the terms of version
- * 1.0 of the CDDL.
- *
- * A full copy of the text of the CDDL should have accompanied this
- * source. A copy of the CDDL is also available via the Internet at
- * http://www.illumos.org/license/CDDL.
- */
-
-/*
- * Copyright 2013 Pluribus Networks Inc.
- * Copyright 2017 Joyent, Inc.
- */
-
-#include <sys/types.h>
-
-#include <machine/vmm.h>
-
-#include <errno.h>
-#include <err.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sysexits.h>
-#include <unistd.h>
-
-#include <vmmapi.h>
-
-#define KB (1024UL)
-#define MB (1024 * 1024UL)
-#define GB (1024 * 1024 * 1024UL)
-
-#define UEFI_ROM_ADDR 0xFFE00000
-#define UEFI_ROM_SIZE (2 * MB)
-/*
- * N.B. the UEFI code zeros the first page in memory so use the second.
- */
-#define BHYVE_HOB_ADDR 0x00002000
-#define BHYVE_BO_HOB_ADDR 0x00002080
-
-#define UEFI_ROM_PATH "/usr/share/bhyve/uefi-rom.bin"
-
-struct platform_info {
- uint32_t ncpus;
-};
-
-/*
- * Boot order code:
- * 0 - EFI_CD_HD
- * 1 - EFI_CD
- * 2 - EFI_HD_CD
- * 3 - EFI_HD
- * 4 - EFI_NET
- * 5 - EFI_NET_CD_HD
- * 6 - EFI_HD_HD_CD
- * 7 - LEGACY_CD_HD
- * 8 - LEGACY_CD
- * 9 - LEGACY_HD_CD
- * 10 - LEGACY_HD
- * 11 - EFI_SHELL
- */
-
-struct bootorder_info {
- uint32_t guestbootorder;
-};
-
-static char *vmname, *progname;
-static struct vmctx *ctx;
-
-static void
-usage(void)
-{
- printf("usage: %s "
- "[-c vcpus] [-m mem-size] [-b bootorder]"
- "<vmname>\n", progname);
- exit(1);
-}
-
-int
-main(int argc, char **argv)
-{
- int opt, error, fd;
- int guest_ncpus;
- int guest_bootorder = 0;
- uint64_t mem_size;
- char *membase, *rombase;
- struct platform_info *pi;
- struct bootorder_info *bi;
-
- progname = argv[0];
-
- guest_ncpus = 1;
- mem_size = 256 * MB;
-
- while ((opt = getopt(argc, argv, "c:m:b:")) != -1) {
- switch (opt) {
- case 'c':
- guest_ncpus = atoi(optarg);
- break;
- case 'm':
- error = vm_parse_memsize(optarg, &mem_size);
- if (error != 0 || mem_size == 0)
- errx(EX_USAGE, "Invalid memsize '%s'", optarg);
- break;
- case 'b':
- guest_bootorder = atoi(optarg);
- if (guest_bootorder < 0 || guest_bootorder > 11) {
- errx(EX_USAGE, "Invalid bootoption: %d\n"
- "\tBoot order code:\n"
- "\t0 - EFI_CD_HD\n"
- "\t1 - EFI_CD\n"
- "\t2 - EFI_HD_CD\n"
- "\t3 - EFI_HD\n"
- "\t4 - EFI_NET\n"
- "\t5 - EFI_NET_CD_HD\n"
- "\t6 - EFI_HD_HD_CD\n"
- "\t7 - LEGACY_CD_HD\n"
- "\t8 - LEGACY_CD\n"
- "\t9 - LEGACY_HD_CD\n"
- "\t10 - LEGACY_HD\n"
- "\t11 - EFI_SHELL\n", guest_bootorder);
- exit(1);
- }
- break;
- case '?':
- usage();
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (argc != 1)
- usage();
-
- vmname = argv[0];
- error = vm_create(vmname);
- if (error != 0 && errno != EEXIST) {
- perror("vm_create");
- exit(1);
-
- }
-
- ctx = vm_open(vmname);
- if (ctx == NULL) {
- perror("vm_open");
- exit(1);
- }
-
- error = vm_set_capability(ctx, 0, VM_CAP_UNRESTRICTED_GUEST, 1);
- if (error) {
- perror("vm_set_capability(VM_CAP_UNRESTRICTED_GUEST)");
- }
-
- error = vm_setup_memory(ctx, mem_size, VM_MMAP_ALL);
- if (error) {
- perror("vm_setup_memory");
- exit(1);
- }
- membase = vm_map_gpa(ctx, 0, 8 * KB);
-
- error = vm_setup_rom(ctx, UEFI_ROM_ADDR, UEFI_ROM_SIZE);
- if (error) {
- perror("vm_setup_rom");
- exit(1);
- }
- rombase = vm_map_gpa(ctx, UEFI_ROM_ADDR, UEFI_ROM_SIZE);
-
- fd = open(UEFI_ROM_PATH, O_RDONLY);
- if (fd == -1) {
- perror("open");
- exit(1);
- }
- read(fd, rombase, UEFI_ROM_SIZE);
- close(fd);
-
- pi = (struct platform_info *)(membase + BHYVE_HOB_ADDR);
- pi->ncpus = guest_ncpus;
- bi = (struct bootorder_info *)(membase + BHYVE_BO_HOB_ADDR);
- bi->guestbootorder = guest_bootorder;
-
- error = vcpu_reset(ctx, 0);
- if (error) {
- perror("vcpu_reset");
- exit(1);
- }
-
- return (0);
-}
diff --git a/usr/src/cmd/bhyveload-uefi/i386/Makefile b/usr/src/cmd/bhyveload-uefi/i386/Makefile
deleted file mode 100644
index f5b7bb6915..0000000000
--- a/usr/src/cmd/bhyveload-uefi/i386/Makefile
+++ /dev/null
@@ -1,18 +0,0 @@
-#
-# This file and its contents are supplied under the terms of the
-# Common Development and Distribution License ("CDDL"), version 1.0.
-# You may only use this file in accordance with the terms of version
-# 1.0 of the CDDL.
-#
-# A full copy of the text of the CDDL should have accompanied this
-# source. A copy of the CDDL is also available via the Internet at
-# http://www.illumos.org/license/CDDL.
-#
-
-#
-# Copyright 2013 Pluribus Networks Inc.
-#
-
-include ../Makefile.com
-
-install: all $(ROOTUSRSBINPROG32)
diff --git a/usr/src/cmd/mdb/intel/amd64/Makefile b/usr/src/cmd/mdb/intel/amd64/Makefile
index e51c3c5a69..7749b75f37 100644
--- a/usr/src/cmd/mdb/intel/amd64/Makefile
+++ b/usr/src/cmd/mdb/intel/amd64/Makefile
@@ -21,11 +21,12 @@
#
# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
+# Copyright 2017 Joyent, Inc.
#
include ../../Makefile.common
-MODULES = $(COMMON_MODULES_PROC) $(COMMON_MODULES_KVM) uhci
+MODULES = $(COMMON_MODULES_PROC) $(COMMON_MODULES_KVM) uhci vmm
SUBDIRS = mdb mdb_ks kmdb libstandctf libstand .WAIT $(MODULES)
diff --git a/usr/src/compat/freebsd/amd64/machine/atomic.h b/usr/src/compat/freebsd/amd64/machine/atomic.h
index 5b78143d21..47a26bd2d3 100644
--- a/usr/src/compat/freebsd/amd64/machine/atomic.h
+++ b/usr/src/compat/freebsd/amd64/machine/atomic.h
@@ -11,6 +11,7 @@
/*
* Copyright 2014 Pluribus Networks Inc.
+ * Copyright 2017 Joyent, Inc.
*/
#ifndef _COMPAT_FREEBSD_AMD64_MACHINE_ATOMIC_H_
@@ -241,4 +242,6 @@ atomic_swap_long(volatile u_long *p, u_long v)
/* Operations on pointers. */
#define atomic_cmpset_ptr atomic_cmpset_long
+#define mb() __asm __volatile("mfence;" : : : "memory")
+
#endif /* _COMPAT_FREEBSD_AMD64_MACHINE_ATOMIC_H_ */
diff --git a/usr/src/compat/freebsd/amd64/machine/iodev.h b/usr/src/compat/freebsd/amd64/machine/iodev.h
new file mode 100644
index 0000000000..c7cdddc817
--- /dev/null
+++ b/usr/src/compat/freebsd/amd64/machine/iodev.h
@@ -0,0 +1,19 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2017 Joyent, Inc.
+ */
+
+#ifndef _COMPAT_FREEBSD_AMD64_MACHINE_IODEV_H
+#define _COMPAT_FREEBSD_AMD64_MACHINE_IODEV_H
+
+#endif /* _COMPAT_FREEBSD_AMD64_MACHINE_IODEV_H */
diff --git a/usr/src/compat/freebsd/net/ethernet.h b/usr/src/compat/freebsd/net/ethernet.h
index a0d5a828c6..dcd3a58925 100644
--- a/usr/src/compat/freebsd/net/ethernet.h
+++ b/usr/src/compat/freebsd/net/ethernet.h
@@ -11,11 +11,25 @@
/*
* Copyright 2013 Pluribus Networks Inc.
+ * Copyright 2017 Joyent, Inc.
*/
#ifndef _COMPAT_FREEBSD_SYS_NET_ETHERNET_H_
#define _COMPAT_FREEBSD_SYS_NET_ETHERNET_H_
+#define ether_addr_octet octet
+
#include <sys/ethernet.h>
+/*
+ * Some basic Ethernet constants.
+ */
+#define ETHER_ADDR_LEN 6 /* length of an Ethernet address */
+#define ETHER_CRC_LEN 4 /* length of the Ethernet CRC */
+#define ETHER_MIN_LEN 64 /* minimum frame len, including CRC */
+
+#define ETHER_VLAN_ENCAP_LEN 4 /* len of 802.1Q VLAN encapsulation */
+
+#define ETHER_IS_MULTICAST(addr) (*(addr) & 0x01) /* is address mcast/bcast? */
+
#endif /* _COMPAT_FREEBSD_SYS_NET_ETHERNET_H_ */
diff --git a/usr/src/compat/freebsd/sys/cdefs.h b/usr/src/compat/freebsd/sys/cdefs.h
index 1d3f4d5434..1713b05630 100644
--- a/usr/src/compat/freebsd/sys/cdefs.h
+++ b/usr/src/compat/freebsd/sys/cdefs.h
@@ -17,9 +17,30 @@
#ifndef _COMPAT_FREEBSD_SYS_CDEFS_H_
#define _COMPAT_FREEBSD_SYS_CDEFS_H_
+/*
+ * Testing against Clang-specific extensions.
+ */
+#ifndef __has_extension
+#define __has_extension __has_feature
+#endif
+#ifndef __has_feature
+#define __has_feature(x) 0
+#endif
+
+/*
+ * Macro to test if we're using a specific version of gcc or later.
+ */
+#if defined(__GNUC__) && !defined(__INTEL_COMPILER)
+#define __GNUC_PREREQ__(ma, mi) \
+ (__GNUC__ > (ma) || __GNUC__ == (ma) && __GNUC_MINOR__ >= (mi))
+#else
+#define __GNUC_PREREQ__(ma, mi) 0
+#endif
+
#define __FBSDID(s)
#ifdef __GNUC__
+#define asm __asm
#define inline __inline
#define __GNUCLIKE___SECTION 1
@@ -56,4 +77,25 @@
#define __STRING(x) "x"
#endif /* !(__STDC__ || __cplusplus) */
+#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112L || defined(lint)
+
+#if !__has_extension(c_static_assert)
+#if (defined(__cplusplus) && __cplusplus >= 201103L) || \
+ __has_extension(cxx_static_assert)
+#define _Static_assert(x, y) static_assert(x, y)
+#elif __GNUC_PREREQ__(4,6)
+/* Nothing, gcc 4.6 and higher has _Static_assert built-in */
+#elif defined(__COUNTER__)
+#define _Static_assert(x, y) __Static_assert(x, __COUNTER__)
+#define __Static_assert(x, y) ___Static_assert(x, y)
+#define ___Static_assert(x, y) typedef char __assert_ ## y[(x) ? 1 : -1] \
+ __unused
+#else
+#define _Static_assert(x, y) struct __hack
+#endif
+#endif
+#define static_assert(x, y) _Static_assert(x, y)
+
+#endif /* __STDC_VERSION__ || __STDC_VERSION__ < 201112L */
+
#endif /* _COMPAT_FREEBSD_SYS_CDEFS_H_ */
diff --git a/usr/src/compat/freebsd/sys/limits.h b/usr/src/compat/freebsd/sys/limits.h
index 99ae0f4d64..0e66319791 100644
--- a/usr/src/compat/freebsd/sys/limits.h
+++ b/usr/src/compat/freebsd/sys/limits.h
@@ -11,9 +11,14 @@
/*
* Copyright 2013 Pluribus Networks Inc.
+ * Copyright 2017 Joyent, Inc.
*/
#ifndef _COMPAT_FREEBSD_SYS_LIMITS_H_
#define _COMPAT_FREEBSD_SYS_LIMITS_H_
+#include_next <limits.h>
+
+#define OFF_MAX ((off_t)-1)
+
#endif /* _COMPAT_FREEBSD_SYS_LIMITS_H_ */
diff --git a/usr/src/compat/freebsd/sys/param.h b/usr/src/compat/freebsd/sys/param.h
index eade1caf5b..b125f9014f 100644
--- a/usr/src/compat/freebsd/sys/param.h
+++ b/usr/src/compat/freebsd/sys/param.h
@@ -19,6 +19,8 @@
#ifndef _KERNEL
#define MAXCOMLEN 16
+/* default value of the kernel tunable 'maxphys' in i86pc */
+#define MAXPHYS (56 * 1024)
#endif
#define MAXHOSTNAMELEN 256
#define SPECNAMELEN 63
@@ -38,6 +40,7 @@
#define nitems(x) (sizeof((x)) / sizeof((x)[0]))
#define rounddown(x,y) (((x)/(y))*(y))
+#define rounddown2(x, y) ((x)&(~((y)-1))) /* if y is power of two */
#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* to any y */
#define roundup2(x,y) (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */
#define powerof2(x) ((((x)-1)&(x))==0)
diff --git a/usr/src/compat/freebsd/sys/socket.h b/usr/src/compat/freebsd/sys/socket.h
new file mode 100644
index 0000000000..3bf7a8f440
--- /dev/null
+++ b/usr/src/compat/freebsd/sys/socket.h
@@ -0,0 +1,23 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2017 Joyent, Inc.
+ */
+
+#ifndef _COMPAT_FREEBSD_SYS_SOCKET_H
+#define _COMPAT_FREEBSD_SYS_SOCKET_H
+
+#include_next <sys/socket.h>
+
+#define SO_NOSIGPIPE 0
+
+#endif /* _COMPAT_FREEBSD_SYS_SOCKET_H */
diff --git a/usr/src/compat/freebsd/unistd.h b/usr/src/compat/freebsd/unistd.h
new file mode 100644
index 0000000000..b4357e1da5
--- /dev/null
+++ b/usr/src/compat/freebsd/unistd.h
@@ -0,0 +1,23 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2017 Joyent, Inc.
+ */
+
+#ifndef _COMPAT_FREEBSD_UNISTD_H
+#define _COMPAT_FREEBSD_UNISTD_H
+
+#define setproctitle(fmt, ...)
+
+#include_next <unistd.h>
+
+#endif /* _COMPAT_FREEBSD_UNISTD_H */