Module Name: src Committed By: hikaru Date: Tue Jun 10 01:42:39 UTC 2014
Modified Files: src/distrib/sets/lists/man: mi src/share/man/man4/man4.x86: Makefile src/sys/arch/amd64/conf: ALL GENERIC src/sys/arch/i386/conf: ALL GENERIC src/sys/arch/x86/pci: files.pci Added Files: src/share/man/man4/man4.x86: vmx.4 src/sys/arch/x86/pci: if_vmx.c if_vmxreg.h Log Message: Add VMware VMXNET3 ethernet driver from OpenBSD, vmx(4). To generate a diff of this commit: cvs rdiff -u -r1.1474 -r1.1475 src/distrib/sets/lists/man/mi cvs rdiff -u -r1.14 -r1.15 src/share/man/man4/man4.x86/Makefile cvs rdiff -u -r0 -r1.1 src/share/man/man4/man4.x86/vmx.4 cvs rdiff -u -r1.8 -r1.9 src/sys/arch/amd64/conf/ALL cvs rdiff -u -r1.387 -r1.388 src/sys/arch/amd64/conf/GENERIC cvs rdiff -u -r1.375 -r1.376 src/sys/arch/i386/conf/ALL cvs rdiff -u -r1.1104 -r1.1105 src/sys/arch/i386/conf/GENERIC cvs rdiff -u -r1.15 -r1.16 src/sys/arch/x86/pci/files.pci cvs rdiff -u -r0 -r1.1 src/sys/arch/x86/pci/if_vmx.c \ src/sys/arch/x86/pci/if_vmxreg.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/distrib/sets/lists/man/mi diff -u src/distrib/sets/lists/man/mi:1.1474 src/distrib/sets/lists/man/mi:1.1475 --- src/distrib/sets/lists/man/mi:1.1474 Sun May 18 18:24:16 2014 +++ src/distrib/sets/lists/man/mi Tue Jun 10 01:42:38 2014 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.1474 2014/05/18 18:24:16 jakllsch Exp $ +# $NetBSD: mi,v 1.1475 2014/06/10 01:42:38 hikaru Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -1970,6 +1970,7 @@ ./usr/share/man/cat4/x86/tprof_amdpmi.0 man-sys-catman .cat ./usr/share/man/cat4/x86/tprof_pmi.0 man-sys-catman .cat ./usr/share/man/cat4/x86/vmt.0 man-sys-catman .cat +./usr/share/man/cat4/x86/vmx.0 man-sys-catman .cat ./usr/share/man/cat4/xbd.0 man-sys-catman .cat ./usr/share/man/cat4/xbdback.0 man-sys-catman .cat ./usr/share/man/cat4/xbox.0 man-sys-catman .cat @@ -4929,6 +4930,7 @@ ./usr/share/man/html4/x86/tprof_amdpmi.html man-sys-htmlman html ./usr/share/man/html4/x86/tprof_pmi.html man-sys-htmlman html ./usr/share/man/html4/x86/vmt.html man-sys-htmlman html +./usr/share/man/html4/x86/vmx.html man-sys-htmlman html ./usr/share/man/html4/xbd.html man-sys-htmlman html ./usr/share/man/html4/xbdback.html man-sys-htmlman html ./usr/share/man/html4/xbox.html man-sys-htmlman html @@ -7818,6 +7820,7 @@ ./usr/share/man/man4/x86/tprof_amdpmi.4 man-sys-man .man ./usr/share/man/man4/x86/tprof_pmi.4 man-sys-man .man ./usr/share/man/man4/x86/vmt.4 man-sys-man .man +./usr/share/man/man4/x86/vmx.4 man-sys-man .man ./usr/share/man/man4/xbd.4 man-sys-man .man ./usr/share/man/man4/xbdback.4 man-sys-man .man ./usr/share/man/man4/xbox.4 man-sys-man .man Index: src/share/man/man4/man4.x86/Makefile diff -u src/share/man/man4/man4.x86/Makefile:1.14 src/share/man/man4/man4.x86/Makefile:1.15 --- src/share/man/man4/man4.x86/Makefile:1.14 Mon Jun 10 07:14:01 2013 +++ src/share/man/man4/man4.x86/Makefile Tue Jun 10 01:42:39 2014 @@ -1,8 +1,8 @@ -# $NetBSD: Makefile,v 1.14 2013/06/10 07:14:01 kardel Exp $ +# $NetBSD: Makefile,v 1.15 2014/06/10 01:42:39 hikaru Exp $ MAN= amdpcib.4 apic.4 balloon.4 coretemp.4 est.4 fdc.4 \ fwhrng.4 hpet.4 ichlpcib.4 lpt.4 mem.4 odcm.4 powernow.4 \ - soekrisgpio.4 tprof_amdpmi.4 tprof_pmi.4 vmt.4 + soekrisgpio.4 tprof_amdpmi.4 tprof_pmi.4 vmt.4 vmx.4 MLINKS+=apic.4 ioapic.4 \ apic.4 lapic.4 Index: src/sys/arch/amd64/conf/ALL diff -u src/sys/arch/amd64/conf/ALL:1.8 src/sys/arch/amd64/conf/ALL:1.9 --- src/sys/arch/amd64/conf/ALL:1.8 Mon Jun 2 02:11:51 2014 +++ src/sys/arch/amd64/conf/ALL Tue Jun 10 01:42:39 2014 @@ -1,4 +1,4 @@ -# $NetBSD: ALL,v 1.8 2014/06/02 02:11:51 dholland Exp $ +# $NetBSD: ALL,v 1.9 2014/06/10 01:42:39 hikaru Exp $ # From NetBSD: GENERIC,v 1.787 2006/10/01 18:37:54 bouyer Exp # # ALL machine description file @@ -17,7 +17,7 @@ include "arch/amd64/conf/std.amd64" options INCLUDE_CONFIG_FILE # embed config file in kernel binary -#ident "ALL-$Revision: 1.8 $" +#ident "ALL-$Revision: 1.9 $" maxusers 64 # estimated number of users @@ -928,6 +928,7 @@ tl* at pci? dev ? function ? # ThunderLA tlp* at pci? dev ? function ? # DECchip 21x4x and clones txp* at pci? dev ? function ? # 3com 3cr990 vge* at pci? dev ? function ? # VIATech VT612X Gigabit Ethernet +vmx* at pci? dev ? function ? # VMware VMXNET3 vr* at pci? dev ? function ? # VIA Rhine Fast Ethernet vte* at pci? dev ? function ? # Vortex86 RDC R6040 Fast Ethernet wi* at pci? dev ? function ? # Intersil Prism Mini-PCI (802.11b) Index: src/sys/arch/amd64/conf/GENERIC diff -u src/sys/arch/amd64/conf/GENERIC:1.387 src/sys/arch/amd64/conf/GENERIC:1.388 --- src/sys/arch/amd64/conf/GENERIC:1.387 Thu May 29 14:49:35 2014 +++ src/sys/arch/amd64/conf/GENERIC Tue Jun 10 01:42:39 2014 @@ -1,4 +1,4 @@ -# $NetBSD: GENERIC,v 1.387 2014/05/29 14:49:35 christos Exp $ +# $NetBSD: GENERIC,v 1.388 2014/06/10 01:42:39 hikaru Exp $ # # GENERIC machine description file # @@ -22,7 +22,7 @@ include "arch/amd64/conf/std.amd64" options INCLUDE_CONFIG_FILE # embed config file in kernel binary -#ident "GENERIC-$Revision: 1.387 $" +#ident "GENERIC-$Revision: 1.388 $" maxusers 64 # estimated number of users @@ -740,6 +740,7 @@ tl* at pci? dev ? function ? # ThunderLA tlp* at pci? dev ? function ? # DECchip 21x4x and clones txp* at pci? dev ? function ? # 3com 3cr990 vge* at pci? dev ? function ? # VIATech VT612X Gigabit Ethernet +vmx* at pci? dev ? function ? # VMware VMXNET3 vr* at pci? dev ? function ? # VIA Rhine Fast Ethernet wi* at pci? dev ? function ? # Intersil Prism Mini-PCI (802.11b) wm* at pci? dev ? function ? # Intel 82543/82544 gigabit Index: src/sys/arch/i386/conf/ALL diff -u src/sys/arch/i386/conf/ALL:1.375 src/sys/arch/i386/conf/ALL:1.376 --- src/sys/arch/i386/conf/ALL:1.375 Mon Jun 2 02:11:52 2014 +++ src/sys/arch/i386/conf/ALL Tue Jun 10 01:42:39 2014 @@ -1,4 +1,4 @@ -# $NetBSD: ALL,v 1.375 2014/06/02 02:11:52 dholland Exp $ +# $NetBSD: ALL,v 1.376 2014/06/10 01:42:39 hikaru Exp $ # From NetBSD: GENERIC,v 1.787 2006/10/01 18:37:54 bouyer Exp # # ALL machine description file @@ -17,7 +17,7 @@ include "arch/i386/conf/std.i386" options INCLUDE_CONFIG_FILE # embed config file in kernel binary -#ident "ALL-$Revision: 1.375 $" +#ident "ALL-$Revision: 1.376 $" maxusers 64 # estimated number of users @@ -1052,6 +1052,7 @@ tl* at pci? dev ? function ? # ThunderLA tlp* at pci? dev ? function ? # DECchip 21x4x and clones txp* at pci? dev ? function ? # 3com 3cr990 vge* at pci? dev ? function ? # VIATech VT612X Gigabit Ethernet +vmx* at pci? dev ? function ? # VMware VMXNET3 vr* at pci? dev ? function ? # VIA Rhine Fast Ethernet vte* at pci? dev ? function ? # Vortex86 RDC R6040 Fast Ethernet wi* at pci? dev ? function ? # Intersil Prism Mini-PCI (802.11b) Index: src/sys/arch/i386/conf/GENERIC diff -u src/sys/arch/i386/conf/GENERIC:1.1104 src/sys/arch/i386/conf/GENERIC:1.1105 --- src/sys/arch/i386/conf/GENERIC:1.1104 Thu May 29 14:48:40 2014 +++ src/sys/arch/i386/conf/GENERIC Tue Jun 10 01:42:39 2014 @@ -1,4 +1,4 @@ -# $NetBSD: GENERIC,v 1.1104 2014/05/29 14:48:40 christos Exp $ +# $NetBSD: GENERIC,v 1.1105 2014/06/10 01:42:39 hikaru Exp $ # # GENERIC machine description file # @@ -22,7 +22,7 @@ include "arch/i386/conf/std.i386" options INCLUDE_CONFIG_FILE # embed config file in kernel binary -#ident "GENERIC-$Revision: 1.1104 $" +#ident "GENERIC-$Revision: 1.1105 $" maxusers 64 # estimated number of users @@ -992,6 +992,7 @@ tlp* at pci? dev ? function ? # DECchip txp* at pci? dev ? function ? # 3com 3cr990 vte* at pci? dev ? function ? # RDC R6040 10/100 Ethernet vge* at pci? dev ? function ? # VIATech VT612X Gigabit Ethernet +vmx* at pci? dev ? function ? # VMware VMXNET3 vr* at pci? dev ? function ? # VIA Rhine Fast Ethernet wi* at pci? dev ? function ? # Intersil Prism Mini-PCI (802.11b) wm* at pci? dev ? function ? # Intel 8254x gigabit Index: src/sys/arch/x86/pci/files.pci diff -u src/sys/arch/x86/pci/files.pci:1.15 src/sys/arch/x86/pci/files.pci:1.16 --- src/sys/arch/x86/pci/files.pci:1.15 Wed Dec 5 16:19:46 2012 +++ src/sys/arch/x86/pci/files.pci Tue Jun 10 01:42:39 2014 @@ -1,4 +1,4 @@ -# $NetBSD: files.pci,v 1.15 2012/12/05 16:19:46 christos Exp $ +# $NetBSD: files.pci,v 1.16 2014/06/10 01:42:39 hikaru Exp $ device aapic attach aapic at pci @@ -59,3 +59,8 @@ file arch/x86/pci/tcpcib.c tcpcib device fwhrng attach fwhrng at fwhichbus file arch/x86/pci/fwhrng.c fwhrng needs-flag + +# VMware VMXNET3 virtual interface +device vmx: ether, ifnet, arp +attach vmx at pci +file arch/x86/pci/if_vmx.c vmx Added files: Index: src/share/man/man4/man4.x86/vmx.4 diff -u /dev/null src/share/man/man4/man4.x86/vmx.4:1.1 --- /dev/null Tue Jun 10 01:42:39 2014 +++ src/share/man/man4/man4.x86/vmx.4 Tue Jun 10 01:42:39 2014 @@ -0,0 +1,113 @@ +.\" $NetBSD: vmx.4,v 1.1 2014/06/10 01:42:39 hikaru Exp $ +.\" $OpenBSD: vmx.4,v 1.1 2013/05/31 20:18:44 reyk Exp $ +.\" +.\" Copyright (c) 2006,2013 Reyk Floeter <r...@openbsd.org> +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd June 7, 2014 +.Dt VMX 4 +.Os +.Sh NAME +.Nm vmx +.Nd VMware VMXNET3 Virtual Interface Controller device +.Sh SYNOPSIS +.Cd "vmx* at pci? dev ? function ?" +.Sh DESCRIPTION +The +.Nm +driver provides support for the VMXNET3 virtual NIC available in virtual +machines by VMware. +It appears as a simple Ethernet device but is actually a virtual network +interface to the underlying host operating system. +.Pp +This driver supports the +.Ic VMXNET3 +driver protocol, as an alternative to the emulated +.Xr pcn 4 , +.Xr wm 4 +interfaces also available in the VMware environment. +The +.Nm vmx +driver is optimized for the virtual machine, it can provide advanced +capabilities depending on the underlying host operating system and +the physical network interface controller of the host. +In comparison to the earlier VMXNET versions, +VMXNET3 supports additional features like multiqueue support, IPv6 +checksum offloading, MSI/MSI-X support and hardware VLAN tagging in +VMware's VLAN Guest Tagging (VGT) mode. +.Pp +The +.Nm +driver supports VMXNET3 VMware virtual NICs provided by the virtual +machine hardware version 7 or newer, as provided by the following +products: +.Pp +.Bl -bullet -compact -offset indent +.It +VMware ESX/ESXi 4.0 and newer +.It +VMware Server 2.0 and newer +.It +VMware Workstation 6.5 and newer +.It +VMware Fusion 2.0 and newer +.El +.Pp +The +.Nm +driver supports the following media types: +.Bl -tag -width autoselect +.It autoselect +Enable autoselection of the media type and options. +The driver always uses the fastest available speed and the media +options provided by the underlying host of the virtual machine. +.It 10GbaseT mediaopt full-duplex +Set 10Gbps operation. +.It 1000baseT mediaopt full-duplex +Set 1000Mbps operation. +.El +.Pp +For more information on configuring this device, see +.Xr ifconfig 8 . +.Sh EXAMPLES +The following entry must be added to the VMware configuration file +to provide the +.Nm +device: +.Bd -literal -offset indent +ethernet0.virtualDev = "vmxnet3" +.Ed +.Sh SEE ALSO +.Xr arp 4 , +.Xr wm 4 , +.Xr ifmedia 4 , +.Xr intro 4 , +.Xr netintro 4 , +.Xr pci 4 , +.Xr pcn 4 , +.Xr ifconfig 8 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Ox 5.5 . +.Sh AUTHORS +.An -nosplit +The +.Nm +driver was originally written by +.An Tsubai Masanari . +.Nx porting was done by +.An Hikaru ABE +.Aq Mt hik...@netbsd.org . Index: src/sys/arch/x86/pci/if_vmx.c diff -u /dev/null src/sys/arch/x86/pci/if_vmx.c:1.1 --- /dev/null Tue Jun 10 01:42:39 2014 +++ src/sys/arch/x86/pci/if_vmx.c Tue Jun 10 01:42:39 2014 @@ -0,0 +1,1246 @@ +/* $NetBSD: if_vmx.c,v 1.1 2014/06/10 01:42:39 hikaru Exp $ */ +/* $OpenBSD: if_vmx.c,v 1.16 2014/01/22 06:04:17 brad Exp $ */ + +/* + * Copyright (c) 2013 Tsubai Masanari + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: if_vmx.c,v 1.1 2014/06/10 01:42:39 hikaru Exp $"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/device.h> +#include <sys/mbuf.h> +#include <sys/sockio.h> + +#include <net/bpf.h> +#include <net/if.h> +#include <net/if_ether.h> +#include <net/if_media.h> + +#include <netinet/if_inarp.h> +#include <netinet/in_systm.h> /* for <netinet/ip.h> */ +#include <netinet/in.h> /* for <netinet/ip.h> */ +#include <netinet/ip.h> /* for struct ip */ +#include <netinet/tcp.h> /* for struct tcphdr */ +#include <netinet/udp.h> /* for struct udphdr */ + +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcidevs.h> + +#include <arch/x86/pci/if_vmxreg.h> + +#define NRXQUEUE 1 +#define NTXQUEUE 1 + +#define NTXDESC 128 /* tx ring size */ +#define NTXSEGS 8 /* tx descriptors per packet */ +#define NRXDESC 128 +#define NTXCOMPDESC NTXDESC +#define NRXCOMPDESC (NRXDESC * 2) /* ring1 + ring2 */ + +#define VMXNET3_DRIVER_VERSION 0x00010000 + +struct vmxnet3_txring { + struct mbuf *m[NTXDESC]; + bus_dmamap_t dmap[NTXDESC]; + struct vmxnet3_txdesc *txd; + u_int head; + u_int next; + uint8_t gen; +}; + +struct vmxnet3_rxring { + struct mbuf *m[NRXDESC]; + bus_dmamap_t dmap[NRXDESC]; + struct vmxnet3_rxdesc *rxd; + u_int fill; + uint8_t gen; + uint8_t rid; +}; + +struct vmxnet3_comp_ring { + union { + struct vmxnet3_txcompdesc *txcd; + struct vmxnet3_rxcompdesc *rxcd; + }; + u_int next; + uint8_t gen; +}; + +struct vmxnet3_txqueue { + struct vmxnet3_txring cmd_ring; + struct vmxnet3_comp_ring comp_ring; + struct vmxnet3_txq_shared *ts; +}; + +struct vmxnet3_rxqueue { + struct vmxnet3_rxring cmd_ring[2]; + struct vmxnet3_comp_ring comp_ring; + struct vmxnet3_rxq_shared *rs; +}; + +struct vmxnet3_softc { + device_t sc_dev; + struct ethercom sc_ethercom; + struct ifmedia sc_media; + + bus_space_tag_t sc_iot0; + bus_space_tag_t sc_iot1; + bus_space_handle_t sc_ioh0; + bus_space_handle_t sc_ioh1; + bus_dma_tag_t sc_dmat; + + struct vmxnet3_txqueue sc_txq[NTXQUEUE]; + struct vmxnet3_rxqueue sc_rxq[NRXQUEUE]; + struct vmxnet3_driver_shared *sc_ds; + uint8_t *sc_mcast; +}; + +#define VMXNET3_STAT + +#ifdef VMXNET3_STAT +struct { + u_int ntxdesc; + u_int nrxdesc; + u_int txhead; + u_int txdone; + u_int maxtxlen; + u_int rxdone; + u_int rxfill; + u_int intr; +} vmxstat = { + .ntxdesc = NTXDESC, + .nrxdesc = NRXDESC +}; +#endif + +#define JUMBO_LEN (MCLBYTES - ETHER_ALIGN) /* XXX */ +#define DMAADDR(map) ((map)->dm_segs[0].ds_addr) + +#define READ_BAR0(sc, reg) bus_space_read_4((sc)->sc_iot0, (sc)->sc_ioh0, reg) +#define READ_BAR1(sc, reg) bus_space_read_4((sc)->sc_iot1, (sc)->sc_ioh1, reg) +#define WRITE_BAR0(sc, reg, val) \ + bus_space_write_4((sc)->sc_iot0, (sc)->sc_ioh0, reg, val) +#define WRITE_BAR1(sc, reg, val) \ + bus_space_write_4((sc)->sc_iot1, (sc)->sc_ioh1, reg, val) +#define WRITE_CMD(sc, cmd) WRITE_BAR1(sc, VMXNET3_BAR1_CMD, cmd) +#define vtophys(va) 0 /* XXX ok? */ + +int vmxnet3_match(device_t, cfdata_t, void *); +void vmxnet3_attach(device_t, device_t, void *); +int vmxnet3_dma_init(struct vmxnet3_softc *); +int vmxnet3_alloc_txring(struct vmxnet3_softc *, int); +int vmxnet3_alloc_rxring(struct vmxnet3_softc *, int); +void vmxnet3_txinit(struct vmxnet3_softc *, struct vmxnet3_txqueue *); +void vmxnet3_rxinit(struct vmxnet3_softc *, struct vmxnet3_rxqueue *); +void vmxnet3_txstop(struct vmxnet3_softc *, struct vmxnet3_txqueue *); +void vmxnet3_rxstop(struct vmxnet3_softc *, struct vmxnet3_rxqueue *); +void vmxnet3_link_state(struct vmxnet3_softc *); +void vmxnet3_enable_all_intrs(struct vmxnet3_softc *); +void vmxnet3_disable_all_intrs(struct vmxnet3_softc *); +int vmxnet3_intr(void *); +void vmxnet3_evintr(struct vmxnet3_softc *); +void vmxnet3_txintr(struct vmxnet3_softc *, struct vmxnet3_txqueue *); +void vmxnet3_rxintr(struct vmxnet3_softc *, struct vmxnet3_rxqueue *); +void vmxnet3_iff(struct vmxnet3_softc *); +void vmxnet3_rx_csum(struct vmxnet3_rxcompdesc *, struct mbuf *); +int vmxnet3_getbuf(struct vmxnet3_softc *, struct vmxnet3_rxring *); +void vmxnet3_stop(struct ifnet *); +void vmxnet3_reset(struct ifnet *); +int vmxnet3_init(struct ifnet *); +int vmxnet3_ioctl(struct ifnet *, u_long, void *); +int vmxnet3_change_mtu(struct vmxnet3_softc *, int); +void vmxnet3_start(struct ifnet *); +int vmxnet3_load_mbuf(struct vmxnet3_softc *, struct mbuf *); +void vmxnet3_watchdog(struct ifnet *); +void vmxnet3_media_status(struct ifnet *, struct ifmediareq *); +int vmxnet3_media_change(struct ifnet *); +void *vmxnet3_dma_allocmem(struct vmxnet3_softc *, u_int, u_int, bus_addr_t *); + +CFATTACH_DECL3_NEW(vmx, sizeof(struct vmxnet3_softc), + vmxnet3_match, vmxnet3_attach, NULL, NULL, NULL, NULL, 0); + +int +vmxnet3_match(device_t parent, cfdata_t match, void *aux) +{ + struct pci_attach_args *pa = (struct pci_attach_args *)aux; + + if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_VMWARE && + PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_VMWARE_VMXNET3) + return 1; + + return 0; +} + +void +vmxnet3_attach(device_t parent, device_t self, void *aux) +{ + struct vmxnet3_softc *sc = device_private(self); + struct pci_attach_args *pa = aux; + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + pci_intr_handle_t ih; + const char *intrstr; + void *vih; + u_int memtype, ver, macl, mach; + pcireg_t preg; + u_char enaddr[ETHER_ADDR_LEN]; + char intrbuf[PCI_INTRSTR_LEN]; + + sc->sc_dev = self; + + pci_aprint_devinfo_fancy(pa, "Ethernet controller", "vmxnet3", 1); + + memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, 0x10); + if (pci_mapreg_map(pa, 0x10, memtype, 0, &sc->sc_iot0, &sc->sc_ioh0, + NULL, NULL)) { + aprint_error_dev(sc->sc_dev, "failed to map BAR0\n"); + return; + } + memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, 0x14); + if (pci_mapreg_map(pa, 0x14, memtype, 0, &sc->sc_iot1, &sc->sc_ioh1, + NULL, NULL)) { + aprint_error_dev(sc->sc_dev, "failed to map BAR1\n"); + return; + } + + ver = READ_BAR1(sc, VMXNET3_BAR1_VRRS); + if ((ver & 0x1) == 0) { + aprint_error_dev(sc->sc_dev, + "unsupported hardware version 0x%x\n", ver); + return; + } + WRITE_BAR1(sc, VMXNET3_BAR1_VRRS, 1); + + ver = READ_BAR1(sc, VMXNET3_BAR1_UVRS); + if ((ver & 0x1) == 0) { + aprint_error_dev(sc->sc_dev, + "incompatiable UPT version 0x%x\n", ver); + return; + } + WRITE_BAR1(sc, VMXNET3_BAR1_UVRS, 1); + + preg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); + preg |= PCI_COMMAND_MASTER_ENABLE; + pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, preg); + + sc->sc_dmat = pa->pa_dmat; + if (vmxnet3_dma_init(sc)) { + aprint_error_dev(sc->sc_dev, "failed to setup DMA\n"); + return; + } + + if (pci_intr_map(pa, &ih)) { + aprint_error_dev(sc->sc_dev, "failed to map interrupt\n"); + return; + } + intrstr = pci_intr_string(pa->pa_pc, ih, intrbuf, sizeof(intrbuf)); + vih = pci_intr_establish(pa->pa_pc, ih, IPL_NET, vmxnet3_intr, sc); + if (vih == NULL) { + aprint_error_dev(sc->sc_dev, + "unable to establish interrupt%s%s\n", + intrstr ? " at " : "", intrstr ? intrstr : ""); + return; + } + aprint_normal_dev(sc->sc_dev, "interrupting at %s\n", intrstr); + + WRITE_CMD(sc, VMXNET3_CMD_GET_MACL); + macl = READ_BAR1(sc, VMXNET3_BAR1_CMD); + enaddr[0] = macl; + enaddr[1] = macl >> 8; + enaddr[2] = macl >> 16; + enaddr[3] = macl >> 24; + WRITE_CMD(sc, VMXNET3_CMD_GET_MACH); + mach = READ_BAR1(sc, VMXNET3_BAR1_CMD); + enaddr[4] = mach; + enaddr[5] = mach >> 8; + + WRITE_BAR1(sc, VMXNET3_BAR1_MACL, macl); + WRITE_BAR1(sc, VMXNET3_BAR1_MACH, mach); + aprint_normal_dev(sc->sc_dev, "Ethernet address %s\n", + ether_sprintf(enaddr)); + + strlcpy(ifp->if_xname, device_xname(sc->sc_dev), IFNAMSIZ); + ifp->if_softc = sc; + ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX; + ifp->if_ioctl = vmxnet3_ioctl; + ifp->if_start = vmxnet3_start; + ifp->if_watchdog = vmxnet3_watchdog; + ifp->if_init = vmxnet3_init; + sc->sc_ethercom.ec_capabilities |= ETHERCAP_VLAN_MTU; + if (sc->sc_ds->upt_features & UPT1_F_CSUM) + sc->sc_ethercom.ec_if.if_capabilities |= + IFCAP_CSUM_IPv4_Rx | + IFCAP_CSUM_TCPv4_Tx | IFCAP_CSUM_TCPv4_Rx | + IFCAP_CSUM_UDPv4_Tx | IFCAP_CSUM_UDPv4_Rx; + if (sc->sc_ds->upt_features & UPT1_F_VLAN) + sc->sc_ethercom.ec_capabilities |= ETHERCAP_VLAN_HWTAGGING; + + IFQ_SET_MAXLEN(&ifp->if_snd, NTXDESC); + IFQ_SET_READY(&ifp->if_snd); + + ifmedia_init(&sc->sc_media, IFM_IMASK, vmxnet3_media_change, + vmxnet3_media_status); + ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_AUTO, 0, NULL); + ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_10G_T|IFM_FDX, 0, NULL); + ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_10G_T, 0, NULL); + ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_1000_T|IFM_FDX, 0, NULL); + ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_1000_T, 0, NULL); + ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_AUTO); + + if_attach(ifp); + ether_ifattach(ifp, enaddr); + vmxnet3_link_state(sc); +} + +int +vmxnet3_dma_init(struct vmxnet3_softc *sc) +{ + struct vmxnet3_driver_shared *ds; + struct vmxnet3_txq_shared *ts; + struct vmxnet3_rxq_shared *rs; + bus_addr_t ds_pa, qs_pa, mcast_pa; + int i, queue, qs_len; + u_int major, minor, release_code, rev; + + qs_len = NTXQUEUE * sizeof *ts + NRXQUEUE * sizeof *rs; + ts = vmxnet3_dma_allocmem(sc, qs_len, VMXNET3_DMADESC_ALIGN, &qs_pa); + if (ts == NULL) + return -1; + for (queue = 0; queue < NTXQUEUE; queue++) + sc->sc_txq[queue].ts = ts++; + rs = (void *)ts; + for (queue = 0; queue < NRXQUEUE; queue++) + sc->sc_rxq[queue].rs = rs++; + + for (queue = 0; queue < NTXQUEUE; queue++) + if (vmxnet3_alloc_txring(sc, queue)) + return -1; + for (queue = 0; queue < NRXQUEUE; queue++) + if (vmxnet3_alloc_rxring(sc, queue)) + return -1; + + sc->sc_mcast = vmxnet3_dma_allocmem(sc, 682 * ETHER_ADDR_LEN, 32, &mcast_pa); + if (sc->sc_mcast == NULL) + return -1; + + ds = vmxnet3_dma_allocmem(sc, sizeof *sc->sc_ds, 8, &ds_pa); + if (ds == NULL) + return -1; + sc->sc_ds = ds; + ds->magic = VMXNET3_REV1_MAGIC; + ds->version = VMXNET3_DRIVER_VERSION; + + /* + * XXX FreeBSD version uses following values: + * (Does the device behavior depend on them?) + * + * major = __FreeBSD_version / 100000; + * minor = (__FreeBSD_version / 1000) % 100; + * release_code = (__FreeBSD_version / 100) % 10; + * rev = __FreeBSD_version % 100; + */ + major = 0; + minor = 0; + release_code = 0; + rev = 0; +#ifdef __LP64__ + ds->guest = release_code << 30 | rev << 22 | major << 14 | minor << 6 + | VMXNET3_GOS_FREEBSD | VMXNET3_GOS_64BIT; +#else + ds->guest = release_code << 30 | rev << 22 | major << 14 | minor << 6 + | VMXNET3_GOS_FREEBSD | VMXNET3_GOS_32BIT; +#endif + ds->vmxnet3_revision = 1; + ds->upt_version = 1; + ds->upt_features = UPT1_F_CSUM | UPT1_F_VLAN; + ds->driver_data = vtophys(sc); + ds->driver_data_len = sizeof(struct vmxnet3_softc); + ds->queue_shared = qs_pa; + ds->queue_shared_len = qs_len; + ds->mtu = ETHERMTU; + ds->ntxqueue = NTXQUEUE; + ds->nrxqueue = NRXQUEUE; + ds->mcast_table = mcast_pa; + ds->automask = 1; + ds->nintr = VMXNET3_NINTR; + ds->evintr = 0; + ds->ictrl = VMXNET3_ICTRL_DISABLE_ALL; + for (i = 0; i < VMXNET3_NINTR; i++) + ds->modlevel[i] = UPT1_IMOD_ADAPTIVE; + WRITE_BAR1(sc, VMXNET3_BAR1_DSL, ds_pa); + WRITE_BAR1(sc, VMXNET3_BAR1_DSH, (uint64_t)ds_pa >> 32); + return 0; +} + +int +vmxnet3_alloc_txring(struct vmxnet3_softc *sc, int queue) +{ + struct vmxnet3_txqueue *tq = &sc->sc_txq[queue]; + struct vmxnet3_txq_shared *ts; + struct vmxnet3_txring *ring = &tq->cmd_ring; + struct vmxnet3_comp_ring *comp_ring = &tq->comp_ring; + bus_addr_t pa, comp_pa; + int idx; + + ring->txd = vmxnet3_dma_allocmem(sc, NTXDESC * sizeof ring->txd[0], 512, &pa); + if (ring->txd == NULL) + return -1; + comp_ring->txcd = vmxnet3_dma_allocmem(sc, + NTXCOMPDESC * sizeof comp_ring->txcd[0], 512, &comp_pa); + if (comp_ring->txcd == NULL) + return -1; + + for (idx = 0; idx < NTXDESC; idx++) { + if (bus_dmamap_create(sc->sc_dmat, JUMBO_LEN, NTXSEGS, + JUMBO_LEN, 0, BUS_DMA_NOWAIT, &ring->dmap[idx])) + return -1; + } + + ts = tq->ts; + memset(ts, 0, sizeof *ts); + ts->npending = 0; + ts->intr_threshold = 1; + ts->cmd_ring = pa; + ts->cmd_ring_len = NTXDESC; + ts->comp_ring = comp_pa; + ts->comp_ring_len = NTXCOMPDESC; + ts->driver_data = vtophys(tq); + ts->driver_data_len = sizeof *tq; + ts->intr_idx = 0; + ts->stopped = 1; + ts->error = 0; + return 0; +} + +int +vmxnet3_alloc_rxring(struct vmxnet3_softc *sc, int queue) +{ + struct vmxnet3_rxqueue *rq = &sc->sc_rxq[queue]; + struct vmxnet3_rxq_shared *rs; + struct vmxnet3_rxring *ring; + struct vmxnet3_comp_ring *comp_ring; + bus_addr_t pa[2], comp_pa; + int i, idx; + + for (i = 0; i < 2; i++) { + ring = &rq->cmd_ring[i]; + ring->rxd = vmxnet3_dma_allocmem(sc, NRXDESC * sizeof ring->rxd[0], + 512, &pa[i]); + if (ring->rxd == NULL) + return -1; + } + comp_ring = &rq->comp_ring; + comp_ring->rxcd = vmxnet3_dma_allocmem(sc, + NRXCOMPDESC * sizeof comp_ring->rxcd[0], 512, &comp_pa); + if (comp_ring->rxcd == NULL) + return -1; + + for (i = 0; i < 2; i++) { + ring = &rq->cmd_ring[i]; + ring->rid = i; + for (idx = 0; idx < NRXDESC; idx++) { + if (bus_dmamap_create(sc->sc_dmat, JUMBO_LEN, 1, + JUMBO_LEN, 0, BUS_DMA_NOWAIT, &ring->dmap[idx])) + return -1; + } + } + + rs = rq->rs; + memset(rs, 0, sizeof *rs); + rs->cmd_ring[0] = pa[0]; + rs->cmd_ring[1] = pa[1]; + rs->cmd_ring_len[0] = NRXDESC; + rs->cmd_ring_len[1] = NRXDESC; + rs->comp_ring = comp_pa; + rs->comp_ring_len = NRXCOMPDESC; + rs->driver_data = vtophys(rq); + rs->driver_data_len = sizeof *rq; + rs->intr_idx = 0; + rs->stopped = 1; + rs->error = 0; + return 0; +} + +void +vmxnet3_txinit(struct vmxnet3_softc *sc, struct vmxnet3_txqueue *tq) +{ + struct vmxnet3_txring *ring = &tq->cmd_ring; + struct vmxnet3_comp_ring *comp_ring = &tq->comp_ring; + + ring->head = ring->next = 0; + ring->gen = 1; + comp_ring->next = 0; + comp_ring->gen = 1; + memset(ring->txd, 0, NTXDESC * sizeof ring->txd[0]); + memset(comp_ring->txcd, 0, NTXCOMPDESC * sizeof comp_ring->txcd[0]); +} + +void +vmxnet3_rxinit(struct vmxnet3_softc *sc, struct vmxnet3_rxqueue *rq) +{ + struct vmxnet3_rxring *ring; + struct vmxnet3_comp_ring *comp_ring; + int i, idx; + + for (i = 0; i < 2; i++) { + ring = &rq->cmd_ring[i]; + ring->fill = 0; + ring->gen = 1; + memset(ring->rxd, 0, NRXDESC * sizeof ring->rxd[0]); + for (idx = 0; idx < NRXDESC; idx++) { + if (vmxnet3_getbuf(sc, ring)) + break; + } + } + comp_ring = &rq->comp_ring; + comp_ring->next = 0; + comp_ring->gen = 1; + memset(comp_ring->rxcd, 0, NRXCOMPDESC * sizeof comp_ring->rxcd[0]); +} + +void +vmxnet3_txstop(struct vmxnet3_softc *sc, struct vmxnet3_txqueue *tq) +{ + struct vmxnet3_txring *ring = &tq->cmd_ring; + int idx; + + for (idx = 0; idx < NTXDESC; idx++) { + if (ring->m[idx]) { + bus_dmamap_unload(sc->sc_dmat, ring->dmap[idx]); + m_freem(ring->m[idx]); + ring->m[idx] = NULL; + } + } +} + +void +vmxnet3_rxstop(struct vmxnet3_softc *sc, struct vmxnet3_rxqueue *rq) +{ + struct vmxnet3_rxring *ring; + int i, idx; + + for (i = 0; i < 2; i++) { + ring = &rq->cmd_ring[i]; + for (idx = 0; idx < NRXDESC; idx++) { + if (ring->m[idx]) { + m_freem(ring->m[idx]); + ring->m[idx] = NULL; + } + } + } +} + +void +vmxnet3_link_state(struct vmxnet3_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + u_int x, link, speed; + + WRITE_CMD(sc, VMXNET3_CMD_GET_LINK); + x = READ_BAR1(sc, VMXNET3_BAR1_CMD); + speed = x >> 16; + if (x & 1) { + ifp->if_baudrate = IF_Mbps(speed); + link = LINK_STATE_UP; + } else + link = LINK_STATE_DOWN; + + if_link_state_change(ifp, link); +} + +static inline void +vmxnet3_enable_intr(struct vmxnet3_softc *sc, int irq) +{ + WRITE_BAR0(sc, VMXNET3_BAR0_IMASK(irq), 0); +} + +static inline void +vmxnet3_disable_intr(struct vmxnet3_softc *sc, int irq) +{ + WRITE_BAR0(sc, VMXNET3_BAR0_IMASK(irq), 1); +} + +void +vmxnet3_enable_all_intrs(struct vmxnet3_softc *sc) +{ + int i; + + sc->sc_ds->ictrl &= ~VMXNET3_ICTRL_DISABLE_ALL; + for (i = 0; i < VMXNET3_NINTR; i++) + vmxnet3_enable_intr(sc, i); +} + +void +vmxnet3_disable_all_intrs(struct vmxnet3_softc *sc) +{ + int i; + + sc->sc_ds->ictrl |= VMXNET3_ICTRL_DISABLE_ALL; + for (i = 0; i < VMXNET3_NINTR; i++) + vmxnet3_disable_intr(sc, i); +} + +int +vmxnet3_intr(void *arg) +{ + struct vmxnet3_softc *sc = arg; + + if (READ_BAR1(sc, VMXNET3_BAR1_INTR) == 0) + return 0; + if (sc->sc_ds->event) + vmxnet3_evintr(sc); + vmxnet3_rxintr(sc, &sc->sc_rxq[0]); + vmxnet3_txintr(sc, &sc->sc_txq[0]); +#ifdef VMXNET3_STAT + vmxstat.intr++; +#endif + vmxnet3_enable_intr(sc, 0); + return 1; +} + +void +vmxnet3_evintr(struct vmxnet3_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + u_int event = sc->sc_ds->event; + struct vmxnet3_txq_shared *ts; + struct vmxnet3_rxq_shared *rs; + + /* Clear events. */ + WRITE_BAR1(sc, VMXNET3_BAR1_EVENT, event); + + /* Link state change? */ + if (event & VMXNET3_EVENT_LINK) + vmxnet3_link_state(sc); + + /* Queue error? */ + if (event & (VMXNET3_EVENT_TQERROR | VMXNET3_EVENT_RQERROR)) { + WRITE_CMD(sc, VMXNET3_CMD_GET_STATUS); + + ts = sc->sc_txq[0].ts; + if (ts->stopped) + printf("%s: TX error 0x%x\n", ifp->if_xname, ts->error); + rs = sc->sc_rxq[0].rs; + if (rs->stopped) + printf("%s: RX error 0x%x\n", ifp->if_xname, rs->error); + vmxnet3_reset(ifp); + } + + if (event & VMXNET3_EVENT_DIC) + printf("%s: device implementation change event\n", + ifp->if_xname); + if (event & VMXNET3_EVENT_DEBUG) + printf("%s: debug event\n", ifp->if_xname); +} + +void +vmxnet3_txintr(struct vmxnet3_softc *sc, struct vmxnet3_txqueue *tq) +{ + struct vmxnet3_txring *ring = &tq->cmd_ring; + struct vmxnet3_comp_ring *comp_ring = &tq->comp_ring; + struct vmxnet3_txcompdesc *txcd; + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + u_int sop; + + for (;;) { + txcd = &comp_ring->txcd[comp_ring->next]; + + if (le32toh((txcd->txc_word3 >> VMXNET3_TXC_GEN_S) & + VMXNET3_TXC_GEN_M) != comp_ring->gen) + break; + + comp_ring->next++; + if (comp_ring->next == NTXCOMPDESC) { + comp_ring->next = 0; + comp_ring->gen ^= 1; + } + + sop = ring->next; + if (ring->m[sop] == NULL) + panic("vmxnet3_txintr"); + m_freem(ring->m[sop]); + ring->m[sop] = NULL; + bus_dmamap_unload(sc->sc_dmat, ring->dmap[sop]); + ring->next = (le32toh((txcd->txc_word0 >> + VMXNET3_TXC_EOPIDX_S) & VMXNET3_TXC_EOPIDX_M) + 1) + % NTXDESC; + + ifp->if_flags &= ~IFF_OACTIVE; + } + if (ring->head == ring->next) + ifp->if_timer = 0; + vmxnet3_start(ifp); +} + +void +vmxnet3_rxintr(struct vmxnet3_softc *sc, struct vmxnet3_rxqueue *rq) +{ + struct vmxnet3_comp_ring *comp_ring = &rq->comp_ring; + struct vmxnet3_rxring *ring; + struct vmxnet3_rxdesc *rxd; + struct vmxnet3_rxcompdesc *rxcd; + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + struct mbuf *m; + int idx, len; + + for (;;) { + rxcd = &comp_ring->rxcd[comp_ring->next]; + if (le32toh((rxcd->rxc_word3 >> VMXNET3_RXC_GEN_S) & + VMXNET3_RXC_GEN_M) != comp_ring->gen) + break; + + comp_ring->next++; + if (comp_ring->next == NRXCOMPDESC) { + comp_ring->next = 0; + comp_ring->gen ^= 1; + } + + idx = le32toh((rxcd->rxc_word0 >> VMXNET3_RXC_IDX_S) & + VMXNET3_RXC_IDX_M); + if (le32toh((rxcd->rxc_word0 >> VMXNET3_RXC_QID_S) & + VMXNET3_RXC_QID_M) < NRXQUEUE) + ring = &rq->cmd_ring[0]; + else + ring = &rq->cmd_ring[1]; + rxd = &ring->rxd[idx]; + len = le32toh((rxcd->rxc_word2 >> VMXNET3_RXC_LEN_S) & + VMXNET3_RXC_LEN_M); + m = ring->m[idx]; + ring->m[idx] = NULL; + bus_dmamap_unload(sc->sc_dmat, ring->dmap[idx]); + + if (m == NULL) + panic("NULL mbuf"); + + if (le32toh((rxd->rx_word2 >> VMXNET3_RX_BTYPE_S) & + VMXNET3_RX_BTYPE_M) != VMXNET3_BTYPE_HEAD) { + m_freem(m); + goto skip_buffer; + } + if (le32toh(rxcd->rxc_word2 & VMXNET3_RXC_ERROR)) { + ifp->if_ierrors++; + m_freem(m); + goto skip_buffer; + } + if (len < VMXNET3_MIN_MTU) { + printf("%s: short packet (%d)\n", ifp->if_xname, len); + m_freem(m); + goto skip_buffer; + } + + ifp->if_ipackets++; + ifp->if_ibytes += len; + + vmxnet3_rx_csum(rxcd, m); + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = len; + if (le32toh(rxcd->rxc_word2 & VMXNET3_RXC_VLAN)) { + VLAN_INPUT_TAG(ifp, m, + le32toh((rxcd->rxc_word2 >> + VMXNET3_RXC_VLANTAG_S) & VMXNET3_RXC_VLANTAG_M), + m_freem(m); goto skip_buffer); + } + + bpf_mtap(ifp, m); + + (*ifp->if_input)(ifp, m); + +skip_buffer: +#ifdef VMXNET3_STAT + vmxstat.rxdone = idx; +#endif + if (rq->rs->update_rxhead) { + u_int qid = le32toh((rxcd->rxc_word0 >> + VMXNET3_RXC_QID_S) & VMXNET3_RXC_QID_M); + + idx = (idx + 1) % NRXDESC; + if (qid < NRXQUEUE) { + WRITE_BAR0(sc, VMXNET3_BAR0_RXH1(qid), idx); + } else { + qid -= NRXQUEUE; + WRITE_BAR0(sc, VMXNET3_BAR0_RXH2(qid), idx); + } + } + } + + /* XXX Should we (try to) allocate buffers for ring 2 too? */ + ring = &rq->cmd_ring[0]; + for (;;) { + idx = ring->fill; + if (ring->m[idx]) + return; + if (vmxnet3_getbuf(sc, ring)) + return; + } +} + +void +vmxnet3_iff(struct vmxnet3_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + struct ethercom *ec = &sc->sc_ethercom; + struct vmxnet3_driver_shared *ds = sc->sc_ds; + struct ether_multi *enm; + struct ether_multistep step; + u_int mode; + uint8_t *p; + + ds->mcast_tablelen = 0; + CLR(ifp->if_flags, IFF_ALLMULTI); + + /* + * Always accept broadcast frames. + * Always accept frames destined to our station address. + */ + mode = VMXNET3_RXMODE_BCAST | VMXNET3_RXMODE_UCAST; + + if (ISSET(ifp->if_flags, IFF_PROMISC) || ec->ec_multicnt > 682) + goto allmulti; + + p = sc->sc_mcast; + ETHER_FIRST_MULTI(step, ec, enm); + while (enm != NULL) { + if (memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) { + /* + * We must listen to a range of multicast addresses. + * For now, just accept all multicasts, rather than + * trying to set only those filter bits needed to match + * the range. (At this time, the only use of address + * ranges is for IP multicast routing, for which the + * range is big enough to require all bits set.) + */ + goto allmulti; + } + memcpy(p, enm->enm_addrlo, ETHER_ADDR_LEN); + + p += ETHER_ADDR_LEN; + + ETHER_NEXT_MULTI(step, enm); + } + + if (ec->ec_multicnt > 0) { + SET(mode, VMXNET3_RXMODE_MCAST); + ds->mcast_tablelen = p - sc->sc_mcast; + } + + goto setit; + +allmulti: + SET(ifp->if_flags, IFF_ALLMULTI); + SET(mode, (VMXNET3_RXMODE_ALLMULTI | VMXNET3_RXMODE_MCAST)); + if (ifp->if_flags & IFF_PROMISC) + SET(mode, VMXNET3_RXMODE_PROMISC); + +setit: + WRITE_CMD(sc, VMXNET3_CMD_SET_FILTER); + ds->rxmode = mode; + WRITE_CMD(sc, VMXNET3_CMD_SET_RXMODE); +} + + +void +vmxnet3_rx_csum(struct vmxnet3_rxcompdesc *rxcd, struct mbuf *m) +{ + if (le32toh(rxcd->rxc_word0 & VMXNET3_RXC_NOCSUM)) + return; + + if (rxcd->rxc_word3 & VMXNET3_RXC_IPV4) { + m->m_pkthdr.csum_flags |= M_CSUM_IPv4; + if ((rxcd->rxc_word3 & VMXNET3_RXC_IPSUM_OK) == 0) + m->m_pkthdr.csum_flags |= M_CSUM_IPv4_BAD; + } + + if (rxcd->rxc_word3 & VMXNET3_RXC_FRAGMENT) + return; + + if (rxcd->rxc_word3 & VMXNET3_RXC_TCP) { + m->m_pkthdr.csum_flags |= M_CSUM_TCPv4; + if ((rxcd->rxc_word3 & VMXNET3_RXC_CSUM_OK) == 0) + m->m_pkthdr.csum_flags |= M_CSUM_TCP_UDP_BAD; + } + + if (rxcd->rxc_word3 & VMXNET3_RXC_UDP) { + m->m_pkthdr.csum_flags |= M_CSUM_UDPv4; + if ((rxcd->rxc_word3 & VMXNET3_RXC_CSUM_OK) == 0) + m->m_pkthdr.csum_flags |= M_CSUM_TCP_UDP_BAD; + } +} + +int +vmxnet3_getbuf(struct vmxnet3_softc *sc, struct vmxnet3_rxring *ring) +{ + int idx = ring->fill; + struct vmxnet3_rxdesc *rxd = &ring->rxd[idx]; + struct mbuf *m; + int btype; + + if (ring->m[idx]) + panic("vmxnet3_getbuf: buffer has mbuf"); + +#if 1 + /* XXX Don't allocate buffers for ring 2 for now. */ + if (ring->rid != 0) + return -1; + btype = VMXNET3_BTYPE_HEAD; +#else + if (ring->rid == 0) + btype = VMXNET3_BTYPE_HEAD; + else + btype = VMXNET3_BTYPE_BODY; +#endif + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return -1; + + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + return -1; + } + + m->m_pkthdr.len = m->m_len = JUMBO_LEN; + m_adj(m, ETHER_ALIGN); + ring->m[idx] = m; + + if (bus_dmamap_load_mbuf(sc->sc_dmat, ring->dmap[idx], m, + BUS_DMA_NOWAIT)) + panic("load mbuf"); + rxd->rx_addr = htole64(DMAADDR(ring->dmap[idx])); + rxd->rx_word2 = htole32(((m->m_pkthdr.len & VMXNET3_RX_LEN_M) << + VMXNET3_RX_LEN_S) | ((btype & VMXNET3_RX_BTYPE_M) << + VMXNET3_RX_BTYPE_S) | ((ring->gen & VMXNET3_RX_GEN_M) << + VMXNET3_RX_GEN_S)); + idx++; + if (idx == NRXDESC) { + idx = 0; + ring->gen ^= 1; + } + ring->fill = idx; +#ifdef VMXNET3_STAT + vmxstat.rxfill = ring->fill; +#endif + return 0; +} + +void +vmxnet3_stop(struct ifnet *ifp) +{ + struct vmxnet3_softc *sc = ifp->if_softc; + int queue; + + if ((ifp->if_flags & IFF_RUNNING) == 0) + return; + + vmxnet3_disable_all_intrs(sc); + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + ifp->if_timer = 0; + + WRITE_CMD(sc, VMXNET3_CMD_DISABLE); + + for (queue = 0; queue < NTXQUEUE; queue++) + vmxnet3_txstop(sc, &sc->sc_txq[queue]); + for (queue = 0; queue < NRXQUEUE; queue++) + vmxnet3_rxstop(sc, &sc->sc_rxq[queue]); +} + +void +vmxnet3_reset(struct ifnet *ifp) +{ + struct vmxnet3_softc *sc = ifp->if_softc; + + vmxnet3_stop(ifp); + WRITE_CMD(sc, VMXNET3_CMD_RESET); + vmxnet3_init(ifp); +} + +int +vmxnet3_init(struct ifnet *ifp) +{ + struct vmxnet3_softc *sc = ifp->if_softc; + int queue; + + if (ifp->if_flags & IFF_RUNNING) + return 0; + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + for (queue = 0; queue < NTXQUEUE; queue++) + vmxnet3_txinit(sc, &sc->sc_txq[queue]); + for (queue = 0; queue < NRXQUEUE; queue++) + vmxnet3_rxinit(sc, &sc->sc_rxq[queue]); + + WRITE_CMD(sc, VMXNET3_CMD_ENABLE); + if (READ_BAR1(sc, VMXNET3_BAR1_CMD)) { + printf("%s: failed to initialize\n", ifp->if_xname); + vmxnet3_stop(ifp); + return EIO; + } + + for (queue = 0; queue < NRXQUEUE; queue++) { + WRITE_BAR0(sc, VMXNET3_BAR0_RXH1(queue), 0); + WRITE_BAR0(sc, VMXNET3_BAR0_RXH2(queue), 0); + } + + vmxnet3_iff(sc); + vmxnet3_enable_all_intrs(sc); + vmxnet3_link_state(sc); + return 0; +} + +int +vmxnet3_change_mtu(struct vmxnet3_softc *sc, int mtu) +{ + struct vmxnet3_driver_shared *ds = sc->sc_ds; + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + int error; + + if (mtu < VMXNET3_MIN_MTU || mtu > VMXNET3_MAX_MTU) + return EINVAL; + vmxnet3_stop(ifp); + ifp->if_mtu = ds->mtu = mtu; + error = vmxnet3_init(ifp); + return error; +} + +int +vmxnet3_ioctl(struct ifnet *ifp, u_long cmd, void *data) +{ + struct vmxnet3_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + int error = 0, s; + + s = splnet(); + + switch (cmd) { + case SIOCSIFMTU: + error = vmxnet3_change_mtu(sc, ifr->ifr_mtu); + break; + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); + break; + default: + error = ether_ioctl(ifp, cmd, data); + } + + if (error == ENETRESET) { + if (ifp->if_flags & IFF_RUNNING) + vmxnet3_iff(sc); + error = 0; + } + + splx(s); + return error; +} + +void +vmxnet3_start(struct ifnet *ifp) +{ + struct vmxnet3_softc *sc = ifp->if_softc; + struct vmxnet3_txqueue *tq = &sc->sc_txq[0]; + struct vmxnet3_txring *ring = &tq->cmd_ring; + struct mbuf *m; + int n = 0; + + if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) + return; + + for (;;) { + IFQ_POLL(&ifp->if_snd, m); + if (m == NULL) + break; + if ((ring->next - ring->head - 1) % NTXDESC < NTXSEGS) { + ifp->if_flags |= IFF_OACTIVE; + break; + } + + IFQ_DEQUEUE(&ifp->if_snd, m); + if (vmxnet3_load_mbuf(sc, m) != 0) { + ifp->if_oerrors++; + continue; + } + bpf_mtap(ifp, m); + + ifp->if_timer = 5; + ifp->if_opackets++; + n++; + } + + if (n > 0) + WRITE_BAR0(sc, VMXNET3_BAR0_TXH(0), ring->head); +#ifdef VMXNET3_STAT + vmxstat.txhead = ring->head; + vmxstat.txdone = ring->next; + vmxstat.maxtxlen = + max(vmxstat.maxtxlen, (ring->head - ring->next) % NTXDESC); +#endif +} + +int +vmxnet3_load_mbuf(struct vmxnet3_softc *sc, struct mbuf *m) +{ + struct vmxnet3_txqueue *tq = &sc->sc_txq[0]; + struct vmxnet3_txring *ring = &tq->cmd_ring; + struct vmxnet3_txdesc *txd = NULL, *sop; + struct mbuf *mp; + struct m_tag *mtag; + struct ip *ip; + bus_dmamap_t map = ring->dmap[ring->head]; + u_int hlen = ETHER_HDR_LEN, csum_off = 0; + int offp, gen, i; + +#if 0 + if (m->m_pkthdr.csum_flags & M_CSUM_IPv4) { + printf("%s: IP checksum offloading is not supported\n", + sc->sc_dev.dv_xname); + return -1; + } +#endif + if (m->m_pkthdr.csum_flags & (M_CSUM_UDPv4|M_CSUM_TCPv4)) { + if (m->m_pkthdr.csum_flags & M_CSUM_TCPv4) + csum_off = offsetof(struct tcphdr, th_sum); + else + csum_off = offsetof(struct udphdr, uh_sum); + + mp = m_pulldown(m, hlen, sizeof(*ip), &offp); + if (mp == NULL) + return (-1); + + ip = (struct ip *)(mp->m_data + offp); + hlen += ip->ip_hl << 2; + + mp = m_pulldown(m, 0, hlen + csum_off + 2, &offp); + if (mp == NULL) + return (-1); + } + + switch (bus_dmamap_load_mbuf(sc->sc_dmat, map, m, BUS_DMA_NOWAIT)) { + case 0: + break; + case EFBIG: + if (m_defrag(m, M_DONTWAIT) == 0 && + bus_dmamap_load_mbuf(sc->sc_dmat, map, m, + BUS_DMA_NOWAIT) == 0) + break; + + /* FALLTHROUGH */ + default: + m_freem(m); + return -1; + } + + ring->m[ring->head] = m; + sop = &ring->txd[ring->head]; + gen = ring->gen ^ 1; /* owned by cpu (yet) */ + for (i = 0; i < map->dm_nsegs; i++) { + txd = &ring->txd[ring->head]; + txd->tx_addr = htole64(map->dm_segs[i].ds_addr); + txd->tx_word2 = htole32(((map->dm_segs[i].ds_len & + VMXNET3_TX_LEN_M) << VMXNET3_TX_LEN_S) | + ((gen & VMXNET3_TX_GEN_M) << VMXNET3_TX_GEN_S)); + txd->tx_word3 = 0; + ring->head++; + if (ring->head == NTXDESC) { + ring->head = 0; + ring->gen ^= 1; + } + gen = ring->gen; + } + if (txd != NULL) + txd->tx_word3 |= htole32(VMXNET3_TX_EOP | VMXNET3_TX_COMPREQ); + + if ((mtag = VLAN_OUTPUT_TAG(&sc->sc_ethercom, m)) != NULL) { + sop->tx_word3 |= htole32(VMXNET3_TX_VTAG_MODE); + sop->tx_word3 |= htole32((VLAN_TAG_VALUE(mtag) & + VMXNET3_TX_VLANTAG_M) << VMXNET3_TX_VLANTAG_S); + } + if (m->m_pkthdr.csum_flags & (M_CSUM_UDPv4|M_CSUM_TCPv4)) { + sop->tx_word2 |= htole32(((hlen + csum_off) & + VMXNET3_TX_OP_M) << VMXNET3_TX_OP_S); + sop->tx_word3 |= htole32(((hlen & VMXNET3_TX_HLEN_M) << + VMXNET3_TX_HLEN_S) | (VMXNET3_OM_CSUM << VMXNET3_TX_OM_S)); + } + + /* Change the ownership by flipping the "generation" bit */ + sop->tx_word2 ^= htole32(VMXNET3_TX_GEN_M << VMXNET3_TX_GEN_S); + + return (0); +} + +void +vmxnet3_watchdog(struct ifnet *ifp) +{ + int s; + + printf("%s: device timeout\n", ifp->if_xname); + s = splnet(); + vmxnet3_stop(ifp); + vmxnet3_init(ifp); + splx(s); +} + +void +vmxnet3_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct vmxnet3_softc *sc = ifp->if_softc; + + vmxnet3_link_state(sc); + + ifmr->ifm_status = IFM_AVALID; + ifmr->ifm_active = IFM_ETHER; + + if (ifp->if_link_state != LINK_STATE_UP) + return; + + ifmr->ifm_status |= IFM_ACTIVE; + + if (ifp->if_baudrate >= IF_Gbps(10ULL)) + ifmr->ifm_active |= IFM_10G_T; +} + +int +vmxnet3_media_change(struct ifnet *ifp) +{ + return 0; +} + +void * +vmxnet3_dma_allocmem(struct vmxnet3_softc *sc, u_int size, u_int align, bus_addr_t *pa) +{ + bus_dma_tag_t t = sc->sc_dmat; + bus_dma_segment_t segs[1]; + bus_dmamap_t map; + void *va; + int n; + + if (bus_dmamem_alloc(t, size, align, 0, segs, 1, &n, BUS_DMA_NOWAIT)) + return NULL; + if (bus_dmamem_map(t, segs, 1, size, &va, BUS_DMA_NOWAIT)) + return NULL; + if (bus_dmamap_create(t, size, 1, size, 0, BUS_DMA_NOWAIT, &map)) + return NULL; + if (bus_dmamap_load(t, map, va, size, NULL, BUS_DMA_NOWAIT)) + return NULL; + memset(va, 0, size); + *pa = DMAADDR(map); + bus_dmamap_unload(t, map); + bus_dmamap_destroy(t, map); + return va; +} Index: src/sys/arch/x86/pci/if_vmxreg.h diff -u /dev/null src/sys/arch/x86/pci/if_vmxreg.h:1.1 --- /dev/null Tue Jun 10 01:42:39 2014 +++ src/sys/arch/x86/pci/if_vmxreg.h Tue Jun 10 01:42:39 2014 @@ -0,0 +1,328 @@ +/* $NetBSD: if_vmxreg.h,v 1.1 2014/06/10 01:42:39 hikaru Exp $ */ +/* $OpenBSD: if_vmxreg.h,v 1.3 2013/08/28 10:19:19 reyk Exp $ */ + +/* + * Copyright (c) 2013 Tsubai Masanari + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +struct UPT1_TxStats { + uint64_t TSO_packets; + uint64_t TSO_bytes; + uint64_t ucast_packets; + uint64_t ucast_bytes; + uint64_t mcast_packets; + uint64_t mcast_bytes; + uint64_t bcast_packets; + uint64_t bcast_bytes; + uint64_t error; + uint64_t discard; +} __packed; + +struct UPT1_RxStats { + uint64_t LRO_packets; + uint64_t LRO_bytes; + uint64_t ucast_packets; + uint64_t ucast_bytes; + uint64_t mcast_packets; + uint64_t mcast_bytes; + uint64_t bcast_packets; + uint64_t bcast_bytes; + uint64_t nobuffer; + uint64_t error; +} __packed; + +#define ETHER_ALIGN 2 + +/* interrupt moderation levels */ +#define UPT1_IMOD_NONE 0 /* no moderation */ +#define UPT1_IMOD_HIGHEST 7 /* least interrupts */ +#define UPT1_IMOD_ADAPTIVE 8 /* adaptive interrupt moderation */ + +/* hardware features */ +#define UPT1_F_CSUM 0x0001 /* Rx checksum verification */ +#define UPT1_F_RSS 0x0002 /* receive side scaling */ +#define UPT1_F_VLAN 0x0004 /* VLAN tag stripping */ +#define UPT1_F_LRO 0x0008 /* large receive offloading */ + +#define VMXNET3_BAR0_IMASK(irq) (0x000 + (irq) * 8) /* interrupt mask */ +#define VMXNET3_BAR0_TXH(q) (0x600 + (q) * 8) /* Tx head */ +#define VMXNET3_BAR0_RXH1(q) (0x800 + (q) * 8) /* ring1 Rx head */ +#define VMXNET3_BAR0_RXH2(q) (0xa00 + (q) * 8) /* ring2 Rx head */ +#define VMXNET3_BAR1_VRRS 0x000 /* VMXNET3 revision report selection */ +#define VMXNET3_BAR1_UVRS 0x008 /* UPT version report selection */ +#define VMXNET3_BAR1_DSL 0x010 /* driver shared address low */ +#define VMXNET3_BAR1_DSH 0x018 /* driver shared address high */ +#define VMXNET3_BAR1_CMD 0x020 /* command */ +#define VMXNET3_BAR1_MACL 0x028 /* MAC address low */ +#define VMXNET3_BAR1_MACH 0x030 /* MAC address high */ +#define VMXNET3_BAR1_INTR 0x038 /* interrupt status */ +#define VMXNET3_BAR1_EVENT 0x040 /* event status */ + +#define VMXNET3_CMD_ENABLE 0xcafe0000 /* enable VMXNET3 */ +#define VMXNET3_CMD_DISABLE 0xcafe0001 /* disable VMXNET3 */ +#define VMXNET3_CMD_RESET 0xcafe0002 /* reset device */ +#define VMXNET3_CMD_SET_RXMODE 0xcafe0003 /* set interface flags */ +#define VMXNET3_CMD_SET_FILTER 0xcafe0004 /* set address filter */ +#define VMXNET3_CMD_GET_STATUS 0xf00d0000 /* get queue errors */ +#define VMXNET3_CMD_GET_LINK 0xf00d0002 /* get link status */ +#define VMXNET3_CMD_GET_MACL 0xf00d0003 +#define VMXNET3_CMD_GET_MACH 0xf00d0004 + +#define VMXNET3_DMADESC_ALIGN 128 + +/* All descriptors are in little-endian format. */ +struct vmxnet3_txdesc { + uint64_t tx_addr; + + uint32_t tx_word2; +#define VMXNET3_TX_LEN_M 0x00003fff +#define VMXNET3_TX_LEN_S 0 +#define VMXNET3_TX_GEN_M 0x00000001 /* generation */ +#define VMXNET3_TX_GEN_S 14 +#define VMXNET3_TX_RES0 0x00008000 +#define VMXNET3_TX_DTYPE_M 0x00000001 /* descriptor type */ +#define VMXNET3_TX_DTYPE_S 16 /* descriptor type */ +#define VMXNET3_TX_RES1 0x00000002 +#define VMXNET3_TX_OP_M 0x00003fff /* offloading position */ +#define VMXNET3_TX_OP_S 18 + + uint32_t tx_word3; +#define VMXNET3_TX_HLEN_M 0x000003ff /* header len */ +#define VMXNET3_TX_HLEN_S 0 +#define VMXNET3_TX_OM_M 0x00000003 /* offloading mode */ +#define VMXNET3_TX_OM_S 10 +#define VMXNET3_TX_EOP 0x00001000 /* end of packet */ +#define VMXNET3_TX_COMPREQ 0x00002000 /* completion request */ +#define VMXNET3_TX_RES2 0x00004000 +#define VMXNET3_TX_VTAG_MODE 0x00008000 /* VLAN tag insertion mode */ +#define VMXNET3_TX_VLANTAG_M 0x0000ffff +#define VMXNET3_TX_VLANTAG_S 16 +} __packed; + +/* offloading modes */ +#define VMXNET3_OM_NONE 0 +#define VMXNET3_OM_CSUM 2 +#define VMXNET3_OM_TSO 3 + +struct vmxnet3_txcompdesc { + uint32_t txc_word0; +#define VMXNET3_TXC_EOPIDX_M 0x00000fff /* eop index in Tx ring */ +#define VMXNET3_TXC_EOPIDX_S 0 +#define VMXNET3_TXC_RES0_M 0x000fffff +#define VMXNET3_TXC_RES0_S 12 + + uint32_t txc_word1; + uint32_t txc_word2; + + uint32_t txc_word3; +#define VMXNET3_TXC_RES2_M 0x00ffffff +#define VMXNET3_TXC_TYPE_M 0x0000007f +#define VMXNET3_TXC_TYPE_S 24 +#define VMXNET3_TXC_GEN_M 0x00000001 +#define VMXNET3_TXC_GEN_S 31 +} __packed; + +struct vmxnet3_rxdesc { + uint64_t rx_addr; + + uint32_t rx_word2; +#define VMXNET3_RX_LEN_M 0x00003fff +#define VMXNET3_RX_LEN_S 0 +#define VMXNET3_RX_BTYPE_M 0x00000001 /* buffer type */ +#define VMXNET3_RX_BTYPE_S 14 +#define VMXNET3_RX_DTYPE_M 0x00000001 /* descriptor type */ +#define VMXNET3_RX_DTYPE_S 15 +#define VMXNET3_RX_RES0_M 0x00007fff +#define VMXNET3_RX_RES0_S 16 +#define VMXNET3_RX_GEN_M 0x00000001 +#define VMXNET3_RX_GEN_S 31 + + uint32_t rx_word3; +} __packed; + +/* buffer types */ +#define VMXNET3_BTYPE_HEAD 0 /* head only */ +#define VMXNET3_BTYPE_BODY 1 /* body only */ + +struct vmxnet3_rxcompdesc { + uint32_t rxc_word0; +#define VMXNET3_RXC_IDX_M 0x00000fff /* Rx descriptor index */ +#define VMXNET3_RXC_IDX_S 0 +#define VMXNET3_RXC_RES0_M 0x00000003 +#define VMXNET3_RXC_RES0_S 12 +#define VMXNET3_RXC_EOP 0x00004000 /* end of packet */ +#define VMXNET3_RXC_SOP 0x00008000 /* start of packet */ +#define VMXNET3_RXC_QID_M 0x000003ff +#define VMXNET3_RXC_QID_S 16 +#define VMXNET3_RXC_RSSTYPE_M 0x0000000f +#define VMXNET3_RXC_RSSTYPE_S 26 +#define VMXNET3_RXC_NOCSUM 0x40000000 /* no checksum calculated */ +#define VMXNET3_RXC_RES1 0x80000000 + + uint32_t rxc_word1; +#define VMXNET3_RXC_RSSHASH_M 0xffffffff /* RSS hash value */ +#define VMXNET3_RXC_RSSHASH_S 0 + + uint32_t rxc_word2; +#define VMXNET3_RXC_LEN_M 0x00003fff +#define VMXNET3_RXC_LEN_S 0 +#define VMXNET3_RXC_ERROR 0x00004000 +#define VMXNET3_RXC_VLAN 0x00008000 /* 802.1Q VLAN frame */ +#define VMXNET3_RXC_VLANTAG_M 0x0000ffff /* VLAN tag */ +#define VMXNET3_RXC_VLANTAG_S 16 + + uint32_t rxc_word3; +#define VMXNET3_RXC_CSUM_M 0x0000ffff /* TCP/UDP checksum */ +#define VMXNET3_RXC_CSUM_S 16 +#define VMXNET3_RXC_CSUM_OK 0x00010000 /* TCP/UDP checksum ok */ +#define VMXNET3_RXC_UDP 0x00020000 +#define VMXNET3_RXC_TCP 0x00040000 +#define VMXNET3_RXC_IPSUM_OK 0x00080000 /* IP checksum ok */ +#define VMXNET3_RXC_IPV6 0x00100000 +#define VMXNET3_RXC_IPV4 0x00200000 +#define VMXNET3_RXC_FRAGMENT 0x00400000 /* IP fragment */ +#define VMXNET3_RXC_FCS 0x00800000 /* frame CRC correct */ +#define VMXNET3_RXC_TYPE_M 0x7f000000 +#define VMXNET3_RXC_GEN_M 0x00000001 +#define VMXNET3_RXC_GEN_S 31 +} __packed; + +#define VMXNET3_REV1_MAGIC 0xbabefee1 + +#define VMXNET3_GOS_UNKNOWN 0x00 +#define VMXNET3_GOS_LINUX 0x04 +#define VMXNET3_GOS_WINDOWS 0x08 +#define VMXNET3_GOS_SOLARIS 0x0c +#define VMXNET3_GOS_FREEBSD 0x10 +#define VMXNET3_GOS_PXE 0x14 + +#define VMXNET3_GOS_32BIT 0x01 +#define VMXNET3_GOS_64BIT 0x02 + +#define VMXNET3_MAX_TX_QUEUES 8 +#define VMXNET3_MAX_RX_QUEUES 16 +#define VMXNET3_MAX_INTRS (VMXNET3_MAX_TX_QUEUES + VMXNET3_MAX_RX_QUEUES + 1) +#define VMXNET3_NINTR 1 + +#define VMXNET3_ICTRL_DISABLE_ALL 0x01 + +#define VMXNET3_RXMODE_UCAST 0x01 +#define VMXNET3_RXMODE_MCAST 0x02 +#define VMXNET3_RXMODE_BCAST 0x04 +#define VMXNET3_RXMODE_ALLMULTI 0x08 +#define VMXNET3_RXMODE_PROMISC 0x10 + +#define VMXNET3_EVENT_RQERROR 0x01 +#define VMXNET3_EVENT_TQERROR 0x02 +#define VMXNET3_EVENT_LINK 0x04 +#define VMXNET3_EVENT_DIC 0x08 +#define VMXNET3_EVENT_DEBUG 0x10 + +#define VMXNET3_MAX_MTU 9000 +#define VMXNET3_MIN_MTU 60 + +struct vmxnet3_driver_shared { + uint32_t magic; + uint32_t pad1; + + uint32_t version; /* driver version */ + uint32_t guest; /* guest OS */ + uint32_t vmxnet3_revision; /* supported VMXNET3 revision */ + uint32_t upt_version; /* supported UPT version */ + uint64_t upt_features; + uint64_t driver_data; + uint64_t queue_shared; + uint32_t driver_data_len; + uint32_t queue_shared_len; + uint32_t mtu; + uint16_t nrxsg_max; + uint8_t ntxqueue; + uint8_t nrxqueue; + uint32_t reserved1[4]; + + /* interrupt control */ + uint8_t automask; + uint8_t nintr; + uint8_t evintr; + uint8_t modlevel[VMXNET3_MAX_INTRS]; + uint32_t ictrl; + uint32_t reserved2[2]; + + /* receive filter parameters */ + uint32_t rxmode; + uint16_t mcast_tablelen; + uint16_t pad2; + uint64_t mcast_table; + uint32_t vlan_filter[4096 / 32]; + + struct { + uint32_t version; + uint32_t len; + uint64_t paddr; + } rss, pm, plugin; + + uint32_t event; + uint32_t reserved3[5]; +} __packed; + +struct vmxnet3_txq_shared { + uint32_t npending; + uint32_t intr_threshold; + uint64_t reserved1; + + uint64_t cmd_ring; + uint64_t data_ring; + uint64_t comp_ring; + uint64_t driver_data; + uint64_t reserved2; + uint32_t cmd_ring_len; + uint32_t data_ring_len; + uint32_t comp_ring_len; + uint32_t driver_data_len; + uint8_t intr_idx; + uint8_t pad1[7]; + + uint8_t stopped; + uint8_t pad2[3]; + uint32_t error; + + struct UPT1_TxStats stats; + + uint8_t pad3[88]; +} __packed; + +struct vmxnet3_rxq_shared { + uint8_t update_rxhead; + uint8_t pad1[7]; + uint64_t reserved1; + + uint64_t cmd_ring[2]; + uint64_t comp_ring; + uint64_t driver_data; + uint64_t reserved2; + uint32_t cmd_ring_len[2]; + uint32_t comp_ring_len; + uint32_t driver_data_len; + uint8_t intr_idx; + uint8_t pad2[7]; + + uint8_t stopped; + uint8_t pad3[3]; + uint32_t error; + + struct UPT1_RxStats stats; + + uint8_t pad4[88]; +} __packed;