From 0b33bb394d0d02918679064caa11ef59e5ff3924 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 17 Aug 2022 18:00:16 +0200 Subject: hw/usb/hcd-xhci: Check whether DMA accesses fail If a guest sets up bad descriptors, it could force QEMU to access non-existing memory regions. Thus we should check the return value of dma_memory_read/write() to make sure that these errors don't go unnoticed. Signed-off-by: Thomas Huth Message-Id: <20220817160016.49752-1-thuth@redhat.com> Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 64 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 16 deletions(-) (limited to 'hw/usb') diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 3c48b58dde..acd60b1a49 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -463,6 +463,12 @@ static void xhci_mfwrap_timer(void *opaque) xhci_mfwrap_update(xhci); } +static void xhci_die(XHCIState *xhci) +{ + xhci->usbsts |= USBSTS_HCE; + DPRINTF("xhci: asserted controller error\n"); +} + static inline dma_addr_t xhci_addr64(uint32_t low, uint32_t high) { if (sizeof(dma_addr_t) == 4) { @@ -488,7 +494,14 @@ static inline void xhci_dma_read_u32s(XHCIState *xhci, dma_addr_t addr, assert((len % sizeof(uint32_t)) == 0); - dma_memory_read(xhci->as, addr, buf, len, MEMTXATTRS_UNSPECIFIED); + if (dma_memory_read(xhci->as, addr, buf, len, + MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA memory access failed!\n", + __func__); + memset(buf, 0xff, len); + xhci_die(xhci); + return; + } for (i = 0; i < (len / sizeof(uint32_t)); i++) { buf[i] = le32_to_cpu(buf[i]); @@ -496,7 +509,7 @@ static inline void xhci_dma_read_u32s(XHCIState *xhci, dma_addr_t addr, } static inline void xhci_dma_write_u32s(XHCIState *xhci, dma_addr_t addr, - uint32_t *buf, size_t len) + const uint32_t *buf, size_t len) { int i; uint32_t tmp[5]; @@ -508,7 +521,13 @@ static inline void xhci_dma_write_u32s(XHCIState *xhci, dma_addr_t addr, for (i = 0; i < n; i++) { tmp[i] = cpu_to_le32(buf[i]); } - dma_memory_write(xhci->as, addr, tmp, len, MEMTXATTRS_UNSPECIFIED); + if (dma_memory_write(xhci->as, addr, tmp, len, + MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA memory access failed!\n", + __func__); + xhci_die(xhci); + return; + } } static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport) @@ -593,12 +612,6 @@ static inline int xhci_running(XHCIState *xhci) return !(xhci->usbsts & USBSTS_HCH); } -static void xhci_die(XHCIState *xhci) -{ - xhci->usbsts |= USBSTS_HCE; - DPRINTF("xhci: asserted controller error\n"); -} - static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v) { XHCIInterrupter *intr = &xhci->intr[v]; @@ -619,7 +632,12 @@ static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v) ev_trb.status, ev_trb.control); addr = intr->er_start + TRB_SIZE*intr->er_ep_idx; - dma_memory_write(xhci->as, addr, &ev_trb, TRB_SIZE, MEMTXATTRS_UNSPECIFIED); + if (dma_memory_write(xhci->as, addr, &ev_trb, TRB_SIZE, + MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA memory access failed!\n", + __func__); + xhci_die(xhci); + } intr->er_ep_idx++; if (intr->er_ep_idx >= intr->er_size) { @@ -680,8 +698,12 @@ static TRBType xhci_ring_fetch(XHCIState *xhci, XHCIRing *ring, XHCITRB *trb, while (1) { TRBType type; - dma_memory_read(xhci->as, ring->dequeue, trb, TRB_SIZE, - MEMTXATTRS_UNSPECIFIED); + if (dma_memory_read(xhci->as, ring->dequeue, trb, TRB_SIZE, + MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA memory access failed!\n", + __func__); + return 0; + } trb->addr = ring->dequeue; trb->ccs = ring->ccs; le64_to_cpus(&trb->parameter); @@ -798,8 +820,14 @@ static void xhci_er_reset(XHCIState *xhci, int v) xhci_die(xhci); return; } - dma_memory_read(xhci->as, erstba, &seg, sizeof(seg), - MEMTXATTRS_UNSPECIFIED); + if (dma_memory_read(xhci->as, erstba, &seg, sizeof(seg), + MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA memory access failed!\n", + __func__); + xhci_die(xhci); + return; + } + le32_to_cpus(&seg.addr_low); le32_to_cpus(&seg.addr_high); le32_to_cpus(&seg.size); @@ -2415,8 +2443,12 @@ static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx) /* TODO: actually implement real values here */ bw_ctx[0] = 0; memset(&bw_ctx[1], 80, xhci->numports); /* 80% */ - dma_memory_write(xhci->as, ctx, bw_ctx, sizeof(bw_ctx), - MEMTXATTRS_UNSPECIFIED); + if (dma_memory_write(xhci->as, ctx, bw_ctx, sizeof(bw_ctx), + MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA memory write failed!\n", + __func__); + return CC_TRB_ERROR; + } return CC_SUCCESS; } -- cgit v1.2.3 From d8c2e6f2f6d29ccb766197181eb1c65c1d46b3a4 Mon Sep 17 00:00:00 2001 From: Qiang Liu Date: Fri, 26 Aug 2022 13:15:56 +0800 Subject: hcd-ohci: Drop ohci_service_iso_td() if ed->head & OHCI_DPTR_MASK is zero An abort happens in ohci_frame_boundary() when ohci->done is 0 [1]. ``` c static void ohci_frame_boundary(void *opaque) { // ... if (ohci->done_count == 0 && !(ohci->intr_status & OHCI_INTR_WD)) { if (!ohci->done) abort(); <----------------------------------------- [1] ``` This was reported in https://bugs.launchpad.net/qemu/+bug/1911216/, https://lists.gnu.org/archive/html/qemu-devel/2021-06/msg03613.html, and https://gitlab.com/qemu-project/qemu/-/issues/545. I can still reproduce it with the latest QEMU. This happends due to crafted ED with putting ISO_TD at physical address 0. Suppose ed->head & OHCI_DPTR_MASK is 0 [2], and we memset 0 to the phyiscal memory from 0 to sizeof(ohci_iso_td). Then, starting_frame [3] and frame_count [4] are both 0. As we can control the value of ohci->frame_number (0 to 0x1f, suppose 1), we then control the value of relative_frame_number to be 1 [6]. The control flow goes to [7] where ohci->done is 0. Have returned from ohci_service_iso_td(), ohci_frame_boundary() will abort() [1]. ``` c static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed) { // ... addr = ed->head & OHCI_DPTR_MASK; // <--------------------- [2] if (ohci_read_iso_td(ohci, addr, &iso_td)) { // <-------- [3] // ... starting_frame = OHCI_BM(iso_td.flags, TD_SF); // <-------- [4] frame_count = OHCI_BM(iso_td.flags, TD_FC); // <-------- [5] relative_frame_number = USUB(ohci->frame_number, starting_frame); // <-------- [6] if (relative_frame_number < 0) { return 1; } else if (relative_frame_number > frame_count) { // ... ohci->done = addr; // <-------- [7] // ... } ``` As only (afaik) a guest root user can manipulate ED, TD and the physical memory, this assertion failure is not a security bug. The idea to fix this issue is to drop ohci_service_iso_td() if ed->head & OHCI_DPTR_MASK is 0, which is similar to the drop operation for ohci_service_ed_list() when head is 0. Probably, a similar issue is in ohci_service_td(). I drop ohci_service_td() if ed->head & OHCI_DPTR_MASK is 0. Fixes: 7bfe577702 ("OHCI USB isochronous transfers support (Arnon Gilboa)") Reported-by: Gaoning Pan Reported-by: Alexander Bulekov Reported-by: Qiang Liu Resolves: https://gitlab.com/qemu-project/qemu/-/issues/545 Buglink: https://lists.gnu.org/archive/html/qemu-devel/2021-06/msg03613.html Buglink: https://bugs.launchpad.net/qemu/+bug/1911216 Signed-off-by: Qiang Liu Message-Id: <20220826051557.119570-1-cyruscyliu@gmail.com> Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ohci.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'hw/usb') diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index 895b29fb86..72bdde9261 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -571,6 +571,11 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed) addr = ed->head & OHCI_DPTR_MASK; + if (addr == 0) { + ohci_die(ohci); + return 1; + } + if (ohci_read_iso_td(ohci, addr, &iso_td)) { trace_usb_ohci_iso_td_read_failed(addr); ohci_die(ohci); @@ -858,6 +863,11 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) int completion; addr = ed->head & OHCI_DPTR_MASK; + if (addr == 0) { + ohci_die(ohci); + return 1; + } + /* See if this TD has already been submitted to the device. */ completion = (addr == ohci->async_td); if (completion && !ohci->async_complete) { -- cgit v1.2.3 From cea5aa85691d7f26b7ea995417d41a32802691b7 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 30 Aug 2022 08:38:26 +0200 Subject: usb/msd: move usb_msd_packet_complete() Change ordering to avoid adding forward declarations in following patches. Fix comment code style while being at it. No functional change. Signed-off-by: Gerd Hoffmann Message-Id: <20220830063827.813053-2-kraxel@redhat.com> --- hw/usb/dev-storage.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'hw/usb') diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index 98639696e6..140ef2aeaa 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -177,6 +177,20 @@ static const USBDesc desc = { .str = desc_strings, }; +static void usb_msd_packet_complete(MSDState *s) +{ + USBPacket *p = s->packet; + + /* + * Set s->packet to NULL before calling usb_packet_complete + * because another request may be issued before + * usb_packet_complete returns. + */ + trace_usb_msd_packet_complete(); + s->packet = NULL; + usb_packet_complete(&s->dev, p); +} + static void usb_msd_copy_data(MSDState *s, USBPacket *p) { uint32_t len; @@ -208,18 +222,6 @@ static void usb_msd_send_status(MSDState *s, USBPacket *p) memset(&s->csw, 0, sizeof(s->csw)); } -static void usb_msd_packet_complete(MSDState *s) -{ - USBPacket *p = s->packet; - - /* Set s->packet to NULL before calling usb_packet_complete - because another request may be issued before - usb_packet_complete returns. */ - trace_usb_msd_packet_complete(); - s->packet = NULL; - usb_packet_complete(&s->dev, p); -} - void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) { MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); -- cgit v1.2.3 From 12b69878fc7b4b92b1bbd3959f2c3d4c717881fb Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 30 Aug 2022 08:38:27 +0200 Subject: usb/msd: add usb_msd_fatal_error() and fix guest-triggerable assert Add handler for fatal errors. Moves device into error state where it stops responding until the guest resets it. Guest can send illegal requests where scsi command and usb packet transfer directions are inconsistent. Use the new usb_msd_fatal_error() function instead of assert() in that case. Reported-by: Qiang Liu Signed-off-by: Gerd Hoffmann Tested-by: Qiang Liu Message-Id: <20220830063827.813053-3-kraxel@redhat.com> --- hw/usb/dev-storage.c | 30 +++++++++++++++++++++++++++++- hw/usb/trace-events | 1 + 2 files changed, 30 insertions(+), 1 deletion(-) (limited to 'hw/usb') diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index 140ef2aeaa..e3bcffb3e0 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -191,6 +191,23 @@ static void usb_msd_packet_complete(MSDState *s) usb_packet_complete(&s->dev, p); } +static void usb_msd_fatal_error(MSDState *s) +{ + trace_usb_msd_fatal_error(); + + if (s->packet) { + s->packet->status = USB_RET_STALL; + usb_msd_packet_complete(s); + } + + /* + * Guest messed up up device state with illegal requests. Go + * ignore any requests until the guests resets the device (and + * brings it into a known state that way). + */ + s->needs_reset = true; +} + static void usb_msd_copy_data(MSDState *s, USBPacket *p) { uint32_t len; @@ -227,7 +244,11 @@ void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); USBPacket *p = s->packet; - assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV)); + if ((s->mode == USB_MSDM_DATAOUT) != (req->cmd.mode == SCSI_XFER_TO_DEV)) { + usb_msd_fatal_error(s); + return; + } + s->scsi_len = len; s->scsi_off = 0; if (p) { @@ -317,6 +338,8 @@ void usb_msd_handle_reset(USBDevice *dev) memset(&s->csw, 0, sizeof(s->csw)); s->mode = USB_MSDM_CBW; + + s->needs_reset = false; } static void usb_msd_handle_control(USBDevice *dev, USBPacket *p, @@ -382,6 +405,11 @@ static void usb_msd_handle_data(USBDevice *dev, USBPacket *p) SCSIDevice *scsi_dev; uint32_t len; + if (s->needs_reset) { + p->status = USB_RET_STALL; + return; + } + switch (p->pid) { case USB_TOKEN_OUT: if (devep != 2) diff --git a/hw/usb/trace-events b/hw/usb/trace-events index 914ca71668..b65269892c 100644 --- a/hw/usb/trace-events +++ b/hw/usb/trace-events @@ -263,6 +263,7 @@ usb_msd_packet_complete(void) "" usb_msd_cmd_submit(unsigned lun, unsigned tag, unsigned flags, unsigned len, unsigned data_len) "lun %u, tag 0x%x, flags 0x%08x, len %d, data-len %d" usb_msd_cmd_complete(unsigned status, unsigned tag) "status %d, tag 0x%x" usb_msd_cmd_cancel(unsigned tag) "tag 0x%x" +usb_msd_fatal_error(void) "" # dev-uas.c usb_uas_reset(int addr) "dev %d" -- cgit v1.2.3 From 145cdaba0f7ea721080e1289dc7a31bb2066406f Mon Sep 17 00:00:00 2001 From: Qiang Liu Date: Sun, 4 Sep 2022 20:59:26 +0800 Subject: hcd-xhci: drop operation with secondary stream arrays enabled The abort() in xhci_find_stream() can be triggered via enabling the secondary stream arrays by setting linear stream array (LSA) bit (in endpoint context) to 0. We may show warnings and drop this operation. Fixes: 024426acc0a2 ("usb-xhci: usb3 streams") Reported-by: Qiang Liu Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1192 Signed-off-by: Qiang Liu Message-Id: <20220904125926.2141607-1-cyruscyliu@gmail.com> Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'hw/usb') diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index acd60b1a49..8299f35e66 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1020,7 +1020,9 @@ static XHCIStreamContext *xhci_find_stream(XHCIEPContext *epctx, } sctx = epctx->pstreams + streamid; } else { - FIXME("secondary streams not implemented yet"); + fprintf(stderr, "xhci: FIXME: secondary streams not implemented yet"); + *cc_error = CC_INVALID_STREAM_TYPE_ERROR; + return NULL; } if (sctx->sct == -1) { -- cgit v1.2.3 From a89003780d0a96b79314da4a4cdb148ff1dcb397 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 6 Sep 2022 19:30:50 +0100 Subject: usbnet: Add missing usb_wakeup() call in usbnet_receive() usbnet_receive() does not currently wake up the USB endpoint, leading to a dead RX datapath when used with a host controller such as xHCI that relies on being woken up. Fix by adding a call to usb_wakeup() at the end of usbnet_receive(). Signed-off-by: Michael Brown Message-Id: <20220906183053.3625472-2-mcb30@ipxe.org> Signed-off-by: Gerd Hoffmann --- hw/usb/dev-network.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'hw/usb') diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index 6c49c16015..61bf598870 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -647,6 +647,7 @@ struct USBNetState { uint8_t in_buf[2048]; USBEndpoint *intr; + USBEndpoint *bulk_in; char usbstring_mac[13]; NICState *nic; @@ -1317,6 +1318,7 @@ static ssize_t usbnet_receive(NetClientState *nc, const uint8_t *buf, size_t siz memcpy(in_buf, buf, size); s->in_len = total_size; s->in_ptr = 0; + usb_wakeup(s->bulk_in, 0); return size; } @@ -1359,6 +1361,7 @@ static void usb_net_realize(USBDevice *dev, Error **errp) s->filter = 0; s->vendorid = 0x1234; s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); + s->bulk_in = usb_ep_get(dev, USB_TOKEN_IN, 2); qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_usbnet_info, &s->conf, -- cgit v1.2.3 From 954cbf7bb53476e99091f9c99a8014af2491f6ef Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 6 Sep 2022 19:30:51 +0100 Subject: usbnet: Accept mandatory USB_CDC_SET_ETHERNET_PACKET_FILTER request The USB_CDC_SET_ETHERNET_PACKET_FILTER request is mandatory for CDC-ECM devices. Accept this request, ignoring the actual filter value (to match the existing behaviour for RNDIS). Signed-off-by: Michael Brown Message-Id: <20220906183053.3625472-3-mcb30@ipxe.org> Signed-off-by: Gerd Hoffmann --- hw/usb/dev-network.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'hw/usb') diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index 61bf598870..155df935cd 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -1122,6 +1122,12 @@ static void usb_net_handle_control(USBDevice *dev, USBPacket *p, #endif break; + case ClassInterfaceOutRequest | USB_CDC_SET_ETHERNET_PACKET_FILTER: + if (is_rndis(s)) { + goto fail; + } + break; + default: fail: fprintf(stderr, "usbnet: failed control transaction: " -- cgit v1.2.3 From 2423ee233872d07c7607ce26f9225c64b30b0dc3 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 6 Sep 2022 19:30:52 +0100 Subject: usbnet: Detect short packets as sent by the xHCI controller The xHCI controller will ignore the endpoint MTU and so may deliver packets of any length. Detect short packets as being any packet that has a length of zero or a length that is not a multiple of the MTU. Signed-off-by: Michael Brown Message-Id: <20220906183053.3625472-4-mcb30@ipxe.org> Signed-off-by: Gerd Hoffmann --- hw/usb/dev-network.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'hw/usb') diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index 155df935cd..9d83974ec9 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -1211,7 +1211,7 @@ static void usb_net_handle_dataout(USBNetState *s, USBPacket *p) s->out_ptr += sz; if (!is_rndis(s)) { - if (p->iov.size < 64) { + if (p->iov.size % 64 || p->iov.size == 0) { qemu_send_packet(qemu_get_queue(s->nic), s->out_buf, s->out_ptr); s->out_ptr = 0; } -- cgit v1.2.3 From f3def4dd42531cb542bb0e004f375b9d89fd5853 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 6 Sep 2022 19:30:53 +0100 Subject: usbnet: Report link-up via interrupt endpoint in CDC-ECM mode Signed-off-by: Michael Brown Message-Id: <20220906183053.3625472-5-mcb30@ipxe.org> Signed-off-by: Gerd Hoffmann --- hw/usb/dev-network.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) (limited to 'hw/usb') diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index 9d83974ec9..ac1adca543 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -91,6 +91,8 @@ enum usbstring_idx { #define USB_CDC_SET_ETHERNET_PACKET_FILTER 0x43 #define USB_CDC_GET_ETHERNET_STATISTIC 0x44 +#define USB_CDC_NETWORK_CONNECTION 0x00 + #define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */ #define STATUS_BYTECOUNT 16 /* 8 byte header + data */ @@ -640,6 +642,8 @@ struct USBNetState { uint16_t filter; uint32_t vendorid; + uint16_t connection; + unsigned int out_ptr; uint8_t out_buf[2048]; @@ -1140,18 +1144,28 @@ static void usb_net_handle_control(USBDevice *dev, USBPacket *p, static void usb_net_handle_statusin(USBNetState *s, USBPacket *p) { - le32 buf[2]; + le32 rbuf[2]; + uint16_t ebuf[4]; if (p->iov.size < 8) { p->status = USB_RET_STALL; return; } - buf[0] = cpu_to_le32(1); - buf[1] = cpu_to_le32(0); - usb_packet_copy(p, buf, 8); - if (!s->rndis_resp.tqh_first) { - p->status = USB_RET_NAK; + if (is_rndis(s)) { + rbuf[0] = cpu_to_le32(1); + rbuf[1] = cpu_to_le32(0); + usb_packet_copy(p, rbuf, 8); + if (!s->rndis_resp.tqh_first) { + p->status = USB_RET_NAK; + } + } else { + ebuf[0] = + cpu_to_be16(ClassInterfaceRequest | USB_CDC_NETWORK_CONNECTION); + ebuf[1] = cpu_to_le16(s->connection); + ebuf[2] = cpu_to_le16(1); + ebuf[3] = cpu_to_le16(0); + usb_packet_copy(p, ebuf, 8); } #ifdef TRAFFIC_DEBUG @@ -1366,6 +1380,7 @@ static void usb_net_realize(USBDevice *dev, Error **errp) s->media_state = 0; /* NDIS_MEDIA_STATE_CONNECTED */; s->filter = 0; s->vendorid = 0x1234; + s->connection = 1; /* Connected */ s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); s->bulk_in = usb_ep_get(dev, USB_TOKEN_IN, 2); -- cgit v1.2.3