I have been trying to evaluate the MMU functionality for the Xilinx zynq7000 soc that contains 2 A9-Cortex processors.
Firstly I have tried using the xil_mmu.h library, however after reading the functions that were implemented there realised that the mapping does not allow real P to V or V to P translation. Further more this library does not include any notion of granularity and only supports 1M page table entries.
Lets analyse a function from this library to illustrate my concerns: xil_mmu.c
dsb(); /* ensure completion of the BP and TLB invalidation */
isb(); /* synchronize context on this processor */
}
</code>
<code>void Xil_SetTlbAttributes(INTPTR Addr, u32 attrib)
{
u32 *ptr;
u32 section;
section = Addr / 0x100000U;
ptr = &MMUTable;
ptr += section;
if(ptr != NULL) {
*ptr = (Addr & 0xFFF00000U) | attrib;
}
Xil_DCacheFlush();
mtcp(XREG_CP15_INVAL_UTLB_UNLOCKED, 0U);
/* Invalidate all branch predictors */
mtcp(XREG_CP15_INVAL_BRANCH_ARRAY, 0U);
dsb(); /* ensure completion of the BP and TLB invalidation */
isb(); /* synchronize context on this processor */
}
</code>
void Xil_SetTlbAttributes(INTPTR Addr, u32 attrib)
{
u32 *ptr;
u32 section;
section = Addr / 0x100000U;
ptr = &MMUTable;
ptr += section;
if(ptr != NULL) {
*ptr = (Addr & 0xFFF00000U) | attrib;
}
Xil_DCacheFlush();
mtcp(XREG_CP15_INVAL_UTLB_UNLOCKED, 0U);
/* Invalidate all branch predictors */
mtcp(XREG_CP15_INVAL_BRANCH_ARRAY, 0U);
dsb(); /* ensure completion of the BP and TLB invalidation */
isb(); /* synchronize context on this processor */
}
As you can tell the section variable (one of the 4096 1MB sections in a 4GB address space) is chosen in terms of the physical address, so for instance there is no way that I can map a physical address 0x100000 to correspond to a virtual 0x200000 or other.
Own implementation
Seeing the lack of modularity with this approach (basically no real virtualization) I decided to implement an extension to the basic xil_mmu library.
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>/*
* Function to flush, invalidate the TLBs and synchronize the processors
*/
static voidhwi_mmu_picoZed_7010_sync()
{
Xil_DCacheFlush();
mtcp(XREG_CP15_INVAL_UTLB_UNLOCKED, 0U);
/* Invalidate all branch predictors */
mtcp(XREG_CP15_INVAL_BRANCH_ARRAY, 0U);
dsb(); /* ensure completion of the BP and TLB invalidation */
isb(); /* synchronize context on this processor */
}
/*
* This function sets an entry in the L1 table given the attributes
<code>/*
* Function to flush, invalidate the TLBs and synchronize the processors
*/
static void hwi_mmu_picoZed_7010_sync()
{
Xil_DCacheFlush();
mtcp(XREG_CP15_INVAL_UTLB_UNLOCKED, 0U);
/* Invalidate all branch predictors */
mtcp(XREG_CP15_INVAL_BRANCH_ARRAY, 0U);
dsb(); /* ensure completion of the BP and TLB invalidation */
isb(); /* synchronize context on this processor */
}
/*
* This function sets an entry in the L1 table given the attributes
*/
static void hwi_mmu_picoZed_7010_set_table_entry(uint32_t entry, uint32_t properties)
{
// Manage entries out of bound
// You can add some type of fault here
if (entry > 4095)
return;
// Get one of the 4096 entries
uintptr_t *new_entry = (uintptr_t *)&MMUTable + entry;
if (new_entry)
*new_entry = properties;
}
// Wrapper functions calling hwi_mmu_picoZed_7010_set_table_entry for specific entry types
// Sets a section in the L1 table with the given properties and the physical address
// bits [31:20] of the address
void hwi_mmu_set_L1_Section(uint32_t addr, uint32_t entry, uint32_t properties)
{
// Verify that this is effectively a section
if (!IS_ENTRY_SECTION(properties))
return;
hwi_mmu_picoZed_7010_set_table_entry(entry, (addr & 0xFFF00000) | properties);
}
</code>
/*
* Function to flush, invalidate the TLBs and synchronize the processors
*/
static void hwi_mmu_picoZed_7010_sync()
{
Xil_DCacheFlush();
mtcp(XREG_CP15_INVAL_UTLB_UNLOCKED, 0U);
/* Invalidate all branch predictors */
mtcp(XREG_CP15_INVAL_BRANCH_ARRAY, 0U);
dsb(); /* ensure completion of the BP and TLB invalidation */
isb(); /* synchronize context on this processor */
}
/*
* This function sets an entry in the L1 table given the attributes
*/
static void hwi_mmu_picoZed_7010_set_table_entry(uint32_t entry, uint32_t properties)
{
// Manage entries out of bound
// You can add some type of fault here
if (entry > 4095)
return;
// Get one of the 4096 entries
uintptr_t *new_entry = (uintptr_t *)&MMUTable + entry;
if (new_entry)
*new_entry = properties;
}
// Wrapper functions calling hwi_mmu_picoZed_7010_set_table_entry for specific entry types
// Sets a section in the L1 table with the given properties and the physical address
// bits [31:20] of the address
void hwi_mmu_set_L1_Section(uint32_t addr, uint32_t entry, uint32_t properties)
{
// Verify that this is effectively a section
if (!IS_ENTRY_SECTION(properties))
return;
hwi_mmu_picoZed_7010_set_table_entry(entry, (addr & 0xFFF00000) | properties);
}
As well as some macros to easier set the required memory attributes
<code>
# define SECTION_ENTRY 0x00000002
# define IS_ENTRY_SECTION(entry) (((entry)&0x04000003) == SECTION_ENTRY)
// NONE for Privileged, NONE for User
# define SECTION_AP_PERMISSION_FAULT 0x0000
// R/W for Privileged, NONE for User
# define SECTION_AP_PRIVILEDGE_ACCESS 0x0400
// R/W for Privileged, R for User
# define SECTION_AP_USER_NO_WRITE 0x0800
// R/W for Privileged, R/W for User
# define SECTION_AP_FULL_ACCESS 0x0C00
// R for Privileged, NONE for User
# define SECTION_AP_PRIVELEDGE_READ 0x8400
// R for Privileged, R for User
# define SECTION_AP_READ_ONLY 0x8800
// Basically non cacheable
# define SECTION_STRONGLY_ORDERED 0x0000
// Write to cache only (memory is stale) and allocate new entries
# define SECTION_SHAREABLE_DEVICE 0x0004
// Write to both cache and memory
# define SECTION_WRITE_THROUGH_NO_ALLOC_ON_WRITE 0x0008
// Write to cache only (memory is stale) no allocation
# define SECTION_WRITE_BACK_NO_ALLOC_ON_WRITE 0x000C
// Non cacheable in Normal memory
# define SECTION_NON_CACHEABLE 0x1000
// Cacheable in normal memory
# define SECTION_CACHEABLE 0x100C
// Non shareable device specific memory
# define SECTION_NON_SHAREABLE 0x2000
# define SECTION_SHARE_BIT_SET 0x00010000
# define SECTION_SHARE_BIT_CLEAR 0x00000000
# define SECTION_NON_GLOBAL_SET 0x02000000
# define SECTION_NON_GLOBAL_CLEAR 0x00000000
# define SECTION_EXECUTE_NEVER_SET 0x00000010
# define SECTION_EXECUTE_NEVER_CLEAR 0x00000000
</code>
# define SECTION_ENTRY 0x00000002
# define IS_ENTRY_SECTION(entry) (((entry)&0x04000003) == SECTION_ENTRY)
// NONE for Privileged, NONE for User
# define SECTION_AP_PERMISSION_FAULT 0x0000
// R/W for Privileged, NONE for User
# define SECTION_AP_PRIVILEDGE_ACCESS 0x0400
// R/W for Privileged, R for User
# define SECTION_AP_USER_NO_WRITE 0x0800
// R/W for Privileged, R/W for User
# define SECTION_AP_FULL_ACCESS 0x0C00
// R for Privileged, NONE for User
# define SECTION_AP_PRIVELEDGE_READ 0x8400
// R for Privileged, R for User
# define SECTION_AP_READ_ONLY 0x8800
// Basically non cacheable
# define SECTION_STRONGLY_ORDERED 0x0000
// Write to cache only (memory is stale) and allocate new entries
# define SECTION_SHAREABLE_DEVICE 0x0004
// Write to both cache and memory
# define SECTION_WRITE_THROUGH_NO_ALLOC_ON_WRITE 0x0008
// Write to cache only (memory is stale) no allocation
# define SECTION_WRITE_BACK_NO_ALLOC_ON_WRITE 0x000C
// Non cacheable in Normal memory
# define SECTION_NON_CACHEABLE 0x1000
// Cacheable in normal memory
# define SECTION_CACHEABLE 0x100C
// Non shareable device specific memory
# define SECTION_NON_SHAREABLE 0x2000
# define SECTION_SHARE_BIT_SET 0x00010000
# define SECTION_SHARE_BIT_CLEAR 0x00000000
# define SECTION_NON_GLOBAL_SET 0x02000000
# define SECTION_NON_GLOBAL_CLEAR 0x00000000
# define SECTION_EXECUTE_NEVER_SET 0x00000010
# define SECTION_EXECUTE_NEVER_CLEAR 0x00000000
All of these macros are based upon the UG585 trm: Chapter 3, Figure 3-5
The problem
Here is the code I have tried to write to test the MMU
As you can tell the test is quite simple, I set the entry at 0x63M to translate to 0x64M and vice versa, after that I write a value at addr1 (0x6400000) and of course I expect it to be written at addr0 (0x6300000).
However it still writes at 0x6400000 like if the MMU has never been activated.
Looking at CP15 C1 register I can tell that it is however active!
I am clueless as to why my code does not work as intended, am I missing something in the initialization of the MMU?