aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tools/libxc/Makefile1
-rw-r--r--tools/libxc/xc_hcall_buf.c162
-rw-r--r--tools/libxc/xc_private.c2
-rw-r--r--tools/libxc/xc_private.h58
-rw-r--r--tools/libxc/xenctrl.h131
5 files changed, 352 insertions, 2 deletions
diff --git a/tools/libxc/Makefile b/tools/libxc/Makefile
index 9f6d70f3fb..1498a4fa7d 100644
--- a/tools/libxc/Makefile
+++ b/tools/libxc/Makefile
@@ -27,6 +27,7 @@ CTRL_SRCS-y += xc_tmem.c
CTRL_SRCS-y += xc_mem_event.c
CTRL_SRCS-y += xc_mem_paging.c
CTRL_SRCS-y += xc_memshr.c
+CTRL_SRCS-y += xc_hcall_buf.c
CTRL_SRCS-y += xtl_core.c
CTRL_SRCS-y += xtl_logger_stdio.c
CTRL_SRCS-$(CONFIG_X86) += xc_pagetab.c
diff --git a/tools/libxc/xc_hcall_buf.c b/tools/libxc/xc_hcall_buf.c
new file mode 100644
index 0000000000..032997f125
--- /dev/null
+++ b/tools/libxc/xc_hcall_buf.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2010, Citrix Systems, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <malloc.h>
+
+#include "xc_private.h"
+#include "xg_private.h"
+
+xc_hypercall_buffer_t XC__HYPERCALL_BUFFER_NAME(HYPERCALL_BUFFER_NULL) = {
+ .hbuf = NULL,
+ .param_shadow = NULL,
+ HYPERCALL_BUFFER_INIT_NO_BOUNCE
+};
+
+void *xc__hypercall_buffer_alloc_pages(xc_interface *xch, xc_hypercall_buffer_t *b, int nr_pages)
+{
+ size_t size = nr_pages * PAGE_SIZE;
+ void *p;
+#if defined(_POSIX_C_SOURCE) && !defined(__sun__)
+ int ret;
+ ret = posix_memalign(&p, PAGE_SIZE, size);
+ if (ret != 0)
+ return NULL;
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+ p = valloc(size);
+#else
+ p = memalign(PAGE_SIZE, size);
+#endif
+
+ if (!p)
+ return NULL;
+
+#ifndef __sun__
+ if ( mlock(p, size) < 0 )
+ {
+ free(p);
+ return NULL;
+ }
+#endif
+
+ b->hbuf = p;
+
+ memset(p, 0, size);
+ return b->hbuf;
+}
+
+void xc__hypercall_buffer_free_pages(xc_interface *xch, xc_hypercall_buffer_t *b, int nr_pages)
+{
+ if ( b->hbuf == NULL )
+ return;
+
+#ifndef __sun__
+ (void) munlock(b->hbuf, nr_pages * PAGE_SIZE);
+#endif
+
+ free(b->hbuf);
+}
+
+struct allocation_header {
+ int nr_pages;
+};
+
+void *xc__hypercall_buffer_alloc(xc_interface *xch, xc_hypercall_buffer_t *b, size_t size)
+{
+ size_t actual_size = ROUNDUP(size + sizeof(struct allocation_header), PAGE_SHIFT);
+ int nr_pages = actual_size >> PAGE_SHIFT;
+ struct allocation_header *hdr;
+
+ hdr = xc__hypercall_buffer_alloc_pages(xch, b, nr_pages);
+ if ( hdr == NULL )
+ return NULL;
+
+ b->hbuf = (void *)(hdr+1);
+
+ hdr->nr_pages = nr_pages;
+ return b->hbuf;
+}
+
+void xc__hypercall_buffer_free(xc_interface *xch, xc_hypercall_buffer_t *b)
+{
+ struct allocation_header *hdr;
+
+ if (b->hbuf == NULL)
+ return;
+
+ hdr = b->hbuf;
+ b->hbuf = --hdr;
+
+ xc__hypercall_buffer_free_pages(xch, b, hdr->nr_pages);
+}
+
+int xc__hypercall_bounce_pre(xc_interface *xch, xc_hypercall_buffer_t *b)
+{
+ void *p;
+
+ /*
+ * Catch hypercall buffer declared other than with DECLARE_HYPERCALL_BOUNCE.
+ */
+ if ( b->ubuf == (void *)-1 || b->dir == XC_HYPERCALL_BUFFER_BOUNCE_NONE )
+ abort();
+
+ /*
+ * Do need to bounce a NULL buffer.
+ */
+ if ( b->ubuf == NULL )
+ {
+ b->hbuf = NULL;
+ return 0;
+ }
+
+ p = xc__hypercall_buffer_alloc(xch, b, b->sz);
+ if ( p == NULL )
+ return -1;
+
+ if ( b->dir == XC_HYPERCALL_BUFFER_BOUNCE_IN || b->dir == XC_HYPERCALL_BUFFER_BOUNCE_BOTH )
+ memcpy(b->hbuf, b->ubuf, b->sz);
+
+ return 0;
+}
+
+void xc__hypercall_bounce_post(xc_interface *xch, xc_hypercall_buffer_t *b)
+{
+ /*
+ * Catch hypercall buffer declared other than with DECLARE_HYPERCALL_BOUNCE.
+ */
+ if ( b->ubuf == (void *)-1 || b->dir == XC_HYPERCALL_BUFFER_BOUNCE_NONE )
+ abort();
+
+ if ( b->hbuf == NULL )
+ return;
+
+ if ( b->dir == XC_HYPERCALL_BUFFER_BOUNCE_OUT || b->dir == XC_HYPERCALL_BUFFER_BOUNCE_BOTH )
+ memcpy(b->ubuf, b->hbuf, b->sz);
+
+ xc__hypercall_buffer_free(xch, b);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libxc/xc_private.c b/tools/libxc/xc_private.c
index 1c09908ee7..5f54d62b45 100644
--- a/tools/libxc/xc_private.c
+++ b/tools/libxc/xc_private.c
@@ -18,13 +18,11 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <inttypes.h>
#include "xc_private.h"
#include "xg_private.h"
#include "xc_dom.h"
#include <stdarg.h>
#include <stdlib.h>
-#include <malloc.h>
#include <unistd.h>
#include <pthread.h>
#include <assert.h>
diff --git a/tools/libxc/xc_private.h b/tools/libxc/xc_private.h
index e08936f633..41e22f8c8a 100644
--- a/tools/libxc/xc_private.h
+++ b/tools/libxc/xc_private.h
@@ -106,6 +106,64 @@ void unlock_pages(xc_interface *xch, void *addr, size_t len);
int hcall_buf_prep(xc_interface *xch, void **addr, size_t len);
void hcall_buf_release(xc_interface *xch, void **addr, size_t len);
+/*
+ * HYPERCALL ARGUMENT BUFFERS
+ *
+ * Augment the public hypercall buffer interface with the ability to
+ * bounce between user provided buffers and hypercall safe memory.
+ *
+ * Use xc_hypercall_bounce_pre/post instead of
+ * xc_hypercall_buffer_alloc/free(_pages). The specified user
+ * supplied buffer is automatically copied in/out of the hypercall
+ * safe memory.
+ */
+enum {
+ XC_HYPERCALL_BUFFER_BOUNCE_NONE = 0,
+ XC_HYPERCALL_BUFFER_BOUNCE_IN = 1,
+ XC_HYPERCALL_BUFFER_BOUNCE_OUT = 2,
+ XC_HYPERCALL_BUFFER_BOUNCE_BOTH = 3
+};
+
+/*
+ * Declare a named bounce buffer.
+ *
+ * Normally you should use DECLARE_HYPERCALL_BOUNCE (see below).
+ *
+ * This declaration should only be used when the user pointer is
+ * non-trivial, e.g. when it is contained within an existing data
+ * structure.
+ */
+#define DECLARE_NAMED_HYPERCALL_BOUNCE(_name, _ubuf, _sz, _dir) \
+ xc_hypercall_buffer_t XC__HYPERCALL_BUFFER_NAME(_name) = { \
+ .hbuf = NULL, \
+ .param_shadow = NULL, \
+ .sz = _sz, .dir = _dir, .ubuf = _ubuf, \
+ }
+
+/*
+ * Declare a bounce buffer shadowing the named user data pointer.
+ */
+#define DECLARE_HYPERCALL_BOUNCE(_ubuf, _sz, _dir) DECLARE_NAMED_HYPERCALL_BOUNCE(_ubuf, _ubuf, _sz, _dir)
+
+/*
+ * Set the size of data to bounce. Useful when the size is not known
+ * when the bounce buffer is declared.
+ */
+#define HYPERCALL_BOUNCE_SET_SIZE(_buf, _sz) do { (HYPERCALL_BUFFER(_buf))->sz = _sz; } while (0)
+
+/*
+ * Initialise and free hypercall safe memory. Takes care of any required
+ * copying.
+ */
+int xc__hypercall_bounce_pre(xc_interface *xch, xc_hypercall_buffer_t *bounce);
+#define xc_hypercall_bounce_pre(_xch, _name) xc__hypercall_bounce_pre(_xch, HYPERCALL_BUFFER(_name))
+void xc__hypercall_bounce_post(xc_interface *xch, xc_hypercall_buffer_t *bounce);
+#define xc_hypercall_bounce_post(_xch, _name) xc__hypercall_bounce_post(_xch, HYPERCALL_BUFFER(_name))
+
+/*
+ * Hypercall interfaces.
+ */
+
int do_xen_hypercall(xc_interface *xch, privcmd_hypercall_t *hypercall);
static inline int do_xen_version(xc_interface *xch, int cmd, void *dest)
diff --git a/tools/libxc/xenctrl.h b/tools/libxc/xenctrl.h
index 49224a8dc2..f69826302b 100644
--- a/tools/libxc/xenctrl.h
+++ b/tools/libxc/xenctrl.h
@@ -149,6 +149,137 @@ enum xc_open_flags {
int xc_interface_close(xc_interface *xch);
/*
+ * HYPERCALL SAFE MEMORY BUFFER
+ *
+ * Ensure that memory which is passed to a hypercall has been
+ * specially allocated in order to be safe to access from the
+ * hypervisor.
+ *
+ * Each user data pointer is shadowed by an xc_hypercall_buffer data
+ * structure. You should never define an xc_hypercall_buffer type
+ * directly, instead use the DECLARE_HYPERCALL_BUFFER* macros below.
+ *
+ * The strucuture should be considered opaque and all access should be
+ * via the macros and helper functions defined below.
+ *
+ * Once the buffer is declared the user is responsible for explicitly
+ * allocating and releasing the memory using
+ * xc_hypercall_buffer_alloc(_pages) and
+ * xc_hypercall_buffer_free(_pages).
+ *
+ * Once the buffer has been allocated the user can initialise the data
+ * via the normal pointer. The xc_hypercall_buffer structure is
+ * transparently referenced by the helper macros (such as
+ * xen_set_guest_handle) in order to check at compile time that the
+ * correct type of memory is being used.
+ */
+struct xc_hypercall_buffer {
+ /* Hypercall safe memory buffer. */
+ void *hbuf;
+
+ /*
+ * Reference to xc_hypercall_buffer passed as argument to the
+ * current function.
+ */
+ struct xc_hypercall_buffer *param_shadow;
+
+ /*
+ * Direction of copy for bounce buffering.
+ */
+ int dir;
+
+ /* Used iff dir != 0. */
+ void *ubuf;
+ size_t sz;
+};
+typedef struct xc_hypercall_buffer xc_hypercall_buffer_t;
+
+/*
+ * Construct the name of the hypercall buffer for a given variable.
+ * For internal use only
+ */
+#define XC__HYPERCALL_BUFFER_NAME(_name) xc__hypercall_buffer_##_name
+
+/*
+ * Returns the hypercall_buffer associated with a variable.
+ */
+#define HYPERCALL_BUFFER(_name) \
+ ({ xc_hypercall_buffer_t _val1; \
+ typeof(XC__HYPERCALL_BUFFER_NAME(_name)) *_val2 = &XC__HYPERCALL_BUFFER_NAME(_name); \
+ (void)(&_val1 == _val2); \
+ (_val2)->param_shadow ? (_val2)->param_shadow : (_val2); \
+ })
+
+#define HYPERCALL_BUFFER_INIT_NO_BOUNCE .dir = 0, .sz = 0, .ubuf = (void *)-1
+
+/*
+ * Defines a hypercall buffer and user pointer with _name of _type.
+ *
+ * The user accesses the data as normal via _name which will be
+ * transparently converted to the hypercall buffer as necessary.
+ */
+#define DECLARE_HYPERCALL_BUFFER(_type, _name) \
+ _type *_name = NULL; \
+ xc_hypercall_buffer_t XC__HYPERCALL_BUFFER_NAME(_name) = { \
+ .hbuf = NULL, \
+ .param_shadow = NULL, \
+ HYPERCALL_BUFFER_INIT_NO_BOUNCE \
+ }
+
+/*
+ * Declare the necessary data structure to allow a hypercall buffer
+ * passed as an argument to a function to be used in the normal way.
+ */
+#define DECLARE_HYPERCALL_BUFFER_ARGUMENT(_name) \
+ xc_hypercall_buffer_t XC__HYPERCALL_BUFFER_NAME(_name) = { \
+ .hbuf = (void *)-1, \
+ .param_shadow = _name, \
+ HYPERCALL_BUFFER_INIT_NO_BOUNCE \
+ }
+
+/*
+ * Get the hypercall buffer data pointer in a form suitable for use
+ * directly as a hypercall argument.
+ */
+#define HYPERCALL_BUFFER_AS_ARG(_name) \
+ ({ xc_hypercall_buffer_t _val1; \
+ typeof(XC__HYPERCALL_BUFFER_NAME(_name)) *_val2 = HYPERCALL_BUFFER(_name); \
+ (void)(&_val1 == _val2); \
+ (unsigned long)(_val2)->hbuf; \
+ })
+
+/*
+ * Set a xen_guest_handle in a type safe manner, ensuring that the
+ * data pointer has been correctly allocated.
+ */
+#define xc_set_xen_guest_handle(_hnd, _val) \
+ do { \
+ xc_hypercall_buffer_t _val1; \
+ typeof(XC__HYPERCALL_BUFFER_NAME(_val)) *_val2 = HYPERCALL_BUFFER(_val); \
+ (void) (&_val1 == _val2); \
+ set_xen_guest_handle_raw(_hnd, (_val2)->hbuf); \
+ } while (0)
+
+/* Use with xc_set_xen_guest_handle in place of NULL */
+extern xc_hypercall_buffer_t XC__HYPERCALL_BUFFER_NAME(HYPERCALL_BUFFER_NULL);
+
+/*
+ * Allocate and free hypercall buffers with byte granularity.
+ */
+void *xc__hypercall_buffer_alloc(xc_interface *xch, xc_hypercall_buffer_t *b, size_t size);
+#define xc_hypercall_buffer_alloc(_xch, _name, _size) xc__hypercall_buffer_alloc(_xch, HYPERCALL_BUFFER(_name), _size)
+void xc__hypercall_buffer_free(xc_interface *xch, xc_hypercall_buffer_t *b);
+#define xc_hypercall_buffer_free(_xch, _name) xc__hypercall_buffer_free(_xch, HYPERCALL_BUFFER(_name))
+
+/*
+ * Allocate and free hypercall buffers with page alignment.
+ */
+void *xc__hypercall_buffer_alloc_pages(xc_interface *xch, xc_hypercall_buffer_t *b, int nr_pages);
+#define xc_hypercall_buffer_alloc_pages(_xch, _name, _nr) xc__hypercall_buffer_alloc_pages(_xch, HYPERCALL_BUFFER(_name), _nr)
+void xc__hypercall_buffer_free_pages(xc_interface *xch, xc_hypercall_buffer_t *b, int nr_pages);
+#define xc_hypercall_buffer_free_pages(_xch, _name, _nr) xc__hypercall_buffer_free_pages(_xch, HYPERCALL_BUFFER(_name), _nr)
+
+/*
* DOMAIN DEBUGGING FUNCTIONS
*/