Files
xen/tools/libxc/xc_private.c
T
kfraser@localhost.localdomain 1df421476a Replace dom0_ops hypercall with three new hypercalls:
1. platform_op -- used by dom0 kernel to perform actions on the
    hardware platform (e.g., MTRR access, microcode update, platform
    quirks, ...)
 2. domctl -- used by management tools to control a specified domain
 3. sysctl -- used by management tools for system-wide actions

Benefits include more sensible factoring of actions to
hypercalls. Also allows tool compatibility to be tracked separately
from the dom0 kernel. The assumption is that it will be easier to
replace libxenctrl, libxenguest and Xen as a matched set if the
dom0 kernel does not need to be replaced too (e.g., because that
would require vendor revalidation).

From here on we hope to maintain dom0 kernel compatibility. This
promise is not extended to tool compatibility beyond the existing
guarantee that compatibility will not be broken within a three-level
stable release [3.0.2, 3.0.3, etc.].

Signed-off-by: Keir Fraser <keir@xensource.com>
2006-08-25 18:39:10 +01:00

473 lines
12 KiB
C

/******************************************************************************
* xc_private.c
*
* Helper functions for the rest of the library.
*/
#include <inttypes.h>
#include "xc_private.h"
/* NB: arr must be mlock'ed */
int xc_get_pfn_type_batch(int xc_handle,
uint32_t dom, int num, unsigned long *arr)
{
DECLARE_DOMCTL;
domctl.cmd = XEN_DOMCTL_getpageframeinfo2;
domctl.domain = (domid_t)dom;
domctl.u.getpageframeinfo2.num = num;
set_xen_guest_handle(domctl.u.getpageframeinfo2.array, arr);
return do_domctl(xc_handle, &domctl);
}
#define GETPFN_ERR (~0U)
unsigned int get_pfn_type(int xc_handle,
unsigned long mfn,
uint32_t dom)
{
DECLARE_DOMCTL;
domctl.cmd = XEN_DOMCTL_getpageframeinfo;
domctl.u.getpageframeinfo.gmfn = mfn;
domctl.domain = (domid_t)dom;
if ( do_domctl(xc_handle, &domctl) < 0 )
{
PERROR("Unexpected failure when getting page frame info!");
return GETPFN_ERR;
}
return domctl.u.getpageframeinfo.type;
}
int xc_mmuext_op(
int xc_handle,
struct mmuext_op *op,
unsigned int nr_ops,
domid_t dom)
{
DECLARE_HYPERCALL;
long ret = -EINVAL;
hypercall.op = __HYPERVISOR_mmuext_op;
hypercall.arg[0] = (unsigned long)op;
hypercall.arg[1] = (unsigned long)nr_ops;
hypercall.arg[2] = (unsigned long)0;
hypercall.arg[3] = (unsigned long)dom;
if ( mlock(op, nr_ops*sizeof(*op)) != 0 )
{
PERROR("Could not lock memory for Xen hypercall");
goto out1;
}
ret = do_xen_hypercall(xc_handle, &hypercall);
safe_munlock(op, nr_ops*sizeof(*op));
out1:
return ret;
}
static int flush_mmu_updates(int xc_handle, xc_mmu_t *mmu)
{
int err = 0;
DECLARE_HYPERCALL;
if ( mmu->idx == 0 )
return 0;
hypercall.op = __HYPERVISOR_mmu_update;
hypercall.arg[0] = (unsigned long)mmu->updates;
hypercall.arg[1] = (unsigned long)mmu->idx;
hypercall.arg[2] = 0;
hypercall.arg[3] = mmu->subject;
if ( mlock(mmu->updates, sizeof(mmu->updates)) != 0 )
{
PERROR("flush_mmu_updates: mmu updates mlock failed");
err = 1;
goto out;
}
if ( do_xen_hypercall(xc_handle, &hypercall) < 0 )
{
ERROR("Failure when submitting mmu updates");
err = 1;
}
mmu->idx = 0;
safe_munlock(mmu->updates, sizeof(mmu->updates));
out:
return err;
}
xc_mmu_t *xc_init_mmu_updates(int xc_handle, domid_t dom)
{
xc_mmu_t *mmu = malloc(sizeof(xc_mmu_t));
if ( mmu == NULL )
return mmu;
mmu->idx = 0;
mmu->subject = dom;
return mmu;
}
int xc_add_mmu_update(int xc_handle, xc_mmu_t *mmu,
unsigned long long ptr, unsigned long long val)
{
mmu->updates[mmu->idx].ptr = ptr;
mmu->updates[mmu->idx].val = val;
if ( ++mmu->idx == MAX_MMU_UPDATES )
return flush_mmu_updates(xc_handle, mmu);
return 0;
}
int xc_finish_mmu_updates(int xc_handle, xc_mmu_t *mmu)
{
return flush_mmu_updates(xc_handle, mmu);
}
int xc_memory_op(int xc_handle,
int cmd,
void *arg)
{
DECLARE_HYPERCALL;
struct xen_memory_reservation *reservation = arg;
struct xen_machphys_mfn_list *xmml = arg;
struct xen_translate_gpfn_list *trans = arg;
xen_pfn_t *extent_start;
xen_pfn_t *gpfn_list;
xen_pfn_t *mfn_list;
long ret = -EINVAL;
hypercall.op = __HYPERVISOR_memory_op;
hypercall.arg[0] = (unsigned long)cmd;
hypercall.arg[1] = (unsigned long)arg;
switch ( cmd )
{
case XENMEM_increase_reservation:
case XENMEM_decrease_reservation:
case XENMEM_populate_physmap:
if ( mlock(reservation, sizeof(*reservation)) != 0 )
{
PERROR("Could not mlock");
goto out1;
}
get_xen_guest_handle(extent_start, reservation->extent_start);
if ( (extent_start != NULL) &&
(mlock(extent_start,
reservation->nr_extents * sizeof(xen_pfn_t)) != 0) )
{
PERROR("Could not mlock");
safe_munlock(reservation, sizeof(*reservation));
goto out1;
}
break;
case XENMEM_machphys_mfn_list:
if ( mlock(xmml, sizeof(*xmml)) != 0 )
{
PERROR("Could not mlock");
goto out1;
}
get_xen_guest_handle(extent_start, xmml->extent_start);
if ( mlock(extent_start,
xmml->max_extents * sizeof(xen_pfn_t)) != 0 )
{
PERROR("Could not mlock");
safe_munlock(xmml, sizeof(*xmml));
goto out1;
}
break;
case XENMEM_add_to_physmap:
if ( mlock(arg, sizeof(struct xen_add_to_physmap)) )
{
PERROR("Could not mlock");
goto out1;
}
break;
case XENMEM_translate_gpfn_list:
if ( mlock(trans, sizeof(*trans)) != 0 )
{
PERROR("Could not mlock");
goto out1;
}
get_xen_guest_handle(gpfn_list, trans->gpfn_list);
if ( mlock(gpfn_list, trans->nr_gpfns * sizeof(xen_pfn_t)) != 0 )
{
PERROR("Could not mlock");
safe_munlock(trans, sizeof(*trans));
goto out1;
}
get_xen_guest_handle(mfn_list, trans->mfn_list);
if ( mlock(mfn_list, trans->nr_gpfns * sizeof(xen_pfn_t)) != 0 )
{
PERROR("Could not mlock");
safe_munlock(gpfn_list, trans->nr_gpfns * sizeof(xen_pfn_t));
safe_munlock(trans, sizeof(*trans));
goto out1;
}
break;
}
ret = do_xen_hypercall(xc_handle, &hypercall);
switch ( cmd )
{
case XENMEM_increase_reservation:
case XENMEM_decrease_reservation:
case XENMEM_populate_physmap:
safe_munlock(reservation, sizeof(*reservation));
get_xen_guest_handle(extent_start, reservation->extent_start);
if ( extent_start != NULL )
safe_munlock(extent_start,
reservation->nr_extents * sizeof(xen_pfn_t));
break;
case XENMEM_machphys_mfn_list:
safe_munlock(xmml, sizeof(*xmml));
get_xen_guest_handle(extent_start, xmml->extent_start);
safe_munlock(extent_start,
xmml->max_extents * sizeof(xen_pfn_t));
break;
case XENMEM_add_to_physmap:
safe_munlock(arg, sizeof(struct xen_add_to_physmap));
break;
case XENMEM_translate_gpfn_list:
get_xen_guest_handle(mfn_list, trans->mfn_list);
safe_munlock(mfn_list, trans->nr_gpfns * sizeof(xen_pfn_t));
get_xen_guest_handle(gpfn_list, trans->gpfn_list);
safe_munlock(gpfn_list, trans->nr_gpfns * sizeof(xen_pfn_t));
safe_munlock(trans, sizeof(*trans));
break;
}
out1:
return ret;
}
long long xc_domain_get_cpu_usage( int xc_handle, domid_t domid, int vcpu )
{
DECLARE_DOMCTL;
domctl.cmd = XEN_DOMCTL_getvcpuinfo;
domctl.domain = (domid_t)domid;
domctl.u.getvcpuinfo.vcpu = (uint16_t)vcpu;
if ( (do_domctl(xc_handle, &domctl) < 0) )
{
PERROR("Could not get info on domain");
return -1;
}
return domctl.u.getvcpuinfo.cpu_time;
}
#ifndef __ia64__
int xc_get_pfn_list(int xc_handle,
uint32_t domid,
xen_pfn_t *pfn_buf,
unsigned long max_pfns)
{
DECLARE_DOMCTL;
int ret;
domctl.cmd = XEN_DOMCTL_getmemlist;
domctl.domain = (domid_t)domid;
domctl.u.getmemlist.max_pfns = max_pfns;
set_xen_guest_handle(domctl.u.getmemlist.buffer, pfn_buf);
#ifdef VALGRIND
memset(pfn_buf, 0, max_pfns * sizeof(xen_pfn_t));
#endif
if ( mlock(pfn_buf, max_pfns * sizeof(xen_pfn_t)) != 0 )
{
PERROR("xc_get_pfn_list: pfn_buf mlock failed");
return -1;
}
ret = do_domctl(xc_handle, &domctl);
safe_munlock(pfn_buf, max_pfns * sizeof(xen_pfn_t));
#if 0
#ifdef DEBUG
DPRINTF(("Ret for xc_get_pfn_list is %d\n", ret));
if (ret >= 0) {
int i, j;
for (i = 0; i < domctl.u.getmemlist.num_pfns; i += 16) {
DPRINTF("0x%x: ", i);
for (j = 0; j < 16; j++)
DPRINTF("0x%lx ", pfn_buf[i + j]);
DPRINTF("\n");
}
}
#endif
#endif
return (ret < 0) ? -1 : domctl.u.getmemlist.num_pfns;
}
#endif
long xc_get_tot_pages(int xc_handle, uint32_t domid)
{
DECLARE_DOMCTL;
domctl.cmd = XEN_DOMCTL_getdomaininfo;
domctl.domain = (domid_t)domid;
return (do_domctl(xc_handle, &domctl) < 0) ?
-1 : domctl.u.getdomaininfo.tot_pages;
}
int xc_copy_to_domain_page(int xc_handle,
uint32_t domid,
unsigned long dst_pfn,
const char *src_page)
{
void *vaddr = xc_map_foreign_range(
xc_handle, domid, PAGE_SIZE, PROT_WRITE, dst_pfn);
if ( vaddr == NULL )
return -1;
memcpy(vaddr, src_page, PAGE_SIZE);
munmap(vaddr, PAGE_SIZE);
return 0;
}
int xc_clear_domain_page(int xc_handle,
uint32_t domid,
unsigned long dst_pfn)
{
void *vaddr = xc_map_foreign_range(
xc_handle, domid, PAGE_SIZE, PROT_WRITE, dst_pfn);
if ( vaddr == NULL )
return -1;
memset(vaddr, 0, PAGE_SIZE);
munmap(vaddr, PAGE_SIZE);
return 0;
}
unsigned long xc_get_filesz(int fd)
{
uint16_t sig;
uint32_t _sz = 0;
unsigned long sz;
lseek(fd, 0, SEEK_SET);
if ( read(fd, &sig, sizeof(sig)) != sizeof(sig) )
return 0;
sz = lseek(fd, 0, SEEK_END);
if ( sig == 0x8b1f ) /* GZIP signature? */
{
lseek(fd, -4, SEEK_END);
if ( read(fd, &_sz, 4) != 4 )
return 0;
sz = _sz;
}
lseek(fd, 0, SEEK_SET);
return sz;
}
void xc_map_memcpy(unsigned long dst, const char *src, unsigned long size,
int xch, uint32_t dom, xen_pfn_t *parray,
unsigned long vstart)
{
char *va;
unsigned long chunksz, done, pa;
for ( done = 0; done < size; done += chunksz )
{
pa = dst + done - vstart;
va = xc_map_foreign_range(
xch, dom, PAGE_SIZE, PROT_WRITE, parray[pa>>PAGE_SHIFT]);
chunksz = size - done;
if ( chunksz > (PAGE_SIZE - (pa & (PAGE_SIZE-1))) )
chunksz = PAGE_SIZE - (pa & (PAGE_SIZE-1));
memcpy(va + (pa & (PAGE_SIZE-1)), src + done, chunksz);
munmap(va, PAGE_SIZE);
}
}
int xc_domctl(int xc_handle, struct xen_domctl *domctl)
{
return do_domctl(xc_handle, domctl);
}
int xc_sysctl(int xc_handle, struct xen_sysctl *sysctl)
{
return do_sysctl(xc_handle, sysctl);
}
int xc_version(int xc_handle, int cmd, void *arg)
{
int rc, argsize = 0;
switch ( cmd )
{
case XENVER_extraversion:
argsize = sizeof(xen_extraversion_t);
break;
case XENVER_compile_info:
argsize = sizeof(xen_compile_info_t);
break;
case XENVER_capabilities:
argsize = sizeof(xen_capabilities_info_t);
break;
case XENVER_changeset:
argsize = sizeof(xen_changeset_info_t);
break;
case XENVER_platform_parameters:
argsize = sizeof(xen_platform_parameters_t);
break;
}
if ( (argsize != 0) && (mlock(arg, argsize) != 0) )
{
PERROR("Could not lock memory for version hypercall");
return -ENOMEM;
}
#ifdef VALGRIND
if (argsize != 0)
memset(arg, 0, argsize);
#endif
rc = do_xen_version(xc_handle, cmd, arg);
if ( argsize != 0 )
safe_munlock(arg, argsize);
return rc;
}
unsigned long xc_make_page_below_4G(
int xc_handle, uint32_t domid, unsigned long mfn)
{
xen_pfn_t old_mfn = mfn;
xen_pfn_t new_mfn;
if ( xc_domain_memory_decrease_reservation(
xc_handle, domid, 1, 0, &old_mfn) != 0 )
{
DPRINTF("xc_make_page_below_4G decrease failed. mfn=%lx\n",mfn);
return 0;
}
if ( xc_domain_memory_increase_reservation(
xc_handle, domid, 1, 0, 32, &new_mfn) != 0 )
{
DPRINTF("xc_make_page_below_4G increase failed. mfn=%lx\n",mfn);
return 0;
}
return new_mfn;
}
/*
* Local variables:
* mode: C
* c-set-style: "BSD"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/