xref: /freebsd/sys/dev/xen/gntdev/gntdev.c (revision d48524e21f1a49752485418324538755571ed13f)
178eb3293SRoger Pau Monné /*-
278eb3293SRoger Pau Monné  * Copyright (c) 2016 Akshay Jaggi <jaggi@FreeBSD.org>
378eb3293SRoger Pau Monné  * All rights reserved.
478eb3293SRoger Pau Monné  *
578eb3293SRoger Pau Monné  * Redistribution and use in source and binary forms, with or without
678eb3293SRoger Pau Monné  * modification, are permitted provided that the following conditions
778eb3293SRoger Pau Monné  * are met:
878eb3293SRoger Pau Monné  * 1. Redistributions of source code must retain the above copyright
978eb3293SRoger Pau Monné  *    notice, this list of conditions and the following disclaimer.
1078eb3293SRoger Pau Monné  * 2. Redistributions in binary form must reproduce the above copyright
1178eb3293SRoger Pau Monné  *    notice, this list of conditions and the following disclaimer in the
1278eb3293SRoger Pau Monné  *    documentation and/or other materials provided with the distribution.
1378eb3293SRoger Pau Monné  *
1478eb3293SRoger Pau Monné  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1578eb3293SRoger Pau Monné  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1678eb3293SRoger Pau Monné  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1778eb3293SRoger Pau Monné  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1878eb3293SRoger Pau Monné  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1978eb3293SRoger Pau Monné  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2078eb3293SRoger Pau Monné  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2178eb3293SRoger Pau Monné  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2278eb3293SRoger Pau Monné  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2378eb3293SRoger Pau Monné  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2478eb3293SRoger Pau Monné  * SUCH DAMAGE.
2578eb3293SRoger Pau Monné  *
2678eb3293SRoger Pau Monné  * gntdev.c
2778eb3293SRoger Pau Monné  *
2878eb3293SRoger Pau Monné  * Interface to /dev/xen/gntdev.
2978eb3293SRoger Pau Monné  *
3078eb3293SRoger Pau Monné  */
3178eb3293SRoger Pau Monné 
3278eb3293SRoger Pau Monné #include <sys/param.h>
3378eb3293SRoger Pau Monné #include <sys/systm.h>
3478eb3293SRoger Pau Monné #include <sys/uio.h>
3578eb3293SRoger Pau Monné #include <sys/bus.h>
3678eb3293SRoger Pau Monné #include <sys/malloc.h>
3778eb3293SRoger Pau Monné #include <sys/kernel.h>
3878eb3293SRoger Pau Monné #include <sys/lock.h>
3978eb3293SRoger Pau Monné #include <sys/mutex.h>
4078eb3293SRoger Pau Monné #include <sys/rwlock.h>
4178eb3293SRoger Pau Monné #include <sys/selinfo.h>
4278eb3293SRoger Pau Monné #include <sys/poll.h>
4378eb3293SRoger Pau Monné #include <sys/conf.h>
4478eb3293SRoger Pau Monné #include <sys/fcntl.h>
4578eb3293SRoger Pau Monné #include <sys/ioccom.h>
4678eb3293SRoger Pau Monné #include <sys/rman.h>
4778eb3293SRoger Pau Monné #include <sys/tree.h>
4878eb3293SRoger Pau Monné #include <sys/module.h>
4978eb3293SRoger Pau Monné #include <sys/proc.h>
5078eb3293SRoger Pau Monné #include <sys/bitset.h>
5178eb3293SRoger Pau Monné #include <sys/queue.h>
5278eb3293SRoger Pau Monné #include <sys/mman.h>
5378eb3293SRoger Pau Monné #include <sys/syslog.h>
5478eb3293SRoger Pau Monné #include <sys/taskqueue.h>
5578eb3293SRoger Pau Monné 
5678eb3293SRoger Pau Monné #include <vm/vm.h>
5778eb3293SRoger Pau Monné #include <vm/vm_param.h>
5878eb3293SRoger Pau Monné #include <vm/vm_extern.h>
5978eb3293SRoger Pau Monné #include <vm/vm_kern.h>
6078eb3293SRoger Pau Monné #include <vm/vm_page.h>
6178eb3293SRoger Pau Monné #include <vm/vm_map.h>
6278eb3293SRoger Pau Monné #include <vm/vm_object.h>
6378eb3293SRoger Pau Monné #include <vm/vm_pager.h>
6478eb3293SRoger Pau Monné 
6578eb3293SRoger Pau Monné #include <machine/md_var.h>
6678eb3293SRoger Pau Monné 
6778eb3293SRoger Pau Monné #include <xen/xen-os.h>
6878eb3293SRoger Pau Monné #include <xen/hypervisor.h>
6978eb3293SRoger Pau Monné #include <xen/error.h>
7078eb3293SRoger Pau Monné #include <xen/xen_intr.h>
7178eb3293SRoger Pau Monné #include <xen/gnttab.h>
7278eb3293SRoger Pau Monné #include <xen/gntdev.h>
7378eb3293SRoger Pau Monné 
7478eb3293SRoger Pau Monné MALLOC_DEFINE(M_GNTDEV, "gntdev", "Xen grant-table user-space device");
7578eb3293SRoger Pau Monné 
7678eb3293SRoger Pau Monné #define MAX_OFFSET_COUNT ((0xffffffffffffffffull >> PAGE_SHIFT) + 1)
7778eb3293SRoger Pau Monné 
7878eb3293SRoger Pau Monné static d_open_t gntdev_open;
7978eb3293SRoger Pau Monné static d_ioctl_t gntdev_ioctl;
8078eb3293SRoger Pau Monné static d_mmap_single_t gntdev_mmap_single;
8178eb3293SRoger Pau Monné 
8278eb3293SRoger Pau Monné static struct cdevsw gntdev_devsw = {
8378eb3293SRoger Pau Monné 	.d_version = D_VERSION,
8478eb3293SRoger Pau Monné 	.d_open = gntdev_open,
8578eb3293SRoger Pau Monné 	.d_ioctl = gntdev_ioctl,
8678eb3293SRoger Pau Monné 	.d_mmap_single = gntdev_mmap_single,
8778eb3293SRoger Pau Monné 	.d_name = "gntdev",
8878eb3293SRoger Pau Monné };
8978eb3293SRoger Pau Monné 
9078eb3293SRoger Pau Monné static device_t gntdev_dev = NULL;
9178eb3293SRoger Pau Monné 
9278eb3293SRoger Pau Monné struct gntdev_gref;
9378eb3293SRoger Pau Monné struct gntdev_gmap;
9478eb3293SRoger Pau Monné STAILQ_HEAD(gref_list_head, gntdev_gref);
9578eb3293SRoger Pau Monné STAILQ_HEAD(gmap_list_head, gntdev_gmap);
9678eb3293SRoger Pau Monné RB_HEAD(gref_tree_head, gntdev_gref);
9778eb3293SRoger Pau Monné RB_HEAD(gmap_tree_head, gntdev_gmap);
9878eb3293SRoger Pau Monné 
9978eb3293SRoger Pau Monné struct file_offset_struct {
10078eb3293SRoger Pau Monné 	RB_ENTRY(file_offset_struct)	next;
10178eb3293SRoger Pau Monné 	uint64_t			file_offset;
10278eb3293SRoger Pau Monné 	uint64_t			count;
10378eb3293SRoger Pau Monné };
10478eb3293SRoger Pau Monné 
10578eb3293SRoger Pau Monné static int
offset_cmp(struct file_offset_struct * f1,struct file_offset_struct * f2)10678eb3293SRoger Pau Monné offset_cmp(struct file_offset_struct *f1, struct file_offset_struct *f2)
10778eb3293SRoger Pau Monné {
10878eb3293SRoger Pau Monné 	return (f1->file_offset - f2->file_offset);
10978eb3293SRoger Pau Monné }
11078eb3293SRoger Pau Monné 
11178eb3293SRoger Pau Monné RB_HEAD(file_offset_head, file_offset_struct);
11278eb3293SRoger Pau Monné RB_GENERATE_STATIC(file_offset_head, file_offset_struct, next, offset_cmp);
11378eb3293SRoger Pau Monné 
11478eb3293SRoger Pau Monné struct per_user_data {
11578eb3293SRoger Pau Monné 	struct mtx		user_data_lock;
11678eb3293SRoger Pau Monné 	struct gref_tree_head	gref_tree;
11778eb3293SRoger Pau Monné 	struct gmap_tree_head	gmap_tree;
11878eb3293SRoger Pau Monné 	struct file_offset_head	file_offset;
11978eb3293SRoger Pau Monné };
12078eb3293SRoger Pau Monné 
12178eb3293SRoger Pau Monné /*
12278eb3293SRoger Pau Monné  * Get offset into the file which will be used while mmapping the
12378eb3293SRoger Pau Monné  * appropriate pages by the userspace program.
12478eb3293SRoger Pau Monné  */
12578eb3293SRoger Pau Monné static int
get_file_offset(struct per_user_data * priv_user,uint32_t count,uint64_t * file_offset)12678eb3293SRoger Pau Monné get_file_offset(struct per_user_data *priv_user, uint32_t count,
12778eb3293SRoger Pau Monné     uint64_t *file_offset)
12878eb3293SRoger Pau Monné {
12978eb3293SRoger Pau Monné 	struct file_offset_struct *offset, *offset_tmp;
13078eb3293SRoger Pau Monné 
13178eb3293SRoger Pau Monné 	if (count == 0)
13278eb3293SRoger Pau Monné 		return (EINVAL);
13378eb3293SRoger Pau Monné 	mtx_lock(&priv_user->user_data_lock);
13478eb3293SRoger Pau Monné 	RB_FOREACH_SAFE(offset, file_offset_head, &priv_user->file_offset,
13578eb3293SRoger Pau Monné 	    offset_tmp) {
13678eb3293SRoger Pau Monné 		if (offset->count >= count) {
13778eb3293SRoger Pau Monné 			offset->count -= count;
13878eb3293SRoger Pau Monné 			*file_offset = offset->file_offset + offset->count *
13978eb3293SRoger Pau Monné 			    PAGE_SIZE;
14078eb3293SRoger Pau Monné 			if (offset->count == 0) {
14178eb3293SRoger Pau Monné 				RB_REMOVE(file_offset_head,
14278eb3293SRoger Pau Monné 				    &priv_user->file_offset, offset);
14378eb3293SRoger Pau Monné 				free(offset, M_GNTDEV);
14478eb3293SRoger Pau Monné 			}
14578eb3293SRoger Pau Monné 			mtx_unlock(&priv_user->user_data_lock);
14678eb3293SRoger Pau Monné 			return (0);
14778eb3293SRoger Pau Monné 		}
14878eb3293SRoger Pau Monné 	}
14978eb3293SRoger Pau Monné 	mtx_unlock(&priv_user->user_data_lock);
15078eb3293SRoger Pau Monné 
15178eb3293SRoger Pau Monné 	return (ENOSPC);
15278eb3293SRoger Pau Monné }
15378eb3293SRoger Pau Monné 
15478eb3293SRoger Pau Monné static void
put_file_offset(struct per_user_data * priv_user,uint32_t count,uint64_t file_offset)15578eb3293SRoger Pau Monné put_file_offset(struct per_user_data *priv_user, uint32_t count,
15678eb3293SRoger Pau Monné     uint64_t file_offset)
15778eb3293SRoger Pau Monné {
15878eb3293SRoger Pau Monné 	struct file_offset_struct *offset, *offset_nxt, *offset_prv;
15978eb3293SRoger Pau Monné 
16078eb3293SRoger Pau Monné 	offset = malloc(sizeof(*offset), M_GNTDEV, M_WAITOK | M_ZERO);
16178eb3293SRoger Pau Monné 	offset->file_offset = file_offset;
16278eb3293SRoger Pau Monné 	offset->count = count;
16378eb3293SRoger Pau Monné 
16478eb3293SRoger Pau Monné 	mtx_lock(&priv_user->user_data_lock);
16578eb3293SRoger Pau Monné 	RB_INSERT(file_offset_head, &priv_user->file_offset, offset);
16678eb3293SRoger Pau Monné 	offset_nxt = RB_NEXT(file_offset_head, &priv_user->file_offset, offset);
16778eb3293SRoger Pau Monné 	offset_prv = RB_PREV(file_offset_head, &priv_user->file_offset, offset);
16878eb3293SRoger Pau Monné 	if (offset_nxt != NULL &&
16978eb3293SRoger Pau Monné 	    offset_nxt->file_offset == offset->file_offset + offset->count *
17078eb3293SRoger Pau Monné 	    PAGE_SIZE) {
17178eb3293SRoger Pau Monné 		offset->count += offset_nxt->count;
17278eb3293SRoger Pau Monné 		RB_REMOVE(file_offset_head, &priv_user->file_offset,
17378eb3293SRoger Pau Monné 		    offset_nxt);
17478eb3293SRoger Pau Monné 		free(offset_nxt, M_GNTDEV);
17578eb3293SRoger Pau Monné 	}
17678eb3293SRoger Pau Monné 	if (offset_prv != NULL &&
17778eb3293SRoger Pau Monné 	    offset->file_offset == offset_prv->file_offset + offset_prv->count *
17878eb3293SRoger Pau Monné 	    PAGE_SIZE) {
17978eb3293SRoger Pau Monné 		offset_prv->count += offset->count;
18078eb3293SRoger Pau Monné 		RB_REMOVE(file_offset_head, &priv_user->file_offset, offset);
18178eb3293SRoger Pau Monné 		free(offset, M_GNTDEV);
18278eb3293SRoger Pau Monné 	}
18378eb3293SRoger Pau Monné 	mtx_unlock(&priv_user->user_data_lock);
18478eb3293SRoger Pau Monné }
18578eb3293SRoger Pau Monné 
18678eb3293SRoger Pau Monné static int	gntdev_gmap_pg_ctor(void *handle, vm_ooffset_t size,
18778eb3293SRoger Pau Monné     vm_prot_t prot, vm_ooffset_t foff, struct ucred *cred, u_short *color);
18878eb3293SRoger Pau Monné static void	gntdev_gmap_pg_dtor(void *handle);
18978eb3293SRoger Pau Monné static int	gntdev_gmap_pg_fault(vm_object_t object, vm_ooffset_t offset,
19078eb3293SRoger Pau Monné     int prot, vm_page_t *mres);
19178eb3293SRoger Pau Monné 
19278eb3293SRoger Pau Monné static struct cdev_pager_ops gntdev_gmap_pg_ops = {
19378eb3293SRoger Pau Monné 	.cdev_pg_fault = gntdev_gmap_pg_fault,
19478eb3293SRoger Pau Monné 	.cdev_pg_ctor =	gntdev_gmap_pg_ctor,
19578eb3293SRoger Pau Monné 	.cdev_pg_dtor =	gntdev_gmap_pg_dtor,
19678eb3293SRoger Pau Monné };
19778eb3293SRoger Pau Monné 
19878eb3293SRoger Pau Monné struct cleanup_data_struct {
19978eb3293SRoger Pau Monné 	struct mtx to_kill_grefs_mtx;
20078eb3293SRoger Pau Monné 	struct mtx to_kill_gmaps_mtx;
20178eb3293SRoger Pau Monné 	struct gref_list_head to_kill_grefs;
20278eb3293SRoger Pau Monné 	struct gmap_list_head to_kill_gmaps;
20378eb3293SRoger Pau Monné };
20478eb3293SRoger Pau Monné 
20578eb3293SRoger Pau Monné static struct cleanup_data_struct cleanup_data = {
20678eb3293SRoger Pau Monné 	.to_kill_grefs = STAILQ_HEAD_INITIALIZER(cleanup_data.to_kill_grefs),
20778eb3293SRoger Pau Monné 	.to_kill_gmaps = STAILQ_HEAD_INITIALIZER(cleanup_data.to_kill_gmaps),
20878eb3293SRoger Pau Monné };
20978eb3293SRoger Pau Monné MTX_SYSINIT(to_kill_grefs_mtx, &cleanup_data.to_kill_grefs_mtx,
21078eb3293SRoger Pau Monné     "gntdev to_kill_grefs mutex", MTX_DEF);
21178eb3293SRoger Pau Monné MTX_SYSINIT(to_kill_gmaps_mtx, &cleanup_data.to_kill_gmaps_mtx,
21278eb3293SRoger Pau Monné     "gntdev to_kill_gmaps mutex", MTX_DEF);
21378eb3293SRoger Pau Monné 
21478eb3293SRoger Pau Monné static void	cleanup_function(void *arg, __unused int pending);
21578eb3293SRoger Pau Monné static struct task cleanup_task = TASK_INITIALIZER(0, cleanup_function,
21678eb3293SRoger Pau Monné     &cleanup_data);
21778eb3293SRoger Pau Monné 
21878eb3293SRoger Pau Monné struct notify_data {
21978eb3293SRoger Pau Monné 	uint64_t		index;
22078eb3293SRoger Pau Monné 	uint32_t		action;
22178eb3293SRoger Pau Monné 	uint32_t		event_channel_port;
22278eb3293SRoger Pau Monné 	xen_intr_handle_t	notify_evtchn_handle;
22378eb3293SRoger Pau Monné };
22478eb3293SRoger Pau Monné 
22578eb3293SRoger Pau Monné static void	notify(struct notify_data *notify, vm_page_t page);
22678eb3293SRoger Pau Monné 
22778eb3293SRoger Pau Monné /*-------------------- Grant Allocation Methods  -----------------------------*/
22878eb3293SRoger Pau Monné 
22978eb3293SRoger Pau Monné struct gntdev_gref {
23078eb3293SRoger Pau Monné 	union gref_next_union {
23178eb3293SRoger Pau Monné 		STAILQ_ENTRY(gntdev_gref) 		list;
23278eb3293SRoger Pau Monné 		RB_ENTRY(gntdev_gref)	 		tree;
23378eb3293SRoger Pau Monné 	}			gref_next;
23478eb3293SRoger Pau Monné 	uint64_t		file_index;
23578eb3293SRoger Pau Monné 	grant_ref_t		gref_id;
23678eb3293SRoger Pau Monné 	vm_page_t		page;
23778eb3293SRoger Pau Monné 	struct notify_data	*notify;
23878eb3293SRoger Pau Monné };
23978eb3293SRoger Pau Monné 
24078eb3293SRoger Pau Monné static int
gref_cmp(struct gntdev_gref * g1,struct gntdev_gref * g2)24178eb3293SRoger Pau Monné gref_cmp(struct gntdev_gref *g1, struct gntdev_gref *g2)
24278eb3293SRoger Pau Monné {
24378eb3293SRoger Pau Monné 	return (g1->file_index - g2->file_index);
24478eb3293SRoger Pau Monné }
24578eb3293SRoger Pau Monné 
24678eb3293SRoger Pau Monné RB_GENERATE_STATIC(gref_tree_head, gntdev_gref, gref_next.tree, gref_cmp);
24778eb3293SRoger Pau Monné 
24878eb3293SRoger Pau Monné /*
24978eb3293SRoger Pau Monné  * Traverse over the device-list of to-be-deleted grants allocated, and
25078eb3293SRoger Pau Monné  * if all accesses, both local mmaps and foreign maps, to them have ended,
25178eb3293SRoger Pau Monné  * destroy them.
25278eb3293SRoger Pau Monné  */
25378eb3293SRoger Pau Monné static void
gref_list_dtor(struct cleanup_data_struct * cleanup_data)25478eb3293SRoger Pau Monné gref_list_dtor(struct cleanup_data_struct *cleanup_data)
25578eb3293SRoger Pau Monné {
25678eb3293SRoger Pau Monné 	struct gref_list_head tmp_grefs;
25778eb3293SRoger Pau Monné 	struct gntdev_gref *gref, *gref_tmp, *gref_previous;
25878eb3293SRoger Pau Monné 
25978eb3293SRoger Pau Monné 	STAILQ_INIT(&tmp_grefs);
26078eb3293SRoger Pau Monné 	mtx_lock(&cleanup_data->to_kill_grefs_mtx);
26178eb3293SRoger Pau Monné 	STAILQ_SWAP(&cleanup_data->to_kill_grefs, &tmp_grefs, gntdev_gref);
26278eb3293SRoger Pau Monné 	mtx_unlock(&cleanup_data->to_kill_grefs_mtx);
26378eb3293SRoger Pau Monné 
26478eb3293SRoger Pau Monné 	gref_previous = NULL;
26578eb3293SRoger Pau Monné 	STAILQ_FOREACH_SAFE(gref, &tmp_grefs, gref_next.list, gref_tmp) {
26678eb3293SRoger Pau Monné 		if (gref->page && gref->page->object == NULL) {
26778eb3293SRoger Pau Monné 			if (gref->notify) {
26878eb3293SRoger Pau Monné 				notify(gref->notify, gref->page);
26978eb3293SRoger Pau Monné 			}
27078eb3293SRoger Pau Monné 			if (gref->gref_id != GRANT_REF_INVALID) {
27178eb3293SRoger Pau Monné 				if (gnttab_query_foreign_access(gref->gref_id))
27278eb3293SRoger Pau Monné 					continue;
27378eb3293SRoger Pau Monné 				if (gnttab_end_foreign_access_ref(gref->gref_id)
27478eb3293SRoger Pau Monné 				    == 0)
27578eb3293SRoger Pau Monné 					continue;
27678eb3293SRoger Pau Monné 				gnttab_free_grant_reference(gref->gref_id);
27778eb3293SRoger Pau Monné 			}
27888ea538aSMark Johnston 			vm_page_unwire_noq(gref->page);
27978eb3293SRoger Pau Monné 			vm_page_free(gref->page);
28078eb3293SRoger Pau Monné 			gref->page = NULL;
28178eb3293SRoger Pau Monné 		}
28278eb3293SRoger Pau Monné 		if (gref->page == NULL) {
28378eb3293SRoger Pau Monné 			if (gref_previous == NULL)
28478eb3293SRoger Pau Monné 				STAILQ_REMOVE_HEAD(&tmp_grefs, gref_next.list);
28578eb3293SRoger Pau Monné 			else
28678eb3293SRoger Pau Monné 				STAILQ_REMOVE_AFTER(&tmp_grefs, gref_previous,
28778eb3293SRoger Pau Monné 				    gref_next.list);
28878eb3293SRoger Pau Monné 			if (gref->notify)
28978eb3293SRoger Pau Monné 				free(gref->notify, M_GNTDEV);
29078eb3293SRoger Pau Monné 			free(gref, M_GNTDEV);
29178eb3293SRoger Pau Monné 		}
29278eb3293SRoger Pau Monné 		else
29378eb3293SRoger Pau Monné 			gref_previous = gref;
29478eb3293SRoger Pau Monné 	}
29578eb3293SRoger Pau Monné 
29678eb3293SRoger Pau Monné 	if (!STAILQ_EMPTY(&tmp_grefs)) {
29778eb3293SRoger Pau Monné 		mtx_lock(&cleanup_data->to_kill_grefs_mtx);
29878eb3293SRoger Pau Monné 		STAILQ_CONCAT(&cleanup_data->to_kill_grefs, &tmp_grefs);
29978eb3293SRoger Pau Monné 		mtx_unlock(&cleanup_data->to_kill_grefs_mtx);
30078eb3293SRoger Pau Monné 	}
30178eb3293SRoger Pau Monné }
30278eb3293SRoger Pau Monné 
30378eb3293SRoger Pau Monné /*
30478eb3293SRoger Pau Monné  * Find count number of contiguous allocated grants for a given userspace
30578eb3293SRoger Pau Monné  * program by file-offset (index).
30678eb3293SRoger Pau Monné  */
30778eb3293SRoger Pau Monné static struct gntdev_gref*
gntdev_find_grefs(struct per_user_data * priv_user,uint64_t index,uint32_t count)30878eb3293SRoger Pau Monné gntdev_find_grefs(struct per_user_data *priv_user,
30978eb3293SRoger Pau Monné 	uint64_t index, uint32_t count)
31078eb3293SRoger Pau Monné {
31178eb3293SRoger Pau Monné 	struct gntdev_gref find_gref, *gref, *gref_start = NULL;
31278eb3293SRoger Pau Monné 
31378eb3293SRoger Pau Monné 	find_gref.file_index = index;
31478eb3293SRoger Pau Monné 
31578eb3293SRoger Pau Monné 	mtx_lock(&priv_user->user_data_lock);
31678eb3293SRoger Pau Monné 	gref_start = RB_FIND(gref_tree_head, &priv_user->gref_tree, &find_gref);
31778eb3293SRoger Pau Monné 	for (gref = gref_start; gref != NULL && count > 0; gref =
31878eb3293SRoger Pau Monné 	    RB_NEXT(gref_tree_head, &priv_user->gref_tree, gref)) {
31978eb3293SRoger Pau Monné 		if (index != gref->file_index)
32078eb3293SRoger Pau Monné 			break;
32178eb3293SRoger Pau Monné 		index += PAGE_SIZE;
32278eb3293SRoger Pau Monné 		count--;
32378eb3293SRoger Pau Monné 	}
32478eb3293SRoger Pau Monné 	mtx_unlock(&priv_user->user_data_lock);
32578eb3293SRoger Pau Monné 
32678eb3293SRoger Pau Monné 	if (count)
32778eb3293SRoger Pau Monné 		return (NULL);
32878eb3293SRoger Pau Monné 	return (gref_start);
32978eb3293SRoger Pau Monné }
33078eb3293SRoger Pau Monné 
33178eb3293SRoger Pau Monné /*
33278eb3293SRoger Pau Monné  * IOCTL_GNTDEV_ALLOC_GREF
33378eb3293SRoger Pau Monné  * Allocate required number of wired pages for the request, grant foreign
33478eb3293SRoger Pau Monné  * access to the physical frames for these pages, and add details about
33578eb3293SRoger Pau Monné  * this allocation to the per user private data, so that these pages can
33678eb3293SRoger Pau Monné  * be mmapped by the userspace program.
33778eb3293SRoger Pau Monné  */
33878eb3293SRoger Pau Monné static int
gntdev_alloc_gref(struct ioctl_gntdev_alloc_gref * arg)33978eb3293SRoger Pau Monné gntdev_alloc_gref(struct ioctl_gntdev_alloc_gref *arg)
34078eb3293SRoger Pau Monné {
34178eb3293SRoger Pau Monné 	uint32_t i;
34278eb3293SRoger Pau Monné 	int error, readonly;
34378eb3293SRoger Pau Monné 	uint64_t file_offset;
34478eb3293SRoger Pau Monné 	struct gntdev_gref *grefs;
34578eb3293SRoger Pau Monné 	struct per_user_data *priv_user;
34678eb3293SRoger Pau Monné 
34778eb3293SRoger Pau Monné 	readonly = !(arg->flags & GNTDEV_ALLOC_FLAG_WRITABLE);
34878eb3293SRoger Pau Monné 
34978eb3293SRoger Pau Monné 	error = devfs_get_cdevpriv((void**) &priv_user);
35078eb3293SRoger Pau Monné 	if (error != 0)
35178eb3293SRoger Pau Monné 		return (EINVAL);
35278eb3293SRoger Pau Monné 
35378eb3293SRoger Pau Monné 	/* Cleanup grefs and free pages. */
35478eb3293SRoger Pau Monné 	taskqueue_enqueue(taskqueue_thread, &cleanup_task);
35578eb3293SRoger Pau Monné 
35678eb3293SRoger Pau Monné 	/* Get file offset for this request. */
35778eb3293SRoger Pau Monné 	error = get_file_offset(priv_user, arg->count, &file_offset);
35878eb3293SRoger Pau Monné 	if (error != 0)
35978eb3293SRoger Pau Monné 		return (error);
36078eb3293SRoger Pau Monné 
36178eb3293SRoger Pau Monné 	/* Allocate grefs. */
36278eb3293SRoger Pau Monné 	grefs = malloc(sizeof(*grefs) * arg->count, M_GNTDEV, M_WAITOK);
36378eb3293SRoger Pau Monné 
36478eb3293SRoger Pau Monné 	for (i = 0; i < arg->count; i++) {
36578eb3293SRoger Pau Monné 		grefs[i].file_index = file_offset + i * PAGE_SIZE;
36678eb3293SRoger Pau Monné 		grefs[i].gref_id = GRANT_REF_INVALID;
36778eb3293SRoger Pau Monné 		grefs[i].notify = NULL;
368a4667e09SMark Johnston 		grefs[i].page = vm_page_alloc_noobj(VM_ALLOC_WIRED |
369a4667e09SMark Johnston 		    VM_ALLOC_ZERO);
37078eb3293SRoger Pau Monné 		if (grefs[i].page == NULL) {
37178eb3293SRoger Pau Monné 			log(LOG_ERR, "Page allocation failed.");
37278eb3293SRoger Pau Monné 			error = ENOMEM;
37378eb3293SRoger Pau Monné 			break;
37478eb3293SRoger Pau Monné 		}
37578eb3293SRoger Pau Monné 		grefs[i].page->valid = VM_PAGE_BITS_ALL;
37678eb3293SRoger Pau Monné 
37778eb3293SRoger Pau Monné 		error = gnttab_grant_foreign_access(arg->domid,
37878eb3293SRoger Pau Monné 			(VM_PAGE_TO_PHYS(grefs[i].page) >> PAGE_SHIFT),
37978eb3293SRoger Pau Monné 			readonly, &grefs[i].gref_id);
38078eb3293SRoger Pau Monné 		if (error != 0) {
38178eb3293SRoger Pau Monné 			log(LOG_ERR, "Grant Table Hypercall failed.");
38278eb3293SRoger Pau Monné 			break;
38378eb3293SRoger Pau Monné 		}
38478eb3293SRoger Pau Monné 	}
38578eb3293SRoger Pau Monné 
3866cdff09cSMark Johnston 	/* Copy the output values. */
3876cdff09cSMark Johnston 	arg->index = file_offset;
3886cdff09cSMark Johnston 	for (i = 0; error == 0 && i < arg->count; i++) {
3896cdff09cSMark Johnston 		if (suword32(&arg->gref_ids[i], grefs[i].gref_id) != 0)
3906cdff09cSMark Johnston 			error = EFAULT;
3916cdff09cSMark Johnston 	}
3926cdff09cSMark Johnston 
39378eb3293SRoger Pau Monné 	if (error != 0) {
39478eb3293SRoger Pau Monné 		/*
39578eb3293SRoger Pau Monné 		 * If target domain maps the gref (by guessing the gref-id),
39678eb3293SRoger Pau Monné 		 * then we can't clean it up yet and we have to leave the
39778eb3293SRoger Pau Monné 		 * page in place so as to not leak our memory to that domain.
39878eb3293SRoger Pau Monné 		 * Add it to a global list to be cleaned up later.
39978eb3293SRoger Pau Monné 		 */
40078eb3293SRoger Pau Monné 		mtx_lock(&cleanup_data.to_kill_grefs_mtx);
40178eb3293SRoger Pau Monné 		for (i = 0; i < arg->count; i++)
40278eb3293SRoger Pau Monné 			STAILQ_INSERT_TAIL(&cleanup_data.to_kill_grefs,
40378eb3293SRoger Pau Monné 			    &grefs[i], gref_next.list);
40478eb3293SRoger Pau Monné 		mtx_unlock(&cleanup_data.to_kill_grefs_mtx);
40578eb3293SRoger Pau Monné 
40678eb3293SRoger Pau Monné 		taskqueue_enqueue(taskqueue_thread, &cleanup_task);
40778eb3293SRoger Pau Monné 
40878eb3293SRoger Pau Monné 		return (error);
40978eb3293SRoger Pau Monné 	}
41078eb3293SRoger Pau Monné 
41178eb3293SRoger Pau Monné 	/* Modify the per user private data. */
41278eb3293SRoger Pau Monné 	mtx_lock(&priv_user->user_data_lock);
41378eb3293SRoger Pau Monné 	for (i = 0; i < arg->count; i++)
41478eb3293SRoger Pau Monné 		RB_INSERT(gref_tree_head, &priv_user->gref_tree, &grefs[i]);
41578eb3293SRoger Pau Monné 	mtx_unlock(&priv_user->user_data_lock);
41678eb3293SRoger Pau Monné 
41778eb3293SRoger Pau Monné 	return (error);
41878eb3293SRoger Pau Monné }
41978eb3293SRoger Pau Monné 
42078eb3293SRoger Pau Monné /*
42178eb3293SRoger Pau Monné  * IOCTL_GNTDEV_DEALLOC_GREF
42278eb3293SRoger Pau Monné  * Remove grant allocation information from the per user private data, so
42378eb3293SRoger Pau Monné  * that it can't be mmapped anymore by the userspace program, and add it
42478eb3293SRoger Pau Monné  * to the to-be-deleted grants global device-list.
42578eb3293SRoger Pau Monné  */
42678eb3293SRoger Pau Monné static int
gntdev_dealloc_gref(struct ioctl_gntdev_dealloc_gref * arg)42778eb3293SRoger Pau Monné gntdev_dealloc_gref(struct ioctl_gntdev_dealloc_gref *arg)
42878eb3293SRoger Pau Monné {
42978eb3293SRoger Pau Monné 	int error;
43078eb3293SRoger Pau Monné 	uint32_t count;
43178eb3293SRoger Pau Monné 	struct gntdev_gref *gref, *gref_tmp;
43278eb3293SRoger Pau Monné 	struct per_user_data *priv_user;
43378eb3293SRoger Pau Monné 
43478eb3293SRoger Pau Monné 	error = devfs_get_cdevpriv((void**) &priv_user);
43578eb3293SRoger Pau Monné 	if (error != 0)
43678eb3293SRoger Pau Monné 		return (EINVAL);
43778eb3293SRoger Pau Monné 
43878eb3293SRoger Pau Monné 	gref = gntdev_find_grefs(priv_user, arg->index, arg->count);
43978eb3293SRoger Pau Monné 	if (gref == NULL) {
44078eb3293SRoger Pau Monné 		log(LOG_ERR, "Can't find requested grant-refs.");
44178eb3293SRoger Pau Monné 		return (EINVAL);
44278eb3293SRoger Pau Monné 	}
44378eb3293SRoger Pau Monné 
44478eb3293SRoger Pau Monné 	/* Remove the grefs from user private data. */
44578eb3293SRoger Pau Monné 	count = arg->count;
44678eb3293SRoger Pau Monné 	mtx_lock(&priv_user->user_data_lock);
44778eb3293SRoger Pau Monné 	mtx_lock(&cleanup_data.to_kill_grefs_mtx);
44878eb3293SRoger Pau Monné 	for (; gref != NULL && count > 0; gref = gref_tmp) {
44978eb3293SRoger Pau Monné 		gref_tmp = RB_NEXT(gref_tree_head, &priv_user->gref_tree, gref);
45078eb3293SRoger Pau Monné 		RB_REMOVE(gref_tree_head, &priv_user->gref_tree, gref);
45178eb3293SRoger Pau Monné 		STAILQ_INSERT_TAIL(&cleanup_data.to_kill_grefs, gref,
45278eb3293SRoger Pau Monné 		    gref_next.list);
45378eb3293SRoger Pau Monné 		count--;
45478eb3293SRoger Pau Monné 	}
45578eb3293SRoger Pau Monné 	mtx_unlock(&cleanup_data.to_kill_grefs_mtx);
45678eb3293SRoger Pau Monné 	mtx_unlock(&priv_user->user_data_lock);
45778eb3293SRoger Pau Monné 
45878eb3293SRoger Pau Monné 	taskqueue_enqueue(taskqueue_thread, &cleanup_task);
45978eb3293SRoger Pau Monné 	put_file_offset(priv_user, arg->count, arg->index);
46078eb3293SRoger Pau Monné 
46178eb3293SRoger Pau Monné 	return (0);
46278eb3293SRoger Pau Monné }
46378eb3293SRoger Pau Monné 
46478eb3293SRoger Pau Monné /*-------------------- Grant Mapping Methods  --------------------------------*/
46578eb3293SRoger Pau Monné 
46678eb3293SRoger Pau Monné struct gntdev_gmap_map {
46778eb3293SRoger Pau Monné 	vm_object_t	mem;
46878eb3293SRoger Pau Monné 	struct resource	*pseudo_phys_res;
46978eb3293SRoger Pau Monné 	int 		pseudo_phys_res_id;
47078eb3293SRoger Pau Monné 	vm_paddr_t	phys_base_addr;
47178eb3293SRoger Pau Monné };
47278eb3293SRoger Pau Monné 
47378eb3293SRoger Pau Monné struct gntdev_gmap {
47478eb3293SRoger Pau Monné 	union gmap_next_union {
47578eb3293SRoger Pau Monné 		STAILQ_ENTRY(gntdev_gmap)		list;
47678eb3293SRoger Pau Monné 		RB_ENTRY(gntdev_gmap)			tree;
47778eb3293SRoger Pau Monné 	}				gmap_next;
47878eb3293SRoger Pau Monné 	uint64_t			file_index;
47978eb3293SRoger Pau Monné 	uint32_t			count;
48078eb3293SRoger Pau Monné 	struct gnttab_map_grant_ref	*grant_map_ops;
48178eb3293SRoger Pau Monné 	struct gntdev_gmap_map		*map;
48278eb3293SRoger Pau Monné 	struct notify_data		*notify;
48378eb3293SRoger Pau Monné };
48478eb3293SRoger Pau Monné 
48578eb3293SRoger Pau Monné static int
gmap_cmp(struct gntdev_gmap * g1,struct gntdev_gmap * g2)48678eb3293SRoger Pau Monné gmap_cmp(struct gntdev_gmap *g1, struct gntdev_gmap *g2)
48778eb3293SRoger Pau Monné {
48878eb3293SRoger Pau Monné 	return (g1->file_index - g2->file_index);
48978eb3293SRoger Pau Monné }
49078eb3293SRoger Pau Monné 
49178eb3293SRoger Pau Monné RB_GENERATE_STATIC(gmap_tree_head, gntdev_gmap, gmap_next.tree, gmap_cmp);
49278eb3293SRoger Pau Monné 
49378eb3293SRoger Pau Monné /*
49478eb3293SRoger Pau Monné  * Traverse over the device-list of to-be-deleted grant mappings, and if
49578eb3293SRoger Pau Monné  * the region is no longer mmapped by anyone, free the memory used to
49678eb3293SRoger Pau Monné  * store information about the mapping.
49778eb3293SRoger Pau Monné  */
49878eb3293SRoger Pau Monné static void
gmap_list_dtor(struct cleanup_data_struct * cleanup_data)49978eb3293SRoger Pau Monné gmap_list_dtor(struct cleanup_data_struct *cleanup_data)
50078eb3293SRoger Pau Monné {
50178eb3293SRoger Pau Monné 	struct gmap_list_head tmp_gmaps;
50278eb3293SRoger Pau Monné 	struct gntdev_gmap *gmap, *gmap_tmp, *gmap_previous;
50378eb3293SRoger Pau Monné 
50478eb3293SRoger Pau Monné 	STAILQ_INIT(&tmp_gmaps);
50578eb3293SRoger Pau Monné 	mtx_lock(&cleanup_data->to_kill_gmaps_mtx);
50678eb3293SRoger Pau Monné 	STAILQ_SWAP(&cleanup_data->to_kill_gmaps, &tmp_gmaps, gntdev_gmap);
50778eb3293SRoger Pau Monné 	mtx_unlock(&cleanup_data->to_kill_gmaps_mtx);
50878eb3293SRoger Pau Monné 
50978eb3293SRoger Pau Monné 	gmap_previous = NULL;
51078eb3293SRoger Pau Monné 	STAILQ_FOREACH_SAFE(gmap, &tmp_gmaps, gmap_next.list, gmap_tmp) {
51178eb3293SRoger Pau Monné 		if (gmap->map == NULL) {
51278eb3293SRoger Pau Monné 			if (gmap_previous == NULL)
51378eb3293SRoger Pau Monné 				STAILQ_REMOVE_HEAD(&tmp_gmaps, gmap_next.list);
51478eb3293SRoger Pau Monné 			else
51578eb3293SRoger Pau Monné 				STAILQ_REMOVE_AFTER(&tmp_gmaps, gmap_previous,
51678eb3293SRoger Pau Monné 				    gmap_next.list);
51778eb3293SRoger Pau Monné 
51878eb3293SRoger Pau Monné 			if (gmap->notify)
51978eb3293SRoger Pau Monné 				free(gmap->notify, M_GNTDEV);
52078eb3293SRoger Pau Monné 			free(gmap->grant_map_ops, M_GNTDEV);
52178eb3293SRoger Pau Monné 			free(gmap, M_GNTDEV);
52278eb3293SRoger Pau Monné 		}
52378eb3293SRoger Pau Monné 		else
52478eb3293SRoger Pau Monné 			gmap_previous = gmap;
52578eb3293SRoger Pau Monné 	}
52678eb3293SRoger Pau Monné 
52778eb3293SRoger Pau Monné 	if (!STAILQ_EMPTY(&tmp_gmaps)) {
52878eb3293SRoger Pau Monné 		mtx_lock(&cleanup_data->to_kill_gmaps_mtx);
52978eb3293SRoger Pau Monné 		STAILQ_CONCAT(&cleanup_data->to_kill_gmaps, &tmp_gmaps);
53078eb3293SRoger Pau Monné 		mtx_unlock(&cleanup_data->to_kill_gmaps_mtx);
53178eb3293SRoger Pau Monné 	}
53278eb3293SRoger Pau Monné }
53378eb3293SRoger Pau Monné 
53478eb3293SRoger Pau Monné /*
53578eb3293SRoger Pau Monné  * Find mapped grants for a given userspace program, by file-offset (index)
53678eb3293SRoger Pau Monné  * and count, as supplied during the map-ioctl.
53778eb3293SRoger Pau Monné  */
53878eb3293SRoger Pau Monné static struct gntdev_gmap*
gntdev_find_gmap(struct per_user_data * priv_user,uint64_t index,uint32_t count)53978eb3293SRoger Pau Monné gntdev_find_gmap(struct per_user_data *priv_user,
54078eb3293SRoger Pau Monné 	uint64_t index, uint32_t count)
54178eb3293SRoger Pau Monné {
54278eb3293SRoger Pau Monné 	struct gntdev_gmap find_gmap, *gmap;
54378eb3293SRoger Pau Monné 
54478eb3293SRoger Pau Monné 	find_gmap.file_index = index;
54578eb3293SRoger Pau Monné 
54678eb3293SRoger Pau Monné 	mtx_lock(&priv_user->user_data_lock);
54778eb3293SRoger Pau Monné 	gmap = RB_FIND(gmap_tree_head, &priv_user->gmap_tree, &find_gmap);
54878eb3293SRoger Pau Monné 	mtx_unlock(&priv_user->user_data_lock);
54978eb3293SRoger Pau Monné 
55078eb3293SRoger Pau Monné 	if (gmap != NULL && gmap->count == count)
55178eb3293SRoger Pau Monné 		return (gmap);
55278eb3293SRoger Pau Monné 	return (NULL);
55378eb3293SRoger Pau Monné }
55478eb3293SRoger Pau Monné 
55578eb3293SRoger Pau Monné /*
55678eb3293SRoger Pau Monné  * Remove the pages from the mgtdevice pager, call the unmap hypercall,
55778eb3293SRoger Pau Monné  * free the xenmem resource. This function is called during the
55878eb3293SRoger Pau Monné  * destruction of the mgtdevice pager, which happens when all mmaps to
55978eb3293SRoger Pau Monné  * it have been removed, and the unmap-ioctl has been performed.
56078eb3293SRoger Pau Monné  */
56178eb3293SRoger Pau Monné static int
notify_unmap_cleanup(struct gntdev_gmap * gmap)56278eb3293SRoger Pau Monné notify_unmap_cleanup(struct gntdev_gmap *gmap)
56378eb3293SRoger Pau Monné {
56478eb3293SRoger Pau Monné 	uint32_t i;
56578eb3293SRoger Pau Monné 	int error, count;
56678eb3293SRoger Pau Monné 	vm_page_t m;
56778eb3293SRoger Pau Monné 	struct gnttab_unmap_grant_ref *unmap_ops;
56878eb3293SRoger Pau Monné 
56978eb3293SRoger Pau Monné 	unmap_ops = malloc(sizeof(struct gnttab_unmap_grant_ref) * gmap->count,
57078eb3293SRoger Pau Monné 			M_GNTDEV, M_WAITOK);
57178eb3293SRoger Pau Monné 
57278eb3293SRoger Pau Monné 	/* Enumerate freeable maps. */
57378eb3293SRoger Pau Monné 	count = 0;
57478eb3293SRoger Pau Monné 	for (i = 0; i < gmap->count; i++) {
57578eb3293SRoger Pau Monné 		if (gmap->grant_map_ops[i].handle != -1) {
57678eb3293SRoger Pau Monné 			unmap_ops[count].handle = gmap->grant_map_ops[i].handle;
57778eb3293SRoger Pau Monné 			unmap_ops[count].host_addr =
57878eb3293SRoger Pau Monné 				gmap->grant_map_ops[i].host_addr;
57978eb3293SRoger Pau Monné 			unmap_ops[count].dev_bus_addr = 0;
58078eb3293SRoger Pau Monné 			count++;
58178eb3293SRoger Pau Monné 		}
58278eb3293SRoger Pau Monné 	}
58378eb3293SRoger Pau Monné 
58478eb3293SRoger Pau Monné 	/* Perform notification. */
58578eb3293SRoger Pau Monné 	if (count > 0 && gmap->notify) {
58678eb3293SRoger Pau Monné 		vm_page_t page;
58778eb3293SRoger Pau Monné 		uint64_t page_offset;
58878eb3293SRoger Pau Monné 
58978eb3293SRoger Pau Monné 		page_offset = gmap->notify->index - gmap->file_index;
59078eb3293SRoger Pau Monné 		page = PHYS_TO_VM_PAGE(gmap->map->phys_base_addr + page_offset);
59178eb3293SRoger Pau Monné 		notify(gmap->notify, page);
59278eb3293SRoger Pau Monné 	}
59378eb3293SRoger Pau Monné 
59478eb3293SRoger Pau Monné 	/* Free the pages. */
59578eb3293SRoger Pau Monné 	VM_OBJECT_WLOCK(gmap->map->mem);
59678eb3293SRoger Pau Monné retry:
59778eb3293SRoger Pau Monné 	for (i = 0; i < gmap->count; i++) {
59878eb3293SRoger Pau Monné 		m = vm_page_lookup(gmap->map->mem, i);
59978eb3293SRoger Pau Monné 		if (m == NULL)
60078eb3293SRoger Pau Monné 			continue;
601c7575748SJeff Roberson 		if (vm_page_busy_acquire(m, VM_ALLOC_WAITFAIL) == 0)
60278eb3293SRoger Pau Monné 			goto retry;
603*d48524e2SDoug Moore 		cdev_mgtdev_pager_free_page(gmap->map->mem, m);
60478eb3293SRoger Pau Monné 	}
60578eb3293SRoger Pau Monné 	VM_OBJECT_WUNLOCK(gmap->map->mem);
60678eb3293SRoger Pau Monné 
60778eb3293SRoger Pau Monné 	/* Perform unmap hypercall. */
60878eb3293SRoger Pau Monné 	error = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref,
60978eb3293SRoger Pau Monné 	    unmap_ops, count);
61078eb3293SRoger Pau Monné 
61178eb3293SRoger Pau Monné 	for (i = 0; i < gmap->count; i++) {
61278eb3293SRoger Pau Monné 		gmap->grant_map_ops[i].handle = -1;
61378eb3293SRoger Pau Monné 		gmap->grant_map_ops[i].host_addr = 0;
61478eb3293SRoger Pau Monné 	}
61578eb3293SRoger Pau Monné 
61678eb3293SRoger Pau Monné 	if (gmap->map) {
61778eb3293SRoger Pau Monné 		error = xenmem_free(gntdev_dev, gmap->map->pseudo_phys_res_id,
61878eb3293SRoger Pau Monné 		    gmap->map->pseudo_phys_res);
61978eb3293SRoger Pau Monné 		KASSERT(error == 0,
62078eb3293SRoger Pau Monné 		    ("Unable to release memory resource: %d", error));
62178eb3293SRoger Pau Monné 
62278eb3293SRoger Pau Monné 		free(gmap->map, M_GNTDEV);
62378eb3293SRoger Pau Monné 		gmap->map = NULL;
62478eb3293SRoger Pau Monné 	}
62578eb3293SRoger Pau Monné 
62678eb3293SRoger Pau Monné 	free(unmap_ops, M_GNTDEV);
62778eb3293SRoger Pau Monné 
62878eb3293SRoger Pau Monné 	return (error);
62978eb3293SRoger Pau Monné }
63078eb3293SRoger Pau Monné 
63178eb3293SRoger Pau Monné /*
63278eb3293SRoger Pau Monné  * IOCTL_GNTDEV_MAP_GRANT_REF
63378eb3293SRoger Pau Monné  * Populate structures for mapping the grant reference in the per user
63478eb3293SRoger Pau Monné  * private data. Actual resource allocation and map hypercall is performed
63578eb3293SRoger Pau Monné  * during the mmap.
63678eb3293SRoger Pau Monné  */
63778eb3293SRoger Pau Monné static int
gntdev_map_grant_ref(struct ioctl_gntdev_map_grant_ref * arg)63878eb3293SRoger Pau Monné gntdev_map_grant_ref(struct ioctl_gntdev_map_grant_ref *arg)
63978eb3293SRoger Pau Monné {
64078eb3293SRoger Pau Monné 	uint32_t i;
64178eb3293SRoger Pau Monné 	int error;
64278eb3293SRoger Pau Monné 	struct gntdev_gmap *gmap;
64378eb3293SRoger Pau Monné 	struct per_user_data *priv_user;
64478eb3293SRoger Pau Monné 
64578eb3293SRoger Pau Monné 	error = devfs_get_cdevpriv((void**) &priv_user);
64678eb3293SRoger Pau Monné 	if (error != 0)
64778eb3293SRoger Pau Monné 		return (EINVAL);
64878eb3293SRoger Pau Monné 
64978eb3293SRoger Pau Monné 	gmap = malloc(sizeof(*gmap), M_GNTDEV, M_WAITOK | M_ZERO);
65078eb3293SRoger Pau Monné 	gmap->count = arg->count;
65178eb3293SRoger Pau Monné 	gmap->grant_map_ops =
65278eb3293SRoger Pau Monné 	    malloc(sizeof(struct gnttab_map_grant_ref) * arg->count,
65378eb3293SRoger Pau Monné 	        M_GNTDEV, M_WAITOK | M_ZERO);
65478eb3293SRoger Pau Monné 
65578eb3293SRoger Pau Monné 	for (i = 0; i < arg->count; i++) {
6562602ef7cSRoger Pau Monné 		struct ioctl_gntdev_grant_ref ref;
6572602ef7cSRoger Pau Monné 
6582602ef7cSRoger Pau Monné 		error = copyin(&arg->refs[i], &ref, sizeof(ref));
6592602ef7cSRoger Pau Monné 		if (error != 0) {
6602602ef7cSRoger Pau Monné 			free(gmap->grant_map_ops, M_GNTDEV);
6612602ef7cSRoger Pau Monné 			free(gmap, M_GNTDEV);
6622602ef7cSRoger Pau Monné 			return (error);
6632602ef7cSRoger Pau Monné 		}
6642602ef7cSRoger Pau Monné 		gmap->grant_map_ops[i].dom = ref.domid;
6652602ef7cSRoger Pau Monné 		gmap->grant_map_ops[i].ref = ref.ref;
66678eb3293SRoger Pau Monné 		gmap->grant_map_ops[i].handle = -1;
66778eb3293SRoger Pau Monné 		gmap->grant_map_ops[i].flags = GNTMAP_host_map;
66878eb3293SRoger Pau Monné 	}
66978eb3293SRoger Pau Monné 
6702602ef7cSRoger Pau Monné 	error = get_file_offset(priv_user, arg->count, &gmap->file_index);
6712602ef7cSRoger Pau Monné 	if (error != 0) {
6722602ef7cSRoger Pau Monné 		free(gmap->grant_map_ops, M_GNTDEV);
6732602ef7cSRoger Pau Monné 		free(gmap, M_GNTDEV);
6742602ef7cSRoger Pau Monné 		return (error);
6752602ef7cSRoger Pau Monné 	}
6762602ef7cSRoger Pau Monné 
67778eb3293SRoger Pau Monné 	mtx_lock(&priv_user->user_data_lock);
67878eb3293SRoger Pau Monné 	RB_INSERT(gmap_tree_head, &priv_user->gmap_tree, gmap);
67978eb3293SRoger Pau Monné 	mtx_unlock(&priv_user->user_data_lock);
68078eb3293SRoger Pau Monné 
68178eb3293SRoger Pau Monné 	arg->index = gmap->file_index;
68278eb3293SRoger Pau Monné 
68378eb3293SRoger Pau Monné 	return (error);
68478eb3293SRoger Pau Monné }
68578eb3293SRoger Pau Monné 
68678eb3293SRoger Pau Monné /*
68778eb3293SRoger Pau Monné  * IOCTL_GNTDEV_UNMAP_GRANT_REF
68878eb3293SRoger Pau Monné  * Remove the map information from the per user private data and add it
68978eb3293SRoger Pau Monné  * to the global device-list of mappings to be deleted. A reference to
69078eb3293SRoger Pau Monné  * the mgtdevice pager is also decreased, the reason for which is
69178eb3293SRoger Pau Monné  * explained in mmap_gmap().
69278eb3293SRoger Pau Monné  */
69378eb3293SRoger Pau Monné static int
gntdev_unmap_grant_ref(struct ioctl_gntdev_unmap_grant_ref * arg)69478eb3293SRoger Pau Monné gntdev_unmap_grant_ref(struct ioctl_gntdev_unmap_grant_ref *arg)
69578eb3293SRoger Pau Monné {
69678eb3293SRoger Pau Monné 	int error;
69778eb3293SRoger Pau Monné 	struct gntdev_gmap *gmap;
69878eb3293SRoger Pau Monné 	struct per_user_data *priv_user;
69978eb3293SRoger Pau Monné 
70078eb3293SRoger Pau Monné 	error = devfs_get_cdevpriv((void**) &priv_user);
70178eb3293SRoger Pau Monné 	if (error != 0)
70278eb3293SRoger Pau Monné 		return (EINVAL);
70378eb3293SRoger Pau Monné 
70478eb3293SRoger Pau Monné 	gmap = gntdev_find_gmap(priv_user, arg->index, arg->count);
70578eb3293SRoger Pau Monné 	if (gmap == NULL) {
70678eb3293SRoger Pau Monné 		log(LOG_ERR, "Can't find requested grant-map.");
70778eb3293SRoger Pau Monné 		return (EINVAL);
70878eb3293SRoger Pau Monné 	}
70978eb3293SRoger Pau Monné 
71078eb3293SRoger Pau Monné 	mtx_lock(&priv_user->user_data_lock);
71178eb3293SRoger Pau Monné 	mtx_lock(&cleanup_data.to_kill_gmaps_mtx);
71278eb3293SRoger Pau Monné 	RB_REMOVE(gmap_tree_head, &priv_user->gmap_tree, gmap);
71378eb3293SRoger Pau Monné 	STAILQ_INSERT_TAIL(&cleanup_data.to_kill_gmaps, gmap, gmap_next.list);
71478eb3293SRoger Pau Monné 	mtx_unlock(&cleanup_data.to_kill_gmaps_mtx);
71578eb3293SRoger Pau Monné 	mtx_unlock(&priv_user->user_data_lock);
71678eb3293SRoger Pau Monné 
71778eb3293SRoger Pau Monné 	if (gmap->map)
71878eb3293SRoger Pau Monné 		vm_object_deallocate(gmap->map->mem);
71978eb3293SRoger Pau Monné 
72078eb3293SRoger Pau Monné 	taskqueue_enqueue(taskqueue_thread, &cleanup_task);
72178eb3293SRoger Pau Monné 	put_file_offset(priv_user, arg->count, arg->index);
72278eb3293SRoger Pau Monné 
72378eb3293SRoger Pau Monné 	return (0);
72478eb3293SRoger Pau Monné }
72578eb3293SRoger Pau Monné 
72678eb3293SRoger Pau Monné /*
72778eb3293SRoger Pau Monné  * IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR
72878eb3293SRoger Pau Monné  * Get file-offset and count for a given mapping, from the virtual address
72978eb3293SRoger Pau Monné  * where the mapping is mmapped.
73078eb3293SRoger Pau Monné  * Please note, this only works for grants mapped by this domain, and not
73178eb3293SRoger Pau Monné  * grants allocated. Count doesn't make much sense in reference to grants
73278eb3293SRoger Pau Monné  * allocated. Also, because this function is present in the linux gntdev
73378eb3293SRoger Pau Monné  * device, but not in the linux gntalloc one, most userspace code only use
73478eb3293SRoger Pau Monné  * it for mapped grants.
73578eb3293SRoger Pau Monné  */
73678eb3293SRoger Pau Monné static int
gntdev_get_offset_for_vaddr(struct ioctl_gntdev_get_offset_for_vaddr * arg,struct thread * td)73778eb3293SRoger Pau Monné gntdev_get_offset_for_vaddr(struct ioctl_gntdev_get_offset_for_vaddr *arg,
73878eb3293SRoger Pau Monné 	struct thread *td)
73978eb3293SRoger Pau Monné {
74078eb3293SRoger Pau Monné 	int error;
74178eb3293SRoger Pau Monné 	vm_map_t map;
74278eb3293SRoger Pau Monné 	vm_map_entry_t entry;
74378eb3293SRoger Pau Monné 	vm_object_t mem;
74478eb3293SRoger Pau Monné 	vm_pindex_t pindex;
74578eb3293SRoger Pau Monné 	vm_prot_t prot;
74678eb3293SRoger Pau Monné 	boolean_t wired;
74778eb3293SRoger Pau Monné 	struct gntdev_gmap *gmap;
748b8aa60dbSRoger Pau Monné 	int rc;
74978eb3293SRoger Pau Monné 
75078eb3293SRoger Pau Monné 	map = &td->td_proc->p_vmspace->vm_map;
75178eb3293SRoger Pau Monné 	error = vm_map_lookup(&map, arg->vaddr, VM_PROT_NONE, &entry,
75278eb3293SRoger Pau Monné 		    &mem, &pindex, &prot, &wired);
75378eb3293SRoger Pau Monné 	if (error != KERN_SUCCESS)
75478eb3293SRoger Pau Monné 		return (EINVAL);
75578eb3293SRoger Pau Monné 
75678eb3293SRoger Pau Monné 	if ((mem->type != OBJT_MGTDEVICE) ||
757b8aa60dbSRoger Pau Monné 	    (mem->un_pager.devp.ops != &gntdev_gmap_pg_ops)) {
758b8aa60dbSRoger Pau Monné 		rc = EINVAL;
759b8aa60dbSRoger Pau Monné 		goto out;
760b8aa60dbSRoger Pau Monné 	}
76178eb3293SRoger Pau Monné 
76278eb3293SRoger Pau Monné 	gmap = mem->handle;
76378eb3293SRoger Pau Monné 	if (gmap == NULL ||
764b8aa60dbSRoger Pau Monné 	    (entry->end - entry->start) != (gmap->count * PAGE_SIZE)) {
765b8aa60dbSRoger Pau Monné 		rc = EINVAL;
766b8aa60dbSRoger Pau Monné 		goto out;
767b8aa60dbSRoger Pau Monné 	}
76878eb3293SRoger Pau Monné 
76978eb3293SRoger Pau Monné 	arg->count = gmap->count;
77078eb3293SRoger Pau Monné 	arg->offset = gmap->file_index;
771b8aa60dbSRoger Pau Monné 	rc = 0;
772b8aa60dbSRoger Pau Monné 
773b8aa60dbSRoger Pau Monné out:
774b8aa60dbSRoger Pau Monné 	vm_map_lookup_done(map, entry);
775b8aa60dbSRoger Pau Monné 	return (rc);
77678eb3293SRoger Pau Monné }
77778eb3293SRoger Pau Monné 
77878eb3293SRoger Pau Monné /*-------------------- Grant Mapping Pager  ----------------------------------*/
77978eb3293SRoger Pau Monné 
78078eb3293SRoger Pau Monné static int
gntdev_gmap_pg_ctor(void * handle,vm_ooffset_t size,vm_prot_t prot,vm_ooffset_t foff,struct ucred * cred,u_short * color)78178eb3293SRoger Pau Monné gntdev_gmap_pg_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot,
78278eb3293SRoger Pau Monné     vm_ooffset_t foff, struct ucred *cred, u_short *color)
78378eb3293SRoger Pau Monné {
78478eb3293SRoger Pau Monné 
78578eb3293SRoger Pau Monné 	return (0);
78678eb3293SRoger Pau Monné }
78778eb3293SRoger Pau Monné 
78878eb3293SRoger Pau Monné static void
gntdev_gmap_pg_dtor(void * handle)78978eb3293SRoger Pau Monné gntdev_gmap_pg_dtor(void *handle)
79078eb3293SRoger Pau Monné {
79178eb3293SRoger Pau Monné 
79278eb3293SRoger Pau Monné 	notify_unmap_cleanup((struct gntdev_gmap *)handle);
79378eb3293SRoger Pau Monné }
79478eb3293SRoger Pau Monné 
79578eb3293SRoger Pau Monné static int
gntdev_gmap_pg_fault(vm_object_t object,vm_ooffset_t offset,int prot,vm_page_t * mres)79678eb3293SRoger Pau Monné gntdev_gmap_pg_fault(vm_object_t object, vm_ooffset_t offset, int prot,
79778eb3293SRoger Pau Monné     vm_page_t *mres)
79878eb3293SRoger Pau Monné {
79978eb3293SRoger Pau Monné 	struct gntdev_gmap *gmap = object->handle;
80078eb3293SRoger Pau Monné 	vm_pindex_t pidx, ridx;
8013cf3b4e6SJeff Roberson 	vm_page_t page;
80278eb3293SRoger Pau Monné 	vm_ooffset_t relative_offset;
80378eb3293SRoger Pau Monné 
80478eb3293SRoger Pau Monné 	if (gmap->map == NULL)
80578eb3293SRoger Pau Monné 		return (VM_PAGER_FAIL);
80678eb3293SRoger Pau Monné 
80778eb3293SRoger Pau Monné 	relative_offset = offset - gmap->file_index;
80878eb3293SRoger Pau Monné 
80910d9120cSKonstantin Belousov 	pidx = OFF_TO_IDX(offset);
81010d9120cSKonstantin Belousov 	ridx = OFF_TO_IDX(relative_offset);
81178eb3293SRoger Pau Monné 	if (ridx >= gmap->count ||
81278eb3293SRoger Pau Monné 	    gmap->grant_map_ops[ridx].status != GNTST_okay)
81378eb3293SRoger Pau Monné 		return (VM_PAGER_FAIL);
81478eb3293SRoger Pau Monné 
81578eb3293SRoger Pau Monné 	page = PHYS_TO_VM_PAGE(gmap->map->phys_base_addr + relative_offset);
81678eb3293SRoger Pau Monné 	if (page == NULL)
81778eb3293SRoger Pau Monné 		return (VM_PAGER_FAIL);
81878eb3293SRoger Pau Monné 
81978eb3293SRoger Pau Monné 	KASSERT((page->flags & PG_FICTITIOUS) != 0,
82078eb3293SRoger Pau Monné 	    ("not fictitious %p", page));
821fee2a2faSMark Johnston 	KASSERT(vm_page_wired(page), ("page %p is not wired", page));
822fee2a2faSMark Johnston 	KASSERT(!vm_page_busied(page), ("page %p is busy", page));
82378eb3293SRoger Pau Monné 
82463e97555SJeff Roberson 	vm_page_busy_acquire(page, 0);
8250012f373SJeff Roberson 	vm_page_valid(page);
8263cf3b4e6SJeff Roberson 	if (*mres != NULL)
8273cf3b4e6SJeff Roberson 		vm_page_replace(page, object, pidx, *mres);
8283cf3b4e6SJeff Roberson 	else
82978eb3293SRoger Pau Monné 		vm_page_insert(page, object, pidx);
83078eb3293SRoger Pau Monné 	*mres = page;
83178eb3293SRoger Pau Monné 	return (VM_PAGER_OK);
83278eb3293SRoger Pau Monné }
83378eb3293SRoger Pau Monné 
83478eb3293SRoger Pau Monné /*------------------ Grant Table Methods  ------------------------------------*/
83578eb3293SRoger Pau Monné 
83678eb3293SRoger Pau Monné static void
notify(struct notify_data * notify,vm_page_t page)83778eb3293SRoger Pau Monné notify(struct notify_data *notify, vm_page_t page)
83878eb3293SRoger Pau Monné {
83978eb3293SRoger Pau Monné 	if (notify->action & UNMAP_NOTIFY_CLEAR_BYTE) {
84078eb3293SRoger Pau Monné 		uint8_t *mem;
84178eb3293SRoger Pau Monné 		uint64_t offset;
84278eb3293SRoger Pau Monné 
84378eb3293SRoger Pau Monné 		offset = notify->index & PAGE_MASK;
84478eb3293SRoger Pau Monné 		mem = (uint8_t *)pmap_quick_enter_page(page);
84578eb3293SRoger Pau Monné 		mem[offset] = 0;
84678eb3293SRoger Pau Monné 		pmap_quick_remove_page((vm_offset_t)mem);
84778eb3293SRoger Pau Monné 	}
84878eb3293SRoger Pau Monné 	if (notify->action & UNMAP_NOTIFY_SEND_EVENT) {
84978eb3293SRoger Pau Monné 		xen_intr_signal(notify->notify_evtchn_handle);
85078eb3293SRoger Pau Monné 		xen_intr_unbind(&notify->notify_evtchn_handle);
85178eb3293SRoger Pau Monné 	}
85278eb3293SRoger Pau Monné 	notify->action = 0;
85378eb3293SRoger Pau Monné }
85478eb3293SRoger Pau Monné 
85578eb3293SRoger Pau Monné /*
85678eb3293SRoger Pau Monné  * Helper to copy new arguments from the notify ioctl into
85778eb3293SRoger Pau Monné  * the existing notify data.
85878eb3293SRoger Pau Monné  */
85978eb3293SRoger Pau Monné static int
copy_notify_helper(struct notify_data * destination,struct ioctl_gntdev_unmap_notify * source)86078eb3293SRoger Pau Monné copy_notify_helper(struct notify_data *destination,
86178eb3293SRoger Pau Monné     struct ioctl_gntdev_unmap_notify *source)
86278eb3293SRoger Pau Monné {
86378eb3293SRoger Pau Monné 	xen_intr_handle_t handlep = NULL;
86478eb3293SRoger Pau Monné 
86578eb3293SRoger Pau Monné 	/*
86678eb3293SRoger Pau Monné 	 * "Get" before "Put"ting previous reference, as we might be
86778eb3293SRoger Pau Monné 	 * holding the last reference to the event channel port.
86878eb3293SRoger Pau Monné 	 */
86978eb3293SRoger Pau Monné 	if (source->action & UNMAP_NOTIFY_SEND_EVENT)
87078eb3293SRoger Pau Monné 		if (xen_intr_get_evtchn_from_port(source->event_channel_port,
87178eb3293SRoger Pau Monné 		    &handlep) != 0)
87278eb3293SRoger Pau Monné 			return (EINVAL);
87378eb3293SRoger Pau Monné 
87478eb3293SRoger Pau Monné 	if (destination->action & UNMAP_NOTIFY_SEND_EVENT)
87578eb3293SRoger Pau Monné 		xen_intr_unbind(&destination->notify_evtchn_handle);
87678eb3293SRoger Pau Monné 
87778eb3293SRoger Pau Monné 	destination->action = source->action;
87878eb3293SRoger Pau Monné 	destination->event_channel_port = source->event_channel_port;
87978eb3293SRoger Pau Monné 	destination->index = source->index;
88078eb3293SRoger Pau Monné 	destination->notify_evtchn_handle = handlep;
88178eb3293SRoger Pau Monné 
88278eb3293SRoger Pau Monné 	return (0);
88378eb3293SRoger Pau Monné }
88478eb3293SRoger Pau Monné 
88578eb3293SRoger Pau Monné /*
88678eb3293SRoger Pau Monné  * IOCTL_GNTDEV_SET_UNMAP_NOTIFY
88778eb3293SRoger Pau Monné  * Set unmap notification inside the appropriate grant. It sends a
88878eb3293SRoger Pau Monné  * notification when the grant is completely munmapped by this domain
88978eb3293SRoger Pau Monné  * and ready for destruction.
89078eb3293SRoger Pau Monné  */
89178eb3293SRoger Pau Monné static int
gntdev_set_unmap_notify(struct ioctl_gntdev_unmap_notify * arg)89278eb3293SRoger Pau Monné gntdev_set_unmap_notify(struct ioctl_gntdev_unmap_notify *arg)
89378eb3293SRoger Pau Monné {
89478eb3293SRoger Pau Monné 	int error;
89578eb3293SRoger Pau Monné 	uint64_t index;
89678eb3293SRoger Pau Monné 	struct per_user_data *priv_user;
89778eb3293SRoger Pau Monné 	struct gntdev_gref *gref = NULL;
89878eb3293SRoger Pau Monné 	struct gntdev_gmap *gmap;
89978eb3293SRoger Pau Monné 
90078eb3293SRoger Pau Monné 	error = devfs_get_cdevpriv((void**) &priv_user);
90178eb3293SRoger Pau Monné 	if (error != 0)
90278eb3293SRoger Pau Monné 		return (EINVAL);
90378eb3293SRoger Pau Monné 
90478eb3293SRoger Pau Monné 	if (arg->action & ~(UNMAP_NOTIFY_CLEAR_BYTE|UNMAP_NOTIFY_SEND_EVENT))
90578eb3293SRoger Pau Monné 		return (EINVAL);
90678eb3293SRoger Pau Monné 
90778eb3293SRoger Pau Monné 	index = arg->index & ~PAGE_MASK;
90878eb3293SRoger Pau Monné 	gref = gntdev_find_grefs(priv_user, index, 1);
90978eb3293SRoger Pau Monné 	if (gref) {
91078eb3293SRoger Pau Monné 		if (gref->notify == NULL)
91178eb3293SRoger Pau Monné 			gref->notify = malloc(sizeof(*arg), M_GNTDEV,
91278eb3293SRoger Pau Monné 			    M_WAITOK | M_ZERO);
91378eb3293SRoger Pau Monné 		return (copy_notify_helper(gref->notify, arg));
91478eb3293SRoger Pau Monné 	}
91578eb3293SRoger Pau Monné 
91678eb3293SRoger Pau Monné 	error = EINVAL;
91778eb3293SRoger Pau Monné 	mtx_lock(&priv_user->user_data_lock);
91878eb3293SRoger Pau Monné 	RB_FOREACH(gmap, gmap_tree_head, &priv_user->gmap_tree) {
91978eb3293SRoger Pau Monné 		if (arg->index >= gmap->file_index &&
92078eb3293SRoger Pau Monné 		    arg->index < gmap->file_index + gmap->count * PAGE_SIZE) {
92178eb3293SRoger Pau Monné 			if (gmap->notify == NULL)
92278eb3293SRoger Pau Monné 				gmap->notify = malloc(sizeof(*arg), M_GNTDEV,
92378eb3293SRoger Pau Monné 				    M_WAITOK | M_ZERO);
92478eb3293SRoger Pau Monné 			error = copy_notify_helper(gmap->notify, arg);
92578eb3293SRoger Pau Monné 			break;
92678eb3293SRoger Pau Monné 		}
92778eb3293SRoger Pau Monné 	}
92878eb3293SRoger Pau Monné 	mtx_unlock(&priv_user->user_data_lock);
92978eb3293SRoger Pau Monné 
93078eb3293SRoger Pau Monné 	return (error);
93178eb3293SRoger Pau Monné }
93278eb3293SRoger Pau Monné 
93378eb3293SRoger Pau Monné /*------------------ Gntdev Char Device Methods  -----------------------------*/
93478eb3293SRoger Pau Monné 
93578eb3293SRoger Pau Monné static void
cleanup_function(void * arg,__unused int pending)93678eb3293SRoger Pau Monné cleanup_function(void *arg, __unused int pending)
93778eb3293SRoger Pau Monné {
93878eb3293SRoger Pau Monné 
93978eb3293SRoger Pau Monné 	gref_list_dtor((struct cleanup_data_struct *) arg);
94078eb3293SRoger Pau Monné 	gmap_list_dtor((struct cleanup_data_struct *) arg);
94178eb3293SRoger Pau Monné }
94278eb3293SRoger Pau Monné 
94378eb3293SRoger Pau Monné static void
per_user_data_dtor(void * arg)94478eb3293SRoger Pau Monné per_user_data_dtor(void *arg)
94578eb3293SRoger Pau Monné {
94678eb3293SRoger Pau Monné 	struct gntdev_gref *gref, *gref_tmp;
94778eb3293SRoger Pau Monné 	struct gntdev_gmap *gmap, *gmap_tmp;
94878eb3293SRoger Pau Monné 	struct file_offset_struct *offset, *offset_tmp;
94978eb3293SRoger Pau Monné 	struct per_user_data *priv_user;
95078eb3293SRoger Pau Monné 
95178eb3293SRoger Pau Monné 	priv_user = (struct per_user_data *) arg;
95278eb3293SRoger Pau Monné 
95378eb3293SRoger Pau Monné 	mtx_lock(&priv_user->user_data_lock);
95478eb3293SRoger Pau Monné 
95578eb3293SRoger Pau Monné 	mtx_lock(&cleanup_data.to_kill_grefs_mtx);
95678eb3293SRoger Pau Monné 	RB_FOREACH_SAFE(gref, gref_tree_head, &priv_user->gref_tree, gref_tmp) {
95778eb3293SRoger Pau Monné 		RB_REMOVE(gref_tree_head, &priv_user->gref_tree, gref);
95878eb3293SRoger Pau Monné 		STAILQ_INSERT_TAIL(&cleanup_data.to_kill_grefs, gref,
95978eb3293SRoger Pau Monné 		    gref_next.list);
96078eb3293SRoger Pau Monné 	}
96178eb3293SRoger Pau Monné 	mtx_unlock(&cleanup_data.to_kill_grefs_mtx);
96278eb3293SRoger Pau Monné 
96378eb3293SRoger Pau Monné 	mtx_lock(&cleanup_data.to_kill_gmaps_mtx);
96478eb3293SRoger Pau Monné 	RB_FOREACH_SAFE(gmap, gmap_tree_head, &priv_user->gmap_tree, gmap_tmp) {
96578eb3293SRoger Pau Monné 		RB_REMOVE(gmap_tree_head, &priv_user->gmap_tree, gmap);
96678eb3293SRoger Pau Monné 		STAILQ_INSERT_TAIL(&cleanup_data.to_kill_gmaps, gmap,
96778eb3293SRoger Pau Monné 		    gmap_next.list);
96878eb3293SRoger Pau Monné 		if (gmap->map)
96978eb3293SRoger Pau Monné 			vm_object_deallocate(gmap->map->mem);
97078eb3293SRoger Pau Monné 	}
97178eb3293SRoger Pau Monné 	mtx_unlock(&cleanup_data.to_kill_gmaps_mtx);
97278eb3293SRoger Pau Monné 
97378eb3293SRoger Pau Monné 	RB_FOREACH_SAFE(offset, file_offset_head, &priv_user->file_offset,
97478eb3293SRoger Pau Monné 	    offset_tmp) {
97578eb3293SRoger Pau Monné 		RB_REMOVE(file_offset_head, &priv_user->file_offset, offset);
97678eb3293SRoger Pau Monné 		free(offset, M_GNTDEV);
97778eb3293SRoger Pau Monné 	}
97878eb3293SRoger Pau Monné 
97978eb3293SRoger Pau Monné 	mtx_unlock(&priv_user->user_data_lock);
98078eb3293SRoger Pau Monné 
98178eb3293SRoger Pau Monné 	taskqueue_enqueue(taskqueue_thread, &cleanup_task);
98278eb3293SRoger Pau Monné 
98378eb3293SRoger Pau Monné 	mtx_destroy(&priv_user->user_data_lock);
98478eb3293SRoger Pau Monné 	free(priv_user, M_GNTDEV);
98578eb3293SRoger Pau Monné }
98678eb3293SRoger Pau Monné 
98778eb3293SRoger Pau Monné static int
gntdev_open(struct cdev * dev,int flag,int otyp,struct thread * td)98878eb3293SRoger Pau Monné gntdev_open(struct cdev *dev, int flag, int otyp, struct thread *td)
98978eb3293SRoger Pau Monné {
99078eb3293SRoger Pau Monné 	int error;
99178eb3293SRoger Pau Monné 	struct per_user_data *priv_user;
99278eb3293SRoger Pau Monné 	struct file_offset_struct *offset;
99378eb3293SRoger Pau Monné 
99478eb3293SRoger Pau Monné 	priv_user = malloc(sizeof(*priv_user), M_GNTDEV, M_WAITOK | M_ZERO);
99578eb3293SRoger Pau Monné 	RB_INIT(&priv_user->gref_tree);
99678eb3293SRoger Pau Monné 	RB_INIT(&priv_user->gmap_tree);
99778eb3293SRoger Pau Monné 	RB_INIT(&priv_user->file_offset);
99878eb3293SRoger Pau Monné 	offset = malloc(sizeof(*offset), M_GNTDEV, M_WAITOK | M_ZERO);
99978eb3293SRoger Pau Monné 	offset->file_offset = 0;
100078eb3293SRoger Pau Monné 	offset->count = MAX_OFFSET_COUNT;
100178eb3293SRoger Pau Monné 	RB_INSERT(file_offset_head, &priv_user->file_offset, offset);
100278eb3293SRoger Pau Monné 	mtx_init(&priv_user->user_data_lock,
100378eb3293SRoger Pau Monné 	    "per user data mutex", NULL, MTX_DEF);
100478eb3293SRoger Pau Monné 
100578eb3293SRoger Pau Monné 	error = devfs_set_cdevpriv(priv_user, per_user_data_dtor);
100678eb3293SRoger Pau Monné 	if (error != 0)
100778eb3293SRoger Pau Monné 		per_user_data_dtor(priv_user);
100878eb3293SRoger Pau Monné 
100978eb3293SRoger Pau Monné 	return (error);
101078eb3293SRoger Pau Monné }
101178eb3293SRoger Pau Monné 
101278eb3293SRoger Pau Monné static int
gntdev_ioctl(struct cdev * dev,u_long cmd,caddr_t data,int fflag,struct thread * td)101378eb3293SRoger Pau Monné gntdev_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
101478eb3293SRoger Pau Monné 	int fflag, struct thread *td)
101578eb3293SRoger Pau Monné {
101678eb3293SRoger Pau Monné 	int error;
101778eb3293SRoger Pau Monné 
101878eb3293SRoger Pau Monné 	switch (cmd) {
101978eb3293SRoger Pau Monné 	case IOCTL_GNTDEV_SET_UNMAP_NOTIFY:
102078eb3293SRoger Pau Monné 		error = gntdev_set_unmap_notify(
102178eb3293SRoger Pau Monné 		    (struct ioctl_gntdev_unmap_notify*) data);
102278eb3293SRoger Pau Monné 		break;
102378eb3293SRoger Pau Monné 	case IOCTL_GNTDEV_ALLOC_GREF:
102478eb3293SRoger Pau Monné 		error = gntdev_alloc_gref(
102578eb3293SRoger Pau Monné 		    (struct ioctl_gntdev_alloc_gref*) data);
102678eb3293SRoger Pau Monné 		break;
102778eb3293SRoger Pau Monné 	case IOCTL_GNTDEV_DEALLOC_GREF:
102878eb3293SRoger Pau Monné 		error = gntdev_dealloc_gref(
102978eb3293SRoger Pau Monné 		    (struct ioctl_gntdev_dealloc_gref*) data);
103078eb3293SRoger Pau Monné 		break;
103178eb3293SRoger Pau Monné 	case IOCTL_GNTDEV_MAP_GRANT_REF:
103278eb3293SRoger Pau Monné 		error = gntdev_map_grant_ref(
103378eb3293SRoger Pau Monné 		    (struct ioctl_gntdev_map_grant_ref*) data);
103478eb3293SRoger Pau Monné 		break;
103578eb3293SRoger Pau Monné 	case IOCTL_GNTDEV_UNMAP_GRANT_REF:
103678eb3293SRoger Pau Monné 		error = gntdev_unmap_grant_ref(
103778eb3293SRoger Pau Monné 		    (struct ioctl_gntdev_unmap_grant_ref*) data);
103878eb3293SRoger Pau Monné 		break;
103978eb3293SRoger Pau Monné 	case IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR:
104078eb3293SRoger Pau Monné 		error = gntdev_get_offset_for_vaddr(
104178eb3293SRoger Pau Monné 		    (struct ioctl_gntdev_get_offset_for_vaddr*) data, td);
104278eb3293SRoger Pau Monné 		break;
104378eb3293SRoger Pau Monné 	default:
104478eb3293SRoger Pau Monné 		error = ENOSYS;
104578eb3293SRoger Pau Monné 		break;
104678eb3293SRoger Pau Monné 	}
104778eb3293SRoger Pau Monné 
104878eb3293SRoger Pau Monné 	return (error);
104978eb3293SRoger Pau Monné }
105078eb3293SRoger Pau Monné 
105178eb3293SRoger Pau Monné /*
105278eb3293SRoger Pau Monné  * MMAP an allocated grant into user memory.
105378eb3293SRoger Pau Monné  * Please note, that the grants must not already be mmapped, otherwise
105478eb3293SRoger Pau Monné  * this function will fail.
105578eb3293SRoger Pau Monné  */
105678eb3293SRoger Pau Monné static int
mmap_gref(struct per_user_data * priv_user,struct gntdev_gref * gref_start,uint32_t count,vm_size_t size,struct vm_object ** object)105778eb3293SRoger Pau Monné mmap_gref(struct per_user_data *priv_user, struct gntdev_gref *gref_start,
105878eb3293SRoger Pau Monné     uint32_t count, vm_size_t size, struct vm_object **object)
105978eb3293SRoger Pau Monné {
106078eb3293SRoger Pau Monné 	vm_object_t mem_obj;
106178eb3293SRoger Pau Monné 	struct gntdev_gref *gref;
106278eb3293SRoger Pau Monné 
1063fbf2a778SKonstantin Belousov 	mem_obj = vm_pager_allocate(OBJT_PHYS, NULL, size, VM_PROT_ALL, 0,
1064fbf2a778SKonstantin Belousov 	    curthread->td_ucred);
106578eb3293SRoger Pau Monné 	if (mem_obj == NULL)
106678eb3293SRoger Pau Monné 		return (ENOMEM);
106778eb3293SRoger Pau Monné 
106878eb3293SRoger Pau Monné 	mtx_lock(&priv_user->user_data_lock);
106978eb3293SRoger Pau Monné 	VM_OBJECT_WLOCK(mem_obj);
107078eb3293SRoger Pau Monné 	for (gref = gref_start; gref != NULL && count > 0; gref =
107178eb3293SRoger Pau Monné 	    RB_NEXT(gref_tree_head, &priv_user->gref_tree, gref)) {
107278eb3293SRoger Pau Monné 		if (gref->page->object)
107378eb3293SRoger Pau Monné 			break;
107478eb3293SRoger Pau Monné 
107578eb3293SRoger Pau Monné 		vm_page_insert(gref->page, mem_obj,
107610d9120cSKonstantin Belousov 		    OFF_TO_IDX(gref->file_index));
107778eb3293SRoger Pau Monné 
107878eb3293SRoger Pau Monné 		count--;
107978eb3293SRoger Pau Monné 	}
108078eb3293SRoger Pau Monné 	VM_OBJECT_WUNLOCK(mem_obj);
108178eb3293SRoger Pau Monné 	mtx_unlock(&priv_user->user_data_lock);
108278eb3293SRoger Pau Monné 
108378eb3293SRoger Pau Monné 	if (count) {
108478eb3293SRoger Pau Monné 		vm_object_deallocate(mem_obj);
108578eb3293SRoger Pau Monné 		return (EINVAL);
108678eb3293SRoger Pau Monné 	}
108778eb3293SRoger Pau Monné 
108878eb3293SRoger Pau Monné 	*object = mem_obj;
108978eb3293SRoger Pau Monné 
109078eb3293SRoger Pau Monné 	return (0);
109178eb3293SRoger Pau Monné 
109278eb3293SRoger Pau Monné }
109378eb3293SRoger Pau Monné 
109478eb3293SRoger Pau Monné /*
109578eb3293SRoger Pau Monné  * MMAP a mapped grant into user memory.
109678eb3293SRoger Pau Monné  */
109778eb3293SRoger Pau Monné static int
mmap_gmap(struct per_user_data * priv_user,struct gntdev_gmap * gmap_start,vm_ooffset_t * offset,vm_size_t size,struct vm_object ** object,int nprot)109878eb3293SRoger Pau Monné mmap_gmap(struct per_user_data *priv_user, struct gntdev_gmap *gmap_start,
109978eb3293SRoger Pau Monné     vm_ooffset_t *offset, vm_size_t size, struct vm_object **object, int nprot)
110078eb3293SRoger Pau Monné {
110178eb3293SRoger Pau Monné 	uint32_t i;
110278eb3293SRoger Pau Monné 	int error;
110378eb3293SRoger Pau Monné 
110478eb3293SRoger Pau Monné 	/*
110578eb3293SRoger Pau Monné 	 * The grant map hypercall might already be done.
110678eb3293SRoger Pau Monné 	 * If that is the case, increase a reference to the
110778eb3293SRoger Pau Monné 	 * vm object and return the already allocated object.
110878eb3293SRoger Pau Monné 	 */
110978eb3293SRoger Pau Monné 	if (gmap_start->map) {
111078eb3293SRoger Pau Monné 		vm_object_reference(gmap_start->map->mem);
111178eb3293SRoger Pau Monné 		*object = gmap_start->map->mem;
111278eb3293SRoger Pau Monné 		return (0);
111378eb3293SRoger Pau Monné 	}
111478eb3293SRoger Pau Monné 
111578eb3293SRoger Pau Monné 	gmap_start->map = malloc(sizeof(*(gmap_start->map)), M_GNTDEV,
111678eb3293SRoger Pau Monné 	    M_WAITOK | M_ZERO);
111778eb3293SRoger Pau Monné 
111878eb3293SRoger Pau Monné 	/* Allocate the xen pseudo physical memory resource. */
111978eb3293SRoger Pau Monné 	gmap_start->map->pseudo_phys_res_id = 0;
112078eb3293SRoger Pau Monné 	gmap_start->map->pseudo_phys_res = xenmem_alloc(gntdev_dev,
112178eb3293SRoger Pau Monné 	    &gmap_start->map->pseudo_phys_res_id, size);
112278eb3293SRoger Pau Monné 	if (gmap_start->map->pseudo_phys_res == NULL) {
112378eb3293SRoger Pau Monné 		free(gmap_start->map, M_GNTDEV);
112478eb3293SRoger Pau Monné 		gmap_start->map = NULL;
112578eb3293SRoger Pau Monné 		return (ENOMEM);
112678eb3293SRoger Pau Monné 	}
112778eb3293SRoger Pau Monné 	gmap_start->map->phys_base_addr =
112878eb3293SRoger Pau Monné 	    rman_get_start(gmap_start->map->pseudo_phys_res);
112978eb3293SRoger Pau Monné 
113078eb3293SRoger Pau Monné 	/* Allocate the mgtdevice pager. */
113178eb3293SRoger Pau Monné 	gmap_start->map->mem = cdev_pager_allocate(gmap_start, OBJT_MGTDEVICE,
113278eb3293SRoger Pau Monné 	    &gntdev_gmap_pg_ops, size, nprot, *offset, NULL);
113378eb3293SRoger Pau Monné 	if (gmap_start->map->mem == NULL) {
113478eb3293SRoger Pau Monné 		xenmem_free(gntdev_dev, gmap_start->map->pseudo_phys_res_id,
113578eb3293SRoger Pau Monné 		    gmap_start->map->pseudo_phys_res);
113678eb3293SRoger Pau Monné 		free(gmap_start->map, M_GNTDEV);
113778eb3293SRoger Pau Monné 		gmap_start->map = NULL;
113878eb3293SRoger Pau Monné 		return (ENOMEM);
113978eb3293SRoger Pau Monné 	}
114078eb3293SRoger Pau Monné 
114178eb3293SRoger Pau Monné 	for (i = 0; i < gmap_start->count; i++) {
114278eb3293SRoger Pau Monné 		gmap_start->grant_map_ops[i].host_addr =
114378eb3293SRoger Pau Monné 		    gmap_start->map->phys_base_addr + i * PAGE_SIZE;
114478eb3293SRoger Pau Monné 
114578eb3293SRoger Pau Monné 		if ((nprot & PROT_WRITE) == 0)
114678eb3293SRoger Pau Monné 			gmap_start->grant_map_ops[i].flags |= GNTMAP_readonly;
114778eb3293SRoger Pau Monné 	}
114878eb3293SRoger Pau Monné 	/* Make the MAP hypercall. */
114978eb3293SRoger Pau Monné 	error = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref,
115078eb3293SRoger Pau Monné 	    gmap_start->grant_map_ops, gmap_start->count);
115178eb3293SRoger Pau Monné 	if (error != 0) {
115278eb3293SRoger Pau Monné 		/*
115378eb3293SRoger Pau Monné 		 * Deallocate pager.
115478eb3293SRoger Pau Monné 		 * Pager deallocation will automatically take care of
115578eb3293SRoger Pau Monné 		 * xenmem deallocation, etc.
115678eb3293SRoger Pau Monné 		 */
115778eb3293SRoger Pau Monné 		vm_object_deallocate(gmap_start->map->mem);
115878eb3293SRoger Pau Monné 
115978eb3293SRoger Pau Monné 		return (EINVAL);
116078eb3293SRoger Pau Monné 	}
116178eb3293SRoger Pau Monné 
116278eb3293SRoger Pau Monné 	/* Retry EAGAIN maps. */
116378eb3293SRoger Pau Monné 	for (i = 0; i < gmap_start->count; i++) {
116478eb3293SRoger Pau Monné 		int delay = 1;
116578eb3293SRoger Pau Monné 		while (delay < 256 &&
116678eb3293SRoger Pau Monné 		    gmap_start->grant_map_ops[i].status == GNTST_eagain) {
116778eb3293SRoger Pau Monné 			HYPERVISOR_grant_table_op( GNTTABOP_map_grant_ref,
116878eb3293SRoger Pau Monné 			    &gmap_start->grant_map_ops[i], 1);
116978eb3293SRoger Pau Monné 			pause(("gntmap"), delay * SBT_1MS);
117078eb3293SRoger Pau Monné 			delay++;
117178eb3293SRoger Pau Monné 		}
117278eb3293SRoger Pau Monné 		if (gmap_start->grant_map_ops[i].status == GNTST_eagain)
117378eb3293SRoger Pau Monné 			gmap_start->grant_map_ops[i].status = GNTST_bad_page;
117478eb3293SRoger Pau Monné 
117578eb3293SRoger Pau Monné 		if (gmap_start->grant_map_ops[i].status != GNTST_okay) {
117678eb3293SRoger Pau Monné 			/*
117778eb3293SRoger Pau Monné 			 * Deallocate pager.
117878eb3293SRoger Pau Monné 			 * Pager deallocation will automatically take care of
117978eb3293SRoger Pau Monné 			 * xenmem deallocation, notification, unmap hypercall,
118078eb3293SRoger Pau Monné 			 * etc.
118178eb3293SRoger Pau Monné 			 */
118278eb3293SRoger Pau Monné 			vm_object_deallocate(gmap_start->map->mem);
118378eb3293SRoger Pau Monné 
118478eb3293SRoger Pau Monné 			return (EINVAL);
118578eb3293SRoger Pau Monné 		}
118678eb3293SRoger Pau Monné 	}
118778eb3293SRoger Pau Monné 
118878eb3293SRoger Pau Monné 	/*
118978eb3293SRoger Pau Monné 	 * Add a reference to the vm object. We do not want
119078eb3293SRoger Pau Monné 	 * the vm object to be deleted when all the mmaps are
119178eb3293SRoger Pau Monné 	 * unmapped, because it may be re-mmapped. Instead,
119278eb3293SRoger Pau Monné 	 * we want the object to be deleted, when along with
119378eb3293SRoger Pau Monné 	 * munmaps, we have also processed the unmap-ioctl.
119478eb3293SRoger Pau Monné 	 */
119578eb3293SRoger Pau Monné 	vm_object_reference(gmap_start->map->mem);
119678eb3293SRoger Pau Monné 
119778eb3293SRoger Pau Monné 	*object = gmap_start->map->mem;
119878eb3293SRoger Pau Monné 
119978eb3293SRoger Pau Monné 	return (0);
120078eb3293SRoger Pau Monné }
120178eb3293SRoger Pau Monné 
120278eb3293SRoger Pau Monné static int
gntdev_mmap_single(struct cdev * cdev,vm_ooffset_t * offset,vm_size_t size,struct vm_object ** object,int nprot)120378eb3293SRoger Pau Monné gntdev_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t size,
120478eb3293SRoger Pau Monné     struct vm_object **object, int nprot)
120578eb3293SRoger Pau Monné {
120678eb3293SRoger Pau Monné 	int error;
120778eb3293SRoger Pau Monné 	uint32_t count;
120878eb3293SRoger Pau Monné 	struct gntdev_gref *gref_start;
120978eb3293SRoger Pau Monné 	struct gntdev_gmap *gmap_start;
121078eb3293SRoger Pau Monné 	struct per_user_data *priv_user;
121178eb3293SRoger Pau Monné 
121278eb3293SRoger Pau Monné 	error = devfs_get_cdevpriv((void**) &priv_user);
121378eb3293SRoger Pau Monné 	if (error != 0)
121478eb3293SRoger Pau Monné 		return (EINVAL);
121578eb3293SRoger Pau Monné 
121610d9120cSKonstantin Belousov 	count = OFF_TO_IDX(size);
121778eb3293SRoger Pau Monné 
121878eb3293SRoger Pau Monné 	gref_start = gntdev_find_grefs(priv_user, *offset, count);
121978eb3293SRoger Pau Monné 	if (gref_start) {
122078eb3293SRoger Pau Monné 		error = mmap_gref(priv_user, gref_start, count, size, object);
122178eb3293SRoger Pau Monné 		return (error);
122278eb3293SRoger Pau Monné 	}
122378eb3293SRoger Pau Monné 
122478eb3293SRoger Pau Monné 	gmap_start = gntdev_find_gmap(priv_user, *offset, count);
122578eb3293SRoger Pau Monné 	if (gmap_start) {
122678eb3293SRoger Pau Monné 		error = mmap_gmap(priv_user, gmap_start, offset, size, object,
122778eb3293SRoger Pau Monné 		    nprot);
122878eb3293SRoger Pau Monné 		return (error);
122978eb3293SRoger Pau Monné 	}
123078eb3293SRoger Pau Monné 
123178eb3293SRoger Pau Monné 	return (EINVAL);
123278eb3293SRoger Pau Monné }
123378eb3293SRoger Pau Monné 
123478eb3293SRoger Pau Monné /*------------------ Private Device Attachment Functions  --------------------*/
123578eb3293SRoger Pau Monné static void
gntdev_identify(driver_t * driver,device_t parent)123678eb3293SRoger Pau Monné gntdev_identify(driver_t *driver, device_t parent)
123778eb3293SRoger Pau Monné {
123878eb3293SRoger Pau Monné 
123978eb3293SRoger Pau Monné 	KASSERT((xen_domain()),
124078eb3293SRoger Pau Monné 	    ("Trying to attach gntdev device on non Xen domain"));
124178eb3293SRoger Pau Monné 
124278eb3293SRoger Pau Monné 	if (BUS_ADD_CHILD(parent, 0, "gntdev", 0) == NULL)
124378eb3293SRoger Pau Monné 		panic("unable to attach gntdev user-space device");
124478eb3293SRoger Pau Monné }
124578eb3293SRoger Pau Monné 
124678eb3293SRoger Pau Monné static int
gntdev_probe(device_t dev)124778eb3293SRoger Pau Monné gntdev_probe(device_t dev)
124878eb3293SRoger Pau Monné {
124978eb3293SRoger Pau Monné 
125078eb3293SRoger Pau Monné 	gntdev_dev = dev;
125178eb3293SRoger Pau Monné 	device_set_desc(dev, "Xen grant-table user-space device");
125278eb3293SRoger Pau Monné 	return (BUS_PROBE_NOWILDCARD);
125378eb3293SRoger Pau Monné }
125478eb3293SRoger Pau Monné 
125578eb3293SRoger Pau Monné static int
gntdev_attach(device_t dev)125678eb3293SRoger Pau Monné gntdev_attach(device_t dev)
125778eb3293SRoger Pau Monné {
125878eb3293SRoger Pau Monné 
125978eb3293SRoger Pau Monné 	make_dev_credf(MAKEDEV_ETERNAL, &gntdev_devsw, 0, NULL, UID_ROOT,
126078eb3293SRoger Pau Monné 	    GID_WHEEL, 0600, "xen/gntdev");
126178eb3293SRoger Pau Monné 	return (0);
126278eb3293SRoger Pau Monné }
126378eb3293SRoger Pau Monné 
126478eb3293SRoger Pau Monné /*-------------------- Private Device Attachment Data  -----------------------*/
126578eb3293SRoger Pau Monné static device_method_t gntdev_methods[] = {
126678eb3293SRoger Pau Monné 	DEVMETHOD(device_identify, gntdev_identify),
126778eb3293SRoger Pau Monné 	DEVMETHOD(device_probe, gntdev_probe),
126878eb3293SRoger Pau Monné 	DEVMETHOD(device_attach, gntdev_attach),
126978eb3293SRoger Pau Monné 	DEVMETHOD_END
127078eb3293SRoger Pau Monné };
127178eb3293SRoger Pau Monné 
127278eb3293SRoger Pau Monné static driver_t gntdev_driver = {
127378eb3293SRoger Pau Monné 	"gntdev",
127478eb3293SRoger Pau Monné 	gntdev_methods,
127578eb3293SRoger Pau Monné 	0,
127678eb3293SRoger Pau Monné };
127778eb3293SRoger Pau Monné 
1278f929eb1eSJohn Baldwin DRIVER_MODULE(gntdev, xenpv, gntdev_driver, 0, 0);
127978eb3293SRoger Pau Monné MODULE_DEPEND(gntdev, xenpv, 1, 1, 1);
1280