/***************************************************************************
 * Copyright (c) 2024 Microsoft Corporation 
 * 
 * This program and the accompanying materials are made available under the
 * terms of the MIT License which is available at
 * https://opensource.org/licenses/MIT.
 * 
 * SPDX-License-Identifier: MIT
 **************************************************************************/


/**************************************************************************/
/**************************************************************************/
/**                                                                       */
/** ThreadX Component                                                     */
/**                                                                       */
/**   Module Manager                                                      */
/**                                                                       */
/**************************************************************************/
/**************************************************************************/

#define TX_SOURCE_CODE

#include "tx_api.h"
#include "txm_module.h"

/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    _txm_module_manager_region_size_get             Cortex-R4/MPU/IAR   */
/*                                                           6.1          */
/*  AUTHOR                                                                */
/*                                                                        */
/*    Scott Larson, Microsoft Corporation                                 */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function converts the region size in bytes to the block size   */
/*    for the Cortex-R4 MPU specification.                                */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    block_size                        Size of the block in bytes        */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    MPU size specification                                              */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    None                                                                */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    _txm_module_manager_mm_register_setup                               */
/*                                                                        */
/*  RELEASE HISTORY                                                       */
/*                                                                        */
/*    DATE              NAME                      DESCRIPTION             */
/*                                                                        */
/*  09-30-2020      Scott Larson            Initial Version 6.1           */
/*                                                                        */
/**************************************************************************/
ULONG  _txm_module_manager_region_size_get(ULONG block_size)
{

ULONG   return_value;

    /* Process relative to the input block size.  */
    if (block_size == 32)
    {
        return_value =  0x04;
    }
    else if (block_size == 64)
    {
        return_value =  0x05;
    }
    else if (block_size == 128)
    {
        return_value =  0x06;
    }
    else if (block_size == 256)
    {
        return_value =  0x07;
    }
    else if (block_size == 512)
    {
        return_value =  0x08;
    }
    else if (block_size == 1024)
    {
        return_value =  0x09;
    }
    else if (block_size == 2048)
    {
        return_value =  0x0A;
    }
    else if (block_size == 4096)
    {
        return_value =  0x0B;
    }
    else if (block_size == 8192)
    {
        return_value =  0x0C;
    }
    else if (block_size == 16384)
    {
        return_value =  0x0D;
    }
    else if (block_size == 32768)
    {
        return_value =  0x0E;
    }
    else if (block_size == 65536)
    {
        return_value =  0x0F;
    }
    else if (block_size == 131072)
    {
        return_value =  0x10;
    }
    else if (block_size == 262144)
    {
        return_value =  0x11;
    }
    else if (block_size == 524288)
    {
        return_value =  0x12;
    }
    else if (block_size == 1048576)
    {
        return_value =  0x13;
    }
    else if (block_size == 2097152)
    {
        return_value =  0x14;
    }
    else
    {
        /* Max 4MB MPU pages for modules.  */
        return_value =  0x15;
    }

    return(return_value);
}


/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    _txm_module_manager_calculate_srd_bits          Cortex-R4/MPU/IAR   */
/*                                                           6.1          */
/*  AUTHOR                                                                */
/*                                                                        */
/*    Scott Larson, Microsoft Corporation                                 */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function calculates the SRD bits that need to be set to        */
/*    protect "length" bytes in a block.                                  */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    block_size                        Size of the block in bytes        */
/*    length                            Actual length in bytes            */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    SRD bits to be OR'ed with region attribute register.                */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    None                                                                */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    _txm_module_manager_mm_register_setup                               */
/*                                                                        */
/*  RELEASE HISTORY                                                       */
/*                                                                        */
/*    DATE              NAME                      DESCRIPTION             */
/*                                                                        */
/*  09-30-2020      Scott Larson            Initial Version 6.1           */
/*                                                                        */
/**************************************************************************/
ULONG  _txm_module_manager_calculate_srd_bits(ULONG block_size, ULONG length)
{

ULONG   srd_bits = 0;
UINT    srd_bit_index;

    /* length is smaller than block_size, set SRD bits if block_size is 256 or more. */
    if((block_size >= 256) && (length < block_size))
    {
        /* Divide block_size by 8 by shifting right 3. Result is size of subregion.  */
        block_size = block_size >> 3;

        /* Set SRD index into attribute register. */
        srd_bit_index = 8;

        /* If subregion overlaps length, move to the next subregion. */
        while(length > block_size)
        {
            length = length - block_size;
            srd_bit_index++;
        }

        /* Check for a portion of code remaining. */
        if(length)
        {
            srd_bit_index++;
        }

        /* Set unused subregion bits. */
        while(srd_bit_index < 16)
        {
            srd_bits = srd_bits | (0x1 << srd_bit_index);
            srd_bit_index++;
        }
    }

    return(srd_bits);
}


/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    _txm_module_manager_mm_register_setup           Cortex-R4/MPU/IAR   */
/*                                                           6.1          */
/*  AUTHOR                                                                */
/*                                                                        */
/*    Scott Larson, Microsoft Corporation                                 */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function sets up the MPU register definitions based on the     */
/*    module's memory characteristics.                                    */
/*    MPU layout for the Cortex-R4:                                       */
/*    Entry     Description                                               */
/*      0       Kernel mode entry                                         */
/*      1       Module code region                                        */
/*      2       Module code region                                        */
/*      3       Module code region                                        */
/*      4       Module code region                                        */
/*      5       Module data region                                        */
/*      6       Module data region                                        */
/*      7       Module data region                                        */
/*      8       Module data region                                        */
/*      9       Module shared memory region                               */
/*      10      Module shared memory region                               */
/*      11      Module shared memory region                               */
/*                                                                        */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    module_instance                   Pointer to module instance        */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    MPU specifications for module in module_instance                    */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    _txm_module_manager_region_size_get                                 */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    _txm_module_manager_thread_create                                   */
/*                                                                        */
/*  RELEASE HISTORY                                                       */
/*                                                                        */
/*    DATE              NAME                      DESCRIPTION             */
/*                                                                        */
/*  09-30-2020      Scott Larson            Initial Version 6.1           */
/*                                                                        */
/**************************************************************************/
VOID  _txm_module_manager_mm_register_setup(TXM_MODULE_INSTANCE *module_instance)
{

ULONG   code_address;
ULONG   code_size;
ULONG   data_address;
ULONG   data_size;
ULONG   start_stop_stack_size;
ULONG   callback_stack_size;
ULONG   block_size;
ULONG   base_address_register;
ULONG   size_register;
ULONG   region_size;
ULONG   srd_bits = 0;
UINT    mpu_table_index;
UINT    i;


    /* Setup the first region for kernel mode entry.  */

    /* Set address register to user mode entry function address, which is guaranteed to be at least 32-byte aligned.  */
    module_instance -> txm_module_instance_mpu_registers[TXM_MODULE_MPU_KERNEL_ENTRY_INDEX].txm_module_mpu_region_address =     (ULONG) _txm_module_manager_user_mode_entry;

    /* Set the size (32 bytes) and enable bit.  */
    module_instance -> txm_module_instance_mpu_registers[TXM_MODULE_MPU_KERNEL_ENTRY_INDEX].txm_module_mpu_region_size =        (_txm_module_manager_region_size_get(32) << 1) | TXM_ENABLE_REGION;

    /* Set attributes.  */
    module_instance -> txm_module_instance_mpu_registers[TXM_MODULE_MPU_KERNEL_ENTRY_INDEX].txm_module_mpu_region_attributes =  TXM_MODULE_MPU_CODE_ACCESS_CONTROL;

    /* End of kernel mode entry setup.  */

    /* Setup code protection.  */

    /* Initialize the MPU table index.  */
    mpu_table_index = 1;

    /* Pickup code starting address and actual size.  */
    code_address =  (ULONG) module_instance -> txm_module_instance_code_start;
    code_size =     module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_code_size;

    /* Determine code block sizes. Minimize the alignment requirement.
       There are 4 MPU code entries available. The following is how the code size
       will be distributed:
       1. 1/4 of the largest power of two that is greater than or equal to code size.
       2. 1/4 of the largest power of two that is greater than or equal to code size.
       3. Largest power of 2 that fits in the remaining space.
       4. Smallest power of 2 that exceeds the remaining space, minimum 32. */

    /* Now loop through to setup MPU protection for the code area.  */
    for (i = 0; i < TXM_MODULE_MPU_CODE_ENTRIES; i++)
    {
        /* First two MPU blocks are 1/4 of the largest power of two
           that is greater than or equal to code size.  */
        if (i < 2)
        {
            block_size = _txm_power_of_two_block_size(code_size) >> 2;
        }

        /* Third MPU block is the largest power of 2 that fits in the remaining space. */
        else if (i == 2)
        {
            /* Subtract (block_size*2) from code_size to calculate remaining space.  */
            code_size = code_size - (block_size << 1);
            block_size = _txm_power_of_two_block_size(code_size) >> 1;
        }

        /* Last MPU block is the smallest power of 2 that exceeds the remaining space, minimum 32.  */
        else
        {
            /* Calculate remaining space.  */
            code_size =  code_size - block_size;
            block_size = _txm_power_of_two_block_size(code_size);
            srd_bits = _txm_module_manager_calculate_srd_bits(block_size, code_size);
        }

        /* Build the base address register.  */
        base_address_register = (code_address & ~(block_size - 1));

        /* Calculate the region size information.  */
        region_size = (_txm_module_manager_region_size_get(block_size) << 1);

        /* Build the size register.  */
        size_register = srd_bits | region_size | TXM_ENABLE_REGION;

        /* Setup the MPU address, size, and attribute registers.  */
        module_instance -> txm_module_instance_mpu_registers[mpu_table_index].txm_module_mpu_region_address =       base_address_register;
        module_instance -> txm_module_instance_mpu_registers[mpu_table_index].txm_module_mpu_region_size =          size_register;
        module_instance -> txm_module_instance_mpu_registers[mpu_table_index].txm_module_mpu_region_attributes =    TXM_MODULE_MPU_CODE_ACCESS_CONTROL;

        /* Adjust the code address.  */
        code_address =  code_address + block_size;

        /* Increment MPU table index.  */
        mpu_table_index++;
    }
    /* End of code protection.  */

    /* Setup data protection.  */

    /* Reset SRD bitfield.  */
    srd_bits = 0;

    /* Pickup data starting address and actual size.  */
    data_address =  (ULONG) module_instance -> txm_module_instance_data_start;

    /* Adjust the size of the module elements to be aligned to the default alignment. We do this
       so that when we partition the allocated memory, we can simply place these regions right beside
       each other without having to align their pointers. Note this only works when they all have
       the same alignment.  */

    data_size =             module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_data_size;
    start_stop_stack_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_start_stop_stack_size;
    callback_stack_size =   module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_callback_stack_size;

    data_size =              ((data_size + TXM_MODULE_DATA_ALIGNMENT - 1)/TXM_MODULE_DATA_ALIGNMENT) * TXM_MODULE_DATA_ALIGNMENT;

    start_stop_stack_size =  ((start_stop_stack_size + TXM_MODULE_DATA_ALIGNMENT - 1)/TXM_MODULE_DATA_ALIGNMENT) * TXM_MODULE_DATA_ALIGNMENT;

    callback_stack_size =    ((callback_stack_size + TXM_MODULE_DATA_ALIGNMENT - 1)/TXM_MODULE_DATA_ALIGNMENT) * TXM_MODULE_DATA_ALIGNMENT;

    /* Update the data size to include thread stacks.  */
    data_size = data_size + start_stop_stack_size + callback_stack_size;

    /* Determine data block sizes. Minimize the alignment requirement.
       There are 4 MPU data entries available. The following is how the data size
       will be distributed:
       1. 1/4 of the largest power of two that is greater than or equal to data size.
       2. 1/4 of the largest power of two that is greater than or equal to data size.
       3. Largest power of 2 that fits in the remaining space.
       4. Smallest power of 2 that exceeds the remaining space, minimum 32. */

    /* Now loop through to setup MPU protection for the data area.  */
    for (i = 0; i < TXM_MODULE_MPU_DATA_ENTRIES; i++)
    {
        /* First two MPU blocks are 1/4 of the largest power of two
           that is greater than or equal to data size.  */
        if (i < 2)
        {
            block_size = _txm_power_of_two_block_size(data_size) >> 2;
        }

        /* Third MPU block is the largest power of 2 that fits in the remaining space.  */
        else if (i == 2)
        {
            /* Subtract (block_size*2) from data_size to calculate remaining space.  */
            data_size = data_size - (block_size << 1);
            block_size = _txm_power_of_two_block_size(data_size) >> 1;
        }

        /* Last MPU block is the smallest power of 2 that exceeds the remaining space, minimum 32.  */
        else
        {
            /* Calculate remaining space.  */
            data_size =  data_size - block_size;
            block_size = _txm_power_of_two_block_size(data_size);
            srd_bits = _txm_module_manager_calculate_srd_bits(block_size, data_size);
        }

        /* Build the base address register.  */
        base_address_register = (data_address & ~(block_size - 1));

        /* Calculate the region size information.  */
        region_size = (_txm_module_manager_region_size_get(block_size) << 1);

        /* Build the size register.  */
        size_register = srd_bits | region_size | TXM_ENABLE_REGION;

        /* Setup the MPU address, size, and attribute registers.  */
        module_instance -> txm_module_instance_mpu_registers[mpu_table_index].txm_module_mpu_region_address =       base_address_register;
        module_instance -> txm_module_instance_mpu_registers[mpu_table_index].txm_module_mpu_region_size =          size_register;
        module_instance -> txm_module_instance_mpu_registers[mpu_table_index].txm_module_mpu_region_attributes =    TXM_MODULE_MPU_DATA_ACCESS_CONTROL;

        /* Adjust the data address.  */
        data_address =  data_address + block_size;

        /* Increment MPU table index.  */
        mpu_table_index++;
    }
}

/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    _txm_module_manager_inside_data_check          Cortex-R4/MPU/IAR    */
/*                                                           6.1.6        */
/*  AUTHOR                                                                */
/*                                                                        */
/*    Scott Larson, Microsoft Corporation                                 */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function checks if the specified object is inside shared       */
/*    memory.                                                             */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    module_instance                   Pointer to module instance        */
/*    obj_ptr                           Pointer to the object             */
/*    obj_size                          Size of the object                */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    Whether the object is inside the shared memory region.              */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    N/A                                                                 */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    Module dispatch check functions                                     */
/*                                                                        */
/*  RELEASE HISTORY                                                       */
/*                                                                        */
/*    DATE              NAME                      DESCRIPTION             */
/*                                                                        */
/*  09-30-2020      Scott Larson            Initial Version 6.1           */
/*  04-02-2021      Scott Larson            Modified comments, added      */
/*                                            check for overflow,         *
/*                                            resulting in version 6.1.6  */
/*                                                                        */
/**************************************************************************/
UINT _txm_module_manager_inside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr, UINT obj_size)
{

UINT shared_memory_index;
UINT num_shared_memory_mpu_entries;
ALIGN_TYPE shared_memory_address_start;
ALIGN_TYPE shared_memory_address_end;

    /* Check for overflow. */
    if ((obj_ptr) > ((obj_ptr) + (obj_size)))
    {
        return(TX_FALSE);
    }
    
    /* Check if the object is inside the module data.  */
    if ((obj_ptr >= (ALIGN_TYPE) module_instance -> txm_module_instance_data_start) &&
        ((obj_ptr + obj_size) <= ((ALIGN_TYPE) module_instance -> txm_module_instance_data_end + 1)))
    {
        return(TX_TRUE);
    }

    /* Check if the object is inside the shared memory.  */
    num_shared_memory_mpu_entries = module_instance -> txm_module_instance_shared_memory_count;
    for (shared_memory_index = 0; shared_memory_index < num_shared_memory_mpu_entries; shared_memory_index++)
    {

        shared_memory_address_start = (ALIGN_TYPE) module_instance -> txm_module_instance_shared_memory_address[shared_memory_index];
        shared_memory_address_end = shared_memory_address_start + module_instance -> txm_module_instance_shared_memory_length[shared_memory_index];

        if ((obj_ptr >= (ALIGN_TYPE) shared_memory_address_start) &&
            ((obj_ptr + obj_size) <= (ALIGN_TYPE) shared_memory_address_end))
        {
            return(TX_TRUE);
        }
    }

    return(TX_FALSE);
}

