/**
* @file arm926_mmu.c
*
* @section descr File description
*
* Driver for memory management unit of ARM926EJ-S CPU
*
* @section copyright Copyright
*
* Trampoline OS
*
* Trampoline is copyright (c) IRCCyN 2005+
* Copyright ESEO for function and data structures documentation and ARM port
* Trampoline is protected by the French intellectual property law.
*
* This software is distributed under the Lesser GNU Public Licence
*
* @section infos File informations
*
* $Date: $
* $Rev: $
* $Author: $
* $URL: $
*/
#include "tpl_compiler.h"
#include "tpl_os_std_types.h"
#include "tpl_machine.h"
#include "tpl_machine_interface.h"
#include "tpl_os_custom_types.h"
#include "tpl_os_definitions.h"
#if WITH_AUTOSAR_TIMING_PROTECTION == YES
#include "tpl_as_timing_protec.h"
#endif /* WITH_AUTOSAR_TIMING_PROTECTION */
#if WITH_AUTOSAR == YES
#include "tpl_as_isr_kernel.h"
#include "tpl_os_kernel.h"
#endif /* WITH_AUTOSAR */
#if WITH_MEMORY_PROTECTION == YES
#include "arm926_mmu.h"
/**
* this is not a variable but a symbol defined by
* linker. So, &MMU_page_table_count (address of this
* pseudo variable) gives the number of page tables
* that are required to describe "self" part of
* MMU configuration.
*/
extern u8 MMU_page_table_count;
/**
* this points on the area where page tables will be stored.
*
* Note that we cannot know the number of page tables before linking.
* That's why the linker allocates the right size. To access each
* page table, the macro page_table should be used
*/
extern u8 MMU_page_tables;
/* the following symbols are defined in the linker script */
extern u8 MMU_page_tables_end;
extern u8 MMU_start_custom_zone;
extern u8 MMU_end_custom_zone;
/**
* MMU translation tables. There is one for each context.
*/
extern MMU_translation_table MMU_translation_tables[TASK_COUNT+ISR_COUNT+1];
#define TT_INDEX(address) ((((u32)address) >> 20) & 0xFFF)
/**
* MMU page tables. There is a set of page table for each context.
* A set of page tables contains as much as needed table to cover
* the whole trampoline and application (each page table covers
* 1 MB of memory space).
*
* Pages tables are fine page tables filled with tiny pages.
*
* @note The type of this macro's expression is MMU_second_level_descriptor *
*
* @param process_id related process' identifier
* @param table_number number of the page table іn the process's page table set
*/
#define PAGE_TABLE_ADDRESS(process_id,table_number) (((MMU_second_level_descriptor*)&MMU_page_tables)+(((process_id)*((u32)&MMU_page_table_count))+(table_number))*1024)
/**
* returns the index in a fine page table of an address
*/
#define PAGE_TABLE_ENTRY_INDEX(address) ((((u32)address) >> 10) & 0x3FF)
/**
* Access directly a page table entry of a process.
*
* @note the type of this macro's expression is MMU_second_level_descriptor
*
* @param process_id related process's identifier
* @param address any virtual address which is related to the page table entry
*/
#define PAGE_TABLE_ENTRY(process_id,table_number,address) PAGE_TABLE_ADDRESS(process_id,table_number)[PAGE_TABLE_ENTRY_INDEX(address)]
/**
* gives the index of an address in a translation table
*/
#define TTABLE_INDEX(address) ((((u32)address) >> 20 ) & 0xFFF)
/**
* Returns the number of tiny page needed for the specified interval
*/
#define PAGE_COUNT(start_address,end_address) ((((u32)end_address - (u32)start_address) >> 10) & 0x3FF)
/**
* gives a section descriptor for the specified address
*/
#define SYSTEM_SECTION(address) ((MMU_section_descriptor){ \
.type = 2, \
.buffered = 0, \
.cacheable = 0, \
.must_be_one = 1, \
.domain = 0, \
.access_permission = 1, \
.section_base_address = ((address) >> 20) & 0xFFF})
#define OS_START_SEC_CODE
#include "tpl_memmap.h"
FUNC(void, OS_CODE) MMU_init (void)
{
u32 *zero_ptr;
u32 i;
u32 page_table_total_entries_count;
/* clears all translation tables */
zero_ptr = (u32*)&MMU_translation_tables;
while ((u8*)zero_ptr != (u8*)&MMU_translation_tables+sizeof(MMU_translation_tables))
*(zero_ptr++) = 0;
/* clears all page tables */
page_table_total_entries_count = TASK_COUNT + ISR_COUNT + 1; /* number of processes */
page_table_total_entries_count *= (u32)&MMU_page_table_count; /* number of page tables in a process' page table set */
page_table_total_entries_count *= 1024; /* number of entries in each (fine) page table */
zero_ptr = (u32*)&MMU_page_tables;
for (i = 0 ; i < page_table_total_entries_count ; i++)
*(zero_ptr++) = 0;
/* setup MMU domains (we use only domain 0 as client, others generates domain fault) */
__asm__ ("mov r0, #1\n"
"mcr p15, 0, r0, c3, c0, 0");
}
/**
* This function allocates a whole section (area of 1MB) with rights for kernel only.
*
* @param this_process process for which the area should be allocated
* @param address first address of the area
* @param cacheable 1 if section is cacheable, otherwise 0
*
* @pre address should be aligned to 1MB boundary
* @pre size of area is 1MB
* @pre area is not already configured (no page configured inside it)
*/
FUNC(void, OS_CODE) MMU_set_system_section (tpl_task_id this_process, u32 address, CacheableMemoryArea cacheable)
{
MMU_first_level_descriptor value = {
.section = {
.type = 2,
.buffered = 0,
.cacheable = 0,
.must_be_one = 1,
.domain = 0,
.must_be_zero1 = 0,
.access_permission = 1,
.must_be_zero2 = 0
}
};
if (cacheable == CACHEABLE)
{
value.section.buffered = 1;
value.section.cacheable = 1;
}
value.section.section_base_address = address >> 20;
MMU_translation_tables[this_process][TT_INDEX(address)].raw = value.raw;
}
/**
* This function allocateѕ all tiny pages needed for the specified area. Pages are allocated throught
* the translation table and the page table set of the specified process. Access permissions are applied
* as given (see parameters for details).
*
* @param this_process process for which the area should be allocated
* @param from first address of the area (must be aligned to 1KB)
* @param to first address after the area (ie to is not included in the area but marks the
* end of the area
* @param this_access_permission access permissions to be set, this can be :
* - 1 : privileged only access (kernel and trusted only)
* - 2 : read only access (kernel and trusted can also write)
* - 3 : read and write access (for untrusted, trusted and kernel)
* @param cacheable 1 if section is cacheable, otherwise 0
*
* @pre from must be aligned to 1KByte boundary
* @pre to must be greater or equals to from
* @pre this_process must be a valid process identifier
* @pre this_access_permission must equals to 1, 2 or 3
* @pre any page in this area must not have been setup by this function for this process
*/
FUNC(void, OS_CODE) MMU_set_tiny_pages_area (tpl_task_id this_process, u8 *from, u8 *to, u8 this_access_permission, CacheableMemoryArea cacheable)
{
u32 ttable_index; /* variable index in translation table */
u32 page_table_number; /* current page table number in the page table set of the process */
u8 *current_page_address; /* start address of current processed page */
MMU_first_level_descriptor fst_lvl_template = {.fine = {.type = 3, .must_be_one = 1, .domain = 0}};
MMU_second_level_descriptor scd_lvl_template = {.tiny_page = {.type = 3, .buffered = 1, .cacheable = 1, .access_permission = this_access_permission}}; /* cached */
if (cacheable == CACHEABLE)
{
scd_lvl_template.tiny_page.buffered = 1;
scd_lvl_template.tiny_page.cacheable = 1;
}
if (from != to)
{
page_table_number = ((u32)from - (u32)&MMU_start_custom_zone) >> 20;
current_page_address = from;
ttable_index = TTABLE_INDEX(from);
do
{
/* set the first level descriptor */
MMU_translation_tables[this_process][TTABLE_INDEX(current_page_address)].raw = fst_lvl_template.raw;
MMU_translation_tables[this_process][TTABLE_INDEX(current_page_address)].fine.fine_page_table_base_address =
((((u32)(PAGE_TABLE_ADDRESS(this_process,page_table_number))) & 0xFFFFF000) >> 12);
do
{
PAGE_TABLE_ENTRY (this_process, page_table_number, current_page_address).raw = scd_lvl_template.raw;
PAGE_TABLE_ENTRY (this_process, page_table_number, current_page_address).tiny_page.page_base_address =
(((u32)current_page_address) & 0xFFFFFC00) >> 10;
current_page_address += 1024; /* next page starts 1KB after */
}
while ((((u32)current_page_address) & 0xFFFFF) && /* if it ends at MByte boundary, we must set next ttable entry */
(((u32)current_page_address) < ((u32)to)));
ttable_index++;
page_table_number++;
}
while (((u32)current_page_address) < ((u32)to));
}
}
FUNC(void, OS_CODE) MMU_set_system_area (tpl_task_id this_process, u8 *from, u8 *to, CacheableMemoryArea cacheable)
{
MMU_set_tiny_pages_area (this_process, from, to, 1, cacheable);
}
FUNC(void, OS_CODE) MMU_set_readonly_area (tpl_task_id this_process, u8 *from, u8* to, CacheableMemoryArea cacheable)
{
MMU_set_tiny_pages_area (this_process, from, to, 2, cacheable);
}
FUNC(void, OS_CODE) MMU_set_readwrite_area (tpl_task_id this_process, u8* from, u8 *to, CacheableMemoryArea cacheable)
{
MMU_set_tiny_pages_area (this_process, from, to, 3, cacheable);
}
FUNC(void, OS_CODE) MMU_enable (void)
{
/* note : we don't have to take care about the following pipelined instructions
* as virtual addresses equals physical addresses */
__asm__ ("mrc p15, 0, r1, c1, C0, 0\n"
"orr r1, r1, #0b11\n"
"mcr p15, 0, r1, c1, C0, 0");
}
FUNC(void, OS_CODE) MMU_disable (void)
{
/* note : we don't have to take care about the following pipelined instructions
* as virtual addresses equals physical addresses */
__asm__ ("mrc p15, 0, r1, c1, C0, 0\n"
"bic r1, r1, #0b11\n"
"mcr p15, 0, r1, c1, C0, 0");
}
FUNC(void, OS_CODE) MMU_set_current_process (tpl_task_id this_process)
{
register u32 base_address;
base_address = (u32)&MMU_translation_tables[this_process];
__asm__ ("mcr p15, 0, %[ba], c2, c0, 0\n" /* sets translation table base address */
"mcr p15, 0, %[z], c8, c7, 0" /* invalidate TLB */
:
: [ba]"r" (base_address), [z]"r" (0));
}
#define OS_STOP_SEC_CODE
#include "tpl_memmap.h"
#endif /* WITH_MEMORY_PROTECTION */