#ifndef _BITS_XEN_H
#define _BITS_XEN_H

/** @file
 *
 * Xen interface
 *
 */

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );

/* Hypercall registers */
#ifdef __x86_64__
#define XEN_REG1 "rdi"
#define XEN_REG2 "rsi"
#define XEN_REG3 "rdx"
#define XEN_REG4 "r10"
#define XEN_REG5 "r8"
#else
#define XEN_REG1 "ebx"
#define XEN_REG2 "ecx"
#define XEN_REG3 "edx"
#define XEN_REG4 "esi"
#define XEN_REG5 "edi"
#endif

/** A hypercall entry point */
struct xen_hypercall {
	/** Code generated by hypervisor */
	uint8_t code[32];
} __attribute__ (( packed ));

/**
 * Issue hypercall with one argument
 *
 * @v xen		Xen hypervisor
 * @v hypercall		Hypercall number
 * @v arg1		First argument
 * @ret retval		Return value
 */
static inline __attribute__ (( always_inline )) unsigned long
xen_hypercall_1 ( struct xen_hypervisor *xen, unsigned int hypercall,
		  unsigned long arg1 ) {
	register unsigned long reg1 asm ( XEN_REG1 ) = arg1;
	unsigned long retval;

	__asm__ __volatile__ ( "call *%2"
			       : "=a" ( retval ), "+r" ( reg1 )
			       : "r" ( &xen->hypercall[hypercall] )
			       : XEN_REG2, XEN_REG3, XEN_REG4, XEN_REG5,
				 "memory" );
	return retval;
}

/**
 * Issue hypercall with two arguments
 *
 * @v xen		Xen hypervisor
 * @v hypercall		Hypercall number
 * @v arg1		First argument
 * @v arg2		Second argument
 * @ret retval		Return value
 */
static inline __attribute__ (( always_inline )) unsigned long
xen_hypercall_2 ( struct xen_hypervisor *xen, unsigned int hypercall,
		  unsigned long arg1, unsigned long arg2 ) {
	register unsigned long reg1 asm ( XEN_REG1 ) = arg1;
	register unsigned long reg2 asm ( XEN_REG2 ) = arg2;
	unsigned long retval;

	__asm__ __volatile__ ( "call *%3"
			       : "=a" ( retval ), "+r" ( reg1 ), "+r" ( reg2 )
			       : "r" ( &xen->hypercall[hypercall] )
			       : XEN_REG3, XEN_REG4, XEN_REG5, "memory" );
	return retval;
}

/**
 * Issue hypercall with three arguments
 *
 * @v xen		Xen hypervisor
 * @v hypercall		Hypercall number
 * @v arg1		First argument
 * @v arg2		Second argument
 * @v arg3		Third argument
 * @ret retval		Return value
 */
static inline __attribute__ (( always_inline )) unsigned long
xen_hypercall_3 ( struct xen_hypervisor *xen, unsigned int hypercall,
		  unsigned long arg1, unsigned long arg2, unsigned long arg3 ) {
	register unsigned long reg1 asm ( XEN_REG1 ) = arg1;
	register unsigned long reg2 asm ( XEN_REG2 ) = arg2;
	register unsigned long reg3 asm ( XEN_REG3 ) = arg3;
	unsigned long retval;

	__asm__ __volatile__ ( "call *%4"
			       : "=a" ( retval ), "+r" ( reg1 ), "+r" ( reg2 ),
				 "+r" ( reg3 )
			       : "r" ( &xen->hypercall[hypercall] )
			       : XEN_REG4, XEN_REG5, "memory" );
	return retval;
}

/**
 * Issue hypercall with four arguments
 *
 * @v xen		Xen hypervisor
 * @v hypercall		Hypercall number
 * @v arg1		First argument
 * @v arg2		Second argument
 * @v arg3		Third argument
 * @v arg4		Fourth argument
 * @ret retval		Return value
 */
static inline __attribute__ (( always_inline )) unsigned long
xen_hypercall_4 ( struct xen_hypervisor *xen, unsigned int hypercall,
		  unsigned long arg1, unsigned long arg2, unsigned long arg3,
		  unsigned long arg4 ) {
	register unsigned long reg1 asm ( XEN_REG1 ) = arg1;
	register unsigned long reg2 asm ( XEN_REG2 ) = arg2;
	register unsigned long reg3 asm ( XEN_REG3 ) = arg3;
	register unsigned long reg4 asm ( XEN_REG4 ) = arg4;
	unsigned long retval;

	__asm__ __volatile__ ( "call *%5"
			       : "=a" ( retval ), "+r" ( reg1 ), "+r" ( reg2 ),
				 "+r" ( reg3 ), "+r" ( reg4 )
			       : "r" ( &xen->hypercall[hypercall] )
			       : XEN_REG5, "memory" );
	return retval;
}

/**
 * Issue hypercall with five arguments
 *
 * @v xen		Xen hypervisor
 * @v hypercall		Hypercall number
 * @v arg1		First argument
 * @v arg2		Second argument
 * @v arg3		Third argument
 * @v arg4		Fourth argument
 * @v arg5		Fifth argument
 * @ret retval		Return value
 */
static inline __attribute__ (( always_inline )) unsigned long
xen_hypercall_5 ( struct xen_hypervisor *xen, unsigned int hypercall,
		  unsigned long arg1, unsigned long arg2, unsigned long arg3,
		  unsigned long arg4, unsigned long arg5 ) {
	register unsigned long reg1 asm ( XEN_REG1 ) = arg1;
	register unsigned long reg2 asm ( XEN_REG2 ) = arg2;
	register unsigned long reg3 asm ( XEN_REG3 ) = arg3;
	register unsigned long reg4 asm ( XEN_REG4 ) = arg4;
	register unsigned long reg5 asm ( XEN_REG5 ) = arg5;
	unsigned long retval;

	__asm__ __volatile__ ( "call *%6"
			       : "=a" ( retval ), "+r" ( reg1 ), "+r" ( reg2 ),
				 "+r" ( reg3 ), "+r" ( reg4 ), "+r" ( reg5 )
			       : "r" ( &xen->hypercall[hypercall] )
			       : "memory" );
	return retval;
}

/**
 * Test and clear pending event
 *
 * @v xen		Xen hypervisor
 * @v port		Event channel port
 * @ret pending		Event was pending
 */
static inline __attribute__ (( always_inline )) uint8_t
xenevent_pending ( struct xen_hypervisor *xen, evtchn_port_t port ) {
	uint8_t pending;

	__asm__ __volatile__ ( "lock btr %2, %0\n\t"
			       "setc %1\n\t"
			       : "+m" ( xen->shared->evtchn_pending ),
				 "=a"  ( pending )
			       : "Ir" ( port ) );
	return pending;
}

#endif /* _BITS_XEN_H */
