/*
 * Copyright (C) 2007 - Dietmar Hahn <dietmar.hahn@fujitsu-siemens.com>
 *
 ****************************************************************************
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 */


#if !defined(__OS_H__)
#define __OS_H__

#if !defined(__ASSEMBLY__)

#include "types.h"
#include "endian.h"
#include "ia64_cpu.h"
#include "atomic.h"
#include "efi.h"
#include "sal.h"
#include "pal.h"
#include "hypervisor.h"


typedef uint64_t paddr_t;		/* Physical address. */
typedef uint64_t caddr_t;		/* rr7/kernel memory address. */

#include "page.h"
#include "mm.h"


void do_exit(void);
void arch_init(start_info_t *si);	/* in common.c */
void arch_print_info(void);		/* in common.c */


/* Size of xen_ia64_boot_param.command_line */
#define COMMAND_LINE_SIZE       512

extern struct xen_ia64_boot_param* ia64_boot_paramP;
extern struct xen_ia64_boot_param ia64BootParamG;
extern char boot_cmd_line[];
extern efi_system_table_t* efiSysTableP;
extern int bootverbose;

extern void ia64_probe_sapics(void);



/* Contains the needed stuff from efi. */
struct efi
{

	efi_system_table_t*		efiSysTableP;
	efi_set_virtual_address_map_t	setVirtAddrMapF;
	efi_get_time_t			getTimeF;
	efi_reset_system_t		resetSystemF;

};

struct machine_fw
{
	struct efi efi;

	uint64_t ia64_port_base;	/* physical address */
	uint64_t ia64_pal_base;		/* virtual rr7 address */

	sal_system_table_t* ia64_sal_tableP;
	sal_entry_t* ia64_sal_entryP;	/* SAL_PROC entrypoint */

	uint64_t ia64_efi_acpi_table;	/* physical address */
	uint64_t ia64_efi_acpi20_table;	/* physical address */

	uint64_t mach_mem_start;	/* phys start addr of machine memory */
	uint64_t mach_mem_size;		/* size of machine memory */

	uint64_t kernstart;		/* virt address of kern text start */
	uint64_t kernend;
	uint64_t kernpstart;		/* phys address of kern text start */
	uint64_t kernpend;
};

extern struct machine_fw machineFwG;

#define ia64_sal_entry machineFwG.ia64_sal_entryP

#define smp_processor_id() 0

static inline uint64_t
xchg8(uint64_t* ptr, uint64_t x)                                               \
{
        uint64_t oldVal;
        asm volatile ("xchg8 %0=[%1],%2" : "=r" (oldVal)
                      : "r" (ptr), "r" (x) : "memory");
        return oldVal;
}
#define xchg xchg8

// Counts the number of 1-bits in x.
#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
# define get_popcnt(x)         __builtin_popcountl(x)
#else
# define get_popcnt(x)					\
  ({							\
	uint64_t num;					\
	asm ("popcnt %0=%1" : "=r" (num) : "r" (x));	\
	num;						\
  })
#endif

/**
 * __ffs - find first bit in word.
 * @x: The word to search
 *
 * Undefined if no bit exists, so code should check against 0 first.
 */
static inline unsigned long
__ffs (unsigned long x)
{
	unsigned long result;

	result = get_popcnt((x-1) & ~x);
	return result;
}


static inline void
synch_clear_bit(int num, volatile void *addr)
{
	clear_bit(num, addr);
}

static inline void
synch_set_bit(int num, volatile void *addr)
{
	set_bit(num, addr);
}

static inline int
synch_test_bit(int nr, const volatile void *addr)
{
	return test_bit(nr, addr);
}

static inline int
synch_test_and_set_bit(int num, volatile void * addr)
{
	return test_and_set_bit(num, addr);
}


#define synch_cmpxchg(ptr, old, new) \
((__typeof__(*(ptr)))__synch_cmpxchg((ptr),\
                                     (unsigned long)(old), \
                                     (unsigned long)(new), \
                                     sizeof(*(ptr))))

static inline unsigned long
__synch_cmpxchg(volatile void *ptr, uint64_t old, uint64_t new, int size)
{
	switch (size)
	{
		case 1:
			return ia64_cmpxchg_acq_8(ptr, old, new);
		case 2:
			return ia64_cmpxchg_acq_16(ptr, old, new);
		case 4:
			return ia64_cmpxchg_acq_32(ptr, old, new);
		case 8:
			return ia64_cmpxchg_acq_64(ptr, old, new);
	}
	return ia64_cmpxchg_acq_64(ptr, old, new);
}

/*
 * Force a proper event-channel callback from Xen after clearing the
 * callback mask. We do this in a very simple manner, by making a call
 * down into Xen. The pending flag will be checked by Xen on return.
 */
static inline void
force_evtchn_callback(void)
{
	(void)HYPERVISOR_xen_version(0, NULL);
}

extern shared_info_t *HYPERVISOR_shared_info;

static inline int
HYPERVISOR_shutdown(unsigned int reason)
{
	struct sched_shutdown sched_shutdown = {
		.reason = reason
	};

	int rc = HYPERVISOR_sched_op(SCHEDOP_shutdown, &sched_shutdown);

	if (rc == -ENOSYS)
		rc = HYPERVISOR_sched_op_compat(SCHEDOP_shutdown, reason);

	return rc;
}


/*
 * This code is from the originally os.h and should be put in a
 * common header file!
 */

/* 
 * The use of 'barrier' in the following reflects their use as local-lock
 * operations. Reentrancy must be prevented (e.g., __cli()) /before/ following
 * critical operations are executed. All critical operations must complete
 * /before/ reentrancy is permitted (e.g., __sti()). Alpha architecture also
 * includes these barriers, for example.
 */

#define __cli()								\
do {									\
	vcpu_info_t *_vcpu;						\
	_vcpu = &HYPERVISOR_shared_info->vcpu_info[smp_processor_id()];	\
	_vcpu->evtchn_upcall_mask = SWAP(1);				\
	barrier();							\
} while (0)

#define __sti()								\
do {									\
	vcpu_info_t *_vcpu;						\
	barrier();							\
	_vcpu = &HYPERVISOR_shared_info->vcpu_info[smp_processor_id()];	\
	_vcpu->evtchn_upcall_mask = 0;					\
	barrier(); /* unmask then check (avoid races) */		\
	if (unlikely(SWAP(_vcpu->evtchn_upcall_pending)))		\
		force_evtchn_callback();				\
} while (0)

#define __save_flags(x)							\
do {									\
	vcpu_info_t *_vcpu;						\
	_vcpu = &HYPERVISOR_shared_info->vcpu_info[smp_processor_id()];	\
	(x) = SWAP(_vcpu->evtchn_upcall_mask);				\
} while (0)

#define __restore_flags(x)						\
do {									\
	vcpu_info_t *_vcpu;						\
	barrier();							\
	_vcpu = &HYPERVISOR_shared_info->vcpu_info[smp_processor_id()];	\
	if ((_vcpu->evtchn_upcall_mask = (x)) == 0) {			\
		barrier(); /* unmask then check (avoid races) */	\
		if ( unlikely(SWAP(_vcpu->evtchn_upcall_pending)) )	\
			force_evtchn_callback();			\
	}\
} while (0)

#define safe_halt()		((void)0)

#define __save_and_cli(x)						\
do {									\
	vcpu_info_t *_vcpu;						\
	_vcpu = &HYPERVISOR_shared_info->vcpu_info[smp_processor_id()];	\
	(x) = SWAP(_vcpu->evtchn_upcall_mask);				\
	_vcpu->evtchn_upcall_mask = SWAP(1);				\
	barrier();							\
} while (0)

#define local_irq_save(x)	__save_and_cli(x)
#define local_irq_restore(x)	__restore_flags(x)
#define local_save_flags(x)	__save_flags(x)
#define local_irq_disable()	__cli()
#define local_irq_enable()	__sti()

#define irqs_disabled()			\
	SWAP(HYPERVISOR_shared_info->vcpu_info[smp_processor_id()].evtchn_upcall_mask)

/* This is a barrier for the compiler only, NOT the processor! */
#define barrier() __asm__ __volatile__("": : :"memory")

#define mb()	ia64_mf()
#define rmb()	mb()
#define wmb()	mb()


#define BUG()	\
	{ printk("mini-os BUG at %s:%d!\n", __FILE__, __LINE__); do_exit(); }

#define PRINT_BV(_fmt, _params...)		\
	if (bootverbose)			\
		printk(_fmt , ## _params)

#endif /* !defined(__ASSEMBLY__) */

#if defined(__ASSEMBLY__)

#define UL_CONST(x)	x
#define UL_TYPE(x)	x

#else /* defined(__ASSEMBLY__) */

#define UL_CONST(x)	x##UL
#define UL_TYPE(x)	((uint64_t)x)

#endif /* defined(__ASSEMBLY__) */

#endif /* !defined(__OS_H__) */
