diff mbox series

[v7,38/51] i386/xen: add monitor commands to test event injection

Message ID 20230116215805.1123514-39-dwmw2@infradead.org
State New
Headers show
Series Xen support under KVM | expand

Commit Message

David Woodhouse Jan. 16, 2023, 9:57 p.m. UTC
From: Joao Martins <joao.m.martins@oracle.com>

Specifically add listing, injection of event channels.

Signed-off-by: Joao Martins <joao.m.martins@oracle.com>
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
---
 hmp-commands.hx          |  29 ++++++++
 hw/i386/kvm/meson.build  |   4 ++
 hw/i386/kvm/xen-stubs.c  |  25 +++++++
 hw/i386/kvm/xen_evtchn.c | 138 +++++++++++++++++++++++++++++++++++++++
 hw/i386/kvm/xen_evtchn.h |   3 +
 monitor/misc.c           |   4 ++
 qapi/misc.json           |  91 ++++++++++++++++++++++++++
 7 files changed, 294 insertions(+)
 create mode 100644 hw/i386/kvm/xen-stubs.c

Comments

Markus Armbruster Jan. 17, 2023, 10:08 a.m. UTC | #1
David Woodhouse <dwmw2@infradead.org> writes:

> From: Joao Martins <joao.m.martins@oracle.com>
>
> Specifically add listing, injection of event channels.
>
> Signed-off-by: Joao Martins <joao.m.martins@oracle.com>
> Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
> Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
> ---
>  hmp-commands.hx          |  29 ++++++++
>  hw/i386/kvm/meson.build  |   4 ++
>  hw/i386/kvm/xen-stubs.c  |  25 +++++++
>  hw/i386/kvm/xen_evtchn.c | 138 +++++++++++++++++++++++++++++++++++++++
>  hw/i386/kvm/xen_evtchn.h |   3 +
>  monitor/misc.c           |   4 ++
>  qapi/misc.json           |  91 ++++++++++++++++++++++++++
>  7 files changed, 294 insertions(+)
>  create mode 100644 hw/i386/kvm/xen-stubs.c
>
> diff --git a/hmp-commands.hx b/hmp-commands.hx
> index 673e39a697..fd77c432c0 100644
> --- a/hmp-commands.hx
> +++ b/hmp-commands.hx
> @@ -1815,3 +1815,32 @@ SRST
>    Dump the FDT in dtb format to *filename*.
>  ERST
>  #endif
> +
> +#if defined(CONFIG_XEN_EMU)
> +    {
> +        .name       = "xen-event-inject",
> +        .args_type  = "port:i",
> +        .params     = "port",
> +        .help       = "inject event channel",
> +        .cmd        = hmp_xen_event_inject,
> +    },
> +
> +SRST
> +``xen-event-inject`` *port*
> +  Notify guest via event channel on port *port*.
> +ERST
> +
> +
> +    {
> +        .name       = "xen-event-list",
> +        .args_type  = "",
> +        .params     = "",
> +        .help       = "list event channel state",
> +        .cmd        = hmp_xen_event_list,
> +    },
> +
> +SRST
> +``xen-event-list``
> +  List event channels in the guest
> +ERST
> +#endif
> diff --git a/hw/i386/kvm/meson.build b/hw/i386/kvm/meson.build
> index cab64df339..577eb50a18 100644
> --- a/hw/i386/kvm/meson.build
> +++ b/hw/i386/kvm/meson.build
> @@ -10,3 +10,7 @@ i386_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files(
>    ))
>  
>  i386_ss.add_all(when: 'CONFIG_KVM', if_true: i386_kvm_ss)
> +
> +specific_ss.add(when: 'CONFIG_XEN_EMU', if_false: files(
> +  'xen-stubs.c',
> +))
> diff --git a/hw/i386/kvm/xen-stubs.c b/hw/i386/kvm/xen-stubs.c
> new file mode 100644
> index 0000000000..6f433dc995
> --- /dev/null
> +++ b/hw/i386/kvm/xen-stubs.c
> @@ -0,0 +1,25 @@
> +/*
> + * QEMU Xen emulation: QMP stubs
> + *
> + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
> + *
> + * Authors: David Woodhouse <dwmw2@infradead.org>
> + *
> + * 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 "qapi/error.h"
> +#include "qapi/qapi-commands-misc.h"
> +
> +EvtchnInfoList *qmp_xen_event_list(Error **errp)
> +{
> +    error_setg(errp, "Xen event channel emulation not enabled");
> +    return NULL;
> +}
> +
> +void qmp_xen_event_inject(uint32_t port, Error **errp)
> +{
> +    error_setg(errp, "Xen event channel emulation not enabled");
> +}
> diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c
> index 6b6df39978..a73db5d2bc 100644
> --- a/hw/i386/kvm/xen_evtchn.c
> +++ b/hw/i386/kvm/xen_evtchn.c
> @@ -14,7 +14,11 @@
>  #include "qemu/module.h"
>  #include "qemu/main-loop.h"
>  #include "qemu/log.h"
> +#include "monitor/monitor.h"
> +#include "monitor/hmp.h"
>  #include "qapi/error.h"
> +#include "qapi/qapi-commands-misc.h"
> +#include "qapi/qmp/qdict.h"
>  #include "qom/object.h"
>  #include "exec/target_page.h"
>  #include "exec/address-spaces.h"
> @@ -1059,3 +1063,137 @@ int xen_evtchn_send_op(struct evtchn_send *send)
>      return ret;
>  }
>  
> +static const char *type_names[] = {
> +    "closed",
> +    "unbound",
> +    "interdomain",
> +    "pirq",
> +    "virq",
> +    "ipi"
> +};
> +
> +EvtchnInfoList *qmp_xen_event_list(Error **errp)
> +{
> +    XenEvtchnState *s = xen_evtchn_singleton;
> +    EvtchnInfoList *head = NULL, **tail = &head;
> +    void *shinfo, *pending, *mask;
> +    int i;
> +
> +    if (!s) {
> +        error_setg(errp, "Xen event channel emulation not enabled");
> +        return NULL;
> +    }
> +
> +    shinfo = xen_overlay_get_shinfo_ptr();
> +    if (!shinfo) {
> +        error_setg(errp, "Xen shared info page not allocated");
> +        return NULL;
> +    }

Suggest a blank line here.

> +    if (xen_is_long_mode()) {
> +        pending = shinfo + offsetof(struct shared_info, evtchn_pending);
> +        mask = shinfo + offsetof(struct shared_info, evtchn_mask);
> +    } else {
> +        pending = shinfo + offsetof(struct compat_shared_info, evtchn_pending);
> +        mask = shinfo + offsetof(struct compat_shared_info, evtchn_mask);
> +    }
> +
> +    QEMU_LOCK_GUARD(&s->port_lock);
> +
> +    for (i = 0; i < s->nr_ports; i++) {
> +        XenEvtchnPort *p = &s->port_table[i];
> +        EvtchnInfo *info;
> +
> +        if (p->type == EVTCHNSTAT_closed) {
> +            continue;
> +        }
> +
> +        info = g_new0(EvtchnInfo, 1);
> +
> +        info->port = i;
> +        info->type = g_strdup(type_names[p->type]);

What ensures p->type is in bounds?

> +        if (p->type == EVTCHNSTAT_interdomain) {
> +            info->remote_domain = g_strdup((p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) ?
> +                                           "qemu" : "loopback");
> +            info->target = p->type_val & PORT_INFO_TYPEVAL_REMOTE_PORT_MASK;
> +        } else {
> +            info->target = p->type_val;
> +        }
> +        info->vcpu = p->vcpu;
> +        info->pending = test_bit(i, pending);
> +        info->masked = test_bit(i, mask);
> +
> +        QAPI_LIST_APPEND(tail, info);
> +    }
> +
> +    return head;
> +}
> +
> +void qmp_xen_event_inject(uint32_t port, Error **errp)
> +{
> +    XenEvtchnState *s = xen_evtchn_singleton;
> +
> +    if (!s) {
> +        error_setg(errp, "Xen event channel emulation not enabled");
> +        return;
> +    }
> +
> +    if (!valid_port(port)) {
> +        error_setg(errp, "Invalid port %u", port);
> +    }
> +
> +    QEMU_LOCK_GUARD(&s->port_lock);
> +
> +    if (set_port_pending(s, port)) {
> +        error_setg(errp, "Failed to set port %u", port);
> +        return;
> +    }
> +}
> +
> +void hmp_xen_event_list(Monitor *mon, const QDict *qdict)
> +{
> +    EvtchnInfoList *iter, *info_list;
> +    Error *err = NULL;
> +
> +    info_list = qmp_xen_event_list(&err);
> +    if (err) {
> +        hmp_handle_error(mon, err);
> +        return;
> +    }
> +
> +    for (iter = info_list; iter; iter = iter->next) {
> +        EvtchnInfo *info = iter->value;
> +
> +        monitor_printf(mon, "port %4lu: vcpu: %ld %s", info->port, info->vcpu,
> +                       info->type);
> +        if (strcmp(info->type, "ipi")) {
> +            monitor_printf(mon,  "(");
> +            if (info->remote_domain) {
> +                monitor_printf(mon, "%s:", info->remote_domain);
> +            }
> +            monitor_printf(mon, "%ld)", info->target);
> +        }
> +        if (info->pending) {
> +            monitor_printf(mon, " PENDING");
> +        }
> +        if (info->masked) {
> +            monitor_printf(mon, " MASKED");
> +        }
> +        monitor_printf(mon, "\n");
> +    }
> +
> +    qapi_free_EvtchnInfoList(info_list);
> +}
> +
> +void hmp_xen_event_inject(Monitor *mon, const QDict *qdict)
> +{
> +    int port = qdict_get_int(qdict, "port");
> +    Error *err = NULL;
> +
> +    qmp_xen_event_inject(port, &err);
> +    if (err) {
> +        hmp_handle_error(mon, err);
> +    } else {
> +        monitor_printf(mon, "Delivered port %d\n", port);
> +    }
> +}
> +

In general, I prefer to keep HMP commands separate, say in
i386-kvm-hmp-cmds.c.  Not sure it's worth the trouble here.

> diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h
> index 5d3e03553f..670f8b3f7d 100644
> --- a/hw/i386/kvm/xen_evtchn.h
> +++ b/hw/i386/kvm/xen_evtchn.h
> @@ -16,6 +16,9 @@ void xen_evtchn_create(void);
>  int xen_evtchn_soft_reset(void);
>  int xen_evtchn_set_callback_param(uint64_t param);
>  
> +void hmp_xen_event_inject(Monitor *mon, const QDict *qdict);
> +void hmp_xen_event_list(Monitor *mon, const QDict *qdict);
> +

Have you considered include/monitor/hmp.h?

>  struct evtchn_status;
>  struct evtchn_close;
>  struct evtchn_unmask;
> diff --git a/monitor/misc.c b/monitor/misc.c
> index bf3f1c67ca..7d8c473ffb 100644
> --- a/monitor/misc.c
> +++ b/monitor/misc.c
> @@ -82,6 +82,10 @@
>  /* Make devices configuration available for use in hmp-commands*.hx templates */
>  #include CONFIG_DEVICES
>  
> +#ifdef CONFIG_XEN_EMU
> +#include "hw/i386/kvm/xen_evtchn.h"
> +#endif
> +

Uh... what for?

>  /* file descriptors passed via SCM_RIGHTS */
>  typedef struct mon_fd_t mon_fd_t;
>  struct mon_fd_t {
> diff --git a/qapi/misc.json b/qapi/misc.json
> index 27ef5a2b20..6284f86a5b 100644
> --- a/qapi/misc.json
> +++ b/qapi/misc.json
> @@ -584,3 +584,94 @@
>  { 'event': 'VFU_CLIENT_HANGUP',
>    'data': { 'vfu-id': 'str', 'vfu-qom-path': 'str',
>              'dev-id': 'str', 'dev-qom-path': 'str' } }
> +
> +##
> +# @EvtchnInfo:
> +#
> +# Information about a Xen event channel port
> +#
> +# @port: the port number
> +#
> +# @vcpu: target vCPU for this port
> +#
> +# @type: the port type
> +#
> +# @remote-domain: remote domain for interdomain ports
> +#
> +# @target: remote port ID, or virq/pirq number
> +#
> +# @pending: port is currently active pending delivery
> +#
> +# @masked: port is masked
> +#
> +# Since: x.xx

"Since: 8.0.0" (with any luck).  More of the same below.

> +##
> +{ 'struct': 'EvtchnInfo',
> +  'data': {'port': 'int',
> +	   'vcpu': 'int',
> +	   'type': 'str',

Could we make this a QAPI enum?

> +	   'remote-domain': 'str',
> +	   'target': 'int',
> +	   'pending': 'bool',
> +	   'masked': 'bool'}}
> +
> +
> +##
> +# @xen-event-list:
> +#
> +# Query the Xen event channels opened by the guest.
> +#
> +# Returns: list of open event channel ports.
> +#
> +# Since: x.xx
> +#
> +# Example:
> +#
> +# -> { "execute": "xen-event-list" }
> +# <- { "return": [
> +#         {
> +#             "pending": false,
> +#             "port": 1,
> +#             "vcpu": 1,
> +#             "remote-domain": "qemu",
> +#             "masked": false,
> +#             "type": "interdomain",
> +#             "target": 1
> +#         },
> +#         {
> +#             "pending": false,
> +#             "port": 2,
> +#             "vcpu": 0,
> +#             "remote-domain": "",

Uh, "" is possible?  @remote-domain's doc string didn't prepare me for
that.  What does it mean?

> +#             "masked": false,
> +#             "type": "virq",
> +#             "target": 0
> +#         }
> +#      ]
> +#    }
> +#
> +##
> +{ 'command': 'xen-event-list',
> +  'returns': ['EvtchnInfo']
> +}
> +
> +##
> +# @xen-event-inject:
> +#
> +# Inject a Xen event channel port to the guest.

Would it be useful to explain what it means to "inject a Xen event
channel port to the guest"?  I have no idea, but perhaps we believe
interested in this command should be able to explain it in their sleep.

> +#
> +# @port: The port number
> +#
> +# Returns: - Nothing on success.
> +#
> +# Since: x.xx
> +#
> +# Example:
> +#
> +# -> { "execute": "xen-event-inject", "arguments": { "port": 1 } }
> +# <- { "return": { } }
> +#
> +##
> +{ 'command': 'xen-event-inject',
> +  'data': { 'port': 'uint32' }
> +}

Did you consider 'if': 'CONFIG_XEN'?

Hmm, it's target-dependent, so it would have to go into
misc-target.json.  qmp_xen_event_inject() then needs target-dependent
header qapi/qapi-commands-misc-target.h.  Fine if it's in a
target-dependent .c anyway.  Else it could be more trouble than it's
worth.
David Woodhouse Jan. 17, 2023, 10:41 a.m. UTC | #2
Hi Markus, thanks for the review.

On Tue, 2023-01-17 at 11:08 +0100, Markus Armbrster wrote:
> David Woodhouse <dwmw2@infradead.org> writes:
> > @@ -1059,3 +1063,137 @@ int xen_evtchn_send_op(struct evtchn_send *send)
> >      return ret;
> >  }
> >  
> > +static const char *type_names[] = {
> > +    "closed",
> > +    "unbound",
> > +    "interdomain",
> > +    "pirq",
> > +    "virq",
> > +    "ipi"
> > +};
> > +
> > +EvtchnInfoList *qmp_xen_event_list(Error **errp)
> > +{
> > +    XenEvtchnState *s = xen_evtchn_singleton;
> > +    EvtchnInfoList *head = NULL, **tail = &head;
> > +    void *shinfo, *pending, *mask;
> > +    int i;
> > +
> > +    if (!s) {
> > +        error_setg(errp, "Xen event channel emulation not enabled");
> > +        return NULL;
> > +    }
> > +
> > +    shinfo = xen_overlay_get_shinfo_ptr();
> > +    if (!shinfo) {
> > +        error_setg(errp, "Xen shared info page not allocated");
> > +        return NULL;
> > +    }
> 
> Suggest a blank line here.

Ack.


> > +    if (xen_is_long_mode()) {
> > +        pending = shinfo + offsetof(struct shared_info, evtchn_pending);
> > +        mask = shinfo + offsetof(struct shared_info, evtchn_mask);
> > +    } else {
> > +        pending = shinfo + offsetof(struct compat_shared_info, evtchn_pending);
> > +        mask = shinfo + offsetof(struct compat_shared_info, evtchn_mask);
> > +    }
> > +
> > +    QEMU_LOCK_GUARD(&s->port_lock);
> > +
> > +    for (i = 0; i < s->nr_ports; i++) {
> > +        XenEvtchnPort *p = &s->port_table[i];
> > +        EvtchnInfo *info;
> > +
> > +        if (p->type == EVTCHNSTAT_closed) {
> > +            continue;
> > +        }
> > +
> > +        info = g_new0(EvtchnInfo, 1);
> > +
> > +        info->port = i;
> > +        info->type = g_strdup(type_names[p->type]);
> 
> What ensures p->type is in bounds?

Because it's an internal data structure and it's never set to anything
else.

I did ponder a defensive 'else g_strdup_printf("unknown type %d")' and
IIRC even started typing it, but then stopped.

Although actually, that was before I was asked to convert to QMP, and
it wasn't as simple as g_strdup_printf(). It's probably easier now. I
can do it if you prefer, just for appearance's sake and paranoia?

> > +        if (p->type == EVTCHNSTAT_interdomain) {
> > +            info->remote_domain = g_strdup((p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) ?
> > +                                           "qemu" : "loopback");
> > +            info->target = p->type_val & PORT_INFO_TYPEVAL_REMOTE_PORT_MASK;
> > +        } else {
> > +            info->target = p->type_val;
> > +        }
> > +        info->vcpu = p->vcpu;
> > +        info->pending = test_bit(i, pending);
> > +        info->masked = test_bit(i, mask);
> > +
> > +        QAPI_LIST_APPEND(tail, info);
> > +    }
> > +
> > +    return head;
> > +}
> > +
> > +void qmp_xen_event_inject(uint32_t port, Error **errp)
> > +{
> > +    XenEvtchnState *s = xen_evtchn_singleton;
> > +
> > +    if (!s) {
> > +        error_setg(errp, "Xen event channel emulation not enabled");
> > +        return;
> > +    }
> > +
> > +    if (!valid_port(port)) {
> > +        error_setg(errp, "Invalid port %u", port);
> > +    }
> > +
> > +    QEMU_LOCK_GUARD(&s->port_lock);
> > +
> > +    if (set_port_pending(s, port)) {
> > +        error_setg(errp, "Failed to set port %u", port);
> > +        return;
> > +    }
> > +}
> > +
> > +void hmp_xen_event_list(Monitor *mon, const QDict *qdict)
> > +{
> > +    EvtchnInfoList *iter, *info_list;
> > +    Error *err = NULL;
> > +
> > +    info_list = qmp_xen_event_list(&err);
> > +    if (err) {
> > +        hmp_handle_error(mon, err);
> > +        return;
> > +    }
> > +
> > +    for (iter = info_list; iter; iter = iter->next) {
> > +        EvtchnInfo *info = iter->value;
> > +
> > +        monitor_printf(mon, "port %4lu: vcpu: %ld %s", info->port, info->vcpu,
> > +                       info->type);
> > +        if (strcmp(info->type, "ipi")) {
> > +            monitor_printf(mon,  "(");
> > +            if (info->remote_domain) {
> > +                monitor_printf(mon, "%s:", info->remote_domain);
> > +            }
> > +            monitor_printf(mon, "%ld)", info->target);
> > +        }
> > +        if (info->pending) {
> > +            monitor_printf(mon, " PENDING");
> > +        }
> > +        if (info->masked) {
> > +            monitor_printf(mon, " MASKED");
> > +        }
> > +        monitor_printf(mon, "\n");
> > +    }
> > +
> > +    qapi_free_EvtchnInfoList(info_list);
> > +}
> > +
> > +void hmp_xen_event_inject(Monitor *mon, const QDict *qdict)
> > +{
> > +    int port = qdict_get_int(qdict, "port");
> > +    Error *err = NULL;
> > +
> > +    qmp_xen_event_inject(port, &err);
> > +    if (err) {
> > +        hmp_handle_error(mon, err);
> > +    } else {
> > +        monitor_printf(mon, "Delivered port %d\n", port);
> > +    }
> > +}
> > +
> 
> In general, I prefer to keep HMP commands separate, say in
> i386-kvm-hmp-cmds.c.  Not sure it's worth the trouble here.
> 
> > diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h
> > index 5d3e03553f..670f8b3f7d 100644
> > --- a/hw/i386/kvm/xen_evtchn.h
> > +++ b/hw/i386/kvm/xen_evtchn.h
> > @@ -16,6 +16,9 @@ void xen_evtchn_create(void);
> >  int xen_evtchn_soft_reset(void);
> >  int xen_evtchn_set_callback_param(uint64_t param);
> >  
> > +void hmp_xen_event_inject(Monitor *mon, const QDict *qdict);
> > +void hmp_xen_event_list(Monitor *mon, const QDict *qdict);
> > +
> 
> Have you considered include/monitor/hmp.h?
> 
> >  struct evtchn_status;
> >  struct evtchn_close;
> >  struct evtchn_unmask;
> > diff --git a/monitor/misc.c b/monitor/misc.c
> > index bf3f1c67ca..7d8c473ffb 100644
> > --- a/monitor/misc.c
> > +++ b/monitor/misc.c
> > @@ -82,6 +82,10 @@
> >  /* Make devices configuration available for use in hmp-commands*.hx templates */
> >  #include CONFIG_DEVICES
> >  
> > +#ifdef CONFIG_XEN_EMU
> > +#include "hw/i386/kvm/xen_evtchn.h"
> > +#endif
> > +
> 
> Uh... what for?
> 

For hmp_xen_event_{inject,list}, so I can drop this after I move those
declarations to include/monitor/hmp.h as you suggested. Thanks.

> >  /* file descriptors passed via SCM_RIGHTS */
> >  typedef struct mon_fd_t mon_fd_t;
> >  struct mon_fd_t {
> > diff --git a/qapi/misc.json b/qapi/misc.json
> > index 27ef5a2b20..6284f86a5b 100644
> > --- a/qapi/misc.json
> > +++ b/qapi/misc.json
> > @@ -584,3 +584,94 @@
> >  { 'event': 'VFU_CLIENT_HANGUP',
> >    'data': { 'vfu-id': 'str', 'vfu-qom-path': 'str',
> >              'dev-id': 'str', 'dev-qom-path': 'str' } }
> > +
> > +##
> > +# @EvtchnInfo:
> > +#
> > +# Information about a Xen event channel port
> > +#
> > +# @port: the port number
> > +#
> > +# @vcpu: target vCPU for this port
> > +#
> > +# @type: the port type
> > +#
> > +# @remote-domain: remote domain for interdomain ports
> > +#
> > +# @target: remote port ID, or virq/pirq number
> > +#
> > +# @pending: port is currently active pending delivery
> > +#
> > +# @masked: port is masked
> > +#
> > +# Since: x.xx
> 
> "Since: 8.0.0" (with any luck).  More of the same below.
> 
> > +##
> > +{ 'struct': 'EvtchnInfo',
> > +  'data': {'port': 'int',
> > +          'vcpu': 'int',
> > +          'type': 'str',
> 
> Could we make this a QAPI enum?

That would be good; I didn't spot that those existed. 

> > +          'remote-domain': 'str',
> > +          'target': 'int',
> > +          'pending': 'bool',
> > +          'masked': 'bool'}}
> > +
> > +
> > +##
> > +# @xen-event-list:
> > +#
> > +# Query the Xen event channels opened by the guest.
> > +#
> > +# Returns: list of open event channel ports.
> > +#
> > +# Since: x.xx
> > +#
> > +# Example:
> > +#
> > +# -> { "execute": "xen-event-list" }
> > +# <- { "return": [
> > +#         {
> > +#             "pending": false,
> > +#             "port": 1,
> > +#             "vcpu": 1,
> > +#             "remote-domain": "qemu",
> > +#             "masked": false,
> > +#             "type": "interdomain",
> > +#             "target": 1
> > +#         },
> > +#         {
> > +#             "pending": false,
> > +#             "port": 2,
> > +#             "vcpu": 0,
> > +#             "remote-domain": "",
> 
> Uh, "" is possible?  @remote-domain's doc string didn't prepare me for
> that.  What does it mean?
> 

It means "this is a type for which remote_domain isn't meaningful,
because it isn't an interdomain port at all. In an ideal world, the
remote_domain element wouldn't even exist but Dave didn't know how to
make it optional/absent and forgot to call attention to that in the
commit message."

> > +#             "masked": false,
> > +#             "type": "virq",
> > +#             "target": 0
> > +#         }
> > +#      ]
> > +#    }
> > +#
> > +##
> > +{ 'command': 'xen-event-list',
> > +  'returns': ['EvtchnInfo']
> > +}
> > +
> > +##
> > +# @xen-event-inject:
> > +#
> > +# Inject a Xen event channel port to the guest.
> 
> Would it be useful to explain what it means to "inject a Xen event
> channel port to the guest"?  I have no idea, but perhaps we believe
> interested in this command should be able to explain it in their sleep.

It's an interrupt. I'll just put '(interrupt)' after the word 'port'.

> > +#
> > +# @port: The port number
> > +#
> > +# Returns: - Nothing on success.
> > +#
> > +# Since: x.xx
> > +#
> > +# Example:
> > +#
> > +# -> { "execute": "xen-event-inject", "arguments": { "port": 1 } }
> > +# <- { "return": { } }
> > +#
> > +##
> > +{ 'command': 'xen-event-inject',
> > +  'data': { 'port': 'uint32' }
> > +}
> 
> Did you consider 'if': 'CONFIG_XEN'?

This is the "Xen emulation on not-Xen" code, so it isn't CONFIG_XEN.

It would have been CONFIG_XEN_EMU perhaps, but that's a target-specific
option.

> Hmm, it's target-dependent, so it would have to go into
> misc-target.json.  qmp_xen_event_inject() then needs target-dependent
> header qapi/qapi-commands-misc-target.h.  Fine if it's in a
> target-dependent .c anyway.  Else it could be more trouble than it's
> worth.

I'd already put empty stubs in to cover other targets, but it would be
good to drop those. Will play and send an incremental patch. Thanks.
David Woodhouse Jan. 17, 2023, 11:31 a.m. UTC | #3
Incremental patch, which I think addresses everything major? Would
still be nice to make the remote-domain field optional.

I *still* assume p->type is in the valid range, but at least if that's
wrong now it'll just put a bad value in the enum instead of crashing ;)

(In fact as I squash this I'll remove xen-stubs.c entirely; that would
end up empty at the point in the series so I'll only add it later.)

---
 hw/i386/kvm/xen-stubs.c  |  13 -----
 hw/i386/kvm/xen_evtchn.c |  25 ++++-----
 hw/i386/kvm/xen_evtchn.h |   3 -
 include/monitor/hmp.h    |   2 +
 monitor/misc.c           |   4 --
 qapi/misc-target.json    | 116 +++++++++++++++++++++++++++++++++++++++
 qapi/misc.json           |  91 ------------------------------
 7 files changed, 130 insertions(+), 124 deletions(-)

diff --git a/hw/i386/kvm/xen-stubs.c b/hw/i386/kvm/xen-stubs.c
index 5660a2ccad..a95964bbac 100644
--- a/hw/i386/kvm/xen-stubs.c
+++ b/hw/i386/kvm/xen-stubs.c
@@ -10,21 +10,8 @@
  */
 
 #include "qemu/osdep.h"
-#include "qapi/error.h"
-#include "qapi/qapi-commands-misc.h"
 #include "xen_evtchn.h"
 
-EvtchnInfoList *qmp_xen_event_list(Error **errp)
-{
-    error_setg(errp, "Xen event channel emulation not enabled");
-    return NULL;
-}
-
-void qmp_xen_event_inject(uint32_t port, Error **errp)
-{
-    error_setg(errp, "Xen event channel emulation not enabled");
-}
-
 void xen_evtchn_snoop_msi(PCIDevice *dev, bool is_msix, unsigned int vector,
                           uint64_t addr, uint32_t data, bool is_masked)
 {
diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c
index be67089f82..dc91da6544 100644
--- a/hw/i386/kvm/xen_evtchn.c
+++ b/hw/i386/kvm/xen_evtchn.c
@@ -17,7 +17,7 @@
 #include "monitor/monitor.h"
 #include "monitor/hmp.h"
 #include "qapi/error.h"
-#include "qapi/qapi-commands-misc.h"
+#include "qapi/qapi-commands-misc-target.h"
 #include "qapi/qmp/qdict.h"
 #include "qom/object.h"
 #include "exec/target_page.h"
@@ -2200,15 +2200,6 @@ int xen_be_evtchn_get_guest_port(struct xenevtchn_handle *xc)
     return xc->guest_port;
 }
 
-static const char *type_names[] = {
-    "closed",
-    "unbound",
-    "interdomain",
-    "pirq",
-    "virq",
-    "ipi"
-};
-
 EvtchnInfoList *qmp_xen_event_list(Error **errp)
 {
     XenEvtchnState *s = xen_evtchn_singleton;
@@ -2226,6 +2217,7 @@ EvtchnInfoList *qmp_xen_event_list(Error **errp)
         error_setg(errp, "Xen shared info page not allocated");
         return NULL;
     }
+
     if (xen_is_long_mode()) {
         pending = shinfo + offsetof(struct shared_info, evtchn_pending);
         mask = shinfo + offsetof(struct shared_info, evtchn_mask);
@@ -2247,7 +2239,14 @@ EvtchnInfoList *qmp_xen_event_list(Error **errp)
         info = g_new0(EvtchnInfo, 1);
 
         info->port = i;
-        info->type = g_strdup(type_names[p->type]);
+        qemu_build_assert(EVTCHN_PORT_TYPE_CLOSED == EVTCHNSTAT_closed);
+        qemu_build_assert(EVTCHN_PORT_TYPE_UNBOUND == EVTCHNSTAT_unbound);
+        qemu_build_assert(EVTCHN_PORT_TYPE_INTERDOMAIN == EVTCHNSTAT_interdomain);
+        qemu_build_assert(EVTCHN_PORT_TYPE_PIRQ == EVTCHNSTAT_pirq);
+        qemu_build_assert(EVTCHN_PORT_TYPE_VIRQ == EVTCHNSTAT_virq);
+        qemu_build_assert(EVTCHN_PORT_TYPE_IPI == EVTCHNSTAT_ipi);
+
+        info->type = p->type;
         if (p->type == EVTCHNSTAT_interdomain) {
             info->remote_domain = g_strdup((p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) ?
                                            "qemu" : "loopback");
@@ -2301,8 +2300,8 @@ void hmp_xen_event_list(Monitor *mon, const QDict *qdict)
         EvtchnInfo *info = iter->value;
 
         monitor_printf(mon, "port %4lu: vcpu: %ld %s", info->port, info->vcpu,
-                       info->type);
-        if (strcmp(info->type, "ipi")) {
+                       EvtchnPortType_str(info->type));
+        if (info->type != EVTCHN_PORT_TYPE_IPI) {
             monitor_printf(mon,  "(");
             if (info->remote_domain) {
                 monitor_printf(mon, "%s:", info->remote_domain);
diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h
index fa8651040c..bfb67ac2bc 100644
--- a/hw/i386/kvm/xen_evtchn.h
+++ b/hw/i386/kvm/xen_evtchn.h
@@ -51,9 +51,6 @@ int xen_be_evtchn_pending(struct xenevtchn_handle *xc);
 /* Apart from this which is a local addition */
 int xen_be_evtchn_get_guest_port(struct xenevtchn_handle *xc);
 
-void hmp_xen_event_inject(Monitor *mon, const QDict *qdict);
-void hmp_xen_event_list(Monitor *mon, const QDict *qdict);
-
 struct evtchn_status;
 struct evtchn_close;
 struct evtchn_unmask;
diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h
index 27f86399f7..ba1e6e8c2e 100644
--- a/include/monitor/hmp.h
+++ b/include/monitor/hmp.h
@@ -101,6 +101,8 @@ void hmp_virtio_status(Monitor *mon, const QDict *qdict);
 void hmp_virtio_queue_status(Monitor *mon, const QDict *qdict);
 void hmp_vhost_queue_status(Monitor *mon, const QDict *qdict);
 void hmp_virtio_queue_element(Monitor *mon, const QDict *qdict);
+void hmp_xen_event_inject(Monitor *mon, const QDict *qdict);
+void hmp_xen_event_list(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 7d8c473ffb..bf3f1c67ca 100644
--- a/monitor/misc.c
+++ b/monitor/misc.c
@@ -82,10 +82,6 @@
 /* Make devices configuration available for use in hmp-commands*.hx templates */
 #include CONFIG_DEVICES
 
-#ifdef CONFIG_XEN_EMU
-#include "hw/i386/kvm/xen_evtchn.h"
-#endif
-
 /* file descriptors passed via SCM_RIGHTS */
 typedef struct mon_fd_t mon_fd_t;
 struct mon_fd_t {
diff --git a/qapi/misc-target.json b/qapi/misc-target.json
index 5b6a8e9185..652e6e0b37 100644
--- a/qapi/misc-target.json
+++ b/qapi/misc-target.json
@@ -380,3 +380,119 @@
 #
 ##
 { 'command': 'query-sgx-capabilities', 'returns': 'SGXInfo', 'if': 'TARGET_I386' }
+
+
+##
+# @EvtchnPortType:
+#
+# An enumeration of Xen event channel port types.
+#
+# @closed: The port is unused.
+#
+# @unbound: The port is allocated and ready to be bound.
+#
+# @interdomain: The port is connected as an interdomain interrupt.
+#
+# @pirq: The port is bound to a physical IRQ (PIRQ).
+#
+# @virq: The port is bound to a virtual IRQ (VIRQ).
+#
+# @ipi: The post is an inter-processor interrupt (IPI).
+#
+# Since: 8.0.0
+##
+{ 'enum': 'EvtchnPortType',
+  'data': ['closed', 'unbound', 'interdomain', 'pirq', 'virq', 'ipi'],
+  'if': 'TARGET_I386' }
+
+##
+# @EvtchnInfo:
+#
+# Information about a Xen event channel port
+#
+# @port: the port number
+#
+# @vcpu: target vCPU for this port
+#
+# @type: the port type
+#
+# @remote-domain: remote domain for interdomain ports
+#
+# @target: remote port ID, or virq/pirq number
+#
+# @pending: port is currently active pending delivery
+#
+# @masked: port is masked
+#
+# Since: 8.0.0
+##
+{ 'struct': 'EvtchnInfo',
+  'data': {'port': 'int',
+           'vcpu': 'int',
+           'type': 'EvtchnPortType',
+           'remote-domain': 'str',
+           'target': 'int',
+           'pending': 'bool',
+           'masked': 'bool'},
+  'if': 'TARGET_I386' }
+
+
+##
+# @xen-event-list:
+#
+# Query the Xen event channels opened by the guest.
+#
+# Returns: list of open event channel ports.
+#
+# Since: 8.0.0
+#
+# Example:
+#
+# -> { "execute": "xen-event-list" }
+# <- { "return": [
+#         {
+#             "pending": false,
+#             "port": 1,
+#             "vcpu": 1,
+#             "remote-domain": "qemu",
+#             "masked": false,
+#             "type": "interdomain",
+#             "target": 1
+#         },
+#         {
+#             "pending": false,
+#             "port": 2,
+#             "vcpu": 0,
+#             "remote-domain": "",
+#             "masked": false,
+#             "type": "virq",
+#             "target": 0
+#         }
+#      ]
+#    }
+#
+##
+{ 'command': 'xen-event-list',
+  'returns': ['EvtchnInfo'],
+  'if': 'TARGET_I386' }
+
+##
+# @xen-event-inject:
+#
+# Inject a Xen event channel port (interrupt) to the guest.
+#
+# @port: The port number
+#
+# Returns: - Nothing on success.
+#
+# Since: 8.0.0
+#
+# Example:
+#
+# -> { "execute": "xen-event-inject", "arguments": { "port": 1 } }
+# <- { "return": { } }
+#
+##
+{ 'command': 'xen-event-inject',
+  'data': { 'port': 'uint32' },
+  'if': 'TARGET_I386' }
diff --git a/qapi/misc.json b/qapi/misc.json
index 6284f86a5b..27ef5a2b20 100644
--- a/qapi/misc.json
+++ b/qapi/misc.json
@@ -584,94 +584,3 @@
 { 'event': 'VFU_CLIENT_HANGUP',
   'data': { 'vfu-id': 'str', 'vfu-qom-path': 'str',
             'dev-id': 'str', 'dev-qom-path': 'str' } }
-
-##
-# @EvtchnInfo:
-#
-# Information about a Xen event channel port
-#
-# @port: the port number
-#
-# @vcpu: target vCPU for this port
-#
-# @type: the port type
-#
-# @remote-domain: remote domain for interdomain ports
-#
-# @target: remote port ID, or virq/pirq number
-#
-# @pending: port is currently active pending delivery
-#
-# @masked: port is masked
-#
-# Since: x.xx
-##
-{ 'struct': 'EvtchnInfo',
-  'data': {'port': 'int',
-	   'vcpu': 'int',
-	   'type': 'str',
-	   'remote-domain': 'str',
-	   'target': 'int',
-	   'pending': 'bool',
-	   'masked': 'bool'}}
-
-
-##
-# @xen-event-list:
-#
-# Query the Xen event channels opened by the guest.
-#
-# Returns: list of open event channel ports.
-#
-# Since: x.xx
-#
-# Example:
-#
-# -> { "execute": "xen-event-list" }
-# <- { "return": [
-#         {
-#             "pending": false,
-#             "port": 1,
-#             "vcpu": 1,
-#             "remote-domain": "qemu",
-#             "masked": false,
-#             "type": "interdomain",
-#             "target": 1
-#         },
-#         {
-#             "pending": false,
-#             "port": 2,
-#             "vcpu": 0,
-#             "remote-domain": "",
-#             "masked": false,
-#             "type": "virq",
-#             "target": 0
-#         }
-#      ]
-#    }
-#
-##
-{ 'command': 'xen-event-list',
-  'returns': ['EvtchnInfo']
-}
-
-##
-# @xen-event-inject:
-#
-# Inject a Xen event channel port to the guest.
-#
-# @port: The port number
-#
-# Returns: - Nothing on success.
-#
-# Since: x.xx
-#
-# Example:
-#
-# -> { "execute": "xen-event-inject", "arguments": { "port": 1 } }
-# <- { "return": { } }
-#
-##
-{ 'command': 'xen-event-inject',
-  'data': { 'port': 'uint32' }
-}
David Woodhouse Jan. 19, 2023, 11:01 a.m. UTC | #4
On Tue, 2023-01-17 at 11:08 +0100, Markus Armbruster wrote:
> 
> > +# Since: x.xx
> 
> "Since: 8.0.0" (with any luck).  More of the same below.

Speaking of which... I guess this first series probably wants to be
considered as KVM and merged via Paolo? Or at least as far as #27/51
'i386/xen: Add support for Xen event channel delivery to vCPU' which is
coincidentally where Paul has got to with his reviews (thanks!).

After that it's less KVM intricacies, and more about the actual Xen
emulation. Perhaps we can mark hw/i386/kvm/xen*.[ch] as being covered
by the Xen maintainers? Or if Stefano and Anthony prefer, we can keep
it separate and mark it as maintained by Paul and myself?

What other reviews should be required for that? I'd certainly
appreciate hearing an opinion from mst and Marcel as PC machine
maintainers on the GSI parts — especially the gsi_handler() hook in
https://git.infradead.org/users/dwmw2/qemu.git/commitdiff/42526211d
("hw/xen: Support GSI mapping to PIRQ").

For reference, leaving out the newly-added files in hw/i386/kvm/xen*
and the imported Xen headers, this is the diffstat (of just phase 1,
not the RFC bits posted on top):

 accel/kvm/kvm-all.c                                |    2 +
 accel/xen/xen-all.c                                |    2 +
 docs/system/i386/xen.rst                           |   50 +
 docs/system/target-i386.rst                        |    1 +
 hmp-commands.hx                                    |   29 +
 hw/Kconfig                                         |    1 +
 hw/i386/Kconfig                                    |    5 +
 hw/i386/pc.c                                       |   26 +
 hw/i386/xen/meson.build                            |    5 +-
 hw/i386/xen/xen_platform.c                         |   57 +-
 hw/xen/Kconfig                                     |    3 +
 include/hw/i386/pc.h                               |    3 +
 include/hw/xen/xen.h                               |   21 +-
 include/monitor/hmp.h                              |    2 +
 include/sysemu/kvm_int.h                           |    3 +
 include/sysemu/kvm_xen.h                           |   40 +
 meson.build                                        |    1 +
 qapi/misc-target.json                              |  116 ++
 softmmu/globals.c                                  |    2 +-
 target/i386/cpu.c                                  |    1 +
 target/i386/cpu.h                                  |   19 +
 target/i386/kvm/kvm.c                              |  206 ++-
 target/i386/kvm/meson.build                        |    2 +
 target/i386/kvm/trace-events                       |    6 +
 target/i386/kvm/xen-compat.h                       |   51 +
 target/i386/kvm/xen-emu.c                          | 1711 ++++++++++++++++++++
 target/i386/kvm/xen-emu.h                          |   33 +
 target/i386/machine.c                              |   25 +
diff mbox series

Patch

diff --git a/hmp-commands.hx b/hmp-commands.hx
index 673e39a697..fd77c432c0 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1815,3 +1815,32 @@  SRST
   Dump the FDT in dtb format to *filename*.
 ERST
 #endif
+
+#if defined(CONFIG_XEN_EMU)
+    {
+        .name       = "xen-event-inject",
+        .args_type  = "port:i",
+        .params     = "port",
+        .help       = "inject event channel",
+        .cmd        = hmp_xen_event_inject,
+    },
+
+SRST
+``xen-event-inject`` *port*
+  Notify guest via event channel on port *port*.
+ERST
+
+
+    {
+        .name       = "xen-event-list",
+        .args_type  = "",
+        .params     = "",
+        .help       = "list event channel state",
+        .cmd        = hmp_xen_event_list,
+    },
+
+SRST
+``xen-event-list``
+  List event channels in the guest
+ERST
+#endif
diff --git a/hw/i386/kvm/meson.build b/hw/i386/kvm/meson.build
index cab64df339..577eb50a18 100644
--- a/hw/i386/kvm/meson.build
+++ b/hw/i386/kvm/meson.build
@@ -10,3 +10,7 @@  i386_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files(
   ))
 
 i386_ss.add_all(when: 'CONFIG_KVM', if_true: i386_kvm_ss)
+
+specific_ss.add(when: 'CONFIG_XEN_EMU', if_false: files(
+  'xen-stubs.c',
+))
diff --git a/hw/i386/kvm/xen-stubs.c b/hw/i386/kvm/xen-stubs.c
new file mode 100644
index 0000000000..6f433dc995
--- /dev/null
+++ b/hw/i386/kvm/xen-stubs.c
@@ -0,0 +1,25 @@ 
+/*
+ * QEMU Xen emulation: QMP stubs
+ *
+ * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Authors: David Woodhouse <dwmw2@infradead.org>
+ *
+ * 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 "qapi/error.h"
+#include "qapi/qapi-commands-misc.h"
+
+EvtchnInfoList *qmp_xen_event_list(Error **errp)
+{
+    error_setg(errp, "Xen event channel emulation not enabled");
+    return NULL;
+}
+
+void qmp_xen_event_inject(uint32_t port, Error **errp)
+{
+    error_setg(errp, "Xen event channel emulation not enabled");
+}
diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c
index 6b6df39978..a73db5d2bc 100644
--- a/hw/i386/kvm/xen_evtchn.c
+++ b/hw/i386/kvm/xen_evtchn.c
@@ -14,7 +14,11 @@ 
 #include "qemu/module.h"
 #include "qemu/main-loop.h"
 #include "qemu/log.h"
+#include "monitor/monitor.h"
+#include "monitor/hmp.h"
 #include "qapi/error.h"
+#include "qapi/qapi-commands-misc.h"
+#include "qapi/qmp/qdict.h"
 #include "qom/object.h"
 #include "exec/target_page.h"
 #include "exec/address-spaces.h"
@@ -1059,3 +1063,137 @@  int xen_evtchn_send_op(struct evtchn_send *send)
     return ret;
 }
 
+static const char *type_names[] = {
+    "closed",
+    "unbound",
+    "interdomain",
+    "pirq",
+    "virq",
+    "ipi"
+};
+
+EvtchnInfoList *qmp_xen_event_list(Error **errp)
+{
+    XenEvtchnState *s = xen_evtchn_singleton;
+    EvtchnInfoList *head = NULL, **tail = &head;
+    void *shinfo, *pending, *mask;
+    int i;
+
+    if (!s) {
+        error_setg(errp, "Xen event channel emulation not enabled");
+        return NULL;
+    }
+
+    shinfo = xen_overlay_get_shinfo_ptr();
+    if (!shinfo) {
+        error_setg(errp, "Xen shared info page not allocated");
+        return NULL;
+    }
+    if (xen_is_long_mode()) {
+        pending = shinfo + offsetof(struct shared_info, evtchn_pending);
+        mask = shinfo + offsetof(struct shared_info, evtchn_mask);
+    } else {
+        pending = shinfo + offsetof(struct compat_shared_info, evtchn_pending);
+        mask = shinfo + offsetof(struct compat_shared_info, evtchn_mask);
+    }
+
+    QEMU_LOCK_GUARD(&s->port_lock);
+
+    for (i = 0; i < s->nr_ports; i++) {
+        XenEvtchnPort *p = &s->port_table[i];
+        EvtchnInfo *info;
+
+        if (p->type == EVTCHNSTAT_closed) {
+            continue;
+        }
+
+        info = g_new0(EvtchnInfo, 1);
+
+        info->port = i;
+        info->type = g_strdup(type_names[p->type]);
+        if (p->type == EVTCHNSTAT_interdomain) {
+            info->remote_domain = g_strdup((p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) ?
+                                           "qemu" : "loopback");
+            info->target = p->type_val & PORT_INFO_TYPEVAL_REMOTE_PORT_MASK;
+        } else {
+            info->target = p->type_val;
+        }
+        info->vcpu = p->vcpu;
+        info->pending = test_bit(i, pending);
+        info->masked = test_bit(i, mask);
+
+        QAPI_LIST_APPEND(tail, info);
+    }
+
+    return head;
+}
+
+void qmp_xen_event_inject(uint32_t port, Error **errp)
+{
+    XenEvtchnState *s = xen_evtchn_singleton;
+
+    if (!s) {
+        error_setg(errp, "Xen event channel emulation not enabled");
+        return;
+    }
+
+    if (!valid_port(port)) {
+        error_setg(errp, "Invalid port %u", port);
+    }
+
+    QEMU_LOCK_GUARD(&s->port_lock);
+
+    if (set_port_pending(s, port)) {
+        error_setg(errp, "Failed to set port %u", port);
+        return;
+    }
+}
+
+void hmp_xen_event_list(Monitor *mon, const QDict *qdict)
+{
+    EvtchnInfoList *iter, *info_list;
+    Error *err = NULL;
+
+    info_list = qmp_xen_event_list(&err);
+    if (err) {
+        hmp_handle_error(mon, err);
+        return;
+    }
+
+    for (iter = info_list; iter; iter = iter->next) {
+        EvtchnInfo *info = iter->value;
+
+        monitor_printf(mon, "port %4lu: vcpu: %ld %s", info->port, info->vcpu,
+                       info->type);
+        if (strcmp(info->type, "ipi")) {
+            monitor_printf(mon,  "(");
+            if (info->remote_domain) {
+                monitor_printf(mon, "%s:", info->remote_domain);
+            }
+            monitor_printf(mon, "%ld)", info->target);
+        }
+        if (info->pending) {
+            monitor_printf(mon, " PENDING");
+        }
+        if (info->masked) {
+            monitor_printf(mon, " MASKED");
+        }
+        monitor_printf(mon, "\n");
+    }
+
+    qapi_free_EvtchnInfoList(info_list);
+}
+
+void hmp_xen_event_inject(Monitor *mon, const QDict *qdict)
+{
+    int port = qdict_get_int(qdict, "port");
+    Error *err = NULL;
+
+    qmp_xen_event_inject(port, &err);
+    if (err) {
+        hmp_handle_error(mon, err);
+    } else {
+        monitor_printf(mon, "Delivered port %d\n", port);
+    }
+}
+
diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h
index 5d3e03553f..670f8b3f7d 100644
--- a/hw/i386/kvm/xen_evtchn.h
+++ b/hw/i386/kvm/xen_evtchn.h
@@ -16,6 +16,9 @@  void xen_evtchn_create(void);
 int xen_evtchn_soft_reset(void);
 int xen_evtchn_set_callback_param(uint64_t param);
 
+void hmp_xen_event_inject(Monitor *mon, const QDict *qdict);
+void hmp_xen_event_list(Monitor *mon, const QDict *qdict);
+
 struct evtchn_status;
 struct evtchn_close;
 struct evtchn_unmask;
diff --git a/monitor/misc.c b/monitor/misc.c
index bf3f1c67ca..7d8c473ffb 100644
--- a/monitor/misc.c
+++ b/monitor/misc.c
@@ -82,6 +82,10 @@ 
 /* Make devices configuration available for use in hmp-commands*.hx templates */
 #include CONFIG_DEVICES
 
+#ifdef CONFIG_XEN_EMU
+#include "hw/i386/kvm/xen_evtchn.h"
+#endif
+
 /* file descriptors passed via SCM_RIGHTS */
 typedef struct mon_fd_t mon_fd_t;
 struct mon_fd_t {
diff --git a/qapi/misc.json b/qapi/misc.json
index 27ef5a2b20..6284f86a5b 100644
--- a/qapi/misc.json
+++ b/qapi/misc.json
@@ -584,3 +584,94 @@ 
 { 'event': 'VFU_CLIENT_HANGUP',
   'data': { 'vfu-id': 'str', 'vfu-qom-path': 'str',
             'dev-id': 'str', 'dev-qom-path': 'str' } }
+
+##
+# @EvtchnInfo:
+#
+# Information about a Xen event channel port
+#
+# @port: the port number
+#
+# @vcpu: target vCPU for this port
+#
+# @type: the port type
+#
+# @remote-domain: remote domain for interdomain ports
+#
+# @target: remote port ID, or virq/pirq number
+#
+# @pending: port is currently active pending delivery
+#
+# @masked: port is masked
+#
+# Since: x.xx
+##
+{ 'struct': 'EvtchnInfo',
+  'data': {'port': 'int',
+	   'vcpu': 'int',
+	   'type': 'str',
+	   'remote-domain': 'str',
+	   'target': 'int',
+	   'pending': 'bool',
+	   'masked': 'bool'}}
+
+
+##
+# @xen-event-list:
+#
+# Query the Xen event channels opened by the guest.
+#
+# Returns: list of open event channel ports.
+#
+# Since: x.xx
+#
+# Example:
+#
+# -> { "execute": "xen-event-list" }
+# <- { "return": [
+#         {
+#             "pending": false,
+#             "port": 1,
+#             "vcpu": 1,
+#             "remote-domain": "qemu",
+#             "masked": false,
+#             "type": "interdomain",
+#             "target": 1
+#         },
+#         {
+#             "pending": false,
+#             "port": 2,
+#             "vcpu": 0,
+#             "remote-domain": "",
+#             "masked": false,
+#             "type": "virq",
+#             "target": 0
+#         }
+#      ]
+#    }
+#
+##
+{ 'command': 'xen-event-list',
+  'returns': ['EvtchnInfo']
+}
+
+##
+# @xen-event-inject:
+#
+# Inject a Xen event channel port to the guest.
+#
+# @port: The port number
+#
+# Returns: - Nothing on success.
+#
+# Since: x.xx
+#
+# Example:
+#
+# -> { "execute": "xen-event-inject", "arguments": { "port": 1 } }
+# <- { "return": { } }
+#
+##
+{ 'command': 'xen-event-inject',
+  'data': { 'port': 'uint32' }
+}