/*======================================================
FILE:  armmutils.c

SERVICES:  MMU Utilities for 6100 including the generic bridle_checkaccess.

GENERAL DESCRIPTION:
   Implements a generic routine for bridle_checkaccess.This generic routine
   will work in any 6100 configuration of sections, page tables, etc.
   

PUBLIC CLASSES AND STATIC FUNCTIONS:
   

INITIALIZATION & SEQUENCING REQUIREMENTS:

   
        Copyright © 1999-2003 QUALCOMM Incorporated.
               All Rights Reserved.
            QUALCOMM Proprietary/GTDR
=====================================================*/
#include "AEEBridle.h"
#include "msm/armmutils.h"
#include "customer.h"
#include "comdef.h"
#include "armmmu.h"
#include "boothw.h"
extern void bridle_access_abort(uint32, uint32, uint32, uint32);
extern unsigned long asmbridle_mmu_get_page_table_base(void);
extern long asmbridle_get_dac_register(void);
extern void asmbridle_switch_to_user(void);
extern void asmbridle_set_dac_register(uint32);
extern uint32 asmbridle_mmu_get_controlregister(void);
extern void asmbridle_mmu_set_controlregister(uint32);



#define TEST_CHECKACCESS
#undef TEST_CHECKACCESS

#define FAST_ADDRESS_TRANSLATION
#undef FAST_ADDRESS_TRANSLATION

int GetPermission(int procmode, int domaintype, int APSR, int aType)
{
   int retval = EFAILED;
   int AP=0, SR =0;

   int usermode = APERM_UNPREDICTABLE, svcmode = APERM_UNPREDICTABLE;
   int *pmodeperm=NULL;

   switch(domaintype)
   {
      case DOMAIN_NOACCESS:
         svcmode = APERM_NONE;
         usermode = APERM_NONE;
         break;
      
      case DOMAIN_CLIENT:
         AP = (APSR>>2)&0x03;
         SR = APSR &0x03; 
         switch(AP)
         { 
       
            case B_0_0:
               switch(SR)
               {
                  case B_0_0:
                     svcmode = APERM_NONE;
                     usermode = APERM_NONE;
                     break;
                  case B_1_0:
                     svcmode = APERM_READONLY;
                     usermode = APERM_NONE;
                     break;
                  case B_0_1:
                     svcmode = APERM_READONLY;
                     usermode = APERM_READONLY;
                     break;
                  case B_1_1:
                     svcmode = APERM_UNPREDICTABLE;
                     usermode = APERM_UNPREDICTABLE;
                     break;
               }
               break;
         
            case B_0_1:
               svcmode = APERM_READWRITE;
               usermode = APERM_NONE;
               break;
            case B_1_0:
               svcmode = APERM_READWRITE;
               usermode = APERM_READONLY;
               break;
            case B_1_1:
               svcmode = APERM_READWRITE;
               usermode = APERM_READWRITE;
               break;         
         }
         break;
             
      case DOMAIN_RESERVED:
         svcmode = APERM_NONE;
         usermode = APERM_NONE;
         break;
      
      case DOMAIN_MANAGER:
         svcmode = APERM_READWRITE;
         usermode = APERM_READWRITE;
         break;
   }

   if(procmode == BRIDLE_USR_MODE)
      pmodeperm = &usermode;
   else
      pmodeperm = &svcmode;

   if((aType == BRIDLE_READ_ACCESS)||(aType == BRIDLE_READ_STRING_ACCESS))
	{
		if((*pmodeperm == APERM_READONLY)||(*pmodeperm == APERM_READWRITE))
			retval = SUCCESS;
		else
			retval = EFAILED;
	}
   else if (aType == BRIDLE_WRITE_ACCESS)
   {
		if(*pmodeperm == APERM_READWRITE)
			retval = SUCCESS;
		else
			retval = EFAILED;
   }
   else if(aType == BRIDLE_NO_ACCESS)
   {
      if(*pmodeperm == APERM_NONE)
         retval = SUCCESS;
      else
         retval = EFAILED;
   }
   else
   {
      retval = EFAILED;
   }

   return retval;
}

void SetSRBits(int S, int R)
{
	uint32 creg = asmbridle_mmu_get_controlregister();
	creg &= 0xFFFFFCFF;
	if(S !=0 )
	 creg |= 0x100;
	if(R !=  0)
	 creg |= 0x200;
	asmbridle_mmu_set_controlregister(creg);
}

int GetSRBits()
{
	uint32 creg = asmbridle_mmu_get_controlregister();
	creg = creg>>8;
	creg = creg&0x03;
	creg = 3 - creg;
	return creg;
}

unsigned long GetStringLength(char * str, unsigned long max)
{
//this code also counts the null terminator of the string
//it will not crash because it ill always be called in svc  mode
	unsigned long strlength = 0;
	if(str[0])//zero length str
		{
		while((str[strlength] !=NULL)&&(strlength++ <max));
		}
	return strlength;
}


int IsBridleImplemented(void)
{
   uint32 dacreg = asmbridle_get_dac_register();
   if (dacreg == 0xFFFFFFFF)
   		return 0;
   else
        return 1;
}


/*===========================================================================

FUNCTION   

DESCRIPTION
  
DEPENDENCIES

RETURN VALUE

SIDE EFFECTS

===========================================================================*/
unsigned long GetFirstLevelDescriptor(unsigned long virtualAddress)
{
   unsigned long tableindex = (virtualAddress>>20) & 0xfff;
   unsigned long *descaddress = 0;
   unsigned long pagetablebase =0;
   pagetablebase = asmbridle_mmu_get_page_table_base();
   descaddress = (unsigned long*)(pagetablebase + (tableindex<<2));
   return *descaddress;
}

//zerobits : 20 for section
//           16 for large
//           12 for small page
//           10 for tiny page          
/*===========================================================================

FUNCTION   

DESCRIPTION
  
DEPENDENCIES

RETURN VALUE

SIDE EFFECTS

===========================================================================*/
unsigned long GetLengthChecked(unsigned long virtualaddress, int nzerobits)
{
   unsigned long temp = virtualaddress;
   temp = temp>>nzerobits;
   temp = temp<<nzerobits;
    switch(nzerobits)
    {
     case 20:
        temp += 0x100000;break;
     case 16:
         temp += 0x10000;break;
     case 12:
          temp += 0x1000;break;
     case 10:
          temp += 0x400;break;
      default:
      ;
          //invalid    
    }
    return (temp-virtualaddress);
}


/*===========================================================================

FUNCTION   

DESCRIPTION
  
DEPENDENCIES

RETURN VALUE

SIDE EFFECTS

===========================================================================*/
unsigned long TranslateSection(unsigned long firstdesc, unsigned long virtualAddress, int *pAP, int *pDomain)
{
   unsigned long temp = firstdesc;
   unsigned long lengthchecked =  0;
   *pDomain = (temp>>5) & 0x0f;
   *pAP = (firstdesc>>10) &0x03;
   lengthchecked = GetLengthChecked(virtualAddress, 20);
   return lengthchecked;
}

/*===========================================================================

FUNCTION   

DESCRIPTION
  
DEPENDENCIES

RETURN VALUE

SIDE EFFECTS

===========================================================================*/
unsigned long TranslateCoarsePageTable(unsigned long firstdesc, unsigned long virtualAddress, int*pAP, int *pDomain)
{
unsigned long virtualsave = virtualAddress;
unsigned long* seconddescaddress =(unsigned long*) (
										(firstdesc & 0xfffffc00) + ((virtualAddress >> 10) & 0x000003fc)
										           );
unsigned long seconddesc = *seconddescaddress;
unsigned long temp  = firstdesc;
int AP0, AP1, AP2, AP3; 
   unsigned long lengthchecked =  0;

*pDomain = (temp>>5) & 0x0f;
   temp = seconddesc;
   AP0 = (temp>>4) &0x03;
   temp = seconddesc;
   AP1 = (temp>>6) &0x03;
   temp = seconddesc;
   AP2 = (temp>>8) &0x03;
   temp = seconddesc;
   AP3 = (temp>>10) &0x03;
   *pAP = AP0;//assume all r same

switch(seconddesc & 0x00000003)
   {
case 0://FAULT
   break;
case 1://large page
    
    lengthchecked = GetLengthChecked(virtualsave, 16);
    break;
case 2://small page
    lengthchecked = GetLengthChecked(virtualsave, 12);
   break;
case 3://fault
   break;
   }
return lengthchecked;
}

/*===========================================================================

FUNCTION   

DESCRIPTION
  
DEPENDENCIES

RETURN VALUE

SIDE EFFECTS

===========================================================================*/
unsigned long TranslateFinePageTable(unsigned long firstdesc, unsigned long virtualAddress,int*AP, int *dom )
{
unsigned long temp = firstdesc;
unsigned long lengthchecked =  0;
unsigned long virtualsave = virtualAddress;

unsigned long* seconddescaddress = (unsigned long*) 
                                       (
                                       (firstdesc & 0xfffff000) +  
                                       ((virtualAddress >> 8) & 0x000003fc)
                                       );
unsigned long seconddesc = *seconddescaddress;
int AP0, AP1, AP2, AP3; 
*dom = (temp>>5) & 0x0f;
temp = seconddesc;
AP0 = (temp>>4) &0x03;
temp = seconddesc;
AP1 = (temp>>6) &0x03;
temp = seconddesc;
AP2 = (temp>>8) &0x03;
temp = seconddesc;
AP3 = (temp>>10) &0x03;
*AP = AP0;

switch(seconddesc & 0x00000003)
   {
case 0://FAULT
   break;
case 1://large page
    lengthchecked = GetLengthChecked(virtualsave, 16);
    break;
case 2://small page
    lengthchecked = GetLengthChecked(virtualsave, 12);
   break;
case 3://tiny page
   lengthchecked = GetLengthChecked(virtualsave, 10);
   break;
   }
   
return lengthchecked;

}

/*===========================================================================

FUNCTION   

DESCRIPTION
  
DEPENDENCIES

RETURN VALUE

SIDE EFFECTS

===========================================================================*/
unsigned long FastTranslateVirtualAddress( unsigned long virtualAddress, int *AP, int *Domain)
{
	unsigned long lengthchecked =0;
	unsigned long offset = 0x100000;
	#ifdef SHADOW_MODE
	offset =0;
	#endif

	if(virtualAddress <= 0x5FFFFF)
	{
		*AP = 0x01;
		*Domain = 0;
        lengthchecked = GetLengthChecked(virtualAddress, 20);
	}
	else if((virtualAddress >= 0x600000+offset) && (virtualAddress <= 0x6fffff+offset))
	{
		*AP = 0x02;
		*Domain = 1;
		lengthchecked = GetLengthChecked(virtualAddress, 20);
	}
	else if((virtualAddress >= 0x700000+offset) && (virtualAddress <= 0x71FFFF+offset))
	{
		*AP = 0x03;
		*Domain = 0;
		lengthchecked = GetLengthChecked(virtualAddress, 16);
	}
	else if((virtualAddress >= 0x720000+offset) && (virtualAddress <= 0x78FFFF+offset))
	{
		*AP = 0x02;
		*Domain = 0;
		lengthchecked = GetLengthChecked(virtualAddress, 16);
	}
	else if((virtualAddress >= 0x790000+offset) && (virtualAddress <= 0x7FFFFF+offset))
	{
		*AP = 0x01;
		*Domain = 0;
		lengthchecked = GetLengthChecked(virtualAddress, 16);
	}
#ifdef SHADOW_MODE
	else if((virtualAddress >= 0x18000000) && (virtualAddress <= 0x181FFFFF))
	{
		*AP = 0x03;
		*Domain = 0x02;
		lengthchecked = GetLengthChecked(virtualAddress, 20);
	}
#else
	else if((virtualAddress >= 0x800000+offset) && (virtualAddress <= 0x9FFFFF+offset))
	{
		*AP = 0x03;
		*Domain = 0x02;
		lengthchecked = GetLengthChecked(virtualAddress, 20);
	}
#endif
	else 
	{
		*AP = 0x01;
		*Domain = 0;
		lengthchecked = GetLengthChecked(virtualAddress, 20);
	}
   return lengthchecked;
}



//slow = no macros right now
//and can be highly optimized by other means
/*===========================================================================

FUNCTION   

DESCRIPTION
  
DEPENDENCIES

RETURN VALUE

SIDE EFFECTS

===========================================================================*/
unsigned long SlowTranslateVirtualAddress( unsigned long virtualAddress, int *AP, int *Domain)
{
     unsigned long firstdesc =  GetFirstLevelDescriptor(virtualAddress);
     unsigned long retval =0;
     switch(firstdesc & 0x00000003)
     {
         case 0://FAULT
            break;
         case 1:
            retval = TranslateCoarsePageTable(firstdesc, virtualAddress, AP, Domain);
            break;
         case 2:
            retval = TranslateSection(firstdesc, virtualAddress, AP, Domain);
            break;
         case 3:
            retval = TranslateFinePageTable(firstdesc, virtualAddress, AP, Domain);
            break;
    }
     return retval;
}

/*===========================================================================

FUNCTION   

DESCRIPTION
  
DEPENDENCIES

RETURN VALUE

SIDE EFFECTS

===========================================================================*/
int GetDomainType(unsigned long dacreg, int domnum)
{
 return ((dacreg>>(domnum<<1))&0x03);
}


/*===========================================================================

FUNCTION   

DESCRIPTION
  
DEPENDENCIES

RETURN VALUE

SIDE EFFECTS

===========================================================================*/
int bridle_CheckGenericAccess(void *pBuf, unsigned long len, int aType, uint32 swinum)
{


   int retval = SUCCESS;
   int AP=0, Domain=0;
   unsigned long lengthchecked = 0, totallengthchecked = 0;
   unsigned long dacreg = asmbridle_get_dac_register();
   unsigned long domaintype = 0;
   int processormode = BRIDLE_GET_REQ_MODE(swinum);
   unsigned long xlen =0;
   int SR = GetSRBits();
   int APSR =0;

   
  while((totallengthchecked < len)||(aType == BRIDLE_READ_STRING_ACCESS))
   {
 #ifdef FAST_ADDRESS_TRANSLATION
      lengthchecked = FastTranslateVirtualAddress( (unsigned long )pBuf + totallengthchecked, &AP, &Domain);
 #else
      lengthchecked = SlowTranslateVirtualAddress( (unsigned long )pBuf + totallengthchecked, &AP, &Domain);
 #endif   
         if(aType == BRIDLE_READ_STRING_ACCESS)
         	{
         	xlen = GetStringLength((char*)pBuf + totallengthchecked, lengthchecked);
         	if(xlen < lengthchecked)
         		{
         		aType = BRIDLE_READ_ACCESS;
         		}
            len += xlen;
            }
          

         APSR = SR + (AP<<2);
         totallengthchecked += lengthchecked;
         domaintype = GetDomainType(dacreg, Domain); 
         retval = GetPermission(processormode, domaintype, APSR, aType);
         if(retval == EFAILED)
         	break;
         else
         	continue;         
    }
    
   return retval;
}



/*===========================================================================

FUNCTION   

DESCRIPTION
  
DEPENDENCIES

RETURN VALUE

SIDE EFFECTS

===========================================================================*/
void ASSERT_RETVAL(int retval, int expected , int testcase)
{
   if(retval != expected) {
      bridle_access_abort(retval, expected, testcase,0);
	}
}
/*
user accesses:
SVC_RW_USR_NA, 0, 0x5fffff
SVC_RW_USR_RO, 0x600000, 0x6fffff
SVC_RW_USR_RW, 0x700000, 0x71ffff
SVC_RW_USR_RO, 0x720000, 0x78ffff
SVC_RW_USR_NA, 0x790000, 0x7fffff
SVC_RW_USR_RW, 0x18000000, 0x181fffff
*/


/*===========================================================================

FUNCTION   

DESCRIPTION
  
DEPENDENCIES

RETURN VALUE

SIDE EFFECTS

===========================================================================*/
void TestCheckAccess()
{
#ifdef TEST_CHECKACCESS

   unsigned long *pAddr = 0;
   int retval =0;

   //length 0 = must always return true no matter what
   pAddr = 0;
   retval = bridle_CheckAccess(pAddr, 0, BRIDLE_WRITE_ACCESS, 0);
   ASSERT_RETVAL(retval, SUCCESS, 1);

   pAddr = 0;
   retval = bridle_CheckAccess(pAddr, 0, BRIDLE_READ_ACCESS, 0);
   ASSERT_RETVAL(retval, SUCCESS, 2);


   pAddr = (unsigned long*)0;
   retval = bridle_CheckAccess(pAddr, 0xFFFFF, BRIDLE_WRITE_ACCESS, 0);
   ASSERT_RETVAL(retval, EFAILED, 1);

   pAddr = (unsigned long*)0;
   retval = bridle_CheckAccess(pAddr, 0xFFFFF, BRIDLE_READ_ACCESS, 0);
   ASSERT_RETVAL(retval, EFAILED, 2);


   pAddr = (unsigned long*) 0x100000;
   retval = bridle_CheckAccess(pAddr, 0xFFFFF, BRIDLE_WRITE_ACCESS, 0);
   ASSERT_RETVAL(retval, EFAILED, 3);

   pAddr = (unsigned long*) 0x100000;
   retval = bridle_CheckAccess(pAddr, 0xFFFFF, BRIDLE_READ_ACCESS, 0);
   ASSERT_RETVAL(retval, EFAILED, 4);


   pAddr = (unsigned long*) 0x200000;
   retval = bridle_CheckAccess(pAddr, 0xFFFFF, BRIDLE_WRITE_ACCESS, 0);
   ASSERT_RETVAL(retval, EFAILED, 5);

   pAddr = (unsigned long*) 0x200000;
   retval = bridle_CheckAccess(pAddr, 0xFFFFF, BRIDLE_READ_ACCESS, 0);
   ASSERT_RETVAL(retval, EFAILED, 6);


   pAddr = (unsigned long*) 0x300000;
   retval = bridle_CheckAccess(pAddr, 0xFFFFF, BRIDLE_WRITE_ACCESS, 0);
   ASSERT_RETVAL(retval, EFAILED, 7);

   pAddr = (unsigned long*) 0x300000;
   retval = bridle_CheckAccess(pAddr, 0xFFFFF, BRIDLE_READ_ACCESS, 0);
   ASSERT_RETVAL(retval, EFAILED, 8);

   pAddr = (unsigned long*) 0x400000;
   retval = bridle_CheckAccess(pAddr, 0xFFFFF, BRIDLE_WRITE_ACCESS, 0);
   ASSERT_RETVAL(retval, EFAILED, 9);

   pAddr = (unsigned long*) 0x400000;
   retval = bridle_CheckAccess(pAddr, 0xFFFFF, BRIDLE_READ_ACCESS, 0);
   ASSERT_RETVAL(retval, EFAILED, 10);

   pAddr = (unsigned long*) 0x500000;
   retval = bridle_CheckAccess(pAddr, 0xFFFFF, BRIDLE_WRITE_ACCESS, 0);
   ASSERT_RETVAL(retval, EFAILED, 11);

   pAddr = (unsigned long*) 0x500000;
   retval = bridle_CheckAccess(pAddr, 0xFFFFF, BRIDLE_READ_ACCESS, 0);
   ASSERT_RETVAL(retval, EFAILED, 12);


   pAddr = (unsigned long*) 0x600000;
   retval = bridle_CheckAccess(pAddr, 0xFFFFF, BRIDLE_WRITE_ACCESS, 0);
   ASSERT_RETVAL(retval, EFAILED, 13);

   pAddr = (unsigned long*) 0x600000;
   retval = bridle_CheckAccess(pAddr, 0xFFFFF, BRIDLE_READ_ACCESS, 0);
   ASSERT_RETVAL(retval, SUCCESS, 14);


   pAddr = (unsigned long*) 0x700000;
   retval = bridle_CheckAccess(pAddr, 0x1FFFF, BRIDLE_WRITE_ACCESS, 0);
   ASSERT_RETVAL(retval, SUCCESS, 15);


   pAddr = (unsigned long*) 0x700000;
   retval = bridle_CheckAccess(pAddr, 0x1FFFF, BRIDLE_READ_ACCESS, 0);
   ASSERT_RETVAL(retval, SUCCESS, 11);


   pAddr = (unsigned long*) 0x720000;
   retval = bridle_CheckAccess(pAddr, 0x6FFFF, BRIDLE_WRITE_ACCESS, 0);
   ASSERT_RETVAL(retval, EFAILED, 12);


   pAddr = (unsigned long*) 0x720000;
   retval = bridle_CheckAccess(pAddr, 0x6FFFF, BRIDLE_READ_ACCESS, 0);
   ASSERT_RETVAL(retval, SUCCESS, 12);


   pAddr = (unsigned long*) 0x790000;
   retval = bridle_CheckAccess(pAddr, 0x6FFFF, BRIDLE_WRITE_ACCESS, 0);
   ASSERT_RETVAL(retval, EFAILED, 13);

   pAddr = (unsigned long*) 0x790000;
   retval = bridle_CheckAccess(pAddr, 0x6FFFF, BRIDLE_READ_ACCESS, 0);
   ASSERT_RETVAL(retval, EFAILED, 13);


#endif
}


