From e0adee910531a62ec15b1a2382c7d65ba7240f4e Mon Sep 17 00:00:00 2001 From: mikeb Date: Wed, 13 Jul 2016 21:59:35 +0000 Subject: [PATCH] A work-in-progress driver for the Hyper-V NetVSC Network VSC is an RNDIS device using NVSP protocol to communicate with the Hyper-V VMBus via VMBus channels and/or shared memory. The code was ported from FreeBSD with some API changes and simplifications. While the Rx path is nearly complete, packet transmition is not implemented yet. ok deraadt, mlarkin --- sys/arch/amd64/conf/GENERIC | 3 +- sys/dev/pv/files.pv | 6 +- sys/dev/pv/if_hvn.c | 1485 +++++++++++++++++++++++++++++++++++ sys/dev/pv/if_hvnreg.h | 830 ++++++++++++++++++++ sys/dev/pv/rndisreg.h | 974 +++++++++++++++++++++++ 5 files changed, 3296 insertions(+), 2 deletions(-) create mode 100644 sys/dev/pv/if_hvn.c create mode 100644 sys/dev/pv/if_hvnreg.h create mode 100644 sys/dev/pv/rndisreg.h diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC index 7cb016a2139..04f24ce86c6 100644 --- a/sys/arch/amd64/conf/GENERIC +++ b/sys/arch/amd64/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.423 2016/07/01 15:02:49 jcs Exp $ +# $OpenBSD: GENERIC,v 1.424 2016/07/13 21:59:35 mikeb Exp $ # # For further information on compiling OpenBSD kernels, see the config(8) # man page. @@ -76,6 +76,7 @@ xen0 at pvbus? # Xen HVM domU xnf* at xen? # Xen Netfront #hyperv0 at pvbus? # Hyper-V guest +#hvn* at hyperv? # Hyper-V netvsc option PCIVERBOSE option USBVERBOSE diff --git a/sys/dev/pv/files.pv b/sys/dev/pv/files.pv index 8f8785c2ba2..c49d39dea20 100644 --- a/sys/dev/pv/files.pv +++ b/sys/dev/pv/files.pv @@ -1,4 +1,4 @@ -# $OpenBSD: files.pv,v 1.6 2016/06/22 01:13:07 mikeb Exp $ +# $OpenBSD: files.pv,v 1.7 2016/07/13 21:59:35 mikeb Exp $ # # Config file and device description for paravirtual devices. # Included by ports that need it. @@ -27,3 +27,7 @@ file dev/pv/if_xnf.c xnf device hyperv {} attach hyperv at pvbus file dev/pv/hyperv.c hyperv needs-flag + +device hvn: ether, ifnet, ifmedia +attach hvn at hyperv +file dev/pv/if_hvn.c hvn diff --git a/sys/dev/pv/if_hvn.c b/sys/dev/pv/if_hvn.c new file mode 100644 index 00000000000..f8e53145073 --- /dev/null +++ b/sys/dev/pv/if_hvn.c @@ -0,0 +1,1485 @@ +/*- + * Copyright (c) 2009-2012 Microsoft Corp. + * Copyright (c) 2010-2012 Citrix Inc. + * Copyright (c) 2012 NetApp Inc. + * Copyright (c) 2016 Mike Belopuhov + * 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. + */ + +/* + * The OpenBSD port was done under funding by Esdenera Networks GmbH. + */ + +#include "bpfilter.h" +#include "vlan.h" +#include "hyperv.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#ifdef INET6 +#include +#endif + +#if NBPFILTER > 0 +#include +#endif + +#define HVN_DEBUG 1 + +/* + * RNDIS control interface + */ +#define HVN_RNDIS_CTLREQS 4 +#define HVN_RNDIS_CMPBUFSZ 512 + +struct rndis_cmd { + uint32_t rc_id; + struct rndis *rc_req; + bus_dmamap_t rc_dmap; + uint64_t rc_pfn; + struct rndis rc_cmp; + uint32_t rc_cmplen; + uint8_t rc_cmpbuf[HVN_RNDIS_CMPBUFSZ]; + struct nvsp rc_nvsp; + struct mutex rc_mtx; + TAILQ_ENTRY(rndis_cmd) rc_entry; +}; +TAILQ_HEAD(rndis_queue, rndis_cmd); + +/* + * Rx ring + */ +#define HVN_RX_BUFID 0xcafe + +/* + * Tx ring + */ +#define HVN_TX_DESC 128 +#define HVN_TX_BUFID 0xface + +struct hvn_softc { + struct device sc_dev; + struct hv_softc *sc_hvsc; + struct hv_channel *sc_chan; + bus_dma_tag_t sc_dmat; + + struct arpcom sc_ac; + struct ifmedia sc_media; + int sc_link_state; + int sc_promisc; + + /* NVSP protocol */ + int sc_proto; + uint32_t sc_nvsptid; + struct nvsp sc_nvsp; + uint8_t *sc_nvspbuf; +#define HVN_NVSP_BUFSIZE (PAGE_SIZE * 4) + struct mutex sc_nvsplck; + + /* RNDIS protocol */ + uint32_t sc_rndisrid; + struct rndis_queue sc_cntl_sq; /* submission queue */ + struct mutex sc_cntl_sqlck; + struct rndis_queue sc_cntl_cq; /* completion queue */ + struct mutex sc_cntl_cqlck; + struct rndis_queue sc_cntl_fq; /* free queue */ + struct mutex sc_cntl_fqlck; + struct rndis_cmd sc_cntl_msgs[HVN_RNDIS_CTLREQS]; + + /* Rx ring */ + void *sc_rx_ring; + int sc_rx_size; + uint32_t sc_rx_hndl; + + /* Tx ring */ + void *sc_tx_ring; + int sc_tx_size; + uint32_t sc_tx_hndl; +}; + +int hvn_match(struct device *, void *, void *); +void hvn_attach(struct device *, struct device *, void *); +int hvn_ioctl(struct ifnet *, u_long, caddr_t); +int hvn_media_change(struct ifnet *); +void hvn_media_status(struct ifnet *, struct ifmediareq *); +int hvn_iff(struct hvn_softc *); +void hvn_init(struct hvn_softc *); +void hvn_stop(struct hvn_softc *); +void hvn_start(struct ifnet *); +void hvn_intr(void *); +void hvn_txeof(struct hvn_softc *, uint64_t); +void hvn_rxeof(struct hvn_softc *, void *); +int hvn_rx_ring_create(struct hvn_softc *); +int hvn_rx_ring_destroy(struct hvn_softc *); +int hvn_tx_ring_create(struct hvn_softc *); +int hvn_tx_ring_destroy(struct hvn_softc *); +int hvn_get_lladdr(struct hvn_softc *); +int hvn_set_lladdr(struct hvn_softc *); +void hvn_get_link_status(struct hvn_softc *); +void hvn_link_status(struct hvn_softc *); + +/* NSVP */ +int hvn_nvsp_attach(struct hvn_softc *); +void hvn_nvsp_intr(void *); +int hvn_nvsp_output(struct hvn_softc *, struct nvsp *, uint64_t, int); +int hvn_nvsp_ack(struct hvn_softc *, struct nvsp *, uint64_t); +void hvn_nvsp_detach(struct hvn_softc *); + +/* RNDIS */ +int hvn_rndis_attach(struct hvn_softc *); +int hvn_rndis_ctloutput(struct hvn_softc *, struct rndis_cmd *, int); +void hvn_rndis_filter(struct hvn_softc *sc, uint64_t, void *); +void hvn_rndis_input(struct hvn_softc *, caddr_t, uint32_t, + struct mbuf_list *); +void hvn_rndis_complete(struct hvn_softc *, caddr_t, uint32_t); +void hvn_rndis_status(struct hvn_softc *, caddr_t, uint32_t); +int hvn_rndis_query(struct hvn_softc *, uint32_t, void *, size_t *); +int hvn_rndis_set(struct hvn_softc *, uint32_t, void *, size_t); +int hvn_rndis_open(struct hvn_softc *); +int hvn_rndis_close(struct hvn_softc *); +void hvn_rndis_detach(struct hvn_softc *); + +struct cfdriver hvn_cd = { + NULL, "hvn", DV_IFNET +}; + +const struct cfattach hvn_ca = { + sizeof(struct hvn_softc), hvn_match, hvn_attach +}; + +int +hvn_match(struct device *parent, void *match, void *aux) +{ + struct hv_attach_args *aa = aux; + + if (strcmp("network", aa->aa_ident)) + return (0); + + return (1); +} + +void +hvn_attach(struct device *parent, struct device *self, void *aux) +{ + struct hv_attach_args *aa = aux; + struct hvn_softc *sc = (struct hvn_softc *)self; + struct ifnet *ifp = &sc->sc_ac.ac_if; + + sc->sc_hvsc = (struct hv_softc *)parent; + sc->sc_chan = aa->aa_chan; + sc->sc_dmat = aa->aa_dmat; + + strlcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ); + + DPRINTF("\n"); + + if (hvn_nvsp_attach(sc)) { + printf(": failed to init NVSP\n"); + return; + } + + if (hvn_rx_ring_create(sc)) { + printf(": failed to create Rx ring\n"); + goto detach; + } + + if (hvn_tx_ring_create(sc)) { + printf(": failed to create Tx ring\n"); + goto detach; + } + + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_xflags = IFXF_MPSAFE; + ifp->if_ioctl = hvn_ioctl; + ifp->if_start = hvn_start; + ifp->if_softc = sc; + + ifp->if_capabilities = IFCAP_CSUM_IPv4 | IFCAP_CSUM_TCPv4 | + IFCAP_CSUM_UDPv4; +#if NVLAN > 0 + ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING; +#endif + + IFQ_SET_MAXLEN(&ifp->if_snd, HVN_TX_DESC - 1); + + ifmedia_init(&sc->sc_media, IFM_IMASK, hvn_media_change, + hvn_media_status); + ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_MANUAL, 0, NULL); + ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_MANUAL); + + if_attach(ifp); + + if (hvn_rndis_attach(sc)) { + printf(": failed to init RNDIS\n"); + goto detach; + } + + if (hvn_get_lladdr(sc)) { + printf(": failed to obtain an ethernet address\n"); + hvn_rndis_detach(sc); + goto detach; + } + + DPRINTF("%s", sc->sc_dev.dv_xname); + printf(": channel %u, address %s\n", sc->sc_chan->ch_relid, + ether_sprintf(sc->sc_ac.ac_enaddr)); + + ether_ifattach(ifp); + return; + + detach: + hvn_rx_ring_destroy(sc); + hvn_tx_ring_destroy(sc); + hvn_nvsp_detach(sc); + if (ifp->if_start) + if_detach(ifp); +} + +int +hvn_ioctl(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct hvn_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + int s, error = 0; + + s = splnet(); + + switch (command) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + if (!(ifp->if_flags & IFF_RUNNING)) + hvn_init(sc); + break; + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (ifp->if_flags & IFF_RUNNING) + error = ENETRESET; + else + hvn_init(sc); + } else { + if (ifp->if_flags & IFF_RUNNING) + hvn_stop(sc); + } + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, command); + break; + default: + error = ether_ioctl(ifp, &sc->sc_ac, command, data); + } + + if (error == ENETRESET) { + if (ifp->if_flags & IFF_RUNNING) + hvn_iff(sc); + error = 0; + } + + splx(s); + + return (error); +} + +int +hvn_media_change(struct ifnet *ifp) +{ + return (0); +} + +void +hvn_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct hvn_softc *sc = ifp->if_softc; + + hvn_get_link_status(sc); + hvn_link_status(sc); + + ifmr->ifm_status = IFM_AVALID; + ifmr->ifm_active = IFM_ETHER | IFM_MANUAL; + if (sc->sc_link_state == LINK_STATE_UP) + ifmr->ifm_status |= IFM_ACTIVE; +} + +void +hvn_link_status(struct hvn_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + + if (sc->sc_link_state != ifp->if_link_state) { + ifp->if_link_state = sc->sc_link_state; + if_link_state_change(ifp); + } +} + +int +hvn_iff(struct hvn_softc *sc) +{ + /* XXX */ + sc->sc_promisc = 0; + + return (0); +} + +void +hvn_init(struct hvn_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + + hvn_stop(sc); + + hvn_iff(sc); + + if (hvn_rndis_open(sc) == 0) { + ifp->if_flags |= IFF_RUNNING; + ifq_clr_oactive(&ifp->if_snd); + } +} + +void +hvn_stop(struct hvn_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + + if (ifp->if_flags & IFF_RUNNING) { + ifp->if_flags &= ~IFF_RUNNING; + hvn_rndis_close(sc); + } + + ifq_barrier(&ifp->if_snd); + intr_barrier(sc->sc_chan); + + ifq_clr_oactive(&ifp->if_snd); +} + +void +hvn_start(struct ifnet *ifp) +{ + struct mbuf *m; + + if (!(ifp->if_flags & IFF_RUNNING) || ifq_is_oactive(&ifp->if_snd)) + return; + + for (;;) { + m = ifq_deq_begin(&ifp->if_snd); + if (m == NULL) + break; + + ifq_deq_commit(&ifp->if_snd, m); + +#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_mtap_ether(ifp->if_bpf, m, BPF_DIRECTION_OUT); +#endif + + m_freem(m); + ifp->if_oerrors++; + } +} + +#if 0 +void +hvn_intr(void *arg) +{ + struct hvn_softc *sc = arg; + struct ifnet *ifp = &sc->sc_ac.ac_if; + uint64_t rid; + uint32_t rlen; + int rv; + + if (!(ifp->if_flags & IFF_RUNNING)) + return; + + if (ifq_is_oactive(&ifp->if_snd)) + ifq_restart(&ifp->if_snd); +} +#endif + +void +hvn_txeof(struct hvn_softc *sc, uint64_t tid) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + + printf("%s: tx tid %llu\n", ifp->if_xname, tid); +} + +int +hvn_rx_ring_create(struct hvn_softc *sc) +{ + struct nvsp *pkt = &sc->sc_nvsp; + struct nvsp_send_rx_buf *msg; + struct nvsp_send_rx_buf_comp *cmp; + uint64_t tid; + + if (sc->sc_proto <= NVSP_PROTOCOL_VERSION_2) + sc->sc_rx_size = 15 * 1024 * 1024; /* 15MB */ + else + sc->sc_rx_size = 16 * 1024 * 1024; /* 16MB */ + sc->sc_rx_ring = km_alloc(sc->sc_rx_size, &kv_any, &kp_zero, + cold ? &kd_nowait : &kd_waitok); + if (sc->sc_rx_ring == NULL) { + DPRINTF("%s: failed to allocate Rx ring buffer\n", + sc->sc_dev.dv_xname); + return (-1); + } + if (hv_handle_alloc(sc->sc_chan, sc->sc_rx_ring, sc->sc_rx_size, + &sc->sc_rx_hndl)) { + DPRINTF("%s: failed to obtain a PA handle\n", + sc->sc_dev.dv_xname); + goto errout; + } + + memset(pkt, 0, sizeof(*pkt)); + pkt->msg_type = nvsp_type_send_rx_buf; + msg = (struct nvsp_send_rx_buf *)&pkt->msg; + msg->gpadl_handle = sc->sc_rx_hndl; + msg->id = HVN_RX_BUFID; + + tid = atomic_inc_int_nv(&sc->sc_nvsptid); + if (hvn_nvsp_output(sc, pkt, tid, 100)) + goto errout; + + cmp = (struct nvsp_send_rx_buf_comp *)&pkt->msg; + if (cmp->status != nvsp_status_success) { + DPRINTF("%s: failed to set up the Rx ring\n", + sc->sc_dev.dv_xname); + goto errout; + } + if (cmp->num_sections > 1) { + DPRINTF("%s: invalid number of Rx ring sections: %d\n", + sc->sc_dev.dv_xname, cmp->num_sections); + hvn_rx_ring_destroy(sc); + return (-1); + } + return (0); + + errout: + if (sc->sc_rx_hndl) { + hv_handle_free(sc->sc_chan, sc->sc_rx_hndl); + sc->sc_rx_hndl = 0; + } + if (sc->sc_rx_ring) { + km_free(sc->sc_rx_ring, sc->sc_rx_size, &kv_any, &kp_zero); + sc->sc_rx_ring = NULL; + } + return (-1); +} + +int +hvn_rx_ring_destroy(struct hvn_softc *sc) +{ + struct nvsp *pkt = &sc->sc_nvsp; + struct nvsp_revoke_rx_buf *msg; + uint64_t tid; + + if (sc->sc_rx_ring == NULL) + return (0); + + memset(pkt, 0, sizeof(*pkt)); + pkt->msg_type = nvsp_type_revoke_rx_buf; + msg = (struct nvsp_revoke_rx_buf *)&pkt->msg; + msg->id = HVN_RX_BUFID; + + tid = atomic_inc_int_nv(&sc->sc_nvsptid); + if (hvn_nvsp_output(sc, pkt, tid, 0)) + return (-1); + + delay(100); + + hv_handle_free(sc->sc_chan, sc->sc_rx_hndl); + + sc->sc_rx_hndl = 0; + + km_free(sc->sc_rx_ring, sc->sc_rx_size, &kv_any, &kp_zero); + sc->sc_rx_ring = NULL; + + return (0); +} + +int +hvn_tx_ring_create(struct hvn_softc *sc) +{ + struct nvsp *pkt = &sc->sc_nvsp; + struct nvsp_send_tx_buf *msg; + struct nvsp_send_tx_buf_comp *cmp; + uint64_t tid; + + sc->sc_tx_size = 15 * 1024 * 1024; /* 15MB */ + sc->sc_tx_ring = km_alloc(sc->sc_tx_size, &kv_any, &kp_zero, + cold ? &kd_nowait : &kd_waitok); + if (sc->sc_tx_ring == NULL) { + DPRINTF("%s: failed to allocate Tx ring buffer\n", + sc->sc_dev.dv_xname); + return (-1); + } + if (hv_handle_alloc(sc->sc_chan, sc->sc_tx_ring, sc->sc_tx_size, + &sc->sc_tx_hndl)) { + DPRINTF("%s: failed to obtain a PA handle\n", + sc->sc_dev.dv_xname); + goto errout; + } + + memset(pkt, 0, sizeof(*pkt)); + pkt->msg_type = nvsp_type_send_tx_buf; + msg = (struct nvsp_send_tx_buf *)&pkt->msg; + msg->gpadl_handle = sc->sc_tx_hndl; + msg->id = HVN_TX_BUFID; + + tid = atomic_inc_int_nv(&sc->sc_nvsptid); + if (hvn_nvsp_output(sc, pkt, tid, 100)) + goto errout; + + cmp = (struct nvsp_send_tx_buf_comp *)&pkt->msg; + if (cmp->status != nvsp_status_success) { + DPRINTF("%s: failed to set up the Tx ring\n", + sc->sc_dev.dv_xname); + goto errout; + } + return (0); + + errout: + if (sc->sc_tx_hndl) { + hv_handle_free(sc->sc_chan, sc->sc_tx_hndl); + sc->sc_tx_hndl = 0; + } + if (sc->sc_tx_ring) { + km_free(sc->sc_tx_ring, sc->sc_tx_size, &kv_any, &kp_zero); + sc->sc_tx_ring = NULL; + } + return (-1); +} + +int +hvn_tx_ring_destroy(struct hvn_softc *sc) +{ + struct nvsp *pkt = &sc->sc_nvsp; + struct nvsp_revoke_tx_buf *msg; + uint64_t tid; + + if (sc->sc_tx_ring == NULL) + return (0); + + memset(pkt, 0, sizeof(*pkt)); + pkt->msg_type = nvsp_type_revoke_tx_buf; + msg = (struct nvsp_revoke_tx_buf *)&pkt->msg; + msg->id = HVN_TX_BUFID; + + tid = atomic_inc_int_nv(&sc->sc_nvsptid); + if (hvn_nvsp_output(sc, pkt, tid, 0)) + return (-1); + + delay(100); + + hv_handle_free(sc->sc_chan, sc->sc_tx_hndl); + + sc->sc_tx_hndl = 0; + + km_free(sc->sc_tx_ring, sc->sc_tx_size, &kv_any, &kp_zero); + sc->sc_tx_ring = NULL; + + return (0); +} + +int +hvn_get_lladdr(struct hvn_softc *sc) +{ + char enaddr[ETHER_ADDR_LEN]; + size_t addrlen = ETHER_ADDR_LEN; + int rv; + + rv = hvn_rndis_query(sc, RNDIS_OID_802_3_PERMANENT_ADDRESS, + enaddr, &addrlen); + if (rv == 0 && addrlen == ETHER_ADDR_LEN) + memcpy(sc->sc_ac.ac_enaddr, enaddr, ETHER_ADDR_LEN); + return (rv); +} + +int +hvn_set_lladdr(struct hvn_softc *sc) +{ + return (hvn_rndis_set(sc, RNDIS_OID_802_3_CURRENT_ADDRESS, + sc->sc_ac.ac_enaddr, ETHER_ADDR_LEN)); +} + +void +hvn_get_link_status(struct hvn_softc *sc) +{ + uint32_t state; + size_t len = sizeof(state); + + if (hvn_rndis_query(sc, RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, + &state, &len) == 0) + sc->sc_link_state = (state == 0) ? LINK_STATE_UP : + LINK_STATE_DOWN; +} + +int +hvn_nvsp_attach(struct hvn_softc *sc) +{ + const uint32_t protos[] = { + NVSP_PROTOCOL_VERSION_5, NVSP_PROTOCOL_VERSION_4, + NVSP_PROTOCOL_VERSION_2, NVSP_PROTOCOL_VERSION_1 + }; + struct nvsp *pkt = &sc->sc_nvsp; + struct nvsp_init *init; + struct nvsp_init_comp *cmp; + struct nvsp_send_ndis_version *ver; + uint64_t tid; + uint32_t ndisver; + int i; + + /* 4 page sized buffer for channel messages */ + sc->sc_nvspbuf = km_alloc(HVN_NVSP_BUFSIZE, &kv_any, &kp_zero, + (cold ? &kd_nowait : &kd_waitok)); + if (sc->sc_nvspbuf == NULL) { + DPRINTF("%s: failed to allocate channel data buffer\n", + sc->sc_dev.dv_xname); + return (-1); + } + sc->sc_chan->ch_buflen = PAGE_SIZE * 4; + + /* Associate our interrupt handler with the channel */ + if (hv_channel_open(sc->sc_chan, NULL, 0, hvn_nvsp_intr, sc)) { + DPRINTF("%s: failed to open channel\n", sc->sc_dev.dv_xname); + km_free(sc->sc_nvspbuf, HVN_NVSP_BUFSIZE, &kv_any, &kp_zero); + return (-1); + } + + mtx_init(&sc->sc_nvsplck, IPL_NET); + + memset(pkt, 0, sizeof(pkt)); + pkt->msg_type = nvsp_type_init; + init = (struct nvsp_init *)&pkt->msg; + + for (i = 0; i < nitems(protos); i++) { + init->protocol_version = init->protocol_version_2 = protos[i]; + tid = atomic_inc_int_nv(&sc->sc_nvsptid); + if (hvn_nvsp_output(sc, pkt, tid, 100)) + return (-1); + cmp = (struct nvsp_init_comp *)&pkt->msg; + if (cmp->status == nvsp_status_success) { + sc->sc_proto = protos[i]; + break; + } + } + if (!sc->sc_proto) { + DPRINTF("%s: failed to negotiate NVSP version\n", + sc->sc_dev.dv_xname); + return (-1); + } + + memset(pkt, 0, sizeof(pkt)); + pkt->msg_type = nvsp_type_send_ndis_vers; + ver = (struct nvsp_send_ndis_version *)&pkt->msg; + if (sc->sc_proto <= NVSP_PROTOCOL_VERSION_4) + ndisver = NDIS_VERSION_6_1; + else + ndisver = NDIS_VERSION_6_30; + ver->ndis_major_vers = (ndisver & 0xffff0000) >> 16; + ver->ndis_minor_vers = (ndisver & 0x0000ffff); + + tid = atomic_inc_int_nv(&sc->sc_nvsptid); + if (hvn_nvsp_output(sc, pkt, tid, 100)) + return (-1); + + DPRINTF("%s: NVSP %u.%u, NDIS %u.%u\n", sc->sc_dev.dv_xname, + sc->sc_proto >> 16, sc->sc_proto & 0xffff, + ndisver >> 16, ndisver & 0xffff); + + return (0); +} + +void +hvn_nvsp_intr(void *arg) +{ + struct hvn_softc *sc = arg; + struct hv_pktdesc *d; + struct nvsp *pkt; + uint64_t rid; + uint32_t rlen; + int rv; + + for (;;) { + rv = hv_channel_recv(sc->sc_chan, sc->sc_nvspbuf, + HVN_NVSP_BUFSIZE, &rlen, &rid, 1); + if (rv != 0 || rlen == 0) { + if (rv != EAGAIN) + printf("%s: failed to receive an NVSP " + "packet\n", sc->sc_dev.dv_xname); + break; + } + d = (struct hv_pktdesc *)sc->sc_nvspbuf; + pkt = (struct nvsp *)((char *)d + (d->offset << 3)); + + if (d->type == HV_PKT_COMPLETION) { + switch (pkt->msg_type) { + case nvsp_type_init_comp: + case nvsp_type_send_rx_buf_comp: + case nvsp_type_send_tx_buf_comp: + case nvsp_type_subchannel: + /* copy the response back */ + memcpy(&sc->sc_nvsp, pkt, sizeof(sc->sc_nvsp)); + wakeup_one(&sc->sc_nvsp); + break; + case nvsp_type_send_rndis_pkt_comp: + hvn_txeof(sc, d->tid); + break; + default: + printf("%s: unhandled NVSP packet type %d " + "on completion\n", sc->sc_dev.dv_xname, + pkt->msg_type); + } + } else if (d->type == HV_PKT_DATA_USING_TRANSFER_PAGES) { + switch (pkt->msg_type) { + case nvsp_type_send_rndis_pkt: + hvn_rndis_filter(sc, d->tid, d + 1); + break; + default: + printf("%s: unhandled NVSP packet type %d " + "on receive\n", sc->sc_dev.dv_xname, + pkt->msg_type); + } + } else + printf("%s: unknown NVSP packet type %u\n", + sc->sc_dev.dv_xname, d->type); + } +} + +int +hvn_nvsp_output(struct hvn_softc *sc, struct nvsp *pkt, uint64_t tid, int timo) +{ + int tries = 10; + int rv; + + do { + rv = hv_channel_send(sc->sc_chan, pkt, sizeof(*pkt), + tid, HV_PKT_DATA_IN_BAND, + timo ? HV_PKTFLAG_COMPLETION_REQUESTED : 0); + if (rv == EAGAIN) { + if (timo) + tsleep(pkt, PRIBIO, "hvnsend", timo / 10); + else + delay(100); + } else if (rv) { + DPRINTF("%s: NVSP operation %d send error %d\n", + sc->sc_dev.dv_xname, pkt->msg_type, rv); + return (rv); + } + } while (rv != 0 && --tries > 0); + + if (timo) { + mtx_enter(&sc->sc_nvsplck); + rv = msleep(&sc->sc_nvsp, &sc->sc_nvsplck, PRIBIO, "hvnvsp", + timo); + mtx_leave(&sc->sc_nvsplck); +#ifdef HVN_DEBUG + switch (rv) { + case EINTR: + rv = 0; + break; + case EWOULDBLOCK: + printf("%s: NVSP opertaion %d timed out\n", + sc->sc_dev.dv_xname, pkt->msg_type); + } + } +#endif + return (rv); +} + +int +hvn_nvsp_ack(struct hvn_softc *sc, struct nvsp *pkt, uint64_t tid) +{ + int tries = 5; + int rv; + + do { + rv = hv_channel_send(sc->sc_chan, pkt, sizeof(*pkt), + tid, HV_PKT_COMPLETION, 0); + if (rv == EAGAIN) + delay(100); + else if (rv) { + DPRINTF("%s: NVSP acknowledgement error %d\n", + sc->sc_dev.dv_xname, rv); + return (rv); + } + } while (rv != 0 && --tries > 0); + return (rv); +} + +void +hvn_nvsp_detach(struct hvn_softc *sc) +{ + if (hv_channel_close(sc->sc_chan) == 0) { + km_free(sc->sc_nvspbuf, HVN_NVSP_BUFSIZE, &kv_any, &kp_zero); + sc->sc_nvspbuf = NULL; + } +} + +static inline void +hexdump(const char *header, void *data, size_t size) +{ + uint8_t *ptr = data; + int i; + + for (i = 0; i < size; i++) { + if ((i % 16) == 0) + printf("%s%s+0x%02x:", i > 0 ? "\n" : "", header, i); + printf(" %02x", ptr[i]); + } + printf("\n"); +} + +static inline struct rndis_cmd * +hvn_alloc_cmd(struct hvn_softc *sc) +{ + struct rndis_cmd *rc; + + mtx_enter(&sc->sc_cntl_fqlck); + while ((rc = TAILQ_FIRST(&sc->sc_cntl_fq)) == NULL) + msleep(&sc->sc_cntl_fq, &sc->sc_cntl_fqlck, + PRIBIO, "hvnrr", 1); + TAILQ_REMOVE(&sc->sc_cntl_fq, rc, rc_entry); + mtx_leave(&sc->sc_cntl_fqlck); + return (rc); +} + +static inline void +hvn_submit_cmd(struct hvn_softc *sc, struct rndis_cmd *rc) +{ + mtx_enter(&sc->sc_cntl_sqlck); + TAILQ_INSERT_TAIL(&sc->sc_cntl_sq, rc, rc_entry); + mtx_leave(&sc->sc_cntl_sqlck); +} + +static inline struct rndis_cmd * +hvn_complete_cmd(struct hvn_softc *sc, uint32_t id) +{ + struct rndis_cmd *rc; + + mtx_enter(&sc->sc_cntl_sqlck); + TAILQ_FOREACH(rc, &sc->sc_cntl_sq, rc_entry) { + if (rc->rc_id == id) { + TAILQ_REMOVE(&sc->sc_cntl_sq, rc, rc_entry); + break; + } + } + mtx_leave(&sc->sc_cntl_sqlck); + if (rc != NULL) { + mtx_enter(&sc->sc_cntl_cqlck); + TAILQ_INSERT_TAIL(&sc->sc_cntl_cq, rc, rc_entry); + mtx_leave(&sc->sc_cntl_cqlck); + } + return (rc); +} + +static inline int +hvn_rollback_cmd(struct hvn_softc *sc, struct rndis_cmd *rc) +{ + struct rndis_cmd *rn; + + mtx_enter(&sc->sc_cntl_sqlck); + TAILQ_FOREACH(rn, &sc->sc_cntl_sq, rc_entry) { + if (rn == rc) { + TAILQ_REMOVE(&sc->sc_cntl_sq, rc, rc_entry); + mtx_leave(&sc->sc_cntl_sqlck); + return (0); + } + } + mtx_leave(&sc->sc_cntl_sqlck); + return (-1); +} + +static inline void +hvn_free_cmd(struct hvn_softc *sc, struct rndis_cmd *rc) +{ + memset(rc->rc_req, 0, sizeof(*rc->rc_req)); + memset(&rc->rc_cmp, 0, sizeof(rc->rc_cmp)); + memset(&rc->rc_nvsp, 0, sizeof(rc->rc_nvsp)); + mtx_enter(&sc->sc_cntl_fqlck); + TAILQ_INSERT_TAIL(&sc->sc_cntl_fq, rc, rc_entry); + mtx_leave(&sc->sc_cntl_fqlck); + wakeup(&sc->sc_cntl_fq); +} + +int +hvn_rndis_attach(struct hvn_softc *sc) +{ + struct rndis_init_req *req; + struct rndis_init_comp *cmp; + struct rndis_cmd *rc; + int i, rv; + + /* RNDIS control message queues */ + TAILQ_INIT(&sc->sc_cntl_sq); + TAILQ_INIT(&sc->sc_cntl_cq); + TAILQ_INIT(&sc->sc_cntl_fq); + mtx_init(&sc->sc_cntl_sqlck, IPL_NET); + mtx_init(&sc->sc_cntl_cqlck, IPL_NET); + mtx_init(&sc->sc_cntl_fqlck, IPL_NET); + + for (i = 0; i < HVN_RNDIS_CTLREQS; i++) { + rc = &sc->sc_cntl_msgs[i]; + if (bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1, + PAGE_SIZE, 0, BUS_DMA_WAITOK, &rc->rc_dmap)) { + DPRINTF("%s: failed to create RNDIS command map\n", + sc->sc_dev.dv_xname); + goto errout; + } + rc->rc_req = km_alloc(PAGE_SIZE, &kv_any, &kp_zero, + &kd_waitok); + if (rc->rc_req == NULL) { + DPRINTF("%s: failed to allocate RNDIS command\n", + sc->sc_dev.dv_xname); + bus_dmamap_destroy(sc->sc_dmat, rc->rc_dmap); + goto errout; + } + if (bus_dmamap_load(sc->sc_dmat, rc->rc_dmap, rc->rc_req, + PAGE_SIZE, NULL, BUS_DMA_WAITOK)) { + DPRINTF("%s: failed to load RNDIS command map\n", + sc->sc_dev.dv_xname); + km_free(rc->rc_req, PAGE_SIZE, &kv_any, &kp_zero); + bus_dmamap_destroy(sc->sc_dmat, rc->rc_dmap); + goto errout; + } + rc->rc_pfn = atop(rc->rc_dmap->dm_segs[0].ds_addr); + mtx_init(&rc->rc_mtx, IPL_NET); + TAILQ_INSERT_TAIL(&sc->sc_cntl_fq, rc, rc_entry); + } + + rc = hvn_alloc_cmd(sc); + + bus_dmamap_sync(sc->sc_dmat, rc->rc_dmap, 0, PAGE_SIZE, + BUS_DMASYNC_PREREAD); + + rc->rc_req->msg_type = RNDIS_INITIALIZE_MSG; + rc->rc_req->msg_len = RNDIS_MESSAGE_SIZE(*req); + rc->rc_cmplen = RNDIS_MESSAGE_SIZE(*cmp); + rc->rc_id = atomic_inc_int_nv(&sc->sc_rndisrid); + req = (struct rndis_init_req *)&rc->rc_req->msg; + req->request_id = rc->rc_id; + req->major_version = RNDIS_MAJOR_VERSION; + req->minor_version = RNDIS_MINOR_VERSION; + req->max_xfer_size = 2048; /* XXX */ + + bus_dmamap_sync(sc->sc_dmat, rc->rc_dmap, 0, PAGE_SIZE, + BUS_DMASYNC_PREWRITE); + + if ((rv = hvn_rndis_ctloutput(sc, rc, 500)) != 0) { + DPRINTF("%s: INITIALIZE_MSG failed, error %u\n", + sc->sc_dev.dv_xname, rv); + hvn_free_cmd(sc, rc); + goto errout; + } + cmp = (struct rndis_init_comp *)&rc->rc_cmp.msg; + if (cmp->status != RNDIS_STATUS_SUCCESS) { + DPRINTF("%s: failed to init RNDIS, error %#x\n", + sc->sc_dev.dv_xname, cmp->status); + hvn_free_cmd(sc, rc); + goto errout; + } + DPRINTF("%s: RNDIS %u.%u\n", sc->sc_dev.dv_xname, + cmp->major_version, cmp->minor_version); + + hvn_free_cmd(sc, rc); + + return (0); + +errout: + for (i = 0; i < HVN_RNDIS_CTLREQS; i++) { + rc = &sc->sc_cntl_msgs[i]; + if (rc->rc_req == NULL) + continue; + TAILQ_REMOVE(&sc->sc_cntl_fq, rc, rc_entry); + km_free(rc->rc_req, PAGE_SIZE, &kv_any, &kp_zero); + rc->rc_req = NULL; + bus_dmamap_destroy(sc->sc_dmat, rc->rc_dmap); + } + return (-1); +} + +int +hvn_rndis_ctloutput(struct hvn_softc *sc, struct rndis_cmd *rc, int timo) +{ + struct nvsp_send_rndis_pkt *msg; + struct hv_page_buffer pb[2]; + int tries = 10; + int rv; + + KASSERT(timo > 0); + + rc->rc_nvsp.msg_type = nvsp_type_send_rndis_pkt; + msg = (struct nvsp_send_rndis_pkt *)&rc->rc_nvsp.msg; + msg->chan_type = 1; /* control */ + msg->send_buf_section_idx = NVSP_INVALID_SECTION_INDEX; + + pb[0].pfn = rc->rc_pfn; + pb[0].length = rc->rc_req->msg_len; + pb[0].offset = 0; + + hvn_submit_cmd(sc, rc); + + do { + rv = hv_channel_sendbuf(sc->sc_chan, pb, 1, &rc->rc_nvsp, + sizeof(struct nvsp), rc->rc_id); + if (rv == EAGAIN) + tsleep(rc, PRIBIO, "hvnsendbuf", timo / 10); + else if (rv) { + DPRINTF("%s: RNDIS operation %d send error %d\n", + sc->sc_dev.dv_xname, rc->rc_req->msg_type, rv); + return (rv); + } + } while (rv != 0 && --tries > 0); + + bus_dmamap_sync(sc->sc_dmat, rc->rc_dmap, 0, PAGE_SIZE, + BUS_DMASYNC_POSTWRITE); + + mtx_enter(&rc->rc_mtx); + rv = msleep(rc, &rc->rc_mtx, PRIBIO, "rndisctl", timo); + mtx_leave(&rc->rc_mtx); + + bus_dmamap_sync(sc->sc_dmat, rc->rc_dmap, 0, PAGE_SIZE, + BUS_DMASYNC_POSTREAD); + +#ifdef HVN_DEBUG + switch (rv) { + case EINTR: + rv = 0; + break; + case EWOULDBLOCK: + if (hvn_rollback_cmd(sc, rc)) { + /* failed to rollback? go for one sleep cycle */ + tsleep(rc, PRIBIO, "rndisctl2", 1); + rv = 0; + break; + } + printf("%s: RNDIS opertaion %d timed out\n", sc->sc_dev.dv_xname, + rc->rc_req->msg_type); + } +#endif + return (rv); +} + +void +hvn_rndis_filter(struct hvn_softc *sc, uint64_t tid, void *arg) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + struct mbuf_list ml = MBUF_LIST_INITIALIZER(); + struct nvsp pkt; + struct hv_transfer_page_header *hdr = arg; + struct nvsp_send_rndis_pkt_comp *cmp; + uint32_t off, len, type, status = 0; + int i; + + if (hdr->set_id != HVN_RX_BUFID) { + DPRINTF("%s: transfer page invalid set id %#x\n", + sc->sc_dev.dv_xname, hdr->set_id); + return; + } + if (sc->sc_rx_ring == NULL) { + DPRINTF("%s: invalid rx ring\n", sc->sc_dev.dv_xname); + return; + } + for (i = 0; i < hdr->range_count; i++) { + off = hdr->range[i].byte_offset; + len = hdr->range[i].byte_count; + + KASSERT(off + len <= sc->sc_rx_size); + KASSERT(len >= RNDIS_HEADER_SIZE + 4); + + memcpy(&type, (caddr_t)sc->sc_rx_ring + off, sizeof(type)); + switch (type) { + /* data message */ + case RNDIS_PACKET_MSG: + hvn_rndis_input(sc, (caddr_t)sc->sc_rx_ring + + off, len, &ml); + break; + /* completion messages */ + case RNDIS_INITIALIZE_CMPLT: + case RNDIS_QUERY_CMPLT: + case RNDIS_SET_CMPLT: + case RNDIS_RESET_CMPLT: + case RNDIS_KEEPALIVE_CMPLT: + hvn_rndis_complete(sc, (caddr_t)sc->sc_rx_ring + + off, len); + break; + /* notification message */ + case RNDIS_INDICATE_STATUS_MSG: + hvn_rndis_status(sc, (caddr_t)sc->sc_rx_ring + + off, len); + break; + default: + printf("%s: unhandled RNDIS message type %u\n", + sc->sc_dev.dv_xname, type); + } + } + memset(&pkt, 0, sizeof(pkt)); + pkt.msg_type = nvsp_type_send_rndis_pkt_comp; + cmp = (struct nvsp_send_rndis_pkt_comp *)&pkt.msg; + cmp->status = status; /* XXX */ + hvn_nvsp_ack(sc, &pkt, tid); + + if (MBUF_LIST_FIRST(&ml)) + if_input(ifp, &ml); +} + +static inline struct mbuf * +hvn_devget(struct hvn_softc *sc, caddr_t buf, uint32_t len) +{ + struct mbuf *m; + + if (len + ETHER_ALIGN <= MHLEN) + MGETHDR(m, M_NOWAIT, MT_DATA); + else + m = MCLGETI(NULL, M_NOWAIT, NULL, len + ETHER_ALIGN); + if (m == NULL) + return (NULL); + m->m_len = m->m_pkthdr.len = len; + m_adj(m, ETHER_ALIGN); + + if (m_copyback(m, 0, len, buf, M_NOWAIT)) { + m_freem(m); + return (NULL); + } + + return (m); +} + +void +hvn_rndis_input(struct hvn_softc *sc, caddr_t buf, uint32_t len, + struct mbuf_list *ml) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + struct rndis_pkt *pkt; + struct rndis_pkt_info *ppi; + struct rndis_tcp_ip_csum_info *csum; + struct ndis_8021q_info *vlan; + struct mbuf *m; + + if (!(ifp->if_flags & IFF_RUNNING)) + return; + + if (len < RNDIS_HEADER_SIZE + sizeof(*pkt)) { + printf("%s: data packet too short: %u\n", + sc->sc_dev.dv_xname, len); + return; + } + + pkt = (struct rndis_pkt *)(buf + RNDIS_HEADER_SIZE); + + if (pkt->data_offset + pkt->data_length > len) { + printf("%s: data packet out of bounds: %u@%u\n", + sc->sc_dev.dv_xname, pkt->data_offset, pkt->data_length); + return; + } + + if ((m = hvn_devget(sc, buf + RNDIS_HEADER_SIZE + pkt->data_offset, + pkt->data_length)) == NULL) { + ifp->if_ierrors++; + return; + } + + while (pkt->pkt_info_length > 0) { + if (pkt->pkt_info_offset + pkt->pkt_info_length > len) { + printf("%s: PPI out of bounds: %u@%u\n", + sc->sc_dev.dv_xname, pkt->pkt_info_length, + pkt->pkt_info_offset); + break; + } + ppi = (struct rndis_pkt_info *)((caddr_t)pkt + + pkt->pkt_info_offset); + if (ppi->size > pkt->pkt_info_length) { + printf("%s: invalid PPI size: %u/%u\n", + sc->sc_dev.dv_xname, ppi->size, + pkt->pkt_info_length); + break; + } + switch (ppi->type) { + case tcpip_chksum_info: + csum = (struct rndis_tcp_ip_csum_info *) + ((caddr_t)ppi + ppi->size); + if (csum->recv.ip_csum_succeeded) + m->m_pkthdr.csum_flags |= M_IPV4_CSUM_IN_OK; + if (csum->recv.tcp_csum_succeeded) + m->m_pkthdr.csum_flags |= M_TCP_CSUM_IN_OK; + if (csum->recv.udp_csum_succeeded) + m->m_pkthdr.csum_flags |= M_UDP_CSUM_IN_OK; + break; + case ieee_8021q_info: + vlan = (struct ndis_8021q_info *) + ((caddr_t)ppi + ppi->size); +#if NVLAN > 0 + m->m_pkthdr.ether_vtag = vlan->vlan_id; + m->m_flags |= M_VLANTAG; +#endif + break; + default: + DPRINTF("%s: unhandled PPI %u\n", sc->sc_dev.dv_xname, + ppi->type); + } + pkt->pkt_info_length -= ppi->size; + } + + ml_enqueue(ml, m); +} + +void +hvn_rndis_complete(struct hvn_softc *sc, caddr_t buf, uint32_t len) +{ + struct rndis_cmd *rc; + uint32_t id; + + memcpy(&id, buf + RNDIS_HEADER_SIZE, sizeof(id)); + if ((rc = hvn_complete_cmd(sc, id)) != NULL) { + if (len < rc->rc_cmplen) + printf("%s: RNDIS response %u too short: %u\n", + sc->sc_dev.dv_xname, id, len); + else + memcpy(&rc->rc_cmp, buf, rc->rc_cmplen); + if (len > rc->rc_cmplen && + len - rc->rc_cmplen > HVN_RNDIS_CMPBUFSZ) + printf("%s: RNDIS response %u too large: %u\n", + sc->sc_dev.dv_xname, id, len); + else if (len > rc->rc_cmplen) + memcpy(&rc->rc_cmpbuf, buf + rc->rc_cmplen, + len - rc->rc_cmplen); + wakeup_one(rc); + } else + DPRINTF("%s: failed to complete RNDIS request id %u\n", + sc->sc_dev.dv_xname, id); +} + +void +hvn_rndis_status(struct hvn_softc *sc, caddr_t buf, uint32_t len) +{ + uint32_t sta; + + memcpy(&sta, buf + RNDIS_HEADER_SIZE, sizeof(sta)); + switch (sta) { + case RNDIS_STATUS_MEDIA_CONNECT: + sc->sc_link_state = LINK_STATE_UP; + break; + case RNDIS_STATUS_MEDIA_DISCONNECT: + sc->sc_link_state = LINK_STATE_DOWN; + break; + /* Ignore these */ + case RNDIS_STATUS_OFFLOAD_CURRENT_CONFIG: + break; + default: + DPRINTF("%s: unhandled status %#x\n", sc->sc_dev.dv_xname, sta); + return; + } + KERNEL_LOCK(); + hvn_link_status(sc); + KERNEL_UNLOCK(); +} + +int +hvn_rndis_query(struct hvn_softc *sc, uint32_t oid, void *res, size_t *length) +{ + struct rndis_cmd *rc; + struct rndis_query_req *req; + struct rndis_query_comp *cmp; + size_t olength = *length; + int rv; + + rc = hvn_alloc_cmd(sc); + + bus_dmamap_sync(sc->sc_dmat, rc->rc_dmap, 0, PAGE_SIZE, + BUS_DMASYNC_PREREAD); + + rc->rc_req->msg_type = RNDIS_QUERY_MSG; + rc->rc_req->msg_len = RNDIS_MESSAGE_SIZE(*req); + rc->rc_cmplen = RNDIS_MESSAGE_SIZE(*cmp); + rc->rc_id = atomic_inc_int_nv(&sc->sc_rndisrid); + req = (struct rndis_query_req *)&rc->rc_req->msg; + req->request_id = rc->rc_id; + req->oid = oid; + req->info_buffer_offset = sizeof(*req); + + bus_dmamap_sync(sc->sc_dmat, rc->rc_dmap, 0, PAGE_SIZE, + BUS_DMASYNC_PREWRITE); + + if ((rv = hvn_rndis_ctloutput(sc, rc, 500)) != 0) { + DPRINTF("%s: QUERY_MSG failed, error %d\n", + sc->sc_dev.dv_xname, rv); + hvn_free_cmd(sc, rc); + return (rv); + } + + cmp = (struct rndis_query_comp *)&rc->rc_cmp.msg; + switch (cmp->status) { + case RNDIS_STATUS_SUCCESS: + if (cmp->info_buffer_length > olength) { + rv = EINVAL; + break; + } + memcpy(res, rc->rc_cmpbuf, cmp->info_buffer_length); + *length = cmp->info_buffer_length; + break; + default: + *length = 0; + rv = EIO; + } + + hvn_free_cmd(sc, rc); + + return (rv); +} + +int +hvn_rndis_set(struct hvn_softc *sc, uint32_t oid, void *data, size_t length) +{ + struct rndis_cmd *rc; + struct rndis_set_req *req; + struct rndis_set_comp *cmp; + int rv; + + rc = hvn_alloc_cmd(sc); + + bus_dmamap_sync(sc->sc_dmat, rc->rc_dmap, 0, PAGE_SIZE, + BUS_DMASYNC_PREREAD); + + rc->rc_req->msg_type = RNDIS_SET_MSG; + rc->rc_req->msg_len = RNDIS_MESSAGE_SIZE(*req) + length; + rc->rc_cmplen = RNDIS_MESSAGE_SIZE(*cmp); + rc->rc_id = atomic_inc_int_nv(&sc->sc_rndisrid); + req = (struct rndis_set_req *)&rc->rc_req->msg; + memset(req, 0, sizeof(*req)); + req->request_id = rc->rc_id; + req->oid = oid; + req->info_buffer_offset = sizeof(*req); + + if (length > 0) { + KASSERT(sizeof(*req) + length < sizeof(struct rndis)); + req->info_buffer_length = length; + memcpy((caddr_t)(req + 1), data, length); + } + + bus_dmamap_sync(sc->sc_dmat, rc->rc_dmap, 0, PAGE_SIZE, + BUS_DMASYNC_PREWRITE); + + if ((rv = hvn_rndis_ctloutput(sc, rc, 500)) != 0) { + DPRINTF("%s: SET_MSG failed, error %u\n", + sc->sc_dev.dv_xname, rv); + hvn_free_cmd(sc, rc); + return (rv); + } + + cmp = (struct rndis_set_comp *)&rc->rc_cmp.msg; + if (cmp->status != RNDIS_STATUS_SUCCESS) + rv = EIO; + + hvn_free_cmd(sc, rc); + + return (rv); +} + +int +hvn_rndis_open(struct hvn_softc *sc) +{ + uint32_t filter; + int rv; + + if (sc->sc_promisc) + filter = NDIS_PACKET_TYPE_PROMISCUOUS; + else + filter = NDIS_PACKET_TYPE_BROADCAST | + NDIS_PACKET_TYPE_ALL_MULTICAST | + NDIS_PACKET_TYPE_DIRECTED; + + rv = hvn_rndis_set(sc, RNDIS_OID_GEN_CURRENT_PACKET_FILTER, + &filter, sizeof(filter)); + if (rv) + DPRINTF("%s: failed to set RNDIS filter to %#x\n", + sc->sc_dev.dv_xname, filter); + return (rv); +} + +int +hvn_rndis_close(struct hvn_softc *sc) +{ + uint32_t filter = 0; + int rv; + + rv = hvn_rndis_set(sc, RNDIS_OID_GEN_CURRENT_PACKET_FILTER, + &filter, sizeof(filter)); + if (rv) + DPRINTF("%s: failed to clear RNDIS filter\n", + sc->sc_dev.dv_xname); + return (rv); +} + +void +hvn_rndis_detach(struct hvn_softc *sc) +{ + struct rndis_cmd *rc; + struct rndis_halt_req *req; + int rv; + + rc = hvn_alloc_cmd(sc); + + bus_dmamap_sync(sc->sc_dmat, rc->rc_dmap, 0, PAGE_SIZE, + BUS_DMASYNC_PREREAD); + + rc->rc_req->msg_type = RNDIS_HALT_MSG; + rc->rc_req->msg_len = RNDIS_MESSAGE_SIZE(*req); + rc->rc_id = atomic_inc_int_nv(&sc->sc_rndisrid); + req = (struct rndis_halt_req *)&rc->rc_req->msg; + req->request_id = rc->rc_id; + + bus_dmamap_sync(sc->sc_dmat, rc->rc_dmap, 0, PAGE_SIZE, + BUS_DMASYNC_PREWRITE); + + if ((rv = hvn_rndis_ctloutput(sc, rc, 500)) != 0) + DPRINTF("%s: HALT_MSG failed, error %u\n", + sc->sc_dev.dv_xname, rv); + + hvn_free_cmd(sc, rc); +} diff --git a/sys/dev/pv/if_hvnreg.h b/sys/dev/pv/if_hvnreg.h new file mode 100644 index 00000000000..af57d44b44e --- /dev/null +++ b/sys/dev/pv/if_hvnreg.h @@ -0,0 +1,830 @@ +/*- + * Copyright (c) 2009-2012 Microsoft Corp. + * Copyright (c) 2010-2012 Citrix Inc. + * 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 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. + */ + +#ifndef _IF_HVNREG_H_ +#define _IF_HVNREG_H_ + +#define NVSP_PROTOCOL_VERSION_1 0x00002 +#define NVSP_PROTOCOL_VERSION_2 0x30002 +#define NVSP_PROTOCOL_VERSION_4 0x40000 +#define NVSP_PROTOCOL_VERSION_5 0x50000 + +#define VERSION_4_OFFLOAD_SIZE 22 + +#define NVSP_OPERATIONAL_STATUS_OK 0x00000000 +#define NVSP_OPERATIONAL_STATUS_DEGRADED 0x00000001 +#define NVSP_OPERATIONAL_STATUS_NONRECOVERABLE 0x00000002 +#define NVSP_OPERATIONAL_STATUS_NO_CONTACT 0x00000003 +#define NVSP_OPERATIONAL_STATUS_LOST_COMM 0x00000004 + +/* + * Maximun number of transfer pages (packets) the VSP will use on a receive + */ +#define NVSP_MAX_PACKETS_PER_RECEIVE 375 + +enum nvsp_type { + nvsp_type_none = 0, + + /* + * Init Messages + */ + nvsp_type_init = 1, + nvsp_type_init_comp = 2, + + nvsp_type_version_start = 100, + + /* + * Version 1 Messages + */ + nvsp_type_send_ndis_vers = nvsp_type_version_start, + + nvsp_type_send_rx_buf, + nvsp_type_send_rx_buf_comp, + nvsp_type_revoke_rx_buf, + + nvsp_type_send_tx_buf, + nvsp_type_send_tx_buf_comp, + nvsp_type_revoke_tx_buf, + + nvsp_type_send_rndis_pkt, + nvsp_type_send_rndis_pkt_comp, + + /* + * Version 2 Messages + */ + nvsp_type_send_chimney_delegated_buf, + nvsp_type_send_chimney_delegated_buf_comp, + nvsp_type_revoke_chimney_delegated_buf, + + nvsp_type_resume_chimney_rx_indication, + + nvsp_type_terminate_chimney, + nvsp_type_terminate_chimney_comp, + + nvsp_type_indicate_chimney_event, + + nvsp_type_send_chimney_packet, + nvsp_type_send_chimney_packet_comp, + + nvsp_type_post_chimney_rx_req, + nvsp_type_post_chimney_rx_req_comp, + + nvsp_type_alloc_rx_buf, + nvsp_type_alloc_rx_buf_comp, + + nvsp_type_free_rx_buf, + + nvsp_type_send_vmq_rndis_pkt, + nvsp_type_send_vmq_rndis_pkt_comp, + + nvsp_type_send_ndis_config, + + nvsp_type_alloc_chimney_handle, + nvsp_type_alloc_chimney_handle_comp, + + /* + * Version 4 Messages + */ + nvsp_type_send_vf_association, + nvsp_type_switch_data_path, + nvsp_type_uplink_connect_state_deprecated, + + /* + * Version 5 Messages + */ + nvsp_type_oid_query_ex, + nvsp_type_oid_query_ex_comp, + nvsp_type_subchannel, + nvsp_type_send_indirection_table, +}; + +enum nvsp_status { + nvsp_status_none = 0, + nvsp_status_success, + nvsp_status_failure, + /* Deprecated */ + nvsp_status_prot_vers_range_too_new, + /* Deprecated */ + nvsp_status_prot_vers_range_too_old, + nvsp_status_invalid_rndis_pkt, + nvsp_status_busy, + nvsp_status_max, +}; + +/* + * Init Messages + */ + +/* + * This message is used by the VSC to initialize the channel + * after the channels has been opened. This message should + * never include anything other then versioning (i.e. this + * message will be the same for ever). + * + * Forever is a long time. The values have been redefined + * in Win7 to indicate major and minor protocol version + * number. + */ +struct nvsp_init { + uint32_t protocol_version; + uint32_t protocol_version_2; +} __packed; + +/* + * This message is used by the VSP to complete the initialization + * of the channel. This message should never include anything other + * then versioning (i.e. this message will be the same forever). + */ +struct nvsp_init_comp { + /* Deprecated */ + uint32_t negotiated_prot_vers; + uint32_t max_mdl_chain_len; + uint32_t status; +} __packed; + +/* + * Version 1 Messages + */ + +/* + * This message is used by the VSC to send the NDIS version + * to the VSP. The VSP can use this information when handling + * OIDs sent by the VSC. + */ +struct nvsp_send_ndis_version { + uint32_t ndis_major_vers; + /* Deprecated */ + uint32_t ndis_minor_vers; +} __packed; + +/* + * This message is used by the VSC to send a receive buffer + * to the VSP. The VSP can then use the receive buffer to + * send data to the VSC. + */ +struct nvsp_send_rx_buf { + uint32_t gpadl_handle; + uint16_t id; +} __packed; + +struct nvsp_rx_buf_section { + uint32_t offset; + uint32_t sub_allocation_size; + uint32_t num_sub_allocations; + uint32_t end_offset; +} __packed; + +/* + * This message is used by the VSP to acknowledge a receive + * buffer send by the VSC. This message must be sent by the + * VSP before the VSP uses the receive buffer. + */ +struct nvsp_send_rx_buf_comp { + uint32_t status; + uint32_t num_sections; + + /* + * The receive buffer is split into two parts, a large + * suballocation section and a small suballocation + * section. These sections are then suballocated by a + * certain size. + * + * For example, the following break up of the receive + * buffer has 6 large suballocations and 10 small + * suballocations. + * + * | Large Section | | Small Section | + * ------------------------------------------------------------ + * | | | | | | | | | | | | | | | | | | + * | | + * LargeOffset SmallOffset + */ + struct nvsp_rx_buf_section sections[1]; +} __packed; + +/* + * This message is sent by the VSC to revoke the receive buffer. + * After the VSP completes this transaction, the VSP should never + * use the receive buffer again. + */ +struct nvsp_revoke_rx_buf { + uint16_t id; +} __packed; + +/* + * This message is used by the VSC to send a send buffer + * to the VSP. The VSC can then use the send buffer to + * send data to the VSP. + */ +struct nvsp_send_tx_buf { + uint32_t gpadl_handle; + uint16_t id; +} __packed; + +/* + * This message is used by the VSP to acknowledge a send + * buffer sent by the VSC. This message must be sent by the + * VSP before the VSP uses the sent buffer. + */ +struct nvsp_send_tx_buf_comp { + uint32_t status; + + /* + * The VSC gets to choose the size of the send buffer and + * the VSP gets to choose the sections size of the buffer. + * This was done to enable dynamic reconfigurations when + * the cost of GPA-direct buffers decreases. + */ + uint32_t section_size; +} __packed; + +/* + * This message is sent by the VSC to revoke the send buffer. + * After the VSP completes this transaction, the vsp should never + * use the send buffer again. + */ +struct nvsp_revoke_tx_buf { + uint16_t id; +} __packed; + +/* + * This message is used by both the VSP and the VSC to send + * an RNDIS message to the opposite channel endpoint. + */ +struct nvsp_send_rndis_pkt { + /* + * This field is specified by RNIDS. They assume there's + * two different channels of communication. However, + * the Network VSP only has one. Therefore, the channel + * travels with the RNDIS packet. + */ + uint32_t chan_type; + + /* + * This field is used to send part or all of the data + * through a send buffer. This values specifies an + * index into the send buffer. If the index is + * 0xFFFFFFFF, then the send buffer is not being used + * and all of the data was sent through other VMBus + * mechanisms. + */ + uint32_t send_buf_section_idx; + uint32_t send_buf_section_size; +} __packed; + +/* + * This message is used by both the VSP and the VSC to complete + * a RNDIS message to the opposite channel endpoint. At this + * point, the initiator of this message cannot use any resources + * associated with the original RNDIS packet. + */ +struct nvsp_send_rndis_pkt_comp { + uint32_t status; +} __packed; + + +/* + * Version 2 Messages + */ + +/* + * This message is used by the VSC to send the NDIS version + * to the VSP. The VSP can use this information when handling + * OIDs sent by the VSC. + */ +struct nvsp_netvsc_capabilities { + union { + uint64_t as_uint64; + struct { + uint64_t vmq : 1; + uint64_t chimney : 1; + uint64_t sriov : 1; + uint64_t ieee8021q : 1; + uint64_t correlationid : 1; + uint64_t teaming : 1; + } ; + }; +} __packed; + +struct nvsp_send_ndis_config { + uint32_t mtu; + uint32_t reserved; + struct nvsp_netvsc_capabilities + capabilities; +} __packed; + +/* + * NvspMessage2TypeSendChimneyDelegatedBuffer + */ +struct nvsp_send_chimney_buf { + /* + * On WIN7 beta, delegated_obj_max_size is defined as a uint32_t + * Since WIN7 RC, it was split into two uint16_t. To have the same + * struct layout, delegated_obj_max_size shall be the first field. + */ + uint16_t delegated_obj_max_size; + + /* + * The revision # of chimney protocol used between NVSC and NVSP. + * + * This revision is NOT related to the chimney revision between + * NDIS protocol and miniport drivers. + */ + uint16_t revision; + + uint32_t gpadl_handle; +} __packed; + + +/* Unsupported chimney revision 0 (only present in WIN7 beta) */ +#define NVSP_CHIMNEY_REVISION_0 0 + +/* WIN7 Beta Chimney QFE */ +#define NVSP_CHIMNEY_REVISION_1 1 + +/* The chimney revision since WIN7 RC */ +#define NVSP_CHIMNEY_REVISION_2 2 + + +/* + * NvspMessage2TypeSendChimneyDelegatedBufferComplete + */ +struct nvsp_send_chimney_buf_comp { + uint32_t status; + + /* + * Maximum number outstanding sends and pre-posted receives. + * + * NVSC should not post more than SendQuota/ReceiveQuota packets. + * Otherwise, it can block the non-chimney path for an indefinite + * amount of time. + * (since chimney sends/receives are affected by the remote peer). + * + * Note: NVSP enforces the quota restrictions on a per-VMBCHANNEL + * basis. It doesn't enforce the restriction separately for chimney + * send/receive. If NVSC doesn't voluntarily enforce "SendQuota", + * it may kill its own network connectivity. + */ + uint32_t tx_quota; + uint32_t rx_quota; +} __packed; + +/* + * NvspMessage2TypeRevokeChimneyDelegatedBuffer + */ +struct nvsp_revoke_chimney_buf { + uint32_t gpadl_handle; +} __packed; + + +#define NVSP_CHIMNEY_OBJECT_TYPE_NEIGHBOR 0 +#define NVSP_CHIMNEY_OBJECT_TYPE_PATH4 1 +#define NVSP_CHIMNEY_OBJECT_TYPE_PATH6 2 +#define NVSP_CHIMNEY_OBJECT_TYPE_TCP 3 + +/* + * NvspMessage2TypeAllocateChimneyHandle + */ +struct nvsp_alloc_chimney_handle { + uint64_t vsc_context; + uint32_t object_type; +} __packed; + +/* + * NvspMessage2TypeAllocateChimneyHandleComplete + */ +struct nvsp_alloc_chimney_handle_comp { + uint32_t vsp_handle; +} __packed; + + +/* + * NvspMessage2TypeResumeChimneyRXIndication + */ +struct nvsp_resume_chimney_rx_indication { + /* + * Handle identifying the offloaded connection + */ + uint32_t vsp_tcp_handle; +} __packed; + +/* + * NvspMessage2TypeTerminateChimney + */ +struct nvsp_terminate_chimney { + /* + * Handle identifying the offloaded object + */ + uint32_t vsp_handle; + + /* + * Terminate Offload Flags + * Bit 0: + * When set to 0, terminate the offload at the destination NIC + * Bit 1-31: Reserved, shall be zero + */ + uint32_t flags; + + union { + /* + * This field is valid only when bit 0 of flags is clear. + * It specifies the index into the premapped delegated + * object buffer. The buffer was sent through the + * NvspMessage2TypeSendChimneyDelegatedBuffer + * message at initialization time. + * + * NVSP will write the delegated state into the delegated + * buffer upon upload completion. + */ + uint32_t index; + + /* + * This field is valid only when bit 0 of flags is set. + * + * The seqence number of the most recently accepted RX + * indication when VSC sets its TCP context into + * "terminating" state. + * + * This allows NVSP to determines if there are any in-flight + * RX indications for which the acceptance state is still + * undefined. + */ + uint64_t last_accepted_rx_seq_no; + }; +} __packed; + +/* + * NvspMessage2TypeTerminateChimneyComplete + */ +struct nvsp_terminate_chimney_comp { + uint64_t vsc_context; + uint32_t flags; +} __packed; + +/* + * NvspMessage2TypeIndicateChimneyEvent + */ +struct nvsp_indicate_chimney_event { + /* + * When VscTcpContext is 0, event_type is an NDIS_STATUS; + * Otherwise, EventType is an TCP connection event (defined + * in NdisTcpOffloadEventHandler chimney DDK document). + */ + uint32_t event_type; + + /* + * When VscTcpContext is 0, EventType is an NDIS_STATUS; + * Otherwise, EventType is an TCP connection event specific + * information (defined in NdisTcpOffloadEventHandler + * chimney DDK document). + */ + uint32_t event_specific_info; + + /* + * If not 0, the event is per-TCP connection event. This field + * contains the VSC's TCP context. If 0, the event indication is + * global. + */ + uint64_t vsc_tcp_context; +} __packed; + + +#define NVSP_INVALID_OOB_INDEX 0xffffu +#define NVSP_INVALID_SECTION_INDEX 0xffffffff + +/* + * NvspMessage2TypeSendChimneyPacket + */ +struct nvsp_send_chimney_pkt { + /* + * Identify the TCP connection for which this chimney send is + */ + uint32_t vsp_tcp_handle; + + /* + * This field is used to send part or all of the data + * through a send buffer. This values specifies an + * index into the send buffer. If the index is + * 0xFFFF, then the send buffer is not being used + * and all of the data was sent through other VMBus + * mechanisms. + */ + uint16_t send_buf_section_index; + uint16_t send_buf_section_size; + + /* + * OOB Data Index + * This an index to the OOB data buffer. If the index is 0xFFFFFFFF, + * then there is no OOB data. + * + * This field shall be always 0xFFFFFFFF for now. It is reserved for + * the future. + */ + uint16_t oob_data_index; + + /* + * DisconnectFlags = 0 + * Normal chimney send. See MiniportTcpOffloadSend for details. + * + * DisconnectFlags = TCP_DISCONNECT_GRACEFUL_CLOSE (0x01) + * Graceful disconnect. See MiniportTcpOffloadDisconnect for details. + * + * DisconnectFlags = TCP_DISCONNECT_ABORTIVE_CLOSE (0x02) + * Abortive disconnect. See MiniportTcpOffloadDisconnect for details. + */ + uint16_t disconnect_flags; + + uint32_t seq_no; +} __packed; + +/* + * NvspMessage2TypeSendChimneyPacketComplete + */ +struct nvsp_send_chimney_pkt_comp { + /* + * The NDIS_STATUS for the chimney send + */ + uint32_t status; + + /* + * Number of bytes that have been sent to the peer (and ACKed by the peer). + */ + uint32_t bytes_transferred; +} __packed; + +/* + * NvspMessage2TypePostChimneyRecvRequest + */ +struct nvsp_post_chimney_rx_req { + /* + * Identify the TCP connection which this chimney receive request + * is for. + */ + uint32_t vsp_tcp_handle; + + /* + * OOB Data Index + * This an index to the OOB data buffer. If the index is 0xFFFFFFFF, + * then there is no OOB data. + * + * This field shall be always 0xFFFFFFFF for now. It is reserved for + * the future. + */ + uint32_t oob_data_index; + + /* + * Bit 0 + * When it is set, this is a "no-push" receive. + * When it is clear, this is a "push" receive. + * + * Bit 1-15: Reserved and shall be zero + */ + uint16_t flags; + + /* + * For debugging and diagnoses purpose. + * The SeqNo is per TCP connection and starts from 0. + */ + uint32_t seq_no; +} __packed; + +/* + * NvspMessage2TypePostChimneyRecvRequestComplete + */ +struct nvsp_post_chimney_rx_req_comp { + /* + * The NDIS_STATUS for the chimney send + */ + uint32_t status; + + /* + * Number of bytes that have been sent to the peer (and ACKed by + * the peer). + */ + uint32_t bytes_xferred; +} __packed; + +/* + * NvspMessage2TypeAllocateReceiveBuffer + */ +struct nvsp_alloc_rx_buf { + /* + * Allocation ID to match the allocation request and response + */ + uint32_t allocation_id; + + /* + * Length of the VM shared memory receive buffer that needs to + * be allocated + */ + uint32_t length; +} __packed; + +/* + * NvspMessage2TypeAllocateReceiveBufferComplete + */ +struct nvsp_alloc_rx_buf_comp { + /* + * The NDIS_STATUS code for buffer allocation + */ + uint32_t status; + + /* + * Allocation ID from NVSP_MESSAGE_ALLOCATE_RECEIVE_BUFFER + */ + uint32_t allocation_id; + + /* + * GPADL handle for the allocated receive buffer + */ + uint32_t gpadl_handle; + + /* + * Receive buffer ID that is further used in + * NvspMessage2SendVmqRndisPacket + */ + uint64_t rx_buf_id; +} __packed; + +/* + * NvspMessage2TypeFreeReceiveBuffer + */ +struct nvsp_free_rx_buf { + /* + * Receive buffer ID previous returned in + * NvspMessage2TypeAllocateReceiveBufferComplete message + */ + uint64_t rx_buf_id; +} __packed; + +/* + * This structure is used in defining the buffers in + * NVSP_MESSAGE_SEND_VMQ_RNDIS_PACKET structure + */ +struct nvsp_xfer_page_range { + /* + * Specifies the ID of the receive buffer that has the buffer. This + * ID can be the general receive buffer ID specified in + * NvspMessage1TypeSendReceiveBuffer or it can be the shared memory + * receive buffer ID allocated by the VSC and specified in + * NvspMessage2TypeAllocateReceiveBufferComplete message + */ + uint64_t xfer_page_set_id; + + /* + * Number of bytes + */ + uint32_t byte_count; + + /* + * Offset in bytes from the beginning of the buffer + */ + uint32_t byte_offset; +} __packed; + +/* + * NvspMessage2SendVmqRndisPacket + */ +struct nvsp_send_vmq_rndis_pkt { + /* + * This field is specified by RNIDS. They assume there's + * two different channels of communication. However, + * the Network VSP only has one. Therefore, the channel + * travels with the RNDIS packet. It must be RMC_DATA + */ + uint32_t channel_type; + + /* + * Only the Range element corresponding to the RNDIS header of + * the first RNDIS message in the multiple RNDIS messages sent + * in one NVSP message. Information about the data portions as well + * as the subsequent RNDIS messages in the same NVSP message are + * embedded in the RNDIS header itself + */ + struct nvsp_xfer_page_range range; +} __packed; + +/* + * This message is used by the VSC to complete + * a RNDIS VMQ message to the VSP. At this point, + * the initiator of this message can use any resources + * associated with the original RNDIS VMQ packet. + */ +struct nvsp_send_vmq_rndis_pkt_comp { + uint32_t status; +} __packed; + +/* + * Version 5 messages + */ +enum nvsp_subchannel_operation { + NVSP_SUBCHANNEL_NONE = 0, + NVSP_SUBCHANNE_ALLOCATE, + NVSP_SUBCHANNE_MAX +}; + +struct nvsp_subchannel_req { + uint32_t op; + uint32_t num_subchannels; +} __packed; + +struct nvsp_subchannel_comp { + uint32_t status; + /* Actual number of subchannels allocated */ + uint32_t num_subchannels; +} __packed; + +struct nvsp_send_indirect_table { + /* The number of entries in the send indirection table */ + uint32_t count; + /* + * The offset of the send indireciton table from top of + * this struct. The send indirection table tells which channel + * to put the send traffic on. Each entry is a channel number. + */ + uint32_t offset; +} __packed; + +/* + * All messages + */ +struct nvsp { + uint32_t msg_type; + + union { + struct nvsp_init init; + struct nvsp_init_comp init_compl; + + /* Version 1 */ + struct nvsp_send_ndis_version send_ndis_vers; + + struct nvsp_send_rx_buf send_rx_buf; + struct nvsp_send_rx_buf_comp send_rx_buf_comp; + struct nvsp_revoke_rx_buf revoke_rx_buf; + + struct nvsp_send_tx_buf send_tx_buf; + struct nvsp_send_tx_buf_comp send_tx_buf_comp; + struct nvsp_revoke_tx_buf revoke_tx_buf; + + struct nvsp_send_rndis_pkt send_rndis_pkt; + struct nvsp_send_rndis_pkt_comp send_rndis_pkt_comp; + + /* Version 2 */ + struct nvsp_send_ndis_config send_ndis_config; + + struct nvsp_send_chimney_buf send_chimney_buf; + struct nvsp_send_chimney_buf_comp send_chimney_buf_comp; + struct nvsp_revoke_chimney_buf revoke_chimney_buf; + + struct nvsp_resume_chimney_rx_indication resume_chimney_rx_indication; + struct nvsp_terminate_chimney terminate_chimney; + struct nvsp_terminate_chimney_comp terminate_chimney_comp; + struct nvsp_indicate_chimney_event indicate_chimney_event; + + struct nvsp_send_chimney_pkt send_chimney_packet; + struct nvsp_send_chimney_pkt_comp send_chimney_packet_comp; + struct nvsp_post_chimney_rx_req post_chimney_rx_req; + struct nvsp_post_chimney_rx_req_comp post_chimney_rx_req_comp; + + struct nvsp_alloc_rx_buf alloc_rx_buffer; + struct nvsp_alloc_rx_buf_comp alloc_rx_buffer_comp; + struct nvsp_free_rx_buf free_rx_buffer; + + struct nvsp_send_vmq_rndis_pkt send_vmq_rndis_pkt; + struct nvsp_send_vmq_rndis_pkt_comp send_vmq_rndis_pkt_comp; + struct nvsp_alloc_chimney_handle alloc_chimney_handle; + struct nvsp_alloc_chimney_handle_comp alloc_chimney_handle_comp; + + /* Version 5 */ + struct nvsp_subchannel_req subchannel_req; + struct nvsp_subchannel_comp subchn_comp; + struct nvsp_send_indirect_table send_table; + } msg; +} __packed; + +#endif /* _IF_HVNREG_H_ */ diff --git a/sys/dev/pv/rndisreg.h b/sys/dev/pv/rndisreg.h new file mode 100644 index 00000000000..48cc645158a --- /dev/null +++ b/sys/dev/pv/rndisreg.h @@ -0,0 +1,974 @@ +/*- + * Copyright (c) 2009-2012 Microsoft Corp. + * Copyright (c) 2010-2012 Citrix Inc. + * 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 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. + */ + +#ifndef _RNDISREG_H_ +#define _RNDISREG_H_ + +/* + * NDIS protocol version numbers + */ +#define NDIS_VERSION_5_0 0x00050000 +#define NDIS_VERSION_5_1 0x00050001 +#define NDIS_VERSION_6_0 0x00060000 +#define NDIS_VERSION_6_1 0x00060001 +#define NDIS_VERSION_6_30 0x0006001e + +/* + * Status codes + */ + +#define STATUS_SUCCESS 0x00000000 +#define STATUS_UNSUCCESSFUL 0xC0000001 +#define STATUS_PENDING 0x00000103 +#define STATUS_INSUFFICIENT_RESOURCES 0xC000009A +#define STATUS_BUFFER_OVERFLOW 0x80000005 +#define STATUS_NOT_SUPPORTED 0xC00000BB + +#define RNDIS_STATUS_SUCCESS 0x00000000 +#define RNDIS_STATUS_PENDING 0x00000103 +#define RNDIS_STATUS_NOT_RECOGNIZED 0x00010001 +#define RNDIS_STATUS_NOT_COPIED 0x00010002 +#define RNDIS_STATUS_NOT_ACCEPTED 0x00010003 +#define RNDIS_STATUS_CALL_ACTIVE 0x00010007 + +#define RNDIS_STATUS_ONLINE 0x40010003 +#define RNDIS_STATUS_RESET_START 0x40010004 +#define RNDIS_STATUS_RESET_END 0x40010005 +#define RNDIS_STATUS_RING_STATUS 0x40010006 +#define RNDIS_STATUS_CLOSED 0x40010007 +#define RNDIS_STATUS_WAN_LINE_UP 0x40010008 +#define RNDIS_STATUS_WAN_LINE_DOWN 0x40010009 +#define RNDIS_STATUS_WAN_FRAGMENT 0x4001000A +#define RNDIS_STATUS_MEDIA_CONNECT 0x4001000B +#define RNDIS_STATUS_MEDIA_DISCONNECT 0x4001000C +#define RNDIS_STATUS_HARDWARE_LINE_UP 0x4001000D +#define RNDIS_STATUS_HARDWARE_LINE_DOWN 0x4001000E +#define RNDIS_STATUS_INTERFACE_UP 0x4001000F +#define RNDIS_STATUS_INTERFACE_DOWN 0x40010010 +#define RNDIS_STATUS_MEDIA_BUSY 0x40010011 +#define RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION 0x40010012 +#define RNDIS_STATUS_WW_INDICATION 0x40010012 +#define RNDIS_STATUS_LINK_SPEED_CHANGE 0x40010013 + +#define RNDIS_STATUS_OFFLOAD_CURRENT_CONFIG 0x40020006 + +#define RNDIS_STATUS_NOT_RESETTABLE 0x80010001 +#define RNDIS_STATUS_SOFT_ERRORS 0x80010003 +#define RNDIS_STATUS_HARD_ERRORS 0x80010004 +#define RNDIS_STATUS_BUFFER_OVERFLOW 0x80000005 + +#define RNDIS_STATUS_FAILURE 0xC0000001 +#define RNDIS_STATUS_RESOURCES 0xC000009A +#define RNDIS_STATUS_CLOSING 0xC0010002 +#define RNDIS_STATUS_BAD_VERSION 0xC0010004 +#define RNDIS_STATUS_BAD_CHARACTERISTICS 0xC0010005 +#define RNDIS_STATUS_ADAPTER_NOT_FOUND 0xC0010006 +#define RNDIS_STATUS_OPEN_FAILED 0xC0010007 +#define RNDIS_STATUS_DEVICE_FAILED 0xC0010008 +#define RNDIS_STATUS_MULTICAST_FULL 0xC0010009 +#define RNDIS_STATUS_MULTICAST_EXISTS 0xC001000A +#define RNDIS_STATUS_MULTICAST_NOT_FOUND 0xC001000B +#define RNDIS_STATUS_REQUEST_ABORTED 0xC001000C +#define RNDIS_STATUS_RESET_IN_PROGRESS 0xC001000D +#define RNDIS_STATUS_CLOSING_INDICATING 0xC001000E +#define RNDIS_STATUS_NOT_SUPPORTED 0xC00000BB +#define RNDIS_STATUS_INVALID_PACKET 0xC001000F +#define RNDIS_STATUS_OPEN_LIST_FULL 0xC0010010 +#define RNDIS_STATUS_ADAPTER_NOT_READY 0xC0010011 +#define RNDIS_STATUS_ADAPTER_NOT_OPEN 0xC0010012 +#define RNDIS_STATUS_NOT_INDICATING 0xC0010013 +#define RNDIS_STATUS_INVALID_LENGTH 0xC0010014 +#define RNDIS_STATUS_INVALID_DATA 0xC0010015 +#define RNDIS_STATUS_BUFFER_TOO_SHORT 0xC0010016 +#define RNDIS_STATUS_INVALID_OID 0xC0010017 +#define RNDIS_STATUS_ADAPTER_REMOVED 0xC0010018 +#define RNDIS_STATUS_UNSUPPORTED_MEDIA 0xC0010019 +#define RNDIS_STATUS_GROUP_ADDRESS_IN_USE 0xC001001A +#define RNDIS_STATUS_FILE_NOT_FOUND 0xC001001B +#define RNDIS_STATUS_ERROR_READING_FILE 0xC001001C +#define RNDIS_STATUS_ALREADY_MAPPED 0xC001001D +#define RNDIS_STATUS_RESOURCE_CONFLICT 0xC001001E +#define RNDIS_STATUS_NO_CABLE 0xC001001F + +#define RNDIS_STATUS_INVALID_SAP 0xC0010020 +#define RNDIS_STATUS_SAP_IN_USE 0xC0010021 +#define RNDIS_STATUS_INVALID_ADDRESS 0xC0010022 +#define RNDIS_STATUS_VC_NOT_ACTIVATED 0xC0010023 +#define RNDIS_STATUS_DEST_OUT_OF_ORDER 0xC0010024 +#define RNDIS_STATUS_VC_NOT_AVAILABLE 0xC0010025 +#define RNDIS_STATUS_CELLRATE_NOT_AVAILABLE 0xC0010026 +#define RNDIS_STATUS_INCOMPATABLE_QOS 0xC0010027 +#define RNDIS_STATUS_AAL_PARAMS_UNSUPPORTED 0xC0010028 +#define RNDIS_STATUS_NO_ROUTE_TO_DESTINATION 0xC0010029 + +#define RNDIS_STATUS_TOKEN_RING_OPEN_ERROR 0xC0011000 + + +/* + * Object Identifiers used by NdisRequest Query/Set Information + */ + +/* + * General Objects + */ + +#define RNDIS_OID_GEN_SUPPORTED_LIST 0x00010101 +#define RNDIS_OID_GEN_HARDWARE_STATUS 0x00010102 +#define RNDIS_OID_GEN_MEDIA_SUPPORTED 0x00010103 +#define RNDIS_OID_GEN_MEDIA_IN_USE 0x00010104 +#define RNDIS_OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105 +#define RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106 +#define RNDIS_OID_GEN_LINK_SPEED 0x00010107 +#define RNDIS_OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108 +#define RNDIS_OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109 +#define RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A +#define RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B +#define RNDIS_OID_GEN_VENDOR_ID 0x0001010C +#define RNDIS_OID_GEN_VENDOR_DESCRIPTION 0x0001010D +#define RNDIS_OID_GEN_CURRENT_PACKET_FILTER 0x0001010E +#define RNDIS_OID_GEN_CURRENT_LOOKAHEAD 0x0001010F +#define RNDIS_OID_GEN_DRIVER_VERSION 0x00010110 +#define RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111 +#define RNDIS_OID_GEN_PROTOCOL_OPTIONS 0x00010112 +#define RNDIS_OID_GEN_MAC_OPTIONS 0x00010113 +#define RNDIS_OID_GEN_MEDIA_CONNECT_STATUS 0x00010114 +#define RNDIS_OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115 +#define RNDIS_OID_GEN_VENDOR_DRIVER_VERSION 0x00010116 +#define RNDIS_OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118 +#define RNDIS_OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119 +#define RNDIS_OID_GEN_MACHINE_NAME 0x0001021A +#define RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B + +#define RNDIS_OID_GEN_XMIT_OK 0x00020101 +#define RNDIS_OID_GEN_RCV_OK 0x00020102 +#define RNDIS_OID_GEN_XMIT_ERROR 0x00020103 +#define RNDIS_OID_GEN_RCV_ERROR 0x00020104 +#define RNDIS_OID_GEN_RCV_NO_BUFFER 0x00020105 + +#define RNDIS_OID_GEN_DIRECTED_BYTES_XMIT 0x00020201 +#define RNDIS_OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202 +#define RNDIS_OID_GEN_MULTICAST_BYTES_XMIT 0x00020203 +#define RNDIS_OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204 +#define RNDIS_OID_GEN_BROADCAST_BYTES_XMIT 0x00020205 +#define RNDIS_OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206 +#define RNDIS_OID_GEN_DIRECTED_BYTES_RCV 0x00020207 +#define RNDIS_OID_GEN_DIRECTED_FRAMES_RCV 0x00020208 +#define RNDIS_OID_GEN_MULTICAST_BYTES_RCV 0x00020209 +#define RNDIS_OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A +#define RNDIS_OID_GEN_BROADCAST_BYTES_RCV 0x0002020B +#define RNDIS_OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C + +#define RNDIS_OID_GEN_RCV_CRC_ERROR 0x0002020D +#define RNDIS_OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E + +#define RNDIS_OID_GEN_GET_TIME_CAPS 0x0002020F +#define RNDIS_OID_GEN_GET_NETCARD_TIME 0x00020210 + +/* + * These are connection-oriented general OIDs. + * These replace the above OIDs for connection-oriented media. + */ +#define RNDIS_OID_GEN_CO_SUPPORTED_LIST 0x00010101 +#define RNDIS_OID_GEN_CO_HARDWARE_STATUS 0x00010102 +#define RNDIS_OID_GEN_CO_MEDIA_SUPPORTED 0x00010103 +#define RNDIS_OID_GEN_CO_MEDIA_IN_USE 0x00010104 +#define RNDIS_OID_GEN_CO_LINK_SPEED 0x00010105 +#define RNDIS_OID_GEN_CO_VENDOR_ID 0x00010106 +#define RNDIS_OID_GEN_CO_VENDOR_DESCRIPTION 0x00010107 +#define RNDIS_OID_GEN_CO_DRIVER_VERSION 0x00010108 +#define RNDIS_OID_GEN_CO_PROTOCOL_OPTIONS 0x00010109 +#define RNDIS_OID_GEN_CO_MAC_OPTIONS 0x0001010A +#define RNDIS_OID_GEN_CO_MEDIA_CONNECT_STATUS 0x0001010B +#define RNDIS_OID_GEN_CO_VENDOR_DRIVER_VERSION 0x0001010C +#define RNDIS_OID_GEN_CO_MINIMUM_LINK_SPEED 0x0001010D + +#define RNDIS_OID_GEN_CO_GET_TIME_CAPS 0x00010201 +#define RNDIS_OID_GEN_CO_GET_NETCARD_TIME 0x00010202 + +/* + * These are connection-oriented statistics OIDs. + */ +#define RNDIS_OID_GEN_CO_XMIT_PDUS_OK 0x00020101 +#define RNDIS_OID_GEN_CO_RCV_PDUS_OK 0x00020102 +#define RNDIS_OID_GEN_CO_XMIT_PDUS_ERROR 0x00020103 +#define RNDIS_OID_GEN_CO_RCV_PDUS_ERROR 0x00020104 +#define RNDIS_OID_GEN_CO_RCV_PDUS_NO_BUFFER 0x00020105 + + +#define RNDIS_OID_GEN_CO_RCV_CRC_ERROR 0x00020201 +#define RNDIS_OID_GEN_CO_TRANSMIT_QUEUE_LENGTH 0x00020202 +#define RNDIS_OID_GEN_CO_BYTES_XMIT 0x00020203 +#define RNDIS_OID_GEN_CO_BYTES_RCV 0x00020204 +#define RNDIS_OID_GEN_CO_BYTES_XMIT_OUTSTANDING 0x00020205 +#define RNDIS_OID_GEN_CO_NETCARD_LOAD 0x00020206 + +/* + * These are objects for Connection-oriented media call-managers. + */ +#define RNDIS_OID_CO_ADD_PVC 0xFF000001 +#define RNDIS_OID_CO_DELETE_PVC 0xFF000002 +#define RNDIS_OID_CO_GET_CALL_INFORMATION 0xFF000003 +#define RNDIS_OID_CO_ADD_ADDRESS 0xFF000004 +#define RNDIS_OID_CO_DELETE_ADDRESS 0xFF000005 +#define RNDIS_OID_CO_GET_ADDRESSES 0xFF000006 +#define RNDIS_OID_CO_ADDRESS_CHANGE 0xFF000007 +#define RNDIS_OID_CO_SIGNALING_ENABLED 0xFF000008 +#define RNDIS_OID_CO_SIGNALING_DISABLED 0xFF000009 + + +/* + * 802.3 Objects (Ethernet) + */ + +#define RNDIS_OID_802_3_PERMANENT_ADDRESS 0x01010101 +#define RNDIS_OID_802_3_CURRENT_ADDRESS 0x01010102 +#define RNDIS_OID_802_3_MULTICAST_LIST 0x01010103 +#define RNDIS_OID_802_3_MAXIMUM_LIST_SIZE 0x01010104 +#define RNDIS_OID_802_3_MAC_OPTIONS 0x01010105 + +/* + * + */ +#define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001 + +#define RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101 +#define RNDIS_OID_802_3_XMIT_ONE_COLLISION 0x01020102 +#define RNDIS_OID_802_3_XMIT_MORE_COLLISIONS 0x01020103 + +#define RNDIS_OID_802_3_XMIT_DEFERRED 0x01020201 +#define RNDIS_OID_802_3_XMIT_MAX_COLLISIONS 0x01020202 +#define RNDIS_OID_802_3_RCV_OVERRUN 0x01020203 +#define RNDIS_OID_802_3_XMIT_UNDERRUN 0x01020204 +#define RNDIS_OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205 +#define RNDIS_OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206 +#define RNDIS_OID_802_3_XMIT_LATE_COLLISIONS 0x01020207 + + +/* + * RNDIS MP custom OID for test + */ +#define OID_RNDISMP_GET_RECEIVE_BUFFERS 0xFFA0C90D + + +/* + * Remote NDIS message types + */ +#define RNDIS_PACKET_MSG 0x00000001 +#define RNDIS_INITIALIZE_MSG 0x00000002 +#define RNDIS_HALT_MSG 0x00000003 +#define RNDIS_QUERY_MSG 0x00000004 +#define RNDIS_SET_MSG 0x00000005 +#define RNDIS_RESET_MSG 0x00000006 +#define RNDIS_INDICATE_STATUS_MSG 0x00000007 +#define RNDIS_KEEPALIVE_MSG 0x00000008 + +#define RCONDIS_MP_CREATE_VC_MSG 0x00008001 +#define RCONDIS_MP_DELETE_VC_MSG 0x00008002 +#define RCONDIS_MP_ACTIVATE_VC_MSG 0x00008005 +#define RCONDIS_MP_DEACTIVATE_VC_MSG 0x00008006 +#define RCONDIS_INDICATE_STATUS_MSG 0x00008007 + +/* + * Remote NDIS message completion types + */ +#define RNDIS_INITIALIZE_CMPLT 0x80000002 +#define RNDIS_QUERY_CMPLT 0x80000004 +#define RNDIS_SET_CMPLT 0x80000005 +#define RNDIS_RESET_CMPLT 0x80000006 +#define RNDIS_KEEPALIVE_CMPLT 0x80000008 + +#define RCONDIS_MP_CREATE_VC_CMPLT 0x80008001 +#define RCONDIS_MP_DELETE_VC_CMPLT 0x80008002 +#define RCONDIS_MP_ACTIVATE_VC_CMPLT 0x80008005 +#define RCONDIS_MP_DEACTIVATE_VC_CMPLT 0x80008006 + +/* + * Reserved message type for private communication between + * lower-layer host driver and remote device, if necessary. + */ +#define RNDIS_BUS_MSG 0xff000001 + +/* + * Defines for DeviceFlags in rndis_initialize_comp + */ +#define RNDIS_DF_CONNECTIONLESS 0x00000001 +#define RNDIS_DF_CONNECTION_ORIENTED 0x00000002 +#define RNDIS_DF_RAW_DATA 0x00000004 + +/* + * Remote NDIS medium types. + */ +#define RNDIS_MEDIUM_802_3 0x00000000 +#define RNDIS_MEDIUM_802_5 0x00000001 +#define RNDIS_MEDIUM_FDDI 0x00000002 +#define RNDIS_MEDIUM_WAN 0x00000003 +#define RNDIS_MEDIUM_LOCAL_TALK 0x00000004 +#define RNDIS_MEDIUM_ARCNET_RAW 0x00000006 +#define RNDIS_MEDIUM_ARCNET_878_2 0x00000007 +#define RNDIS_MEDIUM_ATM 0x00000008 +#define RNDIS_MEDIUM_WIRELESS_WAN 0x00000009 +#define RNDIS_MEDIUM_IRDA 0x0000000a +#define RNDIS_MEDIUM_CO_WAN 0x0000000b +/* Not a real medium, defined as an upper bound */ +#define RNDIS_MEDIUM_MAX 0x0000000d + +/* + * Remote NDIS medium connection states. + */ +#define RNDIS_MEDIA_STATE_CONNECTED 0x00000000 +#define RNDIS_MEDIA_STATE_DISCONNECTED 0x00000001 + +/* + * Remote NDIS version numbers + */ +#define RNDIS_MAJOR_VERSION 0x00000001 +#define RNDIS_MINOR_VERSION 0x00000000 + +/* + * Remote NDIS offload parameters + */ +#define RNDIS_OBJECT_TYPE_DEFAULT 0x80 + +#define RNDIS_OFFLOAD_PARAMS_REVISION_3 3 +#define RNDIS_OFFLOAD_PARAMS_NO_CHANGE 0 +#define RNDIS_OFFLOAD_PARAMS_LSOV2_DISABLED 1 +#define RNDIS_OFFLOAD_PARAMS_LSOV2_ENABLED 2 +#define RNDIS_OFFLOAD_PARAMS_LSOV1_ENABLED 2 +#define RNDIS_OFFLOAD_PARAMS_RSC_DISABLED 1 +#define RNDIS_OFFLOAD_PARAMS_RSC_ENABLED 2 +#define RNDIS_OFFLOAD_PARAMS_TX_RX_DISABLED 1 +#define RNDIS_OFFLOAD_PARAMS_TX_ENABLED_RX_DISABLED 2 +#define RNDIS_OFFLOAD_PARAMS_RX_ENABLED_TX_DISABLED 3 +#define RNDIS_OFFLOAD_PARAMS_TX_RX_ENABLED 4 + +#define RNDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE 1 +#define RNDIS_TCP_LARGE_SEND_OFFLOAD_IPV4 0 +#define RNDIS_TCP_LARGE_SEND_OFFLOAD_IPV6 1 + +#define RNDIS_OID_TCP_OFFLOAD_CURRENT_CONFIG 0xFC01020B /* query only */ +#define RNDIS_OID_TCP_OFFLOAD_PARAMS 0xFC01020C /* set only */ +#define RNDIS_OID_TCP_OFFLOAD_HARDWARE_CAPS 0xFC01020D/* query only */ +#define RNDIS_OID_TCPCON_OFFLOAD_CURRENT_CONFIG 0xFC01020E /* query only */ +#define RNDIS_OID_TCPCON_OFFLOAD_HARDWARE_CAPS 0xFC01020F /* query */ +#define RNDIS_OID_OFFLOAD_ENCAPSULATION 0x0101010A /* set/query */ + +/* + * NdisInitialize message + */ +struct rndis_init_req { + /* RNDIS request ID */ + uint32_t request_id; + uint32_t major_version; + uint32_t minor_version; + uint32_t max_xfer_size; +}; + +/* + * Response to NdisInitialize + */ +struct rndis_init_comp { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS status */ + uint32_t status; + uint32_t major_version; + uint32_t minor_version; + uint32_t device_flags; + /* RNDIS medium */ + uint32_t medium; + uint32_t max_pkts_per_msg; + uint32_t max_xfer_size; + uint32_t pkt_align_factor; + uint32_t af_list_offset; + uint32_t af_list_size; +}; + +/* + * Call manager devices only: Information about an address family + * supported by the device is appended to the response to NdisInitialize. + */ +struct rndis_co_address_family { + /* RNDIS AF */ + uint32_t address_family; + uint32_t major_version; + uint32_t minor_version; +}; + +/* + * NdisHalt message + */ +struct rndis_halt_req { + /* RNDIS request ID */ + uint32_t request_id; +}; + +/* + * NdisQueryRequest message + */ +struct rndis_query_req { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS OID */ + uint32_t oid; + uint32_t info_buffer_length; + uint32_t info_buffer_offset; + /* RNDIS handle */ + uint32_t device_vc_handle; +}; + +/* + * Response to NdisQueryRequest + */ +struct rndis_query_comp { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS status */ + uint32_t status; + uint32_t info_buffer_length; + uint32_t info_buffer_offset; +}; + +/* + * NdisSetRequest message + */ +struct rndis_set_req { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS OID */ + uint32_t oid; + uint32_t info_buffer_length; + uint32_t info_buffer_offset; + /* RNDIS handle */ + uint32_t device_vc_handle; +}; + +/* + * Response to NdisSetRequest + */ +struct rndis_set_comp { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS status */ + uint32_t status; +}; + +/* + * NdisReset message + */ +struct rndis_reset_req { + uint32_t reserved; +}; + +/* + * Response to NdisReset + */ +struct rndis_reset_comp { + /* RNDIS status */ + uint32_t status; + uint32_t addressing_reset; +}; + +/* + * NdisMIndicateStatus message + */ +struct rndis_indicate_status { + /* RNDIS status */ + uint32_t status; + uint32_t status_buf_length; + uint32_t status_buf_offset; +}; + +/* + * Diagnostic information passed as the status buffer in + * rndis_indicate_status messages signifying error conditions. + */ +struct rndis_diagnostic_info { + /* RNDIS status */ + uint32_t diag_status; + uint32_t error_offset; +}; + +/* + * NdisKeepAlive message + */ +struct rndis_keepalive_req { + /* RNDIS request ID */ + uint32_t request_id; +}; + +/* + * Response to NdisKeepAlive + */ +struct rndis_keepalive_comp { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS status */ + uint32_t status; +}; + +/* + * Data message. All offset fields contain byte offsets from the + * beginning of the rndis_pkt structure. All length fields are in + * bytes. VcHandle is set to 0 for connectionless data, otherwise + * it contains the VC handle. + */ +struct rndis_pkt { + uint32_t data_offset; + uint32_t data_length; + uint32_t oob_data_offset; + uint32_t oob_data_length; + uint32_t num_oob_data_elements; + uint32_t pkt_info_offset; + uint32_t pkt_info_length; + /* RNDIS handle */ + uint32_t vc_handle; + uint32_t reserved; +}; + +struct rndis_pkt_ex { + uint32_t data_offset; + uint32_t data_length; + uint32_t oob_data_offset; + uint32_t oob_data_length; + uint32_t num_oob_data_elements; + uint32_t pkt_info_offset; + uint32_t pkt_info_length; + /* RNDIS handle */ + uint32_t vc_handle; + uint32_t reserved; + uint64_t data_buf_id; + uint32_t data_buf_offset; + uint64_t next_header_buf_id; + uint32_t next_header_byte_offset; + uint32_t next_header_byte_count; +}; + +/* + * Optional Out of Band data associated with a Data message. + */ +struct rndis_oobd { + uint32_t size; + /* RNDIS class ID */ + uint32_t type; + uint32_t class_info_offset; +}; + +/* + * Packet extension field contents associated with a Data message. + */ +struct rndis_pkt_info { + uint32_t size; + uint32_t type; + uint32_t pkt_info_offset; +}; + +enum ndis_pkt_info_type { + tcpip_chksum_info, + ipsec_info, + tcp_large_send_info, + classification_handle_info, + ndis_reserved, + sgl_info, + ieee_8021q_info, + original_pkt_info, + pkt_cancel_id, + original_netbuf_list, + cached_netbuf_list, + short_pkt_padding_info, + max_perpkt_info +}; + +struct ndis_8021q_info { + union { + struct { + uint32_t user_pri : 3; /* User Priority */ + uint32_t cfi : 1; /* Canonical Format ID */ + uint32_t vlan_id : 12; + uint32_t reserved : 16; + }; + uint32_t value; + }; +} ndis_8021q_info; + +struct rndis_objhdr { + uint8_t type; + uint8_t revision; + uint16_t size; +}; + +struct rndis_offload_params { + struct rndis_objhdr header; + uint8_t ipv4_csum; + uint8_t tcp_ipv4_csum; + uint8_t udp_ipv4_csum; + uint8_t tcp_ipv6_csum; + uint8_t udp_ipv6_csum; + uint8_t lso_v1; + uint8_t ip_sec_v1; + uint8_t lso_v2_ipv4; + uint8_t lso_v2_ipv6; + uint8_t tcp_connection_ipv4; + uint8_t tcp_connection_ipv6; + uint32_t flags; + uint8_t ip_sec_v2; + uint8_t ip_sec_v2_ipv4; + struct { + uint8_t rsc_ipv4; + uint8_t rsc_ipv6; + }; + struct { + uint8_t encap_packet_task_offload; + uint8_t encap_types; + }; +}; + +struct rndis_tcp_ip_csum_info { + union { + struct { + uint32_t is_ipv4:1; + uint32_t is_ipv6:1; + uint32_t tcp_csum:1; + uint32_t udp_csum:1; + uint32_t ip_header_csum:1; + uint32_t reserved:11; + uint32_t tcp_header_offset:10; + } xmit; + struct { + uint32_t tcp_csum_failed:1; + uint32_t udp_csum_failed:1; + uint32_t ip_csum_failed:1; + uint32_t tcp_csum_succeeded:1; + uint32_t udp_csum_succeeded:1; + uint32_t ip_csum_succeeded:1; + uint32_t loopback:1; + uint32_t tcp_csum_value_invalid:1; + uint32_t ip_csum_value_invalid:1; + } recv; + uint32_t value; + }; +}; + +struct rndis_tcp_tso_info { + union { + struct { + uint32_t unused:30; + uint32_t type:1; + uint32_t reserved2:1; + } xmit; + struct { + uint32_t mss:20; + uint32_t tcp_header_offset:10; + uint32_t type:1; + uint32_t reserved2:1; + } lso_v1_xmit; + struct { + uint32_t tcp_payload:30; + uint32_t type:1; + uint32_t reserved2:1; + } lso_v1_xmit_comp; + struct { + uint32_t mss:20; + uint32_t tcp_header_offset:10; + uint32_t type:1; + uint32_t ip_version:1; + } lso_v2_xmit; + struct { + uint32_t reserved:30; + uint32_t type:1; + uint32_t reserved2:1; + } lso_v2_xmit_comp; + uint32_t value; + }; +}; + +#define RNDIS_VLAN_PPI_SIZE (sizeof(rndis_pkt_info) + \ + sizeof(ndis_8021q_info)) + +#define RNDIS_CSUM_PPI_SIZE (sizeof(rndis_pkt_info) + \ + sizeof(rndis_tcp_ip_csum_info)) + +#define RNDIS_TSO_PPI_SIZE (sizeof(rndis_pkt_info) + \ + sizeof(rndis_tcp_tso_info)) + +/* + * Format of Information buffer passed in a SetRequest for the OID + * OID_GEN_RNDIS_CONFIG_PARAMETER. + */ +struct rndis_config_param_info { + uint32_t name_offset; + uint32_t name_length; + uint32_t param_type; + uint32_t value_offset; + uint32_t value_length; +}; + +/* + * Values for ParameterType in rndis_config_param_info + */ +#define RNDIS_CONFIG_PARAM_TYPE_INTEGER 0 +#define RNDIS_CONFIG_PARAM_TYPE_STRING 2 + + +/* + * CONDIS Miniport messages for connection oriented devices + * that do not implement a call manager. + */ + +/* + * CoNdisMiniportCreateVc message + */ +struct rcondis_mp_create_vc { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS handle */ + uint32_t ndis_vc_handle; +}; + +/* + * Response to CoNdisMiniportCreateVc + */ +struct rcondis_mp_create_vc_comp { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS handle */ + uint32_t device_vc_handle; + /* RNDIS status */ + uint32_t status; +}; + +/* + * CoNdisMiniportDeleteVc message + */ +struct rcondis_mp_delete_vc { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS handle */ + uint32_t device_vc_handle; +}; + +/* + * Response to CoNdisMiniportDeleteVc + */ +struct rcondis_mp_delete_vc_comp { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS status */ + uint32_t status; +}; + +/* + * CoNdisMiniportQueryRequest message + */ +struct rcondis_mp_query_req { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS request type */ + uint32_t request_type; + /* RNDIS OID */ + uint32_t oid; + /* RNDIS handle */ + uint32_t device_vc_handle; + uint32_t info_buf_length; + uint32_t info_buf_offset; +}; + +/* + * CoNdisMiniportSetRequest message + */ +struct rcondis_mp_set_req { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS request type */ + uint32_t request_type; + /* RNDIS OID */ + uint32_t oid; + /* RNDIS handle */ + uint32_t device_vc_handle; + uint32_t info_buf_length; + uint32_t info_buf_offset; +}; + +/* + * CoNdisIndicateStatus message + */ +struct rcondis_indicate_status { + /* RNDIS handle */ + uint32_t ndis_vc_handle; + /* RNDIS status */ + uint32_t status; + uint32_t status_buf_length; + uint32_t status_buf_offset; +}; + +/* + * CONDIS Call/VC parameters + */ + +struct rcondis_specific_params { + uint32_t type; + uint32_t length; + uint32_t offset; +}; + +struct rcondis_media_params { + uint32_t flags; + uint32_t reserved1; + uint32_t reserved2; + struct rcondis_specific_params media_specific; +}; + +struct rndis_flowspec { + uint32_t token_rate; + uint32_t token_bucket_size; + uint32_t peak_bandwidth; + uint32_t latency; + uint32_t delay_variation; + uint32_t service_type; + uint32_t max_sdu_size; + uint32_t minimum_policed_size; +}; + +struct rcondis_call_manager_params { + struct rndis_flowspec transmit; + struct rndis_flowspec receive; + struct rcondis_specific_params call_mgr_specific; +}; + +/* + * CoNdisMiniportActivateVc message + */ +struct rcondis_mp_activate_vc_req { + /* RNDIS request ID */ + uint32_t request_id; + uint32_t flags; + /* RNDIS handle */ + uint32_t device_vc_handle; + uint32_t media_params_offset; + uint32_t media_params_length; + uint32_t call_mgr_params_offset; + uint32_t call_mgr_params_length; +}; + +/* + * Response to CoNdisMiniportActivateVc + */ +struct rcondis_mp_activate_vc_comp { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS status */ + uint32_t status; +}; + +/* + * CoNdisMiniportDeactivateVc message + */ +struct rcondis_mp_deactivate_vc_req { + /* RNDIS request ID */ + uint32_t request_id; + uint32_t flags; + /* RNDIS handle */ + uint32_t device_vc_handle; +}; + +/* + * Response to CoNdisMiniportDeactivateVc + */ +struct rcondis_mp_deactivate_vc_comp { + /* RNDIS request ID */ + uint32_t request_id; + /* RNDIS status */ + uint32_t status; +}; + +/* + * Container with all of the RNDIS messages + */ +union rndis_msg_cont { + struct rndis_pkt pkt; + struct rndis_init_req init_req; + struct rndis_halt_req halt_req; + struct rndis_query_req query_req; + struct rndis_set_req set_req; + struct rndis_reset_req reset_req; + struct rndis_keepalive_req keepalive_req; + struct rndis_indicate_status indicate_status; + struct rndis_init_comp init_comp; + struct rndis_query_comp query_comp; + struct rndis_set_comp set_comp; + struct rndis_reset_comp reset_comp; + struct rndis_keepalive_comp keepalive_comp; + struct rcondis_mp_create_vc co_mp_create_vc; + struct rcondis_mp_delete_vc co_mp_delete_vc; + struct rcondis_indicate_status co_mp_status; + struct rcondis_mp_activate_vc_req co_mp_activate_vc; + struct rcondis_mp_deactivate_vc_req co_mp_deactivate_vc; + struct rcondis_mp_create_vc_comp co_mp_create_vc_comp; + struct rcondis_mp_delete_vc_comp co_mp_delete_vc_comp; + struct rcondis_mp_activate_vc_comp co_mp_activate_vc_comp; + struct rcondis_mp_deactivate_vc_comp co_mp_deactivate_vc_comp; + struct rndis_pkt_ex pkt_ex; +}; + +/* + * Remote NDIS message format + */ +struct rndis { + uint32_t msg_type; + + /* + * Total length of this message, from the beginning + * of the struct, in bytes. + */ + uint32_t msg_len; + + /* Actual message */ + union rndis_msg_cont msg; +}; + +/* + * get the size of an RNDIS message. Pass in the message type, + * rndis_set_req, rndis_packet for example + */ +#define RNDIS_HEADER_SIZE 8 +#define RNDIS_MESSAGE_SIZE(message) \ + (sizeof(message) + RNDIS_HEADER_SIZE) + +#define NDIS_PACKET_TYPE_DIRECTED 0x00000001 +#define NDIS_PACKET_TYPE_MULTICAST 0x00000002 +#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004 +#define NDIS_PACKET_TYPE_BROADCAST 0x00000008 +#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010 +#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020 +#define NDIS_PACKET_TYPE_SMT 0x00000040 +#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080 +#define NDIS_PACKET_TYPE_GROUP 0x00000100 +#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200 +#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400 +#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800 + +#endif /* _RNDISREG_H_ */ -- 2.20.1