# HG changeset patch
# User Ian Campbell <ian.campbell@xxxxxxxxxx>
# Date 1287650254 -3600
# Node ID a40c36db2a03279fcb6a0525359d6a95de4e4800
# Parent 0b5d85ea10f8fff3f654c564c0e66900e83e8012
libxc: infrastructure for hypercall safe data buffers.
Signed-off-by: Ian Campbell <ian.campbell@xxxxxxxxxx>
diff -r 0b5d85ea10f8 -r a40c36db2a03 tools/libxc/Makefile
--- a/tools/libxc/Makefile Tue Oct 19 09:17:18 2010 +0100
+++ b/tools/libxc/Makefile Thu Oct 21 09:37:34 2010 +0100
@@ -27,6 +27,7 @@ CTRL_SRCS-y += xc_mem_event.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 -r 0b5d85ea10f8 -r a40c36db2a03 tools/libxc/xc_hcall_buf.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/libxc/xc_hcall_buf.c Thu Oct 21 09:37:34 2010 +0100
@@ -0,0 +1,160 @@
+/*
+ * 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 <inttypes.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 -r 0b5d85ea10f8 -r a40c36db2a03 tools/libxc/xc_private.h
--- a/tools/libxc/xc_private.h Tue Oct 19 09:17:18 2010 +0100
+++ b/tools/libxc/xc_private.h Thu Oct 21 09:37:34 2010 +0100
@@ -105,6 +105,64 @@ void unlock_pages(xc_interface *xch, voi
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);
diff -r 0b5d85ea10f8 -r a40c36db2a03 tools/libxc/xenctrl.h
--- a/tools/libxc/xenctrl.h Tue Oct 19 09:17:18 2010 +0100
+++ b/tools/libxc/xenctrl.h Thu Oct 21 09:37:34 2010 +0100
@@ -147,6 +147,137 @@ enum xc_open_flags {
* @return 0 on success, -1 otherwise.
*/
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
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|