Changeset View
Changeset View
Standalone View
Standalone View
head/sys/dev/hyperv/netvsc/hv_rndis_filter.c
Show First 20 Lines • Show All 90 Lines • ▼ Show 20 Lines | hv_rf_send_offload_request(struct hn_softc *sc, | ||||
rndis_offload_params *offloads); | rndis_offload_params *offloads); | ||||
static void hn_rndis_sent_halt(struct hn_send_ctx *sndc, | static void hn_rndis_sent_halt(struct hn_send_ctx *sndc, | ||||
struct hn_softc *sc, struct vmbus_channel *chan, | struct hn_softc *sc, struct vmbus_channel *chan, | ||||
const void *data, int dlen); | const void *data, int dlen); | ||||
static void hn_rndis_sent_cb(struct hn_send_ctx *sndc, | static void hn_rndis_sent_cb(struct hn_send_ctx *sndc, | ||||
struct hn_softc *sc, struct vmbus_channel *chan, | struct hn_softc *sc, struct vmbus_channel *chan, | ||||
const void *data, int dlen); | const void *data, int dlen); | ||||
static int hn_rndis_query(struct hn_softc *sc, uint32_t oid, | |||||
const void *idata, size_t idlen, void *odata, size_t *odlen0); | |||||
static __inline uint32_t | static __inline uint32_t | ||||
hn_rndis_rid(struct hn_softc *sc) | hn_rndis_rid(struct hn_softc *sc) | ||||
{ | { | ||||
uint32_t rid; | uint32_t rid; | ||||
again: | again: | ||||
rid = atomic_fetchadd_int(&sc->hn_rndis_rid, 1); | rid = atomic_fetchadd_int(&sc->hn_rndis_rid, 1); | ||||
▲ Show 20 Lines • Show All 583 Lines • ▼ Show 20 Lines | if (request != NULL) | ||||
hv_put_rndis_request(device, request); | hv_put_rndis_request(device, request); | ||||
return (ret); | return (ret); | ||||
} | } | ||||
/* | /* | ||||
* RNDIS filter query device MAC address | * RNDIS filter query device MAC address | ||||
*/ | */ | ||||
static inline int | static int | ||||
hv_rf_query_device_mac(rndis_device *device) | hv_rf_query_device_mac(rndis_device *device) | ||||
{ | { | ||||
uint32_t size = ETHER_ADDR_LEN; | struct hn_softc *sc = device->sc; | ||||
size_t hwaddr_len; | |||||
int error; | |||||
return (hv_rf_query_device(device, | hwaddr_len = ETHER_ADDR_LEN; | ||||
RNDIS_OID_802_3_PERMANENT_ADDRESS, device->hw_mac_addr, &size)); | error = hn_rndis_query(sc, OID_802_3_PERMANENT_ADDRESS, NULL, 0, | ||||
device->hw_mac_addr, &hwaddr_len); | |||||
if (error) | |||||
return error; | |||||
if (hwaddr_len != ETHER_ADDR_LEN) { | |||||
if_printf(sc->hn_ifp, "invalid hwaddr len %zu\n", hwaddr_len); | |||||
return EINVAL; | |||||
} | } | ||||
return 0; | |||||
} | |||||
/* | /* | ||||
* RNDIS filter query device link status | * RNDIS filter query device link status | ||||
*/ | */ | ||||
static inline int | static inline int | ||||
hv_rf_query_device_link_status(rndis_device *device) | hv_rf_query_device_link_status(rndis_device *device) | ||||
{ | { | ||||
uint32_t size = sizeof(uint32_t); | uint32_t size = sizeof(uint32_t); | ||||
▲ Show 20 Lines • Show All 173 Lines • ▼ Show 20 Lines | if (request != NULL) { | ||||
hv_put_rndis_request(device, request); | hv_put_rndis_request(device, request); | ||||
} | } | ||||
exit: | exit: | ||||
return (ret); | return (ret); | ||||
} | } | ||||
static const void * | static const void * | ||||
hn_rndis_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact, uint32_t rid, | hn_rndis_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact, uint32_t rid, | ||||
size_t reqlen, size_t min_complen, uint32_t comp_type) | size_t reqlen, size_t *comp_len0, uint32_t comp_type) | ||||
{ | { | ||||
struct vmbus_gpa gpa[HN_XACT_REQ_PGCNT]; | struct vmbus_gpa gpa[HN_XACT_REQ_PGCNT]; | ||||
const struct rndis_comp_hdr *comp; | const struct rndis_comp_hdr *comp; | ||||
bus_addr_t paddr; | bus_addr_t paddr; | ||||
size_t comp_len; | size_t comp_len, min_complen = *comp_len0; | ||||
int gpa_cnt, error; | int gpa_cnt, error; | ||||
KASSERT(rid > HN_RNDIS_RID_COMPAT_MAX, ("invalid rid %u\n", rid)); | KASSERT(rid > HN_RNDIS_RID_COMPAT_MAX, ("invalid rid %u\n", rid)); | ||||
KASSERT(reqlen <= HN_XACT_REQ_SIZE && reqlen > 0, | KASSERT(reqlen <= HN_XACT_REQ_SIZE && reqlen > 0, | ||||
("invalid request length %zu", reqlen)); | ("invalid request length %zu", reqlen)); | ||||
KASSERT(min_complen >= sizeof(*comp), | KASSERT(min_complen >= sizeof(*comp), | ||||
("invalid minimum complete len %zu", min_complen)); | ("invalid minimum complete len %zu", min_complen)); | ||||
Show All 32 Lines | if (error) { | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
comp = vmbus_xact_wait(xact, &comp_len); | comp = vmbus_xact_wait(xact, &comp_len); | ||||
/* | /* | ||||
* Check this RNDIS complete message. | * Check this RNDIS complete message. | ||||
*/ | */ | ||||
if (comp_len < min_complen) { | if (comp_len < min_complen) { | ||||
if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu\n", comp_len); | if (comp_len >= sizeof(*comp)) { | ||||
/* rm_status field is valid */ | |||||
if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu, " | |||||
"status 0x%08x\n", comp_len, comp->rm_status); | |||||
} else { | |||||
if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu\n", | |||||
comp_len); | |||||
} | |||||
return (NULL); | return (NULL); | ||||
} | } | ||||
if (comp->rm_len < min_complen) { | if (comp->rm_len < min_complen) { | ||||
if_printf(sc->hn_ifp, "invalid RNDIS comp msglen %u\n", | if_printf(sc->hn_ifp, "invalid RNDIS comp msglen %u\n", | ||||
comp->rm_len); | comp->rm_len); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
if (comp->rm_type != comp_type) { | if (comp->rm_type != comp_type) { | ||||
if_printf(sc->hn_ifp, "unexpected RNDIS comp 0x%08x, " | if_printf(sc->hn_ifp, "unexpected RNDIS comp 0x%08x, " | ||||
"expect 0x%08x\n", comp->rm_type, comp_type); | "expect 0x%08x\n", comp->rm_type, comp_type); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
if (comp->rm_rid != rid) { | if (comp->rm_rid != rid) { | ||||
if_printf(sc->hn_ifp, "RNDIS comp rid mismatch %u, " | if_printf(sc->hn_ifp, "RNDIS comp rid mismatch %u, " | ||||
"expect %u\n", comp->rm_rid, rid); | "expect %u\n", comp->rm_rid, rid); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
/* All pass! */ | /* All pass! */ | ||||
*comp_len0 = comp_len; | |||||
return (comp); | return (comp); | ||||
} | } | ||||
static int | |||||
hn_rndis_query(struct hn_softc *sc, uint32_t oid, | |||||
const void *idata, size_t idlen, void *odata, size_t *odlen0) | |||||
{ | |||||
struct rndis_query_req *req; | |||||
const struct rndis_query_comp *comp; | |||||
struct vmbus_xact *xact; | |||||
size_t reqlen, odlen = *odlen0, comp_len; | |||||
int error, ofs; | |||||
uint32_t rid; | |||||
reqlen = sizeof(*req) + idlen; | |||||
xact = vmbus_xact_get(sc->hn_xact, reqlen); | |||||
if (xact == NULL) { | |||||
if_printf(sc->hn_ifp, "no xact for RNDIS query\n"); | |||||
return (ENXIO); | |||||
} | |||||
rid = hn_rndis_rid(sc); | |||||
req = vmbus_xact_req_data(xact); | |||||
req->rm_type = REMOTE_NDIS_QUERY_MSG; | |||||
req->rm_len = reqlen; | |||||
req->rm_rid = rid; | |||||
req->rm_oid = oid; | |||||
/* | /* | ||||
* XXX | |||||
* This is _not_ RNDIS Spec conforming: | |||||
* "This MUST be set to 0 when there is no input data | |||||
* associated with the OID." | |||||
* | |||||
* If this field was set to 0 according to the RNDIS Spec, | |||||
* Hyper-V would set non-SUCCESS status in the query | |||||
* completion. | |||||
*/ | |||||
req->rm_infobufoffset = RNDIS_QUERY_REQ_INFOBUFOFFSET; | |||||
if (idlen > 0) { | |||||
req->rm_infobuflen = idlen; | |||||
/* Input data immediately follows RNDIS query. */ | |||||
memcpy(req + 1, idata, idlen); | |||||
} | |||||
comp_len = sizeof(*comp) + odlen; | |||||
comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len, | |||||
REMOTE_NDIS_QUERY_CMPLT); | |||||
if (comp == NULL) { | |||||
if_printf(sc->hn_ifp, "exec RNDIS query 0x%08x failed\n", oid); | |||||
error = EIO; | |||||
goto done; | |||||
} | |||||
if (comp->rm_status != RNDIS_STATUS_SUCCESS) { | |||||
if_printf(sc->hn_ifp, "RNDIS query 0x%08x failed: " | |||||
"status 0x%08x\n", oid, comp->rm_status); | |||||
error = EIO; | |||||
goto done; | |||||
} | |||||
if (comp->rm_infobuflen == 0 || comp->rm_infobufoffset == 0) { | |||||
/* No output data! */ | |||||
if_printf(sc->hn_ifp, "RNDIS query 0x%08x, no data\n", oid); | |||||
*odlen0 = 0; | |||||
error = 0; | |||||
goto done; | |||||
} | |||||
/* | |||||
* Check output data length and offset. | |||||
*/ | |||||
/* ofs is the offset from the beginning of comp. */ | |||||
ofs = RNDIS_QUERY_COMP_INFOBUFABS(comp->rm_infobufoffset); | |||||
if (ofs < sizeof(*comp) || ofs + comp->rm_infobuflen > comp_len) { | |||||
if_printf(sc->hn_ifp, "RNDIS query invalid comp ib off/len, " | |||||
"%u/%u\n", comp->rm_infobufoffset, comp->rm_infobuflen); | |||||
error = EINVAL; | |||||
goto done; | |||||
} | |||||
/* | |||||
* Save output data. | |||||
*/ | |||||
if (comp->rm_infobuflen < odlen) | |||||
odlen = comp->rm_infobuflen; | |||||
memcpy(odata, ((const uint8_t *)comp) + ofs, odlen); | |||||
*odlen0 = odlen; | |||||
error = 0; | |||||
done: | |||||
vmbus_xact_put(xact); | |||||
return (error); | |||||
} | |||||
/* | |||||
* RNDIS filter init device | * RNDIS filter init device | ||||
*/ | */ | ||||
static int | static int | ||||
hv_rf_init_device(rndis_device *device) | hv_rf_init_device(rndis_device *device) | ||||
{ | { | ||||
struct hn_softc *sc = device->sc; | struct hn_softc *sc = device->sc; | ||||
struct rndis_init_req *req; | struct rndis_init_req *req; | ||||
const struct rndis_init_comp *comp; | const struct rndis_init_comp *comp; | ||||
struct vmbus_xact *xact; | struct vmbus_xact *xact; | ||||
size_t comp_len; | |||||
uint32_t rid; | uint32_t rid; | ||||
int error; | int error; | ||||
/* XXX */ | /* XXX */ | ||||
device->state = RNDIS_DEV_INITIALIZED; | device->state = RNDIS_DEV_INITIALIZED; | ||||
xact = vmbus_xact_get(sc->hn_xact, sizeof(*req)); | xact = vmbus_xact_get(sc->hn_xact, sizeof(*req)); | ||||
if (xact == NULL) { | if (xact == NULL) { | ||||
if_printf(sc->hn_ifp, "no xact for RNDIS init\n"); | if_printf(sc->hn_ifp, "no xact for RNDIS init\n"); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
rid = hn_rndis_rid(sc); | rid = hn_rndis_rid(sc); | ||||
req = vmbus_xact_req_data(xact); | req = vmbus_xact_req_data(xact); | ||||
req->rm_type = REMOTE_NDIS_INITIALIZE_MSG; | req->rm_type = REMOTE_NDIS_INITIALIZE_MSG; | ||||
req->rm_len = sizeof(*req); | req->rm_len = sizeof(*req); | ||||
req->rm_rid = rid; | req->rm_rid = rid; | ||||
req->rm_ver_major = RNDIS_VERSION_MAJOR; | req->rm_ver_major = RNDIS_VERSION_MAJOR; | ||||
req->rm_ver_minor = RNDIS_VERSION_MINOR; | req->rm_ver_minor = RNDIS_VERSION_MINOR; | ||||
req->rm_max_xfersz = HN_RNDIS_XFER_SIZE; | req->rm_max_xfersz = HN_RNDIS_XFER_SIZE; | ||||
comp = hn_rndis_xact_execute(sc, xact, rid, sizeof(*req), | comp_len = RNDIS_INIT_COMP_SIZE_MIN; | ||||
RNDIS_INIT_COMP_SIZE_MIN, REMOTE_NDIS_INITIALIZE_CMPLT); | comp = hn_rndis_xact_execute(sc, xact, rid, sizeof(*req), &comp_len, | ||||
REMOTE_NDIS_INITIALIZE_CMPLT); | |||||
if (comp == NULL) { | if (comp == NULL) { | ||||
if_printf(sc->hn_ifp, "exec RNDIS init failed\n"); | if_printf(sc->hn_ifp, "exec RNDIS init failed\n"); | ||||
error = EIO; | error = EIO; | ||||
goto done; | goto done; | ||||
} | } | ||||
if (comp->rm_status != RNDIS_STATUS_SUCCESS) { | if (comp->rm_status != RNDIS_STATUS_SUCCESS) { | ||||
if_printf(sc->hn_ifp, "RNDIS init failed: status 0x%08x\n", | if_printf(sc->hn_ifp, "RNDIS init failed: status 0x%08x\n", | ||||
▲ Show 20 Lines • Show All 349 Lines • Show Last 20 Lines |