简介
我一直在尝试评估包含 2 个 A9-Cortex 处理器的 Xilinx zynq7000 soc 的 MMU 功能。
首先,我尝试使用 xil_mmu.h 库,但是在阅读了其中实现的函数后,我意识到该映射不允许真正的 P 到 V 或 V 到 P 转换。此外,该库不包含任何粒度概念,并且仅支持 1M 页表条目。 让我们分析这个库中的一个函数来说明我的担忧:xil_mmu.c
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 */
}
正如您所知,节变量(4GB 地址空间中的 4096 个 1MB 节之一)是根据物理地址选择的,因此,例如,我无法将物理地址 0x100000 映射到虚拟地址0x200000 或其他。
自己实现
看到这种方法缺乏模块化(基本上没有真正的虚拟化),我决定实现对基本 xil_mmu 库的扩展。
/*
* 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);
}
以及一些宏可以更轻松地设置所需的内存属性
# 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
所有这些宏均基于 UG585 trm:第 3 章,图 3-5
问题
这是我尝试编写的用于测试 MMU 的代码
#include "hwi_mmu_picoZed_7010.h"
#include <stdint.h>
#include <xil_mmu.h>
int main(void) {
Xil_EnableMMU();
uint32_t addr1 = 0x6400000;
uint32_t addr0 = 0x6300000;
hwi_mmu_set_L1_Section(addr1, 0x63, SECTION_ENTRY | SECTION_SHARE_BIT_SET | SECTION_AP_FULL_ACCESS | SECTION_SHAREABLE_DEVICE);
hwi_mmu_sync();
hwi_mmu_set_L1_Section(addr0, 0x64, SECTION_ENTRY | SECTION_SHARE_BIT_SET | SECTION_AP_FULL_ACCESS | SECTION_SHAREABLE_DEVICE);
hwi_mmu_sync();
*((uint32_t *)(uintptr_t)addr1) = 123;
return 1;
}
正如你所见,测试非常简单,我将 0x63M 处的条目设置为转换为 0x64M,反之亦然,之后我在 addr1 (0x6400000) 处写入一个值,当然我希望它写入 addr0 (0x6300000) .
但是它仍然在 0x6400000 处写入,就像 MMU 从未被激活一样。
查看 CP15 C1 寄存器,我可以看出它是活跃的! 我不知道为什么我的代码不能按预期工作,我是否在 MMU 的初始化中遗漏了一些东西?
MMU 工作正常,内存转储只是从处理器的角度报告地址。也就是说,内存转储中的地址是虚拟地址,而不是物理地址。
可以通过将两个不同的虚拟地址映射到同一物理页并观察一个地址上的修改在另一个地址上可见来验证 MMU 是否正常工作。