#include "secbus_lib.h"

void DES(int *in, int *out, int *key)
{
		for(unsigned int i=0;i<SIZE_64_IN_WORDS;i++)
			out[i] = in[i] ^ key[i];

}

template<typename T>
unsigned int _ctz(T n, unsigned int p)
{
	for (unsigned int i = 0; i<p; ++i )
  {
		if ( (1<<i)&n )
			return i;
  }
	return 0;
}

bool fill_64(int be, addr_t address)
{
	unsigned int i = index(aligned(address, SIZE_64_IN_BYTES), SIZE_256_IN_BYTES);
	return (((be >> i*4) & 0xFF) == 0xFF);
}

bool inclusion(addr_t address, addr_t low_address, addr_t up_address)
{
	return (address >= low_address && address < up_address);
}
bool inferior(addr_t address, addr_t up_address)
{
	return (address <= up_address);
}
bool superior(addr_t address, addr_t low_address)
{
	return (address >= low_address);
}
addr_t aligned(addr_t address, int block_size_in_bytes)
{
	return (address - address % block_size_in_bytes);
}
unsigned int index(addr_t address, int block_size_in_bytes)
{
	return ((address % block_size_in_bytes)/sizeof(int));
}

addr_t get4kPageAddress(addr_t address)
{
	return (address & 0xFFFFF000);
}

addr_t get4mPageAddress(addr_t address)
{
	return (address & 0xFFC00000);
}

addr_t getFirstLevelAddress(addr_t base_address, addr_t address)
{
	//10 msb of address
	int offset = PSPE_SIZE_IN_BYTES * (address >> 22 & 0x3FF);
	return (base_address + offset);
}

addr_t getSecondLevelAddress(addr_t base_address, addr_t address)
{
	//20 msb of address
	int offset = PSPE_SIZE_IN_BYTES * (address >> 12 & 0xFFFFF);
	return (base_address + offset);
}

unsigned int getSPAddress(int SP_index)
{
	return (SP_BASE_ADDRESS + SP_SIZE_IN_BYTES * SP_index);
}

bool getValidBit(int* PSPE)
//bit 0 of PSPE[0] 
{
	return (PSPE[0]&0x1);
}

void setValidBit(int* PSPE, bool V)
//bit 0 of PSPE[0] 
{
	PSPE[0] = (PSPE[0] & 0xFFFFFFFE) | V;
}

PSPEtype_e getTypeBit(int* PSPE)
//bit 2 of PSPE[0]
{
	return (PSPEtype_e)((PSPE[0]&0x4)>>2);
}

void setTypeBit(int* PSPE, PSPEtype_e T)
//bit 2 of PSPE[0]
{
	PSPE[0] = (PSPE[0] & 0xFFFFFFFB) |(T << 2);
}
PageSize_e getSizeBit(int* PSPE)
//bit 1 of PSPE[0]
{
	return (PageSize_e)((PSPE[0] & 0x2)>>1);
}
void setSizeBit(int* PSPE, PageSize_e PS)
//bit 1 of PSPE[0]
{
	PSPE[0] = (PSPE[0] & 0xFFFFFFFD) | (PS << 1);
}

unsigned int getSPindex(int* PSPE)
//bit 3 to 21 of PSPE[0]
{
	return ((PSPE[0] & 0x001FFFF8) >> 3);
}
void setSPindex(int* PSPE, int SP_index)
//bit 3 to 21 of PSPE[0]
{
	PSPE[0] = (PSPE[0] & 0xFFE00007) | (SP_index << 3);
}

void getRHV(int* PSPE, int * RHV)
{
	for (unsigned int i = 0x0 ; i < SIZE_64_IN_WORDS; i++)
		RHV[i] =  (i == 0) ? (PSPE[i] & 0xFFFFFFF8) : PSPE[i] ;
}

void setRHV(int* PSPE, int *RHV)
{
	for (unsigned int i=0;i<SIZE_64_IN_WORDS;i++)
		PSPE[i] = (i == 0) ? ((PSPE[i] & 0x7) | (RHV[i] & 0xFFFFFFF8)) : RHV[i];
}

addr_t getBaseAddress(int* PSPE)
{
	return PSPE[1];
}
void setBaseAddress(int* PSPE, addr_t base_address)
{
	PSPE[1] = base_address;
}

confidentiality_mode_e getPM(int* SP)
//First 2 bits of first SP word 
{
	return (confidentiality_mode_e)(SP[0] & 0x3);
}

integrity_mode_e getIM(int* SP)
//Next 2 bits of first SP word
{
	return (integrity_mode_e)((SP[0] & 0xC)>>2);
}
void setPM(int* SP, confidentiality_mode_e PM)
//First 2 bits of first SP word
{
	SP[0] = (SP[0] & 0xFFFFFFFC) | PM;
}

void setIM(int* SP, integrity_mode_e IM)
//Next 2 bits of first SP word
{
	SP[0] = (SP[0] & 0xFFFFFFF3) | (IM << 2);
}

void getSK(int* SP, int* Key)
{
	unsigned int i;
	for ( i = 0x0 ; i < SP_SIZE ; i++)
		Key[i] = (i == 0) ? (SP[i] & 0xFFFFFFF0) : SP[i];
}

void setSK(int* SP, int *PSPE, int * Key)
{
	unsigned int i;
	for ( i = 0x0; i < SP_SIZE; i++)
		SP[i] = (i == 0) ? ((PSPE[i] & 0xF) + (Key[i] & 0xFFFFFFF0)) : Key[i];
}

addr_t getPageBaseAddress(addr_t address, PageSize_e ht_page_size)
{
	return (ht_page_size == SIZE_4K)? address & 0xFFFFF000 : address & 0xFFC00000; ; 
} 

bool isLastLevel(addr_t address, PageSize_e ht_page_size)
{
	addr_t page_start_address = getPageBaseAddress(address, ht_page_size);
	unsigned int offset = isMasterTree(address)? address - MHT_BASE_ADDRESS : address - page_start_address ;
	
	return inclusion(offset, 0, 2*HASH_ARITY*SIZE_64_IN_BYTES);	
}


addr_t getParentAddress(addr_t node_address, PageSize_e ht_page_size)
{
	addr_t start_address;
	unsigned int index_64, index_256;
 
	if(isMasterTree(node_address))	start_address = MHT_BASE_ADDRESS;
	else start_address = getPageBaseAddress(node_address, ht_page_size);
    //64-bit block index
	index_64 = (node_address - start_address)/SIZE_64_IN_BYTES;
    //256-bit block index
	index_256 = index_64/HASH_ARITY;

	//last set
	if(isLastLevel(node_address, ht_page_size)) { return 0;}
	//next to last set
	else if (inclusion(index_64, 0x8, 0x20)) return (start_address + (index_256 - 2) * SIZE_64_IN_BYTES);	
	else return (start_address + index_256 * SIZE_64_IN_BYTES);	    
} 

addr_t getParentLeavesAddress(addr_t leaf_address, addr_t ht_start_address, PageSize_e ht_page_size)
{
	unsigned int page_offset;
	if(isMasterTree(leaf_address))
	{
		page_offset = leaf_address - PSPE_BASE_ADDRESS;
		return (0x100000 + page_offset/HASH_ARITY + MHT_BASE_ADDRESS);
	}
	else 
	{
		page_offset = (ht_page_size == SIZE_4M)? (leaf_address & 0x003FFFFF) : (leaf_address & 0x00000FFF);
		return (page_offset/HASH_ARITY + ht_start_address);
	}
}

addr_t getSetAddress(addr_t node_address, PageSize_e ht_page_size)
{
	return (isLastLevel(node_address, ht_page_size) ? node_address & 0xFFFFFFC7 : node_address & 0xFFFFFFE7);
}

unsigned int getBlockIndex(addr_t address, PageSize_e ht_page_size)
{
	return (isLastLevel(address, ht_page_size) ? (address & 0x38) >> 3 : (address & 0x18) >> 3);
}

addr_t getMacAddress(addr_t address, addr_t page_address, PageSize_e page_size)
{
	unsigned int page_offset = (page_size == SIZE_4M)? (address & 0x003FFFFF) : (address & 0x00000FFF) ; 
	unsigned int mac_index = page_offset/MS_SIZE_IN_BYTES;
	return (page_address + mac_index * SIZE_64_IN_BYTES);   
} 

bool isMasterTree(addr_t address)
{
	return inclusion(address, PSPE_BASE_ADDRESS, MHT_END_ADDRESS);
}

bool isMasterBlock(addr_t address)
{
	return inclusion(address, PSPE_BASE_ADDRESS, SP_END_ADDRESS);
}

addr_t getBrotherAddress(addr_t node_address, PageSize_e ht_page_size)
{	
	unsigned int temp = isLastLevel(node_address, ht_page_size)? HASH_ARITY + 2 : HASH_ARITY;

	unsigned int index = (getBlockIndex(node_address,  ht_page_size) + 1) % temp;

	return (getSetAddress(node_address, ht_page_size) +  (index * SIZE_64_IN_BYTES));	
}

bool compare(int *value, int *reference, int size_in_bytes)
{
	return !memcmp(value, reference, size_in_bytes);
}

