/*
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef XC_PRIVATE_H
#define XC_PRIVATE_H

#include <unistd.h>
#include <stdarg.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/ioctl.h>

#include "_paths.h"

#define XC_WANT_COMPAT_MAP_FOREIGN_API
#define XC_INTERNAL_COMPAT_MAP_FOREIGN_API
#include "xenctrl.h"

#include <xencall.h>
#include <xenforeignmemory.h>
#include <xendevicemodel.h>

#include <xen/sys/privcmd.h>

#include <xen-tools/libs.h>

#if defined(HAVE_VALGRIND_MEMCHECK_H) && !defined(NDEBUG) && !defined(__MINIOS__)
/* Compile in Valgrind client requests? */
#include <valgrind/memcheck.h>
#else
#define VALGRIND_MAKE_MEM_UNDEFINED(addr, len) /* addr, len */
#endif

#if defined(__MINIOS__)
/*
 * MiniOS's libc doesn't know about sys/uio.h or writev().
 * Declare enough of sys/uio.h to compile.
 */
struct iovec {
    void *iov_base;
    size_t iov_len;
};
#else
#include <sys/uio.h>
#endif

#define ROUNDUP(_x,_w) (((unsigned long)(_x)+(1UL<<(_w))-1) & ~((1UL<<(_w))-1))

#define GET_FIELD(_p, _f, _w) (((_w) == 8) ? ((_p)->x64._f) : ((_p)->x32._f))

#define SET_FIELD(_p, _f, _v, _w) do {          \
    if ((_w) == 8)                              \
        (_p)->x64._f = (_v);                    \
    else                                        \
        (_p)->x32._f = (_v);                    \
} while (0)

/* XXX SMH: following skanky macros rely on variable p2m_size being set */
/* XXX TJD: also, "guest_width" should be the guest's sizeof(unsigned long) */

struct domain_info_context {
    unsigned int guest_width;
    unsigned long p2m_size;
};

/* Number of xen_pfn_t in a page */
#define FPP             (PAGE_SIZE/(dinfo->guest_width))

/* Number of entries in the pfn_to_mfn_frame_list_list */
#define P2M_FLL_ENTRIES (((dinfo->p2m_size)+(FPP*FPP)-1)/(FPP*FPP))

/* Number of entries in the pfn_to_mfn_frame_list */
#define P2M_FL_ENTRIES  (((dinfo->p2m_size)+FPP-1)/FPP)

/* Size in bytes of the pfn_to_mfn_frame_list     */
#define P2M_GUEST_FL_SIZE ((P2M_FL_ENTRIES) * (dinfo->guest_width))
#define P2M_TOOLS_FL_SIZE ((P2M_FL_ENTRIES) *                           \
                           max_t(size_t, sizeof(xen_pfn_t), dinfo->guest_width))

#define DECLARE_DOMCTL struct xen_domctl domctl
#define DECLARE_SYSCTL struct xen_sysctl sysctl
#define DECLARE_PHYSDEV_OP struct physdev_op physdev_op
#define DECLARE_FLASK_OP struct xen_flask_op op
#define DECLARE_PLATFORM_OP struct xen_platform_op platform_op

#undef PAGE_SHIFT
#undef PAGE_SIZE
#undef PAGE_MASK
#define PAGE_SHIFT              XC_PAGE_SHIFT
#define PAGE_SIZE               XC_PAGE_SIZE
#define PAGE_MASK               XC_PAGE_MASK

#define INVALID_PFN ((xen_pfn_t)-1)

/*
** Define max dirty page cache to permit during save/restore -- need to balance 
** keeping cache usage down with CPU impact of invalidating too often.
** (Currently 16MB)
*/
#define MAX_PAGECACHE_USAGE (4*1024)

struct xc_interface_core {
    int flags;
    xentoollog_logger *error_handler,   *error_handler_tofree;
    xentoollog_logger *dombuild_logger, *dombuild_logger_tofree;
    struct xc_error last_error; /* for xc_get_last_error */
    FILE *dombuild_logger_file;
    const char *currently_progress_reporting;

    /* Hypercall interface */
    xencall_handle *xcall;

    /* Foreign mappings */
    xenforeignmemory_handle *fmem;

    /* Device model */
    xendevicemodel_handle *dmod;
};

void *osdep_alloc_hypercall_buffer(xc_interface *xch, int npages);
void osdep_free_hypercall_buffer(xc_interface *xch, void *ptr, int npages);

void xc_report_error(xc_interface *xch, int code, const char *fmt, ...)
    __attribute__((format(printf,3,4)));
void xc_reportv(xc_interface *xch, xentoollog_logger *lg, xentoollog_level,
                int code, const char *fmt, va_list args)
     __attribute__((format(printf,5,0)));
void xc_report(xc_interface *xch, xentoollog_logger *lg, xentoollog_level,
               int code, const char *fmt, ...)
     __attribute__((format(printf,5,6)));

const char *xc_set_progress_prefix(xc_interface *xch, const char *doing);
void xc_report_progress_single(xc_interface *xch, const char *doing);
void xc_report_progress_step(xc_interface *xch,
                             unsigned long done, unsigned long total);

/* anamorphic macros:  struct xc_interface *xch  must be in scope */

#define IPRINTF(_f, _a...)  do { int IPRINTF_errno = errno; \
        xc_report(xch, xch->error_handler, XTL_INFO,0, _f , ## _a); \
        errno = IPRINTF_errno; \
        } while (0)
#define DPRINTF(_f, _a...) do { int DPRINTF_errno = errno; \
        xc_report(xch, xch->error_handler, XTL_DETAIL,0, _f , ## _a); \
        errno = DPRINTF_errno; \
        } while (0)
#define DBGPRINTF(_f, _a...)  do { int DBGPRINTF_errno = errno; \
        xc_report(xch, xch->error_handler, XTL_DEBUG,0, _f , ## _a); \
        errno = DBGPRINTF_errno; \
        } while (0)

#define ERROR(_m, _a...)  do { int ERROR_errno = errno; \
        xc_report_error(xch,XC_INTERNAL_ERROR,_m , ## _a ); \
        errno = ERROR_errno; \
        } while (0)
#define PERROR(_m, _a...) do { int PERROR_errno = errno; \
        xc_report_error(xch,XC_INTERNAL_ERROR,_m " (%d = %s)", \
        ## _a , errno, xc_strerror(xch, errno)); \
        errno = PERROR_errno; \
        } while (0)

/*
 * HYPERCALL ARGUMENT BUFFERS
 *
 * Augment the public hypercall buffer interface with the ability to
 * bounce between user provided buffers and hypercall safe memory.
 *
 * Use xc_hypercall_bounce_pre/post instead of
 * xc_hypercall_buffer_alloc/free(_pages).  The specified user
 * supplied buffer is automatically copied in/out of the hypercall
 * safe memory.
 */
enum {
    XC_HYPERCALL_BUFFER_BOUNCE_NONE = 0,
    XC_HYPERCALL_BUFFER_BOUNCE_IN   = 1,
    XC_HYPERCALL_BUFFER_BOUNCE_OUT  = 2,
    XC_HYPERCALL_BUFFER_BOUNCE_BOTH = 3
};

/*
 * Declare a named bounce buffer.
 *
 * Normally you should use DECLARE_HYPERCALL_BOUNCE (see below).
 *
 * This declaration should only be used when the user pointer is
 * non-trivial, e.g. when it is contained within an existing data
 * structure.
 */
#define DECLARE_NAMED_HYPERCALL_BOUNCE(_name, _ubuf, _sz, _dir) \
    xc_hypercall_buffer_t XC__HYPERCALL_BUFFER_NAME(_name) = {  \
        .hbuf = NULL,                                           \
        .param_shadow = NULL,                                   \
        .sz = _sz, .dir = _dir, .ubuf = _ubuf,                  \
    }

/*
 * Declare a bounce buffer shadowing the named user data pointer.
 */
#define DECLARE_HYPERCALL_BOUNCE(_ubuf, _sz, _dir) DECLARE_NAMED_HYPERCALL_BOUNCE(_ubuf, _ubuf, _sz, _dir)

/*
 * Declare a bounce buffer shadowing the named user data pointer that
 * cannot be modified.
 */
#define DECLARE_HYPERCALL_BOUNCE_IN(_ubuf, _sz)                     \
    DECLARE_NAMED_HYPERCALL_BOUNCE(_ubuf, (void *)(_ubuf), _sz,     \
                                   XC_HYPERCALL_BUFFER_BOUNCE_IN)

/*
 * Set the size of data to bounce. Useful when the size is not known
 * when the bounce buffer is declared.
 */
#define HYPERCALL_BOUNCE_SET_SIZE(_buf, _sz) do { (HYPERCALL_BUFFER(_buf))->sz = _sz; } while (0)

/*
 * Change the direction.
 *
 * Can only be used if the bounce_pre/bounce_post commands have
 * not been used.
 */
#define HYPERCALL_BOUNCE_SET_DIR(_buf, _dir) do { if ((HYPERCALL_BUFFER(_buf))->hbuf)         \
                                                        assert(1);                            \
                                                   (HYPERCALL_BUFFER(_buf))->dir = _dir;      \
                                                } while (0)

/*
 * Initialise and free hypercall safe memory. Takes care of any required
 * copying.
 */
int xc__hypercall_bounce_pre(xc_interface *xch, xc_hypercall_buffer_t *bounce);
#define xc_hypercall_bounce_pre(_xch, _name) xc__hypercall_bounce_pre(_xch, HYPERCALL_BUFFER(_name))
void xc__hypercall_bounce_post(xc_interface *xch, xc_hypercall_buffer_t *bounce);
#define xc_hypercall_bounce_post(_xch, _name) xc__hypercall_bounce_post(_xch, HYPERCALL_BUFFER(_name))

/*
 * Release hypercall buffer cache
 */
void xc__hypercall_buffer_cache_release(xc_interface *xch);

/*
 * Hypercall interfaces.
 */

static inline int do_xen_version(xc_interface *xch, int cmd, xc_hypercall_buffer_t *dest)
{
    DECLARE_HYPERCALL_BUFFER_ARGUMENT(dest);
    return xencall2(xch->xcall, __HYPERVISOR_xen_version,
                    cmd, HYPERCALL_BUFFER_AS_ARG(dest));
}

static inline int do_physdev_op(xc_interface *xch, int cmd, void *op, size_t len)
{
    int ret = -1;
    DECLARE_HYPERCALL_BOUNCE(op, len, XC_HYPERCALL_BUFFER_BOUNCE_BOTH);

    if ( xc_hypercall_bounce_pre(xch, op) )
    {
        PERROR("Could not bounce memory for physdev hypercall");
        goto out1;
    }

    ret = xencall2(xch->xcall, __HYPERVISOR_physdev_op,
                   cmd, HYPERCALL_BUFFER_AS_ARG(op));
    if ( ret < 0 )
    {
        if ( errno == EACCES )
            DPRINTF("physdev operation failed -- need to"
                    " rebuild the user-space tool set?\n");
    }

    xc_hypercall_bounce_post(xch, op);
out1:
    return ret;
}

static inline int do_domctl_maybe_retry_efault(xc_interface *xch,
                                               struct xen_domctl *domctl,
                                               unsigned int retries)
{
    int ret = -1;
    unsigned int retry_cnt = 0;

    DECLARE_HYPERCALL_BOUNCE(domctl, sizeof(*domctl), XC_HYPERCALL_BUFFER_BOUNCE_BOTH);

    domctl->interface_version = XEN_DOMCTL_INTERFACE_VERSION;

    if ( xc_hypercall_bounce_pre(xch, domctl) )
    {
        PERROR("Could not bounce buffer for domctl hypercall");
        goto out1;
    }

    do {
        ret = xencall1(xch->xcall, __HYPERVISOR_domctl,
                       HYPERCALL_BUFFER_AS_ARG(domctl));
    } while ( ret < 0 && errno == EFAULT && retry_cnt++ < retries );

    if ( ret < 0 )
    {
        if ( errno == EACCES )
            DPRINTF("domctl operation failed -- need to"
                    " rebuild the user-space tool set?\n");
    }

    xc_hypercall_bounce_post(xch, domctl);
 out1:
    return ret;
}

static inline int do_domctl(xc_interface *xch, struct xen_domctl *domctl)
{
    return do_domctl_maybe_retry_efault(xch, domctl, 0);
}

static inline int do_domctl_retry_efault(xc_interface *xch, struct xen_domctl *domctl)
{
    unsigned int retries = xencall_buffers_never_fault(xch->xcall) ? 0 : 2;

    return do_domctl_maybe_retry_efault(xch, domctl, retries);
}

static inline int do_sysctl(xc_interface *xch, struct xen_sysctl *sysctl)
{
    int ret = -1;
    DECLARE_HYPERCALL_BOUNCE(sysctl, sizeof(*sysctl), XC_HYPERCALL_BUFFER_BOUNCE_BOTH);

    sysctl->interface_version = XEN_SYSCTL_INTERFACE_VERSION;

    if ( xc_hypercall_bounce_pre(xch, sysctl) )
    {
        PERROR("Could not bounce buffer for sysctl hypercall");
        goto out1;
    }

    ret = xencall1(xch->xcall, __HYPERVISOR_sysctl,
                   HYPERCALL_BUFFER_AS_ARG(sysctl));
    if ( ret < 0 )
    {
        if ( errno == EACCES )
            DPRINTF("sysctl operation failed -- need to"
                    " rebuild the user-space tool set?\n");
    }

    xc_hypercall_bounce_post(xch, sysctl);
 out1:
    return ret;
}

static inline int do_platform_op(xc_interface *xch,
                                 struct xen_platform_op *platform_op)
{
    int ret = -1;
    DECLARE_HYPERCALL_BOUNCE(platform_op, sizeof(*platform_op),
                             XC_HYPERCALL_BUFFER_BOUNCE_BOTH);

    platform_op->interface_version = XENPF_INTERFACE_VERSION;

    if ( xc_hypercall_bounce_pre(xch, platform_op) )
    {
        PERROR("Could not bounce buffer for platform_op hypercall");
        return -1;
    }

    ret = xencall1(xch->xcall, __HYPERVISOR_platform_op,
                   HYPERCALL_BUFFER_AS_ARG(platform_op));
    if ( ret < 0 )
    {
        if ( errno == EACCES )
            DPRINTF("platform operation failed -- need to"
                    " rebuild the user-space tool set?\n");
    }

    xc_hypercall_bounce_post(xch, platform_op);
    return ret;
}

static inline int do_multicall_op(xc_interface *xch,
                                  xc_hypercall_buffer_t *call_list,
                                  uint32_t nr_calls)
{
    int ret = -1;
    DECLARE_HYPERCALL_BUFFER_ARGUMENT(call_list);

    ret = xencall2(xch->xcall, __HYPERVISOR_multicall,
                   HYPERCALL_BUFFER_AS_ARG(call_list), nr_calls);
    if ( ret < 0 )
    {
        if ( errno == EACCES )
            DPRINTF("multicall operation failed -- need to"
                    " rebuild the user-space tool set?\n");
    }

    return ret;
}

long do_memory_op(xc_interface *xch, int cmd, void *arg, size_t len);

void *xc_map_foreign_ranges(xc_interface *xch, uint32_t dom,
                            size_t size, int prot, size_t chunksize,
                            privcmd_mmap_entry_t entries[], int nentries);

int xc_get_pfn_type_batch(xc_interface *xch, uint32_t dom,
                          unsigned int num, xen_pfn_t *);

void bitmap_64_to_byte(uint8_t *bp, const uint64_t *lp, int nbits);
void bitmap_byte_to_64(uint64_t *lp, const uint8_t *bp, int nbits);

/* Optionally flush file to disk and discard page cache */
void discard_file_cache(xc_interface *xch, int fd, int flush);

#define MAX_MMU_UPDATES 1024
struct xc_mmu {
    mmu_update_t updates[MAX_MMU_UPDATES];
    int          idx;
    unsigned int subject;
};
/* Structure returned by xc_alloc_mmu_updates must be free()'ed by caller. */
struct xc_mmu *xc_alloc_mmu_updates(xc_interface *xch, unsigned int subject);
int xc_add_mmu_update(xc_interface *xch, struct xc_mmu *mmu,
                   unsigned long long ptr, unsigned long long val);
int xc_flush_mmu_updates(xc_interface *xch, struct xc_mmu *mmu);

/* Return 0 on success; -1 on error setting errno. */
int read_exact(int fd, void *data, size_t size); /* EOF => -1, errno=0 */
int write_exact(int fd, const void *data, size_t size);
int writev_exact(int fd, const struct iovec *iov, int iovcnt);

int xc_ffs8(uint8_t x);
int xc_ffs16(uint16_t x);
int xc_ffs32(uint32_t x);
int xc_ffs64(uint64_t x);

#define DOMPRINTF(fmt, args...) xc_dom_printf(dom->xch, fmt, ## args)
#define DOMPRINTF_CALLED(xch) xc_dom_printf((xch), "%s: called", __FUNCTION__)

/**
 * vm_event operations. Internal use only.
 */
int xc_vm_event_control(xc_interface *xch, uint32_t domain_id, unsigned int op,
                        unsigned int mode, uint32_t *port);
/*
 * Enables vm_event and returns the mapped ring page indicated by param.
 * param can be HVM_PARAM_PAGING/ACCESS/SHARING_RING_PFN
 */
void *xc_vm_event_enable(xc_interface *xch, uint32_t domain_id, int param,
                         uint32_t *port);

int do_dm_op(xc_interface *xch, uint32_t domid, unsigned int nr_bufs, ...);

#endif /* __XC_PRIVATE_H__ */

/*
 * Local variables:
 * mode: C
 * c-file-style: "BSD"
 * c-basic-offset: 4
 * tab-width: 4
 * indent-tabs-mode: nil
 * End:
 */
