Files
xen/tools/libxc/xc_private.c
T
kfraser@localhost.localdomain 28f7888d7f hvm: Do not save/restore shared_info gpfn location.
Instead of kludging a max_gpfn estimate in shared_info, add a new
XENMEM command to discover the actual maximum gpfn value as known by
the shadow code.

This needs to be more robust when we support HVM ballooning in future
anyway. One interesting point is that max_gpfn may be close to 4GB
even for small-memory HVM guests since for example SVGA LFB is mapped
into the I/O hole. We may need to special case the I/O hole somehow,
or provide some finer-grained way to find out which parts of the GPFN
space are actually used (e.g., get Xen to fill in a bitmap with 1 bit
per 1024 pages, or similar).

Signed-off-by: Keir Fraser <keir@xensource.com>
2007-03-19 16:48:24 +00:00

527 lines
13 KiB
C

/******************************************************************************
* xc_private.c
*
* Helper functions for the rest of the library.
*/
#include <inttypes.h>
#include "xc_private.h"
#include "xg_private.h"
#include <stdarg.h>
#include <pthread.h>
static __thread xc_error last_error = { XC_ERROR_NONE, ""};
#if DEBUG
static xc_error_handler error_handler = xc_default_error_handler;
#else
static xc_error_handler error_handler = NULL;
#endif
void xc_default_error_handler(const xc_error const *err)
{
const char *desc = xc_error_code_to_desc(err->code);
fprintf(stderr, "ERROR %s: %s\n", desc, err->message);
}
const xc_error *xc_get_last_error(void)
{
return &last_error;
}
void xc_clear_last_error(void)
{
last_error.code = XC_ERROR_NONE;
last_error.message[0] = '\0';
}
const char *xc_error_code_to_desc(int code)
{
/* Sync to members of xc_error_code enumeration in xenctrl.h */
switch ( code )
{
case XC_ERROR_NONE:
return "No error details";
case XC_INTERNAL_ERROR:
return "Internal error";
case XC_INVALID_KERNEL:
return "Invalid kernel";
case XC_INVALID_PARAM:
return "Invalid configuration";
case XC_OUT_OF_MEMORY:
return "Out of memory";
}
return "Unknown error code";
}
xc_error_handler xc_set_error_handler(xc_error_handler handler)
{
xc_error_handler old = error_handler;
error_handler = handler;
return old;
}
static void _xc_set_error(int code, const char *msg)
{
last_error.code = code;
strncpy(last_error.message, msg, XC_MAX_ERROR_MSG_LEN - 1);
last_error.message[XC_MAX_ERROR_MSG_LEN-1] = '\0';
}
void xc_set_error(int code, const char *fmt, ...)
{
int saved_errno = errno;
char msg[XC_MAX_ERROR_MSG_LEN];
va_list args;
va_start(args, fmt);
vsnprintf(msg, XC_MAX_ERROR_MSG_LEN-1, fmt, args);
msg[XC_MAX_ERROR_MSG_LEN-1] = '\0';
va_end(args);
_xc_set_error(code, msg);
errno = saved_errno;
if ( error_handler != NULL )
error_handler(&last_error);
}
int lock_pages(void *addr, size_t len)
{
int e = 0;
#ifndef __sun__
e = mlock(addr, len);
#endif
return (e);
}
void unlock_pages(void *addr, size_t len)
{
#ifndef __sun__
safe_munlock(addr, len);
#endif
}
/* NB: arr must be locked */
int xc_get_pfn_type_batch(int xc_handle,
uint32_t dom, int num, uint32_t *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);
}
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 ( lock_pages(op, nr_ops*sizeof(*op)) != 0 )
{
PERROR("Could not lock memory for Xen hypercall");
goto out1;
}
ret = do_xen_hypercall(xc_handle, &hypercall);
unlock_pages(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 ( lock_pages(mmu->updates, sizeof(mmu->updates)) != 0 )
{
PERROR("flush_mmu_updates: mmu updates lock_pages failed");
err = 1;
goto out;
}
if ( do_xen_hypercall(xc_handle, &hypercall) < 0 )
{
ERROR("Failure when submitting mmu updates");
err = 1;
}
mmu->idx = 0;
unlock_pages(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;
xen_pfn_t *extent_start;
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 ( lock_pages(reservation, sizeof(*reservation)) != 0 )
{
PERROR("Could not lock");
goto out1;
}
get_xen_guest_handle(extent_start, reservation->extent_start);
if ( (extent_start != NULL) &&
(lock_pages(extent_start,
reservation->nr_extents * sizeof(xen_pfn_t)) != 0) )
{
PERROR("Could not lock");
unlock_pages(reservation, sizeof(*reservation));
goto out1;
}
break;
case XENMEM_machphys_mfn_list:
if ( lock_pages(xmml, sizeof(*xmml)) != 0 )
{
PERROR("Could not lock");
goto out1;
}
get_xen_guest_handle(extent_start, xmml->extent_start);
if ( lock_pages(extent_start,
xmml->max_extents * sizeof(xen_pfn_t)) != 0 )
{
PERROR("Could not lock");
unlock_pages(xmml, sizeof(*xmml));
goto out1;
}
break;
case XENMEM_add_to_physmap:
if ( lock_pages(arg, sizeof(struct xen_add_to_physmap)) )
{
PERROR("Could not lock");
goto out1;
}
break;
case XENMEM_current_reservation:
case XENMEM_maximum_reservation:
case XENMEM_maximum_gpfn:
if ( lock_pages(arg, sizeof(domid_t)) )
{
PERROR("Could not lock");
goto out1;
}
break;
}
ret = do_xen_hypercall(xc_handle, &hypercall);
switch ( cmd )
{
case XENMEM_increase_reservation:
case XENMEM_decrease_reservation:
case XENMEM_populate_physmap:
unlock_pages(reservation, sizeof(*reservation));
get_xen_guest_handle(extent_start, reservation->extent_start);
if ( extent_start != NULL )
unlock_pages(extent_start,
reservation->nr_extents * sizeof(xen_pfn_t));
break;
case XENMEM_machphys_mfn_list:
unlock_pages(xmml, sizeof(*xmml));
get_xen_guest_handle(extent_start, xmml->extent_start);
unlock_pages(extent_start,
xmml->max_extents * sizeof(xen_pfn_t));
break;
case XENMEM_add_to_physmap:
unlock_pages(arg, sizeof(struct xen_add_to_physmap));
break;
case XENMEM_current_reservation:
case XENMEM_maximum_reservation:
case XENMEM_maximum_gpfn:
unlock_pages(arg, sizeof(domid_t));
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,
uint64_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(*pfn_buf));
#endif
if ( lock_pages(pfn_buf, max_pfns * sizeof(*pfn_buf)) != 0 )
{
PERROR("xc_get_pfn_list: pfn_buf lock failed");
return -1;
}
ret = do_domctl(xc_handle, &domctl);
unlock_pages(pfn_buf, max_pfns * sizeof(*pfn_buf));
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;
}
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) && (lock_pages(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 )
unlock_pages(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;
}
char *safe_strerror(int errcode)
{
static __thread char errbuf[32];
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
char *strerror_str;
/*
* Thread-unsafe strerror() is protected by a local mutex. We copy
* the string to a thread-private buffer before releasing the mutex.
*/
pthread_mutex_lock(&mutex);
strerror_str = strerror(errcode);
strncpy(errbuf, strerror_str, sizeof(errbuf));
errbuf[sizeof(errbuf)-1] = '\0';
pthread_mutex_unlock(&mutex);
return errbuf;
}
void bitmap_64_to_byte(uint8_t *bp, const uint64_t *lp, int nbits)
{
uint64_t l;
int i, j, b;
for (i = 0, b = 0; nbits > 0; i++, b += sizeof(l)) {
l = lp[i];
for (j = 0; (j < sizeof(l)) && (nbits > 0); j++) {
bp[b+j] = l;
l >>= 8;
nbits -= 8;
}
}
}
void bitmap_byte_to_64(uint64_t *lp, const uint8_t *bp, int nbits)
{
uint64_t l;
int i, j, b;
for (i = 0, b = 0; nbits > 0; i++, b += sizeof(l)) {
l = 0;
for (j = 0; (j < sizeof(l)) && (nbits > 0); j++) {
l |= (uint64_t)bp[b+j] << (j*8);
nbits -= 8;
}
lp[i] = l;
}
}
/*
* Local variables:
* mode: C
* c-set-style: "BSD"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/