You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

L1页表条目格式

测试代码与问题现象

我编写了以下测试代码验证映射功能:

#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确实处于激活状态,但地址映射未按预期工作。

问题排查方向

  1. 页表基地址验证:检查CP15的TTBR0/TTBR1寄存器是否正确指向自定义的MMUTable物理地址(需16KB对齐)。原生Xil_EnableMMU可能设置了默认页表,需确保TTBR0已更新为新页表地址。
  2. 页表初始化检查:确认MMUTable使用前已完成初始化,未使用条目需设为无效,避免遗留原生库的"虚拟地址=物理地址"默认映射。
  3. TLB失效范围扩展:当前hwi_mmu_sync仅失效未锁定的UTLB,建议全局失效所有TLB(包括ITLB和锁定条目),可使用mtcp(XREG_CP15_INVAL_TLB, 0U)指令。
  4. 内存屏障强化:在修改页表后,需确保页表写入同步到物理内存,可在hwi_mmu_sync中增加DCache的清理操作,避免页表仅存于缓存中。
  5. 页表条目校验:调试时读取MMUTable[0x64]的值,确认是否包含正确的物理地址0x6300000和属性位,验证条目是否被正确修改。
  6. 多核同步处理:ZYNQ-7000为双核架构,若仅在单核修改页表,需同步失效另一核的TLB,并确保页表所在内存为共享强序类型。

内容的提问来源于stack exchange,提问作者Vladouch

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 00:15:54