// 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 #include #include #include #include #include #include #include #include #include #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__); }