xenvm: evtchn: expand Xen event channel driver functionality

This commit adds new functions, which can be used for Xen event channel
management (allocation, interdomain binding etc.). Such functionality
is needed for Xen PV driver development in Zephyr.

Signed-off-by: Dmytro Firsov <dmytro_firsov@epam.com>
This commit is contained in:
Dmytro Firsov 2022-02-10 13:13:11 +02:00 committed by Carles Cufí
commit a76b492f04
3 changed files with 233 additions and 2 deletions

View file

@ -29,6 +29,68 @@ static void empty_callback(void *data)
events_missed[port] = true; events_missed[port] = true;
} }
int alloc_unbound_event_channel(domid_t remote_dom)
{
int rc;
struct evtchn_alloc_unbound alloc = {
.dom = DOMID_SELF,
.remote_dom = remote_dom,
};
rc = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, &alloc);
if (rc == 0) {
rc = alloc.port;
}
return rc;
}
int bind_interdomain_event_channel(domid_t remote_dom, evtchn_port_t remote_port,
evtchn_cb_t cb, void *data)
{
int rc;
struct evtchn_bind_interdomain bind = {
.remote_dom = remote_dom,
.remote_port = remote_port,
};
rc = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain, &bind);
if (rc < 0) {
return rc;
}
rc = bind_event_channel(bind.local_port, cb, data);
if (rc < 0) {
return rc;
}
return bind.local_port;
}
int evtchn_status(evtchn_status_t *status)
{
return HYPERVISOR_event_channel_op(EVTCHNOP_status, status);
}
int evtchn_close(evtchn_port_t port)
{
struct evtchn_close close = {
.port = port,
};
return HYPERVISOR_event_channel_op(EVTCHNOP_close, &close);
}
int evtchn_set_priority(evtchn_port_t port, uint32_t priority)
{
struct evtchn_set_priority set = {
.port = port,
.priority = priority,
};
return HYPERVISOR_event_channel_op(EVTCHNOP_set_priority, &set);
}
void notify_evtchn(evtchn_port_t port) void notify_evtchn(evtchn_port_t port)
{ {
struct evtchn_send send; struct evtchn_send send;
@ -50,7 +112,6 @@ int bind_event_channel(evtchn_port_t port, evtchn_cb_t cb, void *data)
__ASSERT(cb != NULL, "%s: NULL callback for evtchn #%u\n", __ASSERT(cb != NULL, "%s: NULL callback for evtchn #%u\n",
__func__, port); __func__, port);
if (event_channels[port].cb != empty_callback) if (event_channels[port].cb != empty_callback)
LOG_WRN("%s: re-bind callback for evtchn #%u\n", LOG_WRN("%s: re-bind callback for evtchn #%u\n",
__func__, port); __func__, port);

View file

@ -17,11 +17,54 @@ struct event_channel_handle {
evtchn_cb_t cb; evtchn_cb_t cb;
void *priv; void *priv;
}; };
typedef struct event_channel_handle evtchn_handle_t; typedef struct event_channel_handle evtchn_handle_t;
/*
* Following functions just wrap Xen hypercalls, detailed description
* of parameters and return values are located in include/xen/public/event_channel.h
*/
int evtchn_status(evtchn_status_t *status);
int evtchn_close(evtchn_port_t port);
int evtchn_set_priority(evtchn_port_t port, uint32_t priority);
void notify_evtchn(evtchn_port_t port); void notify_evtchn(evtchn_port_t port);
/*
* Allocate event-channel between caller and remote domain
*
* @param remote_dom - remote domain domid
* @return - local event channel port on success, negative on error
*/
int alloc_unbound_event_channel(domid_t remote_dom);
/*
* Allocate local event channel, binded to remote port and attach specified callback
* to it
*
* @param remote_dom - remote domain domid
* @param remote_port - remote domain event channel port number
* @param cb - callback, attached to locat port
* @param data - private data, that will be passed to cb
* @return - local event channel port on success, negative on error
*/
int bind_interdomain_event_channel(domid_t remote_dom, evtchn_port_t remote_port,
evtchn_cb_t cb, void *data);
/*
* Bind user-defined handler to specified event-channel
*
* @param port - event channel number
* @param cb - pointer to event channel handler
* @param data - private data, that will be passed to handler as parameter
* @return - zero on success
*/
int bind_event_channel(evtchn_port_t port, evtchn_cb_t cb, void *data); int bind_event_channel(evtchn_port_t port, evtchn_cb_t cb, void *data);
/*
* Unbind handler from event channel, substitute it with empty callback
*
* @param port - event channel number to unbind
* @return - zero on success
*/
int unbind_event_channel(evtchn_port_t port); int unbind_event_channel(evtchn_port_t port);
int get_missed_events(evtchn_port_t port); int get_missed_events(evtchn_port_t port);

View file

@ -74,6 +74,62 @@
typedef uint32_t evtchn_port_t; typedef uint32_t evtchn_port_t;
DEFINE_XEN_GUEST_HANDLE(evtchn_port_t); DEFINE_XEN_GUEST_HANDLE(evtchn_port_t);
/*
* EVTCHNOP_alloc_unbound: Allocate a port in domain <dom> and mark as
* accepting interdomain bindings from domain <remote_dom>. A fresh port
* is allocated in <dom> and returned as <port>.
* NOTES:
* 1. If the caller is unprivileged then <dom> must be DOMID_SELF.
* 2. <remote_dom> may be DOMID_SELF, allowing loopback connections.
*/
struct evtchn_alloc_unbound {
/* IN parameters */
domid_t dom, remote_dom;
/* OUT parameters */
evtchn_port_t port;
};
typedef struct evtchn_alloc_unbound evtchn_alloc_unbound_t;
/*
* EVTCHNOP_bind_interdomain: Construct an interdomain event channel between
* the calling domain and <remote_dom>. <remote_dom,remote_port> must identify
* a port that is unbound and marked as accepting bindings from the calling
* domain. A fresh port is allocated in the calling domain and returned as
* <local_port>.
*
* In case the peer domain has already tried to set our event channel
* pending, before it was bound, EVTCHNOP_bind_interdomain always sets
* the local event channel pending.
*
* The usual pattern of use, in the guest's upcall (or subsequent
* handler) is as follows: (Re-enable the event channel for subsequent
* signalling and then) check for the existence of whatever condition
* is being waited for by other means, and take whatever action is
* needed (if any).
*
* NOTES:
* 1. <remote_dom> may be DOMID_SELF, allowing loopback connections.
*/
struct evtchn_bind_interdomain {
/* IN parameters. */
domid_t remote_dom;
evtchn_port_t remote_port;
/* OUT parameters. */
evtchn_port_t local_port;
};
typedef struct evtchn_bind_interdomain evtchn_bind_interdomain_t;
/*
* EVTCHNOP_close: Close a local event channel <port>. If the channel is
* interdomain then the remote end is placed in the unbound state
* (EVTCHNSTAT_unbound), awaiting a new connection.
*/
struct evtchn_close {
/* IN parameters. */
evtchn_port_t port;
};
typedef struct evtchn_close evtchn_close_t;
/* /*
* EVTCHNOP_send: Send an event to the remote end of the channel whose local * EVTCHNOP_send: Send an event to the remote end of the channel whose local
* endpoint is <port>. * endpoint is <port>.
@ -84,6 +140,77 @@ struct evtchn_send {
}; };
typedef struct evtchn_send evtchn_send_t; typedef struct evtchn_send evtchn_send_t;
/*
* EVTCHNOP_status: Get the current status of the communication channel which
* has an endpoint at <dom, port>.
* NOTES:
* 1. <dom> may be specified as DOMID_SELF.
* 2. Only a sufficiently-privileged domain may obtain the status of an event
* channel for which <dom> is not DOMID_SELF.
*/
struct evtchn_status {
/* IN parameters */
domid_t dom;
evtchn_port_t port;
/* OUT parameters */
#define EVTCHNSTAT_closed 0 /* Channel is not in use. */
#define EVTCHNSTAT_unbound 1 /* Channel is waiting interdom connection.*/
#define EVTCHNSTAT_interdomain 2 /* Channel is connected to remote domain. */
#define EVTCHNSTAT_pirq 3 /* Channel is bound to a phys IRQ line. */
#define EVTCHNSTAT_virq 4 /* Channel is bound to a virtual IRQ line */
#define EVTCHNSTAT_ipi 5 /* Channel is bound to a virtual IPI line */
uint32_t status;
uint32_t vcpu; /* VCPU to which this channel is bound. */
union {
struct {
domid_t dom;
} unbound; /* EVTCHNSTAT_unbound */
struct {
domid_t dom;
evtchn_port_t port;
} interdomain; /* EVTCHNSTAT_interdomain */
uint32_t pirq; /* EVTCHNSTAT_pirq */
uint32_t virq; /* EVTCHNSTAT_virq */
} u;
};
typedef struct evtchn_status evtchn_status_t;
/*
* EVTCHNOP_unmask: Unmask the specified local event-channel port and deliver
* a notification to the appropriate VCPU if an event is pending.
*/
struct evtchn_unmask {
/* IN parameters. */
evtchn_port_t port;
};
typedef struct evtchn_unmask evtchn_unmask_t;
/*
* EVTCHNOP_reset: Close all event channels associated with specified domain.
* NOTES:
* 1. <dom> may be specified as DOMID_SELF.
* 2. Only a sufficiently-privileged domain may specify other than DOMID_SELF.
* 3. Destroys all control blocks and event array, resets event channel
* operations to 2-level ABI if called with <dom> == DOMID_SELF and FIFO
* ABI was used. Guests should not bind events during EVTCHNOP_reset call
* as these events are likely to be lost.
*/
struct evtchn_reset {
/* IN parameters. */
domid_t dom;
};
typedef struct evtchn_reset evtchn_reset_t;
/*
* EVTCHNOP_set_priority: set the priority for an event channel.
*/
struct evtchn_set_priority {
/* IN parameters. */
evtchn_port_t port;
uint32_t priority;
};
typedef struct evtchn_set_priority evtchn_set_priority_t;
#define EVTCHN_2L_NR_CHANNELS (sizeof(xen_ulong_t) * sizeof(xen_ulong_t) * 64) #define EVTCHN_2L_NR_CHANNELS (sizeof(xen_ulong_t) * sizeof(xen_ulong_t) * 64)
#endif /* __XEN_PUBLIC_EVENT_CHANNEL_H__ */ #endif /* __XEN_PUBLIC_EVENT_CHANNEL_H__ */