#ifndef __PV_MM_H__
#define __PV_MM_H__

#include <asm/shadow.h>

l1_pgentry_t *map_guest_l1e(unsigned long linear, mfn_t *gl1mfn);

int new_guest_cr3(mfn_t mfn);

/*
 * Read the guest's l1e that maps this address, from the kernel-mode
 * page tables.
 */
static inline l1_pgentry_t guest_get_eff_kern_l1e(unsigned long linear)
{
    struct vcpu *curr = current;
    bool user_mode = !(curr->arch.flags & TF_kernel_mode);
    l1_pgentry_t l1e;

    ASSERT(!paging_mode_translate(curr->domain));
    ASSERT(!paging_mode_external(curr->domain));

    if ( user_mode )
        toggle_guest_pt(curr);

    if ( unlikely(!__addr_ok(linear)) ||
         get_unsafe(l1e, &__linear_l1_table[l1_linear_offset(linear)]) )
        l1e = l1e_empty();

    if ( user_mode )
        toggle_guest_pt(curr);

    return l1e;
}

static inline void paging_write_guest_entry(
    struct vcpu *v, intpte_t *p, intpte_t new, mfn_t gmfn)
{
    if ( unlikely(paging_mode_shadow(v->domain)) )
        shadow_write_guest_entry(v, p, new, gmfn);
    else
        write_atomic(p, new);
}


/* Compare and exchange a guest pagetable entry.  Returns the old value. */
static inline intpte_t paging_cmpxchg_guest_entry(
    struct vcpu *v, intpte_t *p, intpte_t old, intpte_t new, mfn_t gmfn)
{
    if ( unlikely(paging_mode_shadow(v->domain)) )
        return shadow_cmpxchg_guest_entry(v, p, old, new, gmfn);
    return cmpxchg(p, old, new);
}

/*
 * PTE updates can be done with ordinary writes except:
 *  1. Debug builds get extra checking by using CMPXCHG[8B].
 */
#ifndef NDEBUG
#define PTE_UPDATE_WITH_CMPXCHG
#else
#undef PTE_UPDATE_WITH_CMPXCHG
#endif

/*
 * How to write an entry to the guest pagetables.
 * Returns false for failure (pointer not valid), true for success.
 */
static inline bool update_intpte(intpte_t *p, intpte_t old, intpte_t new,
                                 mfn_t mfn, struct vcpu *v, bool preserve_ad)
{
    bool rv = true;

#ifndef PTE_UPDATE_WITH_CMPXCHG
    if ( !preserve_ad )
        paging_write_guest_entry(v, p, new, mfn);
    else
#endif
    {
        for ( ; ; )
        {
            intpte_t _new = new, t;

            if ( preserve_ad )
                _new |= old & (_PAGE_ACCESSED | _PAGE_DIRTY);

            t = paging_cmpxchg_guest_entry(v, p, old, _new, mfn);

            if ( t == old )
                break;

            /* Allowed to change in Accessed/Dirty flags only. */
            BUG_ON((t ^ old) & ~(intpte_t)(_PAGE_ACCESSED|_PAGE_DIRTY));

            old = t;
        }
    }
    return rv;
}

/*
 * Macro that wraps the appropriate type-changes around update_intpte().
 * Arguments are: type, ptr, old, new, mfn, vcpu
 */
#define UPDATE_ENTRY(_t,_p,_o,_n,_m,_v,_ad)                         \
    update_intpte(&_t ## e_get_intpte(*(_p)),                       \
                  _t ## e_get_intpte(_o), _t ## e_get_intpte(_n),   \
                  (_m), (_v), (_ad))

static always_inline l1_pgentry_t adjust_guest_l1e(l1_pgentry_t l1e,
                                                   const struct domain *d)
{
    if ( likely(l1e_get_flags(l1e) & _PAGE_PRESENT) &&
         likely(!is_pv_32bit_domain(d)) )
    {
        /* _PAGE_GUEST_KERNEL page cannot have the Global bit set. */
        if ( (l1e_get_flags(l1e) & (_PAGE_GUEST_KERNEL | _PAGE_GLOBAL)) ==
             (_PAGE_GUEST_KERNEL | _PAGE_GLOBAL) )
            gdprintk(XENLOG_WARNING, "Global bit is set in kernel page %lx\n",
                     l1e_get_pfn(l1e));

        if ( !(l1e_get_flags(l1e) & _PAGE_USER) )
            l1e_add_flags(l1e, (_PAGE_GUEST_KERNEL | _PAGE_USER));

        if ( !(l1e_get_flags(l1e) & _PAGE_GUEST_KERNEL) )
            l1e_add_flags(l1e, (_PAGE_GLOBAL | _PAGE_USER));
    }

    return l1e;
}

static always_inline l2_pgentry_t adjust_guest_l2e(l2_pgentry_t l2e,
                                                   const struct domain *d)
{
    if ( likely(l2e_get_flags(l2e) & _PAGE_PRESENT) &&
         likely(!is_pv_32bit_domain(d)) )
        l2e_add_flags(l2e, _PAGE_USER);

    return l2e;
}

static always_inline l3_pgentry_t adjust_guest_l3e(l3_pgentry_t l3e,
                                                   const struct domain *d)
{
    if ( likely(l3e_get_flags(l3e) & _PAGE_PRESENT) )
        l3e_add_flags(l3e, (likely(!is_pv_32bit_domain(d))
                            ? _PAGE_USER : _PAGE_USER | _PAGE_RW));

    return l3e;
}

static always_inline l3_pgentry_t unadjust_guest_l3e(l3_pgentry_t l3e,
                                                     const struct domain *d)
{
    if ( unlikely(is_pv_32bit_domain(d)) &&
         likely(l3e_get_flags(l3e) & _PAGE_PRESENT) )
        l3e_remove_flags(l3e, _PAGE_USER | _PAGE_RW | _PAGE_ACCESSED);

    return l3e;
}

static always_inline l4_pgentry_t adjust_guest_l4e(l4_pgentry_t l4e,
                                                   const struct domain *d)
{
    /*
     * When shadowing an L4 behind the guests back (e.g. for per-pcpu
     * purposes), we cannot efficiently sync access bit updates from hardware
     * (on the shadow tables) back into the guest view.
     *
     * We therefore unconditionally set _PAGE_ACCESSED even in the guests
     * view.  This will appear to the guest as a CPU which proactively pulls
     * all valid L4e's into its TLB, which is compatible with the x86 ABI.
     *
     * At the time of writing, all PV guests set the access bit anyway, so
     * this is no actual change in their behaviour.
     */
    if ( likely(l4e_get_flags(l4e) & _PAGE_PRESENT) )
        l4e_add_flags(l4e, (_PAGE_ACCESSED |
                            (is_pv_32bit_domain(d) ? 0 : _PAGE_USER)));

    return l4e;
}

#endif /* __PV_MM_H__ */
