/*********************************************************************
 *                
 * Copyright (C) 2002-2006,  Karlsruhe University
 *                
 * File path:     api/v4/fpage.h
 * Description:   V4 flexpages
 *                
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *                
 * $Id: fpage.h,v 1.26 2006/11/14 18:46:31 skoglund Exp $
 *                
 ********************************************************************/
#ifndef __API__V4__FPAGE_H__
#define __API__V4__FPAGE_H__

#include INC_API(config.h)
#include INC_GLUE(fpage.h)

class mempage_t 
{
public:
    union {
	struct {
	    BITFIELD7(word_t,
		      execute		: 1,
		      write		: 1,
		      read		: 1,
		      reserved		: 1,
		      size		: 6,
		      base		: L4_FPAGE_BASE_BITS,
		      : BITS_WORD - L4_FPAGE_BASE_BITS - 10
		);
	} x __attribute__((packed));
	word_t raw;
    };
};

/**
 * Flexpages are size-aligned memory objects and can cover 
 * multiple hardware pages. fpage_t implements the V4 specific
 * flexpage type, having read, write and execute bits.
 */
class fpage_t
{
    /* data members */
public:
    union {
	mempage_t mem;
	arch_fpage_t arch;
	word_t raw;
    };
    /* member functions */
public:
    /**
     * sets the flexpage
     */
    void set(word_t base, word_t size, bool read, bool write, bool exec)
	{
	    if (EXPECT_FALSE(arch.is_valid_page() == false))
	    {
		raw = 0;
		mem.x.base = (base & (~0UL << size)) >> 10;
		mem.x.size = size;
		mem.x.read = read;
		mem.x.write = write;
		mem.x.execute = exec;
	    }
	    else 
	    {
		arch.set(base, size, read, write, exec);
	    }
	}
    
    /**
     * @return true if the flexpage is a nil fpage
     */
    bool is_nil_fpage() 
	{ return raw == 0; }
    
    /**
     * @return true if the flexpage is a memory page
     */
    bool is_mempage (void)
	{ return arch.is_valid_page() == false; }


    /**
     * @return true if the flexpage is an architecture specific page
     */
    bool is_archpage (void)
	{ return arch.is_valid_page() == true; }


    /**
     * @return true if flexpage covers the whole address space
     * the complete fpage is a special value defined in the V4 API
     */
    bool is_complete_mempage() 
	{ return is_mempage () && mem.x.size == 1 && mem.x.base == 0; }

    bool is_complete_fpage() 
	{ return (is_mempage () && is_complete_mempage()) ||
		(arch.is_valid_page () && arch.is_complete_page()); }
    
    /**
     * checks if a given address range is contained completely within
     * the fpage
     * 
     * @param start start address of range
     * @param end end address of range
     * @return true if range is contained within fpage, false otherwise */
    bool is_range_in_fpage(addr_t start, addr_t end)
	{
	    /* VU: should we make sure, that base is valid? */
	    return (is_complete_fpage()) ||
		(get_address() <= start && addr_offset(get_address(), get_size()) >= end);
	}
    
    /**
     * @param addr returns true if addr is within the range of 
     * the flexpage
     */
    bool is_addr_in_fpage(addr_t addr)
	{ return is_range_in_fpage (addr, (addr_t)((word_t)addr + sizeof(addr_t))); }

    /**
     * checks if the current and the given fpage overlap
     * @return true if fpages overlap
     */
    bool is_overlapping(fpage_t fpage)
	{
	    if (is_complete_fpage ())
		return true;
	    else if (fpage.get_address() < this->get_address())
		return (addr_offset(fpage.get_address(), fpage.get_size()) >
			this->get_address());
	    else 
		return (addr_offset(this->get_address(), this->get_size()) >
			fpage.get_address());
	}

    bool is_range_overlapping(addr_t start, addr_t end)
	{
	    if (is_complete_fpage ())
		return true;
	    else if (start < get_address())
		return end > get_address();
	    else
		return addr_offset(get_address(), get_size()) > start;
	}

    /**
     * @return base address of the fpage
     * get_base does not size-align the address
     */
    addr_t get_base() 
	{ return is_mempage() ? (addr_t)(mem.x.base << 10) : arch.get_base(); }

    /**
     * @return size aligned address of the fpage
     */
    addr_t get_address()
	{ return is_mempage() 
		? (addr_t)((mem.x.base << 10) & (~0UL << mem.x.size))
		: (addr_t)(arch.get_address());
	}
    
    
    /**
     * @return size of the flexpage
     */
    word_t get_size() 
	{ return is_mempage() ? (1UL << mem.x.size) : arch.get_size(); }

    /**
     * @return log2 size of the fpage
     */
    word_t get_size_log2() 
	{ return is_mempage() 
		? is_complete_mempage() ? sizeof(word_t) * 8 : mem.x.size
		: arch.get_size_log2(); 
	}

    /**
     * @return access rights of fpage
     */
    word_t get_access (void)
	   { return get_rwx(); }

    /**
     * @return true if the read bit is set
     */  
    bool is_read() 
	{ return mem.x.read; }

    /** 
     * @return true if the write bit is set
     */
    bool is_write() 
	{ return mem.x.write; }

    /**
     * @return true if the execute bit is set
     */
    bool is_execute() 
	{ return mem.x.execute; }

    /**
     * @return true if read, write and execute bits are set
     */
    bool is_rwx()
	{ return mem.x.read && mem.x.write && mem.x.execute; }

    /**
     * sets all permission bits in the fpage
     */
    void set_rwx()
	{ mem.x.read = 1; mem.x.write = 1; mem.x.execute = 1; }

    void set_rwx(word_t rwx)
	{ raw = (raw & ~7) | (rwx & 7); }

    /**
     * @return access rights of fpage
     */
    word_t get_rwx()
	{ return raw & 7; }

    /**
     * @return delivers an fpage covering the complete address space
     */
    static fpage_t complete_mem()
	{
	    fpage_t ret;
	    ret.raw = 0;
	    ret.mem.x.size = 1;
	    return ret;
	}


   /**
     * @return delivers an fpage covering the complete architecture-specific
     * address space
     */
    static fpage_t complete_arch ()
	{
	    fpage_t ret;
	    ret.arch = arch_fpage_t::complete();
	    return ret;
	}

    /**
     * @return delivers an nil fpage
     */
    static fpage_t nilpage()
	{
	    fpage_t ret;
	    ret.raw = 0;
	    return ret;
	}
    

};


/*
 * Helper functions used in conjunction with mapping.
 */

INLINE word_t base_mask (fpage_t fp, word_t size)
{
    return ((~0UL) >> ((sizeof (word_t) * 8) - fp.get_size_log2 ())) &
	((size == 0 ? (~0UL) : ~((~0UL) >> ((sizeof (word_t) * 8) - size))));
}

INLINE addr_t address (fpage_t fp, word_t size)
{
    return (addr_t) ((word_t) fp.get_base () & ~((1UL << size) - 1));
}


#endif /* !__API__V4__FPAGE_H__ */