$NetBSD$

--- libxc/xc_netbsd.c.orig	2011-03-29 17:09:58.000000000 +0000
+++ 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>
 
@@ -351,7 +352,189 @@ void discard_file_cache(xc_interface *xc
     errno = saved_errno;
 }
 
-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_map_grant_ref(xc_gnttab *xch, xc_osdep_handle h,
+    uint32_t domid, uint32_t ref, int prot)
+{
+	int fd = (int)h;
+	struct ioctl_gntdev_map_grant_ref map;
+	void *addr;
+
+	map.count = 1;
+	map.refs[0].domid = domid;
+	map.refs[0].ref = ref;
+
+	if ( ioctl(fd, IOCTL_GNTDEV_MAP_GRANT_REF, &map) ) {
+		PERROR("netbsd_gnttab_map_grant_ref: ioctl MAP_GRANT_REF failed");
+		return NULL;
+	}
+
+mmap_again:
+	addr = mmap(NULL, XC_PAGE_SIZE, prot, MAP_SHARED, fd, map.index);
+	if ( addr == MAP_FAILED )
+	{
+		int saved_errno = errno;
+		struct ioctl_gntdev_unmap_grant_ref unmap_grant;
+
+		if ( saved_errno == EAGAIN )
+		{
+			usleep(1000);
+			goto mmap_again;
+		}
+		/* Unmap the driver slots used to store the grant information. */
+		PERROR("netbsd_gnttab_map_grant_ref: mmap failed");
+		unmap_grant.index = map.index;
+		unmap_grant.count = 1;
+		ioctl(fd, IOCTL_GNTDEV_UNMAP_GRANT_REF, &unmap_grant);
+		errno = saved_errno;
+		return NULL;
+	}
+
+	return addr;
+}
+
+static void *
+do_gnttab_map_grant_refs(xc_gnttab *xch, xc_osdep_handle h,
+    uint32_t count, uint32_t *domids, int domids_stride,
+    uint32_t *refs, int prot)
+{
+	int fd = (int)h;
+	struct ioctl_gntdev_map_grant_ref *map;
+	void *addr = NULL;
+	int i;
+
+	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("xc_gnttab_map_grant_refs: ioctl MAP_GRANT_REF failed");
+		goto out;
+	}
+
+	addr = mmap(NULL, XC_PAGE_SIZE * count, prot, MAP_SHARED, fd,
+		map->index);
+	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 void *
+netbsd_gnttab_map_grant_refs(xc_gnttab *xcg, xc_osdep_handle h,
+    uint32_t count, uint32_t *domids, uint32_t *refs, int prot)
+{
+	return do_gnttab_map_grant_refs(xcg, h, count, domids, 1, refs, prot);
+}
+
+static void *
+netbsd_gnttab_map_domain_grant_refs(xc_gnttab *xcg, xc_osdep_handle h,
+    uint32_t count, uint32_t domid, uint32_t *refs, int prot)
+{
+	return do_gnttab_map_grant_refs(xcg, h, count, &domid, 0, refs, prot);
+}
+
+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 = {
+        .map_grant_ref = &netbsd_gnttab_map_grant_ref,
+        .map_grant_refs = &netbsd_gnttab_map_grant_refs,
+        .map_domain_grant_refs = &netbsd_gnttab_map_domain_grant_refs,
+        .munmap = &netbsd_gnttab_munmap,
+    },
+};
+
+static struct xc_osdep_ops *
+netbsd_osdep_init(xc_interface *xch, enum xc_osdep_type type)
 {
     switch ( type )
     {
@@ -360,8 +543,7 @@ static struct xc_osdep_ops *netbsd_osdep
     case XC_OSDEP_EVTCHN:
         return &netbsd_evtchn_ops;
     case XC_OSDEP_GNTTAB:
-        ERROR("GNTTAB interface not supported on this platform");
-        return NULL;
+        return &netbsd_gnttab_ops;
     default:
         return NULL;
     }
