# HG changeset patch
# User cegger
# Date 1351254310 -7200
NetBSD gnttab support

diff -r 0f9a695ef781 -r 7a5eb2d4ffeb tools/include/xen-sys/NetBSD/gntdev.h
--- /dev/null
+++ b/tools/include/xen-sys/NetBSD/gntdev.h
@@ -0,0 +1,136 @@
+/******************************************************************************
+ * gntdev.h
+ * 
+ * Interface to /dev/xen/gntdev.
+ * 
+ * Copyright (c) 2007, D G Murray
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation; or, when distributed
+ * separately from the Linux kernel or incorporated into other
+ * software packages, subject to the following license:
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef __NetBSD_PUBLIC_GNTDEV_H__
+#define __NetBSD_PUBLIC_GNTDEV_H__
+
+struct ioctl_gntdev_grant_ref {
+	/* The domain ID of the grant to be mapped. */
+	uint32_t domid;
+	/* The grant reference of the grant to be mapped. */
+	uint32_t ref;
+};
+
+/*
+ * Inserts the grant references into the mapping table of an instance
+ * of gntdev. N.B. This does not perform the mapping, which is deferred
+ * until mmap() is called with @index as the offset.
+ */
+#define IOCTL_GNTDEV_MAP_GRANT_REF				\
+	_IOWR('G', 0, sizeof(struct ioctl_gntdev_map_grant_ref))
+struct ioctl_gntdev_map_grant_ref {
+	/* IN parameters */
+	/* The number of grants to be mapped. */
+	uint32_t count;
+	uint32_t pad;
+	/* OUT parameters */
+	/* The offset to be used on a subsequent call to mmap(). */
+	uint64_t index;
+	/* Variable IN parameter. */
+	/* Array of grant references, of size @count. */
+	struct ioctl_gntdev_grant_ref refs[1];
+};
+
+/*
+ * Removes the grant references from the mapping table of an instance of
+ * of gntdev. N.B. munmap() must be called on the relevant virtual address(es)
+ * before this ioctl is called, or an error will result.
+ */
+#define IOCTL_GNTDEV_UNMAP_GRANT_REF				\
+	_IOW('G', 1, sizeof(struct ioctl_gntdev_unmap_grant_ref))       
+struct ioctl_gntdev_unmap_grant_ref {
+	/* IN parameters */
+	/* The offset was returned by the corresponding map operation. */
+	uint64_t index;
+	/* The number of pages to be unmapped. */
+	uint32_t count;
+	uint32_t pad;
+};
+
+/*
+ * Returns the offset in the driver's address space that corresponds
+ * to @vaddr. This can be used to perform a munmap(), followed by an
+ * UNMAP_GRANT_REF ioctl, where no state about the offset is retained by
+ * the caller. The number of pages that were allocated at the same time as
+ * @vaddr is returned in @count.
+ *
+ * N.B. Where more than one page has been mapped into a contiguous range, the
+ *      supplied @vaddr must correspond to the start of the range; otherwise
+ *      an error will result. It is only possible to munmap() the entire
+ *      contiguously-allocated range at once, and not any subrange thereof.
+ */
+#define IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR			\
+	_IOWR('G', 2, sizeof(struct ioctl_gntdev_get_offset_for_vaddr))
+struct ioctl_gntdev_get_offset_for_vaddr {
+	/* IN parameters */
+	/* The virtual address of the first mapped page in a range. */
+	uint64_t vaddr;
+	/* OUT parameters */
+	/* The offset that was used in the initial mmap() operation. */
+	uint64_t offset;
+	/* The number of pages mapped in the VM area that begins at @vaddr. */
+	uint32_t count;
+	uint32_t pad;
+};
+
+/*
+ * Sets up an unmap notification within the page, so that the other side
+ * can do cleanup if this side crashes. Required to implement cross-domain
+ * robust mutexes or close notification on communication channels.
+ *
+ * Each mapped page only supports one notification; multiple calls referring
+ * to the same page overwrite the previous notification. You must clear the
+ * notification prior to the IOCTL_GNTALLOC_DEALLOC_GREF if you do not want it
+ * to occur.
+ */
+#define IOCTL_GNTDEV_SET_UNMAP_NOTIFY \
+	_IOR('G', 3, sizeof(struct ioctl_gntdev_unmap_notify))
+struct ioctl_gntdev_unmap_notify {
+	/* IN parameters */
+	/* Offset in the file descriptor for a byte within the page. This
+	 * offset is the result of the IOCTL_GNTDEV_MAP_GRANT_REF and is the
+	 * same as is used with mmap(). If using UNMAP_NOTIFY_CLEAR_BYTE, this
+	 * is the byte within the page to be cleared.
+	 */
+	uint64_t index;
+	/* Action(s) to take on unmap */
+	uint32_t action;
+	/* Event channel to notify */
+	uint32_t event_channel_port;
+};
+
+/* Clear (set to zero) the byte specified by index */
+#define UNMAP_NOTIFY_CLEAR_BYTE 0x1
+/* Send an interrupt on the indicated event channel */
+#define UNMAP_NOTIFY_SEND_EVENT 0x2
+
+#endif /* __NetBSD_PUBLIC_GNTDEV_H__ */
diff -r 0f9a695ef781 -r 7a5eb2d4ffeb tools/libxc/xc_netbsd.c
--- a/tools/libxc/xc_netbsd.c
+++ b/tools/libxc/xc_netbsd.c
@@ -21,6 +21,7 @@
 #include "xc_private.h"
 
 #include <xen/sys/evtchn.h>
+#include <xen/sys/gntdev.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <malloc.h>
@@ -390,7 +391,174 @@ void *xc_memalign(xc_interface *xch, siz
     return valloc(size);
 }
 
-static struct xc_osdep_ops *netbsd_osdep_init(xc_interface *xch, enum xc_osdep_type type)
+#define DEVXEN  "/dev/xen/"
+
+static xc_osdep_handle
+netbsd_gnttab_open(xc_gnttab *xcg)
+{
+    int fd;
+
+    fd = open(DEVXEN "gntdev", O_RDWR);
+    if (fd == -1)
+        return XC_OSDEP_OPEN_ERROR;
+
+    return (xc_osdep_handle)fd;
+}
+
+static int
+netbsd_gnttab_close(xc_gnttab *xcg, xc_osdep_handle h)
+{
+    int fd = (int)h;
+    return close(fd);
+}
+
+static void *
+netbsd_gnttab_grant_map(xc_gnttab *xch, xc_osdep_handle h,
+    uint32_t count, int flags, int prot,
+    uint32_t *domids, uint32_t *refs,
+    uint32_t notify_offset,
+    evtchn_port_t notify_port)
+{
+	int fd = (int)h;
+	struct ioctl_gntdev_map_grant_ref *map;
+	void *addr = NULL;
+	int domids_stride = 1;
+	int i;
+
+	if (flags & XC_GRANT_MAP_SINGLE_DOMAIN)
+		domids_stride = 0;
+
+	map = malloc(sizeof(*map) +
+		 (count - 1) * sizeof(struct ioctl_gntdev_map_grant_ref));
+	if ( map == NULL )
+		return NULL;
+
+	for ( i = 0; i < count; i++ )
+	{
+		map->refs[i].domid = domids[i * domids_stride];
+		map->refs[i].ref = refs[i];
+	}
+
+	map->count = count;
+
+	if ( ioctl(fd, IOCTL_GNTDEV_MAP_GRANT_REF, map) ) {
+		PERROR("netbsd_gnttab_map_grant_refs: ioctl MAP_GRANT_REF failed");
+		goto out;
+	}
+
+ retry:
+	addr = mmap(NULL, XC_PAGE_SIZE * count, prot, MAP_SHARED, fd,
+		map->index);
+	if (addr == MAP_FAILED && errno == EAGAIN) {
+		/* 
+		 * The grant hypercall can return EAGAIN if the granted page
+		 * is swapped out. Since the paging daemon may be in the same
+		 * domain, the hypercall cannot block without causing a
+		 * deadlock.
+		 *
+		 * Because there are no notifications when the page is swapped
+		 * in, wait a bit before retrying, and hope that the page will
+		 * arrive eventually.
+		 */
+		usleep(1000);
+		goto retry;
+	}
+
+	if (addr != MAP_FAILED)
+	{
+		int rv = 0;
+		struct ioctl_gntdev_unmap_notify notify;
+
+		notify.index = map->index;
+		notify.action = 0;
+		if (notify_offset >= 0 && notify_offset < XC_PAGE_SIZE * count) {
+			notify.index += notify_offset;
+			notify.action |= UNMAP_NOTIFY_CLEAR_BYTE;
+		}
+		if (notify.action)
+			rv = ioctl(fd, IOCTL_GNTDEV_SET_UNMAP_NOTIFY, &notify);
+		if (rv) {
+			PERROR("netbsd_gnttab_grant_map: ioctl SET_UNMAP_NOTIFY failed");
+			munmap(addr, count * XC_PAGE_SIZE);
+			addr = MAP_FAILED;
+		}
+	}
+
+	if (addr == MAP_FAILED)
+	{
+		int saved_errno = errno;
+		struct ioctl_gntdev_unmap_grant_ref unmap_grant;
+
+		/* Unmap the driver slots used to store the grant information. */
+		PERROR("xc_gnttab_map_grant_refs: mmap failed");
+		unmap_grant.index = map->index;
+		unmap_grant.count = count;
+		ioctl(fd, IOCTL_GNTDEV_UNMAP_GRANT_REF, &unmap_grant);
+		errno = saved_errno;
+		addr = NULL;
+	}
+
+ out:
+	free(map);
+
+	return addr;
+}
+
+static int
+netbsd_gnttab_munmap(xc_gnttab *xcg, xc_osdep_handle h,
+    void *start_address, uint32_t count)
+{
+	int fd = (int)h;
+	struct ioctl_gntdev_get_offset_for_vaddr get_offset;
+	struct ioctl_gntdev_unmap_grant_ref unmap_grant;
+	int rc;
+
+	if ( start_address == NULL )
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	/* First, it is necessary to get the offset which was initially used to
+	 * mmap() the pages.
+	 */
+	get_offset.vaddr = (unsigned long)start_address;
+	rc = ioctl(fd, IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR, &get_offset);
+	if ( rc )
+		return rc;
+
+	if ( get_offset.count != count )
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	/* Next, unmap the memory. */
+	rc = munmap(start_address, count * getpagesize());
+	if ( rc )
+		return rc;
+
+	/* Finally, unmap the driver slots used to store the grant information. */
+	unmap_grant.index = get_offset.offset;
+	unmap_grant.count = count;
+	rc = ioctl(fd, IOCTL_GNTDEV_UNMAP_GRANT_REF, &unmap_grant);
+	if ( rc )
+		return rc;
+	return 0;
+}
+
+static struct xc_osdep_ops netbsd_gnttab_ops = {
+    .open = &netbsd_gnttab_open,
+    .close = &netbsd_gnttab_close,
+
+    .u.gnttab = {
+        .grant_map = &netbsd_gnttab_grant_map,
+        .munmap = &netbsd_gnttab_munmap,
+    },
+};
+
+static struct xc_osdep_ops *
+netbsd_osdep_init(xc_interface *xch, enum xc_osdep_type type)
 {
     switch ( type )
     {
@@ -398,6 +566,8 @@ static struct xc_osdep_ops *netbsd_osdep
         return &netbsd_privcmd_ops;
     case XC_OSDEP_EVTCHN:
         return &netbsd_evtchn_ops;
+    case XC_OSDEP_GNTTAB:
+        return &netbsd_gnttab_ops;
     default:
         return NULL;
     }