From a3c27ea0344d3cc7295a5f0589d5514913ec1522 Mon Sep 17 00:00:00 2001 From: Fabrice Fontaine Date: Sun, 13 Dec 2020 22:30:16 +0100 Subject: [PATCH 1/8] hw/usb/host-libusb.c: fix build with kernel < 5.0 USBDEVFS_GET_SPEED is used since version 5.2.0 and https://gitlab.com/qemu-project/qemu/-/commit/202d69a715a4b1824dcd7ec1683d027ed2bae6d3 resulting in the following build failure with kernel < 5.0: ../hw/usb/host-libusb.c: In function 'usb_host_open': ../hw/usb/host-libusb.c:953:32: error: 'USBDEVFS_GET_SPEED' undeclared (first use in this function); did you mean 'USBDEVFS_GETDRIVER'? int rc = ioctl(hostfd, USBDEVFS_GET_SPEED, NULL); ^~~~~~~~~~~~~~~~~~ USBDEVFS_GETDRIVER A tentative was made to fix this build failure with https://gitlab.com/qemu-project/qemu/-/commit/4969e697c15ac536d5c0700381d5d026ef7f0588 However, the assumption that distros with old kernels also have old libusb is just wrong so also add a check for defined(USBDEVFS_GET_SPEED) Signed-off-by: Fabrice Fontaine Message-id: 20201213213016.457350-1-fontaine.fabrice@gmail.com [ kraxel: codestyle whitespace fixup ] Signed-off-by: Gerd Hoffmann --- hw/usb/host-libusb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index b950501d10..295d20227a 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -941,7 +941,8 @@ static int usb_host_open(USBHostDevice *s, libusb_device *dev, int hostfd) usb_host_ep_update(s); libusb_speed = libusb_get_device_speed(dev); -#if LIBUSB_API_VERSION >= 0x01000107 && defined(CONFIG_LINUX) +#if LIBUSB_API_VERSION >= 0x01000107 && defined(CONFIG_LINUX) && \ + defined(USBDEVFS_GET_SPEED) if (hostfd && libusb_speed == 0) { /* * Workaround libusb bug: libusb_get_device_speed() does not From 268c02424b0b8078c15acdf73edbdeeb0dd80808 Mon Sep 17 00:00:00 2001 From: Alex Chen Date: Thu, 19 Nov 2020 02:57:51 +0000 Subject: [PATCH 2/8] hw/usb: Fix bad printf format specifiers We should use printf format specifier "%u" instead of "%d" for argument of type "unsigned int". Reported-by: Euler Robot Signed-off-by: Alex Chen Message-id: 20201119025751.45750-1-alex.chen@huawei.com Signed-off-by: Gerd Hoffmann --- hw/usb/ccid-card-passthru.c | 2 +- hw/usb/core.c | 4 ++-- hw/usb/dev-smartcard-reader.c | 8 ++++---- hw/usb/hcd-ehci.c | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c index c27c602697..c1a90fcc7a 100644 --- a/hw/usb/ccid-card-passthru.c +++ b/hw/usb/ccid-card-passthru.c @@ -336,7 +336,7 @@ static void passthru_apdu_from_guest( PassthruState *card = PASSTHRU_CCID_CARD(base); if (!qemu_chr_fe_backend_connected(&card->cs)) { - printf("ccid-passthru: no chardev, discarding apdu length %d\n", len); + printf("ccid-passthru: no chardev, discarding apdu length %u\n", len); return; } ccid_card_vscard_send_apdu(card, apdu, len); diff --git a/hw/usb/core.c b/hw/usb/core.c index e960036f4d..c895522a1d 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -142,7 +142,7 @@ static void do_token_setup(USBDevice *s, USBPacket *p) setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; if (setup_len > sizeof(s->data_buf)) { fprintf(stderr, - "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", + "usb_generic_handle_packet: ctrl buffer too small (%u > %zu)\n", setup_len, sizeof(s->data_buf)); p->status = USB_RET_STALL; return; @@ -277,7 +277,7 @@ static void do_parameter(USBDevice *s, USBPacket *p) setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; if (setup_len > sizeof(s->data_buf)) { fprintf(stderr, - "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", + "usb_generic_handle_packet: ctrl buffer too small (%u > %zu)\n", setup_len, sizeof(s->data_buf)); p->status = USB_RET_STALL; return; diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index 946df9734a..80109fa551 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -945,7 +945,7 @@ static void ccid_on_apdu_from_guest(USBCCIDState *s, CCID_XferBlock *recv) return; } len = le32_to_cpu(recv->hdr.dwLength); - DPRINTF(s, 1, "%s: seq %d, len %d\n", __func__, + DPRINTF(s, 1, "%s: seq %d, len %u\n", __func__, recv->hdr.bSeq, len); ccid_add_pending_answer(s, (CCID_Header *)recv); if (s->card && len <= BULK_OUT_DATA_SIZE) { @@ -995,13 +995,13 @@ static void ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p) if ((s->bulk_out_pos - 10 < ccid_header->dwLength) && (p->iov.size == CCID_MAX_PACKET_SIZE)) { DPRINTF(s, D_VERBOSE, - "usb-ccid: bulk_in: expecting more packets (%d/%d)\n", + "usb-ccid: bulk_in: expecting more packets (%u/%u)\n", s->bulk_out_pos - 10, ccid_header->dwLength); return; } if (s->bulk_out_pos - 10 != ccid_header->dwLength) { DPRINTF(s, 1, - "usb-ccid: bulk_in: message size mismatch (got %d, expected %d)\n", + "usb-ccid: bulk_in: message size mismatch (got %u, expected %u)\n", s->bulk_out_pos - 10, ccid_header->dwLength); goto err; } @@ -1202,7 +1202,7 @@ void ccid_card_send_apdu_to_guest(CCIDCardState *card, ccid_report_error_failed(s, ERROR_HW_ERROR); return; } - DPRINTF(s, 1, "APDU returned to guest %d (answer seq %d, slot %d)\n", + DPRINTF(s, 1, "APDU returned to guest %u (answer seq %d, slot %d)\n", len, answer->seq, answer->slot); ccid_write_data_block_answer(s, apdu, len); } diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index aca018d8b5..212eb05d3d 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -1192,7 +1192,7 @@ static int ehci_init_transfer(EHCIPacket *p) while (bytes > 0) { if (cpage > 4) { - fprintf(stderr, "cpage out of range (%d)\n", cpage); + fprintf(stderr, "cpage out of range (%u)\n", cpage); qemu_sglist_destroy(&p->sgl); return -1; } @@ -1598,7 +1598,7 @@ static int ehci_state_fetchentry(EHCIState *ehci, int async) default: /* TODO: handle FSTN type */ - fprintf(stderr, "FETCHENTRY: entry at %X is of type %d " + fprintf(stderr, "FETCHENTRY: entry at %X is of type %u " "which is not supported yet\n", entry, NLPTR_TYPE_GET(entry)); return -1; } From c3585b600bae4e67867aac73e03a3edf38ec8f1f Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 19 Jan 2021 13:01:51 +0100 Subject: [PATCH 3/8] hw/usb: Convert to qdev_realize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Device code shouldn't mess with QOM property "realized" since we have proper interfaces (merge commit 6675a653). Commit 8ddab8dd3d "usb/hcd-xhci: Split pci wrapper for xhci base model" and commit f00ff136ee "usb: hcd-xhci-sysbus: Attach xhci to sysbus device" reintroduced two instances. Clean them up. Note that s->xhci is a (bus-less) TYPE_XHCI device. Signed-off-by: Markus Armbruster Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20210119120151.53757-1-armbru@redhat.com> Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci-pci.c | 4 +--- hw/usb/hcd-xhci-sysbus.c | 5 +---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/hw/usb/hcd-xhci-pci.c b/hw/usb/hcd-xhci-pci.c index bba628d3d2..9421734d0f 100644 --- a/hw/usb/hcd-xhci-pci.c +++ b/hw/usb/hcd-xhci-pci.c @@ -115,9 +115,7 @@ static void usb_xhci_pci_realize(struct PCIDevice *dev, Error **errp) object_property_set_link(OBJECT(&s->xhci), "host", OBJECT(s), NULL); s->xhci.intr_update = xhci_pci_intr_update; s->xhci.intr_raise = xhci_pci_intr_raise; - object_property_set_bool(OBJECT(&s->xhci), "realized", true, &err); - if (err) { - error_propagate(errp, err); + if (!qdev_realize(DEVICE(&s->xhci), NULL, errp)) { return; } if (strcmp(object_get_typename(OBJECT(dev)), TYPE_NEC_XHCI) == 0) { diff --git a/hw/usb/hcd-xhci-sysbus.c b/hw/usb/hcd-xhci-sysbus.c index 29185d2261..42e2574c82 100644 --- a/hw/usb/hcd-xhci-sysbus.c +++ b/hw/usb/hcd-xhci-sysbus.c @@ -33,12 +33,9 @@ void xhci_sysbus_reset(DeviceState *dev) static void xhci_sysbus_realize(DeviceState *dev, Error **errp) { XHCISysbusState *s = XHCI_SYSBUS(dev); - Error *err = NULL; object_property_set_link(OBJECT(&s->xhci), "host", OBJECT(s), NULL); - object_property_set_bool(OBJECT(&s->xhci), "realized", true, &err); - if (err) { - error_propagate(errp, err); + if (!qdev_realize(DEVICE(&s->xhci), NULL, errp)) { return; } s->irq = g_new0(qemu_irq, s->xhci.numintrs); From 96b66e55755a81f9e9e959e5f8a3d3aad9949167 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 18 Jan 2021 19:11:15 +0100 Subject: [PATCH 4/8] hw/usb/hcd-xhci: Fix extraneous format-truncation error on 32-bit hosts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For some reason the assert() added in commit ccb799313a5 ("hw/usb: avoid format truncation warning when formatting port name") does not fix when building with GCC 10. KISS and expand the buffer by 4 bytes to silent the following error when using GCC 10.2.1 on Fedora 33: hw/usb/hcd-xhci.c: In function 'usb_xhci_realize': hw/usb/hcd-xhci.c:3309:54: error: '%d' directive output may be truncated writing between 1 and 8 bytes into a region of size 5 [-Werror=format-truncation=] 3309 | snprintf(port->name, sizeof(port->name), "usb2 port #%d", i+1); | ^~~~~~~~~~~~~~~ hw/usb/hcd-xhci.c:3309:54: note: directive argument in the range [1, 89478486] In file included from /usr/include/stdio.h:866, from include/qemu/osdep.h:85, from hw/usb/hcd-xhci.c:22: /usr/include/bits/stdio2.h:70:10: note: '__builtin___snprintf_chk' output between 13 and 20 bytes into a destination of size 16 70 | return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 71 | __bos (__s), __fmt, __va_arg_pack ()); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ hw/usb/hcd-xhci.c:3323:54: error: '%d' directive output may be truncated writing between 1 and 8 bytes into a region of size 5 [-Werror=format-truncation=] 3323 | snprintf(port->name, sizeof(port->name), "usb3 port #%d", i+1); | ^~~~~~~~~~~~~~~ hw/usb/hcd-xhci.c:3323:54: note: directive argument in the range [1, 89478486] In file included from /usr/include/stdio.h:866, from include/qemu/osdep.h:85, from hw/usb/hcd-xhci.c:22: /usr/include/bits/stdio2.h:70:10: note: '__builtin___snprintf_chk' output between 13 and 20 bytes into a destination of size 16 70 | return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 71 | __bos (__s), __fmt, __va_arg_pack ()); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ cc1: all warnings being treated as errors Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20210118181115.313742-1-philmd@redhat.com> Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/usb/hcd-xhci.h b/hw/usb/hcd-xhci.h index 02ebd76450..7bba361f3b 100644 --- a/hw/usb/hcd-xhci.h +++ b/hw/usb/hcd-xhci.h @@ -128,7 +128,7 @@ typedef struct XHCIPort { uint32_t portnr; USBPort *uport; uint32_t speedmask; - char name[16]; + char name[20]; MemoryRegion mem; } XHCIPort; From 2e8f72acb0e948566c129d3e819cd77b9a8789ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 20 Jan 2021 16:35:21 +0100 Subject: [PATCH 5/8] scsi/utils: Add INVALID_PARAM_VALUE sense code definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Eric Blake Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20210120153522.1173897-3-philmd@redhat.com> Signed-off-by: Gerd Hoffmann --- include/scsi/utils.h | 2 ++ scsi/utils.c | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/include/scsi/utils.h b/include/scsi/utils.h index fbc5588279..096489c6cd 100644 --- a/include/scsi/utils.h +++ b/include/scsi/utils.h @@ -57,6 +57,8 @@ extern const struct SCSISense sense_code_LBA_OUT_OF_RANGE; extern const struct SCSISense sense_code_INVALID_FIELD; /* Illegal request, Invalid field in parameter list */ extern const struct SCSISense sense_code_INVALID_PARAM; +/* Illegal request, Invalid value in parameter list */ +extern const struct SCSISense sense_code_INVALID_PARAM_VALUE; /* Illegal request, Parameter list length error */ extern const struct SCSISense sense_code_INVALID_PARAM_LEN; /* Illegal request, LUN not supported */ diff --git a/scsi/utils.c b/scsi/utils.c index b37c283014..793c3a6b9c 100644 --- a/scsi/utils.c +++ b/scsi/utils.c @@ -197,6 +197,11 @@ const struct SCSISense sense_code_INVALID_PARAM = { .key = ILLEGAL_REQUEST, .asc = 0x26, .ascq = 0x00 }; +/* Illegal request, Invalid value in parameter list */ +const struct SCSISense sense_code_INVALID_PARAM_VALUE = { + .key = ILLEGAL_REQUEST, .asc = 0x26, .ascq = 0x01 +}; + /* Illegal request, Parameter list length error */ const struct SCSISense sense_code_INVALID_PARAM_LEN = { .key = ILLEGAL_REQUEST, .asc = 0x1a, .ascq = 0x00 From d755cb9696e8aa16e850ac5f0b908015520cd395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 20 Jan 2021 16:35:22 +0100 Subject: [PATCH 6/8] hw/usb/dev-uas: Report command additional adb length as unsupported MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are not ready to handle additional CDB data. If a guest sends a packet with such additional data, report the command parameter as not supported. Specify a size (of 1 byte) for the add_cdb member we are not using, to fix the following warning: usb/dev-uas.c:157:31: error: field 'status' with variable sized type 'uas_iu' not at the end of a struct or class is a GNU extension [-Werror,-Wgnu-variable-sized-type-not-at-end] uas_iu status; ^ Reported-by: Ed Maste Reported-by: Daniele Buono Reported-by: Han Han Reviewed-by: Eric Blake Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20210120153522.1173897-4-philmd@redhat.com> Signed-off-by: Gerd Hoffmann --- hw/usb/dev-uas.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c index cec071d96c..a51402bc0b 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -16,6 +16,7 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/module.h" +#include "qemu/log.h" #include "hw/usb.h" #include "migration/vmstate.h" @@ -70,7 +71,7 @@ typedef struct { uint8_t reserved_2; uint64_t lun; uint8_t cdb[16]; - uint8_t add_cdb[]; + uint8_t add_cdb[1]; /* not supported by QEMU */ } QEMU_PACKED uas_iu_command; typedef struct { @@ -700,6 +701,11 @@ static void usb_uas_command(UASDevice *uas, uas_iu *iu) uint32_t len; uint16_t tag = be16_to_cpu(iu->hdr.tag); + if (iu->command.add_cdb_length > 0) { + qemu_log_mask(LOG_UNIMP, "additional adb length not yet supported\n"); + goto unsupported_len; + } + if (uas_using_streams(uas) && tag > UAS_MAX_STREAMS) { goto invalid_tag; } @@ -735,6 +741,10 @@ static void usb_uas_command(UASDevice *uas, uas_iu *iu) } return; +unsupported_len: + usb_uas_queue_fake_sense(uas, tag, sense_code_INVALID_PARAM_VALUE); + return; + invalid_tag: usb_uas_queue_fake_sense(uas, tag, sense_code_INVALID_TAG); return; From 0f6dba145a4be998e0e5e4ccd94d9df8609eb327 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 19 Jan 2021 20:44:51 +0100 Subject: [PATCH 7/8] usb: add pcap support. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Log all traffic of a specific usb device to a pcap file for later inspection. File format is compatible with linux usb monitor. Usage: qemu -device usb-${somedevice},pcap=file.pcap wireshark file.pcap Signed-off-by: Gerd Hoffmann Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20210119194452.2148048-1-kraxel@redhat.com> Signed-off-by: Gerd Hoffmann --- hw/usb/bus.c | 16 +++ hw/usb/core.c | 17 +++ hw/usb/meson.build | 1 + hw/usb/pcap.c | 251 +++++++++++++++++++++++++++++++++++++++++++++ include/hw/usb.h | 8 ++ 5 files changed, 293 insertions(+) create mode 100644 hw/usb/pcap.c diff --git a/hw/usb/bus.c b/hw/usb/bus.c index 2b11041451..064f94e9c3 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -23,6 +23,7 @@ static Property usb_props[] = { USB_DEV_FLAG_FULL_PATH, true), DEFINE_PROP_BIT("msos-desc", USBDevice, flags, USB_DEV_FLAG_MSOS_DESC_ENABLE, true), + DEFINE_PROP_STRING("pcap", USBDevice, pcap_filename), DEFINE_PROP_END_OF_LIST() }; @@ -270,6 +271,17 @@ static void usb_qdev_realize(DeviceState *qdev, Error **errp) return; } } + + if (dev->pcap_filename) { + int fd = qemu_open_old(dev->pcap_filename, O_CREAT | O_WRONLY | O_TRUNC, 0666); + if (fd < 0) { + error_setg(errp, "open %s failed", dev->pcap_filename); + usb_qdev_unrealize(qdev); + return; + } + dev->pcap = fdopen(fd, "w"); + usb_pcap_init(dev->pcap); + } } static void usb_qdev_unrealize(DeviceState *qdev) @@ -283,6 +295,10 @@ static void usb_qdev_unrealize(DeviceState *qdev) g_free(s); } + if (dev->pcap) { + fclose(dev->pcap); + } + if (dev->attached) { usb_device_detach(dev); } diff --git a/hw/usb/core.c b/hw/usb/core.c index c895522a1d..975f76250a 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -154,6 +154,7 @@ static void do_token_setup(USBDevice *s, USBPacket *p) index = (s->setup_buf[5] << 8) | s->setup_buf[4]; if (s->setup_buf[0] & USB_DIR_IN) { + usb_pcap_ctrl(p, true); usb_device_handle_control(s, p, request, value, index, s->setup_len, s->data_buf); if (p->status == USB_RET_ASYNC) { @@ -190,6 +191,7 @@ static void do_token_in(USBDevice *s, USBPacket *p) switch(s->setup_state) { case SETUP_STATE_ACK: if (!(s->setup_buf[0] & USB_DIR_IN)) { + usb_pcap_ctrl(p, true); usb_device_handle_control(s, p, request, value, index, s->setup_len, s->data_buf); if (p->status == USB_RET_ASYNC) { @@ -197,6 +199,7 @@ static void do_token_in(USBDevice *s, USBPacket *p) } s->setup_state = SETUP_STATE_IDLE; p->actual_length = 0; + usb_pcap_ctrl(p, false); } break; @@ -215,6 +218,7 @@ static void do_token_in(USBDevice *s, USBPacket *p) } s->setup_state = SETUP_STATE_IDLE; p->status = USB_RET_STALL; + usb_pcap_ctrl(p, false); break; default: @@ -230,6 +234,7 @@ static void do_token_out(USBDevice *s, USBPacket *p) case SETUP_STATE_ACK: if (s->setup_buf[0] & USB_DIR_IN) { s->setup_state = SETUP_STATE_IDLE; + usb_pcap_ctrl(p, false); /* transfer OK */ } else { /* ignore additional output */ @@ -251,6 +256,7 @@ static void do_token_out(USBDevice *s, USBPacket *p) } s->setup_state = SETUP_STATE_IDLE; p->status = USB_RET_STALL; + usb_pcap_ctrl(p, false); break; default: @@ -288,6 +294,7 @@ static void do_parameter(USBDevice *s, USBPacket *p) usb_packet_copy(p, s->data_buf, s->setup_len); } + usb_pcap_ctrl(p, true); usb_device_handle_control(s, p, request, value, index, s->setup_len, s->data_buf); if (p->status == USB_RET_ASYNC) { @@ -301,6 +308,7 @@ static void do_parameter(USBDevice *s, USBPacket *p) p->actual_length = 0; usb_packet_copy(p, s->data_buf, s->setup_len); } + usb_pcap_ctrl(p, false); } /* ctrl complete function for devices which use usb_generic_handle_packet and @@ -311,6 +319,7 @@ void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p) { if (p->status < 0) { s->setup_state = SETUP_STATE_IDLE; + usb_pcap_ctrl(p, false); } switch (s->setup_state) { @@ -325,6 +334,7 @@ void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p) case SETUP_STATE_ACK: s->setup_state = SETUP_STATE_IDLE; p->actual_length = 0; + usb_pcap_ctrl(p, false); break; case SETUP_STATE_PARAM: @@ -359,12 +369,14 @@ USBDevice *usb_find_device(USBPort *port, uint8_t addr) static void usb_process_one(USBPacket *p) { USBDevice *dev = p->ep->dev; + bool nak; /* * Handlers expect status to be initialized to USB_RET_SUCCESS, but it * can be USB_RET_NAK here from a previous usb_process_one() call, * or USB_RET_ASYNC from going through usb_queue_one(). */ + nak = (p->status == USB_RET_NAK); p->status = USB_RET_SUCCESS; if (p->ep->nr == 0) { @@ -388,6 +400,9 @@ static void usb_process_one(USBPacket *p) } } else { /* data pipe */ + if (!nak) { + usb_pcap_data(p, true); + } usb_device_handle_data(dev, p); } } @@ -439,6 +454,7 @@ void usb_handle_packet(USBDevice *dev, USBPacket *p) assert(p->stream || !p->ep->pipeline || QTAILQ_EMPTY(&p->ep->queue)); if (p->status != USB_RET_NAK) { + usb_pcap_data(p, false); usb_packet_set_state(p, USB_PACKET_COMPLETE); } } @@ -458,6 +474,7 @@ void usb_packet_complete_one(USBDevice *dev, USBPacket *p) (p->short_not_ok && (p->actual_length < p->iov.size))) { ep->halted = true; } + usb_pcap_data(p, false); usb_packet_set_state(p, USB_PACKET_COMPLETE); QTAILQ_REMOVE(&ep->queue, p, queue); dev->port->ops->complete(dev->port, p); diff --git a/hw/usb/meson.build b/hw/usb/meson.build index f46c6b6655..653192cff6 100644 --- a/hw/usb/meson.build +++ b/hw/usb/meson.build @@ -5,6 +5,7 @@ softmmu_ss.add(files( 'bus.c', 'combined-packet.c', 'core.c', + 'pcap.c', 'libhw.c' )) diff --git a/hw/usb/pcap.c b/hw/usb/pcap.c new file mode 100644 index 0000000000..4350989d3a --- /dev/null +++ b/hw/usb/pcap.c @@ -0,0 +1,251 @@ +/* + * usb packet capture + * + * Copyright (c) 2021 Gerd Hoffmann + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/usb.h" + +#define PCAP_MAGIC 0xa1b2c3d4 +#define PCAP_MAJOR 2 +#define PCAP_MINOR 4 + +/* https://wiki.wireshark.org/Development/LibpcapFileFormat */ + +struct pcap_hdr { + uint32_t magic_number; /* magic number */ + uint16_t version_major; /* major version number */ + uint16_t version_minor; /* minor version number */ + int32_t thiszone; /* GMT to local correction */ + uint32_t sigfigs; /* accuracy of timestamps */ + uint32_t snaplen; /* max length of captured packets, in octets */ + uint32_t network; /* data link type */ +}; + +struct pcaprec_hdr { + uint32_t ts_sec; /* timestamp seconds */ + uint32_t ts_usec; /* timestamp microseconds */ + uint32_t incl_len; /* number of octets of packet saved in file */ + uint32_t orig_len; /* actual length of packet */ +}; + +/* https://www.tcpdump.org/linktypes.html */ +/* linux: Documentation/usb/usbmon.rst */ +/* linux: drivers/usb/mon/mon_bin.c */ + +#define LINKTYPE_USB_LINUX 189 /* first 48 bytes only */ +#define LINKTYPE_USB_LINUX_MMAPPED 220 /* full 64 byte header */ + +struct usbmon_packet { + uint64_t id; /* 0: URB ID - from submission to callback */ + unsigned char type; /* 8: Same as text; extensible. */ + unsigned char xfer_type; /* ISO (0), Intr, Control, Bulk (3) */ + unsigned char epnum; /* Endpoint number and transfer direction */ + unsigned char devnum; /* Device address */ + uint16_t busnum; /* 12: Bus number */ + char flag_setup; /* 14: Same as text */ + char flag_data; /* 15: Same as text; Binary zero is OK. */ + int64_t ts_sec; /* 16: gettimeofday */ + int32_t ts_usec; /* 24: gettimeofday */ + int32_t status; /* 28: */ + unsigned int length; /* 32: Length of data (submitted or actual) */ + unsigned int len_cap; /* 36: Delivered length */ + union { /* 40: */ + unsigned char setup[8]; /* Only for Control S-type */ + struct iso_rec { /* Only for ISO */ + int32_t error_count; + int32_t numdesc; + } iso; + } s; + int32_t interval; /* 48: Only for Interrupt and ISO */ + int32_t start_frame; /* 52: For ISO */ + uint32_t xfer_flags; /* 56: copy of URB's transfer_flags */ + uint32_t ndesc; /* 60: Actual number of ISO descriptors */ +}; /* 64 total length */ + +/* ------------------------------------------------------------------------ */ + +#define CTRL_LEN 4096 +#define DATA_LEN 256 + +static int usbmon_status(USBPacket *p) +{ + switch (p->status) { + case USB_RET_SUCCESS: + return 0; + case USB_RET_NODEV: + return -19; /* -ENODEV */ + default: + return -121; /* -EREMOTEIO */ + } +} + +static unsigned int usbmon_epnum(USBPacket *p) +{ + unsigned epnum = 0; + + epnum |= p->ep->nr; + epnum |= (p->pid == USB_TOKEN_IN) ? 0x80 : 0; + return epnum; +} + +static unsigned char usbmon_xfer_type[] = { + [USB_ENDPOINT_XFER_CONTROL] = 2, + [USB_ENDPOINT_XFER_ISOC] = 0, + [USB_ENDPOINT_XFER_BULK] = 3, + [USB_ENDPOINT_XFER_INT] = 1, +}; + +static void do_usb_pcap_header(FILE *fp, struct usbmon_packet *packet) +{ + struct pcaprec_hdr header; + struct timeval tv; + + gettimeofday(&tv, NULL); + packet->ts_sec = tv.tv_sec; + packet->ts_usec = tv.tv_usec; + + header.ts_sec = packet->ts_sec; + header.ts_usec = packet->ts_usec; + header.incl_len = packet->len_cap; + header.orig_len = packet->length + sizeof(*packet); + fwrite(&header, sizeof(header), 1, fp); + fwrite(packet, sizeof(*packet), 1, fp); +} + +static void do_usb_pcap_ctrl(FILE *fp, USBPacket *p, bool setup) +{ + USBDevice *dev = p->ep->dev; + bool in = dev->setup_buf[0] & USB_DIR_IN; + struct usbmon_packet packet = { + .id = 0, + .type = setup ? 'S' : 'C', + .xfer_type = usbmon_xfer_type[USB_ENDPOINT_XFER_CONTROL], + .epnum = in ? 0x80 : 0, + .devnum = dev->addr, + .flag_data = '=', + .length = dev->setup_len, + }; + int data_len = dev->setup_len; + + if (data_len > CTRL_LEN) { + data_len = CTRL_LEN; + } + if (setup) { + memcpy(packet.s.setup, dev->setup_buf, 8); + } else { + packet.status = usbmon_status(p); + } + + if (in && setup) { + packet.flag_data = '<'; + packet.length = 0; + data_len = 0; + } + if (!in && !setup) { + packet.flag_data = '>'; + packet.length = 0; + data_len = 0; + } + + packet.len_cap = data_len + sizeof(packet); + do_usb_pcap_header(fp, &packet); + if (data_len) { + fwrite(dev->data_buf, data_len, 1, fp); + } + + fflush(fp); +} + +static void do_usb_pcap_data(FILE *fp, USBPacket *p, bool setup) +{ + struct usbmon_packet packet = { + .id = p->id, + .type = setup ? 'S' : 'C', + .xfer_type = usbmon_xfer_type[p->ep->type], + .epnum = usbmon_epnum(p), + .devnum = p->ep->dev->addr, + .flag_data = '=', + .length = p->iov.size, + }; + int data_len = p->iov.size; + + if (p->ep->nr == 0) { + /* ignore control pipe packets */ + return; + } + + if (data_len > DATA_LEN) { + data_len = DATA_LEN; + } + if (!setup) { + packet.status = usbmon_status(p); + if (packet.length > p->actual_length) { + packet.length = p->actual_length; + } + if (data_len > p->actual_length) { + data_len = p->actual_length; + } + } + + if (p->pid == USB_TOKEN_IN && setup) { + packet.flag_data = '<'; + packet.length = 0; + data_len = 0; + } + if (p->pid == USB_TOKEN_OUT && !setup) { + packet.flag_data = '>'; + packet.length = 0; + data_len = 0; + } + + packet.len_cap = data_len + sizeof(packet); + do_usb_pcap_header(fp, &packet); + if (data_len) { + void *buf = g_malloc(data_len); + iov_to_buf(p->iov.iov, p->iov.niov, 0, buf, data_len); + fwrite(buf, data_len, 1, fp); + g_free(buf); + } + + fflush(fp); +} + +void usb_pcap_init(FILE *fp) +{ + struct pcap_hdr header = { + .magic_number = PCAP_MAGIC, + .version_major = 2, + .version_minor = 4, + .snaplen = MAX(CTRL_LEN, DATA_LEN) + sizeof(struct usbmon_packet), + .network = LINKTYPE_USB_LINUX_MMAPPED, + }; + + fwrite(&header, sizeof(header), 1, fp); +} + +void usb_pcap_ctrl(USBPacket *p, bool setup) +{ + FILE *fp = p->ep->dev->pcap; + + if (!fp) { + return; + } + + do_usb_pcap_ctrl(fp, p, setup); +} + +void usb_pcap_data(USBPacket *p, bool setup) +{ + FILE *fp = p->ep->dev->pcap; + + if (!fp) { + return; + } + + do_usb_pcap_data(fp, p, setup); +} diff --git a/include/hw/usb.h b/include/hw/usb.h index a70a72e917..abfbfc5284 100644 --- a/include/hw/usb.h +++ b/include/hw/usb.h @@ -231,6 +231,9 @@ struct USBDevice { void *opaque; uint32_t flags; + char *pcap_filename; + FILE *pcap; + /* Actual connected speed */ int speed; /* Supported speeds, not in info because it may be variable (hostdevs) */ @@ -570,4 +573,9 @@ int usb_get_quirks(uint16_t vendor_id, uint16_t product_id, uint8_t interface_class, uint8_t interface_subclass, uint8_t interface_protocol); +/* pcap.c */ +void usb_pcap_init(FILE *fp); +void usb_pcap_ctrl(USBPacket *p, bool setup); +void usb_pcap_data(USBPacket *p, bool setup); + #endif From 2980a316734c420e7398aec026909dcfc8614c1d Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 21 Jan 2021 16:08:32 +0100 Subject: [PATCH 8/8] usb-host: map LIBUSB_SPEED_SUPER_PLUS to USB_SPEED_SUPER Handle host superspeedplus (usb 3.1+) devices like superspeed (usb 3.0) devices. That is enough to get them handled properly by xhci. They show up as superspeed devices inside the guest, but should be able to actually run at higher speeds. Reported-by: Angel Pagan Tested-by: Angel Pagan Signed-off-by: Gerd Hoffmann Message-Id: <20210121150832.3564097-1-kraxel@redhat.com> --- hw/usb/host-libusb.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 295d20227a..7dde3d1206 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -179,6 +179,9 @@ static void usb_host_attach_kernel(USBHostDevice *s); #if LIBUSB_API_VERSION >= 0x01000103 # define HAVE_STREAMS 1 #endif +#if LIBUSB_API_VERSION >= 0x01000106 +# define HAVE_SUPER_PLUS 1 +#endif static const char *speed_name[] = { [LIBUSB_SPEED_UNKNOWN] = "?", @@ -186,6 +189,9 @@ static const char *speed_name[] = { [LIBUSB_SPEED_FULL] = "12", [LIBUSB_SPEED_HIGH] = "480", [LIBUSB_SPEED_SUPER] = "5000", +#ifdef HAVE_SUPER_PLUS + [LIBUSB_SPEED_SUPER_PLUS] = "5000+", +#endif }; static const unsigned int speed_map[] = { @@ -193,6 +199,9 @@ static const unsigned int speed_map[] = { [LIBUSB_SPEED_FULL] = USB_SPEED_FULL, [LIBUSB_SPEED_HIGH] = USB_SPEED_HIGH, [LIBUSB_SPEED_SUPER] = USB_SPEED_SUPER, +#ifdef HAVE_SUPER_PLUS + [LIBUSB_SPEED_SUPER_PLUS] = USB_SPEED_SUPER, +#endif }; static const unsigned int status_map[] = { @@ -964,9 +973,15 @@ static int usb_host_open(USBHostDevice *s, libusb_device *dev, int hostfd) libusb_speed = LIBUSB_SPEED_HIGH; break; case 5: /* super */ - case 6: /* super plus */ libusb_speed = LIBUSB_SPEED_SUPER; break; + case 6: /* super plus */ +#ifdef HAVE_SUPER_PLUS + libusb_speed = LIBUSB_SPEED_SUPER_PLUS; +#else + libusb_speed = LIBUSB_SPEED_SUPER; +#endif + break; } } #endif