diff options
-rw-r--r-- | arch/arm/Kconfig | 1 | ||||
-rw-r--r-- | board/xen/xenguest_arm64/xenguest_arm64.c | 16 | ||||
-rw-r--r-- | drivers/xen/Makefile | 1 | ||||
-rw-r--r-- | drivers/xen/hypervisor.c | 2 | ||||
-rw-r--r-- | drivers/xen/xenbus.c | 557 | ||||
-rw-r--r-- | include/xen.h | 2 | ||||
-rw-r--r-- | include/xen/xenbus.h | 116 |
7 files changed, 693 insertions, 2 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 0c1b75426a..8401851666 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1756,6 +1756,7 @@ config TARGET_XENGUEST_ARM64 select OF_CONTROL select LINUX_KERNEL_IMAGE_HEADER select XEN_SERIAL + select SSCANF endchoice config ARCH_SUPPORT_TFABOOT diff --git a/board/xen/xenguest_arm64/xenguest_arm64.c b/board/xen/xenguest_arm64/xenguest_arm64.c index e3903bb987..c87ef70769 100644 --- a/board/xen/xenguest_arm64/xenguest_arm64.c +++ b/board/xen/xenguest_arm64/xenguest_arm64.c @@ -66,7 +66,7 @@ static int setup_mem_map(void) /* * Add "magic" region which is used by Xen to provide some essentials - * for the guest: we need console. + * for the guest: we need console and xenstore. */ ret = hvm_get_parameter_maintain_dcache(HVM_PARAM_CONSOLE_PFN, &gfn); if (ret < 0) { @@ -82,6 +82,20 @@ static int setup_mem_map(void) PTE_BLOCK_INNER_SHARE); i++; + ret = hvm_get_parameter_maintain_dcache(HVM_PARAM_STORE_PFN, &gfn); + if (ret < 0) { + printf("%s: Can't get HVM_PARAM_STORE_PFN, ret %d\n", + __func__, ret); + return -EINVAL; + } + + xen_mem_map[i].virt = PFN_PHYS(gfn); + xen_mem_map[i].phys = PFN_PHYS(gfn); + xen_mem_map[i].size = PAGE_SIZE; + xen_mem_map[i].attrs = (PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_INNER_SHARE); + i++; + mem = get_next_memory_node(blob, -1); if (mem < 0) { printf("%s: Missing /memory node\n", __func__); diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index 0ad35edefb..9d0f604aaa 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile @@ -4,3 +4,4 @@ obj-y += hypervisor.o obj-y += events.o +obj-y += xenbus.o diff --git a/drivers/xen/hypervisor.c b/drivers/xen/hypervisor.c index 63fed6074f..1c7ae94bdf 100644 --- a/drivers/xen/hypervisor.c +++ b/drivers/xen/hypervisor.c @@ -21,6 +21,7 @@ #include <xen/hvm.h> #include <xen/events.h> +#include <xen/xenbus.h> #include <xen/interface/memory.h> #define active_evtchns(cpu, sh, idx) \ @@ -236,5 +237,6 @@ void xen_init(void) map_shared_info(NULL); init_events(); + init_xenbus(); } diff --git a/drivers/xen/xenbus.c b/drivers/xen/xenbus.c new file mode 100644 index 0000000000..177d144723 --- /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__); +} diff --git a/include/xen.h b/include/xen.h index 64ed3f0654..6fe30b83a2 100644 --- a/include/xen.h +++ b/include/xen.h @@ -8,7 +8,7 @@ /** * xen_init() - Xen initialization * - * Map Xen memory pages, initialize event handler. + * Map Xen memory pages, initialize event handler and xenbus. */ void xen_init(void); diff --git a/include/xen/xenbus.h b/include/xen/xenbus.h new file mode 100644 index 0000000000..3ed7fd5733 --- /dev/null +++ b/include/xen/xenbus.h @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef XENBUS_H__ +#define XENBUS_H__ + +#include <xen/interface/xen.h> +#include <xen/interface/io/xenbus.h> + +typedef unsigned long xenbus_transaction_t; +#define XBT_NIL ((xenbus_transaction_t)0) + +extern u32 xenbus_evtchn; + +/* Initialize the XenBus system. */ +void init_xenbus(void); +/* Finalize the XenBus system. */ +void fini_xenbus(void); + +/** + * xenbus_read() - Read the value associated with a path. + * + * Returns a malloc'd error string on failure and sets *value to NULL. + * On success, *value is set to a malloc'd copy of the value. + */ +char *xenbus_read(xenbus_transaction_t xbt, const char *path, char **value); + +char *xenbus_wait_for_state_change(const char *path, XenbusState *state); +char *xenbus_switch_state(xenbus_transaction_t xbt, const char *path, + XenbusState state); + +/** + * xenbus_write() - Associates a value with a path. + * + * Returns a malloc'd error string on failure. + */ +char *xenbus_write(xenbus_transaction_t xbt, const char *path, + const char *value); + +/** + * xenbus_rm() - Removes the value associated with a path. + * + * Returns a malloc'd error string on failure. + */ +char *xenbus_rm(xenbus_transaction_t xbt, const char *path); + +/** + * xenbus_ls() - List the contents of a directory. + * + * Returns a malloc'd error string on failure and sets *contents to NULL. + * On success, *contents is set to a malloc'd array of pointers to malloc'd + * strings. The array is NULL terminated. May block. + */ +char *xenbus_ls(xenbus_transaction_t xbt, const char *prefix, char ***contents); + +/** + * xenbus_get_perms() - Reads permissions associated with a path. + * + * Returns a malloc'd error string on failure and sets *value to NULL. + * On success, *value is set to a malloc'd copy of the value. + */ +char *xenbus_get_perms(xenbus_transaction_t xbt, const char *path, char **value); + +/** + * xenbus_set_perms() - Sets the permissions associated with a path. + * + * Returns a malloc'd error string on failure. + */ +char *xenbus_set_perms(xenbus_transaction_t xbt, const char *path, domid_t dom, + char perm); + +/** + * xenbus_transaction_start() - Start a xenbus transaction. + * + * Returns the transaction in xbt on success or a malloc'd error string + * otherwise. + */ +char *xenbus_transaction_start(xenbus_transaction_t *xbt); + +/** + * xenbus_transaction_end() - End a xenbus transaction. + * + * Returns a malloc'd error string if it fails. Abort says whether the + * transaction should be aborted. + * Returns 1 in *retry if the transaction should be retried. + */ +char *xenbus_transaction_end(xenbus_transaction_t xbt, int abort, + int *retry); + +/** + * xenbus_read_integer() - Read path and parse it as an integer. + * + * Returns -1 on error. + */ +int xenbus_read_integer(const char *path); + +/** + * xenbus_read_uuid() - Read path and parse it as 16 byte uuid. + * + * Returns 1 if read and parsing were successful, 0 if not + */ +int xenbus_read_uuid(const char *path, unsigned char uuid[16]); + +/** + * xenbus_printf() - Contraction of snprintf and xenbus_write(path/node). + */ +char *xenbus_printf(xenbus_transaction_t xbt, + const char *node, const char *path, + const char *fmt, ...) + __attribute__((__format__(printf, 4, 5))); + +/** + * xenbus_get_self_id() - Utility function to figure out our domain id + */ +domid_t xenbus_get_self_id(void); + +#endif /* XENBUS_H__ */ |