/*************************************************************************
*
*             Copyright Mentor Graphics Corporation 2013
*                         All Rights Reserved.
*
* THIS WORK CONTAINS TRADE SECRET AND PROPRIETARY INFORMATION WHICH IS
* THE PROPERTY OF MENTOR GRAPHICS CORPORATION OR ITS LICENSORS AND IS
* SUBJECT TO LICENSE TERMS.
*
**************************************************************************

**************************************************************************
*
* FILE NAME
*
*       arm_proc_tlb.c
*
* COMPONENT
*
*       PROC - Nucleus Processes
*
* DESCRIPTION
*
*       This file contains functions that create, populate, and modify
*       the translation tables for ARM cores.
*
* DATA STRUCTURES
*
*       PROC_AR_L1_Base
*       PROC_AR_L2_Use
*       PROC_AR_L2_Count
*       PROC_AR_Kern_Mem_Setup
*
* FUNCTIONS
*
*       PROC_AR_Mem_Mgmt_Initialize
*       PROC_AR_Schedule
*       PROC_AR_Mem_Mgmt_Cleanup
*       PROC_AR_MMU_Enable
*       PROC_AR_Attach_Memory
*       PROC_AR_Detach_Memory
*       PROC_AR_Set_Attributes
*       PROC_AR_Create_L1_Table
*       PROC_AR_Create_L2_Table
*       PROC_AR_Alloc_L2_Table
*
* DEPENDENCIES
*
*       nucleus.h
*       nu_kernel.h
*       proc_core.h
*       proc_mem_mgmt.h
*       arm_proc_tlb.h
*
*************************************************************************/
#include "nucleus.h"
#include "kernel/nu_kernel.h"
#include "os/kernel/process/core/proc_core.h"
#include "os/kernel/process/mem_mgmt/proc_mem_mgmt.h"
#include "arm_proc_tlb.h"

/* Local prototypes */
static VOID PROC_AR_Set_Attributes(PROC_MEMORY* region,
                                   PROC_AR_L1_DESC *level_1_desc,
                                   UINT32 current_addr);
static PROC_AR_L1_DESC* PROC_AR_Create_L1_Table(PROC_CB* process, UNSIGNED default_attr);
static PROC_AR_L2_DESC* PROC_AR_Create_L2_Table(UINT32 descriptor);
static PROC_AR_L2_DESC *PROC_AR_Alloc_L2_Table(VOID);

/* Translation table data */
static VOID *PROC_AR_L1_Base;
static CHAR  PROC_AR_L2_Use[PROC_AR_L2_TOTAL];
static INT   PROC_AR_L2_Count;

PROC_KERN_MEM PROC_AR_Kern_Mem_Setup[PROC_KERNEL_REGIONS] =
{
    /* Memory region containing code */
    {"text",                                                        /* Region name */
     (NU_MEM_EXEC | NU_SHARE_EXEC),                                 /* Memory attributes */
     TEXT_START,                                                    /* Start address */
     TEXT_END},                                                     /* End address */

    /* Memory region containing data and bss */
    {"data",                                                        /* Region name */
     (NU_MEM_READ | NU_MEM_WRITE | NU_SHARE_READ | PROC_NO_EXECUTE_LOADED),/* Memory attributes */
     DATA_START,                                                    /* Start address */
     BSS_END},                                                      /* End address */

    /* Memory region containing RTL code that must be read/write
       to anyone that uses the library */
    {"rtl",                                                         /* Region name */
     (NU_MEM_EXEC | NU_MEM_WRITE | NU_SHARE_EXEC | NU_SHARE_READ),  /* Memory attributes */
     RTL_START,                                                     /* Start address */
     RTL_END},                                                      /* End address */

    /* Memory region containing read only data */
    {"rodata",                                                      /* Region name */
     (NU_MEM_READ | NU_SHARE_READ | PROC_NO_EXECUTE_LOADED),        /* Memory attributes */
     RODATA_START,                                                  /* Start address */
     RODATA_END},                                                   /* End address */
};

/*************************************************************************
*
* FUNCTION
*
*       PROC_AR_Mem_Mgmt_Initialize
*
* DESCRIPTION
*
*       Setups all global data structures
*
* INPUTS
*
*       None
*
* OUTPUTS
*
*       NU_SUCCESS          Function completed successfully
*       NU_UNAVAILABLE      Not enough space for translation tables
*
*************************************************************************/
STATUS PROC_AR_Mem_Mgmt_Initialize(VOID)
{
    UINT32 tlb_section_size;
    STATUS status = NU_UNAVAILABLE;
    UINT32 translation_req;


    /* Find and set the level 1 table base address */
    PROC_AR_L1_Base = (VOID *)ESAL_GE_MEM_Next_Match_Find((VOID *)TEXT_START, ESAL_NOCACHE,
                                                          ESAL_TLB_MEM, ESAL_DATA);

    /* Verify the region was found */
    if (PROC_AR_L1_Base != NU_NULL)
    {
        /* Get the size of the section */
        tlb_section_size = ESAL_GE_MEM_Remaining_Size_Get(PROC_AR_L1_Base);

        /* Calculate the region size needed to run based on the current
           configuration */
        translation_req = (PROC_AR_L1_TABLE_SIZE * CFG_NU_OS_KERN_PROCESS_CORE_MAX_PROCESSES) +
                          (PROC_AR_L2_TABLE_SIZE * PROC_AR_L2_TOTAL);

        /* Make sure we have enough space for all of the regions */
        if (translation_req <= tlb_section_size)
        {
            status = NU_SUCCESS;
        }
    }

    return (status);
}

/*************************************************************************
*
* FUNCTION
*
*      PROC_AR_Schedule
*
* DESCRIPTION
*
*      Architecture specific code that must be executed when changing
*      processes
*
* INPUTS
*
*      process
*      return_addr
*
* OUTPUTS
*
*      None
*
*************************************************************************/
VOID PROC_AR_Schedule(PROC_CB *process, VOID* return_addr)
{
#if (CFG_NU_OS_ARCH_ARM_COM_MMU_VARIANT == 0)

    /* Clean cache */
    ESAL_GE_MEM_DCACHE_ALL_FLUSH_INVAL();
    ESAL_AR_BARRIER();

#else

    /* Set to a known state ASID during TTBR update */
    ESAL_AR_SET_ASID(PROC_KERNEL_ID);

#endif

    /* Set translation table base address. */
    ESAL_AR_SET_TRANSLATION_TABLE(process -> translation);

#if (CFG_NU_OS_ARCH_ARM_COM_MMU_VARIANT == 0)
    /* Invalidate cache */
    ESAL_GE_MEM_CACHE_ALL_INVALIDATE();
    ESAL_AR_BARRIER();

    /* Drain Write Buffers */
    ESAL_AR_DRAIN_WRITE_BUFFERS();
#else

    /* Set address space identifier. */
    ESAL_AR_SET_ASID(process -> id);

#endif

    /* Invalidate translation lookaside buffers */
    ESAL_AR_INVALIDATE_TLB();

    /* Check if interrupt occurred while thread was in user mode */
    if ((process -> kernel_mode == NU_FALSE) && (return_addr == NU_NULL))
    {
        /* Set client domain access */
        ESAL_AR_SET_DOMAIN(ESAL_AR_DACR_CLIENT);
    }
}

/*************************************************************************
*
* FUNCTION
*
*       PROC_AR_Mem_Mgmt_Cleanup
*
* DESCRIPTION
*
*       Finds all level 2 tables that were allocated for a process and
*       frees them.
*
* INPUTS
*
*       Process
*
* OUTPUTS
*
*       NU_SUCCESS          Function completed successfully
*
*************************************************************************/
STATUS PROC_AR_Mem_Mgmt_Cleanup(PROC_CB *process)
{
    PROC_AR_L1_DESC *level_1;
    PROC_AR_L2_DESC *level_2;
    INT    l1_index;
    INT    use_index;

    /* Read the level 1 base address */
    level_1 = (PROC_AR_L1_DESC *)process -> translation;

    /* Loop through each element to search for previously allocated
       level 2 tables */
    for (l1_index = 0; l1_index < PROC_AR_L1_DESCS; l1_index++)
    {
        /* Determine if this entry points to a table */
        if ((level_1[l1_index] & PROC_AR_L1_PAGE_TABLE) == PROC_AR_L1_PAGE_TABLE)
        {
            /* Read the address of the level 2 table */
            level_2 = (PROC_AR_L1_DESC *)(level_1[l1_index] & PROC_AR_L2_BASE_MASK);

            /* Determine the index in the array */
            use_index = (INT)(((UNSIGNED)level_2 - 
                              ((PROC_AR_L1_TABLE_SIZE * CFG_NU_OS_KERN_PROCESS_CORE_MAX_PROCESSES) + 
                              (UNSIGNED)PROC_AR_L1_Base)) / 
                              PROC_AR_L2_TABLE_SIZE);

            /* Clear the level 2 use array */
            PROC_AR_L2_Use[use_index] = NU_FALSE;

            /* Decrement the l2 counter */
            PROC_AR_L2_Count--;

            /* Note: the level 1 entry does not need to be updated, when the
               level 1 table is used again it will be completely reinitialized */
        }
    }

    return (NU_SUCCESS);
}

/*************************************************************************
*
* FUNCTION
*
*       PROC_AR_MMU_Enable
*
* DESCRIPTION
*
*       Sets up all target related functionality and turns the MMU on.
*
* INPUTS
*
*       ttbr                Translation table pointer
*
* OUTPUTS
*
*       None
*
*************************************************************************/
VOID PROC_AR_MMU_Enable(VOID *ttbr)
{
    /* Set translation table base address. */
    ESAL_AR_SET_TRANSLATION_TABLE(ttbr);

#if (CFG_NU_OS_ARCH_ARM_COM_MMU_VARIANT == 0)

    /* Set PID to kernel module (0) */
    ESAL_TS_RTE_CP_WRITE(ESAL_TS_RTE_CP15, 0, 0, ESAL_TS_RTE_C13, ESAL_TS_RTE_C0, 0);
    ESAL_AR_BARRIER();

#else

    /* Set the ASID for the kernel */
    ESAL_AR_SET_ASID(PROC_KERNEL_ID);

#endif

    /* Initialize Domains */
    ESAL_AR_SET_DOMAIN(ESAL_AR_DACR_MANAGER);

    /* Turn the MMU on */
    ESAL_AR_MMU_ENABLE();
}

/*************************************************************************
*
* FUNCTION
*
*       PROC_AR_Attach_Memory
*
* DESCRIPTION
*
*       Setups up the region in the translation table.  If more
*       space is needed will request more space for tables.
*
* INPUTS
*
*       process
*       region
*
* OUTPUTS
*
*       NU_SUCCESS          Function completed successfully
*       NU_UNAVAILABLE      Not enough space for translation tables
*
*************************************************************************/
STATUS PROC_AR_Attach_Memory(PROC_CB* process, PROC_MEMORY* region)
{
    PROC_AR_L1_DESC *level_1;
    PROC_AR_L2_DESC *level_2;
    PROC_AR_L1_DESC *level_1_desc;
    UINT32           start_addr;
    UINT32           end_addr;
    UINT32           current_addr;
    STATUS           status = NU_SUCCESS;

    /* If a level 1 table does not exist create it */
    if (process -> translation == NU_NULL)
    {
        /* Create level 1 table */
        process -> translation = PROC_AR_Create_L1_Table(process, PROC_AR_DEFAULT_L1_ATTR);
    }

    /* Walk through memory region, initializing minimum number of
       pages necessary to represent it. */
    level_1 = (PROC_AR_L1_DESC*)(process -> translation);
    start_addr = (UINT32)(region -> phys_base);
    end_addr = start_addr + region -> size;
    current_addr = start_addr;

    /* Visit all of the pages that make up the memory region */
    while ((current_addr < (end_addr - 1)) && (status == NU_SUCCESS))
    {
        /* Get the address of the level 1 descriptor */
        level_1_desc = (PROC_AR_L1_DESC*)(((UNSIGNED)(level_1) &
                        PROC_AR_TTBR_MASK) |
                        ((current_addr & PROC_AR_L1_INDEX_MASK) >>
                        PROC_AR_L1_INDEX_SHIFT));

        /* If this is not a page table descriptor then we have not allocated
           a level 2 table to represent it.  We need to allocate the table and
           update the level 1 descriptor. */
        if((*level_1_desc & PROC_AR_L1_PAGE_TABLE) != PROC_AR_L1_PAGE_TABLE)
        {
            /* Create the table */
            level_2 = PROC_AR_Create_L2_Table(*level_1_desc);

            /* Verify the table was created */
            if (level_2 != NU_NULL)
            {
                /* New attributes */
                *level_1_desc = (PROC_AR_DOMAIN);

                /* Put the new page table base address in the descriptor */
                *level_1_desc |= ((UNSIGNED)(level_2) & (PROC_AR_L2_BASE_MASK));

                /* Clear the fault and make it a page table descriptor */
                *level_1_desc |= PROC_AR_L1_PAGE_TABLE;
            }
            else
            {
                /* Could not obtain a new table */
                status = NU_UNAVAILABLE;
            }
        }

        if (status == NU_SUCCESS)
        {
            /* Set Region options */
            PROC_AR_Set_Attributes(region, level_1_desc, current_addr);

            /* Go to next page */
            current_addr += PROC_AR_L2_PAGE_SIZE;
        }
    }

    return (status);
}

/*************************************************************************
*
* FUNCTION
*
*       PROC_AR_Detach_Memory
*
* DESCRIPTION
*
*       Removes all attributes from the given region.
*
* INPUTS
*
*       region
*
* OUTPUTS
*
*       NU_SUCCESS          Function completed successfully
*       NU_UNAVAILABLE      Not enough space for translation tables
*
*************************************************************************/
STATUS PROC_AR_Detach_Memory(PROC_MEMORY* region)
{
    PROC_AR_L1_DESC *level_1;
    PROC_AR_L1_DESC *level_1_desc;
    UINT32           start_addr;
    UINT32           end_addr;
    UINT32           current_addr;
    PROC_CB         *process;
    STATUS           status = NU_SUCCESS;

    /* Get the process control block */
    process = region -> owner;

    /* Walk through memory region, initializing minimum number of
       pages necessary to represent it. */
    level_1 = (PROC_AR_L1_DESC*)(process -> translation);
    start_addr = (UINT32)(region -> phys_base);
    end_addr = start_addr + region -> size;
    current_addr = start_addr;

    /* Clear the region attributes */
    region -> attributes = 0;

    /* Visit all of the pages that make up the memory region */
    while (current_addr < (end_addr - 1))
    {
        /* Get the address of the level 1 descriptor */
        level_1_desc = (PROC_AR_L1_DESC*)(((UNSIGNED)(level_1) &
                        PROC_AR_TTBR_MASK) |
                        ((current_addr & PROC_AR_L1_INDEX_MASK) >>
                        PROC_AR_L1_INDEX_SHIFT));

        /* If this is a page table descriptor then we have previously
           set attributes that need to be updated */
        if((*level_1_desc & PROC_AR_L1_PAGE_TABLE) == PROC_AR_L1_PAGE_TABLE)
        {
            /* Set Region options */
            PROC_AR_Set_Attributes(region, level_1_desc, current_addr);
        }

        /* Go to next page */
        current_addr += PROC_AR_L2_PAGE_SIZE;
    }

    return (status);
}

/*************************************************************************
*
* FUNCTION
*
*       PROC_AR_Set_Attributes
*
* DESCRIPTION
*
*       This service sets the attributes for a specific region
*
* INPUTS
*
*       region              Pointer to the memory region control block
*                           for which to change options.
*       current_addr        Address of page table entry.
*       level_1_desc        Address of the level 1 descriptor.
*
* OUTPUTS
*
*       None
*
*************************************************************************/
static VOID PROC_AR_Set_Attributes(PROC_MEMORY* region,
                                   PROC_AR_L1_DESC *level_1_desc,
                                   UINT32 current_addr)
{
    PROC_AR_L2_DESC  *level_2_desc;
    UINT32            level_2_bits;
    UINT32            phys_addr;

    /* Get the address of the level 2 descriptor */
    level_2_desc = (PROC_AR_L2_DESC*)(((UNSIGNED)(*level_1_desc) &
                   PROC_AR_L2_BASE_MASK) |
                   ((current_addr & PROC_AR_L2_INDEX_MASK) >>
                   PROC_AR_L2_INDEX_SHIFT));

    /* Preserve the address, all virtual mappings will be reused */
    phys_addr = ((*level_2_desc) & PROC_AR_L2_ADDRESS_MASK);

    /* Set up the descriptor */
    level_2_bits = ((phys_addr & PROC_AR_L2_ADDRESS_MASK) | PROC_AR_L2_SMALL_PAGE);

    /* Verify and set the appropriate access permission bits
       if a valid set is not detected no access will be set */
    if((region -> attributes & NU_MEM_WRITE) == NU_MEM_WRITE)
    {
        /* Write attribute implies read */
        level_2_bits |= PROC_AR_L2_AP_READ_WRITE;
    }
    else if((region -> attributes & NU_MEM_READ) == NU_MEM_READ)
    {
        level_2_bits |= PROC_AR_L2_AP_READ_ONLY;
    }
    else if((region -> attributes & NU_MEM_EXEC) == NU_MEM_EXEC)
    {
        /* Executable implies read */
        level_2_bits |= PROC_AR_L2_AP_READ_ONLY;
    }
    else
    {
        level_2_bits |= PROC_AR_L2_AP_NO_ACCESS;
    }

#if (CFG_NU_OS_ARCH_ARM_COM_MMU_VARIANT == 1)
    /* This region has not been marked as executable, do not
       allow execution in this region */
    if ((region -> attributes & PROC_NO_EXECUTE) == PROC_NO_EXECUTE)
    {
        level_2_bits |= PROC_AR_L2_SMALL_PAGE_XN;
    }
#endif

    /* Cache is the default value unless inhibit is set */
    if((region -> attributes & NU_CACHE_INHIBIT) != NU_CACHE_INHIBIT)
    {
        level_2_bits |= PROC_AR_L2_CACHEABLE;
    }

    /* Write back case is the default setting.  However, at the system level
       if the write buffer bit is clear in the coprocessor write through cache
       will still be used regardless. */
    if((region -> attributes & NU_CACHE_WRITE_THROUGH) != NU_CACHE_WRITE_THROUGH)
    {
        level_2_bits |= PROC_AR_L2_BUFFERABLE;
    }

    /* Update the descriptor */
    *level_2_desc = level_2_bits;
}

/*************************************************************************
*
* FUNCTION
*
*       PROC_AR_Create_L1_Table
*
* DESCRIPTION
*
*       This function creates a memory space to be used as
*       the level 1 page table.
*
* INPUTS
*
*       process
*       default_attr
*
* OUTPUTS
*
*       Pointer to the newly created level 1 page table.
*
*************************************************************************/
static PROC_AR_L1_DESC* PROC_AR_Create_L1_Table(PROC_CB* process, UNSIGNED default_attr)
{
    UINT32           desc;
    UNSIGNED         physical_addr;
    UNSIGNED         virtual_addr;
    UNSIGNED         end_addr;
    UNSIGNED         level_1_bits;
    UNSIGNED         cache_type;
    INT              region_num;
    PROC_AR_L1_DESC *table;
    PROC_AR_L1_DESC *level_1_desc;

    /* Level 1 table is calculated by indexing into the
       level 1 table space via the process id, all spaces
       are guaranteed to be aligned properly */
    table = (PROC_AR_L1_DESC *)((PROC_AR_L1_TABLE_SIZE * (process -> id)) + (UNSIGNED)PROC_AR_L1_Base);

    /* Initialize each descriptor. */
    for(desc = 0; desc < PROC_AR_L1_DESCS; ++desc)
    {
        /* insure that all bits are 0 */
        table[desc] = 0;

        /* set up the descriptor, no level 2 table allocated yet */
        table[desc] = ((desc << PROC_AR_L1_SHIFT) |
                       PROC_AR_DOMAIN |
                       PROC_AR_L1_SECTION |
                       PROC_AR_L1_NO_CACHE |
                       default_attr);
    }

    /* Walk through all development platform memory regions defined in table */
    for (region_num = 0; region_num < ESAL_DP_MEM_NUM_REGIONS; region_num++)
    {
        /* Enable caches for this memory section */
        physical_addr = (UINT32)ESAL_DP_MEM_Region_Data[region_num].physical_start_addr;
        virtual_addr = (UINT32)ESAL_DP_MEM_Region_Data[region_num].virtual_start_addr;
        end_addr = virtual_addr + (ESAL_DP_MEM_Region_Data[region_num].size - 1);
        cache_type = ESAL_DP_MEM_Region_Data[region_num].cache_type;

        while(virtual_addr < end_addr)
        {
            /* Get the address of the level 1 descriptor */
            level_1_desc = (PROC_AR_L1_DESC*)(((UNSIGNED)(table) &
                            PROC_AR_TTBR_MASK) |
                            ((virtual_addr & PROC_AR_L1_INDEX_MASK) >>
                            PROC_AR_L1_INDEX_SHIFT));

            /* Read from the table the value of the entry */
            level_1_bits = (~PROC_AR_L1_INDEX_MASK) & (*level_1_desc);

            /* Clear the attributes from the table entry place
               address mapping */
            level_1_bits |= PROC_AR_L1_INDEX_MASK & physical_addr;

            /* Set cache related bits in translation table entry.
            NOTE: Default is uncached instruction and data. */
            if (cache_type == ESAL_WRITEBACK)
            {
                /* Update translation table entry value */
                level_1_bits |= (PROC_AR_L1_BUFFERABLE | PROC_AR_L1_CACHEABLE);
            }
            else if (cache_type == ESAL_WRITETHROUGH)
            {
                /* Update translation table entry value */
                level_1_bits |= PROC_AR_L1_CACHEABLE;
            }

#if (CFG_NU_OS_ARCH_ARM_COM_MMU_VARIANT == 1)
            /* Determine if this section should dis-allow execution */
            if (ESAL_DP_MEM_Region_Data[region_num].access_type == ESAL_DATA)
            {
                /* Update translation table entry value,
                   any attempt to execute this section
                   will result in an exception. */
                level_1_bits |= PROC_AR_L1_XN_BIT;
            }
#endif

            /* Write new bits to table */
            *level_1_desc = level_1_bits;

            /* Go to next segment */
            if ((physical_addr + ESAL_CO_MEM_TTB_SECT_SIZE) > physical_addr)
            {
                physical_addr += ESAL_CO_MEM_TTB_SECT_SIZE;
                virtual_addr += ESAL_CO_MEM_TTB_SECT_SIZE;
            }
            else
            {
                /* If setting up memory for Last MB of memory the
                   increment of current address will overflow.
                   If this happens we need to break out of the loop. */
                break;
            }
        }
    }

    return (table);
}

/*************************************************************************
*
* FUNCTION
*
*       PROC_AR_Create_L2_Table
*
* DESCRIPTION
*
*       This function creates a memory space to be used as
*       the level 2 page table.
*
* INPUTS
*
*       descriptor          Current level 1 descriptor
*
* OUTPUTS
*
*       Pointer to newly created Level 2 table.
*
*************************************************************************/
static PROC_AR_L2_DESC* PROC_AR_Create_L2_Table(UINT32 descriptor)
{
    UINT32           level_2_desc;
    UINT32           address;
    UINT32           attributes;
    PROC_AR_L2_DESC *l2_table;

    /* Allocate a level 2 table */
    l2_table = PROC_AR_Alloc_L2_Table();

    /* If the table was created populate it */
    if (l2_table != NU_NULL)
    {
        /* Read the level 1 address, this is needed for preservation of later
           region creations otherwise orphan tables can exist */
        address = (descriptor & PROC_AR_L2_ADDRESS_MASK);

        /* Read the level 1 cache attributes, this is used to preserve
           the original cache and buffer attributes.  Also note that the cache
           and buffer values are the same for level 1 and level 2 tables. */
        attributes = (descriptor & (PROC_AR_L1_CACHEABLE | PROC_AR_L1_BUFFERABLE));

        /* Read the level 1 permission attributes, this is also used for preservation
           of original attributes in the level 1 table */
        if ((descriptor & PROC_AR_L1_AP_MASK) == PROC_AR_L1_AP_READ_ONLY)
        {
            attributes |= PROC_AR_L2_AP_READ_ONLY;
        }
        else if ((descriptor & PROC_AR_L1_AP_MASK) == PROC_AR_L1_AP_READ_WRITE)
        {
            attributes |= PROC_AR_L2_AP_READ_WRITE;
        }
#if (CFG_NU_OS_ARCH_ARM_COM_MMU_VARIANT == 1)
        else if ((descriptor & PROC_AR_L1_AP_MASK) == PROC_AR_L1_AP_PRIVILEGED_RO)
        {
            attributes |= PROC_AR_L2_AP_PRIVILEGED_RO;
        }
        else if ((descriptor & PROC_AR_L1_AP_MASK) == PROC_AR_L1_AP_PRIVILEGED_RW)
        {
            attributes |= PROC_AR_L2_AP_PRIVILEGED_RW;
        }
#endif
        else /* Default to no access */
        {
            attributes |= PROC_AR_L2_AP_NO_ACCESS;
        }

#if (CFG_NU_OS_ARCH_ARM_COM_MMU_VARIANT == 1)
        /* Determine if execution is allowed */
        if ((descriptor & PROC_AR_L1_XN_BIT) == PROC_AR_L1_XN_BIT)
        {
            /* Update translation table entry value,
               any attempt to execute this section
               will result in an exception. */
            attributes |= PROC_AR_L2_SMALL_PAGE_XN;
        }
#endif

        /* initialize the table with default addresses */
        for(level_2_desc = 0; level_2_desc < PROC_AR_L2_DESCS; ++level_2_desc)
        {
            l2_table[level_2_desc] = 0;
            l2_table[level_2_desc] = ((address & PROC_AR_L1_INDEX_MASK) |
                                      (level_2_desc << PROC_AR_L2_SHIFT) |
                                      PROC_AR_L2_SMALL_PAGE |
                                      attributes);
        }
    }

    return (l2_table);
}

/*************************************************************************
*
* FUNCTION
*
*       PROC_AR_Alloc_L2_Table
*
* DESCRIPTION
*
*       This function finds an available level 2 table in the TLB space.
*       Most processes stay in memory so putting the search to start at
*       the count should speed up the search, when processes are removed
*       the level 2 table will be freed creating possible holes to be
*       reclaimed later.  If no free table is found null will be returned.
*
* INPUTS
*
*       None
*
* OUTPUTS
*
*       Pointer to newly allocated Level 2 table.
*
*************************************************************************/
static PROC_AR_L2_DESC *PROC_AR_Alloc_L2_Table(VOID)
{
    PROC_AR_L2_DESC *table = NU_NULL;
    INT              current_index = PROC_AR_L2_Count;
    INT              count = 0;
    UNSIGNED         l2_base;

    /* Find an available level 2 table */
    while ((table == NU_NULL) && (count < PROC_AR_L2_TOTAL))
    {
        /* Check to see if the table at the current index
           is available */
        if (PROC_AR_L2_Use[current_index] == NU_FALSE)
        {
            /* This space is available, mark as used */
            PROC_AR_L2_Use[current_index] = NU_TRUE;

            /* Increment the number of l2 tables used */
            PROC_AR_L2_Count++;

            /* Index into the table */
            l2_base = (PROC_AR_L1_TABLE_SIZE * CFG_NU_OS_KERN_PROCESS_CORE_MAX_PROCESSES) + (UNSIGNED)PROC_AR_L1_Base;
            table = (PROC_AR_L2_DESC *)((PROC_AR_L2_TABLE_SIZE * current_index) + l2_base);
        }
        else
        {
            /* Increment for the next table in the search */
            current_index++;
            count++;

            /* Check to see if we should loop to the beginning */
            if ((current_index == PROC_AR_L2_TOTAL) && (count != current_index))
            {
                /* Move to the beginning of the array */
                current_index = 0;
            }
        }
    }

    return (table);
}
