/* **************************************************************************************************** * * uC/OS-MMU * * (c) Copyright 2012, Micrium, FL * All rights reserved. * * All rights reserved. Protected by international copyright laws. * Knowledge of the source code may not be used to write a similar * product. This file may only be used in accordance with a license * and should not be redistributed in any way. * * * Version : 3.1.2pp-35237 * File : MMU.c (#1) * Programmer(s) : EO **************************************************************************************************** */ /*! **************************************************************************************************** * \defgroup PAR_CPU_MMU Target Specific MMU Implementation * \ingroup PAR_CORE * * * The memory management unit (MMU) for the ... * **************************************************************************************************** */ /* **************************************************************************************************** * INCLUDES **************************************************************************************************** */ #include "mmu.h" #include "xpseudo_asm.h" #include "xil_cache.h" #include "lib_mem.h" #include "mmu_cfg.h" /* **************************************************************************************************** * GOBAL DATA **************************************************************************************************** */ extern INT32U MMUTable; /* **************************************************************************************************** * PRIVATE FUNCTION PROTOTYPES **************************************************************************************************** */ static MMU_ERR_T MMUTblAdd (PAR_MEM_REGION_REF_T mem, INT32U * pMMUTable); static MMU_ERR_T MMUTblAdd1M (PAR_MEM_REGION_REF_T mem, INT32U off, INT32U * pMMUTable); static void FTTSet (INT32U va, INT32U descr, INT32U * pMMUTable); static INT32U FTTGet (INT32U va, INT32U * pMMUTable); static INT32U FTTDescr (PAR_MEM_REGION_REF_T mem, INT32U offset, INT32U type, INT32U tbl); static void MMUEnable (INT32U * pMMUTable); static void MMUSwitchContext (INT32U id); /* **************************************************************************************************** * FUNCTIONS **************************************************************************************************** */ /*------------------------------------------------------------------------------------------------*/ /*! * \brief INIT THE MMU * * \ingroup PAR_CPU_MMU * * This function initialize the target specific MMU * * \note The integrator must supply the contents of the PARErrLogging() function to handle * possible error conditions. */ /*------------------------------------------------------------------------------------------------*/ MMU_ERR_T MMUInit (void) { INT32U n; /* Local: loop counter */ MMU_ERR_T err = MMU_ERR_NONE; Mem_Clr(&MMUTable,FTT_SIZE * sizeof(CPU_WORD_SIZE_32)); for(n = 0; n < sizeof(PARMemTbl_Core)/sizeof(PAR_MEM_REGION_T); n++){ err = MMUTblAdd((PAR_MEM_REGION_REF_T)&PARMemTbl_Core[n], &MMUTable); if(err != MMU_ERR_NONE) return err; } MMUEnable(&MMUTable); /* Enable the MMU */ return MMU_ERR_NONE; } /* **************************************************************************************************** * LOCAL FUNCTIONS **************************************************************************************************** */ /*------------------------------------------------------------------------------------------------*/ /*! * \brief MMU TABLE CONSTRUCTION * * \ingroup PAR_CPU_MMU * * This function constructs the needed table entries for a single user defined memory * region with the following rules: * * - build user memory size with blocks of 4K, 64K and 1M portions * * - ensure that each block is aligned at size (4K Block is aligned at 4K boundary, etc.) * * \param mem Reference to memory region * * \note Memory portions below 1KB will be ignored, because the MMU granularity starts with 1kB */ /*------------------------------------------------------------------------------------------------*/ static MMU_ERR_T MMUTblAdd(const PAR_MEM_REGION_REF_T mem, INT32U * pMMUTable) { INT32U vaddr; /* Local: virtual address */ INT32U off; /* Local: offset in memory region */ INT32U size; /* Local: remaining size of memory region */ MMU_ERR_T err = MMU_ERR_NONE; /*------------------------------------------*/ vaddr = mem->VA_Start; /* set virtual memory address */ size = mem->Size; /* set size to memory region size */ off = 0; /* set memory region offset to 0 */ if ((vaddr & (MMU_SIZE_1MB - 1)) != 0) { /* see, if VA is not aligned at 4k */ return MMU_ERR_ALIGN; } if ((size % MMU_SIZE_1MB) != 0) { /* see, if size is not multiple of 4k */ return MMU_ERR_SIZE; } while(size >= MMU_SIZE_1MB) { /* create section for memory region */ err = MMUTblAdd1M(mem, off, pMMUTable); /* add a 1M section */ if(err != MMU_ERR_NONE) return err; vaddr += MMU_SIZE_1MB; /* update virtual address and */ off += MMU_SIZE_1MB; /* offset and */ size -= MMU_SIZE_1MB; /* remaining bytes in memory region */ } return MMU_ERR_NONE; } /*------------------------------------------------------------------------------------------------*/ /*! * \brief INSERT 1M SECTION IN MMU TABLE * * \ingroup PAR_CPU_MMU * * This function constructs the needed table entries for a single 1M memory block. * * \param mem Reference to memory region * * \param off Offset within the referenced memory region */ /*------------------------------------------------------------------------------------------------*/ static MMU_ERR_T MMUTblAdd1M(PAR_MEM_REGION_REF_T mem, INT32U off, INT32U * pMMUTable) { INT32U vaddr; /* Local: virtual address */ INT32U descr; /* Local: translation table descriptor */ INT32U type; /* Local: descriptor type information */ /*------------------------------------------*/ vaddr = mem->VA_Start + off; /* calculate virtual address */ type = FTTGet(vaddr, pMMUTable) & FTT_TYPE; /* get descriptor type */ if (type != FTT_TYPE_FLT) { /* see, if section is already in use */ return MMU_ERR_ENTRY_IN_USE; } descr = FTTDescr(mem, off, FTT_TYPE_SEC, 0); /* construct first level section descr. */ FTTSet(vaddr, descr, pMMUTable); /* set section for virtual address */ return MMU_ERR_NONE; } /*------------------------------------------------------------------------------------------------*/ /*! * \brief SET FIRST LEVEL TRANSLATION TABLE DESCRIPTOR * * \ingroup PAR_CPU_MMU * * This function sets the given descriptor in the first level translation table * which corresponds to the given virtual address. * * \param va Virtual Address * * \param descr First level descriptor */ /*------------------------------------------------------------------------------------------------*/ static void FTTSet(INT32U va, INT32U descr, INT32U * pMMUTable) { volatile INT32U *tbl; /* Local: ptr to first level table entry */ INT32U tbl_idx; /* Local: first level table index */ /*------------------------------------------*/ tbl_idx = (va >> 20) & 0x00000FFF; /* get table index out of virtual address */ tbl = pMMUTable + tbl_idx; /* set first level table base address */ /*------------------------------------------*/ *tbl = descr; /* set descriptor in first level table */ dsb(); isb(); } /*------------------------------------------------------------------------------------------------*/ /*! * \brief GET FIRST LEVEL TRANSLATION TABLE DESCRIPTOR * * \ingroup PAR_CPU_MMU * * This function returns the first level translation table descriptor of the given * virtual address. * * \param va Virtual Address * * \return The current active descriptor is returned. */ /*------------------------------------------------------------------------------------------------*/ static INT32U FTTGet(INT32U va, INT32U * pMMUTable) { volatile INT32U *tbl; /* Local: ptr to first level table entry */ INT32U tbl_idx; /* Local: first level table index */ /*------------------------------------------*/ tbl_idx = (va >> 20) & 0x00000FFF; /* get table index out of virtual address */ tbl = pMMUTable + tbl_idx; /* set first level table base address */ /*------------------------------------------*/ return (*tbl); /* get descriptor in first level table */ } /*------------------------------------------------------------------------------------------------*/ /*! * \brief CONSTRUCT FIRST LEVEL TRANSLATION TABLE DESCRIPTOR * * \ingroup PAR_CPU_MMU * * This function constructs the first level translation table descriptor of the given * type for the offset within the memory region. * * \param mem Reference to memory region * * \param offset Offset within the referenced memory region * * \param type Type information (section / coarse page / fine page / fault) * * \param base Base address of page table * * \return For the types FTT_TYPE_SEC, FTT_TYPE_CPT and FTT_TYPE_FPT, the * corresponding table descriptor is returned. For any other type, the return value is * the table descriptor for FTT_TYPE_FLT. * * \see FTT_TYPE_FLT, FTT_TYPE_CPT, FTT_TYPE_SEC, FTT_TYPE_FPT */ /*------------------------------------------------------------------------------------------------*/ static INT32U FTTDescr(PAR_MEM_REGION_REF_T mem, INT32U offset, INT32U type, INT32U base) { INT32U result = 0; /* Local: function result */ INT32U pa; /* Local: physical address */ switch(type){ case FTT_TYPE_SEC: pa = mem->PA_Start + offset; /* yes: calculate physical address */ result = FTT_TYPE_SEC | /* set descriptor type to section */ ((mem->HID & PAR_HID_CB_MASK) << 2u) | /* cachable & bufferable flag */ ((mem->HID & PAR_HID_F_XN) >> 6u) | /* Execute-Never Flag */ ((PAR_DOMAIN_MASTER) << 5) | /* owner = domain */ ((mem->AP & 0x3) << 10u) | /* access permissions AP0 and AP1 */ ((mem->AP & 0x4) << 13u) | /* access permissions AP2 */ ((mem->HID & PAR_HID_TEX_MASK) << 10u) | /* TEX attributes */ ((mem->HID & PAR_HID_F_S) << 8u) | /* shareable-flag */ ((mem->HID & PAR_HID_F_NG) << 8u) | /* Non-Global-Flag */ ((mem->HID & PAR_HID_F_NS) << 8u) | /* non-secure bit */ (pa & 0xFFF00000); /* bits[31:20] of physical address */ break; case FTT_TYPE_PT: case FTT_TYPE_FLT: case FTT_TYPE_RES: default: break; } return result; } /*------------------------------------------------------------------------------------------------*/ /*! * \brief ENABLE MMU * * \ingroup PAR_CPU_MMU * * This function enables the MMU. * * \note The first level translation table and the page tables must be initialized before * calling this function. */ /*------------------------------------------------------------------------------------------------*/ static void MMUEnable(INT32U * pMMUTable) { INT32U ctrl; /* Local: control register */ //Invalidate data and instruction cache Xil_DCacheDisable(); __set_mmu_table(((INT32U)pMMUTable & 0xFFFFC000) | PAR_TTBR0_FLAGS); /* Set first level transl. table base addr. */ __set_domain_control(0x55555555u); /* set all domains to client */ //eo verwenden noch PD1 = 1 für tlb miss in TTBR1 wird zu translation fault. __set_asid(0 & 0xFF); __set_mmu_control(0x00000000); /* Set TTBCR to 0 -> Use TTBR0 and 14Bit alignment */ __clr_mmu_fault(); __dsb(); __invalidate_tlb(); __invalidate_branchpred(); __dsb(); __isb(); ctrl = __get_sys_control(); /* get current control register */ ctrl |= MMU_SCTLR_FLAGS; /* set I,C,S and M to enable MMU */ __set_sys_control(ctrl); /* write new control register */ /*------------------------------------------*/ __no_operation(); /* perform 2 flat fetches */ __no_operation(); #if (UCOS_AMP_MASTER == DEF_ENABLED) // /* Enable L2 Cache */ // This is a "mandatory" step, don't now why, see Zynq Reference manual *((CPU_INT32U*)0xF8000A1C) = 0x020202; Xil_L2CacheEnable(); /* set L2 Aux register */ // CPU_INT32U dat = *((CPU_INT32U*)0xF8F02104); // dat = 0x72760000; // *((CPU_INT32U*)0xF8F02104) = dat; // // /* Set all latencies of tag-ram to 2 */ // // *((CPU_INT32U*)0xF8F02108) = 0x00000111; // // /* set data ram write and setup latencies to 2, data ram read to 3 */ // // *((CPU_INT32U*)0xF8F0210C) = 0x00000121; // // /* set prefetch conrtol register */ // // *((CPU_INT32U*)0xF8F02F60) = 0x30000000; // /* enable L2 Cache */ // *((CPU_INT32U*)0xF8F02100) = 0x1; #endif CPU_INT32U actlr = __get_actlr(); actlr &= ~(0x00000040); __set_actlr(actlr); /*--- MMU/address translation is enabled ---*/ } /*------------------------------------------------------------------------------------------------*/ /*! * \brief Switch Address Space * * \ingroup PAR_CPU_MMU * * This function switches the address space and manipulates the virt->phys translation. * * \param id Partition ID */ /*------------------------------------------------------------------------------------------------*/ static void MMUSwitchContext(INT32U id) { CPU_INT32U cpu_sr = 0; /*------------------------------------------*/ OS_ENTER_CRITICAL(); /* Enter critical section */ //We don't need to flush caches, when for all shared memory regions the L1 cache policy is correct write through or disabled // __flush_caches(); /* Flush caches */ // MMU_DCacheClean(); //We don't need to invalidate the tlb, beacuase we have the ASID value corresponding to a partition // __invalidate_tlb(); /* Invalidate TLB */ __dsb(); __set_asid(id & 0xFF); __set_mmu_table(((INT32U)MMUTable & 0xFFFFC000) | PAR_TTBR0_FLAGS); /* Set first level transl. table base addr. */ __dsb(); __isb(); OS_EXIT_CRITICAL(); /* Exit critical section */ /*------------------------------------------*/ }