diff mbox series

[RFC,v4,6/6] hmp: add x-debug-virtio commands

Message ID 20200507134800.10837-7-lvivier@redhat.com (mailing list archive)
State New, archived
Headers show
Series hmp,qmp: Add some commands to introspect virtio devices | expand

Commit Message

Laurent Vivier May 7, 2020, 1:48 p.m. UTC
This patch implements HMP version of the virtio QMP commands

Signed-off-by: Laurent Vivier <lvivier@redhat.com>
---
 Makefile                |   2 +-
 Makefile.target         |   7 +-
 docs/system/monitor.rst |   2 +
 hmp-commands-virtio.hx  | 160 +++++++++++++++++++++++++++++++++
 hmp-commands.hx         |  10 +++
 hw/virtio/virtio.c      | 193 +++++++++++++++++++++++++++++++++++++++-
 include/monitor/hmp.h   |   4 +
 monitor/misc.c          |  17 ++++
 8 files changed, 391 insertions(+), 4 deletions(-)
 create mode 100644 hmp-commands-virtio.hx

Comments

Dr. David Alan Gilbert May 13, 2020, 10:51 a.m. UTC | #1
* Laurent Vivier (lvivier@redhat.com) wrote:
> This patch implements HMP version of the virtio QMP commands
> 
> Signed-off-by: Laurent Vivier <lvivier@redhat.com>

Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>

With a thought below....

> ---
>  Makefile                |   2 +-
>  Makefile.target         |   7 +-
>  docs/system/monitor.rst |   2 +
>  hmp-commands-virtio.hx  | 160 +++++++++++++++++++++++++++++++++
>  hmp-commands.hx         |  10 +++
>  hw/virtio/virtio.c      | 193 +++++++++++++++++++++++++++++++++++++++-
>  include/monitor/hmp.h   |   4 +
>  monitor/misc.c          |  17 ++++
>  8 files changed, 391 insertions(+), 4 deletions(-)
>  create mode 100644 hmp-commands-virtio.hx
> 
> diff --git a/Makefile b/Makefile
> index 34275f57c9cb..feb300ebb2d4 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -1099,7 +1099,7 @@ $(MANUAL_BUILDDIR)/interop/index.html: $(call manual-deps,interop)
>  $(MANUAL_BUILDDIR)/specs/index.html: $(call manual-deps,specs)
>  	$(call build-manual,specs,html)
>  
> -$(MANUAL_BUILDDIR)/system/index.html: $(call manual-deps,system) $(SRC_PATH)/hmp-commands.hx $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/qemu-options.hx
> +$(MANUAL_BUILDDIR)/system/index.html: $(call manual-deps,system) $(SRC_PATH)/hmp-commands.hx $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/hmp-commands-virtio.hx
>  	$(call build-manual,system,html)
>  
>  $(MANUAL_BUILDDIR)/tools/index.html: $(call manual-deps,tools) $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/docs/qemu-option-trace.rst.inc
> diff --git a/Makefile.target b/Makefile.target
> index 8ed1eba95b9c..66d3ff9bc350 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -171,7 +171,7 @@ else
>  obj-y += hw/$(TARGET_BASE_ARCH)/
>  endif
>  
> -generated-files-y += hmp-commands.h hmp-commands-info.h
> +generated-files-y += hmp-commands.h hmp-commands-info.h hmp-commands-virtio.h
>  generated-files-y += config-devices.h
>  
>  endif # CONFIG_SOFTMMU
> @@ -220,10 +220,13 @@ hmp-commands.h: $(SRC_PATH)/hmp-commands.hx $(SRC_PATH)/scripts/hxtool
>  hmp-commands-info.h: $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/scripts/hxtool
>  	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$(TARGET_DIR)$@")
>  
> +hmp-commands-virtio.h: $(SRC_PATH)/hmp-commands-virtio.hx $(SRC_PATH)/scripts/hxtool
> +	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$(TARGET_DIR)$@")
> +
>  clean: clean-target
>  	rm -f *.a *~ $(PROGS)
>  	rm -f $(shell find . -name '*.[od]')
> -	rm -f hmp-commands.h gdbstub-xml.c
> +	rm -f hmp-commands.h hmp-commands-virtio.h gdbstub-xml.c
>  	rm -f trace/generated-helpers.c trace/generated-helpers.c-timestamp
>  ifdef CONFIG_TRACE_SYSTEMTAP
>  	rm -f *.stp
> diff --git a/docs/system/monitor.rst b/docs/system/monitor.rst
> index 0bcd5da21644..985c3f51ffe7 100644
> --- a/docs/system/monitor.rst
> +++ b/docs/system/monitor.rst
> @@ -21,6 +21,8 @@ The following commands are available:
>  
>  .. hxtool-doc:: hmp-commands.hx
>  
> +.. hxtool-doc:: hmp-commands-virtio.hx
> +
>  .. hxtool-doc:: hmp-commands-info.hx
>  
>  Integer expressions
> diff --git a/hmp-commands-virtio.hx b/hmp-commands-virtio.hx
> new file mode 100644
> index 000000000000..14cb14bfcc70
> --- /dev/null
> +++ b/hmp-commands-virtio.hx
> @@ -0,0 +1,160 @@
> +HXCOMM Use DEFHEADING() to define headings in both help text and rST.
> +HXCOMM Text between SRST and ERST is copied to the rST version and
> +HXCOMM discarded from C version.
> +HXCOMM DEF(command, args, callback, arg_string, help) is used to construct
> +HXCOMM monitor info commands
> +HXCOMM HXCOMM can be used for comments, discarded from both rST and C.
> +HXCOMM
> +HXCOMM In this file, generally SRST fragments should have two extra
> +HXCOMM spaces of indent, so that the documentation list item for "x-debug-virtio cmd"
> +HXCOMM appears inside the documentation list item for the top level
> +HXCOMM "x-debug-virtio" documentation entry. The exception is the first SRST
> +HXCOMM fragment that defines that top level entry.
> +
> +SRST
> +``x-debug-virtio`` *subcommand*
> +  Show various information about virtio.
> +
> +  Example:
> +
> +  List all sub-commands::
> +
> +    (qemu) x-debug-virtio
> +    x-debug-virtio query  -- List all available virtio devices
> +    x-debug-virtio status path -- Display status of a given virtio device
> +    x-debug-virtio queue-status path queue -- Display status of a given virtio queue
> +    x-debug-virtio queue-element path queue [index] -- Display element of a given virtio queue
> +
> +ERST
> +
> +    {
> +        .name       = "query",
> +        .args_type  = "",
> +        .params     = "",
> +        .help       = "List all available virtio devices",
> +        .cmd        = hmp_x_debug_virtio_query,
> +        .flags      = "p",
> +    },
> +
> +SRST
> +  ``x-debug-virtio query``
> +    List all available virtio devices
> +
> +    Example:
> +
> +    List all available virtio devices in the machine::
> +
> +      (qemu) x-debug-virtio query
> +      /machine/peripheral-anon/device[3]/virtio-backend [virtio-net]
> +      /machine/peripheral-anon/device[1]/virtio-backend [virtio-serial]
> +      /machine/peripheral-anon/device[0]/virtio-backend [virtio-blk]
> +
> +ERST
> +
> +    {
> +        .name       = "status",
> +        .args_type  = "path:s",
> +        .params     = "path",
> +        .help       = "Display status of a given virtio device",
> +        .cmd        = hmp_x_debug_virtio_status,
> +        .flags      = "p",
> +    },
> +
> +SRST
> +  ``x-debug-virtio status`` *path*
> +    Display status of a given virtio device
> +
> +    Example:
> +
> +    Dump the status of the first virtio device::
> +
> +      (qemu) x-debug-virtio status /machine/peripheral-anon/device[3]/virtio-backend
> +      /machine/peripheral-anon/device[3]/virtio-backend:
> +        Device Id:        1
> +        Guest features:   event-idx, indirect-desc, version-1
> +                          ctrl-mac-addr, guest-announce, ctrl-vlan, ctrl-rx, ctrl-vq, status, mrg-rxbuf, host-ufo, host-ecn, host-tso6, host-tso4, guest-ufo, guest-ecn, guest-tso6, guest-tso4, mac, ctrl-guest-offloads, guest-csum, csum
> +        Host features:    event-idx, indirect-desc, bad-feature, version-1, any-layout, notify-on-empty
> +                          gso, ctrl-mac-addr, guest-announce, ctrl-rx-extra, ctrl-vlan, ctrl-rx, ctrl-vq, status, mrg-rxbuf, host-ufo, host-ecn, host-tso6, host-tso4, guest-ufo, guest-ecn, guest-tso6, guest-tso4, mac, ctrl-guest-offloads, guest-csum, csum
> +        Backend features:
> +        Endianness:       little
> +        VirtQueues:       3
> +
> +ERST
> +
> +    {
> +        .name       = "queue-status",
> +        .args_type  = "path:s,queue:i",
> +        .params     = "path queue",
> +        .help       = "Display status of a given virtio queue",
> +        .cmd        = hmp_x_debug_virtio_queue_status,
> +        .flags      = "p",
> +    },
> +
> +SRST
> +  ``x-debug-virtio queue-status`` *path* *queue*
> +    Display status of a given virtio queue
> +
> +    Example:
> +
> +    Dump the status of the first queue of the first virtio device::
> +
> +      (qemu) x-debug-virtio queue-status /machine/peripheral-anon/device[3]/virtio-backend 0
> +      /machine/peripheral-anon/device[3]/virtio-backend:
> +        index:                0
> +        inuse:                0
> +        last_avail_idx:       61
> +        shadow_avail_idx:     292
> +        signalled_used:       61
> +        signalled_used_valid: 1
> +        VRing:
> +          num:         256
> +          num_default: 256
> +          align:       4096
> +          desc:        0x000000006c352000
> +          avail:       0x000000006c353000
> +          used:        0x000000006c353240
> +
> +ERST
> +
> +    {
> +        .name       = "queue-element",
> +        .args_type  = "path:s,queue:i,index:i?",
> +        .params     = "path queue [index]",
> +        .help       = "Display element of a given virtio queue",
> +        .cmd        = hmp_x_debug_virtio_queue_element,
> +        .flags      = "p",
> +    },
> +
> +SRST
> +  ``x-debug-virtio queue-element`` *path* *queue* [*index*]
> +    Display element of a given virtio queue
> +
> +    Example:
> +
> +    Dump the information of the head element of the first queue of
> +    the first virtio device::
> +
> +      (qemu) x-debug-virtio queue-element/machine/peripheral-anon/device[3]/virtio-backend 0
> +      index:  67
> +      ndescs: 1
> +      descs:  addr 0x6fe69800 len 1536 (write)
> +
> +      (qemu) xp/128bx 0x6fe69800
> +      000000006fe69800: 0x02 0x00 0x00 0x00 0x00 0x00 0x00 0x00
> +      000000006fe69808: 0x00 0x00 0x01 0x00 0x52 0x54 0x00 0x12
> +      000000006fe69810: 0x34 0x56 0x52 0x54 0x00 0x09 0x51 0xde
> +      000000006fe69818: 0x08 0x00 0x45 0x00 0x00 0x4c 0x8f 0x32
> +
> +    device[3] is a virtio-net device and we can see in the element buffer the
> +    MAC address of the card::
> +
> +      [root@localhost ~]# ip link show ens4
> +      2: ens4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP m0
> +          link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff
> +
> +    and the MAC address of the gateway::
> +
> +      [root@localhost ~]# arp -a
> +      _gateway (192.168.122.1) at 52:54:00:09:51:de [ether] on ens4
> +
> +ERST
> diff --git a/hmp-commands.hx b/hmp-commands.hx
> index 7f0f3974ad90..777761dc48d7 100644
> --- a/hmp-commands.hx
> +++ b/hmp-commands.hx
> @@ -1804,6 +1804,16 @@ SRST
>    Set QOM property *property* of object at location *path* to value *value*
>  ERST
>  
> +    {
> +        .name       = "x-debug-virtio",
> +        .args_type  = "name:S?",
> +        .params     = "[cmd]",
> +        .help       = "show various information about virtio",
> +        .cmd        = hmp_x_debug_virtio_help,
> +        .sub_table  = hmp_x_debug_virtio_cmds,
> +        .flags      = "p",
> +    },
> +
>      {
>          .name       = "info",
>          .args_type  = "item:s?",
> diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
> index 66dc2cef1b39..c3d6b783417e 100644
> --- a/hw/virtio/virtio.c
> +++ b/hw/virtio/virtio.c
> @@ -30,6 +30,9 @@
>  #include "sysemu/dma.h"
>  #include "sysemu/runstate.h"
>  #include "config-devices.h"
> +#include "monitor/hmp.h"
> +#include "monitor/monitor.h"
> +#include "qapi/qmp/qdict.h"
>  
>  static QTAILQ_HEAD(, VirtIODevice) virtio_list;
>  
> @@ -3861,6 +3864,32 @@ VirtioInfoList *qmp_x_debug_query_virtio(Error **errp)
>      return list;
>  }
>  
> +void hmp_x_debug_virtio_query(Monitor *mon, const QDict *qdict)
> +{
> +    Error *err = NULL;
> +    VirtioInfoList *list = qmp_x_debug_query_virtio(&err);
> +    VirtioInfoList *node;
> +
> +    if (err != NULL) {
> +        hmp_handle_error(mon, err);
> +        return;
> +    }
> +
> +    if (list == NULL) {
> +        monitor_printf(mon, "No VirtIO devices\n");
> +        return;
> +    }
> +
> +    node = list;
> +    while (node) {
> +        monitor_printf(mon, "%s [%s]\n", node->value->path,
> +                       VirtioType_str(node->value->type));
> +        node = node->next;
> +    }
> +
> +    qapi_free_VirtioInfoList(list);
> +}
> +
>  static VirtIODevice *virtio_device_find(const char *path)
>  {
>      VirtIODevice *vdev;
> @@ -3912,8 +3941,38 @@ VirtQueueStatus *qmp_x_debug_virtio_queue_status(const char *path,
>      return status;
>  }
>  
> +void hmp_x_debug_virtio_queue_status(Monitor *mon, const QDict *qdict)
> +{
> +    Error *err = NULL;
> +    const char *path = qdict_get_try_str(qdict, "path");
> +    int queue = qdict_get_int(qdict, "queue");
> +    VirtQueueStatus *s = qmp_x_debug_virtio_queue_status(path, queue, &err);
> +
> +    if (err != NULL) {
> +        hmp_handle_error(mon, err);
> +        return;
> +    }
> +    monitor_printf(mon, "%s:\n", path);
> +    monitor_printf(mon, "  index:                %d\n", s->queue_index);
> +    monitor_printf(mon, "  inuse:                %d\n", s->inuse);
> +    monitor_printf(mon, "  last_avail_idx:       %d\n", s->last_avail_idx);
> +    monitor_printf(mon, "  shadow_avail_idx:     %d\n", s->shadow_avail_idx);
> +    monitor_printf(mon, "  signalled_used:       %d\n", s->signalled_used);
> +    monitor_printf(mon, "  signalled_used_valid: %d\n",
> +                   s->signalled_used_valid);
> +    monitor_printf(mon, "  VRing:\n");
> +    monitor_printf(mon, "    num:         %"PRId64"\n", s->vring_num);
> +    monitor_printf(mon, "    num_default: %"PRId64"\n", s->vring_num_default);
> +    monitor_printf(mon, "    align:       %"PRId64"\n", s->vring_align);
> +    monitor_printf(mon, "    desc:        0x%016"PRIx64"\n", s->vring_desc);
> +    monitor_printf(mon, "    avail:       0x%016"PRIx64"\n", s->vring_avail);
> +    monitor_printf(mon, "    used:        0x%016"PRIx64"\n", s->vring_used);
> +
> +    qapi_free_VirtQueueStatus(s);
> +}
> +
>  #define CONVERT_FEATURES(type, map)                \
> -    ({                                           \
> +    ({                                             \
>          type *list = NULL;                         \
>          type *node;                                \
>          for (i = 0; map[i].virtio_bit != -1; i++) {\
> @@ -4033,6 +4092,92 @@ VirtioStatus *qmp_x_debug_virtio_status(const char* path, Error **errp)
>      return status;
>  }
>  
> +#define DUMP_FEATURES(type, field)                                         \
> +    do {                                                                   \
> +        type##FeatureList *list = features->device->u.field.data;          \
> +        if (list) {                                                        \
> +            monitor_printf(mon, "                    ");                   \
> +            while (list) {                                                 \
> +                monitor_printf(mon, "%s", type##Feature_str(list->value)); \
> +                list = list->next;                                         \
> +                if (list != NULL) {                                        \
> +                    monitor_printf(mon, ", ");                             \
> +                }                                                          \
> +            }                                                              \
> +            monitor_printf(mon, "\n");                                     \
> +        }                                                                  \
> +    } while (0)

It feels like you should be able to have an array of Feature_str's
indexed by VIRTIO_DEVICE_FEATURE_KIND_ enum, so that when a new
VIRTIO_DEVICE_FEATURE_KIND is added you don't need to fix this up.

Dave

> +
> +static void hmp_virtio_dump_features(Monitor *mon,
> +                                     VirtioStatusFeatures *features)
> +{
> +    VirtioTransportFeatureList *transport_list = features->transport;
> +    while (transport_list) {
> +        monitor_printf(mon, "%s",
> +                       VirtioTransportFeature_str(transport_list->value));
> +        transport_list = transport_list->next;
> +        if (transport_list != NULL) {
> +            monitor_printf(mon, ", ");
> +        }
> +    }
> +    monitor_printf(mon, "\n");
> +    switch (features->device->type) {
> +    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_SERIAL:
> +        DUMP_FEATURES(VirtioSerial, virtio_serial);
> +        break;
> +    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_BLK:
> +        DUMP_FEATURES(VirtioBlk, virtio_blk);
> +        break;
> +    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_GPU:
> +        DUMP_FEATURES(VirtioGpu, virtio_gpu);
> +        break;
> +    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_NET:
> +        DUMP_FEATURES(VirtioNet, virtio_net);
> +        break;
> +    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_SCSI:
> +        DUMP_FEATURES(VirtioScsi, virtio_scsi);
> +        break;
> +    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_BALLOON:
> +        DUMP_FEATURES(VirtioBalloon, virtio_balloon);
> +        break;
> +    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_IOMMU:
> +        DUMP_FEATURES(VirtioIommu, virtio_iommu);
> +        break;
> +    default:
> +        g_assert_not_reached();
> +    }
> +    if (features->unknown) {
> +        monitor_printf(mon, "                    unknown(0x%016"PRIx64")\n", \
> +                       features->unknown);
> +    }
> +}
> +
> +void hmp_x_debug_virtio_status(Monitor *mon, const QDict *qdict)
> +{
> +    Error *err = NULL;
> +    const char *path = qdict_get_try_str(qdict, "path");
> +    VirtioStatus *s = qmp_x_debug_virtio_status(path, &err);
> +
> +    if (err != NULL) {
> +        hmp_handle_error(mon, err);
> +        return;
> +    }
> +
> +    monitor_printf(mon, "%s:\n", path);
> +    monitor_printf(mon, "  Device Id:        %"PRId64"\n", s->device_id);
> +    monitor_printf(mon, "  Guest features:   ");
> +    hmp_virtio_dump_features(mon, s->guest_features);
> +    monitor_printf(mon, "  Host features:    ");
> +    hmp_virtio_dump_features(mon, s->host_features);
> +    monitor_printf(mon, "  Backend features: ");
> +    hmp_virtio_dump_features(mon, s->backend_features);
> +    monitor_printf(mon, "  Endianness:       %s\n",
> +                   VirtioStatusEndianness_str(s->device_endian));
> +    monitor_printf(mon, "  VirtQueues:       %d\n", s->num_vqs);
> +
> +    qapi_free_VirtioStatus(s);
> +}
> +
>  static VirtioRingDescFlagsList *qmp_decode_vring_desc_flags(uint16_t flags)
>  {
>      VirtioRingDescFlagsList *list = NULL;
> @@ -4163,6 +4308,52 @@ done:
>      return element;
>  }
>  
> +void hmp_x_debug_virtio_queue_element(Monitor *mon, const QDict *qdict)
> +{
> +    Error *err = NULL;
> +    const char *path = qdict_get_try_str(qdict, "path");
> +    int queue = qdict_get_int(qdict, "queue");
> +    int index = qdict_get_try_int(qdict, "index", -1);
> +    VirtioQueueElement *element;
> +    VirtioRingDescList *list;
> +
> +    element = qmp_x_debug_virtio_queue_element(path, queue, index != -1,
> +                                               index, &err);
> +    if (err != NULL) {
> +        hmp_handle_error(mon, err);
> +        return;
> +    }
> +
> +    monitor_printf(mon, "index:  %d\n", element->index);
> +    monitor_printf(mon, "ndescs: %d\n", element->ndescs);
> +    monitor_printf(mon, "descs:  ");
> +
> +    list = element->descs;
> +    while (list) {
> +        monitor_printf(mon, "addr 0x%"PRIx64" len %d", list->value->addr,
> +                       list->value->len);
> +        if (list->value->flags) {
> +            VirtioRingDescFlagsList *flag = list->value->flags;
> +            monitor_printf(mon, " (");
> +            while (flag) {
> +                monitor_printf(mon, "%s", VirtioRingDescFlags_str(flag->value));
> +                flag = flag->next;
> +                if (flag) {
> +                    monitor_printf(mon, ", ");
> +                }
> +            }
> +            monitor_printf(mon, ")");
> +        }
> +        list = list->next;
> +        if (list) {
> +            monitor_printf(mon, ", ");
> +        }
> +    }
> +    monitor_printf(mon, "\n");
> +
> +    qapi_free_VirtioQueueElement(element);
> +}
> +
>  static const TypeInfo virtio_device_info = {
>      .name = TYPE_VIRTIO_DEVICE,
>      .parent = TYPE_DEVICE,
> diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h
> index e33ca5a911a5..f07509985254 100644
> --- a/include/monitor/hmp.h
> +++ b/include/monitor/hmp.h
> @@ -98,6 +98,10 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict);
>  void hmp_qom_list(Monitor *mon, const QDict *qdict);
>  void hmp_qom_set(Monitor *mon, const QDict *qdict);
>  void hmp_info_qom_tree(Monitor *mon, const QDict *dict);
> +void hmp_x_debug_virtio_query(Monitor *mon, const QDict *qdict);
> +void hmp_x_debug_virtio_status(Monitor *mon, const QDict *qdict);
> +void hmp_x_debug_virtio_queue_status(Monitor *mon, const QDict *qdict);
> +void hmp_x_debug_virtio_queue_element(Monitor *mon, const QDict *qdict);
>  void object_add_completion(ReadLineState *rs, int nb_args, const char *str);
>  void object_del_completion(ReadLineState *rs, int nb_args, const char *str);
>  void device_add_completion(ReadLineState *rs, int nb_args, const char *str);
> diff --git a/monitor/misc.c b/monitor/misc.c
> index 9723b466cda1..1a179829250d 100644
> --- a/monitor/misc.c
> +++ b/monitor/misc.c
> @@ -23,6 +23,7 @@
>   */
>  
>  #include "qemu/osdep.h"
> +#include "config-devices.h"
>  #include "monitor-internal.h"
>  #include "cpu.h"
>  #include "monitor/qdev.h"
> @@ -232,6 +233,15 @@ static void hmp_info_help(Monitor *mon, const QDict *qdict)
>      help_cmd(mon, "info");
>  }
>  
> +static void hmp_x_debug_virtio_help(Monitor *mon, const QDict *qdict)
> +{
> +#if defined(CONFIG_VIRTIO)
> +    help_cmd(mon, "x-debug-virtio");
> +#else
> +    monitor_printf(mon, "Virtio is disabled\n");
> +#endif
> +}
> +
>  static void monitor_init_qmp_commands(void)
>  {
>      /*
> @@ -1681,6 +1691,13 @@ static HMPCommand hmp_info_cmds[] = {
>      { NULL, NULL, },
>  };
>  
> +static HMPCommand hmp_x_debug_virtio_cmds[] = {
> +#if defined(CONFIG_VIRTIO)
> +#include "hmp-commands-virtio.h"
> +#endif
> +    { NULL, NULL, },
> +};
> +
>  /* hmp_cmds and hmp_info_cmds would be sorted at runtime */
>  HMPCommand hmp_cmds[] = {
>  #include "hmp-commands.h"
> -- 
> 2.26.2
> 
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
Laurent Vivier May 15, 2020, 3:20 p.m. UTC | #2
On 13/05/2020 12:51, Dr. David Alan Gilbert wrote:
> * Laurent Vivier (lvivier@redhat.com) wrote:
>> This patch implements HMP version of the virtio QMP commands
>>
>> Signed-off-by: Laurent Vivier <lvivier@redhat.com>
> 
> Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
> 
> With a thought below....
> 
>> ---
>>  Makefile                |   2 +-
>>  Makefile.target         |   7 +-
>>  docs/system/monitor.rst |   2 +
>>  hmp-commands-virtio.hx  | 160 +++++++++++++++++++++++++++++++++
>>  hmp-commands.hx         |  10 +++
>>  hw/virtio/virtio.c      | 193 +++++++++++++++++++++++++++++++++++++++-
>>  include/monitor/hmp.h   |   4 +
>>  monitor/misc.c          |  17 ++++
>>  8 files changed, 391 insertions(+), 4 deletions(-)
>>  create mode 100644 hmp-commands-virtio.hx
>>
...
>> diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
>> index 66dc2cef1b39..c3d6b783417e 100644
>> --- a/hw/virtio/virtio.c
>> +++ b/hw/virtio/virtio.c
...
>> @@ -4033,6 +4092,92 @@ VirtioStatus *qmp_x_debug_virtio_status(const char* path, Error **errp)
>>      return status;
>>  }
>>  
>> +#define DUMP_FEATURES(type, field)                                         \
>> +    do {                                                                   \
>> +        type##FeatureList *list = features->device->u.field.data;          \
>> +        if (list) {                                                        \
>> +            monitor_printf(mon, "                    ");                   \
>> +            while (list) {                                                 \
>> +                monitor_printf(mon, "%s", type##Feature_str(list->value)); \
>> +                list = list->next;                                         \
>> +                if (list != NULL) {                                        \
>> +                    monitor_printf(mon, ", ");                             \
>> +                }                                                          \
>> +            }                                                              \
>> +            monitor_printf(mon, "\n");                                     \
>> +        }                                                                  \
>> +    } while (0)
> 
> It feels like you should be able to have an array of Feature_str's
> indexed by VIRTIO_DEVICE_FEATURE_KIND_ enum, so that when a new
> VIRTIO_DEVICE_FEATURE_KIND is added you don't need to fix this up.

I don't understand what you mean here.

>> +
>> +static void hmp_virtio_dump_features(Monitor *mon,
>> +                                     VirtioStatusFeatures *features)
>> +{
>> +    VirtioTransportFeatureList *transport_list = features->transport;
>> +    while (transport_list) {
>> +        monitor_printf(mon, "%s",
>> +                       VirtioTransportFeature_str(transport_list->value));
>> +        transport_list = transport_list->next;
>> +        if (transport_list != NULL) {
>> +            monitor_printf(mon, ", ");
>> +        }
>> +    }
>> +    monitor_printf(mon, "\n");
>> +    switch (features->device->type) {
>> +    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_SERIAL:
>> +        DUMP_FEATURES(VirtioSerial, virtio_serial);
>> +        break;
>> +    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_BLK:
>> +        DUMP_FEATURES(VirtioBlk, virtio_blk);
>> +        break;
>> +    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_GPU:
>> +        DUMP_FEATURES(VirtioGpu, virtio_gpu);
>> +        break;
>> +    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_NET:
>> +        DUMP_FEATURES(VirtioNet, virtio_net);
>> +        break;
>> +    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_SCSI:
>> +        DUMP_FEATURES(VirtioScsi, virtio_scsi);
>> +        break;
>> +    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_BALLOON:
>> +        DUMP_FEATURES(VirtioBalloon, virtio_balloon);
>> +        break;
>> +    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_IOMMU:
>> +        DUMP_FEATURES(VirtioIommu, virtio_iommu);
>> +        break;
>> +    default:
>> +        g_assert_not_reached();
>> +    }
>> +    if (features->unknown) {
>> +        monitor_printf(mon, "                    unknown(0x%016"PRIx64")\n", \
>> +                       features->unknown);
>> +    }
>> +}
...

Thanks,
Laurent
Dr. David Alan Gilbert May 15, 2020, 3:45 p.m. UTC | #3
* Laurent Vivier (lvivier@redhat.com) wrote:
> On 13/05/2020 12:51, Dr. David Alan Gilbert wrote:
> > * Laurent Vivier (lvivier@redhat.com) wrote:
> >> This patch implements HMP version of the virtio QMP commands
> >>
> >> Signed-off-by: Laurent Vivier <lvivier@redhat.com>
> > 
> > Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
> > 
> > With a thought below....
> > 
> >> ---
> >>  Makefile                |   2 +-
> >>  Makefile.target         |   7 +-
> >>  docs/system/monitor.rst |   2 +
> >>  hmp-commands-virtio.hx  | 160 +++++++++++++++++++++++++++++++++
> >>  hmp-commands.hx         |  10 +++
> >>  hw/virtio/virtio.c      | 193 +++++++++++++++++++++++++++++++++++++++-
> >>  include/monitor/hmp.h   |   4 +
> >>  monitor/misc.c          |  17 ++++
> >>  8 files changed, 391 insertions(+), 4 deletions(-)
> >>  create mode 100644 hmp-commands-virtio.hx
> >>
> ...
> >> diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
> >> index 66dc2cef1b39..c3d6b783417e 100644
> >> --- a/hw/virtio/virtio.c
> >> +++ b/hw/virtio/virtio.c
> ...
> >> @@ -4033,6 +4092,92 @@ VirtioStatus *qmp_x_debug_virtio_status(const char* path, Error **errp)
> >>      return status;
> >>  }
> >>  
> >> +#define DUMP_FEATURES(type, field)                                         \
> >> +    do {                                                                   \
> >> +        type##FeatureList *list = features->device->u.field.data;          \
> >> +        if (list) {                                                        \
> >> +            monitor_printf(mon, "                    ");                   \
> >> +            while (list) {                                                 \
> >> +                monitor_printf(mon, "%s", type##Feature_str(list->value)); \
> >> +                list = list->next;                                         \
> >> +                if (list != NULL) {                                        \
> >> +                    monitor_printf(mon, ", ");                             \
> >> +                }                                                          \
> >> +            }                                                              \
> >> +            monitor_printf(mon, "\n");                                     \
> >> +        }                                                                  \
> >> +    } while (0)
> > 
> > It feels like you should be able to have an array of Feature_str's
> > indexed by VIRTIO_DEVICE_FEATURE_KIND_ enum, so that when a new
> > VIRTIO_DEVICE_FEATURE_KIND is added you don't need to fix this up.
> 
> I don't understand what you mean here.

Instead of the switch below, I'm thinking you could have something like:

    if (features->device->type < something_MAX) {
        features_str = anarray[features->device->type];

        ....
        monitor_printf(mon, "%s", features_str(list->value));
        ....
    }

with 'anarray' somewhere more central, so we don't have to keep
these switch structures and macros spread around.

Dave

> >> +
> >> +static void hmp_virtio_dump_features(Monitor *mon,
> >> +                                     VirtioStatusFeatures *features)
> >> +{
> >> +    VirtioTransportFeatureList *transport_list = features->transport;
> >> +    while (transport_list) {
> >> +        monitor_printf(mon, "%s",
> >> +                       VirtioTransportFeature_str(transport_list->value));
> >> +        transport_list = transport_list->next;
> >> +        if (transport_list != NULL) {
> >> +            monitor_printf(mon, ", ");
> >> +        }
> >> +    }
> >> +    monitor_printf(mon, "\n");
> >> +    switch (features->device->type) {
> >> +    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_SERIAL:
> >> +        DUMP_FEATURES(VirtioSerial, virtio_serial);
> >> +        break;
> >> +    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_BLK:
> >> +        DUMP_FEATURES(VirtioBlk, virtio_blk);
> >> +        break;
> >> +    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_GPU:
> >> +        DUMP_FEATURES(VirtioGpu, virtio_gpu);
> >> +        break;
> >> +    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_NET:
> >> +        DUMP_FEATURES(VirtioNet, virtio_net);
> >> +        break;
> >> +    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_SCSI:
> >> +        DUMP_FEATURES(VirtioScsi, virtio_scsi);
> >> +        break;
> >> +    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_BALLOON:
> >> +        DUMP_FEATURES(VirtioBalloon, virtio_balloon);
> >> +        break;
> >> +    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_IOMMU:
> >> +        DUMP_FEATURES(VirtioIommu, virtio_iommu);
> >> +        break;
> >> +    default:
> >> +        g_assert_not_reached();
> >> +    }
> >> +    if (features->unknown) {
> >> +        monitor_printf(mon, "                    unknown(0x%016"PRIx64")\n", \
> >> +                       features->unknown);
> >> +    }
> >> +}
> ...
> 
> Thanks,
> Laurent
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
Laurent Vivier May 15, 2020, 3:57 p.m. UTC | #4
On 15/05/2020 17:45, Dr. David Alan Gilbert wrote:
> * Laurent Vivier (lvivier@redhat.com) wrote:
>> On 13/05/2020 12:51, Dr. David Alan Gilbert wrote:
>>> * Laurent Vivier (lvivier@redhat.com) wrote:
>>>> This patch implements HMP version of the virtio QMP commands
>>>>
>>>> Signed-off-by: Laurent Vivier <lvivier@redhat.com>
>>>
>>> Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
>>>
>>> With a thought below....
>>>
>>>> ---
>>>>  Makefile                |   2 +-
>>>>  Makefile.target         |   7 +-
>>>>  docs/system/monitor.rst |   2 +
>>>>  hmp-commands-virtio.hx  | 160 +++++++++++++++++++++++++++++++++
>>>>  hmp-commands.hx         |  10 +++
>>>>  hw/virtio/virtio.c      | 193 +++++++++++++++++++++++++++++++++++++++-
>>>>  include/monitor/hmp.h   |   4 +
>>>>  monitor/misc.c          |  17 ++++
>>>>  8 files changed, 391 insertions(+), 4 deletions(-)
>>>>  create mode 100644 hmp-commands-virtio.hx
>>>>
>> ...
>>>> diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
>>>> index 66dc2cef1b39..c3d6b783417e 100644
>>>> --- a/hw/virtio/virtio.c
>>>> +++ b/hw/virtio/virtio.c
>> ...
>>>> @@ -4033,6 +4092,92 @@ VirtioStatus *qmp_x_debug_virtio_status(const char* path, Error **errp)
>>>>      return status;
>>>>  }
>>>>  
>>>> +#define DUMP_FEATURES(type, field)                                         \
>>>> +    do {                                                                   \
>>>> +        type##FeatureList *list = features->device->u.field.data;          \
>>>> +        if (list) {                                                        \
>>>> +            monitor_printf(mon, "                    ");                   \
>>>> +            while (list) {                                                 \
>>>> +                monitor_printf(mon, "%s", type##Feature_str(list->value)); \
>>>> +                list = list->next;                                         \
>>>> +                if (list != NULL) {                                        \
>>>> +                    monitor_printf(mon, ", ");                             \
>>>> +                }                                                          \
>>>> +            }                                                              \
>>>> +            monitor_printf(mon, "\n");                                     \
>>>> +        }                                                                  \
>>>> +    } while (0)
>>>
>>> It feels like you should be able to have an array of Feature_str's
>>> indexed by VIRTIO_DEVICE_FEATURE_KIND_ enum, so that when a new
>>> VIRTIO_DEVICE_FEATURE_KIND is added you don't need to fix this up.
>>
>> I don't understand what you mean here.
> 
> Instead of the switch below, I'm thinking you could have something like:
> 
>     if (features->device->type < something_MAX) {
>         features_str = anarray[features->device->type];
> 
>         ....
>         monitor_printf(mon, "%s", features_str(list->value));
>         ....
>     }
> 
> with 'anarray' somewhere more central, so we don't have to keep
> these switch structures and macros spread around.

OK, I tried that, but in fact we need to know the type of the list to be
able to scan it (the "type##FeatureList": VirtoSerialFeatureList,
VirtioBlkFeatureList, ...), except if we introduce a generic feature
list node (for "next " and "value"). But it becomes more complicated,
because we also need to generate the "anarray" somewhere.

[Note: I've changed the legacy enum to a flat enum as proposed by Eric
in v3]

Thanks,
Laurent
diff mbox series

Patch

diff --git a/Makefile b/Makefile
index 34275f57c9cb..feb300ebb2d4 100644
--- a/Makefile
+++ b/Makefile
@@ -1099,7 +1099,7 @@  $(MANUAL_BUILDDIR)/interop/index.html: $(call manual-deps,interop)
 $(MANUAL_BUILDDIR)/specs/index.html: $(call manual-deps,specs)
 	$(call build-manual,specs,html)
 
-$(MANUAL_BUILDDIR)/system/index.html: $(call manual-deps,system) $(SRC_PATH)/hmp-commands.hx $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/qemu-options.hx
+$(MANUAL_BUILDDIR)/system/index.html: $(call manual-deps,system) $(SRC_PATH)/hmp-commands.hx $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/hmp-commands-virtio.hx
 	$(call build-manual,system,html)
 
 $(MANUAL_BUILDDIR)/tools/index.html: $(call manual-deps,tools) $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/docs/qemu-option-trace.rst.inc
diff --git a/Makefile.target b/Makefile.target
index 8ed1eba95b9c..66d3ff9bc350 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -171,7 +171,7 @@  else
 obj-y += hw/$(TARGET_BASE_ARCH)/
 endif
 
-generated-files-y += hmp-commands.h hmp-commands-info.h
+generated-files-y += hmp-commands.h hmp-commands-info.h hmp-commands-virtio.h
 generated-files-y += config-devices.h
 
 endif # CONFIG_SOFTMMU
@@ -220,10 +220,13 @@  hmp-commands.h: $(SRC_PATH)/hmp-commands.hx $(SRC_PATH)/scripts/hxtool
 hmp-commands-info.h: $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/scripts/hxtool
 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$(TARGET_DIR)$@")
 
+hmp-commands-virtio.h: $(SRC_PATH)/hmp-commands-virtio.hx $(SRC_PATH)/scripts/hxtool
+	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$(TARGET_DIR)$@")
+
 clean: clean-target
 	rm -f *.a *~ $(PROGS)
 	rm -f $(shell find . -name '*.[od]')
-	rm -f hmp-commands.h gdbstub-xml.c
+	rm -f hmp-commands.h hmp-commands-virtio.h gdbstub-xml.c
 	rm -f trace/generated-helpers.c trace/generated-helpers.c-timestamp
 ifdef CONFIG_TRACE_SYSTEMTAP
 	rm -f *.stp
diff --git a/docs/system/monitor.rst b/docs/system/monitor.rst
index 0bcd5da21644..985c3f51ffe7 100644
--- a/docs/system/monitor.rst
+++ b/docs/system/monitor.rst
@@ -21,6 +21,8 @@  The following commands are available:
 
 .. hxtool-doc:: hmp-commands.hx
 
+.. hxtool-doc:: hmp-commands-virtio.hx
+
 .. hxtool-doc:: hmp-commands-info.hx
 
 Integer expressions
diff --git a/hmp-commands-virtio.hx b/hmp-commands-virtio.hx
new file mode 100644
index 000000000000..14cb14bfcc70
--- /dev/null
+++ b/hmp-commands-virtio.hx
@@ -0,0 +1,160 @@ 
+HXCOMM Use DEFHEADING() to define headings in both help text and rST.
+HXCOMM Text between SRST and ERST is copied to the rST version and
+HXCOMM discarded from C version.
+HXCOMM DEF(command, args, callback, arg_string, help) is used to construct
+HXCOMM monitor info commands
+HXCOMM HXCOMM can be used for comments, discarded from both rST and C.
+HXCOMM
+HXCOMM In this file, generally SRST fragments should have two extra
+HXCOMM spaces of indent, so that the documentation list item for "x-debug-virtio cmd"
+HXCOMM appears inside the documentation list item for the top level
+HXCOMM "x-debug-virtio" documentation entry. The exception is the first SRST
+HXCOMM fragment that defines that top level entry.
+
+SRST
+``x-debug-virtio`` *subcommand*
+  Show various information about virtio.
+
+  Example:
+
+  List all sub-commands::
+
+    (qemu) x-debug-virtio
+    x-debug-virtio query  -- List all available virtio devices
+    x-debug-virtio status path -- Display status of a given virtio device
+    x-debug-virtio queue-status path queue -- Display status of a given virtio queue
+    x-debug-virtio queue-element path queue [index] -- Display element of a given virtio queue
+
+ERST
+
+    {
+        .name       = "query",
+        .args_type  = "",
+        .params     = "",
+        .help       = "List all available virtio devices",
+        .cmd        = hmp_x_debug_virtio_query,
+        .flags      = "p",
+    },
+
+SRST
+  ``x-debug-virtio query``
+    List all available virtio devices
+
+    Example:
+
+    List all available virtio devices in the machine::
+
+      (qemu) x-debug-virtio query
+      /machine/peripheral-anon/device[3]/virtio-backend [virtio-net]
+      /machine/peripheral-anon/device[1]/virtio-backend [virtio-serial]
+      /machine/peripheral-anon/device[0]/virtio-backend [virtio-blk]
+
+ERST
+
+    {
+        .name       = "status",
+        .args_type  = "path:s",
+        .params     = "path",
+        .help       = "Display status of a given virtio device",
+        .cmd        = hmp_x_debug_virtio_status,
+        .flags      = "p",
+    },
+
+SRST
+  ``x-debug-virtio status`` *path*
+    Display status of a given virtio device
+
+    Example:
+
+    Dump the status of the first virtio device::
+
+      (qemu) x-debug-virtio status /machine/peripheral-anon/device[3]/virtio-backend
+      /machine/peripheral-anon/device[3]/virtio-backend:
+        Device Id:        1
+        Guest features:   event-idx, indirect-desc, version-1
+                          ctrl-mac-addr, guest-announce, ctrl-vlan, ctrl-rx, ctrl-vq, status, mrg-rxbuf, host-ufo, host-ecn, host-tso6, host-tso4, guest-ufo, guest-ecn, guest-tso6, guest-tso4, mac, ctrl-guest-offloads, guest-csum, csum
+        Host features:    event-idx, indirect-desc, bad-feature, version-1, any-layout, notify-on-empty
+                          gso, ctrl-mac-addr, guest-announce, ctrl-rx-extra, ctrl-vlan, ctrl-rx, ctrl-vq, status, mrg-rxbuf, host-ufo, host-ecn, host-tso6, host-tso4, guest-ufo, guest-ecn, guest-tso6, guest-tso4, mac, ctrl-guest-offloads, guest-csum, csum
+        Backend features:
+        Endianness:       little
+        VirtQueues:       3
+
+ERST
+
+    {
+        .name       = "queue-status",
+        .args_type  = "path:s,queue:i",
+        .params     = "path queue",
+        .help       = "Display status of a given virtio queue",
+        .cmd        = hmp_x_debug_virtio_queue_status,
+        .flags      = "p",
+    },
+
+SRST
+  ``x-debug-virtio queue-status`` *path* *queue*
+    Display status of a given virtio queue
+
+    Example:
+
+    Dump the status of the first queue of the first virtio device::
+
+      (qemu) x-debug-virtio queue-status /machine/peripheral-anon/device[3]/virtio-backend 0
+      /machine/peripheral-anon/device[3]/virtio-backend:
+        index:                0
+        inuse:                0
+        last_avail_idx:       61
+        shadow_avail_idx:     292
+        signalled_used:       61
+        signalled_used_valid: 1
+        VRing:
+          num:         256
+          num_default: 256
+          align:       4096
+          desc:        0x000000006c352000
+          avail:       0x000000006c353000
+          used:        0x000000006c353240
+
+ERST
+
+    {
+        .name       = "queue-element",
+        .args_type  = "path:s,queue:i,index:i?",
+        .params     = "path queue [index]",
+        .help       = "Display element of a given virtio queue",
+        .cmd        = hmp_x_debug_virtio_queue_element,
+        .flags      = "p",
+    },
+
+SRST
+  ``x-debug-virtio queue-element`` *path* *queue* [*index*]
+    Display element of a given virtio queue
+
+    Example:
+
+    Dump the information of the head element of the first queue of
+    the first virtio device::
+
+      (qemu) x-debug-virtio queue-element/machine/peripheral-anon/device[3]/virtio-backend 0
+      index:  67
+      ndescs: 1
+      descs:  addr 0x6fe69800 len 1536 (write)
+
+      (qemu) xp/128bx 0x6fe69800
+      000000006fe69800: 0x02 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+      000000006fe69808: 0x00 0x00 0x01 0x00 0x52 0x54 0x00 0x12
+      000000006fe69810: 0x34 0x56 0x52 0x54 0x00 0x09 0x51 0xde
+      000000006fe69818: 0x08 0x00 0x45 0x00 0x00 0x4c 0x8f 0x32
+
+    device[3] is a virtio-net device and we can see in the element buffer the
+    MAC address of the card::
+
+      [root@localhost ~]# ip link show ens4
+      2: ens4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP m0
+          link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff
+
+    and the MAC address of the gateway::
+
+      [root@localhost ~]# arp -a
+      _gateway (192.168.122.1) at 52:54:00:09:51:de [ether] on ens4
+
+ERST
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 7f0f3974ad90..777761dc48d7 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1804,6 +1804,16 @@  SRST
   Set QOM property *property* of object at location *path* to value *value*
 ERST
 
+    {
+        .name       = "x-debug-virtio",
+        .args_type  = "name:S?",
+        .params     = "[cmd]",
+        .help       = "show various information about virtio",
+        .cmd        = hmp_x_debug_virtio_help,
+        .sub_table  = hmp_x_debug_virtio_cmds,
+        .flags      = "p",
+    },
+
     {
         .name       = "info",
         .args_type  = "item:s?",
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index 66dc2cef1b39..c3d6b783417e 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -30,6 +30,9 @@ 
 #include "sysemu/dma.h"
 #include "sysemu/runstate.h"
 #include "config-devices.h"
+#include "monitor/hmp.h"
+#include "monitor/monitor.h"
+#include "qapi/qmp/qdict.h"
 
 static QTAILQ_HEAD(, VirtIODevice) virtio_list;
 
@@ -3861,6 +3864,32 @@  VirtioInfoList *qmp_x_debug_query_virtio(Error **errp)
     return list;
 }
 
+void hmp_x_debug_virtio_query(Monitor *mon, const QDict *qdict)
+{
+    Error *err = NULL;
+    VirtioInfoList *list = qmp_x_debug_query_virtio(&err);
+    VirtioInfoList *node;
+
+    if (err != NULL) {
+        hmp_handle_error(mon, err);
+        return;
+    }
+
+    if (list == NULL) {
+        monitor_printf(mon, "No VirtIO devices\n");
+        return;
+    }
+
+    node = list;
+    while (node) {
+        monitor_printf(mon, "%s [%s]\n", node->value->path,
+                       VirtioType_str(node->value->type));
+        node = node->next;
+    }
+
+    qapi_free_VirtioInfoList(list);
+}
+
 static VirtIODevice *virtio_device_find(const char *path)
 {
     VirtIODevice *vdev;
@@ -3912,8 +3941,38 @@  VirtQueueStatus *qmp_x_debug_virtio_queue_status(const char *path,
     return status;
 }
 
+void hmp_x_debug_virtio_queue_status(Monitor *mon, const QDict *qdict)
+{
+    Error *err = NULL;
+    const char *path = qdict_get_try_str(qdict, "path");
+    int queue = qdict_get_int(qdict, "queue");
+    VirtQueueStatus *s = qmp_x_debug_virtio_queue_status(path, queue, &err);
+
+    if (err != NULL) {
+        hmp_handle_error(mon, err);
+        return;
+    }
+    monitor_printf(mon, "%s:\n", path);
+    monitor_printf(mon, "  index:                %d\n", s->queue_index);
+    monitor_printf(mon, "  inuse:                %d\n", s->inuse);
+    monitor_printf(mon, "  last_avail_idx:       %d\n", s->last_avail_idx);
+    monitor_printf(mon, "  shadow_avail_idx:     %d\n", s->shadow_avail_idx);
+    monitor_printf(mon, "  signalled_used:       %d\n", s->signalled_used);
+    monitor_printf(mon, "  signalled_used_valid: %d\n",
+                   s->signalled_used_valid);
+    monitor_printf(mon, "  VRing:\n");
+    monitor_printf(mon, "    num:         %"PRId64"\n", s->vring_num);
+    monitor_printf(mon, "    num_default: %"PRId64"\n", s->vring_num_default);
+    monitor_printf(mon, "    align:       %"PRId64"\n", s->vring_align);
+    monitor_printf(mon, "    desc:        0x%016"PRIx64"\n", s->vring_desc);
+    monitor_printf(mon, "    avail:       0x%016"PRIx64"\n", s->vring_avail);
+    monitor_printf(mon, "    used:        0x%016"PRIx64"\n", s->vring_used);
+
+    qapi_free_VirtQueueStatus(s);
+}
+
 #define CONVERT_FEATURES(type, map)                \
-    ({                                           \
+    ({                                             \
         type *list = NULL;                         \
         type *node;                                \
         for (i = 0; map[i].virtio_bit != -1; i++) {\
@@ -4033,6 +4092,92 @@  VirtioStatus *qmp_x_debug_virtio_status(const char* path, Error **errp)
     return status;
 }
 
+#define DUMP_FEATURES(type, field)                                         \
+    do {                                                                   \
+        type##FeatureList *list = features->device->u.field.data;          \
+        if (list) {                                                        \
+            monitor_printf(mon, "                    ");                   \
+            while (list) {                                                 \
+                monitor_printf(mon, "%s", type##Feature_str(list->value)); \
+                list = list->next;                                         \
+                if (list != NULL) {                                        \
+                    monitor_printf(mon, ", ");                             \
+                }                                                          \
+            }                                                              \
+            monitor_printf(mon, "\n");                                     \
+        }                                                                  \
+    } while (0)
+
+static void hmp_virtio_dump_features(Monitor *mon,
+                                     VirtioStatusFeatures *features)
+{
+    VirtioTransportFeatureList *transport_list = features->transport;
+    while (transport_list) {
+        monitor_printf(mon, "%s",
+                       VirtioTransportFeature_str(transport_list->value));
+        transport_list = transport_list->next;
+        if (transport_list != NULL) {
+            monitor_printf(mon, ", ");
+        }
+    }
+    monitor_printf(mon, "\n");
+    switch (features->device->type) {
+    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_SERIAL:
+        DUMP_FEATURES(VirtioSerial, virtio_serial);
+        break;
+    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_BLK:
+        DUMP_FEATURES(VirtioBlk, virtio_blk);
+        break;
+    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_GPU:
+        DUMP_FEATURES(VirtioGpu, virtio_gpu);
+        break;
+    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_NET:
+        DUMP_FEATURES(VirtioNet, virtio_net);
+        break;
+    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_SCSI:
+        DUMP_FEATURES(VirtioScsi, virtio_scsi);
+        break;
+    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_BALLOON:
+        DUMP_FEATURES(VirtioBalloon, virtio_balloon);
+        break;
+    case VIRTIO_DEVICE_FEATURES_KIND_VIRTIO_IOMMU:
+        DUMP_FEATURES(VirtioIommu, virtio_iommu);
+        break;
+    default:
+        g_assert_not_reached();
+    }
+    if (features->unknown) {
+        monitor_printf(mon, "                    unknown(0x%016"PRIx64")\n", \
+                       features->unknown);
+    }
+}
+
+void hmp_x_debug_virtio_status(Monitor *mon, const QDict *qdict)
+{
+    Error *err = NULL;
+    const char *path = qdict_get_try_str(qdict, "path");
+    VirtioStatus *s = qmp_x_debug_virtio_status(path, &err);
+
+    if (err != NULL) {
+        hmp_handle_error(mon, err);
+        return;
+    }
+
+    monitor_printf(mon, "%s:\n", path);
+    monitor_printf(mon, "  Device Id:        %"PRId64"\n", s->device_id);
+    monitor_printf(mon, "  Guest features:   ");
+    hmp_virtio_dump_features(mon, s->guest_features);
+    monitor_printf(mon, "  Host features:    ");
+    hmp_virtio_dump_features(mon, s->host_features);
+    monitor_printf(mon, "  Backend features: ");
+    hmp_virtio_dump_features(mon, s->backend_features);
+    monitor_printf(mon, "  Endianness:       %s\n",
+                   VirtioStatusEndianness_str(s->device_endian));
+    monitor_printf(mon, "  VirtQueues:       %d\n", s->num_vqs);
+
+    qapi_free_VirtioStatus(s);
+}
+
 static VirtioRingDescFlagsList *qmp_decode_vring_desc_flags(uint16_t flags)
 {
     VirtioRingDescFlagsList *list = NULL;
@@ -4163,6 +4308,52 @@  done:
     return element;
 }
 
+void hmp_x_debug_virtio_queue_element(Monitor *mon, const QDict *qdict)
+{
+    Error *err = NULL;
+    const char *path = qdict_get_try_str(qdict, "path");
+    int queue = qdict_get_int(qdict, "queue");
+    int index = qdict_get_try_int(qdict, "index", -1);
+    VirtioQueueElement *element;
+    VirtioRingDescList *list;
+
+    element = qmp_x_debug_virtio_queue_element(path, queue, index != -1,
+                                               index, &err);
+    if (err != NULL) {
+        hmp_handle_error(mon, err);
+        return;
+    }
+
+    monitor_printf(mon, "index:  %d\n", element->index);
+    monitor_printf(mon, "ndescs: %d\n", element->ndescs);
+    monitor_printf(mon, "descs:  ");
+
+    list = element->descs;
+    while (list) {
+        monitor_printf(mon, "addr 0x%"PRIx64" len %d", list->value->addr,
+                       list->value->len);
+        if (list->value->flags) {
+            VirtioRingDescFlagsList *flag = list->value->flags;
+            monitor_printf(mon, " (");
+            while (flag) {
+                monitor_printf(mon, "%s", VirtioRingDescFlags_str(flag->value));
+                flag = flag->next;
+                if (flag) {
+                    monitor_printf(mon, ", ");
+                }
+            }
+            monitor_printf(mon, ")");
+        }
+        list = list->next;
+        if (list) {
+            monitor_printf(mon, ", ");
+        }
+    }
+    monitor_printf(mon, "\n");
+
+    qapi_free_VirtioQueueElement(element);
+}
+
 static const TypeInfo virtio_device_info = {
     .name = TYPE_VIRTIO_DEVICE,
     .parent = TYPE_DEVICE,
diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h
index e33ca5a911a5..f07509985254 100644
--- a/include/monitor/hmp.h
+++ b/include/monitor/hmp.h
@@ -98,6 +98,10 @@  void hmp_info_memory_devices(Monitor *mon, const QDict *qdict);
 void hmp_qom_list(Monitor *mon, const QDict *qdict);
 void hmp_qom_set(Monitor *mon, const QDict *qdict);
 void hmp_info_qom_tree(Monitor *mon, const QDict *dict);
+void hmp_x_debug_virtio_query(Monitor *mon, const QDict *qdict);
+void hmp_x_debug_virtio_status(Monitor *mon, const QDict *qdict);
+void hmp_x_debug_virtio_queue_status(Monitor *mon, const QDict *qdict);
+void hmp_x_debug_virtio_queue_element(Monitor *mon, const QDict *qdict);
 void object_add_completion(ReadLineState *rs, int nb_args, const char *str);
 void object_del_completion(ReadLineState *rs, int nb_args, const char *str);
 void device_add_completion(ReadLineState *rs, int nb_args, const char *str);
diff --git a/monitor/misc.c b/monitor/misc.c
index 9723b466cda1..1a179829250d 100644
--- a/monitor/misc.c
+++ b/monitor/misc.c
@@ -23,6 +23,7 @@ 
  */
 
 #include "qemu/osdep.h"
+#include "config-devices.h"
 #include "monitor-internal.h"
 #include "cpu.h"
 #include "monitor/qdev.h"
@@ -232,6 +233,15 @@  static void hmp_info_help(Monitor *mon, const QDict *qdict)
     help_cmd(mon, "info");
 }
 
+static void hmp_x_debug_virtio_help(Monitor *mon, const QDict *qdict)
+{
+#if defined(CONFIG_VIRTIO)
+    help_cmd(mon, "x-debug-virtio");
+#else
+    monitor_printf(mon, "Virtio is disabled\n");
+#endif
+}
+
 static void monitor_init_qmp_commands(void)
 {
     /*
@@ -1681,6 +1691,13 @@  static HMPCommand hmp_info_cmds[] = {
     { NULL, NULL, },
 };
 
+static HMPCommand hmp_x_debug_virtio_cmds[] = {
+#if defined(CONFIG_VIRTIO)
+#include "hmp-commands-virtio.h"
+#endif
+    { NULL, NULL, },
+};
+
 /* hmp_cmds and hmp_info_cmds would be sorted at runtime */
 HMPCommand hmp_cmds[] = {
 #include "hmp-commands.h"