/*
 * xen_internal.c: direct access to Xen hypervisor level
 *
 * Copyright (C) 2005-2012 Red Hat, Inc.
 *
 * See COPYING.LIB for the License of this software
 *
 * Daniel Veillard <veillard@redhat.com>
 */

#include <config.h>

#include <stdio.h>
#include <string.h>
/* required for uint8_t, uint32_t, etc ... */
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <limits.h>
#include <stdint.h>
#include <regex.h>
#include <errno.h>
#include <sys/utsname.h>

#ifdef __sun
# include <sys/systeminfo.h>

# include <priv.h>

# ifndef PRIV_XVM_CONTROL
#  define PRIV_XVM_CONTROL ((const char *)"xvm_control")
# endif

#endif /* __sun */

/* required for dom0_getdomaininfo_t */
#include <xen/dom0_ops.h>
#include <xen/version.h>
#ifdef HAVE_XEN_LINUX_PRIVCMD_H
# include <xen/linux/privcmd.h>
#else
# ifdef HAVE_XEN_SYS_PRIVCMD_H
#  include <xen/sys/privcmd.h>
# endif
#endif

/* required for shutdown flags */
#include <xen/sched.h>

#include "virterror_internal.h"
#include "logging.h"
#include "datatypes.h"
#include "driver.h"
#include "util.h"
#include "xen_driver.h"
#include "xen_hypervisor.h"
#include "xs_internal.h"
#include "stats_linux.h"
#include "block_stats.h"
#include "xend_internal.h"
#include "buf.h"
#include "capabilities.h"
#include "memory.h"
#include "virfile.h"
#include "virnodesuspend.h"
#include "virtypedparam.h"

#define VIR_FROM_THIS VIR_FROM_XEN

/*
 * so far there is 2 versions of the structures usable for doing
 * hypervisor calls.
 */
/* the old one */
typedef struct v0_hypercall_struct {
    unsigned long op;
    unsigned long arg[5];
} v0_hypercall_t;

#ifdef __linux__
# define XEN_V0_IOCTL_HYPERCALL_CMD \
        _IOC(_IOC_NONE, 'P', 0, sizeof(v0_hypercall_t))
/* the new one */
typedef struct v1_hypercall_struct
{
    uint64_t op;
    uint64_t arg[5];
} v1_hypercall_t;
# define XEN_V1_IOCTL_HYPERCALL_CMD                  \
    _IOC(_IOC_NONE, 'P', 0, sizeof(v1_hypercall_t))
typedef v1_hypercall_t hypercall_t;
#elif defined(__sun)
typedef privcmd_hypercall_t hypercall_t;
#else
# error "unsupported platform"
#endif

#ifndef __HYPERVISOR_sysctl
# define __HYPERVISOR_sysctl 35
#endif
#ifndef __HYPERVISOR_domctl
# define __HYPERVISOR_domctl 36
#endif

#ifdef WITH_RHEL5_API
# define SYS_IFACE_MIN_VERS_NUMA 3
#else
# define SYS_IFACE_MIN_VERS_NUMA 4
#endif

static int xen_ioctl_hypercall_cmd = 0;
static int initialized = 0;
static int in_init = 0;
static struct xenHypervisorVersions hv_versions = {
    .hv = 0,
    .hypervisor = 2,
    .sys_interface = -1,
    .dom_interface = -1,
};

static int kb_per_pages = 0;

/* Regular expressions used by xenHypervisorGetCapabilities, and
 * compiled once by xenHypervisorInit.  Note that these are POSIX.2
 * extended regular expressions (regex(7)).
 */
static const char *flags_hvm_re = "^flags[[:blank:]]+:.* (vmx|svm)[[:space:]]";
static regex_t flags_hvm_rec;
static const char *flags_pae_re = "^flags[[:blank:]]+:.* pae[[:space:]]";
static regex_t flags_pae_rec;
static const char *xen_cap_re = "(xen|hvm)-[[:digit:]]+\\.[[:digit:]]+-(x86_32|x86_64|ia64|powerpc64)(p|be)?";
static regex_t xen_cap_rec;

/*
 * The content of the structures for a getdomaininfolist system hypercall
 */
#ifndef DOMFLAGS_DYING
# define DOMFLAGS_DYING     (1<<0) /* Domain is scheduled to die.             */
# define DOMFLAGS_HVM       (1<<1) /* Domain is HVM                           */
# define DOMFLAGS_SHUTDOWN  (1<<2) /* The guest OS has shut down.             */
# define DOMFLAGS_PAUSED    (1<<3) /* Currently paused by control software.   */
# define DOMFLAGS_BLOCKED   (1<<4) /* Currently blocked pending an event.     */
# define DOMFLAGS_RUNNING   (1<<5) /* Domain is currently running.            */
# define DOMFLAGS_CPUMASK      255 /* CPU to which this domain is bound.      */
# define DOMFLAGS_CPUSHIFT       8
# define DOMFLAGS_SHUTDOWNMASK 255 /* DOMFLAGS_SHUTDOWN guest-supplied code.  */
# define DOMFLAGS_SHUTDOWNSHIFT 16
#endif

/*
 * These flags explain why a system is in the state of "shutdown".  Normally,
 * They are defined in xen/sched.h
 */
#ifndef SHUTDOWN_poweroff
# define SHUTDOWN_poweroff   0  /* Domain exited normally. Clean up and kill. */
# define SHUTDOWN_reboot     1  /* Clean up, kill, and then restart.          */
# define SHUTDOWN_suspend    2  /* Clean up, save suspend info, kill.         */
# define SHUTDOWN_crash      3  /* Tell controller we've crashed.             */
#endif

#define XEN_V0_OP_GETDOMAININFOLIST	38
#define XEN_V1_OP_GETDOMAININFOLIST	38
#define XEN_V2_OP_GETDOMAININFOLIST	6

struct xen_v0_getdomaininfo {
    domid_t  domain;	/* the domain number */
    uint32_t flags;	/* flags, see before */
    uint64_t tot_pages;	/* total number of pages used */
    uint64_t max_pages;	/* maximum number of pages allowed */
    unsigned long shared_info_frame; /* MFN of shared_info struct */
    uint64_t cpu_time;  /* CPU time used */
    uint32_t nr_online_vcpus;  /* Number of VCPUs currently online. */
    uint32_t max_vcpu_id; /* Maximum VCPUID in use by this domain. */
    uint32_t ssidref;
    xen_domain_handle_t handle;
};
typedef struct xen_v0_getdomaininfo xen_v0_getdomaininfo;

struct xen_v2_getdomaininfo {
    domid_t  domain;	/* the domain number */
    uint32_t flags;	/* flags, see before */
    uint64_t tot_pages;	/* total number of pages used */
    uint64_t max_pages;	/* maximum number of pages allowed */
    uint64_t shared_info_frame; /* MFN of shared_info struct */
    uint64_t cpu_time;  /* CPU time used */
    uint32_t nr_online_vcpus;  /* Number of VCPUs currently online. */
    uint32_t max_vcpu_id; /* Maximum VCPUID in use by this domain. */
    uint32_t ssidref;
    xen_domain_handle_t handle;
};
typedef struct xen_v2_getdomaininfo xen_v2_getdomaininfo;


/* As of Hypervisor Call v2,  DomCtl v5 we are now 8-byte aligned
   even on 32-bit archs when dealing with uint64_t */
#define ALIGN_64 __attribute__((aligned(8)))

struct xen_v2d5_getdomaininfo {
    domid_t  domain;	/* the domain number */
    uint32_t flags;	/* flags, see before */
    uint64_t tot_pages ALIGN_64;	/* total number of pages used */
    uint64_t max_pages ALIGN_64;	/* maximum number of pages allowed */
    uint64_t shared_info_frame ALIGN_64; /* MFN of shared_info struct */
    uint64_t cpu_time ALIGN_64;  /* CPU time used */
    uint32_t nr_online_vcpus;  /* Number of VCPUs currently online. */
    uint32_t max_vcpu_id; /* Maximum VCPUID in use by this domain. */
    uint32_t ssidref;
    xen_domain_handle_t handle;
};
typedef struct xen_v2d5_getdomaininfo xen_v2d5_getdomaininfo;

struct xen_v2d6_getdomaininfo {
    domid_t  domain;	/* the domain number */
    uint32_t flags;	/* flags, see before */
    uint64_t tot_pages ALIGN_64;	/* total number of pages used */
    uint64_t max_pages ALIGN_64;	/* maximum number of pages allowed */
    uint64_t shr_pages ALIGN_64;    /* number of shared pages */
    uint64_t shared_info_frame ALIGN_64; /* MFN of shared_info struct */
    uint64_t cpu_time ALIGN_64;  /* CPU time used */
    uint32_t nr_online_vcpus;  /* Number of VCPUs currently online. */
    uint32_t max_vcpu_id; /* Maximum VCPUID in use by this domain. */
    uint32_t ssidref;
    xen_domain_handle_t handle;
};
typedef struct xen_v2d6_getdomaininfo xen_v2d6_getdomaininfo;

struct xen_v2d7_getdomaininfo {
    domid_t  domain;	/* the domain number */
    uint32_t flags;	/* flags, see before */
    uint64_t tot_pages ALIGN_64;	/* total number of pages used */
    uint64_t max_pages ALIGN_64;	/* maximum number of pages allowed */
    uint64_t shr_pages ALIGN_64;    /* number of shared pages */
    uint64_t shared_info_frame ALIGN_64; /* MFN of shared_info struct */
    uint64_t cpu_time ALIGN_64;  /* CPU time used */
    uint32_t nr_online_vcpus;  /* Number of VCPUs currently online. */
    uint32_t max_vcpu_id; /* Maximum VCPUID in use by this domain. */
    uint32_t ssidref;
    xen_domain_handle_t handle;
    uint32_t cpupool;
};
typedef struct xen_v2d7_getdomaininfo xen_v2d7_getdomaininfo;

struct xen_v2d8_getdomaininfo {
    domid_t  domain;	/* the domain number */
    uint32_t flags;	/* flags, see before */
    uint64_t tot_pages ALIGN_64;	/* total number of pages used */
    uint64_t max_pages ALIGN_64;	/* maximum number of pages allowed */
    uint64_t shr_pages ALIGN_64;    /* number of shared pages */
    uint64_t paged_pages ALIGN_64;    /* number of paged pages */
    uint64_t shared_info_frame ALIGN_64; /* MFN of shared_info struct */
    uint64_t cpu_time ALIGN_64;  /* CPU time used */
    uint32_t nr_online_vcpus;  /* Number of VCPUs currently online. */
    uint32_t max_vcpu_id; /* Maximum VCPUID in use by this domain. */
    uint32_t ssidref;
    xen_domain_handle_t handle;
    uint32_t cpupool;
};
typedef struct xen_v2d8_getdomaininfo xen_v2d8_getdomaininfo;

union xen_getdomaininfo {
    struct xen_v0_getdomaininfo v0;
    struct xen_v2_getdomaininfo v2;
    struct xen_v2d5_getdomaininfo v2d5;
    struct xen_v2d6_getdomaininfo v2d6;
    struct xen_v2d7_getdomaininfo v2d7;
    struct xen_v2d8_getdomaininfo v2d8;
};
typedef union xen_getdomaininfo xen_getdomaininfo;

union xen_getdomaininfolist {
    struct xen_v0_getdomaininfo *v0;
    struct xen_v2_getdomaininfo *v2;
    struct xen_v2d5_getdomaininfo *v2d5;
    struct xen_v2d6_getdomaininfo *v2d6;
    struct xen_v2d7_getdomaininfo *v2d7;
    struct xen_v2d8_getdomaininfo *v2d8;
};
typedef union xen_getdomaininfolist xen_getdomaininfolist;


struct xen_v2_getschedulerid {
    uint32_t sched_id; /* Get Scheduler ID from Xen */
};
typedef struct xen_v2_getschedulerid xen_v2_getschedulerid;


union xen_getschedulerid {
    struct xen_v2_getschedulerid *v2;
};
typedef union xen_getschedulerid xen_getschedulerid;

struct xen_v2s4_availheap {
    uint32_t min_bitwidth;  /* Smallest address width (zero if don't care). */
    uint32_t max_bitwidth;  /* Largest address width (zero if don't care). */
    int32_t  node;          /* NUMA node (-1 for sum across all nodes). */
    uint64_t avail_bytes;   /* Bytes available in the specified region. */
};

typedef struct xen_v2s4_availheap  xen_v2s4_availheap;

struct xen_v2s5_availheap {
    uint32_t min_bitwidth;  /* Smallest address width (zero if don't care). */
    uint32_t max_bitwidth;  /* Largest address width (zero if don't care). */
    int32_t  node;          /* NUMA node (-1 for sum across all nodes). */
    uint64_t avail_bytes ALIGN_64;   /* Bytes available in the specified region. */
};

typedef struct xen_v2s5_availheap  xen_v2s5_availheap;


#define XEN_GETDOMAININFOLIST_ALLOC(domlist, size)                      \
    (hv_versions.hypervisor < 2 ?                                       \
     (VIR_ALLOC_N(domlist.v0, (size)) == 0) :                           \
     (hv_versions.dom_interface >= 8 ?                                  \
      (VIR_ALLOC_N(domlist.v2d8, (size)) == 0) :                        \
     (hv_versions.dom_interface == 7 ?                                  \
      (VIR_ALLOC_N(domlist.v2d7, (size)) == 0) :                        \
     (hv_versions.dom_interface == 6 ?                                  \
      (VIR_ALLOC_N(domlist.v2d6, (size)) == 0) :                        \
     (hv_versions.dom_interface == 5 ?                                  \
      (VIR_ALLOC_N(domlist.v2d5, (size)) == 0) :                        \
      (VIR_ALLOC_N(domlist.v2, (size)) == 0))))))

#define XEN_GETDOMAININFOLIST_FREE(domlist)            \
    (hv_versions.hypervisor < 2 ?                      \
     VIR_FREE(domlist.v0) :                            \
     (hv_versions.dom_interface >= 8 ?                 \
      VIR_FREE(domlist.v2d8) :                         \
     (hv_versions.dom_interface == 7 ?                 \
      VIR_FREE(domlist.v2d7) :                         \
     (hv_versions.dom_interface == 6 ?                 \
      VIR_FREE(domlist.v2d6) :                         \
     (hv_versions.dom_interface == 5 ?                 \
      VIR_FREE(domlist.v2d5) :                         \
      VIR_FREE(domlist.v2))))))

#define XEN_GETDOMAININFOLIST_CLEAR(domlist, size)            \
    (hv_versions.hypervisor < 2 ?                             \
     memset(domlist.v0, 0, sizeof(*domlist.v0) * size) :      \
     (hv_versions.dom_interface >= 8 ?                        \
      memset(domlist.v2d8, 0, sizeof(*domlist.v2d8) * size) : \
     (hv_versions.dom_interface == 7 ?                        \
      memset(domlist.v2d7, 0, sizeof(*domlist.v2d7) * size) : \
     (hv_versions.dom_interface == 6 ?                        \
      memset(domlist.v2d6, 0, sizeof(*domlist.v2d6) * size) : \
     (hv_versions.dom_interface == 5 ?                        \
      memset(domlist.v2d5, 0, sizeof(*domlist.v2d5) * size) : \
      memset(domlist.v2, 0, sizeof(*domlist.v2) * size))))))

#define XEN_GETDOMAININFOLIST_DOMAIN(domlist, n)    \
    (hv_versions.hypervisor < 2 ?                   \
     domlist.v0[n].domain :                         \
     (hv_versions.dom_interface >= 8 ?              \
      domlist.v2d8[n].domain :                      \
     (hv_versions.dom_interface == 7 ?              \
      domlist.v2d7[n].domain :                      \
     (hv_versions.dom_interface == 6 ?              \
      domlist.v2d6[n].domain :                      \
     (hv_versions.dom_interface == 5 ?              \
      domlist.v2d5[n].domain :                      \
      domlist.v2[n].domain)))))

#define XEN_GETDOMAININFOLIST_UUID(domlist, n)      \
    (hv_versions.hypervisor < 2 ?                   \
     domlist.v0[n].handle :                         \
     (hv_versions.dom_interface >= 8 ?              \
      domlist.v2d8[n].handle :                      \
     (hv_versions.dom_interface == 7 ?              \
      domlist.v2d7[n].handle :                      \
     (hv_versions.dom_interface == 6 ?              \
      domlist.v2d6[n].handle :                      \
     (hv_versions.dom_interface == 5 ?              \
      domlist.v2d5[n].handle :                      \
      domlist.v2[n].handle)))))

#define XEN_GETDOMAININFOLIST_DATA(domlist)        \
    (hv_versions.hypervisor < 2 ?                  \
     (void*)(domlist->v0) :                        \
     (hv_versions.dom_interface >= 8 ?             \
      (void*)(domlist->v2d8) :                     \
     (hv_versions.dom_interface == 7 ?             \
      (void*)(domlist->v2d7) :                     \
     (hv_versions.dom_interface == 6 ?             \
      (void*)(domlist->v2d6) :                     \
     (hv_versions.dom_interface == 5 ?             \
      (void*)(domlist->v2d5) :                     \
      (void*)(domlist->v2))))))

#define XEN_GETDOMAININFO_SIZE                     \
    (hv_versions.hypervisor < 2 ?                  \
     sizeof(xen_v0_getdomaininfo) :                \
     (hv_versions.dom_interface >= 8 ?             \
      sizeof(xen_v2d8_getdomaininfo) :             \
     (hv_versions.dom_interface == 7 ?             \
      sizeof(xen_v2d7_getdomaininfo) :             \
     (hv_versions.dom_interface == 6 ?             \
      sizeof(xen_v2d6_getdomaininfo) :             \
     (hv_versions.dom_interface == 5 ?             \
      sizeof(xen_v2d5_getdomaininfo) :             \
      sizeof(xen_v2_getdomaininfo))))))

#define XEN_GETDOMAININFO_CLEAR(dominfo)                           \
    (hv_versions.hypervisor < 2 ?                                  \
     memset(&(dominfo.v0), 0, sizeof(xen_v0_getdomaininfo)) :      \
     (hv_versions.dom_interface >= 8 ?                             \
      memset(&(dominfo.v2d8), 0, sizeof(xen_v2d8_getdomaininfo)) : \
     (hv_versions.dom_interface == 7 ?                             \
      memset(&(dominfo.v2d7), 0, sizeof(xen_v2d7_getdomaininfo)) : \
     (hv_versions.dom_interface == 6 ?                             \
      memset(&(dominfo.v2d6), 0, sizeof(xen_v2d6_getdomaininfo)) : \
     (hv_versions.dom_interface == 5 ?                             \
      memset(&(dominfo.v2d5), 0, sizeof(xen_v2d5_getdomaininfo)) : \
      memset(&(dominfo.v2), 0, sizeof(xen_v2_getdomaininfo)))))))

#define XEN_GETDOMAININFO_DOMAIN(dominfo)       \
    (hv_versions.hypervisor < 2 ?               \
     dominfo.v0.domain :                        \
     (hv_versions.dom_interface >= 8 ?          \
      dominfo.v2d8.domain :                     \
     (hv_versions.dom_interface == 7 ?          \
      dominfo.v2d7.domain :                     \
     (hv_versions.dom_interface == 6 ?          \
      dominfo.v2d6.domain :                     \
     (hv_versions.dom_interface == 5 ?          \
      dominfo.v2d5.domain :                     \
      dominfo.v2.domain)))))

#define XEN_GETDOMAININFO_CPUTIME(dominfo)      \
    (hv_versions.hypervisor < 2 ?               \
     dominfo.v0.cpu_time :                      \
     (hv_versions.dom_interface >= 8 ?          \
      dominfo.v2d8.cpu_time :                   \
     (hv_versions.dom_interface == 7 ?          \
      dominfo.v2d7.cpu_time :                   \
     (hv_versions.dom_interface == 6 ?          \
      dominfo.v2d6.cpu_time :                   \
     (hv_versions.dom_interface == 5 ?          \
      dominfo.v2d5.cpu_time :                   \
      dominfo.v2.cpu_time)))))


#define XEN_GETDOMAININFO_CPUCOUNT(dominfo)     \
    (hv_versions.hypervisor < 2 ?               \
     dominfo.v0.nr_online_vcpus :               \
     (hv_versions.dom_interface >= 8 ?          \
      dominfo.v2d8.nr_online_vcpus :            \
     (hv_versions.dom_interface == 7 ?          \
      dominfo.v2d7.nr_online_vcpus :            \
     (hv_versions.dom_interface == 6 ?          \
      dominfo.v2d6.nr_online_vcpus :            \
     (hv_versions.dom_interface == 5 ?          \
      dominfo.v2d5.nr_online_vcpus :            \
      dominfo.v2.nr_online_vcpus)))))

#define XEN_GETDOMAININFO_MAXCPUID(dominfo)     \
    (hv_versions.hypervisor < 2 ?               \
     dominfo.v0.max_vcpu_id :                   \
     (hv_versions.dom_interface >= 8 ?          \
      dominfo.v2d8.max_vcpu_id :                \
     (hv_versions.dom_interface == 7 ?          \
      dominfo.v2d7.max_vcpu_id :                \
     (hv_versions.dom_interface == 6 ?          \
      dominfo.v2d6.max_vcpu_id :                \
     (hv_versions.dom_interface == 5 ?          \
      dominfo.v2d5.max_vcpu_id :                \
      dominfo.v2.max_vcpu_id)))))

#define XEN_GETDOMAININFO_FLAGS(dominfo)        \
    (hv_versions.hypervisor < 2 ?               \
     dominfo.v0.flags :                         \
     (hv_versions.dom_interface >= 8 ?          \
      dominfo.v2d8.flags :                      \
     (hv_versions.dom_interface == 7 ?          \
      dominfo.v2d7.flags :                      \
     (hv_versions.dom_interface == 6 ?          \
      dominfo.v2d6.flags :                      \
     (hv_versions.dom_interface == 5 ?          \
      dominfo.v2d5.flags :                      \
      dominfo.v2.flags)))))

#define XEN_GETDOMAININFO_TOT_PAGES(dominfo)    \
    (hv_versions.hypervisor < 2 ?               \
     dominfo.v0.tot_pages :                     \
     (hv_versions.dom_interface >= 8 ?          \
      dominfo.v2d8.tot_pages :                  \
     (hv_versions.dom_interface == 7 ?          \
      dominfo.v2d7.tot_pages :                  \
     (hv_versions.dom_interface == 6 ?          \
      dominfo.v2d6.tot_pages :                  \
     (hv_versions.dom_interface == 5 ?          \
      dominfo.v2d5.tot_pages :                  \
      dominfo.v2.tot_pages)))))

#define XEN_GETDOMAININFO_MAX_PAGES(dominfo)    \
    (hv_versions.hypervisor < 2 ?               \
     dominfo.v0.max_pages :                     \
     (hv_versions.dom_interface >= 8 ?          \
      dominfo.v2d8.max_pages :                  \
     (hv_versions.dom_interface == 7 ?          \
      dominfo.v2d7.max_pages :                  \
     (hv_versions.dom_interface == 6 ?          \
      dominfo.v2d6.max_pages :                  \
     (hv_versions.dom_interface == 5 ?          \
      dominfo.v2d5.max_pages :                  \
      dominfo.v2.max_pages)))))

#define XEN_GETDOMAININFO_UUID(dominfo)         \
    (hv_versions.hypervisor < 2 ?               \
     dominfo.v0.handle :                        \
     (hv_versions.dom_interface >= 8 ?          \
      dominfo.v2d8.handle :                     \
     (hv_versions.dom_interface == 7 ?          \
      dominfo.v2d7.handle :                     \
     (hv_versions.dom_interface == 6 ?          \
      dominfo.v2d6.handle :                     \
     (hv_versions.dom_interface == 5 ?          \
      dominfo.v2d5.handle :                     \
      dominfo.v2.handle)))))


static int
lock_pages(void *addr, size_t len)
{
#ifdef __linux__
        return mlock(addr, len);
#elif defined(__sun)
        return 0;
#endif
}

static int
unlock_pages(void *addr, size_t len)
{
#ifdef __linux__
        return munlock(addr, len);
#elif defined(__sun)
        return 0;
#endif
}


struct xen_v0_getdomaininfolistop {
    domid_t   first_domain;
    uint32_t  max_domains;
    struct xen_v0_getdomaininfo *buffer;
    uint32_t  num_domains;
};
typedef struct xen_v0_getdomaininfolistop xen_v0_getdomaininfolistop;


struct xen_v2_getdomaininfolistop {
    domid_t   first_domain;
    uint32_t  max_domains;
    struct xen_v2_getdomaininfo *buffer;
    uint32_t  num_domains;
};
typedef struct xen_v2_getdomaininfolistop xen_v2_getdomaininfolistop;

/* As of HV version 2, sysctl version 3 the *buffer pointer is 64-bit aligned */
struct xen_v2s3_getdomaininfolistop {
    domid_t   first_domain;
    uint32_t  max_domains;
#ifdef __BIG_ENDIAN__
    struct {
        int __pad[(sizeof(long long) - sizeof(struct xen_v2d5_getdomaininfo *)) / sizeof(int)];
        struct xen_v2d5_getdomaininfo *v;
    } buffer;
#else
    union {
        struct xen_v2d5_getdomaininfo *v;
        uint64_t pad ALIGN_64;
    } buffer;
#endif
    uint32_t  num_domains;
};
typedef struct xen_v2s3_getdomaininfolistop xen_v2s3_getdomaininfolistop;



struct xen_v0_domainop {
    domid_t   domain;
};
typedef struct xen_v0_domainop xen_v0_domainop;

/*
 * The information for a destroydomain system hypercall
 */
#define XEN_V0_OP_DESTROYDOMAIN	9
#define XEN_V1_OP_DESTROYDOMAIN	9
#define XEN_V2_OP_DESTROYDOMAIN	2

/*
 * The information for a pausedomain system hypercall
 */
#define XEN_V0_OP_PAUSEDOMAIN	10
#define XEN_V1_OP_PAUSEDOMAIN	10
#define XEN_V2_OP_PAUSEDOMAIN	3

/*
 * The information for an unpausedomain system hypercall
 */
#define XEN_V0_OP_UNPAUSEDOMAIN	11
#define XEN_V1_OP_UNPAUSEDOMAIN	11
#define XEN_V2_OP_UNPAUSEDOMAIN	4

/*
 * The information for a setmaxmem system hypercall
 */
#define XEN_V0_OP_SETMAXMEM	28
#define XEN_V1_OP_SETMAXMEM	28
#define XEN_V2_OP_SETMAXMEM	11

struct xen_v0_setmaxmem {
    domid_t	domain;
    uint64_t	maxmem;
};
typedef struct xen_v0_setmaxmem xen_v0_setmaxmem;
typedef struct xen_v0_setmaxmem xen_v1_setmaxmem;

struct xen_v2_setmaxmem {
    uint64_t	maxmem;
};
typedef struct xen_v2_setmaxmem xen_v2_setmaxmem;

struct xen_v2d5_setmaxmem {
    uint64_t	maxmem ALIGN_64;
};
typedef struct xen_v2d5_setmaxmem xen_v2d5_setmaxmem;

/*
 * The information for a setmaxvcpu system hypercall
 */
#define XEN_V0_OP_SETMAXVCPU	41
#define XEN_V1_OP_SETMAXVCPU	41
#define XEN_V2_OP_SETMAXVCPU	15

struct xen_v0_setmaxvcpu {
    domid_t	domain;
    uint32_t	maxvcpu;
};
typedef struct xen_v0_setmaxvcpu xen_v0_setmaxvcpu;
typedef struct xen_v0_setmaxvcpu xen_v1_setmaxvcpu;

struct xen_v2_setmaxvcpu {
    uint32_t	maxvcpu;
};
typedef struct xen_v2_setmaxvcpu xen_v2_setmaxvcpu;

/*
 * The information for a setvcpumap system hypercall
 * Note that between 1 and 2 the limitation to 64 physical CPU was lifted
 * hence the difference in structures
 */
#define XEN_V0_OP_SETVCPUMAP	20
#define XEN_V1_OP_SETVCPUMAP	20
#define XEN_V2_OP_SETVCPUMAP	9

struct xen_v0_setvcpumap {
    domid_t	domain;
    uint32_t	vcpu;
    cpumap_t    cpumap;
};
typedef struct xen_v0_setvcpumap xen_v0_setvcpumap;
typedef struct xen_v0_setvcpumap xen_v1_setvcpumap;

struct xen_v2_cpumap {
    uint8_t    *bitmap;
    uint32_t    nr_cpus;
};
struct xen_v2_setvcpumap {
    uint32_t	vcpu;
    struct xen_v2_cpumap cpumap;
};
typedef struct xen_v2_setvcpumap xen_v2_setvcpumap;

/* HV version 2, Dom version 5 requires 64-bit alignment */
struct xen_v2d5_cpumap {
#ifdef __BIG_ENDIAN__
    struct {
        int __pad[(sizeof(long long) - sizeof(uint8_t *)) / sizeof(int)];
        uint8_t *v;
    } bitmap;
#else
    union {
        uint8_t    *v;
        uint64_t   pad ALIGN_64;
    } bitmap;
#endif
    uint32_t    nr_cpus;
};
struct xen_v2d5_setvcpumap {
    uint32_t	vcpu;
    struct xen_v2d5_cpumap cpumap;
};
typedef struct xen_v2d5_setvcpumap xen_v2d5_setvcpumap;

/*
 * The information for a vcpuinfo system hypercall
 */
#define XEN_V0_OP_GETVCPUINFO   43
#define XEN_V1_OP_GETVCPUINFO	43
#define XEN_V2_OP_GETVCPUINFO   14

struct xen_v0_vcpuinfo {
    domid_t	domain;		/* owner's domain */
    uint32_t	vcpu;		/* the vcpu number */
    uint8_t	online;		/* seen as on line */
    uint8_t	blocked;	/* blocked on event */
    uint8_t	running;	/* scheduled on CPU */
    uint64_t    cpu_time;	/* nanosecond of CPU used */
    uint32_t	cpu;		/* current mapping */
    cpumap_t	cpumap;		/* deprecated in V2 */
};
typedef struct xen_v0_vcpuinfo xen_v0_vcpuinfo;
typedef struct xen_v0_vcpuinfo xen_v1_vcpuinfo;

struct xen_v2_vcpuinfo {
    uint32_t	vcpu;		/* the vcpu number */
    uint8_t	online;		/* seen as on line */
    uint8_t	blocked;	/* blocked on event */
    uint8_t	running;	/* scheduled on CPU */
    uint64_t    cpu_time;	/* nanosecond of CPU used */
    uint32_t	cpu;		/* current mapping */
};
typedef struct xen_v2_vcpuinfo xen_v2_vcpuinfo;

struct xen_v2d5_vcpuinfo {
    uint32_t	vcpu;		/* the vcpu number */
    uint8_t	online;		/* seen as on line */
    uint8_t	blocked;	/* blocked on event */
    uint8_t	running;	/* scheduled on CPU */
    uint64_t    cpu_time ALIGN_64; /* nanosecond of CPU used */
    uint32_t	cpu;		/* current mapping */
};
typedef struct xen_v2d5_vcpuinfo xen_v2d5_vcpuinfo;

/*
 * from V2 the pinning of a vcpu is read with a separate call
 */
#define XEN_V2_OP_GETVCPUMAP	25
typedef struct xen_v2_setvcpumap xen_v2_getvcpumap;
typedef struct xen_v2d5_setvcpumap xen_v2d5_getvcpumap;

/*
 * from V2 we get the scheduler information
 */
#define XEN_V2_OP_GETSCHEDULERID	4

/*
 * from V2 we get the available heap information
 */
#define XEN_V2_OP_GETAVAILHEAP		9

/*
 * from V2 we get the scheduler parameter
 */
#define XEN_V2_OP_SCHEDULER		16
/* Scheduler types. */
#define XEN_SCHEDULER_SEDF       4
#define XEN_SCHEDULER_CREDIT     5
/* get/set scheduler parameters */
#define XEN_DOMCTL_SCHEDOP_putinfo 0
#define XEN_DOMCTL_SCHEDOP_getinfo 1

struct xen_v2_setschedinfo {
    uint32_t sched_id;
    uint32_t cmd;
    union {
        struct xen_domctl_sched_sedf {
            uint64_t period ALIGN_64;
            uint64_t slice  ALIGN_64;
            uint64_t latency ALIGN_64;
            uint32_t extratime;
            uint32_t weight;
        } sedf;
        struct xen_domctl_sched_credit {
            uint16_t weight;
            uint16_t cap;
        } credit;
    } u;
};
typedef struct xen_v2_setschedinfo xen_v2_setschedinfo;
typedef struct xen_v2_setschedinfo xen_v2_getschedinfo;


/*
 * The hypercall operation structures also have changed on
 * changeset 86d26e6ec89b
 */
/* the old structure */
struct xen_op_v0 {
    uint32_t cmd;
    uint32_t interface_version;
    union {
        xen_v0_getdomaininfolistop getdomaininfolist;
        xen_v0_domainop          domain;
        xen_v0_setmaxmem         setmaxmem;
        xen_v0_setmaxvcpu        setmaxvcpu;
        xen_v0_setvcpumap        setvcpumap;
        xen_v0_vcpuinfo          getvcpuinfo;
        uint8_t padding[128];
    } u;
};
typedef struct xen_op_v0 xen_op_v0;
typedef struct xen_op_v0 xen_op_v1;

/* the new structure for systems operations */
struct xen_op_v2_sys {
    uint32_t cmd;
    uint32_t interface_version;
    union {
        xen_v2_getdomaininfolistop   getdomaininfolist;
        xen_v2s3_getdomaininfolistop getdomaininfolists3;
        xen_v2_getschedulerid        getschedulerid;
        xen_v2s4_availheap           availheap;
        xen_v2s5_availheap           availheap5;
        uint8_t padding[128];
    } u;
};
typedef struct xen_op_v2_sys xen_op_v2_sys;

/* the new structure for domains operation */
struct xen_op_v2_dom {
    uint32_t cmd;
    uint32_t interface_version;
    domid_t  domain;
    union {
        xen_v2_setmaxmem         setmaxmem;
        xen_v2d5_setmaxmem       setmaxmemd5;
        xen_v2_setmaxvcpu        setmaxvcpu;
        xen_v2_setvcpumap        setvcpumap;
        xen_v2d5_setvcpumap      setvcpumapd5;
        xen_v2_vcpuinfo          getvcpuinfo;
        xen_v2d5_vcpuinfo        getvcpuinfod5;
        xen_v2_getvcpumap        getvcpumap;
        xen_v2d5_getvcpumap      getvcpumapd5;
        xen_v2_setschedinfo      setschedinfo;
        xen_v2_getschedinfo      getschedinfo;
        uint8_t padding[128];
    } u;
};
typedef struct xen_op_v2_dom xen_op_v2_dom;


#ifdef __linux__
# define XEN_HYPERVISOR_SOCKET	"/proc/xen/privcmd"
# define HYPERVISOR_CAPABILITIES	"/sys/hypervisor/properties/capabilities"
#elif defined(__sun)
# define XEN_HYPERVISOR_SOCKET	"/dev/xen/privcmd"
#else
# error "unsupported platform"
#endif

static unsigned long long xenHypervisorGetMaxMemory(virDomainPtr domain);

struct xenUnifiedDriver xenHypervisorDriver = {
    .xenClose = xenHypervisorClose,
    .xenVersion = xenHypervisorGetVersion,
    .xenDomainSuspend = xenHypervisorPauseDomain,
    .xenDomainResume = xenHypervisorResumeDomain,
    .xenDomainDestroyFlags = xenHypervisorDestroyDomainFlags,
    .xenDomainGetOSType = xenHypervisorDomainGetOSType,
    .xenDomainGetMaxMemory = xenHypervisorGetMaxMemory,
    .xenDomainSetMaxMemory = xenHypervisorSetMaxMemory,
    .xenDomainGetInfo = xenHypervisorGetDomainInfo,
    .xenDomainPinVcpu = xenHypervisorPinVcpu,
    .xenDomainGetVcpus = xenHypervisorGetVcpus,
    .xenDomainGetSchedulerType = xenHypervisorGetSchedulerType,
    .xenDomainGetSchedulerParameters = xenHypervisorGetSchedulerParameters,
    .xenDomainSetSchedulerParameters = xenHypervisorSetSchedulerParameters,
};

#define virXenError(code, ...)                                             \
        if (in_init == 0)                                                  \
            virReportErrorHelper(VIR_FROM_XEN, code, __FILE__,             \
                                 __FUNCTION__, __LINE__, __VA_ARGS__)

/**
 * xenHypervisorDoV0Op:
 * @handle: the handle to the Xen hypervisor
 * @op: pointer to the hypervisor operation structure
 *
 * Do a hypervisor operation though the old interface,
 * this leads to a hypervisor call through ioctl.
 *
 * Returns 0 in case of success and -1 in case of error.
 */
static int
xenHypervisorDoV0Op(int handle, xen_op_v0 * op)
{
    int ret;
    v0_hypercall_t hc;

    memset(&hc, 0, sizeof(hc));
    op->interface_version = hv_versions.hv << 8;
    hc.op = __HYPERVISOR_dom0_op;
    hc.arg[0] = (unsigned long) op;

    if (lock_pages(op, sizeof(dom0_op_t)) < 0) {
        virXenError(VIR_ERR_XEN_CALL, " locking");
        return -1;
    }

    ret = ioctl(handle, xen_ioctl_hypercall_cmd, (unsigned long) &hc);
    if (ret < 0) {
        virXenError(VIR_ERR_XEN_CALL, " ioctl %d",
                    xen_ioctl_hypercall_cmd);
    }

    if (unlock_pages(op, sizeof(dom0_op_t)) < 0) {
        virXenError(VIR_ERR_XEN_CALL, " releasing");
        ret = -1;
    }

    if (ret < 0)
        return -1;

    return 0;
}
/**
 * xenHypervisorDoV1Op:
 * @handle: the handle to the Xen hypervisor
 * @op: pointer to the hypervisor operation structure
 *
 * Do a hypervisor v1 operation, this leads to a hypervisor call through
 * ioctl.
 *
 * Returns 0 in case of success and -1 in case of error.
 */
static int
xenHypervisorDoV1Op(int handle, xen_op_v1* op)
{
    int ret;
    hypercall_t hc;

    memset(&hc, 0, sizeof(hc));
    op->interface_version = DOM0_INTERFACE_VERSION;
    hc.op = __HYPERVISOR_dom0_op;
    hc.arg[0] = (unsigned long) op;

    if (lock_pages(op, sizeof(dom0_op_t)) < 0) {
        virXenError(VIR_ERR_XEN_CALL, " locking");
        return -1;
    }

    ret = ioctl(handle, xen_ioctl_hypercall_cmd, (unsigned long) &hc);
    if (ret < 0) {
        virXenError(VIR_ERR_XEN_CALL, " ioctl %d",
                    xen_ioctl_hypercall_cmd);
    }

    if (unlock_pages(op, sizeof(dom0_op_t)) < 0) {
        virXenError(VIR_ERR_XEN_CALL, " releasing");
        ret = -1;
    }

    if (ret < 0)
        return -1;

    return 0;
}

/**
 * xenHypervisorDoV2Sys:
 * @handle: the handle to the Xen hypervisor
 * @op: pointer to the hypervisor operation structure
 *
 * Do a hypervisor v2 system operation, this leads to a hypervisor
 * call through ioctl.
 *
 * Returns 0 in case of success and -1 in case of error.
 */
static int
xenHypervisorDoV2Sys(int handle, xen_op_v2_sys* op)
{
    int ret;
    hypercall_t hc;

    memset(&hc, 0, sizeof(hc));
    op->interface_version = hv_versions.sys_interface;
    hc.op = __HYPERVISOR_sysctl;
    hc.arg[0] = (unsigned long) op;

    if (lock_pages(op, sizeof(dom0_op_t)) < 0) {
        virXenError(VIR_ERR_XEN_CALL, " locking");
        return -1;
    }

    ret = ioctl(handle, xen_ioctl_hypercall_cmd, (unsigned long) &hc);
    if (ret < 0) {
        virXenError(VIR_ERR_XEN_CALL, " sys ioctl %d",
                                            xen_ioctl_hypercall_cmd);
    }

    if (unlock_pages(op, sizeof(dom0_op_t)) < 0) {
        virXenError(VIR_ERR_XEN_CALL, " releasing");
        ret = -1;
    }

    if (ret < 0)
        return -1;

    return 0;
}

/**
 * xenHypervisorDoV2Dom:
 * @handle: the handle to the Xen hypervisor
 * @op: pointer to the hypervisor domain operation structure
 *
 * Do a hypervisor v2 domain operation, this leads to a hypervisor
 * call through ioctl.
 *
 * Returns 0 in case of success and -1 in case of error.
 */
static int
xenHypervisorDoV2Dom(int handle, xen_op_v2_dom* op)
{
    int ret;
    hypercall_t hc;

    memset(&hc, 0, sizeof(hc));
    op->interface_version = hv_versions.dom_interface;
    hc.op = __HYPERVISOR_domctl;
    hc.arg[0] = (unsigned long) op;

    if (lock_pages(op, sizeof(dom0_op_t)) < 0) {
        virXenError(VIR_ERR_XEN_CALL, " locking");
        return -1;
    }

    ret = ioctl(handle, xen_ioctl_hypercall_cmd, (unsigned long) &hc);
    if (ret < 0) {
        virXenError(VIR_ERR_XEN_CALL, " ioctl %d",
                    xen_ioctl_hypercall_cmd);
    }

    if (unlock_pages(op, sizeof(dom0_op_t)) < 0) {
        virXenError(VIR_ERR_XEN_CALL, " releasing");
        ret = -1;
    }

    if (ret < 0)
        return -1;

    return 0;
}

/**
 * virXen_getdomaininfolist:
 * @handle: the hypervisor handle
 * @first_domain: first domain in the range
 * @maxids: maximum number of domains to list
 * @dominfos: output structures
 *
 * Do a low level hypercall to list existing domains information
 *
 * Returns the number of domains or -1 in case of failure
 */
static int
virXen_getdomaininfolist(int handle, int first_domain, int maxids,
                         xen_getdomaininfolist *dominfos)
{
    int ret = -1;

    if (lock_pages(XEN_GETDOMAININFOLIST_DATA(dominfos),
              XEN_GETDOMAININFO_SIZE * maxids) < 0) {
        virXenError(VIR_ERR_XEN_CALL, " locking");
        return -1;
    }
    if (hv_versions.hypervisor > 1) {
        xen_op_v2_sys op;

        memset(&op, 0, sizeof(op));
        op.cmd = XEN_V2_OP_GETDOMAININFOLIST;

        if (hv_versions.sys_interface < 3) {
            op.u.getdomaininfolist.first_domain = (domid_t) first_domain;
            op.u.getdomaininfolist.max_domains = maxids;
            op.u.getdomaininfolist.buffer = dominfos->v2;
            op.u.getdomaininfolist.num_domains = maxids;
        } else {
            op.u.getdomaininfolists3.first_domain = (domid_t) first_domain;
            op.u.getdomaininfolists3.max_domains = maxids;
            op.u.getdomaininfolists3.buffer.v = dominfos->v2d5;
            op.u.getdomaininfolists3.num_domains = maxids;
        }
        ret = xenHypervisorDoV2Sys(handle, &op);

        if (ret == 0) {
            if (hv_versions.sys_interface < 3)
                ret = op.u.getdomaininfolist.num_domains;
            else
                ret = op.u.getdomaininfolists3.num_domains;
        }
    } else if (hv_versions.hypervisor == 1) {
        xen_op_v1 op;

        memset(&op, 0, sizeof(op));
        op.cmd = XEN_V1_OP_GETDOMAININFOLIST;
        op.u.getdomaininfolist.first_domain = (domid_t) first_domain;
        op.u.getdomaininfolist.max_domains = maxids;
        op.u.getdomaininfolist.buffer = dominfos->v0;
        op.u.getdomaininfolist.num_domains = maxids;
        ret = xenHypervisorDoV1Op(handle, &op);
        if (ret == 0)
            ret = op.u.getdomaininfolist.num_domains;
    } else if (hv_versions.hypervisor == 0) {
        xen_op_v0 op;

        memset(&op, 0, sizeof(op));
        op.cmd = XEN_V0_OP_GETDOMAININFOLIST;
        op.u.getdomaininfolist.first_domain = (domid_t) first_domain;
        op.u.getdomaininfolist.max_domains = maxids;
        op.u.getdomaininfolist.buffer = dominfos->v0;
        op.u.getdomaininfolist.num_domains = maxids;
        ret = xenHypervisorDoV0Op(handle, &op);
        if (ret == 0)
            ret = op.u.getdomaininfolist.num_domains;
    }
    if (unlock_pages(XEN_GETDOMAININFOLIST_DATA(dominfos),
                XEN_GETDOMAININFO_SIZE * maxids) < 0) {
        virXenError(VIR_ERR_XEN_CALL, " release");
        ret = -1;
    }
    return ret;
}

static int
virXen_getdomaininfo(int handle, int first_domain,
                     xen_getdomaininfo *dominfo) {
    xen_getdomaininfolist dominfos;

    if (hv_versions.hypervisor < 2) {
        dominfos.v0 = &(dominfo->v0);
    } else {
        dominfos.v2 = &(dominfo->v2);
    }

    return virXen_getdomaininfolist(handle, first_domain, 1, &dominfos);
}


/**
 * xenHypervisorGetSchedulerType:
 * @domain: pointer to the Xen Hypervisor block
 * @nparams:give a number of scheduler parameters.
 *
 * Do a low level hypercall to get scheduler type
 *
 * Returns scheduler name or NULL in case of failure
 */
char *
xenHypervisorGetSchedulerType(virDomainPtr domain, int *nparams)
{
    char *schedulertype = NULL;
    xenUnifiedPrivatePtr priv;

    if (domain->conn == NULL) {
        virXenError(VIR_ERR_INTERNAL_ERROR, "%s",
                    _("domain or conn is NULL"));
        return NULL;
    }

    priv = (xenUnifiedPrivatePtr) domain->conn->privateData;
    if (priv->handle < 0) {
        virXenError(VIR_ERR_INTERNAL_ERROR, "%s",
                    _("priv->handle invalid"));
        return NULL;
    }
    if (domain->id < 0) {
        virXenError(VIR_ERR_OPERATION_INVALID,
                    "%s", _("domain is not running"));
        return NULL;
    }

    /*
     * Support only hv_versions.dom_interface >=5
     * (Xen3.1.0 or later)
     * TODO: check on Xen 3.0.3
     */
    if (hv_versions.dom_interface < 5) {
        virXenError(VIR_ERR_NO_XEN, "%s",
                    _("unsupported in dom interface < 5"));
        return NULL;
    }

    if (hv_versions.hypervisor > 1) {
        xen_op_v2_sys op;
        int ret;

        memset(&op, 0, sizeof(op));
        op.cmd = XEN_V2_OP_GETSCHEDULERID;
        ret = xenHypervisorDoV2Sys(priv->handle, &op);
        if (ret < 0)
            return NULL;

        switch (op.u.getschedulerid.sched_id){
            case XEN_SCHEDULER_SEDF:
                schedulertype = strdup("sedf");
                if (schedulertype == NULL)
                    virReportOOMError();
                if (nparams)
                    *nparams = XEN_SCHED_SEDF_NPARAM;
                break;
            case XEN_SCHEDULER_CREDIT:
                schedulertype = strdup("credit");
                if (schedulertype == NULL)
                    virReportOOMError();
                if (nparams)
                    *nparams = XEN_SCHED_CRED_NPARAM;
                break;
            default:
                break;
        }
    }

    return schedulertype;
}

/**
 * xenHypervisorGetSchedulerParameters:
 * @domain: pointer to the Xen Hypervisor block
 * @params: pointer to scheduler parameters.
 *     This memory area should be allocated before calling.
 * @nparams: this parameter must be at least as large as
 *     the given number of scheduler parameters.
 *     from xenHypervisorGetSchedulerType().
 *
 * Do a low level hypercall to get scheduler parameters
 *
 * Returns 0 or -1 in case of failure
 */
int
xenHypervisorGetSchedulerParameters(virDomainPtr domain,
                                    virTypedParameterPtr params, int *nparams)
{
    xenUnifiedPrivatePtr priv;

    if (domain->conn == NULL) {
        virXenError(VIR_ERR_INTERNAL_ERROR, "%s",
                    _("domain or conn is NULL"));
        return -1;
    }

    priv = (xenUnifiedPrivatePtr) domain->conn->privateData;
    if (priv->handle < 0) {
        virXenError(VIR_ERR_INTERNAL_ERROR, "%s",
                    _("priv->handle invalid"));
        return -1;
    }
    if (domain->id < 0) {
        virXenError(VIR_ERR_OPERATION_INVALID,
                    "%s", _("domain is not running"));
        return -1;
    }

    /*
     * Support only hv_versions.dom_interface >=5
     * (Xen3.1.0 or later)
     * TODO: check on Xen 3.0.3
     */
    if (hv_versions.dom_interface < 5) {
        virXenError(VIR_ERR_NO_XEN, "%s",
                    _("unsupported in dom interface < 5"));
        return -1;
    }

    if (hv_versions.hypervisor > 1) {
        xen_op_v2_sys op_sys;
        xen_op_v2_dom op_dom;
        int ret;

        memset(&op_sys, 0, sizeof(op_sys));
        op_sys.cmd = XEN_V2_OP_GETSCHEDULERID;
        ret = xenHypervisorDoV2Sys(priv->handle, &op_sys);
        if (ret < 0)
            return -1;

        switch (op_sys.u.getschedulerid.sched_id){
            case XEN_SCHEDULER_SEDF:
                if (*nparams < XEN_SCHED_SEDF_NPARAM) {
                    virXenError(VIR_ERR_INVALID_ARG,
                                "%s", _("Invalid parameter count"));
                    return -1;
                }

                /* TODO: Implement for Xen/SEDF */
                TODO
                return -1;
            case XEN_SCHEDULER_CREDIT:
                memset(&op_dom, 0, sizeof(op_dom));
                op_dom.cmd = XEN_V2_OP_SCHEDULER;
                op_dom.domain = (domid_t) domain->id;
                op_dom.u.getschedinfo.sched_id = XEN_SCHEDULER_CREDIT;
                op_dom.u.getschedinfo.cmd = XEN_DOMCTL_SCHEDOP_getinfo;
                ret = xenHypervisorDoV2Dom(priv->handle, &op_dom);
                if (ret < 0)
                    return -1;

                if (virTypedParameterAssign(&params[0],
                                            VIR_DOMAIN_SCHEDULER_WEIGHT,
                                            VIR_TYPED_PARAM_UINT,
                                            op_dom.u.getschedinfo.u.credit.weight) < 0)
                    return -1;

                if (*nparams > 1 &&
                    virTypedParameterAssign(&params[1],
                                            VIR_DOMAIN_SCHEDULER_CAP,
                                            VIR_TYPED_PARAM_UINT,
                                            op_dom.u.getschedinfo.u.credit.cap) < 0)
                        return -1;

                if (*nparams > XEN_SCHED_CRED_NPARAM)
                    *nparams = XEN_SCHED_CRED_NPARAM;
                break;
            default:
                virXenError(VIR_ERR_INVALID_ARG,
                            _("Unknown scheduler %d"),
                            op_sys.u.getschedulerid.sched_id);
                return -1;
        }
    }

    return 0;
}

/**
 * xenHypervisorSetSchedulerParameters:
 * @domain: pointer to the Xen Hypervisor block
 * @nparams:give a number of scheduler setting parameters .
 *
 * Do a low level hypercall to set scheduler parameters
 *
 * Returns 0 or -1 in case of failure
 */
int
xenHypervisorSetSchedulerParameters(virDomainPtr domain,
                                    virTypedParameterPtr params, int nparams)
{
    int i;
    unsigned int val;
    xenUnifiedPrivatePtr priv;
    char buf[256];

    if (domain->conn == NULL) {
        virXenError(VIR_ERR_INTERNAL_ERROR, "%s",
                    _("domain or conn is NULL"));
        return -1;
    }

    if (nparams == 0) {
        /* nothing to do, exit early */
        return 0;
    }

    if (virTypedParameterArrayValidate(params, nparams,
                                       VIR_DOMAIN_SCHEDULER_WEIGHT,
                                       VIR_TYPED_PARAM_UINT,
                                       VIR_DOMAIN_SCHEDULER_CAP,
                                       VIR_TYPED_PARAM_UINT,
                                       NULL) < 0)
        return -1;

    priv = (xenUnifiedPrivatePtr) domain->conn->privateData;
    if (priv->handle < 0) {
        virXenError(VIR_ERR_INTERNAL_ERROR, "%s",
                    _("priv->handle invalid"));
        return -1;
    }
    if (domain->id < 0) {
        virXenError(VIR_ERR_OPERATION_INVALID,
                    "%s", _("domain is not running"));
        return -1;
    }

    /*
     * Support only hv_versions.dom_interface >=5
     * (Xen3.1.0 or later)
     * TODO: check on Xen 3.0.3
     */
    if (hv_versions.dom_interface < 5) {
        virXenError(VIR_ERR_NO_XEN, "%s",
                    _("unsupported in dom interface < 5"));
        return -1;
    }

    if (hv_versions.hypervisor > 1) {
        xen_op_v2_sys op_sys;
        xen_op_v2_dom op_dom;
        int ret;

        memset(&op_sys, 0, sizeof(op_sys));
        op_sys.cmd = XEN_V2_OP_GETSCHEDULERID;
        ret = xenHypervisorDoV2Sys(priv->handle, &op_sys);
        if (ret == -1) return -1;

        switch (op_sys.u.getschedulerid.sched_id){
        case XEN_SCHEDULER_SEDF:
            /* TODO: Implement for Xen/SEDF */
            TODO
            return -1;
        case XEN_SCHEDULER_CREDIT: {
            memset(&op_dom, 0, sizeof(op_dom));
            op_dom.cmd = XEN_V2_OP_SCHEDULER;
            op_dom.domain = (domid_t) domain->id;
            op_dom.u.getschedinfo.sched_id = XEN_SCHEDULER_CREDIT;
            op_dom.u.getschedinfo.cmd = XEN_DOMCTL_SCHEDOP_putinfo;

            /*
             * credit scheduler parameters
             * following values do not change the parameters
             */
            op_dom.u.getschedinfo.u.credit.weight = 0;
            op_dom.u.getschedinfo.u.credit.cap    = (uint16_t)~0U;

            for (i = 0; i < nparams; i++) {
                memset(&buf, 0, sizeof(buf));
                if (STREQ(params[i].field, VIR_DOMAIN_SCHEDULER_WEIGHT)) {
                    val = params[i].value.ui;
                    if ((val < 1) || (val > USHRT_MAX)) {
                        virXenError(VIR_ERR_INVALID_ARG,
                                    _("Credit scheduler weight parameter (%d) "
                                      "is out of range (1-65535)"), val);
                        return -1;
                    }
                    op_dom.u.getschedinfo.u.credit.weight = val;
                } else if (STREQ(params[i].field, VIR_DOMAIN_SCHEDULER_CAP)) {
                    val = params[i].value.ui;
                    if (val >= USHRT_MAX) {
                        virXenError(VIR_ERR_INVALID_ARG,
                                    _("Credit scheduler cap parameter (%d) is "
                                      "out of range (0-65534)"), val);
                        return -1;
                    }
                    op_dom.u.getschedinfo.u.credit.cap = val;
                }
            }

            ret = xenHypervisorDoV2Dom(priv->handle, &op_dom);
            if (ret < 0)
                return -1;
            break;
        }
        default:
            virXenError(VIR_ERR_INVALID_ARG,
                        _("Unknown scheduler %d"),
                        op_sys.u.getschedulerid.sched_id);
            return -1;
        }
    }

    return 0;
}


int
xenHypervisorDomainBlockStats (virDomainPtr dom,
                               const char *path,
                               struct _virDomainBlockStats *stats)
{
#ifdef __linux__
    xenUnifiedPrivatePtr priv;
    int ret;

    priv = (xenUnifiedPrivatePtr) dom->conn->privateData;
    xenUnifiedLock(priv);
    /* Need to lock because it hits the xenstore handle :-( */
    ret = xenLinuxDomainBlockStats (priv, dom, path, stats);
    xenUnifiedUnlock(priv);
    return ret;
#else
    virXenError(VIR_ERR_OPERATION_INVALID, "%s",
                _("block statistics not supported on this platform"));
    return -1;
#endif
}

/* Paths have the form vif<domid>.<n> (this interface checks that
 * <domid> is the real domain ID and returns an error if not).
 *
 * In future we may allow you to query bridge stats (virbrX or
 * xenbrX), but that will probably be through a separate
 * virNetwork interface, as yet not decided.
 */
int
xenHypervisorDomainInterfaceStats (virDomainPtr dom,
                                   const char *path,
                                   struct _virDomainInterfaceStats *stats)
{
#ifdef __linux__
    int rqdomid, device;

    /* Verify that the vif requested is one belonging to the current
     * domain.
     */
    if (sscanf(path, "vif%d.%d", &rqdomid, &device) != 2) {
        virXenError(VIR_ERR_INVALID_ARG, "%s",
                    _("invalid path, should be vif<domid>.<n>."));
        return -1;
    }
    if (rqdomid != dom->id) {
        virXenError(VIR_ERR_INVALID_ARG, "%s",
                    _("invalid path, vif<domid> should match this domain ID"));
        return -1;
    }

    return linuxDomainInterfaceStats(path, stats);
#else
    virXenError(VIR_ERR_OPERATION_INVALID, "%s",
                _("/proc/net/dev: Interface not found"));
    return -1;
#endif
}

/**
 * virXen_pausedomain:
 * @handle: the hypervisor handle
 * @id: the domain id
 *
 * Do a low level hypercall to pause the domain
 *
 * Returns 0 or -1 in case of failure
 */
static int
virXen_pausedomain(int handle, int id)
{
    int ret = -1;

    if (hv_versions.hypervisor > 1) {
        xen_op_v2_dom op;

        memset(&op, 0, sizeof(op));
        op.cmd = XEN_V2_OP_PAUSEDOMAIN;
        op.domain = (domid_t) id;
        ret = xenHypervisorDoV2Dom(handle, &op);
    } else if (hv_versions.hypervisor == 1) {
        xen_op_v1 op;

        memset(&op, 0, sizeof(op));
        op.cmd = XEN_V1_OP_PAUSEDOMAIN;
        op.u.domain.domain = (domid_t) id;
        ret = xenHypervisorDoV1Op(handle, &op);
    } else if (hv_versions.hypervisor == 0) {
        xen_op_v0 op;

        memset(&op, 0, sizeof(op));
        op.cmd = XEN_V0_OP_PAUSEDOMAIN;
        op.u.domain.domain = (domid_t) id;
        ret = xenHypervisorDoV0Op(handle, &op);
    }
    return ret;
}

/**
 * virXen_unpausedomain:
 * @handle: the hypervisor handle
 * @id: the domain id
 *
 * Do a low level hypercall to unpause the domain
 *
 * Returns 0 or -1 in case of failure
 */
static int
virXen_unpausedomain(int handle, int id)
{
    int ret = -1;

    if (hv_versions.hypervisor > 1) {
        xen_op_v2_dom op;

        memset(&op, 0, sizeof(op));
        op.cmd = XEN_V2_OP_UNPAUSEDOMAIN;
        op.domain = (domid_t) id;
        ret = xenHypervisorDoV2Dom(handle, &op);
    } else if (hv_versions.hypervisor == 1) {
        xen_op_v1 op;

        memset(&op, 0, sizeof(op));
        op.cmd = XEN_V1_OP_UNPAUSEDOMAIN;
        op.u.domain.domain = (domid_t) id;
        ret = xenHypervisorDoV1Op(handle, &op);
    } else if (hv_versions.hypervisor == 0) {
        xen_op_v0 op;

        memset(&op, 0, sizeof(op));
        op.cmd = XEN_V0_OP_UNPAUSEDOMAIN;
        op.u.domain.domain = (domid_t) id;
        ret = xenHypervisorDoV0Op(handle, &op);
    }
    return ret;
}

/**
 * virXen_destroydomain:
 * @handle: the hypervisor handle
 * @id: the domain id
 *
 * Do a low level hypercall to destroy the domain
 *
 * Returns 0 or -1 in case of failure
 */
static int
virXen_destroydomain(int handle, int id)
{
    int ret = -1;

    if (hv_versions.hypervisor > 1) {
        xen_op_v2_dom op;

        memset(&op, 0, sizeof(op));
        op.cmd = XEN_V2_OP_DESTROYDOMAIN;
        op.domain = (domid_t) id;
        ret = xenHypervisorDoV2Dom(handle, &op);
    } else if (hv_versions.hypervisor == 1) {
        xen_op_v1 op;

        memset(&op, 0, sizeof(op));
        op.cmd = XEN_V1_OP_DESTROYDOMAIN;
        op.u.domain.domain = (domid_t) id;
        ret = xenHypervisorDoV1Op(handle, &op);
    } else if (hv_versions.hypervisor == 0) {
        xen_op_v0 op;

        memset(&op, 0, sizeof(op));
        op.cmd = XEN_V0_OP_DESTROYDOMAIN;
        op.u.domain.domain = (domid_t) id;
        ret = xenHypervisorDoV0Op(handle, &op);
    }
    return ret;
}

/**
 * virXen_setmaxmem:
 * @handle: the hypervisor handle
 * @id: the domain id
 * @memory: the amount of memory in kilobytes
 *
 * Do a low level hypercall to change the max memory amount
 *
 * Returns 0 or -1 in case of failure
 */
static int
virXen_setmaxmem(int handle, int id, unsigned long memory)
{
    int ret = -1;

    if (hv_versions.hypervisor > 1) {
        xen_op_v2_dom op;

        memset(&op, 0, sizeof(op));
        op.cmd = XEN_V2_OP_SETMAXMEM;
        op.domain = (domid_t) id;
        if (hv_versions.dom_interface < 5)
            op.u.setmaxmem.maxmem = memory;
        else
            op.u.setmaxmemd5.maxmem = memory;
        ret = xenHypervisorDoV2Dom(handle, &op);
    } else if (hv_versions.hypervisor == 1) {
        xen_op_v1 op;

        memset(&op, 0, sizeof(op));
        op.cmd = XEN_V1_OP_SETMAXMEM;
        op.u.setmaxmem.domain = (domid_t) id;
        op.u.setmaxmem.maxmem = memory;
        ret = xenHypervisorDoV1Op(handle, &op);
    } else if (hv_versions.hypervisor == 0) {
        xen_op_v0 op;

        memset(&op, 0, sizeof(op));
        op.cmd = XEN_V0_OP_SETMAXMEM;
        op.u.setmaxmem.domain = (domid_t) id;
        op.u.setmaxmem.maxmem = memory;
        ret = xenHypervisorDoV0Op(handle, &op);
    }
    return ret;
}

/**
 * virXen_setmaxvcpus:
 * @handle: the hypervisor handle
 * @id: the domain id
 * @vcpus: the numbers of vcpus
 *
 * Do a low level hypercall to change the max vcpus amount
 *
 * Returns 0 or -1 in case of failure
 */
static int
virXen_setmaxvcpus(int handle, int id, unsigned int vcpus)
{
    int ret = -1;

    if (hv_versions.hypervisor > 1) {
        xen_op_v2_dom op;

        memset(&op, 0, sizeof(op));
        op.cmd = XEN_V2_OP_SETMAXVCPU;
        op.domain = (domid_t) id;
        op.u.setmaxvcpu.maxvcpu = vcpus;
        ret = xenHypervisorDoV2Dom(handle, &op);
    } else if (hv_versions.hypervisor == 1) {
        xen_op_v1 op;

        memset(&op, 0, sizeof(op));
        op.cmd = XEN_V1_OP_SETMAXVCPU;
        op.u.setmaxvcpu.domain = (domid_t) id;
        op.u.setmaxvcpu.maxvcpu = vcpus;
        ret = xenHypervisorDoV1Op(handle, &op);
    } else if (hv_versions.hypervisor == 0) {
        xen_op_v0 op;

        memset(&op, 0, sizeof(op));
        op.cmd = XEN_V0_OP_SETMAXVCPU;
        op.u.setmaxvcpu.domain = (domid_t) id;
        op.u.setmaxvcpu.maxvcpu = vcpus;
        ret = xenHypervisorDoV0Op(handle, &op);
    }
    return ret;
}

/**
 * virXen_setvcpumap:
 * @handle: the hypervisor handle
 * @id: the domain id
 * @vcpu: the vcpu to map
 * @cpumap: the bitmap for this vcpu
 * @maplen: the size of the bitmap in bytes
 *
 * Do a low level hypercall to change the pinning for vcpu
 *
 * Returns 0 or -1 in case of failure
 */
static int
virXen_setvcpumap(int handle, int id, unsigned int vcpu,
                  unsigned char * cpumap, int maplen)
{
    int ret = -1;
    unsigned char *new = NULL;
    unsigned char *bitmap = NULL;
    uint32_t nr_cpus;

    if (hv_versions.hypervisor > 1) {
        xen_op_v2_dom op;

        if (lock_pages(cpumap, maplen) < 0) {
            virXenError(VIR_ERR_XEN_CALL, " locking");
            return -1;
        }
        memset(&op, 0, sizeof(op));
        op.cmd = XEN_V2_OP_SETVCPUMAP;
        op.domain = (domid_t) id;

        /* The allocated memory to cpumap must be 'sizeof(uint64_t)' byte *
         * for Xen, and also nr_cpus must be 'sizeof(uint64_t) * 8'       */
        if (maplen < 8) {
            if (VIR_ALLOC_N(new, sizeof(uint64_t)) < 0) {
                virReportOOMError();
                return -1;
            }
            memcpy(new, cpumap, maplen);
            bitmap = new;
            nr_cpus = sizeof(uint64_t) * 8;
        } else {
            bitmap = cpumap;
            nr_cpus = maplen * 8;
        }

        if (hv_versions.dom_interface < 5) {
            op.u.setvcpumap.vcpu = vcpu;
            op.u.setvcpumap.cpumap.bitmap = bitmap;
            op.u.setvcpumap.cpumap.nr_cpus = nr_cpus;
        } else {
            op.u.setvcpumapd5.vcpu = vcpu;
            op.u.setvcpumapd5.cpumap.bitmap.v = bitmap;
            op.u.setvcpumapd5.cpumap.nr_cpus = nr_cpus;
        }
        ret = xenHypervisorDoV2Dom(handle, &op);
        VIR_FREE(new);

        if (unlock_pages(cpumap, maplen) < 0) {
            virXenError(VIR_ERR_XEN_CALL, " release");
            ret = -1;
        }
    } else {
        cpumap_t xen_cpumap; /* limited to 64 CPUs in old hypervisors */
        uint64_t *pm = &xen_cpumap;
        int j;

        if ((maplen > (int)sizeof(cpumap_t)) || (sizeof(cpumap_t) & 7))
            return -1;

        memset(pm, 0, sizeof(cpumap_t));
        for (j = 0; j < maplen; j++)
            *(pm + (j / 8)) |= cpumap[j] << (8 * (j & 7));

        if (hv_versions.hypervisor == 1) {
            xen_op_v1 op;

            memset(&op, 0, sizeof(op));
            op.cmd = XEN_V1_OP_SETVCPUMAP;
            op.u.setvcpumap.domain = (domid_t) id;
            op.u.setvcpumap.vcpu = vcpu;
            op.u.setvcpumap.cpumap = xen_cpumap;
            ret = xenHypervisorDoV1Op(handle, &op);
        } else if (hv_versions.hypervisor == 0) {
            xen_op_v0 op;

            memset(&op, 0, sizeof(op));
            op.cmd = XEN_V0_OP_SETVCPUMAP;
            op.u.setvcpumap.domain = (domid_t) id;
            op.u.setvcpumap.vcpu = vcpu;
            op.u.setvcpumap.cpumap = xen_cpumap;
            ret = xenHypervisorDoV0Op(handle, &op);
        }
    }
    return ret;
}


/**
 * virXen_getvcpusinfo:
 * @handle: the hypervisor handle
 * @id: the domain id
 * @vcpu: the vcpu to map
 * @cpumap: the bitmap for this vcpu
 * @maplen: the size of the bitmap in bytes
 *
 * Do a low level hypercall to change the pinning for vcpu
 *
 * Returns 0 or -1 in case of failure
 */
static int
virXen_getvcpusinfo(int handle, int id, unsigned int vcpu, virVcpuInfoPtr ipt,
                    unsigned char *cpumap, int maplen)
{
    int ret = -1;

    if (hv_versions.hypervisor > 1) {
        xen_op_v2_dom op;

        memset(&op, 0, sizeof(op));
        op.cmd = XEN_V2_OP_GETVCPUINFO;
        op.domain = (domid_t) id;
        if (hv_versions.dom_interface < 5)
            op.u.getvcpuinfo.vcpu = (uint16_t) vcpu;
        else
            op.u.getvcpuinfod5.vcpu = (uint16_t) vcpu;
        ret = xenHypervisorDoV2Dom(handle, &op);

        if (ret < 0)
            return -1;
        ipt->number = vcpu;
        if (hv_versions.dom_interface < 5) {
            if (op.u.getvcpuinfo.online) {
                if (op.u.getvcpuinfo.running)
                    ipt->state = VIR_VCPU_RUNNING;
                if (op.u.getvcpuinfo.blocked)
                    ipt->state = VIR_VCPU_BLOCKED;
            } else
                ipt->state = VIR_VCPU_OFFLINE;

            ipt->cpuTime = op.u.getvcpuinfo.cpu_time;
            ipt->cpu = op.u.getvcpuinfo.online ? (int)op.u.getvcpuinfo.cpu : -1;
        } else {
            if (op.u.getvcpuinfod5.online) {
                if (op.u.getvcpuinfod5.running)
                    ipt->state = VIR_VCPU_RUNNING;
                if (op.u.getvcpuinfod5.blocked)
                    ipt->state = VIR_VCPU_BLOCKED;
            } else
                ipt->state = VIR_VCPU_OFFLINE;

            ipt->cpuTime = op.u.getvcpuinfod5.cpu_time;
            ipt->cpu = op.u.getvcpuinfod5.online ? (int)op.u.getvcpuinfod5.cpu : -1;
        }
        if ((cpumap != NULL) && (maplen > 0)) {
            if (lock_pages(cpumap, maplen) < 0) {
                virXenError(VIR_ERR_XEN_CALL, " locking");
                return -1;
            }
            memset(cpumap, 0, maplen);
            memset(&op, 0, sizeof(op));
            op.cmd = XEN_V2_OP_GETVCPUMAP;
            op.domain = (domid_t) id;
            if (hv_versions.dom_interface < 5) {
                op.u.getvcpumap.vcpu = vcpu;
                op.u.getvcpumap.cpumap.bitmap = cpumap;
                op.u.getvcpumap.cpumap.nr_cpus = maplen * 8;
            } else {
                op.u.getvcpumapd5.vcpu = vcpu;
                op.u.getvcpumapd5.cpumap.bitmap.v = cpumap;
                op.u.getvcpumapd5.cpumap.nr_cpus = maplen * 8;
            }
            ret = xenHypervisorDoV2Dom(handle, &op);
            if (unlock_pages(cpumap, maplen) < 0) {
                virXenError(VIR_ERR_XEN_CALL, " release");
                ret = -1;
            }
        }
    } else {
        int mapl = maplen;
        int cpu;

        if (maplen > (int)sizeof(cpumap_t))
            mapl = (int)sizeof(cpumap_t);

        if (hv_versions.hypervisor == 1) {
            xen_op_v1 op;

            memset(&op, 0, sizeof(op));
            op.cmd = XEN_V1_OP_GETVCPUINFO;
            op.u.getvcpuinfo.domain = (domid_t) id;
            op.u.getvcpuinfo.vcpu = vcpu;
            ret = xenHypervisorDoV1Op(handle, &op);
            if (ret < 0)
                return -1;
            ipt->number = vcpu;
            if (op.u.getvcpuinfo.online) {
                if (op.u.getvcpuinfo.running) ipt->state = VIR_VCPU_RUNNING;
                if (op.u.getvcpuinfo.blocked) ipt->state = VIR_VCPU_BLOCKED;
            }
            else ipt->state = VIR_VCPU_OFFLINE;
            ipt->cpuTime = op.u.getvcpuinfo.cpu_time;
            ipt->cpu = op.u.getvcpuinfo.online ? (int)op.u.getvcpuinfo.cpu : -1;
            if ((cpumap != NULL) && (maplen > 0)) {
                for (cpu = 0; cpu < (mapl * 8); cpu++) {
                    if (op.u.getvcpuinfo.cpumap & ((uint64_t)1<<cpu))
                        VIR_USE_CPU(cpumap, cpu);
                }
            }
        } else if (hv_versions.hypervisor == 0) {
            xen_op_v1 op;

            memset(&op, 0, sizeof(op));
            op.cmd = XEN_V0_OP_GETVCPUINFO;
            op.u.getvcpuinfo.domain = (domid_t) id;
            op.u.getvcpuinfo.vcpu = vcpu;
            ret = xenHypervisorDoV0Op(handle, &op);
            if (ret < 0)
                return -1;
            ipt->number = vcpu;
            if (op.u.getvcpuinfo.online) {
                if (op.u.getvcpuinfo.running) ipt->state = VIR_VCPU_RUNNING;
                if (op.u.getvcpuinfo.blocked) ipt->state = VIR_VCPU_BLOCKED;
            }
            else ipt->state = VIR_VCPU_OFFLINE;
            ipt->cpuTime = op.u.getvcpuinfo.cpu_time;
            ipt->cpu = op.u.getvcpuinfo.online ? (int)op.u.getvcpuinfo.cpu : -1;
            if ((cpumap != NULL) && (maplen > 0)) {
                for (cpu = 0; cpu < (mapl * 8); cpu++) {
                    if (op.u.getvcpuinfo.cpumap & ((uint64_t)1<<cpu))
                        VIR_USE_CPU(cpumap, cpu);
                }
            }
        }
    }
    return ret;
}

/**
 * xenHypervisorInit:
 * @override_versions: pointer to optional struct xenHypervisorVersions with
 *     version information used instead of automatic version detection.
 *
 * Initialize the hypervisor layer. Try to detect the kind of interface
 * used i.e. pre or post changeset 10277
 *
 * Returns 0 or -1 in case of failure
 */
int
xenHypervisorInit(struct xenHypervisorVersions *override_versions)
{
    int fd, ret, cmd, errcode;
    hypercall_t hc;
    v0_hypercall_t v0_hc;
    xen_getdomaininfo info;
    virVcpuInfoPtr ipt = NULL;

    if (initialized) {
        if (hv_versions.hypervisor == -1)
            return -1;
        return 0;
    }
    initialized = 1;
    in_init = 1;

    /* Compile regular expressions used by xenHypervisorGetCapabilities.
     * Note that errors here are really internal errors since these
     * regexps should never fail to compile.
     */
    errcode = regcomp (&flags_hvm_rec, flags_hvm_re, REG_EXTENDED);
    if (errcode != 0) {
        char error[100];
        regerror (errcode, &flags_hvm_rec, error, sizeof(error));
        regfree (&flags_hvm_rec);
        virXenError(VIR_ERR_INTERNAL_ERROR, "%s", error);
        in_init = 0;
        return -1;
    }
    errcode = regcomp (&flags_pae_rec, flags_pae_re, REG_EXTENDED);
    if (errcode != 0) {
        char error[100];
        regerror (errcode, &flags_pae_rec, error, sizeof(error));
        regfree (&flags_pae_rec);
        regfree (&flags_hvm_rec);
        virXenError(VIR_ERR_INTERNAL_ERROR, "%s", error);
        in_init = 0;
        return -1;
    }
    errcode = regcomp (&xen_cap_rec, xen_cap_re, REG_EXTENDED);
    if (errcode != 0) {
        char error[100];
        regerror (errcode, &xen_cap_rec, error, sizeof(error));
        regfree (&xen_cap_rec);
        regfree (&flags_pae_rec);
        regfree (&flags_hvm_rec);
        virXenError(VIR_ERR_INTERNAL_ERROR, "%s", error);
        in_init = 0;
        return -1;
    }

    if (override_versions) {
      hv_versions = *override_versions;
      in_init = 0;
      return 0;
    }

    /* Xen hypervisor version detection begins. */
    ret = open(XEN_HYPERVISOR_SOCKET, O_RDWR);
    if (ret < 0) {
        hv_versions.hypervisor = -1;
        return -1;
    }
    fd = ret;

    /*
     * The size of the hypervisor call block changed July 2006
     * this detect if we are using the new or old hypercall_t structure
     */
    hc.op = __HYPERVISOR_xen_version;
    hc.arg[0] = (unsigned long) XENVER_version;
    hc.arg[1] = 0;

    cmd = IOCTL_PRIVCMD_HYPERCALL;
    ret = ioctl(fd, cmd, (unsigned long) &hc);

    if ((ret != -1) && (ret != 0)) {
        VIR_DEBUG("Using new hypervisor call: %X", ret);
        hv_versions.hv = ret;
        xen_ioctl_hypercall_cmd = cmd;
        goto detect_v2;
    }

#ifndef __sun
    /*
     * check if the old hypercall are actually working
     */
    v0_hc.op = __HYPERVISOR_xen_version;
    v0_hc.arg[0] = (unsigned long) XENVER_version;
    v0_hc.arg[1] = 0;
    cmd = _IOC(_IOC_NONE, 'P', 0, sizeof(v0_hypercall_t));
    ret = ioctl(fd, cmd, (unsigned long) &v0_hc);
    if ((ret != -1) && (ret != 0)) {
        VIR_DEBUG("Using old hypervisor call: %X", ret);
        hv_versions.hv = ret;
        xen_ioctl_hypercall_cmd = cmd;
        hv_versions.hypervisor = 0;
        goto done;
    }
#endif

    /*
     * we failed to make any hypercall
     */

    hv_versions.hypervisor = -1;
    virXenError(VIR_ERR_XEN_CALL, " ioctl %lu",
                (unsigned long) IOCTL_PRIVCMD_HYPERCALL);
    VIR_FORCE_CLOSE(fd);
    in_init = 0;
    return -1;

 detect_v2:
    /*
     * The hypercalls were refactored into 3 different section in August 2006
     * Try to detect if we are running a version post 3.0.2 with the new ones
     * or the old ones
     */
    hv_versions.hypervisor = 2;

    if (VIR_ALLOC(ipt) < 0) {
        virReportOOMError();
        return -1;
    }
    /* Currently consider RHEL5.0 Fedora7, xen-3.1, and xen-unstable */
    hv_versions.sys_interface = 2; /* XEN_SYSCTL_INTERFACE_VERSION */
    if (virXen_getdomaininfo(fd, 0, &info) == 1) {
        /* RHEL 5.0 */
        hv_versions.dom_interface = 3; /* XEN_DOMCTL_INTERFACE_VERSION */
        if (virXen_getvcpusinfo(fd, 0, 0, ipt, NULL, 0) == 0){
            VIR_DEBUG("Using hypervisor call v2, sys ver2 dom ver3");
            goto done;
        }
        /* Fedora 7 */
        hv_versions.dom_interface = 4; /* XEN_DOMCTL_INTERFACE_VERSION */
        if (virXen_getvcpusinfo(fd, 0, 0, ipt, NULL, 0) == 0){
            VIR_DEBUG("Using hypervisor call v2, sys ver2 dom ver4");
            goto done;
        }
    }

    hv_versions.sys_interface = 3; /* XEN_SYSCTL_INTERFACE_VERSION */
    if (virXen_getdomaininfo(fd, 0, &info) == 1) {
        /* xen-3.1 */
        hv_versions.dom_interface = 5; /* XEN_DOMCTL_INTERFACE_VERSION */
        if (virXen_getvcpusinfo(fd, 0, 0, ipt, NULL, 0) == 0){
            VIR_DEBUG("Using hypervisor call v2, sys ver3 dom ver5");
            goto done;
        }
    }

    hv_versions.sys_interface = 4; /* XEN_SYSCTL_INTERFACE_VERSION */
    if (virXen_getdomaininfo(fd, 0, &info) == 1) {
        /* Fedora 8 */
        hv_versions.dom_interface = 5; /* XEN_DOMCTL_INTERFACE_VERSION */
        if (virXen_getvcpusinfo(fd, 0, 0, ipt, NULL, 0) == 0){
            VIR_DEBUG("Using hypervisor call v2, sys ver4 dom ver5");
            goto done;
        }
    }

    hv_versions.sys_interface = 6; /* XEN_SYSCTL_INTERFACE_VERSION */
    if (virXen_getdomaininfo(fd, 0, &info) == 1) {
        /* Xen 3.2, Fedora 9 */
        hv_versions.dom_interface = 5; /* XEN_DOMCTL_INTERFACE_VERSION */
        if (virXen_getvcpusinfo(fd, 0, 0, ipt, NULL, 0) == 0){
            VIR_DEBUG("Using hypervisor call v2, sys ver6 dom ver5");
            goto done;
        }
    }

    /* Xen 4.0 */
    hv_versions.sys_interface = 7; /* XEN_SYSCTL_INTERFACE_VERSION */
    if (virXen_getdomaininfo(fd, 0, &info) == 1) {
        hv_versions.dom_interface = 6; /* XEN_DOMCTL_INTERFACE_VERSION */
        VIR_DEBUG("Using hypervisor call v2, sys ver7 dom ver6");
        goto done;
    }

    /* Xen 4.1
     * sysctl version 8 -> xen-unstable c/s 21118:28e5409e3fb3
     * domctl version 7 -> xen-unstable c/s 21212:de94884a669c
     * domctl version 8 -> xen-unstable c/s 23874:651aed73b39c
     */
    hv_versions.sys_interface = 8; /* XEN_SYSCTL_INTERFACE_VERSION */
    if (virXen_getdomaininfo(fd, 0, &info) == 1) {
        hv_versions.dom_interface = 7; /* XEN_DOMCTL_INTERFACE_VERSION */
        if (virXen_getvcpusinfo(fd, 0, 0, ipt, NULL, 0) == 0){
            VIR_DEBUG("Using hypervisor call v2, sys ver8 dom ver7");
            goto done;
        }
        hv_versions.dom_interface = 8; /* XEN_DOMCTL_INTERFACE_VERSION */
        if (virXen_getvcpusinfo(fd, 0, 0, ipt, NULL, 0) == 0){
            VIR_DEBUG("Using hypervisor call v2, sys ver8 dom ver8");
            goto done;
        }
    }

    hv_versions.hypervisor = 1;
    hv_versions.sys_interface = -1;
    if (virXen_getdomaininfo(fd, 0, &info) == 1) {
        VIR_DEBUG("Using hypervisor call v1");
        goto done;
    }

    /*
     * we failed to make the getdomaininfolist hypercall
     */

    VIR_DEBUG("Failed to find any Xen hypervisor method");
    hv_versions.hypervisor = -1;
    virXenError(VIR_ERR_XEN_CALL, " ioctl %lu",
                (unsigned long)IOCTL_PRIVCMD_HYPERCALL);
    VIR_FORCE_CLOSE(fd);
    in_init = 0;
    VIR_FREE(ipt);
    return -1;

 done:
    VIR_FORCE_CLOSE(fd);
    in_init = 0;
    VIR_FREE(ipt);
    return 0;
}

/**
 * xenHypervisorOpen:
 * @conn: pointer to the connection block
 * @name: URL for the target, NULL for local
 * @flags: combination of virDrvOpenFlag(s)
 *
 * Connects to the Xen hypervisor.
 *
 * Returns 0 or -1 in case of error.
 */
virDrvOpenStatus
xenHypervisorOpen(virConnectPtr conn,
                  virConnectAuthPtr auth ATTRIBUTE_UNUSED,
                  unsigned int flags)
{
    int ret;
    xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData;

    virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);

    if (initialized == 0)
        if (xenHypervisorInit(NULL) == -1)
            return -1;

    priv->handle = -1;

    ret = open(XEN_HYPERVISOR_SOCKET, O_RDWR);
    if (ret < 0) {
        virXenError(VIR_ERR_NO_XEN, "%s", XEN_HYPERVISOR_SOCKET);
        return -1;
    }

    priv->handle = ret;

    return 0;
}

/**
 * xenHypervisorClose:
 * @conn: pointer to the connection block
 *
 * Close the connection to the Xen hypervisor.
 *
 * Returns 0 in case of success or -1 in case of error.
 */
int
xenHypervisorClose(virConnectPtr conn)
{
    int ret;
    xenUnifiedPrivatePtr priv;

    if (conn == NULL)
        return -1;

    priv = (xenUnifiedPrivatePtr) conn->privateData;

    if (priv->handle < 0)
        return -1;

    ret = VIR_CLOSE(priv->handle);
    if (ret < 0)
        return -1;

    return 0;
}


/**
 * xenHypervisorGetVersion:
 * @conn: pointer to the connection block
 * @hvVer: where to store the version
 *
 * Call the hypervisor to extracts his own internal API version
 *
 * Returns 0 in case of success, -1 in case of error
 */
int
xenHypervisorGetVersion(virConnectPtr conn, unsigned long *hvVer)
{
    xenUnifiedPrivatePtr priv;

    if (conn == NULL)
        return -1;
    priv = (xenUnifiedPrivatePtr) conn->privateData;
    if (priv->handle < 0 || hvVer == NULL)
        return -1;
    *hvVer = (hv_versions.hv >> 16) * 1000000 + (hv_versions.hv & 0xFFFF) * 1000;
    return 0;
}

struct guest_arch {
    const char *model;
    int bits;
    int hvm;
    int pae;
    int nonpae;
    int ia64_be;
};


static int xenDefaultConsoleType(const char *ostype)
{
    if (STREQ(ostype, "hvm"))
        return VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL;
    else
        return VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_XEN;
}

static virCapsPtr
xenHypervisorBuildCapabilities(virConnectPtr conn,
                               const char *hostmachine,
                               int host_pae,
                               const char *hvm_type,
                               struct guest_arch *guest_archs,
                               int nr_guest_archs) {
    virCapsPtr caps;
    int i;
    int hv_major = hv_versions.hv >> 16;
    int hv_minor = hv_versions.hv & 0xFFFF;

    if ((caps = virCapabilitiesNew(hostmachine, 1, 1)) == NULL)
        goto no_memory;

    virCapabilitiesSetMacPrefix(caps, (unsigned char[]){ 0x00, 0x16, 0x3e });

    if (hvm_type && STRNEQ(hvm_type, "") &&
        virCapabilitiesAddHostFeature(caps, hvm_type) < 0)
        goto no_memory;
    if (host_pae &&
        virCapabilitiesAddHostFeature(caps, "pae") < 0)
        goto no_memory;


    if (virCapabilitiesAddHostMigrateTransport(caps,
                                               "xenmigr") < 0)
        goto no_memory;


    if (hv_versions.sys_interface >= SYS_IFACE_MIN_VERS_NUMA && conn != NULL) {
        if (xenDaemonNodeGetTopology(conn, caps) != 0) {
            virCapabilitiesFree(caps);
            return NULL;
        }
    }

    for (i = 0; i < nr_guest_archs; ++i) {
        virCapsGuestPtr guest;
        char const *const xen_machines[] = {guest_archs[i].hvm ? "xenfv" : "xenpv"};
        virCapsGuestMachinePtr *machines;

        if ((machines = virCapabilitiesAllocMachines(xen_machines, 1)) == NULL)
            goto no_memory;

        if ((guest = virCapabilitiesAddGuest(caps,
                                             guest_archs[i].hvm ? "hvm" : "xen",
                                             guest_archs[i].model,
                                             guest_archs[i].bits,
                                             (STREQ(hostmachine, "x86_64") ?
                                              "/usr/lib64/xen/bin/qemu-dm" :
                                              "/usr/lib/xen/bin/qemu-dm"),
                                             (guest_archs[i].hvm ?
                                              "/usr/lib/xen/boot/hvmloader" :
                                              NULL),
                                             1,
                                             machines)) == NULL) {
            virCapabilitiesFreeMachines(machines, 1);
            goto no_memory;
        }
        machines = NULL;

        if (virCapabilitiesAddGuestDomain(guest,
                                          "xen",
                                          NULL,
                                          NULL,
                                          0,
                                          NULL) == NULL)
            goto no_memory;

        if (guest_archs[i].pae &&
            virCapabilitiesAddGuestFeature(guest,
                                           "pae",
                                           1,
                                           0) == NULL)
            goto no_memory;

        if (guest_archs[i].nonpae &&
            virCapabilitiesAddGuestFeature(guest,
                                           "nonpae",
                                           1,
                                           0) == NULL)
            goto no_memory;

        if (guest_archs[i].ia64_be &&
            virCapabilitiesAddGuestFeature(guest,
                                           "ia64_be",
                                           1,
                                           0) == NULL)
            goto no_memory;

        if (guest_archs[i].hvm) {
            if (virCapabilitiesAddGuestFeature(guest,
                                               "acpi",
                                               1, 1) == NULL)
                goto no_memory;

            /* In Xen 3.1.0, APIC is always on and can't be toggled */
            if (virCapabilitiesAddGuestFeature(guest,
                                               "apic",
                                               1,
                                               (hv_major > 3 &&
                                                hv_minor > 0 ?
                                                0 : 1)) == NULL)
                goto no_memory;

            /* Xen 3.3.x and beyond supports enabling/disabling
             * hardware assisted paging.  Default is off.
             */
            if ((hv_major == 3 && hv_minor >= 3) || (hv_major > 3))
                if (virCapabilitiesAddGuestFeature(guest,
                                                   "hap",
                                                   0,
                                                   1) == NULL)
                    goto no_memory;

            /* Xen 3.4.x and beyond supports the Viridian (Hyper-V)
             * enlightenment interface.  Default is off.
             */
            if ((hv_major == 3 && hv_minor >= 4) || (hv_major > 3))
                if (virCapabilitiesAddGuestFeature(guest,
                                                   "viridian",
                                                   0,
                                                   1) == NULL)
                    goto no_memory;
        }

    }

    caps->defaultConsoleTargetType = xenDefaultConsoleType;

    return caps;

 no_memory:
    virCapabilitiesFree(caps);
    return NULL;
}

#ifdef __sun

static int
get_cpu_flags(virConnectPtr conn, const char **hvm, int *pae, int *longmode)
{
    struct {
        uint32_t r_eax, r_ebx, r_ecx, r_edx;
    } regs;

    char tmpbuf[20];
    int ret = 0;
    int fd;

    /* returns -1, errno 22 if in 32-bit mode */
    *longmode = (sysinfo(SI_ARCHITECTURE_64, tmpbuf, sizeof(tmpbuf)) != -1);

    if ((fd = open("/dev/cpu/self/cpuid", O_RDONLY)) == -1 ||
        pread(fd, &regs, sizeof(regs), 0) != sizeof(regs)) {
        virReportSystemError(errno, "%s", _("could not read CPU flags"));
        goto out;
    }

    *pae = 0;
    *hvm = "";

    if (STREQLEN((const char *)&regs.r_ebx, "AuthcAMDenti", 12)) {
        if (pread(fd, &regs, sizeof(regs), 0x80000001) == sizeof(regs)) {
            /* Read secure virtual machine bit (bit 2 of ECX feature ID) */
            if ((regs.r_ecx >> 2) & 1) {
                *hvm = "svm";
            }
            if ((regs.r_edx >> 6) & 1)
                *pae = 1;
        }
    } else if (STREQLEN((const char *)&regs.r_ebx, "GenuntelineI", 12)) {
        if (pread(fd, &regs, sizeof(regs), 0x00000001) == sizeof(regs)) {
            /* Read VMXE feature bit (bit 5 of ECX feature ID) */
            if ((regs.r_ecx >> 5) & 1)
                *hvm = "vmx";
            if ((regs.r_edx >> 6) & 1)
                *pae = 1;
        }
    }

    ret = 1;

out:
    VIR_FORCE_CLOSE(fd);
    return ret;
}

static virCapsPtr
xenHypervisorMakeCapabilitiesSunOS(virConnectPtr conn)
{
    struct guest_arch guest_arches[32];
    int i = 0;
    virCapsPtr caps = NULL;
    struct utsname utsname;
    int pae, longmode;
    const char *hvm;

    if (!get_cpu_flags(conn, &hvm, &pae, &longmode))
        return NULL;

    /* Really, this never fails - look at the man-page. */
    uname (&utsname);

    guest_arches[i].model = "i686";
    guest_arches[i].bits = 32;
    guest_arches[i].hvm = 0;
    guest_arches[i].pae = pae;
    guest_arches[i].nonpae = !pae;
    guest_arches[i].ia64_be = 0;
    i++;

    if (longmode) {
        guest_arches[i].model = "x86_64";
        guest_arches[i].bits = 64;
        guest_arches[i].hvm = 0;
        guest_arches[i].pae = 0;
        guest_arches[i].nonpae = 0;
        guest_arches[i].ia64_be = 0;
        i++;
    }

    if (hvm[0] != '\0') {
        guest_arches[i].model = "i686";
        guest_arches[i].bits = 32;
        guest_arches[i].hvm = 1;
        guest_arches[i].pae = pae;
        guest_arches[i].nonpae = 1;
        guest_arches[i].ia64_be = 0;
        i++;

        if (longmode) {
            guest_arches[i].model = "x86_64";
            guest_arches[i].bits = 64;
            guest_arches[i].hvm = 1;
            guest_arches[i].pae = 0;
            guest_arches[i].nonpae = 0;
            guest_arches[i].ia64_be = 0;
            i++;
        }
    }

    if ((caps = xenHypervisorBuildCapabilities(conn,
                                               utsname.machine,
                                               pae, hvm,
                                               guest_arches, i)) == NULL)
        virReportOOMError();

    return caps;
}

#endif /* __sun */

/**
 * xenHypervisorMakeCapabilitiesInternal:
 * @conn: pointer to the connection block
 * @cpuinfo: file handle containing /proc/cpuinfo data, or NULL
 * @capabilities: file handle containing /sys/hypervisor/properties/capabilities data, or NULL
 *
 * Return the capabilities of this hypervisor.
 */
virCapsPtr
xenHypervisorMakeCapabilitiesInternal(virConnectPtr conn,
                                      const char *hostmachine,
                                      FILE *cpuinfo, FILE *capabilities)
{
    char line[1024], *str, *token;
    regmatch_t subs[4];
    char *saveptr = NULL;
    int i;

    char hvm_type[4] = ""; /* "vmx" or "svm" (or "" if not in CPU). */
    int host_pae = 0;
    struct guest_arch guest_archs[32];
    int nr_guest_archs = 0;
    virCapsPtr caps = NULL;

    memset(guest_archs, 0, sizeof(guest_archs));

    /* /proc/cpuinfo: flags: Intel calls HVM "vmx", AMD calls it "svm".
     * It's not clear if this will work on IA64, let alone other
     * architectures and non-Linux. (XXX)
     */
    if (cpuinfo) {
        while (fgets (line, sizeof(line), cpuinfo)) {
            if (regexec (&flags_hvm_rec, line, sizeof(subs)/sizeof(regmatch_t), subs, 0) == 0
                && subs[0].rm_so != -1) {
                if (virStrncpy(hvm_type,
                               &line[subs[1].rm_so],
                               subs[1].rm_eo-subs[1].rm_so,
                               sizeof(hvm_type)) == NULL)
                    goto no_memory;
            } else if (regexec (&flags_pae_rec, line, 0, NULL, 0) == 0)
                host_pae = 1;
        }
    }

    /* Most of the useful info is in /sys/hypervisor/properties/capabilities
     * which is documented in the code in xen-unstable.hg/xen/arch/.../setup.c.
     *
     * It is a space-separated list of supported guest architectures.
     *
     * For x86:
     *    TYP-VER-ARCH[p]
     *    ^   ^   ^    ^
     *    |   |   |    +-- PAE supported
     *    |   |   +------- x86_32 or x86_64
     *    |   +----------- the version of Xen, eg. "3.0"
     *    +--------------- "xen" or "hvm" for para or full virt respectively
     *
     * For PPC this file appears to be always empty (?)
     *
     * For IA64:
     *    TYP-VER-ARCH[be]
     *    ^   ^   ^    ^
     *    |   |   |    +-- Big-endian supported
     *    |   |   +------- always "ia64"
     *    |   +----------- the version of Xen, eg. "3.0"
     *    +--------------- "xen" or "hvm" for para or full virt respectively
     */

    /* Expecting one line in this file - ignore any more. */
    if ((capabilities) && (fgets (line, sizeof(line), capabilities))) {
        /* Split the line into tokens.  strtok_r is OK here because we "own"
         * this buffer.  Parse out the features from each token.
         */
        for (str = line, nr_guest_archs = 0;
             nr_guest_archs < sizeof(guest_archs) / sizeof(guest_archs[0])
                 && (token = strtok_r (str, " ", &saveptr)) != NULL;
             str = NULL) {

            if (regexec (&xen_cap_rec, token, sizeof(subs) / sizeof(subs[0]),
                         subs, 0) == 0) {
                int hvm = STRPREFIX(&token[subs[1].rm_so], "hvm");
                const char *model;
                int bits, pae = 0, nonpae = 0, ia64_be = 0;

                if (STRPREFIX(&token[subs[2].rm_so], "x86_32")) {
                    model = "i686";
                    bits = 32;
                    if (subs[3].rm_so != -1 &&
                        STRPREFIX(&token[subs[3].rm_so], "p"))
                        pae = 1;
                    else
                        nonpae = 1;
                }
                else if (STRPREFIX(&token[subs[2].rm_so], "x86_64")) {
                    model = "x86_64";
                    bits = 64;
                }
                else if (STRPREFIX(&token[subs[2].rm_so], "ia64")) {
                    model = "ia64";
                    bits = 64;
                    if (subs[3].rm_so != -1 &&
                        STRPREFIX(&token[subs[3].rm_so], "be"))
                        ia64_be = 1;
                }
                else if (STRPREFIX(&token[subs[2].rm_so], "powerpc64")) {
                    model = "ppc64";
                    bits = 64;
                } else {
                    /* XXX surely no other Xen archs exist  */
                    continue;
                }

                /* Search for existing matching (model,hvm) tuple */
                for (i = 0 ; i < nr_guest_archs ; i++) {
                    if (STREQ(guest_archs[i].model, model) &&
                        guest_archs[i].hvm == hvm) {
                        break;
                    }
                }

                /* Too many arch flavours - highly unlikely ! */
                if (i >= ARRAY_CARDINALITY(guest_archs))
                    continue;
                /* Didn't find a match, so create a new one */
                if (i == nr_guest_archs)
                    nr_guest_archs++;

                guest_archs[i].model = model;
                guest_archs[i].bits = bits;
                guest_archs[i].hvm = hvm;

                /* Careful not to overwrite a previous positive
                   setting with a negative one here - some archs
                   can do both pae & non-pae, but Xen reports
                   separately capabilities so we're merging archs */
                if (pae)
                    guest_archs[i].pae = pae;
                if (nonpae)
                    guest_archs[i].nonpae = nonpae;
                if (ia64_be)
                    guest_archs[i].ia64_be = ia64_be;
            }
        }
    }

    if ((caps = xenHypervisorBuildCapabilities(conn,
                                               hostmachine,
                                               host_pae,
                                               hvm_type,
                                               guest_archs,
                                               nr_guest_archs)) == NULL)
        goto no_memory;

    return caps;

 no_memory:
    virReportOOMError();
    virCapabilitiesFree(caps);
    return NULL;
}

/**
 * xenHypervisorMakeCapabilities:
 *
 * Return the capabilities of this hypervisor.
 */
virCapsPtr
xenHypervisorMakeCapabilities(virConnectPtr conn)
{
#ifdef __sun
    return xenHypervisorMakeCapabilitiesSunOS(conn);
#else
    virCapsPtr caps = NULL;
    FILE *cpuinfo, *capabilities;
    struct utsname utsname;

    /* Really, this never fails - look at the man-page. */
    uname (&utsname);

    cpuinfo = fopen ("/proc/cpuinfo", "r");
    if (cpuinfo == NULL) {
        if (errno != ENOENT) {
            virReportSystemError(errno,
                                 _("cannot read file %s"),
                                 "/proc/cpuinfo");
            return NULL;
        }
    }

    capabilities = fopen ("/sys/hypervisor/properties/capabilities", "r");
    if (capabilities == NULL) {
        if (errno != ENOENT) {
            VIR_FORCE_FCLOSE(cpuinfo);
            virReportSystemError(errno,
                                 _("cannot read file %s"),
                                 "/sys/hypervisor/properties/capabilities");
            return NULL;
        }
    }

    caps = xenHypervisorMakeCapabilitiesInternal(conn,
                                                 utsname.machine,
                                                 cpuinfo,
                                                 capabilities);
    if (caps == NULL)
        goto cleanup;

    if (virNodeSuspendGetTargetMask(&caps->host.powerMgmt) < 0)
        VIR_WARN("Failed to get host power management capabilities");

cleanup:
    VIR_FORCE_FCLOSE(cpuinfo);
    VIR_FORCE_FCLOSE(capabilities);

    return caps;
#endif /* __sun */
}



/**
 * xenHypervisorGetCapabilities:
 * @conn: pointer to the connection block
 *
 * Return the capabilities of this hypervisor.
 */
char *
xenHypervisorGetCapabilities (virConnectPtr conn)
{
    xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData;
    char *xml;

    if (!(xml = virCapabilitiesFormatXML(priv->caps))) {
        virReportOOMError();
        return NULL;
    }

    return xml;
}


/**
 * xenHypervisorNumOfDomains:
 * @conn: pointer to the connection block
 *
 * Provides the number of active domains.
 *
 * Returns the number of domain found or -1 in case of error
 */
int
xenHypervisorNumOfDomains(virConnectPtr conn)
{
    xen_getdomaininfolist dominfos;
    int ret, nbids;
    static int last_maxids = 2;
    int maxids = last_maxids;
    xenUnifiedPrivatePtr priv;

    if (conn == NULL)
        return -1;
    priv = (xenUnifiedPrivatePtr) conn->privateData;
    if (priv->handle < 0)
        return -1;

 retry:
    if (!(XEN_GETDOMAININFOLIST_ALLOC(dominfos, maxids))) {
        virReportOOMError();
        return -1;
    }

    XEN_GETDOMAININFOLIST_CLEAR(dominfos, maxids);

    ret = virXen_getdomaininfolist(priv->handle, 0, maxids, &dominfos);

    XEN_GETDOMAININFOLIST_FREE(dominfos);

    if (ret < 0)
        return -1;

    nbids = ret;
    /* Can't possibly have more than 65,000 concurrent guests
     * so limit how many times we try, to avoid increasing
     * without bound & thus allocating all of system memory !
     * XXX I'll regret this comment in a few years time ;-)
     */
    if (nbids == maxids) {
        if (maxids < 65000) {
            last_maxids *= 2;
            maxids *= 2;
            goto retry;
        }
        nbids = -1;
    }
    if ((nbids < 0) || (nbids > maxids))
        return -1;
    return nbids;
}

/**
 * xenHypervisorListDomains:
 * @conn: pointer to the connection block
 * @ids: array to collect the list of IDs of active domains
 * @maxids: size of @ids
 *
 * Collect the list of active domains, and store their ID in @maxids
 *
 * Returns the number of domain found or -1 in case of error
 */
int
xenHypervisorListDomains(virConnectPtr conn, int *ids, int maxids)
{
    xen_getdomaininfolist dominfos;
    int ret, nbids, i;
    xenUnifiedPrivatePtr priv;

    if (conn == NULL)
        return -1;

    priv = (xenUnifiedPrivatePtr) conn->privateData;
    if (priv->handle < 0 ||
        (ids == NULL) || (maxids < 0))
        return -1;

    if (maxids == 0)
        return 0;

    if (!(XEN_GETDOMAININFOLIST_ALLOC(dominfos, maxids))) {
        virReportOOMError();
        return -1;
    }

    XEN_GETDOMAININFOLIST_CLEAR(dominfos, maxids);
    memset(ids, 0, maxids * sizeof(int));

    ret = virXen_getdomaininfolist(priv->handle, 0, maxids, &dominfos);

    if (ret < 0) {
        XEN_GETDOMAININFOLIST_FREE(dominfos);
        return -1;
    }

    nbids = ret;
    if ((nbids < 0) || (nbids > maxids)) {
        XEN_GETDOMAININFOLIST_FREE(dominfos);
        return -1;
    }

    for (i = 0;i < nbids;i++) {
        ids[i] = XEN_GETDOMAININFOLIST_DOMAIN(dominfos, i);
    }

    XEN_GETDOMAININFOLIST_FREE(dominfos);
    return nbids;
}


char *
xenHypervisorDomainGetOSType (virDomainPtr dom)
{
    xenUnifiedPrivatePtr priv;
    xen_getdomaininfo dominfo;
    char *ostype = NULL;

    priv = (xenUnifiedPrivatePtr) dom->conn->privateData;
    if (priv->handle < 0) {
        virXenError(VIR_ERR_INTERNAL_ERROR, "%s",
                    _("domain shut off or invalid"));
        return NULL;
    }

    /* HV's earlier than 3.1.0 don't include the HVM flags in guests status*/
    if (hv_versions.hypervisor < 2 ||
        hv_versions.dom_interface < 4) {
        virXenError(VIR_ERR_INTERNAL_ERROR, "%s",
                    _("unsupported in dom interface < 4"));
        return NULL;
    }

    XEN_GETDOMAININFO_CLEAR(dominfo);

    if (virXen_getdomaininfo(priv->handle, dom->id, &dominfo) < 0) {
        virXenError(VIR_ERR_INTERNAL_ERROR, "%s",
                    _("cannot get domain details"));
        return NULL;
    }

    if (XEN_GETDOMAININFO_DOMAIN(dominfo) != dom->id) {
        virXenError(VIR_ERR_INTERNAL_ERROR, "%s",
                    _("cannot get domain details"));
        return NULL;
    }

    if (XEN_GETDOMAININFO_FLAGS(dominfo) & DOMFLAGS_HVM)
        ostype = strdup("hvm");
    else
        ostype = strdup("linux");

    if (ostype == NULL)
        virReportOOMError();

    return ostype;
}

int
xenHypervisorHasDomain(virConnectPtr conn,
                       int id)
{
    xenUnifiedPrivatePtr priv;
    xen_getdomaininfo dominfo;

    priv = (xenUnifiedPrivatePtr) conn->privateData;
    if (priv->handle < 0)
        return 0;

    XEN_GETDOMAININFO_CLEAR(dominfo);

    if (virXen_getdomaininfo(priv->handle, id, &dominfo) < 0)
        return 0;

    if (XEN_GETDOMAININFO_DOMAIN(dominfo) != id)
        return 0;

    return 1;
}

virDomainPtr
xenHypervisorLookupDomainByID(virConnectPtr conn,
                              int id)
{
    xenUnifiedPrivatePtr priv;
    xen_getdomaininfo dominfo;
    virDomainPtr ret;
    char *name;

    priv = (xenUnifiedPrivatePtr) conn->privateData;
    if (priv->handle < 0)
        return NULL;

    XEN_GETDOMAININFO_CLEAR(dominfo);

    if (virXen_getdomaininfo(priv->handle, id, &dominfo) < 0)
        return NULL;

    if (XEN_GETDOMAININFO_DOMAIN(dominfo) != id)
        return NULL;

    xenUnifiedLock(priv);
    name = xenStoreDomainGetName(conn, id);
    xenUnifiedUnlock(priv);
    if (!name)
        return NULL;

    ret = virGetDomain(conn, name, XEN_GETDOMAININFO_UUID(dominfo));
    if (ret)
        ret->id = id;
    VIR_FREE(name);
    return ret;
}


virDomainPtr
xenHypervisorLookupDomainByUUID(virConnectPtr conn,
                                const unsigned char *uuid)
{
    xen_getdomaininfolist dominfos;
    xenUnifiedPrivatePtr priv;
    virDomainPtr ret;
    char *name;
    int maxids = 100, nids, i, id;

    priv = (xenUnifiedPrivatePtr) conn->privateData;
    if (priv->handle < 0)
        return NULL;

 retry:
    if (!(XEN_GETDOMAININFOLIST_ALLOC(dominfos, maxids))) {
        virReportOOMError();
        return NULL;
    }

    XEN_GETDOMAININFOLIST_CLEAR(dominfos, maxids);

    nids = virXen_getdomaininfolist(priv->handle, 0, maxids, &dominfos);

    if (nids < 0) {
        XEN_GETDOMAININFOLIST_FREE(dominfos);
        return NULL;
    }

    /* Can't possibly have more than 65,000 concurrent guests
     * so limit how many times we try, to avoid increasing
     * without bound & thus allocating all of system memory !
     * XXX I'll regret this comment in a few years time ;-)
     */
    if (nids == maxids) {
        XEN_GETDOMAININFOLIST_FREE(dominfos);
        if (maxids < 65000) {
            maxids *= 2;
            goto retry;
        }
        return NULL;
    }

    id = -1;
    for (i = 0 ; i < nids ; i++) {
        if (memcmp(XEN_GETDOMAININFOLIST_UUID(dominfos, i), uuid, VIR_UUID_BUFLEN) == 0) {
            id = XEN_GETDOMAININFOLIST_DOMAIN(dominfos, i);
            break;
        }
    }
    XEN_GETDOMAININFOLIST_FREE(dominfos);

    if (id == -1)
        return NULL;

    xenUnifiedLock(priv);
    name = xenStoreDomainGetName(conn, id);
    xenUnifiedUnlock(priv);
    if (!name)
        return NULL;

    ret = virGetDomain(conn, name, uuid);
    if (ret)
        ret->id = id;
    VIR_FREE(name);
    return ret;
}

/**
 * xenHypervisorGetMaxVcpus:
 *
 * Returns the maximum of CPU defined by Xen.
 */
int
xenHypervisorGetMaxVcpus(virConnectPtr conn,
                         const char *type ATTRIBUTE_UNUSED)
{
    xenUnifiedPrivatePtr priv;

    if (conn == NULL)
        return -1;
    priv = (xenUnifiedPrivatePtr) conn->privateData;
    if (priv->handle < 0)
        return -1;

    return MAX_VIRT_CPUS;
}

/**
 * xenHypervisorGetDomMaxMemory:
 * @conn: connection data
 * @id: domain id
 *
 * Retrieve the maximum amount of physical memory allocated to a
 * domain.
 *
 * Returns the memory size in kilobytes or 0 in case of error.
 */
unsigned long
xenHypervisorGetDomMaxMemory(virConnectPtr conn, int id)
{
    xenUnifiedPrivatePtr priv;
    xen_getdomaininfo dominfo;
    int ret;

    if (conn == NULL)
        return 0;

    priv = (xenUnifiedPrivatePtr) conn->privateData;
    if (priv->handle < 0)
        return 0;

    if (kb_per_pages == 0) {
        kb_per_pages = sysconf(_SC_PAGESIZE) / 1024;
        if (kb_per_pages <= 0)
            kb_per_pages = 4;
    }

    XEN_GETDOMAININFO_CLEAR(dominfo);

    ret = virXen_getdomaininfo(priv->handle, id, &dominfo);

    if ((ret < 0) || (XEN_GETDOMAININFO_DOMAIN(dominfo) != id))
        return 0;

    return (unsigned long) XEN_GETDOMAININFO_MAX_PAGES(dominfo) * kb_per_pages;
}

/**
 * xenHypervisorGetMaxMemory:
 * @domain: a domain object or NULL
 *
 * Retrieve the maximum amount of physical memory allocated to a
 * domain. If domain is NULL, then this get the amount of memory reserved
 * to Domain0 i.e. the domain where the application runs.
 *
 * Returns the memory size in kilobytes or 0 in case of error.
 */
static unsigned long long ATTRIBUTE_NONNULL (1)
xenHypervisorGetMaxMemory(virDomainPtr domain)
{
    xenUnifiedPrivatePtr priv;

    if (domain->conn == NULL)
        return 0;

    priv = (xenUnifiedPrivatePtr) domain->conn->privateData;
    if (priv->handle < 0 || domain->id < 0)
        return 0;

    return xenHypervisorGetDomMaxMemory(domain->conn, domain->id);
}

/**
 * xenHypervisorGetDomInfo:
 * @conn: connection data
 * @id: the domain ID
 * @info: the place where information should be stored
 *
 * Do a hypervisor call to get the related set of domain information.
 *
 * Returns 0 in case of success, -1 in case of error.
 */
int
xenHypervisorGetDomInfo(virConnectPtr conn, int id, virDomainInfoPtr info)
{
    xenUnifiedPrivatePtr priv;
    xen_getdomaininfo dominfo;
    int ret;
    uint32_t domain_flags, domain_state, domain_shutdown_cause;

    if (kb_per_pages == 0) {
        kb_per_pages = sysconf(_SC_PAGESIZE) / 1024;
        if (kb_per_pages <= 0)
            kb_per_pages = 4;
    }

    if (conn == NULL)
        return -1;

    priv = (xenUnifiedPrivatePtr) conn->privateData;
    if (priv->handle < 0 || info == NULL)
        return -1;

    memset(info, 0, sizeof(virDomainInfo));
    XEN_GETDOMAININFO_CLEAR(dominfo);

    ret = virXen_getdomaininfo(priv->handle, id, &dominfo);

    if ((ret < 0) || (XEN_GETDOMAININFO_DOMAIN(dominfo) != id))
        return -1;

    domain_flags = XEN_GETDOMAININFO_FLAGS(dominfo);
    domain_flags &= ~DOMFLAGS_HVM; /* Mask out HVM flags */
    domain_state = domain_flags & 0xFF; /* Mask out high bits */
    switch (domain_state) {
        case DOMFLAGS_DYING:
            info->state = VIR_DOMAIN_SHUTDOWN;
            break;
        case DOMFLAGS_SHUTDOWN:
            /* The domain is shutdown.  Determine the cause. */
            domain_shutdown_cause = domain_flags >> DOMFLAGS_SHUTDOWNSHIFT;
            switch (domain_shutdown_cause) {
                case SHUTDOWN_crash:
                    info->state = VIR_DOMAIN_CRASHED;
                    break;
                default:
                    info->state = VIR_DOMAIN_SHUTOFF;
            }
            break;
        case DOMFLAGS_PAUSED:
            info->state = VIR_DOMAIN_PAUSED;
            break;
        case DOMFLAGS_BLOCKED:
            info->state = VIR_DOMAIN_BLOCKED;
            break;
        case DOMFLAGS_RUNNING:
            info->state = VIR_DOMAIN_RUNNING;
            break;
        default:
            info->state = VIR_DOMAIN_NOSTATE;
    }

    /*
     * the API brings back the cpu time in nanoseconds,
     * convert to microseconds, same thing convert to
     * kilobytes from page counts
     */
    info->cpuTime = XEN_GETDOMAININFO_CPUTIME(dominfo);
    info->memory = XEN_GETDOMAININFO_TOT_PAGES(dominfo) * kb_per_pages;
    info->maxMem = XEN_GETDOMAININFO_MAX_PAGES(dominfo);
    if(info->maxMem != UINT_MAX)
        info->maxMem *= kb_per_pages;
    info->nrVirtCpu = XEN_GETDOMAININFO_CPUCOUNT(dominfo);
    return 0;
}

/**
 * xenHypervisorGetDomainInfo:
 * @domain: pointer to the domain block
 * @info: the place where information should be stored
 *
 * Do a hypervisor call to get the related set of domain information.
 *
 * Returns 0 in case of success, -1 in case of error.
 */
int
xenHypervisorGetDomainInfo(virDomainPtr domain, virDomainInfoPtr info)
{
    xenUnifiedPrivatePtr priv;

    if (domain->conn == NULL)
        return -1;

    priv = (xenUnifiedPrivatePtr) domain->conn->privateData;
    if (priv->handle < 0 || info == NULL ||
        (domain->id < 0))
        return -1;

    return xenHypervisorGetDomInfo(domain->conn, domain->id, info);

}

/**
 * xenHypervisorGetDomainState:
 * @domain: pointer to the domain block
 * @state: returned state of the domain
 * @reason: returned reason for the state
 * @flags: additional flags, 0 for now
 *
 * Do a hypervisor call to get the related set of domain information.
 *
 * Returns 0 in case of success, -1 in case of error.
 */
int
xenHypervisorGetDomainState(virDomainPtr domain,
                            int *state,
                            int *reason,
                            unsigned int flags)
{
    xenUnifiedPrivatePtr priv = domain->conn->privateData;
    virDomainInfo info;

    virCheckFlags(0, -1);

    if (domain->conn == NULL)
        return -1;

    if (priv->handle < 0 || domain->id < 0)
        return -1;

    if (xenHypervisorGetDomInfo(domain->conn, domain->id, &info) < 0)
        return -1;

    *state = info.state;
    if (reason)
        *reason = 0;

    return 0;
}

/**
 * xenHypervisorNodeGetCellsFreeMemory:
 * @conn: pointer to the hypervisor connection
 * @freeMems: pointer to the array of unsigned long long
 * @startCell: index of first cell to return freeMems info on.
 * @maxCells: Maximum number of cells for which freeMems information can
 *            be returned.
 *
 * This call returns the amount of free memory in one or more NUMA cells.
 * The @freeMems array must be allocated by the caller and will be filled
 * with the amount of free memory in kilobytes for each cell requested,
 * starting with startCell (in freeMems[0]), up to either
 * (startCell + maxCells), or the number of additional cells in the node,
 * whichever is smaller.
 *
 * Returns the number of entries filled in freeMems, or -1 in case of error.
 */
int
xenHypervisorNodeGetCellsFreeMemory(virConnectPtr conn, unsigned long long *freeMems,
                                    int startCell, int maxCells)
{
    xen_op_v2_sys op_sys;
    int i, j, ret;
    xenUnifiedPrivatePtr priv;

    if (conn == NULL) {
        virXenError(VIR_ERR_INVALID_ARG, "%s", _("invalid argument"));
        return -1;
    }

    priv = conn->privateData;

    if (priv->nbNodeCells < 0) {
        virXenError(VIR_ERR_XEN_CALL, "%s",
                    _("cannot determine actual number of cells"));
        return -1;
    }

    if ((maxCells < 1) || (startCell >= priv->nbNodeCells)) {
        virXenError(VIR_ERR_INVALID_ARG, "%s",
                    _("invalid argument"));
        return -1;
    }

    /*
     * Support only hv_versions.sys_interface >=4
     */
    if (hv_versions.sys_interface < SYS_IFACE_MIN_VERS_NUMA) {
        virXenError(VIR_ERR_XEN_CALL, "%s",
                    _("unsupported in sys interface < 4"));
        return -1;
    }

    if (priv->handle < 0) {
        virXenError(VIR_ERR_INTERNAL_ERROR, "%s",
                    _("priv->handle invalid"));
        return -1;
    }

    memset(&op_sys, 0, sizeof(op_sys));
    op_sys.cmd = XEN_V2_OP_GETAVAILHEAP;

    for (i = startCell, j = 0;(i < priv->nbNodeCells) && (j < maxCells);i++,j++) {
        if (hv_versions.sys_interface >= 5)
            op_sys.u.availheap5.node = i;
        else
            op_sys.u.availheap.node = i;
        ret = xenHypervisorDoV2Sys(priv->handle, &op_sys);
        if (ret < 0) {
            return -1;
        }
        if (hv_versions.sys_interface >= 5)
            freeMems[j] = op_sys.u.availheap5.avail_bytes;
        else
            freeMems[j] = op_sys.u.availheap.avail_bytes;
    }
    return j;
}


/**
 * xenHypervisorPauseDomain:
 * @domain: pointer to the domain block
 *
 * Do a hypervisor call to pause the given domain
 *
 * Returns 0 in case of success, -1 in case of error.
 */
int
xenHypervisorPauseDomain(virDomainPtr domain)
{
    int ret;
    xenUnifiedPrivatePtr priv;

    if (domain->conn == NULL)
        return -1;

    priv = (xenUnifiedPrivatePtr) domain->conn->privateData;
    if (priv->handle < 0 || domain->id < 0)
        return -1;

    ret = virXen_pausedomain(priv->handle, domain->id);
    if (ret < 0)
        return -1;
    return 0;
}

/**
 * xenHypervisorResumeDomain:
 * @domain: pointer to the domain block
 *
 * Do a hypervisor call to resume the given domain
 *
 * Returns 0 in case of success, -1 in case of error.
 */
int
xenHypervisorResumeDomain(virDomainPtr domain)
{
    int ret;
    xenUnifiedPrivatePtr priv;

    if (domain->conn == NULL)
        return -1;

    priv = (xenUnifiedPrivatePtr) domain->conn->privateData;
    if (priv->handle < 0 || domain->id < 0)
        return -1;

    ret = virXen_unpausedomain(priv->handle, domain->id);
    if (ret < 0)
        return -1;
    return 0;
}

/**
 * xenHypervisorDestroyDomainFlags:
 * @domain: pointer to the domain block
 * @flags: an OR'ed set of virDomainDestroyFlagsValues
 *
 * Do a hypervisor call to destroy the given domain
 *
 * Calling this function with no @flags set (equal to zero)
 * is equivalent to calling xenHypervisorDestroyDomain.
 *
 * Returns 0 in case of success, -1 in case of error.
 */
int
xenHypervisorDestroyDomainFlags(virDomainPtr domain,
                                unsigned int flags)
{
    int ret;
    xenUnifiedPrivatePtr priv;

    virCheckFlags(0, -1);

    if (domain->conn == NULL)
        return -1;

    priv = (xenUnifiedPrivatePtr) domain->conn->privateData;
    if (priv->handle < 0 || domain->id < 0)
        return -1;

    ret = virXen_destroydomain(priv->handle, domain->id);
    if (ret < 0)
        return -1;
    return 0;
}

/**
 * xenHypervisorSetMaxMemory:
 * @domain: pointer to the domain block
 * @memory: the max memory size in kilobytes.
 *
 * Do a hypervisor call to change the maximum amount of memory used
 *
 * Returns 0 in case of success, -1 in case of error.
 */
int
xenHypervisorSetMaxMemory(virDomainPtr domain, unsigned long memory)
{
    int ret;
    xenUnifiedPrivatePtr priv;

    if (domain->conn == NULL)
        return -1;

    priv = (xenUnifiedPrivatePtr) domain->conn->privateData;
    if (priv->handle < 0 || domain->id < 0)
        return -1;

    ret = virXen_setmaxmem(priv->handle, domain->id, memory);
    if (ret < 0)
        return -1;
    return 0;
}


/**
 * xenHypervisorSetVcpus:
 * @domain: pointer to domain object
 * @nvcpus: the new number of virtual CPUs for this domain
 *
 * Dynamically change the number of virtual CPUs used by the domain.
 *
 * Returns 0 in case of success, -1 in case of failure.
 */

int
xenHypervisorSetVcpus(virDomainPtr domain, unsigned int nvcpus)
{
    int ret;
    xenUnifiedPrivatePtr priv;

    if (domain->conn == NULL)
        return -1;

    priv = (xenUnifiedPrivatePtr) domain->conn->privateData;
    if (priv->handle < 0 || domain->id < 0 || nvcpus < 1)
        return -1;

    ret = virXen_setmaxvcpus(priv->handle, domain->id, nvcpus);
    if (ret < 0)
        return -1;
    return 0;
}

/**
 * xenHypervisorPinVcpu:
 * @domain: pointer to domain object
 * @vcpu: virtual CPU number
 * @cpumap: pointer to a bit map of real CPUs (in 8-bit bytes)
 * @maplen: length of cpumap in bytes
 *
 * Dynamically change the real CPUs which can be allocated to a virtual CPU.
 *
 * Returns 0 in case of success, -1 in case of failure.
 */

int
xenHypervisorPinVcpu(virDomainPtr domain, unsigned int vcpu,
                     unsigned char *cpumap, int maplen)
{
    int ret;
    xenUnifiedPrivatePtr priv;

    if (domain->conn == NULL)
        return -1;

    priv = (xenUnifiedPrivatePtr) domain->conn->privateData;
    if (priv->handle < 0 || (domain->id < 0) ||
        (cpumap == NULL) || (maplen < 1))
        return -1;

    ret = virXen_setvcpumap(priv->handle, domain->id, vcpu,
                            cpumap, maplen);
    if (ret < 0)
        return -1;
    return 0;
}

/**
 * virDomainGetVcpus:
 * @domain: pointer to domain object, or NULL for Domain0
 * @info: pointer to an array of virVcpuInfo structures (OUT)
 * @maxinfo: number of structures in info array
 * @cpumaps: pointer to a bit map of real CPUs for all vcpus of this domain (in 8-bit bytes) (OUT)
 *	If cpumaps is NULL, then no cpumap information is returned by the API.
 *	It's assumed there is <maxinfo> cpumap in cpumaps array.
 *	The memory allocated to cpumaps must be (maxinfo * maplen) bytes
 *	(ie: calloc(maxinfo, maplen)).
 *	One cpumap inside cpumaps has the format described in virDomainPinVcpu() API.
 * @maplen: number of bytes in one cpumap, from 1 up to size of CPU map in
 *	underlying virtualization system (Xen...).
 *
 * Extract information about virtual CPUs of domain, store it in info array
 * and also in cpumaps if this pointer isn't NULL.
 *
 * Returns the number of info filled in case of success, -1 in case of failure.
 */
int
xenHypervisorGetVcpus(virDomainPtr domain, virVcpuInfoPtr info, int maxinfo,
                      unsigned char *cpumaps, int maplen)
{
    xen_getdomaininfo dominfo;
    int ret;
    xenUnifiedPrivatePtr priv;
    virVcpuInfoPtr ipt;
    int nbinfo, i;

    if (domain->conn == NULL)
        return -1;

    priv = (xenUnifiedPrivatePtr) domain->conn->privateData;
    if (priv->handle < 0 || (domain->id < 0) ||
        (info == NULL) || (maxinfo < 1) ||
        (sizeof(cpumap_t) & 7)) {
        virXenError(VIR_ERR_INTERNAL_ERROR, "%s",
                    _("domain shut off or invalid"));
        return -1;
    }
    if ((cpumaps != NULL) && (maplen < 1)) {
        virXenError(VIR_ERR_INVALID_ARG, "%s",
                    _("invalid argument"));
        return -1;
    }
    /* first get the number of virtual CPUs in this domain */
    XEN_GETDOMAININFO_CLEAR(dominfo);
    ret = virXen_getdomaininfo(priv->handle, domain->id,
                               &dominfo);

    if ((ret < 0) || (XEN_GETDOMAININFO_DOMAIN(dominfo) != domain->id)) {
        virXenError(VIR_ERR_INTERNAL_ERROR, "%s",
                    _("cannot get domain details"));
        return -1;
    }
    nbinfo = XEN_GETDOMAININFO_CPUCOUNT(dominfo) + 1;
    if (nbinfo > maxinfo) nbinfo = maxinfo;

    if (cpumaps != NULL)
        memset(cpumaps, 0, maxinfo * maplen);

    for (i = 0, ipt = info; i < nbinfo; i++, ipt++) {
        if ((cpumaps != NULL) && (i < maxinfo)) {
            ret = virXen_getvcpusinfo(priv->handle, domain->id, i,
                                      ipt,
                                      (unsigned char *)VIR_GET_CPUMAP(cpumaps, maplen, i),
                                      maplen);
            if (ret < 0) {
                virXenError(VIR_ERR_INTERNAL_ERROR, "%s",
                            _("cannot get VCPUs info"));
                return -1;
            }
        } else {
            ret = virXen_getvcpusinfo(priv->handle, domain->id, i,
                                      ipt, NULL, 0);
            if (ret < 0) {
                virXenError(VIR_ERR_INTERNAL_ERROR, "%s",
                            _("cannot get VCPUs info"));
                return -1;
            }
        }
    }
    return nbinfo;
}

/**
 * xenHypervisorGetVcpuMax:
 *
 *  Returns the maximum number of virtual CPUs supported for
 *  the guest VM. If the guest is inactive, this is the maximum
 *  of CPU defined by Xen. If the guest is running this reflect
 *  the maximum number of virtual CPUs the guest was booted with.
 */
int
xenHypervisorGetVcpuMax(virDomainPtr domain)
{
    xen_getdomaininfo dominfo;
    int ret;
    int maxcpu;
    xenUnifiedPrivatePtr priv;

    if (domain->conn == NULL)
        return -1;

    priv = (xenUnifiedPrivatePtr) domain->conn->privateData;
    if (priv->handle < 0)
        return -1;

    /* inactive domain */
    if (domain->id < 0) {
        maxcpu = MAX_VIRT_CPUS;
    } else {
        XEN_GETDOMAININFO_CLEAR(dominfo);
        ret = virXen_getdomaininfo(priv->handle, domain->id,
                                   &dominfo);

        if ((ret < 0) || (XEN_GETDOMAININFO_DOMAIN(dominfo) != domain->id))
            return -1;
        maxcpu = XEN_GETDOMAININFO_MAXCPUID(dominfo) + 1;
    }

    return maxcpu;
}

/**
 * xenHavePrivilege()
 *
 * Return true if the current process should be able to connect to Xen.
 */
int
xenHavePrivilege(void)
{
#ifdef __sun
    return priv_ineffect (PRIV_XVM_CONTROL);
#else
    return access(XEN_HYPERVISOR_SOCKET, R_OK) == 0;
#endif
}
