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