#include "security_context_controller.h"

SecurityContextController::SecurityContextController(sc_module_name name, const cache_param_st & pspe_parameters, const cache_param_st & sp_parameters, ofstream * dfile, ofstream * sfile): sc_module(name)
{
	
	_secbus_debug(cout, TRACE, "BUILDING SECURITY CONTEXT CONTROLLER ..."<<"\n");
	mode_4K = false;

	SC_METHOD(ps);
	sensitive << clk.pos();
	SC_METHOD(pc);
	sensitive << clk.neg();

	doutput = dfile;
	soutput = sfile;

	pspe_cache_param = pspe_parameters;
	sp_cache_param = sp_parameters;

	if (pspe_parameters.enable)
		pspe_cache = new Cache(pspe_parameters);

	if (sp_parameters.enable)
		sp_cache = new Cache(sp_parameters);

	pspe = new int[PSPE_SIZE];
	sp = new int[SP_SIZE];

	_secbus_debug(cout, TRACE, "SECURITY CONTEXT CONTROLLER BUILT"<<"\n");
}
void SecurityContextController::pc()
{
	out_ack.rack = false;
	out_ack.wack = false;

	sc_out_cmd.enable = false;
  sc_out_cmd.enable = false;
	sc_out_cmd.enable = false;

  // Regular Security Context
	scr_out.page_address = m_scontext.page_addr;
	scr_out.sp = m_scontext.sp;
	scr_out.mpspe = m_scontext.mpspe;
  // ALAP Security Context
	scr_out.alap_page_address = s_scontext.page_addr;
	scr_out.alap_spspe_address = s_scontext.spspe_addr;
	scr_out.alap_spspe = s_scontext.spspe;

	switch (state) 
	{
	case IDLE :
		out_ack.rack = true;
		out_ack.wack = true;
		break;

	case START_READ_PSPE_BLOCK :
		sc_out_cmd.enable = true;
		sc_out_cmd.cmd = READ;
		sc_out_cmd.start_address = pspe_address_s;
		sc_out_cmd.plen = pspe_cache_param.block_size * 4;
		break;
 
  case START_WRITE_PSPE_BLOCK:
		//cache disabled : write input block to memory
		sc_out_cmd.enable = true;
		sc_out_cmd.cmd = WRITE;
		sc_out_cmd.start_address = pspe_address_s;
		sc_out_cmd.plen = pspe_cache_param.block_size * 4;
    for (i = 0; i < PSPE_SIZE; i++)
		  sc_out_wdata.data[i] = pspe[i];
		break;

  case START_WRITE_BACK_PSPE_BLOCK : 
		//cache enabled : write victim block to memory
		sc_out_cmd.enable = true;
		sc_out_cmd.cmd = WRITE;
		sc_out_cmd.start_address = pspe_victim_block->getBaseAddress();
		sc_out_cmd.plen = pspe_cache_param.block_size * 4;
    for (i = 0; i < PSPE_SIZE; i++)
		  sc_out_wdata.data[i] = pspe_victim_block->getData()[i];
		break;
	
	case START_WRITE_THROUGH_PSPE_BLOCK :
		//cache enabled : write input block to memory		
		sc_out_cmd.enable = true;
		sc_out_cmd.cmd = WRITE;
		sc_out_cmd.start_address = pspe_address_s;
		sc_out_cmd.plen = pspe_cache_param.block_size * 4;
    for (i = 0; i < PSPE_SIZE; i++)
		  sc_out_wdata.data[i] = pspe[i];
		break;

	case START_READ_SP_BLOCK :
		sc_out_cmd.enable	= true;
		sc_out_cmd.cmd = READ;
		sc_out_cmd.start_address = sp_address_s;
		sc_out_cmd.plen	= sp_cache_param.block_size * 4;
		break;

	case START_WRITE_SP_BLOCK :
		//cache disabled : write input block to memory
		sc_out_cmd.enable	= true;
		sc_out_cmd.cmd = WRITE;
		sc_out_cmd.start_address = sp_address_s;
		sc_out_cmd.plen	= sp_cache_param.block_size * 4;
    for (i = 0; i < SP_SIZE; i++)
		  sc_out_wdata.data[i] = sp[i];
		break;

	case START_WRITE_BACK_SP_BLOCK : 
		//cache enabled : write victim block to memory
		sc_out_cmd.enable	= true;
		sc_out_cmd.cmd = WRITE;
		sc_out_cmd.start_address = sp_victim_block->getBaseAddress();
		sc_out_cmd.plen	= sp_cache_param.block_size * 4;
    for (i = 0; i < SP_SIZE; i++)
		  sc_out_wdata.data[i] = sp_victim_block->getData()[i];
		break;

	case START_WRITE_THROUGH_SP_BLOCK :
		//cache enabled : write input block to memory
		sc_out_cmd.enable	= true;
		sc_out_cmd.cmd = WRITE;
		sc_out_cmd.start_address = sp_address_s;
		sc_out_cmd.plen	= sp_cache_param.block_size * 4;
    for (i = 0; i < SP_SIZE; i++)
		  sc_out_wdata.data[i] = sp[i];
		break;

	default :
		break;
	}
}
void SecurityContextController::ps()
{
		
	_secbus_debug(*doutput, TRACE, dec<<"SECURITY CONTEXT CONTROLLER STATE: "<<state<<"\n");


	if (!reset) 
	{
    en_in = false;
	  address_in = 0xFFFFFFFF;
    level_s = FIRST;
    s_scontext.spspe_addr = 0xFFFFFFFF;
    m_scontext.page_addr = 0xFFFFFFFF;
    for (i = 0; i < PSPE_SIZE; i++)
    {
      pspe[i] = 0;
      m_scontext.mpspe[i] = 0;
      s_scontext.spspe[i] = 0;
    }
    for (i = 0; i < SP_SIZE; i++)
    {
      sp[i] = 0;
      m_scontext.sp[i] = 0;
    }
    
  }

	switch (state) 
	{
	case IDLE ://0
		if (reg_in_cmd.enable)
    {
      en_in = true;
			address_in = reg_in_cmd.address;
			cmd_in = reg_in_cmd.cmd;
      for (i = 0; i < PSPE_SIZE; i++)
        pspe[i] = reg_in_data.data[i];
      for (i = 0; i < SP_SIZE; i++)
        sp[i] = reg_in_data.data[i];
      if (((pspe[0]&0x00000008)>>3)==0)
      {
        pspe_address_s = getFirstLevelAddress(PSPE_BASE_ADDRESS, address_in);
        if (pspe_cache_param.enable)
          hit_s = pspe_cache->lookup(getFirstLevelAddress(PSPE_BASE_ADDRESS, address_in), pspe_victim_block);
      }
      else
      {
        pspe_address_s = getSecondLevelAddress(PSPE_BASE_ADDRESS, address_in);
        if (pspe_cache_param.enable)
          hit_s = pspe_cache->lookup(getSecondLevelAddress(PSPE_BASE_ADDRESS, address_in), pspe_victim_block);
      }
      if (((pspe[0]&0x00000004)>>2)==0) // slave pspe
      {
        if (((pspe[0]&0x00000008)>>3)==0 && ((pspe[0]&0x00000002)>>1==0)) // first level & 4M page size
        {
          pspe[0] = pspe[0]&0x00000007;
          pspe[1]=0;
        }

        if (((pspe[0]&0x00000008)>>3)==1)
        {
          pspe[0] = pspe[0]&0x0000007;
          pspe[1]=0;
        }
      }
      else
      {
        pspe[0] = (pspe[0]&0x00000007)|((pspe[0]&0xfffffff0)>>1);
      }
    }
		if (htm_in_cmd.enable)
    {
      en_in = true;
			address_in = htm_in_cmd.address;
			if (pspe_cache_param.enable)
        hit_s = pspe_cache->lookup(htm_in_cmd.address, pspe_victim_block);
      pspe_address_s = htm_in_cmd.address;
      cmd_in = htm_in_cmd.cmd;
      for (i = 0; i < PSPE_SIZE; i++)
        pspe[i] = htm_in_data.data[i];
    }
		if (en_in)
		{
      en_in = false;
      level_s = FIRST;

			if (cmd_in == READ_SC)
			{
        m_cpt_r = m;

				addr = getFirstLevelAddress(PSPE_BASE_ADDRESS, address_in);
        pspe_address_s = addr;

				//cache statistics
				pspe_read_accesses++;

				if (pspe_cache_param.enable)
				{
					hit_s = pspe_cache->lookup(addr, pspe_victim_block);
				  state = TEST_PSPE_HIT_SIGNAL;
				}
				else
					state = START_READ_PSPE_BLOCK;
			}
			else if (cmd_in == WRITE_SP)
      {
        sp_write_accesses++;
        sp_address_s = getSPAddress((sp[0]&0x003FFFF0)>>4);
				if (sp_cache_param.enable)
				{
					//cache statistics
					hit_s = sp_cache->lookup(getSPAddress((sp[0]&0x003FFFF0)>>4), sp_victim_block);
      
					if (sp_cache_param.write_policy == WRITE_THROUGH)
						//Write in Ram
						state =  START_WRITE_THROUGH_SP_BLOCK;
					else
						//WRITE-BACK
					  state = TEST_SP_HIT_SIGNAL;
				}
				else
					state = START_WRITE_SP_BLOCK;
        
        sp[0] = sp[0] | (rand() & 0xFFFFFFF0);
        sp[1] = rand();
        sp[2] = rand();
        sp[3] = rand();
      }
			else if (cmd_in == WRITE_PSPE)
      {
				pspe_write_accesses++;
        //update 
				if (pspe_cache_param.enable)
				{
					//cache statistics
      
					if (pspe_cache_param.write_policy == WRITE_THROUGH)
						//Write in Ram
						state =  START_WRITE_THROUGH_PSPE_BLOCK;
					else
						//WRITE-BACK
					  state = TEST_PSPE_HIT_SIGNAL;
				}
				else
					state = START_WRITE_PSPE_BLOCK;
		  }
		}
		break;

	case TEST_PSPE_HIT_SIGNAL :
		if (hit_s)
    {
			memcpy(pspe, pspe_cache->readCache(pspe_address_s), pspe_cache_param.block_size*sizeof(int));
			
      _secbus_debug(*soutput, STAT, dec<<"@"<<m_cpt_rpspe<<" SEC_RPSPE "<<m - m_cpt_rpspe<<" cycles\n");
      
      if (cmd_in == READ)
      {
				if (!getValidBit(pspe))
					//Valid Bit false
				{
					_secbus_debug(cout, NONE, "SEC: PSPE VALIDITY BIT OF LEVEL "<<level_s + 1<<" IS 0\n");
					state	= ERROR;
				}
				else
					//Valid Bit true
				{
					if (getSizeBit(pspe) == SIZE_4M)
						//4M-octets Memory Page
					{
						if (getTypeBit(pspe) == SLAVE_PSPE)
						{
							state = IDLE;

              s_scontext.spspe_addr = pspe_address_s;

              for (i = 0; i < PSPE_SIZE; i++)
                s_scontext.spspe[i] = pspe[i];
            }
            else 
            {
              m_cpt_rsp = m;

              for (i = 0; i < PSPE_SIZE; i++)
                m_scontext.mpspe[i] = pspe[i];

							 sp_address_s	= getSPAddress(getSPindex(pspe));

							 if (sp_cache_param.enable)
							 {
							   //cache statistics
							   sp_read_accesses++;
             
								 hit_s = sp_cache->lookup(getSPAddress(getSPindex(pspe)), sp_victim_block);
             
								 state = TEST_SP_HIT_SIGNAL;
							 }
							 else
						    state = START_READ_SP_BLOCK;
            }
					  if (level_s == SECOND) 
					  {
						  _secbus_debug(cout, NONE, "SEC: 2ND LEVEL SIZE BIT IS 4M\n");
						  state	= ERROR;
					  }
          }
          else
						//4K-octets Memory Page
					{
            if (level_s == SECOND)
            {
							if (getTypeBit(pspe) == SLAVE_PSPE)
							{
								state = IDLE;

                s_scontext.spspe_addr = pspe_address_s;
              
                for (i = 0; i < PSPE_SIZE; i++)
                  s_scontext.spspe[i] = pspe[i];
							}
							else
								//get the Security policy
							{
                m_cpt_rsp = m;

                for (i = 0; i < PSPE_SIZE; i++)
                  m_scontext.mpspe[i] = pspe[i];

								sp_address_s	= getSPAddress(getSPindex(pspe));

								if (sp_cache_param.enable)
								{
									//cache statistics
								  sp_read_accesses++;
            
									hit_s = sp_cache->lookup(getSPAddress(getSPindex(pspe)), sp_victim_block);
            
									state = TEST_SP_HIT_SIGNAL;
								}
								else
									state = START_READ_SP_BLOCK;
						  }
            }
            else
            {
 
              addr = getSecondLevelAddress(getBaseAddress(pspe), address_in);
              pspe_address_s = addr;
  
              level_s = SECOND;
 
       				if (pspe_cache_param.enable)
			    		{
					    	//cache statistics
						    pspe_read_accesses++;
          
						    hit_s = pspe_cache->lookup(addr, pspe_victim_block);
  
					      state = TEST_PSPE_HIT_SIGNAL;
					    }
					    else
						    state = START_READ_PSPE_BLOCK;
					  }
          }
				}
      }
      else
			{	
				pspe_cache->writeCache(pspe_address_s, pspe, pspe_cache_param.block_size);
				state = IDLE;
			}
    }
		else
		{
      if (cmd_in == READ_SC)
      {
				//cache statistics
				pspe_read_misses++;
    
				if (pspe_cache_param.write_policy == WRITE_THROUGH)
					//No need to check dirty bits
					state = START_READ_PSPE_BLOCK;
				else
					//Write back
				{
					if (!pspe_victim_block->isDirty())
						//block clean
						state = START_READ_PSPE_BLOCK;
					else
					{
						//cache statistics
						pspe_write_back++;
						state = START_WRITE_BACK_PSPE_BLOCK;
					}
				}
      }
      else
			{
				//cache statistics
				pspe_write_misses++;

				if (!pspe_victim_block->isDirty())
					//clean
				{
					pspe_cache->replace(pspe_address_s, pspe, true);
					state = IDLE;
				}
				else
				{
					//cache statistics
					pspe_write_back++;
					state = START_WRITE_BACK_PSPE_BLOCK;
				}
			}		
		}
    break;

	case START_READ_PSPE_BLOCK :
		state = WAIT_READ_PSPE_BLOCK;
		break;

	case START_WRITE_BACK_PSPE_BLOCK : 
		state = WAIT_WRITE_BACK_PSPE_BLOCK;
		break;

	case WAIT_WRITE_BACK_PSPE_BLOCK:
		if (sc_in_ack.wack)
    {
			//Ram is updated
			if (cmd_in == READ_SC)
			//Read missing block
			  state = START_READ_PSPE_BLOCK;
      else
      {
			  pspe_cache->replace(pspe_address_s, pspe, true);
			  state = IDLE;
      }
    }
		break;
	
  case START_WRITE_PSPE_BLOCK :
		state = WAIT_WRITE_PSPE_BLOCK;
		break;

	case WAIT_WRITE_PSPE_BLOCK :
		if (sc_in_ack.wack)
			state = IDLE;
		break;

	case START_WRITE_THROUGH_PSPE_BLOCK :
		//Write or replace cache
		if (hit_s)
    {
    	pspe_cache->writeCache(pspe_address_s, pspe, pspe_cache_param.block_size);
    }
		else 
		{
			pspe_write_misses++;
			pspe_cache->replace(pspe_address_s, pspe, true);
		}
		state = WAIT_WRITE_THROUGH_PSPE_BLOCK;
		break;


	case WAIT_WRITE_THROUGH_PSPE_BLOCK :
		if (sc_in_ack.wack)
    {
			state = IDLE;
    }
		break;

	case WAIT_READ_PSPE_BLOCK :
		if (sc_in_ack.rack)
		{
      _secbus_debug(*soutput, STAT, dec<<"@"<<m_cpt_rpspe<<" SEC_RPSPE "<<m - m_cpt_rpspe<<" cycles\n");

			pspe[0] = sc_in_rdata.data[0];
			pspe[1] = sc_in_rdata.data[1];

 
		  //Write into cache
			if (pspe_cache_param.enable)
				pspe_cache->replace(pspe_address_s, pspe, false);
      
			if (!getValidBit(pspe))
				//Valid Bit false
			{
				_secbus_debug(cout, NONE, "SEC: PSPE VALIDITY BIT OF LEVEL "<<level_s + 1<<" IS 0\n");
				state	= ERROR;
			}
			else
				//Valid Bit true
			{
				if (getSizeBit(pspe) == SIZE_4M)
					//4M-octets Memory Page
				{
					 if (getTypeBit(pspe) == SLAVE_PSPE)
					 {
					 	state = IDLE;
 
            s_scontext.spspe_addr = pspe_address_s;

            for (i = 0; i < PSPE_SIZE; i++)
              s_scontext.spspe[i] = pspe[i];

           }
           else 
           {
             m_cpt_rsp = m;
            
             for (i = 0; i < PSPE_SIZE; i++)
               m_scontext.mpspe[i] = pspe[i];

             addr = getSPAddress(getSPindex(pspe));
					 	 sp_address_s	= addr;

					 	 if (sp_cache_param.enable)
					 	 {
					 	   //cache statistics
					 	   sp_read_accesses++;
            
					 		 hit_s = sp_cache->lookup(addr, sp_victim_block);
            
					 		 state = TEST_SP_HIT_SIGNAL;
					 	 }
					 	 else
					     state = START_READ_SP_BLOCK;
           }
				   if (level_s == SECOND) 
				   {
					   _secbus_debug(cout, NONE, "SEC: 2ND LEVEL SIZE BIT IS 4M\n");
					   state	= ERROR;
				   }
        }
        else
					//4K-octets Memory Page
				{
          if (level_s == SECOND)
          {
						if (getTypeBit(pspe) == SLAVE_PSPE)
						{
							 state = IDLE;

              s_scontext.spspe_addr = pspe_address_s;
            
              for (i = 0; i < PSPE_SIZE; i++)
                s_scontext.spspe[i] = pspe[i];
						}
						else
							//get the Security policy
						{
              m_cpt_rsp = m;
            
              for (i = 0; i < PSPE_SIZE; i++)
                m_scontext.mpspe[i] = pspe[i];

							addr	= getSPAddress(getSPindex(pspe));
							sp_address_s	= addr;

							if (sp_cache_param.enable)
							{
								//cache statistics
							  sp_read_accesses++;
          
								hit_s = sp_cache->lookup(addr, sp_victim_block);
          
								state = TEST_SP_HIT_SIGNAL;
							}
							else
								state = START_READ_SP_BLOCK;
					  }
          }
          else
          {
            addr = getSecondLevelAddress(getBaseAddress(pspe), address_in);
            pspe_address_s = addr;
              
            level_s = SECOND;

     				if (pspe_cache_param.enable)
		    		{
				    	//cache statistics
					    pspe_read_accesses++;
 
					    hit_s = pspe_cache->lookup(addr, pspe_victim_block);

				      state = TEST_PSPE_HIT_SIGNAL;
				    }
				    else
					    state = START_READ_PSPE_BLOCK;
				  }
        }
			}
    }
		break;

	case TEST_SP_HIT_SIGNAL :
		if (hit_s)
		{
      if (cmd_in == READ_SC)
      {	
        //Store SP
   	 		memcpy(sp, sp_cache->readCache(sp_address_s), sp_cache_param.block_size*sizeof(int));
 
 	  		_secbus_debug(*soutput, STAT, dec<<"@"<<m_cpt_rsp<<" SEC_RSP "<<m - m_cpt_rsp<<" cycles\n");
 
        for (i = 0; i < SP_SIZE; i++)
          m_scontext.sp[i] = sp[i];

 	 	  	if (getIM(sp) == HASH_TREE)
 	 			//Page protected by HASH-TREE, we must get the SLAVE pspe
 	   		{
 	 		  	addr = getBaseAddress(pspe);
 	 		  	address_in = addr;
   	 		  addr = getFirstLevelAddress(PSPE_BASE_ADDRESS, addr);
   	 		  pspe_address_s = addr;
          level_s = FIRST;

  				//cache statistics
  				pspe_read_accesses++;

  				if (pspe_cache_param.enable)
  				{
  					hit_s = pspe_cache->lookup(addr, pspe_victim_block);
  				  state = TEST_PSPE_HIT_SIGNAL;
  				}
  				else
  					state = START_READ_PSPE_BLOCK;
 	 	  	}
 	 	  	else
 	 		  	//End of Security context search
			  	state = IDLE;
       }
       else
       {
			   sp_cache->writeCache(sp_address_s, sp, sp_cache_param.block_size);
			   state = IDLE;
       }
		}
		else
		{
      if (cmd_in == READ_SC)
      {	
				//cache statistics
			  sp_read_misses++;
  
				if (sp_cache_param.write_policy == WRITE_THROUGH)
					//No need to check dirty bit before reading memory
						state = START_READ_SP_BLOCK;
				else
					//Write back
				{
					if (!sp_victim_block->isDirty())
						//clean
						state = START_READ_SP_BLOCK;
					else
					{
						//cache statistics
						sp_write_back++;
						state = START_WRITE_BACK_SP_BLOCK;
					}
				}
      }
      else
      {
				//cache statistics
				sp_write_misses++;
      
				if (!sp_victim_block->isDirty())
					//clean
				{
					sp_cache->replace(sp_address_s, sp, true);
					state = IDLE;
				}
				else
				{
					//cache statistics
					sp_write_back++;
					state = START_WRITE_BACK_SP_BLOCK;
				}
      }  
		}
		break;

  case START_WRITE_SP_BLOCK :
		state = WAIT_WRITE_SP_BLOCK;
		break;

	case START_WRITE_THROUGH_SP_BLOCK :
		//Write or replace cache
		if (hit_s)
    	sp_cache->writeCache(sp_address_s, sp, sp_cache_param.block_size);
		else 
		{
			sp_write_misses++;
			sp_cache->replace(sp_address_s, sp, true);
		}
		state = WAIT_WRITE_THROUGH_SP_BLOCK;
		break;
 
	case START_READ_SP_BLOCK ://2
		state = WAIT_READ_SP_BLOCK;
		break;

	case START_WRITE_BACK_SP_BLOCK : //3
		state = WAIT_WRITE_BACK_SP_BLOCK;
		break;

	case WAIT_WRITE_BACK_SP_BLOCK://4
		if (sc_in_ack.wack)
		{
      if (cmd_in == READ_SC)
			  //Now read missing block
		  	state = START_READ_SP_BLOCK;
      else
      { 
			  sp_cache->replace(sp_address_s, sp, true);
			  state = IDLE; 
      }
		}
		break;

	case WAIT_WRITE_SP_BLOCK :
		if (sc_in_ack.wack)
			state = IDLE;
		break;

	case WAIT_WRITE_THROUGH_SP_BLOCK :
		if (sc_in_ack.wack)
    {
			state = IDLE;
    }
		break;

	case WAIT_READ_SP_BLOCK ://5
		if (sc_in_ack.rack)
		{
      //Store SP
			for(i=0;i < SP_SIZE; i++)
      {
				m_scontext.sp[i] = sc_in_rdata.data[i];
				sp[i] = sc_in_rdata.data[i];
      }
		
    	//Write into cache
			if (sp_cache_param.enable)
				sp_cache->replace(address_in, sp, false);

			_secbus_debug(*soutput, STAT, dec<<"@"<<m_cpt_rsp<<" SEC_RSP "<<m - m_cpt_rsp<<" cycles\n");

			if (getIM(sp) == HASH_TREE)
				//Page protected by HASH-TREE, we must get the SLAVE pspe
			{
				addr = getBaseAddress(pspe);
				address_in = addr;
   	 		addr = getFirstLevelAddress(PSPE_BASE_ADDRESS, addr);
   	 		pspe_address_s = addr;
        level_s = FIRST;

				state			= START_READ_PSPE_BLOCK;	
			}
			else
				//End of Security context search
				state = IDLE;
		}
		break;

	case ERROR ://2
		_secbus_debug(cout, NONE, "pspe address:	"<<hex<<pspe_address_s<<"\n");
		_secbus_debug(cout, NONE, "pspe[0]:	"<<hex<<pspe[0]<<"\n");
		_secbus_debug(cout, NONE, "pspe[1]:	"<<hex<<pspe[1]<<"\n");
		//stop simulation
		sc_stop();
		break;

	default :
		break;
	}
}
