[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Minios-devel] [UNIKRAFT PATCH 3/3] plat/xen: Add Xenbus support
The current implementation is ported from Mini-OS. Signed-off-by: Costin Lupu <costin.lupu@xxxxxxxxx> --- plat/xen/Config.uk | 8 + plat/xen/Makefile.uk | 10 + plat/xen/include/xenbus/client.h | 139 +++++++++++ plat/xen/include/xenbus/xenbus.h | 158 ++++++++++++ plat/xen/include/xenbus/xs.h | 221 +++++++++++++++++ plat/xen/xenbus/client.c | 278 +++++++++++++++++++++ plat/xen/xenbus/xenbus.c | 260 ++++++++++++++++++++ plat/xen/xenbus/xs.c | 518 +++++++++++++++++++++++++++++++++++++++ plat/xen/xenbus/xs_comms.c | 484 ++++++++++++++++++++++++++++++++++++ plat/xen/xenbus/xs_comms.h | 75 ++++++ plat/xen/xenbus/xs_watch.c | 159 ++++++++++++ plat/xen/xenbus/xs_watch.h | 91 +++++++ 12 files changed, 2401 insertions(+) create mode 100644 plat/xen/include/xenbus/client.h create mode 100644 plat/xen/include/xenbus/xenbus.h create mode 100644 plat/xen/include/xenbus/xs.h create mode 100644 plat/xen/xenbus/client.c create mode 100644 plat/xen/xenbus/xenbus.c create mode 100644 plat/xen/xenbus/xs.c create mode 100644 plat/xen/xenbus/xs_comms.c create mode 100644 plat/xen/xenbus/xs_comms.h create mode 100644 plat/xen/xenbus/xs_watch.c create mode 100644 plat/xen/xenbus/xs_watch.h diff --git a/plat/xen/Config.uk b/plat/xen/Config.uk index 9c398f1..d0143e9 100644 --- a/plat/xen/Config.uk +++ b/plat/xen/Config.uk @@ -20,4 +20,12 @@ if (PLAT_XEN) instead of the hypervisor console. When this option is enabled the hypervisor console is used for kernel messages only. + +menuconfig XEN_XENBUS + bool "Xenbus Driver" + default n + depends on (ARCH_X86_64) + select LIBUKBUS + help + Register a Xenbus driver as uk_bus endif diff --git a/plat/xen/Makefile.uk b/plat/xen/Makefile.uk index 45096cb..ff23459 100644 --- a/plat/xen/Makefile.uk +++ b/plat/xen/Makefile.uk @@ -72,3 +72,13 @@ LIBXENPLAT_SRCS-y += $(LIBXENPLAT_BASE)/console.c LIBXENPLAT_SRCS-y += $(LIBXENPLAT_BASE)/shutdown.c LIBXENPLAT_SRCS-y += $(LIBXENPLAT_BASE)/events.c LIBXENPLAT_SRCS-y += $(LIBXENPLAT_BASE)/gnttab.c + +LIBXENBUS_ASFLAGS-y += $(LIBXENPLAT_ASFLAGS-y) +LIBXENBUS_ASINCLUDES-y += $(LIBXENPLAT_ASINCLUDES-y) +LIBXENBUS_CFLAGS-y += $(LIBXENPLAT_CFLAGS-y) +LIBXENBUS_CINCLUDES-y += $(LIBXENPLAT_CINCLUDES-y) +LIBXENBUS_SRCS-y += $(LIBXENPLAT_BASE)/xenbus/xenbus.c +LIBXENBUS_SRCS-y += $(LIBXENPLAT_BASE)/xenbus/client.c +LIBXENBUS_SRCS-y += $(LIBXENPLAT_BASE)/xenbus/xs_comms.c +LIBXENBUS_SRCS-y += $(LIBXENPLAT_BASE)/xenbus/xs_watch.c +LIBXENBUS_SRCS-y += $(LIBXENPLAT_BASE)/xenbus/xs.c diff --git a/plat/xen/include/xenbus/client.h b/plat/xen/include/xenbus/client.h new file mode 100644 index 0000000..112c8c9 --- /dev/null +++ b/plat/xen/include/xenbus/client.h @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Authors: Costin Lupu <costin.lupu@xxxxxxxxx> + * + * Copyright (c) 2018, NEC Europe Ltd., NEC Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + */ +/* + * Client interface between the device and the Xenbus driver. + * Ported from Mini-OS xenbus.c + */ + +#ifndef __XENBUS_CLIENT_H__ +#define __XENBUS_CLIENT_H__ + +#include <xenbus/xenbus.h> +#include <xenbus/xs.h> + +/* + * Returns the name of the state for tracing/debugging purposes. + * + * @param state The Xenbus state + * @return A string representing the state name + */ +const char *xenbus_state_to_str(XenbusState state); + +/* + * Converts a device type value to name + * + * @param devtype The Xenbus device type + * @return A string representing the device type name + */ +const char *xenbus_devtype_to_str(enum xenbus_dev_type devtype); + +/* + * Converts a device type name to value + * + * @param devtypestr The Xenbus device type name + * @return The Xenbus device type + */ +enum xenbus_dev_type xenbus_str_to_devtype(const char *devtypestr); + + +/* + * Watches + */ + +/* + * Waits for a watch event associated with the event list. If no event list is + * provided, a global event list is used instead. Called by a client driver. + * + * @param evlist The watch event list + */ +void xenbus_wait_watch_event(xenbus_watch_evlist_t *evlist); + +/* + * Notifies a client driver waiting for watch events. + * + * @param evlist The watch event list + * @param event The watch event + * @return 0 on success, a negative errno value on error. + */ +int xenbus_notify_watch_event(xenbus_watch_evlist_t *evlist, + struct xenbus_watch_event *event); + +/* + * Waits for a value in Xenstore to change by using watches. If no event list is + * provided, a global event list is used instead. + * + * @param path Xenstore path + * @param value The expected value + * @param evlist The watch event list + * @return 0 on success, a negative errno value on error. + */ +int xenbus_wait_for_value(const char *path, const char *value, + xenbus_watch_evlist_t *evlist); + +/* + * Driver states + */ + +/* + * Returns the driver state found at the given Xenstore path. + * + * @param path Xenstore path + * @return The Xenbus driver state + */ +XenbusState xenbus_read_driver_state(const char *path); + +/* + * Changes the state of a Xen PV driver + * + * @param xendev Xenbus device + * @param state The new Xenbus state + * @param xbt Xenbus transaction id + * @return 0 on success, a negative errno value on error. + */ +int xenbus_switch_state(struct xenbus_device *xendev, XenbusState state, + xenbus_transaction_t xbt); + +/* + * Waits for the driver state found at the given Xenstore path to change by + * using watches. + * + * @param path Xenstore path + * @param state The returned Xenbus state + * @param evlist The watch event list + * @return 0 on success, a negative errno value on error. + */ +int xenbus_wait_for_state_change(const char *path, XenbusState *state, + xenbus_watch_evlist_t *evlist); + +#endif /* __XENBUS_CLIENT_H__ */ diff --git a/plat/xen/include/xenbus/xenbus.h b/plat/xen/include/xenbus/xenbus.h new file mode 100644 index 0000000..2836a85 --- /dev/null +++ b/plat/xen/include/xenbus/xenbus.h @@ -0,0 +1,158 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Authors: Costin Lupu <costin.lupu@xxxxxxxxx> + * + * Copyright (c) 2018, NEC Europe Ltd., NEC Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + */ + +#ifndef __XENBUS_H__ +#define __XENBUS_H__ + +#include <uk/bus.h> +#include <uk/alloc.h> +#include <xen/xen.h> +#include <xen/io/xenbus.h> + + +/* + * Supported device types + */ +typedef enum xenbus_dev_type { + xenbus_dev_none = 0, + xenbus_dev_sysctl, /* System wise control device */ + xenbus_dev_vif, /* Virtual network interface */ + xenbus_dev_vbd, /* Virtual block device */ +} xenbus_dev_type_t; + +struct xenbus_device; + +/* + * Xenbus driver + */ + +typedef int (*xenbus_driver_init_func_t)(struct uk_alloc *a); +typedef int (*xenbus_driver_add_func_t)(struct xenbus_device *dev); + + +struct xenbus_driver { + UK_TAILQ_ENTRY(struct xenbus_driver) next; + const xenbus_dev_type_t *device_types; + + xenbus_driver_init_func_t init; + xenbus_driver_add_func_t add_dev; +}; +UK_TAILQ_HEAD(xenbus_driver_list, struct xenbus_driver); + + +#define XENBUS_REGISTER_DRIVER(b) \ + _XENBUS_REGISTER_DRIVER(__LIBNAME__, (b)) + +#define _XENBUS_REGFNNAME(x, y) x##y + +#define _XENBUS_REGISTER_DRIVER(libname, b) \ + static void __constructor_prio(104) \ + _XENBUS_REGFNNAME(libname, _xenbus_register_driver)(void) \ + { \ + _xenbus_register_driver((b)); \ + } + +/* Do not use this function directly: */ +void _xenbus_register_driver(struct xenbus_driver *drv); + + +/* + * Xenbus watch + */ + +/* Watch event list */ +struct xenbus_watch_event { + struct xenbus_watch_event *next; +}; +typedef struct xenbus_watch_event *xenbus_watch_evlist_t; + +struct xenbus_watch { + struct xenbus_watch *next; + xenbus_watch_evlist_t *events; +}; + + +/* + * Xenbus device + */ + +struct xenbus_device { + /**< in use by Xenbus handler */ + UK_TAILQ_ENTRY(struct xenbus_device) next; + /**< Device state */ + XenbusState state; + /**< Device type */ + enum xenbus_dev_type devtype; + /**< Xenstore path of the device */ + char *nodename; + /**< Xenstore path of the device peer (e.g. backend for frontend) */ + char *otherend; + /**< Domain id of the other end */ + domid_t otherend_id; + /**< Watch events list */ + xenbus_watch_evlist_t watch_events; + /**< Xenbus driver */ + struct xenbus_driver *drv; +}; +UK_TAILQ_HEAD(xenbus_device_list, struct xenbus_device); + + +/* + * Xenbus handler + */ + +struct xenbus_handler { + struct uk_bus b; + struct uk_alloc *a; + struct xenbus_driver_list drv_list; /**< List of Xenbus drivers */ + int drv_list_initialized; + struct xenbus_device_list dev_list; /**< List of Xenbus devices */ +}; + +extern struct xenbus_handler xbh; + +/* Helper macros for Xenbus related allocations */ +#define uk_xb_malloc(size) uk_malloc(xbh.a, (size)) +#define uk_xb_calloc(n, size) uk_calloc(xbh.a, (n), (size)) +#define uk_xb_free(ptr) uk_free(xbh.a, (ptr)) + + +/* Debugging */ +#if DEBUG_XENBUS +#define DBGXB(fmt, ...) uk_printd(DLVL_EXTRA, fmt, __VA_ARGS__) +#else +#define DBGXB(fmt, ...) +#endif + +#endif /* __XENBUS_H__ */ diff --git a/plat/xen/include/xenbus/xs.h b/plat/xen/include/xenbus/xs.h new file mode 100644 index 0000000..d6e1f9e --- /dev/null +++ b/plat/xen/include/xenbus/xs.h @@ -0,0 +1,221 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Authors: Costin Lupu <costin.lupu@xxxxxxxxx> + * + * Copyright (c) 2018, NEC Europe Ltd., NEC Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + */ +/* Xenstore API */ + +#ifndef __XS_H__ +#define __XS_H__ + +#include <xenbus/xenbus.h> + + +typedef unsigned long xenbus_transaction_t; +#define XBT_NIL ((xenbus_transaction_t) 0) + + +/* + * Equivalent of asprintf function. + * + * @param fmt Format string + * @return On success, returns a malloc'd string. On error, returns a negative + * error number which should be checked using PTRISERR. + */ +char *xs_join(const char *fmt, ...) __printf(1, 2); + +/* + * Read the value associated with a path. + * + * @param xbt Xenbus transaction id + * @param path Xenstore path + * @return On success, returns a malloc'd copy of the value. On error, returns + * a negative error number which should be checked using PTRISERR. + */ +char *xs_read(xenbus_transaction_t xbt, const char *path); + +/* + * Associates a value with a path. + * + * @param xbt Xenbus transaction id + * @param path Xenstore path + * @param value Xenstore value + * @return 0 on success, a negative errno value on error. + */ +int xs_write(xenbus_transaction_t xbt, const char *path, const char *value); + +/* + * List the contents of a directory. + * + * @param xbt Xenbus transaction id + * @param path Xenstore directory path + * @return On success, returns a malloc'd array of pointers to malloc'd strings. + * The array is NULL terminated. On error, returns a negative error number which + * should be checked using PTRISERR. May block. + */ +char **xs_ls(xenbus_transaction_t xbt, const char *path); + +/* + * Removes the value associated with a path. + * + * @param xbt Xenbus transaction id + * @param path Xenstore path + * @return 0 on success, a negative errno value on error. + */ +int xs_rm(xenbus_transaction_t xbt, const char *path); + +/* + * Reads permissions associated with a path. + * + * @param xbt Xenbus transaction id + * @param path Xenstore path + * @return On success, returns a malloc'd copy of the value. On error, returns + * a negative error number which should be checked using PTRISERR. + */ +char *xs_get_perms(xenbus_transaction_t xbt, const char *path); + +/* + * Sets the permissions associated with a path. + * + * @param xbt Xenbus transaction id + * @param path Xenstore path + * @param domid The id of the domain for which permissions are set + * @param perm Permissions character (e.g. 'w', 'r', 'b', 'n') + * @return 0 on success, a negative errno value on error. + */ +int xs_set_perms(xenbus_transaction_t xbt, const char *path, + domid_t domid, char perm); + +/* + * Start a xenbus transaction. Returns the transaction in xbt on + * success or an error number otherwise. + * + * @param xbt Address for returning the Xenbus transaction id + * @return 0 on success, a negative errno value on error. + */ +int xs_transaction_start(xenbus_transaction_t *xbt); + +/* + * End a xenbus transaction. Returns non-zero on failure. + * Parameter abort says whether the transaction should be aborted. + * Returns 1 in *retry iff the transaction should be retried. + * + * @param xbt Xenbus transaction id + * @param abort Non-zero if transaction should be aborted + * @param retry Address for returning the retry suggestion + * @return 0 on success, a negative errno value on error. + */ +int xs_transaction_end(xenbus_transaction_t xbt, int abort, int *retry); + +/* + * Sends a debug message to the Xenstore daemon for writing it in the debug log + * + * @param msg The logged message + * @return 0 on success, a negative errno value on error. + */ +int xs_debug_msg(const char *msg); + +/* + * Read path and parse it as an integer. + * + * @param path Xenstore path + * @param value Returned int value + * @return 0 on success, a negative errno value on error. + */ +int xs_read_integer(const char *path, int *value); + +/* + * Contraction of sprintf and xs_read(path/node). + * + * @param xbt Xenbus transaction id + * @param fmt Path format string + * @return On success, returns a malloc'd copy of the value. On error, returns + * a negative error number which should be checked using PTRISERR. + */ +char *xs_readf(xenbus_transaction_t xbt, + const char *fmt, ...) __printf(2, 3); + +/* + * Contraction of sprintf and xs_write(path/node). + */ +int xs_printf(xenbus_transaction_t xbt, const char *node, const char *path, + const char *fmt, ...) __printf(4, 5); + +/* + * Utility function to figure out our domain id + * + * @return Our domain id + */ +domid_t xs_get_self_id(void); + +/* + * Registers a Xenstore watch + * + * @param xbt Xenbus transaction id + * @param path Xenstore path + * @param token Watch identification token + * @param events The associated watch events list + * @return 0 on success, a negative errno value on error. + */ +int xs_watch_path_token(xenbus_transaction_t xbt, + const char *path, const char *token, + xenbus_watch_evlist_t *events); + +/* + * Unregisters a Xenstore watch + * + * @param xbt Xenbus transaction id + * @param path Xenstore path + * @param token Watch identification token + * @return 0 on success, a negative errno value on error. + */ +int xs_unwatch_path_token(xenbus_transaction_t xbt, + const char *path, const char *token); + +/* + * Registers a Xenstore watch using the default global token and event list. + * + * @param xbt Xenbus transaction id + * @param path Xenstore path + * @return 0 on success, a negative errno value on error. + */ +int xs_watch_path(xenbus_transaction_t xbt, const char *path); + +/* + * Unregisters a Xenstore watch using the default global token. + * + * @param xbt Xenbus transaction id + * @param path Xenstore path + * @return 0 on success, a negative errno value on error. + */ +int xs_unwatch_path(xenbus_transaction_t xbt, const char *path); + +#endif /* __XS_H__ */ diff --git a/plat/xen/xenbus/client.c b/plat/xen/xenbus/client.c new file mode 100644 index 0000000..f50f469 --- /dev/null +++ b/plat/xen/xenbus/client.c @@ -0,0 +1,278 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Authors: Steven Smith (sos22@xxxxxxxxx) + * Grzegorz Milos (gm281@xxxxxxxxx) + * John D. Ramsdell + * Costin Lupu <costin.lupu@xxxxxxxxx> + * + * Copyright (c) 2006, Cambridge University + * 2018, NEC Europe Ltd., NEC Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + */ +/* + * Client interface between the device and the Xenbus driver. + * Ported from Mini-OS xenbus.c + */ + +#include <stdio.h> +#include <string.h> +#include <uk/errptr.h> +#include <uk/wait.h> +#include <xenbus/client.h> + + +#define XENBUS_STATE_ENTRY(name) \ + [XenbusState##name] = #name + +static const char *const xb_state_tbl[] = { + XENBUS_STATE_ENTRY(Unknown), + XENBUS_STATE_ENTRY(Initialising), + XENBUS_STATE_ENTRY(InitWait), + XENBUS_STATE_ENTRY(Initialised), + XENBUS_STATE_ENTRY(Connected), + XENBUS_STATE_ENTRY(Closing), + XENBUS_STATE_ENTRY(Closed), + XENBUS_STATE_ENTRY(Reconfiguring), + XENBUS_STATE_ENTRY(Reconfigured), +}; + +const char *xenbus_state_to_str(XenbusState state) +{ + return (state < ARRAY_SIZE(xb_state_tbl)) ? + xb_state_tbl[state] : "INVALID"; +} + +#define XENBUS_DEVTYPE_ENTRY(name) \ + [xenbus_dev_##name] = #name + +static const char *const xb_devtype_tbl[] = { + XENBUS_DEVTYPE_ENTRY(none), + XENBUS_DEVTYPE_ENTRY(sysctl), + XENBUS_DEVTYPE_ENTRY(vif), + XENBUS_DEVTYPE_ENTRY(vbd), +}; + +const char *xenbus_devtype_to_str(enum xenbus_dev_type devtype) +{ + return (devtype < ARRAY_SIZE(xb_devtype_tbl)) ? + xb_devtype_tbl[devtype] : "INVALID"; +} + +enum xenbus_dev_type xenbus_str_to_devtype(const char *devtypestr) +{ + for (int i = 0; i < (int) ARRAY_SIZE(xb_devtype_tbl); i++) { + if (!strcmp(xb_devtype_tbl[i], devtypestr)) + return (enum xenbus_dev_type) i; + } + + return xenbus_dev_none; +} + +/* + * Watches + */ + +static DEFINE_WAIT_QUEUE(xenbus_watch_wq); +static xenbus_watch_evlist_t xenbus_watch_evlist; + +/* + * The split between 'xenbus_wait_watch_event_return' and + * 'xenbus_wait_watch_event' was taken from Mini-OS which uses this approach + * to handle the events explicitly in the TPM frontend. + */ +static struct xenbus_watch_event * +xenbus_wait_watch_event_return(xenbus_watch_evlist_t *evlist) +{ + struct xenbus_watch_event *event; + DEFINE_WAIT(w); + + if (!evlist) + evlist = &xenbus_watch_evlist; + + while (!(event = *evlist)) { + uk_waitq_add_waiter(&xenbus_watch_wq, &w); + uk_sched_yield(); + } + uk_waitq_remove_waiter(&xenbus_watch_wq, &w); + + /* pop the event */ + *evlist = event->next; + + return event; +} + +void xenbus_wait_watch_event(xenbus_watch_evlist_t *evlist) +{ + struct xenbus_watch_event *event; + + if (!evlist) + evlist = &xenbus_watch_evlist; + + event = xenbus_wait_watch_event_return(evlist); + UK_ASSERT(event != NULL); + + uk_xb_free(event); +} + +int xenbus_notify_watch_event(xenbus_watch_evlist_t *evlist, + struct xenbus_watch_event *event) +{ + if (evlist == NULL || event == NULL) + return -EINVAL; + + /* add the event at the beginning of the list */ + event->next = *evlist; + *evlist = event; + + uk_waitq_wake_up(&xenbus_watch_wq); + + return 0; +} + +int xenbus_wait_for_value(const char *path, const char *value, + xenbus_watch_evlist_t *evlist) +{ + char *res; + int rc; + + if (!evlist) + evlist = &xenbus_watch_evlist; + + for (;;) { + res = xs_read(XBT_NIL, path); + if (PTRISERR(res)) + return PTR2ERR(res); + + rc = strcmp(value, res); + uk_xb_free(res); + + if (rc == 0) + break; + + xenbus_wait_watch_event(evlist); + } + + return 0; +} + +XenbusState xenbus_read_driver_state(const char *path) +{ + char state_path[strlen(path) + sizeof("/state")]; + XenbusState state = XenbusStateUnknown; + + sprintf(state_path, "%s/state", path); + xs_read_integer(state_path, (int *) &state); + + return state; +} + +int xenbus_switch_state(struct xenbus_device *xendev, XenbusState state, + xenbus_transaction_t xbt) +{ + char state_path[strlen(xendev->nodename) + sizeof("/state")]; + char *current_state_str; + XenbusState current_state; + int xbt_flag = 0; /* non-zero if transaction started */ + int retry = 0; + int err; + + sprintf(state_path, "%s/state", xendev->nodename); + + do { + if (xbt == XBT_NIL) { + err = xs_transaction_start(&xbt); + if (err) + goto exit; + xbt_flag = 1; + } + + /* check if state is already set */ + current_state_str = xs_read(xbt, state_path); + if (PTRISERR(current_state_str)) { + err = PTR2ERR(current_state_str); + goto exit; + } + + /* convert to int */ + current_state = (XenbusState) (current_state_str[0] - '0'); + uk_xb_free(current_state_str); + + if (current_state == state) + /* state already set */ + goto exit; + + /* set new state */ + err = xs_printf(xbt, xendev->nodename, "state", "%d", state); + +exit: + if (xbt_flag) { + int _err; + + _err = xs_transaction_end(xbt, 0, &retry); + if (!err) + err = _err; + xbt = XBT_NIL; + } + } while (retry); + + if (err) + uk_printd(DLVL_ERR, "Error switching state to %s: %d\n", + xenbus_state_to_str(state), err); + + return err; +} + +int xenbus_wait_for_state_change(const char *path, XenbusState *state, + xenbus_watch_evlist_t *evlist) +{ + char *current_state_str; + XenbusState current_state; + + if (!evlist) + evlist = &xenbus_watch_evlist; + + for (;;) { + current_state_str = xs_read(XBT_NIL, path); + if (PTRISERR(current_state_str)) + return PTR2ERR(current_state_str); + + /* convert to int */ + current_state = (XenbusState) (current_state_str[0] - '0'); + uk_xb_free(current_state_str); + + if (current_state == *state) + xenbus_wait_watch_event(evlist); + else { + *state = current_state; + break; + } + } + + return 0; +} diff --git a/plat/xen/xenbus/xenbus.c b/plat/xen/xenbus/xenbus.c new file mode 100644 index 0000000..cb177e1 --- /dev/null +++ b/plat/xen/xenbus/xenbus.c @@ -0,0 +1,260 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Authors: Costin Lupu <costin.lupu@xxxxxxxxx> + * + * Copyright (c) 2018, NEC Europe Ltd., NEC Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <inttypes.h> +#include <string.h> +#include <uk/essentials.h> +#include <uk/list.h> +#include <uk/bus.h> +#include <uk/print.h> +#include <uk/errptr.h> +#include <uk/assert.h> +#include <xenbus/xenbus.h> +#include <xenbus/xs.h> +#include <xenbus/client.h> +#include "xs_comms.h" + +#define XS_DEV_PATH "device" + +#define FOREACH_DRIVER(drv) \ + UK_TAILQ_FOREACH(drv, &xbh.drv_list, next) + +#define FOREACH_DRIVER_SAFE(drv, drv_next) \ + UK_TAILQ_FOREACH_SAFE(drv, &xbh.drv_list, next, drv_next) + +#define FOREACH_DEVICE(dev) \ + UK_TAILQ_FOREACH(dev, &xbh.dev_list, ph_next) + + +static struct xenbus_driver *xenbus_find_driver(xenbus_dev_type_t devtype) +{ + struct xenbus_driver *drv; + const xenbus_dev_type_t *pdevtype; + + FOREACH_DRIVER(drv) { + for (pdevtype = drv->device_types; + *pdevtype != xenbus_dev_none; pdevtype++) { + if (*pdevtype == devtype) + return drv; + } + } + + return NULL; /* no driver found */ +} + +static int xenbus_probe_device(struct xenbus_driver *drv, + xenbus_dev_type_t type, const char *name) +{ + int err; + struct xenbus_device *dev; + char *nodename; + XenbusState state; + + /* device/type/name */ + nodename = xs_join("%s/%s/%s", + XS_DEV_PATH, xenbus_devtype_to_str(type), name); + if (PTRISERR(nodename)) { + err = PTR2ERR(nodename); + goto out; + } + + state = xenbus_read_driver_state(nodename); + if (state != XenbusStateInitialising) + return 0; + + uk_printd(DLVL_INFO, "Xenbus device: %s\n", nodename); + + dev = uk_xb_calloc(1, sizeof(*dev) + strlen(nodename) + 1); + if (!dev) { + uk_printd(DLVL_ERR, "Failed to initialize: Out of memory!\n"); + err = -ENOMEM; + goto out; + } + + dev->state = XenbusStateInitialising; + dev->devtype = type; + dev->nodename = (char *) (dev + 1); + strcpy(dev->nodename, nodename); + + err = drv->add_dev(dev); + if (err) { + uk_printd(DLVL_ERR, "Failed to add device.\n"); + uk_xb_free(dev); + } + +out: + if (!PTRISERR(nodename)) + uk_xb_free(nodename); + + return err; +} + +static int xenbus_probe_device_type(const char *devtype_str) +{ + struct xenbus_driver *drv; + xenbus_dev_type_t devtype; + char dirname[sizeof(XS_DEV_PATH) + strlen(devtype_str)]; + char **devices = NULL; + int err = 0; + + devtype = xenbus_str_to_devtype(devtype_str); + if (!devtype) { + uk_printd(DLVL_WARN, + "Unsupported device type: %s\n", devtype_str); + goto out; + } + + drv = xenbus_find_driver(devtype); + if (!drv) { + uk_printd(DLVL_WARN, + "No driver for device type: %s\n", devtype_str); + goto out; + } + + sprintf(dirname, "%s/%s", XS_DEV_PATH, devtype_str); + + /* Get device list */ + devices = xs_ls(XBT_NIL, dirname); + if (PTRISERR(devices)) { + err = PTR2ERR(devices); + uk_printd(DLVL_ERR, + "Error reading %s devices: %d\n", devtype_str, err); + goto out; + } + + for (int i = 0; devices[i] != NULL; i++) { + /* Probe only if no prior error */ + if (err == 0) + err = xenbus_probe_device(drv, devtype, devices[i]); + + uk_xb_free(devices[i]); + } + +out: + if (!PTRISERR(devices)) + uk_xb_free(devices); + + return err; +} + +static int xenbus_probe(void) +{ + char **devtypes; + int err = 0; + + uk_printd(DLVL_INFO, "Probe Xenbus\n"); + + /* Get device types list */ + devtypes = xs_ls(XBT_NIL, XS_DEV_PATH); + if (PTRISERR(devtypes)) { + err = PTR2ERR(devtypes); + uk_printd(DLVL_ERR, "Error reading device types: %d\n", err); + goto out; + } + + for (int i = 0; devtypes[i] != NULL; i++) { + /* Probe only if no previous error */ + if (err == 0) + err = xenbus_probe_device_type(devtypes[i]); + + uk_xb_free(devtypes[i]); + } + +out: + if (!PTRISERR(devtypes)) + uk_xb_free(devtypes); + + return err; +} + +static int xenbus_init(struct uk_alloc *a) +{ + struct xenbus_driver *drv, *drv_next; + int ret = 0; + + UK_ASSERT(a != NULL); + + xbh.a = a; + + ret = xs_comms_init(); + if (ret) { + uk_printd(DLVL_ERR, + "Error initializing Xenstore communication."); + return ret; + } + + if (!xbh.drv_list_initialized) { + UK_TAILQ_INIT(&xbh.drv_list); + xbh.drv_list_initialized = 1; + } + UK_TAILQ_INIT(&xbh.dev_list); + + FOREACH_DRIVER_SAFE(drv, drv_next) { + if (drv->init) { + ret = drv->init(a); + if (ret == 0) + continue; + uk_printd(DLVL_ERR, + "Failed to initialize driver %p: %d\n", + drv, ret); + UK_TAILQ_REMOVE(&xbh.drv_list, drv, next); + } + } + + return 0; +} + +void _xenbus_register_driver(struct xenbus_driver *drv) +{ + UK_ASSERT(drv != NULL); + + if (!xbh.drv_list_initialized) { + UK_TAILQ_INIT(&xbh.drv_list); + xbh.drv_list_initialized = 1; + } + + UK_TAILQ_INSERT_TAIL(&xbh.drv_list, drv, next); +} + +/* + * Register this bus driver to libukbus: + */ +struct xenbus_handler xbh = { + .b.init = xenbus_init, + .b.probe = xenbus_probe +}; + +UK_BUS_REGISTER(&xbh.b); diff --git a/plat/xen/xenbus/xs.c b/plat/xen/xenbus/xs.c new file mode 100644 index 0000000..ca9f6a2 --- /dev/null +++ b/plat/xen/xenbus/xs.c @@ -0,0 +1,518 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Authors: Steven Smith (sos22@xxxxxxxxx) + * Grzegorz Milos (gm281@xxxxxxxxx) + * John D. Ramsdell + * Costin Lupu <costin.lupu@xxxxxxxxx> + * + * Copyright (c) 2006, Cambridge University + * 2018, NEC Europe Ltd., NEC Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + */ +/* + * Ported from Mini-OS xenbus.c + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <uk/errptr.h> +#include <xen/io/xs_wire.h> +#include <xenbus/xs.h> +#include "xs_comms.h" +#include "xs_watch.h" + + +static char *vjoin(const char *fmt, va_list ap) +{ + char *path; + unsigned int path_len; + va_list aq; + + /* figure out the path length */ + va_copy(aq, ap); + path_len = vsnprintf(NULL, 0, fmt, aq); + va_end(aq); + + path = uk_xb_malloc(path_len + 1); + if (!path) + return ERR2PTR(ENOMEM); + + vsnprintf(path, path_len + 1, fmt, ap); + + return path; +} + +char *xs_join(const char *fmt, ...) +{ + char *ret; + va_list ap; + + if (fmt == NULL) + return ERR2PTR(EINVAL); + + va_start(ap, fmt); + ret = vjoin(fmt, ap); + va_end(ap); + + return ret; +} + +/* + * Converts a Xenstore reply error to a positive error number. + * Returns 0 if the reply is successful. + */ +static int reply_to_errno(struct xsd_sockmsg *rep) +{ + int err = 0; + char *errstring; + + if (PTRISERR(rep)) { + err = PTR2ERR(rep); + goto out; + } + + if (rep->type != XS_ERROR) + goto out; + + errstring = (char *) (rep + 1); + + for (int i = 0; i < (int) ARRAY_SIZE(xsd_errors); i++) { + if (!strcmp(errstring, xsd_errors[i].errstring)) { + err = xsd_errors[i].errnum; + goto out; + } + } + + uk_printd(DLVL_WARN, "Unknown Xenstore error: %s\n", errstring); + err = EINVAL; + +out: + return err; +} + +/* Common function used for sending requests when replies aren't handled */ +static int xs_msg(enum xsd_sockmsg_type type, xenbus_transaction_t xbt, + struct xs_req *req, int req_num) +{ + struct xsd_sockmsg *rep; + int err; + + rep = xs_msg_reply(type, xbt, req, req_num); + err = -reply_to_errno(rep); + + uk_xb_free(rep); + + return err; +} + +char *xs_read(xenbus_transaction_t xbt, const char *path) +{ + struct xs_req req; + struct xsd_sockmsg *rep; + char *value; + int err; + + if (path == NULL) + return ERR2PTR(EINVAL); + + req = XS_REQ_STR_NULL(path); + rep = xs_msg_reply(XS_READ, xbt, &req, 1); + err = reply_to_errno(rep); + if (err) { + value = ERR2PTR(err); + goto out; + } + + value = uk_xb_malloc(rep->len + 1); + if (!value) { + value = ERR2PTR(ENOMEM); + goto out; + } + + memcpy(value, rep + 1, rep->len); + value[rep->len] = 0; + +out: + uk_xb_free(rep); + + return value; +} + +int xs_write(xenbus_transaction_t xbt, + const char *path, const char *value) +{ + struct xs_req req[2]; + + if (path == NULL || value == NULL) + return -EINVAL; + + req[0] = XS_REQ_STR_NULL(path); + req[1] = XS_REQ_STR(value); + + return xs_msg(XS_WRITE, xbt, req, ARRAY_SIZE(req)); +} + +char **xs_ls(xenbus_transaction_t xbt, const char *path) +{ + struct xs_req req; + struct xsd_sockmsg *rep; + int nr_elems, offs, i; + char *rep_values, **res = NULL; + int err; + + if (path == NULL) + return ERR2PTR(EINVAL); + + req = XS_REQ_STR_NULL(path); + rep = xs_msg_reply(XS_DIRECTORY, xbt, &req, 1); + err = reply_to_errno(rep); + if (err) + goto out_err; + + rep_values = (char *) (rep + 1); + + for (offs = nr_elems = 0; offs < (int) rep->len; offs++) + nr_elems += (rep_values[offs] == 0); + + res = uk_xb_calloc(nr_elems + 1, sizeof(res[0])); + if (!res) { + err = ENOMEM; + goto out_err; + } + + for (offs = i = 0; i < nr_elems; i++) { + char *elem = rep_values + offs; + int elem_len = strlen(elem); + + res[i] = uk_xb_malloc(elem_len + 1); + if (!res[i]) { + err = ENOMEM; + goto out_err; + } + + memcpy(res[i], elem, elem_len + 1); + + offs += elem_len + 1; + } + + uk_xb_free(rep); + + return res; + +out_err: + if (!PTRISERR(res)) { + for (i = 0; i < nr_elems; i++) { + if (res[i]) + uk_xb_free(res[i]); + } + uk_xb_free(res); + } + uk_xb_free(rep); + + return ERR2PTR(err); +} + +int xs_rm(xenbus_transaction_t xbt, const char *path) +{ + struct xs_req req; + + if (path == NULL) + return -EINVAL; + + req = XS_REQ_STR_NULL(path); + + return xs_msg(XS_RM, xbt, &req, 1); +} + +char *xs_get_perms(xenbus_transaction_t xbt, const char *path) +{ + struct xs_req req; + struct xsd_sockmsg *rep; + char *value; + int err; + + if (path == NULL) + return ERR2PTR(EINVAL); + + req = XS_REQ_STR_NULL(path); + rep = xs_msg_reply(XS_GET_PERMS, xbt, &req, 1); + err = reply_to_errno(rep); + if (err) { + value = ERR2PTR(err); + goto out; + } + + value = uk_xb_malloc(rep->len + 1); + if (!value) { + value = ERR2PTR(ENOMEM); + goto out; + } + + memcpy(value, rep + 1, rep->len); + value[rep->len] = 0; + +out: + uk_xb_free(rep); + + return value; +} + +#define PERM_MAX_SIZE 32 +int xs_set_perms(xenbus_transaction_t xbt, const char *path, + domid_t domid, char perm) +{ + char value[PERM_MAX_SIZE]; + struct xs_req req[2]; + + if (path == NULL) + return -EINVAL; + + req[0] = XS_REQ_STR_NULL(path); + + snprintf(value, PERM_MAX_SIZE, "%c%hu", perm, domid); + req[1].data = value; + req[1].len = strlen(value) + 1; + + return xs_msg(XS_SET_PERMS, xbt, req, ARRAY_SIZE(req)); +} + +int xs_transaction_start(xenbus_transaction_t *xbt) +{ + /* + * xenstored becomes angry if you send a length 0 message, + * so just shove a nul terminator on the end + */ + struct xs_req req; + struct xsd_sockmsg *rep; + int err; + + if (xbt == NULL) + return -EINVAL; + + req = XS_REQ_STR_NULL(""); + rep = xs_msg_reply(XS_TRANSACTION_START, 0, &req, 1); + err = -reply_to_errno(rep); + if (err) + goto out; + + *xbt = strtoul((char *) (rep + 1), NULL, 10); + +out: + uk_xb_free(rep); + + return err; +} + +int xs_transaction_end(xenbus_transaction_t xbt, int abort, int *retry) +{ + struct xs_req req; + int err; + + if (retry == NULL) + return -EINVAL; + + req.data = abort ? "F" : "T"; + req.len = 2; + + err = xs_msg(XS_TRANSACTION_END, xbt, &req, 1); + + *retry = (err == EAGAIN) ? 1 : 0; + + return err; +} + +/* Send a debug message to xenbus. Can block. */ +int xs_debug_msg(const char *msg) +{ + struct xs_req req[3]; + struct xsd_sockmsg *rep; + int err; + + if (msg == NULL) + return -EINVAL; + + req[0] = XS_REQ_STR_NULL("print"); + req[1] = XS_REQ_STR(msg); + req[2] = XS_REQ_STR_NULL(""); + + rep = xs_msg_reply(XS_DEBUG, XBT_NIL, req, ARRAY_SIZE(req)); + err = -reply_to_errno(rep); + if (err) + goto out; + + uk_printd(DLVL_EXTRA, + "Got a reply, type %"__PRIu32", id %"__PRIu32", len %"__PRIu32".\n", + rep->type, rep->req_id, rep->len); + +out: + uk_xb_free(rep); + + return err; +} + +int xs_read_integer(const char *path, int *value) +{ + char *value_str; + + if (path == NULL || value == NULL) + return -EINVAL; + + value_str = xs_read(XBT_NIL, path); + if (PTRISERR(value_str)) + return PTR2ERR(value_str); + + *value = atoi(value_str); + + uk_xb_free(value_str); + + return 0; +} + +char *xs_readf(xenbus_transaction_t xbt, const char *fmt, ...) +{ + char *fullpath; + char *val; + va_list args; + + if (fmt == NULL) + return ERR2PTR(EINVAL); + + va_start(args, fmt); + fullpath = vjoin(fmt, args); + va_end(args); + + if (PTRISERR(fullpath)) + return fullpath; + + val = xs_read(xbt, fullpath); + + uk_xb_free(fullpath); + + return val; +} + +int xs_printf(xenbus_transaction_t xbt, + const char *node, const char *path, const char *fmt, ...) +{ +#define BUFFER_SIZE 256 + char fullpath[BUFFER_SIZE]; + char val[BUFFER_SIZE]; + va_list args; + int err; + + if (node == NULL || path == NULL || fmt == NULL) + return -EINVAL; + + if (strlen(node) + strlen(path) + 1 >= BUFFER_SIZE) + return -ENOMEM; + + sprintf(fullpath, "%s/%s", node, path); + + va_start(args, fmt); + vsprintf(val, fmt, args); + va_end(args); + + err = xs_write(xbt, fullpath, val); + + return err; +} + +domid_t xs_get_self_id(void) +{ + char *domid_str; + domid_t domid; + + domid_str = xs_read(XBT_NIL, "domid"); + if (PTRISERR(domid_str)) + UK_CRASH("Error reading domain id."); + + domid = (domid_t) strtoul(domid_str, NULL, 10); + + uk_xb_free(domid_str); + + return domid; +} + +int xs_watch_path_token(xenbus_transaction_t xbt, + const char *path, const char *token, + xenbus_watch_evlist_t *events) +{ + struct xs_watch *watch; + struct xs_req req[2]; + + if (path == NULL || token == NULL || events == NULL) + return -EINVAL; + + watch = xs_watch_create(path, token, events); + if (PTRISERR(watch)) + return PTR2ERR(watch); + + req[0] = XS_REQ_STR_NULL(path); + req[1] = XS_REQ_STR_NULL(token); + + return xs_msg(XS_WATCH, xbt, req, ARRAY_SIZE(req)); +} + +int xs_unwatch_path_token(xenbus_transaction_t xbt, + const char *path, const char *token) +{ + struct xs_req req[2]; + int err; + + if (path == NULL || token == NULL) + return -EINVAL; + + req[0] = XS_REQ_STR_NULL(path); + req[1] = XS_REQ_STR_NULL(token); + + err = xs_msg(XS_UNWATCH, xbt, req, ARRAY_SIZE(req)); + if (err) + goto out; + + err = xs_watch_destroy(path, token); + +out: + return err; +} + +#define GLOBAL_XS_WATCH_TOKEN "global_xenstore_watch" +static xenbus_watch_evlist_t global_xs_watch_evlist; + +int xs_watch_path(xenbus_transaction_t xbt, const char *path) +{ + return xs_watch_path_token(xbt, path, GLOBAL_XS_WATCH_TOKEN, + &global_xs_watch_evlist); +} + +int xs_unwatch_path(xenbus_transaction_t xbt, const char *path) +{ + return xs_unwatch_path_token(xbt, path, GLOBAL_XS_WATCH_TOKEN); +} diff --git a/plat/xen/xenbus/xs_comms.c b/plat/xen/xenbus/xs_comms.c new file mode 100644 index 0000000..80c2bdf --- /dev/null +++ b/plat/xen/xenbus/xs_comms.c @@ -0,0 +1,484 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Authors: Steven Smith (sos22@xxxxxxxxx) + * Grzegorz Milos (gm281@xxxxxxxxx) + * John D. Ramsdell + * Costin Lupu <costin.lupu@xxxxxxxxx> + * + * Copyright (c) 2006, Cambridge University + * 2018, NEC Europe Ltd., NEC Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + */ +/* + * Communication with Xenstore + * Ported from Mini-OS xenbus.c + */ + +#include <string.h> +#include <uk/errptr.h> +#include <uk/wait.h> +#include <uk/arch/spinlock.h> +#include <common/events.h> +#include <xen-x86/mm.h> +#include <xen-x86/setup.h> +#include "xs_comms.h" +#include "xs_watch.h" + + +/* + * Xenstore handler structure + */ +struct xs_handler { + /**< Non-zero if initialized */ + int initialized; + /**< Communication: event channel */ + evtchn_port_t evtchn; + /**< Communication: shared memory */ + struct xenstore_domain_interface *buf; + /**< Thread processing incoming xs replies */ + struct uk_thread *thread; + /**< Waiting queue for notifying incoming xs replies */ + struct uk_waitq waitq; +}; + +static struct xs_handler xsh; + + +struct reqid_map_value { + /**< Non-zero if used and waiting for reply */ + int in_use; + /**< Waiting queue for incoming reply notification */ + struct uk_waitq waitq; + /**< Received reply */ + void *reply; +}; + +/* + * Structure for mapping in-flight requests IDs to incoming replies. + * Request IDs are reused, hence the limited set of ID values. + */ +struct reqid_map { + /**< Number of live requests */ + __u32 num_live; + /**< Current available request ID */ + __u32 probe; + /**< Lock */ + spinlock_t lock; + /**< Waiting queue for 'not-full' notifications */ + struct uk_waitq waitq; + + /* Map size is power of 2 */ +#define REQID_MAP_SHIFT 5 +#define REQID_MAP_SIZE (1 << REQID_MAP_SHIFT) + /**< Device bus */ + struct reqid_map_value values[REQID_MAP_SIZE]; +}; + +static struct reqid_map reqid_map; + +static void reqid_map_init(struct reqid_map *reqidm) +{ + reqidm->num_live = 0; + reqidm->probe = 0; + ukarch_spin_lock_init(&reqidm->lock); + uk_waitq_init(&reqidm->waitq); +} + +/* + * Allocate an identifier for a Xenstore request. + * Blocks if none are available. + */ +static int reqid_map_get_id(void) +{ + struct reqid_map_value *reqid_map_val; + __u32 probe; + + /* wait for an available entry */ + while (1) { + ukarch_spin_lock(&reqid_map.lock); + + if (reqid_map.num_live < REQID_MAP_SIZE) + break; + + ukarch_spin_unlock(&reqid_map.lock); + + uk_waitq_wait_event(&reqid_map.waitq, + (reqid_map.num_live < REQID_MAP_SIZE)); + } + + /* find an available entry */ + probe = reqid_map.probe; + while (1) { + reqid_map_val = &reqid_map.values[probe]; + + if (!reqid_map_val->in_use) + break; + + probe = (probe + 1) & ~REQID_MAP_SIZE; + /* + * The request IDs set must be big enough to hold the + * maximum number of in-flight Xenstore requests. + */ + UK_ASSERT(probe != reqid_map.probe); + } + + reqid_map_val->in_use = 1; + reqid_map.num_live++; + reqid_map.probe = (probe + 1) & ~REQID_MAP_SIZE; + + ukarch_spin_unlock(&reqid_map.lock); + + uk_waitq_init(&reqid_map_val->waitq); + + return probe; +} + +/* Release a request identifier */ +static void reqid_map_put_id(int id) +{ + struct reqid_map_value *reqid_map_val = &reqid_map.values[id]; + + UK_ASSERT(reqid_map_val->in_use); + + ukarch_spin_lock(&reqid_map.lock); + + reqid_map_val->in_use = 0; + reqid_map_val->reply = NULL; + reqid_map.num_live--; + reqid_map.probe = id; + + if (reqid_map.num_live == 0 || reqid_map.num_live == REQID_MAP_SIZE - 1) + uk_waitq_wake_up(&reqid_map.waitq); + + ukarch_spin_unlock(&reqid_map.lock); +} + +static int xs_avail_space_for_read(unsigned int req_size) +{ + return (xsh.buf->rsp_prod - xsh.buf->rsp_cons >= req_size); +} + +static int xs_avail_space_for_write(unsigned int req_size) +{ + return (xsh.buf->req_prod - xsh.buf->req_cons + + req_size <= XENSTORE_RING_SIZE); +} + +static void memcpy_from_ring(const char *ring, char *dest, int off, int len) +{ + int c1, c2; + + c1 = MIN(len, XENSTORE_RING_SIZE - off); + c2 = len - c1; + + memcpy(dest, ring + off, c1); + memcpy(dest + c1, ring, c2); +} + +/* + * Send data to Xenstore. This can block. All of the requests are seen + * by Xenstore as if sent atomically. + */ +static void xs_msg_write(struct xsd_sockmsg *xsd_req, struct xs_req *req) +{ + XENSTORE_RING_IDX prod; + const struct xs_req *crnt_req; + struct xs_req req_hdr; + unsigned int req_size, req_off; + unsigned int buf_off; + unsigned int this_chunk; + int rc; + + req_size = sizeof(*xsd_req) + xsd_req->len; + UK_ASSERT(req_size <= XENSTORE_RING_SIZE); + + req_hdr.data = xsd_req; + req_hdr.len = sizeof(*xsd_req); + + crnt_req = &req_hdr; + + /* + * Wait for the ring to drain to the point where + * we can send the message. + */ + if (!xs_avail_space_for_write(req_size)) { + /* Wait for there to be space on the ring */ + DBGXB("prod %d, len %d, cons %d, size %d; waiting.\n", + prod, req_size, buf->req_cons, XENSTORE_RING_SIZE); + + uk_waitq_wait_event(&xsh.waitq, + xs_avail_space_for_write(req_size)); + DBGXB("Back from wait.\n"); + } + + /* + * We're now guaranteed to be able to send the message + * without overflowing the ring. Do so. + */ + + prod = xsh.buf->req_prod; + req_off = 0; + buf_off = 0; + while (req_off < req_size) { + this_chunk = MIN(crnt_req->len - buf_off, + XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(prod)); + + memcpy( + (char *) xsh.buf->req + MASK_XENSTORE_IDX(prod), + (char *) crnt_req->data + buf_off, + this_chunk + ); + + prod += this_chunk; + req_off += this_chunk; + buf_off += this_chunk; + + if (buf_off == crnt_req->len) { + buf_off = 0; + if (crnt_req == &req_hdr) + crnt_req = req; + else + crnt_req++; + } + } + + DBGXB("Complete main loop of xb_write.\n"); + UK_ASSERT(buf_off == 0); + UK_ASSERT(req_off == req_size); + UK_ASSERT(prod <= xsh.buf->req_cons + XENSTORE_RING_SIZE); + + /* Remote must see entire message before updating indexes */ + wmb(); + + xsh.buf->req_prod += req_size; + + /* Send evtchn to notify remote */ + rc = notify_remote_via_evtchn(xsh.evtchn); + UK_ASSERT(rc == 0); +} + +struct xsd_sockmsg *xs_msg_reply(enum xsd_sockmsg_type msg_type, + xenbus_transaction_t xbt, + struct xs_req *req, int req_num) +{ + __u32 reqid; + struct reqid_map_value *reqid_map_val; + struct xsd_sockmsg xsd_req; + struct xsd_sockmsg *xsd_rep; + + /* get a request id */ + reqid = reqid_map_get_id(); + reqid_map_val = &reqid_map.values[reqid]; + + xsd_req.type = msg_type; + xsd_req.req_id = reqid; + xsd_req.tx_id = xbt; + xsd_req.len = 0; + for (int i = 0; i < req_num; i++) + xsd_req.len += req[i].len; + + /* send the request */ + xs_msg_write(&xsd_req, req); + + /* wait reply */ + uk_waitq_wait_event(&reqid_map_val->waitq, + reqid_map_val->reply != NULL); + + xsd_rep = reqid_map_val->reply; + UK_ASSERT(xsd_rep->req_id == reqid); + + /* free request id */ + reqid_map_put_id(reqid); + + return xsd_rep; +} + +/* Process an incoming xs reply */ +static void process_reply(struct xsd_sockmsg *msg) +{ + struct reqid_map_value *req_map_val = &reqid_map.values[msg->req_id]; + int msg_size = sizeof(*msg) + msg->len; + + req_map_val->reply = uk_xb_malloc(msg_size); + if (req_map_val->reply == NULL) { + uk_printd(DLVL_ERR, + "No memory available for saving Xenstore reply!"); + return; + } + + memcpy_from_ring( + xsh.buf->rsp, + req_map_val->reply, + MASK_XENSTORE_IDX(xsh.buf->rsp_cons), + msg_size + ); + + xsh.buf->rsp_cons += msg_size; + + /* notify waiting requester */ + uk_waitq_wake_up(&req_map_val->waitq); +} + +/* Process an incoming xs watch event */ +static void process_watch_event(struct xsd_sockmsg *msg) +{ + struct xs_watch_event *event; + char *data; + int err; + + event = uk_xb_malloc(sizeof(*event) + msg->len); + if (event == NULL) { + uk_printd(DLVL_ERR, + "No memory available for saving Xenstore watch notification info!"); + return; + } + + data = (char *) event + sizeof(*event); + + memcpy_from_ring( + xsh.buf->rsp, + data, + MASK_XENSTORE_IDX(xsh.buf->rsp_cons + sizeof(*msg)), + msg->len + ); + xsh.buf->rsp_cons += sizeof(*msg) + msg->len; + + event->xs.path = data; + event->xs.token = data + strlen(data) + 1; + + err = xs_watch_notify(event); + if (err) { + uk_printd(DLVL_ERR, "Invalid watch event."); + uk_xb_free(event); + } +} + +static void xs_thread_func(void *ign __unused) +{ + struct xsd_sockmsg msg; + XENSTORE_RING_IDX prod = xsh.buf->rsp_prod; + + for (;;) { + /* wait for incoming xs response */ + uk_waitq_wait_event(&xsh.waitq, prod != xsh.buf->rsp_prod); + + while (1) { + prod = xsh.buf->rsp_prod; + + DBGXB("Rsp_cons %d, rsp_prod %d.\n", + buf->rsp_cons, buf->rsp_prod); + + if (!xs_avail_space_for_read(sizeof(msg))) + break; + + /* Make sure data is read after reading the indexes */ + rmb(); + + /* copy the message */ + memcpy_from_ring( + xsh.buf->rsp, + (char *) &msg, + MASK_XENSTORE_IDX(xsh.buf->rsp_cons), + sizeof(msg) + ); + + DBGXB("Msg len %d, %d avail, id %d.\n", + msg.len + sizeof(msg), + xsh.buf->rsp_prod - xsh.buf->rsp_cons, + msg.req_id); + + if (!xs_avail_space_for_read(sizeof(msg) + msg.len)) + break; + + DBGXB("Message is good.\n"); + + if (msg.type == XS_WATCH_EVENT) + process_watch_event(&msg); + else + process_reply(&msg); + } + } +} + +static void xs_evtchn_handler(evtchn_port_t port, + struct __regs *regs __unused, void *ign __unused) +{ + UK_ASSERT(xsh.initialized == 1); + UK_ASSERT(xsh.evtchn == port); + uk_waitq_wake_up(&xsh.waitq); +} + +int xs_comms_init(void) +{ + struct uk_thread *thread; + evtchn_port_t port; + + UK_ASSERT(xsh.initialized == 0); + + reqid_map_init(&reqid_map); + + uk_waitq_init(&xsh.waitq); + + thread = uk_thread_create("xenstore", xs_thread_func, NULL); + if (PTRISERR(thread)) + return PTR2ERR(thread); + + xsh.thread = thread; + + xsh.evtchn = HYPERVISOR_start_info->store_evtchn; + xsh.buf = mfn_to_virt(HYPERVISOR_start_info->store_mfn); + + port = bind_evtchn(xsh.evtchn, xs_evtchn_handler, NULL); + UK_ASSERT(port == xsh.evtchn); + unmask_evtchn(xsh.evtchn); + + xsh.initialized = 1; + + uk_printd(DLVL_INFO, + "Xenstore connection initialised on port %d, buf %p (mfn %#lx)\n", + port, xsh.buf, HYPERVISOR_start_info->store_mfn); + + return 0; +} + +void xs_comms_fini(void) +{ + UK_ASSERT(xsh.initialized == 1); + + mask_evtchn(xsh.evtchn); + unbind_evtchn(xsh.evtchn); + + xsh.buf = NULL; + + /* TODO stop thread, instead of killing it */ + uk_thread_destroy(xsh.thread); + xsh.thread = NULL; + + xsh.initialized = 0; +} diff --git a/plat/xen/xenbus/xs_comms.h b/plat/xen/xenbus/xs_comms.h new file mode 100644 index 0000000..230c4ad --- /dev/null +++ b/plat/xen/xenbus/xs_comms.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Authors: Costin Lupu <costin.lupu@xxxxxxxxx> + * + * Copyright (c) 2018, NEC Europe Ltd., NEC Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + */ + +#ifndef __XS_COMMS_H__ +#define __XS_COMMS_H__ + +#include <xen/io/xs_wire.h> +#include <xenbus/xenbus.h> +#include <xenbus/xs.h> + +int xs_comms_init(void); +void xs_comms_fini(void); + +struct xs_req { + const void *data; + unsigned int len; +}; + +/* Helper macro for initializing xs requests from strings */ +#define XS_REQ_STR(str) \ + ((struct xs_req) { str, strlen(str) }) + +/* Helper macro for initializing xs requests from strings + * (w/ null terminator) + */ +#define XS_REQ_STR_NULL(str) \ + ((struct xs_req) { str, strlen(str) + 1 }) + +/* + * Sends a message to Xenstore and blocks waiting for a reply. + * The reply is malloc'ed and should be freed by the caller. + * + * @param msg_type Xenstore message type + * @param xbt Xenbus transaction id + * @param req Array of requests + * @param req_num Requests number + * @return On success, returns a malloc'd copy of the reply. On error, returns + * a negative error number which should be checked using PTRISERR. + */ +struct xsd_sockmsg *xs_msg_reply(enum xsd_sockmsg_type msg_type, + xenbus_transaction_t xbt, + struct xs_req *req, int req_num); + +#endif /* __XS_COMMS_H__ */ diff --git a/plat/xen/xenbus/xs_watch.c b/plat/xen/xenbus/xs_watch.c new file mode 100644 index 0000000..912fdd2 --- /dev/null +++ b/plat/xen/xenbus/xs_watch.c @@ -0,0 +1,159 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Authors: Costin Lupu <costin.lupu@xxxxxxxxx> + * + * Copyright (c) 2018, NEC Europe Ltd., NEC Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + */ +/* Internal API for Xenstore watches */ + +#include <string.h> +#include <uk/errptr.h> +#include <xenbus/client.h> +#include "xs_watch.h" + +/* Watches list */ +static struct xenbus_watch *xs_watch_list; + +static void watch_list_add(struct xs_watch *xsw) +{ + xsw->base.next = xs_watch_list; + xs_watch_list = &xsw->base; +} + +static int xs_watch_info_equal(const struct xs_watch_info *xswi, + const char *path, const char *token) +{ + return (strcmp(xswi->path, path) == 0 && + strcmp(xswi->token, token) == 0); +} + +static struct xs_watch *watch_list_remove(const char *path, const char *token) +{ + struct xenbus_watch *xbw, **prev_xbw; + + for (prev_xbw = &xs_watch_list, xbw = *prev_xbw; + xbw != NULL; + prev_xbw = &xbw->next, xbw = *prev_xbw) { + struct xs_watch *xsw = (struct xs_watch *) xbw; + + if (xs_watch_info_equal(&xsw->xs, path, token)) { + *prev_xbw = xbw->next; + return xsw; + } + } + + return NULL; +} + +static struct xs_watch *watch_list_find(const char *path, const char *token) +{ + struct xenbus_watch *xbw; + + for (xbw = xs_watch_list; xbw != NULL; xbw = xbw->next) { + struct xs_watch *xsw = (struct xs_watch *) xbw; + + if (xs_watch_info_equal(&xsw->xs, path, token)) + return xsw; + } + + return NULL; +} + +struct xs_watch *xs_watch_create(const char *path, const char *token, + xenbus_watch_evlist_t *events) +{ + struct xs_watch *xsw; + char *tmpstr; + int stringlen; + + UK_ASSERT(path != NULL); + UK_ASSERT(token != NULL); + UK_ASSERT(events != NULL); + + stringlen = strlen(token) + 1 + strlen(path) + 1; + + xsw = uk_xb_malloc(sizeof(*xsw) + stringlen); + if (!xsw) + return ERR2PTR(ENOMEM); + + xsw->base.events = events; + + /* set token */ + tmpstr = (char *) (xsw + 1); + strcpy(tmpstr, token); + xsw->xs.token = tmpstr; + + /* set path */ + tmpstr += strlen(token) + 1; + strcpy(tmpstr, path); + xsw->xs.path = tmpstr; + + watch_list_add(xsw); + + return xsw; +} + +int xs_watch_destroy(const char *path, const char *token) +{ + struct xs_watch *xsw; + int err = 0; + + UK_ASSERT(path != NULL); + UK_ASSERT(token != NULL); + + xsw = watch_list_remove(path, token); + if (xsw) + uk_xb_free(xsw); + else + err = -ENOENT; + + return err; +} + +int xs_watch_notify(struct xs_watch_event *event) +{ + struct xs_watch *xsw; + int err; + + UK_ASSERT(event != NULL); + + /* check if we have a local watch for it */ + xsw = watch_list_find(event->xs.path, event->xs.token); + if (!xsw) { + uk_printd(DLVL_WARN, "Unexpected watch: token %s, path %s\n", + event->xs.token, event->xs.path); + return -ENOENT; + } + + /* notify the waiting client */ + err = xenbus_notify_watch_event(xsw->base.events, &event->base); + + return err; +} diff --git a/plat/xen/xenbus/xs_watch.h b/plat/xen/xenbus/xs_watch.h new file mode 100644 index 0000000..77dd575 --- /dev/null +++ b/plat/xen/xenbus/xs_watch.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Authors: Costin Lupu <costin.lupu@xxxxxxxxx> + * + * Copyright (c) 2018, NEC Europe Ltd., NEC Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + */ +/* Internal API for Xenstore watches */ + +#ifndef __XS_WATCH_H__ +#define __XS_WATCH_H__ + +#include <xenbus/xenbus.h> + +/* Xenstore watch info */ +struct xs_watch_info { + /**< Watched Xenstore path */ + char *path; + /**< Watch identification token */ + char *token; +}; + +/* Xenstore watch event */ +struct xs_watch_event { + struct xenbus_watch_event base; + struct xs_watch_info xs; +}; + +/* Xenstore watch */ +struct xs_watch { + struct xenbus_watch base; + struct xs_watch_info xs; +}; + +/* + * Create a Xenstore watch associated with a path. + * + * @param path Xenstore path + * @param token Watch identification token + * @param events List of watch events + * @return On success, returns a malloc'd Xenstore watch. On error, returns + * a negative error number which should be checked using PTRISERR. + */ +struct xs_watch *xs_watch_create(const char *path, const char *token, + xenbus_watch_evlist_t *events); + +/* + * Destroy a previously created Xenstore watch. + * + * @param path Xenstore path + * @param token Watch identification token + * @return 0 on success, a negative errno value on error. + */ +int xs_watch_destroy(const char *path, const char *token); + +/* + * Notifies the watch associated with the incoming event. + * + * @param event Incoming watch event + * @return 0 on success (if there is a watch for the input event token), a + * negative errno value on error. + */ +int xs_watch_notify(struct xs_watch_event *event); + +#endif /* __XS_WATCH_H__ */ -- 2.11.0 _______________________________________________ Minios-devel mailing list Minios-devel@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/mailman/listinfo/minios-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |