diff options
Diffstat (limited to 'hw')
-rw-r--r-- | hw/usb/bus.c | 16 | ||||
-rw-r--r-- | hw/usb/ccid-card-passthru.c | 2 | ||||
-rw-r--r-- | hw/usb/core.c | 21 | ||||
-rw-r--r-- | hw/usb/dev-smartcard-reader.c | 8 | ||||
-rw-r--r-- | hw/usb/dev-uas.c | 12 | ||||
-rw-r--r-- | hw/usb/hcd-ehci.c | 4 | ||||
-rw-r--r-- | hw/usb/hcd-xhci-pci.c | 4 | ||||
-rw-r--r-- | hw/usb/hcd-xhci-sysbus.c | 5 | ||||
-rw-r--r-- | hw/usb/hcd-xhci.h | 2 | ||||
-rw-r--r-- | hw/usb/host-libusb.c | 18 | ||||
-rw-r--r-- | hw/usb/meson.build | 1 | ||||
-rw-r--r-- | hw/usb/pcap.c | 251 | ||||
-rw-r--r-- | hw/vfio/ccw.c | 40 |
13 files changed, 361 insertions, 23 deletions
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/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..975f76250a 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; @@ -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: @@ -277,7 +283,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; @@ -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/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/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; 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; } 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); 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; diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index b950501d10..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[] = { @@ -941,7 +950,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 @@ -963,8 +973,14 @@ static int usb_host_open(USBHostDevice *s, libusb_device *dev, int hostfd) libusb_speed = LIBUSB_SPEED_HIGH; break; case 5: /* super */ + 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; } } 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 <kraxel@redhat.com> + * + * 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/hw/vfio/ccw.c b/hw/vfio/ccw.c index d2755d7fc5..bc78a0ad76 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -49,6 +49,7 @@ struct VFIOCCWDevice { struct ccw_crw_region *crw_region; EventNotifier io_notifier; EventNotifier crw_notifier; + EventNotifier req_notifier; bool force_orb_pfch; bool warned_orb_pfch; }; @@ -287,6 +288,21 @@ static void vfio_ccw_crw_read(VFIOCCWDevice *vcdev) } while (1); } +static void vfio_ccw_req_notifier_handler(void *opaque) +{ + VFIOCCWDevice *vcdev = opaque; + Error *err = NULL; + + if (!event_notifier_test_and_clear(&vcdev->req_notifier)) { + return; + } + + qdev_unplug(DEVICE(vcdev), &err); + if (err) { + warn_reportf_err(err, VFIO_MSG_PREFIX, vcdev->vdev.name); + } +} + static void vfio_ccw_crw_notifier_handler(void *opaque) { VFIOCCWDevice *vcdev = opaque; @@ -386,6 +402,10 @@ static void vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev, notifier = &vcdev->crw_notifier; fd_read = vfio_ccw_crw_notifier_handler; break; + case VFIO_CCW_REQ_IRQ_INDEX: + notifier = &vcdev->req_notifier; + fd_read = vfio_ccw_req_notifier_handler; + break; default: error_setg(errp, "vfio: Unsupported device irq(%d)", irq); return; @@ -440,6 +460,9 @@ static void vfio_ccw_unregister_irq_notifier(VFIOCCWDevice *vcdev, case VFIO_CCW_CRW_IRQ_INDEX: notifier = &vcdev->crw_notifier; break; + case VFIO_CCW_REQ_IRQ_INDEX: + notifier = &vcdev->req_notifier; + break; default: error_report("vfio: Unsupported device irq(%d)", irq); return; @@ -661,20 +684,28 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX, &err); if (err) { - goto out_notifier_err; + goto out_io_notifier_err; } if (vcdev->crw_region) { vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_CRW_IRQ_INDEX, &err); if (err) { - vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX); - goto out_notifier_err; + goto out_crw_notifier_err; } } + vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_REQ_IRQ_INDEX, &err); + if (err) { + goto out_req_notifier_err; + } + return; -out_notifier_err: +out_req_notifier_err: + vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_CRW_IRQ_INDEX); +out_crw_notifier_err: + vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX); +out_io_notifier_err: vfio_ccw_put_region(vcdev); out_region_err: vfio_ccw_put_device(vcdev); @@ -696,6 +727,7 @@ static void vfio_ccw_unrealize(DeviceState *dev) S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(cdev); VFIOGroup *group = vcdev->vdev.group; + vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_REQ_IRQ_INDEX); vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_CRW_IRQ_INDEX); vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX); vfio_ccw_put_region(vcdev); |