ZYNQ-7000平台Armv7 MMU虚拟化功能未生效问题排查
ZYNQ-7000 MMU自定义映射失效问题排查
概述
我正在评估Xilinx ZYNQ-7000 SoC(搭载2颗Cortex-A9处理器)的MMU功能。
Xilinx原生MMU库的局限性
最初尝试使用xil_mmu.h库,但其实现存在明显局限:不支持灵活的物理地址(P)与虚拟地址(V)互转,且仅支持1MB粒度的页表项,无法实现跨段地址映射。以Xil_SetTlbAttributes函数为例:
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 */ }
可见section变量直接由物理地址计算得出,只能实现虚拟地址与物理地址同段的映射,无法完成诸如物理地址0x100000到虚拟地址0x200000这类跨段转换。
自定义MMU实现
针对原生库的不足,我对其进行了扩展,实现了支持任意虚拟-物理地址映射的功能:
核心实现代码
/* * 刷新、失效TLB并同步处理器的函数 */ static void hwi_mmu_picoZed_7010_sync() { Xil_DCacheFlush(); mtcp(XREG_CP15_INVAL_UTLB_UNLOCKED, 0U); /* 失效所有分支预测器 */ mtcp(XREG_CP15_INVAL_BRANCH_ARRAY, 0U); dsb(); /* 确保BP和TLB失效操作完成 */ isb(); /* 同步当前处理器上下文 */ } /* * 根据属性设置L1页表中的条目 */ static void hwi_mmu_picoZed_7010_set_table_entry(uint32_t entry, uint32_t properties) { // 处理越界条目 if (entry > 4095) return; // 获取4096个条目中的一个 uintptr_t *new_entry = (uintptr_t *)&MMUTable + entry; if (new_entry) *new_entry = properties; } // 针对特定条目类型的包装函数,调用hwi_mmu_picoZed_7010_set_table_entry // 设置L1页表中的段条目,传入物理地址、虚拟段索引和属性 void hwi_mmu_set_L1_Section(uint32_t addr, uint32_t entry, uint32_t properties) { // 验证是否为段类型条目 if (!IS_ENTRY_SECTION(properties)) return; hwi_mmu_picoZed_7010_set_table_entry(entry, (addr & 0xFFF00000) | properties); }
内存属性宏定义
为简化配置,基于ZYNQ-7000技术参考手册UG585第3章图3-5定义了以下宏:
#define SECTION_ENTRY 0x00000002 #define IS_ENTRY_SECTION(entry) (((entry)&0x04000003) == SECTION_ENTRY) // 权限属性 // 特权级无权限,用户级无权限 #define SECTION_AP_PERMISSION_FAULT 0x0000 // 特权级读写,用户级无权限 #define SECTION_AP_PRIVILEDGE_ACCESS 0x0400 // 特权级读写,用户级只读 #define SECTION_AP_USER_NO_WRITE 0x0800 // 特权级读写,用户级读写 #define SECTION_AP_FULL_ACCESS 0x0C00 // 特权级只读,用户级无权限 #define SECTION_AP_PRIVELEDGE_READ 0x8400 // 特权级只读,用户级只读 #define SECTION_AP_READ_ONLY 0x8800 // 内存类型属性 // 强序内存(不可缓存) #define SECTION_STRONGLY_ORDERED 0x0000 // 可共享设备内存(仅写缓存,分配新条目) #define SECTION_SHAREABLE_DEVICE 0x0004 // 写穿透缓存(写同时更新缓存和内存,写不分配) #define SECTION_WRITE_THROUGH_NO_ALLOC_ON_WRITE 0x0008 // 回写缓存(仅写缓存,写不分配) #define SECTION_WRITE_BACK_NO_ALLOC_ON_WRITE 0x000C // 普通内存不可缓存 #define SECTION_NON_CACHEABLE 0x1000 // 普通内存可缓存 #define SECTION_CACHEABLE 0x100C // 不可共享设备内存 #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

测试代码与问题现象
我编写了以下测试代码验证映射功能:
#include "hwi_mmu_picoZed_7010.h" #include <stdint.h> #include <xil_mmu.h> int main(void) { Xil_EnableMMU(); uint32_t addr1 = 0x6400000; // 虚拟地址0x6400000 uint32_t addr0 = 0x6300000; // 物理地址0x6300000 // 设置虚拟段0x63(对应虚拟地址0x6300000)映射到物理地址0x6400000 hwi_mmu_set_L1_Section(addr1, 0x63, SECTION_ENTRY | SECTION_SHARE_BIT_SET | SECTION_AP_FULL_ACCESS | SECTION_SHAREABLE_DEVICE); hwi_mmu_sync(); // 设置虚拟段0x64(对应虚拟地址0x6400000)映射到物理地址0x6300000 hwi_mmu_set_L1_Section(addr0, 0x64, SECTION_ENTRY | SECTION_SHARE_BIT_SET | SECTION_AP_FULL_ACCESS | SECTION_SHAREABLE_DEVICE); hwi_mmu_sync(); // 向虚拟地址addr1写入值123,预期写入物理地址addr0 *((uint32_t *)(uintptr_t)addr1) = 123; return 1; }
测试逻辑为:将虚拟地址0x6300000映射到物理地址0x6400000,虚拟地址0x6400000映射到物理地址0x6300000,随后向虚拟地址0x6400000写入123,预期该值会被写入物理地址0x6300000。
但实际测试发现,值仍然被写入物理地址0x6400000,仿佛MMU未生效。通过查看CP15 C1寄存器确认MMU确实处于激活状态,但地址映射未按预期工作。
问题排查方向
- 页表基地址验证:检查CP15的TTBR0/TTBR1寄存器是否正确指向自定义的
MMUTable物理地址(需16KB对齐)。原生Xil_EnableMMU可能设置了默认页表,需确保TTBR0已更新为新页表地址。 - 页表初始化检查:确认
MMUTable使用前已完成初始化,未使用条目需设为无效,避免遗留原生库的"虚拟地址=物理地址"默认映射。 - TLB失效范围扩展:当前
hwi_mmu_sync仅失效未锁定的UTLB,建议全局失效所有TLB(包括ITLB和锁定条目),可使用mtcp(XREG_CP15_INVAL_TLB, 0U)指令。 - 内存屏障强化:在修改页表后,需确保页表写入同步到物理内存,可在
hwi_mmu_sync中增加DCache的清理操作,避免页表仅存于缓存中。 - 页表条目校验:调试时读取
MMUTable[0x64]的值,确认是否包含正确的物理地址0x6300000和属性位,验证条目是否被正确修改。 - 多核同步处理:ZYNQ-7000为双核架构,若仅在单核修改页表,需同步失效另一核的TLB,并确保页表所在内存为共享强序类型。
内容的提问来源于stack exchange,提问作者Vladouch
相关产品推荐
相关产品推荐

