/* * Copyright (c) 2007-2008, D G Murray * Copyright (c) 2016-2017, Akshay Jaggi * * 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, see . * * Split out from linux.c */ #include #include #include #include #include #include #include #include #include #include "private.h" #define PAGE_SHIFT 12 #define PAGE_SIZE (1UL << PAGE_SHIFT) #define PAGE_MASK (~(PAGE_SIZE-1)) #define DEVXEN "/dev/xen/gntdev" int osdep_gnttab_open(xengnttab_handle *xgt) { int fd = open(DEVXEN, O_RDWR|O_CLOEXEC); if ( fd == -1 ) return -1; xgt->fd = fd; return 0; } int osdep_gnttab_close(xengnttab_handle *xgt) { if ( xgt->fd == -1 ) return 0; return close(xgt->fd); } int osdep_gnttab_set_max_grants(xengnttab_handle *xgt, uint32_t count) { return 0; } void *osdep_gnttab_grant_map(xengnttab_handle *xgt, uint32_t count, int flags, int prot, uint32_t *domids, uint32_t *refs, uint32_t notify_offset, evtchn_port_t notify_port) { uint32_t i; int fd = xgt->fd; struct ioctl_gntdev_map_grant_ref map; void *addr = NULL; int domids_stride; unsigned int refs_size = ROUNDUP(count * sizeof(struct ioctl_gntdev_grant_ref), PAGE_SHIFT); domids_stride = (flags & XENGNTTAB_GRANT_MAP_SINGLE_DOMAIN) ? 0 : 1; if ( refs_size <= PAGE_SIZE ) map.refs = malloc(refs_size); else { map.refs = mmap(NULL, refs_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); if ( map.refs == MAP_FAILED ) { GTERROR(xgt->logger, "anon mmap of map failed"); 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) ) { GTERROR(xgt->logger, "ioctl MAP_GRANT_REF failed"); goto out; } addr = mmap(NULL, PAGE_SIZE * count, prot, MAP_SHARED, fd, map.index); if ( addr != MAP_FAILED ) { int rv = 0; struct ioctl_gntdev_unmap_notify notify; notify.index = map.index; notify.action = 0; if ( notify_offset < PAGE_SIZE * count ) { notify.index += notify_offset; notify.action |= UNMAP_NOTIFY_CLEAR_BYTE; } if ( notify_port != -1 ) { notify.event_channel_port = notify_port; notify.action |= UNMAP_NOTIFY_SEND_EVENT; } if ( notify.action ) rv = ioctl(fd, IOCTL_GNTDEV_SET_UNMAP_NOTIFY, ¬ify); if ( rv ) { GTERROR(xgt->logger, "ioctl SET_UNMAP_NOTIFY failed"); munmap(addr, count * 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. */ GTERROR(xgt->logger, "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: if ( refs_size > PAGE_SIZE ) munmap(map.refs, refs_size); else free(map.refs); return addr; } int osdep_gnttab_unmap(xengnttab_handle *xgt, void *start_address, uint32_t count) { int rc; int fd = xgt->fd; struct ioctl_gntdev_unmap_grant_ref unmap_grant; struct ioctl_gntdev_get_offset_for_vaddr get_offset; 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; if ( (rc = ioctl(fd, IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR, &get_offset)) ) return rc; if ( get_offset.count != count ) { errno = EINVAL; return -1; } /* Next, unmap the memory. */ if ( (rc = munmap(start_address, count * PAGE_SIZE)) ) return rc; /* Finally, unmap the driver slots used to store the grant information. */ unmap_grant.index = get_offset.offset; unmap_grant.count = count; if ( (rc = ioctl(fd, IOCTL_GNTDEV_UNMAP_GRANT_REF, &unmap_grant)) ) return rc; return 0; } int osdep_gnttab_grant_copy(xengnttab_handle *xgt, uint32_t count, xengnttab_grant_copy_segment_t *segs) { errno = ENOSYS; return -1; } int osdep_gntshr_open(xengntshr_handle *xgs) { int fd = open(DEVXEN, O_RDWR); if ( fd == -1 ) return -1; xgs->fd = fd; return 0; } int osdep_gntshr_close(xengntshr_handle *xgs) { if ( xgs->fd == -1 ) return 0; return close(xgs->fd); } void *osdep_gntshr_share_pages(xengntshr_handle *xgs, uint32_t domid, int count, uint32_t *refs, int writable, uint32_t notify_offset, evtchn_port_t notify_port) { int err; int fd = xgs->fd; void *area = NULL; struct ioctl_gntdev_unmap_notify notify; struct ioctl_gntdev_dealloc_gref gref_drop; struct ioctl_gntdev_alloc_gref gref_info; gref_info.gref_ids = malloc(count * sizeof(uint32_t)); if ( gref_info.gref_ids == NULL ) return NULL; gref_info.domid = domid; gref_info.flags = writable ? GNTDEV_ALLOC_FLAG_WRITABLE : 0; gref_info.count = count; err = ioctl(fd, IOCTL_GNTDEV_ALLOC_GREF, &gref_info); if ( err ) { GSERROR(xgs->logger, "ioctl failed"); goto out; } area = mmap(NULL, count * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, gref_info.index); if ( area == MAP_FAILED ) { area = NULL; GSERROR(xgs->logger, "mmap failed"); goto out_remove_fdmap; } notify.index = gref_info.index; notify.action = 0; if ( notify_offset < PAGE_SIZE * count ) { notify.index += notify_offset; notify.action |= UNMAP_NOTIFY_CLEAR_BYTE; } if ( notify_port != -1 ) { notify.event_channel_port = notify_port; notify.action |= UNMAP_NOTIFY_SEND_EVENT; } if ( notify.action ) err = ioctl(fd, IOCTL_GNTDEV_SET_UNMAP_NOTIFY, ¬ify); if ( err ) { GSERROR(xgs->logger, "ioctl SET_UNMAP_NOTIFY failed"); munmap(area, count * PAGE_SIZE); area = NULL; } memcpy(refs, gref_info.gref_ids, count * sizeof(uint32_t)); out_remove_fdmap: /* * Removing the mapping from the file descriptor does not cause the * pages to be deallocated until the mapping is removed. */ gref_drop.index = gref_info.index; gref_drop.count = count; ioctl(fd, IOCTL_GNTDEV_DEALLOC_GREF, &gref_drop); out: free(gref_info.gref_ids); return area; } int osdep_gntshr_unshare(xengntshr_handle *xgs, void *start_address, uint32_t count) { return munmap(start_address, count * PAGE_SIZE); } /* * The functions below are Linux-isms that will likely never be implemented * on FreeBSD unless FreeBSD also implements something akin to Linux dmabuf. */ int osdep_gnttab_dmabuf_exp_from_refs(xengnttab_handle *xgt, uint32_t domid, uint32_t flags, uint32_t count, const uint32_t *refs, uint32_t *dmabuf_fd) { abort(); } int osdep_gnttab_dmabuf_exp_wait_released(xengnttab_handle *xgt, uint32_t fd, uint32_t wait_to_ms) { abort(); } int osdep_gnttab_dmabuf_imp_to_refs(xengnttab_handle *xgt, uint32_t domid, uint32_t fd, uint32_t count, uint32_t *refs) { abort(); } int osdep_gnttab_dmabuf_imp_release(xengnttab_handle *xgt, uint32_t fd) { abort(); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */