diff options
Diffstat (limited to 'drivers/xen/xenbus.c')
| -rw-r--r-- | drivers/xen/xenbus.c | 557 | 
1 files changed, 557 insertions, 0 deletions
| diff --git a/drivers/xen/xenbus.c b/drivers/xen/xenbus.c new file mode 100644 index 00000000000..177d144723c --- /dev/null +++ b/drivers/xen/xenbus.c @@ -0,0 +1,557 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * (C) 2006 - Cambridge University + * (C) 2020 - EPAM Systems Inc. + * + * File: xenbus.c [1] + * Author: Steven Smith (sos22@cam.ac.uk) + * Changes: Grzegorz Milos (gm281@cam.ac.uk) + * Changes: John D. Ramsdell + * + * Date: Jun 2006, changes Aug 2006 + * + * Description: Minimal implementation of xenbus + * + * [1] - http://xenbits.xen.org/gitweb/?p=mini-os.git;a=summary + */ + +#include <common.h> +#include <log.h> + +#include <asm/armv8/mmu.h> +#include <asm/io.h> +#include <asm/xen/system.h> + +#include <linux/bug.h> +#include <linux/compat.h> + +#include <xen/events.h> +#include <xen/hvm.h> +#include <xen/xenbus.h> + +#include <xen/interface/io/xs_wire.h> + +#define map_frame_virt(v)	(v << PAGE_SHIFT) + +#define SCNd16			"d" + +/* Wait for reply time out, ms */ +#define WAIT_XENBUS_TO_MS	5000 +/* Polling time out, ms */ +#define WAIT_XENBUS_POLL_TO_MS	1 + +static struct xenstore_domain_interface *xenstore_buf; + +static char *errmsg(struct xsd_sockmsg *rep); + +u32 xenbus_evtchn; + +struct write_req { +	const void *data; +	unsigned int len; +}; + +static void memcpy_from_ring(const void *r, void *d, int off, int len) +{ +	int c1, c2; +	const char *ring = r; +	char *dest = d; + +	c1 = min(len, XENSTORE_RING_SIZE - off); +	c2 = len - c1; +	memcpy(dest, ring + off, c1); +	memcpy(dest + c1, ring, c2); +} + +/** + * xenbus_get_reply() - Receive reply from xenbus + * @req_reply: reply message structure + * + * Wait for reply message event from the ring and copy received message + * to input xsd_sockmsg structure. Repeat until full reply is + * proceeded. + * + * Return: false - timeout + *	   true - reply is received + */ +static bool xenbus_get_reply(struct xsd_sockmsg **req_reply) +{ +	struct xsd_sockmsg msg; +	unsigned int prod = xenstore_buf->rsp_prod; + +again: +	if (!wait_event_timeout(NULL, prod != xenstore_buf->rsp_prod, +				WAIT_XENBUS_TO_MS)) { +		printk("%s: wait_event timeout\n", __func__); +		return false; +	} + +	prod = xenstore_buf->rsp_prod; +	if (xenstore_buf->rsp_prod - xenstore_buf->rsp_cons < sizeof(msg)) +		goto again; + +	rmb(); +	memcpy_from_ring(xenstore_buf->rsp, &msg, +			 MASK_XENSTORE_IDX(xenstore_buf->rsp_cons), +			 sizeof(msg)); + +	if (xenstore_buf->rsp_prod - xenstore_buf->rsp_cons < sizeof(msg) + msg.len) +		goto again; + +	/* We do not support and expect any Xen bus wathes. */ +	BUG_ON(msg.type == XS_WATCH_EVENT); + +	*req_reply = malloc(sizeof(msg) + msg.len); +	memcpy_from_ring(xenstore_buf->rsp, *req_reply, +			 MASK_XENSTORE_IDX(xenstore_buf->rsp_cons), +			 msg.len + sizeof(msg)); +	mb(); +	xenstore_buf->rsp_cons += msg.len + sizeof(msg); + +	wmb(); +	notify_remote_via_evtchn(xenbus_evtchn); +	return true; +} + +char *xenbus_switch_state(xenbus_transaction_t xbt, const char *path, +			  XenbusState state) +{ +	char *current_state; +	char *msg = NULL; +	char *msg2 = NULL; +	char value[2]; +	XenbusState rs; +	int xbt_flag = 0; +	int retry = 0; + +	do { +		if (xbt == XBT_NIL) { +			msg = xenbus_transaction_start(&xbt); +			if (msg) +				goto exit; +			xbt_flag = 1; +		} + +		msg = xenbus_read(xbt, path, ¤t_state); +		if (msg) +			goto exit; + +		rs = (XenbusState)(current_state[0] - '0'); +		free(current_state); +		if (rs == state) { +			msg = NULL; +			goto exit; +		} + +		snprintf(value, 2, "%d", state); +		msg = xenbus_write(xbt, path, value); + +exit: +		if (xbt_flag) { +			msg2 = xenbus_transaction_end(xbt, 0, &retry); +			xbt = XBT_NIL; +		} +		if (msg == NULL && msg2 != NULL) +			msg = msg2; +		else +			free(msg2); +	} while (retry); + +	return msg; +} + +char *xenbus_wait_for_state_change(const char *path, XenbusState *state) +{ +	for (;;) { +		char *res, *msg; +		XenbusState rs; + +		msg = xenbus_read(XBT_NIL, path, &res); +		if (msg) +			return msg; + +		rs = (XenbusState)(res[0] - 48); +		free(res); + +		if (rs == *state) { +			wait_event_timeout(NULL, false, WAIT_XENBUS_POLL_TO_MS); +		} else { +			*state = rs; +			break; +		} +	} +	return NULL; +} + +/* Send data to xenbus.  This can block.  All of the requests are seen + * by xenbus as if sent atomically.  The header is added + * automatically, using type %type, req_id %req_id, and trans_id + * %trans_id. + */ +static void xb_write(int type, int req_id, xenbus_transaction_t trans_id, +		     const struct write_req *req, int nr_reqs) +{ +	XENSTORE_RING_IDX prod; +	int r; +	int len = 0; +	const struct write_req *cur_req; +	int req_off; +	int total_off; +	int this_chunk; +	struct xsd_sockmsg m = { +		.type = type, +		.req_id = req_id, +		.tx_id = trans_id +	}; +	struct write_req header_req = { +		&m, +		sizeof(m) +	}; + +	for (r = 0; r < nr_reqs; r++) +		len += req[r].len; +	m.len = len; +	len += sizeof(m); + +	cur_req = &header_req; + +	BUG_ON(len > XENSTORE_RING_SIZE); +	prod = xenstore_buf->req_prod; +	/* We are running synchronously, so it is a bug if we do not +	 * have enough room to send a message: please note that a message +	 * can occupy multiple slots in the ring buffer. +	 */ +	BUG_ON(prod + len - xenstore_buf->req_cons > XENSTORE_RING_SIZE); + +	total_off = 0; +	req_off = 0; +	while (total_off < len) { +		this_chunk = min(cur_req->len - req_off, +				 XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(prod)); +		memcpy((char *)xenstore_buf->req + MASK_XENSTORE_IDX(prod), +		       (char *)cur_req->data + req_off, this_chunk); +		prod += this_chunk; +		req_off += this_chunk; +		total_off += this_chunk; +		if (req_off == cur_req->len) { +			req_off = 0; +			if (cur_req == &header_req) +				cur_req = req; +			else +				cur_req++; +		} +	} + +	BUG_ON(req_off != 0); +	BUG_ON(total_off != len); +	BUG_ON(prod > xenstore_buf->req_cons + XENSTORE_RING_SIZE); + +	/* Remote must see entire message before updating indexes */ +	wmb(); + +	xenstore_buf->req_prod += len; + +	/* Send evtchn to notify remote */ +	notify_remote_via_evtchn(xenbus_evtchn); +} + +/* Send a message to xenbus, in the same fashion as xb_write, and + * block waiting for a reply.  The reply is malloced and should be + * freed by the caller. + */ +struct xsd_sockmsg *xenbus_msg_reply(int type, +				     xenbus_transaction_t trans, +				     struct write_req *io, +				     int nr_reqs) +{ +	struct xsd_sockmsg *rep; + +	/* We do not use request identifier which is echoed in daemon's response. */ +	xb_write(type, 0, trans, io, nr_reqs); +	/* Now wait for the message to arrive. */ +	if (!xenbus_get_reply(&rep)) +		return NULL; +	return rep; +} + +static char *errmsg(struct xsd_sockmsg *rep) +{ +	char *res; + +	if (!rep) { +		char msg[] = "No reply"; +		size_t len = strlen(msg) + 1; + +		return memcpy(malloc(len), msg, len); +	} +	if (rep->type != XS_ERROR) +		return NULL; +	res = malloc(rep->len + 1); +	memcpy(res, rep + 1, rep->len); +	res[rep->len] = 0; +	free(rep); +	return res; +} + +/* List the contents of a directory.  Returns a malloc()ed array of + * pointers to malloc()ed strings.  The array is NULL terminated.  May + * block. + */ +char *xenbus_ls(xenbus_transaction_t xbt, const char *pre, char ***contents) +{ +	struct xsd_sockmsg *reply, *repmsg; +	struct write_req req[] = { { pre, strlen(pre) + 1 } }; +	int nr_elems, x, i; +	char **res, *msg; + +	repmsg = xenbus_msg_reply(XS_DIRECTORY, xbt, req, ARRAY_SIZE(req)); +	msg = errmsg(repmsg); +	if (msg) { +		*contents = NULL; +		return msg; +	} +	reply = repmsg + 1; +	for (x = nr_elems = 0; x < repmsg->len; x++) +		nr_elems += (((char *)reply)[x] == 0); +	res = malloc(sizeof(res[0]) * (nr_elems + 1)); +	for (x = i = 0; i < nr_elems; i++) { +		int l = strlen((char *)reply + x); + +		res[i] = malloc(l + 1); +		memcpy(res[i], (char *)reply + x, l + 1); +		x += l + 1; +	} +	res[i] = NULL; +	free(repmsg); +	*contents = res; +	return NULL; +} + +char *xenbus_read(xenbus_transaction_t xbt, const char *path, char **value) +{ +	struct write_req req[] = { {path, strlen(path) + 1} }; +	struct xsd_sockmsg *rep; +	char *res, *msg; + +	rep = xenbus_msg_reply(XS_READ, xbt, req, ARRAY_SIZE(req)); +	msg = errmsg(rep); +	if (msg) { +		*value = NULL; +		return msg; +	} +	res = malloc(rep->len + 1); +	memcpy(res, rep + 1, rep->len); +	res[rep->len] = 0; +	free(rep); +	*value = res; +	return NULL; +} + +char *xenbus_write(xenbus_transaction_t xbt, const char *path, +				   const char *value) +{ +	struct write_req req[] = { +		{path, strlen(path) + 1}, +		{value, strlen(value)}, +	}; +	struct xsd_sockmsg *rep; +	char *msg; + +	rep = xenbus_msg_reply(XS_WRITE, xbt, req, ARRAY_SIZE(req)); +	msg = errmsg(rep); +	if (msg) +		return msg; +	free(rep); +	return NULL; +} + +char *xenbus_rm(xenbus_transaction_t xbt, const char *path) +{ +	struct write_req req[] = { {path, strlen(path) + 1} }; +	struct xsd_sockmsg *rep; +	char *msg; + +	rep = xenbus_msg_reply(XS_RM, xbt, req, ARRAY_SIZE(req)); +	msg = errmsg(rep); +	if (msg) +		return msg; +	free(rep); +	return NULL; +} + +char *xenbus_get_perms(xenbus_transaction_t xbt, const char *path, char **value) +{ +	struct write_req req[] = { {path, strlen(path) + 1} }; +	struct xsd_sockmsg *rep; +	char *res, *msg; + +	rep = xenbus_msg_reply(XS_GET_PERMS, xbt, req, ARRAY_SIZE(req)); +	msg = errmsg(rep); +	if (msg) { +		*value = NULL; +		return msg; +	} +	res = malloc(rep->len + 1); +	memcpy(res, rep + 1, rep->len); +	res[rep->len] = 0; +	free(rep); +	*value = res; +	return NULL; +} + +#define PERM_MAX_SIZE 32 +char *xenbus_set_perms(xenbus_transaction_t xbt, const char *path, +		       domid_t dom, char perm) +{ +	char value[PERM_MAX_SIZE]; +	struct write_req req[] = { +		{path, strlen(path) + 1}, +		{value, 0}, +	}; +	struct xsd_sockmsg *rep; +	char *msg; + +	snprintf(value, PERM_MAX_SIZE, "%c%hu", perm, dom); +	req[1].len = strlen(value) + 1; +	rep = xenbus_msg_reply(XS_SET_PERMS, xbt, req, ARRAY_SIZE(req)); +	msg = errmsg(rep); +	if (msg) +		return msg; +	free(rep); +	return NULL; +} + +char *xenbus_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 write_req req = { "", 1}; +	struct xsd_sockmsg *rep; +	char *err; + +	rep = xenbus_msg_reply(XS_TRANSACTION_START, 0, &req, 1); +	err = errmsg(rep); +	if (err) +		return err; +	sscanf((char *)(rep + 1), "%lu", xbt); +	free(rep); +	return NULL; +} + +char *xenbus_transaction_end(xenbus_transaction_t t, int abort, int *retry) +{ +	struct xsd_sockmsg *rep; +	struct write_req req; +	char *err; + +	*retry = 0; + +	req.data = abort ? "F" : "T"; +	req.len = 2; +	rep = xenbus_msg_reply(XS_TRANSACTION_END, t, &req, 1); +	err = errmsg(rep); +	if (err) { +		if (!strcmp(err, "EAGAIN")) { +			*retry = 1; +			free(err); +			return NULL; +		} else { +			return err; +		} +	} +	free(rep); +	return NULL; +} + +int xenbus_read_integer(const char *path) +{ +	char *res, *buf; +	int t; + +	res = xenbus_read(XBT_NIL, path, &buf); +	if (res) { +		printk("Failed to read %s.\n", path); +		free(res); +		return -1; +	} +	sscanf(buf, "%d", &t); +	free(buf); +	return t; +} + +int xenbus_read_uuid(const char *path, unsigned char uuid[16]) +{ +	char *res, *buf; + +	res = xenbus_read(XBT_NIL, path, &buf); +	if (res) { +		printk("Failed to read %s.\n", path); +		free(res); +		return 0; +	} +	if (strlen(buf) != ((2 * 16) + 4) /* 16 hex bytes and 4 hyphens */ +	    || sscanf(buf, +		      "%2hhx%2hhx%2hhx%2hhx-" +		      "%2hhx%2hhx-" +		      "%2hhx%2hhx-" +		      "%2hhx%2hhx-" +		      "%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx", +		      uuid, uuid + 1, uuid + 2, uuid + 3, +		      uuid + 4, uuid + 5, uuid + 6, uuid + 7, +		      uuid + 8, uuid + 9, uuid + 10, uuid + 11, +		      uuid + 12, uuid + 13, uuid + 14, uuid + 15) != 16) { +		printk("Xenbus path %s value %s is not a uuid!\n", path, buf); +		free(buf); +		return 0; +	} +	free(buf); +	return 1; +} + +char *xenbus_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; + +	BUG_ON(strlen(node) + strlen(path) + 1 >= BUFFER_SIZE); +	sprintf(fullpath, "%s/%s", node, path); +	va_start(args, fmt); +	vsprintf(val, fmt, args); +	va_end(args); +	return xenbus_write(xbt, fullpath, val); +} + +domid_t xenbus_get_self_id(void) +{ +	char *dom_id; +	domid_t ret; + +	BUG_ON(xenbus_read(XBT_NIL, "domid", &dom_id)); +	sscanf(dom_id, "%"SCNd16, &ret); + +	return ret; +} + +void init_xenbus(void) +{ +	u64 v; + +	debug("%s\n", __func__); +	if (hvm_get_parameter(HVM_PARAM_STORE_EVTCHN, &v)) +		BUG(); +	xenbus_evtchn = v; + +	if (hvm_get_parameter(HVM_PARAM_STORE_PFN, &v)) +		BUG(); +	xenstore_buf = (struct xenstore_domain_interface *)map_frame_virt(v); +} + +void fini_xenbus(void) +{ +	debug("%s\n", __func__); +} | 
