/*
* 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 xc_linus_osdep.c:
*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include "private.h"
#ifndef O_CLOEXEC
#define O_CLOEXEC 0
#endif
int osdep_xenforeignmemory_open(xenforeignmemory_handle *fmem)
{
int fd;
/* prefer this newer interface */
fd = open("/dev/xen/privcmd", O_RDWR|O_CLOEXEC);
if ( fd == -1 && ( errno == ENOENT || errno == ENXIO || errno == ENODEV ))
{
/* Fallback to /proc/xen/privcmd */
fd = open("/proc/xen/privcmd", O_RDWR|O_CLOEXEC);
}
if ( fd == -1 )
{
PERROR("Could not obtain handle on privileged command interface");
return -1;
}
/*
* Older versions of privcmd return -EINVAL for unimplemented ioctls
* so we need to probe for the errno to use rather than just using
* the conventional ENOTTY.
*/
if ( ioctl(fd, IOCTL_PRIVCMD_UNIMPLEMENTED, NULL) >= 0 )
{
xtl_log(fmem->logger, XTL_ERROR, -1, "xenforeignmemory",
"privcmd ioctl should not be implemented");
close(fd);
return -1;
}
else
{
fmem->unimpl_errno = errno;
errno = 0;
}
fmem->fd = fd;
return 0;
}
int osdep_xenforeignmemory_close(xenforeignmemory_handle *fmem)
{
int fd = fmem->fd;
if (fd == -1)
return 0;
return close(fd);
}
static int map_foreign_batch_single(int fd, uint32_t dom,
xen_pfn_t *mfn, unsigned long addr)
{
privcmd_mmapbatch_t ioctlx;
int rc;
ioctlx.num = 1;
ioctlx.dom = dom;
ioctlx.addr = addr;
ioctlx.arr = mfn;
do
{
*mfn ^= PRIVCMD_MMAPBATCH_PAGED_ERROR;
usleep(100);
rc = ioctl(fd, IOCTL_PRIVCMD_MMAPBATCH, &ioctlx);
}
while ( (rc < 0) && (errno == ENOENT) );
return rc;
}
/*
* Retry mmap of all paged gfns in batches
* retuns < 0 on fatal error
* returns 0 if all gfns left paging state
* returns > 0 if some gfns are still in paging state
*
* Walk all gfns and try to assemble blocks of gfns in paging state.
* This will keep the request ring full and avoids delays.
*/
static int retry_paged(int fd, uint32_t dom, void *addr,
const xen_pfn_t *arr, int *err, size_t num)
{
privcmd_mmapbatch_v2_t ioctlx;
int rc, paged = 0;
size_t i = 0;
do
{
/* Skip gfns not in paging state */
if ( err[i] != -ENOENT )
{
i++;
continue;
}
paged++;
/* At least one gfn is still in paging state */
ioctlx.num = 1;
ioctlx.dom = dom;
ioctlx.addr = (unsigned long)addr + (i<fd;
privcmd_mmapbatch_v2_t ioctlx;
size_t i;
int rc;
addr = mmap(addr, num << XC_PAGE_SHIFT, prot, flags | MAP_SHARED,
fd, 0);
if ( addr == MAP_FAILED )
return NULL;
ioctlx.num = num;
ioctlx.dom = dom;
ioctlx.addr = (unsigned long)addr;
ioctlx.arr = arr;
ioctlx.err = err;
rc = ioctl(fd, IOCTL_PRIVCMD_MMAPBATCH_V2, &ioctlx);
/* Command was recognized, some gfn in arr are in paging state */
if ( rc < 0 && errno == ENOENT )
{
do {
usleep(100);
rc = retry_paged(fd, dom, addr, arr, err, num);
} while ( rc > 0 );
}
/* Command was not recognized, use fall back */
else if ( rc < 0 && errno == EINVAL && (int)num > 0 )
{
/*
* IOCTL_PRIVCMD_MMAPBATCH_V2 is not supported - fall back to
* IOCTL_PRIVCMD_MMAPBATCH.
*/
privcmd_mmapbatch_t ioctlx;
xen_pfn_t *pfn;
unsigned int pfn_arr_size = ROUNDUP((num * sizeof(*pfn)), XC_PAGE_SHIFT);
int os_page_size = sysconf(_SC_PAGESIZE);
if ( pfn_arr_size <= os_page_size )
pfn = alloca(num * sizeof(*pfn));
else
{
pfn = mmap(NULL, pfn_arr_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_POPULATE, -1, 0);
if ( pfn == MAP_FAILED )
{
PERROR("mmap of pfn array failed");
(void)munmap(addr, num << XC_PAGE_SHIFT);
return NULL;
}
}
memcpy(pfn, arr, num * sizeof(*arr));
ioctlx.num = num;
ioctlx.dom = dom;
ioctlx.addr = (unsigned long)addr;
ioctlx.arr = pfn;
rc = ioctl(fd, IOCTL_PRIVCMD_MMAPBATCH, &ioctlx);
rc = rc < 0 ? -errno : 0;
for ( i = 0; i < num; ++i )
{
switch ( pfn[i] ^ arr[i] )
{
case 0:
err[i] = rc != -ENOENT ? rc : 0;
continue;
default:
err[i] = -EINVAL;
continue;
case PRIVCMD_MMAPBATCH_PAGED_ERROR:
if ( rc != -ENOENT )
{
err[i] = rc ?: -EINVAL;
continue;
}
rc = map_foreign_batch_single(fd, dom, pfn + i,
(unsigned long)addr + (i< os_page_size )
munmap(pfn, pfn_arr_size);
if ( rc == -ENOENT && i == num )
rc = 0;
else if ( rc )
{
errno = -rc;
rc = -1;
}
}
if ( rc < 0 )
{
int saved_errno = errno;
(void)munmap(addr, num << XC_PAGE_SHIFT);
errno = saved_errno;
return NULL;
}
return addr;
}
int osdep_xenforeignmemory_unmap(xenforeignmemory_handle *fmem,
void *addr, size_t num)
{
return munmap(addr, num << XC_PAGE_SHIFT);
}
int osdep_xenforeignmemory_restrict(xenforeignmemory_handle *fmem,
domid_t domid)
{
return ioctl(fmem->fd, IOCTL_PRIVCMD_RESTRICT, &domid);
}
int osdep_xenforeignmemory_unmap_resource(
xenforeignmemory_handle *fmem, xenforeignmemory_resource_handle *fres)
{
return fres ? munmap(fres->addr, fres->nr_frames << XC_PAGE_SHIFT) : 0;
}
int osdep_xenforeignmemory_map_resource(
xenforeignmemory_handle *fmem, xenforeignmemory_resource_handle *fres)
{
privcmd_mmap_resource_t mr = {
.dom = fres->domid,
.type = fres->type,
.id = fres->id,
.idx = fres->frame,
.num = fres->nr_frames,
};
int rc;
if ( !fres->addr && !fres->nr_frames )
/* Request for resource size. Skip mmap(). */
goto skip_mmap;
fres->addr = mmap(fres->addr, fres->nr_frames << XC_PAGE_SHIFT,
fres->prot, fres->flags | MAP_SHARED, fmem->fd, 0);
if ( fres->addr == MAP_FAILED )
return -1;
mr.addr = (uintptr_t)fres->addr;
skip_mmap:
rc = ioctl(fmem->fd, IOCTL_PRIVCMD_MMAP_RESOURCE, &mr);
if ( rc )
{
int saved_errno;
if ( errno == fmem->unimpl_errno )
errno = EOPNOTSUPP;
if ( fres->addr )
{
saved_errno = errno;
osdep_xenforeignmemory_unmap_resource(fmem, fres);
errno = saved_errno;
}
return -1;
}
/* If requesting size, copy back. */
if ( !fres->addr )
fres->nr_frames = mr.num;
return 0;
}
/*
* Local variables:
* mode: C
* c-file-style: "BSD"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/